mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 10:55:37 +00:00
Merge pull request #10655 from strapi/core/admin-customisations
[Core] Enable admin panel customisations
This commit is contained in:
commit
c2ae41acb0
@ -36,8 +36,6 @@ module.exports = {
|
|||||||
BACKEND_URL: true,
|
BACKEND_URL: true,
|
||||||
PUBLIC_PATH: true,
|
PUBLIC_PATH: true,
|
||||||
NODE_ENV: true,
|
NODE_ENV: true,
|
||||||
STRAPI_ADMIN_SHOW_TUTORIALS: true,
|
|
||||||
STRAPI_ADMIN_UPDATE_NOTIFICATION: true,
|
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
react: {
|
react: {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
const frontPaths = [
|
const frontPaths = [
|
||||||
'packages/**/admin/src/**/*.js',
|
'packages/**/admin/src/**/*.js',
|
||||||
|
'packages/generators/app/lib/resources/files/admin/app.js',
|
||||||
'packages/**/ee/admin/**/*.js',
|
'packages/**/ee/admin/**/*.js',
|
||||||
'packages/core/helper-plugin/**/*.js',
|
'packages/core/helper-plugin/**/*.js',
|
||||||
'packages/**/tests/front/**/*.js',
|
'packages/**/tests/front/**/*.js',
|
||||||
|
|||||||
@ -1,15 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
webpack: (config, webpack) => {
|
|
||||||
// Note: we provide webpack above so you should not `require` it
|
|
||||||
// Perform customizations to webpack config
|
|
||||||
// Important: return the modified config
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
app: config => {
|
|
||||||
config.locales = ['fr'];
|
|
||||||
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
33
examples/getstarted/admin/app.js
Normal file
33
examples/getstarted/admin/app.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// import MyCompo from './extensions/MyCompo';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
config: {
|
||||||
|
// Leaving this commented on purpose
|
||||||
|
// auth: {
|
||||||
|
// logo:
|
||||||
|
// 'https://images.unsplash.com/photo-1593642634367-d91a135587b5?ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=750&q=80',
|
||||||
|
// },
|
||||||
|
// head: {
|
||||||
|
// favicon:
|
||||||
|
// 'https://images.unsplash.com/photo-1593642634367-d91a135587b5?ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=750&q=80',
|
||||||
|
// title: 'Strapi',
|
||||||
|
// },
|
||||||
|
// locales: ['fr', 'toto'],
|
||||||
|
// menu: {
|
||||||
|
// logo: null,
|
||||||
|
// },
|
||||||
|
// theme: {
|
||||||
|
// main: {
|
||||||
|
// colors: { ok: 't' },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// translations: {
|
||||||
|
// fr: {
|
||||||
|
// 'Auth.form.email.label': 'test',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// tutorials: false,
|
||||||
|
// notifications: { release: false },
|
||||||
|
},
|
||||||
|
bootstrap() {},
|
||||||
|
};
|
||||||
@ -45,8 +45,7 @@ module.exports = {
|
|||||||
BACKEND_URL: 'http://localhost:1337',
|
BACKEND_URL: 'http://localhost:1337',
|
||||||
ADMIN_PATH: '/admin',
|
ADMIN_PATH: '/admin',
|
||||||
NODE_ENV: 'test',
|
NODE_ENV: 'test',
|
||||||
'process.env.STRAPI_ADMIN_SHOW_TUTORIALS': 'false',
|
|
||||||
'process.env.STRAPI_ADMIN_UPDATE_NOTIFICATION': 'false',
|
|
||||||
// FIXME create a clean config file
|
// FIXME create a clean config file
|
||||||
},
|
},
|
||||||
moduleDirectories: [
|
moduleDirectories: [
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
STRAPI_ADMIN_SHOW_TUTORIALS=false
|
|
||||||
STRAPI_ADMIN_UPDATE_NOTIFICATION=false
|
|
||||||
@ -1,11 +1,16 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
import merge from 'lodash/merge';
|
||||||
import pick from 'lodash/pick';
|
import pick from 'lodash/pick';
|
||||||
|
import isFunction from 'lodash/isFunction';
|
||||||
import invariant from 'invariant';
|
import invariant from 'invariant';
|
||||||
|
import { Helmet } from 'react-helmet';
|
||||||
import { basename, createHook } from './core/utils';
|
import { basename, createHook } from './core/utils';
|
||||||
import configureStore from './core/store/configureStore';
|
import configureStore from './core/store/configureStore';
|
||||||
import { Plugin } from './core/apis';
|
import { Plugin } from './core/apis';
|
||||||
import App from './pages/App';
|
import App from './pages/App';
|
||||||
|
import AuthLogo from './assets/images/logo_strapi_auth.png';
|
||||||
|
import MenuLogo from './assets/images/logo_strapi_menu.png';
|
||||||
import Providers from './components/Providers';
|
import Providers from './components/Providers';
|
||||||
import Theme from './components/Theme';
|
import Theme from './components/Theme';
|
||||||
import languageNativeNames from './translations/languageNativeNames';
|
import languageNativeNames from './translations/languageNativeNames';
|
||||||
@ -16,11 +21,23 @@ import {
|
|||||||
MUTATE_SINGLE_TYPES_LINKS,
|
MUTATE_SINGLE_TYPES_LINKS,
|
||||||
} from './exposedHooks';
|
} from './exposedHooks';
|
||||||
import injectionZones from './injectionZones';
|
import injectionZones from './injectionZones';
|
||||||
|
import favicon from './favicon.ico';
|
||||||
import themes from './themes';
|
import themes from './themes';
|
||||||
|
|
||||||
class StrapiApp {
|
class StrapiApp {
|
||||||
constructor({ appPlugins, library, locales, middlewares, reducers }) {
|
constructor({ adminConfig, appPlugins, library, middlewares, reducers }) {
|
||||||
this.appLocales = ['en', ...locales.filter(loc => loc !== 'en')];
|
this.customConfigurations = adminConfig.config;
|
||||||
|
this.customBootstrapConfiguration = adminConfig.bootstrap;
|
||||||
|
this.configurations = {
|
||||||
|
authLogo: AuthLogo,
|
||||||
|
head: { favicon },
|
||||||
|
locales: ['en'],
|
||||||
|
menuLogo: MenuLogo,
|
||||||
|
notifications: { releases: true },
|
||||||
|
theme: themes,
|
||||||
|
translations: {},
|
||||||
|
tutorials: true,
|
||||||
|
};
|
||||||
this.appPlugins = appPlugins || {};
|
this.appPlugins = appPlugins || {};
|
||||||
this.library = library;
|
this.library = library;
|
||||||
this.middlewares = middlewares;
|
this.middlewares = middlewares;
|
||||||
@ -155,19 +172,66 @@ class StrapiApp {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (isFunction(this.customBootstrapConfiguration)) {
|
||||||
|
this.customBootstrapConfiguration({
|
||||||
|
addComponents: this.addComponents,
|
||||||
|
addFields: this.addFields,
|
||||||
|
addMenuLink: this.addMenuLink,
|
||||||
|
addReducers: this.addReducers,
|
||||||
|
addSettingsLink: this.addSettingsLink,
|
||||||
|
addSettingsLinks: this.addSettingsLinks,
|
||||||
|
getPlugin: this.getPlugin,
|
||||||
|
injectContentManagerComponent: this.injectContentManagerComponent,
|
||||||
|
registerHook: this.registerHook,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrapAdmin = async () => {
|
bootstrapAdmin = async () => {
|
||||||
|
await this.createCustomConfigurations();
|
||||||
|
|
||||||
this.createHook(INJECT_COLUMN_IN_TABLE);
|
this.createHook(INJECT_COLUMN_IN_TABLE);
|
||||||
this.createHook(MUTATE_COLLECTION_TYPES_LINKS);
|
this.createHook(MUTATE_COLLECTION_TYPES_LINKS);
|
||||||
this.createHook(MUTATE_SINGLE_TYPES_LINKS);
|
this.createHook(MUTATE_SINGLE_TYPES_LINKS);
|
||||||
this.createHook(MUTATE_EDIT_VIEW_LAYOUT);
|
this.createHook(MUTATE_EDIT_VIEW_LAYOUT);
|
||||||
|
|
||||||
await this.loadAdminTrads();
|
|
||||||
|
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
createCustomConfigurations = async () => {
|
||||||
|
if (this.customConfigurations?.locales) {
|
||||||
|
this.configurations.locales = [
|
||||||
|
'en',
|
||||||
|
...this.customConfigurations.locales?.filter(loc => loc !== 'en'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.customConfigurations?.auth?.logo) {
|
||||||
|
this.configurations.authLogo = this.customConfigurations.auth.logo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.customConfigurations?.menu?.logo) {
|
||||||
|
this.configurations.menuLogo = this.customConfigurations.menu.logo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.customConfigurations?.head?.favicon) {
|
||||||
|
this.configurations.head.favicon = this.customConfigurations.head.favicon;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.customConfigurations?.theme) {
|
||||||
|
this.configurations.theme = merge(this.configurations.theme, this.customConfigurations.theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.customConfigurations?.notifications?.releases !== undefined) {
|
||||||
|
this.configurations.notifications.releases = this.customConfigurations.notifications.releases;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.customConfigurations?.tutorials !== undefined) {
|
||||||
|
this.configurations.tutorials = this.customConfigurations.tutorials;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
createHook = name => {
|
createHook = name => {
|
||||||
this.hooksDict[name] = createHook();
|
this.hooksDict[name] = createHook();
|
||||||
};
|
};
|
||||||
@ -235,34 +299,47 @@ class StrapiApp {
|
|||||||
this.admin.injectionZones.contentManager[containerName][blockName].push(component);
|
this.admin.injectionZones.contentManager[containerName][blockName].push(component);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the admin translations
|
||||||
|
* @returns {Object} The imported admin translations
|
||||||
|
*/
|
||||||
async loadAdminTrads() {
|
async loadAdminTrads() {
|
||||||
const arrayOfPromises = this.appLocales.map(locale => {
|
const arrayOfPromises = this.configurations.locales.map(locale => {
|
||||||
return import(/* webpackChunkName: "[request]" */ `./translations/${locale}.json`)
|
return import(/* webpackChunkName: "[request]" */ `./translations/${locale}.json`)
|
||||||
.then(({ default: data }) => {
|
.then(({ default: data }) => {
|
||||||
return { data, locale };
|
return { data, locale };
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
return { data: {}, locale };
|
return { data: null, locale };
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const adminLocales = await Promise.all(arrayOfPromises);
|
const adminLocales = await Promise.all(arrayOfPromises);
|
||||||
|
|
||||||
this.translations = adminLocales.reduce((acc, current) => {
|
const translations = adminLocales.reduce((acc, current) => {
|
||||||
acc[current.locale] = current.data;
|
if (current.data) {
|
||||||
|
acc[current.locale] = current.data;
|
||||||
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
return Promise.resolve();
|
return translations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the application's translations and merged the custom translations
|
||||||
|
* with the default ones.
|
||||||
|
*
|
||||||
|
*/
|
||||||
async loadTrads() {
|
async loadTrads() {
|
||||||
|
const adminTranslations = await this.loadAdminTrads();
|
||||||
|
|
||||||
const arrayOfPromises = Object.keys(this.appPlugins)
|
const arrayOfPromises = Object.keys(this.appPlugins)
|
||||||
.map(plugin => {
|
.map(plugin => {
|
||||||
const registerTrads = this.appPlugins[plugin].registerTrads;
|
const registerTrads = this.appPlugins[plugin].registerTrads;
|
||||||
|
|
||||||
if (registerTrads) {
|
if (registerTrads) {
|
||||||
return registerTrads({ locales: this.appLocales });
|
return registerTrads({ locales: this.configurations.locales });
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -284,15 +361,18 @@ class StrapiApp {
|
|||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
this.translations = Object.keys(this.translations).reduce((acc, current) => {
|
const translations = this.configurations.locales.reduce((acc, current) => {
|
||||||
acc[current] = {
|
acc[current] = {
|
||||||
...this.translations[current],
|
...adminTranslations[current],
|
||||||
...(mergedTrads[current] || {}),
|
...(mergedTrads[current] || {}),
|
||||||
|
...this.customConfigurations?.translations?.[current],
|
||||||
};
|
};
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
this.configurations.translations = translations;
|
||||||
|
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,7 +403,7 @@ class StrapiApp {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const store = this.createStore();
|
const store = this.createStore();
|
||||||
const localeNames = pick(languageNativeNames, this.appLocales);
|
const localeNames = pick(languageNativeNames, this.configurations.locales || []);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
components: { components },
|
components: { components },
|
||||||
@ -331,15 +411,17 @@ class StrapiApp {
|
|||||||
} = this.library;
|
} = this.library;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Theme theme={themes}>
|
<Theme theme={this.configurations.theme}>
|
||||||
<Providers
|
<Providers
|
||||||
|
authLogo={this.configurations.authLogo}
|
||||||
components={components}
|
components={components}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
localeNames={localeNames}
|
localeNames={localeNames}
|
||||||
getAdminInjectedComponents={this.getAdminInjectedComponents}
|
getAdminInjectedComponents={this.getAdminInjectedComponents}
|
||||||
getPlugin={this.getPlugin}
|
getPlugin={this.getPlugin}
|
||||||
messages={this.translations}
|
messages={this.configurations.translations}
|
||||||
menu={this.menu}
|
menu={this.menu}
|
||||||
|
menuLogo={this.configurations.menuLogo}
|
||||||
plugins={this.plugins}
|
plugins={this.plugins}
|
||||||
runHookParallel={this.runHookParallel}
|
runHookParallel={this.runHookParallel}
|
||||||
runHookWaterfall={(name, initialValue, async = false) => {
|
runHookWaterfall={(name, initialValue, async = false) => {
|
||||||
@ -347,16 +429,29 @@ class StrapiApp {
|
|||||||
}}
|
}}
|
||||||
runHookSeries={this.runHookSeries}
|
runHookSeries={this.runHookSeries}
|
||||||
settings={this.settings}
|
settings={this.settings}
|
||||||
|
showTutorials={this.configurations.tutorials}
|
||||||
|
showReleaseNotification={this.configurations.notifications.releases}
|
||||||
store={store}
|
store={store}
|
||||||
>
|
>
|
||||||
<BrowserRouter basename={basename}>
|
<>
|
||||||
<App store={store} />
|
<Helmet
|
||||||
</BrowserRouter>
|
link={[
|
||||||
|
{
|
||||||
|
rel: 'icon',
|
||||||
|
type: 'image/png',
|
||||||
|
href: this.configurations.head.favicon,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<BrowserRouter basename={basename}>
|
||||||
|
<App store={store} />
|
||||||
|
</BrowserRouter>
|
||||||
|
</>
|
||||||
</Providers>
|
</Providers>
|
||||||
</Theme>
|
</Theme>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ({ appPlugins, library, locales, middlewares, reducers }) =>
|
export default ({ adminConfig = {}, appPlugins, library, middlewares, reducers }) =>
|
||||||
new StrapiApp({ appPlugins, library, locales, middlewares, reducers });
|
new StrapiApp({ adminConfig, appPlugins, library, middlewares, reducers });
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
app: config => {
|
|
||||||
config.locales = ['fr'];
|
|
||||||
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
9
packages/core/admin/admin/src/app.js
Normal file
9
packages/core/admin/admin/src/app.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export default {
|
||||||
|
config: {},
|
||||||
|
bootstrap(app) {
|
||||||
|
app.injectContentManagerComponent('editView', 'informations', {
|
||||||
|
name: 'i18n-locale-filter-edit-view',
|
||||||
|
Component: () => 'test',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
@ -2,16 +2,16 @@ import React, { useMemo } from 'react';
|
|||||||
import { LoadingIndicatorPage, AppInfosContext } from '@strapi/helper-plugin';
|
import { LoadingIndicatorPage, AppInfosContext } from '@strapi/helper-plugin';
|
||||||
import { useQueries } from 'react-query';
|
import { useQueries } from 'react-query';
|
||||||
import packageJSON from '../../../../package.json';
|
import packageJSON from '../../../../package.json';
|
||||||
|
import { useConfigurations } from '../../hooks';
|
||||||
import PluginsInitializer from '../PluginsInitializer';
|
import PluginsInitializer from '../PluginsInitializer';
|
||||||
import RBACProvider from '../RBACProvider';
|
import RBACProvider from '../RBACProvider';
|
||||||
import { fetchAppInfo, fetchCurrentUserPermissions, fetchStrapiLatestRelease } from './utils/api';
|
import { fetchAppInfo, fetchCurrentUserPermissions, fetchStrapiLatestRelease } from './utils/api';
|
||||||
import checkLatestStrapiVersion from './utils/checkLatestStrapiVersion';
|
import checkLatestStrapiVersion from './utils/checkLatestStrapiVersion';
|
||||||
|
|
||||||
const { STRAPI_ADMIN_UPDATE_NOTIFICATION } = process.env;
|
|
||||||
const canFetchRelease = STRAPI_ADMIN_UPDATE_NOTIFICATION === 'true';
|
|
||||||
const strapiVersion = packageJSON.version;
|
const strapiVersion = packageJSON.version;
|
||||||
|
|
||||||
const AuthenticatedApp = () => {
|
const AuthenticatedApp = () => {
|
||||||
|
const { showReleaseNotification } = useConfigurations();
|
||||||
const [
|
const [
|
||||||
{ data: appInfos, status },
|
{ data: appInfos, status },
|
||||||
{ data: tag_name, isLoading },
|
{ data: tag_name, isLoading },
|
||||||
@ -21,7 +21,7 @@ const AuthenticatedApp = () => {
|
|||||||
{
|
{
|
||||||
queryKey: 'strapi-release',
|
queryKey: 'strapi-release',
|
||||||
queryFn: fetchStrapiLatestRelease,
|
queryFn: fetchStrapiLatestRelease,
|
||||||
enabled: canFetchRelease,
|
enabled: showReleaseNotification,
|
||||||
initialData: strapiVersion,
|
initialData: strapiVersion,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import { QueryClientProvider, QueryClient } from 'react-query';
|
import { QueryClientProvider, QueryClient } from 'react-query';
|
||||||
|
import { ConfigurationsContext } from '../../../contexts';
|
||||||
import { fetchAppInfo, fetchCurrentUserPermissions, fetchStrapiLatestRelease } from '../utils/api';
|
import { fetchAppInfo, fetchCurrentUserPermissions, fetchStrapiLatestRelease } from '../utils/api';
|
||||||
import packageJSON from '../../../../../package.json';
|
import packageJSON from '../../../../../package.json';
|
||||||
import AuthenticatedApp from '..';
|
import AuthenticatedApp from '..';
|
||||||
@ -27,7 +28,9 @@ const queryClient = new QueryClient({
|
|||||||
|
|
||||||
const app = (
|
const app = (
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<AuthenticatedApp />
|
<ConfigurationsContext.Provider value={{ showReleaseNotification: false }}>
|
||||||
|
<AuthenticatedApp />
|
||||||
|
</ConfigurationsContext.Provider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import Logo from '../../../../assets/images/logo-strapi.png';
|
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
background-color: #007eff;
|
background-color: #007eff;
|
||||||
padding-left: 2rem;
|
padding-left: 2rem;
|
||||||
@ -22,7 +20,7 @@ const Wrapper = styled.div`
|
|||||||
letter-spacing: 0.2rem;
|
letter-spacing: 0.2rem;
|
||||||
color: $white;
|
color: $white;
|
||||||
|
|
||||||
background-image: url(${Logo});
|
background-image: url(${props => props.logo});
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: left center;
|
background-position: left center;
|
||||||
background-size: auto 2.5rem;
|
background-size: auto 2.5rem;
|
||||||
|
|||||||
@ -1,14 +1,18 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import { useConfigurations } from '../../../../hooks';
|
||||||
import Wrapper from './Wrapper';
|
import Wrapper from './Wrapper';
|
||||||
|
|
||||||
const LeftMenuHeader = () => (
|
const LeftMenuHeader = () => {
|
||||||
<Wrapper>
|
const { menuLogo } = useConfigurations();
|
||||||
<Link to="/" className="leftMenuHeaderLink">
|
|
||||||
<span className="projectName" />
|
return (
|
||||||
</Link>
|
<Wrapper logo={menuLogo}>
|
||||||
</Wrapper>
|
<Link to="/" className="leftMenuHeaderLink">
|
||||||
);
|
<span className="projectName" />
|
||||||
|
</Link>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default LeftMenuHeader;
|
export default LeftMenuHeader;
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|||||||
import { faQuestion, faTimes } from '@fortawesome/free-solid-svg-icons';
|
import { faQuestion, faTimes } from '@fortawesome/free-solid-svg-icons';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import { useTracking } from '@strapi/helper-plugin';
|
import { useTracking } from '@strapi/helper-plugin';
|
||||||
|
|
||||||
|
import { useConfigurations } from '../../hooks';
|
||||||
import formatVideoArray from './utils/formatAndStoreVideoArray';
|
import formatVideoArray from './utils/formatAndStoreVideoArray';
|
||||||
import StaticLinks from './StaticLinks';
|
import StaticLinks from './StaticLinks';
|
||||||
import Video from './Video';
|
import Video from './Video';
|
||||||
@ -12,7 +14,9 @@ import Wrapper from './Wrapper';
|
|||||||
import reducer, { initialState } from './reducer';
|
import reducer, { initialState } from './reducer';
|
||||||
|
|
||||||
const Onboarding = () => {
|
const Onboarding = () => {
|
||||||
if (process.env.STRAPI_ADMIN_SHOW_TUTORIALS !== 'true') {
|
const { showTutorials } = useConfigurations();
|
||||||
|
|
||||||
|
if (!showTutorials) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,6 +25,7 @@ const Onboarding = () => {
|
|||||||
|
|
||||||
const OnboardingVideos = () => {
|
const OnboardingVideos = () => {
|
||||||
const { trackUsage } = useTracking();
|
const { trackUsage } = useTracking();
|
||||||
|
|
||||||
const [{ isLoading, isOpen, videos }, dispatch] = useReducer(reducer, initialState);
|
const [{ isLoading, isOpen, videos }, dispatch] = useReducer(reducer, initialState);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -2,10 +2,8 @@ import React, { memo } from 'react';
|
|||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import favicon from '../../favicon.ico';
|
|
||||||
|
|
||||||
const PageTitle = ({ title }) => {
|
const PageTitle = ({ title }) => {
|
||||||
return <Helmet title={title} link={[{ rel: 'icon', type: 'image/png', href: favicon }]} />;
|
return <Helmet title={title} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
PageTitle.propTypes = {
|
PageTitle.propTypes = {
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
import { QueryClientProvider, QueryClient } from 'react-query';
|
import { QueryClientProvider, QueryClient } from 'react-query';
|
||||||
import { LibraryProvider, StrapiAppProvider } from '@strapi/helper-plugin';
|
import { LibraryProvider, StrapiAppProvider } from '@strapi/helper-plugin';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { AdminContext } from '../../contexts';
|
import { AdminContext, ConfigurationsContext } from '../../contexts';
|
||||||
import LanguageProvider from '../LanguageProvider';
|
import LanguageProvider from '../LanguageProvider';
|
||||||
import AutoReloadOverlayBlockerProvider from '../AutoReloadOverlayBlockerProvider';
|
import AutoReloadOverlayBlockerProvider from '../AutoReloadOverlayBlockerProvider';
|
||||||
import Notifications from '../Notifications';
|
import Notifications from '../Notifications';
|
||||||
@ -18,6 +18,7 @@ const queryClient = new QueryClient({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const Providers = ({
|
const Providers = ({
|
||||||
|
authLogo,
|
||||||
children,
|
children,
|
||||||
components,
|
components,
|
||||||
fields,
|
fields,
|
||||||
@ -25,37 +26,45 @@ const Providers = ({
|
|||||||
getPlugin,
|
getPlugin,
|
||||||
localeNames,
|
localeNames,
|
||||||
menu,
|
menu,
|
||||||
|
menuLogo,
|
||||||
messages,
|
messages,
|
||||||
plugins,
|
plugins,
|
||||||
runHookParallel,
|
runHookParallel,
|
||||||
runHookSeries,
|
runHookSeries,
|
||||||
runHookWaterfall,
|
runHookWaterfall,
|
||||||
settings,
|
settings,
|
||||||
|
showReleaseNotification,
|
||||||
|
showTutorials,
|
||||||
|
|
||||||
store,
|
store,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<AdminContext.Provider value={{ getAdminInjectedComponents }}>
|
<AdminContext.Provider value={{ getAdminInjectedComponents }}>
|
||||||
<StrapiAppProvider
|
<ConfigurationsContext.Provider
|
||||||
getPlugin={getPlugin}
|
value={{ authLogo, menuLogo, showReleaseNotification, showTutorials }}
|
||||||
menu={menu}
|
|
||||||
plugins={plugins}
|
|
||||||
runHookParallel={runHookParallel}
|
|
||||||
runHookWaterfall={runHookWaterfall}
|
|
||||||
runHookSeries={runHookSeries}
|
|
||||||
settings={settings}
|
|
||||||
>
|
>
|
||||||
<LibraryProvider components={components} fields={fields}>
|
<StrapiAppProvider
|
||||||
<LanguageProvider messages={messages} localeNames={localeNames}>
|
getPlugin={getPlugin}
|
||||||
<AutoReloadOverlayBlockerProvider>
|
menu={menu}
|
||||||
<OverlayBlocker>
|
plugins={plugins}
|
||||||
<Notifications>{children}</Notifications>
|
runHookParallel={runHookParallel}
|
||||||
</OverlayBlocker>
|
runHookWaterfall={runHookWaterfall}
|
||||||
</AutoReloadOverlayBlockerProvider>
|
runHookSeries={runHookSeries}
|
||||||
</LanguageProvider>
|
settings={settings}
|
||||||
</LibraryProvider>
|
>
|
||||||
</StrapiAppProvider>
|
<LibraryProvider components={components} fields={fields}>
|
||||||
|
<LanguageProvider messages={messages} localeNames={localeNames}>
|
||||||
|
<AutoReloadOverlayBlockerProvider>
|
||||||
|
<OverlayBlocker>
|
||||||
|
<Notifications>{children}</Notifications>
|
||||||
|
</OverlayBlocker>
|
||||||
|
</AutoReloadOverlayBlockerProvider>
|
||||||
|
</LanguageProvider>
|
||||||
|
</LibraryProvider>
|
||||||
|
</StrapiAppProvider>
|
||||||
|
</ConfigurationsContext.Provider>
|
||||||
</AdminContext.Provider>
|
</AdminContext.Provider>
|
||||||
</Provider>
|
</Provider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
@ -63,6 +72,7 @@ const Providers = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
Providers.propTypes = {
|
Providers.propTypes = {
|
||||||
|
authLogo: PropTypes.oneOfType([PropTypes.string, PropTypes.any]).isRequired,
|
||||||
children: PropTypes.element.isRequired,
|
children: PropTypes.element.isRequired,
|
||||||
components: PropTypes.object.isRequired,
|
components: PropTypes.object.isRequired,
|
||||||
fields: PropTypes.object.isRequired,
|
fields: PropTypes.object.isRequired,
|
||||||
@ -81,12 +91,15 @@ Providers.propTypes = {
|
|||||||
Component: PropTypes.func,
|
Component: PropTypes.func,
|
||||||
})
|
})
|
||||||
).isRequired,
|
).isRequired,
|
||||||
|
menuLogo: PropTypes.oneOfType([PropTypes.string, PropTypes.any]).isRequired,
|
||||||
messages: PropTypes.object.isRequired,
|
messages: PropTypes.object.isRequired,
|
||||||
plugins: PropTypes.object.isRequired,
|
plugins: PropTypes.object.isRequired,
|
||||||
runHookParallel: PropTypes.func.isRequired,
|
runHookParallel: PropTypes.func.isRequired,
|
||||||
runHookWaterfall: PropTypes.func.isRequired,
|
runHookWaterfall: PropTypes.func.isRequired,
|
||||||
runHookSeries: PropTypes.func.isRequired,
|
runHookSeries: PropTypes.func.isRequired,
|
||||||
settings: PropTypes.object.isRequired,
|
settings: PropTypes.object.isRequired,
|
||||||
|
showReleaseNotification: PropTypes.bool.isRequired,
|
||||||
|
showTutorials: PropTypes.bool.isRequired,
|
||||||
store: PropTypes.object.isRequired,
|
store: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { permissions } from './data';
|
import testData, { permissions } from './data';
|
||||||
|
|
||||||
export { default as testData } from './data';
|
export { testData };
|
||||||
export { permissions };
|
export { permissions };
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
|
|
||||||
|
const ConfigurationsContext = createContext({});
|
||||||
|
|
||||||
|
export default ConfigurationsContext;
|
||||||
@ -1,2 +1,3 @@
|
|||||||
export { default as AdminContext } from './Admin';
|
export { default as AdminContext } from './Admin';
|
||||||
|
export { default as ConfigurationsContext } from './Configurations';
|
||||||
export { default as PermissionsDataManagerContext } from './PermisssionsDataManagerContext';
|
export { default as PermissionsDataManagerContext } from './PermisssionsDataManagerContext';
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
export { default as useConfigurations } from './useConfigurations';
|
||||||
export { default as useModels } from './useModels';
|
export { default as useModels } from './useModels';
|
||||||
export { default as useFetchPermissionsLayout } from './useFetchPermissionsLayout';
|
export { default as useFetchPermissionsLayout } from './useFetchPermissionsLayout';
|
||||||
export { default as useFetchPluginsFromMarketPlace } from './useFetchPluginsFromMarketPlace';
|
export { default as useFetchPluginsFromMarketPlace } from './useFetchPluginsFromMarketPlace';
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { ConfigurationsContext } from '../../contexts';
|
||||||
|
|
||||||
|
const useConfigurations = () => {
|
||||||
|
const context = useContext(ConfigurationsContext);
|
||||||
|
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useConfigurations;
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { Components, Fields, Middlewares, Reducers } from './core/apis';
|
import { Components, Fields, Middlewares, Reducers } from './core/apis';
|
||||||
import { axiosInstance } from './core/utils';
|
import { axiosInstance } from './core/utils';
|
||||||
import appCustomisations from './admin.config';
|
import appCustomisations from './app';
|
||||||
import plugins from './plugins';
|
import plugins from './plugins';
|
||||||
import appReducers from './reducers';
|
import appReducers from './reducers';
|
||||||
|
|
||||||
@ -14,11 +14,7 @@ window.strapi = {
|
|||||||
projectType: 'Community',
|
projectType: 'Community',
|
||||||
};
|
};
|
||||||
|
|
||||||
const appConfig = {
|
const customConfig = appCustomisations;
|
||||||
locales: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const customConfig = appCustomisations.app(appConfig);
|
|
||||||
|
|
||||||
const library = {
|
const library = {
|
||||||
components: Components(),
|
components: Components(),
|
||||||
@ -56,7 +52,8 @@ const run = async () => {
|
|||||||
const app = StrapiApp.default({
|
const app = StrapiApp.default({
|
||||||
appPlugins: plugins,
|
appPlugins: plugins,
|
||||||
library,
|
library,
|
||||||
locales: customConfig.locales,
|
adminConfig: customConfig,
|
||||||
|
bootstrap: customConfig,
|
||||||
middlewares,
|
middlewares,
|
||||||
reducers,
|
reducers,
|
||||||
});
|
});
|
||||||
@ -64,6 +61,7 @@ const run = async () => {
|
|||||||
await app.bootstrapAdmin();
|
await app.bootstrapAdmin();
|
||||||
await app.initialize();
|
await app.initialize();
|
||||||
await app.bootstrap();
|
await app.bootstrap();
|
||||||
|
|
||||||
await app.loadTrads();
|
await app.loadTrads();
|
||||||
|
|
||||||
ReactDOM.render(app.render(), MOUNT_NODE);
|
ReactDOM.render(app.render(), MOUNT_NODE);
|
||||||
|
|||||||
@ -20,6 +20,7 @@ jest.mock('../../../hooks', () => ({
|
|||||||
useMenu: jest.fn(() => ({ isLoading: true, generalSectionLinks: [], pluginsSectionLinks: [] })),
|
useMenu: jest.fn(() => ({ isLoading: true, generalSectionLinks: [], pluginsSectionLinks: [] })),
|
||||||
useTrackUsage: jest.fn(),
|
useTrackUsage: jest.fn(),
|
||||||
useReleaseNotification: jest.fn(),
|
useReleaseNotification: jest.fn(),
|
||||||
|
useConfigurations: jest.fn(() => ({ showTutorials: false })),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('../../../components/LeftMenu', () => () => <div>menu</div>);
|
jest.mock('../../../components/LeftMenu', () => () => <div>menu</div>);
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import LogoStrapi from '../../../../assets/images/logo_strapi.png';
|
import { useConfigurations } from '../../../../hooks';
|
||||||
import Img from './Img';
|
import Img from './Img';
|
||||||
|
|
||||||
const Logo = () => <Img src={LogoStrapi} alt="strapi-logo" />;
|
const Logo = () => {
|
||||||
|
const { authLogo } = useConfigurations();
|
||||||
|
|
||||||
|
return <Img src={authLogo} alt="strapi" />;
|
||||||
|
};
|
||||||
|
|
||||||
export default Logo;
|
export default Logo;
|
||||||
|
|||||||
@ -7,11 +7,10 @@ import appReducers from '../reducers';
|
|||||||
const library = { fields: Fields(), components: Components() };
|
const library = { fields: Fields(), components: Components() };
|
||||||
const middlewares = { middlewares: [] };
|
const middlewares = { middlewares: [] };
|
||||||
const reducers = { reducers: appReducers };
|
const reducers = { reducers: appReducers };
|
||||||
const locales = [];
|
|
||||||
|
|
||||||
describe('ADMIN | StrapiApp', () => {
|
describe('ADMIN | StrapiApp', () => {
|
||||||
it('should render the app without plugins', () => {
|
it('should render the app without plugins', () => {
|
||||||
const app = StrapiApp({ middlewares, reducers, library, locales });
|
const app = StrapiApp({ middlewares, reducers, library });
|
||||||
const { container } = render(app.render());
|
const { container } = render(app.render());
|
||||||
|
|
||||||
expect(container.firstChild).toMatchInlineSnapshot(`
|
expect(container.firstChild).toMatchInlineSnapshot(`
|
||||||
@ -45,7 +44,7 @@ describe('ADMIN | StrapiApp', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should create a valid store', () => {
|
it('should create a valid store', () => {
|
||||||
const app = StrapiApp({ middlewares, reducers, library, locales });
|
const app = StrapiApp({ middlewares, reducers, library });
|
||||||
|
|
||||||
const store = app.createStore();
|
const store = app.createStore();
|
||||||
|
|
||||||
@ -54,7 +53,7 @@ describe('ADMIN | StrapiApp', () => {
|
|||||||
|
|
||||||
describe('Hook api', () => {
|
describe('Hook api', () => {
|
||||||
it('runs the "moto" hooks in series', () => {
|
it('runs the "moto" hooks in series', () => {
|
||||||
const app = StrapiApp({ middlewares, reducers, library, locales });
|
const app = StrapiApp({ middlewares, reducers, library });
|
||||||
|
|
||||||
app.createHook('hello');
|
app.createHook('hello');
|
||||||
app.createHook('moto');
|
app.createHook('moto');
|
||||||
@ -72,7 +71,7 @@ describe('ADMIN | StrapiApp', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('runs the "moto" hooks in series asynchronously', async () => {
|
it('runs the "moto" hooks in series asynchronously', async () => {
|
||||||
const app = StrapiApp({ middlewares, reducers, library, locales });
|
const app = StrapiApp({ middlewares, reducers, library });
|
||||||
|
|
||||||
app.createHook('hello');
|
app.createHook('hello');
|
||||||
app.createHook('moto');
|
app.createHook('moto');
|
||||||
@ -90,7 +89,7 @@ describe('ADMIN | StrapiApp', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('runs the "moto" hooks in waterfall', () => {
|
it('runs the "moto" hooks in waterfall', () => {
|
||||||
const app = StrapiApp({ middlewares, reducers, library, locales });
|
const app = StrapiApp({ middlewares, reducers, library });
|
||||||
|
|
||||||
app.createHook('hello');
|
app.createHook('hello');
|
||||||
app.createHook('moto');
|
app.createHook('moto');
|
||||||
@ -106,7 +105,7 @@ describe('ADMIN | StrapiApp', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('runs the "moto" hooks in waterfall asynchronously', async () => {
|
it('runs the "moto" hooks in waterfall asynchronously', async () => {
|
||||||
const app = StrapiApp({ middlewares, reducers, library, locales });
|
const app = StrapiApp({ middlewares, reducers, library });
|
||||||
|
|
||||||
app.createHook('hello');
|
app.createHook('hello');
|
||||||
app.createHook('moto');
|
app.createHook('moto');
|
||||||
@ -122,7 +121,7 @@ describe('ADMIN | StrapiApp', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('runs the "moto" hooks in parallel', async () => {
|
it('runs the "moto" hooks in parallel', async () => {
|
||||||
const app = StrapiApp({ middlewares, reducers, library, locales });
|
const app = StrapiApp({ middlewares, reducers, library });
|
||||||
|
|
||||||
app.createHook('hello');
|
app.createHook('hello');
|
||||||
app.createHook('moto');
|
app.createHook('moto');
|
||||||
@ -142,14 +141,14 @@ describe('ADMIN | StrapiApp', () => {
|
|||||||
|
|
||||||
describe('Settings api', () => {
|
describe('Settings api', () => {
|
||||||
it('the settings should be defined', () => {
|
it('the settings should be defined', () => {
|
||||||
const app = StrapiApp({ middlewares, reducers, library, locales });
|
const app = StrapiApp({ middlewares, reducers, library });
|
||||||
|
|
||||||
expect(app.settings).toBeDefined();
|
expect(app.settings).toBeDefined();
|
||||||
expect(app.settings.global).toBeDefined();
|
expect(app.settings.global).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should creates a new section', () => {
|
it('should creates a new section', () => {
|
||||||
const app = StrapiApp({ middlewares, reducers, library, locales });
|
const app = StrapiApp({ middlewares, reducers, library });
|
||||||
const section = { id: 'foo', intlLabel: { id: 'foo', defaultMessage: 'foo' } };
|
const section = { id: 'foo', intlLabel: { id: 'foo', defaultMessage: 'foo' } };
|
||||||
const links = [
|
const links = [
|
||||||
{
|
{
|
||||||
@ -166,7 +165,7 @@ describe('ADMIN | StrapiApp', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should add a link correctly to the global section', () => {
|
it('should add a link correctly to the global section', () => {
|
||||||
const app = StrapiApp({ middlewares, reducers, library, locales });
|
const app = StrapiApp({ middlewares, reducers, library });
|
||||||
const link = {
|
const link = {
|
||||||
Component: jest.fn(),
|
Component: jest.fn(),
|
||||||
to: '/bar',
|
to: '/bar',
|
||||||
@ -181,7 +180,7 @@ describe('ADMIN | StrapiApp', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should add an array of links correctly to the global section', () => {
|
it('should add an array of links correctly to the global section', () => {
|
||||||
const app = StrapiApp({ middlewares, reducers, library, locales });
|
const app = StrapiApp({ middlewares, reducers, library });
|
||||||
const links = [
|
const links = [
|
||||||
{
|
{
|
||||||
Component: jest.fn(),
|
Component: jest.fn(),
|
||||||
@ -200,14 +199,14 @@ describe('ADMIN | StrapiApp', () => {
|
|||||||
|
|
||||||
describe('Menu api', () => {
|
describe('Menu api', () => {
|
||||||
it('the menu should be defined', () => {
|
it('the menu should be defined', () => {
|
||||||
const app = StrapiApp({ middlewares, reducers, library, locales });
|
const app = StrapiApp({ middlewares, reducers, library });
|
||||||
|
|
||||||
expect(app.menu).toBeDefined();
|
expect(app.menu).toBeDefined();
|
||||||
expect(Array.isArray(app.menu)).toBe(true);
|
expect(Array.isArray(app.menu)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('addMenuLink should add a link to the menu', () => {
|
it('addMenuLink should add a link to the menu', () => {
|
||||||
const app = StrapiApp({ middlewares, reducers, library, locales });
|
const app = StrapiApp({ middlewares, reducers, library });
|
||||||
const link = {
|
const link = {
|
||||||
Component: jest.fn(),
|
Component: jest.fn(),
|
||||||
to: '/plugins/bar',
|
to: '/plugins/bar',
|
||||||
@ -223,7 +222,7 @@ describe('ADMIN | StrapiApp', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('addCorePluginMenuLink should add a link to the menu', () => {
|
it('addCorePluginMenuLink should add a link to the menu', () => {
|
||||||
const app = StrapiApp({ middlewares, reducers, library, locales });
|
const app = StrapiApp({ middlewares, reducers, library });
|
||||||
const link = {
|
const link = {
|
||||||
to: '/plugins/content-type-builder',
|
to: '/plugins/content-type-builder',
|
||||||
icon: 'book',
|
icon: 'book',
|
||||||
@ -240,4 +239,83 @@ describe('ADMIN | StrapiApp', () => {
|
|||||||
expect(app.menu[0]).toEqual(link);
|
expect(app.menu[0]).toEqual(link);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('createCustomConfigurations', () => {
|
||||||
|
it('should add a locale', () => {
|
||||||
|
const adminConfig = {
|
||||||
|
config: { locales: ['fr'] },
|
||||||
|
};
|
||||||
|
const app = StrapiApp({ middlewares, reducers, library, adminConfig });
|
||||||
|
|
||||||
|
app.createCustomConfigurations();
|
||||||
|
|
||||||
|
expect(app.configurations.locales).toEqual(['en', 'fr']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should override the authLogo', () => {
|
||||||
|
const adminConfig = {
|
||||||
|
config: { auth: { logo: 'fr' } },
|
||||||
|
};
|
||||||
|
const app = StrapiApp({ middlewares, reducers, library, adminConfig });
|
||||||
|
|
||||||
|
app.createCustomConfigurations();
|
||||||
|
|
||||||
|
expect(app.configurations.authLogo).toBe('fr');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should override the menuLogo', () => {
|
||||||
|
const adminConfig = {
|
||||||
|
config: { menu: { logo: 'fr' } },
|
||||||
|
};
|
||||||
|
const app = StrapiApp({ middlewares, reducers, library, adminConfig });
|
||||||
|
|
||||||
|
app.createCustomConfigurations();
|
||||||
|
|
||||||
|
expect(app.configurations.menuLogo).toBe('fr');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should override the favicon', () => {
|
||||||
|
const adminConfig = {
|
||||||
|
config: { head: { favicon: 'fr' } },
|
||||||
|
};
|
||||||
|
const app = StrapiApp({ middlewares, reducers, library, adminConfig });
|
||||||
|
|
||||||
|
app.createCustomConfigurations();
|
||||||
|
|
||||||
|
expect(app.configurations.head.favicon).toBe('fr');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should override the theme', () => {
|
||||||
|
const adminConfig = {
|
||||||
|
config: { theme: { main: { colors: { red: 'black' } } } },
|
||||||
|
};
|
||||||
|
const app = StrapiApp({ middlewares, reducers, library, adminConfig });
|
||||||
|
|
||||||
|
app.createCustomConfigurations();
|
||||||
|
|
||||||
|
expect(app.configurations.theme.main.colors.red).toBe('black');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should override the tutorials', () => {
|
||||||
|
const adminConfig = {
|
||||||
|
config: { tutorials: false },
|
||||||
|
};
|
||||||
|
const app = StrapiApp({ middlewares, reducers, library, adminConfig });
|
||||||
|
|
||||||
|
app.createCustomConfigurations();
|
||||||
|
|
||||||
|
expect(app.configurations.tutorials).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should override the release notification', () => {
|
||||||
|
const adminConfig = {
|
||||||
|
config: { notifications: { releases: false } },
|
||||||
|
};
|
||||||
|
const app = StrapiApp({ middlewares, reducers, library, adminConfig });
|
||||||
|
|
||||||
|
app.createCustomConfigurations();
|
||||||
|
|
||||||
|
expect(app.configurations.notifications.releases).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -25,8 +25,6 @@ const getClientEnvironment = options => {
|
|||||||
ADMIN_PATH: options.adminPath,
|
ADMIN_PATH: options.adminPath,
|
||||||
NODE_ENV: options.env || 'development',
|
NODE_ENV: options.env || 'development',
|
||||||
STRAPI_ADMIN_BACKEND_URL: options.backend,
|
STRAPI_ADMIN_BACKEND_URL: options.backend,
|
||||||
STRAPI_ADMIN_SHOW_TUTORIALS: 'true',
|
|
||||||
STRAPI_ADMIN_UPDATE_NOTIFICATION: 'true',
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ const fs = require('fs-extra');
|
|||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const WebpackDevServer = require('webpack-dev-server');
|
const WebpackDevServer = require('webpack-dev-server');
|
||||||
const chalk = require('chalk');
|
const chalk = require('chalk');
|
||||||
|
const chokidar = require('chokidar');
|
||||||
const getWebpackConfig = require('./webpack.config');
|
const getWebpackConfig = require('./webpack.config');
|
||||||
|
|
||||||
const getPkgPath = name => path.dirname(require.resolve(`${name}/package.json`));
|
const getPkgPath = name => path.dirname(require.resolve(`${name}/package.json`));
|
||||||
@ -186,14 +187,18 @@ async function createCacheDir(dir) {
|
|||||||
// copy plugins code
|
// copy plugins code
|
||||||
await Promise.all(pluginsToCopy.map(name => copyPlugin(name, cacheDir)));
|
await Promise.all(pluginsToCopy.map(name => copyPlugin(name, cacheDir)));
|
||||||
|
|
||||||
// Copy admin.config.js
|
// Copy app.js
|
||||||
const customAdminConfigFilePath = path.join(dir, 'admin', 'admin.config.js');
|
const customAdminConfigFilePath = path.join(dir, 'admin', 'app.js');
|
||||||
|
|
||||||
if (fs.existsSync(customAdminConfigFilePath)) {
|
if (fs.existsSync(customAdminConfigFilePath)) {
|
||||||
await fs.copy(
|
await fs.copy(customAdminConfigFilePath, path.resolve(cacheDir, 'admin', 'src', 'app.js'));
|
||||||
customAdminConfigFilePath,
|
}
|
||||||
path.resolve(cacheDir, 'admin', 'src', 'admin.config.js')
|
|
||||||
);
|
// Copy admin extensions folder
|
||||||
|
const adminExtensionFolder = path.join(dir, 'admin', 'extensions');
|
||||||
|
|
||||||
|
if (fs.existsSync(adminExtensionFolder)) {
|
||||||
|
await fs.copy(adminExtensionFolder, path.resolve(cacheDir, 'admin', 'src', 'extensions'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// create plugins.js with plugins requires
|
// create plugins.js with plugins requires
|
||||||
@ -248,6 +253,57 @@ async function watchAdmin({ dir, host, port, browser, options }) {
|
|||||||
console.log();
|
console.log();
|
||||||
console.log(chalk.green(`Admin development at http://${host}:${port}${opts.publicPath}`));
|
console.log(chalk.green(`Admin development at http://${host}:${port}${opts.publicPath}`));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watchFiles(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen to files change and copy the changed files in the .cache/admin folder
|
||||||
|
* when using the dev mode
|
||||||
|
* @param {string} dir
|
||||||
|
*/
|
||||||
|
async function watchFiles(dir) {
|
||||||
|
await createCacheDir(dir);
|
||||||
|
const cacheDir = path.join(dir, '.cache');
|
||||||
|
const appExtensionFile = path.join(dir, 'admin', 'app.js');
|
||||||
|
const extensionsPath = path.join(dir, 'admin', 'extensions');
|
||||||
|
|
||||||
|
// Only watch the admin/app.js file and the files that are in the ./admin/extensions/folder
|
||||||
|
const filesToWatch = [appExtensionFile, extensionsPath];
|
||||||
|
|
||||||
|
const watcher = chokidar.watch(filesToWatch, {
|
||||||
|
ignoreInitial: true,
|
||||||
|
ignorePermissionErrors: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
watcher.on('all', async (event, filePath) => {
|
||||||
|
const isAppFile = filePath.includes(appExtensionFile);
|
||||||
|
|
||||||
|
// The app.js file needs to be copied in the .cache/admin/src/app.js and the other ones needs to
|
||||||
|
// be copied in the .cache/admin/src/extensions folder
|
||||||
|
const targetPath = isAppFile
|
||||||
|
? path.join(path.normalize(filePath.split(appExtensionFile)[1]), 'app.js')
|
||||||
|
: path.join('extensions', path.normalize(filePath.split(extensionsPath)[1]));
|
||||||
|
|
||||||
|
const destFolder = path.join(cacheDir, 'admin', 'src');
|
||||||
|
|
||||||
|
if (event === 'unlink' || event === 'unlinkDir') {
|
||||||
|
// Remove the file or folder
|
||||||
|
// We need to copy the original files when deleting an override one
|
||||||
|
try {
|
||||||
|
fs.removeSync(path.join(destFolder, targetPath));
|
||||||
|
} catch (err) {
|
||||||
|
console.log('An error occured while deleting the file', err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// In any other case just copy the file into the .cache/admin/src folder
|
||||||
|
try {
|
||||||
|
await fs.copy(filePath, path.join(destFolder, targetPath));
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
/* eslint-disable no-unused-vars */
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
app: config => {
|
|
||||||
config.locales = ['fr'];
|
|
||||||
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
4
packages/generators/app/lib/resources/files/admin/app.js
Normal file
4
packages/generators/app/lib/resources/files/admin/app.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export default {
|
||||||
|
config: {},
|
||||||
|
bootstrap() {},
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user