【问题标题】:Input bug: cannot type 'S' into search input输入错误:无法在搜索输入中输入“S”
【发布时间】:2021-08-10 14:21:40
【问题描述】:

我几乎放弃了这个错误。 我只是无法在搜索输入中输入“S”。

键盘工作正常。

下面的沙盒。

https://codesandbox.io/s/jolly-raman-61zbx?file=/src/App.js

沙盒代码:

import {
  Box,
  FormControl,
  InputAdornment,
  ListItem,
  Menu,
  TextField,
  withStyles
} from "@material-ui/core";
import {
  clamp,
  difference,
  includes,
  intersection,
  join,
  map,
  union
} from "lodash";

import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown";
import { FixedSizeList } from "react-window";
import Fuse from "fuse.js";
import React from "react";
import SearchIcon from "@material-ui/icons/Search";
import memoize from "memoize-one";

const styles = {
  popoverPaper: {
    width: "100%"
  }
};

const fuseOptions = {
  includeScore: true
};

const initialSearchState = {
  searchValue: ""
};

const getCheckedList = (list) => (!!list ? list : []);

class VirtualisedSelector extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      anchorEl: null,
      ...initialSearchState
    };
  }

  getSearchList = memoize((list, searchValue) => {
    const fuse = new Fuse(list, fuseOptions);
    return !!searchValue ? map(fuse.search(searchValue), (o) => o.item) : list;
  });

  handleSearch = (event) => {
    this.setState({
      ...this.state,
      searchValue: event.target.value
    });
  };

  getCleanSelectedValues = memoize((currentSelectedValues, list) =>
    intersection(currentSelectedValues, list)
  );

  getNewMultipleValue = (currentSelectedValues, clickedItemValue) =>
    includes(currentSelectedValues, clickedItemValue)
      ? difference(currentSelectedValues, [clickedItemValue])
      : union(currentSelectedValues, [clickedItemValue]);

  getNewSingleValue = (currentSelectedValues, clickedItemValue) =>
    includes(currentSelectedValues, clickedItemValue) ? [] : [clickedItemValue];

  getNewValue = (currentSelectedValues, clickedItemValue, list, multiple) =>
    multiple
      ? this.getNewMultipleValue(
          this.getCleanSelectedValues(currentSelectedValues, list),
          clickedItemValue
        )
      : this.getNewSingleValue(currentSelectedValues, clickedItemValue);

  handleChange = (currentSelectedValues, list, multiple) => (event) => {
    this.setState({
      anchorEl: null
    });
    const clickedItemValue = event.target.getAttribute("value");
    const newValue = this.getNewValue(
      currentSelectedValues,
      clickedItemValue,
      list,
      multiple
    );
    this.props.onSelect(newValue);
  };

  getTextFieldDisplayValue = (value, list, labelMap) =>
    join(
      map(this.getCleanSelectedValues(value, list), (e) => labelMap[e] ?? e),
      ", "
    );

  handleMenuOpen = (event) => {
    this.setState({ anchorEl: event.currentTarget });
  };

  handleMenuClose = (event) => {
    this.setState({ anchorEl: null, ...initialSearchState });
  };

  renderRow = () => ({ index, style }) => {
    const { multiple, value, list, labelMap } = this.props;
    const { searchValue } = this.state;
    const searchList = this.getSearchList(list, searchValue);

    const cleanSelectedValues = this.getCleanSelectedValues(value, list);
    const listItem = searchList[index];
    return (
      <ListItem
        value={listItem}
        key={listItem}
        selected={includes(value, listItem)}
        onClick={this.handleChange(cleanSelectedValues, list, multiple)}
        style={style}
      >
        {labelMap?.[listItem] || listItem}
      </ListItem>
    );
  };

  render() {
    const {
      value,
      list,
      name,
      label,
      labelMap,
      required = false,
      classes
    } = this.props;
    const { searchValue } = this.state;
    const formControlClassNames = this.props?.classes?.formControl;

    const searchList = this.getSearchList(getCheckedList(list), searchValue);
    const nItems = searchList.length;
    const stringHeight = 46;
    const fixedSizeListHeight = stringHeight * clamp(nItems, 1, 10);

    return (
      <FormControl className={formControlClassNames} fullWidth>
        <TextField
          onClick={this.handleMenuOpen}
          variant="outlined"
          required={required}
          InputProps={{
            endAdornment: (
              <InputAdornment position="end">
                <ArrowDropDownIcon />
              </InputAdornment>
            )
          }}
          value={this.getTextFieldDisplayValue(value, list, labelMap)}
          multiline
          type="text"
          name={name}
          label={label}
          fullWidth
        />
        <Menu
          anchorEl={this.state.anchorEl}
          keepMounted
          open={Boolean(this.state.anchorEl)}
          onClose={this.handleMenuClose}
          PopoverClasses={{ paper: classes.popoverPaper }}
        >
          {/* Search */}
          <Box p={1}>
            <TextField
              variant="outlined"
              InputProps={{
                startAdornment: (
                  <InputAdornment position="start">
                    <SearchIcon />
                  </InputAdornment>
                )
              }}
              type="text"
              label={"Search"}
              value={searchValue}
              onChange={this.handleSearch}
              fullWidth
            />
          </Box>

          {/* Virtualized list */}
          <FixedSizeList
            height={fixedSizeListHeight}
            width={"100%"}
            itemSize={stringHeight}
            itemCount={nItems}
          >
            {this.renderRow()}
          </FixedSizeList>
        </Menu>
      </FormControl>
    );
  }
}

VirtualisedSelector.defaultProps = {
  value: "",
  list: [],
  name: "",
  label: "",
  labelMap: {},
  required: false
};

export default withStyles(styles)(VirtualisedSelector);

【问题讨论】:

  • 请在问题中直接包含相关代码。您可以链接到场外以获取支持资源,但问题应该无需离开即可回答。
  • 我还没有设法找到问题所在,但我发现:1) 没有&lt;Menu&gt; 组件它可以正常工作,这意味着问题可能存在 2) 行为类似于@ 987654324@ 用作键盘导航的热键。

标签: javascript reactjs material-ui


【解决方案1】:

主要问题是您不应该为此使用MenuMenu 假定它有 MenuItem 子级并且具有针对该假设的可访问性功能。您看到的行为是由尝试通过键入菜单项文本开头的字符来导航到菜单项的功能引起的。在您的情况下,它正在查找标签“搜索”的文本,然后将焦点移至该“菜单项”(这就是为什么您随后会在包含您的 TextField 的 div 上获得焦点轮廓)。如果您将标签更改为“在此处输入”,您会发现“s”有效,但“t”无效。

我的建议是直接使用PopoverMenu 委托给您使用的主要功能的低级组件)。另一种选择是使用Autocomplete component,因为您似乎正在尝试使用Menu 和弹出窗口TextField 来执行您自己的Autocomplete 组件提供的自定义版本。

【讨论】:

    【解决方案2】:

    显然,这是 Material UI 的 MenuList 中的 issue。菜单组件internally uses MenuList,所以也有问题。

    您可以通过阻止 TextField 事件的 bubbling 来解决此问题(如 Github 线程中所述)。

    <TextField
      variant="outlined"
      InputProps={{
        startAdornment: (
          <InputAdornment position="start">
            <SearchIcon />
          </InputAdornment>
        )
      }}
      type="text"
      label={"Search"}
      value={searchValue}
      onChange={this.handleSearch}
      onKeyDown={(event) => event.stopPropagation()}
      fullWidth
    />
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-03-14
      • 2019-12-27
      • 2011-08-30
      • 2013-05-24
      • 1970-01-01
      • 2019-10-10
      相关资源
      最近更新 更多