--- title: "Troubleshooting" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Troubleshooting} %\VignetteEncoding{UTF-8} %\VignetteEngine{knitr::rmarkdown} editor_options: markdown: wrap: 72 --- ```{r setup, include=FALSE} knitr::opts_chunk$set(eval = FALSE) library(shiny) library(glasstabs) ``` Each entry states the expected behavior first. Several of these are intentional design decisions, not bugs — the fix is usually a configuration choice, and the entry says so. ## Dropdown does not look styled **Expected:** glass surfaces, rounded panels, animated markers. **You see:** a plain browser select, or unstyled lists and buttons. **Likely cause:** the package's CSS and JavaScript never loaded. Every app needs exactly one `useGlassTabs()` call at the UI root — except apps built with `glassPage()`, which loads the dependency automatically. **Fix:** add `useGlassTabs()` to the top-level UI (not inside `renderUI()`, not inside a module UI). On Posit Connect, if the app looks styled locally but plain after publishing, check the manifest and the deployed assets: the dependency must publish with the app. ## Server-backed search never returns results **Expected:** typing queries the server and shows matches. **You see:** "Searching..." that never resolves, or an empty list for queries that should match. **Likely causes, in order:** (1) the matching helper was never registered — `glassMultiSelectServer()` for a multi-select, `glassSelectServer()` for a single-select, with the same `inputId`; (2) the helper is registered for a different ID than the widget; (3) in a module, the helper got a namespaced ID instead of the bare ID with the module session (see the modules article). **Fix:** register the helper in the server function, once per input ID, with the session the widget belongs to. A minimal reproduction is one widget, one helper, one search: if that works, the difference between it and the broken app is the cause. ## Multi-select starts with everything checked **Expected:** this is the default, not a bug. `selected = NULL` selects every enabled choice initially. **Fix:** pass `selected = character(0)` for an empty start. See the multi-select guide's "Understanding selection behavior" section for the All/Some/None decision that follows from this choice. ## Clearing a selection shows all records **Expected:** after Clear all, the widget returns `character(0)`. **Likely cause:** the app treats empty as "no filter." That is a valid app policy, but it must be chosen deliberately — otherwise clearing a filter restores every record while the dropdown looks empty. **Fix:** decide what empty means and write it in the filter (see the selection section), or use `glassMultiSelectValue(..., empty_behavior = "all")` / `"none"` to resolve empty consistently. The helper changes how the app resolves empty, not what the widget returns. ## Selection disappears after a choices update **Expected:** updating `choices` without `selected` keeps the current value when it still exists. **Likely cause:** the value is absent from the new choice set. For the multi-select, `drop_invalid = TRUE` (the default) drops it; `FALSE` keeps it for when it reappears. For the single-select there are no knobs: a vanished selection clears to `NULL`. **Fix:** choose the policy explicitly (`preserve_selection` / `drop_invalid` for multi-select; see the linked-filters article for the full scenario table). If the UI must show that an item vanished, remember the old value before updating — neither widget marks invalid selections. ## Dropdown is too narrow **Expected:** the panel matches the trigger width, long labels wrap, and the panel stays inside the viewport. **Likely cause:** the trigger itself is narrow. The panel matching the trigger is the design, not a bug: widening the panel past the field was tried and rejected (long text wraps inside the panel instead). **Fix:** set `width` on the widget (for example `width = "100%"` inside a wide container, or an explicit pixel width), and check the parent container — a narrow column or a fixed-width card caps the trigger before glasstabs ever sees it. If a panel still escapes the viewport, that is a bug: report it with the container layout. ## Theme differs from the surrounding dashboard **Expected:** the widget follows the app's theme. **Likely cause:** `theme` disagrees with the page. `theme = "dark"` and `"light"` are fixed; `"auto"` follows the page's own signal. Dashboards with their own dark-mode toggle (bs4Dash, bslib) need `dark_selector` pointed at that toggle's marker, so the widget flips when the dashboard does. **Fix:** set `theme = "auto"` for plain pages, or `theme = "auto"` plus the dashboard's `dark_selector` for themed dashboards. Compare all three modes in the theme-switcher example before shipping. ## Server update does not reach a module control **Expected:** an update inside a module changes that module's widget. **Likely cause:** a namespaced ID reached an update function or a server helper. Module UI takes `ns("region")`; module server code takes the module session and the bare `"region"`. Passing `ns("region")` to an updater addresses a nonexistent `"filters-filters-region"` input, silently. **Fix:** audit every update and helper call in the module server for bare IDs (see the modules article). If the update reaches the wrong module instance, a hardcoded full ID somewhere in the UI is the cause. ## Dropdown closes when a modal opens **Expected:** this is the lifecycle working as designed. When a full-screen overlay or modal opens, open dropdowns close rather than stranding above the modal. **Fix:** none needed for the close itself. If the app opens modals programmatically and the close looks abrupt, close dropdowns deliberately first with `closeAllGlassSelects()` (or `closeGlassSelect()` / `closeGlassMultiSelect()` for one widget), so the transition is intentional. There is no "reopen" helper: the user reopens with a click, like any other dropdown. ## Widget appears unstyled or unbound after renderUI() **Expected:** a widget created by `renderUI()` binds fresh with the arguments it was created with. **Likely causes:** (1) `useGlassTabs()` was placed inside the rendered UI instead of at the root — dependencies must load once at the top level; (2) a server-backed widget's helper was registered inside the render instead of once in the server function, so the new control has no search backend; (3) the app expected the previous control's selection to survive — a rebuilt widget starts from its UI arguments, so re-apply the intended selection after the render. **Fix:** keep `useGlassTabs()` at the root and server registration in the server function, and treat a `renderUI()` rebuild as a fresh widget (see the linked-filters article's renderUI section).