-
Notifications
You must be signed in to change notification settings - Fork 1
Runtime custom components
Certain sampo apps require more specific customised components than what the core sampo app has to offer. So far the solution has been to fork the sampo repo and simply edit the source code at will. The downside of this is that you end up with many apps that diverge significantly from the sampo core, thus making it very hard to even impossible to sync back up with the core sampo for bugfixes and new features.
The core idea of the changes we made in this fork has been to make sampo more generic and configurable. The ideal way to make a sampo app would be to have all changes happen outside of the core codebase and mounting all kinds of config as volumes into pre-built images. We have implemented a system that allows you to write your own components with all the freedom you need and use them in your sampo app without having to touch the core codebase.
You will need to copy the custom_components directory from the sampo core app which looks like this:
custom_components/
├── build.sh
├── components
│ ├── ExampleCustomComponent
│ │ ├── src
│ │ │ └── index.jsx
│ │ └── webpack.config.js
│ └── ExampleCustomFacet
│ ├── src
│ │ └── index.jsx
│ └── webpack.config.js
├── jsconfig.json
├── package.json
├── package-lock.json
└── shared
└── webpack.base.js
Under components you can create new directories for new components. Each of them needs a webpack.config.js to be able to build it
// ExampleCustomComponent/webpack.config.js
const base = require('../../shared/webpack.base.js')
module.exports = base('ExampleCustomComponent')In order to build your custom components simply run ./build.sh. All your custom components share 1 package.json.
Your built components are then saved to custom_components/dist; this is the directory you need to mount into your sampo-client container onto /app/custom-components.
Because the custom components get mounted into client container if you tried to import and bundle your own version of critical packages (like react) they wouldn't be able to work. For that reason we export certain critical packages so the custom components can use them.
If you want to use components or helper functions from the core sampo app that is possible since we also export those.
Right now what you can use is:
react: 'React',
'react-dom'
'react-intl-universal'
'tss-react/mui'
'react-redux'
'prop-types'
'lodash'
'qs'
'@mui/material/'
'@mui/icons-material/')
'@sampo-ui/components/'
'@sampo-ui/helpers/'
If you want to have auto-completion for sampo components and helpers in your ide you will have to clone the core sampo app.
Importing any package that is not already imported by the sampo client should not cause any issues. Importing non critical packages that the sampo client does import should also not cause any issues.
There are 2 kinds of custom components you can make: facets and resultClasses. They each receive a large set of props. Some of them are critical to be able to write a component, others might not always be needed but still useful in some cases. I tried pass the props in a way that is clean but still offers enough to allow freedom in writing your custom components.
The example custom components contain propTypes objects to show which props get passed.
Simply set your component or facet to "CustomComponent" and then pass "componentName" with the name of your custom component
Example of using a custom resultclass component:
"paginatedResultsConfig": {
"tabID": 0,
"component": "CustomComponent",
"componentName": "ExampleCustomComponent",
"tabPath": "table",
"tabIcon": "CalendarViewDay",
"propertiesQueryBlock": "workProperties",
"pagesize": 10,
"sortBy": null,
"sortDirection": null
}Example of using a custom facet:
"prefLabel": {
"containerClass": "three",
"facetType": "text",
"filterType": "CustomComponent",
"componentName": "ExampleCustomFacet",
"sortByPredicate": "skos:prefLabel",
"textQueryProperty": "skos:prefLabel"
},First of all in the client's index.js we have to make React and the other libraries available to the custom components. We do this by adding them to the window object:
window.React = React
window.ReactDOM = ReactDOM
window.__sharedLibraries = {
MUI,
MuiIcons,
intl,
tssReactMui: { withStyles },
reactRedux: { useSelector, useDispatch, connect },
PropTypes,
_,
qs,
components,
helpers
}On the side of the custom components, when they get built they register themselves to window__customComponents. That makes it possible for the core app to find them.
In ResultClassRoute.js and FacetBar.js we added a new case for custom components. In this case we render a CustomComponentWrapper. This wrapper calls loadCustomComponent. This function will create a new <script/> tag with the built custom component as source and then resolve the exported component registered on window.__customComponents.
You can also create custom mapper functions. To do this create a file called ${portalID}/mappers.js in your configs. In there you can write your mapper functions as if you were to write them directly in the codebase. They will be loaded together with the rest of the configs and the built in mappers at startup.