[NIFI-9423-NIFI-9429]: Show icon and tooltip for Parameters with leading and/or trailing whitespace (#5569)

* NIFI-9423 - Show icon and tooltip for Parameter values that have leading and/or trailing whitespaces

NIFI-9429 - Parameters should allow blank values that are non-null (only whitespace)

* - Update areas to clean up tooltips in parameter values
- Show whitespaces and ellipsis in parameter and property values and tooltips
- Update serializeValue to accommodate for blank values

* - Address review findings

* - Remove commented out code

* - Add multiline check for ellipsis

* NIFI-9459 - Empty string checked will disable Edit Parameter value field on dialog open

* - Add multi-line style to parameter and property table

* - Safely insert title attribute content

* - Fix Edit Parameter bug that clears textarea for sensitive and empty string values on dialog open

This closes #5569
This commit is contained in:
M Tien 2021-12-13 13:01:42 -08:00 committed by GitHub
parent b5414ab195
commit 3d5f357de8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 113 additions and 26 deletions

View File

@ -53,6 +53,20 @@ div.context-menu-provenance {
overflow: hidden;
}
.ellipsis-white-space-pre {
white-space: pre;
overflow: hidden;
text-overflow: ellipsis;
}
/* max 1 line text with ellipsis - works in Chrome and Firefox */
.multi-line-clamp-ellipsis {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
}
.ellipsis.multiline {
white-space: normal;
}

View File

@ -1292,7 +1292,7 @@
// function for formatting the property value
var valueFormatter = function (row, cell, value, columnDef, dataContext) {
var valueMarkup;
var nameWidthOffset = 0;
var valueWidthOffset = 0;
if (nfCommon.isDefinedAndNotNull(value)) {
// get the property descriptor
var descriptors = table.data('descriptors');
@ -1323,14 +1323,20 @@
if (!resolvedAllowableValue && nfCommon.isDefinedAndNotNull(propertyDescriptor.identifiesControllerService)) {
valueMarkup = '<span class="table-cell blank">Incompatible Controller Service Configured</div>';
} else {
valueMarkup = '<div class="table-cell value"><pre class="ellipsis">' + nfCommon.escapeHtml(value) + '</pre></div>';
valueWidthOffset = 10;
// add a tooltip icon for trailing and/or leading whitespace
if (nfCommon.hasLeadTrailWhitespace(value)) {
valueMarkup += '<div class="fa fa-info" alt="Info" style="float: right;"></div>';
nameWidthOffset = 20; // 10 + icon width (10)
// check for multi-line
if (nfCommon.isMultiLine(value)) {
valueMarkup = '<div class="table-cell value"><div class="ellipsis-white-space-pre multi-line-clamp-ellipsis">' + nfCommon.escapeHtml(value) + '</div></div>';
} else {
valueMarkup = '<div class="table-cell value"><div class="ellipsis-white-space-pre">' + nfCommon.escapeHtml(value) + '</div></div>';
}
// check for leading or trailing whitespace
if (nfCommon.hasLeadTrailWhitespace(value)) {
valueMarkup += '<div class="fa fa-info" alt="Info" style="float: right;"></div>';
valueWidthOffset = 20;
}
}
}
}
@ -1343,7 +1349,8 @@
if (dataContext.type === 'required') {
content.addClass('required');
}
content.find('.ellipsis').width(columnDef.width - 10 - nameWidthOffset).ellipsis();
var contentValue = content.find('.ellipsis-white-space-pre');
contentValue.attr('title', contentValue.text()).width(columnDef.width - 10 - valueWidthOffset);
// return the appropriate markup
return $('<div />').append(content).html();
@ -1789,13 +1796,11 @@
if (whitespaceIcon.length && !whitespaceIcon.data('qtip')) {
var whitespaceTooltip = nfCommon.formatWhitespaceTooltip();
if (nfCommon.isDefinedAndNotNull(whitespaceTooltip)) {
whitespaceIcon.qtip($.extend({},
nfCommon.config.tooltipConfig,
{
content: whitespaceTooltip
}));
}
whitespaceIcon.qtip($.extend({},
nfCommon.config.tooltipConfig,
{
content: whitespaceTooltip
}));
}
});
};

View File

@ -196,6 +196,9 @@
* Reset the dialog.
*/
var resetDialog = function () {
// clean up any tooltips that may have been generated
nfCommon.cleanUpTooltips($('#parameter-table'), 'div.fa-question-circle, div.fa-info');
$('#parameter-context-name').val('');
$('#parameter-context-name-read-only').text('');
@ -227,9 +230,6 @@
// reset the current parameter context
currentParameterContextEntity = null;
// clean up any tooltips that may have been generated
nfCommon.cleanUpTooltips($('#parameter-table'), 'div.fa-question-circle');
};
/**
@ -861,6 +861,9 @@
parameterContext: parameterContext
});
// clean up any tooltips that may have been generated
nfCommon.cleanUpTooltips($('#parameter-table'), 'div.fa-question-circle, div.fa-info');
if (_.isNil(param.id)) {
var matchingParameter = _.find(parameterData.getItems(), {name: parameter.name});
if (_.isNil(matchingParameter)) {
@ -993,13 +996,13 @@
var serializedValue;
var value = input.val();
if (!isChecked && _.isEmpty(value)) {
if (!isChecked && value === '') {
value = null;
}
var hasChanged = parameter.value !== value;
if (!nfCommon.isBlank(value)) {
if (nfCommon.isDefinedAndNotNull(value)) {
// if the value is sensitive and the user has not made a change
if (!_.isEmpty(parameter) && parameter.sensitive === true && input.hasClass('sensitive') && parameter.isNew === false) {
serializedValue = parameter.previousValue;
@ -1007,7 +1010,6 @@
} else {
// value is not sensitive or it is sensitive and the user has changed it then always take the current value
serializedValue = value;
// if the param is sensitive and the param value has not "changed", that means it matches the mask and it should still be considered changed
if (!hasChanged && !_.isEmpty(parameter) && parameter.sensitive === true && parameter.isNew === false) {
hasChanged = true;
@ -1061,6 +1063,9 @@
parameterContext: originalParameter.parameterContext
});
// clean up any tooltips that may have been generated
nfCommon.cleanUpTooltips($('#parameter-table'), 'div.fa-question-circle, div.fa-info');
// update row for the parameter
parameterData.updateItem(originalParameter.id, parameter);
@ -1129,6 +1134,9 @@
* @returns {*}
*/
var updateParameterContext = function (parameterContextEntity) {
// clean up any tooltips that may have been generated
nfCommon.cleanUpTooltips($('#parameter-table'), 'div.fa-question-circle, div.fa-info');
var parameters = marshalParameters();
var inheritedParameterContexts = marshalInheritedParameterContexts();
@ -1831,6 +1839,7 @@
};
var valueFormatter = function (row, cell, value, columnDef, dataContext) {
var valueWidthOffset = 0;
if (dataContext.sensitive === true && !_.isNil(value)) {
return '<span class="table-cell sensitive">Sensitive value set</span>';
} else if (value === '') {
@ -1838,7 +1847,28 @@
} else if (_.isNil(value)) {
return '<span class="unset">No value set</span>';
} else {
return nfCommon.escapeHtml(value);
var valueMarkup;
valueWidthOffset = 15;
// check for multi-line
if (nfCommon.isMultiLine(value)) {
valueMarkup = '<div class="table-cell value"><div class="ellipsis-white-space-pre multi-line-clamp-ellipsis">' + nfCommon.escapeHtml(value) + '</div></div>';
} else {
valueMarkup = '<div class="table-cell value"><div class="ellipsis-white-space-pre">' + nfCommon.escapeHtml(value) + '</div></div>';
}
// check for leading or trailing whitespace
if (nfCommon.hasLeadTrailWhitespace(value)) {
valueMarkup += '<div class="fa fa-info" alt="Info" style="float: right;"></div>';
valueWidthOffset = 30;
}
// adjust the width accordingly
var content = $(valueMarkup);
var contentValue = content.find('.ellipsis-white-space-pre');
contentValue.attr('title', contentValue.text()).width(columnDef.width - valueWidthOffset);
return $('<div />').append(content).html();
}
};
@ -1922,6 +1952,9 @@
// determine the desired action
if (target.hasClass('delete-parameter')) {
// clean any tooltips that may have been added for this item
nfCommon.cleanUpTooltips($('#parameter-table'), 'div.fa-question-circle, div.fa-info');
if (!parameter.isNew) {
// mark the parameter in question for removal and refresh the table
parameterData.updateItem(parameter.id, $.extend(parameter, {
@ -1978,7 +2011,10 @@
$('#parameter-sensitive-radio-button').prop('disabled', true);
$('#parameter-not-sensitive-radio-button').prop('disabled', true);
if (parameter.value === '') {
$('#parameter-set-empty-string-field').removeClass('checkbox-unchecked').addClass('checkbox-checked');
if (!parameter.sensitive) {
$('#parameter-set-empty-string-field').removeClass('checkbox-unchecked').addClass('checkbox-checked');
$('#parameter-value-field').prop('disabled', true);
}
} else {
$('#parameter-set-empty-string-field').removeClass('checkbox-checked').addClass('checkbox-unchecked');
}
@ -1988,6 +2024,7 @@
$('#parameter-not-sensitive-radio-button').prop('checked', false);
if (!_.isNil(parameter.value)) {
$('#parameter-value-field').addClass('sensitive').val(nfCommon.config.sensitiveText).select();
$('#parameter-set-empty-string-field').removeClass('checkbox-checked').addClass('checkbox-unchecked');
}
} else {
$('#parameter-sensitive-radio-button').prop('checked', false);
@ -2124,6 +2161,17 @@
}));
}
}
var whitespaceIcon = $(this).find('div.fa-info');
if (whitespaceIcon.length && !whitespaceIcon.data('qtip')) {
var whitespaceTooltip = nfCommon.formatWhitespaceTooltip();
whitespaceIcon.qtip($.extend({},
nfCommon.config.tooltipConfig,
{
content: whitespaceTooltip
}));
}
});
};
@ -2202,6 +2250,8 @@
$('#add-parameter').on('click', function () {
var closeHandler = function () {
// clean up any tooltips that may have been generated
nfCommon.cleanUpTooltips($('#parameter-table'), 'div.fa-question-circle, div.fa-info');
resetParameterDialog();
};

View File

@ -1008,16 +1008,20 @@
},
/**
* Checks the specified value for leading and/or trailing whitespace.
* Constant regex for leading and/or trailing whitespace.
*/
LEAD_TRAIL_WHITE_SPACE_REGEX: /^[ \s]+|[ \s]+$/,
/**
* Checks the specified value for leading and/or trailing whitespace only.
*
* @argument {string} value The value to check
*/
hasLeadTrailWhitespace : function (value) {
if ( !value || value.trim().length === 0 ) {
if (nfCommon.isBlank(value)) {
return false;
}
var leadOrTrailWhitespaceRegex = /^[ \s]+|[ \s]+$/;
return leadOrTrailWhitespaceRegex.test(value);
return nfCommon.LEAD_TRAIL_WHITE_SPACE_REGEX.test(value);
},
/**
@ -1817,6 +1821,20 @@
return key.split('.').reduce(function(o,x){
return(typeof o === undefined || o === null)? o : (typeof o[x] == 'function')?o[x]():o[x];
}, obj);
},
/**
* Checks if the given value has multi-lines.
*
* @param value to check
* @returns {boolean}
*/
isMultiLine: function (value) {
const multiLineMatcher = /\n/.exec(value);
if (multiLineMatcher) {
return true;
}
return false;
}
};