From 8473776db767e9df06213dfe89cd0ab9dde903ab Mon Sep 17 00:00:00 2001 From: Charles Dang Date: Mon, 11 May 2020 12:57:07 +1100 Subject: [PATCH] Fixed many usability issues with the CalendarDay field (#2930) (cherry picked from commit 59ed6310bacc76f571639de048689becbedbeac5) --- .changeset/sour-taxis-hunt.md | 15 +++ .../arch/packages/day-picker/package.json | 2 +- .../packages/day-picker/src/TextDayPicker.js | 111 ++++++++++-------- .../src/types/CalendarDay/views/Filter.js | 16 +-- .../fields/src/types/DateTime/views/Filter.js | 4 +- yarn.lock | 70 +++++++---- 6 files changed, 128 insertions(+), 90 deletions(-) create mode 100644 .changeset/sour-taxis-hunt.md diff --git a/.changeset/sour-taxis-hunt.md b/.changeset/sour-taxis-hunt.md new file mode 100644 index 00000000000..e841053daf9 --- /dev/null +++ b/.changeset/sour-taxis-hunt.md @@ -0,0 +1,15 @@ +--- +'@arch-ui/day-picker': patch +'@keystonejs/fields': patch +--- + +Fixed many usability issues with the CalendarDay field: +- Fixed field not functioning as a proper controlled component in the Create popout. +- Fixed field initially displaying "Invalid Date" before defaulting to 1970-01-01. +- Filter input no longer defaults to the current date. This was bugged; submitting the form with no changes would match nothing. +- Filter input now falls back to no value when given an invalid date. Previously, it was falling back to 1970-01-01. +- Fixed filter input not initially displaying the current value when in edit mode (it was displaying the current date). +- Fixed filter input not being initially focused. +- Fixed filter input not being submitted properly if focus wasn't lost first. + +Updated chrono-node dependency to 1.4.6. \ No newline at end of file diff --git a/packages/arch/packages/day-picker/package.json b/packages/arch/packages/day-picker/package.json index df1271ec153..42e044639f2 100644 --- a/packages/arch/packages/day-picker/package.json +++ b/packages/arch/packages/day-picker/package.json @@ -20,7 +20,7 @@ "@babel/runtime": "^7.8.4", "@emotion/core": "^10.0.28", "@emotion/styled": "^10.0.27", - "chrono-node": "^1.3.5", + "chrono-node": "^1.4.6", "date-fns": "^1.30.1", "intersection-observer": "^0.5.1", "moment": "^2.24.0", diff --git a/packages/arch/packages/day-picker/src/TextDayPicker.js b/packages/arch/packages/day-picker/src/TextDayPicker.js index 002ff20fd71..0e6380d6340 100644 --- a/packages/arch/packages/day-picker/src/TextDayPicker.js +++ b/packages/arch/packages/day-picker/src/TextDayPicker.js @@ -1,57 +1,64 @@ -/** @jsx jsx */ -import { jsx } from '@emotion/core'; -import { useState } from 'react'; +import React, { useState, useEffect, forwardRef, useRef } from 'react'; import chrono from 'chrono-node'; import { Input } from '@arch-ui/input'; import { format } from 'date-fns'; -export const TextDayPicker = ({ - date, - onChange, - format: displayFormat = 'Do MMMM YYYY', - ...props -}) => { - const formatDate = newDate => (newDate === null ? '' : format(newDate, displayFormat)); - - const [isEditing, setIsEditing] = useState(false); - const [value, setValue] = useState({ - raw: date, - formatted: formatDate(date), - }); - - const toggleEditing = () => { - setIsEditing(!isEditing); - }; - - const onBlur = () => { - toggleEditing(); - - const newDate = parseDate(value.raw); - onChange(newDate); - - setValue({ - raw: newDate, - formatted: formatDate(newDate), +export const TextDayPicker = forwardRef( + ({ date = '', onChange, format: displayFormat = 'Do MMMM YYYY', ...props }, ref) => { + const formatDate = newDate => (newDate ? format(newDate, displayFormat) : ''); + + const [isEditing, setIsEditing] = useState(false); + const [value, setValue] = useState({ + raw: date, + formatted: formatDate(date), }); - }; - - const handleChange = ({ target: { value: raw } }) => { - setValue(oldValue => ({ ...oldValue, raw })); - }; - - return ( - - ); -}; - -function parseDate(value) { - const parsed = chrono.parseDate(value); - return parsed === undefined ? null : format(parsed, 'YYYY-MM-DD'); -} + + const parseCache = useRef(); + + useEffect(() => { + // Parse the raw input. This may be a string such as 'Today'. + const parsedDate = chrono.parseDate(value.raw); + + // If valid, convert it to ISO 8601. + const isoDate = parsedDate ? format(parsedDate, 'YYYY-MM-DD') : null; + + // Pass it up the tree. The parent can handle the null case. + onChange(isoDate); + + parseCache.current = isoDate; + }, [value.raw]); + + const onFocus = () => { + setIsEditing(true); + }; + + const onBlur = () => { + setIsEditing(false); + + const raw = parseCache.current; + + // At this point, the parse cache should either be null or an ISO 8601 date. + if (raw) { + setValue({ raw, formatted: formatDate(raw) }); + } else { + setValue({ raw: '', formatted: '' }); + } + }; + + const handleChange = ({ target: { value: raw } }) => { + setValue(oldValue => ({ ...oldValue, raw })); + }; + + return ( + + ); + } +); diff --git a/packages/fields/src/types/CalendarDay/views/Filter.js b/packages/fields/src/types/CalendarDay/views/Filter.js index 97e4ac19658..aadfadae16a 100644 --- a/packages/fields/src/types/CalendarDay/views/Filter.js +++ b/packages/fields/src/types/CalendarDay/views/Filter.js @@ -1,24 +1,18 @@ -import React, { useState } from 'react'; -import { format } from 'date-fns'; +import React from 'react'; import { TextDayPicker } from '@arch-ui/day-picker'; -const FORMAT = 'YYYY-MM-DD'; - -const CalendarDayFilterView = ({ onChange, filter }) => { - const [value, setValue] = useState(format(new Date(), FORMAT)); - - const handleSelectedChange = newValue => { +const CalendarDayFilterView = ({ onChange, filter, value, innerRef }) => { + const handleChange = newValue => { if (newValue === null) { - newValue = format(new Date(), FORMAT); + newValue = ''; } onChange(newValue); - setValue(newValue); }; if (!filter) return null; - return ; + return ; }; export default CalendarDayFilterView; diff --git a/packages/fields/src/types/DateTime/views/Filter.js b/packages/fields/src/types/DateTime/views/Filter.js index fb24fc062f1..54a19a86d99 100644 --- a/packages/fields/src/types/DateTime/views/Filter.js +++ b/packages/fields/src/types/DateTime/views/Filter.js @@ -3,7 +3,7 @@ import { format } from 'date-fns'; import { DayTimePicker } from '@arch-ui/day-picker'; import { stringifyDate, parseDate } from './utils'; -const CalendarDayFilterView = props => { +const DateTimeFilterView = props => { const parsedDate = props.value ? parseDate(props.value) : parseDate(new Date().toISOString()); let handleDayChange = day => { @@ -38,4 +38,4 @@ const CalendarDayFilterView = props => { ); }; -export default CalendarDayFilterView; +export default DateTimeFilterView; diff --git a/yarn.lock b/yarn.lock index da930b19f01..112de0b8612 100644 --- a/yarn.lock +++ b/yarn.lock @@ -421,14 +421,14 @@ "@babel/template" "^7.7.4" "@babel/types" "^7.7.4" -"@babel/helper-function-name@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca" - integrity sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA== +"@babel/helper-function-name@^7.8.3", "@babel/helper-function-name@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c" + integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw== dependencies: "@babel/helper-get-function-arity" "^7.8.3" "@babel/template" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/types" "^7.9.5" "@babel/helper-get-function-arity@^7.7.4": version "7.7.4" @@ -598,10 +598,10 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-validator-identifier@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz#ad53562a7fc29b3b9a91bbf7d10397fd146346ed" - integrity sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw== +"@babel/helper-validator-identifier@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" + integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== "@babel/helper-wrap-function@^7.8.3": version "7.8.3" @@ -766,10 +766,19 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-object-rest-spread" "^7.2.0" -"@babel/plugin-proposal-object-rest-spread@^7.0.0", "@babel/plugin-proposal-object-rest-spread@^7.3.2", "@babel/plugin-proposal-object-rest-spread@^7.5.5", "@babel/plugin-proposal-object-rest-spread@^7.6.2", "@babel/plugin-proposal-object-rest-spread@^7.7.4", "@babel/plugin-proposal-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.8.3.tgz#eb5ae366118ddca67bed583b53d7554cad9951bb" - integrity sha512-8qvuPwU/xxUCt78HocNlv0mXXo0wdh9VT1R04WU8HGOfaOob26pF+9P5/lYjN/q7DHOX1bvX60hnhOvuQUJdbA== +"@babel/plugin-proposal-object-rest-spread@7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.5.tgz#3fd65911306d8746014ec0d0cf78f0e39a149116" + integrity sha512-VP2oXvAf7KCYTthbUHwBlewbl1Iq059f6seJGsxMizaCdgHIeczOr7FBqELhSqfkIl04Fi8okzWzl63UKbQmmg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.9.5" + +"@babel/plugin-proposal-object-rest-spread@^7.0.0", "@babel/plugin-proposal-object-rest-spread@^7.5.5", "@babel/plugin-proposal-object-rest-spread@^7.6.2", "@babel/plugin-proposal-object-rest-spread@^7.7.4", "@babel/plugin-proposal-object-rest-spread@^7.8.3", "@babel/plugin-proposal-object-rest-spread@^7.9.5", "@babel/plugin-proposal-object-rest-spread@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.6.tgz#7a093586fcb18b08266eb1a7177da671ac575b63" + integrity sha512-Ga6/fhGqA9Hj+y6whNpPv8psyaK5xzrQwSPsGPloVkvmH+PqW1ixdnfJ9uIO06OjQNYol3PMnfmJ8vfZtkzF+A== dependencies: "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-object-rest-spread" "^7.8.0" @@ -1149,12 +1158,11 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/helper-replace-supers" "^7.8.3" -"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.4.4", "@babel/plugin-transform-parameters@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.4.tgz#1d5155de0b65db0ccf9971165745d3bb990d77d3" - integrity sha512-IsS3oTxeTsZlE5KqzTbcC2sV0P9pXdec53SU+Yxv7o/6dvGM5AkTotQKhoSffhNgZ/dftsSiOoxy7evCYJXzVA== +"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.4.4", "@babel/plugin-transform-parameters@^7.8.4", "@babel/plugin-transform-parameters@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz#173b265746f5e15b2afe527eeda65b73623a0795" + integrity sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA== dependencies: - "@babel/helper-call-delegate" "^7.8.3" "@babel/helper-get-function-arity" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" @@ -1692,12 +1700,21 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +<<<<<<< HEAD "@babel/types@^7.8.6", "@babel/types@^7.9.0": version "7.9.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.0.tgz#00b064c3df83ad32b2dbf5ff07312b15c7f1efb5" integrity sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng== dependencies: "@babel/helper-validator-identifier" "^7.9.0" +======= +"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.4.4", "@babel/types@^7.6.0", "@babel/types@^7.7.1", "@babel/types@^7.7.2", "@babel/types@^7.7.4", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5", "@babel/types@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7" + integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA== + dependencies: + "@babel/helper-validator-identifier" "^7.9.5" +>>>>>>> 59ed6310b... Fixed many usability issues with the CalendarDay field (#2930) lodash "^4.17.13" to-fast-properties "^2.0.0" @@ -6671,12 +6688,12 @@ chrome-trace-event@^1.0.0, chrome-trace-event@^1.0.2: dependencies: tslib "^1.9.0" -chrono-node@^1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/chrono-node/-/chrono-node-1.3.5.tgz#a2495298a32da82bcc01ad9be7d77efa5e244122" - integrity sha1-oklSmKMtqCvMAa2b59d++l4kQSI= +chrono-node@^1.4.6: + version "1.4.6" + resolved "https://registry.yarnpkg.com/chrono-node/-/chrono-node-1.4.6.tgz#dcdc59f1cc80e65d8f6c977bd6ad4e9b82008a8e" + integrity sha512-CBUbwkj2gRdCSacmJ4UvKOSsSjW5jWce3kiruZSMgmGf7FKwc2ERat32yPSqJDR8aDeA0lUJxiDzZRhXZmXBqw== dependencies: - moment "^2.10.3" + dayjs "^1.8.19" ci-info@2.0.0, ci-info@^2.0.0: version "2.0.0" @@ -8177,6 +8194,11 @@ date-now@^0.1.4: resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs= +dayjs@^1.8.19: + version "1.8.26" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.26.tgz#c6d62ccdf058ca72a8d14bb93a23501058db9f1e" + integrity sha512-KqtAuIfdNfZR5sJY1Dixr2Is4ZvcCqhb0dZpCOt5dGEFiMzoIbjkTSzUb4QKTCsP+WNpGwUjAFIZrnZvUxxkhw== + de-indent@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" @@ -16437,7 +16459,7 @@ mocha@^7.0.1: yargs-parser "13.1.1" yargs-unparser "1.6.0" -moment@2.24.0, moment@^2.10.3, moment@^2.21.0, moment@^2.24.0: +moment@2.24.0, moment@^2.21.0, moment@^2.24.0: version "2.24.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==