【问题标题】:Overriding Option in Base Web Menu breaks keyboard bindings基本 Web 菜单中的覆盖选项会破坏键盘绑定
【发布时间】:2022-08-20 23:07:07
【问题描述】:

8月20日更新:@mark_reeder 和我在 Base Web Slack 上进行了后续聊天。 Mark 向我解释说,您可以将组件传递给 label 道具(而不是传递字符串)。所以现在,我没有用我自己的自定义组件覆盖 Option,而是将一个带有图标的自定义标签组件传递给 label。容易多了!再次感谢@mark_reeder。

8月16日更新:我接受了@mark_reeder 的回答。这解决了让焦点状态起作用的问题。但是,单击 \"Enter\" 以选择焦点元素仍然不起作用,我最终向正文添加了一个事件侦听器并执行了一些 kludegy 逻辑。我希望有人有更好的方法。


我是 Base Web 的新手。我建立了一个自定义的“更多选项”菜单(又名“烤肉串”菜单)。我希望选项具有图标,因此我对菜单组件中的选项进行了覆盖。看起来不错,但键盘绑定不再起作用。如果我注释掉覆盖,键盘绑定会起作用(但我会丢失图标)。

这是我的代码:

//MoreOptionsMenu.js

import React from \'react\'
import { StatefulPopover, PLACEMENT } from \'baseui/popover\'
import Overflow from \'baseui/icon/overflow\'
import { StatefulMenu } from \'baseui/menu\'
import IconOption from \'Components/Shared/IconOption/IconOption\'
import InvisibleButtonWrapper from \'Components/Shared/InvisibleButtonWrapper/InvisibleButtonWrapper\'

const MoreOptionsMenu = ({ items, placement = PLACEMENT.bottom, ariaLabel, id }) => {
  return (
    <StatefulPopover
      content={({ close }) => (
        <StatefulMenu
          items={items}
          overrides={{
            Option: {
              component: IconOption,
              props: {
                close,
                ariaLabel,
                id
              }
            },
            List: {
              style: ({ $theme }) => ({
                borderTopLeftRadius: \'6px\',
                borderTopRightRadius: \'6px\',
                borderBottomLeftRadius: \'6px\',
                borderBottomRightRadius: \'6px\',
                border: `1px solid ${$theme.colors.lightGray}`,
              })
            }
          }}
        />
      )}

      accessibilityType={\'tooltip\'}
      placement={placement}
    >
      <InvisibleButtonWrapper>
        <Overflow size={24} aria-label={ariaLabel} style={{ marginLeft: \'auto\', cursor: \'pointer\' }}/>
      </InvisibleButtonWrapper>
    </StatefulPopover>
  )
}

export default MoreOptionsMenu

//IconOptions
import React, { forwardRef } from \'react\'
import { useStyletron } from \'baseui\'
import { stringToKebabCase } from \'Shared/Utilities\'
import InvisibleButtonWrapper from \'Components/Shared/InvisibleButtonWrapper/InvisibleButtonWrapper\'

const IconOption = forwardRef(( { close, item, ariaLabel, id }, ref) => {
  const [css, theme] = useStyletron()
  return (
    <li
      role=\'option\'
      aria-disabled=\'false\'
      aria-selected=\'false\'
      id={stringToKebabCase(`${id}-${item.label}`)}
      className={css({
        display: \'flex\',
        alignItems: \'center\',
        padding: \'10px\',
        cursor: \'pointer\',
        \':hover\': {
          outline: `${theme.colors.accent} solid 3px`
        },
        \':focus\': {
          outline: `${theme.colors.accent} solid 3px`
        },
        \':active\': {
          outline: `${theme.colors.accent} solid 3px`
        },
        \':nth-child(even)\': {
          backgroundColor: theme.colors.lighterGray
        }
      })}
      aria-labelledby={ariaLabel}
      ref={ref}
      onClick={() => {
        typeof item.callback === \'function\' && item.callback()
        close()
      }}>
      <InvisibleButtonWrapper>
        {item.icon}
        <span style={{ marginLeft: \'10px\' }}>{item.label}</span>
      </InvisibleButtonWrapper>
    </li>
  )
})

export default IconOption

// InvisibleButtonWrapper.js

import { withStyle } from \'baseui\'
import { StyledBaseButton } from \'baseui/button\'

const InvisibleButtonWrapper = withStyle(StyledBaseButton, ({$theme}) => ({
  paddingTop: 0,
  paddingBottom: 0,
  paddingLeft: 0,
  paddingRight: 0,
  color: `${$theme.colors.primaryA}`,
  backgroundColor: \'inherit\',
  \':hover\': {
    color: `${$theme.colors.primaryA}`,
    backgroundColor: \'inherit\',
  }
}))

export default InvisibleButtonWrapper

这是sandbox所以你可以看到/玩代码

多做一些笔记,以期人们可能会遇到的问题。我使用 forwardRef 的原因是我收到关于函数组件无法接收引用的错误。无论我是否使用 forwardRef,键盘绑定都不起作用。 InvisibleButtonWrapper 旨在在不破坏样式的情况下使组件更易于访问。删除它似乎不会影响键盘绑定。

另外,是否有一种惯用的 Base Web 方式来做到这一点,我错过了?文档说“每个菜单项都有一个默认包含图标的选项,但这可以删除。”但是,没有关于如何使其工作的示例。在我创建自己的自定义组件之前,我尝试简单地向项目添加一个图标属性,但它没有呈现。


最新的文件更新,来自 mark_reeder 的修复和上述文档上的事件侦听器:

import React, { forwardRef, useEffect } from \'react\'
import { useStyletron } from \'baseui\'
import { stringToKebabCase } from \'Shared/Utilities\'
import InvisibleButtonWrapper from \'Components/Shared/InvisibleButtonWrapper/InvisibleButtonWrapper\'

const IconOption = forwardRef((props, ref) => {
  const [css, theme] = useStyletron()
  const { close, item, ariaLabel, id, $isHighlighted, $isFocused } = props
  
  const handleKeyDown = ({ code }) => {
    if (code === \'Enter\') {
      document.querySelector(\"[data-iconoption=\'true\'][aria-selected=\'true\']\").click()
    }
  }

  useEffect(() => {
    document.addEventListener(\'keydown\', handleKeyDown)
      return () => {
        document.removeEventListener(\'keydown\', handleKeyDown)
      }
  }, [])

  return (
    <li
      data-iconoption={\'true\'}
      tabIndex={0}
      role=\'option\'
      aria-selected={props[\'aria-selected\'] ? \'true\' : \'false\'}
      id={stringToKebabCase(`${id}-${item.label}`)}
      className={css({
        backgroundColor: $isHighlighted ? theme.colors.lighterGray : theme.colors.white,
        display: \'flex\',
        alignItems: \'center\',
        padding: \'10px\',
        cursor: \'pointer\',
        \':hover\': {
          backgroundColor: theme.colors.lighterGray
        },
        \':focus\': {
          backgroundColor: theme.colors.lighterGray
        },
      })}
      aria-labelledby={ariaLabel}
      ref={ref}
      onClick={() => {
        typeof item.callback === \'function\' && item.callback()
        close()
      }}>
      <InvisibleButtonWrapper>
        {item.icon}
        <span style={{ marginLeft: \'10px\' }}>{item.label}</span>
      </InvisibleButtonWrapper>
    </li>
  )
})

export default IconOption

    标签: reactjs accessibility baseweb


    【解决方案1】:

    基础菜单组件依靠 props 来显示高亮的菜单项:https://github.com/uber/baseweb/blob/master/src/menu/styled-components.tsx#L50

    对您的IconOption 进行两项更改将使您的大纲显示出来:

    首先,将$isHighlighted 添加到IconOption 的道具列表中 -

    const IconOption = forwardRef(({ $isHighlighted, close, item, ariaLabel, id }, ref) => {
    

    其次,根据$isHighlighted的值,将li中的li的样式修改为outline

    outline: $isHighlighted ? `${theme.colors.accent} solid 3px` : 'none',
    

    至于更多 Idiomatic Base Web 执行此操作的方式,有一些示例包括可能对您有用的图像,例如 https://baseweb.design/components/menu/#menu-with-profile-menu

    【讨论】:

    • 谢谢!现在焦点样式起作用了。现在,我试图弄清楚如何在项目集中后通过键盘触发行动号召。输入键或任何其他键都不会发生任何事情。我尝试添加 onKeyDown。当那不起作用时,我尝试添加 tabIndex={0} 仍然没有骰子。有什么想法吗?谢谢!
    猜你喜欢
    • 1970-01-01
    • 2016-08-24
    • 2016-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-30
    • 2011-10-10
    相关资源
    最近更新 更多