-
Notifications
You must be signed in to change notification settings - Fork 130
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
Feature: Add tooltip component #1274
Conversation
Thanks for your contribution! Depending on what you are working on, you may want to request a review from a Shopify team:
|
Benchmark reportThe following table contains a summary of the startup time for all commands.
|
Coverage report
Show files with reduced coverage 🔻
Test suite run success980 tests passing in 498 suites. Report generated by 🧪jest coverage report action from 8af58c2 |
We detected some changes at packages/*/src and there are no updates in the .changeset. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GJ, approving so you can merge without another review, but I'd love it if we addressed some of the feedback. Totally don't have to address it all though.
packages/ui-extensions-dev-console/src/components/Tooltip/Tooltip.tsx
Outdated
Show resolved
Hide resolved
packages/ui-extensions-dev-console/src/components/Tooltip/TooltipPopover.module.css
Outdated
Show resolved
Hide resolved
position: Position | ||
} | ||
|
||
export type TooltipAction = {type: 'show' | 'hide'} | {type: 'position'; payload: Position} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pros & Cons of a seperate types file? It's something we don't do anywhere else, and I think the types are only used in one file, so I'm wondering what we are gaining.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a couple of types that overlap between the Tooltip and TooltipPopover components (specifically, Position appears in multiple places).
We could remove the file and shove the types into their respective files, but we would need to either duplicate Position, or simply write it out in each type that is using it (ie prop: {x: number, y: number}
)
default: | ||
throw new Error() | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like there are just two actions here:
- Show, which sets isVisible and the position
- Hide, which sets is visible
Is a reducer overkill?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps a case of YAGNI but my assumption was that by using a reducer we would be able to easily extend it in the future. Is there a performance impact of using a reducer over setState x2?
ref={ref} | ||
> | ||
{children} | ||
{state.isVisible && <TooltipPopover position={state.position} text={text} />} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this means keyboard users won't get any value from the tooltip? How easy would it be to architect this so that keyboard users do get some value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking into this now. We can add a tabIndex={0}
prop to the component's div element, but the downside for anything clickable we need an additional tab to get into that in its current state.
I think that I could remedy this by using cloneElement
and applying the props from the parent div. I'll look into this today and let you know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the second time now, it looks like cloneElement isn't needed. tabIndex={ typeof children === 'string' ? 0 : undefined }
gives us the behaviour we want
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some functionality with comments that appears to work. There was some weird behaviour when elements (such as buttons and links, anything focusable) were the child. The best solution I was able to come up with was to detect this, set the tabIndex of the first child to -1, and forward ENTER keypresses to the child. I believe this solves the keyboard navigation issues, but does present a complication in the event that we wish to add a tooltip to an input field. This could probably be fixed with a boolean prop to skip the added behaviour, or we could always detect the child node type.
<span className={styles.WithIcon}> | ||
{i18n.translate('extensionList.view')} | ||
<Tooltip text={i18n.translate('tooltips.viewColumnHeader')}> | ||
<Icon source={QuestionMarkMajor} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the color of the Icon doesn't match the deigns, please could we update it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Certainly. Need to modify the icon component and remove the svg fill from the extensions list.
The new version will have an optional muted
prop on the Icon component. In the future we may want to add the ability to colour them.
packages/ui-extensions-dev-console/src/components/Tooltip/TooltipPopover.module.css
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will merge pending final review on approach to better support keyboard nav
packages/ui-extensions-dev-console/src/components/Tooltip/Tooltip.tsx
Outdated
Show resolved
Hide resolved
ref={ref} | ||
> | ||
{children} | ||
{state.isVisible && <TooltipPopover position={state.position} text={text} />} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking into this now. We can add a tabIndex={0}
prop to the component's div element, but the downside for anything clickable we need an additional tab to get into that in its current state.
I think that I could remedy this by using cloneElement
and applying the props from the parent div. I'll look into this today and let you know.
ref={ref} | ||
> | ||
{children} | ||
{state.isVisible && <TooltipPopover position={state.position} text={text} />} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the second time now, it looks like cloneElement isn't needed. tabIndex={ typeof children === 'string' ? 0 : undefined }
gives us the behaviour we want
<span className={styles.WithIcon}> | ||
{i18n.translate('extensionList.view')} | ||
<Tooltip text={i18n.translate('tooltips.viewColumnHeader')}> | ||
<Icon source={QuestionMarkMajor} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Certainly. Need to modify the icon component and remove the svg fill from the extensions list.
The new version will have an optional muted
prop on the Icon component. In the future we may want to add the ability to colour them.
packages/ui-extensions-dev-console/src/components/Tooltip/TooltipPopover.module.css
Outdated
Show resolved
Hide resolved
default: | ||
throw new Error() | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps a case of YAGNI but my assumption was that by using a reducer we would be able to easily extend it in the future. Is there a performance impact of using a reducer over setState x2?
position: Position | ||
} | ||
|
||
export type TooltipAction = {type: 'show' | 'hide'} | {type: 'position'; payload: Position} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a couple of types that overlap between the Tooltip and TooltipPopover components (specifically, Position appears in multiple places).
We could remove the file and shove the types into their respective files, but we would need to either duplicate Position, or simply write it out in each type that is using it (ie prop: {x: number, y: number}
)
ref={ref} | ||
> | ||
{children} | ||
{state.isVisible && <TooltipPopover position={state.position} text={text} />} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some functionality with comments that appears to work. There was some weird behaviour when elements (such as buttons and links, anything focusable) were the child. The best solution I was able to come up with was to detect this, set the tabIndex of the first child to -1, and forward ENTER keypresses to the child. I believe this solves the keyboard navigation issues, but does present a complication in the event that we wish to add a tooltip to an input field. This could probably be fixed with a boolean prop to skip the added behaviour, or we could always detect the child node type.
Add Tooltip Component: https://github.com/Shopify/internal-cli-foundations/issues/526
Add Tooltip to Status Column: https://github.com/Shopify/internal-cli-foundations/issues/527
Updated with keyboard navigation and status column tooltips (with dotted underline). Now with viewport bound checks
tooltip-with-bound-checks.mp4
API/Usage (WIP)
The Tooltip component currently takes only one prop
text
and accepts a single child of either typeJSX.Element
orstring
.Notes:
There are currently no client boundary checks, so it's possible the tooltip can be off the screen under certain circumstances.The component has little accessibility support, save for it appears on focus (elements that are not "tabable" will not trigger with the tab key, ie. images) and the role of the container is set to "tooltip". This can be addressed later.Measuring impact
How do we know this change was effective? Please choose one: