Main entry point of frontend API to register new content element types.
Register new types of content elements.
Register a new type of content element.
(string)
Name of the content element type.
(Object)
| Name | Description |
|---|---|
options.component React.Component
|
|
options.supportsWrappingAroundFloats boolean?
|
In sections with centered layout, content elements can be floated to the left or right. By default all content elements are cleared to position them below floating elements. If a content element renders mainly text that can wrap around floating elements, clearing can be disabled via this option. |
// frontend.js
import {frontend} from 'pageflow-scrolled/frontend';
import {InlineImage} from './InlineImage';
frontend.contentElementTypes.register('inlineImage', {
component: InlineImage
});
Register new types of widgets.
Register a new type of widget.
(string)
Name of the content element type.
(Object)
| Name | Description |
|---|---|
options.component React.Component
|
// frontend.js
import {frontend} from 'pageflow-scrolled/frontend';
import {CustomNavigationBar} from './CustomNavigationBar';
frontend.contentElementTypes.register('customNavigationBar', {
component: CustomNavigationBar
});
Custom error boundary component to wrap content elements. Allows integration with error monitoring tools. The component receives typeName (string), configuration (object), fallback (function returning default UI), and children (content element).
Type: React.Component
Main entry point of editor API to register new content element types.
Integrate new content types into the editor.
Register a new type of content element in the editor.
(string)
Name of the content element type.
(Object)
| Name | Description |
|---|---|
options.configurationEditor Function
|
Function that is evaluated in the context of a
ConfigurationEditorView
(see
pageflow/ui
) which will
be used to edit the configuration of content elements of
this type. Receives an options object with an
entry
property containing the entry model.
|
options.defaultConfig Object
|
Object that is set as initial config for the content element. |
options.split Function?
|
Function that receives configuration attributes and a split point and needs to return a two element array of configuration attributes objects representing the content elements that arise from splitting a content element with the given configuration at the specified split point. Called when inserting content elements at custom split points. |
options.merge Function?
|
Function that receives two configuration attributes objects and needs to return a single merged configuration. If provided, this will function will be called whenever two content elements of this type become adjacent because a common neighbor has been deleted. |
options.supportedPositions Array<string>?
|
Pass array containing a subset of the positions
left
,
right
,
sticky
and
inline
. By default all positions are supported.
|
options.supportedWidthRange Array<string>?
|
Pass array consisting of two widths of the form
xxs
,
xs
,
sm
,
md
,
lg
,
xl
or
full
representing the smallest andlargest
supported width. By default only width
md
is supported.
|
options.configurationPlace Function?
|
Function that receives a content element and the path of one of
its configuration properties. Can return a
label
naming that
property and a
select
function revealing where it is edited.
Both are optional. Root properties are named by the label of
their configuration editor input unless the returned object
says otherwise. Used by the list of places that reference a
file.
|
// editor.js
editor.contentElementTypes.register('inlineImage', {
supportedWidthRange: ['xss', 'full'],
configurationEditor() {
this.tab('general', function() {
this.input('caption', TextInputView);
});
}
});
React components for building content elements.
Render one of the EU icons for labelling AI generated content.
Wrap content element that render a visible box in this component to apply theme specific styles like rounded corners.
Render a figure with a caption text attached.
| Name | Description |
|---|---|
children string
|
Content of figure. |
variant string?
|
Name of figureCaption property scope to apply. |
caption (Array<Object> | string)
|
Formatted text data as provided by onCaptionChange. |
onCaptionChange Function
|
Receives updated value when it changes. |
addCaptionButtonVisible boolean
(default true)
|
Control visiblility of action button. |
captionButtonPosition string
(default 'outside')
|
Position of action button. |
addCaptionButtonPosition any
(default 'outside')
|
|
renderInsideCaption any
|
Render a div with the given aspect ratio which does not
exceed the heigth of the viewport by setting an appropriate
max-width on the container.
Wrap content in FitViewport.Content element:
| Name | Description |
|---|---|
file Object?
|
Use width/height of file to calculate aspect ratio. |
aspectRatio number?
|
Aspect ratio of div. |
fill string?
|
Ignore aspect ration and fill viewport vertically. |
scale number?
|
Only take up fraction of the viewport height supplied as value between 0 and 1. |
fallbackAspectRatio any
|
|
children any
|
Render an image file.
| Name | Description |
|---|---|
imageFile Object
|
Image file obtained via
useFile
.
|
variant (string | Array<string>)?
|
Paperclip style to use. Defaults to
large. Pass an array (e.g.
['large', 'ultra']
) to emit a
srcset
with
width descriptors. The first entry is used as
src
fallback.
|
sizes string?
|
Sizes attribute for srcset. Defaults to
"100vw"
.
|
load boolean?
|
Whether to load the image. Can be used for lazy loading. |
structuredData boolean?
|
Whether to render a JSON+LD script tag. |
preferSvg boolean?
|
Use original if image is SVG. |
fill boolean
(default true)
|
Position absolute and fill parent. |
fit boolean
(default cover)
|
"contain"
or
"cover"
.
|
...
|
Render some text using the default typography scale.
| Name | Description |
|---|---|
inline string?
|
Render a span instread of a div. |
scaleCategory string
|
One of the styles
'heading-lg'
,
'heading-md'
,
'heading-sm'
,
'heading-xs'
,
'headingTagline-lg'
,
'headingTagline-md'
,
'headingTagline-sm'
,
'headingSubtitle-lg'
,
'headingSubtitle-md'
,
'headingSubtitle-sm'
,
'body'
,
'caption'
,
'question'
,
'questionAnswer'
,
'quoteText-lg'
,
'quoteText-md'
,
'quoteText-sm'
,
'quoteText-xs'
,
'quoteAttribution'
,
'counterNumber'
,
'counterUnit'
,
'counterDescription'
,
'infoTableLabel'
,
'infoTableValue
'.
'hotspotsTooltipTitle'
,
'hotspotsTooltipDescription
',
'hotspotsTooltipLink
',
'teaserTitle-lg'
,
'teaserTitle-md'
,
'teaserTitle-sm'
,
'teaserTagline-lg'
,
'teaserTagline-md'
,
'teaserTagline-sm'
,
'teaserDescription-lg'
,
'teaserDescription-md'
,
'teaserDescription-sm'
,
'teaserLink'
.
|
typographyVariant string?
|
Suffix for variant class name. |
typographySize string?
|
Suffix for size class name. |
children string
|
Nodes to render with specified typography. |
Render an SVG icon that can be customized in themes.
| Name | Description |
|---|---|
name string
|
Either: arrowLeft, arrowRight, back, checked, copyright, close, email, enterFullscreen, exitFullscreen, expand, facebook, gear, information, linkedIn, menu, muted, pause, play, share, telegram, textTracks, twitter, unmuted, world, whatsApp, arrowLeft, arrowRight, scrollDown, world |
width any
|
|
height any
|
|
renderFallback any
|
Render opt in prompt instead of children if third party consent cookie has been configured in theme options and user has not given consent for passed provider.
(Object)
| Name | Description |
|---|---|
props.providerName string
|
Only render children if user has given consent for this provider. |
props.children React.ReactElement
|
Children to conditionally render. |
props.wrapper function?
|
Function that receives children to allow wrapping opt-in prompt in custom elements. |
props.icon boolean
(default true)
|
Allow hiding the icon in the opt-in prompt. |
Display info tooltip with a link to opt out of third party embeds. Opt out url needs to be configured in theme options.
Render video file in MediaPlayer.
| Name | Description |
|---|---|
videoFile Object
|
Video file obtained via
useFile
.
|
posterImageFile number?
|
Poster image file obtained via
useFile
.
|
adaptiveMinQuality String?
|
Pass "high" or "fullhd" to use HLS/Dash playlists with at least given quality. |
defaultTextTrackFileId number?
|
Perma id of default text track file. |
load string?
|
Control lazy loading.
"auto"
(default),
"poster"
or
"none"
.
|
fit String?
|
"contain"
(default) or
"cover"
.
|
...
|
React hooks to obtain entry data.
Returns data generated by a Ruby lambda registered server
side via config.additional_frontend_seed_data.register.
(any)
const data = useAdditionalSeedData('someSeed');
Prevent parallel playback of multiple media elements.
(Object)
| Name | Description |
|---|---|
options.key number
|
Unique id used to identify the element. |
options.request boolean
|
Set to true to request audio focus. |
options.onLost Function
|
Callback that will be invoked if another element requests audio focus, thereby preempting your hold of audio focus. The callback should pause the element. |
Use inside a content element component to determine whether the component is being rendered inside the editor iframe, and whether the content element is currently selected. This can be used to implement simple inline editing capabilities like displaying extra information when the content element is selected.
const {isEditable, isSelected} = useContentElementEditorState();
Returns an object containing information about the scroll position
related lifecylce of the content element. Requires the lifecycle
option to be set to true in the frontend.contentElements.register
call for the content element's type.
shouldLoad is true if the content element should start lazy
load. Becomes true before shouldPrepare.
shouldPrepare is true if the content element is about to enter
the viewport.
isActive is true if the content element is completely in the
viewport.
(any)
(Function)
Invoked when content element has entered the viewport.
(Function)
Invoked when content element has left the viewport.
const {isActive, shouldPrepare} = useContentElementLifecycle();
Invokes a callback with the progress of the content element along a
range of its view timeline. Mirrors the concepts of CSS scroll
driven animations: The content element acts as the subject of a
view timeline of the page's scroll container. Requires the
viewTimeline option to be set to true in the
frontend.contentElementTypes.register call for the content
element's type.
Progress is passed to a callback instead of being returned to prevent rerendering the content element on every scroll frame.
(Object
= {})
| Name | Description |
|---|---|
options.range string
(default 'cover')
|
Which part of the content element's view timeline to measure:
|
options.onProgress Function?
|
Invoked with a number between 0 and 1 whenever progress along the range changes. Pass a falsy value to not observe scroll position at all. |
useContentElementViewTimelineProgress({
range: 'cover',
onProgress: progress => player.seekTo(progress)
});
Returns chapter containing the current scroll position.
const chapter = useCurrentChapter();
chapter // =>
{
id: 3,
permaId: 5,
title: 'Chapter 1',
summary: 'An introductory chapter',
chapterSlug: 'chapter-1'
}
Returns the credits string (rich text) of the entry.
const credits = useCredits();
credits // => "Credits: <a href="http://pageflow.com">pageflow.com</a>"
Returns boolean indicating whether dark variant has been activated for the widgets of the entry.
Returns a nested data structure representing the metadata of the entry.
const metaData = useEntryMetadata();
metaData // =>
{
id: 5,
locale: 'en',
shareProviders: {email: false, facebook: true},
share_url: 'http://test.host/test',
credits: 'Credits: Pageflow',
configuration: {darkWidgets: true}
}
Look up a file by its collection and perma id.
const imageFile = useFile({collectionName: 'imageFiles', permaId: 5});
imageFile // =>
{
id: 102,
permaId: 5,
width: 1000,
height: 500,
urls: {
large: 'https://...'
},
configuration: {
alt: '...'
}
}
Returns a collection of rights and source urls of the files referenced in the entry. If none of the files has a rights attribute configured, it falls back to the default file rights of the entry's site, otherwise returns an empty array.
Lists all files of the entry unless the file_rights_from_references feature is enabled, since schemas do not cover all content element types yet.
const fileRights = useFileRights();
fileRights // => [{text: 'author of image 1', urls: ['https://example.com/source-url']}]
Use translations in frontend elements. Uses the configured locale
of the current entry by default. Note that only translation keys
from the pageflow_scrolled.public scope are universally
available.
to render translations for inline editing controls in the editor
preview, you can pass "ui" as locale option and use
translations from the pageflow_scrolled.inline_editing scope.
(Object?)
| Name | Description |
|---|---|
options.locale (
(default "entry")
|
Pass
"ui"
to use the locale of the user interface instead. Note
that this option selects which of the two locales to use. It does
not take a locale name.
|
const {t} = useI18n();
t('pageflow_scrolled.public.some.key')
const {t} = useI18n({locale: 'ui'});
t('pageflow_scrolled.inline_editing.some.key')
Use inside a content element component to determine whether the component is being rendered in a static preview, e.g. editor thumbnails.
const isStaticPreview = useIsStaticPreview();
Returns a nested data structure representing the legal info of the entry. Each legal info is separated into label and url to use in links. Both label and url can be blank, depending on the configuration.
const legalInfo = useLegalInfo();
legalInfo // =>
{
imprint: {
label: '',
url: ''
},
copyright: {
label: '',
url: ''
},
privacy: {
label: '',
url: ''
}
}
Obtain a locale to translate or format data in. Returns the configured locale of the current entry by default.
Elements that are rendered on top of the entry in the editor
preview or while reviewing an entry are part of the user interface
rather than of the entry itself. Pass "ui" as locale option to
obtain the locale of the user interface for those.
(Object?
= {})
| Name | Description |
|---|---|
options.locale (
(default "entry")
|
Pass
"ui"
to obtain the locale of the user interface instead.
Note that this option selects which of the two locales to use. It
does not take a locale name.
|
const locale = useLocale();
date.toLocaleDateString(locale)
const locale = useLocale({locale: 'ui'});
Returns boolean indicating whether the entry is currently muted.
Returns boolean indicating whether viewport orientation is currently portrait.
(any)
Returns the privacy link URL, label and link props. When vendors are passed, the vendors query parameter and consent hash are appended to the URL. Handles absolute, protocol-relative and relative URLs.
const {url, label, props} = usePrivacyLink({vendors: 'youtube'});
// props => {href: '...', target: '_blank', rel: 'noreferrer noopener'}
Returns a list of attributes (iconName, name and url) of all configured share
providers of the entry. The url provides a %
(Object)
| Name | Description |
|---|---|
$0.isPhonePlatform any
|
const shareProviders = useShareProviders(options);
shareProviders // =>
[
{
iconName: 'facebook',
name: 'Facebook',
url: http://www.facebook.com/sharer/sharer.php?u=%<url>s
},
{
iconName: 'twitter',
name: 'Twitter',
url: https://x.com/intent/post?url=%<url>s
}
]
Returns the share url of the entry.
const shareUrl = useShareUrl();
shareUrl // => "http://test.host/test"
Returns an object containing theme asset paths.
const theme = useTheme();
theme // =>
{
assets: {
logoDesktop: 'path/to/logoDesktop.svg',
logoMobile: 'path/to/logoMobile.svg'
},
options: {
// options passed to `themes.register` in `pageflow.rb` initializer
// with camleized keys.
}
}
Helper functions that can be used in content elements.
Resolve a palette color to a CSS custom property.
(any)
<div style={{backgroundColor: paletteColor(configuration.backgroundColor)}}>
Helpers for testing content elements, available via
pageflow-scrolled/testHelpers.
Construct data structure that resembles seed generated by server side JBuilder templates.
(Object?
= {})
| Name | Description |
|---|---|
options.imageFileUrlTemplates Object?
|
Mapping of url template names to url templates. |
options.fileUrlTemplates Object?
|
Mapping of file collection names to mappings of url template names to url templates. |
options.fileModelTypes Object?
|
Mapping of file collection names to model types. Only needed to associate nested files with their parent. Collections of file types registered by plugins get a placeholder model type. |
options.prettyUrl String?
|
The entry's url (Default share url). |
options.shareUrlTemplates Object?
|
Mapping of share provider names to sharing urls. |
options.defaultFileRights String?
|
Default file rights of entry's account. |
options.legalInfo Object?
|
imprint, copyright and privacy information of entry. |
options.themeOptions Object?
|
Options set via theme registration. |
options.themeAssets Object?
|
Paths to theme assets. |
options.themeTranslations Object?
|
Pre-merged theme translations (e.g., scales). |
options.entry Object?
|
attributes of entry. |
options.imageFiles Array?
|
Array of objects with image file attributes of entry. |
options.videoFiles Array?
|
Array of objects with video file attributes of entry. |
options.audioFiles Array?
|
Array of objects with audio file attributes of entry. |
options.textTrackFiles Array?
|
Array of objects with text track file attributes of entry. |
options.storylines Array?
|
Array of objects with storyline attributes of entry. |
options.chapters Array?
|
Array of objects with chapter attributes of entry. |
options.sections Array?
|
Array of objects with section attributes of entry. |
options.contentElements Array?
|
Array of objects with content element attributes of entry. |
options.widgets Array?
|
Array of objects with widget attributes of entry. |
options.additionalSeedData Object?
|
Seed data by name. |
options.consentVendors Array?
|
Server rendered consent vendor data. |
options.contentElementConsentVendors Object?
|
Consent vendor name by content element id. |
options.cutOff any
|
|
options.embed any
|
|
options.originUrl any
|
|
options.fileLicenses any
|
|
options.entryTranslations any
|
|
options.fileReferenceLocations any
|
|
...customFiles
|
Object:
Data that resembles seed generated by server side rendering.
Render a component that depends on entry state. Accepts all options
supported by render of
@testing-library/react.
The seed option can be used to simulate rendering the component
in the published entry. Data passed in this option would normally
be rendered in a server side JBuilder template.
The setup option can be used to simulate rendering the component
in the editor where data is synchronized from Backbone models.
To be able to render components that expect the result of certain hooks as part of their props, instead of a React component, you can pass a function returning a React component as first parameter. The function will be evaluated in a context of a React component and can thus make use of hooks
// DOES NOT WORK
renderInEntry(<Image file={useFile({collectionName: 'imageFiles', permaId: 4})} />, {seed});
// WORKS
renderInEntry(() => <Image file={useFile({collectionName: 'imageFiles', permaId: 4})} />, {seed});
When using the rerender function from the result, you again need
to use the same type of parameter you passed to the original
renderInEntry call.
((React.Component | Function))
React component or function returning a React component
(Object?
= {})
| Name | Description |
|---|---|
options.seed Object?
|
Seed data for entry state. Passed through normalizeSeed . |
options.setup Function?
|
Function that gets called with the internal entry state dispatch
function. The normalized seed constructed from the
seed
option
is passed as a second parameter.
|
options.wrapper any
|
|
options.consent any
(default Consent.create())
|
|
options.phonePlatform any
|
|
...options
|
Provide context as if component was rendered inside of a content element.
Returns additional functions to control content element scroll
lifecycle, view timeline progress, editor commands, and storyline
mode: simulateScrollPosition, simulateScrollProgress,
triggerEditorCommand, and simulateStorylineMode.
simulateScrollProgress passes the given progress to all
useContentElementViewTimelineProgress callbacks, no matter which
range they observe. Pass a range option to only invoke callbacks
observing that range.
(any)
| Name | Description |
|---|---|
options.inlineEditing (boolean | Object)?
|
Opt the content element into an inline-editing context (matching
what's available when inline editing is loaded in production).
Pass
true
for editable defaults, or an object to override:
{isEditable, isSelected, transientState}
.
transientState
is
installed as the
setTransientState
function on the editor state
context — pass a jest spy to assert on it. Omitting the option
gives the frontend-mode defaults (
isEditable: false, isSelected: false, setTransientState: noop
) without wrapping any
inline-editing-only providers (DndProvider, editor command
emitter).
|
options.phonePlatform Object?
(default false)
|
Fake result of
usePhonePlatform
.
|
options.consentState Object?
(default 'accepted')
|
Pass 'undecided' to render third party consent opt in. |
options.wrapper any
|
|
options.seed any
|
|
...options
|
(Function)
React component or function returning a React component.
const {getByRole, simulateScrollPosition, triggerEditorCommand, simulateStorylineMode} =
renderInContentElement(<MyContentElement />, {
seed: {...},
inlineEditing: {isSelected: true}
});
simulateScrollPosition('near viewport');
simulateScrollProgress(0.5);
simulateScrollProgress(0.5, {range: 'pinned'});
triggerEditorCommand({type: 'HIGHLIGHT'});
simulateStorylineMode('background');
Takes the same options as renderInEntry but returns additional helper function to the return value of the `useContentElementLifecycle` hook:
const {simulateScrollPosition} = renderInEntry(...)
simulateScrollPosition('near viewport')
// => Turns `shouldLoad` and `shouldPrepare` to true
(any)
(any)
Render a hook that depends on entry state. Accepts all options
supported by renderHook of
@testing-library/react-hooks
Can be used to test selector hooks which extract information from the entry state.
(Function)
Function that calls the hook.
(Object?
= {})
| Name | Description |
|---|---|
options.seed Object?
|
Seed data for entry state. Passed through normalizeSeed . |
options.setup Function?
|
See renderInEntry . |
options.wrapper any
|
|
options.phonePlatform any
|
|
...options
|
Register the public content element matchers for the surrounding
describe block: toContainContentElementBox and
toContainFitViewport.
Call inside a describe block. Content element plugins can use this to
assert that their component opts into the framework chrome correctly.
import {renderInContentElement, useContentElementMatchers} from 'pageflow-scrolled/testHelpers';
describe('MyContentElement', () => {
useContentElementMatchers();
it('renders inside a box', () => {
const {container} = renderInContentElement(<MyContentElement />);
expect(container).toContainContentElementBox();
});
});
Assert that the subject contains a ContentElementBox and, optionally,
that the box has specific theme styles applied. Registered via
useContentElementMatchers.
The subject is a DOM element, e.g. the container returned by
renderInContentElement. Negate with .not to assert that no
box is present.
(any)
expect(container).toContainContentElementBox();
expect(container).toContainContentElementBox({borderRadius: 'circle', boxShadow: 'md'});
expect(container).not.toContainContentElementBox();
Assert that the subject contains a FitViewport and, optionally, that
it has a specific aspect ratio. Registered via
useContentElementMatchers.
The subject is a DOM element, e.g. the container returned by
renderInContentElement. Negate with .not to assert that no
FitViewport is present.
(any)
(Object
= {})
| Name | Description |
|---|---|
$1.aspectRatio any
|
expect(container).toContainFitViewport();
expect(container).toContainFitViewport({aspectRatio: 'square'});
Render the configuration editor of a content element. Content element packs can use this to assert how their editor integration behaves.
import {renderContentElementConfigurationEditor} from 'pageflow-scrolled/testHelpers';
Register the configuration editor matchers for the surrounding
describe block: toRenderInputsMatching.
Call inside a describe block. Content element plugins can use this to
assert that their configuration schema keeps up with their editor
integration.
import {
renderContentElementConfigurationEditor, useConfigurationEditorMatchers
} from 'pageflow-scrolled/testHelpers';
describe('myContentElement/editor', () => {
useConfigurationEditorMatchers();
it('renders inputs described by schema', () => {
expect(() => renderContentElementConfigurationEditor({entry, contentElement}))
.toRenderInputsMatching(schema);
});
});
Assert that all inputs rendered by a configuration editor are described by a configuration schema. Registered via useConfigurationEditorMatchers.
The subject is a function that renders the configuration editor. Only inputs of the rendered form are seen. Views on nested routes render a form of their own and are checked against the part of the schema that describes the object they configure.
(any)
(Object)
Configuration schema of the subject.
(Object
= {})
| Name | Description |
|---|---|
$2.path any
(default [])
|
expect(() => renderContentElementConfigurationEditor({entry, contentElement}))
.toRenderInputsMatching(require('./schema.json'));
expect(() => renderBackboneView(new SidebarEditAreaView(options)))
.toRenderInputsMatching(require('../schema.json'), {path: ['areas', '*']});
Helper functions to use in content element stories.
When generating a .storybook/seed.json via the
pageflow_scrolled:storybook:seed:setup Rake task, a couple of
examples files are created. This method allows getting the perma id
of one of those files from one of the following reference names:
(string)
Name of a predefined file from the seed JSON file.
// inlineImage/stories.js
import './frontend';
import {storiesOfContentElement, filePermaId} from 'pageflow-scrolled/spec/support/stories';
storiesOfContentElement(module, {
typeName: 'inlineImage',
baseConfiguration: {id: filePermaId('imageFiles', 'turtle')}
});
Add default set of stories for content element to render it in different settings (e.g. different section layouts).
(Object)
the module global available in every file
(Object)
(string)
Name the content element is registered as.
(Object)
Configuration to use for all added stories. A heading element,
for example, would require a text.
(boolean
= false)
Set to true, to include a story which renders the content element
in a state where consent has not yet been given.
(boolean
= false)
Set to true, to include stories which render the content element
with file rights displayed inline.
(boolean
= false)
Set to true, to add further inline file rights stories in which
files are marked as AI generated or AI modified.
// heading/stories.js
import './frontend';
import {storiesOfContentElement, filePermaId} from 'pageflow-scrolled/spec/support/stories';
storiesOfContentElement(module, {
typeName: 'heading',
baseConfiguration: {
text: 'Some Heading'
},
variants: [
{
name: 'Large',
configuration: {size: 'large'},
themeOptions: {
properties: {
root: {
accentColor: '#0f0'
}
}
}
}
]
});