Files
2020-10-03 11:53:01 +02:00

311 lines
7.3 KiB
TypeScript

import React, { useState } from 'react';
import { withCenteredStory, withHorizontallyCenteredStory } from '../../utils/storybook/withCenteredStory';
import { SelectableValue } from '@grafana/data';
import { Icon, Button, ButtonSelect, Select, AsyncSelect, MultiSelect, AsyncMultiSelect } from '@grafana/ui';
import { getAvailableIcons, IconName } from '../../types';
import { select, boolean, number } from '@storybook/addon-knobs';
import { getIconKnob } from '../../utils/storybook/knobs';
import kebabCase from 'lodash/kebabCase';
import { generateOptions } from './mockOptions';
import mdx from './Select.mdx';
export default {
title: 'Forms/Select',
component: Select,
decorators: [withCenteredStory, withHorizontallyCenteredStory],
subcomponents: { AsyncSelect, MultiSelect, AsyncMultiSelect },
parameters: {
docs: {
page: mdx,
},
},
};
const BEHAVIOUR_GROUP = 'Behaviour props';
const loadAsyncOptions = () => {
return new Promise<Array<SelectableValue<string>>>(resolve => {
setTimeout(() => {
resolve(generateOptions());
}, 2000);
});
};
const getKnobs = () => {
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);
const width = number('Width', 0, undefined, VISUAL_GROUP);
let prefixEl: any = prefix;
if (prefix && prefix.match(/icon-/g)) {
prefixEl = <Icon name={prefix.replace(/icon-/g, '') as IconName} />;
}
return {
width,
disabled,
invalid,
loading,
prefixEl,
};
};
const getMultiSelectKnobs = () => {
const isClearable = boolean('Clearable', false, BEHAVIOUR_GROUP);
const closeMenuOnSelect = boolean('Close on Select', false, BEHAVIOUR_GROUP);
const maxVisibleValues = number('Max. visible values', 5, undefined, BEHAVIOUR_GROUP);
return {
isClearable,
closeMenuOnSelect,
maxVisibleValues,
};
};
const getDynamicProps = () => {
const knobs = getKnobs();
return {
width: knobs.width,
disabled: knobs.disabled,
isLoading: knobs.loading,
invalid: knobs.invalid,
prefix: knobs.prefixEl,
};
};
export const Basic = () => {
const [value, setValue] = useState<SelectableValue<string>>();
return (
<>
<Select
options={generateOptions()}
value={value}
onChange={v => {
setValue(v);
}}
{...getDynamicProps()}
/>
</>
);
};
/**
* Uses plain values instead of SelectableValue<T>
*/
export const BasicSelectPlainValue = () => {
const [value, setValue] = useState<string>();
return (
<>
<Select
options={generateOptions()}
value={value}
onChange={v => {
setValue(v.value);
}}
{...getDynamicProps()}
/>
</>
);
};
/**
* 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',
},
];
return (
<>
<Select
options={options}
value={value}
onChange={v => {
setValue(v.value);
}}
{...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));
}}
{...getDynamicProps()}
/>
</>
);
};
export const MultiSelectBasic = () => {
const [value, setValue] = useState<Array<SelectableValue<string>>>([]);
return (
<>
<MultiSelect
options={generateOptions()}
value={value}
onChange={v => {
setValue(v);
}}
{...getDynamicProps()}
{...getMultiSelectKnobs()}
/>
</>
);
};
export const MultiSelectAsync = () => {
const [value, setValue] = useState<Array<SelectableValue<string>>>();
return (
<AsyncMultiSelect
loadOptions={loadAsyncOptions}
defaultOptions
value={value}
onChange={v => {
setValue(v);
}}
allowCustomValue
{...getDynamicProps()}
/>
);
};
export const ButtonSelectBasic = () => {
const [value, setValue] = useState<SelectableValue<string>>();
const icon = getIconKnob();
return (
<ButtonSelect
placeholder="Select all the things..."
value={value}
options={generateOptions()}
onChange={v => {
setValue(v);
}}
allowCustomValue
icon={icon}
{...getDynamicProps()}
/>
);
};
export const BasicSelectAsync = () => {
const [value, setValue] = useState<SelectableValue<string>>();
return (
<AsyncSelect
loadOptions={loadAsyncOptions}
defaultOptions
value={value}
onChange={v => {
setValue(v);
}}
{...getDynamicProps()}
/>
);
};
export const CustomizedControl = () => {
const [value, setValue] = useState<SelectableValue<string>>();
return (
<Select
options={generateOptions()}
value={value}
onChange={v => {
setValue(v);
}}
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={{ width: '100%', height: '95vh', display: 'flex', alignItems: 'flex-end' }}>
<Select
options={generateOptions()}
value={value}
onChange={v => {
setValue(v);
}}
{...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);
}}
allowCustomValue
onCreateOption={v => {
const customValue: SelectableValue<string> = { value: kebabCase(v), label: v };
setCustomOptions([...customOptions, customValue]);
setValue(customValue);
}}
{...getDynamicProps()}
/>
</>
);
};