Skip to content

Commit

Permalink
breaking: rename focusInputOnSelect to closeDropdownOnSelect
Browse files Browse the repository at this point in the history
  • Loading branch information
janosh committed Sep 21, 2023
1 parent 7b2565c commit aefc03a
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 23 deletions.
4 changes: 2 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,10 @@ Full list of props/bindable variables for this component. The `Option` type you
Customize how dropdown options are filtered when user enters search string into `<MultiSelect />`. Defaults to:

1. ```ts
focusInputOnSelect: boolean | 'desktop' = `desktop`
closeDropdownOnSelect: boolean | 'desktop' = `desktop`
```

One of `true`, `false` or `'desktop'`. Whether to move focus back to the input element after selecting a dropdown item. If `false`, component will loose focus and `dropdown` is closed. `'desktop'` means `true` if current window width is larger than the current value of `breakpoint` prop (default is 800, meaning screen width in pixels).
One of `true`, `false` or `'desktop'`. Whether to close the dropdown list after selecting a dropdown item. If `true`, component will loose focus and `dropdown` is closed. `'desktop'` means `false` if current window width is larger than the current value of `breakpoint` prop (default is 800, meaning screen width in pixels). This is to align with the default behavior of many mobile browsers like Safari which close dropdowns after selecting an option while desktop browsers facilitate multi-selection by leaving dropdowns open.

1. ```ts
form_input: HTMLInputElement | null = null
Expand Down
14 changes: 8 additions & 6 deletions src/lib/MultiSelect.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
if (!searchText) return true
return `${get_label(opt)}`.toLowerCase().includes(searchText.toLowerCase())
}
export let focusInputOnSelect: boolean | 'desktop' = `desktop`
export let closeDropdownOnSelect: boolean | 'desktop' = `desktop`
export let form_input: HTMLInputElement | null = null
export let highlightMatches: boolean = true
export let id: string | null = null
Expand Down Expand Up @@ -222,13 +222,15 @@
}
}
const focus_input =
focusInputOnSelect === true ||
(focusInputOnSelect === `desktop` && window_width > breakpoint)
const reached_max_select = selected.length === maxSelect
if (selected.length === maxSelect || !focus_input) {
const dropdown_should_close =
closeDropdownOnSelect === true ||
(closeDropdownOnSelect === `desktop` && window_width < breakpoint)
if (reached_max_select || dropdown_should_close) {
close_dropdown(event)
} else if (focus_input) {
} else if (!dropdown_should_close) {
input?.focus()
}
dispatch(`add`, { option })
Expand Down
4 changes: 2 additions & 2 deletions src/routes/(demos)/ui/+page.svx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
options={foods.map(label => ({ label, style: `background-color: ${random_color()}` }))}
placeholder="Pick your favorite foods"
removeAllTitle="Remove all foods"
focusInputOnSelect={false}
closeDropdownOnSelect={true}
invalid
/>
```
Expand All @@ -44,7 +44,7 @@ This page is used for Playwright testing to ensure
- has `aria-expanded='true'` when open
- options have `aria-selected='false'` and selected items have `aria-selected='true`
- invisible `input.form-control` is `aria-hidden`
- `focusInputOnSelect`: when `false`, the input is not focused when an option is selected and the dropdown is closed
- `closeDropdownOnSelect`: when `true`, the input is not focused when an option is selected and the dropdown is closed

<!-- TODO figure out why Playwright test 'loops through the dropdown list with arrow keys making...'
depends on `html { scroll-behavior: smooth; }` -->
Expand Down
25 changes: 12 additions & 13 deletions tests/unit/MultiSelect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1288,11 +1288,11 @@ test.each([
)

test.each([true, false, `desktop`] as const)(
`focusInputOnSelect=%s controls input focus and dropdown closing`,
async (focusInputOnSelect) => {
`closeDropdownOnSelect=%s controls input focus and dropdown closing`,
async (closeDropdownOnSelect) => {
const select = new MultiSelect({
target: document.body,
props: { options: [1, 2, 3], focusInputOnSelect },
props: { options: [1, 2, 3], closeDropdownOnSelect },
})

// simulate selecting an option
Expand All @@ -1302,20 +1302,19 @@ test.each([true, false, `desktop`] as const)(
await tick() // wait for DOM updates

const is_desktop = window.innerWidth > select.breakpoint
const should_be_focused =
focusInputOnSelect === true ||
(focusInputOnSelect === `desktop` && is_desktop)
const should_be_closed =
closeDropdownOnSelect === true ||
(closeDropdownOnSelect === `desktop` && !is_desktop)

// check that input is focused (or not)
const input = doc_query<HTMLInputElement>(`input[autocomplete]`)
expect(document.activeElement === input).toBe(should_be_focused)

// check that dropdown is closed when focusInputOnSelect = false
// check that dropdown is closed when closeDropdownOnSelect = false
const dropdown = doc_query(`ul.options`)
const should_be_closed = focusInputOnSelect === false
expect(dropdown.classList.contains(`hidden`)).toBe(should_be_closed)

if (focusInputOnSelect === `desktop`) {
// check that input is focused (or not)
const input = doc_query<HTMLInputElement>(`input[autocomplete]`)
expect(document.activeElement === input).toBe(!should_be_closed)

if (closeDropdownOnSelect === `desktop`) {
// reduce window width to simulate mobile
window.innerWidth = 400
window.dispatchEvent(new Event(`resize`))
Expand Down

0 comments on commit aefc03a

Please sign in to comment.