Skip to content

Commit 74ee431

Browse files
jacobo-dominguez-wguarbrandes
authored andcommitted
feat: modifications on the authz title component
Removing the allow public read switch. Updated assign role button text. Title and subtitle appears in the same line.
1 parent 87adaa2 commit 74ee431

14 files changed

Lines changed: 41 additions & 273 deletions

src/authz-module/components/AuthZTitle.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ describe('AuthZTitle', () => {
4040

4141
it('renders page title', () => {
4242
render(<AuthZTitle {...defaultProps} />);
43-
expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent(defaultProps.pageTitle);
43+
expect(screen.getByRole('heading', { level: 2 })).toHaveTextContent(defaultProps.pageTitle);
4444
});
4545

4646
it('renders page subtitle as ReactNode', () => {

src/authz-module/components/AuthZTitle.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
} from 'react';
44
import { Link } from 'react-router-dom';
55
import {
6-
Breadcrumb, Col, Container, Row, Button, Badge,
6+
Breadcrumb, Col, Container, Row, Button,
77
Stack,
88
useMediaQuery,
99
breakpoints,
@@ -50,10 +50,13 @@ const AuthZTitle = ({
5050
/>
5151
<Row className="mt-4">
5252
<Col xs={12} md={7} className="mb-4">
53-
<h1 className="text-primary">{pageTitle}</h1>
54-
{typeof pageSubtitle === 'string'
55-
? <h3><Badge className="py-2 px-3 font-weight-normal" variant="light">{pageSubtitle}</Badge></h3>
56-
: pageSubtitle}
53+
<div className="d-flex align-items-center">
54+
<h2 className="text-primary mb-0">{pageTitle}</h2>
55+
{typeof pageSubtitle === 'string'
56+
? <><hr className="mx-lg-3" /><h3 className="mb-0 py-2 font-weight-light text-gray-700">{pageSubtitle}</h3></>
57+
: <><hr className="mx-lg-3" /> <div className="mb-0">{pageSubtitle}</div></>}
58+
59+
</div>
5760
</Col>
5861
<Col xs={12} md={5}>
5962
<Stack className="justify-content-end" direction={isDesktop ? 'horizontal' : 'vertical'}>

src/authz-module/data/api.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
22
import { LibraryMetadata, TeamMember } from '@src/types';
3-
import { camelCaseObject, snakeCaseObject } from '@edx/frontend-platform';
3+
import { camelCaseObject } from '@edx/frontend-platform';
44
import { getApiUrl, getStudioApiUrl } from '@src/data/utils';
55

66
export interface QuerySettings {
@@ -108,17 +108,3 @@ export const revokeUserRoles = async (
108108
const res = await getAuthenticatedHttpClient().delete(url.toString());
109109
return camelCaseObject(res.data);
110110
};
111-
112-
export const updateLibrary = async (libraryId, updatedData): Promise<LibraryMetadata> => {
113-
const { data } = await getAuthenticatedHttpClient().patch(
114-
getStudioApiUrl(`/api/libraries/v2/${libraryId}/`),
115-
snakeCaseObject(updatedData),
116-
);
117-
return {
118-
id: data.id,
119-
org: data.org,
120-
title: data.title,
121-
slug: data.slug,
122-
allowPublicRead: data.allow_public_read,
123-
};
124-
};

src/authz-module/data/hooks.test.tsx

Lines changed: 0 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
44
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
55
import {
66
useLibrary, usePermissionsByRole, useTeamMembers, useAssignTeamMembersRole, useRevokeUserRoles,
7-
useUpdateLibrary,
87
} from './hooks';
98

109
jest.mock('@edx/frontend-platform/auth', () => ({
@@ -341,80 +340,3 @@ describe('useRevokeUserRoles', () => {
341340
expect(calledUrl.searchParams.get('scope')).toBe(revokeRoleData.scope);
342341
});
343342
});
344-
345-
describe('useUpdateLibrary', () => {
346-
const queryKeyTest = ['org.openedx.frontend.app.adminConsole', 'authz', 'library', 'lib:123'];
347-
348-
beforeEach(() => {
349-
jest.clearAllMocks();
350-
});
351-
352-
it('calls updateLibrary with correct params and updates cache', async () => {
353-
const mockData = { id: 'lib:123', title: 'Library Test' };
354-
getAuthenticatedHttpClient.mockReturnValue({
355-
patch: jest.fn().mockResolvedValue({ data: mockData }),
356-
});
357-
const { result } = renderHook(() => useUpdateLibrary(), { wrapper: createWrapper() });
358-
359-
await act(async () => {
360-
await result.current.mutateAsync({
361-
libraryId: 'lib:123',
362-
updatedData: { title: 'Library Test' },
363-
});
364-
});
365-
366-
expect(getAuthenticatedHttpClient).toHaveBeenCalled();
367-
});
368-
369-
it('sets query data on success', async () => {
370-
const mockData = { id: 'lib:123', title: 'Updated Library' };
371-
getAuthenticatedHttpClient.mockReturnValue({
372-
patch: jest.fn().mockResolvedValue({ data: mockData }),
373-
});
374-
375-
const queryClient = new QueryClient();
376-
const wrapper = ({ children }: { children: ReactNode }) => (
377-
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
378-
);
379-
380-
const { result } = renderHook(() => useUpdateLibrary(), { wrapper });
381-
382-
await act(async () => {
383-
await result.current.mutateAsync({
384-
libraryId: 'lib:123',
385-
updatedData: { title: 'Updated Library' },
386-
});
387-
});
388-
389-
// verify cache updated with the returned data
390-
expect(queryClient.getQueryData(queryKeyTest)).toEqual(mockData);
391-
expect(getAuthenticatedHttpClient).toHaveBeenCalled();
392-
});
393-
394-
it('invalidates query on settled', async () => {
395-
const mockData = { id: 'lib:123', title: 'Final Title' };
396-
getAuthenticatedHttpClient.mockReturnValue({
397-
patch: jest.fn().mockResolvedValue({ data: mockData }),
398-
});
399-
const queryClient = new QueryClient();
400-
const invalidateSpy = jest.spyOn(queryClient, 'invalidateQueries');
401-
402-
const wrapper = ({ children }: { children: ReactNode }) => (
403-
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
404-
);
405-
406-
const { result } = renderHook(() => useUpdateLibrary(), { wrapper });
407-
408-
await act(async () => {
409-
await result.current.mutateAsync({
410-
libraryId: 'lib:123',
411-
updatedData: { title: 'Final Title' },
412-
});
413-
});
414-
415-
expect(invalidateSpy).toHaveBeenCalledWith({
416-
queryKey: queryKeyTest,
417-
});
418-
expect(getAuthenticatedHttpClient).toHaveBeenCalled();
419-
});
420-
});

src/authz-module/data/hooks.ts

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { LibraryMetadata } from '@src/types';
66
import {
77
assignTeamMembersRole, AssignTeamMembersRoleRequest, getLibrary, getPermissionsByRole, getTeamMembers,
88
GetTeamMembersResponse, PermissionsByRole, QuerySettings, revokeUserRoles, RevokeUserRolesRequest,
9-
updateLibrary,
109
} from './api';
1110

1211
const authzQueryKeys = {
@@ -111,28 +110,3 @@ export const useRevokeUserRoles = () => {
111110
},
112111
});
113112
};
114-
115-
/**
116-
* React Query hook to update the library metadata.
117-
*
118-
* @example
119-
* const { mutate: updateLibrary } = useUpdateLibrary();
120-
* updateLibrary({ libraryId: 'lib:123', updatedData: { title: 'Library Test' }});
121-
*/
122-
123-
export const useUpdateLibrary = () => {
124-
const queryClient = useQueryClient();
125-
126-
return useMutation({
127-
mutationFn: ({ libraryId, updatedData }: {
128-
libraryId: string;
129-
updatedData: Partial<LibraryMetadata>
130-
}) => updateLibrary(libraryId, updatedData),
131-
onSuccess: (data) => {
132-
queryClient.setQueryData(authzQueryKeys.library(data.id), data);
133-
},
134-
onSettled: (_data, _error, variables) => {
135-
queryClient.invalidateQueries({ queryKey: authzQueryKeys.library(variables.libraryId) });
136-
},
137-
});
138-
};

src/authz-module/libraries-manager/LibrariesTeamManager.test.tsx

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -150,49 +150,6 @@ describe('LibrariesTeamManager', () => {
150150
expect(matrixScope.getByText('view')).toBeInTheDocument();
151151
});
152152

153-
it('renders allow public library read toggle and change the value by user interaction', async () => {
154-
const user = userEvent.setup();
155-
156-
renderTeamManager();
157-
158-
const readPublicToggle = await screen.findByRole('switch', { name: /Allow public read/i });
159-
160-
await user.click(readPublicToggle);
161-
expect(mutate).toHaveBeenCalledWith(
162-
{
163-
libraryId: 'lib-001',
164-
updatedData: { allowPublicRead: !libraryData.allowPublicRead },
165-
},
166-
expect.objectContaining({
167-
onSuccess: expect.any(Function),
168-
}),
169-
);
170-
const { onSuccess } = (mutate as jest.Mock).mock.calls[0][1];
171-
onSuccess?.();
172-
173-
expect(await screen.findByText(/updated successfully/i)).toBeInTheDocument();
174-
});
175-
176-
it('should not render the toggle if the user can not manage team and the Library Public Read is disabled', () => {
177-
(useLibrary as jest.Mock).mockReturnValue({ data: { ...libraryData, allowPublicRead: false } });
178-
(useLibraryAuthZ as jest.Mock).mockReturnValue({ ...libraryAuthZContext, canManageTeam: false });
179-
180-
renderTeamManager();
181-
expect(screen.queryByRole('switch', { name: /Allow public read/i })).not.toBeInTheDocument();
182-
});
183-
184-
it('should render the toggle as disabled if the user can not manage team but the Library Public Read is enabled', async () => {
185-
(useLibrary as jest.Mock).mockReturnValue({ data: { ...libraryData, allowPublicRead: true } });
186-
(useLibraryAuthZ as jest.Mock).mockReturnValue({ ...libraryAuthZContext, canManageTeam: false });
187-
188-
renderTeamManager();
189-
190-
const readPublicToggle = await screen.findByRole('switch', { name: /Allow public read/i });
191-
192-
expect(readPublicToggle).toBeInTheDocument();
193-
expect(readPublicToggle).toBeDisabled();
194-
});
195-
196153
it('renders correct navigation link label and URL on breadcrumb', () => {
197154
renderTeamManager();
198155
const navLink = screen.getByRole('link', { name: 'Manage Access' });

src/authz-module/libraries-manager/LibrariesTeamManager.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import { AddNewTeamMemberTrigger } from './components/AddNewTeamMemberModal';
1515
import { buildPermissionMatrixByResource, buildPermissionMatrixByRole } from './utils';
1616

1717
import messages from './messages';
18-
import PublicReadToggle from './components/PublicReadToggle';
1918

2019
const LibrariesTeamManager = () => {
2120
const intl = useIntl();
@@ -51,7 +50,7 @@ const LibrariesTeamManager = () => {
5150
pageTitle={pageTitle}
5251
pageSubtitle={libraryId}
5352
actions={
54-
[<PublicReadToggle libraryId={libraryId} canEditToggle={canManageTeam} key="allow-public-read" />,
53+
[
5554
...(canManageTeam ? [<AddNewTeamMemberTrigger libraryId={libraryId} key="add-new-member" />] : []),
5655
]
5756
}

src/authz-module/libraries-manager/LibrariesUserManager.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ describe('LibrariesUserManager', () => {
133133
expect(screen.getByText('Library Team Management')).toBeInTheDocument();
134134
expect(screen.getByRole('listitem', { current: 'page' })).toHaveTextContent('testuser');
135135
// Page title and subtitle
136-
expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent('testuser');
137-
expect(screen.getByRole('paragraph')).toHaveTextContent('[email protected]');
136+
expect(screen.getByRole('heading', { level: 2 })).toHaveTextContent('testuser');
137+
expect(screen.getByRole('heading', { level: 3 })).toHaveTextContent('[email protected]');
138138

139139
expect(screen.getByText('Admin')).toBeInTheDocument();
140140
expect(screen.getByText('Instructor')).toBeInTheDocument();

src/authz-module/libraries-manager/LibrariesUserManager.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ const LibrariesUserManager = () => {
152152
navLinks={[{ label: rootBreadcrumb, to: teamMembersPath }, { label: pageManageTitle, to: teamMembersPath }]}
153153
activeLabel={user?.username || ''}
154154
pageTitle={user?.username || ''}
155-
pageSubtitle={<p>{user?.email}</p>}
155+
pageSubtitle={user?.email || ''}
156156
actions={user && canManageTeam
157157
? [<AssignNewRoleTrigger
158158
username={user.username}

0 commit comments

Comments
 (0)