Added user avatar tooltip (#142162)

* Added user avatar tooltip

* Updated js docs
This commit is contained in:
Thom Heymann 2022-10-03 15:30:04 +01:00 committed by GitHub
parent 40b397d0e1
commit 44b38630e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 334 additions and 10 deletions

View file

@ -31,6 +31,5 @@ export const PanelWithCodeBlock: React.FunctionComponent<PanelWithCodeBlockProps
</EuiCodeBlock>
</EuiSplitPanel.Inner>
</EuiSplitPanel.Outer>
<EuiSpacer size="l" />
</>
);

View file

@ -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
);

View 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} />`;

View file

@ -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';

View file

@ -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 />`);
});
});

View 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>
);
};

View file

@ -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 ? (

View file

@ -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>
`);
});
});

View 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}
/>
);
};