@dhis2-ui/selector-bar 10.16.1 → 10.16.3-alpha.1

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.
Files changed (38) hide show
  1. package/build/cjs/selector-bar-item/selector-bar-item.js +9 -0
  2. package/build/es/selector-bar-item/selector-bar-item.js +9 -0
  3. package/package.json +13 -12
  4. package/src/index.js +2 -0
  5. package/src/locales/ar/translations.json +3 -0
  6. package/src/locales/cs/translations.json +3 -0
  7. package/src/locales/en/translations.json +3 -0
  8. package/src/locales/es/translations.json +3 -0
  9. package/src/locales/es_419/translations.json +3 -0
  10. package/src/locales/fr/translations.json +3 -0
  11. package/src/locales/hi_IN/translations.json +3 -0
  12. package/src/locales/id/translations.json +3 -0
  13. package/src/locales/index.js +50 -0
  14. package/src/locales/lo/translations.json +3 -0
  15. package/src/locales/nb/translations.json +3 -0
  16. package/src/locales/pt/translations.json +3 -0
  17. package/src/locales/ru/translations.json +3 -0
  18. package/src/locales/uk/translations.json +3 -0
  19. package/src/locales/uz_UZ_Cyrl/translations.json +3 -0
  20. package/src/locales/uz_UZ_Latn/translations.json +3 -0
  21. package/src/locales/vi/translations.json +3 -0
  22. package/src/locales/zh/translations.json +3 -0
  23. package/src/locales/zh_CN/translations.json +3 -0
  24. package/src/selector-bar/__stories__/common.js +232 -0
  25. package/src/selector-bar/__stories__/keep-org-unit-expanded.js +71 -0
  26. package/src/selector-bar/__stories__/rtl.js +105 -0
  27. package/src/selector-bar/__stories__/with-multiple-rows.js +131 -0
  28. package/src/selector-bar/__stories__/with-right-hand-side-content.js +103 -0
  29. package/src/selector-bar/__stories__/with-some-inputs.js +95 -0
  30. package/src/selector-bar/index.js +1 -0
  31. package/src/selector-bar/selector-bar.js +171 -0
  32. package/src/selector-bar/selector-bar.prod.stories.js +6 -0
  33. package/src/selector-bar/selector-bar.test.js +124 -0
  34. package/src/selector-bar-item/features/toggle-children/index.js +27 -0
  35. package/src/selector-bar-item/features/toggle-children.feature +11 -0
  36. package/src/selector-bar-item/index.js +1 -0
  37. package/src/selector-bar-item/selector-bar-item.e2e.stories.js +64 -0
  38. package/src/selector-bar-item/selector-bar-item.js +190 -0
@@ -0,0 +1,64 @@
1
+ import { SingleSelectField, SingleSelectOption } from '@dhis2-ui/select'
2
+ import React, { useState } from 'react'
3
+ import { SelectorBarItem } from './selector-bar-item.js'
4
+
5
+ const options = [
6
+ { value: 'value 1', label: 'label 1' },
7
+ { value: 'value 2', label: 'label 2' },
8
+ { value: 'value 3', label: 'label 3' },
9
+ ]
10
+
11
+ const createStateDecorator = (args) => {
12
+ return (fn) => {
13
+ const [selected, setSelected] = useState(args?.selected || '')
14
+ const [open, setOpen] = useState(args?.open || false)
15
+ return fn({ selected, open, setSelected, setOpen })
16
+ }
17
+ }
18
+
19
+ const createStory =
20
+ () =>
21
+ (_, { open, setOpen, selected, setSelected }) =>
22
+ (
23
+ <SelectorBarItem
24
+ label="Selection bar item"
25
+ noValueMessage="No value message"
26
+ open={open}
27
+ setOpen={setOpen}
28
+ value={
29
+ options.find(({ value }) => value === selected)?.label || ''
30
+ }
31
+ >
32
+ <SingleSelectField
33
+ selected={selected}
34
+ onChange={({ selected: nextSelected }) => {
35
+ setSelected(nextSelected)
36
+ setOpen(false)
37
+ }}
38
+ >
39
+ {options.map(({ value, label }) => (
40
+ <SingleSelectOption
41
+ key={value}
42
+ value={value}
43
+ label={label}
44
+ />
45
+ ))}
46
+ </SingleSelectField>
47
+ </SelectorBarItem>
48
+ )
49
+
50
+ export default { title: 'SelectorBarItem' }
51
+
52
+ export const ClosedNoValue = createStory()
53
+ ClosedNoValue.decorators = [createStateDecorator()]
54
+
55
+ export const OpenNoValue = createStory()
56
+ OpenNoValue.decorators = [createStateDecorator({ open: true })]
57
+
58
+ export const ClosedWithValue = createStory()
59
+ ClosedWithValue.decorators = [createStateDecorator({ selected: 'value 1' })]
60
+
61
+ export const OpenWithValue = createStory()
62
+ OpenWithValue.decorators = [
63
+ createStateDecorator({ open: true, selected: 'value 1' }),
64
+ ]
@@ -0,0 +1,190 @@
1
+ import { colors, spacers } from '@dhis2/ui-constants'
2
+ import { IconChevronUp24, IconChevronDown24 } from '@dhis2/ui-icons'
3
+ import { Card } from '@dhis2-ui/card'
4
+ import { Layer } from '@dhis2-ui/layer'
5
+ import { Popper } from '@dhis2-ui/popper'
6
+ import cx from 'classnames'
7
+ import PropTypes from 'prop-types'
8
+ import React, { useRef } from 'react'
9
+
10
+ const offsetModifier = {
11
+ name: 'offset',
12
+ options: {
13
+ offset: [0, 2],
14
+ },
15
+ }
16
+
17
+ /**
18
+ * The reason this component expects the "open" state and "setOpen" function is
19
+ * because certain actions inside the props.children might have to set "open"
20
+ * to false as well.
21
+ * A good example is selecting a value (which is not a click outside) and this
22
+ * component neither has any control over that component nor does it have a way
23
+ * to "observe" that behavior. Especially for selection bar items that allow
24
+ * "multiple" selections, this would be more or less impossible to predict
25
+ * inside this component
26
+ */
27
+ export const SelectorBarItem = ({
28
+ children,
29
+ className,
30
+ dataTest = 'dhis2-ui-selectorbaritem',
31
+ disabled,
32
+ displayOnly,
33
+ label,
34
+ noValueMessage,
35
+ open,
36
+ setOpen,
37
+ value,
38
+ onClearSelectionClick,
39
+ }) => {
40
+ const buttonRef = useRef()
41
+ const Icon = open ? IconChevronUp24 : IconChevronDown24
42
+
43
+ const handleKeyDown = (evt) => {
44
+ if (open && setOpen && evt.key === 'Tab') {
45
+ // Move focus to trigger and defer close so Tab advances before the popup unmounts.
46
+ buttonRef.current?.focus()
47
+ requestAnimationFrame(() => setOpen(false))
48
+ }
49
+ }
50
+
51
+ return (
52
+ <button
53
+ ref={buttonRef}
54
+ className={cx(
55
+ 'selector-bar-item',
56
+ className,
57
+ !displayOnly ? 'openable' : ''
58
+ )}
59
+ disabled={disabled}
60
+ onClick={() => setOpen && setOpen(true)}
61
+ onKeyDown={handleKeyDown}
62
+ data-test={dataTest}
63
+ >
64
+ <span className="label">{label}</span>
65
+
66
+ {!disabled && (
67
+ <>
68
+ <span className="value">{value || noValueMessage}</span>
69
+ {value && onClearSelectionClick && (
70
+ <span
71
+ className="clear-icon"
72
+ onClick={(evt) => {
73
+ evt.stopPropagation()
74
+ onClearSelectionClick()
75
+ }}
76
+ data-test={`${dataTest}-clear-icon`}
77
+ >
78
+ <svg
79
+ width="14"
80
+ height="14"
81
+ viewBox="0 0 14 14"
82
+ fill="none"
83
+ xmlns="http://www.w3.org/2000/svg"
84
+ >
85
+ <path d="M7 14C10.866 14 14 10.866 14 7C14 3.13401 10.866 0 7 0C3.13401 0 0 3.13401 0 7C0 10.866 3.13401 14 7 14ZM4.29289 4.29289C4.68342 3.90237 5.31658 3.90237 5.70711 4.29289L7 5.58579L8.29289 4.29289C8.68342 3.90237 9.31658 3.90237 9.70711 4.29289C10.0976 4.68342 10.0976 5.31658 9.70711 5.70711L8.41421 7L9.70711 8.29289C10.0976 8.68342 10.0976 9.31658 9.70711 9.70711C9.31658 10.0976 8.68342 10.0976 8.29289 9.70711L7 8.41421L5.70711 9.70711C5.31658 10.0976 4.68342 10.0976 4.29289 9.70711C3.90237 9.31658 3.90237 8.68342 4.29289 8.29289L5.58579 7L4.29289 5.70711C3.90237 5.31658 3.90237 4.68342 4.29289 4.29289Z" />
86
+ </svg>
87
+ </span>
88
+ )}
89
+ </>
90
+ )}
91
+
92
+ {!displayOnly && (
93
+ <span className="toggle-icon">
94
+ <Icon />
95
+ </span>
96
+ )}
97
+
98
+ {open && (
99
+ <Layer
100
+ onBackdropClick={(_, evt) => {
101
+ evt.stopPropagation()
102
+ setOpen(false)
103
+ }}
104
+ >
105
+ <Popper
106
+ reference={buttonRef}
107
+ placement="bottom-start"
108
+ modifiers={[offsetModifier]}
109
+ >
110
+ <Card>{children}</Card>
111
+ </Popper>
112
+ </Layer>
113
+ )}
114
+
115
+ <style jsx>{`
116
+ .selector-bar-item {
117
+ display: flex;
118
+ background: none;
119
+ height: 40px;
120
+ align-items: center;
121
+ ${
122
+ /*
123
+ * The arrow icon has ~4px empty space on the sides,
124
+ * that's why the padding on the right is only 8px.
125
+ */ ''
126
+ }
127
+ padding-block: 0;
128
+ padding-inline-start: ${spacers.dp8};
129
+ padding-inline-end: ${spacers.dp12};
130
+ font-size: 14px;
131
+ line-height: 16px;
132
+ border: none;
133
+ box-shadow: 0px 0px 0px 1px ${colors.grey400};
134
+ }
135
+
136
+ .selector-bar-item.openable {
137
+ cursor: pointer;
138
+ }
139
+
140
+ .selector-bar-item:disabled {
141
+ cursor: not-allowed;
142
+ }
143
+
144
+ .label {
145
+ color: ${colors.grey600};
146
+ }
147
+
148
+ .value {
149
+ padding-inline-start: ${spacers.dp8};
150
+ }
151
+
152
+ .clear-icon {
153
+ display: flex;
154
+ align-items: center;
155
+ margin-inline-start: ${spacers.dp4};
156
+ padding: ${spacers.dp4};
157
+ cursor: pointer;
158
+ }
159
+ .clear-icon svg path {
160
+ fill: ${colors.grey500};
161
+ }
162
+
163
+ .clear-icon:hover svg path {
164
+ fill: ${colors.grey700};
165
+ }
166
+
167
+ .toggle-icon {
168
+ display: flex;
169
+ margin-inline-start: ${spacers.dp4};
170
+ height: 100%;
171
+ align-items: center;
172
+ }
173
+ `}</style>
174
+ </button>
175
+ )
176
+ }
177
+
178
+ SelectorBarItem.propTypes = {
179
+ label: PropTypes.string.isRequired,
180
+ children: PropTypes.any,
181
+ className: PropTypes.string,
182
+ dataTest: PropTypes.string,
183
+ disabled: PropTypes.bool,
184
+ displayOnly: PropTypes.bool,
185
+ noValueMessage: PropTypes.string,
186
+ open: PropTypes.bool,
187
+ setOpen: PropTypes.func,
188
+ value: PropTypes.string,
189
+ onClearSelectionClick: PropTypes.func,
190
+ }