@enact/limestone 1.10.0 → 1.10.2

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 (133) hide show
  1. package/.github/PULL_REQUEST_TEMPLATE.md +1 -0
  2. package/ActionGuide/ActionGuide.d.ts +1 -1
  3. package/ActionGuide/ActionGuide.module.css +7 -0
  4. package/Alert/Alert.d.ts +9 -0
  5. package/Alert/Alert.js +63 -21
  6. package/Alert/Alert.module.css +41 -9
  7. package/Alert/AlertImage.module.css +5 -1
  8. package/Alert/tests/Alert-specs.js +6 -2
  9. package/BodyText/BodyText.d.ts +1 -1
  10. package/BodyText/BodyText.js +14 -9
  11. package/BodyText/BodyText.module.css +22 -1
  12. package/Button/Button.d.ts +3 -3
  13. package/Button/Button.module.css +81 -63
  14. package/CHANGELOG.md +87 -0
  15. package/Card/Card.d.ts +81 -9
  16. package/Card/Card.js +324 -84
  17. package/Card/Card.module.css +200 -25
  18. package/Card/tests/Card-specs.js +100 -0
  19. package/Checkbox/Checkbox.d.ts +2 -2
  20. package/Checkbox/Checkbox.module.css +2 -5
  21. package/CheckboxItem/CheckboxItem.d.ts +8 -1
  22. package/CheckboxItem/CheckboxItem.js +39 -12
  23. package/CheckboxItem/CheckboxItem.module.css +228 -4
  24. package/Chips/Chip.module.css +5 -2
  25. package/ContextualMenuDecorator/ContextualMenuDecorator.d.ts +2 -2
  26. package/ContextualPopupDecorator/ContextualPopup.module.css +1 -1
  27. package/ContextualPopupDecorator/ContextualPopupDecorator.js +2 -4
  28. package/ContextualPopupDecorator/tests/ContextualPopupDecorator-specs.js +39 -2
  29. package/Dropdown/Dropdown.d.ts +2 -2
  30. package/Dropdown/Dropdown.module.css +7 -7
  31. package/FixedPopupPanels/FixedPopupPanels.d.ts +2 -2
  32. package/FlexiblePopupPanels/FlexiblePopupPanels.module.css +15 -3
  33. package/FlexiblePopupPanels/index.d.ts +2 -2
  34. package/FormCheckboxItem/FormCheckboxItem.d.ts +1 -1
  35. package/FormCheckboxItem/FormCheckboxItem.js +1 -0
  36. package/FormCheckboxItem/FormCheckboxItem.module.css +1 -1
  37. package/Heading/Heading.d.ts +2 -2
  38. package/Heading/Heading.module.css +16 -0
  39. package/Icon/Icon.d.ts +22 -1
  40. package/Icon/Icon.js +21 -0
  41. package/Icon/Icon.module.css +12 -0
  42. package/Icon/IconList.js +43 -1
  43. package/IconItem/IconItem.d.ts +2 -2
  44. package/IconItem/IconItem.module.css +27 -14
  45. package/Image/Image.d.ts +1 -1
  46. package/Image/Image.module.css +1 -1
  47. package/ImageItem/ImageItem.d.ts +2 -2
  48. package/ImageItem/ImageItem.js +3 -16
  49. package/ImageItem/ImageItem.module.css +122 -97
  50. package/ImageItem/tests/ImageItem-specs.js +6 -5
  51. package/Input/Input.module.css +33 -16
  52. package/Input/InputField.module.css +22 -4
  53. package/Input/index.d.ts +1 -1
  54. package/Input/tests/Input-specs.js +2 -2
  55. package/Input/tests/InputField-specs.js +1 -1
  56. package/Item/Item.d.ts +2 -2
  57. package/Item/Item.module.css +29 -16
  58. package/KeyGuide/KeyGuide.d.ts +2 -2
  59. package/KeyGuide/KeyGuide.module.css +10 -0
  60. package/MediaOverlay/MediaOverlay.d.ts +2 -2
  61. package/MediaOverlay/MediaOverlay.module.css +18 -9
  62. package/MediaPlayer/MediaControls.module.css +3 -0
  63. package/MediaPlayer/MediaSlider.module.css +1 -0
  64. package/MediaPlayer/Times.module.css +5 -1
  65. package/MediaPlayer/index.d.ts +2 -2
  66. package/PageViews/PageViews.module.css +6 -1
  67. package/Panels/Header.module.css +74 -56
  68. package/Panels/Panel.module.css +1 -1
  69. package/Panels/index.d.ts +1 -1
  70. package/Picker/Picker.d.ts +1 -1
  71. package/Popup/Popup.module.css +4 -1
  72. package/PopupTabLayout/PopupTabLayout.d.ts +6 -6
  73. package/PopupTabLayout/PopupTabLayout.module.css +24 -6
  74. package/ProgressBar/ProgressBar.d.ts +1 -1
  75. package/ProgressBar/ProgressBar.module.css +12 -12
  76. package/ProgressBar/ProgressBarTooltip.module.css +7 -7
  77. package/ProgressButton/ProgressButton.d.ts +2 -2
  78. package/QuickGuidePanels/QuickGuidePanels.module.css +1 -1
  79. package/RadioItem/RadioItem.d.ts +1 -1
  80. package/RadioItem/RadioItem.module.css +21 -18
  81. package/Scroller/EditableWrapper.js +2 -1
  82. package/Scroller/Scroller.d.ts +12 -0
  83. package/Scroller/Scroller.js +15 -0
  84. package/Scroller/Scroller.module.css +10 -0
  85. package/Scroller/tests/Scroller-specs.js +120 -2
  86. package/Scroller/useThemeScroller.js +25 -2
  87. package/Slider/Slider.d.ts +1 -1
  88. package/Slider/Slider.module.css +539 -303
  89. package/Spinner/Spinner.d.ts +1 -1
  90. package/Spinner/Spinner.module.css +2 -2
  91. package/Steps/Steps.d.ts +1 -1
  92. package/Steps/Steps.js +12 -6
  93. package/Steps/Steps.module.css +19 -2
  94. package/Switch/Switch.module.css +17 -6
  95. package/SwitchItem/SwitchItem.d.ts +1 -1
  96. package/TabLayout/RefocusDecorator.js +1 -1
  97. package/TabLayout/TabGroup.module.css +13 -7
  98. package/TabLayout/TabLayout.js +4 -0
  99. package/ThemeDecorator/ThemeDecorator.d.ts +1 -1
  100. package/ThemeDecorator/ThemeDecorator.module.css +22 -1
  101. package/TimePicker/TimePicker.module.css +3 -0
  102. package/TooltipDecorator/Tooltip.js +68 -7
  103. package/TooltipDecorator/Tooltip.module.css +44 -23
  104. package/TooltipDecorator/TooltipDecorator.d.ts +52 -0
  105. package/TooltipDecorator/TooltipDecorator.js +42 -0
  106. package/TooltipDecorator/TooltipLabel.js +67 -14
  107. package/TooltipDecorator/tests/TooltipDecorator-specs.js +46 -0
  108. package/TooltipDecorator/useTooltip.js +14 -2
  109. package/VideoPlayer/Feedback.module.css +3 -0
  110. package/VideoPlayer/FeedbackTooltip.module.css +7 -0
  111. package/VideoPlayer/MediaTitle.module.css +9 -1
  112. package/VirtualList/VirtualList.d.ts +24 -0
  113. package/VirtualList/VirtualList.js +30 -0
  114. package/VirtualList/tests/stickTo-specs.js +115 -0
  115. package/VirtualList/tests/useEvent-specs.js +39 -0
  116. package/VirtualList/useEvent.js +45 -10
  117. package/VirtualList/useThemeVirtualList.js +20 -2
  118. package/WizardPanels/WizardPanels.module.css +1 -1
  119. package/fonts/Limestone_Icons.ttf +0 -0
  120. package/internal/DateComponentPicker/DateComponentPicker.module.css +3 -3
  121. package/internal/DateTime/DateTime.module.css +3 -0
  122. package/internal/Picker/Picker.js +8 -10
  123. package/internal/Picker/Picker.module.css +92 -137
  124. package/package.json +14 -17
  125. package/styles/color-mixins.less +4 -60
  126. package/styles/colors-game.less +5 -5
  127. package/styles/colors.less +9 -10
  128. package/styles/mixins.less +0 -46
  129. package/styles/motion-mixins.less +16 -7
  130. package/styles/motions.less +2 -2
  131. package/styles/variables.less +186 -69
  132. package/useScroll/ScrollbarPlaceholder.js +8 -3
  133. package/useScroll/useScroll.js +3 -1
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
 
3
3
  require("@testing-library/jest-dom");
4
+ var _spotlight = _interopRequireDefault(require("@enact/spotlight"));
4
5
  var _react = require("@testing-library/react");
6
+ var _react2 = require("react");
5
7
  var _Scroller = _interopRequireDefault(require("../Scroller"));
6
8
  var _jsxRuntime = require("react/jsx-runtime");
7
9
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
@@ -66,7 +68,7 @@ describe('Scroller', function () {
66
68
  });
67
69
  });
68
70
  describe('focusable Scrollbar', function () {
69
- test('should have focuable body and thumb when \'focusableScrollbar\' is "byEnter"', function () {
71
+ test('should have focusable body and thumb when \'focusableScrollbar\' is "byEnter"', function () {
70
72
  var id = 'scroller';
71
73
  (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_Scroller["default"], {
72
74
  "data-testid": id,
@@ -86,7 +88,7 @@ describe('Scroller', function () {
86
88
  expect(scrollBody).toHaveClass(expected);
87
89
  expect(verticalScrollbar).toHaveClass(expected);
88
90
  });
89
- test('should have focuable scroll thumb when \'focusableScrollbar\' is true', function () {
91
+ test('should have focusable scroll thumb when \'focusableScrollbar\' is true', function () {
90
92
  var id = 'scroller';
91
93
  (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_Scroller["default"], {
92
94
  "data-testid": id,
@@ -101,6 +103,93 @@ describe('Scroller', function () {
101
103
  expect(scrollBody).not.toHaveClass(expected);
102
104
  expect(verticalScrollbar).toHaveClass(expected);
103
105
  });
106
+ describe('ScrollbarPlaceholder focus restoration', function () {
107
+ var focusSpy, pointerModeSpy, currentSpy, pausedSpy, containerSpy;
108
+ var placeholderElement;
109
+ var renderWithFocusedPlaceholder = function renderWithFocusedPlaceholder() {
110
+ placeholderElement = null;
111
+ currentSpy.mockImplementation(function () {
112
+ return placeholderElement;
113
+ });
114
+ var onRender = function onRender(id, phase) {
115
+ if (phase === 'mount') {
116
+ placeholderElement = document.querySelector('[data-spotlight-ignore-restore]');
117
+ }
118
+ };
119
+ var view = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_react2.Profiler, {
120
+ id: "placeholder",
121
+ onRender: onRender,
122
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Scroller["default"], {
123
+ focusableScrollbar: "byEnter",
124
+ verticalScrollbar: "visible",
125
+ children: contents
126
+ })
127
+ }));
128
+ currentSpy.mockImplementation(function () {
129
+ return null;
130
+ });
131
+ focusSpy.mockClear();
132
+ return view;
133
+ };
134
+ beforeEach(function () {
135
+ jest.useFakeTimers();
136
+ focusSpy = jest.spyOn(_spotlight["default"], 'focus').mockImplementation(function () {
137
+ return true;
138
+ });
139
+ pointerModeSpy = jest.spyOn(_spotlight["default"], 'getPointerMode').mockReturnValue(false);
140
+ currentSpy = jest.spyOn(_spotlight["default"], 'getCurrent').mockReturnValue(null);
141
+ pausedSpy = jest.spyOn(_spotlight["default"], 'isPaused').mockReturnValue(false);
142
+ containerSpy = jest.spyOn(_spotlight["default"], 'getActiveContainer').mockReturnValue('active-container');
143
+ });
144
+ afterEach(function () {
145
+ focusSpy.mockRestore();
146
+ pointerModeSpy.mockRestore();
147
+ currentSpy.mockRestore();
148
+ pausedSpy.mockRestore();
149
+ containerSpy.mockRestore();
150
+ jest.useRealTimers();
151
+ });
152
+ test('should focus the active container after the placeholder disappears', function () {
153
+ renderWithFocusedPlaceholder();
154
+ (0, _react.act)(function () {
155
+ return jest.advanceTimersByTime(0);
156
+ });
157
+ expect(focusSpy).toHaveBeenCalledWith('active-container', {
158
+ toOuterContainer: true
159
+ });
160
+ });
161
+ test('should not focus when pointer mode is active', function () {
162
+ pointerModeSpy.mockReturnValue(true);
163
+ renderWithFocusedPlaceholder();
164
+ (0, _react.act)(function () {
165
+ return jest.advanceTimersByTime(0);
166
+ });
167
+ expect(focusSpy).not.toHaveBeenCalled();
168
+ });
169
+ test('should defer focus while spotlight is paused, then focus after 400ms once it unpauses', function () {
170
+ pausedSpy.mockReturnValue(true);
171
+ renderWithFocusedPlaceholder();
172
+ (0, _react.act)(function () {
173
+ return jest.advanceTimersByTime(0);
174
+ });
175
+ expect(focusSpy).not.toHaveBeenCalled();
176
+ pausedSpy.mockReturnValue(false);
177
+ (0, _react.act)(function () {
178
+ return jest.advanceTimersByTime(400);
179
+ });
180
+ expect(focusSpy).toHaveBeenCalledWith('active-container', {
181
+ toOuterContainer: true
182
+ });
183
+ });
184
+ test('should not focus after the 400ms retry if spotlight is still paused', function () {
185
+ pausedSpy.mockReturnValue(true);
186
+ renderWithFocusedPlaceholder();
187
+ (0, _react.act)(function () {
188
+ return jest.advanceTimersByTime(400);
189
+ });
190
+ expect(focusSpy).not.toHaveBeenCalled();
191
+ });
192
+ });
104
193
  });
105
194
  describe('Scrollbar accessibility', function () {
106
195
  test('should set a custom "aria-label" to the scroll thumb in the horizontal scroll bar', function () {
@@ -156,4 +245,33 @@ describe('Scroller', function () {
156
245
  expect(verticalScrollbar).toHaveClass('vertical');
157
246
  });
158
247
  });
248
+ describe('stickTo', function () {
249
+ test('should compute a stick-to-start scroll position when a contained item is focused', function () {
250
+ var scrollToFn = global.Element.prototype.scrollTo;
251
+ global.Element.prototype.scrollTo = jest.fn();
252
+ var pointerSpy = jest.spyOn(_spotlight["default"], 'getPointerMode').mockReturnValue(false);
253
+ var currentSpy = jest.spyOn(_spotlight["default"], 'getCurrent');
254
+ (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_Scroller["default"], {
255
+ direction: "horizontal",
256
+ scrollToContentContainerOnFocus: false,
257
+ stickTo: "start",
258
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
259
+ style: {
260
+ width: '4000px'
261
+ },
262
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
263
+ "data-testid": "stick-target",
264
+ children: "Target"
265
+ })
266
+ })
267
+ }));
268
+ var target = _react.screen.getByTestId('stick-target');
269
+ currentSpy.mockReturnValue(target);
270
+ _react.fireEvent.focusIn(target);
271
+ expect(target).toBeInTheDocument();
272
+ currentSpy.mockRestore();
273
+ pointerSpy.mockRestore();
274
+ global.Element.prototype.scrollTo = scrollToFn;
275
+ });
276
+ });
159
277
  });
@@ -284,8 +284,11 @@ var useSpottable = function useSpottable(props, instances) {
284
284
  itemLeft = _getFocusedItemBounds.left,
285
285
  itemWidth = _getFocusedItemBounds.width;
286
286
  var rtl = props.rtl,
287
+ stickTo = props.stickTo,
287
288
  coordinateCoefficient = rtl && !(_platform["default"].chrome < 85) ? -1 : 1,
288
- clientWidth = scrollContentHandle.current.scrollBounds.clientWidth,
289
+ _scrollContentHandle$ = scrollContentHandle.current.scrollBounds,
290
+ clientWidth = _scrollContentHandle$.clientWidth,
291
+ maxLeft = _scrollContentHandle$.maxLeft,
289
292
  rtlDirection = rtl ? -1 : 1,
290
293
  _scrollContentNode$ge = scrollContentNode.getBoundingClientRect(),
291
294
  containerLeft = _scrollContentNode$ge.left,
@@ -293,6 +296,18 @@ var useSpottable = function useSpottable(props, instances) {
293
296
  currentScrollLeft = rtl && coordinateCoefficient === 1 ? scrollContentHandle.current.scrollBounds.maxLeft - scrollLastPosition : scrollLastPosition,
294
297
  newItemLeft = coordinateCoefficient * scrollContentNode.scrollLeft + (itemLeft - containerLeft);
295
298
  var nextScrollLeft = scrollContentHandle.current.scrollPos.left;
299
+ if (stickTo === 'start') {
300
+ // Anchor the focused item to a fixed "focus position" near the start edge so the focus
301
+ // indicator stays put while the list scrolls beneath it. The offset matches the in-view
302
+ // threshold (`affordanceSize`) used by `calculatePositionOnFocus`.
303
+ var affordanceLeft = _resolution["default"].scale(_useScroll.affordanceSize);
304
+
305
+ // Position the item at the fixed slot, then clamp to the scroll bounds. Clamping handles
306
+ // the end of the list: the item can no longer reach the slot, so the list scrolls only to
307
+ // its last position while the focused item (including its full width) stays visible.
308
+ nextScrollLeft += rtlDirection * (newItemLeft - currentScrollLeft - affordanceLeft);
309
+ return Math.max(0, Math.min(nextScrollLeft, maxLeft));
310
+ }
296
311
  if (newItemLeft + itemWidth > clientWidth + currentScrollLeft && itemWidth < clientWidth) {
297
312
  // If focus is moved to an element outside of view area (to the right), scroller will move
298
313
  // to the right just enough to show the current `focusedItem`. This does not apply to
@@ -325,7 +340,11 @@ var useSpottable = function useSpottable(props, instances) {
325
340
  }
326
341
  var containerRect = (0, _utils.getRect)(containerNode);
327
342
  var itemRect = (0, _utils.getRect)(item);
328
- if (horizontal && !(itemRect.left >= containerRect.left + _resolution["default"].scale(_useScroll.affordanceSize) && itemRect.right <= containerRect.right - _resolution["default"].scale(_useScroll.affordanceSize))) {
343
+
344
+ // When anchoring focus to a fixed slot, always recompute the scroll position so each newly
345
+ // focused item is pulled to the slot rather than only when it falls outside the view.
346
+ var stickToStart = props.stickTo === 'start';
347
+ if (horizontal && (stickToStart || !(itemRect.left >= containerRect.left + _resolution["default"].scale(_useScroll.affordanceSize) && itemRect.right <= containerRect.right - _resolution["default"].scale(_useScroll.affordanceSize)))) {
329
348
  scrollContentHandle.current.scrollPos.left = calculateScrollLeft(item, scrollPosition);
330
349
  }
331
350
  if (vertical && !(itemRect.top >= containerRect.top && itemRect.bottom <= containerRect.bottom - _resolution["default"].scale(_useScroll.affordanceSize))) {
@@ -363,6 +382,7 @@ var useThemeScroller = exports.useThemeScroller = function useThemeScroller(prop
363
382
  delete rest.scrollContentHandle;
364
383
  delete rest.setThemeScrollContentHandle;
365
384
  delete rest.spotlightId;
385
+ delete rest.stickTo;
366
386
 
367
387
  // Hooks
368
388
  var isScrollbarVisible = isHorizontalScrollbarVisible || isVerticalScrollbarVisible;
@@ -377,6 +397,9 @@ var useThemeScroller = exports.useThemeScroller = function useThemeScroller(prop
377
397
  var _ref3 = props.focusableScrollbar === 'byEnter' ? getFocusableBodyProps(scrollContainerRef, contentId, isScrollbarVisible) : {},
378
398
  setNavigableFilter = _ref3.setNavigableFilter,
379
399
  focusableBodyProps = _objectWithoutProperties(_ref3, _excluded2);
400
+ if (focusableBodyProps.className) {
401
+ focusableBodyProps.className = (0, _classnames["default"])(focusableBodyProps.className, isVerticalScrollbarVisible && _ScrollerModule["default"].hasVerticalScrollbar, isHorizontalScrollbarVisible && _ScrollerModule["default"].hasHorizontalScrollbar);
402
+ }
380
403
  (0, _react.useLayoutEffect)(function () {
381
404
  // Initial filter setting
382
405
  if (setNavigableFilter) {
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { ChangeableProps as ui_Changeable_ChangeableProps } from "@enact/ui/Changeable";
4
4
  import { SpottableProps as spotlight_Spottable_SpottableProps } from "@enact/spotlight/Spottable";
5
- import { SkinnableProps as limestone_Skinnable_SkinnableProps } from "limestone/Skinnable";
5
+ import { SkinnableProps as limestone_Skinnable_SkinnableProps } from "@enact/limestone/Skinnable";
6
6
  import { SlottableProps as ui_Slottable_SlottableProps } from "@enact/ui/Slottable";
7
7
  import { SliderDecoratorProps as ui_Slider_SliderDecoratorProps } from "@enact/ui/Slider";
8
8
  import * as React from "react";