mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
React Migration: Migrates FolderPicker from angular to react (#21088)
* Angular/React: Migrates FolderPicker from angular to react * move to core * snap * minor changes * more removes * Managing creating new and saving in movetofolderctrl * Do not use new forms.field, fix select menu * FolderPicker responsible to creating new folder * create new as prop * snap * remove unnecessary things * remove console log * snap * fix null checks * add search debouncing * set folder to state, null check * typing folder * adding case for loadOptions * snap Co-authored-by: Peter Holmberg <peterholmberg@users.noreply.github.com>
This commit is contained in:
parent
ff6a082e23
commit
0d5c1e1bc7
@ -194,6 +194,9 @@ export function SelectBase<T>({
|
||||
return v === o.value || o.value === v.value;
|
||||
})[0];
|
||||
});
|
||||
} else if (loadOptions) {
|
||||
const hasValue = defaultValue || value;
|
||||
selectedValue = hasValue ? [hasValue] : [];
|
||||
} else {
|
||||
selectedValue = options.filter(o => o.value === value || o === value);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ export const getSelectStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
box-shadow: 0px 4px 4px ${menuShadowColor};
|
||||
position: relative;
|
||||
min-width: 100%;
|
||||
z-index: 1;
|
||||
`,
|
||||
option: css`
|
||||
padding: 8px;
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { getFormStyles } from './getFormStyles';
|
||||
import { Label } from './Label';
|
||||
import { Input } from './Input/Input';
|
||||
import { Select } from './Select/Select';
|
||||
import { ButtonSelect } from './Select/ButtonSelect';
|
||||
import { AsyncSelect, Select } from './Select/Select';
|
||||
import { Form } from './Form';
|
||||
import { Field } from './Field';
|
||||
import { Button, LinkButton } from './Button';
|
||||
@ -19,6 +19,7 @@ const Forms = {
|
||||
Select,
|
||||
ButtonSelect,
|
||||
InputControl,
|
||||
AsyncSelect,
|
||||
};
|
||||
|
||||
export default Forms;
|
||||
|
@ -23,6 +23,7 @@ import ReactProfileWrapper from 'app/features/profile/ReactProfileWrapper';
|
||||
import { LokiAnnotationsQueryEditor } from '../plugins/datasource/loki/components/AnnotationsQueryEditor';
|
||||
import { HelpModal } from './components/help/HelpModal';
|
||||
import { Footer } from './components/Footer/Footer';
|
||||
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
|
||||
|
||||
export function registerAngularDirectives() {
|
||||
react2AngularDirective('footer', Footer, []);
|
||||
@ -136,4 +137,18 @@ export function registerAngularDirectives() {
|
||||
'dataSourceConfig',
|
||||
['onChange', { watchDepth: 'reference', wrapApply: true }],
|
||||
]);
|
||||
react2AngularDirective('folderPicker', FolderPicker, [
|
||||
'labelClass',
|
||||
'rootName',
|
||||
'enableCreateNew',
|
||||
'enableReset',
|
||||
'initialTitle',
|
||||
'initialFolderId',
|
||||
'dashboardId',
|
||||
'onCreateFolder',
|
||||
['enterFolderCreation', { watchDepth: 'reference', wrapApply: true }],
|
||||
['exitFolderCreation', { watchDepth: 'reference', wrapApply: true }],
|
||||
['onLoad', { watchDepth: 'reference', wrapApply: true }],
|
||||
['onChange', { watchDepth: 'reference', wrapApply: true }],
|
||||
]);
|
||||
}
|
||||
|
25
public/app/core/components/Select/FolderPicker.test.tsx
Normal file
25
public/app/core/components/Select/FolderPicker.test.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import * as Backend from 'app/core/services/backend_srv';
|
||||
import { FolderPicker } from './FolderPicker';
|
||||
|
||||
jest.spyOn(Backend, 'getBackendSrv').mockReturnValue({
|
||||
search: jest.fn(() => [
|
||||
{ title: 'Dash 1', id: 'A' },
|
||||
{ title: 'Dash 2', id: 'B' },
|
||||
]),
|
||||
} as any);
|
||||
|
||||
jest.mock('app/core/core', () => ({
|
||||
contextSrv: {
|
||||
isEditor: true,
|
||||
},
|
||||
}));
|
||||
|
||||
describe('FolderPicker', () => {
|
||||
it('should render', () => {
|
||||
const wrapper = shallow(<FolderPicker onChange={jest.fn()} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
178
public/app/core/components/Select/FolderPicker.tsx
Normal file
178
public/app/core/components/Select/FolderPicker.tsx
Normal file
@ -0,0 +1,178 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Forms } from '@grafana/ui';
|
||||
import { AppEvents, SelectableValue } from '@grafana/data';
|
||||
import { debounce } from 'lodash';
|
||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
import appEvents from '../../app_events';
|
||||
|
||||
export interface Props {
|
||||
onChange: ($folder: { title: string; id: number }) => void;
|
||||
enableCreateNew: boolean;
|
||||
rootName?: string;
|
||||
enableReset?: boolean;
|
||||
dashboardId?: any;
|
||||
initialTitle?: string;
|
||||
initialFolderId?: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
folder: SelectableValue<number>;
|
||||
validationError: string;
|
||||
hasValidationError: boolean;
|
||||
}
|
||||
|
||||
export class FolderPicker extends PureComponent<Props, State> {
|
||||
debouncedSearch: any;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
folder: {},
|
||||
validationError: '',
|
||||
hasValidationError: false,
|
||||
};
|
||||
|
||||
this.debouncedSearch = debounce(this.getOptions, 300, {
|
||||
leading: true,
|
||||
trailing: true,
|
||||
});
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
rootName: 'General',
|
||||
enableReset: false,
|
||||
initialTitle: '',
|
||||
enableCreateNew: false,
|
||||
};
|
||||
|
||||
componentDidMount = async () => {
|
||||
await this.loadInitialValue();
|
||||
};
|
||||
|
||||
getOptions = async (query: string) => {
|
||||
const { rootName, enableReset, initialTitle } = this.props;
|
||||
const params = {
|
||||
query,
|
||||
type: 'dash-folder',
|
||||
permission: 'Edit',
|
||||
};
|
||||
|
||||
const searchHits = await getBackendSrv().search(params);
|
||||
const options: Array<SelectableValue<number>> = searchHits.map(hit => ({ label: hit.title, value: hit.id }));
|
||||
if (contextSrv.isEditor && rootName?.toLowerCase().startsWith(query.toLowerCase())) {
|
||||
options.unshift({ label: rootName, value: 0 });
|
||||
}
|
||||
|
||||
if (enableReset && query === '' && initialTitle !== '') {
|
||||
options.unshift({ label: initialTitle, value: undefined });
|
||||
}
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
onFolderChange = async (newFolder: SelectableValue<number>) => {
|
||||
if (!newFolder) {
|
||||
newFolder = { value: 0, label: this.props.rootName };
|
||||
}
|
||||
|
||||
this.setState(
|
||||
{
|
||||
folder: newFolder,
|
||||
},
|
||||
() => this.props.onChange({ id: newFolder.value!, title: newFolder.label! })
|
||||
);
|
||||
};
|
||||
|
||||
createNewFolder = async (folderName: string) => {
|
||||
const newFolder = await getBackendSrv().createFolder({ title: folderName });
|
||||
let folder = { value: -1, label: 'Not created' };
|
||||
if (newFolder.id > -1) {
|
||||
appEvents.emit(AppEvents.alertSuccess, ['Folder Created', 'OK']);
|
||||
folder = { value: newFolder.id, label: newFolder.title };
|
||||
await this.onFolderChange(folder);
|
||||
} else {
|
||||
appEvents.emit(AppEvents.alertError, ['Folder could not be created']);
|
||||
}
|
||||
|
||||
return folder;
|
||||
};
|
||||
|
||||
private loadInitialValue = async () => {
|
||||
const { initialTitle, rootName, initialFolderId, enableReset, dashboardId } = this.props;
|
||||
const resetFolder: SelectableValue<number> = { label: initialTitle, value: undefined };
|
||||
const rootFolder: SelectableValue<number> = { label: rootName, value: 0 };
|
||||
|
||||
const options = await this.getOptions('');
|
||||
|
||||
let folder: SelectableValue<number> = { value: -1 };
|
||||
if (initialFolderId || (initialFolderId && initialFolderId > -1)) {
|
||||
folder = options.find(option => option.value === initialFolderId) || { value: -1 };
|
||||
} else if (enableReset && initialTitle && initialFolderId === undefined) {
|
||||
folder = resetFolder;
|
||||
}
|
||||
|
||||
if (!folder) {
|
||||
if (contextSrv.isEditor) {
|
||||
folder = rootFolder;
|
||||
} else {
|
||||
// We shouldn't assign a random folder without the user actively choosing it on a persisted dashboard
|
||||
const isPersistedDashBoard = !!dashboardId;
|
||||
if (isPersistedDashBoard) {
|
||||
folder = resetFolder;
|
||||
} else {
|
||||
folder = options.length > 0 ? options[0] : resetFolder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setState(
|
||||
{
|
||||
folder,
|
||||
},
|
||||
() => {
|
||||
// if this is not the same as our initial value notify parent
|
||||
if (folder.value !== initialFolderId) {
|
||||
this.props.onChange({ id: folder.value!, title: folder.text });
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { folder, validationError, hasValidationError } = this.state;
|
||||
const { enableCreateNew } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<label className="gf-form-label width-7">Folder</label>
|
||||
<Forms.AsyncSelect
|
||||
loadingMessage="Loading folders..."
|
||||
defaultOptions
|
||||
defaultValue={folder}
|
||||
value={folder}
|
||||
allowCustomValue={enableCreateNew}
|
||||
loadOptions={this.debouncedSearch}
|
||||
onChange={this.onFolderChange}
|
||||
onCreateOption={this.createNewFolder}
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{hasValidationError && (
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form gf-form--grow">
|
||||
<label className="gf-form-label text-warning gf-form-label--grow">
|
||||
<i className="fa fa-warning" />
|
||||
{validationError}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`FolderPicker should render 1`] = `
|
||||
<Fragment>
|
||||
<div
|
||||
className="gf-form-inline"
|
||||
>
|
||||
<div
|
||||
className="gf-form"
|
||||
>
|
||||
<label
|
||||
className="gf-form-label width-7"
|
||||
>
|
||||
Folder
|
||||
</label>
|
||||
<AsyncSelect
|
||||
allowCustomValue={false}
|
||||
defaultOptions={true}
|
||||
defaultValue={Object {}}
|
||||
loadOptions={[Function]}
|
||||
loadingMessage="Loading folders..."
|
||||
onChange={[Function]}
|
||||
onCreateOption={[Function]}
|
||||
size="sm"
|
||||
value={Object {}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
`;
|
@ -244,11 +244,11 @@ export class SettingsCtrl {
|
||||
);
|
||||
}
|
||||
|
||||
onFolderChange(folder: { id: number; title: string }) {
|
||||
onFolderChange = (folder: { id: number; title: string }) => {
|
||||
this.dashboard.meta.folderId = folder.id;
|
||||
this.dashboard.meta.folderTitle = folder.title;
|
||||
this.hasUnsavedFolderChange = true;
|
||||
}
|
||||
};
|
||||
|
||||
getFolder() {
|
||||
return {
|
||||
|
@ -1,108 +1,103 @@
|
||||
<aside class="dashboard-settings__aside">
|
||||
<a href="{{::section.url}}" class="dashboard-settings__nav-item" ng-class="{active: ctrl.viewId === section.id}" ng-repeat="section in ctrl.sections" aria-label={{ctrl.selectors.sectionItems(section.title)}}>
|
||||
<a href="{{::section.url}}" class="dashboard-settings__nav-item" ng-class="{active: ctrl.viewId === section.id}"
|
||||
ng-repeat="section in ctrl.sections" aria-label={{ctrl.selectors.sectionItems}}>
|
||||
<i class="{{::section.icon}}"></i>
|
||||
{{::section.title}}
|
||||
</a>
|
||||
{{::section.title}}
|
||||
</a>
|
||||
|
||||
<div class="dashboard-settings__aside-actions">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
ng-click="ctrl.saveDashboard()"
|
||||
ng-show="ctrl.canSave"
|
||||
aria-label={{ctrl.selectors.saveDashBoard}}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-inverse"
|
||||
ng-click="ctrl.openSaveAsModal()"
|
||||
ng-show="ctrl.canSaveAs"
|
||||
aria-label={{ctrl.selectors.saveAsDashBoard}}
|
||||
>
|
||||
Save As...
|
||||
</button>
|
||||
</div>
|
||||
<div class="dashboard-settings__aside-actions">
|
||||
<button class="btn btn-primary" ng-click="ctrl.saveDashboard()" ng-show="ctrl.canSave"
|
||||
aria-label={{ctrl.selectors.saveDashBoard}}>
|
||||
Save
|
||||
</button>
|
||||
<button class="btn btn-inverse" ng-click="ctrl.openSaveAsModal()" ng-show="ctrl.canSaveAs"
|
||||
aria-label={{ctrl.selectors.saveAsDashBoard}}>
|
||||
Save As...
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'settings'">
|
||||
<h3 class="dashboard-settings__header">
|
||||
General
|
||||
</h3>
|
||||
<h3 class="dashboard-settings__header">
|
||||
General
|
||||
</h3>
|
||||
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-7">Name</label>
|
||||
<input type="text" class="gf-form-input width-30" ng-model='ctrl.dashboard.title'></input>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-7">Description</label>
|
||||
<input type="text" class="gf-form-input width-30" ng-model='ctrl.dashboard.description'></input>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-7">
|
||||
Tags
|
||||
<info-popover mode="right-normal">Press enter to add a tag</info-popover>
|
||||
</label>
|
||||
<bootstrap-tagsinput ng-model="ctrl.dashboard.tags" tagclass="label label-tag" placeholder="add tags">
|
||||
</bootstrap-tagsinput>
|
||||
</div>
|
||||
<folder-picker initial-title="ctrl.dashboard.meta.folderTitle"
|
||||
initial-folder-id="ctrl.dashboard.meta.folderId"
|
||||
on-change="ctrl.onFolderChange($folder)"
|
||||
enable-create-new="true"
|
||||
is-valid-selection="true"
|
||||
label-class="width-7"
|
||||
dashboard-id="ctrl.dashboard.id">
|
||||
</folder-picker>
|
||||
<gf-form-switch class="gf-form" label="Editable" tooltip="Uncheck, then save and reload to disable all dashboard editing" checked="ctrl.dashboard.editable" label-class="width-7">
|
||||
</gf-form-switch>
|
||||
</div>
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-7">Name</label>
|
||||
<input type="text" class="gf-form-input width-30" ng-model='ctrl.dashboard.title'></input>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-7">Description</label>
|
||||
<input type="text" class="gf-form-input width-30" ng-model='ctrl.dashboard.description'></input>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-7">
|
||||
Tags
|
||||
<info-popover mode="right-normal">Press enter to add a tag</info-popover>
|
||||
</label>
|
||||
<bootstrap-tagsinput ng-model="ctrl.dashboard.tags" tagclass="label label-tag" placeholder="add tags">
|
||||
</bootstrap-tagsinput>
|
||||
</div>
|
||||
<folder-picker initial-title="ctrl.dashboard.meta.folderTitle" initial-folder-id="ctrl.dashboard.meta.folderId"
|
||||
on-change="ctrl.onFolderChange" enable-create-new="true" is-valid-selection="true" label-class="width-7"
|
||||
dashboard-id="ctrl.dashboard.id">
|
||||
</folder-picker>
|
||||
<gf-form-switch class="gf-form" label="Editable"
|
||||
tooltip="Uncheck, then save and reload to disable all dashboard editing" checked="ctrl.dashboard.editable"
|
||||
label-class="width-7">
|
||||
</gf-form-switch>
|
||||
</div>
|
||||
|
||||
<gf-time-picker-settings dashboard="ctrl.dashboard"></gf-time-picker-settings>
|
||||
<gf-time-picker-settings dashboard="ctrl.dashboard"></gf-time-picker-settings>
|
||||
|
||||
<h5 class="section-heading">Panel Options</h5>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-11">
|
||||
Graph Tooltip
|
||||
<info-popover mode="right-normal">
|
||||
Cycle between options using Shortcut: CTRL+O or CMD+O
|
||||
</info-popover>
|
||||
</label>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select ng-model="ctrl.dashboard.graphTooltip" class='gf-form-input' ng-options="f.value as f.text for f in [{value: 0, text: 'Default'}, {value: 1, text: 'Shared crosshair'},{value: 2, text: 'Shared Tooltip'}]"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form-button-row">
|
||||
<button class="btn btn-danger" ng-click="ctrl.deleteDashboard()" ng-show="ctrl.canDelete" aria-label="Dashboard settings page delete dashboard button">
|
||||
Delete Dashboard
|
||||
</button>
|
||||
<h5 class="section-heading">Panel Options</h5>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-11">
|
||||
Graph Tooltip
|
||||
<info-popover mode="right-normal">
|
||||
Cycle between options using Shortcut: CTRL+O or CMD+O
|
||||
</info-popover>
|
||||
</label>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select ng-model="ctrl.dashboard.graphTooltip" class='gf-form-input'
|
||||
ng-options="f.value as f.text for f in [{value: 0, text: 'Default'}, {value: 1, text: 'Shared crosshair'},{value: 2, text: 'Shared Tooltip'}]"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form-button-row">
|
||||
<button class="btn btn-danger" ng-click="ctrl.deleteDashboard()" ng-show="ctrl.canDelete"
|
||||
aria-label="Dashboard settings page delete dashboard button">
|
||||
Delete Dashboard
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'annotations'" ng-include="'public/app/features/annotations/partials/editor.html'">
|
||||
<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'annotations'"
|
||||
ng-include="'public/app/features/annotations/partials/editor.html'">
|
||||
</div>
|
||||
|
||||
<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'templating'" ng-include="'public/app/features/templating/partials/editor.html'">
|
||||
<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'templating'"
|
||||
ng-include="'public/app/features/templating/partials/editor.html'">
|
||||
</div>
|
||||
|
||||
<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'links'" >
|
||||
<dash-links-editor dashboard="ctrl.dashboard"></dash-links-editor>
|
||||
<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'links'">
|
||||
<dash-links-editor dashboard="ctrl.dashboard"></dash-links-editor>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'versions'" >
|
||||
<gf-dashboard-history dashboard="dashboard"></gf-dashboard-history>
|
||||
<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'versions'">
|
||||
<gf-dashboard-history dashboard="dashboard"></gf-dashboard-history>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'dashboard_json'" >
|
||||
<h3 class="dashboard-settings__header">JSON Model</h3>
|
||||
<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'dashboard_json'">
|
||||
<h3 class="dashboard-settings__header">JSON Model</h3>
|
||||
<div class="dashboard-settings__subheader">
|
||||
The JSON Model below is data structure that defines the dashboard. Including settings, panel settings & layout,
|
||||
queries etc.
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<code-editor content="ctrl.json" data-mode="json" data-max-lines=30 ></code-editor>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<code-editor content="ctrl.json" data-mode="json" data-max-lines=30></code-editor>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-button-row">
|
||||
<button class="btn btn-primary" ng-click="ctrl.saveDashboardJson()" ng-show="ctrl.canSave">
|
||||
@ -111,12 +106,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'permissions'" >
|
||||
<dashboard-permissions ng-if="ctrl.dashboard && !ctrl.hasUnsavedFolderChange"
|
||||
dashboardId="ctrl.dashboard.id"
|
||||
backendSrv="ctrl.backendSrv"
|
||||
folder="ctrl.getFolder()"
|
||||
/>
|
||||
<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'permissions'">
|
||||
<dashboard-permissions ng-if="ctrl.dashboard && !ctrl.hasUnsavedFolderChange" dashboardId="ctrl.dashboard.id"
|
||||
backendSrv="ctrl.backendSrv" folder="ctrl.getFolder()" />
|
||||
<div ng-if="ctrl.hasUnsavedFolderChange">
|
||||
<h5>You have changed folder, please save to view permissions.</h5>
|
||||
</div>
|
||||
@ -137,4 +129,3 @@
|
||||
Make Editable
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
export { FolderPickerCtrl } from './FolderPickerCtrl';
|
@ -1,38 +0,0 @@
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label {{ctrl.labelClass}}">Folder</label>
|
||||
<div class="dropdown" ng-hide="ctrl.createNewFolder">
|
||||
<gf-form-dropdown model="ctrl.folder"
|
||||
get-options="ctrl.getOptions($query)"
|
||||
on-change="ctrl.onFolderChange($option)">
|
||||
</gf-form-dropdown>
|
||||
</div>
|
||||
<input type="text"
|
||||
class="gf-form-input max-width-10"
|
||||
ng-if="ctrl.createNewFolder"
|
||||
give-focus="ctrl.createNewFolder"
|
||||
ng-model="ctrl.newFolderName"
|
||||
ng-model-options="{ debounce: 400 }"
|
||||
ng-change="ctrl.newFolderNameChanged()" />
|
||||
</div>
|
||||
<div class="gf-form" ng-if="ctrl.createNewFolder">
|
||||
<button class="btn btn-inverse"
|
||||
ng-click="ctrl.createFolder($event)"
|
||||
ng-disabled="!ctrl.newFolderNameTouched || ctrl.hasValidationError">
|
||||
Create
|
||||
</button>
|
||||
</div>
|
||||
<div class="gf-form" ng-if="ctrl.createNewFolder">
|
||||
<button class="btn btn-inverse" ng-click="ctrl.cancelCreateFolder($event)">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form-inline" ng-if="ctrl.newFolderNameTouched && ctrl.hasValidationError">
|
||||
<div class="gf-form gf-form--grow">
|
||||
<label class="gf-form-label text-warning gf-form-label--grow">
|
||||
<i class="fa fa-warning"></i>
|
||||
{{ctrl.validationError}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
@ -22,9 +22,9 @@ const template = `
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.clone.title" give-focus="true" required aria-label="Save dashboard title field">
|
||||
</div>
|
||||
<folder-picker initial-folder-id="ctrl.folderId"
|
||||
on-change="ctrl.onFolderChange($folder)"
|
||||
enter-folder-creation="ctrl.onEnterFolderCreation()"
|
||||
exit-folder-creation="ctrl.onExitFolderCreation()"
|
||||
on-change="ctrl.onFolderChange"
|
||||
enter-folder-creation="ctrl.onEnterFolderCreation"
|
||||
exit-folder-creation="ctrl.onExitFolderCreation"
|
||||
enable-create-new="true"
|
||||
label-class="width-8"
|
||||
dashboard-id="ctrl.clone.id">
|
||||
@ -97,17 +97,17 @@ export class SaveDashboardAsModalCtrl {
|
||||
}
|
||||
}
|
||||
|
||||
onFolderChange(folder: { id: any }) {
|
||||
onFolderChange = (folder: { id: any }) => {
|
||||
this.folderId = folder.id;
|
||||
}
|
||||
};
|
||||
|
||||
onEnterFolderCreation() {
|
||||
onEnterFolderCreation = () => {
|
||||
this.isValidFolderSelection = false;
|
||||
}
|
||||
};
|
||||
|
||||
onExitFolderCreation() {
|
||||
onExitFolderCreation = () => {
|
||||
this.isValidFolderSelection = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function saveDashboardAsDirective() {
|
||||
|
@ -8,7 +8,6 @@ import './components/DashLinks';
|
||||
import './components/DashExportModal';
|
||||
import './components/DashNav';
|
||||
import './components/ExportDataModal';
|
||||
import './components/FolderPicker';
|
||||
import './components/VersionHistory';
|
||||
import './components/DashboardSettings';
|
||||
import './components/SubMenu';
|
||||
|
@ -163,18 +163,18 @@ export class DashboardImportCtrl {
|
||||
);
|
||||
}
|
||||
|
||||
onFolderChange(folder: any) {
|
||||
onFolderChange = (folder: any) => {
|
||||
this.folderId = folder.id;
|
||||
this.titleChanged();
|
||||
}
|
||||
};
|
||||
|
||||
onEnterFolderCreation() {
|
||||
onEnterFolderCreation = () => {
|
||||
this.inputsValid = false;
|
||||
}
|
||||
};
|
||||
|
||||
onExitFolderCreation() {
|
||||
onExitFolderCreation = () => {
|
||||
this.inputValueChanged();
|
||||
}
|
||||
};
|
||||
|
||||
isValid() {
|
||||
return this.inputsValid && this.folderId !== null;
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { IScope } from 'angular';
|
||||
import { AppEvents } from '@grafana/data';
|
||||
import coreModule from 'app/core/core_module';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { backendSrv } from 'app/core/services/backend_srv';
|
||||
import { AppEvents } from '@grafana/data';
|
||||
import { IScope } from 'angular';
|
||||
import { promiseToDigest } from 'app/core/utils/promiseToDigest';
|
||||
|
||||
export class MoveToFolderCtrl {
|
||||
@ -14,11 +14,11 @@ export class MoveToFolderCtrl {
|
||||
|
||||
constructor(private $scope: IScope) {}
|
||||
|
||||
onFolderChange(folder: any) {
|
||||
onFolderChange = (folder: any) => {
|
||||
this.folder = folder;
|
||||
}
|
||||
};
|
||||
|
||||
save() {
|
||||
save = () => {
|
||||
return promiseToDigest(this.$scope)(
|
||||
backendSrv.moveDashboards(this.dashboards, this.folder).then((result: any) => {
|
||||
if (result.successCount > 0) {
|
||||
@ -37,15 +37,15 @@ export class MoveToFolderCtrl {
|
||||
return this.afterSave();
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
onEnterFolderCreation() {
|
||||
onEnterFolderCreation = () => {
|
||||
this.isValidFolderSelection = false;
|
||||
}
|
||||
};
|
||||
|
||||
onExitFolderCreation() {
|
||||
onExitFolderCreation = () => {
|
||||
this.isValidFolderSelection = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function moveToFolderModal() {
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="modal-body">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-header-title">
|
||||
<i class="gicon gicon-folder-new"></i>
|
||||
<i class="gicon gicon-folder-new"></i>
|
||||
<span class="p-l-1">Choose Dashboard Folder</span>
|
||||
</h2>
|
||||
|
||||
@ -15,18 +15,23 @@
|
||||
|
||||
<div class="p-t-2">
|
||||
<div class="gf-form">
|
||||
<folder-picker
|
||||
on-load="ctrl.onFolderChange($folder)"
|
||||
on-change="ctrl.onFolderChange($folder)"
|
||||
enter-folder-creation="ctrl.onEnterFolderCreation()"
|
||||
exit-folder-creation="ctrl.onExitFolderCreation()"
|
||||
enable-create-new="true"
|
||||
label-class="width-7">
|
||||
</folder-picker>
|
||||
<folder-picker
|
||||
on-change="ctrl.onFolderChange"
|
||||
label-class="width-7"
|
||||
enable-create-new="true"
|
||||
folder="ctrl.folder"
|
||||
>
|
||||
</folder-picker>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form-button-row text-center">
|
||||
<button type="submit" class="btn btn-primary" ng-disabled="ctrl.saveForm.$invalid || !ctrl.isValidFolderSelection">Move</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary"
|
||||
ng-disabled="ctrl.saveForm.$invalid || !ctrl.isValidFolderSelection"
|
||||
>
|
||||
Move
|
||||
</button>
|
||||
<a class="btn-text" ng-click="ctrl.dismiss();">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -12,7 +12,8 @@
|
||||
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form gf-form--grow">
|
||||
<input type="text" class="gf-form-input max-width-30" ng-model="ctrl.gnetUrl" placeholder="Paste Grafana.com dashboard url or id" ng-blur="ctrl.checkGnetDashboard()"></textarea>
|
||||
<input type="text" class="gf-form-input max-width-30" ng-model="ctrl.gnetUrl"
|
||||
placeholder="Paste Grafana.com dashboard url or id" ng-blur="ctrl.checkGnetDashboard()"></textarea>
|
||||
</div>
|
||||
<div class="gf-form" ng-if="ctrl.gnetError">
|
||||
<label class="gf-form-label text-warning">
|
||||
@ -43,7 +44,8 @@
|
||||
<div class="gf-form-group" ng-if="ctrl.dash.gnetId">
|
||||
<h3 class="section-heading">
|
||||
Importing Dashboard from
|
||||
<a href="https://grafana.com/dashboards/{{ctrl.dash.gnetId}}" class="external-link" target="_blank">Grafana.com</a>
|
||||
<a href="https://grafana.com/dashboards/{{ctrl.dash.gnetId}}" class="external-link"
|
||||
target="_blank">Grafana.com</a>
|
||||
</h3>
|
||||
|
||||
<div class="gf-form">
|
||||
@ -64,7 +66,8 @@
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form gf-form--grow">
|
||||
<label class="gf-form-label width-15">Name</label>
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.dash.title" give-focus="true" ng-change="ctrl.titleChanged()" ng-class="{'validation-error': ctrl.nameExists || !ctrl.dash.title}">
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.dash.title" give-focus="true"
|
||||
ng-change="ctrl.titleChanged()" ng-class="{'validation-error': ctrl.nameExists || !ctrl.dash.title}">
|
||||
<label class="gf-form-label text-success" ng-if="ctrl.titleTouched && !ctrl.hasNameValidationError">
|
||||
<i class="fa fa-check"></i>
|
||||
</label>
|
||||
@ -82,14 +85,10 @@
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form gf-form--grow">
|
||||
<folder-picker label-class="width-15"
|
||||
initial-folder-id="ctrl.folderId"
|
||||
initial-title="ctrl.initialFolderTitle"
|
||||
on-change="ctrl.onFolderChange($folder)"
|
||||
on-load="ctrl.onFolderChange($folder)"
|
||||
enter-folder-creation="ctrl.onEnterFolderCreation()"
|
||||
exit-folder-creation="ctrl.onExitFolderCreation()"
|
||||
enable-create-new="true">
|
||||
<folder-picker label-class="width-15" initial-folder-id="ctrl.folderId"
|
||||
initial-title="ctrl.initialFolderTitle" on-change="ctrl.onFolderChange"
|
||||
enter-folder-creation="ctrl.onEnterFolderCreation" exit-folder-creation="ctrl.onExitFolderCreation"
|
||||
enable-create-new="true">
|
||||
</folder-picker>
|
||||
</div>
|
||||
</div>
|
||||
@ -99,14 +98,20 @@
|
||||
<span class="gf-form-label width-15">
|
||||
Unique identifier (uid)
|
||||
<info-popover mode="right-normal">
|
||||
The unique identifier (uid) of a dashboard can be used for uniquely identify a dashboard between multiple Grafana installs.
|
||||
The uid allows having consistent URL’s for accessing dashboards so changing the title of a dashboard will not break any
|
||||
bookmarked links to that dashboard.
|
||||
The unique identifier (uid) of a dashboard can be used for uniquely identify a dashboard between multiple
|
||||
Grafana installs.
|
||||
The uid allows having consistent URL’s for accessing dashboards so changing the title of a dashboard will
|
||||
not break any
|
||||
bookmarked links to that dashboard.
|
||||
</info-popover>
|
||||
</span>
|
||||
<input type="text" class="gf-form-input" disabled="disabled" ng-model="ctrl.autoGenerateUidValue" ng-if="ctrl.autoGenerateUid">
|
||||
<a class="btn btn-secondary gf-form-btn" href="#" ng-click="ctrl.autoGenerateUid = false" ng-if="ctrl.autoGenerateUid">change</a>
|
||||
<input type="text" class="gf-form-input" maxlength="40" placeholder="optional, will be auto-generated if empty" ng-model="ctrl.dash.uid" ng-change="ctrl.uidChanged()" ng-if="!ctrl.autoGenerateUid">
|
||||
<input type="text" class="gf-form-input" disabled="disabled" ng-model="ctrl.autoGenerateUidValue"
|
||||
ng-if="ctrl.autoGenerateUid">
|
||||
<a class="btn btn-secondary gf-form-btn" href="#" ng-click="ctrl.autoGenerateUid = false"
|
||||
ng-if="ctrl.autoGenerateUid">change</a>
|
||||
<input type="text" class="gf-form-input" maxlength="40"
|
||||
placeholder="optional, will be auto-generated if empty" ng-model="ctrl.dash.uid"
|
||||
ng-change="ctrl.uidChanged()" ng-if="!ctrl.autoGenerateUid">
|
||||
<label class="gf-form-label text-success" ng-if="!ctrl.autoGenerateUid && !ctrl.hasUidValidationError">
|
||||
<i class="fa fa-check"></i>
|
||||
</label>
|
||||
@ -132,12 +137,14 @@
|
||||
</label>
|
||||
<!-- Data source input -->
|
||||
<div class="gf-form-select-wrapper" style="width: 100%" ng-if="input.type === 'datasource'">
|
||||
<select class="gf-form-input" ng-model="input.value" ng-options="v.value as v.text for v in input.options" ng-change="ctrl.inputValueChanged()">
|
||||
<select class="gf-form-input" ng-model="input.value" ng-options="v.value as v.text for v in input.options"
|
||||
ng-change="ctrl.inputValueChanged()">
|
||||
<option value="" ng-hide="input.value">{{input.info}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Constant input -->
|
||||
<input ng-if="input.type === 'constant'" type="text" class="gf-form-input" ng-model="input.value" placeholder="{{input.default}}" ng-change="ctrl.inputValueChanged()">
|
||||
<input ng-if="input.type === 'constant'" type="text" class="gf-form-input" ng-model="input.value"
|
||||
placeholder="{{input.default}}" ng-change="ctrl.inputValueChanged()">
|
||||
<label class="gf-form-label text-success" ng-show="input.value">
|
||||
<i class="fa fa-check"></i>
|
||||
</label>
|
||||
@ -146,10 +153,12 @@
|
||||
</div>
|
||||
|
||||
<div class="gf-form-button-row">
|
||||
<button type="button" class="btn btn-primary width-12" ng-click="ctrl.saveDashboard()" ng-hide="ctrl.nameExists || ctrl.uidExists" ng-disabled="!ctrl.isValid()">
|
||||
<button type="button" class="btn btn-primary width-12" ng-click="ctrl.saveDashboard()"
|
||||
ng-hide="ctrl.nameExists || ctrl.uidExists" ng-disabled="!ctrl.isValid()">
|
||||
Import
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger width-12" ng-click="ctrl.saveDashboard()" ng-show="ctrl.nameExists || ctrl.uidExists" ng-disabled="!ctrl.isValid()">
|
||||
<button type="button" class="btn btn-danger width-12" ng-click="ctrl.saveDashboard()"
|
||||
ng-show="ctrl.nameExists || ctrl.uidExists" ng-disabled="!ctrl.isValid()">
|
||||
Import (Overwrite)
|
||||
</button>
|
||||
<a class="btn btn-link" ng-click="ctrl.back()">Cancel</a>
|
||||
|
@ -17,51 +17,54 @@ export class ValidationSrv {
|
||||
return this.validate(0, name, 'A folder or dashboard in the general folder with the same name already exists');
|
||||
}
|
||||
|
||||
private validate(folderId: any, name: string, existingErrorMessage: string) {
|
||||
private async validate(folderId: any, name: string, existingErrorMessage: string) {
|
||||
name = (name || '').trim();
|
||||
const nameLowerCased = name.toLowerCase();
|
||||
|
||||
if (name.length === 0) {
|
||||
return Promise.reject({
|
||||
throw {
|
||||
type: 'REQUIRED',
|
||||
message: 'Name is required',
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
if (folderId === 0 && nameLowerCased === this.rootName) {
|
||||
return Promise.reject({
|
||||
throw {
|
||||
type: 'EXISTING',
|
||||
message: 'This is a reserved name and cannot be used for a folder.',
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const promises = [];
|
||||
promises.push(backendSrv.search({ type: hitTypes.FOLDER, folderIds: [folderId], query: name }));
|
||||
promises.push(backendSrv.search({ type: hitTypes.DASHBOARD, folderIds: [folderId], query: name }));
|
||||
|
||||
return Promise.all(promises).then(res => {
|
||||
let hits: any[] = [];
|
||||
const res = await Promise.all(promises);
|
||||
let hits: any[] = [];
|
||||
|
||||
if (res.length > 0 && res[0].length > 0) {
|
||||
hits = res[0];
|
||||
if (res.length > 0 && res[0].length > 0) {
|
||||
hits = res[0];
|
||||
}
|
||||
|
||||
if (res.length > 1 && res[1].length > 0) {
|
||||
hits = hits.concat(res[1]);
|
||||
}
|
||||
|
||||
for (const hit of hits) {
|
||||
if (nameLowerCased === hit.title.toLowerCase()) {
|
||||
throw {
|
||||
type: 'EXISTING',
|
||||
message: existingErrorMessage,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (res.length > 1 && res[1].length > 0) {
|
||||
hits = hits.concat(res[1]);
|
||||
}
|
||||
|
||||
for (const hit of hits) {
|
||||
if (nameLowerCased === hit.title.toLowerCase()) {
|
||||
throw {
|
||||
type: 'EXISTING',
|
||||
message: existingErrorMessage,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const validationSrv = new ValidationSrv();
|
||||
|
||||
export default validationSrv;
|
||||
|
||||
coreModule.service('validationSrv', ValidationSrv);
|
||||
|
@ -4,7 +4,8 @@
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-8">Show</span>
|
||||
<div class="gf-form-select-wrapper max-width-15">
|
||||
<select class="gf-form-input" ng-model="ctrl.panel.show" ng-options="f.value as f.text for f in ctrl.showOptions" ng-change="ctrl.onRefresh()"></select>
|
||||
<select class="gf-form-input" ng-model="ctrl.panel.show"
|
||||
ng-options="f.value as f.text for f in ctrl.showOptions" ng-change="ctrl.onRefresh()"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
@ -14,42 +15,50 @@
|
||||
<div class="gf-form" ng-show="ctrl.panel.show === 'current'">
|
||||
<span class="gf-form-label width-8">Sort order</span>
|
||||
<div class="gf-form-select-wrapper max-width-15">
|
||||
<select class="gf-form-input" ng-model="ctrl.panel.sortOrder" ng-options="f.value as f.text for f in ctrl.sortOrderOptions" ng-change="ctrl.onRefresh()"></select>
|
||||
<select class="gf-form-input" ng-model="ctrl.panel.sortOrder"
|
||||
ng-options="f.value as f.text for f in ctrl.sortOrderOptions" ng-change="ctrl.onRefresh()"></select>
|
||||
</div>
|
||||
</div>
|
||||
<gf-form-switch class="gf-form" label="Alerts from this dashboard" label-class="width-18" checked="ctrl.panel.onlyAlertsOnDashboard" on-change="ctrl.updateStateFilter()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Alerts from this dashboard" label-class="width-18"
|
||||
checked="ctrl.panel.onlyAlertsOnDashboard" on-change="ctrl.updateStateFilter()"></gf-form-switch>
|
||||
</div>
|
||||
<div class="section gf-form-group" ng-show="ctrl.panel.show === 'current'">
|
||||
<h5 class="section-heading">Filter</h5>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-8">Alert name</span>
|
||||
<input type="text" class="gf-form-input max-width-15" ng-model="ctrl.panel.nameFilter" placeholder="Alert name query" ng-change="ctrl.onRefresh()" />
|
||||
<input type="text" class="gf-form-input max-width-15" ng-model="ctrl.panel.nameFilter"
|
||||
placeholder="Alert name query" ng-change="ctrl.onRefresh()" />
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-8">Dashboard title</span>
|
||||
<input type="text" class="gf-form-input" placeholder="Dashboard title query" ng-model="ctrl.panel.dashboardFilter" ng-change="ctrl.onRefresh()" ng-model-onblur>
|
||||
<input type="text" class="gf-form-input" placeholder="Dashboard title query" ng-model="ctrl.panel.dashboardFilter"
|
||||
ng-change="ctrl.onRefresh()" ng-model-onblur>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<folder-picker initial-folder-id="ctrl.panel.folderId"
|
||||
on-change="ctrl.onFolderChange($folder)"
|
||||
label-class="width-8"
|
||||
initial-title="'All'"
|
||||
enable-reset="true" >
|
||||
<folder-picker initial-folder-id="ctrl.panel.folderId" on-change="ctrl.onFolderChange" label-class="width-8"
|
||||
initial-title="'All'" enable-reset="true">
|
||||
</folder-picker>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-8">Dashboard tags</span>
|
||||
<bootstrap-tagsinput ng-model="ctrl.panel.dashboardTags" tagclass="label label-tag" placeholder="add tags" on-tags-updated="ctrl.refresh()">
|
||||
</bootstrap-tagsinput>
|
||||
<span class="gf-form-label width-8">Dashboard tags</span>
|
||||
<bootstrap-tagsinput ng-model="ctrl.panel.dashboardTags" tagclass="label label-tag" placeholder="add tags"
|
||||
on-tags-updated="ctrl.refresh()">
|
||||
</bootstrap-tagsinput>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section gf-form-group" ng-show="ctrl.panel.show === 'current'">
|
||||
<h5 class="section-heading">State filter</h5>
|
||||
<gf-form-switch class="gf-form" label="Ok" label-class="width-10" checked="ctrl.stateFilter['ok']" on-change="ctrl.updateStateFilter()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Paused" label-class="width-10" checked="ctrl.stateFilter['paused']" on-change="ctrl.updateStateFilter()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="No data" label-class="width-10" checked="ctrl.stateFilter['no_data']" on-change="ctrl.updateStateFilter()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Execution error" label-class="width-10" checked="ctrl.stateFilter['execution_error']" on-change="ctrl.updateStateFilter()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Alerting" label-class="width-10" checked="ctrl.stateFilter['alerting']" on-change="ctrl.updateStateFilter()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Pending" label-class="width-10" checked="ctrl.stateFilter['pending']" on-change="ctrl.updateStateFilter()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Ok" label-class="width-10" checked="ctrl.stateFilter['ok']"
|
||||
on-change="ctrl.updateStateFilter()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Paused" label-class="width-10" checked="ctrl.stateFilter['paused']"
|
||||
on-change="ctrl.updateStateFilter()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="No data" label-class="width-10" checked="ctrl.stateFilter['no_data']"
|
||||
on-change="ctrl.updateStateFilter()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Execution error" label-class="width-10"
|
||||
checked="ctrl.stateFilter['execution_error']" on-change="ctrl.updateStateFilter()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Alerting" label-class="width-10" checked="ctrl.stateFilter['alerting']"
|
||||
on-change="ctrl.updateStateFilter()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Pending" label-class="width-10" checked="ctrl.stateFilter['pending']"
|
||||
on-change="ctrl.updateStateFilter()"></gf-form-switch>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -101,10 +101,10 @@ class AlertListPanel extends PanelCtrl {
|
||||
});
|
||||
}
|
||||
|
||||
onFolderChange(folder: any) {
|
||||
onFolderChange = (folder: any) => {
|
||||
this.panel.folderId = folder.id;
|
||||
this.refresh();
|
||||
}
|
||||
};
|
||||
|
||||
getStateChanges() {
|
||||
const params: any = {
|
||||
|
@ -2,15 +2,20 @@
|
||||
<div class="section gf-form-group">
|
||||
<h5 class="section-heading">Options</h5>
|
||||
|
||||
<gf-form-switch class="gf-form" label="Starred" label-class="width-9" checked="ctrl.panel.starred" on-change="ctrl.refresh()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Recently viewed" label-class="width-9" checked="ctrl.panel.recent" on-change="ctrl.refresh()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Search" label-class="width-9" checked="ctrl.panel.search" on-change="ctrl.refresh()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Starred" label-class="width-9" checked="ctrl.panel.starred"
|
||||
on-change="ctrl.refresh()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Recently viewed" label-class="width-9" checked="ctrl.panel.recent"
|
||||
on-change="ctrl.refresh()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Search" label-class="width-9" checked="ctrl.panel.search"
|
||||
on-change="ctrl.refresh()"></gf-form-switch>
|
||||
|
||||
<gf-form-switch class="gf-form" label="Show headings" label-class="width-9" checked="ctrl.panel.headings" on-change="ctrl.refresh()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Show headings" label-class="width-9" checked="ctrl.panel.headings"
|
||||
on-change="ctrl.refresh()"></gf-form-switch>
|
||||
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-9">Max items</span>
|
||||
<input class="gf-form-input max-width-5" type="number" ng-model="ctrl.panel.limit" ng-model-onblur ng-change="ctrl.refresh()">
|
||||
<input class="gf-form-input max-width-5" type="number" ng-model="ctrl.panel.limit" ng-model-onblur
|
||||
ng-change="ctrl.refresh()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -19,21 +24,20 @@
|
||||
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-6">Query</span>
|
||||
<input type="text" class="gf-form-input" placeholder="title query" ng-model="ctrl.panel.query" ng-change="ctrl.refresh()" ng-model-onblur>
|
||||
<input type="text" class="gf-form-input" placeholder="title query" ng-model="ctrl.panel.query"
|
||||
ng-change="ctrl.refresh()" ng-model-onblur>
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<folder-picker initial-folder-id="ctrl.panel.folderId"
|
||||
on-change="ctrl.onFolderChange($folder)"
|
||||
label-class="width-6"
|
||||
initial-title="'All'"
|
||||
enable-reset="true">
|
||||
</folder-picker>
|
||||
<folder-picker initial-folder-id="ctrl.panel.folderId" on-change="ctrl.onFolderChange" label-class="width-6"
|
||||
initial-title="'All'" enable-reset="true">
|
||||
</folder-picker>
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-6">Tags</span>
|
||||
<bootstrap-tagsinput ng-model="ctrl.panel.tags" tagclass="label label-tag" placeholder="add tags" on-tags-updated="ctrl.refresh()">
|
||||
<bootstrap-tagsinput ng-model="ctrl.panel.tags" tagclass="label label-tag" placeholder="add tags"
|
||||
on-tags-updated="ctrl.refresh()">
|
||||
</bootstrap-tagsinput>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -147,10 +147,10 @@ class DashListCtrl extends PanelCtrl {
|
||||
);
|
||||
}
|
||||
|
||||
onFolderChange(folder: any) {
|
||||
onFolderChange = (folder: any) => {
|
||||
this.panel.folderId = folder.id;
|
||||
this.refresh();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export { DashListCtrl, DashListCtrl as PanelCtrl };
|
||||
|
@ -13,8 +13,8 @@ export interface DashboardSearchHit {
|
||||
type: DashboardSearchHitType;
|
||||
tags: string[];
|
||||
isStarred: boolean;
|
||||
folderId: number;
|
||||
folderUid: string;
|
||||
folderTitle: string;
|
||||
folderUrl: string;
|
||||
folderId?: number;
|
||||
folderUid?: string;
|
||||
folderTitle?: string;
|
||||
folderUrl?: string;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user