@wix/design-system
Version:
@wix/design-system
367 lines (330 loc) • 12.3 kB
Markdown
## Feature Examples
### Size
- description: <p>Adjust the component's size using the <code>size</code> prop. It supports 3 sizes:</p><li><code>large</code> is for onboarding flows, where the input needs more emphasis.</li><li><code>medium</code> is the default used in all common cases.</li><li><code>small</code> should only be used in dense layouts.</li>
- example:
```jsx
<StorybookComponents.Stack flexDirection="column">
<Search size="large" placeholder="Large" />
<Search size="medium" placeholder="Medium" />
<Search size="small" placeholder="Small" />
</StorybookComponents.Stack>;
```
### Border
- description: <p>Style the component using the <code>border</code> prop. It supports 4 styles:</p><li><code>round</code> is the default used in all common cases.</li><li><code>standard</code> is for when search is part of a form.</li><li><code>bottomLine</code> is for titles that can be edited on selection.</li><li><code>none</code> should be used in the Content Manager's spreadsheet cell.</li>
- example:
```jsx
<StorybookComponents.Stack flexDirection="column">
<Search border="round" placeholder="Search..." />
<Search border="standard" placeholder="Search..." />
<Search border="bottomLine" placeholder="Search..." />
<Search border="none" placeholder="Search..." />
</StorybookComponents.Stack>;
```
### Status
- description: <p>Control a component's status using the <code>status</code> prop. It supports 3 states:</p><li><code>loading</code> is to show that a value is being uploaded to the server or that results are loading.</li><li><code>error</code> is inherited from input, but there's no use case at the moment.</li><li><code>warning</code> is inherited from input, but there's no use case at the moment.</li>
- example:
```jsx
<StorybookComponents.Stack flexDirection="column">
<Search status="loading" placeholder="Search..." />
<Search status="error" placeholder="Search..." />
<Search status="warning" placeholder="Search..." />
</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><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">
<FormField status="loading" statusMessage="This is a loading message.">
<Search placeholder="See message below" />
</FormField>
<Search
status="loading"
placeholder="Hover on the loader"
statusMessage="This is a loading message."
/>
</StorybookComponents.Stack>;
```
### Read-only and disabled
- description: <p>The component can be set to read-only or <code>disabled</code>. To disable users from typing into the input, but allow them to copy or remove an entered value, use the <code>readOnly</code> prop. </p>
- example:
```jsx
<StorybookComponents.Stack flexDirection="column">
<Search readOnly value="Read only" />
<Search disabled value="Disabled" />
</StorybookComponents.Stack>;
```
### Clear button
- description: <p>Remove entered keywords quickly with a clear button. The button is enabled by default for all search fields. Avoid removing it if possible.</p>
- example:
```jsx
() => {
const [inputText, setInputText] = React.useState(
'Click clear button to erase this value',
);
return (
<Search
value={inputText}
clearButton
onChange={e => setInputText(e.target.value)}
onClear={() => setInputText('')}
/>
);
};
```
### Options
- description: <p>Provide common suggestions by passing an array of <code>options</code> to a component. A <code>predicate</code> function can be used to filter these options.</p><p></p><p>In this example, <code>predicate</code> makes the search input case sensitive.</p>
- example:
```jsx
() => {
const [text, setText] = React.useState('');
const options = [
{ value: 'Option 1', id: 0 },
{ value: 'Option 2', id: 1 },
{ value: 'Option 3', id: 2 },
{ value: 'Option 4', id: 3 },
{ value: 'Option 5', id: 4 },
];
return (
<Search
value={text}
onChange={e => setText(e.target.value)}
onClear={() => setText('')}
options={options}
predicate={option => option.value.indexOf(text) !== -1}
/>
);
};
```
### Options container dimensions
- description: <p>Control container size with three properties.</p><li><code>maxHeightPixels</code> specifies the maximum height for longer lists.</li><li><code>dropdownWidth</code> sets the width to "auto" or "max-content" to match the content width or specify it in pixels.</li><li><code>minWidthPixels</code> increases the width of the dropdown popover manually.</li>
- example:
```jsx
() => {
const [text, setText] = React.useState('');
const options = [
{ value: 'Option 1', id: 0 },
{ value: 'Option 2', id: 1 },
{ value: 'Option 3', id: 2 },
{ value: 'Option 4', id: 3 },
{ value: 'Option 5', id: 4 },
];
return (
<StorybookComponents.Stack>
<Search
value={text}
clearButton={true}
onChange={e => setText(e.target.value)}
onClear={() => setText('')}
options={options}
maxHeightPixels="160px"
minWidthPixels="300px"
dropdownWidth="auto"
popoverProps={{
placement: 'bottom-start',
}}
/>
</StorybookComponents.Stack>
);
};
```
### Expandable
- description: <p>Collapse the search input into an icon button and only expand it intentionally on click with the <code>expandable</code> prop. Control the width of expanded input with <code>expandWidth</code>.</p>
- example:
```jsx
<StorybookComponents.Stack flexDirection="column">
<Search expandable />
<Search expandable expandWidth="400px" />
</StorybookComponents.Stack>;
```
### Debounce
- description: <p>Control the delay of a search in milliseconds with the <code>debounceMs</code> prop. Use it to prevent sending a query to the server before the user finishes typing.</p>
- example:
```jsx
() => {
const [textInstant, setTextInstant] = React.useState('');
const [textDelayed, setTextDelayed] = React.useState('');
return (
<StorybookComponents.Stack flexDirection="column">
<StorybookComponents.Stack flexDirection="column" gap="12px">
<Search
placeholder="Search for instant results..."
value={textInstant}
onChange={e => setTextInstant(e.target.value)}
onClear={() => setTextInstant('')}
/>
<StorybookComponents.Stack>
<Text size="tiny">Results for:</Text>
<Text size="tiny" weight="bold" skin="primary">
{textInstant}
</Text>
</StorybookComponents.Stack>
</StorybookComponents.Stack>
<StorybookComponents.Stack flexDirection="column" gap="12px">
<Search
placeholder="Search for delayed results..."
debounceMs={250}
value={textDelayed}
onChange={e => setTextDelayed(e.target.value)}
onClear={() => setTextDelayed('')}
/>
<StorybookComponents.Stack>
<Text size="tiny">Results for:</Text>
<Text size="tiny" weight="bold" skin="primary">
{textDelayed}
</Text>
</StorybookComponents.Stack>
</StorybookComponents.Stack>
</StorybookComponents.Stack>
);
};
```
## Common Use Case Examples
### Filtering the list
- description: Use search as an action inside tables or cards to filter large data sets.
- example:
```jsx
() => {
const [filterBy, setFilterBy] = React.useState('');
const [activeSearch, setActiveSearch] = React.useState('');
const records = [
{
name: 'Red slippers',
sku: 25232564,
status: 'In stock',
price: '$14.00',
},
{ name: 'Velvet hat', sku: 35246432, status: 'In stock', price: '$29.00' },
{
name: 'Silver jeans',
sku: 4864310,
status: 'Out of stock',
price: '$69.00',
},
{
name: 'Orange socks',
sku: 125156422,
status: 'In stock',
price: '$7.00',
},
];
const columns = [
{ title: 'Name', render: row => row.name },
{ title: 'SKU', render: row => row.sku },
{ title: 'Status', render: row => row.status },
{ title: 'Price', render: row => row.price },
];
const filterOptions = [
{ id: '', value: 'All statuses' },
{ id: 'In stock', value: 'In stock' },
{ id: 'Out of stock', value: 'Out of stock' },
];
const getFilteredData = () =>
records.filter(({ name, sku, status, price }) => {
if (filterBy && status !== filterBy) {
return false;
}
const searchData = [name, sku, status, price].join(' ').toLowerCase();
const searchQuery = activeSearch.trim().toLowerCase();
if (searchQuery && searchData.indexOf(searchQuery) === -1) {
return false;
}
return true;
});
const MainToolbar = () => {
return (
<TableToolbar>
<TableToolbar.ItemGroup position="start">
<TableToolbar.Item>
<TableToolbar.Title>Products</TableToolbar.Title>
</TableToolbar.Item>
</TableToolbar.ItemGroup>
<TableToolbar.ItemGroup position="end">
<TableToolbar.Item>
<TableToolbar.Label>
Filter by
<Box width="175">
<Dropdown
size="small"
options={filterOptions}
selectedId={filterBy}
border="round"
onSelect={({ id }) => setFilterBy(id)}
/>
</Box>
</TableToolbar.Label>
</TableToolbar.Item>
<TableToolbar.Item>
<Box width="200">
<Search
size="small"
value={activeSearch}
onChange={e => setActiveSearch(e.target.value)}
onClear={() => setActiveSearch('')}
/>
</Box>
</TableToolbar.Item>
</TableToolbar.ItemGroup>
</TableToolbar>
);
};
const ActionsToolbar = ({ selectedCount, getSelectedIds }) => (
<TableToolbar>
<TableToolbar.ItemGroup position="start">
<TableToolbar.Item>
<TableToolbar.SelectedCount>{`${selectedCount} Selected`}</TableToolbar.SelectedCount>
</TableToolbar.Item>
</TableToolbar.ItemGroup>
<TableToolbar.ItemGroup position="end">
<TableToolbar.Item layout="button">
<Button skin="light" prefixIcon={<Icons.Upload />}>
Export
</Button>
</TableToolbar.Item>
<TableToolbar.Item layout="button">
<Button skin="light" prefixIcon={<Icons.Duplicate />}>
Duplicate
</Button>
</TableToolbar.Item>
<TableToolbar.Item layout="button">
<Button skin="light" prefixIcon={<Icons.Edit />}>
Edit
</Button>
</TableToolbar.Item>
<TableToolbar.Divider />
<TableToolbar.Item>
<Search
expandable
value={activeSearch}
onChange={e => setActiveSearch(e.target.value)}
/>
</TableToolbar.Item>
</TableToolbar.ItemGroup>
</TableToolbar>
);
return (
<Card>
<Table showSelection data={getFilteredData()} columns={columns}>
<Table.ToolbarContainer>
{selectionContext =>
selectionContext.selectedCount === 0
? MainToolbar()
: ActionsToolbar({ ...selectionContext })
}
</Table.ToolbarContainer>
<Table.Content />
{!records.length && (
<Table.EmptyState
subtitle={
<Text>
{'There are no search results matching '}
<Text weight="normal">{`"${activeSearch}"`}</Text>
</Text>
}
/>
)}
</Table>
</Card>
);
};
```