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
33 changes: 30 additions & 3 deletions app/assets/stylesheets/pageflow/ui/forms.scss
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ textarea.short {
}

.radio_input,
.radio_button,
.check_box_input {
padding: space(1) 0;
position: relative;
Expand All @@ -108,6 +109,10 @@ textarea.short {
}
}

.radio_button {
padding: space(2);
}

.radio_input {
padding-top: 10px;
}
Expand Down Expand Up @@ -140,9 +145,20 @@ textarea.short {
.slider_input {
padding: space(1) 0 0;

label {
margin-bottom: space(2.5);
}

.slider_wrapper {
display: flex;
align-items: center;
gap: space(2);
}

.slider {
margin: 10px 10px 0 40px;
flex: 1;
border: 1px solid var(--ui-on-surface-color-lighter);
margin-right: 10px;
}

.ui-slider-handle {
Expand All @@ -152,8 +168,7 @@ textarea.short {

.value {
font-size: 11px;
margin: 7px 0;
float: left;
min-width: space(8);
}

&.disabled .slider,
Expand Down Expand Up @@ -227,6 +242,18 @@ textarea.short {
display: none;
}

.visually_hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}

.input-disabled {
button,
select,
Expand Down
38 changes: 30 additions & 8 deletions entry_types/scrolled/config/locales/de.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1280,6 +1280,12 @@ de:
hide: Außerhalb des Editors ausblenden
show: Außerhalb des Editors einblenden
hidden: Nur im Editor sichtbar
section_padding_visualization:
intersecting_auto: "Darstellung des dynamischen Abstands, der sich an die Größe des Motivbereichs anpasst, um Überlappungen von Text und Motiv zu vermeiden"
intersecting_manual: "Darstellung des manuell definierten Abstands, der bei Änderung der Fenstergröße konstant bleibt"
side_by_side: "Darstellung des oberen Abstands, wenn der Inhalt neben dem Motivbereich Platz findet"
top_padding: "Darstellung des oberen Abstands"
bottom_padding: "Darstellung des unteren Abstands"
select_link_destination:
cancel: Abbrechen
create: Erstellen
Expand All @@ -1296,6 +1302,9 @@ de:
selectable_section_item:
title: Abschnitt auswählen
edit_motif_area_menu_item: Motivbereich markieren...
edit_motif_area_input:
select: Motivbereich auswählen
edit: Motivbereich bearbeiten
backdrop_content_element_input:
add: Neues Element
unset: Nicht mehr als Hintergrund verwenden
Expand Down Expand Up @@ -1348,19 +1357,32 @@ de:
tabs:
sectionPaddings: Innenabstände
portrait: Hochkant

side_by_side_info: 'Wenn Motiv und Inhalt nebeneinander passen:'
attributes:
topPaddingVisualization:
label: Abstand oben
exposeMotifArea:
label: Abstand oben
values:
'true': Automatisch anpassen, um das Motiv im Hintergrund freizustellen
'false': Manuell festlegen
sideBySideVisualization:
label: Nebeneinander
paddingTop:
blank: (Standard)
label: Oben
label: Abstand oben
bottomPaddingVisualization:
label: Abstand unten
paddingBottom:
blank: (Standard)
label: Unten
label: Abstand unten
samePortraitPaddings:
label: Gleiche Innenabstände wie bei Querformat verwenden
portraitExposeMotifArea:
label: Abstand oben
portraitPaddingTop:
blank: (Standard)
label: Oben (Hochkant)
label: Abstand oben (Hochkant)
portraitPaddingBottom:
blank: (Standard)
label: Unten (Hochkant)
label: Abstand unten (Hochkant)
typography_sizes:
xl: Sehr groß
lg: Groß
Expand Down
42 changes: 42 additions & 0 deletions entry_types/scrolled/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1206,6 +1206,8 @@ en:
cardSurfaceColor:
label: Cards background color
auto: "(Auto)"
sectionPaddings:
label: "Top/bottom padding"
tabs:
section: Section
edit_section_transition:
Expand Down Expand Up @@ -1262,6 +1264,12 @@ en:
hide: Hide outside of the editor
show: Show outside of the editor
hidden: Only visible in editor
section_padding_visualization:
intersecting_auto: "Visualization of dynamic padding that adjusts to the motif area size to prevent text from overlapping the motif"
intersecting_manual: "Visualization of manually defined padding that stays constant as viewport size changes"
side_by_side: "Visualization of top padding when content fits next to the motif area"
top_padding: "Visualization of top padding"
bottom_padding: "Visualization of bottom padding"
select_link_destination:
cancel: Cancel
create: Create
Expand All @@ -1278,6 +1286,9 @@ en:
selectable_section_item:
title: Select section
edit_motif_area_menu_item: Select motif area...
edit_motif_area_input:
select: Select motif area
edit: Edit motif area
backdrop_content_element_input:
add: New element
unset: No longer use as backdrop
Expand Down Expand Up @@ -1324,6 +1335,37 @@ en:
remove: "Remove style"
crop_types:
circle: Circle
section_paddings_input:
auto: (Auto)
edit_section_paddings:
tabs:
sectionPaddings: Paddings
portrait: Portrait
side_by_side_info: 'When motif and content fit side by side:'
attributes:
topPaddingVisualization:
label: Top padding
exposeMotifArea:
label: Top Padding
values:
'true': Adjust automatically to expose a motif in the background
'false': Set manually
sideBySideVisualization:
label: Side by side
paddingTop:
label: Top padding
bottomPaddingVisualization:
label: Bottom padding
paddingBottom:
label: Bottom padding
samePortraitPaddings:
label: Use same paddings as in landscape orientation
portraitExposeMotifArea:
label: Top Padding
portraitPaddingTop:
label: Top padding (Portrait)
portraitPaddingBottom:
label: Bottom padding (Portrait)
typography_sizes:
xl: Very large
lg: Large
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,27 @@ describe('PreviewMessageController', () => {
})).resolves.toMatchObject({type: 'SELECT', payload: {id: 1, type: 'sectionTransition'}});
});

it('sends SELECT message to iframe on selectSectionPaddings event on model', async () => {
const entry = factories.entry(ScrolledEntry, {}, {
entryTypeSeed: normalizeSeed({
sections: [{id: 1}]
})
});
const iframeWindow = createIframeWindow();
controller = new PreviewMessageController({entry, iframeWindow});

await postReadyMessageAndWaitForAcknowledgement(iframeWindow);

return expect(new Promise(resolve => {
iframeWindow.addEventListener('message', event => {
if (event.data.type === 'SELECT') {
resolve(event.data);
}
});
entry.trigger('selectSectionPaddings', entry.sections.first());
})).resolves.toMatchObject({type: 'SELECT', payload: {id: 1, type: 'sectionPaddings'}});
});

it('sends SELECT message to iframe on selectWidget event on model', async () => {
const entry = factories.entry(ScrolledEntry, {}, {
widgetTypes: factories.widgetTypes([{
Expand Down Expand Up @@ -348,6 +369,38 @@ describe('PreviewMessageController', () => {
})).resolves.toBe('/widgets/header');
});

it('navigates to paddings route on SELECTED message for sectionPaddings', () => {
const editor = factories.editorApi();
const entry = factories.entry(ScrolledEntry, {}, {
entryTypeSeed: normalizeSeed({
sections: [{id: 1}]
})
});
const iframeWindow = createIframeWindow();
controller = new PreviewMessageController({entry, iframeWindow, editor});

return expect(new Promise(resolve => {
editor.on('navigate', resolve);
window.postMessage({type: 'SELECTED', payload: {id: 1, type: 'sectionPaddings'}}, '*');
})).resolves.toBe('/scrolled/sections/1/paddings');
});

it('navigates to paddings route with position query param on SELECTED message for sectionPaddings', () => {
const editor = factories.editorApi();
const entry = factories.entry(ScrolledEntry, {}, {
entryTypeSeed: normalizeSeed({
sections: [{id: 1}]
})
});
const iframeWindow = createIframeWindow();
controller = new PreviewMessageController({entry, iframeWindow, editor});

return expect(new Promise(resolve => {
editor.on('navigate', resolve);
window.postMessage({type: 'SELECTED', payload: {id: 1, type: 'sectionPaddings', position: 'bottom'}}, '*');
})).resolves.toBe('/scrolled/sections/1/paddings?position=bottom');
});

it('displays insert dialog on INSERT_CONTENT_ELEMENT message', () => {
const editor = factories.editorApi();
const entry = factories.entry(ScrolledEntry, {}, {
Expand Down Expand Up @@ -653,6 +706,105 @@ describe('PreviewMessageController', () => {
window.postMessage({type: 'SAVED_SCROLL_POINT'}, '*');
})).resolves.toEqual('received');
});

it('sends instant SCROLL_TO_SECTION in preserveScrollPoint if paddings were selected', async () => {
const editor = factories.editorApi();
const entry = factories.entry(ScrolledEntry, {}, {
entryTypeSeed: normalizeSeed({
sections: [{id: 5}]
})
});

const iframeWindow = createIframeWindow();
controller = new PreviewMessageController({entry, iframeWindow, editor});

await postReadyMessageAndWaitForAcknowledgement(iframeWindow);

// First select paddings
await new Promise(resolve => {
editor.once('navigate', resolve);
window.postMessage({type: 'SELECTED', payload: {id: 5, type: 'sectionPaddings'}}, '*');
});

return expect(new Promise(resolve => {
iframeWindow.addEventListener('message', event => {
if (event.data.type === 'SCROLL_TO_SECTION') {
resolve(event.data);
}
});
controller.preserveScrollPoint(() => {});
})).resolves.toMatchObject({
type: 'SCROLL_TO_SECTION',
payload: {id: 5, behavior: 'instant'}
});
});

it('sends SCROLL_TO_SECTION with nearEnd align when bottom paddings were selected', async () => {
const editor = factories.editorApi();
const entry = factories.entry(ScrolledEntry, {}, {
entryTypeSeed: normalizeSeed({
sections: [{id: 5}]
})
});

const iframeWindow = createIframeWindow();
controller = new PreviewMessageController({entry, iframeWindow, editor});

await postReadyMessageAndWaitForAcknowledgement(iframeWindow);

// Select bottom paddings
await new Promise(resolve => {
editor.once('navigate', resolve);
window.postMessage({type: 'SELECTED', payload: {id: 5, type: 'sectionPaddings', position: 'bottom'}}, '*');
});

return expect(new Promise(resolve => {
iframeWindow.addEventListener('message', event => {
if (event.data.type === 'SCROLL_TO_SECTION') {
resolve(event.data);
}
});
controller.preserveScrollPoint(() => {});
})).resolves.toMatchObject({
type: 'SCROLL_TO_SECTION',
payload: {id: 5, align: 'nearEnd', behavior: 'instant'}
});
});

it('clears selected paddings when another selection type is received', async () => {
const editor = factories.editorApi();
const entry = factories.entry(ScrolledEntry, {}, {
entryTypeSeed: normalizeSeed({
sections: [{id: 5}]
})
});

const iframeWindow = createIframeWindow();
controller = new PreviewMessageController({entry, iframeWindow, editor});

await postReadyMessageAndWaitForAcknowledgement(iframeWindow);

// First select paddings
await new Promise(resolve => {
editor.once('navigate', resolve);
window.postMessage({type: 'SELECTED', payload: {id: 5, type: 'sectionPaddings'}}, '*');
});

// Then select section settings
await new Promise(resolve => {
editor.once('navigate', resolve);
window.postMessage({type: 'SELECTED', payload: {id: 5, type: 'sectionSettings'}}, '*');
});

return expect(new Promise(resolve => {
iframeWindow.addEventListener('message', event => {
if (event.data.type === 'SAVE_SCROLL_POINT') {
resolve('save_scroll_point');
}
});
controller.preserveScrollPoint(() => {});
})).resolves.toEqual('save_scroll_point');
});
});

function postReadyMessageAndWaitForAcknowledgement(iframeWindow) {
Expand Down
Loading
Loading