Move usePreventLeavingPage to navigation package (#36752)

* Move usePreventLeavingPage to navigation package

* Rename usePreventLeavingPage to useConfirmUnsavedChanges

* Move imports to external dependencies

* Fix up history push overrides

* Add changelog entries

* Fix up rebase issues

* Update i18n dependency to wp-6.0

* Fix up lock file

* Remove unused imports

* Update lock file

* Remove self import for navUtils
This commit is contained in:
Joshua T Flowers 2023-03-16 01:33:22 -07:00 committed by GitHub
parent 1c9b3a58fe
commit 77937dd8cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1012 additions and 1442 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Add hook to check unsaved form changes before page navigation

View File

@ -31,6 +31,7 @@
"@wordpress/compose": "wp-6.0",
"@wordpress/element": "wp-6.0",
"@wordpress/hooks": "wp-6.0",
"@wordpress/i18n": "wp-6.0",
"@wordpress/notices": "wp-6.0",
"@wordpress/url": "wp-6.0",
"history": "^5.3.0",

View File

@ -1,16 +1,17 @@
/**
* External dependencies
*/
import { useContext, useEffect, useMemo } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { parseAdminUrl } from '@woocommerce/navigation';
import {
Location,
UNSAFE_NavigationContext as NavigationContext,
useLocation,
} from 'react-router-dom';
import { Location } from 'react-router-dom';
import { useEffect, useMemo } from '@wordpress/element';
export default function usePreventLeavingPage(
/**
* Internal dependencies
*/
import { getHistory } from '../history';
import { parseAdminUrl } from '../';
export const useConfirmUnsavedChanges = (
hasUnsavedChanges: boolean,
shouldConfirm?: ( path: URL, fromUrl: Location ) => boolean,
/**
@ -19,24 +20,24 @@ export default function usePreventLeavingPage(
* @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 );
const fromUrl = useLocation();
const history = getHistory();
// 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;
const push = history.push;
navigator.push = ( ...args: Parameters< typeof push > ) => {
history.push = ( ...args: Parameters< typeof push > ) => {
const fromUrl = history.location;
const toUrl = parseAdminUrl( args[ 0 ] ) as URL;
if (
typeof shouldConfirm === 'function' &&
@ -54,10 +55,10 @@ export default function usePreventLeavingPage(
};
return () => {
navigator.push = push;
history.push = push;
};
}
}, [ navigator, hasUnsavedChanges, confirmMessage ] );
}, [ history, hasUnsavedChanges, confirmMessage ] );
// This effect listen to the native beforeunload event to show
// a confirmation message
@ -79,4 +80,4 @@ export default function usePreventLeavingPage(
};
}
}, [ hasUnsavedChanges, confirmMessage ] );
}
};

View File

@ -18,9 +18,6 @@ import { getAdminLink } from '@woocommerce/settings';
* Internal dependencies
*/
import { getHistory } from './history';
import * as navUtils from './index';
// For the above, import the module into itself. Functions consumed from this import can be mocked in tests.
// Expose history so all uses get the same history object.
export { getHistory };
@ -28,6 +25,9 @@ export { getHistory };
// Export all filter utilities
export * from './filters';
// Export all hooks
export { useConfirmUnsavedChanges } from './hooks/use-confirm-unsaved-changes';
const TIME_EXCLUDED_SCREENS_FILTER = 'woocommerce_admin_time_excluded_screens';
/**
@ -79,7 +79,7 @@ export function getNewPath(
* @param {Object} query Query containing the parameters.
* @return {Object} Object containing the persisted queries.
*/
export const getPersistedQuery = ( query = navUtils.getQuery() ) => {
export const getPersistedQuery = ( query = getQuery() ) => {
/**
* Filter persisted queries. These query parameters remain in the url when other parameters are updated.
*
@ -226,7 +226,7 @@ export function getIdsFromQuery( queryString = '' ) {
* @param {Object} query Query object.
* @return {Array} List of search words.
*/
export function getSearchWords( query = navUtils.getQuery() ) {
export function getSearchWords( query = getQuery() ) {
if ( typeof query !== 'object' ) {
throw new Error(
'Invalid parameter passed to getSearchWords, it expects an object or no parameters.'

View File

@ -20,7 +20,7 @@ import {
} from '@woocommerce/product-editor';
import { Product } from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks';
import { navigateTo } from '@woocommerce/navigation';
import { navigateTo, useConfirmUnsavedChanges } from '@woocommerce/navigation';
import { useSelect } from '@wordpress/data';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore No types for this exist yet.
@ -30,7 +30,6 @@ import { store } from '@wordpress/viewport';
/**
* Internal dependencies
*/
import usePreventLeavingPage from '~/hooks/usePreventLeavingPage';
import './product-form-actions.scss';
import { useProductMVPCESFooter } from '~/customer-effort-score-tracks/use-product-mvp-ces-footer';
@ -50,7 +49,7 @@ export const ProductFormActions: React.FC = () => {
const { isDirty, isValidForm, values, resetForm } =
useFormContext< Product >();
usePreventLeavingPage( isDirty, preventLeavingProductForm );
useConfirmUnsavedChanges( isDirty, preventLeavingProductForm );
useCustomerEffortScoreExitPageTracker(
! values.id ? 'new_product' : 'editing_new_product',

View File

@ -11,6 +11,7 @@ import {
import { preventLeavingProductForm } from '@woocommerce/product-editor';
import { registerPlugin } from '@wordpress/plugins';
import { useDispatch } from '@wordpress/data';
import { useConfirmUnsavedChanges } from '@woocommerce/navigation';
import { useFormContext } from '@woocommerce/components';
import { useParams } from 'react-router-dom';
import { useState } from '@wordpress/element';
@ -18,7 +19,6 @@ import { useState } from '@wordpress/element';
/**
* Internal dependencies
*/
import usePreventLeavingPage from '~/hooks/usePreventLeavingPage';
import './product-form-actions.scss';
export const ProductVariationFormActions: React.FC = () => {
@ -31,7 +31,7 @@ export const ProductVariationFormActions: React.FC = () => {
const { createNotice } = useDispatch( 'core/notices' );
const [ isSaving, setIsSaving ] = useState( false );
usePreventLeavingPage( isDirty, preventLeavingProductForm );
useConfirmUnsavedChanges( isDirty, preventLeavingProductForm );
const onSave = async () => {
setIsSaving( true );

View File

@ -53,7 +53,10 @@ jest.mock( '@woocommerce/product-editor', () => {
} ),
};
} );
jest.mock( '~/hooks/usePreventLeavingPage' );
jest.mock( '@woocommerce/navigation', () => ( {
...jest.requireActual( '@woocommerce/navigation' ),
useConfirmUnsavedChanges: jest.fn(),
} ) );
jest.mock( '@woocommerce/customer-effort-score', () => ( {
useCustomerEffortScoreExitPageTracker: jest.fn(),
} ) );

View File

@ -0,0 +1,4 @@
Significance: minor
Type: dev
Move hook to confirm unsaved form changes to navigation package

File diff suppressed because it is too large Load Diff