@primer/components 30.3.0-rc.2010c7d4 → 31.0.0-rc.15aa0a10

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 (180) hide show
  1. package/.eslintrc.json +2 -1
  2. package/.storybook/preview.js +4 -4
  3. package/CHANGELOG.md +12 -2
  4. package/codemods/deprecateUtilityComponents.js +1 -1
  5. package/contributor-docs/adrs/adr-003-prop-norms.md +72 -0
  6. package/dist/browser.esm.js +798 -794
  7. package/dist/browser.esm.js.map +1 -1
  8. package/dist/browser.umd.js +801 -797
  9. package/dist/browser.umd.js.map +1 -1
  10. package/docs/content/Autocomplete.mdx +627 -0
  11. package/docs/content/TextInputTokens.mdx +89 -0
  12. package/docs/content/getting-started.md +1 -1
  13. package/docs/content/overriding-styles.mdx +7 -6
  14. package/docs/content/theming.md +5 -5
  15. package/docs/package-lock.json +288 -511
  16. package/docs/package.json +1 -1
  17. package/docs/src/@primer/gatsby-theme-doctocat/components/hero.js +14 -12
  18. package/docs/src/@primer/gatsby-theme-doctocat/nav.yml +2 -0
  19. package/docs/src/@primer/gatsby-theme-doctocat/primer-components-hero.svg +7 -7
  20. package/lib/ActionList/Item.js +1 -1
  21. package/lib/AnchoredOverlay/AnchoredOverlay.d.ts +2 -1
  22. package/lib/AnchoredOverlay/AnchoredOverlay.js +11 -3
  23. package/lib/Autocomplete/Autocomplete.d.ts +304 -0
  24. package/lib/Autocomplete/Autocomplete.js +145 -0
  25. package/lib/Autocomplete/AutocompleteContext.d.ts +17 -0
  26. package/lib/Autocomplete/AutocompleteContext.js +11 -0
  27. package/lib/Autocomplete/AutocompleteInput.d.ts +292 -0
  28. package/lib/Autocomplete/AutocompleteInput.js +157 -0
  29. package/lib/Autocomplete/AutocompleteMenu.d.ts +72 -0
  30. package/lib/Autocomplete/AutocompleteMenu.js +224 -0
  31. package/lib/Autocomplete/AutocompleteOverlay.d.ts +20 -0
  32. package/lib/Autocomplete/AutocompleteOverlay.js +80 -0
  33. package/lib/Autocomplete/index.d.ts +2 -0
  34. package/lib/Autocomplete/index.js +15 -0
  35. package/lib/BaseStyles.js +1 -1
  36. package/lib/BorderBox.js +1 -1
  37. package/lib/Button/ButtonInvisible.js +1 -1
  38. package/lib/Caret.js +2 -2
  39. package/lib/Dialog.js +1 -1
  40. package/lib/FilteredActionList/FilteredActionList.js +5 -31
  41. package/lib/Flash.js +16 -16
  42. package/lib/Label.js +1 -1
  43. package/lib/Overlay.d.ts +1 -0
  44. package/lib/Overlay.js +3 -1
  45. package/lib/ProgressBar.js +1 -1
  46. package/lib/StateLabel.js +13 -19
  47. package/lib/Token/_RemoveTokenButton.js +1 -1
  48. package/lib/__tests__/Autocomplete.test.d.ts +1 -0
  49. package/lib/__tests__/Autocomplete.test.js +528 -0
  50. package/lib/__tests__/BorderBox.test.js +1 -1
  51. package/lib/__tests__/CircleOcticon.test.js +1 -1
  52. package/lib/__tests__/CounterLabel.test.js +4 -4
  53. package/lib/__tests__/Flash.test.js +4 -4
  54. package/lib/__tests__/Link.test.js +1 -1
  55. package/lib/__tests__/behaviors/scrollIntoViewingArea.test.d.ts +1 -0
  56. package/lib/__tests__/behaviors/scrollIntoViewingArea.test.js +226 -0
  57. package/lib/behaviors/scrollIntoViewingArea.d.ts +1 -0
  58. package/lib/behaviors/scrollIntoViewingArea.js +39 -0
  59. package/lib/hooks/useOpenAndCloseFocus.d.ts +2 -1
  60. package/lib/hooks/useOpenAndCloseFocus.js +7 -2
  61. package/lib/hooks/useOverlay.d.ts +2 -1
  62. package/lib/hooks/useOverlay.js +4 -2
  63. package/lib/index.d.ts +2 -0
  64. package/lib/index.js +8 -0
  65. package/lib/stories/Autocomplete.stories.js +608 -0
  66. package/lib/stories/Dialog.stories.js +3 -3
  67. package/lib/stories/IssueLabelToken.stories.js +1 -1
  68. package/lib/stories/ProfileToken.stories.js +1 -1
  69. package/lib/theme-preval.js +370 -3100
  70. package/lib/utils/testing.d.ts +50 -493
  71. package/lib/utils/types/MandateProps.d.ts +3 -0
  72. package/lib/utils/types/MandateProps.js +1 -0
  73. package/lib/utils/types/index.d.ts +1 -0
  74. package/lib/utils/types/index.js +13 -0
  75. package/lib-esm/ActionList/Item.js +1 -1
  76. package/lib-esm/AnchoredOverlay/AnchoredOverlay.d.ts +2 -1
  77. package/lib-esm/AnchoredOverlay/AnchoredOverlay.js +11 -3
  78. package/lib-esm/Autocomplete/Autocomplete.d.ts +304 -0
  79. package/lib-esm/Autocomplete/Autocomplete.js +123 -0
  80. package/lib-esm/Autocomplete/AutocompleteContext.d.ts +17 -0
  81. package/lib-esm/Autocomplete/AutocompleteContext.js +2 -0
  82. package/lib-esm/Autocomplete/AutocompleteInput.d.ts +292 -0
  83. package/lib-esm/Autocomplete/AutocompleteInput.js +138 -0
  84. package/lib-esm/Autocomplete/AutocompleteMenu.d.ts +72 -0
  85. package/lib-esm/Autocomplete/AutocompleteMenu.js +205 -0
  86. package/lib-esm/Autocomplete/AutocompleteOverlay.d.ts +20 -0
  87. package/lib-esm/Autocomplete/AutocompleteOverlay.js +62 -0
  88. package/lib-esm/Autocomplete/index.d.ts +2 -0
  89. package/lib-esm/Autocomplete/index.js +1 -0
  90. package/lib-esm/BaseStyles.js +1 -1
  91. package/lib-esm/BorderBox.js +1 -1
  92. package/lib-esm/Button/ButtonInvisible.js +1 -1
  93. package/lib-esm/Caret.js +2 -2
  94. package/lib-esm/Dialog.js +1 -1
  95. package/lib-esm/FilteredActionList/FilteredActionList.js +3 -31
  96. package/lib-esm/Flash.js +16 -16
  97. package/lib-esm/Label.js +1 -1
  98. package/lib-esm/Overlay.d.ts +1 -0
  99. package/lib-esm/Overlay.js +3 -1
  100. package/lib-esm/ProgressBar.js +1 -1
  101. package/lib-esm/StateLabel.js +13 -19
  102. package/lib-esm/Token/_RemoveTokenButton.js +1 -1
  103. package/lib-esm/__tests__/Autocomplete.test.d.ts +1 -0
  104. package/lib-esm/__tests__/Autocomplete.test.js +494 -0
  105. package/lib-esm/__tests__/BorderBox.test.js +1 -1
  106. package/lib-esm/__tests__/CircleOcticon.test.js +1 -1
  107. package/lib-esm/__tests__/CounterLabel.test.js +4 -4
  108. package/lib-esm/__tests__/Flash.test.js +4 -4
  109. package/lib-esm/__tests__/Link.test.js +1 -1
  110. package/lib-esm/__tests__/behaviors/scrollIntoViewingArea.test.d.ts +1 -0
  111. package/lib-esm/__tests__/behaviors/scrollIntoViewingArea.test.js +224 -0
  112. package/lib-esm/behaviors/scrollIntoViewingArea.d.ts +1 -0
  113. package/lib-esm/behaviors/scrollIntoViewingArea.js +30 -0
  114. package/lib-esm/hooks/useOpenAndCloseFocus.d.ts +2 -1
  115. package/lib-esm/hooks/useOpenAndCloseFocus.js +7 -2
  116. package/lib-esm/hooks/useOverlay.d.ts +2 -1
  117. package/lib-esm/hooks/useOverlay.js +4 -2
  118. package/lib-esm/index.d.ts +2 -0
  119. package/lib-esm/index.js +1 -0
  120. package/lib-esm/stories/Autocomplete.stories.js +549 -0
  121. package/lib-esm/stories/Dialog.stories.js +3 -3
  122. package/lib-esm/stories/IssueLabelToken.stories.js +1 -1
  123. package/lib-esm/stories/ProfileToken.stories.js +1 -1
  124. package/lib-esm/theme-preval.js +370 -3100
  125. package/lib-esm/utils/testing.d.ts +50 -493
  126. package/lib-esm/utils/types/MandateProps.d.ts +3 -0
  127. package/lib-esm/utils/types/MandateProps.js +1 -0
  128. package/lib-esm/utils/types/index.d.ts +1 -0
  129. package/lib-esm/utils/types/index.js +2 -1
  130. package/package-lock.json +11 -8
  131. package/package.json +3 -3
  132. package/src/ActionList/Item.tsx +1 -1
  133. package/src/AnchoredOverlay/AnchoredOverlay.tsx +14 -3
  134. package/src/Autocomplete/Autocomplete.tsx +103 -0
  135. package/src/Autocomplete/AutocompleteContext.tsx +19 -0
  136. package/src/Autocomplete/AutocompleteInput.tsx +179 -0
  137. package/src/Autocomplete/AutocompleteMenu.tsx +341 -0
  138. package/src/Autocomplete/AutocompleteOverlay.tsx +68 -0
  139. package/src/Autocomplete/index.ts +2 -0
  140. package/src/BaseStyles.tsx +1 -1
  141. package/src/BorderBox.tsx +1 -1
  142. package/src/Button/ButtonInvisible.tsx +7 -2
  143. package/src/Caret.tsx +2 -2
  144. package/src/Dialog.tsx +1 -1
  145. package/src/FilteredActionList/FilteredActionList.tsx +10 -25
  146. package/src/Flash.tsx +16 -16
  147. package/src/Label.tsx +1 -1
  148. package/src/Overlay.tsx +4 -1
  149. package/src/ProgressBar.tsx +1 -1
  150. package/src/StateLabel.tsx +12 -20
  151. package/src/Token/_RemoveTokenButton.tsx +4 -2
  152. package/src/__tests__/Autocomplete.test.tsx +444 -0
  153. package/src/__tests__/BorderBox.test.tsx +1 -1
  154. package/src/__tests__/CircleOcticon.test.tsx +1 -1
  155. package/src/__tests__/CounterLabel.test.tsx +4 -4
  156. package/src/__tests__/Flash.test.tsx +4 -4
  157. package/src/__tests__/Link.test.tsx +1 -1
  158. package/src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap +3 -3
  159. package/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap +3414 -0
  160. package/src/__tests__/__snapshots__/Button.test.tsx.snap +9 -1
  161. package/src/__tests__/__snapshots__/ConfirmationDialog.test.tsx.snap +1 -1
  162. package/src/__tests__/__snapshots__/SelectPanel.test.tsx.snap +1 -1
  163. package/src/__tests__/__snapshots__/StateLabel.test.tsx.snap +0 -21
  164. package/src/__tests__/__snapshots__/TextInputWithTokens.test.tsx.snap +16 -16
  165. package/src/__tests__/__snapshots__/Token.test.tsx.snap +34 -34
  166. package/src/__tests__/behaviors/scrollIntoViewingArea.test.ts +195 -0
  167. package/src/behaviors/scrollIntoViewingArea.ts +27 -0
  168. package/src/hooks/useOpenAndCloseFocus.ts +7 -2
  169. package/src/hooks/useOverlay.tsx +4 -2
  170. package/src/index.ts +2 -0
  171. package/src/stories/Autocomplete.stories.tsx +572 -0
  172. package/src/stories/Dialog.stories.tsx +3 -3
  173. package/src/stories/IssueLabelToken.stories.tsx +1 -1
  174. package/src/stories/ProfileToken.stories.tsx +1 -1
  175. package/src/utils/types/MandateProps.ts +19 -0
  176. package/src/utils/types/index.ts +1 -0
  177. package/stats.html +1 -1
  178. package/docs/src/@primer/gatsby-theme-doctocat/components/live-code.js +0 -84
  179. package/docs/src/@primer/gatsby-theme-doctocat/components/nav-dropdown.js +0 -48
  180. package/docs/src/@primer/gatsby-theme-doctocat/components/wrap-page-element.js +0 -25
@@ -0,0 +1,494 @@
1
+ function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
+
3
+ import React from 'react';
4
+ import { render } from '../utils/testing';
5
+ import { render as HTMLRender, fireEvent } from '@testing-library/react';
6
+ import { toHaveNoViolations } from 'jest-axe';
7
+ import 'babel-polyfill';
8
+ import Autocomplete from '../Autocomplete';
9
+ import { SSRProvider } from '../index';
10
+ import theme from '../theme';
11
+ import BaseStyles from '../BaseStyles';
12
+ import { ThemeProvider } from '../ThemeProvider';
13
+ import userEvent from '@testing-library/user-event';
14
+ expect.extend(toHaveNoViolations);
15
+ const mockItems = [{
16
+ text: 'zero',
17
+ id: 0
18
+ }, {
19
+ text: 'one',
20
+ id: 1
21
+ }, {
22
+ text: 'two',
23
+ id: 2
24
+ }, {
25
+ text: 'three',
26
+ id: 3
27
+ }, {
28
+ text: 'four',
29
+ id: 4
30
+ }, {
31
+ text: 'five',
32
+ id: 5
33
+ }, {
34
+ text: 'six',
35
+ id: 6
36
+ }, {
37
+ text: 'seven',
38
+ id: 7
39
+ }, {
40
+ text: 'twenty',
41
+ id: 20
42
+ }, {
43
+ text: 'twentyone',
44
+ id: 21
45
+ }]; // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
+
47
+ const AUTOCOMPLETE_LABEL = 'Autocomplete field';
48
+
49
+ const LabelledAutocomplete = ({
50
+ inputProps = {},
51
+ menuProps
52
+ }) => {
53
+ const {
54
+ ['aria-labelledby']: ariaLabelledBy = 'autocompleteLabel',
55
+ ...menuPropsRest
56
+ } = menuProps;
57
+ const {
58
+ id = 'autocompleteInput',
59
+ ...inputPropsRest
60
+ } = inputProps;
61
+ return /*#__PURE__*/React.createElement(ThemeProvider, {
62
+ theme: theme
63
+ }, /*#__PURE__*/React.createElement(SSRProvider, null, /*#__PURE__*/React.createElement(BaseStyles, null, /*#__PURE__*/React.createElement("label", {
64
+ htmlFor: id,
65
+ id: ariaLabelledBy
66
+ }, "Autocomplete field"), /*#__PURE__*/React.createElement(Autocomplete, {
67
+ id: "autocompleteId"
68
+ }, /*#__PURE__*/React.createElement(Autocomplete.Input, _extends({
69
+ id: id
70
+ }, inputPropsRest)), /*#__PURE__*/React.createElement(Autocomplete.Overlay, null, /*#__PURE__*/React.createElement(Autocomplete.Menu, _extends({
71
+ "aria-labelledby": ariaLabelledBy
72
+ }, menuPropsRest)))))));
73
+ };
74
+
75
+ LabelledAutocomplete.displayName = "LabelledAutocomplete";
76
+ describe('Autocomplete', () => {
77
+ describe('snapshots', () => {
78
+ it('renders a single select input', () => {
79
+ expect(render( /*#__PURE__*/React.createElement(SSRProvider, null, /*#__PURE__*/React.createElement(Autocomplete, {
80
+ id: "autocompleteId"
81
+ }, /*#__PURE__*/React.createElement(Autocomplete.Input, null), /*#__PURE__*/React.createElement(Autocomplete.Menu, {
82
+ "aria-labelledby": "labelId",
83
+ items: mockItems,
84
+ selectedItemIds: []
85
+ }))))).toMatchSnapshot();
86
+ });
87
+ it('renders a multiselect input', () => {
88
+ expect(render( /*#__PURE__*/React.createElement(SSRProvider, null, /*#__PURE__*/React.createElement(Autocomplete, {
89
+ id: "autocompleteId"
90
+ }, /*#__PURE__*/React.createElement(Autocomplete.Input, null), /*#__PURE__*/React.createElement(Autocomplete.Menu, {
91
+ "aria-labelledby": "labelId",
92
+ items: mockItems,
93
+ selectedItemIds: [],
94
+ selectionVariant: "multiple"
95
+ }))))).toMatchSnapshot();
96
+ });
97
+ it('renders a multiselect input with selected menu items', () => {
98
+ expect(render( /*#__PURE__*/React.createElement(SSRProvider, null, /*#__PURE__*/React.createElement(Autocomplete, {
99
+ id: "autocompleteId"
100
+ }, /*#__PURE__*/React.createElement(Autocomplete.Input, null), /*#__PURE__*/React.createElement(Autocomplete.Menu, {
101
+ "aria-labelledby": "labelId",
102
+ items: mockItems,
103
+ selectedItemIds: [0, 1, 2],
104
+ selectionVariant: "multiple"
105
+ }))))).toMatchSnapshot();
106
+ });
107
+ it('renders a menu that contains an item to add to the menu', () => {
108
+ const handleAddItemMock = jest.fn();
109
+ expect(render( /*#__PURE__*/React.createElement(SSRProvider, null, /*#__PURE__*/React.createElement(Autocomplete, {
110
+ id: "autocompleteId"
111
+ }, /*#__PURE__*/React.createElement(Autocomplete.Input, null), /*#__PURE__*/React.createElement(Autocomplete.Menu, {
112
+ "aria-labelledby": "labelId",
113
+ items: mockItems,
114
+ selectionVariant: "multiple",
115
+ selectedItemIds: [],
116
+ addNewItem: {
117
+ text: 'Add new item',
118
+ handleAddItem: handleAddItemMock
119
+ }
120
+ }))))).toMatchSnapshot();
121
+ });
122
+ it('renders a custom empty state message', () => {
123
+ expect(render( /*#__PURE__*/React.createElement(SSRProvider, null, /*#__PURE__*/React.createElement(Autocomplete, {
124
+ id: "autocompleteId"
125
+ }, /*#__PURE__*/React.createElement(Autocomplete.Input, null), /*#__PURE__*/React.createElement(Autocomplete.Menu, {
126
+ "aria-labelledby": "labelId",
127
+ items: [],
128
+ selectedItemIds: [],
129
+ emptyStateText: "No results"
130
+ }))))).toMatchSnapshot();
131
+ });
132
+ it('renders a loading state', () => {
133
+ expect(render( /*#__PURE__*/React.createElement(SSRProvider, null, /*#__PURE__*/React.createElement(Autocomplete, {
134
+ id: "autocompleteId"
135
+ }, /*#__PURE__*/React.createElement(Autocomplete.Input, null), /*#__PURE__*/React.createElement(Autocomplete.Menu, {
136
+ "aria-labelledby": "labelId",
137
+ loading: true,
138
+ items: [],
139
+ selectedItemIds: []
140
+ }))))).toMatchSnapshot();
141
+ });
142
+ it('renders with a custom text input component', () => {
143
+ expect(render( /*#__PURE__*/React.createElement(SSRProvider, null, /*#__PURE__*/React.createElement(Autocomplete, {
144
+ id: "autocompleteId"
145
+ }, /*#__PURE__*/React.createElement(Autocomplete.Input, {
146
+ as: () => /*#__PURE__*/React.createElement("input", {
147
+ type: "text",
148
+ id: "customInput"
149
+ })
150
+ }), /*#__PURE__*/React.createElement(Autocomplete.Menu, {
151
+ "aria-labelledby": "labelId",
152
+ items: mockItems,
153
+ selectedItemIds: []
154
+ }))))).toMatchSnapshot();
155
+ });
156
+ it('renders with an input value', () => {
157
+ expect(render( /*#__PURE__*/React.createElement(SSRProvider, null, /*#__PURE__*/React.createElement(Autocomplete, {
158
+ id: "autocompleteId"
159
+ }, /*#__PURE__*/React.createElement(Autocomplete.Input, {
160
+ value: "test"
161
+ }), /*#__PURE__*/React.createElement(Autocomplete.Menu, {
162
+ "aria-labelledby": "labelId",
163
+ items: mockItems,
164
+ selectedItemIds: []
165
+ }))))).toMatchSnapshot();
166
+ });
167
+ });
168
+ describe('Autocomplete.Input', () => {
169
+ it('calls onChange', () => {
170
+ const onChangeMock = jest.fn();
171
+ const {
172
+ container
173
+ } = HTMLRender( /*#__PURE__*/React.createElement(LabelledAutocomplete, {
174
+ inputProps: {
175
+ onChange: onChangeMock
176
+ },
177
+ menuProps: {
178
+ items: mockItems,
179
+ selectedItemIds: []
180
+ }
181
+ }));
182
+ const inputNode = container.querySelector('#autocompleteInput');
183
+ expect(onChangeMock).not.toHaveBeenCalled();
184
+ inputNode && userEvent.type(inputNode, 'z');
185
+ expect(onChangeMock).toHaveBeenCalled();
186
+ });
187
+ it('calls onFocus', () => {
188
+ const onFocusMock = jest.fn();
189
+ const {
190
+ container
191
+ } = HTMLRender( /*#__PURE__*/React.createElement(LabelledAutocomplete, {
192
+ inputProps: {
193
+ onFocus: onFocusMock
194
+ },
195
+ menuProps: {
196
+ items: mockItems,
197
+ selectedItemIds: []
198
+ }
199
+ }));
200
+ const inputNode = container.querySelector('#autocompleteInput');
201
+ expect(onFocusMock).not.toHaveBeenCalled();
202
+ inputNode && fireEvent.focus(inputNode);
203
+ expect(onFocusMock).toHaveBeenCalled();
204
+ });
205
+ it('calls onKeyDown', () => {
206
+ const onKeyDownMock = jest.fn();
207
+ const {
208
+ getByLabelText
209
+ } = HTMLRender( /*#__PURE__*/React.createElement(LabelledAutocomplete, {
210
+ inputProps: {
211
+ onKeyDown: onKeyDownMock
212
+ },
213
+ menuProps: {
214
+ items: [],
215
+ selectedItemIds: []
216
+ }
217
+ }));
218
+ const inputNode = getByLabelText(AUTOCOMPLETE_LABEL);
219
+ expect(onKeyDownMock).not.toHaveBeenCalled();
220
+ fireEvent.keyDown(inputNode, {
221
+ key: 'Shift'
222
+ });
223
+ expect(onKeyDownMock).toHaveBeenCalled();
224
+ });
225
+ it('calls onKeyUp', () => {
226
+ const onKeyUpMock = jest.fn();
227
+ const {
228
+ getByLabelText
229
+ } = HTMLRender( /*#__PURE__*/React.createElement(LabelledAutocomplete, {
230
+ inputProps: {
231
+ onKeyUp: onKeyUpMock
232
+ },
233
+ menuProps: {
234
+ items: [],
235
+ selectedItemIds: []
236
+ }
237
+ }));
238
+ const inputNode = getByLabelText(AUTOCOMPLETE_LABEL);
239
+ expect(onKeyUpMock).not.toHaveBeenCalled();
240
+ fireEvent.keyUp(inputNode, {
241
+ key: 'Shift'
242
+ });
243
+ expect(onKeyUpMock).toHaveBeenCalled();
244
+ });
245
+ it('calls onKeyPress', () => {
246
+ const onKeyPressMock = jest.fn();
247
+ const {
248
+ getByLabelText
249
+ } = HTMLRender( /*#__PURE__*/React.createElement(LabelledAutocomplete, {
250
+ inputProps: {
251
+ onKeyPress: onKeyPressMock
252
+ },
253
+ menuProps: {
254
+ items: [],
255
+ selectedItemIds: []
256
+ }
257
+ }));
258
+ const inputNode = getByLabelText(AUTOCOMPLETE_LABEL);
259
+ expect(onKeyPressMock).not.toHaveBeenCalled();
260
+ userEvent.type(inputNode, '{enter}');
261
+ expect(onKeyPressMock).toHaveBeenCalled();
262
+ });
263
+ it('opens the menu when the input is focused', () => {
264
+ const {
265
+ getByLabelText
266
+ } = HTMLRender( /*#__PURE__*/React.createElement(LabelledAutocomplete, {
267
+ menuProps: {
268
+ items: [],
269
+ selectedItemIds: []
270
+ }
271
+ }));
272
+ const inputNode = getByLabelText(AUTOCOMPLETE_LABEL);
273
+ expect(inputNode.getAttribute('aria-expanded')).not.toBe('true');
274
+ fireEvent.focus(inputNode);
275
+ expect(inputNode.getAttribute('aria-expanded')).toBe('true');
276
+ });
277
+ it('closes the menu when the input is blurred', () => {
278
+ const {
279
+ getByLabelText
280
+ } = HTMLRender( /*#__PURE__*/React.createElement(LabelledAutocomplete, {
281
+ menuProps: {
282
+ items: [],
283
+ selectedItemIds: []
284
+ }
285
+ }));
286
+ const inputNode = getByLabelText(AUTOCOMPLETE_LABEL);
287
+ expect(inputNode.getAttribute('aria-expanded')).not.toBe('true');
288
+ fireEvent.focus(inputNode);
289
+ expect(inputNode.getAttribute('aria-expanded')).toBe('true'); // eslint-disable-next-line github/no-blur
290
+
291
+ fireEvent.blur(inputNode); // wait a tick for blur to finish
292
+
293
+ setTimeout(() => {
294
+ expect(inputNode.getAttribute('aria-expanded')).not.toBe('true');
295
+ }, 0);
296
+ });
297
+ it('sets the input value to the suggested item text and highlights the untyped part of the word', () => {
298
+ const {
299
+ container,
300
+ getByDisplayValue
301
+ } = HTMLRender( /*#__PURE__*/React.createElement(LabelledAutocomplete, {
302
+ menuProps: {
303
+ items: mockItems,
304
+ selectedItemIds: []
305
+ }
306
+ }));
307
+ const inputNode = container.querySelector('#autocompleteInput');
308
+ inputNode && userEvent.type(inputNode, 'ze');
309
+ expect(getByDisplayValue('zero')).toBeDefined();
310
+ });
311
+ it('does not show or highlight suggestion text after the user hits Backspace until they hit another key', () => {
312
+ const {
313
+ container,
314
+ getByDisplayValue
315
+ } = HTMLRender( /*#__PURE__*/React.createElement(LabelledAutocomplete, {
316
+ menuProps: {
317
+ items: mockItems,
318
+ selectedItemIds: []
319
+ }
320
+ }));
321
+ const inputNode = container.querySelector('#autocompleteInput');
322
+ expect(inputNode.selectionStart).toBe(0);
323
+ inputNode && userEvent.type(inputNode, 'ze');
324
+ expect(getByDisplayValue('zero')).toBeDefined();
325
+ expect(inputNode.selectionStart).toBe(2);
326
+ expect(inputNode.selectionEnd).toBe(4);
327
+ inputNode && userEvent.type(inputNode, '{backspace}');
328
+ expect(inputNode.selectionStart).toBe(2);
329
+ expect(getByDisplayValue('ze')).toBeDefined();
330
+ inputNode && userEvent.type(inputNode, 'r');
331
+ expect(inputNode.selectionStart).toBe(3);
332
+ expect(inputNode.selectionEnd).toBe(4);
333
+ expect(getByDisplayValue('zero')).toBeDefined();
334
+ });
335
+ it('clears the input value when when the user hits Escape', () => {
336
+ const {
337
+ container
338
+ } = HTMLRender( /*#__PURE__*/React.createElement(LabelledAutocomplete, {
339
+ menuProps: {
340
+ items: mockItems,
341
+ selectedItemIds: []
342
+ }
343
+ }));
344
+ const inputNode = container.querySelector('#autocompleteInput');
345
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).not.toBe('true');
346
+ inputNode && userEvent.type(inputNode, 'ze');
347
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).toBe('true');
348
+ inputNode && userEvent.type(inputNode, '{esc}');
349
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).not.toBe('true');
350
+ });
351
+ });
352
+ describe('Autocomplete.Menu', () => {
353
+ it('calls a custom filter function', () => {
354
+ const filterFnMock = jest.fn();
355
+ const {
356
+ container
357
+ } = HTMLRender( /*#__PURE__*/React.createElement(LabelledAutocomplete, {
358
+ menuProps: {
359
+ items: mockItems,
360
+ selectedItemIds: [],
361
+ filterFn: filterFnMock
362
+ }
363
+ }));
364
+ const inputNode = container.querySelector('#autocompleteInput');
365
+ inputNode && userEvent.type(inputNode, 'ze');
366
+ expect(filterFnMock).toHaveBeenCalled();
367
+ });
368
+ it('calls a custom sort function when the menu closes', () => {
369
+ const sortOnCloseFnMock = jest.fn();
370
+ const {
371
+ container
372
+ } = HTMLRender( /*#__PURE__*/React.createElement(LabelledAutocomplete, {
373
+ menuProps: {
374
+ items: mockItems,
375
+ selectedItemIds: [],
376
+ sortOnCloseFn: sortOnCloseFnMock
377
+ }
378
+ }));
379
+ const inputNode = container.querySelector('#autocompleteInput'); // `sortOnCloseFnMock` will be called in a `.sort()` on render to check if the
380
+ // current sort order matches the result of `sortOnCloseFnMock`
381
+
382
+ expect(sortOnCloseFnMock).toHaveBeenCalledTimes(mockItems.length - 1);
383
+
384
+ if (inputNode) {
385
+ userEvent.type(inputNode, 'ze'); // eslint-disable-next-line github/no-blur
386
+
387
+ fireEvent.blur(inputNode);
388
+ } // wait a tick for blur to finish
389
+
390
+
391
+ setTimeout(() => {
392
+ expect(sortOnCloseFnMock).toHaveBeenCalledTimes(mockItems.length);
393
+ }, 0);
394
+ });
395
+ it("calls onOpenChange with the menu's open state", () => {
396
+ const onOpenChangeMock = jest.fn();
397
+ const {
398
+ container
399
+ } = HTMLRender( /*#__PURE__*/React.createElement(LabelledAutocomplete, {
400
+ menuProps: {
401
+ items: mockItems,
402
+ selectedItemIds: [],
403
+ onOpenChange: onOpenChangeMock
404
+ }
405
+ }));
406
+ const inputNode = container.querySelector('#autocompleteInput');
407
+ inputNode && userEvent.type(inputNode, 'ze');
408
+ expect(onOpenChangeMock).toHaveBeenCalled();
409
+ });
410
+ it('calls onSelectedChange with the data for the selected items', () => {
411
+ const onSelectedChangeMock = jest.fn();
412
+ const {
413
+ container
414
+ } = HTMLRender( /*#__PURE__*/React.createElement(LabelledAutocomplete, {
415
+ menuProps: {
416
+ items: mockItems,
417
+ selectedItemIds: [],
418
+ onSelectedChange: onSelectedChangeMock
419
+ }
420
+ }));
421
+ const inputNode = container.querySelector('#autocompleteInput');
422
+ expect(onSelectedChangeMock).not.toHaveBeenCalled();
423
+
424
+ if (inputNode) {
425
+ fireEvent.focus(inputNode);
426
+ userEvent.type(inputNode, '{enter}');
427
+ } // wait a tick for the keyboard event to be dispatched to the menu item
428
+
429
+
430
+ setTimeout(() => {
431
+ expect(onSelectedChangeMock).toHaveBeenCalledWith([mockItems[0]]);
432
+ }, 0);
433
+ });
434
+ it('does not close the menu when clicking an item in the menu if selectionVariant=multiple', () => {
435
+ const {
436
+ getByText,
437
+ container
438
+ } = HTMLRender( /*#__PURE__*/React.createElement(LabelledAutocomplete, {
439
+ menuProps: {
440
+ items: mockItems,
441
+ selectedItemIds: [],
442
+ selectionVariant: 'multiple'
443
+ }
444
+ }));
445
+ const inputNode = container.querySelector('#autocompleteInput');
446
+ const itemToClickNode = getByText(mockItems[1].text);
447
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).not.toBe('true');
448
+ inputNode && fireEvent.focus(inputNode);
449
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).toBe('true');
450
+ fireEvent.click(itemToClickNode);
451
+ inputNode && userEvent.type(inputNode, '{enter}');
452
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).toBe('true');
453
+ });
454
+ it('closes the menu when clicking an item in the menu if selectionVariant=single', () => {
455
+ const {
456
+ getByText,
457
+ container
458
+ } = HTMLRender( /*#__PURE__*/React.createElement(LabelledAutocomplete, {
459
+ menuProps: {
460
+ items: mockItems,
461
+ selectedItemIds: [],
462
+ selectionVariant: 'single'
463
+ }
464
+ }));
465
+ const inputNode = container.querySelector('#autocompleteInput');
466
+ const itemToClickNode = getByText(mockItems[1].text);
467
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).not.toBe('true');
468
+ inputNode && fireEvent.focus(inputNode);
469
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).toBe('true');
470
+ fireEvent.click(itemToClickNode);
471
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).not.toBe('true');
472
+ });
473
+ it('calls handleAddItem with new item data when passing addNewItem', () => {
474
+ const handleAddItemMock = jest.fn();
475
+ const {
476
+ getByText
477
+ } = HTMLRender( /*#__PURE__*/React.createElement(LabelledAutocomplete, {
478
+ menuProps: {
479
+ items: mockItems,
480
+ selectedItemIds: [],
481
+ selectionVariant: 'multiple',
482
+ addNewItem: {
483
+ text: 'Add new item',
484
+ handleAddItem: handleAddItemMock
485
+ }
486
+ }
487
+ }));
488
+ const addNewItemNode = getByText('Add new item');
489
+ expect(handleAddItemMock).not.toHaveBeenCalled();
490
+ fireEvent.click(addNewItemNode);
491
+ expect(handleAddItemMock).toHaveBeenCalled();
492
+ });
493
+ });
494
+ });
@@ -26,7 +26,7 @@ describe('BorderBox', () => {
26
26
 
27
27
  expect(render( /*#__PURE__*/React.createElement(BorderBox, {
28
28
  borderColor: "success.emphasis"
29
- }))).toHaveStyleRule('border-color', (_theme$colorSchemes$l = theme.colorSchemes.light.colors.border) === null || _theme$colorSchemes$l === void 0 ? void 0 : _theme$colorSchemes$l.success);
29
+ }))).toHaveStyleRule('border-color', (_theme$colorSchemes$l = theme.colorSchemes.light.colors.success) === null || _theme$colorSchemes$l === void 0 ? void 0 : _theme$colorSchemes$l.emphasis);
30
30
  expect(render( /*#__PURE__*/React.createElement(BorderBox, {
31
31
  borderBottom: 0
32
32
  }))).toHaveStyleRule('border-bottom', '0');
@@ -47,7 +47,7 @@ describe('CircleOcticon', () => {
47
47
  expect(render( /*#__PURE__*/React.createElement(CircleOcticon, {
48
48
  icon: CheckIcon,
49
49
  bg: "danger.subtle"
50
- }))).toHaveStyleRule('background-color', (_theme$colorSchemes$l = theme.colorSchemes.light.colors.bg) === null || _theme$colorSchemes$l === void 0 ? void 0 : _theme$colorSchemes$l.danger);
50
+ }))).toHaveStyleRule('background-color', (_theme$colorSchemes$l = theme.colorSchemes.light.colors.danger) === null || _theme$colorSchemes$l === void 0 ? void 0 : _theme$colorSchemes$l.subtle);
51
51
  });
52
52
  it('has a default size', () => {
53
53
  const result = render( /*#__PURE__*/React.createElement(CircleOcticon, {
@@ -29,19 +29,19 @@ describe('CounterLabel', () => {
29
29
 
30
30
  expect(render( /*#__PURE__*/React.createElement(CounterLabel, {
31
31
  scheme: "primary"
32
- }))).toHaveStyleRule('color', (_theme$colorSchemes$l = theme.colorSchemes.light.colors.counter) === null || _theme$colorSchemes$l === void 0 ? void 0 : _theme$colorSchemes$l.primary.text.trim());
32
+ }))).toHaveStyleRule('color', (_theme$colorSchemes$l = theme.colorSchemes.light.colors.fg) === null || _theme$colorSchemes$l === void 0 ? void 0 : _theme$colorSchemes$l.onEmphasis.trim());
33
33
  expect(render( /*#__PURE__*/React.createElement(CounterLabel, {
34
34
  scheme: "primary"
35
- }))).toHaveStyleRule('background-color', (_theme$colorSchemes$l2 = theme.colorSchemes.light.colors.counter) === null || _theme$colorSchemes$l2 === void 0 ? void 0 : _theme$colorSchemes$l2.primary.bg.trim());
35
+ }))).toHaveStyleRule('background-color', (_theme$colorSchemes$l2 = theme.colorSchemes.light.colors.neutral) === null || _theme$colorSchemes$l2 === void 0 ? void 0 : _theme$colorSchemes$l2.emphasis.trim());
36
36
  });
37
37
  it('respects the secondary "scheme" prop', () => {
38
38
  var _theme$colorSchemes$l3, _theme$colorSchemes$l4;
39
39
 
40
40
  expect(render( /*#__PURE__*/React.createElement(CounterLabel, {
41
41
  scheme: "secondary"
42
- }))).toHaveStyleRule('color', (_theme$colorSchemes$l3 = theme.colorSchemes.light.colors.counter) === null || _theme$colorSchemes$l3 === void 0 ? void 0 : _theme$colorSchemes$l3.text.trim());
42
+ }))).toHaveStyleRule('color', (_theme$colorSchemes$l3 = theme.colorSchemes.light.colors.fg) === null || _theme$colorSchemes$l3 === void 0 ? void 0 : _theme$colorSchemes$l3.default.trim());
43
43
  expect(render( /*#__PURE__*/React.createElement(CounterLabel, {
44
44
  scheme: "secondary"
45
- }))).toHaveStyleRule('background-color', (_theme$colorSchemes$l4 = theme.colorSchemes.light.colors.counter) === null || _theme$colorSchemes$l4 === void 0 ? void 0 : _theme$colorSchemes$l4.bg);
45
+ }))).toHaveStyleRule('background-color', (_theme$colorSchemes$l4 = theme.colorSchemes.light.colors.neutral) === null || _theme$colorSchemes$l4 === void 0 ? void 0 : _theme$colorSchemes$l4.muted);
46
46
  });
47
47
  });
@@ -39,13 +39,13 @@ describe('Flash', () => {
39
39
 
40
40
  expect(render( /*#__PURE__*/React.createElement(Flash, {
41
41
  variant: "warning"
42
- }))).toHaveStyleRule('background-color', (_theme$colorSchemes$l = theme.colorSchemes.light.colors.alert) === null || _theme$colorSchemes$l === void 0 ? void 0 : _theme$colorSchemes$l.warn.bg);
42
+ }))).toHaveStyleRule('background-color', (_theme$colorSchemes$l = theme.colorSchemes.light.colors.attention) === null || _theme$colorSchemes$l === void 0 ? void 0 : _theme$colorSchemes$l.subtle);
43
43
  expect(render( /*#__PURE__*/React.createElement(Flash, {
44
44
  variant: "danger"
45
- }))).toHaveStyleRule('background-color', (_theme$colorSchemes$l2 = theme.colorSchemes.light.colors.alert) === null || _theme$colorSchemes$l2 === void 0 ? void 0 : _theme$colorSchemes$l2.error.bg);
45
+ }))).toHaveStyleRule('background-color', (_theme$colorSchemes$l2 = theme.colorSchemes.light.colors.danger) === null || _theme$colorSchemes$l2 === void 0 ? void 0 : _theme$colorSchemes$l2.subtle);
46
46
  expect(render( /*#__PURE__*/React.createElement(Flash, {
47
47
  variant: "success"
48
- }))).toHaveStyleRule('background-color', (_theme$colorSchemes$l3 = theme.colorSchemes.light.colors.alert) === null || _theme$colorSchemes$l3 === void 0 ? void 0 : _theme$colorSchemes$l3.success.bg);
49
- expect(render( /*#__PURE__*/React.createElement(Flash, null))).toHaveStyleRule('background-color', (_theme$colorSchemes$l4 = theme.colorSchemes.light.colors.alert) === null || _theme$colorSchemes$l4 === void 0 ? void 0 : _theme$colorSchemes$l4.info.bg);
48
+ }))).toHaveStyleRule('background-color', (_theme$colorSchemes$l3 = theme.colorSchemes.light.colors.success) === null || _theme$colorSchemes$l3 === void 0 ? void 0 : _theme$colorSchemes$l3.subtle);
49
+ expect(render( /*#__PURE__*/React.createElement(Flash, null))).toHaveStyleRule('background-color', (_theme$colorSchemes$l4 = theme.colorSchemes.light.colors.accent) === null || _theme$colorSchemes$l4 === void 0 ? void 0 : _theme$colorSchemes$l4.subtle);
50
50
  });
51
51
  });
@@ -29,7 +29,7 @@ describe('Link', () => {
29
29
  });
30
30
  it('respects hoverColor prop', () => {
31
31
  expect(render( /*#__PURE__*/React.createElement(Link, {
32
- hoverColor: "text.link"
32
+ hoverColor: "accent.fg"
33
33
  }))).toMatchSnapshot();
34
34
  });
35
35
  it('respects the "fontStyle" prop', () => {