Migrate to a monorepo structure (#2909)
This commit is contained in:
@ -0,0 +1,38 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { Comment as CommentType } from '@/activities/types/Comment';
|
||||
|
||||
import { CommentHeader } from './CommentHeader';
|
||||
|
||||
type CommentProps = {
|
||||
comment: CommentType;
|
||||
actionBar?: React.ReactNode;
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledCommentBody = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
|
||||
line-height: ${({ theme }) => theme.text.lineHeight.md};
|
||||
overflow-wrap: anywhere;
|
||||
|
||||
padding-left: 24px;
|
||||
|
||||
text-align: left;
|
||||
`;
|
||||
|
||||
export const Comment = ({ comment, actionBar }: CommentProps) => (
|
||||
<StyledContainer>
|
||||
<CommentHeader comment={comment} actionBar={actionBar} />
|
||||
<StyledCommentBody>{comment.body}</StyledCommentBody>
|
||||
</StyledContainer>
|
||||
);
|
||||
@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { IconComment } from '@/ui/display/icon';
|
||||
|
||||
const StyledCommentIcon = styled.div`
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const CommentCounter = ({ commentCount }: { commentCount: number }) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<div>
|
||||
{commentCount > 0 && (
|
||||
<StyledCommentIcon>
|
||||
<IconComment size={theme.icon.size.md} />
|
||||
{commentCount}
|
||||
</StyledCommentIcon>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CommentCounter;
|
||||
@ -0,0 +1,101 @@
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { Comment } from '@/activities/types/Comment';
|
||||
import { Avatar } from '@/users/components/Avatar';
|
||||
import {
|
||||
beautifyExactDateTime,
|
||||
beautifyPastDateRelativeToNow,
|
||||
} from '~/utils/date-utils';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
||||
justify-content: space-between;
|
||||
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
width: calc(100% - ${({ theme }) => theme.spacing(1)});
|
||||
`;
|
||||
|
||||
const StyledLeftContainer = styled.div`
|
||||
align-items: end;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledName = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
max-width: 160px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const StyledDate = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
margin-left: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledTooltip = styled(Tooltip)`
|
||||
background-color: ${({ theme }) => theme.background.primary};
|
||||
|
||||
box-shadow: 0px 2px 4px 3px
|
||||
${({ theme }) => theme.background.transparent.light};
|
||||
|
||||
box-shadow: 2px 4px 16px 6px
|
||||
${({ theme }) => theme.background.transparent.light};
|
||||
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
|
||||
opacity: 1;
|
||||
padding: 8px;
|
||||
`;
|
||||
|
||||
type CommentHeaderProps = {
|
||||
comment: Pick<Comment, 'id' | 'author' | 'createdAt'>;
|
||||
actionBar?: React.ReactNode;
|
||||
};
|
||||
|
||||
export const CommentHeader = ({ comment, actionBar }: CommentHeaderProps) => {
|
||||
const beautifiedCreatedAt = beautifyPastDateRelativeToNow(comment.createdAt);
|
||||
const exactCreatedAt = beautifyExactDateTime(comment.createdAt);
|
||||
const showDate = beautifiedCreatedAt !== '';
|
||||
|
||||
const author = comment.author;
|
||||
const authorName = author.name.firstName + ' ' + author.name.lastName;
|
||||
const avatarUrl = author.avatarUrl;
|
||||
const commentId = comment.id;
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledLeftContainer>
|
||||
<Avatar
|
||||
avatarUrl={avatarUrl}
|
||||
size="md"
|
||||
colorId={author.id}
|
||||
placeholder={authorName}
|
||||
/>
|
||||
<StyledName>{authorName}</StyledName>
|
||||
{showDate && (
|
||||
<>
|
||||
<StyledDate id={`id-${commentId}`}>
|
||||
{beautifiedCreatedAt}
|
||||
</StyledDate>
|
||||
<StyledTooltip
|
||||
anchorSelect={`#id-${commentId}`}
|
||||
content={exactCreatedAt}
|
||||
clickable
|
||||
noArrow
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</StyledLeftContainer>
|
||||
<div>{actionBar}</div>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,33 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
|
||||
import { ActivityActionBar } from '../../right-drawer/components/ActivityActionBar';
|
||||
import { Comment } from '../Comment';
|
||||
|
||||
import { mockComment, mockCommentWithLongValues } from './mock-comment';
|
||||
|
||||
const meta: Meta<typeof Comment> = {
|
||||
title: 'Modules/Activity/Comment/Comment',
|
||||
component: Comment,
|
||||
decorators: [ComponentDecorator],
|
||||
argTypes: {
|
||||
actionBar: {
|
||||
type: 'boolean',
|
||||
mapping: {
|
||||
true: <ActivityActionBar activityId="test-id" />,
|
||||
false: undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
args: { comment: mockComment },
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Comment>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
||||
export const WithLongValues: Story = {
|
||||
args: { comment: mockCommentWithLongValues },
|
||||
};
|
||||
@ -0,0 +1,87 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { ActivityActionBar } from '@/activities/right-drawer/components/ActivityActionBar';
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
import { avatarUrl } from '~/testing/mock-data/users';
|
||||
|
||||
import { CommentHeader } from '../CommentHeader';
|
||||
|
||||
import { mockComment, mockCommentWithLongValues } from './mock-comment';
|
||||
|
||||
const meta: Meta<typeof CommentHeader> = {
|
||||
title: 'Modules/Activity/Comment/CommentHeader',
|
||||
component: CommentHeader,
|
||||
decorators: [ComponentDecorator],
|
||||
argTypes: {
|
||||
actionBar: {
|
||||
type: 'boolean',
|
||||
mapping: {
|
||||
true: <ActivityActionBar activityId="test-id" />,
|
||||
false: undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
args: { comment: mockComment },
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof CommentHeader>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
||||
export const FewHoursAgo: Story = {
|
||||
args: {
|
||||
comment: {
|
||||
...mockComment,
|
||||
createdAt: DateTime.now().minus({ hours: 2 }).toISO() ?? '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const FewDaysAgo: Story = {
|
||||
args: {
|
||||
comment: {
|
||||
...mockComment,
|
||||
createdAt: DateTime.now().minus({ days: 2 }).toISO() ?? '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const FewMonthsAgo: Story = {
|
||||
args: {
|
||||
comment: {
|
||||
...mockComment,
|
||||
createdAt: DateTime.now().minus({ months: 2 }).toISO() ?? '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const FewYearsAgo: Story = {
|
||||
args: {
|
||||
comment: {
|
||||
...mockComment,
|
||||
createdAt: DateTime.now().minus({ years: 2 }).toISO() ?? '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithAvatar: Story = {
|
||||
args: {
|
||||
comment: {
|
||||
...mockComment,
|
||||
author: {
|
||||
...mockComment.author,
|
||||
avatarUrl,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithLongUserName: Story = {
|
||||
args: { comment: mockCommentWithLongValues },
|
||||
};
|
||||
|
||||
export const WithActionBar: Story = {
|
||||
args: { actionBar: true },
|
||||
};
|
||||
@ -0,0 +1,41 @@
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { Comment } from '@/activities/types/Comment';
|
||||
|
||||
export const mockComment: Pick<
|
||||
Comment,
|
||||
'id' | 'author' | 'createdAt' | 'body' | 'updatedAt' | 'activityId'
|
||||
> = {
|
||||
id: 'fake_comment_1_uuid',
|
||||
body: 'Hello, this is a comment.',
|
||||
author: {
|
||||
id: 'fake_comment_1_author_uuid',
|
||||
name: {
|
||||
firstName: 'Jony' ?? '',
|
||||
lastName: 'Ive' ?? '',
|
||||
},
|
||||
avatarUrl: null,
|
||||
},
|
||||
createdAt: DateTime.fromFormat('2021-03-12', 'yyyy-MM-dd').toISO() ?? '',
|
||||
updatedAt: DateTime.fromFormat('2021-03-13', 'yyyy-MM-dd').toISO() ?? '',
|
||||
activityId: 'fake_activity_1_uuid',
|
||||
};
|
||||
|
||||
export const mockCommentWithLongValues: Pick<
|
||||
Comment,
|
||||
'id' | 'author' | 'createdAt' | 'body' | 'updatedAt' | 'activityId'
|
||||
> = {
|
||||
id: 'fake_comment_2_uuid',
|
||||
body: 'Hello, this is a comment. Hello, this is a comment. Hello, this is a comment. Hello, this is a comment. Hello, this is a comment. Hello, this is a comment.',
|
||||
author: {
|
||||
id: 'fake_comment_1_author_uuid',
|
||||
name: {
|
||||
firstName: 'Jony' ?? '',
|
||||
lastName: 'Ive' ?? '',
|
||||
},
|
||||
avatarUrl: null,
|
||||
},
|
||||
createdAt: DateTime.fromFormat('2021-03-12', 'yyyy-MM-dd').toISO() ?? '',
|
||||
updatedAt: DateTime.fromFormat('2021-03-13', 'yyyy-MM-dd').toISO() ?? '',
|
||||
activityId: 'fake_activity_1_uuid',
|
||||
};
|
||||
Reference in New Issue
Block a user