Display schema defaults inline next to Nodes (#666)
* Display schema defaults inline next to Nodes * Improve usability of schema default display - When value is default, make it bold and set a tooltip - When value is not default, display the default next to the value - When value is default and is a select, show "Default" next to it - Lighten the color of green used for values This increases the contrast between normal values and default values. * Remove styling when value is the same as the schema default This styling may have been confusing for some users and may not have been applicable to all situations. * Apply is-default and is-not-default classes to values This allows the user to supply custom styling for these states. To set styles for values that match the default value in the schema, use the class `.jsoneditor-is-default`. To set styles for values that _do not_ match the default value in the schema, use the class `.jsoneditor-is-not-default`. * Remove extra newline after schema examples in tooltip * Move schema default display from inline to tooltip This presents less opportunity for user confusion and is likely to be more widely applicable. * Add examples of schema metadata display * Add documentation on styling
This commit is contained in:
parent
7a2e89c329
commit
c79bea4eb8
|
@ -0,0 +1,24 @@
|
||||||
|
# Styling Reference
|
||||||
|
|
||||||
|
Documentation for writing custom JSON Editor styles.
|
||||||
|
|
||||||
|
## Node
|
||||||
|
Node is the fundamental unit that makes up the hierarchical JSON display in the Form, Tree, and View modes. It can be
|
||||||
|
customized with several classes that reflect its type and state.
|
||||||
|
|
||||||
|
- `jsoneditor-field`: the property name
|
||||||
|
- `jsoneditor-value`: the value of the property
|
||||||
|
- The value element will have one of the following classes depending on its type:
|
||||||
|
- `jsoneditor-null`
|
||||||
|
- `jsoneditor-undefined`
|
||||||
|
- `jsoneditor-number`
|
||||||
|
- `jsoneditor-string`
|
||||||
|
- `jsoneditor-boolean`
|
||||||
|
- `jsoneditor-regexp`
|
||||||
|
- `jsoneditor-array`
|
||||||
|
- `jsoneditor-object`
|
||||||
|
- `jsoneditor-url`
|
||||||
|
- `jsoneditor-is-default`: applied to the value element when the value matches the default from the schema
|
||||||
|
- `jsoneditor-is-not-default`: applied to the value element when the value does not match the default from the schema
|
||||||
|
- `jsoneditor-schema-error`: the warning icon that appears when the Node has a schema validation error
|
||||||
|
- `jsoneditor-popover`: the popover that appears when hovering over the schema validation error warning icon
|
|
@ -35,16 +35,28 @@
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"firstName": {
|
"firstName": {
|
||||||
|
"title": "First Name",
|
||||||
|
"description": "The given name.",
|
||||||
|
"examples": [
|
||||||
|
"John"
|
||||||
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"lastName": {
|
"lastName": {
|
||||||
|
"title": "Last Name",
|
||||||
|
"description": "The family name.",
|
||||||
|
"examples": [
|
||||||
|
"Smith"
|
||||||
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"gender": {
|
"gender": {
|
||||||
|
"title": "Gender",
|
||||||
"enum": ["male", "female"]
|
"enum": ["male", "female"]
|
||||||
},
|
},
|
||||||
"availableToHire": {
|
"availableToHire": {
|
||||||
"type": "boolean"
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
},
|
},
|
||||||
"age": {
|
"age": {
|
||||||
"description": "Age in years",
|
"description": "Age in years",
|
||||||
|
@ -65,10 +77,19 @@
|
||||||
"required": ["address"],
|
"required": ["address"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"company": {
|
"company": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"examples": [
|
||||||
|
"ACME",
|
||||||
|
"Dexter Industries"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"role": {
|
"role": {
|
||||||
"type": "string"
|
"description": "Job title.",
|
||||||
|
"type": "string",
|
||||||
|
"examples": [
|
||||||
|
"Human Resources Coordinator",
|
||||||
|
"Software Developer"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"address": {
|
"address": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
|
|
@ -5,7 +5,8 @@ div.jsoneditor {
|
||||||
|
|
||||||
div.jsoneditor-field,
|
div.jsoneditor-field,
|
||||||
div.jsoneditor-value,
|
div.jsoneditor-value,
|
||||||
div.jsoneditor-readonly {
|
div.jsoneditor-readonly,
|
||||||
|
div.jsoneditor-default {
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
min-height: 16px;
|
min-height: 16px;
|
||||||
min-width: 32px;
|
min-width: 32px;
|
||||||
|
@ -97,13 +98,12 @@ div.jsoneditor-value.jsoneditor-highlight-active:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
div.jsoneditor-value.jsoneditor-string {
|
div.jsoneditor-value.jsoneditor-string {
|
||||||
color: #008000;
|
color: #006000;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.jsoneditor-value.jsoneditor-object,
|
div.jsoneditor-value.jsoneditor-object,
|
||||||
div.jsoneditor-value.jsoneditor-array {
|
div.jsoneditor-value.jsoneditor-array {
|
||||||
min-width: 16px;
|
min-width: 16px;
|
||||||
color: #808080;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div.jsoneditor-value.jsoneditor-number {
|
div.jsoneditor-value.jsoneditor-number {
|
||||||
|
@ -122,7 +122,10 @@ div.jsoneditor-value.jsoneditor-invalid {
|
||||||
color: #000000;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-default {
|
||||||
|
color: #808080;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
div.jsoneditor-tree button.jsoneditor-button {
|
div.jsoneditor-tree button.jsoneditor-button {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
|
|
|
@ -1822,6 +1822,8 @@ Node.prototype._updateDomValue = function () {
|
||||||
|
|
||||||
// strip formatting from the contents of the editable div
|
// strip formatting from the contents of the editable div
|
||||||
util.stripFormatting(domValue);
|
util.stripFormatting(domValue);
|
||||||
|
|
||||||
|
this._updateDomDefault();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1908,6 +1910,32 @@ Node.prototype._getDomField = function(silent) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the value of the schema default element in the DOM.
|
||||||
|
* @private
|
||||||
|
* @returns {undefined}
|
||||||
|
*/
|
||||||
|
Node.prototype._updateDomDefault = function () {
|
||||||
|
// Short-circuit if schema is missing, has no default, or if Node has children
|
||||||
|
if (!this.schema || !this.schema.default || this._hasChilds()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.value === this.schema.default) {
|
||||||
|
if (this.dom.select) {
|
||||||
|
this.dom.value.removeAttribute('title');
|
||||||
|
} else {
|
||||||
|
this.dom.value.title = translate('default');
|
||||||
|
this.dom.value.classList.add('jsoneditor-is-default');
|
||||||
|
this.dom.value.classList.remove('jsoneditor-is-not-default');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.dom.value.removeAttribute('title');
|
||||||
|
this.dom.value.classList.remove('jsoneditor-is-default');
|
||||||
|
this.dom.value.classList.add('jsoneditor-is-not-default');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate this node and all it's childs
|
* Validate this node and all it's childs
|
||||||
* @return {Array.<{node: Node, error: {message: string}}>} Returns a list with duplicates
|
* @return {Array.<{node: Node, error: {message: string}}>} Returns a list with duplicates
|
||||||
|
|
|
@ -86,6 +86,7 @@ var _defs = {
|
||||||
modeViewText: 'View',
|
modeViewText: 'View',
|
||||||
modeViewTitle: 'Switch to tree view',
|
modeViewTitle: 'Switch to tree view',
|
||||||
examples: 'Examples',
|
examples: 'Examples',
|
||||||
|
default: 'Default',
|
||||||
},
|
},
|
||||||
'zh-CN': {
|
'zh-CN': {
|
||||||
array: '数组',
|
array: '数组',
|
||||||
|
@ -169,6 +170,7 @@ var _defs = {
|
||||||
modeViewText: '视图',
|
modeViewText: '视图',
|
||||||
modeViewTitle: '切换至树视图',
|
modeViewTitle: '切换至树视图',
|
||||||
examples: '例子',
|
examples: '例子',
|
||||||
|
default: '缺省',
|
||||||
},
|
},
|
||||||
'pt-BR': {
|
'pt-BR': {
|
||||||
array: 'Lista',
|
array: 'Lista',
|
||||||
|
@ -264,6 +266,7 @@ var _defs = {
|
||||||
'Campo do tipo nao é determinado através do seu valor, ' +
|
'Campo do tipo nao é determinado através do seu valor, ' +
|
||||||
'mas sempre retornara um texto.',
|
'mas sempre retornara um texto.',
|
||||||
examples: 'Exemplos',
|
examples: 'Exemplos',
|
||||||
|
default: 'Revelia',
|
||||||
},
|
},
|
||||||
tr: {
|
tr: {
|
||||||
array: 'Dizin',
|
array: 'Dizin',
|
||||||
|
@ -347,6 +350,7 @@ var _defs = {
|
||||||
modeViewText: 'Görünüm',
|
modeViewText: 'Görünüm',
|
||||||
modeViewTitle: 'Ağaç görünümüne geç',
|
modeViewTitle: 'Ağaç görünümüne geç',
|
||||||
examples: 'Örnekler',
|
examples: 'Örnekler',
|
||||||
|
default: 'Varsayılan',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1087,13 +1087,24 @@ exports.makeFieldTooltip = function (schema, locale) {
|
||||||
tooltip += schema.description;
|
tooltip += schema.description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (schema.default) {
|
||||||
|
if (tooltip.length > 0) {
|
||||||
|
tooltip += '\n\n';
|
||||||
|
}
|
||||||
|
tooltip += translate('default', undefined, locale) + '\n';
|
||||||
|
tooltip += JSON.stringify(schema.default, null, 2);
|
||||||
|
}
|
||||||
|
|
||||||
if (Array.isArray(schema.examples) && schema.examples.length > 0) {
|
if (Array.isArray(schema.examples) && schema.examples.length > 0) {
|
||||||
if (tooltip.length > 0) {
|
if (tooltip.length > 0) {
|
||||||
tooltip += '\n\n';
|
tooltip += '\n\n';
|
||||||
}
|
}
|
||||||
tooltip += translate('examples', undefined, locale) + '\n';
|
tooltip += translate('examples', undefined, locale) + '\n';
|
||||||
schema.examples.forEach(function (example) {
|
schema.examples.forEach(function (example, index) {
|
||||||
tooltip += JSON.stringify(example, null, 2) + '\n';
|
tooltip += JSON.stringify(example, null, 2);
|
||||||
|
if (index !== schema.examples.length - 1) {
|
||||||
|
tooltip += '\n';
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -160,8 +160,12 @@ describe('util', function () {
|
||||||
assert.strictEqual(util.makeFieldTooltip({description: 'foo'}), 'foo');
|
assert.strictEqual(util.makeFieldTooltip({description: 'foo'}), 'foo');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should make tooltips with only default', function () {
|
||||||
|
assert.strictEqual(util.makeFieldTooltip({default: 'foo'}), 'Default\n"foo"');
|
||||||
|
});
|
||||||
|
|
||||||
it('should make tooltips with only examples', function () {
|
it('should make tooltips with only examples', function () {
|
||||||
assert.strictEqual(util.makeFieldTooltip({examples: ['foo', 'bar']}), 'Examples\n"foo"\n"bar"\n');
|
assert.strictEqual(util.makeFieldTooltip({examples: ['foo', 'bar']}), 'Examples\n"foo"\n"bar"');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should make tooltips with title and description', function () {
|
it('should make tooltips with title and description', function () {
|
||||||
|
@ -180,7 +184,14 @@ describe('util', function () {
|
||||||
it('should make tooltips with title, description, and examples', function () {
|
it('should make tooltips with title, description, and examples', function () {
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
util.makeFieldTooltip({title: 'foo', description: 'bar', examples: ['baz']}),
|
util.makeFieldTooltip({title: 'foo', description: 'bar', examples: ['baz']}),
|
||||||
'foo\nbar\n\nExamples\n"baz"\n',
|
'foo\nbar\n\nExamples\n"baz"',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should make tooltips with title, description, default, and examples', function () {
|
||||||
|
assert.strictEqual(
|
||||||
|
util.makeFieldTooltip({title: 'foo', description: 'bar', default: 'bat', examples: ['baz']}),
|
||||||
|
'foo\nbar\n\nDefault\n"bat"\n\nExamples\n"baz"',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -188,11 +199,15 @@ describe('util', function () {
|
||||||
assert.strictEqual(util.makeFieldTooltip({title: '', description: 'bar'}), 'bar');
|
assert.strictEqual(util.makeFieldTooltip({title: '', description: 'bar'}), 'bar');
|
||||||
assert.strictEqual(util.makeFieldTooltip({title: 'foo', description: ''}), 'foo');
|
assert.strictEqual(util.makeFieldTooltip({title: 'foo', description: ''}), 'foo');
|
||||||
assert.strictEqual(util.makeFieldTooltip({description: 'bar', examples: []}), 'bar');
|
assert.strictEqual(util.makeFieldTooltip({description: 'bar', examples: []}), 'bar');
|
||||||
assert.strictEqual(util.makeFieldTooltip({description: 'bar', examples: ['']}), 'bar\n\nExamples\n""\n');
|
assert.strictEqual(util.makeFieldTooltip({description: 'bar', examples: ['']}), 'bar\n\nExamples\n""');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should internationalize "Defaults" correctly', function () {
|
||||||
|
assert.strictEqual(util.makeFieldTooltip({default: 'foo'}, 'pt-BR'), 'Revelia\n"foo"');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should internationalize "Examples" correctly', function () {
|
it('should internationalize "Examples" correctly', function () {
|
||||||
assert.strictEqual(util.makeFieldTooltip({examples: ['foo']}, 'pt-BR'), 'Exemplos\n"foo"\n');
|
assert.strictEqual(util.makeFieldTooltip({examples: ['foo']}, 'pt-BR'), 'Exemplos\n"foo"');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// TODO: thoroughly test all util methods
|
// TODO: thoroughly test all util methods
|
||||||
|
|
Loading…
Reference in New Issue