Skip to content

feat!: refactor Tooltip to use radix-ui #1722

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/fair-swans-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@strapi/design-system': major
---

chore!: streamline IconButton API

- remove `icon` prop
- remove `ariaLabel` prop
- add `withTooltip` prop (default false)

`children` & `label` are now required props.
7 changes: 7 additions & 0 deletions .changeset/wild-colts-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@strapi/design-system': major
---

feat!: refactor Tooltip to use radix-ui

The Tooltip API has changed significently whilst retaining it's functionality, we recommend your review the documentation to understand the changes and how to migrate your code.
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ of our APIs will change, but functionality should still remain the same. We reco
for any of these components to understand better how to migrate your code.

- `Accordion`
- `Tooltip`

### Removed `ThemeProvider`

Expand All @@ -213,6 +214,17 @@ for any of these components to understand better how to migrate your code.
+ import { DesignSystemProvider } from '@strapi/design-system';
```

### IconButton API changes

The `IconButton` API has been streamlined, namely `icon` and `ariaLabel` have been removed. Users should instead use
`label` and `children`, by default we always show a tooltip to preserve old functionality but this will deprecated in a
future release. If you don't want a tooltip you should set `withTooltip` to `false`:

```diff
- <IconButton icon={Plus} ariaLabel="Add" />
+ <IconButton withTooltip={false} label="Add"><Plus /></IconButton>
```

---

## Icons
Expand Down
4 changes: 2 additions & 2 deletions docs/stories/04-components/Accordion.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -307,10 +307,10 @@ export const WithActions = {
{title}
</Accordion.Trigger>
<Accordion.Actions>
<IconButton aria-label="Duplicate record" onClick={duplicateOnClick}>
<IconButton withTooltip={false} label="Duplicate record" onClick={duplicateOnClick}>
<Duplicate />
</IconButton>
<IconButton aria-label="Delete record" onClick={deleteOnClick}>
<IconButton withTooltip={false} label="Delete record" onClick={deleteOnClick}>
<Trash />
</IconButton>
</Accordion.Actions>
Expand Down
43 changes: 43 additions & 0 deletions docs/stories/04-components/Tooltip.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Meta, Canvas, ArgTypes, Controls } from '@storybook/blocks';
import { Tooltip } from '@strapi/design-system';

import * as TooltipStories from './Tooltip.stories';

<Meta of={TooltipStories} />

# Tooltip

- [Overview](#overview)
- [Usage](#usage)
- [Props](#props)
- [Positioning](#positioning)

## Overview

Tooltips are a specific type of popover that displays information related to an element when said element recieves focus
or the hover.

<ViewSource path="components/Tooltip" />

## Usage

```js
import { Tooltip } from '@strapi/design-system';
```

They shouldn't be used to display critical or urgent information, but rather to provide additional context or
information. Information should be short & concise.

<Canvas of={TooltipStories.Base} />

## Props

<ArgTypes of={Tooltip} />

## Positioning

Use a combination of `align` and `side` to dictate the position of a tooltip, the tooltip will still move itself if it's
going to colide with the viewport. The default position is `top` and `center`.

<Canvas of={TooltipStories.Positioned} />
<Controls of={TooltipStories.Positioned} />
77 changes: 77 additions & 0 deletions docs/stories/04-components/Tooltip.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Meta, StoryObj } from '@storybook/react';
import { Tooltip, IconButton } from '@strapi/design-system';
import { Trash } from '@strapi/icons';
import { outdent } from 'outdent';

const meta: Meta<typeof Tooltip> = {
title: 'Components/Tooltip',
component: Tooltip,
args: {
label: 'Delete all items',
},
render: (args) => {
return (
<Tooltip {...args}>
<IconButton withTooltip={false} label="delete">
<Trash />
</IconButton>
</Tooltip>
);
},
parameters: {
docs: {
source: {
code: outdent`
<Tooltip label="Delete all items">
<button aria-label="delete">
<Trash aria-hidden focusable={false} />
</button>
</Tooltip>
`,
},
},
/* this will never show the component without interaction, so we never want it snapshot. */
chromatic: { disableSnapshot: true },
},
};

export default meta;

type Story = StoryObj<typeof Tooltip>;

export const Base = {
name: 'base',
} satisfies Story;

export const Positioned = {
name: 'positioned',
argTypes: {
align: {
control: 'select',
options: ['start', 'center', 'end'],
},
side: {
control: 'select',
options: ['left', 'right', 'top', 'bottom'],
},
},
args: {
align: 'center',
open: true,
side: 'right',
},
parameters: {
docs: {
source: {
code: outdent`
<Tooltip label="Delete all items" align="center" side="right">
<button aria-label="delete">
<Trash aria-hidden focusable={false} />
</button>
</Tooltip>
`,
},
},
chromatic: { disableSnapshot: false },
},
} satisfies Story;
4 changes: 3 additions & 1 deletion docs/stories/Card.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ export const Base = {
<CardHeader>
<CardCheckbox value />
<CardAction position="end">
<IconButton label="Edit the thing" icon={<Pencil />} />
<IconButton label="Edit the thing">
<Pencil />
</IconButton>
</CardAction>
<CardAsset src={'/stories/carousel/first.jpg'} />
<CardTimer>05:39</CardTimer>
Expand Down
48 changes: 36 additions & 12 deletions docs/stories/Carousel.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,18 @@ export const Base = {
hint="Description line"
actions={
<CarouselActions>
<IconButton onClick={() => console.log('edit')} label="Edit" id="edit" icon={<Pencil />} />
<IconButton onClick={() => console.log('Create')} label="Create" icon={<Plus />} />
<IconButton onClick={() => console.log('Delete')} label="Delete" icon={<Trash />} />
<IconButton onClick={() => console.log('Publish')} label="Publish" icon={<Play />} />
<IconButton onClick={() => console.log('edit')} label="Edit" id="edit">
<Pencil />
</IconButton>
<IconButton onClick={() => console.log('Create')} label="Create">
<Plus />
</IconButton>
<IconButton onClick={() => console.log('Delete')} label="Delete">
<Trash />
</IconButton>
<IconButton onClick={() => console.log('Publish')} label="Publish">
<Play />
</IconButton>
</CarouselActions>
}
style={{
Expand Down Expand Up @@ -74,10 +82,18 @@ export const OneSlideOnly = {
hint="Description line"
actions={
<CarouselActions>
<IconButton onClick={() => console.log('edit')} label="Edit" id="edit" icon={<Pencil />} />
<IconButton onClick={() => console.log('Create')} label="Create" icon={<Plus />} />
<IconButton onClick={() => console.log('Delete')} label="Delete" icon={<Trash />} />
<IconButton onClick={() => console.log('Publish')} label="Publish" icon={<Play />} />
<IconButton onClick={() => console.log('edit')} label="Edit" id="edit">
<Pencil />
</IconButton>
<IconButton onClick={() => console.log('Create')} label="Create">
<Plus />
</IconButton>
<IconButton onClick={() => console.log('Delete')} label="Delete">
<Trash />
</IconButton>
<IconButton onClick={() => console.log('Publish')} label="Publish">
<Play />
</IconButton>
</CarouselActions>
}
style={{
Expand Down Expand Up @@ -105,10 +121,18 @@ export const BrokenAsset = {
hint="Description line"
actions={
<CarouselActions>
<IconButton onClick={() => console.log('edit')} label="Edit" id="edit" icon={<Pencil />} />
<IconButton onClick={() => console.log('Create')} label="Create" icon={<Plus />} />
<IconButton onClick={() => console.log('Delete')} label="Delete" icon={<Trash />} />
<IconButton onClick={() => console.log('Publish')} label="Publish" icon={<Play />} />
<IconButton onClick={() => console.log('edit')} label="Edit" id="edit">
<Pencil />
</IconButton>
<IconButton onClick={() => console.log('Create')} label="Create">
<Plus />
</IconButton>
<IconButton onClick={() => console.log('Delete')} label="Delete">
<Trash />
</IconButton>
<IconButton onClick={() => console.log('Publish')} label="Publish">
<Play />
</IconButton>
</CarouselActions>
}
style={{
Expand Down
76 changes: 55 additions & 21 deletions docs/stories/IconButton.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,18 @@ export const Base = {
<Typography>{currentAction}</Typography>
</Box>
<Flex background="neutral100" gap={1} padding={2}>
<IconButton onClick={() => setCurrentAction('edit')} label="Edit" icon={<Pencil />} />
<IconButton onClick={() => setCurrentAction('Create')} label="Create" icon={<Plus />} />
<IconButton onClick={() => setCurrentAction('Delete')} label="Delete" icon={<Trash />} />
<IconButton onClick={() => setCurrentAction('Publish')} label="Publish" icon={<Play />} />
<IconButton onClick={() => setCurrentAction('edit')} label="Edit">
<Pencil />
</IconButton>
<IconButton onClick={() => setCurrentAction('Create')} label="Create">
<Plus />
</IconButton>
<IconButton onClick={() => setCurrentAction('Delete')} label="Delete">
<Trash />
</IconButton>
<IconButton onClick={() => setCurrentAction('Publish')} label="Publish">
<Play />
</IconButton>
</Flex>
</Box>
);
Expand All @@ -45,10 +53,18 @@ export const Disabled = {
<Typography>{currentAction}</Typography>
</Box>
<Flex background="neutral100" gap={1} padding={2}>
<IconButton disabled onClick={() => setCurrentAction('edit')} label="Edit" icon={<Pencil />} />
<IconButton disabled onClick={() => setCurrentAction('Create')} label="Create" icon={<Plus />} />
<IconButton disabled onClick={() => setCurrentAction('Delete')} label="Delete" icon={<Trash />} />
<IconButton disabled onClick={() => setCurrentAction('Publish')} label="Publish" icon={<Play />} />
<IconButton disabled onClick={() => setCurrentAction('edit')} label="Edit">
<Pencil />
</IconButton>
<IconButton disabled onClick={() => setCurrentAction('Create')} label="Create">
<Plus />
</IconButton>
<IconButton disabled onClick={() => setCurrentAction('Delete')} label="Delete">
<Trash />
</IconButton>
<IconButton disabled onClick={() => setCurrentAction('Publish')} label="Publish">
<Play />
</IconButton>
</Flex>
</Box>
);
Expand All @@ -61,7 +77,9 @@ export const WithoutTooltip = {
render: () => (
<Box padding={7}>
<Flex background="neutral100" gap={1} padding={2}>
<IconButton onClick={() => console.log('edit')} aria-label="Edit" icon={<Pencil />} />
<IconButton onClick={() => console.log('edit')} withTooltip={false} label="Edit">
<Pencil />
</IconButton>
</Flex>
</Box>
),
Expand All @@ -74,10 +92,18 @@ export const Group = {
<Box padding={7}>
<Flex background="neutral100" gap={1} padding={2}>
<IconButtonGroup>
<IconButton onClick={() => console.log('edit')} label="Edit" icon={<Pencil />} />
<IconButton onClick={() => console.log('Create')} label="Create" icon={<Plus />} />
<IconButton onClick={() => console.log('Delete')} label="Delete" icon={<Trash />} />
<IconButton onClick={() => console.log('Publish')} label="Publish" icon={<Play />} />
<IconButton onClick={() => console.log('edit')} label="Edit">
<Pencil />
</IconButton>
<IconButton onClick={() => console.log('Create')} label="Create">
<Plus />
</IconButton>
<IconButton onClick={() => console.log('Delete')} label="Delete">
<Trash />
</IconButton>
<IconButton onClick={() => console.log('Publish')} label="Publish">
<Play />
</IconButton>
</IconButtonGroup>
</Flex>
</Box>
Expand All @@ -89,9 +115,15 @@ export const Group = {
export const Sizes = {
render: () => (
<Flex gap={1}>
<IconButton label="Small" icon={<Pencil />} size="S" />
<IconButton label="Medium" icon={<Pencil />} size="M" />
<IconButton label="Large" icon={<Pencil />} size="L" />
<IconButton label="Small" size="S">
<Pencil />
</IconButton>
<IconButton label="Medium" size="M">
<Pencil />
</IconButton>
<IconButton label="Large" size="L">
<Pencil />
</IconButton>
</Flex>
),

Expand All @@ -102,7 +134,9 @@ export const Variants = {
render: () => (
<Flex gap={2}>
{(['tertiary', 'secondary'] as const).map((variant) => (
<IconButton icon={<Pencil />} variant={variant} key={variant} label={variant} />
<IconButton variant={variant} key={variant} label={variant}>
<Pencil />
</IconButton>
))}
</Flex>
),
Expand All @@ -120,16 +154,16 @@ export const Children = {
<Typography>{currentAction}</Typography>
</Box>
<Flex background="neutral100" gap={1} padding={2}>
<IconButton onClick={() => setCurrentAction('Edit')} aria-label="Edit">
<IconButton onClick={() => setCurrentAction('Edit')} withTooltip={false} label="Edit">
<Pencil />
</IconButton>
<IconButton onClick={() => setCurrentAction('Create')} aria-label="Create">
<IconButton onClick={() => setCurrentAction('Create')} withTooltip={false} label="Create">
<Plus />
</IconButton>
<IconButton onClick={() => setCurrentAction('Delete')} aria-label="Delete">
<IconButton onClick={() => setCurrentAction('Delete')} withTooltip={false} label="Delete">
<Trash />
</IconButton>
<IconButton onClick={() => setCurrentAction('Publish')} aria-label="Publish">
<IconButton onClick={() => setCurrentAction('Publish')} withTooltip={false} label="Publish">
<Play />
</IconButton>
</Flex>
Expand Down
Loading