UNPKG

@wix/design-system

Version:

@wix/design-system

449 lines (404 loc) 15.2 kB
## Feature Examples ### Size - description: <p>Adjust the size using the <code>size</code> prop. There are 3 supported sizes:</p><li>When the input needs to be emphasized, like in onboarding flows, use <code>large.</code></li><li>In most cases, use the default <code>medium.</code></li><li>For dense and narrow layouts where space is tight, use <code>small.</code></li> - example: ```jsx <StorybookComponents.Stack flexDirection="column"> <NumberInput size="large" placeholder="Large" /> <NumberInput size="medium" placeholder="Medium" /> <NumberInput size="small" placeholder="Small" /> </StorybookComponents.Stack>; ``` ### Border - description: <p>Style the field using the <code>border</code> prop. It supports 4 styles:</p><li>For most common cases, like forms, use <code>standard</code>.</li><li>To build inputs that can be used to filter data, use <code>round</code>.</li><li>Use <code>bottomLine</code> to create an editable, underlined title field.</li><li>When a component provides its own hover and focus styles, such as a table or spreadsheet cells, use <code>none</code>.</li> - example: ```jsx <StorybookComponents.Stack flexDirection="column"> <NumberInput border="standard" placeholder="Standard" /> <NumberInput border="round" placeholder="Round" /> <NumberInput border="bottomLine" placeholder="Bottom line" /> <NumberInput border="none" placeholder="None" /> </StorybookComponents.Stack>; ``` ### Status - description: <p>Control the status of the input using the <code>status</code> prop. It supports 3 states:</p><p></p><li><code>error</code> demonstrates that a required input is missing something or the entry was invalid.</li><li><code>warning</code> highlights an input value that might have a significant impact to a user.</li><li><code>loading</code> shows when an input value is being uploaded to the server.</li> - example: ```jsx <StorybookComponents.Stack flexDirection="column"> <NumberInput status="error" placeholder="Error" /> <NumberInput status="warning" placeholder="Warning" /> <NumberInput status="loading" placeholder="Loading" /> </StorybookComponents.Stack>; ``` ### Status message - description: <p>Add text that explains the status or what action the user should take with the <code>statusMessage</code> prop.</p><p></p><p>Showing the status message inline, directly below the input is preferred in all default cases.</p><li>To add an accessible inline message, wrap the component in a <code><FormField/></code> and add the <code>statusMessage</code>.</li><li>To add a status message in a tooltip that requires users to hover on the icon, use the <code>statusMessage</code> prop. Control tooltip placement with <code>tooltipPlacement</code> prop.</li><p></p><p>View more inline status message examples in <code><FormField/></code>.</p> - example: ```jsx <StorybookComponents.Stack flexDirection="column"> <StorybookComponents.Stack flexDirection="column" gap="12px"> <Text secondary>For all default cases:</Text> <FormField status="error" statusMessage="This is an error message."> <NumberInput placeholder="See message below" tooltipPlacement="top-end" /> </FormField> </StorybookComponents.Stack> <StorybookComponents.Stack flexDirection="column" gap="12px"> <Text secondary>For narrow layouts only:</Text> <NumberInput placeholder="Hover on status icon" status="error" statusMessage="This is an error message." tooltipPlacement="top-end" /> </StorybookComponents.Stack> </StorybookComponents.Stack>; ``` ### Prefix and suffix - description: <p>Add text, icons or a button at the beginning or end of the input field using <code>prefix</code> and <code>suffix</code>.</p> - example: ```jsx <StorybookComponents.Stack flexDirection="column"> <NumberInput prefix={<Input.Affix>$</Input.Affix>} value="20.00" /> <NumberInput suffix={<Input.Affix>%</Input.Affix>} value="10" /> <NumberInput prefix={ <Input.IconAffix> <Icons.Users /> </Input.IconAffix> } value="2" suffix={<Input.Affix>guests</Input.Affix>} /> </StorybookComponents.Stack>; ``` ### Read-only and disabled - description: <p>Inputs can also be made read-only or disabled entirely.</p><li><code>readOnly</code> disables the field, so users can't enter new values. The current value can still be copied.</li><li><code>disabled</code> removes all interactions and creates a disabled input state. Use it to highlight functions that are unavailable.</li> - example: ```jsx <StorybookComponents.Stack flexDirection="column"> <NumberInput placeholder="Read-only" readOnly /> <NumberInput placeholder="Disabled" disabled /> </StorybookComponents.Stack>; ``` ### Clear button - description: <p>Enable a button that lets users easily clear the input value by using the <code>clearButton</code> prop. Use it when the input value is optional or has to be cleared often.</p> - example: ```jsx () => { const [inputText, setInputText] = React.useState('20'); return ( <NumberInput value={inputText} clearButton onChange={setInputText} onClear={() => setInputText('')} /> ); }; ``` ### Increment by steps - description: <p>Use the <code>step</code> prop to specify what the increments are when clicking up or down on the arrows.</p><p></p><p>The arrows controls can be hidden with <code>hideStepper</code>. When the arrows are hidden, the field value can still be incremented by the set <code>step</code> when the field is changed using the arrow keys on the keyboard.</p> - example: ```jsx <StorybookComponents.Stack flexDirection="column"> <NumberInput step={20} placeholder="Click on the arrows to change the value by 20 units at a time." /> <NumberInput step={20} hideStepper placeholder="Click the up and down arrow keys to change the value by 20 when the arrows are hidden." /> </StorybookComponents.Stack>; ``` ### Min and max values - description: <p>If the user inputs something outside of the minimum or maximum, explain that it's invalid using the <code>invalidMessage</code> prop. </p><p></p><p>Get informed about invalid message events by using the <code>onInvalid</code> callback function.</p> - example: ```jsx <NumberInput min={-5} max={5} invalidMessage="Enter a number between -5 and 5." placeholder="Try typing a number less than -5 or more than 5" />; ``` ## Developer Examples ### Controlled - description: <p>Use NumberInput in controlled mode by passing <code>value</code> and <code>onChange</code> props.</p> - example: ```jsx () => { const [value, setValue] = React.useState('0'); return ( <NumberInput value={value} onChange={(numberValue, stringValue) => setValue(stringValue)} /> ); }; ``` ### Range validation - description: <p>The <code>min</code> and <code>max</code> properties enable built-in range validation, so no need to add <code>onChange</code> and <code>value.</code></p><p></p><p>Users will still be able to enter invalid numbers, but will be notified that what they entered is invalid. Set the error message using <code>invalidMessage.</code></p><p></p><p>Don't allow users to submit a form with an invalid input value. Use <code>onInvalid</code> to get an invalid event.</p> - example: ```jsx () => { const ref = React.useRef(); const [invalid, setInvalid] = React.useState(false); const [errorMessage, setErrorMessage] = React.useState(''); const onInvalid = value => { if (Number(value) < 2) { setErrorMessage('You must order at least 2 tickets.'); } if (Number(value) > 20) { setErrorMessage('You can only order up to 20 tickets at a time.'); } }; const onSubmit = () => { if (errorMessage) { ref.current.focus(); } }; return ( <Layout> <Cell> <FormField label="Number of tickets" infoContent="You can buy between 2 and 20 tickets at a time." > <NumberInput ref={ref} min={5} max={20} invalidMessage={errorMessage} onInvalid={onInvalid} placeholder="How many tickets would you like?" /> </FormField> </Cell> <Cell> <Button onClick={onSubmit}>Purchase</Button> </Cell> </Layout> ); }; ``` ### Integrating with react-hook-form - description: <p>react-hook-form reduces the amount of code you need to write while removing unnecessary re-renders. The preview here demonstrates form validation in action while using the number input.</p> - example: ```jsx () => { const [state, setState] = React.useState({}); const { handleSubmit, formState: { errors }, control, } = useForm(); const { field } = useController({ name: 'tickets', control, rules: { min: 2, max: 20 }, defaultValue: 2, }); const status = errors.tickets && 'error'; const statusMessage = errors.tickets && errors.tickets.type === 'min' ? 'You must order at least 2 tickets.' : 'You can only order up to 20 tickets at a time.'; const onSubmit = data => setState(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <Layout> <Cell> <FormField label="Number of tickets" infoContent="You can buy between 2 and 20 tickets at a time." > <NumberInput {...field} inputRef={field.ref} status={status} statusMessage={statusMessage} placeholder="How many tickets would you like?" /> </FormField> </Cell> <Cell> <Layout alignItems="center"> <Cell span={2}> <Button type="submit">Purchase</Button> </Cell> <Cell span={10}> <SectionHelper appearance="preview"> {JSON.stringify(state)} </SectionHelper> </Cell> </Layout> </Cell> </Layout> </form> ); }; ``` ## Common Use Case Examples ### Settings panel - description: <p>Use number input with other components like <code><Slider/></code> in a <code><FieldSet/></code> wrapper to group them together and let users define the exact numerical value. </p> - example: ```jsx () => { const [value, setValue] = React.useState(50); const [opacity, setOpacity] = React.useState(100); return ( <SidePanel skin="floating" width="420px" onCloseButtonClick onHelpButtonClick > <SidePanel.Header title="Events" /> <Box height="480px"> <VerticalTabs size="small" activeTabId={1} onChange={() => {}}> <VerticalTabs.TabItem id={0}>Main</VerticalTabs.TabItem> <VerticalTabs.TabItem id={1}>Design</VerticalTabs.TabItem> <VerticalTabs.TabItem id={2}>Text</VerticalTabs.TabItem> </VerticalTabs> <Divider direction="vertical" /> <SidePanel.Content noPadding> <SidePanel.Field> <Button priority="secondary" size="tiny" prefixIcon={<Icons.ChevronLeft />} > Back </Button> </SidePanel.Field> <SidePanel.Section title="Background"> <SidePanel.Field> <FieldSet legend="Background color" legendSize="small" legendPlacement="top" alignment="center" columns="30px auto 72px" > <FillPreview fill="#000000" aspectRatio={1} /> <Slider gradientColor="#000000" min={0} max={100} displayMarks={false} onChange={(value) => setOpacity(value)} value={opacity} /> <NumberInput value={opacity} min={0} max={100} onChange={(value) => setOpacity(value)} suffix={<Input.Affix>%</Input.Affix>} size="small" hideStepper /> </FieldSet> </SidePanel.Field> <SidePanel.Field> <FieldSet gap="small" legend="Border width" columns="auto 60px"> <Slider onChange={setValue} min={0} max={100} value={value} displayMarks={false} /> <Input value={value} size="small" onChange={(e) => setValue(e.target.value)} /> </FieldSet> </SidePanel.Field> </SidePanel.Section> </SidePanel.Content> </Box> </SidePanel> ); }; ``` ### Compound input - description: <p>Stack items next to each other with <code><Box/> </code>when fields represent the same data, or when a change in one input affects one or more other fields.</p> - example: ```jsx () => { const signs = { amount: '$', percentage: '%', }; const [value, setValue] = React.useState(20); const [signValue, setSignValue] = React.useState('amount'); const onClick = (_, value) => { setSignValue(value); }; return ( <FormField label="Discount" infoContent="This is how much will be taken off the final price. Discount can be offered in percentage off the starting price or a specific amount." > <Box gap="12px" width="100%"> <Box direction="vertical" width="100%"> <NumberInput prefix={ signValue === 'amount' ? ( <Input.Affix>{signs[signValue]}</Input.Affix> ) : null } suffix={ signValue === 'percentage' ? ( <Input.Affix>{signs[signValue]}</Input.Affix> ) : null } onChange={setValue} value={value} /> </Box> <Box direction="vertical"> <SegmentedToggle defaultSelected="amount" onClick={onClick} selected={signValue} > <SegmentedToggle.Button value="amount" tooltipText="Fixed amount"> {signs.amount} </SegmentedToggle.Button> <SegmentedToggle.Button value="percentage" tooltipText="Percentage"> {signs.percentage} </SegmentedToggle.Button> </SegmentedToggle> </Box> </Box> </FormField> ); }; ``` ### Disabled stepper - description: <p>Disable the arrows once the minimum or maximum value has been reached.</p> - example: ```jsx () => { const [value, setValue] = React.useState(20); return ( <FormField label="Number of tickets" statusMessage="Between 2 and 20 tickets" > <NumberInput min={2} max={20} value={value} onChange={setValue} placeholder="How many tickets would you like?" strict={true} /> </FormField> ); }; ```