Replace lodash templates with static react renderer for field formatters (#96048)
This commit is contained in:
parent
8ccb0d4ca3
commit
7d0920bbfa
|
@ -28,10 +28,10 @@ describe('Color Format', () => {
|
||||||
|
|
||||||
expect(colorer.convert(99, HTML_CONTEXT_TYPE)).toBe('<span ng-non-bindable>99</span>');
|
expect(colorer.convert(99, HTML_CONTEXT_TYPE)).toBe('<span ng-non-bindable>99</span>');
|
||||||
expect(colorer.convert(100, HTML_CONTEXT_TYPE)).toBe(
|
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(
|
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>');
|
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('B', HTML_CONTEXT_TYPE)).toBe('<span ng-non-bindable>B</span>');
|
||||||
expect(converter('AAA', HTML_CONTEXT_TYPE)).toBe(
|
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(
|
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('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('B', HTML_CONTEXT_TYPE)).toBe('<span ng-non-bindable>B</span>');
|
||||||
expect(converter('AAA', HTML_CONTEXT_TYPE)).toBe(
|
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(
|
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(
|
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('a', HTML_CONTEXT_TYPE)).toBe('<span ng-non-bindable>a</span>');
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,15 +7,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
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 { KBN_FIELD_TYPES } from '../../kbn_field_types/types';
|
||||||
import { FieldFormat } from '../field_format';
|
import { FieldFormat } from '../field_format';
|
||||||
import { HtmlContextTypeConvert, FIELD_FORMAT_IDS } from '../types';
|
import { HtmlContextTypeConvert, FIELD_FORMAT_IDS } from '../types';
|
||||||
import { asPrettyString } from '../utils';
|
import { asPrettyString } from '../utils';
|
||||||
import { DEFAULT_CONVERTER_COLOR } from '../constants/color_default';
|
import { DEFAULT_CONVERTER_COLOR } from '../constants/color_default';
|
||||||
|
|
||||||
const convertTemplate = template('<span style="<%- style %>"><%- val %></span>');
|
|
||||||
|
|
||||||
export class ColorFormat extends FieldFormat {
|
export class ColorFormat extends FieldFormat {
|
||||||
static id = FIELD_FORMAT_IDS.COLOR;
|
static id = FIELD_FORMAT_IDS.COLOR;
|
||||||
static title = i18n.translate('data.fieldFormats.color.title', {
|
static title = i18n.translate('data.fieldFormats.color.title', {
|
||||||
|
@ -51,11 +51,18 @@ export class ColorFormat extends FieldFormat {
|
||||||
|
|
||||||
htmlConvert: HtmlContextTypeConvert = (val) => {
|
htmlConvert: HtmlContextTypeConvert = (val) => {
|
||||||
const color = this.findColorRuleForVal(val) as typeof DEFAULT_CONVERTER_COLOR;
|
const color = this.findColorRuleForVal(val) as typeof DEFAULT_CONVERTER_COLOR;
|
||||||
if (!color) return escape(asPrettyString(val));
|
|
||||||
|
|
||||||
let style = '';
|
const displayVal = escape(asPrettyString(val));
|
||||||
if (color.text) style += `color: ${color.text};`;
|
if (!color) return displayVal;
|
||||||
if (color.background) style += `background-color: ${color.background};`;
|
|
||||||
return convertTemplate({ val, style });
|
return ReactDOM.renderToStaticMarkup(
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
color: color.text,
|
||||||
|
backgroundColor: color.background,
|
||||||
|
}}
|
||||||
|
dangerouslySetInnerHTML={{ __html: displayVal }} // eslint-disable-line react/no-danger
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -9,6 +9,7 @@
|
||||||
import { SourceFormat } from './source';
|
import { SourceFormat } from './source';
|
||||||
import { HtmlContextTypeConvert } from '../types';
|
import { HtmlContextTypeConvert } from '../types';
|
||||||
import { HTML_CONTEXT_TYPE } from '../content_types';
|
import { HTML_CONTEXT_TYPE } from '../content_types';
|
||||||
|
import { stubIndexPatternWithFields } from '../../index_patterns/index_pattern.stub';
|
||||||
|
|
||||||
describe('Source Format', () => {
|
describe('Source Format', () => {
|
||||||
let convertHtml: Function;
|
let convertHtml: Function;
|
||||||
|
@ -31,4 +32,19 @@ describe('Source Format', () => {
|
||||||
'<span ng-non-bindable>{"foo":"bar","number":42,"hello":"<h1>World</h1>","also":"with \\"quotes\\" or 'single quotes'"}</span>'
|
'<span ng-non-bindable>{"foo":"bar","number":42,"hello":"<h1>World</h1>","also":"with \\"quotes\\" or 'single quotes'"}</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>"`
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,40 +6,34 @@
|
||||||
* Side Public License, v 1.
|
* 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 { shortenDottedString } from '../../utils';
|
||||||
import { KBN_FIELD_TYPES } from '../../kbn_field_types/types';
|
import { KBN_FIELD_TYPES } from '../../kbn_field_types/types';
|
||||||
import { FieldFormat } from '../field_format';
|
import { FieldFormat } from '../field_format';
|
||||||
import { TextContextTypeConvert, HtmlContextTypeConvert, FIELD_FORMAT_IDS } from '../types';
|
import { TextContextTypeConvert, HtmlContextTypeConvert, FIELD_FORMAT_IDS } from '../types';
|
||||||
import { UI_SETTINGS } from '../../constants';
|
import { UI_SETTINGS } from '../../constants';
|
||||||
|
|
||||||
/**
|
interface Props {
|
||||||
* Remove all of the whitespace between html tags
|
defPairs: Array<[string, string]>;
|
||||||
* 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, '><');
|
|
||||||
}
|
}
|
||||||
|
const TemplateComponent = ({ defPairs }: Props) => {
|
||||||
const templateHtml = `
|
return (
|
||||||
<dl class="source truncate-by-height">
|
<dl className={'source truncate-by-height'}>
|
||||||
<% defPairs.forEach(function (def) { %>
|
{defPairs.map((pair, idx) => (
|
||||||
<dt><%- def[0] %>:</dt>
|
<Fragment key={idx}>
|
||||||
<dd><%= def[1] %></dd>
|
<dt
|
||||||
<%= ' ' %>
|
dangerouslySetInnerHTML={{ __html: `${escape(pair[0])}:` }} // eslint-disable-line react/no-danger
|
||||||
<% }); %>
|
/>
|
||||||
</dl>`;
|
<dd
|
||||||
const doTemplate = template(noWhiteSpace(templateHtml));
|
dangerouslySetInnerHTML={{ __html: `${pair[1]}` }} // eslint-disable-line react/no-danger
|
||||||
|
/>{' '}
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
|
</dl>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export class SourceFormat extends FieldFormat {
|
export class SourceFormat extends FieldFormat {
|
||||||
static id = FIELD_FORMAT_IDS._SOURCE;
|
static id = FIELD_FORMAT_IDS._SOURCE;
|
||||||
|
@ -70,6 +64,8 @@ export class SourceFormat extends FieldFormat {
|
||||||
pairs.push([newField, val]);
|
pairs.push([newField, val]);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return doTemplate({ defPairs: highlightPairs.concat(sourcePairs) });
|
return ReactDOM.renderToStaticMarkup(
|
||||||
|
<TemplateComponent defPairs={highlightPairs.concat(sourcePairs)} />
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
Loading…
Reference in a new issue