diff --git a/packages/datetime/src/common/classes.ts b/packages/datetime/src/common/classes.ts index b7551385de..067ae64ffc 100644 --- a/packages/datetime/src/common/classes.ts +++ b/packages/datetime/src/common/classes.ts @@ -45,6 +45,7 @@ export const DATERANGEPICKER = `${NS}-daterangepicker`; export const DATERANGEPICKER_CALENDARS = `${DATERANGEPICKER}-calendars`; export const DATERANGEPICKER_CONTIGUOUS = `${DATERANGEPICKER}-contiguous`; export const DATERANGEPICKER_SINGLE_MONTH = `${DATERANGEPICKER}-single-month`; +export const DATERANGEPICKER_SHOWING_OUTSIDE_DAYS = `${DATERANGEPICKER}-showing-outside-days`; export const DATERANGEPICKER_DAY_SELECTED_RANGE = `${DATEPICKER_DAY}--selected-range`; export const DATERANGEPICKER_DAY_HOVERED_RANGE = `${DATEPICKER_DAY}--hovered-range`; export const DATERANGEPICKER_SHORTCUTS = `${DATERANGEPICKER}-shortcuts`; diff --git a/packages/datetime2/src/common/errors.ts b/packages/datetime2/src/common/errors.ts new file mode 100644 index 0000000000..01816b7352 --- /dev/null +++ b/packages/datetime2/src/common/errors.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2024 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const WARNING_IGNORED_SHOW_OUTSIDE_DAYS_PROP = + "dayPickerProps.showOutsideDays will only be applied if the DateRangePicker3 is displaying a single month, either through the singleMonthOnly prop, or with a minDate and maxDate of the same month."; diff --git a/packages/datetime2/src/components/date-range-picker3/_date-range-picker3.scss b/packages/datetime2/src/components/date-range-picker3/_date-range-picker3.scss index 03e35b90a7..f93240a175 100644 --- a/packages/datetime2/src/components/date-range-picker3/_date-range-picker3.scss +++ b/packages/datetime2/src/components/date-range-picker3/_date-range-picker3.scss @@ -59,6 +59,14 @@ } } + + /* stylelint-disable max-line-length */ + &.#{$ns}-datepicker.#{$ns}-daterangepicker-single-month.#{$ns}-daterangepicker-showing-outside-days .rdp-day_outside { + pointer-events: none; // weird interactions happen if allowing outside days to be clicked + visibility: visible; // showing outside days is allowed for single months + } + /* stylelint-enable max-line-length */ + // need some extra specificity to override DatePicker styles &.#{$ns}-datepicker .rdp-day { // we only want outside days to be shown when displaying one month at a time diff --git a/packages/datetime2/src/components/date-range-picker3/date-range-picker3.md b/packages/datetime2/src/components/date-range-picker3/date-range-picker3.md index e0525dc812..2be88128a7 100644 --- a/packages/datetime2/src/components/date-range-picker3/date-range-picker3.md +++ b/packages/datetime2/src/components/date-range-picker3/date-range-picker3.md @@ -64,6 +64,9 @@ The **preset shortcuts** can be seen in the example above. They are as follows: @## Props interface +The `showOutsideDays` prop on `dayPickerProps` will only be respected when a single month is shown. +This is to avoid a possible double representation of a selected date range. + @interface DateRangePicker3Props @## Localization diff --git a/packages/datetime2/src/components/date-range-picker3/dateRangePicker3.tsx b/packages/datetime2/src/components/date-range-picker3/dateRangePicker3.tsx index 5cd0ed9679..d3747af9e5 100644 --- a/packages/datetime2/src/components/date-range-picker3/dateRangePicker3.tsx +++ b/packages/datetime2/src/components/date-range-picker3/dateRangePicker3.tsx @@ -35,6 +35,7 @@ import { import { Classes, dayPickerClassNameOverrides } from "../../classes"; import { combineModifiers, HOVERED_RANGE_MODIFIER } from "../../common/dayPickerModifiers"; +import { WARNING_IGNORED_SHOW_OUTSIDE_DAYS_PROP } from "../../common/errors"; import { DatePicker3Provider } from "../date-picker3/datePicker3Context"; import { DateFnsLocalizedComponent } from "../dateFnsLocalizedComponent"; @@ -120,6 +121,10 @@ export class DateRangePicker3 extends DateFnsLocalizedComponent", () => { }); }); + describe("outside days", () => { + it("visually hides outside days when contiguous months are shown, even if showOutsideDays is true", () => { + const warnSpy = stub(console, "warn"); + const { left } = render({ + dayPickerProps: { showOutsideDays: true }, + initialMonth: new Date(2015, Months.DECEMBER, 2), + }); + assert.strictEqual(warnSpy.callCount, 1); + assert.equal( + left.findOutsideDays().first().getDOMNode().computedStyleMap().get("visibility")?.toString(), + "hidden", + ); + }); + + it("allows outside days to be shown for single month date range", () => { + const { left } = render({ + dayPickerProps: { showOutsideDays: true }, + initialMonth: new Date(2015, Months.DECEMBER, 2), + singleMonthOnly: true, + }); + assert.equal( + left.findOutsideDays().first().getDOMNode().computedStyleMap().get("visibility")?.toString(), + "visible", + ); + }); + + it("does not allow outside days to be selected even when visible", () => { + const { left } = render({ + dayPickerProps: { showOutsideDays: true }, + initialMonth: new Date(2015, Months.DECEMBER, 2), + singleMonthOnly: true, + }); + + assert.equal( + left.findOutsideDays().first().getDOMNode().computedStyleMap().get("pointer-events")?.toString(), + "none", + ); + + // simulated clicks appear to not respect pointerEvents style or else would test with below + // + // November 29th is first outside day shown for December 2015 + // assert.equal(left.findOutsideDays().first().text(), "29"); + // + // this click would navigate to November if interaction was allowed + // left.findOutsideDays().first().simulate("click"); + // left.assertDisplayMonth(Months.DECEMBER, 2015); + }); + }); + function dayNotOutside(day: ReactWrapper) { return !day.hasClass(Datetime2Classes.DATEPICKER3_DAY_OUTSIDE); } @@ -1460,10 +1509,12 @@ describe("", () => { return harness .findDays() .filterWhere(day => day.text() === "" + dayNumber) - .filterWhere(day => !day.hasClass(Datetime2Classes.DATEPICKER3_DAY_OUTSIDE)) + .filterWhere(dayNotOutside) .first(); }, findDays: () => harness.wrapper.find(`.${Datetime2Classes.DATEPICKER3_DAY}`), + findOutsideDays: () => + harness.wrapper.find(`.${Datetime2Classes.DATEPICKER3_DAY}`).filterWhere(day => !dayNotOutside(day)), mouseEnterDay: (dayNumber = 1) => { harness.findDay(dayNumber).simulate("mouseenter"); return harness; diff --git a/packages/docs-app/src/examples/datetime2-examples/dateRangePicker3Example.tsx b/packages/docs-app/src/examples/datetime2-examples/dateRangePicker3Example.tsx index 46a823fcb3..a0a60d4132 100644 --- a/packages/docs-app/src/examples/datetime2-examples/dateRangePicker3Example.tsx +++ b/packages/docs-app/src/examples/datetime2-examples/dateRangePicker3Example.tsx @@ -16,8 +16,8 @@ import * as React from "react"; -import { Classes, FormGroup, Switch } from "@blueprintjs/core"; -import type { DateRange, TimePrecision } from "@blueprintjs/datetime"; +import { Classes, FormGroup, Switch, Tooltip } from "@blueprintjs/core"; +import { type DateRange, DateUtils, type TimePrecision } from "@blueprintjs/datetime"; import { DateRangePicker3 } from "@blueprintjs/datetime2"; import { Example, type ExampleProps, handleBooleanChange, handleValueChange } from "@blueprintjs/docs-theme"; @@ -41,6 +41,7 @@ interface DateRangePicker3ExampleState { showTimeArrowButtons: boolean; timePrecision: TimePrecision | undefined; useAmPm: boolean; + showOutsideDays: boolean; } export class DateRangePicker3Example extends React.PureComponent { @@ -53,6 +54,7 @@ export class DateRangePicker3Example extends React.PureComponent this.setState({ showOutsideDays })); + public render() { const { dateRange, localeCode, maxDate, minDate, showTimeArrowButtons, timePrecision, useAmPm, ...props } = this.state; @@ -105,6 +109,9 @@ export class DateRangePicker3Example extends React.PureComponent + + +