mirror of
https://github.com/strapi/strapi.git
synced 2025-12-27 07:03:38 +00:00
Created dynamic to injectComponent from one plugin into another, write documentation accordingly
This commit is contained in:
parent
d77456e78c
commit
160ffe8626
@ -127,6 +127,198 @@ export default compose(
|
||||
|
||||
***
|
||||
|
||||
## Inject design
|
||||
|
||||
The `ExtendComponent` allows you to inject design from one plugin into another.
|
||||
|
||||
### Example
|
||||
|
||||
Let's say that you want to enable another plugin to inject a component into the top area of your plugin's container called `FooPage`;
|
||||
|
||||
**Path —** `./plugins/my-plugin/admin/src/containers/FooPage/actions.js`.
|
||||
```js
|
||||
import {
|
||||
ON_TOGGLE_SHOW_LOREM,
|
||||
} from './constants';
|
||||
|
||||
export function onToggleShowLorem() {
|
||||
return {
|
||||
type: ON_TOGGLE_SHOW_LOREM,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Path —** `./plugins/my-plugin/admin/src/containers/FooPage/index.js`.
|
||||
```js
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators, compose } from 'redux';
|
||||
import { createStructuredSelector } from 'reselect';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// Import the ExtendComponent
|
||||
import ExtendComponent from 'components/ExtendComponent';
|
||||
|
||||
// Utils
|
||||
import injectReducer from 'utils/injectReducer';
|
||||
|
||||
// Actions
|
||||
import { onToggleShowLorem } from './action'
|
||||
|
||||
import reducer from './reducer';
|
||||
|
||||
// Selectors
|
||||
import { makeSelectShowLorem } from './selectors';
|
||||
|
||||
class FooPage extends React.Component {
|
||||
render() {
|
||||
const lorem = this.props.showLorem ? <p>Lorem ipsum dolor sit amet, consectetur adipiscing</p> : '';
|
||||
return (
|
||||
<div>
|
||||
<h1>This is FooPage container</h1>
|
||||
<ExtendComponent
|
||||
area="top"
|
||||
container="FooPage"
|
||||
plugin="my-plugin"
|
||||
{...props}
|
||||
/>
|
||||
{lorem}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
FooPage.propTypes = {
|
||||
onToggleShowLorem: PropTypes.func.isRequired,
|
||||
showLorem: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return bindActionCreators(
|
||||
{
|
||||
onToggleShowLorem,
|
||||
},
|
||||
dispatch,
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
showLorem: makeSelectShowLorem(),
|
||||
});
|
||||
|
||||
const withConnect = connect(mapDispatchToProps, mapDispatchToProps);
|
||||
const withReducer = injectReducer({ key: 'fooPage', reducer });
|
||||
|
||||
export default compose(
|
||||
withReducer,
|
||||
withConnect,
|
||||
)(FooPage);
|
||||
```
|
||||
|
||||
**Path —** `./plugins/my-plugin/admin/src/containers/FooPage/reducer.js`.
|
||||
```js
|
||||
import { fromJS } from 'immutable';
|
||||
import { ON_TOGGLE_SHOW_LOREM } from './constants';
|
||||
|
||||
const initialState = fromJS({
|
||||
showLorem: false,
|
||||
});
|
||||
|
||||
function fooPageReducer(state= initialState, action) {
|
||||
switch (action.type) {
|
||||
case ON_TOGGLE_SHOW_LOREM:
|
||||
return state.set('showLorem', !state.get('showLorem'));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default fooPageReducer;
|
||||
```
|
||||
|
||||
**Path —** `./plugins/my-plugin/admin/src/containers/FooPage/selectors.js`.
|
||||
```js
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
/**
|
||||
* Direct selector to the fooPage state domain
|
||||
*/
|
||||
|
||||
const selectFooPageDomain = () => state => state.get('fooPage');
|
||||
|
||||
/**
|
||||
* Other specific selectors
|
||||
*/
|
||||
|
||||
const makeSelectShowLorem = () => createSelector(
|
||||
selectFooPageDomain(),
|
||||
(substate) => substate.get('showLorem'),
|
||||
);
|
||||
|
||||
export { makeSelectShowLorem };
|
||||
```
|
||||
|
||||
That's all now your plugin's container is injectable!
|
||||
|
||||
Let's see how to inject some design from another plugin.
|
||||
|
||||
|
||||
### Create your injectedComponent
|
||||
**Path -** `./plugins/another-plugin/admin/src/extendables/BarContainer/index.js`;
|
||||
```js
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// Import our Button component
|
||||
import Button from 'components/Button';
|
||||
|
||||
// Other imports such as actions, selectors, sagas, reducer...
|
||||
|
||||
class BarContainer extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Button primary onClick={this.props.onToggleShowLorem}>
|
||||
Click me to show lorem paragraph
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
BarContainer.propTypes = {
|
||||
onToggleShowLorem: PropTypes.func,
|
||||
};
|
||||
|
||||
BarContainer.defaultProps = {
|
||||
onToggleShowLorem: () => {},
|
||||
};
|
||||
|
||||
export default BarContainer;
|
||||
```
|
||||
|
||||
### Tell the admin that you want to inject some design into another plugin
|
||||
|
||||
You have to create a file called `injectedComponents.js` at the root of your `another-plugin` src folder.
|
||||
|
||||
**Path —** `./plugins/another-plugin/admin/src/injectedComponents.js`.
|
||||
```js
|
||||
import BarContainer from 'extendables/BarContainer';
|
||||
|
||||
// export an array containing all the injected components
|
||||
export default [
|
||||
{
|
||||
area: 'top',
|
||||
container: 'FooPage',
|
||||
injectedComponent: BarContainer,
|
||||
plugin: 'my-plugin',
|
||||
},
|
||||
];
|
||||
```
|
||||
Just by doing so, the `another-plugin` will add a `Button` which toggles the `lorem` paragraph in the `FooPage` view.
|
||||
|
||||
***
|
||||
|
||||
## Routeless container store injection
|
||||
|
||||
See the basic container's store injection [documentation](./development.md#using-redux-sagas).
|
||||
|
||||
@ -74,6 +74,14 @@ export default Button;
|
||||
|
||||
***
|
||||
|
||||
## ExtendComponent
|
||||
|
||||
ExtendComponent allows a plugin to injectDesign into another one.
|
||||
|
||||
> Refer to the advanced plugin [documentation](./advanced.md#inject-design) to see how to use it.
|
||||
|
||||
***
|
||||
|
||||
## Ico
|
||||
|
||||
Ico components that works with fontAwesome.
|
||||
|
||||
@ -64,7 +64,7 @@
|
||||
padding-left: 1.6rem;
|
||||
padding-right: 1.6rem;
|
||||
&:before {
|
||||
content: '\F02d ';
|
||||
content: '\F067';
|
||||
font-family: 'FontAwesome';
|
||||
font-weight: 600;
|
||||
font-size: 1.3rem;
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
"app.components.HomePage.welcome": "Welcome on board!",
|
||||
"app.components.HomePage.description.part1": "We are happy to have you as one of our users!",
|
||||
"app.components.HomePage.description.part2": "You are now a member of our community which will help you building your dreamed app.",
|
||||
"app.components.HomePage.button": "Create your first app",
|
||||
"app.components.HomePage.button": "Create your first content type",
|
||||
"app.components.HomePage.feedback": "Feel free to ask questions or give us feedback by using one of the support channels below.",
|
||||
"app.components.LeftMenuFooter.poweredBy": "Proudly powered by",
|
||||
"app.components.LeftMenuLinkContainer.configuration": "Configuration",
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
"app.components.HomePage.welcome": "Bienvenue à bord!",
|
||||
"app.components.HomePage.description.part1": "Nous somme heureux de vous compter parmi nos utilisateurs",
|
||||
"app.components.HomePage.description.part2": "Vous faites désormais parti de notre communauté qui peut vous aider à développer votre application.",
|
||||
"app.components.HomePage.button": "Créez votre application",
|
||||
"app.components.HomePage.button": "Créez votre premier type de contenu",
|
||||
"app.components.HomePage.feedback": "N'hésitez pas à utiliser un des channels ci-dessous pour poser vos questions ou nous donner vos retours.",
|
||||
"app.components.LeftMenuFooter.poweredBy": "Propulsé par",
|
||||
"app.components.LeftMenuLinkContainer.configuration": "Configuration",
|
||||
|
||||
@ -25,6 +25,13 @@ const tryRequire = (bootstrap = false) => {
|
||||
const bootstrap = tryRequire(true);
|
||||
const pluginRequirements = tryRequire();
|
||||
|
||||
let injectedComponents;
|
||||
|
||||
try {
|
||||
injectedComponents = require('injectedComponents').default;
|
||||
} catch(err) {
|
||||
injectedComponents = [];
|
||||
}
|
||||
|
||||
// Plugin identifier based on the package.json `name` value
|
||||
const pluginPkg = require('../../../../package.json');
|
||||
@ -77,6 +84,7 @@ window.Strapi.registerPlugin({
|
||||
pluginRequirements,
|
||||
preventComponentRendering: false,
|
||||
blockerComponent: null,
|
||||
injectedComponents,
|
||||
blockerComponentProps: {},
|
||||
});
|
||||
|
||||
|
||||
@ -0,0 +1,62 @@
|
||||
/*
|
||||
*
|
||||
* ExtendComponent
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { get, isEmpty } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class ExtendComponent extends React.Component {
|
||||
getInjectedComponent = () => {
|
||||
const injectedComponent = this.context.plugins.reduce((acc, plugin) => {
|
||||
|
||||
if (!isEmpty(plugin.toJS().injectedComponents)) {
|
||||
const injectedComponents = plugin.toJS().injectedComponents.filter((compo) => {
|
||||
|
||||
if (compo.plugin === this.props.plugin && compo.container === this.props.container && compo.area === this.props.area) {
|
||||
return compo;
|
||||
}
|
||||
});
|
||||
|
||||
return injectedComponents[0];
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return injectedComponent;
|
||||
}
|
||||
|
||||
render() {
|
||||
const Component = get(this.getInjectedComponent(), 'injectedComponent');
|
||||
const renderedComponent = Component ? <Component {...this.props} /> : '';
|
||||
|
||||
return (
|
||||
<div>
|
||||
{renderedComponent}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ExtendComponent.contextTypes = {
|
||||
plugins: PropTypes.object,
|
||||
router: PropTypes.object,
|
||||
updatePlugin: PropTypes.func,
|
||||
}
|
||||
|
||||
ExtendComponent.propTypes = {
|
||||
area: PropTypes.string.isRequired,
|
||||
container: PropTypes.string.isRequired,
|
||||
children: PropTypes.node,
|
||||
plugin: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
ExtendComponent.defaultProps = {
|
||||
children: <div />,
|
||||
};
|
||||
|
||||
export default ExtendComponent;
|
||||
@ -177,7 +177,7 @@ export class Edit extends React.Component {
|
||||
const mainField = get(this.props.models, `${this.props.currentModelName}.info.mainField`) || primaryKey;
|
||||
const pluginHeaderTitle = this.props.isCreating ? 'New entry' : templateObject({ mainField }, this.props.record.toJS()).mainField;
|
||||
const pluginHeaderDescription = this.props.isCreating ? 'New entry' : `#${this.props.record && this.props.record.get(primaryKey)}`;
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={`container-fluid ${styles.containerFluid}`}>
|
||||
@ -229,6 +229,12 @@ export class Edit extends React.Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Edit.contextTypes = {
|
||||
plugins: PropTypes.object,
|
||||
updatePlugin: PropTypes.func,
|
||||
};
|
||||
|
||||
/* eslint-disable react/require-default-props */
|
||||
Edit.propTypes = {
|
||||
cancelChanges: PropTypes.func.isRequired,
|
||||
|
||||
@ -45,4 +45,4 @@
|
||||
"npm": ">= 5.3.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user