diff --git a/entry_types/scrolled/package/spec/contentElements/hotspots/Hotspots/panZoomScroller-spec.js b/entry_types/scrolled/package/spec/contentElements/hotspots/Hotspots/panZoomScroller-spec.js index ecc60c21fa..9f232e9fd5 100644 --- a/entry_types/scrolled/package/spec/contentElements/hotspots/Hotspots/panZoomScroller-spec.js +++ b/entry_types/scrolled/package/spec/contentElements/hotspots/Hotspots/panZoomScroller-spec.js @@ -501,6 +501,46 @@ describe('Hotspots', () => { expect(scroller.scrollTo).toHaveBeenCalled(); }); + + it('scrolls pan zoom scroller when link button is clicked', async () => { + const seed = { + imageFileUrlTemplates: {large: ':id_partition/image.webp'}, + imageFiles: [{id: 1, permaId: 100}] + }; + const configuration = { + image: 100, + enablePanZoom: 'always', + areas: [ + { + id: 1, + outline: [[10, 20], [10, 30], [40, 30], [40, 20]], + indicatorPosition: [20, 25], + } + ], + tooltipTexts: { + 1: { + title: [{type: 'heading', children: [{text: 'Some title'}]}], + link: [{type: 'heading', children: [{text: 'Some link'}]}] + } + }, + tooltipLinks: { + 1: {href: 'https://example.com', openInNewTab: true} + } + }; + + const user = userEvent.setup(); + const {container, getByRole, simulateScrollPosition} = renderInContentElement( + , {seed} + ); + simulateScrollPosition('near viewport'); + + simulateIntersecting(container.querySelectorAll(`.${scrollerStyles.step}`)[1]); + const scroller = container.querySelector(`.${scrollerStyles.scroller}`); + scroller.scrollTo = jest.fn(); + await user.click(getByRole('link')); + + expect(scroller.scrollTo).toHaveBeenCalled(); + }); }); function clickableArea(container) { diff --git a/entry_types/scrolled/package/spec/contentElements/hotspots/Hotspots/tooltipDisplay-spec.js b/entry_types/scrolled/package/spec/contentElements/hotspots/Hotspots/tooltipDisplay-spec.js index 2d1e726d7a..f2a84adc97 100644 --- a/entry_types/scrolled/package/spec/contentElements/hotspots/Hotspots/tooltipDisplay-spec.js +++ b/entry_types/scrolled/package/spec/contentElements/hotspots/Hotspots/tooltipDisplay-spec.js @@ -195,6 +195,43 @@ describe('Hotspots', () => { expect(container.querySelector(`.${tooltipStyles.box}`)).not.toBeNull(); }); + it('hides tooltip when link button is clicked', async () => { + const seed = { + imageFileUrlTemplates: {large: ':id_partition/image.webp'}, + imageFiles: [{id: 1, permaId: 100}] + }; + const configuration = { + image: 100, + areas: [ + { + id: 1, + outline: [[10, 20], [10, 30], [40, 30], [40, 20]], + indicatorPosition: [20, 25], + } + ], + tooltipTexts: { + 1: { + title: [{type: 'heading', children: [{text: 'Some title'}]}], + link: [{type: 'heading', children: [{text: 'Some link'}]}] + } + }, + tooltipLinks: { + 1: {href: 'https://example.com', openInNewTab: true} + } + }; + + const user = userEvent.setup(); + const {container, getByRole, simulateScrollPosition} = renderInContentElement( + , {seed} + ); + simulateScrollPosition('near viewport'); + + await user.hover(clickableArea(container)); + await user.click(getByRole('link')); + + expect(container.querySelector(`.${tooltipStyles.box}`)).toBeNull(); + }); + it('hides when backdrop element is intersecting content', async () => { const seed = { imageFileUrlTemplates: {large: ':id_partition/image.webp'}, diff --git a/entry_types/scrolled/package/spec/frontend/EditableLink-spec.js b/entry_types/scrolled/package/spec/frontend/EditableLink-spec.js index a16a9e611e..562e559af7 100644 --- a/entry_types/scrolled/package/spec/frontend/EditableLink-spec.js +++ b/entry_types/scrolled/package/spec/frontend/EditableLink-spec.js @@ -3,6 +3,7 @@ import React from 'react'; import {EditableLink} from 'frontend'; import {renderInEntry} from 'support'; +import userEvent from '@testing-library/user-event'; import '@testing-library/jest-dom/extend-expect' // Link behavior is tested in Link-spec.js. @@ -25,4 +26,18 @@ describe('EditableLink', () => { expect(getByRole('link')).toHaveClass('custom') }); + + it('supports onClick', async () => { + const onClick = jest.fn(event => + event.preventDefault() // Prevent jsdom warning + ); + const user = userEvent.setup(); + const {getByRole} = renderInEntry( + Some link + ); + + await user.click(getByRole('link')); + + expect(onClick).toHaveBeenCalled(); + }); }); diff --git a/entry_types/scrolled/package/spec/frontend/inlineEditing/EditableLink-spec.js b/entry_types/scrolled/package/spec/frontend/inlineEditing/EditableLink-spec.js index 604d8212e1..b5d382e16b 100644 --- a/entry_types/scrolled/package/spec/frontend/inlineEditing/EditableLink-spec.js +++ b/entry_types/scrolled/package/spec/frontend/inlineEditing/EditableLink-spec.js @@ -13,6 +13,12 @@ import '@testing-library/jest-dom/extend-expect' jest.mock('frontend/inlineEditing/useSelectLinkDestination'); describe('EditableLink', () => { + beforeEach(() => { + const rect = {width: 100, height: 20, top: 100, left: 100, bottom: 120, right: 200, x: 100, y: 100}; + Element.prototype.getClientRects = jest.fn(() => [rect]); + Element.prototype.getBoundingClientRect = jest.fn(() => rect); + }); + useFakeTranslations({ 'pageflow_scrolled.inline_editing.change_link_destination': 'Change link destination', 'pageflow_scrolled.inline_editing.select_link_destination': 'Select link destination', @@ -145,4 +151,17 @@ describe('EditableLink', () => { expect(onChange).toHaveBeenCalledWith(null); }); + + it('triggers onClick when tooltip is clicked', async () => { + const onClick = jest.fn(); + const user = userEvent.setup(); + render( + Some link + ); + + await user.hover(screen.getByText('Some link')); + await user.click(screen.getByRole('link')); + + expect(onClick).toHaveBeenCalled(); + }); }); diff --git a/entry_types/scrolled/package/src/contentElements/hotspots/Hotspots.js b/entry_types/scrolled/package/src/contentElements/hotspots/Hotspots.js index 6aa077ee02..32e046ce59 100644 --- a/entry_types/scrolled/package/src/contentElements/hotspots/Hotspots.js +++ b/entry_types/scrolled/package/src/contentElements/hotspots/Hotspots.js @@ -193,6 +193,11 @@ export function HotspotsImage({ onMouseEnter={() => setHoveredIndex(index)} onMouseLeave={() => setHoveredIndex(-1)} onClick={() => setActiveIndex(index)} + onLinkClick={event => { + activateArea(-1); + setHoveredIndex(-1); + event.stopPropagation(); + }} onDismiss={() => activateArea(-1)} /> ); } diff --git a/entry_types/scrolled/package/src/contentElements/hotspots/Tooltip.js b/entry_types/scrolled/package/src/contentElements/hotspots/Tooltip.js index 5a90c0d0df..4e33217619 100644 --- a/entry_types/scrolled/package/src/contentElements/hotspots/Tooltip.js +++ b/entry_types/scrolled/package/src/contentElements/hotspots/Tooltip.js @@ -48,7 +48,7 @@ export function Tooltip({ imageFile, containerRect, keepInViewport, floatingStrategy, aboveNavigationWidgets, wrapperRef, - onMouseEnter, onMouseLeave, onClick, onDismiss, + onMouseEnter, onMouseLeave, onClick, onDismiss, onLinkClick }) { const {t: translateWithEntryLocale} = useI18n(); const {t} = useI18n({locale: 'ui'}); @@ -110,7 +110,10 @@ export function Tooltip({ const dismiss = useDismiss(context, { outsidePressEvent: 'mousedown', - outsidePress: event => !insidePagerButton(event.target) + outsidePress: event => !insidePagerButton(event.target), + capture: { + outsidePress: false + } }); const {getReferenceProps, getFloatingProps} = useInteractions([ @@ -198,7 +201,7 @@ export function Tooltip({ light ? styles.light : styles.dark, {[styles.paddingForScrollButtons]: keepInViewport, [styles.minWidth]: presentOrEditing('link')})} - onMouseEnter={onMouseEnter} + onMouseEnter={() => storylineMode === 'active' && onMouseEnter()} onMouseLeave={onMouseLeave} onClick={onClick} {...getFloatingProps()}> @@ -239,7 +242,8 @@ export function Tooltip({ value={tooltipTexts[area.id]?.link} allowRemove={true} onTextChange={value => handleTextChange('link', value)} - onLinkChange={value => handleLinkChange(value)} />} + onLinkChange={value => handleLinkChange(value)} + onClick={onLinkClick} />} diff --git a/entry_types/scrolled/package/src/frontend/EditableLink.js b/entry_types/scrolled/package/src/frontend/EditableLink.js index 6218f3d866..d6a56ba298 100644 --- a/entry_types/scrolled/package/src/frontend/EditableLink.js +++ b/entry_types/scrolled/package/src/frontend/EditableLink.js @@ -5,11 +5,11 @@ import {Link} from './Link'; export const EditableLink = withInlineEditingAlternative( 'EditableLink', - function EditableLink({className, href, openInNewTab, children}) { + function EditableLink({className, href, openInNewTab, onClick, children}) { return ( ); } diff --git a/entry_types/scrolled/package/src/frontend/inlineEditing/EditableLink.js b/entry_types/scrolled/package/src/frontend/inlineEditing/EditableLink.js index 170e7c372b..f498100458 100644 --- a/entry_types/scrolled/package/src/frontend/inlineEditing/EditableLink.js +++ b/entry_types/scrolled/package/src/frontend/inlineEditing/EditableLink.js @@ -12,6 +12,7 @@ import styles from './EditableLink.module.css'; export function EditableLink({ className, href, openInNewTab, children, onChange, + onClick, linkPreviewDisabled, linkPreviewPosition = 'below', linkPreviewAlign = 'center', @@ -43,6 +44,7 @@ export function EditableLink({ {children} @@ -130,17 +131,25 @@ export function LinkPreview({disabled, href, openInNewTab, children, className}) ); } -export function LinkTooltip({disabled, setFloating, floatingStyles, floatingContext, arrowRef, state}) { +export function LinkTooltip({disabled, setFloating, floatingStyles, floatingContext, arrowRef, onClick, state}) { const {keep, deactivate} = useContext(UpdateContext); if (disabled || !state || !state.href) { return null; } + function handleClick(event) { + event.stopPropagation(); + + if (onClick) { + onClick(event); + } + } + return (
e.stopPropagation()} + onClick={handleClick} onMouseEnter={keep} onMouseLeave={deactivate} style={floatingStyles}>