Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix - CES Prompts Showing Several Times #1564

Merged
merged 12 commits into from
Jun 16, 2022
6 changes: 6 additions & 0 deletions js/src/components/customer-effort-score-prompt/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { recordEvent } from '@woocommerce/tracks';
*/
import { LOCAL_STORAGE_KEYS } from '.~/constants';
import localStorage from '.~/utils/localStorage';
import useEffectRemoveNotice from '.~/hooks/useEffectRemoveNotice';

/**
* CES prompt snackbar open
Expand Down Expand Up @@ -47,6 +48,11 @@ import localStorage from '.~/utils/localStorage';
* @return {JSX.Element} Rendered element.
*/
const CustomerEffortScorePrompt = ( { eventContext, label } ) => {
// NOTE: Currently CES Prompts uses core/notices2 as a store key, this seems something temporal
// and probably will be needed to change back to core/notices.
// See: https://github.com/woocommerce/woocommerce/blob/6.6.0/packages/js/notices/src/store/index.js
useEffectRemoveNotice( label, 'core/notices2' );
eason9487 marked this conversation as resolved.
Show resolved Hide resolved

const removeCESPromptFlagFromLocal = () => {
localStorage.remove(
LOCAL_STORAGE_KEYS.CAN_ONBOARDING_SETUP_CES_PROMPT_OPEN
Expand Down
1 change: 1 addition & 0 deletions js/src/data/constants.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export const STORE_KEY = 'wc/gla';
export const API_NAMESPACE = '/wc/gla';
export const NOTICES_STORE_KEY = 'core/notices';
38 changes: 38 additions & 0 deletions js/src/hooks/useEffectRemoveNotice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* External dependencies
*/
import { useEffect } from '@wordpress/element';
import { dispatch } from '@wordpress/data';

/**
* Internal dependencies
*/
import useNotices from '.~/hooks/useNotices';
import { NOTICES_STORE_KEY } from '.~/data/constants';

/**
* Search for a notice with specific label and remove it if the component is unmounted.
*
* @param {string} label the notice label
* @param {string} [storeKey] the store
*
* @return {import('@wordpress/notices').Notice|null} The notice to be removed otherwise null if it is not found
*/
const useEffectRemoveNotice = ( label, storeKey = NOTICES_STORE_KEY ) => {
const notices = useNotices( storeKey );
const notice = notices.find( ( el ) => el.content === label );

useEffect( () => {
const { removeNotice } = dispatch( storeKey );

return () => {
if ( notice ) {
removeNotice( notice.id );
}
};
}, [ notice, storeKey ] );

return notice || null;
};

export default useEffectRemoveNotice;
76 changes: 76 additions & 0 deletions js/src/hooks/useEffectRemoveNotice.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* External dependencies
*/
import { renderHook } from '@testing-library/react-hooks';

/**
* Internal dependencies
*/
import useEffectRemoveNotice from './useEffectRemoveNotice';
import useNotices from '.~/hooks/useNotices';
import { NOTICES_STORE_KEY } from '.~/data/constants';

const mockRemoveNotice = jest.fn();

jest.mock( '.~/hooks/useNotices', () => {
return jest.fn();
} );

jest.mock( '@wordpress/data', () => {
return {
dispatch: jest.fn().mockImplementation( () => ( {
removeNotice: mockRemoveNotice,
} ) ),
};
} );

describe( 'useEffectRemoveNotice', () => {
const testLabel = 'test_label';
const notice = {
id: 1,
content: testLabel,
};

beforeEach( () => {
jest.clearAllMocks();
useNotices.mockImplementation( () => [ notice ] );
} );

test( 'Should return with the notice', () => {
const { result } = renderHook( () =>
useEffectRemoveNotice( testLabel )
);

expect( mockRemoveNotice ).toHaveBeenCalledTimes( 0 );
expect( useNotices ).toHaveBeenCalledWith( NOTICES_STORE_KEY );

expect( result.current ).toEqual( notice );
} );

test( 'Should return with null if the notice is not found', () => {
useNotices.mockImplementation( () => [
{ ...notice, content: 'different_labels' },
] );

const { result } = renderHook( () =>
useEffectRemoveNotice( testLabel )
);

expect( mockRemoveNotice ).toHaveBeenCalledTimes( 0 );
expect( useNotices ).toHaveBeenCalledWith( NOTICES_STORE_KEY );

expect( result.current ).toEqual( null );
} );

test( 'Should remove notice when the hook is unmounted', () => {
const { result, unmount } = renderHook( () =>
useEffectRemoveNotice( testLabel )
);

unmount();

expect( mockRemoveNotice ).toHaveBeenCalledTimes( 1 );
expect( mockRemoveNotice ).toHaveBeenCalledWith( notice.id );
expect( result.current ).toEqual( notice );
} );
} );
32 changes: 32 additions & 0 deletions js/src/hooks/useNotices.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* External dependencies
*/
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import { NOTICES_STORE_KEY } from '.~/data/constants';

/**
* @typedef {import('@wordpress/notices').Notice} Notice
*/

/**
* A hook that returns the WP notices
*
* @param {string} [storeKey] The store key
*
* @return {Array<Notice>} Returns the Notices
*/
const useNotices = ( storeKey = NOTICES_STORE_KEY ) => {
return useSelect(
( select ) => {
const selector = select( storeKey );
return selector.getNotices();
},
[ storeKey ]
);
};

export default useNotices;