Skip to content

Extremely weird behavior when virtualizing combobox and using down arrow #5930

@nathanmmiller

Description

@nathanmmiller

Latest version

  • I have tested the latest version

Description

I am using react-window FixedSizeList to virtualize the options in a combobox with too many items. Everything with this combobox works as expected, except when the ArrowDown key is pressed while the bottom element is already visible, it jumps to an element higher in the list of options (specifically, the one two above whatever is visible). The Up arrow does not have the same issue.

I believe this is caused by the way that s register themselves, specifically the code here in ListControlState:

  const register = useCallback(
    (optionValue: OptionValue<Item>, element: HTMLElement) => {
      const { id } = optionValue;

      optionsRef.current.push({ data: optionValue, element });

      return () => {
        optionsRef.current = optionsRef.current.filter(
          (item) => item.data.id !== id,
        );
      };
    },
    [],
  );

It seems like the visibility of the option is triggering the register to occur after it was already registered, so that it ends up "lower on the list" and then "getOptionAfter" is pulling this prior option as if it were next.

Steps to reproduce

The actual code is much more complex than the reproduction below, but the reproduction displays the issue. To reproduce, simply run this code, then click the combobox, and hit the down arrow key several times. When you get to "g" hit it one more time to see the jump upward.

Expected behavior

I expect, even when virtualizing combobox items, that the down arrow at the bottom of the list would do nothing.

import { ComboBox, Option } from "@salt-ds/core";
import * as React from "react";
import { FixedSizeList, ListChildComponentProps } from "react-window";

const RenderOption: React.FunctionComponent<ListChildComponentProps<string[]>> = ({
    index,
    style,
    data
}: ListChildComponentProps<string[]>) => (
    <Option key={data[index]} value={data[index]} style={style} id={`${data[index]}-${index}`}>
        {data[index]}
    </Option>
);

const SOURCE: string[] = ["a", "b", "c", "d", "e", "f", "g"];

const VirtualizedComboBox: React.FunctionComponent = () => (
    <ComboBox<string>>
        <FixedSizeList<string[]>
            height={3 * 36}
            width={"100%"}
            itemCount={SOURCE.length}
            itemSize={36}
            itemData={SOURCE}
        >
            {RenderOption}
        </FixedSizeList>
    </ComboBox>
);

Package name(s)

Core (@salt-ds/core)

Package version(s)

1.54.1

Browser

  • Chrome
  • Safari
  • Firefox
  • Microsoft Edge

Operating system

  • macOS
  • Windows
  • Linux
  • iOS
  • Android

Are you a JPMorgan Chase & Co. employee?

  • I am an employee of JPMorgan Chase & Co.

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: awaiting triageAutomatically added to new issues. Should be removed once they have been triaged.

    Type

    Projects

    Status

    Green

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions