Skip to content

Commit

Permalink
add option to open collapsiblePanel with icon click (#169)
Browse files Browse the repository at this point in the history
* add option to open panel with icon click

* fix lint

* fix version

* fix header click to work with custom rendered too
  • Loading branch information
dayesouza committed Oct 7, 2022
1 parent a631699 commit cf54492
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 15 deletions.
4 changes: 4 additions & 0 deletions .yarn/versions/00ea1a4c.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
releases:
"@essex/components": minor
essex-toolkit: minor
essex-toolkit-stories: minor
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,23 @@ const styles = {
CollapsiblePanelStyled.story = {
name: 'Styled Panel Header and Contents',
}

export const CollapsiblePanelIconClickStory = () => {
return (
<CollapsiblePanel
title="Panel"
expandsWithIcon
onHeaderClick={() => alert('header clicked')}
>
<div>This panel opens only with click on the icon</div>
<div>
This panel emits an alert when the header is clicked (except for the
icon)
</div>
</CollapsiblePanel>
)
}

CollapsiblePanelIconClickStory.story = {
name: 'Icon click function',
}
61 changes: 46 additions & 15 deletions packages/components/src/CollapsiblePanel/CollapsiblePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const CollapsiblePanel: React.FC<CollapsiblePanelProps> = ({
children,
onRenderHeader,
onHeaderClick,
expandsWithIcon = false,
styles = {
header: {},
contents: {},
Expand All @@ -33,14 +34,18 @@ export const CollapsiblePanel: React.FC<CollapsiblePanelProps> = ({
const theme = useThematicFluent()
const [expanded, setExpanded] = useState<boolean>(defaultExpanded)
const handleClick = useCallback(() => {
if (onHeaderClick) {
onHeaderClick(!expanded)
}
// if not controlled component, set local state
if (expandedState === undefined) {
setExpanded(!expanded)
}
}, [expanded, onHeaderClick, expandedState])
}, [expanded, expandedState])

const handleHeaderClick = useCallback(() => {
if (onHeaderClick) {
onHeaderClick(!expanded)
}
!expandsWithIcon && handleClick()
}, [handleClick, onHeaderClick, expandsWithIcon, expanded])

const handleKeyDown = useCallback(
(event: React.KeyboardEvent) => {
Expand All @@ -55,18 +60,27 @@ export const CollapsiblePanel: React.FC<CollapsiblePanelProps> = ({
[handleClick, expanded],
)

const handleHeaderKeyDown = useCallback(
(event: React.KeyboardEvent) => {
if ('Enter' === event.key || ' ' === event.key) {
return handleHeaderClick()
}
},
[handleHeaderClick],
)

useEffect(() => {
if (expandedState !== undefined) {
setExpanded(expandedState)
}
}, [setExpanded, expandedState])

const iconProps = useIconProps(expanded)
const iconProps = useIconProps(expanded, expandsWithIcon ? 12 : 10)
const header = useMemo(() => {
if (onRenderHeader) {
return onRenderHeader()
}
return <div style={HeaderLabelStyle}>{title}</div>
return <span style={HeaderLabelStyle}>{title}</span>
}, [onRenderHeader, title])
const contentsStyle = useMemo(
() => ({
Expand All @@ -83,11 +97,15 @@ export const CollapsiblePanel: React.FC<CollapsiblePanelProps> = ({
first={first}
last={last}
expanded={expanded}
onClick={handleClick}
onKeyDown={handleKeyDown}
onKeyDown={!expandsWithIcon ? handleKeyDown : undefined}
onClick={!expandsWithIcon ? handleHeaderClick : undefined}
style={styles.header}
>
<div style={ButtonContainerStyle}>
<div
onKeyDown={expandsWithIcon ? handleKeyDown : undefined}
onClick={expandsWithIcon ? handleClick : undefined}
style={ButtonContainerStyle}
>
<IconButton
title="collapse"
iconProps={iconProps}
Expand All @@ -98,7 +116,17 @@ export const CollapsiblePanel: React.FC<CollapsiblePanelProps> = ({
}}
/>
</div>
{header}
<div
role="group"
//the element is interactive when tabIndex is defined
//eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
tabIndex={expandsWithIcon ? 0 : undefined}
onKeyDown={expandsWithIcon ? handleHeaderKeyDown : undefined}
onClick={expandsWithIcon ? handleHeaderClick : undefined}
style={HeaderStyle}
>
{header}
</div>
</HeaderContainer>
<div style={contentsStyle}>
<AnimateHeight duration={500} height={expanded ? 'auto' : 0}>
Expand Down Expand Up @@ -133,8 +161,11 @@ const ButtonContainerStyle: React.CSSProperties = {
}

const HeaderLabelStyle: React.CSSProperties = {
marginLeft: 4,
fontSize: '0.8em',
}

const HeaderStyle: React.CSSProperties = {
marginLeft: 4,
width: '100%',
}

Expand All @@ -150,8 +181,8 @@ const HeaderContainer: React.FC<{
first?: boolean
last?: boolean
expanded?: boolean
onClick: () => void
onKeyDown: (ev: React.KeyboardEvent) => void
onClick?: () => void
onKeyDown?: (ev: React.KeyboardEvent) => void
style?: React.CSSProperties
children?: React.ReactNode
}> = memo(function HeaderContainer({
Expand Down Expand Up @@ -189,6 +220,6 @@ const HeaderContainer: React.FC<{
})

const IconButtonStyle: React.CSSProperties = {
width: 18,
height: 18,
width: 20,
height: 20,
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export interface CollapsiblePanelProps {
* Optional function to control the state outside the component
*/
onHeaderClick?: (nextState: boolean) => void
/**
* If true panel will be toggled only when carret icon click directly
*/
expandsWithIcon?: boolean
children?: ReactNode
styles?: {
header?: React.CSSProperties
Expand Down

0 comments on commit cf54492

Please sign in to comment.