From ab54ea97a587358b4163878f8d71fad317864e04 Mon Sep 17 00:00:00 2001 From: Tim Leslie Date: Fri, 8 May 2020 10:19:49 +1000 Subject: [PATCH 1/3] Updates to the CalendarDay field --- demo-projects/todo/index.js | 24 +++++++++++- docs/tutorials/add-lists.md | 4 +- .../day-picker/src/TextDayTimePicker.js | 2 +- .../src/types/CalendarDay/Implementation.js | 4 +- .../fields/src/types/CalendarDay/README.md | 4 +- .../src/types/CalendarDay/views/Cell.js | 15 +------- .../src/types/CalendarDay/views/Controller.js | 6 +++ .../src/types/CalendarDay/views/Field.js | 38 +++++++------------ packages/fields/src/types/DateTime/README.md | 4 +- packages/list-plugins/README.md | 2 +- packages/list-plugins/atTracking.md | 2 +- 11 files changed, 57 insertions(+), 48 deletions(-) diff --git a/demo-projects/todo/index.js b/demo-projects/todo/index.js index 957d2d5b3e3..c83b5d6f78d 100644 --- a/demo-projects/todo/index.js +++ b/demo-projects/todo/index.js @@ -1,9 +1,20 @@ const { Keystone } = require('@keystonejs/keystone'); const { MongooseAdapter } = require('@keystonejs/adapter-mongoose'); -const { Text } = require('@keystonejs/fields'); +const { Text, CalendarDay, Virtual } = require('@keystonejs/fields'); const { GraphQLApp } = require('@keystonejs/app-graphql'); const { AdminUIApp } = require('@keystonejs/app-admin-ui'); const { StaticApp } = require('@keystonejs/app-static'); +const { format, parseISO } = require('date-fns'); + +class _CalendarDayImpl extends CalendarDay.implementation { + gqlOutputFieldResolvers() { + return { + [`${this.path}`]: item => + item[this.path] && format(parseISO(item[this.path]), this.config.gqlFormat), + }; + } +} +const _CalendarDay = { ...CalendarDay, implementation: _CalendarDayImpl }; const keystone = new Keystone({ adapter: new MongooseAdapter({ mongoUri: 'mongodb://localhost/todo' }), @@ -13,6 +24,17 @@ keystone.createList('Todo', { schemaDoc: 'A list of things which need to be done', fields: { name: { type: Text, schemaDoc: 'This is the thing you need to do', isRequired: true }, + date: { type: CalendarDay, adminDoc: 'For testing only' }, + niceDate: { + type: Virtual, + resolver: item => item.date && format(parseISO(item.date), 'do MMMM, yyyy'), + }, + nicerDate: { + type: Virtual, + resolver: (item, { formatAs = 'do MMMM, yyyy' }) => + item.date && format(parseISO(item.date), formatAs), + param: 'formatAs: String', + }, }, }); diff --git a/docs/tutorials/add-lists.md b/docs/tutorials/add-lists.md index a058b6d0ea1..d80792a2e77 100644 --- a/docs/tutorials/add-lists.md +++ b/docs/tutorials/add-lists.md @@ -69,11 +69,11 @@ module.exports = { // added fields deadline: { type: CalendarDay, - format: 'Do MMMM YYYY', + format: 'do MMMM yyyy', yearRangeFrom: '2019', yearRangeTo: '2029', isRequired: false, - defaultValue: new Date().toISOString('YYYY-MM-DD').substring(0, 10), + defaultValue: new Date().toISOString('YYYY-MM-DD').substring(0, 10), // Today's date }, assignee: { type: Text, diff --git a/packages/arch/packages/day-picker/src/TextDayTimePicker.js b/packages/arch/packages/day-picker/src/TextDayTimePicker.js index 73cfde63bbe..e2332957b13 100644 --- a/packages/arch/packages/day-picker/src/TextDayTimePicker.js +++ b/packages/arch/packages/day-picker/src/TextDayTimePicker.js @@ -35,7 +35,7 @@ function formatDateTime(date) { // why are we using moment when it's so large and provides a mutable API? // because chrono uses it and consistency is nice and // will probably make bugs with conversion less likely - return date ? moment.parseZone(date).format('h:mm A Do MMMM YYYY Z') : ''; + return date ? moment.parseZone(date).format('h:mm A do MMMM YYYY Z') : ''; } function parseDate(value) { diff --git a/packages/fields/src/types/CalendarDay/Implementation.js b/packages/fields/src/types/CalendarDay/Implementation.js index d24a3f1fed0..7b40b4f9482 100644 --- a/packages/fields/src/types/CalendarDay/Implementation.js +++ b/packages/fields/src/types/CalendarDay/Implementation.js @@ -36,7 +36,9 @@ export class CalendarDay extends Implementation { gqlOutputFields() { return [`${this.path}: String`]; } - + gqlOutputFieldResolvers() { + return { [`${this.path}`]: item => item[this.path] }; + } gqlQueryInputFields() { return [ ...this.equalityInputFields('String'), diff --git a/packages/fields/src/types/CalendarDay/README.md b/packages/fields/src/types/CalendarDay/README.md index abdf2b58450..8ff2dc02359 100644 --- a/packages/fields/src/types/CalendarDay/README.md +++ b/packages/fields/src/types/CalendarDay/README.md @@ -52,7 +52,7 @@ All date values must be in the 10 character ISO8601 format:`YYYY-MM-DD`. ### Filters -All filter fields expect values in the ISO8601 (`YYYY-MM-DD`) format. +All filter fields expect values in the ISO8601 (`yyyy-MM-dd`) format. | Field name | Type | Description | | :--------------- | :--------- | :----------------------------------------- | @@ -69,7 +69,7 @@ All filter fields expect values in the ISO8601 (`YYYY-MM-DD`) format. ### Mongoose adapter -In Mongoose the field is added using the `String` schema type. +In Mongoose the field is stored using the `String` schema type. The `isRequired` config option is enforced by KeystoneJS only. diff --git a/packages/fields/src/types/CalendarDay/views/Cell.js b/packages/fields/src/types/CalendarDay/views/Cell.js index ded38dd7e6a..c30292442a2 100644 --- a/packages/fields/src/types/CalendarDay/views/Cell.js +++ b/packages/fields/src/types/CalendarDay/views/Cell.js @@ -1,15 +1,4 @@ import { format, parseISO } from 'date-fns'; -const CalendarDayCell = ({ data, field: { config } }) => { - if (!data) { - return null; - } - - if (!config.format) { - return data; - } - - return format(parseISO(data), config.format); -}; - -export default CalendarDayCell; +export default ({ data, field: { format: formatString } }) => + data ? (formatString ? format(parseISO(data), formatString) : data) : null; diff --git a/packages/fields/src/types/CalendarDay/views/Controller.js b/packages/fields/src/types/CalendarDay/views/Controller.js index d699814394e..e20987daba0 100644 --- a/packages/fields/src/types/CalendarDay/views/Controller.js +++ b/packages/fields/src/types/CalendarDay/views/Controller.js @@ -1,6 +1,12 @@ import FieldController from '../../../Controller'; export default class CalendarDayController extends FieldController { + constructor({ format, yearRangeFrom, yearRangeTo, ...config }, ...args) { + super({ ...config }, ...args); + this.format = format; + this.yearRangeFrom = yearRangeFrom; + this.yearRangeTo = yearRangeTo; + } getFilterGraphQL = ({ type, value }) => { const key = type === 'is' ? `${this.path}` : `${this.path}_${type}`; return { [key]: value }; diff --git a/packages/fields/src/types/CalendarDay/views/Field.js b/packages/fields/src/types/CalendarDay/views/Field.js index 5126cf60ce7..4c4225d97c2 100644 --- a/packages/fields/src/types/CalendarDay/views/Field.js +++ b/packages/fields/src/types/CalendarDay/views/Field.js @@ -8,14 +8,20 @@ import 'react-day-picker/dist/style.css'; import { DayPicker } from 'react-day-picker'; import { parseISO, compareAsc, formatISO, isValid } from 'date-fns'; -const CalendarDayField = ({ autoFocus, field, value, errors, onChange, isDisabled }) => { - const htmlID = `ks-daypicker-${field.path}`; - const handleDayClick = day => onChange(formatISO(day, { representation: 'date' })); +const CalendarDayField = ({ + autoFocus, + field: { format, path, label, isRequired, adminDoc }, + value, + errors, + onChange, + isDisabled, +}) => { + const htmlID = `ks-daypicker-${path}`; return ( - - + + { - // There is a strange bug where after interacting with the day picker - // and then pressing enter on the input the value is changed to the start - // of the month. I think this is bug with the day picker. - // The following is a work-around: - if (e.key === 'Enter') { - e.preventDefault(); - } - }} - onChange={e => { - // Tiny bit of date format normalisation for convenience - const normalisedValue = e.target.value.replace('/', '-').replace('\\', '-'); - const parsedValue = parseISO(normalisedValue); - if (normalisedValue.length === 10 && isValid(parsedValue)) { - handleDayClick(parsedValue); - } else { - onChange(normalisedValue); - } - }} + date={value} + format={format} + onChange={onChange} disabled={isDisabled} css={{ color: isValid(parseISO(value)) ? undefined : 'darkred' }} value={value} diff --git a/packages/fields/src/types/DateTime/README.md b/packages/fields/src/types/DateTime/README.md index 974c8617bc5..de9a9e448ca 100644 --- a/packages/fields/src/types/DateTime/README.md +++ b/packages/fields/src/types/DateTime/README.md @@ -17,7 +17,7 @@ keystone.createList('User', { fields: { lastOnline: { type: DateTime, - format: 'MM/DD/YYYY h:mm A', + format: 'MM/dd/yyyy h:mm a', yearRangeFrom: 1901, yearRangeTo: 2018, yearPickerType: 'auto', @@ -39,7 +39,7 @@ keystone.createList('User', { #### `format` -Defines the format of the string that the component generates. For example, `MM/DD/YYYY h:mm A`. +Defines the format of the string that the component generates. For example, `MM/dd/yyyy h:mm A`. #### `yearRangeFrom` diff --git a/packages/list-plugins/README.md b/packages/list-plugins/README.md index 183a224cb20..5d9903bb11d 100644 --- a/packages/list-plugins/README.md +++ b/packages/list-plugins/README.md @@ -27,7 +27,7 @@ keystone.createList('ListWithPlugin', { | ---------------- | -------- | ------------------- | ----------------------------------------- | | `createdAtField` | `String` | `createdAt` | Name of the `createdAt` field. | | `updatedAtField` | `String` | `updatedAt` | Name of the `createdAt` field. | -| `format` | `String` | `MM/DD/YYYY h:mm A` | Format of the generated `DateTime` field. | +| `format` | `String` | `MM/dd/yyyy h:mm a` | Format of the generated `DateTime` field. | | `access` | `Object` | See: access | Change default access controls. | ### `access` diff --git a/packages/list-plugins/atTracking.md b/packages/list-plugins/atTracking.md index 8ed11240679..637b0d4d6d3 100644 --- a/packages/list-plugins/atTracking.md +++ b/packages/list-plugins/atTracking.md @@ -27,7 +27,7 @@ keystone.createList('ListWithPlugin', { | ---------------- | -------- | ------------------- | ----------------------------------------- | | `createdAtField` | `String` | `createdAt` | Name of the `createdAt` field. | | `updatedAtField` | `String` | `updatedAt` | Name of the `updatedAt` field. | -| `format` | `String` | `MM/DD/YYYY h:mm A` | Format of the generated `DateTime` field. | +| `format` | `String` | `MM/dd/yyyy h:mm a` | Format of the generated `DateTime` field. | | `access` | `Object` | See: access | Change default access controls. | ### `access` From df995155c1a7b93054ea493bcda516e4a4e61684 Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 9 Jul 2020 14:41:29 +1000 Subject: [PATCH 2/3] Remove test projects --- .../meetup/site/components/Navbar.js | 6 +-- demo-projects/meetup/site/lib/apolloClient.js | 42 +++++++++++++++++++ demo-projects/meetup/site/pages/_app.js | 37 +++------------- demo-projects/meetup/site/pages/about.js | 16 +++++++ demo-projects/meetup/site/pages/events.js | 15 +++++++ demo-projects/meetup/site/pages/index.js | 21 ++++++++-- demo-projects/todo/index.js | 24 +---------- 7 files changed, 101 insertions(+), 60 deletions(-) create mode 100644 demo-projects/meetup/site/lib/apolloClient.js diff --git a/demo-projects/meetup/site/components/Navbar.js b/demo-projects/meetup/site/components/Navbar.js index 6964c6af54b..85a923a3cc3 100644 --- a/demo-projects/meetup/site/components/Navbar.js +++ b/demo-projects/meetup/site/components/Navbar.js @@ -6,7 +6,7 @@ import { jsx } from '@emotion/core'; import Link from 'next/link'; import { useAuth } from '../lib/authentication'; -import { SignoutIcon } from '../primitives'; +import { SignoutIcon, Loading } from '../primitives'; import { getForegroundColor, useLogoDimension } from '../helpers'; import { mq } from '../helpers/media'; import { fontSizes, gridSize, shadows } from '../theme'; @@ -143,7 +143,7 @@ const AnonActions = () => { }; const Navbar = ({ background = 'white', ...props }) => { - const { isAuthenticated, user } = useAuth(); + const { isAuthenticated, user, isLoading } = useAuth(); const { logoWidth, logoHeight, logoWidthSm, logoHeightSm } = useLogoDimension(); const foreground = getForegroundColor(background); @@ -171,7 +171,7 @@ const Navbar = ({ background = 'white', ...props }) => { About Events - {isAuthenticated ? : } + {isLoading ? : isAuthenticated ? : } ); diff --git a/demo-projects/meetup/site/lib/apolloClient.js b/demo-projects/meetup/site/lib/apolloClient.js new file mode 100644 index 00000000000..794ad661646 --- /dev/null +++ b/demo-projects/meetup/site/lib/apolloClient.js @@ -0,0 +1,42 @@ +// Derived from https://github.com/vercel/next.js/blob/canary/examples/with-apollo/lib/apolloClient.js +import { useMemo } from 'react'; +import { ApolloClient } from 'apollo-client'; +import { InMemoryCache } from 'apollo-cache-inmemory'; +import { createUploadLink } from 'apollo-upload-client'; + +let apolloClient; + +function createApolloClient(req) { + return new ApolloClient({ + ssrMode: typeof window === 'undefined', + link: createUploadLink({ + // TODO: server-side requests must have an absolute URI. We should find a way + // to make this part of the project config, seems highly opinionated here + uri: 'http://localhost:3000/admin/api', + credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers` + headers: req && req.headers, + }), + cache: new InMemoryCache(), + }); +} + +export function initializeApollo(initialState = null, req) { + const _apolloClient = apolloClient ?? createApolloClient(req); + + // If your page has Next.js data fetching methods that use Apollo Client, the initial state + // gets hydrated here + if (initialState) { + _apolloClient.cache.restore(initialState); + } + // For SSG and SSR always create a new Apollo Client + if (typeof window === 'undefined') return _apolloClient; + // Create the Apollo Client once in the client + if (!apolloClient) apolloClient = _apolloClient; + + return _apolloClient; +} + +export function useApollo(initialState) { + const store = useMemo(() => initializeApollo(initialState), [initialState]); + return store; +} diff --git a/demo-projects/meetup/site/pages/_app.js b/demo-projects/meetup/site/pages/_app.js index d18676489bb..2623f22ecdd 100644 --- a/demo-projects/meetup/site/pages/_app.js +++ b/demo-projects/meetup/site/pages/_app.js @@ -1,19 +1,17 @@ import React from 'react'; import Head from 'next/head'; -import gql from 'graphql-tag'; -import { ApolloProvider } from '@apollo/react-hooks'; import { ToastProvider } from 'react-toast-notifications'; - -import withApollo from '../lib/withApollo'; import { AuthProvider } from '../lib/authentication'; import StylesBase from '../primitives/StylesBase'; import GoogleAnalytics from '../components/GoogleAnalytics'; - -const MyApp = ({ Component, pageProps, apolloClient, user }) => { +import { useApollo } from '../lib/apolloClient'; +import { ApolloProvider } from '@apollo/react-hooks'; +const MyApp = ({ Component, pageProps }) => { + const apolloClient = useApollo(pageProps.initialApolloState); return ( - + { ); }; -MyApp.getInitialProps = async ({ Component, ctx }) => { - let pageProps = {}; - - const data = await ctx.apolloClient.query({ - query: gql` - query { - authenticatedUser { - id - name - isAdmin - } - } - `, - fetchPolicy: 'network-only', - }); - - if (Component.getInitialProps) { - pageProps = await Component.getInitialProps(ctx); - } - - return { pageProps, user: data.data ? data.data.authenticatedUser : undefined }; -}; - -export default withApollo(MyApp); +export default MyApp; diff --git a/demo-projects/meetup/site/pages/about.js b/demo-projects/meetup/site/pages/about.js index 711b017a01b..370142dcac4 100644 --- a/demo-projects/meetup/site/pages/about.js +++ b/demo-projects/meetup/site/pages/about.js @@ -11,6 +11,7 @@ import Meta from '../components/Meta'; import { colors, gridSize } from '../theme'; import { GET_ORGANISERS } from '../graphql/organisers'; import { mq } from '../helpers/media'; +import { initializeApollo } from '../lib/apolloClient'; const { publicRuntimeConfig } = getConfig(); @@ -121,3 +122,18 @@ const Organiser = ({ organiser }) => ( ); const Content = props =>
; + +export async function getStaticProps() { + const apolloClient = initializeApollo(); + + await apolloClient.query({ + query: GET_ORGANISERS, + }); + + return { + props: { + initialApolloState: apolloClient.cache.extract(), + }, + unstable_revalidate: 1, + }; +} diff --git a/demo-projects/meetup/site/pages/events.js b/demo-projects/meetup/site/pages/events.js index b7481f2f943..911432745a8 100644 --- a/demo-projects/meetup/site/pages/events.js +++ b/demo-projects/meetup/site/pages/events.js @@ -10,6 +10,7 @@ import Navbar from '../components/Navbar'; import Footer from '../components/Footer'; import Meta from '../components/Meta'; import { gridSize } from '../theme'; +import { initializeApollo } from '../lib/apolloClient'; import { GET_ALL_EVENTS } from '../graphql/events'; @@ -38,3 +39,17 @@ export default function Events() { ); } +export async function getStaticProps() { + const apolloClient = initializeApollo(); + + await apolloClient.query({ + query: GET_ALL_EVENTS, + }); + + return { + props: { + initialApolloState: apolloClient.cache.extract(), + }, + unstable_revalidate: 1, + }; +} diff --git a/demo-projects/meetup/site/pages/index.js b/demo-projects/meetup/site/pages/index.js index 347be873377..946b59d2a79 100644 --- a/demo-projects/meetup/site/pages/index.js +++ b/demo-projects/meetup/site/pages/index.js @@ -11,6 +11,7 @@ import Meta from '../components/Meta'; import { GET_CURRENT_EVENTS } from '../graphql/events'; import { GET_EVENT_RSVPS } from '../graphql/rsvps'; import { GET_SPONSORS } from '../graphql/sponsors'; +import { initializeApollo } from '../lib/apolloClient'; import Talks from '../components/Talks'; import Rsvp from '../components/Rsvp'; @@ -328,9 +329,23 @@ const Home = ({ now }) => { ); }; -Home.getInitialProps = async () => ({ - now: new Date().toISOString(), -}); +export async function getStaticProps() { + const apolloClient = initializeApollo(); + + const now = new Date().toISOString(); + await apolloClient.query({ + query: GET_CURRENT_EVENTS, + variables: { now }, + }); + + return { + props: { + initialApolloState: apolloClient.cache.extract(), + now, + }, + unstable_revalidate: 1, + }; +} export default Home; diff --git a/demo-projects/todo/index.js b/demo-projects/todo/index.js index c83b5d6f78d..957d2d5b3e3 100644 --- a/demo-projects/todo/index.js +++ b/demo-projects/todo/index.js @@ -1,20 +1,9 @@ const { Keystone } = require('@keystonejs/keystone'); const { MongooseAdapter } = require('@keystonejs/adapter-mongoose'); -const { Text, CalendarDay, Virtual } = require('@keystonejs/fields'); +const { Text } = require('@keystonejs/fields'); const { GraphQLApp } = require('@keystonejs/app-graphql'); const { AdminUIApp } = require('@keystonejs/app-admin-ui'); const { StaticApp } = require('@keystonejs/app-static'); -const { format, parseISO } = require('date-fns'); - -class _CalendarDayImpl extends CalendarDay.implementation { - gqlOutputFieldResolvers() { - return { - [`${this.path}`]: item => - item[this.path] && format(parseISO(item[this.path]), this.config.gqlFormat), - }; - } -} -const _CalendarDay = { ...CalendarDay, implementation: _CalendarDayImpl }; const keystone = new Keystone({ adapter: new MongooseAdapter({ mongoUri: 'mongodb://localhost/todo' }), @@ -24,17 +13,6 @@ keystone.createList('Todo', { schemaDoc: 'A list of things which need to be done', fields: { name: { type: Text, schemaDoc: 'This is the thing you need to do', isRequired: true }, - date: { type: CalendarDay, adminDoc: 'For testing only' }, - niceDate: { - type: Virtual, - resolver: item => item.date && format(parseISO(item.date), 'do MMMM, yyyy'), - }, - nicerDate: { - type: Virtual, - resolver: (item, { formatAs = 'do MMMM, yyyy' }) => - item.date && format(parseISO(item.date), formatAs), - param: 'formatAs: String', - }, }, }); From 2664c8c450a2f47b0b548d3046aeb5305c90b475 Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 9 Jul 2020 14:50:45 +1000 Subject: [PATCH 3/3] merge changes to calendarDay --- docs/tutorials/add-lists.md | 5 +-- .../src/types/CalendarDay/Implementation.js | 4 +- .../fields/src/types/CalendarDay/README.md | 4 +- .../src/types/CalendarDay/views/Cell.js | 15 +++++++- .../src/types/CalendarDay/views/Controller.js | 6 --- .../src/types/CalendarDay/views/Field.js | 38 ++++++++++++------- 6 files changed, 42 insertions(+), 30 deletions(-) diff --git a/docs/tutorials/add-lists.md b/docs/tutorials/add-lists.md index d80792a2e77..5d60da64aeb 100644 --- a/docs/tutorials/add-lists.md +++ b/docs/tutorials/add-lists.md @@ -69,9 +69,8 @@ module.exports = { // added fields deadline: { type: CalendarDay, - format: 'do MMMM yyyy', - yearRangeFrom: '2019', - yearRangeTo: '2029', + dateFrom: '2019-01-01', + dateTo: '2029-01-01', isRequired: false, defaultValue: new Date().toISOString('YYYY-MM-DD').substring(0, 10), // Today's date }, diff --git a/packages/fields/src/types/CalendarDay/Implementation.js b/packages/fields/src/types/CalendarDay/Implementation.js index 7b40b4f9482..d24a3f1fed0 100644 --- a/packages/fields/src/types/CalendarDay/Implementation.js +++ b/packages/fields/src/types/CalendarDay/Implementation.js @@ -36,9 +36,7 @@ export class CalendarDay extends Implementation { gqlOutputFields() { return [`${this.path}: String`]; } - gqlOutputFieldResolvers() { - return { [`${this.path}`]: item => item[this.path] }; - } + gqlQueryInputFields() { return [ ...this.equalityInputFields('String'), diff --git a/packages/fields/src/types/CalendarDay/README.md b/packages/fields/src/types/CalendarDay/README.md index 8ff2dc02359..abdf2b58450 100644 --- a/packages/fields/src/types/CalendarDay/README.md +++ b/packages/fields/src/types/CalendarDay/README.md @@ -52,7 +52,7 @@ All date values must be in the 10 character ISO8601 format:`YYYY-MM-DD`. ### Filters -All filter fields expect values in the ISO8601 (`yyyy-MM-dd`) format. +All filter fields expect values in the ISO8601 (`YYYY-MM-DD`) format. | Field name | Type | Description | | :--------------- | :--------- | :----------------------------------------- | @@ -69,7 +69,7 @@ All filter fields expect values in the ISO8601 (`yyyy-MM-dd`) format. ### Mongoose adapter -In Mongoose the field is stored using the `String` schema type. +In Mongoose the field is added using the `String` schema type. The `isRequired` config option is enforced by KeystoneJS only. diff --git a/packages/fields/src/types/CalendarDay/views/Cell.js b/packages/fields/src/types/CalendarDay/views/Cell.js index c30292442a2..ded38dd7e6a 100644 --- a/packages/fields/src/types/CalendarDay/views/Cell.js +++ b/packages/fields/src/types/CalendarDay/views/Cell.js @@ -1,4 +1,15 @@ import { format, parseISO } from 'date-fns'; -export default ({ data, field: { format: formatString } }) => - data ? (formatString ? format(parseISO(data), formatString) : data) : null; +const CalendarDayCell = ({ data, field: { config } }) => { + if (!data) { + return null; + } + + if (!config.format) { + return data; + } + + return format(parseISO(data), config.format); +}; + +export default CalendarDayCell; diff --git a/packages/fields/src/types/CalendarDay/views/Controller.js b/packages/fields/src/types/CalendarDay/views/Controller.js index e20987daba0..d699814394e 100644 --- a/packages/fields/src/types/CalendarDay/views/Controller.js +++ b/packages/fields/src/types/CalendarDay/views/Controller.js @@ -1,12 +1,6 @@ import FieldController from '../../../Controller'; export default class CalendarDayController extends FieldController { - constructor({ format, yearRangeFrom, yearRangeTo, ...config }, ...args) { - super({ ...config }, ...args); - this.format = format; - this.yearRangeFrom = yearRangeFrom; - this.yearRangeTo = yearRangeTo; - } getFilterGraphQL = ({ type, value }) => { const key = type === 'is' ? `${this.path}` : `${this.path}_${type}`; return { [key]: value }; diff --git a/packages/fields/src/types/CalendarDay/views/Field.js b/packages/fields/src/types/CalendarDay/views/Field.js index 4c4225d97c2..5126cf60ce7 100644 --- a/packages/fields/src/types/CalendarDay/views/Field.js +++ b/packages/fields/src/types/CalendarDay/views/Field.js @@ -8,20 +8,14 @@ import 'react-day-picker/dist/style.css'; import { DayPicker } from 'react-day-picker'; import { parseISO, compareAsc, formatISO, isValid } from 'date-fns'; -const CalendarDayField = ({ - autoFocus, - field: { format, path, label, isRequired, adminDoc }, - value, - errors, - onChange, - isDisabled, -}) => { - const htmlID = `ks-daypicker-${path}`; +const CalendarDayField = ({ autoFocus, field, value, errors, onChange, isDisabled }) => { + const htmlID = `ks-daypicker-${field.path}`; + const handleDayClick = day => onChange(formatISO(day, { representation: 'date' })); return ( - - + + { + // There is a strange bug where after interacting with the day picker + // and then pressing enter on the input the value is changed to the start + // of the month. I think this is bug with the day picker. + // The following is a work-around: + if (e.key === 'Enter') { + e.preventDefault(); + } + }} + onChange={e => { + // Tiny bit of date format normalisation for convenience + const normalisedValue = e.target.value.replace('/', '-').replace('\\', '-'); + const parsedValue = parseISO(normalisedValue); + if (normalisedValue.length === 10 && isValid(parsedValue)) { + handleDayClick(parsedValue); + } else { + onChange(normalisedValue); + } + }} disabled={isDisabled} css={{ color: isValid(parseISO(value)) ? undefined : 'darkred' }} value={value}