Skip to content

Commit 001d51d

Browse files
authored
Merge pull request #2332 from tf/hotspot-tooltip-closing
Improve closing hotspot tooltips
2 parents dfb1e8b + ec083e9 commit 001d51d

File tree

9 files changed

+140
-9
lines changed

9 files changed

+140
-9
lines changed

entry_types/scrolled/package/spec/contentElements/hotspots/Hotspots/panZoomScroller-spec.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,46 @@ describe('Hotspots', () => {
501501

502502
expect(scroller.scrollTo).toHaveBeenCalled();
503503
});
504+
505+
it('scrolls pan zoom scroller when link button is clicked', async () => {
506+
const seed = {
507+
imageFileUrlTemplates: {large: ':id_partition/image.webp'},
508+
imageFiles: [{id: 1, permaId: 100}]
509+
};
510+
const configuration = {
511+
image: 100,
512+
enablePanZoom: 'always',
513+
areas: [
514+
{
515+
id: 1,
516+
outline: [[10, 20], [10, 30], [40, 30], [40, 20]],
517+
indicatorPosition: [20, 25],
518+
}
519+
],
520+
tooltipTexts: {
521+
1: {
522+
title: [{type: 'heading', children: [{text: 'Some title'}]}],
523+
link: [{type: 'heading', children: [{text: 'Some link'}]}]
524+
}
525+
},
526+
tooltipLinks: {
527+
1: {href: 'https://example.com', openInNewTab: true}
528+
}
529+
};
530+
531+
const user = userEvent.setup();
532+
const {container, getByRole, simulateScrollPosition} = renderInContentElement(
533+
<Hotspots configuration={configuration} />, {seed}
534+
);
535+
simulateScrollPosition('near viewport');
536+
537+
simulateIntersecting(container.querySelectorAll(`.${scrollerStyles.step}`)[1]);
538+
const scroller = container.querySelector(`.${scrollerStyles.scroller}`);
539+
scroller.scrollTo = jest.fn();
540+
await user.click(getByRole('link'));
541+
542+
expect(scroller.scrollTo).toHaveBeenCalled();
543+
});
504544
});
505545

506546
function clickableArea(container) {

entry_types/scrolled/package/spec/contentElements/hotspots/Hotspots/tooltipDisplay-spec.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,43 @@ describe('Hotspots', () => {
195195
expect(container.querySelector(`.${tooltipStyles.box}`)).not.toBeNull();
196196
});
197197

198+
it('hides tooltip when link button is clicked', async () => {
199+
const seed = {
200+
imageFileUrlTemplates: {large: ':id_partition/image.webp'},
201+
imageFiles: [{id: 1, permaId: 100}]
202+
};
203+
const configuration = {
204+
image: 100,
205+
areas: [
206+
{
207+
id: 1,
208+
outline: [[10, 20], [10, 30], [40, 30], [40, 20]],
209+
indicatorPosition: [20, 25],
210+
}
211+
],
212+
tooltipTexts: {
213+
1: {
214+
title: [{type: 'heading', children: [{text: 'Some title'}]}],
215+
link: [{type: 'heading', children: [{text: 'Some link'}]}]
216+
}
217+
},
218+
tooltipLinks: {
219+
1: {href: 'https://example.com', openInNewTab: true}
220+
}
221+
};
222+
223+
const user = userEvent.setup();
224+
const {container, getByRole, simulateScrollPosition} = renderInContentElement(
225+
<Hotspots configuration={configuration} />, {seed}
226+
);
227+
simulateScrollPosition('near viewport');
228+
229+
await user.hover(clickableArea(container));
230+
await user.click(getByRole('link'));
231+
232+
expect(container.querySelector(`.${tooltipStyles.box}`)).toBeNull();
233+
});
234+
198235
it('hides when backdrop element is intersecting content', async () => {
199236
const seed = {
200237
imageFileUrlTemplates: {large: ':id_partition/image.webp'},

entry_types/scrolled/package/spec/frontend/EditableLink-spec.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React from 'react';
33
import {EditableLink} from 'frontend';
44

55
import {renderInEntry} from 'support';
6+
import userEvent from '@testing-library/user-event';
67
import '@testing-library/jest-dom/extend-expect'
78

89
// Link behavior is tested in Link-spec.js.
@@ -25,4 +26,18 @@ describe('EditableLink', () => {
2526

2627
expect(getByRole('link')).toHaveClass('custom')
2728
});
29+
30+
it('supports onClick', async () => {
31+
const onClick = jest.fn(event =>
32+
event.preventDefault() // Prevent jsdom warning
33+
);
34+
const user = userEvent.setup();
35+
const {getByRole} = renderInEntry(
36+
<EditableLink href="https://example.com" onClick={onClick}>Some link</EditableLink>
37+
);
38+
39+
await user.click(getByRole('link'));
40+
41+
expect(onClick).toHaveBeenCalled();
42+
});
2843
});

entry_types/scrolled/package/spec/frontend/inlineEditing/EditableLink-spec.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ import '@testing-library/jest-dom/extend-expect'
1313
jest.mock('frontend/inlineEditing/useSelectLinkDestination');
1414

1515
describe('EditableLink', () => {
16+
beforeEach(() => {
17+
const rect = {width: 100, height: 20, top: 100, left: 100, bottom: 120, right: 200, x: 100, y: 100};
18+
Element.prototype.getClientRects = jest.fn(() => [rect]);
19+
Element.prototype.getBoundingClientRect = jest.fn(() => rect);
20+
});
21+
1622
useFakeTranslations({
1723
'pageflow_scrolled.inline_editing.change_link_destination': 'Change link destination',
1824
'pageflow_scrolled.inline_editing.select_link_destination': 'Select link destination',
@@ -145,4 +151,17 @@ describe('EditableLink', () => {
145151

146152
expect(onChange).toHaveBeenCalledWith(null);
147153
});
154+
155+
it('triggers onClick when tooltip is clicked', async () => {
156+
const onClick = jest.fn();
157+
const user = userEvent.setup();
158+
render(
159+
<EditableLink href="https://example.com" onClick={onClick}>Some link</EditableLink>
160+
);
161+
162+
await user.hover(screen.getByText('Some link'));
163+
await user.click(screen.getByRole('link'));
164+
165+
expect(onClick).toHaveBeenCalled();
166+
});
148167
});

entry_types/scrolled/package/src/contentElements/hotspots/Hotspots.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,11 @@ export function HotspotsImage({
193193
onMouseEnter={() => setHoveredIndex(index)}
194194
onMouseLeave={() => setHoveredIndex(-1)}
195195
onClick={() => setActiveIndex(index)}
196+
onLinkClick={event => {
197+
activateArea(-1);
198+
setHoveredIndex(-1);
199+
event.stopPropagation();
200+
}}
196201
onDismiss={() => activateArea(-1)} />
197202
);
198203
}

entry_types/scrolled/package/src/contentElements/hotspots/Tooltip.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export function Tooltip({
4848
imageFile, containerRect, keepInViewport, floatingStrategy,
4949
aboveNavigationWidgets,
5050
wrapperRef,
51-
onMouseEnter, onMouseLeave, onClick, onDismiss,
51+
onMouseEnter, onMouseLeave, onClick, onDismiss, onLinkClick
5252
}) {
5353
const {t: translateWithEntryLocale} = useI18n();
5454
const {t} = useI18n({locale: 'ui'});
@@ -110,7 +110,10 @@ export function Tooltip({
110110

111111
const dismiss = useDismiss(context, {
112112
outsidePressEvent: 'mousedown',
113-
outsidePress: event => !insidePagerButton(event.target)
113+
outsidePress: event => !insidePagerButton(event.target),
114+
capture: {
115+
outsidePress: false
116+
}
114117
});
115118

116119
const {getReferenceProps, getFloatingProps} = useInteractions([
@@ -198,7 +201,7 @@ export function Tooltip({
198201
light ? styles.light : styles.dark,
199202
{[styles.paddingForScrollButtons]: keepInViewport,
200203
[styles.minWidth]: presentOrEditing('link')})}
201-
onMouseEnter={onMouseEnter}
204+
onMouseEnter={() => storylineMode === 'active' && onMouseEnter()}
202205
onMouseLeave={onMouseLeave}
203206
onClick={onClick}
204207
{...getFloatingProps()}>
@@ -239,7 +242,8 @@ export function Tooltip({
239242
value={tooltipTexts[area.id]?.link}
240243
allowRemove={true}
241244
onTextChange={value => handleTextChange('link', value)}
242-
onLinkChange={value => handleLinkChange(value)} />}
245+
onLinkChange={value => handleLinkChange(value)}
246+
onClick={onLinkClick} />}
243247
</div>
244248
</div>
245249
</div>

entry_types/scrolled/package/src/frontend/EditableLink.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import {Link} from './Link';
55

66
export const EditableLink = withInlineEditingAlternative(
77
'EditableLink',
8-
function EditableLink({className, href, openInNewTab, children}) {
8+
function EditableLink({className, href, openInNewTab, onClick, children}) {
99
return (
1010
<Link href={href}
1111
openInNewTab={openInNewTab}
12-
attributes={{className}}
12+
attributes={{className, onClick}}
1313
children={children} />
1414
);
1515
}

entry_types/scrolled/package/src/frontend/inlineEditing/EditableLink.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import styles from './EditableLink.module.css';
1212
export function EditableLink({
1313
className, href, openInNewTab, children,
1414
onChange,
15+
onClick,
1516
linkPreviewDisabled,
1617
linkPreviewPosition = 'below',
1718
linkPreviewAlign = 'center',
@@ -43,6 +44,7 @@ export function EditableLink({
4344
<LinkTooltipProvider position={linkPreviewPosition}
4445
floatingStrategy={linkPreviewFloatingStrategy}
4546
align={linkPreviewAlign}
47+
onClick={onClick}
4648
gap={5}>
4749
<LinkPreview disabled={linkPreviewDisabled}
4850
href={href}

entry_types/scrolled/package/src/frontend/inlineEditing/LinkTooltip.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export function LinkTooltipProvider(props) {
3838
}
3939

4040
export function LinkTooltipProviderInner({
41-
disabled, position, floatingStrategy, children, align = 'left', gap = 10
41+
disabled, position, floatingStrategy, onClick, children, align = 'left', gap = 10
4242
}) {
4343
const storylineMode = useStorylineActivity();
4444
const [state, setState] = useState();
@@ -103,6 +103,7 @@ export function LinkTooltipProviderInner({
103103
floatingStyles={floatingStyles}
104104
floatingContext={floatingContext}
105105
arrowRef={arrowRef}
106+
onClick={onClick}
106107
disabled={disabled} />
107108
</FloatingPortal>
108109
{children}
@@ -130,17 +131,25 @@ export function LinkPreview({disabled, href, openInNewTab, children, className})
130131
);
131132
}
132133

133-
export function LinkTooltip({disabled, setFloating, floatingStyles, floatingContext, arrowRef, state}) {
134+
export function LinkTooltip({disabled, setFloating, floatingStyles, floatingContext, arrowRef, onClick, state}) {
134135
const {keep, deactivate} = useContext(UpdateContext);
135136

136137
if (disabled || !state || !state.href) {
137138
return null;
138139
}
139140

141+
function handleClick(event) {
142+
event.stopPropagation();
143+
144+
if (onClick) {
145+
onClick(event);
146+
}
147+
}
148+
140149
return (
141150
<div ref={setFloating}
142151
className={classNames(styles.linkTooltip)}
143-
onClick={e => e.stopPropagation()}
152+
onClick={handleClick}
144153
onMouseEnter={keep}
145154
onMouseLeave={deactivate}
146155
style={floatingStyles}>

0 commit comments

Comments
 (0)