Allowing override locale (only for dates) and dates format

https://github.com/Camberi/firecms/issues/58
This commit is contained in:
francesco
2021-03-09 16:50:43 +01:00
parent c5566f43f7
commit 9daaebab5b
10 changed files with 166 additions and 40 deletions

View File

@@ -54,6 +54,7 @@ yarn add @camberi/firecms
## Features
### CMS
- [x] Real-time Collection views for entities
- [x] Infinite scrolling in collections with optional pagination
- [x] Collection text search integration
@@ -66,6 +67,7 @@ yarn add @camberi/firecms
- [ ] Allow set up of a project using a CLI create-firecms-app
### Entity edition
- [x] Create, read, update, delete views
- [x] Form for editing entities
- [x] Implementation of fields for every property (except Geopoint)
@@ -86,7 +88,6 @@ you do not need a specific backend to make it run. Just build your project
following the installation instructions and deploy it in the way you prefer. A
very easy way is using Firebase Hosting.
## Firebase requirements
You need to enable the Firestore database in your Firebase project. If you have
@@ -117,8 +118,8 @@ picked up automatically.
```yarn add @camberi/firecms @material-ui/core @material-ui/icons @material-ui/pickers firebase```
You can replace the content of the file App.tsx with the following sample code.
Remember to replace the Firebase config with the one you get after creating
a webapp in the Firebase console.
Remember to replace the Firebase config with the one you get after creating a
webapp in the Firebase console.
```tsx
import React from "react";
@@ -389,7 +390,15 @@ the following specs:
- `toolbarExtraWidget` A component that gets rendered on the upper side of the
main toolbar.
- `schemaResolver`:
- `dateTimeFormat` Format of the dates in the CMS. Defaults to 'MMMM dd, yyyy,
HH:mm:ss'
- `locale` Locale of the CMS, currently only affecting dates
- `schemaResolver` Used to override schemas based on the collection path and
entityId. This resolver allows to override the schema for specific entities,
or specific collections, app wide. This overrides schemas all through the app.
You can also override schemas in place, when using `useSideEntityController`
## Entities configuration
@@ -596,14 +605,13 @@ the `CMSFieldProps` [here](https://github.com/Camberi/firecms/blob/master/src/fo
You can also pass custom props to your custom field, which you then receive in
the `customProps`.
If you are developing a custom field and need to access the values of the entity,
you can use the `context` field in CMSFieldProps.
If you are developing a custom field and need to access the values of the
entity, you can use the `context` field in CMSFieldProps.
#### Saving callbacks
When you are saving an entity you can attach diff
erent hooks before and after it
gets saved: `onPreSave`, `onSaveSuccess` and `onSaveFailure`.
When you are saving an entity you can attach diff erent hooks before and after
it gets saved: `onPreSave`, `onSaveSuccess` and `onSaveFailure`.
```
const productSchema = buildSchema({
@@ -841,15 +849,13 @@ The props provided by this context are:
* `close()` Close the last panel
* `sidePanels` List of side entity panels currently open
* `open (props: SideEntityPanelProps & Partial<SchemaSidePanelProps>)`
Open a new entity sideDialog. By default, the schema and configuration
of the view is fetched from the collections you have specified in the
navigation.
At least you need to pass the collectionPath of the entity you would like
to edit. You can set an entityId if you would like to edit and existing one
(or a new one with that id).
If you wish, you can also override the `SchemaSidePanelProps` (such
as schema or subcollections) and choose to override the CMSApp
level `SchemaResolver`.
Open a new entity sideDialog. By default, the schema and configuration of the
view is fetched from the collections you have specified in the navigation. At
least you need to pass the collectionPath of the entity you would like to
edit. You can set an entityId if you would like to edit and existing one
(or a new one with that id). If you wish, you can also override
the `SchemaSidePanelProps` (such as schema or subcollections) and choose to
override the CMSApp level `SchemaResolver`.
Example:

View File

@@ -607,6 +607,10 @@ function SampleApp() {
markdown: true
}
},
test_date: {
title: "Test date",
dataType: "timestamp",
},
created_at: {
title: "Created at",
dataType: "timestamp",

View File

@@ -9,7 +9,9 @@ import {
ThemeProvider
} from "@material-ui/core";
import { MuiPickersUtilsProvider } from "@material-ui/pickers";
import DateFnsUtils from "@date-io/date-fns";
import * as locales from "date-fns/locale";
import { BrowserRouter as Router } from "react-router-dom";
@@ -90,9 +92,10 @@ export function CMSApp(props: CMSAppProps) {
fontFamily,
toolbarExtraWidget,
schemaResolver,
locale,
signInOptions = [
firebase.auth.GoogleAuthProvider.PROVIDER_ID
],
]
} = props;
const classes = useStyles();
@@ -251,14 +254,18 @@ export function CMSApp(props: CMSAppProps) {
signInOptions={signInOptions}/>;
}
const dateUtilsLocale = locale ? locales[locale] : undefined;
function renderMainView() {
return (
<Router>
<SchemaRegistryProvider navigation={navigation} schemaResolver={schemaResolver}>
<SchemaRegistryProvider navigation={navigation}
schemaResolver={schemaResolver}>
<SideEntityProvider navigation={navigation}>
<BreadcrumbsProvider>
<MuiPickersUtilsProvider
utils={DateFnsUtils}>
utils={DateFnsUtils}
locale={dateUtilsLocale}>
<DndProvider backend={HTML5Backend}>
<nav>

View File

@@ -4,11 +4,7 @@ import "firebase/auth";
import "firebase/storage";
import "firebase/firestore";
import { Authenticator, EntityCollection } from "./models";
import {
SchemaResolver,
SchemaSidePanelProps,
SideEntityPanelProps
} from "./side_dialog/model";
import { SchemaResolver } from "./side_dialog/model";
/**
* Main entry point that defines the CMS configuration
@@ -80,7 +76,7 @@ export interface CMSAppProps {
primaryColor?: string;
/**
* Primary color of the theme of the CMS
* Secondary color of the theme of the CMS
*/
secondaryColor?: string
@@ -96,6 +92,17 @@ export interface CMSAppProps {
*/
toolbarExtraWidget?: React.ReactNode;
/**
* Format of the dates in the CMS.
* Defaults to 'MMMM dd, yyyy, HH:mm:ss'
*/
dateTimeFormat?: string;
/**
* Locale of the CMS, currently only affecting dates
*/
locale?: Locale;
/**
* Used to override schemas based on the collection path and entityId.
* This resolver allows to override the schema for specific entities, or
@@ -134,3 +141,83 @@ export interface AdditionalView {
group?: string;
}
export type Locale =
"af" |
"ar" |
"arDZ" |
"arMA" |
"arSA" |
"az" |
"be" |
"bg" |
"bn" |
"ca" |
"cs" |
"cy" |
"da" |
"de" |
"el" |
"enAU" |
"enCA" |
"enGB" |
"enIN" |
"enNZ" |
"enUS" |
"eo" |
"es" |
"et" |
"eu" |
"faIR" |
"fi" |
"fil" |
"fr" |
"frCA" |
"frCH" |
"gd" |
"gl" |
"gu" |
"he" |
"hi" |
"hr" |
"hu" |
"hy" |
"id" |
"is" |
"it" |
"ja" |
"ka" |
"kk" |
"kn" |
"ko" |
"lb" |
"lt" |
"lv" |
"mk" |
"ms" |
"mt" |
"nb" |
"nl" |
"nlBE" |
"nn" |
"pl" |
"pt" |
"ptBR" |
"ro" |
"ru" |
"sk" |
"sl" |
"sr" |
"srLatn" |
"sv" |
"ta" |
"te" |
"th" |
"tr" |
"ug" |
"uk" |
"uz" |
"vi" |
"zhCN" |
"zhTW";

View File

@@ -9,7 +9,7 @@ import {
Property,
ReferenceProperty,
saveEntity,
StringProperty
StringProperty, TimestampProperty
} from "../models";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { createStyles, makeStyles, Theme } from "@material-ui/core";
@@ -227,6 +227,7 @@ const PropertyTableCell = <T, S extends EntitySchema<Key>, Key extends string>({
focused={focused}
internalValue={internalValue as Date}
updateValue={updateValue}
property={property as TimestampProperty}
setPreventOutsideClick={setPreventOutsideClick}
/>;
allowScroll = true;

View File

@@ -2,8 +2,9 @@ import React, { useState } from "react";
import { useInputStyles } from "./styles";
import { KeyboardDateTimePicker } from "@material-ui/pickers";
import { Box, Typography } from "@material-ui/core";
import { EmptyValue } from "../../preview";
import { EmptyValue, TimestampPreview } from "../../preview";
import CalendarTodayIcon from "@material-ui/icons/CalendarToday";
import { TimestampProperty } from "../../models";
export function TableDateField(props: {
name: string;
@@ -12,11 +13,12 @@ export function TableDateField(props: {
updateValue: (newValue: (Date | null)) => void;
focused: boolean;
disabled: boolean;
property: TimestampProperty;
onBlur?: React.FocusEventHandler<HTMLInputElement | HTMLTextAreaElement>;
setPreventOutsideClick: (value: any) => void;
}) {
const { disabled, error, internalValue, setPreventOutsideClick, updateValue } = props;
const { disabled, error, internalValue, setPreventOutsideClick, updateValue, property } = props;
const [open, setOpen] = useState<boolean>(false);
const handleOpen = () => {
@@ -37,7 +39,7 @@ export function TableDateField(props: {
<Box flexGrow={1}>
{internalValue &&
<Typography variant={"body2"}>
{internalValue.toLocaleString()}
<TimestampPreview value={internalValue} property={property} size={"regular"}/>
</Typography>}
{!internalValue && <EmptyValue/>}
</Box>

View File

@@ -1,12 +1,14 @@
import React from "react";
import { DateTimePicker } from "@material-ui/pickers";
import { FieldProps } from "../../models/form_props";
import { FieldProps } from "../../models";
import { FieldDescription } from "../../components";
import { LabelWithIcon } from "../components/LabelWithIcon";
import { useClearRestoreValue } from "../useClearRestoreValue";
import firebase from "firebase";
import { CMSAppProps } from "../../CMSAppProps";
import { useAppConfigContext } from "../../contexts";
import { defaultDateFormat } from "../../util/dates";
type DateTimeFieldProps = FieldProps<Date>;
@@ -27,6 +29,9 @@ export default function DateTimeField({
const internalValue = value || null;
const appConfig: CMSAppProps | undefined = useAppConfigContext();
const dateFormat: string = appConfig?.dateTimeFormat ?? defaultDateFormat;
useClearRestoreValue({
property,
value,
@@ -41,6 +46,7 @@ export default function DateTimeField({
clearable
autoFocus={autoFocus}
value={internalValue}
format={dateFormat}
label={<LabelWithIcon scaledIcon={false} property={property}/>}
error={showError}
disabled={disabled}

View File

@@ -42,7 +42,7 @@ export type { PreviewComponentProps } from "./preview";
export { CMSApp } from "./CMSApp";
export type {
CMSAppProps, AdditionalView
CMSAppProps, AdditionalView, Locale
} from "./CMSAppProps";
@@ -68,11 +68,11 @@ export {
export {
FieldDescription,
ErrorBoundary,
ErrorBoundary
} from "./components";
export {
default as EntityPreview,
default as EntityPreview
} from "./components/EntityPreview";
export {

View File

@@ -1,17 +1,29 @@
import { PreviewComponentProps } from "../../preview";
import React from "react";
import firebase from "firebase/app";
import { CMSAppProps } from "../../CMSAppProps";
import { useAppConfigContext } from "../../contexts";
import format from "date-fns/format";
import * as locales from "date-fns/locale";
import { defaultDateFormat } from "../../util/dates";
export function TimestampPreview({
name,
value,
property,
size,
}: PreviewComponentProps<firebase.firestore.Timestamp | Date>): React.ReactElement {
size
}: PreviewComponentProps<Date>): React.ReactElement {
const appConfig: CMSAppProps | undefined = useAppConfigContext();
const dateUtilsLocale = appConfig.locale ? locales[appConfig.locale] : undefined;
const dateFormat: string = appConfig?.dateTimeFormat ?? defaultDateFormat;
const formattedDate = value ? format(value, dateFormat, { locale: dateUtilsLocale }) : "";
return (
<>
{value.toLocaleString()}
{formattedDate}
</>
);
}

1
src/util/dates.ts Normal file
View File

@@ -0,0 +1 @@
export const defaultDateFormat = "MMMM dd, yyyy, HH:mm:ss";