Change "Close project" to "Log out" in nav menu in serverless mode (#211463)

## Summary

This PR resolves [Unify user profile menu "Log
out"option](https://github.com/elastic/kibana/issues/209278) issue.
This commit is contained in:
Paulina Shakirova 2025-03-21 13:38:44 +01:00 committed by GitHub
parent f09945bb1b
commit 6a9a1eeb8a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 12 additions and 120 deletions

View file

@ -35105,7 +35105,6 @@
"xpack.security.management.users.usersTitle": "Utilisateurs",
"xpack.security.management.usersTitle": "Utilisateurs",
"xpack.security.navControlComponent.accountMenuAriaLabel": "Menu Compte",
"xpack.security.navControlComponent.closeProjectLinkText": "Fermer le projet",
"xpack.security.navControlComponent.editProfileLinkText": "Modifier le profil",
"xpack.security.navControlComponent.loginLinkText": "Connexion",
"xpack.security.navControlComponent.logoutLinkText": "Déconnexion",

View file

@ -35080,7 +35080,6 @@
"xpack.security.management.users.usersTitle": "ユーザー",
"xpack.security.management.usersTitle": "ユーザー",
"xpack.security.navControlComponent.accountMenuAriaLabel": "アカウントメニュー",
"xpack.security.navControlComponent.closeProjectLinkText": "プロジェクトを閉じる",
"xpack.security.navControlComponent.editProfileLinkText": "プロフィールを編集",
"xpack.security.navControlComponent.loginLinkText": "ログイン",
"xpack.security.navControlComponent.logoutLinkText": "ログアウト",

View file

@ -35140,7 +35140,6 @@
"xpack.security.management.users.usersTitle": "用户",
"xpack.security.management.usersTitle": "用户",
"xpack.security.navControlComponent.accountMenuAriaLabel": "帐户菜单",
"xpack.security.navControlComponent.closeProjectLinkText": "关闭项目",
"xpack.security.navControlComponent.editProfileLinkText": "编辑配置文件",
"xpack.security.navControlComponent.loginLinkText": "登录",
"xpack.security.navControlComponent.logoutLinkText": "注销",

View file

@ -57,12 +57,7 @@ describe('SecurityNavControl', () => {
it('should render an avatar when user profile has loaded', async () => {
const wrapper = shallow(
<SecurityNavControl
editProfileUrl=""
logoutUrl=""
userMenuLinks$={userMenuLinks$}
buildFlavour={'traditional'}
/>
<SecurityNavControl editProfileUrl="" logoutUrl="" userMenuLinks$={userMenuLinks$} />
);
expect(useUserProfileMock).toHaveBeenCalledTimes(1);
@ -111,12 +106,7 @@ describe('SecurityNavControl', () => {
});
const wrapper = shallow(
<SecurityNavControl
editProfileUrl=""
logoutUrl=""
userMenuLinks$={userMenuLinks$}
buildFlavour={'traditional'}
/>
<SecurityNavControl editProfileUrl="" logoutUrl="" userMenuLinks$={userMenuLinks$} />
);
expect(useUserProfileMock).toHaveBeenCalledTimes(1);
@ -144,12 +134,7 @@ describe('SecurityNavControl', () => {
it('should open popover when avatar is clicked', async () => {
const wrapper = shallow(
<SecurityNavControl
editProfileUrl=""
logoutUrl=""
userMenuLinks$={userMenuLinks$}
buildFlavour={'traditional'}
/>
<SecurityNavControl editProfileUrl="" logoutUrl="" userMenuLinks$={userMenuLinks$} />
);
act(() => {
@ -169,12 +154,7 @@ describe('SecurityNavControl', () => {
});
const wrapper = shallow(
<SecurityNavControl
editProfileUrl=""
logoutUrl=""
userMenuLinks$={userMenuLinks$}
buildFlavour={'traditional'}
/>
<SecurityNavControl editProfileUrl="" logoutUrl="" userMenuLinks$={userMenuLinks$} />
);
act(() => {
@ -206,7 +186,6 @@ describe('SecurityNavControl', () => {
},
])
}
buildFlavour={'traditional'}
/>
);
@ -312,7 +291,6 @@ describe('SecurityNavControl', () => {
},
])
}
buildFlavour={'traditional'}
/>
);
@ -375,72 +353,6 @@ describe('SecurityNavControl', () => {
`);
});
it('should render `Close project` link when in Serverless', async () => {
const wrapper = shallow(
<SecurityNavControl
editProfileUrl="edit-profile-link"
logoutUrl=""
userMenuLinks$={
new BehaviorSubject([
{ label: 'link1', href: 'path-to-link-1', iconType: 'empty', order: 1 },
])
}
buildFlavour={'serverless'}
/>
);
expect(wrapper.find(EuiContextMenu).prop('panels')).toMatchInlineSnapshot(`
Array [
Object {
"content": <ContextMenuContent
closePopover={[Function]}
items={
Array [
Object {
"data-test-subj": "profileLink",
"href": "edit-profile-link",
"icon": <EuiIcon
size="m"
type="user"
/>,
"name": <Memo(MemoizedFormattedMessage)
defaultMessage="Edit profile"
id="xpack.security.navControlComponent.editProfileLinkText"
/>,
"onClick": [Function],
},
Object {
"content": undefined,
"data-test-subj": "userMenuLink__link1",
"href": "path-to-link-1",
"icon": <EuiIcon
size="m"
type="empty"
/>,
"name": "link1",
},
Object {
"data-test-subj": "logoutLink",
"href": "",
"icon": <EuiIcon
size="m"
type="exit"
/>,
"name": <Memo(MemoizedFormattedMessage)
defaultMessage="Close project"
id="xpack.security.navControlComponent.closeProjectLinkText"
/>,
},
]
}
/>,
"id": 0,
"title": "full name",
},
]
`);
});
it('should render anonymous user', async () => {
useUserProfileMock.mockReturnValue({
loading: false,
@ -456,12 +368,7 @@ describe('SecurityNavControl', () => {
});
const wrapper = shallow(
<SecurityNavControl
editProfileUrl=""
logoutUrl=""
userMenuLinks$={userMenuLinks$}
buildFlavour={'traditional'}
/>
<SecurityNavControl editProfileUrl="" logoutUrl="" userMenuLinks$={userMenuLinks$} />
);
expect(wrapper.find(EuiContextMenu).prop('panels')).toMatchInlineSnapshot(`

View file

@ -20,7 +20,6 @@ import React, { Fragment, useState } from 'react';
import useObservable from 'react-use/lib/useObservable';
import type { Observable } from 'rxjs';
import type { BuildFlavor } from '@kbn/config/src/types';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import type { UserMenuLink } from '@kbn/security-plugin-types-public';
@ -71,14 +70,12 @@ interface SecurityNavControlProps {
editProfileUrl: string;
logoutUrl: string;
userMenuLinks$: Observable<UserMenuLink[]>;
buildFlavour: BuildFlavor;
}
export const SecurityNavControl: FunctionComponent<SecurityNavControlProps> = ({
editProfileUrl,
logoutUrl,
userMenuLinks$,
buildFlavour,
}) => {
const userMenuLinks = useObservable(userMenuLinks$, []);
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
@ -158,11 +155,6 @@ export const SecurityNavControl: FunctionComponent<SecurityNavControlProps> = ({
id="xpack.security.navControlComponent.loginLinkText"
defaultMessage="Log in"
/>
) : buildFlavour === 'serverless' ? (
<FormattedMessage
id="xpack.security.navControlComponent.closeProjectLinkText"
defaultMessage="Close project"
/>
) : (
<FormattedMessage
id="xpack.security.navControlComponent.logoutLinkText"

View file

@ -55,7 +55,7 @@ describe('SecurityNavControlService', () => {
const license$ = new BehaviorSubject<ILicense>(validLicense);
const coreStart = coreMock.createStart();
const navControlService = new SecurityNavControlService('traditional');
const navControlService = new SecurityNavControlService();
navControlService.setup({
securityLicense: new SecurityLicenseService().setup({ license$ }).license,
logoutUrl: '/some/logout/url',
@ -125,7 +125,7 @@ describe('SecurityNavControlService', () => {
const license$ = new BehaviorSubject<ILicense>({} as ILicense);
const coreStart = coreMock.createStart();
const navControlService = new SecurityNavControlService('traditional');
const navControlService = new SecurityNavControlService();
navControlService.setup({
securityLicense: new SecurityLicenseService().setup({ license$ }).license,
logoutUrl: '/some/logout/url',
@ -145,7 +145,7 @@ describe('SecurityNavControlService', () => {
const license$ = new BehaviorSubject<ILicense>(validLicense);
const coreStart = coreMock.createStart();
const navControlService = new SecurityNavControlService('traditional');
const navControlService = new SecurityNavControlService();
navControlService.setup({
securityLicense: new SecurityLicenseService().setup({ license$ }).license,
logoutUrl: '/some/logout/url',
@ -162,7 +162,7 @@ describe('SecurityNavControlService', () => {
const license$ = new BehaviorSubject<ILicense>(validLicense);
const coreStart = coreMock.createStart();
const navControlService = new SecurityNavControlService('traditional');
const navControlService = new SecurityNavControlService();
navControlService.setup({
securityLicense: new SecurityLicenseService().setup({ license$ }).license,
logoutUrl: '/some/logout/url',
@ -184,7 +184,7 @@ describe('SecurityNavControlService', () => {
const license$ = new BehaviorSubject<ILicense>(validLicense);
const coreStart = coreMock.createStart();
const navControlService = new SecurityNavControlService('traditional');
const navControlService = new SecurityNavControlService();
navControlService.setup({
securityLicense: new SecurityLicenseService().setup({ license$ }).license,
logoutUrl: '/some/logout/url',
@ -207,7 +207,7 @@ describe('SecurityNavControlService', () => {
const coreSetup = coreMock.createSetup();
const license$ = new BehaviorSubject<ILicense>({} as ILicense);
navControlService = new SecurityNavControlService('traditional');
navControlService = new SecurityNavControlService();
navControlService.setup({
securityLicense: new SecurityLicenseService().setup({ license$ }).license,
logoutUrl: '/some/logout/url',

View file

@ -12,7 +12,6 @@ import ReactDOM from 'react-dom';
import type { Subscription } from 'rxjs';
import { BehaviorSubject, map, ReplaySubject, takeUntil } from 'rxjs';
import type { BuildFlavor } from '@kbn/config/src/types';
import type { CoreStart } from '@kbn/core/public';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
@ -51,8 +50,6 @@ export class SecurityNavControlService {
private readonly stop$ = new ReplaySubject<void>(1);
private userMenuLinks$ = new BehaviorSubject<UserMenuLink[]>([]);
constructor(private readonly buildFlavor: BuildFlavor) {}
public setup({ securityLicense, logoutUrl, securityApiClients }: SetupDeps) {
this.securityLicense = securityLicense;
this.logoutUrl = logoutUrl;
@ -119,7 +116,6 @@ export class SecurityNavControlService {
editProfileUrl={core.http.basePath.prepend('/security/account')}
logoutUrl={this.logoutUrl}
userMenuLinks$={this.userMenuLinks$}
buildFlavour={this.buildFlavor}
/>
</Providers>,
element

View file

@ -93,7 +93,7 @@ export class SecurityPlugin
this.config = this.initializerContext.config.get<ConfigType>();
this.securityCheckupService = new SecurityCheckupService(this.config, localStorage);
this.navControlService = new SecurityNavControlService(this.buildFlavor);
this.navControlService = new SecurityNavControlService();
this.managementService = new ManagementService(this.config);
}