Skip to content

Commit

Permalink
Merge pull request #395 from cidgoh/issue-391
Browse files Browse the repository at this point in the history
Unified date, time, and datetime input widgets
  • Loading branch information
ddooley authored Jun 10, 2023
2 parents 542a4a2 + 06aa10f commit 0e4fbef
Show file tree
Hide file tree
Showing 14 changed files with 717 additions and 109 deletions.
94 changes: 76 additions & 18 deletions lib/DataHarmonizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import {
fieldUnitBinTest,
formatMultivaluedValue,
parseMultivaluedValue,
KEEP_ORIGINAL,
JSON_SCHEMA_FORMAT,
} from './utils/fields';
import { parseDatatype } from './utils/datatypes';
import { Datatypes } from './utils/datatypes';
import {
checkProvenance,
itemCompare,
Expand All @@ -37,6 +39,21 @@ import pkg from '../package.json';
const VERSION = pkg.version;
const VERSION_TEXT = 'DataHarmonizer v' + VERSION;

import { DateEditor, DatetimeEditor, TimeEditor } from './editors';

Handsontable.cellTypes.registerCellType('dh.datetime', {
editor: DatetimeEditor,
renderer: Handsontable.renderers.getRenderer('autocomplete'),
});
Handsontable.cellTypes.registerCellType('dh.date', {
editor: DateEditor,
renderer: Handsontable.renderers.getRenderer('autocomplete'),
});
Handsontable.cellTypes.registerCellType('dh.time', {
editor: TimeEditor,
renderer: Handsontable.renderers.getRenderer('autocomplete'),
});

class DataHarmonizer {
//An instance of DataHarmonizer has a schema, a domElement, and a handsontable .hot object
root = null;
Expand Down Expand Up @@ -70,6 +87,10 @@ class DataHarmonizer {
'examples',
'menus',
];
this.dateFormat = options.dateFormat || 'yyyy-MM-dd';
this.datetimeFormat = options.datetimeFormat || 'yyyy-MM-dd hh:mm aa';
this.timeFormat = options.timeFormat || 'hh:mm aa';
this.dateExportBehavior = options.dateExportBehavior || JSON_SCHEMA_FORMAT;
this.self = this;

// Use help sidebar by default unless turned off by client
Expand Down Expand Up @@ -415,7 +436,9 @@ class DataHarmonizer {
let contentBuffer = await readFileAsync(file);
if (file.type === 'application/json') {
this.loadDataObjects(JSON.parse(contentBuffer));
} else this.loadSpreadsheetData(contentBuffer);
} else {
this.loadSpreadsheetData(contentBuffer);
}
} catch (err) {
console.log(err);
}
Expand Down Expand Up @@ -1100,14 +1123,20 @@ class DataHarmonizer {
//}

if (field.datatype == 'xsd:date') {
col.type = 'date';
// This controls calendar popup date format, default is mm/dd/yyyy
// See https://handsontable.com/docs/8.3.0/Options.html#correctFormat
col.dateFormat = 'YYYY-MM-DD';
// If correctFormat = true, then on import and on data
// entry of cell will convert date values like "2020" to "2020-01-01"
// automatically.
col.correctFormat = false;
col.type = 'dh.date';
col.flatpickrConfig = {
dateFormat: this.dateFormat,
};
} else if (field.datatype == 'xsd:dateTime') {
col.type = 'dh.datetime';
col.flatpickrConfig = {
dateFormat: this.datetimeFormat,
};
} else if (field.datatype == 'xsd:time') {
col.type = 'dh.time';
col.flatpickrConfig = {
dateFormat: this.timeFormat,
};
}

if (typeof field.getColumn === 'function') {
Expand Down Expand Up @@ -1423,19 +1452,36 @@ class DataHarmonizer {

/**
* Get grid data as a list of objects. The keys of each object are the
* field names and the values are parsed according to the field's datatype
* if the `strict` parameter is true (default). If `strict` is false, no
* parsing is done and all values will be strings.
* field names and the values are parsed according to the field's datatype.
* If parsing fails the original string value is used instead. This behavior
* can be changed using the `options` argument.
*
* @param {boolean} strict Exclude values that don't adhere to column data types
* @param {Object} options An object with any of the following keys:
* - parseFailureBehavior: `KEEP_ORIGINAL` (default) | `REMOVE` | `THROW_ERROR`
* controls how values which are not parsable according to the column's datatype are
* handled. By default the original string value is retained. If this option is `REMOVE`
* the unparsable value is removed entirely. If this option is `THROW_ERROR`, an error
* is thrown when the first unparsable value is encountered.
* @return {Array<Object>}
*/
getDataObjects(strict = true) {
getDataObjects(options = {}) {
const { parseFailureBehavior } = {
parseFailureBehavior: KEEP_ORIGINAL,
...options,
};
const listData = this.hot.getData();
const fields = this.getFields();
return listData
.filter((row) => row.some((cell) => cell !== '' && cell != null))
.map((row) => dataArrayToObject(row, fields, { strict: strict }));
.map((row) =>
dataArrayToObject(row, fields, {
dateBehavior: this.dateExportBehavior,
parseFailureBehavior,
dateFormat: this.dateFormat,
datetimeFormat: this.datetimeFormat,
timeFormat: this.timeFormat,
})
);
}

/**
Expand All @@ -1446,7 +1492,14 @@ class DataHarmonizer {
*/
loadDataObjects(data) {
const fields = this.getFields();
const listData = data.map((row) => dataObjectToArray(row, fields));
const listData = data.map((row) =>
dataObjectToArray(row, fields, {
serializedDateFormat: this.dateExportBehavior,
dateFormat: this.dateFormat,
datetimeFormat: this.datetimeFormat,
timeFormat: this.timeFormat,
})
);
this.hot.loadData(this.matrixFieldChangeRules(listData));
}

Expand Down Expand Up @@ -2062,6 +2115,11 @@ class DataHarmonizer {
const fields = this.getFields();
const columnIndex = this.getFieldYCoordinates();
let TODAY = new Date();
const datatypes = new Datatypes({
dateFormat: this.dateFormat,
datetimeFormat: this.datetimeFormat,
timeFormat: this.timeFormat,
});

let provenanceChanges = [];
const indexToRowMap = new Map();
Expand Down Expand Up @@ -2116,7 +2174,7 @@ class DataHarmonizer {
else {
// parseDatatype will return undefined for any value that doesn't
// match the pattern of the given datatype
const parsed = parseDatatype(cellVal, datatype);
const parsed = datatypes.parse(cellVal, datatype);
valid = parsed !== undefined;

switch (datatype) {
Expand Down
2 changes: 1 addition & 1 deletion lib/Toolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class Toolbar {
const baseName = $('#base-name-save-as-input').val();
const ext = $('#file-ext-save-as-select').val();
if (ext == 'json') {
let data = dh.getDataObjects(false);
let data = dh.getDataObjects();
dh.runBehindLoadingScreen(exportJsonFile, [data, baseName, ext]);
} else {
let matrix = [...dh.getFlatHeaders(), ...dh.getTrimmedData()];
Expand Down
11 changes: 11 additions & 0 deletions lib/editors/DateEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import FlatpickrEditor from './FlatpickrEditor';

class DateEditor extends FlatpickrEditor {
getFlatpickrConfig() {
return {
enableTime: false,
};
}
}

export default DateEditor;
11 changes: 11 additions & 0 deletions lib/editors/DatetimeEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import FlatpickrEditor from './FlatpickrEditor';

class DatetimeEditor extends FlatpickrEditor {
getFlatpickrConfig() {
return {
enableTime: true,
};
}
}

export default DatetimeEditor;
Loading

0 comments on commit 0e4fbef

Please sign in to comment.