diff --git a/apps/meteor/client/views/admin/users/AdminUsersPage.tsx b/apps/meteor/client/views/admin/users/AdminUsersPage.tsx index a38c3a7a439d6..46ed046fc5e1e 100644 --- a/apps/meteor/client/views/admin/users/AdminUsersPage.tsx +++ b/apps/meteor/client/views/admin/users/AdminUsersPage.tsx @@ -148,7 +148,11 @@ const AdminUsersPage = (): ReactElement => { [Story.storyName || 'Story', Story]); + +test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => { + const { baseElement } = render(, { wrapper: mockAppRoot().build() }); + expect(baseElement).toMatchSnapshot(); +}); + +test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => { + const { container } = render(, { wrapper: mockAppRoot().build() }); + + // TODO: Needed to skip `button-name` because fuselage‘s `Pagination` buttons are missing names + const results = await axe(container, { rules: { 'button-name': { enabled: false } } }); + expect(results).toHaveNoViolations(); +}); + const createFakeAdminUser = (freeSwitchExtension?: string) => createFakeUser({ active: true, @@ -17,7 +36,11 @@ it('should not render voip extension column when voice call is disabled', async render( []} + total={1} setUserFilters={() => undefined} tab='all' onReload={() => undefined} @@ -44,7 +67,11 @@ it('should not render voip extension column or actions if user doesnt have the r render( []} + total={1} setUserFilters={() => undefined} tab='all' onReload={() => undefined} @@ -71,7 +98,11 @@ it('should render "Unassign_extension" button when user has a associated extensi render( []} + total={1} setUserFilters={() => undefined} tab='all' onReload={() => undefined} @@ -98,7 +129,11 @@ it('should render "Assign_extension" button when user has no associated extensio render( []} + total={1} setUserFilters={() => undefined} tab='all' onReload={() => undefined} diff --git a/apps/meteor/client/views/admin/users/UsersTable/UsersTable.stories.tsx b/apps/meteor/client/views/admin/users/UsersTable/UsersTable.stories.tsx new file mode 100644 index 0000000000000..a892b23248fe1 --- /dev/null +++ b/apps/meteor/client/views/admin/users/UsersTable/UsersTable.stories.tsx @@ -0,0 +1,85 @@ +import { UserStatus } from '@rocket.chat/core-typings'; +import type { Meta, StoryFn } from '@storybook/react'; + +import UsersTable from './UsersTable'; + +export default { + title: 'views/admin/UsersTable', + component: UsersTable, +} satisfies Meta; + +const Template: StoryFn = (args) => ; + +export const Default = Template.bind({}); +Default.args = { + users: [ + { + _id: '1', + username: 'example.user', + name: 'Example User', + emails: [{ address: 'example@rocket.chat', verified: true }], + status: UserStatus.ONLINE, + roles: ['user'], + active: true, + type: '', + }, + { + _id: '2', + username: 'john.doe', + name: 'John Doe', + emails: [{ address: 'john@rocket.chat', verified: true }], + status: UserStatus.OFFLINE, + roles: ['admin', 'user'], + active: true, + type: '', + }, + { + _id: '3', + username: 'sarah.smith', + name: 'Sarah Smith', + emails: [{ address: 'sarah@rocket.chat', verified: true }], + status: UserStatus.AWAY, + roles: ['user'], + active: true, + type: '', + }, + { + _id: '4', + username: 'mike.wilson', + name: 'Mike Wilson', + emails: [{ address: 'mike@rocket.chat', verified: false }], + status: UserStatus.BUSY, + roles: ['user'], + active: true, + type: '', + }, + { + _id: '5', + username: 'emma.davis', + name: 'Emma Davis', + emails: [{ address: 'emma@rocket.chat', verified: true }], + status: UserStatus.ONLINE, + roles: ['moderator', 'user'], + active: true, + type: '', + }, + ], + total: 5, + isLoading: false, + isSuccess: true, + tab: 'all', +}; + +export const Loading = Template.bind({}); +Loading.args = { + isLoading: true, +}; + +export const NoResults = Template.bind({}); +NoResults.args = { + users: [], + total: 0, + isLoading: false, + isError: false, + isSuccess: true, +}; diff --git a/apps/meteor/client/views/admin/users/UsersTable/UsersTable.tsx b/apps/meteor/client/views/admin/users/UsersTable/UsersTable.tsx index bd23c13b88de6..68b780462516e 100644 --- a/apps/meteor/client/views/admin/users/UsersTable/UsersTable.tsx +++ b/apps/meteor/client/views/admin/users/UsersTable/UsersTable.tsx @@ -1,10 +1,9 @@ import type { IRole, IUser, Serialized } from '@rocket.chat/core-typings'; import { Pagination } from '@rocket.chat/fuselage'; import { useEffectEvent, useBreakpoints } from '@rocket.chat/fuselage-hooks'; -import type { PaginatedResult, DefaultUserInfo } from '@rocket.chat/rest-typings'; +import type { DefaultUserInfo } from '@rocket.chat/rest-typings'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useRouter } from '@rocket.chat/ui-contexts'; -import type { UseQueryResult } from '@tanstack/react-query'; import type { ReactElement, Dispatch, SetStateAction, MouseEvent, KeyboardEvent } from 'react'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -27,16 +26,24 @@ import { useVoipExtensionPermission } from '../voip/hooks/useVoipExtensionPermis type UsersTableProps = { tab: AdminUsersTab; roleData: { roles: IRole[] } | undefined; + users: Serialized[]; + total: number; + isLoading: boolean; + isError: boolean; + isSuccess: boolean; onReload: () => void; setUserFilters: Dispatch>; - filteredUsersQueryResult: UseQueryResult[] }>>; paginationData: ReturnType; sortData: ReturnType>; isSeatsCapExceeded: boolean; }; const UsersTable = ({ - filteredUsersQueryResult, + users, + total, + isLoading, + isError, + isSuccess, setUserFilters, roleData, tab, @@ -52,11 +59,6 @@ const UsersTable = ({ const isMobile = !breakpoints.includes('xl'); const isLaptop = !breakpoints.includes('xxl'); - const { data, isLoading, isError, isSuccess } = filteredUsersQueryResult; - - const { current, itemsPerPage, setItemsPerPage, setCurrent, ...paginationProps } = paginationData; - const { sortBy, sortDirection, setSort } = sortData; - const canManageVoipExtension = useVoipExtensionPermission(); const isKeyboardEvent = (event: MouseEvent | KeyboardEvent): event is KeyboardEvent => { @@ -83,18 +85,30 @@ const UsersTable = ({ const headers = useMemo( () => [ - + {t('Name')} , - + {t('Username')} , !isLaptop && ( {t('Email')} @@ -102,12 +116,24 @@ const UsersTable = ({ ), !isLaptop && {t('Roles')}, tab === 'all' && !isMobile && ( - + {t('Registration_status')} ), tab === 'pending' && !isMobile && ( - + {t('Pending_action')} ), @@ -115,17 +141,19 @@ const UsersTable = ({ {t('Voice_call_extension')} ), - , + + {t('Actions')} + , ], - [isLaptop, isMobile, setSort, sortBy, sortDirection, t, tab, canManageVoipExtension], + [sortData, t, isLaptop, tab, isMobile, canManageVoipExtension], ); return ( @@ -145,7 +173,7 @@ const UsersTable = ({ )} - {isSuccess && data.users.length === 0 && ( + {isSuccess && users.length === 0 && ( )} - {isSuccess && !!data?.users && ( + {isSuccess && !!users && ( <> {headers} - {data.users.map((user) => ( + {users.map((user) => ( )} diff --git a/apps/meteor/client/views/admin/users/UsersTable/__snapshots__/UsersTable.spec.tsx.snap b/apps/meteor/client/views/admin/users/UsersTable/__snapshots__/UsersTable.spec.tsx.snap new file mode 100644 index 0000000000000..db5a31c2c0e06 --- /dev/null +++ b/apps/meteor/client/views/admin/users/UsersTable/__snapshots__/UsersTable.spec.tsx.snap @@ -0,0 +1,1769 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`renders Default without crashing 1`] = ` + +
+
+ +
+
+
+ All_roles + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ Name + + + + +
+
+
+ Username + + + + +
+
+
+ Actions +
+
+
+
+ +
+
+
+ + + +
+
+ Example User +
+
+
+
+
+ example.user +
+
+
+ +
+
+
+
+ +
+
+
+ + + +
+
+ John Doe +
+
+
+
+
+ john.doe +
+
+
+ +
+
+
+
+ +
+
+
+ + + +
+
+ Sarah Smith +
+
+
+
+
+ sarah.smith +
+
+
+ +
+
+
+
+ +
+
+
+ + + +
+
+ Mike Wilson +
+
+
+
+
+ mike.wilson +
+
+
+ +
+
+
+
+ +
+
+
+ + + +
+
+ Emma Davis +
+
+
+
+
+ emma.davis +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +`; + +exports[`renders Loading without crashing 1`] = ` + +
+
+ +
+
+
+ All_roles + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ Name + + + + +
+
+
+ Username + + + + +
+
+
+ Actions +
+
+
+ +
+ + +
+
+
+ + + + + + + +
+
+ +
+ + +
+
+
+ + + + + + + +
+
+ +
+ + +
+
+
+ + + + + + + +
+
+ +
+ + +
+
+
+ + + + + + + +
+
+ +
+ + +
+
+
+ + + + + + + +
+
+ +
+ + +
+
+
+ + + + + + + +
+
+ +
+ + +
+
+
+ + + + + + + +
+
+ +
+ + +
+
+
+ + + + + + + +
+
+ +
+ + +
+
+
+ + + + + + + +
+
+ +
+ + +
+
+
+ + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +`; + +exports[`renders NoResults without crashing 1`] = ` + +
+
+ +
+
+
+ All_roles + +
+
+
+
+
+
+ +

+ Users_Table_Generic_No_users +

+
+ Users_Table_no_undefined_users_description +
+
+
+
+
+
+
+
+
+
+ + + + + + + + + +
+
+ Name + + + + +
+
+
+ Username + + + + +
+
+
+ Actions +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +`;