Created
May 20, 2025 23:33
-
-
Save brandonbryant12/38c725ca1e56851cc198a0a2401c53fb to your computer and use it in GitHub Desktop.
This file contains hidden or 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
import * as React from 'react'; | |
import { styled, useTheme, alpha } from '@mui/material/styles'; | |
import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp'; | |
import MuiAccordion, { AccordionProps } from '@mui/material/Accordion'; | |
import MuiAccordionSummary, { | |
AccordionSummaryProps, | |
} from '@mui/material/AccordionSummary'; | |
import MuiAccordionDetails from '@mui/material/AccordionDetails'; | |
import Typography from '@mui/material/Typography'; | |
const Accordion = styled((props: AccordionProps) => ( | |
<MuiAccordion disableGutters elevation={0} square {...props} /> | |
))(({ theme }) => ({ | |
border: `1px solid ${theme.palette.divider}`, | |
'&:not(:last-child)': { borderBottom: 0 }, | |
'&::before': { display: 'none' }, | |
})); | |
const AccordionSummary = styled((props: AccordionSummaryProps) => ( | |
<MuiAccordionSummary | |
expandIcon={<ArrowForwardIosSharpIcon sx={{ fontSize: '0.9rem' }} />} | |
{...props} | |
/> | |
))(() => ({ | |
flexDirection: 'row-reverse', | |
'& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': { | |
transform: 'rotate(90deg)', | |
}, | |
'& .MuiAccordionSummary-content': { marginLeft: 8 }, | |
})); | |
const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({ | |
padding: theme.spacing(2), | |
borderTop: `1px solid ${theme.palette.divider}`, | |
})); | |
interface AccordionItem { | |
header: string; | |
body: string; | |
} | |
interface CustomizedAccordionsProps { | |
items: AccordionItem[]; | |
exclusive?: boolean; | |
defaultExpandedId?: string | string[]; | |
} | |
export default function CustomizedAccordions({ | |
items, | |
exclusive = false, | |
defaultExpandedId, | |
}: CustomizedAccordionsProps) { | |
const theme = useTheme(); | |
type ExpandedState = string | string[] | false; | |
const initialExpanded: ExpandedState = React.useMemo(() => { | |
if (exclusive) { | |
if (typeof defaultExpandedId === 'string') return defaultExpandedId; | |
if (Array.isArray(defaultExpandedId) && defaultExpandedId.length) | |
return defaultExpandedId[0]; | |
return items.length ? 'panel0' : false; | |
} | |
if (Array.isArray(defaultExpandedId)) return defaultExpandedId; | |
if (typeof defaultExpandedId === 'string') return [defaultExpandedId]; | |
return items.length ? ['panel0'] : []; | |
}, [exclusive, defaultExpandedId, items.length]); | |
const [expanded, setExpanded] = React.useState<ExpandedState>(initialExpanded); | |
const handleChange = | |
(panelId: string) => | |
(_e: React.SyntheticEvent, isExpanded: boolean) => { | |
if (exclusive) { | |
setExpanded(isExpanded ? panelId : false); | |
} else { | |
setExpanded((prev) => { | |
const list = Array.isArray(prev) ? prev : []; | |
return isExpanded | |
? [...list, panelId] | |
: list.filter((id) => id !== panelId); | |
}); | |
} | |
}; | |
return ( | |
<div> | |
{items.map((item, idx) => { | |
const panelId = `panel${idx}`; | |
const isOpen = exclusive | |
? expanded === panelId | |
: Array.isArray(expanded) && expanded.includes(panelId); | |
const bg = | |
idx % 2 === 0 | |
? theme.palette.action.hover | |
: alpha(theme.palette.action.hover, 0.6); | |
return ( | |
<Accordion | |
key={panelId} | |
expanded={isOpen} | |
onChange={handleChange(panelId)} | |
> | |
<AccordionSummary sx={{ backgroundColor: bg }}> | |
<Typography>{item.header}</Typography> | |
</AccordionSummary> | |
<AccordionDetails> | |
<Typography>{item.body}</Typography> | |
</AccordionDetails> | |
</Accordion> | |
); | |
})} | |
</div> | |
); | |
} |
This file contains hidden or 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 component renders a themed, accessibility-friendly accordion list whose items (header + body) are provided at run-time. It supports an exclusive mode (only one panel open at once) and applies a subtle striped background to alternate rows for visual scanning. The right-facing arrow icon flips down on expansion, consistent with our design language, and all colours pull from the active theme so it meshes seamlessly with any of our branded palettes. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment