From 90765ba34446cd0d183473ff356de9c680a0ad3e Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Thu, 22 Apr 2021 09:49:10 +0200 Subject: [PATCH 1/5] feat: add confirm dialog for discarding changes to dashboard --- i18n/en.pot | 24 ++++-- src/pages/edit/ActionsBar.js | 52 ++++++++---- src/pages/edit/ConfirmActionDialog.js | 79 +++++++++++++++++++ src/pages/edit/ConfirmDeleteDialog.js | 67 ---------------- ...ule.css => ConfirmActionDialog.module.css} | 0 src/reducers/editDashboard.js | 16 +++- yarn.lock | 11 --- 7 files changed, 151 insertions(+), 98 deletions(-) create mode 100644 src/pages/edit/ConfirmActionDialog.js delete mode 100644 src/pages/edit/ConfirmDeleteDialog.js rename src/pages/edit/styles/{ConfirmDeleteDialog.module.css => ConfirmActionDialog.module.css} (100%) diff --git a/i18n/en.pot b/i18n/en.pot index b3e80469f..469a76d1a 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2021-04-16T11:52:58.307Z\n" -"PO-Revision-Date: 2021-04-16T11:52:58.307Z\n" +"POT-Creation-Date: 2021-04-22T07:41:15.148Z\n" +"PO-Revision-Date: 2021-04-22T07:41:15.148Z\n" msgid "Untitled dashboard" msgstr "" @@ -156,9 +156,6 @@ msgstr "" msgid "Go to dashboards" msgstr "" -msgid "Cancel" -msgstr "" - msgid "Delete dashboard" msgstr "" @@ -168,6 +165,23 @@ msgid "" "this dashboard?" msgstr "" +msgid "Cancel" +msgstr "" + +msgid "Discard changes" +msgstr "" + +msgid "" +"This dashboard has unsaved changes. Are you sure you want to leave and " +"discard these unsaved changes?" +msgstr "" + +msgid "No, stay here" +msgstr "" + +msgid "Yes, discard changes" +msgstr "" + msgid "No access" msgstr "" diff --git a/src/pages/edit/ActionsBar.js b/src/pages/edit/ActionsBar.js index c267a02b6..0f3aeb921 100644 --- a/src/pages/edit/ActionsBar.js +++ b/src/pages/edit/ActionsBar.js @@ -9,7 +9,10 @@ import { useDataEngine, useAlert } from '@dhis2/app-runtime' import { useD2 } from '@dhis2/app-runtime-adapter-d2' import FilterSettingsDialog from './FilterSettingsDialog' -import ConfirmDeleteDialog from './ConfirmDeleteDialog' +import ConfirmActionDialog, { + ACTION_DELETE, + ACTION_DISCARD, +} from './ConfirmActionDialog' import { tSaveDashboard, acClearEditDashboard, @@ -24,6 +27,7 @@ import { sGetEditDashboardRoot, sGetIsNewDashboard, sGetIsPrintPreviewView, + sGetEditIsDirty, } from '../../reducers/editDashboard' import { apiFetchDashboard } from '../../api/fetchDashboard' import { EDIT } from '../../modules/dashboardModes' @@ -47,6 +51,10 @@ const EditBar = props => { ) const [dashboard, setDashboard] = useState(undefined) const [confirmDeleteDlgIsOpen, setConfirmDeleteDlgIsOpen] = useState(false) + const [confirmDiscardDlgIsOpen, setConfirmDiscardDlgIsOpen] = useState( + false + ) + const [redirectUrl, setRedirectUrl] = useState(undefined) const saveFailureAlert = useAlert(saveFailedMessage, { @@ -101,7 +109,15 @@ const EditBar = props => { } } - const onDiscard = () => { + const onConfirmDiscard = () => { + if (props.isDirty) { + setConfirmDiscardDlgIsOpen(true) + } else { + onDiscardConfirmed() + } + } + + const onDiscardConfirmed = () => { props.onDiscardChanges() const redirectUrl = props.dashboardId ? `/${props.dashboardId}` : '/' @@ -109,7 +125,9 @@ const EditBar = props => { } const onContinueEditing = () => { + console.log('onContinueEditing') setConfirmDeleteDlgIsOpen(false) + setConfirmDiscardDlgIsOpen(false) } const onFilterSettingsConfirmed = ( @@ -133,16 +151,6 @@ const EditBar = props => { setFilterSettingsDlgIsOpen(!filterSettingsDlgIsOpen) } - const confirmDeleteDialog = () => - props.deleteAccess && props.dashboardId ? ( - - ) : null - const translationDialog = () => dashboard && dashboard.id ? ( {
{props.updateAccess ? renderActionButtons() : null} -
{filterSettingsDialog()} {translationDialog()} - {confirmDeleteDialog()} + {props.deleteAccess && props.dashboardId && ( + + )} + ) } @@ -236,6 +258,7 @@ EditBar.propTypes = { dashboardName: PropTypes.string, deleteAccess: PropTypes.bool, fetchDashboards: PropTypes.func, + isDirty: PropTypes.bool, isPrintPreviewView: PropTypes.bool, newDashboard: PropTypes.bool, restrictFilters: PropTypes.bool, @@ -270,6 +293,7 @@ const mapStateToProps = state => { restrictFilters: dashboard.restrictFilters, isPrintPreviewView: sGetIsPrintPreviewView(state), updateAccess, + isDirty: sGetEditIsDirty(state), } } diff --git a/src/pages/edit/ConfirmActionDialog.js b/src/pages/edit/ConfirmActionDialog.js new file mode 100644 index 000000000..f1e6aa05e --- /dev/null +++ b/src/pages/edit/ConfirmActionDialog.js @@ -0,0 +1,79 @@ +import React from 'react' +import PropTypes from 'prop-types' +import i18n from '@dhis2/d2-i18n' +import { + Button, + Modal, + ModalContent, + ModalActions, + ButtonStrip, + ModalTitle, +} from '@dhis2/ui' + +import classes from './styles/ConfirmActionDialog.module.css' + +export const ACTION_DELETE = 'delete' +export const ACTION_DISCARD = 'discard' + +export const ConfirmActionDialog = ({ + action, + dashboardName, + onConfirm, + onCancel, + open, +}) => { + const texts = { + [ACTION_DELETE]: { + title: i18n.t('Delete dashboard'), + message: i18n.t( + 'Deleting dashboard "{{ dashboardName }}" will remove it for all users. This action cannot be undone. Are you sure you want to permanently delete this dashboard?', + { dashboardName } + ), + cancel: i18n.t('Cancel'), + confirm: i18n.t('Delete'), + }, + [ACTION_DISCARD]: { + title: i18n.t('Discard changes'), + message: i18n.t( + 'This dashboard has unsaved changes. Are you sure you want to leave and discard these unsaved changes?' + ), + cancel: i18n.t('No, stay here'), + confirm: i18n.t('Yes, discard changes'), + }, + } + + const actions = [ + , + , + ] + + return ( + open && ( + + {texts[action].title} + + + {texts[action].message} + + + + {actions} + + + ) + ) +} + +ConfirmActionDialog.propTypes = { + action: PropTypes.string, + dashboardName: PropTypes.string, + open: PropTypes.bool, + onCancel: PropTypes.func, + onConfirm: PropTypes.func, +} + +export default ConfirmActionDialog diff --git a/src/pages/edit/ConfirmDeleteDialog.js b/src/pages/edit/ConfirmDeleteDialog.js deleted file mode 100644 index eec4463ba..000000000 --- a/src/pages/edit/ConfirmDeleteDialog.js +++ /dev/null @@ -1,67 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import i18n from '@dhis2/d2-i18n' -import { - Button, - Modal, - ModalContent, - ModalActions, - ButtonStrip, - ModalTitle, -} from '@dhis2/ui' - -import classes from './styles/ConfirmDeleteDialog.module.css' - -export const ConfirmDeleteDialog = ({ - dashboardName, - onDeleteConfirmed, - onContinueEditing, - open, -}) => { - const actions = [ - , - , - ] - - return ( - open && ( - - {i18n.t('Delete dashboard')} - - - {i18n.t( - 'Deleting dashboard "{{ dashboardName }}" will remove it for all users. This action cannot be undone. Are you sure you want to permanently delete this dashboard?', - { dashboardName } - )} - - - - {actions} - - - ) - ) -} - -ConfirmDeleteDialog.propTypes = { - dashboardName: PropTypes.string, - open: PropTypes.bool, - onContinueEditing: PropTypes.func, - onDeleteConfirmed: PropTypes.func, -} - -export default ConfirmDeleteDialog diff --git a/src/pages/edit/styles/ConfirmDeleteDialog.module.css b/src/pages/edit/styles/ConfirmActionDialog.module.css similarity index 100% rename from src/pages/edit/styles/ConfirmDeleteDialog.module.css rename to src/pages/edit/styles/ConfirmActionDialog.module.css diff --git a/src/reducers/editDashboard.js b/src/reducers/editDashboard.js index a3b1c7e92..b3446612e 100644 --- a/src/reducers/editDashboard.js +++ b/src/reducers/editDashboard.js @@ -26,6 +26,7 @@ export const NEW_DASHBOARD_STATE = { dashboardItems: [], restrictFilters: false, printPreviewView: false, + isDirty: false, } export default (state = DEFAULT_STATE_EDIT_DASHBOARD, action) => { @@ -36,6 +37,7 @@ export default (state = DEFAULT_STATE_EDIT_DASHBOARD, action) => { k => (newState[k] = action.value[k]) ) newState.printPreviewView = false + newState.isDirty = false return newState } case RECEIVED_NOT_EDITING: @@ -47,17 +49,22 @@ export default (state = DEFAULT_STATE_EDIT_DASHBOARD, action) => { case START_NEW_DASHBOARD: return NEW_DASHBOARD_STATE case RECEIVED_TITLE: { - return Object.assign({}, state, { name: action.value }) + return Object.assign({}, state, { + name: action.value, + isDirty: true, + }) } case RECEIVED_DESCRIPTION: { return Object.assign({}, state, { description: action.value, + isDirty: true, }) } case ADD_DASHBOARD_ITEM: if (!action.value.position) { return update(state, { dashboardItems: { $unshift: [action.value] }, + isDirty: true, }) } @@ -67,6 +74,7 @@ export default (state = DEFAULT_STATE_EDIT_DASHBOARD, action) => { [parseInt(action.value.position), 0, action.value], ], }, + isDirty: true, }) case REMOVE_DASHBOARD_ITEM: { @@ -81,6 +89,7 @@ export default (state = DEFAULT_STATE_EDIT_DASHBOARD, action) => { dashboardItems: { $splice: [[dashboardItemIndex, 1]], }, + isDirty: true, }) } @@ -112,6 +121,7 @@ export default (state = DEFAULT_STATE_EDIT_DASHBOARD, action) => { ? { ...state, dashboardItems: newStateItems, + isDirty: true, } : state } @@ -133,6 +143,7 @@ export default (state = DEFAULT_STATE_EDIT_DASHBOARD, action) => { ], ], }, + isDirty: true, }) return newState @@ -144,6 +155,7 @@ export default (state = DEFAULT_STATE_EDIT_DASHBOARD, action) => { return Object.assign({}, state, { allowedFilters: action.value.allowedFilters, restrictFilters: action.value.restrictFilters, + isDirty: true, }) } default: @@ -174,3 +186,5 @@ export const sGetEditDashboardDescription = state => export const sGetEditDashboardItems = state => orObject(sGetEditDashboardRoot(state)).dashboardItems + +export const sGetEditIsDirty = state => sGetEditDashboardRoot(state).isDirty diff --git a/yarn.lock b/yarn.lock index ebf838a3e..842d752c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1764,17 +1764,6 @@ material-ui "^0.20.0" rxjs "^5.5.7" -"@dhis2/d2-ui-core@7.1.6": - version "7.1.6" - resolved "https://registry.yarnpkg.com/@dhis2/d2-ui-core/-/d2-ui-core-7.1.6.tgz#2bc65f86f1cbf0e04ca17accfb164968e5fcb7d4" - integrity sha512-zF5Xnts5NLmGkPJHR3QI4H9KLruHSp3JPAmR2lxNtturSpwEzecnauydo/Og76wcscd4V3ylEmIY2d5Ey2o9OA== - dependencies: - babel-runtime "^6.26.0" - d2 "~31.7" - lodash "^4.17.10" - material-ui "^0.20.0" - rxjs "^5.5.7" - "@dhis2/d2-ui-core@7.1.8", "@dhis2/d2-ui-core@^7.1.8": version "7.1.8" resolved "https://registry.yarnpkg.com/@dhis2/d2-ui-core/-/d2-ui-core-7.1.8.tgz#1339f91f4ce2e0a38897dab29de22db725d058a5" From 161ca3dd1cb56bdf2b03208801f0279b9d4fc849 Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Thu, 22 Apr 2021 10:33:25 +0200 Subject: [PATCH 2/5] test: update jest tests --- src/pages/edit/ConfirmActionDialog.js | 2 +- src/pages/edit/__tests__/ActionsBar.spec.js | 6 +- .../__tests__/ConfirmActionDialog.spec.js | 56 +++++++++ .../__tests__/ConfirmDeleteDialog.spec.js | 70 ----------- .../__snapshots__/ActionsBar.spec.js.snap | 3 +- .../ConfirmActionDialog.spec.js.snap | 113 ++++++++++++++++++ .../ConfirmDeleteDialog.spec.js.snap | 53 -------- 7 files changed, 175 insertions(+), 128 deletions(-) create mode 100644 src/pages/edit/__tests__/ConfirmActionDialog.spec.js delete mode 100644 src/pages/edit/__tests__/ConfirmDeleteDialog.spec.js create mode 100644 src/pages/edit/__tests__/__snapshots__/ConfirmActionDialog.spec.js.snap delete mode 100644 src/pages/edit/__tests__/__snapshots__/ConfirmDeleteDialog.spec.js.snap diff --git a/src/pages/edit/ConfirmActionDialog.js b/src/pages/edit/ConfirmActionDialog.js index f1e6aa05e..b5cb5558e 100644 --- a/src/pages/edit/ConfirmActionDialog.js +++ b/src/pages/edit/ConfirmActionDialog.js @@ -15,7 +15,7 @@ import classes from './styles/ConfirmActionDialog.module.css' export const ACTION_DELETE = 'delete' export const ACTION_DISCARD = 'discard' -export const ConfirmActionDialog = ({ +const ConfirmActionDialog = ({ action, dashboardName, onConfirm, diff --git a/src/pages/edit/__tests__/ActionsBar.spec.js b/src/pages/edit/__tests__/ActionsBar.spec.js index 4ec847938..85f1f6c2a 100644 --- a/src/pages/edit/__tests__/ActionsBar.spec.js +++ b/src/pages/edit/__tests__/ActionsBar.spec.js @@ -36,10 +36,10 @@ jest.mock( /* eslint-disable react/prop-types */ jest.mock( - '../ConfirmDeleteDialog', + '../ConfirmActionDialog', () => - function MockConfirmDeleteDialog({ open }) { - return open ?
: null + function MockConfirmActionDialog({ open }) { + return open ?
: null } ) /* eslint-enable react/prop-types */ diff --git a/src/pages/edit/__tests__/ConfirmActionDialog.spec.js b/src/pages/edit/__tests__/ConfirmActionDialog.spec.js new file mode 100644 index 000000000..b2ea1d7ad --- /dev/null +++ b/src/pages/edit/__tests__/ConfirmActionDialog.spec.js @@ -0,0 +1,56 @@ +/* eslint-disable react/prop-types */ +import React from 'react' +import { render } from '@testing-library/react' +import ConfirmActionDialog, { + ACTION_DELETE, + ACTION_DISCARD, +} from '../ConfirmActionDialog' + +jest.mock('@dhis2/ui', () => { + const originalModule = jest.requireActual('@dhis2/ui') + + return { + __esModule: true, + ...originalModule, + Modal: function MockComponent(props) { + return
{props.children}
+ }, + } +}) + +test('ConfirmActionDialog renders confirm delete dialog', () => { + const { container } = render( + + ) + expect(container).toMatchSnapshot() +}) + +test('ConfirmActionDialog renders discard changes dialog', () => { + const { container } = render( + + ) + expect(container).toMatchSnapshot() +}) + +test('ConfirmActionDialog does not render dialog if not open', () => { + const { container } = render( + + ) + expect(container).toMatchSnapshot() +}) diff --git a/src/pages/edit/__tests__/ConfirmDeleteDialog.spec.js b/src/pages/edit/__tests__/ConfirmDeleteDialog.spec.js deleted file mode 100644 index 3b23dd738..000000000 --- a/src/pages/edit/__tests__/ConfirmDeleteDialog.spec.js +++ /dev/null @@ -1,70 +0,0 @@ -import React from 'react' -import { shallow } from 'enzyme' -import toJson from 'enzyme-to-json' -import { ButtonStrip } from '@dhis2/ui' -import { ConfirmDeleteDialog } from '../ConfirmDeleteDialog' - -describe('ConfirmDeleteDialog', () => { - let props - let shallowDialog - const dialog = () => { - if (!shallowDialog) { - shallowDialog = shallow() - } - return shallowDialog - } - - beforeEach(() => { - props = { - dashboardName: '', - onDeleteConfirmed: jest.fn(), - onContinueEditing: jest.fn(), - open: false, - } - shallowDialog = undefined - }) - - it('matches the snapshot when open = false', () => { - expect(toJson(dialog())).toMatchSnapshot() - }) - - describe('when open = true', () => { - beforeEach(() => { - props.open = true - }) - - it('matches the snapshot', () => { - expect(toJson(dialog())).toMatchSnapshot() - }) - - it('renders a Button with action onContinueEditing', () => { - props.open = true - expect.assertions(1) - dialog() - .find(ButtonStrip) - .children() - .forEach(actionEl => { - if (actionEl.key() === 'cancel') { - expect(actionEl.prop('onClick')).toBe( - props.onContinueEditing - ) - } - }) - }) - - it('renders a Button with action onDeleteConfirmed', () => { - props.open = true - expect.assertions(1) - dialog() - .find(ButtonStrip) - .children() - .forEach(actionEl => { - if (actionEl.key() === 'delete') { - expect(actionEl.prop('onClick')).toBe( - props.onDeleteConfirmed - ) - } - }) - }) - }) -}) diff --git a/src/pages/edit/__tests__/__snapshots__/ActionsBar.spec.js.snap b/src/pages/edit/__tests__/__snapshots__/ActionsBar.spec.js.snap index d8de1976f..26531d2b8 100644 --- a/src/pages/edit/__tests__/__snapshots__/ActionsBar.spec.js.snap +++ b/src/pages/edit/__tests__/__snapshots__/ActionsBar.spec.js.snap @@ -65,6 +65,7 @@ exports[`renders Save and Discard buttons but no dialogs when no dashboard id 1`
+
`; @@ -322,7 +323,7 @@ exports[`shows the confirm delete dialog when delete button clicked 1`] = `
`; diff --git a/src/pages/edit/__tests__/__snapshots__/ConfirmActionDialog.spec.js.snap b/src/pages/edit/__tests__/__snapshots__/ConfirmActionDialog.spec.js.snap new file mode 100644 index 000000000..b0a67b60c --- /dev/null +++ b/src/pages/edit/__tests__/__snapshots__/ConfirmActionDialog.spec.js.snap @@ -0,0 +1,113 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ConfirmActionDialog does not render dialog if not open 1`] = `
`; + +exports[`ConfirmActionDialog renders confirm delete dialog 1`] = ` +
+
+

+ Delete dashboard +

+
+ + Deleting dashboard "Twilight Sparkle" will remove it for all users. This action cannot be undone. Are you sure you want to permanently delete this dashboard? + +
+
+
+
+ +
+
+ +
+
+
+
+
+`; + +exports[`ConfirmActionDialog renders discard changes dialog 1`] = ` +
+
+

+ Discard changes +

+
+ + This dashboard has unsaved changes. Are you sure you want to leave and discard these unsaved changes? + +
+
+
+
+ +
+
+ +
+
+
+
+
+`; diff --git a/src/pages/edit/__tests__/__snapshots__/ConfirmDeleteDialog.spec.js.snap b/src/pages/edit/__tests__/__snapshots__/ConfirmDeleteDialog.spec.js.snap deleted file mode 100644 index 3165a9973..000000000 --- a/src/pages/edit/__tests__/__snapshots__/ConfirmDeleteDialog.spec.js.snap +++ /dev/null @@ -1,53 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ConfirmDeleteDialog matches the snapshot when open = false 1`] = `""`; - -exports[`ConfirmDeleteDialog when open = true matches the snapshot 1`] = ` - - - Delete dashboard - - - - Deleting dashboard "" will remove it for all users. This action cannot be undone. Are you sure you want to permanently delete this dashboard? - - - - - - - - - -`; From 130545ea114443bb43a4a9835e9770dab900a102 Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Thu, 22 Apr 2021 13:06:10 +0200 Subject: [PATCH 3/5] test: add cypress coverage --- cypress/integration/ui/edit_dashboard.feature | 45 +++++++++++++++++-- .../ui/edit_dashboard/edit_dashboard.js | 36 ++++++++++++--- cypress/selectors/editDashboard.js | 3 ++ src/pages/edit/ConfirmActionDialog.js | 7 ++- src/reducers/editDashboard.js | 8 ++-- 5 files changed, 84 insertions(+), 15 deletions(-) create mode 100644 cypress/selectors/editDashboard.js diff --git a/cypress/integration/ui/edit_dashboard.feature b/cypress/integration/ui/edit_dashboard.feature index eb0fa6065..0895d765b 100644 --- a/cypress/integration/ui/edit_dashboard.feature +++ b/cypress/integration/ui/edit_dashboard.feature @@ -7,13 +7,52 @@ Feature: Creating, editing and deleting dashboard And dashboard items are added And I click outside menu And dashboard is saved - Then dashboard displays in view mode + Then the dashboard displays in view mode And the saved dashboard should be displayed + @mutating + Scenario: I exit without saving when no changes + Given I open existing dashboard + Then the dashboard displays in view mode + When I choose to edit dashboard + And I click Exit without saving + Then the dashboard displays in view mode + + @mutating + Scenario: I cancel exit without saving when name changed + Given I open existing dashboard + Then the dashboard displays in view mode + When I choose to edit dashboard + And dashboard title is added + And I click Exit without saving + And I decide to continue editing + Then the dashboard displays in edit mode + + @mutating + Scenario: I cancel exit without saving when item added + Given I open existing dashboard + Then the dashboard displays in view mode + When I choose to edit dashboard + And dashboard items are added + And I click outside menu + And I click Exit without saving + And I decide to continue editing + Then the dashboard displays in edit mode + + @mutating + Scenario: I exit without saving when name changed + Given I open existing dashboard + Then the dashboard displays in view mode + When I choose to edit dashboard + And dashboard title is added + And I click Exit without saving + And I confirm I want to discard changes + Then the dashboard displays in view mode + @mutating Scenario: I star the dashboard Given I open existing dashboard - Then dashboard displays in view mode + Then the dashboard displays in view mode And the dashboard is not starred When I click to star the dashboard Then the dashboard is starred @@ -25,7 +64,7 @@ Feature: Creating, editing and deleting dashboard Scenario: I toggle show description Given I open existing dashboard # And the description is not shown - Then dashboard displays in view mode + Then the dashboard displays in view mode And the dashboard description is not displayed When I click to show description Then the dashboard description is displayed diff --git a/cypress/integration/ui/edit_dashboard/edit_dashboard.js b/cypress/integration/ui/edit_dashboard/edit_dashboard.js index af4436f37..c28a67f8b 100644 --- a/cypress/integration/ui/edit_dashboard/edit_dashboard.js +++ b/cypress/integration/ui/edit_dashboard/edit_dashboard.js @@ -9,6 +9,11 @@ import { dashboardChipSel, dashboardTitleSel, } from '../../../selectors/viewDashboard' +import { + confirmActionDialogSel, + titleInputSel, + itemMenuSel, +} from '../../../selectors/editDashboard' // the length of the root route of the app (after the slash): #/ const ROOT_ROUTE_LENGTH = 0 @@ -38,17 +43,17 @@ Scenario: I create a new dashboard */ When('dashboard title is added', () => { - cy.get('[data-test="dashboard-title-input"]').type(TEST_DASHBOARD_TITLE) + cy.get(titleInputSel).type(TEST_DASHBOARD_TITLE) }) When('escape key is pressed', () => { cy.get('body').trigger('keydown', { key: 'Escape' }) - cy.get('[data-test="item-menu]').should('not.exist') + cy.get(itemMenuSel).should('not.exist') }) When('I click outside menu', () => { cy.get('[data-test="dhis2-uicore-layer"]').click('topLeft') - cy.get('[data-test="item-menu]').should('not.exist') + cy.get(itemMenuSel).should('not.exist') }) When('dashboard is saved', () => { @@ -62,7 +67,7 @@ Then('the saved dashboard should be displayed', () => { ) }) -Then('dashboard displays in view mode', () => { +Then('the dashboard displays in view mode', () => { cy.location().should(loc => { const currentRoute = getRouteFromHash(loc.hash) @@ -96,12 +101,29 @@ When('I choose to delete dashboard', () => { cy.get('[data-test="delete-dashboard-button"]').click() }) +/* +Scenario: I exit without saving +*/ +When('I confirm I want to discard changes', () => { + cy.get(confirmActionDialogSel) + .find('button') + .contains('Yes, discard changes') + .click() +}) + +When('I decide to continue editing', () => { + cy.get(confirmActionDialogSel) + .find('button') + .contains('No, stay here') + .click() +}) + /* Scenario: I cancel a delete dashboard action */ When('I cancel delete', () => { - cy.get('[data-test="cancel-delete-dashboard"]').click() + cy.get(confirmActionDialogSel).find('button').contains('Cancel').click() }) Then('the confirm delete dialog is displayed', () => { @@ -111,7 +133,7 @@ Then('the confirm delete dialog is displayed', () => { }) Then('the dashboard displays in edit mode', () => { - cy.get('[data-test="dashboard-title-input"]').should('exist') + cy.get(titleInputSel).should('exist') cy.location().should(loc => { expect(getRouteFromHash(loc.hash)).to.eq(ROUTE_EDIT) @@ -123,7 +145,7 @@ Scenario: I delete a dashboard */ When('I confirm delete', () => { - cy.get('[data-test="confirm-delete-dashboard"]').click() + cy.get(confirmActionDialogSel).find('button').contains('Delete').click() }) Then('the dashboard is deleted and first starred dashboard displayed', () => { diff --git a/cypress/selectors/editDashboard.js b/cypress/selectors/editDashboard.js new file mode 100644 index 000000000..79dd5f8ec --- /dev/null +++ b/cypress/selectors/editDashboard.js @@ -0,0 +1,3 @@ +export const confirmActionDialogSel = '[data-test="confirm-action-dialog"]' +export const titleInputSel = '[data-test="dashboard-title-input"]' +export const itemMenuSel = '[data-test="item-menu]' diff --git a/src/pages/edit/ConfirmActionDialog.js b/src/pages/edit/ConfirmActionDialog.js index b5cb5558e..386681079 100644 --- a/src/pages/edit/ConfirmActionDialog.js +++ b/src/pages/edit/ConfirmActionDialog.js @@ -53,7 +53,12 @@ const ConfirmActionDialog = ({ return ( open && ( - + {texts[action].title} diff --git a/src/reducers/editDashboard.js b/src/reducers/editDashboard.js index b3446612e..eab78e780 100644 --- a/src/reducers/editDashboard.js +++ b/src/reducers/editDashboard.js @@ -64,7 +64,7 @@ export default (state = DEFAULT_STATE_EDIT_DASHBOARD, action) => { if (!action.value.position) { return update(state, { dashboardItems: { $unshift: [action.value] }, - isDirty: true, + isDirty: { $set: true }, }) } @@ -74,7 +74,7 @@ export default (state = DEFAULT_STATE_EDIT_DASHBOARD, action) => { [parseInt(action.value.position), 0, action.value], ], }, - isDirty: true, + isDirty: { $set: true }, }) case REMOVE_DASHBOARD_ITEM: { @@ -89,7 +89,7 @@ export default (state = DEFAULT_STATE_EDIT_DASHBOARD, action) => { dashboardItems: { $splice: [[dashboardItemIndex, 1]], }, - isDirty: true, + isDirty: { $set: true }, }) } @@ -143,7 +143,7 @@ export default (state = DEFAULT_STATE_EDIT_DASHBOARD, action) => { ], ], }, - isDirty: true, + isDirty: { $set: true }, }) return newState From fbf8048522cb5e518767caeba520fbaeb2a2fa08 Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Thu, 22 Apr 2021 13:16:08 +0200 Subject: [PATCH 4/5] fix: cleanup --- src/pages/edit/ActionsBar.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/edit/ActionsBar.js b/src/pages/edit/ActionsBar.js index 0f3aeb921..b59bd04ce 100644 --- a/src/pages/edit/ActionsBar.js +++ b/src/pages/edit/ActionsBar.js @@ -79,7 +79,7 @@ const EditBar = props => { setConfirmDeleteDlgIsOpen(true) } - const deleteDashboardConfirmed = () => { + const onDeleteConfirmed = () => { setConfirmDeleteDlgIsOpen(false) dataEngine @@ -125,7 +125,6 @@ const EditBar = props => { } const onContinueEditing = () => { - console.log('onContinueEditing') setConfirmDeleteDlgIsOpen(false) setConfirmDiscardDlgIsOpen(false) } @@ -235,7 +234,7 @@ const EditBar = props => { From 9570c39a91ad188116d333f5ffe92f65a8aaa154 Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Mon, 26 Apr 2021 10:31:44 +0200 Subject: [PATCH 5/5] fix: cypress test element selector --- cypress/integration/ui/edit_dashboard/edit_dashboard.js | 2 +- cypress/integration/ui/error_scenarios/error_during_delete.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cypress/integration/ui/edit_dashboard/edit_dashboard.js b/cypress/integration/ui/edit_dashboard/edit_dashboard.js index c28a67f8b..de58a7d6f 100644 --- a/cypress/integration/ui/edit_dashboard/edit_dashboard.js +++ b/cypress/integration/ui/edit_dashboard/edit_dashboard.js @@ -98,7 +98,7 @@ Given('I open existing dashboard', () => { }) When('I choose to delete dashboard', () => { - cy.get('[data-test="delete-dashboard-button"]').click() + cy.get('button').contains('Delete').click() }) /* diff --git a/cypress/integration/ui/error_scenarios/error_during_delete.js b/cypress/integration/ui/error_scenarios/error_during_delete.js index 8703bf545..09750a95a 100644 --- a/cypress/integration/ui/error_scenarios/error_during_delete.js +++ b/cypress/integration/ui/error_scenarios/error_during_delete.js @@ -1,7 +1,8 @@ import { When } from 'cypress-cucumber-preprocessor/steps' +import { confirmActionDialogSel } from '../../../selectors/editDashboard' When('A 500 error is thrown when I delete the dashboard', () => { cy.intercept('DELETE', '/dashboards', { statusCode: 500 }) cy.get('button').contains('Delete').click() - cy.get('[data-test="confirm-delete-dashboard"]').click() + cy.get(confirmActionDialogSel).find('button').contains('Delete').click() })