Skip to content

Commit

Permalink
Add a confirmation modal when the user tries to navigate away with un…
Browse files Browse the repository at this point in the history
…saved changes (woocommerce#35625)

* Add a confirmation modal when the user tries to navigate away with unsaved changes

* Add support for react router navigation

* Fix unit tests
  • Loading branch information
mdperez86 authored Nov 18, 2022
1 parent 0f69428 commit dba6d33
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 0 deletions.
66 changes: 66 additions & 0 deletions plugins/woocommerce-admin/client/hooks/usePreventLeavingPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* External dependencies
*/
import { useContext, useEffect, useMemo } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { UNSAFE_NavigationContext as NavigationContext } from 'react-router-dom';

export default function usePreventLeavingPage(
hasUnsavedChanges: boolean,
/**
* Some browsers ignore this message currently on before unload event.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event#compatibility_notes
*/
message?: string
) {
const confirmMessage = useMemo(
() =>
message ??
__( 'Changes you made may not be saved.', 'woocommerce' ),
[ message ]
);
const { navigator } = useContext( NavigationContext );

// This effect prevent react router from navigate and show
// a confirmation message. It's a work around to beforeunload
// because react router does not triggers that event.
useEffect( () => {
if ( hasUnsavedChanges ) {
const push = navigator.push;

navigator.push = ( ...args: Parameters< typeof push > ) => {
/* eslint-disable-next-line no-alert */
const result = window.confirm( confirmMessage );
if ( result !== false ) {
push( ...args );
}
};

return () => {
navigator.push = push;
};
}
}, [ navigator, hasUnsavedChanges, confirmMessage ] );

// This effect listen to the native beforeunload event to show
// a confirmation message
useEffect( () => {
if ( hasUnsavedChanges ) {
function onBeforeUnload( event: BeforeUnloadEvent ) {
event.preventDefault();
return ( event.returnValue = confirmMessage );
}

window.addEventListener( 'beforeunload', onBeforeUnload, {
capture: true,
} );

return () => {
window.removeEventListener( 'beforeunload', onBeforeUnload, {
capture: true,
} );
};
}
}, [ hasUnsavedChanges, confirmMessage ] );
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { recordEvent } from '@woocommerce/tracks';
/**
* Internal dependencies
*/
import usePreventLeavingPage from '~/hooks/usePreventLeavingPage';
import { WooHeaderItem } from '~/header/utils';
import { useProductHelper } from './use-product-helper';
import './product-form-actions.scss';
Expand All @@ -35,6 +36,8 @@ export const ProductFormActions: React.FC = () => {
const { isDirty, isValidForm, values, resetForm } =
useFormContext< Product >();

usePreventLeavingPage( isDirty );

const getProductDataForTracks = () => {
return {
product_id: values.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jest.mock( '../use-product-helper', () => {
} ),
};
} );
jest.mock( '~/hooks/usePreventLeavingPage' );

describe( 'ProductFormActions', () => {
beforeEach( () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: enhancement

Add a confirmation modal when the user tries to navigate away with unsaved changes

0 comments on commit dba6d33

Please sign in to comment.