From 399f72d948dd6ddfaf62150bfa1bb8a27cefbc89 Mon Sep 17 00:00:00 2001 From: andy-commonsku Date: Thu, 16 May 2024 14:52:11 -0400 Subject: [PATCH 1/4] CORE-579: add ClearIcon to align with clear icon with react-select --- src/@commonsku/styles/DateRangeDropdown.tsx | 46 ++++++++++------ src/@commonsku/styles/icons/ClearIcon.tsx | 58 +++++++++++++++++++++ src/@commonsku/styles/icons/index.ts | 1 + src/App.tsx | 1 + 4 files changed, 90 insertions(+), 16 deletions(-) create mode 100644 src/@commonsku/styles/icons/ClearIcon.tsx diff --git a/src/@commonsku/styles/DateRangeDropdown.tsx b/src/@commonsku/styles/DateRangeDropdown.tsx index a2d8e2a9..01159611 100644 --- a/src/@commonsku/styles/DateRangeDropdown.tsx +++ b/src/@commonsku/styles/DateRangeDropdown.tsx @@ -7,7 +7,7 @@ import React, { useState, } from "react"; import { Input, InputProps } from "./Input"; -import { CalendarIcon, XIcon } from "./icons"; +import { CalendarIcon, ClearIcon } from "./icons"; import { format } from "date-fns"; import DateRangePicker, { DateRange, @@ -98,16 +98,26 @@ export const DateRangeInput = ({ autoComplete="off" {...props} /> - {((value && isClearable) || !value) && { - if (value && isClearable) { - onClear?.(); - return; - } - onInputSelect(); - }}> - {(value && isClearable) ? : null} - {!value && } - } + {((value && isClearable) || !value) && ( + { + if (value && isClearable) { + onClear?.(); + return; + } + onInputSelect(); + }} + > + {value && isClearable ? : null} + {!value && ( + + )} + + )} ); }; @@ -147,22 +157,26 @@ export const DateRangeDropdown = (props: DateRangeDropdownProps) => { const handleChange = useCallback( (range: DateRange, event?: SyntheticEvent, closeDropdown = false) => { - if (!onChange) { return; } - onChange(range, event); - closeDropdown && setOpen(false); + if (!onChange) { + return; + } + onChange(range, event); + closeDropdown && setOpen(false); }, [onChange], ); const onClear = useCallback(() => { - handleChange({category: '', endDate: null, startDate: null}); + handleChange({ category: "", endDate: null, startDate: null }); }, [handleChange]); return ( <> setOpen(true)} onClear={onClear} error={error} diff --git a/src/@commonsku/styles/icons/ClearIcon.tsx b/src/@commonsku/styles/icons/ClearIcon.tsx new file mode 100644 index 00000000..226f0442 --- /dev/null +++ b/src/@commonsku/styles/icons/ClearIcon.tsx @@ -0,0 +1,58 @@ +import React from "react"; +import SVG, { SVGIconProps } from "./SvgIcon"; +import { errors } from "../colors"; + +const iconSizes = { + tiny: { + width: 12, + height: 12, + viewBox: "0 0 12 12", + }, + small: { + width: 16, + height: 16, + viewBox: "0 0 16 16", + }, + medium: { + width: 20, + height: 20, + viewBox: "0 0 20 20", + }, + large: { + width: 32, + height: 32, + viewBox: "0 0 32 32", + }, + huge: { + width: 48, + height: 48, + viewBox: "0 0 48 48", + }, + default: { + width: 64, + height: 64, + viewBox: "0 0 64 64", + }, +}; + +export default function ClearIcon({ + color = errors["60"], + size = "medium", + altText = "clear", + ...props +}: SVGIconProps) { + return ( + + {altText} + + + ); +} diff --git a/src/@commonsku/styles/icons/index.ts b/src/@commonsku/styles/icons/index.ts index e22eb33b..28047c5d 100644 --- a/src/@commonsku/styles/icons/index.ts +++ b/src/@commonsku/styles/icons/index.ts @@ -116,3 +116,4 @@ export { default as CommentBubbleIcon} from './CommentBubbleIcon'; export { default as WarnIcon} from './WarnIcon'; export { default as LayersIcon } from './LayersIcon'; +export { default as ClearIcon } from './ClearIcon'; diff --git a/src/App.tsx b/src/App.tsx index 980d58fe..501fba30 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5763,6 +5763,7 @@ const App = () => { name="SubtractIcon" /> ]} name="XIcon" /> + ]} name="ClearIcon" /> ]} name="SearchIcon" From 61db9837a80dfe42f24af84980cb450e3ab98f0d Mon Sep 17 00:00:00 2001 From: andy-commonsku Date: Thu, 16 May 2024 16:41:42 -0400 Subject: [PATCH 2/4] CORE-579: modify styles for clear icon in date range dropdown --- src/@commonsku/styles/DateRangeDropdown.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/@commonsku/styles/DateRangeDropdown.tsx b/src/@commonsku/styles/DateRangeDropdown.tsx index 01159611..9549ae9f 100644 --- a/src/@commonsku/styles/DateRangeDropdown.tsx +++ b/src/@commonsku/styles/DateRangeDropdown.tsx @@ -109,7 +109,9 @@ export const DateRangeInput = ({ onInputSelect(); }} > - {value && isClearable ? : null} + {value && isClearable ? ( + + ) : null} {!value && ( Date: Fri, 17 May 2024 12:50:29 -0400 Subject: [PATCH 3/4] CORE-579: check invalid date --- src/@commonsku/styles/DateRangeDropdown.tsx | 2 +- src/@commonsku/styles/DateRangePicker.tsx | 452 +++++++++++--------- 2 files changed, 248 insertions(+), 206 deletions(-) diff --git a/src/@commonsku/styles/DateRangeDropdown.tsx b/src/@commonsku/styles/DateRangeDropdown.tsx index 9549ae9f..e66d9b6d 100644 --- a/src/@commonsku/styles/DateRangeDropdown.tsx +++ b/src/@commonsku/styles/DateRangeDropdown.tsx @@ -110,7 +110,7 @@ export const DateRangeInput = ({ }} > {value && isClearable ? ( - + ) : null} {!value && ( & { - range: DateRange - dateFormat?: string - onChange?: ( - range: DateRange, - event?: SyntheticEvent, - closeDropdown?: boolean - ) => void - presets?: DateRangePreset[], - initialActiveTab?: 'custom' | 'preset' -} +export type DateRangePickerProps = Omit< + DatepickerProps, + "value" | "onChange" | "dateFormat" +> & { + range: DateRange; + dateFormat?: string; + onChange?: ( + range: DateRange, + event?: SyntheticEvent, + closeDropdown?: boolean, + ) => void; + presets?: DateRangePreset[]; + initialActiveTab?: "custom" | "preset"; +}; + +export const checkDateYear = (date: Date | null | undefined): boolean => { + if (!date || !isValid(date)) { + return false; + } + return date.getFullYear() > 1900 && date.getFullYear() < 3000; +}; -export const DateRangePicker = forwardRef(( +export const DateRangePicker = forwardRef( + ( { - range, - onChange, - presets, - error, - customInput, - locale='en', - todayButton='Today', - placeholder, - placeholderText, - dateFormat='yyyy-MM-dd', - isClearable=true, - peekNextMonth=true, - showMonthDropdown=false, - showYearDropdown=false, - showPopperArrow=false, - dropdownMode="select", - nextMonthButtonLabel="", - nextYearButtonLabel="", - previousMonthButtonLabel="", - previousYearButtonLabel="", - initialActiveTab = 'preset' , - popperClassName, - wrapperClassName, - style, - ...props + range, + onChange, + presets, + error, + customInput, + locale = "en", + todayButton = "Today", + placeholder, + placeholderText, + dateFormat = "yyyy-MM-dd", + isClearable = true, + peekNextMonth = true, + showMonthDropdown = false, + showYearDropdown = false, + showPopperArrow = false, + dropdownMode = "select", + nextMonthButtonLabel = "", + nextYearButtonLabel = "", + previousMonthButtonLabel = "", + previousYearButtonLabel = "", + initialActiveTab = "preset", + popperClassName, + wrapperClassName, + style, + ...props }, - ref -) => { + ref, + ) => { const { startDate, endDate } = range; const [selectedPreset, setSelectedPreset] = useState< DateRangePreset | undefined @@ -119,155 +137,179 @@ export const DateRangePicker = forwardRef( const hasPresets = presets != null && presets.length > 0; const handleChange = useCallback( - (selected: 'start' | 'end', newStart?: Date | null, newEnd?: Date | null, event?: SyntheticEvent) => { - if (newStart != null && newEnd != null && newStart > newEnd) { - if (selected === 'start') { - newEnd = newStart; - } else { - newStart = newEnd; - } - } - - // Forces the calendars to re-render on date change - setStartDateKey(startDateKey + 1); - setEndDateKey(endDateKey + 1); + ( + selected: "start" | "end", + newStart?: Date | null, + newEnd?: Date | null, + event?: SyntheticEvent, + ) => { + if (newStart != null && newEnd != null && newStart > newEnd) { + if (selected === "start") { + newEnd = newStart; + } else { + newStart = newEnd; + } + } - if (onChange != null) { - const newDateRange: DateRange = { - category: "custom", - startDate: newStart, - endDate: newEnd, - }; + // Forces the calendars to re-render on date change + setStartDateKey(startDateKey + 1); + setEndDateKey(endDateKey + 1); - onChange(newDateRange, event, newStart != null && newEnd != null); - } - }, - [endDateKey, onChange, startDateKey] + if (onChange != null) { + const newDateRange: DateRange = { + category: "custom", + startDate: newStart, + endDate: newEnd, + }; + onChange(newDateRange, event, newStart != null && newEnd != null); + } + }, + [endDateKey, onChange, startDateKey], ); - const handleSelectPreset = useCallback((preset: DateRangePreset) => { + const handleSelectPreset = useCallback( + (preset: DateRangePreset) => { setSelectedPreset(preset); if (onChange != null) { - onChange({ category: preset.name }, undefined, true); + onChange({ category: preset.name }, undefined, true); } - }, [onChange, setSelectedPreset]); + }, + [onChange, setSelectedPreset], + ); const renderCustomTab = () => ( - - -
- From -
-
- handleChange('start', newStart, endDate, event)} - /> - handleChange('start', newStart, endDate, event)} - {...props} - /> -
- - -
- To -
-
- handleChange('end', startDate, newEnd, event)} - /> - handleChange('end', startDate, newEnd, event)} - {...props} - /> -
- -
+ + +
From
+
+ { + if (!checkDateYear(newStart)) return; + handleChange("start", newStart, endDate, event); + }} + /> + + handleChange("start", newStart, endDate, event) + } + {...props} + /> +
+ + +
To
+
+ { + if (!checkDateYear(newEnd)) return; + handleChange("end", startDate, newEnd, event); + }} + /> + + handleChange("end", startDate, newEnd, event) + } + {...props} + /> +
+ +
); const renderPresetTab = () => ( -
- {presets?.map((preset, idx) => ( - handleSelectPreset(preset)} - checked={selectedPreset != null && preset.label === selectedPreset.label} - /> - ))} -
+
+ {presets?.map((preset, idx) => ( + handleSelectPreset(preset)} + checked={ + selectedPreset != null && preset.label === selectedPreset.label + } + /> + ))} +
); return ( -
- {hasPresets && - - setActiveTab('preset')} - > - Preset - - setActiveTab('custom')} - > - Custom - - - } - {activeTab === 'custom' ? renderCustomTab() : renderPresetTab()} -
+
+ {hasPresets && ( + + setActiveTab("preset")} + > + Preset + + setActiveTab("custom")} + > + Custom + + + )} + {activeTab === "custom" ? renderCustomTab() : renderPresetTab()} +
); -}); + }, +); export default DateRangePicker; From 60c567d7a3a1dae384ff369dfd2e9b444370fbe6 Mon Sep 17 00:00:00 2001 From: andy-commonsku Date: Fri, 24 May 2024 13:23:25 -0400 Subject: [PATCH 4/4] CORE-579: remove unnecessary export --- src/@commonsku/styles/DateRangePicker.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/@commonsku/styles/DateRangePicker.tsx b/src/@commonsku/styles/DateRangePicker.tsx index 445dc53d..909ccaa0 100644 --- a/src/@commonsku/styles/DateRangePicker.tsx +++ b/src/@commonsku/styles/DateRangePicker.tsx @@ -75,7 +75,7 @@ export type DateRangePickerProps = Omit< initialActiveTab?: "custom" | "preset"; }; -export const checkDateYear = (date: Date | null | undefined): boolean => { +const checkDateYear = (date: Date | null | undefined): boolean => { if (!date || !isValid(date)) { return false; }