dequelabs / cauldron

https://cauldron.dequelabs.com/
Mozilla Public License 2.0
86 stars 20 forks source link

Add "Copy" button to cauldron #970

Closed scurker closed 2 weeks ago

scurker commented 1 year ago

Copy button is a component that accepts an optional Cauldron Button variant (defaulting to tertiary) to render an in interactive element to copy a specified string of text.

Interface

interface CopyButtonProps extends React.ComponentProps<typeof Button> {
  value: string
  children?: ContentNode
  notificationLabel?: ContentNode
  hideVisibleLabel?: boolean
  tooltipPlacement?: React.ComponentProps<typeof Tooltip>['placement']
  variant?: Extract<typeof Button, 'primary' | 'secondary' | 'tertiary'>
  onCopy?: (text: string) => void
}

Implementation

By default, the Button will have a visible label that will be displayed with children. When hideVisibleLabel is set, children should be wrapped in an Offscreen component with a tooltip displayed instead.

When the button is clicked, the text should be copied to the clipboard with a visible notification and offscreen aria-live notification using the notificationLabel text.

The existing copy icon should be updated to match the one from the design.

Notification

When copied, a temporary notification should be visible to indicate a successful copy. This includes the following sequence:

After 2s, all of the original content can revert back to its original state.

[!NOTE] The tooltip that appears for when the label is visible or hidden via hideVisibleLabel should always have an aria-association of none. The button should always have an explicit accessible name that doesn't change, so it's unnecessary to associate the accessible name or accessible description with the tooltip.

Usage

This component is essentially a thin polymorphic wrapper around <Button> to add additional copy functionality.

<CopyButton
  value="Text to Copy"
  onCopy={...}
>
  Copy to Clipboard
</CopyButton>
thuey commented 1 year ago

Rather than using a polymorphic component (e.g. as), I think passing a child would be simpler, similar to an internal component we have in another project I could show you. Thoughts?

scurker commented 1 year ago

@thuey I would like to see it. I was primarily thinking of a polymorphic component for 2 reasons:

I think there's pros/cons to both methods so I guess it would be good to weigh them and see where we land.

scurker commented 1 month ago

For an example of copy functionality, look at the existing CopyToClipboardButton component for Cauldron docs: https://github.com/dequelabs/cauldron/blob/develop/docs/components/CopyToClipboardButton.tsx