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

feat: add confirmation dialog for discarding changes to dashboard #1713

Merged
merged 7 commits into from
Apr 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 42 additions & 3 deletions cypress/integration/ui/edit_dashboard.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
38 changes: 30 additions & 8 deletions cypress/integration/ui/edit_dashboard/edit_dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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', () => {
Expand All @@ -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)

Expand Down Expand Up @@ -93,15 +98,32 @@ 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()
})

/*
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', () => {
Expand All @@ -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)
Expand All @@ -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', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -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()
})
3 changes: 3 additions & 0 deletions cypress/selectors/editDashboard.js
Original file line number Diff line number Diff line change
@@ -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]'
20 changes: 17 additions & 3 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,6 @@ msgstr ""
msgid "Go to dashboards"
msgstr ""

msgid "Cancel"
msgstr ""

msgid "Delete dashboard"
msgstr ""

Expand All @@ -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 ""

Expand Down
53 changes: 38 additions & 15 deletions src/pages/edit/ActionsBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -24,6 +27,7 @@ import {
sGetEditDashboardRoot,
sGetIsNewDashboard,
sGetIsPrintPreviewView,
sGetEditIsDirty,
} from '../../reducers/editDashboard'
import { apiFetchDashboard } from '../../api/fetchDashboard'
import { EDIT } from '../../modules/dashboardModes'
Expand All @@ -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, {
Expand All @@ -71,7 +79,7 @@ const EditBar = props => {
setConfirmDeleteDlgIsOpen(true)
}

const deleteDashboardConfirmed = () => {
const onDeleteConfirmed = () => {
setConfirmDeleteDlgIsOpen(false)

dataEngine
Expand Down Expand Up @@ -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}` : '/'

Expand All @@ -110,6 +126,7 @@ const EditBar = props => {

const onContinueEditing = () => {
setConfirmDeleteDlgIsOpen(false)
setConfirmDiscardDlgIsOpen(false)
}

const onFilterSettingsConfirmed = (
Expand All @@ -133,16 +150,6 @@ const EditBar = props => {
setFilterSettingsDlgIsOpen(!filterSettingsDlgIsOpen)
}

const confirmDeleteDialog = () =>
props.deleteAccess && props.dashboardId ? (
<ConfirmDeleteDialog
dashboardName={props.dashboardName}
onDeleteConfirmed={deleteDashboardConfirmed}
onContinueEditing={onContinueEditing}
open={confirmDeleteDlgIsOpen}
/>
) : null

const translationDialog = () =>
dashboard && dashboard.id ? (
<TranslationDialog
Expand Down Expand Up @@ -216,14 +223,28 @@ const EditBar = props => {
<div className={classes.editBar} data-test="edit-control-bar">
<div className={classes.controls}>
{props.updateAccess ? renderActionButtons() : null}
<Button secondary onClick={onDiscard}>
<Button secondary onClick={onConfirmDiscard}>
{discardBtnText}
</Button>
</div>
</div>
{filterSettingsDialog()}
{translationDialog()}
{confirmDeleteDialog()}
{props.deleteAccess && props.dashboardId && (
<ConfirmActionDialog
action={ACTION_DELETE}
dashboardName={props.dashboardName}
onConfirm={onDeleteConfirmed}
onCancel={onContinueEditing}
open={confirmDeleteDlgIsOpen}
/>
)}
<ConfirmActionDialog
action={ACTION_DISCARD}
onConfirm={onDiscardConfirmed}
onCancel={onContinueEditing}
open={confirmDiscardDlgIsOpen}
/>
</>
)
}
Expand All @@ -236,6 +257,7 @@ EditBar.propTypes = {
dashboardName: PropTypes.string,
deleteAccess: PropTypes.bool,
fetchDashboards: PropTypes.func,
isDirty: PropTypes.bool,
isPrintPreviewView: PropTypes.bool,
newDashboard: PropTypes.bool,
restrictFilters: PropTypes.bool,
Expand Down Expand Up @@ -270,6 +292,7 @@ const mapStateToProps = state => {
restrictFilters: dashboard.restrictFilters,
isPrintPreviewView: sGetIsPrintPreviewView(state),
updateAccess,
isDirty: sGetEditIsDirty(state),
}
}

Expand Down
Loading