Reorganize frontend and install Craco to alias modules (#190)

This commit is contained in:
Charles Bochet
2023-06-04 11:23:09 +02:00
committed by GitHub
parent bbc80cd543
commit 7b858fd7c9
149 changed files with 3441 additions and 1158 deletions

View File

@ -0,0 +1,19 @@
import styled from '@emotion/styled';
import { CommentChip, CommentChipProps } from './CommentChip';
const StyledCellWrapper = styled.div`
position: relative;
right: 38px;
top: -13px;
width: 0;
height: 0;
`;
export function CellCommentChip(props: CommentChipProps) {
return (
<StyledCellWrapper>
<CommentChip {...props} />
</StyledCellWrapper>
);
}

View File

@ -0,0 +1,58 @@
import styled from '@emotion/styled';
import { IconComment } from '@/ui/icons';
export type CommentChipProps = {
count: number;
onClick?: (event: React.MouseEvent<HTMLDivElement>) => void;
};
const StyledChip = styled.div`
height: 26px;
min-width: 34px;
padding-left: 2px;
padding-right: 2px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: row;
gap: 2px;
background: ${(props) => props.theme.secondaryBackgroundTransparent};
backdrop-filter: blur(6px);
border-radius: ${(props) => props.theme.borderRadius};
cursor: pointer;
color: ${(props) => props.theme.text30};
&:hover {
background: ${(props) => props.theme.tertiaryBackground};
color: ${(props) => props.theme.text40};
}
user-select: none;
`;
const StyledCount = styled.div`
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 500;
`;
export function CommentChip({ count, onClick }: CommentChipProps) {
const formattedCount = count > 99 ? '99+' : count;
return (
<StyledChip onClick={onClick}>
<StyledCount>{formattedCount}</StyledCount>
<IconComment size={12} />
</StyledChip>
);
}

View File

@ -0,0 +1,123 @@
import { useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { HotkeysEvent } from 'react-hotkeys-hook/dist/types';
import { HiArrowSmRight } from 'react-icons/hi';
import TextareaAutosize from 'react-textarea-autosize';
import styled from '@emotion/styled';
import { IconButton } from '@/ui/components/buttons/IconButton';
type OwnProps = {
onSend?: (text: string) => void;
placeholder?: string;
};
const StyledContainer = styled.div`
display: flex;
min-height: 32px;
width: 100%;
`;
const StyledTextArea = styled(TextareaAutosize)`
width: 100%;
padding: 8px;
font-size: 13px;
font-family: inherit;
font-weight: 400;
line-height: 16px;
border: none;
border-radius: 5px;
background: ${(props) => props.theme.tertiaryBackground};
color: ${(props) => props.theme.text80};
overflow: auto;
resize: none;
&:focus {
outline: none;
border: none;
}
&::placeholder {
color: ${(props) => props.theme.text30};
font-weight: 400;
}
`;
const StyledBottomRightIconButton = styled.div`
width: 0px;
position: relative;
top: calc(100% - 26.5px);
right: 26px;
`;
export function CommentTextInput({ placeholder, onSend }: OwnProps) {
const [text, setText] = useState('');
const isSendButtonDisabled = !text;
useHotkeys(
['shift+enter', 'enter'],
(event: KeyboardEvent, handler: HotkeysEvent) => {
if (handler.shift) {
return;
} else {
event.preventDefault();
onSend?.(text);
setText('');
}
},
{
enableOnContentEditable: true,
enableOnFormTags: true,
},
[onSend],
);
useHotkeys(
'esc',
(event: KeyboardEvent, handler: HotkeysEvent) => {
event.preventDefault();
setText('');
},
{
enableOnContentEditable: true,
enableOnFormTags: true,
},
[onSend],
);
function handleInputChange(event: React.FormEvent<HTMLTextAreaElement>) {
const newText = event.currentTarget.value;
setText(newText);
}
function handleOnClickSendButton() {
onSend?.(text);
setText('');
}
return (
<>
<StyledContainer>
<StyledTextArea
placeholder={placeholder || 'Write something...'}
maxRows={5}
onChange={handleInputChange}
value={text}
/>
<StyledBottomRightIconButton>
<IconButton
onClick={handleOnClickSendButton}
icon={<HiArrowSmRight size={15} />}
disabled={isSendButtonDisabled}
/>
</StyledBottomRightIconButton>
</StyledContainer>
</>
);
}

View File

@ -0,0 +1,31 @@
import { useRecoilState } from 'recoil';
import { RightDrawerBody } from '@/ui/layout/right-drawer/components/RightDrawerBody';
import { RightDrawerPage } from '@/ui/layout/right-drawer/components/RightDrawerPage';
import { RightDrawerTopBar } from '@/ui/layout/right-drawer/components/RightDrawerTopBar';
import { commentableEntityArrayState } from '../../states/commentableEntityArrayState';
import { CommentTextInput } from './CommentTextInput';
export function RightDrawerComments() {
const [commentableEntityArray] = useRecoilState(commentableEntityArrayState);
function handleSendComment(text: string) {
console.log(text);
}
return (
<RightDrawerPage>
<RightDrawerTopBar title="Comments" />
<RightDrawerBody>
{commentableEntityArray.map((commentableEntity) => (
<div key={commentableEntity.id}>
{commentableEntity.type} - {commentableEntity.id}
</div>
))}
<CommentTextInput onSend={handleSendComment} />
</RightDrawerBody>
</RightDrawerPage>
);
}

View File

@ -0,0 +1,68 @@
import styled from '@emotion/styled';
import type { Meta, StoryObj } from '@storybook/react';
import { CellBaseContainer } from '@/ui/components/editable-cell/CellBaseContainer';
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
import { CellCommentChip } from '../CellCommentChip';
import { CommentChip } from '../CommentChip';
const meta: Meta<typeof CellCommentChip> = {
title: 'Components/CellCommentChip',
component: CellCommentChip,
};
export default meta;
type Story = StoryObj<typeof CellCommentChip>;
const TestCellContainer = styled.div`
display: flex;
align-items: center;
justify-content: flex-end;
min-width: 250px;
height: fit-content;
background: ${(props) => props.theme.primaryBackground};
`;
const StyledFakeCellText = styled.div`
display: flex;
width: 100%;
`;
export const OneComment: Story = {
render: getRenderWrapperForComponent(<CommentChip count={1} />),
};
export const TenComments: Story = {
render: getRenderWrapperForComponent(<CommentChip count={10} />),
};
export const TooManyComments: Story = {
render: getRenderWrapperForComponent(<CommentChip count={1000} />),
};
export const InCellDefault: Story = {
render: getRenderWrapperForComponent(
<TestCellContainer>
<CellBaseContainer>
<StyledFakeCellText>Fake short text</StyledFakeCellText>
<CellCommentChip count={12} />
</CellBaseContainer>
</TestCellContainer>,
),
};
export const InCellOverlappingBlur: Story = {
render: getRenderWrapperForComponent(
<TestCellContainer>
<CellBaseContainer>
<StyledFakeCellText>
Fake long text to demonstrate blur effect
</StyledFakeCellText>
<CellCommentChip count={12} />
</CellBaseContainer>
</TestCellContainer>,
),
};

View File

@ -0,0 +1,32 @@
import type { Meta, StoryObj } from '@storybook/react';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
import { CommentTextInput } from '../CommentTextInput';
const meta: Meta<typeof CommentTextInput> = {
title: 'Components/CommentTextInput',
component: CommentTextInput,
argTypes: {
onSend: {
action: 'onSend',
},
},
};
export default meta;
type Story = StoryObj<typeof CommentTextInput>;
export const Default: Story = {
render: getRenderWrapperForComponent(<CommentTextInput />),
parameters: {
msw: graphqlMocks,
actions: { argTypesRegex: '^on.*' },
},
args: {
onSend: (text: string) => {
console.log(text);
},
},
};