Skip to content

Commit 24f101c

Browse files
authored
Upgrades AutoComplete to better support async fetching of results (#637)
<div class='graphite__hidden'> <div>🎥 Video uploaded on Graphite:</div> <a href="https://app.graphite.dev/media/video/NOxkRPuhAfT4F7nmdXtG/62d618eb-553e-400d-9da8-c52a7d4c9adc.mov"> <img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/NOxkRPuhAfT4F7nmdXtG/62d618eb-553e-400d-9da8-c52a7d4c9adc.mov"> </a> </div> <video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/NOxkRPuhAfT4F7nmdXtG/62d618eb-553e-400d-9da8-c52a7d4c9adc.mov">Screen Recording 2024-09-17 at 12.34.28 PM.mov</video>
1 parent 1830597 commit 24f101c

File tree

3 files changed

+98
-3
lines changed

3 files changed

+98
-3
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@terraware/web-components",
3-
"version": "3.3.4",
3+
"version": "3.3.5-rc.0",
44
"author": "Terraformation Inc.",
55
"license": "Apache-2.0",
66
"repository": {

src/components/Autocomplete/Autocomplete.tsx

+27-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export interface Props {
1414
id?: string;
1515
label?: string;
1616
selected?: ValueType;
17+
filterOptions?: (options: ValueType[], state: object) => ValueType[];
1718
freeSolo?: boolean;
1819
disabled?: boolean;
1920
className?: string;
@@ -23,6 +24,12 @@ export interface Props {
2324
errorText?: string;
2425
tooltipTitle?: TooltipProps['title'];
2526
required?: boolean;
27+
noOptionsText?: string;
28+
loading?: boolean;
29+
loadingText?: string;
30+
open?: boolean;
31+
onOpen?: (event: React.SyntheticEvent) => void;
32+
onClose?: (event: React.SyntheticEvent) => void;
2633
sx?: SxProps;
2734
renderOption?: (props: React.HTMLAttributes<HTMLLIElement>, option: ValueType) => React.ReactNode;
2835
}
@@ -35,6 +42,7 @@ export default function Autocomplete({
3542
options,
3643
onChange,
3744
selected,
45+
filterOptions,
3846
freeSolo,
3947
disabled,
4048
className,
@@ -44,6 +52,12 @@ export default function Autocomplete({
4452
errorText,
4553
tooltipTitle,
4654
required,
55+
noOptionsText,
56+
loading,
57+
loadingText,
58+
open,
59+
onOpen,
60+
onClose,
4761
sx,
4862
renderOption,
4963
}: Props): JSX.Element {
@@ -111,11 +125,23 @@ export default function Autocomplete({
111125
onChange={onChangeHandler}
112126
onInputChange={(event: any, value: any) => freeSolo && onChangeHandler(event, value)}
113127
value={selected}
128+
filterOptions={filterOptions}
114129
freeSolo={freeSolo}
115130
forcePopupIcon={true}
116131
renderInput={renderInput}
117132
aria-required={required}
118-
popupIcon={<Icon name='chevronDown' className='auto-complete--icon-right' size='medium' />}
133+
noOptionsText={noOptionsText}
134+
loading={loading}
135+
loadingText={loadingText}
136+
open={open}
137+
onOpen={onOpen}
138+
onClose={onClose}
139+
popupIcon={
140+
<Icon
141+
name={loading ? 'spinner' : open ? 'chevronUp' : 'chevronDown'}
142+
className='auto-complete--icon-right'
143+
size='medium'
144+
/>}
119145
classes={{
120146
paper: 'auto-complete select',
121147
listbox: 'options-container',

src/stories/Autocomplete.stories.tsx

+70-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { action } from '@storybook/addon-actions';
22
import { Story } from '@storybook/react';
33
import { Box, useTheme } from '@mui/material';
4-
import React from 'react';
4+
import React, { useCallback } from 'react';
55
import Autocomplete, { ValueType, Props as AutocompleteProps } from '../components/Autocomplete/Autocomplete';
66

77
export default {
@@ -32,10 +32,71 @@ const Template: Story<AutocompleteProps> = (args) => {
3232
);
3333
};
3434

35+
const AsyncTemplate: Story<AutocompleteProps> = (args) => {
36+
const theme = useTheme();
37+
38+
const [open, setOpen] = React.useState<boolean>(false);
39+
const [loading, setLoading] = React.useState<boolean>(false);
40+
const [selected, setSelected] = React.useState<ValueType>();
41+
const [options, setOptions] = React.useState<ValueType[]>([]);
42+
43+
const sleep = (duration: number): Promise<void> => {
44+
return new Promise<void>((resolve) => {
45+
setTimeout(() => {
46+
resolve();
47+
}, duration);
48+
});
49+
};
50+
51+
const load = useCallback(async () => {
52+
setLoading(true);
53+
await sleep(1e3);
54+
setLoading(false);
55+
setOptions([...args.options]);
56+
}, [setLoading, setOptions]);
57+
58+
const handleOpen = useCallback(async () => {
59+
setOpen(true);
60+
await load();
61+
}, [setOpen, load]);
62+
63+
const handleClose = () => {
64+
setOpen(false);
65+
setOptions([]);
66+
};
67+
68+
const handleChange = (value: ValueType) => {
69+
action('onChange')(value);
70+
setSelected(value);
71+
};
72+
73+
return (
74+
<Box sx={{ marginTop: '30px' }}>
75+
<Autocomplete
76+
{...args}
77+
open={open}
78+
onOpen={() => void handleOpen()}
79+
onClose={handleClose}
80+
selected={selected}
81+
loading={loading}
82+
options={options}
83+
onChange={handleChange}
84+
sx={{
85+
backgroundColor: theme.palette.TwClrBaseWhite,
86+
width: '300px',
87+
}}
88+
/>
89+
</Box>
90+
);
91+
};
92+
3593
export const Default = Template.bind({});
3694

3795
export const Complex = Template.bind({});
3896

97+
export const AsyncDefault = AsyncTemplate.bind({});
98+
99+
39100
Default.args = {
40101
id: '1',
41102
label: 'Test',
@@ -75,3 +136,11 @@ Complex.args = {
75136
isEqual: (option: any, value: any) => option.value === value.value,
76137
tooltipTitle: 'Hello world!',
77138
};
139+
140+
AsyncDefault.args = {
141+
id: '3',
142+
label: 'Test',
143+
options: ['Test 1', 'Test 2', 'Hello'],
144+
freeSolo: true,
145+
errorText: '',
146+
};

0 commit comments

Comments
 (0)