From b08fef02f8cc2f7cb19baeacb4bb528ce36fcc7d Mon Sep 17 00:00:00 2001 From: Jialecl Date: Wed, 6 May 2026 09:48:29 +0200 Subject: [PATCH 1/4] Time picker now preselects values when any value is selected --- .../lib/src/time-input/TimeInput.stories.tsx | 47 +++++--------- .../lib/src/time-input/TimeInput.test.tsx | 14 ++--- packages/lib/src/time-input/TimeInput.tsx | 51 ++++----------- packages/lib/src/time-input/TimePicker.tsx | 63 ++++++++++++------- packages/lib/src/time-input/types.ts | 5 +- 5 files changed, 77 insertions(+), 103 deletions(-) diff --git a/packages/lib/src/time-input/TimeInput.stories.tsx b/packages/lib/src/time-input/TimeInput.stories.tsx index 6a295aa2c..9a8632bda 100644 --- a/packages/lib/src/time-input/TimeInput.stories.tsx +++ b/packages/lib/src/time-input/TimeInput.stories.tsx @@ -119,19 +119,10 @@ const TimePickerExamples = () => { <DxcContainer width="250px"> - <TimePicker onSelectMinutes={() => {}} onSelectHours={() => {}} timeFormat="24" id="testId" tabIndex={0} /> + <TimePicker onPickTime={() => {}} timeFormat="24" id="testId" tabIndex={0} /> + <TimePicker onPickTime={() => {}} timeFormat="24" id="testId" tabIndex={0} hourValue={15} minuteValue={30} /> <TimePicker - onSelectMinutes={() => {}} - onSelectHours={() => {}} - timeFormat="24" - id="testId" - tabIndex={0} - hourValue={15} - minuteValue={30} - /> - <TimePicker - onSelectMinutes={() => {}} - onSelectHours={() => {}} + onPickTime={() => {}} timeFormat="24" id="testId" tabIndex={0} @@ -145,10 +136,9 @@ const TimePickerExamples = () => { <ExampleContainer> <Title title="Time Picker 12h format" theme="light" level={3} /> <DxcContainer width="250px"> - <TimePicker onSelectMinutes={() => {}} onSelectHours={() => {}} timeFormat="12" id="testId" tabIndex={0} /> + <TimePicker onPickTime={() => {}} timeFormat="12" id="testId" tabIndex={0} /> <TimePicker - onSelectMinutes={() => {}} - onSelectHours={() => {}} + onPickTime={() => {}} timeFormat="12" id="testId" tabIndex={0} @@ -157,8 +147,7 @@ const TimePickerExamples = () => { dayPeriod={1} /> <TimePicker - onSelectMinutes={() => {}} - onSelectHours={() => {}} + onPickTime={() => {}} timeFormat="12" id="testId" tabIndex={0} @@ -173,10 +162,9 @@ const TimePickerExamples = () => { <ExampleContainer pseudoState={"pseudo-hover"}> <Title title="hover" theme="light" level={3} /> <DxcContainer width="250px"> - <TimePicker onSelectMinutes={() => {}} onSelectHours={() => {}} timeFormat="12" id="testId" tabIndex={0} /> + <TimePicker onPickTime={() => {}} timeFormat="12" id="testId" tabIndex={0} /> <TimePicker - onSelectMinutes={() => {}} - onSelectHours={() => {}} + onPickTime={() => {}} timeFormat="12" id="testId" tabIndex={0} @@ -185,8 +173,7 @@ const TimePickerExamples = () => { dayPeriod={1} /> <TimePicker - onSelectMinutes={() => {}} - onSelectHours={() => {}} + onPickTime={() => {}} timeFormat="12" id="testId" tabIndex={0} @@ -201,10 +188,9 @@ const TimePickerExamples = () => { <ExampleContainer pseudoState={"pseudo-focus"}> <Title title="focus" theme="light" level={3} /> <DxcContainer width="250px"> - <TimePicker onSelectMinutes={() => {}} onSelectHours={() => {}} timeFormat="12" id="testId" tabIndex={0} /> + <TimePicker onPickTime={() => {}} timeFormat="12" id="testId" tabIndex={0} /> <TimePicker - onSelectMinutes={() => {}} - onSelectHours={() => {}} + onPickTime={() => {}} timeFormat="12" id="testId" tabIndex={0} @@ -213,8 +199,7 @@ const TimePickerExamples = () => { dayPeriod={1} /> <TimePicker - onSelectMinutes={() => {}} - onSelectHours={() => {}} + onPickTime={() => {}} timeFormat="12" id="testId" tabIndex={0} @@ -229,10 +214,9 @@ const TimePickerExamples = () => { <ExampleContainer pseudoState={"pseudo-active"}> <Title title="active" theme="light" level={3} /> <DxcContainer width="250px"> - <TimePicker onSelectMinutes={() => {}} onSelectHours={() => {}} timeFormat="12" id="testId" tabIndex={0} /> + <TimePicker onPickTime={() => {}} timeFormat="12" id="testId" tabIndex={0} /> <TimePicker - onSelectMinutes={() => {}} - onSelectHours={() => {}} + onPickTime={() => {}} timeFormat="12" id="testId" tabIndex={0} @@ -241,8 +225,7 @@ const TimePickerExamples = () => { dayPeriod={1} /> <TimePicker - onSelectMinutes={() => {}} - onSelectHours={() => {}} + onPickTime={() => {}} timeFormat="12" id="testId" tabIndex={0} diff --git a/packages/lib/src/time-input/TimeInput.test.tsx b/packages/lib/src/time-input/TimeInput.test.tsx index 64c3569f5..b1c9451ca 100644 --- a/packages/lib/src/time-input/TimeInput.test.tsx +++ b/packages/lib/src/time-input/TimeInput.test.tsx @@ -110,13 +110,13 @@ describe("DxcTimeInput rendering", () => { expect(getByText("AM")).toBeTruthy(); const hourbutton = getAllByText("07"); if (hourbutton[0]) userEvent.click(hourbutton[0]); - expect(mockOnChange).toHaveBeenCalledWith("07: "); + expect(mockOnChange).toHaveBeenCalledWith("07:00 AM"); const minuteButton = getAllByText("30"); if (minuteButton[0]) userEvent.click(minuteButton[0]); - expect(mockOnChange).toHaveBeenCalledWith("07:30 "); - const amButton = getByText("AM"); + expect(mockOnChange).toHaveBeenCalledWith("07:30 AM"); + const amButton = getAllByText("AM")[0]; expect(amButton).toBeTruthy(); - userEvent.click(amButton); + if (amButton) userEvent.click(amButton); expect(mockOnChange).toHaveBeenCalledWith("07:30 AM"); }); @@ -130,14 +130,14 @@ describe("DxcTimeInput rendering", () => { userEvent.keyboard("{ArrowDown}"); userEvent.keyboard("{ArrowDown}"); userEvent.keyboard("{Enter}"); - expect(mockOnChange).toHaveBeenCalledWith("03: "); + expect(mockOnChange).toHaveBeenCalledWith("03:00 AM"); userEvent.tab(); userEvent.keyboard("{ArrowUp}"); userEvent.keyboard("{Enter}"); - expect(mockOnChange).toHaveBeenCalledWith("03:55 "); + expect(mockOnChange).toHaveBeenCalledWith("03:55 AM"); userEvent.keyboard("{ArrowDown}"); userEvent.keyboard(" "); - expect(mockOnChange).toHaveBeenCalledWith("03:00 "); + expect(mockOnChange).toHaveBeenCalledWith("03:00 AM"); userEvent.tab(); userEvent.keyboard("{ArrowDown}"); userEvent.keyboard("{Enter}"); diff --git a/packages/lib/src/time-input/TimeInput.tsx b/packages/lib/src/time-input/TimeInput.tsx index 01af3b9aa..33602ccb3 100644 --- a/packages/lib/src/time-input/TimeInput.tsx +++ b/packages/lib/src/time-input/TimeInput.tsx @@ -52,6 +52,8 @@ const ColonContainer = styled.span` padding: 0; color: var(--color-fg-neutral-dark); `; +// number can be 2 digit or 1 digit +const isNumber = (value: string) => /^\d{1,2}$/.test(value); const DxcTimeInput = forwardRef<RefType, TimeInputPropsType>( ( @@ -95,10 +97,10 @@ const DxcTimeInput = forwardRef<RefType, TimeInputPropsType>( if (time) { const numberPart = timeFormat === "12" ? time.split(" ")[0] : time; if (numberPart) { - const [hour, minute, second] = numberPart.split(":").map(Number); - setHourValue(hour || hour === 0 ? hour : undefined); - setMinuteValue(minute || minute === 0 ? minute : undefined); - setSecondValue(second || second === 0 ? second : undefined); + const [hourStr, minuteStr, secondStr] = numberPart.split(":").map(String); + setHourValue(hourStr && isNumber(hourStr) ? Number(hourStr) : undefined); + setMinuteValue(minuteStr && isNumber(minuteStr) ? Number(minuteStr) : undefined); + setSecondValue(secondStr && isNumber(secondStr) ? Number(secondStr) : undefined); } if (timeFormat === "12" && time.includes(" ")) { const dayPeriodValue = time.split(" ")[1] === "AM" ? 0 : time.split(" ")[1] === "PM" ? 1 : undefined; @@ -343,44 +345,15 @@ const DxcTimeInput = forwardRef<RefType, TimeInputPropsType>( <DxcPopover popoverContent={ <TimePicker - onSelectHours={(value) => { + onPickTime={(hour, minute, second, dayPeriod) => { if (!isControlled) { - setHourValue(value); + setDayPeriodValue(dayPeriod); + setSecondValue(second); + setMinuteValue(minute); + setHourValue(hour); } if (typeof onChange === "function") { - onChange( - generateEventValue(value, minuteValue, secondValue, dayPeriodValue, showSeconds, timeFormat) - ); - } - }} - onSelectMinutes={(value) => { - if (!isControlled) { - setMinuteValue(value); - } - if (typeof onChange === "function") { - onChange( - generateEventValue(hourValue, value, secondValue, dayPeriodValue, showSeconds, timeFormat) - ); - } - }} - onSelectSeconds={(value) => { - if (!isControlled) { - setSecondValue(value); - } - if (typeof onChange === "function") { - onChange( - generateEventValue(hourValue, minuteValue, value, dayPeriodValue, showSeconds, timeFormat) - ); - } - }} - onSelectDayPeriod={(value: number) => { - if (!isControlled) { - setDayPeriodValue(value); - } - if (typeof onChange === "function") { - onChange( - generateEventValue(hourValue, minuteValue, secondValue, value, showSeconds, timeFormat) - ); + onChange(generateEventValue(hour, minute, second, dayPeriod, showSeconds, timeFormat)); } }} timeFormat={timeFormat} diff --git a/packages/lib/src/time-input/TimePicker.tsx b/packages/lib/src/time-input/TimePicker.tsx index 07f5f7fcf..e66e370ff 100644 --- a/packages/lib/src/time-input/TimePicker.tsx +++ b/packages/lib/src/time-input/TimePicker.tsx @@ -3,7 +3,6 @@ import { TimePickerPropsType } from "./types"; import { useEffect, useState } from "react"; import TimePickerColumn from "./TimePickerColumn"; import { handleColumnKeyDown } from "./utils"; - // Array to be used in seconds and minutes. const STEP = 5; const ARRAY_OF_60 = Array.from({ length: 60 / STEP }, (_, index) => index * STEP); @@ -15,10 +14,7 @@ const TimePickerContainer = styled.div` `; const TimePicker = ({ - onSelectHours, - onSelectMinutes, - onSelectSeconds, - onSelectDayPeriod, + onPickTime, timeFormat, showSeconds, hourValue, @@ -34,6 +30,19 @@ const TimePicker = ({ const [dayPeriodToFocus, setDayPeriodToFocus] = useState(dayPeriod || 0); const totalHours = timeFormat === "12" ? 12 : 24; + const onPickerSelect = (value: number, type: "hour" | "minute" | "second" | "dayPeriod") => { + const hourVal = type === "hour" ? value : hourValue || 0; + const minuteVal = type === "minute" ? value : minuteValue || 0; + const secondVal = type === "second" ? value : secondValue || 0; + const dayPeriodVal = type === "dayPeriod" ? value : dayPeriod || 0; + + setDayPeriodToFocus(dayPeriodVal); + setSecondToFocus(secondVal); + setMinuteToFocus(minuteVal); + setHourToFocus(hourVal); + onPickTime(hourVal, minuteVal, secondVal, dayPeriodVal); + }; + useEffect(() => { if (dayPeriodToFocus !== undefined && id) { document.getElementById(`${id}-dayPeriod-${dayPeriodToFocus}`)?.focus(); @@ -65,11 +74,12 @@ const TimePicker = ({ tabIndex={tabIndex} dataType="hour" onClick={(value: number) => { - onSelectHours(value); - setHourToFocus(value); + onPickerSelect(value, "hour"); }} onKeyboardEvent={(event: React.KeyboardEvent, value: number) => - handleColumnKeyDown(event, "hour", value, totalHours, setHourToFocus, onSelectHours) + handleColumnKeyDown(event, "hour", value, totalHours, setHourToFocus, (value) => + onPickerSelect(value, "hour") + ) } /> <TimePickerColumn @@ -80,11 +90,18 @@ const TimePicker = ({ tabIndex={tabIndex} dataType="minute" onClick={(value: number) => { - onSelectMinutes(value); - setMinuteToFocus(value); + onPickerSelect(value, "minute"); }} onKeyboardEvent={(event: React.KeyboardEvent, value: number) => - handleColumnKeyDown(event, "minute", value, 60, setMinuteToFocus, onSelectMinutes, STEP) + handleColumnKeyDown( + event, + "minute", + value, + 60, + setMinuteToFocus, + (value) => onPickerSelect(value, "minute"), + STEP + ) } /> {showSeconds && ( @@ -96,13 +113,18 @@ const TimePicker = ({ tabIndex={tabIndex} dataType="second" onClick={(value: number) => { - if (typeof onSelectSeconds === "function") { - onSelectSeconds(value); - setSecondToFocus(value); - } + onPickerSelect(value, "second"); }} onKeyboardEvent={(event: React.KeyboardEvent, value: number) => - handleColumnKeyDown(event, "second", value, 60, setSecondToFocus, onSelectSeconds, STEP) + handleColumnKeyDown( + event, + "second", + value, + 60, + setSecondToFocus, + (value) => onPickerSelect(value, "second"), + STEP + ) } /> )} @@ -115,13 +137,12 @@ const TimePicker = ({ tabIndex={tabIndex} dataType="dayPeriod" onClick={(value: number) => { - if (typeof onSelectDayPeriod === "function") { - onSelectDayPeriod(value); - setDayPeriodToFocus(value); - } + onPickerSelect(value, "dayPeriod"); }} onKeyboardEvent={(event: React.KeyboardEvent, value: number) => - handleColumnKeyDown(event, "dayPeriod", value, 2, setDayPeriodToFocus, onSelectDayPeriod) + handleColumnKeyDown(event, "dayPeriod", value, 2, setDayPeriodToFocus, (value) => + onPickerSelect(value, "dayPeriod") + ) } /> )} diff --git a/packages/lib/src/time-input/types.ts b/packages/lib/src/time-input/types.ts index 1ec59e7a8..e10e787af 100644 --- a/packages/lib/src/time-input/types.ts +++ b/packages/lib/src/time-input/types.ts @@ -104,10 +104,7 @@ export type TimeSpinButtonPropsType = { }; export type TimePickerPropsType = { - onSelectHours: (hours: number) => void; - onSelectMinutes: (minutes: number) => void; - onSelectSeconds?: (seconds: number) => void; - onSelectDayPeriod?: (isPM: number) => void; + onPickTime: (hours: number, minutes: number, seconds?: number, dayPeriod?: number) => void; timeFormat: "12" | "24"; showSeconds?: boolean; hourValue?: number; From 29ba7aedf26857bbd4642b74534885d6fa288a48 Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Wed, 6 May 2026 09:56:46 +0200 Subject: [PATCH 2/4] taking into account the time format --- packages/lib/src/time-input/TimePicker.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib/src/time-input/TimePicker.tsx b/packages/lib/src/time-input/TimePicker.tsx index e66e370ff..c87e7b338 100644 --- a/packages/lib/src/time-input/TimePicker.tsx +++ b/packages/lib/src/time-input/TimePicker.tsx @@ -31,7 +31,7 @@ const TimePicker = ({ const totalHours = timeFormat === "12" ? 12 : 24; const onPickerSelect = (value: number, type: "hour" | "minute" | "second" | "dayPeriod") => { - const hourVal = type === "hour" ? value : hourValue || 0; + const hourVal = type === "hour" ? value : hourValue || (timeFormat === "12" ? 1 : 0); const minuteVal = type === "minute" ? value : minuteValue || 0; const secondVal = type === "second" ? value : secondValue || 0; const dayPeriodVal = type === "dayPeriod" ? value : dayPeriod || 0; From 163ab9a761f52e67e20256e8095066b99ab08c63 Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Wed, 6 May 2026 11:41:07 +0200 Subject: [PATCH 3/4] Taking into account that 0 is also a valid value Co-authored-by: Copilot <copilot@github.com> --- packages/lib/src/time-input/TimeInput.tsx | 4 ++-- packages/lib/src/time-input/TimePicker.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/lib/src/time-input/TimeInput.tsx b/packages/lib/src/time-input/TimeInput.tsx index 33602ccb3..d18a4310f 100644 --- a/packages/lib/src/time-input/TimeInput.tsx +++ b/packages/lib/src/time-input/TimeInput.tsx @@ -52,7 +52,7 @@ const ColonContainer = styled.span` padding: 0; color: var(--color-fg-neutral-dark); `; -// number can be 2 digit or 1 digit + const isNumber = (value: string) => /^\d{1,2}$/.test(value); const DxcTimeInput = forwardRef<RefType, TimeInputPropsType>( @@ -97,7 +97,7 @@ const DxcTimeInput = forwardRef<RefType, TimeInputPropsType>( if (time) { const numberPart = timeFormat === "12" ? time.split(" ")[0] : time; if (numberPart) { - const [hourStr, minuteStr, secondStr] = numberPart.split(":").map(String); + const [hourStr, minuteStr, secondStr] = numberPart.split(":"); setHourValue(hourStr && isNumber(hourStr) ? Number(hourStr) : undefined); setMinuteValue(minuteStr && isNumber(minuteStr) ? Number(minuteStr) : undefined); setSecondValue(secondStr && isNumber(secondStr) ? Number(secondStr) : undefined); diff --git a/packages/lib/src/time-input/TimePicker.tsx b/packages/lib/src/time-input/TimePicker.tsx index c87e7b338..96a0b2475 100644 --- a/packages/lib/src/time-input/TimePicker.tsx +++ b/packages/lib/src/time-input/TimePicker.tsx @@ -24,14 +24,14 @@ const TimePicker = ({ id, tabIndex = 0, }: TimePickerPropsType) => { - const [hourToFocus, setHourToFocus] = useState(hourValue || 1); + const [hourToFocus, setHourToFocus] = useState(hourValue || hourValue === 0 ? hourValue : 1); const [minuteToFocus, setMinuteToFocus] = useState(minuteValue || 0); const [secondToFocus, setSecondToFocus] = useState(secondValue || 0); const [dayPeriodToFocus, setDayPeriodToFocus] = useState(dayPeriod || 0); const totalHours = timeFormat === "12" ? 12 : 24; const onPickerSelect = (value: number, type: "hour" | "minute" | "second" | "dayPeriod") => { - const hourVal = type === "hour" ? value : hourValue || (timeFormat === "12" ? 1 : 0); + const hourVal = type === "hour" ? value : hourValue || hourValue === 0 ? hourValue : timeFormat === "12" ? 1 : 0; const minuteVal = type === "minute" ? value : minuteValue || 0; const secondVal = type === "second" ? value : secondValue || 0; const dayPeriodVal = type === "dayPeriod" ? value : dayPeriod || 0; From 3c5206500763343d47200f4002d5b75fbaf7f37a Mon Sep 17 00:00:00 2001 From: Jialecl <jialestrabajos@gmail.com> Date: Wed, 6 May 2026 13:29:24 +0200 Subject: [PATCH 4/4] Changing conditional to make it shorter --- packages/lib/src/time-input/TimePicker.tsx | 16 ++++++++-------- packages/lib/src/time-input/utils.ts | 1 - 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/lib/src/time-input/TimePicker.tsx b/packages/lib/src/time-input/TimePicker.tsx index 96a0b2475..5d43d71b8 100644 --- a/packages/lib/src/time-input/TimePicker.tsx +++ b/packages/lib/src/time-input/TimePicker.tsx @@ -24,17 +24,17 @@ const TimePicker = ({ id, tabIndex = 0, }: TimePickerPropsType) => { - const [hourToFocus, setHourToFocus] = useState(hourValue || hourValue === 0 ? hourValue : 1); - const [minuteToFocus, setMinuteToFocus] = useState(minuteValue || 0); - const [secondToFocus, setSecondToFocus] = useState(secondValue || 0); - const [dayPeriodToFocus, setDayPeriodToFocus] = useState(dayPeriod || 0); + const [hourToFocus, setHourToFocus] = useState(hourValue ?? (timeFormat === "12" ? 1 : 0)); + const [minuteToFocus, setMinuteToFocus] = useState(minuteValue ?? 0); + const [secondToFocus, setSecondToFocus] = useState(secondValue ?? 0); + const [dayPeriodToFocus, setDayPeriodToFocus] = useState(dayPeriod ?? 0); const totalHours = timeFormat === "12" ? 12 : 24; const onPickerSelect = (value: number, type: "hour" | "minute" | "second" | "dayPeriod") => { - const hourVal = type === "hour" ? value : hourValue || hourValue === 0 ? hourValue : timeFormat === "12" ? 1 : 0; - const minuteVal = type === "minute" ? value : minuteValue || 0; - const secondVal = type === "second" ? value : secondValue || 0; - const dayPeriodVal = type === "dayPeriod" ? value : dayPeriod || 0; + const hourVal = type === "hour" ? value : (hourValue ?? (timeFormat === "12" ? 1 : 0)); + const minuteVal = type === "minute" ? value : (minuteValue ?? 0); + const secondVal = type === "second" ? value : (secondValue ?? 0); + const dayPeriodVal = type === "dayPeriod" ? value : (dayPeriod ?? 0); setDayPeriodToFocus(dayPeriodVal); setSecondToFocus(secondVal); diff --git a/packages/lib/src/time-input/utils.ts b/packages/lib/src/time-input/utils.ts index 7a23e110f..def5a10bf 100644 --- a/packages/lib/src/time-input/utils.ts +++ b/packages/lib/src/time-input/utils.ts @@ -69,7 +69,6 @@ export const handleKeyDown = ( rawInput.current = (rawInput.current + newDigit.current).slice(-maxValue.toString().length); newValue = resolveValue(rawInput.current, maxValue, minValue); // If the raw input has reached the max length or exceeds the max value with the new digit, consider it complete and move to the next field. - console.log("rawInput:", rawInput.current, "newDigit:", newDigit.current, "newValue:", newValue); if (checkCompletion(rawInput.current, maxValue)) { rawInput.current = pad(newValue); if (typeof onComplete === "function") {