Skip to content

Commit b62504e

Browse files
Other visual tweaks and sidebar integrations
1 parent 8bf9079 commit b62504e

9 files changed

Lines changed: 242 additions & 25 deletions

File tree

src/core/packages/chrome/browser-internal/src/ui/project/app_menu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const useAppMenuBarStyles = (euiTheme: UseEuiTheme['euiTheme']) =>
3737
justifyContent: 'end',
3838
alignItems: 'center',
3939
padding: `0 ${euiTheme.size.s}`,
40-
background: euiTheme.colors.body,
40+
background: `${euiTheme.colors.backgroundBasePlain}`,
4141
borderBottom: euiTheme.border.thin,
4242
marginBottom: `-${euiTheme.border.width.thin}`,
4343
};

src/core/packages/chrome/navigation/src/components/secondary_menu/section.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { EuiText, useEuiTheme } from '@elastic/eui';
1111
import type { ReactNode } from 'react';
1212
import React from 'react';
1313
import { css } from '@emotion/react';
14-
import { SECONDARY_MENU_WIDTH } from '../../constants';
1514

1615
export interface SecondaryMenuSectionProps {
1716
children: ReactNode;

src/platform/packages/shared/kbn-unified-tabs/src/components/tabs_visual_glue_to_header/tab_with_background.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const TabWithBackground = React.forwardRef<HTMLDivElement, TabWithBackgro
2929
const { isProjectChromeStyle } = useChromeStyle(services);
3030

3131
const selectedTabBackgroundColor = isProjectChromeStyle
32-
? euiTheme.colors.body
32+
? euiTheme.colors.backgroundBasePlain
3333
: euiTheme.colors.emptyShade;
3434

3535
return (

src/platform/plugins/shared/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps)
386386
css={css`
387387
height: 100%;
388388
display: ${isSidebarCollapsed ? 'none' : 'flex'};
389-
background-color: ${euiTheme.colors.body};
389+
background-color: 'transparent';
390390
`}
391391
>
392392
<EuiFlexItem>

x-pack/platform/plugins/shared/fleet/public/layouts/without_header.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
import React from 'react';
99
import styled from 'styled-components';
1010
import { EuiPage, EuiPageBody, EuiSpacer } from '@elastic/eui';
11+
import { layoutVar } from '@kbn/core-chrome-layout-constants';
1112

1213
export const Wrapper = styled.div`
1314
background-color: ${(props) => props.theme.eui.euiColorEmptyShade};
1415
1516
// Set the min height to the viewport size minus the height of any global Kibana headers
16-
min-height: calc(100vh - var(--euiFixedHeadersOffset, 0));
17+
min-height: ${layoutVar('application.content.height', '100vh')};
1718
`;
1819

1920
export const Page = styled(EuiPage)`

x-pack/platform/plugins/shared/security/public/nav_control/nav_control_service.tsx

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
import { sortBy } from 'lodash';
99
import type { FC, PropsWithChildren } from 'react';
1010
import React from 'react';
11-
import ReactDOM from 'react-dom';
1211
import type { Subscription } from 'rxjs';
1312
import { BehaviorSubject, map, ReplaySubject, takeUntil } from 'rxjs';
1413

1514
import type { CoreStart } from '@kbn/core/public';
15+
import { WORKSPACE_SIDEBAR_APP_PROFILE } from '@kbn/core-chrome-browser';
1616
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
1717
import type {
1818
AuthenticationServiceSetup,
@@ -21,7 +21,8 @@ import type {
2121
} from '@kbn/security-plugin-types-public';
2222
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
2323

24-
import { SecurityNavControl } from './nav_control_component';
24+
import { ProfileSidebarApp } from './sidebar_app';
25+
import { ProfileSidebarButton } from './sidebar_button';
2526
import type { SecurityLicense } from '../../common';
2627
import type { SecurityApiClients } from '../components';
2728
import { AuthenticationProvider, SecurityApiClientsProvider } from '../components';
@@ -106,23 +107,25 @@ export class SecurityNavControlService {
106107
}
107108

108109
private registerSecurityNavControl(core: CoreStart, authc: AuthenticationServiceSetup) {
109-
core.chrome.navControls.registerRight({
110-
order: 4000,
111-
mount: (element: HTMLElement) => {
112-
ReactDOM.render(
113-
core.rendering.addContext(
114-
<Providers services={core} authc={authc} securityApiClients={this.securityApiClients}>
115-
<SecurityNavControl
116-
editProfileUrl={core.http.basePath.prepend('/security/account')}
117-
logoutUrl={this.logoutUrl}
118-
userMenuLinks$={this.userMenuLinks$}
119-
/>
120-
</Providers>
121-
),
122-
element
123-
);
124-
125-
return () => ReactDOM.unmountComponentAtNode(element);
110+
core.chrome.workspace.sidebar.registerSidebarApp({
111+
appId: WORKSPACE_SIDEBAR_APP_PROFILE,
112+
button: {
113+
iconType: () => (
114+
<AuthenticationProvider authc={authc}>
115+
<SecurityApiClientsProvider {...this.securityApiClients}>
116+
<ProfileSidebarButton />
117+
</SecurityApiClientsProvider>
118+
</AuthenticationProvider>
119+
),
120+
},
121+
app: {
122+
title: 'Profile',
123+
containerPadding: 'm',
124+
children: (
125+
<Providers services={core} authc={authc} securityApiClients={this.securityApiClients}>
126+
<ProfileSidebarApp {...{ logoutUrl: this.logoutUrl }} />
127+
</Providers>
128+
),
126129
},
127130
});
128131

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
// THIS COMPONENT IS ULTRA-HACKY AND FOR DEMO PURPOSES ONLY
9+
// DO NOT USE THIS AS A REFERENCE FOR MUCH IF ANYTHING
10+
11+
import {
12+
EuiBadge,
13+
EuiButtonEmpty,
14+
EuiFlexGroup,
15+
EuiFlexItem,
16+
EuiIcon,
17+
EuiKeyPadMenu,
18+
EuiKeyPadMenuItem,
19+
EuiText,
20+
EuiTitle,
21+
} from '@elastic/eui';
22+
import React from 'react';
23+
24+
import type { AuthenticatedUser } from '@kbn/security-plugin-types-common';
25+
import type { GetUserProfileResponse } from '@kbn/security-plugin-types-public';
26+
import type { UserProfileAvatarData } from '@kbn/user-profile-components';
27+
28+
import { useCurrentUser, useUserProfile } from '../components';
29+
30+
type UserProfile = ReturnType<typeof useUserProfile<{ avatar: UserProfileAvatarData }>>;
31+
type UserProfileValue = GetUserProfileResponse<{ avatar: UserProfileAvatarData }>;
32+
33+
interface Props {
34+
logoutUrl: string;
35+
}
36+
37+
export const ProfileSidebarApp = (props: Props) => {
38+
const userProfile: UserProfile = useUserProfile<{ avatar: UserProfileAvatarData }>(
39+
'avatar,userSettings'
40+
);
41+
42+
const currentUser = useCurrentUser();
43+
44+
if (userProfile.value) {
45+
return <UserProfileContent profile={userProfile.value} {...props} />;
46+
}
47+
48+
if (currentUser.value) {
49+
return <CurrentUserContent user={currentUser.value} {...props} />;
50+
}
51+
52+
return null;
53+
};
54+
55+
const UserProfileContent = ({
56+
profile: { user },
57+
...props
58+
}: { profile: UserProfileValue } & Props) => {
59+
return (
60+
<>
61+
<CurrentUserContent user={user} {...props} />
62+
</>
63+
);
64+
};
65+
66+
const CurrentUserContent = ({
67+
user,
68+
logoutUrl,
69+
}: { user: Pick<AuthenticatedUser, 'roles' | 'username'> } & Props) => {
70+
const [selectedValue, setSelectedValue] = React.useState('light');
71+
72+
return (
73+
<>
74+
<EuiFlexGroup direction="column" gutterSize="l">
75+
<EuiFlexItem grow={false}>
76+
<EuiFlexGroup alignItems="center">
77+
<EuiFlexItem>
78+
<EuiTitle size="m">
79+
<h4>{user.username}</h4>
80+
</EuiTitle>
81+
</EuiFlexItem>
82+
<EuiFlexItem grow={false}>
83+
<EuiButtonEmpty href={logoutUrl} aria-label="Log out">
84+
Log out
85+
</EuiButtonEmpty>
86+
</EuiFlexItem>
87+
</EuiFlexGroup>
88+
</EuiFlexItem>
89+
<EuiFlexItem grow={false}>
90+
<EuiFlexGroup direction="column" gutterSize="s">
91+
<EuiFlexItem>
92+
<EuiTitle size="xs">
93+
<h5>Roles</h5>
94+
</EuiTitle>
95+
</EuiFlexItem>
96+
<EuiFlexItem>
97+
<div>
98+
{user.roles.map((role) => (
99+
<EuiBadge key={role} color="hollow">
100+
{role}
101+
</EuiBadge>
102+
))}
103+
</div>
104+
</EuiFlexItem>
105+
</EuiFlexGroup>
106+
</EuiFlexItem>
107+
<EuiFlexItem grow={false}>
108+
<EuiFlexGroup direction="column" gutterSize="s">
109+
<EuiFlexItem>
110+
<EuiTitle size="xs">
111+
<h5>Theme</h5>
112+
</EuiTitle>
113+
</EuiFlexItem>
114+
<EuiFlexItem>
115+
<EuiText size="xs">Select the appearance of your interface.</EuiText>
116+
</EuiFlexItem>
117+
<EuiFlexItem>
118+
<EuiKeyPadMenu
119+
aria-label="Theme"
120+
data-test-subj="appearanceColorMode"
121+
checkable={{
122+
legend: <span>Mode</span>,
123+
}}
124+
>
125+
{options.map(({ id, label, icon }) => (
126+
<EuiKeyPadMenuItem
127+
name={id}
128+
key={id}
129+
label={label}
130+
checkable="single"
131+
isSelected={selectedValue === id}
132+
onChange={() => {
133+
setSelectedValue(id);
134+
}}
135+
data-test-subj={`colorModeKeyPadItem${id}`}
136+
>
137+
<EuiIcon type={icon} size="l" />
138+
</EuiKeyPadMenuItem>
139+
))}
140+
</EuiKeyPadMenu>
141+
</EuiFlexItem>
142+
<EuiFlexItem grow={false}>
143+
<EuiFlexGroup direction="column" gutterSize="s">
144+
<EuiFlexItem>
145+
<EuiTitle size="xs">
146+
<h5>Workspace options</h5>
147+
</EuiTitle>
148+
</EuiFlexItem>
149+
</EuiFlexGroup>
150+
</EuiFlexItem>
151+
</EuiFlexGroup>
152+
</EuiFlexItem>
153+
</EuiFlexGroup>
154+
</>
155+
);
156+
};
157+
158+
const options = [
159+
{
160+
id: 'system',
161+
label: 'System',
162+
icon: 'desktop',
163+
},
164+
{
165+
id: 'light',
166+
label: 'Light',
167+
icon: 'sun',
168+
},
169+
{
170+
id: 'dark',
171+
label: 'Dark',
172+
icon: 'moon',
173+
},
174+
{
175+
id: 'space_default',
176+
label: 'Space',
177+
icon: 'spaces',
178+
},
179+
];
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { EuiLoadingSpinner } from '@elastic/eui';
9+
import React from 'react';
10+
11+
import type { UserProfileAvatarData } from '@kbn/user-profile-components';
12+
import { UserAvatar } from '@kbn/user-profile-components';
13+
14+
import { useCurrentUser, useUserProfile } from '../components';
15+
16+
export const ProfileSidebarButton = () => {
17+
const userProfile = useUserProfile<{ avatar: UserProfileAvatarData }>('avatar,userSettings');
18+
const currentUser = useCurrentUser(); // User profiles do not exist for anonymous users so need to fetch current user as well
19+
20+
if (userProfile.value) {
21+
return (
22+
<UserAvatar
23+
user={userProfile.value.user}
24+
avatar={userProfile.value.data.avatar}
25+
size="s"
26+
data-test-subj="userMenuAvatar"
27+
/>
28+
);
29+
} else if (currentUser.value && userProfile.error) {
30+
return <UserAvatar user={currentUser.value} size="s" data-test-subj="userMenuAvatar" />;
31+
}
32+
33+
return <EuiLoadingSpinner size="m" />;
34+
};

x-pack/platform/plugins/shared/security/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@
9595
"@kbn/react-kibana-context-theme",
9696
"@kbn/css-utils",
9797
"@kbn/licensing-types",
98-
"@kbn/lazy-object"
98+
"@kbn/lazy-object",
99+
"@kbn/core-workspace-chrome-state"
99100
],
100101
"exclude": ["target/**/*"]
101102
}

0 commit comments

Comments
 (0)