Lucas/t 223 i can add comments to companies or people using the right (#181)

* wip

* Implemented comment input text component

* Improved behavior
This commit is contained in:
Lucas Bordeau
2023-06-01 19:49:37 +02:00
committed by GitHub
parent f906533b29
commit bf3097500a
13 changed files with 339 additions and 6 deletions

View File

@ -0,0 +1,36 @@
import styled from '@emotion/styled';
const StyledIconButton = styled.button`
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
padding: 0;
border: none;
border-radius: 50%;
background: ${(props) => props.theme.text80};
color: ${(props) => props.theme.text100};
transition: color 0.1s ease-in-out, background 0.1s ease-in-out;
background: ${(props) => props.theme.blue};
color: ${(props) => props.theme.text0};
cursor: pointer;
&:disabled {
background: ${(props) => props.theme.quadraryBackground};
color: ${(props) => props.theme.text80};
cursor: default;
}
`;
export function IconButton({
icon,
...props
}: { icon: React.ReactNode } & React.ButtonHTMLAttributes<HTMLButtonElement>) {
return <StyledIconButton {...props}>{icon}</StyledIconButton>;
}

View File

@ -0,0 +1,122 @@
import styled from '@emotion/styled';
import { useState } from 'react';
import { HiArrowSmRight } from 'react-icons/hi';
import TextareaAutosize from 'react-textarea-autosize';
import { IconButton } from '../buttons/IconButton';
import { useHotkeys } from 'react-hotkeys-hook';
import { HotkeysEvent } from 'react-hotkeys-hook/dist/types';
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

@ -1,9 +1,19 @@
import { RightDrawerBody } from '../../layout/right-drawer/RightDrawerBody';
import { RightDrawerPage } from '../../layout/right-drawer/RightDrawerPage';
import { RightDrawerTopBar } from '../../layout/right-drawer/RightDrawerTopBar';
import { CommentTextInput } from './CommentTextInput';
export function RightDrawerComments() {
function handleSendComment(text: string) {
console.log(text);
}
return (
<>
<RightDrawerPage>
<RightDrawerTopBar title="Comments" />
</>
<RightDrawerBody>
<CommentTextInput onSend={handleSendComment} />
</RightDrawerBody>
</RightDrawerPage>
);
}

View File

@ -0,0 +1,30 @@
import type { Meta, StoryObj } from '@storybook/react';
import { graphqlMocks } from '../../../testing/graphqlMocks';
import { CommentTextInput } from '../CommentTextInput';
import { getRenderWrapperForComponent } from '../../../testing/renderWrappers';
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);
},
},
};

View File

@ -0,0 +1,8 @@
import styled from '@emotion/styled';
export const RightDrawerBody = styled.div`
display: flex;
flex-direction: column;
padding: 8px;
`;

View File

@ -0,0 +1,8 @@
import styled from '@emotion/styled';
export const RightDrawerPage = styled.div`
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
`;

View File

@ -12,7 +12,6 @@ const StyledRightDrawerTopBar = styled.div`
font-size: 13px;
color: ${(props) => props.theme.text60};
border-bottom: 1px solid ${(props) => props.theme.lightBorder};
width: 100%;
`;
const StyledTopBarTitle = styled.div`

View File

@ -27,6 +27,8 @@ const lightThemeSpecific = {
primaryBackground: '#fff',
secondaryBackground: '#fcfcfc',
tertiaryBackground: '#f5f5f5',
quadraryBackground: '#ebebeb',
pinkBackground: '#ffe5f4',
greenBackground: '#e6fff2',
purpleBackground: '#e0e0ff',
@ -64,6 +66,8 @@ const darkThemeSpecific: typeof lightThemeSpecific = {
primaryBackground: '#141414',
secondaryBackground: '#171717',
tertiaryBackground: '#333333',
quadraryBackground: '#444444',
pinkBackground: '#cc0078',
greenBackground: '#1e7e50',
purpleBackground: '#1111b7',

View File

@ -2,7 +2,8 @@ import type { Meta, StoryObj } from '@storybook/react';
import Companies from '../Companies';
import { render, mocks } from './shared';
import { getRenderWrapperForPage } from '../../../testing/renderWrappers';
import { graphqlMocks } from '../../../testing/graphqlMocks';
const meta: Meta<typeof Companies> = {
title: 'Pages/Companies',
@ -14,8 +15,8 @@ export default meta;
export type Story = StoryObj<typeof Companies>;
export const Default: Story = {
render,
render: getRenderWrapperForPage(<Companies />),
parameters: {
msw: mocks,
msw: graphqlMocks,
},
};

View File

@ -0,0 +1,21 @@
import styled from '@emotion/styled';
const StyledLayout = styled.div`
display: flex;
flex-direction: row;
width: fit-content;
height: fit-content;
padding: 20px;
background: ${(props) => props.theme.primaryBackground};
border-radius: 5px;
box-shadow: 0px 0px 2px;
`;
type OwnProps = {
children: JSX.Element;
};
export function ComponentStorybookLayout({ children }: OwnProps) {
return <StyledLayout>{children}</StyledLayout>;
}

View File

@ -0,0 +1,39 @@
import { MemoryRouter } from 'react-router-dom';
import { FullHeightStorybookLayout } from './FullHeightStorybookLayout';
import { ThemeProvider } from '@emotion/react';
import { ApolloProvider } from '@apollo/client';
import { RecoilRoot } from 'recoil';
import { mockedClient } from './mockedClient';
import { lightTheme } from '../layout/styles/themes';
import React from 'react';
import { ComponentStorybookLayout } from './ComponentStorybookLayout';
export function getRenderWrapperForPage(children: React.ReactElement) {
return function render() {
return (
<RecoilRoot>
<ApolloProvider client={mockedClient}>
<ThemeProvider theme={lightTheme}>
<MemoryRouter>
<FullHeightStorybookLayout>{children}</FullHeightStorybookLayout>
</MemoryRouter>
</ThemeProvider>
</ApolloProvider>
</RecoilRoot>
);
};
}
export function getRenderWrapperForComponent(children: React.ReactElement) {
return function render() {
return (
<RecoilRoot>
<ApolloProvider client={mockedClient}>
<ThemeProvider theme={lightTheme}>
<ComponentStorybookLayout>{children}</ComponentStorybookLayout>
</ThemeProvider>
</ApolloProvider>
</RecoilRoot>
);
};
}