Replace lodash templates with static react renderer for field formatters (#96048)

This commit is contained in:
Larry Gregory 2021-04-02 23:26:35 -04:00 committed by GitHub
parent 8ccb0d4ca3
commit 7d0920bbfa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 62 additions and 43 deletions

View file

@ -28,10 +28,10 @@ describe('Color Format', () => {
expect(colorer.convert(99, HTML_CONTEXT_TYPE)).toBe('<span ng-non-bindable>99</span>');
expect(colorer.convert(100, HTML_CONTEXT_TYPE)).toBe(
'<span ng-non-bindable><span style="color: blue;background-color: yellow;">100</span></span>'
'<span ng-non-bindable><span style="color:blue;background-color:yellow">100</span></span>'
);
expect(colorer.convert(150, HTML_CONTEXT_TYPE)).toBe(
'<span ng-non-bindable><span style="color: blue;background-color: yellow;">150</span></span>'
'<span ng-non-bindable><span style="color:blue;background-color:yellow">150</span></span>'
);
expect(colorer.convert(151, HTML_CONTEXT_TYPE)).toBe('<span ng-non-bindable>151</span>');
});
@ -74,22 +74,22 @@ describe('Color Format', () => {
expect(converter('B', HTML_CONTEXT_TYPE)).toBe('<span ng-non-bindable>B</span>');
expect(converter('AAA', HTML_CONTEXT_TYPE)).toBe(
'<span ng-non-bindable><span style="color: blue;background-color: yellow;">AAA</span></span>'
'<span ng-non-bindable><span style="color:blue;background-color:yellow">AAA</span></span>'
);
expect(converter('AB', HTML_CONTEXT_TYPE)).toBe(
'<span ng-non-bindable><span style="color: blue;background-color: yellow;">AB</span></span>'
'<span ng-non-bindable><span style="color:blue;background-color:yellow">AB</span></span>'
);
expect(converter('a', HTML_CONTEXT_TYPE)).toBe('<span ng-non-bindable>a</span>');
expect(converter('B', HTML_CONTEXT_TYPE)).toBe('<span ng-non-bindable>B</span>');
expect(converter('AAA', HTML_CONTEXT_TYPE)).toBe(
'<span ng-non-bindable><span style="color: blue;background-color: yellow;">AAA</span></span>'
'<span ng-non-bindable><span style="color:blue;background-color:yellow">AAA</span></span>'
);
expect(converter('AB', HTML_CONTEXT_TYPE)).toBe(
'<span ng-non-bindable><span style="color: blue;background-color: yellow;">AB</span></span>'
'<span ng-non-bindable><span style="color:blue;background-color:yellow">AB</span></span>'
);
expect(converter('AB <', HTML_CONTEXT_TYPE)).toBe(
'<span ng-non-bindable><span style="color: blue;background-color: yellow;">AB &lt;</span></span>'
'<span ng-non-bindable><span style="color:blue;background-color:yellow">AB &lt;</span></span>'
);
expect(converter('a', HTML_CONTEXT_TYPE)).toBe('<span ng-non-bindable>a</span>');
});

View file

@ -7,15 +7,15 @@
*/
import { i18n } from '@kbn/i18n';
import { findLast, cloneDeep, template, escape } from 'lodash';
import React from 'react';
import ReactDOM from 'react-dom/server';
import { findLast, cloneDeep, escape } from 'lodash';
import { KBN_FIELD_TYPES } from '../../kbn_field_types/types';
import { FieldFormat } from '../field_format';
import { HtmlContextTypeConvert, FIELD_FORMAT_IDS } from '../types';
import { asPrettyString } from '../utils';
import { DEFAULT_CONVERTER_COLOR } from '../constants/color_default';
const convertTemplate = template('<span style="<%- style %>"><%- val %></span>');
export class ColorFormat extends FieldFormat {
static id = FIELD_FORMAT_IDS.COLOR;
static title = i18n.translate('data.fieldFormats.color.title', {
@ -51,11 +51,18 @@ export class ColorFormat extends FieldFormat {
htmlConvert: HtmlContextTypeConvert = (val) => {
const color = this.findColorRuleForVal(val) as typeof DEFAULT_CONVERTER_COLOR;
if (!color) return escape(asPrettyString(val));
let style = '';
if (color.text) style += `color: ${color.text};`;
if (color.background) style += `background-color: ${color.background};`;
return convertTemplate({ val, style });
const displayVal = escape(asPrettyString(val));
if (!color) return displayVal;
return ReactDOM.renderToStaticMarkup(
<span
style={{
color: color.text,
backgroundColor: color.background,
}}
dangerouslySetInnerHTML={{ __html: displayVal }} // eslint-disable-line react/no-danger
/>
);
};
}

View file

@ -9,6 +9,7 @@
import { SourceFormat } from './source';
import { HtmlContextTypeConvert } from '../types';
import { HTML_CONTEXT_TYPE } from '../content_types';
import { stubIndexPatternWithFields } from '../../index_patterns/index_pattern.stub';
describe('Source Format', () => {
let convertHtml: Function;
@ -31,4 +32,19 @@ describe('Source Format', () => {
'<span ng-non-bindable>{&quot;foo&quot;:&quot;bar&quot;,&quot;number&quot;:42,&quot;hello&quot;:&quot;&lt;h1&gt;World&lt;/h1&gt;&quot;,&quot;also&quot;:&quot;with \\&quot;quotes\\&quot; or &#39;single quotes&#39;&quot;}</span>'
);
});
test('should render a description list if a field is passed', () => {
const hit = {
foo: 'bar',
number: 42,
hello: '<h1>World</h1>',
also: 'with "quotes" or \'single quotes\'',
};
const indexPattern = { ...stubIndexPatternWithFields, formatHit: (h: string) => h };
expect(convertHtml(hit, { field: 'field', indexPattern, hit })).toMatchInlineSnapshot(
`"<span ng-non-bindable><dl class=\\"source truncate-by-height\\"><dt>foo:</dt><dd>bar</dd> <dt>number:</dt><dd>42</dd> <dt>hello:</dt><dd><h1>World</h1></dd> <dt>also:</dt><dd>with \\"quotes\\" or 'single quotes'</dd> </dl></span>"`
);
});
});

View file

@ -6,40 +6,34 @@
* Side Public License, v 1.
*/
import { template, escape, keys } from 'lodash';
import React, { Fragment } from 'react';
import ReactDOM from 'react-dom/server';
import { escape, keys } from 'lodash';
import { shortenDottedString } from '../../utils';
import { KBN_FIELD_TYPES } from '../../kbn_field_types/types';
import { FieldFormat } from '../field_format';
import { TextContextTypeConvert, HtmlContextTypeConvert, FIELD_FORMAT_IDS } from '../types';
import { UI_SETTINGS } from '../../constants';
/**
* Remove all of the whitespace between html tags
* so that inline elements don't have extra spaces.
*
* If you have inline elements (span, a, em, etc.) and any
* amount of whitespace around them in your markup, then the
* browser will push them apart. This is ugly in certain
* scenarios and is only fixed by removing the whitespace
* from the html in the first place (or ugly css hacks).
*
* @param {string} html - the html to modify
* @return {string} - modified html
*/
function noWhiteSpace(html: string) {
const TAGS_WITH_WS = />\s+</g;
return html.replace(TAGS_WITH_WS, '><');
interface Props {
defPairs: Array<[string, string]>;
}
const templateHtml = `
<dl class="source truncate-by-height">
<% defPairs.forEach(function (def) { %>
<dt><%- def[0] %>:</dt>
<dd><%= def[1] %></dd>
<%= ' ' %>
<% }); %>
</dl>`;
const doTemplate = template(noWhiteSpace(templateHtml));
const TemplateComponent = ({ defPairs }: Props) => {
return (
<dl className={'source truncate-by-height'}>
{defPairs.map((pair, idx) => (
<Fragment key={idx}>
<dt
dangerouslySetInnerHTML={{ __html: `${escape(pair[0])}:` }} // eslint-disable-line react/no-danger
/>
<dd
dangerouslySetInnerHTML={{ __html: `${pair[1]}` }} // eslint-disable-line react/no-danger
/>{' '}
</Fragment>
))}
</dl>
);
};
export class SourceFormat extends FieldFormat {
static id = FIELD_FORMAT_IDS._SOURCE;
@ -70,6 +64,8 @@ export class SourceFormat extends FieldFormat {
pairs.push([newField, val]);
}, []);
return doTemplate({ defPairs: highlightPairs.concat(sourcePairs) });
return ReactDOM.renderToStaticMarkup(
<TemplateComponent defPairs={highlightPairs.concat(sourcePairs)} />
);
};
}