Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import SvgIcon from './SvgIcon';
/**
* Lightning Design System Accordion Panel Component
* A React 19 compatible implementation following SLDS patterns
*/
const AccordionPanel = ({
id,
summary,
children,
expanded = false,
onTogglePanel,
className = '',
disabled = false,
assistiveText = {},
headerTabIndex = -1,
onHeaderKeyDown,
setHeaderButtonRef,
...otherProps
}) => {
const handleToggle = useCallback((event) => {
if (disabled) return;
if (onTogglePanel) {
onTogglePanel(event, { id, expanded: !expanded });
}
}, [disabled, onTogglePanel, id, expanded]);
const handleKeyDown = useCallback((event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
handleToggle(event);
}
}, [handleToggle]);
const panelClasses = `slds-accordion__list-item ${className}`.trim();
const sectionClasses = `slds-accordion__section ${expanded ? 'slds-is-open' : ''}`.trim();
const buttonClasses = `slds-accordion__summary-action slds-button slds-button_reset ${disabled ? 'slds-is-disabled' : ''}`.trim();
const contentClasses = `slds-accordion__content ${expanded ? 'slds-show' : 'slds-hide'}`.trim();
// Assistive text defaults
const defaultAssistiveText = {
collapsePanel: 'Collapse panel',
expandPanel: 'Expand panel',
};
const mergedAssistiveText = { ...defaultAssistiveText, ...assistiveText };
const buttonAriaLabel = expanded ? mergedAssistiveText.collapsePanel : mergedAssistiveText.expandPanel;
return (
<li className={panelClasses} {...otherProps}>
<section className={sectionClasses}>
<div className="slds-accordion__summary">
<h3 className="slds-accordion__summary-heading">
<button
type="button"
className={buttonClasses}
aria-expanded={expanded}
aria-controls={`${id}-content`}
aria-label={`${buttonAriaLabel}: ${summary}`}
id={`${id}-summary`}
tabIndex={headerTabIndex}
onClick={handleToggle}
onKeyDown={(e) => {
// First allow parent Accordion to handle arrow navigation
if (onHeaderKeyDown) onHeaderKeyDown(e);
// Then handle local toggle keys (Enter/Space)
handleKeyDown(e);
}}
disabled={disabled}
ref={setHeaderButtonRef}
>
{/* <img src={ expanded ? switchIcon : chevronRightIcon} alt="switch" className='slds-button__icon slds-button__icon_left slds-icon slds-icon-text-default slds-icon_x-small'/> */}
<SvgIcon symbol={ expanded ? 'switch' : 'chevronright'} className="slds-button__icon slds-button__icon_left slds-icon slds-icon-text-default slds-icon_x-small"/>
<span className="slds-accordion__summary-content">
{summary}
</span>
</button>
</h3>
</div>
<div
id={`${id}-content`}
className={contentClasses}
role="region"
aria-labelledby={`${id}-summary`}
>
{expanded && children}
</div>
</section>
</li>
);
};
AccordionPanel.propTypes = {
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
summary: PropTypes.node.isRequired,
children: PropTypes.node,
expanded: PropTypes.bool,
onTogglePanel: PropTypes.func,
className: PropTypes.string,
disabled: PropTypes.bool,
assistiveText: PropTypes.shape({
collapsePanel: PropTypes.string,
expandPanel: PropTypes.string,
}),
headerTabIndex: PropTypes.number,
onHeaderKeyDown: PropTypes.func,
setHeaderButtonRef: PropTypes.func,
};
AccordionPanel.defaultProps = {
expanded: false,
className: '',
disabled: false,
assistiveText: {},
};
export default AccordionPanel;
|