Add site visibility settings confirmation modal (#50759)
* Add confirmation modal for site visibility when changing from live to coming soon mode * Changelog * Remove unnecessary space * Update tests * Lint
This commit is contained in:
parent
444cb40c7e
commit
6733c22f3f
|
@ -0,0 +1,116 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Button, Modal } from '@wordpress/components';
|
||||
import { useState, useEffect } from '@wordpress/element';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './confirmation-modal.scss';
|
||||
|
||||
/**
|
||||
* Confirmation modal for the site visibility settings.
|
||||
*
|
||||
* @return {React.ReactNode} The confirmation modal component.
|
||||
*/
|
||||
export const ConfirmationModal = ( {
|
||||
formRef,
|
||||
saveButtonRef,
|
||||
currentSetting,
|
||||
} ) => {
|
||||
const [ pendingSubmitEvent, setPendingSubmitEvent ] = useState( null );
|
||||
const [ isConfirmModalOpen, setIsConfirmModalOpen ] = useState( false );
|
||||
const currentComingSoon = currentSetting?.woocommerce_coming_soon ?? 'no';
|
||||
|
||||
// Hooks into settings' "mainform" to show a confirmation modal when the form is submitted.
|
||||
useEffect( () => {
|
||||
const form = formRef.current;
|
||||
const handleFormSubmit = ( event ) => {
|
||||
const formData = new FormData( form );
|
||||
|
||||
// Only block submission when switching to coming soon mode from live.
|
||||
if (
|
||||
currentComingSoon === 'no' &&
|
||||
formData.get( 'woocommerce_coming_soon' ) === 'yes'
|
||||
) {
|
||||
event.preventDefault();
|
||||
setIsConfirmModalOpen( true );
|
||||
setPendingSubmitEvent( event );
|
||||
}
|
||||
};
|
||||
if ( form ) {
|
||||
form.addEventListener( 'submit', handleFormSubmit );
|
||||
}
|
||||
|
||||
return () => {
|
||||
if ( form ) {
|
||||
form.removeEventListener( 'submit', handleFormSubmit );
|
||||
}
|
||||
};
|
||||
}, [ currentSetting, formRef ] );
|
||||
|
||||
const cancelSubmit = () => {
|
||||
setPendingSubmitEvent( null ); // Clear the pending submit
|
||||
setIsConfirmModalOpen( false ); // Close the modal
|
||||
|
||||
if ( saveButtonRef.current ) {
|
||||
saveButtonRef.current.classList.remove( 'is-busy' );
|
||||
}
|
||||
};
|
||||
|
||||
const confirmSubmit = () => {
|
||||
if ( pendingSubmitEvent ) {
|
||||
// WooCommerce checks for the "save" input.
|
||||
if ( saveButtonRef.current && formRef.current ) {
|
||||
const hiddenInput = document.createElement( 'input' );
|
||||
hiddenInput.type = 'hidden';
|
||||
hiddenInput.name = saveButtonRef.current.name || 'save';
|
||||
hiddenInput.value =
|
||||
saveButtonRef.current.value ||
|
||||
__( 'Save changes', 'woocommerce' );
|
||||
formRef.current.appendChild( hiddenInput );
|
||||
}
|
||||
|
||||
pendingSubmitEvent.target.submit();
|
||||
setPendingSubmitEvent( null );
|
||||
}
|
||||
setIsConfirmModalOpen( false ); // Close the modal
|
||||
};
|
||||
|
||||
return isConfirmModalOpen ? (
|
||||
<Modal
|
||||
title={ __(
|
||||
'Confirm switch to ‘Coming soon’ mode',
|
||||
'woocommerce'
|
||||
) }
|
||||
onRequestClose={ cancelSubmit }
|
||||
size="medium"
|
||||
className="site-visibility-settings-confirmation-modal"
|
||||
>
|
||||
<div className="site-visibility-settings-confirmation-modal__content">
|
||||
{ __(
|
||||
"Are you sure you want to switch from live to coming soon mode? Your site will not be visible, and customers won't be able to make purchases during this time.",
|
||||
'woocommerce'
|
||||
) }
|
||||
</div>
|
||||
<div className="divider-container">
|
||||
<hr />
|
||||
</div>
|
||||
<div className="site-visibility-settings-confirmation-modal__buttons">
|
||||
<Button
|
||||
variant="primary"
|
||||
isBusy={ false }
|
||||
onClick={ confirmSubmit }
|
||||
>
|
||||
{ __( 'Switch', 'woocommerce' ) }
|
||||
</Button>
|
||||
<Button variant="tertiary" onClick={ cancelSubmit }>
|
||||
{ __( 'Cancel', 'woocommerce' ) }
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
) : null;
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
.site-visibility-settings-confirmation-modal {
|
||||
.site-visibility-settings-confirmation-modal__content {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.site-visibility-settings-confirmation-modal__buttons {
|
||||
display: flex;
|
||||
flex-flow: row-reverse;
|
||||
}
|
||||
|
||||
// Hacky solution for a divider line that spans over its container's paddings.
|
||||
.divider-container {
|
||||
height: 1px;
|
||||
margin-bottom: 30px;
|
||||
margin-top: 25px;
|
||||
|
||||
& > hr {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render, fireEvent, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ConfirmationModal } from '../confirmation-modal';
|
||||
|
||||
// Mock the necessary external dependencies
|
||||
jest.mock( '@wordpress/components', () => ( {
|
||||
Modal: jest.fn( ( { title, children, onRequestClose } ) => (
|
||||
<div>
|
||||
<div>{ title }</div>
|
||||
<div>{ children }</div>
|
||||
<button onClick={ onRequestClose }>Close</button>
|
||||
</div>
|
||||
) ),
|
||||
Button: jest.fn( ( { children, onClick } ) => (
|
||||
<button onClick={ onClick }>{ children }</button>
|
||||
) ),
|
||||
} ) );
|
||||
|
||||
describe( 'ConfirmationModal', () => {
|
||||
let formRef, saveButtonRef;
|
||||
|
||||
const mockSelectComingSoon = ( value ) => {
|
||||
// Set up form data
|
||||
const input = document.createElement( 'input' );
|
||||
input.name = 'woocommerce_coming_soon';
|
||||
input.value = value;
|
||||
formRef.current.appendChild( input );
|
||||
};
|
||||
|
||||
const fireSubmitEvent = () => {
|
||||
// Simulate form submission
|
||||
const submitEvent = new Event( 'submit', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
} );
|
||||
fireEvent( formRef.current, submitEvent );
|
||||
};
|
||||
|
||||
beforeEach( () => {
|
||||
formRef = { current: document.createElement( 'form' ) };
|
||||
saveButtonRef = { current: document.createElement( 'button' ) };
|
||||
formRef.current.appendChild( saveButtonRef.current );
|
||||
document.body.appendChild( formRef.current );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
document.body.removeChild( formRef.current );
|
||||
} );
|
||||
|
||||
it( 'should prompt the modal if current setting is live and submit the form', () => {
|
||||
const currentSetting = { woocommerce_coming_soon: 'no' };
|
||||
|
||||
render(
|
||||
<ConfirmationModal
|
||||
formRef={ formRef }
|
||||
saveButtonRef={ saveButtonRef }
|
||||
currentSetting={ currentSetting }
|
||||
/>
|
||||
);
|
||||
|
||||
const submitListener = jest.fn();
|
||||
formRef.current.onsubmit = submitListener;
|
||||
|
||||
mockSelectComingSoon( 'yes' );
|
||||
fireSubmitEvent();
|
||||
|
||||
// Confirm modal is prompted
|
||||
expect(
|
||||
screen.getByText( 'Confirm switch to ‘Coming soon’ mode' )
|
||||
).toBeInTheDocument();
|
||||
|
||||
// Simulate confirming submission
|
||||
fireEvent.click( screen.getByText( 'Switch' ) );
|
||||
|
||||
// Ensure the form is submitted
|
||||
expect( submitListener ).toHaveBeenCalled();
|
||||
} );
|
||||
|
||||
it( 'should prompt the modal if current setting is not set', () => {
|
||||
render(
|
||||
<ConfirmationModal
|
||||
formRef={ formRef }
|
||||
saveButtonRef={ saveButtonRef }
|
||||
currentSetting={ null }
|
||||
/>
|
||||
);
|
||||
|
||||
mockSelectComingSoon( 'yes' );
|
||||
fireSubmitEvent();
|
||||
|
||||
// Confirm that the modal is not prompted
|
||||
expect(
|
||||
screen.queryByText( 'Confirm switch to ‘Coming soon’ mode' )
|
||||
).toBeInTheDocument();
|
||||
} );
|
||||
|
||||
it( 'should not prompt the modal if current setting is already "coming soon"', () => {
|
||||
const currentSetting = { woocommerce_coming_soon: 'yes' };
|
||||
|
||||
render(
|
||||
<ConfirmationModal
|
||||
formRef={ formRef }
|
||||
saveButtonRef={ saveButtonRef }
|
||||
currentSetting={ currentSetting }
|
||||
/>
|
||||
);
|
||||
|
||||
mockSelectComingSoon( 'yes' );
|
||||
fireSubmitEvent();
|
||||
|
||||
// Confirm that the modal is not prompted
|
||||
expect(
|
||||
screen.queryByText( 'Confirm switch to ‘Coming soon’ mode' )
|
||||
).not.toBeInTheDocument();
|
||||
} );
|
||||
|
||||
it( 'should close the modal on cancel', () => {
|
||||
const currentSetting = { woocommerce_coming_soon: 'no' };
|
||||
|
||||
render(
|
||||
<ConfirmationModal
|
||||
formRef={ formRef }
|
||||
saveButtonRef={ saveButtonRef }
|
||||
currentSetting={ currentSetting }
|
||||
/>
|
||||
);
|
||||
|
||||
mockSelectComingSoon( 'yes' );
|
||||
fireSubmitEvent();
|
||||
|
||||
// Confirm modal is prompted
|
||||
expect(
|
||||
screen.getByText( 'Confirm switch to ‘Coming soon’ mode' )
|
||||
).toBeInTheDocument();
|
||||
|
||||
// Simulate canceling the modal
|
||||
fireEvent.click( screen.getByText( 'Cancel' ) );
|
||||
|
||||
// Confirm that the modal is closed
|
||||
expect(
|
||||
screen.queryByText( 'Confirm switch to ‘Coming soon’ mode' )
|
||||
).not.toBeInTheDocument();
|
||||
} );
|
||||
|
||||
it( 'should handle the save button correctly', () => {
|
||||
const currentSetting = { woocommerce_coming_soon: 'no' };
|
||||
saveButtonRef.current.name = 'save';
|
||||
saveButtonRef.current.value = 'Save changes';
|
||||
|
||||
render(
|
||||
<ConfirmationModal
|
||||
formRef={ formRef }
|
||||
saveButtonRef={ saveButtonRef }
|
||||
currentSetting={ currentSetting }
|
||||
/>
|
||||
);
|
||||
|
||||
mockSelectComingSoon( 'yes' );
|
||||
fireSubmitEvent();
|
||||
|
||||
// Confirm modal is prompted
|
||||
expect(
|
||||
screen.getByText( 'Confirm switch to ‘Coming soon’ mode' )
|
||||
).toBeInTheDocument();
|
||||
|
||||
// Simulate confirming submission
|
||||
fireEvent.click( screen.getByText( 'Switch' ) );
|
||||
|
||||
// Check that the hidden input with "save" has been added to the form
|
||||
const hiddenInput =
|
||||
formRef.current.querySelector( 'input[name="save"]' );
|
||||
expect( hiddenInput ).toBeInTheDocument();
|
||||
expect( hiddenInput.value ).toBe( 'Save changes' );
|
||||
} );
|
||||
} );
|
|
@ -12,6 +12,7 @@ import {
|
|||
createInterpolateElement,
|
||||
createElement,
|
||||
useEffect,
|
||||
useRef,
|
||||
} from '@wordpress/element';
|
||||
import { registerPlugin } from '@wordpress/plugins';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
@ -29,6 +30,7 @@ import {
|
|||
COMING_SOON_PAGE_EDITOR_LINK,
|
||||
SITE_VISIBILITY_DOC_LINK,
|
||||
} from '../constants';
|
||||
import { ConfirmationModal } from './components/confirmation-modal';
|
||||
|
||||
const { Fill } = createSlotFill( SETTINGS_SLOT_FILL_CONSTANT );
|
||||
|
||||
|
@ -45,6 +47,21 @@ const SiteVisibility = () => {
|
|||
const [ privateLink, setPrivateLink ] = useState(
|
||||
setting?.woocommerce_private_link || 'no'
|
||||
);
|
||||
const formRef = useRef( null );
|
||||
const saveButtonRef = useRef( null );
|
||||
|
||||
useEffect( () => {
|
||||
const saveButton = document.getElementsByClassName(
|
||||
'woocommerce-save-button'
|
||||
)[ 0 ];
|
||||
if ( saveButton ) {
|
||||
saveButtonRef.current = saveButton;
|
||||
}
|
||||
const form = document.querySelector( '#mainform' );
|
||||
if ( form ) {
|
||||
formRef.current = form;
|
||||
}
|
||||
}, [] );
|
||||
|
||||
useEffect( () => {
|
||||
const initValues = {
|
||||
|
@ -266,6 +283,13 @@ const SiteVisibility = () => {
|
|||
) }
|
||||
</p>
|
||||
</div>
|
||||
{ formRef.current && saveButtonRef.current ? (
|
||||
<ConfirmationModal
|
||||
saveButtonRef={ saveButtonRef }
|
||||
formRef={ formRef }
|
||||
currentSetting={ setting }
|
||||
/>
|
||||
) : null }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: update
|
||||
|
||||
Add confirmation prompt for site visibility settings when changing from live to coming soon mode
|
Loading…
Reference in New Issue