Jelajahi Sumber

wip: add option group component

Erik Sundell 7 tahun lalu
induk
melakukan
fe4c77a8a4

+ 53 - 0
public/app/plugins/datasource/stackdriver/components/OptionGroupPicker.tsx

@@ -0,0 +1,53 @@
+import React from 'react';
+import Select from 'react-select';
+import _ from 'lodash';
+
+import GroupHeading from 'app/core/components/Picker/GroupHeading';
+import DescriptionOption from 'app/core/components/Picker/DescriptionOption';
+import IndicatorsContainer from 'app/core/components/Picker/IndicatorsContainer';
+import ResetStyles from 'app/core/components/Picker/ResetStyles';
+import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage';
+
+export interface Props {
+  onChange: (value: string) => void;
+  groups: any[];
+  searchable: boolean;
+  selected: string;
+  placeholder?: string;
+  className?: string;
+}
+
+export class OptionGroupPicker extends React.Component<Props, any> {
+  constructor(props) {
+    super(props);
+  }
+
+  render() {
+    const { onChange, groups, selected, placeholder, className, searchable } = this.props;
+    const options = _.flatten(groups.map(o => o.options));
+    const selectedOption = options.find(option => option.value === selected);
+
+    return (
+      <Select
+        placeholder={placeholder}
+        classNamePrefix={`gf-form-select-box`}
+        className={className}
+        options={groups}
+        components={{
+          GroupHeading,
+          Option: DescriptionOption,
+          IndicatorsContainer,
+          NoOptionsMessage,
+        }}
+        styles={ResetStyles}
+        isSearchable={searchable}
+        maxMenuHeight={50}
+        onChange={option => onChange(option.value)}
+        getOptionValue={i => i.value}
+        getOptionLabel={i => i.label}
+        value={selectedOption}
+        noOptionsMessage={() => 'No metrics found'}
+      />
+    );
+  }
+}

+ 9 - 6
public/app/plugins/datasource/stackdriver/components/OptionPicker.tsx

@@ -1,19 +1,20 @@
 import React from 'react';
-// import SimplePicker from 'app/core/components/Picker/SimplePicker';
 import Select from 'react-select';
-// import DescriptionPicker from 'app/core/components/Picker/DescriptionPicker';
+import _ from 'lodash';
+
 import DescriptionOption from 'app/core/components/Picker/DescriptionOption';
 import IndicatorsContainer from 'app/core/components/Picker/IndicatorsContainer';
 import ResetStyles from 'app/core/components/Picker/ResetStyles';
 import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage';
-import _ from 'lodash';
 
 export interface Props {
   onChange: (value: string) => void;
   options: any[];
+  searchable: boolean;
   selected: string;
   placeholder?: string;
   className?: string;
+  groups?: boolean;
 }
 
 export class OptionPicker extends React.Component<Props, any> {
@@ -22,8 +23,9 @@ export class OptionPicker extends React.Component<Props, any> {
   }
 
   render() {
-    const { onChange, options, selected, placeholder, className } = this.props;
-    const selectedOption = options.find(metric => metric.value === selected);
+    const { onChange, options, selected, placeholder, className, searchable } = this.props;
+    const selectedOption = options.find(option => option.value === selected);
+
     return (
       <Select
         placeholder={placeholder}
@@ -36,7 +38,8 @@ export class OptionPicker extends React.Component<Props, any> {
           NoOptionsMessage,
         }}
         styles={ResetStyles}
-        isDisabled={false}
+        isSearchable={searchable}
+        maxMenuHeight={50}
         onChange={option => onChange(option.value)}
         getOptionValue={i => i.value}
         getOptionLabel={i => i.label}

+ 8 - 6
public/app/plugins/datasource/stackdriver/partials/query.filter.html

@@ -5,8 +5,9 @@
       onChange="ctrl.handleServiceChange"
       selected="ctrl.service"
       options="ctrl.services"
-      placeholder="ctrl.defaultDropdownValue"
-      className="width-12"
+      searchable="false"
+      placeholder="ctrl.defaultServiceValue"
+      className="&quot;width-15&quot;"
     ></option-picker>
   </div>
   <div class="gf-form gf-form--grow"><div class="gf-form-label gf-form-label--grow"></div></div>
@@ -14,13 +15,14 @@
 <div class="gf-form-inline">
   <div class="gf-form">
     <span class="gf-form-label width-9 query-keyword">Metric</span>
-    <option-picker
+    <option-group-picker
       onChange="ctrl.handleMetricTypeChange"
       selected="ctrl.metricType"
-      options="ctrl.metrics"
+      groups="ctrl.insertTemplateVariables(ctrl.defaultServiceValue !== ctrl.service ? ctrl.metrics : ctrl.metricGroups)"
+      searchable="true"
       placeholder="ctrl.defaultDropdownValue"
-      className="width-12"
-    ></option-picker>
+      className="&quot;width-15&quot;"
+    ></option-group-picker>
   </div>
   <div class="gf-form gf-form--grow"><div class="gf-form-label gf-form-label--grow"></div></div>
 </div>

+ 10 - 0
public/app/plugins/datasource/stackdriver/query_ctrl.ts

@@ -3,6 +3,7 @@ import { QueryCtrl } from 'app/plugins/sdk';
 import './query_aggregation_ctrl';
 import './query_filter_ctrl';
 import { OptionPicker } from './components/OptionPicker';
+import { OptionGroupPicker } from './components/OptionGroupPicker';
 import { react2AngularDirective } from 'app/core/utils/react2angular';
 
 export interface QueryMeta {
@@ -70,6 +71,15 @@ export class StackdriverQueryCtrl extends QueryCtrl {
       'options',
       'onChange',
       'selected',
+      'searchable',
+      'className',
+      'placeholder',
+    ]);
+    react2AngularDirective('optionGroupPicker', OptionGroupPicker, [
+      'groups',
+      'onChange',
+      'selected',
+      'searchable',
       'className',
       'placeholder',
     ]);

+ 37 - 2
public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts

@@ -36,6 +36,7 @@ export class StackdriverFilterCtrl {
   metricType: string;
   metricDescriptors: any[];
   metrics: any[];
+  metricGroups: any[];
   services: any[];
   groupBySegments: any[];
   filterSegments: FilterSegments;
@@ -52,6 +53,7 @@ export class StackdriverFilterCtrl {
 
     this.metricDescriptors = [];
     this.metrics = [];
+    this.metricGroups = [];
     this.services = [];
 
     this.getCurrentProject()
@@ -106,6 +108,7 @@ export class StackdriverFilterCtrl {
       this.metricDescriptors = await this.datasource.getMetricTypes(this.target.defaultProject);
       this.services = this.getServicesList();
       this.metrics = this.getMetricsList();
+      this.metricGroups = this.getMetricGroups();
       return this.metricDescriptors;
     } else {
       return [];
@@ -113,11 +116,11 @@ export class StackdriverFilterCtrl {
   }
 
   getServicesList() {
-    const defaultValue = { value: this.$scope.defaultServiceValue, text: this.$scope.defaultServiceValue };
+    const defaultValue = { value: this.$scope.defaultServiceValue, label: this.$scope.defaultServiceValue };
     const services = this.metricDescriptors.map(m => {
       return {
         value: m.service,
-        label: m.serviceShortName,
+        label: _.startCase(m.serviceShortName),
       };
     });
 
@@ -128,6 +131,37 @@ export class StackdriverFilterCtrl {
     return services.length > 0 ? [defaultValue, ..._.uniqBy(services, 'value')] : [];
   }
 
+  getMetricGroups() {
+    return this.metrics.reduce((acc, curr) => {
+      const group = acc.find(group => group.service === curr.service);
+      if (group) {
+        group.options = [...group.options, { value: curr.value, label: curr.label }];
+      } else {
+        acc = [
+          ...acc,
+          {
+            label: _.startCase(curr.serviceShortName),
+            service: curr.service,
+            options: [{ value: curr.value, label: curr.label }],
+          },
+        ];
+      }
+      return acc;
+    }, []);
+  }
+
+  insertTemplateVariables(options) {
+    const templateVariables = {
+      label: 'Template Variables',
+      options: this.templateSrv.variables.map(v => ({
+        label: `$${v.name}`,
+        value: `$${v.name}`,
+        description: `$${v.definition}`,
+      })),
+    };
+    return [templateVariables, { label: 'Metrics', options }];
+  }
+
   getMetricsList() {
     const metrics = this.metricDescriptors.map(m => {
       return {
@@ -178,6 +212,7 @@ export class StackdriverFilterCtrl {
   handleServiceChange(service) {
     this.target.service = this.service = service;
     this.metrics = this.getMetricsList();
+    this.metricGroups = this.getMetricGroups();
     this.setMetricType();
     this.getLabels();
     if (!this.metrics.find(m => m.value === this.target.metricType)) {