@dhis2-ui/select 10.9.1 → 10.10.0-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 (148) hide show
  1. package/build/cjs/index.js +18 -4
  2. package/build/cjs/locales/en/translations.json +7 -5
  3. package/build/cjs/simple-single-select/__stories__/DefaultPosition.js +38 -0
  4. package/build/cjs/simple-single-select/__stories__/Dense.js +22 -0
  5. package/build/cjs/simple-single-select/__stories__/Empty.js +20 -0
  6. package/build/cjs/simple-single-select/__stories__/EmptyWithEmptyNode.js +28 -0
  7. package/build/cjs/simple-single-select/__stories__/EmptyWithEmptyText.js +21 -0
  8. package/build/cjs/simple-single-select/__stories__/FlippedPosition.js +37 -0
  9. package/build/cjs/simple-single-select/__stories__/InfiniteLoading.js +61 -0
  10. package/build/cjs/simple-single-select/__stories__/Loading.js +20 -0
  11. package/build/cjs/simple-single-select/__stories__/ShiftedIntoView.js +28 -0
  12. package/build/cjs/simple-single-select/__stories__/WithClearButton.js +25 -0
  13. package/build/cjs/simple-single-select/__stories__/WithCustomLowMaxHeight.js +22 -0
  14. package/build/cjs/simple-single-select/__stories__/WithCustomOptions.js +48 -0
  15. package/build/cjs/simple-single-select/__stories__/WithDisabledOption.js +28 -0
  16. package/build/cjs/simple-single-select/__stories__/WithFilterField.js +36 -0
  17. package/build/cjs/simple-single-select/__stories__/WithInitialFocus.js +38 -0
  18. package/build/cjs/simple-single-select/__stories__/WithManyOptions.js +24 -0
  19. package/build/cjs/simple-single-select/__stories__/WithNoMatchText.js +30 -0
  20. package/build/cjs/simple-single-select/__stories__/WithOnBlur.js +23 -0
  21. package/build/cjs/simple-single-select/__stories__/WithOnFocus.js +23 -0
  22. package/build/cjs/simple-single-select/__stories__/WithOptionsAndDisabled.js +22 -0
  23. package/build/cjs/simple-single-select/__stories__/WithOptionsAndLoading.js +34 -0
  24. package/build/cjs/simple-single-select/__stories__/WithOptionsAndLoadingText.js +35 -0
  25. package/build/cjs/simple-single-select/__stories__/WithPlaceholder.js +23 -0
  26. package/build/cjs/simple-single-select/__stories__/WithPlaceholderAndSelection.js +25 -0
  27. package/build/cjs/simple-single-select/__stories__/WithPrefix.js +22 -0
  28. package/build/cjs/simple-single-select/__stories__/WithPrefixAndSelection.js +25 -0
  29. package/build/cjs/simple-single-select/__stories__/WithRTL.js +34 -0
  30. package/build/cjs/simple-single-select/__stories__/WithSelection.js +24 -0
  31. package/build/cjs/simple-single-select/__stories__/WithSelectionAndDisabled.js +25 -0
  32. package/build/cjs/simple-single-select/__stories__/WithoutOptionsAndLoading.js +21 -0
  33. package/build/cjs/simple-single-select/__stories__/WithoutSelection.js +22 -0
  34. package/build/cjs/simple-single-select/__stories__/options.js +77 -0
  35. package/build/cjs/simple-single-select/features/keyboard-interactions.test.e2e.js +441 -0
  36. package/build/cjs/simple-single-select/features/menu-positioning.test.e2e.js +152 -0
  37. package/build/cjs/simple-single-select/index.js +19 -0
  38. package/build/cjs/simple-single-select/is-option-hidden.js +22 -0
  39. package/build/cjs/simple-single-select/menu/empty.js +26 -0
  40. package/build/cjs/simple-single-select/menu/filter.js +60 -0
  41. package/build/cjs/simple-single-select/menu/index.js +12 -0
  42. package/build/cjs/simple-single-select/menu/loading.js +30 -0
  43. package/build/cjs/simple-single-select/menu/menu.js +158 -0
  44. package/build/cjs/simple-single-select/menu/no-match.js +28 -0
  45. package/build/cjs/simple-single-select/menu/option.js +120 -0
  46. package/build/cjs/simple-single-select/menu/options-list.js +97 -0
  47. package/build/cjs/simple-single-select/selected-value/clear-button.js +74 -0
  48. package/build/cjs/simple-single-select/selected-value/container.js +100 -0
  49. package/build/cjs/simple-single-select/selected-value/index.js +12 -0
  50. package/build/cjs/simple-single-select/selected-value/placeholder.js +30 -0
  51. package/build/cjs/simple-single-select/selected-value/prefix.js +30 -0
  52. package/build/cjs/simple-single-select/selected-value/selected-value.js +119 -0
  53. package/build/cjs/simple-single-select/shared-prop-types.js +14 -0
  54. package/build/cjs/simple-single-select/simple-single-select.e2e.stories.js +156 -0
  55. package/build/cjs/simple-single-select/simple-single-select.js +319 -0
  56. package/build/cjs/simple-single-select/simple-single-select.prod.stories.js +228 -0
  57. package/build/cjs/simple-single-select/simple-single-select.test.js +1106 -0
  58. package/build/cjs/simple-single-select/use-handle-key-press/index.js +19 -0
  59. package/build/cjs/simple-single-select/use-handle-key-press/use-focus-option.js +24 -0
  60. package/build/cjs/simple-single-select/use-handle-key-press/use-handle-key-press-on-combobox.js +177 -0
  61. package/build/cjs/simple-single-select/use-handle-key-press/use-handle-key-press-on-filter-input.js +106 -0
  62. package/build/cjs/simple-single-select/use-handle-key-press/use-handle-typing.js +81 -0
  63. package/build/cjs/simple-single-select/use-handle-key-press/use-highlight-first-option-on-previous-page.js +50 -0
  64. package/build/cjs/simple-single-select/use-handle-key-press/use-highlight-first-visible-option.js +30 -0
  65. package/build/cjs/simple-single-select/use-handle-key-press/use-highlight-last-option-on-next-page.js +50 -0
  66. package/build/cjs/simple-single-select/use-handle-key-press/use-highlight-last-visible-option.js +42 -0
  67. package/build/cjs/simple-single-select/use-handle-key-press/use-page-down.js +45 -0
  68. package/build/cjs/simple-single-select/use-handle-key-press/use-page-up.js +44 -0
  69. package/build/cjs/simple-single-select/use-handle-key-press/utils.js +46 -0
  70. package/build/cjs/simple-single-select-field/index.js +12 -0
  71. package/build/cjs/simple-single-select-field/simple-single-select-field.js +138 -0
  72. package/build/cjs/simple-single-select-field/simple-single-select-field.prod.stories.js +188 -0
  73. package/build/cjs/simple-single-select-field/simple-single-select-field.test.js +133 -0
  74. package/build/es/index.js +2 -0
  75. package/build/es/locales/en/translations.json +7 -5
  76. package/build/es/simple-single-select/__stories__/DefaultPosition.js +29 -0
  77. package/build/es/simple-single-select/__stories__/Dense.js +13 -0
  78. package/build/es/simple-single-select/__stories__/Empty.js +11 -0
  79. package/build/es/simple-single-select/__stories__/EmptyWithEmptyNode.js +19 -0
  80. package/build/es/simple-single-select/__stories__/EmptyWithEmptyText.js +12 -0
  81. package/build/es/simple-single-select/__stories__/FlippedPosition.js +28 -0
  82. package/build/es/simple-single-select/__stories__/InfiniteLoading.js +53 -0
  83. package/build/es/simple-single-select/__stories__/Loading.js +12 -0
  84. package/build/es/simple-single-select/__stories__/ShiftedIntoView.js +18 -0
  85. package/build/es/simple-single-select/__stories__/WithClearButton.js +16 -0
  86. package/build/es/simple-single-select/__stories__/WithCustomLowMaxHeight.js +13 -0
  87. package/build/es/simple-single-select/__stories__/WithCustomOptions.js +38 -0
  88. package/build/es/simple-single-select/__stories__/WithDisabledOption.js +19 -0
  89. package/build/es/simple-single-select/__stories__/WithFilterField.js +27 -0
  90. package/build/es/simple-single-select/__stories__/WithInitialFocus.js +29 -0
  91. package/build/es/simple-single-select/__stories__/WithManyOptions.js +15 -0
  92. package/build/es/simple-single-select/__stories__/WithNoMatchText.js +21 -0
  93. package/build/es/simple-single-select/__stories__/WithOnBlur.js +14 -0
  94. package/build/es/simple-single-select/__stories__/WithOnFocus.js +14 -0
  95. package/build/es/simple-single-select/__stories__/WithOptionsAndDisabled.js +13 -0
  96. package/build/es/simple-single-select/__stories__/WithOptionsAndLoading.js +25 -0
  97. package/build/es/simple-single-select/__stories__/WithOptionsAndLoadingText.js +26 -0
  98. package/build/es/simple-single-select/__stories__/WithPlaceholder.js +14 -0
  99. package/build/es/simple-single-select/__stories__/WithPlaceholderAndSelection.js +16 -0
  100. package/build/es/simple-single-select/__stories__/WithPrefix.js +13 -0
  101. package/build/es/simple-single-select/__stories__/WithPrefixAndSelection.js +16 -0
  102. package/build/es/simple-single-select/__stories__/WithRTL.js +25 -0
  103. package/build/es/simple-single-select/__stories__/WithSelection.js +15 -0
  104. package/build/es/simple-single-select/__stories__/WithSelectionAndDisabled.js +16 -0
  105. package/build/es/simple-single-select/__stories__/WithoutOptionsAndLoading.js +12 -0
  106. package/build/es/simple-single-select/__stories__/WithoutSelection.js +13 -0
  107. package/build/es/simple-single-select/__stories__/options.js +71 -0
  108. package/build/es/simple-single-select/features/keyboard-interactions.test.e2e.js +439 -0
  109. package/build/es/simple-single-select/features/menu-positioning.test.e2e.js +150 -0
  110. package/build/es/simple-single-select/index.js +2 -0
  111. package/build/es/simple-single-select/is-option-hidden.js +16 -0
  112. package/build/es/simple-single-select/menu/empty.js +19 -0
  113. package/build/es/simple-single-select/menu/filter.js +53 -0
  114. package/build/es/simple-single-select/menu/index.js +1 -0
  115. package/build/es/simple-single-select/menu/loading.js +23 -0
  116. package/build/es/simple-single-select/menu/menu.js +149 -0
  117. package/build/es/simple-single-select/menu/no-match.js +21 -0
  118. package/build/es/simple-single-select/menu/option.js +111 -0
  119. package/build/es/simple-single-select/menu/options-list.js +88 -0
  120. package/build/es/simple-single-select/selected-value/clear-button.js +68 -0
  121. package/build/es/simple-single-select/selected-value/container.js +91 -0
  122. package/build/es/simple-single-select/selected-value/index.js +1 -0
  123. package/build/es/simple-single-select/selected-value/placeholder.js +23 -0
  124. package/build/es/simple-single-select/selected-value/prefix.js +23 -0
  125. package/build/es/simple-single-select/selected-value/selected-value.js +112 -0
  126. package/build/es/simple-single-select/shared-prop-types.js +7 -0
  127. package/build/es/simple-single-select/simple-single-select.e2e.stories.js +142 -0
  128. package/build/es/simple-single-select/simple-single-select.js +310 -0
  129. package/build/es/simple-single-select/simple-single-select.prod.stories.js +36 -0
  130. package/build/es/simple-single-select/simple-single-select.test.js +1103 -0
  131. package/build/es/simple-single-select/use-handle-key-press/index.js +2 -0
  132. package/build/es/simple-single-select/use-handle-key-press/use-focus-option.js +18 -0
  133. package/build/es/simple-single-select/use-handle-key-press/use-handle-key-press-on-combobox.js +171 -0
  134. package/build/es/simple-single-select/use-handle-key-press/use-handle-key-press-on-filter-input.js +100 -0
  135. package/build/es/simple-single-select/use-handle-key-press/use-handle-typing.js +75 -0
  136. package/build/es/simple-single-select/use-handle-key-press/use-highlight-first-option-on-previous-page.js +44 -0
  137. package/build/es/simple-single-select/use-handle-key-press/use-highlight-first-visible-option.js +24 -0
  138. package/build/es/simple-single-select/use-handle-key-press/use-highlight-last-option-on-next-page.js +44 -0
  139. package/build/es/simple-single-select/use-handle-key-press/use-highlight-last-visible-option.js +36 -0
  140. package/build/es/simple-single-select/use-handle-key-press/use-page-down.js +39 -0
  141. package/build/es/simple-single-select/use-handle-key-press/use-page-up.js +38 -0
  142. package/build/es/simple-single-select/use-handle-key-press/utils.js +36 -0
  143. package/build/es/simple-single-select-field/index.js +1 -0
  144. package/build/es/simple-single-select-field/simple-single-select-field.js +131 -0
  145. package/build/es/simple-single-select-field/simple-single-select-field.prod.stories.js +172 -0
  146. package/build/es/simple-single-select-field/simple-single-select-field.test.js +130 -0
  147. package/package.json +15 -15
  148. package/types/index.d.ts +184 -1
@@ -0,0 +1,441 @@
1
+ "use strict";
2
+
3
+ describe('<SimpleSingleSelect/>', () => {
4
+ it('should highlight the first option on the currently displayed page', () => {
5
+ cy.log('**Given a select with 100 options is displayed**');
6
+ cy.visitStory('Simple Single Select', 'Hundret Options');
7
+ cy.log('**And the menu is visible**');
8
+ cy.findByRole('combobox').click();
9
+ cy.findByRole('option', {
10
+ selected: true
11
+ }).should('be.visible');
12
+ cy.log('**And the 74th option is being highlighted**');
13
+ cy.findByRole('combobox').focus().type(`Select option 73`);
14
+ cy.log('**And the 70th option is the first option on the current page**');
15
+ let optionOffset;
16
+ cy.findAllByRole('option').eq(70).then(option => {
17
+ const {
18
+ offsetTop
19
+ } = option.get(0);
20
+ optionOffset = offsetTop;
21
+ });
22
+ cy.findByRole('option', {
23
+ selected: true
24
+ }).invoke('parent') // listbox
25
+ .invoke('parent') // scrollable div
26
+ .then(listBoxParent => {
27
+ listBoxParent.get(0).scrollTop = optionOffset;
28
+ });
29
+ cy.findAllByRole('option').eq(70).should('be.visible');
30
+ cy.log('**When the PageUp key is pressed**');
31
+ cy.findByRole('combobox').trigger('keydown', {
32
+ key: 'PageUp',
33
+ force: true
34
+ });
35
+ cy.log('**Then the first option on the currently displayed page is highlighted**');
36
+ // For some reason, it takes a little time to change the highlighted option
37
+ cy.wait(1);
38
+ cy.findByRole('option', {
39
+ selected: true
40
+ }).invoke('attr', 'aria-label').should('equal', 'Select option 70');
41
+ });
42
+ it('should highlight the first enabled option before the first (disabled) option on the currently displayed page', () => {
43
+ cy.log('**Given a select with 100 options is displayed and option #17 is disabled**');
44
+ cy.visitStory('Simple Single Select', 'Hundret Options With Disabled');
45
+ cy.log('**And the menu is visible**');
46
+ cy.findByRole('combobox').click();
47
+ cy.findByRole('option', {
48
+ selected: true
49
+ }).should('be.visible');
50
+ cy.log('**And the 26th option is being highlighted**');
51
+ cy.findByRole('combobox').focus().type(`Select option 25`);
52
+ cy.log('**And the 17th option is the first option on the current page**');
53
+ let optionOffset;
54
+ cy.findAllByRole('option').eq(17).then(option => {
55
+ const {
56
+ offsetTop
57
+ } = option.get(0);
58
+ optionOffset = offsetTop;
59
+ });
60
+ cy.findByRole('option', {
61
+ selected: true
62
+ }).invoke('parent') // listbox
63
+ .invoke('parent') // scrollable div
64
+ .then(listBoxParent => {
65
+ listBoxParent.get(0).scrollTop = optionOffset;
66
+ });
67
+ cy.findAllByRole('option').eq(17).should('be.visible');
68
+ cy.log('**When the PageUp key is pressed**');
69
+ cy.findByRole('combobox').trigger('keydown', {
70
+ key: 'PageUp',
71
+ force: true
72
+ });
73
+ cy.log('**Then the first option on the currently displayed page is highlighted**');
74
+ // For some reason, it takes a little time to change the highlighted option
75
+ cy.wait(1);
76
+ cy.findByRole('option', {
77
+ selected: true
78
+ }).invoke('attr', 'aria-label').should('equal', 'Select option 16');
79
+ });
80
+ it('should highlight the first option on the previous page', () => {
81
+ cy.log('**Given a select with 100 options is displayed**');
82
+ cy.visitStory('Simple Single Select', 'Hundret Options');
83
+ cy.log('**And the menu is visible**');
84
+ cy.findByRole('combobox').click();
85
+ cy.findByRole('option', {
86
+ selected: true
87
+ }).should('be.visible');
88
+ cy.log('**And the 70th option is being highlighted**');
89
+ // Will automatically scroll there and make it the first option on the page
90
+ cy.findByRole('combobox').focus().type(`Select option 70`);
91
+ cy.log('**When the PageUp key is pressed**');
92
+ cy.findByRole('combobox').trigger('keydown', {
93
+ key: 'PageUp',
94
+ force: true
95
+ });
96
+ cy.log('**Then the first option on the previous page is highlighted**');
97
+ // For some reason, it takes a little time to change the highlighted option
98
+ cy.wait(1);
99
+ cy.findByRole('option', {
100
+ selected: true
101
+ }).invoke('attr', 'aria-label').should('equal', 'Select option 61');
102
+
103
+ // That option is truly the first option
104
+ cy.get('[role="option"]:visible').first().invoke('attr', 'aria-label').should('equal', 'Select option 61');
105
+ cy.log('**And the previously highlighted option is not visible**');
106
+ cy.findAllByRole('option').eq(70).should('not.be.visible');
107
+ });
108
+ it('should highlight the first enabled option before the first (disabled) option on the previous page', () => {
109
+ cy.log('**Given a select with 100 options is displayed and option #17 is disabled**');
110
+ cy.visitStory('Simple Single Select', 'Hundret Options With Disabled');
111
+ cy.log('**And the menu is visible**');
112
+ cy.findByRole('combobox').click();
113
+ cy.findByRole('option', {
114
+ selected: true
115
+ }).should('be.visible');
116
+ cy.log('**And the 27th option is being highlighted**');
117
+ // Will automatically scroll there and make it the first option on the page
118
+ cy.findByRole('combobox').focus().type(`Select option 27`);
119
+ cy.log('**When the PageUp key is pressed**');
120
+ cy.findByRole('combobox').trigger('keydown', {
121
+ key: 'PageUp',
122
+ force: true
123
+ });
124
+ cy.log('**Then the first option on the previous page is highlighted**');
125
+ // For some reason, it takes a little time to change the highlighted option
126
+ cy.wait(1);
127
+ cy.findByRole('option', {
128
+ selected: true
129
+ }).invoke('attr', 'aria-label').should('equal', 'Select option 16');
130
+ cy.log('**And that option is truly the first option**');
131
+ cy.get('[role="option"]:visible').first().invoke('attr', 'aria-label').should('equal', 'Select option 16');
132
+ cy.log('**And the previously highlighted option is not visible**');
133
+ cy.findAllByRole('option').eq(27).should('not.be.visible');
134
+ });
135
+ it('should highlight the first option', () => {
136
+ cy.log('**Given a select with 100 options is displayed**');
137
+ cy.visitStory('Simple Single Select', 'Hundret Options');
138
+ cy.log('**And the menu is visible**');
139
+ cy.findByRole('combobox').click();
140
+ cy.findByRole('option', {
141
+ selected: true
142
+ }).should('be.visible');
143
+ cy.log('**And the 2nd option is being highlighted**');
144
+ cy.findByRole('combobox').focus().type(`Select option 1`);
145
+ cy.log('**And the 2nd option is the first option on the current page**');
146
+ let optionOffset;
147
+ cy.findAllByRole('option').eq(1).then(option => {
148
+ const {
149
+ offsetTop
150
+ } = option.get(0);
151
+ optionOffset = offsetTop;
152
+ });
153
+ cy.findByRole('option', {
154
+ selected: true
155
+ }).invoke('parent') // listbox
156
+ .invoke('parent') // scrollable div
157
+ .then(listBoxParent => {
158
+ listBoxParent.get(0).scrollTop = optionOffset;
159
+ });
160
+ cy.log('**When the PageUp key is pressed**');
161
+ cy.findByRole('combobox').trigger('keydown', {
162
+ key: 'PageUp',
163
+ force: true
164
+ });
165
+ cy.log('**Then the first option is being highlighted**');
166
+ // For some reason, it takes a little time to change the highlighted option
167
+ cy.wait(1);
168
+ cy.all(() => cy.findAllByRole('option').invoke('get', 0), () => cy.findByRole('option', {
169
+ selected: true
170
+ }).invoke('get', 0)).then(_ref => {
171
+ let [firstOption, highlightedOption] = _ref;
172
+ expect(highlightedOption).to.equal(firstOption);
173
+ });
174
+ });
175
+ it('should highlight the second option when the first option is disabled', () => {
176
+ cy.log('**Given a select with 100 options is displayed and option #2 is disabled**');
177
+ cy.visitStory('Simple Single Select', 'Hundret Options With Disabled');
178
+ cy.log('**And the menu is visible**');
179
+ cy.findByRole('combobox').click();
180
+ cy.findByRole('option', {
181
+ selected: true
182
+ }).should('be.visible');
183
+ cy.log('**And the 3rd option is being highlighted**');
184
+ cy.findByRole('combobox').focus().type(`Select option 2`);
185
+ cy.log('**And the 3rd option is the first option on the current page**');
186
+ let optionOffset;
187
+ cy.findAllByRole('option').eq(2).then(option => {
188
+ const {
189
+ offsetTop
190
+ } = option.get(0);
191
+ optionOffset = offsetTop;
192
+ });
193
+ cy.findByRole('option', {
194
+ selected: true
195
+ }).invoke('parent') // listbox
196
+ .invoke('parent') // scrollable div
197
+ .then(listBoxParent => {
198
+ listBoxParent.get(0).scrollTop = optionOffset;
199
+ });
200
+ cy.log('**When the PageUp key is pressed**');
201
+ cy.findByRole('combobox').trigger('keydown', {
202
+ key: 'PageUp',
203
+ force: true
204
+ });
205
+ cy.log('**Then the second option is being highlighted**');
206
+ // For some reason, it takes a little time to change the highlighted option
207
+ cy.wait(1);
208
+ cy.all(() => cy.findAllByRole('option').invoke('get', 1), () => cy.findByRole('option', {
209
+ selected: true
210
+ }).invoke('get', 0)).then(_ref2 => {
211
+ let [firstOption, highlightedOption] = _ref2;
212
+ expect(highlightedOption).to.equal(firstOption);
213
+ });
214
+ });
215
+ it('should highlight the last option on the currently displayed page', () => {
216
+ cy.log('**Given a select with 100 options is displayed**');
217
+ cy.visitStory('Simple Single Select', 'Hundret Options');
218
+ cy.log('**And the menu is visible**');
219
+ // first option will be highlighted automatically
220
+ cy.findByRole('combobox').click();
221
+ cy.findByRole('option', {
222
+ selected: true
223
+ }).should('be.visible');
224
+ cy.log('**When the PageDown key is pressed**');
225
+ cy.findByRole('combobox').trigger('keydown', {
226
+ key: 'PageDown',
227
+ force: true
228
+ });
229
+ cy.log('**Then the last option on the currently displayed page is highlighted**');
230
+ // For some reason, it takes a little time to change the highlighted option
231
+ cy.wait(1);
232
+ cy.all(() => cy.get('[role="option"]:visible').last().invoke('get', 0), () => cy.findByRole('option', {
233
+ selected: true
234
+ }).invoke('get', 0)).then(_ref3 => {
235
+ let [lastVisibleOption, highlightedOption] = _ref3;
236
+ expect(highlightedOption).to.equal(lastVisibleOption);
237
+ });
238
+ });
239
+ it('should highlight the first enabled option after the last (disabled) option on the currently displayed page', () => {
240
+ cy.log('**Given a select with 100 options is displayed and option #17 is disabled**');
241
+ cy.visitStory('Simple Single Select', 'Hundret Options With Disabled');
242
+ cy.log('**And the 9th option is being selected (will cause it to be the first option on the page when opening the menu)**');
243
+ cy.findByRole('combobox').focus().type(`Select option 9`);
244
+ cy.log('**And the menu is visible**');
245
+ // first option will be highlighted automatically
246
+ cy.findByRole('combobox').click();
247
+ cy.findByRole('option', {
248
+ selected: true
249
+ }).should('be.visible');
250
+
251
+ // That option is truly the first option
252
+ cy.get('[role="option"]:visible').first().invoke('attr', 'aria-label').should('equal', 'Select option 9');
253
+ cy.log('**When the PageDown key is pressed**');
254
+ cy.findByRole('combobox').trigger('keydown', {
255
+ key: 'PageDown',
256
+ force: true
257
+ });
258
+
259
+ // For some reason, it takes a little time to change the highlighted option
260
+ cy.wait(1);
261
+ cy.log('**Then the first enabled option after last option on the currently displayed page is highlighted**');
262
+ cy.all(() => cy.findAllByRole('option').invoke('get', 19), () => cy.findByRole('option', {
263
+ selected: true
264
+ }).invoke('get', 0)).then(_ref4 => {
265
+ let [eighteensOptions, highlightedOption] = _ref4;
266
+ expect(highlightedOption).to.equal(eighteensOptions);
267
+ });
268
+ cy.log('**And the first enabled option after last option on the currently displayed page is the first visible option**');
269
+ cy.all(() => cy.get('[role="option"]:visible').invoke('get', 0), () => cy.findByRole('option', {
270
+ selected: true
271
+ }).invoke('get', 0)).then(_ref5 => {
272
+ let [lastVisibleOption, highlightedOption] = _ref5;
273
+ expect(highlightedOption).to.equal(lastVisibleOption);
274
+ });
275
+ });
276
+ it('should highlight the last option on the next page',
277
+ // We don't want the options to scroll when we check whether they're
278
+ // visible or not (as that'd make them visible)
279
+ {
280
+ scrollBehavior: false
281
+ }, () => {
282
+ const LAST_VISIBLE_OPTION_INDEX = 8;
283
+ cy.log('**Given a select with 100 options is displayed**');
284
+ cy.visitStory('Simple Single Select', 'Hundret Options');
285
+ cy.log('**And the last option on the first page is selected**');
286
+ cy.findByRole('combobox').focus().type(`Select option 8`);
287
+ cy.log('**And the menu is visible**');
288
+ cy.findByRole('combobox').click();
289
+ cy.findByRole('option', {
290
+ selected: true
291
+ }).should('be.visible');
292
+ cy.get('[role="option"]').eq(LAST_VISIBLE_OPTION_INDEX).invoke('attr', 'aria-selected').should('equal', 'true');
293
+ cy.log('**When the PageDown key is pressed**');
294
+ cy.findByRole('combobox').trigger('keydown', {
295
+ key: 'PageDown',
296
+ force: true
297
+ });
298
+ cy.log('**Then the next page is shown**');
299
+ cy.get('[role="option"]').eq(LAST_VISIBLE_OPTION_INDEX).should('not.be.visible');
300
+ cy.get('[role="option"]').eq(LAST_VISIBLE_OPTION_INDEX + 1).should('be.visible');
301
+ cy.log('**And the last option on the next page is highlighted**');
302
+ cy.get('[role="option"]:visible').last().invoke('attr', 'aria-selected').should('equal', 'true');
303
+ cy.log('**And the previously highlighted option is not visible**');
304
+ cy.get('[role="option"]').eq(LAST_VISIBLE_OPTION_INDEX + 1).invoke('attr', 'aria-selected').should('equal', 'false');
305
+ });
306
+ it('should highlight the first enabled option after the last (disabled) option on the next page',
307
+ // We don't want the options to scroll when we check whether they're
308
+ // visible or not (as that'd make them visible)
309
+ {
310
+ scrollBehavior: false
311
+ }, () => {
312
+ cy.log('**Given a select with 100 options is displayed and option #17 is disabled**');
313
+ cy.visitStory('Simple Single Select', 'Hundret Options With Disabled');
314
+ cy.log('**And the 2nd option is being selected**');
315
+ cy.findByRole('combobox').focus().type(`Select option 9`);
316
+ cy.log('**And the menu is visible**');
317
+ cy.findByRole('combobox').click();
318
+ cy.findByRole('option', {
319
+ selected: true
320
+ }).should('be.visible');
321
+ cy.log('**And the 2nd option is the first option on the current page**');
322
+ cy.all(() => cy.findAllByRole('option').eq(1),
323
+ // scrollable div
324
+ () => cy.findByRole('option', {
325
+ selected: true
326
+ }).invoke('parent').invoke('parent')).then(_ref6 => {
327
+ let [nextTopOption, listBoxParent] = _ref6;
328
+ const {
329
+ offsetTop
330
+ } = nextTopOption.get(0);
331
+ listBoxParent.get(0).scrollTop = offsetTop;
332
+ });
333
+ cy.wrap(9).as('lastVisibleOptionIndex');
334
+ cy.get('@lastVisibleOptionIndex').then(lastVisibleOptionIndex => {
335
+ cy.get('[role="option"]').eq(lastVisibleOptionIndex).invoke('attr', 'aria-selected').should('equal', 'true');
336
+ });
337
+ cy.log('**When the PageDown key is pressed**');
338
+ cy.findByRole('combobox').trigger('keydown', {
339
+ key: 'PageDown',
340
+ force: true
341
+ });
342
+ cy.log('**Then the next page is shown,**');
343
+ // but one option is skipped due to the disabled option at the end of the next page
344
+ cy.get('@lastVisibleOptionIndex').then(lastVisibleOptionIndex => {
345
+ cy.get('[role="option"]').eq(lastVisibleOptionIndex + 1).should('not.be.visible');
346
+ cy.get('[role="option"]').eq(lastVisibleOptionIndex + 2).should('be.visible');
347
+ });
348
+ cy.log('**And the last option on the next page is highlighted**');
349
+ cy.get('[role="option"]:visible').last().invoke('attr', 'aria-selected').should('equal', 'true');
350
+ cy.get('[role="option"]:visible').last().invoke('attr', 'aria-label').should('equal', 'Select option 19');
351
+ });
352
+ it('should highlight the last option',
353
+ // We don't want the options to scroll when we check whether they're
354
+ // visible or not (as that'd make them visible)
355
+ {
356
+ scrollBehavior: false
357
+ }, () => {
358
+ cy.log('**Given a select with 100 options is displayed**');
359
+ cy.visitStory('Simple Single Select', 'Hundret Options');
360
+ cy.log('**And the menu is visible**');
361
+ cy.findByRole('combobox').click();
362
+ cy.findByRole('option', {
363
+ selected: true
364
+ }).should('be.visible');
365
+ cy.log('**And the 2nd-last option is being highlighted and visible**');
366
+ for (let i = 0; i < 98;
367
+ // This will bring us to the second last option exactly
368
+ ++i) {
369
+ cy.findByRole('combobox').trigger('keydown', {
370
+ key: 'ArrowDown',
371
+ force: true
372
+ });
373
+ cy.wait(1);
374
+ }
375
+ cy.findAllByRole('option').eq(98).should('be.visible');
376
+ cy.log('**And the last option is not visible**');
377
+ cy.findAllByRole('option').last().should('not.be.visible');
378
+ cy.log('**When the PageDown key is pressed**');
379
+ cy.findByRole('combobox').trigger('keydown', {
380
+ key: 'PageDown',
381
+ force: true
382
+ });
383
+ cy.log('**Then the last option is highlighted**');
384
+ // For some reason, it takes a little time to change the highlighted option
385
+ cy.wait(1);
386
+ cy.all(() => cy.findAllByRole('option').last().invoke('get', 0), () => cy.findByRole('option', {
387
+ selected: true
388
+ }).invoke('get', 0)).then(_ref7 => {
389
+ let [lastOption, highlightedOption] = _ref7;
390
+ expect(highlightedOption).to.equal(lastOption);
391
+ });
392
+ cy.log('**And the last option is visible**');
393
+ cy.findAllByRole('option').last().should('be.visible');
394
+ });
395
+ it('should highlight the last enabled option',
396
+ // We don't want the options to scroll when we check whether they're
397
+ // visible or not (as that'd make them visible)
398
+ {
399
+ scrollBehavior: false
400
+ }, () => {
401
+ cy.log('**Given a select with 100 options is displayed and option #99 is disabled**');
402
+ cy.visitStory('Simple Single Select', 'Hundret Options With Disabled');
403
+ cy.log('**And the 3rd-last option is being selected**');
404
+ cy.findByRole('combobox').focus().type(`Select option 97`);
405
+ cy.log('**And the menu is visible**');
406
+ cy.findByRole('combobox').click();
407
+ cy.findByRole('option', {
408
+ selected: true
409
+ }).should('be.visible');
410
+ cy.log('**And the 3rd-last option is the last option on the current page**');
411
+ cy.all(() => cy.findAllByRole('option').eq(89), () => cy.findByRole('listbox').invoke('parent') // scrollable div
412
+ ).then(_ref8 => {
413
+ let [nextTopOption, listBoxParent] = _ref8;
414
+ const {
415
+ offsetTop
416
+ } = nextTopOption.get(0);
417
+ listBoxParent.get(0).scrollTop = offsetTop;
418
+ });
419
+ cy.findAllByRole('option').eq(97).should('be.visible');
420
+ cy.findAllByRole('option').eq(98).should('not.be.visible');
421
+ cy.findAllByRole('option').last().should('not.be.visible');
422
+ cy.log('**When the PageDown key is pressed**');
423
+ cy.findByRole('combobox').trigger('keydown', {
424
+ key: 'PageDown',
425
+ force: true
426
+ });
427
+ cy.log('**Then the last option is highlighted**');
428
+ // For some reason, it takes a little time to change the highlighted option
429
+ cy.wait(1);
430
+ cy.all(() => cy.findAllByRole('option').invoke('get', 98), () => cy.findByRole('option', {
431
+ selected: true
432
+ }).invoke('get', 0)).then(_ref9 => {
433
+ let [secondLastOption, highlightedOption] = _ref9;
434
+ expect(highlightedOption).to.equal(secondLastOption);
435
+ });
436
+ cy.log('**And the second last option is visible**');
437
+ cy.findAllByRole('option').eq(98).should('be.visible');
438
+ cy.log('**And the last option is not visible**');
439
+ cy.findAllByRole('option').eq(99).should('not.be.visible');
440
+ });
441
+ });
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+
3
+ describe('SimpleSingleSelect: Menu positioning', () => {
4
+ it('should open in the default position', () => {
5
+ // Given there is enough space below the anchor to fit the SingleSelect menu
6
+ cy.visitStory('Simple Single Select', 'Default position');
7
+
8
+ // When the SingleSelect is clicked
9
+ cy.findByRole('combobox').click();
10
+
11
+ // Then the top of the menu is aligned with the bottom of the input
12
+ cy.all(() => cy.findByRole('combobox'), () => cy.findByRole('listbox')).should(_ref => {
13
+ let [selectedValue, menu] = _ref;
14
+ expect(selectedValue.length).to.equal(1);
15
+ expect(menu.length).to.equal(1);
16
+ const $selectedValue = selectedValue[0];
17
+ const $menu = menu[0];
18
+ const selectedValueRect = $selectedValue.getBoundingClientRect();
19
+ const menuRect = $menu.getBoundingClientRect();
20
+
21
+ // The listbox is inside a container with a border,
22
+ // hence the increased delta
23
+ expect(menuRect.top).to.be.closeTo(selectedValueRect.bottom, 2);
24
+ });
25
+
26
+ // And the left of the SingleSelect is aligned with the left of the anchor
27
+ cy.all(() => cy.findByRole('combobox'), () => cy.findByRole('listbox')).should(_ref2 => {
28
+ let [selectedValue, menu] = _ref2;
29
+ expect(selectedValue.length).to.equal(1);
30
+ expect(menu.length).to.equal(1);
31
+ const $selectedValue = selectedValue[0];
32
+ const $menu = menu[0];
33
+ const selectedValueRect = $selectedValue.getBoundingClientRect();
34
+ const menuRect = $menu.getBoundingClientRect();
35
+
36
+ // The listbox is inside a container with a border,
37
+ // hence the increased delta
38
+ expect(selectedValueRect.left).to.be.closeTo(menuRect.left, 2);
39
+ });
40
+ });
41
+ it('should flipp the position when insufficient space below', () => {
42
+ // Given there is not enough space below the anchor to fit the SingleSelect menu
43
+ cy.visitStory('Simple Single Select', 'Flipped position');
44
+
45
+ // When the SingleSelect is clicked
46
+ cy.findByRole('combobox').click();
47
+
48
+ // Then the bottom of the menu is aligned with the top of the input
49
+ cy.all(() => cy.findByRole('combobox'),
50
+ // We need to get the parent as the menu itself
51
+ // extends withing the div container
52
+ () => cy.findByRole('listbox').invoke('parent')).should(_ref3 => {
53
+ let [selectedValue, menu] = _ref3;
54
+ expect(selectedValue.length).to.equal(1);
55
+ expect(menu.length).to.equal(1);
56
+ const $selectedValue = selectedValue[0];
57
+ const $menu = menu[0];
58
+ const selectedValueRect = $selectedValue.getBoundingClientRect();
59
+ const menuRect = $menu.getBoundingClientRect();
60
+ expect(selectedValueRect.top).to.be.closeTo(menuRect.bottom, 2);
61
+ });
62
+
63
+ // And the left of the SingleSelect is aligned with the left of the anchor
64
+ cy.all(() => cy.findByRole('combobox'), () => cy.findByRole('listbox')).should(_ref4 => {
65
+ let [selectedValue, menu] = _ref4;
66
+ expect(selectedValue.length).to.equal(1);
67
+ expect(menu.length).to.equal(1);
68
+ const $selectedValue = selectedValue[0];
69
+ const $menu = menu[0];
70
+ const selectedValueRect = $selectedValue.getBoundingClientRect();
71
+ const menuRect = $menu.getBoundingClientRect();
72
+
73
+ // The listbox is inside a container with a border,
74
+ // hence the increased delta
75
+ expect(selectedValueRect.left).to.be.closeTo(menuRect.left, 2);
76
+ });
77
+ });
78
+ it('should shift the menu into view when insufficient space below and above', () => {
79
+ // Given there is not enough space above or below the anchor to fit the SingleSelect menu
80
+ cy.visitStory('Simple Single Select', 'Shifted into view');
81
+
82
+ // When the SingleSelect is clicked
83
+ cy.findByRole('combobox').click();
84
+
85
+ // Then it is rendered on top of the SingleSelect
86
+ cy.all(() => cy.findByRole('combobox'), () => cy.findByRole('listbox')).should(_ref5 => {
87
+ let [selectedValue, menu] = _ref5;
88
+ expect(selectedValue.length).to.equal(1);
89
+ expect(menu.length).to.equal(1);
90
+ const $selectedValue = selectedValue[0];
91
+ const $menu = menu[0];
92
+ const selectedValueRect = $selectedValue.getBoundingClientRect();
93
+ const menuRect = $menu.getBoundingClientRect();
94
+ expect(selectedValueRect.top).to.be.greaterThan(menuRect.top);
95
+ expect(menuRect.bottom).to.be.greaterThan(selectedValueRect.bottom);
96
+ });
97
+
98
+ // And the left of the SingleSelect is aligned with the left of the anchor
99
+ cy.all(() => cy.findByRole('combobox'), () => cy.findByRole('listbox')).should(_ref6 => {
100
+ let [selectedValue, menu] = _ref6;
101
+ expect(selectedValue.length).to.equal(1);
102
+ expect(menu.length).to.equal(1);
103
+ const $selectedValue = selectedValue[0];
104
+ const $menu = menu[0];
105
+ const selectedValueRect = $selectedValue.getBoundingClientRect();
106
+ const menuRect = $menu.getBoundingClientRect();
107
+ expect(selectedValueRect.top).to.be.greaterThan(menuRect.top);
108
+ expect(menuRect.bottom).to.be.greaterThan(selectedValueRect.bottom);
109
+ });
110
+ });
111
+ it('should keep the menu in position while the window is scrolled', () => {
112
+ // Given there is enough space below the anchor to fit the SingleSelect menu
113
+ cy.visitStory('Simple Single Select', 'Default position');
114
+
115
+ // When the SingleSelect is clicked
116
+ cy.findByRole('combobox').click();
117
+
118
+ // And the window is scrolled down
119
+ cy.get('body').then($body => $body.height('5000px')); // Ensure the body can scroll first
120
+ cy.scrollTo(0, 800);
121
+
122
+ // Then the top of the menu is aligned with the bottom of the input
123
+ cy.all(() => cy.findByRole('combobox'), () => cy.findByRole('listbox')).should(_ref7 => {
124
+ let [selectedValue, menu] = _ref7;
125
+ expect(selectedValue.length).to.equal(1);
126
+ expect(menu.length).to.equal(1);
127
+ const $selectedValue = selectedValue[0];
128
+ const $menu = menu[0];
129
+ const selectedValueRect = $selectedValue.getBoundingClientRect();
130
+ const menuRect = $menu.getBoundingClientRect();
131
+
132
+ // The listbox is inside a container with a border,
133
+ // hence the increased delta
134
+ expect(menuRect.top).to.be.closeTo(selectedValueRect.bottom, 2);
135
+ });
136
+
137
+ // And the left of the SingleSelect is aligned with the left of the anchor
138
+ cy.all(() => cy.findByRole('combobox'), () => cy.findByRole('listbox')).should(_ref8 => {
139
+ let [selectedValue, menu] = _ref8;
140
+ expect(selectedValue.length).to.equal(1);
141
+ expect(menu.length).to.equal(1);
142
+ const $selectedValue = selectedValue[0];
143
+ const $menu = menu[0];
144
+ const selectedValueRect = $selectedValue.getBoundingClientRect();
145
+ const menuRect = $menu.getBoundingClientRect();
146
+
147
+ // The listbox is inside a container with a border,
148
+ // hence the increased delta
149
+ expect(selectedValueRect.left).to.be.closeTo(menuRect.left, 2);
150
+ });
151
+ });
152
+ });
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "SimpleSingleSelect", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _simpleSingleSelect.SimpleSingleSelect;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "optionProp", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _sharedPropTypes.optionProp;
16
+ }
17
+ });
18
+ var _sharedPropTypes = require("./shared-prop-types.js");
19
+ var _simpleSingleSelect = require("./simple-single-select.js");
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isOptionHidden = isOptionHidden;
7
+ // If this many pixels are invisible, we still consider the option visible
8
+ // The reason we're doing this is because of JS' number issues,
9
+ // e.g:
10
+ // optionOffsetBottom is 345.6000061035156
11
+ // containerOffsetBottom is 345.5999984741211
12
+ // -> Without tolerance, option is invisible
13
+ const TOLERANCE = 2;
14
+ function isOptionHidden(option, scrollContainer) {
15
+ const optionOffsetTop = option.getBoundingClientRect().top;
16
+ const optionHeight = option.offsetHeight;
17
+ const optionOffsetBottom = optionOffsetTop + optionHeight;
18
+ const containerOffsetTop = scrollContainer.getBoundingClientRect().top;
19
+ const containerHeight = scrollContainer.offsetHeight;
20
+ const containerOffsetBottom = containerOffsetTop + containerHeight;
21
+ return optionOffsetBottom > containerOffsetBottom + TOLERANCE || optionOffsetTop < containerOffsetTop - TOLERANCE;
22
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Empty = void 0;
7
+ var _style = _interopRequireDefault(require("styled-jsx/style"));
8
+ var _uiConstants = require("@dhis2/ui-constants");
9
+ var _propTypes = _interopRequireDefault(require("prop-types"));
10
+ var _react = _interopRequireDefault(require("react"));
11
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
+ const Empty = _ref => {
13
+ let {
14
+ children
15
+ } = _ref;
16
+ return /*#__PURE__*/_react.default.createElement("div", {
17
+ className: _style.default.dynamic([["3788156646", [_uiConstants.colors.grey700, _uiConstants.theme.fonts, _uiConstants.spacers.dp8, _uiConstants.spacers.dp24]]])
18
+ }, children, /*#__PURE__*/_react.default.createElement(_style.default, {
19
+ id: "3788156646",
20
+ dynamic: [_uiConstants.colors.grey700, _uiConstants.theme.fonts, _uiConstants.spacers.dp8, _uiConstants.spacers.dp24]
21
+ }, [`div.__jsx-style-dynamic-selector{color:${_uiConstants.colors.grey700};font-family:${_uiConstants.theme.fonts};font-size:13px;line-height:16px;padding-block:${_uiConstants.spacers.dp8};padding-inline:${_uiConstants.spacers.dp24};text-align:center;}`]));
22
+ };
23
+ exports.Empty = Empty;
24
+ Empty.propTypes = {
25
+ children: _propTypes.default.string.isRequired
26
+ };