Skip to content

Commit 308e5bc

Browse files
docs(docs changes & few bug fixes): docs changes & few bug fixes
1 parent 9cb28e2 commit 308e5bc

File tree

5 files changed

+101
-85
lines changed

5 files changed

+101
-85
lines changed

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

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,28 +27,33 @@ export const Example01 = component$(() => {
2727
return (
2828
<PreviewCodeExample>
2929
<div q:slot="actualComponent">
30-
<AutocompleteRoot class="relative">
31-
<AutocompleteLabel>Personal Trainers ⚡</AutocompleteLabel>
32-
<AutocompleteTrigger>
33-
<AutocompleteInput />
34-
<AutocompleteButton>
30+
<AutocompleteRoot class="relative text-white">
31+
<AutocompleteLabel class="text-inherit font-semibold">
32+
Personal Trainers ⚡
33+
</AutocompleteLabel>
34+
<AutocompleteTrigger class="bg-[#1f2532] flex items-center rounded-sm border-[#7d95b3] border-[1px] relative">
35+
<AutocompleteInput class="w-44 bg-inherit px-2 pr-6" />
36+
<AutocompleteButton class="w-6 h-6 group absolute right-0">
3537
<svg
3638
xmlns="http://www.w3.org/2000/svg"
3739
viewBox="0 0 24 24"
3840
fill="none"
39-
stroke="currentColor"
4041
stroke-width="2"
42+
class="stroke-white group-aria-expanded:-rotate-180 transition-transform duration-[450ms]"
4143
stroke-linecap="round"
4244
stroke-linejoin="round"
43-
style="width: 20px; height: 20px;"
4445
>
4546
<polyline points="6 9 12 15 18 9"></polyline>
4647
</svg>
4748
</AutocompleteButton>
4849
</AutocompleteTrigger>
49-
<AutocompleteListbox class="w-full bg-black">
50+
<AutocompleteListbox class="w-full bg-[#1f2532] px-4 py-2 mt-2 rounded-sm border-[#7d95b3] border-[1px]">
5051
{trainers.map((trainer, index) => (
51-
<AutocompleteOption optionValue={trainer} key={index}>
52+
<AutocompleteOption
53+
optionValue={trainer}
54+
key={index}
55+
class="rounded-sm px-2 hover:bg-[#496080] focus:bg-[#496080]"
56+
>
5257
{trainer}
5358
</AutocompleteOption>
5459
))}

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

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,28 +25,33 @@ import { APITable } from '../../../../../components/api-table/api-table';
2525

2626
<Example01>
2727
```tsx
28-
<AutocompleteRoot class="relative">
29-
<AutocompleteLabel>Personal Trainers ⚡</AutocompleteLabel>
30-
<AutocompleteTrigger>
31-
<AutocompleteInput />
32-
<AutocompleteButton>
28+
<AutocompleteRoot class="relative text-white">
29+
<AutocompleteLabel class="text-inherit font-semibold">
30+
Personal Trainers ⚡
31+
</AutocompleteLabel>
32+
<AutocompleteTrigger class="bg-[#1f2532] flex items-center rounded-sm border-[#7d95b3] border-[1px] relative">
33+
<AutocompleteInput class="w-44 bg-inherit px-2 pr-6" />
34+
<AutocompleteButton class="w-6 h-6 group absolute right-0">
3335
<svg
3436
xmlns="http://www.w3.org/2000/svg"
3537
viewBox="0 0 24 24"
3638
fill="none"
37-
stroke="currentColor"
3839
stroke-width="2"
40+
class="stroke-white group-aria-expanded:-rotate-180 transition-transform duration-[450ms]"
3941
stroke-linecap="round"
4042
stroke-linejoin="round"
41-
style="width: 20px; height: 20px;"
4243
>
4344
<polyline points="6 9 12 15 18 9"></polyline>
4445
</svg>
4546
</AutocompleteButton>
4647
</AutocompleteTrigger>
47-
<AutocompleteListbox class="w-full bg-black">
48+
<AutocompleteListbox class="w-full bg-[#1f2532] px-4 py-2 mt-2 rounded-sm border-[#7d95b3] border-[1px]">
4849
{trainers.map((trainer, index) => (
49-
<AutocompleteOption optionValue={trainer} key={index}>
50+
<AutocompleteOption
51+
optionValue={trainer}
52+
key={index}
53+
class="rounded-sm px-2 hover:bg-[#496080] focus:bg-[#496080]"
54+
>
5055
{trainer}
5156
</AutocompleteOption>
5257
))}
@@ -93,48 +98,64 @@ import { APITable } from '../../../../../components/api-table/api-table';
9398
<KeyboardInteractionTable keyDescriptors={
9499
[
95100
{
96-
keyTitle: 'Space',
97-
description: 'Expand or collapse the `AccordionItem`.',
101+
keyTitle: 'Tab',
102+
description: 'Moves focus to the next focusable element or option.',
98103
},
99104
{
100-
keyTitle: 'Enter',
101-
description: 'Expand or collapse the `AccordionItem`.',
105+
keyTitle: 'Shift + Tab',
106+
description: 'Moves focus to the previous focusable element or option.',
102107
},
103108
{
104-
keyTitle: 'Tab',
105-
description: 'Moves focus to the next focusable element.',
109+
keyTitle: 'DownArrow',
110+
description: "Opens the listbox and moves focus to the first option. When focus is on an option, moves focus to the next option. If there isn't one, it will go back to the first option.",
106111
},
107112
{
108-
keyTitle: 'Shift + Tab',
109-
description: 'Moves focus to the previous focusable element.',
113+
keyTitle: 'UpArrow',
114+
description: "When focus is on an option, moves focus to the previous option. If there isn't one, it will go back to the last option.",
115+
},
116+
{
117+
keyTitle: 'Enter / Space',
118+
description: `Selects the option when focused. The listbox will close and the selected option becomes the input's value.`,
119+
},
120+
{
121+
keyTitle: 'Escape',
122+
description: 'Closes the listbox.',
110123
},
111124
{
112125
keyTitle: 'Home',
113-
description: 'When on `AccordionItem`, Moves focus to the first `AccordionItem`.',
126+
description: 'When the input is focused, the cursor moves to the start of the input value. When focused on any listbox option, focus is moved to the first option.',
114127
},
115128
{
116129
keyTitle: 'End',
117-
description: 'When on `AccordionItem`, Moves focus to the last `AccordionItem`.',
130+
description: 'When the input is focused, the cursor moves to the end of the input value. When focused on any listbox option, focus is moved to the last option.',
131+
},
132+
{
133+
keyTitle: 'Delete',
134+
description: 'When text is selected in the input, deletes the selected text.',
118135
},
119136

120137
]
121138
}/>
122139

140+
Adheres to the [Combobox](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/) WAI-ARIA design pattern. See the W3C Editable Combobox With List Autocomplete Example for more information.
141+
123142
## API
124143

125-
### AccordionItem
144+
By default, the API holds available attributes of both native DOM elements and custom Qwik elements. This is thanks to **QwikIntrinsicElements**. Here are some of the notable API's for this component.
145+
146+
### AutocompleteRoot
126147

127148
<APITable
128149
propDescriptors={[
129150
{
130151
name: 'class',
131152
type: 'string',
132-
description: 'CSS classes to apply to the accordion container.',
153+
description: 'CSS classes to apply to the autocomplete container.',
133154
},
134155
{
135156
name: 'style',
136157
type: 'string',
137-
description: 'CSS styles to apply to the accordion container.',
158+
description: 'inline CSS styles to apply to the autocomplete container.',
138159
},
139160
{
140161
name: 'label',

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,28 @@ export const Example01 = component$(() => {
1919
<SelectLabel class="text-white font-semibold ml-2">
2020
Qwik Fruits
2121
</SelectLabel>
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-
/>
22+
<SelectTrigger class="flex justify-between items-center px-8 bg-[#1f2532] border-[#7d95b3] border-[1px] rounded-md p-4 group peer">
23+
<SelectValue placeholder="Select a fruit! 🍹" class="text-white" />
2724
<SelectMarker class="w-6 h-6">
2825
<svg
2926
xmlns="http://www.w3.org/2000/svg"
3027
viewBox="0 0 24 24"
3128
fill="none"
3229
stroke-width="2"
33-
class="stroke-gray-700 dark:stroke-white group-aria-expanded:-rotate-180 transition-transform duration-[450ms]"
30+
class="stroke-white group-aria-expanded:-rotate-180 transition-transform duration-[450ms]"
3431
stroke-linecap="round"
3532
stroke-linejoin="round"
3633
>
3734
<polyline points="6 9 12 15 18 9"></polyline>
3835
</svg>
3936
</SelectMarker>
4037
</SelectTrigger>
41-
<SelectListBox class="bg-slate-100 dark:bg-gray-700 border-slate-200 dark:border-gray-600 mt-2 border-[1px] rounded-md">
42-
<SelectOption value="🚀 Qwik" class="p-4" />
43-
<SelectGroup class="p-4 ">
38+
<SelectListBox class="bg-[#1f2532] border-[#7d95b3] mt-2 border-[1px] rounded-md text-white">
39+
<SelectOption
40+
value="🚀 Qwik"
41+
class="p-4 hover:bg-[#496080] focus:bg-[#496080]"
42+
/>
43+
<SelectGroup class="p-4">
4444
<SelectLabel class="p-4">Fruits</SelectLabel>
4545
{[
4646
{ value: '🍎 Apple', disabled: false },
@@ -53,7 +53,7 @@ export const Example01 = component$(() => {
5353
key={option.value}
5454
value={option.value}
5555
disabled={option.disabled}
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"
56+
class="hover:bg-[#496080] focus:bg-[#496080] aria-disabled:text-red-500 aria-disabled:cursor-not-allowed rounded-sm p-4"
5757
/>
5858
);
5959
})}

packages/kit-headless/src/components/Autocomplete/autocomplete.tsx

Lines changed: 30 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,18 @@ import {
1515
useTask$,
1616
} from '@builder.io/qwik';
1717

18+
import { KeyCode } from '../../utils/key-code.type';
19+
20+
// import { Popover, PopoverContent, PopoverTrigger } from '../popover';
21+
1822
import { computePosition, flip } from '@floating-ui/dom';
1923

2024
/*
2125
2226
Input Textbox - Role Combobox
2327
Listbox - Role Listbox
2428
25-
Notes:
29+
Notes:
2630
2731
https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-autocomplete-list/
2832
@@ -126,6 +130,7 @@ interface AutocompleteContext {
126130
isExpanded: Signal<boolean>;
127131
triggerRef: Signal<HTMLElement | undefined>;
128132
listBoxRef: Signal<HTMLElement | undefined>;
133+
labelRef: Signal<HTMLElement | undefined>;
129134
listBoxId: string;
130135
inputId: string;
131136
activeOptionId: Signal<string | null>;
@@ -147,6 +152,7 @@ export const AutocompleteRoot = component$(
147152
const isExpanded = useSignal(false);
148153
const triggerRef = useSignal<HTMLElement>();
149154
const listBoxRef = useSignal<HTMLElement>();
155+
const labelRef = useSignal<HTMLElement>();
150156
const inputValue = useSignal(defaultValue ? defaultValue : '');
151157
const listBoxId = useId();
152158
const inputId = useId();
@@ -159,6 +165,7 @@ export const AutocompleteRoot = component$(
159165
isExpanded,
160166
triggerRef,
161167
listBoxRef,
168+
labelRef,
162169
inputValue,
163170
listBoxId,
164171
inputId,
@@ -203,18 +210,19 @@ export const AutocompleteRoot = component$(
203210
}
204211
});
205212

206-
// useOnWindow(
207-
// 'click',
208-
// $((e) => {
209-
// const target = e.target as HTMLElement;
210-
// if (
211-
// contextService.isExpanded.value === true &&
212-
// !target.contains(contextService.triggerRef.value as Node)
213-
// ) {
214-
// contextService.isExpanded.value = false;
215-
// }
216-
// })
217-
// );
213+
useOnWindow(
214+
'click',
215+
$((e) => {
216+
const target = e.target as HTMLElement;
217+
if (
218+
contextService.isExpanded.value === true &&
219+
!contextService.listBoxRef.value?.contains(target) &&
220+
!contextService.triggerRef.value?.contains(target)
221+
) {
222+
contextService.isExpanded.value = false;
223+
}
224+
})
225+
);
218226

219227
return (
220228
<div
@@ -237,7 +245,10 @@ export const AutocompleteRoot = component$(
237245
export type AutocompleteLabelProps = QwikIntrinsicElements['label'];
238246

239247
export const AutocompleteLabel = component$((props: AutocompleteLabelProps) => {
248+
const ref = useSignal<HTMLElement>();
240249
const contextService = useContext(AutocompleteContextId);
250+
contextService.labelRef = ref;
251+
241252
return (
242253
<label {...props} for={contextService.inputId}>
243254
<Slot />
@@ -268,17 +279,6 @@ export const AutocompleteInput = component$((props: InputProps) => {
268279
const ref = useSignal<HTMLElement>();
269280
const contextService = useContext(AutocompleteContextId);
270281

271-
/*
272-
273-
If we save the file, and then type the exact option value,
274-
then click the down arrow key to focus the first item in the array
275-
276-
it will focus the 2nd thing in the entire array, not the first thing.
277-
278-
works fine when we remount the component in storybook
279-
280-
*/
281-
282282
useTask$(({ track }) => {
283283
track(() => contextService.inputValue.value);
284284

@@ -304,8 +304,6 @@ export const AutocompleteInput = component$((props: InputProps) => {
304304
}
305305
);
306306

307-
console.log(contextService.filteredOptions);
308-
309307
// Probably better to refactor Signal type later
310308
contextService.options.map((option: Signal) => {
311309
if (
@@ -356,6 +354,7 @@ export const AutocompleteButton = component$((props: ButtonProps) => {
356354
),
357355
props.onClick$,
358356
]}
357+
tabIndex={-1}
359358
>
360359
<Slot />
361360
</button>
@@ -371,25 +370,18 @@ export const AutocompleteListbox = component$((props: ListboxProps) => {
371370
const contextService = useContext(AutocompleteContextId);
372371
contextService.listBoxRef = ref;
373372

374-
// useStylesScoped$(`
375-
// ul {
376-
// width: 100%;
377-
// padding-left: 0;
378-
// margin-top: 0px;
379-
// }
380-
// `);
381-
382373
return (
383374
<ul
384375
id={contextService.listBoxId}
385376
ref={ref}
386377
style={`
387-
display: ${
388-
contextService.isExpanded.value ? 'block' : 'none'
389-
}; position: absolute; ${props.style}
390-
`}
378+
display: ${
379+
contextService.isExpanded.value ? 'block' : 'none'
380+
}; position: absolute; z-index: 1; ${props.style}
381+
`}
391382
role="listbox"
392383
{...props}
384+
// aria-label={!contextService.labelRef.value ? contextService.inputValue.value : undefined}
393385
onKeyDown$={(e) => {
394386
const availableOptions = contextService.filteredOptions.map(
395387
(option) => option.value
@@ -447,7 +439,6 @@ export const AutocompleteOption = component$((props: OptionProps) => {
447439
if (e.key === 'Enter' || e.key === ' ') {
448440
contextService.inputValue.value = props.optionValue;
449441
contextService.isExpanded.value = false;
450-
console.log('inside option!');
451442
const inputElement = contextService.triggerRef.value
452443
?.firstElementChild as HTMLElement;
453444
inputElement?.focus();

0 commit comments

Comments
 (0)