mirror of
https://github.com/strapi/strapi.git
synced 2025-07-25 18:05:07 +00:00
Add front documentation
This commit is contained in:
parent
0632b4e28b
commit
88e0266f12
@ -4,19 +4,229 @@
|
|||||||
This feature is currenlty not available (Coming soon).
|
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.
|
## Admin panel
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
Strapi's admin panel and plugins system aim to be an easy and powerful way to create new features.
|
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.
|
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.
|
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 :**
|
**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 :
|
The declaration would be as followed :
|
||||||
|
|
||||||
**Path —** `plugins/my-plugin/admin/src/containers/App/index.js`.
|
**Path —** `plugins/my-plugin/admin/src/containers/App/index.js`.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import React from 'react';
|
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 (
|
return (
|
||||||
<div className={styles.myPlugin}>
|
<div className={styles.myPlugin}>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path="/plugins/my-plugin/user/:id" component={UserPage} />
|
<Route
|
||||||
|
exact
|
||||||
|
path={`/plugins/${pluginId}/user/:id`}
|
||||||
|
component={UserPage}
|
||||||
|
/>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</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
|
### i18n
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
[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.
|
[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:**
|
**Define all your ids with the associated message:**
|
||||||
|
|
||||||
**Path —** `./plugins/my-plugin/admin/src/translations/en.json`.
|
**Path —** `./plugins/my-plugin/admin/src/translations/en.json`.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"notification.error.message": "An error occurred"
|
"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`
|
**Path —** `./plugins/my-plugin/admin/src/translations/fr.json`
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"notification.error.message": "Une erreur est survenue"
|
"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**
|
**Usage inside a component**
|
||||||
|
|
||||||
**Path —** `./plugins/my-plugin/admin/src/components/Foo/index.js`.
|
**Path —** `./plugins/my-plugin/admin/src/components/Foo/index.js`.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import SomeOtherComponent from 'components/SomeOtherComponent';
|
import SomeOtherComponent from 'components/SomeOtherComponent';
|
||||||
|
|
||||||
const Foo = (props) => (
|
const Foo = props => (
|
||||||
<div className={styles.foo}>
|
<div className={styles.foo}>
|
||||||
<FormattedMessage id="my-plugin.notification.error.message" />
|
<FormattedMessage id="my-plugin.notification.error.message" />
|
||||||
<SomeOtherComponent {...props} />
|
<SomeOtherComponent {...props} />
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
|
|
||||||
export default Foo;
|
export default Foo;
|
||||||
```
|
```
|
||||||
|
|
||||||
See [the documentation](https://github.com/yahoo/react-intl/wiki/Components#formattedmessage) for more extensive usage.
|
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 -->
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user