From a38c5e2349b0d02279e6d4b5c1795cb0d6dd260f Mon Sep 17 00:00:00 2001 From: Ben Howell Date: Mon, 15 Aug 2022 14:23:49 -0700 Subject: [PATCH 01/11] Initial field spec --- packages/react-components/react-field/Spec.md | 299 ++++++++++++++++-- 1 file changed, 271 insertions(+), 28 deletions(-) diff --git a/packages/react-components/react-field/Spec.md b/packages/react-components/react-field/Spec.md index b232ee1f411d7..2d3630af9154e 100644 --- a/packages/react-components/react-field/Spec.md +++ b/packages/react-components/react-field/Spec.md @@ -2,7 +2,9 @@ ## Background -_Description and use cases of this component_ +`Field` is used to add a label, validation text, and helper text to form input components, such as `Input`, `Combobox`, `RadioGroup`, etc. + +Epic issue tracking implementation: https://github.com/microsoft/fluentui/issues/19627 ## Prior Art @@ -14,50 +16,291 @@ _Include background research done for this component_ ## Sample Code -_Provide some representative example code that uses the proposed API for the component_ +Each input component has a field version (such as `InputField`, `ComboboxField`, etc.) that includes the features of Field added to that component. + +```jsx +<> + + {/* Other component types are supported, such as: */} + + + + + + + + + + + + + +``` + +These field versions of the components use a common set of Field hooks, and can be defined using very little code. + +```ts +export type InputFieldProps = FieldProps; + +export const InputField: ForwardRefComponent = React.forwardRef((props, ref) => { + const state = useField_unstable(props, ref, Input); + useFieldStyles_unstable(state); + return renderField_unstable(state); +}); + +InputField.displayName = 'InputField'; +``` + +## Components + +The following field components will be defined. If more form components are added in the future, they should also include a Field version. + +- `CheckboxField` + - This will not use the Field's `label`, and instead forward the `label` prop to the underlying `Checkbox`. +- `ComboboxField` +- `DropdownField` +- `InputField` +- `RadioGroupField` +- `SelectField` +- `SliderField` +- `SpinnerField` +- `SwitchField` + - _Open question:_ how should this handle the `label`? Should it forward to the underlying `Switch`, or keep the label in the same place as the field's label? Might need a prop to control this behavior. +- `TextareaField` ## Variants -_Describe visual or functional variants of this control, if applicable. For example, a slider could have a 2D variant._ +- **Orientation**: The `fieldOrientation` prop affects the layout of the label and field component: + - `vertical` (default) - label comes above the field component + - `horizontal` - label is to the left of the field component, and is 33% the width of the field (this allows multiple stacked fields to all align their labels) +- **Status**: The `status` prop affects the icon and color used by the `statusText`: + - `error` - Red x icon, and red status text + - `warning` - Yellow exclamation icon + - `success` - Green check icon + - `undefined` (default): Status text is normal color, and there is no status icon +- **Error**: Some control types (like `Input` and `Combobox`) have a prop that makes the border red. This prop will be set `status="error"`. + +Field also forwards some props from the wrapped component to the label as well: + +- **Size**: If the wrapped component supports a `size` prop, it will also be applied to the field's label. +- **Required**: If set, the Label will get a required asterisk: `*` ## API -_List the **Props** and **Slots** proposed for the component. Ideally this would just be a link to the component's `.types.ts` file_ +### FieldComponent + +The FieldProps, etc. types are templated so they can be merged with the wrapped component's props. The `FieldComponent` type defines the minimum set of props that the wrapped component must support. + +```ts +/** + * The minimum requirement for a component wrapped by makeFieldComponent. + * + * Note: the use of VoidFunctionComponent means that component is not *required* to have a children prop, + * but it is still allowed to have a children prop. + */ +export type FieldComponent = React.VoidFunctionComponent< + Pick< + React.HTMLAttributes, + 'id' | 'className' | 'style' | 'aria-labelledby' | 'aria-describedby' | 'aria-invalid' | 'aria-errormessage' + > +>; +``` + +### Slots + +_Note: TypeScript crashes if the `Slot` type is used with a template type parameter. The `SlotComponent` type is a simplified version of that type, which only supports `React.ComponentType`/`React.VoidFunctionComponent`._ + +```ts +export type FieldSlots = { + root: NonNullable>; + + /** + * The underlying component wrapped by this field. + * + * This is the PRIMARY slot: all intrinsic HTML properties will be applied to this slot, + * except `className` and `style`, which remain on the root slot. + */ + fieldComponent: SlotComponent; + + /** + * The label associated with the field. + */ + label?: Slot; + + /** + * A status or validation message. The appearance of the statusText depends on the value of the `status` prop. + */ + statusText?: Slot<'span'>; + + /** + * The icon associated with the status. If the `status` prop is set, this will default to a corresponding icon. + * + * This will only be displayed if `statusText` is set. + */ + statusIcon?: Slot<'span'>; + + /** + * Additional text below the field. + */ + helperText?: Slot<'span'>; +}; +``` + +### Props + +```ts +export type FieldProps = ComponentProps>, 'fieldComponent'> & { + /** + * The orientation of the label relative to the field component. + * This only affects the label, and not the statusText or helperText (which always appear below the field component). + * + * @default vertical + */ + fieldOrientation?: 'vertical' | 'horizontal'; + + /** + * The status affects the color of the statusText, the statusIcon, and for some field components, an error status + * causes the border to become red. + * + * @default undefined + */ + status?: 'error' | 'warning' | 'success'; +}; +``` + +### State + +```ts +export type FieldState = ComponentState>> & + Pick, 'fieldOrientation' | 'status'>; +``` ## Structure -- _**Public**_ -- _**Internal**_ -- _**DOM** - how the component will be rendered as HTML elements_ +### Public API + +```jsx + +``` + +(similar API for other Field components) + +### Slot structure + +```jsx + + + + + + {slotProps.statusText.children} + + + +``` + +### DOM structure + +```html +
+ + + + ... + This is status text + + This is helper text +
+``` ## Migration -_Describe what will need to be done to upgrade from the existing implementations:_ +### Migration from v8 -- _Migration from v8_ -- _Migration from v0_ +Migration from v8 will require picking between the normal and `Field` version of an input control, depending on whether the field-specific features are required: (`label`, `status="error"`, `statusText`, `helperText`) + +See individual input components for more detailed migration guides. + +| v8 Control | v9 Base control | v9 Field control | Notes | +| ------------- | --------------------- | ------------------------------- | -------------------------------------------------------------------------------------------- | +| `Checkbox` | `Checkbox` | `CheckboxField` | Only use `CheckboxField` if an error message is needed, or if required for layout in a form. | +| `ChoiceGroup` | `RadioGroup` | `RadioGroupField` | | +| `ComboBox` | `Combobox` | `ComboboxField` | `errorMessage="..."` is replaced by `status="error" statusText="..."` | +| `Dropdown` | `Dropdown` | `DropdownField` | `errorMessage="..."` is replaced by `status="error" statusText="..."` | +| `Slider` | `Slider` | `SliderField` | | +| `SpinButton` | `SpinButton` | `SpinButtonField` | | +| `TextField` | `Input` OR `Textarea` | `InputField` OR `TextareaField` | `errorMessage="..."` is replaced by `status="error" statusText="..."` | +| `Toggle` | `Switch` | `SwitchField` | | + +### Migration from v0 + +Many components in v0 have `Form___` versions (such as `FormInput`). Those are replaced by the `___Field` equivalent. See the underlying component's migration guides for more detailed migration information. + +Component mapping: + +- `FormButton` => Not supported +- `FormCheckbox` => `CheckboxField` OR `SwitchField` +- `FormDatepicker` => _(Not yet implemented)_ +- `FormDropdown` => `DropdownField` +- `FormField` => Not supported +- `FormFieldCustom` => Not supported +- `FormLabel` => The `label` prop of the field component +- `FormMessage` => Either the `statusText` or `helperText` prop of the field component +- `FormRadioGroup` => `RadioGroupField` +- `FormSlider` => `SliderField` +- `FormTextArea` => `TextareaField` + +The following props are common to each of the `Form___` components: + +- `label` => `label` +- `message` => either `statusText` or `helperText` +- `errorMessage` => `statusText` with `status="error"` ## Behaviors -_Explain how the component will behave in use, including:_ +### Form validation + +Field has no logic to perform input validation. It is expected that the validation will be done externally (possibly using a third party form validation library like Formik). + +### Interaction -- _Component States_ -- _Interaction_ - - _Keyboard_ - - _Cursor_ - - _Touch_ - - _Screen readers_ +The Field itself is not interactive. The wrapped component has the same interactions as it does outside of a field. ## Accessibility -Base accessibility information is included in the design document. After the spec is filled and review, outcomes from it need to be communicated to design and incorporated in the design document. - -- Decide whether to use **native element** or folow **ARIA** and provide reasons -- Identify the **[ARIA](https://www.w3.org/TR/wai-aria-practices-1.2/) pattern** and, if the component is listed there, follow its specification as possible. -- Identify accessibility **variants**, the `role` ([ARIA roles](https://www.w3.org/TR/wai-aria-1.1/#role_definitions)) of the component, its `slots` and `aria-*` props. -- Describe the **keyboard navigation**: Tab Oder and Arrow Key Navigation. Describe any other keyboard **shortcuts** used -- Specify texts for **state change announcements** - [ARIA live regions - ](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions) (number of available items in dropdown, error messages, confirmations, ...) -- Identify UI parts that appear on **hover or focus** and specify keyboard and screen reader interaction with them -- List cases when **focus** needs to be **trapped** in sections of the UI (for dialogs and popups or for hierarchical navigation) -- List cases when **focus** needs to be **moved programatically** (if parts of the UI are appearing/disappearing or other cases) +- **ARIA pattern** + - Field itself does not implement a defined ARIA pattern. It has no role applied to the root element. +- **Attributes** + - The following are applied on the wrapped component: + - `aria-labelledby={label.id}`, if the label is present. + - `aria-describedby` is set to one of: + - `aria-describedby={statusText.id}`, if statusText is present, and _only if_ `status !== 'error'` + - `aria-describedby={helperText.id}`, if helperText is present + - `aria-describedby={statusText.id + ' ' + helperText.id}`, if both conditions above apply + - `aria-errormessage={statusText.id}`, if statusText is present, and _only if_ `status === 'error'` + - `aria-invalid={true}`, _only if_ `status === 'error'` + - On the `label` slot: + - `htmlFor={fieldComponent.id}` - the wrapped component's `id` (an ID is generated if not supplied via props). +- **Live regions** (state change announcements) + - TBD: Need to determine if the status text should be an aria live region. +- **UI parts appearing on hover or focus** + - None. +- **Focus behavior** + - No special focus behavior: no focus trapping or programmatic focus moving. From ce0dde23ec1e0e97a69277e4783e9111b22d1e05 Mon Sep 17 00:00:00 2001 From: Ben Howell Date: Mon, 15 Aug 2022 15:04:54 -0700 Subject: [PATCH 02/11] Update Prior Art --- packages/react-components/react-field/Spec.md | 42 +++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/packages/react-components/react-field/Spec.md b/packages/react-components/react-field/Spec.md index 2d3630af9154e..e8d335e36a1c5 100644 --- a/packages/react-components/react-field/Spec.md +++ b/packages/react-components/react-field/Spec.md @@ -2,17 +2,24 @@ ## Background -`Field` is used to add a label, validation text, and helper text to form input components, such as `Input`, `Combobox`, `RadioGroup`, etc. +Fields add add a label, validation text, and helper text to form input components. The existing input components (such as `Input` and `Combobox`) are wrapped to create field versions of them (such as `InputField` and `ComboboxField`). Epic issue tracking implementation: https://github.com/microsoft/fluentui/issues/19627 ## Prior Art -_Include background research done for this component_ +Existing libraries tend to take one of the following approaches to field. -- _Link to Open UI research_ -- _Link to comparison of v7 and v0_ -- _Link to GitHub epic issue for the converged component_ +1. Include support for label, error text, etc. in the base input component. Libraries using this approach include: + - **FluentUI v8** - [`TextField`](https://developer.microsoft.com/en-us/fluentui#/controls/web/textfield), [`Dropdown`](https://developer.microsoft.com/en-us/fluentui#/controls/web/dropdown), etc. +2. Provide a set of components that are manually constructed into a field. This requires manually hooking up the components using props like `htmlFor` and `aria-describedby`. Libraries using this approach include: + - **FluentUI v0** - [`FormField`](https://fluentsite.z22.web.core.windows.net/0.64.0/components/form/props#form-field), [`FormLabel`](https://fluentsite.z22.web.core.windows.net/0.64.0/components/form/props#form-label), [`FormMessage`](https://fluentsite.z22.web.core.windows.net/0.64.0/components/form/props#form-message) + - **Material design** - https://material.io/components/text-fields/web + - **Ant** - [`Form.Item`](https://ant.design/components/form/#Form.Item) (uses context to do some of the hooking up between the item and the field component). +3. Provide base components without a label or descriptive text, and then Field versions of those controls. Libraries using this approach include: + - **FluentUI v0** - [`Input`](https://fluentsite.z22.web.core.windows.net/0.64.0/components/input/props) and [`FormInput`](https://fluentsite.z22.web.core.windows.net/0.64.0/components/form/props#form-input), for example. + - **Evergreen UI** - [`TextInput`](https://evergreen.segment.com/components/text-input) and [`TextInputField`](https://evergreen.segment.com/components/text-input#textinputfield), for example. + - ## Sample Code @@ -104,7 +111,7 @@ The FieldProps, etc. types are templated so they can be merged with the wrapped ```ts /** - * The minimum requirement for a component wrapped by makeFieldComponent. + * The minimum requirement for a component used by Field. * * Note: the use of VoidFunctionComponent means that component is not *required* to have a children prop, * but it is still allowed to have a children prop. @@ -136,7 +143,7 @@ export type FieldSlots = { /** * The label associated with the field. */ - label?: Slot; + label?: SlotComponent; /** * A status or validation message. The appearance of the statusText depends on the value of the `status` prop. @@ -179,6 +186,27 @@ export type FieldProps = ComponentProps element has a `size` prop of type `number`. + */ + size?: 'small' | 'medium' | 'large' | number; +}; +``` + ### State ```ts From b8ef0e570f4ae99a7c77dca202836a045969d3ef Mon Sep 17 00:00:00 2001 From: Ben Howell Date: Mon, 15 Aug 2022 16:49:10 -0700 Subject: [PATCH 03/11] Spec updates --- packages/react-components/react-field/Spec.md | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/react-components/react-field/Spec.md b/packages/react-components/react-field/Spec.md index e8d335e36a1c5..8068a5117e4f7 100644 --- a/packages/react-components/react-field/Spec.md +++ b/packages/react-components/react-field/Spec.md @@ -11,15 +11,21 @@ Epic issue tracking implementation: https://github.com/microsoft/fluentui/issues Existing libraries tend to take one of the following approaches to field. 1. Include support for label, error text, etc. in the base input component. Libraries using this approach include: - - **FluentUI v8** - [`TextField`](https://developer.microsoft.com/en-us/fluentui#/controls/web/textfield), [`Dropdown`](https://developer.microsoft.com/en-us/fluentui#/controls/web/dropdown), etc. + - **FluentUI v8** - [`TextField`](https://developer.microsoft.com/en-us/fluentui#/controls/web/textfield), [`Dropdown`](https://developer.microsoft.com/en-us/fluentui#/controls/web/dropdown), [`ChoiceGroup`](https://developer.microsoft.com/en-us/fluentui#/controls/web/choicegroup), etc. + - **Spectrum** - [`TextField`](https://react-spectrum.adobe.com/react-spectrum/TextField.html), [`Slider`](https://react-spectrum.adobe.com/react-spectrum/Slider.html), ['RadioGroup'](https://react-spectrum.adobe.com/react-spectrum/RadioGroup.html), etc. 2. Provide a set of components that are manually constructed into a field. This requires manually hooking up the components using props like `htmlFor` and `aria-describedby`. Libraries using this approach include: - **FluentUI v0** - [`FormField`](https://fluentsite.z22.web.core.windows.net/0.64.0/components/form/props#form-field), [`FormLabel`](https://fluentsite.z22.web.core.windows.net/0.64.0/components/form/props#form-label), [`FormMessage`](https://fluentsite.z22.web.core.windows.net/0.64.0/components/form/props#form-message) - - **Material design** - https://material.io/components/text-fields/web - **Ant** - [`Form.Item`](https://ant.design/components/form/#Form.Item) (uses context to do some of the hooking up between the item and the field component). 3. Provide base components without a label or descriptive text, and then Field versions of those controls. Libraries using this approach include: - **FluentUI v0** - [`Input`](https://fluentsite.z22.web.core.windows.net/0.64.0/components/input/props) and [`FormInput`](https://fluentsite.z22.web.core.windows.net/0.64.0/components/form/props#form-input), for example. - **Evergreen UI** - [`TextInput`](https://evergreen.segment.com/components/text-input) and [`TextInputField`](https://evergreen.segment.com/components/text-input#textinputfield), for example. - - + +The Field implementation in this spec follows pattern (3). There are Field versions of all components that can be used as form inputs. There are several reasons, including: + +- **Accessibility**: By combining a base component with the field props into a single component, all of the accessibility props like `htmlFor` and `aria-describedby` are set correctly for "free". +- **Simplicity**: All props related to the component (such as `label`, `id`, `status="error"`, etc.) are on the same component, rather than split between multiple components (like separate `Field` and `Input` components). +- **Consistency**: All of the Field components share a common set of props for the label, status, helperText, etc. +- **Bundle size**: When the label and other field functionality is not needed, it is still possible to use the base components without pulling in unnecessary dependencies (like `Label` and the field styling). ## Sample Code @@ -39,19 +45,18 @@ Each input component has a field version (such as `InputField`, `ComboboxField`, contentBefore="$" contentAfter=".00" /> - {/* Other component types are supported, such as: */} - + - + - - + + ``` @@ -107,7 +112,7 @@ Field also forwards some props from the wrapped component to the label as well: ### FieldComponent -The FieldProps, etc. types are templated so they can be merged with the wrapped component's props. The `FieldComponent` type defines the minimum set of props that the wrapped component must support. +The `FieldComponent` type defines the minimum set of props that the wrapped component must support. This is used for the generic types as the requirement for the type parameter: `FieldProps` ```ts /** From da10401bb7d98603b226e3e4062a5cbbbb89b809 Mon Sep 17 00:00:00 2001 From: Ben Howell Date: Mon, 15 Aug 2022 16:58:14 -0700 Subject: [PATCH 04/11] Update spec --- packages/react-components/react-field/Spec.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/react-components/react-field/Spec.md b/packages/react-components/react-field/Spec.md index 8068a5117e4f7..fe91eb20bf192 100644 --- a/packages/react-components/react-field/Spec.md +++ b/packages/react-components/react-field/Spec.md @@ -12,7 +12,7 @@ Existing libraries tend to take one of the following approaches to field. 1. Include support for label, error text, etc. in the base input component. Libraries using this approach include: - **FluentUI v8** - [`TextField`](https://developer.microsoft.com/en-us/fluentui#/controls/web/textfield), [`Dropdown`](https://developer.microsoft.com/en-us/fluentui#/controls/web/dropdown), [`ChoiceGroup`](https://developer.microsoft.com/en-us/fluentui#/controls/web/choicegroup), etc. - - **Spectrum** - [`TextField`](https://react-spectrum.adobe.com/react-spectrum/TextField.html), [`Slider`](https://react-spectrum.adobe.com/react-spectrum/Slider.html), ['RadioGroup'](https://react-spectrum.adobe.com/react-spectrum/RadioGroup.html), etc. + - **Spectrum** - [`TextField`](https://react-spectrum.adobe.com/react-spectrum/TextField.html), [`Slider`](https://react-spectrum.adobe.com/react-spectrum/Slider.html), [`RadioGroup`](https://react-spectrum.adobe.com/react-spectrum/RadioGroup.html), etc. 2. Provide a set of components that are manually constructed into a field. This requires manually hooking up the components using props like `htmlFor` and `aria-describedby`. Libraries using this approach include: - **FluentUI v0** - [`FormField`](https://fluentsite.z22.web.core.windows.net/0.64.0/components/form/props#form-field), [`FormLabel`](https://fluentsite.z22.web.core.windows.net/0.64.0/components/form/props#form-label), [`FormMessage`](https://fluentsite.z22.web.core.windows.net/0.64.0/components/form/props#form-message) - **Ant** - [`Form.Item`](https://ant.design/components/form/#Form.Item) (uses context to do some of the hooking up between the item and the field component). @@ -94,13 +94,13 @@ The following field components will be defined. If more form components are adde ## Variants - **Orientation**: The `fieldOrientation` prop affects the layout of the label and field component: - - `vertical` (default) - label comes above the field component - - `horizontal` - label is to the left of the field component, and is 33% the width of the field (this allows multiple stacked fields to all align their labels) + - `'vertical'` (default) - label is above the field component + - `'horizontal'` - label is to the left of the field component, and is 33% the width of the field (this allows multiple stacked fields to all align their labels) - **Status**: The `status` prop affects the icon and color used by the `statusText`: - - `error` - Red x icon, and red status text - - `warning` - Yellow exclamation icon - - `success` - Green check icon - - `undefined` (default): Status text is normal color, and there is no status icon + - `'error'` - Red x icon, red text color + - `'warning'` - Yellow exclamation icon, neutral color text + - `'success'` - Green check icon, neutral color text + - `undefined` (default): No status icon, neutral color text - **Error**: Some control types (like `Input` and `Combobox`) have a prop that makes the border red. This prop will be set `status="error"`. Field also forwards some props from the wrapped component to the label as well: From ca3c4842cebd578de59f0bdf9e6f40235636cbe5 Mon Sep 17 00:00:00 2001 From: Ben Howell Date: Thu, 18 Aug 2022 17:08:07 -0700 Subject: [PATCH 05/11] Rename props based on feedback --- packages/react-components/react-field/Spec.md | 109 +++++++++--------- 1 file changed, 56 insertions(+), 53 deletions(-) diff --git a/packages/react-components/react-field/Spec.md b/packages/react-components/react-field/Spec.md index fe91eb20bf192..471b726e087a2 100644 --- a/packages/react-components/react-field/Spec.md +++ b/packages/react-components/react-field/Spec.md @@ -2,7 +2,7 @@ ## Background -Fields add add a label, validation text, and helper text to form input components. The existing input components (such as `Input` and `Combobox`) are wrapped to create field versions of them (such as `InputField` and `ComboboxField`). +Fields add add a label, validation text, and hint text to form input components. The existing input components (such as `Input` and `Combobox`) are wrapped to create field versions of them (such as `InputField` and `ComboboxField`). Epic issue tracking implementation: https://github.com/microsoft/fluentui/issues/19627 @@ -23,8 +23,8 @@ Existing libraries tend to take one of the following approaches to field. The Field implementation in this spec follows pattern (3). There are Field versions of all components that can be used as form inputs. There are several reasons, including: - **Accessibility**: By combining a base component with the field props into a single component, all of the accessibility props like `htmlFor` and `aria-describedby` are set correctly for "free". -- **Simplicity**: All props related to the component (such as `label`, `id`, `status="error"`, etc.) are on the same component, rather than split between multiple components (like separate `Field` and `Input` components). -- **Consistency**: All of the Field components share a common set of props for the label, status, helperText, etc. +- **Simplicity**: All props related to the component (such as `label`, `id`, `validationState="error"`, etc.) are on the same component, rather than split between multiple components (like separate `Field` and `Input` components). +- **Consistency**: All of the Field components share a common set of props for the label, validationState, hint, etc. - **Bundle size**: When the label and other field functionality is not needed, it is still possible to use the base components without pulling in unnecessary dependencies (like `Label` and the field styling). ## Sample Code @@ -36,9 +36,9 @@ Each input component has a field version (such as `InputField`, `ComboboxField`, - + - - + + ``` @@ -93,15 +93,15 @@ The following field components will be defined. If more form components are adde ## Variants -- **Orientation**: The `fieldOrientation` prop affects the layout of the label and field component: +- **Orientation**: The `orientation` prop affects the layout of the label and field component: - `'vertical'` (default) - label is above the field component - `'horizontal'` - label is to the left of the field component, and is 33% the width of the field (this allows multiple stacked fields to all align their labels) -- **Status**: The `status` prop affects the icon and color used by the `statusText`: +- **Validation state**: The `validationState` prop affects the icon and color used by the `validationMessage`: - `'error'` - Red x icon, red text color - `'warning'` - Yellow exclamation icon, neutral color text - `'success'` - Green check icon, neutral color text - - `undefined` (default): No status icon, neutral color text -- **Error**: Some control types (like `Input` and `Combobox`) have a prop that makes the border red. This prop will be set `status="error"`. + - `undefined` (default): No validation message icon, neutral color text +- **Error**: Some control types (like `Input` and `Combobox`) have a prop that makes the border red. This prop will be set `validationState="error"`. Field also forwards some props from the wrapped component to the label as well: @@ -148,24 +148,25 @@ export type FieldSlots = { /** * The label associated with the field. */ - label?: SlotComponent; + label?: Slot; /** - * A status or validation message. The appearance of the statusText depends on the value of the `status` prop. + * A message about the validation state. The appearance of the `validationMessage` depends on `validationState`. */ - statusText?: Slot<'span'>; + validationMessage?: Slot<'span'>; /** - * The icon associated with the status. If the `status` prop is set, this will default to a corresponding icon. + * The icon associated with the `validationMessage`. If the `validationState` prop is set, this will default to an + * icon corresponding to that state. * - * This will only be displayed if `statusText` is set. + * This will only be displayed if `validationMessage` is set. */ - statusIcon?: Slot<'span'>; + validationMessageIcon?: Slot<'span'>; /** - * Additional text below the field. + * Additional hint text below the field. */ - helperText?: Slot<'span'>; + hint?: Slot<'span'>; }; ``` @@ -175,19 +176,19 @@ export type FieldSlots = { export type FieldProps = ComponentProps>, 'fieldComponent'> & { /** * The orientation of the label relative to the field component. - * This only affects the label, and not the statusText or helperText (which always appear below the field component). + * This only affects the label, and not the validationMessage or hint (which always appear below the field component). * * @default vertical */ - fieldOrientation?: 'vertical' | 'horizontal'; + orientation?: 'vertical' | 'horizontal'; /** - * The status affects the color of the statusText, the statusIcon, and for some field components, an error status - * causes the border to become red. + * The `validationState` affects the color of the `validationMessage`, the `validationMessageIcon`, and for some + * field components, an `validationState="error"` causes the border to become red. * * @default undefined */ - status?: 'error' | 'warning' | 'success'; + validationState?: 'error' | 'warning' | 'success'; }; ``` @@ -216,7 +217,9 @@ export type OptionalFieldComponentProps = { ```ts export type FieldState = ComponentState>> & - Pick, 'fieldOrientation' | 'status'>; + Pick, 'orientation' | 'validationState'> & { + classNames: SlotClassNames>; + }; ``` ## Structure @@ -226,10 +229,10 @@ export type FieldState = ComponentState ``` @@ -241,11 +244,11 @@ export type FieldState = ComponentState - - - {slotProps.statusText.children} - - + + + {slotProps.validationMessage.children} + + ``` @@ -255,11 +258,11 @@ export type FieldState = ComponentState - - ... - This is status text + + ... + This is a validation message - This is helper text + This is a hint message ``` @@ -267,7 +270,7 @@ export type FieldState = ComponentState Not supported - `FormFieldCustom` => Not supported - `FormLabel` => The `label` prop of the field component -- `FormMessage` => Either the `statusText` or `helperText` prop of the field component +- `FormMessage` => Either the `validationMessage` or `hint` prop of the field component - `FormRadioGroup` => `RadioGroupField` - `FormSlider` => `SliderField` - `FormTextArea` => `TextareaField` @@ -303,8 +306,8 @@ Component mapping: The following props are common to each of the `Form___` components: - `label` => `label` -- `message` => either `statusText` or `helperText` -- `errorMessage` => `statusText` with `status="error"` +- `message` => either `validationMessage` or `hint` +- `errorMessage` => `validationMessage` with `validationState="error"` ## Behaviors @@ -324,15 +327,15 @@ The Field itself is not interactive. The wrapped component has the same interact - The following are applied on the wrapped component: - `aria-labelledby={label.id}`, if the label is present. - `aria-describedby` is set to one of: - - `aria-describedby={statusText.id}`, if statusText is present, and _only if_ `status !== 'error'` - - `aria-describedby={helperText.id}`, if helperText is present - - `aria-describedby={statusText.id + ' ' + helperText.id}`, if both conditions above apply - - `aria-errormessage={statusText.id}`, if statusText is present, and _only if_ `status === 'error'` - - `aria-invalid={true}`, _only if_ `status === 'error'` + - `aria-describedby={validationMessage.id}`, if validationMessage is present, and _only if_ `validationState !== 'error'` + - `aria-describedby={hint.id}`, if hint is present + - `aria-describedby={validationMessage.id + ' ' + hint.id}`, if both conditions above apply + - `aria-errormessage={validationMessage.id}`, if validationMessage is present, and _only if_ `validationState === 'error'` + - `aria-invalid={true}`, _only if_ `validationState === 'error'` - On the `label` slot: - `htmlFor={fieldComponent.id}` - the wrapped component's `id` (an ID is generated if not supplied via props). - **Live regions** (state change announcements) - - TBD: Need to determine if the status text should be an aria live region. + - TBD: Need to determine if the validation message should be an aria live region. - **UI parts appearing on hover or focus** - None. - **Focus behavior** From 8e30c0dfc0514e1058cc3fe2b37022c1763f09b9 Mon Sep 17 00:00:00 2001 From: Ben Howell Date: Thu, 18 Aug 2022 17:08:31 -0700 Subject: [PATCH 06/11] Fix typo: Spinner -> SpinButton --- packages/react-components/react-field/Spec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-components/react-field/Spec.md b/packages/react-components/react-field/Spec.md index 471b726e087a2..66a865de4c688 100644 --- a/packages/react-components/react-field/Spec.md +++ b/packages/react-components/react-field/Spec.md @@ -86,7 +86,7 @@ The following field components will be defined. If more form components are adde - `RadioGroupField` - `SelectField` - `SliderField` -- `SpinnerField` +- `SpinButtonField` - `SwitchField` - _Open question:_ how should this handle the `label`? Should it forward to the underlying `Switch`, or keep the label in the same place as the field's label? Might need a prop to control this behavior. - `TextareaField` From 4b341caa2ecf84e7088a844ac95f756b27898292 Mon Sep 17 00:00:00 2001 From: Ben Howell Date: Tue, 30 Aug 2022 16:23:13 -0700 Subject: [PATCH 07/11] Rename `fieldComponent` slot to `field` --- packages/react-components/react-field/Spec.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/react-components/react-field/Spec.md b/packages/react-components/react-field/Spec.md index 66a865de4c688..37ad6ac224730 100644 --- a/packages/react-components/react-field/Spec.md +++ b/packages/react-components/react-field/Spec.md @@ -143,7 +143,7 @@ export type FieldSlots = { * This is the PRIMARY slot: all intrinsic HTML properties will be applied to this slot, * except `className` and `style`, which remain on the root slot. */ - fieldComponent: SlotComponent; + field: SlotComponent; /** * The label associated with the field. @@ -173,7 +173,7 @@ export type FieldSlots = { ### Props ```ts -export type FieldProps = ComponentProps>, 'fieldComponent'> & { +export type FieldProps = ComponentProps>, 'field'> & { /** * The orientation of the label relative to the field component. * This only affects the label, and not the validationMessage or hint (which always appear below the field component). @@ -243,7 +243,7 @@ export type FieldState = ComponentState - + {slotProps.validationMessage.children} @@ -333,7 +333,7 @@ The Field itself is not interactive. The wrapped component has the same interact - `aria-errormessage={validationMessage.id}`, if validationMessage is present, and _only if_ `validationState === 'error'` - `aria-invalid={true}`, _only if_ `validationState === 'error'` - On the `label` slot: - - `htmlFor={fieldComponent.id}` - the wrapped component's `id` (an ID is generated if not supplied via props). + - `htmlFor={field.id}` - the wrapped component's `id` (an ID is generated if not supplied via props). - **Live regions** (state change announcements) - TBD: Need to determine if the validation message should be an aria live region. - **UI parts appearing on hover or focus** From 84a5a999e0914a9a8457ed3ceb7e601d4c0cebd5 Mon Sep 17 00:00:00 2001 From: Ben Howell Date: Wed, 31 Aug 2022 10:57:27 -0700 Subject: [PATCH 08/11] Rename `field` slot to `control` --- packages/react-components/react-field/Spec.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/react-components/react-field/Spec.md b/packages/react-components/react-field/Spec.md index 37ad6ac224730..6716b022d9b95 100644 --- a/packages/react-components/react-field/Spec.md +++ b/packages/react-components/react-field/Spec.md @@ -143,7 +143,7 @@ export type FieldSlots = { * This is the PRIMARY slot: all intrinsic HTML properties will be applied to this slot, * except `className` and `style`, which remain on the root slot. */ - field: SlotComponent; + control: SlotComponent; /** * The label associated with the field. @@ -173,7 +173,7 @@ export type FieldSlots = { ### Props ```ts -export type FieldProps = ComponentProps>, 'field'> & { +export type FieldProps = ComponentProps>, 'control'> & { /** * The orientation of the label relative to the field component. * This only affects the label, and not the validationMessage or hint (which always appear below the field component). @@ -243,7 +243,7 @@ export type FieldState = ComponentState - + {slotProps.validationMessage.children} @@ -333,7 +333,7 @@ The Field itself is not interactive. The wrapped component has the same interact - `aria-errormessage={validationMessage.id}`, if validationMessage is present, and _only if_ `validationState === 'error'` - `aria-invalid={true}`, _only if_ `validationState === 'error'` - On the `label` slot: - - `htmlFor={field.id}` - the wrapped component's `id` (an ID is generated if not supplied via props). + - `htmlFor={control.id}` - the wrapped component's `id` (an ID is generated if not supplied via props). - **Live regions** (state change announcements) - TBD: Need to determine if the validation message should be an aria live region. - **UI parts appearing on hover or focus** From 9278d2968a9a63e58bf6c77830f3f594d1f09d4a Mon Sep 17 00:00:00 2001 From: Ben Howell Date: Thu, 1 Sep 2022 11:42:34 -0700 Subject: [PATCH 09/11] Clarify handling of label for Checkbox and Switch --- packages/react-components/react-field/Spec.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-field/Spec.md b/packages/react-components/react-field/Spec.md index 6716b022d9b95..691a38507466f 100644 --- a/packages/react-components/react-field/Spec.md +++ b/packages/react-components/react-field/Spec.md @@ -79,7 +79,6 @@ InputField.displayName = 'InputField'; The following field components will be defined. If more form components are added in the future, they should also include a Field version. - `CheckboxField` - - This will not use the Field's `label`, and instead forward the `label` prop to the underlying `Checkbox`. - `ComboboxField` - `DropdownField` - `InputField` @@ -88,7 +87,6 @@ The following field components will be defined. If more form components are adde - `SliderField` - `SpinButtonField` - `SwitchField` - - _Open question:_ how should this handle the `label`? Should it forward to the underlying `Switch`, or keep the label in the same place as the field's label? Might need a prop to control this behavior. - `TextareaField` ## Variants @@ -222,6 +220,21 @@ export type FieldState = ComponentState Date: Fri, 2 Sep 2022 11:17:05 -0700 Subject: [PATCH 10/11] Fix tyop --- packages/react-components/react-field/Spec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-components/react-field/Spec.md b/packages/react-components/react-field/Spec.md index 691a38507466f..057e11a8d7c1e 100644 --- a/packages/react-components/react-field/Spec.md +++ b/packages/react-components/react-field/Spec.md @@ -2,7 +2,7 @@ ## Background -Fields add add a label, validation text, and hint text to form input components. The existing input components (such as `Input` and `Combobox`) are wrapped to create field versions of them (such as `InputField` and `ComboboxField`). +Field adds a label, validation text, and hint text to form input components. The existing input components (such as `Input` and `Combobox`) are wrapped to create field versions of them (such as `InputField` and `ComboboxField`). Epic issue tracking implementation: https://github.com/microsoft/fluentui/issues/19627 From 4f077237da0088d6dd112a0c87b04aaf68bc3784 Mon Sep 17 00:00:00 2001 From: Ben Howell Date: Fri, 2 Sep 2022 11:17:44 -0700 Subject: [PATCH 11/11] Remove "needs review" comment for now. Will update spec if this changes based on discussion with design. --- packages/react-components/react-field/Spec.md | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-components/react-field/Spec.md b/packages/react-components/react-field/Spec.md index 057e11a8d7c1e..eab2c880350bf 100644 --- a/packages/react-components/react-field/Spec.md +++ b/packages/react-components/react-field/Spec.md @@ -233,7 +233,6 @@ The Checkbox and Switch components already have a `label` prop, which conflicts - New `valueLabel` prop for the label of the Switch - The `label` prop will go to the Field and NOT the Switch -- _NEEDS REVIEW_ - Should we rename the underlying Switch `label` prop to `valueLabel`? If so, then SwitchField would no longer be a special case. ## Structure