Restructure project (#124)

This commit is contained in:
Charles Bochet
2023-05-17 22:31:16 +02:00
committed by GitHub
parent baca6150f5
commit 434e020846
76 changed files with 295 additions and 304 deletions

View File

@ -0,0 +1,96 @@
import styled from '@emotion/styled';
import { ReactElement, useRef } from 'react';
import { useOutsideAlerter } from '../../hooks/useOutsideAlerter';
type OwnProps = {
editModeContent: ReactElement;
nonEditModeContent: ReactElement;
editModeHorizontalAlign?: 'left' | 'right';
editModeVerticalPosition?: 'over' | 'below';
isEditMode?: boolean;
onOutsideClick?: () => void;
onInsideClick?: () => void;
};
const StyledWrapper = styled.div`
position: relative;
box-sizing: border-box;
height: 32px;
display: flex;
align-items: center;
width: 100%;
`;
type StyledEditModeContainerProps = {
editModeHorizontalAlign?: 'left' | 'right';
editModeVerticalPosition?: 'over' | 'below';
};
const StyledNonEditModeContainer = styled.div`
display: flex;
align-items: center;
width: 100%;
height: 100%;
padding-left: ${(props) => props.theme.spacing(2)};
padding-right: ${(props) => props.theme.spacing(2)};
`;
const StyledEditModeContainer = styled.div<StyledEditModeContainerProps>`
display: flex;
align-items: center;
min-width: 100%;
min-height: 100%;
padding-left: ${(props) => props.theme.spacing(2)};
padding-right: ${(props) => props.theme.spacing(2)};
position: absolute;
left: ${(props) =>
props.editModeHorizontalAlign === 'right' ? 'auto' : '0'};
right: ${(props) =>
props.editModeHorizontalAlign === 'right' ? '0' : 'auto'};
top: ${(props) => (props.editModeVerticalPosition === 'over' ? '0' : '100%')};
background: ${(props) => props.theme.primaryBackground};
border: 1px solid ${(props) => props.theme.primaryBorder};
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.16);
z-index: 1;
border-radius: 4px;
backdrop-filter: blur(20px);
`;
function EditableCellWrapper({
editModeContent,
nonEditModeContent,
editModeHorizontalAlign = 'left',
editModeVerticalPosition = 'over',
isEditMode = false,
onOutsideClick,
onInsideClick,
}: OwnProps) {
const wrapperRef = useRef(null);
useOutsideAlerter(wrapperRef, () => {
onOutsideClick && onOutsideClick();
});
return (
<StyledWrapper
ref={wrapperRef}
onClick={() => {
onInsideClick && onInsideClick();
}}
>
<StyledNonEditModeContainer>
{nonEditModeContent}
</StyledNonEditModeContainer>
{isEditMode && (
<StyledEditModeContainer
editModeHorizontalAlign={editModeHorizontalAlign}
editModeVerticalPosition={editModeVerticalPosition}
>
{editModeContent}
</StyledEditModeContainer>
)}
</StyledWrapper>
);
}
export default EditableCellWrapper;

View File

@ -0,0 +1,59 @@
import styled from '@emotion/styled';
import { ChangeEvent, ComponentType, useRef, useState } from 'react';
import EditableCellWrapper from './EditableCellWrapper';
export type EditableChipProps = {
placeholder?: string;
value: string;
picture: string;
changeHandler: (updated: string) => void;
editModeHorizontalAlign?: 'left' | 'right';
ChipComponent: ComponentType<{ name: string; picture: string }>;
};
const StyledInplaceInput = styled.input`
width: 100%;
border: none;
outline: none;
&::placeholder {
font-weight: 'bold';
color: props.theme.text20;
}
`;
function EditableChip({
value,
placeholder,
changeHandler,
picture,
editModeHorizontalAlign,
ChipComponent,
}: EditableChipProps) {
const inputRef = useRef<HTMLInputElement>(null);
const [inputValue, setInputValue] = useState(value);
const [isEditMode, setIsEditMode] = useState(false);
return (
<EditableCellWrapper
onOutsideClick={() => setIsEditMode(false)}
onInsideClick={() => setIsEditMode(true)}
isEditMode={isEditMode}
editModeHorizontalAlign={editModeHorizontalAlign}
editModeContent={
<StyledInplaceInput
placeholder={placeholder || ''}
autoFocus
ref={inputRef}
value={inputValue}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
setInputValue(event.target.value);
changeHandler(event.target.value);
}}
/>
}
nonEditModeContent={<ChipComponent name={inputValue} picture={picture} />}
></EditableCellWrapper>
);
}
export default EditableChip;

View File

@ -0,0 +1,109 @@
import styled from '@emotion/styled';
import { forwardRef, useState } from 'react';
import EditableCellWrapper from './EditableCellWrapper';
import DatePicker from '../form/DatePicker';
import { CalendarContainer } from 'react-datepicker';
import { modalBackground } from '../../layout/styles/themes';
export type EditableDateProps = {
value: Date;
changeHandler: (date: Date) => void;
editModeHorizontalAlign?: 'left' | 'right';
};
const StyledContainer = styled.div`
display: flex;
align-items: center;
`;
export type StyledCalendarContainerProps = {
editModeHorizontalAlign?: 'left' | 'right';
};
const StyledCalendarContainer = styled.div<StyledCalendarContainerProps>`
position: absolute;
border: 1px solid ${(props) => props.theme.primaryBorder};
border-radius: 8px;
width: 280px;
box-shadow: 0px 3px 12px rgba(0, 0, 0, 0.09);
z-index: 1;
left: -10px;
${modalBackground};
`;
function EditableDate({
value,
changeHandler,
editModeHorizontalAlign,
}: EditableDateProps) {
const [inputValue, setInputValue] = useState(value);
const [isEditMode, setIsEditMode] = useState(false);
type DivProps = React.HTMLProps<HTMLDivElement>;
const DateDisplay = forwardRef<HTMLDivElement, DivProps>(
({ value, onClick }, ref) => (
<div onClick={onClick} ref={ref}>
{value &&
new Intl.DateTimeFormat(undefined, {
month: 'short',
day: 'numeric',
year: 'numeric',
}).format(new Date(value as string))}
</div>
),
);
interface DatePickerContainerProps {
className?: string;
children: React.ReactNode;
}
const DatePickerContainer = ({
className,
children,
}: DatePickerContainerProps) => {
return (
<StyledCalendarContainer>
<CalendarContainer className={className}>
<div style={{ position: 'relative' }}>{children}</div>
</CalendarContainer>
</StyledCalendarContainer>
);
};
return (
<EditableCellWrapper
isEditMode={isEditMode}
onOutsideClick={() => setIsEditMode(false)}
onInsideClick={() => setIsEditMode(true)}
editModeHorizontalAlign={editModeHorizontalAlign}
editModeContent={
<StyledContainer>
<DatePicker
date={inputValue}
onChangeHandler={(date: Date) => {
changeHandler(date);
setInputValue(date);
}}
customInput={<DateDisplay />}
customContainer={DatePickerContainer}
/>
</StyledContainer>
}
nonEditModeContent={
<StyledContainer>
<div>
{inputValue &&
new Intl.DateTimeFormat(undefined, {
month: 'short',
day: 'numeric',
year: 'numeric',
}).format(inputValue)}
</div>
</StyledContainer>
}
></EditableCellWrapper>
);
}
export default EditableDate;

View File

@ -0,0 +1,77 @@
import styled from '@emotion/styled';
import { ChangeEvent, useRef, useState } from 'react';
import EditableCellWrapper from './EditableCellWrapper';
import PersonChip from '../chips/PersonChip';
type OwnProps = {
firstname: string;
lastname: string;
changeHandler: (firstname: string, lastname: string) => void;
};
const StyledContainer = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
& > input:last-child {
padding-left: ${(props) => props.theme.spacing(2)};
border-left: 1px solid ${(props) => props.theme.primaryBorder};
}
`;
const StyledEditInplaceInput = styled.input`
width: 45%;
border: none;
outline: none;
height: 18px;
&::placeholder {
font-weight: bold;
color: ${(props) => props.theme.text20};
}
`;
function EditableFullName({ firstname, lastname, changeHandler }: OwnProps) {
const firstnameInputRef = useRef<HTMLInputElement>(null);
const [firstnameValue, setFirstnameValue] = useState(firstname);
const [lastnameValue, setLastnameValue] = useState(lastname);
const [isEditMode, setIsEditMode] = useState(false);
return (
<EditableCellWrapper
onOutsideClick={() => setIsEditMode(false)}
onInsideClick={() => setIsEditMode(true)}
isEditMode={isEditMode}
editModeContent={
<StyledContainer>
<StyledEditInplaceInput
autoFocus
placeholder="Firstname"
ref={firstnameInputRef}
value={firstnameValue}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
setFirstnameValue(event.target.value);
changeHandler(event.target.value, lastnameValue);
}}
/>
<StyledEditInplaceInput
autoFocus
placeholder={'Lastname'}
ref={firstnameInputRef}
value={lastnameValue}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
setLastnameValue(event.target.value);
changeHandler(firstnameValue, event.target.value);
}}
/>
</StyledContainer>
}
nonEditModeContent={
<PersonChip name={firstnameValue + ' ' + lastnameValue} />
}
></EditableCellWrapper>
);
}
export default EditableFullName;

View File

@ -0,0 +1,72 @@
import styled from '@emotion/styled';
import { ChangeEvent, MouseEvent, useRef, useState } from 'react';
import EditableCellWrapper from './EditableCellWrapper';
import { isValidPhoneNumber, parsePhoneNumber } from 'libphonenumber-js';
import Link from '../link/Link';
type OwnProps = {
placeholder?: string;
value: string;
changeHandler: (updated: string) => void;
};
type StyledEditModeProps = {
isEditMode: boolean;
};
const StyledEditInplaceInput = styled.input<StyledEditModeProps>`
width: 100%;
border: none;
outline: none;
&::placeholder {
font-weight: bold;
color: ${(props) => props.theme.text20};
}
`;
function EditablePhone({ value, placeholder, changeHandler }: OwnProps) {
const inputRef = useRef<HTMLInputElement>(null);
const [inputValue, setInputValue] = useState(value);
const [isEditMode, setIsEditMode] = useState(false);
return (
<EditableCellWrapper
isEditMode={isEditMode}
onOutsideClick={() => setIsEditMode(false)}
onInsideClick={() => setIsEditMode(true)}
editModeContent={
<StyledEditInplaceInput
autoFocus
isEditMode={isEditMode}
placeholder={placeholder || ''}
ref={inputRef}
value={inputValue}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
setInputValue(event.target.value);
changeHandler(event.target.value);
}}
/>
}
nonEditModeContent={
<div>
{isValidPhoneNumber(inputValue) ? (
<Link
href={parsePhoneNumber(inputValue, 'FR')?.getURI()}
onClick={(event: MouseEvent<HTMLElement>) => {
event.stopPropagation();
}}
>
{parsePhoneNumber(inputValue, 'FR')?.formatInternational() ||
inputValue}
</Link>
) : (
<Link href="#">{inputValue}</Link>
)}
</div>
}
/>
);
}
export default EditablePhone;

View File

@ -0,0 +1,144 @@
import { ChangeEvent, ComponentType, useState } from 'react';
import EditableCellWrapper from './EditableCellWrapper';
import styled from '@emotion/styled';
import { useSearch } from '../../services/api/search/search';
import {
SearchConfigType,
SearchableType,
} from '../../interfaces/search/interface';
const StyledEditModeContainer = styled.div`
width: 200px;
margin-left: calc(-1 * ${(props) => props.theme.spacing(2)});
margin-right: calc(-1 * ${(props) => props.theme.spacing(2)});
`;
const StyledEditModeSelectedContainer = styled.div`
height: 31px;
display: flex;
align-items: center;
padding-left: ${(props) => props.theme.spacing(2)};
padding-right: ${(props) => props.theme.spacing(2)};
`;
const StyledEditModeSearchContainer = styled.div`
height: 32px;
display: flex;
align-items: center;
border-top: 1px solid ${(props) => props.theme.primaryBorder};
`;
const StyledEditModeSearchInput = styled.input`
width: 100%;
border: none;
outline: none;
padding-left: ${(props) => props.theme.spacing(2)};
padding-right: ${(props) => props.theme.spacing(2)};
&::placeholder {
font-weight: 'bold';
color: ${(props) => props.theme.text20};
}
`;
const StyledEditModeResults = styled.div`
border-top: 1px solid ${(props) => props.theme.primaryBorder};
padding-left: ${(props) => props.theme.spacing(2)};
padding-right: ${(props) => props.theme.spacing(2)};
`;
const StyledEditModeResultItem = styled.div`
height: 32px;
display: flex;
align-items: center;
cursor: pointer;
`;
export type EditableRelationProps<
RelationType extends SearchableType,
ChipComponentPropsType,
> = {
relation?: RelationType | null;
searchPlaceholder: string;
searchConfig: SearchConfigType<RelationType>;
changeHandler: (relation: RelationType) => void;
editModeHorizontalAlign?: 'left' | 'right';
ChipComponent: ComponentType<ChipComponentPropsType>;
chipComponentPropsMapper: (
relation: RelationType,
) => ChipComponentPropsType & JSX.IntrinsicAttributes;
};
function EditableRelation<
RelationType extends SearchableType,
ChipComponentPropsType,
>({
relation,
searchPlaceholder,
searchConfig,
changeHandler,
editModeHorizontalAlign,
ChipComponent,
chipComponentPropsMapper,
}: EditableRelationProps<RelationType, ChipComponentPropsType>) {
const [selectedRelation, setSelectedRelation] = useState(relation);
const [isEditMode, setIsEditMode] = useState(false);
const [filterSearchResults, setSearchInput, setFilterSearch] =
useSearch<RelationType>();
return (
<EditableCellWrapper
editModeHorizontalAlign={editModeHorizontalAlign}
isEditMode={isEditMode}
onOutsideClick={() => setIsEditMode(false)}
onInsideClick={() => {
if (!isEditMode) {
setIsEditMode(true);
}
}}
editModeContent={
<StyledEditModeContainer>
<StyledEditModeSelectedContainer>
{selectedRelation ? (
<ChipComponent {...chipComponentPropsMapper(selectedRelation)} />
) : (
<></>
)}
</StyledEditModeSelectedContainer>
<StyledEditModeSearchContainer>
<StyledEditModeSearchInput
placeholder={searchPlaceholder}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
setFilterSearch(searchConfig);
setSearchInput(event.target.value);
}}
/>
</StyledEditModeSearchContainer>
<StyledEditModeResults>
{filterSearchResults.results &&
filterSearchResults.results.map((result, index) => (
<StyledEditModeResultItem
key={index}
onClick={() => {
setSelectedRelation(result.value);
changeHandler(result.value);
setIsEditMode(false);
}}
>
<ChipComponent {...chipComponentPropsMapper(result.value)} />
</StyledEditModeResultItem>
))}
</StyledEditModeResults>
</StyledEditModeContainer>
}
nonEditModeContent={
<div>
{selectedRelation ? (
<ChipComponent {...chipComponentPropsMapper(selectedRelation)} />
) : (
<></>
)}
</div>
}
/>
);
}
export default EditableRelation;

View File

@ -0,0 +1,66 @@
import styled from '@emotion/styled';
import { ChangeEvent, useRef, useState } from 'react';
import EditableCellWrapper from './EditableCellWrapper';
type OwnProps = {
placeholder?: string;
content: string;
changeHandler: (updated: string) => void;
editModeHorizontalAlign?: 'left' | 'right';
};
type StyledEditModeProps = {
isEditMode: boolean;
};
const StyledInplaceInput = styled.input<StyledEditModeProps>`
width: 100%;
border: none;
outline: none;
&::placeholder {
font-weight: ${(props) => (props.isEditMode ? 'bold' : 'normal')};
color: ${(props) =>
props.isEditMode ? props.theme.text20 : 'transparent'};
}
`;
const StyledNoEditText = styled.div`
width: 100%;
`;
function EditableText({
content,
placeholder,
changeHandler,
editModeHorizontalAlign,
}: OwnProps) {
const inputRef = useRef<HTMLInputElement>(null);
const [inputValue, setInputValue] = useState(content);
const [isEditMode, setIsEditMode] = useState(false);
return (
<EditableCellWrapper
isEditMode={isEditMode}
onOutsideClick={() => setIsEditMode(false)}
onInsideClick={() => setIsEditMode(true)}
editModeHorizontalAlign={editModeHorizontalAlign}
editModeContent={
<StyledInplaceInput
isEditMode={isEditMode}
placeholder={placeholder || ''}
autoFocus
ref={inputRef}
value={inputValue}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
setInputValue(event.target.value);
changeHandler(event.target.value);
}}
/>
}
nonEditModeContent={<StyledNoEditText>{inputValue}</StyledNoEditText>}
></EditableCellWrapper>
);
}
export default EditableText;

View File

@ -0,0 +1,33 @@
import EditableChip, { EditableChipProps } from '../EditableChip';
import { ThemeProvider } from '@emotion/react';
import { lightTheme } from '../../../layout/styles/themes';
import { StoryFn } from '@storybook/react';
import CompanyChip from '../../chips/CompanyChip';
const component = {
title: 'EditableChip',
component: EditableChip,
};
export default component;
const Template: StoryFn<typeof EditableChip> = (args: EditableChipProps) => {
return (
<ThemeProvider theme={lightTheme}>
<div data-testid="content-editable-parent">
<EditableChip {...args} />
</div>
</ThemeProvider>
);
};
export const EditableChipStory = Template.bind({});
EditableChipStory.args = {
ChipComponent: CompanyChip,
placeholder: 'Test',
value: 'Test',
picture: 'https://picsum.photos/200',
changeHandler: () => {
console.log('changed');
},
};

View File

@ -0,0 +1,29 @@
import EditableDate, { EditableDateProps } from '../EditableDate';
import { ThemeProvider } from '@emotion/react';
import { lightTheme } from '../../../layout/styles/themes';
import { StoryFn } from '@storybook/react';
const component = {
title: 'EditableDate',
component: EditableDate,
};
export default component;
const Template: StoryFn<typeof EditableDate> = (args: EditableDateProps) => {
return (
<ThemeProvider theme={lightTheme}>
<div data-testid="content-editable-parent">
<EditableDate {...args} />
</div>
</ThemeProvider>
);
};
export const EditableDateStory = Template.bind({});
EditableDateStory.args = {
value: new Date(),
changeHandler: () => {
console.log('changed');
},
};

View File

@ -0,0 +1,39 @@
import EditableFullName from '../EditableFullName';
import { ThemeProvider } from '@emotion/react';
import { lightTheme } from '../../../layout/styles/themes';
import { StoryFn } from '@storybook/react';
import { MemoryRouter } from 'react-router-dom';
const component = {
title: 'EditableFullName',
component: EditableFullName,
};
type OwnProps = {
firstname: string;
lastname: string;
changeHandler: (firstname: string, lastname: string) => void;
};
export default component;
const Template: StoryFn<typeof EditableFullName> = (args: OwnProps) => {
return (
<MemoryRouter>
<ThemeProvider theme={lightTheme}>
<div data-testid="content-editable-parent">
<EditableFullName {...args} />
</div>
</ThemeProvider>
</MemoryRouter>
);
};
export const EditableFullNameStory = Template.bind({});
EditableFullNameStory.args = {
firstname: 'John',
lastname: 'Doe',
changeHandler: () => {
console.log('changed');
},
};

View File

@ -0,0 +1,38 @@
import EditablePhone from '../EditablePhone';
import { ThemeProvider } from '@emotion/react';
import { lightTheme } from '../../../layout/styles/themes';
import { StoryFn } from '@storybook/react';
import { MemoryRouter } from 'react-router-dom';
const component = {
title: 'EditablePhone',
component: EditablePhone,
};
type OwnProps = {
value: string;
changeHandler: (updated: string) => void;
};
export default component;
const Template: StoryFn<typeof EditablePhone> = (args: OwnProps) => {
return (
<MemoryRouter>
<ThemeProvider theme={lightTheme}>
<div data-testid="content-editable-parent">
<EditablePhone {...args} />
</div>
</ThemeProvider>
</MemoryRouter>
);
};
export const EditablePhoneStory = Template.bind({});
EditablePhoneStory.args = {
placeholder: 'Test placeholder',
value: '+33657646543',
changeHandler: () => {
console.log('changed');
},
};

View File

@ -0,0 +1,102 @@
import EditableRelation, { EditableRelationProps } from '../EditableRelation';
import { ThemeProvider } from '@emotion/react';
import { lightTheme } from '../../../layout/styles/themes';
import { StoryFn } from '@storybook/react';
import CompanyChip, { CompanyChipPropsType } from '../../chips/CompanyChip';
import {
Company,
mapToCompany,
} from '../../../interfaces/entities/company.interface';
import { MockedProvider } from '@apollo/client/testing';
import { SEARCH_COMPANY_QUERY } from '../../../services/api/search/search';
import styled from '@emotion/styled';
import { SearchConfigType } from '../../../interfaces/search/interface';
const component = {
title: 'editable-cell/EditableRelation',
component: EditableRelation,
};
export default component;
const StyledParent = styled.div`
height: 400px;
`;
const mocks = [
{
request: {
query: SEARCH_COMPANY_QUERY,
variables: {
where: undefined,
},
},
result: {
data: {
companies: [],
},
},
},
{
request: {
query: SEARCH_COMPANY_QUERY,
variables: {
where: { name: { _ilike: '%%' } },
limit: 5,
},
},
result: {
data: {
searchResults: [
{ id: 'abnb', name: 'Airbnb', domain_name: 'abnb.com' },
],
},
},
},
];
const Template: StoryFn<
typeof EditableRelation<Company, CompanyChipPropsType>
> = (args: EditableRelationProps<Company, CompanyChipPropsType>) => {
return (
<MockedProvider mocks={mocks}>
<ThemeProvider theme={lightTheme}>
<StyledParent data-testid="content-editable-parent">
<EditableRelation<Company, CompanyChipPropsType> {...args} />
</StyledParent>
</ThemeProvider>
</MockedProvider>
);
};
export const EditableRelationStory = Template.bind({});
EditableRelationStory.args = {
relation: {
__typename: 'companies',
id: '123',
name: 'Heroku',
domain_name: 'heroku.com',
} as Company,
ChipComponent: CompanyChip,
chipComponentPropsMapper: (company: Company): CompanyChipPropsType => {
return {
name: company.name || '',
picture: company.domainName
? `https://www.google.com/s2/favicons?domain=${company.domainName}&sz=256`
: undefined,
};
},
changeHandler: (relation: Company) => {
console.log('changed', relation);
},
searchConfig: {
query: SEARCH_COMPANY_QUERY,
template: (searchInput: string) => ({
name: { _ilike: `%${searchInput}%` },
}),
resultMapper: (company) => ({
render: (company) => company.name,
value: mapToCompany(company),
}),
} satisfies SearchConfigType<Company>,
};

View File

@ -0,0 +1,35 @@
import EditableText from '../EditableText';
import { ThemeProvider } from '@emotion/react';
import { lightTheme } from '../../../layout/styles/themes';
import { StoryFn } from '@storybook/react';
const component = {
title: 'EditableText',
component: EditableText,
};
type OwnProps = {
content: string;
changeHandler: (updated: string) => void;
};
export default component;
const Template: StoryFn<typeof EditableText> = (args: OwnProps) => {
return (
<ThemeProvider theme={lightTheme}>
<div data-testid="content-editable-parent">
<EditableText {...args} />
</div>
</ThemeProvider>
);
};
export const EditableTextStory = Template.bind({});
EditableTextStory.args = {
placeholder: 'Test placeholder',
content: 'Test string',
changeHandler: () => {
console.log('changed');
},
};

View File

@ -0,0 +1,34 @@
import { fireEvent, render } from '@testing-library/react';
import { EditableChipStory } from '../__stories__/EditableChip.stories';
import CompanyChip from '../../chips/CompanyChip';
it('Checks the EditableChip editing event bubbles up', async () => {
const func = jest.fn(() => null);
const { getByTestId } = render(
<EditableChipStory
value="test"
picture="http://"
changeHandler={func}
ChipComponent={CompanyChip}
/>,
);
const parent = getByTestId('content-editable-parent');
const wrapper = parent.querySelector('div');
if (!wrapper) {
throw new Error('Editable input not found');
}
fireEvent.click(wrapper);
const editableInput = parent.querySelector('input');
if (!editableInput) {
throw new Error('Editable input not found');
}
fireEvent.change(editableInput, { target: { value: 'Test' } });
expect(func).toBeCalledWith('Test');
});

View File

@ -0,0 +1,36 @@
import { fireEvent, render, waitFor } from '@testing-library/react';
import { EditableDateStory } from '../__stories__/EditableDate.stories';
import { act } from 'react-dom/test-utils';
it('Checks the EditableDate editing event bubbles up', async () => {
const changeHandler = jest.fn(() => null);
const { getByTestId, getByText } = render(
<EditableDateStory
value={new Date('2021-03-03')}
changeHandler={changeHandler}
/>,
);
const parent = getByTestId('content-editable-parent');
const wrapper = parent.querySelector('div');
if (!wrapper) {
throw new Error('Cell Wrapper not found');
}
act(() => {
fireEvent.click(wrapper);
const dateDisplay = parent.querySelector('div');
if (!dateDisplay) {
throw new Error('Editable input not found');
}
});
waitFor(() => {
expect(getByText('March 2021')).toBeInTheDocument();
});
fireEvent.click(getByText('5'));
expect(changeHandler).toHaveBeenCalledWith(new Date('2021-03-05'));
});

View File

@ -0,0 +1,41 @@
import { fireEvent, render } from '@testing-library/react';
import { EditableFullNameStory } from '../__stories__/EditableFullName.stories';
it('Checks the EditableFullName editing event bubbles up', async () => {
const func = jest.fn(() => null);
const { getByTestId } = render(
<EditableFullNameStory
firstname="Jone"
lastname="Doe"
changeHandler={func}
/>,
);
const parent = getByTestId('content-editable-parent');
const wrapper = parent.querySelector('div');
if (!wrapper) {
throw new Error('Editable input not found');
}
fireEvent.click(wrapper);
const firstnameInput = parent.querySelector('input:first-child');
if (!firstnameInput) {
throw new Error('Editable input not found');
}
fireEvent.change(firstnameInput, { target: { value: 'Jo' } });
expect(func).toBeCalledWith('Jo', 'Doe');
const lastnameInput = parent.querySelector('input:last-child');
if (!lastnameInput) {
throw new Error('Editable input not found');
}
fireEvent.change(lastnameInput, { target: { value: 'Do' } });
expect(func).toBeCalledWith('Jo', 'Do');
});

View File

@ -0,0 +1,28 @@
import { fireEvent, render } from '@testing-library/react';
import { EditablePhoneStory } from '../__stories__/EditablePhone.stories';
it('Checks the EditablePhone editing event bubbles up', async () => {
const func = jest.fn(() => null);
const { getByTestId } = render(
<EditablePhoneStory value="+33786405315" changeHandler={func} />,
);
const parent = getByTestId('content-editable-parent');
const wrapper = parent.querySelector('div');
if (!wrapper) {
throw new Error('Editable input not found');
}
fireEvent.click(wrapper);
const editableInput = parent.querySelector('input');
if (!editableInput) {
throw new Error('Editable input not found');
}
fireEvent.change(editableInput, { target: { value: '23' } });
expect(func).toBeCalledWith('23');
});

View File

@ -0,0 +1,64 @@
import { fireEvent, render, waitFor } from '@testing-library/react';
import { EditableRelationStory } from '../__stories__/EditableRelation.stories';
import { CompanyChipPropsType } from '../../chips/CompanyChip';
import { EditableRelationProps } from '../EditableRelation';
import { act } from 'react-dom/test-utils';
import { Company } from '../../../interfaces/company.interface';
it('Checks the EditableRelation editing event bubbles up', async () => {
const func = jest.fn(() => null);
const { getByTestId, getByText } = render(
<EditableRelationStory
{...(EditableRelationStory.args as EditableRelationProps<
Company,
CompanyChipPropsType
>)}
changeHandler={func}
/>,
);
const parent = getByTestId('content-editable-parent');
const wrapper = parent.querySelector('div');
await waitFor(() => {
expect(getByText('Heroku')).toBeInTheDocument();
});
if (!wrapper) {
throw new Error('Editable relation not found');
}
fireEvent.click(wrapper);
const input = parent.querySelector('input');
if (!input) {
throw new Error('Search input not found');
}
act(() => {
fireEvent.change(input, { target: { value: 'Ai' } });
});
await waitFor(() => {
expect(getByText('Airbnb')).toBeInTheDocument();
});
act(() => {
fireEvent.click(getByText('Airbnb'));
});
await waitFor(() => {
expect(func).toBeCalledWith({
__typename: 'companies',
accountOwner: undefined,
address: undefined,
domainName: 'abnb.com',
employees: undefined,
creationDate: undefined,
id: 'abnb',
name: 'Airbnb',
pipes: [],
});
});
});

View File

@ -0,0 +1,28 @@
import { fireEvent, render } from '@testing-library/react';
import { EditableTextStory } from '../__stories__/EditableText.stories';
it('Checks the EditableText editing event bubbles up', async () => {
const func = jest.fn(() => null);
const { getByTestId } = render(
<EditableTextStory content="test" changeHandler={func} />,
);
const parent = getByTestId('content-editable-parent');
const wrapper = parent.querySelector('div');
if (!wrapper) {
throw new Error('Editable input not found');
}
fireEvent.click(wrapper);
const editableInput = parent.querySelector('input');
if (!editableInput) {
throw new Error('Editable input not found');
}
fireEvent.change(editableInput, { target: { value: '23' } });
expect(func).toBeCalledWith('23');
});