-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Break component down to trigger and content
- Loading branch information
1 parent
a7fdd9f
commit d706154
Showing
5 changed files
with
139 additions
and
65 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,50 +1,60 @@ | ||
import { Meta, StoryObj } from '@storybook/react' | ||
import React, { useState } from 'react' | ||
|
||
import { ExpandableSection } from './ExpandableSection' | ||
|
||
type ExpandableSectionStory = { | ||
title: string | ||
children: React.ReactNode | ||
} | ||
|
||
const meta: Meta<ExpandableSectionStory> = { | ||
const meta: Meta<typeof ExpandableSection> = { | ||
title: 'Components/ExpandableSection', | ||
component: ExpandableSection, | ||
argTypes: { | ||
title: { | ||
description: 'Title of the expandable section', | ||
control: 'text', | ||
}, | ||
children: { | ||
description: 'Content of the expandable section', | ||
control: 'text', | ||
}, | ||
}, | ||
} | ||
export default meta | ||
|
||
type Story = StoryObj<ExpandableSectionStory> | ||
type Story = StoryObj<typeof ExpandableSection> | ||
|
||
const DefaultStory: React.FC = () => { | ||
const [isExpanded, setIsExpanded] = useState(false) | ||
return ( | ||
<ExpandableSection> | ||
<ExpandableSection.Trigger | ||
isExpanded={isExpanded} | ||
onClick={() => setIsExpanded(!isExpanded)} | ||
> | ||
Expandable Section | ||
</ExpandableSection.Trigger> | ||
<ExpandableSection.Content> | ||
This is the content of the expandable section. | ||
</ExpandableSection.Content> | ||
</ExpandableSection> | ||
) | ||
} | ||
|
||
const WithHtmlContentStory: React.FC = () => { | ||
const [isExpanded, setIsExpanded] = useState(false) | ||
return ( | ||
<ExpandableSection> | ||
<ExpandableSection.Trigger | ||
isExpanded={isExpanded} | ||
onClick={() => setIsExpanded(!isExpanded)} | ||
> | ||
Expandable Section with HTML | ||
</ExpandableSection.Trigger> | ||
<ExpandableSection.Content> | ||
<div> | ||
<p>This is the content of the expandable section.</p> | ||
<p> | ||
It can contain HTML elements like <strong>bold text</strong> and{' '} | ||
<em>italic text</em>. | ||
</p> | ||
</div> | ||
</ExpandableSection.Content> | ||
</ExpandableSection> | ||
) | ||
} | ||
|
||
export const Default: Story = { | ||
args: { | ||
title: 'Expandable Section', | ||
children: 'This is the content of the expandable section.', | ||
}, | ||
render: (args) => <ExpandableSection {...args} />, | ||
render: () => <DefaultStory />, | ||
} | ||
|
||
export const WithHtmlContent: Story = { | ||
args: { | ||
title: 'Expandable Section with HTML', | ||
children: ( | ||
<div> | ||
<p>This is the content of the expandable section.</p> | ||
<p> | ||
It can contain HTML elements like <strong>bold text</strong> and{' '} | ||
<em>italic text</em>. | ||
</p> | ||
</div> | ||
), | ||
}, | ||
render: (args) => <ExpandableSection {...args} />, | ||
render: () => <WithHtmlContentStory />, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,67 @@ | ||
import * as Collapsible from '@radix-ui/react-collapsible' | ||
import React, { ReactNode } from 'react' | ||
import cs from 'classnames' | ||
import React, { forwardRef, ReactNode } from 'react' | ||
|
||
import Icon from 'ui/Icon' | ||
|
||
interface ExpandableSectionProps { | ||
title: string | ||
const ExpandableSectionRoot = forwardRef< | ||
React.ElementRef<typeof Collapsible.Root>, | ||
React.ComponentPropsWithoutRef<typeof Collapsible.Root> | ||
>(({ children, className, ...props }, ref) => ( | ||
<Collapsible.Root | ||
className={cs('my-2 border border-gray-200', className)} | ||
{...props} | ||
ref={ref} | ||
> | ||
{children} | ||
</Collapsible.Root> | ||
)) | ||
|
||
ExpandableSectionRoot.displayName = 'ExpandableSectionRoot' | ||
|
||
interface ExpandableSectionTriggerProps | ||
extends React.ComponentPropsWithoutRef<typeof Collapsible.Trigger> { | ||
isExpanded: boolean | ||
children: ReactNode | ||
} | ||
|
||
export const ExpandableSection: React.FC<ExpandableSectionProps> = ({ | ||
title, | ||
children, | ||
}) => { | ||
const [isExpanded, setIsExpanded] = React.useState(false) | ||
|
||
return ( | ||
<div className="my-2 border border-gray-200"> | ||
<Collapsible.Root open={isExpanded} onOpenChange={setIsExpanded}> | ||
<Collapsible.Trigger asChild> | ||
<button className="flex w-full items-center justify-between p-4 text-left font-semibold hover:bg-gray-100"> | ||
<span>{title}</span> | ||
<Icon name={isExpanded ? 'chevronUp' : 'chevronDown'} size="sm" /> | ||
</button> | ||
</Collapsible.Trigger> | ||
<Collapsible.Content className="border-t border-gray-200 p-4"> | ||
{children} | ||
</Collapsible.Content> | ||
</Collapsible.Root> | ||
</div> | ||
) | ||
} | ||
const ExpandableSectionTrigger = forwardRef< | ||
React.ElementRef<typeof Collapsible.Trigger>, | ||
ExpandableSectionTriggerProps | ||
>(({ isExpanded, className, children, ...props }, ref) => ( | ||
<Collapsible.Trigger asChild> | ||
<button | ||
className={cs( | ||
'flex w-full items-center justify-between p-4 text-left font-semibold hover:bg-gray-100', | ||
className | ||
)} | ||
{...props} | ||
ref={ref} | ||
> | ||
<span>{children}</span> | ||
<Icon name={isExpanded ? 'chevronUp' : 'chevronDown'} size="sm" /> | ||
</button> | ||
</Collapsible.Trigger> | ||
)) | ||
|
||
ExpandableSectionTrigger.displayName = 'ExpandableSectionTrigger' | ||
|
||
const ExpandableSectionContent = forwardRef< | ||
React.ElementRef<typeof Collapsible.Content>, | ||
React.ComponentPropsWithoutRef<typeof Collapsible.Content> | ||
>(({ children, className, ...props }, ref) => ( | ||
<Collapsible.Content | ||
className={cs('border-t border-gray-200 p-4', className)} | ||
{...props} | ||
ref={ref} | ||
> | ||
{children} | ||
</Collapsible.Content> | ||
)) | ||
|
||
ExpandableSectionContent.displayName = 'ExpandableSectionContent' | ||
|
||
export const ExpandableSection = Object.assign(ExpandableSectionRoot, { | ||
Trigger: ExpandableSectionTrigger, | ||
Content: ExpandableSectionContent, | ||
}) |