Lucas/t 366 on comment drawer when i have comments on the selected (#201)
* Fixed right drawer width and shared in theme * Added date packages and tooltip * Added date utils and tests * Added comment thread components * Fixed comment chip * Fix from rebase * Fix from rebase * Fix margin right * Fixed CSS and graphql
This commit is contained in:
@ -4,7 +4,7 @@ import { CommentChip, CommentChipProps } from './CommentChip';
|
||||
|
||||
const StyledCellWrapper = styled.div`
|
||||
position: relative;
|
||||
right: 38px;
|
||||
right: 34px;
|
||||
top: -13px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
@ -9,18 +9,18 @@ export type CommentChipProps = {
|
||||
|
||||
const StyledChip = styled.div`
|
||||
height: 26px;
|
||||
min-width: 34px;
|
||||
width: fit-content;
|
||||
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: row;
|
||||
gap: 2px;
|
||||
gap: 4px;
|
||||
|
||||
background: ${(props) => props.theme.secondaryBackgroundTransparent};
|
||||
background: ${(props) => props.theme.primaryBackgroundTransparent};
|
||||
backdrop-filter: blur(6px);
|
||||
|
||||
border-radius: ${(props) => props.theme.borderRadius};
|
||||
@ -53,7 +53,7 @@ export function CommentChip({ count, onClick }: CommentChipProps) {
|
||||
return (
|
||||
<StyledChip data-testid="comment-chip" onClick={onClick}>
|
||||
<StyledCount>{formattedCount}</StyledCount>
|
||||
<IconComment size={12} />
|
||||
<IconComment size={16} />
|
||||
</StyledChip>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,80 @@
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { UserAvatar } from '@/users/components/UserAvatar';
|
||||
import {
|
||||
beautifyExactDate,
|
||||
beautifyPastDateRelativeToNow,
|
||||
} from '@/utils/datetime/date-utils';
|
||||
|
||||
type OwnProps = {
|
||||
avatarUrl: string | null | undefined;
|
||||
username: string;
|
||||
createdAt: Date;
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
flex-direction: row;
|
||||
|
||||
justify-content: flex-start;
|
||||
|
||||
padding: ${(props) => props.theme.spacing(1)};
|
||||
|
||||
gap: ${(props) => props.theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledName = styled.div`
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
color: ${(props) => props.theme.text80};
|
||||
`;
|
||||
|
||||
const StyledDate = styled.div`
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: ${(props) => props.theme.text30};
|
||||
|
||||
padding-top: 1.5px;
|
||||
|
||||
margin-left: ${(props) => props.theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledTooltip = styled(Tooltip)`
|
||||
padding: 8px;
|
||||
`;
|
||||
|
||||
export function CommentHeader({ avatarUrl, username, createdAt }: OwnProps) {
|
||||
const beautifiedCreatedAt = beautifyPastDateRelativeToNow(createdAt);
|
||||
const exactCreatedAt = beautifyExactDate(createdAt);
|
||||
const showDate = beautifiedCreatedAt !== '';
|
||||
|
||||
const capitalizedFirstUsernameLetter =
|
||||
username !== '' ? username.toLocaleUpperCase()[0] : '';
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<UserAvatar
|
||||
avatarUrl={avatarUrl}
|
||||
size={16}
|
||||
placeholderLetter={capitalizedFirstUsernameLetter}
|
||||
/>
|
||||
<StyledName>{username}</StyledName>
|
||||
{showDate && (
|
||||
<>
|
||||
<StyledDate className="comment-created-at">
|
||||
{beautifiedCreatedAt}
|
||||
</StyledDate>
|
||||
<StyledTooltip
|
||||
anchorSelect=".comment-created-at"
|
||||
content={exactCreatedAt}
|
||||
clickable
|
||||
noArrow
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@ -1,27 +1,37 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { CommentThreadForDrawer } from '@/comments/types/CommentThreadForDrawer';
|
||||
|
||||
import { CommentTextInput } from './CommentTextInput';
|
||||
import { CommentThreadItem } from './CommentThreadItem';
|
||||
|
||||
type OwnProps = {
|
||||
commentThread: CommentThreadForDrawer;
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
flex-direction: column;
|
||||
|
||||
justify-content: flex-start;
|
||||
|
||||
gap: ${(props) => props.theme.spacing(4)};
|
||||
padding: ${(props) => props.theme.spacing(2)};
|
||||
`;
|
||||
|
||||
export function CommentThread({ commentThread }: OwnProps) {
|
||||
function handleSendComment(text: string) {
|
||||
console.log(text);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<StyledContainer>
|
||||
{commentThread.comments?.map((comment) => (
|
||||
<div key={comment.id}>
|
||||
<div>
|
||||
{comment.author?.displayName} - {comment.createdAt}
|
||||
</div>
|
||||
<div>{comment.body}</div>
|
||||
</div>
|
||||
<CommentThreadItem key={comment.id} comment={comment} />
|
||||
))}
|
||||
<CommentTextInput onSend={handleSendComment} />
|
||||
</div>
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { CommentForDrawer } from '@/comments/types/CommentForDrawer';
|
||||
|
||||
import { CommentHeader } from './CommentHeader';
|
||||
|
||||
type OwnProps = {
|
||||
comment: CommentForDrawer;
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
gap: ${(props) => props.theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledCommentBody = styled.div`
|
||||
font-size: ${(props) => props.theme.fontSizeMedium};
|
||||
line-height: 19.5px;
|
||||
|
||||
text-align: left;
|
||||
padding-left: 24px;
|
||||
|
||||
color: ${(props) => props.theme.text60};
|
||||
`;
|
||||
|
||||
export function CommentThreadItem({ comment }: OwnProps) {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<CommentHeader
|
||||
avatarUrl={comment.author.avatarUrl}
|
||||
username={comment.author.displayName}
|
||||
createdAt={comment.createdAt}
|
||||
/>
|
||||
<StyledCommentBody>{comment.body}</StyledCommentBody>
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@ -7,7 +7,7 @@ import { CellCommentChip } from '../CellCommentChip';
|
||||
import { CommentChip } from '../CommentChip';
|
||||
|
||||
const meta: Meta<typeof CellCommentChip> = {
|
||||
title: 'Components/CellCommentChip',
|
||||
title: 'Components/Comments/CellCommentChip',
|
||||
component: CellCommentChip,
|
||||
};
|
||||
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { mockedUsersData } from '~/testing/mock-data/users';
|
||||
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
|
||||
|
||||
import { CommentHeader } from '../CommentHeader';
|
||||
|
||||
const meta: Meta<typeof CommentHeader> = {
|
||||
title: 'Components/Comments/CommentHeader',
|
||||
component: CommentHeader,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof CommentHeader>;
|
||||
|
||||
const mockUser = mockedUsersData[0];
|
||||
|
||||
export const Default: Story = {
|
||||
render: getRenderWrapperForComponent(
|
||||
<CommentHeader
|
||||
avatarUrl={mockUser.avatarUrl ?? ''}
|
||||
username={mockUser.displayName ?? ''}
|
||||
createdAt={DateTime.now().minus({ hours: 2 }).toJSDate()}
|
||||
/>,
|
||||
),
|
||||
};
|
||||
|
||||
export const FewDaysAgo: Story = {
|
||||
render: getRenderWrapperForComponent(
|
||||
<CommentHeader
|
||||
avatarUrl={mockUser.avatarUrl ?? ''}
|
||||
username={mockUser.displayName ?? ''}
|
||||
createdAt={DateTime.now().minus({ days: 2 }).toJSDate()}
|
||||
/>,
|
||||
),
|
||||
};
|
||||
|
||||
export const FewMonthsAgo: Story = {
|
||||
render: getRenderWrapperForComponent(
|
||||
<CommentHeader
|
||||
avatarUrl={mockUser.avatarUrl ?? ''}
|
||||
username={mockUser.displayName ?? ''}
|
||||
createdAt={DateTime.now().minus({ months: 2 }).toJSDate()}
|
||||
/>,
|
||||
),
|
||||
};
|
||||
|
||||
export const FewYearsAgo: Story = {
|
||||
render: getRenderWrapperForComponent(
|
||||
<CommentHeader
|
||||
avatarUrl={mockUser.avatarUrl ?? ''}
|
||||
username={mockUser.displayName ?? ''}
|
||||
createdAt={DateTime.now().minus({ years: 2 }).toJSDate()}
|
||||
/>,
|
||||
),
|
||||
};
|
||||
|
||||
export const WithoutAvatar: Story = {
|
||||
render: getRenderWrapperForComponent(
|
||||
<CommentHeader
|
||||
avatarUrl={''}
|
||||
username={mockUser.displayName ?? ''}
|
||||
createdAt={DateTime.now().minus({ hours: 2 }).toJSDate()}
|
||||
/>,
|
||||
),
|
||||
};
|
||||
@ -6,7 +6,7 @@ import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
|
||||
import { CommentTextInput } from '../CommentTextInput';
|
||||
|
||||
const meta: Meta<typeof CommentTextInput> = {
|
||||
title: 'Components/CommentTextInput',
|
||||
title: 'Components/Comments/CommentTextInput',
|
||||
component: CommentTextInput,
|
||||
argTypes: {
|
||||
onSend: {
|
||||
|
||||
Reference in New Issue
Block a user