|
| 1 | +import { Meta } from '@storybook/addon-docs'; |
| 2 | + |
| 3 | +<Meta title="Concepts/Upgrading/from v8/Components/TextField to Input Upgrade" /> |
| 4 | + |
| 5 | +# TextField to Input Upgrade |
| 6 | + |
| 7 | +Fluent UI v8 provides the `TextField` control for entering and editing text. In Fluent UI v9 `TextField` is replaced with `Input`. |
| 8 | + |
| 9 | +While basic usage is largely the same, `Input` omits some features found in `TextField`, preferring to compose several components together for greater flexibility. |
| 10 | + |
| 11 | +## Examples |
| 12 | + |
| 13 | +### Basic Upgrade |
| 14 | + |
| 15 | +Basic usage of `TextField` looks like |
| 16 | + |
| 17 | +```tsx |
| 18 | +import * as React from 'react'; |
| 19 | +import { TextField } from '@fluentui/react/lib/TextField';} |
| 20 | +import { Stack, IStackProps, IStackStyles } from '@fluentui/react/lib/Stack'; |
| 21 | + |
| 22 | +const columnProps: Partial<IStackProps> = { |
| 23 | + tokens: { childrenGap: 15 }, |
| 24 | + styles: { root: { width: 300 } }, |
| 25 | +}; |
| 26 | + |
| 27 | +const TextFieldV8BasicExample = () => { |
| 28 | + return <Stack {...columnProps}> |
| 29 | + <TextField label="Standard" /> |
| 30 | + <TextField label="Disabled" disabled defaultValue="I am disabled" /> |
| 31 | + <TextField label="Read-only" readOnly defaultValue="I am read-only" /> |
| 32 | + <TextField label="Required " required /> |
| 33 | + <TextField ariaLabel="Required without visible label" required /> |
| 34 | + <TextField label="With error message" errorMessage="Error message" /> |
| 35 | + </Stack> |
| 36 | +} |
| 37 | +``` |
| 38 | + |
| 39 | +An equivalent `Input` usage is: |
| 40 | + |
| 41 | +```tsx |
| 42 | +import * as React from 'react'; |
| 43 | +import { Text } from '@fluentui/react-components'; |
| 44 | +import { Label, Input } from '@fluentui/react-components/unstable'; |
| 45 | +import { useId } from '@fluentui/react-utilities'; |
| 46 | +import { makeStyles } from '@griffel/react'; |
| 47 | + |
| 48 | +const useStackStyles = makeStyles({ |
| 49 | + root: { |
| 50 | + maxWidth: '300px', |
| 51 | + display: 'flex', |
| 52 | + flexDirection: 'column', |
| 53 | + |
| 54 | + '> * + *': { |
| 55 | + marginTop: '20px', |
| 56 | + }, |
| 57 | + |
| 58 | + '> div': { |
| 59 | + display: 'flex', |
| 60 | + flexDirection: 'column', |
| 61 | + }, |
| 62 | + }, |
| 63 | +}); |
| 64 | + |
| 65 | +const InputV9BasicExample = () => { |
| 66 | + const stackStyles = useStackStyles(); |
| 67 | + const standardId = useId('standard'); |
| 68 | + const disabledId = useId('disabled'); |
| 69 | + const readonlyId = useId('readonly'); |
| 70 | + const requiredId = useId('required'); |
| 71 | + const errorId = useId('error'); |
| 72 | + const errorMessageId = useId('error-message'); |
| 73 | + |
| 74 | + return ( |
| 75 | + <div className={stackStyles.root}> |
| 76 | + <div> |
| 77 | + <Label htmlFor={standardId}>Standard</Label> |
| 78 | + <Input id={standardId} /> |
| 79 | + </div> |
| 80 | + |
| 81 | + <div> |
| 82 | + <Label htmlFor={disabledId}>Disabled</Label> |
| 83 | + <Input id={disabledId} disabled defaultValue="I am disabled" /> |
| 84 | + </div> |
| 85 | + |
| 86 | + <div> |
| 87 | + <Label htmlFor={readonlyId}>Read Only</Label> |
| 88 | + <Input id={readonlyId} readOnly defaultValue="I am read only" /> |
| 89 | + </div> |
| 90 | + |
| 91 | + <div> |
| 92 | + <Label htmlFor={requiredId} required> |
| 93 | + Required |
| 94 | + </Label> |
| 95 | + <Input id={requiredId} required defaultValue="I am required" /> |
| 96 | + </div> |
| 97 | + |
| 98 | + <div> |
| 99 | + <Input |
| 100 | + aria-label="Required without visible label" |
| 101 | + required |
| 102 | + defaultValue="I am required without a visible label" |
| 103 | + /> |
| 104 | + </div> |
| 105 | + |
| 106 | + <div> |
| 107 | + <Label htmlFor={errorId}>With Error Message</Label> |
| 108 | + <Input id={errorId} aria-describedby={errorMessageId} /> |
| 109 | + <Text id={errorMessageId}>Error message</Text> |
| 110 | + </div> |
| 111 | + </div> |
| 112 | + ); |
| 113 | +}; |
| 114 | +``` |
| 115 | + |
| 116 | +## Prop Mapping |
| 117 | + |
| 118 | +This table maps v8 `TextField` props to the v9 `Input` equivalent. |
| 119 | + |
| 120 | +| v8 | v9 | Notes | |
| 121 | +| -------------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------ | |
| 122 | +| `componentRef` | `ref` | v9 provides access to the underlyig DOM node, not ITextField | |
| 123 | +| `elementRef` | `ref` | | |
| 124 | +| `multiline` | n/a | Use `Textarea` | |
| 125 | +| `resizable` | n/a | | |
| 126 | +| `autoAjustHeight` | n/a | See `Textarea` docs | |
| 127 | +| `underlined` | `appearance` | | |
| 128 | +| `borderless` | `appearance` | | |
| 129 | +| `label` | Use `Label` component | Be sure to associate `Label` with `Input` via `htmlFor` | |
| 130 | +| `onRenderLabel` | n/a | Use slots to customize `Input` | |
| 131 | +| `description` | n/a | Use another element like `Text` and associate it with `Input` via `aria-describedby` | |
| 132 | +| `onRenderDescription` | n/a | | |
| 133 | +| `onRenderInput` | n/a | Use slots to customize `Input` | |
| 134 | +| `prefix` | `contentBefore` | This is a slot not a `string` | |
| 135 | +| `suffix` | `contentAfter` | This is a slot not a `string` | |
| 136 | +| `onRenderPrefix` | n/a | Use slots to customize `Input` | |
| 137 | +| `onRenderSuffix` | n/a | Use slots to customize `Input` | |
| 138 | +| `iconProps` | Use `contentBefore` or `contentAfter` slot | | |
| 139 | +| `defaultValue` | `defaultValue` | Mutually exclusive with `value` | |
| 140 | +| `value` | `value` | Mutually exclusive with `defaultValue` | |
| 141 | +| `disabled` | `disabled` | | |
| 142 | +| `readOnly` | `readOnly` | In v9 this is the native HTML prop | |
| 143 | +| `invalid` | n/a | v9 `Input` does not handle validation states | |
| 144 | +| `errorMessage` | n/a | Use another element like `Text` and associate it with `Input` via `aria-describedby` | |
| 145 | +| `onChange` | `onChange` | Typescript types have changed | |
| 146 | +| `onNotifyValidationResult` | n/a | v9 `Input` does not handle validation | |
| 147 | +| `onGetErrorMessage` | n/a | v9 `Input` does not handle error states | |
| 148 | +| `deferredValidationTime` | n/a | v9 `Input` does not handle validation | |
| 149 | +| `className` | `className` | | |
| 150 | +| `inputClassName` | Use `input` slot | | |
| 151 | +| `ariaLabel` | `aria-label` | | |
| 152 | +| `validateOnFocusIn` | n/a | v9 `Input` does not handle validation | |
| 153 | +| `validateOnFocusOut` | n/a | v9 `Input` does not handle validation | |
| 154 | +| `validateOnLoad` | n/a | v9 `Input` does not handle validation | |
| 155 | +| `theme` | n/a | Use `FluentProvider` to customize themes | |
| 156 | +| `styles` | `className` | | |
| 157 | +| `autoComplete` | `autoComplete` | In v9 this is the native HTML prop | |
| 158 | +| `canRevealPassword` | n/a | v9 `Input` does not provide built in password reveal behavior | |
| 159 | +| `revealPasswordAriaLabel` | n/a | v9 `Input` does not provide built in password reveal behavior | |
0 commit comments