mirror of
https://github.com/strapi/strapi.git
synced 2025-11-10 07:10:11 +00:00
Merge pull request #10907 from strapi/ds-migration/roles-edit-input-block
[v4] Edit users-permissions roles' name and description
This commit is contained in:
commit
cad6e45ea5
@ -1,13 +1,17 @@
|
|||||||
import { useCallback, useReducer, useEffect } from 'react';
|
import { useCallback, useReducer, useEffect, useRef } from 'react';
|
||||||
import { request, useNotification } from '@strapi/helper-plugin';
|
import { useNotification } from '@strapi/helper-plugin';
|
||||||
import reducer, { initialState } from './reducer';
|
import reducer, { initialState } from './reducer';
|
||||||
|
import axiosIntance from '../../utils/axiosInstance';
|
||||||
import pluginId from '../../pluginId';
|
import pluginId from '../../pluginId';
|
||||||
|
|
||||||
const useFetchRole = id => {
|
const useFetchRole = id => {
|
||||||
const [state, dispatch] = useReducer(reducer, initialState);
|
const [state, dispatch] = useReducer(reducer, initialState);
|
||||||
const toggleNotification = useNotification();
|
const toggleNotification = useNotification();
|
||||||
|
const isMounted = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
isMounted.current = true;
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
fetchRole(id);
|
fetchRole(id);
|
||||||
} else {
|
} else {
|
||||||
@ -17,17 +21,23 @@ const useFetchRole = id => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return () => (isMounted.current = false);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
const fetchRole = async roleId => {
|
const fetchRole = async roleId => {
|
||||||
try {
|
try {
|
||||||
const { role } = await request(`/${pluginId}/roles/${roleId}`, { method: 'GET' });
|
const {
|
||||||
|
data: { role },
|
||||||
|
} = await axiosIntance.get(`/${pluginId}/roles/${roleId}`);
|
||||||
|
|
||||||
dispatch({
|
// Prevent updating state on an unmounted component
|
||||||
type: 'GET_DATA_SUCCEEDED',
|
if (isMounted.current) {
|
||||||
role,
|
dispatch({
|
||||||
});
|
type: 'GET_DATA_SUCCEEDED',
|
||||||
|
role,
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +1,24 @@
|
|||||||
import React, { useState, useRef } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Main, HeaderLayout, Button } from '@strapi/parts';
|
import { Main, Button, Stack, Box, GridItem, Grid, TextInput, Textarea } from '@strapi/parts';
|
||||||
|
import { ContentLayout, HeaderLayout } from '@strapi/parts/Layout';
|
||||||
|
import { H3 } from '@strapi/parts/Text';
|
||||||
|
import { CheckIcon } from '@strapi/icons';
|
||||||
import { Formik } from 'formik';
|
import { Formik } from 'formik';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { useRouteMatch } from 'react-router-dom';
|
import { useRouteMatch } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
request,
|
|
||||||
useNotification,
|
|
||||||
useOverlayBlocker,
|
useOverlayBlocker,
|
||||||
SettingsPageTitle,
|
SettingsPageTitle,
|
||||||
|
LoadingIndicatorPage,
|
||||||
|
Form,
|
||||||
|
useNotification,
|
||||||
} from '@strapi/helper-plugin';
|
} from '@strapi/helper-plugin';
|
||||||
|
|
||||||
import getTrad from '../../../utils/getTrad';
|
import getTrad from '../../../utils/getTrad';
|
||||||
import pluginId from '../../../pluginId';
|
import pluginId from '../../../pluginId';
|
||||||
import { usePlugins, useFetchRole } from '../../../hooks';
|
import { usePlugins, useFetchRole } from '../../../hooks';
|
||||||
|
|
||||||
import schema from './utils/schema';
|
import schema from './utils/schema';
|
||||||
|
import axiosInstance from '../../../utils/axiosInstance';
|
||||||
|
|
||||||
const EditPage = () => {
|
const EditPage = () => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
@ -24,43 +28,44 @@ const EditPage = () => {
|
|||||||
const {
|
const {
|
||||||
params: { id },
|
params: { id },
|
||||||
} = useRouteMatch(`/settings/${pluginId}/roles/:id`);
|
} = useRouteMatch(`/settings/${pluginId}/roles/:id`);
|
||||||
const { isLoading } = usePlugins();
|
const { isLoading: isLoadingPlugins } = usePlugins();
|
||||||
const { role, onSubmitSucceeded } = useFetchRole(id);
|
const { role, onSubmitSucceeded, isLoading: isLoadingRole } = useFetchRole(id);
|
||||||
const permissionsRef = useRef();
|
|
||||||
|
|
||||||
const handleCreateRoleSubmit = data => {
|
const handleCreateRoleSubmit = async data => {
|
||||||
|
// Set loading state
|
||||||
lockApp();
|
lockApp();
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
|
try {
|
||||||
const permissions = permissionsRef.current.getPermissions();
|
// Update role in Strapi
|
||||||
|
await axiosInstance.put(`/${pluginId}/roles/${id}`, { ...data, users: [] });
|
||||||
Promise.resolve(
|
// Notify success
|
||||||
request(`/${pluginId}/roles/${id}`, {
|
onSubmitSucceeded({ name: data.name, description: data.description });
|
||||||
method: 'PUT',
|
toggleNotification({
|
||||||
body: { ...data, ...permissions, users: [] },
|
type: 'success',
|
||||||
})
|
message: {
|
||||||
)
|
id: getTrad('Settings.roles.edited'),
|
||||||
.then(() => {
|
defaultMessage: 'Role edited',
|
||||||
onSubmitSucceeded({ name: data.name, description: data.description });
|
},
|
||||||
permissionsRef.current.setFormAfterSubmit();
|
|
||||||
toggleNotification({
|
|
||||||
type: 'success',
|
|
||||||
message: { id: getTrad('Settings.roles.edited') },
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
toggleNotification({
|
|
||||||
type: 'warning',
|
|
||||||
message: { id: 'notification.error' },
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setIsSubmitting(false);
|
|
||||||
unlockApp();
|
|
||||||
});
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
toggleNotification({
|
||||||
|
type: 'warning',
|
||||||
|
message: {
|
||||||
|
id: 'notification.error',
|
||||||
|
defaultMessage: 'An error occurred',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Unset loading state
|
||||||
|
setIsSubmitting(false);
|
||||||
|
unlockApp();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (isLoadingRole) {
|
||||||
|
return <LoadingIndicatorPage />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Main>
|
<Main>
|
||||||
<SettingsPageTitle name="Roles" />
|
<SettingsPageTitle name="Roles" />
|
||||||
@ -70,15 +75,16 @@ const EditPage = () => {
|
|||||||
onSubmit={handleCreateRoleSubmit}
|
onSubmit={handleCreateRoleSubmit}
|
||||||
validationSchema={schema}
|
validationSchema={schema}
|
||||||
>
|
>
|
||||||
{({ handleSubmit }) => (
|
{({ handleSubmit, values, handleChange, errors }) => (
|
||||||
<form onSubmit={handleSubmit}>
|
<Form noValidate onSubmit={handleSubmit}>
|
||||||
<HeaderLayout
|
<HeaderLayout
|
||||||
primaryAction={
|
primaryAction={
|
||||||
!isLoading && (
|
!isLoadingPlugins && (
|
||||||
<Button
|
<Button
|
||||||
disabled={role.code === 'strapi-super-admin'}
|
disabled={role.code === 'strapi-super-admin'}
|
||||||
onClick={handleSubmit}
|
type="submit"
|
||||||
loading={isSubmitting}
|
loading={isSubmitting}
|
||||||
|
startIcon={<CheckIcon />}
|
||||||
>
|
>
|
||||||
{formatMessage({
|
{formatMessage({
|
||||||
id: 'app.components.Button.save',
|
id: 'app.components.Button.save',
|
||||||
@ -90,7 +96,66 @@ const EditPage = () => {
|
|||||||
title={role.name}
|
title={role.name}
|
||||||
subtitle={role.description}
|
subtitle={role.description}
|
||||||
/>
|
/>
|
||||||
</form>
|
<ContentLayout>
|
||||||
|
<Stack size={7}>
|
||||||
|
<Box
|
||||||
|
background="neutral0"
|
||||||
|
hasRadius
|
||||||
|
shadow="filterShadow"
|
||||||
|
paddingTop={6}
|
||||||
|
paddingBottom={6}
|
||||||
|
paddingLeft={7}
|
||||||
|
paddingRight={7}
|
||||||
|
>
|
||||||
|
<Stack size={4}>
|
||||||
|
<H3 as="h2">
|
||||||
|
{formatMessage({
|
||||||
|
id: getTrad('EditPage.form.roles'),
|
||||||
|
defaultMessage: 'Role details',
|
||||||
|
})}
|
||||||
|
</H3>
|
||||||
|
<Grid gap={4}>
|
||||||
|
<GridItem col={6}>
|
||||||
|
<TextInput
|
||||||
|
name="name"
|
||||||
|
value={values.name || ''}
|
||||||
|
onChange={handleChange}
|
||||||
|
label={formatMessage({
|
||||||
|
id: 'Settings.roles.form.input.name',
|
||||||
|
defaultMessage: 'Name',
|
||||||
|
})}
|
||||||
|
error={
|
||||||
|
errors.name
|
||||||
|
? formatMessage({ id: errors.name, defaultMessage: 'Invalid value' })
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</GridItem>
|
||||||
|
<GridItem col={6}>
|
||||||
|
<Textarea
|
||||||
|
name="description"
|
||||||
|
value={values.description || ''}
|
||||||
|
onChange={handleChange}
|
||||||
|
label={formatMessage({
|
||||||
|
id: 'Settings.roles.form.input.description',
|
||||||
|
defaultMessage: 'Description',
|
||||||
|
})}
|
||||||
|
error={
|
||||||
|
errors.description
|
||||||
|
? formatMessage({
|
||||||
|
id: errors.description,
|
||||||
|
defaultMessage: 'Invalid value',
|
||||||
|
})
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</GridItem>
|
||||||
|
</Grid>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</ContentLayout>
|
||||||
|
</Form>
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
</Main>
|
</Main>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
import { ThemeProvider, lightTheme } from '@strapi/parts';
|
import { ThemeProvider, lightTheme } from '@strapi/parts';
|
||||||
import { Router, Switch, Route } from 'react-router-dom';
|
import { Router, Switch, Route } from 'react-router-dom';
|
||||||
import { IntlProvider } from 'react-intl';
|
import { IntlProvider } from 'react-intl';
|
||||||
@ -7,6 +8,7 @@ import { createMemoryHistory } from 'history';
|
|||||||
|
|
||||||
import pluginId from '../../../../pluginId';
|
import pluginId from '../../../../pluginId';
|
||||||
import RolesEditPage from '..';
|
import RolesEditPage from '..';
|
||||||
|
import server from './server';
|
||||||
|
|
||||||
jest.mock('@strapi/helper-plugin', () => ({
|
jest.mock('@strapi/helper-plugin', () => ({
|
||||||
...jest.requireActual('@strapi/helper-plugin'),
|
...jest.requireActual('@strapi/helper-plugin'),
|
||||||
@ -19,23 +21,15 @@ jest.mock('../../../../hooks', () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...originalModule,
|
...originalModule,
|
||||||
useFetchRole: id => {
|
usePlugins: () => ({
|
||||||
const role = {
|
...originalModule.usePlugins,
|
||||||
id,
|
isLoading: false,
|
||||||
name: 'Authenticated',
|
}),
|
||||||
description: 'Default role given to authenticated user.',
|
|
||||||
type: 'authenticated',
|
|
||||||
};
|
|
||||||
const onSubmitSucceed = jest.fn();
|
|
||||||
|
|
||||||
return { role, onSubmitSucceed };
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders users-permissions edit role and matches snapshot', () => {
|
function makeAndRenderApp() {
|
||||||
const history = createMemoryHistory();
|
const history = createMemoryHistory();
|
||||||
|
|
||||||
const app = (
|
const app = (
|
||||||
<IntlProvider locale="en" messages={{ en: {} }} textComponent="span">
|
<IntlProvider locale="en" messages={{ en: {} }} textComponent="span">
|
||||||
<ThemeProvider theme={lightTheme}>
|
<ThemeProvider theme={lightTheme}>
|
||||||
@ -47,110 +41,640 @@ it('renders users-permissions edit role and matches snapshot', () => {
|
|||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
);
|
);
|
||||||
|
const renderResult = render(app);
|
||||||
const { container } = render(app);
|
|
||||||
history.push(`/settings/${pluginId}/roles/1`);
|
history.push(`/settings/${pluginId}/roles/1`);
|
||||||
|
|
||||||
expect(container.firstChild).toMatchInlineSnapshot(`
|
return renderResult;
|
||||||
.c4 {
|
}
|
||||||
font-weight: 600;
|
|
||||||
font-size: 2rem;
|
|
||||||
line-height: 1.25;
|
|
||||||
color: #32324d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c5 {
|
describe('Admin | containers | RoleEditPage', () => {
|
||||||
font-weight: 400;
|
beforeAll(() => server.listen());
|
||||||
font-size: 0.875rem;
|
|
||||||
line-height: 1.43;
|
|
||||||
color: #666687;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c6 {
|
beforeEach(() => jest.clearAllMocks());
|
||||||
font-size: 1rem;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c1 {
|
afterEach(() => server.resetHandlers());
|
||||||
background: #f6f6f9;
|
|
||||||
padding-top: 56px;
|
|
||||||
padding-right: 56px;
|
|
||||||
padding-bottom: 56px;
|
|
||||||
padding-left: 56px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c2 {
|
afterAll(() => server.close());
|
||||||
display: -webkit-box;
|
|
||||||
display: -webkit-flex;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-flex-direction: row;
|
|
||||||
-ms-flex-direction: row;
|
|
||||||
flex-direction: row;
|
|
||||||
-webkit-box-pack: justify;
|
|
||||||
-webkit-justify-content: space-between;
|
|
||||||
-ms-flex-pack: justify;
|
|
||||||
justify-content: space-between;
|
|
||||||
-webkit-align-items: center;
|
|
||||||
-webkit-box-align: center;
|
|
||||||
-ms-flex-align: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c3 {
|
it('renders users-permissions edit role and matches snapshot', async () => {
|
||||||
display: -webkit-box;
|
const { container, getByTestId } = makeAndRenderApp();
|
||||||
display: -webkit-flex;
|
await waitForElementToBeRemoved(() => getByTestId('loader'));
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-flex-direction: row;
|
|
||||||
-ms-flex-direction: row;
|
|
||||||
flex-direction: row;
|
|
||||||
-webkit-align-items: center;
|
|
||||||
-webkit-box-align: center;
|
|
||||||
-ms-flex-align: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c0 {
|
expect(container.firstChild).toMatchInlineSnapshot(`
|
||||||
outline: none;
|
.c10 {
|
||||||
}
|
font-weight: 500;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
line-height: 1.33;
|
||||||
|
color: #32324d;
|
||||||
|
}
|
||||||
|
|
||||||
<main
|
.c8 {
|
||||||
aria-labelledby="main-content-title"
|
padding-right: 8px;
|
||||||
class="c0"
|
}
|
||||||
id="main-content"
|
|
||||||
tabindex="-1"
|
.c15 {
|
||||||
>
|
background: #ffffff;
|
||||||
<form>
|
padding-top: 24px;
|
||||||
<div
|
padding-right: 32px;
|
||||||
class=""
|
padding-bottom: 24px;
|
||||||
|
padding-left: 32px;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0px 1px 4px rgba(33,33,52,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.c22 {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-flex-direction: row;
|
||||||
|
-ms-flex-direction: row;
|
||||||
|
flex-direction: row;
|
||||||
|
-webkit-align-items: center;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c23 {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-flex-direction: row;
|
||||||
|
-ms-flex-direction: row;
|
||||||
|
flex-direction: row;
|
||||||
|
-webkit-box-pack: justify;
|
||||||
|
-webkit-justify-content: space-between;
|
||||||
|
-ms-flex-pack: justify;
|
||||||
|
justify-content: space-between;
|
||||||
|
-webkit-align-items: center;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c5 {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #dcdce4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c5 svg {
|
||||||
|
height: 12px;
|
||||||
|
width: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c5 svg > g,
|
||||||
|
.c5 svg path {
|
||||||
|
fill: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c5[aria-disabled='true'] {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c6 {
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: #4945ff;
|
||||||
|
border: none;
|
||||||
|
border: 1px solid #4945ff;
|
||||||
|
background: #4945ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c6 .c7 {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-align-items: center;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c6 .c9 {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c6[aria-disabled='true'] {
|
||||||
|
border: 1px solid #dcdce4;
|
||||||
|
background: #eaeaef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c6[aria-disabled='true'] .c9 {
|
||||||
|
color: #666687;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c6[aria-disabled='true'] svg > g,
|
||||||
|
.c6[aria-disabled='true'] svg path {
|
||||||
|
fill: #666687;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c6[aria-disabled='true']:active {
|
||||||
|
border: 1px solid #dcdce4;
|
||||||
|
background: #eaeaef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c6[aria-disabled='true']:active .c9 {
|
||||||
|
color: #666687;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c6[aria-disabled='true']:active svg > g,
|
||||||
|
.c6[aria-disabled='true']:active svg path {
|
||||||
|
fill: #666687;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c6:hover {
|
||||||
|
border: 1px solid #7b79ff;
|
||||||
|
background: #7b79ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c6:active {
|
||||||
|
border: 1px solid #4945ff;
|
||||||
|
background: #4945ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c14 {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-flex-direction: column;
|
||||||
|
-ms-flex-direction: column;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c14 > * {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c14 > * + * {
|
||||||
|
margin-top: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c16 {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-flex-direction: column;
|
||||||
|
-ms-flex-direction: column;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c16 > * {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c16 > * + * {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c21 {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-flex-direction: column;
|
||||||
|
-ms-flex-direction: column;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c21 > * {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c21 > * + * {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c25 {
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding-left: 16px;
|
||||||
|
padding-right: 16px;
|
||||||
|
color: #32324d;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c25::-webkit-input-placeholder {
|
||||||
|
color: #8e8ea9;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c25::-moz-placeholder {
|
||||||
|
color: #8e8ea9;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c25:-ms-input-placeholder {
|
||||||
|
color: #8e8ea9;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c25::placeholder {
|
||||||
|
color: #8e8ea9;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c25:disabled {
|
||||||
|
background: inherit;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c24 {
|
||||||
|
border: 1px solid #dcdce4;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c20 textarea {
|
||||||
|
height: 5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c18 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(12,1fr);
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c19 {
|
||||||
|
grid-column: span 6;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c0 {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c27 {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid #dcdce4;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding-left: 16px;
|
||||||
|
padding-right: 16px;
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: #32324d;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c27::-webkit-input-placeholder {
|
||||||
|
color: #8e8ea9;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c27::-moz-placeholder {
|
||||||
|
color: #8e8ea9;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c27:-ms-input-placeholder {
|
||||||
|
color: #8e8ea9;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c27::placeholder {
|
||||||
|
color: #8e8ea9;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c26 textarea {
|
||||||
|
height: 5rem;
|
||||||
|
line-height: 1.25rem;
|
||||||
|
font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans', 'Helvetica Neue',sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c26 textarea::-webkit-input-placeholder {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.43;
|
||||||
|
color: #8e8ea9;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c26 textarea::-moz-placeholder {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.43;
|
||||||
|
color: #8e8ea9;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c26 textarea:-ms-input-placeholder {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.43;
|
||||||
|
color: #8e8ea9;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c26 textarea::placeholder {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.43;
|
||||||
|
color: #8e8ea9;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c1 {
|
||||||
|
background: #f6f6f9;
|
||||||
|
padding-top: 56px;
|
||||||
|
padding-right: 56px;
|
||||||
|
padding-bottom: 56px;
|
||||||
|
padding-left: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c13 {
|
||||||
|
padding-right: 56px;
|
||||||
|
padding-left: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c2 {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-flex-direction: row;
|
||||||
|
-ms-flex-direction: row;
|
||||||
|
flex-direction: row;
|
||||||
|
-webkit-box-pack: justify;
|
||||||
|
-webkit-justify-content: space-between;
|
||||||
|
-ms-flex-pack: justify;
|
||||||
|
justify-content: space-between;
|
||||||
|
-webkit-align-items: center;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c3 {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-flex-direction: row;
|
||||||
|
-ms-flex-direction: row;
|
||||||
|
flex-direction: row;
|
||||||
|
-webkit-align-items: center;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c4 {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 2rem;
|
||||||
|
line-height: 1.25;
|
||||||
|
color: #32324d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c11 {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.43;
|
||||||
|
color: #666687;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c12 {
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c17 {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.25;
|
||||||
|
color: #32324d;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width:68.75rem) {
|
||||||
|
.c19 {
|
||||||
|
grid-column: span;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width:34.375rem) {
|
||||||
|
.c19 {
|
||||||
|
grid-column: span;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<main
|
||||||
|
aria-labelledby="main-content-title"
|
||||||
|
class="c0"
|
||||||
|
id="main-content"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
action="#"
|
||||||
|
novalidate=""
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="c1"
|
class=""
|
||||||
data-strapi-header="true"
|
style="height: 0px;"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="c2"
|
class="c1"
|
||||||
|
data-strapi-header="true"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="c3"
|
class="c2"
|
||||||
>
|
>
|
||||||
<h1
|
<div
|
||||||
class="c4"
|
class="c3"
|
||||||
id="main-content-title"
|
|
||||||
>
|
>
|
||||||
Authenticated
|
<h1
|
||||||
</h1>
|
class="c4"
|
||||||
|
id="main-content-title"
|
||||||
|
>
|
||||||
|
Authenticated
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
aria-disabled="false"
|
||||||
|
class="c5 c6"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
class="c7 c8"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="none"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M20.727 2.97a.2.2 0 01.286 0l2.85 2.89a.2.2 0 010 .28L9.554 20.854a.2.2 0 01-.285 0l-9.13-9.243a.2.2 0 010-.281l2.85-2.892a.2.2 0 01.284 0l6.14 6.209L20.726 2.97z"
|
||||||
|
fill="#212134"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="c9 c10"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
class="c11 c12"
|
||||||
|
>
|
||||||
|
Default role given to authenticated user.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="c13"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c14"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c7 c15"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c16"
|
||||||
|
>
|
||||||
|
<h2
|
||||||
|
class="c17"
|
||||||
|
>
|
||||||
|
Role details
|
||||||
|
</h2>
|
||||||
|
<div
|
||||||
|
class="c7 c18"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c19"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c7 "
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c20"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="c21"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c7 c22"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="c9 c10"
|
||||||
|
for="textinput-1"
|
||||||
|
>
|
||||||
|
Name
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="c7 c23 c24"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-invalid="false"
|
||||||
|
class="c25"
|
||||||
|
id="textinput-1"
|
||||||
|
name="name"
|
||||||
|
value="Authenticated"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="c19"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c7 "
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c26"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="c21"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c7 c22"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="c9 c10"
|
||||||
|
for="textarea-2"
|
||||||
|
>
|
||||||
|
Description
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<textarea
|
||||||
|
aria-invalid="false"
|
||||||
|
class="c27"
|
||||||
|
id="textarea-2"
|
||||||
|
name="description"
|
||||||
|
>
|
||||||
|
Default role given to authenticated user.
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p
|
|
||||||
class="c5 c6"
|
|
||||||
>
|
|
||||||
Default role given to authenticated user.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
</form>
|
</main>
|
||||||
</main>
|
`);
|
||||||
`);
|
});
|
||||||
|
|
||||||
|
it("can edit a users-permissions role's name and description", async () => {
|
||||||
|
const { getByLabelText, getByRole, getByTestId, getAllByText } = makeAndRenderApp();
|
||||||
|
|
||||||
|
// Check loading screen
|
||||||
|
const loader = getByTestId('loader');
|
||||||
|
expect(loader).toBeInTheDocument();
|
||||||
|
|
||||||
|
// After loading, check other elements
|
||||||
|
await waitForElementToBeRemoved(loader).catch(e => console.error(e));
|
||||||
|
const saveButton = getByRole('button', { name: /save/i });
|
||||||
|
expect(saveButton).toBeInTheDocument();
|
||||||
|
const nameField = getByLabelText(/name/i);
|
||||||
|
expect(nameField).toBeInTheDocument();
|
||||||
|
const descriptionField = getByLabelText(/description/i);
|
||||||
|
expect(descriptionField).toBeInTheDocument();
|
||||||
|
|
||||||
|
// Shows error when name is missing
|
||||||
|
await userEvent.clear(nameField);
|
||||||
|
expect(nameField).toHaveValue('');
|
||||||
|
await userEvent.clear(descriptionField);
|
||||||
|
expect(descriptionField).toHaveValue('');
|
||||||
|
|
||||||
|
// Show errors after form submit
|
||||||
|
await userEvent.click(saveButton);
|
||||||
|
await waitFor(() => expect(saveButton).not.toBeDisabled());
|
||||||
|
const errorMessages = await getAllByText(/invalid value/i);
|
||||||
|
errorMessages.forEach(errorMessage => expect(errorMessage).toBeInTheDocument());
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,43 @@
|
|||||||
|
import { setupServer } from 'msw/node';
|
||||||
|
import { rest } from 'msw';
|
||||||
|
|
||||||
|
const handlers = [
|
||||||
|
// Mock get role route
|
||||||
|
rest.get('*/users-permissions/roles/:roleId', (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.delay(500),
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
role: {
|
||||||
|
id: req.params.roleId,
|
||||||
|
name: 'Authenticated',
|
||||||
|
description: 'Default role given to authenticated user.',
|
||||||
|
type: 'authenticated',
|
||||||
|
created_at: '2021-09-08T16:26:18.061Z',
|
||||||
|
updated_at: '2021-09-08T16:26:18.061Z',
|
||||||
|
permissions: {
|
||||||
|
application: {
|
||||||
|
controllers: {
|
||||||
|
address: {
|
||||||
|
create: {
|
||||||
|
enabled: false,
|
||||||
|
policy: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Mock edit role route
|
||||||
|
rest.put('*/users-permissions/roles/:roleId', (req, res, ctx) => {
|
||||||
|
return res(ctx.delay(500), ctx.status(200), ctx.json({ ok: true }));
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const server = setupServer(...handlers);
|
||||||
|
|
||||||
|
export default server;
|
||||||
Loading…
x
Reference in New Issue
Block a user