Create your own Volto site!Create your own Volto site!
Plone Conference 2019, FerraraPlone Conference 2019, Ferrara
-
“the most dangerous guy in the world”
Rob Gietema @robgietema
What will we cover?What will we cover?
Bootstrapping
Static Resources
Theming
Override Components
i18n
Override Views
Custom Views
Custom Widgets
Rich Text Editor Settings
Actions & Reducers
Bootstrapping a ProjectBootstrapping a Project
$ npx @plone/create-volto-app my-volto-app
Running the ProjectRunning the Project
$ cd my-volto-app
$ yarn start
Running the ProjectRunning the Project
Code WalkthroughCode Walkthrough
my-volto-app
├── README.md
├── node_modules
├── package.json
├── public
│ ├── favicon.ico
│ └── robots.txt
├── theme
│ └── theme.config
├── locales
│ ├── de
│ ├── en
│ │ └── LC_MESSAGES
│ │ └── volto.po
│ ├── nl
│ ├── de.json
│ ├ j
Static ResourcesStatic Resources
$ ls public
favicon.ico robots.txt
ThemingTheming
ThemingTheming
$ cat theme.config
/* Global */
@site : 'pastanaga';
@reset : 'pastanaga';
/* Elements */
@button : 'pastanaga';
@container : 'pastanaga';
@divider : 'pastanaga';
@flag : 'pastanaga';
@header : 'pastanaga';
@icon : 'pastanaga';
@image : 'pastanaga';
@input : 'pastanaga';
...
ThemingTheming
theme
└─ themes
└─ pastanaga
└─ elements
└─ button.overrides
└─ button.variables
└─ container.overrides
└─ container.variables
...
ThemingTheming
$ cat button.variables
/* Button */
@verticalMargin: 0em;
@horizontalMargin: 0.25em;
@backgroundColor: #E0E1E2;
@backgroundImage: none;
@background: @backgroundColor @backgroundImage;
@lineHeight: 1em;
/* Button defaults to using same height as input globally */
@verticalPadding: @inputVerticalPadding;
@horizontalPadding: 1.5em;
...
ThemingTheming
$ cat button.overrides
/*******************************
Theme Overrides
*******************************/
.ui.circular.icon.button {
padding: 0.75em 0.25em 0;
.icon {
font-size: 2em;
}
}
...
Override ComponentsOverride Components
customizations
└─ components
└─ theme
└─ Logo
└─ Logo.svg
InternationalizationInternationalization
react-intl
Extracting i18n StringsExtracting i18n Strings
$ yarn i18n
Extracting messages from source files...
Synchronizing messages to pot file...
Synchronizing messages to po files...
Generating the json files...
done!
Making Text TranslatableMaking Text Translatable
import { FormattedMessage } from 'react-intl';
<FormattedMessage
id="Some string"
defaultMessage="Some string"
/>
Translate AttributesTranslate Attributes
import { defineMessages, injectIntl, intlShape } from 'react-intl';
const messages = defineMessages({
site: {
id: 'Site',
defaultMessage: 'Site',
},
});
...
export default injectIntl(Logo);
PropTypesPropTypes
intl: intlShape.isRequired
UsageUsage
<Link to="/" title={intl.formatMessage(messages.site)}>
Override ViewsOverride Views
customizations
└─ components
└─ theme
└─ View
└─ SummaryView.jsx
Custom ViewsCustom Views
Custom ViewsCustom Views
/**
* Full view component.
* @module components/FullView/FullView
*/
import React from 'react';
import PropTypes from 'prop-types';
import Helmet from 'react-helmet';
import { Link } from 'react-router-dom';
import { Container, Image } from 'semantic-ui-react';
import { FormattedMessage } from 'react-intl';
/**
* Full view component class.
* @function FullView
* @param {Object} content Content object.
i
Custom ViewsCustom Views
components/index.js
import FullView from './FullView/FullView';
export { FullView };
Custom ViewsCustom Views
config.js
import { FullView } from './components';
export const views = {
...defaultViews,
layoutViews: {
...defaultViews.layoutViews,
full_view: FullView,
},
};
Custom WidgetsCustom Widgets
Custom WidgetsCustom Widgets
components/RatingWidget/RatingWidget.jsx
/**
* RatingWidget component.
* @module components/RatingWidget/RatingWidget
*/
import React from 'react';
import PropTypes from 'prop-types';
import { Form, Grid, Icon, Label, Rating } from 'semantic-ui-react';
import { map } from 'lodash';
import { defineMessages, injectIntl, intlShape } from 'react-intl';
const messages = defineMessages({
default: {
id: 'Default',
defaultMessage: 'Default',
},
i i
Custom WidgetsCustom Widgets
components/index.js
import RatingWidget from './RatingWidget/RatingWidget';
export { RatingWidget };
Custom WidgetsCustom Widgets
config.js
import { RatingWidget } from './components';
export const widgets = {
...defaultWidgets,
id: {
...defaultWidgets.id,
rating: RatingWidget,
},
};
Pastanaga Icon SystemPastanaga Icon System
Pastanaga Icon SystemPastanaga Icon System
Injects SVG icons in the markup
import myCustomSVG from '~/icons/my-svg.svg';
import { Icon } from '@plone/volto/components';
<Icon name={myCustomSVG} />
Rich Text Editor SettingsRich Text Editor Settings
Rich Text Editor SettingsRich Text Editor Settings
config.js
import React from 'react';
import createInlineStyleButton from 'draft-js-buttons/lib/utils/creat
import Icon from '@plone/volto/components/theme/Icon/Icon';
import underlineSVG from '@plone/volto/icons/underline.svg';
const UnderlineButton = createInlineStyleButton({
style: 'UNDERLINE',
children: ,
});
Rich Text Editor SettingsRich Text Editor Settings
config.js
export const settings = {
...defaultSettings,
richTextEditorInlineToolbarButtons: [
UnderlineButton,
...defaultSettings.richTextEditorInlineToolbarButtons,
],
};
Actions & ReducersActions & Reducers
Actions & ReducersActions & Reducers
constants/ActionTypes.js
export const GET_FAQ = 'GET_FAQ';
Actions & ReducersActions & Reducers
actions/faq/faq.js
import { GET_FAQ } from '../../constants/ActionTypes';
export function getFaq() {
return {
type: GET_FAQ,
request: {
op: 'get',
path: `/@search?portal_type=faq`,
},
};
}
Actions & ReducersActions & Reducers
actions/index.js
import { getFaq } from './faq/faq';
export { getFaq };
Actions & ReducersActions & Reducers
reducers/faq/faq.js
import { GET_FAQ } from '../../constants/ActionTypes';
const initialState = {
error: null,
items: [],
loaded: false,
loading: false,
};
export default function faq(state = initialState, action = {}) {
switch (action.type) {
case `${GET_FAQ}_PENDING`:
return {
...state,
error: null,
loading: true,
Actions & ReducersActions & Reducers
reducers/index.js
import defaultReducers from '@plone/volto/reducers';
import faq from './faq/faq';
const reducers = {
...defaultReducers,
faq,
};
export default reducers;
Actions & ReducersActions & Reducers
components/FaqView/FaqView.jsx
/**
* Faq view.
* @module components/FaqView/FaqView
*/
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import Helmet from 'react-helmet';
import { FormattedMessage } from 'react-intl';
import { Container } from 'semantic-ui-react';
import { getFaq } from '../../actions';
/**
i
training.plone.org/5/volto/training.plone.org/5/volto/
Join the Sprint!Join the Sprint!
Questions?Questions?
slideshare.net/robgietema

How to create your own Volto site!