feat: optimize HTML text extraction in TableWidgetV2 (#38153)
This commit is contained in:
parent
d52490cabb
commit
dcb27d2c87
|
|
@ -183,6 +183,46 @@ describe("HTML columns", () => {
|
|||
delete input.searchText;
|
||||
});
|
||||
|
||||
it("validate search works when a javascript object is sent in HTMLcolumn", () => {
|
||||
const jsObjectInput = _.cloneDeep(input);
|
||||
|
||||
jsObjectInput.processedTableData[0].status = {
|
||||
color: "yellow",
|
||||
text: "Adventure",
|
||||
};
|
||||
jsObjectInput.searchText = "Adventure";
|
||||
const expected = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Jim Doe",
|
||||
status: {
|
||||
color: "yellow",
|
||||
text: "Adventure",
|
||||
},
|
||||
__originalIndex__: 0,
|
||||
},
|
||||
];
|
||||
|
||||
let result = getFilteredTableData(jsObjectInput, moment, _);
|
||||
|
||||
expect(result).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
it("validate search does not filter based on html attributes", () => {
|
||||
input.searchText = "span";
|
||||
const expected = [];
|
||||
|
||||
let result = getFilteredTableData(input, moment, _);
|
||||
|
||||
expect(result).toStrictEqual(expected);
|
||||
|
||||
input.searchText = "color";
|
||||
result = getFilteredTableData(input, moment, _);
|
||||
|
||||
expect(result).toStrictEqual(expected);
|
||||
delete input.searchText;
|
||||
});
|
||||
|
||||
it("validates filters on table for HTML columns", () => {
|
||||
input.filters = [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -284,13 +284,46 @@ export default {
|
|||
const getTextFromHTML = (html) => {
|
||||
if (!html) return "";
|
||||
|
||||
if (typeof html === "object") {
|
||||
html = JSON.stringify(html);
|
||||
}
|
||||
|
||||
try {
|
||||
const tempDiv = document.createElement("div");
|
||||
|
||||
tempDiv.innerHTML = html;
|
||||
|
||||
return tempDiv.textContent || tempDiv.innerText || "";
|
||||
} catch (e) {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Since getTextFromHTML is an expensive operation, we need to avoid calling it unnecessarily
|
||||
* This optimization ensures that getTextFromHTML is only called when required
|
||||
*/
|
||||
const columnsWithHTML = Object.values(props.primaryColumns).filter(
|
||||
(column) => column.columnType === "html",
|
||||
);
|
||||
const htmlColumnAliases = new Set(
|
||||
columnsWithHTML.map((column) => column.alias),
|
||||
);
|
||||
|
||||
const isFilteringByColumnThatHasHTML = props.filters?.some((filter) =>
|
||||
htmlColumnAliases.has(filter.column),
|
||||
);
|
||||
const isSortingByColumnThatHasHTML =
|
||||
props.sortOrder?.column && htmlColumnAliases.has(props.sortOrder.column);
|
||||
|
||||
const shouldExtractHTMLText = !!(
|
||||
props.searchText ||
|
||||
isFilteringByColumnThatHasHTML ||
|
||||
isSortingByColumnThatHasHTML
|
||||
);
|
||||
const getKeyForExtractedTextFromHTML = (columnAlias) =>
|
||||
`__htmlExtractedText_${columnAlias}__`;
|
||||
|
||||
/* extend processedTableData with values from
|
||||
* - computedValues, in case of normal column
|
||||
* - empty values, in case of derived column
|
||||
|
|
@ -325,6 +358,12 @@ export default {
|
|||
...processedTableData[index],
|
||||
[column.alias]: computedValue,
|
||||
};
|
||||
|
||||
if (shouldExtractHTMLText && column.columnType === "html") {
|
||||
processedTableData[index][
|
||||
getKeyForExtractedTextFromHTML(column.alias)
|
||||
] = getTextFromHTML(computedValue);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -514,11 +553,23 @@ export default {
|
|||
);
|
||||
}
|
||||
}
|
||||
case "html":
|
||||
case "html": {
|
||||
const htmlExtractedTextA =
|
||||
processedA[
|
||||
getKeyForExtractedTextFromHTML(sortByColumnOriginalId)
|
||||
];
|
||||
const htmlExtractedTextB =
|
||||
processedB[
|
||||
getKeyForExtractedTextFromHTML(sortByColumnOriginalId)
|
||||
];
|
||||
|
||||
return sortByOrder(
|
||||
getTextFromHTML(processedA[sortByColumnOriginalId]) >
|
||||
getTextFromHTML(processedB[sortByColumnOriginalId]),
|
||||
(htmlExtractedTextA ??
|
||||
getTextFromHTML(processedA[sortByColumnOriginalId])) >
|
||||
(htmlExtractedTextB ??
|
||||
getTextFromHTML(processedB[sortByColumnOriginalId])),
|
||||
);
|
||||
}
|
||||
default:
|
||||
return sortByOrder(
|
||||
processedA[sortByColumnOriginalId].toString().toLowerCase() >
|
||||
|
|
@ -715,10 +766,6 @@ export default {
|
|||
(column) => column.columnType === "url" && column.displayText,
|
||||
);
|
||||
|
||||
const columnsWithHTML = Object.values(props.primaryColumns).filter(
|
||||
(column) => column.columnType === "html",
|
||||
);
|
||||
|
||||
/*
|
||||
* For select columns with label and values, we need to include the label value
|
||||
* in the search and filter data
|
||||
|
|
@ -814,17 +861,23 @@ export default {
|
|||
return acc;
|
||||
}, {});
|
||||
|
||||
let htmlValues = {};
|
||||
|
||||
/*
|
||||
* We don't want html tags and inline styles to match in search
|
||||
*/
|
||||
const htmlValues = columnsWithHTML.reduce((acc, column) => {
|
||||
if (shouldExtractHTMLText) {
|
||||
htmlValues = columnsWithHTML.reduce((acc, column) => {
|
||||
const value = row[column.alias];
|
||||
|
||||
acc[column.alias] =
|
||||
value === null || value === undefined ? "" : getTextFromHTML(value);
|
||||
acc[column.alias] = _.isNil(value)
|
||||
? ""
|
||||
: row[getKeyForExtractedTextFromHTML(column.alias)] ??
|
||||
getTextFromHTML(value);
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
const displayedRow = {
|
||||
...row,
|
||||
|
|
@ -832,13 +885,12 @@ export default {
|
|||
...displayTextValues,
|
||||
...htmlValues,
|
||||
};
|
||||
const htmlColumns = columnsWithHTML.map((column) => column.alias);
|
||||
|
||||
if (searchKey) {
|
||||
const combinedRowContent = [
|
||||
...Object.values(_.omit(displayedRow, hiddenColumns)),
|
||||
...Object.values(
|
||||
_.omit(originalRow, [...hiddenColumns, ...htmlColumns]),
|
||||
_.omit(originalRow, [...hiddenColumns, ...htmlColumnAliases]),
|
||||
),
|
||||
]
|
||||
.join(", ")
|
||||
|
|
@ -875,12 +927,16 @@ export default {
|
|||
/*
|
||||
* We don't want html tags and inline styles to match in filter conditions
|
||||
*/
|
||||
const isHTMLColumn = htmlColumns.includes(props.filters[i].column);
|
||||
const isHTMLColumn = htmlColumnAliases.has(props.filters[i].column);
|
||||
const originalColValue = isHTMLColumn
|
||||
? getTextFromHTML(originalRow[props.filters[i].column])
|
||||
? originalRow[
|
||||
getKeyForExtractedTextFromHTML(props.filters[i].column)
|
||||
] ?? getTextFromHTML(originalRow[props.filters[i].column])
|
||||
: originalRow[props.filters[i].column];
|
||||
const displayedColValue = isHTMLColumn
|
||||
? getTextFromHTML(displayedRow[props.filters[i].column])
|
||||
? displayedRow[
|
||||
getKeyForExtractedTextFromHTML(props.filters[i].column)
|
||||
] ?? getTextFromHTML(displayedRow[props.filters[i].column])
|
||||
: displayedRow[props.filters[i].column];
|
||||
|
||||
filterResult =
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user