Skip to content

Commit 04baf65

Browse files
authored
Feat: Basic collapsible component added (#407)
1 parent bb41624 commit 04baf65

File tree

3 files changed

+134
-8
lines changed

3 files changed

+134
-8
lines changed

src/components/ui/Button/Button.tsx

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use client';
2-
import React from 'react';
2+
import React, {ButtonHTMLAttributes, DetailedHTMLProps, PropsWithChildren} from 'react';
33
import {customClassSwitcher} from '~/core';
44

55
import ButtonPrimitive from '~/core/primitives/Button';
@@ -9,17 +9,12 @@ const COMPONENT_NAME = 'Button';
99

1010

1111
export type ButtonProps = {
12-
children?: React.ReactNode;
13-
color?: string;
14-
type?: 'button' | 'submit' | 'reset';
15-
className?: string;
1612
customRootClass?: string;
1713
variant?: 'solid' | 'outline' | 'soft' | 'ghost';
1814
size?: 'small' | 'medium' | 'large' | 'x-large';
19-
props?: any
20-
}
15+
} & DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> & PropsWithChildren
2116

22-
const Button = ({children, type='button', customRootClass='', color = '', className='', variant='solid', size='', ...props}: ButtonProps) => {
17+
const Button = ({children, type='button', customRootClass='', className='', color, variant='solid', size='medium', ...props}: ButtonProps) => {
2318
const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME);
2419
// apply data attribute for accent color
2520
// apply attribute only if color is present
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import React, {useState} from 'react';
2+
import {Meta} from '@storybook/react';
3+
import Collapsible from './Collapsible';
4+
import SandboxEditor from '~/components/tools/SandboxEditor/SandboxEditor';
5+
import Button from '../Button/Button';
6+
7+
const placeholderText= ['“One of the penalties for refusing to participate in politics is that you end up being governed by your inferiors.“ – Plato',
8+
'“The superior man understands what is right; the inferior man understands what will sell.” – Confucius',
9+
'“There are no secrets on the internet.” – Paul Babicki'];
10+
11+
12+
const meta: Meta<typeof Collapsible> = {
13+
component: Collapsible,
14+
title: 'UI/Data Display/Collapsible',
15+
};
16+
17+
export default meta;
18+
19+
export const Default = () => {
20+
return (<section>
21+
<SandboxEditor className=''>
22+
<Collapsible>
23+
<div className='grid gap-4 border-2 border-zinc-200 p-2'>
24+
{placeholderText.map((text) =>
25+
<p key={text}> {text} </p>,
26+
)}
27+
</div>
28+
</Collapsible>
29+
</SandboxEditor>
30+
</section>
31+
);
32+
};
33+
34+
export const WithTitle= () => {
35+
return (<section>
36+
<SandboxEditor className=''>
37+
<Collapsible title='Hello World'>
38+
<div className='grid gap-4 border-2 border-zinc-200'>
39+
{placeholderText.map((text) =>
40+
<p key={text}> {text} </p>,
41+
)}
42+
</div>
43+
</Collapsible>
44+
</SandboxEditor>
45+
</section>
46+
);
47+
};
48+
49+
export const ExternalTrigger= () => {
50+
const [open, setOpen] =useState(true);
51+
52+
const toggleHidden=() => setOpen((p) => !p);
53+
54+
return (<section>
55+
<SandboxEditor className=''>
56+
<Collapsible
57+
title='Quotes'
58+
open={open}
59+
trigger={<Button onClick={toggleHidden}>{open?'OPEN':'CLOSE'}</Button>}
60+
>
61+
62+
<div className='grid gap-4 border-2 border-zinc-200'>
63+
{placeholderText.map((text) =>
64+
<p key={text}> {text} </p>,
65+
)}
66+
</div>
67+
68+
</Collapsible>
69+
</SandboxEditor>
70+
</section>
71+
);
72+
};
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import React, {PropsWithChildren, ReactNode, useState} from 'react';
2+
import ButtonPrimitive from '~/core/primitives/Button';
3+
4+
/*
5+
* CHECKLIST
6+
*
7+
* Add rtl and ltr support
8+
* Support animations
9+
* Support basic poitioning of button
10+
* Add title to collapsible
11+
*
12+
* */
13+
14+
export type CollapsibleProps = { open?: boolean, title?: string, trigger?: ReactNode} & PropsWithChildren;
15+
16+
const ExpandIcon = () => (
17+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="size-6">
18+
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15" />
19+
</svg>
20+
);
21+
22+
const CollapseIcon = () => (
23+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="size-6">
24+
<path strokeLinecap="round" strokeLinejoin="round" d="M9 9V4.5M9 9H4.5M9 9 3.75 3.75M9 15v4.5M9 15H4.5M9 15l-5.25 5.25M15 9h4.5M15 9V4.5M15 9l5.25-5.25M15 15h4.5M15 15v4.5m0-4.5 5.25 5.25" />
25+
</svg>
26+
);
27+
28+
const Collapsible = ({children, title, trigger, ...props}: CollapsibleProps) => {
29+
const [open, setOpen] = useState(props.open ?? true);
30+
31+
const toggleCollapse=() => setOpen((p) => !p);
32+
33+
return (
34+
<article>
35+
<span style={{display: 'flex', alignItems: 'center'}}>
36+
{title && <p>{title}</p>}
37+
{
38+
trigger ||
39+
<ButtonPrimitive style={{marginInlineStart: 'auto'}} onClick={toggleCollapse}>{open?<CollapseIcon/>:<ExpandIcon/>}</ButtonPrimitive>
40+
}
41+
</span>
42+
43+
<div
44+
aria-hidden={!open}
45+
style={{
46+
overflow: 'hidden',
47+
display: 'flex',
48+
flexDirection: 'column',
49+
height: (props.open ?? open)? 'auto': '0',
50+
transition: 'all',
51+
}}>
52+
{children}
53+
</div>
54+
55+
</article>
56+
);
57+
};
58+
59+
export default Collapsible;

0 commit comments

Comments
 (0)