Skip to content

Commit

Permalink
feat: DatePicker component (#803)
Browse files Browse the repository at this point in the history
* Initial setup for DatePicker

* Render initial elements

* Add DatePicker utils and constants, set default value

* Handle calendar toggle

* Build Calendar UI, handle toggle

* Implement select date handler, focus on selected day, add tests

* Add date validation

* Handle min, max dates

* Update status text on toggle of calendar

* Added month/year navigation

* Handle external input, pass selectedDate prop to calendar

* Working on MonthPicker

* Add month selection

* Working on YearPicker

* Add year chunk navigation

* Handle date range styling

* Use const instead of enum

* Manage focus in navigation

* Clean up, add TODOs

* Upgrade to React 17

- Keep React 16 peerDependency for now

* Update storybook (for React 17 compatibility)

* Update Storybook names, add focusOut handler

* Handle Day keyboard events

* Add month picker keyboard navigation

* Add year picker keyboard navigation

* Handle escape key

* Implement tab navigation

* Add mousemove handler to date picker

* Add remaining mousemove events

* handle keydown/keyup on calendar element

* Pass in input props, add label intrinsic props to Label, use storybook actions

* Fix status text behavior

* Status text is sr only

* Fix tests

* Add the DatePicker export

* Move deps to dev or peer

* Use prepare script

* Comment out internal component stories

* Clean up, eslint fixes/annotations

* Add some additional edge test cases

* handle some events (:

* Update storybook addons in dependabot config

* Clean up DatePicker stories

* Use readOnly attr on internal input

Co-authored-by: Brandon Lenz <brandon@truss.works>
  • Loading branch information
Suzanne Rozier and brandonlenz authored Feb 12, 2021
1 parent db3798d commit ea996c8
Show file tree
Hide file tree
Showing 29 changed files with 5,021 additions and 79 deletions.
7 changes: 2 additions & 5 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,9 @@ updates:
- dependency-name: '@storybook/react'
versions:
- '^6.0.21'
- dependency-name: '@storybook/addon-docs'
- dependency-name: '@storybook/addon-essentials'
versions:
- '^6.0.21'
- dependency-name: '@storybook/addon-viewport'
versions:
- '^6.0.21'
- '^6.1.5'

- package-ecosystem: npm
directory: '/example/'
Expand Down
2 changes: 1 addition & 1 deletion .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const webpackConfig = (config) => {

module.exports = {
stories: ['../src/**/*.stories.@(ts|tsx)'],
addons: ['@storybook/addon-docs', '@storybook/addon-viewport'],
addons: ['@storybook/addon-essentials'],
typescript: {
check: false,
checkOptions: {},
Expand Down
4 changes: 2 additions & 2 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"@fortawesome/react-fontawesome": "^0.1.14",
"@trussworks/react-uswds": "file:./../",
"formik": "^2.2.6",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-redux": "^7.2.2",
"react-router-dom": "^5.2.0",
"redux": "^4.0.5",
Expand Down
18 changes: 9 additions & 9 deletions example/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import * as serviceWorker from './serviceWorker'
import { createStore } from 'redux'
import { Provider } from 'react-redux'

import { rootReducer } from './redux/reducers'

Expand All @@ -17,9 +17,9 @@ ReactDOM.render(
</React.StrictMode>
</Provider>,
document.getElementById('root')
);
)

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
serviceWorker.unregister()
29 changes: 28 additions & 1 deletion example/src/pages/Forms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import {
Button,
ComboBox,
Form,
FormGroup,
Label,
TextInput,
ValidationChecklist,
ValidationItem,
DatePicker,
} from '@trussworks/react-uswds'

type FormValues = {
Expand Down Expand Up @@ -48,7 +50,12 @@ const FormsPage = (): React.ReactElement => {
<section>
<h2>Formik</h2>
<Formik
initialValues={{ email: '', password: '', fruit: 'avocado' }}
initialValues={{
email: '',
password: '',
fruit: 'avocado',
appointmentDate: '1/20/2021',
}}
validationSchema={FormSchema}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
Expand Down Expand Up @@ -124,6 +131,26 @@ const FormsPage = (): React.ReactElement => {
options={fruitOptions}
onChange={(val: string) => setFieldValue('fruit', val, true)}
defaultValue="avocado"></Field>

<FormGroup>
<Label id="appointment-date-label" htmlFor="appointment-date">
Appointment date
</Label>
<div className="usa-hint" id="appointment-date-hint">
mm/dd/yyyy
</div>
<Field
as={DatePicker}
id="appointmentDate"
name="appointmentDate"
aria-describedby="appointment-date-label appointment-date-hint"
defaultValue="2021-01-01"
onChange={(val: string) =>
setFieldValue('appointmentDate', val, true)
}
/>
</FormGroup>

<Button type="submit" disabled={isSubmitting}>
Submit
</Button>
Expand Down
2 changes: 1 addition & 1 deletion example/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react",
"jsx": "react-jsx",
"noFallthroughCasesInSwitch": true
},
"include": [
Expand Down
30 changes: 14 additions & 16 deletions example/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1755,7 +1755,7 @@
"@babel/runtime" "^7.12.5"

"@trussworks/react-uswds@file:./..":
version "1.10.0"
version "1.11.0"

"@types/anymatch@*":
version "1.3.1"
Expand Down Expand Up @@ -9249,15 +9249,14 @@ react-dev-utils@^11.0.1:
strip-ansi "6.0.0"
text-table "0.2.0"

react-dom@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f"
integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==
react-dom@^17.0.1:
version "17.0.1"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.1.tgz#1de2560474ec9f0e334285662ede52dbc5426fc6"
integrity sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
scheduler "^0.19.1"
scheduler "^0.20.1"

react-error-overlay@^6.0.8:
version "6.0.8"
Expand Down Expand Up @@ -9390,14 +9389,13 @@ react-scripts@^4.0.1:
optionalDependencies:
fsevents "^2.1.3"

react@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==
react@^17.0.1:
version "17.0.1"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127"
integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"

read-pkg-up@^2.0.0:
version "2.0.0"
Expand Down Expand Up @@ -9916,10 +9914,10 @@ saxes@^5.0.0:
dependencies:
xmlchars "^2.2.0"

scheduler@^0.19.1:
version "0.19.1"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
scheduler@^0.20.1:
version "0.20.1"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.1.tgz#da0b907e24026b01181ecbc75efdc7f27b5a000c"
integrity sha512-LKTe+2xNJBNxu/QhHvDR14wUXHRQbVY5ZOYpOGWRzhydZUqrLb2JBvLPY7cAqFmqrWuDED0Mjk7013SZiOz6Bw==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@
"devDependencies": {
"@babel/core": "^7.10.5",
"@babel/preset-react": "^7.10.4",
"@storybook/addon-docs": "^6.1.5",
"@storybook/addon-viewport": "^6.1.5",
"@storybook/addon-essentials": "^6.1.5",
"@storybook/react": "^6.1.5",
"@storybook/storybook-deployer": "^2.8.6",
"@testing-library/dom": "^7.22.3",
Expand Down
70 changes: 70 additions & 0 deletions src/components/forms/DatePicker/Calendar.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react'

import { Calendar } from './Calendar'
import { FocusMode } from './DatePicker'
import { parseDateString } from './utils'

/*
// THIS STORY FOR INTERNAL DEVELOPMENT ONLY
export default {
title: 'Components/Form controls/Date picker/Calendar',
component: Calendar,
argTypes: {
handleSelectDate: { action: 'select date' },
setStatuses: { action: 'set statuses' },
},
}
*/

const defaultProps = {
minDate: new Date('0000-01-01'),
focusMode: FocusMode.None,
}

export const defaultCalendar = (argTypes): React.ReactElement => (
<Calendar
{...defaultProps}
handleSelectDate={argTypes.handleSelectDate}
setStatuses={argTypes.setStatuses}
/>
)

export const givenDate = (argTypes): React.ReactElement => (
<Calendar
{...defaultProps}
handleSelectDate={argTypes.handleSelectDate}
setStatuses={argTypes.setStatuses}
date={new Date('July 4, 2019')}
/>
)

export const selectedDate = (argTypes): React.ReactElement => (
<Calendar
{...defaultProps}
handleSelectDate={argTypes.handleSelectDate}
setStatuses={argTypes.setStatuses}
selectedDate={new Date('January 20, 2021')}
/>
)

export const minAndMax = (argTypes): React.ReactElement => (
<Calendar
{...defaultProps}
handleSelectDate={argTypes.handleSelectDate}
setStatuses={argTypes.setStatuses}
date={new Date('January 15 2021')}
minDate={parseDateString('2021-01-10')}
maxDate={parseDateString('2021-01-20')}
/>
)

export const rangeDate = (argTypes): React.ReactElement => (
<Calendar
{...defaultProps}
handleSelectDate={argTypes.handleSelectDate}
setStatuses={argTypes.setStatuses}
selectedDate={parseDateString('2021-01-20')}
rangeDate={parseDateString('2021-01-08')}
/>
)
Loading

0 comments on commit ea996c8

Please sign in to comment.