mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Added user avatar tooltip (#142162)
* Added user avatar tooltip * Updated js docs
This commit is contained in:
parent
40b397d0e1
commit
44b38630e6
9 changed files with 334 additions and 10 deletions
|
@ -31,6 +31,5 @@ export const PanelWithCodeBlock: React.FunctionComponent<PanelWithCodeBlockProps
|
|||
</EuiCodeBlock>
|
||||
</EuiSplitPanel.Inner>
|
||||
</EuiSplitPanel.Outer>
|
||||
<EuiSpacer size="l" />
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -14,6 +14,7 @@ import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
|
|||
import { AvatarDemo } from './avatar_demo';
|
||||
import { PopoverDemo } from './popover_demo';
|
||||
import { SelectableDemo } from './selectable_demo';
|
||||
import { ToolTipDemo } from './tooltip_demo';
|
||||
|
||||
interface SetupDeps {
|
||||
developerExamples: DeveloperExamplesSetup;
|
||||
|
@ -38,14 +39,20 @@ export class UserProfilesPlugin implements Plugin<void, void, SetupDeps, StartDe
|
|||
// });
|
||||
|
||||
ReactDOM.render(
|
||||
<KibanaPageTemplate
|
||||
pageHeader={{
|
||||
pageTitle: 'User profile components',
|
||||
}}
|
||||
>
|
||||
<AvatarDemo />
|
||||
<SelectableDemo />
|
||||
<PopoverDemo />
|
||||
<KibanaPageTemplate>
|
||||
<KibanaPageTemplate.Header pageTitle="User profile components" />
|
||||
<KibanaPageTemplate.Section>
|
||||
<AvatarDemo />
|
||||
</KibanaPageTemplate.Section>
|
||||
<KibanaPageTemplate.Section>
|
||||
<ToolTipDemo />
|
||||
</KibanaPageTemplate.Section>
|
||||
<KibanaPageTemplate.Section>
|
||||
<SelectableDemo />
|
||||
</KibanaPageTemplate.Section>
|
||||
<KibanaPageTemplate.Section>
|
||||
<PopoverDemo />
|
||||
</KibanaPageTemplate.Section>
|
||||
</KibanaPageTemplate>,
|
||||
element
|
||||
);
|
||||
|
|
62
examples/user_profile_examples/public/tooltip_demo.tsx
Normal file
62
examples/user_profile_examples/public/tooltip_demo.tsx
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import { UserAvatarTip, UserToolTip } from '@kbn/user-profile-components';
|
||||
import type { UserProfile, UserProfileAvatarData } from '@kbn/user-profile-components';
|
||||
import { EuiCommentList, EuiComment } from '@elastic/eui';
|
||||
import { PanelWithCodeBlock } from './panel_with_code_block';
|
||||
|
||||
export const ToolTipDemo: FunctionComponent = () => {
|
||||
const userProfile: UserProfile<{ avatar: UserProfileAvatarData }> = {
|
||||
uid: 'u_9xDEQqUqoYCnFnPPLq5mIRHKL8gBTo_NiKgOnd5gGk0_0',
|
||||
enabled: true,
|
||||
user: {
|
||||
username: 'wet_dingo',
|
||||
email: 'wet_dingo@elastic.co',
|
||||
full_name: 'Wet Dingo',
|
||||
},
|
||||
data: {
|
||||
avatar: {
|
||||
color: '#09e8ca',
|
||||
initials: 'WD',
|
||||
imageUrl: 'https://source.unsplash.com/64x64/?dingo',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<PanelWithCodeBlock title="Tooltip" code={code}>
|
||||
<EuiCommentList>
|
||||
<EuiComment
|
||||
timelineAvatar={
|
||||
<UserAvatarTip user={userProfile.user} avatar={userProfile.data.avatar} />
|
||||
}
|
||||
username={
|
||||
<UserToolTip
|
||||
position="top"
|
||||
delay="regular"
|
||||
user={userProfile.user}
|
||||
avatar={userProfile.data.avatar}
|
||||
>
|
||||
<strong>{userProfile.user.full_name}</strong>
|
||||
</UserToolTip>
|
||||
}
|
||||
event="pushed incident X0Z235 on Jan 3, 2020"
|
||||
/>
|
||||
</EuiCommentList>
|
||||
</PanelWithCodeBlock>
|
||||
);
|
||||
};
|
||||
|
||||
const code = `import { UserToolTip, UserAvatarTip } from '@kbn/user-profile-components';
|
||||
|
||||
<UserToolTip user={userProfile.user} avatar={userProfile.data.avatar}>
|
||||
<!-- An inline element to trigger the tooltip -->
|
||||
</UserToolTip>
|
||||
|
||||
<UserAvatarTip user={userProfile.user} avatar={userProfile.data.avatar} />`;
|
|
@ -7,9 +7,12 @@
|
|||
*/
|
||||
|
||||
export type { UserAvatarProps, UserProfileWithAvatar } from './src/user_avatar';
|
||||
export type { UserToolTipProps } from './src/user_tooltip';
|
||||
export type { UserProfilesSelectableProps } from './src/user_profiles_selectable';
|
||||
export type { UserProfilesPopoverProps } from './src/user_profiles_popover';
|
||||
export { UserAvatar } from './src/user_avatar';
|
||||
export { UserAvatarTip } from './src/user_avatar_tip';
|
||||
export { UserToolTip } from './src/user_tooltip';
|
||||
export { UserProfilesSelectable } from './src/user_profiles_selectable';
|
||||
export { UserProfilesPopover } from './src/user_profiles_popover';
|
||||
export { getUserDisplayName } from './src/user_profile';
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import { UserAvatarTip } from './user_avatar_tip';
|
||||
|
||||
describe('UserAvatarTip', () => {
|
||||
it('should render `UserToolTip` correctly with `UserAvatar`', () => {
|
||||
const wrapper = shallow(
|
||||
<UserAvatarTip
|
||||
user={{
|
||||
username: 'delighted_nightingale',
|
||||
email: 'delighted_nightingale@elastic.co',
|
||||
full_name: 'Delighted Nightingale',
|
||||
}}
|
||||
avatar={{
|
||||
color: '#09e8ca',
|
||||
initials: 'DN',
|
||||
imageUrl: 'https://source.unsplash.com/64x64/?cat',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expect(wrapper).toMatchInlineSnapshot(`
|
||||
<UserToolTip
|
||||
avatar={
|
||||
Object {
|
||||
"color": "#09e8ca",
|
||||
"imageUrl": "https://source.unsplash.com/64x64/?cat",
|
||||
"initials": "DN",
|
||||
}
|
||||
}
|
||||
delay="regular"
|
||||
position="top"
|
||||
user={
|
||||
Object {
|
||||
"email": "delighted_nightingale@elastic.co",
|
||||
"full_name": "Delighted Nightingale",
|
||||
"username": "delighted_nightingale",
|
||||
}
|
||||
}
|
||||
>
|
||||
<UserAvatar
|
||||
avatar={
|
||||
Object {
|
||||
"color": "#09e8ca",
|
||||
"imageUrl": "https://source.unsplash.com/64x64/?cat",
|
||||
"initials": "DN",
|
||||
}
|
||||
}
|
||||
user={
|
||||
Object {
|
||||
"email": "delighted_nightingale@elastic.co",
|
||||
"full_name": "Delighted Nightingale",
|
||||
"username": "delighted_nightingale",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</UserToolTip>
|
||||
`);
|
||||
});
|
||||
|
||||
it('should not render `UserToolTip` when user is not set', () => {
|
||||
const wrapper = shallow(<UserAvatarTip />);
|
||||
expect(wrapper).toMatchInlineSnapshot(`<UserAvatar />`);
|
||||
});
|
||||
});
|
29
packages/kbn-user-profile-components/src/user_avatar_tip.tsx
Normal file
29
packages/kbn-user-profile-components/src/user_avatar_tip.tsx
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { FunctionComponent } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import type { UserAvatarProps } from './user_avatar';
|
||||
import { UserAvatar } from './user_avatar';
|
||||
import { UserToolTip } from './user_tooltip';
|
||||
|
||||
/**
|
||||
* Renders a user avatar with tooltip
|
||||
*/
|
||||
export const UserAvatarTip: FunctionComponent<UserAvatarProps> = ({ user, avatar, ...rest }) => {
|
||||
if (!user) {
|
||||
return <UserAvatar {...rest} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<UserToolTip user={user} avatar={avatar} position="top" delay="regular">
|
||||
<UserAvatar user={user} avatar={avatar} {...rest} />
|
||||
</UserToolTip>
|
||||
);
|
||||
};
|
|
@ -301,7 +301,7 @@ export const UserProfilesSelectable = <Option extends UserProfileWithAvatar | nu
|
|||
<EuiFlexItem grow={false}>
|
||||
<EuiHighlight search={searchValue}>{option.label}</EuiHighlight>
|
||||
</EuiFlexItem>
|
||||
{option.user.email ? (
|
||||
{option.user.email && option.user.email !== option.label ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTextColor color="subdued">
|
||||
{searchValue ? (
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import { UserToolTip } from './user_tooltip';
|
||||
|
||||
describe('UserToolTip', () => {
|
||||
it('should render `EuiToolTip` correctly with `UserAvatar`', () => {
|
||||
const wrapper = shallow(
|
||||
<UserToolTip
|
||||
user={{
|
||||
username: 'delighted_nightingale',
|
||||
email: 'delighted_nightingale@elastic.co',
|
||||
full_name: 'Delighted Nightingale',
|
||||
}}
|
||||
avatar={{
|
||||
color: '#09e8ca',
|
||||
initials: 'DN',
|
||||
imageUrl: 'https://source.unsplash.com/64x64/?cat',
|
||||
}}
|
||||
position="top"
|
||||
delay="regular"
|
||||
>
|
||||
<button>Toggle</button>
|
||||
</UserToolTip>
|
||||
);
|
||||
expect(wrapper).toMatchInlineSnapshot(`
|
||||
<EuiToolTip
|
||||
content={
|
||||
<EuiFlexGroup
|
||||
alignItems="center"
|
||||
gutterSize="s"
|
||||
>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<UserAvatar
|
||||
avatar={
|
||||
Object {
|
||||
"color": "#09e8ca",
|
||||
"imageUrl": "https://source.unsplash.com/64x64/?cat",
|
||||
"initials": "DN",
|
||||
}
|
||||
}
|
||||
size="l"
|
||||
user={
|
||||
Object {
|
||||
"email": "delighted_nightingale@elastic.co",
|
||||
"full_name": "Delighted Nightingale",
|
||||
"username": "delighted_nightingale",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
grow={true}
|
||||
>
|
||||
<EuiFlexGroup
|
||||
direction="column"
|
||||
gutterSize="none"
|
||||
>
|
||||
<EuiFlexItem>
|
||||
<strong>
|
||||
Delighted Nightingale
|
||||
</strong>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
delighted_nightingale@elastic.co
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
delay="regular"
|
||||
display="inlineBlock"
|
||||
position="top"
|
||||
>
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
</EuiToolTip>
|
||||
`);
|
||||
});
|
||||
});
|
60
packages/kbn-user-profile-components/src/user_tooltip.tsx
Normal file
60
packages/kbn-user-profile-components/src/user_tooltip.tsx
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { EuiToolTipProps } from '@elastic/eui';
|
||||
import { EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import type { FunctionComponent } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import type { UserProfileUserInfo, UserProfileAvatarData } from './user_profile';
|
||||
import { UserAvatar } from './user_avatar';
|
||||
import { getUserDisplayName } from './user_profile';
|
||||
|
||||
/**
|
||||
* Props of {@link UserToolTip} component
|
||||
*/
|
||||
export interface UserToolTipProps extends Omit<EuiToolTipProps, 'content' | 'title'> {
|
||||
/**
|
||||
* User to be rendered
|
||||
*/
|
||||
user: UserProfileUserInfo;
|
||||
|
||||
/**
|
||||
* Avatar data of user to be rendered
|
||||
*/
|
||||
avatar?: UserProfileAvatarData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a tooltip with user information
|
||||
*/
|
||||
export const UserToolTip: FunctionComponent<UserToolTipProps> = ({ user, avatar, ...rest }) => {
|
||||
const displayName = getUserDisplayName(user);
|
||||
return (
|
||||
<EuiToolTip
|
||||
content={
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<UserAvatar user={user} avatar={avatar} size="l" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow>
|
||||
<EuiFlexGroup direction="column" gutterSize="none">
|
||||
<EuiFlexItem>
|
||||
<strong>{displayName}</strong>
|
||||
</EuiFlexItem>
|
||||
{user.email && user.email !== displayName ? (
|
||||
<EuiFlexItem>{user.email}</EuiFlexItem>
|
||||
) : undefined}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue