From cd85be144135e2586125a957714b01d71234ee41 Mon Sep 17 00:00:00 2001 From: Robert Niznik Date: Thu, 5 Dec 2024 13:30:02 -0500 Subject: [PATCH] feat(components): add `ToggleButtonGroup` (#1496) --- .changeset/eleven-toys-run.md | 5 ++ .../__tests__/ToggleButtonGroup.spec.tsx | 17 +++++ packages/components/src/ToggleButtonGroup.tsx | 35 +++++++++ packages/components/src/index.ts | 2 + .../src/styles/ToggleButtonGroup.module.css | 9 +++ .../stories/ToggleButtonGroup.stories.tsx | 72 +++++++++++++++++++ 6 files changed, 140 insertions(+) create mode 100644 .changeset/eleven-toys-run.md create mode 100644 packages/components/__tests__/ToggleButtonGroup.spec.tsx create mode 100644 packages/components/src/ToggleButtonGroup.tsx create mode 100644 packages/components/src/styles/ToggleButtonGroup.module.css create mode 100644 packages/components/stories/ToggleButtonGroup.stories.tsx diff --git a/.changeset/eleven-toys-run.md b/.changeset/eleven-toys-run.md new file mode 100644 index 000000000..83f5dae42 --- /dev/null +++ b/.changeset/eleven-toys-run.md @@ -0,0 +1,5 @@ +--- +"@launchpad-ui/components": patch +--- + +Add `ToggleButtonGroup` diff --git a/packages/components/__tests__/ToggleButtonGroup.spec.tsx b/packages/components/__tests__/ToggleButtonGroup.spec.tsx new file mode 100644 index 000000000..769229bac --- /dev/null +++ b/packages/components/__tests__/ToggleButtonGroup.spec.tsx @@ -0,0 +1,17 @@ +import { describe, expect, it } from 'vitest'; + +import { render, screen } from '../../../test/utils'; +import { ToggleButton, ToggleButtonGroup } from '../src'; + +describe('ToggleButtonGroup', () => { + it('renders', () => { + render( + + First + Second + Third + , + ); + expect(screen.getByRole('radiogroup')).toBeVisible(); + }); +}); diff --git a/packages/components/src/ToggleButtonGroup.tsx b/packages/components/src/ToggleButtonGroup.tsx new file mode 100644 index 000000000..27b5906d8 --- /dev/null +++ b/packages/components/src/ToggleButtonGroup.tsx @@ -0,0 +1,35 @@ +import type { ForwardedRef } from 'react'; +import type { ToggleButtonGroupProps } from 'react-aria-components'; + +import { cva } from 'class-variance-authority'; +import { forwardRef } from 'react'; +import { + ToggleButtonGroup as AriaToggleButtonGroup, + composeRenderProps, +} from 'react-aria-components'; + +import styles from './styles/ToggleButtonGroup.module.css'; + +const group = cva(styles.group); + +const _ToggleButtonGroup = (props: ToggleButtonGroupProps, ref: ForwardedRef) => { + return ( + + group({ ...renderProps, className }), + )} + /> + ); +}; + +/** + * A toggle button group allows a user to toggle multiple options, with single or multiple selection. + * + * https://react-spectrum.adobe.com/react-aria/ToggleButtonGroup.html + */ +const ToggleButtonGroup = forwardRef(_ToggleButtonGroup); + +export { ToggleButtonGroup }; +export type { ToggleButtonGroupProps }; diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index 18f2d89fe..5ee118806 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -72,6 +72,7 @@ export type { TextAreaProps } from './TextArea'; export type { TextFieldProps } from './TextField'; export type { SnackbarValue, ToastOptions, ToastValue } from './Toast'; export type { ToggleButtonProps } from './ToggleButton'; +export type { ToggleButtonGroupProps } from './ToggleButtonGroup'; export type { ToggleIconButtonProps } from './ToggleIconButton'; export type { ToolbarProps } from './Toolbar'; export type { TooltipProps, TooltipTriggerProps } from './Tooltip'; @@ -152,6 +153,7 @@ export { TextArea } from './TextArea'; export { TextField } from './TextField'; export { SnackbarContainer, SnackbarQueue, ToastContainer, ToastQueue } from './Toast'; export { ToggleButton } from './ToggleButton'; +export { ToggleButtonGroup } from './ToggleButtonGroup'; export { ToggleIconButton } from './ToggleIconButton'; export { Toolbar } from './Toolbar'; export { Tooltip, TooltipTrigger } from './Tooltip'; diff --git a/packages/components/src/styles/ToggleButtonGroup.module.css b/packages/components/src/styles/ToggleButtonGroup.module.css new file mode 100644 index 000000000..13d71a376 --- /dev/null +++ b/packages/components/src/styles/ToggleButtonGroup.module.css @@ -0,0 +1,9 @@ +.group { + composes: buttonGroup from './base.module.css'; + display: flex; + + &[data-orientation='vertical'] { + flex-direction: column; + width: fit-content; + } +} diff --git a/packages/components/stories/ToggleButtonGroup.stories.tsx b/packages/components/stories/ToggleButtonGroup.stories.tsx new file mode 100644 index 000000000..d815e1f30 --- /dev/null +++ b/packages/components/stories/ToggleButtonGroup.stories.tsx @@ -0,0 +1,72 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { ToggleButton, ToggleButtonGroup, ToggleIconButton } from '../src'; + +const meta: Meta = { + component: ToggleButtonGroup, + // @ts-ignore + subcomponents: { ToggleButton }, + title: 'Components/Buttons/ToggleButtonGroup', + parameters: { + status: { + type: import.meta.env.STORYBOOK_PACKAGE_STATUS__COMPONENTS, + }, + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Example: Story = { + args: { + children: ( + <> + First + Second + Third + + ), + defaultSelectedKeys: ['first'], + }, +}; + +export const MultipleSelection: Story = { + args: { + children: ( + <> + First + Second + Third + + ), + selectionMode: 'multiple', + defaultSelectedKeys: ['first', 'second'], + }, +}; + +export const Orientation: Story = { + args: { + children: ( + <> + First + Second + Third + + ), + orientation: 'vertical', + }, +}; + +export const Icons: Story = { + args: { + children: ( + <> + + + + + ), + orientation: 'vertical', + }, +};