@dhis2-ui/selector-bar 10.16.2 → 10.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -12
- package/src/index.js +2 -0
- package/src/locales/ar/translations.json +3 -0
- package/src/locales/cs/translations.json +3 -0
- package/src/locales/en/translations.json +3 -0
- package/src/locales/es/translations.json +3 -0
- package/src/locales/es_419/translations.json +3 -0
- package/src/locales/fr/translations.json +3 -0
- package/src/locales/hi_IN/translations.json +3 -0
- package/src/locales/id/translations.json +3 -0
- package/src/locales/index.js +50 -0
- package/src/locales/lo/translations.json +3 -0
- package/src/locales/nb/translations.json +3 -0
- package/src/locales/pt/translations.json +3 -0
- package/src/locales/ru/translations.json +3 -0
- package/src/locales/uk/translations.json +3 -0
- package/src/locales/uz_UZ_Cyrl/translations.json +3 -0
- package/src/locales/uz_UZ_Latn/translations.json +3 -0
- package/src/locales/vi/translations.json +3 -0
- package/src/locales/zh/translations.json +3 -0
- package/src/locales/zh_CN/translations.json +3 -0
- package/src/selector-bar/__stories__/common.js +232 -0
- package/src/selector-bar/__stories__/keep-org-unit-expanded.js +71 -0
- package/src/selector-bar/__stories__/rtl.js +105 -0
- package/src/selector-bar/__stories__/with-multiple-rows.js +131 -0
- package/src/selector-bar/__stories__/with-right-hand-side-content.js +103 -0
- package/src/selector-bar/__stories__/with-some-inputs.js +95 -0
- package/src/selector-bar/index.js +1 -0
- package/src/selector-bar/selector-bar.js +171 -0
- package/src/selector-bar/selector-bar.prod.stories.js +6 -0
- package/src/selector-bar/selector-bar.test.js +124 -0
- package/src/selector-bar-item/features/toggle-children/index.js +27 -0
- package/src/selector-bar-item/features/toggle-children.feature +11 -0
- package/src/selector-bar-item/index.js +1 -0
- package/src/selector-bar-item/selector-bar-item.e2e.stories.js +64 -0
- package/src/selector-bar-item/selector-bar-item.js +190 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { OrganisationUnitTree } from '@dhis2-ui/organisation-unit-tree'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { SelectorBar, SelectorBarItem } from '../../index.js'
|
|
4
|
+
import {
|
|
5
|
+
MenuSelect,
|
|
6
|
+
createDecoratorCustomDataProvider,
|
|
7
|
+
createStatefulDecorator,
|
|
8
|
+
dataSets,
|
|
9
|
+
decoratorCommonStyles,
|
|
10
|
+
workflows,
|
|
11
|
+
} from './common.js'
|
|
12
|
+
|
|
13
|
+
export const WithMultipleRows = (
|
|
14
|
+
_,
|
|
15
|
+
{
|
|
16
|
+
workflow,
|
|
17
|
+
setWorkflow,
|
|
18
|
+
workflowOpen,
|
|
19
|
+
setWorkflowOpen,
|
|
20
|
+
dataSet,
|
|
21
|
+
setDataSet,
|
|
22
|
+
dataSetOpen,
|
|
23
|
+
setDataSetOpen,
|
|
24
|
+
orgUnit,
|
|
25
|
+
setOrgUnit,
|
|
26
|
+
orgUnitOpen,
|
|
27
|
+
setOrgUnitOpen,
|
|
28
|
+
}
|
|
29
|
+
) => {
|
|
30
|
+
return (
|
|
31
|
+
<div className="narrow-container">
|
|
32
|
+
<SelectorBar
|
|
33
|
+
disableClearSelections
|
|
34
|
+
onClearSelectionClick={() => null}
|
|
35
|
+
>
|
|
36
|
+
<SelectorBarItem
|
|
37
|
+
disabled={false}
|
|
38
|
+
label="Workflow"
|
|
39
|
+
value={workflow?.label}
|
|
40
|
+
noValueMessage="Choose a workflow"
|
|
41
|
+
open={workflowOpen}
|
|
42
|
+
setOpen={setWorkflowOpen}
|
|
43
|
+
>
|
|
44
|
+
<MenuSelect
|
|
45
|
+
values={workflows}
|
|
46
|
+
selected={workflow?.value}
|
|
47
|
+
onChange={({ selected }) => {
|
|
48
|
+
setWorkflow(
|
|
49
|
+
workflows.find(
|
|
50
|
+
(currentWorkflow) =>
|
|
51
|
+
selected === currentWorkflow.value
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
setWorkflowOpen(false)
|
|
55
|
+
}}
|
|
56
|
+
/>
|
|
57
|
+
</SelectorBarItem>
|
|
58
|
+
|
|
59
|
+
<SelectorBarItem
|
|
60
|
+
disabled={!workflow}
|
|
61
|
+
label="Data set"
|
|
62
|
+
value={dataSet?.label}
|
|
63
|
+
noValueMessage="Choose a data set"
|
|
64
|
+
open={dataSetOpen}
|
|
65
|
+
setOpen={setDataSetOpen}
|
|
66
|
+
>
|
|
67
|
+
<MenuSelect
|
|
68
|
+
values={dataSets}
|
|
69
|
+
selected={dataSet?.value}
|
|
70
|
+
onChange={({ selected }) => {
|
|
71
|
+
setDataSet(
|
|
72
|
+
dataSets.find(
|
|
73
|
+
(currentWorkflow) =>
|
|
74
|
+
selected === currentWorkflow.value
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
setDataSetOpen(false)
|
|
78
|
+
}}
|
|
79
|
+
/>
|
|
80
|
+
</SelectorBarItem>
|
|
81
|
+
|
|
82
|
+
<SelectorBarItem
|
|
83
|
+
disabled={!workflow && !dataSet}
|
|
84
|
+
label="Org unit"
|
|
85
|
+
value={orgUnit?.displayName}
|
|
86
|
+
noValueMessage="Choose an organisation unit"
|
|
87
|
+
open={orgUnitOpen}
|
|
88
|
+
setOpen={setOrgUnitOpen}
|
|
89
|
+
>
|
|
90
|
+
<div
|
|
91
|
+
style={{
|
|
92
|
+
width: 400,
|
|
93
|
+
minHeight: 400,
|
|
94
|
+
maxHeight: '70vh',
|
|
95
|
+
}}
|
|
96
|
+
>
|
|
97
|
+
<OrganisationUnitTree
|
|
98
|
+
singleSelection
|
|
99
|
+
onChange={(nextOrgUnit, evt) => {
|
|
100
|
+
evt.stopPropagation()
|
|
101
|
+
setOrgUnit(nextOrgUnit)
|
|
102
|
+
setOrgUnitOpen(false)
|
|
103
|
+
}}
|
|
104
|
+
roots={['A0000000000']}
|
|
105
|
+
selected={orgUnit ? [orgUnit.path] : []}
|
|
106
|
+
/>
|
|
107
|
+
</div>
|
|
108
|
+
</SelectorBarItem>
|
|
109
|
+
</SelectorBar>
|
|
110
|
+
|
|
111
|
+
<p style={{ padding: '0 16px', textAlign: 'center' }}>
|
|
112
|
+
This story shows the selection bar component with multiple rows
|
|
113
|
+
when a workflow has been selected.
|
|
114
|
+
</p>
|
|
115
|
+
|
|
116
|
+
<style jsx>{`
|
|
117
|
+
.narrow-container {
|
|
118
|
+
margin: 0 auto;
|
|
119
|
+
width: 100%;
|
|
120
|
+
max-width: 600px;
|
|
121
|
+
}
|
|
122
|
+
`}</style>
|
|
123
|
+
</div>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
WithMultipleRows.decorators = [
|
|
128
|
+
decoratorCommonStyles,
|
|
129
|
+
createStatefulDecorator({ workflow: workflows[0] }),
|
|
130
|
+
createDecoratorCustomDataProvider(),
|
|
131
|
+
]
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Button } from '@dhis2-ui/button'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { SelectorBar, SelectorBarItem } from '../../index.js'
|
|
4
|
+
import {
|
|
5
|
+
OrgUnitSelect,
|
|
6
|
+
MenuSelect,
|
|
7
|
+
createDecoratorCustomDataProvider,
|
|
8
|
+
createStatefulDecorator,
|
|
9
|
+
decoratorCommonStyles,
|
|
10
|
+
workflows,
|
|
11
|
+
} from './common.js'
|
|
12
|
+
|
|
13
|
+
const RHSContent = () => (
|
|
14
|
+
<div className="rhs-content">
|
|
15
|
+
<Button small>Options</Button>
|
|
16
|
+
<style jsx>{`
|
|
17
|
+
.rhs-content {
|
|
18
|
+
display: flex;
|
|
19
|
+
height: 40px;
|
|
20
|
+
padding: 0 16px;
|
|
21
|
+
align-items: center;
|
|
22
|
+
}
|
|
23
|
+
`}</style>
|
|
24
|
+
</div>
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
export const WithAdditionalContent = (
|
|
28
|
+
_,
|
|
29
|
+
{
|
|
30
|
+
workflow,
|
|
31
|
+
setWorkflow,
|
|
32
|
+
workflowOpen,
|
|
33
|
+
setWorkflowOpen,
|
|
34
|
+
orgUnit,
|
|
35
|
+
setOrgUnit,
|
|
36
|
+
orgUnitOpen,
|
|
37
|
+
setOrgUnitOpen,
|
|
38
|
+
}
|
|
39
|
+
) => {
|
|
40
|
+
return (
|
|
41
|
+
<SelectorBar
|
|
42
|
+
disableClearSelections={!workflow || !orgUnit}
|
|
43
|
+
onClearSelectionClick={() => {
|
|
44
|
+
setWorkflow(null)
|
|
45
|
+
setOrgUnit(null)
|
|
46
|
+
}}
|
|
47
|
+
additionalContent={<RHSContent />}
|
|
48
|
+
>
|
|
49
|
+
<SelectorBarItem
|
|
50
|
+
disabled={false}
|
|
51
|
+
label="Workflow"
|
|
52
|
+
value={workflow?.label}
|
|
53
|
+
noValueMessage="Choose a workflow"
|
|
54
|
+
open={workflowOpen}
|
|
55
|
+
setOpen={setWorkflowOpen}
|
|
56
|
+
onClearSelectionClick={() => {
|
|
57
|
+
setWorkflow(null)
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
<MenuSelect
|
|
61
|
+
values={workflows}
|
|
62
|
+
selected={workflow?.value}
|
|
63
|
+
onChange={({ selected }) => {
|
|
64
|
+
setWorkflow(
|
|
65
|
+
workflows.find(
|
|
66
|
+
(currentWorkflow) =>
|
|
67
|
+
selected === currentWorkflow.value
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
setWorkflowOpen(false)
|
|
71
|
+
}}
|
|
72
|
+
/>
|
|
73
|
+
</SelectorBarItem>
|
|
74
|
+
|
|
75
|
+
<SelectorBarItem
|
|
76
|
+
disabled={!workflow}
|
|
77
|
+
label="Org unit"
|
|
78
|
+
value={orgUnit?.displayName}
|
|
79
|
+
noValueMessage="Choose an organisation unit"
|
|
80
|
+
open={orgUnitOpen}
|
|
81
|
+
setOpen={setOrgUnitOpen}
|
|
82
|
+
onClearSelectionClick={() => {
|
|
83
|
+
setOrgUnit(null)
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
<OrgUnitSelect
|
|
87
|
+
selected={orgUnit ? [orgUnit.path] : []}
|
|
88
|
+
onChange={(nextOrgUnit, evt) => {
|
|
89
|
+
evt.stopPropagation()
|
|
90
|
+
setOrgUnit(nextOrgUnit)
|
|
91
|
+
setOrgUnitOpen(false)
|
|
92
|
+
}}
|
|
93
|
+
/>
|
|
94
|
+
</SelectorBarItem>
|
|
95
|
+
</SelectorBar>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
WithAdditionalContent.decorators = [
|
|
100
|
+
decoratorCommonStyles,
|
|
101
|
+
createStatefulDecorator(),
|
|
102
|
+
createDecoratorCustomDataProvider(),
|
|
103
|
+
]
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { SelectorBar, SelectorBarItem } from '../../index.js'
|
|
3
|
+
import {
|
|
4
|
+
OrgUnitSelect,
|
|
5
|
+
MenuSelect,
|
|
6
|
+
createDecoratorCustomDataProvider,
|
|
7
|
+
createStatefulDecorator,
|
|
8
|
+
decoratorCommonStyles,
|
|
9
|
+
workflows,
|
|
10
|
+
} from './common.js'
|
|
11
|
+
|
|
12
|
+
export const WithSomeInputs = (
|
|
13
|
+
_,
|
|
14
|
+
{
|
|
15
|
+
workflow,
|
|
16
|
+
setWorkflow,
|
|
17
|
+
workflowOpen,
|
|
18
|
+
setWorkflowOpen,
|
|
19
|
+
orgUnit,
|
|
20
|
+
setOrgUnit,
|
|
21
|
+
orgUnitOpen,
|
|
22
|
+
setOrgUnitOpen,
|
|
23
|
+
}
|
|
24
|
+
) => {
|
|
25
|
+
return (
|
|
26
|
+
<SelectorBar
|
|
27
|
+
disableClearSelections={!workflow && !orgUnit}
|
|
28
|
+
onClearSelectionClick={() => {
|
|
29
|
+
setWorkflow(null)
|
|
30
|
+
setOrgUnit(null)
|
|
31
|
+
}}
|
|
32
|
+
>
|
|
33
|
+
<SelectorBarItem
|
|
34
|
+
disabled={false}
|
|
35
|
+
label="Workflow"
|
|
36
|
+
value={workflow?.label}
|
|
37
|
+
noValueMessage="Choose a workflow"
|
|
38
|
+
open={workflowOpen}
|
|
39
|
+
setOpen={setWorkflowOpen}
|
|
40
|
+
onClearSelectionClick={() => {
|
|
41
|
+
setWorkflow(null)
|
|
42
|
+
}}
|
|
43
|
+
>
|
|
44
|
+
<MenuSelect
|
|
45
|
+
values={workflows}
|
|
46
|
+
selected={workflow?.value}
|
|
47
|
+
onChange={({ selected }) => {
|
|
48
|
+
setWorkflow(
|
|
49
|
+
workflows.find(
|
|
50
|
+
(currentWorkflow) =>
|
|
51
|
+
selected === currentWorkflow.value
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
setWorkflowOpen(false)
|
|
55
|
+
}}
|
|
56
|
+
/>
|
|
57
|
+
</SelectorBarItem>
|
|
58
|
+
|
|
59
|
+
<SelectorBarItem
|
|
60
|
+
disabled={!workflow}
|
|
61
|
+
label="Org unit"
|
|
62
|
+
value={orgUnit?.displayName}
|
|
63
|
+
noValueMessage="Choose an organisation unit"
|
|
64
|
+
open={orgUnitOpen}
|
|
65
|
+
setOpen={setOrgUnitOpen}
|
|
66
|
+
onClearSelectionClick={() => {
|
|
67
|
+
setOrgUnit(null)
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
<OrgUnitSelect
|
|
71
|
+
selected={orgUnit ? [orgUnit.path] : []}
|
|
72
|
+
onChange={(nextOrgUnit, evt) => {
|
|
73
|
+
evt.stopPropagation()
|
|
74
|
+
setOrgUnit(nextOrgUnit)
|
|
75
|
+
setOrgUnitOpen(false)
|
|
76
|
+
}}
|
|
77
|
+
/>
|
|
78
|
+
</SelectorBarItem>
|
|
79
|
+
<SelectorBarItem
|
|
80
|
+
displayOnly={true}
|
|
81
|
+
label="Person"
|
|
82
|
+
value="John doe"
|
|
83
|
+
onClearSelectionClick={() => {
|
|
84
|
+
alert('This selection would be cleared')
|
|
85
|
+
}}
|
|
86
|
+
/>
|
|
87
|
+
</SelectorBar>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
WithSomeInputs.decorators = [
|
|
92
|
+
decoratorCommonStyles,
|
|
93
|
+
createStatefulDecorator({ workflow: workflows[0] }),
|
|
94
|
+
createDecoratorCustomDataProvider(),
|
|
95
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SelectorBar } from './selector-bar.js'
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { colors, spacers } from '@dhis2/ui-constants'
|
|
2
|
+
import { Button } from '@dhis2-ui/button'
|
|
3
|
+
import cx from 'classnames'
|
|
4
|
+
import PropTypes from 'prop-types'
|
|
5
|
+
import React, { useRef, useState, useEffect } from 'react'
|
|
6
|
+
import i18n from '../locales/index.js'
|
|
7
|
+
|
|
8
|
+
const ClearSelection = ({ disabled, onClick }) => {
|
|
9
|
+
return (
|
|
10
|
+
<div className="clear-selections">
|
|
11
|
+
<Button small secondary disabled={disabled} onClick={onClick}>
|
|
12
|
+
{i18n.t('Clear selections')}
|
|
13
|
+
</Button>
|
|
14
|
+
|
|
15
|
+
<style jsx>{`
|
|
16
|
+
.clear-selections {
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
height: 40px;
|
|
20
|
+
padding: 0 ${spacers.dp8};
|
|
21
|
+
}
|
|
22
|
+
`}</style>
|
|
23
|
+
</div>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
ClearSelection.propTypes = {
|
|
28
|
+
disabled: PropTypes.bool,
|
|
29
|
+
onClick: PropTypes.func,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const SelectorBar = ({
|
|
33
|
+
className,
|
|
34
|
+
children,
|
|
35
|
+
onClearSelectionClick,
|
|
36
|
+
dataTest = 'dhis2-ui-selectorbar',
|
|
37
|
+
disableClearSelections,
|
|
38
|
+
additionalContent,
|
|
39
|
+
ariaLabel,
|
|
40
|
+
}) => {
|
|
41
|
+
const container = useRef(null)
|
|
42
|
+
|
|
43
|
+
const [childrenToFocus, setChildrenToFocus] = useState([])
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (container.current) {
|
|
47
|
+
const controlsDiv = container.current.querySelector('.controls')
|
|
48
|
+
if (controlsDiv) {
|
|
49
|
+
const childElements = Array.from(controlsDiv.children)
|
|
50
|
+
childElements.forEach((child) => {
|
|
51
|
+
child.tabIndex = -1
|
|
52
|
+
})
|
|
53
|
+
setChildrenToFocus(childElements)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}, [children])
|
|
57
|
+
|
|
58
|
+
const handleKeyDown = (event) => {
|
|
59
|
+
const currentFocus = document.activeElement
|
|
60
|
+
|
|
61
|
+
if (container.current && container.current === currentFocus) {
|
|
62
|
+
if (childrenToFocus.length > 0 && childrenToFocus[0]) {
|
|
63
|
+
childrenToFocus[0].focus()
|
|
64
|
+
}
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
if (!childrenToFocus.length) {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const currentIndex = childrenToFocus.findIndex(
|
|
72
|
+
(element) => element === currentFocus
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if (currentIndex === -1) {
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (event.key === 'ArrowRight') {
|
|
80
|
+
event.preventDefault()
|
|
81
|
+
const nextIndex = (currentIndex + 1) % childrenToFocus.length
|
|
82
|
+
childrenToFocus[nextIndex].focus()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (event.key === 'ArrowLeft') {
|
|
86
|
+
event.preventDefault()
|
|
87
|
+
const prevIndex =
|
|
88
|
+
(currentIndex - 1 + childrenToFocus.length) %
|
|
89
|
+
childrenToFocus.length
|
|
90
|
+
childrenToFocus[prevIndex].focus()
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<>
|
|
96
|
+
<div
|
|
97
|
+
className={cx(
|
|
98
|
+
'container',
|
|
99
|
+
{ withRHSContents: additionalContent },
|
|
100
|
+
className
|
|
101
|
+
)}
|
|
102
|
+
data-test={dataTest}
|
|
103
|
+
onKeyDown={handleKeyDown}
|
|
104
|
+
tabIndex={0}
|
|
105
|
+
ref={container}
|
|
106
|
+
role="toolbar"
|
|
107
|
+
aria-label={ariaLabel}
|
|
108
|
+
>
|
|
109
|
+
<div className="controls">
|
|
110
|
+
{/* {React.Children.map(children, (child, index) =>
|
|
111
|
+
React.cloneElement(child, {
|
|
112
|
+
ref: childrenRefs[index],
|
|
113
|
+
})
|
|
114
|
+
)} */}
|
|
115
|
+
{children}
|
|
116
|
+
{onClearSelectionClick && (
|
|
117
|
+
<ClearSelection
|
|
118
|
+
disabled={disableClearSelections}
|
|
119
|
+
onClick={onClearSelectionClick}
|
|
120
|
+
/>
|
|
121
|
+
)}
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
{additionalContent && (
|
|
125
|
+
<div className="additional-contents">
|
|
126
|
+
{additionalContent}
|
|
127
|
+
</div>
|
|
128
|
+
)}
|
|
129
|
+
|
|
130
|
+
<style jsx>{`
|
|
131
|
+
.container {
|
|
132
|
+
background: ${colors.white};
|
|
133
|
+
box-shadow: inset 0 -1px 0 0 ${colors.grey400};
|
|
134
|
+
padding-bottom: 1px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.withRHSContents {
|
|
138
|
+
display: flex;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.controls {
|
|
142
|
+
display: flex;
|
|
143
|
+
flex-wrap: wrap;
|
|
144
|
+
flex-grow: 1;
|
|
145
|
+
gap: 1px;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.additional-contents {
|
|
149
|
+
${
|
|
150
|
+
/*
|
|
151
|
+
* Specs define the space to be 12px.
|
|
152
|
+
* 8px already come from the clear selection component
|
|
153
|
+
*/ ''
|
|
154
|
+
}
|
|
155
|
+
padding-inline-start: 4px;
|
|
156
|
+
}
|
|
157
|
+
`}</style>
|
|
158
|
+
</div>
|
|
159
|
+
</>
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
SelectorBar.propTypes = {
|
|
164
|
+
children: PropTypes.any.isRequired,
|
|
165
|
+
additionalContent: PropTypes.any,
|
|
166
|
+
ariaLabel: PropTypes.string,
|
|
167
|
+
className: PropTypes.string,
|
|
168
|
+
dataTest: PropTypes.string,
|
|
169
|
+
disableClearSelections: PropTypes.bool,
|
|
170
|
+
onClearSelectionClick: PropTypes.func,
|
|
171
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export default { title: 'SelectionBar' }
|
|
2
|
+
export { WithSomeInputs } from './__stories__/with-some-inputs.js'
|
|
3
|
+
export { WithAdditionalContent } from './__stories__/with-right-hand-side-content.js'
|
|
4
|
+
export { KeepOrgUnitExpanded } from './__stories__/keep-org-unit-expanded.js'
|
|
5
|
+
export { WithMultipleRows } from './__stories__/with-multiple-rows.js'
|
|
6
|
+
export { RTL } from './__stories__/rtl.js'
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
import { render, screen } from '@testing-library/react'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { SelectorBarItem } from '../selector-bar-item/index.js'
|
|
5
|
+
import { SelectorBar } from './selector-bar.js'
|
|
6
|
+
|
|
7
|
+
const noop = () => null
|
|
8
|
+
|
|
9
|
+
describe('SelectorBar', () => {
|
|
10
|
+
it('should render the selection bar items', () => {
|
|
11
|
+
render(
|
|
12
|
+
<SelectorBar>
|
|
13
|
+
<SelectorBarItem
|
|
14
|
+
label="Selection bar item 1"
|
|
15
|
+
noValueMessage="No value message 1"
|
|
16
|
+
open={false}
|
|
17
|
+
setOpen={noop}
|
|
18
|
+
>
|
|
19
|
+
Content
|
|
20
|
+
</SelectorBarItem>
|
|
21
|
+
|
|
22
|
+
<SelectorBarItem
|
|
23
|
+
label="Selection bar item 2"
|
|
24
|
+
noValueMessage="No value message 2"
|
|
25
|
+
open={false}
|
|
26
|
+
setOpen={noop}
|
|
27
|
+
>
|
|
28
|
+
Content
|
|
29
|
+
</SelectorBarItem>
|
|
30
|
+
</SelectorBar>
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
expect(screen.getByText('Selection bar item 1')).not.toBeNull()
|
|
34
|
+
expect(screen.getByText('Selection bar item 2')).not.toBeNull()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('should not disable the clear selection button by default', () => {
|
|
38
|
+
render(
|
|
39
|
+
<SelectorBar onClearSelectionClick={noop}>
|
|
40
|
+
<SelectorBarItem
|
|
41
|
+
label="label"
|
|
42
|
+
noValueMessage="msg"
|
|
43
|
+
open={false}
|
|
44
|
+
setOpen={noop}
|
|
45
|
+
>
|
|
46
|
+
Content
|
|
47
|
+
</SelectorBarItem>
|
|
48
|
+
</SelectorBar>
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
const clearBtn = screen.getByText('Clear selections')
|
|
52
|
+
expect(clearBtn).not.toBeNull()
|
|
53
|
+
expect(clearBtn).not.toBeDisabled()
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('should disable the clear selection button', () => {
|
|
57
|
+
render(
|
|
58
|
+
<SelectorBar disableClearSelections onClearSelectionClick={noop}>
|
|
59
|
+
<SelectorBarItem
|
|
60
|
+
label="label"
|
|
61
|
+
noValueMessage="msg"
|
|
62
|
+
open={false}
|
|
63
|
+
setOpen={noop}
|
|
64
|
+
>
|
|
65
|
+
Content
|
|
66
|
+
</SelectorBarItem>
|
|
67
|
+
</SelectorBar>
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
const clearBtn = screen.getByText('Clear selections')
|
|
71
|
+
expect(clearBtn).not.toBeNull()
|
|
72
|
+
expect(clearBtn).toBeDisabled()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should render the optional content', () => {
|
|
76
|
+
render(
|
|
77
|
+
<SelectorBar additionalContent={<div>Foobar</div>}>
|
|
78
|
+
<SelectorBarItem
|
|
79
|
+
label="label"
|
|
80
|
+
noValueMessage="msg"
|
|
81
|
+
open={false}
|
|
82
|
+
setOpen={noop}
|
|
83
|
+
>
|
|
84
|
+
Content
|
|
85
|
+
</SelectorBarItem>
|
|
86
|
+
</SelectorBar>
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
const extraContent = screen.getByText('Foobar')
|
|
90
|
+
expect(extraContent).not.toBeNull()
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('should render the displayOnly value', () => {
|
|
94
|
+
render(
|
|
95
|
+
<SelectorBar>
|
|
96
|
+
<SelectorBarItem
|
|
97
|
+
displayOnly={true}
|
|
98
|
+
label="Person"
|
|
99
|
+
value="John doe"
|
|
100
|
+
/>
|
|
101
|
+
</SelectorBar>
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
const value = screen.getByText('John doe')
|
|
105
|
+
expect(value).not.toBeNull()
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('should render the cross icon', () => {
|
|
109
|
+
render(
|
|
110
|
+
<SelectorBar>
|
|
111
|
+
<SelectorBarItem
|
|
112
|
+
label="label"
|
|
113
|
+
value="selected value"
|
|
114
|
+
onClearSelectionClick={noop}
|
|
115
|
+
/>
|
|
116
|
+
</SelectorBar>
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
const clearIcon = screen.getByTestId(
|
|
120
|
+
'dhis2-ui-selectorbaritem-clear-icon'
|
|
121
|
+
)
|
|
122
|
+
expect(clearIcon).not.toBeNull()
|
|
123
|
+
})
|
|
124
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
|
|
2
|
+
|
|
3
|
+
Given('the selector bar item is closed', () => {
|
|
4
|
+
cy.visitStory('SelectorBarItem', 'closed no value')
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
Given('the selector bar item is open', () => {
|
|
8
|
+
cy.visitStory('SelectorBarItem', 'open no value')
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
When('the user opens the selector bar item', () => {
|
|
12
|
+
cy.contains('Selection bar item', { selector: 'button' }).click()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
When('the user closes the selector bar item', () => {
|
|
16
|
+
cy.get('body').click('topLeft')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
Then('the children should be displayed', () => {
|
|
20
|
+
cy.get('[data-test="dhis2-uiwidgets-singleselectfield"]').should('exist')
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
Then('the children should not be displayed', () => {
|
|
24
|
+
cy.get('[data-test="dhis2-uiwidgets-singleselectfield"]').should(
|
|
25
|
+
'not.exist'
|
|
26
|
+
)
|
|
27
|
+
})
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Feature: The display of the children can be toggled
|
|
2
|
+
|
|
3
|
+
Scenario: The children are being opened
|
|
4
|
+
Given the selector bar item is closed
|
|
5
|
+
When the user opens the selector bar item
|
|
6
|
+
Then the children should be displayed
|
|
7
|
+
|
|
8
|
+
Scenario: The children are being closed
|
|
9
|
+
Given the selector bar item is open
|
|
10
|
+
When the user closes the selector bar item
|
|
11
|
+
Then the children should not be displayed
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SelectorBarItem } from './selector-bar-item.js'
|