From e0b5b2c7b9b0844d22aeaf5d73193c9da817281e Mon Sep 17 00:00:00 2001 From: Revanshu Paliwal Date: Tue, 2 Jan 2024 12:18:43 +0100 Subject: [PATCH] SONAR-21384 Migrating users list inside security to adopt the new UI --- .../src/main/js/apps/users/UsersApp.tsx | 1 + .../src/main/js/apps/users/UsersList.tsx | 67 ++++++++------- .../js/apps/users/__tests__/UsersApp-it.tsx | 37 +++++---- .../js/apps/users/components/UserActions.tsx | 41 +++++----- .../js/apps/users/components/UserListItem.tsx | 81 ++++++++++--------- .../users/components/UserListItemIdentity.tsx | 22 +++-- .../apps/users/components/UserScmAccounts.tsx | 17 ++-- 7 files changed, 139 insertions(+), 127 deletions(-) diff --git a/server/sonar-web/src/main/js/apps/users/UsersApp.tsx b/server/sonar-web/src/main/js/apps/users/UsersApp.tsx index 4dd2306a8a9..af4769e5e1f 100644 --- a/server/sonar-web/src/main/js/apps/users/UsersApp.tsx +++ b/server/sonar-web/src/main/js/apps/users/UsersApp.tsx @@ -147,6 +147,7 @@ export default function UsersApp() { loadMore={fetchNextPage} ready={!isLoading} total={data?.pages[0].page.total} + useMIUIButtons /> ); diff --git a/server/sonar-web/src/main/js/apps/users/UsersList.tsx b/server/sonar-web/src/main/js/apps/users/UsersList.tsx index 2b53c10c50a..19a2bb20dd6 100644 --- a/server/sonar-web/src/main/js/apps/users/UsersList.tsx +++ b/server/sonar-web/src/main/js/apps/users/UsersList.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 { ActionCell, ContentCell, HelperHintIcon, Table, TableRow } from 'design-system'; import * as React from 'react'; import HelpTooltip from '../../components/controls/HelpTooltip'; import { translate } from '../../helpers/l10n'; @@ -31,41 +32,37 @@ interface Props { } export default function UsersList({ identityProviders, users, manageProvider }: Props) { + const header = ( + + {translate('users.user_name')} + {translate('my_profile.scm_accounts')} + {translate('users.last_connection')} + + {translate('users.last_sonarlint_connection')} + + + + + {translate('my_profile.groups')} + {translate('users.tokens')} + {(manageProvider === undefined || users.some((u) => !u.managed)) && ( + {translate('actions')} + )} + + ); + return ( -
- - - - - - - - - - {(manageProvider === undefined || users.some((u) => !u.managed)) && ( - - )} - - - - {users.map((user) => ( - user.externalProvider === provider.key, - )} - key={user.login} - user={user} - manageProvider={manageProvider} - /> - ))} - -
{translate('users.user_name')}{translate('my_profile.scm_accounts')}{translate('users.last_connection')} - {translate('users.last_sonarlint_connection')} - - {translate('my_profile.groups')}{translate('users.tokens')}{translate('actions')}
-
+ + {users.map((user) => ( + user.externalProvider === provider.key, + )} + key={user.login} + user={user} + manageProvider={manageProvider} + /> + ))} +
); } diff --git a/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-it.tsx b/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-it.tsx index 7dfaebb95b2..33a0be26b46 100644 --- a/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-it.tsx @@ -52,7 +52,7 @@ const ui = { aliceUpdateGroupButton: byRole('button', { name: 'users.update_users_groups.alice.merveille' }), aliceUpdateButton: byRole('button', { name: 'users.manage_user.alice.merveille' }), denisUpdateButton: byRole('button', { name: 'users.manage_user.denis.villeneuve' }), - alicedDeactivateButton: byRole('button', { name: 'users.deactivate' }), + alicedDeactivateButton: byRole('menuitem', { name: 'users.deactivate' }), bobUpdateGroupButton: byRole('button', { name: 'users.update_users_groups.bob.marley' }), bobUpdateButton: byRole('button', { name: 'users.manage_user.bob.marley' }), scmAddButton: byRole('button', { name: 'add_verb' }), @@ -70,31 +70,34 @@ const ui = { name: `remove_x.users.create_user.scm_account_${value ? `x.${value}` : 'new'}`, }), userRows: byRole('row', { - name: (accessibleName) => /^[A-Z]+ /.test(accessibleName), + name: (accessibleName) => /^[A-Z]+[a-z]*/.test(accessibleName), }), aliceRow: byRole('row', { - name: (accessibleName) => accessibleName.startsWith('AM Alice Merveille alice.merveille '), + name: (accessibleName) => + accessibleName.startsWith('Alice Merveille Alice Merveille alice.merveille '), }), aliceRowWithLocalBadge: byRole('row', { name: (accessibleName) => accessibleName.startsWith( - 'AM Alice Merveille alice.merveille alice.merveille@wonderland.com local ', + 'Alice Merveille Alice Merveille alice.merveille alice.merveille@wonderland.com local ', ), }), bobRow: byRole('row', { - name: (accessibleName) => accessibleName.startsWith('BM Bob Marley bob.marley '), + name: (accessibleName) => accessibleName.startsWith('Bob Marley Bob Marley bob.marley '), }), charlieRow: byRole('row', { - name: (accessibleName) => accessibleName.startsWith('CC Charlie Cox charlie.cox'), + name: (accessibleName) => accessibleName.startsWith('Charlie Cox Charlie Cox charlie.cox'), }), denisRow: byRole('row', { - name: (accessibleName) => accessibleName.startsWith('DV Denis Villeneuve denis.villeneuve '), + name: (accessibleName) => + accessibleName.startsWith('Denis Villeneuve Denis Villeneuve denis.villeneuve '), }), evaRow: byRole('row', { - name: (accessibleName) => accessibleName.startsWith('EG Eva Green eva.green '), + name: (accessibleName) => accessibleName.startsWith('Eva Green Eva Green eva.green '), }), franckRow: byRole('row', { - name: (accessibleName) => accessibleName.startsWith('FG Franck Grillo franck.grillo '), + name: (accessibleName) => + accessibleName.startsWith('Franck Grillo Franck Grillo franck.grillo '), }), jackRow: byRole('row', { name: /Jack/ }), @@ -338,7 +341,7 @@ describe('in non managed mode', () => { renderUsersApp(); await user.click(await ui.aliceUpdateButton.find()); - await user.click(await ui.aliceRow.byRole('button', { name: 'update_details' }).find()); + await user.click(await ui.aliceRow.byRole('menuitem', { name: 'update_details' }).find()); expect(await ui.dialogUpdateUser.find()).toBeInTheDocument(); expect(ui.userNameInput.get()).toHaveValue('Alice Merveille'); @@ -357,7 +360,7 @@ describe('in non managed mode', () => { renderUsersApp(); await user.click(await ui.aliceUpdateButton.find()); - await user.click(await ui.aliceRow.byRole('button', { name: 'users.deactivate' }).find()); + await user.click(await ui.aliceRow.byRole('menuitem', { name: 'users.deactivate' }).find()); expect(await ui.dialogDeactivateUser.find()).toBeInTheDocument(); expect(ui.deleteUserAlert.query()).not.toBeInTheDocument(); await user.click(ui.deleteUserCheckbox.get()); @@ -375,7 +378,7 @@ describe('in non managed mode', () => { await user.click(await ui.aliceUpdateButton.find()); await user.click( - await ui.aliceRow.byRole('button', { name: 'my_profile.password.title' }).find(), + await ui.aliceRow.byRole('menuitem', { name: 'my_profile.password.title' }).find(), ); expect(await ui.dialogPasswords.find()).toBeInTheDocument(); @@ -427,7 +430,7 @@ describe('in non managed mode', () => { renderUsersApp([], currentUser); await user.click(await ui.denisUpdateButton.find()); - await user.click(await ui.denisRow.byRole('button', { name: 'update_details' }).find()); + await user.click(await ui.denisRow.byRole('menuitem', { name: 'update_details' }).find()); expect(await ui.dialogUpdateUser.find()).toBeInTheDocument(); expect(ui.userNameInput.get()).toHaveValue('Denis Villeneuve'); @@ -447,7 +450,7 @@ describe('in non managed mode', () => { expect(await ui.aliceRow.byText('alice.merveille@wonderland.com').find()).toBeInTheDocument(); await user.click(await ui.aliceUpdateButton.find()); - await user.click(await ui.aliceRow.byRole('button', { name: 'update_details' }).find()); + await user.click(await ui.aliceRow.byRole('menuitem', { name: 'update_details' }).find()); expect(await ui.dialogUpdateUser.find()).toBeInTheDocument(); expect(ui.emailInput.get()).toHaveValue('alice.merveille@wonderland.com'); @@ -498,7 +501,7 @@ describe('in manage mode', () => { ui.bobRow.byRole('button', { name: 'my_profile.password.title' }).query(), ).not.toBeInTheDocument(); - await user.click(ui.bobRow.byRole('button', { name: 'update_scm' }).get()); + await user.click(ui.bobRow.byRole('menuitem', { name: 'update_scm' }).get()); expect(ui.userNameInput.get()).toBeDisabled(); expect(ui.emailInput.get()).toBeDisabled(); @@ -691,7 +694,7 @@ it('accessibility', async () => { // user update dialog should be accessible await user.click(await ui.aliceUpdateButton.find()); - await user.click(await ui.aliceRow.byRole('button', { name: 'update_details' }).find()); + await user.click(await ui.aliceRow.byRole('menuitem', { name: 'update_details' }).find()); expect(await ui.dialogUpdateUser.find()).toBeInTheDocument(); await expect(await ui.dialogUpdateUser.find()).toHaveNoA11yViolations(); await user.click(ui.cancelButton.get()); @@ -711,7 +714,7 @@ it('accessibility', async () => { // user password dialog should be accessible await user.click(await ui.aliceUpdateButton.find()); await user.click( - await ui.aliceRow.byRole('button', { name: 'my_profile.password.title' }).find(), + await ui.aliceRow.byRole('menuitem', { name: 'my_profile.password.title' }).find(), ); expect(await ui.dialogPasswords.find()).toBeInTheDocument(); await expect(await ui.dialogPasswords.find()).toHaveNoA11yViolations(); diff --git a/server/sonar-web/src/main/js/apps/users/components/UserActions.tsx b/server/sonar-web/src/main/js/apps/users/components/UserActions.tsx index 1d1fc383f53..2bac4faced2 100644 --- a/server/sonar-web/src/main/js/apps/users/components/UserActions.tsx +++ b/server/sonar-web/src/main/js/apps/users/components/UserActions.tsx @@ -17,11 +17,15 @@ * 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, + ItemDangerButton, + ItemDivider, + PopupZLevel, +} from 'design-system'; import * as React from 'react'; -import ActionsDropdown, { - ActionsDropdownDivider, - ActionsDropdownItem, -} from '../../../components/controls/ActionsDropdown'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { Provider } from '../../../types/types'; import { RestUserDetailed, isUserActive } from '../../../types/users'; @@ -45,28 +49,29 @@ export default function UserActions(props: Props) { return ( <> - - setOpenForm('update')}> + + setOpenForm('update')}> {isInstanceManaged ? translate('update_scm') : translate('update_details')} - + {!isInstanceManaged && user.local && ( - setOpenForm('password')} - > + setOpenForm('password')}> {translate('my_profile.password.title')} - + )} - - {isUserActive(user) && !isInstanceManaged && } + {isUserActive(user) && !isInstanceManaged && } {isUserActive(user) && (!isInstanceManaged || isUserLocal) && ( - setOpenForm('deactivate')} > {translate('users.deactivate')} - + )} {openForm === 'deactivate' && isUserActive(user) && ( diff --git a/server/sonar-web/src/main/js/apps/users/components/UserListItem.tsx b/server/sonar-web/src/main/js/apps/users/components/UserListItem.tsx index 4c61f7830b7..3a429d3287a 100644 --- a/server/sonar-web/src/main/js/apps/users/components/UserListItem.tsx +++ b/server/sonar-web/src/main/js/apps/users/components/UserListItem.tsx @@ -17,12 +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 { + Avatar, + ContentCell, + InteractiveIcon, + MenuIcon, + Spinner, + TableRow, + Tooltip, +} from 'design-system'; import * as React from 'react'; -import { ButtonIcon } from '../../../components/controls/buttons'; -import BulletListIcon from '../../../components/icons/BulletListIcon'; import DateFromNow from '../../../components/intl/DateFromNow'; -import LegacyAvatar from '../../../components/ui/LegacyAvatar'; -import Spinner from '../../../components/ui/Spinner'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { useUserGroupsCountQuery, useUserTokensQuery } from '../../../queries/users'; import { IdentityProvider, Provider } from '../../../types/types'; @@ -56,61 +61,63 @@ export default function UserListItem(props: UserListItemProps) { const { data: groupsCount, isLoading: groupsAreLoading } = useUserGroupsCountQuery(login); return ( - - + +
- +
- - +
+ - - + + - - + + - - + + {groupsCount} {manageProvider === undefined && ( - setOpenGroupForm(true)} - tooltip={translate('users.update_groups')} - > - - + + setOpenGroupForm(true)} + size="small" + /> + )} - - + + {tokens?.length} - setOpenTokenForm(true)} - tooltip={translateWithParameters('users.update_tokens')} - aria-label={translateWithParameters('users.update_tokens_for_x', name ?? login)} - > - - + + setOpenTokenForm(true)} + size="small" + /> + - + - + - + {openTokenForm && setOpenTokenForm(false)} user={user} />} {openGroupForm && setOpenGroupForm(false)} user={user} />} - +
); } diff --git a/server/sonar-web/src/main/js/apps/users/components/UserListItemIdentity.tsx b/server/sonar-web/src/main/js/apps/users/components/UserListItemIdentity.tsx index 08fa00509c6..60de244e2ae 100644 --- a/server/sonar-web/src/main/js/apps/users/components/UserListItemIdentity.tsx +++ b/server/sonar-web/src/main/js/apps/users/components/UserListItemIdentity.tsx @@ -17,7 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { getTextColor } from 'design-system'; + +import { Badge, Note, getTextColor } from 'design-system'; import * as React from 'react'; import { colors } from '../../../app/theme'; import { translate } from '../../../helpers/l10n'; @@ -34,17 +35,15 @@ export interface Props { export default function UserListItemIdentity({ identityProvider, user, manageProvider }: Props) { return (
-
- {user.name} - {user.login} +
+ {user.name} + {user.login}
- {user.email &&
{user.email}
} + {user.email &&
{user.email}
} {!user.local && user.externalProvider !== 'sonarqube' && ( )} - {!user.managed && manageProvider !== undefined && ( - {translate('local')} - )} + {!user.managed && manageProvider !== undefined && {translate('local')}}
); } @@ -52,7 +51,7 @@ export default function UserListItemIdentity({ identityProvider, user, managePro export function ExternalProvider({ identityProvider, user }: Omit) { if (!identityProvider) { return ( -
+
{user.externalProvider}: {user.externalLogin} @@ -61,9 +60,8 @@ export function ExternalProvider({ identityProvider, user }: Omit +
{identityProvider.name} { const { scmAccounts } = this.props; const limit = scmAccounts.length > SCM_LIMIT ? SCM_LIMIT - 1 : SCM_LIMIT; return ( -
    - {scmAccounts.slice(0, limit).map((scmAccount, idx) => ( -
  • + -- 2.39.5