11import type { Meta , StoryObj } from '@storybook/react-vite' ;
2- import { Autocomplete } from './Autocomplete' ;
2+ import { Autocomplete , type AutocompleteProps } from './Autocomplete' ;
33
4- const options = [ 'England' , 'Scotland' , 'Wales' , 'Northern Ireland' ] ;
4+ type AutocompleteOptionSet = 'countries' | 'cities' | 'empty' ;
55
6- const meta : Meta < typeof Autocomplete > = {
6+ type AutocompleteStoryArgs = AutocompleteProps & {
7+ optionSet ?: AutocompleteOptionSet ;
8+ } ;
9+
10+ const countriesOptions = [ 'England' , 'Scotland' , 'Wales' , 'Northern Ireland' ] ;
11+
12+ const citiesOptions = [
13+ 'London' ,
14+ 'Manchester' ,
15+ 'Birmingham' ,
16+ 'Leeds' ,
17+ 'Liverpool' ,
18+ ] ;
19+
20+ const optionSets : Record < AutocompleteOptionSet , string [ ] > = {
21+ countries : countriesOptions ,
22+ cities : citiesOptions ,
23+ empty : [ ] ,
24+ } ;
25+
26+ const defaultStoryCode = `import { Autocomplete } from '@ourfuturehealth/react-components';
27+
28+ const countriesOptions = ['England', 'Scotland', 'Wales', 'Northern Ireland'];
29+
30+ <Autocomplete
31+ hint="Start typing to filter the list."
32+ label="Country"
33+ name="country"
34+ options={countriesOptions}
35+ />;
36+ ` ;
37+
38+ const withErrorStoryCode = `import { Autocomplete } from '@ourfuturehealth/react-components';
39+
40+ const countriesOptions = ['England', 'Scotland', 'Wales', 'Northern Ireland'];
41+
42+ <Autocomplete
43+ errorMessage="Select a country from the list or enter a new one."
44+ hint="Start typing to filter the list."
45+ label="Country"
46+ name="country"
47+ options={countriesOptions}
48+ />;
49+ ` ;
50+
51+ const fixedWidthStoryCode = `import { Autocomplete } from '@ourfuturehealth/react-components';
52+
53+ const countriesOptions = ['England', 'Scotland', 'Wales', 'Northern Ireland'];
54+
55+ <Autocomplete
56+ hint="Start typing to filter the list."
57+ inputWidth={20}
58+ label="Country"
59+ name="country"
60+ options={countriesOptions}
61+ />;
62+ ` ;
63+
64+ const customNoResultsStoryCode = `import { Autocomplete } from '@ourfuturehealth/react-components';
65+
66+ const countriesOptions = ['England', 'Scotland', 'Wales', 'Northern Ireland'];
67+
68+ <Autocomplete
69+ hint="Start typing to filter the list."
70+ label="Country"
71+ name="country"
72+ noResultsText="No matching countries. Enter a new country instead."
73+ options={countriesOptions}
74+ />;
75+ ` ;
76+
77+ const renderAutocompleteStory = ( {
78+ optionSet,
79+ options = countriesOptions ,
80+ ...args
81+ } : AutocompleteStoryArgs ) => {
82+ const resolvedOptions =
83+ optionSet === undefined ? options : optionSets [ optionSet ] ;
84+
85+ return < Autocomplete { ...args } options = { resolvedOptions } /> ;
86+ } ;
87+
88+ const meta : Meta < AutocompleteStoryArgs > = {
789 title : 'Components/Input/Autocomplete' ,
890 component : Autocomplete ,
991 parameters : {
@@ -12,15 +94,15 @@ const meta: Meta<typeof Autocomplete> = {
1294 docs : {
1395 description : {
1496 component :
15- 'An input with inline suggestions that reuses the toolkit autocomplete classes, shared input-family label treatment, and suggestions list styling. The component manages the suggestion menu internally and supports both controlled and uncontrolled input values .' ,
97+ 'Use Autocomplete for a text field that suggests matches while the user types. The React API stays small: pass a label, an array of suggestion strings, and the usual form-field props such as hint, error message, width, and `defaultValue` or `value` when you need them. Use the `Builder` story to explore the component with friendly preset controls, and use the other stories as fixed examples .' ,
1698 } ,
1799 } ,
18100 } ,
19101 tags : [ 'autodocs' ] ,
20102 args : {
21103 label : 'Country' ,
22104 name : 'country' ,
23- options,
105+ options : countriesOptions ,
24106 } ,
25107 argTypes : {
26108 label : {
@@ -29,28 +111,41 @@ const meta: Meta<typeof Autocomplete> = {
29111 } ,
30112 hint : {
31113 control : 'text' ,
32- description : 'Optional supporting text shown below the label and above any error message.' ,
114+ description :
115+ 'Optional supporting text shown below the label and above any error message.' ,
33116 } ,
34117 errorMessage : {
35118 control : 'text' ,
36- description : 'Validation message shown above the input. When present, the input is marked invalid and linked with `aria-describedby`.' ,
119+ description :
120+ 'Validation message shown above the input. When present, the input is marked invalid and linked with `aria-describedby`.' ,
37121 } ,
38122 name : {
39123 control : 'text' ,
40124 description : 'HTML name submitted with the form.' ,
41125 } ,
42126 options : {
43- control : 'object' ,
44- description : 'Plain-text options used to build the suggestions list. Edit this as a simple array of strings.' ,
127+ control : false ,
128+ description :
129+ 'Suggestion strings used to build the autocomplete menu. Pass a plain array of labels, for example country names or common answers.' ,
45130 table : {
46131 type : {
47132 summary : 'string[]' ,
48133 } ,
49134 } ,
50135 } ,
136+ optionSet : {
137+ control : 'select' ,
138+ options : [ 'countries' , 'cities' , 'empty' ] ,
139+ description :
140+ 'Storybook-only helper for the Builder story. Switches between preset suggestion lists without editing the real `options` array.' ,
141+ table : {
142+ disable : true ,
143+ } ,
144+ } ,
51145 noResultsText : {
52146 control : 'text' ,
53- description : 'Override for the no-results message shown when the query matches no suggestions.' ,
147+ description :
148+ 'Override for the no-results message shown when the query matches no suggestions.' ,
54149 } ,
55150 width : {
56151 control : 'select' ,
@@ -62,16 +157,24 @@ const meta: Meta<typeof Autocomplete> = {
62157 'one-third' ,
63158 'one-quarter' ,
64159 ] ,
65- description : 'Responsive width utility for the autocomplete field, including its suggestions dropdown.' ,
160+ description :
161+ 'Responsive width utility for the autocomplete field, including its suggestions dropdown.' ,
66162 } ,
67163 inputWidth : {
68164 control : 'select' ,
69165 options : [ 2 , 3 , 4 , 5 , 10 , 20 , 30 ] ,
70- description : 'Fixed character-width modifier that helps signal the expected answer length and also constrains the suggestions dropdown width.' ,
166+ description :
167+ 'Fixed character-width modifier that helps signal the expected answer length and also constrains the suggestions dropdown width.' ,
71168 } ,
72169 describedBy : {
73170 control : 'text' ,
74- description : 'Additional element IDs to append to the component-generated `aria-describedby` value.' ,
171+ description :
172+ 'Additional element IDs to append to the component-generated `aria-describedby` value.' ,
173+ } ,
174+ isPageHeading : {
175+ control : 'boolean' ,
176+ description :
177+ 'Wrap the label in an `h1` when this question is also the page heading.' ,
75178 } ,
76179 onOptionSelect : {
77180 control : false ,
@@ -133,25 +236,114 @@ export const Default: Story = {
133236 args : {
134237 hint : 'Start typing to filter the list.' ,
135238 } ,
239+ parameters : {
240+ controls : {
241+ disable : true ,
242+ } ,
243+ docs : {
244+ description : {
245+ story :
246+ 'A realistic autocomplete example with a small set of country suggestions.' ,
247+ } ,
248+ source : {
249+ code : defaultStoryCode ,
250+ } ,
251+ } ,
252+ } ,
253+ } ;
254+
255+ export const Builder : Story = {
256+ args : {
257+ hint : 'Start typing to filter the list.' ,
258+ label : 'Country' ,
259+ name : 'country' ,
260+ optionSet : 'countries' ,
261+ options : countriesOptions ,
262+ } ,
263+ render : renderAutocompleteStory ,
264+ parameters : {
265+ controls : {
266+ include : [
267+ 'label' ,
268+ 'hint' ,
269+ 'errorMessage' ,
270+ 'name' ,
271+ 'optionSet' ,
272+ 'noResultsText' ,
273+ 'width' ,
274+ 'inputWidth' ,
275+ 'describedBy' ,
276+ 'isPageHeading' ,
277+ ] ,
278+ } ,
279+ docs : {
280+ description : {
281+ story :
282+ 'Use the friendly controls here to explore the component with preset suggestion lists and the most useful visible props.' ,
283+ } ,
284+ } ,
285+ } ,
136286} ;
137287
138288export const WithError : Story = {
139289 args : {
140290 errorMessage : 'Select a country from the list or enter a new one.' ,
141291 hint : 'Start typing to filter the list.' ,
142292 } ,
293+ parameters : {
294+ controls : {
295+ disable : true ,
296+ } ,
297+ docs : {
298+ description : {
299+ story :
300+ 'Shows how the component presents a validation message above the input.' ,
301+ } ,
302+ source : {
303+ code : withErrorStoryCode ,
304+ } ,
305+ } ,
306+ } ,
143307} ;
144308
145309export const FixedWidth : Story = {
146310 args : {
147311 hint : 'Start typing to filter the list.' ,
148312 inputWidth : 20 ,
149313 } ,
314+ parameters : {
315+ controls : {
316+ disable : true ,
317+ } ,
318+ docs : {
319+ description : {
320+ story :
321+ 'Shows the fixed character-width modifier that signals the expected answer length.' ,
322+ } ,
323+ source : {
324+ code : fixedWidthStoryCode ,
325+ } ,
326+ } ,
327+ } ,
150328} ;
151329
152330export const CustomNoResultsText : Story = {
153331 args : {
154332 hint : 'Start typing to filter the list.' ,
155333 noResultsText : 'No matching countries. Enter a new country instead.' ,
156334 } ,
335+ parameters : {
336+ controls : {
337+ disable : true ,
338+ } ,
339+ docs : {
340+ description : {
341+ story :
342+ 'Shows how to override the empty-results message when you need service-specific wording.' ,
343+ } ,
344+ source : {
345+ code : customNoResultsStoryCode ,
346+ } ,
347+ } ,
348+ } ,
157349} ;
0 commit comments