From 921a11980447f87acb427bfb46684983bfbcc28b Mon Sep 17 00:00:00 2001 From: Pete Harverson Date: Mon, 23 Nov 2020 17:39:14 +0000 Subject: [PATCH 001/236] [ML] Remove all usage of font awesome icons in ML plugin (#84066) --- .../plugins/ml/public/application/_index.scss | 1 - x-pack/plugins/ml/public/application/app.tsx | 2 - .../field_title_bar/_field_title_bar.scss | 2 +- ...e_bar.test.js => field_title_bar.test.tsx} | 70 +++++++----- ...field_title_bar.js => field_title_bar.tsx} | 31 +++--- .../field_title_bar/{index.js => index.ts} | 0 .../field_type_icon.test.js.snap | 19 ---- .../field_type_icon.test.tsx.snap | 16 +++ .../field_type_icon/_field_type_icon.scss | 22 ---- .../components/field_type_icon/_index.scss | 1 - ..._icon.test.js => field_type_icon.test.tsx} | 25 +---- ...field_type_icon.js => field_type_icon.tsx} | 104 ++++++++++-------- .../field_type_icon/{index.js => index.ts} | 0 .../fields_stats/_field_stats_card.scss | 36 ------ .../fields_stats/field_stats_card.js | 74 ++++++++----- .../field_data_card/_field_data_card.scss | 36 ------ .../field_data_card/field_data_card.tsx | 1 - .../_explorer_charts_container.scss | 15 +-- .../_timeseriesexplorer.scss | 9 +- .../timeseries_chart/timeseries_chart.js | 20 ++-- 20 files changed, 209 insertions(+), 275 deletions(-) rename x-pack/plugins/ml/public/application/components/field_title_bar/{field_title_bar.test.js => field_title_bar.test.tsx} (63%) rename x-pack/plugins/ml/public/application/components/field_title_bar/{field_title_bar.js => field_title_bar.tsx} (68%) rename x-pack/plugins/ml/public/application/components/field_title_bar/{index.js => index.ts} (100%) delete mode 100644 x-pack/plugins/ml/public/application/components/field_type_icon/__snapshots__/field_type_icon.test.js.snap create mode 100644 x-pack/plugins/ml/public/application/components/field_type_icon/__snapshots__/field_type_icon.test.tsx.snap delete mode 100644 x-pack/plugins/ml/public/application/components/field_type_icon/_field_type_icon.scss delete mode 100644 x-pack/plugins/ml/public/application/components/field_type_icon/_index.scss rename x-pack/plugins/ml/public/application/components/field_type_icon/{field_type_icon.test.js => field_type_icon.test.tsx} (63%) rename x-pack/plugins/ml/public/application/components/field_type_icon/{field_type_icon.js => field_type_icon.tsx} (54%) rename x-pack/plugins/ml/public/application/components/field_type_icon/{index.js => index.ts} (100%) diff --git a/x-pack/plugins/ml/public/application/_index.scss b/x-pack/plugins/ml/public/application/_index.scss index 45b14543946c7c..7512c180970ad1 100644 --- a/x-pack/plugins/ml/public/application/_index.scss +++ b/x-pack/plugins/ml/public/application/_index.scss @@ -23,7 +23,6 @@ @import 'components/controls/index'; @import 'components/entity_cell/index'; @import 'components/field_title_bar/index'; - @import 'components/field_type_icon/index'; @import 'components/influencers_list/index'; @import 'components/items_grid/index'; @import 'components/job_selector/index'; diff --git a/x-pack/plugins/ml/public/application/app.tsx b/x-pack/plugins/ml/public/application/app.tsx index 9dc0814e3a3e66..d3a055f957c3aa 100644 --- a/x-pack/plugins/ml/public/application/app.tsx +++ b/x-pack/plugins/ml/public/application/app.tsx @@ -120,8 +120,6 @@ export const renderApp = ( urlGenerators: deps.share.urlGenerators, }); - deps.kibanaLegacy.loadFontAwesome(); - appMountParams.onAppLeave((actions) => actions.default()); const mlLicense = setLicenseCache(deps.licensing, [ diff --git a/x-pack/plugins/ml/public/application/components/field_title_bar/_field_title_bar.scss b/x-pack/plugins/ml/public/application/components/field_title_bar/_field_title_bar.scss index 75118266d45dba..77d95653638aca 100644 --- a/x-pack/plugins/ml/public/application/components/field_title_bar/_field_title_bar.scss +++ b/x-pack/plugins/ml/public/application/components/field_title_bar/_field_title_bar.scss @@ -12,7 +12,7 @@ .field-type-icon { vertical-align: middle; - padding-right: $euiSizeXS; + margin-bottom: -$euiSizeXS; display: inline-block; } diff --git a/x-pack/plugins/ml/public/application/components/field_title_bar/field_title_bar.test.js b/x-pack/plugins/ml/public/application/components/field_title_bar/field_title_bar.test.tsx similarity index 63% rename from x-pack/plugins/ml/public/application/components/field_title_bar/field_title_bar.test.js rename to x-pack/plugins/ml/public/application/components/field_title_bar/field_title_bar.test.tsx index b467b6bfa3654f..aed66550554d3c 100644 --- a/x-pack/plugins/ml/public/application/components/field_title_bar/field_title_bar.test.js +++ b/x-pack/plugins/ml/public/application/components/field_title_bar/field_title_bar.test.tsx @@ -5,26 +5,22 @@ */ import { mountWithIntl } from '@kbn/test/jest'; + import React from 'react'; import { FieldTitleBar } from './field_title_bar'; - -// helper to let PropTypes throw errors instead of just doing console.error() -const error = console.error; -console.error = (warning, ...args) => { - if (/(Invalid prop|Failed prop type)/gi.test(warning)) { - throw new Error(warning); - } - error.apply(console, [warning, ...args]); -}; +import { ML_JOB_FIELD_TYPES } from '../../../../common/constants/field_types'; describe('FieldTitleBar', () => { - test(`throws an error because card is a required prop`, () => { - expect(() => ).toThrow(); - }); - test(`card prop is an empty object`, () => { - const props = { card: {} }; + const props = { + card: { + type: ML_JOB_FIELD_TYPES.NUMBER, + existsInDocs: true, + loading: false, + aggregatable: true, + }, + }; const wrapper = mountWithIntl(); @@ -36,29 +32,43 @@ describe('FieldTitleBar', () => { }); test(`card.isUnsupportedType is true`, () => { - const testFieldName = 'foo'; - const props = { card: { fieldName: testFieldName, isUnsupportedType: true } }; + const props = { + card: { + type: ML_JOB_FIELD_TYPES.UNKNOWN, + fieldName: 'foo', + existsInDocs: true, + loading: false, + aggregatable: true, + isUnsupportedType: true, + }, + }; const wrapper = mountWithIntl(); const fieldName = wrapper.find({ className: 'field-name' }).text(); - expect(fieldName).toEqual(testFieldName); + expect(fieldName).toEqual(props.card.fieldName); const hasClassName = wrapper.find('EuiText').hasClass('type-other'); expect(hasClassName).toBeTruthy(); }); test(`card.fieldName and card.type is set`, () => { - const testFieldName = 'foo'; - const testType = 'bar'; - const props = { card: { fieldName: testFieldName, type: testType } }; + const props = { + card: { + type: ML_JOB_FIELD_TYPES.KEYWORD, + fieldName: 'bar', + existsInDocs: true, + loading: false, + aggregatable: true, + }, + }; const wrapper = mountWithIntl(); const fieldName = wrapper.find({ className: 'field-name' }).text(); - expect(fieldName).toEqual(testFieldName); + expect(fieldName).toEqual(props.card.fieldName); - const hasClassName = wrapper.find('EuiText').hasClass(testType); + const hasClassName = wrapper.find('EuiText').hasClass(props.card.type); expect(hasClassName).toBeTruthy(); }); @@ -66,11 +76,19 @@ describe('FieldTitleBar', () => { // Use fake timers so we don't have to wait for the EuiToolTip timeout jest.useFakeTimers(); - const props = { card: { fieldName: 'foo', type: 'bar' } }; + const props = { + card: { + type: ML_JOB_FIELD_TYPES.KEYWORD, + fieldName: 'bar', + existsInDocs: true, + loading: false, + aggregatable: true, + }, + }; const wrapper = mountWithIntl(); const container = wrapper.find({ className: 'field-name' }); - expect(wrapper.find('EuiToolTip').children()).toHaveLength(1); + expect(wrapper.find('EuiToolTip').children()).toHaveLength(2); container.simulate('mouseover'); @@ -78,7 +96,7 @@ describe('FieldTitleBar', () => { jest.runAllTimers(); wrapper.update(); - expect(wrapper.find('EuiToolTip').children()).toHaveLength(2); + expect(wrapper.find('EuiToolTip').children()).toHaveLength(3); container.simulate('mouseout'); @@ -86,7 +104,7 @@ describe('FieldTitleBar', () => { jest.runAllTimers(); wrapper.update(); - expect(wrapper.find('EuiToolTip').children()).toHaveLength(1); + expect(wrapper.find('EuiToolTip').children()).toHaveLength(2); // Clearing all mocks will also reset fake timers. jest.clearAllMocks(); diff --git a/x-pack/plugins/ml/public/application/components/field_title_bar/field_title_bar.js b/x-pack/plugins/ml/public/application/components/field_title_bar/field_title_bar.tsx similarity index 68% rename from x-pack/plugins/ml/public/application/components/field_title_bar/field_title_bar.js rename to x-pack/plugins/ml/public/application/components/field_title_bar/field_title_bar.tsx index 28aa15c2cfab06..0e98a23637f036 100644 --- a/x-pack/plugins/ml/public/application/components/field_title_bar/field_title_bar.js +++ b/x-pack/plugins/ml/public/application/components/field_title_bar/field_title_bar.tsx @@ -4,21 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import PropTypes from 'prop-types'; -import React from 'react'; +import React, { FC } from 'react'; import { EuiText, EuiToolTip } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + import { FieldTypeIcon } from '../field_type_icon'; +import { FieldVisConfig } from '../../datavisualizer/index_based/common'; import { getMLJobTypeAriaLabel } from '../../util/field_types_utils'; -import { i18n } from '@kbn/i18n'; -export function FieldTitleBar({ card }) { - // don't render and fail gracefully if card prop isn't set - if (typeof card !== 'object' || card === null) { - return null; - } +interface Props { + card: FieldVisConfig; +} +export const FieldTitleBar: FC = ({ card }) => { const fieldName = card.fieldName || i18n.translate('xpack.ml.fieldTitleBar.documentCountLabel', { @@ -37,20 +37,23 @@ export function FieldTitleBar({ card }) { } if (card.isUnsupportedType !== true) { - cardTitleAriaLabel.unshift(getMLJobTypeAriaLabel(card.type)); + // All the supported field types have aria labels. + cardTitleAriaLabel.unshift(getMLJobTypeAriaLabel(card.type)!); } return ( - + -
+
{fieldName}
); -} -FieldTitleBar.propTypes = { - card: PropTypes.object.isRequired, }; diff --git a/x-pack/plugins/ml/public/application/components/field_title_bar/index.js b/x-pack/plugins/ml/public/application/components/field_title_bar/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/components/field_title_bar/index.js rename to x-pack/plugins/ml/public/application/components/field_title_bar/index.ts diff --git a/x-pack/plugins/ml/public/application/components/field_type_icon/__snapshots__/field_type_icon.test.js.snap b/x-pack/plugins/ml/public/application/components/field_type_icon/__snapshots__/field_type_icon.test.js.snap deleted file mode 100644 index 3952d0b090a7dc..00000000000000 --- a/x-pack/plugins/ml/public/application/components/field_type_icon/__snapshots__/field_type_icon.test.js.snap +++ /dev/null @@ -1,19 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`FieldTypeIcon render component when type matches a field type 1`] = ` - -`; - -exports[`FieldTypeIcon update component 1`] = ` - -`; diff --git a/x-pack/plugins/ml/public/application/components/field_type_icon/__snapshots__/field_type_icon.test.tsx.snap b/x-pack/plugins/ml/public/application/components/field_type_icon/__snapshots__/field_type_icon.test.tsx.snap new file mode 100644 index 00000000000000..769ebdeba9955c --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/field_type_icon/__snapshots__/field_type_icon.test.tsx.snap @@ -0,0 +1,16 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FieldTypeIcon render component when type matches a field type 1`] = ` + + + +`; diff --git a/x-pack/plugins/ml/public/application/components/field_type_icon/_field_type_icon.scss b/x-pack/plugins/ml/public/application/components/field_type_icon/_field_type_icon.scss deleted file mode 100644 index 741974c56987e8..00000000000000 --- a/x-pack/plugins/ml/public/application/components/field_type_icon/_field_type_icon.scss +++ /dev/null @@ -1,22 +0,0 @@ -$icon-size: 20px; - -.field-type-icon-container { - display: inline-block !important; - vertical-align: middle; - border: 1px solid; - border-radius: 4px; - width: $icon-size; - height: $icon-size; - line-height: $icon-size; - text-align: center; - position: relative; - - .field-type-icon { - padding: 0; - display: inline-block !important; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - } -} diff --git a/x-pack/plugins/ml/public/application/components/field_type_icon/_index.scss b/x-pack/plugins/ml/public/application/components/field_type_icon/_index.scss deleted file mode 100644 index afd1cb353edb4e..00000000000000 --- a/x-pack/plugins/ml/public/application/components/field_type_icon/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'field_type_icon'; \ No newline at end of file diff --git a/x-pack/plugins/ml/public/application/components/field_type_icon/field_type_icon.test.js b/x-pack/plugins/ml/public/application/components/field_type_icon/field_type_icon.test.tsx similarity index 63% rename from x-pack/plugins/ml/public/application/components/field_type_icon/field_type_icon.test.js rename to x-pack/plugins/ml/public/application/components/field_type_icon/field_type_icon.test.tsx index d4200c2f8366b4..667cca99389cf5 100644 --- a/x-pack/plugins/ml/public/application/components/field_type_icon/field_type_icon.test.js +++ b/x-pack/plugins/ml/public/application/components/field_type_icon/field_type_icon.test.tsx @@ -11,18 +11,10 @@ import { FieldTypeIcon } from './field_type_icon'; import { ML_JOB_FIELD_TYPES } from '../../../../common/constants/field_types'; describe('FieldTypeIcon', () => { - test(`don't render component when type is undefined`, () => { - const typeIconComponent = shallow(); - expect(typeIconComponent.isEmptyRender()).toBeTruthy(); - }); - - test(`don't render component when type doesn't match a field type`, () => { - const typeIconComponent = shallow(); - expect(typeIconComponent.isEmptyRender()).toBeTruthy(); - }); - test(`render component when type matches a field type`, () => { - const typeIconComponent = shallow(); + const typeIconComponent = shallow( + + ); expect(typeIconComponent).toMatchSnapshot(); }); @@ -31,9 +23,9 @@ describe('FieldTypeIcon', () => { jest.useFakeTimers(); const typeIconComponent = mount( - + ); - const container = typeIconComponent.find({ className: 'field-type-icon-container' }); + const container = typeIconComponent.find({ 'data-test-subj': 'mlFieldTypeIcon' }); expect(typeIconComponent.find('EuiToolTip').children()).toHaveLength(1); @@ -56,11 +48,4 @@ describe('FieldTypeIcon', () => { // Clearing all mocks will also reset fake timers. jest.clearAllMocks(); }); - - test(`update component`, () => { - const typeIconComponent = shallow(); - expect(typeIconComponent.isEmptyRender()).toBeTruthy(); - typeIconComponent.setProps({ type: ML_JOB_FIELD_TYPES.IP }); - expect(typeIconComponent).toMatchSnapshot(); - }); }); diff --git a/x-pack/plugins/ml/public/application/components/field_type_icon/field_type_icon.js b/x-pack/plugins/ml/public/application/components/field_type_icon/field_type_icon.tsx similarity index 54% rename from x-pack/plugins/ml/public/application/components/field_type_icon/field_type_icon.js rename to x-pack/plugins/ml/public/application/components/field_type_icon/field_type_icon.tsx index 1853c3d629c3e3..7f736b65d494c5 100644 --- a/x-pack/plugins/ml/public/application/components/field_type_icon/field_type_icon.js +++ b/x-pack/plugins/ml/public/application/components/field_type_icon/field_type_icon.tsx @@ -4,63 +4,80 @@ * you may not use this file except in compliance with the Elastic License. */ -import PropTypes from 'prop-types'; -import React from 'react'; +import React, { FC } from 'react'; -import { EuiToolTip } from '@elastic/eui'; +import { EuiToken, EuiToolTip } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; import { getMLJobTypeAriaLabel } from '../../util/field_types_utils'; import { ML_JOB_FIELD_TYPES } from '../../../../common/constants/field_types'; -import { i18n } from '@kbn/i18n'; -export const FieldTypeIcon = ({ tooltipEnabled = false, type, needsAria = true }) => { - const ariaLabel = getMLJobTypeAriaLabel(type); +interface FieldTypeIconProps { + tooltipEnabled: boolean; + type: ML_JOB_FIELD_TYPES; + fieldName?: string; + needsAria: boolean; +} - if (ariaLabel === null) { - // All ml job field types should have associated aria labels. - // Once it is missing, it means that the passed *type* is not a valid field type. - // if type doesn't match one of ML_JOB_FIELD_TYPES - // don't render the component at all - return null; - } +interface FieldTypeIconContainerProps { + ariaLabel: string | null; + iconType: string; + color: string; + needsAria: boolean; + [key: string]: any; +} - const iconClass = ['field-type-icon']; - let iconChar = ''; +export const FieldTypeIcon: FC = ({ + tooltipEnabled = false, + type, + fieldName, + needsAria = true, +}) => { + const ariaLabel = getMLJobTypeAriaLabel(type); + + let iconType = 'questionInCircle'; + let color = 'euiColorVis6'; switch (type) { - // icon class names + // Set icon types and colors case ML_JOB_FIELD_TYPES.BOOLEAN: - iconClass.push('kuiIcon', 'fa-adjust'); + iconType = 'tokenBoolean'; + color = 'euiColorVis5'; break; case ML_JOB_FIELD_TYPES.DATE: - iconClass.push('kuiIcon', 'fa-clock-o'); + iconType = 'tokenDate'; + color = 'euiColorVis7'; break; case ML_JOB_FIELD_TYPES.GEO_POINT: - iconClass.push('kuiIcon', 'fa-globe'); + iconType = 'tokenGeo'; + color = 'euiColorVis8'; break; case ML_JOB_FIELD_TYPES.TEXT: - iconClass.push('kuiIcon', 'fa-file-text-o'); + iconType = 'document'; + color = 'euiColorVis9'; break; case ML_JOB_FIELD_TYPES.IP: - iconClass.push('kuiIcon', 'fa-laptop'); + iconType = 'tokenIP'; + color = 'euiColorVis3'; break; - - // icon chars case ML_JOB_FIELD_TYPES.KEYWORD: - iconChar = 't'; + iconType = 'tokenText'; + color = 'euiColorVis0'; break; case ML_JOB_FIELD_TYPES.NUMBER: - iconChar = '#'; + iconType = 'tokenNumber'; + color = fieldName !== undefined ? 'euiColorVis1' : 'euiColorVis2'; break; case ML_JOB_FIELD_TYPES.UNKNOWN: - iconChar = '?'; + // Use defaults break; } const containerProps = { ariaLabel, - className: iconClass.join(' '), - iconChar, + iconType, + color, needsAria, }; @@ -84,28 +101,27 @@ export const FieldTypeIcon = ({ tooltipEnabled = false, type, needsAria = true } return ; }; -FieldTypeIcon.propTypes = { - tooltipEnabled: PropTypes.bool, - type: PropTypes.string, -}; - // If the tooltip is used, it will apply its events to its first inner child. // To pass on its properties we apply `rest` to the outer `span` element. -function FieldTypeIconContainer({ ariaLabel, className, iconChar, needsAria, ...rest }) { - const wrapperProps = { className }; +const FieldTypeIconContainer: FC = ({ + ariaLabel, + iconType, + color, + needsAria, + ...rest +}) => { + const wrapperProps: { className: string; 'aria-label'?: string } = { + className: 'field-type-icon', + }; if (needsAria && ariaLabel) { wrapperProps['aria-label'] = ariaLabel; } return ( - - {iconChar === '' ? ( - - ) : ( - - - - )} + + + + ); -} +}; diff --git a/x-pack/plugins/ml/public/application/components/field_type_icon/index.js b/x-pack/plugins/ml/public/application/components/field_type_icon/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/components/field_type_icon/index.js rename to x-pack/plugins/ml/public/application/components/field_type_icon/index.ts diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_field_stats_card.scss b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_field_stats_card.scss index 48aab16d85be6c..f6851fcb8eca4c 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_field_stats_card.scss +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_field_stats_card.scss @@ -15,83 +15,47 @@ .boolean { color: $euiColorVis5; border-color: $euiColorVis5; - - .field-type-icon-container { - background-color: rgba($euiColorVis5, 0.2); - } } .date { color: $euiColorVis7; border-color: $euiColorVis7; - - .field-type-icon-container { - background-color: rgba($euiColorVis7, 0.2); - } } .document_count { color: $euiColorVis2; border-color: $euiColorVis2; - - .field-type-icon-container { - background-color: rgba($euiColorVis2, 0.2); - } } .geo_point { color: $euiColorVis8; border-color: $euiColorVis8; - - .field-type-icon-container { - background-color: rgba($euiColorVis8, 0.2); - } } .ip { color: $euiColorVis3; border-color: $euiColorVis3; - - .field-type-icon-container { - background-color: rgba($euiColorVis3, 0.2); - } } .keyword { color: $euiColorVis0; border-color: $euiColorVis0; - - .field-type-icon-container { - background-color: rgba($euiColorVis0, 0.2); - } } .number { color: $euiColorVis1; border-color: $euiColorVis1; - - .field-type-icon-container { - background-color: rgba($euiColorVis1, 0.2); - } } .text { color: $euiColorVis9; border-color: $euiColorVis9; - - .field-type-icon-container { - background-color: rgba($euiColorVis9, 0.2); - } } .type-other, .unknown { color: $euiColorVis6; border-color: $euiColorVis6; - - .field-type-icon-container { - background-color: rgba($euiColorVis6, 0.2); - } } // Use euiPanel styling diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/field_stats_card.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/field_stats_card.js index e68d73fc6acfaa..2e9efa43f36bc7 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/field_stats_card.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/field_stats_card.js @@ -5,7 +5,15 @@ */ import React from 'react'; -import { EuiSpacer, EuiPanel, EuiFlexGroup, EuiFlexItem, EuiText, EuiProgress } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPanel, + EuiProgress, + EuiSpacer, + EuiText, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { FieldTypeIcon } from '../../../../components/field_type_icon'; @@ -28,7 +36,7 @@ export function FieldStatsCard({ field }) {
- +
{field.name}
@@ -38,29 +46,45 @@ export function FieldStatsCard({ field }) { {field.count > 0 && (
-
-