Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: parse the ou filter correctly for all ou types #2691

Merged
merged 7 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 28 additions & 11 deletions cypress/integration/common/view/add_a_FILTERTYPE_filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,35 @@ const FACILITY_TYPE = 'Clinic'
When('I add a {string} filter', (dimensionType) => {
cy.contains('Add filter').click()

// open the dimensions modal
cy.get(filterDimensionsPanelSel).contains(dimensionType).click()

// select an item in the modal
if (dimensionType === 'Period') {
cy.get(unselectedItemsSel).contains(PERIOD).dblclick()
} else if (dimensionType === 'Organisation unit') {
cy.get(orgUnitTreeSel, EXTENDED_TIMEOUT)
.find('[type="checkbox"]', EXTENDED_TIMEOUT)
.check(OU_ID)
} else {
cy.get(unselectedItemsSel).contains(FACILITY_TYPE).dblclick()
switch (dimensionType) {
case 'Period':
cy.get(filterDimensionsPanelSel).contains(dimensionType).click()
cy.get(unselectedItemsSel).contains(PERIOD).dblclick()
break
case 'Organisation unit':
cy.get(filterDimensionsPanelSel).contains(dimensionType).click()
cy.get(orgUnitTreeSel, EXTENDED_TIMEOUT)
.find('[type="checkbox"]', EXTENDED_TIMEOUT)
.check(OU_ID)
break
case 'Org unit group':
cy.get(filterDimensionsPanelSel)
.contains('Organisation unit')
.click()
cy.getByDataTest('org-unit-group-select').click()
cy.getByDataTest('dhis2-uicore-select-menu-menuwrapper')
.contains('District')
.click()
// close the popup
cy.getByDataTest('dhis2-uicore-select-menu-menuwrapper')
.closest('[data-test="dhis2-uicore-layer"]')
.click('topLeft')
break

default:
cy.get(filterDimensionsPanelSel).contains(dimensionType).click()
cy.get(unselectedItemsSel).contains(FACILITY_TYPE).dblclick()
}

// confirm to apply the filter
Expand Down
8 changes: 7 additions & 1 deletion cypress/integration/view/dashboard_filter.feature
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Feature: Dashboard filter

Scenario: I add a Period filter
When I start a new dashboard
And I add a MAP and a CHART and save
And I add items and save
Then the dashboard displays in view mode
When I add a "Period" filter
Then the Period filter is applied to the dashboard
Expand All @@ -19,6 +19,12 @@ Feature: Dashboard filter
When I add a "Facility Type" filter
Then the Facility Type filter is applied to the dashboard

Scenario: I add a Org unit group filter
Given I open existing dashboard
Then the dashboard displays in view mode
When I add a "Org unit group" filter
Then the Org unit group filter is applied to the dashboard

Scenario: I can access the dimensions modal from the filter badge
Given I open existing dashboard
When I add a "Period" filter
Expand Down
88 changes: 60 additions & 28 deletions cypress/integration/view/dashboard_filter/create_dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,78 @@ import {
dashboardChipSel,
dashboardTitleSel,
} from '../../../elements/viewDashboard.js'
import { getApiBaseUrl } from '../../../support/server/utils.js'
import {
EXTENDED_TIMEOUT,
createDashboardTitle,
} from '../../../support/utils.js'

const TEST_DASHBOARD_TITLE = createDashboardTitle('af')

When('I add a MAP and a CHART and save', () => {
//add the title
cy.get('[data-test="dashboard-title-input"]').type(TEST_DASHBOARD_TITLE)
const customApp = {
name: 'Users-Role-Monitor-Widget',
id: '5e43908a-3105-4baa-9a00-87a94ebdc034',
}

// add items
cy.get('[data-test="item-search"]').click()
cy.get('[data-test="item-search"]')
.find('input')
.type('Inpatient', { force: true })
When('I add items and save', () => {
// first install a custom app
cy.request('POST', `${getApiBaseUrl()}/api/appHub/${customApp.id}`).then(
(response) => {
expect(response.status).to.eq(204)

//chart
cy.get(
'[data-test="menu-item-Inpatient: BMI this year by districts"]'
).click()
//add the dashboard title
cy.get('[data-test="dashboard-title-input"]').type(
TEST_DASHBOARD_TITLE
)

cy.get('[data-test="dhis2-uicore-layer"]').click('topLeft')
// open item selector
cy.get('[data-test="item-search"]').click()
cy.get('[data-test="item-search"]')
.find('input')
.type('Inpatient', { force: true })

cy.get('[data-test="item-search"]').click()
cy.get('[data-test="item-search"]')
.find('input')
.type('ipt 2', { force: true })
//CHART
cy.get(
'[data-test="menu-item-Inpatient: BMI this year by districts"]'
).click()

//map
cy.get('[data-test="menu-item-ANC: IPT 2 Coverage this year"]').click()
cy.get('[data-test="dhis2-uicore-layer"]').click('topLeft')

cy.get('[data-test="dhis2-uicore-layer"]').click('topLeft')
cy.get('[data-test="item-search"]').click()
cy.get('[data-test="item-search"]')
.find('input')
.type('ipt 2', { force: true })

//move things so the dashboard is more compact
// eslint-disable-next-line cypress/unsafe-to-chain-command
cy.get(`${gridItemSel}.MAP`)
.trigger('mousedown')
.trigger('mousemove', { clientX: 650 })
.trigger('mouseup')
//MAP
cy.get(
'[data-test="menu-item-ANC: IPT 2 Coverage this year"]'
).click()

//save
cy.get('button').contains('Save changes', EXTENDED_TIMEOUT).click()
// close the item selector
cy.get('[data-test="dhis2-uicore-layer"]').click('topLeft')

//add a custom app item
cy.get('[data-test="item-search"]').click()
cy.get('[data-test="item-search"]')
.find('input')
.type('Role Monitor', { force: true })

cy.contains('Role Monitor Widget').click()

// close the item selector
cy.get('[data-test="dhis2-uicore-layer"]').click('topLeft')

//move things so the dashboard is more compact
// eslint-disable-next-line cypress/unsafe-to-chain-command
cy.get(`${gridItemSel}.MAP`)
.trigger('mousedown')
.trigger('mousemove', { clientX: 650 })
.trigger('mouseup')

//save
cy.get('button').contains('Save changes', EXTENDED_TIMEOUT).click()
}
)
})

Given('I open existing dashboard', () => {
Expand Down Expand Up @@ -84,4 +113,7 @@ Then('different dashboard displays in view mode', () => {
cy.get(dashboardTitleSel)
.should('be.visible')
.and('not.contain', TEST_DASHBOARD_TITLE)

// remove the custom app
cy.request('DELETE', `${getApiBaseUrl()}/api/apps/${customApp.name}`)
})
17 changes: 17 additions & 0 deletions cypress/integration/view/dashboard_filter/dashboard_filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,23 @@ Then('the Facility Type filter is applied to the dashboard', () => {
// .should('be.visible')
})

Then('the Org unit group filter is applied to the dashboard', () => {
// check that the filter badge is correct
cy.get(filterBadgeSel)
.contains('Organisation unit: District')
.should('be.visible')

// check that the custom app is loaded (see ticket DHIS2-14544)
cy.get('iframe')
.invoke('attr', 'title')
.contains('Role Monitor Widget')
.scrollIntoView()
cy.get('iframe')
.invoke('attr', 'title')
.contains('Role Monitor Widget')
.should('be.visible')
})

Then('the filter modal is opened', () => {
cy.get(dimensionsModalSel, EXTENDED_TIMEOUT).should('be.visible')
})
25 changes: 25 additions & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Cypress.Commands.add('getByDataTest', (selector, ...args) =>
cy.get(`[data-test=${selector}]`, ...args)
)
Cypress.Commands.add(
'findByDataTest',
{
prevSubject: true,
},
(subject, selector, ...args) =>
cy.wrap(subject).find(`[data-test="${selector}"]`, ...args)
)

Cypress.Commands.add(
'containsExact',
{
prevSubject: 'optional',
},
(subject, selector) =>
cy.wrap(subject).contains(
new RegExp(
`^${selector.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')}$`, //eslint-disable-line no-useless-escape
'gm'
)
)
)
1 change: 1 addition & 0 deletions cypress/support/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { enableAutoLogin } from '@dhis2/cypress-commands'
import { enableNetworkShim } from './server/index.js'
import { getDefaultMode, isStubMode } from './server/utils.js'
import './commands.js'

enableNetworkShim()

Expand Down
16 changes: 1 addition & 15 deletions src/components/Item/AppItem/Item.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,13 @@ import { Divider, colors, spacers, IconQuestion24 } from '@dhis2/ui'
import PropTypes from 'prop-types'
import React from 'react'
import { connect } from 'react-redux'
import { FILTER_ORG_UNIT } from '../../../actions/itemFilters.js'
import { EDIT, isEditMode } from '../../../modules/dashboardModes.js'
import {
sGetItemFiltersRoot,
DEFAULT_STATE_ITEM_FILTERS,
} from '../../../reducers/itemFilters.js'
import ItemHeader from '../ItemHeader/ItemHeader.js'

const getIframeSrc = (appDetails, item, itemFilters) => {
let iframeSrc = `${appDetails.launchUrl}?dashboardItemId=${item.id}`

if (itemFilters[FILTER_ORG_UNIT] && itemFilters[FILTER_ORG_UNIT].length) {
const ouIds = itemFilters[FILTER_ORG_UNIT].map(
(ouFilter) => ouFilter.path.split('/').slice(-1)[0]
)

iframeSrc += `&userOrgUnit=${ouIds.join(',')}`
}

return iframeSrc
}
import { getIframeSrc } from './getIframeSrc.js'

const AppItem = ({ dashboardMode, item, itemFilters }) => {
const { d2 } = useD2()
Expand Down
106 changes: 106 additions & 0 deletions src/components/Item/AppItem/__tests__/getIframeSrc.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { getIframeSrc } from '../getIframeSrc.js'

const appDetails = { launchUrl: 'debug/dev' }
const dashboardItem = { id: 'rainbowdashitem' }
const expectedSrc = `${appDetails.launchUrl}?dashboardItemId=${dashboardItem.id}`

describe('getIframeSrc', () => {
it('no ou filter', () => {
const ouFilter = []

const src = getIframeSrc(appDetails, dashboardItem, { ou: ouFilter })
expect(src).toEqual(expectedSrc)
})

it('org units chosen from the tree', () => {
const ouFilter = [
{
id: 'fdc6uOvgoji',
path: '/ImspTQPwCqd/fdc6uOvgoji',
name: 'Bombali',
},
{
id: 'lc3eMKXaEfw',
path: '/ImspTQPwCqd/lc3eMKXaEfw',
name: 'Bonthe',
},
]

const src = getIframeSrc(appDetails, dashboardItem, { ou: ouFilter })
expect(src).toEqual(
`${expectedSrc}&userOrgUnit=fdc6uOvgoji,lc3eMKXaEfw`
)
})

it('org unit group and org unit from tree', () => {
const ouFilter = [
{
id: 'OU_GROUP-b0EsAxm8Nge',
name: 'Western Area',
},
{
id: 'lc3eMKXaEfw',
path: '/ImspTQPwCqd/lc3eMKXaEfw',
name: 'Bonthe',
},
]

const src = getIframeSrc(appDetails, dashboardItem, { ou: ouFilter })
expect(src).toEqual(
`${expectedSrc}&userOrgUnit=OU_GROUP-b0EsAxm8Nge,lc3eMKXaEfw`
)
})

it('org unit level and org unit from tree', () => {
const ouFilter = [
{
id: 'LEVEL-m9lBJogzE95',
name: 'Facility',
},
{
id: 'fdc6uOvgoji',
path: '/ImspTQPwCqd/fdc6uOvgoji',
name: 'Bombali',
},
]

const src = getIframeSrc(appDetails, dashboardItem, { ou: ouFilter })
expect(src).toEqual(
`${expectedSrc}&userOrgUnit=LEVEL-m9lBJogzE95,fdc6uOvgoji`
)
})

it('user org unit', () => {
const ouFilter = [
{
id: 'USER_ORGUNIT',
displayName: 'User organisation unit',
},
]

const src = getIframeSrc(appDetails, dashboardItem, { ou: ouFilter })
expect(src).toEqual(`${expectedSrc}&userOrgUnit=USER_ORGUNIT`)
})

it('all user org units', () => {
const ouFilter = [
{
id: 'USER_ORGUNIT_CHILDREN',
displayName: 'User sub-units',
},
{
id: 'USER_ORGUNIT_GRANDCHILDREN',
displayName: 'User sub-x2-units',
},
{
id: 'USER_ORGUNIT',
displayName: 'User organisation unit',
},
]

const src = getIframeSrc(appDetails, dashboardItem, { ou: ouFilter })
expect(src).toEqual(
`${expectedSrc}&userOrgUnit=USER_ORGUNIT_CHILDREN,USER_ORGUNIT_GRANDCHILDREN,USER_ORGUNIT`
)
})
})
15 changes: 15 additions & 0 deletions src/components/Item/AppItem/getIframeSrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { FILTER_ORG_UNIT } from '../../../actions/itemFilters.js'

export const getIframeSrc = (appDetails, item, itemFilters) => {
let iframeSrc = `${appDetails.launchUrl}?dashboardItemId=${item.id}`

if (itemFilters[FILTER_ORG_UNIT] && itemFilters[FILTER_ORG_UNIT].length) {
const ouIds = itemFilters[FILTER_ORG_UNIT].map(({ id, path }) =>
jenniferarnesen marked this conversation as resolved.
Show resolved Hide resolved
path ? path.split('/').slice(-1)[0] : id
)

iframeSrc += `&userOrgUnit=${ouIds.join(',')}`
}

return iframeSrc
}
Loading