Front small ui fixes (#428)

* fix: add ellipsis in all table cells

* fix: workspace click redirect to home

* fix: add company chip story and edit comment cell story

* fix: remove cursor pointer on workspace name

* fix: snoop pill height

* fix: rebase
This commit is contained in:
Jérémy M
2023-06-27 17:56:48 +02:00
committed by GitHub
parent edee69bc07
commit c9038bb93a
15 changed files with 144 additions and 61 deletions

View File

@ -3,27 +3,14 @@ import styled from '@emotion/styled';
import { CommentChip, CommentChipProps } from './CommentChip'; import { CommentChip, CommentChipProps } from './CommentChip';
// TODO: tie those fixed values to the other components in the cell // TODO: tie those fixed values to the other components in the cell
const StyledCellWrapper = styled.div` const StyledCellWrapper = styled.div``;
position: absolute;
right: -46px;
top: 3px;
`;
const StyledCommentChipContainer = styled.div`
display: flex;
justify-content: flex-end;
position: relative;
right: 50px;
width: 50px;
`;
export function CellCommentChip(props: CommentChipProps) { export function CellCommentChip(props: CommentChipProps) {
if (props.count === 0) return null;
return ( return (
<StyledCellWrapper> <StyledCellWrapper>
<StyledCommentChipContainer> <CommentChip {...props} />
<CommentChip {...props} />
</StyledCommentChipContainer>
</StyledCellWrapper> </StyledCellWrapper>
); );
} }

View File

@ -20,19 +20,20 @@ const TestCellContainer = styled.div`
display: flex; display: flex;
height: fit-content; height: fit-content;
justify-content: flex-start; justify-content: space-between;
max-width: 250px; max-width: 250px;
min-width: 250px; min-width: 250px;
overflow: hidden; overflow: hidden;
text-wrap: nowrap; text-wrap: nowrap;
`; `;
const StyledFakeCellText = styled.div` const StyledFakeCellText = styled.div`
display: flex; overflow: hidden;
width: 100%; text-overflow: ellipsis;
white-space: nowrap;
`; `;
export const OneComment: Story = { export const OneComment: Story = {
@ -60,7 +61,7 @@ export const InCellOverlappingBlur: Story = {
render: getRenderWrapperForComponent( render: getRenderWrapperForComponent(
<TestCellContainer> <TestCellContainer>
<StyledFakeCellText> <StyledFakeCellText>
Fake long text to demonstrate blur effect Fake long text to demonstrate ellipsis
</StyledFakeCellText> </StyledFakeCellText>
<CellCommentChip count={12} /> <CellCommentChip count={12} />
</TestCellContainer>, </TestCellContainer>,

View File

@ -15,6 +15,7 @@ const StyledContainer = styled.span`
display: inline-flex; display: inline-flex;
gap: ${({ theme }) => theme.spacing(1)}; gap: ${({ theme }) => theme.spacing(1)};
height: calc(20px - 2 * ${({ theme }) => theme.spacing(1)}); height: calc(20px - 2 * ${({ theme }) => theme.spacing(1)});
overflow: hidden;
padding: ${({ theme }) => theme.spacing(1)}; padding: ${({ theme }) => theme.spacing(1)};
@ -31,6 +32,12 @@ const StyledContainer = styled.span`
} }
`; `;
const StyledName = styled.span`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;
function CompanyChip({ name, picture }: CompanyChipPropsType) { function CompanyChip({ name, picture }: CompanyChipPropsType) {
return ( return (
<StyledContainer data-testid="company-chip"> <StyledContainer data-testid="company-chip">
@ -42,7 +49,7 @@ function CompanyChip({ name, picture }: CompanyChipPropsType) {
size={14} size={14}
/> />
)} )}
{name} <StyledName>{name}</StyledName>
</StyledContainer> </StyledContainer>
); );
} }

View File

@ -0,0 +1,52 @@
import styled from '@emotion/styled';
import type { Meta, StoryObj } from '@storybook/react';
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
import CompanyChip from '../CompanyChip';
const meta: Meta<typeof CompanyChip> = {
title: 'Modules/Companies/CompanyChip',
component: CompanyChip,
};
export default meta;
type Story = StoryObj<typeof CompanyChip>;
const TestCellContainer = styled.div`
align-items: center;
background: ${({ theme }) => theme.background.primary};
display: flex;
height: fit-content;
justify-content: space-between;
max-width: 250px;
min-width: 250px;
overflow: hidden;
text-wrap: nowrap;
`;
export const SmallName: Story = {
render: getRenderWrapperForComponent(
<TestCellContainer>
<CompanyChip
name="Instragram"
picture="https://api.faviconkit.com/instagram.com/144"
/>
</TestCellContainer>,
),
};
export const BigName: Story = {
render: getRenderWrapperForComponent(
<TestCellContainer>
<CompanyChip
name="Google with a real big name to overflow the cell"
picture="https://api.faviconkit.com/google.com/144"
/>
</TestCellContainer>,
),
};

View File

@ -13,13 +13,17 @@ type OwnProps = {
onChange: (firstname: string, lastname: string) => void; onChange: (firstname: string, lastname: string) => void;
}; };
const StyledDiv = styled.div` const NoEditModeContainer = styled.div`
align-items: center; align-items: center;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
width: 100%; width: 100%;
`; `;
const RightContainer = styled.div`
margin-left: ${(props) => props.theme.spacing(1)};
`;
export function EditablePeopleFullName({ person, onChange }: OwnProps) { export function EditablePeopleFullName({ person, onChange }: OwnProps) {
const [firstnameValue, setFirstnameValue] = useState(person.firstname ?? ''); const [firstnameValue, setFirstnameValue] = useState(person.firstname ?? '');
const [lastnameValue, setLastnameValue] = useState(person.lastname ?? ''); const [lastnameValue, setLastnameValue] = useState(person.lastname ?? '');
@ -55,15 +59,15 @@ export function EditablePeopleFullName({ person, onChange }: OwnProps) {
secondValuePlaceholder="Last name" secondValuePlaceholder="Last name"
onChange={handleDoubleTextChange} onChange={handleDoubleTextChange}
nonEditModeContent={ nonEditModeContent={
<> <NoEditModeContainer>
<StyledDiv> <PersonChip name={person.firstname + ' ' + person.lastname} />
<PersonChip name={person.firstname + ' ' + person.lastname} /> <RightContainer>
</StyledDiv> <CellCommentChip
<CellCommentChip count={person._commentCount ?? 0}
count={person._commentCount ?? 0} onClick={handleCommentClick}
onClick={handleCommentClick} />
/> </RightContainer>
</> </NoEditModeContainer>
} }
/> />
); );

View File

@ -34,6 +34,12 @@ const StyledContainer = styled.span`
white-space: nowrap; white-space: nowrap;
`; `;
const StyledName = styled.span`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;
export function PersonChip({ name, picture }: PersonChipPropsType) { export function PersonChip({ name, picture }: PersonChipPropsType) {
return ( return (
<StyledContainer data-testid="person-chip"> <StyledContainer data-testid="person-chip">
@ -42,7 +48,7 @@ export function PersonChip({ name, picture }: PersonChipPropsType) {
src={picture ? picture.toString() : PersonPlaceholder.toString()} src={picture ? picture.toString() : PersonPlaceholder.toString()}
alt="person" alt="person"
/> />
{name} <StyledName>{name}</StyledName>
</StyledContainer> </StyledContainer>
); );
} }

View File

@ -6,8 +6,8 @@ export const EditableCellNormalModeOuterContainer = styled.div`
display: flex; display: flex;
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
padding-left: ${({ theme }) => theme.spacing(2)};
padding-left: ${({ theme }) => theme.spacing(2)};
padding-right: ${({ theme }) => theme.spacing(1)}; padding-right: ${({ theme }) => theme.spacing(1)};
width: 100%; width: 100%;

View File

@ -11,7 +11,11 @@ export type EditableChipProps = {
picture: string; picture: string;
changeHandler: (updated: string) => void; changeHandler: (updated: string) => void;
editModeHorizontalAlign?: 'left' | 'right'; editModeHorizontalAlign?: 'left' | 'right';
ChipComponent: ComponentType<{ name: string; picture: string }>; ChipComponent: ComponentType<{
name: string;
picture: string;
isOverlapped?: boolean;
}>;
commentCount?: number; commentCount?: number;
onCommentClick?: (event: React.MouseEvent<HTMLDivElement>) => void; onCommentClick?: (event: React.MouseEvent<HTMLDivElement>) => void;
rightEndContents?: ReactNode[]; rightEndContents?: ReactNode[];
@ -24,11 +28,17 @@ const StyledInplaceInput = styled.input`
${textInputStyle} ${textInputStyle}
`; `;
const StyledInplaceShow = styled.div` const NoEditModeContainer = styled.div`
align-items: center;
display: flex; display: flex;
justify-content: space-between;
width: 100%; width: 100%;
`; `;
const RightContainer = styled.div`
margin-left: ${(props) => props.theme.spacing(1)};
`;
function EditableChip({ function EditableChip({
value, value,
placeholder, placeholder,
@ -67,20 +77,20 @@ function EditableChip({
/> />
} }
nonEditModeContent={ nonEditModeContent={
<> <NoEditModeContainer>
<StyledInplaceShow> <ChipComponent name={inputValue} picture={picture} />
<ChipComponent name={inputValue} picture={picture} /> <RightContainer>
</StyledInplaceShow> {rightEndContents &&
{rightEndContents && rightEndContents.length > 0 &&
rightEndContents.length > 0 && rightEndContents.map((content, index) => (
rightEndContents.map((content, index) => ( <div key={index} onClick={handleRightEndContentClick}>
<div key={index} onClick={handleRightEndContentClick}> {content}
{content} </div>
</div> ))}
))} </RightContainer>
</> </NoEditModeContainer>
} }
></EditableCell> />
); );
} }

View File

@ -17,6 +17,16 @@ type StyledEditModeProps = {
isEditMode: boolean; isEditMode: boolean;
}; };
const StyledRawLink = styled(RawLink)`
overflow: hidden;
a {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
`;
// TODO: refactor // TODO: refactor
const StyledEditInplaceInput = styled.input<StyledEditModeProps>` const StyledEditInplaceInput = styled.input<StyledEditModeProps>`
margin: 0; margin: 0;
@ -48,9 +58,9 @@ export function EditablePhone({ value, placeholder, changeHandler }: OwnProps) {
/> />
} }
nonEditModeContent={ nonEditModeContent={
<div> <>
{isValidPhoneNumber(inputValue) ? ( {isValidPhoneNumber(inputValue) ? (
<RawLink <StyledRawLink
href={parsePhoneNumber(inputValue, 'FR')?.getURI()} href={parsePhoneNumber(inputValue, 'FR')?.getURI()}
onClick={(event: MouseEvent<HTMLElement>) => { onClick={(event: MouseEvent<HTMLElement>) => {
event.stopPropagation(); event.stopPropagation();
@ -58,11 +68,11 @@ export function EditablePhone({ value, placeholder, changeHandler }: OwnProps) {
> >
{parsePhoneNumber(inputValue, 'FR')?.formatInternational() || {parsePhoneNumber(inputValue, 'FR')?.formatInternational() ||
inputValue} inputValue}
</RawLink> </StyledRawLink>
) : ( ) : (
<RawLink href="#">{inputValue}</RawLink> <StyledRawLink href="#">{inputValue}</StyledRawLink>
)} )}
</div> </>
} }
/> />
); );

View File

@ -24,6 +24,9 @@ const StyledInplaceInput = styled.input<StyledEditModeProps>`
`; `;
const StyledNoEditText = styled.div` const StyledNoEditText = styled.div`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%; width: 100%;
`; `;

View File

@ -3,6 +3,7 @@ import { Link as ReactLink } from 'react-router-dom';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
type OwnProps = { type OwnProps = {
className?: string;
href: string; href: string;
children?: React.ReactNode; children?: React.ReactNode;
onClick?: (event: React.MouseEvent<HTMLElement>) => void; onClick?: (event: React.MouseEvent<HTMLElement>) => void;
@ -17,9 +18,9 @@ const StyledClickable = styled.div`
} }
`; `;
export function RawLink({ href, children, onClick }: OwnProps) { export function RawLink({ className, href, children, onClick }: OwnProps) {
return ( return (
<StyledClickable> <StyledClickable className={className}>
<ReactLink onClick={onClick} to={href}> <ReactLink onClick={onClick} to={href}>
{children} {children}
</ReactLink> </ReactLink>

View File

@ -70,9 +70,10 @@ const StyledSoonPill = styled.div`
align-items: center; align-items: center;
border-radius: 50px; border-radius: 50px;
background-color: ${({ theme }) => theme.background.transparent.light}; background-color: ${({ theme }) => theme.background.transparent.light};
font-size: ${({ theme }) => theme.font.size.xs}; font-size: ${({ theme }) => theme.font.size.xxs};
padding: ${({ theme }) => theme.spacing(1)} ${({ theme }) => theme.spacing(2)} height: 16px;
${({ theme }) => theme.spacing(1)} ${({ theme }) => theme.spacing(2)}; padding-left: ${({ theme }) => theme.spacing(2)};
padding-right: ${({ theme }) => theme.spacing(2)};
margin-left: auto; // this aligns the pill to the right margin-left: auto; // this aligns the pill to the right
`; `;

View File

@ -22,7 +22,6 @@ const StyledContainer = styled.div`
const LogoAndNameContainer = styled.div` const LogoAndNameContainer = styled.div`
align-items: center; align-items: center;
cursor: pointer;
display: flex; display: flex;
`; `;

View File

@ -2,6 +2,7 @@ import { grayScale } from './colors';
const common = { const common = {
size: { size: {
xxs: '0.625rem',
xs: '0.85rem', xs: '0.85rem',
sm: '0.92rem', sm: '0.92rem',
md: '1rem', md: '1rem',

View File

@ -22,6 +22,7 @@ export const StyledAvatar = styled.div<Omit<OwnProps, 'placeholder'>>`
border-radius: ${(props) => (props.type === 'rounded' ? '50%' : '2px')}; border-radius: ${(props) => (props.type === 'rounded' ? '50%' : '2px')};
color: ${({ theme }) => theme.font.color.primary}; color: ${({ theme }) => theme.font.color.primary};
display: flex; display: flex;
flex-shrink: 0;
font-size: ${({ theme }) => theme.font.size.sm}; font-size: ${({ theme }) => theme.font.size.sm};
font-weight: ${({ theme }) => theme.font.weight.medium}; font-weight: ${({ theme }) => theme.font.weight.medium};