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:
Ilyas Foo 2024-08-20 11:17:17 +08:00 committed by GitHub
parent 444cb40c7e
commit 6733c22f3f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 351 additions and 0 deletions

View File

@ -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;
};

View File

@ -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;
}
}
}

View File

@ -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' );
} );
} );

View File

@ -12,6 +12,7 @@ import {
createInterpolateElement, createInterpolateElement,
createElement, createElement,
useEffect, useEffect,
useRef,
} from '@wordpress/element'; } from '@wordpress/element';
import { registerPlugin } from '@wordpress/plugins'; import { registerPlugin } from '@wordpress/plugins';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
@ -29,6 +30,7 @@ import {
COMING_SOON_PAGE_EDITOR_LINK, COMING_SOON_PAGE_EDITOR_LINK,
SITE_VISIBILITY_DOC_LINK, SITE_VISIBILITY_DOC_LINK,
} from '../constants'; } from '../constants';
import { ConfirmationModal } from './components/confirmation-modal';
const { Fill } = createSlotFill( SETTINGS_SLOT_FILL_CONSTANT ); const { Fill } = createSlotFill( SETTINGS_SLOT_FILL_CONSTANT );
@ -45,6 +47,21 @@ const SiteVisibility = () => {
const [ privateLink, setPrivateLink ] = useState( const [ privateLink, setPrivateLink ] = useState(
setting?.woocommerce_private_link || 'no' 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( () => { useEffect( () => {
const initValues = { const initValues = {
@ -266,6 +283,13 @@ const SiteVisibility = () => {
) } ) }
</p> </p>
</div> </div>
{ formRef.current && saveButtonRef.current ? (
<ConfirmationModal
saveButtonRef={ saveButtonRef }
formRef={ formRef }
currentSetting={ setting }
/>
) : null }
</div> </div>
); );
}; };

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Add confirmation prompt for site visibility settings when changing from live to coming soon mode