1- import { html , repeat } from '@microsoft/fast-element' ;
2- import { uniqueId } from '@microsoft/fast-web-utilities' ;
1+ import { html , ref , repeat } from '@microsoft/fast-element' ;
32import { type Meta , renderComponent , type StoryArgs , type StoryObj } from '../helpers.stories.js' ;
43import { colorStatusSuccessBackground3 } from '../theme/design-tokens.js' ;
54import type { Field as FluentField } from './field.js' ;
@@ -20,10 +19,35 @@ export const storyTemplate = html<StoryArgs<FluentField>>`
2019 </fluent-field>
2120` ;
2221
22+ const textInputLink = '<a href="/docs/components-textinput--docs">Text Input</a>' ;
23+ const textAreaLink = '<a href="/docs/components-textarea--docs">Text Area</a>' ;
24+
25+ const storyDescription = `
26+ The ${ textInputLink } and ${ textAreaLink } components have a specific implementation with \`<fluent-field>\` that differs from other form controls to ensure proper accessibility support.
27+
28+ **For Text Input and Text Area:**
29+ - The label must be passed as a child element (not using the field's label slot)
30+ - This implementation is required for accessibility features like zoom text and voice over to work correctly
31+ - A previous issue where the aria-hidden attribute prevented zoom text functionality on the field label has been resolved
32+
33+ **For Other Form Controls (checkbox, radio, etc.):**
34+ - The label should be placed in the field's label slot using \`slot="label"\`
35+ - This is the standard implementation pattern for most form controls
36+
37+ This distinction ensures that text-based inputs maintain proper accessibility while other controls follow the standard slotting pattern.
38+ ` ;
39+
2340export default {
2441 title : 'Components/Field' ,
2542 render : renderComponent ( storyTemplate ) ,
2643 excludeStories : [ 'storyTemplate' ] ,
44+ parameters : {
45+ docs : {
46+ description : {
47+ component : storyDescription ,
48+ } ,
49+ } ,
50+ } ,
2751 args : {
2852 label : {
2953 text : 'Example field' ,
@@ -33,7 +57,7 @@ export default {
3357 icon : ( ) => html `${ SuccessIcon } ` ,
3458 } ,
3559 labelSlottedContent : ( ) => html `< label slot ="label "> ${ story => story . label . text } </ label > ` ,
36- inputSlottedContent : ( ) => html `< fluent-text-input slot ="input "> </ fluent-text-input > ` ,
60+ inputSlottedContent : ( ) => html `< fluent-checkbox slot ="input "> </ fluent-checkbox > ` ,
3761 labelPosition : LabelPosition . above ,
3862 } ,
3963 argTypes : {
@@ -81,7 +105,7 @@ export const LabelPositions: Story = {
81105 < div >
82106 < fluent-field label-position ="${ story => story . labelPosition } ">
83107 < label slot ="label "> ${ story => story . label } </ label >
84- < fluent-text-input slot ="input "> </ fluent-text-input >
108+ < fluent-checkbox slot ="input "> </ fluent-checkbox >
85109 </ fluent-field >
86110 </ div >
87111 < br />
@@ -96,12 +120,45 @@ export const LabelPositions: Story = {
96120 } ,
97121} ;
98122
123+ export const TextInput : Story = {
124+ args : {
125+ label : {
126+ text : 'Text Input' ,
127+ } ,
128+ labelPosition : undefined ,
129+ inputSlottedContent : ( ) => html `< fluent-text-input slot ="input "> ${ story => story . label . text } </ fluent-text-input > ` ,
130+ labelSlottedContent : ( ) => html `` ,
131+ messageSlottedContent : undefined ,
132+ } ,
133+ } ;
134+
135+ export const TextInputFormSubmission : Story = {
136+ render : renderComponent ( html < StoryArgs < FluentField >> `
137+ <form
138+ style="display: inline-flex; align-items: start; flex-direction: column; gap: 20px"
139+ @reset="${ x => x . successMessage . toggleAttribute ( 'hidden' , true ) } "
140+ @submit="${ x => x . input ?. checkValidity ( ) && x . successMessage . toggleAttribute ( 'hidden' , false ) } "
141+ >
142+ <fluent-field>
143+ <fluent-text-input ${ ref ( 'input' ) } required slot="input" id="form-input"
144+ >${ story => story . label } </fluent-text-input
145+ >
146+ </fluent-field>
147+ <fluent-button type="submit">Submit</fluent-button>
148+ <span id="success-message" hidden ${ ref ( 'successMessage' ) } > Form submitted successfully! </span>
149+ </form>
150+ ` ) ,
151+ args : {
152+ label : 'Form' ,
153+ } ,
154+ } ;
155+
99156export const Required : Story = {
100157 args : {
101158 label : {
102159 text : 'Required field' ,
103160 } ,
104- inputSlottedContent : ( ) => html `< fluent-text-input required slot ="input "> </ fluent-text-input > ` ,
161+ inputSlottedContent : ( ) => html `< fluent-checkbox required slot ="input "> </ fluent-checkbox > ` ,
105162 messageSlottedContent : undefined ,
106163 } ,
107164} ;
@@ -111,24 +168,20 @@ export const DisabledControl: Story = {
111168 label : {
112169 text : 'Disabled field' ,
113170 } ,
114- inputSlottedContent : ( ) => html `< fluent-text-input disabled slot ="input "> </ fluent-text-input > ` ,
171+ inputSlottedContent : ( ) => html `< fluent-checkbox disabled slot ="input "> </ fluent-checkbox > ` ,
115172 messageSlottedContent : undefined ,
116173 } ,
117174} ;
118175
119176export const Size : Story = {
120177 render : renderComponent ( html `
121- < fluent-field size ="small ">
122- < label slot ="label " for ="field-small-size "> Small field</ label >
123- < fluent-text-input control-size ="small " slot ="input " id ="field-small-size "> </ fluent-text-input >
124- </ fluent-field >
125178 < fluent-field size ="medium ">
126179 < label slot ="label " for ="field-medium-size "> Medium field</ label >
127- < fluent-text-input control- size ="medium " slot ="input " id ="field-medium-size "> </ fluent-text-input >
180+ < fluent-checkbox size ="medium " slot ="input " id ="field-medium-size "> </ fluent-checkbox >
128181 </ fluent-field >
129182 < fluent-field size ="large ">
130183 < label slot ="label " for ="field-large-size "> Large field</ label >
131- < fluent-text-input control- size ="large " slot ="input " id ="field-large-size "> </ fluent-text-input >
184+ < fluent-checkbox size ="large " slot ="input " id ="field-large-size "> </ fluent-checkbox >
132185 </ fluent-field >
133186 ` ) ,
134187} ;
@@ -142,7 +195,10 @@ export const Hint: Story = {
142195 message : 'Sample hint text.' ,
143196 } ,
144197 inputSlottedContent : ( ) =>
145- html `< fluent-text-input slot ="input " aria-describedby ="hint-message "> </ fluent-text-input > ` ,
198+ html `< fluent-text-input slot ="input " aria-describedby ="hint-message "
199+ > ${ story => story . label . text } </ fluent-text-input
200+ > ` ,
201+ labelSlottedContent : ( ) => html `` ,
146202 messageSlottedContent : ( ) =>
147203 html `< fluent-text slot ="message " size ="200 " id ="hint-message "> ${ story => story . message ?. message } </ fluent-text > ` ,
148204 } ,
@@ -184,11 +240,6 @@ export const ComponentExamples: Story = {
184240 </ fluent-field >
185241 </ fluent-radio-group >
186242 </ fluent-field >
187- < fluent-field >
188- < label slot ="label " for ="field-textarea "> Text Area</ label >
189- < fluent-textarea slot ="input " id ="field-textarea " placeholder ="Placeholder text " resize ="both ">
190- </ fluent-textarea >
191- </ fluent-field >
192243 </ div >
193244 ` ) ,
194245} ;
@@ -197,8 +248,8 @@ export const ThirdPartyControls: Story = {
197248 render : renderComponent ( html `
198249 < form action ="# " style ="display:flex;flex-flow:column;align-items:start;gap:10px ">
199250 < fluent-field label-position ="above " style ="max-width: 400px ">
200- < label slot ="label " for ="native-text-input " > Text Input </ label >
201- < input slot ="input " id ="native-text-input " required />
251+ < label slot ="label " for ="native-color " > Color picker </ label >
252+ < input slot ="input " type =" color " id ="native-color " required />
202253 </ fluent-field >
203254 < fluent-field label-position ="after ">
204255 < label slot ="label " for ="native-checkbox "> Checkbox</ label >
0 commit comments