Feature/edit name from show page (#806)
* Enable company name edition from page * Enable editing persons as well * Add styling for titles * Better manage style with inheritance * Add stories for poeple editable fields * Remove failing test * Revert "Remove failing test" This reverts commit 02cdeeba64276a26f93cf4af94f5857e47d36fff. * Fix test * Update name * Fix location * Rename tests * Fix stories
This commit is contained in:
@ -0,0 +1,63 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
||||||
|
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
|
||||||
|
import { InplaceInputText } from '@/ui/inplace-input/components/InplaceInputText';
|
||||||
|
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
|
||||||
|
import { Company, useUpdateOneCompanyMutation } from '~/generated/graphql';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
company: Pick<Company, 'id' | 'name'>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function CompanyNameEditableField({ company }: OwnProps) {
|
||||||
|
const [internalValue, setInternalValue] = useState(company.name);
|
||||||
|
|
||||||
|
const [updateCompany] = useUpdateOneCompanyMutation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setInternalValue(company.name);
|
||||||
|
}, [company.name]);
|
||||||
|
|
||||||
|
async function handleChange(newValue: string) {
|
||||||
|
setInternalValue(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
await updateCompany({
|
||||||
|
variables: {
|
||||||
|
where: {
|
||||||
|
id: company.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
name: internalValue ?? '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleCancel() {
|
||||||
|
setInternalValue(company.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RecoilScope SpecificContext={FieldContext}>
|
||||||
|
<EditableField
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
editModeContent={
|
||||||
|
<InplaceInputText
|
||||||
|
placeholder={'Name'}
|
||||||
|
autoFocus
|
||||||
|
value={internalValue}
|
||||||
|
onChange={(newValue: string) => {
|
||||||
|
handleChange(newValue);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
displayModeContent={internalValue ?? ''}
|
||||||
|
isDisplayModeContentEmpty={!(internalValue !== '')}
|
||||||
|
/>
|
||||||
|
</RecoilScope>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
|
import { mockedPeopleData } from '~/testing/mock-data/people';
|
||||||
|
|
||||||
|
import { PeopleCompanyEditableField } from '../../editable-field/components/PeopleCompanyEditableField';
|
||||||
|
|
||||||
|
const meta: Meta<typeof PeopleCompanyEditableField> = {
|
||||||
|
title: 'Modules/People/EditableFields/PeopleCompanyEditableField',
|
||||||
|
component: PeopleCompanyEditableField,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof PeopleCompanyEditableField>;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
render: () => (
|
||||||
|
<BrowserRouter>
|
||||||
|
<PeopleCompanyEditableField people={mockedPeopleData[0]} />
|
||||||
|
</BrowserRouter>
|
||||||
|
),
|
||||||
|
};
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
|
import { mockedPeopleData } from '~/testing/mock-data/people';
|
||||||
|
|
||||||
|
import { PeopleFullNameEditableField } from '../../editable-field/components/PeopleFullNameEditableField';
|
||||||
|
|
||||||
|
const meta: Meta<typeof PeopleFullNameEditableField> = {
|
||||||
|
title: 'Modules/People/EditableFields/PeopleFullNameEditableField',
|
||||||
|
component: PeopleFullNameEditableField,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof PeopleFullNameEditableField>;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
render: () => <PeopleFullNameEditableField people={mockedPeopleData[0]} />,
|
||||||
|
};
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
||||||
|
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
|
||||||
|
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
|
||||||
|
import { Person, useUpdateOnePersonMutation } from '~/generated/graphql';
|
||||||
|
|
||||||
|
import { InplaceInputDoubleText } from '../../../ui/inplace-input/components/InplaceInputDoubleText';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
people: Pick<Person, 'id' | 'firstName' | 'lastName'>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function PeopleFullNameEditableField({ people }: OwnProps) {
|
||||||
|
const [internalValueFirstName, setInternalValueFirstName] = useState(
|
||||||
|
people.firstName,
|
||||||
|
);
|
||||||
|
const [internalValueLastName, setInternalValueLastName] = useState(
|
||||||
|
people.lastName,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [updatePeople] = useUpdateOnePersonMutation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setInternalValueFirstName(people.firstName);
|
||||||
|
setInternalValueLastName(people.lastName);
|
||||||
|
}, [people.firstName, people.lastName]);
|
||||||
|
|
||||||
|
async function handleChange(
|
||||||
|
newValueFirstName: string,
|
||||||
|
newValueLastName: string,
|
||||||
|
) {
|
||||||
|
setInternalValueFirstName(newValueFirstName);
|
||||||
|
setInternalValueLastName(newValueLastName);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
await updatePeople({
|
||||||
|
variables: {
|
||||||
|
where: {
|
||||||
|
id: people.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
firstName: internalValueFirstName ?? '',
|
||||||
|
lastName: internalValueLastName ?? '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleCancel() {
|
||||||
|
setInternalValueFirstName(people.firstName);
|
||||||
|
setInternalValueLastName(people.lastName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RecoilScope SpecificContext={FieldContext}>
|
||||||
|
<EditableField
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
editModeContent={
|
||||||
|
<InplaceInputDoubleText
|
||||||
|
firstValuePlaceholder={'First name'}
|
||||||
|
secondValuePlaceholder={'Last name'}
|
||||||
|
firstValue={internalValueFirstName ?? ''}
|
||||||
|
secondValue={internalValueLastName ?? ''}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
displayModeContent={`${internalValueFirstName} ${internalValueLastName}`}
|
||||||
|
isDisplayModeContentEmpty={
|
||||||
|
!(internalValueFirstName !== '') && !(internalValueLastName !== '')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</RecoilScope>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -51,8 +51,8 @@ export const EditableFieldNormalModeOuterContainer = styled.div<
|
|||||||
export const EditableFieldNormalModeInnerContainer = styled.div`
|
export const EditableFieldNormalModeInnerContainer = styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: ${({ theme }) => theme.font.color.primary};
|
color: ${({ theme }) => theme.font.color.primary};
|
||||||
font-size: ${({ theme }) => theme.font.size.md};
|
font-size: 'inherit';
|
||||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
font-weight: 'inherit';
|
||||||
|
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,51 @@
|
|||||||
|
import { ChangeEvent } from 'react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import { InplaceInputTextEditMode } from '@/ui/inplace-input/components/InplaceInputTextEditMode';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
firstValue: string;
|
||||||
|
secondValue: string;
|
||||||
|
firstValuePlaceholder: string;
|
||||||
|
secondValuePlaceholder: string;
|
||||||
|
onChange: (firstValue: string, secondValue: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const StyledContainer = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
& > input:last-child {
|
||||||
|
border-left: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||||
|
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export function InplaceInputDoubleText({
|
||||||
|
firstValue,
|
||||||
|
secondValue,
|
||||||
|
firstValuePlaceholder,
|
||||||
|
secondValuePlaceholder,
|
||||||
|
onChange,
|
||||||
|
}: OwnProps) {
|
||||||
|
return (
|
||||||
|
<StyledContainer>
|
||||||
|
<InplaceInputTextEditMode
|
||||||
|
autoFocus
|
||||||
|
placeholder={firstValuePlaceholder}
|
||||||
|
value={firstValue}
|
||||||
|
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
onChange(event.target.value, secondValue);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<InplaceInputTextEditMode
|
||||||
|
placeholder={secondValuePlaceholder}
|
||||||
|
value={secondValue}
|
||||||
|
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
onChange(firstValue, event.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</StyledContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -16,6 +16,7 @@ type OwnProps = {
|
|||||||
logoOrAvatar?: string;
|
logoOrAvatar?: string;
|
||||||
title: string;
|
title: string;
|
||||||
date: string;
|
date: string;
|
||||||
|
renderTitleEditComponent?: () => JSX.Element;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledShowPageSummaryCard = styled.div`
|
const StyledShowPageSummaryCard = styled.div`
|
||||||
@ -45,7 +46,6 @@ const StyledTitle = styled.div`
|
|||||||
color: ${({ theme }) => theme.font.color.primary};
|
color: ${({ theme }) => theme.font.color.primary};
|
||||||
font-size: ${({ theme }) => theme.font.size.xl};
|
font-size: ${({ theme }) => theme.font.size.xl};
|
||||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||||
|
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -65,6 +65,7 @@ export function ShowPageSummaryCard({
|
|||||||
logoOrAvatar,
|
logoOrAvatar,
|
||||||
title,
|
title,
|
||||||
date,
|
date,
|
||||||
|
renderTitleEditComponent,
|
||||||
}: OwnProps) {
|
}: OwnProps) {
|
||||||
const beautifiedCreatedAt =
|
const beautifiedCreatedAt =
|
||||||
date !== '' ? beautifyPastDateRelativeToNow(date) : '';
|
date !== '' ? beautifyPastDateRelativeToNow(date) : '';
|
||||||
@ -82,7 +83,11 @@ export function ShowPageSummaryCard({
|
|||||||
/>
|
/>
|
||||||
<StyledInfoContainer>
|
<StyledInfoContainer>
|
||||||
<StyledTitle>
|
<StyledTitle>
|
||||||
<OverflowingTextWithTooltip text={title} />
|
{renderTitleEditComponent ? (
|
||||||
|
renderTitleEditComponent()
|
||||||
|
) : (
|
||||||
|
<OverflowingTextWithTooltip text={title} />
|
||||||
|
)}
|
||||||
</StyledTitle>
|
</StyledTitle>
|
||||||
<StyledDate id={dateElementId}>
|
<StyledDate id={dateElementId}>
|
||||||
Added {beautifiedCreatedAt} ago
|
Added {beautifiedCreatedAt} ago
|
||||||
|
|||||||
@ -15,9 +15,8 @@ export const textInputStyle = (props: { theme: ThemeType }) =>
|
|||||||
border: none;
|
border: none;
|
||||||
color: ${props.theme.font.color.primary};
|
color: ${props.theme.font.color.primary};
|
||||||
font-family: ${props.theme.font.family};
|
font-family: ${props.theme.font.family};
|
||||||
font-size: ${props.theme.font.size.md};
|
font-size: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
font-weight: ${props.theme.font.weight.regular};
|
|
||||||
outline: none;
|
outline: none;
|
||||||
padding: ${props.theme.spacing(0)} ${props.theme.spacing(2)};
|
padding: ${props.theme.spacing(0)} ${props.theme.spacing(2)};
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,8 @@ import { ShowPageSummaryCard } from '@/ui/layout/show-page/components/ShowPageSu
|
|||||||
import { CommentableType } from '~/generated/graphql';
|
import { CommentableType } from '~/generated/graphql';
|
||||||
import { getLogoUrlFromDomainName } from '~/utils';
|
import { getLogoUrlFromDomainName } from '~/utils';
|
||||||
|
|
||||||
|
import { CompanyNameEditableField } from '../../modules/companies/editable-field/components/CompanyNameEditableField';
|
||||||
|
|
||||||
export function CompanyShow() {
|
export function CompanyShow() {
|
||||||
const companyId = useParams().companyId ?? '';
|
const companyId = useParams().companyId ?? '';
|
||||||
|
|
||||||
@ -39,6 +41,9 @@ export function CompanyShow() {
|
|||||||
logoOrAvatar={getLogoUrlFromDomainName(company?.domainName ?? '')}
|
logoOrAvatar={getLogoUrlFromDomainName(company?.domainName ?? '')}
|
||||||
title={company?.name ?? 'No name'}
|
title={company?.name ?? 'No name'}
|
||||||
date={company?.createdAt ?? ''}
|
date={company?.createdAt ?? ''}
|
||||||
|
renderTitleEditComponent={() => (
|
||||||
|
<CompanyNameEditableField company={company} />
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
<PropertyBox extraPadding={true}>
|
<PropertyBox extraPadding={true}>
|
||||||
<CompanyDomainNameEditableField company={company} />
|
<CompanyDomainNameEditableField company={company} />
|
||||||
|
|||||||
@ -11,6 +11,8 @@ import { ShowPageRightContainer } from '@/ui/layout/show-page/components/ShowPag
|
|||||||
import { ShowPageSummaryCard } from '@/ui/layout/show-page/components/ShowPageSummaryCard';
|
import { ShowPageSummaryCard } from '@/ui/layout/show-page/components/ShowPageSummaryCard';
|
||||||
import { CommentableType } from '~/generated/graphql';
|
import { CommentableType } from '~/generated/graphql';
|
||||||
|
|
||||||
|
import { PeopleFullNameEditableField } from '../../modules/people/editable-field/components/PeopleFullNameEditableField';
|
||||||
|
|
||||||
export function PersonShow() {
|
export function PersonShow() {
|
||||||
const personId = useParams().personId ?? '';
|
const personId = useParams().personId ?? '';
|
||||||
|
|
||||||
@ -31,6 +33,9 @@ export function PersonShow() {
|
|||||||
id={person?.id}
|
id={person?.id}
|
||||||
title={person?.displayName ?? 'No name'}
|
title={person?.displayName ?? 'No name'}
|
||||||
date={person?.createdAt ?? ''}
|
date={person?.createdAt ?? ''}
|
||||||
|
renderTitleEditComponent={() =>
|
||||||
|
person ? <PeopleFullNameEditableField people={person} /> : <></>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
{person && <PersonPropertyBox person={person} />}
|
{person && <PersonPropertyBox person={person} />}
|
||||||
</ShowPageLeftContainer>
|
</ShowPageLeftContainer>
|
||||||
|
|||||||
Reference in New Issue
Block a user