Skip to content

Commit

Permalink
Merge pull request #2135 from tf/hotspots-4
Browse files Browse the repository at this point in the history
Further improvements for hotspots element
  • Loading branch information
tf authored Jul 31, 2024
2 parents bed5063 + 260b3e9 commit 336a640
Show file tree
Hide file tree
Showing 12 changed files with 396 additions and 51 deletions.
20 changes: 20 additions & 0 deletions entry_types/scrolled/config/locales/new/hotspots.de.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,19 @@ de:
values:
indicator: Am Indikator
area: Am Bereich
tooltipMaxWidth:
label: Tooltip-Maximalbreite
values:
medium: "Mittel"
veryNarrow: Sehr schmal
narrow: Schmal
wide: Breit
tooltipTextAlign:
label: Textausrichtung in Tooltip
values:
left: Links
center: Zentriert
right: Rechts
color:
label: Farbe
activeImage:
Expand All @@ -51,6 +64,13 @@ de:
values:
indicator: Am Indikator
area: Am Bereich
portraitTooltipMaxWidth:
label: Tooltip-Maximalbreite (Hochkant)
values:
medium: "Mittel"
narrow: Schmal
veryNarrow: Sehr schmal
wide: Breit
portraitColor:
label: Farbe (Hochkant)
portraitActiveImage:
Expand Down
20 changes: 20 additions & 0 deletions entry_types/scrolled/config/locales/new/hotspots.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,19 @@ en:
values:
indicator: At indicator
area: At area
tooltipMaxWidth:
label: Tooltip maximum width
values:
medium: Medium
narrow: Narrow
veryNarrow: Very Narrow
wide: Wide
tooltipTextAlign:
label: Text alignment in tooltip
values:
left: Left
center: Center
right: Right
color:
label: Color
activeImage:
Expand All @@ -51,6 +64,13 @@ en:
values:
indicator: At indicator
area: At area
portraitTooltipMaxWidth:
label: Tooltip maximum width (Portrait)
values:
medium: Medium
narrow: Narrow
veryNarrow: Very Narrow
wide: Wide
portraitColor:
label: Color (Portrait)
portraitActiveImage:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe('Hotspots', () => {
this.callback = callback;
this.observe = observeResizeMock;
this.unobserve = function(element) {};
this.disconnect = function() {};
};
});

Expand Down Expand Up @@ -356,7 +357,7 @@ describe('Hotspots', () => {
const seed = {
imageFileUrlTemplates: {
large: 'large/:id_partition/image.webp',
linkThumbnailLarge: 'linkThumbnailLarge/:id_partition/image.webp'
medium: 'medium/:id_partition/image.webp'
},
imageFiles: [{id: 1, permaId: 100}, {id: 2, permaId: 101}]
};
Expand Down Expand Up @@ -395,7 +396,7 @@ describe('Hotspots', () => {
expect(queryByText('Some link')).not.toBeNull();
expect(getByRole('link')).toHaveAttribute('href', 'https://example.com');
expect(getByRole('link')).toHaveAttribute('target', '_blank');
expect(getByRole('img')).toHaveAttribute('src', 'linkThumbnailLarge/000/000/002/image.webp');
expect(getByRole('img')).toHaveAttribute('src', 'medium/000/000/002/image.webp');
});

it('does not render tooltip link if link text is blank', async () => {
Expand Down Expand Up @@ -424,14 +425,184 @@ describe('Hotspots', () => {
};

const user = userEvent.setup();
const {container, queryByRole} = renderInContentElement(
const {container, queryByRole, simulateScrollPosition} = renderInContentElement(
<Hotspots configuration={configuration} />, {seed}
);
simulateScrollPosition('near viewport');
await user.click(container.querySelector(`.${areaStyles.clip}`))

expect(queryByRole('link')).toBeNull();
});

it('does not apply min width to tooltip without link', async () => {
const seed = {
imageFileUrlTemplates: {large: ':id_partition/image.webp'},
imageFiles: [{id: 1, permaId: 100}]
};
const configuration = {
image: 100,
areas: [
{
id: 1,
indicatorPosition: [10, 20],
}
],
tooltipTexts: {
1: {
title: [{type: 'heading', children: [{text: 'Some title'}]}],
description: [{type: 'paragraph', children: [{text: 'Some description'}]}],
link: [{type: 'paragraph', children: [{text: ''}]}]
}
},
tooltipLinks: {
1: {href: 'https://example.com', openInNewTab: true}
}
};

const user = userEvent.setup();
const {container, simulateScrollPosition} = renderInContentElement(
<Hotspots configuration={configuration} />, {seed}
);
simulateScrollPosition('near viewport');
await user.click(container.querySelector(`.${areaStyles.clip}`))

expect(container.querySelector(`.${tooltipStyles.box}`)).not.toHaveClass(tooltipStyles.minWidth);
});

it('applies min width to tooltip with link', async () => {
const seed = {
imageFileUrlTemplates: {large: ':id_partition/image.webp'},
imageFiles: [{id: 1, permaId: 100}]
};
const configuration = {
image: 100,
areas: [
{
id: 1,
indicatorPosition: [10, 20],
}
],
tooltipTexts: {
1: {
title: [{type: 'heading', children: [{text: 'Some title'}]}],
description: [{type: 'paragraph', children: [{text: 'Some description'}]}],
link: [{type: 'paragraph', children: [{text: 'Some link'}]}]
}
},
tooltipLinks: {
1: {href: 'https://example.com', openInNewTab: true}
}
};

const user = userEvent.setup();
const {container, simulateScrollPosition} = renderInContentElement(
<Hotspots configuration={configuration} />, {seed}
);
simulateScrollPosition('near viewport');
await user.click(container.querySelector(`.${areaStyles.clip}`))

expect(container.querySelector(`.${tooltipStyles.box}`)).toHaveClass(tooltipStyles.minWidth);
});

it('applies max width to tooltip', async () => {
const seed = {
imageFileUrlTemplates: {large: ':id_partition/image.webp'},
imageFiles: [{id: 1, permaId: 100}]
};
const configuration = {
image: 100,
areas: [
{
id: 1,
indicatorPosition: [10, 20],
tooltipMaxWidth: 'narrow'
}
],
tooltipTexts: {
1: {
title: [{type: 'heading', children: [{text: 'Some title'}]}],
description: [{type: 'paragraph', children: [{text: 'Some description'}]}]
}
}
};

const user = userEvent.setup();
const {container, simulateScrollPosition} = renderInContentElement(
<Hotspots configuration={configuration} />, {seed}
);
simulateScrollPosition('near viewport');
await user.click(container.querySelector(`.${areaStyles.clip}`))

expect(container.querySelector(`.${tooltipStyles.box}`)).toHaveClass(tooltipStyles['maxWidth-narrow']);
});

it('supports separate max width for portrait mode', async () => {
const seed = {
imageFileUrlTemplates: {large: ':id_partition/image.webp'},
imageFiles: [{id: 1, permaId: 100}]
};
const configuration = {
image: 100,
portraitImage: 100,
areas: [
{
id: 1,
indicatorPosition: [10, 20],
tooltipMaxWidth: 'narrow',
portraitTooltipMaxWidth: 'veryNarrow'
}
],
tooltipTexts: {
1: {
title: [{type: 'heading', children: [{text: 'Some title'}]}],
description: [{type: 'paragraph', children: [{text: 'Some description'}]}]
}
}
};

const user = userEvent.setup();
window.matchMedia.mockPortrait();
const {container, simulateScrollPosition} = renderInContentElement(
<Hotspots configuration={configuration} />, {seed}
);
simulateScrollPosition('near viewport');
await user.click(container.querySelector(`.${areaStyles.clip}`))

expect(container.querySelector(`.${tooltipStyles.box}`)).toHaveClass(tooltipStyles['maxWidth-veryNarrow']);
});

it('supports setting tooltip text align', async () => {
const seed = {
imageFileUrlTemplates: {large: ':id_partition/image.webp'},
imageFiles: [{id: 1, permaId: 100}]
};
const configuration = {
image: 100,
areas: [
{
id: 1,
indicatorPosition: [10, 20],
tooltipTextAlign: 'center'
}
],
tooltipTexts: {
1: {
title: [{type: 'heading', children: [{text: 'Some title'}]}],
description: [{type: 'paragraph', children: [{text: 'Some description'}]}]
}
}
};

const user = userEvent.setup();
const {container, simulateScrollPosition} = renderInContentElement(
<Hotspots configuration={configuration} />, {seed}
);
simulateScrollPosition('near viewport');
await user.click(container.querySelector(`.${areaStyles.clip}`))

expect(container.querySelector(`.${tooltipStyles.box}`)).toHaveClass(tooltipStyles['align-center']);
});

it('does not observe resize by default', () => {
const seed = {
imageFileUrlTemplates: {large: ':id_partition/image.webp'},
Expand Down Expand Up @@ -570,7 +741,7 @@ describe('Hotspots', () => {
image: 100,
areas: [
{
indicatorPosition: [10, 20],
indicatorPosition: [15, 20],
outline: [[10, 20], [10, 30], [40, 30], [40, 15]],
tooltipReference: 'area'
}
Expand All @@ -584,9 +755,8 @@ describe('Hotspots', () => {
simulateScrollPosition('near viewport');

expect(container.querySelector(`.${tooltipStyles.reference}`)).toHaveStyle({
left: '10px',
left: '15px',
top: '15px',
width: '30px',
height: '15px'
});
});
Expand Down Expand Up @@ -618,9 +788,8 @@ describe('Hotspots', () => {
simulateScrollPosition('near viewport');

expect(container.querySelector(`.${tooltipStyles.reference}`)).toHaveStyle({
left: '10px',
left: '20px',
top: '15px',
width: '30px',
height: '15px'
});
});
Expand Down Expand Up @@ -654,7 +823,6 @@ describe('Hotspots', () => {
expect(container.querySelector(`.${tooltipStyles.reference}`)).toHaveStyle({
left: '10px',
top: '15px',
width: '30px',
height: '15px'
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export function Tooltip({
panZoomEnabled, imageFile, containerRect, keepInViewport, floatingStrategy,
onMouseEnter, onMouseLeave, onClick, onDismiss,
}) {
const {t: translateWithEntryLocale} = useI18n();
const {t} = useI18n({locale: 'ui'});
const updateConfiguration = useContentElementConfigurationUpdate();
const {isEditable} = useContentElementEditorState();
Expand All @@ -55,6 +56,7 @@ export function Tooltip({

const referenceType = portraitMode ? area.portraitTooltipReference : area.tooltipReference;
const position = portraitMode ? area.portraitTooltipPosition : area.tooltipPosition;
const maxWidth = portraitMode ? area.portraitTooltipMaxWidth : area.tooltipMaxWidth;

const arrowRef = useRef();
const {refs, floatingStyles, context} = useFloating({
Expand All @@ -64,18 +66,14 @@ export function Tooltip({
placement: position === 'above' ? 'top' : 'bottom',
middleware: [
offset(referenceType === 'area' ? 7 : 20),
shift({crossAxis: keepInViewport}),
shift({crossAxis: keepInViewport, padding: {left: 16, right: 16}}),
keepInViewport && flip(),
arrow({
element: arrowRef
element: arrowRef,
padding: 5
})
],
whileElementsMounted(referenceEl, floatingEl, update) {
return autoUpdate(referenceEl, floatingEl, update, {
elementResize: false,
layoutShift: false,
});
}
whileElementsMounted: autoUpdate
});

const role = useRole(context, {role: 'label'});
Expand Down Expand Up @@ -107,7 +105,7 @@ export function Tooltip({
if (utils.isBlankEditableTextValue(tooltipTexts[area.id]?.link)) {
handleTextChange('link', [{
type: 'heading',
children: [{text: t('pageflow_scrolled.public.more')}]
children: [{text: translateWithEntryLocale('pageflow_scrolled.public.more')}]
}]);
}

Expand Down Expand Up @@ -143,17 +141,20 @@ export function Tooltip({
<div ref={refs.setFloating}
style={floatingStyles}
className={classNames(styles.box,
{[styles.editable]: isEditable})}
styles[`maxWidth-${maxWidth}`],
styles[`align-${area.tooltipTextAlign}`],
{[styles.editable]: isEditable,
[styles.minWidth]: presentOrEditing('link')})}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onClick={onClick}
{...getFloatingProps()}>
<FloatingArrow ref={arrowRef} context={context} />
<Image imageFile={tooltipImageFile}
variant={'linkThumbnailLarge'}
variant={'medium'}
fill={false}
width={394}
height={226}
width={tooltipImageFile?.width}
height={tooltipImageFile?.height}
preferSvg={true} />
{presentOrEditing('title') &&
<h3 id={`hotspots-tooltip-title-${contentElementId}-${area.id}`}>
Expand Down
Loading

0 comments on commit 336a640

Please sign in to comment.