import { classnames, PropTypes, React } from '@gravityforms/libraries';
import { ConditionalWrapper, useStateWithDep } from '@gravityforms/react-utils';
import { spacerClasses } from '@gravityforms/utils';
import Button from '../../elements/Button';
const { forwardRef } = React;
/**
* @module MetaBox
* @description A metabox component with optional header and footer.
*
* @since 1.1.15
*
* @param {object} props Component props.
* @param {boolean} props.collapsible Whether the meta box is collapsible.
* @param {string} props.collapsibleIcon The icon for the collapsible button.
* @param {string} props.collapsibleIconPrefix The icon prefix for the collapsible button.
* @param {boolean} props.collapsed Whether the meta box is collapsed.
* @param {string} props.contentId The ID for the content, used for a11y when collapsible is true.
* @param {JSX.Element} props.children React element children.
* @param {object} props.customAttributes Custom attributes for the layout.
* @param {string|Array|object} props.customClasses Custom classes for the layout.
* @param {JSX.Element} props.HeaderContent The header element.
* @param {object} props.i18n Internationalization object.
* @param {JSX.Element} props.FooterContent The footer element.
* @param {string|number|Array|object} props.spacing The spacing for the component, as a string, number, array, or object.
* @param {string} props.tagName The tag name for the layout.
*
* @return {JSX.Element} The MetaBox component.
* @example
* import MetaBox from '@gravityforms/components/admin/modules/MetaBox';
*
* const HeaderContent = ( props ) => <SomeComponent { ...props } />;
* const FooterContent = ( props ) => <SomeComponent { ...props } />;
*
* <MetaBox
* HeaderContent={ HeaderContent }
* FooterContent={ FooterContent }
* customClasses={ 'my-custom-class' }
* customAttributes={ { 'data-custom-attribute': 'custom' } }
* tagName={ 'section' }
* >
* <div>Content</div>
*</MetaBox>
*
*/
const MetaBox = forwardRef( ( {
children = null,
collapsible = false,
collapsibleIcon = 'accordion-arrow',
collapsibleIconPrefix = 'gform-icon',
collapsed = false,
contentId = '',
customAttributes = {},
customClasses = {},
FooterContent = null,
HeaderContent = null,
i18n = {},
spacing = '',
tagName = 'div',
...props
}, ref ) => {
const [ isCollapsed, setIsCollapsed ] = useStateWithDep( collapsed );
const {
collapsedAriaLabel = '',
expandedAriaLabel = '',
} = i18n;
const componentProps = {
className: classnames( {
'gform-meta-box': true,
'gform-meta-box--is-collapsible': collapsible,
'gform-meta-box--is-collapsed': isCollapsed,
...spacerClasses( spacing ),
}, customClasses ),
...customAttributes,
'data-testid': 'gform-meta-box',
ref,
};
const toggleButtonProps = {
customAttributes: {
'aria-controls': contentId ? contentId : undefined,
'aria-expanded': ! isCollapsed,
'aria-label': isCollapsed ? collapsedAriaLabel : expandedAriaLabel,
},
customClasses: [ 'gform-meta-box__toggle' ],
icon: collapsibleIcon,
iconAttributes: {
customClasses: [ 'gform-meta-box__toggle-icon' ],
},
iconPrefix: collapsibleIconPrefix,
onClick: () => setIsCollapsed( ! isCollapsed ),
type: 'unstyled',
};
const Container = tagName;
return (
<Container { ...componentProps }>
{ HeaderContent &&
<div
className="gform-meta-box__header"
data-testid="gform-meta-box-header"
>
<ConditionalWrapper
condition={ collapsible }
wrapper={ ( ch ) => <Button { ...toggleButtonProps }>{ ch }</Button> }
>
<HeaderContent { ...props } />
</ConditionalWrapper>
</div>
}
<div
className="gform-meta-box__content"
id={ contentId ? contentId : undefined }
style={ { display: isCollapsed ? 'none' : '' } }
>
{ children }
</div>
{ FooterContent &&
<div
className="gform-meta-box__footer"
data-testid="gform-meta-box-footer"
>
<FooterContent { ...props } />
</div>
}
</Container>
);
} );
MetaBox.propTypes = {
children: PropTypes.oneOfType( [
PropTypes.arrayOf( PropTypes.node ),
PropTypes.node,
] ),
customAttributes: PropTypes.object,
customClasses: PropTypes.oneOfType( [
PropTypes.string,
PropTypes.array,
PropTypes.object,
] ),
FooterContent: PropTypes.func,
HeaderContent: PropTypes.func,
spacing: PropTypes.oneOfType( [
PropTypes.string,
PropTypes.number,
PropTypes.array,
PropTypes.object,
] ),
tagName: PropTypes.string,
};
MetaBox.displayName = 'MetaBox';
export default MetaBox;