UNPKG

@zendeskgarden/container-accordion

Version:

Containers relating to accordions in the Garden Design System

131 lines (125 loc) 4.27 kB
/** * 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. */ 'use strict'; 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;