Skip to content

Commit

Permalink
feat(Tooltip): add max-inline-size
Browse files Browse the repository at this point in the history
  • Loading branch information
filiptammergard committed Apr 3, 2023
1 parent 9f6b104 commit 2d1e04f
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 14 deletions.
5 changes: 5 additions & 0 deletions .changeset/moody-taxis-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@einride/ui": minor
---

Add maxInlineSize to <Tooltip> to make multiline possible with logical properties.
65 changes: 65 additions & 0 deletions packages/einride-ui/src/components/content/Tooltip/Tooltip.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
Meta,
Primary,
Description,
Source,
Title,
ArgTypes,
Subtitle,
Controls,
Story,
Canvas,
Stories,
Markdown,
} from "@storybook/blocks"
import * as TooltipStories from "./Tooltip.stories.tsx"

<Meta of={TooltipStories} />

<Title />

<Description of={TooltipStories} />

```tsx
import { Tooltip } from "@einride/ui"
```

## Basic

<Description of={TooltipStories.Basic} />

<Canvas of={TooltipStories.Basic} />

<Controls of={TooltipStories.Basic} />

## Button with tooltip

<Description of={TooltipStories.ButtonTrigger} />

<Canvas of={TooltipStories.ButtonTrigger} />

<Controls of={TooltipStories.ButtonTrigger} />

## Open delay

<Description of={TooltipStories.OpenDelay} />

<Canvas of={TooltipStories.OpenDelay} />

<Controls of={TooltipStories.OpenDelay} />

## Multiline

<Description of={TooltipStories.Multiline} />

<Canvas of={TooltipStories.Multiline} />

<Controls of={TooltipStories.Multiline} />

## Tooltip alignment

<Description of={TooltipStories.Align} />

<Canvas of={TooltipStories.Align} />

<Controls of={TooltipStories.Align} />
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import styled from "@emotion/styled"
import { expect } from "@storybook/jest"
import { Meta, StoryObj } from "@storybook/react"
import { userEvent, waitFor, within } from "@storybook/testing-library"
import { spacings } from "../../../lib/theme/types"
import { PrimaryButton } from "../../controls/buttons/PrimaryButton/PrimaryButton"
import { Table as TableComponent } from "../../table/Table/Table"
import { Tbody } from "../../table/Tbody/Tbody"
Expand All @@ -12,34 +13,59 @@ import { Tr } from "../../table/Tr/Tr"
import { Text } from "../../typography/Text/Text"
import { Tooltip } from "./Tooltip"

/** Use tooltips to show additional information. */
const meta = {
component: Tooltip,
argTypes: {
maxInlineSize: {
control: {
type: "select",
},
options: spacings,
},
maxWidth: {
control: {
type: "select",
},
options: spacings,
},
width: {
control: {
type: "select",
},
options: spacings,
},
},
} satisfies Meta<typeof Tooltip>

export default meta
type Story = StoryObj<typeof meta>

export const Basic = {
args: {
children: "Tooltip trigger",
children: "Hover or focus me",
content: "Tooltip content",
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
const tooltipTrigger = canvas.getByRole("button", { name: "Tooltip trigger" })
const tooltipTrigger = canvas.getByRole("button", { name: Basic.args.children })
await expect(tooltipTrigger).toHaveAttribute("data-state", "closed")
},
} satisfies Story

/** If you want to use a custom element as the tooltip trigger, for example a button, you can use `triggerAsChild`. Notice that if you don't enable `triggerAsChild`, the button will have two tab stops and some attributes will land on the wrong element. */
export const ButtonTrigger = {
args: {
children: <PrimaryButton>Hover me</PrimaryButton>,
children: <PrimaryButton>Hover or focus me</PrimaryButton>,
content: "Here's some more context on what the button does",
triggerAsChild: true,
},
parameters: {
controls: { include: "triggerAsChild" },
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
const tooltipTrigger = canvas.getByRole("button", { name: "Hover me" })
const tooltipTrigger = canvas.getByRole("button", { name: /hover or focus me/i })
await expect(tooltipTrigger).toHaveAttribute("data-state", "closed")
},
} satisfies Story
Expand Down Expand Up @@ -104,31 +130,56 @@ export const Table = {
},
} satisfies StoryObj

/** In some situations, for example when many tooltips are used in the same view, you might want to show the tooltip with a delay to avoid clutter. You can do that with `openDelayDuraton`. */
export const OpenDelay = {
args: {
children: "Text with tooltip that opens with a delay",
children: "Hover or focus me and wait",
content: "Here's the tooltip!",
openDelayDuration: 700,
},
parameters: {
controls: { include: "openDelayDuration" },
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
const tooltipTrigger = canvas.getByRole("button", {
name: "Text with tooltip that opens with a delay",
name: OpenDelay.args.children,
})
await expect(tooltipTrigger).toHaveAttribute("data-state", "closed")
},
} satisfies Story

/** If you need to render a long tooltip, you can limit the size with `maxInlineSize` to make it multiline. */
export const Multiline = {
args: {
children: "Tooltip trigger",
content:
"Some really really really really really really really really really really really really really really really really really really really really really really really long tooltip content.",
width: 50,
maxInlineSize: 50,
},
parameters: {
controls: { include: "maxInlineSize" },
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
const tooltipTrigger = canvas.getByRole("button", { name: Multiline.args.children })
await expect(tooltipTrigger).toHaveAttribute("data-state", "closed")
},
} satisfies Story

/** You can change the tooltip alignment with `align`. */
export const Align = {
args: {
children: "Hover or focus me and notice that where the tooltip shows up",
content: "Tooltip content",
align: "start",
},
parameters: {
controls: { include: "align" },
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
const tooltipTrigger = canvas.getByRole("button", { name: "Tooltip trigger" })
const tooltipTrigger = canvas.getByRole("button", { name: Align.args.children })
await expect(tooltipTrigger).toHaveAttribute("data-state", "closed")
},
} satisfies Story
Expand All @@ -139,13 +190,13 @@ export const Pointer = {
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
const tooltipTrigger = canvas.getByRole("button", { name: "Tooltip trigger" })
const tooltipTrigger = canvas.getByRole("button", { name: Pointer.args.children })
await expect(tooltipTrigger).toHaveAttribute("data-state", "closed")
await expect(tooltipTrigger).toHaveAccessibleDescription("")
await userEvent.hover(tooltipTrigger)
waitFor(() => {
expect(tooltipTrigger).toHaveAttribute("data-state", "delayed-open")
expect(tooltipTrigger).toHaveAccessibleDescription("Tooltip content")
expect(tooltipTrigger).toHaveAccessibleDescription(Pointer.args.content)
})
},
} satisfies Story
Expand All @@ -156,13 +207,13 @@ export const Keyboard = {
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
const tooltipTrigger = canvas.getByRole("button", { name: "Tooltip trigger" })
const tooltipTrigger = canvas.getByRole("button", { name: Keyboard.args.children })
await expect(tooltipTrigger).toHaveAttribute("data-state", "closed")
await expect(tooltipTrigger).toHaveAccessibleDescription("")
await userEvent.tab()
waitFor(() => {
expect(tooltipTrigger).toHaveAttribute("data-state", "instant-open")
expect(tooltipTrigger).toHaveAccessibleDescription("Tooltip content")
expect(tooltipTrigger).toHaveAccessibleDescription(Keyboard.args.content)
})
await userEvent.tab()
await expect(tooltipTrigger).toHaveAttribute("data-state", "closed")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import styled from "@emotion/styled"
import * as RadixTooltip from "@radix-ui/react-tooltip"
import { ReactNode } from "react"
import { useTheme } from "../../../hooks/useTheme"
import { MaxWidth, Width } from "../../../lib/theme/props"
import { MaxInlineSize, MaxWidth, Width } from "../../../lib/theme/props"
import { zIndex } from "../../../lib/zIndex"
import { Box } from "../../layout/Box/Box"

export interface TooltipProps {
/* The preferred alignment against the trigger. May change when collisions occur. Default is `center`. */
/** The preferred alignment against the trigger. May change when collisions occur. Default is `center`. */
align?: "start" | "center" | "end"

/** Tooltip components. */
Expand All @@ -23,6 +23,9 @@ export interface TooltipProps {
/** Determines whether or not to show a dashed underline on children as a tooltip hint. */
hint?: boolean

/** Max inline size of the tooltip. */
maxInlineSize?: MaxInlineSize

/** Max width of the tooltip. */
maxWidth?: MaxWidth

Expand Down

0 comments on commit 2d1e04f

Please sign in to comment.