Add front documentation

This commit is contained in:
soupette 2019-05-06 17:36:25 +02:00
parent 0632b4e28b
commit 88e0266f12

View File

@ -4,19 +4,229 @@
This feature is currenlty not available (Coming soon).
:::
<!-- This section explains how to create your plugin interface in the admin panel. Refer to the Plugin Development [Quick Start Section](./quick-start.md) to start the project in development mode.
## Introduction
## Admin panel
Strapi's admin panel and plugins system aim to be an easy and powerful way to create new features.
The admin panel is a [React](https://facebook.github.io/react/) application which can embed other React applications. These other React applications are the `admin` parts of each Strapi's plugins.
## Routing
### Admin Lifecycle
The admin package has the following lifecycle.
1. Retrieve all the installed plugin and and store them into the main redux store
2. Load until all the plugin emit the event `isReady`
3. Runtime
### Strapi global variable
The administration exposes a global variable thqt is accessible for all the plugins.
#### `strapi.backendURL`
Retrieve the back-end URL. (e.g. `http://localhost:1337`).
#### `strapi.currentLanguage`
Retrieve the administration panel default language (e.g. `en-US`)
#### `strapi.languages`
Array of the administration panel's supported languages. (e.g. `['ar', 'en', 'fr', ...]`).
#### `strapi.lockApp()`
Display a loader that will prevent the user from interacting with the application.
#### `strapi.unlockApp()`
Remove the loader so the user can interact with the application
#### `strapi.injectSaga`
Dynamically inject a plugin's saga.
**Path —** `plugins/my-plugin/admin/src/containers/App/index.js`.
```js
import React from 'react';
import { compose } from 'redux';
import pluginId from '../../pluginId';
import saga from './saga';
class App extends React.Component {
render() {
return null;
}
}
const withSaga = strapi.injectSaga({ key: 'app', saga, pluginId });
export default compose(withSaga)(App);
```
#### `strapi.injectReducer`
Dynamically inject a plugin's reducer.
**Path —** `plugins/my-plugin/admin/src/containers/App/index.js`.
```js
import React from 'react';
import { compose } from 'redux';
import pluginId from '../../pluginId';
import reducer from './reducer';
class App extends React.Component {
render() {
return null;
}
}
const withReducer = strapi.injectReducer({ key: 'app', reducer, pluginId });
export default compose(withReducer)(App);
```
#### `strapi.notification`
Display a notification (works with i18n message id). Use this command anywhere in your code.
```js
strapi.notification.error('app.notification.error');
strapi.notification.info('app.notification.info');
strapi.notification.success('app.notification.success');
strapi.notification.warning('app.notification.warning');
```
#### `strapi.remoteURL`
The administration url (e.g. `http://localhost:4000/admin`).
### Available hooks
The Admin container exposes hooks in which a plugin can run custom code.
(Documentation coming soon).
## Plugin development
(Coming soon).
### Main plugin object
Each plugin exports all its configurations in a object. This object is located in `my-plugin/admin/src/index.js`
Here are its properties:
| key | type | value |
| ------------------------- | -------- | --------------------------------------------------------------------------------- |
| blockerComponent | node | can be either `null` or React node (e.g. `() => <div />`) |
| blockerComponentProps | object | `{}` |
| description | string | `My awesome plugin` |
| id | string | `content-manager` |
| initializer | node | Refer to the [Initializer documentation](#initializer) |
| injectedComponents | array | Refer to the [Injected Component documentation](#injected-components) |
| leftMenuLinks | array | `[]` |
| lifecycles | function | Refer to the [Lifecycle documentation](#lifecycle) |
| mainComponent | node | The plugin's App container |
| preventComponentRendering | boolean | Wheter or not display the plugin's blockerComponent instead of the main component |
| trads | object | The plugin's translation files |
### Initializer
The component is generated by default when you create a new plugin. Use this component to execute some logic when the app is loading. When the logic has been executed this component should emit the `isReady` event so the user can interact with the application.
:::note
Below is the initializer component of the content-builder plugin.
It checks whether or not the autoreload feature is enabled and depending on this value changes the mainComponent of the plugin.
:::
```js
/**
*
* Initializer
*
*/
import React from 'react';
import PropTypes from 'prop-types';
import pluginId from '../../pluginId';
class Initializer extends React.PureComponent {
// eslint-disable-line react/prefer-stateless-function
componentDidMount() {
const {
admin: { autoReload, currentEnvironment },
} = this.props;
let preventComponentRendering;
let blockerComponentProps;
if (currentEnvironment === 'production') {
preventComponentRendering = true;
blockerComponentProps = {
blockerComponentTitle: 'components.ProductionBlocker.header',
blockerComponentDescription: 'components.ProductionBlocker.description',
blockerComponentIcon: 'fa-ban',
blockerComponentContent: 'renderButton',
};
} else {
// Don't render the plugin if the server autoReload is disabled
preventComponentRendering = !autoReload;
blockerComponentProps = {
blockerComponentTitle: 'components.AutoReloadBlocker.header',
blockerComponentDescription: 'components.AutoReloadBlocker.description',
blockerComponentIcon: 'fa-refresh',
blockerComponentContent: 'renderIde',
};
}
// Prevent the plugin from being rendered if currentEnvironment === PRODUCTION
this.props.updatePlugin(
pluginId,
'preventComponentRendering',
preventComponentRendering,
);
this.props.updatePlugin(
pluginId,
'blockerComponentProps',
blockerComponentProps,
);
// Emit the event plugin ready
this.props.updatePlugin(pluginId, 'isReady', true);
}
render() {
return null;
}
}
Initializer.propTypes = {
admin: PropTypes.object.isRequired,
updatePlugin: PropTypes.func.isRequired,
};
export default Initializer;
```
### Lifecycle
(Coming soon)
### Injected Components
(Coming soon)
### Routing
The routing is based on the [React Router V4](https://reacttraining.com/react-router/web/guides/philosophy), due to it's implementation each route is declared in the `containers/App/index.js` file.
Also, we chose to use the [Switch Router](https://reacttraining.com/react-router/web/api/Switch) because it renders a route exclusively.
::: note
Each route defined in a plugin must be prefixed by the plugin's id.
:::
**Route declaration :**
@ -25,9 +235,12 @@ Let's say that you want to create a route `/user` with params `/:id` associated
The declaration would be as followed :
**Path —** `plugins/my-plugin/admin/src/containers/App/index.js`.
```js
import React from 'react';
import UserPage from 'containers/UserPage';
import pluginId from '../../pluginId';
import UserPage from '../UserPage';
// ...
@ -38,7 +251,11 @@ class App extends React.Component {
return (
<div className={styles.myPlugin}>
<Switch>
<Route exact path="/plugins/my-plugin/user/:id" component={UserPage} />
<Route
exact
path={`/plugins/${pluginId}/user/:id`}
component={UserPage}
/>
</Switch>
</div>
);
@ -47,55 +264,8 @@ class App extends React.Component {
// ...
```
See the [Front-end Use Cases](./frontend-use-cases.md#handle-user-navigation) for more informations.
## Data flow
Each plugin has its own data store, so it stays completely independent from the others.
Data flow is controlled thanks to Redux and redux-sagas.
## Styling
The [Bootstrap styles](http://getbootstrap.com/) are inherited by the plugins. However, each component has its own styles, so it possible to completely customize it.
**See the [plugin styles](../concepts/concepts.md#plugin-styles) for informations on its concept.**
To style a plugin component:
- Add a `styles.scss` file in the component directory
- Require it from the `index.js` file (`import styles from './styles.scss';`)
- Add some styles in the `styles.scss` file
```
.wrapper {
display: block;
background: red;
height: 100px;
width: 100px;
}
```
Use this style in the component: `<div className={styles.wrapper}></div>`.
::: note
if you want to use several classes:
:::
```js
import cn from 'classnames';
import styles from './styles.scss';
// ...
return (
<div className={cn(styles.wrapper, styles.otherClass)}>{this.props.children}</div>
);
// ...
```
## i18n
### i18n
[React Intl](https://github.com/yahoo/react-intl) provides React components and an API to format dates, numbers, and strings, including pluralization and handling translations.
@ -108,6 +278,7 @@ The example below shows how to use i18n inside your plugin.
**Define all your ids with the associated message:**
**Path —** `./plugins/my-plugin/admin/src/translations/en.json`.
```json
{
"notification.error.message": "An error occurred"
@ -115,6 +286,7 @@ The example below shows how to use i18n inside your plugin.
```
**Path —** `./plugins/my-plugin/admin/src/translations/fr.json`
```json
{
"notification.error.message": "Une erreur est survenue"
@ -124,25 +296,19 @@ The example below shows how to use i18n inside your plugin.
**Usage inside a component**
**Path —** `./plugins/my-plugin/admin/src/components/Foo/index.js`.
```js
import { FormattedMessage } from 'react-intl';
import SomeOtherComponent from 'components/SomeOtherComponent';
const Foo = (props) => (
const Foo = props => (
<div className={styles.foo}>
<FormattedMessage id="my-plugin.notification.error.message" />
<SomeOtherComponent {...props} />
</div>
)
);
export default Foo;
```
See [the documentation](https://github.com/yahoo/react-intl/wiki/Components#formattedmessage) for more extensive usage.
## Generators
You can use generators to create React components or containers for your plugin.
1. In your terminal go to your plugin folder `cd plugins/my-plugin`
2. Run `npm run generate` and choose the type of component your want to create -->