From b0c1e4d754807df758a760d66a859941ef033511 Mon Sep 17 00:00:00 2001 From: Raymond Zhou <56318341+rayzhou-bit@users.noreply.github.com> Date: Tue, 7 Mar 2023 10:27:55 -0800 Subject: [PATCH] feat: clear save failed status when closing error (#266) --- .../__snapshots__/index.test.jsx.snap | 1 - .../components/EditorFooter/index.jsx | 11 ++++---- .../containers/EditorContainer/hooks.js | 10 +++++--- .../containers/EditorContainer/hooks.test.jsx | 3 +++ .../containers/EditorContainer/index.jsx | 7 +++--- .../containers/EditorContainer/index.test.jsx | 7 +++++- src/editors/hooks.js | 7 +++++- src/editors/hooks.test.jsx | 25 +++++++++++++------ 8 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/editors/containers/EditorContainer/components/EditorFooter/__snapshots__/index.test.jsx.snap b/src/editors/containers/EditorContainer/components/EditorFooter/__snapshots__/index.test.jsx.snap index e6c47e92d..5cf3fdc4d 100644 --- a/src/editors/containers/EditorContainer/components/EditorFooter/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/EditorContainer/components/EditorFooter/__snapshots__/index.test.jsx.snap @@ -112,7 +112,6 @@ exports[`EditorFooter render snapshot: save failed. Show error message 1`] = ` className="editor-footer fixed-bottom" > {saveFailed && ( - - - + + + )} @@ -69,6 +69,7 @@ export const EditorFooter = ({ ); }; EditorFooter.propTypes = { + clearSaveFailed: PropTypes.func.isRequired, disableSave: PropTypes.bool.isRequired, onCancel: PropTypes.func.isRequired, onSave: PropTypes.func.isRequired, diff --git a/src/editors/containers/EditorContainer/hooks.js b/src/editors/containers/EditorContainer/hooks.js index aa5466560..ce378ea6d 100644 --- a/src/editors/containers/EditorContainer/hooks.js +++ b/src/editors/containers/EditorContainer/hooks.js @@ -1,14 +1,15 @@ +import { useState } from 'react'; import { useSelector } from 'react-redux'; -import { useState } from 'react'; +import analyticsEvt from '../../data/constants/analyticsEvt'; import { RequestKeys } from '../../data/constants/requests'; import { selectors } from '../../data/redux'; +import { StrictDict } from '../../utils'; import * as appHooks from '../../hooks'; import * as module from './hooks'; -import analyticsEvt from '../../data/constants/analyticsEvt'; -import { StrictDict } from '../../utils'; export const { + clearSaveError, navigateCallback, nullMethod, saveBlock, @@ -30,6 +31,7 @@ export const handleSaveClicked = ({ dispatch, getContent, validateEntry }) => { validateEntry, }); }; + export const cancelConfirmModalToggle = () => { const [isCancelConfirmOpen, setIsOpen] = module.state.isCancelConfirmModalOpen(false); return { @@ -49,7 +51,9 @@ export const handleCancel = ({ onClose }) => { analytics: useSelector(selectors.app.analytics), }); }; + export const isInitialized = () => useSelector(selectors.app.isInitialized); + export const saveFailed = () => useSelector((rootState) => ( selectors.requests.isFailed(rootState, { requestKey: RequestKeys.saveBlock }) )); diff --git a/src/editors/containers/EditorContainer/hooks.test.jsx b/src/editors/containers/EditorContainer/hooks.test.jsx index 01b9412a7..c2e4d4649 100644 --- a/src/editors/containers/EditorContainer/hooks.test.jsx +++ b/src/editors/containers/EditorContainer/hooks.test.jsx @@ -30,6 +30,9 @@ jest.mock('../../hooks', () => ({ const dispatch = jest.fn(); describe('EditorContainer hooks', () => { describe('forwarded hooks', () => { + it('forwards clearSaveError from app hooks', () => { + expect(hooks.clearSaveError).toEqual(appHooks.clearSaveError); + }); it('forwards navigateCallback from app hooks', () => { expect(hooks.navigateCallback).toEqual(appHooks.navigateCallback); }); diff --git a/src/editors/containers/EditorContainer/index.jsx b/src/editors/containers/EditorContainer/index.jsx index 9cc96572d..69986d327 100644 --- a/src/editors/containers/EditorContainer/index.jsx +++ b/src/editors/containers/EditorContainer/index.jsx @@ -6,12 +6,12 @@ import { Icon, ModalDialog, IconButton, Button, } from '@edx/paragon'; import { Close } from '@edx/paragon/icons'; - import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n'; + +import BaseModal from '../../sharedComponents/BaseModal'; import EditorFooter from './components/EditorFooter'; import TitleHeader from './components/TitleHeader'; import * as hooks from './hooks'; -import BaseModal from '../../sharedComponents/BaseModal'; import messages from './messages'; export const EditorContainer = ({ @@ -65,9 +65,10 @@ export const EditorContainer = ({ {isInitialized && children} diff --git a/src/editors/containers/EditorContainer/index.test.jsx b/src/editors/containers/EditorContainer/index.test.jsx index bbf4eaaee..d4bdbb8da 100644 --- a/src/editors/containers/EditorContainer/index.test.jsx +++ b/src/editors/containers/EditorContainer/index.test.jsx @@ -14,6 +14,7 @@ const props = { }; jest.mock('./hooks', () => ({ + clearSaveError: jest.fn().mockName('hooks.clearSaveError'), isInitialized: jest.fn().mockReturnValue(true), handleCancel: (args) => ({ handleCancel: args }), handleSaveClicked: (args) => ({ handleSaveClicked: args }), @@ -45,7 +46,6 @@ describe('EditorContainer component', () => { beforeEach(() => { el = shallow({testContent}); }); - test('save behavior is linked to footer onSave', () => { const expected = hooks.handleSaveClicked({ dispatch: useDispatch(), @@ -55,6 +55,11 @@ describe('EditorContainer component', () => { expect(el.children().at(3) .props().onSave).toEqual(expected); }); + test('behavior is linked to clearSaveError', () => { + const expected = hooks.clearSaveError({ dispatch: useDispatch() }); + expect(el.children().at(3) + .props().clearSaveFailed).toEqual(expected); + }); }); }); }); diff --git a/src/editors/hooks.js b/src/editors/hooks.js index 834e9cb48..93d927224 100644 --- a/src/editors/hooks.js +++ b/src/editors/hooks.js @@ -3,8 +3,9 @@ import { useEffect } from 'react'; import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import analyticsEvt from './data/constants/analyticsEvt'; -import { thunkActions } from './data/redux'; +import { actions, thunkActions } from './data/redux'; import * as module from './hooks'; +import { RequestKeys } from './data/constants/requests'; export const initializeApp = ({ dispatch, data }) => useEffect( () => dispatch(thunkActions.app.initialize(data)), @@ -54,3 +55,7 @@ export const saveBlock = ({ })); } }; + +export const clearSaveError = ({ + dispatch, +}) => () => dispatch(actions.requests.clearRequest({ requestKey: RequestKeys.saveBlock })); diff --git a/src/editors/hooks.test.jsx b/src/editors/hooks.test.jsx index 15d64ffb1..0098b9afb 100644 --- a/src/editors/hooks.test.jsx +++ b/src/editors/hooks.test.jsx @@ -1,10 +1,11 @@ import { useEffect } from 'react'; import { sendTrackEvent } from '@edx/frontend-platform/analytics'; -import analyticsEvt from './data/constants/analyticsEvt'; +import analyticsEvt from './data/constants/analyticsEvt'; +import { RequestKeys } from './data/constants/requests'; +import { actions, thunkActions } from './data/redux'; import { keyStore } from './utils'; -import { thunkActions } from './data/redux'; import * as hooks from './hooks'; jest.mock('react', () => ({ @@ -15,17 +16,17 @@ jest.mock('react', () => ({ })); jest.mock('./data/redux', () => ({ + actions: { + requests: { + clearRequest: (args) => ({ clearRequest: args }), + }, + }, thunkActions: { app: { initialize: (args) => ({ initializeApp: args }), saveBlock: (args) => ({ saveBlock: args }), }, }, - selectors: { - app: { - returnUrl: jest.fn(), - }, - }, })); jest.mock('@edx/frontend-platform/analytics', () => ({ @@ -131,4 +132,14 @@ describe('hooks', () => { })); }); }); + + describe('clearSaveError', () => { + it('dispatches actions.requests.clearRequest with saveBlock requestKey', () => { + const dispatch = jest.fn(); + hooks.clearSaveError({ dispatch })(); + expect(dispatch).toHaveBeenCalledWith(actions.requests.clearRequest({ + requestKey: RequestKeys.saveBlock, + })); + }); + }); });