diff --git a/src/components/ControlledCollapsible/ControlledCollapsible.stories.tsx b/src/components/ControlledCollapsible/ControlledCollapsible.stories.tsx new file mode 100644 index 000000000..099d05c0d --- /dev/null +++ b/src/components/ControlledCollapsible/ControlledCollapsible.stories.tsx @@ -0,0 +1,39 @@ +import { Meta, StoryObj } from '@storybook/react' +import { useEffect, useState } from 'react' + +import { Text } from '~/components/Text' + +import { ControlledCollapsible } from './ControlledCollapsible' + +export default { + title: 'Components/Collapsible', + component: ControlledCollapsible, +} as Meta + +type Story = StoryObj + +const CollapsibleStory = () => { + const [isOpen, setIsOpen] = useState(false) + + useEffect(() => { + setIsOpen(true) + }, []) + + return ( + setIsOpen(open)} + > + {[1, 2, 3, 4, 5].map(x => ( + + Item {x} + + ))} + + ) +} + +export const Default: Story = { + render: () => , +} diff --git a/src/components/ControlledCollapsible/ControlledCollapsible.test.tsx b/src/components/ControlledCollapsible/ControlledCollapsible.test.tsx new file mode 100644 index 000000000..2d9958710 --- /dev/null +++ b/src/components/ControlledCollapsible/ControlledCollapsible.test.tsx @@ -0,0 +1,41 @@ +import { cleanup, render, screen, fireEvent } from '@testing-library/react' +import { useState } from 'react' + +import { ControlledCollapsible } from './ControlledCollapsible' + +const TestComponent = () => { + const [isOpen, setIsOpen] = useState(false) + + return ( + setIsOpen(open)} + label="Hello" + > + World + + ) +} + +describe('', () => { + afterEach(cleanup) + + it('renders', () => { + render() + expect(screen.getByText(/Hello/)).toBeInTheDocument() + expect(screen.queryByText(/World/)).toBeNull() + + fireEvent.click(screen.getByRole('button')) + + expect(screen.getByText(/World/)).toBeInTheDocument() + }) + + it('with default open', () => { + render( + + World + + ) + expect(screen.getByText(/World/)).toBeInTheDocument() + }) +}) diff --git a/src/components/ControlledCollapsible/ControlledCollapsible.tsx b/src/components/ControlledCollapsible/ControlledCollapsible.tsx new file mode 100644 index 000000000..cc58694e8 --- /dev/null +++ b/src/components/ControlledCollapsible/ControlledCollapsible.tsx @@ -0,0 +1,73 @@ +import * as CollapsiblePrimitive from '@radix-ui/react-collapsible' +import { clsx } from 'clsx' +import { AnimatePresence, motion } from 'framer-motion' +import { ReactNode } from 'react' + +import { Text } from '~/components/Text' +import { ChevronDownIcon } from '~/icons' + +import { Box, BoxProps } from '../Box' +import * as styles from '../Collapsible/styles.css' + +type ControlledCollapsibleProps = BoxProps & + Omit & { + open: boolean + label: ReactNode + } + +export const ControlledCollapsible = (props: ControlledCollapsibleProps) => { + const { className, children, open, onOpenChange, label, ...rest } = props + + return ( + + + + + {label} + + + + + + + {open && ( + + + {children} + + + )} + + + + ) +} diff --git a/src/components/ControlledCollapsible/index.ts b/src/components/ControlledCollapsible/index.ts new file mode 100644 index 000000000..9f74f69dd --- /dev/null +++ b/src/components/ControlledCollapsible/index.ts @@ -0,0 +1 @@ +export { ControlledCollapsible } from './ControlledCollapsible'