@zendeskgarden/container-accordion
Version:
Containers relating to accordions in the Garden Design System
131 lines (125 loc) • 4.27 kB
JavaScript
/**
* Copyright Zendesk, Inc.
*
* Use of this source code is governed under the Apache License, Version 2.0
* found at http://www.apache.org/licenses/LICENSE-2.0.
*/
;
var React = require('react');
var containerUtilities = require('@zendeskgarden/container-utilities');
var PropTypes = require('prop-types');
function useAccordion({
idPrefix,
sections = [],
expandedSections,
defaultExpandedSections,
onChange = () => undefined,
expandable = true,
collapsible = true
}) {
const prefix = containerUtilities.useId(idPrefix);
const TRIGGER_ID = `${prefix}--trigger`;
const PANEL_ID = `${prefix}--panel`;
const isControlled = expandedSections !== null && expandedSections !== undefined;
const [expandedState, setExpandedState] = React.useState(defaultExpandedSections || sections.slice(0, 1));
const [disabledState, setDisabledState] = React.useState(collapsible ? [] : expandedState);
const internalExpandedState = containerUtilities.getControlledValue(expandedSections, expandedState);
const toggle = React.useCallback(value => {
const expanded = [];
const disabled = [];
sections.forEach(sectionValue => {
let isExpanded = false;
if (sectionValue === value) {
isExpanded = collapsible ? internalExpandedState.includes(sectionValue) === false : true;
} else if (expandable) {
isExpanded = internalExpandedState.includes(sectionValue);
}
if (isExpanded) {
expanded.push(sectionValue);
if (!collapsible) {
disabled.push(sectionValue);
}
}
});
onChange(value);
if (isControlled === false) {
setExpandedState(expanded);
}
setDisabledState(disabled);
}, [sections, internalExpandedState, collapsible, expandable, isControlled, onChange]);
const getHeaderProps = React.useCallback(({
role = 'heading',
'aria-level': ariaLevel,
...props
}) => ({
role: role === null ? undefined : role,
'aria-level': ariaLevel,
'data-garden-container-id': 'containers.accordion',
'data-garden-container-version': '3.0.15',
...props
}), []);
const getTriggerProps = React.useCallback(({
value,
role = 'button',
tabIndex = 0,
...props
}) => ({
id: `${TRIGGER_ID}:${value}`,
role: role === null ? undefined : role,
tabIndex,
'aria-controls': `${PANEL_ID}:${value}`,
'aria-disabled': disabledState.includes(value) || undefined,
'aria-expanded': internalExpandedState.includes(value),
onClick: containerUtilities.composeEventHandlers(props.onClick, () => toggle(value)),
onKeyDown: containerUtilities.composeEventHandlers(props.onKeyDown, event => {
if (event.key === containerUtilities.KEYS.SPACE || event.key === containerUtilities.KEYS.ENTER) {
toggle(value);
event.preventDefault();
}
}),
...props
}), [PANEL_ID, TRIGGER_ID, internalExpandedState, disabledState, toggle]);
const getPanelProps = React.useCallback(({
value,
role = 'region',
...props
}) => ({
id: `${PANEL_ID}:${value}`,
role: role === null ? undefined : role,
'aria-hidden': !internalExpandedState.includes(value),
'aria-labelledby': `${TRIGGER_ID}:${value}`,
...props
}), [PANEL_ID, TRIGGER_ID, internalExpandedState]);
return React.useMemo(() => ({
getHeaderProps,
getTriggerProps,
getPanelProps,
expandedSections: internalExpandedState,
disabledSections: disabledState
}), [getHeaderProps, getTriggerProps, getPanelProps, internalExpandedState, disabledState]);
}
const AccordionContainer = props => {
const {
children,
render = children,
...options
} = props;
return React.createElement(React.Fragment, null, render(useAccordion(options)));
};
AccordionContainer.defaultProps = {
expandable: true,
collapsible: true
};
AccordionContainer.propTypes = {
children: PropTypes.func,
render: PropTypes.func,
sections: PropTypes.array.isRequired,
expandedSections: PropTypes.array,
defaultExpandedSections: PropTypes.array,
expandable: PropTypes.bool,
collapsible: PropTypes.bool,
idPrefix: PropTypes.string,
onChange: PropTypes.func
};
exports.AccordionContainer = AccordionContainer;
exports.useAccordion = useAccordion;