diff --git a/packages/grafana-ui/src/components/Segment/Segment.story.tsx b/packages/grafana-ui/src/components/Segment/Segment.story.tsx index be31b41171e..cd831418315 100644 --- a/packages/grafana-ui/src/components/Segment/Segment.story.tsx +++ b/packages/grafana-ui/src/components/Segment/Segment.story.tsx @@ -1,12 +1,6 @@ -import React from 'react'; -import { storiesOf } from '@storybook/react'; +import React, { useState } from 'react'; import { action } from '@storybook/addon-actions'; - -import { SelectableValue } from '@grafana/data'; import { Segment } from './'; -import { UseState } from '../../utils/storybook/UseState'; - -const SegmentStories = storiesOf('UI/Segment/SegmentSync', module); const AddButton = ( @@ -15,131 +9,125 @@ const AddButton = ( ); const toOption = (value: any) => ({ label: value, value: value }); - -SegmentStories.add('Array Options', () => { - const options = ['Option1', 'Option2', 'OptionWithLooongLabel', 'Option4'].map(toOption); - options[0].label = 'Option1 Label'; - return ( - - {(value, updateValue) => ( - <> -
-
- Segment Name -
- { - updateValue(item); - action('Segment value changed')(item.value); - }} - /> - ) => action('New value added')(value)} - options={options} - /> -
- - )} -
- ); -}); - +const options = ['Option1', 'Option2', 'OptionWithLooongLabel', 'Option4'].map(toOption); const groupedOptions = [ { label: 'Names', options: ['Jane', 'Tom', 'Lisa'].map(toOption) }, { label: 'Prime', options: [2, 3, 5, 7, 11, 13].map(toOption) }, ]; -SegmentStories.add('Grouped Array Options', () => { - return ( - - {(value, updateValue) => ( - <> -
-
- Segment Name -
- { - updateValue(item); - action('Segment value changed')(item.value); - }} - /> - action('New value added')(value)} - options={groupedOptions} - /> -
- - )} -
- ); -}); +const SegmentFrame = ({ options, children }: any) => ( + <> +
+
+ Segment Name +
+ {children} + action('New value added')(value)} options={options} /> +
+ +); -SegmentStories.add('With custom options allowed', () => { - const options = ['Option1', 'Option2', 'OptionWithLooongLabel', 'Option4'].map(toOption); +export const ArrayOptions = () => { + const [value, setValue] = useState(options[0]); return ( - - {(value, updateValue) => ( - <> -
-
- Segment Name -
- { - updateValue(item); - action('Segment value changed')(item.value); - }} - /> - ) => action('New value added')(value)} - options={options} - /> -
- - )} -
+ + { + setValue(item); + action('Segment value changed')(item.value); + }} + /> + ); -}); +}; -const CustomLabelComponent = ({ value: { value } }: any) =>
custom({value})
; +export default { + title: 'UI/Segment/SegmentSync', + component: ArrayOptions, +}; -SegmentStories.add('Custom Label Field', () => { +export const ArrayOptionsWithPrimitiveValue = () => { + const [value, setValue] = useState('Option1'); return ( - - {(value, setValue) => ( - <> -
-
- Segment Name -
- } - options={groupedOptions} - onChange={item => { - setValue(item); - action('Segment value changed')(item.value); - }} - /> - action('New value added')(value)} - options={groupedOptions} - /> -
- - )} -
+ + { + setValue(value); + action('Segment value changed')(value); + }} + /> + ); -}); +}; + +export const ArrayOptionsWithPlaceholder = () => { + const [value, setValue] = useState(undefined); + return ( + + { + setValue(item); + action('Segment value changed')(item.value); + }} + /> + + ); +}; + +export const GroupedArrayOptions = () => { + const [value, setValue] = useState(groupedOptions[0].options[0]); + return ( + + { + setValue(item); + action('Segment value changed')(item.value); + }} + /> + + ); +}; + +export const CustomOptionsAllowed = () => { + const [value, setValue] = useState(options[0]); + return ( + + { + setValue(value); + action('Segment value changed')(value); + }} + /> + + ); +}; + +const CustomLabelComponent = ({ value }: any) =>
custom({value})
; + +export const CustomLabelField = () => { + const [value, setValue] = useState(groupedOptions[0].options[0].value); + return ( + + } + options={groupedOptions} + onChange={({ value }) => { + setValue(value); + action('Segment value changed')(value); + }} + /> + + ); +}; diff --git a/packages/grafana-ui/src/components/Segment/Segment.tsx b/packages/grafana-ui/src/components/Segment/Segment.tsx index fbdf617ad2f..7c0c77c8f33 100644 --- a/packages/grafana-ui/src/components/Segment/Segment.tsx +++ b/packages/grafana-ui/src/components/Segment/Segment.tsx @@ -1,10 +1,11 @@ import React from 'react'; import { cx } from 'emotion'; +import _ from 'lodash'; import { SelectableValue } from '@grafana/data'; import { SegmentSelect, useExpandableLabel, SegmentProps } from './'; export interface SegmentSyncProps extends SegmentProps { - value?: SelectableValue; + value?: T | SelectableValue; onChange: (item: SelectableValue) => void; options: Array>; } @@ -16,20 +17,28 @@ export function Segment({ Component, className, allowCustomValue, + placeholder, }: React.PropsWithChildren>) { const [Label, width, expanded, setExpanded] = useExpandableLabel(false); if (!expanded) { + const label = _.isObject(value) ? value.label : value; return (
} + Component={ + Component || ( + + {label || placeholder} + + ) + } /> ); } return ( setExpanded(false)} diff --git a/packages/grafana-ui/src/components/Segment/SegmentAsync.story.tsx b/packages/grafana-ui/src/components/Segment/SegmentAsync.story.tsx index 6da7709f1e2..feb12827af8 100644 --- a/packages/grafana-ui/src/components/Segment/SegmentAsync.story.tsx +++ b/packages/grafana-ui/src/components/Segment/SegmentAsync.story.tsx @@ -1,10 +1,7 @@ -import React from 'react'; -import { storiesOf } from '@storybook/react'; +import React, { useState } from 'react'; import { action } from '@storybook/addon-actions'; import { SelectableValue } from '@grafana/data'; -const SegmentStories = storiesOf('UI/Segment/SegmentAsync', module); import { SegmentAsync } from './'; -import { UseState } from '../../utils/storybook/UseState'; const AddButton = ( @@ -13,132 +10,116 @@ const AddButton = ( ); const toOption = (value: any) => ({ label: value, value: value }); +const options = ['Option1', 'Option2', 'OptionWithLooongLabel', 'Option4'].map(toOption); const loadOptions = (options: any): Promise>> => new Promise(res => setTimeout(() => res(options), 2000)); -SegmentStories.add('Array Options', () => { - const options = ['Option1', 'Option2', 'OptionWithLooongLabel', 'Option4'].map(toOption); - return ( - - {(value, updateValue) => ( - <> -
-
- Segment Name -
- loadOptions(options)} - onChange={item => { - updateValue(item); - action('Segment value changed')(item.value); - }} - /> - action('New value added')(value)} - loadOptions={() => loadOptions(options)} - /> -
- - )} -
- ); -}); +const SegmentFrame = ({ loadOptions, children }: any) => ( + <> +
+
+ Segment Name +
+ {children} + action('New value added')(value)} + loadOptions={() => loadOptions(options)} + /> +
+ +); -const groupedOptions = [ +export const ArrayOptions = () => { + const [value, setValue] = useState(options[0]); + return ( + loadOptions(options)}> + loadOptions(options)} + onChange={item => { + setValue(item); + action('Segment value changed')(item.value); + }} + /> + + ); +}; + +export default { + title: 'UI/Segment/SegmentAsync', + component: ArrayOptions, +}; + +export const ArrayOptionsWithPrimitiveValue = () => { + const [value, setValue] = useState(options[0].value); + return ( + loadOptions(options)}> + loadOptions(options)} + onChange={({ value }) => { + setValue(value); + action('Segment value changed')(value); + }} + /> + + ); +}; + +const groupedOptions: any = [ { label: 'Names', options: ['Jane', 'Tom', 'Lisa'].map(toOption) }, { label: 'Prime', options: [2, 3, 5, 7, 11, 13].map(toOption) }, ]; -SegmentStories.add('Grouped Array Options', () => { +export const GroupedArrayOptions = () => { + const [value, setValue] = useState(groupedOptions[0].options[0]); return ( - - {(value, updateValue) => ( - <> -
-
- Segment Name -
- loadOptions(groupedOptions)} - onChange={item => { - updateValue(item); - action('Segment value changed')(item.value); - }} - /> - action('New value added')(value)} - loadOptions={() => loadOptions(groupedOptions)} - /> -
- - )} -
+ loadOptions(groupedOptions)}> + loadOptions(groupedOptions)} + onChange={item => { + setValue(item); + action('Segment value changed')(item.value); + }} + /> + ); -}); +}; -SegmentStories.add('With custom options allowed', () => { - const options = ['Option1', 'Option2', 'OptionWithLooongLabel', 'Option4'].map(toOption); +export const CustomOptionsAllowed = () => { + const [value, setValue] = useState(groupedOptions[0].options[0]); return ( - - {(value, updateValue) => ( - <> -
-
- Segment Name -
- loadOptions(options)} - onChange={item => { - updateValue(item); - action('Segment value changed')(item.value); - }} - /> - action('New value added')(value)} - loadOptions={() => loadOptions(options)} - /> -
- - )} -
+ loadOptions(groupedOptions)}> + loadOptions(options)} + onChange={item => { + setValue(item); + action('Segment value changed')(item.value); + }} + /> + ); -}); +}; -const CustomLabelComponent = ({ value: { value } }: any) =>
custom({value})
; -SegmentStories.add('Custom Label Field', () => { +const CustomLabelComponent = ({ value }: any) =>
custom({value})
; + +export const CustomLabel = () => { + const [value, setValue] = useState(groupedOptions[0].options[0].value); return ( - - {(value, updateValue) => ( - <> -
-
- Segment Name -
- } - loadOptions={() => loadOptions(groupedOptions)} - onChange={item => { - updateValue(item); - action('Segment value changed')(item.value); - }} - /> - action('New value added')(value)} - loadOptions={() => loadOptions(groupedOptions)} - /> -
- - )} -
+ loadOptions(groupedOptions)}> + } + loadOptions={() => loadOptions(groupedOptions)} + onChange={({ value }) => { + setValue(value); + action('Segment value changed')(value); + }} + /> + ); -}); +}; diff --git a/packages/grafana-ui/src/components/Segment/SegmentAsync.tsx b/packages/grafana-ui/src/components/Segment/SegmentAsync.tsx index 2dd849bc25e..ef32da8a1cc 100644 --- a/packages/grafana-ui/src/components/Segment/SegmentAsync.tsx +++ b/packages/grafana-ui/src/components/Segment/SegmentAsync.tsx @@ -1,11 +1,12 @@ import React, { useState } from 'react'; import { cx } from 'emotion'; +import _ from 'lodash'; import { SegmentSelect } from './SegmentSelect'; import { SelectableValue } from '@grafana/data'; import { useExpandableLabel, SegmentProps } from '.'; export interface SegmentAsyncProps extends SegmentProps { - value?: SelectableValue; + value?: T | SelectableValue; loadOptions: (query?: string) => Promise>>; onChange: (item: SelectableValue) => void; } @@ -17,12 +18,14 @@ export function SegmentAsync({ Component, className, allowCustomValue, + placeholder, }: React.PropsWithChildren>) { const [selectPlaceholder, setSelectPlaceholder] = useState(''); const [loadedOptions, setLoadedOptions] = useState>>([]); const [Label, width, expanded, setExpanded] = useExpandableLabel(false); if (!expanded) { + const label = _.isObject(value) ? value.label : value; return (
{value && value.label}} + Component={ + Component || ( + + {label || placeholder} + + ) + } /> ); } return ( { +const SegmentFrame = ({ children }: any) => ( + <> +
+
+ Segment Name +
+ {children} +
+ +); + +export const BasicInput = () => { + const [value, setValue] = useState('some text'); return ( - - {(value, updateValue) => ( - <> -
-
- Segment Name -
- { - updateValue(text as string); - action('Segment value changed')(text); - }} - /> -
- - )} -
+ + { + setValue(text as string); + action('Segment value changed')(text); + }} + /> + ); -}); +}; + +export default { + title: 'UI/Segment/SegmentInput', + component: BasicInput, +}; + +export const BasicInputWithPlaceholder = () => { + const [value, setValue] = useState(''); + return ( + + { + setValue(text as string); + action('Segment value changed')(text); + }} + /> + + ); +}; + +const InputComponent = ({ initialValue }: any) => { + const [value, setValue] = useState(initialValue); + return ( + { + setValue(text as string); + action('Segment value changed')(text); + }} + /> + ); +}; + +export const InputWithAutoFocus = () => { + const [inputComponents, setInputComponents] = useState([]); + return ( + + {inputComponents.map((InputComponent: any) => ( + + ))} + { + setInputComponents([...inputComponents, InputComponent]); + }} + > + + + + ); +}; diff --git a/packages/grafana-ui/src/components/Segment/SegmentInput.tsx b/packages/grafana-ui/src/components/Segment/SegmentInput.tsx index b046f842c57..054cecf1455 100644 --- a/packages/grafana-ui/src/components/Segment/SegmentInput.tsx +++ b/packages/grafana-ui/src/components/Segment/SegmentInput.tsx @@ -7,6 +7,7 @@ import { useExpandableLabel, SegmentProps } from '.'; export interface SegmentInputProps extends SegmentProps { value: string | number; onChange: (text: string | number) => void; + autofocus?: boolean; } const FONT_SIZE = 14; @@ -16,16 +17,30 @@ export function SegmentInput({ onChange, Component, className, + placeholder, + autofocus = false, }: React.PropsWithChildren>) { - const ref = useRef(null); + const ref = useRef(null); const [value, setValue] = useState(initialValue); - const [inputWidth, setInputWidth] = useState(measureText(initialValue.toString(), FONT_SIZE).width); - const [Label, , expanded, setExpanded] = useExpandableLabel(false); - useClickAway(ref, () => setExpanded(false)); + const [inputWidth, setInputWidth] = useState(measureText((initialValue || '').toString(), FONT_SIZE).width); + const [Label, , expanded, setExpanded] = useExpandableLabel(autofocus); + + useClickAway(ref, () => { + setExpanded(false); + onChange(value); + }); if (!expanded) { return ( -