This is an internal documentation. There is a good chance you’re looking for something else. See Disclaimer.
Custom Widgets¶
Custom widgets are independent applications, tocco-apps, that can be embedded on external websites.
Widget package¶
Create a new package for your widget in the widgets
folder:
yarn plop Package
Add the widget to a bundle:
yarn plop Bundle app
The src/main.js
is the entry point of the widget.
Env¶
Define the env
inside the initApp
of the main.js
. There is a helper function env.setInputEnvs(input)
which sets the env vars from the input automatically.
Without the correct envs the core component will not behave properly for widgets (e.g. entity-detail
should not render the meta-information inside the footer).
import {env} from 'tocco-util'
...
const initApp = (id, input, events, publicPath) => {
const content = <MyWidget />
// set env from input
env.setInputEnvs(input)
const store = appFactory.createStore(reducers, sagas, input, packageName)
...
return appFactory.createApp(packageName, content, store, {...})
}
Input / PropTypes¶
The widgets receives the config from the Widget Config entity as input object with some additional meta information:
appContext
object containing thewidgetConfigKey
backendUrl
locale
Note
The selection
prop type, which has been added automatically, can be removed for widgets.
Example:
import PropTypes from 'prop-types'
import {appContext} from 'tocco-util'
const initApp = (...) => {
...
}
...
const ExampleWidgetApp = props => {
...
}
ExampleWidgetApp.propTypes = {
appContext: appContext.propTypes.isRequired,
backendUrl: PropTypes.string,
locale: PropTypes.string,
...
}
Name |
Description |
Example |
---|---|---|
appContext |
Meta information about the embedded widget.
Available |
{
"embedType": "widget",
"widgetConfigKey": "123",
}
|
backendUrl |
The base url of the nice2 backend. |
|
locale |
The locale of the widget. If the |
|
Visibility status integration¶
Your widget app will get a function onVisibilityStatusChange
as an input property. This can be used to communicate to the system running the widget
that some state, in which it might want to display some content, was reached. An example are the status list
and detail
in the entity-browser
app
that get triggered when the user navigates to one of the corresponding sites, or the success
status in the event-registration
app that gets triggered
when the registration is successfully created.
See Visibility status configuration on how to configure visibility status to be available on a widget config.
When implementing your own visibility status, we recommend creating a visibilityStatus.js
status file in the root of your src
folder, containing an
object of all your implemented visibility status and then only using the values defined in there. This ensures no typo is made when changing visibilty status
which would create hard to debug errors, as well as serving as a common way to document all available status in code.
Instead of passing onVisibilityStatusChange
through your app and calling it by hand, use the action creator
externalEvents.fireVisibilityStatusChangeEvent
.
export const VisibilityStatus = {
list: 'list',
detail: 'detail',
success: 'success'
}
// ExampleAppContainer.js
import {connect} from 'react-redux'
import {externalEvents} from 'tocco-app-extensions'
const mapActionCreators = {
fireVisibilityStatusChangeEvent: externalEvents.fireVisibilityStatusChangeEvent
}
const mapStateToProps = state => ({})
export default connect(mapStateToProps, mapActionCreators)(ExampleApp)
// ExampleApp.js
import {VisibilityStatus} from './visibilityStatus'
const ExampleApp = ({fireVisibilityStatusChangeEvent}) => {
fireVisibilityStatusChangeEvent([VisibilityStatus.success])
}
For externalEvents.fireVisibilityStatusChangeEvent
to work you’ll have to make sure that externalEvents
has been added to your apps store
with the correct events.
const EXTERNAL_EVENTS = ['onVisibilityStatusChange']
const initApp = (id, input, events, publicPath) => {
const store = appFactory.createStore(reducers, sagas, input, packageName)
externalEvents.addToStore(store, state => appFactory.getEvents(EXTERNAL_EVENTS, state.input))
}
Reuse the entity browser¶
Often the entity browser can be reused if a custom widgets is implemented. For example if a widget configuration parameter influences the form definition
(e.g. box removed or creation allowed), the modifyFormDefinition
of the EntityBrowserApp
can be used. All other input properties are passed to the entity-browser.
As an example:
const ExampleApp = ({
allowCreate,
reportIds,
searchFilters,
limit,
backendUrl,
businessUnit,
appContext,
intl,
onVisibilityStatusChange
}) => {
const modifyFormDefinition = formDefinition => {
if (allowCreate) {
return form.addCreate(formDefinition, intl)
} else {
return form.removeActions(formDefinition, ['new', 'copy'])
}
}
return (
<EntityBrowserApp
entityName="ENTITY"
formBase="FORM_BASE"
searchFilters={searchFilters}
limit={limit}
modifyFormDefinition={modifyFormDefinition}
backendUrl={backendUrl}
businessUnit={businessUnit}
appContext={appContext}
reportIds={reportIds}
onVisibilityStatusChange={onVisibilityStatusChange}
/>
)
}
The app-extensions provides serveral helper methods for modifing the form definition (See JSDoc on how to use them). It’s possible to dynamically modify the form definition. As an example only remove a box on the detail if an entity is related to a certain lookup entity value.
Sometimes it is necessary to add a field which is not part of the form definition (e.g. a mandatory checkbox for the privacy protection).
Add a pseudo form field to the form definition. Additionally use the modifyEntityPaths
to add the entity paths of the pseudo fields.
Once the form is initialized modifying the entity paths has no effect.
const ExampleApp = ({
// ...
}) => {
const fields = [
form.createCheckbox('privacy_protection', false, 'privacy protection label', true)
]
const modifyFormDefinition = formDefinition => form.appendChildrenToBox(
formDefinition,
'master_data',
fields.map(f => f.formField))
const modifyEntityPaths = entityPaths => ({
...form.getMergedEntityPaths(fields),
...entityPaths
})
// also pass other properties to entity-browser
return (
<EntityBrowserApp
modifyFormDefinition={modifyFormDefinition}
modifyEntityPaths={modifyEntityPaths}
/>
)
}