@datarobot/design-system 29.7.5 → 29.7.7

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 (45) hide show
  1. package/cjs/datetime-range-picker/datetime-range-picker-panel.d.ts +2 -2
  2. package/cjs/datetime-range-picker/datetime-range-picker-panel.js +3 -1
  3. package/cjs/datetime-range-picker/datetime-range-picker.d.ts +1 -1
  4. package/cjs/datetime-range-picker/helpers.d.ts +8 -0
  5. package/cjs/datetime-range-picker/helpers.js +40 -1
  6. package/cjs/multiselect-searchable-list/multiselect-searchable-list.js +7 -1
  7. package/cjs/multiselect-searchable-list/selected-items-board.js +12 -1
  8. package/cjs/scheduler/index.d.ts +2 -2
  9. package/cjs/scheduler/index.js +6 -0
  10. package/cjs/scheduler/utils.d.ts +4 -0
  11. package/cjs/scheduler/utils.js +87 -0
  12. package/cjs/scheduler/utils.test.js +149 -0
  13. package/cjs/zoom-controls/zoom-controls.js +3 -3
  14. package/esm/datetime-range-picker/datetime-range-picker-panel.d.ts +2 -2
  15. package/esm/datetime-range-picker/datetime-range-picker-panel.js +4 -2
  16. package/esm/datetime-range-picker/datetime-range-picker.d.ts +1 -1
  17. package/esm/datetime-range-picker/helpers.d.ts +8 -0
  18. package/esm/datetime-range-picker/helpers.js +38 -1
  19. package/esm/multiselect-searchable-list/multiselect-searchable-list.js +7 -1
  20. package/esm/multiselect-searchable-list/selected-items-board.js +12 -1
  21. package/esm/scheduler/index.d.ts +2 -2
  22. package/esm/scheduler/index.js +2 -2
  23. package/esm/scheduler/utils.d.ts +4 -0
  24. package/esm/scheduler/utils.js +87 -1
  25. package/esm/scheduler/utils.test.js +147 -0
  26. package/esm/zoom-controls/zoom-controls.js +3 -3
  27. package/js/139/139.min.js +1 -1
  28. package/js/244/244.min.js +1 -1
  29. package/js/633/633.min.js +1 -1
  30. package/js/784/784.min.js +1 -1
  31. package/js/86/86.min.js +1 -1
  32. package/js/bundle/bundle.js +173 -21
  33. package/js/bundle/bundle.min.js +1 -1
  34. package/js/bundle/index.d.ts +16 -4
  35. package/js/src_locales_es_419_translation_json/src_locales_es_419_translation_json.js +1 -1
  36. package/js/src_locales_fr_translation_json/src_locales_fr_translation_json.js +1 -1
  37. package/js/src_locales_ja_translation_json/src_locales_ja_translation_json.js +1 -1
  38. package/js/src_locales_ko_translation_json/src_locales_ko_translation_json.js +1 -1
  39. package/js/src_locales_pt_BR_translation_json/src_locales_pt_BR_translation_json.js +1 -1
  40. package/locales/es_419/translation.json +3 -3
  41. package/locales/fr/translation.json +3 -3
  42. package/locales/ja/translation.json +3 -3
  43. package/locales/ko/translation.json +3 -3
  44. package/locales/pt_BR/translation.json +3 -3
  45. package/package.json +1 -1
@@ -61,10 +61,10 @@ export type DateRangePickerPanelProps = {
61
61
  timeFormat: string | boolean;
62
62
  utcSuffix?: string;
63
63
  validateOnClick?: ValidateOnClick;
64
- validateOnChange: ValidateOnChange;
64
+ validateOnChange?: ValidateOnChange;
65
65
  dateStartInputAriaLabel: string;
66
66
  dateEndInputAriaLabel: string;
67
67
  };
68
68
  /** Represents the panel used to select two datetimes that make up a range. */
69
- declare function DateRangePickerPanel({ buttonText, cancelButtonText, dateFormat, defaultEnd, defaultStart, initialDirection, initialViewMode, initialTargetInput, maxEnd, minStart, sameDateError, onSubmit, onDismiss, resetText, timeFormat, utcSuffix, validateOnClick: propValidateOnClick, validateOnChange, dateStartInputAriaLabel, dateEndInputAriaLabel, }: DateRangePickerPanelProps): import("react/jsx-runtime").JSX.Element;
69
+ declare function DateRangePickerPanel({ buttonText, cancelButtonText, dateFormat, defaultEnd, defaultStart, initialDirection, initialViewMode, initialTargetInput, maxEnd, minStart, sameDateError, onSubmit, onDismiss, resetText, timeFormat, utcSuffix, validateOnClick: propValidateOnClick, validateOnChange: propValidateOnChange, dateStartInputAriaLabel, dateEndInputAriaLabel, }: DateRangePickerPanelProps): import("react/jsx-runtime").JSX.Element;
70
70
  export default DateRangePickerPanel;
@@ -98,12 +98,14 @@ function DateRangePickerPanel({
98
98
  timeFormat,
99
99
  utcSuffix,
100
100
  validateOnClick: propValidateOnClick,
101
- validateOnChange,
101
+ validateOnChange: propValidateOnChange,
102
102
  dateStartInputAriaLabel,
103
103
  dateEndInputAriaLabel
104
104
  }) {
105
105
  const defaultValidateOnClick = (0, _helpers.useValidateOnClick)();
106
+ const defaultValidateOnChange = (0, _helpers.useGetDateTimeValidityError)();
106
107
  const validateOnClick = propValidateOnClick || defaultValidateOnClick;
108
+ const validateOnChange = propValidateOnChange || defaultValidateOnChange;
107
109
  const dateTimeFormat = (0, _helpers.getDateTimeFormat)(dateFormat, timeFormat);
108
110
 
109
111
  // Developer-provided datetimes might not always follow the exact date and time format they want to use.
@@ -24,7 +24,7 @@ export type InitialDirection = ValueOf<typeof INITIAL_DIRECTION>;
24
24
  export type DateRangePickerProps = {
25
25
  onUpdate: (updatedStart: Moment, updatedEnd: Moment) => void;
26
26
  validateOnClick?: ValidateOnClick;
27
- validateOnChange: ValidateOnChange;
27
+ validateOnChange?: ValidateOnChange;
28
28
  start: Moment;
29
29
  end: Moment;
30
30
  minStart?: Moment;
@@ -23,6 +23,14 @@ export declare function useValidateOnClick(): ({ start, end, minStart, maxEnd, v
23
23
  END_AFTER_MAX?: string | undefined;
24
24
  };
25
25
  };
26
+ /** Checks whether a string can be strictly parsed as a moment object following a provided datetime format
27
+ *
28
+ * @param {string} proposedDateTime
29
+ * @param {string} dateTimeFormat
30
+ * @returns {boolean}
31
+ */
32
+ export declare function isValidDateTime(proposedDateTime: string, dateTimeFormat: string): boolean;
33
+ export declare function useGetDateTimeValidityError(): (dateTime: string, dateTimeFormat: string) => string | undefined;
26
34
  export interface StringToMomentParams {
27
35
  dateTimeFormat?: string;
28
36
  strictParse?: boolean;
@@ -13,7 +13,9 @@ exports.getValidityFromErrors = getValidityFromErrors;
13
13
  exports.isMonthRelatedToTimePicker = isMonthRelatedToTimePicker;
14
14
  exports.isNavigationAvailableForMonthsView = isNavigationAvailableForMonthsView;
15
15
  exports.isNavigationAvailableForYearsView = isNavigationAvailableForYearsView;
16
+ exports.isValidDateTime = isValidDateTime;
16
17
  exports.truncateDateTimeToFormat = truncateDateTimeToFormat;
18
+ exports.useGetDateTimeValidityError = useGetDateTimeValidityError;
17
19
  exports.useValidateOnClick = useValidateOnClick;
18
20
  var _react = _interopRequireDefault(require("react"));
19
21
  var _moment = _interopRequireDefault(require("moment"));
@@ -93,7 +95,7 @@ function useValidateOnClick() {
93
95
  fieldName: VALIDATION_ENTITY[viewMode].end
94
96
  });
95
97
  }
96
- if (start && end) {
98
+ if (start && end && end.isValid()) {
97
99
  endErrors = (0, _omit.default)(endErrors, [_datetimeRangePickerPanel.DATE_RANGE_ERROR_TYPES.INVALID_END_DATE]);
98
100
  }
99
101
  if (sameDateError && start?.isSame(end)) {
@@ -108,6 +110,43 @@ function useValidateOnClick() {
108
110
  };
109
111
  };
110
112
  }
113
+
114
+ /** Checks whether a string can be strictly parsed as a moment object following a provided datetime format
115
+ *
116
+ * @param {string} proposedDateTime
117
+ * @param {string} dateTimeFormat
118
+ * @returns {boolean}
119
+ */
120
+ function isValidDateTime(proposedDateTime, dateTimeFormat) {
121
+ try {
122
+ convertStringToMoment(proposedDateTime, {
123
+ dateTimeFormat
124
+ })?.isValid();
125
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
126
+ } catch (err) {
127
+ return false;
128
+ }
129
+ return true;
130
+ }
131
+ function useGetDateTimeValidityError() {
132
+ const {
133
+ t
134
+ } = (0, _useTranslation.useTranslation)();
135
+ return function getDateTimeValidityError(dateTime, dateTimeFormat) {
136
+ if (_moment.default.isMoment(dateTime)) {
137
+ return;
138
+ }
139
+ if (typeof dateTime === 'string') {
140
+ if (isValidDateTime(dateTime, dateTimeFormat)) {
141
+ return;
142
+ }
143
+ return t('Must follow {{dateTimeFormat}} format', {
144
+ dateTimeFormat
145
+ });
146
+ }
147
+ return t('Expected a datetime');
148
+ };
149
+ }
111
150
  function getValidityFromErrors(errorMapping) {
112
151
  let validityValues;
113
152
  if ((0, _isEmpty.default)(errorMapping)) {
@@ -34,6 +34,12 @@ const MultiselectSearchableList = ({
34
34
  const {
35
35
  t
36
36
  } = (0, _useTranslation.useTranslation)();
37
+ const getSelectedItemsMessageDefault = itemsLength => itemsLength === 1 ? t('{{itemsLength}} item selected', {
38
+ itemsLength
39
+ }) : t('{{itemsLength}} items selected', {
40
+ itemsLength
41
+ });
42
+ const selectedItemsMessageFn = selectedItemsMessage || getSelectedItemsMessageDefault;
37
43
  return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
38
44
  className: "multiselect-items-list",
39
45
  "test-id": "multiselect-items-list",
@@ -49,7 +55,7 @@ const MultiselectSearchableList = ({
49
55
  listKey: ({
50
56
  id
51
57
  }) => id,
52
- header: selectedItemsMessage(selectedItems.length),
58
+ header: selectedItemsMessageFn(selectedItems.length),
53
59
  onItemRemove: onSelectToggle,
54
60
  onClearAll: onClearAll,
55
61
  clearAllText: clearAllText ?? t('Clear all'),
@@ -8,6 +8,7 @@ var _react = _interopRequireWildcard(require("react"));
8
8
  var _faChevronDown = require("@fortawesome/free-solid-svg-icons/faChevronDown");
9
9
  var _faChevronUp = require("@fortawesome/free-solid-svg-icons/faChevronUp");
10
10
  var _faCircleXmark = require("@fortawesome/free-solid-svg-icons/faCircleXmark");
11
+ var _useTranslation = require("../hooks/use-translation");
11
12
  var _fontAwesomeIcon = require("../font-awesome-icon");
12
13
  var _button = require("../button");
13
14
  var _jsxRuntime = require("react/jsx-runtime");
@@ -23,9 +24,18 @@ function SelectedItemsBoard({
23
24
  clearAllText,
24
25
  removeText
25
26
  }) {
27
+ const {
28
+ t
29
+ } = (0, _useTranslation.useTranslation)();
26
30
  const [isExpanded, toggleExpanded] = (0, _react.useState)(true);
31
+ const getSelectedItemsMessageDefault = itemsLength => itemsLength === 1 ? t('{{itemsLength}} item selected', {
32
+ itemsLength
33
+ }) : t('{{itemsLength}} items selected', {
34
+ itemsLength
35
+ });
36
+
27
37
  // not in default props because it depends on items length
28
- const headerContent = header || getDefaultHeaderText?.(items.length);
38
+ const headerContent = header || getDefaultHeaderText?.(items.length) || getSelectedItemsMessageDefault(items.length);
29
39
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
30
40
  className: "selected-items-board",
31
41
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("header", {
@@ -47,6 +57,7 @@ function SelectedItemsBoard({
47
57
  "test-id": "selected-items-board-item-label",
48
58
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
49
59
  className: "item-display-label",
60
+ "test-id": item?.testId,
50
61
  children: displayLabel(item, index)
51
62
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_button.Button, {
52
63
  testId: "selected-items-board-remove-item-btn",
@@ -1,6 +1,6 @@
1
1
  import { Scheduler } from './scheduler';
2
2
  import { DEFAULT_SCHEDULE, FREQUENCIES, DEFAULT_SCHEDULES, SWITCHER_TYPES, TAB_KEYS } from './constants';
3
- import { getFrequency } from './utils';
3
+ import { getFrequency, useGetScheduleString } from './utils';
4
4
  export type { ScheduleValidity, Schedule, DateItem, Months, LabelTexts, Frequency, } from './constants';
5
5
  export type { SchedulerProps } from './scheduler';
6
- export { Scheduler, getFrequency, DEFAULT_SCHEDULE, FREQUENCIES, DEFAULT_SCHEDULES, SWITCHER_TYPES, TAB_KEYS, };
6
+ export { Scheduler, getFrequency, useGetScheduleString, DEFAULT_SCHEDULE, FREQUENCIES, DEFAULT_SCHEDULES, SWITCHER_TYPES, TAB_KEYS, };
@@ -45,6 +45,12 @@ Object.defineProperty(exports, "getFrequency", {
45
45
  return _utils.getFrequency;
46
46
  }
47
47
  });
48
+ Object.defineProperty(exports, "useGetScheduleString", {
49
+ enumerable: true,
50
+ get: function () {
51
+ return _utils.useGetScheduleString;
52
+ }
53
+ });
48
54
  var _scheduler = require("./scheduler");
49
55
  var _constants = require("./constants");
50
56
  var _utils = require("./utils");
@@ -1,4 +1,8 @@
1
1
  import { Frequency, Schedule, ScheduleValidity } from './constants';
2
2
  export declare const stringToArrayOfNumbersOrAll: (value?: string | number) => (number | "*")[];
3
3
  export declare function useArrayOfRangedNumbersOrAllValidator(): ({ min, max, values, allowAll, }: ScheduleValidity) => ScheduleValidity | undefined;
4
+ export declare function useGetScheduleString(schedule: Schedule, stringOptions?: {
5
+ complexScheduleString?: string | null;
6
+ timeStandardString?: string | null;
7
+ }): string | null;
4
8
  export declare function getFrequency({ minute, hour, month, dayOfMonth, dayOfWeek, }: Schedule): Frequency | undefined;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.getFrequency = getFrequency;
7
7
  exports.stringToArrayOfNumbersOrAll = void 0;
8
8
  exports.useArrayOfRangedNumbersOrAllValidator = useArrayOfRangedNumbersOrAllValidator;
9
+ exports.useGetScheduleString = useGetScheduleString;
9
10
  var _message = require("../message");
10
11
  var _useTranslation = require("../hooks/use-translation");
11
12
  var _constants = require("./constants");
@@ -62,6 +63,92 @@ function useArrayOfRangedNumbersOrAllValidator() {
62
63
  return validity;
63
64
  };
64
65
  }
66
+ function useGetScheduleString(schedule, stringOptions = {}) {
67
+ const {
68
+ t
69
+ } = (0, _useTranslation.useTranslation)();
70
+ const {
71
+ complexScheduleString = t('Complex schedule'),
72
+ timeStandardString = t('(UTC)')
73
+ } = stringOptions;
74
+ const scheduleStringTimeStandard = timeStandardString ? ` ${timeStandardString}` : '';
75
+ const months = (0, _constants.useMonths)();
76
+ const daysOfWeek = (0, _constants.useDaysOfWeek)();
77
+ switch (getFrequency(schedule)) {
78
+ case _constants.FREQUENCIES.HOURLY:
79
+ return schedule?.minute?.[0] === 1 ? t('Hourly at {{minutes}} minute past the hour', {
80
+ minutes: schedule.minute[0].toString()
81
+ }) : t('Hourly at {{minutes}} minutes past the hour', {
82
+ minutes: schedule?.minute?.[0].toString()
83
+ });
84
+ case _constants.FREQUENCIES.DAILY:
85
+ return `${t('Daily at {{hour}}:{{minutes}}', {
86
+ hour: schedule?.hour?.[0].toString().padStart(2, '0'),
87
+ minutes: schedule?.minute?.[0].toString().padStart(2, '0')
88
+ })}${scheduleStringTimeStandard}`;
89
+ case _constants.FREQUENCIES.WEEKLY:
90
+ {
91
+ const options = {
92
+ hour: schedule?.hour?.[0].toString().padStart(2, '0'),
93
+ minutes: schedule?.minute?.[0].toString().padStart(2, '0'),
94
+ day: daysOfWeek.find(d => d.index === schedule?.dayOfWeek?.[0])?.label
95
+ };
96
+ const weeklyString = schedule?.dayOfWeek?.length === 1 ? t('Weekly on {{day}} at {{hour}}:{{minutes}}', {
97
+ ...options
98
+ }) : t('Weekly on multiple days at {{hour}}:{{minutes}}', {
99
+ ...options
100
+ });
101
+ return `${weeklyString}${scheduleStringTimeStandard}`;
102
+ }
103
+ case _constants.FREQUENCIES.MONTHLY:
104
+ {
105
+ const options = {
106
+ hour: schedule?.hour?.[0].toString().padStart(2, '0'),
107
+ minutes: schedule?.minute?.[0].toString().padStart(2, '0'),
108
+ day: schedule?.dayOfMonth?.[0],
109
+ days: schedule?.dayOfMonth?.join(', ') // day 1, 2, 3
110
+ };
111
+ const monthlyString = schedule?.dayOfMonth?.length === 1 ? t('Monthly on day {{day}} at {{hour}}:{{minutes}}', {
112
+ ...options
113
+ }) : t('Monthly on days {{days}} at {{hour}}:{{minutes}}', {
114
+ ...options
115
+ });
116
+ return `${monthlyString}${scheduleStringTimeStandard}`;
117
+ }
118
+ case _constants.FREQUENCIES.QUARTERLY:
119
+ {
120
+ const options = {
121
+ hour: schedule?.hour?.[0].toString().padStart(2, '0'),
122
+ minutes: schedule?.minute?.[0].toString().padStart(2, '0'),
123
+ day: schedule?.dayOfMonth?.[0],
124
+ days: schedule?.dayOfMonth?.join(', ') // day 1, 2, 3
125
+ };
126
+ const quarterlyString = schedule?.dayOfMonth?.length === 1 ? t('Quarterly on day {{day}} at {{hour}}:{{minutes}}', {
127
+ ...options
128
+ }) : t('Quarterly on days {{days}} at {{hour}}:{{minutes}}', {
129
+ ...options
130
+ });
131
+ return `${quarterlyString}${scheduleStringTimeStandard}`;
132
+ }
133
+ case _constants.FREQUENCIES.YEARLY:
134
+ {
135
+ const options = {
136
+ hour: schedule?.hour?.[0].toString().padStart(2, '0'),
137
+ minutes: schedule?.minute?.[0].toString().padStart(2, '0'),
138
+ day: schedule?.dayOfMonth?.[0],
139
+ month: Object.values(months).find(m => m.index === schedule?.month?.[0])?.label
140
+ };
141
+ const yearlyString = Math.max(schedule?.month?.length ?? 0, schedule?.dayOfMonth?.length ?? 0) ? t('Yearly on {{month}} {{day}} at {{hour}}:{{minutes}}', {
142
+ ...options
143
+ }) : t('Yearly on multiple dates at {{hour}}:{{minutes}}', {
144
+ ...options
145
+ });
146
+ return `${yearlyString}${scheduleStringTimeStandard}`;
147
+ }
148
+ default:
149
+ return complexScheduleString;
150
+ }
151
+ }
65
152
  function hasSameItems(arr1, arr2) {
66
153
  return arr1.length === arr2?.length && arr1.every(item1 => {
67
154
  return arr2.find(item2 => item2 === item1);
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+
3
+ var _react = require("@testing-library/react");
4
+ var _utils = require("./utils");
5
+ describe('useGetScheduleString', function () {
6
+ describe('Schedule strings', function () {
7
+ const createSchedule = (partialSchedule = {}) => ({
8
+ minute: ['*'],
9
+ hour: ['*'],
10
+ dayOfMonth: ['*'],
11
+ month: ['*'],
12
+ dayOfWeek: ['*'],
13
+ ...partialSchedule
14
+ });
15
+ const testSchedules = [{
16
+ schedule: createSchedule({
17
+ minute: [15, 45],
18
+ month: [1, 4, 8, 12]
19
+ }),
20
+ expectedScheduleString: 'Complex schedule',
21
+ hasTimeStandard: false
22
+ }, {
23
+ schedule: createSchedule({
24
+ minute: [1]
25
+ }),
26
+ expectedScheduleString: 'Hourly at 1 minute past the hour',
27
+ hasTimeStandard: false
28
+ }, {
29
+ schedule: createSchedule({
30
+ minute: [5]
31
+ }),
32
+ expectedScheduleString: 'Hourly at 5 minutes past the hour',
33
+ hasTimeStandard: false
34
+ }, {
35
+ schedule: createSchedule({
36
+ hour: [12],
37
+ minute: [30]
38
+ }),
39
+ expectedScheduleString: 'Daily at 12:30',
40
+ hasTimeStandard: true
41
+ }, {
42
+ schedule: createSchedule({
43
+ dayOfWeek: [1],
44
+ hour: [12],
45
+ minute: [30]
46
+ }),
47
+ expectedScheduleString: 'Weekly on Monday at 12:30',
48
+ hasTimeStandard: true
49
+ }, {
50
+ schedule: createSchedule({
51
+ dayOfWeek: [1, 2],
52
+ hour: [12],
53
+ minute: [30]
54
+ }),
55
+ expectedScheduleString: 'Weekly on multiple days at 12:30',
56
+ hasTimeStandard: true
57
+ }, {
58
+ schedule: createSchedule({
59
+ dayOfMonth: [1],
60
+ hour: [6],
61
+ minute: [45]
62
+ }),
63
+ expectedScheduleString: 'Monthly on day 1 at 06:45',
64
+ hasTimeStandard: true
65
+ }, {
66
+ schedule: createSchedule({
67
+ dayOfMonth: [1, 2],
68
+ hour: [6],
69
+ minute: [45]
70
+ }),
71
+ expectedScheduleString: 'Monthly on days 1, 2 at 06:45',
72
+ hasTimeStandard: true
73
+ }, {
74
+ schedule: createSchedule({
75
+ month: [1, 4, 7, 10],
76
+ dayOfMonth: [1],
77
+ hour: [1],
78
+ minute: [30]
79
+ }),
80
+ expectedScheduleString: 'Quarterly on day 1 at 01:30',
81
+ hasTimeStandard: true
82
+ }, {
83
+ schedule: createSchedule({
84
+ month: [1, 4, 7, 10],
85
+ dayOfMonth: [1, 15],
86
+ hour: [1],
87
+ minute: [30]
88
+ }),
89
+ expectedScheduleString: 'Quarterly on days 1, 15 at 01:30',
90
+ hasTimeStandard: true
91
+ }, {
92
+ schedule: createSchedule({
93
+ dayOfMonth: [1],
94
+ month: [6],
95
+ hour: [17],
96
+ minute: [0]
97
+ }),
98
+ expectedScheduleString: 'Yearly on June 1 at 17:00',
99
+ hasTimeStandard: true
100
+ }];
101
+ describe('Default time standard (UTC)', function () {
102
+ testSchedules.forEach(function ({
103
+ schedule,
104
+ expectedScheduleString,
105
+ hasTimeStandard
106
+ }) {
107
+ const expectedString = `${expectedScheduleString}${hasTimeStandard ? ' (UTC)' : ''}`;
108
+ it(`should generate "${expectedString}"`, function () {
109
+ const {
110
+ result
111
+ } = (0, _react.renderHook)(() => (0, _utils.useGetScheduleString)(schedule));
112
+ expect(result.current).toBe(`${expectedString}`);
113
+ });
114
+ });
115
+ });
116
+ describe('Alternate time standard (EST)', function () {
117
+ testSchedules.forEach(function ({
118
+ schedule,
119
+ expectedScheduleString,
120
+ hasTimeStandard
121
+ }) {
122
+ const expectedString = `${expectedScheduleString}${hasTimeStandard ? ' (EST)' : ''}`;
123
+ it(`should generate "${expectedString}"`, function () {
124
+ const {
125
+ result
126
+ } = (0, _react.renderHook)(() => (0, _utils.useGetScheduleString)(schedule, {
127
+ timeStandardString: '(EST)'
128
+ }));
129
+ expect(result.current).toBe(`${expectedString}`);
130
+ });
131
+ });
132
+ });
133
+ describe('No time standard (locale time)', function () {
134
+ testSchedules.forEach(function ({
135
+ schedule,
136
+ expectedScheduleString
137
+ }) {
138
+ it(`should generate "${expectedScheduleString}"`, function () {
139
+ const {
140
+ result
141
+ } = (0, _react.renderHook)(() => (0, _utils.useGetScheduleString)(schedule, {
142
+ timeStandardString: null
143
+ }));
144
+ expect(result.current).toBe(expectedScheduleString);
145
+ });
146
+ });
147
+ });
148
+ });
149
+ });
@@ -39,9 +39,9 @@ function ZoomControls({
39
39
  const {
40
40
  t
41
41
  } = (0, _useTranslation.useTranslation)();
42
- const zoomInTextWithDefault = zoomInText ?? t('Zoom In');
43
- const zoomOutTextWithDefault = zoomOutText ?? t('Zoom Out');
44
- const resetTextWithDefault = resetText ?? t('Reset Zoom');
42
+ const zoomInTextWithDefault = zoomInText ?? t('Zoom in');
43
+ const zoomOutTextWithDefault = zoomOutText ?? t('Zoom out');
44
+ const resetTextWithDefault = resetText ?? t('Reset zoom');
45
45
  if (hidden) {
46
46
  return null;
47
47
  }
@@ -61,10 +61,10 @@ export type DateRangePickerPanelProps = {
61
61
  timeFormat: string | boolean;
62
62
  utcSuffix?: string;
63
63
  validateOnClick?: ValidateOnClick;
64
- validateOnChange: ValidateOnChange;
64
+ validateOnChange?: ValidateOnChange;
65
65
  dateStartInputAriaLabel: string;
66
66
  dateEndInputAriaLabel: string;
67
67
  };
68
68
  /** Represents the panel used to select two datetimes that make up a range. */
69
- declare function DateRangePickerPanel({ buttonText, cancelButtonText, dateFormat, defaultEnd, defaultStart, initialDirection, initialViewMode, initialTargetInput, maxEnd, minStart, sameDateError, onSubmit, onDismiss, resetText, timeFormat, utcSuffix, validateOnClick: propValidateOnClick, validateOnChange, dateStartInputAriaLabel, dateEndInputAriaLabel, }: DateRangePickerPanelProps): import("react/jsx-runtime").JSX.Element;
69
+ declare function DateRangePickerPanel({ buttonText, cancelButtonText, dateFormat, defaultEnd, defaultStart, initialDirection, initialViewMode, initialTargetInput, maxEnd, minStart, sameDateError, onSubmit, onDismiss, resetText, timeFormat, utcSuffix, validateOnClick: propValidateOnClick, validateOnChange: propValidateOnChange, dateStartInputAriaLabel, dateEndInputAriaLabel, }: DateRangePickerPanelProps): import("react/jsx-runtime").JSX.Element;
70
70
  export default DateRangePickerPanel;
@@ -7,7 +7,7 @@ import { ValidityMessages } from '../form-field';
7
7
  import DateRangeSingleDatePicker from './date-range-single-date-picker';
8
8
  import { VIEW_MODE_TYPE } from './datetime-range-picker';
9
9
  import DateRangeSingleDatePickerInput from './date-range-single-date-picker-input';
10
- import { getValidityFromErrors, customizeCalendarView, customizeRenderCalendarView, isMonthRelatedToTimePicker, isNavigationAvailableForMonthsView, isNavigationAvailableForYearsView, getLeftCalendarInitialView, getRightCalendarInitialView, truncateDateTimeToFormat, convertStringToMoment, getDateTimeFormat, useValidateOnClick } from './helpers';
10
+ import { getValidityFromErrors, customizeCalendarView, customizeRenderCalendarView, isMonthRelatedToTimePicker, isNavigationAvailableForMonthsView, isNavigationAvailableForYearsView, getLeftCalendarInitialView, getRightCalendarInitialView, truncateDateTimeToFormat, convertStringToMoment, getDateTimeFormat, useValidateOnClick, useGetDateTimeValidityError } from './helpers';
11
11
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
12
12
  export let DATE_RANGE_ERROR_TYPES = /*#__PURE__*/function (DATE_RANGE_ERROR_TYPES) {
13
13
  DATE_RANGE_ERROR_TYPES["INVALID_DATETIME"] = "INVALID_DATETIME";
@@ -90,12 +90,14 @@ function DateRangePickerPanel({
90
90
  timeFormat,
91
91
  utcSuffix,
92
92
  validateOnClick: propValidateOnClick,
93
- validateOnChange,
93
+ validateOnChange: propValidateOnChange,
94
94
  dateStartInputAriaLabel,
95
95
  dateEndInputAriaLabel
96
96
  }) {
97
97
  const defaultValidateOnClick = useValidateOnClick();
98
+ const defaultValidateOnChange = useGetDateTimeValidityError();
98
99
  const validateOnClick = propValidateOnClick || defaultValidateOnClick;
100
+ const validateOnChange = propValidateOnChange || defaultValidateOnChange;
99
101
  const dateTimeFormat = getDateTimeFormat(dateFormat, timeFormat);
100
102
 
101
103
  // Developer-provided datetimes might not always follow the exact date and time format they want to use.
@@ -24,7 +24,7 @@ export type InitialDirection = ValueOf<typeof INITIAL_DIRECTION>;
24
24
  export type DateRangePickerProps = {
25
25
  onUpdate: (updatedStart: Moment, updatedEnd: Moment) => void;
26
26
  validateOnClick?: ValidateOnClick;
27
- validateOnChange: ValidateOnChange;
27
+ validateOnChange?: ValidateOnChange;
28
28
  start: Moment;
29
29
  end: Moment;
30
30
  minStart?: Moment;
@@ -23,6 +23,14 @@ export declare function useValidateOnClick(): ({ start, end, minStart, maxEnd, v
23
23
  END_AFTER_MAX?: string | undefined;
24
24
  };
25
25
  };
26
+ /** Checks whether a string can be strictly parsed as a moment object following a provided datetime format
27
+ *
28
+ * @param {string} proposedDateTime
29
+ * @param {string} dateTimeFormat
30
+ * @returns {boolean}
31
+ */
32
+ export declare function isValidDateTime(proposedDateTime: string, dateTimeFormat: string): boolean;
33
+ export declare function useGetDateTimeValidityError(): (dateTime: string, dateTimeFormat: string) => string | undefined;
26
34
  export interface StringToMomentParams {
27
35
  dateTimeFormat?: string;
28
36
  strictParse?: boolean;
@@ -75,7 +75,7 @@ export function useValidateOnClick() {
75
75
  fieldName: VALIDATION_ENTITY[viewMode].end
76
76
  });
77
77
  }
78
- if (start && end) {
78
+ if (start && end && end.isValid()) {
79
79
  endErrors = omit(endErrors, [DATE_RANGE_ERROR_TYPES.INVALID_END_DATE]);
80
80
  }
81
81
  if (sameDateError && start?.isSame(end)) {
@@ -90,6 +90,43 @@ export function useValidateOnClick() {
90
90
  };
91
91
  };
92
92
  }
93
+
94
+ /** Checks whether a string can be strictly parsed as a moment object following a provided datetime format
95
+ *
96
+ * @param {string} proposedDateTime
97
+ * @param {string} dateTimeFormat
98
+ * @returns {boolean}
99
+ */
100
+ export function isValidDateTime(proposedDateTime, dateTimeFormat) {
101
+ try {
102
+ convertStringToMoment(proposedDateTime, {
103
+ dateTimeFormat
104
+ })?.isValid();
105
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
106
+ } catch (err) {
107
+ return false;
108
+ }
109
+ return true;
110
+ }
111
+ export function useGetDateTimeValidityError() {
112
+ const {
113
+ t
114
+ } = useTranslation();
115
+ return function getDateTimeValidityError(dateTime, dateTimeFormat) {
116
+ if (moment.isMoment(dateTime)) {
117
+ return;
118
+ }
119
+ if (typeof dateTime === 'string') {
120
+ if (isValidDateTime(dateTime, dateTimeFormat)) {
121
+ return;
122
+ }
123
+ return t('Must follow {{dateTimeFormat}} format', {
124
+ dateTimeFormat
125
+ });
126
+ }
127
+ return t('Expected a datetime');
128
+ };
129
+ }
93
130
  export function getValidityFromErrors(errorMapping) {
94
131
  let validityValues;
95
132
  if (isEmpty(errorMapping)) {
@@ -27,6 +27,12 @@ const MultiselectSearchableList = ({
27
27
  const {
28
28
  t
29
29
  } = useTranslation();
30
+ const getSelectedItemsMessageDefault = itemsLength => itemsLength === 1 ? t('{{itemsLength}} item selected', {
31
+ itemsLength
32
+ }) : t('{{itemsLength}} items selected', {
33
+ itemsLength
34
+ });
35
+ const selectedItemsMessageFn = selectedItemsMessage || getSelectedItemsMessageDefault;
30
36
  return /*#__PURE__*/_jsx("div", {
31
37
  className: "multiselect-items-list",
32
38
  "test-id": "multiselect-items-list",
@@ -42,7 +48,7 @@ const MultiselectSearchableList = ({
42
48
  listKey: ({
43
49
  id
44
50
  }) => id,
45
- header: selectedItemsMessage(selectedItems.length),
51
+ header: selectedItemsMessageFn(selectedItems.length),
46
52
  onItemRemove: onSelectToggle,
47
53
  onClearAll: onClearAll,
48
54
  clearAllText: clearAllText ?? t('Clear all'),
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
2
2
  import { faChevronDown } from '@fortawesome/free-solid-svg-icons/faChevronDown';
3
3
  import { faChevronUp } from '@fortawesome/free-solid-svg-icons/faChevronUp';
4
4
  import { faCircleXmark } from '@fortawesome/free-solid-svg-icons/faCircleXmark';
5
+ import { useTranslation } from '../hooks/use-translation';
5
6
  import { FontAwesomeIcon } from '../font-awesome-icon';
6
7
  import { Button, ACCENT_TYPES } from '../button';
7
8
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
@@ -16,9 +17,18 @@ export default function SelectedItemsBoard({
16
17
  clearAllText,
17
18
  removeText
18
19
  }) {
20
+ const {
21
+ t
22
+ } = useTranslation();
19
23
  const [isExpanded, toggleExpanded] = useState(true);
24
+ const getSelectedItemsMessageDefault = itemsLength => itemsLength === 1 ? t('{{itemsLength}} item selected', {
25
+ itemsLength
26
+ }) : t('{{itemsLength}} items selected', {
27
+ itemsLength
28
+ });
29
+
20
30
  // not in default props because it depends on items length
21
- const headerContent = header || getDefaultHeaderText?.(items.length);
31
+ const headerContent = header || getDefaultHeaderText?.(items.length) || getSelectedItemsMessageDefault(items.length);
22
32
  return /*#__PURE__*/_jsxs("div", {
23
33
  className: "selected-items-board",
24
34
  children: [/*#__PURE__*/_jsx("header", {
@@ -40,6 +50,7 @@ export default function SelectedItemsBoard({
40
50
  "test-id": "selected-items-board-item-label",
41
51
  children: [/*#__PURE__*/_jsx("div", {
42
52
  className: "item-display-label",
53
+ "test-id": item?.testId,
43
54
  children: displayLabel(item, index)
44
55
  }), /*#__PURE__*/_jsx(Button, {
45
56
  testId: "selected-items-board-remove-item-btn",
@@ -1,6 +1,6 @@
1
1
  import { Scheduler } from './scheduler';
2
2
  import { DEFAULT_SCHEDULE, FREQUENCIES, DEFAULT_SCHEDULES, SWITCHER_TYPES, TAB_KEYS } from './constants';
3
- import { getFrequency } from './utils';
3
+ import { getFrequency, useGetScheduleString } from './utils';
4
4
  export type { ScheduleValidity, Schedule, DateItem, Months, LabelTexts, Frequency, } from './constants';
5
5
  export type { SchedulerProps } from './scheduler';
6
- export { Scheduler, getFrequency, DEFAULT_SCHEDULE, FREQUENCIES, DEFAULT_SCHEDULES, SWITCHER_TYPES, TAB_KEYS, };
6
+ export { Scheduler, getFrequency, useGetScheduleString, DEFAULT_SCHEDULE, FREQUENCIES, DEFAULT_SCHEDULES, SWITCHER_TYPES, TAB_KEYS, };