Hugo Häggmark 7 年之前
父节点
当前提交
d97cd450c9
共有 46 个文件被更改,包括 215 次插入149 次删除
  1. 1 1
      docs/sources/reference/dashboard.md
  2. 4 2
      docs/sources/reference/templating.md
  3. 11 0
      packages/grafana-ui/src/components/LoadingPlaceholder/LoadingPlaceholder.tsx
  4. 4 1
      packages/grafana-ui/src/components/Select/IndicatorsContainer.tsx
  5. 4 0
      packages/grafana-ui/src/components/Select/NoOptionsMessage.tsx
  6. 13 8
      packages/grafana-ui/src/components/Select/Select.tsx
  7. 22 12
      packages/grafana-ui/src/components/Select/SelectOption.test.tsx
  8. 6 3
      packages/grafana-ui/src/components/Select/SelectOption.tsx
  9. 2 2
      packages/grafana-ui/src/components/Select/SelectOptionGroup.tsx
  10. 0 0
      packages/grafana-ui/src/components/Select/_Select.scss
  11. 7 2
      packages/grafana-ui/src/components/Select/__snapshots__/SelectOption.test.tsx.snap
  12. 27 0
      packages/grafana-ui/src/components/Select/resetSelectStyles.ts
  13. 1 0
      packages/grafana-ui/src/components/index.scss
  14. 8 0
      packages/grafana-ui/src/components/index.ts
  15. 1 1
      pkg/models/dashboards.go
  16. 1 1
      public/app/core/components/PermissionList/AddPermission.tsx
  17. 1 1
      public/app/core/components/PermissionList/DisabledPermissionListItem.tsx
  18. 1 1
      public/app/core/components/PermissionList/PermissionListItem.tsx
  19. 1 1
      public/app/core/components/Select/DataSourcePicker.tsx
  20. 0 25
      public/app/core/components/Select/ResetStyles.tsx
  21. 1 1
      public/app/core/components/Select/TeamPicker.tsx
  22. 1 1
      public/app/core/components/Select/UnitPicker.tsx
  23. 1 1
      public/app/core/components/Select/UserPicker.tsx
  24. 1 1
      public/app/core/components/SharedPreferences/SharedPreferences.tsx
  25. 2 4
      public/app/core/components/TagFilter/TagFilter.tsx
  26. 8 0
      public/app/core/specs/factors.test.ts
  27. 5 0
      public/app/core/utils/factors.ts
  28. 5 15
      public/app/features/alerting/AlertTab.tsx
  29. 3 4
      public/app/features/alerting/TestRuleResult.test.tsx
  30. 5 4
      public/app/features/alerting/TestRuleResult.tsx
  31. 0 13
      public/app/features/alerting/__snapshots__/TestRuleButton.test.tsx.snap
  32. 7 0
      public/app/features/alerting/__snapshots__/TestRuleResult.test.tsx.snap
  33. 20 1
      public/app/features/dashboard/dashboard_migration.ts
  34. 2 2
      public/app/features/dashboard/dashboard_model.ts
  35. 3 9
      public/app/features/dashboard/dashgrid/QueriesTab.tsx
  36. 1 2
      public/app/features/dashboard/dashgrid/QueryInspector.tsx
  37. 1 1
      public/app/features/dashboard/panel_model.ts
  38. 11 9
      public/app/features/dashboard/specs/dashboard_migration.test.ts
  39. 3 6
      public/app/features/dashboard/utils/panel.ts
  40. 4 1
      public/app/features/panel/panel_ctrl.ts
  41. 7 2
      public/app/features/panel/partials/general_tab.html
  42. 1 2
      public/app/plugins/panel/gauge/MappingRow.tsx
  43. 1 1
      public/app/plugins/panel/gauge/ValueOptions.tsx
  44. 1 1
      public/dashboards/home.json
  45. 4 5
      public/sass/_grafana.scss
  46. 2 2
      yarn.lock

+ 1 - 1
docs/sources/reference/dashboard.md

@@ -51,7 +51,7 @@ When a user creates a new dashboard, a new dashboard JSON object is initialized
     "list": []
     "list": []
   },
   },
   "refresh": "5s",
   "refresh": "5s",
-  "schemaVersion": 16,
+  "schemaVersion": 17,
   "version": 0,
   "version": 0,
   "links": []
   "links": []
 }
 }

+ 4 - 2
docs/sources/reference/templating.md

@@ -292,9 +292,11 @@ The `direction` controls how the panels will be arranged.
 
 
 By choosing `horizontal` the panels will be arranged side-by-side. Grafana will automatically adjust the width
 By choosing `horizontal` the panels will be arranged side-by-side. Grafana will automatically adjust the width
 of each repeated panel so that the whole row is filled. Currently, you cannot mix other panels on a row with a repeated
 of each repeated panel so that the whole row is filled. Currently, you cannot mix other panels on a row with a repeated
-panel. Each panel will never be smaller that the provided `Min width` if you have many selected values.
+panel.
 
 
-By choosing `vertical` the panels will be arranged from top to bottom in a column. The `Min width` doesn't have any effect in this case. The width of the repeated panels will be the same as of the first panel (the original template) being repeated.
+Set `Max per row` to tell grafana how many panels per row you want at most. It defaults to *4* if you don't set anything.
+
+By choosing `vertical` the panels will be arranged from top to bottom in a column. The width of the repeated panels will be the same as of the first panel (the original template) being repeated.
 
 
 Only make changes to the first panel (the original template). To have the changes take effect on all panels you need to trigger a dynamic dashboard re-build.
 Only make changes to the first panel (the original template). To have the changes take effect on all panels you need to trigger a dynamic dashboard re-build.
 You can do this by either changing the variable value (that is the basis for the repeat) or reload the dashboard.
 You can do this by either changing the variable value (that is the basis for the repeat) or reload the dashboard.

+ 11 - 0
packages/grafana-ui/src/components/LoadingPlaceholder/LoadingPlaceholder.tsx

@@ -0,0 +1,11 @@
+import React, { SFC } from 'react';
+
+interface LoadingPlaceholderProps {
+  text: string;
+}
+
+export const LoadingPlaceholder: SFC<LoadingPlaceholderProps> = ({ text }) => (
+  <div className="gf-form-group">
+    {text} <i className="fa fa-spinner fa-spin" />
+  </div>
+);

+ 4 - 1
public/app/core/components/Select/IndicatorsContainer.tsx → packages/grafana-ui/src/components/Select/IndicatorsContainer.tsx

@@ -1,7 +1,10 @@
 import React from 'react';
 import React from 'react';
+
+// Ignoring because I couldn't get @types/react-select work wih Torkel's fork
+// @ts-ignore
 import { components } from '@torkelo/react-select';
 import { components } from '@torkelo/react-select';
 
 
-export const IndicatorsContainer = props => {
+export const IndicatorsContainer = (props: any) => {
   const isOpen = props.selectProps.menuIsOpen;
   const isOpen = props.selectProps.menuIsOpen;
   return (
   return (
     <components.IndicatorsContainer {...props}>
     <components.IndicatorsContainer {...props}>

+ 4 - 0
public/app/core/components/Select/NoOptionsMessage.tsx → packages/grafana-ui/src/components/Select/NoOptionsMessage.tsx

@@ -1,5 +1,9 @@
 import React from 'react';
 import React from 'react';
+
+// Ignoring because I couldn't get @types/react-select work wih Torkel's fork
+// @ts-ignore
 import { components } from '@torkelo/react-select';
 import { components } from '@torkelo/react-select';
+// @ts-ignore
 import { OptionProps } from '@torkelo/react-select/lib/components/Option';
 import { OptionProps } from '@torkelo/react-select/lib/components/Option';
 
 
 export interface Props {
 export interface Props {

+ 13 - 8
public/app/core/components/Select/Select.tsx → packages/grafana-ui/src/components/Select/Select.tsx

@@ -1,16 +1,21 @@
 // Libraries
 // Libraries
 import classNames from 'classnames';
 import classNames from 'classnames';
 import React, { PureComponent } from 'react';
 import React, { PureComponent } from 'react';
+
+// Ignoring because I couldn't get @types/react-select work wih Torkel's fork
+// @ts-ignore
 import { default as ReactSelect } from '@torkelo/react-select';
 import { default as ReactSelect } from '@torkelo/react-select';
+// @ts-ignore
 import { default as ReactAsyncSelect } from '@torkelo/react-select/lib/Async';
 import { default as ReactAsyncSelect } from '@torkelo/react-select/lib/Async';
+// @ts-ignore
 import { components } from '@torkelo/react-select';
 import { components } from '@torkelo/react-select';
 
 
 // Components
 // Components
-import { Option, SingleValue } from './PickerOption';
-import OptionGroup from './OptionGroup';
+import { SelectOption, SingleValue } from './SelectOption';
+import SelectOptionGroup from './SelectOptionGroup';
 import IndicatorsContainer from './IndicatorsContainer';
 import IndicatorsContainer from './IndicatorsContainer';
 import NoOptionsMessage from './NoOptionsMessage';
 import NoOptionsMessage from './NoOptionsMessage';
-import ResetStyles from './ResetStyles';
+import resetSelectStyles from './resetSelectStyles';
 import { CustomScrollbar } from '@grafana/ui';
 import { CustomScrollbar } from '@grafana/ui';
 
 
 export interface SelectOptionItem {
 export interface SelectOptionItem {
@@ -53,7 +58,7 @@ interface AsyncProps {
   loadingMessage?: () => string;
   loadingMessage?: () => string;
 }
 }
 
 
-export const MenuList = props => {
+export const MenuList = (props: any) => {
   return (
   return (
     <components.MenuList {...props}>
     <components.MenuList {...props}>
       <CustomScrollbar autoHide={false}>{props.children}</CustomScrollbar>
       <CustomScrollbar autoHide={false}>{props.children}</CustomScrollbar>
@@ -112,11 +117,11 @@ export class Select extends PureComponent<CommonProps & SelectProps> {
         classNamePrefix="gf-form-select-box"
         classNamePrefix="gf-form-select-box"
         className={selectClassNames}
         className={selectClassNames}
         components={{
         components={{
-          Option,
+          Option: SelectOption,
           SingleValue,
           SingleValue,
           IndicatorsContainer,
           IndicatorsContainer,
           MenuList,
           MenuList,
-          Group: OptionGroup,
+          Group: SelectOptionGroup,
         }}
         }}
         defaultValue={defaultValue}
         defaultValue={defaultValue}
         value={value}
         value={value}
@@ -127,7 +132,7 @@ export class Select extends PureComponent<CommonProps & SelectProps> {
         onChange={onChange}
         onChange={onChange}
         options={options}
         options={options}
         placeholder={placeholder || 'Choose'}
         placeholder={placeholder || 'Choose'}
-        styles={ResetStyles}
+        styles={resetSelectStyles()}
         isDisabled={isDisabled}
         isDisabled={isDisabled}
         isLoading={isLoading}
         isLoading={isLoading}
         isClearable={isClearable}
         isClearable={isClearable}
@@ -212,7 +217,7 @@ export class AsyncSelect extends PureComponent<CommonProps & AsyncProps> {
         isLoading={isLoading}
         isLoading={isLoading}
         defaultOptions={defaultOptions}
         defaultOptions={defaultOptions}
         placeholder={placeholder || 'Choose'}
         placeholder={placeholder || 'Choose'}
-        styles={ResetStyles}
+        styles={resetSelectStyles()}
         loadingMessage={loadingMessage}
         loadingMessage={loadingMessage}
         noOptionsMessage={noOptionsMessage}
         noOptionsMessage={noOptionsMessage}
         isDisabled={isDisabled}
         isDisabled={isDisabled}

+ 22 - 12
public/app/core/components/Select/PickerOption.test.tsx → packages/grafana-ui/src/components/Select/SelectOption.test.tsx

@@ -1,11 +1,11 @@
 import React from 'react';
 import React from 'react';
 import renderer from 'react-test-renderer';
 import renderer from 'react-test-renderer';
-import PickerOption from './PickerOption';
+import SelectOption from './SelectOption';
+import { OptionProps } from 'react-select/lib/components/Option';
 
 
-const model = {
+const model: OptionProps<any> = {
   cx: jest.fn(),
   cx: jest.fn(),
   clearValue: jest.fn(),
   clearValue: jest.fn(),
-  onSelect: jest.fn(),
   getStyles: jest.fn(),
   getStyles: jest.fn(),
   getValue: jest.fn(),
   getValue: jest.fn(),
   hasValue: true,
   hasValue: true,
@@ -18,21 +18,31 @@ const model = {
   isFocused: false,
   isFocused: false,
   isSelected: false,
   isSelected: false,
   innerRef: null,
   innerRef: null,
-  innerProps: null,
+  innerProps: {
+    id: '',
+    key: '',
+    onClick: jest.fn(),
+    onMouseOver: jest.fn(),
+    tabIndex: 1,
+  },
   label: 'Option label',
   label: 'Option label',
-  type: null,
+  type: 'option',
   children: 'Model title',
   children: 'Model title',
-  data: {
-    title: 'Model title',
-    imgUrl: 'url/to/avatar',
-    label: 'User picker label',
-  },
   className: 'class-for-user-picker',
   className: 'class-for-user-picker',
 };
 };
 
 
-describe('PickerOption', () => {
+describe('SelectOption', () => {
   it('renders correctly', () => {
   it('renders correctly', () => {
-    const tree = renderer.create(<PickerOption {...model} />).toJSON();
+    const tree = renderer
+      .create(
+        <SelectOption
+          {...model}
+          data={{
+            imgUrl: 'url/to/avatar',
+          }}
+        />
+      )
+      .toJSON();
     expect(tree).toMatchSnapshot();
     expect(tree).toMatchSnapshot();
   });
   });
 });
 });

+ 6 - 3
public/app/core/components/Select/PickerOption.tsx → packages/grafana-ui/src/components/Select/SelectOption.tsx

@@ -1,4 +1,7 @@
 import React from 'react';
 import React from 'react';
+
+// Ignoring because I couldn't get @types/react-select work wih Torkel's fork
+// @ts-ignore
 import { components } from '@torkelo/react-select';
 import { components } from '@torkelo/react-select';
 import { OptionProps } from 'react-select/lib/components/Option';
 import { OptionProps } from 'react-select/lib/components/Option';
 
 
@@ -10,7 +13,7 @@ interface ExtendedOptionProps extends OptionProps<any> {
   };
   };
 }
 }
 
 
-export const Option = (props: ExtendedOptionProps) => {
+export const SelectOption = (props: ExtendedOptionProps) => {
   const { children, isSelected, data } = props;
   const { children, isSelected, data } = props;
 
 
   return (
   return (
@@ -28,7 +31,7 @@ export const Option = (props: ExtendedOptionProps) => {
 };
 };
 
 
 // was not able to type this without typescript error
 // was not able to type this without typescript error
-export const SingleValue = props => {
+export const SingleValue = (props: any) => {
   const { children, data } = props;
   const { children, data } = props;
 
 
   return (
   return (
@@ -41,4 +44,4 @@ export const SingleValue = props => {
   );
   );
 };
 };
 
 
-export default Option;
+export default SelectOption;

+ 2 - 2
public/app/core/components/Select/OptionGroup.tsx → packages/grafana-ui/src/components/Select/SelectOptionGroup.tsx

@@ -9,7 +9,7 @@ interface State {
   expanded: boolean;
   expanded: boolean;
 }
 }
 
 
-export default class OptionGroup extends PureComponent<ExtendedGroupProps, State> {
+export default class SelectOptionGroup extends PureComponent<ExtendedGroupProps, State> {
   state = {
   state = {
     expanded: false,
     expanded: false,
   };
   };
@@ -24,7 +24,7 @@ export default class OptionGroup extends PureComponent<ExtendedGroupProps, State
     }
     }
   }
   }
 
 
-  componentDidUpdate(nextProps) {
+  componentDidUpdate(nextProps: ExtendedGroupProps) {
     if (nextProps.selectProps.inputValue !== '') {
     if (nextProps.selectProps.inputValue !== '') {
       this.setState({ expanded: true });
       this.setState({ expanded: true });
     }
     }

+ 0 - 0
public/sass/components/_form_select_box.scss → packages/grafana-ui/src/components/Select/_Select.scss


+ 7 - 2
public/app/core/components/Select/__snapshots__/PickerOption.test.tsx.snap → packages/grafana-ui/src/components/Select/__snapshots__/SelectOption.test.tsx.snap

@@ -1,7 +1,12 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 
-exports[`PickerOption renders correctly 1`] = `
-<div>
+exports[`SelectOption renders correctly 1`] = `
+<div
+  id=""
+  onClick={[MockFunction]}
+  onMouseOver={[MockFunction]}
+  tabIndex={1}
+>
   <div
   <div
     className="gf-form-select-box__desc-option"
     className="gf-form-select-box__desc-option"
   >
   >

+ 27 - 0
packages/grafana-ui/src/components/Select/resetSelectStyles.ts

@@ -0,0 +1,27 @@
+export default function resetSelectStyles() {
+  return {
+    clearIndicator: () => ({}),
+    container: () => ({}),
+    control: () => ({}),
+    dropdownIndicator: () => ({}),
+    group: () => ({}),
+    groupHeading: () => ({}),
+    indicatorsContainer: () => ({}),
+    indicatorSeparator: () => ({}),
+    input: () => ({}),
+    loadingIndicator: () => ({}),
+    loadingMessage: () => ({}),
+    menu: () => ({}),
+    menuList: ({ maxHeight }: { maxHeight: number }) => ({
+      maxHeight,
+    }),
+    multiValue: () => ({}),
+    multiValueLabel: () => ({}),
+    multiValueRemove: () => ({}),
+    noOptionsMessage: () => ({}),
+    option: () => ({}),
+    placeholder: () => ({}),
+    singleValue: () => ({}),
+    valueContainer: () => ({}),
+  };
+}

+ 1 - 0
packages/grafana-ui/src/components/index.scss

@@ -2,3 +2,4 @@
 @import 'DeleteButton/DeleteButton';
 @import 'DeleteButton/DeleteButton';
 @import 'ThresholdsEditor/ThresholdsEditor';
 @import 'ThresholdsEditor/ThresholdsEditor';
 @import 'Tooltip/Tooltip';
 @import 'Tooltip/Tooltip';
+@import 'Select/Select';

+ 8 - 0
packages/grafana-ui/src/components/index.ts

@@ -2,6 +2,14 @@ export { DeleteButton } from './DeleteButton/DeleteButton';
 export { Tooltip } from './Tooltip/Tooltip';
 export { Tooltip } from './Tooltip/Tooltip';
 export { Portal } from './Portal/Portal';
 export { Portal } from './Portal/Portal';
 export { CustomScrollbar } from './CustomScrollbar/CustomScrollbar';
 export { CustomScrollbar } from './CustomScrollbar/CustomScrollbar';
+
+// Select
+export { Select, AsyncSelect, SelectOptionItem } from './Select/Select';
+export { IndicatorsContainer } from './Select/IndicatorsContainer';
+export { NoOptionsMessage } from './Select/NoOptionsMessage';
+export { default as resetSelectStyles } from './Select/resetSelectStyles';
+
+export { LoadingPlaceholder } from './LoadingPlaceholder/LoadingPlaceholder';
 export { ColorPicker } from './ColorPicker/ColorPicker';
 export { ColorPicker } from './ColorPicker/ColorPicker';
 export { SeriesColorPickerPopover } from './ColorPicker/SeriesColorPickerPopover';
 export { SeriesColorPickerPopover } from './ColorPicker/SeriesColorPickerPopover';
 export { SeriesColorPicker } from './ColorPicker/SeriesColorPicker';
 export { SeriesColorPicker } from './ColorPicker/SeriesColorPicker';

+ 1 - 1
pkg/models/dashboards.go

@@ -112,7 +112,7 @@ func NewDashboard(title string) *Dashboard {
 func NewDashboardFolder(title string) *Dashboard {
 func NewDashboardFolder(title string) *Dashboard {
 	folder := NewDashboard(title)
 	folder := NewDashboard(title)
 	folder.IsFolder = true
 	folder.IsFolder = true
-	folder.Data.Set("schemaVersion", 16)
+	folder.Data.Set("schemaVersion", 17)
 	folder.Data.Set("version", 0)
 	folder.Data.Set("version", 0)
 	folder.IsFolder = true
 	folder.IsFolder = true
 	return folder
 	return folder

+ 1 - 1
public/app/core/components/PermissionList/AddPermission.tsx

@@ -1,7 +1,7 @@
 import React, { Component } from 'react';
 import React, { Component } from 'react';
 import { UserPicker } from 'app/core/components/Select/UserPicker';
 import { UserPicker } from 'app/core/components/Select/UserPicker';
 import { TeamPicker, Team } from 'app/core/components/Select/TeamPicker';
 import { TeamPicker, Team } from 'app/core/components/Select/TeamPicker';
-import { Select, SelectOptionItem } from 'app/core/components/Select/Select';
+import { Select, SelectOptionItem } from '@grafana/ui';
 import { User } from 'app/types';
 import { User } from 'app/types';
 import {
 import {
   dashboardPermissionLevels,
   dashboardPermissionLevels,

+ 1 - 1
public/app/core/components/PermissionList/DisabledPermissionListItem.tsx

@@ -1,5 +1,5 @@
 import React, { Component } from 'react';
 import React, { Component } from 'react';
-import Select from 'app/core/components/Select/Select';
+import { Select } from '@grafana/ui';
 import { dashboardPermissionLevels } from 'app/types/acl';
 import { dashboardPermissionLevels } from 'app/types/acl';
 
 
 export interface Props {
 export interface Props {

+ 1 - 1
public/app/core/components/PermissionList/PermissionListItem.tsx

@@ -1,5 +1,5 @@
 import React, { PureComponent } from 'react';
 import React, { PureComponent } from 'react';
-import { Select } from 'app/core/components/Select/Select';
+import { Select } from '@grafana/ui';
 import { dashboardPermissionLevels, DashboardAcl, PermissionLevel } from 'app/types/acl';
 import { dashboardPermissionLevels, DashboardAcl, PermissionLevel } from 'app/types/acl';
 import { FolderInfo } from 'app/types';
 import { FolderInfo } from 'app/types';
 
 

+ 1 - 1
public/app/core/components/Select/DataSourcePicker.tsx

@@ -3,7 +3,7 @@ import React, { PureComponent } from 'react';
 import _ from 'lodash';
 import _ from 'lodash';
 
 
 // Components
 // Components
-import Select from './Select';
+import { Select } from '@grafana/ui';
 
 
 // Types
 // Types
 import { DataSourceSelectItem } from 'app/types';
 import { DataSourceSelectItem } from 'app/types';

+ 0 - 25
public/app/core/components/Select/ResetStyles.tsx

@@ -1,25 +0,0 @@
-export default {
-  clearIndicator: () => ({}),
-  container: () => ({}),
-  control: () => ({}),
-  dropdownIndicator: () => ({}),
-  group: () => ({}),
-  groupHeading: () => ({}),
-  indicatorsContainer: () => ({}),
-  indicatorSeparator: () => ({}),
-  input: () => ({}),
-  loadingIndicator: () => ({}),
-  loadingMessage: () => ({}),
-  menu: () => ({}),
-  menuList: ({ maxHeight }: { maxHeight: number }) => ({
-    maxHeight,
-  }),
-  multiValue: () => ({}),
-  multiValueLabel: () => ({}),
-  multiValueRemove: () => ({}),
-  noOptionsMessage: () => ({}),
-  option: () => ({}),
-  placeholder: () => ({}),
-  singleValue: () => ({}),
-  valueContainer: () => ({}),
-};

+ 1 - 1
public/app/core/components/Select/TeamPicker.tsx

@@ -1,6 +1,6 @@
 import React, { Component } from 'react';
 import React, { Component } from 'react';
 import _ from 'lodash';
 import _ from 'lodash';
-import { AsyncSelect } from './Select';
+import { AsyncSelect } from '@grafana/ui';
 import { debounce } from 'lodash';
 import { debounce } from 'lodash';
 import { getBackendSrv } from 'app/core/services/backend_srv';
 import { getBackendSrv } from 'app/core/services/backend_srv';
 
 

+ 1 - 1
public/app/core/components/Select/UnitPicker.tsx

@@ -1,5 +1,5 @@
 import React, { PureComponent } from 'react';
 import React, { PureComponent } from 'react';
-import Select from './Select';
+import { Select } from '@grafana/ui';
 import kbn from 'app/core/utils/kbn';
 import kbn from 'app/core/utils/kbn';
 
 
 interface Props {
 interface Props {

+ 1 - 1
public/app/core/components/Select/UserPicker.tsx

@@ -3,7 +3,7 @@ import React, { Component } from 'react';
 import _ from 'lodash';
 import _ from 'lodash';
 
 
 // Components
 // Components
-import { AsyncSelect } from './Select';
+import { AsyncSelect } from '@grafana/ui';
 
 
 // Utils & Services
 // Utils & Services
 import { debounce } from 'lodash';
 import { debounce } from 'lodash';

+ 1 - 1
public/app/core/components/SharedPreferences/SharedPreferences.tsx

@@ -1,7 +1,7 @@
 import React, { PureComponent } from 'react';
 import React, { PureComponent } from 'react';
 
 
 import { Label } from 'app/core/components/Label/Label';
 import { Label } from 'app/core/components/Label/Label';
-import Select from 'app/core/components/Select/Select';
+import { Select } from '@grafana/ui';
 import { getBackendSrv, BackendSrv } from 'app/core/services/backend_srv';
 import { getBackendSrv, BackendSrv } from 'app/core/services/backend_srv';
 
 
 import { DashboardSearchHit } from 'app/types';
 import { DashboardSearchHit } from 'app/types';

+ 2 - 4
public/app/core/components/TagFilter/TagFilter.tsx

@@ -1,12 +1,10 @@
 import React from 'react';
 import React from 'react';
+import { NoOptionsMessage, IndicatorsContainer, resetSelectStyles } from '@grafana/ui';
 import AsyncSelect from '@torkelo/react-select/lib/Async';
 import AsyncSelect from '@torkelo/react-select/lib/Async';
 
 
 import { TagOption } from './TagOption';
 import { TagOption } from './TagOption';
 import { TagBadge } from './TagBadge';
 import { TagBadge } from './TagBadge';
-import IndicatorsContainer from 'app/core/components/Select/IndicatorsContainer';
-import NoOptionsMessage from 'app/core/components/Select/NoOptionsMessage';
 import { components } from '@torkelo/react-select';
 import { components } from '@torkelo/react-select';
-import ResetStyles from 'app/core/components/Select/ResetStyles';
 
 
 export interface Props {
 export interface Props {
   tags: string[];
   tags: string[];
@@ -51,7 +49,7 @@ export class TagFilter extends React.Component<Props, any> {
       getOptionValue: i => i.value,
       getOptionValue: i => i.value,
       getOptionLabel: i => i.label,
       getOptionLabel: i => i.label,
       value: tags,
       value: tags,
-      styles: ResetStyles,
+      styles: resetSelectStyles(),
       filterOption: (option, searchQuery) => {
       filterOption: (option, searchQuery) => {
         const regex = RegExp(searchQuery, 'i');
         const regex = RegExp(searchQuery, 'i');
         return regex.test(option.value);
         return regex.test(option.value);

+ 8 - 0
public/app/core/specs/factors.test.ts

@@ -0,0 +1,8 @@
+import getFactors from 'app/core/utils/factors';
+
+describe('factors', () => {
+  it('should return factors for 12', () => {
+    const factors = getFactors(12);
+    expect(factors).toEqual([1, 2, 3, 4, 6, 12]);
+  });
+});

+ 5 - 0
public/app/core/utils/factors.ts

@@ -0,0 +1,5 @@
+// Returns the factors of a number
+// Example getFactors(12) -> [1, 2, 3, 4, 6, 12]
+export default function getFactors(num: number): number[] {
+  return Array.from(new Array(num + 1), (_, i) => i).filter(i => num % i === 0);
+}

+ 5 - 15
public/app/features/alerting/AlertTab.tsx

@@ -1,5 +1,5 @@
 // Libraries
 // Libraries
-import React, { PureComponent, SFC } from 'react';
+import React, { PureComponent } from 'react';
 
 
 // Services & Utils
 // Services & Utils
 import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader';
 import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader';
@@ -14,7 +14,7 @@ import 'app/features/alerting/AlertTabCtrl';
 // Types
 // Types
 import { DashboardModel } from '../dashboard/dashboard_model';
 import { DashboardModel } from '../dashboard/dashboard_model';
 import { PanelModel } from '../dashboard/panel_model';
 import { PanelModel } from '../dashboard/panel_model';
-import { TestRuleButton } from './TestRuleButton';
+import { TestRuleResult } from './TestRuleResult';
 
 
 interface Props {
 interface Props {
   angularPanel?: AngularComponent;
   angularPanel?: AngularComponent;
@@ -22,16 +22,6 @@ interface Props {
   panel: PanelModel;
   panel: PanelModel;
 }
 }
 
 
-interface LoadingPlaceholderProps {
-  text: string;
-}
-
-const LoadingPlaceholder: SFC<LoadingPlaceholderProps> = ({ text }) => (
-  <div className="gf-form-group">
-    {text} <i className="fa fa-spinner fa-spin" />
-  </div>
-);
-
 export class AlertTab extends PureComponent<Props> {
 export class AlertTab extends PureComponent<Props> {
   element: any;
   element: any;
   component: AngularComponent;
   component: AngularComponent;
@@ -120,14 +110,14 @@ export class AlertTab extends PureComponent<Props> {
     };
     };
   };
   };
 
 
-  renderTestRuleButton = () => {
+  renderTestRuleResult = () => {
     const { panel, dashboard } = this.props;
     const { panel, dashboard } = this.props;
-    return <TestRuleButton panelId={panel.id} dashboard={dashboard} LoadingPlaceholder={LoadingPlaceholder} />;
+    return <TestRuleResult panelId={panel.id} dashboard={dashboard} />;
   };
   };
 
 
   testRule = (): EditorToolbarView => ({
   testRule = (): EditorToolbarView => ({
     title: 'Test Rule',
     title: 'Test Rule',
-    render: () => this.renderTestRuleButton(),
+    render: () => this.renderTestRuleResult(),
   });
   });
 
 
   onAddAlert = () => {
   onAddAlert = () => {

+ 3 - 4
public/app/features/alerting/TestRuleButton.test.tsx → public/app/features/alerting/TestRuleResult.test.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import React from 'react';
 import { shallow } from 'enzyme';
 import { shallow } from 'enzyme';
 import { DashboardModel } from '../dashboard/dashboard_model';
 import { DashboardModel } from '../dashboard/dashboard_model';
-import { Props, TestRuleButton } from './TestRuleButton';
+import { Props, TestRuleResult } from './TestRuleResult';
 
 
 jest.mock('app/core/services/backend_srv', () => ({
 jest.mock('app/core/services/backend_srv', () => ({
   getBackendSrv: () => ({
   getBackendSrv: () => ({
@@ -13,14 +13,13 @@ const setup = (propOverrides?: object) => {
   const props: Props = {
   const props: Props = {
     panelId: 1,
     panelId: 1,
     dashboard: new DashboardModel({ panels: [{ id: 1 }] }),
     dashboard: new DashboardModel({ panels: [{ id: 1 }] }),
-    LoadingPlaceholder: {},
   };
   };
 
 
   Object.assign(props, propOverrides);
   Object.assign(props, propOverrides);
 
 
-  const wrapper = shallow(<TestRuleButton {...props} />);
+  const wrapper = shallow(<TestRuleResult {...props} />);
 
 
-  return { wrapper, instance: wrapper.instance() as TestRuleButton };
+  return { wrapper, instance: wrapper.instance() as TestRuleResult };
 };
 };
 
 
 describe('Render', () => {
 describe('Render', () => {

+ 5 - 4
public/app/features/alerting/TestRuleButton.tsx → public/app/features/alerting/TestRuleResult.tsx

@@ -2,11 +2,11 @@ import React, { PureComponent } from 'react';
 import { JSONFormatter } from 'app/core/components/JSONFormatter/JSONFormatter';
 import { JSONFormatter } from 'app/core/components/JSONFormatter/JSONFormatter';
 import { getBackendSrv } from 'app/core/services/backend_srv';
 import { getBackendSrv } from 'app/core/services/backend_srv';
 import { DashboardModel } from '../dashboard/dashboard_model';
 import { DashboardModel } from '../dashboard/dashboard_model';
+import { LoadingPlaceholder } from '@grafana/ui/src';
 
 
 export interface Props {
 export interface Props {
   panelId: number;
   panelId: number;
   dashboard: DashboardModel;
   dashboard: DashboardModel;
-  LoadingPlaceholder: any;
 }
 }
 
 
 interface State {
 interface State {
@@ -14,7 +14,7 @@ interface State {
   testRuleResponse: {};
   testRuleResponse: {};
 }
 }
 
 
-export class TestRuleButton extends PureComponent<Props, State> {
+export class TestRuleResult extends PureComponent<Props, State> {
   readonly state: State = {
   readonly state: State = {
     isLoading: false,
     isLoading: false,
     testRuleResponse: {},
     testRuleResponse: {},
@@ -27,13 +27,14 @@ export class TestRuleButton extends PureComponent<Props, State> {
   async testRule() {
   async testRule() {
     const { panelId, dashboard } = this.props;
     const { panelId, dashboard } = this.props;
     const payload = { dashboard: dashboard.getSaveModelClone(), panelId };
     const payload = { dashboard: dashboard.getSaveModelClone(), panelId };
+
+    this.setState({ isLoading: true });
     const testRuleResponse = await getBackendSrv().post(`/api/alerts/test`, payload);
     const testRuleResponse = await getBackendSrv().post(`/api/alerts/test`, payload);
-    this.setState(prevState => ({ ...prevState, isLoading: false, testRuleResponse }));
+    this.setState({ isLoading: false, testRuleResponse });
   }
   }
 
 
   render() {
   render() {
     const { testRuleResponse, isLoading } = this.state;
     const { testRuleResponse, isLoading } = this.state;
-    const { LoadingPlaceholder } = this.props;
 
 
     if (isLoading === true) {
     if (isLoading === true) {
       return <LoadingPlaceholder text="Evaluating rule" />;
       return <LoadingPlaceholder text="Evaluating rule" />;

+ 0 - 13
public/app/features/alerting/__snapshots__/TestRuleButton.test.tsx.snap

@@ -1,13 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Render should render component 1`] = `
-<JSONFormatter
-  config={
-    Object {
-      "animateOpen": true,
-    }
-  }
-  json={Object {}}
-  open={3}
-/>
-`;

+ 7 - 0
public/app/features/alerting/__snapshots__/TestRuleResult.test.tsx.snap

@@ -0,0 +1,7 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Render should render component 1`] = `
+<Component
+  text="Evaluating rule"
+/>
+`;

+ 20 - 1
public/app/features/dashboard/dashboard_migration.ts

@@ -9,6 +9,7 @@ import {
 } from 'app/core/constants';
 } from 'app/core/constants';
 import { PanelModel } from './panel_model';
 import { PanelModel } from './panel_model';
 import { DashboardModel } from './dashboard_model';
 import { DashboardModel } from './dashboard_model';
+import getFactors from 'app/core/utils/factors';
 
 
 export class DashboardMigrator {
 export class DashboardMigrator {
   dashboard: DashboardModel;
   dashboard: DashboardModel;
@@ -21,7 +22,7 @@ export class DashboardMigrator {
     let i, j, k, n;
     let i, j, k, n;
     const oldVersion = this.dashboard.schemaVersion;
     const oldVersion = this.dashboard.schemaVersion;
     const panelUpgrades = [];
     const panelUpgrades = [];
-    this.dashboard.schemaVersion = 16;
+    this.dashboard.schemaVersion = 17;
 
 
     if (oldVersion === this.dashboard.schemaVersion) {
     if (oldVersion === this.dashboard.schemaVersion) {
       return;
       return;
@@ -368,6 +369,24 @@ export class DashboardMigrator {
       this.upgradeToGridLayout(old);
       this.upgradeToGridLayout(old);
     }
     }
 
 
+    if (oldVersion < 17) {
+      panelUpgrades.push(panel => {
+        if (panel.minSpan) {
+          const max = GRID_COLUMN_COUNT / panel.minSpan;
+          const factors = getFactors(GRID_COLUMN_COUNT);
+          // find the best match compared to factors
+          // (ie. [1,2,3,4,6,12,24] for 24 columns)
+          panel.maxPerRow =
+            factors[
+              _.findIndex(factors, o => {
+                return o > max;
+              }) - 1
+            ];
+        }
+        delete panel.minSpan;
+      });
+    }
+
     if (panelUpgrades.length === 0) {
     if (panelUpgrades.length === 0) {
       return;
       return;
     }
     }

+ 2 - 2
public/app/features/dashboard/dashboard_model.ts

@@ -442,7 +442,7 @@ export class DashboardModel {
     }
     }
 
 
     const selectedOptions = this.getSelectedVariableOptions(variable);
     const selectedOptions = this.getSelectedVariableOptions(variable);
-    const minWidth = panel.minSpan || 6;
+    const maxPerRow = panel.maxPerRow || 4;
     let xPos = 0;
     let xPos = 0;
     let yPos = panel.gridPos.y;
     let yPos = panel.gridPos.y;
 
 
@@ -462,7 +462,7 @@ export class DashboardModel {
       } else {
       } else {
         // set width based on how many are selected
         // set width based on how many are selected
         // assumed the repeated panels should take up full row width
         // assumed the repeated panels should take up full row width
-        copy.gridPos.w = Math.max(GRID_COLUMN_COUNT / selectedOptions.length, minWidth);
+        copy.gridPos.w = Math.max(GRID_COLUMN_COUNT / selectedOptions.length, GRID_COLUMN_COUNT / maxPerRow);
         copy.gridPos.x = xPos;
         copy.gridPos.x = xPos;
         copy.gridPos.y = yPos;
         copy.gridPos.y = yPos;
 
 

+ 3 - 9
public/app/features/dashboard/dashgrid/QueriesTab.tsx

@@ -1,10 +1,10 @@
 // Libraries
 // Libraries
-import React, { PureComponent, SFC } from 'react';
+import React, { PureComponent } from 'react';
 import _ from 'lodash';
 import _ from 'lodash';
 
 
 // Components
 // Components
 import 'app/features/panel/metrics_tab';
 import 'app/features/panel/metrics_tab';
-import { EditorTabBody, EditorToolbarView} from './EditorTabBody';
+import { EditorTabBody, EditorToolbarView } from './EditorTabBody';
 import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
 import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
 import { QueryInspector } from './QueryInspector';
 import { QueryInspector } from './QueryInspector';
 import { QueryOptions } from './QueryOptions';
 import { QueryOptions } from './QueryOptions';
@@ -36,12 +36,6 @@ interface State {
   isAddingMixed: boolean;
   isAddingMixed: boolean;
 }
 }
 
 
-interface LoadingPlaceholderProps {
-  text: string;
-}
-
-const LoadingPlaceholder: SFC<LoadingPlaceholderProps> = ({ text }) => <h2>{text}</h2>;
-
 export class QueriesTab extends PureComponent<Props, State> {
 export class QueriesTab extends PureComponent<Props, State> {
   element: HTMLElement;
   element: HTMLElement;
   component: AngularComponent;
   component: AngularComponent;
@@ -134,7 +128,7 @@ export class QueriesTab extends PureComponent<Props, State> {
 
 
   renderQueryInspector = () => {
   renderQueryInspector = () => {
     const { panel } = this.props;
     const { panel } = this.props;
-    return <QueryInspector panel={panel} LoadingPlaceholder={LoadingPlaceholder} />;
+    return <QueryInspector panel={panel} />;
   };
   };
 
 
   renderHelp = () => {
   renderHelp = () => {

+ 1 - 2
public/app/features/dashboard/dashgrid/QueryInspector.tsx

@@ -2,6 +2,7 @@ import React, { PureComponent } from 'react';
 import { JSONFormatter } from 'app/core/components/JSONFormatter/JSONFormatter';
 import { JSONFormatter } from 'app/core/components/JSONFormatter/JSONFormatter';
 import appEvents from 'app/core/app_events';
 import appEvents from 'app/core/app_events';
 import { CopyToClipboard } from 'app/core/components/CopyToClipboard/CopyToClipboard';
 import { CopyToClipboard } from 'app/core/components/CopyToClipboard/CopyToClipboard';
+import { LoadingPlaceholder } from '@grafana/ui';
 
 
 interface DsQuery {
 interface DsQuery {
   isLoading: boolean;
   isLoading: boolean;
@@ -10,7 +11,6 @@ interface DsQuery {
 
 
 interface Props {
 interface Props {
   panel: any;
   panel: any;
-  LoadingPlaceholder: any;
 }
 }
 
 
 interface State {
 interface State {
@@ -177,7 +177,6 @@ export class QueryInspector extends PureComponent<Props, State> {
 
 
   render() {
   render() {
     const { response, isLoading } = this.state.dsQuery;
     const { response, isLoading } = this.state.dsQuery;
-    const { LoadingPlaceholder } = this.props;
     const { isMocking } = this.state;
     const { isMocking } = this.state;
     const openNodes = this.getNrOfOpenNodes();
     const openNodes = this.getNrOfOpenNodes();
 
 

+ 1 - 1
public/app/features/dashboard/panel_model.ts

@@ -77,7 +77,7 @@ export class PanelModel {
   repeatPanelId?: number;
   repeatPanelId?: number;
   repeatDirection?: string;
   repeatDirection?: string;
   repeatedByRow?: boolean;
   repeatedByRow?: boolean;
-  minSpan?: number;
+  maxPerRow?: number;
   collapsed?: boolean;
   collapsed?: boolean;
   panels?: any;
   panels?: any;
   soloMode?: boolean;
   soloMode?: boolean;

+ 11 - 9
public/app/features/dashboard/specs/dashboard_migration.test.ts

@@ -127,7 +127,7 @@ describe('DashboardModel', () => {
     });
     });
 
 
     it('dashboard schema version should be set to latest', () => {
     it('dashboard schema version should be set to latest', () => {
-      expect(model.schemaVersion).toBe(16);
+      expect(model.schemaVersion).toBe(17);
     });
     });
 
 
     it('graph thresholds should be migrated', () => {
     it('graph thresholds should be migrated', () => {
@@ -364,14 +364,6 @@ describe('DashboardModel', () => {
       expect(dashboard.panels.length).toBe(2);
       expect(dashboard.panels.length).toBe(2);
     });
     });
 
 
-    it('minSpan should be twice', () => {
-      model.rows = [createRow({ height: 8 }, [[6]])];
-      model.rows[0].panels[0] = { minSpan: 12 };
-
-      const dashboard = new DashboardModel(model);
-      expect(dashboard.panels[0].minSpan).toBe(24);
-    });
-
     it('should assign id', () => {
     it('should assign id', () => {
       model.rows = [createRow({ collapse: true, height: 8 }, [[6], [6]])];
       model.rows = [createRow({ collapse: true, height: 8 }, [[6], [6]])];
       model.rows[0].panels[0] = {};
       model.rows[0].panels[0] = {};
@@ -380,6 +372,16 @@ describe('DashboardModel', () => {
       expect(dashboard.panels[0].id).toBe(1);
       expect(dashboard.panels[0].id).toBe(1);
     });
     });
   });
   });
+
+  describe('when migrating from minSpan to maxPerRow', () => {
+    it('maxPerRow should be correct', () => {
+      const model = {
+        panels: [{ minSpan: 8 }],
+      };
+      const dashboard = new DashboardModel(model);
+      expect(dashboard.panels[0].maxPerRow).toBe(3);
+    });
+  });
 });
 });
 
 
 function createRow(options, panelDescriptions: any[]) {
 function createRow(options, panelDescriptions: any[]) {

+ 3 - 6
public/app/features/dashboard/utils/panel.ts

@@ -143,12 +143,9 @@ export function applyPanelTimeOverrides(panel: PanelModel, timeRange: TimeRange)
     const timeShift = '-' + timeShiftInterpolated;
     const timeShift = '-' + timeShiftInterpolated;
     newTimeData.timeInfo += ' timeshift ' + timeShift;
     newTimeData.timeInfo += ' timeshift ' + timeShift;
     newTimeData.timeRange = {
     newTimeData.timeRange = {
-      from: dateMath.parseDateMath(timeShift, timeRange.from, false),
-      to: dateMath.parseDateMath(timeShift, timeRange.to, true),
-      raw: {
-        from: timeRange.from,
-        to: timeRange.to,
-      },
+      from: dateMath.parseDateMath(timeShift, newTimeData.timeRange.from, false),
+      to: dateMath.parseDateMath(timeShift, newTimeData.timeRange.to, true),
+      raw: newTimeData.timeRange.raw,
     };
     };
   }
   }
 
 

+ 4 - 1
public/app/features/panel/panel_ctrl.ts

@@ -5,6 +5,7 @@ import Remarkable from 'remarkable';
 import config from 'app/core/config';
 import config from 'app/core/config';
 import { profiler } from 'app/core/core';
 import { profiler } from 'app/core/core';
 import { Emitter } from 'app/core/core';
 import { Emitter } from 'app/core/core';
+import getFactors from 'app/core/utils/factors';
 import {
 import {
   duplicatePanel,
   duplicatePanel,
   copyPanel as copyPanelUtil,
   copyPanel as copyPanelUtil,
@@ -12,7 +13,7 @@ import {
   sharePanel as sharePanelUtil,
   sharePanel as sharePanelUtil,
 } from 'app/features/dashboard/utils/panel';
 } from 'app/features/dashboard/utils/panel';
 
 
-import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, PANEL_HEADER_HEIGHT, PANEL_BORDER } from 'app/core/constants';
+import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT, PANEL_HEADER_HEIGHT, PANEL_BORDER } from 'app/core/constants';
 
 
 export class PanelCtrl {
 export class PanelCtrl {
   panel: any;
   panel: any;
@@ -32,6 +33,7 @@ export class PanelCtrl {
   events: Emitter;
   events: Emitter;
   timing: any;
   timing: any;
   loading: boolean;
   loading: boolean;
+  maxPanelsPerRowOptions: number[];
 
 
   constructor($scope, $injector) {
   constructor($scope, $injector) {
     this.$injector = $injector;
     this.$injector = $injector;
@@ -92,6 +94,7 @@ export class PanelCtrl {
     if (!this.editModeInitiated) {
     if (!this.editModeInitiated) {
       this.editModeInitiated = true;
       this.editModeInitiated = true;
       this.events.emit('init-edit-mode', null);
       this.events.emit('init-edit-mode', null);
+      this.maxPanelsPerRowOptions = getFactors(GRID_COLUMN_COUNT);
     }
     }
   }
   }
 
 

+ 7 - 2
public/app/features/panel/partials/general_tab.html

@@ -32,12 +32,17 @@
         </select>
         </select>
       </div>
       </div>
       <div class="gf-form" ng-show="ctrl.panel.repeat && ctrl.panel.repeatDirection == 'h'">
       <div class="gf-form" ng-show="ctrl.panel.repeat && ctrl.panel.repeatDirection == 'h'">
-        <span class="gf-form-label width-9">Min width</span>
-        <select class="gf-form-input" ng-model="ctrl.panel.minSpan" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]">
+        <span class="gf-form-label width-9">Max per row</span>
+        <select class="gf-form-input" ng-model="ctrl.panel.maxPerRow" ng-options="f for f in [2,3,4,6,12,24]">
           <option value=""></option>
           <option value=""></option>
         </select>
         </select>
       </div>
       </div>
+      <div class="gf-form-hint">
+        <div class="gf-form-hint-text muted">
+          Note: You may need to change the variable selection to see this in action.
+        </div>
       </div>
       </div>
+    </div>
   </div>
   </div>
 </div>
 </div>
 
 

+ 1 - 2
public/app/plugins/panel/gauge/MappingRow.tsx

@@ -1,8 +1,7 @@
 import React, { PureComponent } from 'react';
 import React, { PureComponent } from 'react';
-import { MappingType, RangeMap, ValueMap } from '@grafana/ui';
+import { MappingType, RangeMap, Select, ValueMap } from '@grafana/ui';
 
 
 import { Label } from 'app/core/components/Label/Label';
 import { Label } from 'app/core/components/Label/Label';
-import { Select } from 'app/core/components/Select/Select';
 
 
 interface Props {
 interface Props {
   mapping: ValueMap | RangeMap;
   mapping: ValueMap | RangeMap;

+ 1 - 1
public/app/plugins/panel/gauge/ValueOptions.tsx

@@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
 import { GaugeOptions, PanelOptionsProps } from '@grafana/ui';
 import { GaugeOptions, PanelOptionsProps } from '@grafana/ui';
 
 
 import { Label } from 'app/core/components/Label/Label';
 import { Label } from 'app/core/components/Label/Label';
-import Select from 'app/core/components/Select/Select';
+import { Select} from '@grafana/ui';
 import UnitPicker from 'app/core/components/Select/UnitPicker';
 import UnitPicker from 'app/core/components/Select/UnitPicker';
 
 
 const statOptions = [
 const statOptions = [

+ 1 - 1
public/dashboards/home.json

@@ -65,7 +65,7 @@
     }
     }
   ],
   ],
   "rows": [],
   "rows": [],
-  "schemaVersion": 16,
+  "schemaVersion": 17,
   "style": "dark",
   "style": "dark",
   "tags": [],
   "tags": [],
   "templating": {
   "templating": {

+ 4 - 5
public/sass/_grafana.scss

@@ -1,4 +1,4 @@
-// DEPENDENCIES
+ // DEPENDENCIES
 @import '../../node_modules/react-table/react-table.css';
 @import '../../node_modules/react-table/react-table.css';
 
 
 // VENDOR
 // VENDOR
@@ -38,9 +38,6 @@
 @import 'layout/lists';
 @import 'layout/lists';
 @import 'layout/page';
 @import 'layout/page';
 
 
-// LOAD @grafana/ui components
-@import '../../packages/grafana-ui/src/index';
-
 // COMPONENTS
 // COMPONENTS
 @import 'components/scrollbar';
 @import 'components/scrollbar';
 @import 'components/cards';
 @import 'components/cards';
@@ -97,7 +94,6 @@
 @import 'components/page_header';
 @import 'components/page_header';
 @import 'components/dashboard_settings';
 @import 'components/dashboard_settings';
 @import 'components/empty_list_cta';
 @import 'components/empty_list_cta';
-@import 'components/form_select_box';
 @import 'components/panel_editor';
 @import 'components/panel_editor';
 @import 'components/toolbar';
 @import 'components/toolbar';
 @import 'components/add_data_source.scss';
 @import 'components/add_data_source.scss';
@@ -106,6 +102,9 @@
 @import 'components/value-mappings';
 @import 'components/value-mappings';
 @import 'components/popover-box';
 @import 'components/popover-box';
 
 
+// LOAD @grafana/ui components
+@import '../../packages/grafana-ui/src/index';
+
 // PAGES
 // PAGES
 @import 'pages/login';
 @import 'pages/login';
 @import 'pages/dashboard';
 @import 'pages/dashboard';

+ 2 - 2
yarn.lock

@@ -1105,7 +1105,7 @@
   dependencies:
   dependencies:
     "@types/react" "*"
     "@types/react" "*"
 
 
-"@types/react@*", "@types/react@^16.1.0", "@types/react@^16.7.6":
+"@types/react@*", "@types/react@16.7.6", "@types/react@^16.1.0", "@types/react@^16.7.6":
   version "16.7.6"
   version "16.7.6"
   resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.6.tgz#80e4bab0d0731ad3ae51f320c4b08bdca5f03040"
   resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.6.tgz#80e4bab0d0731ad3ae51f320c4b08bdca5f03040"
   integrity sha512-QBUfzftr/8eg/q3ZRgf/GaDP6rTYc7ZNem+g4oZM38C9vXyV8AWRWaTQuW5yCoZTsfHrN7b3DeEiUnqH9SrnpA==
   integrity sha512-QBUfzftr/8eg/q3ZRgf/GaDP6rTYc7ZNem+g4oZM38C9vXyV8AWRWaTQuW5yCoZTsfHrN7b3DeEiUnqH9SrnpA==
@@ -3185,7 +3185,7 @@ caniuse-api@^1.5.2:
     lodash.memoize "^4.1.2"
     lodash.memoize "^4.1.2"
     lodash.uniq "^4.5.0"
     lodash.uniq "^4.5.0"
 
 
-caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
+caniuse-db@1.0.30000772, caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
   version "1.0.30000772"
   version "1.0.30000772"
   resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000772.tgz#51aae891768286eade4a3d8319ea76d6a01b512b"
   resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000772.tgz#51aae891768286eade4a3d8319ea76d6a01b512b"
   integrity sha1-UarokXaChureSj2DGep21qAbUSs=
   integrity sha1-UarokXaChureSj2DGep21qAbUSs=