Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds FormControl's "Auto-Wiring" to ActionMenu Component #4372

Conversation

JeffreyKozik
Copy link
Contributor

@JeffreyKozik JeffreyKozik commented Mar 8, 2024

Followup to previous discussions:

Changelog

New

Changed

Made use of the the new useFormControlForwardedProps hook (introduced by #3632) to automatically wire the ActionMenu component to the accessibility and validation provided by the FormControl component it's nested within. If ActionMenu isn't nested within FormControl the hook has no effect.

/**
* Make any component compatible with `FormControl`'s automatic wiring up of accessibility attributes & validation by
* reading the props from this hook and merging them with the passed-in props. If used outside of `FormControl`, this
* hook has no effect.
*
* @param externalProps The external props passed to this component. If provided, these props will be merged with the
* `FormControl` props, with external props taking priority.
*/
export function useFormControlForwardedProps<P>(externalProps: P): P & FormControlForwardedProps
/**
* Make any component compatible with `FormControl`'s automatic wiring up of accessibility attributes & validation by
* reading the props from this hook and handling them / assigning them to the underlying form control. If used outside
* of `FormControl`, this hook has no effect.
*/
export function useFormControlForwardedProps(): FormControlForwardedProps
export function useFormControlForwardedProps(externalProps: FormControlForwardedProps = {}) {

Within a form the accessible name is now the text within the corresponding FormControl.Label tag
Screenshot as evidence for the statement above
Outside of a form the accessible name is still the text within the ActionMenu.Button itself
Screenshot as evidence for the statement above

I also updated Jest snapshots for the Token component (all it did was rearrange class names). I'm unsure why it did this since it seems unrelated to the changes I made, but I'm not too concerned about it since we seem to be moving away from snapshot testing: https://github.com/primer/react/blob/cb54f4298700d369e616aaefb2e7df4830231736/contributor-docs/testing.md#snapshots.

Removed

Rollout strategy

  • Patch release
  • Minor release
  • Major release; if selected, include a written rollout or migration plan
  • None; if selected, include a brief description as to why

Testing & Reviewing

Merge checklist

@JeffreyKozik JeffreyKozik requested a review from a team as a code owner March 8, 2024 21:57
Copy link

changeset-bot bot commented Mar 8, 2024

🦋 Changeset detected

Latest commit: 14f60fe

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@primer/react Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Contributor

github-actions bot commented Mar 8, 2024

size-limit report 📦

Path Size
packages/react/dist/browser.esm.js 87.51 KB (-0.08% 🔽)
packages/react/dist/browser.umd.js 87.93 KB (-0.03% 🔽)

@github-actions github-actions bot temporarily deployed to storybook-preview-4372 March 8, 2024 22:01 Inactive
@JeffreyKozik JeffreyKozik self-assigned this Mar 11, 2024
@JeffreyKozik JeffreyKozik changed the title adds automatic wiring of a11y attributes to ActionMenu.Button adds automatic wiring of a11y attributes to ActionMenu.Button primer change Mar 11, 2024
@github-actions github-actions bot temporarily deployed to storybook-preview-4372 March 11, 2024 21:26 Inactive
@JeffreyKozik JeffreyKozik changed the title adds automatic wiring of a11y attributes to ActionMenu.Button primer change Adds FormControl's "Auto-Wiring" to ActionMenu Component Mar 12, 2024
@JeffreyKozik JeffreyKozik added the skip changeset This change does not need a changelog label Mar 12, 2024
@JeffreyKozik JeffreyKozik removed the skip changeset This change does not need a changelog label Mar 12, 2024
@github-actions github-actions bot temporarily deployed to storybook-preview-4372 March 12, 2024 21:33 Inactive
@primer primer deleted a comment from github-actions bot Mar 12, 2024
@siddharthkp
Copy link
Member

siddharthkp commented Mar 13, 2024

Thanks for creating a PR!

Leaving rewatch link for myself to catch up: https://github.rewatch.com/video/2xqmd8rdinwuuedx-accessibility-team-eng-office-hours-march-11-2024?t=60.69

}: ActionMenuProps) => {
const [combinedOpenState, setCombinedOpenState] = useProvidedStateOrCreate(open, onOpenChange, false)
const onOpen = React.useCallback(() => setCombinedOpenState(true), [setCombinedOpenState])
const onClose = React.useCallback(() => setCombinedOpenState(false), [setCombinedOpenState])
const inputProps = useFormControlForwardedProps(externalInputProps)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the hook recently created by this PR: #3632. This hook is also being used in the InlineAutocomplete draft component:

const inputProps = useFormControlForwardedProps(externalInputProps)

@@ -69,7 +72,7 @@ const Menu: React.FC<React.PropsWithChildren<ActionMenuProps>> = ({
renderAnchor = anchorProps => {
// We need to attach the anchor props to the tooltip trigger (ActionMenu.Button's grandchild) not the tooltip itself.
const triggerButton = React.cloneElement(anchorChildren, {...anchorProps})
return React.cloneElement(child, {children: triggerButton, ref: anchorRef})
return React.cloneElement(child, {children: triggerButton, ref: anchorRef, ...inputProps})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -22,20 +22,6 @@ exports[`Token components AvatarToken renders all sizes 1`] = `
border-width: 0;
}

.c3 {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated snapshots as detailed in the contributor docs: https://github.com/primer/react/blob/cb54f4298700d369e616aaefb2e7df4830231736/contributor-docs/testing.md#snapshots

Not sure why these classes had to be rearranged as it's not clear to me how Token relates to my ActionMenu changes at all

</FormControl>
)
}

Copy link
Contributor Author

@JeffreyKozik JeffreyKozik Mar 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried adding a test case for TooltipV2

function ExampleWithTooltipV2WithForm(): JSX.Element {
  return (
    <FormControl>
      <FormControl.Label>Action Menu Label</FormControl.Label>
      <ThemeProvider theme={theme}>
        <SSRProvider>
          <BaseStyles>
            <ActionMenu>
              <TooltipV2 text="Additional context about the menu button" direction="s">
                <ActionMenu.Button>Toggle Menu</ActionMenu.Button>
              </TooltipV2>
              <ActionMenu.Overlay>
                <ActionList>
                  <ActionList.Item>New file</ActionList.Item>
                </ActionList>
              </ActionMenu.Overlay>
            </ActionMenu>
          </BaseStyles>
        </SSRProvider>
      </ThemeProvider>
    </FormControl>
  )
}
  it('MenuButton wrapped by TooltipV2 within FormControl should be labelled by FormControl.Label', async () => {
    const component = HTMLRender(<ExampleWithTooltipV2WithForm />)
    const button = component.getByLabelText('Action Menu Label')
    expect(button).toBeVisible()
    const buttonByRole = component.getByRole('button')
    expect(button.id).toBe(buttonByRole.id)
  })

But I got this error because the FormControl.Label ended up labelling the TooltipV2 instead of the ActionMenu.Button (presumably since the tooltip is wrapping the button).
image

If I try to force the TooltipV2 to act similarly to Tooltip (v1) and wrap the entire ActionMenu

function ExampleWithTooltipV2WithForm(): JSX.Element {
  return (
    <FormControl>
      <FormControl.Label>Action Menu Label</FormControl.Label>
      <ThemeProvider theme={theme}>
        <SSRProvider>
          <BaseStyles>
            <TooltipV2 text="Additional context about the menu button" direction="s">
              <ActionMenu>
                <ActionMenu.Button>Toggle Menu</ActionMenu.Button>
                <ActionMenu.Overlay>
                  <ActionList>
                    <ActionList.Item>New file</ActionList.Item>
                  </ActionList>
                </ActionMenu.Overlay>
              </ActionMenu>
            </TooltipV2>
          </BaseStyles>
        </SSRProvider>
      </ThemeProvider>
    </FormControl>
  )
}

I get this error since that's not how TooltipV2 is supposed to be used.
image

Either way I'm not concerned about not having a test including TooltipV2 since I can't imagine a valid use case for having an ActionMenu within a FormControl being labelled by a FormControl.Label and then also adding a tooltip on top. Furthermore our Primer a11y guidelines advise against using tooltips in most circumstances: https://primer.style/guides/accessibility/tooltip-alternatives#why.

@JeffreyKozik
Copy link
Contributor Author

JeffreyKozik commented Mar 13, 2024

Instead of auto-wiring the ActionMenu component, I wrote #4389 to auto-wire the SelectPanel v2 component since it's more fitting to be used in forms: https://github.slack.com/archives/C01L618AEP9/p1710345435224569?thread_ts=1710344884.849579&cid=C01L618AEP9

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants