Migrate tab list to scope map (#3333)

* Migrate tab list to scope map

* Return state to hook and let client subscribe to state

* Run prettier

---------

Co-authored-by: Thomas Trompette <thomast@twenty.com>
This commit is contained in:
Thomas Trompette
2024-01-09 17:01:27 +01:00
committed by GitHub
parent 8d2758ee5e
commit ebf7688e3d
11 changed files with 108 additions and 48 deletions

View File

@ -1,16 +1,15 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer'; import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext';
import { useTasks } from '@/activities/tasks/hooks/useTasks'; import { useTasks } from '@/activities/tasks/hooks/useTasks';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity'; import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { IconPlus } from '@/ui/display/icon'; import { IconPlus } from '@/ui/display/icon';
import { Button } from '@/ui/input/button/components/Button'; import { Button } from '@/ui/input/button/components/Button';
import { activeTabIdScopedState } from '@/ui/layout/tab/states/activeTabIdScopedState';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { AddTaskButton } from './AddTaskButton'; import { AddTaskButton } from './AddTaskButton';
import { TaskList } from './TaskList'; import { TaskList } from './TaskList';
import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
import { useRecoilValue } from 'recoil';
const StyledTaskGroupEmptyContainer = styled.div` const StyledTaskGroupEmptyContainer = styled.div`
align-items: center; align-items: center;
@ -69,10 +68,8 @@ export const TaskGroups = ({
const openCreateActivity = useOpenCreateActivityDrawer(); const openCreateActivity = useOpenCreateActivityDrawer();
const [activeTabId] = useRecoilScopedState( const { activeTabIdState } = useTabList('task-groups-tab-list');
activeTabIdScopedState, const activeTabId = useRecoilValue(activeTabIdState);
TasksRecoilScopeContext,
);
if ( if (
(activeTabId !== 'done' && (activeTabId !== 'done' &&

View File

@ -15,12 +15,11 @@ import {
IconTimelineEvent, IconTimelineEvent,
} from '@/ui/display/icon'; } from '@/ui/display/icon';
import { TabList } from '@/ui/layout/tab/components/TabList'; import { TabList } from '@/ui/layout/tab/components/TabList';
import { activeTabIdScopedState } from '@/ui/layout/tab/states/activeTabIdScopedState';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { ShowPageRecoilScopeContext } from '../../states/ShowPageRecoilScopeContext'; import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
import { useRecoilValue } from 'recoil';
const StyledShowPageRightContainer = styled.div` const StyledShowPageRightContainer = styled.div`
display: flex; display: flex;
@ -40,6 +39,8 @@ const StyledTabListContainer = styled.div`
height: 40px; height: 40px;
`; `;
const TAB_LIST_COMPONENT_ID = 'show-page-right-tab-list';
type ShowPageRightContainerProps = { type ShowPageRightContainerProps = {
targetableObject?: ActivityTargetableObject; targetableObject?: ActivityTargetableObject;
timeline?: boolean; timeline?: boolean;
@ -56,10 +57,9 @@ export const ShowPageRightContainer = ({
emails, emails,
}: ShowPageRightContainerProps) => { }: ShowPageRightContainerProps) => {
const isMessagingEnabled = useIsFeatureEnabled('IS_MESSAGING_ENABLED'); const isMessagingEnabled = useIsFeatureEnabled('IS_MESSAGING_ENABLED');
const [activeTabId] = useRecoilScopedState(
activeTabIdScopedState, const { activeTabIdState } = useTabList(TAB_LIST_COMPONENT_ID);
ShowPageRecoilScopeContext, const activeTabId = useRecoilValue(activeTabIdState);
);
if (!targetableObject) return <></>; if (!targetableObject) return <></>;
@ -105,7 +105,7 @@ export const ShowPageRightContainer = ({
return ( return (
<StyledShowPageRightContainer> <StyledShowPageRightContainer>
<StyledTabListContainer> <StyledTabListContainer>
<TabList context={ShowPageRecoilScopeContext} tabs={TASK_TABS} /> <TabList tabListId={TAB_LIST_COMPONENT_ID} tabs={TASK_TABS} />
</StyledTabListContainer> </StyledTabListContainer>
{activeTabId === 'timeline' && ( {activeTabId === 'timeline' && (
<Timeline targetableObject={targetableObject} /> <Timeline targetableObject={targetableObject} />

View File

@ -2,11 +2,11 @@ import * as React from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { IconComponent } from '@/ui/display/icon/types/IconComponent'; import { IconComponent } from '@/ui/display/icon/types/IconComponent';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { activeTabIdScopedState } from '../states/activeTabIdScopedState';
import { Tab } from './Tab'; import { Tab } from './Tab';
import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
import { TabListScope } from '@/ui/layout/tab/scopes/TabListScope';
import { useRecoilValue } from 'recoil';
type SingleTabProps = { type SingleTabProps = {
title: string; title: string;
@ -17,8 +17,8 @@ type SingleTabProps = {
}; };
type TabListProps = { type TabListProps = {
tabListId: string;
tabs: SingleTabProps[]; tabs: SingleTabProps[];
context: React.Context<string | null>;
}; };
const StyledContainer = styled.div` const StyledContainer = styled.div`
@ -31,35 +31,36 @@ const StyledContainer = styled.div`
user-select: none; user-select: none;
`; `;
export const TabList = ({ tabs, context }: TabListProps) => { export const TabList = ({ tabs, tabListId }: TabListProps) => {
const initialActiveTabId = tabs[0].id; const initialActiveTabId = tabs[0].id;
const [activeTabId, setActiveTabId] = useRecoilScopedState( const { activeTabIdState, setActiveTabId } = useTabList(tabListId);
activeTabIdScopedState,
context, const activeTabId = useRecoilValue(activeTabIdState);
);
React.useEffect(() => { React.useEffect(() => {
setActiveTabId(initialActiveTabId); setActiveTabId(initialActiveTabId);
}, [initialActiveTabId, setActiveTabId]); }, [initialActiveTabId, setActiveTabId]);
return ( return (
<StyledContainer> <TabListScope tabListScopeId={tabListId}>
{tabs <StyledContainer>
.filter((tab) => !tab.hide) {tabs
.map((tab) => ( .filter((tab) => !tab.hide)
<Tab .map((tab) => (
id={tab.id} <Tab
key={tab.id} id={tab.id}
title={tab.title} key={tab.id}
Icon={tab.Icon} title={tab.title}
active={tab.id === activeTabId} Icon={tab.Icon}
onClick={() => { active={tab.id === activeTabId}
setActiveTabId(tab.id); onClick={() => {
}} setActiveTabId(tab.id);
disabled={tab.disabled} }}
/> disabled={tab.disabled}
))} />
</StyledContainer> ))}
</StyledContainer>
</TabListScope>
); );
}; };

View File

@ -40,6 +40,7 @@ const meta: Meta<typeof TabList> = {
title: 'UI/Layout/Tab/TabList', title: 'UI/Layout/Tab/TabList',
component: TabList, component: TabList,
args: { args: {
tabListId: 'tab-list-id',
tabs: tabs, tabs: tabs,
}, },
decorators: [ decorators: [

View File

@ -0,0 +1,20 @@
import { TabListScopeInternalContext } from '@/ui/layout/tab/scopes/scope-internal-context/TabListScopeInternalContext';
import { activeTabIdStateScopeMap } from '@/ui/layout/tab/states/activeTabIdStateScopeMap';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { getState } from '@/ui/utilities/recoil-scope/utils/getState';
type useTabListStatesProps = {
tabListScopeId?: string;
};
export const useTabListStates = ({ tabListScopeId }: useTabListStatesProps) => {
const scopeId = useAvailableScopeIdOrThrow(
TabListScopeInternalContext,
tabListScopeId,
);
return {
scopeId,
activeTabIdState: getState(activeTabIdStateScopeMap, scopeId),
};
};

View File

@ -0,0 +1,15 @@
import { useTabListStates } from '@/ui/layout/tab/hooks/internal/useTabListStates';
import { useSetRecoilState } from 'recoil';
export const useTabList = (tabListId?: string) => {
const { activeTabIdState } = useTabListStates({
tabListScopeId: `${tabListId}-scope`,
});
const setActiveTabId = useSetRecoilState(activeTabIdState);
return {
activeTabIdState,
setActiveTabId,
};
};

View File

@ -0,0 +1,19 @@
import { ReactNode } from 'react';
import { TabListScopeInternalContext } from './scope-internal-context/TabListScopeInternalContext';
type TabListScopeProps = {
children: ReactNode;
tabListScopeId: string;
};
export const TabListScope = ({
children,
tabListScopeId,
}: TabListScopeProps) => {
return (
<TabListScopeInternalContext.Provider value={{ scopeId: tabListScopeId }}>
{children}
</TabListScopeInternalContext.Provider>
);
};

View File

@ -0,0 +1,7 @@
import { StateScopeMapKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/StateScopeMapKey';
import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext';
type TabListScopeInternalContextProps = StateScopeMapKey;
export const TabListScopeInternalContext =
createScopeInternalContext<TabListScopeInternalContextProps>();

View File

@ -1,6 +0,0 @@
import { atomFamily } from 'recoil';
export const activeTabIdScopedState = atomFamily<string | null, string>({
key: 'activeTabIdScopedState',
default: null,
});

View File

@ -0,0 +1,6 @@
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
export const activeTabIdStateScopeMap = createStateScopeMap<string | null>({
key: 'activeTabIdStateScopeMap',
defaultValue: null,
});

View File

@ -58,7 +58,7 @@ export const Tasks = () => {
<TopBar <TopBar
leftComponent={ leftComponent={
<StyledTabListContainer> <StyledTabListContainer>
<TabList context={TasksRecoilScopeContext} tabs={TASK_TABS} /> <TabList tabListId="tasks-tab-list" tabs={TASK_TABS} />
</StyledTabListContainer> </StyledTabListContainer>
} }
rightComponent={ rightComponent={