@autoguru/overdrive 4.45.2 → 4.47.0

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 (95) hide show
  1. package/dist/components/Calendar/Calendar.css.d.ts +8 -0
  2. package/dist/components/Calendar/Calendar.css.d.ts.map +1 -0
  3. package/dist/components/Calendar/Calendar.css.js +113 -0
  4. package/dist/components/Calendar/Calendar.d.ts +64 -0
  5. package/dist/components/Calendar/Calendar.d.ts.map +1 -0
  6. package/dist/components/Calendar/Calendar.js +122 -0
  7. package/dist/components/Calendar/CalendarButton.d.ts +7 -0
  8. package/dist/components/Calendar/CalendarButton.d.ts.map +1 -0
  9. package/dist/components/{DateTimePicker → Calendar}/CalendarButton.js +4 -3
  10. package/dist/components/{DateTimePicker → Calendar}/CalendarGrid.d.ts +4 -1
  11. package/dist/components/Calendar/CalendarGrid.d.ts.map +1 -0
  12. package/dist/components/{DateTimePicker → Calendar}/CalendarGrid.js +8 -7
  13. package/dist/components/Calendar/index.d.ts +3 -0
  14. package/dist/components/Calendar/index.d.ts.map +1 -0
  15. package/dist/components/Calendar/index.js +3 -0
  16. package/dist/components/DatePicker/DatePicker.css.d.ts +2 -1
  17. package/dist/components/DatePicker/DatePicker.css.d.ts.map +1 -1
  18. package/dist/components/DatePicker/DatePicker.css.js +60 -30
  19. package/dist/components/DatePicker/DatePicker.d.ts +83 -5
  20. package/dist/components/DatePicker/DatePicker.d.ts.map +1 -1
  21. package/dist/components/DatePicker/DatePicker.js +160 -43
  22. package/dist/components/DateTimePicker/DateTimePicker.css.d.ts +0 -15
  23. package/dist/components/DateTimePicker/DateTimePicker.css.d.ts.map +1 -1
  24. package/dist/components/DateTimePicker/DateTimePicker.css.js +16 -99
  25. package/dist/components/DateTimePicker/DateTimePicker.d.ts +5 -2
  26. package/dist/components/DateTimePicker/DateTimePicker.d.ts.map +1 -1
  27. package/dist/components/DateTimePicker/DateTimePicker.js +24 -80
  28. package/dist/components/OptionGrid/OptionGrid.css.d.ts +33 -34
  29. package/dist/components/OptionGrid/OptionGrid.css.d.ts.map +1 -1
  30. package/dist/components/OptionGrid/OptionGrid.css.js +208 -146
  31. package/dist/components/OptionGrid/OptionGrid.d.ts +16 -5
  32. package/dist/components/OptionGrid/OptionGrid.d.ts.map +1 -1
  33. package/dist/components/OptionGrid/OptionGrid.js +38 -13
  34. package/dist/components/OptionList/OptionList.css.d.ts +2 -10
  35. package/dist/components/OptionList/OptionList.css.d.ts.map +1 -1
  36. package/dist/components/OptionList/OptionList.css.js +92 -92
  37. package/dist/components/OptionList/OptionList.d.ts +10 -3
  38. package/dist/components/OptionList/OptionList.d.ts.map +1 -1
  39. package/dist/components/OptionList/OptionListItem.js +3 -3
  40. package/dist/components/Popover/Popover.css.d.ts +4 -0
  41. package/dist/components/Popover/Popover.css.d.ts.map +1 -0
  42. package/dist/components/Popover/Popover.css.js +46 -0
  43. package/dist/components/Popover/Popover.d.ts +34 -0
  44. package/dist/components/Popover/Popover.d.ts.map +1 -0
  45. package/dist/components/Popover/Popover.js +127 -0
  46. package/dist/components/Popover/PopoverTrigger.d.ts +51 -0
  47. package/dist/components/Popover/PopoverTrigger.d.ts.map +1 -0
  48. package/dist/components/Popover/PopoverTrigger.js +94 -0
  49. package/dist/components/Popover/index.d.ts +5 -0
  50. package/dist/components/Popover/index.d.ts.map +1 -0
  51. package/dist/components/Popover/index.js +4 -0
  52. package/dist/components/SearchBar/SearchBar.css.d.ts +7 -12
  53. package/dist/components/SearchBar/SearchBar.css.d.ts.map +1 -1
  54. package/dist/components/SearchBar/SearchBar.css.js +121 -66
  55. package/dist/components/SearchBar/SearchBar.d.ts.map +1 -1
  56. package/dist/components/SearchBar/SearchBar.js +17 -10
  57. package/dist/components/Slider/Slider.css.d.ts +9 -0
  58. package/dist/components/Slider/Slider.css.d.ts.map +1 -0
  59. package/dist/components/Slider/Slider.css.js +92 -0
  60. package/dist/components/Slider/Slider.d.ts +47 -0
  61. package/dist/components/Slider/Slider.d.ts.map +1 -0
  62. package/dist/components/Slider/Slider.js +137 -0
  63. package/dist/components/Slider/index.d.ts +2 -0
  64. package/dist/components/Slider/index.d.ts.map +1 -0
  65. package/dist/components/Slider/index.js +3 -0
  66. package/dist/components/index.d.ts +1 -0
  67. package/dist/components/index.d.ts.map +1 -1
  68. package/dist/components/index.js +1 -0
  69. package/dist/hooks/useMedia/useMedia.spec.d.ts +2 -0
  70. package/dist/hooks/useMedia/useMedia.spec.d.ts.map +1 -0
  71. package/dist/hooks/useMedia/useMedia.spec.js +288 -0
  72. package/dist/styles/selectors.d.ts +14 -0
  73. package/dist/styles/selectors.d.ts.map +1 -0
  74. package/dist/styles/selectors.js +27 -0
  75. package/dist/utils/css.d.ts +0 -22
  76. package/dist/utils/css.d.ts.map +1 -1
  77. package/dist/utils/css.js +0 -52
  78. package/dist/utils/css.spec.d.ts +2 -0
  79. package/dist/utils/css.spec.d.ts.map +1 -0
  80. package/dist/utils/css.spec.js +66 -0
  81. package/dist/utils/dateFormat.d.ts +24 -0
  82. package/dist/utils/dateFormat.d.ts.map +1 -0
  83. package/dist/utils/dateFormat.js +57 -0
  84. package/dist/utils/object.spec.d.ts +2 -0
  85. package/dist/utils/object.spec.d.ts.map +1 -0
  86. package/dist/utils/object.spec.js +135 -0
  87. package/dist/utils/responsiveStyle.spec.d.ts +2 -0
  88. package/dist/utils/responsiveStyle.spec.d.ts.map +1 -0
  89. package/dist/utils/responsiveStyle.spec.js +134 -0
  90. package/dist/utils/utils.spec.d.ts.map +1 -1
  91. package/dist/utils/utils.spec.js +342 -0
  92. package/package.json +25 -25
  93. package/dist/components/DateTimePicker/CalendarButton.d.ts +0 -4
  94. package/dist/components/DateTimePicker/CalendarButton.d.ts.map +0 -1
  95. package/dist/components/DateTimePicker/CalendarGrid.d.ts.map +0 -1
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+
3
+ import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
4
+ import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
5
+ const _excluded = ["testId", "unitText", "step", "formatOptions"];
6
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
7
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
8
+ import { MinusIcon, PlusIcon } from '@autoguru/icons';
9
+ import React from 'react';
10
+ import { mergeProps, useFocusRing, useNumberFormatter, useSlider, useSliderThumb } from 'react-aria';
11
+ import { useSliderState } from 'react-stately';
12
+ import { dataAttrs } from "../../utils/dataAttrs.js";
13
+ import { Box } from "../Box/index.js";
14
+ import { Icon } from "../Icon/index.js";
15
+ import { VisuallyHidden } from "../VisuallyHidden/index.js";
16
+ import * as styles from "./Slider.css.js";
17
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
18
+ const SliderThumb = ({
19
+ state,
20
+ trackRef,
21
+ index,
22
+ name
23
+ }) => {
24
+ const inputRef = React.useRef(null);
25
+ const {
26
+ thumbProps,
27
+ inputProps,
28
+ isDisabled,
29
+ isDragging
30
+ } = useSliderThumb({
31
+ index,
32
+ trackRef,
33
+ inputRef,
34
+ name
35
+ }, state);
36
+ const {
37
+ isFocusVisible,
38
+ focusProps
39
+ } = useFocusRing();
40
+ return /*#__PURE__*/_jsx("div", _objectSpread(_objectSpread(_objectSpread({}, thumbProps), {}, {
41
+ className: styles.thumb
42
+ }, dataAttrs({
43
+ 'focus-visible': isFocusVisible,
44
+ dragging: isDragging,
45
+ disabled: isDisabled
46
+ })), {}, {
47
+ children: /*#__PURE__*/_jsx(VisuallyHidden, {
48
+ children: /*#__PURE__*/_jsx("input", _objectSpread(_objectSpread({}, mergeProps(inputProps, focusProps)), {}, {
49
+ ref: inputRef
50
+ }))
51
+ })
52
+ }));
53
+ };
54
+
55
+ /**
56
+ * A slider component that allows users to select a value from a range,
57
+ * supports configurable step values and value unit post-fix. Use the `onChange` handler
58
+ * in most instances for retrieving the current value.
59
+ *
60
+ * @example
61
+ * const handleOnChange = (value) => {
62
+ * console.log('Distance changed to:', value);
63
+ * };
64
+ *
65
+ * <Slider
66
+ * label="Distance"
67
+ * value={distance}
68
+ * onChange={handleOnChange}
69
+ * minValue={0}
70
+ * maxValue={100}
71
+ * step={5}
72
+ * unitText="kms"
73
+ * />;
74
+ */
75
+ export const Slider = _ref => {
76
+ let {
77
+ testId,
78
+ unitText = '',
79
+ step = 5,
80
+ formatOptions
81
+ } = _ref,
82
+ props = _objectWithoutProperties(_ref, _excluded);
83
+ const trackRef = React.useRef(null);
84
+ const numberFormatter = useNumberFormatter(formatOptions);
85
+ const state = useSliderState(_objectSpread(_objectSpread({}, props), {}, {
86
+ step,
87
+ numberFormatter
88
+ }));
89
+ const {
90
+ groupProps,
91
+ trackProps,
92
+ labelProps,
93
+ outputProps
94
+ } = useSlider(_objectSpread(_objectSpread({}, props), {}, {
95
+ step
96
+ }), state, trackRef);
97
+ return /*#__PURE__*/_jsxs(Box, _objectSpread(_objectSpread({}, groupProps), {}, {
98
+ className: styles.container,
99
+ odComponent: "slider",
100
+ testId: testId,
101
+ children: [props.label && /*#__PURE__*/_jsx("label", _objectSpread(_objectSpread({}, labelProps), {}, {
102
+ className: styles.label,
103
+ children: props.label
104
+ })), /*#__PURE__*/_jsx("div", {
105
+ className: styles.valueDisplay,
106
+ children: /*#__PURE__*/_jsxs("output", _objectSpread(_objectSpread({}, outputProps), {}, {
107
+ className: styles.valueText,
108
+ children: [state.getThumbValueLabel(0), unitText]
109
+ }))
110
+ }), /*#__PURE__*/_jsxs("div", {
111
+ className: styles.sliderContainer,
112
+ children: [/*#__PURE__*/_jsx(Icon, {
113
+ icon: MinusIcon,
114
+ size: "medium"
115
+ }), /*#__PURE__*/_jsx("div", {
116
+ className: styles.trackContainer,
117
+ children: /*#__PURE__*/_jsx("div", _objectSpread(_objectSpread(_objectSpread({}, trackProps), {}, {
118
+ ref: trackRef,
119
+ className: styles.track
120
+ }, dataAttrs({
121
+ disabled: props.isDisabled
122
+ })), {}, {
123
+ children: /*#__PURE__*/_jsx(SliderThumb, {
124
+ index: 0,
125
+ state: state,
126
+ trackRef: trackRef,
127
+ name: props.name
128
+ })
129
+ }))
130
+ }), /*#__PURE__*/_jsx(Icon, {
131
+ icon: PlusIcon,
132
+ size: "medium"
133
+ })]
134
+ })]
135
+ }));
136
+ };
137
+ Slider.displayName = 'Slider';
@@ -0,0 +1,2 @@
1
+ export { Slider, type SliderProps } from './Slider';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/components/Slider/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+
3
+ export { Slider } from "./Slider.js";
@@ -44,6 +44,7 @@ export * from './ScrollPane';
44
44
  export * from './Section';
45
45
  export * from './SelectInput';
46
46
  export * from './SimplePagination';
47
+ export * from './Slider';
47
48
  export * from './SliderProgress';
48
49
  export * from './Stack';
49
50
  export * from './StandardModal';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,0BAA0B,CAAC;AACzC,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,cAAc,CAAC;AAC7B,cAAc,QAAQ,CAAC;AACvB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,SAAS,CAAC;AACxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,0BAA0B,CAAC;AACzC,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,cAAc,CAAC;AAC7B,cAAc,QAAQ,CAAC;AACvB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,UAAU,CAAC;AACzB,cAAc,kBAAkB,CAAC;AACjC,cAAc,SAAS,CAAC;AACxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC"}
@@ -46,6 +46,7 @@ export * from "./ScrollPane/index.js";
46
46
  export * from "./Section/index.js";
47
47
  export * from "./SelectInput/index.js";
48
48
  export * from "./SimplePagination/index.js";
49
+ export * from "./Slider/index.js";
49
50
  export * from "./SliderProgress/index.js";
50
51
  export * from "./Stack/index.js";
51
52
  export * from "./StandardModal/index.js";
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=useMedia.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMedia.spec.d.ts","sourceRoot":"","sources":["../../../lib/hooks/useMedia/useMedia.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,288 @@
1
+ "use strict";
2
+
3
+ import { renderHook, act } from '@testing-library/react';
4
+ import React from 'react';
5
+ import { OverdriveProvider } from "../../components/OverdriveProvider/index.js";
6
+ import baseTheme from "../../themes/base/index.js";
7
+ import { useMedia } from "./useMedia.js"; // Mock the utils module to control isBrowser
8
+ vi.mock('../../utils', () => ({
9
+ isBrowser: true
10
+ }));
11
+
12
+ // Mock window.matchMedia since it's not available in test environment
13
+ const mockMatchMedia = vi.fn();
14
+ Object.defineProperty(window, 'matchMedia', {
15
+ writable: true,
16
+ value: mockMatchMedia
17
+ });
18
+
19
+ // Helper to create a mock MediaQueryList
20
+ const createMockMediaQueryList = (matches = false) => ({
21
+ matches,
22
+ media: '',
23
+ onchange: null,
24
+ addListener: vi.fn(),
25
+ // deprecated but still used by some
26
+ removeListener: vi.fn(),
27
+ // deprecated but still used by some
28
+ addEventListener: vi.fn(),
29
+ removeEventListener: vi.fn(),
30
+ dispatchEvent: vi.fn()
31
+ });
32
+
33
+ // Wrapper component with OverdriveProvider
34
+ const createWrapper = () => {
35
+ // eslint-disable-next-line react/display-name
36
+ return ({
37
+ children
38
+ }) => /*#__PURE__*/React.createElement(OverdriveProvider, {
39
+ theme: baseTheme
40
+ }, children);
41
+ };
42
+ describe('useMedia hook', () => {
43
+ beforeEach(() => {
44
+ vi.clearAllMocks();
45
+
46
+ // Default mock implementation
47
+ mockMatchMedia.mockImplementation(() => createMockMediaQueryList(false));
48
+ });
49
+ afterEach(() => {
50
+ vi.restoreAllMocks();
51
+ });
52
+
53
+ // Note: SSR testing is complex due to static imports, focusing on browser behavior
54
+
55
+ describe('Browser environment', () => {
56
+ it('creates media queries with correct breakpoint values', () => {
57
+ const mockMQL = createMockMediaQueryList(false);
58
+ mockMatchMedia.mockReturnValue(mockMQL);
59
+ renderHook(() => useMedia(['mobile', 'tablet']), {
60
+ wrapper: createWrapper()
61
+ });
62
+
63
+ // Should be called twice for each query, once for initial and once for effect
64
+ expect(mockMatchMedia).toHaveBeenCalledWith('(min-width: 0px)'); // mobile
65
+ expect(mockMatchMedia).toHaveBeenCalledWith('(min-width: 768px)'); // tablet
66
+ });
67
+ it('returns initial matches state', () => {
68
+ const mockMobileMatch = createMockMediaQueryList(true);
69
+ const mockTabletMatch = createMockMediaQueryList(false);
70
+ mockMatchMedia.mockReturnValueOnce(mockMobileMatch) // initial mobile
71
+ .mockReturnValueOnce(mockTabletMatch) // initial tablet
72
+ .mockReturnValueOnce(mockMobileMatch) // effect mobile
73
+ .mockReturnValueOnce(mockTabletMatch); // effect tablet
74
+
75
+ const {
76
+ result
77
+ } = renderHook(() => useMedia(['mobile', 'tablet']), {
78
+ wrapper: createWrapper()
79
+ });
80
+ expect(result.current).toEqual([true, false]);
81
+ });
82
+ it('sets up event listeners for media query changes', () => {
83
+ const mockMQL1 = createMockMediaQueryList(false);
84
+ const mockMQL2 = createMockMediaQueryList(false);
85
+ mockMatchMedia.mockReturnValueOnce(mockMQL1).mockReturnValueOnce(mockMQL2).mockReturnValueOnce(mockMQL1).mockReturnValueOnce(mockMQL2);
86
+ renderHook(() => useMedia(['mobile', 'tablet']), {
87
+ wrapper: createWrapper()
88
+ });
89
+ expect(mockMQL1.addEventListener).toHaveBeenCalledWith('change', expect.any(Function), {
90
+ passive: true
91
+ });
92
+ expect(mockMQL2.addEventListener).toHaveBeenCalledWith('change', expect.any(Function), {
93
+ passive: true
94
+ });
95
+ });
96
+ it('updates state when media query changes', () => {
97
+ const mockMQL = createMockMediaQueryList(false);
98
+ let changeHandler;
99
+ mockMQL.addEventListener.mockImplementation((event, handler) => {
100
+ if (event === 'change') {
101
+ changeHandler = handler;
102
+ }
103
+ });
104
+ mockMatchMedia.mockReturnValue(mockMQL);
105
+ const {
106
+ result
107
+ } = renderHook(() => useMedia(['mobile']), {
108
+ wrapper: createWrapper()
109
+ });
110
+ expect(result.current).toEqual([false]);
111
+
112
+ // Simulate media query change
113
+ act(() => {
114
+ changeHandler({
115
+ matches: true
116
+ });
117
+ });
118
+ expect(result.current).toEqual([true]);
119
+ });
120
+ it('handles multiple media query changes independently', () => {
121
+ const mockMQL1 = createMockMediaQueryList(false);
122
+ const mockMQL2 = createMockMediaQueryList(false);
123
+ let changeHandler1;
124
+ let changeHandler2;
125
+ mockMQL1.addEventListener.mockImplementation((event, handler) => {
126
+ if (event === 'change') changeHandler1 = handler;
127
+ });
128
+ mockMQL2.addEventListener.mockImplementation((event, handler) => {
129
+ if (event === 'change') changeHandler2 = handler;
130
+ });
131
+ mockMatchMedia.mockReturnValueOnce(mockMQL1).mockReturnValueOnce(mockMQL2).mockReturnValueOnce(mockMQL1).mockReturnValueOnce(mockMQL2);
132
+ const {
133
+ result
134
+ } = renderHook(() => useMedia(['mobile', 'tablet']), {
135
+ wrapper: createWrapper()
136
+ });
137
+ expect(result.current).toEqual([false, false]);
138
+
139
+ // Change first media query
140
+ act(() => {
141
+ changeHandler1({
142
+ matches: true
143
+ });
144
+ });
145
+ expect(result.current).toEqual([true, false]);
146
+
147
+ // Change second media query
148
+ act(() => {
149
+ changeHandler2({
150
+ matches: true
151
+ });
152
+ });
153
+ expect(result.current).toEqual([true, true]);
154
+ });
155
+ it('removes event listeners on unmount', () => {
156
+ const mockMQL = createMockMediaQueryList(false);
157
+ mockMatchMedia.mockReturnValue(mockMQL);
158
+ const {
159
+ unmount
160
+ } = renderHook(() => useMedia(['mobile']), {
161
+ wrapper: createWrapper()
162
+ });
163
+ expect(mockMQL.removeEventListener).not.toHaveBeenCalled();
164
+ unmount();
165
+ expect(mockMQL.removeEventListener).toHaveBeenCalledWith('change', expect.any(Function));
166
+ });
167
+ it('prevents state updates after unmount', () => {
168
+ const mockMQL = createMockMediaQueryList(false);
169
+ let changeHandler;
170
+ mockMQL.addEventListener.mockImplementation((event, handler) => {
171
+ if (event === 'change') changeHandler = handler;
172
+ });
173
+ mockMatchMedia.mockReturnValue(mockMQL);
174
+ const {
175
+ result,
176
+ unmount
177
+ } = renderHook(() => useMedia(['mobile']), {
178
+ wrapper: createWrapper()
179
+ });
180
+ const initialResult = result.current;
181
+ unmount();
182
+
183
+ // Simulate change after unmount - should not update state
184
+ act(() => {
185
+ changeHandler({
186
+ matches: true
187
+ });
188
+ });
189
+
190
+ // State should remain unchanged after unmount
191
+ expect(result.current).toBe(initialResult);
192
+ });
193
+ it('updates listeners when queries change', () => {
194
+ const mockMQL = createMockMediaQueryList(false);
195
+ mockMatchMedia.mockReturnValue(mockMQL);
196
+ const {
197
+ rerender
198
+ } = renderHook(({
199
+ queries
200
+ }) => useMedia(queries), {
201
+ wrapper: createWrapper(),
202
+ initialProps: {
203
+ queries: ['mobile']
204
+ }
205
+ });
206
+
207
+ // Change queries
208
+ rerender({
209
+ queries: ['tablet', 'desktop']
210
+ });
211
+
212
+ // Should have been called more times now with the new queries
213
+ expect(mockMatchMedia.mock.calls.length).toBeGreaterThan(2);
214
+ });
215
+ it('handles all standard breakpoint keys', () => {
216
+ const allBreakpoints = ['mobile', 'tablet', 'desktop', 'largeDesktop'];
217
+ mockMatchMedia.mockReturnValue(createMockMediaQueryList(false));
218
+ const {
219
+ result
220
+ } = renderHook(() => useMedia(allBreakpoints), {
221
+ wrapper: createWrapper()
222
+ });
223
+ expect(result.current).toHaveLength(4);
224
+ expect(result.current).toEqual([false, false, false, false]);
225
+
226
+ // Verify correct media queries were created
227
+ expect(mockMatchMedia).toHaveBeenCalledWith('(min-width: 0px)'); // mobile
228
+ expect(mockMatchMedia).toHaveBeenCalledWith('(min-width: 768px)'); // tablet
229
+ expect(mockMatchMedia).toHaveBeenCalledWith('(min-width: 1024px)'); // desktop
230
+ expect(mockMatchMedia).toHaveBeenCalledWith('(min-width: 1920px)'); // largeDesktop
231
+ });
232
+ it('handles empty queries array in browser', () => {
233
+ mockMatchMedia.mockReturnValue(createMockMediaQueryList(false));
234
+ const {
235
+ result
236
+ } = renderHook(() => useMedia([]), {
237
+ wrapper: createWrapper()
238
+ });
239
+ expect(result.current).toEqual([]);
240
+ expect(mockMatchMedia).not.toHaveBeenCalled();
241
+ });
242
+ it('maintains referential stability when values do not change', () => {
243
+ const mockMQL = createMockMediaQueryList(false);
244
+ mockMatchMedia.mockReturnValue(mockMQL);
245
+ const {
246
+ result,
247
+ rerender
248
+ } = renderHook(() => useMedia(['mobile']), {
249
+ wrapper: createWrapper()
250
+ });
251
+ const firstResult = result.current;
252
+ rerender();
253
+
254
+ // Should maintain the same reference when values haven't changed
255
+ expect(result.current).toBe(firstResult);
256
+ });
257
+ });
258
+ describe('Edge cases', () => {
259
+ it('handles rapid successive changes', () => {
260
+ const mockMQL = createMockMediaQueryList(false);
261
+ let changeHandler;
262
+ mockMQL.addEventListener.mockImplementation((event, handler) => {
263
+ if (event === 'change') changeHandler = handler;
264
+ });
265
+ mockMatchMedia.mockReturnValue(mockMQL);
266
+ const {
267
+ result
268
+ } = renderHook(() => useMedia(['mobile']), {
269
+ wrapper: createWrapper()
270
+ });
271
+ expect(result.current).toEqual([false]);
272
+
273
+ // Rapid changes
274
+ act(() => {
275
+ changeHandler({
276
+ matches: true
277
+ });
278
+ changeHandler({
279
+ matches: false
280
+ });
281
+ changeHandler({
282
+ matches: true
283
+ });
284
+ });
285
+ expect(result.current).toEqual([true]);
286
+ });
287
+ });
288
+ });
@@ -0,0 +1,14 @@
1
+ export declare const selectors: {
2
+ readonly active: "&:active";
3
+ readonly checked: "&:checked, &[data-checked], &[data-selected]";
4
+ readonly disabled: "&:disabled, &[data-disabled], &[aria-disabled=\"true\"]";
5
+ readonly focus: "&:focus, &[data-focus], &[data-focused]";
6
+ readonly focusVisible: "&:focus-visible, &[data-focus-visible]";
7
+ readonly selected: "&[data-selected], &[aria-selected=\"true\"]";
8
+ readonly unavailable: "&[data-unavailable]";
9
+ readonly hover: string;
10
+ readonly hoverNotDisabled: string;
11
+ readonly hoverNotSelected: string;
12
+ readonly focusVisibleNotSelected: string;
13
+ };
14
+ //# sourceMappingURL=selectors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectors.d.ts","sourceRoot":"","sources":["../../lib/styles/selectors.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,SAAS;;;;;;;;;;;;CAoBZ,CAAC"}
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+
3
+ // === Common selector patterns for consistent interactive states
4
+ // These patterns provide a standardized way to target element states across components and
5
+ // include support for both pseudo-classes and data attributes used by libraries like React Aria
6
+
7
+ export const selectors = {
8
+ active: '&:active',
9
+ checked: '&:checked, &[data-checked], &[data-selected]',
10
+ disabled: '&:disabled, &[data-disabled], &[aria-disabled="true"]',
11
+ focus: '&:focus, &[data-focus], &[data-focused]',
12
+ focusVisible: '&:focus-visible, &[data-focus-visible]',
13
+ selected: '&[data-selected], &[aria-selected="true"]',
14
+ unavailable: '&[data-unavailable]',
15
+ get hover() {
16
+ return `&:hover:not(${this.disabled}), &[data-hover]:not(${this.disabled})`;
17
+ },
18
+ get hoverNotDisabled() {
19
+ return `&:hover:not(${this.disabled}), &[data-hover]:not(${this.disabled})`;
20
+ },
21
+ get hoverNotSelected() {
22
+ return `&:hover:not(${this.disabled}, ${this.selected}), &[data-hover]:not(${this.disabled}, ${this.selected})`;
23
+ },
24
+ get focusVisibleNotSelected() {
25
+ return `&:focus-visible:not(${this.disabled}, ${this.selected}), &[data-focus-visible]:not(${this.disabled}, ${this.selected})`;
26
+ }
27
+ };
@@ -1,25 +1,3 @@
1
- import type { StyleRule } from '@vanilla-extract/css';
2
1
  export declare const cssVarUnwrap: (value: string) => string;
3
2
  export declare const getThemeTokenValue: (themeClass?: string | null, token?: string | null) => string;
4
- export type SelectorStates = 'base' | 'active' | 'checked' | 'selected' | 'disabled' | 'focus' | 'focusVisible' | 'hover' | 'hoverNotFocus' | 'hoverNotSelected';
5
- export type InteractionStyleProps = Partial<Record<SelectorStates, StyleRule>>;
6
- export declare const notDisabled = ":not(:disabled,[data-disabled])";
7
- export declare const notFocused = ":not(:focus,[data-focus],[data-focused])";
8
- export declare const notSelected = ":not(:checked,[data-checked],[data-selected])";
9
- /**
10
- * Creates style variants for different interaction states that can be spread
11
- * within vanilla-extract `style` object.
12
- *
13
- * @example
14
- * const buttonStyle = style({
15
- * backgroundColor: tokens.colours.background.primary,
16
- * color: tokens.colours.text.primary,
17
- * ...interactionStyle({
18
- * initial: { background: 'white', color: 'black' },
19
- * hover: { background: 'lightgray' },
20
- * disabled: { opacity: 0.5 }
21
- * })
22
- * });
23
- */
24
- export declare const interactionStyle: (selectors: InteractionStyleProps) => StyleRule;
25
3
  //# sourceMappingURL=css.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../../lib/utils/css.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAItD,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,WAIzC,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC9B,aAAa,MAAM,GAAG,IAAI,EAC1B,QAAQ,MAAM,GAAG,IAAI,KACnB,MAQF,CAAC;AAEF,MAAM,MAAM,cAAc,GACvB,MAAM,GACN,QAAQ,GACR,SAAS,GACT,UAAU,GACV,UAAU,GACV,OAAO,GACP,cAAc,GACd,OAAO,GACP,eAAe,GACf,kBAAkB,CAAC;AACtB,MAAM,MAAM,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC,CAAC;AAG/E,eAAO,MAAM,WAAW,oCAAoC,CAAC;AAC7D,eAAO,MAAM,UAAU,6CAA6C,CAAC;AACrE,eAAO,MAAM,WAAW,kDAAkD,CAAC;AAE3E;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,GAC5B,WAAW,qBAAqB,KAC9B,SAwBF,CAAC"}
1
+ {"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../../lib/utils/css.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,WAIzC,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC9B,aAAa,MAAM,GAAG,IAAI,EAC1B,QAAQ,MAAM,GAAG,IAAI,KACnB,MAQF,CAAC"}
package/dist/utils/css.js CHANGED
@@ -1,10 +1,5 @@
1
1
  "use strict";
2
2
 
3
- import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
4
- import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
5
- const _excluded = ["base"];
6
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
7
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
8
3
  const cssVarRegExp = /var\(([^)]+)\)/;
9
4
  export const cssVarUnwrap = value => {
10
5
  const matches = cssVarRegExp.exec(value);
@@ -17,51 +12,4 @@ export const getThemeTokenValue = (themeClass, token) => {
17
12
  const themedElement = document.querySelector(`.${themeClass}`);
18
13
  if (!themedElement || !cssVar) return '';
19
14
  return ((_getComputedStyle$get = getComputedStyle(themedElement).getPropertyValue(cssVar)) === null || _getComputedStyle$get === void 0 ? void 0 : _getComputedStyle$get.trim()) || '';
20
- };
21
- export const notDisabled = ':not(:disabled,[data-disabled])';
22
- export const notFocused = ':not(:focus,[data-focus],[data-focused])';
23
- export const notSelected = ':not(:checked,[data-checked],[data-selected])';
24
-
25
- /**
26
- * Creates style variants for different interaction states that can be spread
27
- * within vanilla-extract `style` object.
28
- *
29
- * @example
30
- * const buttonStyle = style({
31
- * backgroundColor: tokens.colours.background.primary,
32
- * color: tokens.colours.text.primary,
33
- * ...interactionStyle({
34
- * initial: { background: 'white', color: 'black' },
35
- * hover: { background: 'lightgray' },
36
- * disabled: { opacity: 0.5 }
37
- * })
38
- * });
39
- */
40
- export const interactionStyle = selectors => {
41
- const {
42
- base = {}
43
- } = selectors,
44
- interactionStyles = _objectWithoutProperties(selectors, _excluded);
45
- const styles = _objectSpread(_objectSpread({}, base), {}, {
46
- selectors: {}
47
- });
48
- const rules = {
49
- hover: `&:hover${notDisabled}, &[data-hover]${notDisabled}`,
50
- hoverNotFocus: `&:hover${notFocused}${notDisabled}, &[data-hover]${notFocused}${notDisabled}`,
51
- hoverNotSelected: `&:hover${notSelected}${notDisabled}, &[data-hover]${notSelected}${notDisabled}`,
52
- active: '&:active',
53
- selected: '&:checked, &[data-checked], &[data-selected]',
54
- focus: '&:focus, &[data-focus], &[data-focused]',
55
- focusVisible: '&:focus-visible, &[data-focus-visible]',
56
- disabled: '&:disabled, &[data-disabled]'
57
- };
58
-
59
- // Add selectors for each interaction state that has styles
60
- Object.entries(interactionStyles).forEach(([state, stateStyles]) => {
61
- if (state in rules && stateStyles) {
62
- if (state === 'checked') state = 'selected';
63
- styles.selectors[rules[state]] = stateStyles;
64
- }
65
- });
66
- return styles;
67
15
  };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=css.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css.spec.d.ts","sourceRoot":"","sources":["../../lib/utils/css.spec.ts"],"names":[],"mappings":""}