feat(deps): migrate frontend to React 19 (#21531)
## What Migrates the frontend stack from **React 18.3 → 19.2**. The website, sdk, companion and emails packages were already on React 19; this brings the remaining holdouts (`twenty-front`, `twenty-ui`, `twenty-ui-deprecated`, `twenty-front-component-renderer`) and `twenty-server`'s email rendering onto 19, and pins a single React version repo-wide. ## Why React 18.x is now the legacy line. Staying current keeps us on the patched/maintained branch and unblocks downstream library majors (react-router 7, mantine 9, etc.) that require React 19 peers. ## Dependency bumps (required by React 19 peers / removed APIs) | Package | From | To | Reason | |---|---|---|---| | react / react-dom | 18.3.1 | 19.2.3 | core | | @hello-pangea/dnd | 16 | 18 | peer `^18 \|\| ^19` | | react-datepicker | 6 | 9 | v<7 used removed `findDOMNode`; drops `@types/react-datepicker` | | react-data-grid | beta.13 | beta.59 | peer `^19.2`; new render API | | graphiql (+ @graphiql/react, plugin-explorer) | 3 / 0.23 / 1 | 5 / 0.37 / 5.1 | peer `^18 \|\| ^19` | | react-helmet-async | 1.3 | **@dr.pogodin/react-helmet** 3.2 | upstream caps peer at `^18`; drop-in React 19 fork | A `resolutions` pin enforces a single React (19.2.3) + `@types/react` (19.2.14) across the monorepo to avoid duplicate copies / type-identity splits. Versions are the aged lockfile patches (clears the `npmMinimalAgeGate`). ## Code changes - **Global `JSX` shim** (`react-jsx-global.d.ts` per package): React 19 moved the `JSX` namespace under `React.JSX`; several deps' published types (notably `@linaria/react`'s `styled.d.ts`, which types every `styled.x` via `keyof JSX.IntrinsicElements`) still reference the global namespace. Without the shim, every styled component degrades to `any` props. - **Ref nullability**: `useRef<T>(null)` now returns `RefObject<T | null>`; widened consumer prop/hook ref types accordingly (incl. the shared `useListenClickOutside`). - **react-datepicker v9**: `onChange`/`onSelect` accept `Date | null`, `calendarStartDay` typing, `ReactDatePickerProps`→`DatePickerProps`, relaxed the dynamic `selectsMultiple` discriminated union. - **react-data-grid beta.59**: `formatter`→`renderCell`, `editor`→`renderEditCell`, `headerRenderer`→`renderHeaderCell`, `components`→`renderers`, `onRowClick`→`onCellClick`, object-shaped `useRowSelection`, Set-based selection. - **dnd style cast**: `@radix-ui/react-popper` augments `CSSProperties` with a `--radix-*` index signature that dnd's closed `DraggingStyle` doesn't satisfy → cast at the spread. ## Status / testing - ✅ `typecheck` green: twenty-front, twenty-ui, twenty-ui-deprecated, twenty-front-component-renderer, twenty-server - ⏳ build / lint / unit tests / storybook+argos / runtime smoke-test in progress Draft until local + CI verification completes. Notable behavior to QA manually: spreadsheet import (data-grid), date pickers, drag-and-drop boards/lists, GraphQL playground, page titles/favicon. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21531?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
+4
-3
@@ -7,8 +7,8 @@
|
||||
"@nx/storybook": "22.7.5",
|
||||
"@nx/vite": "22.7.5",
|
||||
"@nx/web": "22.7.5",
|
||||
"@types/react": "^18.2.39",
|
||||
"@types/react-dom": "^18.2.15",
|
||||
"@types/react": "^19.2.0",
|
||||
"@types/react-dom": "^19.2.0",
|
||||
"@yarnpkg/types": "^4.0.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"http-server": "^14.1.1",
|
||||
@@ -26,6 +26,7 @@
|
||||
"name": "twenty",
|
||||
"packageManager": "yarn@4.13.0",
|
||||
"resolutions": {
|
||||
"@remote-dom/react/@types/react": "^19.2.0",
|
||||
"graphql": "16.8.1",
|
||||
"graphql-redis-subscriptions/ioredis": "5.10.1",
|
||||
"@types/qs": "6.9.16",
|
||||
@@ -50,7 +51,7 @@
|
||||
"@react-email/ui/esbuild": "0.28.1",
|
||||
"react-email/esbuild": "0.28.1"
|
||||
},
|
||||
"//resolutions": "Each entry is load-bearing: it forces a version OUTSIDE some parent's declared range where no fixed upstream release exists; remove each once its blocker ships. graphql 16.8.1 -> singleton pin held below msw's ^16.12.0 dep and @nestjs/graphql's ^16.11.0 peer; drop after a validated repo-wide bump to latest 16.x; graphql-redis-subscriptions/ioredis 5.10.1 -> TS type-identity dedup: twenty-server passes its ioredis client into RedisPubSub, so this must equal the exact ioredis version pinned by twenty-server and bullmq (bump in lockstep); @types/qs 6.9.16 -> holdback below the 6.9.17 ParsedQs typing break (node-saml wants ^6.9.18); @opentelemetry/api 1.9.1 -> singleton guard for the NoopMeterProvider bug (#20231): ai 6.0.x pins 1.9.0 exact vs @sentry/node ^1.9.1, drop when workspace ai >=6.0.178 AND @scalar/agent-chat moves off ai 6.0.33; chokidar ^3 -> NestJS CLI watch needs fsevents on macOS, removed in chokidar 4/5 (#20316); tmp ^0.2.7 -> CVE, zapier-platform-cli 19 (latest) pins 0.2.5 and inquirer 7/8's external-editor wants ^0.0.33; make-fetch-happen ^15 + @electron/{rebuild,node-gyp}/tar ^7.5.16 -> tar CVE eviction for the @electron/rebuild 3.x toolchain (rebuild 3.x pins tar ^6, its node-gyp fork pins tar ^6.2.1 + mfh ^10), drop when electron-forge declares @electron/rebuild >=4; @angular-devkit/core 19.2.24 -> picomatch CVE, blocked on @nestjs/cli >11.0.23 fixing the dist/src output regression (repo held at 11.0.16); yeoman-environment 6.0.1 -> CVE, zapier-platform-cli 19 (latest) pins 4.4.3; webpack-dev-server 5.2.4 -> CVE, @electron-forge/plugin-webpack (incl. 8.x alphas) still declares ^4; express/qs + @cypress/request/qs 6.15.2 -> qs CVE for old express 4.22.0/4.22.1 pinned by @mintlify/previewing and verdaccio (verdaccio also pins @cypress/request 3.0.10; all other qs parents resolve safe naturally); next/postcss 8.5.15 -> postcss CVE, every stable next pins 8.4.31 exact (fix only in 16.3.0 canaries; @react-email/ui also pins next 16.2.6); <pkg>/uuid 11.1.1 -> uuid CVE for parents pinning uuid <11 with no fixed release (sockjs dormant since 2021; @cypress/request 3.0.10 via verdaccio; @ptc-org/nestjs-query-typeorm at latest; googleapis 105 -> common 8 drops uuid but needs the googleapis >=152 migration). Preserves the intentional uuid 13.x; @cyntler/react-doc-viewer/ajv 8.20.0 -> CVE, upstream (latest 1.17.1) pins ajv ^7 but never imports it, forcing v8 is safe; wrangler/esbuild + @react-email/ui/esbuild + react-email/esbuild 0.28.1 -> esbuild dev-server path-traversal GHSA-g7r4-m6w7-qqqr (Windows, vulnerable >=0.27.3 <0.28.1, fixed in 0.28.1): wrangler exact-pins esbuild 0.27.3 (still 0.27.3 in latest 4.100.0) and @react-email/ui exact-pins 0.28.0 (still 0.28.0 in latest 6.6.0) with no fixed upstream release; react-email allows ^0.28.0 but the npmMinimalAgeGate down-selects it to the still-vulnerable 0.28.0 until 0.28.1 ages past the gate (published 2026-06-11), so it is pinned forward here. Drop the exact-pin parents once they ship an esbuild >=0.28.1 pin; drop react-email once 0.28.1 clears the age gate (it then resolves to it naturally). Our own twenty-client-sdk raises its esbuild floor to ^0.28.1 directly in its package.json instead of via a resolution",
|
||||
"//resolutions": "Each entry is load-bearing: it forces a version OUTSIDE some parent's declared range where no fixed upstream release exists; remove each once its blocker ships. @remote-dom/react/@types/react ^19.2.0 -> React type-identity dedup, SCOPED to @remote-dom/react only (every other package resolves @types/react 19 naturally from the workspace ^19.2.0 ranges). @remote-dom/react (transitive via twenty-front-component-renderer) lists @types/react ^18 in its own dependencies and nests its own copy; React 18 and 19 declare ReactNode differently (19 adds bigint + Promise<AwaitedReactNode>, drops ReactFragment's {}), so the two copies are mutually non-assignable and break twenty-front's typecheck (~156 TS2322 errors). Range-aligning our own packages can't fix a third-party's transitive @types pin, hence this single scoped override (only @types/react splits; runtime react/react-dom are peer-deps that converge naturally, and @types/react-dom does not nest a copy). Drop once @remote-dom/react widens @types/react to ^19 (latest 1.2.2 still pins ^18; tracked upstream in Shopify/remote-dom#153). graphql 16.8.1 -> singleton pin held below msw's ^16.12.0 dep and @nestjs/graphql's ^16.11.0 peer; drop after a validated repo-wide bump to latest 16.x; graphql-redis-subscriptions/ioredis 5.10.1 -> TS type-identity dedup: twenty-server passes its ioredis client into RedisPubSub, so this must equal the exact ioredis version pinned by twenty-server and bullmq (bump in lockstep); @types/qs 6.9.16 -> holdback below the 6.9.17 ParsedQs typing break (node-saml wants ^6.9.18); @opentelemetry/api 1.9.1 -> singleton guard for the NoopMeterProvider bug (#20231): ai 6.0.x pins 1.9.0 exact vs @sentry/node ^1.9.1, drop when workspace ai >=6.0.178 AND @scalar/agent-chat moves off ai 6.0.33; chokidar ^3 -> NestJS CLI watch needs fsevents on macOS, removed in chokidar 4/5 (#20316); tmp ^0.2.7 -> CVE, zapier-platform-cli 19 (latest) pins 0.2.5 and inquirer 7/8's external-editor wants ^0.0.33; make-fetch-happen ^15 + @electron/{rebuild,node-gyp}/tar ^7.5.16 -> tar CVE eviction for the @electron/rebuild 3.x toolchain (rebuild 3.x pins tar ^6, its node-gyp fork pins tar ^6.2.1 + mfh ^10), drop when electron-forge declares @electron/rebuild >=4; @angular-devkit/core 19.2.24 -> picomatch CVE, blocked on @nestjs/cli >11.0.23 fixing the dist/src output regression (repo held at 11.0.16); yeoman-environment 6.0.1 -> CVE, zapier-platform-cli 19 (latest) pins 4.4.3; webpack-dev-server 5.2.4 -> CVE, @electron-forge/plugin-webpack (incl. 8.x alphas) still declares ^4; express/qs + @cypress/request/qs 6.15.2 -> qs CVE for old express 4.22.0/4.22.1 pinned by @mintlify/previewing and verdaccio (verdaccio also pins @cypress/request 3.0.10; all other qs parents resolve safe naturally); next/postcss 8.5.15 -> postcss CVE, every stable next pins 8.4.31 exact (fix only in 16.3.0 canaries; @react-email/ui also pins next 16.2.6); <pkg>/uuid 11.1.1 -> uuid CVE for parents pinning uuid <11 with no fixed release (sockjs dormant since 2021; @cypress/request 3.0.10 via verdaccio; @ptc-org/nestjs-query-typeorm at latest; googleapis 105 -> common 8 drops uuid but needs the googleapis >=152 migration). Preserves the intentional uuid 13.x; @cyntler/react-doc-viewer/ajv 8.20.0 -> CVE, upstream (latest 1.17.1) pins ajv ^7 but never imports it, forcing v8 is safe; wrangler/esbuild + @react-email/ui/esbuild + react-email/esbuild 0.28.1 -> esbuild dev-server path-traversal GHSA-g7r4-m6w7-qqqr (Windows, vulnerable >=0.27.3 <0.28.1, fixed in 0.28.1): wrangler exact-pins esbuild 0.27.3 (still 0.27.3 in latest 4.100.0) and @react-email/ui exact-pins 0.28.0 (still 0.28.0 in latest 6.6.0) with no fixed upstream release; react-email allows ^0.28.0 but the npmMinimalAgeGate down-selects it to the still-vulnerable 0.28.0 until 0.28.1 ages past the gate (published 2026-06-11), so it is pinned forward here. Drop the exact-pin parents once they ship an esbuild >=0.28.1 pin; drop react-email once 0.28.1 clears the age gate (it then resolves to it naturally). Our own twenty-client-sdk raises its esbuild floor to ^0.28.1 directly in its package.json instead of via a resolution",
|
||||
"version": "0.2.1",
|
||||
"nx": {},
|
||||
"scripts": {
|
||||
|
||||
@@ -53,8 +53,8 @@
|
||||
"express": "^4.18.2",
|
||||
"marked": "^5.0.0",
|
||||
"openai": "^4.97.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"zod": "^3.25.76"
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
"@lingui/vite-plugin": "^5.9.5",
|
||||
"@react-email/ui": "6.5.0",
|
||||
"@tiptap/core": "^3.4.2",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@types/react": "^19.2.0",
|
||||
"@types/react-dom": "^19.2.0",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260116.1",
|
||||
"@vitejs/plugin-react-swc": "^4.3.1",
|
||||
"react-email": "6.5.0",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { type JSX } from 'react';
|
||||
import { I18nProvider } from '@lingui/react';
|
||||
import { Container, Html } from '@react-email/components';
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { type JSX } from 'react';
|
||||
import { Button } from '@react-email/components';
|
||||
|
||||
import { emailTheme } from 'src/common-style';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, Container, Row } from '@react-email/components';
|
||||
import React from 'react';
|
||||
import React, { type JSX } from 'react';
|
||||
|
||||
import { emailTheme } from 'src/common-style';
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { type JSX } from 'react';
|
||||
import { Link as EmailLink } from '@react-email/components';
|
||||
|
||||
import { emailTheme } from 'src/common-style';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { type JSX } from 'react';
|
||||
import { Text } from '@react-email/components';
|
||||
|
||||
import { emailTheme } from 'src/common-style';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { type JSX } from 'react';
|
||||
import { Text } from '@react-email/components';
|
||||
|
||||
import { emailTheme } from 'src/common-style';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { type JSX } from 'react';
|
||||
import { Heading } from '@react-email/components';
|
||||
|
||||
import { emailTheme } from 'src/common-style';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { type JSX } from 'react';
|
||||
import { Heading } from '@react-email/components';
|
||||
|
||||
import { emailTheme } from 'src/common-style';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type JSONContent } from '@tiptap/core';
|
||||
import { Fragment, type ReactNode } from 'react';
|
||||
import { type JSX, Fragment, type ReactNode } from 'react';
|
||||
import { TIPTAP_NODE_TYPES, type TipTapNodeType } from 'twenty-shared/utils';
|
||||
import { bulletList } from '@/utils/email-renderer/nodes/bullet-list';
|
||||
import { hardBreak } from '@/utils/email-renderer/nodes/hard-break';
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
"@remote-dom/core": "^1.10.1",
|
||||
"@remote-dom/react": "^1.2.2",
|
||||
"@sniptt/guards": "^0.2.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"zod": "^4.1.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -36,8 +36,8 @@
|
||||
"@storybook/addon-vitest": "^10.2.13",
|
||||
"@storybook/react-vite": "^10.2.13",
|
||||
"@types/node": "^24.0.0",
|
||||
"@types/react": "^18.2.39",
|
||||
"@types/react-dom": "^18.2.15",
|
||||
"@types/react": "^19.2.0",
|
||||
"@types/react-dom": "^19.2.0",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260116.1",
|
||||
"@vitest/browser-playwright": "^4.1.0",
|
||||
"playwright": "^1.60.0",
|
||||
|
||||
+1
-5
@@ -24,11 +24,7 @@ const FormSubmitFrontComponent = () => {
|
||||
|
||||
return (
|
||||
<FrontComponentCard title="form:submit">
|
||||
<form
|
||||
data-testid="subject"
|
||||
action="javascript:void(0);"
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<form data-testid="subject" onSubmit={handleSubmit}>
|
||||
<input
|
||||
data-testid="form-field"
|
||||
type="text"
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ const TableClickFrontComponent = () => {
|
||||
tabIndex={0}
|
||||
style={FILL_RECT_STYLE}
|
||||
>
|
||||
table
|
||||
<caption>table</caption>
|
||||
</table>
|
||||
<span data-testid="front-component-value">{interactionCount}</span>
|
||||
<EventLog entries={entries} />
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ const TableFocusBlurFrontComponent = () => {
|
||||
tabIndex={0}
|
||||
style={FILL_RECT_STYLE}
|
||||
>
|
||||
table
|
||||
<caption>table</caption>
|
||||
</table>
|
||||
<span data-testid="front-component-value">{interactionCount}</span>
|
||||
<EventLog entries={entries} />
|
||||
|
||||
@@ -406,6 +406,7 @@ const createCaretPreservingElement = (
|
||||
export const createHtmlHostWrapper = (htmlTag: string) => {
|
||||
const isVoid = VOID_ELEMENTS.has(htmlTag);
|
||||
const isIframe = htmlTag === 'iframe';
|
||||
const isForm = htmlTag === 'form';
|
||||
|
||||
return ({ children, ...props }: WrapperProps) => {
|
||||
const setEditableFocused = useContext(FrontComponentInputFocusContext);
|
||||
@@ -413,7 +414,29 @@ export const createHtmlHostWrapper = (htmlTag: string) => {
|
||||
|
||||
const forcedProps: Record<string, unknown> | undefined = isIframe
|
||||
? { sandbox: sanitizeIframeSandbox(reactProps.sandbox) }
|
||||
: undefined;
|
||||
: isForm
|
||||
? {
|
||||
// The remote component's onSubmit is forwarded asynchronously across
|
||||
// the remote-dom boundary, so its preventDefault lands too late to
|
||||
// stop a native form submission (which navigates and closes the
|
||||
// page). Guard synchronously on the host while still forwarding the
|
||||
// event to the remote handler. (React 19 also blocks the previous
|
||||
// `action="javascript:void(0)"` guard.)
|
||||
onSubmit: (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
const remoteOnSubmit = reactProps.onSubmit;
|
||||
// The remote prop is untrusted across the remote-dom boundary, so
|
||||
// it may not be a function — guard before invoking.
|
||||
if (typeof remoteOnSubmit === 'function') {
|
||||
(
|
||||
remoteOnSubmit as (
|
||||
event: React.FormEvent<HTMLFormElement>,
|
||||
) => void
|
||||
)(event);
|
||||
}
|
||||
},
|
||||
}
|
||||
: undefined;
|
||||
|
||||
if (
|
||||
htmlTag === 'textarea' ||
|
||||
|
||||
@@ -90,6 +90,7 @@ const jestConfig = {
|
||||
maxWorkers: 3,
|
||||
workerIdleMemoryLimit: '512MB',
|
||||
errorOnDeprecated: true,
|
||||
testTimeout: 30000,
|
||||
};
|
||||
|
||||
export default jestConfig;
|
||||
|
||||
@@ -38,10 +38,11 @@
|
||||
"@cyntler/react-doc-viewer": "^1.17.0",
|
||||
"@dagrejs/dagre": "^1.1.8",
|
||||
"@dnd-kit/react": "^0.3.2",
|
||||
"@dr.pogodin/react-helmet": "^3.2.2",
|
||||
"@floating-ui/react": "^0.24.3",
|
||||
"@graphiql/plugin-explorer": "^1.0.2",
|
||||
"@graphiql/react": "^0.23.0",
|
||||
"@hello-pangea/dnd": "^16.2.0",
|
||||
"@graphiql/plugin-explorer": "^5.1.2",
|
||||
"@graphiql/react": "^0.37.6",
|
||||
"@hello-pangea/dnd": "^18.0.1",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@lingui/core": "^5.9.5",
|
||||
"@lingui/detect-locale": "^5.9.5",
|
||||
@@ -89,7 +90,7 @@
|
||||
"file-saver": "^2.0.5",
|
||||
"framer-motion": "^11.18.0",
|
||||
"fuse.js": "^7.1.0",
|
||||
"graphiql": "^3.1.1",
|
||||
"graphiql": "^5.2.3",
|
||||
"graphql": "16.8.1",
|
||||
"graphql-sse": "^2.5.4",
|
||||
"graphql-tag": "^2.12.6",
|
||||
@@ -113,14 +114,13 @@
|
||||
"papaparse": "^5.4.1",
|
||||
"pluralize": "^8.0.0",
|
||||
"qs": "^6.15.2",
|
||||
"react": "^18.2.0",
|
||||
"react-data-grid": "7.0.0-beta.13",
|
||||
"react-datepicker": "^6.7.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react": "^19.2.0",
|
||||
"react-data-grid": "7.0.0-beta.59",
|
||||
"react-datepicker": "^9.1.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-error-boundary": "^4.0.11",
|
||||
"react-grid-layout": "^1.5.2",
|
||||
"react-helmet-async": "^1.3.0",
|
||||
"react-hook-form": "^7.45.1",
|
||||
"react-hotkeys-hook": "^4.4.4",
|
||||
"react-imask": "^7.6.0",
|
||||
@@ -175,7 +175,6 @@
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/js-cookie": "^3.0.3",
|
||||
"@types/json-logic-js": "^2",
|
||||
"@types/react-datepicker": "^6.2.0",
|
||||
"@types/react-grid-layout": "^1",
|
||||
"@types/uuid": "^9.0.2",
|
||||
"@vitejs/plugin-react-swc": "^4.3.1",
|
||||
|
||||
@@ -9,7 +9,7 @@ import { i18n } from '@lingui/core';
|
||||
import { I18nProvider } from '@lingui/react';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { HelmetProvider } from 'react-helmet-async';
|
||||
import { HelmetProvider } from '@dr.pogodin/react-helmet';
|
||||
import { IconsProvider } from 'twenty-ui-deprecated/display';
|
||||
import { initialI18nActivate } from '~/utils/i18n/initialI18nActivate';
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useC
|
||||
import { useContext, type RefObject } from 'react';
|
||||
|
||||
export type RecordBoardDragSelectProps = {
|
||||
boardRef: RefObject<HTMLDivElement>;
|
||||
boardRef: RefObject<HTMLDivElement | null>;
|
||||
};
|
||||
|
||||
export const RecordBoardDragSelect = ({
|
||||
|
||||
+4
-1
@@ -2,6 +2,7 @@ import { Draggable } from '@hello-pangea/dnd';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { getCssCompatibleDraggableProps } from '@/ui/layout/draggable-list/utils/getCssCompatibleDraggableProps';
|
||||
import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly';
|
||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||
import { RecordBoardCard } from '@/object-record/record-board/record-board-card/components/RecordBoardCard';
|
||||
@@ -71,7 +72,9 @@ export const RecordBoardCardDraggableContainer = ({
|
||||
// oxlint-disable-next-line react/jsx-props-no-spreading
|
||||
{...draggableProvided?.dragHandleProps}
|
||||
// oxlint-disable-next-line react/jsx-props-no-spreading
|
||||
{...draggableProvided?.draggableProps}
|
||||
{...getCssCompatibleDraggableProps(
|
||||
draggableProvided.draggableProps,
|
||||
)}
|
||||
data-selectable-id={recordId}
|
||||
data-select-disable
|
||||
>
|
||||
|
||||
+4
-1
@@ -3,6 +3,7 @@ import { styled } from '@linaria/react';
|
||||
import { useContext } from 'react';
|
||||
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
|
||||
|
||||
import { getCssCompatibleDraggableProps } from '@/ui/layout/draggable-list/utils/getCssCompatibleDraggableProps';
|
||||
import { RecordBoardCardDraggableContainer } from '@/object-record/record-board/record-board-card/components/RecordBoardCardDraggableContainer';
|
||||
|
||||
import { RecordBoardColumnNewRecordButton } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnNewRecordButton';
|
||||
@@ -63,7 +64,9 @@ export const RecordBoardColumnCardsContainer = ({
|
||||
<div
|
||||
ref={draggableProvided.innerRef}
|
||||
// oxlint-disable-next-line react/jsx-props-no-spreading
|
||||
{...draggableProvided.draggableProps}
|
||||
{...getCssCompatibleDraggableProps(
|
||||
draggableProvided.draggableProps,
|
||||
)}
|
||||
></div>
|
||||
)}
|
||||
</Draggable>
|
||||
|
||||
+4
-1
@@ -1,6 +1,7 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { Draggable } from '@hello-pangea/dnd';
|
||||
|
||||
import { getCssCompatibleDraggableProps } from '@/ui/layout/draggable-list/utils/getCssCompatibleDraggableProps';
|
||||
import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject';
|
||||
import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly';
|
||||
import { isFieldMetadataReadOnlyByPermissions } from '@/object-record/read-only/utils/internal/isFieldMetadataReadOnlyByPermissions';
|
||||
@@ -80,7 +81,9 @@ export const RecordCalendarCardDraggableContainer = ({
|
||||
// oxlint-disable-next-line react/jsx-props-no-spreading
|
||||
{...draggableProvided?.dragHandleProps}
|
||||
// oxlint-disable-next-line react/jsx-props-no-spreading
|
||||
{...draggableProvided?.draggableProps}
|
||||
{...getCssCompatibleDraggableProps(
|
||||
draggableProvided.draggableProps,
|
||||
)}
|
||||
data-selectable-id={recordId}
|
||||
>
|
||||
<RecordCalendarCard recordId={recordId} />
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ export const Default: Story = {};
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'ActorFieldDisplay',
|
||||
averageThresholdInMs: 0.2,
|
||||
averageThresholdInMs: 0.4,
|
||||
numberOfRuns: 20,
|
||||
numberOfTestsPerRun: 100,
|
||||
});
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ export const Elipsis: Story = {
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'AddressFieldDisplay',
|
||||
averageThresholdInMs: 0.15,
|
||||
averageThresholdInMs: 0.3,
|
||||
numberOfRuns: 20,
|
||||
numberOfTestsPerRun: 100,
|
||||
});
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ export const Default: Story = {};
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'BooleanFieldDisplay',
|
||||
averageThresholdInMs: 0.15,
|
||||
averageThresholdInMs: 0.3,
|
||||
numberOfRuns: 50,
|
||||
numberOfTestsPerRun: 100,
|
||||
});
|
||||
|
||||
+1
-1
@@ -83,7 +83,7 @@ export const Default: Story = {};
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'ChipFieldDisplay',
|
||||
averageThresholdInMs: 0.3,
|
||||
averageThresholdInMs: 0.6,
|
||||
numberOfRuns: 20,
|
||||
numberOfTestsPerRun: 100,
|
||||
});
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ export const Bazillions: Story = {
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'CurrencyFieldDisplay',
|
||||
averageThresholdInMs: 0.2,
|
||||
averageThresholdInMs: 0.4,
|
||||
numberOfRuns: 50,
|
||||
numberOfTestsPerRun: 100,
|
||||
});
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ export const Elipsis: Story = {
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'DateFieldDisplay',
|
||||
averageThresholdInMs: 0.1,
|
||||
averageThresholdInMs: 0.2,
|
||||
numberOfRuns: 50,
|
||||
numberOfTestsPerRun: 100,
|
||||
});
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ export const Elipsis: Story = {
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'DateTimeFieldDisplay',
|
||||
averageThresholdInMs: 0.35,
|
||||
averageThresholdInMs: 0.7,
|
||||
numberOfRuns: 30,
|
||||
numberOfTestsPerRun: 30,
|
||||
});
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ export const Elipsis: Story = {
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'EmailsFieldDisplay',
|
||||
averageThresholdInMs: 0.5,
|
||||
averageThresholdInMs: 1,
|
||||
numberOfRuns: 50,
|
||||
numberOfTestsPerRun: 100,
|
||||
});
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ export const Elipsis: Story = {
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'FilesFieldDisplay',
|
||||
averageThresholdInMs: 0.8,
|
||||
averageThresholdInMs: 1.6,
|
||||
numberOfRuns: 50,
|
||||
numberOfTestsPerRun: 100,
|
||||
});
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ export const Elipsis: Story = {
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'FullNameFieldDisplay',
|
||||
averageThresholdInMs: 0.5,
|
||||
averageThresholdInMs: 1,
|
||||
numberOfRuns: 50,
|
||||
numberOfTestsPerRun: 100,
|
||||
});
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ export const Elipsis: Story = {
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'LinksFieldDisplay',
|
||||
averageThresholdInMs: 0.5,
|
||||
averageThresholdInMs: 1,
|
||||
numberOfRuns: 50,
|
||||
numberOfTestsPerRun: 100,
|
||||
});
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ export const Elipsis: Story = {
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'MultiSelectFieldDisplay',
|
||||
averageThresholdInMs: 0.2,
|
||||
averageThresholdInMs: 0.4,
|
||||
numberOfRuns: 50,
|
||||
numberOfTestsPerRun: 100,
|
||||
});
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ export const Float: Story = {
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'NumberFieldDisplay',
|
||||
averageThresholdInMs: 0.5,
|
||||
averageThresholdInMs: 1,
|
||||
numberOfRuns: 50,
|
||||
numberOfTestsPerRun: 100,
|
||||
});
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ export const WrongNumber: Story = {
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'PhonesFieldDisplay',
|
||||
averageThresholdInMs: 0.5,
|
||||
averageThresholdInMs: 1,
|
||||
numberOfRuns: 20,
|
||||
numberOfTestsPerRun: 100,
|
||||
});
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ export const Default: Story = {};
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'RatingFieldDisplay',
|
||||
averageThresholdInMs: 0.5,
|
||||
averageThresholdInMs: 1,
|
||||
numberOfRuns: 30,
|
||||
numberOfTestsPerRun: 30,
|
||||
});
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ export const Default: Story = {};
|
||||
// TODO: optimize this component once we have morph many
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'RelationFromManyFieldDisplay',
|
||||
averageThresholdInMs: 1,
|
||||
averageThresholdInMs: 2,
|
||||
numberOfRuns: 20,
|
||||
numberOfTestsPerRun: 100,
|
||||
});
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ export const Default: Story = {};
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'RelationFieldDisplay',
|
||||
averageThresholdInMs: 0.4,
|
||||
averageThresholdInMs: 0.8,
|
||||
numberOfRuns: 20,
|
||||
numberOfTestsPerRun: 100,
|
||||
});
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ export const Elipsis: Story = {
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'SelectFieldDisplay',
|
||||
averageThresholdInMs: 0.2,
|
||||
averageThresholdInMs: 0.4,
|
||||
numberOfRuns: 50,
|
||||
numberOfTestsPerRun: 100,
|
||||
});
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ export const Elipsis: Story = {
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'TextFieldDisplay',
|
||||
averageThresholdInMs: 0.5,
|
||||
averageThresholdInMs: 1,
|
||||
numberOfRuns: 50,
|
||||
numberOfTestsPerRun: 100,
|
||||
});
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ export const MultipleRecordPickerOnClickOutsideEffect = ({
|
||||
containerRef,
|
||||
onClickOutside,
|
||||
}: {
|
||||
containerRef: React.RefObject<HTMLDivElement>;
|
||||
containerRef: React.RefObject<HTMLDivElement | null>;
|
||||
onClickOutside: () => void;
|
||||
}) => {
|
||||
useListenClickOutside({
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import { RecordTableVirtualizedInitialDataLoadEffect } from '@/object-record/rec
|
||||
|
||||
export interface RecordTableBodyEffectsWrapperProps {
|
||||
hasRecordGroups: boolean;
|
||||
tableBodyRef: React.RefObject<HTMLDivElement>;
|
||||
tableBodyRef: React.RefObject<HTMLDivElement | null>;
|
||||
}
|
||||
|
||||
export const RecordTableBodyEffectsWrapper = ({
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ const StyledTableContainer = styled.div`
|
||||
`;
|
||||
|
||||
export interface RecordTableContentProps {
|
||||
tableBodyRef: React.RefObject<HTMLDivElement>;
|
||||
tableBodyRef: React.RefObject<HTMLDivElement | null>;
|
||||
handleDragSelectionStart: () => void;
|
||||
handleDragSelectionEnd: () => void;
|
||||
hasRecordGroups: boolean;
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ const StyledEmptyStateContainer = styled.div<{ width: number }>`
|
||||
`;
|
||||
|
||||
export interface RecordTableEmptyProps {
|
||||
tableBodyRef: React.RefObject<HTMLDivElement>;
|
||||
tableBodyRef: React.RefObject<HTMLDivElement | null>;
|
||||
}
|
||||
|
||||
export const RecordTableEmpty = ({ tableBodyRef }: RecordTableEmptyProps) => {
|
||||
|
||||
+1
-1
@@ -280,7 +280,7 @@ export const Default: Story = {};
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'RecordTableCell',
|
||||
averageThresholdInMs: 0.3,
|
||||
averageThresholdInMs: 0.6,
|
||||
numberOfRuns: 50,
|
||||
numberOfTestsPerRun: 200,
|
||||
warmUpRounds: 20,
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentTyp
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
type RecordTableBodyFocusClickOutsideEffectProps = {
|
||||
tableBodyRef: React.RefObject<HTMLDivElement>;
|
||||
tableBodyRef: React.RefObject<HTMLDivElement | null>;
|
||||
};
|
||||
|
||||
export const RecordTableBodyFocusClickOutsideEffect = ({
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import { type RefObject } from 'react';
|
||||
export const PageLayoutGridLayoutDragSelector = ({
|
||||
gridContainerRef,
|
||||
}: {
|
||||
gridContainerRef: RefObject<HTMLDivElement>;
|
||||
gridContainerRef: RefObject<HTMLDivElement | null>;
|
||||
}) => {
|
||||
const { startPageLayoutDragSelection } = useStartPageLayoutDragSelection();
|
||||
const { changePageLayoutDragSelection } = useChangePageLayoutDragSelection();
|
||||
|
||||
+3
-1
@@ -17,6 +17,8 @@ import { type ReactNode, useId } from 'react';
|
||||
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
|
||||
import { useIsMobile } from 'twenty-ui-deprecated/utilities';
|
||||
|
||||
import { getCssCompatibleDraggableProps } from '@/ui/layout/draggable-list/utils/getCssCompatibleDraggableProps';
|
||||
|
||||
const StyledVerticalListContainer = styled.div<{
|
||||
variant: PageLayoutVerticalListViewerVariant;
|
||||
shouldUseWhiteBackground: boolean;
|
||||
@@ -102,7 +104,7 @@ export const PageLayoutVerticalListEditor = ({
|
||||
<StyledDraggableWrapper
|
||||
ref={provided.innerRef}
|
||||
// oxlint-disable-next-line react/jsx-props-no-spreading
|
||||
{...provided.draggableProps}
|
||||
{...getCssCompatibleDraggableProps(provided.draggableProps)}
|
||||
isDragging={snapshot.isDragging}
|
||||
>
|
||||
{/* oxlint-disable-next-line react/jsx-props-no-spreading */}
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@ import { styled } from '@linaria/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem';
|
||||
import { getCssCompatibleDraggableProps } from '@/ui/layout/draggable-list/utils/getCssCompatibleDraggableProps';
|
||||
|
||||
import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem';
|
||||
import { FieldsConfigurationFieldEditor } from '@/page-layout/widgets/fields/components/FieldsConfigurationFieldEditor';
|
||||
@@ -134,7 +135,7 @@ export const FieldsConfigurationGroupEditor = ({
|
||||
<StyledGroupContainer
|
||||
ref={draggableProvided.innerRef}
|
||||
// oxlint-disable-next-line react/jsx-props-no-spreading
|
||||
{...draggableProvided.draggableProps}
|
||||
{...getCssCompatibleDraggableProps(draggableProvided.draggableProps)}
|
||||
isDragging={isDragging}
|
||||
>
|
||||
{/* oxlint-disable-next-line react/jsx-props-no-spreading */}
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ type BarChartBaseLayerEffectProps = {
|
||||
showGrid: boolean;
|
||||
highlightedLegendId: string | null;
|
||||
allowDataTransitions: boolean;
|
||||
canvasRef: RefObject<HTMLCanvasElement>;
|
||||
canvasRef: RefObject<HTMLCanvasElement | null>;
|
||||
};
|
||||
|
||||
type AnimationState = {
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ type BarChartHoverLayerEffectProps = {
|
||||
chartHeight: number;
|
||||
margins: ChartMargins;
|
||||
layout: BarChartLayout;
|
||||
canvasRef: RefObject<HTMLCanvasElement>;
|
||||
canvasRef: RefObject<HTMLCanvasElement | null>;
|
||||
};
|
||||
|
||||
export const BarChartHoverLayerEffect = ({
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import { type RefObject } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type BarChartTooltipProps = {
|
||||
containerRef: RefObject<HTMLDivElement>;
|
||||
containerRef: RefObject<HTMLDivElement | null>;
|
||||
dataByIndexValue: Map<string, BarChartDatum>;
|
||||
enrichedKeys: BarChartEnrichedKey[];
|
||||
formatOptions: GraphValueFormatOptions;
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ import { type RefObject } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type GraphLineChartTooltipProps = {
|
||||
containerRef: RefObject<HTMLDivElement>;
|
||||
containerRef: RefObject<HTMLDivElement | null>;
|
||||
enrichedSeries: LineChartEnrichedSeries[];
|
||||
formatOptions: GraphValueFormatOptions;
|
||||
isStacked?: boolean;
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ import { type RefObject } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type GraphPieChartTooltipProps = {
|
||||
containerRef: RefObject<HTMLDivElement>;
|
||||
containerRef: RefObject<HTMLDivElement | null>;
|
||||
enrichedData: PieChartEnrichedData[];
|
||||
formatOptions: GraphValueFormatOptions;
|
||||
displayType?: string;
|
||||
|
||||
+13
-2
@@ -164,7 +164,11 @@ export const SettingsObjectRelationItemTableRow = ({
|
||||
return (
|
||||
<TableRow
|
||||
gridTemplateColumns={OBJECT_RELATION_TABLE_ROW_GRID_TEMPLATE_COLUMNS}
|
||||
to={linkToNavigate}
|
||||
// The row can't be a Link: it contains a nested link to the related
|
||||
// object, and <a> inside <a> is invalid HTML (React 19 errors on it).
|
||||
// oxlint-disable-next-line twenty/no-navigate-prefer-link
|
||||
onClick={navigateToFieldEdit}
|
||||
cursor="pointer"
|
||||
>
|
||||
<TableCell
|
||||
color={themeCssVariables.font.color.primary}
|
||||
@@ -230,7 +234,14 @@ export const SettingsObjectRelationItemTableRow = ({
|
||||
padding={`0 ${themeCssVariables.spacing[1]} 0 ${themeCssVariables.spacing[2]}`}
|
||||
>
|
||||
{fieldMetadataItem.isActive ? (
|
||||
<UndecoratedLink to={linkToNavigate}>
|
||||
// The row navigates via onClick (it can't be a Link because it
|
||||
// contains a nested link to the related object). This chevron is a
|
||||
// real link to the same destination so keyboard users can still reach
|
||||
// field edit; stopPropagation avoids firing the row onClick too.
|
||||
<UndecoratedLink
|
||||
to={linkToNavigate}
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<StyledIconChevronRightContainer>
|
||||
<IconChevronRight
|
||||
size={theme.icon.size.md}
|
||||
|
||||
+2
-2
@@ -6,10 +6,10 @@ import { PlaygroundSchemas } from '@/settings/playground/types/PlaygroundSchemas
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { styled } from '@linaria/react';
|
||||
import { explorerPlugin } from '@graphiql/plugin-explorer';
|
||||
import '@graphiql/plugin-explorer/dist/style.css';
|
||||
import '@graphiql/plugin-explorer/style.css';
|
||||
import { createGraphiQLFetcher } from '@graphiql/toolkit';
|
||||
import { GraphiQL } from 'graphiql';
|
||||
import 'graphiql/graphiql.css';
|
||||
import 'graphiql/style.css';
|
||||
import { useContext } from 'react';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import {
|
||||
|
||||
+3
-1
@@ -1,6 +1,8 @@
|
||||
import { Draggable } from '@hello-pangea/dnd';
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
import { getCssCompatibleDraggableProps } from '@/ui/layout/draggable-list/utils/getCssCompatibleDraggableProps';
|
||||
|
||||
type SidePanelAddToNavigationDraggablePlaceholderProps = {
|
||||
index: number;
|
||||
children: ReactNode;
|
||||
@@ -19,7 +21,7 @@ export const SidePanelAddToNavigationDraggablePlaceholder = ({
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
// oxlint-disable-next-line react/jsx-props-no-spreading
|
||||
{...provided.draggableProps}
|
||||
{...getCssCompatibleDraggableProps(provided.draggableProps)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import { useEffect } from 'react';
|
||||
import { SidePanelPages } from 'twenty-shared/types';
|
||||
|
||||
type SidePanelTopBarInputFocusEffectProps = {
|
||||
inputRef: React.RefObject<HTMLInputElement>;
|
||||
inputRef: React.RefObject<HTMLInputElement | null>;
|
||||
};
|
||||
|
||||
export const SidePanelTopBarInputFocusEffect = ({
|
||||
|
||||
+7
-8
@@ -1,7 +1,6 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { useContext } from 'react';
|
||||
// @ts-expect-error // Todo: remove usage of react-data-grid
|
||||
import DataGrid, { type DataGridProps } from 'react-data-grid';
|
||||
import { DataGrid, type DataGridProps } from 'react-data-grid';
|
||||
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
||||
import {
|
||||
ThemeContext,
|
||||
@@ -118,7 +117,7 @@ type SpreadsheetImportTableProps<Data> = Pick<
|
||||
| 'rows'
|
||||
> &
|
||||
Partial<
|
||||
Pick<DataGridProps<Data>, 'onRowClick' | 'components' | 'onRowsChange'>
|
||||
Pick<DataGridProps<Data>, 'onCellClick' | 'renderers' | 'onRowsChange'>
|
||||
> & {
|
||||
className?: string;
|
||||
rowHeight?: number;
|
||||
@@ -128,12 +127,12 @@ type SpreadsheetImportTableProps<Data> = Pick<
|
||||
export const SpreadsheetImportTable = <Data,>({
|
||||
className,
|
||||
columns,
|
||||
components,
|
||||
renderers,
|
||||
headerRowHeight,
|
||||
rowKeyGetter,
|
||||
rows,
|
||||
onRowsChange,
|
||||
onRowClick,
|
||||
onCellClick,
|
||||
onSelectedRowsChange,
|
||||
selectedRows,
|
||||
}: SpreadsheetImportTableProps<Data>) => {
|
||||
@@ -145,7 +144,7 @@ export const SpreadsheetImportTable = <Data,>({
|
||||
if (!rows?.length || !columns?.length) return null;
|
||||
|
||||
return (
|
||||
<StyledDataGridContainer headerRowHeight={headerRowHeight}>
|
||||
<StyledDataGridContainer headerRowHeight={headerRowHeight ?? undefined}>
|
||||
<DataGrid
|
||||
direction={rtl ? 'rtl' : 'ltr'}
|
||||
rowHeight={40}
|
||||
@@ -156,8 +155,8 @@ export const SpreadsheetImportTable = <Data,>({
|
||||
rowKeyGetter,
|
||||
onRowsChange,
|
||||
rows,
|
||||
components,
|
||||
onRowClick,
|
||||
renderers,
|
||||
onCellClick,
|
||||
onSelectedRowsChange,
|
||||
selectedRows,
|
||||
}}
|
||||
|
||||
+4
-5
@@ -1,6 +1,5 @@
|
||||
// @ts-expect-error // Todo: remove usage of react-data-grid
|
||||
// prettier-ignore
|
||||
import { useRowSelection, type Column, type FormatterProps } from 'react-data-grid';
|
||||
import { useRowSelection, type Column, type RenderCellProps } from 'react-data-grid';
|
||||
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { type ImportedRow } from '@/spreadsheet-import/types';
|
||||
@@ -8,10 +7,10 @@ import { Radio } from 'twenty-ui-deprecated/input';
|
||||
|
||||
const SELECT_COLUMN_KEY = 'select-row';
|
||||
|
||||
type SelectFormatterProps = FormatterProps<unknown>;
|
||||
type SelectFormatterProps = RenderCellProps<unknown>;
|
||||
|
||||
const SelectFormatter = (props: SelectFormatterProps) => {
|
||||
const [isRowSelected, onRowSelectionChange] = useRowSelection();
|
||||
const { isRowSelected, onRowSelectionChange } = useRowSelection();
|
||||
|
||||
return (
|
||||
<Radio
|
||||
@@ -38,7 +37,7 @@ export const SelectColumn: Column<any, any> = {
|
||||
sortable: false,
|
||||
frozen: true,
|
||||
cellClass: 'rdg-radio',
|
||||
formatter: SelectFormatter,
|
||||
renderCell: SelectFormatter,
|
||||
};
|
||||
|
||||
export const generateSelectionColumns = (data: ImportedRow[]) => {
|
||||
|
||||
+8
-7
@@ -28,16 +28,17 @@ export const SelectHeaderTable = ({
|
||||
rows={importedRows}
|
||||
columns={columns}
|
||||
selectedRows={selectedRowIndexes}
|
||||
onSelectedRowsChange={(newRowIndexes: number[]) => {
|
||||
newRowIndexes.forEach((value) => {
|
||||
if (!selectedRowIndexes.has(value)) {
|
||||
setSelectedRowIndexes(new Set([value]));
|
||||
onSelectedRowsChange={(newSelectedRows) => {
|
||||
for (const value of newSelectedRows) {
|
||||
const rowIndex = Number(value);
|
||||
if (!selectedRowIndexes.has(rowIndex)) {
|
||||
setSelectedRowIndexes(new Set([rowIndex]));
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}}
|
||||
onRowClick={(row: any) => {
|
||||
setSelectedRowIndexes(new Set([importedRows.indexOf(row)]));
|
||||
onCellClick={(args) => {
|
||||
setSelectedRowIndexes(new Set([importedRows.indexOf(args.row)]));
|
||||
}}
|
||||
headerRowHeight={0}
|
||||
/>
|
||||
|
||||
+2
-3
@@ -23,7 +23,6 @@ import {
|
||||
} from 'react';
|
||||
import { ModalContent } from 'twenty-ui-deprecated/layout';
|
||||
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
|
||||
// @ts-expect-error Todo: remove usage of react-data-grid`
|
||||
import { type RowsChangeData } from 'react-data-grid';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconTrash } from 'twenty-ui-deprecated/display';
|
||||
@@ -200,7 +199,7 @@ export const ValidationStep = ({
|
||||
if (!hasBeenImported) return null;
|
||||
return column;
|
||||
})
|
||||
.filter(Boolean),
|
||||
.filter(isDefined),
|
||||
[fields, importedColumns],
|
||||
);
|
||||
|
||||
@@ -306,7 +305,7 @@ export const ValidationStep = ({
|
||||
columns={columns}
|
||||
selectedRows={selectedRows}
|
||||
onSelectedRowsChange={setSelectedRows as any} // TODO: replace 'any'
|
||||
components={{
|
||||
renderers={{
|
||||
noRowsFallback: (
|
||||
<StyledNoRowsContainer>
|
||||
{filterByErrors
|
||||
|
||||
+5
-9
@@ -1,7 +1,6 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
|
||||
// @ts-expect-error // Todo: remove usage of react-data-grid
|
||||
import { type Column, useRowSelection } from 'react-data-grid';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
@@ -86,9 +85,9 @@ export const generateColumns = (
|
||||
resizable: false,
|
||||
sortable: false,
|
||||
frozen: true,
|
||||
formatter: (props: any) => {
|
||||
renderCell: (props: any) => {
|
||||
// oxlint-disable-next-line react-hooks/rules-of-hooks
|
||||
const [isRowSelected, onRowSelectionChange] = useRowSelection();
|
||||
const { isRowSelected, onRowSelectionChange } = useRowSelection();
|
||||
|
||||
return (
|
||||
<StyledCheckboxContainer>
|
||||
@@ -116,7 +115,7 @@ export const generateColumns = (
|
||||
name: column.label,
|
||||
minWidth: 150,
|
||||
resizable: true,
|
||||
headerRenderer: () => (
|
||||
renderHeaderCell: () => (
|
||||
<StyledHeaderContainer>
|
||||
<StyledHeaderLabel id={formatSafeId(column.key)}>
|
||||
{column.label}
|
||||
@@ -136,7 +135,7 @@ export const generateColumns = (
|
||||
),
|
||||
editable: column.fieldType.type !== 'checkbox',
|
||||
// Todo: remove usage of react-data-grid
|
||||
editor: ({ row, onRowChange, onClose }: any) => {
|
||||
renderEditCell: ({ row, onRowChange, onClose }: any) => {
|
||||
const columnKey = column.key as keyof (ImportedStructuredRow &
|
||||
ImportedStructuredRowMetadata);
|
||||
let component;
|
||||
@@ -166,11 +165,8 @@ export const generateColumns = (
|
||||
|
||||
return <StyledInputContainer>{component}</StyledInputContainer>;
|
||||
},
|
||||
editorOptions: {
|
||||
editOnClick: true,
|
||||
},
|
||||
// Todo: remove usage of react-data-grid
|
||||
formatter: ({ row, onRowChange }: { row: any; onRowChange: any }) => {
|
||||
renderCell: ({ row, onRowChange }: { row: any; onRowChange: any }) => {
|
||||
const columnKey = column.key as keyof (ImportedStructuredRow &
|
||||
ImportedStructuredRowMetadata);
|
||||
let component;
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
|
||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { lazy, Suspense, useEffect } from 'react';
|
||||
import { type JSX, lazy, Suspense, useEffect } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconDownload, IconX } from 'twenty-ui-deprecated/display';
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ export const Default: Story = {};
|
||||
|
||||
export const Performance = getProfilingStory({
|
||||
componentName: 'EllipsisDisplay',
|
||||
averageThresholdInMs: 0.1,
|
||||
averageThresholdInMs: 0.2,
|
||||
numberOfRuns: 20,
|
||||
numberOfTestsPerRun: 10,
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import { type FieldAddressDraftValue } from '@/object-record/record-field/ui/typ
|
||||
|
||||
export const useFocusManagement = (
|
||||
inputRefs: {
|
||||
[key in keyof FieldAddressDraftValue]?: RefObject<HTMLInputElement>;
|
||||
[key in keyof FieldAddressDraftValue]?: RefObject<HTMLInputElement | null>;
|
||||
},
|
||||
internalValue: FieldAddressDraftValue,
|
||||
onTab?: (newAddress: FieldAddressDraftValue) => void,
|
||||
|
||||
+25
-8
@@ -1,6 +1,6 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { lazy, Suspense, useContext, type ComponentType } from 'react';
|
||||
import type { ReactDatePickerProps as ReactDatePickerLibProps } from 'react-datepicker';
|
||||
import type { DatePickerProps as ReactDatePickerLibProps } from 'react-datepicker';
|
||||
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
|
||||
|
||||
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
|
||||
@@ -334,10 +334,19 @@ type DatePickerProps = {
|
||||
hideCalendar?: boolean;
|
||||
};
|
||||
|
||||
type DatePickerPropsType = ReactDatePickerLibProps<
|
||||
boolean | undefined,
|
||||
boolean | undefined
|
||||
>;
|
||||
// react-datepicker v9 types its props as a discriminated union keyed on
|
||||
// selectsRange/selectsMultiple. We drive selectsMultiple dynamically, which TS
|
||||
// cannot narrow to a single union branch, so collapse the discriminants to plain
|
||||
// optionals (selectedDates is accepted but ignored by the library at runtime).
|
||||
type DatePickerPropsType = Omit<
|
||||
ReactDatePickerLibProps,
|
||||
'selectsRange' | 'selectsMultiple' | 'onChange' | 'formatMultipleDates'
|
||||
> & {
|
||||
selectsRange?: boolean;
|
||||
selectsMultiple?: boolean;
|
||||
selectedDates?: Date[];
|
||||
onChange?: (date: Date | null) => void;
|
||||
};
|
||||
|
||||
const ReactDatePicker = lazy<ComponentType<DatePickerPropsType>>(() =>
|
||||
import('react-datepicker').then((mod) => ({
|
||||
@@ -410,13 +419,19 @@ export const DatePicker = ({
|
||||
onChange?.(newDate?.toString() ?? null);
|
||||
};
|
||||
|
||||
const handleDateChange = (datePicked: Date) => {
|
||||
const handleDateChange = (datePicked: Date | null) => {
|
||||
if (!isDefined(datePicked)) {
|
||||
return;
|
||||
}
|
||||
const plainDatePicked = turnJSDateToPlainDate(datePicked);
|
||||
|
||||
onChange?.(plainDatePicked.toString());
|
||||
};
|
||||
|
||||
const handleDateSelect = (datePicked: Date) => {
|
||||
const handleDateSelect = (datePicked: Date | null) => {
|
||||
if (!isDefined(datePicked)) {
|
||||
return;
|
||||
}
|
||||
const plainDatePicked = turnJSDateToPlainDate(datePicked);
|
||||
|
||||
handleClose?.(plainDatePicked.toString());
|
||||
@@ -493,7 +508,9 @@ export const DatePicker = ({
|
||||
openToDate={dateForDatePicker ?? undefined}
|
||||
disabledKeyboardNavigation
|
||||
onChange={handleDateChange}
|
||||
calendarStartDay={calendarStartDay}
|
||||
calendarStartDay={
|
||||
calendarStartDay as 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
|
||||
}
|
||||
renderCustomHeader={({
|
||||
prevMonthButtonDisabled,
|
||||
nextMonthButtonDisabled,
|
||||
|
||||
+13
-8
@@ -1,6 +1,6 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { Suspense, lazy, useContext, type ComponentType } from 'react';
|
||||
import type { ReactDatePickerProps as ReactDatePickerLibProps } from 'react-datepicker';
|
||||
import type { DatePickerProps as ReactDatePickerLibProps } from 'react-datepicker';
|
||||
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
|
||||
|
||||
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
|
||||
@@ -309,10 +309,7 @@ type DatePickerWithoutCalendarProps = {
|
||||
keyboardEventsDisabled?: boolean;
|
||||
};
|
||||
|
||||
type DatePickerPropsType = ReactDatePickerLibProps<
|
||||
boolean | undefined,
|
||||
boolean | undefined
|
||||
>;
|
||||
type DatePickerPropsType = ReactDatePickerLibProps;
|
||||
|
||||
const ReactDatePicker = lazy<ComponentType<DatePickerPropsType>>(() =>
|
||||
import('react-datepicker').then((mod) => ({
|
||||
@@ -370,13 +367,19 @@ export const DatePickerWithoutCalendar = ({
|
||||
onChange?.(newDate?.toString() ?? null);
|
||||
};
|
||||
|
||||
const handleDateChange = (datePicked: Date) => {
|
||||
const handleDateChange = (datePicked: Date | null) => {
|
||||
if (!isDefined(datePicked)) {
|
||||
return;
|
||||
}
|
||||
const plainDatePicked = turnJSDateToPlainDate(datePicked);
|
||||
|
||||
onChange?.(plainDatePicked.toString());
|
||||
};
|
||||
|
||||
const handleDateSelect = (datePicked: Date) => {
|
||||
const handleDateSelect = (datePicked: Date | null) => {
|
||||
if (!isDefined(datePicked)) {
|
||||
return;
|
||||
}
|
||||
const plainDatePicked = turnJSDateToPlainDate(datePicked);
|
||||
|
||||
handleClose?.(plainDatePicked.toString());
|
||||
@@ -443,7 +446,9 @@ export const DatePickerWithoutCalendar = ({
|
||||
openToDate={dateForDatePicker ?? undefined}
|
||||
disabledKeyboardNavigation
|
||||
onChange={handleDateChange}
|
||||
calendarStartDay={calendarStartDay}
|
||||
calendarStartDay={
|
||||
calendarStartDay as 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
|
||||
}
|
||||
renderCustomHeader={({
|
||||
prevMonthButtonDisabled,
|
||||
nextMonthButtonDisabled,
|
||||
|
||||
+25
-8
@@ -15,7 +15,7 @@ import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { styled } from '@linaria/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { lazy, Suspense, useContext, type ComponentType } from 'react';
|
||||
import type { ReactDatePickerProps as ReactDatePickerLibProps } from 'react-datepicker';
|
||||
import type { DatePickerProps as ReactDatePickerLibProps } from 'react-datepicker';
|
||||
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
|
||||
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
@@ -346,10 +346,19 @@ type DateTimePickerProps = {
|
||||
timeZone?: string;
|
||||
};
|
||||
|
||||
type DatePickerPropsType = ReactDatePickerLibProps<
|
||||
boolean | undefined,
|
||||
boolean | undefined
|
||||
>;
|
||||
// react-datepicker v9 types its props as a discriminated union keyed on
|
||||
// selectsRange/selectsMultiple. We drive selectsMultiple dynamically, which TS
|
||||
// cannot narrow to a single union branch, so collapse the discriminants to plain
|
||||
// optionals (selectedDates is accepted but ignored by the library at runtime).
|
||||
type DatePickerPropsType = Omit<
|
||||
ReactDatePickerLibProps,
|
||||
'selectsRange' | 'selectsMultiple' | 'onChange' | 'formatMultipleDates'
|
||||
> & {
|
||||
selectsRange?: boolean;
|
||||
selectsMultiple?: boolean;
|
||||
selectedDates?: Date[];
|
||||
onChange?: (date: Date | null) => void;
|
||||
};
|
||||
|
||||
const ReactDatePicker = lazy<ComponentType<DatePickerPropsType>>(() =>
|
||||
import('react-datepicker').then((mod) => ({
|
||||
@@ -438,13 +447,19 @@ export const DateTimePicker = ({
|
||||
onChange?.(newZonedDateTime);
|
||||
};
|
||||
|
||||
const handleDateChange = (newDate: Date) => {
|
||||
const handleDateChange = (newDate: Date | null) => {
|
||||
if (!isDefined(newDate)) {
|
||||
return;
|
||||
}
|
||||
const { zonedDateTime } = getZonedDateTimeFromDatePicked(newDate);
|
||||
|
||||
onChange?.(zonedDateTime);
|
||||
};
|
||||
|
||||
const handleDateSelect = (newDate: Date) => {
|
||||
const handleDateSelect = (newDate: Date | null) => {
|
||||
if (!isDefined(newDate)) {
|
||||
return;
|
||||
}
|
||||
const { zonedDateTime } = getZonedDateTimeFromDatePicked(newDate);
|
||||
|
||||
handleClose?.(zonedDateTime);
|
||||
@@ -524,7 +539,9 @@ export const DateTimePicker = ({
|
||||
openToDate={shiftedDateForReactDatePicker}
|
||||
disabledKeyboardNavigation
|
||||
onChange={handleDateChange}
|
||||
calendarStartDay={calendarStartDayNumber}
|
||||
calendarStartDay={
|
||||
calendarStartDayNumber as 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
|
||||
}
|
||||
renderCustomHeader={({
|
||||
prevMonthButtonDisabled,
|
||||
nextMonthButtonDisabled,
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { Draggable } from '@hello-pangea/dnd';
|
||||
import { isFunction } from '@sniptt/guards';
|
||||
import { useContext } from 'react';
|
||||
import { type JSX, useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { ThemeContext } from 'twenty-ui-deprecated/theme-constants';
|
||||
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import { type DraggableProvidedDraggableProps } from '@hello-pangea/dnd';
|
||||
import { type CSSProperties } from 'react';
|
||||
|
||||
type CssCompatibleDraggableProps = Omit<
|
||||
DraggableProvidedDraggableProps,
|
||||
'style'
|
||||
> & { style?: CSSProperties };
|
||||
|
||||
// @hello-pangea/dnd types draggableProps.style as DraggingStyle | NotDraggingStyle —
|
||||
// closed interfaces that do not satisfy the `--radix-${string}` index signature
|
||||
// @radix-ui/react-popper augments onto React.CSSProperties. Widen the style so the
|
||||
// props can be spread onto a styled element without a per-call-site cast.
|
||||
export const getCssCompatibleDraggableProps = (
|
||||
draggableProps: DraggableProvidedDraggableProps,
|
||||
): CssCompatibleDraggableProps => draggableProps as CssCompatibleDraggableProps;
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
import { type JSX } from 'react';
|
||||
import { PageHeader } from '@/ui/layout/page/components/PageHeader';
|
||||
import { PAGE_BAR_MIN_HEIGHT } from '@/ui/layout/page/constants/PageBarMinHeight';
|
||||
import {
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useLis
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
type ModalHotkeysAndClickOutsideEffectProps = {
|
||||
modalRef: React.RefObject<HTMLDivElement>;
|
||||
modalRef: React.RefObject<HTMLDivElement | null>;
|
||||
onEnter?: () => void;
|
||||
isClosable?: boolean;
|
||||
onClose?: () => void;
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { type FocusEvent, useRef } from 'react';
|
||||
import { type JSX, type FocusEvent, useRef } from 'react';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomStat
|
||||
import { styled } from '@linaria/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { type ReactNode, useContext } from 'react';
|
||||
import { type JSX, type ReactNode, useContext } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Pill } from 'twenty-ui-deprecated/components';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createContext } from 'react';
|
||||
import { type JSX, createContext } from 'react';
|
||||
|
||||
import { useSystemColorScheme } from '@/ui/theme/hooks/useSystemColorScheme';
|
||||
import { persistedColorSchemeState } from '@/ui/theme/states/persistedColorSchemeState';
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { type RefObject, useEffect } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type NodeDimensionEffectProps = {
|
||||
elementRef: RefObject<HTMLElement>;
|
||||
elementRef: RefObject<HTMLElement | null>;
|
||||
onDimensionChange: (dimensions: { width: number; height: number }) => void;
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import { type SelectionBox } from '@/ui/utilities/drag-select/types/SelectionBox
|
||||
import { isValidSelectionStart } from '@/ui/utilities/drag-select/utils/selectionBoxValidation';
|
||||
|
||||
type DragSelectProps = {
|
||||
selectableItemsContainerRef: RefObject<HTMLElement>;
|
||||
selectableItemsContainerRef: RefObject<HTMLElement | null>;
|
||||
onDragSelectionChange: (id: string, selected: boolean) => void;
|
||||
onDragSelectionStart?: (event: MouseEvent | TouchEvent) => void;
|
||||
onDragSelectionEnd?: (event: MouseEvent | TouchEvent) => void;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState';
|
||||
import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { getImageAbsoluteURI } from 'twenty-shared/utils';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
|
||||
type PageTitleProps = {
|
||||
title: string;
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
const CLICK_OUTSIDE_DEBUG_MODE = false;
|
||||
|
||||
export type ClickOutsideListenerProps<T extends Element> = {
|
||||
refs: Array<RefObject<T>>;
|
||||
refs: Array<RefObject<T | null>>;
|
||||
excludedClickOutsideIds?: string[];
|
||||
callback: (event: MouseEvent | TouchEvent) => void;
|
||||
listenerId: string;
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import { workflowDiagramRightClickMenuPositionState } from '@/workflow/workflow-
|
||||
export const WorkflowDiagramRightClickCommandMenuClickOutsideEffect = ({
|
||||
rightClickCommandMenuRef,
|
||||
}: {
|
||||
rightClickCommandMenuRef: React.RefObject<HTMLDivElement>;
|
||||
rightClickCommandMenuRef: React.RefObject<HTMLDivElement | null>;
|
||||
}) => {
|
||||
const setWorkflowDiagramRightClickMenuPosition = useSetAtomComponentState(
|
||||
workflowDiagramRightClickMenuPositionState,
|
||||
|
||||
@@ -23,7 +23,7 @@ import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWork
|
||||
import { useGetPublicWorkspaceDataByDomain } from '@/domain-manager/hooks/useGetPublicWorkspaceDataByDomain';
|
||||
import { useIsCurrentLocationOnAWorkspace } from '@/domain-manager/hooks/useIsCurrentLocationOnAWorkspace';
|
||||
import { useIsCurrentLocationOnDefaultDomain } from '@/domain-manager/hooks/useIsCurrentLocationOnDefaultDomain';
|
||||
import { useMemo } from 'react';
|
||||
import { type JSX, useMemo } from 'react';
|
||||
|
||||
import { SignInUpGlobalScopeFormEffect } from '@/auth/sign-in-up/components/internal/SignInUpGlobalScopeFormEffect';
|
||||
import { SignInUpTwoFactorAuthenticationProvision } from '@/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationProvision';
|
||||
|
||||
+6
@@ -12,6 +12,12 @@ import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
const meta: Meta<typeof SettingsGraphQLPlayground> = {
|
||||
title: 'Pages/Settings/Playground/GraphQLPlayground',
|
||||
component: SettingsGraphQLPlayground,
|
||||
// graphiql 5 renders a Monaco editor that cannot spin up web workers in the
|
||||
// headless storybook test runner and throws unhandled async errors there
|
||||
// (Cannot read properties of undefined 'toUrl'). Exclude this 3rd-party-tool
|
||||
// wrapper from the vitest run; it is still available in the Storybook UI.
|
||||
// (!test removes the auto-applied `test` tag the vitest addon filters on.)
|
||||
tags: ['!test'],
|
||||
decorators: [
|
||||
(Story) => {
|
||||
jotaiStore.set(playgroundApiKeyState.atom, {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { type JSX } from 'react';
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
const StyledLayout = styled.div<{ width?: number }>`
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { type JSX } from 'react';
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
const StyledLayout = styled.div`
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { type JSX } from 'react';
|
||||
import { type Decorator } from '@storybook/react-vite';
|
||||
import {
|
||||
createMemoryRouter,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type Decorator } from '@storybook/react-vite';
|
||||
import { HelmetProvider } from 'react-helmet-async';
|
||||
import { HelmetProvider } from '@dr.pogodin/react-helmet';
|
||||
|
||||
export const HelmetProviderDecorator: Decorator = (Story) => {
|
||||
return (
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ApolloProvider } from '@apollo/client/react';
|
||||
import { loadDevMessages } from '@apollo/client/dev';
|
||||
import { type Decorator } from '@storybook/react-vite';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { HelmetProvider } from 'react-helmet-async';
|
||||
import { HelmetProvider } from '@dr.pogodin/react-helmet';
|
||||
import {
|
||||
createMemoryRouter,
|
||||
createRoutesFromElements,
|
||||
@@ -18,7 +18,7 @@ import { MinimalMetadataGater } from '@/metadata-store/components/MinimalMetadat
|
||||
import { UserMetadataProviderInitialEffect } from '@/metadata-store/effect-components/UserMetadataProviderInitialEffect';
|
||||
import { MockedMetadataLoadEffect } from '~/testing/decorators/MockedMetadataLoadEffect';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { useState } from 'react';
|
||||
import { type JSX, useState } from 'react';
|
||||
import { ClientConfigProvider } from '~/modules/client-config/components/ClientConfigProvider';
|
||||
import { mockedApolloClient } from '~/testing/mockedApolloClient';
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { type JSX } from 'react';
|
||||
export type ComponentParameters<
|
||||
ComponentTypeToExtract extends (args: object) => JSX.Element,
|
||||
> = Parameters<ComponentTypeToExtract>[0];
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
"inquirer": "^14.0.0",
|
||||
"jsonc-parser": "^3.2.0",
|
||||
"preact": "^10.28.3",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"twenty-client-sdk": "workspace:*",
|
||||
"typescript": "^5.9.3",
|
||||
@@ -93,8 +93,8 @@
|
||||
"devDependencies": {
|
||||
"@prettier/sync": "^0.5.2",
|
||||
"@types/node": "^24.0.0",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"@types/react": "^19.2.0",
|
||||
"@types/react-dom": "^19.2.0",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260116.1",
|
||||
"@vitest/coverage-v8": "^4.0.18",
|
||||
"prettier": "^3.1.1",
|
||||
|
||||
@@ -152,8 +152,8 @@
|
||||
"pluralize": "8.0.0",
|
||||
"postal-mime": "^2.6.1",
|
||||
"psl": "^1.9.0",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"redis": "^4.7.0",
|
||||
"reflect-metadata": "0.2.2",
|
||||
"rxjs": "7.8.1",
|
||||
@@ -210,7 +210,7 @@
|
||||
"@types/passport-microsoft": "^2.1.0",
|
||||
"@types/pluralize": "^0.0.33",
|
||||
"@types/psl": "^1.1.3",
|
||||
"@types/react": "^18.2.39",
|
||||
"@types/react": "^19.2.0",
|
||||
"@types/supertest": "^2.0.11",
|
||||
"@types/unzipper": "^0",
|
||||
"@types/uuid": "^9.0.2",
|
||||
|
||||
+1
-1
@@ -122,7 +122,7 @@ describe('Send Email Body Rendering', () => {
|
||||
if (!element || typeof element !== 'object') return false;
|
||||
if (element.type === 'br') return true;
|
||||
|
||||
const children = element.props?.children;
|
||||
const children = (element.props as { children?: unknown })?.children;
|
||||
|
||||
if (!children) return false;
|
||||
if (Array.isArray(children)) {
|
||||
|
||||
+1
@@ -389,6 +389,7 @@ exports[`syncApplication should delete old field and create equivalent one when
|
||||
"isNullable": true,
|
||||
"isSystem": false,
|
||||
"isUIEditable": true,
|
||||
"isUIReadOnly": false,
|
||||
"isUnique": false,
|
||||
"kanbanAggregateOperationViewIds": [],
|
||||
"kanbanAggregateOperationViewUniversalIdentifiers": [],
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@types/babel__preset-env": "^7",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/react": "^18.2.39",
|
||||
"@types/react-dom": "^18.2.15",
|
||||
"@types/react": "^19.2.0",
|
||||
"@types/react-dom": "^19.2.0",
|
||||
"@vitejs/plugin-react-swc": "^4.3.1",
|
||||
"@wyw-in-js/babel-preset": "^1.1.0",
|
||||
"@wyw-in-js/vite": "^1.1.0",
|
||||
@@ -47,7 +47,7 @@
|
||||
"vite-plugin-svgr": "^4.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@linaria/react": "^6.2.1",
|
||||
"@linaria/react": "^7.0.1",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@radix-ui/colors": "^3.0.0",
|
||||
"@sniptt/guards": "^0.2.0",
|
||||
@@ -56,8 +56,8 @@
|
||||
"framer-motion": "^11.18.0",
|
||||
"glob": "^11.1.0",
|
||||
"jotai": "^2.17.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-responsive": "^9.0.2",
|
||||
"react-router-dom": "^6.4.4",
|
||||
"react-tooltip": "^5.13.1",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { useEffect } from 'react';
|
||||
import { type JSX, useEffect } from 'react';
|
||||
|
||||
import { iconsState } from '@ui/display/icon/states/iconsState';
|
||||
|
||||
|
||||
@@ -22,5 +22,5 @@ export type ModalProps = React.PropsWithChildren & {
|
||||
backdropClickOutsideId?: string;
|
||||
preventClickOutside?: boolean;
|
||||
onBackdropMouseDown?: (e: React.MouseEvent) => void;
|
||||
modalRef?: React.RefObject<HTMLDivElement>;
|
||||
modalRef?: React.RefObject<HTMLDivElement | null>;
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user