@kaizen/components 1.73.12 → 1.74.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 (26) hide show
  1. package/dist/cjs/Filter/FilterMultiSelect/subcomponents/MultiSelectOption/MultiSelectOption.cjs +18 -3
  2. package/dist/cjs/Filter/FilterMultiSelect/subcomponents/SelectionControlButton/ClearButton/ClearButton.cjs +11 -1
  3. package/dist/cjs/Filter/FilterMultiSelect/subcomponents/SelectionControlButton/SelectAllButton/SelectAllButton.cjs +7 -1
  4. package/dist/cjs/TimeField/TimeField.cjs +2 -1
  5. package/dist/cjs/TimeField/subcomponents/TimeSegment/TimeSegment.cjs +4 -2
  6. package/dist/cjs/TimeField/subcomponents/TimeSegment/TimeSegment.module.scss.cjs +1 -0
  7. package/dist/esm/Filter/FilterMultiSelect/subcomponents/MultiSelectOption/MultiSelectOption.mjs +18 -3
  8. package/dist/esm/Filter/FilterMultiSelect/subcomponents/SelectionControlButton/ClearButton/ClearButton.mjs +11 -1
  9. package/dist/esm/Filter/FilterMultiSelect/subcomponents/SelectionControlButton/SelectAllButton/SelectAllButton.mjs +7 -1
  10. package/dist/esm/TimeField/TimeField.mjs +2 -1
  11. package/dist/esm/TimeField/subcomponents/TimeSegment/TimeSegment.mjs +4 -2
  12. package/dist/esm/TimeField/subcomponents/TimeSegment/TimeSegment.module.scss.mjs +1 -0
  13. package/dist/styles.css +4 -1
  14. package/dist/types/Badge/Badge.d.ts +3 -4
  15. package/dist/types/TimeField/subcomponents/TimeSegment/TimeSegment.d.ts +2 -1
  16. package/locales/en.json +12 -0
  17. package/package.json +1 -1
  18. package/src/Badge/Badge.tsx +9 -4
  19. package/src/Filter/FilterMultiSelect/subcomponents/MultiSelectOption/MultiSelectOption.spec.tsx +3 -3
  20. package/src/Filter/FilterMultiSelect/subcomponents/MultiSelectOption/MultiSelectOption.tsx +16 -2
  21. package/src/Filter/FilterMultiSelect/subcomponents/SelectionControlButton/ClearButton/ClearButton.tsx +11 -1
  22. package/src/Filter/FilterMultiSelect/subcomponents/SelectionControlButton/SelectAllButton/SelectAllButton.tsx +8 -1
  23. package/src/TimeField/TimeField.tsx +11 -3
  24. package/src/TimeField/subcomponents/TimeSegment/TimeSegment.module.scss +4 -1
  25. package/src/TimeField/subcomponents/TimeSegment/TimeSegment.tsx +7 -1
  26. package/src/__next__/Tooltip/_docs/Tooltip.spec.stories.tsx +4 -1
@@ -2,6 +2,7 @@
2
2
 
3
3
  var tslib = require('tslib');
4
4
  var React = require('react');
5
+ var i18nReactIntl = require('@cultureamp/i18n-react-intl');
5
6
  var focus = require('@react-aria/focus');
6
7
  var listbox = require('@react-aria/listbox');
7
8
  var utils = require('@react-aria/utils');
@@ -38,6 +39,7 @@ var MultiSelectOption = function (_a) {
38
39
  isFocusVisible = _f.isFocusVisible,
39
40
  focusProps = _f.focusProps;
40
41
  var countElementId = React.useId();
42
+ var formatNumber = i18nReactIntl.useIntl().formatNumber;
41
43
  return React__default.default.createElement("li", tslib.__assign({}, utils.mergeProps(optionProps, focusProps), {
42
44
  ref: ref,
43
45
  className: classnames__default.default(MultiSelectOption_module.option, classNameOverride, isSelected && MultiSelectOption_module.isSelected, isFocusVisible && MultiSelectOption_module.isFocusVisible, isDisabled && MultiSelectOption_module.isDisabled),
@@ -51,9 +53,22 @@ var MultiSelectOption = function (_a) {
51
53
  })), item.rendered, ((_d = item.value) === null || _d === void 0 ? void 0 : _d.count) && React__default.default.createElement("span", {
52
54
  id: countElementId,
53
55
  className: MultiSelectOption_module.badgeContainer
54
- }, React__default.default.createElement(Badge.Badge, {
55
- classNameOverride: MultiSelectOption_module.badge
56
- }, item.value.count), React__default.default.createElement(VisuallyHidden.VisuallyHidden, null, " available")));
56
+ }, React__default.default.createElement(i18nReactIntl.FormattedMessage, {
57
+ defaultMessage: "<Badge>{count}</Badge><VisuallyHidden> available</VisuallyHidden>",
58
+ id: "filterMultiSelectMultiSelectOption.available",
59
+ description: "Number of filter items available",
60
+ values: {
61
+ count: formatNumber(parseInt(item.value.count)),
62
+ Badge: function (children) {
63
+ return React__default.default.createElement(Badge.Badge, {
64
+ classNameOverride: MultiSelectOption_module.badge
65
+ }, children);
66
+ },
67
+ VisuallyHidden: function (children) {
68
+ return React__default.default.createElement(VisuallyHidden.VisuallyHidden, null, children);
69
+ }
70
+ }
71
+ })));
57
72
  };
58
73
  MultiSelectOption.displayName = 'FilterMultiSelect.Option';
59
74
  exports.MultiSelectOption = MultiSelectOption;
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var React = require('react');
4
+ var i18nReactIntl = require('@cultureamp/i18n-react-intl');
4
5
  var classnames = require('classnames');
5
6
  var VisuallyHidden = require('../../../../../VisuallyHidden/VisuallyHidden.cjs');
6
7
  require('../../../context/MenuTriggerProvider/MenuTriggerProvider.cjs');
@@ -31,7 +32,16 @@ var ClearButton = function () {
31
32
  }));
32
33
  }
33
34
  }
34
- }, "Clear", React__default.default.createElement(VisuallyHidden.VisuallyHidden, null, " selections"));
35
+ }, React__default.default.createElement(i18nReactIntl.FormattedMessage, {
36
+ defaultMessage: "Clear<VisuallyHidden> selections</VisuallyHidden>",
37
+ id: "filterMultiSelectClearButton.label",
38
+ description: "Clear button label for filter multi-select",
39
+ values: {
40
+ VisuallyHidden: function (children) {
41
+ return React__default.default.createElement(VisuallyHidden.VisuallyHidden, null, children);
42
+ }
43
+ }
44
+ }));
35
45
  };
36
46
  ClearButton.displayName = 'FilterMultiSelect.ClearButton';
37
47
  exports.ClearButton = ClearButton;
@@ -2,6 +2,7 @@
2
2
 
3
3
  var tslib = require('tslib');
4
4
  var React = require('react');
5
+ var i18nReactIntl = require('@cultureamp/i18n-react-intl');
5
6
  var classnames = require('classnames');
6
7
  require('../../../context/MenuTriggerProvider/MenuTriggerProvider.cjs');
7
8
  var SelectionProvider = require('../../../context/SelectionProvider/SelectionProvider.cjs');
@@ -20,6 +21,7 @@ var SelectAllButton = function () {
20
21
  var filteredOptions = Array.from(selectionState.collection.getKeys()).filter(function (key) {
21
22
  return !disabledOptions.includes(key);
22
23
  });
24
+ var formatMessage = i18nReactIntl.useIntl().formatMessage;
23
25
  return React__default.default.createElement("button", {
24
26
  type: "button",
25
27
  className: classnames__default.default(SelectionControlButton_module.button, selectionState.selectionManager.isSelectAll && SelectionControlButton_module.isDisabled),
@@ -27,7 +29,11 @@ var SelectAllButton = function () {
27
29
  onClick: function () {
28
30
  return !selectionState.selectionManager.isSelectAll && selectionState.selectionManager.setSelectedKeys(tslib.__spreadArray(tslib.__spreadArray([], selectedOptions, true), filteredOptions, true));
29
31
  }
30
- }, "Select all");
32
+ }, formatMessage({
33
+ defaultMessage: 'Select all',
34
+ id: 'filterMultiSelectSelectAllButton.label',
35
+ description: 'Select all button in filter multi select'
36
+ }));
31
37
  };
32
38
  SelectAllButton.displayName = 'FilterMultiSelect.SelectAllButton';
33
39
  exports.SelectAllButton = SelectAllButton;
@@ -84,7 +84,8 @@ var TimeFieldComponent = function (_a) {
84
84
  return React__default.default.createElement(TimeSegment.TimeSegment, {
85
85
  key: i,
86
86
  segment: segment,
87
- state: state
87
+ state: state,
88
+ hasPadding: ![8294, 8297].includes(segment.text.charCodeAt(0))
88
89
  });
89
90
  }), React__default.default.createElement("div", {
90
91
  className: TimeField_module.focusRing
@@ -15,7 +15,9 @@ var React__default = /*#__PURE__*/_interopDefault(React);
15
15
  var classnames__default = /*#__PURE__*/_interopDefault(classnames);
16
16
  var TimeSegment = function (_a) {
17
17
  var segment = _a.segment,
18
- state = _a.state;
18
+ state = _a.state,
19
+ _b = _a.hasPadding,
20
+ hasPadding = _b === void 0 ? true : _b;
19
21
  var ref = React__default.default.useRef(null);
20
22
  var segmentProps = datepicker.useDateSegment(segment, state, ref).segmentProps;
21
23
  // Chrome has a bug where `contenteditable` elements receive focus from external clicks.
@@ -26,7 +28,7 @@ var TimeSegment = function (_a) {
26
28
  className: TimeSegment_module.timeSegmentWrapper
27
29
  }, "\u200B", React__default.default.createElement("span", tslib.__assign({}, segmentProps, {
28
30
  ref: ref,
29
- className: classnames__default.default(TimeSegment_module.timeSegment, segment.type === 'literal' && TimeSegment_module.literal, segment.isPlaceholder && TimeSegment_module.placeholder, segment.type === 'dayPeriod' && TimeSegment_module.dayPeriod)
31
+ className: classnames__default.default(TimeSegment_module.timeSegment, segment.type === 'literal' && TimeSegment_module.literal, segment.isPlaceholder && TimeSegment_module.placeholder, segment.type === 'dayPeriod' && TimeSegment_module.dayPeriod, hasPadding && TimeSegment_module.hasPadding)
30
32
  }), generateSegmentDisplayText.generateSegmentDisplayText(segment)), "\u200B");
31
33
  };
32
34
  TimeSegment.displayName = 'TimeSegment';
@@ -3,6 +3,7 @@
3
3
  var styles = {
4
4
  "timeSegmentWrapper": "TimeSegment-module_timeSegmentWrapper__WYD6y",
5
5
  "timeSegment": "TimeSegment-module_timeSegment__eXb0c",
6
+ "hasPadding": "TimeSegment-module_hasPadding__9oYW3",
6
7
  "placeholder": "TimeSegment-module_placeholder__j1-gK",
7
8
  "literal": "TimeSegment-module_literal__mOkqz",
8
9
  "dayPeriod": "TimeSegment-module_dayPeriod__fzT5I"
@@ -1,5 +1,6 @@
1
1
  import { __assign } from 'tslib';
2
2
  import React, { useId } from 'react';
3
+ import { useIntl, FormattedMessage } from '@cultureamp/i18n-react-intl';
3
4
  import { useFocusRing } from '@react-aria/focus';
4
5
  import { useOption } from '@react-aria/listbox';
5
6
  import { mergeProps } from '@react-aria/utils';
@@ -30,6 +31,7 @@ const MultiSelectOption = /*#__PURE__*/function () {
30
31
  isFocusVisible = _f.isFocusVisible,
31
32
  focusProps = _f.focusProps;
32
33
  var countElementId = useId();
34
+ var formatNumber = useIntl().formatNumber;
33
35
  return /*#__PURE__*/React.createElement("li", __assign({}, mergeProps(optionProps, focusProps), {
34
36
  ref: ref,
35
37
  className: classnames(styles.option, classNameOverride, isSelected && styles.isSelected, isFocusVisible && styles.isFocusVisible, isDisabled && styles.isDisabled),
@@ -43,9 +45,22 @@ const MultiSelectOption = /*#__PURE__*/function () {
43
45
  })), item.rendered, ((_d = item.value) === null || _d === void 0 ? void 0 : _d.count) && (/*#__PURE__*/React.createElement("span", {
44
46
  id: countElementId,
45
47
  className: styles.badgeContainer
46
- }, /*#__PURE__*/React.createElement(Badge, {
47
- classNameOverride: styles.badge
48
- }, item.value.count), /*#__PURE__*/React.createElement(VisuallyHidden, null, " available"))));
48
+ }, /*#__PURE__*/React.createElement(FormattedMessage, {
49
+ defaultMessage: "<Badge>{count}</Badge><VisuallyHidden> available</VisuallyHidden>",
50
+ id: "filterMultiSelectMultiSelectOption.available",
51
+ description: "Number of filter items available",
52
+ values: {
53
+ count: formatNumber(parseInt(item.value.count)),
54
+ Badge: function (children) {
55
+ return /*#__PURE__*/React.createElement(Badge, {
56
+ classNameOverride: styles.badge
57
+ }, children);
58
+ },
59
+ VisuallyHidden: function (children) {
60
+ return /*#__PURE__*/React.createElement(VisuallyHidden, null, children);
61
+ }
62
+ }
63
+ }))));
49
64
  };
50
65
  MultiSelectOption.displayName = 'FilterMultiSelect.Option';
51
66
  return MultiSelectOption;
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import { FormattedMessage } from '@cultureamp/i18n-react-intl';
2
3
  import classnames from 'classnames';
3
4
  import { VisuallyHidden } from '../../../../../VisuallyHidden/VisuallyHidden.mjs';
4
5
  import '../../../context/MenuTriggerProvider/MenuTriggerProvider.mjs';
@@ -23,7 +24,16 @@ const ClearButton = /*#__PURE__*/function () {
23
24
  }));
24
25
  }
25
26
  }
26
- }, "Clear", /*#__PURE__*/React.createElement(VisuallyHidden, null, " selections"));
27
+ }, /*#__PURE__*/React.createElement(FormattedMessage, {
28
+ defaultMessage: "Clear<VisuallyHidden> selections</VisuallyHidden>",
29
+ id: "filterMultiSelectClearButton.label",
30
+ description: "Clear button label for filter multi-select",
31
+ values: {
32
+ VisuallyHidden: function (children) {
33
+ return /*#__PURE__*/React.createElement(VisuallyHidden, null, children);
34
+ }
35
+ }
36
+ }));
27
37
  };
28
38
  ClearButton.displayName = 'FilterMultiSelect.ClearButton';
29
39
  return ClearButton;
@@ -1,5 +1,6 @@
1
1
  import { __spreadArray } from 'tslib';
2
2
  import React from 'react';
3
+ import { useIntl } from '@cultureamp/i18n-react-intl';
3
4
  import classnames from 'classnames';
4
5
  import '../../../context/MenuTriggerProvider/MenuTriggerProvider.mjs';
5
6
  import { useSelectionContext } from '../../../context/SelectionProvider/SelectionProvider.mjs';
@@ -12,6 +13,7 @@ const SelectAllButton = /*#__PURE__*/function () {
12
13
  var filteredOptions = Array.from(selectionState.collection.getKeys()).filter(function (key) {
13
14
  return !disabledOptions.includes(key);
14
15
  });
16
+ var formatMessage = useIntl().formatMessage;
15
17
  return /*#__PURE__*/React.createElement("button", {
16
18
  type: "button",
17
19
  className: classnames(styles.button, selectionState.selectionManager.isSelectAll && styles.isDisabled),
@@ -19,7 +21,11 @@ const SelectAllButton = /*#__PURE__*/function () {
19
21
  onClick: function () {
20
22
  return !selectionState.selectionManager.isSelectAll && selectionState.selectionManager.setSelectedKeys(__spreadArray(__spreadArray([], selectedOptions, true), filteredOptions, true));
21
23
  }
22
- }, "Select all");
24
+ }, formatMessage({
25
+ defaultMessage: 'Select all',
26
+ id: 'filterMultiSelectSelectAllButton.label',
27
+ description: 'Select all button in filter multi select'
28
+ }));
23
29
  };
24
30
  SelectAllButton.displayName = 'FilterMultiSelect.SelectAllButton';
25
31
  return SelectAllButton;
@@ -79,7 +79,8 @@ const TimeFieldComponent = /*#__PURE__*/function () {
79
79
  return /*#__PURE__*/React.createElement(TimeSegment, {
80
80
  key: i,
81
81
  segment: segment,
82
- state: state
82
+ state: state,
83
+ hasPadding: ![8294, 8297].includes(segment.text.charCodeAt(0))
83
84
  });
84
85
  }), /*#__PURE__*/React.createElement("div", {
85
86
  className: styles.focusRing
@@ -7,7 +7,9 @@ import styles from './TimeSegment.module.scss.mjs';
7
7
  const TimeSegment = /*#__PURE__*/function () {
8
8
  const TimeSegment = function (_a) {
9
9
  var segment = _a.segment,
10
- state = _a.state;
10
+ state = _a.state,
11
+ _b = _a.hasPadding,
12
+ hasPadding = _b === void 0 ? true : _b;
11
13
  var ref = React.useRef(null);
12
14
  var segmentProps = useDateSegment(segment, state, ref).segmentProps;
13
15
  // Chrome has a bug where `contenteditable` elements receive focus from external clicks.
@@ -18,7 +20,7 @@ const TimeSegment = /*#__PURE__*/function () {
18
20
  className: styles.timeSegmentWrapper
19
21
  }, "\u200B", /*#__PURE__*/React.createElement("span", __assign({}, segmentProps, {
20
22
  ref: ref,
21
- className: classnames(styles.timeSegment, segment.type === 'literal' && styles.literal, segment.isPlaceholder && styles.placeholder, segment.type === 'dayPeriod' && styles.dayPeriod)
23
+ className: classnames(styles.timeSegment, segment.type === 'literal' && styles.literal, segment.isPlaceholder && styles.placeholder, segment.type === 'dayPeriod' && styles.dayPeriod, hasPadding && styles.hasPadding)
22
24
  }), generateSegmentDisplayText(segment)), "\u200B");
23
25
  };
24
26
  TimeSegment.displayName = 'TimeSegment';
@@ -1,6 +1,7 @@
1
1
  var styles = {
2
2
  "timeSegmentWrapper": "TimeSegment-module_timeSegmentWrapper__WYD6y",
3
3
  "timeSegment": "TimeSegment-module_timeSegment__eXb0c",
4
+ "hasPadding": "TimeSegment-module_hasPadding__9oYW3",
4
5
  "placeholder": "TimeSegment-module_placeholder__j1-gK",
5
6
  "literal": "TimeSegment-module_literal__mOkqz",
6
7
  "dayPeriod": "TimeSegment-module_dayPeriod__fzT5I"
package/dist/styles.css CHANGED
@@ -10408,7 +10408,6 @@ input[type=range].InputRange-module_ratingScaleRange__gI-rs::-ms-thumb:not(:disa
10408
10408
 
10409
10409
  .TimeSegment-module_timeSegment__eXb0c {
10410
10410
  display: block;
10411
- padding: 0 4px;
10412
10411
  text-align: end;
10413
10412
  background-color: var(--color-gray-300, #eaeaec);
10414
10413
  border-radius: 3px;
@@ -10423,6 +10422,10 @@ input[type=range].InputRange-module_ratingScaleRange__gI-rs::-ms-thumb:not(:disa
10423
10422
  outline: none;
10424
10423
  }
10425
10424
 
10425
+ .TimeSegment-module_hasPadding__9oYW3 {
10426
+ padding: 0 4px;
10427
+ }
10428
+
10426
10429
  .TimeSegment-module_placeholder__j1-gK {
10427
10430
  color: rgba(var(--color-purple-800-rgb, 47, 36, 56), 0.7);
10428
10431
  }
@@ -1,7 +1,6 @@
1
- import { type HTMLAttributes } from 'react';
1
+ import { type HTMLAttributes, type PropsWithChildren } from 'react';
2
2
  import { type OverrideClassName } from "../types/OverrideClassName";
3
- type BadgeCommonProps = {
4
- children?: string;
3
+ type BadgeCommonProps = PropsWithChildren<{
5
4
  /**
6
5
  * The "dark" variant is no longer in the UI kit
7
6
  */
@@ -14,7 +13,7 @@ type BadgeCommonProps = {
14
13
  * Supports "small" and "large" sizes - defaults to "small"
15
14
  */
16
15
  size?: 'small' | 'large';
17
- } & OverrideClassName<HTMLAttributes<HTMLSpanElement>>;
16
+ }> & OverrideClassName<HTMLAttributes<HTMLSpanElement>>;
18
17
  type DotProps = Omit<BadgeCommonProps, 'variant'> & {
19
18
  children?: never;
20
19
  variant: 'dot';
@@ -2,8 +2,9 @@ import { type DateFieldState, type DateSegment } from '@react-stately/datepicker
2
2
  export type TimeSegmentProps = {
3
3
  segment: DateSegment;
4
4
  state: DateFieldState;
5
+ hasPadding?: boolean;
5
6
  };
6
7
  export declare const TimeSegment: {
7
- ({ segment, state }: TimeSegmentProps): JSX.Element;
8
+ ({ segment, state, hasPadding, }: TimeSegmentProps): JSX.Element;
8
9
  displayName: string;
9
10
  };
package/locales/en.json CHANGED
@@ -62,6 +62,14 @@
62
62
  "description": "Label for the 'date to' field",
63
63
  "message": "Date to"
64
64
  },
65
+ "filterMultiSelectClearButton.label": {
66
+ "description": "Clear button label for filter multi-select",
67
+ "message": "Clear<VisuallyHidden> selections</VisuallyHidden>"
68
+ },
69
+ "filterMultiSelectMultiSelectOption.available": {
70
+ "description": "Number of filter items available",
71
+ "message": "<Badge>{count}</Badge><VisuallyHidden> available</VisuallyHidden>"
72
+ },
65
73
  "filterMultiSelectSearchInput.label": {
66
74
  "description": "Label for the search input",
67
75
  "message": "Filter options by search query"
@@ -70,6 +78,10 @@
70
78
  "description": "Placeholder for the search input",
71
79
  "message": "Search…"
72
80
  },
81
+ "filterMultiSelectSelectAllButton.label": {
82
+ "description": "Select all button in filter multi select",
83
+ "message": "Select all"
84
+ },
73
85
  "inputSearch.clear": {
74
86
  "description": "Label for the clear search button",
75
87
  "message": "Clear search"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaizen/components",
3
- "version": "1.73.12",
3
+ "version": "1.74.0",
4
4
  "description": "Kaizen component library",
5
5
  "author": "Geoffrey Chong <geoff.chong@cultureamp.com>",
6
6
  "homepage": "https://cultureamp.design",
@@ -1,10 +1,14 @@
1
- import React, { useLayoutEffect, useState, type HTMLAttributes } from 'react'
1
+ import React, {
2
+ useLayoutEffect,
3
+ useState,
4
+ type HTMLAttributes,
5
+ type PropsWithChildren,
6
+ } from 'react'
2
7
  import classnames from 'classnames'
3
8
  import { type OverrideClassName } from '~components/types/OverrideClassName'
4
9
  import styles from './Badge.module.css'
5
10
 
6
- type BadgeCommonProps = {
7
- children?: string
11
+ type BadgeCommonProps = PropsWithChildren<{
8
12
  /**
9
13
  * The "dark" variant is no longer in the UI kit
10
14
  */
@@ -17,7 +21,8 @@ type BadgeCommonProps = {
17
21
  * Supports "small" and "large" sizes - defaults to "small"
18
22
  */
19
23
  size?: 'small' | 'large'
20
- } & OverrideClassName<HTMLAttributes<HTMLSpanElement>>
24
+ }> &
25
+ OverrideClassName<HTMLAttributes<HTMLSpanElement>>
21
26
 
22
27
  type DotProps = Omit<BadgeCommonProps, 'variant'> & {
23
28
  children?: never
@@ -82,20 +82,20 @@ describe('<MultiSelectOptionWrapper /> - Visual content', () => {
82
82
  <MultiSelectOptionWrapper
83
83
  item={{
84
84
  ...itemMock,
85
- value: { ...itemMock.value, count: 'count-mock' },
85
+ value: { ...itemMock.value, count: '14' },
86
86
  }}
87
87
  />,
88
88
  )
89
89
  })
90
90
 
91
91
  it('shows the count in the badge', () => {
92
- const badge = screen.getByText('count-mock')
92
+ const badge = screen.getByText('14')
93
93
  expect(badge).toBeInTheDocument()
94
94
  })
95
95
 
96
96
  it('has aria-description to describe the count are available for this option', () => {
97
97
  const option = screen.getByLabelText('label-mock')
98
- expect(option).toHaveAccessibleDescription('count-mock available')
98
+ expect(option).toHaveAccessibleDescription('14 available')
99
99
  })
100
100
  })
101
101
  })
@@ -1,4 +1,5 @@
1
1
  import React, { useId } from 'react'
2
+ import { FormattedMessage, useIntl } from '@cultureamp/i18n-react-intl'
2
3
  import { useFocusRing } from '@react-aria/focus'
3
4
  import { useOption } from '@react-aria/listbox'
4
5
  import { mergeProps } from '@react-aria/utils'
@@ -28,6 +29,7 @@ export const MultiSelectOption = ({
28
29
  // focus ring for accessibility
29
30
  const { isFocusVisible, focusProps } = useFocusRing()
30
31
  const countElementId = useId()
32
+ const { formatNumber } = useIntl()
31
33
 
32
34
  return (
33
35
  <li
@@ -50,8 +52,20 @@ export const MultiSelectOption = ({
50
52
  {item.rendered}
51
53
  {item.value?.count && (
52
54
  <span id={countElementId} className={styles.badgeContainer}>
53
- <Badge classNameOverride={styles.badge}>{item.value.count}</Badge>
54
- <VisuallyHidden> available</VisuallyHidden>
55
+ <FormattedMessage
56
+ defaultMessage="<Badge>{count}</Badge><VisuallyHidden> available</VisuallyHidden>"
57
+ id="filterMultiSelectMultiSelectOption.available"
58
+ description="Number of filter items available"
59
+ values={{
60
+ count: formatNumber(parseInt(item.value.count)),
61
+ Badge: (children: React.ReactNode) => (
62
+ <Badge classNameOverride={styles.badge}>{children}</Badge>
63
+ ),
64
+ VisuallyHidden: (children: React.ReactNode) => (
65
+ <VisuallyHidden>{children}</VisuallyHidden>
66
+ ),
67
+ }}
68
+ />
55
69
  </span>
56
70
  )}
57
71
  </li>
@@ -1,4 +1,5 @@
1
1
  import React from 'react'
2
+ import { FormattedMessage } from '@cultureamp/i18n-react-intl'
2
3
  import classnames from 'classnames'
3
4
  import { VisuallyHidden } from '~components/VisuallyHidden'
4
5
  import { useSelectionContext } from '../../../context'
@@ -28,7 +29,16 @@ export const ClearButton = (): JSX.Element => {
28
29
  // TODO: add announcement here to inform selection cleared
29
30
  }
30
31
  >
31
- Clear<VisuallyHidden> selections</VisuallyHidden>
32
+ <FormattedMessage
33
+ defaultMessage="Clear<VisuallyHidden> selections</VisuallyHidden>"
34
+ id="filterMultiSelectClearButton.label"
35
+ description="Clear button label for filter multi-select"
36
+ values={{
37
+ VisuallyHidden: (children: React.ReactNode) => (
38
+ <VisuallyHidden>{children}</VisuallyHidden>
39
+ ),
40
+ }}
41
+ />
32
42
  </button>
33
43
  )
34
44
  }
@@ -1,4 +1,5 @@
1
1
  import React from 'react'
2
+ import { useIntl } from '@cultureamp/i18n-react-intl'
2
3
  import classnames from 'classnames'
3
4
  import { useSelectionContext } from '../../../context'
4
5
  import styles from '../SelectionControlButton.module.scss'
@@ -11,6 +12,8 @@ export const SelectAllButton = (): JSX.Element => {
11
12
  (key) => !disabledOptions.includes(key),
12
13
  )
13
14
 
15
+ const { formatMessage } = useIntl()
16
+
14
17
  return (
15
18
  <button
16
19
  type="button"
@@ -26,7 +29,11 @@ export const SelectAllButton = (): JSX.Element => {
26
29
  // TODO: add announcement here to inform all selected
27
30
  }
28
31
  >
29
- Select all
32
+ {formatMessage({
33
+ defaultMessage: 'Select all',
34
+ id: 'filterMultiSelectSelectAllButton.label',
35
+ description: 'Select all button in filter multi select',
36
+ })}
30
37
  </button>
31
38
  )
32
39
  }
@@ -108,9 +108,17 @@ const TimeFieldComponent = ({
108
108
  state.validationState === 'invalid' && styles.error,
109
109
  )}
110
110
  >
111
- {state.segments.map((segment, i) => (
112
- <TimeSegment key={i} segment={segment} state={state} />
113
- ))}
111
+ {state.segments.map((segment, i) => {
112
+ return (
113
+ <TimeSegment
114
+ key={i}
115
+ segment={segment}
116
+ state={state}
117
+ hasPadding={![8294, 8297].includes(segment.text.charCodeAt(0))}
118
+ // ^react-aria includes these characters to ensure correct RTL behaviour, but we want to avoid these adding random spacing
119
+ />
120
+ )
121
+ })}
114
122
  <div className={styles.focusRing} />
115
123
  </div>
116
124
  </div>
@@ -10,7 +10,6 @@
10
10
 
11
11
  .timeSegment {
12
12
  display: block;
13
- padding: 0 4px;
14
13
  text-align: end;
15
14
  background-color: $color-gray-300;
16
15
  border-radius: 3px;
@@ -27,6 +26,10 @@
27
26
  }
28
27
  }
29
28
 
29
+ .hasPadding {
30
+ padding: 0 4px;
31
+ }
32
+
30
33
  .placeholder {
31
34
  color: rgba($color-purple-800-rgb, 0.7);
32
35
  }
@@ -8,9 +8,14 @@ import styles from './TimeSegment.module.scss'
8
8
  export type TimeSegmentProps = {
9
9
  segment: DateSegment
10
10
  state: DateFieldState
11
+ hasPadding?: boolean
11
12
  }
12
13
 
13
- export const TimeSegment = ({ segment, state }: TimeSegmentProps): JSX.Element => {
14
+ export const TimeSegment = ({
15
+ segment,
16
+ state,
17
+ hasPadding = true,
18
+ }: TimeSegmentProps): JSX.Element => {
14
19
  const ref = React.useRef<HTMLDivElement>(null)
15
20
  const { segmentProps } = useDateSegment(segment, state, ref)
16
21
 
@@ -29,6 +34,7 @@ export const TimeSegment = ({ segment, state }: TimeSegmentProps): JSX.Element =
29
34
  segment.type === 'literal' && styles.literal,
30
35
  segment.isPlaceholder && styles.placeholder,
31
36
  segment.type === 'dayPeriod' && styles.dayPeriod,
37
+ hasPadding && styles.hasPadding,
32
38
  )}
33
39
  >
34
40
  {generateSegmentDisplayText(segment)}
@@ -62,8 +62,11 @@ export const OnButton: Story = {
62
62
 
63
63
  await step('Escape closes', async () => {
64
64
  await userEvent.tab() // focus
65
+ await waitFor(() => expect(canvas.getByRole('tooltip')).toBeVisible())
65
66
  await userEvent.keyboard('{Escape}')
66
- await waitFor(() => expect(canvas.queryByRole('tooltip')).toBeNull())
67
+ await waitFor(() => expect(canvas.queryByRole('tooltip')).toBeNull(), {
68
+ timeout: 5000,
69
+ })
67
70
  await userEvent.tab() // unfocus
68
71
  })
69
72
  },