From 59ed6310bacc76f571639de048689becbedbeac5 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) --- .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 | 59 ++-------- 6 files changed, 94 insertions(+), 113 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 48bcdbcd789..220ba1e42f3 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.10.0", "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 bc8532e8b35..ea6dce48433 100644 --- a/yarn.lock +++ b/yarn.lock @@ -432,15 +432,6 @@ "@babel/template" "^7.8.3" "@babel/types" "^7.9.5" -"@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.9.5" - "@babel/helper-get-function-arity@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz#cb46348d2f8808e632f0ab048172130e636005f0" @@ -609,11 +600,6 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== -"@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" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610" @@ -772,16 +758,7 @@ "@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": - 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" - "@babel/plugin-transform-parameters" "^7.9.5" - -"@babel/plugin-proposal-object-rest-spread@^7.9.5", "@babel/plugin-proposal-object-rest-spread@^7.9.6": +"@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== @@ -1239,14 +1216,6 @@ "@babel/helper-get-function-arity" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" -"@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-get-function-arity" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-transform-property-literals@^7.0.0", "@babel/plugin-transform-property-literals@^7.2.0", "@babel/plugin-transform-property-literals@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz#33194300d8539c1ed28c62ad5087ba3807b98263" @@ -1844,15 +1813,6 @@ lodash "^4.17.13" to-fast-properties "^2.0.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" - lodash "^4.17.13" - to-fast-properties "^2.0.0" - "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -6851,12 +6811,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" @@ -8321,6 +8281,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" @@ -16556,7 +16521,7 @@ mocha@^7.1.2: yargs-parser "13.1.2" 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==