Skip to content

Commit

Permalink
Merge pull request #2202 from tf/scroll-indicator
Browse files Browse the repository at this point in the history
Add scroll indicator widget
  • Loading branch information
tf authored Feb 4, 2025
2 parents 7255c4a + f9d86f1 commit 3d24a71
Show file tree
Hide file tree
Showing 31 changed files with 584 additions and 38 deletions.
33 changes: 33 additions & 0 deletions entry_types/scrolled/config/locales/new/scroll_indicator.de.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
de:
pageflow:
icon_scroll_indicator:
feature_name: Icon Scroll-Indikator
iconScrollIndicator:
widget_type_name: Icon unten am Viewport-Rand
editor:
widgets:
attributes:
iconScrollIndicator:
animation:
label: Animation
values:
none: Keine
smallBounce: Subtil
largeBounce: Deutlich
alignment:
label: Ausrichtung
values:
centerContent: Zentriert zum Inhalt
centerViewport: Zentriert zum Viewport
size:
label: Größe
values:
large: Groß
small: Klein
ui:
configuration_editor:
tabs:
iconScrollIndicator: Icon Scroll-Indikator
widgets:
roles:
scrollIndicator: Scroll-Indikator
33 changes: 33 additions & 0 deletions entry_types/scrolled/config/locales/new/scroll_indicator.en.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
en:
pageflow:
icon_scroll_indicator:
feature_name: Icon Scroll Indicator
iconScrollIndicator:
widget_type_name: Icon at bottom of viewport
editor:
widgets:
attributes:
iconScrollIndicator:
animation:
label: Animation
values:
none: None
smallBounce: Subtle
largeBounce: Obvious
alignment:
label: Alignment
values:
centerContent: Center with content
centerViewport: Center with viewport
size:
label: Size
values:
large: Large
small: Small
ui:
configuration_editor:
tabs:
iconScrollIndicator: Icon Scroll Indicator
widgets:
roles:
scrollIndicator: Scroll indicator
8 changes: 8 additions & 0 deletions entry_types/scrolled/lib/pageflow_scrolled/plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ def configure(config)
c.widget_types.register(ReactWidgetType.new(name: 'textInlineFileRights',
role: 'inlineFileRights'))

c.features.register('icon_scroll_indicator') do |feature_config|
feature_config.widget_types.register(
ReactWidgetType.new(name: 'iconScrollIndicator',
role: 'scrollIndicator'),
default: true
)
end

c.features.register('datawrapper_chart_embed_opt_in')
c.features.enable_by_default('datawrapper_chart_embed_opt_in')
c.features.register('iframe_embed_content_element')
Expand Down
6 changes: 6 additions & 0 deletions entry_types/scrolled/package/config/webpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ module.exports = {
'pageflow-scrolled/widgets/textInlineFileRights',
'pageflow-scrolled/widgets/textInlineFileRights.css'
]
},
'pageflow-scrolled/widgets/iconScrollIndicator': {
import: [
'pageflow-scrolled/widgets/iconScrollIndicator',
'pageflow-scrolled/widgets/iconScrollIndicator.css'
]
}
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,32 @@ describe('PreviewMessageController', () => {
})).resolves.toMatchObject({type: 'SELECT', payload: {id: 1, type: 'sectionTransition'}});
});

it('sends SELECT message to iframe on selectWidget event on model', async () => {
const entry = factories.entry(ScrolledEntry, {}, {
widgetTypes: factories.widgetTypes([{
role: 'header', name: 'someNavigation'
}]),
widgetsAttributes: [{
type_name: 'someNavigation',
role: 'header'
}],
entryTypeSeed: normalizeSeed()
});
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('selectWidget', entry.widgets.first());
})).resolves.toMatchObject({type: 'SELECT', payload: {id: 'header', type: 'widget'}});
});

it('supports sending CONTENT_ELEMENT_EDITOR_COMMAND message to iframe', async () => {
const entry = factories.entry(ScrolledEntry, {}, {
entryTypeSeed: normalizeSeed({
Expand Down Expand Up @@ -298,6 +324,18 @@ describe('PreviewMessageController', () => {
})).resolves.toBe('/scrolled/sections/1/transition');
});

it('navigates to edit widget route on SELECTED message for widget role', () => {
const editor = factories.editorApi();
const entry = factories.entry(ScrolledEntry);
const iframeWindow = createIframeWindow();
controller = new PreviewMessageController({entry, iframeWindow, editor});

return expect(new Promise(resolve => {
editor.on('navigate', resolve);
window.postMessage({type: 'SELECTED', payload: {id: 'header', type: 'widget'}}, '*');
})).resolves.toBe('/widgets/header');
});

it('displays insert dialog on INSERT_CONTENT_ELEMENT message', () => {
const editor = factories.editorApi();
const entry = factories.entry(ScrolledEntry, {}, {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';

import {renderEntry, usePageObjects} from 'support/pageObjects';

import {frontend} from 'frontend';

describe('scroll indicator widget', () => {
usePageObjects();

it('is rendered once', () => {
frontend.widgetTypes.register('testScrollIndicator', {
component: function ({children}) {
return (
<div data-testid="scrollIndicator">Scroll Indicator</div>
)
}
});

const {getByTestId} = renderEntry({
seed: {
sections: [{id: 5}, {id: 6}],
widgets: [{
typeName: 'testScrollIndicator',
role:'scrollIndicator'
}]
}
});

expect(getByTestId('scrollIndicator')).not.toBeNull();
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {frontend} from 'frontend';
import React from 'react';
import {frontend, WidgetSelectionRect} from 'frontend';

import {useInlineEditingPageObjects, renderEntry} from 'support/pageObjects';
import {fakeParentWindow} from 'support';
Expand Down Expand Up @@ -121,4 +122,31 @@ describe('SELECTED message', () => {
payload: {id: 2, type: 'sectionTransition'}
}, expect.anything());
});

it('is posted when widget selection rect is clicked', () => {
frontend.widgetTypes.register('customNavigation', {
component: function ({children}) {
return (
<div>
<WidgetSelectionRect>
Custom navigation
</WidgetSelectionRect>
</div>
)
}
});

const {getByText} = renderEntry({
seed: {
widgets: [{typeName: 'customNavigation', role: 'header'}]
}
});

fireEvent.click(getByText('Custom navigation'));

expect(window.parent.postMessage).toHaveBeenCalledWith({
type: 'SELECTED',
payload: {id: 'header', type: 'widget'}
}, expect.anything());
});
});
39 changes: 38 additions & 1 deletion entry_types/scrolled/package/src/editor/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import {SideBarRouter} from './routers/SideBarRouter';
import {SideBarController} from './controllers/SideBarController';

import {browser} from 'pageflow/frontend';
import {CheckBoxInputView, ConfigurationEditorView} from 'pageflow/ui';

import {
CheckBoxInputView,
ConfigurationEditorView,
SelectInputView
} from 'pageflow/ui';

import {ColorSelectInputView} from './views/inputs/ColorSelectInputView';
import {BrowserNotSupportedView} from './views/BrowserNotSupportedView';

Expand Down Expand Up @@ -52,6 +58,10 @@ editor.widgetTypes.registerRole('header', {
isOptional: true
});

editor.widgetTypes.registerRole('scrollIndicator', {
isOptional: true
});

editor.widgetTypes.register('defaultNavigation', {
configurationEditorView: ConfigurationEditorView.extend({
configure: function() {
Expand Down Expand Up @@ -92,3 +102,30 @@ editor.widgetTypes.register('textInlineFileRights', {
}
}
});

editor.widgetTypes.register('iconScrollIndicator', {
configurationEditorView: ConfigurationEditorView.extend({
configure: function() {
const firstSection = this.options.entry.sections.first();

if (firstSection) {
this.options.entry.trigger('scrollToSection', firstSection);
}

this.tab('iconScrollIndicator', function() {
this.input('alignment', SelectInputView, {
values: ['centerContent', 'centerViewport']
});
this.input('size', SelectInputView, {
defaultValue: 'small',
values: ['large', 'small']
});
this.input('animation', SelectInputView, {
defaultValue: 'smallBounce',
values: ['none', 'smallBounce', 'largeBounce']
});

});
}
})
});
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,16 @@ export const PreviewMessageController = Object.extend({
})
});

this.listenTo(this.entry, 'selectWidget', widget => {
postMessage({
type: 'SELECT',
payload: {
id: widget.get('role'),
type: 'widget'
}
})
});

this.listenTo(this.entry, 'resetSelection', contentElement =>
postMessage({
type: 'SELECT',
Expand Down Expand Up @@ -123,6 +133,9 @@ export const PreviewMessageController = Object.extend({
else if (type === 'sectionTransition') {
this.editor.navigate(`/scrolled/sections/${id}/transition`, {trigger: true})
}
else if (type === 'widget') {
this.editor.navigate(`/widgets/${id}`, {trigger: true})
}
else {
this.editor.navigate('/', {trigger: true})
}
Expand Down
11 changes: 7 additions & 4 deletions entry_types/scrolled/package/src/frontend/Entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import React from 'react';

import {Content} from './Content';
import {Widget} from './Widget';
import {SelectableWidget} from './SelectableWidget';

export function Entry() {
import {withInlineEditingDecorator} from './inlineEditing';

export const Entry = withInlineEditingDecorator('EntryDecorator', function Entry() {
return (
<>
<Widget role="consent" />
<Widget role="header" />
<SelectableWidget role="header" />
<Content />
<Widget role="footer" />
</>
)
}
);
});
7 changes: 7 additions & 0 deletions entry_types/scrolled/package/src/frontend/Section.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
useAdditionalSeedData,
useFileWithInlineRights
} from '../entryState';

import Foreground from './Foreground';
import {SectionInlineFileRights} from './SectionInlineFileRights';
import {Layout, widths as contentElementWidths} from './layouts';
Expand All @@ -17,6 +18,8 @@ import {SectionLifecycleProvider, useSectionLifecycle} from './useSectionLifecyc
import {SectionViewTimelineProvider} from './SectionViewTimelineProvider';
import {withInlineEditingDecorator} from './inlineEditing';
import {BackgroundColorProvider} from './backgroundColor';
import {SelectableWidget} from './SelectableWidget';

import * as v1 from './v1';
import * as v2 from './v2';

Expand Down Expand Up @@ -75,6 +78,10 @@ const Section = withInlineEditingDecorator('SectionDecorator', function Section(
backdrop={backdrop}
atmoAudioFile={atmoAudioFile}
state={state} />

{section.sectionIndex === 0 &&
<SelectableWidget role="scrollIndicator"
props={{sectionLayout: section.layout}} />}
</BackgroundColorProvider>
</SectionViewTimelineProvider>
</SectionLifecycleProvider>
Expand Down
8 changes: 8 additions & 0 deletions entry_types/scrolled/package/src/frontend/SelectableWidget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {withInlineEditingDecorator} from './inlineEditing';

import {Widget} from './Widget'

export const SelectableWidget = withInlineEditingDecorator(
'SelectableWidgetDecorator',
Widget
);
4 changes: 3 additions & 1 deletion entry_types/scrolled/package/src/frontend/ThemeIcon.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import whatsApp from './icons/social/whatsApp.svg';

import arrowLeft from './icons/arrowLeft.svg';
import arrowRight from './icons/arrowRight.svg';
import scrollDown from './icons/scrollDown.svg';

import enterFullscreen from './icons/enterFullscreen.svg';
import exitFullscreen from './icons/exitFullscreen.svg';
Expand Down Expand Up @@ -51,6 +52,7 @@ const icons = {

arrowLeft,
arrowRight,
scrollDown,

enterFullscreen,
exitFullscreen,
Expand All @@ -68,7 +70,7 @@ const icons = {
* enterFullscreen, exitFullscreen, expand, facebook, gear, information,
* linkedIn, menu, muted, pause, play, share, telegram,
* textTracks, twitter, unmuted, world, whatsApp,
* arrowLeft, arrowRight, world
* arrowLeft, arrowRight, scrollDown, world
* @params {number} [props.width] - Image width.
* @params {number} [props.height] - Image height.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {withInlineEditingAlternative} from './inlineEditing';

export const WidgetSelectionRect = withInlineEditingAlternative(
'WidgetSelectionRect',
function WidgetSelectionRect({children}) {
return children;
}
);
Loading

0 comments on commit 3d24a71

Please sign in to comment.