Skip to content

Commit

Permalink
Merge pull request #38 from scottnath/feature/ux-scripts
Browse files Browse the repository at this point in the history
Feature/ux scripts
  • Loading branch information
rachelnicole authored Jun 28, 2016
2 parents d6e0a3e + c74e15a commit 2c20173
Show file tree
Hide file tree
Showing 9 changed files with 254 additions and 16 deletions.
48 changes: 40 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,21 +148,53 @@ import plugin from '../'; // Input Plugin's index.js
types.pluginTests(test, plugin);
```

### Forms
### Create your forms

#### Usage

##### In your node application:

```javascript
cont contentTypes = require('punchcard-content-types');
cont content = require('punchcard-content-types');
contentTypes.only('my-awesome-content-type').then(type => {
return contentTypes.form(type);
content.only('my-awesome-content-type').then(type => {
return content.form(type);
}).then(form => {
// HTML Rendered Form
// use form object within your html to create a content type form (see below)
});
```

```javascript
cont contentTypes = require('punchcard-content-types');
##### In your HTML

```html
<form action="{{action}}" method="post" enctype="multipart/form-data" novalidate>
{{form.html | safe}}
<fieldset id="sunrise-sunset">
<legend>Select Sunrise and Sunset</legend>
<label for="sunrise-date"><input type="date" name="sunrise-date" id="sunrise-date" value="{{data['sunrise-date'].value}}"></label>
<label for="sunrise-time"><input type="time" name="sunrise-time" id="sunrise-time" value="{{data['sunrise-time'].value}}"></label>
<label for="sunset-date"><input type="date" name="sunset-date" id="sunset-date" value="{{data['sunset-date'].value}}"></label>
<label for="sunset-time"><input type="time" name="sunset-time" id="sunset-time" value="{{data['sunset-time'].value}}"></label>
</fieldset>
contentTypes.form.validation(parsedForm);
<button type="submit">Submit</button>
<button type="cancel">Cancel</button>
</form>
<script>
{{form.validation | safe}}
{{form.scripts | safe}}
</script>
<script type="text/javascript" src="/js/sunrise-sunset.js"></script>
```

#### Form response object

```javascript
form.html // string of wrapped form elements; should be placed inside a <form> tag
form.validation // validation, wrapped for browser via browserify
form.script // UX scripts, wrapped for browser via browserify
```
8 changes: 8 additions & 0 deletions lib/content-types/merge.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ const squish = (types) => {
// Override any overriding with defaults
merged.validation = plugin.inputs[attr].validation;
merged.type = plugin.inputs[attr].type;
merged.script = {
on: '',
function: '',
};

if (plugin.inputs[attr].hasOwnProperty('script')) {
merged.script = plugin.inputs[attr].script;
}

// Options of attribute overrides default
if (attribute.inputs[attr].hasOwnProperty('options')) {
Expand Down
5 changes: 5 additions & 0 deletions lib/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

const html = require('./form/html');
const validation = require('./form/validation');
const scripts = require('./form/scripts');

const validate = require('./form/validate');

Expand All @@ -18,6 +19,10 @@ const form = (type, errors) => {
return validation(type);
}).then(result => {
rendered.validation = result;

return scripts(type);
}).then(result => {
rendered.scripts = result;
}).then(() => {
return rendered;
});
Expand Down
152 changes: 152 additions & 0 deletions lib/form/scripts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
'use strict';

const browserify = require('browserify');
const stream = require('stream');

const rendered = (type) => {
return new Promise((res, rej) => {
const b = browserify();
const s = new stream.Readable();

const scriptFunctions = {};
const allSettings = {};
const inputs = {};
const ids = {};
let plugins = type.attributes.map(plugin => {
return plugin.type;
});

plugins = plugins.filter((item, pos, self) => {
return self.indexOf(item) === pos;
});

plugins.forEach(plugin => {
// Require Plugins for Browserify
b.require(`input-plugin-${plugin}`);
scriptFunctions[plugin] = `input-plugin-${plugin}`;
});

type.attributes.map(plugin => {
const children = Object.keys(plugin.inputs);

// All settings for this plugin
allSettings[plugin.id] = {};

// Add ID to all IDs
ids[plugin.id] = plugin.id;

children.forEach(input => {
// Get input
const inp = plugin.inputs[input];

// Get its siblings
const siblings = children.filter(child => {
if (child === input) {
return false;
}

return true;
});

// Set settings for this input blank as default
allSettings[plugin.id][input] = {};

// Set to actual settings if it exists
if (inp.hasOwnProperty('settings')) {
allSettings[plugin.id][input] = inp.settings;
}

// Build input object for this specific input
inputs[inp.id] = {};
inputs[inp.id].name = input;
inputs[inp.id].script = inp.script;
inputs[inp.id].settings = {};
inputs[inp.id].siblings = {};

if (inp.hasOwnProperty('settings')) {
inputs[inp.id].settings = inp.settings;
}

// Get IDs for all siblings
siblings.forEach(sibling => {
inputs[inp.id].siblings[sibling] = plugin.inputs[sibling].id;
});

// Add ID to all IDs
ids[inp.id] = inp.id;
});
});

const scripts = `
var allScripts = ${JSON.stringify(scriptFunctions)};
var allInputs = ${JSON.stringify(inputs)};
var allSettings = ${JSON.stringify(allSettings)};
var allIDs = ${JSON.stringify(ids)};
// Work those requires
Object.keys(allScripts).forEach(function(plugin) {
var plug = require(allScripts[plugin]);
// Require the script from the inputs
allScripts[plugin] = plug.scripts;
});
// Script Function
var script = function (e) {
var target = e.target;
var inp = allInputs[target.id];
var input = {};
var settings = {};
// Set up script input
input.target = target;
input.all = {};
input.all[inp.name] = target;
// Set up script settings
settings.target = inp.settings;
settings.all = {};
settings.all[inp.name] = inp.settings;
// Set all sibling input and settings
Object.keys(inp.siblings).forEach(function (sibling) {
input.all[sibling] = allIDs[inp.siblings[sibling]];
settings.all[sibling] = allSettings[inp.parent.id][sibling];
});
// Run the script
script = allScripts[inp.parent.type][inp.script.function](input, settings);
};
// Add Document Event Listener
document.addEventListener('DOMContentLoaded', function () {
// Get all elements so we don't need to constantly read from the DOM
Object.keys(allIDs).forEach(function (id) {
allIDs[id] = document.getElementById(id);
});
// Add all event listeners
Object.keys(allInputs).forEach(function (id) {
if (allInputs[id].hasOwnProperty('script')) {
allIDs[id].addEventListener(allInputs[id].script.on, script);
}
});
});`;
s._read = function read() {};

s.push(scripts);
s.push(null);

b.add(s);
b.bundle((err, file) => {
if (err) {
rej(err);
}

res(file.toString());
});
});
};

module.exports = rendered;
1 change: 0 additions & 1 deletion lib/form/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,6 @@ document.addEventListener('DOMContentLoaded', function () {

s._read = function read() {};

// s.push(test);
s.push(validation);
s.push(null);

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"input-plugin-select": "^0.1.0",
"input-plugin-email": "^0.1.0",
"input-plugin-quote": "0.0.1",
"input-plugin-selects-related": "0.0.1",
"input-plugin-text": "^0.0.5",
"input-plugin-textarea": "^0.0.1",
"node-dir": "^0.1.12",
Expand Down
29 changes: 24 additions & 5 deletions tests/content-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,22 +198,35 @@ test('merged with correct param', t => {
},
},
},
{
type: 'selects-related',
id: 'related-selects',
name: 'Related Select Fields',
description: 'Please choose wisely',
inputs: {
select1: {
label: 'I am the first',
},
select2: {
label: 'I am the second',
},
},
},
],
};

return types([testCT])
.then(result => {
let input;
const merged = result[0];

// console.log(util.inspect(result, false, null));

t.is(result.length, 1, 'There is one result');

t.is(merged.name, 'FooRific', 'Content type name does not change');
t.is(merged.description, 'A very foo content model.', 'Content type description does not change');
t.is(merged.id, 'foo-rific', 'Content type ID does not change');
t.true(merged.hasOwnProperty('attributes'), 'Content type has attributes');
t.is(merged.attributes.length, 2, 'Content type has two attributes');
t.is(merged.attributes.length, 3, 'Content type has three attributes');

merged.attributes.forEach((attr, i) => {
let base = testCT.attributes[i];
Expand All @@ -228,7 +241,7 @@ test('merged with correct param', t => {
t.true(attr.hasOwnProperty('inputs'), 'Attribute has inputs');

if (Object.keys(attr.inputs).length === 1) {
let input = Object.keys(attr.inputs)[0];
input = Object.keys(attr.inputs)[0];

if (base.hasOwnProperty('inputs')) {
if (base.inputs.hasOwnProperty(input)) {
Expand All @@ -238,10 +251,16 @@ test('merged with correct param', t => {
}
}
else {
t.is(attr.inputs[input].label, base.name, 'Input label is set to name if one input')
t.is(attr.inputs[input].label, base.name, 'Input label is set to name if one input');
}
}

if (attr === merged.attributes[2]) {
input = Object.keys(attr.inputs);
t.true(attr.inputs[input[0]].hasOwnProperty('script'), 'Attribute has scripts');
t.true(attr.inputs[input[1]].hasOwnProperty('script'), 'Attribute has scripts');
}

});
});
})
4 changes: 4 additions & 0 deletions tests/fixtures/content-types/baz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ attributes:
required: 'publish'
settings:
empty: false
- type: selects-related
name: Two related select elements
id: input-related-selects
description: I am a set of selects
- type: select
name: example dropdown
id: example-dropdown
Expand Down
Loading

0 comments on commit 2c20173

Please sign in to comment.