@primer/components 30.3.0-rc.2010c7d4 → 30.3.0-rc.9dbc85a9

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 (97) hide show
  1. package/CHANGELOG.md +4 -2
  2. package/dist/browser.esm.js +717 -718
  3. package/dist/browser.esm.js.map +1 -1
  4. package/dist/browser.umd.js +320 -321
  5. package/dist/browser.umd.js.map +1 -1
  6. package/docs/content/Autocomplete.mdx +627 -0
  7. package/docs/content/TextInputTokens.mdx +89 -0
  8. package/docs/src/@primer/gatsby-theme-doctocat/nav.yml +2 -0
  9. package/lib/AnchoredOverlay/AnchoredOverlay.d.ts +2 -1
  10. package/lib/AnchoredOverlay/AnchoredOverlay.js +11 -3
  11. package/lib/Autocomplete/Autocomplete.d.ts +304 -0
  12. package/lib/Autocomplete/Autocomplete.js +145 -0
  13. package/lib/Autocomplete/AutocompleteContext.d.ts +17 -0
  14. package/lib/Autocomplete/AutocompleteContext.js +11 -0
  15. package/lib/Autocomplete/AutocompleteInput.d.ts +292 -0
  16. package/lib/Autocomplete/AutocompleteInput.js +157 -0
  17. package/lib/Autocomplete/AutocompleteMenu.d.ts +72 -0
  18. package/lib/Autocomplete/AutocompleteMenu.js +224 -0
  19. package/lib/Autocomplete/AutocompleteOverlay.d.ts +20 -0
  20. package/lib/Autocomplete/AutocompleteOverlay.js +80 -0
  21. package/lib/Autocomplete/index.d.ts +2 -0
  22. package/lib/Autocomplete/index.js +15 -0
  23. package/lib/FilteredActionList/FilteredActionList.js +5 -31
  24. package/lib/Overlay.d.ts +1 -0
  25. package/lib/Overlay.js +3 -1
  26. package/lib/__tests__/Autocomplete.test.d.ts +1 -0
  27. package/lib/__tests__/Autocomplete.test.js +528 -0
  28. package/lib/__tests__/behaviors/scrollIntoViewingArea.test.d.ts +1 -0
  29. package/lib/__tests__/behaviors/scrollIntoViewingArea.test.js +226 -0
  30. package/lib/behaviors/scrollIntoViewingArea.d.ts +1 -0
  31. package/lib/behaviors/scrollIntoViewingArea.js +39 -0
  32. package/lib/hooks/useOpenAndCloseFocus.d.ts +2 -1
  33. package/lib/hooks/useOpenAndCloseFocus.js +7 -2
  34. package/lib/hooks/useOverlay.d.ts +2 -1
  35. package/lib/hooks/useOverlay.js +4 -2
  36. package/lib/index.d.ts +2 -0
  37. package/lib/index.js +8 -0
  38. package/lib/stories/Autocomplete.stories.js +608 -0
  39. package/lib/utils/types/MandateProps.d.ts +3 -0
  40. package/lib/utils/types/MandateProps.js +1 -0
  41. package/lib/utils/types/index.d.ts +1 -0
  42. package/lib/utils/types/index.js +13 -0
  43. package/lib-esm/AnchoredOverlay/AnchoredOverlay.d.ts +2 -1
  44. package/lib-esm/AnchoredOverlay/AnchoredOverlay.js +11 -3
  45. package/lib-esm/Autocomplete/Autocomplete.d.ts +304 -0
  46. package/lib-esm/Autocomplete/Autocomplete.js +123 -0
  47. package/lib-esm/Autocomplete/AutocompleteContext.d.ts +17 -0
  48. package/lib-esm/Autocomplete/AutocompleteContext.js +2 -0
  49. package/lib-esm/Autocomplete/AutocompleteInput.d.ts +292 -0
  50. package/lib-esm/Autocomplete/AutocompleteInput.js +138 -0
  51. package/lib-esm/Autocomplete/AutocompleteMenu.d.ts +72 -0
  52. package/lib-esm/Autocomplete/AutocompleteMenu.js +205 -0
  53. package/lib-esm/Autocomplete/AutocompleteOverlay.d.ts +20 -0
  54. package/lib-esm/Autocomplete/AutocompleteOverlay.js +62 -0
  55. package/lib-esm/Autocomplete/index.d.ts +2 -0
  56. package/lib-esm/Autocomplete/index.js +1 -0
  57. package/lib-esm/FilteredActionList/FilteredActionList.js +3 -31
  58. package/lib-esm/Overlay.d.ts +1 -0
  59. package/lib-esm/Overlay.js +3 -1
  60. package/lib-esm/__tests__/Autocomplete.test.d.ts +1 -0
  61. package/lib-esm/__tests__/Autocomplete.test.js +494 -0
  62. package/lib-esm/__tests__/behaviors/scrollIntoViewingArea.test.d.ts +1 -0
  63. package/lib-esm/__tests__/behaviors/scrollIntoViewingArea.test.js +224 -0
  64. package/lib-esm/behaviors/scrollIntoViewingArea.d.ts +1 -0
  65. package/lib-esm/behaviors/scrollIntoViewingArea.js +30 -0
  66. package/lib-esm/hooks/useOpenAndCloseFocus.d.ts +2 -1
  67. package/lib-esm/hooks/useOpenAndCloseFocus.js +7 -2
  68. package/lib-esm/hooks/useOverlay.d.ts +2 -1
  69. package/lib-esm/hooks/useOverlay.js +4 -2
  70. package/lib-esm/index.d.ts +2 -0
  71. package/lib-esm/index.js +1 -0
  72. package/lib-esm/stories/Autocomplete.stories.js +549 -0
  73. package/lib-esm/utils/types/MandateProps.d.ts +3 -0
  74. package/lib-esm/utils/types/MandateProps.js +1 -0
  75. package/lib-esm/utils/types/index.d.ts +1 -0
  76. package/lib-esm/utils/types/index.js +2 -1
  77. package/package.json +1 -1
  78. package/src/AnchoredOverlay/AnchoredOverlay.tsx +14 -3
  79. package/src/Autocomplete/Autocomplete.tsx +103 -0
  80. package/src/Autocomplete/AutocompleteContext.tsx +19 -0
  81. package/src/Autocomplete/AutocompleteInput.tsx +179 -0
  82. package/src/Autocomplete/AutocompleteMenu.tsx +341 -0
  83. package/src/Autocomplete/AutocompleteOverlay.tsx +68 -0
  84. package/src/Autocomplete/index.ts +2 -0
  85. package/src/FilteredActionList/FilteredActionList.tsx +10 -25
  86. package/src/Overlay.tsx +4 -1
  87. package/src/__tests__/Autocomplete.test.tsx +444 -0
  88. package/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap +3414 -0
  89. package/src/__tests__/behaviors/scrollIntoViewingArea.test.ts +195 -0
  90. package/src/behaviors/scrollIntoViewingArea.ts +27 -0
  91. package/src/hooks/useOpenAndCloseFocus.ts +7 -2
  92. package/src/hooks/useOverlay.tsx +4 -2
  93. package/src/index.ts +2 -0
  94. package/src/stories/Autocomplete.stories.tsx +572 -0
  95. package/src/utils/types/MandateProps.ts +19 -0
  96. package/src/utils/types/index.ts +1 -0
  97. package/stats.html +1 -1
@@ -0,0 +1,226 @@
1
+ "use strict";
2
+
3
+ var _scrollIntoViewingArea = require("../../behaviors/scrollIntoViewingArea");
4
+
5
+ function scrollPositionFormula(positionData, isChildAboveViewingArea) {
6
+ const {
7
+ viewingAreaEdgePosition,
8
+ childEdgePosition,
9
+ margin
10
+ } = positionData;
11
+ const marginOffset = margin * (isChildAboveViewingArea ? -1 : 1);
12
+ return childEdgePosition - viewingAreaEdgePosition + marginOffset;
13
+ } // The DOMRect constructor isn't available in JSDOM, so we improvise here.
14
+
15
+
16
+ function makeDOMRect(x, y, width, height) {
17
+ return {
18
+ x,
19
+ y,
20
+ width,
21
+ height,
22
+ top: y,
23
+ left: x,
24
+ right: x + width,
25
+ bottom: y + height,
26
+
27
+ toJSON() {
28
+ return this;
29
+ }
30
+
31
+ };
32
+ } // Since Jest/JSDOM doesn't support layout, we can stub out getBoundingClientRect if we know the
33
+ // correct dimensions. JSDOM will handle the rest of the DOM API used by getAnchoredPosition.
34
+
35
+
36
+ function createVirtualDOM(viewingAreaRect, childRect) {
37
+ const viewingArea = document.createElement('div');
38
+ viewingArea.style.overflow = 'auto';
39
+ viewingArea.id = 'viewingArea'; // eslint-disable-next-line github/unescaped-html-literal
40
+
41
+ viewingArea.innerHTML = '<div id="child"></div>';
42
+ const child = viewingArea.querySelector('#child');
43
+
44
+ child.getBoundingClientRect = () => childRect;
45
+
46
+ viewingArea.getBoundingClientRect = () => viewingAreaRect;
47
+
48
+ return {
49
+ viewingArea,
50
+ child
51
+ };
52
+ }
53
+
54
+ describe('scrollIntoViewingArea', () => {
55
+ it('scrolls the expected amount when only the viewingArea element and child element are passed to the function', () => {
56
+ const scrollToMock = jest.fn();
57
+ Object.defineProperty(window.Element.prototype, 'scrollTo', {
58
+ writable: true,
59
+ value: scrollToMock
60
+ });
61
+ const childHeight = 50;
62
+ const viewAreaHeight = 100;
63
+ const childStart = viewAreaHeight + 10;
64
+ const expectedScrollPosition = scrollPositionFormula({
65
+ viewingAreaEdgePosition: viewAreaHeight,
66
+ childEdgePosition: childStart + childHeight,
67
+ margin: 8
68
+ }, false);
69
+ const viewingAreaRect = makeDOMRect(0, 0, 100, viewAreaHeight);
70
+ const childRect = makeDOMRect(0, childStart, 100, childHeight);
71
+ const {
72
+ viewingArea,
73
+ child
74
+ } = createVirtualDOM(viewingAreaRect, childRect);
75
+
76
+ viewingArea.getBoundingClientRect = () => viewingAreaRect;
77
+
78
+ viewingArea.scrollTop = 0;
79
+
80
+ child.getBoundingClientRect = () => childRect;
81
+
82
+ (0, _scrollIntoViewingArea.scrollIntoViewingArea)(child, viewingArea);
83
+ expect(scrollToMock).toHaveBeenCalledWith({
84
+ behavior: 'smooth',
85
+ top: expectedScrollPosition
86
+ });
87
+ });
88
+ describe('y-axis', () => {
89
+ it('scrolls the child into the viewing area when it is AFTER the overflow cutoff point', () => {
90
+ const scrollToMock = jest.fn();
91
+ Object.defineProperty(window.Element.prototype, 'scrollTo', {
92
+ writable: true,
93
+ value: scrollToMock
94
+ });
95
+ const childHeight = 50;
96
+ const viewAreaHeight = 100;
97
+ const childStart = viewAreaHeight + 10;
98
+ const scrollMargin = 10;
99
+ const expectedScrollPosition = scrollPositionFormula({
100
+ viewingAreaEdgePosition: viewAreaHeight,
101
+ childEdgePosition: childStart + childHeight,
102
+ margin: scrollMargin
103
+ }, false);
104
+ const viewingAreaRect = makeDOMRect(0, 0, 100, viewAreaHeight);
105
+ const childRect = makeDOMRect(0, childStart, 100, childHeight);
106
+ const {
107
+ viewingArea,
108
+ child
109
+ } = createVirtualDOM(viewingAreaRect, childRect);
110
+
111
+ viewingArea.getBoundingClientRect = () => viewingAreaRect;
112
+
113
+ viewingArea.scrollTop = 0;
114
+
115
+ child.getBoundingClientRect = () => childRect;
116
+
117
+ (0, _scrollIntoViewingArea.scrollIntoViewingArea)(child, viewingArea, 'vertical', scrollMargin, scrollMargin, 'auto');
118
+ expect(scrollToMock).toHaveBeenCalledWith({
119
+ behavior: 'auto',
120
+ top: expectedScrollPosition
121
+ });
122
+ });
123
+ it('scrolls the child into the viewing area when it is BEFORE the overflow cutoff point', () => {
124
+ const scrollToMock = jest.fn();
125
+ Object.defineProperty(window.Element.prototype, 'scrollTo', {
126
+ writable: true,
127
+ value: scrollToMock
128
+ });
129
+ const childHeight = 50;
130
+ const childStart = childHeight * -1 - 10;
131
+ const scrollMargin = 10;
132
+ const expectedScrollPosition = scrollPositionFormula({
133
+ viewingAreaEdgePosition: 0,
134
+ childEdgePosition: childStart,
135
+ margin: scrollMargin
136
+ }, true);
137
+ const viewingAreaRect = makeDOMRect(0, 0, 100, 100);
138
+ const childRect = makeDOMRect(0, childStart, 100, childHeight);
139
+ const {
140
+ viewingArea,
141
+ child
142
+ } = createVirtualDOM(viewingAreaRect, childRect);
143
+
144
+ viewingArea.getBoundingClientRect = () => viewingAreaRect;
145
+
146
+ viewingArea.scrollTop = 0;
147
+
148
+ child.getBoundingClientRect = () => childRect;
149
+
150
+ (0, _scrollIntoViewingArea.scrollIntoViewingArea)(child, viewingArea, 'vertical', scrollMargin, scrollMargin, 'auto');
151
+ expect(scrollToMock).toHaveBeenCalledWith({
152
+ behavior: 'auto',
153
+ top: expectedScrollPosition
154
+ });
155
+ });
156
+ });
157
+ describe('x-axis', () => {
158
+ it('scrolls the child into the viewing area when it is AFTER the overflow cutoff point', () => {
159
+ const scrollToMock = jest.fn();
160
+ Object.defineProperty(window.Element.prototype, 'scrollTo', {
161
+ writable: true,
162
+ value: scrollToMock
163
+ });
164
+ const childWidth = 50;
165
+ const viewAreaWidth = 100;
166
+ const childStart = viewAreaWidth + 10;
167
+ const scrollMargin = 10;
168
+ const expectedScrollPosition = scrollPositionFormula({
169
+ viewingAreaEdgePosition: viewAreaWidth,
170
+ childEdgePosition: childStart + childWidth,
171
+ margin: scrollMargin
172
+ }, false);
173
+ const viewingAreaRect = makeDOMRect(0, 0, 100, viewAreaWidth);
174
+ const childRect = makeDOMRect(childStart, 0, childWidth, 100);
175
+ const {
176
+ viewingArea,
177
+ child
178
+ } = createVirtualDOM(viewingAreaRect, childRect);
179
+
180
+ viewingArea.getBoundingClientRect = () => viewingAreaRect;
181
+
182
+ viewingArea.scrollLeft = 0;
183
+
184
+ child.getBoundingClientRect = () => childRect;
185
+
186
+ (0, _scrollIntoViewingArea.scrollIntoViewingArea)(child, viewingArea, 'horizontal', scrollMargin, scrollMargin, 'auto');
187
+ expect(scrollToMock).toHaveBeenCalledWith({
188
+ behavior: 'auto',
189
+ left: expectedScrollPosition
190
+ });
191
+ });
192
+ it('scrolls the child into the viewing area when it is BEFORE the overflow cutoff point', () => {
193
+ const scrollToMock = jest.fn();
194
+ Object.defineProperty(window.Element.prototype, 'scrollTo', {
195
+ writable: true,
196
+ value: scrollToMock
197
+ });
198
+ const childWidth = 50;
199
+ const childStart = childWidth * -1 - 10;
200
+ const scrollMargin = 10;
201
+ const expectedScrollPosition = scrollPositionFormula({
202
+ viewingAreaEdgePosition: 0,
203
+ childEdgePosition: childStart,
204
+ margin: scrollMargin
205
+ }, true);
206
+ const viewingAreaRect = makeDOMRect(0, 0, 100, 100);
207
+ const childRect = makeDOMRect(childStart, 0, childWidth, 100);
208
+ const {
209
+ viewingArea,
210
+ child
211
+ } = createVirtualDOM(viewingAreaRect, childRect);
212
+
213
+ viewingArea.getBoundingClientRect = () => viewingAreaRect;
214
+
215
+ viewingArea.scrollTop = 0;
216
+
217
+ child.getBoundingClientRect = () => childRect;
218
+
219
+ (0, _scrollIntoViewingArea.scrollIntoViewingArea)(child, viewingArea, 'horizontal', scrollMargin, scrollMargin, 'auto');
220
+ expect(scrollToMock).toHaveBeenCalledWith({
221
+ behavior: 'auto',
222
+ left: expectedScrollPosition
223
+ });
224
+ });
225
+ });
226
+ });
@@ -0,0 +1 @@
1
+ export declare const scrollIntoViewingArea: (child: HTMLElement, viewingArea: HTMLElement, direction?: 'horizontal' | 'vertical', startMargin?: number, endMargin?: number, behavior?: ScrollBehavior) => void;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.scrollIntoViewingArea = void 0;
7
+
8
+ const scrollIntoViewingArea = (child, viewingArea, direction = 'vertical', startMargin = 8, endMargin = 0, behavior = 'smooth') => {
9
+ const startSide = direction === 'vertical' ? 'top' : 'left';
10
+ const endSide = direction === 'vertical' ? 'bottom' : 'right';
11
+ const scrollSide = direction === 'vertical' ? 'scrollTop' : 'scrollLeft';
12
+ const {
13
+ [startSide]: childStart,
14
+ [endSide]: childEnd
15
+ } = child.getBoundingClientRect();
16
+ const {
17
+ [startSide]: viewingAreaStart,
18
+ [endSide]: viewingAreaEnd
19
+ } = viewingArea.getBoundingClientRect();
20
+ const isChildStartAboveViewingArea = childStart < viewingAreaStart + endMargin;
21
+ const isChildBottomBelowViewingArea = childEnd > viewingAreaEnd - startMargin;
22
+
23
+ if (isChildStartAboveViewingArea) {
24
+ const scrollHeightToChildStart = childStart - viewingAreaStart + viewingArea[scrollSide];
25
+ viewingArea.scrollTo({
26
+ behavior,
27
+ [startSide]: scrollHeightToChildStart - endMargin
28
+ });
29
+ } else if (isChildBottomBelowViewingArea) {
30
+ const scrollHeightToChildBottom = childEnd - viewingAreaEnd + viewingArea[scrollSide];
31
+ viewingArea.scrollTo({
32
+ behavior,
33
+ [startSide]: scrollHeightToChildBottom + startMargin
34
+ });
35
+ } // either completely in view or outside viewing area on both ends, don't scroll
36
+
37
+ };
38
+
39
+ exports.scrollIntoViewingArea = scrollIntoViewingArea;
@@ -3,5 +3,6 @@ export declare type UseOpenAndCloseFocusSettings = {
3
3
  initialFocusRef?: React.RefObject<HTMLElement>;
4
4
  containerRef: React.RefObject<HTMLElement>;
5
5
  returnFocusRef: React.RefObject<HTMLElement>;
6
+ preventFocusOnOpen?: boolean;
6
7
  };
7
- export declare function useOpenAndCloseFocus({ initialFocusRef, returnFocusRef, containerRef }: UseOpenAndCloseFocusSettings): void;
8
+ export declare function useOpenAndCloseFocus({ initialFocusRef, returnFocusRef, containerRef, preventFocusOnOpen }: UseOpenAndCloseFocusSettings): void;
@@ -12,9 +12,14 @@ var _iterateFocusableElements = require("../utils/iterateFocusableElements");
12
12
  function useOpenAndCloseFocus({
13
13
  initialFocusRef,
14
14
  returnFocusRef,
15
- containerRef
15
+ containerRef,
16
+ preventFocusOnOpen
16
17
  }) {
17
18
  (0, _react.useEffect)(() => {
19
+ if (preventFocusOnOpen) {
20
+ return;
21
+ }
22
+
18
23
  const returnRef = returnFocusRef.current;
19
24
 
20
25
  if (initialFocusRef && initialFocusRef.current) {
@@ -27,5 +32,5 @@ function useOpenAndCloseFocus({
27
32
  return function () {
28
33
  returnRef === null || returnRef === void 0 ? void 0 : returnRef.focus();
29
34
  };
30
- }, [initialFocusRef, returnFocusRef, containerRef]);
35
+ }, [initialFocusRef, returnFocusRef, containerRef, preventFocusOnOpen]);
31
36
  }
@@ -7,8 +7,9 @@ export declare type UseOverlaySettings = {
7
7
  onEscape: (e: KeyboardEvent) => void;
8
8
  onClickOutside: (e: TouchOrMouseEvent) => void;
9
9
  overlayRef?: React.RefObject<HTMLDivElement>;
10
+ preventFocusOnOpen?: boolean;
10
11
  };
11
12
  export declare type OverlayReturnProps = {
12
13
  ref: React.RefObject<HTMLDivElement>;
13
14
  };
14
- export declare const useOverlay: ({ overlayRef: _overlayRef, returnFocusRef, initialFocusRef, onEscape, ignoreClickRefs, onClickOutside }: UseOverlaySettings) => OverlayReturnProps;
15
+ export declare const useOverlay: ({ overlayRef: _overlayRef, returnFocusRef, initialFocusRef, onEscape, ignoreClickRefs, onClickOutside, preventFocusOnOpen }: UseOverlaySettings) => OverlayReturnProps;
@@ -19,13 +19,15 @@ const useOverlay = ({
19
19
  initialFocusRef,
20
20
  onEscape,
21
21
  ignoreClickRefs,
22
- onClickOutside
22
+ onClickOutside,
23
+ preventFocusOnOpen
23
24
  }) => {
24
25
  const overlayRef = (0, _useProvidedRefOrCreate.useProvidedRefOrCreate)(_overlayRef);
25
26
  (0, _useOpenAndCloseFocus.useOpenAndCloseFocus)({
26
27
  containerRef: overlayRef,
27
28
  returnFocusRef,
28
- initialFocusRef
29
+ initialFocusRef,
30
+ preventFocusOnOpen
29
31
  });
30
32
  (0, _useOnOutsideClick.useOnOutsideClick)({
31
33
  containerRef: overlayRef,
package/lib/index.d.ts CHANGED
@@ -24,6 +24,8 @@ export { useConfirm } from './Dialog/ConfirmationDialog';
24
24
  export { ActionList } from './ActionList';
25
25
  export { ActionMenu } from './ActionMenu';
26
26
  export type { ActionMenuProps } from './ActionMenu';
27
+ export { default as Autocomplete } from './Autocomplete';
28
+ export type { AutocompleteMenuProps, AutocompleteInputProps, AutocompleteOverlayProps } from './Autocomplete';
27
29
  export { default as Avatar } from './Avatar';
28
30
  export type { AvatarProps } from './Avatar';
29
31
  export { default as AvatarPair } from './AvatarPair';
package/lib/index.js CHANGED
@@ -153,6 +153,12 @@ Object.defineProperty(exports, "ActionMenu", {
153
153
  return _ActionMenu.ActionMenu;
154
154
  }
155
155
  });
156
+ Object.defineProperty(exports, "Autocomplete", {
157
+ enumerable: true,
158
+ get: function () {
159
+ return _Autocomplete.default;
160
+ }
161
+ });
156
162
  Object.defineProperty(exports, "Avatar", {
157
163
  enumerable: true,
158
164
  get: function () {
@@ -532,6 +538,8 @@ var _ActionList = require("./ActionList");
532
538
 
533
539
  var _ActionMenu = require("./ActionMenu");
534
540
 
541
+ var _Autocomplete = _interopRequireDefault(require("./Autocomplete"));
542
+
535
543
  var _Avatar = _interopRequireDefault(require("./Avatar"));
536
544
 
537
545
  var _AvatarPair = _interopRequireDefault(require("./AvatarPair"));