diff --git a/packages/twenty-ui/package.json b/packages/twenty-ui/package.json index 175007843c..c0fbc76005 100644 --- a/packages/twenty-ui/package.json +++ b/packages/twenty-ui/package.json @@ -76,7 +76,6 @@ "date-fns": "^4.4.0", "framer-motion": "^11.18.0", "glob": "^11.1.0", - "jotai": "^2.17.1", "react": "^19.2.0", "react-dom": "^19.2.0", "react-responsive": "^9.0.2", diff --git a/packages/twenty-ui/src/data-display/Avatar/Avatar.tsx b/packages/twenty-ui/src/data-display/Avatar/Avatar.tsx index 944e03beb7..87423aa3c3 100644 --- a/packages/twenty-ui/src/data-display/Avatar/Avatar.tsx +++ b/packages/twenty-ui/src/data-display/Avatar/Avatar.tsx @@ -1,10 +1,8 @@ import { isNonEmptyString, isNull, isUndefined } from '@sniptt/guards'; import { clsx } from 'clsx'; -import { useAtom } from 'jotai'; -import { useContext } from 'react'; +import { useContext, useState } from 'react'; import { handleClickableElementKeyDown } from '@ui/accessibility/utils/handleClickableElementKeyDown'; -import { invalidAvatarUrlsAtomV2 } from '@ui/data-display/Avatar/states/invalidAvatarUrlsAtomV2'; import { type AvatarSize } from '@ui/data-display/Avatar/types/AvatarSize'; import { type AvatarType } from '@ui/data-display/Avatar/types/AvatarType'; import { type IconComponent } from '@ui/icon/types/IconComponent'; @@ -47,9 +45,9 @@ export const Avatar = ({ }: AvatarProps) => { const { theme } = useContext(ThemeContext); - const [invalidAvatarUrls, setInvalidAvatarUrls] = useAtom( - invalidAvatarUrlsAtomV2, - ); + const [erroredAvatarImageURI, setErroredAvatarImageURI] = useState< + string | null + >(null); const avatarImageURI = isNonEmptyString(avatarUrl) ? getImageAbsoluteURI({ @@ -64,11 +62,11 @@ export const Avatar = ({ const placeholderChar = placeholderFirstChar?.toUpperCase() || '-'; const showPlaceholder = - isNull(avatarImageURI) || invalidAvatarUrls.includes(avatarImageURI); + isNull(avatarImageURI) || erroredAvatarImageURI === avatarImageURI; const handleImageError = () => { if (isNonEmptyString(avatarImageURI)) { - setInvalidAvatarUrls((prev) => [...prev, avatarImageURI]); + setErroredAvatarImageURI(avatarImageURI); } }; diff --git a/packages/twenty-ui/src/data-display/Avatar/__stories__/Avatar.stories.tsx b/packages/twenty-ui/src/data-display/Avatar/__stories__/Avatar.stories.tsx index 38fb5c2c87..c7117e093a 100644 --- a/packages/twenty-ui/src/data-display/Avatar/__stories__/Avatar.stories.tsx +++ b/packages/twenty-ui/src/data-display/Avatar/__stories__/Avatar.stories.tsx @@ -1,17 +1,13 @@ import { type Meta, type StoryObj } from '@storybook/react-vite'; -import { - AVATAR_URL_MOCK, - ComponentDecorator, - JotaiRootDecorator, -} from '@ui/testing'; +import { AVATAR_URL_MOCK, ComponentDecorator } from '@ui/testing'; import { Avatar } from '@ui/data-display/Avatar/Avatar'; const meta: Meta = { title: 'UI/Data Display/Avatar', component: Avatar, - decorators: [ComponentDecorator, JotaiRootDecorator], + decorators: [ComponentDecorator], args: { avatarUrl: AVATAR_URL_MOCK, size: 'md', diff --git a/packages/twenty-ui/src/data-display/Avatar/states/invalidAvatarUrlsAtomV2.ts b/packages/twenty-ui/src/data-display/Avatar/states/invalidAvatarUrlsAtomV2.ts deleted file mode 100644 index 94ae8d03b3..0000000000 --- a/packages/twenty-ui/src/data-display/Avatar/states/invalidAvatarUrlsAtomV2.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { atom } from 'jotai'; - -export const invalidAvatarUrlsAtomV2 = atom([]); -invalidAvatarUrlsAtomV2.debugLabel = 'invalidAvatarUrlsAtomV2'; diff --git a/packages/twenty-ui/src/data-display/AvatarGroup/__stories__/AvatarGroup.stories.tsx b/packages/twenty-ui/src/data-display/AvatarGroup/__stories__/AvatarGroup.stories.tsx index f65790115f..e18b57d0f3 100644 --- a/packages/twenty-ui/src/data-display/AvatarGroup/__stories__/AvatarGroup.stories.tsx +++ b/packages/twenty-ui/src/data-display/AvatarGroup/__stories__/AvatarGroup.stories.tsx @@ -8,7 +8,6 @@ import { AVATAR_URL_MOCK, CatalogDecorator, ComponentDecorator, - JotaiRootDecorator, } from '@ui/testing'; import { @@ -43,7 +42,7 @@ export default meta; type Story = StoryObj; export const Default: Story = { - decorators: [ComponentDecorator, JotaiRootDecorator], + decorators: [ComponentDecorator], }; export const Catalog: Story = { @@ -69,5 +68,5 @@ export const Catalog: Story = { ], }, }, - decorators: [CatalogDecorator, JotaiRootDecorator], + decorators: [CatalogDecorator], }; diff --git a/packages/twenty-ui/src/data-display/AvatarOrIcon/__stories__/AvatarOrIcon.stories.tsx b/packages/twenty-ui/src/data-display/AvatarOrIcon/__stories__/AvatarOrIcon.stories.tsx index cc98012282..9a231b26c9 100644 --- a/packages/twenty-ui/src/data-display/AvatarOrIcon/__stories__/AvatarOrIcon.stories.tsx +++ b/packages/twenty-ui/src/data-display/AvatarOrIcon/__stories__/AvatarOrIcon.stories.tsx @@ -1,16 +1,12 @@ import { type Meta, type StoryObj } from '@storybook/react-vite'; import { IconBuildingSkyscraper, IconUser } from '@ui/icon'; -import { - AVATAR_URL_MOCK, - ComponentDecorator, - JotaiRootDecorator, -} from '@ui/testing'; +import { AVATAR_URL_MOCK, ComponentDecorator } from '@ui/testing'; import { AvatarOrIcon } from '@ui/data-display/AvatarOrIcon/AvatarOrIcon'; const meta: Meta = { title: 'UI/Data Display/AvatarOrIcon', component: AvatarOrIcon, - decorators: [ComponentDecorator, JotaiRootDecorator], + decorators: [ComponentDecorator], }; export default meta; diff --git a/packages/twenty-ui/src/data-display/index.ts b/packages/twenty-ui/src/data-display/index.ts index 0f217f1482..66b0ea7211 100644 --- a/packages/twenty-ui/src/data-display/index.ts +++ b/packages/twenty-ui/src/data-display/index.ts @@ -12,7 +12,6 @@ export { AnimatedCheckmark } from './AnimatedCheckmark/AnimatedCheckmark'; export type { AvatarProps } from './Avatar/Avatar'; export { Avatar } from './Avatar/Avatar'; export { AVATAR_PROPERTIES_BY_SIZE } from './Avatar/constants/AvatarPropertiesBySize'; -export { invalidAvatarUrlsAtomV2 } from './Avatar/states/invalidAvatarUrlsAtomV2'; export type { AvatarSize } from './Avatar/types/AvatarSize'; export type { AvatarType } from './Avatar/types/AvatarType'; export type { AvatarGroupProps } from './AvatarGroup/AvatarGroup'; diff --git a/packages/twenty-ui/src/icon/components/__stories__/Icon.stories.tsx b/packages/twenty-ui/src/icon/components/__stories__/Icon.stories.tsx index 03ad27c0ce..8b540ef590 100644 --- a/packages/twenty-ui/src/icon/components/__stories__/Icon.stories.tsx +++ b/packages/twenty-ui/src/icon/components/__stories__/Icon.stories.tsx @@ -2,7 +2,6 @@ import { type Meta, type StoryObj } from '@storybook/react-vite'; import { IconsProvider } from '@ui/icon/providers/IconsProvider'; import { ComponentDecorator } from '@ui/testing/decorators/ComponentDecorator'; -import { JotaiRootDecorator } from '@ui/testing/decorators/JotaiRootDecorator'; import { Icon } from '../Icon'; @@ -16,7 +15,6 @@ const meta: Meta = { ), - JotaiRootDecorator, ], }; diff --git a/packages/twenty-ui/src/icon/hooks/__tests__/useIcons.test.ts b/packages/twenty-ui/src/icon/hooks/__tests__/useIcons.test.tsx similarity index 80% rename from packages/twenty-ui/src/icon/hooks/__tests__/useIcons.test.ts rename to packages/twenty-ui/src/icon/hooks/__tests__/useIcons.test.tsx index 677878811c..17d9bb1c13 100644 --- a/packages/twenty-ui/src/icon/hooks/__tests__/useIcons.test.ts +++ b/packages/twenty-ui/src/icon/hooks/__tests__/useIcons.test.tsx @@ -1,11 +1,12 @@ import { renderHook } from '@testing-library/react'; -import { Provider } from 'jotai'; +import { type ReactNode } from 'react'; import { Icon123, IconBuildingSkyscraper, IconUser, } from '@ui/icon/components/TablerIcons'; +import { IconsContext } from '@ui/icon/internal/IconsContext'; import { useIcons } from '@ui/icon/hooks/useIcons'; const mockedStateIcons = { @@ -14,14 +15,15 @@ const mockedStateIcons = { IconBuildingSkyscraper, }; -jest.mock('jotai', () => ({ - ...jest.requireActual('jotai'), - useAtomValue: () => mockedStateIcons, -})); +const Wrapper = ({ children }: { children: ReactNode }) => ( + + {children} + +); describe('useIcons', () => { const { result } = renderHook(() => useIcons(), { - wrapper: Provider, + wrapper: Wrapper, }); it('returns default icon when no icon key is provided', () => { diff --git a/packages/twenty-ui/src/icon/hooks/useIcons.ts b/packages/twenty-ui/src/icon/hooks/useIcons.ts index 658c547c3d..e5c48bb86a 100644 --- a/packages/twenty-ui/src/icon/hooks/useIcons.ts +++ b/packages/twenty-ui/src/icon/hooks/useIcons.ts @@ -1,10 +1,10 @@ -import { useAtomValue } from 'jotai'; +import { useContext } from 'react'; import { Icon123 } from '@ui/icon/components/TablerIcons'; -import { iconsState } from '@ui/icon/states/iconsState'; +import { IconsContext } from '@ui/icon/internal/IconsContext'; export const useIcons = () => { - const icons = useAtomValue(iconsState); + const icons = useContext(IconsContext); const defaultIcon = Icon123; const getIcons = () => { diff --git a/packages/twenty-ui/src/icon/index.ts b/packages/twenty-ui/src/icon/index.ts index d4bf4f960a..add4f94e68 100644 --- a/packages/twenty-ui/src/icon/index.ts +++ b/packages/twenty-ui/src/icon/index.ts @@ -489,5 +489,4 @@ export { export { ThinkingOrbitLoaderIcon } from './components/ThinkingOrbitLoaderIcon'; export { useIcons } from './hooks/useIcons'; export { IconsProvider } from './providers/IconsProvider'; -export { iconsState } from './states/iconsState'; export type { IconComponentProps, IconComponent } from './types/IconComponent'; diff --git a/packages/twenty-ui/src/icon/internal/IconsContext.ts b/packages/twenty-ui/src/icon/internal/IconsContext.ts new file mode 100644 index 0000000000..1711e84908 --- /dev/null +++ b/packages/twenty-ui/src/icon/internal/IconsContext.ts @@ -0,0 +1,5 @@ +import { createContext } from 'react'; + +import { type IconComponent } from '@ui/icon/types/IconComponent'; + +export const IconsContext = createContext>({}); diff --git a/packages/twenty-ui/src/icon/providers/IconsProvider.tsx b/packages/twenty-ui/src/icon/providers/IconsProvider.tsx index aaa2c6e896..cf85d7df4e 100644 --- a/packages/twenty-ui/src/icon/providers/IconsProvider.tsx +++ b/packages/twenty-ui/src/icon/providers/IconsProvider.tsx @@ -1,20 +1,22 @@ -import { useSetAtom } from 'jotai'; -import { type JSX, useEffect } from 'react'; +import { type JSX, useEffect, useState } from 'react'; -import { iconsState } from '@ui/icon/states/iconsState'; +import { IconsContext } from '@ui/icon/internal/IconsContext'; +import { type IconComponent } from '@ui/icon/types/IconComponent'; type IconsProviderProps = { children: JSX.Element; }; export const IconsProvider = ({ children }: IconsProviderProps) => { - const setIcons = useSetAtom(iconsState); + const [icons, setIcons] = useState>({}); useEffect(() => { import('./internal/AllIcons').then(({ ALL_ICONS }) => { setIcons(ALL_ICONS); }); - }, [setIcons]); + }, []); - return children; + return ( + {children} + ); }; diff --git a/packages/twenty-ui/src/icon/states/iconsState.ts b/packages/twenty-ui/src/icon/states/iconsState.ts deleted file mode 100644 index f4cbea163e..0000000000 --- a/packages/twenty-ui/src/icon/states/iconsState.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { atom } from 'jotai'; - -import { type IconComponent } from '@ui/icon/types/IconComponent'; - -export const iconsState = atom>({}); -iconsState.debugLabel = 'iconsState'; diff --git a/packages/twenty-ui/src/input/TabButton/__stories__/TabButton.stories.tsx b/packages/twenty-ui/src/input/TabButton/__stories__/TabButton.stories.tsx index 9d2634eb33..4697bea600 100644 --- a/packages/twenty-ui/src/input/TabButton/__stories__/TabButton.stories.tsx +++ b/packages/twenty-ui/src/input/TabButton/__stories__/TabButton.stories.tsx @@ -15,7 +15,6 @@ import { CatalogDecorator, type CatalogStory, ComponentWithRouterDecorator, - JotaiRootDecorator, } from '@ui/testing'; import { type ReactNode } from 'react'; @@ -28,7 +27,7 @@ const TabContainer = ({ children }: { children?: ReactNode }) => { const meta: Meta = { title: 'UI/Input/Button/TabButton', component: TabButton, - decorators: [ComponentWithRouterDecorator, JotaiRootDecorator], + decorators: [ComponentWithRouterDecorator], args: { id: 'tab-button', title: 'Tab Title', diff --git a/packages/twenty-ui/src/navigation/MenuItemMultiSelectAvatar/__stories__/MenuItemMultiSelectAvatar.stories.tsx b/packages/twenty-ui/src/navigation/MenuItemMultiSelectAvatar/__stories__/MenuItemMultiSelectAvatar.stories.tsx index 03493e574f..d70b633dd4 100644 --- a/packages/twenty-ui/src/navigation/MenuItemMultiSelectAvatar/__stories__/MenuItemMultiSelectAvatar.stories.tsx +++ b/packages/twenty-ui/src/navigation/MenuItemMultiSelectAvatar/__stories__/MenuItemMultiSelectAvatar.stories.tsx @@ -9,7 +9,6 @@ import { type CatalogOptions, type CatalogStory, ComponentDecorator, - JotaiRootDecorator, } from '@ui/testing'; import { MenuItemMultiSelectAvatar } from '@ui/navigation/MenuItemMultiSelectAvatar/MenuItemMultiSelectAvatar'; @@ -29,7 +28,7 @@ export const Default: Story = { contextualText: 'Contextual text', avatar: , }, - decorators: [ComponentDecorator, JotaiRootDecorator], + decorators: [ComponentDecorator], }; export const Catalog: CatalogStory = { @@ -84,5 +83,5 @@ export const Catalog: CatalogStory = { } as CatalogOptions, }, }, - decorators: [CatalogDecorator, JotaiRootDecorator], + decorators: [CatalogDecorator], }; diff --git a/packages/twenty-ui/src/navigation/MenuItemSelectAvatar/__stories__/MenuItemSelectAvatar.stories.tsx b/packages/twenty-ui/src/navigation/MenuItemSelectAvatar/__stories__/MenuItemSelectAvatar.stories.tsx index 8b6d79e083..22c33af7cd 100644 --- a/packages/twenty-ui/src/navigation/MenuItemSelectAvatar/__stories__/MenuItemSelectAvatar.stories.tsx +++ b/packages/twenty-ui/src/navigation/MenuItemSelectAvatar/__stories__/MenuItemSelectAvatar.stories.tsx @@ -9,7 +9,6 @@ import { type CatalogOptions, type CatalogStory, ComponentDecorator, - JotaiRootDecorator, } from '@ui/testing'; import { MenuItemSelectAvatar } from '@ui/navigation/MenuItemSelectAvatar/MenuItemSelectAvatar'; @@ -38,7 +37,6 @@ export const Default: Story = { ), ComponentDecorator, - JotaiRootDecorator, ], }; @@ -107,6 +105,5 @@ export const Catalog: CatalogStory = { ), CatalogDecorator, - JotaiRootDecorator, ], }; diff --git a/packages/twenty-ui/src/testing/decorators/JotaiRootDecorator.tsx b/packages/twenty-ui/src/testing/decorators/JotaiRootDecorator.tsx deleted file mode 100644 index b4ec98bce4..0000000000 --- a/packages/twenty-ui/src/testing/decorators/JotaiRootDecorator.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { type Decorator } from '@storybook/react-vite'; - -import { Provider as JotaiProvider } from 'jotai'; - -export const JotaiRootDecorator: Decorator = (Story) => ( - - - -); diff --git a/packages/twenty-ui/src/testing/index.ts b/packages/twenty-ui/src/testing/index.ts index c188b460ee..5a4a3d4f74 100644 --- a/packages/twenty-ui/src/testing/index.ts +++ b/packages/twenty-ui/src/testing/index.ts @@ -21,7 +21,6 @@ export { computeLocation, ComponentWithRouterDecorator, } from './decorators/ComponentWithRouterDecorator'; -export { JotaiRootDecorator } from './decorators/JotaiRootDecorator'; export { RouterDecorator } from './decorators/RouterDecorator'; export { AVATAR_URL_MOCK } from './mocks/avatarUrlMock'; export type { CatalogStory } from './types/CatalogStory'; diff --git a/packages/twenty-ui/src/utilities/index.ts b/packages/twenty-ui/src/utilities/index.ts index e5bbd31d1d..716cb77a0e 100644 --- a/packages/twenty-ui/src/utilities/index.ts +++ b/packages/twenty-ui/src/utilities/index.ts @@ -24,7 +24,6 @@ export { isNavigationModifierPressed } from './navigation/isNavigationModifierPr export type { TriggerEventType } from './navigation/types/trigger-event.type'; export { useIsMobile } from './responsive/hooks/useIsMobile'; export { useScreenSize } from './screen-size/hooks/useScreenSize'; -export { createState } from './state/utils/createState'; export type { ClickOutsideAttributes } from './types/ClickOutsideAttributes'; export type { Nullable } from './types/Nullable'; export { getDisplayValueByUrlType } from './utils/getDisplayValueByUrlType'; diff --git a/packages/twenty-ui/src/utilities/state/utils/createState.ts b/packages/twenty-ui/src/utilities/state/utils/createState.ts deleted file mode 100644 index 220424f24c..0000000000 --- a/packages/twenty-ui/src/utilities/state/utils/createState.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { atom, type PrimitiveAtom } from 'jotai'; - -export const createState = ({ - key, - defaultValue, -}: { - key: string; - defaultValue: ValueType; -}): PrimitiveAtom => { - const jotaiAtom = atom(defaultValue); - jotaiAtom.debugLabel = key; - return jotaiAtom; -}; diff --git a/yarn.lock b/yarn.lock index 77ab916147..9c1aadfa46 100644 --- a/yarn.lock +++ b/yarn.lock @@ -54395,7 +54395,6 @@ __metadata: glob: "npm:^11.1.0" jest: "npm:29.7.0" jest-environment-jsdom: "npm:30.0.0-beta.3" - jotai: "npm:^2.17.1" prettier: "npm:^3.1.1" react: "npm:^19.2.0" react-dom: "npm:^19.2.0"