mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
VisualizationSuggestions: Support image & text instead of real previews. Adds suggestions for all non data panels when there are no data (#42074)
* Make suggestion cards support img & text mode instead of only preview * Generic solution for non data panels * minor review tweaks
This commit is contained in:
parent
a897154017
commit
781067ee45
@ -201,12 +201,20 @@ export interface VisualizationSuggestion<TOptions = any, TFieldConfig = any> {
|
|||||||
fieldConfig?: FieldConfigSource<Partial<TFieldConfig>>;
|
fieldConfig?: FieldConfigSource<Partial<TFieldConfig>>;
|
||||||
/** Data transformations */
|
/** Data transformations */
|
||||||
transformations?: DataTransformerConfig[];
|
transformations?: DataTransformerConfig[];
|
||||||
/** Tweak for small preview */
|
/** Options for how to render suggestion card */
|
||||||
previewModifier?: (suggestion: VisualizationSuggestion) => void;
|
cardOptions?: {
|
||||||
|
/** Tweak for small preview */
|
||||||
|
previewModifier?: (suggestion: VisualizationSuggestion) => void;
|
||||||
|
icon?: string;
|
||||||
|
imgSrc?: string;
|
||||||
|
};
|
||||||
/** A value between 0-100 how suitable suggestion is */
|
/** A value between 0-100 how suitable suggestion is */
|
||||||
score?: VisualizationSuggestionScore;
|
score?: VisualizationSuggestionScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @alpha
|
||||||
|
*/
|
||||||
export enum VisualizationSuggestionScore {
|
export enum VisualizationSuggestionScore {
|
||||||
/** We are pretty sure this is the best possible option */
|
/** We are pretty sure this is the best possible option */
|
||||||
Best = 100,
|
Best = 100,
|
||||||
|
@ -57,21 +57,6 @@ export const VisualizationSelectPane: FC<Props> = ({ panel, data }) => {
|
|||||||
dispatch(toggleVizPicker(false));
|
dispatch(toggleVizPicker(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
// const onKeyPress = useCallback(
|
|
||||||
// (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
||||||
// if (e.key === 'Enter') {
|
|
||||||
// const query = e.currentTarget.value;
|
|
||||||
// const plugins = getAllPanelPluginMeta();
|
|
||||||
// const match = filterPluginList(plugins, query, plugin.meta);
|
|
||||||
|
|
||||||
// if (match && match.length) {
|
|
||||||
// onPluginTypeChange(match[0], false);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// [onPluginTypeChange, plugin.meta]
|
|
||||||
// );
|
|
||||||
|
|
||||||
if (!plugin) {
|
if (!plugin) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import React, { CSSProperties } from 'react';
|
import React, { CSSProperties } from 'react';
|
||||||
import { GrafanaTheme2, PanelData, VisualizationSuggestion } from '@grafana/data';
|
import { GrafanaTheme2, PanelData, VisualizationSuggestion } from '@grafana/data';
|
||||||
import { PanelRenderer } from '../PanelRenderer';
|
import { PanelRenderer } from '../PanelRenderer';
|
||||||
import { css } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { Tooltip, useStyles2 } from '@grafana/ui';
|
import { Tooltip, useStyles2 } from '@grafana/ui';
|
||||||
import { VizTypeChangeDetails } from './types';
|
import { VizTypeChangeDetails } from './types';
|
||||||
import { cloneDeep } from 'lodash';
|
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
import { cloneDeep } from 'lodash';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
data: PanelData;
|
data: PanelData;
|
||||||
@ -15,50 +15,59 @@ export interface Props {
|
|||||||
onChange: (details: VizTypeChangeDetails) => void;
|
onChange: (details: VizTypeChangeDetails) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function VisualizationPreview({ data, suggestion, onChange, width, showTitle }: Props) {
|
export function VisualizationSuggestionCard({ data, suggestion, onChange, width, showTitle }: Props) {
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const { innerStyles, outerStyles, renderWidth, renderHeight } = getPreviewDimensionsAndStyles(width);
|
const { innerStyles, outerStyles, renderWidth, renderHeight } = getPreviewDimensionsAndStyles(width);
|
||||||
|
const cardOptions = suggestion.cardOptions ?? {};
|
||||||
|
|
||||||
const onClick = () => {
|
const commonButtonProps = {
|
||||||
onChange({
|
'aria-label': suggestion.name,
|
||||||
pluginId: suggestion.pluginId,
|
className: styles.vizBox,
|
||||||
options: suggestion.options,
|
'data-testid': selectors.components.VisualizationPreview.card(suggestion.name),
|
||||||
fieldConfig: suggestion.fieldConfig,
|
style: outerStyles,
|
||||||
});
|
onClick: () => {
|
||||||
|
onChange({
|
||||||
|
pluginId: suggestion.pluginId,
|
||||||
|
options: suggestion.options,
|
||||||
|
fieldConfig: suggestion.fieldConfig,
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (cardOptions.imgSrc) {
|
||||||
|
return (
|
||||||
|
<Tooltip content={suggestion.description ?? suggestion.name}>
|
||||||
|
<button {...commonButtonProps} className={cx(styles.vizBox, styles.imgBox)}>
|
||||||
|
<div className={styles.name}>{suggestion.name}</div>
|
||||||
|
<img className={styles.img} src={cardOptions.imgSrc} alt={suggestion.name} />
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let preview = suggestion;
|
let preview = suggestion;
|
||||||
if (suggestion.previewModifier) {
|
if (suggestion.cardOptions?.previewModifier) {
|
||||||
preview = cloneDeep(suggestion);
|
preview = cloneDeep(suggestion);
|
||||||
suggestion.previewModifier(preview);
|
suggestion.cardOptions.previewModifier(preview);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<button {...commonButtonProps}>
|
||||||
{showTitle && <div className={styles.name}>{suggestion.name}</div>}
|
<Tooltip content={suggestion.name}>
|
||||||
<button
|
<div style={innerStyles} className={styles.renderContainer}>
|
||||||
aria-label={suggestion.name}
|
<PanelRenderer
|
||||||
className={styles.vizBox}
|
title=""
|
||||||
data-testid={selectors.components.VisualizationPreview.card(suggestion.name)}
|
data={data}
|
||||||
style={outerStyles}
|
pluginId={suggestion.pluginId}
|
||||||
onClick={onClick}
|
width={renderWidth}
|
||||||
>
|
height={renderHeight}
|
||||||
<Tooltip content={suggestion.name}>
|
options={preview.options}
|
||||||
<div style={innerStyles} className={styles.renderContainer}>
|
fieldConfig={preview.fieldConfig}
|
||||||
<PanelRenderer
|
/>
|
||||||
title=""
|
<div className={styles.hoverPane} />
|
||||||
data={data}
|
</div>
|
||||||
pluginId={suggestion.pluginId}
|
</Tooltip>
|
||||||
width={renderWidth}
|
</button>
|
||||||
height={renderHeight}
|
|
||||||
options={preview.options}
|
|
||||||
fieldConfig={preview.fieldConfig}
|
|
||||||
/>
|
|
||||||
<div className={styles.hoverPane} />
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,8 +86,7 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
background: none;
|
background: none;
|
||||||
border-radius: ${theme.shape.borderRadius(1)};
|
border-radius: ${theme.shape.borderRadius(1)};
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-align: left;
|
border: 1px solid ${theme.colors.border.medium};
|
||||||
border: 1px solid ${theme.colors.border.strong};
|
|
||||||
|
|
||||||
transition: ${theme.transitions.create(['background'], {
|
transition: ${theme.transitions.create(['background'], {
|
||||||
duration: theme.transitions.duration.short,
|
duration: theme.transitions.duration.short,
|
||||||
@ -88,7 +96,23 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
background: ${theme.colors.background.secondary};
|
background: ${theme.colors.background.secondary};
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
imgBox: css`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
justify-self: center;
|
||||||
|
color: ${theme.colors.text.primary};
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
`,
|
||||||
name: css`
|
name: css`
|
||||||
|
padding-bottom: ${theme.spacing(0.5)};
|
||||||
|
margin-top: ${theme.spacing(-1)};
|
||||||
font-size: ${theme.typography.bodySmall.fontSize};
|
font-size: ${theme.typography.bodySmall.fontSize};
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -96,6 +120,10 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
font-weight: ${theme.typography.fontWeightMedium};
|
font-weight: ${theme.typography.fontWeightMedium};
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
`,
|
`,
|
||||||
|
img: css`
|
||||||
|
max-width: ${theme.spacing(8)};
|
||||||
|
max-height: ${theme.spacing(8)};
|
||||||
|
`,
|
||||||
renderContainer: css`
|
renderContainer: css`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transform-origin: left top;
|
transform-origin: left top;
|
@ -3,7 +3,7 @@ import { useStyles2 } from '@grafana/ui';
|
|||||||
import { GrafanaTheme2, PanelData, PanelPluginMeta, PanelModel, VisualizationSuggestion } from '@grafana/data';
|
import { GrafanaTheme2, PanelData, PanelPluginMeta, PanelModel, VisualizationSuggestion } from '@grafana/data';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { VizTypeChangeDetails } from './types';
|
import { VizTypeChangeDetails } from './types';
|
||||||
import { VisualizationPreview } from './VisualizationPreview';
|
import { VisualizationSuggestionCard } from './VisualizationSuggestionCard';
|
||||||
import { getAllSuggestions } from '../../state/getAllSuggestions';
|
import { getAllSuggestions } from '../../state/getAllSuggestions';
|
||||||
import { useAsync, useLocalStorage } from 'react-use';
|
import { useAsync, useLocalStorage } from 'react-use';
|
||||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||||
@ -44,7 +44,7 @@ export function VisualizationSuggestions({ onChange, data, panel, searchQuery }:
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles.grid} style={{ gridTemplateColumns: `repeat(auto-fill, ${previewWidth - 1}px)` }}>
|
<div className={styles.grid} style={{ gridTemplateColumns: `repeat(auto-fill, ${previewWidth - 1}px)` }}>
|
||||||
{filteredSuggestions.map((suggestion, index) => (
|
{filteredSuggestions.map((suggestion, index) => (
|
||||||
<VisualizationPreview
|
<VisualizationSuggestionCard
|
||||||
key={index}
|
key={index}
|
||||||
data={data!}
|
data={data!}
|
||||||
suggestion={suggestion}
|
suggestion={suggestion}
|
||||||
|
@ -4,6 +4,7 @@ import {
|
|||||||
getDefaultTimeRange,
|
getDefaultTimeRange,
|
||||||
LoadingState,
|
LoadingState,
|
||||||
PanelData,
|
PanelData,
|
||||||
|
PanelPluginMeta,
|
||||||
toDataFrame,
|
toDataFrame,
|
||||||
VisualizationSuggestion,
|
VisualizationSuggestion,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
@ -20,6 +21,16 @@ for (const pluginId of panelsToCheckFirst) {
|
|||||||
} as any;
|
} as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.panels['text'] = {
|
||||||
|
id: 'text',
|
||||||
|
name: 'Text',
|
||||||
|
skipDataQuery: true,
|
||||||
|
info: {
|
||||||
|
description: 'pretty decent plugin',
|
||||||
|
logos: { small: 'small/logo', large: 'large/logo' },
|
||||||
|
},
|
||||||
|
} as PanelPluginMeta;
|
||||||
|
|
||||||
class ScenarioContext {
|
class ScenarioContext {
|
||||||
data: DataFrame[] = [];
|
data: DataFrame[] = [];
|
||||||
suggestions: VisualizationSuggestion[] = [];
|
suggestions: VisualizationSuggestion[] = [];
|
||||||
@ -58,7 +69,7 @@ scenario('No series', (ctx) => {
|
|||||||
ctx.setData([]);
|
ctx.setData([]);
|
||||||
|
|
||||||
it('should return correct suggestions', () => {
|
it('should return correct suggestions', () => {
|
||||||
expect(ctx.names()).toEqual([SuggestionName.Table, SuggestionName.TextPanel, SuggestionName.DashboardList]);
|
expect(ctx.names()).toEqual([SuggestionName.Table, SuggestionName.TextPanel]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -73,7 +84,7 @@ scenario('No rows', (ctx) => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
it('should return correct suggestions', () => {
|
it('should return correct suggestions', () => {
|
||||||
expect(ctx.names()).toEqual([SuggestionName.Table, SuggestionName.TextPanel, SuggestionName.DashboardList]);
|
expect(ctx.names()).toEqual([SuggestionName.Table]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
PanelModel,
|
PanelModel,
|
||||||
VisualizationSuggestionScore,
|
VisualizationSuggestionScore,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
|
import { config } from '@grafana/runtime';
|
||||||
import { importPanelPlugin } from 'app/features/plugins/importPanelPlugin';
|
import { importPanelPlugin } from 'app/features/plugins/importPanelPlugin';
|
||||||
|
|
||||||
export const panelsToCheckFirst = [
|
export const panelsToCheckFirst = [
|
||||||
@ -17,8 +18,6 @@ export const panelsToCheckFirst = [
|
|||||||
'table',
|
'table',
|
||||||
'state-timeline',
|
'state-timeline',
|
||||||
'status-history',
|
'status-history',
|
||||||
'text',
|
|
||||||
'dashlist',
|
|
||||||
'logs',
|
'logs',
|
||||||
'candlestick',
|
'candlestick',
|
||||||
];
|
];
|
||||||
@ -35,7 +34,26 @@ export async function getAllSuggestions(data?: PanelData, panel?: PanelModel): P
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.getList().sort((a, b) => {
|
const list = builder.getList();
|
||||||
|
|
||||||
|
if (builder.dataSummary.fieldCount === 0) {
|
||||||
|
for (const plugin of Object.values(config.panels)) {
|
||||||
|
if (!plugin.skipDataQuery || plugin.hideFromList) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.push({
|
||||||
|
name: plugin.name,
|
||||||
|
pluginId: plugin.id,
|
||||||
|
description: plugin.info.description,
|
||||||
|
cardOptions: {
|
||||||
|
imgSrc: plugin.info.logos.small,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list.sort((a, b) => {
|
||||||
return (b.score ?? VisualizationSuggestionScore.OK) - (a.score ?? VisualizationSuggestionScore.OK);
|
return (b.score ?? VisualizationSuggestionScore.OK) - (a.score ?? VisualizationSuggestionScore.OK);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,10 @@ export class BarChartSuggestionsSupplier {
|
|||||||
},
|
},
|
||||||
overrides: [],
|
overrides: [],
|
||||||
},
|
},
|
||||||
previewModifier: (s) => {
|
cardOptions: {
|
||||||
s.options!.barWidth = 0.8;
|
previewModifier: (s) => {
|
||||||
|
s.options!.barWidth = 0.8;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ export class BarGaugeSuggestionsSupplier {
|
|||||||
},
|
},
|
||||||
overrides: [],
|
overrides: [],
|
||||||
},
|
},
|
||||||
previewModifier: (s) => {},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// This is probably not a good option for many numeric fields
|
// This is probably not a good option for many numeric fields
|
||||||
|
@ -38,7 +38,6 @@ export class CandlestickSuggestionsSupplier {
|
|||||||
},
|
},
|
||||||
overrides: [],
|
overrides: [],
|
||||||
},
|
},
|
||||||
previewModifier: (s) => {},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
list.append({
|
list.append({
|
||||||
|
@ -8,7 +8,6 @@ import {
|
|||||||
GENERAL_FOLDER,
|
GENERAL_FOLDER,
|
||||||
ReadonlyFolderPicker,
|
ReadonlyFolderPicker,
|
||||||
} from '../../../core/components/Select/ReadonlyFolderPicker/ReadonlyFolderPicker';
|
} from '../../../core/components/Select/ReadonlyFolderPicker/ReadonlyFolderPicker';
|
||||||
import { DashListSuggestionsSupplier } from './suggestions';
|
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<DashListOptions>(DashList)
|
export const plugin = new PanelPlugin<DashListOptions>(DashList)
|
||||||
.setPanelOptions((builder) => {
|
.setPanelOptions((builder) => {
|
||||||
@ -88,5 +87,4 @@ export const plugin = new PanelPlugin<DashListOptions>(DashList)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return newOptions;
|
return newOptions;
|
||||||
})
|
});
|
||||||
.setSuggestionsSupplier(new DashListSuggestionsSupplier());
|
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
import { VisualizationSuggestionsBuilder } from '@grafana/data';
|
|
||||||
import { PanelOptions } from './models.gen';
|
|
||||||
|
|
||||||
export class DashListSuggestionsSupplier {
|
|
||||||
getSuggestionsForData(builder: VisualizationSuggestionsBuilder) {
|
|
||||||
const { dataSummary } = builder;
|
|
||||||
|
|
||||||
if (dataSummary.hasData) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const list = builder.getListAppender<PanelOptions, {}>({
|
|
||||||
name: 'Dashboard list',
|
|
||||||
pluginId: 'dashlist',
|
|
||||||
options: {},
|
|
||||||
});
|
|
||||||
|
|
||||||
list.append({});
|
|
||||||
}
|
|
||||||
}
|
|
@ -33,10 +33,12 @@ export class GaugeSuggestionsSupplier {
|
|||||||
},
|
},
|
||||||
overrides: [],
|
overrides: [],
|
||||||
},
|
},
|
||||||
previewModifier: (s) => {
|
cardOptions: {
|
||||||
if (s.options!.reduceOptions.values) {
|
previewModifier: (s) => {
|
||||||
s.options!.reduceOptions.limit = 2;
|
if (s.options!.reduceOptions.values) {
|
||||||
}
|
s.options!.reduceOptions.limit = 2;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ export class LogsPanelSuggestionsSupplier {
|
|||||||
},
|
},
|
||||||
overrides: [],
|
overrides: [],
|
||||||
},
|
},
|
||||||
previewModifier: () => {},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { dataSummary: ds } = builder;
|
const { dataSummary: ds } = builder;
|
||||||
|
@ -19,9 +19,11 @@ export class PieChartSuggestionsSupplier {
|
|||||||
values: [],
|
values: [],
|
||||||
} as any,
|
} as any,
|
||||||
},
|
},
|
||||||
previewModifier: (s) => {
|
cardOptions: {
|
||||||
// Hide labels in preview
|
previewModifier: (s) => {
|
||||||
s.options!.legend.displayMode = LegendDisplayMode.Hidden;
|
// Hide labels in preview
|
||||||
|
s.options!.legend.displayMode = LegendDisplayMode.Hidden;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -22,10 +22,12 @@ export class StatSuggestionsSupplier {
|
|||||||
},
|
},
|
||||||
overrides: [],
|
overrides: [],
|
||||||
},
|
},
|
||||||
previewModifier: (s) => {
|
cardOptions: {
|
||||||
if (s.options!.reduceOptions.values) {
|
previewModifier: (s) => {
|
||||||
s.options!.reduceOptions.limit = 1;
|
if (s.options!.reduceOptions.values) {
|
||||||
}
|
s.options!.reduceOptions.limit = 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ export class StatTimelineSuggestionsSupplier {
|
|||||||
},
|
},
|
||||||
overrides: [],
|
overrides: [],
|
||||||
},
|
},
|
||||||
previewModifier: (s) => {},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
list.append({ name: SuggestionName.StateTimeline });
|
list.append({ name: SuggestionName.StateTimeline });
|
||||||
|
@ -43,8 +43,10 @@ export class StatusHistorySuggestionsSupplier {
|
|||||||
},
|
},
|
||||||
overrides: [],
|
overrides: [],
|
||||||
},
|
},
|
||||||
previewModifier: (s) => {
|
cardOptions: {
|
||||||
s.options!.colWidth = 0.7;
|
previewModifier: (s) => {
|
||||||
|
s.options!.colWidth = 0.7;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import { PanelOptions } from './models.gen';
|
|||||||
export class TableSuggestionsSupplier {
|
export class TableSuggestionsSupplier {
|
||||||
getSuggestionsForData(builder: VisualizationSuggestionsBuilder) {
|
getSuggestionsForData(builder: VisualizationSuggestionsBuilder) {
|
||||||
const list = builder.getListAppender<PanelOptions, TableFieldOptions>({
|
const list = builder.getListAppender<PanelOptions, TableFieldOptions>({
|
||||||
name: '',
|
name: SuggestionName.Table,
|
||||||
pluginId: 'table',
|
pluginId: 'table',
|
||||||
options: {},
|
options: {},
|
||||||
fieldConfig: {
|
fieldConfig: {
|
||||||
@ -15,9 +15,22 @@ export class TableSuggestionsSupplier {
|
|||||||
},
|
},
|
||||||
overrides: [],
|
overrides: [],
|
||||||
},
|
},
|
||||||
previewModifier: (s) => {},
|
cardOptions: {
|
||||||
|
previewModifier: (s) => {
|
||||||
|
s.fieldConfig!.defaults.custom!.minWidth = 50;
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
list.append({ name: SuggestionName.Table });
|
// If there are not data suggest table anyway but use icon instead of real preview
|
||||||
|
if (builder.dataSummary.fieldCount === 0) {
|
||||||
|
list.append({
|
||||||
|
cardOptions: {
|
||||||
|
imgSrc: 'public/app/plugins/panel/table/img/icn-table-panel.svg',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
list.append({});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import { TextPanel } from './TextPanel';
|
|||||||
import { textPanelMigrationHandler } from './textPanelMigrationHandler';
|
import { textPanelMigrationHandler } from './textPanelMigrationHandler';
|
||||||
import { TextPanelEditor } from './TextPanelEditor';
|
import { TextPanelEditor } from './TextPanelEditor';
|
||||||
import { defaultPanelOptions, PanelOptions, TextMode } from './models.gen';
|
import { defaultPanelOptions, PanelOptions, TextMode } from './models.gen';
|
||||||
import { TextPanelSuggestionSupplier } from './suggestions';
|
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<PanelOptions>(TextPanel)
|
export const plugin = new PanelPlugin<PanelOptions>(TextPanel)
|
||||||
.setPanelOptions((builder) => {
|
.setPanelOptions((builder) => {
|
||||||
@ -30,5 +29,4 @@ export const plugin = new PanelPlugin<PanelOptions>(TextPanel)
|
|||||||
defaultValue: defaultPanelOptions.content,
|
defaultValue: defaultPanelOptions.content,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.setMigrationHandler(textPanelMigrationHandler)
|
.setMigrationHandler(textPanelMigrationHandler);
|
||||||
.setSuggestionsSupplier(new TextPanelSuggestionSupplier());
|
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
import { VisualizationSuggestionsBuilder } from '@grafana/data';
|
|
||||||
import { PanelOptions } from './models.gen';
|
|
||||||
|
|
||||||
export class TextPanelSuggestionSupplier {
|
|
||||||
getSuggestionsForData(builder: VisualizationSuggestionsBuilder) {
|
|
||||||
const { dataSummary } = builder;
|
|
||||||
|
|
||||||
if (dataSummary.hasData) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const list = builder.getListAppender<PanelOptions, {}>({
|
|
||||||
name: 'Text panel',
|
|
||||||
pluginId: 'text',
|
|
||||||
options: {
|
|
||||||
content: `
|
|
||||||
# Title
|
|
||||||
|
|
||||||
For markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)
|
|
||||||
|
|
||||||
* First item
|
|
||||||
* Second item
|
|
||||||
* Third item`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
list.append({});
|
|
||||||
}
|
|
||||||
}
|
|
@ -30,12 +30,14 @@ export class TimeSeriesSuggestionsSupplier {
|
|||||||
},
|
},
|
||||||
overrides: [],
|
overrides: [],
|
||||||
},
|
},
|
||||||
previewModifier: (s) => {
|
cardOptions: {
|
||||||
s.options!.legend.displayMode = LegendDisplayMode.Hidden;
|
previewModifier: (s) => {
|
||||||
|
s.options!.legend.displayMode = LegendDisplayMode.Hidden;
|
||||||
|
|
||||||
if (s.fieldConfig?.defaults.custom?.drawStyle !== GraphDrawStyle.Bars) {
|
if (s.fieldConfig?.defaults.custom?.drawStyle !== GraphDrawStyle.Bars) {
|
||||||
s.fieldConfig!.defaults.custom!.lineWidth = Math.max(s.fieldConfig!.defaults.custom!.lineWidth ?? 1, 2);
|
s.fieldConfig!.defaults.custom!.lineWidth = Math.max(s.fieldConfig!.defaults.custom!.lineWidth ?? 1, 2);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ export enum SuggestionName {
|
|||||||
Table = 'Table',
|
Table = 'Table',
|
||||||
StateTimeline = 'State timeline',
|
StateTimeline = 'State timeline',
|
||||||
StatusHistory = 'Status history',
|
StatusHistory = 'Status history',
|
||||||
TextPanel = 'Text panel',
|
TextPanel = 'Text',
|
||||||
DashboardList = 'Dashboard list',
|
DashboardList = 'Dashboard list',
|
||||||
Logs = 'Logs',
|
Logs = 'Logs',
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user