Skip to content
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

Further improvements for hotspots element #2135

Merged
merged 9 commits into from
Jul 31, 2024
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
Loading