From 02cebf4f56e0a7cc05855e258ba6ed37175b0015 Mon Sep 17 00:00:00 2001 From: Dipanshu Gupta Date: Tue, 19 Nov 2024 17:27:33 +0530 Subject: [PATCH] 'Run every' - 'Select' dropdown greys out and cannot be selected --- .../contentSections/TriggerTypeField.tsx | 33 +++++---- .../contentSections/__tests__/utils.spec.ts | 28 +++++++ .../createRun/contentSections/utils.ts | 18 +++++ .../content/createRun/submitUtils.ts | 4 +- .../src/utilities/__tests__/string.spec.ts | 74 ------------------- frontend/src/utilities/__tests__/time.spec.ts | 7 ++ frontend/src/utilities/string.ts | 55 -------------- frontend/src/utilities/time.ts | 5 +- 8 files changed, 79 insertions(+), 145 deletions(-) create mode 100644 frontend/src/concepts/pipelines/content/createRun/contentSections/__tests__/utils.spec.ts create mode 100644 frontend/src/concepts/pipelines/content/createRun/contentSections/utils.ts diff --git a/frontend/src/concepts/pipelines/content/createRun/contentSections/TriggerTypeField.tsx b/frontend/src/concepts/pipelines/content/createRun/contentSections/TriggerTypeField.tsx index af425c2b15..f7279a19d6 100644 --- a/frontend/src/concepts/pipelines/content/createRun/contentSections/TriggerTypeField.tsx +++ b/frontend/src/concepts/pipelines/content/createRun/contentSections/TriggerTypeField.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { ClipboardCopy, FormGroup, + NumberInput, Split, SplitItem, Stack, @@ -13,12 +14,11 @@ import { RunTypeScheduledData, ScheduledType, } from '~/concepts/pipelines/content/createRun/types'; -import NumberInputWrapper from '~/components/NumberInputWrapper'; -import { replaceNonNumericPartWithString, replaceNumericPartWithString } from '~/utilities/string'; import { DEFAULT_CRON_STRING, DEFAULT_PERIODIC_OPTION, } from '~/concepts/pipelines/content/createRun/const'; +import { extractNumberAndTimeUnit } from './utils'; type TriggerTypeFieldProps = { data: RunTypeScheduledData; @@ -27,6 +27,8 @@ type TriggerTypeFieldProps = { const TriggerTypeField: React.FC = ({ data, onChange }) => { let content: React.ReactNode | null; + const [numberPart, unitPart] = extractNumberAndTimeUnit(data.value); + switch (data.triggerType) { case ScheduledType.CRON: content = ( @@ -50,15 +52,19 @@ const TriggerTypeField: React.FC = ({ data, onChange }) = - + value={numberPart} + onChange={(newNumberPart) => { + const updatedValue = `${Number(newNumberPart.currentTarget.value).toLocaleString( + 'fullwide', + { useGrouping: false }, + )}${unitPart}`; onChange({ ...data, - value: replaceNumericPartWithString(data.value, value ?? 0), - }) - } + value: updatedValue, + }); + }} /> @@ -69,13 +75,14 @@ const TriggerTypeField: React.FC = ({ data, onChange }) = key: v, label: v, }))} - value={data.value.replace(/\d+/, '')} - onChange={(value) => + value={unitPart} + onChange={(newUnitPart) => { + const updatedValue = `${numberPart}${newUnitPart}`; onChange({ ...data, - value: replaceNonNumericPartWithString(data.value, value), - }) - } + value: updatedValue, + }); + }} /> diff --git a/frontend/src/concepts/pipelines/content/createRun/contentSections/__tests__/utils.spec.ts b/frontend/src/concepts/pipelines/content/createRun/contentSections/__tests__/utils.spec.ts new file mode 100644 index 0000000000..d434fe4728 --- /dev/null +++ b/frontend/src/concepts/pipelines/content/createRun/contentSections/__tests__/utils.spec.ts @@ -0,0 +1,28 @@ +import { extractNumberAndTimeUnit } from '~/concepts/pipelines/content/createRun/contentSections/utils'; + +describe('extractNumberAndTimeUnit', () => { + test('splits valid numeric and unit parts', () => { + expect(extractNumberAndTimeUnit('1555Days')).toEqual([1555, 'Days']); + expect(extractNumberAndTimeUnit('1.23e+21Week')).toEqual([1.23e21, 'Week']); + expect(extractNumberAndTimeUnit('1.2342342342342342e+32Week')).toEqual([ + 1.2342342342342342e32, + 'Week', + ]); + }); + + test('handles missing numeric part', () => { + expect(extractNumberAndTimeUnit('Day')).toEqual([1, 'Day']); + expect(extractNumberAndTimeUnit('Minute')).toEqual([1, 'Minute']); + }); + + test('handles edge cases', () => { + expect(extractNumberAndTimeUnit('')).toEqual([1, '']); + expect(extractNumberAndTimeUnit('InfinityYear')).toEqual([1, 'InfinityYear']); + expect(extractNumberAndTimeUnit('-InfinityWeek')).toEqual([1, '-InfinityWeek']); + }); + + test('trims whitespace', () => { + expect(extractNumberAndTimeUnit(' 123Day ')).toEqual([123, 'Day']); + expect(extractNumberAndTimeUnit(' Day ')).toEqual([1, 'Day']); + }); +}); diff --git a/frontend/src/concepts/pipelines/content/createRun/contentSections/utils.ts b/frontend/src/concepts/pipelines/content/createRun/contentSections/utils.ts new file mode 100644 index 0000000000..c177f5d47e --- /dev/null +++ b/frontend/src/concepts/pipelines/content/createRun/contentSections/utils.ts @@ -0,0 +1,18 @@ +/** + * Splits a string into a numeric part and a unit part + * @param value The input string to be split. + * @returns [numberPart, unitPart] + */ +export const extractNumberAndTimeUnit = (value: string): [number, string] => { + const trimmedValue = value.trim(); + + const match = trimmedValue.match(/^([+-]?\d+(\.\d+)?([eE][+-]?\d+)?)([a-zA-Z]*)$/); + if (match) { + const numericPart = parseFloat(match[1]); + const unitPart = match[4] || ''; + return [numericPart, unitPart]; + } + + // The required minimum numeric value is set to 1 + return [1, trimmedValue]; +}; diff --git a/frontend/src/concepts/pipelines/content/createRun/submitUtils.ts b/frontend/src/concepts/pipelines/content/createRun/submitUtils.ts index 68a9d8f3c1..13d6b95338 100644 --- a/frontend/src/concepts/pipelines/content/createRun/submitUtils.ts +++ b/frontend/src/concepts/pipelines/content/createRun/submitUtils.ts @@ -82,7 +82,9 @@ const createRecurringRun = async ( periodic_schedule: formData.runType.data.triggerType === ScheduledType.PERIODIC ? { - interval_second: periodicScheduleIntervalTime.toString(), + interval_second: periodicScheduleIntervalTime.toLocaleString('fullwide', { + useGrouping: false, + }), start_time: startDate, end_time: endDate, } diff --git a/frontend/src/utilities/__tests__/string.spec.ts b/frontend/src/utilities/__tests__/string.spec.ts index aa007f52e8..566aeff48d 100644 --- a/frontend/src/utilities/__tests__/string.spec.ts +++ b/frontend/src/utilities/__tests__/string.spec.ts @@ -3,8 +3,6 @@ import { containsOnlySlashes, downloadString, removeLeadingSlash, - replaceNonNumericPartWithString, - replaceNumericPartWithString, containsMultipleSlashesPattern, triggerFileDownload, joinWithCommaAnd, @@ -33,78 +31,6 @@ describe('downloadString', () => { }); }); -describe('replaceNumericPartWithString', () => { - it('should replace the numeric part of a string with a number', () => { - expect(replaceNumericPartWithString('abc123xyz', 456)).toBe('abc456xyz'); - }); - - it('should handle empty input string', () => { - expect(replaceNumericPartWithString('', 789)).toBe('789'); - }); - - it('should handle input string without numeric part', () => { - expect(replaceNumericPartWithString('abcdef', 123)).toBe('123abcdef'); - }); - - it('should handle numeric part at the beginning of the string', () => { - expect(replaceNumericPartWithString('123xyz', 789)).toBe('789xyz'); - }); - - it('should handle numeric part at the end of the string', () => { - expect(replaceNumericPartWithString('abc456', 123)).toBe('abc123'); - }); - - it('should handle Pipeline scheduled time', () => { - expect(replaceNumericPartWithString('123Hour', 43424)).toBe('43424Hour'); - }); - - it('should handle default Pipeline scheduled time', () => { - expect(replaceNumericPartWithString('1Week', 26)).toBe('26Week'); - }); -}); - -describe('replaceNonNumericPartWithString', () => { - it('should replace the non-numeric part of a string with another string', () => { - expect(replaceNonNumericPartWithString('abc123xyz', 'XYZ')).toBe('XYZ123xyz'); - }); - - it('should handle empty input string', () => { - expect(replaceNonNumericPartWithString('', 'XYZ')).toBe('XYZ'); - }); - - it('should handle input string with no non-numeric part', () => { - expect(replaceNonNumericPartWithString('123', 'XYZ')).toBe('123XYZ'); - }); - - it('should handle input string with only non-numeric part', () => { - expect(replaceNonNumericPartWithString('abc', 'XYZ')).toBe('XYZ'); - }); - - it('should handle input string with multiple non-numeric parts', () => { - expect(replaceNonNumericPartWithString('abc123def456', 'XYZ')).toBe('XYZ123def456'); - }); - - it('should handle replacement string containing numbers', () => { - expect(replaceNonNumericPartWithString('abc123xyz', '123')).toBe('123123xyz'); - }); - - it('should handle replacement string containing special characters', () => { - expect(replaceNonNumericPartWithString('abc123xyz', '@#$')).toBe('@#$123xyz'); - }); - - it('should handle replacement string containing spaces', () => { - expect(replaceNonNumericPartWithString('abc123xyz', ' ')).toBe(' 123xyz'); - }); - - it('should handle Pipeline scheduled time', () => { - expect(replaceNonNumericPartWithString('123Week', 'Minute')).toBe('123Minute'); - }); - - it('should handle default Pipeline scheduled time', () => { - expect(replaceNonNumericPartWithString('1Week', 'Minute')).toBe('1Minute'); - }); -}); - describe('removeLeadingSlash', () => { it('removes leading slashes from a string if present', () => { expect(removeLeadingSlash('/example')).toBe('example'); diff --git a/frontend/src/utilities/__tests__/time.spec.ts b/frontend/src/utilities/__tests__/time.spec.ts index 6051a4de55..f348c79254 100644 --- a/frontend/src/utilities/__tests__/time.spec.ts +++ b/frontend/src/utilities/__tests__/time.spec.ts @@ -224,6 +224,13 @@ describe('convertPeriodicTimeToSeconds', () => { it('should default to 0 seconds for unrecognized units', () => { expect(convertPeriodicTimeToSeconds('3Weeks')).toBe(0); }); + + it('should convert exponential time to seconds', () => { + const timeString = '5.2341124234234124123e+68Hour'; + const numericValue = parseFloat('5.2341124234234124123e+68'); + const expectedSeconds = numericValue * 60 * 60; // Convert hours to seconds + expect(convertPeriodicTimeToSeconds(timeString)).toBe(expectedSeconds); + }); }); describe('convertSecondsToPeriodicTime', () => { diff --git a/frontend/src/utilities/string.ts b/frontend/src/utilities/string.ts index 793d94ad3b..252051f986 100644 --- a/frontend/src/utilities/string.ts +++ b/frontend/src/utilities/string.ts @@ -24,61 +24,6 @@ export const triggerFileDownload = (filename: string, href: string): void => { document.body.removeChild(element); }; -/** - * This function replaces the first occurrence of a numeric part in the input string - * with the specified replacement numeric value. - * @param inputString - * @param replacementString - */ -export const replaceNumericPartWithString = ( - inputString: string, - replacementString: number, -): string => { - // If the input string is empty or contains only whitespace, return the replacement as a string. - if (inputString.trim() === '') { - return replacementString.toString(); - } - - const match = inputString.match(/\d+/); //Find numeric part in string (only first occurance) - let updatedString = inputString; - - if (match) { - const matchedNumber = match[0]; - updatedString = inputString.replace(matchedNumber, String(replacementString)); - } else { - // If no numeric part is found, prepend the replacement numeric value to the input string. - updatedString = replacementString + inputString; - } - return updatedString; -}; - -/** - * This function replaces the first occurrence of a non-numeric part in the input string - * with the specified replacement string. - * @param inputString - * @param replacementString - */ -export const replaceNonNumericPartWithString = ( - inputString: string, - replacementString: string, -): string => { - if (inputString.trim() === '') { - return replacementString; - } - - const match = inputString.match(/\D+/); //Find non-numeric part in string (only first occurance) - let updatedString = inputString; - - if (match) { - const matchedString = match[0]; - updatedString = inputString.replace(matchedString, replacementString); - } else { - // If no non-numeric part is found, append the replacement non-numeric value to the input string. - updatedString = inputString + replacementString; - } - return updatedString; -}; - /** * This function removes the leading slash (/) from string if exists */ diff --git a/frontend/src/utilities/time.ts b/frontend/src/utilities/time.ts index 3b09aea014..bd5c73e2ee 100644 --- a/frontend/src/utilities/time.ts +++ b/frontend/src/utilities/time.ts @@ -182,13 +182,14 @@ export const relativeTime = (current: number, previous: number): string => { /** Function to convert time strings like "2Hour" to seconds */ export const convertPeriodicTimeToSeconds = (timeString: string): number => { - let numericValue = parseInt(timeString, 10); + const numericMatch = timeString.match(/^[\d.eE+-]+/); + let numericValue = numericMatch ? parseFloat(numericMatch[0]) : 1; if (Number.isNaN(numericValue)) { numericValue = 1; } - const unit = timeString.toLowerCase().replace(/\d+/g, ''); + const unit = timeString.replace(/^[\d.eE+-]+/, '').toLowerCase(); switch (unit) { case 'hour':