diff --git a/README.md b/README.md index 6b553c8225..4d4571496f 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) i - [Alternative widgets](#alternative-widgets) - [For boolean fields](#for-boolean-fields) - [For string fields](#for-string-fields) + - [String formats](#string-formats) - [For number and integer fields](#for-number-and-integer-fields) - [Hidden widgets](#hidden-widgets) - [Object fields ordering](#object-fields-ordering) @@ -215,13 +216,22 @@ Here's a list of supported alternative widgets for different JSONSchema data typ * `password`: an `input[type=password]` element is used; * by default, a regular `input[type=text]` element is used. -The built-in string field also supports the JSONSchema `format` property, and will render an appropriate widget by default for the following formats: +##### String formats + +The built-in string field also supports the JSONSchema `format` property, and will render an appropriate widget by default for the following string formats: -- `date-time`: Six `select` elements are used to select the year, the month, the day, the hour, the minute and the second; - * If you don't want to deal with time, a `"ui:widget": "date"` uiSchema widget is alternatively available, exposing three selects for year, month and day only; - `email`: An `input[type=email]` element is used; - `uri`: An `input[type=url]` element is used; -- More formats could be supported in a near future, feel free to help us going faster! +- `date-time`: By default, an `input[type=datetime-local]` element is used; if you solely want to rely on a date, a `date` uiSchema alternative widget is available: + +![](http://i.imgur.com/xqu6Lcp.png) + +Please note that while standardized, `datetime-local` and `date` input elements are not yet supported by Firefox and IE. If you plan on targetting these platforms, two alternative widgets are available: + +- `alt-datetime`: Six `select` elements are used to select the year, the month, the day, the hour, the minute and the second; +- `alt-date`: Three `select` elements are used to select the year, month and the day. + +![](http://i.imgur.com/VF5tY60.png) #### For `number` and `integer` fields diff --git a/playground/samples/date.js b/playground/samples/date.js new file mode 100644 index 0000000000..0ecf66a9ba --- /dev/null +++ b/playground/samples/date.js @@ -0,0 +1,54 @@ +module.exports = { + schema: { + title: "Date and time widgets", + type: "object", + properties: { + native: { + title: "Native", + description: "May not work on some browsers, notably Firefox Desktop and IE.", + type: "object", + properties: { + "datetime": { + type: "string", + format: "date-time" + }, + "date": { + type: "string", + format: "date-time" + } + } + }, + alternative: { + title: "Alternative", + description: "These work on every platform.", + type: "object", + properties: { + "alt-datetime": { + type: "string", + format: "date-time" + }, + "alt-date": { + type: "string", + format: "date-time" + } + } + } + } + }, + uiSchema: { + native: { + date: { + "ui:widget": "date" + } + }, + alternative: { + "alt-datetime": { + "ui:widget": "alt-datetime" + }, + "alt-date": { + "ui:widget": "alt-date" + } + } + }, + formData: {} +}; diff --git a/playground/samples/index.js b/playground/samples/index.js index a05e5b5e13..b6d642095d 100644 --- a/playground/samples/index.js +++ b/playground/samples/index.js @@ -8,6 +8,7 @@ import references from "./references"; import custom from "./custom"; import errors from "./errors"; import large from "./large"; +import date from "./date"; export const samples = { Simple: simple, @@ -20,4 +21,5 @@ export const samples = { Custom: custom, Errors: errors, Large: large, + "Date & time": date, }; diff --git a/playground/samples/simple.js b/playground/samples/simple.js index 7f6665066a..7d93af7e60 100644 --- a/playground/samples/simple.js +++ b/playground/samples/simple.js @@ -24,11 +24,6 @@ module.exports = { type: "string", title: "Password", minLength: 3 - }, - date: { - type: "string", - format: "date-time", - title: "Subscription date" } } }, @@ -42,6 +37,9 @@ module.exports = { password: { "ui:widget": "password", "ui:help": "Hint: Make it strong!" + }, + date: { + "ui:widget": "alt-datetime" } }, formData: { @@ -49,7 +47,6 @@ module.exports = { lastName: "Norris", age: 75, bio: "Roundhouse kicking asses since 1940", - password: "noneed", - date: new Date().toJSON() + password: "noneed" } }; diff --git a/playground/samples/widgets.js b/playground/samples/widgets.js index 759fa01356..80c2971b0d 100644 --- a/playground/samples/widgets.js +++ b/playground/samples/widgets.js @@ -14,14 +14,6 @@ module.exports = { uri: { type: "string", format: "uri" - }, - datetime: { - type: "string", - format: "date-time" - }, - date: { - type: "string", - format: "date-time" } } }, @@ -77,11 +69,6 @@ module.exports = { "ui:widget": "textarea" } }, - stringFormats: { - date: { - "ui:widget": "date" - } - }, secret: { "ui:widget": "hidden" } diff --git a/src/components/widgets/AltDateTimeWidget.js b/src/components/widgets/AltDateTimeWidget.js new file mode 100644 index 0000000000..cf36536e4d --- /dev/null +++ b/src/components/widgets/AltDateTimeWidget.js @@ -0,0 +1,21 @@ +import React, { PropTypes } from "react"; + +import AltDateWidget from "./AltDateWidget"; + + +function AltDateTimeWidget(props) { + return ; +} + +if (process.env.NODE_ENV !== "production") { + AltDateTimeWidget.propTypes = { + schema: PropTypes.object.isRequired, + id: PropTypes.string.isRequired, + placeholder: PropTypes.string, + value: React.PropTypes.string, + required: PropTypes.bool, + onChange: PropTypes.func, + }; +} + +export default AltDateTimeWidget; diff --git a/src/components/widgets/AltDateWidget.js b/src/components/widgets/AltDateWidget.js new file mode 100644 index 0000000000..e388720d75 --- /dev/null +++ b/src/components/widgets/AltDateWidget.js @@ -0,0 +1,125 @@ +import React, { Component, PropTypes } from "react"; + +import { shouldRender, parseDateString, toDateString, pad } from "../../utils"; +import SelectWidget from "../widgets/SelectWidget"; + + +function rangeOptions(type, start, stop) { + let options = [{value: -1, label: type}]; + for (let i=start; i<= stop; i++) { + options.push({value: i, label: pad(i, 2)}); + } + return options; +} + +function valid(state) { + return Object.keys(state).every(key => state[key] !== -1); +} + +function DateElement({type, range, value, select, rootId}) { + const id = rootId + "_" + type; + return ( + select(type, value)} /> + ); +} + +class AltDateWidget extends Component { + static defaultProps = { + time: false + }; + + constructor(props) { + super(props); + this.state = parseDateString(props.value, props.time); + } + + componentWillReceiveProps(nextProps) { + this.setState(parseDateString(nextProps.value, nextProps.time)); + } + + shouldComponentUpdate(nextProps, nextState) { + return shouldRender(this, nextProps, nextState); + } + + onChange = (property, value) => { + this.setState({[property]: value}, () => { + // Only propagate to parent state if we have a complete date{time} + if (valid(this.state)) { + this.props.onChange(toDateString(this.state)); + } + }); + }; + + setNow = (event) => { + event.preventDefault(); + const {time, onChange} = this.props; + const nowDateObj = parseDateString(new Date().toJSON(), time); + this.setState(nowDateObj, () => onChange(toDateString(this.state))); + }; + + clear = (event) => { + event.preventDefault(); + const {time, onChange} = this.props; + this.setState(parseDateString("", time), () => onChange(undefined)); + }; + + get dateElementProps() { + const {time} = this.props; + const {year, month, day, hour, minute, second} = this.state; + const data = [ + {type: "year", range: [1900, 2020], value: year}, + {type: "month", range: [1, 12], value: month}, + {type: "day", range: [1, 31], value: day}, + ]; + if (time) { + data.push( + {type: "hour", range: [0, 23], value: hour}, + {type: "minute", range: [0, 59], value: minute}, + {type: "second", range: [0, 59], value: second} + ); + } + return data; + } + + render() { + const {id} = this.props; + return ( + + ); + } +} + +if (process.env.NODE_ENV !== "production") { + AltDateWidget.propTypes = { + schema: PropTypes.object.isRequired, + id: PropTypes.string.isRequired, + placeholder: PropTypes.string, + value: React.PropTypes.string, + required: PropTypes.bool, + onChange: PropTypes.func, + time: PropTypes.bool, + }; +} + +export default AltDateWidget; diff --git a/src/components/widgets/DateTimeWidget.js b/src/components/widgets/DateTimeWidget.js index 47c351b9a4..f3a6f6e6c4 100644 --- a/src/components/widgets/DateTimeWidget.js +++ b/src/components/widgets/DateTimeWidget.js @@ -1,10 +1,31 @@ import React, { PropTypes } from "react"; -import DateWidget from "./DateWidget"; +function fromJSONDate(jsonDate) { + return jsonDate ? jsonDate.slice(0, 19) : ""; +} + +function toJSONDate(dateString) { + if (dateString) { + return new Date(dateString).toJSON(); + } +} -function DateTimeWidget(props) { - return ; +function DateTimeWidget({ + schema, + id, + value, + required, + onChange +}) { + return ( + onChange(toJSONDate(event.target.value))} /> + ); } if (process.env.NODE_ENV !== "production") { @@ -12,7 +33,7 @@ if (process.env.NODE_ENV !== "production") { schema: PropTypes.object.isRequired, id: PropTypes.string.isRequired, placeholder: PropTypes.string, - value: React.PropTypes.string, + value: PropTypes.string, required: PropTypes.bool, onChange: PropTypes.func, }; diff --git a/src/components/widgets/DateWidget.js b/src/components/widgets/DateWidget.js index 16cd164b6b..5375348cbd 100644 --- a/src/components/widgets/DateWidget.js +++ b/src/components/widgets/DateWidget.js @@ -1,124 +1,41 @@ -import React, { Component, PropTypes } from "react"; +import React, { PropTypes } from "react"; -import { shouldRender, parseDateString, toDateString, pad } from "../../utils"; -import SelectWidget from "../widgets/SelectWidget"; - -function rangeOptions(type, start, stop) { - let options = [{value: -1, label: type}]; - for (let i=start; i<= stop; i++) { - options.push({value: i, label: pad(i, 2)}); - } - return options; +function fromJSONDate(jsonDate) { + return jsonDate ? jsonDate.slice(0, 10) : ""; } -function valid(state) { - return Object.keys(state).every(key => state[key] !== -1); +function toJSONDate(dateString) { + if (dateString) { + return new Date(dateString).toJSON(); + } } -function DateElement({type, range, value, select, rootId}) { - const id = rootId + "_" + type; +function DateWidget({ + schema, + id, + value, + required, + onChange +}) { return ( - select(type, value)} /> + value={fromJSONDate(value)} + required={required} + onChange={(event) => onChange(toJSONDate(event.target.value))} /> ); } -class DateWidget extends Component { - static defaultProps = { - time: false - }; - - constructor(props) { - super(props); - this.state = parseDateString(props.value, props.time); - } - - componentWillReceiveProps(nextProps) { - this.setState(parseDateString(nextProps.value, nextProps.time)); - } - - shouldComponentUpdate(nextProps, nextState) { - return shouldRender(this, nextProps, nextState); - } - - onChange = (property, value) => { - this.setState({[property]: value}, () => { - // Only propagate to parent state if we have a complete date{time} - if (valid(this.state)) { - this.props.onChange(toDateString(this.state)); - } - }); - }; - - setNow = (event) => { - event.preventDefault(); - const {time, onChange} = this.props; - const nowDateObj = parseDateString(new Date().toJSON(), time); - this.setState(nowDateObj, () => onChange(toDateString(this.state))); - }; - - clear = (event) => { - event.preventDefault(); - const {time, onChange} = this.props; - this.setState(parseDateString("", time), () => onChange(undefined)); - }; - - get dateElementProps() { - const {time} = this.props; - const {year, month, day, hour, minute, second} = this.state; - const data = [ - {type: "year", range: [1900, 2020], value: year}, - {type: "month", range: [1, 12], value: month}, - {type: "day", range: [1, 31], value: day}, - ]; - if (time) { - data.push( - {type: "hour", range: [0, 23], value: hour}, - {type: "minute", range: [0, 59], value: minute}, - {type: "second", range: [0, 59], value: second} - ); - } - return data; - } - - render() { - const {id} = this.props; - return ( -
    { - this.dateElementProps.map((props, i) => ( -
  • - -
  • - )) - } -
  • - Now -
  • -
  • - Clear -
  • -
- ); - } -} - if (process.env.NODE_ENV !== "production") { DateWidget.propTypes = { schema: PropTypes.object.isRequired, id: PropTypes.string.isRequired, placeholder: PropTypes.string, - value: React.PropTypes.string, + value: PropTypes.string, required: PropTypes.bool, onChange: PropTypes.func, - time: PropTypes.bool, }; } diff --git a/src/utils.js b/src/utils.js index 4ca5299d46..bef3d00594 100644 --- a/src/utils.js +++ b/src/utils.js @@ -9,6 +9,8 @@ import SelectWidget from "./components/widgets/SelectWidget"; import TextWidget from "./components/widgets/TextWidget"; import DateWidget from "./components/widgets/DateWidget"; import DateTimeWidget from "./components/widgets/DateTimeWidget"; +import AltDateWidget from "./components/widgets/AltDateWidget"; +import AltDateTimeWidget from "./components/widgets/AltDateTimeWidget"; import EmailWidget from "./components/widgets/EmailWidget"; import URLWidget from "./components/widgets/URLWidget"; import TextareaWidget from "./components/widgets/TextareaWidget"; @@ -29,6 +31,9 @@ const altWidgetMap = { textarea: TextareaWidget, hidden: HiddenWidget, date: DateWidget, + datetime: DateTimeWidget, + "alt-date": AltDateWidget, + "alt-datetime": AltDateTimeWidget, }, number: { updown: UpDownWidget, diff --git a/test/StringField_test.js b/test/StringField_test.js index 1359934349..41b7c3c200 100644 --- a/test/StringField_test.js +++ b/test/StringField_test.js @@ -176,12 +176,160 @@ describe("StringField", () => { }); describe("DateTimeWidget", () => { - it("should render a datetime field", () => { + it("should render an datetime-local field", () => { + const {node} = createFormComponent({schema: { + type: "string", + format: "date-time", + }}); + + expect(node.querySelectorAll(".field [type=datetime-local]")) + .to.have.length.of(1); + }); + + it("should assign a default value", () => { + const datetime = new Date().toJSON(); + const {comp} = createFormComponent({schema: { + type: "string", + format: "date-time", + default: datetime, + }}); + + expect(comp.state.formData).eql(datetime); + }); + + it("should reflect the change into the dom", () => { + const {node} = createFormComponent({schema: { + type: "string", + format: "date-time", + }}); + + const newDatetime = new Date().toJSON(); + return SimulateAsync().change(node.querySelector("[type=datetime-local]"), { + target: {value: newDatetime} + }) + .then(() => { + return expect(node.querySelector("[type=datetime-local]").value) + // XXX import and use conversion helper + .eql(newDatetime.slice(0, 19)); + }); + }); + + it("should fill field with data", () => { + const datetime = new Date().toJSON(); + const {comp} = createFormComponent({schema: { + type: "string", + format: "date-time", + }, formData: datetime}); + + expect(comp.state.formData).eql(datetime); + }); + + it("should render the widget with the expected id", () => { const {node} = createFormComponent({schema: { type: "string", format: "date-time", }}); + expect(node.querySelector("[type=datetime-local]").id) + .eql("root"); + }); + + it("should reject an invalid entered datetime", () => { + const {comp, node} = createFormComponent({schema: { + type: "string", + format: "date-time", + }, liveValidate: true}); + + return SimulateAsync().change(node.querySelector("[type=datetime-local]"), { + target: {value: "invalid"} + }) + .then(() => expect(comp.state.errors).to.have.length.of(1)); + }); + }); + + describe("DateWidget", () => { + const uiSchema = {"ui:widget": "date"}; + + it("should render a date field", () => { + const {node} = createFormComponent({schema: { + type: "string", + format: "date-time", + }, uiSchema}); + + expect(node.querySelectorAll(".field [type=date]")) + .to.have.length.of(1); + }); + + it("should assign a default value", () => { + const datetime = new Date().toJSON(); + const {comp} = createFormComponent({schema: { + type: "string", + format: "date-time", + default: datetime, + }, uiSchema}); + + expect(comp.state.formData).eql(datetime); + }); + + it("should reflect the change into the dom", () => { + const {node} = createFormComponent({schema: { + type: "string", + format: "date-time", + }, uiSchema}); + + const newDatetime = new Date().toJSON(); + return SimulateAsync().change(node.querySelector("[type=date]"), { + target: {value: newDatetime} + }) + .then(() => { + return expect(node.querySelector("[type=date]").value) + // XXX import and use conversion helper + .eql(newDatetime.slice(0, 10)); + }); + }); + + it("should fill field with data", () => { + const datetime = new Date().toJSON(); + const {comp} = createFormComponent({schema: { + type: "string", + format: "date-time", + }, formData: datetime}); + + expect(comp.state.formData).eql(datetime); + }); + + it("should render the widget with the expected id", () => { + const {node} = createFormComponent({schema: { + type: "string", + format: "date-time", + }, uiSchema}); + + expect(node.querySelector("[type=date]").id) + .eql("root"); + }); + + it("should reject an invalid entered datetime", () => { + const {comp, node} = createFormComponent({schema: { + type: "string", + format: "date-time", + }, uiSchema, liveValidate: true}); + + return SimulateAsync().change(node.querySelector("[type=date]"), { + target: {value: "invalid"} + }) + .then(() => expect(comp.state.errors).to.have.length.of(1)); + }); + }); + + describe("AltDateTimeWidget", () => { + const uiSchema = {"ui:widget": "alt-datetime"}; + + it("should render a datetime field", () => { + const {node} = createFormComponent({schema: { + type: "string", + format: "date-time", + }, uiSchema}); + expect(node.querySelectorAll(".field select")) .to.have.length.of(6); }); @@ -191,7 +339,7 @@ describe("StringField", () => { type: "string", format: "date-time", title: "foo", - }}); + }, uiSchema}); expect(node.querySelector(".field label").textContent) .eql("foo"); @@ -203,7 +351,7 @@ describe("StringField", () => { type: "string", format: "date-time", default: datetime, - }}); + }, uiSchema}); expect(comp.state.formData).eql(datetime); }); @@ -212,15 +360,21 @@ describe("StringField", () => { const {comp, node} = createFormComponent({schema: { type: "string", format: "date-time", - }}); + }, uiSchema}); return Promise.all([ - SimulateAsync().change(node.querySelector("#root_year"), {target: {value: 2012}}), - SimulateAsync().change(node.querySelector("#root_month"), {target: {value: 10}}), - SimulateAsync().change(node.querySelector("#root_day"), {target: {value: 2}}), - SimulateAsync().change(node.querySelector("#root_hour"), {target: {value: 1}}), - SimulateAsync().change(node.querySelector("#root_minute"), {target: {value: 2}}), - SimulateAsync().change(node.querySelector("#root_second"), {target: {value: 3}}), + SimulateAsync().change( + node.querySelector("#root_year"), {target: {value: 2012}}), + SimulateAsync().change( + node.querySelector("#root_month"), {target: {value: 10}}), + SimulateAsync().change( + node.querySelector("#root_day"), {target: {value: 2}}), + SimulateAsync().change( + node.querySelector("#root_hour"), {target: {value: 1}}), + SimulateAsync().change( + node.querySelector("#root_minute"), {target: {value: 2}}), + SimulateAsync().change( + node.querySelector("#root_second"), {target: {value: 3}}), ]) .then(() => { expect(comp.state.formData).eql("2012-10-02T01:02:03.000Z"); @@ -241,7 +395,7 @@ describe("StringField", () => { const {node} = createFormComponent({schema: { type: "string", format: "date-time", - }}); + }, uiSchema}); const ids = [].map.call(node.querySelectorAll("select"), node => node.id); @@ -259,9 +413,10 @@ describe("StringField", () => { const {node} = createFormComponent({schema: { type: "string", format: "date-time", - }}); + }, uiSchema}); - const lengths = [].map.call(node.querySelectorAll("select"), node => node.length); + const lengths = [].map.call(node.querySelectorAll("select"), + node => node.length); expect(lengths).eql([ 121 + 1, // from 1900 to 2020 + undefined @@ -272,29 +427,31 @@ describe("StringField", () => { 60 + 1 ]); const monthOptions = node.querySelectorAll("select#root_month option"); - const monthOptionsValues = [].map.call(monthOptions, option => option.value); + const monthOptionsValues = [].map.call(monthOptions, o => o.value); expect(monthOptionsValues).eql([ - "-1", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"]); + "-1", "1", "2", "3", "4", "5", "6", + "7", "8", "9", "10", "11", "12"]); }); it("should render the widgets with the expected options' labels", () => { const {node} = createFormComponent({schema: { type: "string", format: "date-time", - }}); + }, uiSchema}); const monthOptions = node.querySelectorAll("select#root_month option"); - const monthOptionsLabels = [].map.call(monthOptions, option => option.text); + const monthOptionsLabels = [].map.call(monthOptions, o => o.text); expect(monthOptionsLabels).eql([ - "month", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"]); + "month", "01", "02", "03", "04", "05", "06", + "07", "08", "09", "10", "11", "12"]); }); describe("Action buttons", () => { - it("should render a action buttons", () => { + it("should render action buttons", () => { const {node} = createFormComponent({schema: { type: "string", format: "date-time", - }}); + }, uiSchema}); const buttonLabels = [].map.call(node.querySelectorAll("a.btn"), x => x.textContent); @@ -305,7 +462,7 @@ describe("StringField", () => { const {comp, node} = createFormComponent({schema: { type: "string", format: "date-time", - }}); + }, uiSchema}); return SimulateAsync().click(node.querySelector("a.btn-now")) .then(() => { @@ -318,7 +475,7 @@ describe("StringField", () => { const {comp, node} = createFormComponent({schema: { type: "string", format: "date-time", - }}); + }, uiSchema}); return SimulateAsync().click(node.querySelector("a.btn-now")) .then(() => SimulateAsync().click(node.querySelector("a.btn-clear"))) @@ -327,8 +484,8 @@ describe("StringField", () => { }); }); - describe("DateWidget", () => { - const uiSchema = {"ui:widget": "date"}; + describe("AltDateWidget", () => { + const uiSchema = {"ui:widget": "alt-date"}; it("should render a date field", () => { const {node} = createFormComponent({schema: { @@ -369,9 +526,12 @@ describe("StringField", () => { }, uiSchema}); return Promise.all([ - SimulateAsync().change(node.querySelector("#root_year"), {target: {value: 2012}}), - SimulateAsync().change(node.querySelector("#root_month"), {target: {value: 10}}), - SimulateAsync().change(node.querySelector("#root_day"), {target: {value: 2}}), + SimulateAsync().change( + node.querySelector("#root_year"), {target: {value: 2012}}), + SimulateAsync().change( + node.querySelector("#root_month"), {target: {value: 10}}), + SimulateAsync().change( + node.querySelector("#root_day"), {target: {value: 2}}), ]) .then(() => { expect(comp.state.formData).eql("2012-10-02T00:00:00.000Z"); @@ -409,7 +569,8 @@ describe("StringField", () => { format: "date-time", }, uiSchema}); - const lengths = [].map.call(node.querySelectorAll("select"), node => node.length); + const lengths = [].map.call(node.querySelectorAll("select"), + node => node.length); expect(lengths).eql([ 121 + 1, // from 1900 to 2020 + undefined @@ -417,7 +578,7 @@ describe("StringField", () => { 31 + 1, ]); const monthOptions = node.querySelectorAll("select#root_month option"); - const monthOptionsValues = [].map.call(monthOptions, option => option.value); + const monthOptionsValues = [].map.call(monthOptions, o => o.value); expect(monthOptionsValues).eql([ "-1", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"]); }); @@ -429,13 +590,14 @@ describe("StringField", () => { }, uiSchema}); const monthOptions = node.querySelectorAll("select#root_month option"); - const monthOptionsLabels = [].map.call(monthOptions, option => option.text); + const monthOptionsLabels = [].map.call(monthOptions, o => o.text); expect(monthOptionsLabels).eql([ - "month", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"]); + "month", "01", "02", "03", "04", "05", "06", + "07", "08", "09", "10", "11", "12"]); }); describe("Action buttons", () => { - it("should render a action buttons", () => { + it("should render action buttons", () => { const {node} = createFormComponent({schema: { type: "string", format: "date-time", @@ -526,7 +688,10 @@ describe("StringField", () => { return SimulateAsync().change(node.querySelector("[type=email]"), { target: {value: newDatetime} }) - .then(() => expect(node.querySelector("[type=email]").value).eql(newDatetime)); + .then(() => { + return expect(node.querySelector("[type=email]").value) + .eql(newDatetime); + }); }); it("should fill field with data", () => { @@ -639,7 +804,7 @@ describe("StringField", () => { .eql("root"); }); - it("should reject an invalid entered email", () => { + it("should reject an invalid entered url", () => { const {comp, node} = createFormComponent({schema: { type: "string", format: "uri",