+
{cell.render('Cell', { field, tableStyles })}
);
diff --git a/packages/grafana-ui/src/components/Table/styles.ts b/packages/grafana-ui/src/components/Table/styles.ts
index 25c7587a67b..63850aa0a77 100644
--- a/packages/grafana-ui/src/components/Table/styles.ts
+++ b/packages/grafana-ui/src/components/Table/styles.ts
@@ -1,6 +1,6 @@
import { css } from 'emotion';
import { GrafanaTheme } from '@grafana/data';
-import { stylesFactory, selectThemeVariant as stv } from '../../themes';
+import { stylesFactory } from '../../themes';
export interface TableStyles {
cellHeight: number;
@@ -11,14 +11,18 @@ export interface TableStyles {
thead: string;
headerCell: string;
tableCell: string;
+ tableCellWrapper: string;
row: string;
theme: GrafanaTheme;
+ resizeHandle: string;
}
export const getTableStyles = stylesFactory(
(theme: GrafanaTheme): TableStyles => {
const colors = theme.colors;
- const headerBg = stv({ light: colors.gray6, dark: colors.dark7 }, theme.type);
+ const headerBg = colors.panelBorder;
+ const headerBorderColor = theme.isLight ? colors.gray70 : colors.gray05;
+ const resizerColor = theme.isLight ? colors.blue77 : colors.blue95;
const padding = 6;
const lineHeight = theme.typography.lineHeight.md;
const bodyFontSize = 14;
@@ -41,16 +45,29 @@ export const getTableStyles = stylesFactory(
overflow-y: auto;
overflow-x: hidden;
background: ${headerBg};
+ position: relative;
`,
headerCell: css`
padding: ${padding}px 10px;
cursor: pointer;
white-space: nowrap;
color: ${colors.blue};
+ border-right: 1px solid ${headerBorderColor};
+
+ &:last-child {
+ border-right: none;
+ }
`,
row: css`
label: row;
- border-bottom: 2px solid ${colors.bodyBg};
+ border-bottom: 1px solid ${headerBg};
+ `,
+ tableCellWrapper: css`
+ border-right: 1px solid ${headerBg};
+
+ &:last-child {
+ border-right: none;
+ }
`,
tableCell: css`
padding: ${padding}px 10px;
@@ -58,6 +75,25 @@ export const getTableStyles = stylesFactory(
white-space: nowrap;
overflow: hidden;
`,
+ resizeHandle: css`
+ label: resizeHandle;
+ cursor: col-resize !important;
+ display: inline-block;
+ border-right: 2px solid ${resizerColor};
+ opacity: 0;
+ transition: opacity 0.2s ease-in-out;
+ width: 10px;
+ height: 100%;
+ position: absolute;
+ right: 0;
+ top: 0;
+ z-index: ${theme.zIndex.dropdown};
+ touch-action: none;
+
+ &:hover {
+ opacity: 1;
+ }
+ `,
};
}
);
diff --git a/packages/grafana-ui/src/components/Table/types.ts b/packages/grafana-ui/src/components/Table/types.ts
index 0b778b59929..ebc43b7fcd9 100644
--- a/packages/grafana-ui/src/components/Table/types.ts
+++ b/packages/grafana-ui/src/components/Table/types.ts
@@ -23,6 +23,7 @@ export interface TableRow {
}
export type TableFilterActionCallback = (key: string, value: string) => void;
+export type ColumnResizeActionCallback = (field: Field, width: number) => void;
export interface TableCellProps extends CellProps
{
tableStyles: TableStyles;
diff --git a/packages/grafana-ui/src/components/TagsInput/TagsInput.tsx b/packages/grafana-ui/src/components/TagsInput/TagsInput.tsx
index 2b5e1387dd4..5681c3db5b0 100644
--- a/packages/grafana-ui/src/components/TagsInput/TagsInput.tsx
+++ b/packages/grafana-ui/src/components/TagsInput/TagsInput.tsx
@@ -2,7 +2,7 @@ import React, { ChangeEvent, KeyboardEvent, PureComponent } from 'react';
import { css, cx } from 'emotion';
import { stylesFactory } from '../../themes/stylesFactory';
import { Button } from '../Button';
-import { Input } from '../Input/Input';
+import { Input } from '../Forms/Legacy/Input/Input';
import { TagItem } from './TagItem';
interface Props {
diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx
index 522b34b8e0a..e0fb069b721 100644
--- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx
+++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx
@@ -3,7 +3,7 @@ import { Threshold, sortThresholds, ThresholdsConfig, ThresholdsMode, Selectable
import { colors } from '../../utils';
import { getColorFromHexRgbOrName } from '@grafana/data';
import { ThemeContext } from '../../themes/ThemeContext';
-import { Input } from '../Input/Input';
+import { Input } from '../Forms/Legacy/Input/Input';
import { ColorPicker } from '../ColorPicker/ColorPicker';
import { css } from 'emotion';
import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup';
diff --git a/packages/grafana-ui/src/components/ThresholdsEditorNew/ThresholdsEditor.tsx b/packages/grafana-ui/src/components/ThresholdsEditorNew/ThresholdsEditor.tsx
index 355e6c4863f..517feae1c14 100644
--- a/packages/grafana-ui/src/components/ThresholdsEditorNew/ThresholdsEditor.tsx
+++ b/packages/grafana-ui/src/components/ThresholdsEditorNew/ThresholdsEditor.tsx
@@ -10,7 +10,7 @@ import {
} from '@grafana/data';
import { colors } from '../../utils';
import { ThemeContext } from '../../themes/ThemeContext';
-import { Input } from '../Forms/Input/Input';
+import { Input } from '../Input/Input';
import { ColorPicker } from '../ColorPicker/ColorPicker';
import { stylesFactory } from '../../themes';
import { Icon } from '../Icon/Icon';
diff --git a/packages/grafana-ui/src/components/TimePicker/TimePickerContent/TimeRangeForm.tsx b/packages/grafana-ui/src/components/TimePicker/TimePickerContent/TimeRangeForm.tsx
index de37fa43e2f..538a9dd2b35 100644
--- a/packages/grafana-ui/src/components/TimePicker/TimePickerContent/TimeRangeForm.tsx
+++ b/packages/grafana-ui/src/components/TimePicker/TimePickerContent/TimeRangeForm.tsx
@@ -4,7 +4,7 @@ import { stringToDateTimeType, isValidTimeString } from '../time';
import { mapStringsToTimeRange } from './mapper';
import { TimePickerCalendar } from './TimePickerCalendar';
import Forms from '../../Forms';
-import { Input } from '../../Forms/Input/Input';
+import { Input } from '../../Input/Input';
import { Button } from '../../Button';
interface Props {
diff --git a/packages/grafana-ui/src/components/TimePicker/__snapshots__/TimePicker.test.tsx.snap b/packages/grafana-ui/src/components/TimePicker/__snapshots__/TimePicker.test.tsx.snap
index 65ee7a7a791..9b6453ad3e7 100644
--- a/packages/grafana-ui/src/components/TimePicker/__snapshots__/TimePicker.test.tsx.snap
+++ b/packages/grafana-ui/src/components/TimePicker/__snapshots__/TimePicker.test.tsx.snap
@@ -169,7 +169,7 @@ exports[`TimePicker renders buttons correctly 1`] = `
"red88": "#e02f44",
"redBase": "#e02f44",
"redShade": "#c4162a",
- "text": "#d8d9da",
+ "text": "#c7d0d9",
"textEmphasis": "#ececec",
"textFaint": "#222426",
"textStrong": "#ffffff",
@@ -480,7 +480,7 @@ exports[`TimePicker renders content correctly after beeing open 1`] = `
"red88": "#e02f44",
"redBase": "#e02f44",
"redShade": "#c4162a",
- "text": "#d8d9da",
+ "text": "#c7d0d9",
"textEmphasis": "#ececec",
"textFaint": "#222426",
"textStrong": "#ffffff",
diff --git a/packages/grafana-ui/src/components/TransformersUI/OrganizeFieldsTransformerEditor.tsx b/packages/grafana-ui/src/components/TransformersUI/OrganizeFieldsTransformerEditor.tsx
new file mode 100644
index 00000000000..480e76c8b8b
--- /dev/null
+++ b/packages/grafana-ui/src/components/TransformersUI/OrganizeFieldsTransformerEditor.tsx
@@ -0,0 +1,221 @@
+import React, { useMemo, useCallback } from 'react';
+import { css, cx } from 'emotion';
+import { OrganizeFieldsTransformerOptions } from '@grafana/data/src/transformations/transformers/organize';
+import { DragDropContext, Droppable, Draggable, DropResult } from 'react-beautiful-dnd';
+import { TransformerUIRegistyItem, TransformerUIProps } from './types';
+import { DataTransformerID, transformersRegistry, DataFrame, GrafanaTheme } from '@grafana/data';
+import { stylesFactory, useTheme } from '../../themes';
+import { Button } from '../Button';
+import { createFieldsComparer } from '@grafana/data/src/transformations/transformers/order';
+import { VerticalGroup } from '../Layout/Layout';
+import { Input } from '../Input/Input';
+
+interface OrganizeFieldsTransformerEditorProps extends TransformerUIProps {}
+
+const OrganizeFieldsTransformerEditor: React.FC = props => {
+ const { options, input, onChange } = props;
+ const { indexByName, excludeByName, renameByName } = options;
+
+ const fieldNames = useMemo(() => fieldNamesFromInput(input), [input]);
+ const orderedFieldNames = useMemo(() => orderFieldNamesByIndex(fieldNames, indexByName), [fieldNames, indexByName]);
+
+ const onToggleVisibility = useCallback(
+ (field: string, shouldExclude: boolean) => {
+ onChange({
+ ...options,
+ excludeByName: {
+ ...excludeByName,
+ [field]: shouldExclude,
+ },
+ });
+ },
+ [onChange, excludeByName, indexByName]
+ );
+
+ const onDragEnd = useCallback(
+ (result: DropResult) => {
+ if (!result || !result.destination) {
+ return;
+ }
+
+ const startIndex = result.source.index;
+ const endIndex = result.destination.index;
+
+ if (startIndex === endIndex) {
+ return;
+ }
+
+ onChange({
+ ...options,
+ indexByName: reorderToIndex(fieldNames, startIndex, endIndex),
+ });
+ },
+ [onChange, indexByName, excludeByName, fieldNames]
+ );
+
+ const onRenameField = useCallback(
+ (from: string, to: string) => {
+ onChange({
+ ...options,
+ renameByName: {
+ ...options.renameByName,
+ [from]: to,
+ },
+ });
+ },
+ [onChange, fieldNames, renameByName]
+ );
+
+ return (
+
+
+
+ {provided => (
+
+ {orderedFieldNames.map((fieldName, index) => {
+ return (
+
+ );
+ })}
+ {provided.placeholder}
+
+ )}
+
+
+
+ );
+};
+
+interface DraggableFieldProps {
+ fieldName: string;
+ index: number;
+ visible: boolean;
+ onToggleVisibility: (fieldName: string, isVisible: boolean) => void;
+ onRenameField: (from: string, to: string) => void;
+}
+
+const DraggableFieldName: React.FC = ({
+ fieldName,
+ index,
+ visible,
+ onToggleVisibility,
+ onRenameField,
+}) => {
+ const theme = useTheme();
+ const styles = getFieldNameStyles(theme);
+
+ return (
+
+ {provided => (
+
+
+
+ onToggleVisibility(fieldName, visible)}
+ />
+ {fieldName}
+
+
+ onRenameField(fieldName, event.currentTarget.value)}
+ />
+
+
+ )}
+
+ );
+};
+
+const getFieldNameStyles = stylesFactory((theme: GrafanaTheme) => ({
+ container: css`
+ display: flex;
+ align-items: center;
+ margin-top: 8px;
+ `,
+ left: css`
+ width: 35%;
+ padding: 0 8px;
+ border-radius: 3px;
+ background-color: ${theme.isDark ? theme.colors.grayBlue : theme.colors.gray6};
+ border: 1px solid ${theme.isDark ? theme.colors.dark6 : theme.colors.gray5};
+ `,
+ right: css`
+ width: 65%;
+ margin-left: 8px;
+ `,
+ toggle: css`
+ padding: 5px;
+ margin: 0 5px;
+ `,
+ draggable: css`
+ font-size: ${theme.typography.size.md};
+ opacity: 0.4;
+ `,
+ name: css`
+ font-size: ${theme.typography.size.sm};
+ font-weight: ${theme.typography.weight.semibold};
+ `,
+}));
+
+const reorderToIndex = (fieldNames: string[], startIndex: number, endIndex: number) => {
+ const result = Array.from(fieldNames);
+ const [removed] = result.splice(startIndex, 1);
+ result.splice(endIndex, 0, removed);
+
+ return result.reduce((nameByIndex, fieldName, index) => {
+ nameByIndex[fieldName] = index;
+ return nameByIndex;
+ }, {} as Record);
+};
+
+const orderFieldNamesByIndex = (fieldNames: string[], indexByName: Record = {}): string[] => {
+ if (!indexByName || Object.keys(indexByName).length === 0) {
+ return fieldNames;
+ }
+ const comparer = createFieldsComparer(indexByName);
+ return fieldNames.sort(comparer);
+};
+
+const fieldNamesFromInput = (input: DataFrame[]): string[] => {
+ if (!Array.isArray(input)) {
+ return [] as string[];
+ }
+
+ return Object.keys(
+ input.reduce((names, frame) => {
+ if (!frame || !Array.isArray(frame.fields)) {
+ return names;
+ }
+
+ return frame.fields.reduce((names, field) => {
+ names[field.name] = null;
+ return names;
+ }, names);
+ }, {} as Record)
+ );
+};
+
+export const organizeFieldsTransformRegistryItem: TransformerUIRegistyItem = {
+ id: DataTransformerID.organize,
+ component: OrganizeFieldsTransformerEditor,
+ transformer: transformersRegistry.get(DataTransformerID.organize),
+ name: 'Organize fields',
+ description: 'UI for organizing fields',
+};
diff --git a/packages/grafana-ui/src/components/TransformersUI/transformers.ts b/packages/grafana-ui/src/components/TransformersUI/transformers.ts
index 55e906d9af8..28780c3df11 100644
--- a/packages/grafana-ui/src/components/TransformersUI/transformers.ts
+++ b/packages/grafana-ui/src/components/TransformersUI/transformers.ts
@@ -3,11 +3,13 @@ import { reduceTransformRegistryItem } from './ReduceTransformerEditor';
import { filterFieldsByNameTransformRegistryItem } from './FilterByNameTransformerEditor';
import { filterFramesByRefIdTransformRegistryItem } from './FilterByRefIdTransformerEditor';
import { TransformerUIRegistyItem } from './types';
+import { organizeFieldsTransformRegistryItem } from './OrganizeFieldsTransformerEditor';
export const transformersUIRegistry = new Registry>(() => {
return [
reduceTransformRegistryItem,
filterFieldsByNameTransformRegistryItem,
filterFramesByRefIdTransformRegistryItem,
+ organizeFieldsTransformRegistryItem,
];
});
diff --git a/packages/grafana-ui/src/components/ValueMappingsEditor/LegacyMappingRow.tsx b/packages/grafana-ui/src/components/ValueMappingsEditor/LegacyMappingRow.tsx
index 750cb1144bf..127daed718f 100644
--- a/packages/grafana-ui/src/components/ValueMappingsEditor/LegacyMappingRow.tsx
+++ b/packages/grafana-ui/src/components/ValueMappingsEditor/LegacyMappingRow.tsx
@@ -2,7 +2,7 @@ import React, { ChangeEvent, PureComponent } from 'react';
import { FormField } from '../FormField/FormField';
import { FormLabel } from '../FormLabel/FormLabel';
-import { Input } from '../Input/Input';
+import { Input } from '../Forms/Legacy/Input/Input';
import { Select } from '../Forms/Legacy/Select/Select';
import { MappingType, ValueMapping } from '@grafana/data';
diff --git a/packages/grafana-ui/src/components/ValueMappingsEditor/MappingRow.tsx b/packages/grafana-ui/src/components/ValueMappingsEditor/MappingRow.tsx
index f7c80432322..4f25c87b05d 100644
--- a/packages/grafana-ui/src/components/ValueMappingsEditor/MappingRow.tsx
+++ b/packages/grafana-ui/src/components/ValueMappingsEditor/MappingRow.tsx
@@ -2,7 +2,7 @@ import React, { ChangeEvent } from 'react';
import { HorizontalGroup } from '../Layout/Layout';
import { Select } from '../index';
import Forms from '../Forms';
-import { Input } from '../Forms/Input/Input';
+import { Input } from '../Input/Input';
import { MappingType, RangeMap, ValueMap, ValueMapping } from '@grafana/data';
import * as styleMixins from '../../themes/mixins';
import { useTheme } from '../../themes';
diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts
index 4d74af70a81..1dfa95fb442 100644
--- a/packages/grafana-ui/src/components/index.ts
+++ b/packages/grafana-ui/src/components/index.ts
@@ -21,7 +21,6 @@ export { SeriesColorPickerPopover, SeriesColorPickerPopoverWithTheme } from './C
export { PanelOptionsGroup } from './PanelOptionsGroup/PanelOptionsGroup';
export { PanelOptionsGrid } from './PanelOptionsGrid/PanelOptionsGrid';
export { LegacyValueMappingsEditor } from './ValueMappingsEditor/LegacyValueMappingsEditor';
-export { Switch } from './Switch/Switch';
export { EmptySearchResult } from './EmptySearchResult/EmptySearchResult';
export { PieChart, PieChartType } from './PieChart/PieChart';
export { UnitPicker } from './UnitPicker/UnitPicker';
@@ -103,8 +102,6 @@ export { DataLinkInput } from './DataLinks/DataLinkInput';
export { DataLinksContextMenu } from './DataLinks/DataLinksContextMenu';
export { SeriesIcon } from './Legend/SeriesIcon';
export { transformersUIRegistry } from './TransformersUI/transformers';
-export { TransformationRow } from './TransformersUI/TransformationRow';
-export { TransformationsEditor } from './TransformersUI/TransformationsEditor';
export { JSONFormatter } from './JSONFormatter/JSONFormatter';
export { JsonExplorer } from './JSONFormatter/json_explorer/json_explorer';
export { ErrorBoundary, ErrorBoundaryAlert } from './ErrorBoundary/ErrorBoundary';
@@ -139,7 +136,7 @@ export { ButtonSelect } from './Select/ButtonSelect';
export { HorizontalGroup, VerticalGroup, Container } from './Layout/Layout';
export { RadioButtonGroup } from './Forms/RadioButtonGroup/RadioButtonGroup';
-export { Input } from './Forms/Input/Input';
+export { Input } from './Input/Input';
// Legacy forms
@@ -150,9 +147,9 @@ import { NoOptionsMessage } from './Forms/Legacy/Select/NoOptionsMessage';
import { ButtonSelect } from './Forms/Legacy/Select/ButtonSelect';
//Input
-import { Input, LegacyInputStatus } from './Input/Input';
-// Export these until Enterprise migrations have been merged
-// export { Input, InputStatus}
+import { Input, LegacyInputStatus } from './Forms/Legacy/Input/Input';
+
+import { Switch } from './Switch/Switch';
const LegacyForms = {
Select,
@@ -161,6 +158,7 @@ const LegacyForms = {
NoOptionsMessage,
ButtonSelect,
Input,
+ Switch,
};
-
+export { Switch };
export { LegacyForms, LegacyInputStatus };
diff --git a/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts b/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts
index 67c8b7f1be8..cbaf7740d40 100644
--- a/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts
+++ b/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts
@@ -204,13 +204,13 @@ $input-bg: $input-black;
$input-bg-disabled: $dark-6;
$input-color: ${theme.colors.formInputText};
-$input-border-color: $dark-6;
+$input-border-color: ${theme.colors.gray15};
$input-box-shadow: inset 1px 0px 4px 0px rgba(150, 150, 150, 0.1);
-$input-border-focus: $dark-6 !default;
+$input-border-focus: ${theme.colors.blue95};
$input-box-shadow-focus: $blue-light !default;
$input-color-placeholder: ${theme.colors.formInputPlaceholderText};
-$input-label-bg: $gray-blue;
-$input-label-border-color: $dark-6;
+$input-label-bg: ${theme.colors.gray15};
+$input-label-border-color: ${theme.colors.gray15};
$input-color-select-arrow: $white;
// Search
diff --git a/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts b/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts
index 9e0d0be289e..351a518dee9 100644
--- a/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts
+++ b/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts
@@ -195,15 +195,15 @@ $btn-active-box-shadow: 0px 0px 4px rgba(234, 161, 51, 0.6);
$input-bg: $white;
$input-bg-disabled: $gray-5;
-$input-color: $dark-2;
-$input-border-color: $gray-5;
+$input-color: ${theme.colors.formInputText};
+$input-border-color: ${theme.colors.gray95};
$input-box-shadow: none;
-$input-border-focus: $gray-5 !default;
-$input-box-shadow-focus: $blue-light !default;
+$input-border-focus: ${theme.colors.blue95};
+$input-box-shadow-focus: ${theme.colors.blue95};
$input-color-placeholder: ${theme.colors.formInputPlaceholderText};
-$input-label-bg: $gray-5;
-$input-label-border-color: $gray-5;
-$input-color-select-arrow: $gray-1;
+$input-label-bg: ${theme.colors.gray95};
+$input-label-border-color: ${theme.colors.gray95};
+$input-color-select-arrow: ${theme.colors.gray60};
// search
$search-shadow: 0 1px 5px 0 $gray-5;
diff --git a/packages/grafana-ui/src/themes/_variables.scss.tmpl.ts b/packages/grafana-ui/src/themes/_variables.scss.tmpl.ts
index 392336aaf66..9f590f6c2e9 100644
--- a/packages/grafana-ui/src/themes/_variables.scss.tmpl.ts
+++ b/packages/grafana-ui/src/themes/_variables.scss.tmpl.ts
@@ -149,8 +149,8 @@ $input-border-radius-sm: 0 $border-radius-sm $border-radius-sm 0 !default;
$label-border-radius: $border-radius 0 0 $border-radius !default;
$label-border-radius-sm: $border-radius-sm 0 0 $border-radius-sm !default;
-$input-padding: ${theme.spacing.sm};
-$input-height: 35px !default;
+$input-padding: 0 ${theme.spacing.sm};
+$input-height: 32px !default;
$cursor-disabled: not-allowed !default;
diff --git a/packages/grafana-ui/src/themes/dark.ts b/packages/grafana-ui/src/themes/dark.ts
index c0ca4864c73..1a637d80745 100644
--- a/packages/grafana-ui/src/themes/dark.ts
+++ b/packages/grafana-ui/src/themes/dark.ts
@@ -64,7 +64,7 @@ const darkTheme: GrafanaTheme = {
bodyBg: basicColors.gray05,
pageBg: basicColors.gray10,
body: basicColors.gray4,
- text: basicColors.gray4,
+ text: basicColors.gray85,
textStrong: basicColors.white,
textWeak: basicColors.gray2,
textEmphasis: basicColors.gray5,
diff --git a/packages/grafana-ui/src/types/icon.ts b/packages/grafana-ui/src/types/icon.ts
index 95d86f6cd4b..1cbe7b9df05 100644
--- a/packages/grafana-ui/src/types/icon.ts
+++ b/packages/grafana-ui/src/types/icon.ts
@@ -30,7 +30,6 @@ export type IconName =
| 'tag-alt'
| 'forward'
| 'check'
- | 'add-panel'
| 'copy'
| 'lock'
| 'panel-add'
@@ -128,7 +127,6 @@ export const getAvailableIcons = (): IconName[] => [
'camera',
'forward',
'check',
- 'add-panel',
'copy',
'lock',
'panel-add',
diff --git a/packages/grafana-ui/src/utils/standardEditors.tsx b/packages/grafana-ui/src/utils/standardEditors.tsx
index 67c2fe86236..32025c9a90f 100644
--- a/packages/grafana-ui/src/utils/standardEditors.tsx
+++ b/packages/grafana-ui/src/utils/standardEditors.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import {
DataLink,
dataLinksOverrideProcessor,
- FieldPropertyEditorItem,
+ FieldConfigPropertyItem,
FieldType,
identityOverrideProcessor,
NumberFieldConfigSettings,
@@ -17,6 +17,7 @@ import {
ValueMapping,
ValueMappingFieldConfigSettings,
valueMappingsOverrideProcessor,
+ ThresholdsMode,
} from '@grafana/data';
import { NumberValueEditor, Forms, StringValueEditor, Select } from '../components';
import { ValueMappingsValueEditor } from '../components/OptionsUI/mappings';
@@ -30,8 +31,9 @@ import { StatsPickerEditor } from '../components/OptionsUI/stats';
* Returns collection of common field config properties definitions
*/
export const getStandardFieldConfigs = () => {
- const title: FieldPropertyEditorItem = {
+ const title: FieldConfigPropertyItem = {
id: 'title',
+ path: 'title',
name: 'Title',
description: "Field's title",
editor: standardEditorsRegistry.get('text').editor as any,
@@ -44,8 +46,9 @@ export const getStandardFieldConfigs = () => {
shouldApply: field => field.type !== FieldType.time,
};
- const unit: FieldPropertyEditorItem = {
+ const unit: FieldConfigPropertyItem = {
id: 'unit',
+ path: 'unit',
name: 'Unit',
description: 'Value units',
@@ -60,8 +63,9 @@ export const getStandardFieldConfigs = () => {
shouldApply: field => field.type === FieldType.number,
};
- const min: FieldPropertyEditorItem = {
+ const min: FieldConfigPropertyItem = {
id: 'min',
+ path: 'min',
name: 'Min',
description: 'Minimum expected value',
@@ -75,8 +79,9 @@ export const getStandardFieldConfigs = () => {
shouldApply: field => field.type === FieldType.number,
};
- const max: FieldPropertyEditorItem = {
+ const max: FieldConfigPropertyItem = {
id: 'max',
+ path: 'max',
name: 'Max',
description: 'Maximum expected value',
@@ -91,8 +96,9 @@ export const getStandardFieldConfigs = () => {
shouldApply: field => field.type === FieldType.number,
};
- const decimals: FieldPropertyEditorItem = {
+ const decimals: FieldConfigPropertyItem = {
id: 'decimals',
+ path: 'decimals',
name: 'Decimals',
description: 'Number of decimal to be shown for a value',
@@ -110,39 +116,43 @@ export const getStandardFieldConfigs = () => {
shouldApply: field => field.type === FieldType.number,
};
- const thresholds: FieldPropertyEditorItem = {
+ const thresholds: FieldConfigPropertyItem = {
id: 'thresholds',
+ path: 'thresholds',
name: 'Thresholds',
description: 'Manage thresholds',
editor: standardEditorsRegistry.get('thresholds').editor as any,
override: standardEditorsRegistry.get('thresholds').editor as any,
process: thresholdsOverrideProcessor,
-
- settings: {
- // ??
+ settings: {},
+ defaultValue: {
+ mode: ThresholdsMode.Absolute,
+ steps: [
+ { value: -Infinity, color: 'green' },
+ { value: 80, color: 'red' },
+ ],
},
-
shouldApply: field => field.type === FieldType.number,
};
- const mappings: FieldPropertyEditorItem = {
+ const mappings: FieldConfigPropertyItem = {
id: 'mappings',
+ path: 'mappings',
name: 'Value mappings',
description: 'Manage value mappings',
editor: standardEditorsRegistry.get('mappings').editor as any,
override: standardEditorsRegistry.get('mappings').editor as any,
process: valueMappingsOverrideProcessor,
- settings: {
- // ??
- },
-
+ settings: {},
+ defaultValue: [],
shouldApply: field => field.type === FieldType.number,
};
- const noValue: FieldPropertyEditorItem = {
+ const noValue: FieldConfigPropertyItem = {
id: 'noValue',
+ path: 'noValue',
name: 'No Value',
description: 'What to show when there is no value',
@@ -157,8 +167,9 @@ export const getStandardFieldConfigs = () => {
shouldApply: () => true,
};
- const links: FieldPropertyEditorItem = {
+ const links: FieldConfigPropertyItem = {
id: 'links',
+ path: 'links',
name: 'DataLinks',
description: 'Manage date links',
editor: standardEditorsRegistry.get('links').editor as any,
@@ -170,8 +181,9 @@ export const getStandardFieldConfigs = () => {
shouldApply: () => true,
};
- const color: FieldPropertyEditorItem = {
+ const color: FieldConfigPropertyItem = {
id: 'color',
+ path: 'color',
name: 'Color',
description: 'Customise color',
editor: standardEditorsRegistry.get('color').editor as any,
diff --git a/pkg/api/login.go b/pkg/api/login.go
index 7812c2ac4eb..df7621e61a8 100644
--- a/pkg/api/login.go
+++ b/pkg/api/login.go
@@ -46,8 +46,12 @@ func (hs *HTTPServer) validateRedirectTo(redirectTo string) error {
}
func (hs *HTTPServer) cookieOptionsFromCfg() middleware.CookieOptions {
+ path := "/"
+ if len(hs.Cfg.AppSubUrl) > 0 {
+ path = hs.Cfg.AppSubUrl
+ }
return middleware.CookieOptions{
- Path: hs.Cfg.AppSubUrl + "/",
+ Path: path,
Secure: hs.Cfg.CookieSecure,
SameSiteDisabled: hs.Cfg.CookieSameSiteDisabled,
SameSiteMode: hs.Cfg.CookieSameSiteMode,
diff --git a/pkg/api/login_test.go b/pkg/api/login_test.go
index 4ff624ac21a..a0c713ac46f 100644
--- a/pkg/api/login_test.go
+++ b/pkg/api/login_test.go
@@ -10,8 +10,6 @@ import (
"strings"
"testing"
- "github.com/grafana/grafana/pkg/services/licensing"
-
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
@@ -19,6 +17,7 @@ import (
"github.com/grafana/grafana/pkg/login"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/auth"
+ "github.com/grafana/grafana/pkg/services/licensing"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"github.com/stretchr/testify/assert"
@@ -109,12 +108,16 @@ func TestLoginErrorCookieApiEndpoint(t *testing.T) {
oauthError := errors.New("User not a member of one of the required organizations")
encryptedError, _ := util.Encrypt([]byte(oauthError.Error()), setting.SecretKey)
+ expCookiePath := "/"
+ if len(setting.AppSubUrl) > 0 {
+ expCookiePath = setting.AppSubUrl
+ }
cookie := http.Cookie{
Name: LoginErrorCookieName,
MaxAge: 60,
Value: hex.EncodeToString(encryptedError),
HttpOnly: true,
- Path: setting.AppSubUrl + "/",
+ Path: expCookiePath,
Secure: hs.Cfg.CookieSecure,
SameSite: hs.Cfg.CookieSameSiteMode,
}
@@ -210,12 +213,16 @@ func TestLoginViewRedirect(t *testing.T) {
hs.Cfg.AppUrl = c.appURL
hs.Cfg.AppSubUrl = c.appSubURL
t.Run(c.desc, func(t *testing.T) {
+ expCookiePath := "/"
+ if len(hs.Cfg.AppSubUrl) > 0 {
+ expCookiePath = hs.Cfg.AppSubUrl
+ }
cookie := http.Cookie{
Name: "redirect_to",
MaxAge: 60,
Value: c.url,
HttpOnly: true,
- Path: hs.Cfg.AppSubUrl + "/",
+ Path: expCookiePath,
Secure: hs.Cfg.CookieSecure,
SameSite: hs.Cfg.CookieSameSiteMode,
}
@@ -238,7 +245,7 @@ func TestLoginViewRedirect(t *testing.T) {
expCookieValue = ""
expCookieMaxAge = 0
}
- expCookie := fmt.Sprintf("redirect_to=%v; Path=%v; Max-Age=%v; HttpOnly; Secure", expCookieValue, hs.Cfg.AppSubUrl+"/", expCookieMaxAge)
+ expCookie := fmt.Sprintf("redirect_to=%v; Path=%v; Max-Age=%v; HttpOnly; Secure", expCookieValue, expCookiePath, expCookieMaxAge)
for _, cookieValue := range setCookie {
if cookieValue == expCookie {
redirectToCookieFound = true
@@ -332,12 +339,16 @@ func TestLoginPostRedirect(t *testing.T) {
hs.Cfg.AppUrl = c.appURL
hs.Cfg.AppSubUrl = c.appSubURL
t.Run(c.desc, func(t *testing.T) {
+ expCookiePath := "/"
+ if len(hs.Cfg.AppSubUrl) > 0 {
+ expCookiePath = hs.Cfg.AppSubUrl
+ }
cookie := http.Cookie{
Name: "redirect_to",
MaxAge: 60,
Value: c.url,
HttpOnly: true,
- Path: hs.Cfg.AppSubUrl + "/",
+ Path: expCookiePath,
Secure: hs.Cfg.CookieSecure,
SameSite: hs.Cfg.CookieSameSiteMode,
}
@@ -358,7 +369,7 @@ func TestLoginPostRedirect(t *testing.T) {
assert.True(t, ok, "Set-Cookie exists")
assert.Greater(t, len(setCookie), 0)
var redirectToCookieFound bool
- expCookieValue := fmt.Sprintf("redirect_to=; Path=%v; Max-Age=0; HttpOnly; Secure", hs.Cfg.AppSubUrl+"/")
+ expCookieValue := fmt.Sprintf("redirect_to=; Path=%v; Max-Age=0; HttpOnly; Secure", expCookiePath)
for _, cookieValue := range setCookie {
if cookieValue == expCookieValue {
redirectToCookieFound = true
diff --git a/pkg/components/gtime/gtime.go b/pkg/components/gtime/gtime.go
index 12a66ff1d7f..e1e00d965c9 100644
--- a/pkg/components/gtime/gtime.go
+++ b/pkg/components/gtime/gtime.go
@@ -1,12 +1,13 @@
package gtime
import (
+ "fmt"
"regexp"
"strconv"
"time"
)
-var dateUnitPattern = regexp.MustCompile(`(\d+)([wdy])`)
+var dateUnitPattern = regexp.MustCompile(`^(\d+)([dwMy])$`)
// ParseInterval parses an interval with support for all units that Grafana uses.
func ParseInterval(interval string) (time.Duration, error) {
@@ -18,14 +19,18 @@ func ParseInterval(interval string) (time.Duration, error) {
num, _ := strconv.Atoi(string(result[1]))
period := string(result[2])
+ now := time.Now()
- if period == `d` {
- return time.Hour * 24 * time.Duration(num), nil
+ switch period {
+ case "d":
+ return now.Sub(now.AddDate(0, 0, -num)), nil
+ case "w":
+ return now.Sub(now.AddDate(0, 0, -num*7)), nil
+ case "M":
+ return now.Sub(now.AddDate(0, -num, 0)), nil
+ case "y":
+ return now.Sub(now.AddDate(-num, 0, 0)), nil
}
- if period == `w` {
- return time.Hour * 24 * 7 * time.Duration(num), nil
- }
-
- return time.Hour * 24 * 7 * 365 * time.Duration(num), nil
+ return 0, fmt.Errorf("ParseInterval: invalid duration %q", interval)
}
diff --git a/pkg/components/gtime/gtime_test.go b/pkg/components/gtime/gtime_test.go
index 65489826af2..4dab30fbf6a 100644
--- a/pkg/components/gtime/gtime_test.go
+++ b/pkg/components/gtime/gtime_test.go
@@ -1,35 +1,38 @@
package gtime
import (
- "errors"
"fmt"
"testing"
"time"
+
+ "github.com/stretchr/testify/require"
)
func TestParseInterval(t *testing.T) {
+ now := time.Now()
+
tcs := []struct {
interval string
duration time.Duration
- err error
+ err string
}{
- {interval: "1d", duration: time.Hour * 24},
- {interval: "1w", duration: time.Hour * 24 * 7},
- {interval: "2w", duration: time.Hour * 24 * 7 * 2},
- {interval: "1y", duration: time.Hour * 24 * 7 * 365},
- {interval: "5y", duration: time.Hour * 24 * 7 * 365 * 5},
- {interval: "1M", err: errors.New("time: unknown unit M in duration 1M")},
- {interval: "invalid-duration", err: errors.New("time: invalid duration invalid-duration")},
+ {interval: "1d", duration: now.Sub(now.AddDate(0, 0, -1))},
+ {interval: "1w", duration: now.Sub(now.AddDate(0, 0, -7))},
+ {interval: "2w", duration: now.Sub(now.AddDate(0, 0, -14))},
+ {interval: "1M", duration: now.Sub(now.AddDate(0, -1, 0))},
+ {interval: "1y", duration: now.Sub(now.AddDate(-1, 0, 0))},
+ {interval: "5y", duration: now.Sub(now.AddDate(-5, 0, 0))},
+ {interval: "invalid-duration", err: "time: invalid duration invalid-duration"},
}
for i, tc := range tcs {
t.Run(fmt.Sprintf("testcase %d", i), func(t *testing.T) {
res, err := ParseInterval(tc.interval)
- if err != nil && err.Error() != tc.err.Error() {
- t.Fatalf("expected '%v' got '%v'", tc.err, err)
- }
- if res != tc.duration {
- t.Errorf("expected %v got %v", tc.duration, res)
+ if tc.err == "" {
+ require.NoError(t, err, "interval %q", tc.interval)
+ require.Equal(t, tc.duration, res, "interval %q", tc.interval)
+ } else {
+ require.EqualError(t, err, tc.err, "interval %q", tc.interval)
}
})
}
diff --git a/pkg/middleware/cookie.go b/pkg/middleware/cookie.go
index 895b98f4e8f..c81cb0dfa6c 100644
--- a/pkg/middleware/cookie.go
+++ b/pkg/middleware/cookie.go
@@ -14,8 +14,12 @@ type CookieOptions struct {
}
func newCookieOptions() CookieOptions {
+ path := "/"
+ if len(setting.AppSubUrl) > 0 {
+ path = setting.AppSubUrl
+ }
return CookieOptions{
- Path: setting.AppSubUrl + "/",
+ Path: path,
Secure: setting.CookieSecure,
SameSiteDisabled: setting.CookieSameSiteDisabled,
SameSiteMode: setting.CookieSameSiteMode,
diff --git a/pkg/middleware/middleware_test.go b/pkg/middleware/middleware_test.go
index 0536442beb1..5bbc60a9cbc 100644
--- a/pkg/middleware/middleware_test.go
+++ b/pkg/middleware/middleware_test.go
@@ -264,10 +264,14 @@ func TestMiddlewareContext(t *testing.T) {
}
for _, sameSitePolicy := range sameSitePolicies {
setting.CookieSameSiteMode = sameSitePolicy
+ expectedCookiePath := "/"
+ if len(setting.AppSubUrl) > 0 {
+ expectedCookiePath = setting.AppSubUrl
+ }
expectedCookie := &http.Cookie{
Name: setting.LoginCookieName,
Value: "rotated",
- Path: setting.AppSubUrl + "/",
+ Path: expectedCookiePath,
HttpOnly: true,
MaxAge: int(maxAge),
Secure: setting.CookieSecure,
@@ -291,10 +295,14 @@ func TestMiddlewareContext(t *testing.T) {
Convey("Should not set cookie with SameSite attribute when setting.CookieSameSiteDisabled is true", func() {
setting.CookieSameSiteDisabled = true
setting.CookieSameSiteMode = http.SameSiteLaxMode
+ expectedCookiePath := "/"
+ if len(setting.AppSubUrl) > 0 {
+ expectedCookiePath = setting.AppSubUrl
+ }
expectedCookie := &http.Cookie{
Name: setting.LoginCookieName,
Value: "rotated",
- Path: setting.AppSubUrl + "/",
+ Path: expectedCookiePath,
HttpOnly: true,
MaxAge: int(maxAge),
Secure: setting.CookieSecure,
diff --git a/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper_v2.go b/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper_v2.go
index d7bbfb0c274..ce3f05cb466 100644
--- a/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper_v2.go
+++ b/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper_v2.go
@@ -2,12 +2,13 @@ package wrapper
import (
"context"
+ "fmt"
"time"
+ "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2"
- "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/tsdb"
@@ -81,12 +82,24 @@ func (tw *DatasourcePluginWrapperV2) Query(ctx context.Context, ds *models.DataS
return nil, err
}
- return &tsdb.Response{
- Results: map[string]*tsdb.QueryResult{
- "": {
- Dataframes: pbRes.Frames,
- Meta: simplejson.NewFromAny(pbRes.Metadata),
- },
- },
- }, nil
+ tR := &tsdb.Response{
+ Results: make(map[string]*tsdb.QueryResult, len(pbRes.Responses)),
+ }
+
+ for refID, pRes := range pbRes.Responses {
+ qr := &tsdb.QueryResult{
+ RefId: refID,
+ Dataframes: pRes.Frames,
+ }
+ if len(pRes.JsonMeta) != 0 {
+ qr.Meta = simplejson.NewFromAny(pRes.JsonMeta)
+ }
+ if pRes.Error != "" {
+ qr.Error = fmt.Errorf(pRes.Error)
+ qr.ErrorString = pRes.Error
+ }
+ tR.Results[refID] = qr
+ }
+
+ return tR, nil
}
diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go
index 30d2a64d4c1..78680dff11e 100644
--- a/pkg/plugins/plugins.go
+++ b/pkg/plugins/plugins.go
@@ -78,6 +78,14 @@ func (pm *PluginManager) Init() error {
return errutil.Wrapf(err, "Failed to scan main plugin directory '%s'", plugDir)
}
+ pm.log.Info("Checking Bundled Plugins")
+ plugDir = path.Join(setting.HomePath, "plugins-bundled")
+ if _, err := os.Stat(plugDir); !os.IsNotExist(err) {
+ if err := pm.scan(plugDir); err != nil {
+ return errutil.Wrapf(err, "failed to scan bundled plugin directory '%s'", plugDir)
+ }
+ }
+
// check if plugins dir exists
if _, err := os.Stat(setting.PluginsPath); os.IsNotExist(err) {
if err = os.MkdirAll(setting.PluginsPath, os.ModePerm); err != nil {
diff --git a/pkg/plugins/transform_plugin.go b/pkg/plugins/transform_plugin.go
index bf04b8ae708..e040f1dc7e6 100644
--- a/pkg/plugins/transform_plugin.go
+++ b/pkg/plugins/transform_plugin.go
@@ -103,14 +103,25 @@ func (tw *TransformWrapper) Transform(ctx context.Context, query *tsdb.TsdbQuery
return nil, err
}
- return &tsdb.Response{
- Results: map[string]*tsdb.QueryResult{
- "": {
- Dataframes: pbRes.Frames,
- Meta: simplejson.NewFromAny(pbRes.Metadata),
- },
- },
- }, nil
+ tR := &tsdb.Response{
+ Results: make(map[string]*tsdb.QueryResult, len(pbRes.Responses)),
+ }
+ for refID, res := range pbRes.Responses {
+ tRes := &tsdb.QueryResult{
+ RefId: refID,
+ Dataframes: res.Frames,
+ }
+ if len(res.JsonMeta) != 0 {
+ tRes.Meta = simplejson.NewFromAny(res.JsonMeta)
+ }
+ if res.Error != "" {
+ tRes.Error = fmt.Errorf(res.Error)
+ tRes.ErrorString = res.Error
+ }
+ tR.Results[refID] = tRes
+ }
+
+ return tR, nil
}
type transformCallback struct {
@@ -168,18 +179,16 @@ func (s *transformCallback) QueryData(ctx context.Context, req *pluginv2.QueryDa
}
// Convert tsdb results (map) to plugin-model/datasource (slice) results.
// Only error, tsdb.Series, and encoded Dataframes responses are mapped.
-
- encodedFrames := [][]byte{}
+ responses := make(map[string]*pluginv2.DataResponse, len(tsdbRes.Results))
for refID, res := range tsdbRes.Results {
-
+ pRes := &pluginv2.DataResponse{}
if res.Error != nil {
- // TODO add Errors property to Frame
- encodedFrames = append(encodedFrames, nil)
- continue
+ pRes.Error = res.Error.Error()
}
if res.Dataframes != nil {
- encodedFrames = append(encodedFrames, res.Dataframes...)
+ pRes.Frames = res.Dataframes
+ responses[refID] = pRes
continue
}
@@ -193,8 +202,18 @@ func (s *transformCallback) QueryData(ctx context.Context, req *pluginv2.QueryDa
if err != nil {
return nil, err
}
- encodedFrames = append(encodedFrames, encFrame)
+ pRes.Frames = append(pRes.Frames, encFrame)
}
+ if res.Meta != nil {
+ b, err := res.Meta.MarshalJSON()
+ if err != nil {
+ s.logger.Error("failed to marhsal json metadata", err)
+ }
+ pRes.JsonMeta = b
+ }
+ responses[refID] = pRes
}
- return &pluginv2.QueryDataResponse{Frames: encodedFrames}, nil
+ return &pluginv2.QueryDataResponse{
+ Responses: responses,
+ }, nil
}
diff --git a/pkg/services/sqlstore/dashboard_version.go b/pkg/services/sqlstore/dashboard_version.go
index 602d58d5715..30e02be0c07 100644
--- a/pkg/services/sqlstore/dashboard_version.go
+++ b/pkg/services/sqlstore/dashboard_version.go
@@ -67,49 +67,68 @@ func GetDashboardVersions(query *models.GetDashboardVersionsQuery) error {
return nil
}
-const MAX_VERSIONS_TO_DELETE = 100
+const MAX_VERSIONS_TO_DELETE_PER_BATCH = 100
+const MAX_VERSION_DELETION_BATCHES = 50
func DeleteExpiredVersions(cmd *models.DeleteExpiredVersionsCommand) error {
- return inTransaction(func(sess *DBSession) error {
- versionsToKeep := setting.DashboardVersionsToKeep
- if versionsToKeep < 1 {
- versionsToKeep = 1
- }
+ return deleteExpiredVersions(cmd, MAX_VERSIONS_TO_DELETE_PER_BATCH, MAX_VERSION_DELETION_BATCHES)
+}
- // Idea of this query is finding version IDs to delete based on formula:
- // min_version_to_keep = min_version + (versions_count - versions_to_keep)
- // where version stats is processed for each dashboard. This guarantees that we keep at least versions_to_keep
- // versions, but in some cases (when versions are sparse) this number may be more.
- versionIdsToDeleteQuery := `SELECT id
- FROM dashboard_version, (
- SELECT dashboard_id, count(version) as count, min(version) as min
- FROM dashboard_version
- GROUP BY dashboard_id
- ) AS vtd
- WHERE dashboard_version.dashboard_id=vtd.dashboard_id
- AND version < vtd.min + vtd.count - ?`
+func deleteExpiredVersions(cmd *models.DeleteExpiredVersionsCommand, perBatch int, maxBatches int) error {
+ versionsToKeep := setting.DashboardVersionsToKeep
+ if versionsToKeep < 1 {
+ versionsToKeep = 1
+ }
- var versionIdsToDelete []interface{}
- err := sess.SQL(versionIdsToDeleteQuery, versionsToKeep).Find(&versionIdsToDelete)
- if err != nil {
- return err
- }
+ for batch := 0; batch < maxBatches; batch++ {
+ deleted := int64(0)
- // Don't delete more than MAX_VERSIONS_TO_DELETE version per time
- if len(versionIdsToDelete) > MAX_VERSIONS_TO_DELETE {
- versionIdsToDelete = versionIdsToDelete[:MAX_VERSIONS_TO_DELETE]
- }
+ batchErr := inTransaction(func(sess *DBSession) error {
+ // Idea of this query is finding version IDs to delete based on formula:
+ // min_version_to_keep = min_version + (versions_count - versions_to_keep)
+ // where version stats is processed for each dashboard. This guarantees that we keep at least versions_to_keep
+ // versions, but in some cases (when versions are sparse) this number may be more.
+ versionIdsToDeleteQuery := `SELECT id
+ FROM dashboard_version, (
+ SELECT dashboard_id, count(version) as count, min(version) as min
+ FROM dashboard_version
+ GROUP BY dashboard_id
+ ) AS vtd
+ WHERE dashboard_version.dashboard_id=vtd.dashboard_id
+ AND version < vtd.min + vtd.count - ?
+ LIMIT ?`
+
+ var versionIdsToDelete []interface{}
+ err := sess.SQL(versionIdsToDeleteQuery, versionsToKeep, perBatch).Find(&versionIdsToDelete)
+ if err != nil {
+ return err
+ }
+
+ if len(versionIdsToDelete) < 1 {
+ return nil
+ }
- if len(versionIdsToDelete) > 0 {
deleteExpiredSql := `DELETE FROM dashboard_version WHERE id IN (?` + strings.Repeat(",?", len(versionIdsToDelete)-1) + `)`
sqlOrArgs := append([]interface{}{deleteExpiredSql}, versionIdsToDelete...)
expiredResponse, err := sess.Exec(sqlOrArgs...)
if err != nil {
return err
}
- cmd.DeletedRows, _ = expiredResponse.RowsAffected()
+
+ deleted, err = expiredResponse.RowsAffected()
+ return err
+ })
+
+ if batchErr != nil {
+ return batchErr
}
- return nil
- })
+ cmd.DeletedRows += deleted
+
+ if deleted < int64(perBatch) {
+ break
+ }
+ }
+
+ return nil
}
diff --git a/pkg/services/sqlstore/dashboard_version_test.go b/pkg/services/sqlstore/dashboard_version_test.go
index a00b022e607..5b15db5ab07 100644
--- a/pkg/services/sqlstore/dashboard_version_test.go
+++ b/pkg/services/sqlstore/dashboard_version_test.go
@@ -131,7 +131,7 @@ func TestDeleteExpiredVersions(t *testing.T) {
So(query.Result[0].Version, ShouldEqual, versionsToWrite)
})
- Convey("Don't delete anything if there're no expired versions", func() {
+ Convey("Don't delete anything if there are no expired versions", func() {
setting.DashboardVersionsToKeep = versionsToWrite
err := DeleteExpiredVersions(&models.DeleteExpiredVersionsCommand{})
@@ -144,15 +144,18 @@ func TestDeleteExpiredVersions(t *testing.T) {
So(len(query.Result), ShouldEqual, versionsToWrite)
})
- Convey("Don't delete more than MAX_VERSIONS_TO_DELETE per iteration", func() {
- versionsToWriteBigNumber := MAX_VERSIONS_TO_DELETE + versionsToWrite
+ Convey("Don't delete more than MAX_VERSIONS_TO_DELETE_PER_BATCH * MAX_VERSION_DELETION_BATCHES per iteration", func() {
+ perBatch := 10
+ maxBatches := 10
+
+ versionsToWriteBigNumber := perBatch*maxBatches + versionsToWrite
for i := 0; i < versionsToWriteBigNumber-versionsToWrite; i++ {
updateTestDashboard(savedDash, map[string]interface{}{
"tags": "different-tag",
})
}
- err := DeleteExpiredVersions(&models.DeleteExpiredVersionsCommand{})
+ err := deleteExpiredVersions(&models.DeleteExpiredVersionsCommand{}, perBatch, maxBatches)
So(err, ShouldBeNil)
query := models.GetDashboardVersionsQuery{DashboardId: savedDash.Id, OrgId: 1, Limit: versionsToWriteBigNumber}
@@ -161,8 +164,8 @@ func TestDeleteExpiredVersions(t *testing.T) {
// Ensure we have at least versionsToKeep versions
So(len(query.Result), ShouldBeGreaterThanOrEqualTo, versionsToKeep)
- // Ensure we haven't deleted more than MAX_VERSIONS_TO_DELETE rows
- So(versionsToWriteBigNumber-len(query.Result), ShouldBeLessThanOrEqualTo, MAX_VERSIONS_TO_DELETE)
+ // Ensure we haven't deleted more than perBatch * maxBatches rows
+ So(versionsToWriteBigNumber-len(query.Result), ShouldBeLessThanOrEqualTo, perBatch*maxBatches)
})
})
}
diff --git a/pkg/services/sqlstore/user.go b/pkg/services/sqlstore/user.go
index 3b16bfeed80..b4f78a5a6f0 100644
--- a/pkg/services/sqlstore/user.go
+++ b/pkg/services/sqlstore/user.go
@@ -3,6 +3,7 @@ package sqlstore
import (
"context"
"fmt"
+ "sort"
"strconv"
"strings"
"time"
@@ -311,6 +312,27 @@ func GetUserProfile(query *models.GetUserProfileQuery) error {
return err
}
+type byOrgName []*models.UserOrgDTO
+
+// Len returns the length of an array of organisations.
+func (o byOrgName) Len() int {
+ return len(o)
+}
+
+// Swap swaps two indices of an array of organizations.
+func (o byOrgName) Swap(i, j int) {
+ o[i], o[j] = o[j], o[i]
+}
+
+// Less returns whether element i of an array of organizations is less than element j.
+func (o byOrgName) Less(i, j int) bool {
+ if strings.ToLower(o[i].Name) < strings.ToLower(o[j].Name) {
+ return true
+ }
+
+ return o[i].Name < o[j].Name
+}
+
func GetUserOrgList(query *models.GetUserOrgListQuery) error {
query.Result = make([]*models.UserOrgDTO, 0)
sess := x.Table("org_user")
@@ -319,6 +341,7 @@ func GetUserOrgList(query *models.GetUserOrgListQuery) error {
sess.Cols("org.name", "org_user.role", "org_user.org_id")
sess.OrderBy("org.name")
err := sess.Find(&query.Result)
+ sort.Sort(byOrgName(query.Result))
return err
}
diff --git a/pkg/tsdb/time_range.go b/pkg/tsdb/time_range.go
index 18e389e5993..085604a44f5 100644
--- a/pkg/tsdb/time_range.go
+++ b/pkg/tsdb/time_range.go
@@ -1,10 +1,11 @@
package tsdb
import (
- "fmt"
"strconv"
"strings"
"time"
+
+ "github.com/timberio/go-datemath"
)
func NewTimeRange(from, to string) *TimeRange {
@@ -79,38 +80,26 @@ func tryParseUnixMsEpoch(val string) (time.Time, bool) {
}
func (tr *TimeRange) ParseFrom() (time.Time, error) {
- if res, ok := tryParseUnixMsEpoch(tr.From); ok {
- return res, nil
- }
-
- fromRaw := strings.Replace(tr.From, "now-", "", 1)
- diff, err := time.ParseDuration("-" + fromRaw)
- if err != nil {
- return time.Time{}, err
- }
-
- return tr.now.Add(diff), nil
+ return parse(tr.From, tr.now, false)
}
func (tr *TimeRange) ParseTo() (time.Time, error) {
- if tr.To == "now" {
- return tr.now, nil
- } else if strings.HasPrefix(tr.To, "now-") {
- withoutNow := strings.Replace(tr.To, "now-", "", 1)
+ return parse(tr.To, tr.now, true)
+}
- diff, err := time.ParseDuration("-" + withoutNow)
- if err != nil {
- return time.Time{}, nil
- }
-
- return tr.now.Add(diff), nil
- }
-
- if res, ok := tryParseUnixMsEpoch(tr.To); ok {
+func parse(s string, now time.Time, withRoundUp bool) (time.Time, error) {
+ if res, ok := tryParseUnixMsEpoch(s); ok {
return res, nil
}
- return time.Time{}, fmt.Errorf("cannot parse to value %s", tr.To)
+ withoutNow := strings.Replace(s, "now-", "", 1)
+
+ diff, err := time.ParseDuration("-" + withoutNow)
+ if err != nil {
+ return datemath.ParseAndEvaluate(s, datemath.WithNow(now), datemath.WithRoundUp(withRoundUp))
+ }
+
+ return now.Add(diff), nil
}
// EpochPrecisionToMs converts epoch precision to millisecond, if needed.
diff --git a/pkg/tsdb/time_range_test.go b/pkg/tsdb/time_range_test.go
index 38b14768954..89de8e81463 100644
--- a/pkg/tsdb/time_range_test.go
+++ b/pkg/tsdb/time_range_test.go
@@ -1,7 +1,9 @@
package tsdb
import (
+ "strconv"
"testing"
+
"time"
. "github.com/smartystreets/goconvey/convey"
@@ -20,7 +22,8 @@ func TestTimeRange(t *testing.T) {
}
Convey("5m ago ", func() {
- fiveMinAgo, _ := time.ParseDuration("-5m")
+ fiveMinAgo, err := time.ParseDuration("-5m")
+ So(err, ShouldBeNil)
expected := now.Add(fiveMinAgo)
res, err := tr.ParseFrom()
@@ -43,7 +46,8 @@ func TestTimeRange(t *testing.T) {
}
Convey("5h ago ", func() {
- fiveHourAgo, _ := time.ParseDuration("-5h")
+ fiveHourAgo, err := time.ParseDuration("-5h")
+ So(err, ShouldBeNil)
expected := now.Add(fiveHourAgo)
res, err := tr.ParseFrom()
@@ -52,7 +56,8 @@ func TestTimeRange(t *testing.T) {
})
Convey("now-10m ", func() {
- tenMinAgo, _ := time.ParseDuration("-10m")
+ tenMinAgo, err := time.ParseDuration("-10m")
+ So(err, ShouldBeNil)
expected := now.Add(tenMinAgo)
res, err := tr.ParseTo()
So(err, ShouldBeNil)
@@ -60,7 +65,101 @@ func TestTimeRange(t *testing.T) {
})
})
- Convey("can parse unix epocs", func() {
+ now, err := time.Parse(time.RFC3339Nano, "2020-03-26T15:12:56.000Z")
+ So(err, ShouldBeNil)
+ Convey("Can parse now-1M/M, now-1M/M", func() {
+ tr := TimeRange{
+ From: "now-1M/M",
+ To: "now-1M/M",
+ now: now,
+ }
+
+ Convey("from now-1M/M ", func() {
+ expected, err := time.Parse(time.RFC3339Nano, "2020-02-01T00:00:00.000Z")
+ So(err, ShouldBeNil)
+
+ res, err := tr.ParseFrom()
+ So(err, ShouldBeNil)
+ So(res, ShouldEqual, expected)
+ })
+
+ Convey("to now-1M/M ", func() {
+ expected, err := time.Parse(time.RFC3339Nano, "2020-02-29T23:59:59.999Z")
+ So(err, ShouldBeNil)
+
+ res, err := tr.ParseTo()
+ So(err, ShouldBeNil)
+ So(res, ShouldEqual, expected)
+ })
+ })
+
+ Convey("Can parse now-3d, now+3w", func() {
+ tr := TimeRange{
+ From: "now-3d",
+ To: "now+3w",
+ now: now,
+ }
+
+ Convey("now-3d ", func() {
+ expected, err := time.Parse(time.RFC3339Nano, "2020-03-23T15:12:56.000Z")
+ So(err, ShouldBeNil)
+
+ res, err := tr.ParseFrom()
+ So(err, ShouldBeNil)
+ So(res, ShouldEqual, expected)
+ })
+
+ Convey("now+3w ", func() {
+ expected, err := time.Parse(time.RFC3339Nano, "2020-04-16T15:12:56.000Z")
+ So(err, ShouldBeNil)
+
+ res, err := tr.ParseTo()
+ So(err, ShouldBeNil)
+ So(res, ShouldEqual, expected)
+ })
+ })
+
+ Convey("Can parse 1960-02-01T07:00:00.000Z, 1965-02-03T08:00:00.000Z", func() {
+ tr := TimeRange{
+ From: "1960-02-01T07:00:00.000Z",
+ To: "1965-02-03T08:00:00.000Z",
+ now: now,
+ }
+
+ Convey("1960-02-01T07:00:00.000Z ", func() {
+ expected, err := time.Parse(time.RFC3339Nano, "1960-02-01T07:00:00.000Z")
+ So(err, ShouldBeNil)
+
+ res, err := tr.ParseFrom()
+ So(err, ShouldBeNil)
+ So(res, ShouldEqual, expected)
+ })
+
+ Convey("1965-02-03T08:00:00.000Z ", func() {
+ expected, err := time.Parse(time.RFC3339Nano, "1965-02-03T08:00:00.000Z")
+ So(err, ShouldBeNil)
+
+ res, err := tr.ParseTo()
+ So(err, ShouldBeNil)
+ So(res, ShouldEqual, expected)
+ })
+ })
+
+ Convey("Can parse negative unix epochs", func() {
+ from := time.Date(1960, 2, 1, 7, 0, 0, 0, time.UTC)
+ to := time.Date(1965, 2, 3, 8, 0, 0, 0, time.UTC)
+ tr := NewTimeRange(strconv.FormatInt(from.UnixNano()/int64(time.Millisecond), 10), strconv.FormatInt(to.UnixNano()/int64(time.Millisecond), 10))
+
+ res, err := tr.ParseFrom()
+ So(err, ShouldBeNil)
+ So(res, ShouldEqual, from)
+
+ res, err = tr.ParseTo()
+ So(err, ShouldBeNil)
+ So(res, ShouldEqual, to)
+ })
+
+ Convey("can parse unix epochs", func() {
var err error
tr := TimeRange{
From: "1474973725473",
diff --git a/plugins-bundled/.gitignore b/plugins-bundled/.gitignore
new file mode 100644
index 00000000000..f6b56a2cb7c
--- /dev/null
+++ b/plugins-bundled/.gitignore
@@ -0,0 +1,6 @@
+# packaged by toolkit
+dist
+coverage
+
+# Ignore external git configs
+external
diff --git a/plugins-bundled/README.md b/plugins-bundled/README.md
new file mode 100644
index 00000000000..11a5929e30e
--- /dev/null
+++ b/plugins-bundled/README.md
@@ -0,0 +1,6 @@
+Bundled Plugins
+===============
+
+Bundled plugins are built as true plugins, and managed by the grafana install.
+
+TODO: the packaging system should move all `dist` items to the root and remove sources.
diff --git a/plugins-bundled/internal/input-datasource/LICENSE b/plugins-bundled/internal/input-datasource/LICENSE
new file mode 100644
index 00000000000..d6456956733
--- /dev/null
+++ b/plugins-bundled/internal/input-datasource/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/public/app/plugins/datasource/input/README.md b/plugins-bundled/internal/input-datasource/README.md
similarity index 100%
rename from public/app/plugins/datasource/input/README.md
rename to plugins-bundled/internal/input-datasource/README.md
diff --git a/plugins-bundled/internal/input-datasource/package.json b/plugins-bundled/internal/input-datasource/package.json
new file mode 100644
index 00000000000..7835a546ef0
--- /dev/null
+++ b/plugins-bundled/internal/input-datasource/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "@grafana-plugins/input-datasource",
+ "version": "6.6.0-pre",
+ "description": "Input Datasource",
+ "repository": {
+ "type": "git",
+ "url": "http://github.com/grafana/grafana.git"
+ },
+ "scripts": {
+ "build": "grafana-toolkit plugin:build",
+ "test": "grafana-toolkit plugin:test",
+ "dev": "grafana-toolkit plugin:dev",
+ "watch": "grafana-toolkit plugin:dev --watch"
+ },
+ "author": "Grafana Labs",
+ "license": "Apache-2.0",
+ "devDependencies": {
+ "@grafana/data": "^6.6.0-pre",
+ "@grafana/ui": "^6.6.0-pre",
+ "@grafana/toolkit": "^6.6.0-pre"
+ }
+}
diff --git a/public/app/plugins/datasource/input/InputConfigEditor.tsx b/plugins-bundled/internal/input-datasource/src/InputConfigEditor.tsx
similarity index 100%
rename from public/app/plugins/datasource/input/InputConfigEditor.tsx
rename to plugins-bundled/internal/input-datasource/src/InputConfigEditor.tsx
diff --git a/public/app/plugins/datasource/input/InputDatasource.test.ts b/plugins-bundled/internal/input-datasource/src/InputDatasource.test.ts
similarity index 95%
rename from public/app/plugins/datasource/input/InputDatasource.test.ts
rename to plugins-bundled/internal/input-datasource/src/InputDatasource.test.ts
index 4fcde2a0eaf..23ff3a17b9a 100644
--- a/public/app/plugins/datasource/input/InputDatasource.test.ts
+++ b/plugins-bundled/internal/input-datasource/src/InputDatasource.test.ts
@@ -9,7 +9,7 @@ import {
readCSV,
} from '@grafana/data';
-import { getQueryOptions } from 'test/helpers/getQueryOptions';
+import { getQueryOptions } from './testHelpers';
describe('InputDatasource', () => {
const data = readCSV('a,b,c\n1,2,3\n4,5,6');
diff --git a/public/app/plugins/datasource/input/InputDatasource.ts b/plugins-bundled/internal/input-datasource/src/InputDatasource.ts
similarity index 98%
rename from public/app/plugins/datasource/input/InputDatasource.ts
rename to plugins-bundled/internal/input-datasource/src/InputDatasource.ts
index 5aadbcdd38b..b4b887de4cb 100644
--- a/public/app/plugins/datasource/input/InputDatasource.ts
+++ b/plugins-bundled/internal/input-datasource/src/InputDatasource.ts
@@ -99,7 +99,7 @@ function getLength(data?: DataFrameDTO | DataFrame) {
if (data.hasOwnProperty('length')) {
return (data as DataFrame).length;
}
- return data.fields[0].values.length;
+ return data.fields[0].values!.length;
}
export function describeDataFrame(data: Array): string {
diff --git a/public/app/plugins/datasource/input/InputQueryEditor.tsx b/plugins-bundled/internal/input-datasource/src/InputQueryEditor.tsx
similarity index 100%
rename from public/app/plugins/datasource/input/InputQueryEditor.tsx
rename to plugins-bundled/internal/input-datasource/src/InputQueryEditor.tsx
diff --git a/public/app/plugins/datasource/input/img/input.svg b/plugins-bundled/internal/input-datasource/src/img/input.svg
similarity index 100%
rename from public/app/plugins/datasource/input/img/input.svg
rename to plugins-bundled/internal/input-datasource/src/img/input.svg
diff --git a/public/app/plugins/datasource/input/module.ts b/plugins-bundled/internal/input-datasource/src/module.ts
similarity index 100%
rename from public/app/plugins/datasource/input/module.ts
rename to plugins-bundled/internal/input-datasource/src/module.ts
diff --git a/public/app/plugins/datasource/input/plugin.json b/plugins-bundled/internal/input-datasource/src/plugin.json
similarity index 85%
rename from public/app/plugins/datasource/input/plugin.json
rename to plugins-bundled/internal/input-datasource/src/plugin.json
index dbfa0ad489a..05b5a647a8b 100644
--- a/public/app/plugins/datasource/input/plugin.json
+++ b/plugins-bundled/internal/input-datasource/src/plugin.json
@@ -5,9 +5,6 @@
"state": "alpha",
"metrics": true,
- "alerting": false,
- "annotations": false,
- "logs": false,
"info": {
"description": "Data source that supports manual table & CSV input",
diff --git a/plugins-bundled/internal/input-datasource/src/testHelpers.ts b/plugins-bundled/internal/input-datasource/src/testHelpers.ts
new file mode 100644
index 00000000000..47b3308f60c
--- /dev/null
+++ b/plugins-bundled/internal/input-datasource/src/testHelpers.ts
@@ -0,0 +1,28 @@
+import { DataQueryRequest, DataQuery, CoreApp } from '@grafana/data';
+import { dateTime } from '@grafana/data';
+
+export function getQueryOptions(
+ options: Partial>
+): DataQueryRequest {
+ const raw = { from: 'now', to: 'now-1h' };
+ const range = { from: dateTime(), to: dateTime(), raw: raw };
+
+ const defaults: DataQueryRequest = {
+ requestId: 'TEST',
+ app: CoreApp.Dashboard,
+ range: range,
+ targets: [],
+ scopedVars: {},
+ timezone: 'browser',
+ panelId: 1,
+ dashboardId: 1,
+ interval: '60s',
+ intervalMs: 60000,
+ maxDataPoints: 500,
+ startTime: 0,
+ };
+
+ Object.assign(defaults, options);
+
+ return defaults;
+}
diff --git a/public/app/plugins/datasource/input/types.ts b/plugins-bundled/internal/input-datasource/src/types.ts
similarity index 100%
rename from public/app/plugins/datasource/input/types.ts
rename to plugins-bundled/internal/input-datasource/src/types.ts
diff --git a/public/app/plugins/datasource/input/utils.ts b/plugins-bundled/internal/input-datasource/src/utils.ts
similarity index 100%
rename from public/app/plugins/datasource/input/utils.ts
rename to plugins-bundled/internal/input-datasource/src/utils.ts
diff --git a/plugins-bundled/internal/input-datasource/tsconfig.json b/plugins-bundled/internal/input-datasource/tsconfig.json
new file mode 100644
index 00000000000..1a8b6a087a4
--- /dev/null
+++ b/plugins-bundled/internal/input-datasource/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "../../../packages/grafana-toolkit/src/config/tsconfig.plugin.json",
+ "include": ["src", "types"],
+ "compilerOptions": {
+ "rootDir": "./src",
+ "baseUrl": "./src",
+ "typeRoots": ["./node_modules/@types"]
+ }
+}
diff --git a/public/app/core/components/OrgSwitcher.tsx b/public/app/core/components/OrgSwitcher.tsx
index 6fab2a0f284..b608ed4ee11 100644
--- a/public/app/core/components/OrgSwitcher.tsx
+++ b/public/app/core/components/OrgSwitcher.tsx
@@ -26,9 +26,7 @@ export class OrgSwitcher extends React.PureComponent {
getUserOrgs = async () => {
const orgs: UserOrgDTO[] = await getBackendSrv().get('/api/user/orgs');
- this.setState({
- orgs: orgs.sort((a, b) => a.orgId - b.orgId),
- });
+ this.setState({ orgs });
};
setCurrentOrg = async (org: UserOrgDTO) => {
diff --git a/public/app/core/components/info_popover.ts b/public/app/core/components/info_popover.ts
index 72fad71131f..9c712b19849 100644
--- a/public/app/core/components/info_popover.ts
+++ b/public/app/core/components/info_popover.ts
@@ -6,7 +6,7 @@ import Drop from 'tether-drop';
export function infoPopover() {
return {
restrict: 'E',
- template: ` `,
+ template: ` `,
transclude: true,
link: (scope: any, elem: any, attrs: any, ctrl: any, transclude: any) => {
const offset = attrs.offset || '0 -10px';
diff --git a/public/app/core/reducers/root.test.ts b/public/app/core/reducers/root.test.ts
index c777f4773fe..209b2558c7d 100644
--- a/public/app/core/reducers/root.test.ts
+++ b/public/app/core/reducers/root.test.ts
@@ -8,13 +8,13 @@ import { cleanUpAction } from '../actions/cleanUp';
import { initialTeamsState, teamsLoaded } from '../../features/teams/state/reducers';
jest.mock('@grafana/runtime', () => ({
+ ...jest.requireActual('@grafana/runtime'),
config: {
bootData: {
navTree: [] as NavModelItem[],
user: {},
},
},
- DataSourceWithBackend: jest.fn(),
}));
describe('recursiveCleanState', () => {
diff --git a/public/app/core/utils/fetch.test.ts b/public/app/core/utils/fetch.test.ts
index ff80ba52c50..71731d11a9c 100644
--- a/public/app/core/utils/fetch.test.ts
+++ b/public/app/core/utils/fetch.test.ts
@@ -27,16 +27,17 @@ describe('parseUrlFromOptions', () => {
describe('parseInitFromOptions', () => {
it.each`
- method | data | expected
- ${undefined} | ${undefined} | ${{ method: undefined, headers: { map: { accept: 'application/json, text/plain, */*' } }, body: undefined }}
- ${'GET'} | ${undefined} | ${{ method: 'GET', headers: { map: { accept: 'application/json, text/plain, */*' } }, body: undefined }}
- ${'POST'} | ${{ id: '0' }} | ${{ method: 'POST', headers: { map: { 'content-type': 'application/json', accept: 'application/json, text/plain, */*' } }, body: '{"id":"0"}' }}
- ${'PUT'} | ${{ id: '0' }} | ${{ method: 'PUT', headers: { map: { 'content-type': 'application/json', accept: 'application/json, text/plain, */*' } }, body: '{"id":"0"}' }}
- ${'monkey'} | ${undefined} | ${{ method: 'monkey', headers: { map: { accept: 'application/json, text/plain, */*' } }, body: undefined }}
+ method | data | withCredentials | expected
+ ${undefined} | ${undefined} | ${undefined} | ${{ method: undefined, headers: { map: { accept: 'application/json, text/plain, */*' } }, body: undefined }}
+ ${'GET'} | ${undefined} | ${undefined} | ${{ method: 'GET', headers: { map: { accept: 'application/json, text/plain, */*' } }, body: undefined }}
+ ${'POST'} | ${{ id: '0' }} | ${undefined} | ${{ method: 'POST', headers: { map: { 'content-type': 'application/json', accept: 'application/json, text/plain, */*' } }, body: '{"id":"0"}' }}
+ ${'PUT'} | ${{ id: '0' }} | ${undefined} | ${{ method: 'PUT', headers: { map: { 'content-type': 'application/json', accept: 'application/json, text/plain, */*' } }, body: '{"id":"0"}' }}
+ ${'monkey'} | ${undefined} | ${undefined} | ${{ method: 'monkey', headers: { map: { accept: 'application/json, text/plain, */*' } }, body: undefined }}
+ ${'GET'} | ${undefined} | ${true} | ${{ method: 'GET', headers: { map: { accept: 'application/json, text/plain, */*' } }, body: undefined, credentials: 'include' }}
`(
- "when called with method: '$method' and data: '$data' then result should be '$expected'",
- ({ method, data, expected }) => {
- expect(parseInitFromOptions({ method, data, url: '' })).toEqual(expected);
+ "when called with method: '$method', data: '$data' and withCredentials: '$withCredentials' then result should be '$expected'",
+ ({ method, data, withCredentials, expected }) => {
+ expect(parseInitFromOptions({ method, data, withCredentials, url: '' })).toEqual(expected);
}
);
});
diff --git a/public/app/core/utils/fetch.ts b/public/app/core/utils/fetch.ts
index e7fa4b84dbb..fb706f52ec3 100644
--- a/public/app/core/utils/fetch.ts
+++ b/public/app/core/utils/fetch.ts
@@ -7,6 +7,15 @@ export const parseInitFromOptions = (options: BackendSrvRequest): RequestInit =>
const isAppJson = isContentTypeApplicationJson(headers);
const body = parseBody(options, isAppJson);
+ if (options?.withCredentials) {
+ return {
+ method,
+ headers,
+ body,
+ credentials: 'include',
+ };
+ }
+
return {
method,
headers,
diff --git a/public/app/features/admin/__snapshots__/ServerStats.test.tsx.snap b/public/app/features/admin/__snapshots__/ServerStats.test.tsx.snap
index 1c8ace0e59d..b02fb952d51 100644
--- a/public/app/features/admin/__snapshots__/ServerStats.test.tsx.snap
+++ b/public/app/features/admin/__snapshots__/ServerStats.test.tsx.snap
@@ -100,7 +100,7 @@ exports[`ServerStats Should render table with stats 1`] = `
className="css-payll4"
>
diff --git a/public/app/features/api-keys/ApiKeysPage.tsx b/public/app/features/api-keys/ApiKeysPage.tsx
index ce5d2f16bc2..a1cc5eb3746 100644
--- a/public/app/features/api-keys/ApiKeysPage.tsx
+++ b/public/app/features/api-keys/ApiKeysPage.tsx
@@ -13,16 +13,8 @@ import ApiKeysAddedModal from './ApiKeysAddedModal';
import config from 'app/core/config';
import appEvents from 'app/core/app_events';
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
-import {
- DeleteButton,
- EventsWithValidation,
- FormLabel,
- LegacyForms,
- Switch,
- ValidationEvents,
- Icon,
-} from '@grafana/ui';
-const { Input } = LegacyForms;
+import { DeleteButton, EventsWithValidation, FormLabel, LegacyForms, ValidationEvents, Icon } from '@grafana/ui';
+const { Input, Switch } = LegacyForms;
import { dateTime, isDateTime, NavModel } from '@grafana/data';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
import { store } from 'app/store/store';
diff --git a/public/app/features/dashboard/components/DashExportModal/DashboardExporter.test.ts b/public/app/features/dashboard/components/DashExportModal/DashboardExporter.test.ts
index f54f112a788..5e79995f639 100644
--- a/public/app/features/dashboard/components/DashExportModal/DashboardExporter.test.ts
+++ b/public/app/features/dashboard/components/DashExportModal/DashboardExporter.test.ts
@@ -12,6 +12,7 @@ jest.mock('app/core/store', () => {
});
jest.mock('@grafana/runtime', () => ({
+ ...jest.requireActual('@grafana/runtime'),
getDataSourceSrv: () => ({
get: jest.fn(arg => getStub(arg)),
}),
@@ -22,7 +23,6 @@ jest.mock('@grafana/runtime', () => ({
newVariables: false,
},
},
- DataSourceWithBackend: jest.fn(),
}));
describe('given dashboard with repeated panels', () => {
diff --git a/public/app/features/dashboard/components/Inspector/PanelInspector.tsx b/public/app/features/dashboard/components/Inspector/PanelInspector.tsx
index 82c0ca5671d..496f4416d0d 100644
--- a/public/app/features/dashboard/components/Inspector/PanelInspector.tsx
+++ b/public/app/features/dashboard/components/Inspector/PanelInspector.tsx
@@ -181,7 +181,7 @@ export class PanelInspector extends PureComponent {
const processed = applyFieldOverrides({
data,
theme: config.theme,
- fieldOptions: { defaults: {}, overrides: [] },
+ fieldConfig: { defaults: {}, overrides: [] },
replaceVariables: (value: string) => {
return value;
},
diff --git a/public/app/features/dashboard/components/PanelEditor/DynamicConfigValueEditor.tsx b/public/app/features/dashboard/components/PanelEditor/DynamicConfigValueEditor.tsx
index 9e055348c27..90749d48530 100644
--- a/public/app/features/dashboard/components/PanelEditor/DynamicConfigValueEditor.tsx
+++ b/public/app/features/dashboard/components/PanelEditor/DynamicConfigValueEditor.tsx
@@ -1,11 +1,11 @@
import React from 'react';
-import { DynamicConfigValue, FieldConfigEditorRegistry, FieldOverrideContext, GrafanaTheme } from '@grafana/data';
+import { DynamicConfigValue, FieldConfigOptionsRegistry, FieldOverrideContext, GrafanaTheme } from '@grafana/data';
import { FieldConfigItemHeaderTitle, selectThemeVariant, stylesFactory, useTheme } from '@grafana/ui';
import { css } from 'emotion';
interface DynamicConfigValueEditorProps {
property: DynamicConfigValue;
- editorsRegistry: FieldConfigEditorRegistry;
+ registry: FieldConfigOptionsRegistry;
onChange: (value: DynamicConfigValue) => void;
context: FieldOverrideContext;
onRemove: () => void;
@@ -14,13 +14,13 @@ interface DynamicConfigValueEditorProps {
export const DynamicConfigValueEditor: React.FC = ({
property,
context,
- editorsRegistry,
+ registry,
onChange,
onRemove,
}) => {
const theme = useTheme();
const styles = getStyles(theme);
- const item = editorsRegistry?.getIfExists(property.prop);
+ const item = registry?.getIfExists(property.id);
if (!item) {
return null;
diff --git a/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx b/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx
index f04ebff88ce..6247c26e44f 100644
--- a/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx
+++ b/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx
@@ -3,12 +3,10 @@ import cloneDeep from 'lodash/cloneDeep';
import {
FieldConfigSource,
DataFrame,
- FieldPropertyEditorItem,
+ FieldConfigPropertyItem,
VariableSuggestionsScope,
- standardFieldConfigEditorRegistry,
PanelPlugin,
SelectableValue,
- FieldConfigProperty,
} from '@grafana/data';
import { Forms, fieldMatchersUI, ValuePicker, useTheme } from '@grafana/ui';
import { getDataLinksVariableSuggestions } from '../../../panel/panellinks/link_srv';
@@ -18,7 +16,6 @@ import { css } from 'emotion';
interface Props {
plugin: PanelPlugin;
config: FieldConfigSource;
- include?: FieldConfigProperty[]; // Ordered list of which fields should be shown/included
onChange: (config: FieldConfigSource) => void;
/* Helpful for IntelliSense */
data: DataFrame[];
@@ -62,33 +59,12 @@ export const OverrideFieldConfigEditor: React.FC = props => {
const renderOverrides = () => {
const { config, data, plugin } = props;
- const { customFieldConfigs } = plugin;
+ const { fieldConfigRegistry } = plugin;
if (config.overrides.length === 0) {
return null;
}
- let configPropertiesOptions = plugin.standardFieldConfigProperties.map(i => {
- const editor = standardFieldConfigEditorRegistry.get(i);
- return {
- label: editor.name,
- value: editor.id,
- description: editor.description,
- custom: false,
- };
- });
-
- if (customFieldConfigs) {
- configPropertiesOptions = configPropertiesOptions.concat(
- customFieldConfigs.list().map(i => ({
- label: i.name,
- value: i.id,
- description: i.description,
- custom: true,
- }))
- );
- }
-
return (
{config.overrides.map((o, i) => {
@@ -100,8 +76,7 @@ export const OverrideFieldConfigEditor: React.FC
= props => {
override={o}
onChange={value => onOverrideChange(i, value)}
onRemove={() => onOverrideRemove(i)}
- configPropertiesOptions={configPropertiesOptions}
- customPropertiesRegistry={customFieldConfigs}
+ registry={fieldConfigRegistry}
/>
);
})}
@@ -135,7 +110,7 @@ export const OverrideFieldConfigEditor: React.FC = props => {
);
};
-export const DefaultFieldConfigEditor: React.FC = ({ include, data, onChange, config, plugin }) => {
+export const DefaultFieldConfigEditor: React.FC = ({ data, onChange, config, plugin }) => {
const setDefaultValue = useCallback(
(name: string, value: any, custom: boolean) => {
const defaults = { ...config.defaults };
@@ -167,16 +142,20 @@ export const DefaultFieldConfigEditor: React.FC = ({ include, data, onCha
);
const renderEditor = useCallback(
- (item: FieldPropertyEditorItem, custom: boolean) => {
+ (item: FieldConfigPropertyItem) => {
const defaults = config.defaults;
- const value = custom ? (defaults.custom ? defaults.custom[item.id] : undefined) : (defaults as any)[item.id];
+ const value = item.isCustom
+ ? defaults.custom
+ ? defaults.custom[item.path]
+ : undefined
+ : (defaults as any)[item.path];
return (
-
+
setDefaultValue(item.id, v, custom)}
+ onChange={v => setDefaultValue(item.path, v, item.isCustom)}
context={{
data,
getSuggestions: (scope?: VariableSuggestionsScope) => getDataLinksVariableSuggestions(data, scope),
@@ -188,28 +167,6 @@ export const DefaultFieldConfigEditor: React.FC = ({ include, data, onCha
[config]
);
- const renderStandardConfigs = useCallback(() => {
- if (include && include.length === 0) {
- return null;
- }
- if (include) {
- return <>{include.map(f => renderEditor(standardFieldConfigEditorRegistry.get(f), false))}>;
- }
- return <>{standardFieldConfigEditorRegistry.list().map(f => renderEditor(f, false))}>;
- }, [plugin, config]);
-
- const renderCustomConfigs = useCallback(() => {
- if (!plugin.customFieldConfigs) {
- return null;
- }
-
- return plugin.customFieldConfigs.list().map(f => renderEditor(f, true));
- }, [plugin, config]);
-
- return (
- <>
- {plugin.customFieldConfigs && renderCustomConfigs()}
- {renderStandardConfigs()}
- >
- );
+ // render all field configs
+ return <>{plugin.fieldConfigRegistry.list().map(renderEditor)}>;
};
diff --git a/public/app/features/dashboard/components/PanelEditor/GeneralPanelOptions.tsx b/public/app/features/dashboard/components/PanelEditor/GeneralPanelOptions.tsx
deleted file mode 100644
index 7fb337a8cea..00000000000
--- a/public/app/features/dashboard/components/PanelEditor/GeneralPanelOptions.tsx
+++ /dev/null
@@ -1,105 +0,0 @@
-import React, { FC, useMemo } from 'react';
-import { PanelModel } from '../../state';
-import { SelectableValue } from '@grafana/data';
-import { Forms, Select, DataLinksInlineEditor, Input } from '@grafana/ui';
-import { OptionsGroup } from './OptionsGroup';
-import { getPanelLinksVariableSuggestions } from '../../../panel/panellinks/link_srv';
-import { getVariables } from '../../../variables/state/selectors';
-
-export const GeneralPanelOptions: FC<{
- panel: PanelModel;
- onPanelConfigChange: (configKey: string, value: any) => void;
-}> = ({ panel, onPanelConfigChange }) => {
- const linkVariablesSuggestions = useMemo(() => getPanelLinksVariableSuggestions(), []);
-
- const variableOptions = getVariableOptions();
- const directionOptions = [
- { label: 'Horizontal', value: 'h' },
- { label: 'Vertical', value: 'v' },
- ];
-
- const maxPerRowOptions = [2, 3, 4, 6, 8, 12].map(value => ({ label: value.toString(), value }));
-
- return (
-
-
-
- onPanelConfigChange('title', e.currentTarget.value)} />
-
-
- onPanelConfigChange('description', e.currentTarget.value)}
- />
-
-
- onPanelConfigChange('transparent', e.currentTarget.checked)}
- />
-
-
-
- onPanelConfigChange('links', links)}
- suggestions={linkVariablesSuggestions}
- data={[]}
- />
-
-
-
- onPanelConfigChange('repeat', value.value)}
- options={variableOptions}
- />
-
- {panel.repeat && (
-
- onPanelConfigChange('repeatDirection', value)}
- />
-
- )}
-
- {panel.repeat && panel.repeatDirection === 'h' && (
-
- onPanelConfigChange('maxPerRow', value.value)}
- />
-
- )}
-
-
- );
-};
-
-function getVariableOptions(): Array> {
- const options = getVariables().map((item: any) => {
- return { label: item.name, value: item.name };
- });
-
- if (options.length === 0) {
- options.unshift({
- label: 'No template variables found',
- value: null,
- });
- }
-
- options.unshift({
- label: 'Disable repeating',
- value: null,
- });
-
- return options;
-}
diff --git a/public/app/features/dashboard/components/PanelEditor/OptionsGroup.tsx b/public/app/features/dashboard/components/PanelEditor/OptionsGroup.tsx
index d851ceb3637..c3042c5cdc4 100644
--- a/public/app/features/dashboard/components/PanelEditor/OptionsGroup.tsx
+++ b/public/app/features/dashboard/components/PanelEditor/OptionsGroup.tsx
@@ -5,12 +5,13 @@ import { useTheme, Icon, stylesFactory } from '@grafana/ui';
interface Props {
title: string;
+ defaultToClosed?: boolean;
}
-export const OptionsGroup: FC = ({ title, children }) => {
- const [isExpanded, toggleExpand] = useState(true);
+export const OptionsGroup: FC = ({ title, children, defaultToClosed }) => {
+ const [isExpanded, toggleExpand] = useState(defaultToClosed ? false : true);
const theme = useTheme();
- const styles = getStyles(theme);
+ const styles = getStyles(theme, isExpanded);
return (
@@ -25,7 +26,7 @@ export const OptionsGroup: FC
= ({ title, children }) => {
);
};
-const getStyles = stylesFactory((theme: GrafanaTheme) => {
+const getStyles = stylesFactory((theme: GrafanaTheme, isExpanded: boolean) => {
return {
box: css`
border-bottom: 1px solid ${theme.colors.pageHeaderBorder};
@@ -40,6 +41,7 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
justify-content: space-between;
align-items: center;
padding: ${theme.spacing.sm} ${theme.spacing.md};
+ color: ${isExpanded ? theme.colors.text : theme.colors.formLabel};
font-weight: ${theme.typography.weight.semibold};
&:hover {
diff --git a/public/app/features/dashboard/components/PanelEditor/OptionsPaneContent.tsx b/public/app/features/dashboard/components/PanelEditor/OptionsPaneContent.tsx
index c461cd32f07..96b8da80cb0 100644
--- a/public/app/features/dashboard/components/PanelEditor/OptionsPaneContent.tsx
+++ b/public/app/features/dashboard/components/PanelEditor/OptionsPaneContent.tsx
@@ -1,6 +1,6 @@
import React, { useCallback, useState, CSSProperties } from 'react';
import Transition from 'react-transition-group/Transition';
-import { FieldConfigSource, GrafanaTheme, PanelData, PanelPlugin } from '@grafana/data';
+import { FieldConfigSource, GrafanaTheme, PanelData, PanelPlugin, SelectableValue } from '@grafana/data';
import { DashboardModel, PanelModel } from '../../state';
import {
CustomScrollbar,
@@ -8,22 +8,22 @@ import {
Tab,
TabContent,
TabsBar,
+ Select,
useTheme,
Container,
Icon,
Input,
} from '@grafana/ui';
import { DefaultFieldConfigEditor, OverrideFieldConfigEditor } from './FieldConfigEditor';
-import { AngularPanelOptions } from './AngularPanelOptions';
import { css } from 'emotion';
-import { GeneralPanelOptions } from './GeneralPanelOptions';
-import { PanelOptionsEditor } from './PanelOptionsEditor';
+import { PanelOptionsTab } from './PanelOptionsTab';
import { DashNavButton } from 'app/features/dashboard/components/DashNav/DashNavButton';
export const OptionsPaneContent: React.FC<{
- plugin?: PanelPlugin;
+ plugin: PanelPlugin;
panel: PanelModel;
data: PanelData;
+ width: number;
dashboard: DashboardModel;
onClose: () => void;
onFieldConfigsChange: (config: FieldConfigSource) => void;
@@ -33,6 +33,7 @@ export const OptionsPaneContent: React.FC<{
plugin,
panel,
data,
+ width,
onFieldConfigsChange,
onPanelOptionsChanged,
onPanelConfigChange,
@@ -41,7 +42,7 @@ export const OptionsPaneContent: React.FC<{
}) => {
const theme = useTheme();
const styles = getStyles(theme);
- const [activeTab, setActiveTab] = useState('defaults');
+ const [activeTab, setActiveTab] = useState('options');
const [isSearching, setSearchMode] = useState(false);
const renderFieldOptions = useCallback(
@@ -54,13 +55,11 @@ export const OptionsPaneContent: React.FC<{
return (
- {renderCustomPanelSettings(plugin)}
);
@@ -88,49 +87,54 @@ export const OptionsPaneContent: React.FC<{
[data, plugin, panel, onFieldConfigsChange]
);
- const renderCustomPanelSettings = useCallback(
- (plugin: PanelPlugin) => {
- const editors: JSX.Element[] = [];
- if (plugin.editor && panel) {
- editors.push(
-
-
+ {plugin && (
+
+
+
-
- );
- }
-
- // When editor created declaratively
- if (plugin.optionEditors && panel) {
- editors.push(
-
- );
- }
-
- if (editors.length > 0) {
- return editors;
- }
-
- return (
-
-
+
+
+
+ {activeTab === 'options' && (
+
+ )}
+ {activeTab === 'defaults' && renderFieldOptions(plugin)}
+ {activeTab === 'overrides' && renderFieldOverrideOptions(plugin)}
+
+
- );
- },
- [data, plugin, panel, onFieldConfigsChange]
+ )}
+
);
+};
- const renderSearchInput = useCallback(() => {
+export const TabsBarContent: React.FC<{
+ width: number;
+ isSearching: boolean;
+ activeTab: string;
+ styles: OptionsPaneStyles;
+ onClose: () => void;
+ setSearchMode: (mode: boolean) => void;
+ setActiveTab: (tab: string) => void;
+}> = ({ width, isSearching, activeTab, onClose, setSearchMode, setActiveTab, styles }) => {
+ if (isSearching) {
const defaultStyles = {
transition: 'width 50ms ease-in-out',
width: '50%',
@@ -163,56 +167,64 @@ export const OptionsPaneContent: React.FC<{
}}
);
- }, []);
+ }
return (
-
- {plugin && (
-
-
- {isSearching && renderSearchInput()}
- {!isSearching && (
- <>
- setActiveTab('defaults')} />
- setActiveTab('overrides')}
- />
- setActiveTab('panel')} />
-
-
- setSearchMode(true)}
- />
-
-
-
-
- >
- )}
-
-
-
- {activeTab === 'defaults' && renderFieldOptions(plugin)}
- {activeTab === 'overrides' && renderFieldOverrideOptions(plugin)}
- {activeTab === 'panel' && }
-
-
+ <>
+ {width < 352 ? (
+
+ v.value === activeTab)}
+ onChange={v => {
+ setActiveTab(v.value);
+ }}
+ />
+ ) : (
+ <>
+ {tabSelections.map(item => (
+
setActiveTab(item.value)}
+ />
+ ))}
+
+ >
)}
-
+
+
+ setSearchMode(true)}
+ />
+
+
+
+
+ >
);
};
+const tabSelections: Array
> = [
+ {
+ label: 'Panel',
+ value: 'options',
+ },
+ {
+ label: 'Fields',
+ value: 'defaults',
+ },
+ {
+ label: 'Overrides',
+ value: 'overrides',
+ },
+];
+
const getStyles = stylesFactory((theme: GrafanaTheme) => {
return {
wrapper: css`
@@ -274,3 +286,5 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
`,
};
});
+
+type OptionsPaneStyles = ReturnType;
diff --git a/public/app/features/dashboard/components/PanelEditor/OverrideEditor.tsx b/public/app/features/dashboard/components/PanelEditor/OverrideEditor.tsx
index 9b707c62267..28608606a55 100644
--- a/public/app/features/dashboard/components/PanelEditor/OverrideEditor.tsx
+++ b/public/app/features/dashboard/components/PanelEditor/OverrideEditor.tsx
@@ -3,10 +3,8 @@ import {
ConfigOverrideRule,
DataFrame,
DynamicConfigValue,
- FieldConfigEditorRegistry,
- standardFieldConfigEditorRegistry,
+ FieldConfigOptionsRegistry,
VariableSuggestionsScope,
- SelectableValue,
GrafanaTheme,
} from '@grafana/data';
import { fieldMatchersUI, stylesFactory, useTheme, ValuePicker, selectThemeVariant } from '@grafana/ui';
@@ -21,18 +19,10 @@ interface OverrideEditorProps {
override: ConfigOverrideRule;
onChange: (config: ConfigOverrideRule) => void;
onRemove: () => void;
- customPropertiesRegistry?: FieldConfigEditorRegistry;
- configPropertiesOptions: Array>;
+ registry: FieldConfigOptionsRegistry;
}
-export const OverrideEditor: React.FC = ({
- data,
- override,
- onChange,
- onRemove,
- customPropertiesRegistry,
- configPropertiesOptions,
-}) => {
+export const OverrideEditor: React.FC = ({ data, override, onChange, onRemove, registry }) => {
const theme = useTheme();
const onMatcherConfigChange = useCallback(
(matcherConfig: any) => {
@@ -59,10 +49,9 @@ export const OverrideEditor: React.FC = ({
);
const onDynamicConfigValueAdd = useCallback(
- (prop: string, custom?: boolean) => {
+ (id: string) => {
const propertyConfig: DynamicConfigValue = {
- prop,
- custom,
+ id,
};
if (override.properties) {
override.properties.push(propertyConfig);
@@ -74,6 +63,14 @@ export const OverrideEditor: React.FC = ({
[override, onChange]
);
+ let configPropertiesOptions = registry.list().map(item => {
+ return {
+ label: item.name,
+ value: item.id,
+ description: item.description,
+ };
+ });
+
const matcherUi = fieldMatchersUI.get(override.matcher.id);
const styles = getStyles(theme);
return (
@@ -90,20 +87,19 @@ export const OverrideEditor: React.FC = ({
{override.properties.map((p, j) => {
- const reg = p.custom ? customPropertiesRegistry : standardFieldConfigEditorRegistry;
- const item = reg?.getIfExists(p.prop);
+ const item = registry.getIfExists(p.id);
if (!item) {
- return
Unknown property: {p.prop}
;
+ return
Unknown property: {p.id}
;
}
return (
-
+
onDynamicConfigValueChange(j, value)}
onRemove={() => onDynamicConfigValueRemove(j)}
property={p}
- editorsRegistry={reg}
+ registry={registry}
context={{
data,
getSuggestions: (scope?: VariableSuggestionsScope) => getDataLinksVariableSuggestions(data, scope),
@@ -119,7 +115,7 @@ export const OverrideEditor: React.FC = ({
options={configPropertiesOptions}
variant={'link'}
onChange={o => {
- onDynamicConfigValueAdd(o.value, o.custom);
+ onDynamicConfigValueAdd(o.value);
}}
/>
diff --git a/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx b/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx
index 86cef537ce4..8b93b087136 100644
--- a/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx
+++ b/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx
@@ -30,11 +30,6 @@ import { VariableModel } from 'app/features/templating/types';
import { getVariables } from 'app/features/variables/state/selectors';
import { SubMenuItems } from 'app/features/dashboard/components/SubMenu/SubMenuItems';
-enum Pane {
- Right,
- Top,
-}
-
interface OwnProps {
dashboard: DashboardModel;
sourcePanel: PanelModel;
@@ -255,7 +250,7 @@ export class PanelEditorUnconnected extends PureComponent
{
}
renderOptionsPane() {
- const { plugin, dashboard, data, panel } = this.props;
+ const { plugin, dashboard, data, panel, uiState } = this.props;
if (!plugin) {
return
;
@@ -267,6 +262,7 @@ export class PanelEditorUnconnected extends PureComponent {
dashboard={dashboard}
data={data}
panel={panel}
+ width={uiState.rightPaneSize as number}
onClose={this.onTogglePanelOptions}
onFieldConfigsChange={this.onFieldConfigChange}
onPanelOptionsChanged={this.onPanelOptionsChanged}
@@ -342,6 +338,11 @@ const mapDispatchToProps: MapDispatchToProps = {
export const PanelEditor = connect(mapStateToProps, mapDispatchToProps)(PanelEditorUnconnected);
+enum Pane {
+ Right,
+ Top,
+}
+
/*
* Styles
*/
diff --git a/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx b/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx
index 31040a9a09a..3db0846c049 100644
--- a/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx
+++ b/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import { config } from 'app/core/config';
import { css } from 'emotion';
-import { TabsBar, Tab, stylesFactory, TabContent, TransformationsEditor, IconName } from '@grafana/ui';
+import { TabsBar, Tab, stylesFactory, TabContent, IconName } from '@grafana/ui';
import { DataTransformerConfig, LoadingState, PanelData } from '@grafana/data';
import { PanelEditorTab, PanelEditorTabId } from './types';
import { DashboardModel } from '../../state';
@@ -9,6 +9,7 @@ import { QueriesTab } from '../../panel_editor/QueriesTab';
import { PanelModel } from '../../state/PanelModel';
import { AlertTab } from 'app/features/alerting/AlertTab';
import { VisualizationTab } from './VisualizationTab';
+import { TransformationsEditor } from '../TransformationsEditor/TransformationsEditor';
interface PanelEditorTabsProps {
panel: PanelModel;
diff --git a/public/app/features/dashboard/components/PanelEditor/PanelOptionsEditor.tsx b/public/app/features/dashboard/components/PanelEditor/PanelOptionsEditor.tsx
index 8429c5770c9..8344ce599f3 100644
--- a/public/app/features/dashboard/components/PanelEditor/PanelOptionsEditor.tsx
+++ b/public/app/features/dashboard/components/PanelEditor/PanelOptionsEditor.tsx
@@ -22,7 +22,7 @@ export const PanelOptionsEditor: React.FC> = ({ plu
{optionEditors.list().map(e => {
return (
- onOptionChange(e.id, value)} item={e} />
+ onOptionChange(e.path, value)} item={e} />
);
})}
diff --git a/public/app/features/dashboard/components/PanelEditor/PanelOptionsTab.tsx b/public/app/features/dashboard/components/PanelEditor/PanelOptionsTab.tsx
new file mode 100644
index 00000000000..8851a40e2e2
--- /dev/null
+++ b/public/app/features/dashboard/components/PanelEditor/PanelOptionsTab.tsx
@@ -0,0 +1,166 @@
+import React, { FC, useMemo } from 'react';
+import { PanelModel, DashboardModel } from '../../state';
+import { SelectableValue, PanelPlugin, FieldConfigSource, PanelData } from '@grafana/data';
+import { Forms, Select, DataLinksInlineEditor, Input } from '@grafana/ui';
+import { OptionsGroup } from './OptionsGroup';
+import { getPanelLinksVariableSuggestions } from '../../../panel/panellinks/link_srv';
+import { getVariables } from '../../../variables/state/selectors';
+import { PanelOptionsEditor } from './PanelOptionsEditor';
+import { AngularPanelOptions } from '../../panel_editor/AngularPanelOptions';
+
+interface Props {
+ panel: PanelModel;
+ plugin: PanelPlugin;
+ data: PanelData;
+ dashboard: DashboardModel;
+ onPanelConfigChange: (configKey: string, value: any) => void;
+ onPanelOptionsChanged: (options: any) => void;
+ onFieldConfigsChange: (config: FieldConfigSource) => void;
+}
+
+export const PanelOptionsTab: FC = ({
+ panel,
+ plugin,
+ data,
+ dashboard,
+ onPanelConfigChange,
+ onPanelOptionsChanged,
+ onFieldConfigsChange,
+}) => {
+ const elements: JSX.Element[] = [];
+ const linkVariablesSuggestions = useMemo(() => getPanelLinksVariableSuggestions(), []);
+
+ const variableOptions = getVariableOptions();
+ const directionOptions = [
+ { label: 'Horizontal', value: 'h' },
+ { label: 'Vertical', value: 'v' },
+ ];
+
+ const maxPerRowOptions = [2, 3, 4, 6, 8, 12].map(value => ({ label: value.toString(), value }));
+
+ // Fist common panel settings Title, description
+ elements.push(
+
+
+ onPanelConfigChange('title', e.currentTarget.value)} />
+
+
+ onPanelConfigChange('description', e.currentTarget.value)}
+ />
+
+
+ onPanelConfigChange('transparent', e.currentTarget.checked)}
+ />
+
+
+ );
+
+ // Old legacy react editor
+ if (plugin.editor && panel && !plugin.optionEditors) {
+ elements.push(
+
+
+
+ );
+ }
+
+ if (plugin.optionEditors && panel) {
+ elements.push(
+
+
+
+ );
+ }
+
+ if (plugin.angularPanelCtrl) {
+ elements.push(
+
+
+
+ );
+ }
+
+ elements.push(
+
+ onPanelConfigChange('links', links)}
+ suggestions={linkVariablesSuggestions}
+ data={[]}
+ />
+
+ );
+
+ elements.push(
+
+
+ onPanelConfigChange('repeat', value.value)}
+ options={variableOptions}
+ />
+
+ {panel.repeat && (
+
+ onPanelConfigChange('repeatDirection', value)}
+ />
+
+ )}
+
+ {panel.repeat && panel.repeatDirection === 'h' && (
+
+ onPanelConfigChange('maxPerRow', value.value)}
+ />
+
+ )}
+
+ );
+
+ return <>{elements}>;
+};
+
+function getVariableOptions(): Array> {
+ const options = getVariables().map((item: any) => {
+ return { label: item.name, value: item.name };
+ });
+
+ if (options.length === 0) {
+ options.unshift({
+ label: 'No template variables found',
+ value: null,
+ });
+ }
+
+ options.unshift({
+ label: 'Disable repeating',
+ value: null,
+ });
+
+ return options;
+}
diff --git a/public/app/features/dashboard/components/PanelEditor/VisualizationTab.tsx b/public/app/features/dashboard/components/PanelEditor/VisualizationTab.tsx
index e8398817d36..560e3d40a8f 100644
--- a/public/app/features/dashboard/components/PanelEditor/VisualizationTab.tsx
+++ b/public/app/features/dashboard/components/PanelEditor/VisualizationTab.tsx
@@ -1,4 +1,4 @@
-import React, { FC, useState } from 'react';
+import React, { FC, useCallback, useState } from 'react';
import { css } from 'emotion';
import { GrafanaTheme, PanelPlugin, PanelPluginMeta } from '@grafana/data';
import { CustomScrollbar, useTheme, stylesFactory, Icon, Input } from '@grafana/ui';
@@ -6,7 +6,8 @@ import { changePanelPlugin } from '../../state/actions';
import { StoreState } from 'app/types';
import { PanelModel } from '../../state/PanelModel';
import { connect, MapStateToProps, MapDispatchToProps } from 'react-redux';
-import { VizTypePicker } from '../../panel_editor/VizTypePicker';
+import { VizTypePicker, getAllPanelPluginMeta, filterPluginList } from '../../panel_editor/VizTypePicker';
+import { Field } from '@grafana/ui/src/components/Forms/Field';
interface OwnProps {
panel: PanelModel;
@@ -34,6 +35,21 @@ export const VisualizationTabUnconnected: FC = ({ panel, plugin, changePa
const onPluginTypeChange = (meta: PanelPluginMeta) => {
changePanelPlugin(panel, meta.id);
};
+
+ const onKeyPress = useCallback(
+ (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter') {
+ const query = e.currentTarget.value;
+ const plugins = getAllPanelPluginMeta();
+ const match = filterPluginList(plugins, query);
+ if (match && match.length) {
+ onPluginTypeChange(match[0]);
+ }
+ }
+ },
+ [onPluginTypeChange]
+ );
+
const suffix =
searchQuery !== '' ? (
setSearchQuery('')}>
@@ -45,14 +61,17 @@ export const VisualizationTabUnconnected: FC = ({ panel, plugin, changePa
return (
- setSearchQuery(e.currentTarget.value)}
- prefix={ }
- suffix={suffix}
- placeholder="Filter visualisations"
- autoFocus
- />
+
+ setSearchQuery(e.currentTarget.value)}
+ onKeyPress={onKeyPress}
+ prefix={ }
+ suffix={suffix}
+ placeholder="Filter visualisations"
+ autoFocus
+ />
+
@@ -78,12 +97,11 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
flex-direction: column;
flex-grow: 1;
max-height: 100%;
+ padding: ${theme.spacing.md};
`,
search: css`
- padding: ${theme.spacing.sm} ${theme.spacing.md};
flex-grow: 0;
flex-shrink: 1;
- margin-bottom: ${theme.spacing.sm};
`,
searchClear: css`
color: ${theme.colors.gray60};
@@ -93,7 +111,6 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
flex-grow: 1;
height: 100%;
overflow: hidden;
- padding-left: ${theme.spacing.md};
`,
};
});
diff --git a/public/app/features/dashboard/components/PanelEditor/state/actions.ts b/public/app/features/dashboard/components/PanelEditor/state/actions.ts
index 79c4c37486b..56a4ea8c85f 100644
--- a/public/app/features/dashboard/components/PanelEditor/state/actions.ts
+++ b/public/app/features/dashboard/components/PanelEditor/state/actions.ts
@@ -17,7 +17,7 @@ export function initPanelEditor(sourcePanel: PanelModel, dashboard: DashboardMod
const panel = dashboard.initPanelEditor(sourcePanel);
const queryRunner = panel.getQueryRunner();
- const querySubscription = queryRunner.getData().subscribe({
+ const querySubscription = queryRunner.getData(false).subscribe({
next: (data: PanelData) => dispatch(setEditorPanelData(data)),
});
diff --git a/public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx b/public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx
index 8e1fbe13c34..fe2544b4cad 100644
--- a/public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx
+++ b/public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx
@@ -1,6 +1,6 @@
import React, { PureComponent } from 'react';
-import { Switch, LegacyForms, Icon } from '@grafana/ui';
-const { Select } = LegacyForms;
+import { LegacyForms, Icon } from '@grafana/ui';
+const { Select, Switch } = LegacyForms;
import { SelectableValue } from '@grafana/data';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
import { buildIframeHtml } from './utils';
diff --git a/public/app/features/dashboard/components/ShareModal/ShareExport.tsx b/public/app/features/dashboard/components/ShareModal/ShareExport.tsx
index 097eba15c79..31f17c9d526 100644
--- a/public/app/features/dashboard/components/ShareModal/ShareExport.tsx
+++ b/public/app/features/dashboard/components/ShareModal/ShareExport.tsx
@@ -1,6 +1,7 @@
import React, { PureComponent } from 'react';
import { saveAs } from 'file-saver';
-import { Button, Switch, Icon } from '@grafana/ui';
+import { Button, LegacyForms, Icon } from '@grafana/ui';
+const { Switch } = LegacyForms;
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
import { DashboardExporter } from 'app/features/dashboard/components/DashExportModal';
import { appEvents } from 'app/core/core';
diff --git a/public/app/features/dashboard/components/ShareModal/ShareLink.tsx b/public/app/features/dashboard/components/ShareModal/ShareLink.tsx
index 0776b2f867f..514c44b4d78 100644
--- a/public/app/features/dashboard/components/ShareModal/ShareLink.tsx
+++ b/public/app/features/dashboard/components/ShareModal/ShareLink.tsx
@@ -1,7 +1,7 @@
import React, { PureComponent } from 'react';
import { e2e } from '@grafana/e2e';
-import { Switch, LegacyForms, ClipboardButton, Icon } from '@grafana/ui';
-const { Select } = LegacyForms;
+import { LegacyForms, ClipboardButton, Icon } from '@grafana/ui';
+const { Select, Switch } = LegacyForms;
import { SelectableValue, PanelModel, AppEvents } from '@grafana/data';
import { DashboardModel } from 'app/features/dashboard/state';
import { buildImageUrl, buildShareUrl } from './utils';
diff --git a/public/app/features/dashboard/components/SubMenu/Annotations.tsx b/public/app/features/dashboard/components/SubMenu/Annotations.tsx
index c182a2c9574..1125c1ebecc 100644
--- a/public/app/features/dashboard/components/SubMenu/Annotations.tsx
+++ b/public/app/features/dashboard/components/SubMenu/Annotations.tsx
@@ -1,5 +1,6 @@
import React, { FunctionComponent, useEffect, useState } from 'react';
-import { Switch } from '@grafana/ui';
+import { LegacyForms } from '@grafana/ui';
+const { Switch } = LegacyForms;
interface Props {
annotations: any[];
diff --git a/packages/grafana-ui/src/components/TransformersUI/TransformationRow.tsx b/public/app/features/dashboard/components/TransformationsEditor/TransformationRow.tsx
similarity index 90%
rename from packages/grafana-ui/src/components/TransformersUI/TransformationRow.tsx
rename to public/app/features/dashboard/components/TransformationsEditor/TransformationRow.tsx
index 56fcdb4ba84..24ec55c6374 100644
--- a/packages/grafana-ui/src/components/TransformersUI/TransformationRow.tsx
+++ b/public/app/features/dashboard/components/TransformationsEditor/TransformationRow.tsx
@@ -1,9 +1,7 @@
import React, { useContext, useState } from 'react';
-import { ThemeContext } from '../../themes/ThemeContext';
import { css } from 'emotion';
-import { DataFrame } from '@grafana/data';
-import { JSONFormatter } from '../JSONFormatter/JSONFormatter';
-import { GrafanaTheme } from '@grafana/data';
+import { JSONFormatter, ThemeContext } from '@grafana/ui';
+import { GrafanaTheme, DataFrame } from '@grafana/data';
interface TransformationRowProps {
name: string;
@@ -13,6 +11,39 @@ interface TransformationRowProps {
input: DataFrame[];
}
+export const TransformationRow = ({ onRemove, editor, name, input }: TransformationRowProps) => {
+ const theme = useContext(ThemeContext);
+ const [viewDebug, setViewDebug] = useState(false);
+ const styles = getStyles(theme);
+ return (
+
+
+
{name}
+
+
setViewDebug(!viewDebug)} className={styles.icon}>
+
+
+
+
+
+
+
+
+ {editor}
+ {viewDebug && (
+
+
+
+ )}
+
+
+ );
+};
+
const getStyles = (theme: GrafanaTheme) => ({
title: css`
display: flex;
@@ -50,36 +81,3 @@ const getStyles = (theme: GrafanaTheme) => ({
padding: 8px;
`,
});
-
-export const TransformationRow = ({ onRemove, editor, name, input }: TransformationRowProps) => {
- const theme = useContext(ThemeContext);
- const [viewDebug, setViewDebug] = useState(false);
- const styles = getStyles(theme);
- return (
-
-
-
{name}
-
-
setViewDebug(!viewDebug)} className={styles.icon}>
-
-
-
-
-
-
-
-
- {editor}
- {viewDebug && (
-
-
-
- )}
-
-
- );
-};
diff --git a/packages/grafana-ui/src/components/TransformersUI/TransformationsEditor.tsx b/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx
similarity index 86%
rename from packages/grafana-ui/src/components/TransformersUI/TransformationsEditor.tsx
rename to public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx
index f37bddb19ff..ad7d2e628fe 100644
--- a/packages/grafana-ui/src/components/TransformersUI/TransformationsEditor.tsx
+++ b/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx
@@ -1,22 +1,21 @@
-import { DataTransformerID, DataTransformerConfig, DataFrame, transformDataFrame } from '@grafana/data';
-import { Select } from '../Forms/Legacy/Select/Select';
-import { transformersUIRegistry } from './transformers';
-import React from 'react';
-import { TransformationRow } from './TransformationRow';
-import { Button } from '../Button';
import { css } from 'emotion';
+import React from 'react';
+import { transformersUIRegistry } from '@grafana/ui/src/components/TransformersUI/transformers';
+import { DataTransformerID, DataTransformerConfig, DataFrame, transformDataFrame } from '@grafana/data';
+import { Button, Select } from '@grafana/ui';
+import { TransformationRow } from './TransformationRow';
-interface TransformationsEditorState {
- updateCounter: number;
-}
-
-interface TransformationsEditorProps {
+interface Props {
onChange: (transformations: DataTransformerConfig[]) => void;
transformations: DataTransformerConfig[];
dataFrames: DataFrame[];
}
-export class TransformationsEditor extends React.PureComponent {
+interface State {
+ updateCounter: number;
+}
+
+export class TransformationsEditor extends React.PureComponent {
state = { updateCounter: 0 };
onTransformationAdd = () => {
@@ -116,12 +115,16 @@ export class TransformationsEditor extends React.PureComponent
+
+
+ Transformations allow you to combine, re-order, hide and rename specific parts the the data set before being
+ visualized.
+
{this.renderTransformationEditors()}
Add transformation
- >
+
);
}
}
diff --git a/public/app/features/dashboard/panel_editor/QueryOptions.tsx b/public/app/features/dashboard/panel_editor/QueryOptions.tsx
index 17dc0a7ccc5..8b0cff7fe9d 100644
--- a/public/app/features/dashboard/panel_editor/QueryOptions.tsx
+++ b/public/app/features/dashboard/panel_editor/QueryOptions.tsx
@@ -5,9 +5,9 @@ import React, { PureComponent, ChangeEvent, FocusEvent, ReactText } from 'react'
import { rangeUtil, DataSourceSelectItem } from '@grafana/data';
// Components
-import { EventsWithValidation, LegacyInputStatus, LegacyForms, Switch, ValidationEvents, FormLabel } from '@grafana/ui';
+import { EventsWithValidation, LegacyInputStatus, LegacyForms, ValidationEvents, FormLabel } from '@grafana/ui';
import { DataSourceOption } from './DataSourceOption';
-const { Input } = LegacyForms;
+const { Input, Switch } = LegacyForms;
// Types
import { PanelModel } from '../state';
diff --git a/public/app/features/dashboard/panel_editor/VizTypePicker.tsx b/public/app/features/dashboard/panel_editor/VizTypePicker.tsx
index 5a40f94b5bf..8e0d31b30e1 100644
--- a/public/app/features/dashboard/panel_editor/VizTypePicker.tsx
+++ b/public/app/features/dashboard/panel_editor/VizTypePicker.tsx
@@ -13,16 +13,39 @@ export interface Props {
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): PanelPluginMeta[] {
+ if (!searchQuery.length) {
+ return pluginsList;
+ }
+ const query = searchQuery.toLowerCase();
+ const first: PanelPluginMeta[] = [];
+ const match: PanelPluginMeta[] = [];
+ for (const item of pluginsList) {
+ 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 = ({ searchQuery, onTypeChange, current }) => {
const theme = useTheme();
const styles = getStyles(theme);
const pluginsList: PanelPluginMeta[] = useMemo(() => {
- 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);
+ return getAllPanelPluginMeta();
}, []);
const renderVizPlugin = (plugin: PanelPluginMeta, index: number) => {
@@ -42,10 +65,7 @@ export const VizTypePicker: React.FC = ({ searchQuery, onTypeChange, curr
};
const getFilteredPluginList = useCallback((): PanelPluginMeta[] => {
- const regex = new RegExp(searchQuery, 'i');
- return pluginsList.filter(item => {
- return regex.test(item.name);
- });
+ return filterPluginList(pluginsList, searchQuery);
}, [searchQuery]);
const filteredPluginList = getFilteredPluginList();
@@ -68,7 +88,8 @@ export const VizTypePicker: React.FC = ({ searchQuery, onTypeChange, curr
const getStyles = stylesFactory((theme: GrafanaTheme) => {
return {
wrapper: css`
- padding-right: ${theme.spacing.md};
+ // this needed here to make the box shadow not be clicked by the parent scroll container
+ padding-top: ${theme.spacing.md};
`,
grid: css`
max-width: 100%;
diff --git a/public/app/features/dashboard/panel_editor/VizTypePickerPlugin.tsx b/public/app/features/dashboard/panel_editor/VizTypePickerPlugin.tsx
index 8775c7830d0..29b6e65cbbc 100644
--- a/public/app/features/dashboard/panel_editor/VizTypePickerPlugin.tsx
+++ b/public/app/features/dashboard/panel_editor/VizTypePickerPlugin.tsx
@@ -2,7 +2,6 @@ import React from 'react';
import { GrafanaTheme, PanelPluginMeta } from '@grafana/data';
import { stylesFactory, useTheme } from '@grafana/ui';
import { css, cx } from 'emotion';
-import tinycolor from 'tinycolor2';
interface Props {
isCurrent: boolean;
@@ -29,10 +28,11 @@ const VizTypePickerPlugin: React.FC = ({ isCurrent, plugin, onClick, disa
};
const getStyles = stylesFactory((theme: GrafanaTheme) => {
- const itemBorder = `1px solid ${theme.isLight ? theme.colors.gray3 : theme.colors.dark10}`;
+ const itemBorder = `1px solid ${theme.isLight ? theme.colors.gray85 : theme.colors.gray25}`;
+
return {
item: css`
- background: ${theme.isLight ? theme.colors.white : theme.colors.gray05};
+ background: ${theme.isLight ? theme.colors.gray98 : theme.colors.gray15};
border: ${itemBorder};
border-radius: 3px;
height: 100px;
@@ -44,27 +44,18 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
cursor: pointer;
display: flex;
margin-right: 10px;
- margin-bottom: 10px;
align-items: center;
justify-content: center;
padding-bottom: 6px;
- transition: transform 1 ease;
+
&:hover {
box-shadow: 0 0 4px ${theme.colors.blueLight};
- background: ${theme.isLight
- ? tinycolor(theme.colors.blueBase)
- .lighten(45)
- .toHexString()
- : tinycolor(theme.colors.blueBase)
- .darken(46)
- .toHexString()};
border: 1px solid ${theme.colors.blueLight};
}
`,
current: css`
box-shadow: 0 0 6px ${theme.colors.orange} !important;
border: 1px solid ${theme.colors.orange} !important;
- background: ${theme.isLight ? theme.colors.white : theme.colors.gray05};
`,
disabled: css`
opacity: 0.2;
diff --git a/public/app/features/dashboard/services/TimeSrv.test.ts b/public/app/features/dashboard/services/TimeSrv.test.ts
index d9e5adfebaf..04786c33b43 100644
--- a/public/app/features/dashboard/services/TimeSrv.test.ts
+++ b/public/app/features/dashboard/services/TimeSrv.test.ts
@@ -135,6 +135,38 @@ describe('timeSrv', () => {
expect(time.to.valueOf()).toEqual(1410337665699);
});
+ it('should handle epochs that look like formatted date without time', () => {
+ location = {
+ search: jest.fn(() => ({
+ from: '20149999',
+ to: '20159999',
+ })),
+ };
+
+ timeSrv = new TimeSrv(rootScope as any, jest.fn() as any, location as any, timer, new ContextSrvStub() as any);
+
+ timeSrv.init(_dashboard);
+ const time = timeSrv.timeRange();
+ expect(time.from.valueOf()).toEqual(20149999);
+ expect(time.to.valueOf()).toEqual(20159999);
+ });
+
+ it('should handle epochs that look like formatted date', () => {
+ location = {
+ search: jest.fn(() => ({
+ from: '201499991234567',
+ to: '201599991234567',
+ })),
+ };
+
+ timeSrv = new TimeSrv(rootScope as any, jest.fn() as any, location as any, timer, new ContextSrvStub() as any);
+
+ timeSrv.init(_dashboard);
+ const time = timeSrv.timeRange();
+ expect(time.from.valueOf()).toEqual(201499991234567);
+ expect(time.to.valueOf()).toEqual(201599991234567);
+ });
+
it('should handle bad dates', () => {
location = {
search: jest.fn(() => ({
diff --git a/public/app/features/dashboard/services/TimeSrv.ts b/public/app/features/dashboard/services/TimeSrv.ts
index 7d2e522fece..dbc5b176941 100644
--- a/public/app/features/dashboard/services/TimeSrv.ts
+++ b/public/app/features/dashboard/services/TimeSrv.ts
@@ -102,10 +102,15 @@ export class TimeSrv {
return value;
}
if (value.length === 8) {
- return toUtc(value, 'YYYYMMDD');
- }
- if (value.length === 15) {
- return toUtc(value, 'YYYYMMDDTHHmmss');
+ const utcValue = toUtc(value, 'YYYYMMDD');
+ if (utcValue.isValid()) {
+ return utcValue;
+ }
+ } else if (value.length === 15) {
+ const utcValue = toUtc(value, 'YYYYMMDDTHHmmss');
+ if (utcValue.isValid()) {
+ return utcValue;
+ }
}
if (!isNaN(value)) {
diff --git a/public/app/features/dashboard/state/PanelModel.test.ts b/public/app/features/dashboard/state/PanelModel.test.ts
index aac9ae7af06..258c30556b0 100644
--- a/public/app/features/dashboard/state/PanelModel.test.ts
+++ b/public/app/features/dashboard/state/PanelModel.test.ts
@@ -1,10 +1,46 @@
import { PanelModel } from './PanelModel';
import { getPanelPlugin } from '../../plugins/__mocks__/pluginMocks';
-import { PanelProps, FieldConfigProperty } from '@grafana/data';
+import {
+ FieldConfigProperty,
+ identityOverrideProcessor,
+ PanelProps,
+ standardFieldConfigEditorRegistry,
+} from '@grafana/data';
import { ComponentClass } from 'react';
class TablePanelCtrl {}
+export const mockStandardProperties = () => {
+ const unit = {
+ id: 'unit',
+ path: 'unit',
+ name: 'Unit',
+ description: 'Value units',
+ // @ts-ignore
+ editor: () => null,
+ // @ts-ignore
+ override: () => null,
+ process: identityOverrideProcessor,
+ shouldApply: () => true,
+ };
+
+ const decimals = {
+ id: 'decimals',
+ path: 'decimals',
+ name: 'Decimals',
+ description: 'Number of decimal to be shown for a value',
+ // @ts-ignore
+ editor: () => null,
+ // @ts-ignore
+ override: () => null,
+ process: identityOverrideProcessor,
+ shouldApply: () => true,
+ };
+
+ return [unit, decimals];
+};
+standardFieldConfigEditorRegistry.setInit(() => mockStandardProperties());
+
describe('PanelModel', () => {
describe('when creating new panel model', () => {
let model: any;
@@ -79,9 +115,16 @@ describe('PanelModel', () => {
TablePanelCtrl // angular
);
panelPlugin.setDefaults(defaultOptionsMock);
- panelPlugin.useStandardFieldConfig([FieldConfigProperty.Unit, FieldConfigProperty.Decimals], {
- [FieldConfigProperty.Unit]: 'flop',
- [FieldConfigProperty.Decimals]: 2,
+ /* panelPlugin.useStandardFieldConfig([FieldConfigOptionId.Unit, FieldConfigOptionId.Decimals], {
+ [FieldConfigOptionId.Unit]: 'flop',
+ [FieldConfigOptionId.Decimals]: 2,
+ }); */
+ panelPlugin.useFieldConfig({
+ standardOptions: [FieldConfigProperty.Unit, FieldConfigProperty.Decimals],
+ standardOptionsDefaults: {
+ [FieldConfigProperty.Unit]: 'flop',
+ [FieldConfigProperty.Decimals]: 2,
+ },
});
model.pluginLoaded(panelPlugin);
});
@@ -100,9 +143,9 @@ describe('PanelModel', () => {
it('should apply field config defaults', () => {
// default unit is overriden by model
- expect(model.getFieldOverrideOptions().fieldOptions.defaults.unit).toBe('mpg');
+ expect(model.getFieldOverrideOptions().fieldConfig.defaults.unit).toBe('mpg');
// default decimals are aplied
- expect(model.getFieldOverrideOptions().fieldOptions.defaults.decimals).toBe(2);
+ expect(model.getFieldOverrideOptions().fieldConfig.defaults.decimals).toBe(2);
});
it('should set model props on instance', () => {
diff --git a/public/app/features/dashboard/state/PanelModel.ts b/public/app/features/dashboard/state/PanelModel.ts
index 5c2ad580604..f6dff94460b 100644
--- a/public/app/features/dashboard/state/PanelModel.ts
+++ b/public/app/features/dashboard/state/PanelModel.ts
@@ -415,9 +415,9 @@ export class PanelModel implements DataConfigSource {
}
return {
- fieldOptions: this.fieldConfig,
+ fieldConfig: this.fieldConfig,
replaceVariables: this.replaceVariables,
- custom: this.plugin.customFieldConfigs,
+ fieldConfigRegistry: this.plugin.fieldConfigRegistry,
theme: config.theme,
};
}
diff --git a/public/app/features/dashboard/state/PanelQueryRunner.test.ts b/public/app/features/dashboard/state/PanelQueryRunner.test.ts
index 2d44a648130..895d51cd223 100644
--- a/public/app/features/dashboard/state/PanelQueryRunner.test.ts
+++ b/public/app/features/dashboard/state/PanelQueryRunner.test.ts
@@ -210,7 +210,7 @@ describe('PanelQueryRunner', () => {
},
{
getFieldOverrideOptions: () => ({
- fieldOptions: {
+ fieldConfig: {
defaults: {
unit: 'm/s',
},
diff --git a/public/app/features/dashboard/state/PanelQueryRunner.ts b/public/app/features/dashboard/state/PanelQueryRunner.ts
index 1952ad34b1b..1e9a2efacf9 100644
--- a/public/app/features/dashboard/state/PanelQueryRunner.ts
+++ b/public/app/features/dashboard/state/PanelQueryRunner.ts
@@ -4,7 +4,6 @@ import { ReplaySubject, Unsubscribable, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
// Services & Utils
-import { config } from 'app/core/config';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import kbn from 'app/core/utils/kbn';
import templateSrv from 'app/features/templating/template_srv';
@@ -70,37 +69,38 @@ export class PanelQueryRunner {
return this.subject.pipe(
map((data: PanelData) => {
let processedData = data;
- // apply transformations
- if (transform && this.hasTransformations()) {
- processedData = {
- ...processedData,
- series: transformDataFrame(this.dataConfigSource.getTransformations(), data.series),
- };
+
+ // Apply transformations
+
+ if (transform) {
+ const transformations = this.dataConfigSource.getTransformations();
+
+ if (transformations && transformations.length > 0) {
+ processedData = {
+ ...processedData,
+ series: transformDataFrame(this.dataConfigSource.getTransformations(), data.series),
+ };
+ }
}
- // apply overrides
- if (this.hasFieldOverrideOptions()) {
+
+ // Apply field defaults & overrides
+ const fieldConfig = this.dataConfigSource.getFieldOverrideOptions();
+
+ if (fieldConfig) {
processedData = {
...processedData,
series: applyFieldOverrides({
data: processedData.series,
- ...this.dataConfigSource.getFieldOverrideOptions(),
+ ...fieldConfig,
}),
};
}
+
return processedData;
})
);
}
- hasTransformations = () => {
- const transformations = this.dataConfigSource.getTransformations();
- return config.featureToggles.transformations && transformations && transformations.length > 0;
- };
-
- hasFieldOverrideOptions = () => {
- return this.dataConfigSource.getFieldOverrideOptions();
- };
-
async run(options: QueryRunnerOptions) {
const {
queries,
diff --git a/public/app/features/datasources/settings/BasicSettings.tsx b/public/app/features/datasources/settings/BasicSettings.tsx
index 3619800f60f..e0ec59f9e6e 100644
--- a/public/app/features/datasources/settings/BasicSettings.tsx
+++ b/public/app/features/datasources/settings/BasicSettings.tsx
@@ -1,6 +1,6 @@
import React, { FC } from 'react';
-import { FormLabel, LegacyForms, Switch } from '@grafana/ui';
-const { Input } = LegacyForms;
+import { FormLabel, LegacyForms } from '@grafana/ui';
+const { Input, Switch } = LegacyForms;
import { e2e } from '@grafana/e2e';
export interface Props {
diff --git a/public/app/features/explore/Logs.tsx b/public/app/features/explore/Logs.tsx
index f4e5f146532..9a07635c089 100644
--- a/public/app/features/explore/Logs.tsx
+++ b/public/app/features/explore/Logs.tsx
@@ -15,7 +15,8 @@ import {
LinkModel,
Field,
} from '@grafana/data';
-import { Switch, LogLabels, ToggleButtonGroup, ToggleButton, LogRows } from '@grafana/ui';
+import { LegacyForms, LogLabels, ToggleButtonGroup, ToggleButton, LogRows } from '@grafana/ui';
+const { Switch } = LegacyForms;
import store from 'app/core/store';
import { ExploreGraphPanel } from './ExploreGraphPanel';
diff --git a/public/app/features/panel/panellinks/fieldDisplayValuesProxy.test.ts b/public/app/features/panel/panellinks/fieldDisplayValuesProxy.test.ts
index 1187350cd94..f69de4cd36f 100644
--- a/public/app/features/panel/panellinks/fieldDisplayValuesProxy.test.ts
+++ b/public/app/features/panel/panellinks/fieldDisplayValuesProxy.test.ts
@@ -21,7 +21,7 @@ describe('getFieldDisplayValuesProxy', () => {
],
}),
],
- fieldOptions: {
+ fieldConfig: {
defaults: {},
overrides: [],
},
diff --git a/public/app/features/panel/panellinks/linkSuppliers.test.ts b/public/app/features/panel/panellinks/linkSuppliers.test.ts
index dea4ef5e5dc..ee10e211e8e 100644
--- a/public/app/features/panel/panellinks/linkSuppliers.test.ts
+++ b/public/app/features/panel/panellinks/linkSuppliers.test.ts
@@ -129,7 +129,7 @@ describe('getLinksFromLogsField', () => {
],
}),
],
- fieldOptions: {
+ fieldConfig: {
defaults: {},
overrides: [],
},
diff --git a/public/app/features/plugins/built_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts
index ec35b5cade1..78058438ebe 100644
--- a/public/app/features/plugins/built_in_plugins.ts
+++ b/public/app/features/plugins/built_in_plugins.ts
@@ -27,8 +27,6 @@ const mssqlPlugin = async () =>
await import(/* webpackChunkName: "mssqlPlugin" */ 'app/plugins/datasource/mssql/module');
const testDataDSPlugin = async () =>
await import(/* webpackChunkName: "testDataDSPlugin" */ 'app/plugins/datasource/testdata/module');
-const inputDatasourcePlugin = async () =>
- await import(/* webpackChunkName: "inputDatasourcePlugin" */ 'app/plugins/datasource/input/module');
const stackdriverPlugin = async () =>
await import(/* webpackChunkName: "stackdriverPlugin" */ 'app/plugins/datasource/stackdriver/module');
const azureMonitorPlugin = async () =>
@@ -73,7 +71,6 @@ const builtInPlugins: any = {
'app/plugins/datasource/mssql/module': mssqlPlugin,
'app/plugins/datasource/prometheus/module': prometheusPlugin,
'app/plugins/datasource/testdata/module': testDataDSPlugin,
- 'app/plugins/datasource/input/module': inputDatasourcePlugin,
'app/plugins/datasource/stackdriver/module': stackdriverPlugin,
'app/plugins/datasource/grafana-azure-monitor-datasource/module': azureMonitorPlugin,
diff --git a/public/app/features/plugins/plugin_loader.test.ts b/public/app/features/plugins/plugin_loader.test.ts
new file mode 100644
index 00000000000..2d365233710
--- /dev/null
+++ b/public/app/features/plugins/plugin_loader.test.ts
@@ -0,0 +1,68 @@
+// Use the real plugin_loader (stubbed by default)
+jest.unmock('app/features/plugins/plugin_loader');
+
+(global as any).ace = {
+ define: jest.fn(),
+};
+
+jest.mock('app/core/core', () => {
+ return {
+ coreModule: {
+ directive: jest.fn(),
+ },
+ };
+});
+
+import { SystemJS } from '@grafana/runtime';
+import { AppPluginMeta, PluginMetaInfo, PluginType, AppPlugin } from '@grafana/data';
+
+// Loaded after the `unmock` abve
+import { importAppPlugin } from './plugin_loader';
+
+class MyCustomApp extends AppPlugin {
+ initWasCalled = false;
+ calledTwice = false;
+
+ init(meta: AppPluginMeta) {
+ this.initWasCalled = true;
+ this.calledTwice = this.meta === meta;
+ }
+}
+
+describe('Load App', () => {
+ const app = new MyCustomApp();
+ const modulePath = 'my/custom/plugin/module';
+
+ beforeAll(() => {
+ SystemJS.set(modulePath, SystemJS.newModule({ plugin: app }));
+ });
+
+ afterAll(() => {
+ SystemJS.delete(modulePath);
+ });
+
+ it('should call init and set meta', async () => {
+ const meta: AppPluginMeta = {
+ id: 'test-app',
+ module: modulePath,
+ baseUrl: 'xxx',
+ info: {} as PluginMetaInfo,
+ type: PluginType.app,
+ name: 'test',
+ };
+
+ // Check that we mocked the import OK
+ const m = await SystemJS.import(modulePath);
+ expect(m.plugin).toBe(app);
+
+ const loaded = await importAppPlugin(meta);
+ expect(loaded).toBe(app);
+ expect(app.meta).toBe(meta);
+ expect(app.initWasCalled).toBeTruthy();
+ expect(app.calledTwice).toBeFalsy();
+
+ const again = await importAppPlugin(meta);
+ expect(again).toBe(app);
+ expect(app.calledTwice).toBeTruthy();
+ });
+});
diff --git a/public/app/features/templating/TextBoxVariable.ts b/public/app/features/templating/TextBoxVariable.ts
index 62c2c1b2f51..680faa32db0 100644
--- a/public/app/features/templating/TextBoxVariable.ts
+++ b/public/app/features/templating/TextBoxVariable.ts
@@ -4,10 +4,10 @@ import {
VariableActions,
VariableHide,
VariableOption,
- VariableType,
variableTypes,
} from './types';
import { VariableSrv } from './variable_srv';
+import { VariableType } from '@grafana/data';
export class TextBoxVariable implements TextBoxVariableModel, VariableActions {
type: VariableType;
diff --git a/public/app/features/templating/adhoc_variable.ts b/public/app/features/templating/adhoc_variable.ts
index 36fa527c1a3..9e220f4a1a3 100644
--- a/public/app/features/templating/adhoc_variable.ts
+++ b/public/app/features/templating/adhoc_variable.ts
@@ -5,10 +5,11 @@ import {
assignModelProperties,
VariableActions,
VariableHide,
- VariableType,
variableTypes,
} from './types';
+import { VariableType } from '@grafana/data';
+
export class AdhocVariable implements AdHocVariableModel, VariableActions {
type: VariableType;
name: string;
diff --git a/public/app/features/templating/constant_variable.ts b/public/app/features/templating/constant_variable.ts
index 15eb3c3538a..75fc90a0272 100644
--- a/public/app/features/templating/constant_variable.ts
+++ b/public/app/features/templating/constant_variable.ts
@@ -4,10 +4,10 @@ import {
VariableActions,
VariableHide,
VariableOption,
- VariableType,
variableTypes,
} from './types';
import { VariableSrv } from './all';
+import { VariableType } from '@grafana/data';
export class ConstantVariable implements ConstantVariableModel, VariableActions {
type: VariableType;
diff --git a/public/app/features/templating/custom_variable.ts b/public/app/features/templating/custom_variable.ts
index 032dfc6ef4a..35c8cac7007 100644
--- a/public/app/features/templating/custom_variable.ts
+++ b/public/app/features/templating/custom_variable.ts
@@ -5,10 +5,10 @@ import {
VariableActions,
VariableHide,
VariableOption,
- VariableType,
variableTypes,
} from './types';
import { VariableSrv } from './variable_srv';
+import { VariableType } from '@grafana/data';
export class CustomVariable implements CustomVariableModel, VariableActions {
type: VariableType;
diff --git a/public/app/features/templating/datasource_variable.ts b/public/app/features/templating/datasource_variable.ts
index 924be3da150..3ea1c6948d4 100644
--- a/public/app/features/templating/datasource_variable.ts
+++ b/public/app/features/templating/datasource_variable.ts
@@ -5,10 +5,9 @@ import {
VariableHide,
VariableOption,
VariableRefresh,
- VariableType,
variableTypes,
} from './types';
-import { stringToJsRegex } from '@grafana/data';
+import { VariableType, stringToJsRegex } from '@grafana/data';
import { VariableSrv } from './variable_srv';
import { TemplateSrv } from './template_srv';
import { DatasourceSrv } from '../plugins/datasource_srv';
diff --git a/public/app/features/templating/interval_variable.ts b/public/app/features/templating/interval_variable.ts
index 1152e1f24ed..9041246b162 100644
--- a/public/app/features/templating/interval_variable.ts
+++ b/public/app/features/templating/interval_variable.ts
@@ -7,12 +7,12 @@ import {
VariableHide,
VariableOption,
VariableRefresh,
- VariableType,
variableTypes,
} from './types';
import { TimeSrv } from '../dashboard/services/TimeSrv';
import { TemplateSrv } from './template_srv';
import { VariableSrv } from './variable_srv';
+import { VariableType } from '@grafana/data';
export class IntervalVariable implements IntervalVariableModel, VariableActions {
type: VariableType;
diff --git a/public/app/features/templating/query_variable.ts b/public/app/features/templating/query_variable.ts
index ccab522ffc2..0b1b85314a6 100644
--- a/public/app/features/templating/query_variable.ts
+++ b/public/app/features/templating/query_variable.ts
@@ -8,10 +8,9 @@ import {
VariableRefresh,
VariableSort,
VariableTag,
- VariableType,
variableTypes,
} from './types';
-import { DataSourceApi, stringToJsRegex } from '@grafana/data';
+import { VariableType, DataSourceApi, stringToJsRegex } from '@grafana/data';
import DatasourceSrv from '../plugins/datasource_srv';
import { TemplateSrv } from './template_srv';
import { VariableSrv } from './variable_srv';
diff --git a/public/app/features/templating/template_srv.ts b/public/app/features/templating/template_srv.ts
index 7d6d7402604..478a0679d2b 100644
--- a/public/app/features/templating/template_srv.ts
+++ b/public/app/features/templating/template_srv.ts
@@ -7,6 +7,7 @@ import { getConfig } from 'app/core/config';
import { variableRegex } from './utils';
import { isAdHoc } from '../variables/guard';
import { VariableModel } from './types';
+import { setTemplateSrv, TemplateSrv as BaseTemplateSrv } from '@grafana/runtime';
function luceneEscape(value: string) {
return value.replace(/([\!\*\+\-\=<>\s\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g, '\\$1');
@@ -28,7 +29,7 @@ const runtimeDependencies: TemplateSrvDependencies = {
getVariableWithName,
};
-export class TemplateSrv {
+export class TemplateSrv implements BaseTemplateSrv {
private _variables: any[];
private regex = variableRegex;
private index: any = {};
@@ -448,4 +449,7 @@ export class TemplateSrv {
};
}
-export default new TemplateSrv();
+// Expose the template srv
+const srv = new TemplateSrv();
+setTemplateSrv(srv);
+export default srv;
diff --git a/public/app/features/templating/types.ts b/public/app/features/templating/types.ts
index d31f7037508..0f5519e6e80 100644
--- a/public/app/features/templating/types.ts
+++ b/public/app/features/templating/types.ts
@@ -1,5 +1,6 @@
import { assignModelProperties } from 'app/core/utils/model_utils';
import { Deferred } from '../../core/utils/deferred';
+import { VariableModel as BaseVariableModel } from '@grafana/data';
export enum VariableRefresh {
never,
@@ -38,8 +39,6 @@ export interface VariableOption {
tags?: VariableTag[];
}
-export type VariableType = 'query' | 'adhoc' | 'constant' | 'datasource' | 'interval' | 'textbox' | 'custom';
-
export interface AdHocVariableFilter {
key: string;
operator: string;
@@ -93,12 +92,9 @@ export interface VariableWithOptions extends VariableModel {
query: string;
}
-export interface VariableModel {
+export interface VariableModel extends BaseVariableModel {
id?: string; // only exists for variables in redux state
global?: boolean; // only exists for variables in redux state
- type: VariableType;
- name: string;
- label: string | null;
hide: VariableHide;
skipUrlSync: boolean;
index?: number;
diff --git a/public/app/features/variables/adapters.ts b/public/app/features/variables/adapters.ts
index 4c5244d5b10..703eed3c394 100644
--- a/public/app/features/variables/adapters.ts
+++ b/public/app/features/variables/adapters.ts
@@ -12,12 +12,11 @@ import {
TextBoxVariableModel,
VariableModel,
VariableOption,
- VariableType,
} from '../templating/types';
import { VariableEditorProps } from './editor/types';
import { VariablesState } from './state/variablesReducer';
import { VariablePickerProps } from './pickers/types';
-import { Registry } from '@grafana/data';
+import { Registry, VariableType } from '@grafana/data';
import { createQueryVariableAdapter } from './query/adapter';
import { createCustomVariableAdapter } from './custom/adapter';
import { createTextBoxVariableAdapter } from './textbox/adapter';
diff --git a/public/app/features/variables/editor/SelectionOptionsEditor.tsx b/public/app/features/variables/editor/SelectionOptionsEditor.tsx
index adfe5cb2afc..103b5e3be4c 100644
--- a/public/app/features/variables/editor/SelectionOptionsEditor.tsx
+++ b/public/app/features/variables/editor/SelectionOptionsEditor.tsx
@@ -1,5 +1,6 @@
import React, { FunctionComponent, useCallback } from 'react';
-import { Switch } from '@grafana/ui';
+import { LegacyForms } from '@grafana/ui';
+const { Switch } = LegacyForms;
import { e2e } from '@grafana/e2e';
import { VariableWithMultiSupport } from '../../templating/types';
diff --git a/public/app/features/variables/editor/VariableEditorEditor.tsx b/public/app/features/variables/editor/VariableEditorEditor.tsx
index bcd8a9d0df8..1ad63b61aa2 100644
--- a/public/app/features/variables/editor/VariableEditorEditor.tsx
+++ b/public/app/features/variables/editor/VariableEditorEditor.tsx
@@ -1,11 +1,11 @@
import React, { ChangeEvent, FormEvent, PureComponent } from 'react';
import isEqual from 'lodash/isEqual';
-import { AppEvents } from '@grafana/data';
+import { AppEvents, VariableType } from '@grafana/data';
import { FormLabel } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
import { variableAdapters } from '../adapters';
import { NEW_VARIABLE_ID, toVariablePayload, VariableIdentifier } from '../state/types';
-import { VariableHide, VariableModel, VariableType } from '../../templating/types';
+import { VariableHide, VariableModel } from '../../templating/types';
import { appEvents } from '../../../core/core';
import { VariableValuesPreview } from './VariableValuesPreview';
import { changeVariableName, onEditorAdd, onEditorUpdate, variableEditorMount, variableEditorUnMount } from './actions';
diff --git a/public/app/features/variables/editor/actions.ts b/public/app/features/variables/editor/actions.ts
index 11f072274ed..879903f4de6 100644
--- a/public/app/features/variables/editor/actions.ts
+++ b/public/app/features/variables/editor/actions.ts
@@ -17,7 +17,7 @@ import {
VariableIdentifier,
} from '../state/types';
import cloneDeep from 'lodash/cloneDeep';
-import { VariableType } from '../../templating/types';
+import { VariableType } from '@grafana/data';
import { addVariable, removeVariable, storeNewVariable } from '../state/sharedReducer';
export const variableEditorMount = (identifier: VariableIdentifier): ThunkResult => {
diff --git a/public/app/features/variables/interval/IntervalVariableEditor.tsx b/public/app/features/variables/interval/IntervalVariableEditor.tsx
index 05504e27af5..d203ec65b56 100644
--- a/public/app/features/variables/interval/IntervalVariableEditor.tsx
+++ b/public/app/features/variables/interval/IntervalVariableEditor.tsx
@@ -2,7 +2,8 @@ import React, { ChangeEvent, FocusEvent, PureComponent } from 'react';
import { IntervalVariableModel } from '../../templating/types';
import { VariableEditorProps } from '../editor/types';
-import { FormLabel, Switch } from '@grafana/ui';
+import { FormLabel, LegacyForms } from '@grafana/ui';
+const { Switch } = LegacyForms;
export interface Props extends VariableEditorProps {}
diff --git a/public/app/features/variables/pickers/PickerRenderer.tsx b/public/app/features/variables/pickers/PickerRenderer.tsx
index aa5d78316bf..2592147ba25 100644
--- a/public/app/features/variables/pickers/PickerRenderer.tsx
+++ b/public/app/features/variables/pickers/PickerRenderer.tsx
@@ -19,7 +19,7 @@ export const PickerRenderer: FunctionComponent = props => {
{props.variable.hide === VariableHide.dontHide && (
{labelOrName}
diff --git a/public/app/features/variables/query/QueryVariableEditor.tsx b/public/app/features/variables/query/QueryVariableEditor.tsx
index cc6261ead30..434004dbade 100644
--- a/public/app/features/variables/query/QueryVariableEditor.tsx
+++ b/public/app/features/variables/query/QueryVariableEditor.tsx
@@ -1,6 +1,7 @@
import React, { ChangeEvent, PureComponent } from 'react';
import { e2e } from '@grafana/e2e';
-import { FormLabel, Switch } from '@grafana/ui';
+import { FormLabel, LegacyForms } from '@grafana/ui';
+const { Switch } = LegacyForms;
import templateSrv from '../../templating/template_srv';
import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor';
diff --git a/public/app/features/variables/state/reducers.test.ts b/public/app/features/variables/state/reducers.test.ts
index cdec57ec326..b76320f44ef 100644
--- a/public/app/features/variables/state/reducers.test.ts
+++ b/public/app/features/variables/state/reducers.test.ts
@@ -1,10 +1,11 @@
import { reducerTester } from '../../../../test/core/redux/reducerTester';
import { cleanUpDashboard } from 'app/features/dashboard/state/reducers';
-import { QueryVariableModel, VariableHide, VariableType } from '../../templating/types';
+import { QueryVariableModel, VariableHide } from '../../templating/types';
import { VariableAdapter, variableAdapters } from '../adapters';
import { createAction } from '@reduxjs/toolkit';
import { variablesReducer, VariablesState } from './variablesReducer';
import { toVariablePayload, VariablePayload } from './types';
+import { VariableType } from '@grafana/data';
const variableAdapter: VariableAdapter = {
id: ('mock' as unknown) as VariableType,
diff --git a/public/app/features/variables/state/sharedReducer.ts b/public/app/features/variables/state/sharedReducer.ts
index 5baee615e17..e99d473a969 100644
--- a/public/app/features/variables/state/sharedReducer.ts
+++ b/public/app/features/variables/state/sharedReducer.ts
@@ -1,7 +1,8 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import cloneDeep from 'lodash/cloneDeep';
-import { VariableModel, VariableOption, VariableType, VariableWithOptions } from '../../templating/types';
+import { VariableType } from '@grafana/data';
+import { VariableModel, VariableOption, VariableWithOptions } from '../../templating/types';
import { AddVariable, ALL_VARIABLE_VALUE, getInstanceState, NEW_VARIABLE_ID, VariablePayload } from './types';
import { variableAdapters } from '../adapters';
import { changeVariableNameSucceeded } from '../editor/reducer';
diff --git a/public/app/features/variables/state/types.ts b/public/app/features/variables/state/types.ts
index 76a4e53cdd0..6171a722747 100644
--- a/public/app/features/variables/state/types.ts
+++ b/public/app/features/variables/state/types.ts
@@ -1,5 +1,6 @@
-import { VariableModel, VariableType } from '../../templating/types';
+import { VariableModel } from '../../templating/types';
import { VariablesState } from './variablesReducer';
+import { VariableType } from '@grafana/data';
export const NEW_VARIABLE_ID = '00000000-0000-0000-0000-000000000000';
export const ALL_VARIABLE_TEXT = 'All';
diff --git a/public/app/plugins/datasource/cloudwatch/components/AnnotationQueryEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/AnnotationQueryEditor.tsx
index a5fdc14c967..0acf0a5a8d4 100644
--- a/public/app/plugins/datasource/cloudwatch/components/AnnotationQueryEditor.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/AnnotationQueryEditor.tsx
@@ -1,5 +1,6 @@
import React, { ChangeEvent } from 'react';
-import { Switch } from '@grafana/ui';
+import { LegacyForms } from '@grafana/ui';
+const { Switch } = LegacyForms;
import { PanelData } from '@grafana/data';
import { CloudWatchQuery, AnnotationQuery } from '../types';
import CloudWatchDatasource from '../datasource';
diff --git a/public/app/plugins/datasource/cloudwatch/components/QueryEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/QueryEditor.tsx
index 1d866b8c2fc..f3b64de2c30 100644
--- a/public/app/plugins/datasource/cloudwatch/components/QueryEditor.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/QueryEditor.tsx
@@ -1,7 +1,7 @@
import React, { PureComponent, ChangeEvent } from 'react';
import { ExploreQueryFieldProps } from '@grafana/data';
-import { LegacyForms, ValidationEvents, EventsWithValidation, Switch } from '@grafana/ui';
-const { Input } = LegacyForms;
+import { LegacyForms, ValidationEvents, EventsWithValidation } from '@grafana/ui';
+const { Input, Switch } = LegacyForms;
import isEmpty from 'lodash/isEmpty';
import { CloudWatchQuery } from '../types';
import CloudWatchDatasource from '../datasource';
diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AnalyticsConfig.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AnalyticsConfig.tsx
index efc612af5fd..dfb776ae761 100644
--- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AnalyticsConfig.tsx
+++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AnalyticsConfig.tsx
@@ -1,8 +1,8 @@
import React, { PureComponent, ChangeEvent } from 'react';
import { SelectableValue } from '@grafana/data';
import { AzureCredentialsForm } from './AzureCredentialsForm';
-import { Switch, FormLabel, LegacyForms, Button } from '@grafana/ui';
-const { Select } = LegacyForms;
+import { FormLabel, LegacyForms, Button } from '@grafana/ui';
+const { Select, Switch } = LegacyForms;
import { AzureDataSourceSettings } from '../types';
export interface State {
diff --git a/public/app/plugins/datasource/graphite/configuration/ConfigEditor.tsx b/public/app/plugins/datasource/graphite/configuration/ConfigEditor.tsx
index b3903bc70ba..0ea91c9667d 100644
--- a/public/app/plugins/datasource/graphite/configuration/ConfigEditor.tsx
+++ b/public/app/plugins/datasource/graphite/configuration/ConfigEditor.tsx
@@ -1,6 +1,6 @@
import React, { PureComponent } from 'react';
-import { DataSourceHttpSettings, FormLabel, LegacyForms, Switch } from '@grafana/ui';
-const { Select } = LegacyForms;
+import { DataSourceHttpSettings, FormLabel, LegacyForms } from '@grafana/ui';
+const { Select, Switch } = LegacyForms;
import {
DataSourcePluginOptionsEditorProps,
onUpdateDatasourceJsonDataOptionSelect,
diff --git a/public/app/plugins/datasource/loki/configuration/DerivedField.tsx b/public/app/plugins/datasource/loki/configuration/DerivedField.tsx
index 4f6b4eb153a..7ab1dd8414d 100644
--- a/public/app/plugins/datasource/loki/configuration/DerivedField.tsx
+++ b/public/app/plugins/datasource/loki/configuration/DerivedField.tsx
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { css } from 'emotion';
-import { Button, FormField, DataLinkInput, stylesFactory, Switch } from '@grafana/ui';
+import { Button, FormField, DataLinkInput, stylesFactory, LegacyForms } from '@grafana/ui';
+const { Switch } = LegacyForms;
import { VariableSuggestion } from '@grafana/data';
import { DataSourceSelectItem } from '@grafana/data';
diff --git a/public/app/plugins/datasource/prometheus/components/PromQueryEditor.tsx b/public/app/plugins/datasource/prometheus/components/PromQueryEditor.tsx
index c7a5b26097f..9dc9d911076 100644
--- a/public/app/plugins/datasource/prometheus/components/PromQueryEditor.tsx
+++ b/public/app/plugins/datasource/prometheus/components/PromQueryEditor.tsx
@@ -2,10 +2,10 @@ import _ from 'lodash';
import React, { PureComponent } from 'react';
// Types
-import { FormLabel, LegacyForms, Switch } from '@grafana/ui';
+import { FormLabel, LegacyForms } from '@grafana/ui';
import { SelectableValue, QueryEditorProps } from '@grafana/data';
-const { Select } = LegacyForms;
+const { Select, Switch } = LegacyForms;
import { PrometheusDatasource } from '../datasource';
import { PromQuery, PromOptions } from '../types';
diff --git a/public/app/plugins/panel/annolist/AnnoListEditor.tsx b/public/app/plugins/panel/annolist/AnnoListEditor.tsx
index 78f5eb336df..b6ea26425b3 100644
--- a/public/app/plugins/panel/annolist/AnnoListEditor.tsx
+++ b/public/app/plugins/panel/annolist/AnnoListEditor.tsx
@@ -2,7 +2,8 @@
import React, { PureComponent, ChangeEvent } from 'react';
// Components
-import { PanelOptionsGroup, PanelOptionsGrid, Switch, FormField, FormLabel } from '@grafana/ui';
+import { PanelOptionsGroup, PanelOptionsGrid, FormField, FormLabel, LegacyForms } from '@grafana/ui';
+const { Switch } = LegacyForms;
import { PanelEditorProps, toIntegerOrUndefined, toNumberString } from '@grafana/data';
diff --git a/public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx b/public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx
index 8a9daea9d1f..841d4874a98 100644
--- a/public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx
+++ b/public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx
@@ -7,13 +7,12 @@ import {
PanelOptionsGroup,
FormLabel,
LegacyForms,
- Switch,
FieldPropertiesEditor,
ThresholdsEditor,
LegacyValueMappingsEditor,
DataLinksEditor,
} from '@grafana/ui';
-const { Select } = LegacyForms;
+const { Select, Switch } = LegacyForms;
import {
DataLink,
FieldConfig,
diff --git a/public/app/plugins/panel/bargauge/module.tsx b/public/app/plugins/panel/bargauge/module.tsx
index 6b44bfd0a3a..0bea792ad71 100644
--- a/public/app/plugins/panel/bargauge/module.tsx
+++ b/public/app/plugins/panel/bargauge/module.tsx
@@ -9,12 +9,13 @@ import { barGaugePanelMigrationHandler } from './BarGaugeMigrations';
export const plugin = new PanelPlugin(BarGaugePanel)
.setDefaults(defaults)
.setEditor(BarGaugePanelEditor)
+ .useFieldConfig()
.setPanelOptions(builder => {
addStandardDataReduceOptions(builder);
builder
.addRadio({
- id: 'displayMode',
+ path: 'displayMode',
name: 'Display mode',
description: 'Controls the bar style',
settings: {
@@ -26,11 +27,10 @@ export const plugin = new PanelPlugin(BarGaugePanel)
},
})
.addBooleanSwitch({
- id: 'showUnfilled',
+ path: 'showUnfilled',
name: 'Show unfilled area',
description: 'When enabled renders the unfilled region as gray',
});
})
.setPanelChangeHandler(sharedSingleStatPanelChangedHandler)
- .setMigrationHandler(barGaugePanelMigrationHandler)
- .useStandardFieldConfig();
+ .setMigrationHandler(barGaugePanelMigrationHandler);
diff --git a/public/app/plugins/panel/gauge/GaugePanelEditor.tsx b/public/app/plugins/panel/gauge/GaugePanelEditor.tsx
index 4c00b7eb6e7..9ef96b68d93 100644
--- a/public/app/plugins/panel/gauge/GaugePanelEditor.tsx
+++ b/public/app/plugins/panel/gauge/GaugePanelEditor.tsx
@@ -3,13 +3,14 @@ import React, { PureComponent } from 'react';
import {
PanelOptionsGrid,
FieldDisplayEditor,
- Switch,
+ LegacyForms,
PanelOptionsGroup,
FieldPropertiesEditor,
ThresholdsEditor,
LegacyValueMappingsEditor,
DataLinksEditor,
} from '@grafana/ui';
+const { Switch } = LegacyForms;
import {
PanelEditorProps,
ReduceDataOptions,
diff --git a/public/app/plugins/panel/gauge/module.tsx b/public/app/plugins/panel/gauge/module.tsx
index 16ad4b049c9..e2292bdac4c 100644
--- a/public/app/plugins/panel/gauge/module.tsx
+++ b/public/app/plugins/panel/gauge/module.tsx
@@ -8,21 +8,20 @@ import { gaugePanelMigrationHandler, gaugePanelChangedHandler } from './GaugeMig
export const plugin = new PanelPlugin(GaugePanel)
.setDefaults(defaults)
.setEditor(GaugePanelEditor)
+ .useFieldConfig()
.setPanelOptions(builder => {
addStandardDataReduceOptions(builder);
-
builder
.addBooleanSwitch({
- id: 'showThresholdLabels',
+ path: 'showThresholdLabels',
name: 'Show threshold Labels',
description: 'Render the threshold values around the gauge bar',
})
.addBooleanSwitch({
- id: 'showThresholdMarkers',
+ path: 'showThresholdMarkers',
name: 'Show threshold markers',
description: 'Renders the thresholds as an outer bar',
});
})
.setPanelChangeHandler(gaugePanelChangedHandler)
- .setMigrationHandler(gaugePanelMigrationHandler)
- .useStandardFieldConfig();
+ .setMigrationHandler(gaugePanelMigrationHandler);
diff --git a/public/app/plugins/panel/graph2/GraphLegendEditor.tsx b/public/app/plugins/panel/graph2/GraphLegendEditor.tsx
index 158022d72ba..284bb09529b 100644
--- a/public/app/plugins/panel/graph2/GraphLegendEditor.tsx
+++ b/public/app/plugins/panel/graph2/GraphLegendEditor.tsx
@@ -1,6 +1,6 @@
import React from 'react';
-import { LegendOptions, PanelOptionsGroup, Switch, LegacyForms, StatsPicker } from '@grafana/ui';
-const { Input } = LegacyForms;
+import { LegendOptions, PanelOptionsGroup, LegacyForms, StatsPicker } from '@grafana/ui';
+const { Input, Switch } = LegacyForms;
export interface GraphLegendEditorLegendOptions extends LegendOptions {
stats?: string[];
diff --git a/public/app/plugins/panel/graph2/GraphPanelEditor.tsx b/public/app/plugins/panel/graph2/GraphPanelEditor.tsx
index 370ac06154d..c34b55e6bda 100644
--- a/public/app/plugins/panel/graph2/GraphPanelEditor.tsx
+++ b/public/app/plugins/panel/graph2/GraphPanelEditor.tsx
@@ -5,7 +5,6 @@ import React, { PureComponent } from 'react';
// Types
import { FieldConfig, PanelEditorProps } from '@grafana/data';
import {
- Switch,
LegendOptions,
GraphTooltipOptions,
PanelOptionsGrid,
@@ -13,7 +12,7 @@ import {
LegacyForms,
FieldPropertiesEditor,
} from '@grafana/ui';
-const { Select } = LegacyForms;
+const { Select, Switch } = LegacyForms;
import { Options, GraphOptions } from './types';
import { GraphLegendEditor } from './GraphLegendEditor';
import { NewPanelEditorContext } from 'app/features/dashboard/components/PanelEditor/PanelEditor';
diff --git a/public/app/plugins/panel/logs/LogsPanelEditor.tsx b/public/app/plugins/panel/logs/LogsPanelEditor.tsx
index 8118a887cdd..4126dd2d733 100644
--- a/public/app/plugins/panel/logs/LogsPanelEditor.tsx
+++ b/public/app/plugins/panel/logs/LogsPanelEditor.tsx
@@ -1,7 +1,7 @@
// Libraries
import React, { PureComponent } from 'react';
-import { Switch, PanelOptionsGrid, PanelOptionsGroup, FormLabel, LegacyForms } from '@grafana/ui';
-const { Select } = LegacyForms;
+import { PanelOptionsGrid, PanelOptionsGroup, FormLabel, LegacyForms } from '@grafana/ui';
+const { Select, Switch } = LegacyForms;
// Types
import { Options } from './types';
diff --git a/public/app/plugins/panel/piechart/module.tsx b/public/app/plugins/panel/piechart/module.tsx
index d3bca4a506e..c5dedf9ac67 100644
--- a/public/app/plugins/panel/piechart/module.tsx
+++ b/public/app/plugins/panel/piechart/module.tsx
@@ -1,11 +1,8 @@
-import { PanelPlugin, FieldConfigProperty } from '@grafana/data';
+import { PanelPlugin } from '@grafana/data';
import { PieChartPanelEditor } from './PieChartPanelEditor';
import { PieChartPanel } from './PieChartPanel';
import { PieChartOptions, defaults } from './types';
export const plugin = new PanelPlugin(PieChartPanel)
.setDefaults(defaults)
- .useStandardFieldConfig(null, {
- [FieldConfigProperty.Unit]: 'short',
- })
.setEditor(PieChartPanelEditor);
diff --git a/public/app/plugins/panel/stat/StatPanel.tsx b/public/app/plugins/panel/stat/StatPanel.tsx
index 47f2da9821e..ecb6ccf2118 100644
--- a/public/app/plugins/panel/stat/StatPanel.tsx
+++ b/public/app/plugins/panel/stat/StatPanel.tsx
@@ -95,6 +95,7 @@ export class StatPanel extends PureComponent> {
width={width}
height={height}
source={data}
+ itemSpacing={3}
renderCounter={renderCounter}
autoGrid={true}
orientation={options.orientation}
diff --git a/public/app/plugins/panel/stat/module.tsx b/public/app/plugins/panel/stat/module.tsx
index c872adfa174..8c18855525b 100644
--- a/public/app/plugins/panel/stat/module.tsx
+++ b/public/app/plugins/panel/stat/module.tsx
@@ -7,12 +7,13 @@ import { StatPanelEditor } from './StatPanelEditor';
export const plugin = new PanelPlugin(StatPanel)
.setDefaults(defaults)
.setEditor(StatPanelEditor)
+ .useFieldConfig()
.setPanelOptions(builder => {
addStandardDataReduceOptions(builder);
builder
.addRadio({
- id: 'colorMode',
+ path: 'colorMode',
name: 'Color mode',
description: 'Color either the value or the background',
settings: {
@@ -23,7 +24,7 @@ export const plugin = new PanelPlugin(StatPanel)
},
})
.addRadio({
- id: 'graphMode',
+ path: 'graphMode',
name: 'Graph mode',
description: 'Stat panel graph / sparkline mode',
settings: {
@@ -34,7 +35,7 @@ export const plugin = new PanelPlugin(StatPanel)
},
})
.addRadio({
- id: 'justifyMode',
+ path: 'justifyMode',
name: 'Justify mode',
description: 'Value & title posititioning',
settings: {
@@ -47,5 +48,4 @@ export const plugin = new PanelPlugin(StatPanel)
})
.setNoPadding()
.setPanelChangeHandler(sharedSingleStatPanelChangedHandler)
- .setMigrationHandler(sharedSingleStatMigrationHandler)
- .useStandardFieldConfig();
+ .setMigrationHandler(sharedSingleStatMigrationHandler);
diff --git a/public/app/plugins/panel/stat/types.ts b/public/app/plugins/panel/stat/types.ts
index 8426d9c138c..7500f66f8f3 100644
--- a/public/app/plugins/panel/stat/types.ts
+++ b/public/app/plugins/panel/stat/types.ts
@@ -1,13 +1,5 @@
import { SingleStatBaseOptions, BigValueColorMode, BigValueGraphMode, BigValueJustifyMode } from '@grafana/ui';
-import {
- VizOrientation,
- ReducerID,
- ReduceDataOptions,
- SelectableValue,
- ThresholdsMode,
- standardEditorsRegistry,
- FieldConfigProperty,
-} from '@grafana/data';
+import { VizOrientation, ReducerID, ReduceDataOptions, SelectableValue, standardEditorsRegistry } from '@grafana/data';
import { PanelOptionsEditorBuilder } from '@grafana/data/src/utils/OptionsUIBuilders';
// Structure copied from angular
@@ -37,20 +29,9 @@ export const commonValueOptionDefaults: ReduceDataOptions = {
calcs: [ReducerID.mean],
};
-export const standardFieldConfigDefaults: Partial> = {
- [FieldConfigProperty.Thresholds]: {
- mode: ThresholdsMode.Absolute,
- steps: [
- { value: -Infinity, color: 'green' },
- { value: 80, color: 'red' },
- ],
- },
- [FieldConfigProperty.Mappings]: [],
-};
-
export function addStandardDataReduceOptions(builder: PanelOptionsEditorBuilder) {
builder.addRadio({
- id: 'reduceOptions.values',
+ path: 'reduceOptions.values',
name: 'Show',
description: 'Calculate a single value per colum or series or show each row',
settings: {
@@ -62,7 +43,7 @@ export function addStandardDataReduceOptions(builder: PanelOptionsEditorBuilder<
});
builder.addNumberInput({
- id: 'reduceOptions.limit',
+ path: 'reduceOptions.limit',
name: 'Limit',
description: 'Max number of rows to display',
settings: {
@@ -75,13 +56,14 @@ export function addStandardDataReduceOptions(builder: PanelOptionsEditorBuilder<
builder.addCustomEditor({
id: 'reduceOptions.calcs',
+ path: 'reduceOptions.calcs',
name: 'Value',
description: 'Choose a reducer function / calculation',
editor: standardEditorsRegistry.get('stats-picker').editor as any,
});
builder.addRadio({
- id: 'orientation',
+ path: 'orientation',
name: 'Orientation',
description: 'Stacking direction in case of multiple series or fields',
settings: {
diff --git a/public/app/plugins/panel/table2/TablePanel.tsx b/public/app/plugins/panel/table2/TablePanel.tsx
index ea01b9c772d..74d6bd21686 100644
--- a/public/app/plugins/panel/table2/TablePanel.tsx
+++ b/public/app/plugins/panel/table2/TablePanel.tsx
@@ -1,9 +1,7 @@
-// Libraries
import React, { Component } from 'react';
-// Types
import { Table } from '@grafana/ui';
-import { PanelProps } from '@grafana/data';
+import { Field, FieldMatcherID, PanelProps } from '@grafana/data';
import { Options } from './types';
interface Props extends PanelProps {}
@@ -13,13 +11,39 @@ export class TablePanel extends Component {
super(props);
}
+ onColumnResize = (field: Field, width: number) => {
+ const current = this.props.fieldConfig;
+ const matcherId = FieldMatcherID.byName;
+ const prop = 'width';
+ const overrides = current.overrides.filter(
+ o => o.matcher.id !== matcherId || o.matcher.options !== field.name || o.properties[0].id !== prop
+ );
+
+ overrides.push({
+ matcher: { id: matcherId, options: field.name },
+ properties: [{ id: prop, value: width }],
+ });
+
+ this.props.onFieldConfigChange({
+ ...current,
+ overrides,
+ });
+ };
+
render() {
- const { data, height, width, options } = this.props;
+ const {
+ data,
+ height,
+ width,
+ options: { showHeader, resizable },
+ } = this.props;
if (data.series.length < 1) {
return No Table Data...
;
}
- return ;
+ return (
+
+ );
}
}
diff --git a/public/app/plugins/panel/table2/TablePanelEditor.tsx b/public/app/plugins/panel/table2/TablePanelEditor.tsx
index 80d9811c0b7..fec7d93cc62 100644
--- a/public/app/plugins/panel/table2/TablePanelEditor.tsx
+++ b/public/app/plugins/panel/table2/TablePanelEditor.tsx
@@ -1,10 +1,9 @@
//// Libraries
-import _ from 'lodash';
import React, { PureComponent } from 'react';
-
// Types
import { PanelEditorProps } from '@grafana/data';
-import { Switch } from '@grafana/ui';
+import { LegacyForms } from '@grafana/ui';
+const { Switch } = LegacyForms;
import { Options } from './types';
export class TablePanelEditor extends PureComponent> {
diff --git a/public/app/plugins/panel/table2/module.tsx b/public/app/plugins/panel/table2/module.tsx
index 5fb17e3108e..d3aacc6d635 100644
--- a/public/app/plugins/panel/table2/module.tsx
+++ b/public/app/plugins/panel/table2/module.tsx
@@ -4,37 +4,59 @@ import { CustomFieldConfig, defaults, Options } from './types';
export const plugin = new PanelPlugin(TablePanel)
.setDefaults(defaults)
- .setCustomFieldOptions(builder => {
- builder
- .addNumberInput({
- id: 'width',
- name: 'Column width',
- description: 'column width (for table)',
- settings: {
- placeholder: 'auto',
- min: 20,
- max: 300,
- },
- defaultValue: 1,
- })
- .addSelect({
- id: 'displayMode',
- name: 'Cell display mode',
- description: 'Color value, background, show as gauge, etc',
- settings: {
- options: [
- { value: 'auto', label: 'Auto' },
- { value: 'color-background', label: 'Color background' },
- { value: 'gradient-gauge', label: 'Gradient gauge' },
- { value: 'lcd-gauge', label: 'LCD gauge' },
- ],
- },
- });
+ .useFieldConfig({
+ useCustomConfig: builder => {
+ builder
+ .addNumberInput({
+ path: 'width',
+ name: 'Column width',
+ description: 'column width (for table)',
+ settings: {
+ placeholder: 'auto',
+ min: 20,
+ max: 300,
+ },
+ })
+ .addRadio({
+ path: 'align',
+ name: 'Column alignment',
+ description: 'column alignment (for table)',
+ settings: {
+ options: [
+ { label: 'auto', value: null },
+ { label: 'left', value: 'left' },
+ { label: 'center', value: 'center' },
+ { label: 'right', value: 'right' },
+ ],
+ },
+ defaultValue: null,
+ })
+ .addSelect({
+ path: 'displayMode',
+ name: 'Cell display mode',
+ description: 'Color value, background, show as gauge, etc',
+ settings: {
+ options: [
+ { value: 'auto', label: 'Auto' },
+ { value: 'color-background', label: 'Color background' },
+ { value: 'gradient-gauge', label: 'Gradient gauge' },
+ { value: 'lcd-gauge', label: 'LCD gauge' },
+ ],
+ },
+ });
+ },
})
.setPanelOptions(builder => {
builder.addBooleanSwitch({
- id: 'showHeader',
+ path: 'showHeader',
name: 'Show header',
description: "To display table's header or not to display",
});
+ })
+ .setPanelOptions(builder => {
+ builder.addBooleanSwitch({
+ path: 'resizable',
+ name: 'Resizable',
+ description: 'Toggles if table columns are resizable or not',
+ });
});
diff --git a/public/app/plugins/panel/table2/types.ts b/public/app/plugins/panel/table2/types.ts
index d927f72e07a..fff20eedb9d 100644
--- a/public/app/plugins/panel/table2/types.ts
+++ b/public/app/plugins/panel/table2/types.ts
@@ -1,5 +1,6 @@
export interface Options {
showHeader: boolean;
+ resizable: boolean;
}
export interface CustomFieldConfig {
@@ -9,4 +10,5 @@ export interface CustomFieldConfig {
export const defaults: Options = {
showHeader: true,
+ resizable: false,
};
diff --git a/public/sass/_variables.dark.generated.scss b/public/sass/_variables.dark.generated.scss
index fefd0a01524..eee9f1551a7 100644
--- a/public/sass/_variables.dark.generated.scss
+++ b/public/sass/_variables.dark.generated.scss
@@ -93,7 +93,7 @@ $body-bg: #0b0c0e;
$page-bg: #141619;
$body-color: #d8d9da;
-$text-color: #d8d9da;
+$text-color: #c7d0d9;
$text-color-strong: #ffffff;
$text-color-weak: #8e8e8e;
$text-color-faint: #222426;
@@ -207,13 +207,13 @@ $input-bg: $input-black;
$input-bg-disabled: $dark-6;
$input-color: #c7d0d9;
-$input-border-color: $dark-6;
+$input-border-color: #202226;
$input-box-shadow: inset 1px 0px 4px 0px rgba(150, 150, 150, 0.1);
-$input-border-focus: $dark-6 !default;
+$input-border-focus: #5794f2;
$input-box-shadow-focus: $blue-light !default;
$input-color-placeholder: #555555;
-$input-label-bg: $gray-blue;
-$input-label-border-color: $dark-6;
+$input-label-bg: #202226;
+$input-label-border-color: #202226;
$input-color-select-arrow: $white;
// Search
diff --git a/public/sass/_variables.generated.scss b/public/sass/_variables.generated.scss
index 3f03aad0c45..bc77bbd7959 100644
--- a/public/sass/_variables.generated.scss
+++ b/public/sass/_variables.generated.scss
@@ -152,8 +152,8 @@ $input-border-radius-sm: 0 $border-radius-sm $border-radius-sm 0 !default;
$label-border-radius: $border-radius 0 0 $border-radius !default;
$label-border-radius-sm: $border-radius-sm 0 0 $border-radius-sm !default;
-$input-padding: 8px;
-$input-height: 35px !default;
+$input-padding: 0 8px;
+$input-height: 32px !default;
$cursor-disabled: not-allowed !default;
diff --git a/public/sass/_variables.light.generated.scss b/public/sass/_variables.light.generated.scss
index 0303fab8a87..3d2412b51a0 100644
--- a/public/sass/_variables.light.generated.scss
+++ b/public/sass/_variables.light.generated.scss
@@ -198,15 +198,15 @@ $btn-active-box-shadow: 0px 0px 4px rgba(234, 161, 51, 0.6);
$input-bg: $white;
$input-bg-disabled: $gray-5;
-$input-color: $dark-2;
-$input-border-color: $gray-5;
+$input-color: #343b40;
+$input-border-color: #e9edf2;
$input-box-shadow: none;
-$input-border-focus: $gray-5 !default;
-$input-box-shadow-focus: $blue-light !default;
+$input-border-focus: #5794f2;
+$input-box-shadow-focus: #5794f2;
$input-color-placeholder: #9fa7b3;
-$input-label-bg: $gray-5;
-$input-label-border-color: $gray-5;
-$input-color-select-arrow: $gray-1;
+$input-label-bg: #e9edf2;
+$input-label-border-color: #e9edf2;
+$input-color-select-arrow: #7b8087;
// search
$search-shadow: 0 1px 5px 0 $gray-5;
diff --git a/public/sass/components/_gf-form.scss b/public/sass/components/_gf-form.scss
index a451adf43c3..cf5815e3b16 100644
--- a/public/sass/components/_gf-form.scss
+++ b/public/sass/components/_gf-form.scss
@@ -101,13 +101,14 @@ $input-border: 1px solid $input-border-color;
}
.gf-form-label {
+ display: flex;
+ align-items: center;
padding: $input-padding;
flex-shrink: 0;
font-weight: $font-weight-semi-bold;
font-size: $font-size-sm;
background-color: $input-label-bg;
- display: block;
height: $input-height;
border: $border-width solid $input-label-border-color;
@@ -129,6 +130,13 @@ $input-border: 1px solid $input-border-color;
padding-left: 0px;
}
+ &--variable {
+ color: $variable;
+ background: $panel-bg;
+ border: $panel-border;
+ border-right: none;
+ }
+
&--btn {
border-right: $border-width solid $input-label-border-color;
border-radius: $border-radius;
diff --git a/public/sass/components/_panel_editor.scss b/public/sass/components/_panel_editor.scss
index 9aa97d2abe2..0b7b2cb014f 100644
--- a/public/sass/components/_panel_editor.scss
+++ b/public/sass/components/_panel_editor.scss
@@ -70,7 +70,7 @@
}
.panel-editor__content {
- padding: 15px;
+ padding: 16px;
}
.panel-in-fullscreen {
diff --git a/public/sass/components/_query_editor.scss b/public/sass/components/_query_editor.scss
index df4c8e94ae5..6b7a4b230d7 100644
--- a/public/sass/components/_query_editor.scss
+++ b/public/sass/components/_query_editor.scss
@@ -30,6 +30,15 @@
margin-right: 2px;
}
+ .gf-form-label {
+ border-right: 1px solid $input-border-color;
+ border-radius: $border-radius;
+ }
+
+ .gf-form-input {
+ border-radius: $border-radius;
+ }
+
.gf-form + .gf-form {
margin-left: 0;
}
@@ -137,7 +146,7 @@ input[type='text'].tight-form-func-param {
padding: 4px 0px 4px 8px;
position: relative;
height: 35px;
- background: $page-bg;
+ background: $input-label-bg;
flex-wrap: nowrap;
align-items: center;
}
diff --git a/public/sass/components/_slate_editor.scss b/public/sass/components/_slate_editor.scss
index b53b05881a8..7067a447244 100644
--- a/public/sass/components/_slate_editor.scss
+++ b/public/sass/components/_slate_editor.scss
@@ -8,7 +8,7 @@
.slate-query-field__wrapper {
position: relative;
display: inline-block;
- padding: $input-padding;
+ padding: 6px 8px;
min-height: $input-height;
width: 100%;
cursor: text;
diff --git a/public/sass/components/_submenu.scss b/public/sass/components/_submenu.scss
index e4a580ba889..4fee427e072 100644
--- a/public/sass/components/_submenu.scss
+++ b/public/sass/components/_submenu.scss
@@ -28,19 +28,18 @@
.fa-caret-down {
font-size: 75%;
- position: relative;
- top: -1px;
- left: 1px;
+ padding-left: 8px;
}
}
.variable-value-link {
padding-right: 10px;
- padding: $space-sm;
+ padding: 0 $space-sm;
background-color: $input-bg;
border: 1px solid $input-border-color;
border-radius: $input-border-radius;
- display: inline-block;
+ display: flex;
+ align-items: center;
color: $text-color;
height: $input-height;
diff --git a/public/sass/mixins/_forms.scss b/public/sass/mixins/_forms.scss
index 727f1af1bee..dc51cbe5152 100644
--- a/public/sass/mixins/_forms.scss
+++ b/public/sass/mixins/_forms.scss
@@ -34,8 +34,6 @@
&:focus {
border-color: $input-border-focus;
outline: none;
- $shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 5px $input-box-shadow-focus;
- @include box-shadow($shadow);
}
}
diff --git a/public/sass/pages/_dashboard.scss b/public/sass/pages/_dashboard.scss
index a4535a982aa..32660716c97 100644
--- a/public/sass/pages/_dashboard.scss
+++ b/public/sass/pages/_dashboard.scss
@@ -5,10 +5,6 @@
box-sizing: border-box;
}
-.template-variable {
- color: $variable;
-}
-
div.flot-text {
color: $text-color !important;
}
@@ -56,8 +52,8 @@ div.flot-text {
width: 100%;
&--transparent {
- background-color: $page-bg;
- border: none;
+ background-color: transparent;
+ border: 1px solid transparent;
}
&:hover {
diff --git a/vendor/github.com/grafana/grafana-plugin-sdk-go/data/arrow.go b/vendor/github.com/grafana/grafana-plugin-sdk-go/data/arrow.go
index f4a93ccd9fd..2a8bf726c99 100644
--- a/vendor/github.com/grafana/grafana-plugin-sdk-go/data/arrow.go
+++ b/vendor/github.com/grafana/grafana-plugin-sdk-go/data/arrow.go
@@ -657,7 +657,7 @@ func UnmarshalArrow(b []byte) (*Frame, error) {
if metaAsString, ok := getMDKey("meta", metaData); ok {
var err error
- frame.Meta, err = QueryResultMetaFromJSON(metaAsString)
+ frame.Meta, err = FrameMetaFromJSON(metaAsString)
if err != nil {
return nil, err
}
@@ -692,3 +692,29 @@ func toJSONString(val interface{}) (string, error) {
}
return string(b), nil
}
+
+// BytesSliceToFrames decodes a slice of encoded Arrow frames to a slice of *Frame.
+func BytesSliceToFrames(bFrames [][]byte) ([]*Frame, error) {
+ frames := make([]*Frame, len(bFrames))
+ var err error
+ for i, encodedFrame := range bFrames {
+ frames[i], err = UnmarshalArrow(encodedFrame)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return frames, nil
+}
+
+// FramesToBytesSlice encodes a slice of Frames into a slice of []byte.
+func FramesToBytesSlice(frames []*Frame) ([][]byte, error) {
+ bs := make([][]byte, len(frames))
+ var err error
+ for i, frame := range frames {
+ bs[i], err = MarshalArrow(frame)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return bs, nil
+}
diff --git a/vendor/github.com/grafana/grafana-plugin-sdk-go/data/frame.go b/vendor/github.com/grafana/grafana-plugin-sdk-go/data/frame.go
index 92a596a7086..2cd529fd1cb 100644
--- a/vendor/github.com/grafana/grafana-plugin-sdk-go/data/frame.go
+++ b/vendor/github.com/grafana/grafana-plugin-sdk-go/data/frame.go
@@ -28,7 +28,7 @@ type Frame struct {
Fields []*Field
RefID string
- Meta *QueryResultMeta
+ Meta *FrameMeta
Warnings []Warning
}
@@ -263,29 +263,13 @@ func FrameTestCompareOptions() []cmp.Option {
if x == nil && y == nil {
return true
}
- if y == nil {
- if math.IsNaN(float64(*x)) {
- return true
- }
- if math.IsInf(float64(*x), 1) {
- return true
- }
- if math.IsInf(float64(*x), -1) {
- return true
- }
+ if y == nil && x != nil || y != nil && x == nil {
+ return false
}
- if x == nil {
- if math.IsNaN(float64(*y)) {
- return true
- }
- if math.IsInf(float64(*y), 1) {
- return true
- }
- if math.IsInf(float64(*y), -1) {
- return true
- }
- }
- return *x == *y
+ return (math.IsNaN(float64(*x)) && math.IsNaN(float64(*y))) ||
+ (math.IsInf(float64(*x), 1) && math.IsInf(float64(*y), 1)) ||
+ (math.IsInf(float64(*x), -1) && math.IsInf(float64(*y), -1)) ||
+ *x == *y
})
f64s := cmp.Comparer(func(x, y float64) bool {
return (math.IsNaN(x) && math.IsNaN(y)) ||
@@ -297,29 +281,13 @@ func FrameTestCompareOptions() []cmp.Option {
if x == nil && y == nil {
return true
}
- if y == nil {
- if math.IsNaN(float64(*x)) {
- return true
- }
- if math.IsInf(float64(*x), 1) {
- return true
- }
- if math.IsInf(float64(*x), -1) {
- return true
- }
+ if y == nil && x != nil || y != nil && x == nil {
+ return false
}
- if x == nil {
- if math.IsNaN(float64(*y)) {
- return true
- }
- if math.IsInf(float64(*y), 1) {
- return true
- }
- if math.IsInf(float64(*y), -1) {
- return true
- }
- }
- return *x == *y
+ return (math.IsNaN(float64(*x)) && math.IsNaN(float64(*y))) ||
+ (math.IsInf(float64(*x), 1) && math.IsInf(float64(*y), 1)) ||
+ (math.IsInf(float64(*x), -1) && math.IsInf(float64(*y), -1)) ||
+ *x == *y
})
f32s := cmp.Comparer(func(x, y float32) bool {
return (math.IsNaN(float64(x)) && math.IsNaN(float64(y))) ||
@@ -369,6 +337,8 @@ func (f *Frame) StringTable(maxFields, maxRows int) (string, error) {
sb := &strings.Builder{}
sb.WriteString(fmt.Sprintf("Name: %v\n", f.Name))
+ sb.WriteString(fmt.Sprintf("Dimensions: %v Fields by %v Rows\n", len(f.Fields), rowLen))
+
table := tablewriter.NewWriter(sb)
// table formatting options
@@ -377,9 +347,6 @@ func (f *Frame) StringTable(maxFields, maxRows int) (string, error) {
table.SetAutoWrapText(false)
table.SetAlignment(tablewriter.ALIGN_LEFT)
- // (caption is below the table)
- table.SetCaption(true, fmt.Sprintf("Field Count: %v\nRow Count: %v", len(f.Fields), rowLen))
-
// set table headers
headers := make([]string, width)
for colIdx, field := range f.Fields {
diff --git a/vendor/github.com/grafana/grafana-plugin-sdk-go/data/query_result_meta.go b/vendor/github.com/grafana/grafana-plugin-sdk-go/data/frame_meta.go
similarity index 52%
rename from vendor/github.com/grafana/grafana-plugin-sdk-go/data/query_result_meta.go
rename to vendor/github.com/grafana/grafana-plugin-sdk-go/data/frame_meta.go
index 5cf72f5fc24..e022087c1c3 100644
--- a/vendor/github.com/grafana/grafana-plugin-sdk-go/data/query_result_meta.go
+++ b/vendor/github.com/grafana/grafana-plugin-sdk-go/data/frame_meta.go
@@ -2,24 +2,24 @@ package data
import "encoding/json"
-// QueryResultMeta matches:
+// FrameMeta matches:
// https://github.com/grafana/grafana/blob/master/packages/grafana-data/src/types/data.ts#L11
// NOTE -- in javascript this can accept any `[key: string]: any;` however
// this interface only exposes the values we want to be exposed
-type QueryResultMeta struct {
- // Used in Explore for highlighting
- SearchWords []string `json:"searchWords,omitempty"`
-
- // Used in Explore to show limit applied to search result
- Limit int64 `json:"limit,omitempty"`
-
+type FrameMeta struct {
// Datasource specific values
Custom map[string]interface{} `json:"custom,omitempty"`
+
+ // Stats is TODO
+ Stats interface{} `json:"stats,omitempty"`
+
+ // Notices is TODO
+ Notices interface{} `json:"notices,omitempty"`
}
-// QueryResultMetaFromJSON creates a QueryResultMeta from a json string
-func QueryResultMetaFromJSON(jsonStr string) (*QueryResultMeta, error) {
- var m QueryResultMeta
+// FrameMetaFromJSON creates a QueryResultMeta from a json string
+func FrameMetaFromJSON(jsonStr string) (*FrameMeta, error) {
+ var m FrameMeta
err := json.Unmarshal([]byte(jsonStr), &m)
if err != nil {
return nil, err
diff --git a/vendor/github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2/backend.pb.go b/vendor/github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2/backend.pb.go
index 7b5b7e52ef2..f36289a27ee 100644
--- a/vendor/github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2/backend.pb.go
+++ b/vendor/github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2/backend.pb.go
@@ -49,7 +49,7 @@ func (x CheckHealthResponse_HealthStatus) String() string {
}
func (CheckHealthResponse_HealthStatus) EnumDescriptor() ([]byte, []int) {
- return fileDescriptor_5ab9ba5b8d8b2ba5, []int{13, 0}
+ return fileDescriptor_5ab9ba5b8d8b2ba5, []int{14, 0}
}
type DataSourceConfig struct {
@@ -677,14 +677,11 @@ func (m *QueryDataRequest) GetQueries() []*DataQuery {
}
type QueryDataResponse struct {
- // Arrow encoded DataFrames
- // Each frame encodes its own: Errors, meta, and refId
- Frames [][]byte `protobuf:"bytes,1,rep,name=frames,proto3" json:"frames,omitempty"`
- // Additional response metadata
- Metadata map[string]string `protobuf:"bytes,2,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ // Map of refId to response
+ Responses map[string]*DataResponse `protobuf:"bytes,1,rep,name=responses,proto3" json:"responses,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
}
func (m *QueryDataResponse) Reset() { *m = QueryDataResponse{} }
@@ -712,16 +709,66 @@ func (m *QueryDataResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_QueryDataResponse proto.InternalMessageInfo
-func (m *QueryDataResponse) GetFrames() [][]byte {
+func (m *QueryDataResponse) GetResponses() map[string]*DataResponse {
+ if m != nil {
+ return m.Responses
+ }
+ return nil
+}
+
+type DataResponse struct {
+ // Arrow encoded DataFrames
+ // Frame has its own meta, warnings, and repeats refId
+ Frames [][]byte `protobuf:"bytes,1,rep,name=frames,proto3" json:"frames,omitempty"`
+ Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"`
+ JsonMeta []byte `protobuf:"bytes,3,opt,name=jsonMeta,proto3" json:"jsonMeta,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *DataResponse) Reset() { *m = DataResponse{} }
+func (m *DataResponse) String() string { return proto.CompactTextString(m) }
+func (*DataResponse) ProtoMessage() {}
+func (*DataResponse) Descriptor() ([]byte, []int) {
+ return fileDescriptor_5ab9ba5b8d8b2ba5, []int{10}
+}
+
+func (m *DataResponse) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_DataResponse.Unmarshal(m, b)
+}
+func (m *DataResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_DataResponse.Marshal(b, m, deterministic)
+}
+func (m *DataResponse) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_DataResponse.Merge(m, src)
+}
+func (m *DataResponse) XXX_Size() int {
+ return xxx_messageInfo_DataResponse.Size(m)
+}
+func (m *DataResponse) XXX_DiscardUnknown() {
+ xxx_messageInfo_DataResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_DataResponse proto.InternalMessageInfo
+
+func (m *DataResponse) GetFrames() [][]byte {
if m != nil {
return m.Frames
}
return nil
}
-func (m *QueryDataResponse) GetMetadata() map[string]string {
+func (m *DataResponse) GetError() string {
if m != nil {
- return m.Metadata
+ return m.Error
+ }
+ return ""
+}
+
+func (m *DataResponse) GetJsonMeta() []byte {
+ if m != nil {
+ return m.JsonMeta
}
return nil
}
@@ -736,7 +783,7 @@ func (m *CollectMetricsRequest) Reset() { *m = CollectMetricsRequest{} }
func (m *CollectMetricsRequest) String() string { return proto.CompactTextString(m) }
func (*CollectMetricsRequest) ProtoMessage() {}
func (*CollectMetricsRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_5ab9ba5b8d8b2ba5, []int{10}
+ return fileDescriptor_5ab9ba5b8d8b2ba5, []int{11}
}
func (m *CollectMetricsRequest) XXX_Unmarshal(b []byte) error {
@@ -768,7 +815,7 @@ func (m *CollectMetricsResponse) Reset() { *m = CollectMetricsResponse{}
func (m *CollectMetricsResponse) String() string { return proto.CompactTextString(m) }
func (*CollectMetricsResponse) ProtoMessage() {}
func (*CollectMetricsResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_5ab9ba5b8d8b2ba5, []int{11}
+ return fileDescriptor_5ab9ba5b8d8b2ba5, []int{12}
}
func (m *CollectMetricsResponse) XXX_Unmarshal(b []byte) error {
@@ -807,7 +854,7 @@ func (m *CollectMetricsResponse_Payload) Reset() { *m = CollectMetricsRe
func (m *CollectMetricsResponse_Payload) String() string { return proto.CompactTextString(m) }
func (*CollectMetricsResponse_Payload) ProtoMessage() {}
func (*CollectMetricsResponse_Payload) Descriptor() ([]byte, []int) {
- return fileDescriptor_5ab9ba5b8d8b2ba5, []int{11, 0}
+ return fileDescriptor_5ab9ba5b8d8b2ba5, []int{12, 0}
}
func (m *CollectMetricsResponse_Payload) XXX_Unmarshal(b []byte) error {
@@ -846,7 +893,7 @@ func (m *CheckHealthRequest) Reset() { *m = CheckHealthRequest{} }
func (m *CheckHealthRequest) String() string { return proto.CompactTextString(m) }
func (*CheckHealthRequest) ProtoMessage() {}
func (*CheckHealthRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_5ab9ba5b8d8b2ba5, []int{12}
+ return fileDescriptor_5ab9ba5b8d8b2ba5, []int{13}
}
func (m *CheckHealthRequest) XXX_Unmarshal(b []byte) error {
@@ -887,7 +934,7 @@ func (m *CheckHealthResponse) Reset() { *m = CheckHealthResponse{} }
func (m *CheckHealthResponse) String() string { return proto.CompactTextString(m) }
func (*CheckHealthResponse) ProtoMessage() {}
func (*CheckHealthResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_5ab9ba5b8d8b2ba5, []int{13}
+ return fileDescriptor_5ab9ba5b8d8b2ba5, []int{14}
}
func (m *CheckHealthResponse) XXX_Unmarshal(b []byte) error {
@@ -946,7 +993,8 @@ func init() {
proto.RegisterType((*QueryDataRequest)(nil), "pluginv2.QueryDataRequest")
proto.RegisterMapType((map[string]string)(nil), "pluginv2.QueryDataRequest.HeadersEntry")
proto.RegisterType((*QueryDataResponse)(nil), "pluginv2.QueryDataResponse")
- proto.RegisterMapType((map[string]string)(nil), "pluginv2.QueryDataResponse.MetadataEntry")
+ proto.RegisterMapType((map[string]*DataResponse)(nil), "pluginv2.QueryDataResponse.ResponsesEntry")
+ proto.RegisterType((*DataResponse)(nil), "pluginv2.DataResponse")
proto.RegisterType((*CollectMetricsRequest)(nil), "pluginv2.CollectMetricsRequest")
proto.RegisterType((*CollectMetricsResponse)(nil), "pluginv2.CollectMetricsResponse")
proto.RegisterType((*CollectMetricsResponse_Payload)(nil), "pluginv2.CollectMetricsResponse.Payload")
@@ -957,77 +1005,79 @@ func init() {
func init() { proto.RegisterFile("backend.proto", fileDescriptor_5ab9ba5b8d8b2ba5) }
var fileDescriptor_5ab9ba5b8d8b2ba5 = []byte{
- // 1115 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xcd, 0x6e, 0xdb, 0x46,
- 0x10, 0x2e, 0x49, 0xfd, 0x71, 0xa4, 0x18, 0xea, 0x5a, 0x71, 0x08, 0x35, 0x49, 0x05, 0x22, 0x40,
- 0x95, 0x14, 0x15, 0x5a, 0xe5, 0xd0, 0x22, 0x39, 0xc5, 0x96, 0x0a, 0xc7, 0x8e, 0x7f, 0xba, 0x8a,
- 0x51, 0x20, 0x45, 0x0f, 0x2b, 0x72, 0x2d, 0xb1, 0xe6, 0x8f, 0xcc, 0x5d, 0x1a, 0xf5, 0x03, 0x14,
- 0x7d, 0x81, 0x3e, 0x47, 0xfa, 0x14, 0x3d, 0xf5, 0xd8, 0x43, 0x5f, 0xa7, 0xd8, 0xe5, 0x52, 0x24,
- 0x6d, 0xc9, 0x46, 0x5b, 0xe7, 0xb6, 0x33, 0x9c, 0x9d, 0x9d, 0xf9, 0xe6, 0x9b, 0xcf, 0x32, 0xdc,
- 0x9b, 0x12, 0xe7, 0x8c, 0x86, 0xee, 0x60, 0x11, 0x47, 0x3c, 0x42, 0x8d, 0x85, 0x9f, 0xcc, 0xbc,
- 0xf0, 0x62, 0x68, 0xff, 0x69, 0x40, 0x7b, 0x44, 0x38, 0x99, 0x44, 0x49, 0xec, 0xd0, 0x9d, 0x28,
- 0x3c, 0xf5, 0x66, 0x68, 0x03, 0x74, 0xcf, 0xb5, 0xb4, 0x9e, 0xd6, 0x37, 0xb0, 0xee, 0xb9, 0x08,
- 0x41, 0x25, 0x24, 0x01, 0xb5, 0xf4, 0x9e, 0xd6, 0x37, 0xb1, 0x3c, 0xa3, 0x36, 0x18, 0x49, 0xec,
- 0x5b, 0x86, 0x74, 0x89, 0xa3, 0x88, 0x4a, 0x18, 0x8d, 0xad, 0x4a, 0x1a, 0x25, 0xce, 0xa8, 0x0b,
- 0x0d, 0x97, 0x70, 0x32, 0x25, 0x8c, 0x5a, 0x55, 0xe9, 0x5f, 0xda, 0xe8, 0x19, 0xb4, 0xa7, 0x84,
- 0x79, 0xce, 0xab, 0x84, 0xcf, 0xc7, 0x21, 0x99, 0xfa, 0xd4, 0xb5, 0x6a, 0x3d, 0xad, 0xdf, 0xc0,
- 0xd7, 0xfc, 0xe8, 0x89, 0xe8, 0x40, 0xf9, 0x4e, 0xc4, 0x23, 0x75, 0x99, 0xac, 0xec, 0x14, 0xaf,
- 0xfd, 0xc4, 0xa2, 0x50, 0xf4, 0x63, 0x35, 0x7a, 0x5a, 0xbf, 0x85, 0x97, 0x36, 0x3a, 0x87, 0x07,
- 0x2e, 0x75, 0xe2, 0xcb, 0x05, 0xa7, 0xee, 0x84, 0x3a, 0x49, 0x4c, 0xf7, 0xb2, 0x50, 0xb3, 0x67,
- 0xf4, 0x9b, 0xc3, 0xaf, 0x07, 0x19, 0x28, 0x83, 0xab, 0x80, 0x0c, 0x46, 0xab, 0x6f, 0x8e, 0x43,
- 0x1e, 0x5f, 0xe2, 0x75, 0x79, 0x45, 0xd1, 0x3e, 0x61, 0xfc, 0x64, 0xe1, 0x12, 0x4e, 0xdd, 0x83,
- 0x89, 0x05, 0x12, 0xd1, 0xb2, 0xb3, 0xbb, 0x07, 0x0f, 0x6f, 0x4a, 0x2f, 0x80, 0x3e, 0xa3, 0x97,
- 0x72, 0x1a, 0x26, 0x16, 0x47, 0xd4, 0x81, 0xea, 0x05, 0xf1, 0x93, 0x6c, 0x1e, 0xa9, 0xf1, 0x42,
- 0xff, 0x46, 0xb3, 0x7f, 0x31, 0xa0, 0x75, 0x2c, 0xbb, 0x50, 0x93, 0xec, 0x40, 0x35, 0x8a, 0x67,
- 0xaf, 0xb3, 0x61, 0xa6, 0x86, 0xc0, 0x29, 0xed, 0xf5, 0xb5, 0xab, 0x72, 0x2c, 0xed, 0x12, 0x86,
- 0xc6, 0x15, 0x0c, 0x83, 0xf5, 0x18, 0x56, 0x24, 0x86, 0xcf, 0x73, 0x0c, 0x8b, 0x65, 0xdc, 0x15,
- 0x7e, 0xd5, 0x15, 0xf8, 0xa1, 0x6f, 0xa1, 0x2d, 0x28, 0xc5, 0x0a, 0xf3, 0x92, 0x34, 0x6a, 0x0e,
- 0xbb, 0xeb, 0x27, 0x8a, 0xaf, 0xdd, 0xb9, 0xd3, 0x39, 0xbc, 0x83, 0x8a, 0x24, 0x64, 0x07, 0xaa,
- 0x7e, 0x34, 0xf3, 0x42, 0x75, 0x2b, 0x35, 0x56, 0xae, 0x53, 0x07, 0xaa, 0x34, 0x20, 0x5e, 0xb6,
- 0x50, 0xa9, 0x21, 0x22, 0xe3, 0xc8, 0xa7, 0xd9, 0x4a, 0x89, 0xb3, 0xfd, 0x04, 0x60, 0xc2, 0x63,
- 0x2f, 0x9c, 0xbd, 0xf1, 0x18, 0x47, 0x5b, 0x50, 0x93, 0xcf, 0x32, 0x4b, 0xeb, 0x19, 0x7d, 0x13,
- 0x2b, 0xcb, 0xfe, 0x4b, 0x87, 0xcd, 0x1d, 0xe2, 0xfb, 0x98, 0xa6, 0x4d, 0x62, 0x7a, 0x9e, 0x50,
- 0xc6, 0xd1, 0x00, 0x6a, 0x4e, 0x8a, 0x91, 0x26, 0x31, 0xda, 0x5a, 0x3d, 0x31, 0xac, 0xa2, 0x90,
- 0xad, 0x96, 0x5a, 0x97, 0xd1, 0x1b, 0x79, 0xb4, 0xe8, 0x4f, 0x2d, 0x39, 0x82, 0xca, 0x82, 0xf0,
- 0xb9, 0x2a, 0x5d, 0x9e, 0x45, 0x5d, 0x01, 0xe5, 0xf3, 0xc8, 0x55, 0xb5, 0x2b, 0x2b, 0x93, 0x8d,
- 0x6a, 0x2e, 0x1b, 0x23, 0xa8, 0xcf, 0x29, 0x71, 0x69, 0xcc, 0xac, 0x9a, 0x24, 0xd1, 0xb3, 0xfc,
- 0x91, 0x15, 0x1d, 0x0c, 0x76, 0xd3, 0xe0, 0x94, 0x3b, 0xd9, 0x55, 0x51, 0xc3, 0x34, 0x72, 0x2f,
- 0xa5, 0x2e, 0xb4, 0xb0, 0x3c, 0x77, 0x8f, 0xa1, 0x55, 0x0c, 0x5e, 0x31, 0xc1, 0x67, 0xc5, 0x09,
- 0x36, 0x87, 0x9d, 0xfc, 0xe5, 0x1c, 0xe2, 0xe2, 0x5c, 0xff, 0xd6, 0xa0, 0x53, 0xae, 0x89, 0x2d,
- 0xa2, 0x90, 0x51, 0xf1, 0xbc, 0x13, 0xb9, 0x54, 0xe6, 0xae, 0x62, 0x79, 0x46, 0xe3, 0xbc, 0x31,
- 0x5d, 0x36, 0xf6, 0xf9, 0xba, 0xc6, 0xd2, 0x24, 0xb7, 0x74, 0x66, 0x7c, 0xd0, 0xce, 0xf6, 0xc1,
- 0x7c, 0xeb, 0x05, 0x14, 0x93, 0x70, 0x46, 0x51, 0x0f, 0x9a, 0xa7, 0x71, 0x14, 0x8c, 0x17, 0x91,
- 0x33, 0x3f, 0x98, 0x28, 0xed, 0x28, 0xba, 0xd0, 0x43, 0x30, 0x79, 0x94, 0x7d, 0xd7, 0xe5, 0xf7,
- 0xdc, 0x61, 0xbf, 0xd7, 0xc0, 0x14, 0x8b, 0xf3, 0x5d, 0x42, 0x63, 0xb9, 0x26, 0x31, 0x3d, 0x55,
- 0x1a, 0x64, 0xe2, 0xd4, 0x10, 0xcb, 0x1d, 0x90, 0x9f, 0x45, 0xd4, 0x71, 0xe4, 0x85, 0x9c, 0xa9,
- 0x2c, 0x65, 0x27, 0x7a, 0x0c, 0xe0, 0x85, 0x9c, 0xc6, 0x17, 0xc4, 0x3f, 0x98, 0x48, 0x08, 0x0c,
- 0x5c, 0xf0, 0xa0, 0xaf, 0xc0, 0xe4, 0x59, 0xd9, 0x92, 0x69, 0xcd, 0xe1, 0x66, 0xde, 0xea, 0xb2,
- 0x23, 0x9c, 0x47, 0x09, 0x3c, 0x85, 0xa0, 0x49, 0x0a, 0xb6, 0xb0, 0x3c, 0xdb, 0xbf, 0xe9, 0xd0,
- 0x96, 0xc5, 0x8a, 0xa7, 0x3f, 0xe4, 0xaa, 0xbc, 0xca, 0x39, 0x61, 0x48, 0x4e, 0x7c, 0x96, 0x87,
- 0x5d, 0x2d, 0x60, 0x0d, 0x1f, 0xbe, 0x80, 0xfa, 0x79, 0x42, 0x63, 0x8f, 0x32, 0x25, 0xba, 0x9b,
- 0x65, 0x99, 0x93, 0x69, 0x70, 0x16, 0xd3, 0x7d, 0x71, 0x2b, 0x55, 0xd6, 0xcb, 0xd8, 0xef, 0x1a,
- 0x7c, 0x5c, 0xa8, 0x4a, 0x71, 0x7d, 0x0b, 0x6a, 0xa7, 0x31, 0x09, 0x94, 0xe4, 0xb4, 0xb0, 0xb2,
- 0xd0, 0x18, 0x1a, 0x01, 0xe5, 0x44, 0x08, 0xab, 0x22, 0xfc, 0xd3, 0x95, 0xcd, 0x29, 0xb6, 0x1f,
- 0xa8, 0xd8, 0xb4, 0xbd, 0xe5, 0xd5, 0xee, 0x4b, 0xb8, 0x57, 0xfa, 0xf4, 0xaf, 0x2a, 0x7e, 0x00,
- 0xf7, 0x77, 0x22, 0xdf, 0xa7, 0x0e, 0x3f, 0xa0, 0x3c, 0xf6, 0x1c, 0xa6, 0xb0, 0xb4, 0x7f, 0xd5,
- 0x60, 0xeb, 0xea, 0x17, 0xd5, 0xcf, 0x36, 0xd4, 0x83, 0xd4, 0xa5, 0x06, 0xdd, 0x2f, 0xec, 0xe9,
- 0xca, 0x2b, 0x83, 0x63, 0x72, 0xe9, 0x47, 0xc4, 0xc5, 0xd9, 0xc5, 0xee, 0x53, 0xa8, 0x2b, 0x9f,
- 0xa0, 0xec, 0x22, 0x8e, 0x84, 0xdc, 0xd1, 0x24, 0xcd, 0xd8, 0xc2, 0x05, 0x8f, 0x3d, 0x02, 0xb4,
- 0x33, 0xa7, 0xce, 0xd9, 0x2e, 0x25, 0x3e, 0x9f, 0xff, 0x47, 0xb2, 0xd9, 0x7f, 0x68, 0xb0, 0x59,
- 0x4a, 0xb3, 0x6c, 0xa6, 0xc6, 0x38, 0xe1, 0xea, 0xe5, 0x8d, 0x92, 0x98, 0x5e, 0x0f, 0x1f, 0xa4,
- 0xe6, 0x44, 0xde, 0xc0, 0xea, 0x26, 0xb2, 0x04, 0x20, 0x8c, 0x91, 0x59, 0x06, 0x70, 0x66, 0x0a,
- 0x61, 0x90, 0x3f, 0x06, 0x28, 0x27, 0x9e, 0xcf, 0x94, 0x24, 0x15, 0x5d, 0xf6, 0x40, 0xd2, 0x6d,
- 0x99, 0x13, 0x35, 0xa1, 0x7e, 0x72, 0xb8, 0x7f, 0x78, 0xf4, 0xfd, 0x61, 0xfb, 0x23, 0x54, 0x03,
- 0xfd, 0x68, 0xbf, 0xad, 0x21, 0x13, 0xaa, 0x63, 0x8c, 0x8f, 0x70, 0x5b, 0x1f, 0xfe, 0x00, 0x8d,
- 0x4c, 0x07, 0xd1, 0x11, 0xb4, 0x8a, 0xba, 0x88, 0x1e, 0xdd, 0xf8, 0x87, 0xa0, 0xfb, 0xf8, 0x66,
- 0x39, 0xfd, 0x52, 0x1b, 0xbe, 0x81, 0x8a, 0xfc, 0x21, 0x31, 0x02, 0x73, 0xc9, 0x3f, 0xd4, 0x5d,
- 0xbf, 0x71, 0xdd, 0x4f, 0x6e, 0x20, 0xec, 0xf0, 0xbd, 0x06, 0xcd, 0x91, 0x47, 0x66, 0x61, 0xc4,
- 0xb8, 0xe7, 0x30, 0xb4, 0x07, 0xcd, 0x02, 0xa4, 0xe8, 0xe1, 0x1a, 0xa4, 0xd3, 0xcc, 0x8f, 0x6e,
- 0x9c, 0x03, 0x9a, 0xc0, 0x46, 0x99, 0x6a, 0xe8, 0xd3, 0xf5, 0x24, 0x4c, 0x33, 0xf6, 0x6e, 0x63,
- 0xe9, 0xf0, 0x04, 0xcc, 0xb7, 0x31, 0x09, 0xd9, 0x69, 0x14, 0x07, 0x68, 0x17, 0xee, 0x2d, 0x8d,
- 0xff, 0x87, 0xc3, 0x8f, 0x70, 0xbf, 0x94, 0x49, 0x80, 0xbf, 0x4d, 0x9c, 0xb3, 0xbb, 0x81, 0x79,
- 0xbb, 0xf5, 0x0e, 0x06, 0x2f, 0xb3, 0xef, 0xd3, 0x9a, 0xfc, 0x87, 0xe5, 0xf9, 0x3f, 0x01, 0x00,
- 0x00, 0xff, 0xff, 0xfc, 0xf3, 0x6d, 0x5e, 0xc1, 0x0c, 0x00, 0x00,
+ // 1144 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xcd, 0x6e, 0xdb, 0xc6,
+ 0x13, 0xff, 0x53, 0x9f, 0xe6, 0x48, 0x31, 0xf4, 0x5f, 0x2b, 0x8e, 0xa0, 0x26, 0xa9, 0x40, 0x04,
+ 0xa8, 0x9a, 0xb6, 0x44, 0xab, 0x1c, 0x5a, 0xa4, 0xa7, 0xd8, 0x72, 0xe1, 0xd8, 0xf1, 0x47, 0x57,
+ 0x36, 0x5a, 0xa4, 0xe8, 0x61, 0x45, 0xae, 0x25, 0xd6, 0x14, 0x57, 0xde, 0x5d, 0x1a, 0xf5, 0x03,
+ 0x14, 0x7d, 0x81, 0x3e, 0x47, 0x2e, 0x7d, 0x86, 0x9e, 0x7a, 0xec, 0xa1, 0xaf, 0x53, 0xec, 0x72,
+ 0x29, 0x92, 0xb6, 0x64, 0x03, 0xad, 0x73, 0x9b, 0x99, 0x9d, 0x9d, 0x9d, 0xf9, 0xcd, 0x6f, 0x86,
+ 0x12, 0x3c, 0x18, 0x13, 0xef, 0x9c, 0x46, 0xbe, 0x3b, 0xe7, 0x4c, 0x32, 0xb4, 0x36, 0x0f, 0xe3,
+ 0x49, 0x10, 0x5d, 0x0e, 0x9c, 0x3f, 0xcb, 0xd0, 0x1a, 0x12, 0x49, 0x46, 0x2c, 0xe6, 0x1e, 0xdd,
+ 0x66, 0xd1, 0x59, 0x30, 0x41, 0xeb, 0x50, 0x0a, 0xfc, 0x8e, 0xd5, 0xb3, 0xfa, 0x65, 0x5c, 0x0a,
+ 0x7c, 0x84, 0xa0, 0x12, 0x91, 0x19, 0xed, 0x94, 0x7a, 0x56, 0xdf, 0xc6, 0x5a, 0x46, 0x2d, 0x28,
+ 0xc7, 0x3c, 0xec, 0x94, 0xb5, 0x49, 0x89, 0xca, 0x2b, 0x16, 0x94, 0x77, 0x2a, 0x89, 0x97, 0x92,
+ 0x51, 0x17, 0xd6, 0x7c, 0x22, 0xc9, 0x98, 0x08, 0xda, 0xa9, 0x6a, 0xfb, 0x42, 0x47, 0xcf, 0xa1,
+ 0x35, 0x26, 0x22, 0xf0, 0x5e, 0xc5, 0x72, 0xba, 0x13, 0x91, 0x71, 0x48, 0xfd, 0x4e, 0xad, 0x67,
+ 0xf5, 0xd7, 0xf0, 0x0d, 0x3b, 0x7a, 0xa6, 0x2a, 0x30, 0xb6, 0x53, 0xf5, 0x48, 0x5d, 0x07, 0x2b,
+ 0x1a, 0xd5, 0x6b, 0x3f, 0x09, 0x16, 0xa9, 0x7a, 0x3a, 0x6b, 0x3d, 0xab, 0xdf, 0xc4, 0x0b, 0x1d,
+ 0x5d, 0xc0, 0x23, 0x9f, 0x7a, 0xfc, 0x6a, 0x2e, 0xa9, 0x3f, 0xa2, 0x5e, 0xcc, 0xe9, 0x5e, 0xea,
+ 0x6a, 0xf7, 0xca, 0xfd, 0xc6, 0xe0, 0x4b, 0x37, 0x05, 0xc5, 0xbd, 0x0e, 0x88, 0x3b, 0x5c, 0x7e,
+ 0x73, 0x27, 0x92, 0xfc, 0x0a, 0xaf, 0x8a, 0xab, 0x92, 0x0e, 0x89, 0x90, 0xa7, 0x73, 0x9f, 0x48,
+ 0xea, 0x1f, 0x8c, 0x3a, 0xa0, 0x11, 0x2d, 0x1a, 0xbb, 0x7b, 0xf0, 0xf8, 0xb6, 0xf0, 0x0a, 0xe8,
+ 0x73, 0x7a, 0xa5, 0xbb, 0x61, 0x63, 0x25, 0xa2, 0x36, 0x54, 0x2f, 0x49, 0x18, 0xa7, 0xfd, 0x48,
+ 0x94, 0x97, 0xa5, 0xaf, 0x2c, 0xe7, 0x97, 0x32, 0x34, 0x8f, 0x75, 0x15, 0xa6, 0x93, 0x6d, 0xa8,
+ 0x32, 0x3e, 0x79, 0x9d, 0x36, 0x33, 0x51, 0x14, 0x4e, 0x49, 0xad, 0xaf, 0x7d, 0x13, 0x63, 0xa1,
+ 0x17, 0x30, 0x2c, 0x5f, 0xc3, 0x70, 0xb6, 0x1a, 0xc3, 0x8a, 0xc6, 0xf0, 0x45, 0x86, 0x61, 0x3e,
+ 0x8d, 0xfb, 0xc2, 0xaf, 0xba, 0x04, 0x3f, 0xf4, 0x0d, 0xb4, 0x14, 0xa5, 0x44, 0xae, 0x5f, 0x9a,
+ 0x46, 0x8d, 0x41, 0x77, 0x75, 0x47, 0xf1, 0x8d, 0x3b, 0xf7, 0xda, 0x87, 0xb7, 0x50, 0xd1, 0x84,
+ 0x6c, 0x43, 0x35, 0x64, 0x93, 0x20, 0x32, 0xb7, 0x12, 0x65, 0xe9, 0x38, 0xb5, 0xa1, 0x4a, 0x67,
+ 0x24, 0x48, 0x07, 0x2a, 0x51, 0x94, 0x27, 0x67, 0x21, 0x4d, 0x47, 0x4a, 0xc9, 0xce, 0x33, 0x80,
+ 0x91, 0xe4, 0x41, 0x34, 0x79, 0x13, 0x08, 0x89, 0x36, 0xa1, 0xa6, 0x9f, 0x15, 0x1d, 0xab, 0x57,
+ 0xee, 0xdb, 0xd8, 0x68, 0xce, 0x5f, 0x25, 0xd8, 0xd8, 0x26, 0x61, 0x88, 0x69, 0x52, 0x24, 0xa6,
+ 0x17, 0x31, 0x15, 0x12, 0xb9, 0x50, 0xf3, 0x12, 0x8c, 0x2c, 0x8d, 0xd1, 0xe6, 0xf2, 0x8e, 0x61,
+ 0xe3, 0x85, 0x1c, 0x33, 0xd4, 0x25, 0xed, 0xbd, 0x9e, 0x79, 0xab, 0xfa, 0xcc, 0x90, 0x23, 0xa8,
+ 0xcc, 0x89, 0x9c, 0x9a, 0xd4, 0xb5, 0xac, 0xf2, 0x9a, 0x51, 0x39, 0x65, 0xbe, 0xc9, 0xdd, 0x68,
+ 0xe9, 0xda, 0xa8, 0x66, 0x6b, 0x63, 0x08, 0xf5, 0x29, 0x25, 0x3e, 0xe5, 0xa2, 0x53, 0xd3, 0x24,
+ 0x7a, 0x9e, 0x3d, 0xb2, 0xa4, 0x02, 0x77, 0x37, 0x71, 0x4e, 0xb8, 0x93, 0x5e, 0x55, 0x39, 0x8c,
+ 0x99, 0x7f, 0xa5, 0xf7, 0x42, 0x13, 0x6b, 0xb9, 0x7b, 0x0c, 0xcd, 0xbc, 0xf3, 0x92, 0x0e, 0x3e,
+ 0xcf, 0x77, 0xb0, 0x31, 0x68, 0x67, 0x2f, 0x67, 0x10, 0xe7, 0xfb, 0xfa, 0xb7, 0x05, 0xed, 0x62,
+ 0x4e, 0x62, 0xce, 0x22, 0x41, 0xd5, 0xf3, 0x1e, 0xf3, 0xa9, 0x8e, 0x5d, 0xc5, 0x5a, 0x46, 0x3b,
+ 0x59, 0x61, 0x25, 0x5d, 0xd8, 0x27, 0xab, 0x0a, 0x4b, 0x82, 0xdc, 0x51, 0x59, 0xf9, 0xbd, 0x56,
+ 0xb6, 0x0f, 0xf6, 0x49, 0x30, 0xa3, 0x98, 0x44, 0x13, 0x8a, 0x7a, 0xd0, 0x38, 0xe3, 0x6c, 0xb6,
+ 0x33, 0x67, 0xde, 0xf4, 0x60, 0x64, 0x76, 0x47, 0xde, 0x84, 0x1e, 0x83, 0x2d, 0x59, 0x7a, 0x5e,
+ 0xd2, 0xe7, 0x99, 0xc1, 0x79, 0x67, 0x81, 0xad, 0x06, 0xe7, 0xdb, 0x98, 0x72, 0x3d, 0x26, 0x9c,
+ 0x9e, 0x99, 0x1d, 0x64, 0xe3, 0x44, 0x51, 0xc3, 0x3d, 0x23, 0x3f, 0x2b, 0xaf, 0x63, 0x16, 0x44,
+ 0x52, 0x98, 0x28, 0x45, 0x23, 0x7a, 0x0a, 0x10, 0x44, 0x92, 0xf2, 0x4b, 0x12, 0x1e, 0x8c, 0x34,
+ 0x04, 0x65, 0x9c, 0xb3, 0xa0, 0x2f, 0xc0, 0x96, 0x69, 0xda, 0x9a, 0x69, 0x8d, 0xc1, 0x46, 0x56,
+ 0xea, 0xa2, 0x22, 0x9c, 0x79, 0x29, 0x3c, 0xd5, 0x42, 0xd3, 0x14, 0x6c, 0x62, 0x2d, 0x3b, 0xbf,
+ 0x95, 0xa0, 0xa5, 0x93, 0x55, 0x4f, 0xbf, 0xcf, 0x51, 0x79, 0x95, 0x71, 0xa2, 0xac, 0x39, 0xf1,
+ 0x51, 0xe6, 0x76, 0x3d, 0x81, 0x15, 0x7c, 0xf8, 0x0c, 0xea, 0x17, 0x31, 0xe5, 0x01, 0x15, 0x66,
+ 0xe9, 0x6e, 0x14, 0xd7, 0x9c, 0x0e, 0x83, 0x53, 0x9f, 0xee, 0xcb, 0x3b, 0xa9, 0xb2, 0x7a, 0x8d,
+ 0xfd, 0x6e, 0xc1, 0xff, 0x73, 0x59, 0x19, 0xae, 0xef, 0x82, 0xcd, 0x8d, 0x9c, 0x6c, 0x9d, 0xc2,
+ 0xc8, 0xde, 0xf0, 0x77, 0x53, 0xc1, 0x14, 0x92, 0x5d, 0xee, 0x9e, 0xc0, 0x7a, 0xf1, 0x70, 0x49,
+ 0x76, 0x9f, 0x16, 0x89, 0xbc, 0x59, 0x2c, 0x36, 0xbd, 0x9e, 0xcf, 0xfa, 0x7b, 0x68, 0x16, 0xf2,
+ 0xdd, 0x84, 0xda, 0x19, 0x27, 0x33, 0x93, 0x6c, 0x13, 0x1b, 0x4d, 0xaf, 0x5c, 0xce, 0x19, 0x4f,
+ 0xeb, 0xd6, 0x4a, 0xfa, 0xfd, 0x3b, 0xa0, 0xc5, 0xef, 0x9f, 0xd2, 0x9d, 0x47, 0xf0, 0x70, 0x9b,
+ 0x85, 0x21, 0xf5, 0xe4, 0x01, 0x95, 0x3c, 0xf0, 0x84, 0xe9, 0x94, 0xf3, 0xab, 0x05, 0x9b, 0xd7,
+ 0x4f, 0xcc, 0xeb, 0x5b, 0x50, 0x9f, 0x25, 0x26, 0x43, 0xa3, 0x7e, 0x6e, 0x0b, 0x2c, 0xbd, 0xe2,
+ 0x1e, 0x93, 0xab, 0x90, 0x11, 0x1f, 0xa7, 0x17, 0xbb, 0x1f, 0x43, 0xdd, 0xd8, 0xd4, 0x40, 0xcc,
+ 0x39, 0x53, 0xcb, 0x94, 0xc6, 0x49, 0xc4, 0x26, 0xce, 0x59, 0x9c, 0x21, 0xa0, 0xed, 0x29, 0xf5,
+ 0xce, 0x77, 0x29, 0x09, 0xe5, 0xf4, 0x5f, 0x52, 0xd9, 0xf9, 0xc3, 0x82, 0x8d, 0x42, 0x98, 0x45,
+ 0x31, 0x35, 0x21, 0x89, 0x34, 0x2f, 0xaf, 0x17, 0x56, 0xf5, 0x4d, 0x77, 0x37, 0x51, 0x47, 0xfa,
+ 0x06, 0x36, 0x37, 0x51, 0x47, 0x01, 0x22, 0x04, 0x99, 0xa4, 0x84, 0x4b, 0x55, 0xb5, 0x76, 0xf4,
+ 0x4f, 0x0d, 0x2a, 0x49, 0x10, 0x0a, 0x83, 0x7e, 0xde, 0xe4, 0xb8, 0x9a, 0xcc, 0x8b, 0x98, 0xa8,
+ 0x01, 0xf5, 0xd3, 0xc3, 0xfd, 0xc3, 0xa3, 0xef, 0x0e, 0x5b, 0xff, 0x43, 0x35, 0x28, 0x1d, 0xed,
+ 0xb7, 0x2c, 0x64, 0x43, 0x75, 0x07, 0xe3, 0x23, 0xdc, 0x2a, 0x0d, 0x7e, 0x80, 0xb5, 0x74, 0xcb,
+ 0xa2, 0x23, 0x68, 0xe6, 0xb7, 0x2e, 0x7a, 0x72, 0xeb, 0x67, 0xa6, 0xfb, 0xf4, 0xf6, 0x65, 0xfd,
+ 0xb9, 0x35, 0x78, 0x03, 0x15, 0xfd, 0x33, 0x65, 0x08, 0xf6, 0x82, 0xf4, 0xa8, 0xbb, 0x7a, 0x9e,
+ 0xbb, 0x1f, 0xdc, 0x32, 0x25, 0x83, 0x77, 0x16, 0x34, 0x86, 0x01, 0x99, 0x44, 0x4c, 0xc8, 0xc0,
+ 0x13, 0x68, 0x0f, 0x1a, 0x39, 0x48, 0xd1, 0xe3, 0x15, 0x48, 0x27, 0x91, 0x9f, 0xdc, 0xda, 0x07,
+ 0x34, 0x82, 0xf5, 0x22, 0xd5, 0xd0, 0x87, 0xab, 0x49, 0x98, 0x44, 0xec, 0xdd, 0xc5, 0xd2, 0xc1,
+ 0x29, 0xd8, 0x27, 0x9c, 0x44, 0xe2, 0x8c, 0xf1, 0x19, 0xda, 0x85, 0x07, 0x0b, 0xe5, 0xbf, 0xe1,
+ 0xf0, 0x23, 0x3c, 0x2c, 0x44, 0x52, 0xe0, 0x6f, 0x11, 0xef, 0xfc, 0x7e, 0x60, 0xde, 0x6a, 0xbe,
+ 0x05, 0xf7, 0xeb, 0xf4, 0x7c, 0x5c, 0xd3, 0x7f, 0x87, 0x5e, 0xfc, 0x13, 0x00, 0x00, 0xff, 0xff,
+ 0x4e, 0x49, 0x76, 0x89, 0x1f, 0x0d, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 61ebf55d993..4cb9f7bf033 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -142,7 +142,7 @@ github.com/gosimple/slug
# github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4
github.com/grafana/grafana-plugin-model/go/datasource
github.com/grafana/grafana-plugin-model/go/renderer
-# github.com/grafana/grafana-plugin-sdk-go v0.33.0
+# github.com/grafana/grafana-plugin-sdk-go v0.35.0
github.com/grafana/grafana-plugin-sdk-go/backend/grpcplugin
github.com/grafana/grafana-plugin-sdk-go/data
github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2
diff --git a/yarn.lock b/yarn.lock
index 5ff03b4375f..f883859d8fd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5794,6 +5794,13 @@
"@types/history" "*"
"@types/react" "*"
+"@types/react-beautiful-dnd@12.1.2":
+ version "12.1.2"
+ resolved "https://registry.yarnpkg.com/@types/react-beautiful-dnd/-/react-beautiful-dnd-12.1.2.tgz#dfd1bdb072e92c1363e5f7a4c1842eaf95f77b21"
+ integrity sha512-h+0mA4cHmzL4BhyCniB6ZSSZhfO9LpXXbnhdAfa2k7klS03woiOT+Dh5AchY6eoQXk3vQVtqn40YY3u+MwFs8A==
+ dependencies:
+ "@types/react" "*"
+
"@types/react-color@3.0.1", "@types/react-color@^3.0.1":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@types/react-color/-/react-color-3.0.1.tgz#5433e2f503ea0e0831cbc6fd0c20f8157d93add0"
@@ -8138,16 +8145,6 @@ browserslist@^4.8.5:
electron-to-chromium "^1.3.341"
node-releases "^1.1.47"
-browserslist@^4.9.1:
- version "4.11.0"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.11.0.tgz#aef4357b10a8abda00f97aac7cd587b2082ba1ad"
- integrity sha512-WqEC7Yr5wUH5sg6ruR++v2SGOQYpyUdYYd4tZoAq1F7y+QXoLoYGXVbxhtaIqWmAJjtNTRjVD3HuJc1OXTel2A==
- dependencies:
- caniuse-lite "^1.0.30001035"
- electron-to-chromium "^1.3.380"
- node-releases "^1.1.52"
- pkg-up "^3.1.0"
-
bs-logger@0.x:
version "0.2.6"
resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8"
@@ -8445,6 +8442,11 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
+caniuse-db@1.0.30000772:
+ version "1.0.30000772"
+ resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000772.tgz#51aae891768286eade4a3d8319ea76d6a01b512b"
+ integrity sha1-UarokXaChureSj2DGep21qAbUSs=
+
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30000999:
version "1.0.30000999"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000999.tgz#427253a69ad7bea4aa8d8345687b8eec51ca0e43"
@@ -8465,11 +8467,6 @@ caniuse-lite@^1.0.30001023:
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001027.tgz#283e2ef17d94889cc216a22c6f85303d78ca852d"
integrity sha512-7xvKeErvXZFtUItTHgNtLgS9RJpVnwBlWX8jSo/BO8VsF6deszemZSkJJJA1KOKrXuzZH4WALpAJdq5EyfgMLg==
-caniuse-lite@^1.0.30001035:
- version "1.0.30001036"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001036.tgz#930ea5272010d8bf190d859159d757c0b398caf0"
- integrity sha512-jU8CIFIj2oR7r4W+5AKcsvWNVIb6Q6OZE3UsrXrZBHFtreT4YgTeOJtTucp+zSedEpTi3L5wASSP0LYIE3if6w==
-
capture-exit@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4"
@@ -9702,6 +9699,13 @@ css-blank-pseudo@^0.1.4:
dependencies:
postcss "^7.0.5"
+css-box-model@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.0.tgz#3a26377b4162b3200d2ede4b064ec5b6a75186d0"
+ integrity sha512-lri0br+jSNV0kkkiGEp9y9y3Njq2PmpqbeGWRFQJuZteZzY9iC9GZhQ8Y4WpPwM/2YocjHePxy14igJY7YKzkA==
+ dependencies:
+ tiny-invariant "^1.0.6"
+
css-color-names@0.0.4, css-color-names@^0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
@@ -10930,6 +10934,11 @@ electron-to-chromium@^1.3.341:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.349.tgz#663f26a69d348a462df47b4d7ab162a2f29bbcb7"
integrity sha512-uEb2zs6EJ6OZIqaMsCSliYVgzE/f7/s1fLWqtvRtHg/v5KBF2xds974fUnyatfxIDgkqzQVwFtam5KExqywx0Q==
+electron-to-chromium@^1.3.378:
+ version "1.3.395"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.395.tgz#2c531a0477bcc41deb440877d1f27509ed286aed"
+ integrity sha512-kdn2cX6hZXDdz/O2Q8tZscITlsSv1a/7bOq/fQs7QAJ9iaRlnhZPccarNhxZv1tXgmgwCnKp/1lJNYLOG8Dxiw==
+
electron-to-chromium@^1.3.380:
version "1.3.381"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.381.tgz#952678ff91a5f36175a3832358a6dd2de3bf62b7"
@@ -16686,7 +16695,7 @@ mem@^4.0.0:
mimic-fn "^2.0.0"
p-is-promise "^2.0.0"
-memoize-one@5.1.1, memoize-one@^5.0.0:
+memoize-one@5.1.1, "memoize-one@>=3.1.1 <6", memoize-one@^5.0.0, memoize-one@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==
@@ -20080,6 +20089,11 @@ quick-lru@^1.0.0:
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8"
integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=
+raf-schd@^4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.2.tgz#bd44c708188f2e84c810bf55fcea9231bcaed8a0"
+ integrity sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ==
+
raf@^3.1.0, raf@^3.4.0, raf@^3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
@@ -20318,6 +20332,19 @@ react-addons-create-fragment@^15.6.2:
loose-envify "^1.3.1"
object-assign "^4.1.0"
+react-beautiful-dnd@13.0.0:
+ version "13.0.0"
+ resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-13.0.0.tgz#f70cc8ff82b84bc718f8af157c9f95757a6c3b40"
+ integrity sha512-87It8sN0ineoC3nBW0SbQuTFXM6bUqM62uJGY4BtTf0yzPl8/3+bHMWkgIe0Z6m8e+gJgjWxefGRVfpE3VcdEg==
+ dependencies:
+ "@babel/runtime" "^7.8.4"
+ css-box-model "^1.2.0"
+ memoize-one "^5.1.1"
+ raf-schd "^4.0.2"
+ react-redux "^7.1.1"
+ redux "^4.0.4"
+ use-memo-one "^1.1.1"
+
react-calendar@2.19.2:
version "2.19.2"
resolved "https://registry.yarnpkg.com/react-calendar/-/react-calendar-2.19.2.tgz#496e78eb11a00aee1ae6b5d02d221ed1ca2db952"
@@ -20694,6 +20721,17 @@ react-redux@7.1.1:
prop-types "^15.7.2"
react-is "^16.9.0"
+react-redux@^7.1.1:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d"
+ integrity sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA==
+ dependencies:
+ "@babel/runtime" "^7.5.5"
+ hoist-non-react-statics "^3.3.0"
+ loose-envify "^1.4.0"
+ prop-types "^15.7.2"
+ react-is "^16.9.0"
+
react-resizable@^1.9.0:
version "1.10.1"
resolved "https://registry.yarnpkg.com/react-resizable/-/react-resizable-1.10.1.tgz#f0c2cf1d83b3470b87676ce6d6b02bbe3f4d8cd4"
@@ -21207,6 +21245,14 @@ redux@^3.6.0:
loose-envify "^1.1.0"
symbol-observable "^1.0.3"
+redux@^4.0.4:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
+ integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
+ dependencies:
+ loose-envify "^1.4.0"
+ symbol-observable "^1.2.0"
+
reflect.ownkeys@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460"
@@ -23920,6 +23966,11 @@ tiny-invariant@^1.0.1:
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.6.tgz#b3f9b38835e36a41c843a3b0907a5a7b3755de73"
integrity sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==
+tiny-invariant@^1.0.6:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
+ integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
+
tiny-warning@^0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-0.0.3.tgz#1807eb4c5f81784a6354d58ea1d5024f18c6c81f"
@@ -24718,6 +24769,11 @@ use-callback-ref@^1.2.1:
resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.2.1.tgz#898759ccb9e14be6c7a860abafa3ffbd826c89bb"
integrity sha512-C3nvxh0ZpaOxs9RCnWwAJ+7bJPwQI8LHF71LzbQ3BvzH5XkdtlkMadqElGevg5bYBDFip4sAnD4m06zAKebg1w==
+use-memo-one@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.1.tgz#39e6f08fe27e422a7d7b234b5f9056af313bd22c"
+ integrity sha512-oFfsyun+bP7RX8X2AskHNTxu+R3QdE/RC5IefMbqptmACAA/gfol1KDD5KRzPsGMa62sWxGZw+Ui43u6x4ddoQ==
+
use-sidecar@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.0.2.tgz#e72f582a75842f7de4ef8becd6235a4720ad8af6"