Vulcan icon indicating copy to clipboard operation
Vulcan copied to clipboard

New Feature: Field Type Decorators

Open SachaG opened this issue 5 years ago • 1 comments

Let's imagine a complex form component such as a map that lets you select a location stored as a { lat, lng } pair. Currently we can do input: Map which activates the correct form input component.

But the component won't be functional unless we also specify the correct type (a SimpleSchema object with lat and lng subfields), GraphQL type (including input types), and maybe even validation (lat and lng have to be floats contained between x and y).

Currently all this has to be done manually, but we could automate it via a pattern I'm calling "field type templates" until I find a better name (field factories? field models?):

{
  location: makeMap('location', fieldSchema, options)
}

The makeMap template function would take a field name, field schema, and some options, and return a new schema that includes all the elements mentioned above. That field schema is then included in the collection schema as before.

Here's a real-world example for a future Likert Scale component:

import { addGraphQLSchema } from 'meteor/vulcan:core';
import SimpleSchema from 'simpl-schema';

export const makeLikert = (fieldName, field) => {
  // get typeName from fieldName unless it's already specified in field object
  const { typeName = `${fieldName}Type` } = field;

  if (!field.options) {
    throw new Error(`Likert fields need an 'options' property`);
  }

  // build SimpleSchema type object for validation
  const typeObject = {};
  field.options.forEach(({ value }) => {
    typeObject[value] = {
      type: SimpleSchema.Integer,
    };
  });

  // create GraphQL types for main type, create input type, and update input type
  addGraphQLSchema(`type ${typeName} {
  ${field.options.map(({ value }) => `${value}: Int`).join('\n  ')}
}

input Create${typeName}DataInput {
  ${field.options.map(({ value }) => `${value}: Int`).join('\n  ')}
}

input Update${typeName}DataInput {
  ${field.options.map(({ value }) => `${value}: Int`).join('\n  ')}
}
  `);

  // add additional field object properties
  const likertField = {
    ...field,
    type: new SimpleSchema(typeObject),
    input: 'likert',
    typeName,
  };

  return likertField;
};

SachaG avatar Feb 27 '20 09:02 SachaG

Maybe "decorator" would be a better name than "template"?

SachaG avatar Feb 28 '20 02:02 SachaG