@ -4,16 +4,16 @@ import React, { ComponentProps, useCallback, useEffect, useState } from 'react';
import { SelectableValue } from '@grafana/data' ;
import { useTheme2 } from '../../themes/ThemeContext' ;
import { Alert } from '../Alert/Alert' ;
import { Divider } from '../Divider/Divider' ;
import { Field } from '../Forms/Field' ;
import { AsyncSelect , Select } from '../Select/Select' ;
import { AsyncSelect } from '../Select/Select' ;
import { Combobox , ComboboxOption } from './Combobox' ;
import mdx from './Combobox.mdx' ;
type PropsAndCustomArgs = ComponentProps < typeof Combobox > & { numberOfOptions : number } ;
type PropsAndCustomArgs < T extends string | number = string > = ComponentProps < typeof Combobox < T > > & {
numberOfOptions : number ;
} ;
const meta : Meta < PropsAndCustomArgs > = {
title : 'Forms/Combobox' ,
@ -27,6 +27,7 @@ const meta: Meta<PropsAndCustomArgs> = {
loading : undefined ,
invalid : undefined ,
width : undefined ,
isClearable : false ,
placeholder : 'Select an option...' ,
options : [
{ label : 'Apple' , value : 'apple' } ,
@ -45,12 +46,6 @@ const meta: Meta<PropsAndCustomArgs> = {
{ label : 'Honeydew' , value : 'honeydew' } ,
{ label : 'Iceberg Lettuce' , value : 'iceberg-lettuce' } ,
{ label : 'Jackfruit' , value : 'jackfruit' } ,
{ label : '1' , value : 1 } ,
{ label : '2' , value : 2 } ,
{ label : '3' , value : 3 } ,
{ label : '4' , value : 4 } ,
{ label : '5' , value : 5 } ,
{ label : '6' , value : 6 } ,
] ,
value : 'banana' ,
} ,
@ -59,16 +54,16 @@ const meta: Meta<PropsAndCustomArgs> = {
decorators : [ InDevDecorator ] ,
} ;
const BasicWithState : StoryFn < typeof Combobox > = ( args ) = > {
const [ value , setValue ] = useState ( args . value ) ;
const BasicWithState : StoryFn < PropsAndCustomArgs > = ( args ) = > {
const [ value , setValue ] = useState < string | null > ( ) ;
return (
< Field label = "Test input" description = "Input with a few options" >
< Combobox
id = "test-combobox"
{ . . . args }
value = { value }
onChange = { ( val ) = > {
onChange = { ( val : ComboboxOption | null ) = > {
// TODO: Figure out how to update value on args
setValue ( val ? . value || null ) ;
action ( 'onChange' ) ( val ) ;
} }
@ -88,7 +83,7 @@ async function generateOptions(amount: number): Promise<ComboboxOption[]> {
} ) ) ;
}
const ManyOptionsStory : StoryFn < PropsAndCustomArgs > = ( { numberOfOptions , . . . args } ) = > {
const ManyOptionsStory : StoryFn < PropsAndCustomArgs < string > > = ( { numberOfOptions , . . . args } ) = > {
const [ value , setValue ] = useState < string | null > ( null ) ;
const [ options , setOptions ] = useState < ComboboxOption [ ] > ( [ ] ) ;
const [ isLoading , setIsLoading ] = useState ( true ) ;
@ -103,13 +98,14 @@ const ManyOptionsStory: StoryFn<PropsAndCustomArgs> = ({ numberOfOptions, ...arg
} , 1000 ) ;
} , [ numberOfOptions ] ) ;
const { onChange , . . . rest } = args ;
return (
< Combobox
{ . . . args }
{ . . . rest }
loading = { isLoading }
options = { options }
value = { value }
onChange = { ( opt ) = > {
onChange = { ( opt : ComboboxOption | null ) = > {
setValue ( opt ? . value || null ) ;
action ( 'onChange' ) ( opt ) ;
} }
@ -117,132 +113,6 @@ const ManyOptionsStory: StoryFn<PropsAndCustomArgs> = ({ numberOfOptions, ...arg
) ;
} ;
const SelectComparisonStory : StoryFn < typeof Combobox > = ( args ) = > {
const [ comboboxValue , setComboboxValue ] = useState ( args . value ) ;
const theme = useTheme2 ( ) ;
if ( typeof args . options === 'function' ) {
throw new Error ( 'This story does not support async options' ) ;
}
return (
< div style = { { border : '1px solid ' + theme . colors . border . weak , padding : 16 } } >
< Field label = "Combobox with default size" >
< Combobox
{ . . . args }
id = "combobox-default-size"
value = { comboboxValue }
options = { args . options }
onChange = { ( val ) = > {
setComboboxValue ( val ? . value || null ) ;
action ( 'onChange' ) ( val ) ;
} }
/ >
< / Field >
< Field label = "Select with default size" >
< Select
id = "select-default-size"
value = { comboboxValue }
options = { args . options }
onChange = { ( val ) = > {
setComboboxValue ( val ? . value || null ) ;
action ( 'onChange' ) ( val ) ;
} }
/ >
< / Field >
< Divider / >
< Field label = "Combobox with explicit size (25)" >
{ /*@ts-ignore minWidth and maxWidth has never, which is incompatible with args. It lacks the context that width=25 on the component*/ }
< Combobox
{ . . . args }
id = "combobox-explicit-size"
width = { 25 }
value = { comboboxValue }
options = { args . options }
onChange = { ( val ) = > {
setComboboxValue ( val ? . value || null ) ;
action ( 'onChange' ) ( val ) ;
} }
/ >
< / Field >
< Field label = "Select with explicit size (25)" >
< Select
id = "select-explicit-size"
width = { 25 }
value = { comboboxValue }
options = { args . options }
onChange = { ( val ) = > {
setComboboxValue ( val ? . value || null ) ;
action ( 'onChange' ) ( val ) ;
} }
/ >
< / Field >
< Divider / >
< Field label = "Combobox with auto width, minWidth 15" >
< Combobox
{ . . . args }
id = "combobox-auto-size"
width = "auto"
minWidth = { 15 }
value = { comboboxValue }
options = { args . options }
onChange = { ( val ) = > {
setComboboxValue ( val ? . value || null ) ;
action ( 'onChange' ) ( val ) ;
} }
/ >
< / Field >
< Field label = "Select with auto width" >
< Select
id = "select-auto-size"
width = "auto"
value = { comboboxValue }
options = { args . options }
onChange = { ( val ) = > {
setComboboxValue ( val ? . value || null ) ;
action ( 'onChange' ) ( val ) ;
} }
/ >
< / Field >
< Field label = "Combobox with auto width, minWidth 15, empty value" >
< Combobox
{ . . . args }
id = "combobox-auto-size-empty"
width = "auto"
minWidth = { 15 }
value = { null }
options = { args . options }
onChange = { ( val ) = > {
setComboboxValue ( val ? . value || null ) ;
action ( 'onChange' ) ( val ) ;
} }
/ >
< / Field >
< Field label = "Select with auto width, empty value" >
< Select
id = "select-auto-size-empty"
width = "auto"
value = { null }
options = { args . options }
onChange = { ( val ) = > {
setComboboxValue ( val ? . value || null ) ;
action ( 'onChange' ) ( val ) ;
} }
/ >
< / Field >
< / div >
) ;
} ;
export const AutoSize : StoryObj < PropsAndCustomArgs > = {
args : {
width : 'auto' ,
@ -294,6 +164,8 @@ const AsyncStory: StoryFn<PropsAndCustomArgs> = (args) => {
}
} , [ ] ) ;
const { onChange , . . . rest } = args ;
return (
< >
< Field
@ -301,12 +173,12 @@ const AsyncStory: StoryFn<PropsAndCustomArgs> = (args) => {
description = "This tests when options have both a label and a value. Consumers are required to pass in a full ComboboxOption as a value with a label"
>
< Combobox
{ . . . args }
{ . . . rest }
id = "test-combobox-one"
placeholder = "Select an option"
options = { loadOptionsWithLabels }
value = { selectedOption }
onChange = { ( val ) = > {
onChange = { ( val : ComboboxOption | null ) = > {
action ( 'onChange' ) ( val ) ;
setSelectedOption ( val ) ;
} }
@ -324,7 +196,7 @@ const AsyncStory: StoryFn<PropsAndCustomArgs> = (args) => {
placeholder = "Select an option"
options = { loadOptionsOnlyValues }
value = { selectedOption ? . value ? ? null }
onChange = { ( val ) = > {
onChange = { ( val : ComboboxOption | null ) = > {
action ( 'onChange' ) ( val ) ;
setSelectedOption ( val ) ;
} }
@ -366,7 +238,7 @@ const AsyncStory: StoryFn<PropsAndCustomArgs> = (args) => {
placeholder = "Select an option"
options = { loadOptionsWithErrors }
value = { selectedOption }
onChange = { ( val ) = > {
onChange = { ( val : ComboboxOption | null ) = > {
action ( 'onChange' ) ( val ) ;
setSelectedOption ( val ) ;
} }
@ -426,13 +298,6 @@ export const PositioningTest: StoryObj<PropsAndCustomArgs> = {
render : PositioningTestStory ,
} ;
export const ComparisonToSelect : StoryObj < PropsAndCustomArgs > = {
args : {
numberOfOptions : 100 ,
} ,
render : SelectComparisonStory ,
} ;
export default meta ;
function InDevDecorator ( Story : React.ElementType ) {