Skip to content

Commit

Permalink
New field type: JSON keystonejs#190
Browse files Browse the repository at this point in the history
  • Loading branch information
Juan Benavides authored and Sverre Kristian Valskrå committed Apr 4, 2017
1 parent 5a318be commit b8f0e92
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 1 deletion.
2 changes: 1 addition & 1 deletion admin/public/styles/keystone/field-types.less
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@



// Code
// Code, JSON
// ------------------------------

.CodeMirror-container {
Expand Down
96 changes: 96 additions & 0 deletions fields/types/json/JsonField.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
var _ = require('underscore'),
React = require('react'),
Field = require('../Field'),
CodeMirror = require('codemirror');

// See CodeMirror docs for API:
// http://codemirror.net/doc/manual.html

module.exports = Field.create({

displayName: 'JsonField',

getInitialState: function() {
return {
isFocused: false
};
},

componentDidMount: function() {
if (!this.refs.codemirror) {
return;
}

var options = _.defaults({}, this.props.editor, {
lineNumbers: true,
readOnly: this.shouldRenderField() ? false : true
});

this.codeMirror = CodeMirror.fromTextArea(this.refs.codemirror.getDOMNode(), options);
this.codeMirror.on('change', this.codemirrorValueChanged);
this.codeMirror.on('focus', this.focusChanged.bind(this, true));
this.codeMirror.on('blur', this.focusChanged.bind(this, false));
this._currentCodemirrorValue = this.props.value;
},

componentWillUnmount: function() {
// todo: is there a lighter-weight way to remove the cm instance?
if (this.codeMirror) {
this.codeMirror.toTextArea();
}
},

componentWillReceiveProps: function(nextProps) {
if (this.codeMirror && this._currentCodemirrorValue !== nextProps.value) {
this.codeMirror.setValue(nextProps.value);
}
},

focus: function() {
if (this.codeMirror) {
this.codeMirror.focus();
}
},

focusChanged: function(focused) {
this.setState({
isFocused: focused
});
},

codemirrorValueChanged: function(doc, change) {//eslint-disable-line no-unused-vars
var newValue = doc.getValue();
this._currentCodemirrorValue = newValue;
this.props.onChange({
path: this.props.path,
value: newValue
});
},

renderCodemirror: function() {
var className = 'CodeMirror-container';
var propValue = this.props.value;
var jsonStringify = '';

if(propValue != null) {
jsonStringify = JSON.stringify(this.props.value, null, '\t');
}
if (this.state.isFocused && this.shouldRenderField()) {
className += ' is-focused';
}
return (
<div className={className}>
<textarea ref="codemirror" name={this.props.path} value={jsonStringify} onChange={this.valueChanged} autoComplete="off" className="form-control" />
</div>
);
},

renderValue: function() {
return this.renderCodemirror();
},

renderField: function() {
return this.renderCodemirror();
}

});
74 changes: 74 additions & 0 deletions fields/types/json/JsonType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
var _ = require('underscore');
var FieldType = require('../Type');
var TextType = require('../text/TextType');
var util = require('util');
var utils = require('keystone-utils');


/**
* HTML FieldType Constructor
* @extends Field
* @api public
*/
function json(list, path, options) {
this._nativeType = Object;
this._defaultSize = 'full';
this.height = options.height || 180;
this._properties = ['editor', 'height'];
this.codemirror = options.codemirror || {};
this.editor = _.defaults(this.codemirror, { mode : json });
json.super_.call(this, list, path, options);
}
util.inherits(json, FieldType);

/* Inherit from TextType prototype */
json.prototype.addFilterToQuery = TextType.prototype.addFilterToQuery;


json.prototype.getValueFromData = function(data, canThrow) {
var value = this.path in data ? data[this.path] : this._path.get(data);

if(typeof value == 'string') {
try {
value = JSON.parse(value);
} catch(ex) {
if(canThrow === true) {
throw ex;
} else {
value = null;
}
}
}

return value;
};


json.prototype.validateInput = function(data, required, item) {
var value = this.getValueFromData(data);

if (value === undefined && item && (item.get(this.path) || item.get(this.path) === 0)) {
return true;
}

if (value == null && required) {
return false;
} else if(value == null && !required) {
return true;
} else {
try {
value = this.getValueFromData(data, true);

if(typeof value != 'object') {
return false;
} else {
return true;
}
} catch(ex) {
return false;
}
}
};

/* Export Field Type */
exports = module.exports = json;
83 changes: 83 additions & 0 deletions fields/types/json/test/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
var demand = require('must'),
CodeType = require('../CodeType');

exports.initList = function(List) {
List.add({
code: { type: CodeType },
nested: {
code: { type: CodeType }
},
lang: {
type: CodeType,
lang: 'c'
},
language: {
type: CodeType,
lang: 'js'
},
codemirror: {
type: CodeType,
lang: 'html',
codemirror: {
value: 'fooga'
}
}
});
};

exports.createData = function(List) {//eslint-disable-line no-unused-vars

};

exports.testFilters = function(List) {//eslint-disable-line no-unused-vars

};

exports.testFieldType = function(List) {
var testItem = new List.model();

it('should update top level fields', function() {
List.fields.code.updateItem(testItem, {
code: 'foo(bar);'
});
demand(testItem.code).be('foo(bar);');
testItem.code = undefined;
});

it('should update nested fields', function() {
List.fields['nested.code'].updateItem(testItem, {
nested: {
code: 'foo(bar);'
}
});
demand(testItem.nested.code).be('foo(bar);');
testItem.nested.code = undefined;
});

it('should update nested fields with flat paths', function() {
List.fields['nested.code'].updateItem(testItem, {
'nested.code': 'foo(bar);'
});
demand(testItem.nested.code).be('foo(bar);');
testItem.nested.code = undefined;
});

it('should handle a `lang` config property', function() {
demand(List.fields.lang.lang).be('c');
});

it('should handle a `language` config property', function() {
demand(List.fields.language.lang).be('js');
});

it('should support a `codemirror` config property', function() {
demand(List.fields.codemirror.codemirror).be.object();
demand(List.fields.codemirror.codemirror.value).be('fooga');
});

it('should merge the `lang` and `codemirror` config properties', function() {
demand(List.fields.codemirror.editor).be.object();
demand(List.fields.codemirror.editor.mode).be('html');
demand(List.fields.codemirror.editor.value).be('fooga');
});
};
1 change: 1 addition & 0 deletions lib/fieldTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var fields = {
get File () { return require('../fields/types/file/FileType'); },
get GeoPoint () { return require('../fields/types/geopoint/GeoPointType'); },
get Html () { return require('../fields/types/html/HtmlType'); },
get Json () { return require('../fields/types/json/JsonType'); },
get Key () { return require('../fields/types/key/KeyType'); },
get LocalFile () { return require('../fields/types/localfile/LocalFileType'); },
get LocalFiles () { return require('../fields/types/localfiles/LocalFilesType'); },
Expand Down

0 comments on commit b8f0e92

Please sign in to comment.