@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.
- package/package.json +10 -9
- package/src/__e2e__/add_remove-highlighted-options.e2e.stories.js +30 -0
- package/src/__e2e__/common/options.js +90 -0
- package/src/__e2e__/common/stateful-decorator.js +33 -0
- package/src/__e2e__/common.js +0 -0
- package/src/__e2e__/disabled-transfer-buttons.e2e.stories.js +49 -0
- package/src/__e2e__/disabled-transfer-options.e2e.stories.js +21 -0
- package/src/__e2e__/display-order.e2e.stories.js +24 -0
- package/src/__e2e__/filter-options-list.e2e.stories.js +87 -0
- package/src/__e2e__/highlight-range-of-options.e2e.stories.js +52 -0
- package/src/__e2e__/loading_lists.e2e.stories.js +26 -0
- package/src/__e2e__/notify_at_end_of_list.e2e.stories.js +116 -0
- package/src/__e2e__/reorder-with-buttons.e2e.stories.js +35 -0
- package/src/__e2e__/set_unset-highlighted-option.e2e.stories.js +30 -0
- package/src/__e2e__/transferring-items.e2e.stories.js +27 -0
- package/src/__tests__/common.test.js +131 -0
- package/src/__tests__/helper/add-all-selectable-source-options.test.js +46 -0
- package/src/__tests__/helper/add-individual-source-options.test.js +80 -0
- package/src/__tests__/helper/default-filter-callback.test.js +45 -0
- package/src/__tests__/helper/is-reorder-down-disabled.test.js +96 -0
- package/src/__tests__/helper/is-reorder-up-disabled.test.js +96 -0
- package/src/__tests__/helper/move-highlighted-picked-option-down.test.js +111 -0
- package/src/__tests__/helper/move-highlighted-picked-option-to-bottom.test.js +101 -0
- package/src/__tests__/helper/move-highlighted-picked-option-to-top.test.js +101 -0
- package/src/__tests__/helper/move-highlighted-picked-option-up.test.js +111 -0
- package/src/__tests__/helper/remove-all-picked-options.test.js +29 -0
- package/src/__tests__/helper/remove-individual-picked-options.test.js +38 -0
- package/src/__tests__/helper/use-highlighted-option/create-toggle-highlighted-option.test.js +104 -0
- package/src/__tests__/helper/use-highlighted-option/toggle-add.test.js +84 -0
- package/src/__tests__/helper/use-highlighted-option/toggle-range.test.js +150 -0
- package/src/__tests__/helper/use-highlighted-option/toggle-replace.test.js +39 -0
- package/src/__tests__/helper/use-highlighted-option.test.js +41 -0
- package/src/__tests__/reordering-actions.test.js +165 -0
- package/src/__tests__/transfer.test.js +137 -0
- package/src/actions.js +33 -0
- package/src/add-all.js +27 -0
- package/src/add-individual.js +27 -0
- package/src/common/find-option-index.js +9 -0
- package/src/common/get-mode-by-modifier-key.js +35 -0
- package/src/common/index.js +5 -0
- package/src/common/is-option.js +7 -0
- package/src/common/modes.js +11 -0
- package/src/common/remove-option.js +19 -0
- package/src/common/toggle-value.js +18 -0
- package/src/container.js +23 -0
- package/src/end-intersection-detector.js +37 -0
- package/src/features/add_remove-highlighted-options/index.js +92 -0
- package/src/features/add_remove-highlighted-options.feature +41 -0
- package/src/features/common/index.js +8 -0
- package/src/features/disabled-transfer-buttons/index.js +118 -0
- package/src/features/disabled-transfer-buttons.feature +46 -0
- package/src/features/disabled-transfer-options/index.js +182 -0
- package/src/features/disabled-transfer-options.feature +42 -0
- package/src/features/display-order/index.js +205 -0
- package/src/features/display-order.feature +30 -0
- package/src/features/filter-options-list/index.js +133 -0
- package/src/features/filter-options-list.feature +40 -0
- package/src/features/highlight-range-of-options/index.js +336 -0
- package/src/features/highlight-range-of-options.feature +70 -0
- package/src/features/loading_lists/index.js +43 -0
- package/src/features/loading_lists.feature +19 -0
- package/src/features/notify_at_end_of_list/index.js +125 -0
- package/src/features/notify_at_end_of_list.feature +64 -0
- package/src/features/reorder-with-buttons/index.js +181 -0
- package/src/features/reorder-with-buttons.feature +138 -0
- package/src/features/set_unset-highlighted-option/index.js +121 -0
- package/src/features/set_unset-highlighted-option.feature +42 -0
- package/src/features/transferring-items/index.js +375 -0
- package/src/features/transferring-items.feature +44 -0
- package/src/filter.js +38 -0
- package/src/icons.js +194 -0
- package/src/index.js +2 -0
- package/src/left-footer.js +22 -0
- package/src/left-header.js +22 -0
- package/src/left-side.js +34 -0
- package/src/locales/en/translations.json +7 -0
- package/src/locales/index.js +16 -0
- package/src/options-container.js +127 -0
- package/src/remove-all.js +27 -0
- package/src/remove-individual.js +27 -0
- package/src/reordering-actions.js +136 -0
- package/src/right-footer.js +22 -0
- package/src/right-header.js +22 -0
- package/src/right-side.js +33 -0
- package/src/transfer/add-all-selectable-source-options.js +37 -0
- package/src/transfer/add-individual-source-options.js +61 -0
- package/src/transfer/create-double-click-handlers.js +36 -0
- package/src/transfer/default-filter-callback.js +17 -0
- package/src/transfer/get-highlighted-picked-indices.js +26 -0
- package/src/transfer/get-option-click-handlers.js +19 -0
- package/src/transfer/index.js +17 -0
- package/src/transfer/is-reorder-down-disabled.js +34 -0
- package/src/transfer/is-reorder-up-disabled.js +30 -0
- package/src/transfer/move-highlighted-picked-option-down.js +54 -0
- package/src/transfer/move-highlighted-picked-option-to-bottom.js +44 -0
- package/src/transfer/move-highlighted-picked-option-to-top.js +38 -0
- package/src/transfer/move-highlighted-picked-option-up.js +47 -0
- package/src/transfer/remove-all-picked-options.js +13 -0
- package/src/transfer/remove-individual-picked-options.js +49 -0
- package/src/transfer/use-filter.js +17 -0
- package/src/transfer/use-highlighted-options/create-toggle-highlighted-option.js +64 -0
- package/src/transfer/use-highlighted-options/toggle-add.js +20 -0
- package/src/transfer/use-highlighted-options/toggle-range.js +61 -0
- package/src/transfer/use-highlighted-options/toggle-replace.js +26 -0
- package/src/transfer/use-highlighted-options.js +34 -0
- package/src/transfer/use-options-key-monitor.js +41 -0
- package/src/transfer-option.js +91 -0
- package/src/transfer.js +539 -0
- 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,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'
|