Skip to content

Commit

Permalink
Add missing material theme string widgets (#1789)
Browse files Browse the repository at this point in the history
* Add missing material UI string widgets

* Update snapshots for new widgets

* Update snapshots for additional format types

* Support full parameter options for TextField and disable shrink on Date widgets

* Fix types and update snapshots

* Fix formatting for datetime widget

* Update snapshots

* Fix test
  • Loading branch information
stanlemon committed Jun 8, 2020
1 parent fdea78a commit 4a05644
Show file tree
Hide file tree
Showing 20 changed files with 996 additions and 91 deletions.
5 changes: 5 additions & 0 deletions packages/core/index.d.ts
Expand Up @@ -109,6 +109,7 @@ declare module '@rjsf/core' {
onBlur: (id: string, value: boolean | number | string | null) => void;
onFocus: (id: string, value: boolean | number | string | null) => void;
label: string;
type: string;
multiple: boolean;
rawErrors: string[];
}
Expand Down Expand Up @@ -389,6 +390,10 @@ declare module '@rjsf/core' {

export function toDateString(dateObject: DateObject, time?: boolean): string;

export function utcToLocal(jsonDate: string): string;

export function localToUTC(dateString: string): Date;

export function pad(num: number, size: number): string;

export function setState(instance: React.Component, state: any, callback: Function): void;
Expand Down
32 changes: 1 addition & 31 deletions packages/core/src/components/widgets/DateTimeWidget.js
@@ -1,36 +1,6 @@
import React from "react";
import PropTypes from "prop-types";
import { pad } from "../../utils";

export function utcToLocal(jsonDate) {
if (!jsonDate) {
return "";
}

// required format of `"yyyy-MM-ddThh:mm" followed by optional ":ss" or ":ss.SSS"
// https://html.spec.whatwg.org/multipage/input.html#local-date-and-time-state-(type%3Ddatetime-local)
// > should be a _valid local date and time string_ (not GMT)

// Note - date constructor passed local ISO-8601 does not correctly
// change time to UTC in node pre-8
const date = new Date(jsonDate);

const yyyy = pad(date.getFullYear(), 4);
const MM = pad(date.getMonth() + 1, 2);
const dd = pad(date.getDate(), 2);
const hh = pad(date.getHours(), 2);
const mm = pad(date.getMinutes(), 2);
const ss = pad(date.getSeconds(), 2);
const SSS = pad(date.getMilliseconds(), 3);

return `${yyyy}-${MM}-${dd}T${hh}:${mm}:${ss}.${SSS}`;
}

export function localToUTC(dateString) {
if (dateString) {
return new Date(dateString).toJSON();
}
}
import { utcToLocal, localToUTC } from "../../utils";

function DateTimeWidget(props) {
const {
Expand Down
30 changes: 30 additions & 0 deletions packages/core/src/utils.js
Expand Up @@ -1048,6 +1048,36 @@ export function toDateString(
return time ? datetime : datetime.slice(0, 10);
}

export function utcToLocal(jsonDate) {
if (!jsonDate) {
return "";
}

// required format of `"yyyy-MM-ddThh:mm" followed by optional ":ss" or ":ss.SSS"
// https://html.spec.whatwg.org/multipage/input.html#local-date-and-time-state-(type%3Ddatetime-local)
// > should be a _valid local date and time string_ (not GMT)

// Note - date constructor passed local ISO-8601 does not correctly
// change time to UTC in node pre-8
const date = new Date(jsonDate);

const yyyy = pad(date.getFullYear(), 4);
const MM = pad(date.getMonth() + 1, 2);
const dd = pad(date.getDate(), 2);
const hh = pad(date.getHours(), 2);
const mm = pad(date.getMinutes(), 2);
const ss = pad(date.getSeconds(), 2);
const SSS = pad(date.getMilliseconds(), 3);

return `${yyyy}-${MM}-${dd}T${hh}:${mm}:${ss}.${SSS}`;
}

export function localToUTC(dateString) {
if (dateString) {
return new Date(dateString).toJSON();
}
}

export function pad(num, size) {
let s = String(num);
while (s.length < size) {
Expand Down
3 changes: 1 addition & 2 deletions packages/core/test/StringField_test.js
Expand Up @@ -3,8 +3,7 @@ import { expect } from "chai";
import { Simulate } from "react-dom/test-utils";
import sinon from "sinon";

import { parseDateString, toDateString } from "../src/utils";
import { utcToLocal } from "../src/components/widgets/DateTimeWidget";
import { parseDateString, toDateString, utcToLocal } from "../src/utils";
import { createFormComponent, createSandbox, submitForm } from "./test_utils";

describe("StringField", () => {
Expand Down
8 changes: 8 additions & 0 deletions packages/material-ui/src/ColorWidget/ColorWidget.tsx
@@ -0,0 +1,8 @@
import React from "react";
import TextWidget, { TextWidgetProps } from "../TextWidget";

const ColorWidget = (props: TextWidgetProps) => {
return <TextWidget type="color" {...props} />;
};

export default ColorWidget;
2 changes: 2 additions & 0 deletions packages/material-ui/src/ColorWidget/index.ts
@@ -0,0 +1,2 @@
export { default } from "./ColorWidget";
export * from "./ColorWidget";
26 changes: 26 additions & 0 deletions packages/material-ui/src/DateTimeWidget/DateTimeWidget.tsx
@@ -0,0 +1,26 @@
import React from "react";
import { utils } from "@rjsf/core";
import TextWidget, { TextWidgetProps } from "../TextWidget";

const { localToUTC, utcToLocal } = utils;

const DateTimeWidget = (props: TextWidgetProps) => {
const value = utcToLocal(props.value);
const onChange = (value: any) => {
props.onChange(localToUTC(value));
};

return (
<TextWidget
type="datetime-local"
InputLabelProps={{
shrink: true,
}}
{...props}
value={value}
onChange={onChange}
/>
);
};

export default DateTimeWidget;
2 changes: 2 additions & 0 deletions packages/material-ui/src/DateTimeWidget/index.ts
@@ -0,0 +1,2 @@
export { default } from "./DateTimeWidget";
export * from "./DateTimeWidget";
16 changes: 16 additions & 0 deletions packages/material-ui/src/DateWidget/DateWidget.tsx
@@ -0,0 +1,16 @@
import React from "react";
import TextWidget, { TextWidgetProps } from "../TextWidget";

const DateWidget = (props: TextWidgetProps) => {
return (
<TextWidget
type="date"
InputLabelProps={{
shrink: true,
}}
{...props}
/>
);
};

export default DateWidget;
2 changes: 2 additions & 0 deletions packages/material-ui/src/DateWidget/index.ts
@@ -0,0 +1,2 @@
export { default } from "./DateWidget";
export * from "./DateWidget";
8 changes: 8 additions & 0 deletions packages/material-ui/src/EmailWidget/EmailWidget.tsx
@@ -0,0 +1,8 @@
import React from "react";
import TextWidget, { TextWidgetProps } from "../TextWidget";

const EmailWidget = (props: TextWidgetProps) => {
return <TextWidget type="email" {...props} />;
};

export default EmailWidget;
2 changes: 2 additions & 0 deletions packages/material-ui/src/EmailWidget/index.ts
@@ -0,0 +1,2 @@
export { default } from './EmailWidget';
export * from './EmailWidget';
27 changes: 16 additions & 11 deletions packages/material-ui/src/TextWidget/TextWidget.tsx
@@ -1,15 +1,20 @@
import React from 'react';
import React from "react";

import FormControl from '@material-ui/core/FormControl';
import TextField from '@material-ui/core/TextField';
import FormControl from "@material-ui/core/FormControl";
import TextField, {
StandardTextFieldProps as TextFieldProps,
} from "@material-ui/core/TextField";

import { WidgetProps } from '@rjsf/core';
import { WidgetProps } from "@rjsf/core";

export type TextWidgetProps = WidgetProps & TextFieldProps;

const TextWidget = ({
id,
required,
readonly,
disabled,
type,
label,
value,
onChange,
Expand All @@ -18,11 +23,12 @@ const TextWidget = ({
autofocus,
options,
schema,
}: WidgetProps) => {
...textFieldProps
}: TextWidgetProps) => {
const _onChange = ({
target: { value },
}: React.ChangeEvent<HTMLInputElement>) =>
onChange(value === '' ? options.emptyValue : value);
onChange(value === "" ? options.emptyValue : value);
const _onBlur = ({ target: { value } }: React.FocusEvent<HTMLInputElement>) =>
onBlur(id, value);
const _onFocus = ({
Expand All @@ -33,20 +39,19 @@ const TextWidget = ({
<FormControl
fullWidth={true}
//error={!!rawErrors}
required={required}
>
required={required}>
<TextField
id={id}
label={label || schema.title}
autoFocus={autofocus}
required={required}
disabled={disabled || readonly}
name={name}
type={schema.type as string}
value={value || (value === 0) ? value : ''}
type={type || (schema.type as string)}
value={value || value === 0 ? value : ""}
onChange={_onChange}
onBlur={_onBlur}
onFocus={_onFocus}
{...(textFieldProps as TextFieldProps)}
/>
</FormControl>
);
Expand Down
8 changes: 8 additions & 0 deletions packages/material-ui/src/URLWidget/URLWidget.tsx
@@ -0,0 +1,8 @@
import React from "react";
import TextWidget, { TextWidgetProps } from "../TextWidget";

const URLWidget = (props: TextWidgetProps) => {
return <TextWidget type="url" {...props} />;
};

export default URLWidget;
2 changes: 2 additions & 0 deletions packages/material-ui/src/URLWidget/index.ts
@@ -0,0 +1,2 @@
export { default } from "./URLWidget";
export * from "./URLWidget";
28 changes: 19 additions & 9 deletions packages/material-ui/src/Widgets/Widgets.ts
@@ -1,21 +1,31 @@
import CheckboxWidget from '../CheckboxWidget/CheckboxWidget';
import CheckboxesWidget from '../CheckboxesWidget/CheckboxesWidget';
import PasswordWidget from '../PasswordWidget/PasswordWidget';
import RadioWidget from '../RadioWidget/RadioWidget';
import RangeWidget from '../RangeWidget/RangeWidget';
import SelectWidget from '../SelectWidget/SelectWidget';
import TextareaWidget from '../TextareaWidget/TextareaWidget';
import TextWidget from '../TextWidget/TextWidget';
import UpDownWidget from '../UpDownWidget/UpDownWidget';
import CheckboxWidget from "../CheckboxWidget/CheckboxWidget";
import CheckboxesWidget from "../CheckboxesWidget/CheckboxesWidget";
import ColorWidget from "../ColorWidget/ColorWidget";
import DateWidget from "../DateWidget/DateWidget";
import DateTimeWidget from "../DateTimeWidget/DateTimeWidget";
import EmailWidget from "../EmailWidget/EmailWidget";
import PasswordWidget from "../PasswordWidget/PasswordWidget";
import RadioWidget from "../RadioWidget/RadioWidget";
import RangeWidget from "../RangeWidget/RangeWidget";
import SelectWidget from "../SelectWidget/SelectWidget";
import TextareaWidget from "../TextareaWidget/TextareaWidget";
import TextWidget from "../TextWidget/TextWidget";
import UpDownWidget from "../UpDownWidget/UpDownWidget";
import URLWidget from "../URLWidget/URLWidget";

export default {
CheckboxWidget,
CheckboxesWidget,
ColorWidget,
DateWidget,
DateTimeWidget,
EmailWidget,
PasswordWidget,
RadioWidget,
RangeWidget,
SelectWidget,
TextareaWidget,
TextWidget,
UpDownWidget,
URLWidget,
};
26 changes: 25 additions & 1 deletion packages/material-ui/test/Form.test.tsx
Expand Up @@ -82,4 +82,28 @@ describe("single fields", () => {
.toJSON();
expect(tree).toMatchSnapshot();
});
});
test("format color", () => {
const schema: JSONSchema7 = {
type: "string",
format: "color",
};
const tree = renderer.create(<Form schema={schema} />).toJSON();
expect(tree).toMatchSnapshot();
});
test("format date", () => {
const schema: JSONSchema7 = {
type: "string",
format: "date",
};
const tree = renderer.create(<Form schema={schema} />).toJSON();
expect(tree).toMatchSnapshot();
});
test("format datetime", () => {
const schema: JSONSchema7 = {
type: "string",
format: "datetime",
};
const tree = renderer.create(<Form schema={schema} />).toJSON();
expect(tree).toMatchSnapshot();
});
});

0 comments on commit 4a05644

Please sign in to comment.