From 695497e6da833c8a01641c43901c6a8d87a12295 Mon Sep 17 00:00:00 2001 From: Wojciech Maj Date: Tue, 9 Jan 2024 14:18:07 +0100 Subject: [PATCH] Add support for showNeighboringCentury and showNeighboringDecade props --- packages/react-calendar/README.md | 2 + packages/react-calendar/src/Calendar.css | 4 +- packages/react-calendar/src/Calendar.spec.tsx | 30 ++++++++++++++ packages/react-calendar/src/Calendar.tsx | 34 +++++++++++++++- packages/react-calendar/src/CenturyView.tsx | 2 + .../src/CenturyView/Decade.spec.tsx | 1 + .../react-calendar/src/CenturyView/Decade.tsx | 20 +++++++++- .../src/CenturyView/Decades.tsx | 14 +++++-- packages/react-calendar/src/DecadeView.tsx | 2 + .../src/DecadeView/Year.spec.tsx | 1 + .../react-calendar/src/DecadeView/Year.tsx | 20 +++++++++- .../react-calendar/src/DecadeView/Years.tsx | 14 +++++-- test/Test.tsx | 8 ++++ test/ViewOptions.tsx | 40 +++++++++++++++++++ 14 files changed, 179 insertions(+), 13 deletions(-) diff --git a/packages/react-calendar/README.md b/packages/react-calendar/README.md index 00fe2e7f..f0acae92 100644 --- a/packages/react-calendar/README.md +++ b/packages/react-calendar/README.md @@ -142,6 +142,8 @@ Displays a complete, interactive calendar. | showDoubleView | Whether to show two months/years/… at a time instead of one. Defaults `showFixedNumberOfWeeks` prop to be `true`. | `false` | `true` | | showFixedNumberOfWeeks | Whether to always show fixed number of weeks (6). Forces `showNeighboringMonth` prop to be `true`. | `false` | `true` | | showNavigation | Whether a navigation bar with arrows and title shall be rendered. | `true` | `false` | +| showNeighboringCentury | Whether decades from next century shall be rendered to fill the entire last row in. | `false` | `true` | +| showNeighboringDecade | Whether years from next decade shall be rendered to fill the entire last row in. | `false` | `true` | | showNeighboringMonth | Whether days from previous or next month shall be rendered if the month doesn't start on the first day of the week or doesn't end on the last day of the week, respectively. | `true` | `false` | | showWeekNumbers | Whether week numbers shall be shown at the left of MonthView or not. | `false` | `true` | | tileClassName | Class name(s) that will be applied to a given calendar item (day on month view, month on year view and so on). | n/a | | diff --git a/packages/react-calendar/src/Calendar.css b/packages/react-calendar/src/Calendar.css index 8a4c8717..4aa38f07 100644 --- a/packages/react-calendar/src/Calendar.css +++ b/packages/react-calendar/src/Calendar.css @@ -85,7 +85,9 @@ color: #d10000; } -.react-calendar__month-view__days__day--neighboringMonth { +.react-calendar__month-view__days__day--neighboringMonth, +.react-calendar__decade-view__years__year--neighboringDecade, +.react-calendar__century-view__decades__decade--neighboringCentury { color: #757575; } diff --git a/packages/react-calendar/src/Calendar.spec.tsx b/packages/react-calendar/src/Calendar.spec.tsx index 8127b979..b76526d9 100644 --- a/packages/react-calendar/src/Calendar.spec.tsx +++ b/packages/react-calendar/src/Calendar.spec.tsx @@ -301,6 +301,36 @@ describe('Calendar', () => { expect(firstDayTileTimeAbbr).toHaveAccessibleName(format(new Date(2016, 11, 26))); }); + it('passes showNeighboringDecade flag to DecadeView component given showNeighboringDecade flag', () => { + const activeStartDate = new Date(2001, 0, 1); + + const { container } = render( + , + ); + + const lastYearTile = container.querySelector( + '.react-calendar__tile:last-child', + ) as HTMLDivElement; + + // The last year that this calendar should show is 2029. + expect(lastYearTile).toHaveAccessibleName('2012'); + }); + + it('passes showNeighboringCentury flag to CenturyView component given showNeighboringCentury flag', () => { + const activeStartDate = new Date(2001, 0, 1); + + const { container } = render( + , + ); + + const lastDecadeTile = container.querySelector( + '.react-calendar__tile:last-child', + ) as HTMLDivElement; + + // The last decade that this calendar should show is 2111 – 2120. + expect(lastDecadeTile).toHaveAccessibleName('2111 – 2120'); + }); + describe('displays initial view properly', () => { it('displays a view with a given value when defaultValue is given', () => { const defaultValue = new Date(2017, 0, 15); diff --git a/packages/react-calendar/src/Calendar.tsx b/packages/react-calendar/src/Calendar.tsx index cfbf93a9..eec48ddf 100644 --- a/packages/react-calendar/src/Calendar.tsx +++ b/packages/react-calendar/src/Calendar.tsx @@ -367,6 +367,20 @@ export type CalendarProps = { * @example false */ showNavigation?: boolean; + /** + * Whether decades from next century shall be rendered to fill the entire last row in. + * + * @default false + * @example true + */ + showNeighboringCentury?: boolean; + /** + * Whether years from next decade shall be rendered to fill the entire last row in. + * + * @default false + * @example true + */ + showNeighboringDecade?: boolean; /** * Whether days from previous or next month shall be rendered if the month doesn't start on the first day of the week or doesn't end on the last day of the week, respectively. * @@ -643,6 +657,8 @@ const Calendar = forwardRef(function Calendar(props: CalendarProps, ref) { showDoubleView, showFixedNumberOfWeeks, showNavigation = true, + showNeighboringCentury, + showNeighboringDecade, showNeighboringMonth = true, showWeekNumbers, tileClassName, @@ -1022,10 +1038,22 @@ const Calendar = forwardRef(function Calendar(props: CalendarProps, ref) { switch (view) { case 'century': { - return ; + return ( + + ); } case 'decade': { - return ; + return ( + + ); } case 'year': { return ( @@ -1170,6 +1198,8 @@ Calendar.propTypes = { showDoubleView: PropTypes.bool, showFixedNumberOfWeeks: PropTypes.bool, showNavigation: PropTypes.bool, + showNeighboringCentury: PropTypes.bool, + showNeighboringDecade: PropTypes.bool, showNeighboringMonth: PropTypes.bool, showWeekNumbers: PropTypes.bool, tileClassName: PropTypes.oneOfType([PropTypes.func, isClassName]), diff --git a/packages/react-calendar/src/CenturyView.tsx b/packages/react-calendar/src/CenturyView.tsx index 44676307..821b5f1f 100644 --- a/packages/react-calendar/src/CenturyView.tsx +++ b/packages/react-calendar/src/CenturyView.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import Decades from './CenturyView/Decades.js'; @@ -19,6 +20,7 @@ const CenturyView: React.FC = function CenturyView(props) { CenturyView.propTypes = { ...tileGroupProps, + showNeighboringCentury: PropTypes.bool, }; export default CenturyView; diff --git a/packages/react-calendar/src/CenturyView/Decade.spec.tsx b/packages/react-calendar/src/CenturyView/Decade.spec.tsx index d4402eec..bc489b50 100644 --- a/packages/react-calendar/src/CenturyView/Decade.spec.tsx +++ b/packages/react-calendar/src/CenturyView/Decade.spec.tsx @@ -7,6 +7,7 @@ import Decade from './Decade.js'; const tileProps = { activeStartDate: new Date(2018, 0, 1), classes: ['react-calendar__tile'], + currentCentury: 2001, date: new Date(2011, 0, 1), }; diff --git a/packages/react-calendar/src/CenturyView/Decade.tsx b/packages/react-calendar/src/CenturyView/Decade.tsx index ed15f0e0..92ef1d61 100644 --- a/packages/react-calendar/src/CenturyView/Decade.tsx +++ b/packages/react-calendar/src/CenturyView/Decade.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { getDecadeStart, getDecadeEnd } from '@wojtekmaj/date-utils'; +import { getDecadeStart, getDecadeEnd, getCenturyStart } from '@wojtekmaj/date-utils'; import Tile from '../Tile.js'; @@ -10,6 +10,7 @@ const className = 'react-calendar__century-view__decades__decade'; type DecadeProps = { classes?: string[]; + currentCentury: number; /** * Function called to override default formatting of year in the top navigation section. Can be used to use your own formatting function. * @@ -23,15 +24,30 @@ type DecadeProps = { export default function Decade({ classes = [], + currentCentury, formatYear = defaultFormatYear, ...otherProps }: DecadeProps) { const { date, locale } = otherProps; + const classesProps: string[] = []; + + if (classes) { + classesProps.push(...classes); + } + + if (className) { + classesProps.push(className); + } + + if (getCenturyStart(date).getFullYear() !== currentCentury) { + classesProps.push(`${className}--neighboringCentury`); + } + return ( , 'dateTransform' | 'dateType' | 'end' | 'renderTile' | 'start' > & - Omit, 'classes' | 'date'>; + Omit, 'classes' | 'currentCentury' | 'date'>; export default function Decades(props: DecadesProps) { - const { activeStartDate, hover, value, valueType, ...otherProps } = props; + const { activeStartDate, hover, showNeighboringCentury, value, valueType, ...otherProps } = props; const start = getBeginOfCenturyYear(activeStartDate); - const end = start + 99; + const end = start + (showNeighboringCentury ? 119 : 99); return ( )} diff --git a/packages/react-calendar/src/DecadeView.tsx b/packages/react-calendar/src/DecadeView.tsx index 3478482e..e3a67d30 100644 --- a/packages/react-calendar/src/DecadeView.tsx +++ b/packages/react-calendar/src/DecadeView.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import Years from './DecadeView/Years.js'; @@ -19,6 +20,7 @@ const DecadeView: React.FC = function DecadeView(props) { DecadeView.propTypes = { ...tileGroupProps, + showNeighboringDecade: PropTypes.bool, }; export default DecadeView; diff --git a/packages/react-calendar/src/DecadeView/Year.spec.tsx b/packages/react-calendar/src/DecadeView/Year.spec.tsx index b215d16f..bb46e670 100644 --- a/packages/react-calendar/src/DecadeView/Year.spec.tsx +++ b/packages/react-calendar/src/DecadeView/Year.spec.tsx @@ -7,6 +7,7 @@ import Year from './Year.js'; const tileProps = { activeStartDate: new Date(2018, 0, 1), classes: ['react-calendar__tile'], + currentDecade: 2011, date: new Date(2018, 0, 1), }; diff --git a/packages/react-calendar/src/DecadeView/Year.tsx b/packages/react-calendar/src/DecadeView/Year.tsx index 4f8028d8..e7b9b8ae 100644 --- a/packages/react-calendar/src/DecadeView/Year.tsx +++ b/packages/react-calendar/src/DecadeView/Year.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { getYearStart, getYearEnd } from '@wojtekmaj/date-utils'; +import { getYearStart, getYearEnd, getDecadeStart } from '@wojtekmaj/date-utils'; import Tile from '../Tile.js'; @@ -9,6 +9,7 @@ const className = 'react-calendar__decade-view__years__year'; type YearProps = { classes?: string[]; + currentDecade: number; /** * Function called to override default formatting of year in the top navigation section. Can be used to use your own formatting function. * @@ -22,15 +23,30 @@ type YearProps = { export default function Year({ classes = [], + currentDecade, formatYear = defaultFormatYear, ...otherProps }: YearProps) { const { date, locale } = otherProps; + const classesProps: string[] = []; + + if (classes) { + classesProps.push(...classes); + } + + if (className) { + classesProps.push(className); + } + + if (getDecadeStart(date).getFullYear() !== currentDecade) { + classesProps.push(`${className}--neighboringDecade`); + } + return ( , 'dateTransform' | 'dateType' | 'end' | 'renderTile' | 'start' > & - Omit, 'classes' | 'date'>; + Omit, 'classes' | 'currentDecade' | 'date'>; export default function Years(props: YearsProps) { - const { activeStartDate, hover, value, valueType, ...otherProps } = props; + const { activeStartDate, hover, showNeighboringDecade, value, valueType, ...otherProps } = props; const start = getBeginOfDecadeYear(activeStartDate); - const end = start + 9; + const end = start + (showNeighboringDecade ? 11 : 9); return ( )} diff --git a/test/Test.tsx b/test/Test.tsx index 15d4f5f7..c587511a 100644 --- a/test/Test.tsx +++ b/test/Test.tsx @@ -83,6 +83,8 @@ export default function Test() { const [selectRange, setSelectRange] = useState(false); const [showDoubleView, setShowDoubleView] = useState(false); const [showFixedNumberOfWeeks, setShowFixedNumberOfWeeks] = useState(false); + const [showNeighboringCentury, setShowNeighboringCentury] = useState(false); + const [showNeighboringDecade, setShowNeighboringDecade] = useState(false); const [showNeighboringMonth, setShowNeighboringMonth] = useState(true); const [showWeekNumbers, setShowWeekNumbers] = useState(false); const [value, setValue] = useState(now); @@ -153,6 +155,8 @@ export default function Test() { selectRange, showDoubleView, showFixedNumberOfWeeks, + showNeighboringCentury, + showNeighboringDecade, showNeighboringMonth, showWeekNumbers, tileClassName, @@ -192,10 +196,14 @@ export default function Test() { diff --git a/test/ViewOptions.tsx b/test/ViewOptions.tsx index ca661a1c..1a0acdcc 100644 --- a/test/ViewOptions.tsx +++ b/test/ViewOptions.tsx @@ -1,10 +1,14 @@ type ViewOptionsProps = { setShowDoubleView: (showDoubleView: boolean) => void; setShowFixedNumberOfWeeks: (showFixedNumberOfWeeks: boolean) => void; + setShowNeighboringCentury: (showNeighboringCentury: boolean) => void; + setShowNeighboringDecade: (showNeighboringDecade: boolean) => void; setShowNeighboringMonth: (showNeighboringMonth: boolean) => void; setShowWeekNumbers: (showWeekNumbers: boolean) => void; showDoubleView: boolean; showFixedNumberOfWeeks: boolean; + showNeighboringCentury: boolean; + showNeighboringDecade: boolean; showNeighboringMonth: boolean; showWeekNumbers: boolean; }; @@ -12,10 +16,14 @@ type ViewOptionsProps = { export default function ViewOptions({ setShowDoubleView, setShowFixedNumberOfWeeks, + setShowNeighboringCentury, + setShowNeighboringDecade, setShowNeighboringMonth, setShowWeekNumbers, showDoubleView, showFixedNumberOfWeeks, + showNeighboringCentury, + showNeighboringDecade, showNeighboringMonth, showWeekNumbers, }: ViewOptionsProps) { @@ -37,6 +45,18 @@ export default function ViewOptions({ setShowWeekNumbers(checked); } + function onShowNeighboringCenturyChange(event: React.ChangeEvent) { + const { checked } = event.target; + + setShowNeighboringCentury(checked); + } + + function onShowNeighboringDecadeChange(event: React.ChangeEvent) { + const { checked } = event.target; + + setShowNeighboringDecade(checked); + } + function onShowNeighboringMonthChange(event: React.ChangeEvent) { const { checked } = event.target; @@ -68,6 +88,26 @@ export default function ViewOptions({ +
+ + +
+ +
+ + +
+