@dhis2-ui/transfer 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.
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,131 @@
1
+ import {
2
+ ADD_MODE,
3
+ RANGE_MODE,
4
+ REPLACE_MODE,
5
+ findOptionIndex,
6
+ getModeByModifierKey,
7
+ isOption,
8
+ toggleValue,
9
+ } from '../common/index.js'
10
+
11
+ describe('Transfer - isOption', () => {
12
+ it('should return true when the options are the same', () => {
13
+ const option1 = { label: 'foo', value: 'bar' }
14
+ const option2 = { label: 'foo', value: 'bar' }
15
+ const actual = isOption(option1, option2)
16
+ const expected = true
17
+
18
+ expect(actual).toBe(expected)
19
+ })
20
+
21
+ it('should return false when the labels do not match', () => {
22
+ const option1 = { label: 'foo', value: 'bar' }
23
+ const option2 = { label: 'baz', value: 'bar' }
24
+ const actual = isOption(option1, option2)
25
+ const expected = false
26
+
27
+ expect(actual).toBe(expected)
28
+ })
29
+
30
+ it('should return false when the values do not match', () => {
31
+ const option1 = { label: 'foo', value: 'bar' }
32
+ const option2 = { label: 'foo', value: 'baz' }
33
+ const actual = isOption(option1, option2)
34
+ const expected = false
35
+
36
+ expect(actual).toBe(expected)
37
+ })
38
+ })
39
+
40
+ describe('Transfer - findOptionIndex', () => {
41
+ it('should return index 1', () => {
42
+ const options = [
43
+ { label: 'foo', value: 'bar' },
44
+ { label: 'foo', value: 'baz' },
45
+ ]
46
+ const option = { label: 'foo', value: 'baz' }
47
+ const actual = findOptionIndex(options, option)
48
+ const expected = 1
49
+
50
+ expect(actual).toBe(expected)
51
+ })
52
+
53
+ it('should return -1 when the option is not included', () => {
54
+ const options = [
55
+ { label: 'foo', value: 'bar' },
56
+ { label: 'foo', value: 'baz' },
57
+ ]
58
+ const option = { label: 'baz', value: 'baz' }
59
+ const actual = findOptionIndex(options, option)
60
+ const expected = -1
61
+
62
+ expect(actual).toBe(expected)
63
+ })
64
+ })
65
+
66
+ describe('Transfer - toggleValue', () => {
67
+ it('should remove the last value from the array when value inside array', () => {
68
+ const values = ['foo', 'bar', 'baz']
69
+ const value = 'baz'
70
+ const expected = ['foo', 'bar']
71
+ const actual = toggleValue(values, value)
72
+
73
+ expect(actual).toEqual(expected)
74
+ })
75
+
76
+ it('should remove the first value from the array when value inside array', () => {
77
+ const values = ['foo', 'bar', 'baz']
78
+ const value = 'foo'
79
+ const expected = ['bar', 'baz']
80
+ const actual = toggleValue(values, value)
81
+
82
+ expect(actual).toEqual(expected)
83
+ })
84
+
85
+ it('should add the value if not inside the array', () => {
86
+ const values = ['foo', 'bar']
87
+ const value = 'baz'
88
+ const expected = ['foo', 'bar', 'baz']
89
+ const actual = toggleValue(values, value)
90
+
91
+ expect(actual).toEqual(expected)
92
+ })
93
+ })
94
+
95
+ describe('Transfer - getModeByModifierKey', () => {
96
+ it('should return REPLACE_MODE when more than one key is pressed', () => {
97
+ const expected = REPLACE_MODE
98
+ const actual = getModeByModifierKey({ altKey: true, ctrlKey: true })
99
+ expect(actual).toBe(expected)
100
+ })
101
+
102
+ it('should return ADD_MODE if alt key is pressed', () => {
103
+ const expected = ADD_MODE
104
+ const actual = getModeByModifierKey({ altKey: true })
105
+ expect(actual).toBe(expected)
106
+ })
107
+
108
+ it('should return ADD_MODE if ctrl key is pressed', () => {
109
+ const expected = ADD_MODE
110
+ const actual = getModeByModifierKey({ ctrlKey: true })
111
+ expect(actual).toBe(expected)
112
+ })
113
+
114
+ it('should return ADD_MODE if meta key is pressed', () => {
115
+ const expected = ADD_MODE
116
+ const actual = getModeByModifierKey({ metaKey: true })
117
+ expect(actual).toBe(expected)
118
+ })
119
+
120
+ it('should return RANGE_MODE if shift key is pressed', () => {
121
+ const expected = RANGE_MODE
122
+ const actual = getModeByModifierKey({ shiftKey: true })
123
+ expect(actual).toBe(expected)
124
+ })
125
+
126
+ it('should return REPLACE_MODE if no key is pressed', () => {
127
+ const expected = REPLACE_MODE
128
+ const actual = getModeByModifierKey({})
129
+ expect(actual).toBe(expected)
130
+ })
131
+ })
@@ -0,0 +1,46 @@
1
+ import { addAllSelectableSourceOptions } from '../../transfer/add-all-selectable-source-options.js'
2
+
3
+ describe('Transfer - addAllSelectableSourceOptions', () => {
4
+ const onChange = jest.fn()
5
+ const setHighlightedSourceOptions = jest.fn()
6
+
7
+ const sourceOptions = [
8
+ { label: 'Foo', value: 'foo' },
9
+ { label: 'Bar', value: 'bar' },
10
+ { label: 'Baz', value: 'baz' },
11
+ { label: 'Foobar', value: 'foobar' },
12
+ { label: 'Foobaz', value: 'foobaz' },
13
+ ]
14
+
15
+ afterEach(() => {
16
+ onChange.mockClear()
17
+ setHighlightedSourceOptions.mockClear()
18
+ })
19
+
20
+ it('should add all selectable source options to the selected array', () => {
21
+ const selected = ['barfoo']
22
+ const expected = {
23
+ selected: ['barfoo', 'foo', 'bar', 'baz', 'foobar', 'foobaz'],
24
+ }
25
+
26
+ addAllSelectableSourceOptions({
27
+ sourceOptions,
28
+ selected,
29
+ onChange,
30
+ setHighlightedSourceOptions,
31
+ })
32
+
33
+ expect(onChange).toHaveBeenCalledWith(expected)
34
+ })
35
+
36
+ it('should reset all highlighted source options', () => {
37
+ addAllSelectableSourceOptions({
38
+ sourceOptions,
39
+ selected: ['barfoo'],
40
+ onChange,
41
+ setHighlightedSourceOptions,
42
+ })
43
+
44
+ expect(setHighlightedSourceOptions).toHaveBeenCalledWith([])
45
+ })
46
+ })
@@ -0,0 +1,80 @@
1
+ import { addIndividualSourceOptions } from '../../transfer/add-individual-source-options.js'
2
+
3
+ describe('Transfer - addIndividualSourceOptions', () => {
4
+ const onChange = jest.fn()
5
+ const setHighlightedSourceOptions = jest.fn()
6
+
7
+ const sourceOptions = [
8
+ { label: 'Foo', value: 'foo' },
9
+ { label: 'Foobar', value: 'foobar' },
10
+ { label: 'Foobaz', value: 'foobaz' },
11
+ ]
12
+
13
+ const highlightedSourceOptions = ['foobaz', 'bar']
14
+ const selected = ['barfoo']
15
+
16
+ afterEach(() => {
17
+ onChange.mockClear()
18
+ setHighlightedSourceOptions.mockClear()
19
+ })
20
+
21
+ it('should add the highlighted source options to the selected array', () => {
22
+ addIndividualSourceOptions({
23
+ highlightedSourceOptions,
24
+ maxSelections: Infinity,
25
+ onChange,
26
+ selected,
27
+ setHighlightedSourceOptions,
28
+ })
29
+
30
+ expect(onChange).toHaveBeenCalledWith({
31
+ selected: ['barfoo', 'foobaz', 'bar'],
32
+ })
33
+ })
34
+
35
+ it('should reset the highlighted source options', () => {
36
+ addIndividualSourceOptions({
37
+ highlightedSourceOptions,
38
+ maxSelections: Infinity,
39
+ onChange,
40
+ selected,
41
+ setHighlightedSourceOptions,
42
+ })
43
+
44
+ expect(setHighlightedSourceOptions).toHaveBeenCalledWith([])
45
+ })
46
+
47
+ it('should only select the filtered source options', () => {
48
+ addIndividualSourceOptions({
49
+ filterable: true,
50
+ filter: 'oo',
51
+ sourceOptions,
52
+ highlightedSourceOptions,
53
+ maxSelections: Infinity,
54
+ onChange,
55
+ selected,
56
+ setHighlightedSourceOptions,
57
+ })
58
+
59
+ expect(onChange).toHaveBeenCalledWith({
60
+ selected: ['barfoo', 'foobaz'],
61
+ })
62
+ })
63
+
64
+ it('should only call onChange with the max selection amount', () => {
65
+ addIndividualSourceOptions({
66
+ filterable: true,
67
+ filter: 'oo',
68
+ sourceOptions,
69
+ highlightedSourceOptions: highlightedSourceOptions.slice(0, 1),
70
+ maxSelections: 1,
71
+ onChange,
72
+ selected: selected,
73
+ setHighlightedSourceOptions,
74
+ })
75
+
76
+ expect(onChange).toHaveBeenCalledWith({
77
+ selected: ['foobaz'],
78
+ })
79
+ })
80
+ })
@@ -0,0 +1,45 @@
1
+ import { defaultFilterCallback } from '../../transfer/default-filter-callback.js'
2
+
3
+ describe('Transfer - defaultFilterCallback', () => {
4
+ const options = [
5
+ { label: 'Foo', value: 'foo' },
6
+ { label: 'FOO', value: 'fOO' },
7
+ { label: 'Bar', value: 'bar' },
8
+ { label: 'BAR', value: 'bAR' },
9
+ ]
10
+
11
+ it('should returl all options when the filter is empty', () => {
12
+ const filter = ''
13
+ const expected = options
14
+ const actual = defaultFilterCallback(options, filter)
15
+
16
+ expect(actual).toEqual(expected)
17
+ })
18
+
19
+ it('should return options matching the filter, regardless of case-sensitvity', () => {
20
+ const filter = 'Fo'
21
+ const expected = [
22
+ { label: 'Foo', value: 'foo' },
23
+ { label: 'FOO', value: 'fOO' },
24
+ ]
25
+ const actual = defaultFilterCallback(options, filter)
26
+
27
+ expect(actual).toEqual(expected)
28
+ })
29
+
30
+ it('should return an empty array when there are no matches', () => {
31
+ const filter = 'Baz'
32
+ const expected = []
33
+ const actual = defaultFilterCallback(options, filter)
34
+
35
+ expect(actual).toEqual(expected)
36
+ })
37
+
38
+ it('should return all options when the filter is invalid regexp', () => {
39
+ const filter = '('
40
+ const expected = options
41
+ const actual = defaultFilterCallback(options, filter)
42
+
43
+ expect(actual).toEqual(expected)
44
+ })
45
+ })
@@ -0,0 +1,96 @@
1
+ import { isReorderDownDisabled } from '../../transfer/is-reorder-down-disabled.js'
2
+
3
+ describe('Transfer - isReorderDownDisabled', () => {
4
+ const selected = ['foo', 'bar', 'baz']
5
+
6
+ it('should return true when there are no highlighted picked options', () => {
7
+ const actual = isReorderDownDisabled({
8
+ highlightedPickedOptions: [],
9
+ selected,
10
+ })
11
+
12
+ expect(actual).toBe(true)
13
+ })
14
+
15
+ it('should return true if the last picked option is the only highlighted one', () => {
16
+ const actual = isReorderDownDisabled({
17
+ highlightedPickedOptions: ['baz'],
18
+ selected,
19
+ })
20
+
21
+ expect(actual).toBe(true)
22
+ })
23
+
24
+ it('should return false when one picked option is highlighted which is not the last one', () => {
25
+ const actual = isReorderDownDisabled({
26
+ highlightedPickedOptions: ['bar'],
27
+ selected,
28
+ })
29
+
30
+ expect(actual).toBe(false)
31
+ })
32
+
33
+ it('should return false for a contiguous multi-select not flush to the bottom', () => {
34
+ const actual = isReorderDownDisabled({
35
+ highlightedPickedOptions: ['foo', 'bar'],
36
+ selected,
37
+ })
38
+
39
+ expect(actual).toBe(false)
40
+ })
41
+
42
+ it('should return true for a contiguous multi-select flush to the bottom', () => {
43
+ const actual = isReorderDownDisabled({
44
+ highlightedPickedOptions: ['bar', 'baz'],
45
+ selected,
46
+ })
47
+
48
+ expect(actual).toBe(true)
49
+ })
50
+
51
+ it('should return true when all items are highlighted', () => {
52
+ const actual = isReorderDownDisabled({
53
+ highlightedPickedOptions: ['foo', 'bar', 'baz'],
54
+ selected,
55
+ })
56
+
57
+ expect(actual).toBe(true)
58
+ })
59
+
60
+ it('should return false for a non-contiguous selection containing the last item', () => {
61
+ const actual = isReorderDownDisabled({
62
+ highlightedPickedOptions: ['foo', 'baz'],
63
+ selected,
64
+ })
65
+
66
+ expect(actual).toBe(false)
67
+ })
68
+
69
+ it('should ignore highlighted values that do not exist in selected', () => {
70
+ const actual = isReorderDownDisabled({
71
+ highlightedPickedOptions: ['ghost', 'foo'],
72
+ selected,
73
+ })
74
+
75
+ expect(actual).toBe(false)
76
+ })
77
+
78
+ it('should return true when all highlighted values are missing from selected', () => {
79
+ const actual = isReorderDownDisabled({
80
+ highlightedPickedOptions: ['ghost'],
81
+ selected,
82
+ })
83
+
84
+ expect(actual).toBe(true)
85
+ })
86
+
87
+ it('should return true when a filter is active on the picked side', () => {
88
+ const actual = isReorderDownDisabled({
89
+ highlightedPickedOptions: ['foo'],
90
+ selected,
91
+ filterActivePicked: true,
92
+ })
93
+
94
+ expect(actual).toBe(true)
95
+ })
96
+ })
@@ -0,0 +1,96 @@
1
+ import { isReorderUpDisabled } from '../../transfer/is-reorder-up-disabled.js'
2
+
3
+ describe('Transfer - isReorderUpDisabled', () => {
4
+ const selected = ['foo', 'bar', 'baz']
5
+
6
+ it('should return true when there are no highlighted picked options', () => {
7
+ const actual = isReorderUpDisabled({
8
+ highlightedPickedOptions: [],
9
+ selected,
10
+ })
11
+
12
+ expect(actual).toBe(true)
13
+ })
14
+
15
+ it('should return true if the first picked option is the only highlighted one', () => {
16
+ const actual = isReorderUpDisabled({
17
+ highlightedPickedOptions: ['foo'],
18
+ selected,
19
+ })
20
+
21
+ expect(actual).toBe(true)
22
+ })
23
+
24
+ it('should return false when one picked option is highlighted which is not the first one', () => {
25
+ const actual = isReorderUpDisabled({
26
+ highlightedPickedOptions: ['baz'],
27
+ selected,
28
+ })
29
+
30
+ expect(actual).toBe(false)
31
+ })
32
+
33
+ it('should return false for a contiguous multi-select not flush to the top', () => {
34
+ const actual = isReorderUpDisabled({
35
+ highlightedPickedOptions: ['bar', 'baz'],
36
+ selected,
37
+ })
38
+
39
+ expect(actual).toBe(false)
40
+ })
41
+
42
+ it('should return true for a contiguous multi-select flush to the top', () => {
43
+ const actual = isReorderUpDisabled({
44
+ highlightedPickedOptions: ['foo', 'bar'],
45
+ selected,
46
+ })
47
+
48
+ expect(actual).toBe(true)
49
+ })
50
+
51
+ it('should return true when all items are highlighted', () => {
52
+ const actual = isReorderUpDisabled({
53
+ highlightedPickedOptions: ['foo', 'bar', 'baz'],
54
+ selected,
55
+ })
56
+
57
+ expect(actual).toBe(true)
58
+ })
59
+
60
+ it('should return false for a non-contiguous selection containing the first item', () => {
61
+ const actual = isReorderUpDisabled({
62
+ highlightedPickedOptions: ['foo', 'baz'],
63
+ selected,
64
+ })
65
+
66
+ expect(actual).toBe(false)
67
+ })
68
+
69
+ it('should ignore highlighted values that do not exist in selected', () => {
70
+ const actual = isReorderUpDisabled({
71
+ highlightedPickedOptions: ['ghost', 'baz'],
72
+ selected,
73
+ })
74
+
75
+ expect(actual).toBe(false)
76
+ })
77
+
78
+ it('should return true when all highlighted values are missing from selected', () => {
79
+ const actual = isReorderUpDisabled({
80
+ highlightedPickedOptions: ['ghost'],
81
+ selected,
82
+ })
83
+
84
+ expect(actual).toBe(true)
85
+ })
86
+
87
+ it('should return true when a filter is active on the picked side', () => {
88
+ const actual = isReorderUpDisabled({
89
+ highlightedPickedOptions: ['baz'],
90
+ selected,
91
+ filterActivePicked: true,
92
+ })
93
+
94
+ expect(actual).toBe(true)
95
+ })
96
+ })
@@ -0,0 +1,111 @@
1
+ import { moveHighlightedPickedOptionDown } from '../../transfer/move-highlighted-picked-option-down.js'
2
+
3
+ describe('Transfer - moveHighlightedPickedOptionDown', () => {
4
+ const onChange = jest.fn()
5
+
6
+ afterEach(() => {
7
+ onChange.mockClear()
8
+ })
9
+
10
+ it('should move a single highlighted option down', () => {
11
+ moveHighlightedPickedOptionDown({
12
+ selected: ['foo', 'bar', 'baz'],
13
+ highlightedPickedOptions: ['bar'],
14
+ onChange,
15
+ })
16
+
17
+ expect(onChange).toHaveBeenCalledWith({
18
+ selected: ['foo', 'baz', 'bar'],
19
+ })
20
+ })
21
+
22
+ it('should do nothing when trying to move down the last option', () => {
23
+ moveHighlightedPickedOptionDown({
24
+ selected: ['foo', 'bar', 'baz'],
25
+ highlightedPickedOptions: ['baz'],
26
+ onChange,
27
+ })
28
+
29
+ expect(onChange).toHaveBeenCalledTimes(0)
30
+ })
31
+
32
+ it('should do nothing when trying to move down a non-existing option', () => {
33
+ moveHighlightedPickedOptionDown({
34
+ selected: ['foo', 'bar', 'baz'],
35
+ highlightedPickedOptions: ['ghost'],
36
+ onChange,
37
+ })
38
+
39
+ expect(onChange).toHaveBeenCalledTimes(0)
40
+ })
41
+
42
+ it('should shift a contiguous block of highlighted options down as a group', () => {
43
+ moveHighlightedPickedOptionDown({
44
+ selected: ['a', 'b', 'c', 'd', 'e'],
45
+ highlightedPickedOptions: ['b', 'c'],
46
+ onChange,
47
+ })
48
+
49
+ expect(onChange).toHaveBeenCalledWith({
50
+ selected: ['a', 'd', 'b', 'c', 'e'],
51
+ })
52
+ })
53
+
54
+ it('should collapse and shift a non-contiguous selection down in one call', () => {
55
+ moveHighlightedPickedOptionDown({
56
+ selected: ['a', 'b', 'c', 'd', 'e'],
57
+ highlightedPickedOptions: ['b', 'd'],
58
+ onChange,
59
+ })
60
+
61
+ expect(onChange).toHaveBeenCalledWith({
62
+ selected: ['a', 'c', 'e', 'b', 'd'],
63
+ })
64
+ })
65
+
66
+ it('should preserve the relative order of highlighted items regardless of input order', () => {
67
+ moveHighlightedPickedOptionDown({
68
+ selected: ['a', 'b', 'c', 'd', 'e'],
69
+ highlightedPickedOptions: ['d', 'b'],
70
+ onChange,
71
+ })
72
+
73
+ expect(onChange).toHaveBeenCalledWith({
74
+ selected: ['a', 'c', 'e', 'b', 'd'],
75
+ })
76
+ })
77
+
78
+ it('should collapse a non-contiguous selection containing the last item without shifting past the end', () => {
79
+ moveHighlightedPickedOptionDown({
80
+ selected: ['a', 'b', 'c'],
81
+ highlightedPickedOptions: ['a', 'c'],
82
+ onChange,
83
+ })
84
+
85
+ expect(onChange).toHaveBeenCalledWith({
86
+ selected: ['b', 'a', 'c'],
87
+ })
88
+ })
89
+
90
+ it('should do nothing when the highlighted block is already flush to the bottom', () => {
91
+ moveHighlightedPickedOptionDown({
92
+ selected: ['a', 'b', 'c', 'd'],
93
+ highlightedPickedOptions: ['c', 'd'],
94
+ onChange,
95
+ })
96
+
97
+ expect(onChange).toHaveBeenCalledTimes(0)
98
+ })
99
+
100
+ it('should ignore highlighted values that do not exist in selected', () => {
101
+ moveHighlightedPickedOptionDown({
102
+ selected: ['a', 'b', 'c'],
103
+ highlightedPickedOptions: ['ghost', 'a'],
104
+ onChange,
105
+ })
106
+
107
+ expect(onChange).toHaveBeenCalledWith({
108
+ selected: ['b', 'a', 'c'],
109
+ })
110
+ })
111
+ })