From ec0e03e547d7cf9fbf542d1c192ccc11830b5214 Mon Sep 17 00:00:00 2001 From: Valentin Agachi Date: Fri, 8 Feb 2019 00:44:09 +0800 Subject: [PATCH 001/118] Support ANSI colors codes in Loki logs Closes #15114 --- package.json | 1 + public/app/core/utils/text.ts | 4 ++ .../features/explore/LogMessageAnsi.test.tsx | 24 +++++++ .../app/features/explore/LogMessageAnsi.tsx | 70 +++++++++++++++++++ public/app/features/explore/LogRow.tsx | 30 ++++---- yarn.lock | 5 ++ 6 files changed, 122 insertions(+), 12 deletions(-) create mode 100644 public/app/features/explore/LogMessageAnsi.test.tsx create mode 100644 public/app/features/explore/LogMessageAnsi.tsx diff --git a/package.json b/package.json index 5ac751ced3f..9e65ffc97b5 100644 --- a/package.json +++ b/package.json @@ -155,6 +155,7 @@ "angular-native-dragdrop": "1.2.2", "angular-route": "1.6.6", "angular-sanitize": "1.6.6", + "ansicolor": "1.1.78", "baron": "^3.0.3", "brace": "^0.10.0", "classnames": "^2.2.6", diff --git a/public/app/core/utils/text.ts b/public/app/core/utils/text.ts index 427b0102c95..6ea4c665598 100644 --- a/public/app/core/utils/text.ts +++ b/public/app/core/utils/text.ts @@ -68,3 +68,7 @@ export function sanitize (unsanitizedString: string): string { return unsanitizedString; } } + +export function hasAnsiCodes(input: string): boolean { + return /\u001b\[\d{1,2}m/.test(input); +} diff --git a/public/app/features/explore/LogMessageAnsi.test.tsx b/public/app/features/explore/LogMessageAnsi.test.tsx new file mode 100644 index 00000000000..6560fd7b7dd --- /dev/null +++ b/public/app/features/explore/LogMessageAnsi.test.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import { LogMessageAnsi } from './LogMessageAnsi'; + +describe('', () => { + it('renders string without ANSI codes', () => { + const wrapper = shallow(); + + expect(wrapper.find('span').exists()).toBe(false); + expect(wrapper.text()).toBe('Lorem ipsum'); + }); + + it('renders string with ANSI codes', () => { + const value = 'Lorem \u001B[31mipsum\u001B[0m et dolor'; + const wrapper = shallow(); + + expect(wrapper.find('span')).toHaveLength(1); + expect(wrapper.find('span').first().prop('style')).toMatchObject(expect.objectContaining({ + color: expect.any(String) + })); + expect(wrapper.find('span').first().text()).toBe('ipsum'); + }); +}); diff --git a/public/app/features/explore/LogMessageAnsi.tsx b/public/app/features/explore/LogMessageAnsi.tsx new file mode 100644 index 00000000000..e4df16fa13c --- /dev/null +++ b/public/app/features/explore/LogMessageAnsi.tsx @@ -0,0 +1,70 @@ +import React, { PureComponent } from 'react'; +import ansicolor from 'ansicolor'; + +interface Style { + [key: string]: string; +} + +interface ParsedChunk { + style: Style; + text: string; +} + +function convertCSSToStyle(css: string): Style { + return css.split(/;\s*/).reduce((accumulated, line) => { + const match = line.match(/([^:\s]+)\s*:\s*(.+)/); + + if (match && match[1] && match[2]) { + const key = match[1].replace(/-(a-z)/g, (_, character) => character.toUpperCase()); + accumulated[key] = match[2]; + } + + return accumulated; + }, {}); +} + +interface Props { + value: string; +} + +interface State { + chunks: ParsedChunk[]; + prevValue: string; +} + +export class LogMessageAnsi extends PureComponent { + state = { + chunks: [], + prevValue: '', + }; + + static getDerivedStateFromProps(props, state) { + if (props.value === state.prevValue) { + return null; + } + + const parsed = ansicolor.parse(props.value); + + return { + chunks: parsed.spans.map((span) => { + return span.css ? + { + style: convertCSSToStyle(span.css), + text: span.text + } : + { text: span.text }; + }), + prevValue: props.value + }; + } + + render() { + const { chunks } = this.state; + + return chunks.map( + (chunk, index) => chunk.style ? + {chunk.text} : + chunk.text + ); + } +} diff --git a/public/app/features/explore/LogRow.tsx b/public/app/features/explore/LogRow.tsx index 66b0e6a69fe..d7ba0f8d12e 100644 --- a/public/app/features/explore/LogRow.tsx +++ b/public/app/features/explore/LogRow.tsx @@ -5,8 +5,9 @@ import classnames from 'classnames'; import { LogRowModel, LogLabelStatsModel, LogsParser, calculateFieldStats, getParser } from 'app/core/logs_model'; import { LogLabels } from './LogLabels'; -import { findHighlightChunksInText } from 'app/core/utils/text'; +import { findHighlightChunksInText, hasAnsiCodes } from 'app/core/utils/text'; import { LogLabelStats } from './LogLabelStats'; +import { LogMessageAnsi } from './LogMessageAnsi'; interface Props { highlighterExpressions?: string[]; @@ -135,6 +136,8 @@ export class LogRow extends PureComponent { const highlightClassName = classnames('logs-row__match-highlight', { 'logs-row__match-highlight--preview': previewHighlights, }); + const containsAnsiCodes = hasAnsiCodes(row.entry); + return (
{showDuplicates && ( @@ -157,16 +160,19 @@ export class LogRow extends PureComponent {
)}
- {parsed && ( - - )} - {!parsed && + {containsAnsiCodes && } + {!containsAnsiCodes && + parsed && ( + + )} + {!containsAnsiCodes && + !parsed && needsHighlighter && ( { highlightClassName={highlightClassName} /> )} - {!parsed && !needsHighlighter && row.entry} + {!containsAnsiCodes && !parsed && !needsHighlighter && row.entry} {showFieldStats && (
Date: Thu, 7 Feb 2019 21:22:16 +0100 Subject: [PATCH 002/118] fix: Add missing typing --- public/app/plugins/panel/graph2/GraphPanel.tsx | 4 ++-- public/app/plugins/panel/text2/module.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/app/plugins/panel/graph2/GraphPanel.tsx b/public/app/plugins/panel/graph2/GraphPanel.tsx index 01e5b2d819d..f1fc2b43d51 100644 --- a/public/app/plugins/panel/graph2/GraphPanel.tsx +++ b/public/app/plugins/panel/graph2/GraphPanel.tsx @@ -9,7 +9,7 @@ import { processTimeSeries } from '@grafana/ui/src/utils'; import { Graph } from '@grafana/ui'; // Types -import { PanelProps, NullValueMode } from '@grafana/ui/src/types'; +import { PanelProps, NullValueMode, TimeSeriesVMs } from '@grafana/ui/src/types'; import { Options } from './types'; interface Props extends PanelProps {} @@ -19,7 +19,7 @@ export class GraphPanel extends PureComponent { const { panelData, timeRange, width, height } = this.props; const { showLines, showBars, showPoints } = this.props.options; - let vmSeries; + let vmSeries: TimeSeriesVMs; if (panelData.timeSeries) { vmSeries = processTimeSeries({ timeSeries: panelData.timeSeries, diff --git a/public/app/plugins/panel/text2/module.tsx b/public/app/plugins/panel/text2/module.tsx index 68523ff0880..cc3ec016273 100644 --- a/public/app/plugins/panel/text2/module.tsx +++ b/public/app/plugins/panel/text2/module.tsx @@ -2,7 +2,7 @@ import React, { PureComponent } from 'react'; import { PanelProps } from '@grafana/ui'; export class Text2 extends PureComponent { - constructor(props) { + constructor(props: PanelProps) { super(props); } From a8a9bca07b04f7a92975edecdd8b1d3645b28ef1 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Thu, 7 Feb 2019 21:26:23 +0100 Subject: [PATCH 003/118] feat: Introduce IsDataPanel attribute to plugin.json --- pkg/api/frontendsettings.go | 1 + pkg/plugins/models.go | 1 + public/app/plugins/panel/gauge/plugin.json | 1 + public/app/plugins/panel/graph2/plugin.json | 2 +- public/app/plugins/panel/text2/plugin.json | 2 +- public/app/types/plugins.ts | 1 + 6 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index ed7054050e4..238a3965641 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -145,6 +145,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *m.ReqContext) (map[string]interf "info": panel.Info, "hideFromList": panel.HideFromList, "sort": getPanelSort(panel.Id), + "isDataPanel": panel.IsDataPanel, } } diff --git a/pkg/plugins/models.go b/pkg/plugins/models.go index 5ac436205c1..e37b1fcf7d9 100644 --- a/pkg/plugins/models.go +++ b/pkg/plugins/models.go @@ -47,6 +47,7 @@ type PluginBase struct { BaseUrl string `json:"baseUrl"` HideFromList bool `json:"hideFromList,omitempty"` State PluginState `json:"state,omitempty"` + IsDataPanel bool `json:"isDataPanel"` IncludedInAppId string `json:"-"` PluginDir string `json:"-"` diff --git a/public/app/plugins/panel/gauge/plugin.json b/public/app/plugins/panel/gauge/plugin.json index 58437779d25..733d2281cf4 100644 --- a/public/app/plugins/panel/gauge/plugin.json +++ b/public/app/plugins/panel/gauge/plugin.json @@ -2,6 +2,7 @@ "type": "panel", "name": "Gauge", "id": "gauge", + "isDataPanel": true, "info": { "author": { diff --git a/public/app/plugins/panel/graph2/plugin.json b/public/app/plugins/panel/graph2/plugin.json index 9cb6a1f78a4..9b2a915a597 100644 --- a/public/app/plugins/panel/graph2/plugin.json +++ b/public/app/plugins/panel/graph2/plugin.json @@ -2,7 +2,7 @@ "type": "panel", "name": "React Graph", "id": "graph2", - + "isDataPanel": true, "state": "alpha", "info": { diff --git a/public/app/plugins/panel/text2/plugin.json b/public/app/plugins/panel/text2/plugin.json index 53885dbd0f4..cd4ff424d89 100644 --- a/public/app/plugins/panel/text2/plugin.json +++ b/public/app/plugins/panel/text2/plugin.json @@ -2,8 +2,8 @@ "type": "panel", "name": "Text v2", "id": "text2", - "state": "alpha", + "isDataPanel": false, "info": { "author": { diff --git a/public/app/types/plugins.ts b/public/app/types/plugins.ts index 51c3b7b0476..0c5c53eb6f0 100644 --- a/public/app/types/plugins.ts +++ b/public/app/types/plugins.ts @@ -9,6 +9,7 @@ export interface PanelPlugin { info: any; sort: number; exports?: PluginExports; + isDataPanel?: boolean; } export interface Plugin { From 8d4caa593e924304f3dccdb06b541bf0bed69972 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Thu, 7 Feb 2019 21:31:05 +0100 Subject: [PATCH 004/118] feat: Add util to convert snapshotData to PanelData --- public/app/features/dashboard/utils/panel.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/public/app/features/dashboard/utils/panel.ts b/public/app/features/dashboard/utils/panel.ts index c0d753477a7..c60a153d889 100644 --- a/public/app/features/dashboard/utils/panel.ts +++ b/public/app/features/dashboard/utils/panel.ts @@ -4,7 +4,8 @@ import store from 'app/core/store'; // Models import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; import { PanelModel } from 'app/features/dashboard/state/PanelModel'; -import { TimeRange } from '@grafana/ui'; +import { PanelData, TimeRange, TimeSeries } from '@grafana/ui'; +import { TableData } from '@grafana/ui/src'; // Utils import { isString as _isString } from 'lodash'; @@ -168,3 +169,19 @@ export function getResolution(panel: PanelModel): number { return panel.maxDataPoints ? panel.maxDataPoints : Math.ceil(width * (panel.gridPos.w / 24)); } + +const isTimeSeries = (data: any): data is TimeSeries => data && data.hasOwnProperty('datapoints'); +const isTableData = (data: any): data is TableData => data && data.hasOwnProperty('columns'); +export const snapshotDataToPanelData = (panel: PanelModel): PanelData => { + const snapshotData = panel.snapshotData; + if (isTimeSeries(snapshotData[0])) { + return { + timeSeries: snapshotData + } as PanelData; + } else if (isTableData(snapshotData[0])) { + return { + tableData: snapshotData[0] + } as PanelData; + } + throw new Error('snapshotData is invalid:' + snapshotData.toString()); +}; From ec02ddd27b1c595fad0d721bde261022057602ec Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Thu, 7 Feb 2019 21:34:50 +0100 Subject: [PATCH 005/118] feat: Only use the DataPanel component when panel plugin has isDataPanel set to true in plugin.json. And fix PanelData when using snapshots --- .../dashboard/dashgrid/PanelChrome.tsx | 64 ++++++++++++------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index b02d9479dcc..1f9a2a32a5d 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -10,14 +10,14 @@ import { PanelHeader } from './PanelHeader/PanelHeader'; import { DataPanel } from './DataPanel'; // Utils -import { applyPanelTimeOverrides } from 'app/features/dashboard/utils/panel'; +import { applyPanelTimeOverrides, snapshotDataToPanelData } from 'app/features/dashboard/utils/panel'; import { PANEL_HEADER_HEIGHT } from 'app/core/constants'; import { profiler } from 'app/core/profiler'; // Types import { DashboardModel, PanelModel } from '../state'; import { PanelPlugin } from 'app/types'; -import { TimeRange, LoadingState } from '@grafana/ui'; +import { TimeRange, LoadingState, PanelData } from '@grafana/ui'; import variables from 'sass/_variables.scss'; import templateSrv from 'app/features/templating/template_srv'; @@ -94,7 +94,7 @@ export class PanelChrome extends PureComponent { return !this.props.dashboard.otherPanelInFullscreen(this.props.panel); } - renderPanel(loading, panelData, width, height): JSX.Element { + renderPanelPlugin(loading: LoadingState, panelData: PanelData, width: number, height: number): JSX.Element { const { panel, plugin } = this.props; const { timeRange, renderCounter } = this.state; const PanelComponent = plugin.exports.Panel; @@ -121,11 +121,45 @@ export class PanelChrome extends PureComponent { ); } + renderHelper = (width: number, height: number): JSX.Element => { + const { panel, plugin } = this.props; + const { refreshCounter, timeRange } = this.state; + const { datasource, targets } = panel; + return ( + <> + {panel.snapshotData && panel.snapshotData.length > 0 ? ( + this.renderPanelPlugin(LoadingState.Done, snapshotDataToPanelData(panel), width, height) + ) : ( + <> + {plugin.isDataPanel === true ? + + {({ loading, panelData }) => { + return this.renderPanelPlugin(loading, panelData, width, height); + }} + + : ( + this.renderPanelPlugin(LoadingState.Done, null, width, height) + )} + + )} + + ); + } + + render() { - const { panel, dashboard } = this.props; - const { refreshCounter, timeRange, timeInfo } = this.state; + const { dashboard, panel } = this.props; + const { timeInfo } = this.state; + const { transparent } = panel; - const { datasource, targets, transparent } = panel; const containerClassNames = `panel-container panel-container--absolute ${transparent ? 'panel-transparent' : ''}`; return ( @@ -145,23 +179,7 @@ export class PanelChrome extends PureComponent { scopedVars={panel.scopedVars} links={panel.links} /> - {panel.snapshotData ? ( - this.renderPanel(false, panel.snapshotData, width, height) - ) : ( - - {({ loading, panelData }) => { - return this.renderPanel(loading, panelData, width, height); - }} - - )} + {this.renderHelper(width, height)}
); }} From 0019e0ffc9fc4d9e7768165b2022d18624528d2f Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Mon, 11 Feb 2019 16:20:32 +0100 Subject: [PATCH 006/118] chore: Only show Queries tab for panel plugins with isDataPanel set to true --- .../dashboard/panel_editor/PanelEditor.tsx | 53 +++++++++++++------ 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/public/app/features/dashboard/panel_editor/PanelEditor.tsx b/public/app/features/dashboard/panel_editor/PanelEditor.tsx index bfdc13bc8f2..37240389373 100644 --- a/public/app/features/dashboard/panel_editor/PanelEditor.tsx +++ b/public/app/features/dashboard/panel_editor/PanelEditor.tsx @@ -30,6 +30,32 @@ interface PanelEditorTab { text: string; } +enum PanelEditorTabIds { + Queries = 'queries', + Visualization = 'visualization', + Advanced = 'advanced', + Alert = 'alert' +} + +interface PanelEditorTab { + id: string; + text: string; +} + +const panelEditorTabTexts = { + [PanelEditorTabIds.Queries]: 'Queries', + [PanelEditorTabIds.Visualization]: 'Visualization', + [PanelEditorTabIds.Advanced]: 'Panel Options', + [PanelEditorTabIds.Alert]: 'Alert', +}; + +const getPanelEditorTab = (tabId: PanelEditorTabIds): PanelEditorTab => { + return { + id: tabId, + text: panelEditorTabTexts[tabId] + }; +}; + export class PanelEditor extends PureComponent { constructor(props) { super(props); @@ -72,31 +98,26 @@ export class PanelEditor extends PureComponent { render() { const { plugin } = this.props; - let activeTab = store.getState().location.query.tab || 'queries'; + let activeTab: PanelEditorTabIds = store.getState().location.query.tab || PanelEditorTabIds.Queries; const tabs: PanelEditorTab[] = [ - { id: 'queries', text: 'Queries' }, - { id: 'visualization', text: 'Visualization' }, - { id: 'advanced', text: 'Panel Options' }, + getPanelEditorTab(PanelEditorTabIds.Queries), + getPanelEditorTab(PanelEditorTabIds.Visualization), + getPanelEditorTab(PanelEditorTabIds.Advanced), ]; // handle panels that do not have queries tab - if (plugin.exports.PanelCtrl) { - if (!plugin.exports.PanelCtrl.prototype.onDataReceived) { - // remove queries tab - tabs.shift(); - // switch tab - if (activeTab === 'queries') { - activeTab = 'visualization'; - } + if (!plugin.isDataPanel) { + // remove queries tab + tabs.shift(); + // switch tab + if (activeTab === PanelEditorTabIds.Queries) { + activeTab = PanelEditorTabIds.Visualization; } } if (config.alertingEnabled && plugin.id === 'graph') { - tabs.push({ - id: 'alert', - text: 'Alert', - }); + tabs.push(getPanelEditorTab(PanelEditorTabIds.Alert)); } return ( From a2dad6157a0e77dbdae2f6c7440b55d6a40e3864 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Mon, 11 Feb 2019 16:44:09 +0100 Subject: [PATCH 007/118] hard move --- .../features/dashboard/dashgrid/DataPanel.tsx | 23 ++----- .../dashboard/dashgrid/PanelChrome.tsx | 62 ++++++++++++++----- 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/DataPanel.tsx b/public/app/features/dashboard/dashgrid/DataPanel.tsx index 2183548000b..5b0b8588ad0 100644 --- a/public/app/features/dashboard/dashgrid/DataPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DataPanel.tsx @@ -2,7 +2,6 @@ import React, { Component } from 'react'; import { Tooltip } from '@grafana/ui'; -import ErrorBoundary from 'app/core/components/ErrorBoundary/ErrorBoundary'; // Services import { DatasourceSrv, getDatasourceSrv } from 'app/features/plugins/datasource_srv'; // Utils @@ -18,8 +17,6 @@ import { TimeSeries, } from '@grafana/ui'; -const DEFAULT_PLUGIN_ERROR = 'Error in plugin'; - interface RenderProps { loading: LoadingState; panelData: PanelData; @@ -203,22 +200,10 @@ export class DataPanel extends Component { return ( <> {this.renderLoadingStates()} - - {({ error, errorInfo }) => { - if (errorInfo) { - this.onError(error.message || DEFAULT_PLUGIN_ERROR); - return null; - } - return ( - <> - {this.props.children({ - loading, - panelData, - })} - - ); - }} - + {this.props.children({ + loading, + panelData, + })} ); } diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index b02d9479dcc..1f69fb81d30 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -8,6 +8,7 @@ import { getTimeSrv, TimeSrv } from '../services/TimeSrv'; // Components import { PanelHeader } from './PanelHeader/PanelHeader'; import { DataPanel } from './DataPanel'; +import ErrorBoundary from '../../../core/components/ErrorBoundary/ErrorBoundary'; // Utils import { applyPanelTimeOverrides } from 'app/features/dashboard/utils/panel'; @@ -23,6 +24,8 @@ import variables from 'sass/_variables.scss'; import templateSrv from 'app/features/templating/template_srv'; import { DataQueryResponse } from '@grafana/ui/src'; +const DEFAULT_PLUGIN_ERROR = 'Error in plugin'; + export interface Props { panel: PanelModel; dashboard: DashboardModel; @@ -34,6 +37,9 @@ export interface State { renderCounter: number; timeInfo?: string; timeRange?: TimeRange; + loading: LoadingState; + isFirstLoad: boolean; + errorMessage: string; } export class PanelChrome extends PureComponent { @@ -43,8 +49,11 @@ export class PanelChrome extends PureComponent { super(props); this.state = { + loading: LoadingState.NotStarted, refreshCounter: 0, renderCounter: 0, + isFirstLoad: false, + errorMessage: '', }; } @@ -94,6 +103,16 @@ export class PanelChrome extends PureComponent { return !this.props.dashboard.otherPanelInFullscreen(this.props.panel); } + onError = (errorMessage: string) => { + if (this.state.loading !== LoadingState.Error || this.state.errorMessage !== errorMessage) { + this.setState({ + loading: LoadingState.Error, + isFirstLoad: false, + errorMessage: errorMessage, + }); + } + }; + renderPanel(loading, panelData, width, height): JSX.Element { const { panel, plugin } = this.props; const { timeRange, renderCounter } = this.state; @@ -145,23 +164,32 @@ export class PanelChrome extends PureComponent { scopedVars={panel.scopedVars} links={panel.links} /> - {panel.snapshotData ? ( - this.renderPanel(false, panel.snapshotData, width, height) - ) : ( - - {({ loading, panelData }) => { - return this.renderPanel(loading, panelData, width, height); - }} - - )} + + {({ error, errorInfo }) => { + if (errorInfo) { + this.onError(error.message || DEFAULT_PLUGIN_ERROR); + return null; + } + + return panel.snapshotData ? ( + this.renderPanel(false, panel.snapshotData, width, height) + ) : ( + + {({ loading, panelData }) => { + return this.renderPanel(loading, panelData, width, height); + }} + + ); + }} +
); }} From 5388541fd7dbefc7300c46b23e280872bdf61881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Tue, 12 Feb 2019 08:03:43 +0100 Subject: [PATCH 008/118] Fixes bug #12972 with a new type of input that escapes and unescapes special regexp characters --- .../components/OrgActionBar/OrgActionBar.tsx | 10 ++-- .../__snapshots__/OrgActionBar.test.tsx.snap | 5 +- .../RegExpSafeInput/RegExpSafeInput.tsx | 48 +++++++++++++++++++ .../features/alerting/AlertRuleList.test.tsx | 5 +- .../app/features/alerting/AlertRuleList.tsx | 11 ++--- .../__snapshots__/AlertRuleList.test.tsx.snap | 6 +-- .../features/api-keys/ApiKeysPage.test.tsx | 9 ++-- public/app/features/api-keys/ApiKeysPage.tsx | 16 ++----- .../panel_editor/VisualizationTab.tsx | 7 ++- .../datasources/NewDataSourcePage.tsx | 10 ++-- public/app/features/teams/TeamList.test.tsx | 9 ++-- public/app/features/teams/TeamList.tsx | 12 ++--- .../app/features/teams/TeamMembers.test.tsx | 3 +- public/app/features/teams/TeamMembers.tsx | 8 ++-- .../__snapshots__/TeamMembers.test.tsx.snap | 9 ++-- public/app/features/users/UsersActionBar.tsx | 6 +-- .../UsersActionBar.test.tsx.snap | 20 ++++---- 17 files changed, 109 insertions(+), 85 deletions(-) create mode 100644 public/app/core/components/RegExpSafeInput/RegExpSafeInput.tsx diff --git a/public/app/core/components/OrgActionBar/OrgActionBar.tsx b/public/app/core/components/OrgActionBar/OrgActionBar.tsx index b6b2046736f..9bf1eacc515 100644 --- a/public/app/core/components/OrgActionBar/OrgActionBar.tsx +++ b/public/app/core/components/OrgActionBar/OrgActionBar.tsx @@ -1,5 +1,6 @@ import React, { PureComponent } from 'react'; import LayoutSelector, { LayoutMode } from '../LayoutSelector/LayoutSelector'; +import { RegExpSafeInput } from '../RegExpSafeInput/RegExpSafeInput'; export interface Props { searchQuery: string; @@ -23,12 +24,11 @@ export default class OrgActionBar extends PureComponent {
diff --git a/public/app/core/components/OrgActionBar/__snapshots__/OrgActionBar.test.tsx.snap b/public/app/core/components/OrgActionBar/__snapshots__/OrgActionBar.test.tsx.snap index 25de037930a..db453b8cc3f 100644 --- a/public/app/core/components/OrgActionBar/__snapshots__/OrgActionBar.test.tsx.snap +++ b/public/app/core/components/OrgActionBar/__snapshots__/OrgActionBar.test.tsx.snap @@ -10,11 +10,10 @@ exports[`Render should render component 1`] = `