From b49713d32327bd3dd351f368032042a954a30b36 Mon Sep 17 00:00:00 2001 From: kay delaney <45561153+kaydelaney@users.noreply.github.com> Date: Tue, 25 Oct 2022 23:34:53 +0100 Subject: [PATCH] Chore: Remove unused files (#57515) --- .betterer.results | 35 -- .../core/components/AppChrome/constants.ts | 1 - .../core/components/TagFilter/TagValue.tsx | 26 -- public/app/core/hooks/useRefMounted.ts | 12 - public/app/core/reducers/processsAclItems.ts | 32 -- .../alerting/unified/components/TimeToNow.tsx | 16 - .../components/silences/SilenceTableRow.tsx | 143 ------ .../DashboardSettingsHeader.tsx | 33 -- .../dashboard/components/PanelEditor/index.ts | 1 - .../dashboard/dashgrid/PanelResizer.tsx | 77 ---- .../datasources/passwordHandlers.test.ts | 32 -- .../features/datasources/passwordHandlers.ts | 43 -- public/app/features/explore/utils/set.test.ts | 52 --- public/app/features/explore/utils/set.ts | 35 -- .../panel/state/getOptionSuggestions.ts | 17 - .../admin/components/PluginTypeIcon.tsx | 37 -- public/app/features/plugins/admin/guards.ts | 6 - .../plugins/admin/pages/NotEnabed.tsx | 33 -- .../components/AppPluginLoader.test.tsx | 132 ------ .../plugins/components/AppPluginLoader.tsx | 44 -- .../hooks/tests/useImportAppPlugin.test.tsx | 150 ------- .../plugins/hooks/useImportAppPlugin.ts | 26 -- .../features/query/components/HelpToggle.tsx | 27 -- public/app/features/query/state/types.ts | 5 - .../editor/VariableSectionHeader.tsx | 25 -- .../components/VariableQueryEditor/index.tsx | 1 - .../VisualInfluxQLEditor/SectionFill.tsx | 7 - .../querybuilder/shared/FeedbackLink.tsx | 50 --- .../plugins/panel/timeseries/LayoutBuilder.ts | 52 --- .../overrides/hideSeriesConfigFactory.test.ts | 420 ------------------ .../overrides/hideSeriesConfigFactory.ts | 175 -------- .../timeseries/plugins/AnnotationsPlugin.tsx | 1 + .../plugins/annotations/AnnotationEditor.tsx | 1 + .../annotations/AnnotationEditorForm.tsx | 2 + .../plugins/annotations/AnnotationMarker.tsx | 1 + .../plugins/annotations/AnnotationTooltip.tsx | 2 + .../panel/timeseries/plugins/styles.ts | 2 + .../plugins/panel/timeseries/plugins/types.ts | 2 +- 38 files changed, 10 insertions(+), 1746 deletions(-) delete mode 100644 public/app/core/components/AppChrome/constants.ts delete mode 100644 public/app/core/components/TagFilter/TagValue.tsx delete mode 100644 public/app/core/hooks/useRefMounted.ts delete mode 100644 public/app/core/reducers/processsAclItems.ts delete mode 100644 public/app/features/alerting/unified/components/TimeToNow.tsx delete mode 100644 public/app/features/alerting/unified/components/silences/SilenceTableRow.tsx delete mode 100644 public/app/features/dashboard/components/DashboardSettings/DashboardSettingsHeader.tsx delete mode 100644 public/app/features/dashboard/components/PanelEditor/index.ts delete mode 100644 public/app/features/dashboard/dashgrid/PanelResizer.tsx delete mode 100644 public/app/features/datasources/passwordHandlers.test.ts delete mode 100644 public/app/features/datasources/passwordHandlers.ts delete mode 100644 public/app/features/explore/utils/set.test.ts delete mode 100644 public/app/features/explore/utils/set.ts delete mode 100644 public/app/features/panel/state/getOptionSuggestions.ts delete mode 100644 public/app/features/plugins/admin/components/PluginTypeIcon.tsx delete mode 100644 public/app/features/plugins/admin/guards.ts delete mode 100644 public/app/features/plugins/admin/pages/NotEnabed.tsx delete mode 100644 public/app/features/plugins/components/AppPluginLoader.test.tsx delete mode 100644 public/app/features/plugins/components/AppPluginLoader.tsx delete mode 100644 public/app/features/plugins/hooks/tests/useImportAppPlugin.test.tsx delete mode 100644 public/app/features/plugins/hooks/useImportAppPlugin.ts delete mode 100644 public/app/features/query/components/HelpToggle.tsx delete mode 100644 public/app/features/query/state/types.ts delete mode 100644 public/app/features/variables/editor/VariableSectionHeader.tsx delete mode 100644 public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/index.tsx delete mode 100644 public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/SectionFill.tsx delete mode 100644 public/app/plugins/datasource/prometheus/querybuilder/shared/FeedbackLink.tsx delete mode 100644 public/app/plugins/panel/timeseries/LayoutBuilder.ts delete mode 100644 public/app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory.test.ts delete mode 100644 public/app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory.ts diff --git a/.betterer.results b/.betterer.results index a63e905a236..6332bbb59c1 100644 --- a/.betterer.results +++ b/.betterer.results @@ -2511,10 +2511,6 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], - "public/app/core/components/TagFilter/TagValue.tsx:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"] - ], "public/app/core/components/connectWithCleanUp.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"] @@ -2554,9 +2550,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], - "public/app/core/reducers/processsAclItems.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] - ], "public/app/core/reducers/root.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -3799,12 +3792,6 @@ exports[`better eslint`] = { "public/app/features/datasources/components/ButtonRow.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], - "public/app/features/datasources/passwordHandlers.test.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], - "public/app/features/datasources/passwordHandlers.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], "public/app/features/datasources/state/actions.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], @@ -4428,9 +4415,6 @@ exports[`better eslint`] = { "public/app/features/plugins/admin/components/SearchField.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], - "public/app/features/plugins/admin/guards.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], "public/app/features/plugins/admin/helpers.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -4497,16 +4481,6 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "23"], [0, 0, 0, "Do not use any type assertions.", "24"] ], - "public/app/features/plugins/hooks/tests/useImportAppPlugin.test.tsx:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"], - [0, 0, 0, "Unexpected any. Specify a different type.", "4"], - [0, 0, 0, "Unexpected any. Specify a different type.", "5"], - [0, 0, 0, "Unexpected any. Specify a different type.", "6"], - [0, 0, 0, "Unexpected any. Specify a different type.", "7"] - ], "public/app/features/plugins/importPanelPlugin.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -8392,10 +8366,6 @@ exports[`better eslint`] = { "public/app/plugins/panel/timeseries/FillBelowToEditor.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], - "public/app/plugins/panel/timeseries/LayoutBuilder.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"] - ], "public/app/plugins/panel/timeseries/LineStyleEditor.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -8455,11 +8425,6 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "15"], [0, 0, 0, "Unexpected any. Specify a different type.", "16"] ], - "public/app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Do not use any type assertions.", "2"] - ], "public/app/plugins/panel/timeseries/plugins/ContextMenuPlugin.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], diff --git a/public/app/core/components/AppChrome/constants.ts b/public/app/core/components/AppChrome/constants.ts deleted file mode 100644 index 92ca1590811..00000000000 --- a/public/app/core/components/AppChrome/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const NEWS_FEED = 'https://grafana.com/blog/news.xml'; diff --git a/public/app/core/components/TagFilter/TagValue.tsx b/public/app/core/components/TagFilter/TagValue.tsx deleted file mode 100644 index e481ebf9531..00000000000 --- a/public/app/core/components/TagFilter/TagValue.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; - -import { TagBadge } from './TagBadge'; - -export interface Props { - value: any; - className: string; - onClick: React.MouseEventHandler; - onRemove: (value: any, event: React.MouseEvent) => void; -} - -export class TagValue extends React.Component { - constructor(props: Props) { - super(props); - this.onClick = this.onClick.bind(this); - } - - onClick(event: React.MouseEvent) { - this.props.onRemove(this.props.value, event); - } - - render() { - const { value } = this.props; - return ; - } -} diff --git a/public/app/core/hooks/useRefMounted.ts b/public/app/core/hooks/useRefMounted.ts deleted file mode 100644 index a87cca19935..00000000000 --- a/public/app/core/hooks/useRefMounted.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useRef, useEffect } from 'react'; - -export const useRefMounted = () => { - const refMounted = useRef(false); - useEffect(() => { - refMounted.current = true; - return () => { - refMounted.current = false; - }; - }); - return refMounted; -}; diff --git a/public/app/core/reducers/processsAclItems.ts b/public/app/core/reducers/processsAclItems.ts deleted file mode 100644 index e8aeaa60fec..00000000000 --- a/public/app/core/reducers/processsAclItems.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { DashboardAcl, DashboardAclDTO } from 'app/types/acl'; - -export function processAclItems(items: DashboardAclDTO[]): DashboardAcl[] { - return items.map(processAclItem).sort((a, b) => b.sortRank! - a.sortRank! || a.name!.localeCompare(b.name!)); -} - -function processAclItem(dto: DashboardAclDTO): DashboardAcl { - const item = dto as DashboardAcl; - - item.sortRank = 0; - - if (item.userId! > 0) { - item.name = item.userLogin; - item.sortRank = 10; - } else if (item.teamId! > 0) { - item.name = item.team; - item.sortRank = 20; - } else if (item.role) { - item.icon = 'fa fa-fw fa-street-view'; - item.name = item.role; - item.sortRank = 30; - if (item.role === 'Editor') { - item.sortRank += 1; - } - } - - if (item.inherited) { - item.sortRank += 100; - } - - return item; -} diff --git a/public/app/features/alerting/unified/components/TimeToNow.tsx b/public/app/features/alerting/unified/components/TimeToNow.tsx deleted file mode 100644 index 65cfc2fd75c..00000000000 --- a/public/app/features/alerting/unified/components/TimeToNow.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React, { FC, useEffect, useState } from 'react'; - -import { dateTimeFormatTimeAgo, DateTimeInput } from '@grafana/data'; - -export interface Props { - date: DateTimeInput; -} - -export const TimeToNow: FC = ({ date }) => { - const setRandom = useState(0)[1]; - useEffect(() => { - const interval = setInterval(() => setRandom(Math.random()), 1000); - return () => clearInterval(interval); - }); - return {dateTimeFormatTimeAgo(date)}; -}; diff --git a/public/app/features/alerting/unified/components/silences/SilenceTableRow.tsx b/public/app/features/alerting/unified/components/silences/SilenceTableRow.tsx deleted file mode 100644 index e21e6fe8ba4..00000000000 --- a/public/app/features/alerting/unified/components/silences/SilenceTableRow.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { css, cx } from '@emotion/css'; -import React, { FC, Fragment, useState } from 'react'; - -import { dateMath, GrafanaTheme, intervalToAbbreviatedDurationString } from '@grafana/data'; -import { useStyles, Link } from '@grafana/ui'; -import { contextSrv } from 'app/core/services/context_srv'; -import { Silence, AlertmanagerAlert } from 'app/plugins/datasource/alertmanager/types'; -import { useDispatch } from 'app/types'; - -import { expireSilenceAction } from '../../state/actions'; -import { makeAMLink } from '../../utils/misc'; -import { CollapseToggle } from '../CollapseToggle'; -import { ActionButton } from '../rules/ActionButton'; -import { ActionIcon } from '../rules/ActionIcon'; - -import { Matchers } from './Matchers'; -import { SilenceStateTag } from './SilenceStateTag'; -import SilencedAlertsTable from './SilencedAlertsTable'; - -interface Props { - className?: string; - silence: Silence; - silencedAlerts: AlertmanagerAlert[]; - alertManagerSourceName: string; -} - -const SilenceTableRow: FC = ({ silence, className, silencedAlerts, alertManagerSourceName }) => { - const [isCollapsed, setIsCollapsed] = useState(true); - const dispatch = useDispatch(); - - const styles = useStyles(getStyles); - const { status, matchers = [], startsAt, endsAt, comment, createdBy } = silence; - - const dateDisplayFormat = 'YYYY-MM-DD HH:mm'; - const startsAtDate = dateMath.parse(startsAt); - const endsAtDate = dateMath.parse(endsAt); - const duration = intervalToAbbreviatedDurationString({ start: new Date(startsAt), end: new Date(endsAt) }); - - const handleExpireSilenceClick = () => { - dispatch(expireSilenceAction(alertManagerSourceName, silence.id)); - }; - - const detailsColspan = contextSrv.isEditor ? 4 : 3; - - return ( - - - - setIsCollapsed(value)} /> - - - - - - - - {silencedAlerts.length} - - {startsAtDate?.format(dateDisplayFormat)} {'-'} -
- {endsAtDate?.format(dateDisplayFormat)} - - {contextSrv.isEditor && ( - - {status.state === 'expired' ? ( - - Recreate - - ) : ( - - Unsilence - - )} - {status.state !== 'expired' && ( - - )} - - )} - - {!isCollapsed && ( - <> - - - Comment - {comment} - - - - Schedule - {`${startsAtDate?.format(dateDisplayFormat)} - ${endsAtDate?.format( - dateDisplayFormat - )}`} - - - - Duration - {duration} - - - - Created by - {createdBy} - - {!!silencedAlerts.length && ( - - - Affected alerts - - - - - )} - - )} -
- ); -}; - -const getStyles = (theme: GrafanaTheme) => ({ - matchersCell: css` - & > * + * { - margin-left: ${theme.spacing.xs}; - } - `, - actionsCell: css` - text-align: right; - width: 1%; - white-space: nowrap; - - & > * + * { - margin-left: ${theme.spacing.sm}; - } - `, - alertRulesCell: css` - vertical-align: top; - `, -}); - -export default SilenceTableRow; diff --git a/public/app/features/dashboard/components/DashboardSettings/DashboardSettingsHeader.tsx b/public/app/features/dashboard/components/DashboardSettings/DashboardSettingsHeader.tsx deleted file mode 100644 index da6ddcacffc..00000000000 --- a/public/app/features/dashboard/components/DashboardSettings/DashboardSettingsHeader.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; - -import { config } from '@grafana/runtime'; -import { Icon, HorizontalGroup } from '@grafana/ui'; - -type Props = { - title: string; - onGoBack: () => void; - isEditing: boolean; -}; - -export const DashboardSettingsHeader: React.FC = ({ onGoBack, isEditing, title }) => { - if (config.featureToggles.topnav) { - return null; - } - - return ( -
- -

- - {title} - - {isEditing && ( - - Edit - - )} -

-
-
- ); -}; diff --git a/public/app/features/dashboard/components/PanelEditor/index.ts b/public/app/features/dashboard/components/PanelEditor/index.ts deleted file mode 100644 index e7f029fdb41..00000000000 --- a/public/app/features/dashboard/components/PanelEditor/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { PanelEditor } from './PanelEditor'; diff --git a/public/app/features/dashboard/dashgrid/PanelResizer.tsx b/public/app/features/dashboard/dashgrid/PanelResizer.tsx deleted file mode 100644 index f7dd6757dac..00000000000 --- a/public/app/features/dashboard/dashgrid/PanelResizer.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { throttle } from 'lodash'; -import React, { PureComponent } from 'react'; -import Draggable, { DraggableEventHandler } from 'react-draggable'; - -import { PanelModel } from '../state/PanelModel'; - -interface Props { - isEditing: boolean; - render: (styles: object) => JSX.Element; - panel: PanelModel; -} - -interface State { - editorHeight: number; -} - -export class PanelResizer extends PureComponent { - initialHeight: number = Math.floor(document.documentElement.scrollHeight * 0.3); - prevEditorHeight?: number; - throttledChangeHeight: (height: number) => void; - throttledResizeDone?: () => void; - noStyles: object = {}; - - constructor(props: Props) { - super(props); - - this.state = { - editorHeight: this.initialHeight, - }; - - this.throttledChangeHeight = throttle(this.changeHeight, 20, { trailing: true }); - } - - get largestHeight() { - return document.documentElement.scrollHeight * 0.9; - } - get smallestHeight() { - return 100; - } - - changeHeight = (height: number) => { - const sh = this.smallestHeight; - const lh = this.largestHeight; - height = height < sh ? sh : height; - height = height > lh ? lh : height; - - this.prevEditorHeight = this.state.editorHeight; - this.setState({ - editorHeight: height, - }); - }; - - onDrag: DraggableEventHandler = (evt, data) => { - const newHeight = this.state.editorHeight + data.y; - this.throttledChangeHeight(newHeight); - }; - - render() { - const { render, isEditing } = this.props; - const { editorHeight } = this.state; - - return ( - <> - {render(isEditing ? { height: editorHeight } : this.noStyles)} - {isEditing && ( -
- -
-
-
- -
- )} - - ); - } -} diff --git a/public/app/features/datasources/passwordHandlers.test.ts b/public/app/features/datasources/passwordHandlers.test.ts deleted file mode 100644 index bb55b95d04d..00000000000 --- a/public/app/features/datasources/passwordHandlers.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { createResetHandler, PasswordFieldEnum, Ctrl } from './passwordHandlers'; -describe('createResetHandler', () => { - Object.values(PasswordFieldEnum).forEach((field) => { - it(`should reset existing ${field} field`, () => { - const event: any = { - preventDefault: () => {}, - }; - const ctrl: Ctrl = { - current: { - [field]: 'set', - secureJsonData: { - [field]: 'set', - }, - secureJsonFields: {}, - }, - }; - - createResetHandler(ctrl, field)(event); - expect(ctrl).toEqual({ - current: { - [field]: undefined, - secureJsonData: { - [field]: '', - }, - secureJsonFields: { - [field]: false, - }, - }, - }); - }); - }); -}); diff --git a/public/app/features/datasources/passwordHandlers.ts b/public/app/features/datasources/passwordHandlers.ts deleted file mode 100644 index 4c25a95076e..00000000000 --- a/public/app/features/datasources/passwordHandlers.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Set of handlers for secure password field in Angular components. They handle backward compatibility with - * passwords stored in plain text fields. - */ - -import { SyntheticEvent } from 'react'; - -export enum PasswordFieldEnum { - Password = 'password', - BasicAuthPassword = 'basicAuthPassword', -} - -/** - * Basic shape for settings controllers in at the moment mostly angular data source plugins. - */ -export type Ctrl = { - current: { - secureJsonFields: { - [key: string]: boolean; - }; - secureJsonData?: { - [key: string]: string; - }; - password?: string; - basicAuthPassword?: string; - }; -}; - -export const createResetHandler = - (ctrl: Ctrl, field: PasswordFieldEnum) => (event: SyntheticEvent) => { - event.preventDefault(); - // Reset also normal plain text password to remove it and only save it in secureJsonData. - ctrl.current[field] = undefined; - ctrl.current.secureJsonFields[field] = false; - ctrl.current.secureJsonData = ctrl.current.secureJsonData || {}; - ctrl.current.secureJsonData[field] = ''; - }; - -export const createChangeHandler = - (ctrl: any, field: PasswordFieldEnum) => (event: SyntheticEvent) => { - ctrl.current.secureJsonData = ctrl.current.secureJsonData || {}; - ctrl.current.secureJsonData[field] = event.currentTarget.value; - }; diff --git a/public/app/features/explore/utils/set.test.ts b/public/app/features/explore/utils/set.test.ts deleted file mode 100644 index 4f586814fc7..00000000000 --- a/public/app/features/explore/utils/set.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { equal, intersect } from './set'; - -describe('equal', () => { - it('returns false for two sets of differing sizes', () => { - const s1 = new Set([1, 2, 3]); - const s2 = new Set([4, 5, 6, 7]); - expect(equal(s1, s2)).toBe(false); - }); - it('returns false for two sets where one is a subset of the other', () => { - const s1 = new Set([1, 2, 3]); - const s2 = new Set([1, 2, 3, 4]); - expect(equal(s1, s2)).toBe(false); - }); - it('returns false for two sets with uncommon elements', () => { - const s1 = new Set([1, 2, 3, 4]); - const s2 = new Set([1, 2, 5, 6]); - expect(equal(s1, s2)).toBe(false); - }); - it('returns false for two deeply equivalent sets', () => { - const s1 = new Set([{ a: 1 }, { b: 2 }, { c: 3 }, { d: 4 }]); - const s2 = new Set([{ a: 1 }, { b: 2 }, { c: 3 }, { d: 4 }]); - expect(equal(s1, s2)).toBe(false); - }); - it('returns true for two sets with the same elements', () => { - const s1 = new Set([1, 2, 3, 4]); - const s2 = new Set([4, 3, 2, 1]); - expect(equal(s1, s2)).toBe(true); - }); -}); - -describe('intersect', () => { - it('returns an empty set for two sets without any common elements', () => { - const s1 = new Set([1, 2, 3, 4]); - const s2 = new Set([5, 6, 7, 8]); - expect(intersect(s1, s2)).toEqual(new Set()); - }); - it('returns an empty set for two deeply equivalent sets', () => { - const s1 = new Set([{ a: 1 }, { b: 2 }, { c: 3 }, { d: 4 }]); - const s2 = new Set([{ a: 1 }, { b: 2 }, { c: 3 }, { d: 4 }]); - expect(intersect(s1, s2)).toEqual(new Set()); - }); - it('returns a set containing common elements between two sets of the same size', () => { - const s1 = new Set([1, 2, 3, 4]); - const s2 = new Set([5, 2, 7, 4]); - expect(intersect(s1, s2)).toEqual(new Set([2, 4])); - }); - it('returns a set containing common elements between two sets of differing sizes', () => { - const s1 = new Set([1, 2, 3, 4]); - const s2 = new Set([5, 4, 3, 2, 1]); - expect(intersect(s1, s2)).toEqual(new Set([1, 2, 3, 4])); - }); -}); diff --git a/public/app/features/explore/utils/set.ts b/public/app/features/explore/utils/set.ts deleted file mode 100644 index 12430384fe4..00000000000 --- a/public/app/features/explore/utils/set.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Performs a shallow comparison of two sets with the same item type. - */ -export function equal(a: Set, b: Set): boolean { - if (a.size !== b.size) { - return false; - } - const it = a.values(); - while (true) { - const { value, done } = it.next(); - if (done) { - return true; - } - if (!b.has(value)) { - return false; - } - } -} - -/** - * Returns a new set with items in both sets using shallow comparison. - */ -export function intersect(a: Set, b: Set): Set { - const result = new Set(); - const it = b.values(); - while (true) { - const { value, done } = it.next(); - if (done) { - return result; - } - if (a.has(value)) { - result.add(value); - } - } -} diff --git a/public/app/features/panel/state/getOptionSuggestions.ts b/public/app/features/panel/state/getOptionSuggestions.ts deleted file mode 100644 index 09b04d83787..00000000000 --- a/public/app/features/panel/state/getOptionSuggestions.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { VisualizationSuggestion, PanelModel, PanelPlugin, PanelData } from '@grafana/data'; - -export function getOptionSuggestions( - plugin: PanelPlugin, - panel: PanelModel, - data?: PanelData -): VisualizationSuggestion[] { - // const supplier = plugin.getSuggestionsSupplier(); - - // if (supplier && supplier.getOptionSuggestions) { - // const builder = new VisualizationSuggestionsBuilder(data, panel); - // supplier.getOptionSuggestions(builder); - // return builder.getList(); - // } - - return []; -} diff --git a/public/app/features/plugins/admin/components/PluginTypeIcon.tsx b/public/app/features/plugins/admin/components/PluginTypeIcon.tsx deleted file mode 100644 index 0d0d9d716f4..00000000000 --- a/public/app/features/plugins/admin/components/PluginTypeIcon.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { css } from '@emotion/css'; -import React from 'react'; - -import { PluginTypeCode } from '../types'; - -interface PluginTypeIconProps { - typeCode: PluginTypeCode; - size: number; -} - -export const PluginTypeIcon = ({ typeCode, size }: PluginTypeIconProps) => { - const imageUrl = ((typeCode: string) => { - switch (typeCode) { - case 'panel': - return 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNSIgaGVpZ2h0PSIyNC4zMzEiIHZpZXdCb3g9Ii02MiA2My42NjkgMjUgMjQuMzMxIj48dGl0bGU+aWNvbl9kYXRhLXNvdXJjZTwvdGl0bGU+PHBhdGggZD0iTS00MS40MDUgNjMuNjczaC0xNi4xOUE0LjQxIDQuNDEgMCAwIDAtNjIgNjguMDc4djE1LjUxN0E0LjQxIDQuNDEgMCAwIDAtNTcuNTk1IDg4aDE2LjE5QTQuNDEgNC40MSAwIDAgMC0zNyA4My41OTVWNjguMDc4YTQuNDEgNC40MSAwIDAgMC00LjQwNS00LjQwNXptMy43MjcgMTkuOTIyYTMuNzMxIDMuNzMxIDAgMCAxLTMuNzI3IDMuNzI3aC0xNi4xOWEzLjczMSAzLjczMSAwIDAgMS0zLjcyNy0zLjcyN1Y2OC4wNzhhMy43MzEgMy43MzEgMCAwIDEgMy43MjctMy43MjdoMTYuMTlhMy43MzEgMy43MzEgMCAwIDEgMy43MjcgMy43Mjd2MTUuNTE3eiIgZmlsbD0iIzg5ODk4OSIvPjxnIGZpbGw9IiM4OTg5ODkiPjxwYXRoIGQ9Ik0tNTYuNDU3IDg1LjE0N2gxMy45MTRhMi4zNSAyLjM1IDAgMCAwIDIuMjctMS43NTloLTE4LjQ1NGEyLjM1MSAyLjM1MSAwIDAgMCAyLjI3IDEuNzU5em0uMDQ3LTguNzA2bDIuMDg3LjgzLjgxLS45NzdoLTUuMjk5djEuNjgzbDEuNjM2LTEuNDA4YS43NTEuNzUxIDAgMCAxIC43NjYtLjEyOHptNS44MzktMy42OTRoLTguMjQxdjIuODI4aDUuODk1em03Ljk0OSAyLjgyOGguNzM5bDEuNjk1LTEuMzA0di0xLjUyNGgtNC4yN3ptLTE2LjE5IDQuMzgxdjIuNzEzaDE4LjYyNHYtMi44MjhoLTE4LjQ5MXptOS43NjYtOS4wNDdhLjc0OC43NDggMCAwIDEgLjg5MS0uMjAybDIuODY5IDEuMzIyaDUuMDk5VjY5LjJoLTE4LjYyNXYyLjgyOGg4LjgzOGwuOTI4LTEuMTE5em02LjUwMy00LjM4N2gtMTMuOTE0YTIuMzUyIDIuMzUyIDAgMCAwLTIuMzE2IDEuOTZoMTguNTQ1YTIuMzUgMi4zNSAwIDAgMC0yLjMxNS0xLjk2em0tNC43NjggNi4yMjVoLTEuMzExbC0yLjM0NiAyLjgyOGg2LjU1OGwtMS4zODItMi4xMjh6Ii8+PHBhdGggZD0iTS00Mi4xMDUgNzcuNjM5YS43NDcuNzQ3IDAgMCAxLTEuMDg2LS4xODZsLS43NTItMS4xNThoLTcuNjIxbC0xLjk1MiAyLjM1NGEuNzUuNzUgMCAwIDEtLjg1NC4yMTlsLTIuMTcyLS44NjQtMS4zMDEgMS4xMmgxNy42NTd2LTIuODI4aC0uMTdsLTEuNzQ5IDEuMzQzeiIvPjwvZz48L3N2Zz4='; - case 'datasource': - return 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOS4wLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMjVweCIgaGVpZ2h0PSIyNC4zcHgiIHZpZXdCb3g9Ii0xODcgNzMuNyAyNSAyNC4zIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IC0xODcgNzMuNyAyNSAyNC4zOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSINCgk+DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KCS5zdDB7ZmlsbDojNWE1YTVhO30NCjwvc3R5bGU+DQo8Zz4NCgk8dGl0bGU+aWNvbl9kYXRhLXNvdXJjZTwvdGl0bGU+DQoJPGc+DQoJCTxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0tMTc0LjUsOTQuM2MtNS41LDAtMTAuMi0xLjYtMTIuMy00Yy0wLjEsMC4zLTAuMiwwLjYtMC4yLDFjMCwzLjIsNS43LDYsMTIuNSw2czEyLjUtMi43LDEyLjUtNg0KCQkJYzAtMC4zLTAuMS0wLjctMC4yLTFDLTE2NC40LDkyLjctMTY5LDk0LjMtMTc0LjUsOTQuM3oiLz4NCgkJPHBhdGggY2xhc3M9InN0MCIgZD0iTS0xNzQuNSw4OC45Yy01LjUsMC0xMC4yLTEuNi0xMi4zLTRjLTAuMSwwLjMtMC4yLDAuNi0wLjIsMWMwLDMuMiw1LjcsNiwxMi41LDZzMTIuNS0yLjcsMTIuNS02DQoJCQljMC0wLjMtMC4xLTAuNy0wLjItMUMtMTY0LjQsODcuMy0xNjksODguOS0xNzQuNSw4OC45eiIvPg0KCQk8cGF0aCBjbGFzcz0ic3QwIiBkPSJNLTE4Nyw4MC40YzAsMy4yLDUuNyw2LDEyLjUsNnMxMi41LTIuNywxMi41LTZzLTUuNy02LTEyLjUtNlMtMTg3LDc3LjEtMTg3LDgwLjR6Ii8+DQoJPC9nPg0KPC9nPg0KPC9zdmc+DQo='; - case 'app': - return 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMi45NzgiIGhlaWdodD0iMjUiIHZpZXdCb3g9IjAgMCAyMi45NzggMjUiPjx0aXRsZT5pY29uX2FwcHM8L3RpdGxlPjxwYXRoIGQ9Ik0yMS4yMjQgMTEuMjFhMS43NiAxLjc2IDAgMCAwLTEuNjgyIDEuMjU3SDE0Ljg5YTQuMjMgNC4yMyAwIDAgMC0uMy0xLjE0MmwzLjExNC0xLjhBMi4wNSAyLjA1IDAgMSAwIDE3LjEyIDguMWExLjk4NiAxLjk4NiAwIDAgMCAuMDguNTY1bC0zLjExOCAxLjhhNC4yNDMgNC4yNDMgMCAwIDAtLjgzNS0uODM1bC41ODYtMS4wMTVhMi4xNjUgMi4xNjUgMCAwIDAgLjU5My4wODYgMi4xMTYgMi4xMTYgMCAxIDAtMS40NS0uNThsLS41OCAxLjAxMmEzLjk1NSAzLjk1NSAwIDAgMC0xLjE0LS4zVjMuMDA4YTEuNTQ3IDEuNTQ3IDAgMSAwLTEgMHY1LjgxN2E0LjIzIDQuMjMgMCAwIDAtMS4xNDMuM2wtMi4wNi0zLjU2MkExLjY4NCAxLjY4NCAwIDAgMCA3LjUxIDQuNGExLjcxIDEuNzEgMCAxIDAtMS4zMiAxLjY2bDIuMDYgMy41N2E0LjMyMyA0LjMyMyAwIDAgMC0uODQzLjg0M2wtMy41NjYtMi4wNmExLjc2IDEuNzYgMCAwIDAgLjA0NS0uMzkgMS43IDEuNyAwIDEgMC0xLjcgMS43IDEuNjg2IDEuNjg2IDAgMCAwIDEuMTU1LS40NTNsMy41NyAyLjA2YTQuMDkgNC4wOSAwIDAgMC0uMyAxLjEzM0g1LjIwNmEyLjMwNSAyLjMwNSAwIDEgMCAwIDFoMS40MDdhNC4yMyA0LjIzIDAgMCAwIC4zIDEuMTQyTDMuMTAyIDE2LjhhMS44MjMgMS44MjMgMCAxIDAgLjU1IDEuMyAxLjc3NSAxLjc3NSAwIDAgMC0uMDYzLS40MzhsMy44MjItMi4yMDZhNC4yIDQuMiAwIDAgMCAuODQzLjg0bC0yLjk4IDUuMTkzYTEuNzI3IDEuNzI3IDAgMCAwLS40MTMtLjA1IDEuNzggMS43OCAwIDEgMCAxLjI3Ny41NGwyLjk4LTUuMTc4YTQuMDkgNC4wOSAwIDAgMCAxLjEzMy4zdjEuNDA4YTIuMDU1IDIuMDU1IDAgMSAwIC45OSAwVjE3LjFhNC4yMyA0LjIzIDAgMCAwIDEuMTQzLS4zbDIuNDYgNC4yNmExLjgyNCAxLjgyNCAwIDEgMCAxLjMwNi0uNTUyIDEuNzc4IDEuNzc4IDAgMCAwLS40NDYuMDU3bC0yLjQ2LTQuMjY1YTMuOTYgMy45NiAwIDAgMCAuODI2LS44MjdsLjQ0Ni4yNThhMi4zMjQgMi4zMjQgMCAwIDAtLjEyLjczOCAyLjQgMi40IDAgMSAwIC42Mi0xLjZsLS40NDMtLjI1NGE0LjE1NSA0LjE1NSAwIDAgMCAuMzEtMS4xNTRoNC42NmExLjc1MyAxLjc1MyAwIDEgMCAxLjY4LTIuMjV6bTAgMi43MWEuOTU4Ljk1OCAwIDEgMSAuOTU4LS45NTguOTYuOTYgMCAwIDEtLjk1OC45NnpNMTAuNzUgMTYuMTRhMy4xNzcgMy4xNzcgMCAxIDEgMy4xNzctMy4xNzggMy4xOCAzLjE4IDAgMCAxLTMuMTc3IDMuMTc3em03LjE2My04LjA0YTEuMjYgMS4yNiAwIDEgMSAxLjI2IDEuMjYgMS4yNiAxLjI2IDAgMCAxLTEuMjYtMS4yNnpNMTUuNzQgNi41OTRhMS4zMTQgMS4zMTQgMCAxIDEtMS4zMTUtMS4zMTQgMS4zMTUgMS4zMTUgMCAwIDEgMS4zMTQgMS4zMTR6TTkuOTk2IDEuNTQ4YS43NTMuNzUzIDAgMSAxIC43NTMuNzUzLjc1NC43NTQgMCAwIDEtLjc1My0uNzUyek00Ljg5NyA0LjRhLjkxLjkxIDAgMSAxIC45MS45MS45MS45MSAwIDAgMS0uOTEtLjkxek0yLjE5IDguOTM2YS45MS45MSAwIDEgMSAuOTA4LS45MS45MS45MSAwIDAgMS0uOTEuOTF6bS43NyA1LjUyNmExLjUwNiAxLjUwNiAwIDEgMSAxLjUwNS0xLjUwNiAxLjUwOCAxLjUwOCAwIDAgMS0xLjUwOCAxLjUwNnptLS4xIDMuNjQ2YTEuMDMyIDEuMDMyIDAgMSAxLTEuMDMzLTEuMDMyIDEuMDMzIDEuMDMzIDAgMCAxIDEuMDMyIDEuMDMyem0yLjk4NyA1LjExYS45ODYuOTg2IDAgMSAxLS45ODYtLjk4Ny45ODcuOTg3IDAgMCAxIC45ODcuOTg3em00LjktMS40NmExLjI2IDEuMjYgMCAxIDEgMS4yNi0xLjI2IDEuMjYgMS4yNiAwIDAgMS0xLjI1NyAxLjI2em02LjQ0Mi41N2ExLjAzMiAxLjAzMiAwIDEgMS0xLjAzMy0xLjAyOCAxLjAzMyAxLjAzMyAwIDAgMSAxLjAzMiAxLjAyOHptLS40LTcuNDU4YTEuNiAxLjYgMCAxIDEtMS42IDEuNiAxLjYgMS42IDAgMCAxIDEuNi0xLjZ6IiBmaWxsPSIjODk4OTg5Ii8+PC9zdmc+'; - default: - return undefined; - } - })(typeCode); - - return imageUrl ? ( -
- ) : null; -}; diff --git a/public/app/features/plugins/admin/guards.ts b/public/app/features/plugins/admin/guards.ts deleted file mode 100644 index 29a22132891..00000000000 --- a/public/app/features/plugins/admin/guards.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { LocalPlugin } from './types'; - -export function isLocalPlugin(plugin: any): plugin is LocalPlugin { - // super naive way of figuring out if this is a local plugin - return 'category' in plugin; -} diff --git a/public/app/features/plugins/admin/pages/NotEnabed.tsx b/public/app/features/plugins/admin/pages/NotEnabed.tsx deleted file mode 100644 index 8a54f4045f4..00000000000 --- a/public/app/features/plugins/admin/pages/NotEnabed.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { css } from '@emotion/css'; -import React from 'react'; - -import { NavModel, NavModelItem } from '@grafana/data'; -import { Page } from 'app/core/components/Page/Page'; - -const node: NavModelItem = { - id: 'not-found', - text: 'The plugin catalog is not enabled', - icon: 'exclamation-triangle', - url: 'not-found', -}; - -const navModel: NavModel = { node, main: node }; - -export default function NotEnabled(): JSX.Element | null { - return ( - - - To enable installing plugins via catalog, please refer to the{' '} - - Plugin Catalog - {' '} - instructions - - - ); -} diff --git a/public/app/features/plugins/components/AppPluginLoader.test.tsx b/public/app/features/plugins/components/AppPluginLoader.test.tsx deleted file mode 100644 index 4c1430c7372..00000000000 --- a/public/app/features/plugins/components/AppPluginLoader.test.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import React, { Component } from 'react'; -import { Router } from 'react-router-dom'; - -import { AppPlugin, PluginType, AppRootProps, NavModelItem } from '@grafana/data'; -import { locationService, setEchoSrv } from '@grafana/runtime'; -import { Echo } from 'app/core/services/echo/Echo'; - -import { getMockPlugin } from '../__mocks__/pluginMocks'; -import { useImportAppPlugin } from '../hooks/useImportAppPlugin'; - -import { AppPluginLoader } from './AppPluginLoader'; - -jest.mock('../hooks/useImportAppPlugin', () => ({ - useImportAppPlugin: jest.fn(), -})); - -const useImportAppPluginMock = useImportAppPlugin as jest.Mock< - ReturnType, - Parameters ->; - -const TEXTS = { - PLUGIN_TITLE: 'Amazing App', - PLUGIN_CONTENT: 'This is my amazing app plugin!', - PLUGIN_TAB_TITLE_A: 'Tab (A)', - PLUGIN_TAB_TITLE_B: 'Tab (B)', -}; - -describe('AppPluginLoader', () => { - beforeEach(() => { - jest.resetAllMocks(); - AppPluginComponent.timesMounted = 0; - setEchoSrv(new Echo()); - }); - - test('renders the app plugin correctly', async () => { - useImportAppPluginMock.mockReturnValue({ value: getAppPluginMock(), loading: false, error: undefined }); - - renderAppPlugin(); - - expect(await screen.findByText(TEXTS.PLUGIN_TITLE)).toBeVisible(); - expect(await screen.findByText(TEXTS.PLUGIN_CONTENT)).toBeVisible(); - expect(await screen.findByLabelText(`Tab ${TEXTS.PLUGIN_TAB_TITLE_A}`)).toBeVisible(); - expect(await screen.findByLabelText(`Tab ${TEXTS.PLUGIN_TAB_TITLE_B}`)).toBeVisible(); - expect(screen.queryByText('Loading ...')).not.toBeInTheDocument(); - }); - - test('renders the app plugin only once', async () => { - useImportAppPluginMock.mockReturnValue({ value: getAppPluginMock(), loading: false, error: undefined }); - - renderAppPlugin(); - - expect(await screen.findByText(TEXTS.PLUGIN_TITLE)).toBeVisible(); - expect(AppPluginComponent.timesMounted).toEqual(1); - }); - - test('renders a loader while the plugin is loading', async () => { - useImportAppPluginMock.mockReturnValue({ value: undefined, loading: true, error: undefined }); - - renderAppPlugin(); - - expect(await screen.findByText('Loading ...')).toBeVisible(); - expect(screen.queryByText(TEXTS.PLUGIN_TITLE)).not.toBeInTheDocument(); - }); - - test('renders an error message if there are any errors while importing the plugin', async () => { - const errorMsg = 'Unable to find plugin'; - useImportAppPluginMock.mockReturnValue({ value: undefined, loading: false, error: new Error(errorMsg) }); - - renderAppPlugin(); - - expect(await screen.findByText(errorMsg)).toBeVisible(); - expect(screen.queryByText(TEXTS.PLUGIN_TITLE)).not.toBeInTheDocument(); - }); -}); - -function renderAppPlugin() { - render( - - ; - - ); -} -class AppPluginComponent extends Component { - static timesMounted = 0; - - componentDidMount() { - AppPluginComponent.timesMounted += 1; - - const node: NavModelItem = { - text: TEXTS.PLUGIN_TITLE, - children: [ - { - text: TEXTS.PLUGIN_TAB_TITLE_A, - url: '/tab-a', - id: 'a', - }, - { - text: TEXTS.PLUGIN_TAB_TITLE_B, - url: '/tab-b', - id: 'b', - }, - ], - }; - - this.props.onNavChanged({ - main: node, - node, - }); - } - - render() { - return

{TEXTS.PLUGIN_CONTENT}

; - } -} - -function getAppPluginMeta() { - return getMockPlugin({ - type: PluginType.app, - enabled: true, - }); -} - -function getAppPluginMock() { - const plugin = new AppPlugin(); - - plugin.root = AppPluginComponent; - plugin.init(getAppPluginMeta()); - - return plugin; -} diff --git a/public/app/features/plugins/components/AppPluginLoader.tsx b/public/app/features/plugins/components/AppPluginLoader.tsx deleted file mode 100644 index 37516513f1a..00000000000 --- a/public/app/features/plugins/components/AppPluginLoader.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React, { useState } from 'react'; -import { useLocation, useParams } from 'react-router-dom'; - -import { NavModel } from '@grafana/data'; -import { getWarningNav } from 'app/angular/services/nav_model_srv'; -import { Page } from 'app/core/components/Page/Page'; -import PageLoader from 'app/core/components/PageLoader/PageLoader'; - -import { useImportAppPlugin } from '../hooks/useImportAppPlugin'; - -type AppPluginLoaderProps = { - // The id of the app plugin to be loaded - id: string; - // The base URL path - defaults to the current path - basePath?: string; -}; - -// This component can be used to render an app-plugin based on its plugin ID. -export const AppPluginLoader = ({ id, basePath }: AppPluginLoaderProps) => { - const [nav, setNav] = useState(null); - const { value: plugin, error, loading } = useImportAppPlugin(id); - const queryParams = useParams(); - const { pathname } = useLocation(); - - if (error) { - return ; - } - - return ( - <> - {loading && } - {nav && } - {!loading && plugin && plugin.root && ( - - )} - - ); -}; diff --git a/public/app/features/plugins/hooks/tests/useImportAppPlugin.test.tsx b/public/app/features/plugins/hooks/tests/useImportAppPlugin.test.tsx deleted file mode 100644 index 5d90c9700ce..00000000000 --- a/public/app/features/plugins/hooks/tests/useImportAppPlugin.test.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import { render, act, waitFor } from '@testing-library/react'; -import React from 'react'; - -import { AppPlugin, PluginType } from '@grafana/data'; - -import { getMockPlugin } from '../../__mocks__/pluginMocks'; -import { getPluginSettings } from '../../pluginSettings'; -import { importAppPlugin } from '../../plugin_loader'; -import { useImportAppPlugin } from '../useImportAppPlugin'; - -jest.mock('../../pluginSettings', () => ({ - getPluginSettings: jest.fn(), -})); -jest.mock('../../plugin_loader', () => ({ - importAppPlugin: jest.fn(), -})); - -const importAppPluginMock = importAppPlugin as jest.Mock< - ReturnType, - Parameters ->; - -const getPluginSettingsMock = getPluginSettings as jest.Mock< - ReturnType, - Parameters ->; - -const PLUGIN_ID = 'sample-plugin'; - -describe('useImportAppPlugin()', () => { - beforeEach(() => { - jest.resetAllMocks(); - }); - - test('returns the imported plugin in case it exists', async () => { - let response: any; - getPluginSettingsMock.mockResolvedValue(getAppPluginMeta()); - importAppPluginMock.mockResolvedValue(getAppPluginMock()); - - act(() => { - response = runHook(PLUGIN_ID); - }); - - await waitFor(() => expect(response.value).not.toBeUndefined()); - await waitFor(() => expect(response.error).toBeUndefined()); - await waitFor(() => expect(response.loading).toBe(false)); - }); - - test('returns an error if the plugin does not exist', async () => { - let response: any; - - act(() => { - response = runHook(PLUGIN_ID); - }); - - await waitFor(() => expect(response.value).toBeUndefined()); - await waitFor(() => expect(response.error).not.toBeUndefined()); - await waitFor(() => expect(response.error.message).toMatch(/unknown plugin/i)); - await waitFor(() => expect(response.loading).toBe(false)); - }); - - test('returns an error if the plugin is not an app', async () => { - let response: any; - getPluginSettingsMock.mockResolvedValue(getAppPluginMeta({ type: PluginType.panel })); - importAppPluginMock.mockResolvedValue(getAppPluginMock()); - - act(() => { - response = runHook(PLUGIN_ID); - }); - - await waitFor(() => expect(response.value).toBeUndefined()); - await waitFor(() => expect(response.error).not.toBeUndefined()); - await waitFor(() => expect(response.error.message).toMatch(/plugin must be an app/i)); - await waitFor(() => expect(response.loading).toBe(false)); - }); - - test('returns an error if the plugin is not enabled', async () => { - let response: any; - getPluginSettingsMock.mockResolvedValue(getAppPluginMeta({ enabled: false })); - importAppPluginMock.mockResolvedValue(getAppPluginMock()); - - act(() => { - response = runHook(PLUGIN_ID); - }); - - await waitFor(() => expect(response.value).toBeUndefined()); - await waitFor(() => expect(response.error).not.toBeUndefined()); - await waitFor(() => expect(response.error.message).toMatch(/is not enabled/i)); - await waitFor(() => expect(response.loading).toBe(false)); - }); - - test('returns errors that happen during fetching plugin settings', async () => { - let response: any; - const errorMsg = 'Error while fetching plugin data'; - getPluginSettingsMock.mockRejectedValue(new Error(errorMsg)); - importAppPluginMock.mockResolvedValue(getAppPluginMock()); - - act(() => { - response = runHook(PLUGIN_ID); - }); - - await waitFor(() => expect(response.value).toBeUndefined()); - await waitFor(() => expect(response.error).not.toBeUndefined()); - await waitFor(() => expect(response.error.message).toBe(errorMsg)); - await waitFor(() => expect(response.loading).toBe(false)); - }); - - test('returns errors that happen during importing a plugin', async () => { - let response: any; - const errorMsg = 'Error while importing the plugin'; - getPluginSettingsMock.mockResolvedValue(getAppPluginMeta()); - importAppPluginMock.mockRejectedValue(new Error(errorMsg)); - - act(() => { - response = runHook(PLUGIN_ID); - }); - - await waitFor(() => expect(response.value).toBeUndefined()); - await waitFor(() => expect(response.error).not.toBeUndefined()); - await waitFor(() => expect(response.error.message).toBe(errorMsg)); - await waitFor(() => expect(response.loading).toBe(false)); - }); -}); - -function runHook(id: string): any { - const returnVal = {}; - function TestComponent() { - Object.assign(returnVal, useImportAppPlugin(id)); - return null; - } - render(); - return returnVal; -} - -function getAppPluginMeta(overrides?: Record) { - return getMockPlugin({ - id: PLUGIN_ID, - type: PluginType.app, - enabled: true, - ...overrides, - }); -} - -function getAppPluginMock() { - const plugin = new AppPlugin(); - - plugin.init(getAppPluginMeta()); - - return plugin; -} diff --git a/public/app/features/plugins/hooks/useImportAppPlugin.ts b/public/app/features/plugins/hooks/useImportAppPlugin.ts deleted file mode 100644 index cd48d9ce22f..00000000000 --- a/public/app/features/plugins/hooks/useImportAppPlugin.ts +++ /dev/null @@ -1,26 +0,0 @@ -import useAsync from 'react-use/lib/useAsync'; - -import { PluginType } from '@grafana/data'; - -import { getPluginSettings } from '../pluginSettings'; -import { importAppPlugin } from '../plugin_loader'; - -export const useImportAppPlugin = (id: string) => { - return useAsync(async () => { - const pluginMeta = await getPluginSettings(id); - - if (!pluginMeta) { - throw new Error(`Unknown plugin: "${id}"`); - } - - if (pluginMeta.type !== PluginType.app) { - throw new Error(`Plugin must be an app (currently "${pluginMeta.type}")`); - } - - if (!pluginMeta.enabled) { - throw new Error(`Application "${id}" is not enabled`); - } - - return await importAppPlugin(pluginMeta); - }); -}; diff --git a/public/app/features/query/components/HelpToggle.tsx b/public/app/features/query/components/HelpToggle.tsx deleted file mode 100644 index 82351816ff0..00000000000 --- a/public/app/features/query/components/HelpToggle.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { css, cx } from '@emotion/css'; -import React, { useState } from 'react'; - -import { GrafanaTheme } from '@grafana/data'; -import { Icon, InfoBox, stylesFactory, useTheme } from '@grafana/ui'; - -const getStyles = stylesFactory((theme: GrafanaTheme) => ({ - infoBox: css` - margin-top: ${theme.spacing.xs}; - `, -})); - -export const HelpToggle = ({ children }: React.PropsWithChildren<{}>) => { - const [isHelpVisible, setIsHelpVisible] = useState(false); - const theme = useTheme(); - const styles = getStyles(theme); - - return ( - <> - - {isHelpVisible && {children}} - - ); -}; diff --git a/public/app/features/query/state/types.ts b/public/app/features/query/state/types.ts deleted file mode 100644 index 009688dc2c8..00000000000 --- a/public/app/features/query/state/types.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { EventBusExtended } from '@grafana/data'; - -export interface PanelModelForLegacyQueryEditors { - events: EventBusExtended; -} diff --git a/public/app/features/variables/editor/VariableSectionHeader.tsx b/public/app/features/variables/editor/VariableSectionHeader.tsx deleted file mode 100644 index 407d2c6eb75..00000000000 --- a/public/app/features/variables/editor/VariableSectionHeader.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { css } from '@emotion/css'; -import React, { PropsWithChildren, ReactElement } from 'react'; - -import { GrafanaTheme } from '@grafana/data'; -import { useStyles } from '@grafana/ui'; - -interface VariableSectionHeaderProps { - name: string; -} - -export function VariableSectionHeader({ name }: PropsWithChildren): ReactElement { - const styles = useStyles(getStyles); - - return
{name}
; -} - -function getStyles(theme: GrafanaTheme) { - return { - sectionHeading: css` - label: sectionHeading; - font-size: ${theme.typography.size.md}; - margin-bottom: ${theme.spacing.sm}; - `, - }; -} diff --git a/public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/index.tsx b/public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/index.tsx deleted file mode 100644 index 423ec3dd3f6..00000000000 --- a/public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { VariableQueryEditor } from './VariableQueryEditor'; diff --git a/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/SectionFill.tsx b/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/SectionFill.tsx deleted file mode 100644 index db0de0952ac..00000000000 --- a/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/SectionFill.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; - -export const SectionFill = () => ( -
- -
-); diff --git a/public/app/plugins/datasource/prometheus/querybuilder/shared/FeedbackLink.tsx b/public/app/plugins/datasource/prometheus/querybuilder/shared/FeedbackLink.tsx deleted file mode 100644 index ef0b03a76a0..00000000000 --- a/public/app/plugins/datasource/prometheus/querybuilder/shared/FeedbackLink.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { css } from '@emotion/css'; -import React from 'react'; - -import { GrafanaTheme2 } from '@grafana/data'; -import { Stack } from '@grafana/experimental'; -import { config, reportInteraction } from '@grafana/runtime'; -import { Icon, useStyles2 } from '@grafana/ui'; - -export interface Props { - feedbackUrl?: string; -} - -export function FeedbackLink({ feedbackUrl }: Props) { - const styles = useStyles2(getStyles); - - if (!config.feedbackLinksEnabled) { - return null; - } - - return ( - - - reportInteraction('grafana_feedback_link_clicked', { - link: feedbackUrl, - }) - } - > - Give feedback - - - ); -} - -function getStyles(theme: GrafanaTheme2) { - return { - link: css({ - color: theme.colors.text.secondary, - fontSize: theme.typography.bodySmall.fontSize, - ':hover': { - color: theme.colors.text.link, - }, - }), - }; -} diff --git a/public/app/plugins/panel/timeseries/LayoutBuilder.ts b/public/app/plugins/panel/timeseries/LayoutBuilder.ts deleted file mode 100644 index ee1810cb2e0..00000000000 --- a/public/app/plugins/panel/timeseries/LayoutBuilder.ts +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; - -export interface LayoutRendererComponentProps { - slots: Partial>; - refs: Record void>; - width: number; - height: number; -} - -export type LayoutRendererComponent = React.ComponentType>; - -// Fluent API for defining and rendering layout -export class LayoutBuilder { - private layout: Partial> = {}; - - constructor( - private renderer: LayoutRendererComponent, - private refsMap: Record void>, - private width: number, - private height: number - ) {} - - getLayout() { - return this.layout; - } - addSlot(id: T, node: React.ReactNode) { - this.layout[id] = node; - return this; - } - - clearSlot(id: T) { - if (this.layout[id] && this.refsMap[id]) { - delete this.layout[id]; - - this.refsMap[id](null); - } - return this; - } - - render() { - if (!this.layout) { - return null; - } - - return React.createElement(this.renderer, { - slots: this.layout, - refs: this.refsMap, - width: this.width, - height: this.height, - }); - } -} diff --git a/public/app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory.test.ts b/public/app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory.test.ts deleted file mode 100644 index 059e65db4a8..00000000000 --- a/public/app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory.test.ts +++ /dev/null @@ -1,420 +0,0 @@ -import { - ByNamesMatcherMode, - DataFrame, - FieldConfigSource, - FieldMatcherID, - FieldType, - toDataFrame, -} from '@grafana/data'; -import { GraphNGLegendEvent, SeriesVisibilityChangeMode } from '@grafana/ui'; - -import { hideSeriesConfigFactory } from './hideSeriesConfigFactory'; - -describe('hideSeriesConfigFactory', () => { - it('should create config override matching one series', () => { - const event: GraphNGLegendEvent = { - mode: SeriesVisibilityChangeMode.ToggleSelection, - fieldIndex: { - frameIndex: 0, - fieldIndex: 1, - }, - }; - - const existingConfig: FieldConfigSource = { - defaults: {}, - overrides: [], - }; - - const data: DataFrame[] = [ - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'temperature', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - ]; - - const config = hideSeriesConfigFactory(event, existingConfig, data); - - expect(config).toEqual({ - defaults: {}, - overrides: [createOverride(['temperature'])], - }); - }); - - it('should create config override matching one series if selected with others', () => { - const event: GraphNGLegendEvent = { - mode: SeriesVisibilityChangeMode.ToggleSelection, - fieldIndex: { - frameIndex: 0, - fieldIndex: 1, - }, - }; - - const existingConfig: FieldConfigSource = { - defaults: {}, - overrides: [createOverride(['temperature', 'humidity'])], - }; - - const data: DataFrame[] = [ - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'temperature', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'humidity', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'pressure', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - ]; - - const config = hideSeriesConfigFactory(event, existingConfig, data); - - expect(config).toEqual({ - defaults: {}, - overrides: [createOverride(['temperature'])], - }); - }); - - it('should create config override that append series to existing override', () => { - const event: GraphNGLegendEvent = { - mode: SeriesVisibilityChangeMode.AppendToSelection, - fieldIndex: { - frameIndex: 1, - fieldIndex: 1, - }, - }; - - const existingConfig: FieldConfigSource = { - defaults: {}, - overrides: [createOverride(['temperature'])], - }; - - const data: DataFrame[] = [ - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'temperature', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'humidity', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'pressure', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - ]; - - const config = hideSeriesConfigFactory(event, existingConfig, data); - - expect(config).toEqual({ - defaults: {}, - overrides: [createOverride(['temperature', 'humidity'])], - }); - }); - - it('should create config override that hides all series if appending only existing series', () => { - const event: GraphNGLegendEvent = { - mode: SeriesVisibilityChangeMode.AppendToSelection, - fieldIndex: { - frameIndex: 0, - fieldIndex: 1, - }, - }; - - const existingConfig: FieldConfigSource = { - defaults: {}, - overrides: [createOverride(['temperature'])], - }; - - const data: DataFrame[] = [ - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'temperature', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'humidity', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - ]; - - const config = hideSeriesConfigFactory(event, existingConfig, data); - - expect(config).toEqual({ - defaults: {}, - overrides: [createOverride([])], - }); - }); - - it('should create config override that removes series if appending existing field', () => { - const event: GraphNGLegendEvent = { - mode: SeriesVisibilityChangeMode.AppendToSelection, - fieldIndex: { - frameIndex: 0, - fieldIndex: 1, - }, - }; - - const existingConfig: FieldConfigSource = { - defaults: {}, - overrides: [createOverride(['temperature', 'humidity'])], - }; - - const data: DataFrame[] = [ - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'temperature', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'humidity', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - ]; - - const config = hideSeriesConfigFactory(event, existingConfig, data); - - expect(config).toEqual({ - defaults: {}, - overrides: [createOverride(['humidity'])], - }); - }); - - it('should create config override replacing existing series', () => { - const event: GraphNGLegendEvent = { - mode: SeriesVisibilityChangeMode.ToggleSelection, - fieldIndex: { - frameIndex: 1, - fieldIndex: 1, - }, - }; - - const existingConfig: FieldConfigSource = { - defaults: {}, - overrides: [createOverride(['temperature'])], - }; - - const data: DataFrame[] = [ - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'temperature', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'humidity', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - ]; - - const config = hideSeriesConfigFactory(event, existingConfig, data); - - expect(config).toEqual({ - defaults: {}, - overrides: [createOverride(['humidity'])], - }); - }); - - it('should create config override removing existing series', () => { - const event: GraphNGLegendEvent = { - mode: SeriesVisibilityChangeMode.ToggleSelection, - fieldIndex: { - frameIndex: 0, - fieldIndex: 1, - }, - }; - - const existingConfig: FieldConfigSource = { - defaults: {}, - overrides: [createOverride(['temperature'])], - }; - - const data: DataFrame[] = [ - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'temperature', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'humidity', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - ]; - - const config = hideSeriesConfigFactory(event, existingConfig, data); - - expect(config).toEqual({ - defaults: {}, - overrides: [], - }); - }); - - it('should remove override if all fields are appended', () => { - const event: GraphNGLegendEvent = { - mode: SeriesVisibilityChangeMode.AppendToSelection, - fieldIndex: { - frameIndex: 1, - fieldIndex: 1, - }, - }; - - const existingConfig: FieldConfigSource = { - defaults: {}, - overrides: [createOverride(['temperature'])], - }; - - const data: DataFrame[] = [ - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'temperature', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'humidity', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - ]; - - const config = hideSeriesConfigFactory(event, existingConfig, data); - - expect(config).toEqual({ - defaults: {}, - overrides: [], - }); - }); - - it('should create config override hiding appended series if no previous override exists', () => { - const event: GraphNGLegendEvent = { - mode: SeriesVisibilityChangeMode.AppendToSelection, - fieldIndex: { - frameIndex: 0, - fieldIndex: 1, - }, - }; - - const existingConfig: FieldConfigSource = { - defaults: {}, - overrides: [], - }; - - const data: DataFrame[] = [ - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'temperature', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'humidity', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'pressure', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - ]; - - const config = hideSeriesConfigFactory(event, existingConfig, data); - - expect(config).toEqual({ - defaults: {}, - overrides: [createOverride(['humidity', 'pressure'])], - }); - }); - - it('should return existing override if invalid index is passed', () => { - const event: GraphNGLegendEvent = { - mode: SeriesVisibilityChangeMode.ToggleSelection, - fieldIndex: { - frameIndex: 4, - fieldIndex: 1, - }, - }; - - const existingConfig: FieldConfigSource = { - defaults: {}, - overrides: [createOverride(['temperature'])], - }; - - const data: DataFrame[] = [ - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'temperature', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - toDataFrame({ - fields: [ - { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000] }, - { name: 'humidity', type: FieldType.number, values: [1, 3, 5, 7] }, - ], - }), - ]; - - const config = hideSeriesConfigFactory(event, existingConfig, data); - - expect(config).toEqual({ - defaults: {}, - overrides: [createOverride(['temperature'])], - }); - }); -}); - -const createOverride = (matchers: string[]) => { - return { - __systemRef: 'hideSeriesFrom', - matcher: { - id: FieldMatcherID.byNames, - options: { - mode: ByNamesMatcherMode.exclude, - names: matchers, - prefix: 'All except:', - readOnly: true, - }, - }, - properties: [ - { - id: 'custom.hideFrom', - value: { - graph: true, - legend: false, - tooltip: false, - }, - }, - ], - }; -}; diff --git a/public/app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory.ts b/public/app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory.ts deleted file mode 100644 index c4e546c1b1b..00000000000 --- a/public/app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { - ByNamesMatcherMode, - DataFrame, - DynamicConfigValue, - FieldConfigSource, - FieldMatcherID, - FieldType, - getFieldDisplayName, - isSystemOverrideWithRef, - SystemConfigOverrideRule, -} from '@grafana/data'; -import { GraphNGLegendEvent, SeriesVisibilityChangeMode } from '@grafana/ui'; - -const displayOverrideRef = 'hideSeriesFrom'; -const isHideSeriesOverride = isSystemOverrideWithRef(displayOverrideRef); - -export const hideSeriesConfigFactory = ( - event: GraphNGLegendEvent, - fieldConfig: FieldConfigSource, - data: DataFrame[] -): FieldConfigSource => { - const { fieldIndex, mode } = event; - const { overrides } = fieldConfig; - - const frame = data[fieldIndex.frameIndex]; - - if (!frame) { - return fieldConfig; - } - - const field = frame.fields[fieldIndex.fieldIndex]; - - if (!field) { - return fieldConfig; - } - - const displayName = getFieldDisplayName(field, frame, data); - const currentIndex = overrides.findIndex(isHideSeriesOverride); - - if (currentIndex < 0) { - if (mode === SeriesVisibilityChangeMode.ToggleSelection) { - const override = createOverride([displayName]); - - return { - ...fieldConfig, - overrides: [override, ...fieldConfig.overrides], - }; - } - - const displayNames = getDisplayNames(data, displayName); - const override = createOverride(displayNames); - - return { - ...fieldConfig, - overrides: [override, ...fieldConfig.overrides], - }; - } - - const overridesCopy = Array.from(overrides); - const [current] = overridesCopy.splice(currentIndex, 1) as SystemConfigOverrideRule[]; - - if (mode === SeriesVisibilityChangeMode.ToggleSelection) { - const existing = getExistingDisplayNames(current); - - if (existing[0] === displayName && existing.length === 1) { - return { - ...fieldConfig, - overrides: overridesCopy, - }; - } - - const override = createOverride([displayName]); - - return { - ...fieldConfig, - overrides: [override, ...overridesCopy], - }; - } - - const override = createExtendedOverride(current, displayName); - - if (allFieldsAreExcluded(override, data)) { - return { - ...fieldConfig, - overrides: overridesCopy, - }; - } - - return { - ...fieldConfig, - overrides: [override, ...overridesCopy], - }; -}; - -const createExtendedOverride = (current: SystemConfigOverrideRule, displayName: string): SystemConfigOverrideRule => { - const property = current.properties.find((p) => p.id === 'custom.hideFrom'); - const existing = getExistingDisplayNames(current); - const index = existing.findIndex((name) => name === displayName); - - if (index < 0) { - existing.push(displayName); - } else { - existing.splice(index, 1); - } - - return createOverride(existing, property); -}; - -const getExistingDisplayNames = (rule: SystemConfigOverrideRule): string[] => { - const names = rule.matcher.options?.names; - if (!Array.isArray(names)) { - return []; - } - return names; -}; - -const createOverride = (names: string[], property?: DynamicConfigValue): SystemConfigOverrideRule => { - property = property ?? { - id: 'custom.hideFrom', - value: { - graph: true, - legend: false, - tooltip: false, - }, - }; - - return { - __systemRef: displayOverrideRef, - matcher: { - id: FieldMatcherID.byNames, - options: { - mode: ByNamesMatcherMode.exclude, - names: names, - prefix: 'All except:', - readOnly: true, - }, - }, - properties: [ - { - ...property, - value: { - graph: true, - legend: false, - tooltip: false, - }, - }, - ], - }; -}; - -const allFieldsAreExcluded = (override: SystemConfigOverrideRule, data: DataFrame[]): boolean => { - return getExistingDisplayNames(override).length === getDisplayNames(data).length; -}; - -const getDisplayNames = (data: DataFrame[], excludeName?: string): string[] => { - const unique = new Set(); - - for (const frame of data) { - for (const field of frame.fields) { - if (field.type !== FieldType.number) { - continue; - } - - const name = getFieldDisplayName(field, frame, data); - - if (name === excludeName) { - continue; - } - - unique.add(name); - } - } - - return Array.from(unique); -}; diff --git a/public/app/plugins/panel/timeseries/plugins/AnnotationsPlugin.tsx b/public/app/plugins/panel/timeseries/plugins/AnnotationsPlugin.tsx index 8b65c8208ae..936e93e6d77 100644 --- a/public/app/plugins/panel/timeseries/plugins/AnnotationsPlugin.tsx +++ b/public/app/plugins/panel/timeseries/plugins/AnnotationsPlugin.tsx @@ -5,6 +5,7 @@ import { colorManipulator, DataFrame, DataFrameFieldIndex, DataFrameView, TimeZo import { EventsCanvas, UPlotConfigBuilder, useTheme2 } from '@grafana/ui'; import { AnnotationMarker } from './annotations/AnnotationMarker'; +import { AnnotationsDataFrameViewDTO } from './types'; interface AnnotationsPluginProps { config: UPlotConfigBuilder; diff --git a/public/app/plugins/panel/timeseries/plugins/annotations/AnnotationEditor.tsx b/public/app/plugins/panel/timeseries/plugins/annotations/AnnotationEditor.tsx index b140d9e80ec..ad2e9cee156 100644 --- a/public/app/plugins/panel/timeseries/plugins/annotations/AnnotationEditor.tsx +++ b/public/app/plugins/panel/timeseries/plugins/annotations/AnnotationEditor.tsx @@ -6,6 +6,7 @@ import { colorManipulator, DataFrame, getDisplayProcessor, GrafanaTheme2, TimeZo import { PlotSelection, useStyles2, useTheme2, Portal, DEFAULT_ANNOTATION_COLOR } from '@grafana/ui'; import { getCommonAnnotationStyles } from '../styles'; +import { AnnotationsDataFrameViewDTO } from '../types'; import { AnnotationEditorForm } from './AnnotationEditorForm'; diff --git a/public/app/plugins/panel/timeseries/plugins/annotations/AnnotationEditorForm.tsx b/public/app/plugins/panel/timeseries/plugins/annotations/AnnotationEditorForm.tsx index edc4cfd149e..b829c1ebc85 100644 --- a/public/app/plugins/panel/timeseries/plugins/annotations/AnnotationEditorForm.tsx +++ b/public/app/plugins/panel/timeseries/plugins/annotations/AnnotationEditorForm.tsx @@ -8,6 +8,8 @@ import { Button, Field, Form, HorizontalGroup, InputControl, TextArea, usePanelC import { TagFilter } from 'app/core/components/TagFilter/TagFilter'; import { getAnnotationTags } from 'app/features/annotations/api'; +import { AnnotationsDataFrameViewDTO } from '../types'; + interface AnnotationEditFormDTO { description: string; tags: string[]; diff --git a/public/app/plugins/panel/timeseries/plugins/annotations/AnnotationMarker.tsx b/public/app/plugins/panel/timeseries/plugins/annotations/AnnotationMarker.tsx index fc4e666f459..a073dc71e0f 100644 --- a/public/app/plugins/panel/timeseries/plugins/annotations/AnnotationMarker.tsx +++ b/public/app/plugins/panel/timeseries/plugins/annotations/AnnotationMarker.tsx @@ -7,6 +7,7 @@ import { Portal, useStyles2, usePanelContext } from '@grafana/ui'; import { getTooltipContainerStyles } from '@grafana/ui/src/themes/mixins'; import { getCommonAnnotationStyles } from '../styles'; +import { AnnotationsDataFrameViewDTO } from '../types'; import { AnnotationEditorForm } from './AnnotationEditorForm'; import { AnnotationTooltip } from './AnnotationTooltip'; diff --git a/public/app/plugins/panel/timeseries/plugins/annotations/AnnotationTooltip.tsx b/public/app/plugins/panel/timeseries/plugins/annotations/AnnotationTooltip.tsx index 51a0f01a149..da85261f39f 100644 --- a/public/app/plugins/panel/timeseries/plugins/annotations/AnnotationTooltip.tsx +++ b/public/app/plugins/panel/timeseries/plugins/annotations/AnnotationTooltip.tsx @@ -7,6 +7,8 @@ import config from 'app/core/config'; import alertDef from 'app/features/alerting/state/alertDef'; import { CommentManager } from 'app/features/comments/CommentManager'; +import { AnnotationsDataFrameViewDTO } from '../types'; + interface AnnotationTooltipProps { annotation: AnnotationsDataFrameViewDTO; timeFormatter: (v: number) => string; diff --git a/public/app/plugins/panel/timeseries/plugins/styles.ts b/public/app/plugins/panel/timeseries/plugins/styles.ts index 9bb7054dfae..f8a3f582961 100644 --- a/public/app/plugins/panel/timeseries/plugins/styles.ts +++ b/public/app/plugins/panel/timeseries/plugins/styles.ts @@ -3,6 +3,8 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { DEFAULT_ANNOTATION_COLOR } from '@grafana/ui'; +import { AnnotationsDataFrameViewDTO } from './types'; + export const getCommonAnnotationStyles = (theme: GrafanaTheme2) => { return (annotation?: AnnotationsDataFrameViewDTO) => { const color = theme.visualization.getColorByName(annotation?.color || DEFAULT_ANNOTATION_COLOR); diff --git a/public/app/plugins/panel/timeseries/plugins/types.ts b/public/app/plugins/panel/timeseries/plugins/types.ts index ec0d97daf93..33b1b9327c2 100644 --- a/public/app/plugins/panel/timeseries/plugins/types.ts +++ b/public/app/plugins/panel/timeseries/plugins/types.ts @@ -1,4 +1,4 @@ -interface AnnotationsDataFrameViewDTO { +export interface AnnotationsDataFrameViewDTO { id: string; /** @deprecate */ dashboardId: number;