-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[DST-363]: Adding Guideline-Page of how to use
Multiselection
(#3824)
Co-authored-by: sarahgm <sarahgm@users.noreply.github.com> Co-authored-by: Sebastian Sebald <sebastian.sebald@gmail.com> Co-authored-by: sarahgm <sarah.gosmann@reservix.de> Co-authored-by: sarahgm <38324334+sarahgm@users.noreply.github.com> Co-authored-by: Sebastian Sebald <sebastian.sebald@reservix.de>
- Loading branch information
1 parent
8d2fea1
commit 879a0e1
Showing
12 changed files
with
343 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
"@marigold/components": patch | ||
"@marigold/docs": patch | ||
--- | ||
|
||
Adding Multiselect guideline |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
--- | ||
title: Multiple Selection | ||
caption: Learn about how & when to use multiple selection | ||
badge: new | ||
--- | ||
|
||
The purpose of this guide is to provide clear instructions and guidelines for selecting the appropriate method of multiple selection within your design system. The guide will outline the different possibilities for multiple selection, explain their differences, and provide examples and recommendations for when to use each type. | ||
|
||
### Different possibilities for multiple selection: | ||
|
||
- [Checkbox Group](#checkbox-group) | ||
- [Multiselect](#multiselect) | ||
- [Tag Group](#taggroup) | ||
|
||
So let us explain each different possibility for multiple selection: | ||
|
||
### Checkbox Group | ||
|
||
Checkbox Group is a type of multiple selection interfaces that presents options as checkboxes. User can Select Multiple options by checking the corresponding checkboxes. | ||
|
||
It provides a familiar and straightforward interface for selecting multiple options. | ||
|
||
#### When to use ? | ||
|
||
- Use CheckboxGroup when the number of options is moderate to large, typically more than 10-15 options. | ||
- Use CheckboxGroup when the selected options do not need to be visible within the selection interface. | ||
- CheckboxGroup is useful when the primary focus is on selecting options rather than displaying the selected choices. | ||
|
||
#### Examples | ||
|
||
<ComponentDemo file="./multiselect-checkbox-group.demo.tsx" /> | ||
|
||
### Multiselect | ||
|
||
Multiselect is a type of multiple selection interface that displays options in a list format with the ability to select multiple items. | ||
|
||
It allows users to choose multiple options by clicking on the items in the list and provides a comprehensive view of available options and the selected items. | ||
|
||
#### When to use ? | ||
|
||
- Multiselect is a type of multiple selection interface that displays options in a list format with the ability to select multiple items. | ||
- It allows users to choose multiple options by clicking on the items in the list. | ||
- Multiselect is suitable when the number of options is large, and the selected options need to be visible within the selection interface. | ||
- It provides a comprehensive view of available options and the selected items. | ||
|
||
#### Examples | ||
|
||
<ComponentDemo file="./multiselect.demo.tsx" /> | ||
|
||
### TagGroup | ||
|
||
TagGroup is a type of multiple selection interface that allows users to select multiple options and displays the selected options as tags. | ||
|
||
It is useful when the number of options is relatively small, and the selected options need to be visible at all times. | ||
|
||
#### When to use ? | ||
|
||
- Use TagGroup when the number of options is limited, typically up to 10-15 options. | ||
- Use TagGroup when the selected options need to be visible to the user at all times. | ||
- TagGroup is well-suited for situations where space is a concern and displaying selected options as tags provides a clear representation. | ||
|
||
#### Examples | ||
|
||
<ComponentDemo file="./tag-group-multiselect.demo.tsx" /> | ||
|
||
## Related | ||
|
||
- [MultiSelect Recipe](/recipes/multiselect-recipe) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { useState } from 'react'; | ||
|
||
import { Checkbox, CheckboxGroup } from '@marigold/components'; | ||
|
||
export default () => { | ||
const [selected, setSelected] = useState<string[]>([]); | ||
return ( | ||
<> | ||
<CheckboxGroup | ||
label="Choose your toppings:" | ||
onChange={setSelected} | ||
description="Just click on the options" | ||
> | ||
<Checkbox value="ham">🐖 Ham</Checkbox> | ||
<Checkbox value="beef" disabled> | ||
🐄 Beef (out of stock) | ||
</Checkbox> | ||
<Checkbox value="tuna">🐟 Tuna</Checkbox> | ||
<Checkbox value="tomatos">🍅 Tomatos</Checkbox> | ||
<Checkbox value="onions">🧅 Onions</Checkbox> | ||
<Checkbox value="pineapple">🍍 Pineapple</Checkbox> | ||
</CheckboxGroup> | ||
<hr /> | ||
<pre>Selected values: {selected.join(', ')}</pre> | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { Multiselect } from '@marigold/components'; | ||
|
||
export default () => { | ||
return ( | ||
<Multiselect label="Select favorite fruits"> | ||
<Multiselect.Item id="apple">🍎 Apple</Multiselect.Item> | ||
<Multiselect.Item id="banana">🍌 Banana</Multiselect.Item> | ||
<Multiselect.Item id="orange">🍊 Orange</Multiselect.Item> | ||
<Multiselect.Item id="strawberry">🍓 Strawberry</Multiselect.Item> | ||
<Multiselect.Item id="mango">🥭 Mango</Multiselect.Item> | ||
<Multiselect.Item id="watermelon">🍉 Watermelon</Multiselect.Item> | ||
</Multiselect> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { useState } from 'react'; | ||
|
||
import { Tag } from '@marigold/components'; | ||
|
||
export default () => { | ||
const [selected, setSelected] = useState(new Set(['parking'])); | ||
|
||
return ( | ||
<> | ||
<Tag.Group | ||
label="Amenities" | ||
selectionMode="multiple" | ||
selectedKeys={selected} | ||
onSelectionChange={setSelected as any} | ||
> | ||
<Tag id="laundry">Laundry</Tag> | ||
<Tag id="fitness">Fitness center</Tag> | ||
<Tag id="parking">Parking</Tag> | ||
<Tag id="pool">Swimming pool</Tag> | ||
<Tag id="breakfast">Breakfast</Tag> | ||
</Tag.Group> | ||
<p> | ||
Current selection (controlled):{' '} | ||
{selected === 'all' ? 'all' : [...selected].join(', ')} | ||
</p> | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { Multiselect } from '@marigold/components'; | ||
|
||
export default () => { | ||
return ( | ||
<Multiselect label="Select a country"> | ||
<Multiselect.Item id="Germany">🇩🇪 Germany</Multiselect.Item> | ||
<Multiselect.Item id="France">🇫🇷 France</Multiselect.Item> | ||
<Multiselect.Item id="India">🇮🇳 India</Multiselect.Item> | ||
<Multiselect.Item id="Brazil">🇧🇷 Brazil</Multiselect.Item> | ||
<Multiselect.Item id="Canada">🇨🇦 Canada</Multiselect.Item> | ||
<Multiselect.Item id="Australia">🇦🇺 Australia</Multiselect.Item> | ||
</Multiselect> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
--- | ||
title: MultiSelect | ||
caption: Here you can find some recipes for using the MultiSelect component. | ||
badge: new | ||
--- | ||
|
||
The `<MultiSelect>` component combines a text input with a listbox, allowing users to filter a list of options to items matching a query, selecting multiple items or adding a new value. | ||
|
||
## Usage | ||
|
||
### Import | ||
|
||
To import the component you just have to use this code below. | ||
|
||
```tsx | ||
import { MultiSelect } from '@marigold/components'; | ||
``` | ||
|
||
## Example | ||
|
||
You can use the `MultiSelect` to enable users to select multiple values and search for them. | ||
|
||
<ComponentDemo file="./multiselect-basic.demo.tsx" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
packages/components/src/Multiselect/Multiselect.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* eslint-disable react-hooks/rules-of-hooks */ | ||
import { Meta, StoryObj } from '@storybook/react'; | ||
|
||
import { Multiselect } from './Multiselect'; | ||
|
||
const meta = { | ||
title: 'Components/Multiselect', | ||
argTypes: {}, | ||
args: { | ||
description: 'This is a help text description', | ||
errorMessage: 'Something went wrong', | ||
}, | ||
} satisfies Meta; | ||
|
||
export default meta; | ||
|
||
export const Basic: StoryObj<typeof Multiselect> = { | ||
render: () => { | ||
return ( | ||
<> | ||
<Multiselect | ||
label="Animals" | ||
// disabledKeys={['snake']} | ||
defaultSelectedKeys={['cat', 'dog']} | ||
> | ||
<Multiselect.Item id="red-panda">Red Panda</Multiselect.Item> | ||
<Multiselect.Item id="cat">Cat</Multiselect.Item> | ||
<Multiselect.Item id="dog">Dog</Multiselect.Item> | ||
<Multiselect.Item id="aardvark">Aardvark</Multiselect.Item> | ||
<Multiselect.Item id="kangaroo">Kangaroo</Multiselect.Item> | ||
<Multiselect.Item id="snake">Snake</Multiselect.Item> | ||
<Multiselect.Item id="vegan">Vegan</Multiselect.Item> | ||
<Multiselect.Item id="margrita">Margrita</Multiselect.Item> | ||
</Multiselect> | ||
</> | ||
); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
/** | ||
* - list of removable tags | ||
* - combobox to search for an item | ||
* | ||
* - selecting an item from the combobox -> adds it to the tags, clears combobox input | ||
* - combobox only shows unselected items | ||
*/ | ||
import { Children, ReactNode, useState } from 'react'; | ||
import { Key } from 'react-aria-components'; | ||
import type RAC from 'react-aria-components'; | ||
|
||
import { useListData } from '@react-stately/data'; | ||
|
||
import { ComboBox } from '../ComboBox'; | ||
import { Tag } from '../TagGroup'; | ||
|
||
// Item | ||
// --------------- | ||
export interface MultiSelectItemProps { | ||
id: Key; | ||
children: ReactNode; | ||
} | ||
|
||
const Item = (_: MultiSelectItemProps) => null; | ||
|
||
// Props | ||
// --------------- | ||
export interface MultiSelectProps extends RAC.ComboBoxProps<object> { | ||
label?: string; | ||
children?: ReactNode; | ||
defaultSelectedKeys?: 'all' | Iterable<Key>; | ||
} | ||
|
||
// Component | ||
// --------------- | ||
export const Multiselect = ({ | ||
label, | ||
children, | ||
...props | ||
}: MultiSelectProps) => { | ||
// Fake react-aria collection items | ||
const items = Children.map(children, ({ props }: any) => props); | ||
|
||
const list = useListData<MultiSelectItemProps>({ | ||
initialItems: items, | ||
initialSelectedKeys: props.defaultSelectedKeys, | ||
getKey: item => item.id, | ||
}); | ||
|
||
const selected = list.items.filter(item => | ||
list.selectedKeys === 'all' ? true : list.selectedKeys.has(item.id) | ||
); | ||
const unselected = list.items.filter(item => !selected.includes(item)); | ||
|
||
// Remove tag | ||
const setUnselected = (keys: Set<Key>) => { | ||
const next: Set<Key> = | ||
list.selectedKeys === 'all' ? new Set(items) : new Set(list.selectedKeys); | ||
|
||
if (list.selectedKeys !== 'all') { | ||
keys.forEach(key => { | ||
next.delete(key); | ||
}); | ||
} | ||
|
||
list.setSelectedKeys(next); | ||
}; | ||
|
||
// Combobox Stuff | ||
const [value, setValue] = useState(''); | ||
const selectItem = (key: Key) => { | ||
// add to selected items | ||
if (list.selectedKeys !== 'all') { | ||
const next = list.selectedKeys.add(key); | ||
list.setSelectedKeys(next); | ||
} | ||
|
||
// Clear combobox | ||
const input = document.activeElement as HTMLInputElement; | ||
setTimeout(() => { | ||
setValue(''); | ||
}, 0); | ||
input.focus(); | ||
}; | ||
|
||
return ( | ||
<div className="flex flex-wrap gap-1"> | ||
<Tag.Group | ||
items={selected} | ||
allowsRemoving | ||
onRemove={setUnselected} | ||
renderEmptyState={() => null} | ||
> | ||
{(item: MultiSelectItemProps) => ( | ||
<Tag key={item.id} id={item.id}> | ||
{item.children} | ||
</Tag> | ||
)} | ||
</Tag.Group> | ||
<ComboBox | ||
value={value} | ||
onChange={setValue} | ||
onSelectionChange={selectItem} | ||
menuTrigger="focus" | ||
disabled={unselected.length === 0} | ||
placeholder={unselected.length === 0 ? 'All items selected' : ''} | ||
{...props} | ||
> | ||
{unselected.map((item: MultiSelectItemProps) => ( | ||
<ComboBox.Item key={item.id} id={item.id}> | ||
{item.children} | ||
</ComboBox.Item> | ||
))} | ||
</ComboBox> | ||
</div> | ||
); | ||
}; | ||
|
||
Multiselect.Item = Item; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/** | ||
* @private | ||
*/ | ||
export * from './Multiselect'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters