Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(org unit tree): allow request customization #681

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions components/organisation-unit-tree/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"build"
],
"devDependencies": {
"@dhis2-ui/button": "6.12.0",
"@dhis2/app-runtime": "^2.7.1",
"@dhis2/d2-i18n": "^1.1.0",
"@testing-library/react-hooks": "^7.0.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from 'react'
import { OrganisationUnitTree } from '../index.js'

export const Collapsed = () => (
<OrganisationUnitTree name="Root org unit" roots={['A0000000000']} />
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react'
import { OrganisationUnitTree } from '../index.js'

export const CustomNodeLabel = () => (
<OrganisationUnitTree
name="Root org unit"
roots="A0000000000"
initiallyExpanded={['/A0000000000/A0000000001']}
renderNodeLabel={({ node, loading }) => {
const { displayName } = node

if (loading) {
return `${displayName} (loading)`
}

return <span>--- {displayName}</span>
}}
/>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { CustomDataProvider, useDataEngine } from '@dhis2/app-runtime'
import React from 'react'
import {
OrganisationUnitTreeControllable,
useFetchOrgData,
useFetchRootOrgData,
} from '../index.js'
import { customData } from './customData.js'

const customDataWithApprovals = {
...customData,
'dataApprovals/approvals': (type, { params }) => {
return [
{
wf: params.wf,
pe: params.pe,
ou: params.ou,
aoc: 'aocUID',
state: 'ACCEPTED_HERE',
level: 'KaTJLhGmU95',
permissions: {
mayApprove: true,
mayUnapprove: true,
mayAccept: false,
mayUnaccept: true,
mayReadData: true,
},
},
]
},
}

export const CustomRequests = () => {
const engine = useDataEngine()
const fetchOrgData = useFetchOrgData()
const fetchRootOrgData = useFetchRootOrgData()

const fetchOrgDataWithApprovalStatus = async args => {
const { id } = args

const [approvalStatusResponse, origResponse] = await Promise.all([
await engine.query({
approvalStatuses: {
resource: 'dataApprovals/approvals',
params: {
wf: 'rIUL4hYOjJc',
pe: '202101',
ou: id,
},
},
}),
await fetchOrgData(args),
])

const nextData = { ...approvalStatusResponse, ...origResponse }
return nextData
}

return (
<OrganisationUnitTreeControllable
fetchOrgData={fetchOrgDataWithApprovalStatus}
fetchRootOrgData={fetchRootOrgData}
name="Root org unit"
roots={['A0000000000']}
initiallyExpanded={['/A0000000000/A0000000001/A0000000003']}
renderNodeLabel={({ loading, node, additional }) => {
const { displayName } = node

if (loading) {
return displayName
}

const { approvalStatuses } = additional
const [approvalStatus] = approvalStatuses
const { permissions } = approvalStatus
const { mayApprove } = permissions

return (
<span>
{displayName} (mayApprove: {mayApprove.toString()})
</span>
)
}}
/>
)
}

CustomRequests.decorators = [
Story => (
<CustomDataProvider data={customDataWithApprovals}>
<Story />
</CustomDataProvider>
),
]
107 changes: 107 additions & 0 deletions components/organisation-unit-tree/src/__stories__/customData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
export const customData = {
organisationUnits: (...args) => {
const [, { id }] = args

let data,
delay = 0

if (id === 'A0000000000') {
delay = 1000
data = {
id: 'A0000000000',
path: '/A0000000000',
displayName: 'Org Unit 1',
children: [
{
id: 'A0000000001',
path: '/A0000000000/A0000000001',
children: [
{ id: 'A0000000003' },
{ id: 'A0000000004' },
],
displayName: 'Org Unit 2',
},
{
id: 'A0000000002',
path: '/A0000000000/A0000000002',
children: [],
displayName: 'Org Unit 3',
},
{
id: 'A0000000006',
path: '/A0000000000/A0000000006',
children: [],
displayName: 'Org Unit 7',
},
],
}
}

if (id === 'A0000000001') {
data = {
id: 'A0000000001',
path: '/A0000000000/A0000000001',
displayName: 'Org Unit 2',
children: [
{
id: 'A0000000003',
path: '/A0000000000/A0000000001/A0000000003',
children: [],
displayName: 'Org Unit 4',
},
{
id: 'A0000000004',
path: '/A0000000000/A0000000001/A0000000004',
children: [],
displayName: 'Org Unit 5',
},
],
}
}

if (id === 'A0000000002') {
delay = 1000
data = {
displayName: 'Org Unit 3',
id: 'A0000000002',
path: '/A0000000000/A0000000002',
children: [],
}
}

if (id === 'A0000000003') {
data = {
displayName: 'Org Unit 4',
id: 'A0000000003',
path: '/A0000000000/A0000000001/A0000000003',
children: [],
}
}

if (id === 'A0000000004') {
data = {
displayName: 'Org Unit 5',
id: 'A0000000004',
path: '/A0000000000/A0000000001/A0000000004',
children: [],
}
}

if (id === 'A0000000006') {
data = {
displayName: 'Org Unit 7',
id: 'A0000000006',
path: '/A0000000000/A0000000006',
children: [],
}
}

if (!data) {
return Promise.reject(new Error('404 - Org unit not found'))
}

return new Promise(resolve => {
setTimeout(() => resolve(data), delay)
})
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react'
import { Wrapper } from './dx-wrapper.js'

export const DxMultiSelection = () => <Wrapper />
DxMultiSelection.storyName = 'DX: Multi selection'
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react'
import { Wrapper } from './dx-wrapper.js'

export const DxNoSelection = () => <Wrapper disableSelection />
DxNoSelection.storyName = 'DX: No selection'
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react'
import { Wrapper } from './dx-wrapper.js'

export const DxSingleSelection = () => <Wrapper singleSelection />
DxSingleSelection.storyName = 'DX: Single selection'
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { DataProvider } from '@dhis2/app-runtime'
import React from 'react'
import { Wrapper } from './dx-wrapper.js'

export const DxWithRealBackend = () => (
<div>
<div style={{ marginBottom: 20, lineHeight: '28px' }}>
<b>
This story doesn&apos;t work on netlify for some reason, just
run it locally.
</b>
<br />
You need to log in to{' '}
<a
href="https://debug.dhis2.org/dev"
target="_blank"
rel="noopener noreferrer"
>
https://debug.dhis2.org/dev
</a>
<br />
Make sure the{' '}
<code style={{ background: '#ccc' }}>localhost:[PORT]</code> is part
of the accepted list:{' '}
<a
href="https://debug.dhis2.org/dev/dhis-web-settings/#/access"
target="_blank"
rel="noopener noreferrer"
>
Settings app / Access
</a>
</div>
<DataProvider baseUrl="https://debug.dhis2.org/dev" apiVersion="">
<Wrapper
//initiallyExpanded={['/ImspTQPwCqd/eIQbndfxQMb']}
suppressAlphabeticalSorting
roots="ImspTQPwCqd"
/>
</DataProvider>
</div>
)
DxWithRealBackend.storyName = 'DX: With real backend'
40 changes: 40 additions & 0 deletions components/organisation-unit-tree/src/__stories__/dx-wrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* eslint-disable react/prop-types */
import React, { useState } from 'react'
import { OrganisationUnitTree } from '../index.js'

const DX_onChange = (selected, setSelected, singleSelection) => ({
id,
path,
checked,
}) => {
console.log('onChange', { path, id, checked })
const pathIndex = selected.indexOf(path)

if (checked) {
setSelected(singleSelection ? [path] : [...selected, path])
} else {
setSelected(
singleSelection
? []
: [
...selected.slice(0, pathIndex),
...selected.slice(pathIndex + 1),
]
)
}
}

export const Wrapper = props => {
const [selected, setSelected] = useState([])

return (
<OrganisationUnitTree
name="Root org unit"
roots={['A0000000000']}
selected={selected}
onChange={DX_onChange(selected, setSelected, props.singleSelection)}
initiallyExpanded={['A0000000001/A0000000002']}
{...props}
/>
)
}
10 changes: 10 additions & 0 deletions components/organisation-unit-tree/src/__stories__/expanded.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react'
import { OrganisationUnitTree } from '../index.js'

export const Expanded = () => (
<OrganisationUnitTree
name="Root org unit"
roots={['A0000000000']}
initiallyExpanded={['/A0000000000/A0000000001']}
/>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react'
import { OrganisationUnitTree } from '../index.js'

export const FilteredRoot = () => (
<OrganisationUnitTree
name="Root org unit"
roots={['A0000000000', 'A0000000001']}
initiallyExpanded={['/A0000000000/A0000000001']}
filter={['/A0000000000']}
/>
)

FilteredRoot.storyName = 'Filtered (root)'
11 changes: 11 additions & 0 deletions components/organisation-unit-tree/src/__stories__/filtered.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react'
import { OrganisationUnitTree } from '../index.js'

export const Filtered = () => (
<OrganisationUnitTree
name="Root org unit"
roots={['A0000000000']}
initiallyExpanded={['/A0000000000/A0000000001']}
filter={['/A0000000000/A0000000001']}
/>
)
Loading