reactjstypescriptshopifyshopify-apppolaris

Using Polaris Combobox with @shopify/react-form


Is there a way to directly hook up the Polaris Combobox to @shopify/react-form?

Following the default example for the Combobox where the available options are filtered as you type, it requires hooking into onChange which is under the control of useForm when hooked up directly.

Perhaps there's some good examples of this scenario out there in the wild that I've not managed to find yet?


Solution

  • The solution I came up with was to take the default example for the Combobox, wrap it in a component, then pass in the field properties from useForm.

    <SearchableCombobox 
      options={data.timezones.map((timezone) => timezone.label)}
      label="Time zone"
      helpText="The time zone used by CargoWise for saving dates."
      value={fields.timezone.value}
      onChange={fields.timezone.onChange}
      onBlur={fields.timezone.onBlur}
      error={fields.timezone.error}
    />
    

    Then in the component, modifying the original example code to use the useForm properties and passing along the onChange event:

    <Combobox.TextField
        prefix={<Icon source={SearchIcon} />}
        label={props.label}
        labelHidden={typeof props.label === 'undefined'}
        helpText={props.helpText}
        value={inputValue}
        placeholder={props.placeholder}
        autoComplete="off"
        onBlur={props.onBlur}
        onChange={updateText}
        error={props.error}
    />
    
    const updateText = useCallback(
        (value: string) => {
            setInputValue(value);
    
            if (value === '') {
                setOptions(deselectedOptions);
                if (props.onChange) {
                    // Pass the underlying value along
                    props.onChange(value);
                }
                return;
            }
    
            const filterRegex = new RegExp(escapeSpecialRegExCharacters(value), 'i');
            const resultOptions = deselectedOptions.filter((option) =>
                option.match(filterRegex),
            );
            setOptions(resultOptions);
        },
        [deselectedOptions, escapeSpecialRegExCharacters],
    );
    
    const updateSelection = useCallback(
        (selected: string) => {
            const filterRegex = new RegExp(escapeSpecialRegExCharacters(selected), 'i');
            const matchedOption = options.find((option) => {
                return option.match(filterRegex);
            });
    
            setSelectedOption(selected);
            setInputValue(matchedOption || '');
    
            if (props.onChange) {
                // Pass the underlying value along
                props.onChange(matchedOption);
            }
        },
        [options],
    );