diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_accordion-section.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_accordion-section.scss
index 28ac713ce..d4894abd8 100644
--- a/modules/ppcp-settings/resources/css/components/reusable-components/_accordion-section.scss
+++ b/modules/ppcp-settings/resources/css/components/reusable-components/_accordion-section.scss
@@ -2,26 +2,27 @@
margin-left: auto;
margin-right: auto;
- &--title {
+ &__toggler {
+ display: block;
+ cursor: pointer;
+
+ background: transparent;
+ border: 0;
+ box-shadow: none;
+ padding: 0;
+ margin: 24px auto;
+ }
+
+ &__title-wrapper {
@include font(14, 32, 450);
color: $color-gray-900;
display: flex;
align-items: center;
gap: 16px;
- margin: 24px auto;
- border: 0;
- background: transparent;
- cursor: pointer;
}
- &--content {
+ &__content {
margin: 24px 0 0;
}
-
- &.ppcp--is-open {
- .ppcp-r-accordion--icon {
- transform: rotate(180deg);
- }
- }
}
diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/AccordionSection.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/AccordionSection.js
index 23f01a09c..f5b071945 100644
--- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/AccordionSection.js
+++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/AccordionSection.js
@@ -1,65 +1,72 @@
-import { useEffect } from '@wordpress/element';
import { Icon } from '@wordpress/components';
import { chevronDown, chevronUp } from '@wordpress/icons';
-import { useState } from 'react';
+import classNames from 'classnames';
+
+import { useAccordionState } from '../../hooks/useAccordionState';
+
+// Provide defaults for all layout components so the generic version just works.
+const DefaultHeader = ( { children, className = '' } ) => (
+
+ { children }
+
+);
+const DefaultTitleWrapper = ( { children } ) => (
+ { children }
+);
+const DefaultTitle = ( { children } ) => (
+ { children }
+);
+const DefaultAction = ( { children } ) => (
+ { children }
+);
+const DefaultDescription = ( { children } ) => (
+ { children }
+);
const Accordion = ( {
title,
- initiallyOpen = null,
- className = '',
id = '',
- children,
+ initiallyOpen = null,
+ description = '',
+ children = null,
+ className = '',
+
+ // Layout components can be overridden by the caller
+ Header = DefaultHeader,
+ TitleWrapper = DefaultTitleWrapper,
+ Title = DefaultTitle,
+ Action = DefaultAction,
+ Description = DefaultDescription,
} ) => {
- const determineInitialState = () => {
- if ( id && initiallyOpen === null ) {
- return window.location.hash === `#${ id }`;
- }
- return !! initiallyOpen;
- };
+ const { isOpen, toggleOpen } = useAccordionState( { id, initiallyOpen } );
+ const wrapperClasses = classNames( 'ppcp-r-accordion', className, {
+ 'ppcp--is-open': isOpen,
+ } );
- const [ isOpen, setIsOpen ] = useState( determineInitialState );
-
- useEffect( () => {
- const handleHashChange = () => {
- if ( id && window.location.hash === `#${ id }` ) {
- setIsOpen( true );
- }
- };
-
- window.addEventListener( 'hashchange', handleHashChange );
-
- return () => {
- window.removeEventListener( 'hashchange', handleHashChange );
- };
- }, [ id ] );
-
- const toggleOpen = ( ev ) => {
- setIsOpen( ! isOpen );
- ev?.preventDefault();
- return false;
- };
-
- const wrapperClasses = [ 'ppcp-r-accordion' ];
- if ( className ) {
- wrapperClasses.push( className );
- }
- if ( isOpen ) {
- wrapperClasses.push( 'ppcp--is-open' );
- }
+ const icon = isOpen ? chevronUp : chevronDown;
return (
-
+
- { isOpen && (
-
{ children }
+ { isOpen && children && (
+
{ children }
) }
);
diff --git a/modules/ppcp-settings/resources/js/hooks/useAccordionState.js b/modules/ppcp-settings/resources/js/hooks/useAccordionState.js
new file mode 100644
index 000000000..f54018262
--- /dev/null
+++ b/modules/ppcp-settings/resources/js/hooks/useAccordionState.js
@@ -0,0 +1,39 @@
+import { useEffect, useState } from '@wordpress/element';
+
+const checkIfCurrentTab = ( id ) => {
+ return id && window.location.hash === `#${ id }`;
+};
+
+const determineInitialState = ( id, initiallyOpen ) => {
+ if ( initiallyOpen !== null ) {
+ return initiallyOpen;
+ }
+ return checkIfCurrentTab( id );
+};
+
+export function useAccordionState( { id = '', initiallyOpen = null } ) {
+ const [ isOpen, setIsOpen ] = useState(
+ determineInitialState( id, initiallyOpen )
+ );
+
+ useEffect( () => {
+ const handleHashChange = () => {
+ if ( checkIfCurrentTab( id ) ) {
+ setIsOpen( true );
+ }
+ };
+
+ window.addEventListener( 'hashchange', handleHashChange );
+ return () => {
+ window.removeEventListener( 'hashchange', handleHashChange );
+ };
+ }, [ id ] );
+
+ const toggleOpen = ( ev ) => {
+ setIsOpen( ! isOpen );
+ ev?.preventDefault();
+ return false;
+ };
+
+ return { isOpen, toggleOpen };
+}