mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AngularPanels: Move angular panel infra & components to angular/app folder (#40373)
* AngularPanels: Move to app/angular folder * Fixing issues
This commit is contained in:
@@ -11,7 +11,7 @@ import { PanelPlugin, PanelPluginMeta } from '@grafana/data';
|
||||
import { changePanelPlugin } from 'app/features/panel/state/actions';
|
||||
import { StoreState } from 'app/types';
|
||||
import { getSectionOpenState, saveSectionOpenState } from './state/utils';
|
||||
import { PanelCtrl } from 'app/features/panel/panel_ctrl';
|
||||
import { PanelCtrl } from 'app/angular/panel/panel_ctrl';
|
||||
import { getPanelStateForModel } from 'app/features/panel/state/selectors';
|
||||
|
||||
interface OwnProps {
|
||||
|
||||
@@ -5,7 +5,11 @@ import { Button, CustomScrollbar, Icon, Input, RadioButtonGroup, useStyles } fro
|
||||
import { changePanelPlugin } from '../../../panel/state/actions';
|
||||
import { PanelModel } from '../../state/PanelModel';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { filterPluginList, getAllPanelPluginMeta, VizTypePicker } from '../VizTypePicker/VizTypePicker';
|
||||
import {
|
||||
filterPluginList,
|
||||
getAllPanelPluginMeta,
|
||||
VizTypePicker,
|
||||
} from '../../../panel/components/VizTypePicker/VizTypePicker';
|
||||
import { Field } from '@grafana/ui/src/components/Forms/Field';
|
||||
import { PanelLibraryOptionsGroup } from 'app/features/library-panels/components/PanelLibraryOptionsGroup/PanelLibraryOptionsGroup';
|
||||
import { toggleVizPicker } from './state/reducers';
|
||||
|
||||
@@ -14,7 +14,7 @@ import { OptionPaneRenderProps } from './types';
|
||||
import { OptionsPaneItemDescriptor } from './OptionsPaneItemDescriptor';
|
||||
import { OptionsPaneCategoryDescriptor } from './OptionsPaneCategoryDescriptor';
|
||||
import { DynamicConfigValueEditor } from './DynamicConfigValueEditor';
|
||||
import { getDataLinksVariableSuggestions } from 'app/features/panel/panellinks/link_srv';
|
||||
import { getDataLinksVariableSuggestions } from 'app/angular/panel/panellinks/link_srv';
|
||||
import { OverrideCategoryTitle } from './OverrideCategoryTitle';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DataLinksInlineEditor, Input, RadioButtonGroup, Select, Switch, TextArea } from '@grafana/ui';
|
||||
import { getPanelLinksVariableSuggestions } from 'app/features/panel/panellinks/link_srv';
|
||||
import { getPanelLinksVariableSuggestions } from 'app/angular/panel/panellinks/link_srv';
|
||||
import React from 'react';
|
||||
import { RepeatRowSelect } from '../RepeatRowSelect/RepeatRowSelect';
|
||||
import { OptionsPaneItemDescriptor } from './OptionsPaneItemDescriptor';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { StandardEditorContext, VariableSuggestionsScope } from '@grafana/data';
|
||||
import { get as lodashGet } from 'lodash';
|
||||
import { getDataLinksVariableSuggestions } from 'app/features/panel/panellinks/link_srv';
|
||||
import { getDataLinksVariableSuggestions } from 'app/angular/panel/panellinks/link_srv';
|
||||
import { OptionPaneRenderProps } from './types';
|
||||
import { updateDefaultFieldConfigValue, setOptionImmutably } from './utils';
|
||||
import { OptionsPaneItemDescriptor } from './OptionsPaneItemDescriptor';
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { FC } from 'react';
|
||||
import { Icon, IconName, Tooltip, useForceUpdate } from '@grafana/ui';
|
||||
import { sanitizeUrl } from '@grafana/data/src/text/sanitize';
|
||||
import { DashboardLinksDashboard } from './DashboardLinksDashboard';
|
||||
import { getLinkSrv } from '../../../panel/panellinks/link_srv';
|
||||
import { getLinkSrv } from '../../../../angular/panel/panellinks/link_srv';
|
||||
|
||||
import { DashboardModel } from '../../state';
|
||||
import { DashboardLink } from '../../state/DashboardModel';
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useRef, useState, useLayoutEffect } from 'react';
|
||||
import { Icon, ToolbarButton, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
import { sanitize, sanitizeUrl } from '@grafana/data/src/text/sanitize';
|
||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||
import { getLinkSrv } from '../../../panel/panellinks/link_srv';
|
||||
import { getLinkSrv } from '../../../../angular/panel/panellinks/link_srv';
|
||||
import { DashboardLink } from '../../state/DashboardModel';
|
||||
import { DashboardSearchHit } from 'app/features/search/types';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
import React, { MouseEventHandler } from 'react';
|
||||
import { GrafanaTheme2, isUnsignedPluginSignature, PanelPluginMeta, PluginState } from '@grafana/data';
|
||||
import { IconButton, PluginSignatureBadge, useStyles2 } from '@grafana/ui';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { PluginStateInfo } from 'app/features/plugins/PluginStateInfo';
|
||||
|
||||
interface Props {
|
||||
isCurrent: boolean;
|
||||
plugin: PanelPluginMeta;
|
||||
title: string;
|
||||
onClick: MouseEventHandler<HTMLDivElement>;
|
||||
onDelete?: () => void;
|
||||
disabled?: boolean;
|
||||
showBadge?: boolean;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export const PanelTypeCard: React.FC<Props> = ({
|
||||
isCurrent,
|
||||
title,
|
||||
plugin,
|
||||
onClick,
|
||||
onDelete,
|
||||
disabled,
|
||||
showBadge,
|
||||
description,
|
||||
children,
|
||||
}) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
const cssClass = cx({
|
||||
[styles.item]: true,
|
||||
[styles.disabled]: disabled || plugin.state === PluginState.deprecated,
|
||||
[styles.current]: isCurrent,
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cssClass}
|
||||
aria-label={selectors.components.PluginVisualization.item(plugin.name)}
|
||||
onClick={disabled ? undefined : onClick}
|
||||
title={isCurrent ? 'Click again to close this section' : plugin.name}
|
||||
>
|
||||
<img className={styles.img} src={plugin.info.logos.small} alt="" />
|
||||
|
||||
<div className={styles.itemContent}>
|
||||
<div className={styles.name}>{title}</div>
|
||||
{description ? <span className={styles.description}>{description}</span> : null}
|
||||
{children}
|
||||
</div>
|
||||
{showBadge && (
|
||||
<div className={cx(styles.badge, disabled && styles.disabled)}>
|
||||
<PanelPluginBadge plugin={plugin} />
|
||||
</div>
|
||||
)}
|
||||
{onDelete && (
|
||||
<IconButton
|
||||
name="trash-alt"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDelete();
|
||||
}}
|
||||
aria-label="Delete button on panel type card"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
PanelTypeCard.displayName = 'PanelTypeCard';
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
item: css`
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
background: ${theme.colors.background.secondary};
|
||||
border-radius: ${theme.shape.borderRadius()};
|
||||
box-shadow: ${theme.shadows.z1};
|
||||
border: 1px solid ${theme.colors.background.secondary};
|
||||
align-items: center;
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: ${theme.transitions.create(['background'], {
|
||||
duration: theme.transitions.duration.short,
|
||||
})};
|
||||
|
||||
&:hover {
|
||||
background: ${theme.colors.emphasize(theme.colors.background.secondary, 0.03)};
|
||||
}
|
||||
`,
|
||||
itemContent: css`
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: ${theme.spacing(0, 1)};
|
||||
`,
|
||||
current: css`
|
||||
label: currentVisualizationItem;
|
||||
border: 1px solid ${theme.colors.primary.border};
|
||||
background: ${theme.colors.action.selected};
|
||||
`,
|
||||
disabled: css`
|
||||
opacity: 0.2;
|
||||
filter: grayscale(1);
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
`,
|
||||
name: css`
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
font-size: ${theme.typography.size.sm};
|
||||
font-weight: ${theme.typography.fontWeightMedium};
|
||||
width: 100%;
|
||||
`,
|
||||
description: css`
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
color: ${theme.colors.text.secondary};
|
||||
font-size: ${theme.typography.bodySmall.fontSize};
|
||||
font-weight: ${theme.typography.fontWeightLight};
|
||||
width: 100%;
|
||||
`,
|
||||
img: css`
|
||||
max-height: 38px;
|
||||
width: 38px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`,
|
||||
badge: css`
|
||||
background: ${theme.colors.background.primary};
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
interface PanelPluginBadgeProps {
|
||||
plugin: PanelPluginMeta;
|
||||
}
|
||||
|
||||
const PanelPluginBadge: React.FC<PanelPluginBadgeProps> = ({ plugin }) => {
|
||||
if (isUnsignedPluginSignature(plugin.signature)) {
|
||||
return <PluginSignatureBadge status={plugin.signature} />;
|
||||
}
|
||||
|
||||
return <PluginStateInfo state={plugin.state} />;
|
||||
};
|
||||
|
||||
PanelPluginBadge.displayName = 'PanelPluginBadge';
|
||||
@@ -1,118 +0,0 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
|
||||
import config from 'app/core/config';
|
||||
import { VizTypePickerPlugin } from './VizTypePickerPlugin';
|
||||
import { EmptySearchResult, stylesFactory, useTheme } from '@grafana/ui';
|
||||
import { GrafanaTheme, PanelPluginMeta, PluginState } from '@grafana/data';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
export interface Props {
|
||||
current: PanelPluginMeta;
|
||||
onTypeChange: (newType: PanelPluginMeta, withModKey: boolean) => void;
|
||||
searchQuery: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function getAllPanelPluginMeta(): PanelPluginMeta[] {
|
||||
const allPanels = config.panels;
|
||||
|
||||
return Object.keys(allPanels)
|
||||
.filter((key) => allPanels[key]['hideFromList'] === false)
|
||||
.map((key) => allPanels[key])
|
||||
.sort((a: PanelPluginMeta, b: PanelPluginMeta) => a.sort - b.sort);
|
||||
}
|
||||
|
||||
export function filterPluginList(
|
||||
pluginsList: PanelPluginMeta[],
|
||||
searchQuery: string,
|
||||
current: PanelPluginMeta
|
||||
): PanelPluginMeta[] {
|
||||
if (!searchQuery.length) {
|
||||
return pluginsList.filter((p) => {
|
||||
if (p.state === PluginState.deprecated) {
|
||||
return current.id === p.id;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
const query = searchQuery.toLowerCase();
|
||||
const first: PanelPluginMeta[] = [];
|
||||
const match: PanelPluginMeta[] = [];
|
||||
|
||||
for (const item of pluginsList) {
|
||||
if (item.state === PluginState.deprecated && current.id !== item.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const name = item.name.toLowerCase();
|
||||
const idx = name.indexOf(query);
|
||||
|
||||
if (idx === 0) {
|
||||
first.push(item);
|
||||
} else if (idx > 0) {
|
||||
match.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return first.concat(match);
|
||||
}
|
||||
|
||||
export const VizTypePicker: React.FC<Props> = ({ searchQuery, onTypeChange, current }) => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyles(theme);
|
||||
const pluginsList: PanelPluginMeta[] = useMemo(() => {
|
||||
return getAllPanelPluginMeta();
|
||||
}, []);
|
||||
|
||||
const getFilteredPluginList = useCallback((): PanelPluginMeta[] => {
|
||||
return filterPluginList(pluginsList, searchQuery, current);
|
||||
}, [current, pluginsList, searchQuery]);
|
||||
|
||||
const renderVizPlugin = (plugin: PanelPluginMeta, index: number) => {
|
||||
const isCurrent = plugin.id === current.id;
|
||||
const filteredPluginList = getFilteredPluginList();
|
||||
|
||||
const matchesQuery = filteredPluginList.indexOf(plugin) > -1;
|
||||
return (
|
||||
<VizTypePickerPlugin
|
||||
disabled={!matchesQuery && !!searchQuery}
|
||||
key={plugin.id}
|
||||
isCurrent={isCurrent}
|
||||
plugin={plugin}
|
||||
onClick={(e) => onTypeChange(plugin, Boolean(e.metaKey || e.ctrlKey || e.altKey))}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const filteredPluginList = getFilteredPluginList();
|
||||
const hasResults = filteredPluginList.length > 0;
|
||||
const renderList = filteredPluginList.concat(pluginsList.filter((p) => filteredPluginList.indexOf(p) === -1));
|
||||
|
||||
return (
|
||||
<div className={styles.grid}>
|
||||
{hasResults ? (
|
||||
renderList.map((plugin, index) => {
|
||||
if (plugin.state === PluginState.deprecated) {
|
||||
return null;
|
||||
}
|
||||
return renderVizPlugin(plugin, index);
|
||||
})
|
||||
) : (
|
||||
<EmptySearchResult>Could not find anything matching your query</EmptySearchResult>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
VizTypePicker.displayName = 'VizTypePicker';
|
||||
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
return {
|
||||
grid: css`
|
||||
max-width: 100%;
|
||||
display: grid;
|
||||
grid-gap: ${theme.spacing.sm};
|
||||
`,
|
||||
};
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
import React, { MouseEventHandler } from 'react';
|
||||
import { PanelPluginMeta } from '@grafana/data';
|
||||
import { PanelTypeCard } from './PanelTypeCard';
|
||||
|
||||
interface Props {
|
||||
isCurrent: boolean;
|
||||
plugin: PanelPluginMeta;
|
||||
onClick: MouseEventHandler<HTMLDivElement>;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
export const VizTypePickerPlugin: React.FC<Props> = ({ isCurrent, plugin, onClick, disabled }) => {
|
||||
return (
|
||||
<PanelTypeCard
|
||||
title={plugin.name}
|
||||
plugin={plugin}
|
||||
description={plugin.info.description}
|
||||
onClick={onClick}
|
||||
isCurrent={isCurrent}
|
||||
disabled={disabled}
|
||||
showBadge={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
VizTypePickerPlugin.displayName = 'VizTypePickerPlugin';
|
||||
Reference in New Issue
Block a user