[Summary Widget] support enum fields (#2406)

* Display a drop down menu if the selected key is of type enum.

* Create normalized dataum when persisting telemerty datum using  metadatum source as key.:

* * Clear config values before creating new inputs.
* Emit ‘change' event with the value of the first option after creating the select element.
* If a value is a number, pass it as a number when emitting ‘change’. Similarly, if the cashed telemetry value is a number, convert it to number before applying the operation and validation.

* Update description.

* Update description in operations.js also.
This commit is contained in:
Pegah Sarram 2019-05-24 09:18:46 -07:00 committed by Andrew Henry
parent 35d0c02bc5
commit 72ea7b80fd
5 changed files with 114 additions and 30 deletions

View File

@ -70,16 +70,14 @@ define([
*/ */
function onValueInput(event) { function onValueInput(event) {
var elem = event.target, var elem = event.target,
value = (isNaN(elem.valueAsNumber) ? elem.value : elem.valueAsNumber), value = isNaN(Number(elem.value)) ? elem.value : Number(elem.value),
inputIndex = self.valueInputs.indexOf(elem); inputIndex = self.valueInputs.indexOf(elem);
if (elem.tagName.toUpperCase() === 'INPUT') { self.eventEmitter.emit('change', {
self.eventEmitter.emit('change', { value: value,
value: value, property: 'values[' + inputIndex + ']',
property: 'values[' + inputIndex + ']', index: self.index
index: self.index });
});
}
} }
this.listenTo(this.deleteButton, 'click', this.remove, this); this.listenTo(this.deleteButton, 'click', this.remove, this);
@ -108,8 +106,7 @@ define([
Object.values(this.selects).forEach(function (select) { Object.values(this.selects).forEach(function (select) {
$('.t-configuration', self.domElement).append(select.getDOM()); $('.t-configuration', self.domElement).append(select.getDOM());
}); });
this.listenTo($('.t-value-inputs', this.domElement), 'input', onValueInput);
this.listenTo($(this.domElement), 'input', onValueInput);
} }
Condition.prototype.getDOM = function (container) { Condition.prototype.getDOM = function (container) {
@ -167,7 +164,9 @@ define([
/** /**
* When an operation is selected, create the appropriate value inputs * When an operation is selected, create the appropriate value inputs
* and add them to the view * and add them to the view. If an operation is of type enum, create
* a drop-down menu instead.
*
* @param {string} operation The key of currently selected operation * @param {string} operation The key of currently selected operation
*/ */
Condition.prototype.generateValueInputs = function (operation) { Condition.prototype.generateValueInputs = function (operation) {
@ -176,25 +175,49 @@ define([
inputCount, inputCount,
inputType, inputType,
newInput, newInput,
index = 0; index = 0,
emitChange = false;
inputArea.html(''); inputArea.html('');
this.valueInputs = []; this.valueInputs = [];
this.config.values = [];
if (evaluator.getInputCount(operation)) { if (evaluator.getInputCount(operation)) {
inputCount = evaluator.getInputCount(operation); inputCount = evaluator.getInputCount(operation);
inputType = evaluator.getInputType(operation); inputType = evaluator.getInputType(operation);
while (index < inputCount) { while (index < inputCount) {
if (!this.config.values[index]) { if (inputType === 'select') {
this.config.values[index] = (inputType === 'number' ? 0 : ''); newInput = $('<select>' + this.generateSelectOptions() + '</select>');
emitChange = true;
} else {
this.config.values[index] = inputType === 'number' ? 0 : '';
newInput = $('<input type = "' + inputType + '" value = "' + this.config.values[index] + '"> </input>');
} }
newInput = $('<input type = "' + inputType + '" value = "' + this.config.values[index] + '"> </input>');
this.valueInputs.push(newInput.get(0)); this.valueInputs.push(newInput.get(0));
inputArea.append(newInput); inputArea.append(newInput);
index += 1; index += 1;
} }
if (emitChange) {
this.eventEmitter.emit('change', {
value: Number(newInput[0].options[0].value),
property: 'values[0]',
index: this.index
});
}
} }
}; };
Condition.prototype.generateSelectOptions = function () {
let telemetryMetadata = this.conditionManager.getTelemetryMetadata(this.config.object);
let options = '';
telemetryMetadata[this.config.key].enumerations.forEach(enumeration => {
options += '<option value="' + enumeration.value + '">'+ enumeration.string + '</option>';
});
return options;
};
return Condition; return Condition;
}); });

View File

@ -24,7 +24,8 @@ define([], function () {
*/ */
this.inputTypes = { this.inputTypes = {
number: 'number', number: 'number',
string: 'text' string: 'text',
enum: 'select'
}; };
/** /**
@ -34,7 +35,8 @@ define([], function () {
*/ */
this.inputValidators = { this.inputValidators = {
number: this.validateNumberInput, number: this.validateNumberInput,
string: this.validateStringInput string: this.validateStringInput,
enum: this.validateNumberInput
}; };
/** /**
@ -201,7 +203,7 @@ define([], function () {
return typeof input[0] === 'undefined'; return typeof input[0] === 'undefined';
}, },
text: 'is undefined', text: 'is undefined',
appliesTo: ['string', 'number'], appliesTo: ['string', 'number', 'enum'],
inputCount: 0, inputCount: 0,
getDescription: function () { getDescription: function () {
return ' is undefined'; return ' is undefined';
@ -212,11 +214,33 @@ define([], function () {
return typeof input[0] !== 'undefined'; return typeof input[0] !== 'undefined';
}, },
text: 'is defined', text: 'is defined',
appliesTo: ['string', 'number'], appliesTo: ['string', 'number', 'enum'],
inputCount: 0, inputCount: 0,
getDescription: function () { getDescription: function () {
return ' is defined'; return ' is defined';
} }
},
enumValueIs: {
operation: function (input) {
return input[0] === input[1];
},
text: 'is',
appliesTo: ['enum'],
inputCount: 1,
getDescription: function (values) {
return ' == ' + values[0];
}
},
enumValueIsNot: {
operation: function (input) {
return input[0] !== input[1];
},
text: 'is not',
appliesTo: ['enum'],
inputCount: 1,
getDescription: function (values) {
return ' != ' + values[0];
}
} }
}; };
} }
@ -310,13 +334,16 @@ define([], function () {
validator; validator;
if (cache[object] && typeof cache[object][key] !== 'undefined') { if (cache[object] && typeof cache[object][key] !== 'undefined') {
telemetryValue = [cache[object][key]]; let value = cache[object][key];
telemetryValue = [isNaN(Number(value)) ? value : Number(value)];
} }
op = this.operations[operation] && this.operations[operation].operation; op = this.operations[operation] && this.operations[operation].operation;
input = telemetryValue && telemetryValue.concat(values); input = telemetryValue && telemetryValue.concat(values);
validator = op && this.inputValidators[this.operations[operation].appliesTo[0]]; validator = op && this.inputValidators[this.operations[operation].appliesTo[0]];
if (op && input && validator) { if (op && input && validator) {
if (this.operations[operation].appliesTo.length === 2) { if (this.operations[operation].appliesTo.length > 1) {
return (this.validateNumberInput(input) || this.validateStringInput(input)) && op(input); return (this.validateNumberInput(input) || this.validateStringInput(input)) && op(input);
} else { } else {
return validator(input) && op(input); return validator(input) && op(input);
@ -372,7 +399,7 @@ define([], function () {
}; };
/** /**
* Returns true only of the given operation applies to a given type * Returns true only if the given operation applies to a given type
* @param {string} key The key of the operation * @param {string} key The key of the operation
* @param {string} type The value type to query * @param {string} type The value type to query
* @returns {boolean} True if the condition applies, false otherwise * @returns {boolean} True if the condition applies, false otherwise

View File

@ -130,7 +130,9 @@ define ([
this.telemetryTypesById[objectId] = {}; this.telemetryTypesById[objectId] = {};
Object.values(this.telemetryMetadataById[objectId]).forEach(function (valueMetadata) { Object.values(this.telemetryMetadataById[objectId]).forEach(function (valueMetadata) {
var type; var type;
if (valueMetadata.hints.hasOwnProperty('range')) { if (valueMetadata.enumerations !== undefined) {
type = 'enum';
} else if (valueMetadata.hints.hasOwnProperty('range')) {
type = 'number'; type = 'number';
} else if (valueMetadata.hints.hasOwnProperty('domain')) { } else if (valueMetadata.hints.hasOwnProperty('domain')) {
type = 'number'; type = 'number';
@ -163,11 +165,18 @@ define ([
* @param {datum} datum The new data from the telemetry source * @param {datum} datum The new data from the telemetry source
* @private * @private
*/ */
ConditionManager.prototype.handleSubscriptionCallback = function (objId, datum) { ConditionManager.prototype.handleSubscriptionCallback = function (objId, telemetryDatum) {
this.subscriptionCache[objId] = datum; this.subscriptionCache[objId] = this.createNormalizedDatum(objId, telemetryDatum);
this.eventEmitter.emit('receiveTelemetry'); this.eventEmitter.emit('receiveTelemetry');
}; };
ConditionManager.prototype.createNormalizedDatum = function (objId, telemetryDatum) {
return Object.values(this.telemetryMetadataById[objId]).reduce((normalizedDatum, metadatum) => {
normalizedDatum[metadatum.key] = telemetryDatum[metadatum.source];
return normalizedDatum;
}, {});
};
/** /**
* Event handler for an add event in this Summary Widget's composition. * Event handler for an add event in this Summary Widget's composition.
* Sets up subscription handlers and parses its property types. * Sets up subscription handlers and parses its property types.
@ -236,6 +245,7 @@ define ([
id.namespace === identifier.namespace; id.namespace === identifier.namespace;
}); });
delete this.compositionObjs[objectId]; delete this.compositionObjs[objectId];
delete this.subscriptionCache[objectId];
this.subscriptions[objectId](); //unsubscribe from telemetry source this.subscriptions[objectId](); //unsubscribe from telemetry source
delete this.subscriptions[objectId]; delete this.subscriptions[objectId];
this.eventEmitter.emit('remove', identifier); this.eventEmitter.emit('remove', identifier);

View File

@ -110,9 +110,11 @@ define([
type = self.manager.getTelemetryPropertyType(self.config.object, key); type = self.manager.getTelemetryPropertyType(self.config.object, key);
self.operationKeys = operations.filter(function (operation) { if (type !== undefined) {
return self.evaluator.operationAppliesTo(operation, type); self.operationKeys = operations.filter(function (operation) {
}); return self.evaluator.operationAppliesTo(operation, type);
});
}
}; };
OperationSelect.prototype.destroy = function () { OperationSelect.prototype.destroy = function () {

View File

@ -174,7 +174,7 @@ define([
return typeof input[0] === 'undefined'; return typeof input[0] === 'undefined';
}, },
text: 'is undefined', text: 'is undefined',
appliesTo: ['string', 'number'], appliesTo: ['string', 'number', 'enum'],
inputCount: 0, inputCount: 0,
getDescription: function () { getDescription: function () {
return ' is undefined'; return ' is undefined';
@ -185,11 +185,33 @@ define([
return typeof input[0] !== 'undefined'; return typeof input[0] !== 'undefined';
}, },
text: 'is defined', text: 'is defined',
appliesTo: ['string', 'number'], appliesTo: ['string', 'number', 'enum'],
inputCount: 0, inputCount: 0,
getDescription: function () { getDescription: function () {
return ' is defined'; return ' is defined';
} }
},
enumValueIs: {
operation: function (input) {
return input[0] === input[1];
},
text: 'is',
appliesTo: ['enum'],
inputCount: 1,
getDescription: function (values) {
return ' == ' + values[0];
}
},
enumValueIsNot: {
operation: function (input) {
return input[0] !== input[1];
},
text: 'is not',
appliesTo: ['enum'],
inputCount: 1,
getDescription: function (values) {
return ' != ' + values[0];
}
} }
}; };