Skip to content

Commit

Permalink
Merge branch 'main' into issue-4076/fix/emit-onCalendarClose-onsame-r…
Browse files Browse the repository at this point in the history
…ange-select
  • Loading branch information
Balaji Sridharan committed Jan 3, 2024
2 parents c1ae2fa + 57c0438 commit 0188371
Show file tree
Hide file tree
Showing 20 changed files with 561 additions and 315 deletions.
10 changes: 7 additions & 3 deletions README.md
Expand Up @@ -134,14 +134,18 @@ The `main` branch contains the latest version of the Datepicker component.

To begin local development:

1. `yarn install`
2. `yarn build-dev`
3. `yarn start`
1. Run `yarn link` from project root
2. Run `cd docs-site && yarn link react-datepicker`
3. Run `yarn install` from project root
4. Run `yarn build` from project root
5. Run `yarn start` from project root

The last step starts documentation app as a simple webserver on http://localhost:3000.

You can run `yarn test` to execute the test suite and linters. To help you develop the component we’ve set up some tests that cover the basic functionality (can be found in `/tests`). Even though we’re big fans of testing, this only covers a small piece of the component. We highly recommend you add tests when you’re adding new functionality.

Please refer to `CONTRIBUTING.md` file for more details about getting set up.

### The examples

The examples are hosted within the docs folder and are ran in the simple app that loads the Datepicker. To extend the examples with a new example, you can simply duplicate one of the existing examples and change the unique properties of your example.
Expand Down
2 changes: 1 addition & 1 deletion docs-site/package.json
Expand Up @@ -33,6 +33,6 @@
},
"devDependencies": {
"raw-loader": "^4.0.2",
"sass": "^1.69.5"
"sass": "^1.69.6"
}
}
5 changes: 5 additions & 0 deletions docs-site/src/components/Examples/index.js
Expand Up @@ -102,6 +102,7 @@ import ExternalForm from "../../examples/externalForm";
import CalendarIcon from "../../examples/calendarIcon";
import CalendarIconExternal from "../../examples/calendarIconExternal";
import CalendarIconSvgIcon from "../../examples/calendarIconSvgIcon";
import ToggleCalendarOnIconClick from "../../examples/toggleCalendarOnIconClick";

import "./style.scss";
import "react-datepicker/dist/react-datepicker.css";
Expand Down Expand Up @@ -129,6 +130,10 @@ export default class exampleComponents extends React.Component {
title: "Calendar Icon using External Lib",
component: CalendarIconExternal,
},
{
title: "Toggle Calendar open status on click of the calendar icon",
component: ToggleCalendarOnIconClick,
},
{
title: "Calendar container",
component: CalendarContainer,
Expand Down
6 changes: 4 additions & 2 deletions docs-site/src/examples/renderCustomMonth.js
@@ -1,6 +1,8 @@
() => {
const renderMonthContent = (month, shortMonth, longMonth) => {
const tooltipText = `Tooltip for month: ${longMonth}`;
const renderMonthContent = (month, shortMonth, longMonth, day) => {
const fullYear = new Date(day).getFullYear();
const tooltipText = `Tooltip for month: ${longMonth} ${fullYear}`;

return <span title={tooltipText}>{shortMonth}</span>;
};
return (
Expand Down
11 changes: 11 additions & 0 deletions docs-site/src/examples/toggleCalendarOnIconClick.js
@@ -0,0 +1,11 @@
() => {
const [selectedDate, setSelectedDate] = useState(new Date());
return (
<DatePicker
showIcon
toggleCalendarOnIconClick
selected={selectedDate}
onChange={(date) => setSelectedDate(date)}
/>
);
};
8 changes: 4 additions & 4 deletions docs-site/yarn.lock
Expand Up @@ -7823,10 +7823,10 @@ sass-loader@^12.3.0:
klona "^2.0.4"
neo-async "^2.6.2"

sass@^1.69.5:
version "1.69.5"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.69.5.tgz#23e18d1c757a35f2e52cc81871060b9ad653dfde"
integrity sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==
sass@^1.69.6:
version "1.69.6"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.69.6.tgz#88ae1f93facc46d2da9b0bdd652d65068bcfa397"
integrity sha512-qbRr3k9JGHWXCvZU77SD2OTwUlC+gNT+61JOLcmLm+XqH4h/5D+p4IIsxvpkB89S9AwJOyb5+rWNpIucaFxSFQ==
dependencies:
chokidar ">=3.0.0 <4.0.0"
immutable "^4.0.0"
Expand Down
1 change: 1 addition & 0 deletions docs/calendar_icon.md
Expand Up @@ -4,3 +4,4 @@
| ----------- | --------------------- | ------------- | ----------- |
| `className` | `string` | `""` | |
| `icon` | `union(string\|node)` | | |
| `onClick` | `func` | | |
1 change: 1 addition & 0 deletions docs/index.md
Expand Up @@ -147,6 +147,7 @@
| `timeIntervals` | `number` | `30` | |
| `title` | `string` | | |
| `todayButton` | `node` | | |
| `toggleCalendarOnIconClick` | `func` | `false` | |
| `useShortMonthInDropdown` | `bool` | | |
| `useWeekdaysShort` | `bool` | | |
| `value` | `string` | | |
Expand Down
2 changes: 1 addition & 1 deletion examples/hello-world/package.json
Expand Up @@ -4,7 +4,7 @@
"private": true,
"dependencies": {
"react": "^18.2.0",
"react-datepicker": "^4.23.0",
"react-datepicker": "^4.25.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1"
},
Expand Down
8 changes: 4 additions & 4 deletions examples/hello-world/yarn.lock
Expand Up @@ -7311,10 +7311,10 @@ react-app-polyfill@^3.0.0:
regenerator-runtime "^0.13.9"
whatwg-fetch "^3.6.2"

react-datepicker@^4.23.0:
version "4.23.0"
resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-4.23.0.tgz#0da50b3815b3350b7df6cf0dbc63e37c6c3079b4"
integrity sha512-w+msqlOZ14v6H1UknTKtZw/dw9naFMgAOspf59eY130gWpvy5dvKj/bgsFICDdvxB7PtKWxDcbGlAqCloY1d2A==
react-datepicker@^4.25.0:
version "4.25.0"
resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-4.25.0.tgz#86b3ee8ac764bad1650046d0cf9280837bf6d845"
integrity sha512-zB7CSi44SJ0sqo8hUQ3BF1saE/knn7u25qEMTO1CQGofY1VAKahO8k9drZtp0cfW1DMfoYLR3uSY1/uMvbEzbg==
dependencies:
"@popperjs/core" "^2.11.8"
classnames "^2.2.6"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -2,7 +2,7 @@
"author": "HackerOne",
"name": "react-datepicker",
"description": "A simple and reusable datepicker component for React",
"version": "4.23.0",
"version": "4.25.0",
"license": "MIT",
"homepage": "https://github.com/Hacker0x01/react-datepicker",
"main": "dist/index.js",
Expand Down
14 changes: 13 additions & 1 deletion src/calendar_icon.jsx
@@ -1,12 +1,21 @@
import React from "react";
import PropTypes from "prop-types";

const CalendarIcon = ({ icon, className = "" }) => {
const CalendarIcon = ({ icon, className = "", onClick }) => {
const defaultClass = "react-datepicker__calendar-icon";

if (React.isValidElement(icon)) {
return React.cloneElement(icon, {
className: `${icon.props.className || ""} ${defaultClass} ${className}`,
onClick: (e) => {
if (typeof icon.props.onClick === "function") {
icon.props.onClick(e);
}

if (typeof onClick === "function") {
onClick(e);
}
},
});
}

Expand All @@ -15,6 +24,7 @@ const CalendarIcon = ({ icon, className = "" }) => {
<i
className={`${defaultClass} ${icon} ${className}`}
aria-hidden="true"
onClick={onClick}
/>
);
}
Expand All @@ -25,6 +35,7 @@ const CalendarIcon = ({ icon, className = "" }) => {
className={`${defaultClass} ${className}`}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 448 512"
onClick={onClick}
>
<path d="M96 32V64H48C21.5 64 0 85.5 0 112v48H448V112c0-26.5-21.5-48-48-48H352V32c0-17.7-14.3-32-32-32s-32 14.3-32 32V64H160V32c0-17.7-14.3-32-32-32S96 14.3 96 32zM448 192H0V464c0 26.5 21.5 48 48 48H400c26.5 0 48-21.5 48-48V192z" />
</svg>
Expand All @@ -34,6 +45,7 @@ const CalendarIcon = ({ icon, className = "" }) => {
CalendarIcon.propTypes = {
icon: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
className: PropTypes.string,
onClick: PropTypes.func,
};

export default CalendarIcon;
2 changes: 1 addition & 1 deletion src/date_utils.js
Expand Up @@ -459,7 +459,7 @@ export function isMonthDisabled(
);
}

export function isMonthinRange(startDate, endDate, m, day) {
export function isMonthInRange(startDate, endDate, m, day) {
const startDateYear = getYear(startDate);
const startDateMonth = getMonth(startDate);
const endDateYear = getYear(endDate);
Expand Down
23 changes: 21 additions & 2 deletions src/index.jsx
Expand Up @@ -130,6 +130,7 @@ export default class DatePicker extends React.Component {
excludeScrollbar: true,
customTimeInput: null,
calendarStartDay: undefined,
toggleCalendarOnIconClick: false,
};
}

Expand Down Expand Up @@ -184,6 +185,7 @@ export default class DatePicker extends React.Component {
injectTimes: PropTypes.array,
inline: PropTypes.bool,
isClearable: PropTypes.bool,
toggleCalendarOnIconClick: PropTypes.func,
showIcon: PropTypes.bool,
icon: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
calendarIconClassname: PropTypes.string,
Expand Down Expand Up @@ -716,6 +718,10 @@ export default class DatePicker extends React.Component {
}
};

toggleCalendar = () => {
this.setOpen(!this.state.open);
};

handleTimeChange = (time) => {
const selected = this.props.selected
? this.props.selected
Expand Down Expand Up @@ -1252,15 +1258,28 @@ export default class DatePicker extends React.Component {
};

renderInputContainer() {
const { showIcon, icon, calendarIconClassname } = this.props;
const { showIcon, icon, calendarIconClassname, toggleCalendarOnIconClick } =
this.props;
const { open } = this.state;

return (
<div
className={`react-datepicker__input-container${
showIcon ? " react-datepicker__view-calendar-icon" : ""
}`}
>
{showIcon && (
<CalendarIcon icon={icon} className={calendarIconClassname} />
<CalendarIcon
icon={icon}
className={`${calendarIconClassname} ${
open && "react-datepicker-ignore-onclickoutside"
}`}
{...(toggleCalendarOnIconClick
? {
onClick: this.toggleCalendar,
}
: null)}
/>
)}
{this.state.isRenderAriaLiveMessage && this.renderAriaLiveRegion()}
{this.renderDateInput()}
Expand Down
17 changes: 9 additions & 8 deletions src/month.jsx
Expand Up @@ -191,15 +191,15 @@ export default class Month extends React.Component {
}

if (selectsStart && endDate) {
return utils.isMonthinRange(selectingDate, endDate, m, day);
return utils.isMonthInRange(selectingDate, endDate, m, day);
}

if (selectsEnd && startDate) {
return utils.isMonthinRange(startDate, selectingDate, m, day);
return utils.isMonthInRange(startDate, selectingDate, m, day);
}

if (selectsRange && startDate && !endDate) {
return utils.isMonthinRange(startDate, selectingDate, m, day);
return utils.isMonthInRange(startDate, selectingDate, m, day);
}

return false;
Expand Down Expand Up @@ -532,7 +532,7 @@ export default class Month extends React.Component {
utils.getMonth(preSelection) === m,
"react-datepicker__month-text--in-selecting-range":
this.isInSelectingRangeMonth(m),
"react-datepicker__month-text--in-range": utils.isMonthinRange(
"react-datepicker__month-text--in-range": utils.isMonthInRange(
startDate,
endDate,
m,
Expand Down Expand Up @@ -626,11 +626,12 @@ export default class Month extends React.Component {
};

getMonthContent = (m) => {
const { showFullMonthYearPicker, renderMonthContent, locale } = this.props;
const { showFullMonthYearPicker, renderMonthContent, locale, day } =
this.props;
const shortMonthText = utils.getMonthShortInLocale(m, locale);
const fullMonthText = utils.getMonthInLocale(m, locale);
if (renderMonthContent) {
return renderMonthContent(m, shortMonthText, fullMonthText);
return renderMonthContent(m, shortMonthText, fullMonthText, day);
}
return showFullMonthYearPicker ? fullMonthText : shortMonthText;
};
Expand Down Expand Up @@ -753,8 +754,8 @@ export default class Month extends React.Component {
{showMonthYearPicker
? this.renderMonths()
: showQuarterYearPicker
? this.renderQuarters()
: this.renderWeeks()}
? this.renderQuarters()
: this.renderWeeks()}
</div>
);
}
Expand Down
56 changes: 56 additions & 0 deletions test/calendar_icon.test.js
@@ -1,5 +1,6 @@
import React from "react";
import { mount } from "enzyme";
import { render, fireEvent } from "@testing-library/react";
import CalendarIcon from "../src/calendar_icon";
import { IconParkSolidApplication } from "./helper_components/calendar_icon";

Expand All @@ -12,6 +13,14 @@ afterAll(() => {
});

describe("CalendarIcon", () => {
let onClickMock;
beforeEach(() => {
onClickMock = jest.fn();
});
afterEach(() => {
onClickMock.mockClear();
});

it("renders a custom SVG icon when provided", () => {
const wrapper = mount(
<CalendarIcon showIcon icon={<IconParkSolidApplication />} />,
Expand All @@ -30,4 +39,51 @@ describe("CalendarIcon", () => {
const wrapper = mount(<CalendarIcon showIcon />);
expect(wrapper.find("svg.react-datepicker__calendar-icon")).toHaveLength(1);
});

it("should fire onClick event when the icon is clicked", () => {
const { container } = render(
<CalendarIcon showIcon onClick={onClickMock} />,
);

const icon = container.querySelector("svg.react-datepicker__calendar-icon");
fireEvent.click(icon);

expect(onClickMock).toHaveBeenCalledTimes(1);
});

it("should fire onClick event on the click of font-awesome icon when provided", () => {
const { container } = render(
<CalendarIcon showIcon icon="fa-example-icon" onClick={onClickMock} />,
);

const icon = container.querySelector("i.fa-example-icon");
fireEvent.click(icon);

expect(onClickMock).toHaveBeenCalledTimes(1);
});

it("should fire onClick event on the click of custom icon component when provided", () => {
const onClickCustomIcon = jest.fn();

const { container } = render(
<CalendarIcon
icon={
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 48 48"
onClick={onClickCustomIcon}
/>
}
onClick={onClickMock}
/>,
);

const icon = container.querySelector("svg.react-datepicker__calendar-icon");
fireEvent.click(icon);

expect(onClickMock).toHaveBeenCalledTimes(1);
expect(onClickCustomIcon).toHaveBeenCalledTimes(1);
});
});

0 comments on commit 0188371

Please sign in to comment.