diff --git a/example/src/App.js b/example/src/App.js index 2dbba28f..dc049cc9 100644 --- a/example/src/App.js +++ b/example/src/App.js @@ -32,6 +32,7 @@ import { Loader, Select, Option, + DatePicker, // IMPORT_INJECTOR } from '@cision/rover-ui'; @@ -49,6 +50,8 @@ const App = () => { const [inputTimeValue, setInputTimeValue] = useState(''); const [isModalOpen, setIsModalOpen] = useState(false); const [isKiteVisible, setIsKiteVisible] = useState(false); + const [selectedDay, setSelectedDay] = useState(new Date()); + const [showInlineCalendar, setShowInlineCalendar] = useState(false); const toggleTooltip = function () { setTooltipOpen((prev) => !prev); @@ -61,6 +64,15 @@ const App = () => { ); + const handleDateClick = (day) => { + setSelectedDay(day); + }; + + const handleInlineDateClick = (day) => { + setSelectedDay(day); + setShowInlineCalendar(false); + }; + return (
@@ -518,6 +530,34 @@ const App = () => { {' '} (required)
+
+ +
+ setShowInlineCalendar(true)} + /> + {showInlineCalendar && ( + + + + )} +
{/** USAGE_INJECTOR */}
diff --git a/package.json b/package.json index bd82228b..dec92450 100644 --- a/package.json +++ b/package.json @@ -158,6 +158,7 @@ "@cision/react-container-query": "1.0.0-alpha.3", "classnames": "^2.2.6", "lodash": "^4.17.19", - "nanoid": "^3.1.23" + "nanoid": "^3.1.23", + "react-day-picker": "^7.4.10" } } diff --git a/src/components/DatePicker/DatePicker.module.css b/src/components/DatePicker/DatePicker.module.css new file mode 100644 index 00000000..f0fe6ca8 --- /dev/null +++ b/src/components/DatePicker/DatePicker.module.css @@ -0,0 +1,132 @@ +.container { + display: inline-block; +} + +.wrapper { + border-radius: var(--rvr-border-radius); +} + +.interactionDisabled { + cursor: default; +} + +.navBar { + position: relative; +} + +.navButtonPrev, +.navButtonNext { + position: absolute; + border: solid var(--rvr-color-font); + border-width: 3px 3px 0px 0px; + display: inline-block; + padding: 5px; +} + +.navButtonPrev { + transform: rotate(-135deg); + top: 18px; + left: 18px; +} + +.navButtonNext { + transform: rotate(45deg); + top: 18px; + right: 18px; +} + +.navButtonInteractionDisabled { + opacity: 0; +} + +.months { + display: flex; + flex-wrap: wrap; + justify-content: center; +} + +.month { + padding: 12px; +} + +.caption { + font-weight: var(--rvr-font-weight-bold); + text-align: center; + color: var(--rvr-black); +} + +.weekdaysBody { + display: table-header-group; +} + +.weekdaysRow { + display: table-row; +} + +.weekday { + color: var(--rvr-color-font); + display: table-cell; + text-align: center; + padding: 4px 6px; +} +.weekday > abbr { + text-decoration: none; +} + +.weekNumber { + display: table-cell; + color: var(--rvr-color-font); + padding: 4px 6px; + border-right: solid 1px var(--rvr-color-disabled); +} + +.body { + display: table-row-group; +} + +.week { + display: table-row; +} + +.day { + display: table-cell; + text-align: center; + padding: 4px 6px; + border-radius: var(--rvr-border-radius); + color: var(--rvr-black); +} + +.day[aria-disabled='false'] { + cursor: pointer; +} + +.day:hover[aria-disabled='false'][aria-selected='false'] { + background-color: var(--rvr-color-highlight); +} + +.footer { + display: flex; + justify-content: center; +} + +.todayButton { + color: var(--rvr-btn-color-link-active); +} + +.today { + text-decoration: underline; + font-weight: var(--rvr-font-weight-bold); +} + +.selected { + background-color: var(--rvr-color-primary); + color: var(--rvr-white); +} + +.disabled { + color: var(--rvr-color-disabled); +} + +.outside { + color: var(--rvr-color-disabled); +} diff --git a/src/components/DatePicker/DatePicker.test.tsx b/src/components/DatePicker/DatePicker.test.tsx new file mode 100644 index 00000000..11cb8495 --- /dev/null +++ b/src/components/DatePicker/DatePicker.test.tsx @@ -0,0 +1,29 @@ +import React from 'react'; + +import { render } from '@testing-library/react'; +import '@testing-library/jest-dom'; + +import DatePicker from '.'; + +describe('DatePicker', () => { + it('renders', () => { + render(); + }); + + describe('when rendered with classnames props', () => { + it('should append the custom class to the exisiting class', () => { + const styleOverride = { wrapper: 'customWrapperClass' }; + const { getByTestId } = render( +
+ +
+ ); + + const DatePickerElem = getByTestId('datePickerElem') as HTMLDivElement; + const wrapperElem = DatePickerElem.firstChild + ?.firstChild as HTMLDivElement; + + expect(wrapperElem.className).toContain('customWrapperClass'); + }); + }); +}); diff --git a/src/components/DatePicker/DatePicker.tsx b/src/components/DatePicker/DatePicker.tsx new file mode 100644 index 00000000..e4a0df89 --- /dev/null +++ b/src/components/DatePicker/DatePicker.tsx @@ -0,0 +1,78 @@ +import React from 'react'; + +import DayPicker from 'react-day-picker'; +import { DayPickerProps } from 'react-day-picker/types/Props'; + +import 'react-day-picker/lib/style.css'; +import { ClassNames } from 'react-day-picker/types/ClassNames'; +import styles from './DatePicker.module.css'; + +interface DatePickerProps extends Omit { + classNames?: { + container?: string; + wrapper?: string; + interactionDisabled?: string; + navBar?: string; + navButtonPrev?: string; + navButtonNext?: string; + navButtonInteractionDisabled?: string; + months?: string; + month?: string; + caption?: string; + weekdays?: string; + weekdaysRow?: string; + weekday?: string; + weekNumber?: string; + body?: string; + week?: string; + day?: string; + footer?: string; + todayButton?: string; + today?: string; + selected?: string; + disabled?: string; + outside?: string; + }; +} + +const DatePicker: React.FC = ({ ...passedProps }) => { + const styleOverride = { + container: styles.container, + wrapper: styles.wrapper, + interactionDisabled: styles.interactionDisabled, + navBar: styles.navBar, + navButtonPrev: styles.navButtonPrev, + navButtonNext: styles.navButtonNext, + navButtonInteractionDisabled: styles.navButtonInteractionDisabled, + months: styles.months, + month: styles.month, + caption: styles.caption, + weekdays: styles.weekdaysBody, + weekdaysRow: styles.weekdaysRow, + weekday: styles.weekday, + weekNumber: styles.weekNumber, + body: styles.body, + week: styles.week, + day: styles.day, + footer: styles.footer, + todayButton: styles.todayButton, + today: styles.today, + selected: styles.selected, + disabled: styles.disabled, + outside: styles.outside, + } as ClassNames; + + if (passedProps.classNames) { + Object.entries(passedProps.classNames).forEach((classes) => { + if (classes.length === 2) { + styleOverride[classes[0]] = `${styleOverride[classes[0]]} ${ + classes[1] + }`; + } + }); + } + + return ; +}; + +export default DatePicker; diff --git a/src/components/DatePicker/README.md b/src/components/DatePicker/README.md new file mode 100644 index 00000000..fb3334e3 --- /dev/null +++ b/src/components/DatePicker/README.md @@ -0,0 +1,11 @@ +# \ + +Render a date picker UI, this is a pass-through component to the [react-day-picker](https://react-day-picker.js.org/) library. + +### Override Classes + +This date picker has been restyle using rover-ui themes/colors. Consuming application can override these classes with their own classes via `classNames` prop, see [/api/Daypicker#classNames](https://react-day-picker.js.org/api/DayPicker#classNames) + +```jsx + +``` diff --git a/src/components/DatePicker/index.ts b/src/components/DatePicker/index.ts new file mode 100644 index 00000000..2ef49e25 --- /dev/null +++ b/src/components/DatePicker/index.ts @@ -0,0 +1 @@ +export { default } from './DatePicker'; diff --git a/src/components/DatePicker/story.tsx b/src/components/DatePicker/story.tsx new file mode 100644 index 00000000..abd79c42 --- /dev/null +++ b/src/components/DatePicker/story.tsx @@ -0,0 +1,88 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import { boolean, number, date } from '@storybook/addon-knobs'; + +import { action } from '@storybook/addon-actions'; +import DatePicker from './DatePicker'; +import Readme from './README.md'; + +import { Wrap } from '../../stories/storybook-helpers'; + +storiesOf('Planets/DatePicker', module) + .addParameters({ + readme: { + sidebar: Readme, + }, + }) + .add('Overview', () => ( + + + + )) + .add('Examples', () => ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )); diff --git a/src/index.ts b/src/index.ts index a6c5071b..a0d964a6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,6 +33,7 @@ export { export { default as Tooltip, EasyRichTooltip } from './components/Tooltip'; export { default as Input, Checkbox, Toggle } from './components/Input'; export { default as InputTime } from './components/InputTime'; +export { default as DatePicker } from './components/DatePicker'; export { default as Typography } from './components/Typography'; export { default as Modal } from './components/Modal'; export { default as Kite } from './components/Kite'; diff --git a/src/stories/index.js b/src/stories/index.js index 539b5558..f59ff5fd 100644 --- a/src/stories/index.js +++ b/src/stories/index.js @@ -24,6 +24,7 @@ import '../components/Input/story'; import '../components/Input/Checkbox/story'; import '../components/Input/Toggle/story'; import '../components/InputTime/story'; +import '../components/DatePicker/story'; import '../components/Select/story'; import '../components/Typography/story'; import '../components/Loader/story'; diff --git a/yarn.lock b/yarn.lock index 63ac6cdb..ef01254f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13704,6 +13704,13 @@ react-color@^2.17.0: reactcss "^1.2.0" tinycolor2 "^1.4.1" +react-day-picker@^7.4.10: + version "7.4.10" + resolved "https://registry.yarnpkg.com/react-day-picker/-/react-day-picker-7.4.10.tgz#d3928fa65c04379ad28c76de22aa85374a8361e1" + integrity sha512-/QkK75qLKdyLmv0kcVzhL7HoJPazoZXS8a6HixbVoK6vWey1Od1WRLcxfyEiUsRfccAlIlf6oKHShqY2SM82rA== + dependencies: + prop-types "^15.6.2" + react-dev-utils@^10.2.1: version "10.2.1" resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-10.2.1.tgz#f6de325ae25fa4d546d09df4bb1befdc6dd19c19"