Repository: mozilla-services/react-jsonschema-form
Pull request #657 (2017-08-04 07:33:05)

Onfocus support
### Reasons for making this change

I'm currently developing a JSON Based Configuration editor, where I'll have a split screen where the Form and the raw JSON will be displayed side-by-side. One of the requirements is to highlight the correspondent field on the raw JSON split as soon a field is focused on the form. Currently only onBlur event is supported, so, I've added onFocus support.

### Checklist

* [ ] **I'm updating documentation**
  - [ ] I've checked the rendering of the Markdown text I've added
  - [ ] If I'm adding a new section, I've updated the Table of Content
* [ ] **I'm adding or updating code**
  - [X] I've added and/or updated tests
  - [X] I've updated docs if needed
  - [X] I've run `npm run cs-format` on my branch to conform my code to [prettier](https://github.com/prettier/prettier) coding style
* [X] **I'm adding a new feature**
  - [X] I've updated the playground with an example use of the feature



Patch:
diff --git a/README.md b/README.md
index 2b361fc3e..f472af4ec 100644
--- a/README.md
+++ b/README.md
@@ -213,6 +213,10 @@ If you plan on being notified everytime the form data are updated, you can pass
 
 Sometimes you may want to trigger events or modify external state when a field has been touched, so you can pass an `onBlur` handler, which will receive the id of the input that was blurred and the field value.
 
+#### Form field focus events
+
+Sometimes you may want to trigger events or modify external state when a field has been focused, so you can pass an `onFocus` handler, which will receive the id of the input that is focused and the field value.
+
 ## Form customization
 
 ### The `uiSchema` object
@@ -850,6 +854,7 @@ The following props are passed to each `ArrayFieldTemplate`:
 - `uiSchema`: The uiSchema object for this array field.
 - `title`: A string value containing the title for the array.
 - `formContext`: The `formContext` object that you passed to Form.
+- `formData`: The formData for this array.
 
 The following props are part of each element in `items`:
 
@@ -896,6 +901,13 @@ render((
 
 > Note: Your custom `ErrorList` template will only render when `showErrorList` is `true`.
 
+The following props are passed to `ErrorList`
+
+- `errors`: An array of the errors.
+- `errorSchema`: The errorSchema constructed by `Form`.
+- `schema`: The schema that was passed to `Form`.
+- `uiSchema`: The uiSchema that was passed to `Form`.
+- `formContext`: The `formContext` object that you passed to Form.
 
 ### Custom widgets and fields
 
@@ -946,6 +958,7 @@ The following props are passed to custom widget components:
 - `readonly`: `true` if the widget is read-only;
 - `onChange`: The value change event handler; call it with the new value everytime it changes;
 - `onBlur`: The input blur event handler; call it with the the widget id and value;
+- `onFocus`: The input focus event handler; call it with the the widget id and value;
 - `options`: A map of options passed as a prop to the component (see [Custom widget options](#custom-widget-options)).
 - `formContext`: The `formContext` object that you passed to Form.
 
diff --git a/playground/app.js b/playground/app.js
index 4757b35fb..ba83885ef 100644
--- a/playground/app.js
+++ b/playground/app.js
@@ -477,6 +477,8 @@ class App extends Component {
               validate={validate}
               onBlur={(id, value) =>
                 console.log(`Touched ${id} with value ${value}`)}
+              onFocus={(id, value) =>
+                console.log(`Focused ${id} with value ${value}`)}
               transformErrors={transformErrors}
               onError={log("errors")}>
               <div className="row">
diff --git a/playground/samples/arrays.js b/playground/samples/arrays.js
index c4142a3eb..e11313189 100644
--- a/playground/samples/arrays.js
+++ b/playground/samples/arrays.js
@@ -57,6 +57,16 @@ module.exports = {
           $ref: "#/definitions/Thing",
         },
       },
+      defaultsAndMinItems: {
+        type: "array",
+        title: "List and item level defaults",
+        minItems: 5,
+        default: ["carp", "trout", "bream"],
+        items: {
+          type: "string",
+          default: "unidentified",
+        },
+      },
       nestedList: {
         type: "array",
         title: "Nested list",
diff --git a/src/components/Form.js b/src/components/Form.js
index 1455d4f21..2c09819ce 100644
--- a/src/components/Form.js
+++ b/src/components/Form.js
@@ -77,11 +77,19 @@ export default class Form extends Component {
   }
 
   renderErrors() {
-    const { status, errors } = this.state;
-    const { ErrorList, showErrorList } = this.props;
+    const { status, errors, errorSchema, schema, uiSchema } = this.state;
+    const { ErrorList, showErrorList, formContext } = this.props;
 
     if (status !== "editing" && errors.length && showErrorList != false) {
-      return <ErrorList errors={errors} />;
+      return (
+        <ErrorList
+          errors={errors}
+          errorSchema={errorSchema}
+          schema={schema}
+          uiSchema={uiSchema}
+          formContext={formContext}
+        />
+      );
     }
     return null;
   }
@@ -107,6 +115,12 @@ export default class Form extends Component {
     }
   };
 
+  onFocus = (...args) => {
+    if (this.props.onFocus) {
+      this.props.onFocus(...args);
+    }
+  };
+
   onSubmit = event => {
     event.preventDefault();
     this.setState({ status: "submitted" });
@@ -187,6 +201,7 @@ export default class Form extends Component {
           formData={formData}
           onChange={this.onChange}
           onBlur={this.onBlur}
+          onFocus={this.onFocus}
           registry={registry}
           safeRenderCompletion={safeRenderCompletion}
         />
diff --git a/src/components/fields/ArrayField.js b/src/components/fields/ArrayField.js
index 4aa51bf86..b6bb28473 100644
--- a/src/components/fields/ArrayField.js
+++ b/src/components/fields/ArrayField.js
@@ -304,6 +304,7 @@ class ArrayField extends Component {
       registry = getDefaultRegistry(),
       formContext,
       onBlur,
+      onFocus,
     } = this.props;
     const title = schema.title === undefined ? name : schema.title;
     const { ArrayFieldTemplate, definitions, fields } = registry;
@@ -326,6 +327,7 @@ class ArrayField extends Component {
           itemUiSchema: uiSchema.items,
           autofocus: autofocus && index === 0,
           onBlur,
+          onFocus,
         });
       }),
       className: `field field-array field-array-of-${itemsSchema.type}`,
@@ -340,6 +342,7 @@ class ArrayField extends Component {
       title,
       TitleField,
       formContext,
+      formData,
     };
 
     // Check if a custom render function was passed in
@@ -356,6 +359,7 @@ class ArrayField extends Component {
       readonly,
       autofocus,
       onBlur,
+      onFocus,
       registry = getDefaultRegistry(),
     } = this.props;
     const items = this.props.formData;
@@ -373,6 +377,7 @@ class ArrayField extends Component {
         multiple
         onChange={this.onSelectChange}
         onBlur={onBlur}
+        onFocus={onFocus}
         options={options}
         schema={schema}
         value={items}
@@ -394,6 +399,7 @@ class ArrayField extends Component {
       readonly,
       autofocus,
       onBlur,
+      onFocus,
       registry = getDefaultRegistry(),
     } = this.props;
     const title = schema.title || name;
@@ -408,6 +414,7 @@ class ArrayField extends Component {
         multiple
         onChange={this.onSelectChange}
         onBlur={onBlur}
+        onFocus={onFocus}
         schema={schema}
         title={title}
         value={items}
@@ -432,6 +439,7 @@ class ArrayField extends Component {
       autofocus,
       registry = getDefaultRegistry(),
       onBlur,
+      onFocus,
     } = this.props;
     const title = schema.title || name;
     let items = this.props.formData;
@@ -480,6 +488,7 @@ class ArrayField extends Component {
           itemErrorSchema,
           autofocus: autofocus && index === 0,
           onBlur,
+          onFocus,
         });
       }),
       onAddClick: this.onAddClick,
@@ -509,6 +518,7 @@ class ArrayField extends Component {
       itemErrorSchema,
       autofocus,
       onBlur,
+      onFocus,
     } = props;
     const {
       disabled,
@@ -540,6 +550,7 @@ class ArrayField extends Component {
           required={this.isItemRequired(itemSchema)}
           onChange={this.onChangeForIndex(index)}
           onBlur={onBlur}
+          onFocus={onFocus}
           registry={this.props.registry}
           disabled={this.props.disabled}
           readonly={this.props.readonly}
@@ -591,6 +602,7 @@ if (process.env.NODE_ENV !== "production") {
     errorSchema: PropTypes.object,
     onChange: PropTypes.func.isRequired,
     onBlur: PropTypes.func,
+    onFocus: PropTypes.func,
     formData: PropTypes.array,
     required: PropTypes.bool,
     disabled: PropTypes.bool,
diff --git a/src/components/fields/ObjectField.js b/src/components/fields/ObjectField.js
index 9f1efee25..9a7d3ac8c 100644
--- a/src/components/fields/ObjectField.js
+++ b/src/components/fields/ObjectField.js
@@ -43,6 +43,7 @@ class ObjectField extends Component {
       disabled,
       readonly,
       onBlur,
+      onFocus,
       registry = getDefaultRegistry(),
     } = this.props;
     const { definitions, fields, formContext } = registry;
@@ -94,6 +95,7 @@ class ObjectField extends Component {
               formData={formData[name]}
               onChange={this.onPropertyChange(name)}
               onBlur={onBlur}
+              onFocus={onFocus}
               registry={registry}
               disabled={disabled}
               readonly={readonly}
diff --git a/src/components/fields/StringField.js b/src/components/fields/StringField.js
index 9e9ed6160..9ecee6eb9 100644
--- a/src/components/fields/StringField.js
+++ b/src/components/fields/StringField.js
@@ -22,6 +22,7 @@ function StringField(props) {
     autofocus,
     onChange,
     onBlur,
+    onFocus,
     registry = getDefaultRegistry(),
   } = props;
   const { title, format } = schema;
@@ -42,6 +43,7 @@ function StringField(props) {
       value={formData}
       onChange={onChange}
       onBlur={onBlur}
+      onFocus={onFocus}
       required={required}
       disabled={disabled}
       readonly={readonly}
@@ -60,6 +62,7 @@ if (process.env.NODE_ENV !== "production") {
     idSchema: PropTypes.object,
     onChange: PropTypes.func.isRequired,
     onBlur: PropTypes.func,
+    onFocus: PropTypes.func,
     formData: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
     registry: PropTypes.shape({
       widgets: PropTypes.objectOf(
diff --git a/src/components/widgets/BaseInput.js b/src/components/widgets/BaseInput.js
index 58f06d30f..108b250d1 100644
--- a/src/components/widgets/BaseInput.js
+++ b/src/components/widgets/BaseInput.js
@@ -10,6 +10,7 @@ function BaseInput(props) {
     disabled,
     autofocus,
     onBlur,
+    onFocus,
     options,
     schema,
     formContext,
@@ -31,6 +32,7 @@ function BaseInput(props) {
       {...inputProps}
       onChange={_onChange}
       onBlur={onBlur && (event => onBlur(inputProps.id, event.target.value))}
+      onFocus={onFocus && (event => onFocus(inputProps.id, event.target.value))}
     />
   );
 }
@@ -54,6 +56,7 @@ if (process.env.NODE_ENV !== "production") {
     autofocus: PropTypes.bool,
     onChange: PropTypes.func,
     onBlur: PropTypes.func,
+    onFocus: PropTypes.func,
   };
 }
 
diff --git a/src/components/widgets/SelectWidget.js b/src/components/widgets/SelectWidget.js
index df3328b38..867641355 100644
--- a/src/components/widgets/SelectWidget.js
+++ b/src/components/widgets/SelectWidget.js
@@ -48,6 +48,7 @@ function SelectWidget(props) {
     autofocus,
     onChange,
     onBlur,
+    onFocus,
     placeholder,
   } = props;
   const { enumOptions } = options;
@@ -68,6 +69,13 @@ function SelectWidget(props) {
           onBlur(id, processValue(schema, newValue));
         })
       }
+      onFocus={
+        onFocus &&
+        (event => {
+          const newValue = getValue(event, multiple);
+          onFocus(id, processValue(schema, newValue));
+        })
+      }
       onChange={event => {
         const newValue = getValue(event, multiple);
         onChange(processValue(schema, newValue));
@@ -107,6 +115,7 @@ if (process.env.NODE_ENV !== "production") {
     autofocus: PropTypes.bool,
     onChange: PropTypes.func,
     onBlur: PropTypes.func,
+    onFocus: PropTypes.func,
   };
 }
 
diff --git a/src/components/widgets/TextareaWidget.js b/src/components/widgets/TextareaWidget.js
index c2250d20d..85979a418 100644
--- a/src/components/widgets/TextareaWidget.js
+++ b/src/components/widgets/TextareaWidget.js
@@ -13,6 +13,7 @@ function TextareaWidget(props) {
     autofocus,
     onChange,
     onBlur,
+    onFocus,
   } = props;
   const _onChange = ({ target: { value } }) => {
     return onChange(value === "" ? options.emptyValue : value);
@@ -29,6 +30,7 @@ function TextareaWidget(props) {
       autoFocus={autofocus}
       rows={options.rows}
       onBlur={onBlur && (event => onBlur(id, event.target.value))}
+      onFocus={onFocus && (event => onFocus(id, event.target.value))}
       onChange={_onChange}
     />
   );
@@ -54,6 +56,7 @@ if (process.env.NODE_ENV !== "production") {
     autofocus: PropTypes.bool,
     onChange: PropTypes.func,
     onBlur: PropTypes.func,
+    onFocus: PropTypes.func,
   };
 }
 
diff --git a/src/utils.js b/src/utils.js
index a14299d57..0c24acf71 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -140,9 +140,23 @@ function computeDefaults(schema, parentDefaults, definitions = {}) {
 
     case "array":
       if (schema.minItems) {
-        return new Array(schema.minItems).fill(
-          computeDefaults(schema.items, defaults, definitions)
-        );
+        if (!isMultiSelect(schema, definitions)) {
+          const defaultsLength = defaults ? defaults.length : 0;
+          if (schema.minItems > defaultsLength) {
+            const defaultEntries = defaults || [];
+            // populate the array with the defaults
+            const fillerEntries = new Array(
+              schema.minItems - defaultsLength
+            ).fill(
+              computeDefaults(schema.items, schema.items.defaults, definitions)
+            );
+            // then fill up the rest with either the item default or empty, up to minItems
+
+            return defaultEntries.concat(fillerEntries);
+          }
+        } else {
+          return [];
+        }
       }
   }
   return defaults;


Test patch:
diff --git a/test/ArrayField_test.js b/test/ArrayField_test.js
index b37c914a6..73166cd14 100644
--- a/test/ArrayField_test.js
+++ b/test/ArrayField_test.js
@@ -410,6 +410,110 @@ describe("ArrayField", () => {
       expect(inputs[1].value).eql("Default name");
     });
 
+    it("should render an input for each default value, even when this is greater than minItems", () => {
+      const schema = {
+        type: "object",
+        properties: {
+          turtles: {
+            type: "array",
+            minItems: 2,
+            default: ["Raphael", "Michaelangelo", "Donatello", "Leonardo"],
+            items: {
+              type: "string",
+            },
+          },
+        },
+      };
+      const { node } = createFormComponent({ schema: schema });
+      const inputs = node.querySelectorAll("input[type=text]");
+      expect(inputs.length).to.eql(4);
+      expect(inputs[0].value).to.eql("Raphael");
+      expect(inputs[1].value).to.eql("Michaelangelo");
+      expect(inputs[2].value).to.eql("Donatello");
+      expect(inputs[3].value).to.eql("Leonardo");
+    });
+
+    it("should render enough input to match minItems, populating the first with default values, and the rest empty", () => {
+      const schema = {
+        type: "object",
+        properties: {
+          turtles: {
+            type: "array",
+            minItems: 4,
+            default: ["Raphael", "Michaelangelo"],
+            items: {
+              type: "string",
+            },
+          },
+        },
+      };
+      const { node } = createFormComponent({ schema });
+      const inputs = node.querySelectorAll("input[type=text]");
+      expect(inputs.length).to.eql(4);
+      expect(inputs[0].value).to.eql("Raphael");
+      expect(inputs[1].value).to.eql("Michaelangelo");
+      expect(inputs[2].value).to.eql("");
+      expect(inputs[3].value).to.eql("");
+    });
+
+    it("should render enough input to match minItems, populating the first with default values, and the rest with the item default", () => {
+      const schema = {
+        type: "object",
+        properties: {
+          turtles: {
+            type: "array",
+            minItems: 4,
+            default: ["Raphael", "Michaelangelo"],
+            items: {
+              type: "string",
+              default: "Unknown",
+            },
+          },
+        },
+      };
+      const { node } = createFormComponent({ schema });
+      const inputs = node.querySelectorAll("input[type=text]");
+      expect(inputs.length).to.eql(4);
+      expect(inputs[0].value).to.eql("Raphael");
+      expect(inputs[1].value).to.eql("Michaelangelo");
+      expect(inputs[2].value).to.eql("Unknown");
+      expect(inputs[3].value).to.eql("Unknown");
+    });
+
+    it("should not add minItems extra formData entries when schema item is a multiselect", () => {
+      const schema = {
+        type: "object",
+        properties: {
+          multipleChoicesList: {
+            type: "array",
+            minItems: 3,
+            uniqueItems: true,
+            items: {
+              type: "string",
+              enum: ["Aramis", "Athos", "Porthos", "d'Artagnan"],
+            },
+          },
+        },
+      };
+      const uiSchema = {
+        multipleChoicesList: {
+          "ui:widget": "checkboxes",
+        },
+      };
+      const form = createFormComponent({
+        schema: schema,
+        uiSchema: uiSchema,
+        formData: {},
+        liveValidate: true,
+      }).comp;
+
+      expect(form.state.formData).to.have.property("multipleChoicesList");
+      expect(form.state.formData.multipleChoicesList).to.be.empty;
+      expect(form.state.errors.length).to.equal(1);
+      expect(form.state.errors[0].name).to.equal("minItems");
+      expect(form.state.errors[0].argument).to.equal(3);
+    });
+
     it("should honor given formData, even when it does not meet ths minItems-requirement", () => {
       const complexSchema = {
         type: "object",
@@ -514,6 +618,24 @@ describe("ArrayField", () => {
         expect(onBlur.calledWith(select.id, ["foo", "bar"])).to.be.true;
       });
 
+      it("should handle a focus event", () => {
+        const onFocus = sandbox.spy();
+        const { node } = createFormComponent({ schema, onFocus });
+
+        const select = node.querySelector(".field select");
+        Simulate.focus(select, {
+          target: {
+            options: [
+              { selected: true, value: "foo" },
+              { selected: true, value: "bar" },
+              { selected: false, value: "fuzz" },
+            ],
+          },
+        });
+
+        expect(onFocus.calledWith(select.id, ["foo", "bar"])).to.be.true;
+      });
+
       it("should fill field with data", () => {
         const { node } = createFormComponent({
           schema,
diff --git a/test/Form_test.js b/test/Form_test.js
index e98937246..14c5c5336 100644
--- a/test/Form_test.js
+++ b/test/Form_test.js
@@ -606,6 +606,31 @@ describe("Form", () => {
     });
   });
 
+  describe("Focus handler", () => {
+    it("should call provided focus handler on form input focus event", () => {
+      const schema = {
+        type: "object",
+        properties: {
+          foo: {
+            type: "string",
+          },
+        },
+      };
+      const formData = {
+        foo: "",
+      };
+      const onFocus = sandbox.spy();
+      const { node } = createFormComponent({ schema, formData, onFocus });
+
+      const input = node.querySelector("[type=text]");
+      Simulate.focus(input, {
+        target: { value: "new" },
+      });
+
+      sinon.assert.calledWithMatch(onFocus, input.id, "new");
+    });
+  });
+
   describe("Error handler", () => {
     it("should call provided error handler on validation errors", () => {
       const schema = {
diff --git a/test/NumberField_test.js b/test/NumberField_test.js
index 0695b85cd..b5eee0fe3 100644
--- a/test/NumberField_test.js
+++ b/test/NumberField_test.js
@@ -97,6 +97,24 @@ describe("NumberField", () => {
 
       expect(onBlur.calledWith(input.id, 2));
     });
+
+    it("should handle a focus event", () => {
+      const onFocus = sandbox.spy();
+      const { node } = createFormComponent({
+        schema: {
+          type: "number",
+        },
+        onFocus,
+      });
+
+      const input = node.querySelector("input");
+      Simulate.focus(input, {
+        target: { value: "2" },
+      });
+
+      expect(onFocus.calledWith(input.id, 2));
+    });
+
     it("should fill field with data", () => {
       const { node } = createFormComponent({
         schema: {
diff --git a/test/ObjectField_test.js b/test/ObjectField_test.js
index 4dc3cae02..bce38d914 100644
--- a/test/ObjectField_test.js
+++ b/test/ObjectField_test.js
@@ -161,6 +161,18 @@ describe("ObjectField", () => {
       expect(onBlur.calledWith(input.id, "changed")).to.be.true;
     });
 
+    it("should handle object fields with focus events", () => {
+      const onFocus = sandbox.spy();
+      const { node } = createFormComponent({ schema, onFocus });
+
+      const input = node.querySelector("input[type=text]");
+      Simulate.focus(input, {
+        target: { value: "changed" },
+      });
+
+      expect(onFocus.calledWith(input.id, "changed")).to.be.true;
+    });
+
     it("should render the widget with the expected id", () => {
       const { node } = createFormComponent({ schema });
 
diff --git a/test/StringField_test.js b/test/StringField_test.js
index 4c8281062..1446f89e8 100644
--- a/test/StringField_test.js
+++ b/test/StringField_test.js
@@ -100,6 +100,22 @@ describe("StringField", () => {
       expect(onBlur.calledWith(input.id, "yo")).to.be.true;
     });
 
+    it("should handle a focus event", () => {
+      const onFocus = sandbox.spy();
+      const { node } = createFormComponent({
+        schema: {
+          type: "string",
+        },
+        onFocus,
+      });
+      const input = node.querySelector("input");
+      Simulate.focus(input, {
+        target: { value: "yo" },
+      });
+
+      expect(onFocus.calledWith(input.id, "yo")).to.be.true;
+    });
+
     it("should handle an empty string change event", () => {
       const { comp, node } = createFormComponent({
         schema: { type: "string" },
diff --git a/test/performance_test.js b/test/performance_test.js
index 1e04667e5..64e36b5dd 100644
--- a/test/performance_test.js
+++ b/test/performance_test.js
@@ -105,6 +105,7 @@ describe("Rendering performance optimizations", () => {
   describe("SchemaField", () => {
     const onChange = () => {};
     const onBlur = () => {};
+    const onFocus = () => {};
     const registry = getDefaultRegistry();
     const uiSchema = {};
     const schema = {
@@ -123,6 +124,7 @@ describe("Rendering performance optimizations", () => {
         onChange,
         idSchema,
         onBlur,
+        onFocus,
       };
 
       const { comp } = createComponent(SchemaField, props);
diff --git a/test/validate_test.js b/test/validate_test.js
index 83c568250..2b9d7d2a6 100644
--- a/test/validate_test.js
+++ b/test/validate_test.js
@@ -513,24 +513,57 @@ describe("Validation", () => {
         minLength: 1,
       };
 
+      const uiSchema = {
+        foo: "bar",
+      };
+
       const formData = 0;
 
-      const CustomErrorList = ({ errors }) =>
-        <div className="CustomErrorList">
-          {errors.length} custom
+      const CustomErrorList = ({
+        errors,
+        errorSchema,
+        schema,
+        uiSchema,
+        formContext: { className },
+      }) =>
+        <div>
+          <div className="CustomErrorList">
+            {errors.length} custom
+          </div>
+          <div className={"ErrorSchema"}>
+            {errorSchema.__errors[0]}
+          </div>
+          <div className={"Schema"}>
+            {schema.type}
+          </div>
+          <div className={"UiSchema"}>
+            {uiSchema.foo}
+          </div>
+          <div className={className} />
         </div>;
 
       it("should use CustomErrorList", () => {
         const { node } = createFormComponent({
           schema,
+          uiSchema,
           liveValidate: true,
           formData,
           ErrorList: CustomErrorList,
+          formContext: { className: "foo" },
         });
         expect(node.querySelectorAll(".CustomErrorList")).to.have.length.of(1);
         expect(node.querySelector(".CustomErrorList").textContent).eql(
           "1 custom"
         );
+        expect(node.querySelectorAll(".ErrorSchema")).to.have.length.of(1);
+        expect(node.querySelector(".ErrorSchema").textContent).eql(
+          "is required"
+        );
+        expect(node.querySelectorAll(".Schema")).to.have.length.of(1);
+        expect(node.querySelector(".Schema").textContent).eql("string");
+        expect(node.querySelectorAll(".UiSchema")).to.have.length.of(1);
+        expect(node.querySelector(".UiSchema").textContent).eql("bar");
+        expect(node.querySelectorAll(".foo")).to.have.length.of(1);
       });
     });
   });