mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 02:09:28 +08:00
Forms migration: Old Select to Legacy namespace (#23200)
* Export other components as Legacy * More Select Legacy * Add namespacing to more files * Export new elements * Move Legacy Select folder * Let's not forget the scss file * Move new Select folder * Move new Select from Forms namespace * Little oopsie * Fix errors * Fix merge issues
This commit is contained in:
@ -1,101 +1,300 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { withKnobs, object } from '@storybook/addon-knobs';
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { UseState } from '../../utils/storybook/UseState';
|
||||
import React, { useState } from 'react';
|
||||
import { Select, AsyncSelect, MultiSelect, AsyncMultiSelect } from './Select';
|
||||
import { withCenteredStory, withHorizontallyCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { Select, AsyncSelect } from './Select';
|
||||
import { getAvailableIcons, IconType } from '../Icon/types';
|
||||
import { select, boolean } from '@storybook/addon-knobs';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
import { Button } from '../Button';
|
||||
import { ButtonSelect } from './ButtonSelect';
|
||||
import { getIconKnob } from '../../utils/storybook/knobs';
|
||||
import kebabCase from 'lodash/kebabCase';
|
||||
import { generateOptions } from './mockOptions';
|
||||
|
||||
export default {
|
||||
title: 'General/Select/Select',
|
||||
title: 'Forms/Select',
|
||||
component: Select,
|
||||
decorators: [withCenteredStory, withKnobs],
|
||||
decorators: [withCenteredStory, withHorizontallyCenteredStory],
|
||||
};
|
||||
|
||||
const intialState: SelectableValue<string> = { label: 'A label', value: 'A value' };
|
||||
const loadAsyncOptions = () => {
|
||||
return new Promise<Array<SelectableValue<string>>>(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve(generateOptions());
|
||||
}, 2000);
|
||||
});
|
||||
};
|
||||
|
||||
const options = object<Array<SelectableValue<string>>>('Options:', [
|
||||
intialState,
|
||||
{ label: 'Another label', value: 'Another value 1' },
|
||||
{ label: 'Another label', value: 'Another value 2' },
|
||||
{ label: 'Another label', value: 'Another value 3' },
|
||||
{ label: 'Another label', value: 'Another value 4' },
|
||||
{ label: 'Another label', value: 'Another value 5' },
|
||||
{ label: 'Another label', value: 'Another value ' },
|
||||
]);
|
||||
const getKnobs = () => {
|
||||
const BEHAVIOUR_GROUP = 'Behaviour props';
|
||||
const disabled = boolean('Disabled', false, BEHAVIOUR_GROUP);
|
||||
const invalid = boolean('Invalid', false, BEHAVIOUR_GROUP);
|
||||
const loading = boolean('Loading', false, BEHAVIOUR_GROUP);
|
||||
const prefixSuffixOpts = {
|
||||
None: null,
|
||||
Text: '$',
|
||||
...getAvailableIcons().reduce<Record<string, string>>((prev, c) => {
|
||||
return {
|
||||
...prev,
|
||||
[`Icon: ${c}`]: `icon-${c}`,
|
||||
};
|
||||
}, {}),
|
||||
};
|
||||
const VISUAL_GROUP = 'Visual options';
|
||||
// ---
|
||||
const prefix = select('Prefix', prefixSuffixOpts, null, VISUAL_GROUP);
|
||||
|
||||
let prefixEl: any = prefix;
|
||||
if (prefix && prefix.match(/icon-/g)) {
|
||||
prefixEl = <Icon name={prefix.replace(/icon-/g, '') as IconType} />;
|
||||
}
|
||||
|
||||
return {
|
||||
disabled,
|
||||
invalid,
|
||||
loading,
|
||||
prefixEl,
|
||||
};
|
||||
};
|
||||
|
||||
const getDynamicProps = () => {
|
||||
const knobs = getKnobs();
|
||||
return {
|
||||
disabled: knobs.disabled,
|
||||
isLoading: knobs.loading,
|
||||
invalid: knobs.invalid,
|
||||
prefix: knobs.prefixEl,
|
||||
};
|
||||
};
|
||||
|
||||
export const basic = () => {
|
||||
const value = object<SelectableValue<string>>('Selected Value:', intialState);
|
||||
const [value, setValue] = useState<SelectableValue<string>>();
|
||||
|
||||
return (
|
||||
<UseState initialState={value}>
|
||||
{(value, updateValue) => {
|
||||
return (
|
||||
<Select
|
||||
placeholder="Choose..."
|
||||
options={options}
|
||||
width={20}
|
||||
onChange={value => {
|
||||
action('onChanged fired')(value);
|
||||
updateValue(value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</UseState>
|
||||
<>
|
||||
<Select
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={v => {
|
||||
setValue(v);
|
||||
}}
|
||||
size="md"
|
||||
{...getDynamicProps()}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const withAllowCustomValue = () => {
|
||||
// @ts-ignore
|
||||
const value = object<SelectableValue<string>>('Selected Value:', null);
|
||||
|
||||
/**
|
||||
* Uses plain values instead of SelectableValue<T>
|
||||
*/
|
||||
export const basicSelectPlainValue = () => {
|
||||
const [value, setValue] = useState<string>();
|
||||
return (
|
||||
<UseState initialState={value}>
|
||||
{(value, updateValue) => {
|
||||
return (
|
||||
<Select
|
||||
// value={value}
|
||||
placeholder="Choose..."
|
||||
options={options}
|
||||
width={20}
|
||||
allowCustomValue={true}
|
||||
onChange={value => {
|
||||
action('onChanged fired')(value);
|
||||
updateValue(value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</UseState>
|
||||
<>
|
||||
<Select
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={v => {
|
||||
setValue(v.value);
|
||||
}}
|
||||
size="md"
|
||||
{...getDynamicProps()}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const asyncSelect = () => {
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [value, setValue] = useState();
|
||||
const loadAsyncOptions = useCallback(
|
||||
inputValue => {
|
||||
return new Promise<Array<SelectableValue<string>>>(resolve => {
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
resolve(options.filter(option => option.label && option.label.includes(inputValue)));
|
||||
}, 1000);
|
||||
});
|
||||
/**
|
||||
* Uses plain values instead of SelectableValue<T>
|
||||
*/
|
||||
export const SelectWithOptionDescriptions = () => {
|
||||
// TODO this is not working with new Select
|
||||
|
||||
const [value, setValue] = useState<number>();
|
||||
const options = [
|
||||
{ label: 'Basic option', value: 0 },
|
||||
{ label: 'Option with description', value: 1, description: 'this is a description' },
|
||||
{
|
||||
label: 'Option with description and image',
|
||||
value: 2,
|
||||
description: 'This is a very elaborate description, describing all the wonders in the world.',
|
||||
imgUrl: 'https://placekitten.com/40/40',
|
||||
},
|
||||
[value]
|
||||
);
|
||||
];
|
||||
|
||||
return (
|
||||
<AsyncSelect
|
||||
value={value}
|
||||
defaultOptions
|
||||
width={20}
|
||||
isLoading={isLoading}
|
||||
<>
|
||||
<Select
|
||||
options={options}
|
||||
value={value}
|
||||
onChange={v => {
|
||||
setValue(v.value);
|
||||
}}
|
||||
size="md"
|
||||
{...getDynamicProps()}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Uses plain values instead of SelectableValue<T>
|
||||
*/
|
||||
export const multiPlainValue = () => {
|
||||
const [value, setValue] = useState<string[]>();
|
||||
|
||||
return (
|
||||
<>
|
||||
<MultiSelect
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={v => {
|
||||
setValue(v.map((v: any) => v.value));
|
||||
}}
|
||||
size="md"
|
||||
{...getDynamicProps()}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const multiSelect = () => {
|
||||
const [value, setValue] = useState<Array<SelectableValue<string>>>([]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MultiSelect
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={v => {
|
||||
setValue(v);
|
||||
}}
|
||||
size="md"
|
||||
{...getDynamicProps()}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const multiSelectAsync = () => {
|
||||
const [value, setValue] = useState<Array<SelectableValue<string>>>();
|
||||
|
||||
return (
|
||||
<AsyncMultiSelect
|
||||
loadOptions={loadAsyncOptions}
|
||||
onChange={value => {
|
||||
action('onChange')(value);
|
||||
setValue(value);
|
||||
defaultOptions
|
||||
value={value}
|
||||
onChange={v => {
|
||||
setValue(v);
|
||||
}}
|
||||
size="md"
|
||||
allowCustomValue
|
||||
{...getDynamicProps()}
|
||||
/>
|
||||
);
|
||||
};
|
||||
export const buttonSelect = () => {
|
||||
const [value, setValue] = useState<SelectableValue<string>>();
|
||||
const icon = getIconKnob();
|
||||
return (
|
||||
<ButtonSelect
|
||||
placeholder="Select all the things..."
|
||||
value={value}
|
||||
options={generateOptions()}
|
||||
onChange={v => {
|
||||
setValue(v);
|
||||
}}
|
||||
size="md"
|
||||
allowCustomValue
|
||||
icon={icon}
|
||||
{...getDynamicProps()}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const basicSelectAsync = () => {
|
||||
const [value, setValue] = useState<SelectableValue<string>>();
|
||||
|
||||
return (
|
||||
<AsyncSelect
|
||||
loadOptions={loadAsyncOptions}
|
||||
defaultOptions
|
||||
value={value}
|
||||
onChange={v => {
|
||||
setValue(v);
|
||||
}}
|
||||
size="md"
|
||||
{...getDynamicProps()}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const customizedControl = () => {
|
||||
const [value, setValue] = useState<SelectableValue<string>>();
|
||||
|
||||
return (
|
||||
<Select
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={v => {
|
||||
setValue(v);
|
||||
}}
|
||||
size="md"
|
||||
renderControl={React.forwardRef(({ isOpen, value, ...otherProps }, ref) => {
|
||||
return (
|
||||
<Button {...otherProps} ref={ref}>
|
||||
{' '}
|
||||
{isOpen ? 'Open' : 'Closed'}
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
{...getDynamicProps()}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const autoMenuPlacement = () => {
|
||||
const [value, setValue] = useState<SelectableValue<string>>();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={{ height: '95vh', display: 'flex', alignItems: 'flex-end' }}>
|
||||
<Select
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={v => {
|
||||
setValue(v);
|
||||
}}
|
||||
size="md"
|
||||
{...getDynamicProps()}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const customValueCreation = () => {
|
||||
const [value, setValue] = useState<SelectableValue<string>>();
|
||||
const [customOptions, setCustomOptions] = useState<Array<SelectableValue<string>>>([]);
|
||||
const options = generateOptions();
|
||||
return (
|
||||
<>
|
||||
<Select
|
||||
options={[...options, ...customOptions]}
|
||||
value={value}
|
||||
onChange={v => {
|
||||
setValue(v);
|
||||
}}
|
||||
size="md"
|
||||
allowCustomValue
|
||||
onCreateOption={v => {
|
||||
const customValue: SelectableValue<string> = { value: kebabCase(v), label: v };
|
||||
setCustomOptions([...customOptions, customValue]);
|
||||
setValue(customValue);
|
||||
}}
|
||||
{...getDynamicProps()}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
Reference in New Issue
Block a user