Skip to content

Commit

Permalink
feat(components): update Chip.Prefix to accept more than Icon and Ava…
Browse files Browse the repository at this point in the history
…tar (#2020)

* feat(components): Update Chip.Prefix to accept more than Icon and Avatar as children

* test: update tests for Chip.Prefix to cover new behavior

* feat(components): Update Chip.Prefix to conditionally render based on content presence

* Add Chip.Prefix behavior to the docs

* refactor: use fragment isntead of casting and remove unnecessary guard
  • Loading branch information
demoraesgui authored Sep 18, 2024
1 parent e95e2f4 commit 1a6c72c
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 62 deletions.
125 changes: 77 additions & 48 deletions docs/components/Chip/Chip.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,37 @@ import { Flex } from "@jobber/components/Flex";

# Chip

Chip is a flexible component that can be used for
Chip is a flexible component that can be used for

- inline single- or multi-selection of items
- triggering filtering and selection components like [Combobox](../?path=/docs/components-selections-combobox--docs)
- triggering filtering and selection components like
[Combobox](../?path=/docs/components-selections-combobox--docs)
- presenting grouped items that can be added or removed

<Canvas>
<Chip label="Gavin Messina">
<Chip.Prefix>
<Avatar size="small" imageUrl="https://images.unsplash.com/photo-1669475535925-a011d7c31d45?q=80&w=1886&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" />
<Avatar
size="small"
imageUrl="https://images.unsplash.com/photo-1669475535925-a011d7c31d45?q=80&w=1886&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
/>
</Chip.Prefix>
<Chip.Suffix>
<Icon name="cross" size="small" />
</Chip.Suffix>
</Chip.Suffix>
</Chip>
</Canvas>

## Usage guidelines

See the [Comparison story](../?path=/story/components-selections-chip-web-comparisons--all)
See the
[Comparison story](../?path=/story/components-selections-chip-web-comparisons--all)
for a full overview of potential Chip variants.

### Variations

The base variation of Chip should be used in most cases. When a lighter-weight approach
is desired, use the subtle variation.
The base variation of Chip should be used in most cases. When a lighter-weight
approach is desired, use the subtle variation.

<Canvas>
<Flex template={["shrink", "shrink"]} direction="row" gap="small">
Expand All @@ -43,9 +49,9 @@ is desired, use the subtle variation.

### Selection

Chip allows users to make selections in scenarios where space
is at a premium. It has three high-level usages: single-select, multi-select,
and add/dismiss selection.
Chip allows users to make selections in scenarios where space is at a premium.
It has three high-level usages: single-select, multi-select, and add/dismiss
selection.

#### Single-select

Expand All @@ -61,12 +67,16 @@ selected single-select Chip can be de-selected by the user, leaving all
selections blank.

<Canvas>
<Flex template={["shrink", "shrink", "shrink", "shrink"]} direction="row" gap="small">
<Flex
template={["shrink", "shrink", "shrink", "shrink"]}
direction="row"
gap="small"
>
<Chip label="Option 1" variation="subtle" />
<Chip label="Option 2">
<Chip.Suffix>
<Icon name="checkmark" size="small" color="interactiveSubtle" />
</Chip.Suffix>
</Chip.Suffix>
</Chip>
<Chip label="Option 3" variation="subtle" />
<Chip label="Option 4" variation="subtle" />
Expand All @@ -88,31 +98,35 @@ Similar to Checkbox, a selected multi-select Chip can be de-selected by the
user, leaving all selections blank.

<Canvas>
<Flex template={["shrink", "shrink", "shrink", "shrink"]} direction="row" gap="small">
<Flex
template={["shrink", "shrink", "shrink", "shrink"]}
direction="row"
gap="small"
>
<Chip label="Option 1">
<Chip.Suffix>
<Icon name="checkmark" size="small" color="interactiveSubtle" />
</Chip.Suffix>
</Chip.Suffix>
</Chip>
<Chip label="Option 2">
<Chip.Suffix>
<Icon name="checkmark" size="small" color="interactiveSubtle" />
</Chip.Suffix>
</Chip.Suffix>
</Chip>
<Chip label="Option 3" variation="subtle" />
<Chip label="Option 4" >
<Chip label="Option 4">
<Chip.Suffix>
<Icon name="checkmark" size="small" color="interactiveSubtle" />
</Chip.Suffix>
</Chip.Suffix>
</Chip>
</Flex>
</Canvas>

#### Add/dismiss selection

When the user will be selecting one or more items by inputting their own Chip
options, use a dismissible Chip. Think of a case like team assignment, where
the user can add multiple users represented as Chips, and click on the dismiss
options, use a dismissible Chip. Think of a case like team assignment, where the
user can add multiple users represented as Chips, and click on the dismiss
suffix of the Chip to remove a user.

The dismissible Chip allows them to remove previous selections from the Chips.
Expand All @@ -122,75 +136,91 @@ would be far too many Chip options to present in one group, and would be
overwhelming for the user to interpret.

<Canvas>
<Flex template={["shrink", "shrink", "shrink", "shrink", "shrink"]} direction="row" gap="small">
<Flex
template={["shrink", "shrink", "shrink", "shrink", "shrink"]}
direction="row"
gap="small"
>
<Chip label="Add" variation="subtle">
<Chip.Suffix>
<Icon name="add" size="small" color="interactiveSubtle" />
</Chip.Suffix>
</Chip.Suffix>
</Chip>
<Chip label="Option 1">
<Chip.Suffix>
<Icon name="cross" size="small" color="interactiveSubtle" />
</Chip.Suffix>
</Chip.Suffix>
</Chip>
<Chip label="Option 2">
<Chip.Suffix>
<Icon name="cross" size="small" color="interactiveSubtle" />
</Chip.Suffix>
</Chip.Suffix>
</Chip>
<Chip label="Option 3" >
<Chip label="Option 3">
<Chip.Suffix>
<Icon name="cross" size="small" color="interactiveSubtle" />
</Chip.Suffix>
</Chip.Suffix>
</Chip>
<Chip label="Option 4" >
<Chip label="Option 4">
<Chip.Suffix>
<Icon name="cross" size="small" color="interactiveSubtle" />
</Chip.Suffix>
</Chip.Suffix>
</Chip>
</Flex>
</Canvas>

### Invalid

If something goes awry with a selection or you otherwise need to convey to the user
that something's gone wrong in relation to the Chip, you can use the invalid state.
If something goes awry with a selection or you otherwise need to convey to the
user that something's gone wrong in relation to the Chip, you can use the
invalid state.

<Canvas>
<Chip label="Select team" invalid>
<Chip.Prefix>
<Icon name="alert" size="small" />
</Chip.Prefix>
</Chip.Prefix>
</Chip>
</Canvas>

## Sub Components

### Chip.Prefix

When `Chip.Prefix` is provided an Icon or Avatar as the immediate child or
children, some extra markup and styles will automatically be added. If these
styles and markup are not desired, you may wrap the Avatar(s) or Icon(s) in any
other element and provide your own layout.

## Related components

- [Chips](../?path=/docs/components-selections-chips--docs) is a convenience wrapper
that offers the single-select, multi-select, and add/dismiss functionality "out of the box"
- [Combobox](../?path=/docs/components-selections-combobox--docs) is most commonly
triggered by a Chip, but is a separate component
- [Select](../?path=/docs/components-selections-select--docs) is a simpler single-select
"dropdown" that presents as a form element and should be preferred in forms
- [Chips](../?path=/docs/components-selections-chips--docs) is a convenience
wrapper that offers the single-select, multi-select, and add/dismiss
functionality "out of the box"
- [Combobox](../?path=/docs/components-selections-combobox--docs) is most
commonly triggered by a Chip, but is a separate component
- [Select](../?path=/docs/components-selections-select--docs) is a simpler
single-select "dropdown" that presents as a form element and should be
preferred in forms
- [RadioGroup](../?path=/docs/components-forms-and-inputs-radiogroup--docs)
should be used to allow the user to select "one-of-many" items (single-select)
and the labels for the items are longer than 1 or 2 words.
- [Checkbox](../?path=/docs/components-selections-checkbox--docs) should be used
to allow the user to select "one-or-more-of-many" items (multi-select) and the
labels for the items are longer than 1 or 2 words.
- [InlineLabel](../?path=/docs/components-status-and-feedback-inlinelabel--docs)
should be used when you just need a rounded-rectangular element that displays
should be used when you just need a rounded-rectangular element that displays
metadata about an element

## Content guidelines

Chip headings and labels for single- or multi-select should be succinct - ideally 1–2 words.
If any of the options in the group may have longer labels, consider Checkbox or Radio as necessary
for your selection type.
Chip headings and labels for single- or multi-select should be succinct -
ideally 1–2 words. If any of the options in the group may have longer labels,
consider Checkbox or Radio as necessary for your selection type.

In cases where a Chip displays name of its selections, such as when used to trigger
a Combobox or a date range selector, use the heading to identify the "category" and
the label to identify the selected items.
In cases where a Chip displays name of its selections, such as when used to
trigger a Combobox or a date range selector, use the heading to identify the
"category" and the label to identify the selected items.

## Accessibility

Expand All @@ -202,7 +232,6 @@ dealing with a checkbox or radio button.
If Chips is set for add/dismiss selections, the dismiss button should notify the
user that they will "dismiss \{label name\}" upon press.


## Responsiveness

The Chips themselves will take up as much space as their container allows, and
Expand All @@ -212,9 +241,9 @@ out of view in a single row, depending on your use case.
Chip can truncate if its' container is limited in space, but does not inherently
cap its own width and will default to "hug" its contents.


## Notes

Chip is in the process of being applied to the more opinionated Chips convenience wrapper,
but by design does not carry the same level of "out of the box" functionality as it is a more
"atomic" element that can be used outside of those more complex selection flows.
Chip is in the process of being applied to the more opinionated Chips
convenience wrapper, but by design does not carry the same level of "out of the
box" functionality as it is a more "atomic" element that can be used outside of
those more complex selection flows.
Original file line number Diff line number Diff line change
@@ -1,32 +1,38 @@
import React from "react";
import { render } from "@testing-library/react";
import { render, screen } from "@testing-library/react";
import { Chip } from "../../Chip";
import { Avatar } from "../../../Avatar";
import { Icon } from "../../../Icon";

describe("Chip.Prefix Component", () => {
it("renders Prefix", () => {
const { getByText } = render(
render(
<Chip.Prefix>
<Avatar initials="DT" />
</Chip.Prefix>,
);

expect(getByText("DT")).toBeInTheDocument();
expect(screen.getByText("DT")).toBeInTheDocument();
});

it("should hide prefix when passed bad child", () => {
const { queryByText } = render(<Chip.Prefix>Hello!</Chip.Prefix>);
expect(queryByText("Hello!")).not.toBeInTheDocument();
it("should show all children when icon or avatar isn't provided", () => {
render(
<Chip.Prefix>
<p>First child</p>
<p>Second child</p>
</Chip.Prefix>,
);
expect(screen.getByText("First child")).toBeInTheDocument();
expect(screen.getByText("Second child")).toBeInTheDocument();
});

it("prefix renders only one valid child", () => {
const { container } = render(
render(
<Chip.Prefix>
<Icon name="cross" />
<Icon name="cross" />
</Chip.Prefix>,
);
expect(container.querySelectorAll("svg")).toHaveLength(1);
expect(screen.getAllByTestId("cross")).toHaveLength(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@ import { useChildComponent } from "../../hooks/index";
import styles from "../../Chip.css";

export function ChipPrefix({ children }: PropsWithChildren) {
const singleChild = useChildComponent(
const avatarOrIcon = useChildComponent(
children,
d => d.type === Avatar || d.type === Icon,
);

return (
<span className={classNames(styles.prefix, !singleChild && styles.empty)}>
{singleChild}
</span>
);
if (!avatarOrIcon) return <>{children}</>;

return <span className={classNames(styles.prefix)}>{avatarOrIcon}</span>;
}

0 comments on commit 1a6c72c

Please sign in to comment.