diff --git a/src/features/organization/roles/constants/tableDefinition.test.tsx b/src/features/organization/roles/constants/tableDefinition.test.tsx new file mode 100644 index 000000000..a57da6fd2 --- /dev/null +++ b/src/features/organization/roles/constants/tableDefinition.test.tsx @@ -0,0 +1,67 @@ +/** + * @vitest-environment jsdom + */ +import { SimpleBrowseDataTable } from '@/components/SimpleBrowseDataTable'; +import { SchemaOrganizationRole } from '@/integrations/api/api.gen'; +import { act, cleanup, fireEvent, render, screen } from '@testing-library/react'; +import { afterEach, describe, expect, it } from 'vitest'; +import { dataTableColumns } from './tableDefinition'; + +afterEach(() => cleanup()); + +function role(id: string, roleName: string, userCount: number): SchemaOrganizationRole { + return { + id, + organizationId: 'org-1', + roleName, + userIds: Array.from({ length: userCount }, (_, index) => `user-${index}`), + }; +} + +function renderedRoleNames() { + return Array.from(document.querySelectorAll('tbody tr')).map((row) => row.querySelector('td')?.textContent); +} + +function renderRoles(roles: SchemaOrganizationRole[]) { + render( + , + ); +} + +async function typeFilter(text: string) { + await act(async () => { + fireEvent.change(screen.getByRole('searchbox', { name: 'Filter roles' }), { target: { value: text } }); + }); +} + +describe('org roles table columns', () => { + it('filters by role name, not by id or user count', async () => { + renderRoles([role('ab12', 'Admin', 1), role('cd34', 'Developer', 12), role('ef56', 'Viewer', 0)]); + + await typeFilter('dev'); + expect(renderedRoleNames()).toEqual(['Developer']); + + await typeFilter('ab12'); + expect(screen.getByText('No results.')).toBeTruthy(); + + await typeFilter('12'); + expect(screen.getByText('No results.')).toBeTruthy(); + }); + + it('sorts by how many users hold the role, showing a role nobody holds as unassigned', () => { + renderRoles([role('a', 'Admin', 2), role('b', 'Developer', 12), role('c', 'Viewer', 0)]); + + fireEvent.click(screen.getByRole('button', { name: 'Users Assigned' })); + + expect(renderedRoleNames()).toEqual(['Viewer', 'Admin', 'Developer']); + expect(screen.getByText('No users assigned')).toBeTruthy(); + }); + + it('sorts by role name', () => { + renderRoles([role('a', 'viewer', 0), role('b', 'Admin', 0), role('c', 'developer', 0)]); + + fireEvent.click(screen.getByRole('button', { name: 'Role Name' })); + + expect(renderedRoleNames()).toEqual(['Admin', 'developer', 'viewer']); + }); +}); diff --git a/src/features/organization/roles/constants/tableDefinition.tsx b/src/features/organization/roles/constants/tableDefinition.tsx index ecacdba0f..9bc2eb74f 100644 --- a/src/features/organization/roles/constants/tableDefinition.tsx +++ b/src/features/organization/roles/constants/tableDefinition.tsx @@ -1,34 +1,34 @@ import { SchemaOrganizationRole } from '@/integrations/api/api.gen'; -import { ColumnDef, createColumnHelper } from '@/lib/table'; +import { ColumnDef } from '@/lib/table'; -const columnHelper = createColumnHelper(); +function assignedUserCount(role: SchemaOrganizationRole) { + return role.userIds?.length ?? 0; +} export const dataTableColumns: Array> = [ { header: 'Role Name', accessorKey: 'roleName', - enableSorting: false, + enableSorting: true, }, - columnHelper.display({ + { header: 'ID', - id: 'id', - enableSorting: false, + accessorKey: 'id', + enableSorting: true, + enableGlobalFilter: false, // Role names are not unique; surfacing the id makes roles that share a name distinguishable. cell: (props) => ( {props.row.original.id} ), - }), - columnHelper.display({ + }, + { header: 'Users Assigned', - enableSorting: false, id: 'userIds', - cell: (props) => { - if (!props.row.original.userIds || props.row.original.userIds.length === 0) { - return 'No users assigned'; - } - return props.row.original.userIds.length; - }, - }), + accessorFn: assignedUserCount, + enableSorting: true, + enableGlobalFilter: false, + cell: (props) => assignedUserCount(props.row.original) || 'No users assigned', + }, ]; diff --git a/src/features/organization/roles/index.tsx b/src/features/organization/roles/index.tsx index 2ef60b8f7..c9ba93bb6 100644 --- a/src/features/organization/roles/index.tsx +++ b/src/features/organization/roles/index.tsx @@ -75,7 +75,13 @@ export function OrgConfigRolesIndex() { }> - +