Skip to content
Snippets Groups Projects
Unverified Commit 47c861e3 authored by Douglas Fabris's avatar Douglas Fabris Committed by GitHub
Browse files

[FIX] UserCard sanitization (#25089)

* fix: UserCard sanitization

* chore: fix customStatus margin
parent 977f167f
No related branches found
No related tags found
No related merge requests found
Showing
with 226 additions and 56 deletions
import { ActionButton } from '@rocket.chat/fuselage';
import React from 'react';
/**
* @param {Object} props
* @param {string=} props.label
* @param {string=} props.icon
*/
const Action = ({ label, ...props }) => <ActionButton small title={label} {...props} mi='x2' />;
export default Action;
import { Box } from '@rocket.chat/fuselage';
import React from 'react';
const Info = (props) => <Box mbe='x4' is='span' fontScale='p2' color='hint' withTruncatedText {...props} />;
export default Info;
import React from 'react';
import Info from './Info';
const Roles = ({ children }) => (
<Info rcx-user-card__roles m='neg-x2' flexWrap='wrap' display='flex' flexShrink={0}>
{children}
</Info>
);
export default Roles;
......@@ -12,9 +12,20 @@ export default {
args: {
name: 'guilherme.gazzo',
customStatus: '🛴 currently working on User Card',
roles: [<UserCard.Role>Admin</UserCard.Role>, <UserCard.Role>Rocket.Chat</UserCard.Role>, <UserCard.Role>Team</UserCard.Role>],
roles: (
<>
<UserCard.Role>Admin</UserCard.Role>
<UserCard.Role>Rocket.Chat</UserCard.Role>
<UserCard.Role>Team</UserCard.Role>
</>
),
bio: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla tempus, eros convallis vulputate cursus, nisi neque eleifend libero, eget lacinia justo purus nec est. In at sodales ipsum. Sed lacinia quis purus eget pulvinar. Aenean eu pretium nunc, at aliquam magna. Praesent dignissim, tortor sed volutpat mattis, mauris diam pulvinar leo, porta commodo risus est non purus. Mauris in justo vel lorem ullamcorper hendrerit. Nam est metus, viverra a pellentesque vitae, ornare eget odio. Morbi tempor feugiat mattis. Morbi non felis tempor, aliquam justo sed, sagittis nibh. Mauris consequat ex metus. Praesent sodales sit amet nibh a vulputate. Integer commodo, mi vel bibendum sollicitudin, urna lectus accumsan ante, eget faucibus augue ex id neque. Aenean consectetur, orci a pellentesque mattis, tortor tellus fringilla elit, non ullamcorper risus nunc feugiat risus. Fusce sit amet nisi dapibus turpis commodo placerat. In tortor ante, vehicula sit amet augue et, imperdiet porta sem.',
actions: [<UserCard.Action icon='message' />, <UserCard.Action icon='phone' />],
actions: (
<>
<UserCard.Action icon='message' />
<UserCard.Action icon='phone' />
</>
),
localTime: 'Local Time: 7:44 AM',
},
} as ComponentMeta<typeof UserCard>;
......
import { css } from '@rocket.chat/css-in-js';
import { Box, ActionButton, Skeleton } from '@rocket.chat/fuselage';
import React, { forwardRef } from 'react';
import React, { forwardRef, ReactNode, ComponentProps } from 'react';
import { useTranslation } from '../../contexts/TranslationContext';
import MarkdownText from '../MarkdownText';
import * as Status from '../UserStatus';
import UserAvatar from '../avatar/UserAvatar';
import Info from './Info';
import Roles from './Roles';
import UserCardContainer from './UserCardContainer';
import Username from './Username';
import UserCardInfo from './UserCardInfo';
import UserCardRoles from './UserCardRoles';
import UserCardUsername from './UserCardUsername';
const clampStyle = {
display: '-webkit-box',
overflow: 'hidden',
WebkitLineClamp: 3,
WebkitBoxOrient: 'vertical',
wordBreak: 'break-all',
const clampStyle = css`
display: -webkit-box;
overflow: hidden;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
word-break: break-all;
`;
type UserCardProps = {
className?: string;
style?: ComponentProps<typeof Box>['style'];
open?: () => void;
name?: string;
username?: string;
etag?: string;
customStatus?: ReactNode;
roles?: ReactNode;
bio?: ReactNode;
status?: ReactNode;
actions?: ReactNode;
localTime?: ReactNode;
onClose?: () => void;
nickname?: string;
};
const UserCard = forwardRef(function UserCard(
......@@ -22,7 +41,7 @@ const UserCard = forwardRef(function UserCard(
className,
style,
open,
name = <Skeleton width='100%' />,
name,
username,
etag,
customStatus = <Skeleton width='100%' />,
......@@ -45,44 +64,51 @@ const UserCard = forwardRef(function UserCard(
localTime = <Skeleton width='100%' />,
onClose,
nickname,
t = (e) => e,
},
}: UserCardProps,
ref,
) {
const t = useTranslation();
return (
<UserCardContainer className={className} ref={ref} style={style}>
<Box>
<UserAvatar username={username} etag={etag} size='x124' />
{!username ? <Skeleton width='x124' height='x124' variant='rect' /> : <UserAvatar username={username} etag={etag} size='x124' />}
{actions && (
<Box flexGrow={0} display='flex' mb='x8' align='center' justifyContent='center'>
<Box flexGrow={0} display='flex' mb='x12' alignItems='center' justifyContent='center'>
{actions}
</Box>
)}
</Box>
<Box display='flex' flexDirection='column' flexGrow={1} flexShrink={1} mis='x24' width='1px'>
<Box withTruncatedText display='flex'>
<Username status={status} name={name} title={username !== name ? username : undefined} />
<Box mbe='x4' withTruncatedText display='flex'>
{!name ? <Skeleton width='100%' /> : <UserCardUsername status={status} name={name} />}
{nickname && (
<Box title={t('Nickname')} color='hint' mis='x8' fontScale='p2' withTruncatedText>
<Box flexGrow={1} flexShrink={1} flexBasis={0} title={t('Nickname')} color='hint' mis='x4' fontScale='p2' withTruncatedText>
({nickname})
</Box>
)}
</Box>
{customStatus && (
<Info>{typeof customStatus === 'string' ? <MarkdownText content={customStatus} parseEmoji={true} /> : customStatus}</Info>
<UserCardInfo mbe='x16'>
{typeof customStatus === 'string' ? (
<MarkdownText withTruncatedText variant='inlineWithoutBreaks' content={customStatus} parseEmoji={true} />
) : (
customStatus
)}
</UserCardInfo>
)}
<Roles>{roles}</Roles>
<Info>{localTime}</Info>
<UserCardRoles>{roles}</UserCardRoles>
<UserCardInfo>{localTime}</UserCardInfo>
{bio && (
<Info withTruncatedText={false} style={clampStyle} height='x60'>
{typeof bio === 'string' ? <MarkdownText content={bio} /> : bio}
</Info>
<UserCardInfo withTruncatedText={false} className={clampStyle} height='x60'>
{typeof bio === 'string' ? <MarkdownText variant='inline' content={bio} /> : bio}
</UserCardInfo>
)}
{open && <a onClick={open}>{t('See_full_profile')}</a>}
</Box>
{onClose && (
<Box>
<ActionButton ghost icon='cross' onClick={onClose} />
<ActionButton small ghost title={t('Close')} icon='cross' onClick={onClose} />
</Box>
)}
</UserCardContainer>
......
import { ActionButton, Icon } from '@rocket.chat/fuselage';
import React, { ReactElement, ComponentProps } from 'react';
type UserCardActionProps = {
label?: string;
icon: ComponentProps<typeof Icon>['name'];
};
const UserCardAction = ({ label, icon, ...props }: UserCardActionProps): ReactElement => (
<ActionButton icon={icon} small title={label} {...props} mi='x2' />
);
export default UserCardAction;
import { Box } from '@rocket.chat/fuselage';
import React, { forwardRef } from 'react';
import React, { forwardRef, ComponentProps } from 'react';
const UserCardContainer = forwardRef(function UserCardContainer(props, ref) {
return <Box rcx-user-card bg='surface' elevation='2' p='x24' display='flex' borderRadius='x2' width='439px' {...props} ref={ref} />;
const UserCardContainer = forwardRef(function UserCardContainer(props: ComponentProps<typeof Box>, ref) {
return <Box ref={ref} rcx-user-card bg='surface' elevation='2' p='x24' display='flex' borderRadius='x2' width='439px' {...props} />;
});
export default UserCardContainer;
import { Box } from '@rocket.chat/fuselage';
import React, { ReactElement, ComponentProps } from 'react';
const UserCardInfo = (props: ComponentProps<typeof Box>): ReactElement => (
<Box mbe='x8' is='span' fontScale='p2' color='hint' withTruncatedText {...props} />
);
export default UserCardInfo;
import { Box, Tag } from '@rocket.chat/fuselage';
import React from 'react';
import React, { ReactNode, ReactElement } from 'react';
const Role = ({ children }) => (
const UserCardRole = ({ children }: { children: ReactNode }): ReactElement => (
<Box m='x2' fontScale='c2'>
<Tag disabled children={children} />
</Box>
);
export default Role;
export default UserCardRole;
import { Box } from '@rocket.chat/fuselage';
import React, { ReactNode, ReactElement } from 'react';
import UserCardInfo from './UserCardInfo';
const UserCardRoles = ({ children }: { children: ReactNode }): ReactElement => (
<Box m='neg-x2'>
<UserCardInfo flexWrap='wrap' display='flex' flexShrink={0}>
{children}
</UserCardInfo>
</Box>
);
export default UserCardRoles;
import { Box } from '@rocket.chat/fuselage';
import React, { ReactElement, ReactNode, ComponentProps } from 'react';
import * as UserStatus from '../UserStatus';
type UserCardUsernameProps = ComponentProps<typeof Box> & {
name: ReactNode;
status: ReactNode;
};
const UserCardUsername = ({ name, status = <UserStatus.Offline />, ...props }: UserCardUsernameProps): ReactElement => (
<Box
display='flex'
title={name}
flexGrow={2}
flexShrink={1}
flexBasis={0}
alignItems='center'
fontScale='h4'
color='default'
withTruncatedText
{...props}
>
{status}{' '}
<Box mis='x8' flexGrow={1} withTruncatedText>
{name}
</Box>
</Box>
);
export default UserCardUsername;
import { Box } from '@rocket.chat/fuselage';
import React from 'react';
import * as UserStatus from '../UserStatus';
const Username = ({ name, status = <UserStatus.Offline />, title, ...props }) => (
<Box {...props} display='flex' title={title} flexShrink={0} alignItems='center' fontScale='h4' color='default' withTruncatedText>
{status}{' '}
<Box mis='x8' flexGrow={1} withTruncatedText>
{name}
</Box>
</Box>
);
export default Username;
import Action from './Action';
import Info from './Info';
import Role from './Role';
import Roles from './Roles';
import UserCard from './UserCard';
import Username from './Username';
import UserCardAction from './UserCardAction';
import UserCardInfo from './UserCardInfo';
import UserCardRole from './UserCardRole';
import UserCardRoles from './UserCardRoles';
import UserCardUsername from './UserCardUsername';
export default Object.assign(UserCard, {
Action,
Role,
Roles,
Info,
Username,
Action: UserCardAction,
Role: UserCardRole,
Roles: UserCardRoles,
Info: UserCardInfo,
Username: UserCardUsername,
});
......@@ -87,7 +87,7 @@ const UserCardWithData = ({ username, onClose, target, open, rid }) => {
const actions = useMemo(() => {
const mapAction = ([key, { label, icon, action }]) => (
<UserCard.Action key={key} title={label} aria-label={label} onClick={action} icon={icon} />
<UserCard.Action key={key} label={label} aria-label={label} onClick={action} icon={icon} />
);
return [...actionsDefinition.map(mapAction), menu].filter(Boolean);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment