From 170ba7cf9de1a450c60aa78022be28f6c63922ea Mon Sep 17 00:00:00 2001 From: Conglei Date: Thu, 12 Sep 2019 13:11:08 -0700 Subject: [PATCH] feat(datatable): render html correctly (#199) * feat(datatable): render html correctly n * fix(sanitize string): sanitize string before parsing n --- .../package.json | 4 ++- .../src/Table.tsx | 30 ++++++++++++++++++- .../src/renderer.tsx | 8 +++-- .../stories/plugin-chart-table/Stories.jsx | 2 +- .../stories/plugin-chart-table/bigData.js | 3 +- 5 files changed, 41 insertions(+), 6 deletions(-) diff --git a/packages/superset-ui-plugin-chart-table/package.json b/packages/superset-ui-plugin-chart-table/package.json index bc2b0fbf3..cfa89d28a 100644 --- a/packages/superset-ui-plugin-chart-table/package.json +++ b/packages/superset-ui-plugin-chart-table/package.json @@ -27,7 +27,9 @@ }, "dependencies": { "@airbnb/lunar": "^2.16.0", - "@airbnb/lunar-icons": "^2.1.4" + "@airbnb/lunar-icons": "^2.1.4", + "@types/dompurify": "^0.0.33", + "dompurify": "^1.0.11" }, "peerDependencies": { "@superset-ui/chart": "^0.12.0", diff --git a/packages/superset-ui-plugin-chart-table/src/Table.tsx b/packages/superset-ui-plugin-chart-table/src/Table.tsx index ed46a9cc1..c92663a32 100644 --- a/packages/superset-ui-plugin-chart-table/src/Table.tsx +++ b/packages/superset-ui-plugin-chart-table/src/Table.tsx @@ -4,6 +4,7 @@ import Text from '@airbnb/lunar/lib/components/Text'; import Input from '@airbnb/lunar/lib/components/Input'; import withStyles, { WithStylesProps } from '@airbnb/lunar/lib/composers/withStyles'; import { Renderers, ParentRow, ColumnMetadata } from '@airbnb/lunar/lib/components/DataTable/types'; +import dompurify from 'dompurify'; import { getRenderer, ColumnType, heightType, Cell } from './renderer'; type Props = { @@ -58,6 +59,18 @@ function getCellHash(cell: Cell) { return `${cell.key}#${cell.value}`; } +function getText(value: string | number) { + if (typeof value === 'string') { + const span = document.createElement('span'); + const sanitizedString = dompurify.sanitize(value); + span.innerHTML = sanitizedString; + + return String(span.textContent || span.innerText); + } + + return String(value); +} + class TableVis extends React.PureComponent { static defaultProps = defaultProps; @@ -187,7 +200,7 @@ class TableVis extends React.PureComponent { const keys = dataToRender && dataToRender.length > 0 ? Object.keys(dataToRender[0].data) : []; let calculatedWidth = 0; keys.forEach(key => { - const maxLength = Math.max(...data.map(d => String(d.data[key]).length), key.length); + const maxLength = Math.max(...data.map(d => getText(d.data[key]).length), key.length); const stringWidth = maxLength * CHAR_WIDTH + CELL_PADDING; columnMetadata[key] = { maxWidth: MAX_COLUMN_WIDTH, @@ -195,6 +208,21 @@ class TableVis extends React.PureComponent { ...columnMetadata[key], }; calculatedWidth += Math.min(stringWidth, MAX_COLUMN_WIDTH); + + if (!renderers[key]) { + renderers[key] = getRenderer({ + alignPositiveNegative, + colorPositiveNegative, + column: { + key, + label: key, + type: 'string', + }, + enableFilter: tableFilter, + handleCellSelected: this.handleCellSelected, + isSelected: this.isSelected, + }); + } }); const tableHeight = includeSearch ? height - SEARCH_BAR_HEIGHT : height; diff --git a/packages/superset-ui-plugin-chart-table/src/renderer.tsx b/packages/superset-ui-plugin-chart-table/src/renderer.tsx index a91b558b6..ba311759b 100644 --- a/packages/superset-ui-plugin-chart-table/src/renderer.tsx +++ b/packages/superset-ui-plugin-chart-table/src/renderer.tsx @@ -1,9 +1,11 @@ +/* eslint-disable complexity */ /* eslint-disable jsx-a11y/no-static-element-interactions */ /* eslint-disable jsx-a11y/click-events-have-key-events */ /* eslint-disable no-magic-numbers */ import React, { CSSProperties } from 'react'; import { HEIGHT_TO_PX } from '@airbnb/lunar/lib/components/DataTable/constants'; import { RendererProps } from '@airbnb/lunar/lib/components/DataTable/types'; +import Interweave from '@airbnb/lunar/lib/components/Interweave'; const NEGATIVE_COLOR = '#FFA8A8'; const POSITIVE_COLOR = '#ced4da'; @@ -51,10 +53,12 @@ export const getRenderer = ({ const isMetric = column.type === 'metric'; let Parent; + const cursorStyle = enableFilter && !isMetric ? 'pointer' : 'default'; + const boxStyle: CSSProperties = { backgroundColor: enableFilter && isSelected({ key: keyName, value }) ? SELECTION_COLOR : undefined, - cursor: isMetric ? 'default' : 'pointer', + cursor: cursorStyle, margin: '0px -16px', }; @@ -123,7 +127,7 @@ export const getRenderer = ({ }) } > - {column.format ? column.format(value) : value} + {column.format ? column.format(value) : } ); }; diff --git a/packages/superset-ui-plugins-demo/storybook/stories/plugin-chart-table/Stories.jsx b/packages/superset-ui-plugins-demo/storybook/stories/plugin-chart-table/Stories.jsx index 1ab6a1d6b..b2e1a8fbc 100644 --- a/packages/superset-ui-plugins-demo/storybook/stories/plugin-chart-table/Stories.jsx +++ b/packages/superset-ui-plugins-demo/storybook/stories/plugin-chart-table/Stories.jsx @@ -113,7 +113,7 @@ export default [ orderDesc: true, pageLength: 0, percentMetrics: [], - tableFilter: true, + tableFilter: false, tableTimestampFormat: '%Y-%m-%d %H:%M:%S', timeseriesLimitMetric: null, }} diff --git a/packages/superset-ui-plugins-demo/storybook/stories/plugin-chart-table/bigData.js b/packages/superset-ui-plugins-demo/storybook/stories/plugin-chart-table/bigData.js index b3f6845b1..98a854c2c 100644 --- a/packages/superset-ui-plugins-demo/storybook/stories/plugin-chart-table/bigData.js +++ b/packages/superset-ui-plugins-demo/storybook/stories/plugin-chart-table/bigData.js @@ -4,7 +4,7 @@ const ROW_COUNT = 30; const COLUMN_COUNT = 20; -export const keys = ['ds'].concat( +export const keys = ['ds', 'html'].concat( Array(COLUMN_COUNT) .fill(0) .map((_, i) => `clm ${i}`), @@ -17,6 +17,7 @@ keys.forEach((key, i) => { .join(''); }); item.ds = '2019-09-09'; +item.html = 'Link Test'; export default Array(ROW_COUNT) .fill(0)