From de18a7c5eb765352002805c5d4f8bbd94650b23c Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Tue, 6 Aug 2024 23:09:20 -0400 Subject: [PATCH 01/24] Adds initial commit to add header components and modifies get started and user list pages Signed-off-by: Darshit Chanpura --- .../header/header-components.tsx | 69 +++++++++ .../configuration/header/header-props.tsx | 40 +++++ .../apps/configuration/panels/get-started.tsx | 41 +++++- .../apps/configuration/panels/user-list.tsx | 139 +++++++++++++----- 4 files changed, 243 insertions(+), 46 deletions(-) create mode 100644 public/apps/configuration/header/header-components.tsx create mode 100644 public/apps/configuration/header/header-props.tsx diff --git a/public/apps/configuration/header/header-components.tsx b/public/apps/configuration/header/header-components.tsx new file mode 100644 index 00000000..67162001 --- /dev/null +++ b/public/apps/configuration/header/header-components.tsx @@ -0,0 +1,69 @@ +/* + * Copyright OpenSearch Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React from 'react'; +import { TopNavControlData } from 'src/plugins/navigation/public/top_nav_menu/top_nav_control_data'; +import { EuiTitle } from '@elastic/eui'; +import { ControlProps, DescriptionProps, TitleProps } from './header-props'; + +// controlType should be one of: https://github.com/AMoo-Miki/OpenSearch-Dashboards/blob/header-collective/src/plugins/navigation/public/top_nav_menu/top_nav_control_data.tsx#L91 + +export const HeaderButtonOrLink = React.memo((props: ControlProps) => { + const { HeaderControl } = props.navigation.ui; + + return ( + + ); +}); + +export const HeaderTitle = React.memo((props: TitleProps) => { + const { HeaderControl } = props.navigation.ui; + const titleData: TopNavControlData[] = [ + { + renderComponent: ( + +

+ {props.pageHeader} + {props.shouldDisplayCount ? ` (${props.count})` : null} +

+
+ ), + }, + ]; + + return ( + + ); +}); + +export const HeaderDescription = React.memo((props: DescriptionProps) => { + const { HeaderControl } = props.navigation.ui; // need to get this from SecurityPluginStartDependencies + + return ( + + ); +}); diff --git a/public/apps/configuration/header/header-props.tsx b/public/apps/configuration/header/header-props.tsx new file mode 100644 index 00000000..b65aeb8e --- /dev/null +++ b/public/apps/configuration/header/header-props.tsx @@ -0,0 +1,40 @@ +/* + * Copyright OpenSearch Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { ApplicationStart } from 'opensearch-dashboards/public'; +import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; +import { TopNavControlData } from 'src/plugins/navigation/public/top_nav_menu/top_nav_control_data'; + +export interface HeaderProps { + navigation: NavigationPublicPluginStart; + className?: string; + application: ApplicationStart; +} + +export interface ControlProps extends HeaderProps { + controls: TopNavControlData[]; + // mount: (menuMount: MountPoint | undefined) => void; +} + +export interface TitleProps extends HeaderProps { + pageHeader: string; + shouldDisplayCount?: boolean; + count?: number; +} + +export interface DescriptionProps extends HeaderProps { + description: string; + controls: TopNavControlData[]; +} diff --git a/public/apps/configuration/panels/get-started.tsx b/public/apps/configuration/panels/get-started.tsx index 4918bc16..4b13f173 100644 --- a/public/apps/configuration/panels/get-started.tsx +++ b/public/apps/configuration/panels/get-started.tsx @@ -39,6 +39,7 @@ import { createSuccessToast, createUnknownErrorToast, useToastState } from '../u import { SecurityPluginTopNavMenu } from '../top-nav-menu'; import { DataSourceContext } from '../app-router'; import { getClusterInfo } from '../../../utils/datasource-utils'; +import { HeaderButtonOrLink, HeaderTitle } from '../header/header-components'; const addBackendStep = { title: 'Add backends', @@ -165,6 +166,8 @@ export function GetStarted(props: AppDependencies) { const dataSourceEnabled = !!props.depsStart.dataSource?.dataSourceEnabled; const { dataSource, setDataSource } = useContext(DataSourceContext)!; + const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); + let steps; if (props.config.ui.backend_configurable) { steps = [addBackendStep, ...setOfSteps]; @@ -173,6 +176,17 @@ export function GetStarted(props: AppDependencies) { } const [toasts, addToast, removeToast] = useToastState(); + const buttonData = [ + { + label: 'Open in new window', + isLoading: false, + href: buildHashUrl(), + iconType: 'popout', + iconSide: 'right', + type: 'button', + // target: "_blank" + }, + ]; return ( <>
@@ -182,12 +196,27 @@ export function GetStarted(props: AppDependencies) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - - -

Get started

-
- -
+ {updatedUX ? ( + <> + + + + ) : ( + + +

Get started

+
+ +
+ )} diff --git a/public/apps/configuration/panels/user-list.tsx b/public/apps/configuration/panels/user-list.tsx index 1d1aedf3..b71e1234 100644 --- a/public/apps/configuration/panels/user-list.tsx +++ b/public/apps/configuration/panels/user-list.tsx @@ -33,6 +33,7 @@ import { } from '@elastic/eui'; import { Dictionary, difference, isEmpty, map } from 'lodash'; import React, { useContext, useState } from 'react'; +import { TopNavControlData } from 'src/plugins/navigation/public/top_nav_menu/top_nav_control_data'; import { getAuthInfo } from '../../../utils/auth-info-utils'; import { AppDependencies } from '../../types'; import { API_ENDPOINT_INTERNALUSERS, DocLinks } from '../constants'; @@ -52,6 +53,7 @@ import { buildHashUrl } from '../utils/url-builder'; import { DataSourceContext } from '../app-router'; import { SecurityPluginTopNavMenu } from '../top-nav-menu'; import { AccessErrorComponent } from '../access-error-component'; +import { HeaderButtonOrLink, HeaderTitle, HeaderDescription } from '../header/header-components'; export function dictView(items: Dictionary) { if (isEmpty(items)) { @@ -204,6 +206,37 @@ export function UserList(props: AppDependencies) { const [actionsMenu, closeActionsMenu] = useContextMenuState('Actions', {}, actionsMenuItems); + const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); + const buttonData = [ + { + label: 'Create internal user', + isLoading: false, + href: buildHashUrl(ResourceType.users, Action.create), + fill: true, + iconType: 'plus', + iconSide: 'left', + type: 'button', + testId: 'create-user', + // target: "_blank" + }, + ]; + const descriptionData: TopNavControlData[] = [ + { + isLoading: loading, + renderComponent: ( + + The Security plugin includes an internal user database. Use this database in place of, or + in addition to, an external
authentication system such as LDAP server or Active + Directory. You can map an internal user to a role from{' '} + Roles + . First, click
into the detail page of the role. Then, under “Mapped users”, click + “Manage mapping” +
+ ), + }, + ]; + + const userLen = Query.execute(query || '', userData).length; return ( <> - - -

Internal users

-
-
+ {updatedUX ? ( + <> + + + + + ) : ( + + +

Internal users

+
+
+ )} {loading ? ( ) : accessErrorFlag ? ( ) : ( - - - -

- Internal users - - {' '} - ({Query.execute(query || '', userData).length}) - -

-
- - The Security plugin includes an internal user database. Use this database in place - of, or in addition to, an external authentication system such as LDAP server or - Active Directory. You can map an internal user to a role from{' '} - Roles - . First, click into the detail page of the role. Then, under “Mapped users”, click - “Manage mapping” - -
- - - {actionsMenu} - - - Create internal user - - - - -
+ {updatedUX ? null : ( + + + +

+ Internal users + + {' '} + ({Query.execute(query || '', userData).length}) + +

+
+ + The Security plugin includes an internal user database. Use this database in place + of, or in addition to, an external authentication system such as LDAP server or + Active Directory. You can map an internal user to a role from{' '} + Roles + . First, click into the detail page of the role. Then, under “Mapped users”, click + “Manage mapping” + +
+ + + {actionsMenu} + + + Create internal user + + + + +
+ )} {actionsMenu}] : undefined, }} // @ts-ignore selection={{ onSelectionChange: setSelection }} From a773c6a5e172f3dc2d19d9f9d267f2bcab127978 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Tue, 6 Aug 2024 23:49:08 -0400 Subject: [PATCH 02/24] Updates auth-view page Signed-off-by: Darshit Chanpura --- .../panels/auth-view/auth-view.tsx | 90 ++++++++++++++----- .../panels/auth-view/instruction-view.tsx | 4 +- 2 files changed, 70 insertions(+), 24 deletions(-) diff --git a/public/apps/configuration/panels/auth-view/auth-view.tsx b/public/apps/configuration/panels/auth-view/auth-view.tsx index 8022ca26..83715e65 100644 --- a/public/apps/configuration/panels/auth-view/auth-view.tsx +++ b/public/apps/configuration/panels/auth-view/auth-view.tsx @@ -26,6 +26,7 @@ import { InstructionView } from './instruction-view'; import { DataSourceContext } from '../../app-router'; import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; import { AccessErrorComponent } from '../../access-error-component'; +import { HeaderButtonOrLink, HeaderTitle } from '../../header/header-components'; export function AuthView(props: AppDependencies) { const [authentication, setAuthentication] = React.useState([]); @@ -60,6 +61,21 @@ export function AuthView(props: AppDependencies) { fetchData(); }, [props.coreStart.http, dataSource]); + + const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); + + const buttonData = [ + { + label: 'Manage via config.yml', + isLoading: false, + href: DocLinks.BackendConfigurationDoc, + iconType: 'popout', + iconSide: 'right', + type: 'button', + // target: "_blank" + }, + ]; + if (isEmpty(authentication) && !loading) { return ( <> @@ -69,18 +85,34 @@ export function AuthView(props: AppDependencies) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {accessErrorFlag ? ( - -

Authentication and authorization

-
- ) : null} - {accessErrorFlag ? ( - + { updatedUX ? ( + <> + + { accessErrorFlag ? ( + + ) : ( + + )} + + ) : ( - + <> + +

Authentication and authorization

+
+ { accessErrorFlag ? : + } + )} ); @@ -94,17 +126,31 @@ export function AuthView(props: AppDependencies) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - - -

Authentication and authorization

-
- {!loading && !errorFlag && props.config.ui.backend_configurable && ( - - )} -
+ {updatedUX ? ( + <> + + + + ) : ( + + +

Authentication and authorization

+
+ {!loading && !errorFlag && props.config.ui.backend_configurable && ( + + )} +
)} {loading ? ( ) : accessErrorFlag ? ( diff --git a/public/apps/configuration/panels/auth-view/instruction-view.tsx b/public/apps/configuration/panels/auth-view/instruction-view.tsx index c06383e2..20985f01 100644 --- a/public/apps/configuration/panels/auth-view/instruction-view.tsx +++ b/public/apps/configuration/panels/auth-view/instruction-view.tsx @@ -22,9 +22,9 @@ import { DocLinks } from '../../constants'; export function InstructionView(props: { config: ClientConfigType }) { return ( <> - + {/*

Authentication and authorization

-
+
*/} From 128d15c97cc66b8ede9906fe7953780d3a03546b Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 7 Aug 2024 00:13:42 -0400 Subject: [PATCH 03/24] Updates user edit and create pages Signed-off-by: Darshit Chanpura --- .../panels/auth-view/auth-view.tsx | 44 ++++++------ .../panels/auth-view/instruction-view.tsx | 6 +- .../internal-user-edit/internal-user-edit.tsx | 69 ++++++++++++++----- .../apps/configuration/panels/user-list.tsx | 3 +- 4 files changed, 76 insertions(+), 46 deletions(-) diff --git a/public/apps/configuration/panels/auth-view/auth-view.tsx b/public/apps/configuration/panels/auth-view/auth-view.tsx index 83715e65..c1db7e44 100644 --- a/public/apps/configuration/panels/auth-view/auth-view.tsx +++ b/public/apps/configuration/panels/auth-view/auth-view.tsx @@ -61,7 +61,6 @@ export function AuthView(props: AppDependencies) { fetchData(); }, [props.coreStart.http, dataSource]); - const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); const buttonData = [ @@ -85,14 +84,14 @@ export function AuthView(props: AppDependencies) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - { updatedUX ? ( + {updatedUX ? ( <> - { accessErrorFlag ? ( + {accessErrorFlag ? ( )} - ) : ( <>

Authentication and authorization

- { accessErrorFlag ? : - } + /> + ) : ( + + )} )} @@ -127,19 +128,19 @@ export function AuthView(props: AppDependencies) { selectedDataSource={dataSource} /> {updatedUX ? ( - <> - - - - ) : ( + <> + + + + ) : (

Authentication and authorization

@@ -150,7 +151,8 @@ export function AuthView(props: AppDependencies) { text="Manage via config.yml" /> )} -
)} + + )} {loading ? ( ) : accessErrorFlag ? ( diff --git a/public/apps/configuration/panels/auth-view/instruction-view.tsx b/public/apps/configuration/panels/auth-view/instruction-view.tsx index 20985f01..0e53938e 100644 --- a/public/apps/configuration/panels/auth-view/instruction-view.tsx +++ b/public/apps/configuration/panels/auth-view/instruction-view.tsx @@ -13,7 +13,7 @@ * permissions and limitations under the License. */ -import { EuiCode, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; +import { EuiCode, EuiSpacer, EuiText } from '@elastic/eui'; import React from 'react'; import { ClientConfigType } from '../../../../types'; import { ExternalLinkButton } from '../../utils/display-utils'; @@ -22,10 +22,6 @@ import { DocLinks } from '../../constants'; export function InstructionView(props: { config: ClientConfigType }) { return ( <> - {/* -

Authentication and authorization

-
*/} - diff --git a/public/apps/configuration/panels/internal-user-edit/internal-user-edit.tsx b/public/apps/configuration/panels/internal-user-edit/internal-user-edit.tsx index 5b122f38..73c57272 100644 --- a/public/apps/configuration/panels/internal-user-edit/internal-user-edit.tsx +++ b/public/apps/configuration/panels/internal-user-edit/internal-user-edit.tsx @@ -49,6 +49,7 @@ import { constructErrorMessageAndLog } from '../../../error-utils'; import { BackendRolePanel } from './backend-role-panel'; import { DataSourceContext } from '../../app-router'; import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; +import { HeaderTitle, HeaderDescription } from '../../header/header-components'; interface InternalUserEditDeps extends BreadcrumbsPageDependencies { action: 'create' | 'edit' | 'duplicate'; @@ -147,6 +148,20 @@ export function InternalUserEdit(props: InternalUserEditDeps) { } }; + const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); + + const descriptionData = [ + { + renderComponent: ( + + The security plugin includes an internal user database. Use this database in place of, or + in addition to, an external
authentication system such as LDAP or Active Directory.{' '} + +
+ ), + }, + ]; + return ( <> - {props.buildBreadcrumbs(TITLE_TEXT_DICT[props.action])} - - - - - -

{TITLE_TEXT_DICT[props.action]}

-
-
- - - The security plugin includes an internal user database. Use this database in place of, - or in addition to, an external authentication system such as LDAP or Active Directory.{' '} - - - -
-
+ {updatedUX ? ( + <> + + + + ) : ( + <> + {props.buildBreadcrumbs(TITLE_TEXT_DICT[props.action])} + + + + + +

{TITLE_TEXT_DICT[props.action]}

+
+
+ + + The security plugin includes an internal user database. Use this database in place + of, or in addition to, an external authentication system such as LDAP or Active + Directory. + + +
+
+ + )} Date: Wed, 7 Aug 2024 00:38:25 -0400 Subject: [PATCH 04/24] Updates user permissions page Signed-off-by: Darshit Chanpura --- .../permission-list/permission-list.tsx | 122 +++++++++++++----- .../apps/configuration/utils/context-menu.tsx | 7 +- 2 files changed, 93 insertions(+), 36 deletions(-) diff --git a/public/apps/configuration/panels/permission-list/permission-list.tsx b/public/apps/configuration/panels/permission-list/permission-list.tsx index 17116dd3..5539eef9 100644 --- a/public/apps/configuration/panels/permission-list/permission-list.tsx +++ b/public/apps/configuration/panels/permission-list/permission-list.tsx @@ -65,6 +65,7 @@ import { DocLinks } from '../../constants'; import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; import { DataSourceContext } from '../../app-router'; import { AccessErrorComponent } from '../../access-error-component'; +import { HeaderTitle, HeaderDescription, HeaderButtonOrLink } from '../../header/header-components'; export function renderBooleanToCheckMark(value: boolean): React.ReactNode { return value ? : ''; @@ -353,12 +354,41 @@ export function PermissionList(props: AppDependencies) { , ]; + const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); + const [createActionGroupMenu] = useContextMenuState( 'Create action group', { fill: true }, - createActionGroupMenuItems + createActionGroupMenuItems, + updatedUX ); + const buttonData = [ + { + isLoading: loading, + renderComponent: {createActionGroupMenu}, + }, + ]; + + const descriptionData = [ + { + isLoading: loading, + renderComponent: ( + + Permissions are individual actions, such as cluster:admin/snapshot/restore, which lets you + restore snapshots. Action groups
+ are reusable collections of permissions, such as MANAGE_SNAPSHOTS, which lets you view, + take, delete, and restore
+ snapshots. You can often meet your security needs using the default action groups, but you + might find it convenient to create
+ your own. +
+ ), + }, + ]; + + const permissionLen = Query.execute(query || '', permissionList).length; + return ( <> - - -

Permissions

-
-
+ {updatedUX ? ( + <> + + + + + ) : ( + + +

Permissions

+
+
+ )} {loading ? ( ) : accessErrorFlag ? ( ) : ( - - - -

- Permissions - - {' '} - ({Query.execute(query || '', permissionList).length}) - -

-
- - Permissions are individual actions, such as cluster:admin/snapshot/restore, which - lets you restore snapshots. Action groups are reusable collections of permissions, - such as MANAGE_SNAPSHOTS, which lets you view, take, delete, and restore snapshots. - You can often meet your security needs using the default action groups, but you - might find it convenient to create your own.{' '} - - -
- - - {actionsMenu} - {createActionGroupMenu} - - -
+ {updatedUX ? null : ( + + + +

+ Permissions + + {' '} + ({Query.execute(query || '', permissionList).length}) + +

+
+ + Permissions are individual actions, such as cluster:admin/snapshot/restore, which + lets you restore snapshots. Action groups are reusable collections of permissions, + such as MANAGE_SNAPSHOTS, which lets you view, take, delete, and restore + snapshots. You can often meet your security needs using the default action groups, + but you might find it convenient to create your own.{' '} + + +
+ + + {actionsMenu} + {createActionGroupMenu} + + +
+ )} {actionsMenu}] : undefined, }} selection={{ onSelectionChange: setSelection }} sorting={{ sort: { field: 'type', direction: 'asc' } }} diff --git a/public/apps/configuration/utils/context-menu.tsx b/public/apps/configuration/utils/context-menu.tsx index b0a199a8..aeb44a76 100644 --- a/public/apps/configuration/utils/context-menu.tsx +++ b/public/apps/configuration/utils/context-menu.tsx @@ -27,15 +27,16 @@ import React from 'react'; export function useContextMenuState( buttonText: string, buttonProps: EuiButtonProps, - children: React.ReactElement[] + children: React.ReactElement[], + updatedUX?: boolean ): [React.ReactElement, () => void] { const [isContextMenuOpen, setContextMenuOpen] = useState(false); const closeContextMenu = () => setContextMenuOpen(false); const button = ( { setContextMenuOpen(true); }} From baea892e685b6d00ba43e309cf9cc46441349e69 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 7 Aug 2024 01:03:22 -0400 Subject: [PATCH 05/24] Updates dashboards tenancy page Signed-off-by: Darshit Chanpura --- .../panels/tenant-list/manage_tab.tsx | 24 +++++++- .../panels/tenant-list/tenant-list.tsx | 59 +++++++++++++++---- 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/public/apps/configuration/panels/tenant-list/manage_tab.tsx b/public/apps/configuration/panels/tenant-list/manage_tab.tsx index 1d9a4918..758a4da0 100644 --- a/public/apps/configuration/panels/tenant-list/manage_tab.tsx +++ b/public/apps/configuration/panels/tenant-list/manage_tab.tsx @@ -75,6 +75,7 @@ import { buildUrl } from '../../utils/url-builder'; import { CrossPageToast } from '../../cross-page-toast'; import { getDashboardsInfo } from '../../../../utils/dashboards-info-utils'; import { AccessErrorComponent } from '../../access-error-component'; +import { HeaderButtonOrLink } from '../../header/header-components'; export function ManageTab(props: AppDependencies) { const setGlobalBreadcrumbs = flow(getBreadcrumbs, props.coreStart.chrome.setBreadcrumbs); @@ -503,6 +504,20 @@ export function ManageTab(props: AppDependencies) { /> ); } + const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); + const createTenantButton = [ + { + id: 'createTenant', + label: 'Creat tenant', + iconType: 'plus', + iconSide: 'left', + isLoading: false, + run: () => showEditModal('', Action.create, ''), + type: 'button', + fill: true, + // target: "_blank" + }, + ]; /* eslint-disable */ return ( <> @@ -528,6 +543,13 @@ export function ManageTab(props: AppDependencies) { {actionsMenu} + { updatedUX ? + + : Create tenant - + } diff --git a/public/apps/configuration/panels/tenant-list/tenant-list.tsx b/public/apps/configuration/panels/tenant-list/tenant-list.tsx index f804f09d..1a2e8428 100644 --- a/public/apps/configuration/panels/tenant-list/tenant-list.tsx +++ b/public/apps/configuration/panels/tenant-list/tenant-list.tsx @@ -32,6 +32,7 @@ import { DocLinks } from '../../constants'; import { getDashboardsInfo } from '../../../../utils/dashboards-info-utils'; import { LocalCluster } from '../../../../utils/datasource-utils'; import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; +import { HeaderTitle, HeaderDescription } from '../../header/header-components'; interface TenantListProps extends AppDependencies { tabID: string; @@ -127,6 +128,21 @@ export function TenantList(props: TenantListProps) { )); }; + const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); + const descriptionData = [ + { + renderComponent: ( + + Tenants in OpenSearch Dashboards are spaces for saving index patterns, visualizations, + dashboards, and other OpenSearch Dashboards objects. Tenants are useful for safely sharing + your work with other OpenSearch Dashboards users. You can control which roles have access + to a tenant and whether those roles have read or write access.{' '} + + + ), + }, + ]; + return ( <> {}} selectedDataSource={LocalCluster} /> - - -

Dashboards multi-tenancy

-
-
- - Tenants in OpenSearch Dashboards are spaces for saving index patterns, visualizations, - dashboards, and other OpenSearch Dashboards objects. Tenants are useful for safely sharing - your work with other OpenSearch Dashboards users. You can control which roles have access to - a tenant and whether those roles have read or write access.{' '} - - + {updatedUX ? ( + <> + + + + ) : ( + <> + + +

Dashboards multi-tenancy

+
+
+ + Tenants in OpenSearch Dashboards are spaces for saving index patterns, visualizations, + dashboards, and other OpenSearch +
Dashboards objects. Tenants are useful for safely sharing your work with other + OpenSearch Dashboards users. You can control
+ which roles have access to a tenant and whether those roles have read or write access.{' '} + +
+ + )} {renderTabs()} {!isMultiTenancyEnabled && selectedTabId === 'Manage' && tenancyDisabledWarning} From 451e95790866dd5210a6b7a21cdfef3a837e944e Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 7 Aug 2024 01:08:31 -0400 Subject: [PATCH 06/24] Updates dashboards audit logs page Signed-off-by: Darshit Chanpura --- .../panels/audit-logging/audit-logging.tsx | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/public/apps/configuration/panels/audit-logging/audit-logging.tsx b/public/apps/configuration/panels/audit-logging/audit-logging.tsx index 4db10637..a7a1449a 100644 --- a/public/apps/configuration/panels/audit-logging/audit-logging.tsx +++ b/public/apps/configuration/panels/audit-logging/audit-logging.tsx @@ -49,6 +49,7 @@ import { DocLinks } from '../../constants'; import { DataSourceContext } from '../../app-router'; import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; import { AccessErrorComponent } from '../../access-error-component'; +import { HeaderTitle } from '../../header/header-components'; interface AuditLoggingProps extends AppDependencies { fromType: string; @@ -249,6 +250,7 @@ export function AuditLogging(props: AuditLoggingProps) { ); } + const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); return (
@@ -258,11 +260,19 @@ export function AuditLogging(props: AuditLoggingProps) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - - -

Audit logging

-
-
+ {updatedUX ? ( + + ) : ( + + +

Audit logging

+
+
+ )} {loading ? : content}
From d4e5da3f0023f8d717b197f9eef21a5f3d8ea02f Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 7 Aug 2024 14:30:44 -0400 Subject: [PATCH 07/24] Updates roles and related pages Signed-off-by: Darshit Chanpura --- .../panels/role-edit/role-edit.tsx | 63 ++++++-- .../apps/configuration/panels/role-list.tsx | 141 +++++++++++++----- .../panels/role-view/role-view.tsx | 90 +++++++++-- .../apps/configuration/utils/context-menu.tsx | 2 +- 4 files changed, 233 insertions(+), 63 deletions(-) diff --git a/public/apps/configuration/panels/role-edit/role-edit.tsx b/public/apps/configuration/panels/role-edit/role-edit.tsx index e025cda0..efe0b27d 100644 --- a/public/apps/configuration/panels/role-edit/role-edit.tsx +++ b/public/apps/configuration/panels/role-edit/role-edit.tsx @@ -59,6 +59,7 @@ import { generateResourceName } from '../../utils/resource-utils'; import { NameRow } from '../../utils/name-row'; import { DataSourceContext } from '../../app-router'; import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; +import { HeaderDescription, HeaderTitle } from '../../header/header-components'; interface RoleEditDeps extends BreadcrumbsPageDependencies { action: 'create' | 'edit' | 'duplicate'; @@ -233,6 +234,23 @@ export function RoleEdit(props: RoleEditDeps) { const tenantOptions = tenantNames.map(stringToComboBoxOption); + const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); + + const descriptionData = [ + { + renderComponent: ( + + Roles are the core way of controlling access to your cluster. Roles contain any + combination of cluster-wide permission, index- +
+ specific permissions, document- and field-level security, and tenants. Then you map users + to these roles so that users
+ gain those permissions. +
+ ), + }, + ]; + return ( <> - {props.buildBreadcrumbs(TITLE_TEXT_DICT[props.action])} - - - -

{TITLE_TEXT_DICT[props.action]}

-
- Roles are the core way of controlling access to your cluster. Roles contain any - combination of cluster-wide permission, index-specific permissions, document- and - field-level security, and tenants. Once you've created the role, you can map users to - the roles so that users gain those permissions.{' '} - -
-
+ {updatedUX ? ( + <> + + + + ) : ( + <> + {' '} + {props.buildBreadcrumbs(TITLE_TEXT_DICT[props.action])} + + + +

{TITLE_TEXT_DICT[props.action]}

+
+ Roles are the core way of controlling access to your cluster. Roles contain any + combination of cluster-wide permission, index-specific permissions, document- and + field-level security, and tenants. Once you've created the role, you can map + users to the roles so that users gain those permissions.{' '} + +
+
+ + )} > = [ { @@ -198,6 +199,9 @@ export function RoleList(props: AppDependencies) { const [searchOptions, setSearchOptions] = useState({}); const [query, setQuery] = useState(null); + + const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); + useEffect(() => { setSearchOptions({ onChange: (arg) => { @@ -260,6 +264,37 @@ export function RoleList(props: AppDependencies) { }); }, [roleData]); + const buttonData = [ + { + label: 'Create role', + isLoading: false, + href: buildHashUrl(ResourceType.roles, Action.create), + fill: true, + iconType: 'plus', + iconSide: 'left', + type: 'button', + testId: 'create-role', + // target: "_blank" + }, + ]; + const descriptionData = [ + { + isLoading: loading, + renderComponent: ( + + Roles are the core way of controlling access to your cluster. Roles contain any + combination of cluster-wide permission, index- +
+ specific permissions, document- and field-level security, and tenants. Then you map users + to these roles so that users
+ gain those permissions. +
+ ), + }, + ]; + + const roleLen = Query.execute(query || '', roleData).length; + return ( <> - - -

Roles

-
-
+ {updatedUX ? ( + <> + + + + + ) : ( + + +

Roles

+
+
+ )} {loading ? ( ) : accessErrorFlag ? ( ) : ( - - - -

- Roles - - {' '} - ({Query.execute(query || '', roleData).length}) - -

-
- - Roles are the core way of controlling access to your cluster. Roles contain any - combination of cluster-wide permission, index-specific permissions, document- and - field-level security, and tenants. Then you map users to these roles so that users - gain those permissions. - -
- - - {actionsMenu} - - - Create role - - - - -
+ {updatedUX ? null : ( + + + +

+ Roles + + {' '} + ({Query.execute(query || '', roleData).length}) + +

+
+ + Roles are the core way of controlling access to your cluster. Roles contain any + combination of cluster-wide permission, index-specific permissions, document- and + field-level security, and tenants. Then you map users to these roles so that users + gain those permissions. + +
+ + + {actionsMenu} + + + Create role + + + + +
+ )} {actionsMenu}] : undefined, + }} error={errorFlag ? 'Load data failed, please check console log for more detail.' : ''} message={showTableStatusMessage(loading, roleData)} /> diff --git a/public/apps/configuration/panels/role-view/role-view.tsx b/public/apps/configuration/panels/role-view/role-view.tsx index 7e41f27c..14564dbb 100644 --- a/public/apps/configuration/panels/role-view/role-view.tsx +++ b/public/apps/configuration/panels/role-view/role-view.tsx @@ -72,6 +72,7 @@ import { setCrossPageToast } from '../../utils/storage-utils'; import { DataSourceContext } from '../../app-router'; import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; import { getClusterInfo } from '../../../../utils/datasource-utils'; +import { HeaderTitle, HeaderButtonOrLink } from '../../header/header-components'; interface RoleViewProps extends BreadcrumbsPageDependencies { roleName: string; @@ -378,6 +379,7 @@ export function RoleView(props: RoleViewProps) { , ]; const [actionsMenu] = useContextMenuState('Actions', {}, actionsMenuItems); + const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); if (isReserved) { pageActions = Duplicate role; @@ -394,6 +396,58 @@ export function RoleView(props: RoleViewProps) { ); } + const reservedRoleButtons = [ + { + label: 'Duplicate role', + isLoading: false, + href: buildHashUrl(ResourceType.roles, Action.edit, props.roleName), + type: 'button', + fill: true, + // target: "_blank" + }, + ]; + const roleButtons = [ + { + isLoading: false, + run: async () => { + try { + await requestDeleteRoles(props.coreStart.http, [props.roleName], dataSource.id); + setCrossPageToast(buildUrl(ResourceType.roles), { + id: 'deleteRole', + color: 'success', + title: `${props.roleName} deleted ${getClusterInfo(dataSourceEnabled, dataSource)}`, + }); + window.location.href = buildHashUrl(ResourceType.roles); + } catch (e) { + addToast(createUnknownErrorToast('deleteRole', 'delete role')); + } + }, + iconType: 'trash', + color: 'danger', + type: 'button', // this should be icon, but icons current do not support a border currently + testId: 'delete', + ariaLabel: 'delete', + // target: "_blank" + }, + { + label: 'Duplicate', + isLoading: false, + href: duplicateRoleLink, + type: 'button', + // target: "_blank" + }, + { + label: 'Edit role', + isLoading: false, + href: buildHashUrl(ResourceType.roles, Action.edit, props.roleName), + fill: true, + type: 'button', + // target: "_blank" + }, + ]; + + const roleView = isReserved ? reservedRoleButtons : roleButtons; + return ( <> - {props.buildBreadcrumbs(props.roleName)} - - - - -

{props.roleName}

-
-
+ {updatedUX ? ( + <> + + + + ) : ( + <> + {props.buildBreadcrumbs(props.roleName)} - {pageActions} -
+ + + +

{props.roleName}

+
+
+ {pageActions} +
+ + )} { - setContextMenuOpen(true); + setContextMenuOpen(!isContextMenuOpen); }} {...buttonProps} > From f4f6278db07ba0ed084c36944145595b2c8ab38c Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 7 Aug 2024 14:39:47 -0400 Subject: [PATCH 08/24] Updates variable name and fixes indentation Signed-off-by: Darshit Chanpura --- .../panels/audit-logging/audit-logging.tsx | 4 ++-- .../panels/auth-view/auth-view.tsx | 6 +++--- .../apps/configuration/panels/get-started.tsx | 4 ++-- .../internal-user-edit/internal-user-edit.tsx | 4 ++-- .../panels/permission-list/permission-list.tsx | 10 +++++----- .../panels/role-edit/role-edit.tsx | 4 ++-- public/apps/configuration/panels/role-list.tsx | 14 +++++--------- .../panels/role-view/role-view.tsx | 4 ++-- .../panels/tenant-list/manage_tab.tsx | 4 ++-- .../panels/tenant-list/tenant-list.tsx | 18 +++++++++--------- public/apps/configuration/panels/user-list.tsx | 13 +++++-------- .../apps/configuration/utils/context-menu.tsx | 6 +++--- 12 files changed, 42 insertions(+), 49 deletions(-) diff --git a/public/apps/configuration/panels/audit-logging/audit-logging.tsx b/public/apps/configuration/panels/audit-logging/audit-logging.tsx index a7a1449a..744d886c 100644 --- a/public/apps/configuration/panels/audit-logging/audit-logging.tsx +++ b/public/apps/configuration/panels/audit-logging/audit-logging.tsx @@ -250,7 +250,7 @@ export function AuditLogging(props: AuditLoggingProps) { ); } - const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); + const useUpdatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); return (
@@ -260,7 +260,7 @@ export function AuditLogging(props: AuditLoggingProps) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {updatedUX ? ( + {useUpdatedUX ? ( - {updatedUX ? ( + {useUpdatedUX ? ( <> - {updatedUX ? ( + {useUpdatedUX ? ( <> - {updatedUX ? ( + {useUpdatedUX ? ( <> - {updatedUX ? ( + {useUpdatedUX ? ( <> , ]; - const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); + const useUpdatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); const [createActionGroupMenu] = useContextMenuState( 'Create action group', { fill: true }, createActionGroupMenuItems, - updatedUX + useUpdatedUX ); const buttonData = [ @@ -397,7 +397,7 @@ export function PermissionList(props: AppDependencies) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {updatedUX ? ( + {useUpdatedUX ? ( <> ) : ( - {updatedUX ? null : ( + {useUpdatedUX ? null : ( @@ -478,7 +478,7 @@ export function PermissionList(props: AppDependencies) { setQuery(arg.query); return true; }, - toolsRight: updatedUX ? [{actionsMenu}] : undefined, + toolsRight: useUpdatedUX ? [{actionsMenu}] : undefined, }} selection={{ onSelectionChange: setSelection }} sorting={{ sort: { field: 'type', direction: 'asc' } }} diff --git a/public/apps/configuration/panels/role-edit/role-edit.tsx b/public/apps/configuration/panels/role-edit/role-edit.tsx index efe0b27d..43e71497 100644 --- a/public/apps/configuration/panels/role-edit/role-edit.tsx +++ b/public/apps/configuration/panels/role-edit/role-edit.tsx @@ -234,7 +234,7 @@ export function RoleEdit(props: RoleEditDeps) { const tenantOptions = tenantNames.map(stringToComboBoxOption); - const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); + const useUpdatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); const descriptionData = [ { @@ -259,7 +259,7 @@ export function RoleEdit(props: RoleEditDeps) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {updatedUX ? ( + {useUpdatedUX ? ( <> ({}); const [query, setQuery] = useState(null); - const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); - useEffect(() => { setSearchOptions({ onChange: (arg) => { @@ -264,6 +262,7 @@ export function RoleList(props: AppDependencies) { }); }, [roleData]); + const useUpdatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); const buttonData = [ { label: 'Create role', @@ -303,7 +302,7 @@ export function RoleList(props: AppDependencies) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {updatedUX ? ( + {useUpdatedUX ? ( <> ) : ( - {updatedUX ? null : ( + {useUpdatedUX ? null : (

Roles - - {' '} - ({Query.execute(query || '', roleData).length}) - + ({roleLen})

@@ -385,7 +381,7 @@ export function RoleList(props: AppDependencies) { sorting={true} search={{ ...searchOptions, - toolsRight: updatedUX ? [{actionsMenu}] : undefined, + toolsRight: useUpdatedUX ? [{actionsMenu}] : undefined, }} error={errorFlag ? 'Load data failed, please check console log for more detail.' : ''} message={showTableStatusMessage(loading, roleData)} diff --git a/public/apps/configuration/panels/role-view/role-view.tsx b/public/apps/configuration/panels/role-view/role-view.tsx index 14564dbb..0e857600 100644 --- a/public/apps/configuration/panels/role-view/role-view.tsx +++ b/public/apps/configuration/panels/role-view/role-view.tsx @@ -379,7 +379,7 @@ export function RoleView(props: RoleViewProps) { , ]; const [actionsMenu] = useContextMenuState('Actions', {}, actionsMenuItems); - const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); + const useUpdatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); if (isReserved) { pageActions = Duplicate role; @@ -456,7 +456,7 @@ export function RoleView(props: RoleViewProps) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {updatedUX ? ( + {useUpdatedUX ? ( <> ); } - const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); + const useUpdatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); const createTenantButton = [ { id: 'createTenant', @@ -543,7 +543,7 @@ export function ManageTab(props: AppDependencies) { {actionsMenu} - { updatedUX ? + { useUpdatedUX ? Tenants in OpenSearch Dashboards are spaces for saving index patterns, visualizations, - dashboards, and other OpenSearch Dashboards objects. Tenants are useful for safely sharing - your work with other OpenSearch Dashboards users. You can control which roles have access - to a tenant and whether those roles have read or write access.{' '} + dashboards, and other OpenSearch +
Dashboards objects. Tenants are useful for safely sharing your work with other + OpenSearch Dashboards users. You can control
+ which roles have access to a tenant and whether those roles have read or write access.{' '}
), @@ -151,7 +152,7 @@ export function TenantList(props: TenantListProps) { setDataSource={() => {}} selectedDataSource={LocalCluster} /> - {updatedUX ? ( + {useUpdatedUX ? ( <> Tenants in OpenSearch Dashboards are spaces for saving index patterns, visualizations, - dashboards, and other OpenSearch -
Dashboards objects. Tenants are useful for safely sharing your work with other - OpenSearch Dashboards users. You can control
- which roles have access to a tenant and whether those roles have read or write access.{' '} + dashboards, and other OpenSearch Dashboards objects. Tenants are useful for safely + sharing your work with other OpenSearch Dashboards users. You can control which roles + have access to a tenant and whether those roles have read or write access.{' '}
diff --git a/public/apps/configuration/panels/user-list.tsx b/public/apps/configuration/panels/user-list.tsx index 88c3218c..239f5b9a 100644 --- a/public/apps/configuration/panels/user-list.tsx +++ b/public/apps/configuration/panels/user-list.tsx @@ -205,7 +205,7 @@ export function UserList(props: AppDependencies) { const [actionsMenu, closeActionsMenu] = useContextMenuState('Actions', {}, actionsMenuItems); - const updatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); + const useUpdatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); const buttonData = [ { label: 'Create internal user', @@ -244,7 +244,7 @@ export function UserList(props: AppDependencies) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {updatedUX ? ( + {useUpdatedUX ? ( <> ) : ( - {updatedUX ? null : ( + {useUpdatedUX ? null : (

Internal users - - {' '} - ({Query.execute(query || '', userData).length}) - + ({userLen})

@@ -330,7 +327,7 @@ export function UserList(props: AppDependencies) { setQuery(arg.query); return true; }, - toolsRight: updatedUX ? [{actionsMenu}] : undefined, + toolsRight: useUpdatedUX ? [{actionsMenu}] : undefined, }} // @ts-ignore selection={{ onSelectionChange: setSelection }} diff --git a/public/apps/configuration/utils/context-menu.tsx b/public/apps/configuration/utils/context-menu.tsx index 7a5eee74..aea6ae5a 100644 --- a/public/apps/configuration/utils/context-menu.tsx +++ b/public/apps/configuration/utils/context-menu.tsx @@ -28,15 +28,15 @@ export function useContextMenuState( buttonText: string, buttonProps: EuiButtonProps, children: React.ReactElement[], - updatedUX?: boolean + useUpdatedUX?: boolean ): [React.ReactElement, () => void] { const [isContextMenuOpen, setContextMenuOpen] = useState(false); const closeContextMenu = () => setContextMenuOpen(false); const button = ( { setContextMenuOpen(!isContextMenuOpen); }} From 67c4f245f643e9c1645e885509a881526b5aafea Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Wed, 7 Aug 2024 15:26:34 -0400 Subject: [PATCH 09/24] Push logic into new component Signed-off-by: Derek Ho --- .../header/header-components.tsx | 27 +++++++++++++- .../configuration/header/header-props.tsx | 21 ++++------- .../apps/configuration/panels/get-started.tsx | 37 ++++++------------- 3 files changed, 45 insertions(+), 40 deletions(-) diff --git a/public/apps/configuration/header/header-components.tsx b/public/apps/configuration/header/header-components.tsx index 67162001..62da6d6b 100644 --- a/public/apps/configuration/header/header-components.tsx +++ b/public/apps/configuration/header/header-components.tsx @@ -16,7 +16,7 @@ import React from 'react'; import { TopNavControlData } from 'src/plugins/navigation/public/top_nav_menu/top_nav_control_data'; import { EuiTitle } from '@elastic/eui'; -import { ControlProps, DescriptionProps, TitleProps } from './header-props'; +import { ControlProps, DescriptionProps, HeaderProps, TitleProps } from './header-props'; // controlType should be one of: https://github.com/AMoo-Miki/OpenSearch-Dashboards/blob/header-collective/src/plugins/navigation/public/top_nav_menu/top_nav_control_data.tsx#L91 @@ -67,3 +67,28 @@ export const HeaderDescription = React.memo((props: DescriptionProps) => { /> ); }); + +export const PageHeader = (props: HeaderProps & DescriptionProps & ControlProps) => { + const { HeaderControl } = props.navigation.ui; // need to get this from SecurityPluginStartDependencies + const useNewUx = props.coreStart.chrome.navGroup.getNavGroupEnabled(); + if (useNewUx) { + return ( + <> + {props.descriptionControls ? : null} + {props.controlControls ? : null} + + ); + + } else { + return props.fallBackComponent; + } + + + +} \ No newline at end of file diff --git a/public/apps/configuration/header/header-props.tsx b/public/apps/configuration/header/header-props.tsx index b65aeb8e..dbadcd33 100644 --- a/public/apps/configuration/header/header-props.tsx +++ b/public/apps/configuration/header/header-props.tsx @@ -13,28 +13,21 @@ * permissions and limitations under the License. */ -import { ApplicationStart } from 'opensearch-dashboards/public'; +import { CoreStart } from 'opensearch-dashboards/public'; import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; import { TopNavControlData } from 'src/plugins/navigation/public/top_nav_menu/top_nav_control_data'; export interface HeaderProps { navigation: NavigationPublicPluginStart; - className?: string; - application: ApplicationStart; + coreStart: CoreStart; + fallBackComponent: JSX.Element } -export interface ControlProps extends HeaderProps { - controls: TopNavControlData[]; +export interface ControlProps { + controlControls?: TopNavControlData[]; // mount: (menuMount: MountPoint | undefined) => void; } -export interface TitleProps extends HeaderProps { - pageHeader: string; - shouldDisplayCount?: boolean; - count?: number; -} - -export interface DescriptionProps extends HeaderProps { - description: string; - controls: TopNavControlData[]; +export interface DescriptionProps { + descriptionControls?: TopNavControlData[]; } diff --git a/public/apps/configuration/panels/get-started.tsx b/public/apps/configuration/panels/get-started.tsx index 23c6ab67..796ec1b6 100644 --- a/public/apps/configuration/panels/get-started.tsx +++ b/public/apps/configuration/panels/get-started.tsx @@ -39,7 +39,7 @@ import { createSuccessToast, createUnknownErrorToast, useToastState } from '../u import { SecurityPluginTopNavMenu } from '../top-nav-menu'; import { DataSourceContext } from '../app-router'; import { getClusterInfo } from '../../../utils/datasource-utils'; -import { HeaderButtonOrLink, HeaderTitle } from '../header/header-components'; +import { HeaderButtonOrLink, HeaderTitle, PageHeader } from '../header/header-components'; const addBackendStep = { title: 'Add backends', @@ -166,8 +166,6 @@ export function GetStarted(props: AppDependencies) { const dataSourceEnabled = !!props.depsStart.dataSource?.dataSourceEnabled; const { dataSource, setDataSource } = useContext(DataSourceContext)!; - const useUpdatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); - let steps; if (props.config.ui.backend_configurable) { steps = [addBackendStep, ...setOfSteps]; @@ -196,28 +194,17 @@ export function GetStarted(props: AppDependencies) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {useUpdatedUX ? ( - <> - - - - ) : ( - - -

Get started

-
- -
- )} - + + +

Get started

+
+ + } + />

From 8309c6625171dfdd97e1801ede0bc8a8ba067563 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Wed, 7 Aug 2024 15:26:50 -0400 Subject: [PATCH 10/24] Lint Signed-off-by: Derek Ho --- .../header/header-components.tsx | 26 +++++++++---------- .../configuration/header/header-props.tsx | 2 +- .../apps/configuration/panels/get-started.tsx | 22 +++++++++------- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/public/apps/configuration/header/header-components.tsx b/public/apps/configuration/header/header-components.tsx index 62da6d6b..a4bd9d83 100644 --- a/public/apps/configuration/header/header-components.tsx +++ b/public/apps/configuration/header/header-components.tsx @@ -74,21 +74,21 @@ export const PageHeader = (props: HeaderProps & DescriptionProps & ControlProps) if (useNewUx) { return ( <> - {props.descriptionControls ? : null} - {props.controlControls ? : null} + {props.descriptionControls ? ( + + ) : null} + {props.controlControls ? ( + + ) : null} ); - } else { return props.fallBackComponent; } - - - -} \ No newline at end of file +}; diff --git a/public/apps/configuration/header/header-props.tsx b/public/apps/configuration/header/header-props.tsx index dbadcd33..aee1878c 100644 --- a/public/apps/configuration/header/header-props.tsx +++ b/public/apps/configuration/header/header-props.tsx @@ -20,7 +20,7 @@ import { TopNavControlData } from 'src/plugins/navigation/public/top_nav_menu/to export interface HeaderProps { navigation: NavigationPublicPluginStart; coreStart: CoreStart; - fallBackComponent: JSX.Element + fallBackComponent: JSX.Element; } export interface ControlProps { diff --git a/public/apps/configuration/panels/get-started.tsx b/public/apps/configuration/panels/get-started.tsx index 796ec1b6..fb7355b8 100644 --- a/public/apps/configuration/panels/get-started.tsx +++ b/public/apps/configuration/panels/get-started.tsx @@ -39,7 +39,7 @@ import { createSuccessToast, createUnknownErrorToast, useToastState } from '../u import { SecurityPluginTopNavMenu } from '../top-nav-menu'; import { DataSourceContext } from '../app-router'; import { getClusterInfo } from '../../../utils/datasource-utils'; -import { HeaderButtonOrLink, HeaderTitle, PageHeader } from '../header/header-components'; +import { PageHeader } from '../header/header-components'; const addBackendStep = { title: 'Add backends', @@ -195,15 +195,17 @@ export function GetStarted(props: AppDependencies) { selectedDataSource={dataSource} /> - -

Get started

-
- - } + navigation={props.depsStart.navigation} + coreStart={props.coreStart} + controlControls={buttonData} + fallBackComponent={ + + +

Get started

+
+ +
+ } /> From 9f8b026254a9f22a91635734bb234a012cd8c84a Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Wed, 7 Aug 2024 15:45:01 -0400 Subject: [PATCH 11/24] Migrate audit logging and tenant tabs to new page header Signed-off-by: Derek Ho --- .../audit-logging-edit-settings.tsx | 33 ++++++++---- .../panels/audit-logging/audit-logging.tsx | 27 +++++----- .../panels/tenant-list/tenant-list.tsx | 53 ++++++++----------- 3 files changed, 57 insertions(+), 56 deletions(-) diff --git a/public/apps/configuration/panels/audit-logging/audit-logging-edit-settings.tsx b/public/apps/configuration/panels/audit-logging/audit-logging-edit-settings.tsx index 7f2ab07a..1f90442b 100644 --- a/public/apps/configuration/panels/audit-logging/audit-logging-edit-settings.tsx +++ b/public/apps/configuration/panels/audit-logging/audit-logging-edit-settings.tsx @@ -38,6 +38,7 @@ import { setCrossPageToast } from '../../utils/storage-utils'; import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; import { DataSourceContext } from '../../app-router'; import { getClusterInfo } from '../../../../utils/datasource-utils'; +import { PageHeader } from '../../header/header-components'; interface AuditLoggingEditSettingProps extends AppDependencies { setting: 'general' | 'compliance'; @@ -155,11 +156,17 @@ export function AuditLoggingEditSettings(props: AuditLoggingEditSettingProps) { const renderComplianceSetting = () => { return ( <> - - -

Compliance settings

-
-
+ + +

Compliance settings

+
+ + } + /> { return ( <> - - -

General settings

-
-
+ + +

General settings

+
+ + } + /> ); } - const useUpdatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); return (
@@ -260,19 +259,17 @@ export function AuditLogging(props: AuditLoggingProps) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {useUpdatedUX ? ( - - ) : ( - - -

Audit logging

-
-
- )} + + +

Audit logging

+
+ + } + /> {loading ? : content}
diff --git a/public/apps/configuration/panels/tenant-list/tenant-list.tsx b/public/apps/configuration/panels/tenant-list/tenant-list.tsx index 70ee1b86..1a64e38a 100644 --- a/public/apps/configuration/panels/tenant-list/tenant-list.tsx +++ b/public/apps/configuration/panels/tenant-list/tenant-list.tsx @@ -32,7 +32,7 @@ import { DocLinks } from '../../constants'; import { getDashboardsInfo } from '../../../../utils/dashboards-info-utils'; import { LocalCluster } from '../../../../utils/datasource-utils'; import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; -import { HeaderTitle, HeaderDescription } from '../../header/header-components'; +import { PageHeader } from '../../header/header-components'; interface TenantListProps extends AppDependencies { tabID: string; @@ -152,36 +152,27 @@ export function TenantList(props: TenantListProps) { setDataSource={() => {}} selectedDataSource={LocalCluster} /> - {useUpdatedUX ? ( - <> - - - - ) : ( - <> - - -

Dashboards multi-tenancy

-
-
- - Tenants in OpenSearch Dashboards are spaces for saving index patterns, visualizations, - dashboards, and other OpenSearch Dashboards objects. Tenants are useful for safely - sharing your work with other OpenSearch Dashboards users. You can control which roles - have access to a tenant and whether those roles have read or write access.{' '} - - - - )} + + + +

Dashboards multi-tenancy

+
+
+ + Tenants in OpenSearch Dashboards are spaces for saving index patterns, visualizations, + dashboards, and other OpenSearch Dashboards objects. Tenants are useful for safely + sharing your work with other OpenSearch Dashboards users. You can control which roles + have access to a tenant and whether those roles have read or write access.{' '} + + + + } + /> {renderTabs()} {!isMultiTenancyEnabled && selectedTabId === 'Manage' && tenancyDisabledWarning} From 0b4b9e8acc5865e4a67a34103b4e162c0f9d9ff9 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Wed, 7 Aug 2024 16:39:56 -0400 Subject: [PATCH 12/24] Migrate all tabs to new component Signed-off-by: Derek Ho --- .../panels/auth-view/auth-view.tsx | 88 +++++++------------ .../internal-user-edit/internal-user-edit.tsx | 67 ++++++-------- .../permission-list/permission-list.tsx | 43 +++------ .../panels/role-edit/role-edit.tsx | 57 +++++------- .../apps/configuration/panels/role-list.tsx | 43 +++------ .../panels/role-view/role-view.tsx | 46 ++++------ .../panels/tenant-list/tenant-list.tsx | 1 - .../apps/configuration/panels/user-list.tsx | 43 +++------ 8 files changed, 145 insertions(+), 243 deletions(-) diff --git a/public/apps/configuration/panels/auth-view/auth-view.tsx b/public/apps/configuration/panels/auth-view/auth-view.tsx index c8afc91f..ce96223b 100644 --- a/public/apps/configuration/panels/auth-view/auth-view.tsx +++ b/public/apps/configuration/panels/auth-view/auth-view.tsx @@ -26,7 +26,7 @@ import { InstructionView } from './instruction-view'; import { DataSourceContext } from '../../app-router'; import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; import { AccessErrorComponent } from '../../access-error-component'; -import { HeaderButtonOrLink, HeaderTitle } from '../../header/header-components'; +import { PageHeader } from '../../header/header-components'; export function AuthView(props: AppDependencies) { const [authentication, setAuthentication] = React.useState([]); @@ -61,8 +61,6 @@ export function AuthView(props: AppDependencies) { fetchData(); }, [props.coreStart.http, dataSource]); - const useUpdatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); - const buttonData = [ { label: 'Manage via config.yml', @@ -84,36 +82,22 @@ export function AuthView(props: AppDependencies) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {useUpdatedUX ? ( - <> - - {accessErrorFlag ? ( - - ) : ( - - )} - - ) : ( - <> +

Authentication and authorization

- {accessErrorFlag ? ( - - ) : ( - - )} - + } + /> + {accessErrorFlag ? ( + + ) : ( + )} ); @@ -127,32 +111,24 @@ export function AuthView(props: AppDependencies) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {useUpdatedUX ? ( - <> - - - - ) : ( - - -

Authentication and authorization

-
- {!loading && !errorFlag && props.config.ui.backend_configurable && ( - - )} -
- )} + + +

Authentication and authorization

+
+ {!loading && !errorFlag && props.config.ui.backend_configurable && ( + + )} + + } + /> {loading ? ( ) : accessErrorFlag ? ( diff --git a/public/apps/configuration/panels/internal-user-edit/internal-user-edit.tsx b/public/apps/configuration/panels/internal-user-edit/internal-user-edit.tsx index 228b242c..66edb73e 100644 --- a/public/apps/configuration/panels/internal-user-edit/internal-user-edit.tsx +++ b/public/apps/configuration/panels/internal-user-edit/internal-user-edit.tsx @@ -49,7 +49,7 @@ import { constructErrorMessageAndLog } from '../../../error-utils'; import { BackendRolePanel } from './backend-role-panel'; import { DataSourceContext } from '../../app-router'; import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; -import { HeaderTitle, HeaderDescription } from '../../header/header-components'; +import { PageHeader } from '../../header/header-components'; interface InternalUserEditDeps extends BreadcrumbsPageDependencies { action: 'create' | 'edit' | 'duplicate'; @@ -148,8 +148,6 @@ export function InternalUserEdit(props: InternalUserEditDeps) { } }; - const useUpdatedUX = props.coreStart.uiSettings.get('home:useNewHomePage'); - const descriptionData = [ { renderComponent: ( @@ -170,42 +168,33 @@ export function InternalUserEdit(props: InternalUserEditDeps) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {useUpdatedUX ? ( - <> - - - - ) : ( - <> - {props.buildBreadcrumbs(TITLE_TEXT_DICT[props.action])} - - - - - -

{TITLE_TEXT_DICT[props.action]}

-
-
- - - The security plugin includes an internal user database. Use this database in place - of, or in addition to, an external authentication system such as LDAP or Active - Directory. - - -
-
- - )} + + {props.buildBreadcrumbs(TITLE_TEXT_DICT[props.action])} + + + + + +

{TITLE_TEXT_DICT[props.action]}

+
+
+ + + The security plugin includes an internal user database. Use this database in + place of, or in addition to, an external authentication system such as LDAP or + Active Directory. + + +
+
+ + } + /> : ''; @@ -397,34 +397,19 @@ export function PermissionList(props: AppDependencies) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {useUpdatedUX ? ( - <> - - - - - ) : ( - - -

Permissions

-
-
- )} + + +

Permissions

+
+ + } + /> {loading ? ( ) : accessErrorFlag ? ( diff --git a/public/apps/configuration/panels/role-edit/role-edit.tsx b/public/apps/configuration/panels/role-edit/role-edit.tsx index 43e71497..eb4a035a 100644 --- a/public/apps/configuration/panels/role-edit/role-edit.tsx +++ b/public/apps/configuration/panels/role-edit/role-edit.tsx @@ -59,7 +59,7 @@ import { generateResourceName } from '../../utils/resource-utils'; import { NameRow } from '../../utils/name-row'; import { DataSourceContext } from '../../app-router'; import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; -import { HeaderDescription, HeaderTitle } from '../../header/header-components'; +import { PageHeader } from '../../header/header-components'; interface RoleEditDeps extends BreadcrumbsPageDependencies { action: 'create' | 'edit' | 'duplicate'; @@ -259,38 +259,29 @@ export function RoleEdit(props: RoleEditDeps) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {useUpdatedUX ? ( - <> - - - - ) : ( - <> - {' '} - {props.buildBreadcrumbs(TITLE_TEXT_DICT[props.action])} - - - -

{TITLE_TEXT_DICT[props.action]}

-
- Roles are the core way of controlling access to your cluster. Roles contain any - combination of cluster-wide permission, index-specific permissions, document- and - field-level security, and tenants. Once you've created the role, you can map - users to the roles so that users gain those permissions.{' '} - -
-
- - )} + + {' '} + {props.buildBreadcrumbs(TITLE_TEXT_DICT[props.action])} + + + +

{TITLE_TEXT_DICT[props.action]}

+
+ Roles are the core way of controlling access to your cluster. Roles contain any + combination of cluster-wide permission, index-specific permissions, document- and + field-level security, and tenants. Once you've created the role, you can map + users to the roles so that users gain those permissions.{' '} + +
+
+ + } + navigation={props.depsStart.navigation} + coreStart={props.coreStart} + descriptionControls={descriptionData} + /> > = [ { @@ -302,34 +302,19 @@ export function RoleList(props: AppDependencies) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {useUpdatedUX ? ( - <> - - - - - ) : ( - - -

Roles

-
-
- )} + + +

Roles

+
+ + } + /> {loading ? ( ) : accessErrorFlag ? ( diff --git a/public/apps/configuration/panels/role-view/role-view.tsx b/public/apps/configuration/panels/role-view/role-view.tsx index 0e857600..d1fe3e44 100644 --- a/public/apps/configuration/panels/role-view/role-view.tsx +++ b/public/apps/configuration/panels/role-view/role-view.tsx @@ -72,7 +72,7 @@ import { setCrossPageToast } from '../../utils/storage-utils'; import { DataSourceContext } from '../../app-router'; import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; import { getClusterInfo } from '../../../../utils/datasource-utils'; -import { HeaderTitle, HeaderButtonOrLink } from '../../header/header-components'; +import { PageHeader } from '../../header/header-components'; interface RoleViewProps extends BreadcrumbsPageDependencies { roleName: string; @@ -456,34 +456,26 @@ export function RoleView(props: RoleViewProps) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {useUpdatedUX ? ( - <> - - - - ) : ( - <> - {props.buildBreadcrumbs(props.roleName)} + + {props.buildBreadcrumbs(props.roleName)} - - - -

{props.roleName}

-
-
+ + + +

{props.roleName}

+
+
- {pageActions} -
- - )} + {pageActions} +
+ + } + /> ) { if (isEmpty(items)) { @@ -244,34 +244,19 @@ export function UserList(props: AppDependencies) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {useUpdatedUX ? ( - <> - - - - - ) : ( - - -

Internal users

-
-
- )} + + +

Internal users

+
+ + } + /> {loading ? ( ) : accessErrorFlag ? ( From f3f2cafd3313d91bb42fc4d07b3be691ab55ec3d Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Wed, 7 Aug 2024 18:05:49 -0400 Subject: [PATCH 13/24] Remove prop and fix some test failures Signed-off-by: Derek Ho --- .../configuration/header/header-props.tsx | 1 - .../audit-logging/test/audit-logging.test.tsx | 14 +++++++------ .../panels/auth-view/test/auth-view.test.tsx | 8 ++++--- .../test/permission-list.test.tsx | 14 +++++++++---- .../panels/role-edit/test/role-edit.test.tsx | 3 +++ .../panels/role-view/test/role-view.test.tsx | 3 +++ .../panels/test/role-list.test.tsx | 20 +++++++++++------- .../panels/test/user-list.test.tsx | 21 +++++++++++++------ 8 files changed, 56 insertions(+), 28 deletions(-) diff --git a/public/apps/configuration/header/header-props.tsx b/public/apps/configuration/header/header-props.tsx index aee1878c..0a6712eb 100644 --- a/public/apps/configuration/header/header-props.tsx +++ b/public/apps/configuration/header/header-props.tsx @@ -25,7 +25,6 @@ export interface HeaderProps { export interface ControlProps { controlControls?: TopNavControlData[]; - // mount: (menuMount: MountPoint | undefined) => void; } export interface DescriptionProps { diff --git a/public/apps/configuration/panels/audit-logging/test/audit-logging.test.tsx b/public/apps/configuration/panels/audit-logging/test/audit-logging.test.tsx index b3628e0c..572d2259 100644 --- a/public/apps/configuration/panels/audit-logging/test/audit-logging.test.tsx +++ b/public/apps/configuration/panels/audit-logging/test/audit-logging.test.tsx @@ -58,7 +58,7 @@ describe('Audit logs', () => { mockAuditLoggingUtils.getAuditLogging = jest.fn().mockReturnValue(mockAuditLoggingData); const component = shallow( - + ); const switchFound = component.find(EuiCompressedSwitch); @@ -76,7 +76,9 @@ describe('Audit logs', () => { mockAuditLoggingUtils.getAuditLogging = jest.fn().mockReturnValue(mockAuditLoggingData); - shallow(); + shallow( + + ); process.nextTick(() => { expect(mockAuditLoggingUtils.getAuditLogging).toHaveBeenCalledTimes(1); @@ -120,7 +122,7 @@ describe('Audit logs', () => { it('audit logging switch change', () => { const component = shallow( - + ); component.find('[data-test-subj="audit-logging-enabled-switch"]').simulate('change'); expect(mockAuditLoggingUtils.updateAuditLogging).toHaveBeenCalledTimes(1); @@ -150,7 +152,7 @@ describe('Audit logs', () => { .mockImplementationOnce(() => [auditLoggingSettings, setState]) .mockImplementationOnce(() => [false, jest.fn()]); const component = shallow( - + ); expect(component).toMatchSnapshot(); }); @@ -163,7 +165,7 @@ describe('Audit logs', () => { .mockImplementationOnce(() => [auditLoggingSettings, setState]) .mockImplementationOnce(() => [false, jest.fn()]); const component = shallow( - + ); component.find('[data-test-subj="general-settings-configure"]').simulate('click'); expect(window.location.hash).toBe( @@ -198,7 +200,7 @@ describe('Audit logs', () => { .mockImplementationOnce(() => [false, jest.fn()]) .mockImplementationOnce(() => [true, jest.fn()]); const component = shallow( - + ); expect(component).toMatchSnapshot(); }); diff --git a/public/apps/configuration/panels/auth-view/test/auth-view.test.tsx b/public/apps/configuration/panels/auth-view/test/auth-view.test.tsx index 5643f86f..91d020da 100644 --- a/public/apps/configuration/panels/auth-view/test/auth-view.test.tsx +++ b/public/apps/configuration/panels/auth-view/test/auth-view.test.tsx @@ -62,7 +62,7 @@ describe('Auth view', () => { it('valid data', (done) => { mockAuthViewUtils.getSecurityConfig = jest.fn().mockReturnValue(config); - shallow(); + shallow(); process.nextTick(() => { expect(mockAuthViewUtils.getSecurityConfig).toHaveBeenCalledTimes(1); @@ -81,7 +81,7 @@ describe('Auth view', () => { jest.spyOn(console, 'log').mockImplementationOnce(() => {}); - shallow(); + shallow(); process.nextTick(() => { expect(mockAuthViewUtils.getSecurityConfig).toHaveBeenCalledTimes(1); @@ -104,7 +104,9 @@ describe('Auth view', () => { mockAuthViewUtils.getSecurityConfig = jest .fn() .mockRejectedValue({ response: { status: 403 } }); - const component = shallow(); + const component = shallow( + + ); expect(component).toMatchSnapshot(); }); }); diff --git a/public/apps/configuration/panels/permission-list/test/permission-list.test.tsx b/public/apps/configuration/panels/permission-list/test/permission-list.test.tsx index daa6a86f..cb26109e 100644 --- a/public/apps/configuration/panels/permission-list/test/permission-list.test.tsx +++ b/public/apps/configuration/panels/permission-list/test/permission-list.test.tsx @@ -100,12 +100,15 @@ describe('Permission list page ', () => { describe('PermissionList', () => { const mockCoreStart = { + uiSettings: { + get: jest.fn().mockReturnValue(false), + }, http: 1, }; it('render empty', () => { const component = shallow( { jest.spyOn(console, 'log').mockImplementationOnce(() => {}); shallow( { }); const component = shallow( { jest.spyOn(React, 'useState').mockImplementation(() => [[sampleActionGroup], jest.fn()]); const component = shallow( { describe('AccessError component', () => { const mockCoreStart = { http: 1, + uiSettings: { + get: jest.fn().mockReturnValue(false), + }, }; let component; beforeEach(() => { diff --git a/public/apps/configuration/panels/role-edit/test/role-edit.test.tsx b/public/apps/configuration/panels/role-edit/test/role-edit.test.tsx index d5051809..80730c0e 100644 --- a/public/apps/configuration/panels/role-edit/test/role-edit.test.tsx +++ b/public/apps/configuration/panels/role-edit/test/role-edit.test.tsx @@ -47,6 +47,9 @@ describe('Role edit', () => { const sampleSourceRole = 'role'; const mockCoreStart = { http: 1, + uiSettings: { + get: jest.fn().mockReturnValue(false), + }, }; const useEffect = jest.spyOn(React, 'useEffect'); diff --git a/public/apps/configuration/panels/role-view/test/role-view.test.tsx b/public/apps/configuration/panels/role-view/test/role-view.test.tsx index 48a76b59..5f1157ca 100644 --- a/public/apps/configuration/panels/role-view/test/role-view.test.tsx +++ b/public/apps/configuration/panels/role-view/test/role-view.test.tsx @@ -82,6 +82,9 @@ describe('Role view', () => { const sampleRole = 'role'; const mockCoreStart = { http: 1, + uiSettings: { + get: jest.fn().mockReturnValue(false), + }, }; const buildBreadcrumbs = jest.fn(); diff --git a/public/apps/configuration/panels/test/role-list.test.tsx b/public/apps/configuration/panels/test/role-list.test.tsx index c5f69363..48c1a99d 100644 --- a/public/apps/configuration/panels/test/role-list.test.tsx +++ b/public/apps/configuration/panels/test/role-list.test.tsx @@ -48,6 +48,9 @@ describe('Role list', () => { const setState = jest.fn(); const mockCoreStart = { http: 1, + uiSettings: { + get: jest.fn().mockReturnValue(false), + }, }; beforeEach(() => { @@ -72,7 +75,7 @@ describe('Role list', () => { const component = shallow( @@ -92,7 +95,7 @@ describe('Role list', () => { shallow( @@ -125,7 +128,7 @@ describe('Role list', () => { shallow( @@ -144,7 +147,7 @@ describe('Role list', () => { shallow( @@ -167,7 +170,7 @@ describe('Role list', () => { shallow( @@ -200,7 +203,7 @@ describe('Role list', () => { component = shallow( @@ -236,7 +239,7 @@ describe('Role list', () => { const wrapper = shallow( @@ -255,6 +258,7 @@ describe('Role list', () => { const wrapper = shallow( { component = shallow( diff --git a/public/apps/configuration/panels/test/user-list.test.tsx b/public/apps/configuration/panels/test/user-list.test.tsx index 980e8d21..d7f6e47f 100644 --- a/public/apps/configuration/panels/test/user-list.test.tsx +++ b/public/apps/configuration/panels/test/user-list.test.tsx @@ -90,6 +90,9 @@ describe('User list', () => { describe('UserList', () => { const mockCoreStart = { http: 1, + uiSettings: { + get: jest.fn().mockReturnValue(false), + }, }; const setState = jest.fn(); jest.spyOn(React, 'useState').mockImplementation((initValue) => [initValue, setState]); @@ -98,7 +101,7 @@ describe('User list', () => { const component = shallow( @@ -132,7 +135,7 @@ describe('User list', () => { shallow( @@ -146,7 +149,7 @@ describe('User list', () => { shallow( @@ -171,7 +174,7 @@ describe('User list', () => { shallow( @@ -194,6 +197,9 @@ describe('User list', () => { serverBasePath: '', }, }, + uiSettings: { + get: jest.fn().mockReturnValue(false), + }, }; let component; const mockUserListingData: InternalUsersListing = { @@ -215,7 +221,7 @@ describe('User list', () => { component = shallow( @@ -244,6 +250,9 @@ describe('User list', () => { serverBasePath: '', }, }, + uiSettings: { + get: jest.fn().mockReturnValue(false), + }, }; let component; beforeEach(() => { @@ -260,7 +269,7 @@ describe('User list', () => { component = shallow( From 2174a3be5f5b7a11b3fe33d9d7eeb850d597d9c3 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Thu, 8 Aug 2024 00:38:08 -0400 Subject: [PATCH 14/24] fix most tests Signed-off-by: Derek Ho --- .../audit-logging/test/audit-logging.test.tsx | 11 +++++-- .../auth-view/test/instruction-view.test.tsx | 3 -- .../test/role-edit-filtering.test.tsx | 12 +++++-- .../panels/role-view/test/role-view.test.tsx | 11 ++++--- .../tenant-list/test/tenant-list.test.tsx | 32 +++++++++---------- .../panels/test/role-list.test.tsx | 3 +- .../panels/test/user-list.test.tsx | 2 +- 7 files changed, 42 insertions(+), 32 deletions(-) diff --git a/public/apps/configuration/panels/audit-logging/test/audit-logging.test.tsx b/public/apps/configuration/panels/audit-logging/test/audit-logging.test.tsx index 572d2259..49fe0f80 100644 --- a/public/apps/configuration/panels/audit-logging/test/audit-logging.test.tsx +++ b/public/apps/configuration/panels/audit-logging/test/audit-logging.test.tsx @@ -37,6 +37,9 @@ describe('Audit logs', () => { const setState = jest.fn(); const mockCoreStart = { http: 1, + uiSettings: { + get: jest.fn().mockReturnValue(false), + }, }; beforeEach(() => { @@ -96,7 +99,9 @@ describe('Audit logs', () => { throw Error(); }); - shallow(); + shallow( + + ); process.nextTick(() => { expect(mockAuditLoggingUtils.getAuditLogging).toHaveBeenCalledTimes(1); @@ -136,7 +141,7 @@ describe('Audit logs', () => { throw Error(); }); const component = shallow( - + ); component.find('[data-test-subj="audit-logging-enabled-switch"]').simulate('change'); @@ -181,7 +186,7 @@ describe('Audit logs', () => { .mockImplementationOnce(() => [auditLoggingSettings, setState]) .mockImplementationOnce(() => [false, jest.fn()]); const component = shallow( - + ); component.find('[data-test-subj="compliance-settings-configure"]').simulate('click'); expect(window.location.hash).toBe( diff --git a/public/apps/configuration/panels/auth-view/test/instruction-view.test.tsx b/public/apps/configuration/panels/auth-view/test/instruction-view.test.tsx index 16a8c664..9ab4902a 100644 --- a/public/apps/configuration/panels/auth-view/test/instruction-view.test.tsx +++ b/public/apps/configuration/panels/auth-view/test/instruction-view.test.tsx @@ -15,7 +15,6 @@ import { shallow } from 'enzyme'; import { InstructionView } from '../instruction-view'; -import { EuiTitle } from '@elastic/eui'; import React from 'react'; import { ExternalLinkButton } from '../../../utils/display-utils'; @@ -27,8 +26,6 @@ describe('Instruction view', () => { }, }; const component = shallow(); - - expect(component.find(EuiTitle).find('h1').text()).toBe('Authentication and authorization'); expect(component.find(ExternalLinkButton).prop('text')).toBe('Create config.yml'); }); }); diff --git a/public/apps/configuration/panels/role-edit/test/role-edit-filtering.test.tsx b/public/apps/configuration/panels/role-edit/test/role-edit-filtering.test.tsx index 36d67fdc..715be82a 100644 --- a/public/apps/configuration/panels/role-edit/test/role-edit-filtering.test.tsx +++ b/public/apps/configuration/panels/role-edit/test/role-edit-filtering.test.tsx @@ -53,6 +53,14 @@ describe('Role edit filtering', () => { const sampleSourceRole = 'role'; const mockCoreStart = { http: 1, + uiSettings: { + get: jest.fn().mockReturnValue(false), + }, + chrome: { + navGroup: { + getNavGroupEnabled: jest.fn().mockReturnValue(false), + }, + }, }; (fetchActionGroups as jest.Mock).mockResolvedValue({ @@ -100,7 +108,7 @@ describe('Role edit filtering', () => { sourceRoleName={sampleSourceRole} buildBreadcrumbs={buildBreadcrumbs} coreStart={mockCoreStart as any} - depsStart={{} as any} + depsStart={{ navigation: { ui: {} } } as any} params={{} as any} config={{} as any} /> @@ -156,7 +164,7 @@ describe('Role edit filtering', () => { sourceRoleName={sampleSourceRole} buildBreadcrumbs={buildBreadcrumbs} coreStart={mockCoreStart as any} - depsStart={{} as any} + depsStart={{ navigation: { ui: {} } } as any} params={{} as any} config={{} as any} /> diff --git a/public/apps/configuration/panels/role-view/test/role-view.test.tsx b/public/apps/configuration/panels/role-view/test/role-view.test.tsx index 5f1157ca..021b44f6 100644 --- a/public/apps/configuration/panels/role-view/test/role-view.test.tsx +++ b/public/apps/configuration/panels/role-view/test/role-view.test.tsx @@ -14,7 +14,7 @@ */ import React from 'react'; -import { mount, shallow } from 'enzyme'; +import { mount, render, shallow } from 'enzyme'; import { RoleView } from '../role-view'; import { ClusterPermissionPanel } from '../../role-view/cluster-permission-panel'; import { IndexPermissionPanel } from '../index-permission-panel'; @@ -85,6 +85,7 @@ describe('Role view', () => { uiSettings: { get: jest.fn().mockReturnValue(false), }, + chrome: { navGroup: { getNavGroupEnabled: jest.fn().mockReturnValue(false) } }, }; const buildBreadcrumbs = jest.fn(); @@ -246,13 +247,13 @@ describe('Role view', () => { throw new Error(); }); const spy = jest.spyOn(console, 'log').mockImplementationOnce(() => {}); - shallow( + render( @@ -268,13 +269,13 @@ describe('Role view', () => { }); it('delete role', () => { - const component = shallow( + const component = render( diff --git a/public/apps/configuration/panels/tenant-list/test/tenant-list.test.tsx b/public/apps/configuration/panels/tenant-list/test/tenant-list.test.tsx index 66117c39..a602f507 100644 --- a/public/apps/configuration/panels/tenant-list/test/tenant-list.test.tsx +++ b/public/apps/configuration/panels/tenant-list/test/tenant-list.test.tsx @@ -60,13 +60,13 @@ describe('Tenant list', () => { const setState = jest.fn(); const mockCoreStart = { http: 1, + uiSettings: { + get: jest.fn().mockReturnValue(false), + }, chrome: { setBreadcrumbs() { return 1; }, - navGroup: { - getNavGroupEnabled: jest.fn().mockReturnValue(false), - }, }, }; const config = { @@ -101,7 +101,7 @@ describe('Tenant list', () => { const component = shallow( @@ -121,7 +121,7 @@ describe('Tenant list', () => { shallow( @@ -151,7 +151,7 @@ describe('Tenant list', () => { shallow( @@ -171,7 +171,7 @@ describe('Tenant list', () => { shallow( @@ -196,7 +196,7 @@ describe('Tenant list', () => { shallow( @@ -215,7 +215,7 @@ describe('Tenant list', () => { const component = shallow( @@ -237,7 +237,7 @@ describe('Tenant list', () => { const component = shallow( @@ -290,7 +290,7 @@ describe('Tenant list', () => { const component = shallow( @@ -319,7 +319,7 @@ describe('Tenant list', () => { const component = shallow( @@ -352,7 +352,7 @@ describe('Tenant list', () => { const component = shallow( @@ -397,7 +397,7 @@ describe('Tenant list', () => { component = shallow( @@ -487,7 +487,7 @@ describe('Tenant list', () => { component = shallow( @@ -499,7 +499,7 @@ describe('Tenant list', () => { component = shallow( diff --git a/public/apps/configuration/panels/test/role-list.test.tsx b/public/apps/configuration/panels/test/role-list.test.tsx index 48c1a99d..4fd5e7ce 100644 --- a/public/apps/configuration/panels/test/role-list.test.tsx +++ b/public/apps/configuration/panels/test/role-list.test.tsx @@ -94,7 +94,7 @@ describe('Role list', () => { shallow( { diff --git a/public/apps/configuration/panels/test/user-list.test.tsx b/public/apps/configuration/panels/test/user-list.test.tsx index d7f6e47f..3b2e062c 100644 --- a/public/apps/configuration/panels/test/user-list.test.tsx +++ b/public/apps/configuration/panels/test/user-list.test.tsx @@ -115,7 +115,7 @@ describe('User list', () => { shallow( From 6bf5b8e3fca2bb431ca0b278f6620a3ebb4fb577 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Thu, 8 Aug 2024 12:43:45 -0400 Subject: [PATCH 15/24] Fix existing tests Signed-off-by: Derek Ho --- .../__snapshots__/audit-logging.test.tsx.snap | 80 +++- .../__snapshots__/auth-view.test.tsx.snap | 30 +- .../permission-list.test.tsx.snap | 156 +++++++- .../__snapshots__/role-view.test.tsx.snap | 370 ++++++++++++++---- .../panels/role-view/test/role-view.test.tsx | 10 +- .../__snapshots__/get-started.test.tsx.snap | 94 +++-- .../__snapshots__/role-list.test.tsx.snap | 107 ++++- .../__snapshots__/user-list.test.tsx.snap | 109 +++++- 8 files changed, 785 insertions(+), 171 deletions(-) diff --git a/public/apps/configuration/panels/audit-logging/test/__snapshots__/audit-logging.test.tsx.snap b/public/apps/configuration/panels/audit-logging/test/__snapshots__/audit-logging.test.tsx.snap index c5b689b9..cd5ae973 100644 --- a/public/apps/configuration/panels/audit-logging/test/__snapshots__/audit-logging.test.tsx.snap +++ b/public/apps/configuration/panels/audit-logging/test/__snapshots__/audit-logging.test.tsx.snap @@ -245,10 +245,17 @@ exports[`Audit logs render when AuditLoggingSettings.enabled is true 1`] = ` coreStart={ Object { "http": 1, + "uiSettings": Object { + "get": [MockFunction], + }, } } dataSourcePickerReadOnly={false} - navigation={Object {}} + depsStart={ + Object { + "navigation": Object {}, + } + } selectedDataSource={ Object { "id": "test", @@ -256,15 +263,28 @@ exports[`Audit logs render when AuditLoggingSettings.enabled is true 1`] = ` } setDataSource={[MockFunction]} /> - - -

- Audit logging -

-
-
+ + +

+ Audit logging +

+
+ + } + navigation={Object {}} + /> @@ -655,10 +675,17 @@ exports[`Audit logs should load access error component 1`] = ` coreStart={ Object { "http": 1, + "uiSettings": Object { + "get": [MockFunction], + }, } } dataSourcePickerReadOnly={false} - navigation={Object {}} + depsStart={ + Object { + "navigation": Object {}, + } + } selectedDataSource={ Object { "id": "test", @@ -666,15 +693,28 @@ exports[`Audit logs should load access error component 1`] = ` } setDataSource={[MockFunction]} /> - - -

- Audit logging -

-
-
+ + +

+ Audit logging +

+
+ + } + navigation={Object {}} + /> - -

- Authentication and authorization -

-
+ +

+ Authentication and authorization +

+ + } + navigation={Object {}} + /> diff --git a/public/apps/configuration/panels/permission-list/test/__snapshots__/permission-list.test.tsx.snap b/public/apps/configuration/panels/permission-list/test/__snapshots__/permission-list.test.tsx.snap index 4b86b497..0a286c12 100644 --- a/public/apps/configuration/panels/permission-list/test/__snapshots__/permission-list.test.tsx.snap +++ b/public/apps/configuration/panels/permission-list/test/__snapshots__/permission-list.test.tsx.snap @@ -7,6 +7,49 @@ exports[`Permission list page AccessError component should load access error co coreStart={ Object { "http": 1, + "uiSettings": Object { + "get": [MockFunction] { + "calls": Array [ + Array [ + "home:useNewHomePage", + ], + Array [ + "home:useNewHomePage", + ], + Array [ + "home:useNewHomePage", + ], + Array [ + "home:useNewHomePage", + ], + Array [ + "home:useNewHomePage", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": false, + }, + Object { + "type": "return", + "value": false, + }, + Object { + "type": "return", + "value": false, + }, + Object { + "type": "return", + "value": false, + }, + Object { + "type": "return", + "value": false, + }, + ], + }, + }, } } dataSourcePickerReadOnly={false} @@ -19,15 +62,110 @@ exports[`Permission list page AccessError component should load access error co } setDataSource={[MockFunction]} /> - - -

- Permissions -

-
-
+ + + Create from blank + + + Create from selection + + , + }, + ] + } + coreStart={ + Object { + "http": 1, + "uiSettings": Object { + "get": [MockFunction] { + "calls": Array [ + Array [ + "home:useNewHomePage", + ], + Array [ + "home:useNewHomePage", + ], + Array [ + "home:useNewHomePage", + ], + Array [ + "home:useNewHomePage", + ], + Array [ + "home:useNewHomePage", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": false, + }, + Object { + "type": "return", + "value": false, + }, + Object { + "type": "return", + "value": false, + }, + Object { + "type": "return", + "value": false, + }, + Object { + "type": "return", + "value": false, + }, + ], + }, + }, + } + } + descriptionControls={ + Array [ + Object { + "isLoading": false, + "renderComponent": + Permissions are individual actions, such as cluster:admin/snapshot/restore, which lets you restore snapshots. Action groups +
+ are reusable collections of permissions, such as MANAGE_SNAPSHOTS, which lets you view, take, delete, and restore +
+ snapshots. You can often meet your security needs using the default action groups, but you might find it convenient to create +
+ your own. + +
, + }, + ] + } + fallBackComponent={ + + +

+ Permissions +

+
+
+ } + /> diff --git a/public/apps/configuration/panels/role-view/test/__snapshots__/role-view.test.tsx.snap b/public/apps/configuration/panels/role-view/test/__snapshots__/role-view.test.tsx.snap index ece52281..b5e526b4 100644 --- a/public/apps/configuration/panels/role-view/test/__snapshots__/role-view.test.tsx.snap +++ b/public/apps/configuration/panels/role-view/test/__snapshots__/role-view.test.tsx.snap @@ -21,7 +21,27 @@ exports[`Role view basic rendering when permission tab is selected 1`] = ` config={Object {}} coreStart={ Object { + "chrome": Object { + "navGroup": Object { + "getNavGroupEnabled": [MockFunction], + }, + }, "http": 1, + "uiSettings": Object { + "get": [MockFunction] { + "calls": Array [ + Array [ + "home:useNewHomePage", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": false, + }, + ], + }, + }, } } dataSourcePickerReadOnly={true} @@ -36,46 +56,101 @@ exports[`Role view basic rendering when permission tab is selected 1`] = ` } setDataSource={[MockFunction]} /> - - - -

- role -

-
-
- - - - - duplicate - - - delete - - - - - Edit role - - - - -
+ + + + +

+ role +

+
+
+ + + + + duplicate + + + delete + + + + + Edit role + + + + +
+ + } + /> - - - -

- role -

-
-
- - - - - duplicate - - - delete - - - - - Edit role - - - - -
+ + + + +

+ role +

+
+
+ + + + + duplicate + + + delete + + + + + Edit role + + + + +
+ + } + /> { }); it('delete role', () => { - const component = render( + const component = mount( { config={{} as any} /> ); - component.find('[data-test-subj="delete"]').simulate('click'); + component.find('[data-test-subj="delete"]').first().simulate('click'); expect(requestDeleteRoles).toBeCalled(); }); @@ -289,18 +289,18 @@ describe('Role view', () => { (requestDeleteRoles as jest.Mock).mockImplementationOnce(() => { throw new Error(); }); - const component = shallow( + const component = mount( ); - component.find('[data-test-subj="delete"]').simulate('click'); + component.find('[data-test-subj="delete"]').first().simulate('click'); expect(createUnknownErrorToast).toBeCalled(); }); }); diff --git a/public/apps/configuration/panels/test/__snapshots__/get-started.test.tsx.snap b/public/apps/configuration/panels/test/__snapshots__/get-started.test.tsx.snap index d380f615..d2edd18e 100644 --- a/public/apps/configuration/panels/test/__snapshots__/get-started.test.tsx.snap +++ b/public/apps/configuration/panels/test/__snapshots__/get-started.test.tsx.snap @@ -31,19 +31,40 @@ exports[`Get started (landing page) renders when backend configuration is disabl } setDataSource={[MockFunction]} /> - - -

- Get started -

-
- -
+ + +

+ Get started +

+
+ + + } + /> @@ -310,19 +331,40 @@ exports[`Get started (landing page) renders when backend configuration is enable } setDataSource={[MockFunction]} /> - - -

- Get started -

-
- -
+ + +

+ Get started +

+
+ + + } + /> diff --git a/public/apps/configuration/panels/test/__snapshots__/role-list.test.tsx.snap b/public/apps/configuration/panels/test/__snapshots__/role-list.test.tsx.snap index 52233ddc..78ec518d 100644 --- a/public/apps/configuration/panels/test/__snapshots__/role-list.test.tsx.snap +++ b/public/apps/configuration/panels/test/__snapshots__/role-list.test.tsx.snap @@ -7,10 +7,29 @@ exports[`Role list AccessError component should load access error component 1`] coreStart={ Object { "http": 1, + "uiSettings": Object { + "get": [MockFunction] { + "calls": Array [ + Array [ + "home:useNewHomePage", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": false, + }, + ], + }, + }, } } dataSourcePickerReadOnly={false} - navigation={Object {}} + depsStart={ + Object { + "navigation": Object {}, + } + } params={Object {}} selectedDataSource={ Object { @@ -19,15 +38,74 @@ exports[`Role list AccessError component should load access error component 1`] } setDataSource={[MockFunction]} /> - - -

- Roles -

-
-
+ + Roles are the core way of controlling access to your cluster. Roles contain any combination of cluster-wide permission, index- +
+ specific permissions, document- and field-level security, and tenants. Then you map users to these roles so that users +
+ gain those permissions. + +
, + }, + ] + } + fallBackComponent={ + + +

+ Roles +

+
+
+ } + navigation={Object {}} + /> - - ( + ( 0 ) @@ -150,7 +227,11 @@ exports[`Role list AccessError component should load access error component 1`] message="No items found" pagination={true} responsive={true} - search={Object {}} + search={ + Object { + "toolsRight": undefined, + } + } selection={ Object { "onSelectionChange": [MockFunction], diff --git a/public/apps/configuration/panels/test/__snapshots__/user-list.test.tsx.snap b/public/apps/configuration/panels/test/__snapshots__/user-list.test.tsx.snap index 82f4af42..efeebad7 100644 --- a/public/apps/configuration/panels/test/__snapshots__/user-list.test.tsx.snap +++ b/public/apps/configuration/panels/test/__snapshots__/user-list.test.tsx.snap @@ -11,10 +11,29 @@ exports[`User list AccessError component should load access error component 1`] "serverBasePath": "", }, }, + "uiSettings": Object { + "get": [MockFunction] { + "calls": Array [ + Array [ + "home:useNewHomePage", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": false, + }, + ], + }, + }, } } dataSourcePickerReadOnly={false} - navigation={Object {}} + depsStart={ + Object { + "navigation": Object {}, + } + } params={Object {}} selectedDataSource={ Object { @@ -23,15 +42,85 @@ exports[`User list AccessError component should load access error component 1`] } setDataSource={[MockFunction]} /> - - -

- Internal users -

-
-
+ + The Security plugin includes an internal user database. Use this database in place of, or in addition to, an external +
+ authentication system such as LDAP server or Active Directory. You can map an internal user to a role from + + + Roles + + . First, click +
+ into the detail page of the role. Then, under “Mapped users”, click “Manage mapping” + + , + }, + ] + } + fallBackComponent={ + + +

+ Internal users +

+
+
+ } + navigation={Object {}} + /> From 132001e8801c386b478707c14777568411ed1b1c Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Fri, 9 Aug 2024 12:01:54 -0400 Subject: [PATCH 16/24] Push breadcrumb population into child components Signed-off-by: Derek Ho --- public/apps/configuration/app-router.tsx | 75 ++++++++++++++----- .../header/header-components.tsx | 14 ++++ .../configuration/header/header-props.tsx | 4 + .../audit-logging-edit-settings.tsx | 4 + .../panels/audit-logging/audit-logging.tsx | 1 + .../__snapshots__/audit-logging.test.tsx.snap | 2 + .../panels/auth-view/auth-view.tsx | 2 + .../apps/configuration/panels/get-started.tsx | 1 + .../permission-list/permission-list.tsx | 2 + .../permission-list.test.tsx.snap | 1 + .../panels/role-edit/role-edit.tsx | 2 - .../test/role-edit-filtering.test.tsx | 1 + .../apps/configuration/panels/role-list.tsx | 1 + .../__snapshots__/role-view.test.tsx.snap | 7 ++ .../panels/role-view/test/role-view.test.tsx | 5 +- .../panels/tenant-list/tenant-list.tsx | 2 + .../__snapshots__/get-started.test.tsx.snap | 2 + .../__snapshots__/role-list.test.tsx.snap | 1 + .../__snapshots__/user-list.test.tsx.snap | 1 + .../apps/configuration/panels/user-list.tsx | 1 + .../__snapshots__/app-router.test.tsx.snap | 70 +++++++++++++++++ .../configuration/test/app-router.test.tsx | 3 + public/apps/configuration/types.ts | 1 + .../configuration/utils/resource-utils.tsx | 48 ++++++++++++ 24 files changed, 228 insertions(+), 23 deletions(-) diff --git a/public/apps/configuration/app-router.tsx b/public/apps/configuration/app-router.tsx index f0dc0cae..74615722 100644 --- a/public/apps/configuration/app-router.tsx +++ b/public/apps/configuration/app-router.tsx @@ -47,22 +47,27 @@ const LANDING_PAGE_URL = '/getstarted'; export const ROUTE_MAP: { [key: string]: RouteItem } = { getStarted: { name: 'Get Started', + displayNameWithoutSecurityBase: 'Get started with access control', href: LANDING_PAGE_URL, }, [ResourceType.roles]: { name: 'Roles', + displayNameWithoutSecurityBase: 'Roles', href: buildUrl(ResourceType.roles), }, [ResourceType.users]: { name: 'Internal users', + displayNameWithoutSecurityBase: 'Internal users', href: buildUrl(ResourceType.users), }, [ResourceType.permissions]: { name: 'Permissions', + displayNameWithoutSecurityBase: 'Permissions', href: buildUrl(ResourceType.permissions), }, [ResourceType.tenants]: { name: 'Tenants', + displayNameWithoutSecurityBase: 'Dashboard multi-tenancy', href: buildUrl(ResourceType.tenants), }, [ResourceType.tenantsConfigureTab]: { @@ -71,10 +76,12 @@ export const ROUTE_MAP: { [key: string]: RouteItem } = { }, [ResourceType.auth]: { name: 'Authentication', + displayNameWithoutSecurityBase: 'Authentication and authorization', href: buildUrl(ResourceType.auth), }, [ResourceType.auditLogging]: { name: 'Audit logs', + displayNameWithoutSecurityBase: 'Audit logs', href: buildUrl(ResourceType.auditLogging), }, }; @@ -101,16 +108,19 @@ export const allNavPanelUrls = (multitenancyEnabled: boolean) => ]); export function getBreadcrumbs( + includeSecurityBase: boolean, resourceType?: ResourceType, pageTitle?: string, subAction?: string ): EuiBreadcrumb[] { - const breadcrumbs: EuiBreadcrumb[] = [ - { - text: 'Security', - href: buildHashUrl(), - }, - ]; + const breadcrumbs: EuiBreadcrumb[] = includeSecurityBase + ? [ + { + text: 'Security', + href: buildHashUrl(), + }, + ] + : []; if (resourceType) { breadcrumbs.push({ @@ -154,6 +164,7 @@ export function AppRouter(props: AppDependencies) { const dataSourceFromUrl = dataSourceEnabled ? getDataSourceFromUrl() : LocalCluster; const [dataSource, setDataSource] = useState(dataSourceFromUrl); + const includeSecurityBase = !props.coreStart.uiSettings.get('home:useNewHomePage'); return ( @@ -174,7 +185,11 @@ export function AppRouter(props: AppDependencies) { path={buildUrl(ResourceType.roles, Action.edit) + '/:roleName/' + SubAction.mapuser} render={(match) => ( )} @@ -183,7 +198,11 @@ export function AppRouter(props: AppDependencies) { path={buildUrl(ResourceType.roles, Action.view) + '/:roleName/:prevAction?'} render={(match) => ( )} @@ -192,7 +211,11 @@ export function AppRouter(props: AppDependencies) { path={buildUrl(ResourceType.roles) + '/:action/:sourceRoleName?'} render={(match) => ( )} @@ -200,14 +223,14 @@ export function AppRouter(props: AppDependencies) { { - setGlobalBreadcrumbs(ResourceType.roles); + // setGlobalBreadcrumbs(includeSecurityBase, ResourceType.roles); return ; }} /> { - setGlobalBreadcrumbs(ResourceType.auth); + // setGlobalBreadcrumbs(includeSecurityBase, ResourceType.auth); return ; }} /> @@ -215,7 +238,11 @@ export function AppRouter(props: AppDependencies) { path={buildUrl(ResourceType.users) + '/:action/:sourceUserName?'} render={(match) => ( )} @@ -223,42 +250,50 @@ export function AppRouter(props: AppDependencies) { { - setGlobalBreadcrumbs(ResourceType.users); + // setGlobalBreadcrumbs(includeSecurityBase, ResourceType.users); return ; }} /> { - setGlobalBreadcrumbs(ResourceType.auditLogging, 'General settings'); + // setGlobalBreadcrumbs( + // includeSecurityBase, + // ResourceType.auditLogging, + // 'General settings' + // ); return ; }} /> { - setGlobalBreadcrumbs(ResourceType.auditLogging, 'Compliance settings'); + // setGlobalBreadcrumbs( + // includeSecurityBase, + // ResourceType.auditLogging, + // 'Compliance settings' + // ); return ; }} /> { - setGlobalBreadcrumbs(ResourceType.auditLogging); + // setGlobalBreadcrumbs(includeSecurityBase, ResourceType.auditLogging); return ; }} /> { - setGlobalBreadcrumbs(ResourceType.permissions); + // setGlobalBreadcrumbs(includeSecurityBase, ResourceType.permissions); return ; }} /> { - setGlobalBreadcrumbs(); + // setGlobalBreadcrumbs(includeSecurityBase); return ; }} /> @@ -266,7 +301,7 @@ export function AppRouter(props: AppDependencies) { { - setGlobalBreadcrumbs(ResourceType.tenants); + // setGlobalBreadcrumbs(includeSecurityBase, ResourceType.tenants); return ; }} /> @@ -275,7 +310,7 @@ export function AppRouter(props: AppDependencies) { { - setGlobalBreadcrumbs(ResourceType.tenants); + // setGlobalBreadcrumbs(includeSecurityBase, ResourceType.tenants); return ; }} /> diff --git a/public/apps/configuration/header/header-components.tsx b/public/apps/configuration/header/header-components.tsx index a4bd9d83..59e30e65 100644 --- a/public/apps/configuration/header/header-components.tsx +++ b/public/apps/configuration/header/header-components.tsx @@ -16,7 +16,9 @@ import React from 'react'; import { TopNavControlData } from 'src/plugins/navigation/public/top_nav_menu/top_nav_control_data'; import { EuiTitle } from '@elastic/eui'; +import { flow } from 'lodash'; import { ControlProps, DescriptionProps, HeaderProps, TitleProps } from './header-props'; +import { getBreadcrumbs } from '../utils/resource-utils'; // controlType should be one of: https://github.com/AMoo-Miki/OpenSearch-Dashboards/blob/header-collective/src/plugins/navigation/public/top_nav_menu/top_nav_control_data.tsx#L91 @@ -72,6 +74,12 @@ export const PageHeader = (props: HeaderProps & DescriptionProps & ControlProps) const { HeaderControl } = props.navigation.ui; // need to get this from SecurityPluginStartDependencies const useNewUx = props.coreStart.chrome.navGroup.getNavGroupEnabled(); if (useNewUx) { + flow(getBreadcrumbs, props.coreStart.chrome.setBreadcrumbs)( + !props.coreStart.uiSettings.get('home:useNewHomePage'), + props.resourceType, + props.pageTitle, + props.subAction + ); return ( <> {props.descriptionControls ? ( @@ -89,6 +97,12 @@ export const PageHeader = (props: HeaderProps & DescriptionProps & ControlProps) ); } else { + flow(getBreadcrumbs, props.coreStart.chrome.setBreadcrumbs)( + !props.coreStart.uiSettings.get('home:useNewHomePage'), + props.resourceType, + props.pageTitle, + props.subAction + ); return props.fallBackComponent; } }; diff --git a/public/apps/configuration/header/header-props.tsx b/public/apps/configuration/header/header-props.tsx index 0a6712eb..e716b425 100644 --- a/public/apps/configuration/header/header-props.tsx +++ b/public/apps/configuration/header/header-props.tsx @@ -14,6 +14,7 @@ */ import { CoreStart } from 'opensearch-dashboards/public'; +import { ResourceType } from 'plugins/security-dashboards-plugin/common'; import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; import { TopNavControlData } from 'src/plugins/navigation/public/top_nav_menu/top_nav_control_data'; @@ -21,6 +22,9 @@ export interface HeaderProps { navigation: NavigationPublicPluginStart; coreStart: CoreStart; fallBackComponent: JSX.Element; + resourceType?: ResourceType; + pageTitle?: string; + subAction?: string; } export interface ControlProps { diff --git a/public/apps/configuration/panels/audit-logging/audit-logging-edit-settings.tsx b/public/apps/configuration/panels/audit-logging/audit-logging-edit-settings.tsx index 1f90442b..4de73094 100644 --- a/public/apps/configuration/panels/audit-logging/audit-logging-edit-settings.tsx +++ b/public/apps/configuration/panels/audit-logging/audit-logging-edit-settings.tsx @@ -166,6 +166,8 @@ export function AuditLoggingEditSettings(props: AuditLoggingEditSettingProps) { } + resourceType={ResourceType.auditLogging} + pageTitle="Compliance settings" /> @@ -224,6 +226,8 @@ export function AuditLoggingEditSettings(props: AuditLoggingEditSettingProps) { } + resourceType={ResourceType.auditLogging} + pageTitle="General settings" /> diff --git a/public/apps/configuration/panels/audit-logging/audit-logging.tsx b/public/apps/configuration/panels/audit-logging/audit-logging.tsx index 0eb46b61..efaa13d1 100644 --- a/public/apps/configuration/panels/audit-logging/audit-logging.tsx +++ b/public/apps/configuration/panels/audit-logging/audit-logging.tsx @@ -269,6 +269,7 @@ export function AuditLogging(props: AuditLoggingProps) { } + resourceType={ResourceType.auditLogging} /> {loading ? : content} diff --git a/public/apps/configuration/panels/audit-logging/test/__snapshots__/audit-logging.test.tsx.snap b/public/apps/configuration/panels/audit-logging/test/__snapshots__/audit-logging.test.tsx.snap index cd5ae973..fa157563 100644 --- a/public/apps/configuration/panels/audit-logging/test/__snapshots__/audit-logging.test.tsx.snap +++ b/public/apps/configuration/panels/audit-logging/test/__snapshots__/audit-logging.test.tsx.snap @@ -284,6 +284,7 @@ exports[`Audit logs render when AuditLoggingSettings.enabled is true 1`] = ` } navigation={Object {}} + resourceType="auditLogging" /> @@ -714,6 +715,7 @@ exports[`Audit logs should load access error component 1`] = ` } navigation={Object {}} + resourceType="auditLogging" /> } + resourceType={ResourceType.auth} /> {loading ? ( diff --git a/public/apps/configuration/panels/get-started.tsx b/public/apps/configuration/panels/get-started.tsx index fb7355b8..e0a965ee 100644 --- a/public/apps/configuration/panels/get-started.tsx +++ b/public/apps/configuration/panels/get-started.tsx @@ -206,6 +206,7 @@ export function GetStarted(props: AppDependencies) { } + resourceType={'getStarted'} /> diff --git a/public/apps/configuration/panels/permission-list/permission-list.tsx b/public/apps/configuration/panels/permission-list/permission-list.tsx index 8406f005..42922c77 100644 --- a/public/apps/configuration/panels/permission-list/permission-list.tsx +++ b/public/apps/configuration/panels/permission-list/permission-list.tsx @@ -66,6 +66,7 @@ import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; import { DataSourceContext } from '../../app-router'; import { AccessErrorComponent } from '../../access-error-component'; import { PageHeader } from '../../header/header-components'; +import { ResourceType } from '../../../../../common'; export function renderBooleanToCheckMark(value: boolean): React.ReactNode { return value ? : ''; @@ -409,6 +410,7 @@ export function PermissionList(props: AppDependencies) { } + resourceType={ResourceType.permissions} /> {loading ? ( diff --git a/public/apps/configuration/panels/permission-list/test/__snapshots__/permission-list.test.tsx.snap b/public/apps/configuration/panels/permission-list/test/__snapshots__/permission-list.test.tsx.snap index 0a286c12..2bc30b2c 100644 --- a/public/apps/configuration/panels/permission-list/test/__snapshots__/permission-list.test.tsx.snap +++ b/public/apps/configuration/panels/permission-list/test/__snapshots__/permission-list.test.tsx.snap @@ -165,6 +165,7 @@ exports[`Permission list page AccessError component should load access error co } + resourceType="permissions" /> { navGroup: { getNavGroupEnabled: jest.fn().mockReturnValue(false), }, + setBreadcrumbs: jest.fn(), }, }; diff --git a/public/apps/configuration/panels/role-list.tsx b/public/apps/configuration/panels/role-list.tsx index 03b0494d..94b642bc 100644 --- a/public/apps/configuration/panels/role-list.tsx +++ b/public/apps/configuration/panels/role-list.tsx @@ -314,6 +314,7 @@ export function RoleList(props: AppDependencies) { } + resourceType={ResourceType.roles} /> {loading ? ( diff --git a/public/apps/configuration/panels/role-view/test/__snapshots__/role-view.test.tsx.snap b/public/apps/configuration/panels/role-view/test/__snapshots__/role-view.test.tsx.snap index b5e526b4..c7ca56a9 100644 --- a/public/apps/configuration/panels/role-view/test/__snapshots__/role-view.test.tsx.snap +++ b/public/apps/configuration/panels/role-view/test/__snapshots__/role-view.test.tsx.snap @@ -25,6 +25,7 @@ exports[`Role view basic rendering when permission tab is selected 1`] = ` "navGroup": Object { "getNavGroupEnabled": [MockFunction], }, + "setBreadcrumbs": [MockFunction], }, "http": 1, "uiSettings": Object { @@ -89,6 +90,7 @@ exports[`Role view basic rendering when permission tab is selected 1`] = ` "navGroup": Object { "getNavGroupEnabled": [MockFunction], }, + "setBreadcrumbs": [MockFunction], }, "http": 1, "uiSettings": Object { @@ -190,6 +192,7 @@ exports[`Role view basic rendering when permission tab is selected 1`] = ` "navGroup": Object { "getNavGroupEnabled": [MockFunction], }, + "setBreadcrumbs": [MockFunction], }, "http": 1, "uiSettings": Object { @@ -260,6 +263,7 @@ exports[`Role view basic rendering when permission tab is selected 1`] = ` "navGroup": Object { "getNavGroupEnabled": [MockFunction], }, + "setBreadcrumbs": [MockFunction], }, "http": 1, "uiSettings": Object { @@ -467,6 +471,7 @@ exports[`Role view renders when mapped user tab is selected 1`] = ` "navGroup": Object { "getNavGroupEnabled": [MockFunction], }, + "setBreadcrumbs": [MockFunction], }, "http": 1, "uiSettings": Object { @@ -531,6 +536,7 @@ exports[`Role view renders when mapped user tab is selected 1`] = ` "navGroup": Object { "getNavGroupEnabled": [MockFunction], }, + "setBreadcrumbs": [MockFunction], }, "http": 1, "uiSettings": Object { @@ -773,6 +779,7 @@ exports[`Role view renders when mapped user tab is selected 1`] = ` "navGroup": Object { "getNavGroupEnabled": [MockFunction], }, + "setBreadcrumbs": [MockFunction], }, "http": 1, "uiSettings": Object { diff --git a/public/apps/configuration/panels/role-view/test/role-view.test.tsx b/public/apps/configuration/panels/role-view/test/role-view.test.tsx index 6b3dac86..52d48b5c 100644 --- a/public/apps/configuration/panels/role-view/test/role-view.test.tsx +++ b/public/apps/configuration/panels/role-view/test/role-view.test.tsx @@ -85,7 +85,10 @@ describe('Role view', () => { uiSettings: { get: jest.fn().mockReturnValue(false), }, - chrome: { navGroup: { getNavGroupEnabled: jest.fn().mockReturnValue(false) } }, + chrome: { + navGroup: { getNavGroupEnabled: jest.fn().mockReturnValue(false) }, + setBreadcrumbs: jest.fn(), + }, }; const buildBreadcrumbs = jest.fn(); diff --git a/public/apps/configuration/panels/tenant-list/tenant-list.tsx b/public/apps/configuration/panels/tenant-list/tenant-list.tsx index 7fdcf50c..aa5ee9e4 100644 --- a/public/apps/configuration/panels/tenant-list/tenant-list.tsx +++ b/public/apps/configuration/panels/tenant-list/tenant-list.tsx @@ -33,6 +33,7 @@ import { getDashboardsInfo } from '../../../../utils/dashboards-info-utils'; import { LocalCluster } from '../../../../utils/datasource-utils'; import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; import { PageHeader } from '../../header/header-components'; +import { ResourceType } from '../../../../../common'; interface TenantListProps extends AppDependencies { tabID: string; @@ -171,6 +172,7 @@ export function TenantList(props: TenantListProps) { } + resourceType={ResourceType.tenants} /> {renderTabs()} diff --git a/public/apps/configuration/panels/test/__snapshots__/get-started.test.tsx.snap b/public/apps/configuration/panels/test/__snapshots__/get-started.test.tsx.snap index d2edd18e..574297b0 100644 --- a/public/apps/configuration/panels/test/__snapshots__/get-started.test.tsx.snap +++ b/public/apps/configuration/panels/test/__snapshots__/get-started.test.tsx.snap @@ -64,6 +64,7 @@ exports[`Get started (landing page) renders when backend configuration is disabl /> } + resourceType="getStarted" /> } + resourceType="getStarted" /> } navigation={Object {}} + resourceType="roles" /> } navigation={Object {}} + resourceType="users" /> } + resourceType={ResourceType.users} /> {loading ? ( diff --git a/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap b/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap index d5ee503b..7f96d34b 100644 --- a/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap +++ b/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap @@ -24,30 +24,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { + "displayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { + "displayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { + "displayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { + "displayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { + "displayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { + "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { + "displayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -66,30 +73,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { + "displayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { + "displayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { + "displayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { + "displayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { + "displayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { + "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { + "displayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -108,30 +122,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { + "displayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { + "displayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { + "displayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { + "displayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { + "displayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { + "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { + "displayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -150,30 +171,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { + "displayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { + "displayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { + "displayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { + "displayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { + "displayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { + "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { + "displayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -192,30 +220,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { + "displayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { + "displayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { + "displayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { + "displayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { + "displayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { + "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { + "displayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -234,30 +269,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { + "displayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { + "displayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { + "displayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { + "displayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { + "displayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { + "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { + "displayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -276,30 +318,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { + "displayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { + "displayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { + "displayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { + "displayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { + "displayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { + "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { + "displayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -318,30 +367,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { + "displayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { + "displayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { + "displayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { + "displayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { + "displayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { + "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { + "displayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -360,30 +416,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { + "displayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { + "displayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { + "displayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { + "displayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { + "displayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { + "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { + "displayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -402,30 +465,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { + "displayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { + "displayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { + "displayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { + "displayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { + "displayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { + "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { + "displayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, diff --git a/public/apps/configuration/test/app-router.test.tsx b/public/apps/configuration/test/app-router.test.tsx index 4bb61888..ff3ededf 100644 --- a/public/apps/configuration/test/app-router.test.tsx +++ b/public/apps/configuration/test/app-router.test.tsx @@ -35,6 +35,9 @@ describe('SecurityPluginTopNavMenu', () => { getNavGroupEnabled: jest.fn().mockReturnValue(false), }, }, + uiSettings: { + get: jest.fn().mockReturnValue(false), + }, }; const securityPluginConfigMock = { diff --git a/public/apps/configuration/types.ts b/public/apps/configuration/types.ts index 967072d1..adbede12 100644 --- a/public/apps/configuration/types.ts +++ b/public/apps/configuration/types.ts @@ -46,6 +46,7 @@ export enum TenantPermissionType { export interface RouteItem { name: string; href: string; + displayNameWithoutSecurityBase: string; } export interface DataObject { diff --git a/public/apps/configuration/utils/resource-utils.tsx b/public/apps/configuration/utils/resource-utils.tsx index ac30e425..aa56b4bf 100644 --- a/public/apps/configuration/utils/resource-utils.tsx +++ b/public/apps/configuration/utils/resource-utils.tsx @@ -13,6 +13,11 @@ * permissions and limitations under the License. */ +import { EuiBreadcrumb } from '@elastic/eui'; +import { ResourceType } from 'plugins/security-dashboards-plugin/common'; +import { buildHashUrl } from './url-builder'; +import { ROUTE_MAP } from '../app-router'; + export function generateResourceName(action: string, sourceResourceName: string): string { switch (action) { case 'edit': @@ -27,3 +32,46 @@ export function generateResourceName(action: string, sourceResourceName: string) export function getResourceUrl(endpoint: string, resourceName: string) { return endpoint + '/' + encodeURIComponent(resourceName); } + +export function getBreadcrumbs( + includeSecurityBase: boolean, + resourceType?: ResourceType, + pageTitle?: string, + subAction?: string +): EuiBreadcrumb[] { + const breadcrumbs: EuiBreadcrumb[] = includeSecurityBase + ? [ + { + text: 'Security', + href: buildHashUrl(), + }, + ] + : []; + + if (resourceType) { + if (includeSecurityBase) { + breadcrumbs.push({ + text: ROUTE_MAP[resourceType].name, + href: buildHashUrl(resourceType), + }); + } else { + breadcrumbs.push({ + text: ROUTE_MAP[resourceType].displayNameWithoutSecurityBase, + href: buildHashUrl(resourceType), + }); + } + } + + if (pageTitle) { + breadcrumbs.push({ + text: pageTitle, + }); + } + + if (subAction) { + breadcrumbs.push({ + text: subAction, + }); + } + return breadcrumbs; +} From 44983ad4997c54fa127402e1c56b182bf408f876 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Fri, 9 Aug 2024 14:44:29 -0400 Subject: [PATCH 17/24] Migrate breadcrumbs into the page header component for the top level of all pages Signed-off-by: Derek Ho --- public/apps/configuration/app-router.tsx | 55 +------------------ .../header/header-components.tsx | 20 +++---- .../configuration/header/header-props.tsx | 1 + .../permission-list/permission-list.tsx | 1 + .../permission-list.test.tsx.snap | 1 + .../apps/configuration/panels/role-list.tsx | 1 + .../panels/tenant-list/manage_tab.tsx | 3 +- .../__snapshots__/role-list.test.tsx.snap | 1 + .../__snapshots__/user-list.test.tsx.snap | 1 + .../apps/configuration/panels/user-list.tsx | 1 + .../configuration/utils/resource-utils.tsx | 8 ++- 11 files changed, 22 insertions(+), 71 deletions(-) diff --git a/public/apps/configuration/app-router.tsx b/public/apps/configuration/app-router.tsx index 74615722..ddcc0494 100644 --- a/public/apps/configuration/app-router.tsx +++ b/public/apps/configuration/app-router.tsx @@ -41,6 +41,7 @@ import { ResourceType } from '../../../common'; import { buildHashUrl, buildUrl } from './utils/url-builder'; import { CrossPageToast } from './cross-page-toast'; import { getDataSourceFromUrl, LocalCluster } from '../../utils/datasource-utils'; +import { getBreadcrumbs } from './utils/resource-utils'; const LANDING_PAGE_URL = '/getstarted'; @@ -107,42 +108,6 @@ export const allNavPanelUrls = (multitenancyEnabled: boolean) => ...(multitenancyEnabled ? [buildUrl(ResourceType.tenantsConfigureTab)] : []), ]); -export function getBreadcrumbs( - includeSecurityBase: boolean, - resourceType?: ResourceType, - pageTitle?: string, - subAction?: string -): EuiBreadcrumb[] { - const breadcrumbs: EuiBreadcrumb[] = includeSecurityBase - ? [ - { - text: 'Security', - href: buildHashUrl(), - }, - ] - : []; - - if (resourceType) { - breadcrumbs.push({ - text: ROUTE_MAP[resourceType].name, - href: buildHashUrl(resourceType), - }); - } - - if (pageTitle) { - breadcrumbs.push({ - text: pageTitle, - }); - } - - if (subAction) { - breadcrumbs.push({ - text: subAction, - }); - } - return breadcrumbs; -} - function decodeParams(params: { [k: string]: string }): any { return Object.keys(params).reduce((obj: { [k: string]: string }, key: string) => { obj[key] = decodeURIComponent(params[key]); @@ -223,14 +188,12 @@ export function AppRouter(props: AppDependencies) { { - // setGlobalBreadcrumbs(includeSecurityBase, ResourceType.roles); return ; }} /> { - // setGlobalBreadcrumbs(includeSecurityBase, ResourceType.auth); return ; }} /> @@ -250,50 +213,36 @@ export function AppRouter(props: AppDependencies) { { - // setGlobalBreadcrumbs(includeSecurityBase, ResourceType.users); return ; }} /> { - // setGlobalBreadcrumbs( - // includeSecurityBase, - // ResourceType.auditLogging, - // 'General settings' - // ); return ; }} /> { - // setGlobalBreadcrumbs( - // includeSecurityBase, - // ResourceType.auditLogging, - // 'Compliance settings' - // ); return ; }} /> { - // setGlobalBreadcrumbs(includeSecurityBase, ResourceType.auditLogging); return ; }} /> { - // setGlobalBreadcrumbs(includeSecurityBase, ResourceType.permissions); return ; }} /> { - // setGlobalBreadcrumbs(includeSecurityBase); return ; }} /> @@ -301,7 +250,6 @@ export function AppRouter(props: AppDependencies) { { - // setGlobalBreadcrumbs(includeSecurityBase, ResourceType.tenants); return ; }} /> @@ -310,7 +258,6 @@ export function AppRouter(props: AppDependencies) { { - // setGlobalBreadcrumbs(includeSecurityBase, ResourceType.tenants); return ; }} /> diff --git a/public/apps/configuration/header/header-components.tsx b/public/apps/configuration/header/header-components.tsx index 59e30e65..e3437c8f 100644 --- a/public/apps/configuration/header/header-components.tsx +++ b/public/apps/configuration/header/header-components.tsx @@ -73,13 +73,15 @@ export const HeaderDescription = React.memo((props: DescriptionProps) => { export const PageHeader = (props: HeaderProps & DescriptionProps & ControlProps) => { const { HeaderControl } = props.navigation.ui; // need to get this from SecurityPluginStartDependencies const useNewUx = props.coreStart.chrome.navGroup.getNavGroupEnabled(); + flow(getBreadcrumbs, props.coreStart.chrome.setBreadcrumbs)( + !props.coreStart.uiSettings.get('home:useNewHomePage'), + props.resourceType, + props.pageTitle, + props.subAction, + props.count + ); if (useNewUx) { - flow(getBreadcrumbs, props.coreStart.chrome.setBreadcrumbs)( - !props.coreStart.uiSettings.get('home:useNewHomePage'), - props.resourceType, - props.pageTitle, - props.subAction - ); + return ( <> {props.descriptionControls ? ( @@ -97,12 +99,6 @@ export const PageHeader = (props: HeaderProps & DescriptionProps & ControlProps) ); } else { - flow(getBreadcrumbs, props.coreStart.chrome.setBreadcrumbs)( - !props.coreStart.uiSettings.get('home:useNewHomePage'), - props.resourceType, - props.pageTitle, - props.subAction - ); return props.fallBackComponent; } }; diff --git a/public/apps/configuration/header/header-props.tsx b/public/apps/configuration/header/header-props.tsx index e716b425..9e532e2e 100644 --- a/public/apps/configuration/header/header-props.tsx +++ b/public/apps/configuration/header/header-props.tsx @@ -25,6 +25,7 @@ export interface HeaderProps { resourceType?: ResourceType; pageTitle?: string; subAction?: string; + count?: number; } export interface ControlProps { diff --git a/public/apps/configuration/panels/permission-list/permission-list.tsx b/public/apps/configuration/panels/permission-list/permission-list.tsx index 42922c77..bc17f93e 100644 --- a/public/apps/configuration/panels/permission-list/permission-list.tsx +++ b/public/apps/configuration/panels/permission-list/permission-list.tsx @@ -411,6 +411,7 @@ export function PermissionList(props: AppDependencies) { } resourceType={ResourceType.permissions} + count={permissionList.length} /> {loading ? ( diff --git a/public/apps/configuration/panels/permission-list/test/__snapshots__/permission-list.test.tsx.snap b/public/apps/configuration/panels/permission-list/test/__snapshots__/permission-list.test.tsx.snap index 2bc30b2c..04c70f89 100644 --- a/public/apps/configuration/panels/permission-list/test/__snapshots__/permission-list.test.tsx.snap +++ b/public/apps/configuration/panels/permission-list/test/__snapshots__/permission-list.test.tsx.snap @@ -132,6 +132,7 @@ exports[`Permission list page AccessError component should load access error co }, } } + count={0} descriptionControls={ Array [ Object { diff --git a/public/apps/configuration/panels/role-list.tsx b/public/apps/configuration/panels/role-list.tsx index 94b642bc..94635a2a 100644 --- a/public/apps/configuration/panels/role-list.tsx +++ b/public/apps/configuration/panels/role-list.tsx @@ -315,6 +315,7 @@ export function RoleList(props: AppDependencies) { } resourceType={ResourceType.roles} + count={roleData.length} /> {loading ? ( diff --git a/public/apps/configuration/panels/tenant-list/manage_tab.tsx b/public/apps/configuration/panels/tenant-list/manage_tab.tsx index c5307dd1..2e3f0e8e 100644 --- a/public/apps/configuration/panels/tenant-list/manage_tab.tsx +++ b/public/apps/configuration/panels/tenant-list/manage_tab.tsx @@ -66,10 +66,9 @@ import { PageId } from '../../types'; import { useDeleteConfirmState } from '../../utils/delete-confirm-modal-utils'; import { showTableStatusMessage } from '../../utils/loading-spinner-utils'; import { useContextMenuState } from '../../utils/context-menu'; -import { generateResourceName } from '../../utils/resource-utils'; +import { generateResourceName, getBreadcrumbs } from '../../utils/resource-utils'; import { DocLinks } from '../../constants'; import { TenantList } from './tenant-list'; -import { getBreadcrumbs } from '../../app-router'; import { LocalCluster } from '../../../../utils/datasource-utils'; import { buildUrl } from '../../utils/url-builder'; import { CrossPageToast } from '../../cross-page-toast'; diff --git a/public/apps/configuration/panels/test/__snapshots__/role-list.test.tsx.snap b/public/apps/configuration/panels/test/__snapshots__/role-list.test.tsx.snap index 7b4c6ad7..f55060ec 100644 --- a/public/apps/configuration/panels/test/__snapshots__/role-list.test.tsx.snap +++ b/public/apps/configuration/panels/test/__snapshots__/role-list.test.tsx.snap @@ -73,6 +73,7 @@ exports[`Role list AccessError component should load access error component 1`] }, } } + count={0} descriptionControls={ Array [ Object { diff --git a/public/apps/configuration/panels/test/__snapshots__/user-list.test.tsx.snap b/public/apps/configuration/panels/test/__snapshots__/user-list.test.tsx.snap index 6a3e0719..f069a919 100644 --- a/public/apps/configuration/panels/test/__snapshots__/user-list.test.tsx.snap +++ b/public/apps/configuration/panels/test/__snapshots__/user-list.test.tsx.snap @@ -81,6 +81,7 @@ exports[`User list AccessError component should load access error component 1`] }, } } + count={0} descriptionControls={ Array [ Object { diff --git a/public/apps/configuration/panels/user-list.tsx b/public/apps/configuration/panels/user-list.tsx index dab0129f..ac89e280 100644 --- a/public/apps/configuration/panels/user-list.tsx +++ b/public/apps/configuration/panels/user-list.tsx @@ -257,6 +257,7 @@ export function UserList(props: AppDependencies) { } resourceType={ResourceType.users} + count={userData.length} /> {loading ? ( diff --git a/public/apps/configuration/utils/resource-utils.tsx b/public/apps/configuration/utils/resource-utils.tsx index aa56b4bf..e0e3e571 100644 --- a/public/apps/configuration/utils/resource-utils.tsx +++ b/public/apps/configuration/utils/resource-utils.tsx @@ -14,7 +14,8 @@ */ import { EuiBreadcrumb } from '@elastic/eui'; -import { ResourceType } from 'plugins/security-dashboards-plugin/common'; +import { ResourceType } from '../../../../common'; +import { i18n } from '@osd/i18n'; import { buildHashUrl } from './url-builder'; import { ROUTE_MAP } from '../app-router'; @@ -37,7 +38,8 @@ export function getBreadcrumbs( includeSecurityBase: boolean, resourceType?: ResourceType, pageTitle?: string, - subAction?: string + subAction?: string, + count?: number, ): EuiBreadcrumb[] { const breadcrumbs: EuiBreadcrumb[] = includeSecurityBase ? [ @@ -56,7 +58,7 @@ export function getBreadcrumbs( }); } else { breadcrumbs.push({ - text: ROUTE_MAP[resourceType].displayNameWithoutSecurityBase, + text: count ? `${ROUTE_MAP[resourceType].displayNameWithoutSecurityBase} (${count})` : ROUTE_MAP[resourceType].displayNameWithoutSecurityBase, href: buildHashUrl(resourceType), }); } From a09d6437d7e55bfd2524baff33a75da9eac74342 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Fri, 9 Aug 2024 14:44:53 -0400 Subject: [PATCH 18/24] Lint Signed-off-by: Derek Ho --- public/apps/configuration/app-router.tsx | 4 ++-- public/apps/configuration/header/header-components.tsx | 1 - public/apps/configuration/utils/resource-utils.tsx | 7 ++++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/public/apps/configuration/app-router.tsx b/public/apps/configuration/app-router.tsx index ddcc0494..f83bb331 100644 --- a/public/apps/configuration/app-router.tsx +++ b/public/apps/configuration/app-router.tsx @@ -13,7 +13,7 @@ * permissions and limitations under the License. */ -import { EuiBreadcrumb, EuiPage, EuiPageBody, EuiPageSideBar } from '@elastic/eui'; +import { EuiPage, EuiPageBody, EuiPageSideBar } from '@elastic/eui'; import { flow, partial } from 'lodash'; import React, { createContext, useState } from 'react'; import { HashRouter as Router, Route, Switch, Redirect } from 'react-router-dom'; @@ -38,7 +38,7 @@ import { TenantList } from './panels/tenant-list/tenant-list'; import { UserList } from './panels/user-list'; import { Action, RouteItem, SubAction } from './types'; import { ResourceType } from '../../../common'; -import { buildHashUrl, buildUrl } from './utils/url-builder'; +import { buildUrl } from './utils/url-builder'; import { CrossPageToast } from './cross-page-toast'; import { getDataSourceFromUrl, LocalCluster } from '../../utils/datasource-utils'; import { getBreadcrumbs } from './utils/resource-utils'; diff --git a/public/apps/configuration/header/header-components.tsx b/public/apps/configuration/header/header-components.tsx index e3437c8f..05d0f566 100644 --- a/public/apps/configuration/header/header-components.tsx +++ b/public/apps/configuration/header/header-components.tsx @@ -81,7 +81,6 @@ export const PageHeader = (props: HeaderProps & DescriptionProps & ControlProps) props.count ); if (useNewUx) { - return ( <> {props.descriptionControls ? ( diff --git a/public/apps/configuration/utils/resource-utils.tsx b/public/apps/configuration/utils/resource-utils.tsx index e0e3e571..708b64f2 100644 --- a/public/apps/configuration/utils/resource-utils.tsx +++ b/public/apps/configuration/utils/resource-utils.tsx @@ -15,7 +15,6 @@ import { EuiBreadcrumb } from '@elastic/eui'; import { ResourceType } from '../../../../common'; -import { i18n } from '@osd/i18n'; import { buildHashUrl } from './url-builder'; import { ROUTE_MAP } from '../app-router'; @@ -39,7 +38,7 @@ export function getBreadcrumbs( resourceType?: ResourceType, pageTitle?: string, subAction?: string, - count?: number, + count?: number ): EuiBreadcrumb[] { const breadcrumbs: EuiBreadcrumb[] = includeSecurityBase ? [ @@ -58,7 +57,9 @@ export function getBreadcrumbs( }); } else { breadcrumbs.push({ - text: count ? `${ROUTE_MAP[resourceType].displayNameWithoutSecurityBase} (${count})` : ROUTE_MAP[resourceType].displayNameWithoutSecurityBase, + text: count + ? `${ROUTE_MAP[resourceType].displayNameWithoutSecurityBase} (${count})` + : ROUTE_MAP[resourceType].displayNameWithoutSecurityBase, href: buildHashUrl(resourceType), }); } From 9b74854086f0af2a757bfe55df7299907845ba95 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Fri, 9 Aug 2024 18:09:02 -0400 Subject: [PATCH 19/24] Update all instances of breadcrumbs to new component and all existing tests pass Signed-off-by: Derek Ho --- public/apps/configuration/app-router.tsx | 38 +++-------------- .../header/header-components.tsx | 40 +----------------- .../internal-user-edit/internal-user-edit.tsx | 3 +- .../test/internal-user-edit.test.tsx | 2 - .../panels/role-edit/role-edit.tsx | 3 +- .../panels/role-edit/test/role-edit.test.tsx | 5 --- .../role-mapping/role-edit-mapped-user.tsx | 41 ++++++++++++++----- .../test/role-edit-mapped-user.test.tsx | 6 --- .../panels/role-view/role-view.tsx | 4 +- .../__snapshots__/role-view.test.tsx.snap | 34 ++------------- .../panels/role-view/test/role-view.test.tsx | 3 -- 11 files changed, 47 insertions(+), 132 deletions(-) diff --git a/public/apps/configuration/app-router.tsx b/public/apps/configuration/app-router.tsx index f83bb331..36c3c381 100644 --- a/public/apps/configuration/app-router.tsx +++ b/public/apps/configuration/app-router.tsx @@ -14,7 +14,7 @@ */ import { EuiPage, EuiPageBody, EuiPageSideBar } from '@elastic/eui'; -import { flow, partial } from 'lodash'; +import { flow } from 'lodash'; import React, { createContext, useState } from 'react'; import { HashRouter as Router, Route, Switch, Redirect } from 'react-router-dom'; import { DataSourceOption } from 'src/plugins/data_source_management/public/components/data_source_menu/types'; @@ -149,40 +149,19 @@ export function AppRouter(props: AppDependencies) { ( - + )} /> ( - + )} /> ( - + )} /> ( - + )} /> { ); }); -export const HeaderTitle = React.memo((props: TitleProps) => { - const { HeaderControl } = props.navigation.ui; - const titleData: TopNavControlData[] = [ - { - renderComponent: ( - -

- {props.pageHeader} - {props.shouldDisplayCount ? ` (${props.count})` : null} -

-
- ), - }, - ]; - - return ( - - ); -}); - -export const HeaderDescription = React.memo((props: DescriptionProps) => { - const { HeaderControl } = props.navigation.ui; // need to get this from SecurityPluginStartDependencies - - return ( - - ); -}); - export const PageHeader = (props: HeaderProps & DescriptionProps & ControlProps) => { const { HeaderControl } = props.navigation.ui; // need to get this from SecurityPluginStartDependencies const useNewUx = props.coreStart.chrome.navGroup.getNavGroupEnabled(); diff --git a/public/apps/configuration/panels/internal-user-edit/internal-user-edit.tsx b/public/apps/configuration/panels/internal-user-edit/internal-user-edit.tsx index 66edb73e..6546978c 100644 --- a/public/apps/configuration/panels/internal-user-edit/internal-user-edit.tsx +++ b/public/apps/configuration/panels/internal-user-edit/internal-user-edit.tsx @@ -174,7 +174,6 @@ export function InternalUserEdit(props: InternalUserEditDeps) { descriptionControls={descriptionData} fallBackComponent={ <> - {props.buildBreadcrumbs(TITLE_TEXT_DICT[props.action])} @@ -194,6 +193,8 @@ export function InternalUserEdit(props: InternalUserEditDeps) { } + resourceType={ResourceType.users} + subAction={TITLE_TEXT_DICT[props.action]} /> diff --git a/public/apps/configuration/panels/internal-user-edit/test/internal-user-edit.test.tsx b/public/apps/configuration/panels/internal-user-edit/test/internal-user-edit.test.tsx index 241f4e14..dc734e45 100644 --- a/public/apps/configuration/panels/internal-user-edit/test/internal-user-edit.test.tsx +++ b/public/apps/configuration/panels/internal-user-edit/test/internal-user-edit.test.tsx @@ -54,7 +54,6 @@ describe('Internal user edit', () => { { /> ); - expect(buildBreadcrumbs).toBeCalledTimes(1); expect(component.find(AttributePanel).length).toBe(1); }); diff --git a/public/apps/configuration/panels/role-edit/role-edit.tsx b/public/apps/configuration/panels/role-edit/role-edit.tsx index 5ed47cab..6006600f 100644 --- a/public/apps/configuration/panels/role-edit/role-edit.tsx +++ b/public/apps/configuration/panels/role-edit/role-edit.tsx @@ -261,7 +261,6 @@ export function RoleEdit(props: RoleEditDeps) { fallBackComponent={ <> {' '} - {props.buildBreadcrumbs(TITLE_TEXT_DICT[props.action])} @@ -279,6 +278,8 @@ export function RoleEdit(props: RoleEditDeps) { navigation={props.depsStart.navigation} coreStart={props.coreStart} descriptionControls={descriptionData} + resourceType={ResourceType.roles} + subAction={TITLE_TEXT_DICT[props.action]} /> diff --git a/public/apps/configuration/panels/role-edit/test/role-edit.test.tsx b/public/apps/configuration/panels/role-edit/test/role-edit.test.tsx index 80730c0e..fca458cb 100644 --- a/public/apps/configuration/panels/role-edit/test/role-edit.test.tsx +++ b/public/apps/configuration/panels/role-edit/test/role-edit.test.tsx @@ -58,13 +58,11 @@ describe('Role edit', () => { it('basic rendering', () => { const action = 'create'; - const buildBreadcrumbs = jest.fn(); const component = shallow( { /> ); - expect(buildBreadcrumbs).toBeCalledTimes(1); expect(component.find(ClusterPermissionPanel).length).toBe(1); expect(component.find(IndexPermissionPanel).length).toBe(1); expect(component.find(TenantPanel).length).toBe(1); @@ -84,13 +81,11 @@ describe('Role edit', () => { useEffect.mockImplementationOnce((f) => f()); useState.mockImplementation((initialValue) => [initialValue, jest.fn()]); const action = 'edit'; - const buildBreadcrumbs = jest.fn(); const component = shallow( + Map users to this role to inherit role permissions. Two types of users are supported: user, + and backend role. + + ), + }, +]; + export function RoleEditMappedUser(props: RoleEditMappedUserProps) { const [internalUsers, setInternalUsers] = React.useState([]); const [externalIdentities, setExternalIdentities] = React.useState( @@ -150,16 +162,25 @@ export function RoleEditMappedUser(props: RoleEditMappedUserProps) { setDataSource={setDataSource} selectedDataSource={dataSource} /> - {props.buildBreadcrumbs(props.roleName, TITLE_TEXT_DICT[SubAction.mapuser])} - - - -

Map user

-
- Map users to this role to inherit role permissions. Two types of users are supported: - user, and backend role. -
-
+ + + +

Map user

+
+ Map users to this role to inherit role permissions. Two types of users are supported: + user, and backend role. +
+
+ } + resourceType={ResourceType.roles} + subAction={TITLE_TEXT_DICT[SubAction.mapuser]} + pageTitle={props.roleName} + descriptionControls={descriptionData} + /> { const mockCoreStart = { http: 1, }; - const buildBreadcrumbs = jest.fn(); const useEffect = jest.spyOn(React, 'useEffect'); const useState = jest.spyOn(React, 'useState'); @@ -54,7 +53,6 @@ describe('Role mapping edit', () => { const component = shallow( { /> ); - expect(buildBreadcrumbs).toBeCalledTimes(1); expect(component.find(InternalUsersPanel).length).toBe(1); expect(component.find(ExternalIdentitiesPanel).length).toBe(1); }); @@ -79,7 +76,6 @@ describe('Role mapping edit', () => { shallow( { const component = shallow( { const component = shallow( - {props.buildBreadcrumbs(props.roleName)} - @@ -475,6 +473,8 @@ export function RoleView(props: RoleViewProps) { } + resourceType={ResourceType.roles} + subAction={props.roleName} /> } + resourceType="roles" + subAction="role" /> } + resourceType="roles" + subAction="role" /> { { /> ); - expect(buildBreadcrumbs).toBeCalledTimes(1); expect(component.find(EuiTabbedContent).length).toBe(1); const tabs = component.find(EuiTabbedContent).dive(); @@ -128,7 +126,6 @@ describe('Role view', () => { Date: Fri, 9 Aug 2024 19:26:16 -0400 Subject: [PATCH 20/24] Fixes target _blank redirects Signed-off-by: Darshit Chanpura --- public/apps/configuration/panels/auth-view/auth-view.tsx | 2 +- public/apps/configuration/panels/get-started.tsx | 2 +- public/apps/configuration/panels/role-list.tsx | 1 - public/apps/configuration/panels/role-view/role-view.tsx | 4 ---- public/apps/configuration/panels/tenant-list/manage_tab.tsx | 1 - public/apps/configuration/panels/user-list.tsx | 1 - 6 files changed, 2 insertions(+), 9 deletions(-) diff --git a/public/apps/configuration/panels/auth-view/auth-view.tsx b/public/apps/configuration/panels/auth-view/auth-view.tsx index a244e974..feb18b5d 100644 --- a/public/apps/configuration/panels/auth-view/auth-view.tsx +++ b/public/apps/configuration/panels/auth-view/auth-view.tsx @@ -70,7 +70,7 @@ export function AuthView(props: AppDependencies) { iconType: 'popout', iconSide: 'right', type: 'button', - // target: "_blank" + target: '_blank', }, ]; diff --git a/public/apps/configuration/panels/get-started.tsx b/public/apps/configuration/panels/get-started.tsx index e0a965ee..1e45d09b 100644 --- a/public/apps/configuration/panels/get-started.tsx +++ b/public/apps/configuration/panels/get-started.tsx @@ -182,7 +182,7 @@ export function GetStarted(props: AppDependencies) { iconType: 'popout', iconSide: 'right', type: 'button', - // target: "_blank" + target: '_blank', }, ]; return ( diff --git a/public/apps/configuration/panels/role-list.tsx b/public/apps/configuration/panels/role-list.tsx index 94635a2a..74623083 100644 --- a/public/apps/configuration/panels/role-list.tsx +++ b/public/apps/configuration/panels/role-list.tsx @@ -273,7 +273,6 @@ export function RoleList(props: AppDependencies) { iconSide: 'left', type: 'button', testId: 'create-role', - // target: "_blank" }, ]; const descriptionData = [ diff --git a/public/apps/configuration/panels/role-view/role-view.tsx b/public/apps/configuration/panels/role-view/role-view.tsx index 6ac2b750..aebfc67d 100644 --- a/public/apps/configuration/panels/role-view/role-view.tsx +++ b/public/apps/configuration/panels/role-view/role-view.tsx @@ -403,7 +403,6 @@ export function RoleView(props: RoleViewProps) { href: buildHashUrl(ResourceType.roles, Action.edit, props.roleName), type: 'button', fill: true, - // target: "_blank" }, ]; const roleButtons = [ @@ -427,14 +426,12 @@ export function RoleView(props: RoleViewProps) { type: 'button', // this should be icon, but icons current do not support a border currently testId: 'delete', ariaLabel: 'delete', - // target: "_blank" }, { label: 'Duplicate', isLoading: false, href: duplicateRoleLink, type: 'button', - // target: "_blank" }, { label: 'Edit role', @@ -442,7 +439,6 @@ export function RoleView(props: RoleViewProps) { href: buildHashUrl(ResourceType.roles, Action.edit, props.roleName), fill: true, type: 'button', - // target: "_blank" }, ]; diff --git a/public/apps/configuration/panels/tenant-list/manage_tab.tsx b/public/apps/configuration/panels/tenant-list/manage_tab.tsx index 2e3f0e8e..f0f91856 100644 --- a/public/apps/configuration/panels/tenant-list/manage_tab.tsx +++ b/public/apps/configuration/panels/tenant-list/manage_tab.tsx @@ -514,7 +514,6 @@ export function ManageTab(props: AppDependencies) { run: () => showEditModal('', Action.create, ''), type: 'button', fill: true, - // target: "_blank" }, ]; /* eslint-disable */ diff --git a/public/apps/configuration/panels/user-list.tsx b/public/apps/configuration/panels/user-list.tsx index ac89e280..b51a0d61 100644 --- a/public/apps/configuration/panels/user-list.tsx +++ b/public/apps/configuration/panels/user-list.tsx @@ -216,7 +216,6 @@ export function UserList(props: AppDependencies) { iconSide: 'left', type: 'button', testId: 'create-user', - // target: "_blank" }, ]; const descriptionData = [ From d4ed1dfec9a31f1284991a29e584710c73e93429 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 9 Aug 2024 19:47:45 -0400 Subject: [PATCH 21/24] Fixes unit tests Signed-off-by: Darshit Chanpura --- .../panels/test/__snapshots__/get-started.test.tsx.snap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/apps/configuration/panels/test/__snapshots__/get-started.test.tsx.snap b/public/apps/configuration/panels/test/__snapshots__/get-started.test.tsx.snap index 574297b0..b43cc332 100644 --- a/public/apps/configuration/panels/test/__snapshots__/get-started.test.tsx.snap +++ b/public/apps/configuration/panels/test/__snapshots__/get-started.test.tsx.snap @@ -40,6 +40,7 @@ exports[`Get started (landing page) renders when backend configuration is disabl "iconType": "popout", "isLoading": false, "label": "Open in new window", + "target": "_blank", "type": "button", }, ] @@ -341,6 +342,7 @@ exports[`Get started (landing page) renders when backend configuration is enable "iconType": "popout", "isLoading": false, "label": "Open in new window", + "target": "_blank", "type": "button", }, ] From 6ccede6469095699800fa6ae7ab03874bed88815 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Mon, 12 Aug 2024 14:16:54 -0400 Subject: [PATCH 22/24] Add tests for new component and breadcrumb function Signed-off-by: Derek Ho --- .../header/header-components.tsx | 4 +- .../configuration/header/header-props.tsx | 2 +- .../header/test/header-components.test.tsx | 92 +++++++++++++++++++ .../utils/test/resource-utils.test.tsx | 61 +++++++++++- 4 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 public/apps/configuration/header/test/header-components.test.tsx diff --git a/public/apps/configuration/header/header-components.tsx b/public/apps/configuration/header/header-components.tsx index b2edb396..2be3edfc 100644 --- a/public/apps/configuration/header/header-components.tsx +++ b/public/apps/configuration/header/header-components.tsx @@ -34,9 +34,9 @@ export const HeaderButtonOrLink = React.memo((props: ControlProps) => { export const PageHeader = (props: HeaderProps & DescriptionProps & ControlProps) => { const { HeaderControl } = props.navigation.ui; // need to get this from SecurityPluginStartDependencies - const useNewUx = props.coreStart.chrome.navGroup.getNavGroupEnabled(); + const useNewUx = props.coreStart.uiSettings.get('home:useNewHomePage'); flow(getBreadcrumbs, props.coreStart.chrome.setBreadcrumbs)( - !props.coreStart.uiSettings.get('home:useNewHomePage'), + !useNewUx, props.resourceType, props.pageTitle, props.subAction, diff --git a/public/apps/configuration/header/header-props.tsx b/public/apps/configuration/header/header-props.tsx index 9e532e2e..516c1833 100644 --- a/public/apps/configuration/header/header-props.tsx +++ b/public/apps/configuration/header/header-props.tsx @@ -14,9 +14,9 @@ */ import { CoreStart } from 'opensearch-dashboards/public'; -import { ResourceType } from 'plugins/security-dashboards-plugin/common'; import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; import { TopNavControlData } from 'src/plugins/navigation/public/top_nav_menu/top_nav_control_data'; +import { ResourceType } from '../../../../common'; export interface HeaderProps { navigation: NavigationPublicPluginStart; diff --git a/public/apps/configuration/header/test/header-components.test.tsx b/public/apps/configuration/header/test/header-components.test.tsx new file mode 100644 index 00000000..703faec5 --- /dev/null +++ b/public/apps/configuration/header/test/header-components.test.tsx @@ -0,0 +1,92 @@ +/* + * Copyright OpenSearch Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React from 'react'; +import { mount } from 'enzyme'; +import { PageHeader } from '../header-components'; // Adjust the import path as needed +import { getBreadcrumbs } from '../../utils/resource-utils'; + +jest.mock('../../utils/resource-utils', () => ({ + getBreadcrumbs: jest.fn(), +})); + +describe('PageHeader', () => { + let props; + + beforeEach(() => { + jest.resetAllMocks(); + props = { + navigation: { + ui: { + HeaderControl: jest.fn(({ setMountPoint, controls }) => null), + }, + }, + coreStart: { + chrome: { + navGroup: { + getNavGroupEnabled: jest.fn(), + }, + setBreadcrumbs: jest.fn(), + }, + application: { + setAppRightControls: jest.fn(), + setAppDescriptionControls: jest.fn(), + }, + uiSettings: { + get: jest.fn(), + }, + }, + resourceType: 'test-resource', + pageTitle: 'Test Page Title', + subAction: 'test-sub-action', + count: 5, + descriptionControls: ['control-1'], + controlControls: ['control-2'], + fallBackComponent:
Fallback Component
, + }; + }); + + it('renders with the feature flag off', () => { + props.coreStart.uiSettings.get.mockReturnValueOnce(false); + const wrapper = mount(); + + expect(getBreadcrumbs).toHaveBeenCalledWith( + true, + 'test-resource', + 'Test Page Title', + 'test-sub-action', + 5 + ); + expect(wrapper.contains(props.fallBackComponent)).toBe(true); + + expect(props.navigation.ui.HeaderControl).not.toBeCalled(); + }); + + it('renders with the feature flag on', () => { + props.coreStart.uiSettings.get.mockReturnValueOnce(true); + const wrapper = mount(); + + expect(getBreadcrumbs).toHaveBeenCalledWith( + false, + 'test-resource', + 'Test Page Title', + 'test-sub-action', + 5 + ); + expect(wrapper.contains(props.fallBackComponent)).toBe(false); + expect(props.navigation.ui.HeaderControl.mock.calls[0][0].controls).toEqual(['control-1']); + expect(props.navigation.ui.HeaderControl.mock.calls[1][0].controls).toEqual(['control-2']); + }); +}); diff --git a/public/apps/configuration/utils/test/resource-utils.test.tsx b/public/apps/configuration/utils/test/resource-utils.test.tsx index ab0dac6b..b69b899d 100644 --- a/public/apps/configuration/utils/test/resource-utils.test.tsx +++ b/public/apps/configuration/utils/test/resource-utils.test.tsx @@ -13,24 +13,75 @@ * permissions and limitations under the License. */ -import { generateResourceName } from '../resource-utils'; +import { ResourceType } from '../../../../../common'; +import { generateResourceName, getBreadcrumbs } from '../resource-utils'; -describe('generateResourceName', () => { - it('edit should return same name', () => { +describe('ResourceUtilsTests', () => { + it('generateResourceName edit should return same name', () => { const result = generateResourceName('edit', 'user1'); expect(result).toBe('user1'); }); - it('duplicate should append _copy suffix', () => { + it('generateResourceName duplicate should append _copy suffix', () => { const result = generateResourceName('duplicate', 'role1'); expect(result).toBe('role1_copy'); }); - it('other action should return empty string', () => { + it('generateResourceName other action should return empty string', () => { const result = generateResourceName('create', 'tenant1'); expect(result).toBe(''); }); + + it('getBreadcrumbs with security base should include security breadcrumb', () => { + const breadcrumbs = getBreadcrumbs(true); + expect(breadcrumbs).toEqual([ + { + href: '#/', + text: 'Security', + }, + ]); + }); + + it('getBreadcrumbs without security base should not include security breadcrumb', () => { + const breadcrumbs = getBreadcrumbs(false); + expect(breadcrumbs).toEqual([]); + }); + + it('getBreadcrumbs without security base should use display name', () => { + const breadcrumbs = getBreadcrumbs(false, ResourceType.auth); + expect(breadcrumbs).toEqual([{ href: '#/auth', text: 'Authentication and authorization' }]); + }); + + it('getBreadcrumbs with security base should use regular name and have security base', () => { + const breadcrumbs = getBreadcrumbs(true, ResourceType.auth); + expect(breadcrumbs).toEqual([ + { href: '#/', text: 'Security' }, + { href: '#/auth', text: 'Authentication' }, + ]); + }); + + it('getBreadcrumbs with title and subactions should include those breadcrumbs', () => { + const breadcrumbs = getBreadcrumbs(false, ResourceType.roles, 'Derek', 'Map user'); + expect(breadcrumbs).toEqual([ + { href: '#/roles', text: 'Roles' }, + { text: 'Derek' }, + { text: 'Map user' }, + ]); + }); + + it('getBreadcrumbs should show breadcrumb with count when security base is not included', () => { + const breadcrumbs = getBreadcrumbs(false, ResourceType.roles, undefined, undefined, 30); + expect(breadcrumbs).toEqual([{ href: '#/roles', text: 'Roles (30)' }]); + }); + + it('getBreadcrumbs should not show breadcrumb with count when security base is included', () => { + const breadcrumbs = getBreadcrumbs(true, ResourceType.roles, undefined, undefined, 30); + expect(breadcrumbs).toEqual([ + { href: '#/', text: 'Security' }, + { href: '#/roles', text: 'Roles' }, + ]); + }); }); From 442f525acd2ae2720e5fbf3426f20f2c0eadd1c9 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Mon, 19 Aug 2024 14:53:43 -0400 Subject: [PATCH 23/24] Address PR feedback Signed-off-by: Derek Ho --- public/apps/configuration/app-router.tsx | 14 +- .../header/header-components.tsx | 13 +- .../configuration/header/header-props.tsx | 2 +- .../header/test/header-components.test.tsx | 1 + .../panels/auth-view/auth-view.tsx | 2 +- .../apps/configuration/panels/get-started.tsx | 2 +- .../permission-list/permission-list.tsx | 2 +- .../apps/configuration/panels/role-list.tsx | 2 +- .../panels/role-view/role-view.tsx | 2 +- .../panels/tenant-list/manage_tab.tsx | 8 +- .../apps/configuration/panels/user-list.tsx | 2 +- .../__snapshots__/app-router.test.tsx.snap | 140 +++++++++--------- public/apps/configuration/types.ts | 2 +- .../configuration/utils/resource-utils.tsx | 4 +- 14 files changed, 98 insertions(+), 98 deletions(-) diff --git a/public/apps/configuration/app-router.tsx b/public/apps/configuration/app-router.tsx index 36c3c381..eb987e7a 100644 --- a/public/apps/configuration/app-router.tsx +++ b/public/apps/configuration/app-router.tsx @@ -48,27 +48,27 @@ const LANDING_PAGE_URL = '/getstarted'; export const ROUTE_MAP: { [key: string]: RouteItem } = { getStarted: { name: 'Get Started', - displayNameWithoutSecurityBase: 'Get started with access control', + breadCrumbDisplayNameWithoutSecurityBase: 'Get started with access control', href: LANDING_PAGE_URL, }, [ResourceType.roles]: { name: 'Roles', - displayNameWithoutSecurityBase: 'Roles', + breadCrumbDisplayNameWithoutSecurityBase: 'Roles', href: buildUrl(ResourceType.roles), }, [ResourceType.users]: { name: 'Internal users', - displayNameWithoutSecurityBase: 'Internal users', + breadCrumbDisplayNameWithoutSecurityBase: 'Internal users', href: buildUrl(ResourceType.users), }, [ResourceType.permissions]: { name: 'Permissions', - displayNameWithoutSecurityBase: 'Permissions', + breadCrumbDisplayNameWithoutSecurityBase: 'Permissions', href: buildUrl(ResourceType.permissions), }, [ResourceType.tenants]: { name: 'Tenants', - displayNameWithoutSecurityBase: 'Dashboard multi-tenancy', + breadCrumbDisplayNameWithoutSecurityBase: 'Dashboard multi-tenancy', href: buildUrl(ResourceType.tenants), }, [ResourceType.tenantsConfigureTab]: { @@ -77,12 +77,12 @@ export const ROUTE_MAP: { [key: string]: RouteItem } = { }, [ResourceType.auth]: { name: 'Authentication', - displayNameWithoutSecurityBase: 'Authentication and authorization', + breadCrumbDisplayNameWithoutSecurityBase: 'Authentication and authorization', href: buildUrl(ResourceType.auth), }, [ResourceType.auditLogging]: { name: 'Audit logs', - displayNameWithoutSecurityBase: 'Audit logs', + breadCrumbDisplayNameWithoutSecurityBase: 'Audit logs', href: buildUrl(ResourceType.auditLogging), }, }; diff --git a/public/apps/configuration/header/header-components.tsx b/public/apps/configuration/header/header-components.tsx index 2be3edfc..dde2bbbc 100644 --- a/public/apps/configuration/header/header-components.tsx +++ b/public/apps/configuration/header/header-components.tsx @@ -18,16 +18,13 @@ import { flow } from 'lodash'; import { ControlProps, DescriptionProps, HeaderProps } from './header-props'; import { getBreadcrumbs } from '../utils/resource-utils'; -// controlType should be one of: https://github.com/AMoo-Miki/OpenSearch-Dashboards/blob/header-collective/src/plugins/navigation/public/top_nav_menu/top_nav_control_data.tsx#L91 - -export const HeaderButtonOrLink = React.memo((props: ControlProps) => { +export const HeaderButtonOrLink = React.memo((props: HeaderProps & ControlProps) => { const { HeaderControl } = props.navigation.ui; return ( ); }); @@ -51,10 +48,10 @@ export const PageHeader = (props: HeaderProps & DescriptionProps & ControlProps) controls={props.descriptionControls} /> ) : null} - {props.controlControls ? ( + {props.appRightControls ? ( ) : null} diff --git a/public/apps/configuration/header/header-props.tsx b/public/apps/configuration/header/header-props.tsx index 516c1833..f9411e20 100644 --- a/public/apps/configuration/header/header-props.tsx +++ b/public/apps/configuration/header/header-props.tsx @@ -29,7 +29,7 @@ export interface HeaderProps { } export interface ControlProps { - controlControls?: TopNavControlData[]; + appRightControls?: TopNavControlData[]; } export interface DescriptionProps { diff --git a/public/apps/configuration/header/test/header-components.test.tsx b/public/apps/configuration/header/test/header-components.test.tsx index 703faec5..24b41f15 100644 --- a/public/apps/configuration/header/test/header-components.test.tsx +++ b/public/apps/configuration/header/test/header-components.test.tsx @@ -86,6 +86,7 @@ describe('PageHeader', () => { 5 ); expect(wrapper.contains(props.fallBackComponent)).toBe(false); + // Verifies that the HeaderControl is called with both controls passed as props expect(props.navigation.ui.HeaderControl.mock.calls[0][0].controls).toEqual(['control-1']); expect(props.navigation.ui.HeaderControl.mock.calls[1][0].controls).toEqual(['control-2']); }); diff --git a/public/apps/configuration/panels/auth-view/auth-view.tsx b/public/apps/configuration/panels/auth-view/auth-view.tsx index feb18b5d..6c19a981 100644 --- a/public/apps/configuration/panels/auth-view/auth-view.tsx +++ b/public/apps/configuration/panels/auth-view/auth-view.tsx @@ -115,7 +115,7 @@ export function AuthView(props: AppDependencies) { diff --git a/public/apps/configuration/panels/get-started.tsx b/public/apps/configuration/panels/get-started.tsx index 1e45d09b..d1a1fa72 100644 --- a/public/apps/configuration/panels/get-started.tsx +++ b/public/apps/configuration/panels/get-started.tsx @@ -197,7 +197,7 @@ export function GetStarted(props: AppDependencies) { diff --git a/public/apps/configuration/panels/permission-list/permission-list.tsx b/public/apps/configuration/panels/permission-list/permission-list.tsx index bc17f93e..7981abb2 100644 --- a/public/apps/configuration/panels/permission-list/permission-list.tsx +++ b/public/apps/configuration/panels/permission-list/permission-list.tsx @@ -402,7 +402,7 @@ export function PermissionList(props: AppDependencies) { navigation={props.depsStart.navigation} coreStart={props.coreStart} descriptionControls={descriptionData} - controlControls={buttonData} + appRightControls={buttonData} fallBackComponent={ diff --git a/public/apps/configuration/panels/role-list.tsx b/public/apps/configuration/panels/role-list.tsx index 74623083..af640bf1 100644 --- a/public/apps/configuration/panels/role-list.tsx +++ b/public/apps/configuration/panels/role-list.tsx @@ -305,7 +305,7 @@ export function RoleList(props: AppDependencies) { navigation={props.depsStart.navigation} coreStart={props.coreStart} descriptionControls={descriptionData} - controlControls={buttonData} + appRightControls={buttonData} fallBackComponent={ diff --git a/public/apps/configuration/panels/role-view/role-view.tsx b/public/apps/configuration/panels/role-view/role-view.tsx index aebfc67d..1c04351c 100644 --- a/public/apps/configuration/panels/role-view/role-view.tsx +++ b/public/apps/configuration/panels/role-view/role-view.tsx @@ -455,7 +455,7 @@ export function RoleView(props: RoleViewProps) { diff --git a/public/apps/configuration/panels/tenant-list/manage_tab.tsx b/public/apps/configuration/panels/tenant-list/manage_tab.tsx index f0f91856..adce0ec7 100644 --- a/public/apps/configuration/panels/tenant-list/manage_tab.tsx +++ b/public/apps/configuration/panels/tenant-list/manage_tab.tsx @@ -543,9 +543,11 @@ export function ManageTab(props: AppDependencies) { {actionsMenu} { useUpdatedUX ? : diff --git a/public/apps/configuration/panels/user-list.tsx b/public/apps/configuration/panels/user-list.tsx index b51a0d61..545f9071 100644 --- a/public/apps/configuration/panels/user-list.tsx +++ b/public/apps/configuration/panels/user-list.tsx @@ -247,7 +247,7 @@ export function UserList(props: AppDependencies) { navigation={props.depsStart.navigation} coreStart={props.coreStart} descriptionControls={descriptionData} - controlControls={buttonData} + appRightControls={buttonData} fallBackComponent={ diff --git a/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap b/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap index 7f96d34b..85c8181a 100644 --- a/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap +++ b/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap @@ -24,37 +24,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { - "displayNameWithoutSecurityBase": "Get started with access control", + "breadCrumbDisplayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { - "displayNameWithoutSecurityBase": "Authentication and authorization", + "breadCrumbDisplayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { - "displayNameWithoutSecurityBase": "Roles", + "breadCrumbDisplayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { - "displayNameWithoutSecurityBase": "Internal users", + "breadCrumbDisplayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { - "displayNameWithoutSecurityBase": "Permissions", + "breadCrumbDisplayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { - "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", + "breadCrumbDisplayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { - "displayNameWithoutSecurityBase": "Audit logs", + "breadCrumbDisplayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -73,37 +73,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { - "displayNameWithoutSecurityBase": "Get started with access control", + "breadCrumbDisplayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { - "displayNameWithoutSecurityBase": "Authentication and authorization", + "breadCrumbDisplayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { - "displayNameWithoutSecurityBase": "Roles", + "breadCrumbDisplayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { - "displayNameWithoutSecurityBase": "Internal users", + "breadCrumbDisplayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { - "displayNameWithoutSecurityBase": "Permissions", + "breadCrumbDisplayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { - "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", + "breadCrumbDisplayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { - "displayNameWithoutSecurityBase": "Audit logs", + "breadCrumbDisplayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -122,37 +122,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { - "displayNameWithoutSecurityBase": "Get started with access control", + "breadCrumbDisplayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { - "displayNameWithoutSecurityBase": "Authentication and authorization", + "breadCrumbDisplayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { - "displayNameWithoutSecurityBase": "Roles", + "breadCrumbDisplayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { - "displayNameWithoutSecurityBase": "Internal users", + "breadCrumbDisplayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { - "displayNameWithoutSecurityBase": "Permissions", + "breadCrumbDisplayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { - "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", + "breadCrumbDisplayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { - "displayNameWithoutSecurityBase": "Audit logs", + "breadCrumbDisplayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -171,37 +171,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { - "displayNameWithoutSecurityBase": "Get started with access control", + "breadCrumbDisplayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { - "displayNameWithoutSecurityBase": "Authentication and authorization", + "breadCrumbDisplayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { - "displayNameWithoutSecurityBase": "Roles", + "breadCrumbDisplayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { - "displayNameWithoutSecurityBase": "Internal users", + "breadCrumbDisplayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { - "displayNameWithoutSecurityBase": "Permissions", + "breadCrumbDisplayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { - "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", + "breadCrumbDisplayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { - "displayNameWithoutSecurityBase": "Audit logs", + "breadCrumbDisplayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -220,37 +220,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { - "displayNameWithoutSecurityBase": "Get started with access control", + "breadCrumbDisplayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { - "displayNameWithoutSecurityBase": "Authentication and authorization", + "breadCrumbDisplayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { - "displayNameWithoutSecurityBase": "Roles", + "breadCrumbDisplayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { - "displayNameWithoutSecurityBase": "Internal users", + "breadCrumbDisplayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { - "displayNameWithoutSecurityBase": "Permissions", + "breadCrumbDisplayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { - "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", + "breadCrumbDisplayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { - "displayNameWithoutSecurityBase": "Audit logs", + "breadCrumbDisplayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -269,37 +269,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { - "displayNameWithoutSecurityBase": "Get started with access control", + "breadCrumbDisplayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { - "displayNameWithoutSecurityBase": "Authentication and authorization", + "breadCrumbDisplayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { - "displayNameWithoutSecurityBase": "Roles", + "breadCrumbDisplayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { - "displayNameWithoutSecurityBase": "Internal users", + "breadCrumbDisplayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { - "displayNameWithoutSecurityBase": "Permissions", + "breadCrumbDisplayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { - "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", + "breadCrumbDisplayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { - "displayNameWithoutSecurityBase": "Audit logs", + "breadCrumbDisplayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -318,37 +318,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { - "displayNameWithoutSecurityBase": "Get started with access control", + "breadCrumbDisplayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { - "displayNameWithoutSecurityBase": "Authentication and authorization", + "breadCrumbDisplayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { - "displayNameWithoutSecurityBase": "Roles", + "breadCrumbDisplayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { - "displayNameWithoutSecurityBase": "Internal users", + "breadCrumbDisplayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { - "displayNameWithoutSecurityBase": "Permissions", + "breadCrumbDisplayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { - "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", + "breadCrumbDisplayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { - "displayNameWithoutSecurityBase": "Audit logs", + "breadCrumbDisplayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -367,37 +367,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { - "displayNameWithoutSecurityBase": "Get started with access control", + "breadCrumbDisplayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { - "displayNameWithoutSecurityBase": "Authentication and authorization", + "breadCrumbDisplayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { - "displayNameWithoutSecurityBase": "Roles", + "breadCrumbDisplayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { - "displayNameWithoutSecurityBase": "Internal users", + "breadCrumbDisplayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { - "displayNameWithoutSecurityBase": "Permissions", + "breadCrumbDisplayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { - "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", + "breadCrumbDisplayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { - "displayNameWithoutSecurityBase": "Audit logs", + "breadCrumbDisplayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -416,37 +416,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { - "displayNameWithoutSecurityBase": "Get started with access control", + "breadCrumbDisplayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { - "displayNameWithoutSecurityBase": "Authentication and authorization", + "breadCrumbDisplayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { - "displayNameWithoutSecurityBase": "Roles", + "breadCrumbDisplayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { - "displayNameWithoutSecurityBase": "Internal users", + "breadCrumbDisplayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { - "displayNameWithoutSecurityBase": "Permissions", + "breadCrumbDisplayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { - "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", + "breadCrumbDisplayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { - "displayNameWithoutSecurityBase": "Audit logs", + "breadCrumbDisplayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, @@ -465,37 +465,37 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab items={ Array [ Object { - "displayNameWithoutSecurityBase": "Get started with access control", + "breadCrumbDisplayNameWithoutSecurityBase": "Get started with access control", "href": "/getstarted", "name": "Get Started", }, Object { - "displayNameWithoutSecurityBase": "Authentication and authorization", + "breadCrumbDisplayNameWithoutSecurityBase": "Authentication and authorization", "href": "/auth", "name": "Authentication", }, Object { - "displayNameWithoutSecurityBase": "Roles", + "breadCrumbDisplayNameWithoutSecurityBase": "Roles", "href": "/roles", "name": "Roles", }, Object { - "displayNameWithoutSecurityBase": "Internal users", + "breadCrumbDisplayNameWithoutSecurityBase": "Internal users", "href": "/users", "name": "Internal users", }, Object { - "displayNameWithoutSecurityBase": "Permissions", + "breadCrumbDisplayNameWithoutSecurityBase": "Permissions", "href": "/permissions", "name": "Permissions", }, Object { - "displayNameWithoutSecurityBase": "Dashboard multi-tenancy", + "breadCrumbDisplayNameWithoutSecurityBase": "Dashboard multi-tenancy", "href": "/tenants", "name": "Tenants", }, Object { - "displayNameWithoutSecurityBase": "Audit logs", + "breadCrumbDisplayNameWithoutSecurityBase": "Audit logs", "href": "/auditLogging", "name": "Audit logs", }, diff --git a/public/apps/configuration/types.ts b/public/apps/configuration/types.ts index adbede12..6736d8e1 100644 --- a/public/apps/configuration/types.ts +++ b/public/apps/configuration/types.ts @@ -46,7 +46,7 @@ export enum TenantPermissionType { export interface RouteItem { name: string; href: string; - displayNameWithoutSecurityBase: string; + breadCrumbDisplayNameWithoutSecurityBase: string; } export interface DataObject { diff --git a/public/apps/configuration/utils/resource-utils.tsx b/public/apps/configuration/utils/resource-utils.tsx index 708b64f2..e5d1156a 100644 --- a/public/apps/configuration/utils/resource-utils.tsx +++ b/public/apps/configuration/utils/resource-utils.tsx @@ -58,8 +58,8 @@ export function getBreadcrumbs( } else { breadcrumbs.push({ text: count - ? `${ROUTE_MAP[resourceType].displayNameWithoutSecurityBase} (${count})` - : ROUTE_MAP[resourceType].displayNameWithoutSecurityBase, + ? `${ROUTE_MAP[resourceType].breadCrumbDisplayNameWithoutSecurityBase} (${count})` + : ROUTE_MAP[resourceType].breadCrumbDisplayNameWithoutSecurityBase, href: buildHashUrl(resourceType), }); } From 040bc3e43371cc415774e650dd3cfce0a0b4fbb2 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Mon, 19 Aug 2024 15:07:40 -0400 Subject: [PATCH 24/24] Update tests and snapshots Signed-off-by: Derek Ho --- .../apps/configuration/header/test/header-components.test.tsx | 2 +- .../test/__snapshots__/permission-list.test.tsx.snap | 2 +- .../role-view/test/__snapshots__/role-view.test.tsx.snap | 4 ++-- .../panels/test/__snapshots__/get-started.test.tsx.snap | 4 ++-- .../panels/test/__snapshots__/role-list.test.tsx.snap | 2 +- .../panels/test/__snapshots__/user-list.test.tsx.snap | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/public/apps/configuration/header/test/header-components.test.tsx b/public/apps/configuration/header/test/header-components.test.tsx index 24b41f15..e9a677f5 100644 --- a/public/apps/configuration/header/test/header-components.test.tsx +++ b/public/apps/configuration/header/test/header-components.test.tsx @@ -53,7 +53,7 @@ describe('PageHeader', () => { subAction: 'test-sub-action', count: 5, descriptionControls: ['control-1'], - controlControls: ['control-2'], + appRightControls: ['control-2'], fallBackComponent:
Fallback Component
, }; }); diff --git a/public/apps/configuration/panels/permission-list/test/__snapshots__/permission-list.test.tsx.snap b/public/apps/configuration/panels/permission-list/test/__snapshots__/permission-list.test.tsx.snap index 04c70f89..2dc58bac 100644 --- a/public/apps/configuration/panels/permission-list/test/__snapshots__/permission-list.test.tsx.snap +++ b/public/apps/configuration/panels/permission-list/test/__snapshots__/permission-list.test.tsx.snap @@ -63,7 +63,7 @@ exports[`Permission list page AccessError component should load access error co setDataSource={[MockFunction]} />