mirror of
https://github.com/strapi/strapi.git
synced 2025-11-06 21:29:24 +00:00
Merge pull request #10893 from strapi/ds-migration/roles-list-header
[v4] migrate roles page header
This commit is contained in:
commit
13fb791c67
@ -1,195 +1,62 @@
|
|||||||
import React, { useCallback, useMemo, useState } from 'react';
|
import React from 'react';
|
||||||
import { List, Header } from '@buffetjs/custom';
|
import { Button, HeaderLayout, Layout, Main } from '@strapi/parts';
|
||||||
import { Helmet } from 'react-helmet';
|
import { AddIcon } from '@strapi/icons';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
import { useTracking, SettingsPageTitle, CheckPermissions } from '@strapi/helper-plugin';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
||||||
import {
|
|
||||||
useRBAC,
|
|
||||||
PopUpWarning,
|
|
||||||
request,
|
|
||||||
useTracking,
|
|
||||||
useNotification,
|
|
||||||
useOverlayBlocker,
|
|
||||||
} from '@strapi/helper-plugin';
|
|
||||||
|
|
||||||
import permissions from '../../../permissions';
|
|
||||||
import { EmptyRole, RoleListWrapper, RoleRow } from '../../../components/Roles';
|
|
||||||
import { useRolesList } from '../../../hooks';
|
|
||||||
import BaselineAlignment from './BaselineAlignment';
|
|
||||||
import pluginId from '../../../pluginId';
|
|
||||||
import { getTrad } from '../../../utils';
|
import { getTrad } from '../../../utils';
|
||||||
|
import pluginId from '../../../pluginId';
|
||||||
|
import permissions from '../../../permissions';
|
||||||
|
|
||||||
const RoleListPage = () => {
|
const RoleListPage = () => {
|
||||||
const { formatMessage } = useIntl();
|
|
||||||
const { trackUsage } = useTracking();
|
const { trackUsage } = useTracking();
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
const { push } = useHistory();
|
const { push } = useHistory();
|
||||||
const toggleNotification = useNotification();
|
|
||||||
const { lockApp, unlockApp } = useOverlayBlocker();
|
|
||||||
const [modalToDelete, setModalDelete] = useState();
|
|
||||||
const [shouldRefetchData, setShouldRefetchData] = useState(false);
|
|
||||||
const [showModalConfirmButtonLoading, setModalButtonLoading] = useState(false);
|
|
||||||
|
|
||||||
const updatePermissions = useMemo(() => {
|
|
||||||
return {
|
|
||||||
update: permissions.updateRole,
|
|
||||||
create: permissions.createRole,
|
|
||||||
delete: permissions.deleteRole,
|
|
||||||
read: permissions.readRoles,
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
const {
|
|
||||||
isLoading: isLoadingForPermissions,
|
|
||||||
allowedActions: { canCreate, canUpdate, canDelete, canRead },
|
|
||||||
} = useRBAC(updatePermissions);
|
|
||||||
const shouldFetchData = !isLoadingForPermissions && canRead;
|
|
||||||
|
|
||||||
const { roles, getData, isLoading } = useRolesList(shouldFetchData);
|
|
||||||
|
|
||||||
const handleGoTo = id => {
|
|
||||||
if (canUpdate) {
|
|
||||||
push(`/settings/${pluginId}/roles/${id}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDelete = () => {
|
|
||||||
lockApp();
|
|
||||||
|
|
||||||
setModalButtonLoading(true);
|
|
||||||
|
|
||||||
Promise.resolve(
|
|
||||||
request(`/${pluginId}/roles/${modalToDelete}`, {
|
|
||||||
method: 'DELETE',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.then(() => {
|
|
||||||
setShouldRefetchData(true);
|
|
||||||
toggleNotification({
|
|
||||||
type: 'success',
|
|
||||||
message: { id: getTrad('Settings.roles.deleted') },
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
toggleNotification({
|
|
||||||
type: 'warning',
|
|
||||||
message: { id: 'notification.error' },
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setModalDelete(null);
|
|
||||||
unlockApp();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClosedModalDelete = () => {
|
|
||||||
if (shouldRefetchData) {
|
|
||||||
getData();
|
|
||||||
}
|
|
||||||
setModalButtonLoading(false);
|
|
||||||
setShouldRefetchData(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleNewRoleClick = () => {
|
const handleNewRoleClick = () => {
|
||||||
trackUsage('willCreateRole');
|
trackUsage('willCreateRole');
|
||||||
push(`/settings/${pluginId}/roles/new`);
|
push(`/settings/${pluginId}/roles/new`);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* eslint-disable indent */
|
const pageTitle = formatMessage({
|
||||||
const headerActions = canCreate
|
id: getTrad('HeaderNav.link.roles'),
|
||||||
? [
|
defaultMessage: 'Roles',
|
||||||
{
|
});
|
||||||
label: formatMessage({
|
|
||||||
id: getTrad('List.button.roles'),
|
|
||||||
defaultMessage: 'Add new role',
|
|
||||||
}),
|
|
||||||
onClick: handleNewRoleClick,
|
|
||||||
color: 'primary',
|
|
||||||
type: 'button',
|
|
||||||
icon: true,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: [];
|
|
||||||
/* eslint-enable indent */
|
|
||||||
|
|
||||||
const checkCanDeleteRole = useCallback(
|
|
||||||
role => {
|
|
||||||
return canDelete && !['public', 'authenticated'].includes(role.type);
|
|
||||||
},
|
|
||||||
[canDelete]
|
|
||||||
);
|
|
||||||
|
|
||||||
const getLinks = role => {
|
|
||||||
const links = [];
|
|
||||||
|
|
||||||
if (canUpdate) {
|
|
||||||
links.push({
|
|
||||||
icon: <FontAwesomeIcon icon="pencil-alt" />,
|
|
||||||
onClick: () => handleGoTo(role.id),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (checkCanDeleteRole(role)) {
|
|
||||||
links.push({
|
|
||||||
icon: <FontAwesomeIcon icon="trash-alt" />,
|
|
||||||
onClick: e => {
|
|
||||||
e.preventDefault();
|
|
||||||
setModalDelete(role.id);
|
|
||||||
e.stopPropagation();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return links;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Layout>
|
||||||
<Helmet title={formatMessage({ id: getTrad('page.title') })} />
|
<SettingsPageTitle name={pageTitle} />
|
||||||
|
<Main
|
||||||
<Header
|
labelledBy={formatMessage({
|
||||||
icon
|
id: getTrad('HeaderNav.link.roles'),
|
||||||
title={{
|
defaultMessage: 'Roles',
|
||||||
label: formatMessage({
|
|
||||||
id: 'Settings.roles.title',
|
|
||||||
defaultMessage: 'Roles & Permissions',
|
|
||||||
}),
|
|
||||||
}}
|
|
||||||
content={formatMessage({
|
|
||||||
id: 'Settings.roles.list.description',
|
|
||||||
defaultMessage: 'Define the roles and permissions for your users.',
|
|
||||||
})}
|
})}
|
||||||
actions={headerActions}
|
>
|
||||||
// Show a loader in the header while requesting data
|
<HeaderLayout
|
||||||
isLoading={isLoading || isLoadingForPermissions}
|
as="h1"
|
||||||
/>
|
id="roles"
|
||||||
|
title={formatMessage({
|
||||||
<BaselineAlignment />
|
id: 'Settings.roles.title',
|
||||||
{canRead && (
|
defaultMessage: 'Roles',
|
||||||
<RoleListWrapper>
|
})}
|
||||||
<List
|
subtitle={formatMessage({
|
||||||
title={formatMessage(
|
id: 'Settings.roles.list.description',
|
||||||
{
|
defaultMessage: 'List of roles',
|
||||||
id: `Settings.roles.list.title${roles.length > 1 ? '.plural' : '.singular'}`,
|
})}
|
||||||
},
|
primaryAction={
|
||||||
{ number: roles.length }
|
<CheckPermissions permissions={permissions.createRole}>
|
||||||
)}
|
<Button onClick={handleNewRoleClick} startIcon={<AddIcon />}>
|
||||||
items={roles}
|
{formatMessage({
|
||||||
isLoading={isLoading || isLoadingForPermissions}
|
id: getTrad('List.button.roles'),
|
||||||
customRowComponent={role => (
|
defaultMessage: 'Add new role',
|
||||||
<RoleRow onClick={() => handleGoTo(role.id)} links={getLinks(role)} role={role} />
|
})}
|
||||||
)}
|
</Button>
|
||||||
/>
|
</CheckPermissions>
|
||||||
{!roles && !isLoading && !isLoadingForPermissions && <EmptyRole />}
|
}
|
||||||
<PopUpWarning
|
/>
|
||||||
isOpen={Boolean(modalToDelete)}
|
</Main>
|
||||||
onConfirm={handleDelete}
|
</Layout>
|
||||||
onClosed={handleClosedModalDelete}
|
|
||||||
toggleModal={() => setModalDelete(null)}
|
|
||||||
isConfirmButtonLoading={showModalConfirmButtonLoading}
|
|
||||||
/>
|
|
||||||
</RoleListWrapper>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,160 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { render } from '@testing-library/react';
|
||||||
|
import { IntlProvider } from 'react-intl';
|
||||||
|
import { ThemeProvider, lightTheme } from '@strapi/parts';
|
||||||
|
|
||||||
|
import RoleListPage from '../index';
|
||||||
|
|
||||||
|
jest.mock('@strapi/helper-plugin', () => ({
|
||||||
|
...jest.requireActual('@strapi/helper-plugin'),
|
||||||
|
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||||
|
useNotification: jest.fn(),
|
||||||
|
CheckPermissions: jest.fn(() => <div />),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
...jest.requireActual('react-router-dom'),
|
||||||
|
useHistory: () => ({
|
||||||
|
push: jest.fn(),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const App = (
|
||||||
|
<ThemeProvider theme={lightTheme}>
|
||||||
|
<IntlProvider locale="en" messages={{ en: {} }} textComponent="span">
|
||||||
|
<RoleListPage />
|
||||||
|
</IntlProvider>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('Admin | containers | RoleListPage', () => {
|
||||||
|
it('renders and matches the snapshot', () => {
|
||||||
|
const {
|
||||||
|
container: { firstChild },
|
||||||
|
} = render(App);
|
||||||
|
|
||||||
|
expect(firstChild).toMatchInlineSnapshot(`
|
||||||
|
.c7 {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 2rem;
|
||||||
|
line-height: 1.25;
|
||||||
|
color: #32324d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c8 {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.43;
|
||||||
|
color: #666687;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c9 {
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c1 {
|
||||||
|
padding-bottom: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c4 {
|
||||||
|
background: #f6f6f9;
|
||||||
|
padding-top: 56px;
|
||||||
|
padding-right: 56px;
|
||||||
|
padding-bottom: 56px;
|
||||||
|
padding-left: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c5 {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c6 {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c0 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c2 {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c3 {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="c0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c1 c2"
|
||||||
|
>
|
||||||
|
<main
|
||||||
|
aria-labelledby="Roles"
|
||||||
|
class="c3"
|
||||||
|
id="main-content"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class=""
|
||||||
|
style="height: 0px;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c4"
|
||||||
|
data-strapi-header="true"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c5"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c6"
|
||||||
|
>
|
||||||
|
<h1
|
||||||
|
class="c7"
|
||||||
|
id="roles"
|
||||||
|
>
|
||||||
|
Roles
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
class="c8 c9"
|
||||||
|
>
|
||||||
|
List of roles
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user