Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
4b2859a
fix: select up arrow key highlights last item
thejackshelton Jun 12, 2024
8a757e2
add listbox research
thejackshelton Jun 12, 2024
7ac4e70
docs: push up latest combobox research
thejackshelton Jun 12, 2024
a154ee7
add more research
thejackshelton Jun 12, 2024
9109cb9
lockfile changes
thejackshelton Jun 13, 2024
edb15f7
fix dumb pnpm issue
thejackshelton Jun 13, 2024
d166423
feat: initial combobox scaffolding
thejackshelton Jun 13, 2024
20eda9b
add example styles
thejackshelton Jun 14, 2024
c8c9df4
take from select tests, initial tests passing
thejackshelton Jun 14, 2024
bd2a75a
feat: close combobox listbox on click
thejackshelton Jun 14, 2024
f3a2913
feat: combobox inline comp
thejackshelton Jun 14, 2024
b4985a5
setup refs
thejackshelton Jun 14, 2024
6ff0bc8
feat: add combobox selecting with mouse
thejackshelton Jun 14, 2024
924aae1
feat: select items input val in combobox
thejackshelton Jun 16, 2024
8627595
feat: add combobox group labels
thejackshelton Jun 16, 2024
7cd79d2
feat: add initial combobox keyboard interactions
thejackshelton Jun 16, 2024
bbb2300
feat: dismisses listbox
thejackshelton Jun 16, 2024
a2c4f9c
tests: more combobox tests
thejackshelton Jun 16, 2024
97f9b2c
feat: add selecting options via keyboard
thejackshelton Jun 16, 2024
7ed9b81
get latest from main
thejackshelton Jun 16, 2024
80ec42b
feat: a11y navigation and removing excess select code
thejackshelton Jun 17, 2024
6f9ced0
feat: popover code and item indicator
thejackshelton Jun 17, 2024
c23682f
feat: looped tests
thejackshelton Jun 17, 2024
39d5976
feat: scroll behavior
thejackshelton Jun 17, 2024
0ffb516
label the options
thejackshelton Jun 17, 2024
95b250a
tests: disabled
thejackshelton Jun 17, 2024
ea6b9b9
feat: placeholder
thejackshelton Jun 17, 2024
a32aae7
remove nx workspace
thejackshelton Jun 17, 2024
f171cfc
Merge branch 'main' of https://github.com/qwikifiers/qwik-ui into com…
thejackshelton Jun 17, 2024
c8af763
feat: onChange$ and onOpenChange$
thejackshelton Jun 17, 2024
e50ba79
test: updated combobox tests
thejackshelton Jun 18, 2024
daf7fc6
reactive values and scrolling
thejackshelton Jun 18, 2024
5a5c416
test: combobox bind tests
thejackshelton Jun 19, 2024
a22b7dc
a11y: axe validation tests
thejackshelton Jun 19, 2024
cfe64ad
a11y: major select a11y improvements
thejackshelton Jun 19, 2024
d8c9d6b
feat: combobox groups
thejackshelton Jun 19, 2024
720e23e
test: new combobox a11y tests
thejackshelton Jun 19, 2024
78753cd
small code improvements
thejackshelton Jun 19, 2024
39a5bd7
feat: initial multiple support
thejackshelton Jun 19, 2024
23e0d35
test: all tests passing
thejackshelton Jun 20, 2024
d1a54d6
fix build
thejackshelton Jun 20, 2024
3714fc3
tests: new combobox filter tests
thejackshelton Jun 20, 2024
483e668
feat: initial filtering
thejackshelton Jun 20, 2024
da63b69
working navigation
thejackshelton Jun 20, 2024
7fc0240
fix: combobox css styles
thejackshelton Jun 20, 2024
ab30247
feat: listbox closes when no options are present
thejackshelton Jun 21, 2024
22da266
test: finish initial filtered options tests
thejackshelton Jun 21, 2024
c4a6831
feat: custom filters
thejackshelton Jun 21, 2024
db95b9c
fix: filter tests
thejackshelton Jun 21, 2024
eb74635
fix: sync open signal and flaky test
thejackshelton Jun 22, 2024
f4a3604
add in onInput$
thejackshelton Jun 22, 2024
8db99ad
docs: initial
thejackshelton Jun 22, 2024
9f0fff8
initial docs
thejackshelton Jun 23, 2024
82aebab
deprecate listbox
thejackshelton Jun 23, 2024
dcf3b5c
docs(headless/combobox/examples): remove deprecated .Listbox
maiieul Jun 23, 2024
a469c8a
middle docs
thejackshelton Jun 23, 2024
9e32f4f
remove listbox
thejackshelton Jun 23, 2024
8cabf9b
add placeholder
thejackshelton Jun 23, 2024
6748a45
onInput$ and filter docs
thejackshelton Jun 23, 2024
9753d68
docs API
thejackshelton Jun 23, 2024
88537e4
fix(styled combobox): pass children instead of Slot
maiieul Jun 23, 2024
5c3f18e
docs(styled/combobox): update component code
maiieul Jun 23, 2024
539812c
temp fix(contributing): workaround the toc continuous heading bug
maiieul Jun 23, 2024
4e072a6
add new toggle tests
thejackshelton Jun 23, 2024
4cebd38
latest
thejackshelton Jun 23, 2024
dbb2619
fix: toggle in single selection mode
thejackshelton Jun 23, 2024
a4cc586
latest from main
thejackshelton Jun 24, 2024
34dd7ee
automatic empty component
thejackshelton Jun 25, 2024
c552547
more docs
thejackshelton Jun 25, 2024
37d4847
fix mdx structure
thejackshelton Jun 25, 2024
492dd83
initial merged ref
thejackshelton Jun 26, 2024
dbc86b1
the ability to pass in refs to any component
thejackshelton Jun 27, 2024
ae00c0b
feat: reactive-input
thejackshelton Jun 27, 2024
34220df
add form support
thejackshelton Jun 28, 2024
c921d5b
feat: combobox form validation
thejackshelton Jun 29, 2024
910f881
combobox description + fix validation
thejackshelton Jun 29, 2024
bc2493a
refactor: deprecate select listbox
thejackshelton Jul 2, 2024
9a5f876
refactored combined refs
thejackshelton Jul 4, 2024
1447668
fix: grab popover ref
thejackshelton Jul 4, 2024
66d07b4
Merge branch 'main' of https://github.com/qwikifiers/qwik-ui into com…
thejackshelton Jul 4, 2024
4061837
remove refs in hero example
thejackshelton Jul 5, 2024
3fdf005
better filtering API, but bugs
thejackshelton Jul 5, 2024
eb3feaa
correct initial load sig
thejackshelton Jul 5, 2024
7f2c9a7
get correct items map
thejackshelton Jul 6, 2024
1a81865
fix: CSR
thejackshelton Jul 6, 2024
05adaab
make sure navigation is working
thejackshelton Jul 7, 2024
67ab2de
update custom filter
thejackshelton Jul 7, 2024
7341fc4
fix: initial value and controlled value
thejackshelton Jul 7, 2024
f5cadfe
fix: initial values
thejackshelton Jul 7, 2024
0136557
fix: scrollbar issues
thejackshelton Jul 7, 2024
30d2c2d
fix: use debounce for scrolling
thejackshelton Jul 7, 2024
bae993c
fix: navigation bug between mouse and keyboard
thejackshelton Jul 7, 2024
ff5885a
feat: multiple backspace
thejackshelton Jul 7, 2024
878050a
fix: multiple items reset the input
thejackshelton Jul 7, 2024
aff5dc8
update multiple example
thejackshelton Jul 8, 2024
0ee1ecb
changelog updates
thejackshelton Jul 8, 2024
d80e222
fix: styled select
thejackshelton Jul 8, 2024
634a0a5
latest from main
thejackshelton Jul 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
369 changes: 369 additions & 0 deletions .changeset/stale-mails-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,369 @@
---
'@qwik-ui/headless': minor
---

# Combobox v2, New Dropdown Component, and Progress bar reaches beta!

0.5 continues our move towards a 1.0 release. It includes a few breaking changes to the Combobox in order to make sure that the components have a clear API.

Below is a migration guide of API's for the Combobox.

## Combobox

The combobox has been refactored from the ground up, including new features, components, and QOL updates.

### Anatomy changes

The new Combobox anatomy is as follows:

```tsx
import { component$ } from '@builder.io/qwik';
import { Combobox } from '@qwik-ui/headless';
import { LuCheck } from '@qwikest/icons/lucide';

export default component$(() => {
return (
<Combobox.Root>
<Combobox.Label>label</Combobox.Label>

<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger>trigger</Combobox.Trigger>
</Combobox.Control>

<Combobox.Popover>
<Combobox.Item>
<Combobox.ItemLabel>item label</Combobox.ItemLabel>
<Combobox.ItemIndicator>
<LuCheck />
</Combobox.ItemIndicator>
</Combobox.Item>
</Combobox.Popover>
</Combobox.Root>
);
});
```

### Anatomy Changes

1. **Combobox.Option** has been renamed to **Combobox.Item**:

- The item is no longer restricted to a string value; any UI can be placed inside the item.
- Use the `Combobox.ItemLabel` component to display the item's label, which becomes the item's value if no `value` prop is passed to the `Combobox.Item`. (required)

2. **Combobox.Listbox** has been deprecated.

3. **Combobox.ItemLabel** has been added:

- Move the string value that was once inside `Combobox.Option` into `Combobox.ItemLabel`. (required)

4. **Combobox.ItemIndicator** has been added:

- This component is used to render UI based on the selected state of the item. (optional)

5. **Combobox.Description** has been added:

- The text rendered inside the description component is displayed to screen readers as an accessible description of the combobox. (optional)

6. **Combobox.ErrorMessage** has been added:

- When this component is rendered, the Combobox will be marked as invalid. (optional)

7. **Combobox.HiddenNativeSelect** has been added:

- A native select element allows the submission of forms with the combobox. This component is visually hidden and hidden from screen readers. (optional)

8. **Combobox.Group** has been added:

- Used to visually group related items together. (optional)

9. **Combobox.GroupLabel** has been added:

- Provides an accessible name for the group. (optional)

10. **Combobox.Empty** has been added:
- Displays a message when there are no items to display.
- Previously, an empty popup was displayed when the combobox was empty. The new default behavior is to close the popup unless the `Combobox.Empty` component is rendered. (optional)

### API Changes

#### Rendering Items (required)

The `optionRenderer$` prop on the `Combobox.Listbox` component has been deprecated.

Instead:

1. pass a `<Combobox.Item />` as a child of the `<Combobox.Popover />` component.
2. pass a `Combobox.ItemLabel` as a child of the `<Combobox.Item />` component.

It should look something like this:

```tsx
<Combobox.Popover>
<Combobox.Item>
<Combobox.ItemLabel>item label</Combobox.ItemLabel>
{/* other content */}
</Combobox.Item>
</Combobox.Popover>
```

You are now in full control of how the item is rendered. Because you control the rendering of the item, there is no need for the previous API's including the data's key values.

> `optionDisabledKey`, `optionValueKey`, and `optionLabelKey` props have been removed.

There is also no need to pass an `index` prop to the `Combobox.Item` component. It is handled automatically.

#### Pass in distinct values

The `value` prop has been added to the `Combobox.Item` component to allow for passing in a distinct value for the combobox.

For example, identifying a user by their ID, rather than the display username.

#### Add your own filter

Filters are an important part of the combobox. It was a design decision in this refactor to make filtering data as easy as possible to integrate with other tools and libraries.

The `filter$` prop has been replaced. Instead, items are by default filtered by the `includes` function.

To opt-out of the default filter, add the `filter={false}` prop to the `Combobox.Root` component, which will disable the default filter.

```tsx
import { component$, useSignal, useStyles$, useTask$ } from '@builder.io/qwik';
import { Combobox } from '@qwik-ui/headless';
import { LuCheck, LuChevronDown } from '@qwikest/icons/lucide';
import { matchSorter } from 'match-sorter';

export default component$(() => {
useStyles$(styles);

const inputValue = useSignal('');
const filteredItems = useSignal<string[]>([]);

const fruits = [
'Apple',
'Apricot',
'Bilberry',
'Blackberry',
'Blackcurrant',
'Currant',
'Cherry',
'Coconut',
];

useTask$(({ track }) => {
track(() => inputValue.value);

filteredItems.value = matchSorter(fruits, inputValue.value);
});

return (
<Combobox.Root filter={false}>
<Combobox.Label>Fruits</Combobox.Label>
<Combobox.Control>
<Combobox.Input bind:value={inputValue} />
<Combobox.Trigger>
<LuChevronDown />
</Combobox.Trigger>
</Combobox.Control>
<Combobox.Popover gutter={8}>
{filteredItems.value.map((fruit) => (
<Combobox.Item key={fruit}>
<Combobox.ItemLabel>{fruit}</Combobox.ItemLabel>
<Combobox.ItemIndicator>
<LuCheck />
</Combobox.ItemIndicator>
</Combobox.Item>
))}
</Combobox.Popover>
</Combobox.Root>
);
});
```

The above example uses the `matchSorter` function from the `match-sorter` library to filter the items.

#### `bind:value` instead of `bind:selectedIndex`

bind:value has been added in favor of what was previously used to reactively control the combobox, bind:selectedIndex.

> This change was needed to create a more consistent API across components, but also keeping the state in the case of custom filters.

`onChange$` has been added to the `Combobox.Root` component so that you can listen to when the selected value changes.

#### Add initial values to the combobox

The `value` prop has been added to the `Combobox.Root` component to select the initial value of the combobox when it is rendered.

> `defaultLabel` has been removed, as it does not reflect the selected state of the combobox.

#### Input state management

`bind:inputValue` (on the Root) has been replaced by using the `bind:value` prop on the `<Combobox.Input />` component instead.

You can also now listen to when the input value changes by using the `onInput$` prop on the `<Combobox.Root />` component.

#### Passing refs to the combobox

The combobox is the first component to support passing refs! You can now pass a ref of your own to any component inside the combobox.

```tsx
const inputRef = useSignal<HTMLInputElement>();

<Combobox.Input ref={inputRef} />
<button onClick$={() => (inputRef.value?.focus())}>Focus input</button>
```

#### Multiple selections

You can now select multiple items by passing the `multiple` prop to the `<Combobox.Root />` component.

#### removeOnBackspace

When in multiple selection mode, and the `removeOnBackspace` prop has been added to the `Combobox.Root` component, selected items can be removed by pressing the backspace key. (when the input is empty)

#### Managing display values

`bind:displayValue` has been added to the `Combobox.Root` component to allow for grabbing the updated display values of the combobox.

> This allows for full control over each display item. For example, a couple of display values shown as pills.

#### Item indicators

The item indicator shows when the item is selected. Inside can be the UI of choice.

#### `bind:open` instead of `bind:isListboxOpen`

bind:open has been added to control the open state of the listbox, replacing bind:isListboxOpen.

`onOpenChange$` has been added to the `Combobox.Root` component so that you can listen to when the popup opens or closes.

#### Focus State Management

bind:isInputFocused has been deprecated. Instead, if you decide to manage focus state using event handlers like onFocus$ and onBlur$. OR pass a ref to the `Combobox.Input` component.

#### Placeholders

The placeholder prop has been added to the `Combobox.Root` component to allow for a custom placeholder.

#### Environment

Like many of the latest components in Qwik UI, each function of the Combobox has been optimized to run in both SSR or CSR automatically depending on the environment.

#### Looping

Looping is now opt-in by default. To enable looping, add the `loop` prop to the `Combobox.Root` component.

#### Scrolling

When a scrollbar is present, the combobox will now properly scroll to the selected item. The scroll behavior can be customized using the `scrollOptions` prop on the `Combobox.Root` component.

#### Forms

The Combobox now supports form submissions. To enable this:

1. Add the `name` prop to the `Combobox.Root` component, with the name of the Combobox form field.

2. Add the `<Combobox.HiddenNativeSelect />` component inside of the `<Combobox.Root />` component.

#### Validation

The Combobox now supports validation. It was a design decision to make validation as easy as possible to integrate with other tools and libraries, such as Modular Forms.

A component is invalid when the `Combobox.ErrorMessage` component is rendered. This component provides an accessible description of the error to assistive technologies.

### Floating / Top layer items

The props previously on the `Combobox.Listbox`, have been moved to the `Combobox.Popover` component to be more consistent with the rest of the Qwik UI components.

`placement` has been deprecated in favor of `floating`, which can be a boolean or the direction of the floating element.

`autoPlacement` has been removed, as `flip` should be used instead.

Ex: `floating={true}` or `floating="top"`

### Keyboard interactions

Key
Description

| Key | Description |
| --------- | ---------------------------------------------------- |
| Enter | Selects a highlighted item when open. |
| ArrowDown | Opens the combobox or moves focus down. |
| ArrowUp | Opens the combobox or moves focus up. |
| Home | When focus is on an item, moves focus to first item. |
| End | When focus is on an item, moves focus to last item. |
| Esc | Closes the combobox and moves focus to the trigger. |
| Tab | Moves focus to the next focusable element. |

The Enter key will toggle the selection of the highlighted item without closing the combobox if an item is already selected, otherwise it will close the popup.

### Multi Select

When in multi select mode, additional keyboard interactions are available.

| Key | Description |
| ----- | --------------------------------------------------------------------------- |
| Enter | Toggles the selection of the highlighted item without closing the combobox. |

### Data Attributes

- `data-invalid` is added to the combobox when the combobox is invalid.
- `data-open` is added to the combobox when the combobox is open.
- `data-closed` is added to the combobox when the combobox is closed.
- `data-highlighted` is added to the combobox item when the item is highlighted.
- `data-selected` is added to the combobox item when the item is selected.
- `data-disabled` is added to the combobox item when the item is disabled.

### Accessibility

Announcements to the Combobox are more consistent and follow the [WAI-ARIA Combobox design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/).

So far, the Combobox has been tested with VoiceOver, Axe, and NVDA.

## Select

The select component also includes some improvments

### Accessibility

Announcements to the Select are more consistent and follow the [WAI-ARIA Listbox design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/listbox/).

So far, the Select has been tested with VoiceOver, Axe, and NVDA.

## Dropdown

A new component has been added to Qwik UI, the Dropdown. It is currently in a draft state, and is not yet ready for production use. We will be working on it more deeply in the near future.

### Anatomy

Here is the initial API:

```tsx
import { component$ } from '@builder.io/qwik';
import { Dropdown } from '@qwik-ui/headless';
export default component$(() => {
return (
<Dropdown.Root>
<Dropdown.Trigger>
Open Dropdown
</Dropdown.Trigger>
<Dropdown.Popover>
<Dropdown.Arrow />
<Dropdown.Content>
<Dropdown.Group>
<Dropdown.GroupLabel>
Group 1
</Dropdown.GroupLabel>
</Dropdown.Group>
<Dropdown.Separator />
</Dropdown.Content>
</Dropdown.Popover>
</Dropdown.Root>
```

Feel free to play around with it! Feedback is very appreciated.

## Progress Bar

The progress bar has been around for a while, it has finally reached a **beta state**, make sure to open an issue on the [Qwik UI repo](https://github.com/qwikifiers/qwik-ui/issues) if you run into any problems.
2 changes: 1 addition & 1 deletion apps/website/adapters/cloudflare-pages/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default extendConfig(baseConfig, () => {
cloudflarePagesAdapter({
ssg: {
include: ['/*'],
origin: 'https://qwikui.com'
origin: 'https://qwikui.com',
},
}),
],
Expand Down
Loading