From 104292df636fc44399a811094b92793f96ba8190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 14 Dec 2018 11:23:05 +0100 Subject: [PATCH 1/2] wip: unifying select components --- public/app/core/components/Picker/Select.tsx | 142 ++++++++++-------- .../SharedPreferences/SharedPreferences.tsx | 26 ++-- .../app/plugins/panel/gauge/ValueOptions.tsx | 30 ++-- 3 files changed, 107 insertions(+), 91 deletions(-) diff --git a/public/app/core/components/Picker/Select.tsx b/public/app/core/components/Picker/Select.tsx index b8704893cdb..40ee1af2148 100644 --- a/public/app/core/components/Picker/Select.tsx +++ b/public/app/core/components/Picker/Select.tsx @@ -1,60 +1,82 @@ -// import React, { PureComponent } from 'react'; -// import Select as ReactSelect from 'react-select'; -// import DescriptionOption from './DescriptionOption'; -// import IndicatorsContainer from './IndicatorsContainer'; -// import ResetStyles from './ResetStyles'; -// -// export interface OptionType { -// label: string; -// value: string; -// } -// -// interface Props { -// defaultValue?: any; -// getOptionLabel: (item: T) => string; -// getOptionValue: (item: T) => string; -// onChange: (item: T) => {} | void; -// options: T[]; -// placeholder?: string; -// width?: number; -// value: T; -// className?: string; -// } -// -// export class Select extends PureComponent> { -// static defaultProps = { -// width: null, -// className: '', -// } -// -// render() { -// const { defaultValue, getOptionLabel, getOptionValue, onSelected, options, placeholder, width, value, className } = this.props; -// let widthClass = ''; -// if (width) { -// widthClass = 'width-'+width; -// } -// -// return ( -// -// ); -// } -// } -// -// export default Select; +// Libraries +import classNames from 'classnames'; +import React, { PureComponent } from 'react'; +import { default as ReactSelect } from 'react-select'; + +// Components +import DescriptionOption from './DescriptionOption'; +import IndicatorsContainer from './IndicatorsContainer'; +import ResetStyles from './ResetStyles'; + +export interface SelectOptionItem { + label?: string; + value?: string; + imgUrl?: string; + description?: string; + [key: string]: any; +} + +interface Props { + defaultValue?: any; + getOptionLabel?: (item: SelectOptionItem) => string; + getOptionValue?: (item: SelectOptionItem) => string; + onChange: (item: SelectOptionItem) => {} | void; + options: SelectOptionItem[]; + placeholder?: string; + width?: number; + value: SelectOptionItem; + className?: string; + components: object; +} + +export class Select extends PureComponent { + static defaultProps = { + width: null, + className: '', + components: {}, + }; + + render() { + const { + defaultValue, + getOptionLabel, + getOptionValue, + onChange, + options, + placeholder, + width, + value, + className, + } = this.props; + + let widthClass = ''; + if (width) { + widthClass = 'width-' + width; + } + + const selectClassNames = classNames('gf-form-input', 'gf-form-input--form-dropdown', widthClass, className); + + return ( + + ); + } +} + +export default Select; diff --git a/public/app/core/components/SharedPreferences/SharedPreferences.tsx b/public/app/core/components/SharedPreferences/SharedPreferences.tsx index 098cbe16ab8..1ab3f048a9b 100644 --- a/public/app/core/components/SharedPreferences/SharedPreferences.tsx +++ b/public/app/core/components/SharedPreferences/SharedPreferences.tsx @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react'; import { Label } from 'app/core/components/Label/Label'; -import SimplePicker from 'app/core/components/Picker/SimplePicker'; +import Select from 'app/core/components/Picker/Select'; import { getBackendSrv, BackendSrv } from 'app/core/services/backend_srv'; import { DashboardSearchHit } from 'app/types'; @@ -17,12 +17,12 @@ export interface State { dashboards: DashboardSearchHit[]; } -const themes = [{ value: '', text: 'Default' }, { value: 'dark', text: 'Dark' }, { value: 'light', text: 'Light' }]; +const themes = [{ value: '', label: 'Default' }, { value: 'dark', label: 'Dark' }, { value: 'light', label: 'Light' }]; const timezones = [ - { value: '', text: 'Default' }, - { value: 'browser', text: 'Local browser time' }, - { value: 'utc', text: 'UTC' }, + { value: '', label: 'Default' }, + { value: 'browser', label: 'Local browser time' }, + { value: 'utc', label: 'UTC' }, ]; export class SharedPreferences extends PureComponent { @@ -91,12 +91,10 @@ export class SharedPreferences extends PureComponent {

Preferences

UI Theme - item.value === theme)} options={themes} - getOptionValue={i => i.value} - getOptionLabel={i => i.text} - onSelected={theme => this.onThemeChanged(theme.value)} + onChange={theme => this.onThemeChanged(theme.value)} width={20} />
@@ -107,11 +105,11 @@ export class SharedPreferences extends PureComponent { > Home Dashboard - dashboard.id === homeDashboardId)} getOptionValue={i => i.id} getOptionLabel={i => i.title} - onSelected={(dashboard: DashboardSearchHit) => this.onHomeDashboardChanged(dashboard.id)} + onChange={(dashboard: DashboardSearchHit) => this.onHomeDashboardChanged(dashboard.id)} options={dashboards} placeholder="Chose default dashboard" width={20} @@ -119,11 +117,9 @@ export class SharedPreferences extends PureComponent {
- item.value === timezone)} - getOptionValue={i => i.value} - getOptionLabel={i => i.text} - onSelected={timezone => this.onTimeZoneChanged(timezone.value)} + onChange={timezone => this.onTimeZoneChanged(timezone.value)} options={timezones} width={20} /> diff --git a/public/app/plugins/panel/gauge/ValueOptions.tsx b/public/app/plugins/panel/gauge/ValueOptions.tsx index e3052f10861..7cb1c90d347 100644 --- a/public/app/plugins/panel/gauge/ValueOptions.tsx +++ b/public/app/plugins/panel/gauge/ValueOptions.tsx @@ -1,21 +1,21 @@ import React, { PureComponent } from 'react'; import { Label } from 'app/core/components/Label/Label'; -import SimplePicker from 'app/core/components/Picker/SimplePicker'; +import Select from 'app/core/components/Picker/Select'; import UnitPicker from 'app/core/components/Picker/Unit/UnitPicker'; import { OptionModuleProps } from './module'; const statOptions = [ - { value: 'min', text: 'Min' }, - { value: 'max', text: 'Max' }, - { value: 'avg', text: 'Average' }, - { value: 'current', text: 'Current' }, - { value: 'total', text: 'Total' }, - { value: 'name', text: 'Name' }, - { value: 'first', text: 'First' }, - { value: 'delta', text: 'Delta' }, - { value: 'diff', text: 'Difference' }, - { value: 'range', text: 'Range' }, - { value: 'last_time', text: 'Time of last point' }, + { value: 'min', label: 'Min' }, + { value: 'max', label: 'Max' }, + { value: 'avg', label: 'Average' }, + { value: 'current', label: 'Current' }, + { value: 'total', label: 'Total' }, + { value: 'name', label: 'Name' }, + { value: 'first', label: 'First' }, + { value: 'delta', label: 'Delta' }, + { value: 'diff', label: 'Difference' }, + { value: 'range', label: 'Range' }, + { value: 'last_time', label: 'Time of last point' }, ]; const labelWidth = 6; @@ -43,12 +43,10 @@ export default class ValueOptions extends PureComponent {
Value
- i.text} - getOptionValue={i => i.value} - onSelected={this.onStatChange} + onChange={this.onStatChange} value={statOptions.find(option => option.value === stat)} />
From 58cc2e34d68cdf9691e840876a93bb722373c53c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 14 Dec 2018 12:38:36 +0100 Subject: [PATCH 2/2] User picker using common select componnet --- .../components/Picker/NoOptionsMessage.tsx | 12 +-- public/app/core/components/Picker/Select.tsx | 87 +++++++++++++++++-- .../app/core/components/Picker/UserPicker.tsx | 27 +++--- public/app/features/panel/panel_header.ts | 4 +- 4 files changed, 100 insertions(+), 30 deletions(-) diff --git a/public/app/core/components/Picker/NoOptionsMessage.tsx b/public/app/core/components/Picker/NoOptionsMessage.tsx index 1d2ad4a179e..3626684df22 100644 --- a/public/app/core/components/Picker/NoOptionsMessage.tsx +++ b/public/app/core/components/Picker/NoOptionsMessage.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React from 'react'; import { components } from 'react-select'; import { OptionProps } from 'react-select/lib/components/Option'; @@ -6,13 +6,15 @@ export interface Props { children: Element; } -export const PickerOption = (props: OptionProps) => { - const { children, className } = props; +export const NoOptionsMessage = (props: OptionProps) => { + const { children } = props; return ( -
{children}
+
+
{children}
+
); }; -export default PickerOption; +export default NoOptionsMessage; diff --git a/public/app/core/components/Picker/Select.tsx b/public/app/core/components/Picker/Select.tsx index 40ee1af2148..5b7cad5b921 100644 --- a/public/app/core/components/Picker/Select.tsx +++ b/public/app/core/components/Picker/Select.tsx @@ -2,10 +2,12 @@ import classNames from 'classnames'; import React, { PureComponent } from 'react'; import { default as ReactSelect } from 'react-select'; +import { default as ReactAsyncSelect } from 'react-select/lib/Async'; // Components -import DescriptionOption from './DescriptionOption'; +import { Option, SingleValue } from './PickerOption'; import IndicatorsContainer from './IndicatorsContainer'; +import NoOptionsMessage from './NoOptionsMessage'; import ResetStyles from './ResetStyles'; export interface SelectOptionItem { @@ -16,20 +18,31 @@ export interface SelectOptionItem { [key: string]: any; } -interface Props { +interface CommonProps { defaultValue?: any; getOptionLabel?: (item: SelectOptionItem) => string; getOptionValue?: (item: SelectOptionItem) => string; onChange: (item: SelectOptionItem) => {} | void; - options: SelectOptionItem[]; placeholder?: string; width?: number; - value: SelectOptionItem; + value?: SelectOptionItem; className?: string; components: object; } -export class Select extends PureComponent { +interface SelectProps { + options: SelectOptionItem[]; +} + +interface AsyncProps { + defaultOptions: boolean; + loadOptions: (query: string) => Promise; + isLoading: boolean; + loadingMessage?: () => string; + noOptionsMessage?: () => string; +} + +export class Select extends PureComponent { static defaultProps = { width: null, className: '', @@ -61,7 +74,8 @@ export class Select extends PureComponent { classNamePrefix="gf-form-select-box" className={selectClassNames} components={{ - Option: DescriptionOption, + Option, + SingleValue, IndicatorsContainer, }} defaultValue={defaultValue} @@ -79,4 +93,65 @@ export class Select extends PureComponent { } } +export class AsyncSelect extends PureComponent { + static defaultProps = { + width: null, + className: '', + components: {}, + loadingMessage: () => 'Loading...', + }; + + render() { + const { + defaultValue, + getOptionLabel, + getOptionValue, + onChange, + placeholder, + width, + value, + className, + loadOptions, + defaultOptions, + isLoading, + loadingMessage, + noOptionsMessage, + } = this.props; + + let widthClass = ''; + if (width) { + widthClass = 'width-' + width; + } + + const selectClassNames = classNames('gf-form-input', 'gf-form-input--form-dropdown', widthClass, className); + + return ( + + ); + } +} + export default Select; diff --git a/public/app/core/components/Picker/UserPicker.tsx b/public/app/core/components/Picker/UserPicker.tsx index 01dbb1fd3ea..97bdc230173 100644 --- a/public/app/core/components/Picker/UserPicker.tsx +++ b/public/app/core/components/Picker/UserPicker.tsx @@ -1,12 +1,15 @@ +// Libraries import React, { Component } from 'react'; -import AsyncSelect from 'react-select/lib/Async'; -import PickerOption from './PickerOption'; + +// Components +import { AsyncSelect } from 'app/core/components/Picker/Select'; + +// Utils & Services import { debounce } from 'lodash'; import { getBackendSrv } from 'app/core/services/backend_srv'; + +// Types import { User } from 'app/types'; -import ResetStyles from './ResetStyles'; -import IndicatorsContainer from './IndicatorsContainer'; -import NoOptionsMessage from './NoOptionsMessage'; export interface Props { onSelected: (user: User) => void; @@ -40,6 +43,7 @@ export class UserPicker extends Component { .then(result => { return result.map(user => ({ id: user.userId, + value: user.userId, label: user.login === user.email ? user.login : `${user.login} - ${user.email}`, imgUrl: user.avatarUrl, login: user.login, @@ -57,24 +61,13 @@ export class UserPicker extends Component { return (
'Loading...'} noOptionsMessage={() => 'No users found'} - getOptionValue={i => i.id} - getOptionLabel={i => i.label} />
); diff --git a/public/app/features/panel/panel_header.ts b/public/app/features/panel/panel_header.ts index 1d29d04ad98..6a0f81a4c33 100644 --- a/public/app/features/panel/panel_header.ts +++ b/public/app/features/panel/panel_header.ts @@ -111,11 +111,11 @@ function panelHeader($compile) { */ function togglePanelStackPosition() { const menuOpenClass = 'dropdown-menu-open'; - const panelGridClass = '.react-grid-item.panel'; + const panelGridClass = '.react-grid-item'; let panelElem = elem .find('[data-toggle=dropdown]') - .parentsUntil('.panel') + .parentsUntil(panelGridClass) .parent(); const menuElem = elem.find('[data-toggle=dropdown]').parent(); panelElem = panelElem && panelElem.length ? panelElem[0] : undefined;