Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions entry_types/scrolled/config/locales/de.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ de:
pageflow:
datawrapper_chart_embed_opt_in:
feature_name: Opt-in für Datawrapper Embeds
image_srcset:
feature_name: Responsive Bild-Srcset
defaultNavigation:
widget_type_name: Standard Navigation
editor:
Expand Down
2 changes: 2 additions & 0 deletions entry_types/scrolled/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ en:
pageflow:
datawrapper_chart_embed_opt_in:
feature_name: Opt-in for Datawrapper embeds
image_srcset:
feature_name: Responsive image srcset
defaultNavigation:
widget_type_name: Default navigation
editor:
Expand Down
2 changes: 2 additions & 0 deletions entry_types/scrolled/lib/pageflow_scrolled/plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ def configure(config)
c.features.register('custom_palette_colors')
c.features.register('decoration_effects')
c.features.register('backdrop_size')
c.features.register('image_srcset')
c.features.enable_by_default('image_srcset')

c.features.register('faq_page_structured_data') do |feature_config|
feature_config.entry_structured_data_types.register(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import React from 'react';
import 'contentElements/inlineImage/frontend';
import {renderContentElement, usePageObjects} from 'support/pageObjects';
import {renderInContentElement} from 'pageflow-scrolled/testHelpers';
import '@testing-library/jest-dom/extend-expect'

import {InlineImage} from 'contentElements/inlineImage/InlineImage';
import {features} from 'pageflow/frontend';
import {usePortraitOrientation} from 'frontend/usePortraitOrientation';
jest.mock('frontend/usePortraitOrientation');

Expand Down Expand Up @@ -190,6 +194,118 @@ describe('InlineImage', () => {
});
});

describe('srcset', () => {
beforeEach(() => features.enable('frontend', ['image_srcset']));
afterEach(() => features.enabledFeatureNames = []);

function renderInlineImage({contentElementWidth = 0, ...seedOptions} = {}) {
const result = renderInContentElement(
<InlineImage contentElementId={42}
contentElementWidth={contentElementWidth}
configuration={{id: 100}} />,
{seed: seedOptions}
);
result.simulateScrollPosition('near viewport');
return result;
}

it('uses medium and large srcset for default width', () => {
const {getByRole} = renderInlineImage({
imageFileUrlTemplates: {
medium: ':id_partition/medium/image.jpg',
large: ':id_partition/large/image.jpg'
},
imageFiles: [{permaId: 100, id: 1, width: 4000, height: 3000}]
});

expect(getByRole('img')).toHaveAttribute('srcset',
'000/000/001/medium/image.jpg 1024w, 000/000/001/large/image.jpg 1920w');
expect(getByRole('img')).toHaveAttribute('sizes',
'(min-width: 950px) 950px, 100vw');
});

it('uses medium, large and ultra srcset for full width', () => {
const {getByRole} = renderInlineImage({
contentElementWidth: 3,
imageFileUrlTemplates: {
medium: ':id_partition/medium/image.jpg',
large: ':id_partition/large/image.jpg',
ultra: ':id_partition/ultra/image.jpg'
},
imageFiles: [{permaId: 100, id: 1, width: 4000, height: 3000}]
});

expect(getByRole('img')).toHaveAttribute('srcset',
'000/000/001/medium/image.jpg 1024w, ' +
'000/000/001/large/image.jpg 1920w, ' +
'000/000/001/ultra/image.jpg 3840w');
expect(getByRole('img')).toHaveAttribute('sizes', '100vw');
});

it('uses medium, large and ultra srcset with 1200px sizes for xl width', () => {
const {getByRole} = renderInlineImage({
contentElementWidth: 2,
imageFileUrlTemplates: {
medium: ':id_partition/medium/image.jpg',
large: ':id_partition/large/image.jpg',
ultra: ':id_partition/ultra/image.jpg'
},
imageFiles: [{permaId: 100, id: 1, width: 4000, height: 3000}]
});

expect(getByRole('img')).toHaveAttribute('srcset',
'000/000/001/medium/image.jpg 1024w, ' +
'000/000/001/large/image.jpg 1920w, ' +
'000/000/001/ultra/image.jpg 3840w');
expect(getByRole('img')).toHaveAttribute('sizes',
'(min-width: 950px) 1200px, 100vw');
});

it('uses computed width descriptors for portrait images', () => {
const {getByRole} = renderInlineImage({
imageFileUrlTemplates: {
medium: ':id_partition/medium/image.jpg',
large: ':id_partition/large/image.jpg'
},
imageFiles: [{permaId: 100, id: 1, width: 2160, height: 3840}]
});

expect(getByRole('img')).toHaveAttribute('srcset',
'000/000/001/medium/image.jpg 576w, 000/000/001/large/image.jpg 1080w');
});

it('uses plain medium variant for small widths', () => {
const {getByRole} = renderInlineImage({
contentElementWidth: -1,
imageFileUrlTemplates: {
medium: ':id_partition/medium/image.jpg',
large: ':id_partition/large/image.jpg'
},
imageFiles: [{permaId: 100, id: 1, width: 200, height: 100}]
});

expect(getByRole('img')).not.toHaveAttribute('srcset');
expect(getByRole('img')).toHaveAttribute('src',
'000/000/001/medium/image.jpg');
});

it('falls back to original behavior when feature is disabled', () => {
features.enabledFeatureNames = [];

const {getByRole} = renderInlineImage({
imageFileUrlTemplates: {
medium: ':id_partition/medium/image.jpg',
large: ':id_partition/large/image.jpg'
},
imageFiles: [{permaId: 100, id: 1, width: 200, height: 100}]
});

expect(getByRole('img')).not.toHaveAttribute('srcset');
expect(getByRole('img')).toHaveAttribute('src',
'000/000/001/medium/image.jpg');
});
});

describe('basic functionality', () => {
it('renders with FitViewport and ContentElementBox', () => {
const {getContentElement} = renderContentElement({
Expand Down
134 changes: 134 additions & 0 deletions entry_types/scrolled/package/spec/entryState/useFile-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,140 @@ describe('useFile', () => {
});
});

it('includes variantWidths for image files', () => {
const {result} = renderHookInEntry(
() => useFile({collectionName: 'imageFiles', permaId: 1}),
{
seed: {
fileUrlTemplates: {
imageFiles: {
medium: '/image_files/:id_partition/medium/:basename.:processed_extension',
large: '/image_files/:id_partition/large/:basename.:processed_extension',
}
},
fileModelTypes: {
imageFiles: 'Pageflow::ImageFile'
},
imageFiles: [
{
id: 100,
permaId: 1,
basename: 'image',
extension: 'jpg',
processedExtension: 'webp',
width: 4000,
height: 3000
}
]
}
}
);

expect(result.current.variantWidths).toEqual([
['1024w', 'medium'],
['1920w', 'large']
]);
});

it('computes variantWidths based on actual image dimensions for portrait images', () => {
const {result} = renderHookInEntry(
() => useFile({collectionName: 'imageFiles', permaId: 1}),
{
seed: {
fileUrlTemplates: {
imageFiles: {
medium: '/image_files/:id_partition/medium/:basename.:processed_extension',
large: '/image_files/:id_partition/large/:basename.:processed_extension',
}
},
fileModelTypes: {
imageFiles: 'Pageflow::ImageFile'
},
imageFiles: [
{
id: 100,
permaId: 1,
basename: 'image',
extension: 'jpg',
processedExtension: 'webp',
width: 2160,
height: 3840
}
]
}
}
);

expect(result.current.variantWidths).toEqual([
['576w', 'medium'],
['1080w', 'large']
]);
});

it('deduplicates variantWidths when variants produce same width', () => {
const {result} = renderHookInEntry(
() => useFile({collectionName: 'imageFiles', permaId: 1}),
{
seed: {
fileUrlTemplates: {
imageFiles: {
medium: '/image_files/:id_partition/medium/:basename.:processed_extension',
large: '/image_files/:id_partition/large/:basename.:processed_extension',
ultra: '/image_files/:id_partition/ultra/:basename.:processed_extension',
}
},
fileModelTypes: {
imageFiles: 'Pageflow::ImageFile'
},
imageFiles: [
{
id: 100,
permaId: 1,
basename: 'image',
extension: 'jpg',
processedExtension: 'webp',
width: 1920,
height: 1080
}
]
}
}
);

expect(result.current.variantWidths).toEqual([
['1024w', 'medium'],
['1920w', 'large']
]);
});

it('does not include variantWidths for video files', () => {
const {result} = renderHookInEntry(
() => useFile({collectionName: 'videoFiles', permaId: 1}),
{
seed: {
fileUrlTemplates: {
videoFiles: {
high: '/video_files/:id_partition/high.mp4',
},
},
fileModelTypes: {
videoFiles: 'Pageflow::VideoFile'
},
videoFiles: [
{
id: 100,
permaId: 1,
basename: 'video',
variants: ['high'],
}
]
}
}
);

expect(result.current.variantWidths).toBeUndefined();
});

it('falls back to file name for display name from watched collection', () => {
const {result} = renderHookInEntry(
() => useFile({collectionName: 'imageFiles', permaId: 1}),
Expand Down
Loading
Loading