From d1531b2c4390c351746a311a6f85e66651879d23 Mon Sep 17 00:00:00 2001 From: Kevin Silva Date: Thu, 30 Nov 2023 10:07:35 +0100 Subject: [PATCH] SONAR-21068 - Update permissions to adapt the new UI --- .../design-system/src/components/Table.tsx | 13 +- .../js/app/components/GlobalContainer.tsx | 3 + .../components/ActionsCell.tsx | 51 ++++---- .../components/ListItem.tsx | 2 +- .../components/Template.tsx | 89 +++++++------- .../components/TemplateDetails.tsx | 8 +- .../components/TemplateHeader.tsx | 25 ++-- .../__tests__/PermissionTemplatesApp-it.tsx | 22 ++-- .../global/components/PageHeader.tsx | 26 ++-- .../components/PermissionsGlobalApp.tsx | 49 ++++---- .../project/components/ApplyTemplate.tsx | 94 +++++++-------- .../project/components/PageHeader.tsx | 78 ++++++------ .../components/PermissionsProjectApp.tsx | 113 +++++++++--------- .../components/PublicProjectDisclaimer.tsx | 6 +- .../__tests__/PermissionsProject-it.tsx | 32 ++--- .../main/js/apps/permissions/test-utils.ts | 8 +- .../__tests__/ProjectManagementApp-it.tsx | 6 +- .../components/permissions/AllHoldersList.tsx | 34 ++++-- .../js/components/permissions/GroupHolder.tsx | 34 +++--- .../js/components/permissions/HoldersList.tsx | 66 +++++----- .../components/permissions/PermissionCell.tsx | 75 +++++++----- .../permissions/PermissionHeader.tsx | 43 ++++--- .../js/components/permissions/SearchForm.tsx | 11 +- .../js/components/permissions/UserHolder.tsx | 57 ++++----- 24 files changed, 492 insertions(+), 453 deletions(-) diff --git a/server/sonar-web/design-system/src/components/Table.tsx b/server/sonar-web/design-system/src/components/Table.tsx index 332dd65230e..03c528826c3 100644 --- a/server/sonar-web/design-system/src/components/Table.tsx +++ b/server/sonar-web/design-system/src/components/Table.tsx @@ -86,6 +86,11 @@ export function Table(props: TableProps) { ); } +export const TableSeparator = styled.tr` + ${tw`sw-h-4`} + border-top: ${themeBorder('default')}; +`; + export const TableRow = styled.tr` td, th { @@ -182,10 +187,14 @@ export function CellComponent(props: CellComponentProps) { return ; } -export function ContentCell({ children, ...props }: CellComponentProps) { +export function ContentCell({ children, className, ...props }: CellComponentProps) { return ( -
{children}
+
+ {children} +
); } diff --git a/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx b/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx index 6c8010b6b7c..7fbe8586840 100644 --- a/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx @@ -67,6 +67,9 @@ const TEMP_PAGELIST_WITH_NEW_BACKGROUND_WHITE = [ '/project/quality_profiles', '/project/webhooks', '/admin/webhooks', + '/project_roles', + '/admin/permissions', + '/admin/permission_templates', ]; export default function GlobalContainer() { diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.tsx index 85cc3137e67..7e45df12279 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.tsx @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { ActionsDropdown, ItemButton, ItemLink, PopupZLevel } from 'design-system'; import { difference } from 'lodash'; import * as React from 'react'; import { @@ -24,7 +25,6 @@ import { setDefaultPermissionTemplate, updatePermissionTemplate, } from '../../../api/permissions'; -import ActionsDropdown, { ActionsDropdownItem } from '../../../components/controls/ActionsDropdown'; import { Router, withRouter } from '../../../components/hoc/withRouter'; import QualifierIcon from '../../../components/icons/QualifierIcon'; import { translate, translateWithParameters } from '../../../helpers/l10n'; @@ -121,14 +121,14 @@ class ActionsCell extends React.PureComponent { renderSetDefaultLink(qualifier: string, child: React.ReactNode) { return ( - {child} - + ); } @@ -159,27 +159,32 @@ class ActionsCell extends React.PureComponent { return ( <> - {this.renderSetDefaultsControl()} - - {!this.props.fromDetails && ( - - {translate('edit_permissions')} - - )} - - - {translate('update_details')} - - - {t.defaultFor.length === 0 && ( - - {translate('delete')} - - )} + <> + {this.renderSetDefaultsControl()} + + {!this.props.fromDetails && ( + + {translate('edit_permissions')} + + )} + + + {translate('update_details')} + + + {t.defaultFor.length === 0 && ( + + {translate('delete')} + + )} + {this.state.updateModal && ( diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.tsx index e8b450c6876..9f78b8a707f 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.tsx @@ -40,7 +40,7 @@ export default function ListItem(props: Props) { {permissions} - + { } return ( -
- - - -
- - - {({ data: githubProvisioningStatus }) => - githubProvisioningStatus ? ( - - {translate('permission_templates.github_warning')} - - ) : null - } - - - + + + + -
-
+
+ + + {({ data: githubProvisioningStatus }) => + githubProvisioningStatus ? ( + + {translate('permission_templates.github_warning')} + + ) : null + } + + + +
+ + ); } } diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateDetails.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateDetails.tsx index a5dd0012f9e..05e42b34a90 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateDetails.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateDetails.tsx @@ -27,19 +27,19 @@ interface Props { export default function TemplateDetails({ template }: Props) { return ( -
+
{template.defaultFor.length > 0 && ( -
+
)} {!!template.description && ( -
{template.description}
+
{template.description}
)} {!!template.projectKeyPattern && ( -
+
Project Key Pattern: {template.projectKeyPattern}
)} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.tsx index 3625d0be7a5..1b76323f8a2 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.tsx @@ -17,34 +17,33 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { Link, Title } from 'design-system'; import * as React from 'react'; -import Link from '../../../components/common/Link'; -import Spinner from '../../../components/ui/Spinner'; import { translate } from '../../../helpers/l10n'; import { PermissionTemplate } from '../../../types/types'; import { PERMISSION_TEMPLATES_PATH } from '../utils'; import ActionsCell from './ActionsCell'; interface Props { - loading: boolean; refresh: () => void; template: PermissionTemplate; topQualifiers: string[]; } export default function TemplateHeader(props: Props) { - const { template, loading } = props; + const { template } = props; return ( -
-
- {translate('permission_templates.page')} +
+
+
+ {translate('permission_templates.page')} +
+
+ {template.name} +
+
{translate('global_permissions.page.description')}
- -

{template.name}

- - - -
+
{ await ui.openTemplateDetails('Permission Template 1'); await ui.appLoaded(); - expect(screen.getAllByRole('row').length).toBe(12); + expect(screen.getAllByRole('row').length).toBe(11); await ui.toggleFilterByPermission(Permissions.Admin); expect(screen.getAllByRole('row').length).toBe(3); await ui.toggleFilterByPermission(Permissions.Admin); - expect(screen.getAllByRole('row').length).toBe(12); + expect(screen.getAllByRole('row').length).toBe(11); }); }); @@ -368,9 +368,9 @@ it('should correctly handle pagination', async () => { await ui.openTemplateDetails('Permission Template 1'); await ui.appLoaded(); - expect(screen.getAllByRole('row').length).toBe(14); + expect(screen.getAllByRole('row').length).toBe(13); await ui.clickLoadMore(); - expect(screen.getAllByRole('row').length).toBe(24); + expect(screen.getAllByRole('row').length).toBe(23); }); it.each([ComponentQualifier.Project, ComponentQualifier.Application, ComponentQualifier.Portfolio])( @@ -412,21 +412,21 @@ function getPageObject(user: UserEvent) { name: `permission.assign_x_to_y.projects_role.${permission}.${target}`, }), tableHeaderFilter: (permission: Permissions) => - byRole('link', { name: `projects_role.${permission}` }), - onlyUsersBtn: byRole('button', { name: 'users.page' }), - onlyGroupsBtn: byRole('button', { name: 'user_groups.page' }), + byRole('button', { name: `projects_role.${permission}` }), + onlyUsersBtn: byRole('radio', { name: 'users.page' }), + onlyGroupsBtn: byRole('radio', { name: 'user_groups.page' }), githubWarning: byText('permission_templates.github_warning'), - showAllBtn: byRole('button', { name: 'all' }), + showAllBtn: byRole('radio', { name: 'all' }), searchInput: byRole('searchbox', { name: 'search.search_for_users_or_groups' }), loadMoreBtn: byRole('button', { name: 'show_more' }), createNewTemplateBtn: byRole('button', { name: 'create' }), modal: byRole('dialog'), cogMenuBtn: (name: string) => byRole('button', { name: `permission_templates.show_actions_for_x.${name}` }), - deleteBtn: byRole('button', { name: 'delete' }), - updateDetailsBtn: byRole('button', { name: 'update_details' }), + deleteBtn: byRole('menuitem', { name: 'delete' }), + updateDetailsBtn: byRole('menuitem', { name: 'update_details' }), setDefaultBtn: (qualifier: ComponentQualifier) => - byRole('button', { + byRole('menuitem', { name: qualifier === ComponentQualifier.Project ? 'permission_templates.set_default' diff --git a/server/sonar-web/src/main/js/apps/permissions/global/components/PageHeader.tsx b/server/sonar-web/src/main/js/apps/permissions/global/components/PageHeader.tsx index 41fe542f964..f60fdac36d8 100644 --- a/server/sonar-web/src/main/js/apps/permissions/global/components/PageHeader.tsx +++ b/server/sonar-web/src/main/js/apps/permissions/global/components/PageHeader.tsx @@ -17,23 +17,17 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { Title } from 'design-system'; import * as React from 'react'; import { translate } from '../../../../helpers/l10n'; -interface Props { - loading?: boolean; -} - -export default class PageHeader extends React.PureComponent { - render() { - return ( -
-

{translate('global_permissions.page')}

- - {this.props.loading && } - -
{translate('global_permissions.page.description')}
-
- ); - } +export default function PageHeader() { + return ( +
+
+ {translate('global_permissions.page')} +
+
{translate('global_permissions.page.description')}
+
+ ); } diff --git a/server/sonar-web/src/main/js/apps/permissions/global/components/PermissionsGlobalApp.tsx b/server/sonar-web/src/main/js/apps/permissions/global/components/PermissionsGlobalApp.tsx index b048597dda2..93be364f5fc 100644 --- a/server/sonar-web/src/main/js/apps/permissions/global/components/PermissionsGlobalApp.tsx +++ b/server/sonar-web/src/main/js/apps/permissions/global/components/PermissionsGlobalApp.tsx @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { LargeCenteredLayout, PageContentFontWrapper } from 'design-system'; import { without } from 'lodash'; import * as React from 'react'; import { Helmet } from 'react-helmet-async'; @@ -29,9 +30,9 @@ import AllHoldersList from '../../../../components/permissions/AllHoldersList'; import { FilterOption } from '../../../../components/permissions/SearchForm'; import { translate } from '../../../../helpers/l10n'; import { + PERMISSIONS_ORDER_GLOBAL, convertToPermissionDefinitions, filterPermissions, - PERMISSIONS_ORDER_GLOBAL, } from '../../../../helpers/permissions'; import { ComponentQualifier } from '../../../../types/component'; import { Paging, PermissionGroup, PermissionUser } from '../../../../types/types'; @@ -256,28 +257,30 @@ class PermissionsGlobalApp extends React.PureComponent { 'global_permissions', ); return ( -
- - - - -
+ + + + + + + + ); } } diff --git a/server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx b/server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx index 6a119bd9738..8fbaa59da61 100644 --- a/server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx +++ b/server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx @@ -17,15 +17,17 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { + ButtonPrimary, + FlagMessage, + FormField, + InputSelect, + LabelValueSelectOption, + Modal, +} from 'design-system'; import * as React from 'react'; import { applyTemplateToProject, getPermissionTemplates } from '../../../../api/permissions'; -import Select from '../../../../components/controls/Select'; -import SimpleModal from '../../../../components/controls/SimpleModal'; -import { ResetButtonLink, SubmitButton } from '../../../../components/controls/buttons'; -import { Alert } from '../../../../components/ui/Alert'; -import MandatoryFieldMarker from '../../../../components/ui/MandatoryFieldMarker'; import MandatoryFieldsExplanation from '../../../../components/ui/MandatoryFieldsExplanation'; -import Spinner from '../../../../components/ui/Spinner'; import { translate, translateWithParameters } from '../../../../helpers/l10n'; import { PermissionTemplate } from '../../../../types/types'; @@ -70,8 +72,10 @@ export default class ApplyTemplate extends React.PureComponent { ); }; - handleSubmit = () => { + handleSubmit = (event: React.SyntheticEvent) => { + event.preventDefault(); if (this.state.permissionTemplate) { + this.setState({ loading: true }); return applyTemplateToProject({ projectKey: this.props.project.key, templateId: this.state.permissionTemplate, @@ -80,13 +84,13 @@ export default class ApplyTemplate extends React.PureComponent { if (this.props.onApply) { this.props.onApply(); } - this.setState({ done: true }); + this.setState({ done: true, loading: false }); } }); } }; - handlePermissionTemplateChange = ({ value }: { value: string }) => { + handlePermissionTemplateChange = ({ value }: LabelValueSelectOption) => { this.setState({ permissionTemplate: value }); }; @@ -103,63 +107,59 @@ export default class ApplyTemplate extends React.PureComponent { })) : []; + const FORM_ID = 'project-permissions-apply-template-form'; + return ( - - {({ onCloseClick, onFormSubmit, submitting }) => ( -
-
-

{header}

-
- -
+ primaryButton={ + !this.state.done && ( + + {translate('apply')} + + ) + } + secondaryButtonLabel={translate(this.state.done ? 'close' : 'cancel')} + body={ + +
{this.state.done && ( - {translate('projects_role.apply_template.success')} + + {translate('projects_role.apply_template.success')} + )} - {this.state.loading && } - {!this.state.done && !this.state.loading && ( <> - -
- + + {this.state.permissionTemplates && ( -