diff --git a/.changeset/olive-snails-tie.md b/.changeset/olive-snails-tie.md new file mode 100644 index 000000000..53328610b --- /dev/null +++ b/.changeset/olive-snails-tie.md @@ -0,0 +1,8 @@ +--- +"@wizleap-inc/wiz-ui-react": minor +"@wizleap-inc/wiz-ui-next": minor +"@wizleap-inc/wiz-ui-constants": minor +"@wizleap-inc/wiz-ui-styles": minor +--- + +今日の日付を目立つようにする & キャンセル・適用ボタンの追加 diff --git a/packages/constants/component/aria-label.ts b/packages/constants/component/aria-label.ts index 374553272..6e982a5a7 100644 --- a/packages/constants/component/aria-label.ts +++ b/packages/constants/component/aria-label.ts @@ -36,4 +36,6 @@ export const ARIA_LABELS = { FULL_MODAL_VIEW: { CLOSE: "モーダルを閉じる", }, + APPLY: "適用", + CANCEL: "キャンセル", } as const; diff --git a/packages/styles/bases/calendar.css.ts b/packages/styles/bases/calendar.css.ts index 08422e960..2f5ea8028 100644 --- a/packages/styles/bases/calendar.css.ts +++ b/packages/styles/bases/calendar.css.ts @@ -125,3 +125,11 @@ export const calendarItemInteractiveStyle = style([ }, }, ]); +export const calendarItemInteractiveTodayStyle = style([ + calendarItemInteractiveStyle, + { + border: `1px solid ${THEME.color.green[800]}`, + borderRadius: "50%", + boxSizing: "border-box", + }, +]); diff --git a/packages/wiz-ui-next/src/components/base/calendar/calendar.stories.ts b/packages/wiz-ui-next/src/components/base/calendar/calendar.stories.ts index 55e26bf07..b95be4b66 100644 --- a/packages/wiz-ui-next/src/components/base/calendar/calendar.stories.ts +++ b/packages/wiz-ui-next/src/components/base/calendar/calendar.stories.ts @@ -179,3 +179,72 @@ FilledWeeks.parameters = { }, }, }; + +export const Today = Template.bind({}); +Today.args = { + currentMonth: new Date("2023-03"), + _today: new Date("2023-03-05"), +}; +Today.parameters = { + docs: { + description: { + story: + "本日の日付が丸で囲われるようになっています。通常利用では `_today` パラメータの設定は不要ですが、 `_today`パラメータを設定することで任意の日付を本日の日付として扱うことができます。", + }, + source: { + code: ` + + `, + }, + }, +}; + +export const DisabledToday = Template.bind({}); +DisabledToday.args = { + currentMonth: new Date("2023-03"), + _today: new Date("2023-03-05"), + disabledDate: (date: Date) => date.getDate() === 5, +}; +DisabledToday.parameters = { + docs: { + description: { + story: "本日の日付がdisabledの場合も丸で囲われます。", + }, + source: { + code: ` + + `, + }, + }, +}; + +export const SelectedToday = Template.bind({}); +SelectedToday.args = { + currentMonth: new Date("2023-03"), + _today: new Date("2023-03-05"), + activeDates: [{ date: new Date("2023-03-05"), state: "primary" }], +}; +SelectedToday.parameters = { + docs: { + description: { + story: "本日の日付がActiveの場合はActiveの見た目が優先されます。", + }, + source: { + code: ` + + `, + }, + }, +}; diff --git a/packages/wiz-ui-next/src/components/base/calendar/calendar.vue b/packages/wiz-ui-next/src/components/base/calendar/calendar.vue index 09b4ce34f..404740a07 100644 --- a/packages/wiz-ui-next/src/components/base/calendar/calendar.vue +++ b/packages/wiz-ui-next/src/components/base/calendar/calendar.vue @@ -50,7 +50,13 @@ ) " > -
+
{{ adjacent.current.day }}
@@ -99,6 +105,12 @@ const props = defineProps({ required: false, default: () => false, }, + // eslint-disable-next-line vue/prop-name-casing + _today: { + type: Date as PropType, + required: false, + default: new Date(), + }, }); const calendars = computed(() => { @@ -250,6 +262,17 @@ const updateSelectedDate = (row: number, col: number, day: string) => { } }; +const isToday = (row: number, col: number, day: string) => { + if (!isCurrentMonth(row, col)) return false; + const date = new Date( + props.currentMonth.getFullYear(), + props.currentMonth.getMonth(), + Number(day) + ); + + return date.toDateString() === (props._today || new Date()).toDateString(); +}; + const isActiveDate = computed(() => { const activeDatesSet = new Set( props.activeDates?.map((activeDate) => { diff --git a/packages/wiz-ui-next/src/components/base/inputs/date-picker/date-picker.stories.ts b/packages/wiz-ui-next/src/components/base/inputs/date-picker/date-picker.stories.ts index aa92ec8d7..d023c2843 100644 --- a/packages/wiz-ui-next/src/components/base/inputs/date-picker/date-picker.stories.ts +++ b/packages/wiz-ui-next/src/components/base/inputs/date-picker/date-picker.stories.ts @@ -259,6 +259,7 @@ Test.play = async ({ canvasElement }) => { // その月の15日を選択 const body = within(canvasElement.ownerDocument.body); + const initialDate = new Date(date.getFullYear(), date.getMonth(), 1); const clickDate = new Date(date.getFullYear(), date.getMonth(), 15); const pastClickDate = new Date(date.getFullYear(), date.getMonth() - 1, 15); const clickDateEl = body.getByLabelText(_formatDateJp(clickDate)); @@ -270,11 +271,24 @@ Test.play = async ({ canvasElement }) => { `${_formatDateJp(clickDate)}-選択済み` ) ); + + // クリックした段階ではまだInputに反映されていないこと + await waitFor(() => + expect(button.textContent).toBe(_formatDateJp(initialDate)) + ); + + // 適用ボタンをクリック + const applyButton = body.getByText(ARIA_LABELS.APPLY); + await userEvent.click(applyButton); + // Input内が選択した日付になることを確認 await waitFor(() => expect(button.textContent).toBe(_formatDateJp(clickDate)) ); + // カレンダー再オープン + await userEvent.click(button); + // 月セレクターのPrevを1回押して操作月を1ヶ月前にする const monthSelectorPrev = body.getByLabelText( ARIA_LABELS.MONTH_SELECTOR_PREV @@ -296,6 +310,14 @@ Test.play = async ({ canvasElement }) => { ) ); + // Input内が選択した日付になることを確認 + await waitFor(() => + expect(button.textContent).toBe(_formatDateJp(new Date(clickDate))) + ); + + // 適用ボタンをクリック + await userEvent.click(applyButton); + // Input内が選択した日付になることを確認 await waitFor(() => expect(button.textContent).toBe(_formatDateJp(new Date(pastClickDate))) @@ -340,3 +362,48 @@ const date = ref(null); }, }, }; + +export const Today: StoryFn = (args) => ({ + components: { WizDatepicker, WizHStack }, + setup() { + const date = ref(new Date(2023, 2, 1)); + const isOpen = ref(true); + const setIsOpen = (value: boolean) => (isOpen.value = value); + const today = new Date(2023, 2, 5); + return { args, date, isOpen, setIsOpen, today }; + }, + template: ` + + `, +}); + +export const DisabledToday: StoryFn = (args) => ({ + components: { WizDatepicker, WizHStack }, + setup() { + const date = ref(new Date(2023, 2, 1)); + const isOpen = ref(true); + const setIsOpen = (value: boolean) => (isOpen.value = value); + const today = new Date(2023, 2, 15); + const disabledDate = (date: Date) => + date.getDate() >= 10 && date.getDate() < 17; + return { args, date, isOpen, setIsOpen, today, disabledDate }; + }, + template: ` + + `, +}); diff --git a/packages/wiz-ui-next/src/components/base/inputs/date-picker/date-picker.vue b/packages/wiz-ui-next/src/components/base/inputs/date-picker/date-picker.vue index 9f740ac25..1f80c9c78 100644 --- a/packages/wiz-ui-next/src/components/base/inputs/date-picker/date-picker.vue +++ b/packages/wiz-ui-next/src/components/base/inputs/date-picker/date-picker.vue @@ -36,7 +36,7 @@
@@ -107,10 +107,10 @@ + + + + {{ ARIA_LABELS.CANCEL }} + + + {{ ARIA_LABELS.APPLY }} + +
@@ -147,11 +157,13 @@ import { PropType, computed, inject, ref } from "vue"; import { WizCalendar, + WizDivider, WizHStack, WizIcon, WizPopup, WizPopupContainer, WizText, + WizTextButton, WizVStack, } from "@/components"; import { @@ -231,6 +243,12 @@ const props = defineProps({ default: (date: Date) => `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日`, }, + // eslint-disable-next-line vue/prop-name-casing + _today: { + type: Date as PropType, + required: false, + default: new Date(), + }, }); const emit = defineEmits(); @@ -239,7 +257,7 @@ const defaultCurrentMonth = props.modelValue || new Date(); const currentMonth = ref(defaultCurrentMonth); const setIsOpen = (value: boolean) => emit("update:isOpen", value); -const onClickCancel = () => emit("update:modelValue", null); +const tempDate = ref(props.modelValue); const clickToNextMonth = (e: KeyboardEvent | MouseEvent) => { e.preventDefault(); @@ -305,5 +323,24 @@ const variant = computed(() => { return "default"; }); -const handleClickCalendar = (date: Date) => (calendarValue.value = date); +const handleClickCalendar = (date: Date) => (tempDate.value = date); + +const onClickCancel = (e: MouseEvent) => { + e.stopPropagation(); + tempDate.value = null; + currentMonth.value = new Date(defaultCurrentMonth); + emit("update:modelValue", null); + setIsOpen(false); +}; + +const onClose = () => { + tempDate.value = calendarValue.value; + currentMonth.value = new Date(defaultCurrentMonth); + setIsOpen(false); +}; + +const onSubmit = () => { + calendarValue.value = tempDate.value; + setIsOpen(false); +}; diff --git a/packages/wiz-ui-next/src/components/base/inputs/date-range-picker/date-range-picker.stories.ts b/packages/wiz-ui-next/src/components/base/inputs/date-range-picker/date-range-picker.stories.ts index feaec142b..ea608fe27 100644 --- a/packages/wiz-ui-next/src/components/base/inputs/date-range-picker/date-range-picker.stories.ts +++ b/packages/wiz-ui-next/src/components/base/inputs/date-range-picker/date-range-picker.stories.ts @@ -511,6 +511,16 @@ Test.play = async ({ canvasElement }) => { //左のCalenderから15日を選択 const body = within(canvasElement.ownerDocument.body); + const initialLeftDate = new Date( + intermediateDate.getFullYear(), + intermediateDate.getMonth(), + 0 + ); + const initialRightDate = new Date( + intermediateDate.getFullYear(), + intermediateDate.getMonth() + 1, + 0 + ); const leftClickDate = new Date( intermediateDate.getFullYear(), intermediateDate.getMonth() - 1, @@ -525,11 +535,26 @@ Test.play = async ({ canvasElement }) => { `${_formatDateJp(leftClickDate)}-選択済み` ) ); + + // クリックした段階ではまだInputに反映されていないこと + await waitFor(() => + expect(button.textContent).toBe( + `${_formatDateJp(initialLeftDate)}-${_formatDateJp(initialRightDate)}` + ) + ); + + // 適用ボタンをクリック + const applyButton = body.getByText(ARIA_LABELS.APPLY); + await userEvent.click(applyButton); + // Input内が選択した日付になることを確認 await waitFor(() => expect(button.textContent).toBe(_formatDateJp(leftClickDate) + "-終了日") ); + // カレンダー再オープン + await userEvent.click(button); + // 右のCalenderから15日を選択 const rightClickDate = new Date( intermediateDate.getFullYear(), @@ -545,12 +570,24 @@ Test.play = async ({ canvasElement }) => { `${_formatDateJp(rightClickDate)}-選択済み` ) ); + // クリックした段階ではまだInputに反映されていないこと + await waitFor(() => + expect(button.textContent).toBe(_formatDateJp(leftClickDate) + "-終了日") + ); + + // 適用ボタンをクリック + await userEvent.click(applyButton); + // Input内が選択した日付になることを確認 await waitFor(() => expect(button.textContent?.replace(/\s+/g, "")).toBe( `${_formatDateJp(leftClickDate)}-${_formatDateJp(rightClickDate)}` ) ); + + // カレンダー再オープン + await userEvent.click(button); + // data-is-selectedなボタンがrightClickedDate ~ leftClickedDateの間の数だけあることを確認 const diff = Math.floor( (rightClickDate.getTime() - leftClickDate.getTime()) / (1000 * 60 * 60 * 24) @@ -590,3 +627,81 @@ Test.play = async ({ canvasElement }) => { await userEvent.tab(); await userEvent.tab(); }; + +export const Today: StoryFn = (args) => ({ + components: { WizDateRangePicker }, + setup() { + const dateRange1 = ref({ + start: new Date(2000, 0, 15), + end: new Date(2000, 1, 15), + }); + const selectBoxValue1 = ref(); + const isOpen1 = ref(true); + const setIsOpen1 = (value: boolean) => (isOpen1.value = value); + const today = new Date(2000, 0, 28); + return { + dateRange1, + selectBoxValue1, + selectBoxOptions, + isOpen1, + setIsOpen1, + today, + args, + }; + }, + template: ` +
+ +
+ `, +}); + +export const DisabledToday: StoryFn = (args) => ({ + components: { WizDateRangePicker }, + setup() { + const dateRange1 = ref({ + start: new Date(2000, 0, 15), + end: new Date(2000, 1, 15), + }); + const selectBoxValue1 = ref(); + const isOpen1 = ref(true); + const setIsOpen1 = (value: boolean) => (isOpen1.value = value); + const today = new Date(2000, 0, 28); + const disabledDate = (date: Date) => + date.getDate() >= 20 && date.getDate() < 31; + return { + dateRange1, + selectBoxValue1, + selectBoxOptions, + isOpen1, + setIsOpen1, + today, + disabledDate, + args, + }; + }, + template: ` +
+ +
+ `, +}); diff --git a/packages/wiz-ui-next/src/components/base/inputs/date-range-picker/date-range-picker.vue b/packages/wiz-ui-next/src/components/base/inputs/date-range-picker/date-range-picker.vue index 33b13a63f..71ce7dbeb 100644 --- a/packages/wiz-ui-next/src/components/base/inputs/date-range-picker/date-range-picker.vue +++ b/packages/wiz-ui-next/src/components/base/inputs/date-range-picker/date-range-picker.vue @@ -53,7 +53,7 @@ @@ -120,6 +120,7 @@ @click="handleDayClick" :disabledDate="disabledDate" :filledWeeks="true" + :_today="_today || new Date()" />
@@ -145,9 +146,19 @@ @click="handleDayClick" :disabledDate="disabledDate" :filledWeeks="true" + :_today="_today || new Date()" />
+ + + + {{ ARIA_LABELS.CANCEL }} + + + {{ ARIA_LABELS.APPLY }} + +
@@ -163,6 +174,7 @@ import { computed, inject, PropType, ref } from "vue"; import { WizCalendar, WizCard, + WizDivider, WizHStack, WizICalendar, WizICancel, @@ -173,6 +185,7 @@ import { WizIExpandMore, WizPopup, WizPopupContainer, + WizTextButton, } from "@/components"; import { useClickOutside } from "@/hooks/use-click-outside"; import { formControlKey } from "@/hooks/use-form-control-provider"; @@ -237,6 +250,12 @@ const props = defineProps({ default: (date: Date) => `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日`, }, + // eslint-disable-next-line vue/prop-name-casing + _today: { + type: Date as PropType, + required: false, + default: new Date(), + }, }); const emit = defineEmits(); @@ -263,10 +282,24 @@ const leftCalendarDate = computed(() => { ); return date; }); +const initializeRightCalendarDate = () => { + rightCalendarDate.value = new Date(); + const [start, end] = [props.modelValue.start, props.modelValue.end]; + if (end) { + rightCalendarDate.value = new Date(end); + } else if (start) { + rightCalendarDate.value = new Date( + start.getFullYear(), + start.getMonth() + 1, + 1 + ); + } else { + rightCalendarDate.value = new Date(); + } +}; const setIsOpen = (value: boolean) => emit("update:isOpen", value); -const onClickCancel = () => - emit("update:modelValue", { start: null, end: null }); +const tempDateRange = ref(props.modelValue); const moveToNextMonth = (e: KeyboardEvent | MouseEvent) => { e.preventDefault(); @@ -285,7 +318,7 @@ const selectedDates = computed(() => { date, state, }); - const [start, end] = [props.modelValue.start, props.modelValue.end]; + const [start, end] = [tempDateRange.value.start, tempDateRange.value.end]; if (start && end) { const secondaries: DateStatus[] = []; const tomorrowOfStart = new Date(start); @@ -306,17 +339,17 @@ const selectedDates = computed(() => { }); const handleDayClick = (date: Date) => { - const [start, end] = [props.modelValue.start, props.modelValue.end]; + const [start, end] = [tempDateRange.value.start, tempDateRange.value.end]; if (start && end) { - emit("update:modelValue", { start: date, end: null }); + tempDateRange.value = { start: date, end: null }; return; } if (start) { const [nextStart, nextEnd] = start > date ? [date, start] : [start, date]; - emit("update:modelValue", { start: nextStart, end: nextEnd }); + tempDateRange.value = { start: nextStart, end: nextEnd }; return; } - emit("update:modelValue", { start: date, end: null }); + tempDateRange.value = { start: date, end: null }; }; const toggleSelectBoxOpen = () => { @@ -349,4 +382,23 @@ const borderState = computed(() => { if (props.isOpen && !props.disabled) return "active"; return "default"; }); + +const onClickCancel = (e: MouseEvent) => { + e.stopPropagation(); + tempDateRange.value = { start: null, end: null }; + initializeRightCalendarDate(); + emit("update:modelValue", tempDateRange.value); + setIsOpen(false); +}; + +const onClose = () => { + tempDateRange.value = props.modelValue; + initializeRightCalendarDate(); + setIsOpen(false); +}; + +const onSubmit = () => { + emit("update:modelValue", tempDateRange.value); + setIsOpen(false); +}; diff --git a/packages/wiz-ui-react/src/components/base/calendar/components/calendar.tsx b/packages/wiz-ui-react/src/components/base/calendar/components/calendar.tsx index 7fc7ac2a0..997a36770 100644 --- a/packages/wiz-ui-react/src/components/base/calendar/components/calendar.tsx +++ b/packages/wiz-ui-react/src/components/base/calendar/components/calendar.tsx @@ -93,6 +93,7 @@ type Props = BaseProps & { currentMonth?: Date; activeDates?: DateStatus[]; filledWeeks?: boolean; + _today?: Date; onClickDate?: (selectedValue: Date) => void; /** * @description 日付が無効かどうかを判定する関数です。無効な日付はクリック不可になります。 @@ -108,6 +109,7 @@ const Calendar: FC = ({ currentMonth = new Date(), activeDates, filledWeeks, + _today, onClickDate, disabledDate, }) => { @@ -126,6 +128,18 @@ const Calendar: FC = ({ }); } + function getItemDate(item: CalendarDataItem) { + return new Date( + currentMonth.getFullYear(), + currentMonth.getMonth(), + Number(item.label) + ); + } + + function getIsToday(a: Date) { + return a.toDateString() === (_today || new Date()).toDateString(); + } + function getItemStyleState( item: CalendarDataItem, dateStatus?: DateStatus @@ -139,13 +153,7 @@ const Calendar: FC = ({ if (item.isOutOfCurrentMonth) { return; } - onClickDate?.( - new Date( - currentMonth.getFullYear(), - currentMonth.getMonth(), - Number(item.label) - ) - ); + onClickDate?.(getItemDate(item)); } const calendarItems = calendarData.map((weekDateItems) => { return weekDateItems.map((item) => { @@ -214,6 +222,7 @@ const Calendar: FC = ({ const item = adjacent.current.item; const itemStyle = adjacent.current.itemStyle; const activeDateStatus = adjacent.current.activeDateStatus; + const isToday = getIsToday(getItemDate(item)); return ( = ({ adjacent.current.itemStyle === "primary" )} > -
+
{item.label}
diff --git a/packages/wiz-ui-react/src/components/base/calendar/stories/calendar.stories.tsx b/packages/wiz-ui-react/src/components/base/calendar/stories/calendar.stories.tsx index 7b1d22f42..32dcd8302 100644 --- a/packages/wiz-ui-react/src/components/base/calendar/stories/calendar.stories.tsx +++ b/packages/wiz-ui-react/src/components/base/calendar/stories/calendar.stories.tsx @@ -107,3 +107,29 @@ export const FilledWeeks: Story = { }, }, }; + +export const Today: Story = { + ...Template, + args: { + currentMonth: new Date("2023-03"), + _today: new Date("2023-03-05"), + }, +}; + +export const DisabledToday: Story = { + ...Template, + args: { + currentMonth: new Date("2023-03"), + _today: new Date("2023-03-05"), + disabledDate: (date: Date) => date.getDate() === 5, + }, +}; + +export const SelectedToday: Story = { + ...Template, + args: { + currentMonth: new Date("2023-03"), + _today: new Date("2023-03-05"), + activeDates: [{ date: new Date("2023-03-05"), state: "primary" }], + }, +}; diff --git a/packages/wiz-ui-react/src/components/base/inputs/date-picker/components/date-picker.tsx b/packages/wiz-ui-react/src/components/base/inputs/date-picker/components/date-picker.tsx index 8455dcbee..b1b43ea76 100644 --- a/packages/wiz-ui-react/src/components/base/inputs/date-picker/components/date-picker.tsx +++ b/packages/wiz-ui-react/src/components/base/inputs/date-picker/components/date-picker.tsx @@ -6,9 +6,22 @@ import { inputBorderStyle, } from "@wizleap-inc/wiz-ui-styles/commons"; import clsx from "clsx"; -import { FC, KeyboardEvent, useContext, useRef, useState } from "react"; +import { + FC, + KeyboardEvent, + MouseEvent, + useContext, + useRef, + useState, +} from "react"; -import { WizCalendar, WizPopup, WizText } from "@/components"; +import { + WizCalendar, + WizDivider, + WizPopup, + WizText, + WizTextButton, +} from "@/components"; import { WizIcon } from "@/components/base/icon"; import { WizHStack, WizVStack } from "@/components/base/stack"; import { FormControlContext } from "@/components/custom/form/components/form-control-context"; @@ -26,6 +39,7 @@ type Props = BaseProps & { disabled?: boolean; error?: boolean; isDirectionFixed?: boolean; + _today?: Date; onChangeDate: (selectedValue: Date | null) => void; /** * @description 日付が無効かどうかを判定する関数です。無効な日付はクリック不可になります。 @@ -60,6 +74,7 @@ const DatePicker: FC = ({ formatYear = (year) => `${year}`, formatDate = (date) => `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日`, + _today, }: Props) => { const [isOpen, setIsOpen] = useState(false); const cancelButtonVisible = !disabled && !!date; @@ -73,6 +88,7 @@ const DatePicker: FC = ({ ) ); }; + const [tempDate, setTempDate] = useState(date); const handleKeyDown = (e: KeyboardEvent) => { e.preventDefault(); @@ -108,6 +124,26 @@ const DatePicker: FC = ({ return "default"; })(); const wrapperButtonRef = useRef(null); + + const onCancel = (e: MouseEvent) => { + e.stopPropagation(); + setTempDate(null); + setCurrentMonth(new Date()); + onChangeDate(null); + setIsOpen(false); + }; + + const onClose = () => { + setTempDate(date); + setCurrentMonth(date || new Date()); + setIsOpen(false); + }; + + const onSubmit = () => { + onChangeDate(tempDate); + setIsOpen(false); + }; + return ( <> setIsOpen(false)} + onClose={onClose} direction="bottomLeft" isDirectionFixed={isDirectionFixed} anchorElement={wrapperButtonRef} @@ -238,13 +274,23 @@ const DatePicker: FC = ({ onChangeDate(date)} + onClickDate={(date) => setTempDate(date)} currentMonth={currentMonth} filledWeeks + _today={_today || new Date()} disabledDate={disabledDate} /> + + + + {ARIA_LABELS.CANCEL} + + + {ARIA_LABELS.APPLY} + + diff --git a/packages/wiz-ui-react/src/components/base/inputs/date-picker/stories/date-picker.stories.tsx b/packages/wiz-ui-react/src/components/base/inputs/date-picker/stories/date-picker.stories.tsx index 077bd642e..74bfccacc 100644 --- a/packages/wiz-ui-react/src/components/base/inputs/date-picker/stories/date-picker.stories.tsx +++ b/packages/wiz-ui-react/src/components/base/inputs/date-picker/stories/date-picker.stories.tsx @@ -98,3 +98,26 @@ export const Playground: Story = { return ; }, }; + +export const Today: Story = { + args: { + date: new Date(2023, 0, 1), + _today: new Date("2023-01-15"), + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + canvas.getByLabelText(ARIA_LABELS.DATE_PICKER_INPUT).click(); + }, +}; + +export const DisabledToday: Story = { + args: { + date: new Date(2023, 0, 1), + disabledDate: (date: Date) => date.getDate() >= 10 && date.getDate() < 17, + _today: new Date("2023-01-15"), + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + canvas.getByLabelText(ARIA_LABELS.DATE_PICKER_INPUT).click(); + }, +}; diff --git a/packages/wiz-ui-react/src/components/base/inputs/date-range-picker/components/date-range-picker.tsx b/packages/wiz-ui-react/src/components/base/inputs/date-range-picker/components/date-range-picker.tsx index 0d986b58b..9ff0ecaeb 100644 --- a/packages/wiz-ui-react/src/components/base/inputs/date-range-picker/components/date-range-picker.tsx +++ b/packages/wiz-ui-react/src/components/base/inputs/date-range-picker/components/date-range-picker.tsx @@ -5,6 +5,7 @@ import clsx from "clsx"; import { FC, KeyboardEvent, + MouseEvent, useCallback, useContext, useMemo, @@ -15,6 +16,7 @@ import { import { WizCalendar, WizCard, + WizDivider, WizHStack, WizICalendar, WizICancel, @@ -24,6 +26,7 @@ import { WizIExpandMore, WizIcon, WizPopup, + WizTextButton, } from "@/components"; import { DateStatus } from "@/components/base/calendar/components/types"; import { FormControlContext } from "@/components/custom/form/components/form-control-context"; @@ -40,6 +43,7 @@ type Props = BaseProps & { selectBoxValue?: string; isDirectionFixed?: boolean; error?: boolean; + _today?: Date; onChangeDateRange: (dateRange: DateRange) => void; onChangeSelectBoxValue?: (value: string) => void; disabledDate?: (date: Date) => boolean; @@ -60,6 +64,7 @@ const DateRangePicker: FC = ({ selectBoxValue, isDirectionFixed = false, error, + _today, onChangeDateRange, onChangeSelectBoxValue, disabledDate = () => false, @@ -68,6 +73,7 @@ const DateRangePicker: FC = ({ }: Props) => { const [isOpen, setIsOpen] = useState(false); const [isSelectBoxOpen, setIsSelectBoxOpen] = useState(false); + const [tempDateRange, setTempDateRange] = useState(dateRange); const cancelButtonVisible = !disabled && !!dateRange.start; const [rightCalendarDate, setRightCalendarDate] = useState( (() => { @@ -90,7 +96,6 @@ const DateRangePicker: FC = ({ ), [rightCalendarDate] ); - const onClickCancel = () => onChangeDateRange({ start: null, end: null }); const moveCalendar = (command: "nextMonth" | "prevMonth") => { const dm = command === "nextMonth" ? 1 : -1; setRightCalendarDate( @@ -102,6 +107,19 @@ const DateRangePicker: FC = ({ ); }; + const initiaizeRightCalendarDate = () => { + const [start, end] = [dateRange.start, dateRange.end]; + if (end) { + setRightCalendarDate(new Date(end)); + } else if (start) { + setRightCalendarDate( + new Date(start.getFullYear(), start.getMonth() + 1, 1) + ); + } else { + setRightCalendarDate(new Date()); + } + }; + const selectedDates = useMemo(() => { const getDateStatus = ( date: Date, @@ -110,7 +128,7 @@ const DateRangePicker: FC = ({ date, state, }); - const [start, end] = [dateRange.start, dateRange.end]; + const [start, end] = [tempDateRange.start, tempDateRange.end]; if (start && end) { const secondaries: DateStatus[] = []; const tomorrowOfStart = new Date(start); @@ -128,7 +146,7 @@ const DateRangePicker: FC = ({ return [getDateStatus(start, "primary")]; } return []; - }, [dateRange]); + }, [tempDateRange]); const handleKeyDown = (e: KeyboardEvent) => { e.preventDefault(); @@ -144,18 +162,18 @@ const DateRangePicker: FC = ({ const onClickDate = useCallback( (date: Date) => { - const [start, end] = [dateRange.start, dateRange.end]; + const [start, end] = [tempDateRange.start, tempDateRange.end]; if (start && end) { - onChangeDateRange({ start: date, end: null }); + setTempDateRange({ start: date, end: null }); } else if (start) { const [nextStart, nextEnd] = start > date ? [date, start] : [start, date]; - onChangeDateRange({ start: nextStart, end: nextEnd }); + setTempDateRange({ start: nextStart, end: nextEnd }); } else { - onChangeDateRange({ start: date, end: null }); + setTempDateRange({ start: date, end: null }); } }, - [dateRange, onChangeDateRange] + [tempDateRange] ); const selectedOption = (() => { @@ -182,6 +200,26 @@ const DateRangePicker: FC = ({ if (isOpen && !disabled) return "active"; return "default"; })(); + + const onClickCancel = (e: MouseEvent) => { + e.stopPropagation(); + initiaizeRightCalendarDate(); + setTempDateRange({ start: null, end: null }); + onChangeDateRange({ start: null, end: null }); + setIsOpen(false); + }; + + const onClose = () => { + initiaizeRightCalendarDate(); + setTempDateRange(dateRange); + setIsOpen(false); + }; + + const onSubmit = () => { + onChangeDateRange(tempDateRange); + setIsOpen(false); + }; + return ( <> setIsOpen(false)} + onClose={onClose} isDirectionFixed={isDirectionFixed} anchorElement={anchor} > @@ -321,6 +359,7 @@ const DateRangePicker: FC = ({ onClickDate={onClickDate} disabledDate={disabledDate} filledWeeks={true} + _today={_today || new Date()} />
@@ -350,9 +389,19 @@ const DateRangePicker: FC = ({ onClickDate={onClickDate} disabledDate={disabledDate} filledWeeks={true} + _today={_today || new Date()} />
+ + + + {ARIA_LABELS.CANCEL} + + + {ARIA_LABELS.APPLY} + +
diff --git a/packages/wiz-ui-react/src/components/base/inputs/date-range-picker/stories/date-range-picker.stories.tsx b/packages/wiz-ui-react/src/components/base/inputs/date-range-picker/stories/date-range-picker.stories.tsx index 43ef9505e..3ca2c245a 100644 --- a/packages/wiz-ui-react/src/components/base/inputs/date-range-picker/stories/date-range-picker.stories.tsx +++ b/packages/wiz-ui-react/src/components/base/inputs/date-range-picker/stories/date-range-picker.stories.tsx @@ -185,3 +185,32 @@ export const Playground: Story = { ); }, }; + +export const Today: Story = { + args: { + dateRange: { + start: new Date("2021-01-15"), + end: new Date("2021-02-15"), + }, + _today: new Date("2021-01-28"), + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + canvas.getByLabelText(ARIA_LABELS.RANGE_DATE_PICKER_INPUT).click(); + }, +}; + +export const DisabledWithToday: Story = { + args: { + dateRange: { + start: new Date("2021-01-01"), + end: new Date("2021-01-31"), + }, + disabledDate: (date: Date) => date.getDate() >= 10 && date.getDate() < 17, + _today: new Date("2021-01-15"), + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + canvas.getByLabelText(ARIA_LABELS.RANGE_DATE_PICKER_INPUT).click(); + }, +};