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