Skip to content

Commit 76f8853

Browse files
Merge pull request #342 from thejackshelton/add-headless-autocomplete
Autocomplete critical ux changes and docs design changes
2 parents 58327ac + 9cb28e2 commit 76f8853

File tree

9 files changed

+470
-47
lines changed

9 files changed

+470
-47
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { component$, Slot } from '@builder.io/qwik';
2+
import {
3+
AutocompleteRoot,
4+
AutocompleteLabel,
5+
AutocompleteTrigger,
6+
AutocompleteInput,
7+
AutocompleteButton,
8+
AutocompleteListbox,
9+
AutocompleteOption,
10+
} from '@qwik-ui/headless';
11+
import { PreviewCodeExample } from '../../../../../components/preview-code-example/preview-code-example';
12+
13+
const trainers = [
14+
'Caleb',
15+
'Olivia',
16+
'James',
17+
'Ava',
18+
'Noah',
19+
'Emma',
20+
'Oliver',
21+
'Amelia',
22+
'Theodore',
23+
'Elizabeth',
24+
];
25+
26+
export const Example01 = component$(() => {
27+
return (
28+
<PreviewCodeExample>
29+
<div q:slot="actualComponent">
30+
<AutocompleteRoot class="relative">
31+
<AutocompleteLabel>Personal Trainers ⚡</AutocompleteLabel>
32+
<AutocompleteTrigger>
33+
<AutocompleteInput />
34+
<AutocompleteButton>
35+
<svg
36+
xmlns="http://www.w3.org/2000/svg"
37+
viewBox="0 0 24 24"
38+
fill="none"
39+
stroke="currentColor"
40+
stroke-width="2"
41+
stroke-linecap="round"
42+
stroke-linejoin="round"
43+
style="width: 20px; height: 20px;"
44+
>
45+
<polyline points="6 9 12 15 18 9"></polyline>
46+
</svg>
47+
</AutocompleteButton>
48+
</AutocompleteTrigger>
49+
<AutocompleteListbox class="w-full bg-black">
50+
{trainers.map((trainer, index) => (
51+
<AutocompleteOption optionValue={trainer} key={index}>
52+
{trainer}
53+
</AutocompleteOption>
54+
))}
55+
</AutocompleteListbox>
56+
</AutocompleteRoot>
57+
</div>
58+
59+
<div q:slot="codeExample">
60+
<Slot />
61+
</div>
62+
</PreviewCodeExample>
63+
);
64+
});
65+
66+
export const Example02 = component$(() => {
67+
return <PreviewCodeExample></PreviewCodeExample>;
68+
});
69+
70+
export const Example03 = component$(() => {
71+
return <PreviewCodeExample></PreviewCodeExample>;
72+
});
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
---
2+
title: Qwik UI | Autocomplete
3+
---
4+
5+
import {
6+
AutocompleteRoot,
7+
AutocompleteLabel,
8+
AutocompleteTrigger,
9+
AutocompleteInput,
10+
AutocompleteButton,
11+
AutocompleteListbox,
12+
AutocompleteOption,
13+
} from '@qwik-ui/headless';
14+
15+
import { Example01, Example02, Example03 } from './examples';
16+
import { CodeExample } from '../../../../../components/code-example/code-example';
17+
import { KeyboardInteractionTable } from '../../../../../components/keyboard-interaction-table/keyboard-interaction-table';
18+
import { APITable } from '../../../../../components/api-table/api-table';
19+
20+
# Autocomplete
21+
22+
#### A combobox pattern that allows users to select from a list of previously searched terms, ensuring that the listbox popup is not automatically triggered but only when a character is typed in or an open command is given.
23+
24+
{' '}
25+
26+
<Example01>
27+
```tsx
28+
<AutocompleteRoot class="relative">
29+
<AutocompleteLabel>Personal Trainers ⚡</AutocompleteLabel>
30+
<AutocompleteTrigger>
31+
<AutocompleteInput />
32+
<AutocompleteButton>
33+
<svg
34+
xmlns="http://www.w3.org/2000/svg"
35+
viewBox="0 0 24 24"
36+
fill="none"
37+
stroke="currentColor"
38+
stroke-width="2"
39+
stroke-linecap="round"
40+
stroke-linejoin="round"
41+
style="width: 20px; height: 20px;"
42+
>
43+
<polyline points="6 9 12 15 18 9"></polyline>
44+
</svg>
45+
</AutocompleteButton>
46+
</AutocompleteTrigger>
47+
<AutocompleteListbox class="w-full bg-black">
48+
{trainers.map((trainer, index) => (
49+
<AutocompleteOption optionValue={trainer} key={index}>
50+
{trainer}
51+
</AutocompleteOption>
52+
))}
53+
</AutocompleteListbox>
54+
</AutocompleteRoot>
55+
```
56+
</Example01>
57+
58+
## Building blocks
59+
60+
<CodeExample>
61+
```tsx
62+
import { component$ } from '@builder.io/qwik';
63+
import { AutocompleteRoot, AutocompleteLabel, AutocompleteTrigger, AutocompleteInput, AutocompleteButton, AutocompleteListbox, AutocompleteOption } from '@qwik-ui/headless';
64+
65+
export default component$(() => (
66+
<AutocompleteRoot>
67+
<AutocompleteLabel>Label</AutocompleteLabel>
68+
<AutocompleteTrigger>
69+
<AutocompleteInput />
70+
<AutocompleteButton>
71+
Button Marker
72+
</AutocompleteButton>
73+
</AutocompleteTrigger>
74+
<AutocompleteListbox>
75+
<AutocompleteOption />
76+
</AutocompleteListbox>
77+
</AutocompleteRoot>
78+
))
79+
```
80+
81+
</CodeExample>
82+
83+
## Examples
84+
85+
### EXAMPLE: Frequently Asked Questions
86+
87+
<Example02>```tsx ```</Example02>
88+
89+
## Accessibility
90+
91+
### Keyboard interaction
92+
93+
<KeyboardInteractionTable keyDescriptors={
94+
[
95+
{
96+
keyTitle: 'Space',
97+
description: 'Expand or collapse the `AccordionItem`.',
98+
},
99+
{
100+
keyTitle: 'Enter',
101+
description: 'Expand or collapse the `AccordionItem`.',
102+
},
103+
{
104+
keyTitle: 'Tab',
105+
description: 'Moves focus to the next focusable element.',
106+
},
107+
{
108+
keyTitle: 'Shift + Tab',
109+
description: 'Moves focus to the previous focusable element.',
110+
},
111+
{
112+
keyTitle: 'Home',
113+
description: 'When on `AccordionItem`, Moves focus to the first `AccordionItem`.',
114+
},
115+
{
116+
keyTitle: 'End',
117+
description: 'When on `AccordionItem`, Moves focus to the last `AccordionItem`.',
118+
},
119+
120+
]
121+
}/>
122+
123+
## API
124+
125+
### AccordionItem
126+
127+
<APITable
128+
propDescriptors={[
129+
{
130+
name: 'class',
131+
type: 'string',
132+
description: 'CSS classes to apply to the accordion container.',
133+
},
134+
{
135+
name: 'style',
136+
type: 'string',
137+
description: 'CSS styles to apply to the accordion container.',
138+
},
139+
{
140+
name: 'label',
141+
type: 'string',
142+
description: 'The label to give to the accordion item.',
143+
},
144+
{
145+
name: 'onClick$',
146+
type: 'PropFunction<() => void>',
147+
description: 'A custom click handler',
148+
},
149+
]}
150+
/>

apps/website/src/routes/docs/headless/(components)/select/examples.tsx

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,38 @@ import {
99
SelectOption,
1010
SelectGroup,
1111
} from '@qwik-ui/headless';
12-
import { PreviewCodeExample } from 'apps/website/src/components/preview-code-example/preview-code-example';
12+
import { PreviewCodeExample } from '../../../../../components/preview-code-example/preview-code-example';
1313

1414
export const Example01 = component$(() => {
1515
return (
1616
<PreviewCodeExample>
1717
<div q:slot="actualComponent">
18-
<SelectRoot>
19-
<SelectLabel class="text-black dark:text-white">
20-
Lorem ipsum dolor sit amet
18+
<SelectRoot class="dark:bg-gray-700">
19+
<SelectLabel class="text-white font-semibold ml-2">
20+
Qwik Fruits
2121
</SelectLabel>
22-
<SelectTrigger class="flex justify-between items-center border-slate-200 dark:border-gray-600 border-[1px] p-4">
23-
<SelectValue placeholder="Select an option! ⚡" />
22+
<SelectTrigger class="flex justify-between items-center px-8 bg-slate-100 dark:bg-gray-700 border-slate-200 dark:border-gray-600 border-[1px] rounded-md p-4 group peer">
23+
<SelectValue
24+
placeholder="Select a fruit! 🍹"
25+
class="text-gray-700 dark:text-white"
26+
/>
2427
<SelectMarker class="w-6 h-6">
2528
<svg
2629
xmlns="http://www.w3.org/2000/svg"
2730
viewBox="0 0 24 24"
2831
fill="none"
29-
stroke="currentColor"
3032
stroke-width="2"
33+
class="stroke-gray-700 dark:stroke-white group-aria-expanded:-rotate-180 transition-transform duration-[450ms]"
3134
stroke-linecap="round"
3235
stroke-linejoin="round"
3336
>
3437
<polyline points="6 9 12 15 18 9"></polyline>
3538
</svg>
3639
</SelectMarker>
3740
</SelectTrigger>
38-
<SelectListBox class="bg-slate-100 dark:bg-gray-700 border-slate-200 dark:border-gray-600 border-[1px]">
41+
<SelectListBox class="bg-slate-100 dark:bg-gray-700 border-slate-200 dark:border-gray-600 mt-2 border-[1px] rounded-md">
3942
<SelectOption value="🚀 Qwik" class="p-4" />
40-
<SelectGroup class="p-4">
43+
<SelectGroup class="p-4 ">
4144
<SelectLabel class="p-4">Fruits</SelectLabel>
4245
{[
4346
{ value: '🍎 Apple', disabled: false },
@@ -50,7 +53,7 @@ export const Example01 = component$(() => {
5053
key={option.value}
5154
value={option.value}
5255
disabled={option.disabled}
53-
class="aria-disabled:text-red-500 aria-disabled:cursor-not-allowed hover:bg-slate-300 dark:hover:bg-gray-600 p-4"
56+
class="aria-disabled:text-red-500 aria-disabled:cursor-not-allowed hover:bg-slate-200 rounded-sm dark:hover:bg-gray-600 p-4"
5457
/>
5558
);
5659
})}
@@ -71,14 +74,17 @@ export const Example02 = component$(() => {
7174
<PreviewCodeExample>
7275
<div q:slot="actualComponent">
7376
<SelectRoot>
74-
<SelectTrigger class="flex justify-between items-center border-slate-200 dark:border-gray-600 border-[1px] p-4">
75-
<SelectValue placeholder="Home" />
77+
<SelectTrigger class="flex justify-between items-center bg-slate-100 dark:bg-gray-700 border-slate-200 dark:border-gray-600 border-[1px] p-4">
78+
<SelectValue
79+
placeholder="Home"
80+
class="text-gray-700 dark:text-white"
81+
/>
7682
<SelectMarker class="w-6 h-6">
7783
<svg
7884
xmlns="http://www.w3.org/2000/svg"
7985
viewBox="0 0 24 24"
8086
fill="none"
81-
stroke="currentColor"
87+
class="stroke-gray-700 dark:stroke-white"
8288
stroke-width="2"
8389
stroke-linecap="round"
8490
stroke-linejoin="round"

apps/website/src/routes/docs/headless/(components)/select/index.mdx

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,30 @@ import { APITable } from '../../../../../components/api-table/api-table';
1515

1616
<Example01>
1717
```tsx
18-
<SelectRoot>
19-
<SelectLabel class="text-black dark:text-white">
20-
Lorem ipsum dolor sit amet
21-
</SelectLabel>
22-
<SelectTrigger class="flex justify-between items-center border-slate-200 dark:border-gray-600 border-[1px] p-4">
23-
<SelectValue placeholder="Select an option! ⚡" />
18+
<SelectRoot class="dark:bg-gray-700">
19+
<SelectLabel class="text-white font-semibold ml-2">Qwik Fruits</SelectLabel>
20+
<SelectTrigger class="flex justify-between items-center px-8 bg-slate-100 dark:bg-gray-700 border-slate-200 dark:border-gray-600 border-[1px] rounded-md p-4 group peer">
21+
<SelectValue
22+
placeholder="Select a fruit! 🍹"
23+
class="text-gray-700 dark:text-white"
24+
/>
2425
<SelectMarker class="w-6 h-6">
2526
<svg
2627
xmlns="http://www.w3.org/2000/svg"
2728
viewBox="0 0 24 24"
2829
fill="none"
29-
stroke="currentColor"
3030
stroke-width="2"
31+
class="stroke-gray-700 dark:stroke-white group-aria-expanded:-rotate-180 transition-transform duration-[450ms]"
3132
stroke-linecap="round"
3233
stroke-linejoin="round"
3334
>
3435
<polyline points="6 9 12 15 18 9"></polyline>
3536
</svg>
3637
</SelectMarker>
3738
</SelectTrigger>
38-
<SelectListBox class="bg-slate-100 dark:bg-gray-700 border-slate-200 dark:border-gray-600 border-[1px]">
39+
<SelectListBox class="bg-slate-100 dark:bg-gray-700 border-slate-200 dark:border-gray-600 mt-2 border-[1px] rounded-md">
3940
<SelectOption value="🚀 Qwik" class="p-4" />
40-
<SelectGroup class="p-4">
41+
<SelectGroup class="p-4 ">
4142
<SelectLabel class="p-4">Fruits</SelectLabel>
4243
{[
4344
{ value: '🍎 Apple', disabled: false },
@@ -50,7 +51,7 @@ import { APITable } from '../../../../../components/api-table/api-table';
5051
key={option.value}
5152
value={option.value}
5253
disabled={option.disabled}
53-
class="aria-disabled:text-red-500 aria-disabled:cursor-not-allowed hover:bg-slate-300 dark:hover:bg-gray-600 p-4"
54+
class="aria-disabled:text-red-500 aria-disabled:cursor-not-allowed hover:bg-slate-200 rounded-sm dark:hover:bg-gray-600 p-4"
5455
/>
5556
);
5657
})}
@@ -199,7 +200,7 @@ import { APITable } from '../../../../../components/api-table/api-table';
199200
{
200201
name: 'disabled',
201202
type: 'boolean',
202-
description: 'Use this property to disabled the `SelectTrigger` element.',
203+
description: 'Use this property to disable the `SelectTrigger` element.',
203204
},
204205
]}
205206
/>
@@ -281,7 +282,7 @@ import { APITable } from '../../../../../components/api-table/api-table';
281282
{
282283
name: 'disabled',
283284
type: 'boolean',
284-
description: 'Use this property to disabled the `SelectGroup` element.',
285+
description: 'Use this property to disable the `SelectGroup` element.',
285286
},
286287
]}
287288
/>
@@ -322,7 +323,7 @@ import { APITable } from '../../../../../components/api-table/api-table';
322323
{
323324
name: 'disabled',
324325
type: 'boolean',
325-
description: 'Use this property to disabled the `SelectOption` element.',
326+
description: 'Use this property to disable the `SelectOption` element.',
326327
},
327328
{
328329
name: 'value',

apps/website/src/routes/docs/headless/menu.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
## Components
1010

1111
- [Accordion](/docs/headless/accordion)
12+
- [Autocomplete](/docs/headless/autocomplete)
1213
- [Carousel](/docs/headless/carousel)
1314
- [Popover](/docs/headless/popover)
1415
- [Select](/docs/headless/select)

0 commit comments

Comments
 (0)