* Fix keyboard navigation in DS::Select dropdowns.
Open the listbox on Tab focus and arrow/space/enter from the trigger so
account and category selectors in the transaction form are usable without
a mouse, and reset option tabindex when the menu closes.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix Tab skipping DS::Select triggers in transaction form.
Keep focus on the trigger when Tab opens the menu, keep listbox options
at tabindex -1 with inert on the closed menu, and move focus into options
only via arrow keys so Tab no longer jumps to amount or date.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix search input losing focus during DS::Select filtering.
Only repoint focus in syncTabindex when a listbox option was focused and
became hidden; skip while the user is typing in the search field or when
clearSearch runs on menu open.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix Escape closing the parent dialog from an open DS::Select.
Stop Escape propagation so only the listbox closes, and suppress
handleButtonFocus briefly after close+refocus so keyboard modality
does not reopen the menu via :focus-visible.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix Tab leaving DS::Select after suppressReopenOnFocus change.
Focus the trigger before closing on Tab and advance to the next dialog
field manually so inert on the menu no longer drops focus to body.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(design-system): DS::Select a11y — fix aria-expanded, listbox keyboard nav, label binding
Closes#1744. Several concrete bugs from the savings-goals audit:
1. **`aria-expanded` wired to the wrong state.** The template had
`aria-expanded="<%= @selected_value.present? ? "true" : "false" %>"`,
which is "has a value been chosen", not "is the menu open".
AT users heard a misleading signal on every page load.
Init to `"false"`; the Stimulus controller's openMenu/close
already correctly maintains the attribute after that.
2. **`aria-labelledby` referenced a nonexistent id.** The trigger
pointed at `"#{method}_label"`, but the rendered `<label>` had
no id at all — the binding silently failed. Add
`id: "#{method}_label"` to `form.label` so the reference
actually resolves to the label text. Only emit
`aria-labelledby` when there *is* a visible label.
3. **`tabindex="0"` on every option.** Listbox options should use
roving tabindex (only the selected option is in tab order; the
rest are reachable via ArrowUp/Down). Set
`tabindex="0"` on the selected option only; `"-1"` on the rest.
The select controller's `select()` handler keeps the roving
invariant on user interaction.
4. **No keyboard navigation between options.** Add ArrowDown/Up
(cycle), Home (first), End (last). The existing Enter/Escape
handlers stay. ArrowUp/Down inside the search input is left
alone so the input's caret behavior isn't hijacked.
5. **Search input had no accessible name.** Add an explicit
`aria-label` matching the placeholder copy so AT users hear
"search" when focus enters the field.
API unchanged. Builder-level routing fix in
`StyledFormBuilder#select` (calling DS::Select for `f.select(...)`
the same way `f.collection_select` already does) is intentionally
out of scope — it's a separate translation pass for the choices
format. Documented as a follow-up.
* fix(review): bridge search input to visible options in DS::Select
ArrowDown/Up from the search input now focus the first/last visible
option, and keyboard navigation operates on visible options only. After
typing a search query, the controller promotes the first visible option
to tabindex="0" so Tab can land on it even when the previously
tab-eligible option is filtered out.
Addresses Codex review on PR #1848 (issue #1744).
* fix(review): include trigger in DS::Select aria-labelledby
Codex P2 follow-up on #1848: \`aria-labelledby=\"#{method}_label\"\`
makes the trigger button's accessible name come solely from the
external form label — that overrides the button's own text node
(\`selected_item[:label]\` / placeholder). Screen readers therefore
announce only "Currency" without ever hearing the selected "USD"
unless the user opens the listbox.
Give the trigger \`id=\"#{method}_trigger\"\` and reference both ids:
\`aria-labelledby=\"#{method}_label #{method}_trigger\"\`. The
accessible-name algorithm concatenates the two, so AT users now
hear \"<Label> <selected value>\" while \`aria-expanded\` /
\`aria-haspopup\` continue to convey the dropdown state.
* feat(select): improve merchant dropdown behavior and placement controls
- add configurable menu_placement strategy to DS::Select (auto/down/up) with safe normalization
forward menu_placement through StyledFormBuilder#collection_select
- force Merchant dropdown to open downward in transaction create and editor forms
- fix select option/search text contrast by applying text-primary in DS select menu
- prevent form jump on open by scrolling only inside dropdown content instead of using scrollIntoView
- clamp internal dropdown scroll to valid bounds for stability
- refactor select controller placement logic for readability (placementMode, clamp) without changing behavior
* set menu_placement=auto for metchant selector
* feat: add new UI component to display dropdown select with filter
* feat: use new dropdown componet for category selection in transactions
* feat: improve dropdown controller
* feat: Add checkbox indicator to highlight selected element in list
* feat: add possibility to define dropdown without search
* feat: initial implementation of variants
* feat: Add default color for dropdown menu
* feat: add "icon" variant for dropdown
* refactor: component + controller refactoring
* refactor: view + component
* fix: adjust min width in selection for mobile
* feat: refactor collection_select method to use new filter dropdown component
* fix: compute fixed position for dropdown
* feat: controller improvements
* lint issues
* feat: add dot color if no icon is available
* refactor: controller refactor + update naming for variant from icon to logo
* fix: set width to 100% for select dropdown
* feat: add variant to collection_select in new transaction form
* fix: typo in placeholder value
* fix: add back include_blank property
* refactor: rename component from FilterDropdown to Select
* fix: translate placeholder and keep value_method and text_method
* fix: remove duplicate variable assignment
* fix: translate placeholder
* fix: verify color format
* fix: use right autocomplete value
* fix: selection issue + controller adjustments
* fix: move calls to startAutoUpdate and stopAutoUpdate
* Update app/javascript/controllers/select_controller.js
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Alessio Cappa <104093777+alessiocappa@users.noreply.github.com>
* fix: add aria-labels
* fix: pass html_options to DS::Select
* fix: unnecessary closing tag
* fix: use offsetvalue for position checks
* fix: use right classes for dropdown transitions
* include options[:prompt] in placeholder init
* fix: remove unused locale key
* fix: Emit a native change event after updating the input value.
* fix: Guard against negative maxHeight in constrained layouts.
* fix: Update test
* fix: lint issues
* Update test/system/transfers_test.rb
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Alessio Cappa <104093777+alessiocappa@users.noreply.github.com>
* Update test/system/transfers_test.rb
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Alessio Cappa <104093777+alessiocappa@users.noreply.github.com>
* refactor: move CSS class for button select form in maybe-design-system.css
---------
Signed-off-by: Alessio Cappa <104093777+alessiocappa@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Make forms more composable, opt-in to form builder
* Remove unused method
* Simpler money input controls
* Add in new form styling to imports
* Lint fixes
* Small tweak of multi select styles