import { createElement, useState } from 'react';
import {
  ArgsTable,
  Canvas,
  Meta,
  Source,
  Story
} from '@storybook/addon-docs/blocks';
import Autocomplete from 'components/Autocomplete';
import TextInput from 'components/TextInput';
import CWBTheme, { ThemeProvider } from 'styles/CWBTheme';

<Meta title="Components API/Autocomplete" component={Autocomplete} />

# Autocomplete

Allows the user to perform an action on a page.

<Canvas>
  <Story name="Autocomplete" height="150px">
    {() => {
      const options = [
        { label: 'Option 1', value: 1 },
        { label: 'Option 2', value: 2 },
        { label: 'Option 3', value: 3 }
      ];
      const [value, setValue] = useState(options[0]);
      const [inputValue, setInputValue] = useState('Option');
      return (
        <Autocomplete
          options={options}
          renderInput={(props) => (
            <TextInput
              {...props}
              placeholder="Start typing..."
              value={inputValue}
              onChange={(e) => setInputValue(e.target.value)}
            />
          )}
          value={value}
          onSelect={(option) => {
            setValue(option);
            setInputValue(option ? option.label : '');
          }}
        />
      );
    }}
  </Story>
</Canvas>

<Source code="import { Autocomplete } from 'cwb-react';" />

## Usage

Similar to Select, Autocomplete allows the user to choose an option from a list of options. Unlike Select, Autocompletes should be used when you want to allow the user to filter the options by typing into the TextInput.

In order to use the basic functions of Autocomplete, you must pass in the props `options`, `renderInput`, `value` and `onSelect`. Here, `renderInput` expects a function that returns a **controlled** TextInput (or equivalent), and **you must pass `props` down to the input component**.

<ThemeProvider theme={CWBTheme}>
  <Canvas style={{ marginBottom: '0' }}>
    <div style={{ height: '150px' }}>
      {createElement(() => {
        const options = [
          { label: 'Option 1', value: 1 },
          { label: 'Option 2', value: 2 },
          { label: 'Option 3', value: 3 }
        ];
        const [value, setValue] = useState(options[0]);
        const [inputValue, setInputValue] = useState('');
        return (
          <Autocomplete
            options={options}
            renderInput={(props) => (
              <TextInput
                {...props}
                placeholder="Start typing..."
                value={inputValue}
                onChange={(e) => setInputValue(e.target.value)}
              />
            )}
            value={value}
            onSelect={(option) => {
              setValue(option);
              setInputValue(option ? option.label : '');
            }}
          />
        );
      })}
    </div>
  </Canvas>
  <div style={{ marginTop: '-20px' }}>
    <Source
      code={
        'const options = [\n' +
        `  { label: 'Option 1', value: 1 },\n` +
        `  { label: 'Option 2', value: 2 },\n` +
        `  { label: 'Option 3', value: 3 }\n` +
        '];\n' +
        'const [value, setValue] = useState(options[0]);\n' +
        `const [inputValue, setInputValue] = useState('');\n\n` +
        'return (\n' +
        '  <Autocomplete\n' +
        '    options={options}\n' +
        '    renderInput={(props) => (\n' +
        '      <TextInput\n' +
        `        // passing down props allows Autocomplete to control the input's focus\n` +
        '        {...props}\n' +
        '        placeholder="Start typing..."\n' +
        '        value={inputValue}\n' +
        '        onChange={(e) => setInputValue(e.target.value)}\n' +
        '      />\n' +
        '    )}\n' +
        '    value={value}\n' +
        '    onSelect={(option) => {\n' +
        '      setValue(option);\n' +
        `      setInputValue(option ? option.label : '');\n` +
        '    }}\n' +
        '  />\n' +
        ');'
      }
    />
  </div>
</ThemeProvider>

You can customize the behavior of Autocomplete to allow users to add options. In the below example, the additional props passed to Autocomplete to make this happen are `allowAdd` and `onAdd`.

<ThemeProvider theme={CWBTheme}>
  <Canvas style={{ marginBottom: '0' }}>
    <div style={{ height: '180px' }}>
      {createElement(() => {
        const [options, setOptions] = useState([
          { label: 'Option 1', value: 1 },
          { label: 'Option 2', value: 2 },
          { label: 'Option 3', value: 3 }
        ]);
        const [value, setValue] = useState(options[0]);
        const [inputValue, setInputValue] = useState('');
        return (
          <Autocomplete
            allowAdd
            options={options}
            renderInput={(props) => (
              <TextInput
                {...props}
                placeholder="Start typing..."
                value={inputValue}
                onChange={(e) => setInputValue(e.target.value)}
              />
            )}
            value={value}
            onAdd={() => {
              setOptions((options) => {
                return [...options, { label: inputValue, value: inputValue }];
              });
            }}
            onSelect={(option) => {
              setValue(option);
              setInputValue(option ? option.label : '');
            }}
          />
        );
      })}
    </div>
  </Canvas>
  <div style={{ marginTop: '-20px' }}>
    <Source
      code={
        'const [options, setOptions] = useState([\n' +
        `  { label: 'Option 1', value: 1 },\n` +
        `  { label: 'Option 2', value: 2 },\n` +
        `  { label: 'Option 3', value: 3 }\n` +
        ']);\n' +
        'const [value, setValue] = useState(options[0]);\n' +
        `const [inputValue, setInputValue] = useState('');\n\n` +
        'return (\n' +
        '  <Autocomplete\n' +
        '    allowAdd\n' +
        '    options={options}\n' +
        '    renderInput={(props) => (\n' +
        '      <TextInput\n' +
        '        {...props}\n' +
        '        placeholder="Start typing..."\n' +
        '        value={inputValue}\n' +
        '        onChange={(e) => setInputValue(e.target.value)}\n' +
        '      />\n' +
        '    )}\n' +
        '    value={value}\n' +
        '    onAdd={() => {\n' +
        '      // here, you would put your custom logic instead, e.g. opening a modal\n' +
        '      setOptions((options) => {\n' +
        '        return [\n' +
        '          ...options,\n' +
        '          { label: inputValue, value: inputValue }\n' +
        '        ];\n' +
        '      });\n' +
        '    }}\n' +
        '    onSelect={(option) => {\n' +
        '      setValue(option);\n' +
        `      setInputValue(option ? option.label : '');\n` +
        '    }}\n' +
        '  />\n' +
        ');'
      }
    />
  </div>
</ThemeProvider>

If your options are fetched asynchronously, it is helpful to display a loading state. Also, you would want to disable the ability automatically filter (since your backend is doing that already).

<ThemeProvider theme={CWBTheme}>
  <Canvas style={{ marginBottom: '0' }}>
    <div style={{ height: '180px' }}>
      {createElement(() => {
        const [isLoading, setIsLoading] = useState(true);
        const [options, setOptions] = useState([
          { label: 'Option 1', value: 1 },
          { label: 'Option 2', value: 2 },
          { label: 'Option 3', value: 3 }
        ]);
        const [value, setValue] = useState(null);
        const [inputValue, setInputValue] = useState('');
        const getOptions = () => {
          setIsLoading(true);
          setTimeout(() => {
            setIsLoading(false);
          }, 2000);
        };
        const handleChange = (e) => {
          if (inputValue.length === 1 && e.target.value.length === 2) {
            getOptions();
          }
          setInputValue(e.target.value);
        };
        return (
          <Autocomplete
            isLoading={isLoading}
            noFilter
            options={options}
            renderInput={(props) => (
              <TextInput
                {...props}
                placeholder="Start typing..."
                value={inputValue}
                onChange={handleChange}
              />
            )}
            value={value}
            onSelect={(option) => {
              setValue(option);
              setInputValue(option ? option.label : '');
            }}
          />
        );
      })}
    </div>
  </Canvas>
  <div style={{ marginTop: '-20px' }}>
    <Source
      code={
        'const [isLoading, setIsLoading] = useState(true);\n' +
        'const [options, setOptions] = useState([\n' +
        `  { label: 'Option 1', value: 1 },\n` +
        `  { label: 'Option 2', value: 2 },\n` +
        `  { label: 'Option 3', value: 3 }\n` +
        ']);\n' +
        'const [value, setValue] = useState(null);\n' +
        `const [inputValue, setInputValue] = useState('');\n\n` +
        'const getOptions = () => {\n' +
        '  setIsLoading(true);\n' +
        '  setTimeout(() => {\n' +
        '    setIsLoading(false);\n' +
        '  }, 2000);\n' +
        '};\n\n' +
        'const handleChange = (e) => {\n' +
        '  // this is here to simulate fetching the data asynchronously\n' +
        '  if (inputValue.length === 1 && e.target.value.length === 2) {\n' +
        '    getOptions();\n' +
        '  }\n' +
        '  setInputValue(e.target.value)\n' +
        '};\n\n' +
        'return (\n' +
        '  <Autocomplete\n' +
        '    isLoading={isLoading}\n' +
        '    noFilter\n' +
        '    options={options}\n' +
        '    renderInput={(props) => (\n' +
        '      <TextInput\n' +
        '        {...props}\n' +
        '        placeholder="Start typing..."\n' +
        '        value={inputValue}\n' +
        '        onChange={handleChange}\n' +
        '      />\n' +
        '    )}\n' +
        '    value={value}\n' +
        '    onSelect={(option) => {\n' +
        '      setValue(option);\n' +
        `      setInputValue(option ? option.label : '');\n` +
        '    }}\n' +
        '  />\n' +
        ');'
      }
    />
  </div>
</ThemeProvider>

## Props

<ArgsTable />
