Skip to content

Commit 99e31e6

Browse files
feat(initial api additions & scaffolding): initial API, Setting up types & props
1 parent b581c88 commit 99e31e6

File tree

3 files changed

+210
-0
lines changed

3 files changed

+210
-0
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { component$ } from '@builder.io/qwik';
2+
import {
3+
Autocomplete,
4+
AutocompleteLabel,
5+
AutocompleteInput,
6+
AutocompleteButton,
7+
AutocompleteListbox,
8+
AutocompleteOption,
9+
} from './autocomplete';
10+
11+
const RegularAutocomplete = component$(() => {
12+
return (
13+
<>
14+
<AutocompleteLabel>Label</AutocompleteLabel>
15+
<Autocomplete>
16+
<div>
17+
<AutocompleteInput />
18+
<AutocompleteButton class="test">
19+
<svg
20+
xmlns="http://www.w3.org/2000/svg"
21+
viewBox="0 0 24 24"
22+
fill="none"
23+
stroke="currentColor"
24+
stroke-width="2"
25+
stroke-linecap="round"
26+
stroke-linejoin="round"
27+
style="width: 20px; height: 20px;"
28+
>
29+
<polyline points="6 9 12 15 18 9"></polyline>
30+
</svg>
31+
</AutocompleteButton>
32+
</div>
33+
<AutocompleteListbox>
34+
<AutocompleteOption>Option 1</AutocompleteOption>
35+
<AutocompleteOption>Option 2</AutocompleteOption>
36+
<AutocompleteOption>Option 3</AutocompleteOption>
37+
</AutocompleteListbox>
38+
</Autocomplete>
39+
</>
40+
);
41+
});
42+
43+
describe('Autocomplete', () => {
44+
it('INIT', () => {
45+
cy.mount(<RegularAutocomplete />);
46+
47+
cy.checkA11yForComponent();
48+
});
49+
});
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { Meta, StoryObj } from 'storybook-framework-qwik';
2+
import { userEvent, within } from '@storybook/testing-library';
3+
import { expect } from '@storybook/jest';
4+
5+
import {
6+
Autocomplete,
7+
AutocompleteLabel,
8+
AutocompleteInput,
9+
AutocompleteButton,
10+
AutocompleteListbox,
11+
AutocompleteOption,
12+
} from './autocomplete';
13+
14+
const meta: Meta<AutocompleteProps> = {
15+
component: Autocomplete,
16+
};
17+
18+
type Story = StoryObj<{}>;
19+
20+
export const Primary: Story = {
21+
args: {},
22+
render: (args) => (
23+
<>
24+
<AutocompleteLabel>Label</AutocompleteLabel>
25+
<Autocomplete>
26+
<div>
27+
<AutocompleteInput />
28+
<AutocompleteButton class="test">
29+
<svg
30+
xmlns="http://www.w3.org/2000/svg"
31+
viewBox="0 0 24 24"
32+
fill="none"
33+
stroke="currentColor"
34+
stroke-width="2"
35+
stroke-linecap="round"
36+
stroke-linejoin="round"
37+
style="width: 20px; height: 20px;"
38+
>
39+
<polyline points="6 9 12 15 18 9"></polyline>
40+
</svg>
41+
</AutocompleteButton>
42+
</div>
43+
<AutocompleteListbox>
44+
<AutocompleteOption>Option 1</AutocompleteOption>
45+
<AutocompleteOption>Option 2</AutocompleteOption>
46+
<AutocompleteOption>Option 3</AutocompleteOption>
47+
</AutocompleteListbox>
48+
</Autocomplete>
49+
</>
50+
),
51+
play: ({ canvasElement, args }) => {
52+
const canvas = within(canvasElement);
53+
},
54+
};
55+
56+
export default meta;
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import {
2+
component$,
3+
Slot,
4+
useContext,
5+
useContextProvider,
6+
createContextId,
7+
useSignal,
8+
Signal,
9+
HTMLAttributes,
10+
} from '@builder.io/qwik';
11+
12+
/*
13+
14+
TODO:
15+
16+
Input Textbox - Role Combobox
17+
Listbox - Role Listbox
18+
19+
Notes:
20+
21+
https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-autocomplete-list/
22+
23+
https://headlessui.com/react/combobox
24+
25+
https://thejackshelton.notion.site/Combobox-e82cae2325914c3c82d4cbfcab574825?pvs=4
26+
27+
28+
Current thoughts for API implementation
29+
30+
<AutocompleteLabel />
31+
<Autocomplete>
32+
<div>
33+
<AutocompleteInput />
34+
<AutocompleteButton />
35+
</div>
36+
<AutoCompleteListbox>
37+
<AutoCompleteOption />
38+
</AutoCompleteListbox>
39+
</Autocomplete>
40+
41+
Side note: This is based off of both the ARIA combobox pattern linked above and headless UI
42+
43+
In Headless UI Label is automatically the input value, unless specified otherwise
44+
45+
*/
46+
47+
// Taken similar props from select + input Value
48+
interface AutocompleteContext {
49+
options: Signal<HTMLElement | undefined>[];
50+
selectedOption: Signal<string>;
51+
isExpanded: Signal<boolean>;
52+
triggerRef: Signal<HTMLElement | undefined>;
53+
listBoxRef: Signal<HTMLElement | undefined>;
54+
inputValue: Signal<string>;
55+
}
56+
57+
export type ButtonProps = {
58+
disabled?: boolean;
59+
} & HTMLAttributes<HTMLElement>;
60+
61+
export const AutocompleteContextId =
62+
createContextId<AutocompleteContext>('autocomplete');
63+
64+
export const Autocomplete = component$(() => {
65+
return (
66+
<div>
67+
<Slot />
68+
</div>
69+
);
70+
});
71+
72+
export const AutocompleteLabel = component$(() => {
73+
return (
74+
<label for="autocomplete-test">
75+
<Slot />
76+
</label>
77+
);
78+
});
79+
80+
// Add required context here
81+
export const AutocompleteInput = component$(() => {
82+
return <input id="autocomplete-test" role="combobox" />;
83+
});
84+
85+
export const AutocompleteButton = component$(
86+
({ disabled, ...props }: ButtonProps) => {
87+
return (
88+
<button>
89+
<Slot />
90+
</button>
91+
);
92+
}
93+
);
94+
95+
export const AutocompleteListbox = component$(() => {
96+
return <ul role="listbox"></ul>;
97+
});
98+
99+
export const AutocompleteOption = component$(() => {
100+
return (
101+
<li role="option">
102+
<Slot />
103+
</li>
104+
);
105+
});

0 commit comments

Comments
 (0)