mirror of
https://github.com/vale981/Vulcan
synced 2025-03-06 10:01:40 -05:00
Merge branch 'feature/nested-schema' of https://github.com/lbke/Vulcan into lbke-feature/nested-schema
This commit is contained in:
commit
96a396fd8c
12 changed files with 414 additions and 125 deletions
|
@ -393,6 +393,7 @@ class SmartForm extends Component {
|
|||
if (fieldSchema.schema) {
|
||||
field.nestedSchema = fieldSchema.schema;
|
||||
field.nestedInput = true;
|
||||
|
||||
// get nested schema
|
||||
// for each nested field, get field object by calling createField recursively
|
||||
field.nestedFields = this.getFieldNames({ schema: field.nestedSchema }).map(subFieldName => {
|
||||
|
|
|
@ -5,6 +5,7 @@ import { registerComponent } from 'meteor/vulcan:core';
|
|||
import get from 'lodash/get';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import { isEmptyValue, mergeValue } from '../modules/utils.js';
|
||||
import SimpleSchema from 'simpl-schema'
|
||||
|
||||
class FormComponent extends Component {
|
||||
constructor(props) {
|
||||
|
@ -287,11 +288,24 @@ class FormComponent extends Component {
|
|||
}
|
||||
};
|
||||
|
||||
getFieldType = () => {
|
||||
return this.props.datatype[0].type
|
||||
}
|
||||
isArrayField = () => {
|
||||
return this.getFieldType() === Array
|
||||
}
|
||||
isObjectField = () => {
|
||||
return this.getFieldType() instanceof SimpleSchema
|
||||
}
|
||||
render() {
|
||||
if (this.props.intlInput) {
|
||||
return <Components.FormIntl {...this.props} />;
|
||||
} else if (this.props.nestedInput){
|
||||
return <Components.FormNested {...this.props} />;
|
||||
} else if (this.props.nestedInput) {
|
||||
if (this.isArrayField()) {
|
||||
return <Components.FormNestedArray {...this.props} />;
|
||||
} else if (this.isObjectField()) {
|
||||
return <Components.FormNestedObject {...this.props} />;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Components.FormComponentInner
|
||||
|
@ -337,4 +351,6 @@ FormComponent.contextTypes = {
|
|||
getDocument: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
module.exports = FormComponent
|
||||
|
||||
registerComponent('FormComponent', FormComponent);
|
||||
|
|
|
@ -25,7 +25,7 @@ class FormGroup extends PureComponent {
|
|||
<div className="form-section-heading" onClick={this.toggle}>
|
||||
<h3 className="form-section-heading-title">{this.props.label}</h3>
|
||||
<span className="form-section-heading-toggle">
|
||||
{this.state.collapsed ? <Components.IconRight height={16} width={16}/> : <Components.IconDown height={16} width={16} />}
|
||||
{this.state.collapsed ? <Components.IconRight height={16} width={16} /> : <Components.IconDown height={16} width={16} />}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
@ -80,6 +80,8 @@ FormGroup.propTypes = {
|
|||
currentUser: PropTypes.object,
|
||||
};
|
||||
|
||||
module.exports = FormGroup
|
||||
|
||||
registerComponent('FormGroup', FormGroup);
|
||||
|
||||
const IconRight = ({ width = 24, height = 24 }) => (
|
||||
|
|
|
@ -1,49 +1,9 @@
|
|||
import React, { PureComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Components, registerComponent } from 'meteor/vulcan:core';
|
||||
import "./FormNestedItem"
|
||||
|
||||
const FormNestedItem = ({ nestedFields, name, path, removeItem, itemIndex, ...props }, { errors }) => {
|
||||
return (
|
||||
<div className="form-nested-item">
|
||||
<div className="form-nested-item-inner">
|
||||
{nestedFields.map((field, i) => {
|
||||
return (
|
||||
<Components.FormComponent
|
||||
key={i}
|
||||
{...props}
|
||||
{...field}
|
||||
path={`${path}.${field.name}`}
|
||||
itemIndex={itemIndex}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="form-nested-item-remove">
|
||||
<Components.Button
|
||||
className="form-nested-button"
|
||||
variant="danger"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
removeItem(name);
|
||||
}}
|
||||
>
|
||||
<Components.IconRemove height={12} width={12} />
|
||||
</Components.Button>
|
||||
</div>
|
||||
<div className="form-nested-item-deleted-overlay" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
FormNestedItem.contextTypes = {
|
||||
errors: PropTypes.array,
|
||||
};
|
||||
|
||||
|
||||
registerComponent('FormNestedItem', FormNestedItem);
|
||||
|
||||
class FormNested extends PureComponent {
|
||||
class FormNestedArray extends PureComponent {
|
||||
getCurrentValue() {
|
||||
return this.props.currentValues[this.props.path] || []
|
||||
}
|
||||
|
@ -78,7 +38,7 @@ class FormNested extends PureComponent {
|
|||
{value.map(
|
||||
(subDocument, i) =>
|
||||
!this.isDeleted(i) && (
|
||||
<FormNestedItem
|
||||
<Components.FormNestedItem
|
||||
{...properties}
|
||||
key={i}
|
||||
itemIndex={i}
|
||||
|
@ -98,15 +58,15 @@ class FormNested extends PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
FormNested.propTypes = {
|
||||
FormNestedArray.propTypes = {
|
||||
currentValues: PropTypes.object,
|
||||
path: PropTypes.string,
|
||||
label: PropTypes.string
|
||||
};
|
||||
|
||||
module.exports = FormNested
|
||||
module.exports = FormNestedArray
|
||||
|
||||
registerComponent('FormNested', FormNested);
|
||||
registerComponent('FormNestedArray', FormNestedArray);
|
||||
|
||||
const IconAdd = ({ width = 24, height = 24 }) => (
|
||||
<svg width={width} height={height} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
|
53
packages/vulcan-forms/lib/components/FormNestedItem.jsx
Normal file
53
packages/vulcan-forms/lib/components/FormNestedItem.jsx
Normal file
|
@ -0,0 +1,53 @@
|
|||
import React from "react"
|
||||
import PropTypes from 'prop-types';
|
||||
import { Components, registerComponent } from 'meteor/vulcan:core';
|
||||
|
||||
const FormNestedItem = ({ nestedFields, name, path, removeItem, itemIndex, ...props }, { errors }) => {
|
||||
const isArray = typeof itemIndex !== 'undefined'
|
||||
return (
|
||||
<div className="form-nested-item">
|
||||
<div className="form-nested-item-inner">
|
||||
{nestedFields.map((field, i) => {
|
||||
return (
|
||||
<Components.FormComponent
|
||||
key={i}
|
||||
{...props}
|
||||
{...field}
|
||||
path={`${path}.${field.name}`}
|
||||
itemIndex={itemIndex}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{
|
||||
isArray && [
|
||||
<div key="remove-button" className="form-nested-item-remove">
|
||||
<Components.Button
|
||||
className="form-nested-button"
|
||||
variant="danger"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
removeItem(name);
|
||||
}}
|
||||
>
|
||||
<Components.IconRemove height={12} width={12} />
|
||||
</Components.Button>
|
||||
</div>,
|
||||
<div key="remove-button-overlay" className="form-nested-item-deleted-overlay" />
|
||||
]
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
FormNestedItem.propTypes = {
|
||||
path: PropTypes.string.isRequired,
|
||||
itemIndex: PropTypes.number
|
||||
}
|
||||
|
||||
|
||||
FormNestedItem.contextTypes = {
|
||||
errors: PropTypes.array,
|
||||
};
|
||||
|
||||
registerComponent('FormNestedItem', FormNestedItem);
|
37
packages/vulcan-forms/lib/components/FormNestedObject.jsx
Normal file
37
packages/vulcan-forms/lib/components/FormNestedObject.jsx
Normal file
|
@ -0,0 +1,37 @@
|
|||
import React, { PureComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Components, registerComponent } from 'meteor/vulcan:core';
|
||||
import "./FormNestedItem"
|
||||
|
||||
class FormNestedObject extends PureComponent {
|
||||
/*getCurrentValue() {
|
||||
return this.props.currentValues[this.props.path] || {}
|
||||
}*/
|
||||
render() {
|
||||
//const value = this.getCurrentValue()
|
||||
// do not pass FormNested's own value, input and inputProperties props down
|
||||
const properties = _.omit(this.props, 'value', 'input', 'inputProperties', 'nestedInput');
|
||||
return (
|
||||
<div className="form-group row form-nested">
|
||||
<label className="control-label col-sm-3">{this.props.label}</label>
|
||||
<div className="col-sm-9">
|
||||
<Components.FormNestedItem
|
||||
{...properties}
|
||||
path={`${this.props.path}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
FormNestedObject.propTypes = {
|
||||
currentValues: PropTypes.object,
|
||||
path: PropTypes.string,
|
||||
label: PropTypes.string
|
||||
};
|
||||
|
||||
module.exports = FormNestedObject
|
||||
|
||||
registerComponent('FormNestedObject', FormNestedObject);
|
||||
|
|
@ -2,7 +2,8 @@ import '../components/FieldErrors.jsx';
|
|||
import '../components/FormErrors.jsx';
|
||||
import '../components/FormError.jsx';
|
||||
import '../components/FormComponent.jsx';
|
||||
import '../components/FormNested.jsx';
|
||||
import '../components/FormNestedArray.jsx';
|
||||
import '../components/FormNestedObject.jsx';
|
||||
import '../components/FormIntl.jsx';
|
||||
import '../components/FormGroup.jsx';
|
||||
import '../components/FormSubmit.jsx';
|
||||
|
|
|
@ -17,7 +17,7 @@ export const convertSchema = (schema, flatten = false) => {
|
|||
// extract schema
|
||||
jsonSchema[fieldName] = getFieldSchema(fieldName, schema);
|
||||
|
||||
// check for existence of nested schema on corresponding array field
|
||||
// check for existence of nested schema
|
||||
const subSchema = getNestedSchema(fieldName, schema);
|
||||
// if nested schema exists, call convertSchema recursively
|
||||
if (subSchema) {
|
||||
|
@ -51,15 +51,43 @@ export const getFieldSchema = (fieldName, schema) => {
|
|||
return fieldSchema;
|
||||
};
|
||||
|
||||
|
||||
// type is an array due to the possibility of using SimpleSchema.oneOf
|
||||
// right now we support only fields with one type
|
||||
export const getSchemaType = schema => schema.type.definitions[0].type
|
||||
const getArrayNestedSchema = (fieldName, schema) => {
|
||||
const arrayItemSchema = schema._schema[`${fieldName}.$`];
|
||||
const nestedSchema = arrayItemSchema && getSchemaType(arrayItemSchema)
|
||||
return nestedSchema
|
||||
}
|
||||
// nested object fields type is of the form "type: new SimpleSchema({...})"
|
||||
// so they should possess a "_schema" prop
|
||||
const isNestedSchemaField = (fieldSchema) => {
|
||||
const fieldType = getSchemaType(fieldSchema)
|
||||
//console.log('fieldType', typeof fieldType, fieldType._schema)
|
||||
return fieldType && !!fieldType._schema
|
||||
}
|
||||
const getObjectNestedSchema = (fieldName, schema) => {
|
||||
const fieldSchema = schema._schema[fieldName]
|
||||
if (!isNestedSchemaField(fieldSchema)) return null
|
||||
const nestedSchema = fieldSchema && getSchemaType(fieldSchema)
|
||||
return nestedSchema
|
||||
}
|
||||
/*
|
||||
|
||||
Given an array field, get its nested schema
|
||||
|
||||
*/
|
||||
export const getNestedSchema = (fieldName, schema) => {
|
||||
const arrayItemSchema = schema._schema[`${fieldName}.$`];
|
||||
const nestedSchema = arrayItemSchema && arrayItemSchema.type.definitions[0].type;
|
||||
return nestedSchema;
|
||||
const arrayItemSchema = getArrayNestedSchema(fieldName, schema)
|
||||
if (!arrayItemSchema) {
|
||||
// look for an object schema
|
||||
const objectItemSchema = getObjectNestedSchema(fieldName, schema)
|
||||
// no schema was found
|
||||
if (!objectItemSchema) return null
|
||||
return objectItemSchema
|
||||
}
|
||||
return arrayItemSchema
|
||||
};
|
||||
|
||||
export const schemaProperties = [
|
||||
|
|
|
@ -8,7 +8,7 @@ Package.describe({
|
|||
Package.onUse(function (api) {
|
||||
api.versionsFrom("1.6.1");
|
||||
|
||||
api.use(["vulcan:core@1.11.2", "fourseven:scss@4.5.0"]);
|
||||
api.use(["vulcan:core@1.11.2", "vulcan:ui-bootstrap@1.11.2", "fourseven:scss@4.5.0"]);
|
||||
|
||||
api.addFiles(["lib/stylesheets/style.scss", "lib/stylesheets/datetime.scss"], "client");
|
||||
|
||||
|
|
|
@ -1,20 +1,32 @@
|
|||
// setup JSDOM server side for testing (necessary for Enzyme to mount)
|
||||
import 'jsdom-global/register'
|
||||
import React from 'react'
|
||||
// TODO: should be loaded from Components instead?
|
||||
import Form from '../lib/components/Form'
|
||||
import FormNested from '../lib/components/FormNested'
|
||||
import FormGroup from "../lib/components/FormGroup"
|
||||
import FormComponent from "../lib/components/FormComponent"
|
||||
import '../lib/components/FormNestedArray'
|
||||
import expect from 'expect'
|
||||
import Enzyme, { mount, shallow } from 'enzyme'
|
||||
import Adapter from 'enzyme-adapter-react-16';
|
||||
// we must import all the other components, so that "registerComponent" is called
|
||||
import "../lib/modules/components"
|
||||
|
||||
|
||||
import { Components } from "meteor/vulcan:core"
|
||||
|
||||
// setup enzyme
|
||||
// TODO: write a reusable helper and move this to the tests setup
|
||||
Enzyme.configure({ adapter: new Adapter() })
|
||||
|
||||
// we must import all the other components, so that "registerComponent" is called
|
||||
import "../lib/modules/components"
|
||||
// and then load them in the app so that <Component.Whatever /> is defined
|
||||
import { populateComponentsApp, initializeFragments } from "meteor/vulcan:lib"
|
||||
// we need registered fragments to be initialized because populateComponentsApp will run
|
||||
// hocs, like withUpdate, that rely on fragments
|
||||
initializeFragments()
|
||||
// actually fills the Components object
|
||||
populateComponentsApp()
|
||||
|
||||
|
||||
|
||||
// fixtures
|
||||
import SimpleSchema from "simpl-schema";
|
||||
const addressGroup = {
|
||||
|
@ -22,61 +34,52 @@ const addressGroup = {
|
|||
label: "Addresses",
|
||||
order: 10
|
||||
};
|
||||
const addressSchema = new SimpleSchema({
|
||||
const addressSchema = {
|
||||
street: {
|
||||
type: String,
|
||||
optional: true,
|
||||
viewableBy: ["guests"],
|
||||
editableBy: ["members"],
|
||||
insertableBy: ["members"],
|
||||
editableBy: ["quests"],
|
||||
insertableBy: ["quests"],
|
||||
max: 100 // limit street address to 100 characters
|
||||
},
|
||||
});
|
||||
const schema = {
|
||||
_id: {
|
||||
type: String,
|
||||
optional: true,
|
||||
viewableBy: ["guests"]
|
||||
},
|
||||
createdAt: {
|
||||
type: Date,
|
||||
optional: true,
|
||||
onInsert: (document, currentUser) => {
|
||||
return new Date();
|
||||
}
|
||||
},
|
||||
userId: {
|
||||
type: String,
|
||||
optional: true
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
optional: false,
|
||||
viewableBy: ["guests"],
|
||||
editableBy: ["members"],
|
||||
insertableBy: ["members"],
|
||||
searchable: true // make field searchable
|
||||
},
|
||||
};
|
||||
const arraySchema = {
|
||||
addresses: {
|
||||
type: Array,
|
||||
viewableBy: ["guests"],
|
||||
editableBy: ["members"],
|
||||
insertableBy: ["members"],
|
||||
editableBy: ["quests"],
|
||||
insertableBy: ["quests"],
|
||||
group: addressGroup
|
||||
},
|
||||
"addresses.$": {
|
||||
type: addressSchema
|
||||
type: new SimpleSchema(addressSchema)
|
||||
}
|
||||
};
|
||||
const objectSchema = {
|
||||
addresses: {
|
||||
type: new SimpleSchema(addressSchema),
|
||||
viewableBy: ["guests"],
|
||||
editableBy: ["quests"],
|
||||
insertableBy: ["quests"],
|
||||
},
|
||||
};
|
||||
|
||||
// stub collection
|
||||
import { createCollection, getDefaultResolvers, getDefaultMutations } from 'meteor/vulcan:core'
|
||||
const Customers = createCollection({
|
||||
collectionName: 'Customers',
|
||||
typeName: 'Customer',
|
||||
schema,
|
||||
resolvers: getDefaultResolvers('Customers'),
|
||||
mutations: getDefaultMutations('Customers'),
|
||||
const WithArrays = createCollection({
|
||||
collectionName: 'WithArrays',
|
||||
typeName: 'WithArray',
|
||||
schema: arraySchema,
|
||||
resolvers: getDefaultResolvers('WithArrays'),
|
||||
mutations: getDefaultMutations('WithArrays'),
|
||||
});
|
||||
const WithObjects = createCollection({
|
||||
collectionName: 'WithObjects',
|
||||
typeName: 'WithObject',
|
||||
schema: objectSchema,
|
||||
resolvers: getDefaultResolvers('WithObjects'),
|
||||
mutations: getDefaultMutations('WithObjects'),
|
||||
});
|
||||
|
||||
const Addresses = createCollection({
|
||||
|
@ -87,24 +90,26 @@ const Addresses = createCollection({
|
|||
mutations: getDefaultMutations('Addresses'),
|
||||
})
|
||||
|
||||
// tests
|
||||
describe('vulcan-forms/components', function () {
|
||||
describe('Form', function () {
|
||||
const mountWithContext = C => mount(C, {
|
||||
context: {
|
||||
intl: {
|
||||
formatMessage: () => "",
|
||||
formatDate: () => ""
|
||||
}
|
||||
}
|
||||
})
|
||||
const shallowWithContext = C => shallow(C, {
|
||||
context: {
|
||||
const context = {
|
||||
intl: {
|
||||
formatMessage: () => "",
|
||||
formatDate: () => "",
|
||||
formatTime: () => ""
|
||||
formatTime: () => "",
|
||||
formatRelative: () => "",
|
||||
formatNumber: () => "",
|
||||
formatPlural: () => "",
|
||||
formatHTMLMessage: () => ""
|
||||
}
|
||||
|
||||
}
|
||||
const mountWithContext = C => mount(C, {
|
||||
context
|
||||
})
|
||||
const shallowWithContext = C => shallow(C, {
|
||||
context
|
||||
})
|
||||
describe('basic', function () {
|
||||
it('shallow render', function () {
|
||||
|
@ -112,19 +117,165 @@ describe('vulcan-forms/components', function () {
|
|||
expect(wrapper).toBeDefined()
|
||||
})
|
||||
})
|
||||
describe('nested forms', function () {
|
||||
describe('nested array', function () {
|
||||
it('shallow render', () => {
|
||||
const wrapper = shallowWithContext(<Form collection={Customers} />)
|
||||
const wrapper = shallowWithContext(<Form collection={WithArrays} />)
|
||||
expect(wrapper).toBeDefined()
|
||||
})
|
||||
it('render a FormGroup for addresses', function () {
|
||||
const wrapper = shallowWithContext(<Form collection={WithArrays} />)
|
||||
const formGroup = wrapper.find('FormGroup').find({ name: 'addresses' })
|
||||
expect(formGroup).toBeDefined()
|
||||
expect(formGroup).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
describe('nested object', function () {
|
||||
it('shallow render', () => {
|
||||
const wrapper = shallowWithContext(<Form collection={WithObjects} />)
|
||||
expect(wrapper).toBeDefined()
|
||||
})
|
||||
it('define one field', () => {
|
||||
const wrapper = shallowWithContext(<Form collection={WithObjects} />)
|
||||
const defaultGroup = wrapper.find('FormGroup').first()
|
||||
const fields = defaultGroup.prop('fields')
|
||||
expect(fields).toHaveLength(1) // addresses field
|
||||
})
|
||||
|
||||
const getFormFields = (wrapper) => {
|
||||
const defaultGroup = wrapper.find('FormGroup').first()
|
||||
const fields = defaultGroup.prop('fields')
|
||||
return fields
|
||||
}
|
||||
const getFirstField = () => {
|
||||
const wrapper = shallowWithContext(<Form collection={WithObjects} />)
|
||||
const fields = getFormFields(wrapper)
|
||||
return fields[0]
|
||||
}
|
||||
it('define the nestedSchema', () => {
|
||||
const addressField = getFirstField()
|
||||
expect(addressField.nestedSchema.street).toBeDefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('FormNested', function () {
|
||||
it('mount', function () {
|
||||
const wrapper = shallow(<FormNested path="foobar" currentValues={{}} />)
|
||||
describe('FormComponent', function () {
|
||||
const shallowWithContext = C => shallow(C, {
|
||||
context: {
|
||||
getDocument: () => { }
|
||||
}
|
||||
})
|
||||
const defaultProps = {
|
||||
"disabled": false,
|
||||
"optional": true,
|
||||
"document": {},
|
||||
"name": "meetingPlace",
|
||||
"path": "meetingPlace",
|
||||
"datatype": [{ type: Object }],
|
||||
"layout": "horizontal",
|
||||
"label": "Meeting place",
|
||||
"currentValues": {},
|
||||
"formType": "new",
|
||||
deletedValues: [],
|
||||
throwError: () => { },
|
||||
updateCurrentValues: () => { },
|
||||
errors: [],
|
||||
clearFieldErrors: () => { },
|
||||
}
|
||||
it('shallow render', function () {
|
||||
const wrapper = shallowWithContext(<FormComponent {...defaultProps} />)
|
||||
expect(wrapper).toBeDefined()
|
||||
})
|
||||
describe('nested array', function () {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
"datatype": [{ type: Array }],
|
||||
"nestedSchema": {
|
||||
"street": {},
|
||||
"country": {},
|
||||
"zipCode": {}
|
||||
},
|
||||
"nestedInput": true,
|
||||
"nestedFields": [
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
],
|
||||
"currentValues": {},
|
||||
}
|
||||
it('render a FormNestedArray', function () {
|
||||
const wrapper = shallowWithContext(<FormComponent {...props} />)
|
||||
const formNested = wrapper.find('FormNestedArray')
|
||||
expect(formNested).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
describe('nested object', function () {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
"datatype": [{ type: new SimpleSchema({}) }],
|
||||
"nestedSchema": {
|
||||
"street": {},
|
||||
"country": {},
|
||||
"zipCode": {}
|
||||
},
|
||||
"nestedInput": true,
|
||||
"nestedFields": [
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
],
|
||||
"currentValues": {},
|
||||
}
|
||||
it('shallow render', function () {
|
||||
const wrapper = shallowWithContext(<FormComponent {...props} />)
|
||||
expect(wrapper).toBeDefined()
|
||||
})
|
||||
it('render a FormNestedObject', function () {
|
||||
const wrapper = shallowWithContext(<FormComponent {...props} />)
|
||||
const formNested = wrapper.find('FormNestedObject')
|
||||
expect(formNested).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('FormNestedArray', function () {
|
||||
it('shallow render', function () {
|
||||
const wrapper = shallow(<Components.FormNestedArray path="foobar" currentValues={{}} />)
|
||||
expect(wrapper).toBeDefined()
|
||||
})
|
||||
it('shows a button', function () {
|
||||
const wrapper = shallow(<Components.FormNestedArray path="foobar" currentValues={{}} />)
|
||||
const button = wrapper.find('BootstrapButton')
|
||||
expect(button).toHaveLength(1)
|
||||
})
|
||||
it('shows an add button', function () {
|
||||
const wrapper = shallow(<Components.FormNestedArray path="foobar" currentValues={{}} />)
|
||||
const addButton = wrapper.find('IconAdd')
|
||||
expect(addButton).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
describe('FormNestedObject', function () {
|
||||
it('shallow render', function () {
|
||||
const wrapper = shallow(<Components.FormNestedObject path="foobar" currentValues={{}} />)
|
||||
expect(wrapper).toBeDefined()
|
||||
})
|
||||
it.skip('render a form for the object', function () {
|
||||
const wrapper = shallow(<Components.FormNestedObject path="foobar" currentValues={{}} />)
|
||||
expect(false).toBe(true)
|
||||
})
|
||||
it('does not show any button', function () {
|
||||
const wrapper = shallow(<Components.FormNestedObject path="foobar" currentValues={{}} />)
|
||||
const button = wrapper.find('BootstrapButton')
|
||||
expect(button).toHaveLength(0)
|
||||
})
|
||||
it('does not show add button', function () {
|
||||
const wrapper = shallow(<Components.FormNestedObject path="foobar" currentValues={{}} />)
|
||||
const addButton = wrapper.find('IconAdd')
|
||||
expect(addButton).toHaveLength(0)
|
||||
})
|
||||
it('does not show remove button', function () {
|
||||
const wrapper = shallow(<Components.FormNestedObject path="foobar" currentValues={{}} />)
|
||||
const removeButton = wrapper.find('IconRemove')
|
||||
expect(removeButton).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,3 +1,2 @@
|
|||
import './schema_utils.test.js'
|
||||
import './package.test.js'
|
||||
import './components.test.js'
|
|
@ -1,10 +1,51 @@
|
|||
//import { convertSchema } from '../lib/modules/schema_utils.js'
|
||||
import { convertSchema, getSchemaType, getNestedSchema } from '../lib/modules/schema_utils.js'
|
||||
import SimpleSchema from 'simpl-schema'
|
||||
import expect from 'expect'
|
||||
|
||||
const addressSchema = {
|
||||
street: {
|
||||
type: String,
|
||||
},
|
||||
country: {
|
||||
type: String,
|
||||
},
|
||||
}
|
||||
const addressSimpleSchema = new SimpleSchema(addressSchema)
|
||||
|
||||
describe('schema_utils', function () {
|
||||
describe('convertSchema', function () {
|
||||
it('run a test', function () {
|
||||
expect(true).toBe(true)
|
||||
describe('getNestedSchema', function () {
|
||||
it('get nested schema of an array', function () {
|
||||
const simpleSchema = new SimpleSchema({
|
||||
addresses: {
|
||||
type: Array
|
||||
},
|
||||
"addresses.$": {
|
||||
// this is due to SimpleSchema objects structure
|
||||
type: addressSimpleSchema
|
||||
}
|
||||
})
|
||||
const nestedSchema = getNestedSchema('addresses', simpleSchema)
|
||||
// nestedSchema is a complex SimpleSchema object, so we can only
|
||||
// test its type instead (might not be the simplest way though)
|
||||
expect(Object.keys(nestedSchema._schema)).toEqual(Object.keys(addressSchema))
|
||||
})
|
||||
it('get nested schema of an object', function () {
|
||||
const simpleSchema = new SimpleSchema({
|
||||
meetingPlace: {
|
||||
type: addressSimpleSchema
|
||||
}
|
||||
})
|
||||
const nestedSchema = getNestedSchema('meetingPlace', simpleSchema)
|
||||
expect(Object.keys(nestedSchema._schema)).toEqual(Object.keys(addressSchema))
|
||||
})
|
||||
it('return null for other types', function () {
|
||||
const simpleSchema = new SimpleSchema({
|
||||
createdAt: {
|
||||
type: Date
|
||||
}
|
||||
})
|
||||
const nestedSchema = getNestedSchema('createdAt', simpleSchema)
|
||||
expect(nestedSchema).toBeNull()
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Add table
Reference in a new issue