Procházet zdrojové kódy

Merge branch 'unit-picker' into gauge-value-options

Peter Holmberg před 7 roky
rodič
revize
5722773467

+ 43 - 0
public/app/core/components/Picker/Unit/UnitGroup.tsx

@@ -0,0 +1,43 @@
+import React, { PureComponent } from 'react';
+import { GroupProps } from 'react-select/lib/components/Group';
+
+interface ExtendedGroupProps extends GroupProps<any> {
+  data: any;
+}
+
+interface State {
+  expanded: boolean;
+}
+
+export default class UnitGroup extends PureComponent<ExtendedGroupProps, State> {
+  state = {
+    expanded: false,
+  };
+
+  componentDidUpdate(nextProps) {
+    if (nextProps.selectProps.inputValue !== '') {
+      this.setState({ expanded: true });
+    }
+  }
+
+  onToggleChildren = () => {
+    this.setState(prevState => ({
+      expanded: !prevState.expanded,
+    }));
+  };
+
+  render() {
+    const { children, label } = this.props;
+    const { expanded } = this.state;
+
+    return (
+      <div className="width-21 unit-picker-group" style={{ marginBottom: '5px' }}>
+        <div className="unit-picker-group-item" onClick={this.onToggleChildren}>
+          <span style={{ textTransform: 'capitalize' }}>{label}</span>
+          <i className={`fa ${expanded ? 'fa-minus' : 'fa-plus'}`} />{' '}
+        </div>
+        {expanded && children}
+      </div>
+    );
+  }
+}

+ 22 - 0
public/app/core/components/Picker/Unit/UnitOption.tsx

@@ -0,0 +1,22 @@
+import React, { SFC } from 'react';
+import { components } from 'react-select';
+import { OptionProps } from 'react-select/lib/components/Option';
+
+interface ExtendedOptionProps extends OptionProps<any> {
+  data: any;
+}
+
+const UnitOption: SFC<ExtendedOptionProps> = props => {
+  const { children, isSelected, className } = props;
+
+  return (
+    <components.Option {...props}>
+      <div className={`unit-picker-option__button btn btn-link ${className}`}>
+        {isSelected && <i className="fa fa-check pull-right" aria-hidden="true" />}
+        <div className="gf-form">{children}</div>
+      </div>
+    </components.Option>
+  );
+};
+
+export default UnitOption;

+ 68 - 0
public/app/core/components/Picker/Unit/UnitPicker.tsx

@@ -0,0 +1,68 @@
+import React, { PureComponent } from 'react';
+import Select from 'react-select';
+import UnitGroup from './UnitGroup';
+import UnitOption from './UnitOption';
+import ResetStyles from '../ResetStyles';
+import kbn from '../../../utils/kbn';
+
+interface Props {
+  onSelected: (item: any) => {} | void;
+  defaultValue?: string;
+}
+
+export default class UnitPicker extends PureComponent<Props> {
+  render() {
+    const { defaultValue, onSelected } = this.props;
+
+    const unitGroups = kbn.getUnitFormats();
+
+    // Need to transform the data structure to work well with Select
+    const groupOptions = unitGroups.map(group => {
+      const options = group.submenu.map(unit => {
+        return {
+          label: unit.text,
+          value: unit.value,
+        };
+      });
+
+      return {
+        label: group.text,
+        options,
+      };
+    });
+
+    const styles = {
+      ...ResetStyles,
+      menu: () => ({
+        maxHeight: '500px',
+        overflow: 'scroll',
+      }),
+      menuList: () =>
+        ({
+          overflowY: 'auto',
+          position: 'relative',
+        } as React.CSSProperties),
+    };
+
+    const value = groupOptions.map(group => {
+      return group.options.find(option => option.value === defaultValue);
+    });
+
+    return (
+      <Select
+        classNamePrefix="gf-form-select-box"
+        className="width-20 gf-form-input--form-dropdown"
+        defaultValue={value}
+        isSearchable={true}
+        options={groupOptions}
+        placeholder="Choose"
+        onChange={onSelected}
+        components={{
+          Group: UnitGroup,
+          Option: UnitOption,
+        }}
+        styles={styles}
+      />
+    );
+  }
+}

+ 2 - 1
public/app/core/utils/kbn.ts

@@ -405,7 +405,8 @@ kbn.valueFormats.percentunit = (size, decimals) => {
 };
 
 /* Formats the value to hex. Uses float if specified decimals are not 0.
- * There are two options, one with 0x, and one without */
+ * There are two submenu
+ * , one with 0x, and one without */
 
 kbn.valueFormats.hex = (value, decimals) => {
   if (value == null) {

+ 1 - 0
public/app/features/dashboard/dashgrid/DataPanel.tsx

@@ -134,6 +134,7 @@ export class DataPanel extends Component<Props, State> {
   render() {
     const { queries } = this.props;
     const { response, loading, isFirstLoad } = this.state;
+
     const timeSeries = response.data;
 
     if (isFirstLoad && loading === LoadingState.Loading) {

+ 0 - 2
public/app/features/dashboard/dashgrid/PanelChrome.tsx

@@ -70,7 +70,6 @@ export class PanelChrome extends PureComponent<Props, State> {
   };
 
   onRender = () => {
-    console.log('onRender');
     this.setState({
       renderCounter: this.state.renderCounter + 1,
     });
@@ -87,7 +86,6 @@ export class PanelChrome extends PureComponent<Props, State> {
     const { datasource, targets } = panel;
     const PanelComponent = this.props.component;
 
-    console.log('panelChrome render');
     return (
       <AutoSizer>
         {({ width, height }) => {

+ 9 - 7
public/app/plugins/datasource/elasticsearch/config_ctrl.ts

@@ -28,13 +28,15 @@ export class ElasticConfigCtrl {
   ];
 
   indexPatternTypeChanged() {
-    if (!this.current.database ||
-        this.current.database.length === 0 ||
-        this.current.database.startsWith('[logstash-]')) {
-        const def = _.find(this.indexPatternTypes, {
-          value: this.current.jsonData.interval,
-        });
-        this.current.database = def.example || 'es-index-name';
+    if (
+      !this.current.database ||
+      this.current.database.length === 0 ||
+      this.current.database.startsWith('[logstash-]')
+    ) {
+      const def = _.find(this.indexPatternTypes, {
+        value: this.current.jsonData.interval,
+      });
+      this.current.database = def.example || 'es-index-name';
     }
   }
 }

+ 15 - 7
public/app/plugins/panel/gauge/module.tsx

@@ -1,12 +1,14 @@
 import React, { PureComponent } from 'react';
+import { Label } from 'app/core/components/Label/Label';
+import SimplePicker from 'app/core/components/Picker/SimplePicker';
+import UnitPicker from 'app/core/components/Picker/Unit/UnitPicker';
 import Gauge from 'app/viz/Gauge';
 import { NullValueMode, PanelOptionsProps, PanelProps } from 'app/types';
 import { getTimeSeriesVMs } from 'app/viz/state/timeSeries';
-import { Label } from '../../../core/components/Label/Label';
-import SimplePicker from '../../../core/components/Picker/SimplePicker';
 
 export interface Options {
   stat: { value: string; text: string };
+  unit: { label: string; value: string };
 }
 
 interface Props extends PanelProps<Options> {}
@@ -25,7 +27,7 @@ const statOptions = [
   { value: 'last_time', text: 'Time of last point' },
 ];
 
-export class GaugePanel extends PureComponent<Props> {
+class GaugePanel extends PureComponent<Props> {
   render() {
     const { timeSeries, width, height } = this.props;
 
@@ -39,13 +41,15 @@ export class GaugePanel extends PureComponent<Props> {
 }
 
 class GaugeOptions extends PureComponent<PanelOptionsProps<Options>> {
+  onUnitChange = value => {
+    this.props.onChange({ ...this.props.options, unit: value });
+  };
+
   onStatChange = value => {
     this.props.onChange({ ...this.props.options, stat: value });
   };
 
   render() {
-    const { stat } = this.props.options;
-
     return (
       <div>
         <div className="section gf-form-group">
@@ -53,15 +57,19 @@ class GaugeOptions extends PureComponent<PanelOptionsProps<Options>> {
           <div className="gf-form-inline">
             <Label width={5}>Stat</Label>
             <SimplePicker
-              defaultValue={statOptions.find(option => option.value === stat.value)}
+              defaultValue={statOptions.find(option => option.value === this.props.options.stat.value)}
               width={11}
               options={statOptions}
               getOptionLabel={i => i.text}
               getOptionValue={i => i.value}
               onSelected={this.onStatChange}
-              value={stat}
+              value={this.props.options.stat}
             />
           </div>
+          <div className="gf-form-inline">
+            <Label width={5}>Unit</Label>
+            <UnitPicker defaultValue={this.props.options.unit.value} onSelected={value => this.onUnitChange(value)} />
+          </div>
         </div>
       </div>
     );

+ 10 - 1
public/app/viz/Gauge.tsx

@@ -2,6 +2,7 @@ import React, { PureComponent } from 'react';
 import $ from 'jquery';
 import { TimeSeriesVMs } from 'app/types';
 import config from '../core/config';
+import kbn from '../core/utils/kbn';
 
 interface Props {
   timeSeries: TimeSeriesVMs;
@@ -10,6 +11,7 @@ interface Props {
   showThresholdMarkers?: boolean;
   thresholds?: number[];
   showThresholdLables?: boolean;
+  unit: { label: string; value: string };
   width: number;
   height: number;
   stat?: { value: string; text: string };
@@ -37,6 +39,13 @@ export class Gauge extends PureComponent<Props> {
     this.draw();
   }
 
+  formatValue(value) {
+    const { unit } = this.props;
+
+    const formatFunc = kbn.valueFormats[unit.value];
+    return formatFunc(value);
+  }
+
   draw() {
     const {
       timeSeries,
@@ -96,7 +105,7 @@ export class Gauge extends PureComponent<Props> {
           value: {
             color: fontColor,
             formatter: () => {
-              return Math.round(timeSeries[0].stats[stat.value]);
+              return this.formatValue(timeSeries[0].stats[stat.value]);
             },
             font: {
               size: fontSize,

+ 1 - 0
public/sass/_grafana.scss

@@ -102,6 +102,7 @@
 @import 'components/delete_button';
 @import 'components/add_data_source.scss';
 @import 'components/page_loader';
+@import 'components/unit-picker';
 
 // PAGES
 @import 'pages/login';

+ 24 - 0
public/sass/components/_unit-picker.scss

@@ -0,0 +1,24 @@
+.unit-picker-option {
+  position: relative;
+  width: 100%;
+  display: block;
+  border-radius: 0;
+  white-space: normal;
+
+  i.fa-check {
+    padding-left: 2px;
+  }
+}
+
+.unit-picker-group {
+  margin-bottom: 5px;
+}
+
+.unit-picker-group-item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 10px;
+  font-size: 14px;
+  border-bottom: 1px solid #555;
+}

+ 1 - 9
yarn.lock

@@ -397,15 +397,7 @@
   dependencies:
     "@types/react" "*"
 
-"@types/react@*":
-  version "16.4.16"
-  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.4.16.tgz#99f91b1200ae8c2062030402006d3b3c3a177043"
-  integrity sha512-lxyoipLWweAnLnSsV4Ho2NAZTKKmxeYgkTQ6PaDiPDU9JJBUY2zJVVGiK1smzYv8+ZgbqEmcm5xM74GCpunSEA==
-  dependencies:
-    "@types/prop-types" "*"
-    csstype "^2.2.0"
-
-"@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"
   resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.6.tgz#80e4bab0d0731ad3ae51f320c4b08bdca5f03040"
   integrity sha512-QBUfzftr/8eg/q3ZRgf/GaDP6rTYc7ZNem+g4oZM38C9vXyV8AWRWaTQuW5yCoZTsfHrN7b3DeEiUnqH9SrnpA==