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); +};