mirror of
https://github.com/strapi/strapi.git
synced 2025-08-22 07:38:41 +00:00
Load plugin documentation and associated translations
Signed-off-by: soupette <cyril.lpz@gmail.com>
This commit is contained in:
parent
5dbb3776cc
commit
37e70cdcdf
@ -5,7 +5,7 @@ import { QueryClientProvider, QueryClient } from 'react-query';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
import { StrapiProvider } from '@strapi/helper-plugin';
|
||||
import configureStore from './core/store/configureStore';
|
||||
import { Middlewares, Plugin } from './core/apis';
|
||||
import { Plugin } from './core/apis';
|
||||
import basename from './utils/basename';
|
||||
import App from './pages/App';
|
||||
import LanguageProvider from './components/LanguageProvider';
|
||||
@ -19,7 +19,7 @@ import themes from './themes';
|
||||
import reducers from './reducers';
|
||||
|
||||
// TODO
|
||||
import translationMessages from './translations';
|
||||
import translations from './translations';
|
||||
|
||||
window.strapi = {
|
||||
backendURL: process.env.STRAPI_ADMIN_BACKEND_URL,
|
||||
@ -33,14 +33,22 @@ const queryClient = new QueryClient({
|
||||
},
|
||||
});
|
||||
|
||||
// FIXME
|
||||
const appLocales = Object.keys(translations);
|
||||
|
||||
class StrapiApp {
|
||||
constructor({ appPlugins }) {
|
||||
this.translationMessages = translations;
|
||||
this.appPlugins = appPlugins || {};
|
||||
this.middlewares = Middlewares();
|
||||
this.middlewares = [];
|
||||
this.plugins = {};
|
||||
this.reducers = { ...reducers };
|
||||
}
|
||||
|
||||
addMiddleware(middleware) {
|
||||
this.middlewares.push(middleware);
|
||||
}
|
||||
|
||||
addReducers(reducers) {
|
||||
Object.keys(reducers).forEach(reducerName => {
|
||||
this.reducers[reducerName] = reducers[reducerName];
|
||||
@ -51,22 +59,60 @@ class StrapiApp {
|
||||
Object.keys(this.appPlugins).forEach(plugin => {
|
||||
this.appPlugins[plugin].register(this);
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
async boot() {
|
||||
console.log('booting');
|
||||
Object.keys(this.appPlugins).forEach(plugin => {
|
||||
const boot = this.appPlugins[plugin].boot;
|
||||
|
||||
return this;
|
||||
if (boot) {
|
||||
boot(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getPlugin(pluginId) {
|
||||
return this.plugins[pluginId] || null;
|
||||
}
|
||||
|
||||
// FIXME
|
||||
registerPluginTranslations(pluginId, trads) {
|
||||
const pluginTranslations = appLocales.reduce((acc, currentLanguage) => {
|
||||
const currentLocale = trads[currentLanguage];
|
||||
|
||||
if (currentLocale) {
|
||||
const localeprefixedWithPluginId = Object.keys(currentLocale).reduce((acc2, current) => {
|
||||
acc2[`${pluginId}.${current}`] = currentLocale[current];
|
||||
|
||||
return acc2;
|
||||
}, {});
|
||||
|
||||
acc[currentLanguage] = localeprefixedWithPluginId;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
this.translationMessages = Object.keys(this.translationMessages).reduce((acc, current) => {
|
||||
acc[current] = {
|
||||
...this.translationMessages[current],
|
||||
...(pluginTranslations[current] || {}),
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
registerPlugin(pluginConf) {
|
||||
this.plugins[pluginConf.id] = Plugin(pluginConf);
|
||||
const plugin = Plugin(pluginConf);
|
||||
|
||||
this.plugins[plugin.pluginId] = plugin;
|
||||
|
||||
// FIXME
|
||||
// Translations should be loaded differently
|
||||
// This is a temporary fix
|
||||
|
||||
this.registerPluginTranslations(plugin.pluginId, plugin.trads);
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -79,7 +125,7 @@ class StrapiApp {
|
||||
<Fonts />
|
||||
<Provider store={store}>
|
||||
<StrapiProvider strapi={this}>
|
||||
<LanguageProvider messages={translationMessages}>
|
||||
<LanguageProvider messages={this.translationMessages}>
|
||||
<>
|
||||
<AutoReloadOverlayBlocker />
|
||||
<OverlayBlocker />
|
||||
|
@ -0,0 +1,41 @@
|
||||
import React, { useReducer, useRef } from 'react';
|
||||
import { LoadingIndicatorPage, useStrapi } from '@strapi/helper-plugin';
|
||||
import Admin from '../../pages/Admin';
|
||||
import init from './init';
|
||||
import reducer, { initialState } from './reducer';
|
||||
|
||||
const PluginsInitializer = () => {
|
||||
// TODO rename strapi to avoid mismatch with the window.strapi
|
||||
const { strapi: app } = useStrapi();
|
||||
const [{ plugins }, dispatch] = useReducer(reducer, initialState, () => init(app.plugins));
|
||||
const setPlugin = useRef(pluginId => {
|
||||
dispatch({ type: 'SET_PLUGIN_READY', pluginId });
|
||||
});
|
||||
|
||||
const hasApluginNotReady = Object.keys(plugins).some(plugin => plugins[plugin].isReady === false);
|
||||
|
||||
if (hasApluginNotReady) {
|
||||
const initializers = Object.keys(plugins).reduce((acc, current) => {
|
||||
const InitializerComponent = plugins[current].initializer;
|
||||
|
||||
if (InitializerComponent) {
|
||||
const key = plugins[current].pluginId;
|
||||
|
||||
acc.push(<InitializerComponent key={key} setPlugin={setPlugin.current} />);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{initializers}
|
||||
<LoadingIndicatorPage />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return <Admin plugins={plugins} />;
|
||||
};
|
||||
|
||||
export default PluginsInitializer;
|
@ -0,0 +1,11 @@
|
||||
const init = plugins => {
|
||||
return {
|
||||
plugins: Object.keys(plugins).reduce((acc, current) => {
|
||||
acc[current] = { ...plugins[current] };
|
||||
|
||||
return acc;
|
||||
}, {}),
|
||||
};
|
||||
};
|
||||
|
||||
export default init;
|
@ -0,0 +1,22 @@
|
||||
import produce from 'immer';
|
||||
import set from 'lodash/set';
|
||||
|
||||
const initialState = {
|
||||
plugins: null,
|
||||
};
|
||||
|
||||
const reducer = (state = initialState, action) =>
|
||||
/* eslint-disable-next-line consistent-return */
|
||||
produce(state, draftState => {
|
||||
switch (action.type) {
|
||||
case 'SET_PLUGIN_READY': {
|
||||
set(draftState, ['plugins', action.pluginId, 'isReady'], true);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return draftState;
|
||||
}
|
||||
});
|
||||
|
||||
export { initialState };
|
||||
export default reducer;
|
@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { StrapiProvider } from '@strapi/helper-plugin';
|
||||
import { render } from '@testing-library/react';
|
||||
import PluginsInitializer from '../index';
|
||||
|
||||
jest.mock('../../../pages/Admin', () => () => <div>ADMIN</div>);
|
||||
|
||||
describe('ADMIN | COMPONENTS | PluginsInitializer', () => {
|
||||
it('should not crash', () => {
|
||||
expect(
|
||||
render(
|
||||
<StrapiProvider strapi={{ plugins: {} }}>
|
||||
<PluginsInitializer />
|
||||
</StrapiProvider>
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
@ -0,0 +1,16 @@
|
||||
import init from '../init';
|
||||
|
||||
describe('ADMIN | COMPONENT | PluginsInitializer | init', () => {
|
||||
it('should return the initialState', () => {
|
||||
const plugins = {
|
||||
pluginA: {
|
||||
isReady: false,
|
||||
},
|
||||
pluginB: {
|
||||
isReady: false,
|
||||
},
|
||||
};
|
||||
|
||||
expect(init(plugins)).toEqual({ plugins });
|
||||
});
|
||||
});
|
@ -0,0 +1,40 @@
|
||||
import reducer, { initialState } from '../reducer';
|
||||
|
||||
describe('ADMIN | COMPONENTS | PluginsInitializer | reducer', () => {
|
||||
let state;
|
||||
|
||||
beforeEach(() => {
|
||||
state = initialState;
|
||||
});
|
||||
|
||||
describe('DEFAULT_ACTION', () => {
|
||||
it('should return the initialState', () => {
|
||||
expect(reducer(state, {})).toEqual(initialState);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SET_PLUGIN_READY', () => {
|
||||
it('should set the isReady property to true for a plugin', () => {
|
||||
state = {
|
||||
plugins: {
|
||||
pluginA: {
|
||||
isReady: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const expected = {
|
||||
plugins: {
|
||||
pluginA: { isReady: true },
|
||||
},
|
||||
};
|
||||
|
||||
const action = {
|
||||
type: 'SET_PLUGIN_READY',
|
||||
pluginId: 'pluginA',
|
||||
};
|
||||
|
||||
expect(reducer(state, action)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,15 +0,0 @@
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
|
||||
class Middlewares {
|
||||
middlewares = [];
|
||||
|
||||
add(middleware) {
|
||||
this.middlewares.push(middleware);
|
||||
}
|
||||
|
||||
get middlewares() {
|
||||
return cloneDeep(this.middlewares);
|
||||
}
|
||||
}
|
||||
|
||||
export default () => new Middlewares();
|
@ -5,8 +5,9 @@ class Plugin {
|
||||
this.description = pluginConf.description;
|
||||
// TODO
|
||||
this.icon = pluginConf.icon;
|
||||
this.initializer = pluginConf.initializer || null;
|
||||
this.injectionZones = pluginConf.injectionZones || {};
|
||||
this.isReady = pluginConf.isReady || true;
|
||||
this.isReady = pluginConf.isReady !== undefined ? pluginConf.isReady : true;
|
||||
// TODO
|
||||
this.isRequired = pluginConf.isRequired;
|
||||
// TODO
|
||||
|
@ -1,2 +1,2 @@
|
||||
export { default as Middlewares } from './Middlewares';
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export { default as Plugin } from './Plugin';
|
||||
|
@ -2,9 +2,12 @@ import { createStore, applyMiddleware } from 'redux';
|
||||
import createReducer from './createReducer';
|
||||
|
||||
const configureStore = app => {
|
||||
// TODO
|
||||
const middlewares = [];
|
||||
|
||||
middlewares.forEach(middleware => {
|
||||
middlewares.push(middleware());
|
||||
});
|
||||
|
||||
return createStore(createReducer(app.reducers), {}, applyMiddleware(...middlewares));
|
||||
};
|
||||
|
||||
|
@ -2,8 +2,6 @@ import ReactDOM from 'react-dom';
|
||||
import StrapiApp from './StrapiApp';
|
||||
import plugins from './plugins';
|
||||
|
||||
console.log({ plugins });
|
||||
|
||||
const app = StrapiApp({ appPlugins: plugins });
|
||||
|
||||
const MOUNT_NODE = document.getElementById('app');
|
||||
|
@ -17,7 +17,6 @@ import { isEmpty } from 'lodash';
|
||||
import {
|
||||
difference,
|
||||
GlobalContextProvider,
|
||||
LoadingIndicatorPage,
|
||||
CheckPagePermissions,
|
||||
request,
|
||||
} from '@strapi/helper-plugin';
|
||||
@ -149,45 +148,11 @@ export class Admin extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
hasApluginNotReady = props => {
|
||||
const {
|
||||
global: { plugins },
|
||||
} = props;
|
||||
|
||||
return !Object.keys(plugins).every(plugin => plugins[plugin].isReady === true);
|
||||
};
|
||||
|
||||
initApp = async () => {
|
||||
await this.fetchAppInfo();
|
||||
await this.fetchStrapiLatestRelease();
|
||||
};
|
||||
|
||||
/**
|
||||
* Display the app loader until the app is ready
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
showLoader = () => {
|
||||
return this.hasApluginNotReady(this.props);
|
||||
};
|
||||
|
||||
renderInitializers = () => {
|
||||
const {
|
||||
global: { plugins },
|
||||
} = this.props;
|
||||
|
||||
return Object.keys(plugins).reduce((acc, current) => {
|
||||
const InitializerComponent = plugins[current].initializer;
|
||||
|
||||
if (InitializerComponent) {
|
||||
const key = plugins[current].id;
|
||||
|
||||
acc.push(<InitializerComponent key={key} {...this.props} {...this.helpers} />);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
};
|
||||
|
||||
renderPluginDispatcher = props => {
|
||||
// NOTE: Send the needed props instead of everything...
|
||||
|
||||
@ -203,22 +168,14 @@ export class Admin extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
admin: { shouldUpdateStrapi },
|
||||
global: { autoReload, currentEnvironment, plugins, strapiVersion },
|
||||
global: { autoReload, currentEnvironment, strapiVersion },
|
||||
// FIXME
|
||||
intl: { formatMessage, locale },
|
||||
// FIXME
|
||||
plugins,
|
||||
updatePlugin,
|
||||
} = this.props;
|
||||
|
||||
// We need the admin data in order to make the initializers work
|
||||
if (this.showLoader()) {
|
||||
return (
|
||||
<>
|
||||
{this.renderInitializers()}
|
||||
<LoadingIndicatorPage />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PermissionsManager>
|
||||
<GlobalContextProvider
|
||||
@ -297,7 +254,6 @@ Admin.propTypes = {
|
||||
global: PropTypes.shape({
|
||||
autoReload: PropTypes.bool,
|
||||
currentEnvironment: PropTypes.string,
|
||||
plugins: PropTypes.object,
|
||||
strapiVersion: PropTypes.string,
|
||||
uuid: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
|
||||
}).isRequired,
|
||||
@ -305,7 +261,7 @@ Admin.propTypes = {
|
||||
formatMessage: PropTypes.func,
|
||||
locale: PropTypes.string,
|
||||
}),
|
||||
location: PropTypes.object.isRequired,
|
||||
plugins: PropTypes.object.isRequired,
|
||||
setAppError: PropTypes.func.isRequired,
|
||||
updatePlugin: PropTypes.func.isRequired,
|
||||
};
|
||||
|
@ -24,12 +24,12 @@ describe('<Admin />', () => {
|
||||
getUserPermissions: jest.fn(),
|
||||
getUserPermissionsError: jest.fn(),
|
||||
getUserPermissionsSucceeded: jest.fn(),
|
||||
plugins: {},
|
||||
global: {
|
||||
autoReload: false,
|
||||
currentEnvironment: 'development',
|
||||
hasAdminUser: false,
|
||||
isLoading: true,
|
||||
plugins: {},
|
||||
strapiVersion: '3',
|
||||
uuid: false,
|
||||
},
|
||||
|
@ -10,8 +10,8 @@ import { Switch, Route } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators, compose } from 'redux';
|
||||
import { LoadingIndicatorPage, auth, request } from '@strapi/helper-plugin';
|
||||
import PluginsInitializer from '../../components/PluginsInitializer';
|
||||
import PrivateRoute from '../../components/PrivateRoute';
|
||||
import Admin from '../Admin';
|
||||
import AuthPage from '../AuthPage';
|
||||
import NotFoundPage from '../NotFoundPage';
|
||||
import { getUID } from './utils';
|
||||
@ -117,7 +117,7 @@ function App(props) {
|
||||
)}
|
||||
exact
|
||||
/>
|
||||
<PrivateRoute path="/" component={Admin} />
|
||||
<PrivateRoute path="/" component={PluginsInitializer} />
|
||||
<Route path="" component={NotFoundPage} />
|
||||
</Switch>
|
||||
</Content>
|
||||
|
@ -14,7 +14,7 @@ import PageTitle from '../../components/PageTitle';
|
||||
|
||||
export function PluginDispatcher(props) {
|
||||
const {
|
||||
global: { plugins },
|
||||
plugins,
|
||||
match: {
|
||||
params: { pluginId },
|
||||
},
|
||||
@ -53,12 +53,12 @@ export function PluginDispatcher(props) {
|
||||
PluginDispatcher.defaultProps = {};
|
||||
|
||||
PluginDispatcher.propTypes = {
|
||||
global: PropTypes.object.isRequired,
|
||||
match: PropTypes.shape({
|
||||
params: PropTypes.shape({
|
||||
pluginId: PropTypes.string,
|
||||
}),
|
||||
}).isRequired,
|
||||
plugins: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default memo(PluginDispatcher);
|
||||
|
@ -9,7 +9,7 @@ const Email = () => <div>Email Plugin</div>;
|
||||
describe('<PluginDispatcher />', () => {
|
||||
it('Should return null if the params does not match the pluginId', () => {
|
||||
const props = {
|
||||
global: { plugins: {} },
|
||||
plugins: {},
|
||||
match: { params: { pluginId: 'email' } },
|
||||
};
|
||||
|
||||
@ -20,13 +20,11 @@ describe('<PluginDispatcher />', () => {
|
||||
|
||||
it('Should return the BlockerComponent if the plugin preventRendering prop is true', () => {
|
||||
const props = {
|
||||
global: {
|
||||
plugins: {
|
||||
email: {
|
||||
mainComponent: Email,
|
||||
preventComponentRendering: true,
|
||||
blockerComponent: null,
|
||||
},
|
||||
plugins: {
|
||||
email: {
|
||||
mainComponent: Email,
|
||||
preventComponentRendering: true,
|
||||
blockerComponent: null,
|
||||
},
|
||||
},
|
||||
match: { params: { pluginId: 'email' } },
|
||||
@ -39,13 +37,11 @@ describe('<PluginDispatcher />', () => {
|
||||
|
||||
it('Should return a custom BlockerComponent if the plugin preventRendering prop is true and a custom blocker is given', () => {
|
||||
const props = {
|
||||
global: {
|
||||
plugins: {
|
||||
email: {
|
||||
mainComponent: Email,
|
||||
preventComponentRendering: true,
|
||||
blockerComponent: BlockerComponent2,
|
||||
},
|
||||
plugins: {
|
||||
email: {
|
||||
mainComponent: Email,
|
||||
preventComponentRendering: true,
|
||||
blockerComponent: BlockerComponent2,
|
||||
},
|
||||
},
|
||||
match: { params: { pluginId: 'email' } },
|
||||
@ -58,11 +54,9 @@ describe('<PluginDispatcher />', () => {
|
||||
|
||||
it("Should return the plugin's mainComponent if all conditions are met", () => {
|
||||
const props = {
|
||||
global: {
|
||||
plugins: {
|
||||
email: {
|
||||
mainComponent: Email,
|
||||
},
|
||||
plugins: {
|
||||
email: {
|
||||
mainComponent: Email,
|
||||
},
|
||||
},
|
||||
match: { params: { pluginId: 'email' } },
|
||||
|
@ -1,15 +1,15 @@
|
||||
import globalReducer from './pages/App/reducer';
|
||||
import adminReducer from './pages/Admin/reducer';
|
||||
import languageProviderReducer from './components/LanguageProvider/reducer';
|
||||
import permissionsManagerReducer from './components/PermissionsManager/reducer';
|
||||
import menuReducer from './components/LeftMenu/reducer';
|
||||
import permissionsManagerReducer from './components/PermissionsManager/reducer';
|
||||
|
||||
const reducers = {
|
||||
app: globalReducer,
|
||||
admin: adminReducer,
|
||||
app: globalReducer,
|
||||
language: languageProviderReducer,
|
||||
permissionsManager: permissionsManagerReducer,
|
||||
menu: menuReducer,
|
||||
permissionsManager: permissionsManagerReducer,
|
||||
};
|
||||
|
||||
export default reducers;
|
||||
|
@ -4,7 +4,6 @@
|
||||
// Here's the file: strapi/docs/3.0.0-beta.x/guides/registering-a-field-in-admin.md
|
||||
// Also the strapi-generate-plugins/files/admin/src/index.js needs to be updated
|
||||
// IF THE DOC IS NOT UPDATED THE PULL REQUEST WILL NOT BE MERGED
|
||||
|
||||
import pluginPkg from '../../package.json';
|
||||
import pluginPermissions from './permissions';
|
||||
import pluginId from './pluginId';
|
||||
@ -12,43 +11,6 @@ import pluginLogo from './assets/images/logo.svg';
|
||||
import App from './containers/App';
|
||||
import trads from './translations';
|
||||
|
||||
// export default strapi => {
|
||||
// const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
|
||||
// const icon = pluginPkg.strapi.icon;
|
||||
// const name = pluginPkg.strapi.name;
|
||||
// const plugin = {
|
||||
// blockerComponent: null,
|
||||
// blockerComponentProps: {},
|
||||
// description: pluginDescription,
|
||||
// icon,
|
||||
// id: pluginId,
|
||||
// injectedComponents: [],
|
||||
// isReady: true,
|
||||
// isRequired: pluginPkg.strapi.required || false,
|
||||
// mainComponent: App,
|
||||
// name,
|
||||
// pluginLogo,
|
||||
// preventComponentRendering: false,
|
||||
// trads,
|
||||
// menu: {
|
||||
// pluginsSectionLinks: [
|
||||
// {
|
||||
// destination: `/plugins/${pluginId}`,
|
||||
// icon,
|
||||
// label: {
|
||||
// id: `${pluginId}.plugin.name`,
|
||||
// defaultMessage: 'Documentation',
|
||||
// },
|
||||
// name,
|
||||
// permissions: pluginPermissions.main,
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// };
|
||||
|
||||
// return strapi.registerPlugin(plugin);
|
||||
// };
|
||||
|
||||
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
|
||||
const icon = pluginPkg.strapi.icon;
|
||||
const name = pluginPkg.strapi.name;
|
||||
|
Loading…
x
Reference in New Issue
Block a user