From 39dfc411009f67fe93854466c2c7358d007b4027 Mon Sep 17 00:00:00 2001 From: jos Date: Wed, 27 Nov 2019 15:28:50 +0100 Subject: [PATCH] Improve `timestampTag` API (see #847) --- HISTORY.md | 2 +- docs/api.md | 42 +++++++++++++++++++++++++++++++++++------- src/js/Node.js | 31 +++++++++++++++++++++++++++++-- src/js/treemode.js | 18 ------------------ src/js/util.js | 22 +++++++++++++++------- test/util.test.js | 13 ++++++++++++- 6 files changed, 92 insertions(+), 36 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index c071096..c6bf77a 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -8,7 +8,7 @@ https://github.com/josdejong/jsoneditor - Implemented callbacks `onFocus` and `onBlur` (PR #809, issue #727). Thanks @123survesh. - Fixed #847: allow customizing the in rules determining whether a value - is a timestamp or not. + is a timestamp or not by passing a callback function to `timestampTag`. ## 2019-10-27, version 7.2.1 diff --git a/docs/api.md b/docs/api.md index aa8fe6c..1a1bce8 100644 --- a/docs/api.md +++ b/docs/api.md @@ -432,16 +432,44 @@ Constructs a new JSONEditor. } ``` -- `{boolean | function(value: any) -> boolean} timestampTag` +- `{boolean | function({field, value, path}) -> boolean} timestampTag` If `true` (default), a tag with the date/time of a timestamp is displayed - right from values containing a timestamp. A value is considered a timestamp - when it is a number with value larger than Jan 1th 2000, `946684800000`. + right from values containing a timestamp. By default, a value is + considered a timestamp when it is a number and it's field name contains any + of the following strings (case insensitive): `'date'`, `'time'`, `'created'`, + `'updated'`, `'deleted'`. - When `timestampTag` a is a function, a timestamp tag will be displayed for - values for which `timestampTag(value)` returns `true`. This way it is - possible to alter the default rules for determining whether a value - is a timestamp or not. + When `timestampTag` a is a function, a timestamp tag will be displayed when + this function returns `true`. The function is invoked with an object as first + parameter: + + ``` + { + field: string, + value: string, + path: string[] + } + ``` + + Whether a value is a timestamp can be determined implicitly based on + the `value`, or explicitly based on `field` or `path`. + + Example: + + ```js + var options = { + timestampTag: function ({ field, value, path }) { + if (field === 'dateCreated') { + return true + } + + return false + } + } + ``` + + Only applicable for modes `tree`, `form`, and `view`. - `{string} language` diff --git a/src/js/Node.js b/src/js/Node.js index 67e78e5..650c2d5 100644 --- a/src/js/Node.js +++ b/src/js/Node.js @@ -18,12 +18,14 @@ import { getAbsoluteTop, getInnerText, getType, + isTimestampFieldName, isUrl, isValidColor, makeFieldTooltip, parse, parsePath, - parseString, removeAllClassNames, + parseString, + removeAllClassNames, removeClassName, removeEventListener, selectContentEditable, @@ -1782,7 +1784,7 @@ export class Node { } // show date tag when value is a timestamp in milliseconds - if (this.editor.showTimestampTag(value)) { + if (this._showTimestampTag()) { if (!this.dom.date) { this.dom.date = document.createElement('div') this.dom.date.className = 'jsoneditor-date' @@ -1933,6 +1935,31 @@ export class Node { } } + /** + * Test whether to show a timestamp tag or not + * @return {boolean} Returns true when the value is a timestamp + */ + _showTimestampTag () { + if (typeof this.value !== 'number') { + return false + } + + const timestampTag = this.editor.options.timestampTag + if (typeof timestampTag === 'function') { + return timestampTag({ + field: this.field, + value: this.value, + path: this.getPath() + }) + } + + if (timestampTag === true) { + return isTimestampFieldName(this.field) + } + + return false + } + /** * Clear the dom of the node */ diff --git a/src/js/treemode.js b/src/js/treemode.js index 6356d73..d1775a5 100644 --- a/src/js/treemode.js +++ b/src/js/treemode.js @@ -18,7 +18,6 @@ import { hasParentNode, improveSchemaError, isPromise, - isTimestamp, isValidValidationError, parse, removeClassName, @@ -1786,23 +1785,6 @@ treemode.getNodesByRange = function (start, end) { return serializableNodes } -/** - * Test whether to show a timestamp tag or not - * @param {*} value Any type of value - * @return {boolean} Returns true when the value is a timestamp - */ -treemode.showTimestampTag = function (value) { - if (typeof this.options.timestampTag === 'function') { - return this.options.timestampTag(value) - } - - if (this.options.timestampTag === true) { - return isTimestamp(value) - } - - return false -} - // define modes export const treeModeMixins = [ { diff --git a/src/js/util.js b/src/js/util.js index 94c3051..f5ce604 100644 --- a/src/js/util.js +++ b/src/js/util.js @@ -1355,15 +1355,23 @@ export function parseString (str) { return str } +const TIMESTAMP_FIELDS = [ + 'DATE', + 'TIME', + 'CREATED', + 'UPDATED', + 'DELETED' +] + /** - * Test whether some value contains a timestamp. - * @param {*} value Any type of value - * @return {boolean} Returns true when the value is a timestamp + * Test whether some field has a naming like "date" or "time" + * @param {string} field + * @return {boolean} */ -export function isTimestamp (value) { - return typeof value === 'number' && - value > YEAR_2000 && - !isNaN(new Date(value).valueOf()) +export function isTimestampFieldName (field) { + const fieldUpper = field.toUpperCase() + + return TIMESTAMP_FIELDS.some(search => fieldUpper.indexOf(search) !== -1) } /** diff --git a/test/util.test.js b/test/util.test.js index 76accec..f58d777 100644 --- a/test/util.test.js +++ b/test/util.test.js @@ -6,7 +6,7 @@ import { get, getChildPaths, getIndexForPosition, - isObject, + isObject, isTimestampFieldName, limitCharacters, makeFieldTooltip, parsePath, @@ -476,5 +476,16 @@ describe('util', () => { assert.strictEqual(compileJSONPointer([]), '') }) + it('should test whether a field is a timestamp', () => { + assert.strictEqual(isTimestampFieldName('dateCreated'), true) + assert.strictEqual(isTimestampFieldName('updatedAt'), true) + assert.strictEqual(isTimestampFieldName('deletedAt'), true) + assert.strictEqual(isTimestampFieldName('DATE'), true) + assert.strictEqual(isTimestampFieldName('TIMESTAMP'), true) + assert.strictEqual(isTimestampFieldName('timestamp'), true) + assert.strictEqual(isTimestampFieldName('hello'), false) + assert.strictEqual(isTimestampFieldName('TIM'), false) + }) + // TODO: thoroughly test all util methods })