fix: Handling filename overflow in mobile viewports (#7364)

Fixes #7330
Fixes https://github.com/twentyhq/twenty/issues/7516 

<div style="display: flex">
<img style="max-width:50%"
src="https://github.com/user-attachments/assets/51027a9d-8745-4cc7-9f17-4000e3615e44"/>
<img style="max-width:50%"
src="https://github.com/user-attachments/assets/827f69ba-c581-402f-9498-6b1a4dde7b69"/>
</div>

---------

Co-authored-by: sid0-0 <a@b.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
sid0-0
2024-10-10 13:42:38 +05:30
committed by GitHub
parent 97ab0481e4
commit 7b7c67fb64
10 changed files with 109 additions and 102 deletions

View File

@@ -0,0 +1,16 @@
import { Card } from '@/ui/layout/card/components/Card';
import styled from '@emotion/styled';
const StyledList = styled(Card)`
& > :not(:last-child) {
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
}
width: calc(100% - 2px);
overflow: auto;
`;
export const ActivityList = ({ children }: React.PropsWithChildren) => {
return <StyledList>{children}</StyledList>;
};

View File

@@ -0,0 +1,35 @@
import { CardContent } from '@/ui/layout/card/components/CardContent';
import styled from '@emotion/styled';
import React from 'react';
const StyledRowContent = styled(CardContent)<{
clickable?: boolean;
}>`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(2)};
height: ${({ theme }) => theme.spacing(12)};
padding: ${({ theme }) => theme.spacing(0, 4)};
cursor: ${({ clickable }) => (clickable === true ? 'pointer' : 'default')};
`;
export const ActivityRow = ({
children,
onClick,
disabled,
}: React.PropsWithChildren<{
onClick?: (event: React.MouseEvent<HTMLDivElement>) => void;
disabled?: boolean;
}>) => {
const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
if (disabled !== true) {
onClick?.(event);
}
};
return (
<StyledRowContent onClick={handleClick} clickable={disabled !== true}>
{children}
</StyledRowContent>
);
};

View File

@@ -1,30 +1,15 @@
import styled from '@emotion/styled';
import { useRef } from 'react';
import { useRecoilCallback } from 'recoil';
import { Avatar, GRAY_SCALE } from 'twenty-ui';
import { ActivityRow } from '@/activities/components/ActivityRow';
import { EmailThreadNotShared } from '@/activities/emails/components/EmailThreadNotShared';
import { useEmailThread } from '@/activities/emails/hooks/useEmailThread';
import { emailThreadIdWhenEmailThreadWasClosedState } from '@/activities/emails/states/lastViewableEmailThreadIdState';
import { CardContent } from '@/ui/layout/card/components/CardContent';
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
import { MessageChannelVisibility, TimelineThread } from '~/generated/graphql';
import { formatToHumanReadableDate } from '~/utils/date-utils';
const StyledCardContent = styled(CardContent)<{
visibility: MessageChannelVisibility;
}>`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(2)};
height: ${({ theme }) => theme.spacing(12)};
padding: ${({ theme }) => theme.spacing(0, 4)};
cursor: ${({ visibility }) =>
visibility === MessageChannelVisibility.ShareEverything
? 'pointer'
: 'default'};
`;
const StyledHeading = styled.div<{ unread: boolean }>`
display: flex;
overflow: hidden;
@@ -82,16 +67,10 @@ const StyledReceivedAt = styled.div`
`;
type EmailThreadPreviewProps = {
divider?: boolean;
thread: TimelineThread;
};
export const EmailThreadPreview = ({
divider,
thread,
}: EmailThreadPreviewProps) => {
const cardRef = useRef<HTMLDivElement>(null);
export const EmailThreadPreview = ({ thread }: EmailThreadPreviewProps) => {
const { openEmailThread } = useEmailThread();
const visibility = thread.visibility;
@@ -143,12 +122,12 @@ export const EmailThreadPreview = ({
],
);
const isDisabled = visibility !== MessageChannelVisibility.ShareEverything;
return (
<StyledCardContent
ref={cardRef}
<ActivityRow
onClick={(event) => handleThreadClick(event)}
divider={divider}
visibility={visibility}
disabled={isDisabled}
>
<StyledHeading unread={!thread.read}>
<StyledParticipantsContainer>
@@ -201,6 +180,6 @@ export const EmailThreadPreview = ({
<StyledReceivedAt>
{formatToHumanReadableDate(thread.lastMessageReceivedAt)}
</StyledReceivedAt>
</StyledCardContent>
</ActivityRow>
);
};

View File

@@ -1,6 +1,7 @@
import styled from '@emotion/styled';
import { H1Title, H1TitleFontColor } from 'twenty-ui';
import { ActivityList } from '@/activities/components/ActivityList';
import { CustomResolverFetchMoreLoader } from '@/activities/components/CustomResolverFetchMoreLoader';
import { SkeletonLoader } from '@/activities/components/SkeletonLoader';
import { EmailThreadPreview } from '@/activities/emails/components/EmailThreadPreview';
@@ -18,7 +19,6 @@ import {
AnimatedPlaceholderEmptyTitle,
EMPTY_PLACEHOLDER_TRANSITION_PROPS,
} from '@/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled';
import { Card } from '@/ui/layout/card/components/Card';
import { Section } from '@/ui/layout/section/components/Section';
import { TimelineThread, TimelineThreadsWithTotal } from '~/generated/graphql';
@@ -106,15 +106,11 @@ export const EmailThreads = ({
fontColor={H1TitleFontColor.Primary}
/>
{!firstQueryLoading && (
<Card>
{timelineThreads?.map((thread: TimelineThread, index: number) => (
<EmailThreadPreview
key={index}
divider={index < timelineThreads.length - 1}
thread={thread}
/>
<ActivityList>
{timelineThreads?.map((thread: TimelineThread) => (
<EmailThreadPreview key={thread.id} thread={thread} />
))}
</Card>
</ActivityList>
)}
<CustomResolverFetchMoreLoader
loading={isFetchingMore || firstQueryLoading}

View File

@@ -6,6 +6,7 @@ import { useUploadAttachmentFile } from '@/activities/files/hooks/useUploadAttac
import { Attachment } from '@/activities/files/types/Attachment';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { ActivityList } from '@/activities/components/ActivityList';
import { AttachmentRow } from './AttachmentRow';
type AttachmentListProps = {
@@ -22,6 +23,9 @@ const StyledContainer = styled.div`
flex-direction: column;
justify-content: center;
padding: ${({ theme }) => theme.spacing(2, 6, 6)};
width: calc(100% - ${({ theme }) => theme.spacing(12)});
height: 100%;
`;
@@ -44,21 +48,11 @@ const StyledCount = styled.span`
margin-left: ${({ theme }) => theme.spacing(2)};
`;
const StyledAttachmentContainer = styled.div`
align-items: flex-start;
align-self: stretch;
background: ${({ theme }) => theme.background.secondary};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.border.radius.md};
display: flex;
flex-flow: column nowrap;
justify-content: center;
width: 100%;
`;
const StyledDropZoneContainer = styled.div`
height: 100%;
width: 100%;
overflow: auto;
`;
export const AttachmentList = ({
@@ -91,11 +85,11 @@ export const AttachmentList = ({
onUploadFile={onUploadFile}
/>
) : (
<StyledAttachmentContainer>
<ActivityList>
{attachments.map((attachment) => (
<AttachmentRow key={attachment.id} attachment={attachment} />
))}
</StyledAttachmentContainer>
</ActivityList>
)}
</StyledDropZoneContainer>
</StyledContainer>

View File

@@ -1,3 +1,4 @@
import { ActivityRow } from '@/activities/components/ActivityRow';
import { AttachmentDropdown } from '@/activities/files/components/AttachmentDropdown';
import { AttachmentIcon } from '@/activities/files/components/AttachmentIcon';
import { Attachment } from '@/activities/files/types/Attachment';
@@ -13,26 +14,19 @@ import { TextInput } from '@/ui/input/components/TextInput';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useMemo, useState } from 'react';
import { IconCalendar } from 'twenty-ui';
import { IconCalendar, OverflowingTextWithTooltip } from 'twenty-ui';
import { formatToHumanReadableDate } from '~/utils/date-utils';
import { getFileAbsoluteURI } from '~/utils/file/getFileAbsoluteURI';
const StyledRow = styled.div`
align-items: center;
align-self: stretch;
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
color: ${({ theme }) => theme.font.color.primary};
display: flex;
justify-content: space-between;
padding: ${({ theme }) => theme.spacing(2)};
height: 32px;
`;
const StyledLeftContent = styled.div`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(3)};
width: 100%;
overflow: auto;
flex: 1;
`;
const StyledRightContent = styled.div`
@@ -52,11 +46,19 @@ const StyledLink = styled.a`
color: ${({ theme }) => theme.font.color.primary};
display: flex;
text-decoration: none;
width: 100%;
:hover {
color: ${({ theme }) => theme.font.color.secondary};
}
`;
const StyledLinkContainer = styled.div`
overflow: auto;
width: 100%;
`;
export const AttachmentRow = ({ attachment }: { attachment: Attachment }) => {
const theme = useTheme();
const [isEditing, setIsEditing] = useState(false);
@@ -97,7 +99,7 @@ export const AttachmentRow = ({ attachment }: { attachment: Attachment }) => {
return (
<FieldContext.Provider value={fieldContext as GenericFieldContextType}>
<StyledRow>
<ActivityRow disabled>
<StyledLeftContent>
<AttachmentIcon attachmentType={attachment.type} />
{isEditing ? (
@@ -109,12 +111,14 @@ export const AttachmentRow = ({ attachment }: { attachment: Attachment }) => {
fullWidth
/>
) : (
<StyledLink
href={getFileAbsoluteURI(attachment.fullPath)}
target="__blank"
>
{attachment.name}
</StyledLink>
<StyledLinkContainer>
<StyledLink
href={getFileAbsoluteURI(attachment.fullPath)}
target="__blank"
>
<OverflowingTextWithTooltip text={attachment.name} />
</StyledLink>
</StyledLinkContainer>
)}
</StyledLeftContent>
<StyledRightContent>
@@ -131,7 +135,7 @@ export const AttachmentRow = ({ attachment }: { attachment: Attachment }) => {
onRename={handleRename}
/>
</StyledRightContent>
</StyledRow>
</ActivityRow>
</FieldContext.Provider>
);
};

View File

@@ -1,6 +1,7 @@
import styled from '@emotion/styled';
import { ReactElement } from 'react';
import { ActivityList } from '@/activities/components/ActivityList';
import { Task } from '@/activities/types/Task';
import { TaskRow } from './TaskRow';
@@ -17,7 +18,9 @@ const StyledContainer = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
padding: 8px 24px;
padding: 8px ${({ theme }) => theme.spacing(6)};
width: calc(100% - ${({ theme }) => theme.spacing(12)});
`;
const StyledTitleBar = styled.div`
@@ -39,13 +42,6 @@ const StyledCount = styled.span`
margin-left: ${({ theme }) => theme.spacing(2)};
`;
const StyledTaskRows = styled.div`
background-color: ${({ theme }) => theme.background.secondary};
border: 1px solid ${({ theme }) => theme.border.color.light};
border-radius: ${({ theme }) => theme.border.radius.md};
width: 100%;
`;
export const TaskList = ({ title, tasks, button }: TaskListProps) => (
<>
{tasks && tasks.length > 0 && (
@@ -58,11 +54,11 @@ export const TaskList = ({ title, tasks, button }: TaskListProps) => (
)}
{button}
</StyledTitleBar>
<StyledTaskRows>
<ActivityList>
{tasks.map((task) => (
<TaskRow key={task.id} task={task} />
))}
</StyledTaskRows>
</ActivityList>
</StyledContainer>
)}
</>

View File

@@ -8,28 +8,12 @@ import { getActivitySummary } from '@/activities/utils/getActivitySummary';
import { Checkbox, CheckboxShape } from '@/ui/input/components/Checkbox';
import { beautifyExactDate, hasDatePassed } from '~/utils/date-utils';
import { ActivityRow } from '@/activities/components/ActivityRow';
import { Task } from '@/activities/types/Task';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useFieldContext } from '@/object-record/hooks/useFieldContext';
import { useCompleteTask } from '../hooks/useCompleteTask';
const StyledContainer = styled.div`
align-items: center;
justify-content: space-between;
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
cursor: pointer;
display: flex;
height: ${({ theme }) => theme.spacing(12)};
min-width: calc(100% - ${({ theme }) => theme.spacing(8)});
max-width: calc(100% - ${({ theme }) => theme.spacing(8)});
padding: 0 ${({ theme }) => theme.spacing(4)};
overflow: hidden;
max-inline-size: 60px;
&:last-child {
border-bottom: 0;
}
`;
const StyledTaskBody = styled.div`
color: ${({ theme }) => theme.font.color.tertiary};
display: flex;
@@ -105,7 +89,7 @@ export const TaskRow = ({ task }: { task: Task }) => {
});
return (
<StyledContainer
<ActivityRow
onClick={() => {
openActivityRightDrawer(task.id);
}}
@@ -150,6 +134,6 @@ export const TaskRow = ({ task }: { task: Task }) => {
</TaskTargetsContextProvider>
)}
</StyledRightSideContainer>
</StyledContainer>
</ActivityRow>
);
};

View File

@@ -37,12 +37,12 @@ import {
const StyledShowPageRightContainer = styled.div<{ isMobile: boolean }>`
display: flex;
flex: 1 0 0;
flex-direction: column;
height: 100%;
justify-content: start;
width: 100%;
position: relative;
height: 100%;
overflow: auto;
`;
const StyledTabListContainer = styled.div`

View File

@@ -28,6 +28,9 @@ export type Story = StoryObj<typeof SettingsNewObject>;
export const WithStandardSelected: Story = {
play: async () => {
const canvas = within(document.body);
await canvas.findByText('New Object');
const listingInput = await canvas.findByPlaceholderText('Listing');
const pluralInput = await canvas.findByPlaceholderText('Listings');
const descriptionInput = await canvas.findByPlaceholderText(