@dhis2-ui/transfer 10.16.2 → 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 (109) hide show
  1. package/package.json +10 -9
  2. package/src/__e2e__/add_remove-highlighted-options.e2e.stories.js +30 -0
  3. package/src/__e2e__/common/options.js +90 -0
  4. package/src/__e2e__/common/stateful-decorator.js +33 -0
  5. package/src/__e2e__/common.js +0 -0
  6. package/src/__e2e__/disabled-transfer-buttons.e2e.stories.js +49 -0
  7. package/src/__e2e__/disabled-transfer-options.e2e.stories.js +21 -0
  8. package/src/__e2e__/display-order.e2e.stories.js +24 -0
  9. package/src/__e2e__/filter-options-list.e2e.stories.js +87 -0
  10. package/src/__e2e__/highlight-range-of-options.e2e.stories.js +52 -0
  11. package/src/__e2e__/loading_lists.e2e.stories.js +26 -0
  12. package/src/__e2e__/notify_at_end_of_list.e2e.stories.js +116 -0
  13. package/src/__e2e__/reorder-with-buttons.e2e.stories.js +35 -0
  14. package/src/__e2e__/set_unset-highlighted-option.e2e.stories.js +30 -0
  15. package/src/__e2e__/transferring-items.e2e.stories.js +27 -0
  16. package/src/__tests__/common.test.js +131 -0
  17. package/src/__tests__/helper/add-all-selectable-source-options.test.js +46 -0
  18. package/src/__tests__/helper/add-individual-source-options.test.js +80 -0
  19. package/src/__tests__/helper/default-filter-callback.test.js +45 -0
  20. package/src/__tests__/helper/is-reorder-down-disabled.test.js +96 -0
  21. package/src/__tests__/helper/is-reorder-up-disabled.test.js +96 -0
  22. package/src/__tests__/helper/move-highlighted-picked-option-down.test.js +111 -0
  23. package/src/__tests__/helper/move-highlighted-picked-option-to-bottom.test.js +101 -0
  24. package/src/__tests__/helper/move-highlighted-picked-option-to-top.test.js +101 -0
  25. package/src/__tests__/helper/move-highlighted-picked-option-up.test.js +111 -0
  26. package/src/__tests__/helper/remove-all-picked-options.test.js +29 -0
  27. package/src/__tests__/helper/remove-individual-picked-options.test.js +38 -0
  28. package/src/__tests__/helper/use-highlighted-option/create-toggle-highlighted-option.test.js +104 -0
  29. package/src/__tests__/helper/use-highlighted-option/toggle-add.test.js +84 -0
  30. package/src/__tests__/helper/use-highlighted-option/toggle-range.test.js +150 -0
  31. package/src/__tests__/helper/use-highlighted-option/toggle-replace.test.js +39 -0
  32. package/src/__tests__/helper/use-highlighted-option.test.js +41 -0
  33. package/src/__tests__/reordering-actions.test.js +165 -0
  34. package/src/__tests__/transfer.test.js +137 -0
  35. package/src/actions.js +33 -0
  36. package/src/add-all.js +27 -0
  37. package/src/add-individual.js +27 -0
  38. package/src/common/find-option-index.js +9 -0
  39. package/src/common/get-mode-by-modifier-key.js +35 -0
  40. package/src/common/index.js +5 -0
  41. package/src/common/is-option.js +7 -0
  42. package/src/common/modes.js +11 -0
  43. package/src/common/remove-option.js +19 -0
  44. package/src/common/toggle-value.js +18 -0
  45. package/src/container.js +23 -0
  46. package/src/end-intersection-detector.js +37 -0
  47. package/src/features/add_remove-highlighted-options/index.js +92 -0
  48. package/src/features/add_remove-highlighted-options.feature +41 -0
  49. package/src/features/common/index.js +8 -0
  50. package/src/features/disabled-transfer-buttons/index.js +118 -0
  51. package/src/features/disabled-transfer-buttons.feature +46 -0
  52. package/src/features/disabled-transfer-options/index.js +182 -0
  53. package/src/features/disabled-transfer-options.feature +42 -0
  54. package/src/features/display-order/index.js +205 -0
  55. package/src/features/display-order.feature +30 -0
  56. package/src/features/filter-options-list/index.js +133 -0
  57. package/src/features/filter-options-list.feature +40 -0
  58. package/src/features/highlight-range-of-options/index.js +336 -0
  59. package/src/features/highlight-range-of-options.feature +70 -0
  60. package/src/features/loading_lists/index.js +43 -0
  61. package/src/features/loading_lists.feature +19 -0
  62. package/src/features/notify_at_end_of_list/index.js +125 -0
  63. package/src/features/notify_at_end_of_list.feature +64 -0
  64. package/src/features/reorder-with-buttons/index.js +181 -0
  65. package/src/features/reorder-with-buttons.feature +138 -0
  66. package/src/features/set_unset-highlighted-option/index.js +121 -0
  67. package/src/features/set_unset-highlighted-option.feature +42 -0
  68. package/src/features/transferring-items/index.js +375 -0
  69. package/src/features/transferring-items.feature +44 -0
  70. package/src/filter.js +38 -0
  71. package/src/icons.js +194 -0
  72. package/src/index.js +2 -0
  73. package/src/left-footer.js +22 -0
  74. package/src/left-header.js +22 -0
  75. package/src/left-side.js +34 -0
  76. package/src/locales/en/translations.json +7 -0
  77. package/src/locales/index.js +16 -0
  78. package/src/options-container.js +127 -0
  79. package/src/remove-all.js +27 -0
  80. package/src/remove-individual.js +27 -0
  81. package/src/reordering-actions.js +136 -0
  82. package/src/right-footer.js +22 -0
  83. package/src/right-header.js +22 -0
  84. package/src/right-side.js +33 -0
  85. package/src/transfer/add-all-selectable-source-options.js +37 -0
  86. package/src/transfer/add-individual-source-options.js +61 -0
  87. package/src/transfer/create-double-click-handlers.js +36 -0
  88. package/src/transfer/default-filter-callback.js +17 -0
  89. package/src/transfer/get-highlighted-picked-indices.js +26 -0
  90. package/src/transfer/get-option-click-handlers.js +19 -0
  91. package/src/transfer/index.js +17 -0
  92. package/src/transfer/is-reorder-down-disabled.js +34 -0
  93. package/src/transfer/is-reorder-up-disabled.js +30 -0
  94. package/src/transfer/move-highlighted-picked-option-down.js +54 -0
  95. package/src/transfer/move-highlighted-picked-option-to-bottom.js +44 -0
  96. package/src/transfer/move-highlighted-picked-option-to-top.js +38 -0
  97. package/src/transfer/move-highlighted-picked-option-up.js +47 -0
  98. package/src/transfer/remove-all-picked-options.js +13 -0
  99. package/src/transfer/remove-individual-picked-options.js +49 -0
  100. package/src/transfer/use-filter.js +17 -0
  101. package/src/transfer/use-highlighted-options/create-toggle-highlighted-option.js +64 -0
  102. package/src/transfer/use-highlighted-options/toggle-add.js +20 -0
  103. package/src/transfer/use-highlighted-options/toggle-range.js +61 -0
  104. package/src/transfer/use-highlighted-options/toggle-replace.js +26 -0
  105. package/src/transfer/use-highlighted-options.js +34 -0
  106. package/src/transfer/use-options-key-monitor.js +41 -0
  107. package/src/transfer-option.js +91 -0
  108. package/src/transfer.js +539 -0
  109. package/src/transfer.prod.stories.js +621 -0
@@ -0,0 +1,150 @@
1
+ import { toggleRange } from '../../../transfer/use-highlighted-options/toggle-range.js'
2
+
3
+ describe('Transfer- useHighlightedOptions - toggleRange', () => {
4
+ const setHighlightedOptions = jest.fn()
5
+ const options = [
6
+ { label: 'Foo', value: 'foo' },
7
+ { label: 'Bar', value: 'bar' },
8
+ { label: 'Baz', value: 'baz' },
9
+ { label: 'Foobar', value: 'foobar' },
10
+ { label: 'Foobaz', value: 'foobaz' },
11
+ { label: 'Foobarbaz', value: 'foobarbaz' },
12
+ ]
13
+
14
+ afterEach(() => {
15
+ setHighlightedOptions.mockClear()
16
+ })
17
+
18
+ describe('maxSelections=1', () => {
19
+ const maxSelections = 1
20
+ const highlightedOptions = options.slice(0, 1).map(({ value }) => value)
21
+ const lastClicked = options[0].value
22
+
23
+ it('should replace the current highlighted option', () => {
24
+ const option = options[1]
25
+ const expected = [option.value]
26
+
27
+ toggleRange({
28
+ lastClicked,
29
+ maxSelections,
30
+ highlightedOptions,
31
+ option,
32
+ setHighlightedOptions,
33
+ options,
34
+ })
35
+
36
+ expect(setHighlightedOptions).toHaveBeenCalledWith(expected)
37
+ })
38
+
39
+ it('should not change the highlighted options', () => {
40
+ const option = options[0]
41
+ const expected = highlightedOptions
42
+
43
+ toggleRange({
44
+ lastClicked,
45
+ maxSelections,
46
+ highlightedOptions,
47
+ option,
48
+ setHighlightedOptions,
49
+ options,
50
+ })
51
+
52
+ expect(setHighlightedOptions).toHaveBeenCalledWith(expected)
53
+ })
54
+ })
55
+
56
+ describe('maxSelections=Infinity', () => {
57
+ const maxSelections = Infinity
58
+ const highlightedOptions = options.slice(0, 1).map(({ value }) => value)
59
+ const lastClicked = options[0].value
60
+
61
+ it('should not change the highlighted options', () => {
62
+ const option = options[0]
63
+ const expected = highlightedOptions
64
+
65
+ toggleRange({
66
+ lastClicked,
67
+ maxSelections,
68
+ highlightedOptions,
69
+ option,
70
+ setHighlightedOptions,
71
+ options,
72
+ })
73
+
74
+ expect(setHighlightedOptions).toHaveBeenCalledWith(expected)
75
+ })
76
+
77
+ it('should highlight all option from the highlighted one to the clicked one', () => {
78
+ const expected = options.slice(0, 4).map(({ value }) => value)
79
+
80
+ toggleRange({
81
+ option: options[3],
82
+ lastClicked,
83
+ maxSelections,
84
+ highlightedOptions,
85
+ setHighlightedOptions,
86
+ options,
87
+ })
88
+
89
+ expect(setHighlightedOptions).toHaveBeenCalledWith(expected)
90
+ })
91
+
92
+ it('should highlight from the lastClicked to the most recently clicked one', () => {
93
+ const expected = options.slice(1, 4).map(({ value }) => value)
94
+
95
+ toggleRange({
96
+ option: options[3],
97
+ lastClicked: options[1].value,
98
+ maxSelections,
99
+ highlightedOptions,
100
+ setHighlightedOptions,
101
+ options,
102
+ })
103
+
104
+ expect(setHighlightedOptions).toHaveBeenCalledWith(expected)
105
+ })
106
+
107
+ /**
108
+ * E. g. lastClicked is hidden because of a filter change
109
+ */
110
+ it('should highlight from the highest in list to the clicked when last clicked not visible anymore', () => {
111
+ const expected = options.slice(1, 6).map(({ value }) => value)
112
+
113
+ toggleRange({
114
+ option: options[5],
115
+ lastClicked: 'not visible anymore',
116
+ maxSelections,
117
+ highlightedOptions: [
118
+ ...options.slice(1, 2),
119
+ ...options.slice(3, 5),
120
+ ].map(({ value }) => value),
121
+ setHighlightedOptions,
122
+ options,
123
+ })
124
+
125
+ expect(setHighlightedOptions).toHaveBeenCalledWith(expected)
126
+ })
127
+
128
+ /**
129
+ * E. g. lastClicked is null because all options so far
130
+ * have been clicked with shift
131
+ */
132
+ it('should highlight from the highest in list to the clicked when last clicked null', () => {
133
+ const expected = options.slice(1, 6).map(({ value }) => value)
134
+
135
+ toggleRange({
136
+ option: options[5],
137
+ lastClicked: null,
138
+ maxSelections,
139
+ highlightedOptions: [
140
+ ...options.slice(1, 2),
141
+ ...options.slice(3, 5),
142
+ ].map(({ value }) => value),
143
+ setHighlightedOptions,
144
+ options,
145
+ })
146
+
147
+ expect(setHighlightedOptions).toHaveBeenCalledWith(expected)
148
+ })
149
+ })
150
+ })
@@ -0,0 +1,39 @@
1
+ import { toggleReplace } from '../../../transfer/use-highlighted-options/toggle-replace.js'
2
+
3
+ describe('Transfer - useHighlightedOptions - toggleReplace', () => {
4
+ const setHighlightedOptions = jest.fn()
5
+
6
+ afterEach(() => {
7
+ setHighlightedOptions.mockClear()
8
+ })
9
+
10
+ describe('maxSelections=1', () => {
11
+ it('should replace the current highlighted option', () => {
12
+ const highlightedOptions = ['foo']
13
+ const option = { value: 'bar', label: 'Bar' }
14
+ const expected = [option.value]
15
+
16
+ toggleReplace({
17
+ highlightedOptions,
18
+ option,
19
+ setHighlightedOptions,
20
+ })
21
+
22
+ expect(setHighlightedOptions).toHaveBeenCalledWith(expected)
23
+ })
24
+
25
+ it('should empty the highlighted options', () => {
26
+ const highlightedOptions = ['foo']
27
+ const option = { value: 'foo', label: 'Foo' }
28
+ const expected = []
29
+
30
+ toggleReplace({
31
+ highlightedOptions,
32
+ option,
33
+ setHighlightedOptions,
34
+ })
35
+
36
+ expect(setHighlightedOptions).toHaveBeenCalledWith(expected)
37
+ })
38
+ })
39
+ })
@@ -0,0 +1,41 @@
1
+ import { createToggleHighlightedOption } from '../../transfer/use-highlighted-options/create-toggle-highlighted-option.js'
2
+ import { useHighlightedOptions } from '../../transfer/use-highlighted-options.js'
3
+
4
+ jest.mock(
5
+ '../../transfer/use-highlighted-options/create-toggle-highlighted-option.js',
6
+ () => ({
7
+ createToggleHighlightedOption: jest.fn(),
8
+ })
9
+ )
10
+
11
+ jest.mock('react', () => ({
12
+ useState: jest.fn((initialValue) => [initialValue, jest.fn()]),
13
+ }))
14
+
15
+ describe('Transfer - useHighlightedOptions', () => {
16
+ it('should use null as the default value of lastClicked', () => {
17
+ useHighlightedOptions({})
18
+ expect(createToggleHighlightedOption).toHaveBeenCalledWith(
19
+ expect.objectContaining({
20
+ lastClicked: null,
21
+ })
22
+ )
23
+ })
24
+
25
+ it('should use an empty array as default value of highlightedOptions', () => {
26
+ const { highlightedOptions } = useHighlightedOptions({})
27
+ expect(highlightedOptions).toEqual([])
28
+ })
29
+
30
+ it('should use the createToggleHighlightedOption factory to create the toggleHighlightedOption function', () => {
31
+ const createToggleHighlightedOptionReturnValue = jest.fn()
32
+ createToggleHighlightedOption.mockImplementationOnce(
33
+ () => createToggleHighlightedOptionReturnValue
34
+ )
35
+
36
+ const { toggleHighlightedOption } = useHighlightedOptions({})
37
+ expect(toggleHighlightedOption).toBe(
38
+ createToggleHighlightedOptionReturnValue
39
+ )
40
+ })
41
+ })
@@ -0,0 +1,165 @@
1
+ import { mount } from 'enzyme'
2
+ import React from 'react'
3
+ import { ReorderingActions } from '../reordering-actions.js'
4
+
5
+ const findButton = (wrapper, dataTest) =>
6
+ wrapper.find(`button[data-test="${dataTest}"]`)
7
+
8
+ const allButtonHooks = (dataTest) => [
9
+ `${dataTest}-buttonmovetotop`,
10
+ `${dataTest}-buttonmoveup`,
11
+ `${dataTest}-buttonmovedown`,
12
+ `${dataTest}-buttonmovetobottom`,
13
+ ]
14
+
15
+ describe('Transfer - ReorderingActions', () => {
16
+ const dataTest = 'test-reorderingactions'
17
+ const baseProps = {
18
+ dataTest,
19
+ onChangeUp: jest.fn(),
20
+ onChangeDown: jest.fn(),
21
+ onChangeToTop: jest.fn(),
22
+ onChangeToBottom: jest.fn(),
23
+ }
24
+
25
+ afterEach(() => {
26
+ Object.values(baseProps).forEach((value) => {
27
+ if (jest.isMockFunction(value)) {
28
+ value.mockClear()
29
+ }
30
+ })
31
+ })
32
+
33
+ it('renders all four reorder buttons', () => {
34
+ const wrapper = mount(<ReorderingActions {...baseProps} />)
35
+
36
+ expect(findButton(wrapper, `${dataTest}-buttonmovetotop`)).toHaveLength(
37
+ 1
38
+ )
39
+ expect(findButton(wrapper, `${dataTest}-buttonmoveup`)).toHaveLength(1)
40
+ expect(findButton(wrapper, `${dataTest}-buttonmovedown`)).toHaveLength(
41
+ 1
42
+ )
43
+ expect(
44
+ findButton(wrapper, `${dataTest}-buttonmovetobottom`)
45
+ ).toHaveLength(1)
46
+ })
47
+
48
+ it('invokes the corresponding handler for each button', () => {
49
+ const wrapper = mount(<ReorderingActions {...baseProps} />)
50
+
51
+ findButton(wrapper, `${dataTest}-buttonmovetotop`).simulate('click')
52
+ expect(baseProps.onChangeToTop).toHaveBeenCalledTimes(1)
53
+
54
+ findButton(wrapper, `${dataTest}-buttonmoveup`).simulate('click')
55
+ expect(baseProps.onChangeUp).toHaveBeenCalledTimes(1)
56
+
57
+ findButton(wrapper, `${dataTest}-buttonmovedown`).simulate('click')
58
+ expect(baseProps.onChangeDown).toHaveBeenCalledTimes(1)
59
+
60
+ findButton(wrapper, `${dataTest}-buttonmovetobottom`).simulate('click')
61
+ expect(baseProps.onChangeToBottom).toHaveBeenCalledTimes(1)
62
+ })
63
+
64
+ it('does not cross-fire handlers when a single button is clicked', () => {
65
+ const wrapper = mount(<ReorderingActions {...baseProps} />)
66
+
67
+ findButton(wrapper, `${dataTest}-buttonmoveup`).simulate('click')
68
+
69
+ expect(baseProps.onChangeUp).toHaveBeenCalledTimes(1)
70
+ expect(baseProps.onChangeDown).not.toHaveBeenCalled()
71
+ expect(baseProps.onChangeToTop).not.toHaveBeenCalled()
72
+ expect(baseProps.onChangeToBottom).not.toHaveBeenCalled()
73
+ })
74
+
75
+ it('disables both up-side buttons when disabledUp is true', () => {
76
+ const wrapper = mount(
77
+ <ReorderingActions {...baseProps} disabledUp={true} />
78
+ )
79
+
80
+ expect(
81
+ findButton(wrapper, `${dataTest}-buttonmovetotop`).prop('disabled')
82
+ ).toBe(true)
83
+ expect(
84
+ findButton(wrapper, `${dataTest}-buttonmoveup`).prop('disabled')
85
+ ).toBe(true)
86
+ expect(
87
+ findButton(wrapper, `${dataTest}-buttonmovedown`).prop('disabled')
88
+ ).toBeFalsy()
89
+ expect(
90
+ findButton(wrapper, `${dataTest}-buttonmovetobottom`).prop(
91
+ 'disabled'
92
+ )
93
+ ).toBeFalsy()
94
+ })
95
+
96
+ it('disables both down-side buttons when disabledDown is true', () => {
97
+ const wrapper = mount(
98
+ <ReorderingActions {...baseProps} disabledDown={true} />
99
+ )
100
+
101
+ expect(
102
+ findButton(wrapper, `${dataTest}-buttonmovedown`).prop('disabled')
103
+ ).toBe(true)
104
+ expect(
105
+ findButton(wrapper, `${dataTest}-buttonmovetobottom`).prop(
106
+ 'disabled'
107
+ )
108
+ ).toBe(true)
109
+ expect(
110
+ findButton(wrapper, `${dataTest}-buttonmoveup`).prop('disabled')
111
+ ).toBeFalsy()
112
+ expect(
113
+ findButton(wrapper, `${dataTest}-buttonmovetotop`).prop('disabled')
114
+ ).toBeFalsy()
115
+ })
116
+
117
+ it('does not invoke handlers for disabled buttons', () => {
118
+ const wrapper = mount(
119
+ <ReorderingActions
120
+ {...baseProps}
121
+ disabledUp={true}
122
+ disabledDown={true}
123
+ />
124
+ )
125
+
126
+ findButton(wrapper, `${dataTest}-buttonmovetotop`).simulate('click')
127
+ findButton(wrapper, `${dataTest}-buttonmoveup`).simulate('click')
128
+ findButton(wrapper, `${dataTest}-buttonmovedown`).simulate('click')
129
+ findButton(wrapper, `${dataTest}-buttonmovetobottom`).simulate('click')
130
+
131
+ expect(baseProps.onChangeToTop).not.toHaveBeenCalled()
132
+ expect(baseProps.onChangeUp).not.toHaveBeenCalled()
133
+ expect(baseProps.onChangeDown).not.toHaveBeenCalled()
134
+ expect(baseProps.onChangeToBottom).not.toHaveBeenCalled()
135
+ })
136
+
137
+ it('sets aria-label on every button', () => {
138
+ const wrapper = mount(<ReorderingActions {...baseProps} />)
139
+
140
+ const assertLabel = (hook, label) => {
141
+ expect(findButton(wrapper, hook).prop('aria-label')).toBe(label)
142
+ }
143
+
144
+ assertLabel(`${dataTest}-buttonmovetotop`, 'Move selected items to top')
145
+ assertLabel(`${dataTest}-buttonmoveup`, 'Move selected items up')
146
+ assertLabel(`${dataTest}-buttonmovedown`, 'Move selected items down')
147
+ assertLabel(
148
+ `${dataTest}-buttonmovetobottom`,
149
+ 'Move selected items to bottom'
150
+ )
151
+ })
152
+
153
+ it('still renders and wires up all four buttons when filterActive is true', () => {
154
+ const wrapper = mount(
155
+ <ReorderingActions {...baseProps} filterActive={true} />
156
+ )
157
+
158
+ allButtonHooks(dataTest).forEach((hook) => {
159
+ expect(findButton(wrapper, hook)).toHaveLength(1)
160
+ })
161
+
162
+ findButton(wrapper, `${dataTest}-buttonmoveup`).simulate('click')
163
+ expect(baseProps.onChangeUp).toHaveBeenCalledTimes(1)
164
+ })
165
+ })
@@ -0,0 +1,137 @@
1
+ import { mount } from 'enzyme'
2
+ import React from 'react'
3
+ import { Transfer } from '../transfer.js'
4
+
5
+ const options = [
6
+ {
7
+ label: 'ANC 1st visit',
8
+ value: 'anc_1st_visit',
9
+ },
10
+ {
11
+ label: 'ANC 2nd visit',
12
+ value: 'anc_2nd_visit',
13
+ },
14
+ {
15
+ label: 'ANC 3rd visit',
16
+ value: 'anc_3rd_visit',
17
+ },
18
+ {
19
+ label: 'ANC 4th or more visits',
20
+ value: 'anc_4th_or_more_visits',
21
+ },
22
+ {
23
+ label: 'ARI treated with antibiotics (pneumonia) follow-up',
24
+ value: 'ari_treated_with_antibiotics_(pneumonia)_follow-up',
25
+ },
26
+ {
27
+ label: 'ARI treated with antibiotics (pneumonia) new',
28
+ value: 'ari_treated_with_antibiotics_(pneumonia)_new',
29
+ },
30
+ {
31
+ label: 'ARI treated with antibiotics (pneumonia) referrals',
32
+ value: 'ari_treated_with_antibiotics_(pneumonia)_referrals',
33
+ },
34
+ {
35
+ label: 'ARI treated without antibiotics (cough) follow-up',
36
+ value: 'ari_treated_without_antibiotics_(cough)_follow-up',
37
+ },
38
+ {
39
+ label: 'ARI treated without antibiotics (cough) new',
40
+ value: 'ari_treated_without_antibiotics_(cough)_new',
41
+ },
42
+ {
43
+ label: 'ARI treated without antibiotics (cough) referrals',
44
+ value: 'ari_treated_without_antibiotics_(cough)_referrals',
45
+ },
46
+ {
47
+ label: 'ART No clients who stopped TRT due to TRT failure',
48
+ value: 'art_no_clients_who_stopped_trt_due_to_trt_failure',
49
+ },
50
+ {
51
+ label: 'ART No clients who stopped TRT due to adverse clinical status/event',
52
+ value: 'art_no_clients_who_stopped_trt_due_to_adverse_clinical_status/event',
53
+ },
54
+ {
55
+ label: 'ART No clients with change of regimen due to drug toxicity',
56
+ value: 'art_no_clients_with_change_of_regimen_due_to_drug_toxicity',
57
+ },
58
+ {
59
+ label: 'ART No clients with new adverse drug reaction',
60
+ value: 'art_no_clients_with_new_adverse_drug_reaction',
61
+ },
62
+ {
63
+ label: 'ART No started Opportunist Infection prophylaxis',
64
+ value: 'art_no_started_opportunist_infection_prophylaxis',
65
+ },
66
+ {
67
+ label: 'ART clients with new adverse clinical event',
68
+ value: 'art_clients_with_new_adverse_clinical_event',
69
+ },
70
+ {
71
+ label: 'ART defaulters',
72
+ value: 'art_defaulters',
73
+ },
74
+ {
75
+ label: 'ART enrollment stage 1',
76
+ value: 'art_enrollment_stage_1',
77
+ },
78
+ {
79
+ label: 'ART enrollment stage 2',
80
+ value: 'art_enrollment_stage_2',
81
+ },
82
+ {
83
+ label: 'ART enrollment stage 3',
84
+ value: 'art_enrollment_stage_3',
85
+ },
86
+ {
87
+ label: 'ART enrollment stage 4',
88
+ value: 'art_enrollment_stage_4',
89
+ },
90
+ {
91
+ label: 'ART entry point: No PMTCT',
92
+ value: 'art_entry_point:_no_pmtct',
93
+ },
94
+ ]
95
+
96
+ describe('Pass selected state to renderOptions function', () => {
97
+ it('should pass selected=false to source options', () => {
98
+ const renderOption = jest.fn()
99
+ mount(
100
+ <Transfer
101
+ renderOption={renderOption}
102
+ onChange={jest.fn()}
103
+ selected={[]}
104
+ options={options}
105
+ />
106
+ )
107
+
108
+ expect(renderOption).toHaveBeenCalledTimes(options.length)
109
+ renderOption.mock.calls.forEach(([{ selected }]) => {
110
+ expect(selected).toBe(false)
111
+ })
112
+ })
113
+
114
+ it('should pass selected=true to picked options', () => {
115
+ const renderOption = jest.fn()
116
+ const picked = options.slice(0, 3)
117
+ const pickedValues = picked.map(({ value }) => value)
118
+
119
+ mount(
120
+ <Transfer
121
+ renderOption={renderOption}
122
+ onChange={jest.fn()}
123
+ selected={pickedValues}
124
+ options={options}
125
+ />
126
+ )
127
+
128
+ const withSelectedTrue = renderOption.mock.calls.filter(
129
+ ([{ selected }]) => selected
130
+ )
131
+
132
+ expect(withSelectedTrue).toHaveLength(picked.length)
133
+ withSelectedTrue.forEach(([selected], index) => {
134
+ expect(selected).toEqual(expect.objectContaining(picked[index]))
135
+ })
136
+ })
137
+ })
package/src/actions.js ADDED
@@ -0,0 +1,33 @@
1
+ import { spacers } from '@dhis2/ui-constants'
2
+ import PropTypes from 'prop-types'
3
+ import React from 'react'
4
+
5
+ export const Actions = ({ children, dataTest }) => (
6
+ <div data-test={dataTest}>
7
+ {children}
8
+
9
+ <style jsx>{`
10
+ div {
11
+ align-content: center;
12
+ display: flex;
13
+ flex-direction: column;
14
+ flex-shrink: 1;
15
+ justify-content: center;
16
+ margin: 0 ${spacers.dp8};
17
+ }
18
+
19
+ div > :global(button) {
20
+ margin-top: 8px;
21
+ }
22
+
23
+ div > :global(button):first-child {
24
+ margin-top: 0;
25
+ }
26
+ `}</style>
27
+ </div>
28
+ )
29
+
30
+ Actions.propTypes = {
31
+ dataTest: PropTypes.string.isRequired,
32
+ children: PropTypes.node,
33
+ }
package/src/add-all.js ADDED
@@ -0,0 +1,27 @@
1
+ import { Button } from '@dhis2-ui/button'
2
+ import PropTypes from 'prop-types'
3
+ import React from 'react'
4
+ import { IconAddAll } from './icons.js'
5
+
6
+ export const AddAll = ({ label, dataTest, disabled, onClick }) => (
7
+ <Button
8
+ dataTest={dataTest}
9
+ disabled={disabled}
10
+ onClick={onClick}
11
+ icon={
12
+ <IconAddAll
13
+ dataTest={`${dataTest}-iconaddall`}
14
+ disabled={disabled}
15
+ />
16
+ }
17
+ >
18
+ {label}
19
+ </Button>
20
+ )
21
+
22
+ AddAll.propTypes = {
23
+ dataTest: PropTypes.string.isRequired,
24
+ onClick: PropTypes.func.isRequired,
25
+ disabled: PropTypes.bool,
26
+ label: PropTypes.string,
27
+ }
@@ -0,0 +1,27 @@
1
+ import { Button } from '@dhis2-ui/button'
2
+ import PropTypes from 'prop-types'
3
+ import React from 'react'
4
+ import { IconAddIndividual } from './icons.js'
5
+
6
+ export const AddIndividual = ({ label, dataTest, disabled, onClick }) => (
7
+ <Button
8
+ dataTest={dataTest}
9
+ disabled={disabled}
10
+ onClick={onClick}
11
+ icon={
12
+ <IconAddIndividual
13
+ disabled={disabled}
14
+ dataTest={`${dataTest}-iconaddindividual`}
15
+ />
16
+ }
17
+ >
18
+ {label}
19
+ </Button>
20
+ )
21
+
22
+ AddIndividual.propTypes = {
23
+ dataTest: PropTypes.string.isRequired,
24
+ onClick: PropTypes.func.isRequired,
25
+ disabled: PropTypes.bool,
26
+ label: PropTypes.string,
27
+ }
@@ -0,0 +1,9 @@
1
+ import { isOption } from './is-option.js'
2
+
3
+ /**
4
+ * @param {Object[]} options
5
+ * @param {Object} option
6
+ * @returns {Int}
7
+ */
8
+ export const findOptionIndex = (options, option) =>
9
+ options.findIndex((current) => isOption(current, option))
@@ -0,0 +1,35 @@
1
+ import { ADD_MODE, RANGE_MODE, REPLACE_MODE } from './modes.js'
2
+
3
+ /**
4
+ * @param {Object} args
5
+ * @param {bool} args.altKey
6
+ * @param {bool} args.shiftKey
7
+ * @param {bool} args.ctrlKey
8
+ * @param {bool} args.metaKey
9
+ * @return {string}
10
+ */
11
+ export const getModeByModifierKey = ({
12
+ altKey,
13
+ shiftKey,
14
+ ctrlKey,
15
+ metaKey,
16
+ }) => {
17
+ const keys = [altKey, shiftKey, ctrlKey, metaKey]
18
+ const amountKeyPressed = keys.filter((v) => v)
19
+ const moreThanOneKeyPressed = amountKeyPressed.length
20
+
21
+ if (moreThanOneKeyPressed !== 1) {
22
+ return REPLACE_MODE
23
+ }
24
+
25
+ if (altKey || ctrlKey || metaKey) {
26
+ return ADD_MODE
27
+ }
28
+
29
+ if (shiftKey) {
30
+ return RANGE_MODE
31
+ }
32
+
33
+ // default to replace mode
34
+ return REPLACE_MODE
35
+ }
@@ -0,0 +1,5 @@
1
+ export { ADD_MODE, RANGE_MODE, REPLACE_MODE } from './modes.js'
2
+ export { findOptionIndex } from './find-option-index.js'
3
+ export { getModeByModifierKey } from './get-mode-by-modifier-key.js'
4
+ export { isOption } from './is-option.js'
5
+ export { toggleValue } from './toggle-value.js'
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @param {Object} left
3
+ * @param {Object} left
4
+ * @returns {bool}
5
+ */
6
+ export const isOption = (left, right) =>
7
+ left.label === right.label && left.value === right.value