Skip to content

Commit

Permalink
feat(DatePicker): Add DatePicker component
Browse files Browse the repository at this point in the history
In progress
  • Loading branch information
Merkur39 committed Dec 24, 2024
1 parent c5c4481 commit 5008038
Show file tree
Hide file tree
Showing 9 changed files with 353 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/styleguide.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ module.exports = {
'../react/ContactPicker',
'../react/CozyDialogs',
'../react/CozyDialogs/SpecificDialogs',
'../react/DatePicker',
'../react/FileImageLoader',
'../react/FilePicker',
'../react/HistoryRow',
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,10 @@
},
"dependencies": {
"@babel/runtime": "^7.3.4",
"@date-io/date-fns": "1",
"@material-ui/core": "4.12.3",
"@material-ui/lab": "^4.0.0-alpha.61",
"@material-ui/pickers": "3.3.11",
"@popperjs/core": "^2.4.4",
"chart.js": "3.7.1",
"classnames": "^2.2.5",
Expand Down
16 changes: 16 additions & 0 deletions react/DatePicker/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
### Usage

```jsx
import React, { useState } from 'react'
import DemoProvider from 'cozy-ui/docs/components/DemoProvider'
import Variants from 'cozy-ui/docs/components/Variants'
import DatePicker from 'cozy-ui/transpiled/react/DatePicker'

;
const [selectedDate, setSelectedDate] = useState(null);

<DemoProvider>
<DatePicker onChange={setSelectedDate} value={selectedDate} />
</DemoProvider>

```
253 changes: 253 additions & 0 deletions react/DatePicker/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
import DateFnsUtils from '@date-io/date-fns'
import {
MuiPickersUtilsProvider,
KeyboardDatePicker
} from '@material-ui/pickers'
import cx from 'classnames'
import formatFNS from 'date-fns/format'
import isBefore from 'date-fns/isBefore'
import LocaleEN from 'date-fns/locale/en-US'
import LocaleFR from 'date-fns/locale/fr'
import subDays from 'date-fns/subDays'
import PropTypes from 'prop-types'
import React, { forwardRef, useState } from 'react'

import withOwnLocales from './locales/withOwnLocales'
import useBreakpoints from '../providers/Breakpoints'
import { useI18n } from '../providers/I18n'
import { makeStyles } from '../styles'

const localesFNS = {
fr: LocaleFR,
en: LocaleEN
}

const useStyles = makeStyles(() => ({
overrides: {
width: '100%',
height: isDesktop => (isDesktop ? '5rem' : 'inehrit'),
MuiOutlinedInput: {
'&:focused': {
notchedOutline: {
borderColor: 'var(--primaryColor)'
}
}
}
}
}))

const DatePicker = forwardRef(
(
{
className,
label,
hasClearButton = false,
value = null,
placeholder,
isValid,
onFocus,
onBlur,
onChange,
minDate,
minDateLabelError,
format,
cancelLabel,
clearLabel,
okLabel,
todayLabel,
showTodayButton = false,
helperText,
errorLabel,
inputVariant = 'outlined',
inputProps,
KeyboardButtonProps
},
ref
) => {
const [error, setError] = useState(null)
const [isFocused, setIsFocused] = useState(false)

const { isDesktop } = useBreakpoints()
const classes = useStyles(isDesktop)
const { t, lang } = useI18n()

const isError = !isFocused && Boolean(error)
const _helperText = isError ? error : helperText ?? null
const _format = format || (lang === 'fr' ? 'dd/LL/yyyy' : 'LL/dd/yyyy')
const _placeholder = placeholder ?? formatFNS(new Date(), _format)
const _clearLabel = clearLabel || t('clear')
const _todayLabel = todayLabel || t('today')
const _cancelLabel = cancelLabel || t('cancel')
const _okLabel = okLabel || t('ok')
const _minDateLabelError = minDateLabelError
? minDateLabelError
: minDate
? t('minDateLabelError', {
date: formatFNS(minDate, _format)
})
: null

const _KeyboardButtonProps = {
'aria-label': label,
...KeyboardButtonProps
}
const _inputProps = { inputMode: 'numeric', ...inputProps }

const handleChange = val => {
if (val?.toString() !== 'Invalid Date') {
if (minDate && isBefore(val, subDays(minDate, 1))) {
isValid?.(false)
setError(_minDateLabelError)
return
}
setError(null)
isValid?.(true)
onChange(val)
} else if (val === '') {
setError(null)
isValid?.(true)
onChange(null)
} else {
setError(errorLabel ?? t('invalidDate'))
isValid?.(false)
onChange(val)
}
}

const handleBlur = () => {
setIsFocused(false)
onFocus?.(true)
onBlur?.(false)
}
const handleFocus = () => {
setIsFocused(true)
onFocus?.(false)
onBlur?.(true)
}

return (
<MuiPickersUtilsProvider utils={DateFnsUtils} locale={localesFNS[lang]}>
<KeyboardDatePicker
inputRef={ref}
label={label}
placeholder={_placeholder}
value={value}
helperText={_helperText}
className={cx(classes.overrides, className)}
minDate={minDate}
format={_format}
onChange={handleChange}
error={isError}
onBlur={handleBlur}
onFocus={handleFocus}
inputVariant={inputVariant}
inputProps={_inputProps}
KeyboardButtonProps={_KeyboardButtonProps}
// Modal start
showTodayButton={showTodayButton}
todayLabel={_todayLabel}
clearable={hasClearButton}
clearLabel={_clearLabel}
cancelLabel={_cancelLabel}
okLabel={_okLabel}
// Modal end
/>
</MuiPickersUtilsProvider>
)
}
)

DatePicker.displayName = 'DatePicker'

DatePicker.prototype = {
/*
Classname to override the input style
*/
className: PropTypes.string,
/*
Label of the input
*/
label: PropTypes.string,
/*
Value of th input. If set by default with a Date, isValidDate will be false if the value is empty (KeyboardDatePicker behavior)
*/
value: PropTypes.string.isRequired,
/*
Placeholder of the input
*/
placeholder: PropTypes.string,
/*
Function that returns if the value of the input is a valid Date
*/
isValid: PropTypes.func,
/*
Function that returns the value of the input
*/
onChange: PropTypes.func.isRequired,
/*
Function that returns if the input is blured
*/
onBlur: PropTypes.func,
/*
Function that returns if the input is focused
*/
onFocus: PropTypes.func,
/*
Helper text to display when the input is in error
*/
helperText: PropTypes.string,
/*
Min date selectable with the date picker (exclusive)
*/
minDate: PropTypes.instanceOf(Date),
/*
Error message when the min date is not respected
*/
minDateLabelError: PropTypes.string,
/*
Format of the date
*/
format: PropTypes.string,
/*
Date picker cancellation label
*/
cancelLabel: PropTypes.string,
/*
Show today button
*/
showTodayButton: PropTypes.bool,
/*
Date picker today label
*/
todayLabel: PropTypes.string,
/*
Date picker ok label
*/
okLabel: PropTypes.string,
/*
Show the clear button
*/
hasClearButton: PropTypes.bool,
/*
Date picker clear label
*/
clearLabel: PropTypes.string,
/*
Error message when the date is invalid
*/
errorLabel: PropTypes.string,
/*
Variant of the input
*/
inputVariant: PropTypes.string,
/*
Props to override the input
*/
inputProps: PropTypes.object,
/*
Props to override the keyboard button
*/
KeyboardButtonProps: PropTypes.object
}

export default withOwnLocales(React.memo(DatePicker))
8 changes: 8 additions & 0 deletions react/DatePicker/locales/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"cancel": "Cancel",
"clear": "Clear",
"invalidDate": "Invalid date",
"ok": "Ok",
"today": "Today",
"minDateLabelError": "Date should not be before minimal date (%{date})"
}
8 changes: 8 additions & 0 deletions react/DatePicker/locales/fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"cancel": "Annuler",
"clear": "Supprimer",
"invalidDate": "Date invalide",
"ok": "Ok",
"today": "Aujourd'hui",
"minDateLabelError": "La date ne doit pas être antérieure à la date minimale (%{date})"
}
10 changes: 10 additions & 0 deletions react/DatePicker/locales/withOwnLocales.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import en from './en.json'
import fr from './fr.json'
import withOnlyLocales from '../../providers/I18n/withOnlyLocales'

export const locales = {
en,
fr
}

export default withOnlyLocales(locales)
1 change: 1 addition & 0 deletions react/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,4 @@ export { default as Modal } from './Modal'
export { ListSkeleton, ListItemSkeleton } from './Skeletons'
export { default as ActionsBar } from './ActionsBar'
export { default as Markdown } from './Markdown'
export { default as DatePicker } from './DatePicker'
Loading

0 comments on commit 5008038

Please sign in to comment.