Skip to content

Commit

Permalink
feat(chip): apply feedback given to stage 6 and key definitions
Browse files Browse the repository at this point in the history
  • Loading branch information
andresin87 committed Aug 23, 2023
1 parent 388959b commit f7899bc
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 85 deletions.
5 changes: 3 additions & 2 deletions packages/components/chip/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@
"homepage": "https://sparkui.vercel.app",
"license": "MIT",
"dependencies": {
"@spark-ui/internal-utils": "^2.0.2",
"@spark-ui/icon": "^2.0.2",
"@spark-ui/icons": "^1.19.5",
"@spark-ui/slot": "^1.6.1"
"@spark-ui/internal-utils": "^2.0.2",
"@spark-ui/slot": "^1.6.1",
"emulate-tab": "^1.2.1"
}
}
123 changes: 73 additions & 50 deletions packages/components/chip/src/Chip.doc.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ArgTypes as ExtendedArgTypes } from '@docs/helpers/ArgTypes'

import { Chip } from '.'
import { Callout } from '@docs/helpers/Callout'
import { Kbd } from '@spark-ui/kbd'

import * as stories from './Chip.stories'

Expand All @@ -12,13 +13,13 @@ import * as stories from './Chip.stories'

Help people enter information, make selections, filter content, or trigger actions.

### Chips aren’t buttons
#### Chips aren’t buttons

<Callout kind="warning">
Chips and buttons are similar in that they both provide visual cues to prompt users to take
actions and make selections. However, there are important distinctions to be aware of.
</Callout>

<br />
<Callout kind="warning">
Multiple chips should appear together in a group, whereas there should be no more than 3 buttons
in a single arrangement.
Expand All @@ -28,7 +29,21 @@ Chips are reactive and contextual, whereas buttons are static and predetermined.
A chip should offer a different action depending on the nature of the content it supports,
whereas a button should be a persistent fixture of a layout.
From a user-flow perspective,
chips represent forking paths in an experience while a button represents a linear step along a flow.
Chips represent forking paths in an experience while a button represents a linear step along a flow.

Chips handle 4 different features:

- **Display** data
- Fire **Actions** (button role)
- Handle **Selections** (button role selected)
- Manage **Deletions** (clear button)

Combining those features chips give 4 different usages:

- [Assist](#assist)
- [Filter](#filter)
- [Input](#input)
- [Suggestion](#suggestion)

## Install

Expand All @@ -50,44 +65,74 @@ import { Chip } from '@spark-ui/chip'
subcomponents={{
'Chip.Content': {
of: Chip.Content,
description: '',
description: 'The main chip container',
},
'Chip.LeadingIcon': {
of: Chip.LeadingIcon,
description: '',
description: 'Prepend a decorative icon inside the chip (to the left).',
},
'Chip.ClearButton': {
of: Chip.ClearButton,
description: '',
description: 'For removing the chip.',
},
}}
/>

## Default
## Usage

### Default

<Canvas of={stories.Default} />

## Types
### Intent

Chips handle 4 different features:
All intents are available

- **Display** data
- Fire **Actions** (button role)
- Handle **Selections** (button role selected)
- Manage **Deletions** (clear button)
<Canvas of={stories.DefaultIntent} />

Use chips to show options for a specific context combining those features.
When the chip has an associated action, specific hover and focus styles will be applied.

## Usages
<Canvas of={stories.ActionIntent} />

Four types of usages:
### Disabled

- [Assist](#assist)
- [Filter](#filter)
- [Input](#input)
- [Suggestion](#suggestion)
When a chip is disabled no action is fired when clicking on it

<Canvas of={stories.Disabled} />

### Max Width

The max width of any Chip is 240px including its borders, icons, and clear button.

<Canvas of={stories.MaxWidth} />

## Accessibility

- Focusable for **Clickable**, **Selectable**.
- Clear button keyboard accessible by default when appearing.

### Keyboard Interactions

#### Clickable

- Chip is **focusable**.

- <Kbd>Space</Kbd>: Activates the Chip.
- <Kbd>Enter</Kbd>: Activates the Chip.

## Assist
#### Selectable

- <Kbd>Space</Kbd>: Toggle its on/off state.
- <Kbd>Enter</Kbd>: Toggle its on/off state.

#### Cleanable

- <Kbd>Del</Kbd>: Removes the Chip and focus the next focusable element.
- <Kbd>Backspace</Kbd>: Removes the Chip and focus the previous focusable element.

## Advanced usage

### Assist

Assist chips represent smart or automated actions that can span multiple apps,
such as opening a calendar event from the home screen.
Expand All @@ -99,25 +144,25 @@ An alternative to assist chips is buttons, which should appear persistently and

<Canvas of={stories.AssistEvent} />

## Filter
### Filter

Filter chips represent filters for a collection.
They use tags or descriptive words to filter content.
They can be a good alternative to toggle buttons or checkboxes.

Tapping on a filter chip activates it and appends a leading checkmark icon to the starting edge of the chip label.

### Single selection
#### Single selection

<Canvas of={stories.SingleSelectionFilter} />

When there is no element selected (no filter applied) all the results are shown.

### Multiple Selection
#### Multiple Selection

Multi Selection Filters can represent unions or intersections between multiple categories.

#### Union
##### Union

The results shown on a union filter are all the elements that satisfy at least 1 of the selected filters.

Expand All @@ -126,7 +171,7 @@ The results shown on a union filter are all the elements that satisfy at least 1
- When there is no element selected (no filter applied) all the results are shown.
- When all the elements are selected all the results are shown.

#### Intersection
##### Intersection

The results shown on a union filter are all the elements that satisfy all the selected filters.

Expand All @@ -136,40 +181,18 @@ The results shown on a union filter are all the elements that satisfy all the se
- When all the elements are selected only shows the results that
satisfy all the filters (normally none of them).

## Input
### Input

Input chips represent discrete pieces of information entered by a user, such as Gmail contacts or filter options within a search field.

They enable user input and verify that input by converting text into chips.

<Canvas of={stories.Input} />

## Suggestion
### Suggestion

Suggestion chips help narrow a user’s intent by presenting dynamically generated suggestions, such as possible responses or search filters.

<Canvas of={stories.Suggestion} />

Suggestion chips can offer quick-reply options in a chat or email app, or It can help the user start a search.

## Max Width

The max width of any Chip is 240px including its borders, icons, and clear button.

<Canvas of={stories.MaxWidth} />

## Intent

All intents are available

<Canvas of={stories.DefaultIntent} />

When the chip has an associated action, specific hover and focus styles will be applied.

<Canvas of={stories.ActionIntent} />

# Disabled

When a chip is disabled no action is fired when clicking on it

<Canvas of={stories.Disabled} />
56 changes: 34 additions & 22 deletions packages/components/chip/src/Chip.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,22 @@ const singleSet = Array.from(
'🍎': ['fruit'],
'🍇': ['fruit'],
'🍌': ['fruit'],
'🍆': ['tubercle'],
'🧅': ['tubercle'],
'🥔': ['tubercle'],
'🌶': ['tubercle'],
'🥕': ['tubercle'],
'🍆': ['vegetable'],
'🧅': ['vegetable'],
'🥔': ['vegetable'],
'🌶': ['vegetable'],
'🥕': ['vegetable'],
})
)
).sort(() => 0.5 - Math.random())
)

export const SingleSelectionFilter: StoryFn = () => {
const [activeFilter, setActive] = useState<undefined | string>('fruit')

return (
<div className="flex flex-col gap-md">
<div className="flex flex-row items-start gap-md">
{['fruit', 'tubercle'].map(filter => {
{['fruit', 'vegetable'].map(filter => {
const isActive = activeFilter === filter

return (
Expand Down Expand Up @@ -118,7 +118,7 @@ const multipleUnionSet = Array.from(
'🌲': ['tree'],
})
)
).sort(() => 0.5 - Math.random())
)
export const UnionFilter: StoryFn = () => {
const [activeFilters, setActiveFilters] = useState<string[]>(['animal', 'tree'])

Expand Down Expand Up @@ -196,7 +196,7 @@ const multipleintersectionSet = Array.from(
'🦜': ['land', 'air', 'wild', 'domestic'],
})
)
).sort(() => 0.5 - Math.random())
)
export const IntersectionFilter: StoryFn = () => {
const [activeFilters, setActiveFilters] = useState<string[]>(['land', 'wild'])

Expand Down Expand Up @@ -273,12 +273,16 @@ export const Input: StoryFn = () => {
</div>
<span className="flex gap-md">
{tags.map(tag => (
<Chip intent="neutral" key={tag} design="dashed">
<Chip
intent="neutral"
key={tag}
design="dashed"
onClear={() => {
setTags(tags.filter(currentTag => tag !== currentTag))
}}
>
<Chip.Content>{tag}</Chip.Content>
<Chip.ClearButton
onClick={() => setTags(tags.filter(currentTag => tag !== currentTag))}
label="clear"
/>
<Chip.ClearButton label="clear" />
</Chip>
))}
</span>
Expand All @@ -303,21 +307,21 @@ export const MaxWidth: StoryFn = () => {
</div>
<div className="flex flex-col items-start justify-start gap-md">
<Chip>{content}</Chip>
<Chip>
<Chip onClear={() => console.log('clear')}>
<Chip.LeadingIcon aria-label="label">
<Icon>
<Check />
</Icon>
</Chip.LeadingIcon>
<Chip.Content>{content}</Chip.Content>
<Chip.ClearButton onClick={() => console.log('clear')} label="clear" />
<Chip.ClearButton label="clear" />
</Chip>
<Chip>
<Chip.Content>{content}</Chip.Content>
</Chip>
<Chip>
<Chip onClear={() => console.log('clear')}>
<Chip.Content>{content}</Chip.Content>
<Chip.ClearButton onClick={() => console.log('clear')} label="clear" />
<Chip.ClearButton label="clear" />
</Chip>
</div>
</div>
Expand All @@ -329,9 +333,14 @@ export const DefaultIntent: StoryFn = _args => (
{designs.map(design => (
<div key={design} className="flex flex-wrap gap-md">
{intents.map(intent => (
<Chip design={design} key={`${design}-${intent}`} intent={intent}>
<Chip
onClear={() => console.log('clear')}
design={design}
key={`${design}-${intent}`}
intent={intent}
>
<Chip.Content>{intent}</Chip.Content>
<Chip.ClearButton onClick={() => console.log('clear')} label="clear" />
<Chip.ClearButton label="clear" />
</Chip>
))}
</div>
Expand All @@ -349,9 +358,10 @@ export const ActionIntent: StoryFn = _args => (
key={`${design}-${intent}`}
intent={intent}
onClick={() => console.log(`click ${design} ${intent}`)}
onClear={() => console.log('clear')}
>
<Chip.Content>{intent}</Chip.Content>
<Chip.ClearButton onClick={() => console.log('clear')} label="clear" />
<Chip.ClearButton label="clear" />
</Chip>
))}
</div>
Expand All @@ -370,9 +380,10 @@ export const Disabled: StoryFn = _args => (
key={`${design}-${intent}`}
intent={intent}
onClick={() => console.log(`click ${design} ${intent}`)}
onClear={() => console.log('clear')}
>
<Chip.Content>{intent}</Chip.Content>
<Chip.ClearButton onClick={() => console.log('clear')} label="clear" />
<Chip.ClearButton label="clear" />
</Chip>
))}
</div>
Expand Down Expand Up @@ -438,6 +449,7 @@ export const Suggestion: StoryFn = () => {
aria-label="email"
onBlur={blurHandler}
onFocus={focusHandler}
onChange={event => setContent(event.target.value)}
value={content}
/>
<InputGroup.ClearButton aria-label="clear" onClick={() => setContent('')} />
Expand Down
Loading

0 comments on commit f7899bc

Please sign in to comment.