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

Remove usages of react-onclickoutside to support React 19 #4979

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
"@types/eslint": "^8.56.10",
"@types/jest": "^29.5.12",
"@types/node": "20",
"@types/react-onclickoutside": "^6.7.10",
"@typescript-eslint/eslint-plugin": "^7.9.0",
"@typescript-eslint/parser": "^7.9.0",
"axe-core": "^4.4.1",
Expand Down Expand Up @@ -83,8 +82,7 @@
"@floating-ui/react": "^0.26.2",
"clsx": "^2.1.0",
"date-fns": "^3.3.1",
"prop-types": "^15.7.2",
"react-onclickoutside": "^6.13.0"
"prop-types": "^15.7.2"
},
"scripts": {
"eslint": "eslint --ext .js,.jsx,.ts,.tsx ./src",
Expand Down
1 change: 0 additions & 1 deletion rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ const dateFnsSubpackages = Object.keys(dateFnsPackageJson.exports)
const globals = {
react: "React",
"prop-types": "PropTypes",
"react-onclickoutside": "onClickOutside",
};

// NOTE:https://rollupjs.org/migration/#changed-defaults
Expand Down
8 changes: 0 additions & 8 deletions src/@types/react-onclickoutside/index.d.ts

This file was deleted.

16 changes: 12 additions & 4 deletions src/calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { clsx } from "clsx";
import React, { Component, createRef } from "react";

import CalendarContainer from "./calendar_container";
import { ClickOutsideWrapper } from "./click_outside_wrapper";
import {
newDate,
setMonth,
Expand Down Expand Up @@ -48,6 +49,7 @@ import Time from "./time";
import Year from "./year";
import YearDropdown from "./year_dropdown";

import type { ClickOutsideHandler } from "./click_outside_wrapper";
import type { Day } from "date-fns/types";

interface YearDropdownProps
Expand Down Expand Up @@ -154,7 +156,8 @@ type CalendarProps = React.PropsWithChildren &
onDayMouseEnter?: (date: Date) => void;
onMonthMouseLeave?: VoidFunction;
weekLabel?: string;
onClickOutside: React.MouseEventHandler<HTMLElement>;
onClickOutside: ClickOutsideHandler;
outsideClickIgnoreClass?: string;
previousMonthButtonLabel?: React.ReactNode;
previousYearButtonLabel?: string;
previousMonthAriaLabel?: string;
Expand Down Expand Up @@ -279,7 +282,7 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {

assignMonthContainer: void | undefined;

handleClickOutside = (event: React.MouseEvent<HTMLElement>): void => {
handleClickOutside = (event: MouseEvent): void => {
this.props.onClickOutside(event);
};

Expand Down Expand Up @@ -1082,7 +1085,12 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
render(): JSX.Element {
const Container = this.props.container || CalendarContainer;
return (
<div style={{ display: "contents" }} ref={this.containerRef}>
<ClickOutsideWrapper
onClickOutside={this.handleClickOutside}
style={{ display: "contents" }}
containerRef={this.containerRef}
ignoreClass={this.props.outsideClickIgnoreClass}
>
<Container
className={clsx("react-datepicker", this.props.className, {
"react-datepicker--time-only": this.props.showTimeSelectOnly,
Expand All @@ -1100,7 +1108,7 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
{this.renderInputTimeSection()}
{this.renderChildren()}
</Container>
</div>
</ClickOutsideWrapper>
);
}
}
69 changes: 69 additions & 0 deletions src/click_outside_wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React, { useCallback, useEffect, useRef } from "react";

export type ClickOutsideHandler = (event: MouseEvent) => void;

interface ClickOutsideWrapperProps {
onClickOutside: ClickOutsideHandler;
className?: string;
children: React.ReactNode;
containerRef?: React.MutableRefObject<HTMLDivElement | null>;
style?: React.CSSProperties;
ignoreClass?: string;
}

const useDetectClickOutside = (
onClickOutside: ClickOutsideHandler,
ignoreClass?: string,
) => {
const ref = useRef<HTMLDivElement | null>(null);
const onClickOutsideRef = useRef(onClickOutside);
onClickOutsideRef.current = onClickOutside;
const handleClickOutside = useCallback(
(event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
if (
!(
ignoreClass &&
event.target instanceof HTMLElement &&
event.target.classList.contains(ignoreClass)

Check warning on line 28 in src/click_outside_wrapper.tsx

View check run for this annotation

Codecov / codecov/patch

src/click_outside_wrapper.tsx#L27-L28

Added lines #L27 - L28 were not covered by tests
)
) {
onClickOutsideRef.current?.(event);
}
}
},
[ignoreClass],
);
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [handleClickOutside]);
return ref;
};

export const ClickOutsideWrapper: React.FC<ClickOutsideWrapperProps> = ({
children,
onClickOutside,
className,
containerRef,
style,
ignoreClass,
}) => {
const detectRef = useDetectClickOutside(onClickOutside, ignoreClass);
return (
<div
className={className}
style={style}
ref={(node: HTMLDivElement | null) => {
detectRef.current = node;
if (containerRef) {
containerRef.current = node;
}
}}
>
{children}
</div>
);
};
32 changes: 16 additions & 16 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { clsx } from "clsx";
import React, { Component, cloneElement } from "react";
import onClickOutside, {
type WrapperInstance,
type AdditionalProps,
} from "react-onclickoutside";

import Calendar from "./calendar";
import CalendarIcon from "./calendar_icon";
Expand Down Expand Up @@ -59,12 +55,13 @@ import PopperComponent from "./popper_component";
import Portal from "./portal";
import TabLoop from "./tab_loop";

import type { ClickOutsideHandler } from "./click_outside_wrapper";

export { default as CalendarContainer } from "./calendar_container";

export { registerLocale, setDefaultLocale, getDefaultLocale };

const outsideClickIgnoreClass = "react-datepicker-ignore-onclickoutside";
const WrappedCalendar = onClickOutside(Calendar);

export { ReactDatePickerCustomHeaderProps } from "./calendar";

Expand Down Expand Up @@ -102,7 +99,10 @@ interface PortalProps extends React.ComponentPropsWithoutRef<typeof Portal> {}
interface PopperComponentProps
extends React.ComponentPropsWithoutRef<typeof PopperComponent> {}

export type DatePickerProps = Omit<
// see https://github.com/microsoft/TypeScript/issues/31501
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type OmitUnion<T, K extends keyof any> = T extends any ? Omit<T, K> : never;
export type DatePickerProps = OmitUnion<
CalendarProps,
| "setOpen"
| "dateFormat"
Expand All @@ -126,10 +126,9 @@ export type DatePickerProps = Omit<
| "selectsMultiple"
| "dropdownMode"
> &
Partial<Pick<AdditionalProps, "excludeScrollbar">> &
Pick<CalendarIconProps, "icon"> &
Omit<PortalProps, "children" | "portalId"> &
Omit<
OmitUnion<PortalProps, "children" | "portalId"> &
OmitUnion<
PopperComponentProps,
| "className"
| "hidePopper"
Expand All @@ -151,7 +150,7 @@ export type DatePickerProps = Omit<
startOpen?: boolean;
onFocus?: React.FocusEventHandler<HTMLElement>;
onBlur?: React.FocusEventHandler<HTMLElement>;
onClickOutside?: React.MouseEventHandler<HTMLElement>;
onClickOutside?: ClickOutsideHandler;
onInputClick?: VoidFunction;
preventOpenOnFocus?: boolean;
closeOnScroll?: boolean | ((event: Event) => boolean);
Expand Down Expand Up @@ -383,7 +382,7 @@ export default class DatePicker extends Component<

inputFocusTimeout: ReturnType<typeof setTimeout> | undefined;

calendar: WrapperInstance<CalendarProps, typeof Calendar> | null = null;
calendar: Calendar | null = null;

input: HTMLElement | null = null;

Expand Down Expand Up @@ -568,7 +567,7 @@ export default class DatePicker extends Component<
this.setState({ focused: false });
};

handleCalendarClickOutside = (event: React.MouseEvent<HTMLElement>) => {
handleCalendarClickOutside = (event: MouseEvent) => {
if (!this.props.inline) {
this.setOpen(false);
}
Expand Down Expand Up @@ -932,8 +931,8 @@ export default class DatePicker extends Component<
? '.react-datepicker__month-text[tabindex="0"]'
: '.react-datepicker__day[tabindex="0"]';
const selectedItem =
this.calendar?.componentNode instanceof Element &&
this.calendar.componentNode.querySelector(selectorString);
this.calendar?.containerRef.current instanceof Element &&
this.calendar.containerRef.current.querySelector(selectorString);
selectedItem instanceof HTMLElement &&
selectedItem.focus({ preventScroll: true });

Expand Down Expand Up @@ -1214,7 +1213,8 @@ export default class DatePicker extends Component<
return null;
}
return (
<WrappedCalendar
<Calendar
showMonthYearDropdown={undefined}
ref={(elem) => {
this.calendar = elem;
}}
Expand All @@ -1241,7 +1241,7 @@ export default class DatePicker extends Component<
}
>
{this.props.children}
</WrappedCalendar>
</Calendar>
);
};

Expand Down
5 changes: 1 addition & 4 deletions src/month_dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { Component } from "react";
import onClickOutside from "react-onclickoutside";

import {
getMonthShortInLocale,
Expand All @@ -11,8 +10,6 @@ import MonthDropdownOptions from "./month_dropdown_options";
interface MonthDropdownOptionsProps
extends React.ComponentPropsWithoutRef<typeof MonthDropdownOptions> {}

const WrappedMonthDropdownOptions = onClickOutside(MonthDropdownOptions);

interface MonthDropdownProps
extends Omit<
MonthDropdownOptionsProps,
Expand Down Expand Up @@ -70,7 +67,7 @@ export default class MonthDropdown extends Component<
);

renderDropdown = (monthNames: string[]): JSX.Element => (
<WrappedMonthDropdownOptions
<MonthDropdownOptions
key="dropdown"
{...this.props}
monthNames={monthNames}
Expand Down
9 changes: 7 additions & 2 deletions src/month_dropdown_options.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { Component } from "react";

import { ClickOutsideWrapper } from "./click_outside_wrapper";

interface MonthDropdownOptionsProps {
onCancel: VoidFunction;
onChange: (month: number) => void;
Expand Down Expand Up @@ -40,9 +42,12 @@ export default class MonthDropdownOptions extends Component<MonthDropdownOptions

render(): JSX.Element {
return (
<div className="react-datepicker__month-dropdown">
<ClickOutsideWrapper
className="react-datepicker__month-dropdown"
onClickOutside={this.handleClickOutside}
>
{this.renderOptions()}
</div>
</ClickOutsideWrapper>
);
}
}
7 changes: 1 addition & 6 deletions src/month_year_dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { Component } from "react";
import onClickOutside from "react-onclickoutside";

import {
addMonths,
Expand All @@ -17,10 +16,6 @@ import MonthYearDropdownOptions from "./month_year_dropdown_options";
interface MonthYearDropdownOptionsProps
extends React.ComponentPropsWithoutRef<typeof MonthYearDropdownOptions> {}

const WrappedMonthYearDropdownOptions = onClickOutside(
MonthYearDropdownOptions,
);

interface MonthYearDropdownProps
extends Omit<MonthYearDropdownOptionsProps, "onChange" | "onCancel"> {
dropdownMode: "scroll" | "select";
Expand Down Expand Up @@ -96,7 +91,7 @@ export default class MonthYearDropdown extends Component<
};

renderDropdown = (): JSX.Element => (
<WrappedMonthYearDropdownOptions
<MonthYearDropdownOptions
key="dropdown"
{...this.props}
onChange={this.onChange}
Expand Down
10 changes: 9 additions & 1 deletion src/month_year_dropdown_options.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { clsx } from "clsx";
import React, { Component } from "react";

import { ClickOutsideWrapper } from "./click_outside_wrapper";
import {
addMonths,
formatDate,
Expand Down Expand Up @@ -103,6 +104,13 @@ export default class MonthYearDropdownOptions extends Component<
this.props.scrollableMonthYearDropdown,
});

return <div className={dropdownClass}>{this.renderOptions()}</div>;
return (
<ClickOutsideWrapper
className={dropdownClass}
onClickOutside={this.handleClickOutside}
>
{this.renderOptions()}
</ClickOutsideWrapper>
);
}
}
Loading
Loading