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

feat(ui): Add focus trigger to QuestionTooltip #78836

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

gggritso
Copy link
Member

@gggritso gggritso commented Oct 9, 2024

QuestionTooltip has an unpleasant A11y downside, in which it doesn't appear on keyboard focus because it doesn't receive keyboard focus. This PR wraps it in an invisible button which receives focus, and makes it possible to see the tooltip while navigating with a keyboard.

The problem is, I'm a little wary of using Button. I want the focus state from it (the border including the animation) but it has a lot of styling I had to override. Is there a better way to achieve this? I could rip those styles out, but I didn't like that, either.

Also, it would probably be nice to use React ARIA's Tooltip but one thing at a time.

e.g.,
Screenshot 2024-10-09 at 10 20 00 AM

@github-actions github-actions bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Oct 9, 2024
@gggritso gggritso requested a review from a team October 9, 2024 14:25
Copy link

codecov bot commented Oct 9, 2024

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
7948 1 7947 0
View the top 1 failed tests by shortest run time
Flamegraph renders a missing profile Flamegraph renders a missing profile
Stack Traces | 1.09s run time
Error: Expected test not to call console.error().

If the error is expected, test for it explicitly by mocking it out using jest.spyOn(console, 'error').mockImplementation() and test that the warning occurs.

Warning: validateDOMNesting(...): <button> cannot appear as a descendant of <button>.
    at button
    at .../app/components/button.tsx:4191:3
    at .../sentry/sentry/node_modules/@.../react/dist/emotion-element-48d2c2e4.cjs.dev.js:62:23
    at BaseButton (.../app/components/button.tsx:3592:3)
    at Button
    at .../sentry/sentry/node_modules/@.../react/dist/emotion-element-48d2c2e4.cjs.dev.js:62:23
    at span
    at Tooltip (.../app/components/tooltip.tsx:531:3)
    at span
    at .../sentry/sentry/node_modules/@.../react/dist/emotion-element-48d2c2e4.cjs.dev.js:62:23
    at QuestionTooltip (.../app/components/questionTooltip.tsx:248:3)
    at span
    at button
    at .../sentry/sentry/node_modules/@.../react/dist/emotion-element-48d2c2e4.cjs.dev.js:62:23
    at div
    at .../sentry/sentry/node_modules/@.../react/dist/emotion-element-48d2c2e4.cjs.dev.js:62:23
    at div
    at .../sentry/sentry/node_modules/@.../react/dist/emotion-element-48d2c2e4.cjs.dev.js:62:23
    at div
    at .../sentry/sentry/node_modules/@.../react/dist/emotion-element-48d2c2e4.cjs.dev.js:62:23
    at div
    at .../sentry/sentry/node_modules/@.../react/dist/emotion-element-48d2c2e4.cjs.dev.js:62:23
    at FlamegraphTreeTable (.../flamegraph/flamegraphDrawer/flamegraphTreeTable.tsx:1937:3)
    at div
    at .../sentry/sentry/node_modules/@.../react/dist/emotion-element-48d2c2e4.cjs.dev.js:62:23
    at FlamegraphDrawer (.../flamegraph/flamegraphDrawer/flamegraphDrawer.tsx:2464:11)
    at div
    at .../sentry/sentry/node_modules/@.../react/dist/emotion-element-48d2c2e4.cjs.dev.js:62:23
    at div
    at .../sentry/sentry/node_modules/@.../react/dist/emotion-element-48d2c2e4.cjs.dev.js:62:23
    at div
    at .../sentry/sentry/node_modules/@.../react/dist/emotion-element-48d2c2e4.cjs.dev.js:62:23
    at FlamegraphLayout (.../profiling/flamegraph/flamegraphLayout.tsx:2550:22)
    at Flamegraph (.../profiling/flamegraph/flamegraph.tsx:12644:23)
    at div
    at .../sentry/sentry/node_modules/@.../react/dist/emotion-element-48d2c2e4.cjs.dev.js:62:23
    at FlamegraphThemeProvider (.../profiling/flamegraph/flamegraphThemeProvider.tsx:416:18)
    at ProfileGroupProvider (.../views/profiling/profileGroupProvider.tsx:927:13)
    at ProfileGroupTypeProvider (.../views/profiling/profileFlamechart.tsx:872:3)
    at FlamegraphStateProvider (.../flamegraph/flamegraphStateProvider/flamegraphContextProvider.tsx:752:49)
    at SentryDocumentTitle (.../app/components/sentryDocumentTitle.tsx:708:3)
    at ProfileFlamegraph (.../views/profiling/profileFlamechart.tsx:605:11)
    at ProfilesProvider (.../views/profiling/profilesProvider.tsx:1471:3)
    at ProfilesAndTransactionProvider (.../views/profiling/profilesProvider.tsx:1387:11)
    at GlobalDrawer (.../components/globalDrawer/index.tsx:1117:3)
    at routeContext (.../sentry/sentry/node_modules/react-router/lib/hooks.tsx:658:26)
    at RenderErrorBoundary (.../sentry/sentry/node_modules/react-router/lib/hooks.tsx:584:5)
    at routes (.../sentry/sentry/node_modules/react-router-dom/index.tsx:762:3)
    at basenameProp (.../sentry/sentry/node_modules/react-router/lib/components.tsx:413:13)
    at fallbackElement (.../sentry/sentry/node_modules/react-router-dom/index.tsx:489:3)
    at QueryClientProvider (.../sentry/sentry/node_modules/@.../react-query/src/QueryClientProvider.tsx:30:3)
    at ThemeProvider (.../sentry/sentry/node_modules/@.../react/dist/emotion-element-48d2c2e4.cjs.dev.js:125:32)
    at .../js/sentry-test/reactTestingLibrary.tsx:74:5
    at console.captureMessage [as error] (.../sentry/sentry/node_modules/jest-fail-on-console/index.js:83:25)
    at printWarning (.../sentry/sentry/node_modules/react-dom/cjs/react-dom.development.js:86:30)
    at error (.../sentry/sentry/node_modules/react-dom/cjs/react-dom.development.js:60:7)
    at validateDOMNesting (.../sentry/sentry/node_modules/react-dom/cjs/react-dom.development.js:10849:7)
    at createInstance (.../sentry/sentry/node_modules/react-dom/cjs/react-dom.development.js:10930:5)
    at completeWork (.../sentry/sentry/node_modules/react-dom/cjs/react-dom.development.js:22187:28)
    at completeUnitOfWork (.../sentry/sentry/node_modules/react-dom/cjs/react-dom.development.js:26593:16)
    at performUnitOfWork (.../sentry/sentry/node_modules/react-dom/cjs/react-dom.development.js:26568:5)
    at workLoopSync (.../sentry/sentry/node_modules/react-dom/cjs/react-dom.development.js:26466:5)
    at renderRootSync (.../sentry/sentry/node_modules/react-dom/cjs/react-dom.development.js:26434:7)
    at performConcurrentWorkOnRoot (.../sentry/sentry/node_modules/react-dom/cjs/react-dom.development.js:25738:74)
    at flushActQueue (.../sentry/sentry/node_modules/react/cjs/react.development.js:2667:24)
    at act (.../sentry/sentry/node_modules/react/cjs/react.development.js:2582:11)
    at .../sentry/sentry/node_modules/@.../react/dist/act-compat.js:47:25
    at renderRoot (.../sentry/sentry/node_modules/@.../react/dist/pure.js:180:26)
    at Object.render (.../sentry/sentry/node_modules/@.../react/dist/pure.js:271:10)
    at render (.../js/sentry-test/reactTestingLibrary.tsx:179:14)
    at Object.<anonymous> (.../profiling/flamegraph/flamegraph.spec.tsx:144:37)
    at Promise.then.completed (.../sentry/sentry/node_modules/jest-circus/build/utils.js:298:28)
    at new Promise (<anonymous>)
    at callAsyncCircusFn (.../sentry/sentry/node_modules/jest-circus/build/utils.js:231:10)
    at _callCircusTest (.../sentry/sentry/node_modules/jest-circus/build/run.js:316:40)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at _runTest (.../sentry/sentry/node_modules/jest-circus/build/run.js:252:3)
    at _runTestsForDescribeBlock (.../sentry/sentry/node_modules/jest-circus/build/run.js:126:9)
    at _runTestsForDescribeBlock (.../sentry/sentry/node_modules/jest-circus/build/run.js:121:9)
    at run (.../sentry/sentry/node_modules/jest-circus/build/run.js:71:3)
    at runAndTransformResultsToJestFormat (.../sentry/sentry/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
    at jestAdapter (.../sentry/sentry/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
    at runTestInternal (.../sentry/sentry/node_modules/jest-runner/build/runTest.js:367:16)
    at runTest (.../sentry/sentry/node_modules/jest-runner/build/runTest.js:444:34)
    at Object.worker (.../sentry/sentry/node_modules/jest-runner/build/testWorker.js:106:12)
    at flushUnexpectedConsoleCalls (.../sentry/sentry/node_modules/jest-fail-on-console/index.js:48:13)
    at Object.<anonymous> (.../sentry/sentry/node_modules/jest-fail-on-console/index.js:139:7)
    at Promise.then.completed (.../sentry/sentry/node_modules/jest-circus/build/utils.js:298:28)
    at new Promise (<anonymous>)
    at callAsyncCircusFn (.../sentry/sentry/node_modules/jest-circus/build/utils.js:231:10)
    at _callCircusHook (.../sentry/sentry/node_modules/jest-circus/build/run.js:281:40)
    at _runTest (.../sentry/sentry/node_modules/jest-circus/build/run.js:254:5)
    at _runTestsForDescribeBlock (.../sentry/sentry/node_modules/jest-circus/build/run.js:126:9)
    at _runTestsForDescribeBlock (.../sentry/sentry/node_modules/jest-circus/build/run.js:121:9)
    at run (.../sentry/sentry/node_modules/jest-circus/build/run.js:71:3)
    at runAndTransformResultsToJestFormat (.../sentry/sentry/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
    at jestAdapter (.../sentry/sentry/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
    at runTestInternal (.../sentry/sentry/node_modules/jest-runner/build/runTest.js:367:16)
    at runTest (.../sentry/sentry/node_modules/jest-runner/build/runTest.js:444:34)
    at Object.worker (.../sentry/sentry/node_modules/jest-runner/build/testWorker.js:106:12)

To view individual test run time comparison to the main branch, go to the Test Analytics Dashboard

@@ -61,4 +65,11 @@ const QuestionIconContainer = styled('span')<Pick<QuestionProps, 'size' | 'class
}
`;

const TooltipTriggerButton = styled(Button)`
Copy link
Member

Choose a reason for hiding this comment

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

Already discussed in slack, but yeah, let's not use our button and just use a regular button element here. So we don't have to override styles (and instead apply styles)

afaik there's nothing we would want from our button component.

We should also give it a proper aria-label then too.

Really all of the usages of this component should pass some kind of label prop so we can indicate to the screen reader that this button will tell you more.

Copy link
Member

Choose a reason for hiding this comment

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

I think if we were to do this right, then the Tooltip component should ensure that whatever the trigger is, it is focusable. We could ensure this in different ways, but since tooltips are tied to a html trigger element, providing a tab index would probably be a good idea. The downside here is that this would require the children to implement the full html interface (something we pretty much never do, but should)

Rough example

function Tooltip(){
  return cloneElement(children, {tabIndex})
}

// implement full interface and pass it to the dom node
interface ButtonProps extends HTMLButtonProps {}

function Button(props){
  return <Buttton {...props}/>
}

@getsantry
Copy link
Contributor

getsantry bot commented Oct 31, 2024

This pull request has gone three weeks without activity. In another week, I will close it.

But! If you comment or otherwise update it, I will reset the clock, and if you add the label WIP, I will leave it alone unless WIP is removed ... forever!


"A weed is but an unloved flower." ― Ella Wheeler Wilcox 🥀

@getsantry getsantry bot added Stale and removed Stale labels Oct 31, 2024
@getsantry getsantry bot added the Stale label Nov 22, 2024
@getsantry
Copy link
Contributor

getsantry bot commented Nov 22, 2024

This pull request has gone three weeks without activity. In another week, I will close it.

But! If you comment or otherwise update it, I will reset the clock, and if you add the label WIP, I will leave it alone unless WIP is removed ... forever!


"A weed is but an unloved flower." ― Ella Wheeler Wilcox 🥀

@getsantry getsantry bot removed the Stale label Nov 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Scope: Frontend Automatically applied to PRs that change frontend components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants