diff --git a/package-lock.json b/package-lock.json index a4337493..47310111 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@nordcloud/gnui", - "version": "11.3.0", + "version": "11.6.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@nordcloud/gnui", - "version": "11.3.0", + "version": "11.6.0", "license": "MIT", "dependencies": { "@types/styled-system": "^5.1.22", diff --git a/package.json b/package.json index 93ff276b..99dfe835 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@nordcloud/gnui", "description": "Nordcloud Design System - a collection of reusable React components used in Nordcloud's SaaS products", - "version": "11.5.3", + "version": "11.6.0", "license": "MIT", "repository": { "type": "git", diff --git a/rollup.config.js b/rollup.config.js index ff8bc342..a2ce646c 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -23,6 +23,7 @@ export default { ], plugins: [ typescript({ + include: ["src/**/*.{ts,tsx}"], useTsconfigDeclarationDir: true, tsconfigOverride: { exclude: [ diff --git a/src/components/timeRangePicker/TimeRangePicker.mdx b/src/components/timeRangePicker/TimeRangePicker.mdx index 7ad3b49f..22a83d39 100644 --- a/src/components/timeRangePicker/TimeRangePicker.mdx +++ b/src/components/timeRangePicker/TimeRangePicker.mdx @@ -9,15 +9,16 @@ import * as TimeRangePickerStories from "./TimeRangePicker.stories"; ## TimeRangePicker properties -| properties | required | type | description | -| ------------: | :------: | :-------------------------------------------- | :-------------------------------------------------------------------------------------------------------- | -| initTimeRange | false | Interval | Start date and end date should be within the same day, current date will be used otherwise and by default | -| type | false | Days, Hours | Type of time selected in component | -| weekCounts | false | DailyCount\[] | Item counts in each time range (Type "Hours" only) | -| countsLoading | false | boolean | Loading state of week counts (Type "Hours" only) | -| disabledDays | false | Matcher\[] | Dates not allowed to select | -| onChange | true | (newTimeRange: Interval) => void | Function to handle timeRange change | -| onWeekChange | false | (monday: Date) => void | Function to submit when showing week updates (Type "Hours" only) | +| properties | required | type | description | +| ------------------: | :------: | :-------------------------------------------- | :-------------------------------------------------------------------------------------------------------- | +| initTimeRange | false | Interval | Start date and end date should be within the same day, current date will be used otherwise and by default | +| type | false | Days, Hours | Type of time selected in component | +| weekCounts | false | DailyCount\[] | Item counts in each time range (Type "Hours" only) | +| countsLoading | false | boolean | Loading state of week counts (Type "Hours" only) | +| disabledDays | false | Matcher\[] | Dates not allowed to select | +| keepSelectedWeekday | false | boolean | Whether to preserve week day selection when changing week (Type "Hours" only) | +| onChange | true | (newTimeRange: Interval) => void | Function to handle timeRange change | +| onWeekChange | false | (monday: Date) => void | Function to submit when showing week updates (Type "Hours" only) | ```typescript import { Interval } from "date-fns"; @@ -49,6 +50,12 @@ type Matcher = (boolean | (date: Date)) => boolean | Date | Date[] | DateRange | +## Type "Hours" with preserved day selection + + + + + ## Type "Hours" with Histogram diff --git a/src/components/timeRangePicker/TimeRangePicker.stories.tsx b/src/components/timeRangePicker/TimeRangePicker.stories.tsx index 7b8db367..fb0a593f 100644 --- a/src/components/timeRangePicker/TimeRangePicker.stories.tsx +++ b/src/components/timeRangePicker/TimeRangePicker.stories.tsx @@ -1,113 +1,133 @@ import { useState } from "react"; import { Meta, StoryObj } from "@storybook/react"; -import { previousMonday, addDays } from "date-fns"; +import { addDays, previousMonday } from "date-fns"; import { Spacer } from "../spacer"; import { TimeRangePicker } from "./TimeRangePicker"; -const meta: Meta = { +const meta: Meta = { title: "Forms/TimeRangePicker", component: TimeRangePicker, }; export default meta; -export const TimerangepickerOfDays: StoryObj = { - render: () => { - const initValue = { - start: new Date(), - end: new Date(), - }; - - const [value, setValue] = useState<{ - start: Date | number; - end: Date | number; - }>(initValue); - - return ( - <> - setValue(newValue)} - /> - -
Current Value:{JSON.stringify(value)}
- - ); - }, +type TimeRangeValue = { + start: Date | number; + end: Date | number; +}; + +function TimerangepickerOfDaysComponent() { + const initValue = { + start: new Date(), + end: new Date(), + }; + + const [value, setValue] = useState(initValue); + return ( + <> + setValue(newValue)} + /> + +
Current Value:{JSON.stringify(value)}
+ + ); +} + +export const TimerangepickerOfDays: StoryObj = { + render: () => , name: "timerangepicker of days", }; -export const TimerangepickerOfHours: StoryObj = { - render: () => { - const [value, setValue] = useState<{ - start: Date | number; - end: Date | number; - }>(); - - return ( - <> - - -
Current Value:{JSON.stringify(value)}
- - ); - }, +function TimerangepickerOfHoursComponent() { + const [value, setValue] = useState(); + + return ( + <> + + +
Current Value:{JSON.stringify(value)}
+ + ); +} +export const TimerangepickerOfHours: StoryObj = { + render: () => , name: "timerangepicker of hours ", }; -export const TimerangepickerOfHoursWithHistogram: StoryObj = { - render: () => { - const [value, setValue] = useState<{ - start: Date | number; - end: Date | number; - }>(); - const monday = previousMonday(new Date()); - - return ( - <> - - -
- Current Value:{JSON.stringify(value)} -
- - ); - }, +function TimerangepickerOfHoursWithKeepSelectedWeekdayComponent() { + const [value, setValue] = useState(); + + return ( + <> + + +
Current Value:{JSON.stringify(value)}
+ + ); +} + +export const TimerangepickerOfHoursWithKeepSelectedWeekday: StoryObj< + typeof TimeRangePicker +> = { + render: () => , + name: "timerangepicker of hours with preserved day", +}; + +function TimerangepickerOfHoursWithHistogramComponent() { + const [value, setValue] = useState(); + const monday = previousMonday(new Date()); + + return ( + <> + + +
+ Current Value:{JSON.stringify(value)} +
+ + ); +} +export const TimerangepickerOfHoursWithHistogram: StoryObj< + typeof TimeRangePicker +> = { + render: () => , name: "timerangepicker of hours with histogram", }; diff --git a/src/components/timeRangePicker/TimeRangePicker.tsx b/src/components/timeRangePicker/TimeRangePicker.tsx index 63713731..ee31a8b4 100644 --- a/src/components/timeRangePicker/TimeRangePicker.tsx +++ b/src/components/timeRangePicker/TimeRangePicker.tsx @@ -4,6 +4,7 @@ import { DatesPicker, DateHourPicker } from "./components"; type Props = Omit, "initTimeRange"> & { initTimeRange?: Interval; type?: "Days" | "Hours"; + keepSelectedWeekday?: boolean; }; const DEFAULT_TIME_RANGE: Interval = { @@ -17,6 +18,7 @@ export function TimeRangePicker({ weekCounts, countsLoading = false, disabledDays, + keepSelectedWeekday = false, onChange, onWeekChange, }: Props) { @@ -35,6 +37,7 @@ export function TimeRangePicker({ weekCounts={weekCounts} countsLoading={countsLoading} disabledDays={disabledDays} + keepSelectedWeekday={keepSelectedWeekday} onChange={onChange} onWeekChange={onWeekChange} /> diff --git a/src/components/timeRangePicker/components/DateHourPicker/DateHourPicker.tsx b/src/components/timeRangePicker/components/DateHourPicker/DateHourPicker.tsx index c50170ec..8dc72192 100644 --- a/src/components/timeRangePicker/components/DateHourPicker/DateHourPicker.tsx +++ b/src/components/timeRangePicker/components/DateHourPicker/DateHourPicker.tsx @@ -15,12 +15,14 @@ import { getMonday, getTimeRangeDate, isSameTimeRange, + getNewSelectedDate, } from "../utils"; import { DateSelector, HourSelector } from "./components"; type Props = DatesPickerProps & { weekCounts?: DailyCount[]; countsLoading?: boolean; + keepSelectedWeekday?: boolean; onWeekChange?: (monday: Date) => void; }; @@ -29,11 +31,12 @@ export function DateHourPicker({ weekCounts, countsLoading = false, disabledDays, + keepSelectedWeekday = false, onChange, onWeekChange, }: Props) { const [selectedDate, setSelectedDate] = useState( - getTimeRangeDate(initTimeRange) + getTimeRangeDate(initTimeRange, "Hours") ); const [dateOptions, setDateOptions] = useState( getDateOptions(getMonday(initTimeRange.start)) @@ -93,6 +96,15 @@ export function DateHourPicker({ if (onWeekChange) { onWeekChange(newMonday); } + if (keepSelectedWeekday) { + const newSeletedDate = getNewSelectedDate( + selectedDate, + currentMonday, + newMonday + ); + setSelectedDate(newSeletedDate); + submitDateHour(newSeletedDate, selectedTimeRange); + } }; // Function to update time range options diff --git a/src/components/timeRangePicker/components/utils.ts b/src/components/timeRangePicker/components/utils.ts index 340600ca..46e973be 100644 --- a/src/components/timeRangePicker/components/utils.ts +++ b/src/components/timeRangePicker/components/utils.ts @@ -9,6 +9,10 @@ import { addMonths, addYears, getHours, + differenceInCalendarDays, + startOfDay, + set, + subMilliseconds, } from "date-fns"; import { RANGE_TYPE, DateOption, TimeRangeOption } from "../types"; import { @@ -23,8 +27,16 @@ export const getMonday = (date: Date | number): Date => { return isMonday(currentDate) ? currentDate : previousMonday(currentDate); }; -export const getTimeRangeDate = (initRange: Interval): Date => { - return isSameDay(initRange.start, initRange.end) +export const getTimeRangeDate = ( + initRange: Interval, + timePickerType?: "Days" | "Hours" +): Date => { + // 18-24 time slot technically ends in different day + const end = + timePickerType === "Hours" + ? subMilliseconds(initRange.end, 1) + : initRange.end; + return isSameDay(initRange.start, end) ? new Date(new Date(initRange.start).setSeconds(0, 0)) : new Date(); }; @@ -139,3 +151,23 @@ export const isSameTimeRange = ( getHours(interval.end) === optionEnd ); }; + +export const getNewSelectedDate = ( + selectedDate: Date, + currentMonday: Date, + newMonday: Date +) => { + const dayOffset = differenceInCalendarDays( + startOfDay(selectedDate), + startOfDay(currentMonday) + ); + + const baseDate = addDays(startOfDay(newMonday), dayOffset); + + return set(baseDate, { + hours: selectedDate.getHours(), + minutes: selectedDate.getMinutes(), + seconds: selectedDate.getSeconds(), + milliseconds: selectedDate.getMilliseconds(), + }); +};