Skip to content

Commit

Permalink
Right drawer to edit records (twentyhq#5551)
Browse files Browse the repository at this point in the history
This PR introduces a new side panel to edit records and the ability to
minimize the side panel.

The goal is leverage this sidepanel to be able to create records while
being in another show page.

I'm opening the PR for feedback since it involved refactoring and
therefore already touches a lot of files, even though it was quick to
implement.

<img width="1503" alt="Screenshot 2024-05-23 at 17 41 37"
src="https://github.com/twentyhq/twenty/assets/6399865/6f17e7a8-f4e9-4eb4-b392-c756db7198ac">
  • Loading branch information
FelixMalfait authored Jun 3, 2024
1 parent ccd93f7 commit 5ce23cc
Show file tree
Hide file tree
Showing 61 changed files with 957 additions and 452 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ import { useRecoilValue } from 'recoil';

import { CalendarEventDetails } from '@/activities/calendar/components/CalendarEventDetails';
import { FIND_ONE_CALENDAR_EVENT_OPERATION_SIGNATURE } from '@/activities/calendar/graphql/operation-signatures/FindOneCalendarEventOperationSignature';
import { viewableCalendarEventIdState } from '@/activities/calendar/states/viewableCalendarEventIdState';
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { useSetRecordInStore } from '@/object-record/record-store/hooks/useSetRecordInStore';

export const RightDrawerCalendarEvent = () => {
const { setRecords } = useSetRecordInStore();
const viewableCalendarEventId = useRecoilValue(viewableCalendarEventIdState);
const viewableRecordId = useRecoilValue(viewableRecordIdState);
const { record: calendarEvent } = useFindOneRecord<CalendarEvent>({
objectNameSingular:
FIND_ONE_CALENDAR_EVENT_OPERATION_SIGNATURE.objectNameSingular,
objectRecordId: viewableCalendarEventId ?? '',
objectRecordId: viewableRecordId ?? '',
recordGqlFields: FIND_ONE_CALENDAR_EVENT_OPERATION_SIGNATURE.fields,
onCompleted: (record) => setRecords([record]),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,32 @@ import { act, renderHook } from '@testing-library/react';
import { RecoilRoot, useRecoilValue } from 'recoil';

import { useOpenCalendarEventRightDrawer } from '@/activities/calendar/right-drawer/hooks/useOpenCalendarEventRightDrawer';
import { viewableCalendarEventIdState } from '@/activities/calendar/states/viewableCalendarEventIdState';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { isRightDrawerOpenState } from '@/ui/layout/right-drawer/states/isRightDrawerOpenState';

describe('useOpenCalendarEventRightDrawer', () => {
it('opens the right drawer with the calendar event', () => {
const { result } = renderHook(
() => {
const isRightDrawerOpen = useRecoilValue(isRightDrawerOpenState);
const viewableCalendarEventId = useRecoilValue(
viewableCalendarEventIdState,
);
const viewableRecordId = useRecoilValue(viewableRecordIdState);
return {
...useOpenCalendarEventRightDrawer(),
isRightDrawerOpen,
viewableCalendarEventId,
viewableRecordId,
};
},
{ wrapper: RecoilRoot },
);

expect(result.current.isRightDrawerOpen).toBe(false);
expect(result.current.viewableCalendarEventId).toBeNull();
expect(result.current.viewableRecordId).toBeNull();

act(() => {
result.current.openCalendarEventRightDrawer('1234');
});

expect(result.current.isRightDrawerOpen).toBe(true);
expect(result.current.viewableCalendarEventId).toBe('1234');
expect(result.current.viewableRecordId).toBe('1234');
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useSetRecoilState } from 'recoil';

import { viewableCalendarEventIdState } from '@/activities/calendar/states/viewableCalendarEventIdState';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
import { RightDrawerHotkeyScope } from '@/ui/layout/right-drawer/types/RightDrawerHotkeyScope';
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
Expand All @@ -9,14 +9,12 @@ import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope
export const useOpenCalendarEventRightDrawer = () => {
const { openRightDrawer } = useRightDrawer();
const setHotkeyScope = useSetHotkeyScope();
const setViewableCalendarEventId = useSetRecoilState(
viewableCalendarEventIdState,
);
const setViewableRecordId = useSetRecoilState(viewableRecordIdState);

const openCalendarEventRightDrawer = (calendarEventId: string) => {
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
openRightDrawer(RightDrawerPages.ViewCalendarEvent);
setViewableCalendarEventId(calendarEventId);
setViewableRecordId(calendarEventId);
};

return { openCalendarEventRightDrawer };
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ import { Meta, StoryObj } from '@storybook/react';
import { useSetRecoilState } from 'recoil';
import { ComponentDecorator } from 'twenty-ui';

import { viewableActivityIdState } from '@/activities/states/viewableActivityIdState';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';

import { ActivityActionBar } from '../../right-drawer/components/ActivityActionBar';
import { Comment } from '../Comment';

import { mockComment, mockCommentWithLongValues } from './mock-comment';

const CommentSetterEffect = () => {
const setViewableActivity = useSetRecoilState(viewableActivityIdState);
const setViewableRecord = useSetRecoilState(viewableRecordIdState);

useEffect(() => {
setViewableActivity('test-id');
}, [setViewableActivity]);
setViewableRecord('test-id');
}, [setViewableRecord]);

return null;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { DateTime } from 'luxon';
import { useSetRecoilState } from 'recoil';

import { ActivityActionBar } from '@/activities/right-drawer/components/ActivityActionBar';
import { viewableActivityIdState } from '@/activities/states/viewableActivityIdState';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
import { avatarUrl } from '~/testing/mock-data/users';

Expand All @@ -13,11 +13,11 @@ import { CommentHeader } from '../CommentHeader';
import { mockComment, mockCommentWithLongValues } from './mock-comment';

const CommentHeaderSetterEffect = () => {
const setViewableActivity = useSetRecoilState(viewableActivityIdState);
const setViewableRecord = useSetRecoilState(viewableRecordIdState);

useEffect(() => {
setViewableActivity('test-id');
}, [setViewableActivity]);
setViewableRecord('test-id');
}, [setViewableRecord]);

return null;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { ActivityComments } from '@/activities/components/ActivityComments';
import { ActivityCreationDate } from '@/activities/components/ActivityCreationDate';
import { ActivityEditorFields } from '@/activities/components/ActivityEditorFields';
import { ActivityTitleEffect } from '@/activities/components/ActivityTitleEffect';
import { ActivityTypeDropdown } from '@/activities/components/ActivityTypeDropdown';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';

import { ActivityTitle } from './ActivityTitle';
Expand Down Expand Up @@ -68,7 +67,6 @@ export const ActivityEditor = ({
<StyledContainer ref={containerRef}>
<StyledUpperPartContainer>
<StyledTopContainer>
<ActivityTypeDropdown activityId={activityId} />
<ActivityTitleEffect activityId={activityId} />
<StyledTitleContainer>
<ActivityTitle activityId={activityId} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import styled from '@emotion/styled';
import { IconMail, Tag } from 'twenty-ui';

import { beautifyPastDateRelativeToNow } from '~/utils/date-utils';

Expand Down Expand Up @@ -43,7 +42,6 @@ export const EmailThreadHeader = ({
}: EmailThreadHeaderProps) => {
return (
<StyledContainer>
<Tag Icon={IconMail} color="gray" text="Email" onClick={() => {}} />
<StyledHead>
<StyledHeading>{subject}</StyledHeading>
<StyledContent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { act, renderHook } from '@testing-library/react';
import { RecoilRoot, useRecoilState, useRecoilValue } from 'recoil';

import { useEmailThread } from '@/activities/emails/hooks/useEmailThread';
import { viewableEmailThreadIdState } from '@/activities/emails/states/viewableEmailThreadIdState';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { isRightDrawerOpenState } from '@/ui/layout/right-drawer/states/isRightDrawerOpenState';

const viewableEmailThreadId = '1234';
Expand All @@ -13,24 +13,22 @@ describe('useEmailThread', () => {
() => {
const emailThread = useEmailThread();
const isRightDrawerOpen = useRecoilValue(isRightDrawerOpenState);
const viewableEmailThreadId = useRecoilValue(
viewableEmailThreadIdState,
);
const viewableRecordId = useRecoilValue(viewableRecordIdState);

return { ...emailThread, isRightDrawerOpen, viewableEmailThreadId };
return { ...emailThread, isRightDrawerOpen, viewableRecordId };
},
{ wrapper: RecoilRoot },
);

expect(result.current.isRightDrawerOpen).toBe(false);
expect(result.current.viewableEmailThreadId).toBeNull();
expect(result.current.viewableRecordId).toBeNull();

act(() => {
result.current.openEmailThread(viewableEmailThreadId);
});

expect(result.current.isRightDrawerOpen).toBe(true);
expect(result.current.viewableEmailThreadId).toBe(viewableEmailThreadId);
expect(result.current.viewableRecordId).toBe(viewableEmailThreadId);
});

it('should close email thread if trying to open the same thread id', () => {
Expand All @@ -40,30 +38,31 @@ describe('useEmailThread', () => {
const [isRightDrawerOpen, setIsRightDrawerOpen] = useRecoilState(
isRightDrawerOpenState,
);
const [viewableEmailThreadId, setViewableEmailThreadId] =
useRecoilState(viewableEmailThreadIdState);
const [viewableRecordId, setViewableRecordId] = useRecoilState(
viewableRecordIdState,
);

return {
...emailThread,
isRightDrawerOpen,
viewableEmailThreadId,
viewableRecordId,
setIsRightDrawerOpen,
setViewableEmailThreadId,
setViewableRecordId,
};
},
{ wrapper: RecoilRoot },
);

act(() => {
result.current.setIsRightDrawerOpen(true);
result.current.setViewableEmailThreadId(viewableEmailThreadId);
result.current.setViewableRecordId(viewableEmailThreadId);
});

act(() => {
result.current.openEmailThread(viewableEmailThreadId);
});

expect(result.current.isRightDrawerOpen).toBe(false);
expect(result.current.viewableEmailThreadId).toBeNull();
expect(result.current.viewableRecordId).toBeNull();
});
});
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useRecoilCallback } from 'recoil';

import { useOpenEmailThreadRightDrawer } from '@/activities/emails/right-drawer/hooks/useOpenEmailThreadRightDrawer';
import { viewableEmailThreadIdState } from '@/activities/emails/states/viewableEmailThreadIdState';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
import { isRightDrawerOpenState } from '@/ui/layout/right-drawer/states/isRightDrawerOpenState';

export const useEmailThread = () => {
const { closeRightDrawer } = useRightDrawer();
const openEmailThredRightDrawer = useOpenEmailThreadRightDrawer();
const openEmailThreadRightDrawer = useOpenEmailThreadRightDrawer();

const openEmailThread = useRecoilCallback(
({ snapshot, set }) =>
Expand All @@ -17,19 +17,19 @@ export const useEmailThread = () => {
.getValue();

const viewableEmailThreadId = snapshot
.getLoadable(viewableEmailThreadIdState)
.getLoadable(viewableRecordIdState)
.getValue();

if (isRightDrawerOpen && viewableEmailThreadId === threadId) {
set(viewableEmailThreadIdState, null);
set(viewableRecordIdState, null);
closeRightDrawer();
return;
}

openEmailThredRightDrawer();
set(viewableEmailThreadIdState, threadId);
openEmailThreadRightDrawer();
set(viewableRecordIdState, threadId);
},
[closeRightDrawer, openEmailThredRightDrawer],
[closeRightDrawer, openEmailThreadRightDrawer],
);

return { openEmailThread };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import gql from 'graphql-tag';
import { useRecoilValue } from 'recoil';

import { fetchAllThreadMessagesOperationSignatureFactory } from '@/activities/emails/graphql/operation-signatures/factories/fetchAllThreadMessagesOperationSignatureFactory';
import { viewableEmailThreadIdState } from '@/activities/emails/states/viewableEmailThreadIdState';
import { EmailThreadMessage as EmailThreadMessageType } from '@/activities/emails/types/EmailThreadMessage';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';

export const useRightDrawerEmailThread = () => {
const viewableEmailThreadId = useRecoilValue(viewableEmailThreadIdState);
const viewableRecordId = useRecoilValue(viewableRecordIdState);

const apolloClient = useApolloClient();
const thread = apolloClient.readFragment({
id: `TimelineThread:${viewableEmailThreadId}`,
id: `TimelineThread:${viewableRecordId}`,
fragment: gql`
fragment timelineThread on TimelineThread {
id
Expand All @@ -25,7 +25,7 @@ export const useRightDrawerEmailThread = () => {

const FETCH_ALL_MESSAGES_OPERATION_SIGNATURE =
fetchAllThreadMessagesOperationSignatureFactory({
messageThreadId: viewableEmailThreadId,
messageThreadId: viewableRecordId,
});

const {
Expand All @@ -39,7 +39,7 @@ export const useRightDrawerEmailThread = () => {
FETCH_ALL_MESSAGES_OPERATION_SIGNATURE.objectNameSingular,
orderBy: FETCH_ALL_MESSAGES_OPERATION_SIGNATURE.variables.orderBy,
recordGqlFields: FETCH_ALL_MESSAGES_OPERATION_SIGNATURE.fields,
skip: !viewableEmailThreadId,
skip: !viewableRecordId,
});

const fetchMoreMessages = useCallback(() => {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { RecoilRoot, useRecoilValue } from 'recoil';

import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
import { activityIdInDrawerState } from '@/activities/states/activityIdInDrawerState';
import { viewableActivityIdState } from '@/activities/states/viewableActivityIdState';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';

const Wrapper = ({ children }: { children: ReactNode }) => (
<RecoilRoot>
Expand All @@ -18,12 +18,12 @@ describe('useOpenActivityRightDrawer', () => {
const { result } = renderHook(
() => {
const openActivityRightDrawer = useOpenActivityRightDrawer();
const viewableActivityId = useRecoilValue(viewableActivityIdState);
const viewableRecordId = useRecoilValue(viewableRecordIdState);
const activityIdInDrawer = useRecoilValue(activityIdInDrawerState);
return {
openActivityRightDrawer,
activityIdInDrawer,
viewableActivityId,
viewableRecordId,
};
},
{
Expand All @@ -32,11 +32,11 @@ describe('useOpenActivityRightDrawer', () => {
);

expect(result.current.activityIdInDrawer).toBeNull();
expect(result.current.viewableActivityId).toBeNull();
expect(result.current.viewableRecordId).toBeNull();
act(() => {
result.current.openActivityRightDrawer('123');
});
expect(result.current.activityIdInDrawer).toBe('123');
expect(result.current.viewableActivityId).toBe('123');
expect(result.current.viewableRecordId).toBe('123');
});
});
Loading

0 comments on commit 5ce23cc

Please sign in to comment.