Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit464fccd

Browse files
jaaydenhEmyrk
andauthored
chore: create collapsible summary component (#16705)
This is based on the Figma designs here:https://www.figma.com/design/WfqIgsTFXN2BscBSSyXWF8/Coder-kit?node-id=507-1525&m=dev---------Co-authored-by: Steven Masley <stevenmasley@gmail.com>
1 parentcccdf1e commit464fccd

File tree

3 files changed

+224
-35
lines changed

3 files changed

+224
-35
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
importtype{Meta,StoryObj}from"@storybook/react";
2+
import{Button}from"../Button/Button";
3+
import{CollapsibleSummary}from"./CollapsibleSummary";
4+
5+
constmeta:Meta<typeofCollapsibleSummary>={
6+
title:"components/CollapsibleSummary",
7+
component:CollapsibleSummary,
8+
args:{
9+
label:"Advanced options",
10+
children:(
11+
<>
12+
<divclassName="p-2 border border-border rounded-md border-solid">
13+
Option 1
14+
</div>
15+
<divclassName="p-2 border border-border rounded-md border-solid">
16+
Option 2
17+
</div>
18+
<divclassName="p-2 border border-border rounded-md border-solid">
19+
Option 3
20+
</div>
21+
</>
22+
),
23+
},
24+
};
25+
26+
exportdefaultmeta;
27+
typeStory=StoryObj<typeofCollapsibleSummary>;
28+
29+
exportconstDefault:Story={};
30+
31+
exportconstDefaultOpen:Story={
32+
args:{
33+
defaultOpen:true,
34+
},
35+
};
36+
37+
exportconstMediumSize:Story={
38+
args:{
39+
size:"md",
40+
},
41+
};
42+
43+
exportconstSmallSize:Story={
44+
args:{
45+
size:"sm",
46+
},
47+
};
48+
49+
exportconstCustomClassName:Story={
50+
args:{
51+
className:"text-blue-500 font-bold",
52+
},
53+
};
54+
55+
exportconstManyChildren:Story={
56+
args:{
57+
defaultOpen:true,
58+
children:(
59+
<>
60+
{Array.from({length:10}).map((_,i)=>(
61+
<div
62+
key={`option-${i+1}`}
63+
className="p-2 border border-border rounded-md border-solid"
64+
>
65+
Option{i+1}
66+
</div>
67+
))}
68+
</>
69+
),
70+
},
71+
};
72+
73+
exportconstNestedCollapsible:Story={
74+
args:{
75+
defaultOpen:true,
76+
children:(
77+
<>
78+
<divclassName="p-2 border border-border rounded-md border-solid">
79+
Option 1
80+
</div>
81+
<CollapsibleSummarylabel="Nested options"size="sm">
82+
<divclassName="p-2 border border-border rounded-md border-solid">
83+
Nested Option 1
84+
</div>
85+
<divclassName="p-2 border border-border rounded-md border-solid">
86+
Nested Option 2
87+
</div>
88+
</CollapsibleSummary>
89+
<divclassName="p-2 border border-border rounded-md border-solid">
90+
Option 3
91+
</div>
92+
</>
93+
),
94+
},
95+
};
96+
97+
exportconstComplexContent:Story={
98+
args:{
99+
defaultOpen:true,
100+
children:(
101+
<divclassName="p-4 border border-border rounded-md bg-surface-secondary">
102+
<h3className="text-lg font-bold mb-2">Complex Content</h3>
103+
<pclassName="mb-4">
104+
This is a more complex content example with various elements.
105+
</p>
106+
<divclassName="flex gap-2">
107+
<Button>Action 1</Button>
108+
<Button>Action 2</Button>
109+
</div>
110+
</div>
111+
),
112+
},
113+
};
114+
115+
exportconstLongLabel:Story={
116+
args:{
117+
label:
118+
"This is a very long label that might wrap or cause layout issues if not handled properly",
119+
},
120+
};
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import{typeVariantProps,cva}from"class-variance-authority";
2+
import{ChevronRightIcon}from"lucide-react";
3+
import{typeFC,typeReactNode,useState}from"react";
4+
import{cn}from"utils/cn";
5+
6+
constcollapsibleSummaryVariants=cva(
7+
`flex items-center gap-1 p-0 bg-transparent border-0 text-inherit cursor-pointer
8+
transition-colors text-content-secondary hover:text-content-primary font-medium
9+
whitespace-nowrap`,
10+
{
11+
variants:{
12+
size:{
13+
md:"text-sm",
14+
sm:"text-xs",
15+
},
16+
},
17+
defaultVariants:{
18+
size:"md",
19+
},
20+
},
21+
);
22+
23+
exportinterfaceCollapsibleSummaryProps
24+
extendsVariantProps<typeofcollapsibleSummaryVariants>{
25+
/**
26+
* The label to display for the collapsible section
27+
*/
28+
label:string;
29+
/**
30+
* The content to show when expanded
31+
*/
32+
children:ReactNode;
33+
/**
34+
* Whether the section is initially expanded
35+
*/
36+
defaultOpen?:boolean;
37+
/**
38+
* Optional className for the button
39+
*/
40+
className?:string;
41+
/**
42+
* The size of the component
43+
*/
44+
size?:"md"|"sm";
45+
}
46+
47+
exportconstCollapsibleSummary:FC<CollapsibleSummaryProps>=({
48+
label,
49+
children,
50+
defaultOpen=false,
51+
className,
52+
size,
53+
})=>{
54+
const[isOpen,setIsOpen]=useState(defaultOpen);
55+
56+
return(
57+
<divclassName="flex flex-col gap-4">
58+
<button
59+
className={cn(
60+
collapsibleSummaryVariants({ size}),
61+
isOpen&&"text-content-primary",
62+
className,
63+
)}
64+
type="button"
65+
onClick={()=>{
66+
setIsOpen((v)=>!v);
67+
}}
68+
>
69+
<div
70+
className={cn(
71+
"flex items-center justify-center transition-transform duration-200",
72+
isOpen ?"rotate-90" :"rotate-0",
73+
)}
74+
>
75+
<ChevronRightIcon
76+
className={cn(
77+
"p-0.5",
78+
size==="sm" ?"size-icon-xs" :"size-icon-sm",
79+
)}
80+
/>
81+
</div>
82+
<spanclassName="sr-only">
83+
({isOpen ?"Hide" :"Show"}){label}
84+
</span>
85+
<spanclassName="[&:first-letter]:uppercase">{label}</span>
86+
</button>
87+
88+
{isOpen&&<divclassName="flex flex-col gap-4">{children}</div>}
89+
</div>
90+
);
91+
};

‎site/src/pages/OrganizationSettingsPage/UserTable/EditRolesButton.tsx

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Checkbox from "@mui/material/Checkbox";
33
importTooltipfrom"@mui/material/Tooltip";
44
importtype{SlimRole}from"api/typesGenerated";
55
import{Button}from"components/Button/Button";
6+
import{CollapsibleSummary}from"components/CollapsibleSummary/CollapsibleSummary";
67
import{
78
HelpTooltip,
89
HelpTooltipContent,
@@ -159,41 +160,18 @@ export const EditRolesButton: FC<EditRolesButtonProps> = ({
159160
/>
160161
))}
161162
{advancedRoles.length>0&&(
162-
<>
163-
<button
164-
className={cn([
165-
"flex items-center gap-1 p-0 bg-transparent border-0 text-inherit text-sm cursor-pointer",
166-
"transition-colors text-content-secondary hover:text-content-primary font-medium whitespace-nowrap",
167-
isAdvancedOpen&&"text-content-primary",
168-
])}
169-
type="button"
170-
onClick={()=>{
171-
setIsAdvancedOpen((v)=>!v);
172-
}}
173-
>
174-
{isAdvancedOpen ?(
175-
<ChevronDownIconclassName="size-icon-sm p-0.5"/>
176-
) :(
177-
<ChevronRightIconclassName="size-icon-sm p-0.5"/>
178-
)}
179-
<spanclassName="sr-only">
180-
({isAdvancedOpen ?"Hide" :"Show advanced"})
181-
</span>
182-
<spanclassName="[&:first-letter]:uppercase">Advanced</span>
183-
</button>
184-
185-
{isAdvancedOpen&&
186-
advancedRoles.map((role)=>(
187-
<Option
188-
key={role.name}
189-
onChange={handleChange}
190-
isChecked={selectedRoleNames.has(role.name)}
191-
value={role.name}
192-
name={role.display_name||role.name}
193-
description={roleDescriptions[role.name]??""}
194-
/>
195-
))}
196-
</>
163+
<CollapsibleSummarylabel="advanced"defaultOpen={isAdvancedOpen}>
164+
{advancedRoles.map((role)=>(
165+
<Option
166+
key={role.name}
167+
onChange={handleChange}
168+
isChecked={selectedRoleNames.has(role.name)}
169+
value={role.name}
170+
name={role.display_name||role.name}
171+
description={roleDescriptions[role.name]??""}
172+
/>
173+
))}
174+
</CollapsibleSummary>
197175
)}
198176
</div>
199177
</fieldset>

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp