Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat : add usePointerEvent props #4553

Merged
merged 2 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/datepicker.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,4 @@ General datepicker component.
| `icon` | `string` or `element` | | Allows using a custom calendar icon. Accepts a string (icon class name) or a React component (e.g., custom SVG). If a string is passed, an `<i>` element is rendered with that string as its class name. If a React component is passed, it is rendered as-is. |
| `calendarIconClassName` | `string` | | Accepts a string that will be added as an additional CSS class to the calendar icon, allowing further styling customization. |
| `showIcon` | `bool` | `true` | Determines whether the calendar icon is displayed. Set to `true` to display the icon, and `false` to hide it. If `icon` prop is also provided, the custom icon will be displayed when `showIcon` is `true`. |
| `usePointerEvent` | `bool` | false | True if Pointer Events (e.g, onPointerEnter, onPointerLeave) are used internally instead of Mouse Events (e.g, onMouseEnter, onMouseLeave). |
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,4 @@
| `wrapperClassName` | `string` | | |
| `yearDropdownItemNumber` | `number` | | |
| `yearItemNumber` | `number` | `DEFAULT_YEAR_ITEM_NUMBER` | |
| `usePointerEvent` | `bool` | `false` | |
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be useful to add a brief note about what usePointerEvent does and when to use it.

🔹 Improve Readability (Nice to have)

Image of Ryan Ryan

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added description to docs/datepicker.md, since the description of other Props was also written there.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that's my mistake, I meant to make that comment on that file instead. Thanks!

Image of Ryan Ryan

1 change: 1 addition & 0 deletions docs/year.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@
| `setPreSelection` | `func` | | |
| `startDate` | `instanceOfDate` | | |
| `yearItemNumber` | `number` | | |
| `usePointerEvent` | `bool` | | |
2 changes: 2 additions & 0 deletions src/calendar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ export default class Calendar extends React.Component {
renderMonthContent: PropTypes.func,
renderQuarterContent: PropTypes.func,
renderYearContent: PropTypes.func,
usePointerEvent: PropTypes.bool,
onDayMouseEnter: PropTypes.func,
onMonthMouseLeave: PropTypes.func,
onYearMouseEnter: PropTypes.func,
Expand Down Expand Up @@ -897,6 +898,7 @@ export default class Calendar extends React.Component {
onDayClick={this.handleDayClick}
handleOnKeyDown={this.props.handleOnDayKeyDown}
handleOnMonthKeyDown={this.props.handleOnKeyDown}
usePointerEvent={this.props.usePointerEvent}
onDayMouseEnter={this.handleDayMouseEnter}
onMouseLeave={this.handleMonthMouseLeave}
onWeekSelect={this.props.onWeekSelect}
Expand Down
4 changes: 3 additions & 1 deletion src/day.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export default class Day extends React.Component {
shouldFocusDayInline: PropTypes.bool,
month: PropTypes.number,
onClick: PropTypes.func,
usePointerEvent: PropTypes.bool,
onMouseEnter: PropTypes.func,
preSelection: PropTypes.instanceOf(Date),
selected: PropTypes.object,
Expand Down Expand Up @@ -434,7 +435,8 @@ export default class Day extends React.Component {
className={this.getClassNames(this.props.day)}
onKeyDown={this.handleOnKeyDown}
onClick={this.handleClick}
onMouseEnter={this.handleMouseEnter}
onMouseEnter={!this.props.usePointerEvent ? this.handleMouseEnter : undefined}
onPointerEnter={this.props.usePointerEvent ? this.handleMouseEnter : undefined}
tabIndex={this.getTabIndex()}
aria-label={this.getAriaLabel()}
role="option"
Expand Down
3 changes: 3 additions & 0 deletions src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export default class DatePicker extends React.Component {
customTimeInput: null,
calendarStartDay: undefined,
toggleCalendarOnIconClick: false,
usePointerEvent: false,
};
}

Expand Down Expand Up @@ -311,6 +312,7 @@ export default class DatePicker extends React.Component {
customTimeInput: PropTypes.element,
weekAriaLabelPrefix: PropTypes.string,
monthAriaLabelPrefix: PropTypes.string,
usePointerEvent: PropTypes.bool,
};

constructor(props) {
Expand Down Expand Up @@ -1152,6 +1154,7 @@ export default class DatePicker extends React.Component {
isInputFocused={this.state.focused}
customTimeInput={this.props.customTimeInput}
setPreSelection={this.setPreSelection}
usePointerEvent={this.props.usePointerEvent}
>
{this.props.children}
</WrappedCalendar>
Expand Down
11 changes: 8 additions & 3 deletions src/month.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export default class Month extends React.Component {
maxDate: PropTypes.instanceOf(Date),
minDate: PropTypes.instanceOf(Date),
onDayClick: PropTypes.func,
usePointerEvent: PropTypes.bool,
onDayMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
onWeekSelect: PropTypes.func,
Expand Down Expand Up @@ -311,6 +312,7 @@ export default class Month extends React.Component {
day={currentWeekStart}
month={utils.getMonth(this.props.day)}
onDayClick={this.handleDayClick}
usePointerEvent={this.props.usePointerEvent}
onDayMouseEnter={this.handleDayMouseEnter}
onWeekSelect={this.props.onWeekSelect}
formatWeekNumber={this.props.formatWeekNumber}
Expand Down Expand Up @@ -684,7 +686,8 @@ export default class Month extends React.Component {

this.onMonthKeyDown(ev, m);
}}
onMouseEnter={() => this.onMonthMouseEnter(m)}
onMouseEnter={!this.props.usePointerEvent ? () => this.onMonthMouseEnter(m) : undefined}
onPointerEnter={this.props.usePointerEvent ? () => this.onMonthMouseEnter(m) : undefined}
tabIndex={this.getTabIndex(m)}
className={this.getMonthClassNames(m)}
role="option"
Expand Down Expand Up @@ -715,7 +718,8 @@ export default class Month extends React.Component {
onKeyDown={(ev) => {
this.onQuarterKeyDown(ev, q);
}}
onMouseEnter={() => this.onQuarterMouseEnter(q)}
onMouseEnter={!this.props.usePointerEvent ? () => this.onQuarterMouseEnter(q) : undefined}
onPointerEnter={this.props.usePointerEvent ? () => this.onQuarterMouseEnter(q) : undefined}
className={this.getQuarterClassNames(q)}
aria-selected={this.isSelectedQuarter(day, q, selected)}
tabIndex={this.getQuarterTabIndex(q)}
Expand Down Expand Up @@ -760,7 +764,8 @@ export default class Month extends React.Component {
return (
<div
className={this.getClassNames()}
onMouseLeave={this.handleMouseLeave}
onMouseLeave={!this.props.usePointerEvent ? this.handleMouseLeave : undefined}
onPointerLeave={this.props.usePointerEvent ? this.handleMouseLeave : undefined}
aria-label={`${ariaLabelPrefix} ${utils.formatDate(day, "yyyy-MM")}`}
role="listbox"
>
Expand Down
2 changes: 2 additions & 0 deletions src/week.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default class Week extends React.Component {
minDate: PropTypes.instanceOf(Date),
month: PropTypes.number,
onDayClick: PropTypes.func,
usePointerEvent: PropTypes.bool,
onDayMouseEnter: PropTypes.func,
onWeekSelect: PropTypes.func,
preSelection: PropTypes.instanceOf(Date),
Expand Down Expand Up @@ -149,6 +150,7 @@ export default class Week extends React.Component {
day={day}
month={this.props.month}
onClick={this.handleDayClick.bind(this, day)}
usePointerEvent={this.props.usePointerEvent}
onMouseEnter={this.handleDayMouseEnter.bind(this, day)}
minDate={this.props.minDate}
maxDate={this.props.maxDate}
Expand Down
10 changes: 7 additions & 3 deletions src/year.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default class Year extends React.Component {
inline: PropTypes.bool,
maxDate: PropTypes.instanceOf(Date),
minDate: PropTypes.instanceOf(Date),
usePointerEvent: PropTypes.bool,
onYearMouseEnter: PropTypes.func.isRequired,
onYearMouseLeave: PropTypes.func.isRequired,
selectingDate: PropTypes.instanceOf(Date),
Expand Down Expand Up @@ -259,8 +260,10 @@ export default class Year extends React.Component {
}}
tabIndex={this.getYearTabIndex(y)}
className={this.getYearClassNames(y)}
onMouseEnter={(ev) => onYearMouseEnter(ev, y)}
onMouseLeave={(ev) => onYearMouseLeave(ev, y)}
onMouseEnter={!this.props.usePointerEvent ? (ev) => onYearMouseEnter(ev, y) : undefined}
onPointerEnter={this.props.usePointerEvent ? (ev) => onYearMouseEnter(ev, y) : undefined}
onMouseLeave={!this.props.usePointerEvent ? (ev) => onYearMouseLeave(ev, y) : undefined}
onPointerLeave={this.props.usePointerEvent ? (ev) => onYearMouseLeave(ev, y) : undefined}
key={y}
aria-current={this.isCurrentYear(y) ? "date" : undefined}
>
Expand All @@ -273,7 +276,8 @@ export default class Year extends React.Component {
<div className={this.getYearContainerClassNames()}>
<div
className="react-datepicker__year-wrapper"
onMouseLeave={this.props.clearSelectingDate}
onMouseLeave={!this.props.usePointerEvent ? this.props.clearSelectingDate : undefined}
onPointerLeave={this.props.usePointerEvent ? this.props.clearSelectingDate : undefined}
>
{yearsList}
</div>
Expand Down
44 changes: 34 additions & 10 deletions test/calendar_test.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
import React from "react";
import Calendar from "../src/calendar";
import Month from "../src/month";
import Day from "../src/day";
import ReactDOM from "react-dom";
import TestUtils from "react-dom/test-utils";
import { render } from "@testing-library/react";
import { render, fireEvent } from "@testing-library/react";
import YearDropdown from "../src/year_dropdown";
import MonthDropdown from "../src/month_dropdown";
import MonthYearDropdown from "../src/month_year_dropdown";
Expand Down Expand Up @@ -859,21 +858,46 @@ describe("Calendar", () => {
expect(weekLabel.at(0).text()).toBe("Foo");
});

it("should track the currently hovered day", () => {
const calendar = mount(
it("should track the currently hovered day (Mouse Event)", () => {
const onDayMouseEnterSpy = jest.fn();

const { container } = render(
<Calendar
dateFormat={dateFormat}
dropdownMode="scroll"
onClickOutside={() => {}}
onSelect={() => {}}
onDayMouseEnter={onDayMouseEnterSpy}
/>,
);
const day = calendar.find(Day).first();
day.simulate("mouseenter");
const month = calendar.find(Month).first();
expect(month.prop("selectingDate")).toBeDefined();
expect(utils.isSameDay(month.prop("selectingDate"), day.prop("day"))).toBe(
true,

const day = container.querySelector(".react-datepicker__day");
fireEvent.mouseEnter(day);

expect(onDayMouseEnterSpy).toHaveBeenLastCalledWith(
utils.getStartOfWeek(utils.getStartOfMonth(utils.newDate())),
);
});

it("should track the currently hovered day (Pointer Event)", () => {
const onDayMouseEnterSpy = jest.fn();

const { container } = render(
<Calendar
dateFormat={dateFormat}
dropdownMode="scroll"
onClickOutside={() => {}}
onSelect={() => {}}
onDayMouseEnter={onDayMouseEnterSpy}
usePointerEvent
/>,
);

const day = container.querySelector(".react-datepicker__day");
fireEvent.pointerEnter(day);

expect(onDayMouseEnterSpy).toHaveBeenLastCalledWith(
utils.getStartOfWeek(utils.getStartOfMonth(utils.newDate())),
);
});

Expand Down
41 changes: 33 additions & 8 deletions test/datepicker_test.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2589,27 +2589,52 @@ describe("DatePicker", () => {
});

describe("Year picker", () => {
it("should call onYearMouseEnter and onYearMouseEnter", () => {
it("should call onYearMouseEnter and onYearMouseEnter (Mouse Event)", () => {
const onYearMouseEnterSpy = jest.fn();
const onYearMouseLeaveSpy = jest.fn();
const datePicker = mount(
const { container } = render(
<DatePicker
selected={new Date(2023, 0, 1)}
showYearPicker
onYearMouseEnter={onYearMouseEnterSpy}
onYearMouseLeave={onYearMouseLeaveSpy}
/>,
);

const dateInput = container.querySelector("input");
fireEvent.focus(dateInput);
const selectedYear = container.querySelector(
".react-datepicker__year-text--selected",
);

fireEvent.mouseEnter(selectedYear);
fireEvent.mouseLeave(selectedYear);

expect(onYearMouseEnterSpy).toHaveBeenCalled();
expect(onYearMouseLeaveSpy).toHaveBeenCalled();
});

it("should call onYearMouseEnter and onYearMouseEnter (Pointer Event)", () => {
const onYearMouseEnterSpy = jest.fn();
const onYearMouseLeaveSpy = jest.fn();
const { container } = render(
<DatePicker
selected={new Date(2023, 0, 1)}
showYearPicker
onYearMouseEnter={onYearMouseEnterSpy}
onYearMouseLeave={onYearMouseLeaveSpy}
usePointerEvent
/>,
);

const dateInputWrapper = datePicker.find("input");
dateInputWrapper.simulate("click");
const calendarWrapper = datePicker.find("Calendar");
const selectedYear = calendarWrapper.find(
const dateInput = container.querySelector("input");
fireEvent.focus(dateInput);
const selectedYear = container.querySelector(
".react-datepicker__year-text--selected",
);

selectedYear.simulate("mouseenter");
selectedYear.simulate("mouseleave");
fireEvent.pointerEnter(selectedYear);
fireEvent.pointerLeave(selectedYear);

expect(onYearMouseEnterSpy).toHaveBeenCalled();
expect(onYearMouseLeaveSpy).toHaveBeenCalled();
Expand Down
31 changes: 21 additions & 10 deletions test/day_test.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from "react";
import { render, fireEvent } from "@testing-library/react";
import { es } from "date-fns/locale";
import Day from "../src/day";
import { mount, shallow } from "enzyme";
Expand Down Expand Up @@ -915,20 +916,30 @@ describe("Day", () => {
});

describe("mouse enter", () => {
var onMouseEnterCalled;
it("should call onMouseEnter if day is hovered", () => {
const onMouseEnterSpy = jest.fn();

function onMouseEnter() {
onMouseEnterCalled = true;
}
const day = newDate();

beforeEach(() => {
onMouseEnterCalled = false;
const { container } = render(
<Day day={day} onMouseEnter={onMouseEnterSpy} />,
);

fireEvent.mouseEnter(container.querySelector(".react-datepicker__day"));
expect(onMouseEnterSpy).toHaveBeenCalled();
});

it("should call onMouseEnter if day is hovered", () => {
const shallowDay = renderDay(newDate(), { onMouseEnter });
shallowDay.find(".react-datepicker__day").simulate("mouseenter");
expect(onMouseEnterCalled).toBe(true);
it("should call onPointerEnter if day is hovered", () => {
const onMouseEnterSpy = jest.fn();

const day = newDate();

const { container } = render(
<Day day={day} onMouseEnter={onMouseEnterSpy} usePointerEvent />,
);

fireEvent.pointerEnter(container.querySelector(".react-datepicker__day"));
expect(onMouseEnterSpy).toHaveBeenCalled();
});
});

Expand Down
43 changes: 33 additions & 10 deletions test/month_test.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,19 +130,42 @@ describe("Month", () => {
expect(mouseLeaveCalled).toBe(true);
});

it("should call the provided onDayMouseEnter function", () => {
let dayMouseEntered = null;
it("should call the provided onDayMouseEnter (Mouse Event) function", () => {
const onDayMouseEnterSpy = jest.fn();

function onDayMouseEnter(day) {
dayMouseEntered = day;
}
const startDay = utils.newDate();

const month = mount(
<Month day={utils.newDate()} onDayMouseEnter={onDayMouseEnter} />,
const { container } = render(
<Month day={startDay} onDayMouseEnter={onDayMouseEnterSpy} />,
);

const day = container.querySelector(".react-datepicker__day");
fireEvent.mouseEnter(day);

expect(onDayMouseEnterSpy).toHaveBeenLastCalledWith(
utils.getStartOfWeek(utils.getStartOfMonth(startDay)),
);
});

it("should call the provided onDayMouseEnter (Pointer Event) function", () => {
const onDayMouseEnterSpy = jest.fn();

const startDay = utils.newDate();

const { container } = render(
<Month
day={startDay}
onDayMouseEnter={onDayMouseEnterSpy}
usePointerEvent
/>,
);

const day = container.querySelector(".react-datepicker__day");
fireEvent.pointerEnter(day);

expect(onDayMouseEnterSpy).toHaveBeenLastCalledWith(
utils.getStartOfWeek(utils.getStartOfMonth(startDay)),
);
const day = month.find(Day).first();
day.simulate("mouseenter");
expect(utils.isSameDay(day.prop("day"), dayMouseEntered)).toBe(true);
});

it("should use its month order in handleDayClick", () => {
Expand Down
Loading
Loading