diff --git a/.eslintrc.js b/.eslintrc.js
index 74ea743b2..26a5001e9 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -2,4 +2,15 @@ const { config } = require('@dhis2/cli-style')
module.exports = {
extends: [config.eslintReact, 'plugin:cypress/recommended'],
+ overrides: [
+ {
+ files: ['src/**/*.spec.js'],
+ rules: {
+ 'react/prop-types': 'off',
+ 'react/display-name': 'off',
+ 'react/no-unknown-property': 'off',
+ 'no-unused-vars': ['error', { ignoreRestSiblings: true }],
+ },
+ },
+ ],
}
diff --git a/.github/workflows/generate-and-upload-bom.yml b/.github/workflows/generate-and-upload-bom.yml
new file mode 100644
index 000000000..5c4dda9eb
--- /dev/null
+++ b/.github/workflows/generate-and-upload-bom.yml
@@ -0,0 +1,49 @@
+name: 'This workflow creates bill of material and uploads it to Dependency-Track each night'
+
+on:
+ schedule:
+ - cron: '0 0 * * *'
+ workflow_dispatch:
+
+concurrency:
+ group: ${{ github.workflow}}-${{ github.ref }}
+ cancel-in-progress: true
+
+defaults:
+ run:
+ shell: bash
+
+jobs:
+ create-bom:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 18.x
+
+ - name: Install
+ run: yarn install --frozen-lockfile
+
+ - name: Install CycloneDX CLI
+ run: |
+ curl -s https://api.github.com/repos/CycloneDX/cyclonedx-cli/releases/latest | grep "browser_download_url.*linux.x64" | cut -d '"' -f 4 | wget -i -
+ sudo mv cyclonedx-linux-x64 /usr/local/bin/
+ sudo chmod +x /usr/local/bin/cyclonedx-linux-x64
+
+ - name: Generate BOMs
+ run: |
+ npm install -g @cyclonedx/cdxgen
+ cdxgen -o sbom.json
+
+ - name: Upload SBOM to DependencyTrack
+ env:
+ DEPENDENCY_TRACK_API: 'https://dt.security.dhis2.org/api/v1/bom'
+ run: |
+ curl -X POST "$DEPENDENCY_TRACK_API" \
+ --fail-with-body \
+ -H "Content-Type: multipart/form-data" \
+ -H "X-Api-Key: ${{ secrets.DEPENDENCYTRACK_APIKEY }}" \
+ -F "project=c0bd0f2d-d512-460a-81f9-e256e4fb1054" \
+ -F "bom=@sbom.json"
diff --git a/cypress/e2e/common/add_a_FILTERTYPE_filter.js b/cypress/e2e/common/add_a_FILTERTYPE_filter.js
index 5e3e15bef..89ff1b6e0 100644
--- a/cypress/e2e/common/add_a_FILTERTYPE_filter.js
+++ b/cypress/e2e/common/add_a_FILTERTYPE_filter.js
@@ -11,7 +11,7 @@ const OU_ID = 'ImspTQPwCqd' //Sierra Leone
const FACILITY_TYPE = 'Clinic'
When('I add a {string} filter', (dimensionType) => {
- cy.contains('Add filter').click()
+ cy.containsExact('Filter').click()
// select an item in the modal
switch (dimensionType) {
diff --git a/cypress/e2e/common/click_on_the_FILTERTYPE_filter_badge.js b/cypress/e2e/common/click_on_the_FILTERTYPE_filter_badge.js
index 8b59fa991..52a929068 100644
--- a/cypress/e2e/common/click_on_the_FILTERTYPE_filter_badge.js
+++ b/cypress/e2e/common/click_on_the_FILTERTYPE_filter_badge.js
@@ -2,5 +2,8 @@ import { When } from '@badeball/cypress-cucumber-preprocessor'
import { filterBadgeSel } from '../../elements/dashboardFilter.js'
When('I click on the {string} filter badge', (filterName) => {
- cy.get(filterBadgeSel).find('span:visible').contains(filterName).click()
+ cy.get(filterBadgeSel)
+ .find('button')
+ .contains(filterName)
+ .click({ force: true })
})
diff --git a/cypress/e2e/common/open_print_layout.js b/cypress/e2e/common/open_print_layout.js
index ac013f180..b06116a37 100644
--- a/cypress/e2e/common/open_print_layout.js
+++ b/cypress/e2e/common/open_print_layout.js
@@ -1,8 +1,7 @@
import { When } from '@badeball/cypress-cucumber-preprocessor'
-import { clickViewActionButton } from '../../elements/viewDashboard.js'
When('I click to preview the print layout', () => {
- clickViewActionButton('More')
+ cy.get('[data-test="more-actions-button"]').click()
cy.get('[data-test="print-menu-item"]').click()
cy.get('[data-test="print-layout-menu-item"]').click()
})
diff --git a/cypress/e2e/common/open_the_SL_dashboard.js b/cypress/e2e/common/open_the_SL_dashboard.js
index 7c7ce15c0..ad7e30137 100644
--- a/cypress/e2e/common/open_the_SL_dashboard.js
+++ b/cypress/e2e/common/open_the_SL_dashboard.js
@@ -1,14 +1,11 @@
import { Given } from '@badeball/cypress-cucumber-preprocessor'
import { dashboards } from '../../assets/backends/index.js'
// import { gridItemSel, chartSel } from '../../elements/dashboardItem.js'
-import {
- dashboardTitleSel,
- dashboardChipSel,
-} from '../../elements/viewDashboard.js'
-import { EXTENDED_TIMEOUT } from '../../support/utils.js'
+import { getNavigationMenuItem } from '../../elements/navigationMenu.js'
+import { dashboardTitleSel } from '../../elements/viewDashboard.js'
Given('I open the {string} dashboard', (title) => {
- cy.get(dashboardChipSel, EXTENDED_TIMEOUT).contains(title).click()
+ getNavigationMenuItem(title).click()
cy.location().should((loc) => {
expect(loc.hash).to.equal(dashboards[title].route)
diff --git a/cypress/e2e/dashboard_filter/create_dashboard.js b/cypress/e2e/dashboard_filter/create_dashboard.js
index c4102b05c..c283a0262 100644
--- a/cypress/e2e/dashboard_filter/create_dashboard.js
+++ b/cypress/e2e/dashboard_filter/create_dashboard.js
@@ -1,9 +1,7 @@
import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
import { gridItemSel } from '../../elements/dashboardItem.js'
-import {
- dashboardChipSel,
- dashboardTitleSel,
-} from '../../elements/viewDashboard.js'
+import { getNavigationMenuItem } from '../../elements/navigationMenu.js'
+import { dashboardTitleSel } from '../../elements/viewDashboard.js'
import {
EXTENDED_TIMEOUT,
createDashboardTitle,
@@ -79,9 +77,7 @@ When('I add items and save', () => {
})
Given('I open an existing dashboard', () => {
- cy.get(dashboardChipSel, EXTENDED_TIMEOUT)
- .contains(TEST_DASHBOARD_TITLE)
- .click()
+ getNavigationMenuItem(TEST_DASHBOARD_TITLE).click()
})
// Some map visualization load very slowly:
diff --git a/cypress/e2e/dashboard_filter/dashboard_filter.js b/cypress/e2e/dashboard_filter/dashboard_filter.js
index 693f99d28..2521e9593 100644
--- a/cypress/e2e/dashboard_filter/dashboard_filter.js
+++ b/cypress/e2e/dashboard_filter/dashboard_filter.js
@@ -2,6 +2,7 @@ import { Then, When } from '@badeball/cypress-cucumber-preprocessor'
import {
filterBadgeSel,
dimensionsModalSel,
+ filterBadgeDeleteBtnSel,
} from '../../elements/dashboardFilter.js'
// import {
// gridItemSel,
@@ -128,7 +129,7 @@ Then('the filter modal is opened', () => {
})
When('I remove the {string} filter', () => {
- cy.get(filterBadgeSel).find('button').contains('Remove').click()
+ cy.get(filterBadgeDeleteBtnSel).click()
})
Then('the filter is removed from the dashboard', () => {
diff --git a/cypress/e2e/edit_dashboard/edit_dashboard.js b/cypress/e2e/edit_dashboard/edit_dashboard.js
index abf8b758f..3eeb428cb 100644
--- a/cypress/e2e/edit_dashboard/edit_dashboard.js
+++ b/cypress/e2e/edit_dashboard/edit_dashboard.js
@@ -9,9 +9,10 @@ import {
titleInputSel,
clickEditActionButton,
} from '../../elements/editDashboard.js'
+import { getNavigationMenuItem } from '../../elements/navigationMenu.js'
import {
- dashboardChipSel,
dashboardTitleSel,
+ dashboardsNavMenuButtonSel,
} from '../../elements/viewDashboard.js'
import { EXTENDED_TIMEOUT, createDashboardTitle } from '../../support/utils.js'
@@ -79,9 +80,8 @@ Then('different valid dashboard displays in view mode', () => {
})
Given('I open existing dashboard', () => {
- cy.get(dashboardChipSel, EXTENDED_TIMEOUT)
- .contains(TEST_DASHBOARD_TITLE)
- .click()
+ cy.get(dashboardsNavMenuButtonSel, EXTENDED_TIMEOUT).click()
+ cy.get('[role="menu"]').find('li').contains(TEST_DASHBOARD_TITLE).click()
cy.location().should((loc) => {
const currentRoute = getRouteFromHash(loc.hash)
@@ -124,8 +124,7 @@ Scenario: I delete a dashboard
*/
Then('the dashboard is deleted and first starred dashboard displayed', () => {
- cy.get(dashboardChipSel).contains(TEST_DASHBOARD_TITLE).should('not.exist')
-
+ getNavigationMenuItem(TEST_DASHBOARD_TITLE).should('not.exist')
cy.get(dashboardTitleSel).should('exist').should('not.be.empty')
})
diff --git a/cypress/e2e/edit_dashboard/star_dashboard.js b/cypress/e2e/edit_dashboard/star_dashboard.js
index 99abed3e6..a4f6cd6bf 100644
--- a/cypress/e2e/edit_dashboard/star_dashboard.js
+++ b/cypress/e2e/edit_dashboard/star_dashboard.js
@@ -1,10 +1,12 @@
import { When, Then } from '@badeball/cypress-cucumber-preprocessor'
import {
- starSel,
+ getNavigationMenuItem,
+ closeNavigationMenu,
+} from '../../elements/navigationMenu.js'
+import {
dashboardStarredSel,
dashboardUnstarredSel,
- dashboardChipSel,
- chipStarSel,
+ navMenuItemStarIconSel,
} from '../../elements/viewDashboard.js'
import { TEST_DASHBOARD_TITLE } from './edit_dashboard.js'
@@ -12,14 +14,14 @@ import { TEST_DASHBOARD_TITLE } from './edit_dashboard.js'
When('I click to star the dashboard', () => {
cy.intercept('POST', '**/favorite').as('starDashboard')
- cy.get(starSel).click()
+ cy.get(dashboardUnstarredSel).click()
cy.wait('@starDashboard').its('response.statusCode').should('eq', 200)
})
When('I click to unstar the dashboard', () => {
cy.intercept('DELETE', '**/favorite').as('unstarDashboard')
- cy.get(starSel).click()
+ cy.get(dashboardStarredSel).click()
cy.wait('@unstarDashboard').its('response.statusCode').should('eq', 200)
})
@@ -28,12 +30,11 @@ Then('the dashboard is starred', () => {
cy.get(dashboardStarredSel).should('be.visible')
cy.get(dashboardUnstarredSel).should('not.exist')
- cy.get(dashboardChipSel)
- .contains(TEST_DASHBOARD_TITLE)
- .parent()
- .siblings(chipStarSel)
- .first()
+ getNavigationMenuItem(TEST_DASHBOARD_TITLE)
+ .find(navMenuItemStarIconSel)
.should('be.visible')
+
+ closeNavigationMenu()
})
Then('the dashboard is not starred', () => {
@@ -41,9 +42,9 @@ Then('the dashboard is not starred', () => {
cy.get(dashboardUnstarredSel).should('be.visible')
cy.get(dashboardStarredSel).should('not.exist')
- cy.get(dashboardChipSel)
- .contains(TEST_DASHBOARD_TITLE)
- .parent()
- .siblings()
+ getNavigationMenuItem(TEST_DASHBOARD_TITLE)
+ .find(navMenuItemStarIconSel)
.should('not.exist')
+
+ closeNavigationMenu()
})
diff --git a/cypress/e2e/filter_restrict/filter_restrict.js b/cypress/e2e/filter_restrict/filter_restrict.js
index a262ba146..2dd028a5a 100644
--- a/cypress/e2e/filter_restrict/filter_restrict.js
+++ b/cypress/e2e/filter_restrict/filter_restrict.js
@@ -146,7 +146,7 @@ When('I save the dashboard', () => {
})
When('I click Add Filter', () => {
- clickViewActionButton('Add filter')
+ clickViewActionButton('Filter')
})
Then('I see Facility Ownership and no other dimensions', () => {
@@ -168,7 +168,7 @@ Scenario: I restrict filters to no dimensions and do not see Add Filter in dashb
*/
Then('Add Filter button is not visible', () => {
- cy.contains('Add filter').should('not.exist')
+ cy.containsExact('Filter').should('not.exist')
})
When('I delete the dashboard', () => {
diff --git a/cypress/e2e/offline/offline.js b/cypress/e2e/offline/offline.js
index 767bcefc0..93cd00bb2 100644
--- a/cypress/e2e/offline/offline.js
+++ b/cypress/e2e/offline/offline.js
@@ -212,7 +212,7 @@ Then(
// edit, sharing, starring, filtering, all options under more
getViewActionButton('Edit').should('be.disabled')
getViewActionButton('Share').should('be.disabled')
- getViewActionButton('Add filter').should('be.disabled')
+ getViewActionButton('Filter').should('be.disabled')
getViewActionButton('More').should('be.enabled')
checkCorrectMoreOptionsEnabledState(false, cacheState)
diff --git a/cypress/e2e/responsive_dashboard/responsive_dashboard.js b/cypress/e2e/responsive_dashboard/responsive_dashboard.js
index 7b8bf5b15..c63af7378 100644
--- a/cypress/e2e/responsive_dashboard/responsive_dashboard.js
+++ b/cypress/e2e/responsive_dashboard/responsive_dashboard.js
@@ -27,10 +27,7 @@ Then('the small screen view is shown', () => {
//titlebar - only the More button and the title
cy.get('button').contains('Edit').should('not.be.visible')
cy.get('button').contains('Share').should('not.be.visible')
- cy.get('button').contains('Add filter').should('not.be.visible')
-
- cy.get('button.small').contains('More').should('be.visible')
- cy.get('button').not('.small').contains('More').should('not.be.visible')
+ cy.get('button').contains('Filter').should('not.be.visible')
})
When('I restore the wide screen', () => {
@@ -44,10 +41,7 @@ Then('the wide screen view is shown', () => {
cy.get('button').contains('Edit').should('be.visible')
cy.get('button').contains('Share').should('be.visible')
- cy.get('button').contains('Add filter').should('be.visible')
-
- cy.get('button').not('.small').contains('More').should('be.visible')
- cy.get('button.small').contains('More').should('not.be.visible')
+ cy.get('button').contains('Filter').should('be.visible')
})
Then('the small screen edit view is shown', () => {
diff --git a/cypress/e2e/slideshow.feature b/cypress/e2e/slideshow.feature
new file mode 100644
index 000000000..ff2c4c2ec
--- /dev/null
+++ b/cypress/e2e/slideshow.feature
@@ -0,0 +1,31 @@
+Feature: Slideshow
+
+ Scenario: I view a dashboard in slideshow
+ Given I open the "Delivery" dashboard
+ When I click the slideshow button
+ Then item 1 is shown in fullscreen
+ When I click the next slide button
+ Then item 2 is shown in fullscreen
+ When I click the previous slide button
+ Then item 1 is shown in fullscreen
+ When I click the exit slideshow button
+ Then the normal view is shown
+
+
+ Scenario: I view fullscreen on the second item of the dashboard
+ Given I open the "Delivery" dashboard
+ When I click the fullscreen button on the second item
+ Then item 2 is shown in fullscreen
+ When I click the exit slideshow button
+ Then the normal view is shown
+
+ Scenario: I view fullscreen on the third item of the dashboard and navigate backwards
+ Given I open the "Delivery" dashboard
+ When I click the fullscreen button on the third item
+ Then item 3 is shown in fullscreen
+ When I click the previous slide button
+ Then item 2 is shown in fullscreen
+ When I click the previous slide button
+ Then item 1 is shown in fullscreen
+ When I click the exit slideshow button
+ Then the normal view is shown
diff --git a/cypress/e2e/slideshow/index.js b/cypress/e2e/slideshow/index.js
new file mode 100644
index 000000000..6a8a6a12a
--- /dev/null
+++ b/cypress/e2e/slideshow/index.js
@@ -0,0 +1 @@
+'../common/index.js'
diff --git a/cypress/e2e/slideshow/slideshow.js b/cypress/e2e/slideshow/slideshow.js
new file mode 100644
index 000000000..254dd3976
--- /dev/null
+++ b/cypress/e2e/slideshow/slideshow.js
@@ -0,0 +1,103 @@
+import { When, Then } from '@badeball/cypress-cucumber-preprocessor'
+import {
+ getDashboardItem,
+ clickMenuButton,
+} from '../../elements/dashboardItem.js'
+
+const sortedDashboardItemIds = ['GaVhJpqABYX', 'qXsjttMYuoZ', 'Rwb3oXJ3bZ9']
+
+const assertItemIsVisible = (slideshowItemIndex) => {
+ getDashboardItem(sortedDashboardItemIds[slideshowItemIndex]).should(
+ 'have.css',
+ 'opacity',
+ '1'
+ )
+}
+
+const assertItemIsNotVisible = (slideshowItemIndex) => {
+ getDashboardItem(sortedDashboardItemIds[slideshowItemIndex]).should(
+ 'have.css',
+ 'opacity',
+ '0'
+ )
+}
+
+const getSlideshowExitButton = () =>
+ cy.getByDataTest('slideshow-exit-button', { timeout: 15000 })
+
+When('I click the slideshow button', () => {
+ cy.get('button').contains('Slideshow').realClick()
+})
+
+Then('item 1 is shown in fullscreen', () => {
+ getSlideshowExitButton().should('be.visible')
+
+ // check that only the first item is shown
+ assertItemIsVisible(0)
+ assertItemIsNotVisible(1)
+ assertItemIsNotVisible(2)
+
+ cy.getByDataTest('slideshow-page-counter').should('have.text', '1 / 11')
+
+ // visible item does not have context menu button
+ getDashboardItem(sortedDashboardItemIds[0])
+ .findByDataTest('dashboarditem-menu-button')
+ .should('not.exist')
+})
+
+When('I click the exit slideshow button', () => {
+ getSlideshowExitButton().realClick()
+})
+
+Then('the normal view is shown', () => {
+ getSlideshowExitButton().should('not.exist')
+
+ // check that multiple items are shown
+ assertItemIsVisible(0)
+ assertItemIsVisible(1)
+ assertItemIsVisible(2)
+
+ // items have context menu button
+ getDashboardItem(sortedDashboardItemIds[0])
+ .findByDataTest('dashboarditem-menu-button')
+ .should('be.visible')
+
+ getDashboardItem(sortedDashboardItemIds[1])
+ .findByDataTest('dashboarditem-menu-button')
+ .should('be.visible')
+})
+
+// When I click the next slide button
+When('I click the next slide button', () => {
+ cy.getByDataTest('slideshow-next-button').realClick()
+})
+
+Then('item 2 is shown in fullscreen', () => {
+ assertItemIsNotVisible(0)
+ assertItemIsVisible(1)
+ assertItemIsNotVisible(2)
+
+ cy.getByDataTest('slideshow-page-counter').should('have.text', '2 / 11')
+})
+
+When('I click the previous slide button', () => {
+ cy.getByDataTest('slideshow-prev-button').realClick()
+})
+
+When('I click the fullscreen button on the second item', () => {
+ clickMenuButton(sortedDashboardItemIds[1])
+ cy.contains('View fullscreen').realClick()
+})
+
+When('I click the fullscreen button on the third item', () => {
+ clickMenuButton(sortedDashboardItemIds[2])
+ cy.contains('View fullscreen').realClick()
+})
+
+Then('item 3 is shown in fullscreen', () => {
+ assertItemIsNotVisible(0)
+ assertItemIsNotVisible(1)
+ assertItemIsVisible(2)
+
+ cy.getByDataTest('slideshow-page-counter').should('have.text', '3 / 11')
+})
diff --git a/cypress/e2e/view_dashboard.feature b/cypress/e2e/view_dashboard.feature
index 4ecf38f00..6057b23e6 100644
--- a/cypress/e2e/view_dashboard.feature
+++ b/cypress/e2e/view_dashboard.feature
@@ -11,7 +11,7 @@ Feature: Viewing dashboards
Given I open the "Antenatal Care" dashboard
When I search for dashboards containing "Immun"
Then Immunization and Immunization data dashboards are choices
- When I press enter in the search dashboard field
+ When I press tab in the search dashboard field and then enter
Then the "Immunization" dashboard displays in view mode
@nonmutating
@@ -19,8 +19,6 @@ Feature: Viewing dashboards
Given I open the "Antenatal Care" dashboard
When I search for dashboards containing "Noexist"
Then no dashboards are choices
- When I press enter in the search dashboard field
- Then dashboards list restored and dashboard is still "Antenatal Care"
@nonmutating
Scenario: I view the print layout preview and then print one-item-per-page preview
@@ -39,21 +37,6 @@ Feature: Viewing dashboards
Given I open the "Delivery" dashboard with shapes removed
Then the "Delivery" dashboard displays in view mode
- @nonmutating
- Scenario: I expand the control bar
- Given I open the "Delivery" dashboard
- Then the control bar should be at collapsed height
- When I toggle show more dashboards
- Then the control bar should be expanded to full height
-
- @nonmutating
- Scenario: I expand the control bar when dashboard not found
- Given I type an invalid dashboard id in the browser url
- Then a message displays informing that the dashboard is not found
- And the control bar should be at collapsed height
- When I toggle show more dashboards
- Then the control bar should be expanded to full height
-
# @nonmutating
# FIXME
# Scenario: Maps with tracked entities show layer names in legend
diff --git a/cypress/e2e/view_dashboard/dashboard_items_without_shape.js b/cypress/e2e/view_dashboard/dashboard_items_without_shape.js
index 128ed7825..284b8c37a 100644
--- a/cypress/e2e/view_dashboard/dashboard_items_without_shape.js
+++ b/cypress/e2e/view_dashboard/dashboard_items_without_shape.js
@@ -1,7 +1,6 @@
import { Given } from '@badeball/cypress-cucumber-preprocessor'
import { dashboards } from '../../assets/backends/index.js'
-import { dashboardChipSel } from '../../elements/viewDashboard.js'
-import { EXTENDED_TIMEOUT } from '../../support/utils.js'
+import { getNavigationMenuItem } from '../../elements/navigationMenu.js'
Given('I open the {string} dashboard with shapes removed', (title) => {
const regex = new RegExp(`dashboards/${dashboards[title].id}`, 'g')
@@ -17,5 +16,5 @@ Given('I open the {string} dashboard with shapes removed', (title) => {
res.send({ body: res.body })
})
})
- cy.get(dashboardChipSel, EXTENDED_TIMEOUT).contains(title).click()
+ getNavigationMenuItem(title).click()
})
diff --git a/cypress/e2e/view_dashboard/open_dashboard_app.js b/cypress/e2e/view_dashboard/open_dashboard_app.js
index 531eb937b..7786dc3d5 100644
--- a/cypress/e2e/view_dashboard/open_dashboard_app.js
+++ b/cypress/e2e/view_dashboard/open_dashboard_app.js
@@ -1,8 +1,5 @@
import { When, Then } from '@badeball/cypress-cucumber-preprocessor'
-import {
- dashboardTitleSel,
- dashboardChipSel,
-} from '../../elements/viewDashboard.js'
+import { dashboardTitleSel } from '../../elements/viewDashboard.js'
import { EXTENDED_TIMEOUT } from '../../support/utils.js'
When('I open the dashboard app with the root url', () => {
@@ -13,7 +10,6 @@ When('I open the dashboard app with the root url', () => {
})
cy.get(dashboardTitleSel).should('be.visible')
- cy.get(dashboardChipSel, EXTENDED_TIMEOUT).should('be.visible')
})
Then('the {string} dashboard displays', (title) => {
diff --git a/cypress/e2e/view_dashboard/print.js b/cypress/e2e/view_dashboard/print.js
index ddd1a9329..eae166b20 100644
--- a/cypress/e2e/view_dashboard/print.js
+++ b/cypress/e2e/view_dashboard/print.js
@@ -1,9 +1,8 @@
import { When, Then } from '@badeball/cypress-cucumber-preprocessor'
import { dashboards } from '../../assets/backends/sierraLeone_236.js'
-import { clickViewActionButton } from '../../elements/viewDashboard.js'
When('I click to preview the print one-item-per-page', () => {
- clickViewActionButton('More')
+ cy.get('[data-test="more-actions-button"]').click()
cy.get('[data-test="print-menu-item"]').click()
cy.get('[data-test="print-oipp-menu-item"]').click()
})
diff --git a/cypress/e2e/view_dashboard/resize_dashboards_bar.js b/cypress/e2e/view_dashboard/resize_dashboards_bar.js
deleted file mode 100644
index 6906c746d..000000000
--- a/cypress/e2e/view_dashboard/resize_dashboards_bar.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import { When, Then } from '@badeball/cypress-cucumber-preprocessor'
-import {
- dragHandleSel,
- dashboardsBarSel,
-} from '../../elements/viewDashboard.js'
-import { EXTENDED_TIMEOUT } from '../../support/utils.js'
-
-const RESP_CODE_200 = 200
-const RESP_CODE_201 = 201
-
-// Scenario: I change the height of the control bar
-When('I drag to increase the height of the control bar', () => {
- cy.intercept('PUT', '**/userDataStore/dashboard/controlBarRows').as(
- 'putRows'
- )
- cy.get(dragHandleSel, EXTENDED_TIMEOUT).as('dragHandleSel')
-
- cy.get('@dragHandleSel').trigger('mousedown')
- cy.get('@dragHandleSel').trigger('mousemove', { clientY: 300 })
- cy.get('@dragHandleSel').trigger('mouseup')
-
- cy.wait('@putRows').its('response.statusCode').should('eq', 201)
-})
-
-Then('the control bar height should be updated', () => {
- cy.visit('/')
- cy.get(dashboardsBarSel, EXTENDED_TIMEOUT)
- .invoke('height')
- .should('eq', 231)
-
- // restore the original height
- // eslint-disable-next-line cypress/unsafe-to-chain-command
- cy.get(dragHandleSel)
- .trigger('mousedown')
- .trigger('mousemove', { clientY: 71 })
- .trigger('mouseup')
- cy.wait('@putRows')
- .its('response.statusCode')
- .should('be.oneOf', [RESP_CODE_200, RESP_CODE_201])
-})
-
-When('I drag to decrease the height of the control bar', () => {
- cy.intercept('PUT', '**/userDataStore/dashboard/controlBarRows').as(
- 'putRows'
- )
- cy.get(dragHandleSel, EXTENDED_TIMEOUT).as('dragHandleSel')
-
- cy.get('@dragHandleSel').trigger('mousedown')
- cy.get('@dragHandleSel').trigger('mousemove', { clientY: 300 })
- cy.get('@dragHandleSel').trigger('mouseup')
- cy.wait('@putRows')
- .its('response.statusCode')
- .should('be.oneOf', [RESP_CODE_200, RESP_CODE_201])
-})
diff --git a/cypress/e2e/view_dashboard/search_for_dashboard.js b/cypress/e2e/view_dashboard/search_for_dashboard.js
index 3d958a4a9..c532817b2 100644
--- a/cypress/e2e/view_dashboard/search_for_dashboard.js
+++ b/cypress/e2e/view_dashboard/search_for_dashboard.js
@@ -1,40 +1,31 @@
import { When, Then } from '@badeball/cypress-cucumber-preprocessor'
-import { dashboards } from '../../assets/backends/sierraLeone_236.js'
-// import { gridItemSel } from '../../elements/dashboardItem.js'
import {
- dashboardTitleSel,
- dashboardChipSel,
- dashboardSearchInputSel,
-} from '../../elements/viewDashboard.js'
+ getNavigationMenuFilter,
+ getNavigationMenu,
+} from '../../elements/navigationMenu.js'
When('I search for dashboards containing {string}', (title) => {
- cy.get(dashboardSearchInputSel).type(title)
+ getNavigationMenuFilter().type(title)
})
Then('Immunization and Immunization data dashboards are choices', () => {
- cy.get(dashboardChipSel).should('be.visible').and('have.length', 2)
+ getNavigationMenu(true)
+ .find('li')
+ .should('be.visible')
+ .and('have.length', 2)
+})
+When('I press tab in the search dashboard field and then enter', () => {
+ cy.realPress('Tab')
+ cy.realPress('Enter')
})
When('I press enter in the search dashboard field', () => {
- cy.get(dashboardSearchInputSel).type('{enter}')
+ getNavigationMenuFilter(true).type('{enter}')
})
Then('no dashboards are choices', () => {
- cy.get(dashboardChipSel).should('not.exist')
-})
-
-Then('dashboards list restored and dashboard is still {string}', (title) => {
- cy.get(dashboardChipSel).should('be.visible').and('have.lengthOf.above', 0)
-
- cy.location().should((loc) => {
- expect(loc.hash).to.equal(dashboards[title].route)
- })
-
- cy.get(dashboardTitleSel).should('be.visible').and('contain', title)
- // FIXME
- // cy.get(`${gridItemSel}.VISUALIZATION`)
- // .first()
- // .getIframeBody()
- // .find('.highcharts-background')
- // .should('exist')
+ getNavigationMenu(true)
+ .find('li')
+ .contains('No dashboards found')
+ .should('be.visible')
})
diff --git a/cypress/e2e/view_dashboard/toggle_show_more_dashboards.js b/cypress/e2e/view_dashboard/toggle_show_more_dashboards.js
index c9c8f5edc..9a48903e2 100644
--- a/cypress/e2e/view_dashboard/toggle_show_more_dashboards.js
+++ b/cypress/e2e/view_dashboard/toggle_show_more_dashboards.js
@@ -1,12 +1,4 @@
-import { When, Then } from '@badeball/cypress-cucumber-preprocessor'
-import {
- dashboardsBarContainerSel,
- showMoreLessSel,
-} from '../../elements/viewDashboard.js'
-import { getApiBaseUrl, EXTENDED_TIMEOUT } from '../../support/utils.js'
-
-const MIN_DASHBOARDS_BAR_HEIGHT = 71
-const MAX_DASHBOARDS_BAR_HEIGHT = 431
+import { getApiBaseUrl } from '../../support/utils.js'
const RESP_CODE_200 = 200
const RESP_CODE_201 = 201
@@ -23,19 +15,3 @@ beforeEach(() => {
expect(response.status).to.be.oneOf([RESP_CODE_201, RESP_CODE_200])
)
})
-
-When('I toggle show more dashboards', () => {
- cy.get(showMoreLessSel).click()
-})
-
-Then('the control bar should be at collapsed height', () => {
- cy.get(dashboardsBarContainerSel, EXTENDED_TIMEOUT)
- .invoke('height')
- .should('eq', MIN_DASHBOARDS_BAR_HEIGHT)
-})
-
-Then('the control bar should be expanded to full height', () => {
- cy.get(dashboardsBarContainerSel, EXTENDED_TIMEOUT)
- .invoke('height')
- .should('eq', MAX_DASHBOARDS_BAR_HEIGHT)
-})
diff --git a/cypress/e2e/view_errors.feature b/cypress/e2e/view_errors.feature
index cb518deb5..43d602cb1 100644
--- a/cypress/e2e/view_errors.feature
+++ b/cypress/e2e/view_errors.feature
@@ -19,6 +19,11 @@ Feature: Errors while in view mode
When I open the "Delivery" dashboard
Then the "Delivery" dashboard displays in view mode
+ @nonmutating
+ Scenario: I navigate to a dashboard that fails to load
+ Given I type a dashboard id in the browser url that fails to load
+ Then a warning message is displayed stating that the dashboard could not be loaded
+
# @nonmutating
# Scenario: I navigate to print dashboard that doesn't exist
# Given I type an invalid print dashboard id in the browser url
diff --git a/cypress/e2e/view_errors/dashboard_item_missing_type.js b/cypress/e2e/view_errors/dashboard_item_missing_type.js
index 83247188d..ab62883da 100644
--- a/cypress/e2e/view_errors/dashboard_item_missing_type.js
+++ b/cypress/e2e/view_errors/dashboard_item_missing_type.js
@@ -3,11 +3,8 @@ import {
getDashboardItem,
clickItemDeleteButton,
} from '../../elements/dashboardItem.js'
-import {
- dashboardChipSel,
- dashboardTitleSel,
-} from '../../elements/viewDashboard.js'
-import { EXTENDED_TIMEOUT } from '../../support/utils.js'
+import { getNavigationMenuItem } from '../../elements/navigationMenu.js'
+import { dashboardTitleSel } from '../../elements/viewDashboard.js'
const ITEM_1_UID = 'GaVhJpqABYX'
const ITEM_2_UID = 'qXsjttMYuoZ'
@@ -38,7 +35,7 @@ const interceptDashboardRequest = () => {
Given('I open the Delivery dashboard with items missing a type', () => {
interceptDashboardRequest()
- cy.get(dashboardChipSel, EXTENDED_TIMEOUT).contains('Delivery').click()
+ getNavigationMenuItem('Delivery').click()
cy.get(dashboardTitleSel).should('be.visible').and('contain', 'Delivery')
})
diff --git a/cypress/e2e/view_errors/error_while_fetching_dashboard_details.js b/cypress/e2e/view_errors/error_while_fetching_dashboard_details.js
new file mode 100644
index 000000000..9569c589e
--- /dev/null
+++ b/cypress/e2e/view_errors/error_while_fetching_dashboard_details.js
@@ -0,0 +1,21 @@
+import { When, Then } from '@badeball/cypress-cucumber-preprocessor'
+
+When('I type a dashboard id in the browser url that fails to load', () => {
+ cy.intercept('**/dashboards/iMnYyBfSxmM**', {
+ statusCode: 500,
+ body: 'Oopsie!',
+ }).as('failure')
+
+ cy.visit('#/iMnYyBfSxmM')
+ cy.wait('@failure')
+})
+
+Then(
+ 'a warning message is displayed stating that the dashboard could not be loaded',
+ () => {
+ cy.contains('Load dashboard failed').should('exist')
+ cy.contains(
+ 'This dashboard could not be loaded. Please try again later.'
+ ).should('exist')
+ }
+)
diff --git a/cypress/e2e/view_errors/error_while_starring_dashboard.js b/cypress/e2e/view_errors/error_while_starring_dashboard.js
index 5e836daae..50b13f268 100644
--- a/cypress/e2e/view_errors/error_while_starring_dashboard.js
+++ b/cypress/e2e/view_errors/error_while_starring_dashboard.js
@@ -1,10 +1,8 @@
import { When, Then } from '@badeball/cypress-cucumber-preprocessor'
import { dashboards } from '../../assets/backends/index.js'
import {
- starSel,
dashboardUnstarredSel,
dashboardStarredSel,
- dashboardChipSel,
} from '../../elements/viewDashboard.js'
When('clicking to star {string} dashboard fails', (title) => {
@@ -13,7 +11,7 @@ When('clicking to star {string} dashboard fails', (title) => {
statusCode: 409,
}).as('starDashboardFail')
- cy.get(starSel).click()
+ cy.get(dashboardUnstarredSel).click()
cy.wait('@starDashboardFail').its('response.statusCode').should('eq', 409)
})
@@ -28,10 +26,8 @@ Then(
}
)
-Then('the {string} dashboard is not starred', (title) => {
+Then('the {string} dashboard is not starred', () => {
// check for the unfilled star next to the title
cy.get(dashboardUnstarredSel).should('be.visible')
cy.get(dashboardStarredSel).should('not.exist')
-
- cy.get(dashboardChipSel).contains(title).siblings().should('not.exist')
})
diff --git a/cypress/e2e/view_errors/item_chart_fails_to_render.js b/cypress/e2e/view_errors/item_chart_fails_to_render.js
index 9bf1241b4..d3139d5f2 100644
--- a/cypress/e2e/view_errors/item_chart_fails_to_render.js
+++ b/cypress/e2e/view_errors/item_chart_fails_to_render.js
@@ -41,7 +41,7 @@ Given('I open a dashboard with a chart that will fail', () => {
When(
'I apply a {string} filter of type {string}',
(dimensionType, filterName) => {
- cy.contains('Add filter').click()
+ cy.containsExact('Filter').click()
cy.get(filterDimensionsPanelSel).contains(dimensionType).click()
cy.get(dimensionsModalSel, EXTENDED_TIMEOUT).should('be.visible')
diff --git a/cypress/elements/dashboardFilter.js b/cypress/elements/dashboardFilter.js
index e614774e5..13dcdcbab 100644
--- a/cypress/elements/dashboardFilter.js
+++ b/cypress/elements/dashboardFilter.js
@@ -1,5 +1,7 @@
export const filterBadgeSel = '[data-test="dashboard-filter-badge"]'
+export const filterBadgeDeleteBtnSel = '[data-test="filter-badge-clear-button"]'
+
export const filterDimensionsPanelSel = '[data-test="dashboard-filter-popover"]'
export const unselectedItemsSel =
diff --git a/cypress/elements/navigationMenu.js b/cypress/elements/navigationMenu.js
new file mode 100644
index 000000000..3459458be
--- /dev/null
+++ b/cypress/elements/navigationMenu.js
@@ -0,0 +1,24 @@
+export const getNavigationMenuDropdown = () =>
+ cy.get('[data-test="dashboards-nav-menu-button"]')
+
+export const getNavigationMenu = (isOpen = false) => {
+ if (!isOpen) {
+ getNavigationMenuDropdown().click()
+ }
+ return cy.get('[role="menu"]')
+}
+
+export const getNavigationMenuItem = (dashboardDisplayName, isOpen) =>
+ getNavigationMenu(isOpen).find('li').contains(dashboardDisplayName)
+
+export const closeNavigationMenu = () => {
+ cy.get('.backdrop').click()
+ cy.get('.backdrop').should('not.exist')
+}
+
+export const getNavigationMenuFilter = (isOpen) => {
+ if (!isOpen) {
+ getNavigationMenuDropdown().click()
+ }
+ return cy.get('input:visible[placeholder="Search for a dashboard"]')
+}
diff --git a/cypress/elements/viewDashboard.js b/cypress/elements/viewDashboard.js
index 6c23b6487..ed7ef6e0a 100644
--- a/cypress/elements/viewDashboard.js
+++ b/cypress/elements/viewDashboard.js
@@ -4,19 +4,15 @@ import { EXTENDED_TIMEOUT } from '../support/utils.js'
// Dashboards bar
export const dashboardChipSel = '[data-test="dashboard-chip"]'
+export const dashboardsNavMenuButtonSel =
+ '[data-test="dashboards-nav-menu-button"]'
export const newButtonSel = '[data-test="new-button"]'
-export const chipStarSel = '[data-test="dhis2-uicore-chip-icon"]'
-export const dashboardSearchInputSel =
- 'input:visible[placeholder="Search for a dashboard"]'
-export const showMoreLessSel = '[data-test="showmore-button"]'
-export const dragHandleSel = '[data-test="controlbar-drag-handle"]'
+export const navMenuItemStarIconSel = '[data-test="starred-dashboard"]'
export const dashboardsBarSel = '[data-test="dashboards-bar"]'
// Active dashboard
export const dashboardTitleSel = '[data-test="view-dashboard-title"]'
-export const dashboardsBarContainerSel = '[data-test="dashboardsbar-container"]'
export const dashboardDescriptionSel = '[data-test="dashboard-description"]'
-export const starSel = '[data-test="button-star-dashboard"]'
export const dashboardStarredSel = '[data-test="dashboard-starred"]'
export const dashboardUnstarredSel = '[data-test="dashboard-unstarred"]'
export const titleBarSel = '[data-test="title-bar"]'
diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js
index 7045ae0f0..7a4034227 100644
--- a/cypress/support/e2e.js
+++ b/cypress/support/e2e.js
@@ -1,5 +1,6 @@
// import '@dhis2/cypress-commands'
import { enableAutoLogin } from '@dhis2/cypress-commands'
+import 'cypress-real-events'
import './commands.js'
enableAutoLogin()
@@ -51,9 +52,14 @@ before(() => {
beforeEach(() => {
const baseUrl = Cypress.env('dhis2BaseUrl')
const instanceVersion = Cypress.env('dhis2InstanceVersion')
+ const hideRequestsFromLog = Cypress.env('hideRequestsFromLog')
const envVariableName = computeEnvVariableName(instanceVersion)
const { name, value, ...options } = JSON.parse(Cypress.env(envVariableName))
+ if (hideRequestsFromLog) {
+ // disable Cypress's default behavior of logging all XMLHttpRequests and fetches
+ cy.intercept({ resourceType: /xhr|fetch/ }, { log: false })
+ }
localStorage.setItem(LOCAL_STORAGE_KEY, baseUrl)
cy.setCookie(name, value, options)
diff --git a/docs/dashboards.md b/docs/dashboards.md
index b19b4a9e2..b5902fb64 100644
--- a/docs/dashboards.md
+++ b/docs/dashboards.md
@@ -2,11 +2,12 @@
## About the Dashboards app
-The Dashboards app makes it possible to present a high level overview of your data, including displaying analytical objects such as maps, charts, reports and tables, as well as displaying text-based information, resource links, and app widgets.
+The Dashboards app makes it possible to present a high level overview of your data, including displaying analytical objects such as maps, charts, reports, tables, and line lists, as well as displaying text-based information, resource links, and app widgets.
Features of the Dashboards app include:
- View and print dashboards
+- Slideshow
- Create and edit dashboards
- Share dashboards with users and user groups
- Apply temporary filters while viewing dashboards
@@ -15,60 +16,55 @@ Features of the Dashboards app include:
## Dashboards app layout { #dashboards_setup }
-Dashboards have a title, description, and any number of dashboard items. Above the dashboard is the dashboards bar, which shows all your available dashboards, a dashboard search field, and a **+** button for creating a new dashboard.
+Dashboards have a title, description, dashboard items. The title and actions bar is at the top of the page under the header bar, and shows the title, a dropdown for searching and selecting dashboards, and actions that can be taken with the current dashboard. There is also a **+** button for creating a new dashboard.
The Dashboards app has two modes: _view_ and _edit/create_. When you first log in
to DHIS2, your most recently used dashboard will be displayed in view mode, if you are on the same computer as you were previously. If you are using a different computer or browser, then the first starred dashboard will be displayed. If there are no starred dashboards, then the first dashboard (alphabetically) will be displayed. Starred dashboards always show first in the dashboard list.
-Below is an example of a dashboard named "Antenatal Care", which has been populated with charts and maps:
+Below is a dashboard named "Antenatal Care", which has been populated with charts and maps:
![](resources/images/dashboard-view-mode.png)
### Personalization
-The Dashboards app can be personalized in the following ways:
+To adjust the Dashboards app to suit your needs, you can:
-- [Set the height of the dashboards bar](#dashboards_personalize_bar)
- [Star dashboards for quick access to your favorite dashboards](#dashboard-star-dashboard)
- [Show or hide dashboard description](#dashboard-show-description)
### Responsive view on small screens
-When viewing dashboards on small screens (for instance, portrait orientation on a mobile phone ), the dashboard will adapt to the screen and show all items in a single column. Some options, including editing, filtering and sharing, will not be available.
+When viewing dashboards on small screens like mobile phones, the dashboard will adapt to the screen and show all items in a single column. Some options, including editing, filtering and sharing, will not be available.
-![](resources/images/dashboard-small-screen.png)
+
### Searching for a dashboard
-You can search for a specific dashboard using the search field in the upper left of the dashboards bar entitled “Search for a dashboard”. The search is case insensitive, and as you type, the list of dashboards will be narrowed down to those that match your search text.
+You can search for a specific dashboard using the search field available from the Dashboards dropdown selector in the title bar. The search is case insensitive, and as you type, the list of dashboards will be narrowed down to those that match your search text.
-![](resources/images/dashboard-search-for-dashboard.png)
-
-### Personalizing the height of the dashboards bar { #dashboards_personalize_bar }
-
-You can set a specific height for the dashboards bar by
-down-clicking and dragging the bottom edge of the bar. When you finish dragging, the new height will be set. Clicking on the down arrow at the bottom of the dashboards bar will expand the bar to its maximum height (10 "rows"). Clicking on the up arrow will reset the height to your personalized height.
+![](resources/images/dashboard-list-and-filter.png)
## Creating and editing a dashboard
-To create a new dashboard, click the **+** button in the left corner of the dashboards bar to enter create/edit mode:
+To create a new dashboard, click the **+** button in the upper corner of the title bar to enter create/edit mode:
![](resources/images/dashboard-new-button.png)
-To edit an existing dashboard, click the **Edit** button next to the dashboard title (you must have edit access to see this button):
+To edit an existing dashboard, click the **Edit** button (you must have edit access to see this button):
-![](resources/images/dashboard-title-bar.png)
+![](resources/images/dashboard-edit-button.png)
-In create/edit mode, you can add or change the dashboard title, description and dashboard items. If you do not add a title, the dashboard will automatically be titled "Untitled dashboard".
+In create/edit mode, you can add or change the dashboard title, description, dashboard code and dashboard items. If you do not add a title, the dashboard will automatically be titled "Untitled dashboard".
![](resources/images/dashboard-create-mode.png)
### Adding items to the dashboard
-Add items to the dashboard by searching for items using the **Search for items to add to this dashboard** drop down selector. Item types are:
+Add items to the dashboard by searching for items using the **Search for items to add to this dashboard** dropdown selector. Item types are:
-- Visualizations (charts and tables)
+- Visualizations (charts and pivot tables)
- Maps
+- Line lists
- Event reports
- Event charts
- Reports
@@ -80,14 +76,14 @@ Add items to the dashboard by searching for items using the **Search for items t
![](resources/images/dashboard-item-selector.png)
-The list of items in the drop-down initially displays 10 visualizations (charts and tables), and 5 from each of the other categories, based on the search text you enter. Messages (Email), text boxes and spacer items are also found in the list. To view more items, click on **Show more**, and the list for that type will be extended to 25 items. If you still do not find the item you want, try typing a more specific search text.
+The list of items in the dropdown initially displays 10 visualizations (charts and tables), and 5 from each of the other categories, based on the search text you enter. To view more items, click on **Show more**, and the list for that type will be extended to 25 items. If you still do not find the item you want, try typing a more specific search text. Messages (Email), text boxes and spacer items can also be chosen from the list.
#### Dashboard layout and placement of new items
When adding items to the dashboard you can choose an overall layout by clicking on **Change layout** button. You can change this layout setting at any time.
- With _Freeflow_ layout, the added items can be moved using the mouse by down-clicking on the item and dragging it to the desired position. Items can also be resized with the mouse by down-clicking on the drag handle in the lower right corner of the item and dragging to the desired size.
-- With _Fixed columns_ layout, you can choose the number of columns to have on the dashboard, and the dashboard will automatically be layed out for you. Items cannot be moved or resized in _Fixed columns_ layout.
+- With _Fixed columns_ layout, you can choose the number of columns to have on the dashboard, and the dashboard will automatically be layed out for you. Items cannot be moved or resized in _Fixed columns_ layout. If you want to make custom adjustments to a _Fixed columns_ layout, return to a _Freeflow_ layout.
![](resources/images/dashboard-layout-modal.png)
@@ -109,13 +105,13 @@ Spacer in **view mode**:
#### Removing items
-Remove items by clicking on the red trash can at the upper right of the item. Be aware that when you remove an item while in _Freeflow_ layout, the items that are positioned below the removed item will "rise" upwards until they bump into an item above.
+Remove items by clicking on the red trash can at the upper right of the item. Be aware that when you remove an item while in _Freeflow_ layout, the items that are positioned below the removed item will "rise" upwards until they bump into an item above. in _Fixed columns_ layout, the items will be adjusted to fill every column in the layout so there are no empty column slots.
### Actions in create/edit mode
In create/edit mode you will see the following buttons in the actions bar at the top of the page: **Save changes**, **Print preview**, **Filter settings**, **Translate**, **Delete**, and **Exit without saving**. The **Translate** and **Delete** buttons are only shown if you are editing an existing dashboard.
-![](resources/images/dashboard-edit-mode-actions.png)
+![](resources/images/dashboard-edit-anc.png)
### Saving the dashboard
@@ -137,7 +133,7 @@ By default, users will be able to filter dashboard items by any dimension define
To restrict available filters, you can click **Only allow filtering by selected dimensions** and select the filters you wish to allow on the dashboard. Period and Organisation Unit are selected by default but can be removed if desired. When the dashboard is viewed, users will only be able to choose from among the filters selected.
-![](resources/images/dashboard-filter-settings.png)
+
In order to save updates to filter settings, you need to first click **Confirm** to close the Filter settings dialog and then click **Save changes** to save the dashboard changes.
@@ -145,41 +141,50 @@ In order to save updates to filter settings, you need to first click **Confirm**
### Translating dashboard title and description
-If you are editing an existing dashboard, then there will be a **Translate** button. Click on this button to open the Translation dialog, which provides a list of languages to translate to, and shows the original dashboard title underneath the name input field. First choose the language you want to translate for, then fill in the dashboard name and description translation.
+If you are editing an existing dashboard, then there will be a **Translate** button. Click on this button to open the Translation dialog, which provides a list of languages to translate to, and shows the original dashboard title and description. First choose the language you want to translate for, then fill in the dashboard name and description translation.
-![](resources/images/dashboard-translation-dialog.png)
+
### Deleting a dashboard
If you have access to delete the dashboard, then there will be a **Delete** button. When you click the **Delete** button, a confirmation dialog will first be displayed to confirm that you want to delete the dashboard.
-![](resources/images/dashboard-confirm-delete.png)
+
## Viewing a dashboard
-From view mode, you can toggle showing the description, star a dashboard, apply filters, print the dashboard, make the dashboard available offline, and share the dashboard with other users and user groups.
+The following actions are available on the dashboard in view mode:
+
+- Set the show/hide description setting
+- Star the dashboard so it appears first in the dashboard list
+- Filter the dashboard
+- Print the dashboard
+- Display the dashboard in a slideshow
+- Make the dashboard available offline
+- Share the dashboard with other users and user groups
+- Close the dashboard
![](resources/images/dashboard-more-menu.png)
### Show description { #dashboard-show-description }
-To toggle the description, open the **...More** menu and choose **Show description** (or **Hide description**). This setting will be remembered for all dashboards that you open. This setting applies to you, not other users.
+To toggle the description, open the **...** menu and choose **Show description** (or **Hide description**). This setting will be remembered for all dashboards that you open. This setting applies to you, not other users.
### Star dashboards { #dashboard-star-dashboard }
-Your starred dashboards are listed first in the list of dashboards for quick access. To star a dashboard, click on the star button to the right of the title. You can also toggle the star from the **...More** menu. When the star is “filled”, that means the dashboard is starred. Starring a dashboard only applies to you, not other users.
+Your starred dashboards are listed first in the list of dashboards for quick access. To star a dashboard, click on the star button to the right of the title. You can also toggle the star from the **...** menu. When the star is “filled”, that means the dashboard is starred. Starring a dashboard only applies to you, not other users.
-### Filtering a dashboard
+### Filter a dashboard
Applying filters to a dashboard change the data displayed in dashboard items containing visualizations. The filters are applied to each dashboard item in the same way: each added filter overrides the original value for that dimension in the original chart, table or map. It is possible to filter on Organisation Units and Periods, as well as dynamic dimensions, depending on the DHIS2 instance. You can apply multiple filters to the dashboard.
-To add a filter, click on the **Add Filter** button and choose a dimension:
+To add a filter, click on the **Filter** button and choose a dimension:
![Adding a filter](resources/images/dashboard-filters.png)
A dialog opens where the filter selection can be made.
-![Org Unit filter selection](resources/images/dashboard-orgunit-filter-dialog.png)
+![Org Unit filter selection](resources/images/dashboard-period-filter-dialog.png)
Click on **Confirm** in the dialog to apply the filter to the
current dashboard.
@@ -192,23 +197,31 @@ You can edit a filter by clicking on the filter badge to open the filter selecti
By default, users are able to filter dashboard items by any dimension defined in the DHIS2 instance. To limit available filters, see [Restricting dashboard filters](#restricting-dashboard-filters).
-### Making dashboards available offline
+### Display the dashboard in a slideshow { #dashboard-slideshow }
+
+The dashboard can be displayed in a slideshow by clicking on the **Slideshow** button.
+
+![Slideshow button](resources/images/dashboard-slideshow-button.png)
-To make a dashboard available offline, choose the **Make dashboard available offline** option in the **...More** menu. This will cause a reload of the dashboard where requests to the server are recorded and saved in browser storage. Note that offline dashboards are only available on the computer and browser where you set it to offline. If you currently have a filter applied when requesting the dashboard be made available offline, a dialog will appear to confirm the removal of the filters.
+When you enter the slideshow, you'll find navigation buttons and an exit button in a navigation bar at the bottom of the page. You can also navigate with the forward and back arrow keys on the keyboard, and exit the slideshow with the **esc** key. Any filters that are applied will be displayed in the navigation bar. Note that messages and spacer items are not displayed in the slideshow.
+
+![Slideshow navigation bar](resources/images/dashboard-slideshow-navbar.png)
+
+### Make dashboards available offline
+
+To make a dashboard available offline, choose the **Make available offline** option in the **...** menu. This will cause a reload of the dashboard where requests to the server are recorded and saved in browser storage. Note that offline dashboards are only available on the computer and browser where you set it to offline. If you currently have a filter applied when requesting the dashboard be made available offline, a dialog will appear to confirm the removal of the filters.
![](resources/images/dashboard-clear-filters-to-sync.png)
-Dashboards that have been saved for offline have an indicator on the dashboard chip in the dashboards bar, as well as a tag showing the time it was saved.
+Dashboards that have been saved for offline display a tag next to the dashboard title showing the time it was saved. In the dashboard selector, an icon is displayed if the dashboard is available offline.
![](resources/images/dashboard-offline-dashboard.png)
-If the dashboard has been changed since you made it available offline, either by you or someone else, you'll need to choose **Sync offline data now** from the **...More** menu to save the latest version of the dashboard.
-
-![](resources/images/dashboard-sync-offline.png)
+If the dashboard has been changed since you made it available offline, either by you or someone else, you'll need to choose **Sync offline data now** from the **...** menu to save the latest version of the dashboard.
-You can remove a dashboard from offline storaged by choosing **Remove from offline storage** in the **...More** menu.
+You can remove a dashboard from offline storaged by choosing **Remove from offline storage** in the **...** menu.
-![](resources/images/dashboard-remove-offline.png)
+![](resources/images/dashboard-sync-remove-offline.png)
#### Other notes about Dashboards app when you are offline:
@@ -218,7 +231,7 @@ If you are offline, any buttons or actions that require a connection to complete
### Printing a dashboard
-From the **...More** menu you can print the current dashboard. There are two styles of dashboard print: _Dashboard layout_ and _One item per page_. For both styles, a title page is added that shows the dashboard title, description (if the _Show description_ setting is enabled), and any applied dashboard filters.
+From the **...** menu you can print the current dashboard. There are two styles of dashboard print: _Dashboard layout_ and _One item per page_. For both styles, a title page is added that shows the dashboard title, description (if the _Show description_ setting is enabled), and any applied dashboard filters.
![](resources/images/dashboard-print-menu.png)
@@ -244,19 +257,19 @@ Click on the **Print** button in the upper right to trigger the browser print fu
![](resources/images/dashboard-print-oipp.png)
-## Dashboard items with charts, pivot tables or maps
+## Dashboard items with charts, pivot tables, maps and line lists
-Dashboard items with charts, pivot table or maps may have an item menu button in the upper right corner of the item with additional viewing options, depending on the system settings that have been configured for the DHIS2 instance. If all the relevant system settings have been disabled by the DHIS2 instance, then there will not be an item menu button. Here are the possible item menu options:
+Dashboard items with charts, pivot table, maps, line lists, event reports and event charts may have an item menu button in the upper right corner of the item with additional viewing options, depending on the system settings that have been configured for the DHIS2 instance. If all the relevant system settings have been disabled by the DHIS2 instance, then there will not be an item menu button. Here are the possible item menu options:
### Switching between visualizations
-It is possible to toggle the visualization view for items containing charts, pivot tables and maps. Click on the item menu button and choose the desired view (e.g., **View as Table**, **View as Map**, **View as Chart**):
+It is possible to toggle the visualization view of charts, pivot tables and maps, and between event charts and reports. Click on the item menu button and choose the desired view (e.g., **View as Table**, **View as Map**, **View as Chart**):
![](resources/images/dashboard-item-menu.png)
### View item in fullscreen
-To view the chart, table or map in fullscreen, click on the **View fullscreen** option. To exit fullscreen, you can either press **esc** key or click the exit button in the upper right corner of the fullscreen view.
+To view the chart, table, map or line list in fullscreen, click on the **View fullscreen** option. To exit fullscreen, you can either press **esc** key or click the exit button in the upper right corner of the fullscreen view. Note that you actually enter the slideshow, and can then use the navigation bar as described in the [Slideshow section](#dashboard-slideshow) to navigate to other dashboard items in fullscreen.
### Open in app
@@ -264,23 +277,23 @@ To open the visualization in its corresponding app (e.g., Data Visualizer, Maps)
### Show interpretations and details
-You can write interpretations for the chart, pivot table, map, event report, and event chart items by clicking on **Show interpretations and details**:
+You can write interpretations for charts, pivot tables, maps, line lists, event reports, and event charts by clicking on **Show interpretations and details**. The item will be expanded vertically underneath to show the description, interpretations and replies:
![](resources/images/dashboard-item-menu-interpretations.png)
-The item will be expanded vertically underneath to show the description, interpretations and replies. You can like an interpretation, reply to an interpretation, and add your own interpretation. You can edit, share or delete your own interpretations and replies, and if you have moderator access, you can delete others’ interpretations.
+You can like an interpretation, reply to an interpretation, and add your own interpretations. You can edit, share or delete your own interpretations and replies, and if you have moderator access, you can delete others’ interpretations.
-It is possible to format the description field, and interpretations with **bold**, _italic_ by using the Markdown style markers \* and \_ for **bold** and _italic_ respectively. The text field for writing new interpretations has a toolbar for adding rich text. Keyboard shortcuts are also available: Ctrl/Cmd + B and Ctrl/Cmd + I. A limited set of smilies is supported and can be used by typing one of the following character combinations: :) :-) :( :-( :+1 :-1. URLs are automatically detected and converted into a clickable link.
+It is possible to format interpretation text with **bold**, _italic_ by using the Markdown style markers \* and \_ for **bold** and _italic_ respectively. The text field for writing new interpretations has a toolbar for adding rich text. Keyboard shortcuts are also available: Ctrl/Cmd + B and Ctrl/Cmd + I. A limited set of smilies is supported and can be used by typing one of the following character combinations: :) :-) :( :-( :+1 :-1. URLs are automatically detected and converted into a clickable link.
Interpretations are sorted in descending order by date, with the most recent shown on top. Interpretation replies are sorted in ascending order by date, with the oldest shown on top.
-![](resources/images/dashboard-interpretations.png)
+
-## Sharing a dashboard { #dashboard_sharing }
+## Share the dashboard { #dashboard_sharing }
-In order to share a dashboard with users and user groups, click on the **Share** button to the right of the dashboard title to display the _Sharing and access_ dialog.
+In order to share the dashboard with users and user groups, click on the **Share** button to the right of the dashboard title to display the _Sharing and access_ dialog.
-![](resources/images/dashboard-sharing-dialog.png)
+
There are three levels of sharing permissions available for a dashboard:
@@ -302,7 +315,7 @@ All dashboards have the _All users_ group set to **No access** by default. The _
To share a dashboard with specific users and user groups, type the name in the input field, choose the desired access level and click on **Give access**.
-![](resources/images/dashboard-sharing-add-user.png)
+
You can provide users with the url of the dashboard, allowing them to navigate directly to the dashboard. To get the dashboard url, just open the dashboard in view mode, and copy the browser url. For example, the url to the Antenatal Care dashboard in play.dhis2.org/dev is:
@@ -312,4 +325,4 @@ https://play.dhis2.org/dev/dhis-web-dashboard/#/nghVC4wtyzi
To ensure that all charts, maps and tables on the dashboard are shared with the chosen users and user groups, click on the **Apply sharing to dashboard items** button.
-![](resources/images/dashboard-sharing-cascade-sharing.png)
+
diff --git a/docs/resources/images/dashboard-clear-filters-to-sync.png b/docs/resources/images/dashboard-clear-filters-to-sync.png
index 3583c53a6..41e7b536c 100644
Binary files a/docs/resources/images/dashboard-clear-filters-to-sync.png and b/docs/resources/images/dashboard-clear-filters-to-sync.png differ
diff --git a/docs/resources/images/dashboard-confirm-delete.png b/docs/resources/images/dashboard-confirm-delete.png
index b1b592957..4e02264f9 100644
Binary files a/docs/resources/images/dashboard-confirm-delete.png and b/docs/resources/images/dashboard-confirm-delete.png differ
diff --git a/docs/resources/images/dashboard-create-mode.png b/docs/resources/images/dashboard-create-mode.png
index c8ec30901..73e4b1c74 100644
Binary files a/docs/resources/images/dashboard-create-mode.png and b/docs/resources/images/dashboard-create-mode.png differ
diff --git a/docs/resources/images/dashboard-edit-anc.png b/docs/resources/images/dashboard-edit-anc.png
new file mode 100644
index 000000000..f49566b24
Binary files /dev/null and b/docs/resources/images/dashboard-edit-anc.png differ
diff --git a/docs/resources/images/dashboard-edit-button.png b/docs/resources/images/dashboard-edit-button.png
new file mode 100644
index 000000000..db22d1a47
Binary files /dev/null and b/docs/resources/images/dashboard-edit-button.png differ
diff --git a/docs/resources/images/dashboard-edit-print-preview.png b/docs/resources/images/dashboard-edit-print-preview.png
index bdd3bcda0..991a4bb12 100644
Binary files a/docs/resources/images/dashboard-edit-print-preview.png and b/docs/resources/images/dashboard-edit-print-preview.png differ
diff --git a/docs/resources/images/dashboard-filter-badges.png b/docs/resources/images/dashboard-filter-badges.png
index 5266adafd..e2dbe5757 100644
Binary files a/docs/resources/images/dashboard-filter-badges.png and b/docs/resources/images/dashboard-filter-badges.png differ
diff --git a/docs/resources/images/dashboard-filter-settings.png b/docs/resources/images/dashboard-filter-settings.png
index 8e81706e4..09da39edd 100644
Binary files a/docs/resources/images/dashboard-filter-settings.png and b/docs/resources/images/dashboard-filter-settings.png differ
diff --git a/docs/resources/images/dashboard-filters.png b/docs/resources/images/dashboard-filters.png
index 4681e4cc4..e9b640f9d 100644
Binary files a/docs/resources/images/dashboard-filters.png and b/docs/resources/images/dashboard-filters.png differ
diff --git a/docs/resources/images/dashboard-generic.png b/docs/resources/images/dashboard-generic.png
new file mode 100644
index 000000000..5c2b60d8b
Binary files /dev/null and b/docs/resources/images/dashboard-generic.png differ
diff --git a/docs/resources/images/dashboard-interpretations.png b/docs/resources/images/dashboard-interpretations.png
new file mode 100644
index 000000000..570917c22
Binary files /dev/null and b/docs/resources/images/dashboard-interpretations.png differ
diff --git a/docs/resources/images/dashboard-item-menu-interpretations.png b/docs/resources/images/dashboard-item-menu-interpretations.png
index d50233b8d..d9edcde1c 100644
Binary files a/docs/resources/images/dashboard-item-menu-interpretations.png and b/docs/resources/images/dashboard-item-menu-interpretations.png differ
diff --git a/docs/resources/images/dashboard-item-menu.png b/docs/resources/images/dashboard-item-menu.png
index 986687f37..0d117c02d 100644
Binary files a/docs/resources/images/dashboard-item-menu.png and b/docs/resources/images/dashboard-item-menu.png differ
diff --git a/docs/resources/images/dashboard-item-selector.png b/docs/resources/images/dashboard-item-selector.png
index a8c05edb5..f7469b0e5 100644
Binary files a/docs/resources/images/dashboard-item-selector.png and b/docs/resources/images/dashboard-item-selector.png differ
diff --git a/docs/resources/images/dashboard-layout-modal.png b/docs/resources/images/dashboard-layout-modal.png
index 594f5769e..27747bfd4 100644
Binary files a/docs/resources/images/dashboard-layout-modal.png and b/docs/resources/images/dashboard-layout-modal.png differ
diff --git a/docs/resources/images/dashboard-list-and-filter.png b/docs/resources/images/dashboard-list-and-filter.png
new file mode 100644
index 000000000..f4c0bea00
Binary files /dev/null and b/docs/resources/images/dashboard-list-and-filter.png differ
diff --git a/docs/resources/images/dashboard-more-menu.png b/docs/resources/images/dashboard-more-menu.png
index 7f9150f18..b194581d3 100644
Binary files a/docs/resources/images/dashboard-more-menu.png and b/docs/resources/images/dashboard-more-menu.png differ
diff --git a/docs/resources/images/dashboard-new-button.png b/docs/resources/images/dashboard-new-button.png
index 25781b134..803ced2b2 100644
Binary files a/docs/resources/images/dashboard-new-button.png and b/docs/resources/images/dashboard-new-button.png differ
diff --git a/docs/resources/images/dashboard-offline-dashboard.png b/docs/resources/images/dashboard-offline-dashboard.png
index ec2efc734..283b03606 100644
Binary files a/docs/resources/images/dashboard-offline-dashboard.png and b/docs/resources/images/dashboard-offline-dashboard.png differ
diff --git a/docs/resources/images/dashboard-orgunit-filter-dialog.png b/docs/resources/images/dashboard-orgunit-filter-dialog.png
deleted file mode 100644
index e158a36dd..000000000
Binary files a/docs/resources/images/dashboard-orgunit-filter-dialog.png and /dev/null differ
diff --git a/docs/resources/images/dashboard-period-filter-dialog.png b/docs/resources/images/dashboard-period-filter-dialog.png
new file mode 100644
index 000000000..7b8e20d3e
Binary files /dev/null and b/docs/resources/images/dashboard-period-filter-dialog.png differ
diff --git a/docs/resources/images/dashboard-place-items.png b/docs/resources/images/dashboard-place-items.png
new file mode 100644
index 000000000..73ef06aeb
Binary files /dev/null and b/docs/resources/images/dashboard-place-items.png differ
diff --git a/docs/resources/images/dashboard-print-menu.png b/docs/resources/images/dashboard-print-menu.png
index 53473be44..73b274344 100644
Binary files a/docs/resources/images/dashboard-print-menu.png and b/docs/resources/images/dashboard-print-menu.png differ
diff --git a/docs/resources/images/dashboard-search-for-dashboard.png b/docs/resources/images/dashboard-search-for-dashboard.png
deleted file mode 100644
index 63ac3cef3..000000000
Binary files a/docs/resources/images/dashboard-search-for-dashboard.png and /dev/null differ
diff --git a/docs/resources/images/dashboard-sharing-add-user.png b/docs/resources/images/dashboard-sharing-add-user.png
index b78187413..9eadd989c 100644
Binary files a/docs/resources/images/dashboard-sharing-add-user.png and b/docs/resources/images/dashboard-sharing-add-user.png differ
diff --git a/docs/resources/images/dashboard-sharing-cascade-sharing.png b/docs/resources/images/dashboard-sharing-cascade-sharing.png
index 46af33666..52b59e94b 100644
Binary files a/docs/resources/images/dashboard-sharing-cascade-sharing.png and b/docs/resources/images/dashboard-sharing-cascade-sharing.png differ
diff --git a/docs/resources/images/dashboard-sharing-dialog.png b/docs/resources/images/dashboard-sharing-dialog.png
index 6853cff2a..1e641f116 100644
Binary files a/docs/resources/images/dashboard-sharing-dialog.png and b/docs/resources/images/dashboard-sharing-dialog.png differ
diff --git a/docs/resources/images/dashboard-slideshow-button.png b/docs/resources/images/dashboard-slideshow-button.png
new file mode 100644
index 000000000..81609dced
Binary files /dev/null and b/docs/resources/images/dashboard-slideshow-button.png differ
diff --git a/docs/resources/images/dashboard-slideshow-navbar.png b/docs/resources/images/dashboard-slideshow-navbar.png
new file mode 100644
index 000000000..57c455798
Binary files /dev/null and b/docs/resources/images/dashboard-slideshow-navbar.png differ
diff --git a/docs/resources/images/dashboard-small-screen.png b/docs/resources/images/dashboard-small-screen.png
index 8099aa5b8..b26e6d0f6 100644
Binary files a/docs/resources/images/dashboard-small-screen.png and b/docs/resources/images/dashboard-small-screen.png differ
diff --git a/docs/resources/images/dashboard-spacer-edit-mode.png b/docs/resources/images/dashboard-spacer-edit-mode.png
index c03588b0d..285376f64 100644
Binary files a/docs/resources/images/dashboard-spacer-edit-mode.png and b/docs/resources/images/dashboard-spacer-edit-mode.png differ
diff --git a/docs/resources/images/dashboard-spacer-view-mode.png b/docs/resources/images/dashboard-spacer-view-mode.png
index 22a3a99d9..2ad12d8f9 100644
Binary files a/docs/resources/images/dashboard-spacer-view-mode.png and b/docs/resources/images/dashboard-spacer-view-mode.png differ
diff --git a/docs/resources/images/dashboard-sync-remove-offline.png b/docs/resources/images/dashboard-sync-remove-offline.png
new file mode 100644
index 000000000..466810721
Binary files /dev/null and b/docs/resources/images/dashboard-sync-remove-offline.png differ
diff --git a/docs/resources/images/dashboard-translation-dialog.png b/docs/resources/images/dashboard-translation-dialog.png
index 118375078..9321fce9f 100644
Binary files a/docs/resources/images/dashboard-translation-dialog.png and b/docs/resources/images/dashboard-translation-dialog.png differ
diff --git a/docs/resources/images/dashboard-view-mode.png b/docs/resources/images/dashboard-view-mode.png
index 6572b9297..ef998ad96 100644
Binary files a/docs/resources/images/dashboard-view-mode.png and b/docs/resources/images/dashboard-view-mode.png differ
diff --git a/i18n/en.pot b/i18n/en.pot
index 76eae083b..fa8cab2e1 100644
--- a/i18n/en.pot
+++ b/i18n/en.pot
@@ -5,26 +5,123 @@ 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: 2024-08-27T07:26:05.058Z\n"
-"PO-Revision-Date: 2024-08-27T07:26:05.060Z\n"
+"POT-Creation-Date: 2024-12-19T11:30:27.893Z\n"
+"PO-Revision-Date: 2024-12-19T11:30:27.893Z\n"
msgid "Untitled dashboard"
msgstr "Untitled dashboard"
-msgid "Cannot create a dashboard while offline"
-msgstr "Cannot create a dashboard while offline"
+msgid "Dashboards"
+msgstr "Dashboards"
-msgid "Create new dashboard"
-msgstr "Create new dashboard"
+msgid "The dashboard couldn't be made available offline. Try again."
+msgstr "The dashboard couldn't be made available offline. Try again."
+
+msgid "Remove from offline storage"
+msgstr "Remove from offline storage"
+
+msgid "Make available offline"
+msgstr "Make available offline"
+
+msgid "Sync offline data now"
+msgstr "Sync offline data now"
+
+msgid "Unstar dashboard"
+msgstr "Unstar dashboard"
+
+msgid "Star dashboard"
+msgstr "Star dashboard"
+
+msgid "Hide description"
+msgstr "Hide description"
+
+msgid "Show description"
+msgstr "Show description"
+
+msgid "Print"
+msgstr "Print"
+
+msgid "Dashboard layout"
+msgstr "Dashboard layout"
+
+msgid "One item per page"
+msgstr "One item per page"
+
+msgid "Close dashboard"
+msgstr "Close dashboard"
+
+msgid "No dashboard items to show in slideshow"
+msgstr "No dashboard items to show in slideshow"
+
+msgid "Not available offline"
+msgstr "Not available offline"
+
+msgid "Edit"
+msgstr "Edit"
+
+msgid "Share"
+msgstr "Share"
+
+msgid "Slideshow"
+msgstr "Slideshow"
+
+msgid "Clear dashboard filters?"
+msgstr "Clear dashboard filters?"
+
+msgid ""
+"A dashboard's filters can’t be saved offline. Do you want to remove the "
+"filters and make this dashboard available offline?"
+msgstr ""
+"A dashboard's filters can’t be saved offline. Do you want to remove the "
+"filters and make this dashboard available offline?"
+
+msgid "No, cancel"
+msgstr "No, cancel"
+
+msgid "Yes, clear filters and sync"
+msgstr "Yes, clear filters and sync"
+
+msgid "Cancel"
+msgstr "Cancel"
+
+msgid "Confirm"
+msgstr "Confirm"
+
+msgid "Filter"
+msgstr "Filter"
+
+msgid "Failed to unstar the dashboard"
+msgstr "Failed to unstar the dashboard"
+
+msgid "Failed to star the dashboard"
+msgstr "Failed to star the dashboard"
+
+msgid "Offline data last updated {{timeAgo}}"
+msgstr "Offline data last updated {{timeAgo}}"
+
+msgid "Synced {{timeAgo}}"
+msgstr "Synced {{timeAgo}}"
+
+msgid "Cannot unstar this dashboard while offline"
+msgstr "Cannot unstar this dashboard while offline"
+
+msgid "Cannot star this dashboard while offline"
+msgstr "Cannot star this dashboard while offline"
+
+msgid "No dashboards available."
+msgstr "No dashboards available."
+
+msgid "Create a new dashboard using the + button."
+msgstr "Create a new dashboard using the + button."
msgid "Search for a dashboard"
msgstr "Search for a dashboard"
-msgid "Show fewer dashboards"
-msgstr "Show fewer dashboards"
+msgid "No dashboards found"
+msgstr "No dashboards found"
-msgid "Show more dashboards"
-msgstr "Show more dashboards"
+msgid "{{appKey}} app not found"
+msgstr "{{appKey}} app not found"
msgid "Remove this item"
msgstr "Remove this item"
@@ -86,6 +183,9 @@ msgstr "Hide details and interpretations"
msgid "Show details and interpretations"
msgstr "Show details and interpretations"
+msgid "Open menu"
+msgstr "Open menu"
+
msgid "Open in {{appName}} app"
msgstr "Open in {{appName}} app"
@@ -149,8 +249,11 @@ msgstr "There was an error loading data for this item"
msgid "Open this item in {{appName}}"
msgstr "Open this item in {{appName}}"
-msgid "Not available offline"
-msgstr "Not available offline"
+msgid "Resources"
+msgstr "Resources"
+
+msgid "Reports"
+msgstr "Reports"
msgid "Visualizations"
msgstr "Visualizations"
@@ -176,12 +279,6 @@ msgstr "Line lists"
msgid "Apps"
msgstr "Apps"
-msgid "Reports"
-msgstr "Reports"
-
-msgid "Resources"
-msgstr "Resources"
-
msgid "Users"
msgstr "Users"
@@ -248,9 +345,6 @@ msgstr ""
"This action cannot be undone. Are you sure you want to permanently delete "
"this dashboard?"
-msgid "Cancel"
-msgstr "Cancel"
-
msgid "Discard changes"
msgstr "Discard changes"
@@ -310,9 +404,6 @@ msgstr "Available Filters"
msgid "Selected Filters"
msgstr "Selected Filters"
-msgid "Confirm"
-msgstr "Confirm"
-
msgid "There are no items on this dashboard"
msgstr "There are no items on this dashboard"
@@ -340,9 +431,6 @@ msgstr "Cannot search for dashboard items while offline"
msgid "Additional items"
msgstr "Additional items"
-msgid "Dashboard layout"
-msgstr "Dashboard layout"
-
msgid "Freeflow"
msgstr "Freeflow"
@@ -410,9 +498,6 @@ msgstr "End of dashboard"
msgid "Start of dashboard"
msgstr "Start of dashboard"
-msgid "Print"
-msgstr "Print"
-
msgid "dashboard layout"
msgstr "dashboard layout"
@@ -463,13 +548,19 @@ msgstr "No dashboards found. Use the + button to create a new dashboard."
msgid "Requested dashboard not found"
msgstr "Requested dashboard not found"
+msgid "No description"
+msgstr "No description"
+
msgid "{{count}} selected"
msgid_plural "{{count}} selected"
msgstr[0] "{{count}} selected"
msgstr[1] "{{count}} selected"
-msgid "Cannot remove filters while offline"
-msgstr "Cannot remove filters while offline"
+msgid "Cannot edit filters while offline"
+msgstr "Cannot edit filters while offline"
+
+msgid "Cannot edit filters on a small screen"
+msgstr "Cannot edit filters on a small screen"
msgid "Removing filters while offline"
msgstr "Removing filters while offline"
@@ -481,84 +572,25 @@ msgstr ""
"Removing this filter while offline will remove all other filters. Do you "
"want to remove all filters on this dashboard?"
-msgid "No, cancel"
-msgstr "No, cancel"
-
msgid "Yes, remove filters"
msgstr "Yes, remove filters"
-msgid "The dashboard couldn't be made available offline. Try again."
-msgstr "The dashboard couldn't be made available offline. Try again."
-
-msgid "Failed to unstar the dashboard"
-msgstr "Failed to unstar the dashboard"
-
-msgid "Failed to star the dashboard"
-msgstr "Failed to star the dashboard"
-
-msgid "Remove from offline storage"
-msgstr "Remove from offline storage"
-
-msgid "Make available offline"
-msgstr "Make available offline"
-
-msgid "Sync offline data now"
-msgstr "Sync offline data now"
-
-msgid "Unstar dashboard"
-msgstr "Unstar dashboard"
-
-msgid "Star dashboard"
-msgstr "Star dashboard"
-
-msgid "Hide description"
-msgstr "Hide description"
-
-msgid "Show description"
-msgstr "Show description"
-
-msgid "One item per page"
-msgstr "One item per page"
-
-msgid "Close dashboard"
-msgstr "Close dashboard"
-
-msgid "More"
-msgstr "More"
-
-msgid "Edit"
-msgstr "Edit"
-
-msgid "Share"
-msgstr "Share"
-
-msgid "Clear dashboard filters?"
-msgstr "Clear dashboard filters?"
-
-msgid ""
-"A dashboard's filters can’t be saved offline. Do you want to remove the "
-"filters and make this dashboard available offline?"
-msgstr ""
-"A dashboard's filters can’t be saved offline. Do you want to remove the "
-"filters and make this dashboard available offline?"
-
-msgid "Yes, clear filters and sync"
-msgstr "Yes, clear filters and sync"
-
-msgid "No description"
-msgstr "No description"
+msgid "Exit slideshow"
+msgstr "Exit slideshow"
-msgid "Add filter"
-msgstr "Add filter"
+msgid "Previous item"
+msgstr "Previous item"
-msgid "Offline data last updated {{time}}"
-msgstr "Offline data last updated {{time}}"
+msgid "Next item"
+msgstr "Next item"
-msgid "Cannot unstar this dashboard while offline"
-msgstr "Cannot unstar this dashboard while offline"
+msgid "{{name}}: {{filter}}"
+msgstr "{{name}}: {{filter}}"
-msgid "Cannot star this dashboard while offline"
-msgstr "Cannot star this dashboard while offline"
+msgid "{{count}} filters active"
+msgid_plural "{{count}} filters active"
+msgstr[0] "{{count}} filter active"
+msgstr[1] "{{count}} filters active"
msgid "Loading dashboard – {{name}}"
msgstr "Loading dashboard – {{name}}"
@@ -566,6 +598,12 @@ msgstr "Loading dashboard – {{name}}"
msgid "Loading dashboard"
msgstr "Loading dashboard"
+msgid "Load dashboard failed"
+msgstr "Load dashboard failed"
+
+msgid "This dashboard could not be loaded. Please try again later."
+msgstr "This dashboard could not be loaded. Please try again later."
+
msgid "Offline"
msgstr "Offline"
@@ -574,9 +612,3 @@ msgstr "This dashboard cannot be loaded while offline."
msgid "Go to start page"
msgstr "Go to start page"
-
-msgid "Load dashboard failed"
-msgstr "Load dashboard failed"
-
-msgid "This dashboard could not be loaded. Please try again later."
-msgstr "This dashboard could not be loaded. Please try again later."
diff --git a/package.json b/package.json
index 131892cf7..b62d147e0 100644
--- a/package.json
+++ b/package.json
@@ -5,11 +5,11 @@
"private": true,
"license": "BSD-3-Clause",
"dependencies": {
- "@dhis2/analytics": "^26.8.2",
+ "@dhis2/analytics": "git+https://github.com/d2-ci/analytics.git#e398d08e696356908725c8f51f32c30e7cb002ec",
"@dhis2/app-runtime": "^3.10.6",
"@dhis2/app-runtime-adapter-d2": "^1.1.0",
"@dhis2/d2-i18n": "^1.1.3",
- "@dhis2/ui": "^9.11.3",
+ "@dhis2/ui": "^10.1.4",
"@krakenjs/post-robot": "^11.0.0",
"classnames": "^2.3.2",
"d2": "^31.10.0",
@@ -51,9 +51,10 @@
"@semantic-release/changelog": "^6",
"@semantic-release/exec": "^6",
"@semantic-release/git": "^10",
- "@testing-library/jest-dom": "^6.1.2",
+ "@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^12",
"cypress": "^13.13.1",
+ "cypress-real-events": "^1.13.0",
"d2-manifest": "^1.0.0",
"eslint-plugin-cypress": "^3.3.0",
"immutability-helper": "^3.1.1",
@@ -68,6 +69,12 @@
"jest": {
"moduleNameMapper": {
"^.+\\.(css|sass|scss)$": "identity-obj-proxy"
- }
+ },
+ "setupFilesAfterEnv": [
+ "/config/testSetup.js"
+ ]
+ },
+ "resolutions": {
+ "@dhis2/ui": "^10.1.4"
}
}
diff --git a/src/actions/controlBar.js b/src/actions/controlBar.js
deleted file mode 100644
index e7f6eae90..000000000
--- a/src/actions/controlBar.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import { apiGetControlBarRows } from '../api/controlBar.js'
-import { SET_CONTROLBAR_USER_ROWS } from '../reducers/controlBar.js'
-
-// actions
-
-export const acSetControlBarUserRows = (rows) => ({
- type: SET_CONTROLBAR_USER_ROWS,
- value: rows,
-})
-
-// thunks
-
-export const tSetControlBarRows = () => async (dispatch) => {
- const onSuccess = (rows) => {
- dispatch(acSetControlBarUserRows(rows))
- }
-
- const onError = (error) => {
- console.log('Error (apiGetControlBarRows): ', error)
- return error
- }
-
- try {
- const controlBarRows = await apiGetControlBarRows()
- return onSuccess(controlBarRows)
- } catch (err) {
- return onError(err)
- }
-}
diff --git a/src/actions/slideshow.js b/src/actions/slideshow.js
new file mode 100644
index 000000000..38a841ff6
--- /dev/null
+++ b/src/actions/slideshow.js
@@ -0,0 +1,6 @@
+import { SET_SLIDESHOW } from '../reducers/slideshow.js'
+
+export const acSetSlideshow = (isSlideshow) => ({
+ type: SET_SLIDESHOW,
+ value: isSlideshow,
+})
diff --git a/src/api/controlBar.js b/src/api/controlBar.js
deleted file mode 100644
index ce3627c5b..000000000
--- a/src/api/controlBar.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { DEFAULT_STATE_CONTROLBAR_ROWS } from '../reducers/controlBar.js'
-import {
- apiGetUserDataStoreValue,
- apiPostUserDataStoreValue,
-} from './userDataStore.js'
-
-const KEY_CONTROLBAR_ROWS = 'controlBarRows'
-
-export const apiGetControlBarRows = async () =>
- await apiGetUserDataStoreValue(
- KEY_CONTROLBAR_ROWS,
- DEFAULT_STATE_CONTROLBAR_ROWS
- )
-
-export const apiPostControlBarRows = async (value) =>
- await apiPostUserDataStoreValue(KEY_CONTROLBAR_ROWS, value)
diff --git a/src/components/App.js b/src/components/App.js
index 28820c851..66e12a726 100644
--- a/src/components/App.js
+++ b/src/components/App.js
@@ -5,7 +5,6 @@ import React, { useEffect } from 'react'
import { connect } from 'react-redux'
import { Redirect, HashRouter as Router, Route, Switch } from 'react-router-dom'
import { acClearActiveModalDimension } from '../actions/activeModalDimension.js'
-import { tSetControlBarRows } from '../actions/controlBar.js'
import { tFetchDashboards } from '../actions/dashboards.js'
import { acClearDashboardsFilter } from '../actions/dashboardsFilter.js'
import { acClearEditDashboard } from '../actions/editDashboard.js'
@@ -31,7 +30,6 @@ const App = (props) => {
useEffect(() => {
props.fetchDashboards()
- props.setControlBarRows()
props.setShowDescription()
// store the headerbar height for controlbar height calculations
@@ -48,7 +46,7 @@ const App = (props) => {
return (
systemSettings && (
<>
-
+ {
App.propTypes = {
fetchDashboards: PropTypes.func,
resetState: PropTypes.func,
- setControlBarRows: PropTypes.func,
setShowDescription: PropTypes.func,
}
const mapDispatchToProps = {
fetchDashboards: tFetchDashboards,
- setControlBarRows: tSetControlBarRows,
setShowDescription: tSetShowDescription,
resetState: () => (dispatch) => {
dispatch(acSetSelected({}))
diff --git a/src/components/DashboardsBar/Chip.js b/src/components/DashboardsBar/Chip.js
deleted file mode 100644
index 0e9cbd010..000000000
--- a/src/components/DashboardsBar/Chip.js
+++ /dev/null
@@ -1,74 +0,0 @@
-import { useDhis2ConnectionStatus, useDataEngine } from '@dhis2/app-runtime'
-import { Chip as UiChip, colors, IconStarFilled24 } from '@dhis2/ui'
-import cx from 'classnames'
-import debounce from 'lodash/debounce.js'
-import PropTypes from 'prop-types'
-import React from 'react'
-import { Link } from 'react-router-dom'
-import { apiPostDataStatistics } from '../../api/dataStatistics.js'
-import { useCacheableSection } from '../../modules/useCacheableSection.js'
-import { OfflineSaved } from './assets/icons.js'
-import classes from './styles/Chip.module.css'
-
-const Chip = ({ starred, selected, label, dashboardId, onClick }) => {
- const { lastUpdated } = useCacheableSection(dashboardId)
- const { isConnected: online } = useDhis2ConnectionStatus()
- const engine = useDataEngine()
- const chipProps = {
- selected,
- }
-
- if (starred) {
- chipProps.icon = (
-
- )
- }
- const debouncedPostStatistics = debounce(
- () => apiPostDataStatistics('DASHBOARD_VIEW', dashboardId, engine),
- 500
- )
-
- const handleClick = () => {
- online && debouncedPostStatistics()
- onClick()
- }
-
- return (
-
-
-
- {label}
-
- {lastUpdated && (
-
- )}
-
-
- )
-}
-
-Chip.propTypes = {
- dashboardId: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired,
- selected: PropTypes.bool.isRequired,
- starred: PropTypes.bool.isRequired,
- onClick: PropTypes.func.isRequired,
-}
-
-export default Chip
diff --git a/src/components/DashboardsBar/ClearButton.js b/src/components/DashboardsBar/ClearButton.js
deleted file mode 100644
index 05214033e..000000000
--- a/src/components/DashboardsBar/ClearButton.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import PropTypes from 'prop-types'
-import React from 'react'
-import ClearIcon from './assets/Clear.js'
-import classes from './styles/ClearButton.module.css'
-
-const ClearButton = ({ onClear }) => (
-
-)
-
-ClearButton.propTypes = {
- onClear: PropTypes.func.isRequired,
-}
-
-export default ClearButton
diff --git a/src/components/DashboardsBar/Content.js b/src/components/DashboardsBar/Content.js
deleted file mode 100644
index e95f1a44a..000000000
--- a/src/components/DashboardsBar/Content.js
+++ /dev/null
@@ -1,135 +0,0 @@
-import { useDhis2ConnectionStatus } from '@dhis2/app-runtime'
-import i18n from '@dhis2/d2-i18n'
-import { Button, ComponentCover, Tooltip, IconAdd24 } from '@dhis2/ui'
-import cx from 'classnames'
-import PropTypes from 'prop-types'
-import React, { useState } from 'react'
-import { connect } from 'react-redux'
-import { Redirect, withRouter } from 'react-router-dom'
-import { sGetAllDashboards } from '../../reducers/dashboards.js'
-import { sGetDashboardsFilter } from '../../reducers/dashboardsFilter.js'
-import { sGetSelectedId } from '../../reducers/selected.js'
-import Chip from './Chip.js'
-import Filter from './Filter.js'
-import { getFilteredDashboards } from './getFilteredDashboards.js'
-import classes from './styles/Content.module.css'
-
-const Content = ({
- dashboards,
- expanded,
- filterText,
- history,
- selectedId,
- onChipClicked,
- onSearchClicked,
-}) => {
- const [redirectUrl, setRedirectUrl] = useState(null)
- const { isDisconnected: offline } = useDhis2ConnectionStatus()
-
- const onSelectDashboard = () => {
- const id = getFilteredDashboards(dashboards, filterText)[0]?.id
- if (id) {
- history.push(id)
- }
- }
-
- const enterNewMode = () => {
- if (!offline) {
- setRedirectUrl('/new')
- }
- }
-
- const getChips = () =>
- getFilteredDashboards(dashboards, filterText).map((dashboard) => (
-
- ))
-
- const getControlsSmall = () => (
-