From bd94bb16a37c6e23487ddf65a7f19ac65dd8f5b3 Mon Sep 17 00:00:00 2001
From: jason-evans-genesys
<127438502+jason-evans-genesys@users.noreply.github.com>
Date: Fri, 6 Sep 2024 10:54:54 -0400
Subject: [PATCH] feat(time-picker): Added min and max props
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
✅ Closes: COMUI-1736
feat(time-picker): Fixed up min/max logic
✅ Closes: COMUI-1736
feat(time-picker): Fixed up min/max logic to make it more functional
✅ Closes: COMUI-1736
test(time-picker): Added min and max tests
✅ Closes: COMUI-1736
test(time-picker): Fixed 12h test for min and max
✅ Closes: COMUI-1736
chore(time-picker): General refactoring
✅ Closes: COMUI-1736
test(time-picker): Fixed min/max tests
✅ Closes: COMUI-1736
chore(time-picker): Refactoring changes
✅ Closes: COMUI-1736
chore(time-picker): Fixed comment to be more descriptive
✅ Closes: COMUI-1736
test(time-picker): Fixed test title
✅ Closes: COMUI-1736
chore(time-picker): PR feedback
✅ Closes: COMUI-1736
chore(time-picker): Fixed hour conversion logic
✅ Closes: COMUI-1736
chore(time-picker): Variable name change
✅ Closes: COMUI-1736
chore(time-picker): Removed unneeded 12h clock type test
✅ Closes: COMUI-1736
chore(time-picker): Added tests and wrapped around boundary logic
✅ Closes: COMUI-1736
chore(time-picker): Conditional check before applying boundaries
✅ Closes: COMUI-1736
chore(time-picker): Fixed boundary logic to be inclusive of min and max
✅ Closes: COMUI-1736
---
.../stable/gux-time-picker/example.html | 21 +++++++
.../gux-time-picker.service.ts | 52 +++++++++++++++-
.../gux-time-picker/gux-time-picker.tsx | 41 +++++++-----
.../stable/gux-time-picker/readme.md | 2 +
.../tests/gux-time-picker.service.spec.ts | 62 +++++++++++++++++++
5 files changed, 162 insertions(+), 16 deletions(-)
diff --git a/packages/genesys-spark-components/src/components/stable/gux-time-picker/example.html b/packages/genesys-spark-components/src/components/stable/gux-time-picker/example.html
index 51750e4ff3..fb2529263f 100644
--- a/packages/genesys-spark-components/src/components/stable/gux-time-picker/example.html
+++ b/packages/genesys-spark-components/src/components/stable/gux-time-picker/example.html
@@ -45,6 +45,27 @@
Disabled
Has Error
+
+ Min of "03:15" and Max of "10:45" with 24 Hour Time Format
+
+
+
+ Wrapped around boundaries: min of "20:30" and Max of "04:45" with 24 Hour
+ Time Format
+
+
Languages
diff --git a/packages/genesys-spark-components/src/components/stable/gux-time-picker/gux-time-picker.service.ts b/packages/genesys-spark-components/src/components/stable/gux-time-picker/gux-time-picker.service.ts
index debfc1a4ce..8867d5d58b 100644
--- a/packages/genesys-spark-components/src/components/stable/gux-time-picker/gux-time-picker.service.ts
+++ b/packages/genesys-spark-components/src/components/stable/gux-time-picker/gux-time-picker.service.ts
@@ -12,7 +12,9 @@ import {
export function getTimeDisplayValues(
minuteInterval: GuxMinuteInterval,
- clockType: GuxClockType
+ clockType: GuxClockType,
+ min?: string,
+ max?: string
): GuxISOHourMinute[] {
const minuteOptions = [0, 15, 30, 45]
.filter(option => Number.isInteger(option / minuteInterval))
@@ -22,13 +24,59 @@ export function getTimeDisplayValues(
? ['12', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11']
: Array.from(Array(24).keys()).map(x => String(x).padStart(2, '0'));
- return hourOptions.reduce((acc, hourOption) => {
+ const hourOptionsFormatted = hourOptions.reduce((acc, hourOption) => {
return acc.concat(
minuteOptions.map(
minuteOption => `${hourOption}:${minuteOption}`
) as GuxISOHourMinute[]
);
}, [] as GuxISOHourMinute[]);
+
+ return clockType === '24h'
+ ? applyHourBoundaries(hourOptionsFormatted, min, max)
+ : hourOptionsFormatted;
+}
+
+function applyHourBoundaries(hours: string[], min?: string, max?: string) {
+ // Check if min and max are wrapped around (e.g. min of 22:00 and max of 04:00)
+ if (min && max && hourToMilliseconds(min) > hourToMilliseconds(max)) {
+ hours = hours.filter(hour => {
+ const hourConverted = hourToMilliseconds(hour);
+ const minConverted = hourToMilliseconds(min);
+ const maxConverted = hourToMilliseconds(max);
+ const dayCeiling = hourToMilliseconds('23:59');
+
+ return (
+ (hourConverted >= minConverted && hourConverted < dayCeiling) ||
+ hourConverted <= maxConverted
+ );
+ });
+ } else {
+ // min and max are not wrapped around (e.g. min of 03:30 and max of 20:00)
+ if (min) {
+ hours = hours.filter(
+ hour => hourToMilliseconds(hour) >= hourToMilliseconds(min)
+ );
+ }
+ if (max) {
+ hours = hours.filter(
+ hour => hourToMilliseconds(hour) <= hourToMilliseconds(max)
+ );
+ }
+ }
+
+ return hours as GuxISOHourMinute[];
+}
+
+function hourToMilliseconds(hour: string): number {
+ // Convert the hour to milliseconds from midnight
+ const date = new Date();
+ const [hours, minutes] = hour.split(':');
+ date.setHours(parseFloat(hours));
+ date.setMinutes(parseFloat(minutes));
+ date.setSeconds(0);
+ const seconds = date.getTime();
+ return seconds;
}
export function getLocaleClockType(root: HTMLElement): GuxClockType {
diff --git a/packages/genesys-spark-components/src/components/stable/gux-time-picker/gux-time-picker.tsx b/packages/genesys-spark-components/src/components/stable/gux-time-picker/gux-time-picker.tsx
index ad925dfbed..2369b75d70 100644
--- a/packages/genesys-spark-components/src/components/stable/gux-time-picker/gux-time-picker.tsx
+++ b/packages/genesys-spark-components/src/components/stable/gux-time-picker/gux-time-picker.tsx
@@ -67,6 +67,12 @@ export class GuxTimePicker {
@Prop({ mutable: true })
clockType: GuxClockType;
+ @Prop()
+ min?: string;
+
+ @Prop()
+ max?: string;
+
@State()
expanded: boolean = false;
@@ -102,6 +108,10 @@ export class GuxTimePicker {
this.i18n = await buildI18nForComponent(this.root, translationResources);
this.clockType = this.clockType || getLocaleClockType(this.root);
+
+ if (this.clockType == '12h' && (this.min || this.max)) {
+ console.error('clock type must be "24h" when using min/max props');
+ }
}
private updateValue(
@@ -343,20 +353,23 @@ export class GuxTimePicker {
}
private renderTimeListItems(): JSX.Element[] {
- return getTimeDisplayValues(this.interval, this.clockType).map(
- displayValue => {
- const value = getValue(displayValue, this.clockType, isAm(this.value));
-
- return (
- this.handleClickDropdownValue(displayValue)}
- >
- {displayValue}
-
- ) as JSX.Element;
- }
- );
+ return getTimeDisplayValues(
+ this.interval,
+ this.clockType,
+ this.min,
+ this.max
+ ).map(displayValue => {
+ const value = getValue(displayValue, this.clockType, isAm(this.value));
+
+ return (
+ this.handleClickDropdownValue(displayValue)}
+ >
+ {displayValue}
+
+ ) as JSX.Element;
+ });
}
private renderTarget(): JSX.Element {
diff --git a/packages/genesys-spark-components/src/components/stable/gux-time-picker/readme.md b/packages/genesys-spark-components/src/components/stable/gux-time-picker/readme.md
index 7f10575bff..7e4f538f17 100644
--- a/packages/genesys-spark-components/src/components/stable/gux-time-picker/readme.md
+++ b/packages/genesys-spark-components/src/components/stable/gux-time-picker/readme.md
@@ -13,6 +13,8 @@
| `disabled` | `disabled` | | `boolean` | `false` |
| `hasError` | `has-error` | | `boolean` | `false` |
| `interval` | `interval` | | `15 \| 30 \| 60` | `60` |
+| `max` | `max` | | `string` | `undefined` |
+| `min` | `min` | | `string` | `undefined` |
| `required` | `required` | | `boolean` | `false` |
| `step` | `step` | | `1 \| 10 \| 15 \| 20 \| 30 \| 5 \| 60` | `1` |
| `value` | `value` | | ``${string}:${string}`` | `'00:00'` |
diff --git a/packages/genesys-spark-components/src/components/stable/gux-time-picker/tests/gux-time-picker.service.spec.ts b/packages/genesys-spark-components/src/components/stable/gux-time-picker/tests/gux-time-picker.service.spec.ts
index 11cd55404d..eddf387d36 100644
--- a/packages/genesys-spark-components/src/components/stable/gux-time-picker/tests/gux-time-picker.service.spec.ts
+++ b/packages/genesys-spark-components/src/components/stable/gux-time-picker/tests/gux-time-picker.service.spec.ts
@@ -331,6 +331,68 @@ describe('gux-time-picker.service', () => {
);
});
+ describe('#getTimeDisplayValues with set boundaries', () => {
+ it(`should work as expected for 24h with 30 minute intervals and boundaries of 08:30-20:00`, async () => {
+ const minuteInterval = 30;
+ const clockType = '24h';
+ const min = '08:30';
+ const max = '20:00';
+ const expectedOutput = [
+ '08:30',
+ '09:00',
+ '09:30',
+ '10:00',
+ '10:30',
+ '11:00',
+ '11:30',
+ '12:00',
+ '12:30',
+ '13:00',
+ '13:30',
+ '14:00',
+ '14:30',
+ '15:00',
+ '15:30',
+ '16:00',
+ '16:30',
+ '17:00',
+ '17:30',
+ '18:00',
+ '18:30',
+ '19:00',
+ '19:30',
+ '20:00'
+ ];
+
+ expect(
+ getTimeDisplayValues(minuteInterval, clockType, min, max)
+ ).toStrictEqual(expectedOutput);
+ });
+
+ it(`should work as expected for 24h with 30 minute intervals and wrapped around boundaries of 22:00-02:30`, async () => {
+ const minuteInterval = 30;
+ const clockType = '24h';
+ const min = '22:00';
+ const max = '02:30';
+ const expectedOutput = [
+ '00:00',
+ '00:30',
+ '01:00',
+ '01:30',
+ '02:00',
+ '02:30',
+ '22:00',
+ '22:30',
+ '23:00',
+ '23:30'
+ ];
+
+ expect(
+ getTimeDisplayValues(minuteInterval, clockType, min, max)
+ ).toStrictEqual(expectedOutput);
+ });
+ });
+
describe('#getLocaleClockType', () => {
[
{ locale: 'ar' },