mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Grafana UI: Add Avatar component (#76429)
* Grafana/UI: Add Avatar component * Use the new component * Add docs and story * Update type check * Codeformat
This commit is contained in:
parent
c04e96b3ed
commit
bc98f3d139
27
packages/grafana-ui/src/components/UsersIndicator/Avatar.mdx
Normal file
27
packages/grafana-ui/src/components/UsersIndicator/Avatar.mdx
Normal file
@ -0,0 +1,27 @@
|
||||
import { Meta, ArgTypes } from '@storybook/blocks';
|
||||
import { Avatar } from './Avatar';
|
||||
|
||||
<Meta title="MDX|Avatar" component={Avatar} />
|
||||
|
||||
# Avatar
|
||||
|
||||
A basic component for displaying a user's avatar. The default dimensions (width and height) of this component are set to 24 pixels, but these can be overridden by passing a `width` and `height` prop. Both props are `ThemeSpacingTokens`, meaning that instead of passing a specific pixel value, you should pass a token value which will be converted into pixels by multiplying it with the base spacing value - `8`.
|
||||
|
||||
## Usage
|
||||
|
||||
```jsx
|
||||
import { Avatar } from '@grafana/ui';
|
||||
|
||||
const user = {
|
||||
id: 5,
|
||||
name: 'Admin',
|
||||
email: 'admin@org.com',
|
||||
avatarUrl: 'https://secure.gravatar.com/avatar',
|
||||
};
|
||||
|
||||
const Example = () => {
|
||||
return <Avatar src={user.avatarUrl} alt={`Avatar for the user ${user.name}`} width={4} height={4} />;
|
||||
};
|
||||
```
|
||||
|
||||
<ArgTypes of={Avatar} />
|
@ -0,0 +1,32 @@
|
||||
import { Meta, StoryFn } from '@storybook/react';
|
||||
import React from 'react';
|
||||
|
||||
import { Avatar } from '@grafana/ui';
|
||||
|
||||
import mdx from './Avatar.mdx';
|
||||
|
||||
const meta: Meta<typeof Avatar> = {
|
||||
title: 'General/UsersIndicator/Avatar',
|
||||
component: Avatar,
|
||||
parameters: {
|
||||
docs: { page: mdx },
|
||||
controls: { exclude: ['alt'] },
|
||||
},
|
||||
argTypes: {
|
||||
width: { control: 'number' },
|
||||
height: { control: 'number' },
|
||||
},
|
||||
};
|
||||
|
||||
const Template: StoryFn<typeof Avatar> = (args) => <Avatar {...args} />;
|
||||
|
||||
export const Basic = Template.bind({});
|
||||
|
||||
Basic.args = {
|
||||
src: 'https://secure.gravatar.com/avatar',
|
||||
alt: 'User avatar',
|
||||
width: 3,
|
||||
height: 3,
|
||||
};
|
||||
|
||||
export default meta;
|
33
packages/grafana-ui/src/components/UsersIndicator/Avatar.tsx
Normal file
33
packages/grafana-ui/src/components/UsersIndicator/Avatar.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import { GrafanaTheme2, ThemeSpacingTokens } from '@grafana/data';
|
||||
|
||||
import { useStyles2 } from '../../themes';
|
||||
import { getResponsiveStyle, ResponsiveProp } from '../Layout/utils/responsiveness';
|
||||
|
||||
export interface AvatarProps {
|
||||
src: string;
|
||||
alt: string;
|
||||
width?: ResponsiveProp<ThemeSpacingTokens>;
|
||||
height?: ResponsiveProp<ThemeSpacingTokens>;
|
||||
}
|
||||
export const Avatar = ({ src, alt, width, height }: AvatarProps) => {
|
||||
const styles = useStyles2(getStyles, width, height);
|
||||
|
||||
return <img className={styles.image} src={src} alt={alt} />;
|
||||
};
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2, width: AvatarProps['width'] = 3, height: AvatarProps['height'] = 3) => {
|
||||
return {
|
||||
image: css([
|
||||
getResponsiveStyle(theme, width, (val) => ({
|
||||
width: theme.spacing(val),
|
||||
})),
|
||||
getResponsiveStyle(theme, height, (val) => ({
|
||||
height: theme.spacing(val),
|
||||
})),
|
||||
{ borderRadius: theme.shape.radius.circle },
|
||||
]),
|
||||
};
|
||||
};
|
@ -261,6 +261,7 @@ export { Dropdown } from './Dropdown/Dropdown';
|
||||
export { PluginSignatureBadge, type PluginSignatureBadgeProps } from './PluginSignatureBadge/PluginSignatureBadge';
|
||||
export { UserIcon, type UserIconProps } from './UsersIndicator/UserIcon';
|
||||
export { type UserView } from './UsersIndicator/types';
|
||||
export { Avatar } from './UsersIndicator/Avatar';
|
||||
// Export this until we've figured out a good approach to inline form styles.
|
||||
export { InlineFormLabel } from './FormLabel/FormLabel';
|
||||
export { Divider } from './Divider/Divider';
|
||||
|
@ -1,28 +0,0 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
|
||||
export interface AvatarProps {
|
||||
src?: string;
|
||||
alt: string;
|
||||
}
|
||||
export const Avatar = ({ src, alt }: AvatarProps) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
if (!src) {
|
||||
return null;
|
||||
}
|
||||
return <img className={styles.image} src={src} alt={alt} />;
|
||||
};
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
image: css({
|
||||
width: theme.spacing(3),
|
||||
height: theme.spacing(3),
|
||||
borderRadius: theme.shape.radius.circle,
|
||||
}),
|
||||
};
|
||||
};
|
@ -17,6 +17,7 @@ import {
|
||||
Pagination,
|
||||
HorizontalGroup,
|
||||
VerticalGroup,
|
||||
Avatar,
|
||||
} from '@grafana/ui';
|
||||
import { UserRolePicker } from 'app/core/components/RolePicker/UserRolePicker';
|
||||
import { fetchRoleOptions } from 'app/core/components/RolePicker/api';
|
||||
@ -27,8 +28,6 @@ import { AccessControlAction, OrgUser, Role } from 'app/types';
|
||||
|
||||
import { OrgRolePicker } from '../OrgRolePicker';
|
||||
|
||||
import { Avatar } from './Avatar';
|
||||
|
||||
type Cell<T extends keyof OrgUser = keyof OrgUser> = CellProps<OrgUser, OrgUser[T]>;
|
||||
|
||||
const disabledRoleMessage = `This user's role is not editable because it is synchronized from your auth provider.
|
||||
@ -95,7 +94,7 @@ export const OrgUsersTable = ({
|
||||
{
|
||||
id: 'avatarUrl',
|
||||
header: '',
|
||||
cell: ({ cell: { value } }: Cell<'avatarUrl'>) => <Avatar src={value} alt="User avatar" />,
|
||||
cell: ({ cell: { value } }: Cell<'avatarUrl'>) => value && <Avatar src={value} alt="User avatar" />,
|
||||
},
|
||||
{
|
||||
id: 'login',
|
||||
|
@ -14,11 +14,11 @@ import {
|
||||
VerticalGroup,
|
||||
HorizontalGroup,
|
||||
FetchDataFunc,
|
||||
Avatar,
|
||||
} from '@grafana/ui';
|
||||
import { TagBadge } from 'app/core/components/TagFilter/TagBadge';
|
||||
import { UserDTO } from 'app/types';
|
||||
|
||||
import { Avatar } from './Avatar';
|
||||
import { OrgUnits } from './OrgUnits';
|
||||
|
||||
type Cell<T extends keyof UserDTO = keyof UserDTO> = CellProps<UserDTO, UserDTO[T]>;
|
||||
@ -46,7 +46,7 @@ export const UsersTable = ({
|
||||
{
|
||||
id: 'avatarUrl',
|
||||
header: '',
|
||||
cell: ({ cell: { value } }: Cell<'avatarUrl'>) => <Avatar src={value} alt={'User avatar'} />,
|
||||
cell: ({ cell: { value } }: Cell<'avatarUrl'>) => value && <Avatar src={value} alt={'User avatar'} />,
|
||||
},
|
||||
{
|
||||
id: 'login',
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
Pagination,
|
||||
VerticalGroup,
|
||||
useStyles2,
|
||||
Avatar,
|
||||
} from '@grafana/ui';
|
||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
@ -25,7 +26,6 @@ import { contextSrv } from 'app/core/services/context_srv';
|
||||
import { AccessControlAction, Role, StoreState, Team } from 'app/types';
|
||||
|
||||
import { TeamRolePicker } from '../../core/components/RolePicker/TeamRolePicker';
|
||||
import { Avatar } from '../admin/Users/Avatar';
|
||||
|
||||
import { deleteTeam, loadTeams, changePage, changeQuery, changeSort } from './state/actions';
|
||||
|
||||
@ -70,7 +70,7 @@ export const TeamList = ({
|
||||
{
|
||||
id: 'avatarUrl',
|
||||
header: '',
|
||||
cell: ({ cell: { value } }: Cell<'avatarUrl'>) => <Avatar src={value} alt="User avatar" />,
|
||||
cell: ({ cell: { value } }: Cell<'avatarUrl'>) => value && <Avatar src={value} alt="User avatar" />,
|
||||
},
|
||||
{
|
||||
id: 'name',
|
||||
|
Loading…
Reference in New Issue
Block a user