From a93c5a513bcdf777563510df5280d02205403db0 Mon Sep 17 00:00:00 2001 From: "Eyo O. Eyo" <7893459+eokoneyo@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:28:50 +0100 Subject: [PATCH] [Spaces] Open 'manage roles' link for spaces assign role flyout in new tab (#199506) ## Summary Closes https://github.com/elastic/kibana-team/issues/1281 Modify the "manage role" link in the assign roles to space tab, so it opens the roles screen in a new tab, with an improvement so that on transitioning back to the assign roles space tab the user is presented with an updated list of roles created in the page that was created opened, if any. Caveat: This approach will continually make calls to refresh the role list on every page visibility event that matches the conditions provided until the flyout gets closed. ##### Visuals https://github.com/user-attachments/assets/64cb296d-246d-4033-a655-7b10d0dafab1 ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../space_assign_role_privilege_form.test.tsx | 28 +++++++++++- .../space_assign_role_privilege_form.tsx | 43 ++++++++++++++++--- 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/spaces/public/management/edit_space/roles/component/space_assign_role_privilege_form.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/roles/component/space_assign_role_privilege_form.test.tsx index 1c97b9c4d2bc6..d35bdbee4b8bb 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/roles/component/space_assign_role_privilege_form.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/roles/component/space_assign_role_privilege_form.test.tsx @@ -6,7 +6,7 @@ */ import { EuiThemeProvider } from '@elastic/eui'; -import { render, screen, waitFor, within } from '@testing-library/react'; +import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import crypto from 'crypto'; import React from 'react'; @@ -142,6 +142,15 @@ describe('PrivilegesRolesForm', () => { jest.clearAllMocks(); }); + it("would open the 'manage roles' link in a new tab", () => { + getRolesSpy.mockResolvedValue([]); + getAllKibanaPrivilegeSpy.mockResolvedValue(createRawKibanaPrivileges(kibanaFeatures)); + + renderPrivilegeRolesForm(); + + expect(screen.getByText('Manage roles')).toHaveAttribute('target', '_blank'); + }); + it('does not display the privilege selection buttons or customization form when no role is selected', async () => { getRolesSpy.mockResolvedValue([]); getAllKibanaPrivilegeSpy.mockResolvedValue(createRawKibanaPrivileges(kibanaFeatures)); @@ -170,6 +179,23 @@ describe('PrivilegesRolesForm', () => { expect(screen.getByTestId('space-assign-role-create-roles-privilege-button')).toBeDisabled(); }); + it('makes a request to refetch available roles if page transitions back from a user interaction page visibility change', () => { + getRolesSpy.mockResolvedValue([]); + getAllKibanaPrivilegeSpy.mockResolvedValue(createRawKibanaPrivileges(kibanaFeatures)); + + renderPrivilegeRolesForm(); + + expect(getRolesSpy).toHaveBeenCalledTimes(1); + + // trigger click on manage roles link, which is perquisite for page visibility handler to trigger role refetch + fireEvent.click(screen.getByText(/manage roles/i)); + + // trigger page visibility change + fireEvent(document, new Event('visibilitychange')); + + expect(getRolesSpy).toHaveBeenCalledTimes(2); + }); + it('renders with the assign roles button disabled when no base privileges or feature privileges are selected', async () => { getRolesSpy.mockResolvedValue([]); getAllKibanaPrivilegeSpy.mockResolvedValue(createRawKibanaPrivileges(kibanaFeatures)); diff --git a/x-pack/plugins/spaces/public/management/edit_space/roles/component/space_assign_role_privilege_form.tsx b/x-pack/plugins/spaces/public/management/edit_space/roles/component/space_assign_role_privilege_form.tsx index e0f3e8f3714c6..74f2b2fde4667 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/roles/component/space_assign_role_privilege_form.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/roles/component/space_assign_role_privilege_form.tsx @@ -23,6 +23,7 @@ import { EuiSpacer, EuiText, EuiTitle, + useGeneratedHtmlId, } from '@elastic/eui'; import type { EuiComboBoxOptionOption } from '@elastic/eui'; import type { FC } from 'react'; @@ -81,10 +82,13 @@ export const PrivilegesRolesForm: FC = (props) => { const [selectedRoles, setSelectedRoles] = useState>( createRolesComboBoxOptions(defaultSelected) ); + const manageRoleLinkId = useGeneratedHtmlId(); const isEditOperation = useRef(Boolean(defaultSelected.length)); - useEffect(() => { - async function fetchRequiredData(spaceId: string) { + const userInvokedPageVisibilityChange = useRef(null); + + const fetchRequiredData = useCallback( + async (spaceId: string) => { setFetchingDataDeps(true); const [systemRoles, _kibanaPrivileges] = await invokeClient((clients) => @@ -109,10 +113,28 @@ export const PrivilegesRolesForm: FC = (props) => { ); setKibanaPrivileges(_kibanaPrivileges); - } + }, + [invokeClient] + ); + useEffect(() => { fetchRequiredData(space.id!).finally(() => setFetchingDataDeps(false)); - }, [invokeClient, space.id]); + }, [fetchRequiredData, invokeClient, space.id]); + + useEffect(() => { + async function visibilityChangeHandler() { + // page just transitioned back to visible state from hidden state caused by user interaction + if (userInvokedPageVisibilityChange.current && !document.hidden) { + await fetchRequiredData(space.id!).finally(() => setFetchingDataDeps(false)); + } + } + + document.addEventListener('visibilitychange', visibilityChangeHandler); + + return () => { + document.removeEventListener('visibilitychange', visibilityChangeHandler); + }; + }, [fetchRequiredData, invokeClient, space.id]); const selectedRolesCombinedPrivileges = useMemo(() => { const combinedPrivilege = new Set( @@ -345,12 +367,23 @@ export const PrivilegesRolesForm: FC = (props) => { {!isEditOperation.current && ( ) => { + // leverage event propagation, check if manage role link element was clicked + if ((event.target as HTMLFieldSetElement).id === manageRoleLinkId) { + userInvokedPageVisibilityChange.current = true; + } + }} label={i18n.translate( 'xpack.spaces.management.spaceDetails.roles.selectRolesFormRowLabel', { defaultMessage: 'Select roles' } )} labelAppend={ - + {i18n.translate( 'xpack.spaces.management.spaceDetails.roles.selectRolesFormRowLabelAnchor', { defaultMessage: 'Manage roles' }