Merge branch 'master' into feature/relations-main-view

This commit is contained in:
Pierre Noël 2022-08-11 16:42:27 +02:00
commit 76fd68a934
1439 changed files with 8877 additions and 12021 deletions

View File

@ -7,6 +7,7 @@ examples/**
cypress/** cypress/**
packages/generators/generators/lib/files/ packages/generators/generators/lib/files/
packages/generators/app/lib/resources/files/ packages/generators/app/lib/resources/files/
packages/core/admin/admin/src/plugins.js
packages/core/helper-plugin/build/** packages/core/helper-plugin/build/**
packages/core/helper-plugin/lib/src/old/components/** packages/core/helper-plugin/lib/src/old/components/**
packages/core/helper-plugin/lib/src/testUtils/** packages/core/helper-plugin/lib/src/testUtils/**

View File

@ -1,55 +1,36 @@
'use strict'; 'use strict';
module.exports = { module.exports = {
extends: [ extends: '@strapi/eslint-config/back',
'eslint:recommended',
'prettier',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:node/recommended',
// 'plugin:jsdoc/recommended',
],
plugins: ['jsdoc'],
env: {
es6: true,
node: true,
jest: true,
},
globals: { globals: {
strapi: false, strapi: false,
}, },
rules: { rules: {
// 'jsdoc/require-jsdoc': [ 'import/no-dynamic-require': 'off',
// 'warn', 'global-require': 'off',
// { 'import/no-extraneous-dependencies': [
// publicOnly: true, 'error',
// require: { {
// ArrowFunctionExpression: true, devDependencies: [
// ClassDeclaration: true, 'packages/admin-test-utils/**/*.js',
// ClassExpression: true, 'packages/generators/admin/**/*.js',
// FunctionDeclaration: true, 'scripts/**/*.js',
// FunctionExpression: true, '**/test/**/*.js',
// MethodDefinition: true, '**/tests/**/*.js',
// }, '**/__tests__/**/*.js',
// }, '**/__mocks__/**/*.js',
// ], ],
'node/no-unpublished-require': 'off', },
'require-atomic-updates': 'off', ],
'no-process-exit': 'off', 'prefer-destructuring': ['error', { AssignmentExpression: { array: false } }],
strict: ['error', 'global'], eqeqeq: 'warn',
'no-return-await': 'error', 'no-underscore-dangle': 'warn',
'object-shorthand': ['error', 'always', { avoidExplicitReturnArrows: true }], 'no-use-before-define': 'warn',
'import/order': 'error', 'no-param-reassign': 'warn',
'import/no-cycle': 'error', 'no-continue': 'warn',
'import/no-useless-path-segments': 'error', 'no-process-exit': 'warn',
'import/first': 'error', 'no-plusplus': 'warn',
'import/extensions': ['error', 'never'], 'no-loop-func': 'warn',
'import/newline-after-import': 'error', 'guard-for-in': 'warn',
'node/exports-style': ['error', 'module.exports'],
'node/no-new-require': 'error',
'node/no-path-concat': 'error',
'node/no-callback-literal': 'error',
'node/handle-callback-err': 'error',
'one-var': ['error', 'never'],
}, },
}; };

View File

@ -1,13 +1,6 @@
module.exports = { module.exports = {
parser: 'babel-eslint', parser: '@babel/eslint-parser',
extends: [ extends: ['@strapi/eslint-config/front'],
'airbnb',
'eslint:recommended',
'plugin:react/recommended',
'plugin:redux-saga/recommended',
'prettier',
],
plugins: ['react', 'redux-saga', 'react-hooks', 'import', 'jsx-a11y'],
env: { env: {
browser: true, browser: true,
commonjs: true, commonjs: true,
@ -16,11 +9,10 @@ module.exports = {
mocha: true, mocha: true,
}, },
parserOptions: { parserOptions: {
ecmaVersion: 2018, requireConfigFile: false,
ecmaFeatures: { babelOptions: {
jsx: true, presets: ['@babel/preset-react'],
}, },
sourceType: 'module',
}, },
globals: { globals: {
strapi: false, strapi: false,
@ -39,86 +31,12 @@ module.exports = {
}, },
settings: { settings: {
react: { react: {
version: '16.5.2', version: 'detect',
}, },
}, },
rules: { rules: {
'import/no-unresolved': 0, 'react/jsx-no-constructed-context-values': 'warn',
'generator-star-spacing': 0, 'react/jsx-no-useless-fragment': 'warn',
'no-console': 0, 'react/no-unstable-nested-components': 'warn',
'require-atomic-updates': 0,
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'error',
'arrow-body-style': 0,
'arrow-parens': 0,
camelcase: 0,
'comma-dangle': 0,
'consistent-return': [
2,
{
treatUndefinedAsUnspecified: true,
},
],
'template-curly-spacing': 0,
'func-names': ['error', 'never'],
'function-paren-newline': 0,
'implicit-arrow-linebreak': 0,
'import/no-extraneous-dependencies': 0,
'import/no-named-as-default': 0,
'import/order': 2,
'import/prefer-default-export': 'off',
'jsx-a11y/click-events-have-key-events': 1,
'max-len': [
2,
{
code: 120,
ignoreComments: true,
ignoreUrls: true,
ignoreTrailingComments: true,
ignoreStrings: true,
ignoreTemplateLiterals: true,
},
],
'newline-before-return': 2,
'no-confusing-arrow': 0,
'no-else-return': 1,
'no-nested-ternary': ['error'],
'no-return-assign': 0,
'no-param-reassign': 0,
'no-plusplus': 0,
'no-shadow': 0,
'no-underscore-dangle': 0,
'no-use-before-define': ['error', { functions: false, classes: false, variables: false }],
'object-curly-newline': [2, { multiline: true, consistent: true }],
'one-var': ['error', 'never'],
'operator-linebreak': 0,
'padding-line-between-statements': [
'error',
{ blankLine: 'always', prev: '*', next: 'if' },
{ blankLine: 'any', prev: 'block-like', next: 'if' },
],
'prefer-arrow-callback': 0,
'prefer-const': 0,
'prefer-destructuring': 0,
'prefer-object-spread': 0,
'prefer-spread': 0,
'space-before-function-paren': [
'error',
{
anonymous: 'never',
named: 'never',
asyncArrow: 'always',
},
],
'react/destructuring-assignment': 0,
'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx'] }],
'react/forbid-prop-types': 0,
'react/no-unused-prop-types': 2,
'react/jsx-props-no-spreading': 0,
'react/jsx-one-expression-per-line': 0,
'react/state-in-constructor': 0,
'react/static-property-placement': 0,
'react/display-name': 0,
'react/jsx-wrap-multilines': 0,
}, },
}; };

View File

@ -6,7 +6,7 @@ jest.mock('@actions/core');
const github = require('@actions/github'); const github = require('@actions/github');
const core = require('@actions/core'); const core = require('@actions/core');
test.each(action.BLOCKING_LABELS)('Test blocking labels %s', async label => { test.each(action.BLOCKING_LABELS)('Test blocking labels %s', async (label) => {
github.context = { github.context = {
payload: { payload: {
pull_request: { pull_request: {

View File

@ -7,20 +7,20 @@ async function main() {
try { try {
const labels = github.context.payload.pull_request?.labels ?? []; const labels = github.context.payload.pull_request?.labels ?? [];
const blockingLabels = labels.filter(label => BLOCKING_LABELS.includes(label.name)); const blockingLabels = labels.filter((label) => BLOCKING_LABELS.includes(label.name));
if (blockingLabels.length > 0) { if (blockingLabels.length > 0) {
core.setFailed( core.setFailed(
`The PR has been labelled with a blocking label (${blockingLabels `The PR has been labelled with a blocking label (${blockingLabels
.map(label => label.name) .map((label) => label.name)
.join(', ')}).` .join(', ')}).`
); );
return; return;
} }
const sourceLabelCount = labels.filter(label => label.name.startsWith('source: ')).length; const sourceLabelCount = labels.filter((label) => label.name.startsWith('source: ')).length;
const issueLabelCount = labels.filter(label => label.name.startsWith('pr: ')).length; const issueLabelCount = labels.filter((label) => label.name.startsWith('pr: ')).length;
if (sourceLabelCount !== 1) { if (sourceLabelCount !== 1) {
core.setFailed(`The PR must have one and only one 'source:' label.`); core.setFailed(`The PR must have one and only one 'source:' label.`);

View File

@ -1,6 +1,6 @@
{ {
"name": "check-pr-status", "name": "check-pr-status",
"version": "4.3.2", "version": "4.3.4",
"main": "dist/index.js", "main": "dist/index.js",
"license": "MIT", "license": "MIT",
"private": true, "private": true,

View File

@ -5,4 +5,5 @@ module.exports = {
tabWidth: 2, tabWidth: 2,
trailingComma: 'es5', trailingComma: 'es5',
printWidth: 100, printWidth: 100,
arrowParens: 'always',
}; };

View File

@ -1,3 +0,0 @@
[mysqld]
collation-server=utf8_unicode_ci
character-set-server=utf8

View File

@ -1,61 +0,0 @@
{
"sourceType": "unambiguous",
"presets": [
[
"@babel/preset-env",
{
"shippedProposals": true,
"loose": true
}
],
"@babel/preset-typescript"
],
"plugins": [
"@babel/plugin-transform-shorthand-properties",
"@babel/plugin-transform-block-scoping",
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
],
[
"@babel/plugin-proposal-class-properties",
{
"loose": true
}
],
[
"@babel/plugin-proposal-private-methods",
{
"loose": true
}
],
"@babel/plugin-proposal-export-default-from",
"@babel/plugin-syntax-dynamic-import",
[
"@babel/plugin-proposal-object-rest-spread",
{
"loose": true,
"useBuiltIns": true
}
],
"@babel/plugin-transform-classes",
"@babel/plugin-transform-arrow-functions",
"@babel/plugin-transform-parameters",
"@babel/plugin-transform-destructuring",
"@babel/plugin-transform-spread",
"@babel/plugin-transform-for-of",
"babel-plugin-macros",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator",
[
"babel-plugin-polyfill-corejs3",
{
"method": "usage-global",
"absoluteImports": "core-js",
"version": "3.21.1"
}
]
]
}

View File

@ -1,15 +0,0 @@
module.exports = {
stories: [
'../*.stories.mdx',
'../../../packages/core/**/admin/src/**/*.stories.mdx',
'../../../packages/core/**/admin/src/**/*.stories.@(js|jsx|ts|tsx)',
'../../../packages/plugins/**/admin/src/**/*.stories.mdx',
'../../../packages/plugins/**/admin/src/**/*.stories.@(js|jsx|ts|tsx)',
],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
],
framework: '@storybook/react',
};

View File

@ -1,27 +0,0 @@
import { ThemeProvider, lightTheme } from '@strapi/design-system';
import { IntlProvider } from 'react-intl';
import { MemoryRouter } from 'react-router-dom';
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
};
export const decorators = [
Story => (
<MemoryRouter>
<ThemeProvider theme={lightTheme}>
<IntlProvider messages={{}} textComponent="span" locale="en">
<main>
<Story />
</main>
</IntlProvider>
</ThemeProvider>
</MemoryRouter>
),
];

View File

@ -1,9 +0,0 @@
import { Meta } from '@storybook/addon-docs';
<Meta title="Introduction" />
# Welcome to the documentation
Use this app to develop local components in plugins.
To do so just create a story in your plugins `./admin/src` folder

View File

@ -1,42 +0,0 @@
{
"name": "admin-development",
"version": "4.3.2",
"main": "index.js",
"license": "MIT",
"private": true,
"devDependencies": {
"@babel/core": "7.18.10",
"@babel/plugin-proposal-class-properties": "7.16.7",
"@babel/plugin-proposal-decorators": "7.16.7",
"@babel/plugin-proposal-export-default-from": "7.16.7",
"@babel/plugin-proposal-nullish-coalescing-operator": "7.16.7",
"@babel/plugin-proposal-object-rest-spread": "7.16.7",
"@babel/plugin-proposal-optional-chaining": "7.18.9",
"@babel/plugin-proposal-private-methods": "7.16.7",
"@babel/plugin-syntax-dynamic-import": "7.8.3",
"@babel/plugin-transform-arrow-functions": "7.16.7",
"@babel/plugin-transform-block-scoping": "7.16.7",
"@babel/plugin-transform-classes": "7.16.7",
"@babel/plugin-transform-destructuring": "7.17.7",
"@babel/plugin-transform-for-of": "7.16.7",
"@babel/plugin-transform-parameters": "7.16.7",
"@babel/plugin-transform-shorthand-properties": "7.16.7",
"@babel/plugin-transform-spread": "7.16.7",
"@babel/preset-env": "7.18.10",
"@babel/preset-typescript": "7.16.7",
"@storybook/addon-actions": "6.5.10",
"@storybook/addon-essentials": "6.4.10",
"@storybook/addon-interactions": "6.4.10",
"@storybook/addon-links": "6.4.10",
"@storybook/react": "^6.3.7",
"@storybook/testing-library": "^0.0.9",
"babel-loader": "^8.2.4",
"babel-plugin-macros": "3.1.0",
"babel-plugin-polyfill-corejs3": "0.5.2",
"core-js": "3.21.1"
},
"scripts": {
"storybook": "start-storybook -p 6007",
"build-storybook": "build-storybook"
}
}

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
module.exports = { module.exports = {
'404': async (/* ctx */) => { 404: async (/* ctx */) => {
// return ctx.notFound('My custom message 404'); // return ctx.notFound('My custom message 404');
}, },
}; };

View File

@ -1,7 +1,7 @@
{ {
"name": "getstarted", "name": "getstarted",
"private": true, "private": true,
"version": "4.3.2", "version": "4.3.4",
"description": "A Strapi application.", "description": "A Strapi application.",
"scripts": { "scripts": {
"develop": "strapi develop", "develop": "strapi develop",
@ -12,17 +12,15 @@
"strapi": "strapi" "strapi": "strapi"
}, },
"dependencies": { "dependencies": {
"@strapi/admin": "4.3.2", "@strapi/plugin-documentation": "4.3.4",
"@strapi/plugin-documentation": "4.3.2", "@strapi/plugin-graphql": "4.3.4",
"@strapi/plugin-graphql": "4.3.2", "@strapi/plugin-i18n": "4.3.4",
"@strapi/plugin-i18n": "4.3.2", "@strapi/plugin-sentry": "4.3.4",
"@strapi/plugin-sentry": "4.3.2", "@strapi/plugin-users-permissions": "4.3.4",
"@strapi/plugin-users-permissions": "4.3.2", "@strapi/provider-email-mailgun": "4.3.4",
"@strapi/provider-email-mailgun": "4.3.2", "@strapi/provider-upload-aws-s3": "4.3.4",
"@strapi/provider-upload-aws-s3": "4.3.2", "@strapi/provider-upload-cloudinary": "4.3.4",
"@strapi/provider-upload-cloudinary": "4.3.2", "@strapi/strapi": "4.3.4",
"@strapi/strapi": "4.3.2",
"@strapi/utils": "4.3.2",
"@vscode/sqlite3": "5.0.8", "@vscode/sqlite3": "5.0.8",
"better-sqlite3": "7.4.6", "better-sqlite3": "7.4.6",
"lodash": "4.17.21", "lodash": "4.17.21",

View File

@ -1,4 +1,4 @@
module.exports = options => { module.exports = (options) => {
return (ctx, next) => { return (ctx, next) => {
ctx.set('X-Strapi-Test', 'Address Middleware'); ctx.set('X-Strapi-Test', 'Address Middleware');
return next(); return next();

View File

@ -4,7 +4,7 @@ module.exports = createCoreService('api::address.address', {
async find(...args) { async find(...args) {
const { results, pagination } = await super.find(...args); const { results, pagination } = await super.find(...args);
results.forEach(result => { results.forEach((result) => {
result.counter = 1; result.counter = 1;
}); });

View File

@ -1,4 +1,5 @@
{ {
"kind": "collectionType",
"collectionName": "reviews", "collectionName": "reviews",
"info": { "info": {
"displayName": "Review", "displayName": "Review",

View File

@ -1,3 +1,3 @@
module.exports = plugin => { module.exports = (plugin) => {
return plugin; return plugin;
}; };

View File

@ -29,7 +29,7 @@ export default {
bootstrap() {}, bootstrap() {},
async registerTrads({ locales }) { async registerTrads({ locales }) {
const importedTrads = await Promise.all( const importedTrads = await Promise.all(
locales.map(locale => { locales.map((locale) => {
return import( return import(
/* webpackChunkName: "[pluginId]-[request]" */ `./translations/${locale}.json` /* webpackChunkName: "[pluginId]-[request]" */ `./translations/${locale}.json`
) )

View File

@ -1,5 +1,5 @@
import pluginId from '../pluginId'; import pluginId from '../pluginId';
const getTrad = id => `${pluginId}.${id}`; const getTrad = (id) => `${pluginId}.${id}`;
export default getTrad; export default getTrad;

View File

@ -4,7 +4,7 @@ module.exports = {
default: { default: {
testConf: 1, testConf: 1,
}, },
validator: config => { validator: (config) => {
if (typeof config.testConf !== 'number') { if (typeof config.testConf !== 'number') {
throw new Error('testConfig has to be a number'); throw new Error('testConfig has to be a number');
} }

View File

@ -1,7 +1,7 @@
{ {
"name": "kitchensink-ts", "name": "kitchensink-ts",
"private": true, "private": true,
"version": "4.3.2", "version": "4.3.4",
"description": "A Strapi application", "description": "A Strapi application",
"scripts": { "scripts": {
"develop": "strapi develop", "develop": "strapi develop",
@ -10,9 +10,9 @@
"strapi": "strapi" "strapi": "strapi"
}, },
"dependencies": { "dependencies": {
"@strapi/plugin-i18n": "4.1.12", "@strapi/plugin-i18n": "4.3.4",
"@strapi/plugin-users-permissions": "4.1.12", "@strapi/plugin-users-permissions": "4.3.4",
"@strapi/strapi": "4.2.2", "@strapi/strapi": "4.3.4",
"better-sqlite3": "7.4.6" "better-sqlite3": "7.4.6"
}, },
"author": { "author": {

View File

@ -1,7 +1,7 @@
{ {
"name": "kitchensink", "name": "kitchensink",
"private": true, "private": true,
"version": "4.3.2", "version": "4.3.4",
"description": "A Strapi application.", "description": "A Strapi application.",
"scripts": { "scripts": {
"develop": "strapi develop", "develop": "strapi develop",
@ -12,12 +12,10 @@
"strapi": "strapi" "strapi": "strapi"
}, },
"dependencies": { "dependencies": {
"@strapi/admin": "4.3.2", "@strapi/provider-email-mailgun": "4.3.4",
"@strapi/provider-email-mailgun": "4.3.2", "@strapi/provider-upload-aws-s3": "4.3.4",
"@strapi/provider-upload-aws-s3": "4.3.2", "@strapi/provider-upload-cloudinary": "4.3.4",
"@strapi/provider-upload-cloudinary": "4.3.2", "@strapi/strapi": "4.3.4",
"@strapi/strapi": "4.3.2",
"@strapi/utils": "4.3.2",
"lodash": "4.17.21", "lodash": "4.17.21",
"mysql": "2.18.1", "mysql": "2.18.1",
"passport-google-oauth2": "0.2.0", "passport-google-oauth2": "0.2.0",

View File

@ -2,8 +2,7 @@ import theme from './extensions/theme';
const config = { const config = {
auth: { auth: {
logo: logo: 'https://images.unsplash.com/photo-1593642634367-d91a135587b5?ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=750&q=80',
'https://images.unsplash.com/photo-1593642634367-d91a135587b5?ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=750&q=80',
}, },
head: { head: {
favicon: favicon:
@ -12,8 +11,7 @@ const config = {
}, },
locales: ['fr', 'de'], locales: ['fr', 'de'],
menu: { menu: {
logo: logo: 'https://images.unsplash.com/photo-1593642634367-d91a135587b5?ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=750&q=80',
'https://images.unsplash.com/photo-1593642634367-d91a135587b5?ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=750&q=80',
}, },
theme, theme,
translations: { translations: {
@ -29,7 +27,7 @@ const config = {
notifications: { release: false }, notifications: { release: false },
}; };
const bootstrap = app => { const bootstrap = (app) => {
console.log(app); console.log(app);
}; };

View File

@ -2,8 +2,7 @@ import theme from './extensions/theme';
const config = { const config = {
auth: { auth: {
logo: logo: 'https://images.unsplash.com/photo-1593642634367-d91a135587b5?ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=750&q=80',
'https://images.unsplash.com/photo-1593642634367-d91a135587b5?ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=750&q=80',
}, },
head: { head: {
favicon: favicon:
@ -12,8 +11,7 @@ const config = {
}, },
locales: ['fr', 'de'], locales: ['fr', 'de'],
menu: { menu: {
logo: logo: 'https://images.unsplash.com/photo-1593642634367-d91a135587b5?ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=750&q=80',
'https://images.unsplash.com/photo-1593642634367-d91a135587b5?ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=750&q=80',
}, },
theme, theme,
translations: { translations: {
@ -29,7 +27,7 @@ const config = {
notifications: { release: false }, notifications: { release: false },
}; };
const bootstrap = app => { const bootstrap = (app) => {
console.log(app); console.log(app);
}; };

View File

@ -1,5 +1,5 @@
{ {
"version": "4.3.2", "version": "4.3.4",
"packages": [ "packages": [
"packages/*", "packages/*",
"examples/*" "examples/*"

View File

@ -71,7 +71,11 @@
"@babel/polyfill": "7.12.1" "@babel/polyfill": "7.12.1"
}, },
"devDependencies": { "devDependencies": {
"@swc/core": "1.2.218", "@babel/core": "7.18.10",
"@babel/eslint-parser": "7.18.9",
"@babel/preset-react": "7.18.6",
"@strapi/eslint-config": "0.1.1",
"@swc/core": "1.2.224",
"@swc/jest": "0.2.22", "@swc/jest": "0.2.22",
"@testing-library/react": "11.2.7", "@testing-library/react": "11.2.7",
"@testing-library/react-hooks": "3.7.0", "@testing-library/react-hooks": "3.7.0",
@ -82,22 +86,13 @@
"chokidar": "3.5.3", "chokidar": "3.5.3",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"dotenv": "14.2.0", "dotenv": "14.2.0",
"eslint": "7.32.0", "eslint": "8.21.0",
"eslint-config-airbnb": "18.2.1",
"eslint-config-airbnb-base": "14.2.1",
"eslint-config-prettier": "6.15.0",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-jsdoc": "36.1.1",
"eslint-plugin-jsx-a11y": "6.6.1",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-react": "7.30.1",
"eslint-plugin-react-hooks": "4.6.0",
"eslint-plugin-redux-saga": "1.3.2",
"execa": "1.0.0", "execa": "1.0.0",
"fs-extra": "10.1.0", "fs-extra": "10.1.0",
"get-port": "5.1.1", "get-port": "5.1.1",
"glob": "7.2.3", "glob": "7.2.3",
"husky": "3.1.0", "husky": "3.1.0",
"inquirer": "8.2.4",
"istanbul": "~0.4.2", "istanbul": "~0.4.2",
"jest": "26.6.3", "jest": "26.6.3",
"jest-circus": "26.6.3", "jest-circus": "26.6.3",
@ -110,7 +105,7 @@
"npm-run-all": "4.1.5", "npm-run-all": "4.1.5",
"nx": "14.4.2", "nx": "14.4.2",
"plop": "2.7.6", "plop": "2.7.6",
"prettier": "1.19.1", "prettier": "2.7.1",
"qs": "6.11.0", "qs": "6.11.0",
"react-test-renderer": "17.0.2", "react-test-renderer": "17.0.2",
"request": "2.88.2", "request": "2.88.2",

View File

@ -1,6 +1,5 @@
'use strict'; 'use strict';
// eslint-disable-next-line node/no-extraneous-require
const { combineReducers, createStore } = require('redux'); const { combineReducers, createStore } = require('redux');
const reducers = { const reducers = {

View File

@ -25,4 +25,4 @@ global.strapi = {
global.prompt = jest.fn(); global.prompt = jest.fn();
global.URL.createObjectURL = file => `http://localhost:4000/assets/${file.name}`; global.URL.createObjectURL = (file) => `http://localhost:4000/assets/${file.name}`;

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
// needed for regenerator-runtime // needed for regenerator-runtime
// (ES7 generator support is required by redux-saga)
require('@babel/polyfill'); require('@babel/polyfill');
const noop = () => {}; const noop = () => {};

View File

@ -1,6 +1,6 @@
{ {
"name": "@strapi/admin-test-utils", "name": "@strapi/admin-test-utils",
"version": "4.3.2", "version": "4.3.4",
"private": true, "private": true,
"description": "Test utilities for the Strapi administration panel", "description": "Test utilities for the Strapi administration panel",
"license": "MIT", "license": "MIT",
@ -21,9 +21,12 @@
"@babel/polyfill": "7.12.1" "@babel/polyfill": "7.12.1"
}, },
"devDependencies": { "devDependencies": {
"@testing-library/jest-dom": "5.16.4", "@testing-library/jest-dom": "5.16.5",
"jest-styled-components": "7.0.2" "jest-styled-components": "7.0.2"
}, },
"peerDependencies": {
"redux": "^4.0.1"
},
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },

View File

@ -39,7 +39,7 @@ program
.option('--template <templateurl>', 'Specify a Strapi template') .option('--template <templateurl>', 'Specify a Strapi template')
.option('--ts, --typescript', 'Use TypeScript to generate the project') .option('--ts, --typescript', 'Use TypeScript to generate the project')
.description('create a new application') .description('create a new application')
.action(directory => { .action((directory) => {
initProject(directory, program); initProject(directory, program);
}) })
.parse(process.argv); .parse(process.argv);
@ -62,7 +62,7 @@ async function initProject(projectName, program) {
await checkInstallPath(resolve(projectName)); await checkInstallPath(resolve(projectName));
} }
const hasDatabaseOptions = databaseOptions.some(opt => program[opt]); const hasDatabaseOptions = databaseOptions.some((opt) => program[opt]);
if (program.quickstart && hasDatabaseOptions) { if (program.quickstart && hasDatabaseOptions) {
console.error( console.error(

View File

@ -1,4 +1,5 @@
#!/usr/bin/env node #!/usr/bin/env node
'use strict'; 'use strict';
require('./create-strapi-app'); require('./create-strapi-app');

View File

@ -1,6 +1,6 @@
{ {
"name": "create-strapi-app", "name": "create-strapi-app",
"version": "4.3.2", "version": "4.3.4",
"description": "Generate a new Strapi application.", "description": "Generate a new Strapi application.",
"keywords": [ "keywords": [
"create-strapi-app", "create-strapi-app",
@ -38,7 +38,7 @@
"test": "echo \"no tests yet\"" "test": "echo \"no tests yet\""
}, },
"dependencies": { "dependencies": {
"@strapi/generate-new": "4.3.2", "@strapi/generate-new": "4.3.4",
"commander": "6.1.0", "commander": "6.1.0",
"inquirer": "8.2.4" "inquirer": "8.2.4"
}, },

View File

@ -57,7 +57,9 @@ function generateApp(projectArgs, programArgs) {
} }
async function initProject(projectArgs, program) { async function initProject(projectArgs, program) {
const hasIncompatibleQuickstartOptions = incompatibleQuickstartOptions.some(opt => program[opt]); const hasIncompatibleQuickstartOptions = incompatibleQuickstartOptions.some(
(opt) => program[opt]
);
if (program.quickstart && hasIncompatibleQuickstartOptions) { if (program.quickstart && hasIncompatibleQuickstartOptions) {
console.error( console.error(
@ -96,7 +98,7 @@ async function initProject(projectArgs, program) {
try { try {
program.parse(process.argv); program.parse(process.argv);
} catch (err) { } catch (err) {
if (err.exitCode && err.exitCode != 0) { if (err.exitCode && err.exitCode !== 0) {
program.outputHelp(); program.outputHelp();
} }
} }

View File

@ -1,4 +1,5 @@
#!/usr/bin/env node #!/usr/bin/env node
'use strict'; 'use strict';
require('./create-strapi-starter'); require('./create-strapi-starter');

View File

@ -1,6 +1,6 @@
{ {
"name": "create-strapi-starter", "name": "create-strapi-starter",
"version": "4.3.2", "version": "4.3.4",
"description": "Generate a new Strapi application.", "description": "Generate a new Strapi application.",
"keywords": [ "keywords": [
"create-strapi-starter", "create-strapi-starter",
@ -38,7 +38,7 @@
"test": "echo \"no tests yet\"" "test": "echo \"no tests yet\""
}, },
"dependencies": { "dependencies": {
"@strapi/generate-new": "4.3.2", "@strapi/generate-new": "4.3.4",
"chalk": "4.1.1", "chalk": "4.1.1",
"ci-info": "3.3.2", "ci-info": "3.3.2",
"commander": "7.1.0", "commander": "7.1.0",

View File

@ -72,10 +72,7 @@ async function installWithLogs(path, options) {
const installPrefix = chalk.yellow('Installing dependencies:'); const installPrefix = chalk.yellow('Installing dependencies:');
const loader = ora(installPrefix).start(); const loader = ora(installPrefix).start();
const logInstall = (chunk = '') => { const logInstall = (chunk = '') => {
loader.text = `${installPrefix} ${chunk loader.text = `${installPrefix} ${chunk.toString().split('\n').join(' ')}`;
.toString()
.split('\n')
.join(' ')}`;
}; };
const runner = runInstall(path, options); const runner = runInstall(path, options);
@ -94,7 +91,7 @@ async function installWithLogs(path, options) {
* @param {boolean} options.useYarn Use yarn instead of npm * @param {boolean} options.useYarn Use yarn instead of npm
*/ */
async function getStarterInfo(starter, { useYarn } = {}) { async function getStarterInfo(starter, { useYarn } = {}) {
const isLocalStarter = ['./', '../', '/'].some(filePrefix => starter.startsWith(filePrefix)); const isLocalStarter = ['./', '../', '/'].some((filePrefix) => starter.startsWith(filePrefix));
let starterPath; let starterPath;
let starterParentPath; let starterParentPath;
@ -125,12 +122,8 @@ async function getStarterInfo(starter, { useYarn } = {}) {
*/ */
module.exports = async function buildStarter({ projectName, starter }, program) { module.exports = async function buildStarter({ projectName, starter }, program) {
const hasYarnInstalled = await hasYarn(); const hasYarnInstalled = await hasYarn();
const { const { isLocalStarter, starterPath, starterParentPath, starterPackageInfo } =
isLocalStarter, await getStarterInfo(starter, { useYarn: hasYarnInstalled });
starterPath,
starterParentPath,
starterPackageInfo,
} = await getStarterInfo(starter, { useYarn: hasYarnInstalled });
// Project directory // Project directory
const rootPath = resolve(projectName); const rootPath = resolve(projectName);

View File

@ -27,12 +27,11 @@ function runApp(rootPath, { useYarn } = {}) {
stdio: 'inherit', stdio: 'inherit',
cwd: rootPath, cwd: rootPath,
}); });
} else {
return execa('npm', ['run', 'develop'], {
stdio: 'inherit',
cwd: rootPath,
});
} }
return execa('npm', ['run', 'develop'], {
stdio: 'inherit',
cwd: rootPath,
});
} }
async function initGit(rootPath) { async function initGit(rootPath) {

View File

@ -1,4 +1,5 @@
'use strict'; 'use strict';
const logger = require('./logger'); const logger = require('./logger');
module.exports = function stopProcess(message) { module.exports = function stopProcess(message) {

View File

@ -61,15 +61,15 @@ class StrapiApp {
}; };
} }
addComponents = components => { addComponents = (components) => {
if (Array.isArray(components)) { if (Array.isArray(components)) {
components.map(compo => this.library.components.add(compo)); components.map((compo) => this.library.components.add(compo));
} else { } else {
this.library.components.add(components); this.library.components.add(components);
} }
}; };
addCorePluginMenuLink = link => { addCorePluginMenuLink = (link) => {
const stringifiedLink = JSON.stringify(link); const stringifiedLink = JSON.stringify(link);
invariant(link.to, `link.to should be defined for ${stringifiedLink}`); invariant(link.to, `link.to should be defined for ${stringifiedLink}`);
@ -89,15 +89,15 @@ class StrapiApp {
this.menu.push(link); this.menu.push(link);
}; };
addFields = fields => { addFields = (fields) => {
if (Array.isArray(fields)) { if (Array.isArray(fields)) {
fields.map(field => this.library.fields.add(field)); fields.map((field) => this.library.fields.add(field));
} else { } else {
this.library.fields.add(fields); this.library.fields.add(fields);
} }
}; };
addMenuLink = link => { addMenuLink = (link) => {
const stringifiedLink = JSON.stringify(link); const stringifiedLink = JSON.stringify(link);
invariant(link.to, `link.to should be defined for ${stringifiedLink}`); invariant(link.to, `link.to should be defined for ${stringifiedLink}`);
@ -121,14 +121,14 @@ class StrapiApp {
this.menu.push(link); this.menu.push(link);
}; };
addMiddlewares = middlewares => { addMiddlewares = (middlewares) => {
middlewares.forEach(middleware => { middlewares.forEach((middleware) => {
this.middlewares.add(middleware); this.middlewares.add(middleware);
}); });
}; };
addReducers = reducers => { addReducers = (reducers) => {
Object.keys(reducers).forEach(reducerName => { Object.keys(reducers).forEach((reducerName) => {
this.reducers.add(reducerName, reducers[reducerName]); this.reducers.add(reducerName, reducers[reducerName]);
}); });
}; };
@ -156,13 +156,13 @@ class StrapiApp {
invariant(this.settings[sectionId], 'The section does not exist'); invariant(this.settings[sectionId], 'The section does not exist');
invariant(Array.isArray(links), 'TypeError expected links to be an array'); invariant(Array.isArray(links), 'TypeError expected links to be an array');
links.forEach(link => { links.forEach((link) => {
this.addSettingsLink(sectionId, link); this.addSettingsLink(sectionId, link);
}); });
}; };
async bootstrap() { async bootstrap() {
Object.keys(this.appPlugins).forEach(plugin => { Object.keys(this.appPlugins).forEach((plugin) => {
const bootstrap = this.appPlugins[plugin].bootstrap; const bootstrap = this.appPlugins[plugin].bootstrap;
if (bootstrap) { if (bootstrap) {
@ -208,7 +208,7 @@ class StrapiApp {
if (this.customConfigurations?.locales) { if (this.customConfigurations?.locales) {
this.configurations.locales = [ this.configurations.locales = [
'en', 'en',
...this.customConfigurations.locales?.filter(loc => loc !== 'en'), ...(this.customConfigurations.locales?.filter((loc) => loc !== 'en') || []),
]; ];
} }
@ -237,7 +237,7 @@ class StrapiApp {
} }
}; };
createHook = name => { createHook = (name) => {
this.hooksDict[name] = createHook(); this.hooksDict[name] = createHook();
}; };
@ -253,7 +253,7 @@ class StrapiApp {
this.settings[section.id] = { ...section, links: [] }; this.settings[section.id] = { ...section, links: [] };
links.forEach(link => { links.forEach((link) => {
this.addSettingsLink(section.id, link); this.addSettingsLink(section.id, link);
}); });
}; };
@ -274,12 +274,12 @@ class StrapiApp {
} }
}; };
getPlugin = pluginId => { getPlugin = (pluginId) => {
return this.plugins[pluginId]; return this.plugins[pluginId];
}; };
async initialize() { async initialize() {
Object.keys(this.appPlugins).forEach(plugin => { Object.keys(this.appPlugins).forEach((plugin) => {
this.appPlugins[plugin].register({ this.appPlugins[plugin].register({
addComponents: this.addComponents, addComponents: this.addComponents,
addCorePluginMenuLink: this.addCorePluginMenuLink, addCorePluginMenuLink: this.addCorePluginMenuLink,
@ -319,7 +319,7 @@ class StrapiApp {
* @returns {Object} The imported admin translations * @returns {Object} The imported admin translations
*/ */
async loadAdminTrads() { async loadAdminTrads() {
const arrayOfPromises = this.configurations.locales.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 };
@ -350,7 +350,7 @@ class StrapiApp {
const adminTranslations = await this.loadAdminTrads(); 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) {
@ -359,7 +359,7 @@ class StrapiApp {
return null; return null;
}) })
.filter(a => a); .filter((a) => a);
const pluginsTrads = await Promise.all(arrayOfPromises); const pluginsTrads = await Promise.all(arrayOfPromises);
const mergedTrads = pluginsTrads.reduce((acc, currentPluginTrads) => { const mergedTrads = pluginsTrads.reduce((acc, currentPluginTrads) => {
@ -369,7 +369,7 @@ class StrapiApp {
return acc1; return acc1;
}, {}); }, {});
Object.keys(pluginTrads).forEach(locale => { Object.keys(pluginTrads).forEach((locale) => {
acc[locale] = { ...acc[locale], ...pluginTrads[locale] }; acc[locale] = { ...acc[locale], ...pluginTrads[locale] };
}); });
@ -399,7 +399,7 @@ class StrapiApp {
this.hooksDict[name].register(fn); this.hooksDict[name].register(fn);
}; };
registerPlugin = pluginConf => { registerPlugin = (pluginConf) => {
const plugin = Plugin(pluginConf); const plugin = Plugin(pluginConf);
this.plugins[plugin.pluginId] = plugin; this.plugins[plugin.pluginId] = plugin;
@ -414,7 +414,7 @@ class StrapiApp {
: this.hooksDict[name].runWaterfall(initialValue, store); : this.hooksDict[name].runWaterfall(initialValue, store);
}; };
runHookParallel = name => this.hooksDict[name].runParallel(); runHookParallel = (name) => this.hooksDict[name].runParallel();
render() { render() {
const store = this.createStore(); const store = this.createStore();

View File

@ -56,9 +56,10 @@ const AuthenticatedApp = () => {
}, },
]); ]);
const shouldUpdateStrapi = useMemo(() => checkLatestStrapiVersion(strapiVersion, tag_name), [ const shouldUpdateStrapi = useMemo(
tag_name, () => checkLatestStrapiVersion(strapiVersion, tag_name),
]); [tag_name]
);
useEffect(() => { useEffect(() => {
if (userRoles) { if (userRoles) {
@ -77,6 +78,16 @@ const AuthenticatedApp = () => {
const shouldShowLoader = isLoading || shouldShowNotDependentQueriesLoader; const shouldShowLoader = isLoading || shouldShowNotDependentQueriesLoader;
const appInfosValue = useMemo(() => {
return {
...appInfos,
latestStrapiReleaseTag: tag_name,
setUserDisplayName,
shouldUpdateStrapi,
userDisplayName,
};
}, [appInfos, tag_name, shouldUpdateStrapi, userDisplayName]);
if (shouldShowLoader) { if (shouldShowLoader) {
return <LoadingIndicatorPage />; return <LoadingIndicatorPage />;
} }
@ -87,15 +98,7 @@ const AuthenticatedApp = () => {
} }
return ( return (
<AppInfosContext.Provider <AppInfosContext.Provider value={appInfosValue}>
value={{
...appInfos,
latestStrapiReleaseTag: tag_name,
setUserDisplayName,
shouldUpdateStrapi,
userDisplayName,
}}
>
<RBACProvider permissions={permissions} refetchPermissions={refetch}> <RBACProvider permissions={permissions} refetchPermissions={refetch}>
<PluginsInitializer /> <PluginsInitializer />
</RBACProvider> </RBACProvider>

View File

@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import { render, waitFor } from '@testing-library/react'; import { render, waitFor } from '@testing-library/react';
import { QueryClientProvider, QueryClient } from 'react-query'; import { QueryClientProvider, QueryClient } from 'react-query';
import { useGuidedTour } from '@strapi/helper-plugin'; import { useGuidedTour } from '@strapi/helper-plugin';
@ -35,9 +36,19 @@ jest.mock('../utils/api', () => ({
fetchUserRoles: jest.fn(), fetchUserRoles: jest.fn(),
})); }));
jest.mock('../../PluginsInitializer', () => () => <div>PluginsInitializer</div>); jest.mock('../../PluginsInitializer', () => () => {
return <div>PluginsInitializer</div>;
});
// eslint-disable-next-line react/prop-types // eslint-disable-next-line react/prop-types
jest.mock('../../RBACProvider', () => ({ children }) => <div>{children}</div>); jest.mock('../../RBACProvider', () => {
const Compo = ({ children }) => <div>{children}</div>;
Compo.propTypes = {
children: PropTypes.node.isRequired,
};
return Compo;
});
const queryClient = new QueryClient({ const queryClient = new QueryClient({
defaultOptions: { defaultOptions: {
@ -47,11 +58,13 @@ const queryClient = new QueryClient({
}, },
}); });
const app = ( const configurationContextValue = { showReleaseNotification: false };
const App = () => (
<ThemeToggleProvider themes={{ light: lightTheme, dark: darkTheme }}> <ThemeToggleProvider themes={{ light: lightTheme, dark: darkTheme }}>
<Theme> <Theme>
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<ConfigurationsContext.Provider value={{ showReleaseNotification: false }}> <ConfigurationsContext.Provider value={configurationContextValue}>
<AuthenticatedApp /> <AuthenticatedApp />
</ConfigurationsContext.Provider> </ConfigurationsContext.Provider>
</QueryClientProvider> </QueryClientProvider>
@ -81,7 +94,7 @@ describe('Admin | components | AuthenticatedApp', () => {
); );
fetchCurrentUserPermissions.mockImplementation(() => Promise.resolve([])); fetchCurrentUserPermissions.mockImplementation(() => Promise.resolve([]));
const { container } = render(app); const { container } = render(<App />);
expect(container.firstChild).toMatchInlineSnapshot(` expect(container.firstChild).toMatchInlineSnapshot(`
.c0 { .c0 {
@ -147,7 +160,7 @@ describe('Admin | components | AuthenticatedApp', () => {
}); });
it('should not fetch the latest release', () => { it('should not fetch the latest release', () => {
render(app); render(<App />);
expect(fetchStrapiLatestRelease).not.toHaveBeenCalled(); expect(fetchStrapiLatestRelease).not.toHaveBeenCalled();
}); });
@ -157,7 +170,7 @@ describe('Admin | components | AuthenticatedApp', () => {
const setGuidedTourVisibility = jest.fn(); const setGuidedTourVisibility = jest.fn();
useGuidedTour.mockImplementation(() => ({ setGuidedTourVisibility })); useGuidedTour.mockImplementation(() => ({ setGuidedTourVisibility }));
render(app); render(<App />);
await waitFor(() => expect(setGuidedTourVisibility).not.toHaveBeenCalled()); await waitFor(() => expect(setGuidedTourVisibility).not.toHaveBeenCalled());
}); });
@ -166,7 +179,7 @@ describe('Admin | components | AuthenticatedApp', () => {
fetchUserRoles.mockImplementationOnce(() => [{ code: 'strapi-super-admin' }]); fetchUserRoles.mockImplementationOnce(() => [{ code: 'strapi-super-admin' }]);
const setGuidedTourVisibility = jest.fn(); const setGuidedTourVisibility = jest.fn();
useGuidedTour.mockImplementation(() => ({ setGuidedTourVisibility })); useGuidedTour.mockImplementation(() => ({ setGuidedTourVisibility }));
render(app); render(<App />);
await waitFor(() => expect(setGuidedTourVisibility).toHaveBeenCalledWith(true)); await waitFor(() => expect(setGuidedTourVisibility).toHaveBeenCalledWith(true));
}); });

View File

@ -6,7 +6,7 @@ import packageJSON from '../../../../../package.json';
const strapiVersion = packageJSON.version; const strapiVersion = packageJSON.version;
const showUpdateNotif = !JSON.parse(localStorage.getItem('STRAPI_UPDATE_NOTIF')); const showUpdateNotif = !JSON.parse(localStorage.getItem('STRAPI_UPDATE_NOTIF'));
const fetchStrapiLatestRelease = async toggleNotification => { const fetchStrapiLatestRelease = async (toggleNotification) => {
try { try {
const { const {
data: { tag_name }, data: { tag_name },

View File

@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useMemo, useRef, useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { AutoReloadOverlayBockerContext } from '@strapi/helper-plugin'; import { AutoReloadOverlayBockerContext } from '@strapi/helper-plugin';
import Blocker from './Blocker'; import Blocker from './Blocker';
@ -13,7 +13,7 @@ const AutoReloadOverlayBlockerProvider = ({ children }) => {
const lockAppWithAutoreload = (config = undefined) => { const lockAppWithAutoreload = (config = undefined) => {
setIsOpen(true); setIsOpen(true);
setConfig(config); setConfig(config);
setState(prev => ({ ...prev, start: Date.now() })); setState((prev) => ({ ...prev, start: Date.now() }));
}; };
const unlockAppWithAutoreload = () => { const unlockAppWithAutoreload = () => {
@ -36,7 +36,7 @@ const AutoReloadOverlayBlockerProvider = ({ children }) => {
return null; return null;
} }
setState(prev => ({ ...prev, elapsed: Math.round(Date.now() - prev.start) / 1000 })); setState((prev) => ({ ...prev, elapsed: Math.round(Date.now() - prev.start) / 1000 }));
return null; return null;
}, 1000); }, 1000);
@ -75,10 +75,12 @@ const AutoReloadOverlayBlockerProvider = ({ children }) => {
}; };
} }
const autoReloadValue = useMemo(() => {
return { lockApp: lockApp.current, unlockApp: unlockApp.current };
}, [lockApp, unlockApp]);
return ( return (
<AutoReloadOverlayBockerContext.Provider <AutoReloadOverlayBockerContext.Provider value={autoReloadValue}>
value={{ lockApp: lockApp.current, unlockApp: unlockApp.current }}
>
<Blocker <Blocker
displayedIcon={displayedIcon} displayedIcon={displayedIcon}
isOpen={isOpen} isOpen={isOpen}

View File

@ -1,4 +1,4 @@
import React, { useReducer, useRef } from 'react'; import React, { useMemo, useReducer, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { ConfigurationsContext } from '../../contexts'; import { ConfigurationsContext } from '../../contexts';
import reducer, { initialState } from './reducer'; import reducer, { initialState } from './reducer';
@ -23,18 +23,27 @@ const ConfigurationsProvider = ({
const updateProjectSettingsRef = useRef(updateProjectSettings); const updateProjectSettingsRef = useRef(updateProjectSettings);
const configurationValue = useMemo(() => {
return {
logos: {
menu: { custom: menuLogo, default: defaultMenuLogo },
auth: { custom: null, default: authLogo },
},
updateProjectSettings: updateProjectSettingsRef.current,
showReleaseNotification,
showTutorials,
};
}, [
authLogo,
menuLogo,
showReleaseNotification,
showTutorials,
updateProjectSettingsRef,
defaultMenuLogo,
]);
return ( return (
<ConfigurationsContext.Provider <ConfigurationsContext.Provider value={configurationValue}>
value={{
logos: {
menu: { custom: menuLogo, default: defaultMenuLogo },
auth: { custom: null, default: authLogo },
},
updateProjectSettings: updateProjectSettingsRef.current,
showReleaseNotification,
showTutorials,
}}
>
{children} {children}
</ConfigurationsContext.Provider> </ConfigurationsContext.Provider>
); );

View File

@ -12,7 +12,7 @@ const initialState = {
}; };
const reducer = (state = initialState, action) => const reducer = (state = initialState, action) =>
produce(state, draftState => { produce(state, (draftState) => {
switch (action.type) { switch (action.type) {
case 'UPDATE_PROJECT_SETTINGS': { case 'UPDATE_PROJECT_SETTINGS': {
Object.assign(draftState, action.values); Object.assign(draftState, action.values);

View File

@ -19,7 +19,7 @@ const getType = (activeSectionIndex, index) => {
}; };
const StepperHomepage = ({ sections, currentSectionKey }) => { const StepperHomepage = ({ sections, currentSectionKey }) => {
const activeSectionIndex = sections.findIndex(section => section.key === currentSectionKey); const activeSectionIndex = sections.findIndex((section) => section.key === currentSectionKey);
return ( return (
<Box> <Box>

View File

@ -31,7 +31,7 @@ const sections = [
}, },
]; ];
const App = children => ( const App = (children) => (
<ThemeProvider theme={lightTheme}> <ThemeProvider theme={lightTheme}>
<IntlProvider locale="en" messages={{}} textComponent="span"> <IntlProvider locale="en" messages={{}} textComponent="span">
{children} {children}

View File

@ -29,12 +29,12 @@ const GuidedTourHomepage = () => {
), ),
})); }));
const enrichedSections = sections.map(section => ({ const enrichedSections = sections.map((section) => ({
isDone: Object.entries(guidedTourState[section.key]).every(([, value]) => value), isDone: Object.entries(guidedTourState[section.key]).every(([, value]) => value),
...section, ...section,
})); }));
const activeSection = enrichedSections.find(section => !section.isDone)?.key; const activeSection = enrichedSections.find((section) => !section.isDone)?.key;
const handleSkip = () => { const handleSkip = () => {
setSkipped(true); setSkipped(true);

View File

@ -3,7 +3,7 @@ import { render, screen } from '@testing-library/react';
import { Router } from 'react-router-dom'; import { Router } from 'react-router-dom';
import { createMemoryHistory } from 'history'; import { createMemoryHistory } from 'history';
import { IntlProvider } from 'react-intl'; import { IntlProvider } from 'react-intl';
import { useGuidedTour, TrackingContext } from '@strapi/helper-plugin'; import { useGuidedTour, TrackingProvider } from '@strapi/helper-plugin';
import { ThemeProvider, lightTheme } from '@strapi/design-system'; import { ThemeProvider, lightTheme } from '@strapi/design-system';
import GuidedTourHomepage from '../index'; import GuidedTourHomepage from '../index';
@ -31,7 +31,7 @@ jest.mock('@strapi/helper-plugin', () => ({
const history = createMemoryHistory(); const history = createMemoryHistory();
const App = ( const App = (
<TrackingContext.Provider value={{ uuid: null, telemetryProperties: undefined }}> <TrackingProvider>
<ThemeProvider theme={lightTheme}> <ThemeProvider theme={lightTheme}>
<IntlProvider locale="en" messages={{}} textComponent="span"> <IntlProvider locale="en" messages={{}} textComponent="span">
<Router history={history}> <Router history={history}>
@ -39,7 +39,7 @@ const App = (
</Router> </Router>
</IntlProvider> </IntlProvider>
</ThemeProvider> </ThemeProvider>
</TrackingContext.Provider> </TrackingProvider>
); );
describe('GuidedTour Homepage', () => { describe('GuidedTour Homepage', () => {

View File

@ -18,7 +18,7 @@ const Content = ({ id, defaultMessage }) => {
{formatMessage( {formatMessage(
{ id, defaultMessage }, { id, defaultMessage },
{ {
documentationLink: children => ( documentationLink: (children) => (
<a <a
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -27,15 +27,15 @@ const Content = ({ id, defaultMessage }) => {
{children} {children}
</a> </a>
), ),
b: children => <Typography fontWeight="semiBold">{children}</Typography>, b: (children) => <Typography fontWeight="semiBold">{children}</Typography>,
p: children => <Typography>{children}</Typography>, p: (children) => <Typography>{children}</Typography>,
light: children => <Typography textColor="neutral600">{children}</Typography>, light: (children) => <Typography textColor="neutral600">{children}</Typography>,
ul: children => ( ul: (children) => (
<Box paddingLeft={6}> <Box paddingLeft={6}>
<ul>{children}</ul> <ul>{children}</ul>
</Box> </Box>
), ),
li: children => <LiStyled>{children}</LiStyled>, li: (children) => <LiStyled>{children}</LiStyled>,
} }
)} )}
</Stack> </Stack>

View File

@ -36,7 +36,7 @@ const Modal = ({ onClose, onSkip, children, hideSkip }) => {
spacing={8} spacing={8}
role="dialog" role="dialog"
aria-modal aria-modal
onClick={e => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
<Flex justifyContent="flex-end"> <Flex justifyContent="flex-end">
<IconButton <IconButton

View File

@ -16,10 +16,8 @@ const GuidedTourModal = () => {
setSkipped, setSkipped,
} = useGuidedTour(); } = useGuidedTour();
const [isVisible, setIsVisible] = useState(currentStep); const [isVisible, setIsVisible] = useState(currentStep);
const [ const [{ stepContent, sectionIndex, stepIndex, hasSectionAfter, hasStepAfter }, dispatch] =
{ stepContent, sectionIndex, stepIndex, hasSectionAfter, hasStepAfter }, useReducer(reducer, initialState);
dispatch,
] = useReducer(reducer, initialState);
const { trackUsage } = useTracking(); const { trackUsage } = useTracking();
useEffect(() => { useEffect(() => {

View File

@ -10,7 +10,7 @@ export const initialState = {
}; };
const reducer = (state = initialState, action) => const reducer = (state = initialState, action) =>
produce(state, draftState => { produce(state, (draftState) => {
switch (action.type) { switch (action.type) {
case 'UPDATE_MODAL': { case 'UPDATE_MODAL': {
draftState.stepContent = action.content; draftState.stepContent = action.content;

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { render, screen } from '@testing-library/react'; import { render, screen } from '@testing-library/react';
import { IntlProvider } from 'react-intl'; import { IntlProvider } from 'react-intl';
import { useGuidedTour, TrackingContext } from '@strapi/helper-plugin'; import { useGuidedTour, TrackingProvider } from '@strapi/helper-plugin';
import { lightTheme, darkTheme } from '@strapi/design-system'; import { lightTheme, darkTheme } from '@strapi/design-system';
import Theme from '../../../Theme'; import Theme from '../../../Theme';
import ThemeToggleProvider from '../../../ThemeToggleProvider'; import ThemeToggleProvider from '../../../ThemeToggleProvider';
@ -30,7 +30,7 @@ jest.mock('@strapi/helper-plugin', () => ({
})); }));
const App = ( const App = (
<TrackingContext.Provider value={{ uuid: null, telemetryProperties: undefined }}> <TrackingProvider>
<ThemeToggleProvider themes={{ light: lightTheme, dark: darkTheme }}> <ThemeToggleProvider themes={{ light: lightTheme, dark: darkTheme }}>
<Theme> <Theme>
<IntlProvider locale="en" messages={{}} defaultLocale="en" textComponent="span"> <IntlProvider locale="en" messages={{}} defaultLocale="en" textComponent="span">
@ -38,7 +38,7 @@ const App = (
</IntlProvider> </IntlProvider>
</Theme> </Theme>
</ThemeToggleProvider> </ThemeToggleProvider>
</TrackingContext.Provider> </TrackingProvider>
); );
describe('<GuidedTourModal />', () => { describe('<GuidedTourModal />', () => {

View File

@ -15,7 +15,7 @@ const GuidedTour = ({ children }) => {
init init
); );
const setCurrentStep = step => { const setCurrentStep = (step) => {
// if step is null it is intentional, we need to dispatch it // if step is null it is intentional, we need to dispatch it
if (step !== null) { if (step !== null) {
const isStepAlreadyDone = get(guidedTourState, step); const isStepAlreadyDone = get(guidedTourState, step);
@ -34,7 +34,7 @@ const GuidedTour = ({ children }) => {
}); });
}; };
const setGuidedTourVisibility = value => { const setGuidedTourVisibility = (value) => {
dispatch({ dispatch({
type: 'SET_GUIDED_TOUR_VISIBILITY', type: 'SET_GUIDED_TOUR_VISIBILITY',
value, value,
@ -51,7 +51,7 @@ const GuidedTour = ({ children }) => {
}); });
}; };
const startSection = sectionName => { const startSection = (sectionName) => {
const sectionSteps = guidedTourState[sectionName]; const sectionSteps = guidedTourState[sectionName];
if (sectionSteps) { if (sectionSteps) {
@ -67,7 +67,7 @@ const GuidedTour = ({ children }) => {
return null; return null;
}; };
const setSkipped = value => { const setSkipped = (value) => {
persistStateToLocaleStorage.setSkipped(value); persistStateToLocaleStorage.setSkipped(value);
dispatch({ dispatch({

View File

@ -5,14 +5,14 @@ import persistStateToLocaleStorage, {
SKIPPED, SKIPPED,
} from './utils/persistStateToLocaleStorage'; } from './utils/persistStateToLocaleStorage';
const init = initialState => { const init = (initialState) => {
const copyInitialState = { ...initialState }; const copyInitialState = { ...initialState };
const guidedTourLocaleStorage = persistStateToLocaleStorage.get(COMPLETED_STEPS); const guidedTourLocaleStorage = persistStateToLocaleStorage.get(COMPLETED_STEPS);
const currentStepLocaleStorage = persistStateToLocaleStorage.get(CURRENT_STEP); const currentStepLocaleStorage = persistStateToLocaleStorage.get(CURRENT_STEP);
const skippedLocaleStorage = persistStateToLocaleStorage.get(SKIPPED); const skippedLocaleStorage = persistStateToLocaleStorage.get(SKIPPED);
if (guidedTourLocaleStorage) { if (guidedTourLocaleStorage) {
guidedTourLocaleStorage.forEach(step => { guidedTourLocaleStorage.forEach((step) => {
const [sectionName, stepName] = step.split('.'); const [sectionName, stepName] = step.split('.');
set(copyInitialState, ['guidedTourState', sectionName, stepName], true); set(copyInitialState, ['guidedTourState', sectionName, stepName], true);
}); });

View File

@ -22,7 +22,7 @@ export const initialState = {
}; };
const reducer = (state = initialState, action) => const reducer = (state = initialState, action) =>
produce(state, draftState => { produce(state, (draftState) => {
switch (action.type) { switch (action.type) {
case 'SET_CURRENT_STEP': { case 'SET_CURRENT_STEP': {
draftState.currentStep = action.step; draftState.currentStep = action.step;

View File

@ -1,4 +1,4 @@
const isGuidedTourCompleted = guidedTourState => const isGuidedTourCompleted = (guidedTourState) =>
Object.entries(guidedTourState).every(([, section]) => Object.entries(guidedTourState).every(([, section]) =>
Object.entries(section).every(([, step]) => step) Object.entries(section).every(([, step]) => step)
); );

View File

@ -9,7 +9,7 @@ const persistStateToLocaleStorage = {
localStorage.removeItem(CURRENT_STEP); localStorage.removeItem(CURRENT_STEP);
localStorage.removeItem(COMPLETED_STEPS); localStorage.removeItem(COMPLETED_STEPS);
}, },
addCompletedStep: completedStep => { addCompletedStep(completedStep) {
const currentSteps = parse(localStorage.getItem(COMPLETED_STEPS))?.slice() || []; const currentSteps = parse(localStorage.getItem(COMPLETED_STEPS))?.slice() || [];
const isAlreadyStored = currentSteps.includes(completedStep); const isAlreadyStored = currentSteps.includes(completedStep);
@ -20,13 +20,13 @@ const persistStateToLocaleStorage = {
currentSteps.push(completedStep); currentSteps.push(completedStep);
localStorage.setItem(COMPLETED_STEPS, stringify(currentSteps)); localStorage.setItem(COMPLETED_STEPS, stringify(currentSteps));
}, },
addCurrentStep: currentStep => { addCurrentStep(currentStep) {
localStorage.setItem(CURRENT_STEP, stringify(currentStep)); localStorage.setItem(CURRENT_STEP, stringify(currentStep));
}, },
setSkipped: value => { setSkipped(value) {
localStorage.setItem(SKIPPED, stringify(value)); localStorage.setItem(SKIPPED, stringify(value));
}, },
get: item => { get(item) {
return parse(localStorage.getItem(item)); return parse(localStorage.getItem(item));
}, },
}; };

View File

@ -23,7 +23,7 @@ const LanguageProvider = ({ children, localeNames, messages }) => {
window.localStorage.setItem(localStorageKey, locale); window.localStorage.setItem(localStorageKey, locale);
}, [locale]); }, [locale]);
const changeLocale = locale => { const changeLocale = (locale) => {
dispatch({ dispatch({
type: 'CHANGE_LOCALE', type: 'CHANGE_LOCALE',
locale, locale,

View File

@ -1,6 +1,6 @@
import localStorageKey from './utils/localStorageKey'; import localStorageKey from './utils/localStorageKey';
const init = localeNames => { const init = (localeNames) => {
const languageFromLocaleStorage = window.localStorage.getItem(localStorageKey); const languageFromLocaleStorage = window.localStorage.getItem(localStorageKey);
const appLanguage = localeNames[languageFromLocaleStorage] ? languageFromLocaleStorage : 'en'; const appLanguage = localeNames[languageFromLocaleStorage] ? languageFromLocaleStorage : 'en';

View File

@ -62,18 +62,18 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
const initials = userDisplayName const initials = userDisplayName
.split(' ') .split(' ')
.map(name => name.substring(0, 1)) .map((name) => name.substring(0, 1))
.join('') .join('')
.substring(0, 2); .substring(0, 2);
const handleToggleUserLinks = () => setUserLinksVisible(prev => !prev); const handleToggleUserLinks = () => setUserLinksVisible((prev) => !prev);
const handleLogout = () => { const handleLogout = () => {
auth.clearAppStorage(); auth.clearAppStorage();
handleToggleUserLinks(); handleToggleUserLinks();
}; };
const handleBlur = e => { const handleBlur = (e) => {
if ( if (
!e.currentTarget.contains(e.relatedTarget) && !e.currentTarget.contains(e.relatedTarget) &&
e.relatedTarget?.parentElement?.id !== 'main-nav-user-button' e.relatedTarget?.parentElement?.id !== 'main-nav-user-button'
@ -121,7 +121,7 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
defaultMessage: 'Plugins', defaultMessage: 'Plugins',
})} })}
> >
{pluginsSectionLinks.map(link => { {pluginsSectionLinks.map((link) => {
const Icon = link.icon; const Icon = link.icon;
return ( return (
@ -140,7 +140,7 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
defaultMessage: 'General', defaultMessage: 'General',
})} })}
> >
{generalSectionLinks.map(link => { {generalSectionLinks.map((link) => {
const LinkIcon = link.icon; const LinkIcon = link.icon;
return ( return (
@ -201,7 +201,7 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
</LinkUserWrapper> </LinkUserWrapper>
)} )}
<NavCondense onClick={() => setCondensed(s => !s)}> <NavCondense onClick={() => setCondensed((s) => !s)}>
{condensed {condensed
? formatMessage({ ? formatMessage({
id: 'app.components.LeftMenu.expand', id: 'app.components.LeftMenu.expand',

View File

@ -8,7 +8,8 @@ const Notification = ({ dispatch, notification }) => {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const { message, link, type, id, onClose, timeout, blockTransition } = notification; const { message, link, type, id, onClose, timeout, blockTransition } = notification;
const formattedMessage = msg => (typeof msg === 'string' ? msg : formatMessage(msg, msg.values)); const formattedMessage = (msg) =>
typeof msg === 'string' ? msg : formatMessage(msg, msg.values);
const handleClose = useCallback(() => { const handleClose = useCallback(() => {
if (onClose) { if (onClose) {
onClose(); onClose();
@ -66,9 +67,7 @@ const Notification = ({ dispatch, notification }) => {
defaultMessage: link.label?.defaultMessage || link.label?.id || link.label, defaultMessage: link.label?.defaultMessage || link.label?.id || link.label,
})} })}
</Link> </Link>
) : ( ) : undefined
undefined
)
} }
onClose={handleClose} onClose={handleClose}
closeLabel="Close" closeLabel="Close"

View File

@ -8,7 +8,7 @@ import reducer, { initialState } from './reducer';
const Notifications = ({ children }) => { const Notifications = ({ children }) => {
const [{ notifications }, dispatch] = useReducer(reducer, initialState); const [{ notifications }, dispatch] = useReducer(reducer, initialState);
const displayNotification = config => { const displayNotification = (config) => {
dispatch({ dispatch({
type: 'SHOW_NOTIFICATION', type: 'SHOW_NOTIFICATION',
config, config,
@ -26,7 +26,7 @@ const Notifications = ({ children }) => {
width={`${500 / 16}rem`} width={`${500 / 16}rem`}
zIndex={10} zIndex={10}
> >
{notifications.map(notification => { {notifications.map((notification) => {
return ( return (
<Notification key={notification.id} dispatch={dispatch} notification={notification} /> <Notification key={notification.id} dispatch={dispatch} notification={notification} />
); );

View File

@ -8,7 +8,7 @@ const initialState = {
const notificationReducer = (state = initialState, action) => const notificationReducer = (state = initialState, action) =>
// eslint-disable-next-line consistent-return // eslint-disable-next-line consistent-return
produce(state, draftState => { produce(state, (draftState) => {
switch (action.type) { switch (action.type) {
case 'SHOW_NOTIFICATION': { case 'SHOW_NOTIFICATION': {
draftState.notifications.push({ draftState.notifications.push({
@ -28,7 +28,7 @@ const notificationReducer = (state = initialState, action) =>
break; break;
} }
case 'HIDE_NOTIFICATION': { case 'HIDE_NOTIFICATION': {
const indexToRemove = state.notifications.findIndex(notif => notif.id === action.id); const indexToRemove = state.notifications.findIndex((notif) => notif.id === action.id);
if (indexToRemove !== -1) { if (indexToRemove !== -1) {
draftState.notifications.splice(indexToRemove, 1); draftState.notifications.splice(indexToRemove, 1);

View File

@ -108,7 +108,9 @@ describe('<Notifications />', () => {
expect(items).toHaveLength(1); expect(items).toHaveLength(1);
await act(async () => { await act(async () => {
await new Promise(resolve => setTimeout(resolve, 2500)); await new Promise((resolve) => {
setTimeout(resolve, 2500);
});
}); });
const foundItems = screen.queryAllByText(/simple notif/); const foundItems = screen.queryAllByText(/simple notif/);
@ -151,7 +153,9 @@ describe('<Notifications />', () => {
expect(items).toHaveLength(1); expect(items).toHaveLength(1);
await act(async () => { await act(async () => {
await new Promise(resolve => setTimeout(resolve, 2500)); await new Promise((resolve) => {
setTimeout(resolve, 2500);
});
}); });
const foundItems = screen.queryAllByText(/simple notif/); const foundItems = screen.queryAllByText(/simple notif/);

View File

@ -8,11 +8,13 @@ import reducer, { initialState } from './reducer';
const PluginsInitializer = () => { const PluginsInitializer = () => {
const { plugins: appPlugins } = useStrapiApp(); const { plugins: appPlugins } = useStrapiApp();
const [{ plugins }, dispatch] = useReducer(reducer, initialState, () => init(appPlugins)); const [{ plugins }, dispatch] = useReducer(reducer, initialState, () => init(appPlugins));
const setPlugin = useRef(pluginId => { const setPlugin = useRef((pluginId) => {
dispatch({ type: 'SET_PLUGIN_READY', pluginId }); dispatch({ type: 'SET_PLUGIN_READY', pluginId });
}); });
const hasApluginNotReady = Object.keys(plugins).some(plugin => plugins[plugin].isReady === false); const hasApluginNotReady = Object.keys(plugins).some(
(plugin) => plugins[plugin].isReady === false
);
if (hasApluginNotReady) { if (hasApluginNotReady) {
const initializers = Object.keys(plugins).reduce((acc, current) => { const initializers = Object.keys(plugins).reduce((acc, current) => {

View File

@ -1,4 +1,4 @@
const init = plugins => { const init = (plugins) => {
return { return {
plugins: Object.keys(plugins).reduce((acc, current) => { plugins: Object.keys(plugins).reduce((acc, current) => {
acc[current] = { ...plugins[current] }; acc[current] = { ...plugins[current] };

View File

@ -7,7 +7,7 @@ const initialState = {
const reducer = (state = initialState, action) => const reducer = (state = initialState, action) =>
/* eslint-disable-next-line consistent-return */ /* eslint-disable-next-line consistent-return */
produce(state, draftState => { produce(state, (draftState) => {
switch (action.type) { switch (action.type) {
case 'SET_PLUGIN_READY': { case 'SET_PLUGIN_READY': {
set(draftState, ['plugins', action.pluginId, 'isReady'], true); set(draftState, ['plugins', action.pluginId, 'isReady'], true);

View File

@ -3,7 +3,9 @@ import { StrapiAppProvider } from '@strapi/helper-plugin';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import PluginsInitializer from '../index'; import PluginsInitializer from '../index';
jest.mock('../../../pages/Admin', () => () => <div>ADMIN</div>); jest.mock('../../../pages/Admin', () => () => {
return <div>ADMIN</div>;
});
describe('ADMIN | COMPONENTS | PluginsInitializer', () => { describe('ADMIN | COMPONENTS | PluginsInitializer', () => {
it('should not crash', () => { it('should not crash', () => {

View File

@ -20,7 +20,7 @@ const PrivateRoute = ({ component: Component, path, ...rest }) => {
return ( return (
<Route <Route
path={path} path={path}
render={props => render={(props) =>
auth.getToken() !== null ? ( auth.getToken() !== null ? (
<Component {...rest} {...props} /> <Component {...rest} {...props} />
) : ( ) : (

View File

@ -2,7 +2,7 @@ import { RESET_STORE, SET_PERMISSIONS } from './constants';
const resetStore = () => ({ type: RESET_STORE }); const resetStore = () => ({ type: RESET_STORE });
const setPermissions = permissions => ({ const setPermissions = (permissions) => ({
type: SET_PERMISSIONS, type: SET_PERMISSIONS,
permissions, permissions,
}); });

View File

@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
import { resetStore, setPermissions } from './actions'; import { resetStore, setPermissions } from './actions';
const RBACProvider = ({ children, permissions, refetchPermissions }) => { const RBACProvider = ({ children, permissions, refetchPermissions }) => {
const { allPermissions } = useSelector(state => state.rbacProvider); const { allPermissions } = useSelector((state) => state.rbacProvider);
const dispatch = useDispatch(); const dispatch = useDispatch();

View File

@ -18,12 +18,12 @@ const initialState = {
const reducer = (state = initialState, action) => const reducer = (state = initialState, action) =>
// eslint-disable-next-line consistent-return // eslint-disable-next-line consistent-return
produce(state, draftState => { produce(state, (draftState) => {
switch (action.type) { switch (action.type) {
case SET_PERMISSIONS: { case SET_PERMISSIONS: {
draftState.allPermissions = action.permissions; draftState.allPermissions = action.permissions;
draftState.collectionTypesRelatedPermissions = action.permissions draftState.collectionTypesRelatedPermissions = action.permissions
.filter(perm => perm.subject) .filter((perm) => perm.subject)
.reduce((acc, current) => { .reduce((acc, current) => {
const { subject, action } = current; const { subject, action } = current;

View File

@ -1,4 +1,4 @@
import { permissions } from '../../../../../../../admin-test-utils/lib/fixtures'; import { permissions } from '@strapi/admin-test-utils/lib/fixtures';
import { setPermissions, resetStore } from '../actions'; import { setPermissions, resetStore } from '../actions';
import rbacProviderReducer, { initialState } from '../reducer'; import rbacProviderReducer, { initialState } from '../reducer';

View File

@ -20,7 +20,7 @@ const getDefaultTheme = () => {
const ThemeToggleProvider = ({ children, themes }) => { const ThemeToggleProvider = ({ children, themes }) => {
const [currentTheme, setCurrentTheme] = useState(getDefaultTheme()); const [currentTheme, setCurrentTheme] = useState(getDefaultTheme());
const handleChangeTheme = nextTheme => { const handleChangeTheme = (nextTheme) => {
setCurrentTheme(nextTheme); setCurrentTheme(nextTheme);
localStorage.setItem(THEME_KEY, nextTheme); localStorage.setItem(THEME_KEY, nextTheme);
}; };

View File

@ -73,7 +73,7 @@ const UpgradePlanModal = ({ onClose, isOpen }) => {
<UpgradeWrapper onClick={onClose}> <UpgradeWrapper onClick={onClose}>
<FocusTrap onEscape={onClose}> <FocusTrap onEscape={onClose}>
<UpgradeContainer <UpgradeContainer
onClick={e => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
aria-labelledby="upgrade-plan" aria-labelledby="upgrade-plan"
background="neutral0" background="neutral0"
hasRadius hasRadius

View File

@ -16,7 +16,7 @@ const Filters = ({ displayedFilters }) => {
if (!isVisible) { if (!isVisible) {
trackUsage('willFilterEntries'); trackUsage('willFilterEntries');
} }
setIsVisible(prev => !prev); setIsVisible((prev) => !prev);
}; };
return ( return (

View File

@ -17,7 +17,7 @@ const useAllowedAttributes = (contentType, slug) => {
const readPermissionForAttr = get(readPermissionsForSlug, ['0', 'properties', 'fields'], []); const readPermissionForAttr = get(readPermissionsForSlug, ['0', 'properties', 'fields'], []);
const attributesArray = Object.keys(get(contentType, ['attributes']), {}); const attributesArray = Object.keys(get(contentType, ['attributes']), {});
const allowedAttributes = attributesArray const allowedAttributes = attributesArray
.filter(attr => { .filter((attr) => {
const current = get(contentType, ['attributes', attr], {}); const current = get(contentType, ['attributes', attr], {});
if (!current.type) { if (!current.type) {

View File

@ -1,11 +1,13 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useIntl } from 'react-intl';
import useAllowedAttributes from './hooks/useAllowedAttributes'; import useAllowedAttributes from './hooks/useAllowedAttributes';
import Filters from './Filters'; import Filters from './Filters';
const AttributeFilter = ({ contentType, slug, metadatas }) => { const AttributeFilter = ({ contentType, slug, metadatas }) => {
const { formatMessage } = useIntl();
const allowedAttributes = useAllowedAttributes(contentType, slug); const allowedAttributes = useAllowedAttributes(contentType, slug);
const displayedFilters = allowedAttributes.map(name => { const displayedFilters = allowedAttributes.map((name) => {
const attribute = contentType.attributes[name]; const attribute = contentType.attributes[name];
const { type, enum: options } = attribute; const { type, enum: options } = attribute;
@ -18,7 +20,7 @@ const AttributeFilter = ({ contentType, slug, metadatas }) => {
return { return {
name, name,
metadatas: { label }, metadatas: { label: formatMessage({ id: label, defaultMessage: label }) },
fieldSchema: { type, options, mainField }, fieldSchema: { type, options, mainField },
trackedEvent, trackedEvent,
}; };

View File

@ -8,7 +8,7 @@ import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { Wrapper, Sub } from './components'; import { Wrapper, Sub } from './components';
const renderMsg = msg => <p>{msg}</p>; const renderMsg = (msg) => <p>{msg}</p>;
const Block = ({ children, description, style, title }) => ( const Block = ({ children, description, style, title }) => (
<div className="col-md-12"> <div className="col-md-12">

View File

@ -40,13 +40,8 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
const { push, replace } = useHistory(); const { push, replace } = useHistory();
const [{ rawQuery }] = useQueryParams(); const [{ rawQuery }] = useQueryParams();
const dispatch = useDispatch(); const dispatch = useDispatch();
const { const { componentsDataStructure, contentTypeDataStructure, data, isLoading, status } =
componentsDataStructure, useSelector(selectCrudReducer);
contentTypeDataStructure,
data,
isLoading,
status,
} = useSelector(selectCrudReducer);
const redirectionLink = useFindRedirectionLink(slug); const redirectionLink = useFindRedirectionLink(slug);
const isMounted = useRef(true); const isMounted = useRef(true);
@ -65,7 +60,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
}, [slug, id, isCreatingEntry, origin]); }, [slug, id, isCreatingEntry, origin]);
const cleanClonedData = useCallback( const cleanClonedData = useCallback(
data => { (data) => {
if (!origin) { if (!origin) {
return data; return data;
} }
@ -81,7 +76,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
[origin] [origin]
); );
const cleanReceivedData = useCallback(data => { const cleanReceivedData = useCallback((data) => {
const cleaned = removePasswordFieldsFromData( const cleaned = removePasswordFieldsFromData(
data, data,
allLayoutDataRef.current.contentType, allLayoutDataRef.current.contentType,
@ -136,7 +131,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
const CancelToken = axios.CancelToken; const CancelToken = axios.CancelToken;
const source = CancelToken.source(); const source = CancelToken.source();
const fetchData = async source => { const fetchData = async (source) => {
dispatch(getData()); dispatch(getData());
try { try {
@ -201,7 +196,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
]); ]);
const displayErrors = useCallback( const displayErrors = useCallback(
err => { (err) => {
const errorPayload = err.response.data; const errorPayload = err.response.data;
let errorMessage = get(errorPayload, ['error', 'message'], 'Bad Request'); let errorMessage = get(errorPayload, ['error', 'message'], 'Bad Request');
@ -218,7 +213,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
); );
const onDelete = useCallback( const onDelete = useCallback(
async trackerProperty => { async (trackerProperty) => {
try { try {
trackUsageRef.current('willDeleteEntry', trackerProperty); trackUsageRef.current('willDeleteEntry', trackerProperty);

View File

@ -36,7 +36,7 @@ function getItemStyles(initialOffset, currentOffset, mouseOffset) {
const CustomDragLayer = () => { const CustomDragLayer = () => {
const { itemType, isDragging, item, initialOffset, currentOffset, mouseOffset } = useDragLayer( const { itemType, isDragging, item, initialOffset, currentOffset, mouseOffset } = useDragLayer(
monitor => ({ (monitor) => ({
item: monitor.getItem(), item: monitor.getItem(),
itemType: monitor.getItemType(), itemType: monitor.getItemType(),
initialOffset: monitor.getInitialSourceClientOffset(), initialOffset: monitor.getInitialSourceClientOffset(),

View File

@ -85,7 +85,7 @@ const RelationMultiple = ({ fieldSchema, metadatas, queryInfos, name, rowId, val
{status === 'success' && ( {status === 'success' && (
<> <>
{data?.results.map(entry => ( {data?.results.map((entry) => (
<MenuItem key={entry.id} aria-disabled> <MenuItem key={entry.id} aria-disabled>
<TypographyMaxWidth ellipsis> <TypographyMaxWidth ellipsis>
<CellValue <CellValue

View File

@ -36,7 +36,7 @@ const RepeatableComponentCell = ({ value, metadatas }) => {
return ( return (
<Box {...stopPropagation}> <Box {...stopPropagation}>
<SimpleMenu label={Label} size="S"> <SimpleMenu label={Label} size="S">
{value.map(item => ( {value.map((item) => (
<MenuItem key={item.id} aria-disabled> <MenuItem key={item.id} aria-disabled>
<TypographyMaxWidth ellipsis> <TypographyMaxWidth ellipsis>
<CellValue type={mainFieldType} value={item[mainFieldName] || item.id} /> <CellValue type={mainFieldType} value={item[mainFieldName] || item.id} />

View File

@ -1,7 +1,7 @@
import isSingleRelation from '../isSingleRelation'; import isSingleRelation from '../isSingleRelation';
describe('isSingleRelation', () => { describe('isSingleRelation', () => {
['oneToOne', 'manyToOne', 'oneToOneMorph'].forEach(type => { ['oneToOne', 'manyToOne', 'oneToOneMorph'].forEach((type) => {
test(`is single relation: ${type}`, () => { test(`is single relation: ${type}`, () => {
expect(isSingleRelation(type)).toBeTruthy(); expect(isSingleRelation(type)).toBeTruthy();
}); });

View File

@ -38,7 +38,7 @@ const TableRows = ({
return ( return (
<Tbody> <Tbody>
{rows.map((data, index) => { {rows.map((data, index) => {
const isChecked = entriesToDelete.findIndex(id => id === data.id) !== -1; const isChecked = entriesToDelete.findIndex((id) => id === data.id) !== -1;
const itemLineText = formatMessage( const itemLineText = formatMessage(
{ {
id: 'content-manager.components.DynamicTable.row-line', id: 'content-manager.components.DynamicTable.row-line',
@ -51,7 +51,7 @@ const TableRows = ({
<Tr <Tr
key={data.id} key={data.id}
{...onRowClick({ {...onRowClick({
fn: () => { fn() {
trackUsage('willEditEntryFromList'); trackUsage('willEditEntryFromList');
push({ push({
pathname: `${pathname}/${data.id}`, pathname: `${pathname}/${data.id}`,
@ -170,8 +170,8 @@ TableRows.defaultProps = {
canCreate: false, canCreate: false,
canDelete: false, canDelete: false,
entriesToDelete: [], entriesToDelete: [],
onClickDelete: () => {}, onClickDelete() {},
onSelectRow: () => {}, onSelectRow() {},
rows: [], rows: [],
withBulkActions: false, withBulkActions: false,
withMainAction: false, withMainAction: false,

View File

@ -35,7 +35,7 @@ const DynamicTable = ({
layout, layout,
}); });
const formattedHeaders = headers.displayedHeaders.map(header => { const formattedHeaders = headers.displayedHeaders.map((header) => {
if (header.fieldSchema.type === 'relation') { if (header.fieldSchema.type === 'relation') {
const sortFieldValue = `${header.name}.${header.metadatas.mainField.name}`; const sortFieldValue = `${header.name}.${header.metadatas.mainField.name}`;
@ -65,7 +65,7 @@ const DynamicTable = ({
searchable: false, searchable: false,
sortable: true, sortable: true,
}, },
cellFormatter: cellData => { cellFormatter(cellData) {
const isPublished = !isEmpty(cellData.publishedAt); const isPublished = !isEmpty(cellData.publishedAt);
return <State isPublished={isPublished} />; return <State isPublished={isPublished} />;

View File

@ -120,26 +120,24 @@ const AddComponentButton = ({
} }
return ( return (
<> <Flex justifyContent="center">
<Flex justifyContent="center"> <Box style={{ cursor: isDisabled ? 'not-allowed' : 'pointer' }}>
<Box style={{ cursor: isDisabled ? 'not-allowed' : 'pointer' }}> <StyledButton type="button" onClick={onClick} disabled={isDisabled} hasError={hasError}>
<StyledButton type="button" onClick={onClick} disabled={isDisabled} hasError={hasError}> <Flex>
<Flex> <BoxFullHeight aria-hidden paddingRight={2}>
<BoxFullHeight aria-hidden paddingRight={2}> <StyledAddIcon $isOpen={isOpen} $hasError={hasError && !isOpen} />
<StyledAddIcon $isOpen={isOpen} $hasError={hasError && !isOpen} /> </BoxFullHeight>
</BoxFullHeight> <Typography
<Typography variant="pi"
variant="pi" fontWeight="bold"
fontWeight="bold" textColor={hasError && !isOpen ? 'danger600' : 'neutral500'}
textColor={hasError && !isOpen ? 'danger600' : 'neutral500'} >
> {buttonLabel}
{buttonLabel} </Typography>
</Typography> </Flex>
</Flex> </StyledButton>
</StyledButton> </Box>
</Box> </Flex>
</Flex>
</>
); );
}; };

View File

@ -88,7 +88,7 @@ const Component = ({
const formErrorsKeys = Object.keys(formErrors); const formErrorsKeys = Object.keys(formErrors);
const fieldsErrors = formErrorsKeys.filter(errorKey => { const fieldsErrors = formErrorsKeys.filter((errorKey) => {
const errorKeysArray = errorKey.split('.'); const errorKeysArray = errorKey.split('.');
if (`${errorKeysArray[0]}.${errorKeysArray[1]}` === `${name}.${index}`) { if (`${errorKeysArray[0]}.${errorKeysArray[1]}` === `${name}.${index}`) {

View File

@ -75,7 +75,7 @@ function ComponentCard({ componentUid, intlLabel, icon, onClick }) {
ComponentCard.defaultProps = { ComponentCard.defaultProps = {
icon: 'dice-d6', icon: 'dice-d6',
onClick: () => {}, onClick() {},
}; };
ComponentCard.propTypes = { ComponentCard.propTypes = {

View File

@ -16,7 +16,7 @@ const ComponentPicker = ({ components, isOpen, onClickAddComponent }) => {
const [categoryToOpen, setCategoryToOpen] = useState(''); const [categoryToOpen, setCategoryToOpen] = useState('');
const dynamicComponentCategories = useMemo(() => { const dynamicComponentCategories = useMemo(() => {
const componentsWithInfo = components.map(componentUid => { const componentsWithInfo = components.map((componentUid) => {
const { category, info } = getComponentLayout(componentUid); const { category, info } = getComponentLayout(componentUid);
return { componentUid, category, info }; return { componentUid, category, info };
@ -38,7 +38,7 @@ const ComponentPicker = ({ components, isOpen, onClickAddComponent }) => {
}, [isOpen, dynamicComponentCategories]); }, [isOpen, dynamicComponentCategories]);
const handleAddComponentToDz = useCallback( const handleAddComponentToDz = useCallback(
componentUid => { (componentUid) => {
onClickAddComponent(componentUid); onClickAddComponent(componentUid);
setCategoryToOpen(''); setCategoryToOpen('');
}, },
@ -46,7 +46,7 @@ const ComponentPicker = ({ components, isOpen, onClickAddComponent }) => {
); );
const handleClickToggle = useCallback( const handleClickToggle = useCallback(
categoryName => { (categoryName) => {
const nextCategoryToOpen = categoryToOpen === categoryName ? '' : categoryName; const nextCategoryToOpen = categoryToOpen === categoryName ? '' : categoryName;
setCategoryToOpen(nextCategoryToOpen); setCategoryToOpen(nextCategoryToOpen);

View File

@ -16,7 +16,7 @@ import ComponentPicker from './components/ComponentPicker';
/* eslint-disable react/no-array-index-key */ /* eslint-disable react/no-array-index-key */
const createCollapses = arrayLength => const createCollapses = (arrayLength) =>
Array.from({ length: arrayLength }).map(() => ({ isOpen: false })); Array.from({ length: arrayLength }).map(() => ({ isOpen: false }));
const DynamicZone = ({ const DynamicZone = ({
@ -53,7 +53,7 @@ const DynamicZone = ({
useEffect(() => { useEffect(() => {
if (shouldOpenAddedComponent) { if (shouldOpenAddedComponent) {
setComponentsCollapses(prev => setComponentsCollapses((prev) =>
prev.map((collapse, index) => { prev.map((collapse, index) => {
if (index === prev.length - 1) { if (index === prev.length - 1) {
return { ...collapse, isOpen: true }; return { ...collapse, isOpen: true };
@ -71,10 +71,10 @@ const DynamicZone = ({
const { max = Infinity, min = -Infinity } = fieldSchema; const { max = Infinity, min = -Infinity } = fieldSchema;
const dynamicZoneErrors = useMemo(() => { const dynamicZoneErrors = useMemo(() => {
return Object.keys(formErrors) return Object.keys(formErrors)
.filter(key => { .filter((key) => {
return key === name; return key === name;
}) })
.map(key => formErrors[key]); .map((key) => formErrors[key]);
}, [formErrors, name]); }, [formErrors, name]);
const dynamicZoneAvailableComponents = useMemo(() => fieldSchema.components || [], [fieldSchema]); const dynamicZoneAvailableComponents = useMemo(() => fieldSchema.components || [], [fieldSchema]);
@ -89,7 +89,7 @@ const DynamicZone = ({
hasError && get(dynamicZoneErrors, [0, 'id'], '') === 'components.Input.error.validation.max'; hasError && get(dynamicZoneErrors, [0, 'id'], '') === 'components.Input.error.validation.max';
const handleAddComponent = useCallback( const handleAddComponent = useCallback(
componentUid => { (componentUid) => {
setIsOpen(false); setIsOpen(false);
addComponentToDynamicZone(name, componentUid, hasError); addComponentToDynamicZone(name, componentUid, hasError);
@ -100,7 +100,7 @@ const DynamicZone = ({
const handleClickOpenPicker = () => { const handleClickOpenPicker = () => {
if (dynamicDisplayedComponentsLength < max) { if (dynamicDisplayedComponentsLength < max) {
setIsOpen(prev => !prev); setIsOpen((prev) => !prev);
} else { } else {
toggleNotification({ toggleNotification({
type: 'info', type: 'info',
@ -109,8 +109,8 @@ const DynamicZone = ({
} }
}; };
const handleToggleComponent = indexToToggle => { const handleToggleComponent = (indexToToggle) => {
setComponentsCollapses(prev => setComponentsCollapses((prev) =>
prev.map(({ isOpen }, index) => { prev.map(({ isOpen }, index) => {
if (index === indexToToggle) { if (index === indexToToggle) {
return { isOpen: !isOpen }; return { isOpen: !isOpen };
@ -123,7 +123,7 @@ const DynamicZone = ({
const handleMoveComponentDown = (name, currentIndex) => { const handleMoveComponentDown = (name, currentIndex) => {
moveComponentDown(name, currentIndex); moveComponentDown(name, currentIndex);
setComponentsCollapses(prev => { setComponentsCollapses((prev) => {
return prev.map(({ isOpen }, index, refArray) => { return prev.map(({ isOpen }, index, refArray) => {
if (index === currentIndex + 1) { if (index === currentIndex + 1) {
return { isOpen: refArray[currentIndex].isOpen }; return { isOpen: refArray[currentIndex].isOpen };
@ -140,7 +140,7 @@ const DynamicZone = ({
const handleMoveComponentUp = (name, currentIndex) => { const handleMoveComponentUp = (name, currentIndex) => {
moveComponentUp(name, currentIndex); moveComponentUp(name, currentIndex);
setComponentsCollapses(prev => { setComponentsCollapses((prev) => {
return prev.map(({ isOpen }, index, refArray) => { return prev.map(({ isOpen }, index, refArray) => {
if (index === currentIndex - 1) { if (index === currentIndex - 1) {
return { isOpen: refArray[currentIndex].isOpen }; return { isOpen: refArray[currentIndex].isOpen };

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
function connect(WrappedComponent, select) { function connect(WrappedComponent, select) {
return function(props) { return (props) => {
// eslint-disable-next-line react/prop-types // eslint-disable-next-line react/prop-types
const selectors = select(props.name); const selectors = select(props.name);

View File

@ -17,7 +17,7 @@ function useSelect(name) {
} = useCMEditViewDataManager(); } = useCMEditViewDataManager();
const dynamicDisplayedComponents = useMemo( const dynamicDisplayedComponents = useMemo(
() => get(modifiedData, [name], []).map(data => data.__component), () => get(modifiedData, [name], []).map((data) => data.__component),
[modifiedData, name] [modifiedData, name]
); );

View File

@ -264,7 +264,7 @@ const EditViewDataManagerProvider = ({
); );
const createFormData = useCallback( const createFormData = useCallback(
data => { (data) => {
// First we need to remove the added keys needed for the dnd // First we need to remove the added keys needed for the dnd
const preparedData = removeKeyInObject(cloneDeep(data), '__temp_key__'); const preparedData = removeKeyInObject(cloneDeep(data), '__temp_key__');
// Then we need to apply our helper // Then we need to apply our helper
@ -288,7 +288,7 @@ const EditViewDataManagerProvider = ({
}, [hasDraftAndPublish, shouldNotRunValidations]); }, [hasDraftAndPublish, shouldNotRunValidations]);
const handleSubmit = useCallback( const handleSubmit = useCallback(
async e => { async (e) => {
e.preventDefault(); e.preventDefault();
let errors = {}; let errors = {};
@ -358,8 +358,8 @@ const EditViewDataManagerProvider = ({
}, [allLayoutData, currentContentTypeLayout, isCreatingEntry, modifiedData, onPublish]); }, [allLayoutData, currentContentTypeLayout, isCreatingEntry, modifiedData, onPublish]);
const shouldCheckDZErrors = useCallback( const shouldCheckDZErrors = useCallback(
dzName => { (dzName) => {
const doesDZHaveError = Object.keys(formErrors).some(key => key.split('.')[0] === dzName); const doesDZHaveError = Object.keys(formErrors).some((key) => key.split('.')[0] === dzName);
const shouldCheckErrors = !isEmpty(formErrors) && doesDZHaveError; const shouldCheckErrors = !isEmpty(formErrors) && doesDZHaveError;
return shouldCheckErrors; return shouldCheckErrors;
@ -517,7 +517,7 @@ const EditViewDataManagerProvider = ({
EditViewDataManagerProvider.defaultProps = { EditViewDataManagerProvider.defaultProps = {
from: '/', from: '/',
initialValues: null, initialValues: null,
redirectToPreviousPage: () => {}, redirectToPreviousPage() {},
}; };
EditViewDataManagerProvider.propTypes = { EditViewDataManagerProvider.propTypes = {

View File

@ -18,7 +18,7 @@ const initialState = {
const reducer = (state, action) => const reducer = (state, action) =>
// eslint-disable-next-line consistent-return // eslint-disable-next-line consistent-return
produce(state, draftState => { produce(state, (draftState) => {
switch (action.type) { switch (action.type) {
case 'ADD_NON_REPEATABLE_COMPONENT_TO_FIELD': { case 'ADD_NON_REPEATABLE_COMPONENT_TO_FIELD': {
set( set(

Some files were not shown because too many files have changed in this diff Show More