-
Notifications
You must be signed in to change notification settings - Fork 116
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(annotations): tooltip #1364
Conversation
|
||
beforeEach(() => { | ||
jest.spyOn(React, 'useEffect').mockImplementation(cb => { | ||
unmount = cb() as () => void; // Enzyme unmount helper does not currently invoke useEffect cleanup | ||
if (!found) { |
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.
After adding TargetedClickThroughGuideTooltip
there is now more than one useEffect spied on. The imported useEffect from BUIE that is being spied on is https://github.com/box/box-ui-elements/blob/master/src/features/targeting/utils/useOnClickBody.js. Therefore I modified this to select the first one
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.
Can we mock AnnotationsTargetedTooltip
, instead? It doesn't look like it's under test at the moment.
{...rest} | ||
<TargetedClickThroughGuideTooltip | ||
body={__('annotations_tooltip_body')} | ||
className="preview-annotations-tooltip" |
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.
We've been using bp-
as the prefix for preview classnames
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.
We also use SUIT CSS naming conventions, so bp-AnnotationsTooltip
would work here.
if (this.wasClosedByUser.tooltipFlowAnnotationsExperience) { | ||
return false; | ||
} | ||
|
||
if (!this.experiences.tooltipFlowAnnotationsExperience) { | ||
return false; | ||
} | ||
|
||
return this.experiences.tooltipFlowAnnotationsExperience.canShow; |
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.
Is there a way this logic could be enhanced to scale to support more experiences?
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.
Let me know what you think of my solution that can be used by more experiences once I push my changes!
// Destroy timeouts on unmount | ||
React.useEffect(() => helpersRef.current.clean, []); | ||
|
||
return ( | ||
<div | ||
className={`bp-ControlsLayer ${isShown ? SHOW_CLASSNAME : ''}`} | ||
className={`bp-ControlsLayer ${isShown || forceShow ? SHOW_CLASSNAME : ''}`} |
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.
Does forceShow
need to be maintained separately from isShown
state? Or can it be rolled into the state value?
@@ -76,7 +82,9 @@ export default function AnnotationsControls({ | |||
isEnabled={showDrawing} | |||
mode={AnnotationMode.DRAWING} | |||
onClick={handleModeClick} | |||
setWasClosedByUser={setWasClosedByUser} |
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.
Does setWasClosedByUser
need to be passed down or can the tooltipFlowAnnotationsExperience
be modified here to include setWasClosedByUser
so that it can be passed transparently in the child component
@@ -67,7 +82,32 @@ export default class ControlsRoot { | |||
this.controlsEl.classList.remove('bp-is-hidden'); | |||
} | |||
|
|||
shouldForceShow(): boolean { | |||
if (this.wasClosedByUser.tooltipFlowAnnotationsExperience) { |
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.
Can we move this logic to another component? ControlsRoot is currently pretty generic and shouldn't know about what controls/experiments it's rendering. I'm thinking something like:
<ControlsLayer onMount={this.handleMount}>
<ControlsExperiments onClosed={this.helpers.reset()}>
{controls}
</ControlsExperiments>
</ControlsLayer>
@@ -10,13 +10,14 @@ export type Helpers = { | |||
|
|||
export type Props = { | |||
children: React.ReactNode; | |||
forceShow?: boolean; |
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.
Can we avoid having a separate prop for showing/hiding? It could get difficult to keep things synced up.
}; | ||
|
||
export default function AnnotationsControls({ | ||
annotationColor = bdlBoxBlue, | ||
annotationMode = AnnotationMode.NONE, | ||
experiences, |
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.
Can we avoid prop drilling by using context?
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.
Yes, I think that's a good idea!
{...rest} | ||
<TargetedClickThroughGuideTooltip | ||
body={__('annotations_tooltip_body')} | ||
className="preview-annotations-tooltip" |
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.
We also use SUIT CSS naming conventions, so bp-AnnotationsTooltip
would work here.
onClick={(): void => onClick(mode)} | ||
type="button" | ||
{...rest} | ||
<TargetedClickThroughGuideTooltip |
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.
Can this live in a separate component?
src/lib/Preview.js
Outdated
* @return {void} | ||
*/ | ||
updateExperiences(experiences) { | ||
this.options.experiences = experiences; |
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.
Do we need to keep a copy of this on the Preview instance, viewer instance, and controls instance? Keeping them all in sync seems to be adding some overhead.
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.
Cool! It looks like we don't need it in Preview instance 👍 We do need it in viewer instance to pass it to the context. I think we need it in controls but will continue to look
@@ -11,6 +13,8 @@ export type Props = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick | |||
isEnabled?: boolean; | |||
mode: AnnotationMode; | |||
onClick?: (mode: AnnotationMode) => void; | |||
setWasClosedByUser?: (experienceName: string | undefined) => void; | |||
tooltipFlowAnnotationsExperience?: TargetingApi; | |||
}; | |||
|
|||
export default function AnnotationsButton({ |
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.
Should every annotation button be wrapped in this tooltip? Will only one show?
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.
My first way of doing it was every AnnotationsButton was wrapped in this tooltip, but it was only ever shown for one due to the shouldTarget prop. I've moved the tooltip out to the Annotations Control component. Now it only wraps one button. Let me know what you think. I wasn't sure if creating another component was a good idea. Thank you!
@@ -20,22 +24,46 @@ export default function AnnotationsButton({ | |||
isEnabled = true, | |||
mode, | |||
onClick = noop, | |||
setWasClosedByUser = noop, | |||
tooltipFlowAnnotationsExperience = { |
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.
Can we check for this value being defined and not render the tooltip if it's missing?
Still WIP, I'm trying to figure out how to hide/show control bar when tooltip is showing & closed |
Thank you @jstoffan for your help! I referenced your example of using 2 different contexts. The only thing I had to add was a |
src/i18n/en-US.properties
Outdated
@@ -212,6 +212,10 @@ annotations_create_error=We’re sorry, the annotation could not be created. | |||
annotations_delete_error=We’re sorry, the annotation could not be deleted. | |||
# Text for when the authorization token is invalid | |||
annotations_authorization_error=Your session has expired. Please refresh the page. | |||
# Text for annotations tooltip body |
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.
Can we add that this is a first-time user experience tooltip to the description? Our localization service uses this string to help translate the content properly.
import IconDrawing24 from '../icons/IconDrawing24'; | ||
import IconHighlightText16 from '../icons/IconHighlightText16'; | ||
import IconRegion24 from '../icons/IconRegion24'; | ||
import useFullscreen from '../hooks/useFullscreen'; | ||
import { AnnotationMode } from '../../../types'; | ||
|
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.
We typically group all imports together without whitespace.
> | ||
<IconDrawing24 fill={isDrawingActive ? annotationColor : '#fff'} /> | ||
</AnnotationsButton> | ||
<AnnotationsTargetedTooltip isEnabled={showDrawing}> |
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.
Do we have a preference for which annotation type we want to draw the most attention to? It seems like the choice would be between drawing and regions, the latter of which may be a bit simpler of a user experience for a first-time user. It may be worth checking with the product team on this question.
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 will confirm with product 👍
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.
Thanks, it looks like they prefer the comment region button!
@@ -0,0 +1,57 @@ | |||
import React from 'react'; | |||
import TargetedClickThroughGuideTooltip from 'box-ui-elements/es/features/targeting/TargetedClickThroughGuideTooltip'; |
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 this relies on react-tether
. Can we ensure that we're using the same version BUIE expects? Can we also do a comparison of the production bundle size before and after this change?
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 updated react-tether to 1.0.5. I will compare the bundle sizes now...
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 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.
Hm, that seems like a big jump given the nature of the change. Can we use the bundle analyzer to make sure we're not pulling in code we shouldn't be, like another version of React or something?
We should also check the built CSS file to make sure we're not bringing in extra global styles from the Button components in GuideTooltip. I think we've run into issues around that before.
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.
); | ||
|
||
if (!shouldTarget) { | ||
return <div>{children}</div>; |
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.
Can we omit the wrapping div
here?
|
||
beforeEach(() => { | ||
jest.spyOn(React, 'useEffect').mockImplementation(cb => { | ||
unmount = cb() as () => void; // Enzyme unmount helper does not currently invoke useEffect cleanup | ||
if (!found) { |
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.
Can we mock AnnotationsTargetedTooltip
, instead? It doesn't look like it's under test at the moment.
import { TargetingApi } from '../../../types'; | ||
|
||
export type Props = React.PropsWithChildren<{ | ||
experiences?: { |
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.
Can we use the shared Experiences
type here?
|
||
expect(wrapper.children().text()).not.toBe('Child'); | ||
expect(wrapper.children().prop('shouldTarget')).toBe(true); | ||
expect(wrapper.children().prop('body')).toBeTruthy(); |
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.
Can we use more explicit assertions here?
@@ -1,5 +1,7 @@ | |||
import React from 'react'; | |||
import noop from 'lodash/noop'; | |||
import ControlsLayerContext from './ControlsLayerContext'; | |||
|
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.
We typically group our imports without extra whitespace.
@@ -0,0 +1,57 @@ | |||
import React from 'react'; | |||
import TargetedClickThroughGuideTooltip from 'box-ui-elements/es/features/targeting/TargetedClickThroughGuideTooltip'; |
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.
Hm, that seems like a big jump given the nature of the change. Can we use the bundle analyzer to make sure we're not pulling in code we shouldn't be, like another version of React or something?
We should also check the built CSS file to make sure we're not bringing in extra global styles from the Button components in GuideTooltip. I think we've run into issues around that before.
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.
This is looking great!
src/lib/viewers/controls/annotations/AnnotationsTargetedTooltip.tsx
Outdated
Show resolved
Hide resolved
src/lib/viewers/controls/annotations/__tests__/AnnotationsTargetedTooltip-test.tsx
Outdated
Show resolved
Hide resolved
src/lib/viewers/controls/annotations/AnnotationsTargetedTooltip.tsx
Outdated
Show resolved
Hide resolved
src/lib/viewers/controls/annotations/AnnotationsTargetedTooltip.tsx
Outdated
Show resolved
Hide resolved
src/lib/viewers/controls/annotations/AnnotationsTargetedTooltip.scss
Outdated
Show resolved
Hide resolved
src/lib/viewers/controls/annotations/AnnotationsTargetedTooltip.scss
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.
🚀
this.preview.updateExperiences
will be called whenever the experiences props passed in updates feat(content-preview): update props in content preview box-ui-elements#2549forceShow
prop toControlsLayer
ComponentsetWasClosedByUser