Skip to content

Commit 942bf46

Browse files
feat: add space efficient dashboard bar design and dashboard selection, and keyboard navigation
Implements JIRA issue DHIS2-18441 according to the design specs --- ### Key features 1. New design 2. New navigation menu 3. New keyboard navigation --- ### Description Implements the new design according to the design doc. Some other changes are also included in this PR and these are listed below. #### Changes to `ViewDashboard` - Use the `useAlert` hook instead of rendering a `AlertStack`. This ensures the show/hide animation runs and that there won't be problems when we show this alert together with another alert - The logic to load a dashboard has been simplified, I suspect this component still contained some logic that predates the inclusion of react-router. In any case, there was some logic present that accommodated "loading a new dashboard". But this scenario would never occur, because when the route changes the component is remounted. - Instead of having a `getContent` function, we now have the `ViewDashboardContent` component. #### Changes to `CacheableViewDashboard` A `useEffect` hook was added to ensure that the selected dashboard is cleared from the redux store when the user chooses "Close dashboard" in the "action menu" dropdown. #### Changes to `FilterBadge` The design was updated and some modifications were done to the behaviour as well: - When online on a large screen it is possible to edit and/or remove a filter - When online on a small screen (width =< 480px) it is not possible to edit a filter, but it can be removed. When attempting to edit a filter a tooltip shows to inform the user this is not possible. - When offline filters cannot be edited or deleted. The filter-badge is still visible but the remove-icon is not rendered. When attempting to edit a filter a tooltip shows to inform the user this is not possible. #### Major version upgrade of `@dhis2/ui` (v10) Version 10 includes some improvements in terms of accessibility and keyboard navigation. To comply with the new guidelines in that regard a few small tweaks to components were needed. This involved adding things like `ariaLabel` props, and addressing some invalid DOM nesting (nested `<a>` tags). #### Some tweaks to the unit-testing configuration - React testing-library was configured to work with the `data-test` attribute we use at DHIS2 (i.e. the `dataTest` prop for components from `@dhis2/ui`). The default data-attribute used by React testing-library is `data-test-id`, and some tests needed to be adjusted to no longer use that. - Some ESLint rules were added specifically for `spec.js` to remove the need to add ESLint ignore comments when making mock-components and other types of mocks. --- ### Known issues - Keyboard navigation inconsistencies: this PR includes a major version bump in `@dhis2/ui` which comes with some accessibility improvements including arrow-key keyboard navigation for menus. The navigation- and more-actions-menu in the new dashboards-bar are using these. But the filter-menu is using a custom menu-list and this can be used by keyboard by using the tab-key. This inconsistency will be tackled in a separate PR as described in JIRA issue DHIS2-18537. --- ### References Current JIRA Issue DHIS2-18441: https://dhis2.atlassian.net/browse/DHIS2-18441 Follow-up JIRA issue DHIS2-18537: https://dhis2.atlassian.net/browse/DHIS2-18537 Design specs: https://docs.google.com/document/d/1c8Ll1aLbFYwU8HYsyDlH1wk_QKTrUXwNanCiqayczOM
1 parent c3959c5 commit 942bf46

File tree

145 files changed

+2297
-3722
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

145 files changed

+2297
-3722
lines changed

.eslintrc.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,15 @@ const { config } = require('@dhis2/cli-style')
22

33
module.exports = {
44
extends: [config.eslintReact, 'plugin:cypress/recommended'],
5+
overrides: [
6+
{
7+
files: ['src/**/*.spec.js'],
8+
rules: {
9+
'react/prop-types': 'off',
10+
'react/display-name': 'off',
11+
'react/no-unknown-property': 'off',
12+
'no-unused-vars': ['error', { ignoreRestSiblings: true }],
13+
},
14+
},
15+
],
516
}

config/testSetup.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { configure } from '@testing-library/dom'
2+
import '@testing-library/jest-dom'
3+
4+
configure({
5+
testIdAttribute: 'data-test',
6+
})

cypress/e2e/common/add_a_FILTERTYPE_filter.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const OU_ID = 'ImspTQPwCqd' //Sierra Leone
1111
const FACILITY_TYPE = 'Clinic'
1212

1313
When('I add a {string} filter', (dimensionType) => {
14-
cy.contains('Add filter').click()
14+
cy.containsExact('Filter').click()
1515

1616
// select an item in the modal
1717
switch (dimensionType) {

cypress/e2e/common/click_on_the_FILTERTYPE_filter_badge.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@ import { When } from '@badeball/cypress-cucumber-preprocessor'
22
import { filterBadgeSel } from '../../elements/dashboardFilter.js'
33

44
When('I click on the {string} filter badge', (filterName) => {
5-
cy.get(filterBadgeSel).find('span:visible').contains(filterName).click()
5+
cy.get(filterBadgeSel)
6+
.find('button')
7+
.contains(filterName)
8+
.click({ force: true })
69
})
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { When } from '@badeball/cypress-cucumber-preprocessor'
2-
import { clickViewActionButton } from '../../elements/viewDashboard.js'
32

43
When('I click to preview the print layout', () => {
5-
clickViewActionButton('More')
4+
cy.get('[data-test="more-actions-button"]').click()
65
cy.get('[data-test="print-menu-item"]').click()
76
cy.get('[data-test="print-layout-menu-item"]').click()
87
})

cypress/e2e/common/open_the_SL_dashboard.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
import { Given } from '@badeball/cypress-cucumber-preprocessor'
22
import { dashboards } from '../../assets/backends/index.js'
33
// import { gridItemSel, chartSel } from '../../elements/dashboardItem.js'
4-
import {
5-
dashboardTitleSel,
6-
dashboardChipSel,
7-
} from '../../elements/viewDashboard.js'
8-
import { EXTENDED_TIMEOUT } from '../../support/utils.js'
4+
import { getNavigationMenuItem } from '../../elements/navigationMenu.js'
5+
import { dashboardTitleSel } from '../../elements/viewDashboard.js'
96

107
Given('I open the {string} dashboard', (title) => {
11-
cy.get(dashboardChipSel, EXTENDED_TIMEOUT).contains(title).click()
8+
getNavigationMenuItem(title).click()
129

1310
cy.location().should((loc) => {
1411
expect(loc.hash).to.equal(dashboards[title].route)

cypress/e2e/dashboard_filter/create_dashboard.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
22
import { gridItemSel } from '../../elements/dashboardItem.js'
3-
import {
4-
dashboardChipSel,
5-
dashboardTitleSel,
6-
} from '../../elements/viewDashboard.js'
3+
import { getNavigationMenuItem } from '../../elements/navigationMenu.js'
4+
import { dashboardTitleSel } from '../../elements/viewDashboard.js'
75
import {
86
EXTENDED_TIMEOUT,
97
createDashboardTitle,
@@ -79,9 +77,7 @@ When('I add items and save', () => {
7977
})
8078

8179
Given('I open an existing dashboard', () => {
82-
cy.get(dashboardChipSel, EXTENDED_TIMEOUT)
83-
.contains(TEST_DASHBOARD_TITLE)
84-
.click()
80+
getNavigationMenuItem(TEST_DASHBOARD_TITLE).click()
8581
})
8682

8783
// Some map visualization load very slowly:

cypress/e2e/dashboard_filter/dashboard_filter.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Then, When } from '@badeball/cypress-cucumber-preprocessor'
22
import {
33
filterBadgeSel,
44
dimensionsModalSel,
5+
filterBadgeDeleteBtnSel,
56
} from '../../elements/dashboardFilter.js'
67
// import {
78
// gridItemSel,
@@ -128,7 +129,7 @@ Then('the filter modal is opened', () => {
128129
})
129130

130131
When('I remove the {string} filter', () => {
131-
cy.get(filterBadgeSel).find('button').contains('Remove').click()
132+
cy.get(filterBadgeDeleteBtnSel).click()
132133
})
133134

134135
Then('the filter is removed from the dashboard', () => {

cypress/e2e/edit_dashboard/edit_dashboard.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ import {
99
titleInputSel,
1010
clickEditActionButton,
1111
} from '../../elements/editDashboard.js'
12+
import { getNavigationMenuItem } from '../../elements/navigationMenu.js'
1213
import {
13-
dashboardChipSel,
1414
dashboardTitleSel,
15+
dashboardsNavMenuButtonSel,
1516
} from '../../elements/viewDashboard.js'
1617
import { EXTENDED_TIMEOUT, createDashboardTitle } from '../../support/utils.js'
1718

@@ -79,9 +80,8 @@ Then('different valid dashboard displays in view mode', () => {
7980
})
8081

8182
Given('I open existing dashboard', () => {
82-
cy.get(dashboardChipSel, EXTENDED_TIMEOUT)
83-
.contains(TEST_DASHBOARD_TITLE)
84-
.click()
83+
cy.get(dashboardsNavMenuButtonSel, EXTENDED_TIMEOUT).click()
84+
cy.get('[role="menu"]').find('li').contains(TEST_DASHBOARD_TITLE).click()
8585

8686
cy.location().should((loc) => {
8787
const currentRoute = getRouteFromHash(loc.hash)
@@ -124,8 +124,7 @@ Scenario: I delete a dashboard
124124
*/
125125

126126
Then('the dashboard is deleted and first starred dashboard displayed', () => {
127-
cy.get(dashboardChipSel).contains(TEST_DASHBOARD_TITLE).should('not.exist')
128-
127+
getNavigationMenuItem(TEST_DASHBOARD_TITLE).should('not.exist')
129128
cy.get(dashboardTitleSel).should('exist').should('not.be.empty')
130129
})
131130

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
11
import { When, Then } from '@badeball/cypress-cucumber-preprocessor'
22
import {
3-
starSel,
3+
getNavigationMenuItem,
4+
closeNavigationMenu,
5+
} from '../../elements/navigationMenu.js'
6+
import {
47
dashboardStarredSel,
58
dashboardUnstarredSel,
6-
dashboardChipSel,
7-
chipStarSel,
9+
navMenuItemStarIconSel,
810
} from '../../elements/viewDashboard.js'
911
import { TEST_DASHBOARD_TITLE } from './edit_dashboard.js'
1012

1113
// Scenario: I star the dashboard
1214
When('I click to star the dashboard', () => {
1315
cy.intercept('POST', '**/favorite').as('starDashboard')
1416

15-
cy.get(starSel).click()
17+
cy.get(dashboardUnstarredSel).click()
1618
cy.wait('@starDashboard').its('response.statusCode').should('eq', 200)
1719
})
1820

1921
When('I click to unstar the dashboard', () => {
2022
cy.intercept('DELETE', '**/favorite').as('unstarDashboard')
2123

22-
cy.get(starSel).click()
24+
cy.get(dashboardStarredSel).click()
2325
cy.wait('@unstarDashboard').its('response.statusCode').should('eq', 200)
2426
})
2527

@@ -28,22 +30,21 @@ Then('the dashboard is starred', () => {
2830
cy.get(dashboardStarredSel).should('be.visible')
2931
cy.get(dashboardUnstarredSel).should('not.exist')
3032

31-
cy.get(dashboardChipSel)
32-
.contains(TEST_DASHBOARD_TITLE)
33-
.parent()
34-
.siblings(chipStarSel)
35-
.first()
33+
getNavigationMenuItem(TEST_DASHBOARD_TITLE)
34+
.find(navMenuItemStarIconSel)
3635
.should('be.visible')
36+
37+
closeNavigationMenu()
3738
})
3839

3940
Then('the dashboard is not starred', () => {
4041
// check for the unfilled star next to the title
4142
cy.get(dashboardUnstarredSel).should('be.visible')
4243
cy.get(dashboardStarredSel).should('not.exist')
4344

44-
cy.get(dashboardChipSel)
45-
.contains(TEST_DASHBOARD_TITLE)
46-
.parent()
47-
.siblings()
45+
getNavigationMenuItem(TEST_DASHBOARD_TITLE)
46+
.find(navMenuItemStarIconSel)
4847
.should('not.exist')
48+
49+
closeNavigationMenu()
4950
})

0 commit comments

Comments
 (0)