2023-12-10 17:10:54 +00:00
|
|
|
{
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
"workspaceLayout": {
|
|
|
|
|
"appsDir": "packages",
|
|
|
|
|
"libsDir": "packages"
|
|
|
|
|
},
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
"namedInputs": {
|
2026-01-17 06:37:17 +00:00
|
|
|
"default": ["{projectRoot}/**/*"],
|
2024-05-03 12:59:09 +00:00
|
|
|
"excludeStories": [
|
|
|
|
|
"default",
|
|
|
|
|
"!{projectRoot}/.storybook/*",
|
|
|
|
|
"!{projectRoot}/**/tsconfig.storybook.json",
|
|
|
|
|
"!{projectRoot}/**/*.stories.(ts|tsx)",
|
|
|
|
|
"!{projectRoot}/**/__stories__/*"
|
|
|
|
|
],
|
|
|
|
|
"excludeTests": [
|
|
|
|
|
"default",
|
|
|
|
|
"!{projectRoot}/**/jest.config.(js|ts)",
|
|
|
|
|
"!{projectRoot}/**/tsconfig.spec.json",
|
|
|
|
|
"!{projectRoot}/**/*.test.(ts|tsx)",
|
|
|
|
|
"!{projectRoot}/**/*.spec.(ts|tsx)",
|
2025-01-09 17:30:41 +00:00
|
|
|
"!{projectRoot}/**/*.integration-spec.ts",
|
2024-05-03 12:59:09 +00:00
|
|
|
"!{projectRoot}/**/__tests__/*"
|
|
|
|
|
],
|
|
|
|
|
"production": [
|
|
|
|
|
"default",
|
|
|
|
|
"excludeStories",
|
|
|
|
|
"excludeTests",
|
|
|
|
|
"!{projectRoot}/**/__mocks__/*",
|
|
|
|
|
"!{projectRoot}/**/testing/*"
|
|
|
|
|
]
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
},
|
2023-12-10 17:10:54 +00:00
|
|
|
"targetDefaults": {
|
|
|
|
|
"build": {
|
|
|
|
|
"cache": true,
|
2026-01-17 06:37:17 +00:00
|
|
|
"inputs": ["^production", "production"],
|
|
|
|
|
"dependsOn": ["^build"]
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
},
|
|
|
|
|
"start": {
|
Upgrade NestJS from 10.x to 11.x (#15836)
## Overview
This PR upgrades all NestJS dependencies from version 10.x to 11.x,
following the [official migration
guide](https://docs.nestjs.com/migration-guide). This builds on top of
the v9 to v10 upgrade completed in PR #15835.
## Changes
### Dependencies Updated
**Core packages (10.x → 11.x):**
- `@nestjs/common`: 10.4.16 → 11.0.8
- `@nestjs/core`: 10.4.16 → 11.0.8
- `@nestjs/platform-express`: 10.4.16 → 11.0.8
- `@nestjs/config`: 3.2.3 → 3.3.0
- `@nestjs/passport`: 10.0.3 → 11.0.0
- `@nestjs/axios`: 3.0.2 → 3.1.2
- `@nestjs/schedule`: ^3.0.0 → ^4.1.1
- `@nestjs/serve-static`: 4.0.2 → 5.0.1
- `@nestjs/cache-manager`: ^2.2.1 → ^2.3.0
- `@nestjs/jwt`: 10.2.0 → 11.0.0
- `@nestjs/typeorm`: 10.0.2 → 11.0.0
- `@nestjs/terminus`: 11.0.0 (already on v11)
- `@nestjs/event-emitter`: 2.1.0 (compatible)
**DevDependencies:**
- `@nestjs/testing`: ^10.4.16 → ^11.0.8
- `@nestjs/schematics`: ^10.1.0 → ^11.0.2
- `@nestjs/cli`: 10.3.0 → 11.0.0
### Code Changes
**Fixed: TwentyConfigModule conditional imports**
- Updated `TwentyConfigModule.forRoot()` to use spread operator for
conditional imports
- Fixes TypeScript error with NestJS 11's stricter DynamicModule type
checking
**Cleanup: Removed unused package**
- Removed `@revertdotdev/revert-react` (not being used anywhere in the
codebase)
## Breaking Changes Addressed
### 1. ✅ Reflector Type Inference
- **Impact**: None - codebase only uses `reflector.get()` method
- **Analysis**: Does not use `getAllAndMerge()` or `getAllAndOverride()`
(the methods with breaking changes)
- **Files reviewed**: feature-flag.guard.ts,
message-queue-metadata.accessor.ts,
workspace-query-hook-metadata.accessor.ts
### 2. ✅ Lifecycle Hooks Execution Order
- **Change**: Termination hooks (`OnModuleDestroy`,
`BeforeApplicationShutdown`, `OnApplicationShutdown`) now execute in
REVERSE order
- **Analysis**: Reviewed all lifecycle hook implementations
- Redis client cleanup
- Database connection cleanup (GlobalWorkspaceDataSource)
- BullMQ queue/worker cleanup
- Cache storage cleanup
- **Result**: Dependency order is safe - services using connections
clean up before the connections themselves
### 3. ✅ Middleware Registration Order
- **Change**: Global middleware now executes first regardless of import
order
- **Analysis**: Middleware is not registered as global, so execution
order remains consistent
- **Files reviewed**: app.module.ts, middleware.module.ts
## Testing
All tests passing and build successful:
**Unit Tests (283+ tests):**
- ✅ Health module: 38 tests passed
- ✅ Auth module: 115 tests passed (passport v11 integration)
- ✅ REST API: 90 tests passed (middleware and express platform)
- ✅ Feature flags: 17 tests passed (Reflector usage)
- ✅ Workspace: 23 tests passed
**Build & Quality:**
- ✅ Type checking: Passed
- ✅ Linting: Passed
- ✅ Build: 3,683 files compiled successfully
## Verification
Tested critical NestJS functionality:
- ✅ Authentication & Security (JWT, OAuth, guards)
- ✅ HTTP Platform (Express integration, REST endpoints)
- ✅ Dependency Injection (Services, factories, providers)
- ✅ Cache Management (Redis with @nestjs/cache-manager)
- ✅ GraphQL (Query runners, resolvers)
- ✅ Configuration (Environment config)
- ✅ Scheduling (Cron jobs with @nestjs/schedule v4)
- ✅ Lifecycle Hooks (Module initialization and cleanup)
- ✅ Reflector (Metadata reflection in guards)
## Related PRs
- #15835 - Upgrade NestJS from 9.x to 10.x (completed)
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Upgrades NestJS to v11 and updates routing patterns, auth strategies,
GraphQL schema options, and build/dist paths (scripts, Docker, Nx,
migrations, assets), plus enables Devtools in development.
>
> - **Backend (NestJS 11 upgrade)**:
> - Bump `@nestjs/*` packages (core, platform-express, jwt, passport,
typeorm, serve-static, schedule, cli/testing/schematics) to v11.
> - Update REST/route-trigger/file controllers to new wildcard syntax
(`*path`).
> - Refactor OAuth (Google/Microsoft) and SAML strategies (abstract base
+ explicit `validate`); minor typings.
> - Enable `DevtoolsModule` in development.
> - **GraphQL**:
> - Add `buildSchemaOptions.orphanedTypes` for client-config types; keep
Yoga/Sentry setup.
> - **Build/Runtime & Config**:
> - Standardize dist layout (remove `src` in paths): update scripts,
Docker `CMD`, Nx `project.json`, render scripts, TypeORM migration
paths, asset resolution.
> - Adjust `nest-cli.json` (watchOptions, asset globs, migrations
outDir, monorepo/root).
> - Improve config module imports (spread conditional); tsconfig
excludes `node_modules`.
> - Minor Nx default: `start` target caching disabled.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
1139fd85a97d0c72314d416d07464cc3c9942783. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
2025-11-16 17:20:06 +00:00
|
|
|
"cache": false,
|
2026-01-17 06:37:17 +00:00
|
|
|
"dependsOn": ["^build"]
|
2023-12-10 17:10:54 +00:00
|
|
|
},
|
|
|
|
|
"lint": {
|
Migrate from ESLint to OxLint (#18443)
## Summary
Fully replaces ESLint with OxLint across the entire monorepo:
- **Replaced all ESLint configs** (`eslint.config.mjs`) with OxLint
configs (`.oxlintrc.json`) for every package: `twenty-front`,
`twenty-server`, `twenty-emails`, `twenty-ui`, `twenty-shared`,
`twenty-sdk`, `twenty-zapier`, `twenty-docs`, `twenty-website`,
`twenty-apps/*`, `create-twenty-app`
- **Migrated custom lint rules** from ESLint plugin format to OxLint JS
plugin system (`@oxlint/plugins`), including
`styled-components-prefixed-with-styled`, `no-hardcoded-colors`,
`sort-css-properties-alphabetically`,
`graphql-resolvers-should-be-guarded`,
`rest-api-methods-should-be-guarded`, `max-consts-per-file`, and
Jotai-related rules
- **Migrated custom rule tests** from ESLint `RuleTester` + Jest to
`oxlint/plugins-dev` `RuleTester` + Vitest
- **Removed all ESLint dependencies** from `package.json` files and
regenerated lockfiles
- **Updated Nx targets** (`lint`, `lint:diff-with-main`, `fmt`) in
`nx.json` and per-project `project.json` to use `oxlint` commands with
proper `dependsOn` for plugin builds
- **Updated CI workflows** (`.github/workflows/ci-*.yaml`) — no more
ESLint executor
- **Updated IDE setup**: replaced `dbaeumer.vscode-eslint` with
`oxc.oxc-vscode` extension, configured `source.fixAll.oxc` and
format-on-save with Prettier
- **Replaced all `eslint-disable` comments** with `oxlint-disable`
equivalents across the codebase
- **Updated docs** (`twenty-docs`) to reference OxLint instead of ESLint
- **Renamed** `twenty-eslint-rules` package to `twenty-oxlint-rules`
### Temporarily disabled rules (tracked in `OXLINT_MIGRATION_TODO.md`)
| Rule | Package | Violations | Auto-fixable |
|------|---------|-----------|-------------|
| `twenty/sort-css-properties-alphabetically` | twenty-front | 578 | Yes
|
| `typescript/consistent-type-imports` | twenty-server | 3814 | Yes |
| `twenty/max-consts-per-file` | twenty-server | 94 | No |
### Dropped plugins (no OxLint equivalent)
`eslint-plugin-project-structure`, `lingui/*`, `@stylistic/*`,
`import/order`, `prefer-arrow/prefer-arrow-functions`,
`eslint-plugin-mdx`, `@next/eslint-plugin-next`,
`eslint-plugin-storybook`, `eslint-plugin-react-refresh`. Partial
coverage for `jsx-a11y` and `unused-imports`.
### Additional fixes (pre-existing issues exposed by merge)
- Fixed `EmailThreadPreview.tsx` broken import from main rename
(`useOpenEmailThreadInSidePanel`)
- Restored truthiness guard in `getActivityTargetObjectRecords.ts`
- Fixed `AgentTurnResolver` return types to match entity (virtual
`fileMediaType`/`fileUrl` are resolved via `@ResolveField()`)
## Test plan
- [x] `npx nx lint twenty-front` passes
- [x] `npx nx lint twenty-server` passes
- [x] `npx nx lint twenty-docs` passes
- [x] Custom oxlint rules validated with Vitest: `npx nx test
twenty-oxlint-rules`
- [x] `npx nx typecheck twenty-front` passes
- [x] `npx nx typecheck twenty-server` passes
- [x] CI workflows trigger correctly with `dependsOn:
["twenty-oxlint-rules:build"]`
- [x] IDE linting works with `oxc.oxc-vscode` extension
2026-03-06 00:03:50 +00:00
|
|
|
"executor": "nx:run-commands",
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
"cache": true,
|
|
|
|
|
"options": {
|
Migrate from ESLint to OxLint (#18443)
## Summary
Fully replaces ESLint with OxLint across the entire monorepo:
- **Replaced all ESLint configs** (`eslint.config.mjs`) with OxLint
configs (`.oxlintrc.json`) for every package: `twenty-front`,
`twenty-server`, `twenty-emails`, `twenty-ui`, `twenty-shared`,
`twenty-sdk`, `twenty-zapier`, `twenty-docs`, `twenty-website`,
`twenty-apps/*`, `create-twenty-app`
- **Migrated custom lint rules** from ESLint plugin format to OxLint JS
plugin system (`@oxlint/plugins`), including
`styled-components-prefixed-with-styled`, `no-hardcoded-colors`,
`sort-css-properties-alphabetically`,
`graphql-resolvers-should-be-guarded`,
`rest-api-methods-should-be-guarded`, `max-consts-per-file`, and
Jotai-related rules
- **Migrated custom rule tests** from ESLint `RuleTester` + Jest to
`oxlint/plugins-dev` `RuleTester` + Vitest
- **Removed all ESLint dependencies** from `package.json` files and
regenerated lockfiles
- **Updated Nx targets** (`lint`, `lint:diff-with-main`, `fmt`) in
`nx.json` and per-project `project.json` to use `oxlint` commands with
proper `dependsOn` for plugin builds
- **Updated CI workflows** (`.github/workflows/ci-*.yaml`) — no more
ESLint executor
- **Updated IDE setup**: replaced `dbaeumer.vscode-eslint` with
`oxc.oxc-vscode` extension, configured `source.fixAll.oxc` and
format-on-save with Prettier
- **Replaced all `eslint-disable` comments** with `oxlint-disable`
equivalents across the codebase
- **Updated docs** (`twenty-docs`) to reference OxLint instead of ESLint
- **Renamed** `twenty-eslint-rules` package to `twenty-oxlint-rules`
### Temporarily disabled rules (tracked in `OXLINT_MIGRATION_TODO.md`)
| Rule | Package | Violations | Auto-fixable |
|------|---------|-----------|-------------|
| `twenty/sort-css-properties-alphabetically` | twenty-front | 578 | Yes
|
| `typescript/consistent-type-imports` | twenty-server | 3814 | Yes |
| `twenty/max-consts-per-file` | twenty-server | 94 | No |
### Dropped plugins (no OxLint equivalent)
`eslint-plugin-project-structure`, `lingui/*`, `@stylistic/*`,
`import/order`, `prefer-arrow/prefer-arrow-functions`,
`eslint-plugin-mdx`, `@next/eslint-plugin-next`,
`eslint-plugin-storybook`, `eslint-plugin-react-refresh`. Partial
coverage for `jsx-a11y` and `unused-imports`.
### Additional fixes (pre-existing issues exposed by merge)
- Fixed `EmailThreadPreview.tsx` broken import from main rename
(`useOpenEmailThreadInSidePanel`)
- Restored truthiness guard in `getActivityTargetObjectRecords.ts`
- Fixed `AgentTurnResolver` return types to match entity (virtual
`fileMediaType`/`fileUrl` are resolved via `@ResolveField()`)
## Test plan
- [x] `npx nx lint twenty-front` passes
- [x] `npx nx lint twenty-server` passes
- [x] `npx nx lint twenty-docs` passes
- [x] Custom oxlint rules validated with Vitest: `npx nx test
twenty-oxlint-rules`
- [x] `npx nx typecheck twenty-front` passes
- [x] `npx nx typecheck twenty-server` passes
- [x] CI workflows trigger correctly with `dependsOn:
["twenty-oxlint-rules:build"]`
- [x] IDE linting works with `oxc.oxc-vscode` extension
2026-03-06 00:03:50 +00:00
|
|
|
"cwd": "{projectRoot}",
|
Optimize merge queue to only run E2E and integrate prettier into lint (#18459)
## Summary
- **Merge queue optimization**: Created a dedicated
`ci-merge-queue.yaml` workflow that only runs Playwright E2E tests on
`ubuntu-latest-8-cores`. Removed `merge_group` trigger from all 7
existing CI workflows (front, server, shared, website, sdk, zapier,
docker-compose). The merge queue goes from ~30+ parallel jobs to a
single focused E2E job.
- **Label-based merge queue simulation**: Added `run-merge-queue` label
support so developers can trigger the exact merge queue E2E pipeline on
any open PR before it enters the queue.
- **Prettier in lint**: Chained `prettier --check` into `lint` and
`prettier --write` into `lint --configuration=fix` across `nx.json`
defaults, `twenty-front`, and `twenty-server`. Prettier formatting
errors are now caught by `lint` and fixed by `lint:fix` /
`lint:diff-with-main --configuration=fix`.
## After merge (manual repo settings)
Update GitHub branch protection required status checks:
1. Remove old per-workflow merge queue checks (`ci-front-status-check`,
`ci-e2e-status-check`, `ci-server-status-check`, etc.)
2. Add `ci-merge-queue-status-check` as the required check for the merge
queue
2026-03-06 12:20:57 +00:00
|
|
|
"command": "npx oxlint -c .oxlintrc.json . && (prettier . --check --cache --cache-location ../../.cache/prettier/{projectRoot} --cache-strategy metadata || (echo 'ERROR: Prettier formatting check failed! Fix with: npx nx lint --configuration=fix' && false))"
|
2025-08-08 14:21:57 +00:00
|
|
|
},
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
"configurations": {
|
Migrate from ESLint to OxLint (#18443)
## Summary
Fully replaces ESLint with OxLint across the entire monorepo:
- **Replaced all ESLint configs** (`eslint.config.mjs`) with OxLint
configs (`.oxlintrc.json`) for every package: `twenty-front`,
`twenty-server`, `twenty-emails`, `twenty-ui`, `twenty-shared`,
`twenty-sdk`, `twenty-zapier`, `twenty-docs`, `twenty-website`,
`twenty-apps/*`, `create-twenty-app`
- **Migrated custom lint rules** from ESLint plugin format to OxLint JS
plugin system (`@oxlint/plugins`), including
`styled-components-prefixed-with-styled`, `no-hardcoded-colors`,
`sort-css-properties-alphabetically`,
`graphql-resolvers-should-be-guarded`,
`rest-api-methods-should-be-guarded`, `max-consts-per-file`, and
Jotai-related rules
- **Migrated custom rule tests** from ESLint `RuleTester` + Jest to
`oxlint/plugins-dev` `RuleTester` + Vitest
- **Removed all ESLint dependencies** from `package.json` files and
regenerated lockfiles
- **Updated Nx targets** (`lint`, `lint:diff-with-main`, `fmt`) in
`nx.json` and per-project `project.json` to use `oxlint` commands with
proper `dependsOn` for plugin builds
- **Updated CI workflows** (`.github/workflows/ci-*.yaml`) — no more
ESLint executor
- **Updated IDE setup**: replaced `dbaeumer.vscode-eslint` with
`oxc.oxc-vscode` extension, configured `source.fixAll.oxc` and
format-on-save with Prettier
- **Replaced all `eslint-disable` comments** with `oxlint-disable`
equivalents across the codebase
- **Updated docs** (`twenty-docs`) to reference OxLint instead of ESLint
- **Renamed** `twenty-eslint-rules` package to `twenty-oxlint-rules`
### Temporarily disabled rules (tracked in `OXLINT_MIGRATION_TODO.md`)
| Rule | Package | Violations | Auto-fixable |
|------|---------|-----------|-------------|
| `twenty/sort-css-properties-alphabetically` | twenty-front | 578 | Yes
|
| `typescript/consistent-type-imports` | twenty-server | 3814 | Yes |
| `twenty/max-consts-per-file` | twenty-server | 94 | No |
### Dropped plugins (no OxLint equivalent)
`eslint-plugin-project-structure`, `lingui/*`, `@stylistic/*`,
`import/order`, `prefer-arrow/prefer-arrow-functions`,
`eslint-plugin-mdx`, `@next/eslint-plugin-next`,
`eslint-plugin-storybook`, `eslint-plugin-react-refresh`. Partial
coverage for `jsx-a11y` and `unused-imports`.
### Additional fixes (pre-existing issues exposed by merge)
- Fixed `EmailThreadPreview.tsx` broken import from main rename
(`useOpenEmailThreadInSidePanel`)
- Restored truthiness guard in `getActivityTargetObjectRecords.ts`
- Fixed `AgentTurnResolver` return types to match entity (virtual
`fileMediaType`/`fileUrl` are resolved via `@ResolveField()`)
## Test plan
- [x] `npx nx lint twenty-front` passes
- [x] `npx nx lint twenty-server` passes
- [x] `npx nx lint twenty-docs` passes
- [x] Custom oxlint rules validated with Vitest: `npx nx test
twenty-oxlint-rules`
- [x] `npx nx typecheck twenty-front` passes
- [x] `npx nx typecheck twenty-server` passes
- [x] CI workflows trigger correctly with `dependsOn:
["twenty-oxlint-rules:build"]`
- [x] IDE linting works with `oxc.oxc-vscode` extension
2026-03-06 00:03:50 +00:00
|
|
|
"ci": {},
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
"fix": {
|
Optimize merge queue to only run E2E and integrate prettier into lint (#18459)
## Summary
- **Merge queue optimization**: Created a dedicated
`ci-merge-queue.yaml` workflow that only runs Playwright E2E tests on
`ubuntu-latest-8-cores`. Removed `merge_group` trigger from all 7
existing CI workflows (front, server, shared, website, sdk, zapier,
docker-compose). The merge queue goes from ~30+ parallel jobs to a
single focused E2E job.
- **Label-based merge queue simulation**: Added `run-merge-queue` label
support so developers can trigger the exact merge queue E2E pipeline on
any open PR before it enters the queue.
- **Prettier in lint**: Chained `prettier --check` into `lint` and
`prettier --write` into `lint --configuration=fix` across `nx.json`
defaults, `twenty-front`, and `twenty-server`. Prettier formatting
errors are now caught by `lint` and fixed by `lint:fix` /
`lint:diff-with-main --configuration=fix`.
## After merge (manual repo settings)
Update GitHub branch protection required status checks:
1. Remove old per-workflow merge queue checks (`ci-front-status-check`,
`ci-e2e-status-check`, `ci-server-status-check`, etc.)
2. Add `ci-merge-queue-status-check` as the required check for the merge
queue
2026-03-06 12:20:57 +00:00
|
|
|
"command": "npx oxlint --fix -c .oxlintrc.json . && prettier . --write --cache --cache-location ../../.cache/prettier/{projectRoot} --cache-strategy metadata"
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
}
|
2024-12-17 08:24:21 +00:00
|
|
|
},
|
Migrate from ESLint to OxLint (#18443)
## Summary
Fully replaces ESLint with OxLint across the entire monorepo:
- **Replaced all ESLint configs** (`eslint.config.mjs`) with OxLint
configs (`.oxlintrc.json`) for every package: `twenty-front`,
`twenty-server`, `twenty-emails`, `twenty-ui`, `twenty-shared`,
`twenty-sdk`, `twenty-zapier`, `twenty-docs`, `twenty-website`,
`twenty-apps/*`, `create-twenty-app`
- **Migrated custom lint rules** from ESLint plugin format to OxLint JS
plugin system (`@oxlint/plugins`), including
`styled-components-prefixed-with-styled`, `no-hardcoded-colors`,
`sort-css-properties-alphabetically`,
`graphql-resolvers-should-be-guarded`,
`rest-api-methods-should-be-guarded`, `max-consts-per-file`, and
Jotai-related rules
- **Migrated custom rule tests** from ESLint `RuleTester` + Jest to
`oxlint/plugins-dev` `RuleTester` + Vitest
- **Removed all ESLint dependencies** from `package.json` files and
regenerated lockfiles
- **Updated Nx targets** (`lint`, `lint:diff-with-main`, `fmt`) in
`nx.json` and per-project `project.json` to use `oxlint` commands with
proper `dependsOn` for plugin builds
- **Updated CI workflows** (`.github/workflows/ci-*.yaml`) — no more
ESLint executor
- **Updated IDE setup**: replaced `dbaeumer.vscode-eslint` with
`oxc.oxc-vscode` extension, configured `source.fixAll.oxc` and
format-on-save with Prettier
- **Replaced all `eslint-disable` comments** with `oxlint-disable`
equivalents across the codebase
- **Updated docs** (`twenty-docs`) to reference OxLint instead of ESLint
- **Renamed** `twenty-eslint-rules` package to `twenty-oxlint-rules`
### Temporarily disabled rules (tracked in `OXLINT_MIGRATION_TODO.md`)
| Rule | Package | Violations | Auto-fixable |
|------|---------|-----------|-------------|
| `twenty/sort-css-properties-alphabetically` | twenty-front | 578 | Yes
|
| `typescript/consistent-type-imports` | twenty-server | 3814 | Yes |
| `twenty/max-consts-per-file` | twenty-server | 94 | No |
### Dropped plugins (no OxLint equivalent)
`eslint-plugin-project-structure`, `lingui/*`, `@stylistic/*`,
`import/order`, `prefer-arrow/prefer-arrow-functions`,
`eslint-plugin-mdx`, `@next/eslint-plugin-next`,
`eslint-plugin-storybook`, `eslint-plugin-react-refresh`. Partial
coverage for `jsx-a11y` and `unused-imports`.
### Additional fixes (pre-existing issues exposed by merge)
- Fixed `EmailThreadPreview.tsx` broken import from main rename
(`useOpenEmailThreadInSidePanel`)
- Restored truthiness guard in `getActivityTargetObjectRecords.ts`
- Fixed `AgentTurnResolver` return types to match entity (virtual
`fileMediaType`/`fileUrl` are resolved via `@ResolveField()`)
## Test plan
- [x] `npx nx lint twenty-front` passes
- [x] `npx nx lint twenty-server` passes
- [x] `npx nx lint twenty-docs` passes
- [x] Custom oxlint rules validated with Vitest: `npx nx test
twenty-oxlint-rules`
- [x] `npx nx typecheck twenty-front` passes
- [x] `npx nx typecheck twenty-server` passes
- [x] CI workflows trigger correctly with `dependsOn:
["twenty-oxlint-rules:build"]`
- [x] IDE linting works with `oxc.oxc-vscode` extension
2026-03-06 00:03:50 +00:00
|
|
|
"dependsOn": ["^build", "twenty-oxlint-rules:build"]
|
2025-12-31 12:47:20 +00:00
|
|
|
},
|
|
|
|
|
"lint:diff-with-main": {
|
|
|
|
|
"executor": "nx:run-commands",
|
|
|
|
|
"cache": false,
|
|
|
|
|
"options": {
|
Optimize merge queue to only run E2E and integrate prettier into lint (#18459)
## Summary
- **Merge queue optimization**: Created a dedicated
`ci-merge-queue.yaml` workflow that only runs Playwright E2E tests on
`ubuntu-latest-8-cores`. Removed `merge_group` trigger from all 7
existing CI workflows (front, server, shared, website, sdk, zapier,
docker-compose). The merge queue goes from ~30+ parallel jobs to a
single focused E2E job.
- **Label-based merge queue simulation**: Added `run-merge-queue` label
support so developers can trigger the exact merge queue E2E pipeline on
any open PR before it enters the queue.
- **Prettier in lint**: Chained `prettier --check` into `lint` and
`prettier --write` into `lint --configuration=fix` across `nx.json`
defaults, `twenty-front`, and `twenty-server`. Prettier formatting
errors are now caught by `lint` and fixed by `lint:fix` /
`lint:diff-with-main --configuration=fix`.
## After merge (manual repo settings)
Update GitHub branch protection required status checks:
1. Remove old per-workflow merge queue checks (`ci-front-status-check`,
`ci-e2e-status-check`, `ci-server-status-check`, etc.)
2. Add `ci-merge-queue-status-check` as the required check for the merge
queue
2026-03-06 12:20:57 +00:00
|
|
|
"command": "FILES=$(git diff --name-only --diff-filter=d main -- {projectRoot}/ | grep -E '{args.pattern}'); [ -z \"$FILES\" ] && echo 'No changed files.' || (npx oxlint -c {projectRoot}/.oxlintrc.json $FILES && (prettier --check $FILES || (echo 'ERROR: Prettier formatting check failed! Fix with: npx nx lint:diff-with-main --configuration=fix' && false)))",
|
2025-12-31 12:47:20 +00:00
|
|
|
"pattern": "\\.(ts|tsx|js|jsx)$"
|
|
|
|
|
},
|
|
|
|
|
"configurations": {
|
|
|
|
|
"fix": {
|
Optimize merge queue to only run E2E and integrate prettier into lint (#18459)
## Summary
- **Merge queue optimization**: Created a dedicated
`ci-merge-queue.yaml` workflow that only runs Playwright E2E tests on
`ubuntu-latest-8-cores`. Removed `merge_group` trigger from all 7
existing CI workflows (front, server, shared, website, sdk, zapier,
docker-compose). The merge queue goes from ~30+ parallel jobs to a
single focused E2E job.
- **Label-based merge queue simulation**: Added `run-merge-queue` label
support so developers can trigger the exact merge queue E2E pipeline on
any open PR before it enters the queue.
- **Prettier in lint**: Chained `prettier --check` into `lint` and
`prettier --write` into `lint --configuration=fix` across `nx.json`
defaults, `twenty-front`, and `twenty-server`. Prettier formatting
errors are now caught by `lint` and fixed by `lint:fix` /
`lint:diff-with-main --configuration=fix`.
## After merge (manual repo settings)
Update GitHub branch protection required status checks:
1. Remove old per-workflow merge queue checks (`ci-front-status-check`,
`ci-e2e-status-check`, `ci-server-status-check`, etc.)
2. Add `ci-merge-queue-status-check` as the required check for the merge
queue
2026-03-06 12:20:57 +00:00
|
|
|
"command": "FILES=$(git diff --name-only --diff-filter=d main -- {projectRoot}/ | grep -E '{args.pattern}'); [ -z \"$FILES\" ] && echo 'No changed files.' || (npx oxlint --fix -c {projectRoot}/.oxlintrc.json $FILES && prettier --write $FILES)"
|
2025-12-31 12:47:20 +00:00
|
|
|
}
|
|
|
|
|
}
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
},
|
|
|
|
|
"fmt": {
|
|
|
|
|
"executor": "nx:run-commands",
|
|
|
|
|
"cache": true,
|
|
|
|
|
"options": {
|
|
|
|
|
"cwd": "{projectRoot}",
|
|
|
|
|
"command": "prettier {args.files} --check --cache {args.cache} --cache-location {args.cacheLocation} --write {args.write} --cache-strategy {args.cacheStrategy}",
|
|
|
|
|
"cache": true,
|
|
|
|
|
"cacheLocation": "../../.cache/prettier/{projectRoot}",
|
|
|
|
|
"cacheStrategy": "metadata",
|
|
|
|
|
"write": false
|
|
|
|
|
},
|
|
|
|
|
"configurations": {
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
"ci": {
|
|
|
|
|
"cacheStrategy": "content"
|
|
|
|
|
},
|
|
|
|
|
"fix": {
|
|
|
|
|
"write": true
|
|
|
|
|
}
|
2024-12-17 08:24:21 +00:00
|
|
|
},
|
2026-01-17 06:37:17 +00:00
|
|
|
"dependsOn": ["^build"]
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
},
|
|
|
|
|
"typecheck": {
|
|
|
|
|
"executor": "nx:run-commands",
|
|
|
|
|
"cache": true,
|
|
|
|
|
"options": {
|
|
|
|
|
"cwd": "{projectRoot}",
|
2026-01-19 11:46:34 +00:00
|
|
|
"command": "tsgo -p tsconfig.json"
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
},
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
"configurations": {
|
|
|
|
|
"watch": {
|
2026-01-19 11:46:34 +00:00
|
|
|
"command": "tsgo -p tsconfig.json --watch"
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
}
|
|
|
|
|
},
|
2026-01-17 06:37:17 +00:00
|
|
|
"dependsOn": ["^build"]
|
2023-12-10 17:10:54 +00:00
|
|
|
},
|
|
|
|
|
"test": {
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
"executor": "@nx/jest:jest",
|
2024-01-29 09:17:12 +00:00
|
|
|
"cache": true,
|
2026-01-17 06:37:17 +00:00
|
|
|
"dependsOn": ["^build"],
|
2024-05-03 12:59:09 +00:00
|
|
|
"inputs": [
|
|
|
|
|
"^default",
|
|
|
|
|
"excludeStories",
|
|
|
|
|
"{workspaceRoot}/jest.preset.js"
|
|
|
|
|
],
|
2026-01-17 06:37:17 +00:00
|
|
|
"outputs": ["{projectRoot}/coverage"],
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
"options": {
|
2025-08-19 16:10:35 +00:00
|
|
|
"jestConfig": "{projectRoot}/jest.config.mjs",
|
2026-02-17 12:46:35 +00:00
|
|
|
"silent": true,
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
"coverage": true,
|
2026-01-17 06:37:17 +00:00
|
|
|
"coverageReporters": ["text-summary"],
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
"cacheDirectory": "../../.cache/jest/{projectRoot}"
|
|
|
|
|
},
|
|
|
|
|
"configurations": {
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
"ci": {
|
|
|
|
|
"ci": true,
|
2026-02-25 22:47:08 +00:00
|
|
|
"maxWorkers": 1
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
},
|
|
|
|
|
"coverage": {
|
2026-01-17 06:37:17 +00:00
|
|
|
"coverageReporters": ["lcov", "text"]
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
},
|
|
|
|
|
"watch": {
|
|
|
|
|
"watch": true
|
|
|
|
|
}
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
}
|
2023-12-10 17:10:54 +00:00
|
|
|
},
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
"test:e2e": {
|
|
|
|
|
"cache": true,
|
2026-01-17 06:37:17 +00:00
|
|
|
"dependsOn": ["^build"]
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
},
|
2026-03-11 15:30:28 +00:00
|
|
|
"set-local-version": {
|
|
|
|
|
"executor": "nx:run-commands",
|
|
|
|
|
"cache": false,
|
|
|
|
|
"options": {
|
|
|
|
|
"cwd": "{projectRoot}",
|
2026-04-05 18:56:37 +00:00
|
|
|
"command": "node -e \"const fs=require('fs'),p=JSON.parse(fs.readFileSync('package.json','utf8'));p.version='{args.releaseVersion}';fs.writeFileSync('package.json',JSON.stringify(p,null,2)+'\\n');\""
|
2026-03-11 15:30:28 +00:00
|
|
|
}
|
|
|
|
|
},
|
2024-04-04 13:38:01 +00:00
|
|
|
"storybook:build": {
|
2024-05-07 19:05:45 +00:00
|
|
|
"executor": "nx:run-commands",
|
2024-04-04 13:38:01 +00:00
|
|
|
"cache": true,
|
2026-01-17 06:37:17 +00:00
|
|
|
"inputs": ["^default", "excludeTests"],
|
|
|
|
|
"outputs": ["{projectRoot}/{options.output-dir}"],
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
"options": {
|
2024-05-07 19:05:45 +00:00
|
|
|
"cwd": "{projectRoot}",
|
2026-03-05 17:55:04 +00:00
|
|
|
"command": "NODE_OPTIONS='--max-old-space-size=10240' storybook build --test",
|
2024-05-07 19:05:45 +00:00
|
|
|
"output-dir": "storybook-static",
|
|
|
|
|
"config-dir": ".storybook"
|
2024-12-17 08:24:21 +00:00
|
|
|
},
|
2026-01-17 06:37:17 +00:00
|
|
|
"dependsOn": ["^build"]
|
2024-04-04 13:38:01 +00:00
|
|
|
},
|
2024-06-30 18:02:13 +00:00
|
|
|
"storybook:serve:dev": {
|
2024-05-07 19:05:45 +00:00
|
|
|
"executor": "nx:run-commands",
|
2024-04-04 13:38:01 +00:00
|
|
|
"cache": true,
|
2026-01-17 06:37:17 +00:00
|
|
|
"dependsOn": ["^build"],
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
"options": {
|
2024-05-07 19:05:45 +00:00
|
|
|
"cwd": "{projectRoot}",
|
|
|
|
|
"command": "storybook dev",
|
|
|
|
|
"config-dir": ".storybook"
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
}
|
|
|
|
|
},
|
2024-06-30 18:02:13 +00:00
|
|
|
"storybook:serve:static": {
|
2024-05-07 19:05:45 +00:00
|
|
|
"executor": "nx:run-commands",
|
2026-01-17 06:37:17 +00:00
|
|
|
"dependsOn": ["storybook:build"],
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
"options": {
|
2024-05-07 19:05:45 +00:00
|
|
|
"cwd": "{projectRoot}",
|
|
|
|
|
"command": "npx http-server {args.staticDir} -a={args.host} --port={args.port} --silent={args.silent}",
|
|
|
|
|
"staticDir": "storybook-static",
|
|
|
|
|
"host": "localhost",
|
|
|
|
|
"port": 6006,
|
|
|
|
|
"silent": true
|
2024-05-03 12:59:09 +00:00
|
|
|
}
|
|
|
|
|
},
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
"storybook:test": {
|
|
|
|
|
"executor": "nx:run-commands",
|
2024-05-03 12:59:09 +00:00
|
|
|
"cache": true,
|
2026-01-17 06:37:17 +00:00
|
|
|
"inputs": ["^default", "excludeTests"],
|
|
|
|
|
"outputs": ["{projectRoot}/coverage/storybook"],
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
"options": {
|
2024-05-03 12:59:09 +00:00
|
|
|
"cwd": "{projectRoot}",
|
2026-01-18 03:25:45 +00:00
|
|
|
"command": "vitest run --coverage --shard={args.shard}",
|
|
|
|
|
"shard": "1/1"
|
2024-05-03 12:59:09 +00:00
|
|
|
}
|
|
|
|
|
},
|
2024-06-30 18:02:13 +00:00
|
|
|
"storybook:test:no-coverage": {
|
Generic Profiling story to wrap any component (#5341)
This PR introduces a Profiling feature for our story book tests.
It also implements a new CI job : front-sb-test-performance, that only
runs stories suffixed with `.perf.stories.tsx`
## How it works
It allows to wrap any component into an array of React Profiler
components that will run tests many times to have the most replicable
average render time possible.
It is simply used by calling the new `getProfilingStory` util.
Internally it creates a defined number of tests, separated by an
arbitrary waiting time to allow the CPU to give more stable results.
It will do 3 warm-up and 3 finishing runs of tests because the first and
last renders are always a bit erratic, so we want to measure only the
runs in-between.
On the UI side it gives a table of results :
<img width="515" alt="image"
src="https://github.com/twentyhq/twenty/assets/26528466/273d2d91-26da-437a-890e-778cb6c1f993">
On the programmatic side, it stores the result in a div that can then be
parsed by the play fonction of storybook, to expect a defined threshold.
```tsx
play: async ({ canvasElement }) => {
await findByTestId(
canvasElement,
'profiling-session-finished',
{},
{ timeout: 60000 },
);
const profilingReport = getProfilingReportFromDocument(canvasElement);
if (!isDefined(profilingReport)) {
return;
}
const p95result = profilingReport?.total.p95;
expect(
p95result,
`Component render time is more than p95 threshold (${p95ThresholdInMs}ms)`,
).toBeLessThan(p95ThresholdInMs);
},
```
2024-05-15 11:50:02 +00:00
|
|
|
"executor": "nx:run-commands",
|
2026-01-17 06:37:17 +00:00
|
|
|
"inputs": ["^default", "excludeTests"],
|
Generic Profiling story to wrap any component (#5341)
This PR introduces a Profiling feature for our story book tests.
It also implements a new CI job : front-sb-test-performance, that only
runs stories suffixed with `.perf.stories.tsx`
## How it works
It allows to wrap any component into an array of React Profiler
components that will run tests many times to have the most replicable
average render time possible.
It is simply used by calling the new `getProfilingStory` util.
Internally it creates a defined number of tests, separated by an
arbitrary waiting time to allow the CPU to give more stable results.
It will do 3 warm-up and 3 finishing runs of tests because the first and
last renders are always a bit erratic, so we want to measure only the
runs in-between.
On the UI side it gives a table of results :
<img width="515" alt="image"
src="https://github.com/twentyhq/twenty/assets/26528466/273d2d91-26da-437a-890e-778cb6c1f993">
On the programmatic side, it stores the result in a div that can then be
parsed by the play fonction of storybook, to expect a defined threshold.
```tsx
play: async ({ canvasElement }) => {
await findByTestId(
canvasElement,
'profiling-session-finished',
{},
{ timeout: 60000 },
);
const profilingReport = getProfilingReportFromDocument(canvasElement);
if (!isDefined(profilingReport)) {
return;
}
const p95result = profilingReport?.total.p95;
expect(
p95result,
`Component render time is more than p95 threshold (${p95ThresholdInMs}ms)`,
).toBeLessThan(p95ThresholdInMs);
},
```
2024-05-15 11:50:02 +00:00
|
|
|
"options": {
|
|
|
|
|
"cwd": "{projectRoot}",
|
2026-01-18 03:25:45 +00:00
|
|
|
"command": "vitest run --shard={args.shard}",
|
|
|
|
|
"shard": "1/1"
|
Generic Profiling story to wrap any component (#5341)
This PR introduces a Profiling feature for our story book tests.
It also implements a new CI job : front-sb-test-performance, that only
runs stories suffixed with `.perf.stories.tsx`
## How it works
It allows to wrap any component into an array of React Profiler
components that will run tests many times to have the most replicable
average render time possible.
It is simply used by calling the new `getProfilingStory` util.
Internally it creates a defined number of tests, separated by an
arbitrary waiting time to allow the CPU to give more stable results.
It will do 3 warm-up and 3 finishing runs of tests because the first and
last renders are always a bit erratic, so we want to measure only the
runs in-between.
On the UI side it gives a table of results :
<img width="515" alt="image"
src="https://github.com/twentyhq/twenty/assets/26528466/273d2d91-26da-437a-890e-778cb6c1f993">
On the programmatic side, it stores the result in a div that can then be
parsed by the play fonction of storybook, to expect a defined threshold.
```tsx
play: async ({ canvasElement }) => {
await findByTestId(
canvasElement,
'profiling-session-finished',
{},
{ timeout: 60000 },
);
const profilingReport = getProfilingReportFromDocument(canvasElement);
if (!isDefined(profilingReport)) {
return;
}
const p95result = profilingReport?.total.p95;
expect(
p95result,
`Component render time is more than p95 threshold (${p95ThresholdInMs}ms)`,
).toBeLessThan(p95ThresholdInMs);
},
```
2024-05-15 11:50:02 +00:00
|
|
|
}
|
|
|
|
|
},
|
2024-06-30 18:02:13 +00:00
|
|
|
"storybook:coverage": {
|
|
|
|
|
"executor": "nx:run-commands",
|
|
|
|
|
"cache": true,
|
|
|
|
|
"inputs": [
|
|
|
|
|
"^default",
|
|
|
|
|
"excludeTests",
|
|
|
|
|
"{projectRoot}/coverage/storybook/coverage-storybook.json"
|
|
|
|
|
],
|
|
|
|
|
"outputs": [
|
|
|
|
|
"{projectRoot}/coverage/storybook",
|
|
|
|
|
"!{projectRoot}/coverage/storybook/coverage-storybook.json"
|
|
|
|
|
],
|
|
|
|
|
"options": {
|
2025-01-10 17:40:03 +00:00
|
|
|
"command": "npx nyc report --reporter={args.reporter} --reporter=text-summary -t {args.coverageDir} --report-dir {args.coverageDir} --check-coverage={args.checkCoverage} --cwd={projectRoot}",
|
2024-06-30 18:02:13 +00:00
|
|
|
"coverageDir": "coverage/storybook",
|
2025-01-10 17:40:03 +00:00
|
|
|
"reporter": "lcov",
|
|
|
|
|
"checkCoverage": true
|
2024-06-30 18:02:13 +00:00
|
|
|
},
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
"configurations": {
|
|
|
|
|
"text": {
|
|
|
|
|
"reporter": "text"
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-30 18:02:13 +00:00
|
|
|
},
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
"chromatic": {
|
|
|
|
|
"executor": "nx:run-commands",
|
|
|
|
|
"options": {
|
|
|
|
|
"cwd": "{projectRoot}",
|
2024-05-22 09:18:16 +00:00
|
|
|
"commands": [
|
|
|
|
|
{
|
2024-06-30 18:02:13 +00:00
|
|
|
"command": "nx storybook:build {projectName}",
|
2024-05-22 09:18:16 +00:00
|
|
|
"forwardAllArgs": false
|
|
|
|
|
},
|
2025-11-16 19:12:25 +00:00
|
|
|
"chromatic --storybook-build-dir=storybook-static {args.ci}"
|
2024-05-22 09:18:16 +00:00
|
|
|
],
|
|
|
|
|
"parallel": false
|
chore: use Nx affected tasks in CI (#5110)
Closes #5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
2024-04-30 14:28:25 +00:00
|
|
|
},
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
"configurations": {
|
|
|
|
|
"ci": {
|
|
|
|
|
"ci": "--exit-zero-on-changes"
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-04 13:38:01 +00:00
|
|
|
},
|
2024-01-03 22:07:25 +00:00
|
|
|
"@nx/jest:jest": {
|
|
|
|
|
"cache": true,
|
2024-05-03 12:59:09 +00:00
|
|
|
"inputs": [
|
|
|
|
|
"^default",
|
|
|
|
|
"excludeStories",
|
|
|
|
|
"{workspaceRoot}/jest.preset.js"
|
|
|
|
|
],
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
"options": {
|
|
|
|
|
"passWithNoTests": true
|
|
|
|
|
},
|
|
|
|
|
"configurations": {
|
|
|
|
|
"ci": {
|
|
|
|
|
"ci": true,
|
|
|
|
|
"codeCoverage": true
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-29 09:17:12 +00:00
|
|
|
},
|
2026-01-17 06:37:17 +00:00
|
|
|
"@nx/vite:build": {
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
"cache": true,
|
2026-01-17 06:37:17 +00:00
|
|
|
"dependsOn": ["^build"],
|
|
|
|
|
"inputs": ["default", "^default"]
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
},
|
2026-01-17 06:37:17 +00:00
|
|
|
"@nx/vitest:test": {
|
2024-04-04 10:30:49 +00:00
|
|
|
"cache": true,
|
2026-01-17 06:37:17 +00:00
|
|
|
"inputs": ["default", "^default"]
|
2023-12-10 17:10:54 +00:00
|
|
|
}
|
|
|
|
|
},
|
2024-01-29 09:17:12 +00:00
|
|
|
"generators": {
|
|
|
|
|
"@nx/react": {
|
|
|
|
|
"application": {
|
Complete linaria migration (#18361)
## Summary
Completes the migration of the frontend styling system from **Emotion**
(`@emotion/styled`, `@emotion/react`) to **Linaria** (`@linaria/react`,
`@linaria/core`), a zero-runtime CSS-in-JS library where styles are
extracted at build time.
This is the final step of the migration — all ~494 files across
`twenty-front`, `twenty-ui`, `twenty-website`, and `twenty-sdk` are now
fully converted.
## Changes
### Styling Migration (across ~480 component files)
- Replaced all `@emotion/styled` imports with `@linaria/react`
- Converted runtime theme access patterns (`({ theme }) => theme.x.y`)
to build-time `themeCssVariables` CSS custom properties
- Replaced `useTheme()` hook (from Emotion) with
`useContext(ThemeContext)` where runtime theme values are still needed
(e.g., passing colors to non-CSS props like icon components)
- Removed `@emotion/react` `css` helper usages in favor of Linaria
template literals
### Dependency & Configuration Changes
- **Removed**: `@emotion/react`, `@emotion/styled` from root
`package.json`
- **Added**: `@wyw-in-js/babel-preset`, `next-with-linaria` (for
twenty-website SSR support)
- Updated Nx generator defaults from `@emotion/styled` to
`@linaria/react` in `nx.json`
- Simplified `vite.config.ts` (removed Emotion-specific configuration)
- Updated `twenty-website/next.config.js` to use `next-with-linaria` for
SSR Linaria support
### Storybook & Testing
- Removed `ThemeProvider` from Emotion in Storybook previews
(`twenty-front`, `twenty-sdk`)
- Now relies solely on `ThemeContextProvider` for theme injection
### Documentation
- Removed the temporary `docs/emotion-to-linaria-migration-plan.md`
(migration complete)
- Updated `CLAUDE.md` and `README.md` to reflect Linaria as the styling
stack
- Updated frontend style guide docs across all locales
## How it works
Linaria extracts styles at build time via the `@wyw-in-js/vite` plugin.
All expressions in `styled` template literals must be **statically
evaluable** — no runtime theme objects or closures over component state.
- **Static styles** use `themeCssVariables` which map to CSS custom
properties (`var(--theme-color-x)`)
- **Runtime theme access** (for non-CSS use cases like icon `color`
props) uses `useContext(ThemeContext)` instead of Emotion's `useTheme()`
2026-03-03 23:50:06 +00:00
|
|
|
"style": "@linaria/react",
|
2024-04-04 13:38:01 +00:00
|
|
|
"bundler": "vite",
|
|
|
|
|
"compiler": "swc",
|
|
|
|
|
"unitTestRunner": "jest",
|
|
|
|
|
"projectNameAndRootFormat": "derived"
|
2024-01-29 09:17:12 +00:00
|
|
|
},
|
|
|
|
|
"library": {
|
Complete linaria migration (#18361)
## Summary
Completes the migration of the frontend styling system from **Emotion**
(`@emotion/styled`, `@emotion/react`) to **Linaria** (`@linaria/react`,
`@linaria/core`), a zero-runtime CSS-in-JS library where styles are
extracted at build time.
This is the final step of the migration — all ~494 files across
`twenty-front`, `twenty-ui`, `twenty-website`, and `twenty-sdk` are now
fully converted.
## Changes
### Styling Migration (across ~480 component files)
- Replaced all `@emotion/styled` imports with `@linaria/react`
- Converted runtime theme access patterns (`({ theme }) => theme.x.y`)
to build-time `themeCssVariables` CSS custom properties
- Replaced `useTheme()` hook (from Emotion) with
`useContext(ThemeContext)` where runtime theme values are still needed
(e.g., passing colors to non-CSS props like icon components)
- Removed `@emotion/react` `css` helper usages in favor of Linaria
template literals
### Dependency & Configuration Changes
- **Removed**: `@emotion/react`, `@emotion/styled` from root
`package.json`
- **Added**: `@wyw-in-js/babel-preset`, `next-with-linaria` (for
twenty-website SSR support)
- Updated Nx generator defaults from `@emotion/styled` to
`@linaria/react` in `nx.json`
- Simplified `vite.config.ts` (removed Emotion-specific configuration)
- Updated `twenty-website/next.config.js` to use `next-with-linaria` for
SSR Linaria support
### Storybook & Testing
- Removed `ThemeProvider` from Emotion in Storybook previews
(`twenty-front`, `twenty-sdk`)
- Now relies solely on `ThemeContextProvider` for theme injection
### Documentation
- Removed the temporary `docs/emotion-to-linaria-migration-plan.md`
(migration complete)
- Updated `CLAUDE.md` and `README.md` to reflect Linaria as the styling
stack
- Updated frontend style guide docs across all locales
## How it works
Linaria extracts styles at build time via the `@wyw-in-js/vite` plugin.
All expressions in `styled` template literals must be **statically
evaluable** — no runtime theme objects or closures over component state.
- **Static styles** use `themeCssVariables` which map to CSS custom
properties (`var(--theme-color-x)`)
- **Runtime theme access** (for non-CSS use cases like icon `color`
props) uses `useContext(ThemeContext)` instead of Emotion's `useTheme()`
2026-03-03 23:50:06 +00:00
|
|
|
"style": "@linaria/react",
|
2024-04-04 13:38:01 +00:00
|
|
|
"bundler": "vite",
|
|
|
|
|
"compiler": "swc",
|
|
|
|
|
"unitTestRunner": "jest",
|
|
|
|
|
"projectNameAndRootFormat": "derived"
|
|
|
|
|
},
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
"component": {
|
Complete linaria migration (#18361)
## Summary
Completes the migration of the frontend styling system from **Emotion**
(`@emotion/styled`, `@emotion/react`) to **Linaria** (`@linaria/react`,
`@linaria/core`), a zero-runtime CSS-in-JS library where styles are
extracted at build time.
This is the final step of the migration — all ~494 files across
`twenty-front`, `twenty-ui`, `twenty-website`, and `twenty-sdk` are now
fully converted.
## Changes
### Styling Migration (across ~480 component files)
- Replaced all `@emotion/styled` imports with `@linaria/react`
- Converted runtime theme access patterns (`({ theme }) => theme.x.y`)
to build-time `themeCssVariables` CSS custom properties
- Replaced `useTheme()` hook (from Emotion) with
`useContext(ThemeContext)` where runtime theme values are still needed
(e.g., passing colors to non-CSS props like icon components)
- Removed `@emotion/react` `css` helper usages in favor of Linaria
template literals
### Dependency & Configuration Changes
- **Removed**: `@emotion/react`, `@emotion/styled` from root
`package.json`
- **Added**: `@wyw-in-js/babel-preset`, `next-with-linaria` (for
twenty-website SSR support)
- Updated Nx generator defaults from `@emotion/styled` to
`@linaria/react` in `nx.json`
- Simplified `vite.config.ts` (removed Emotion-specific configuration)
- Updated `twenty-website/next.config.js` to use `next-with-linaria` for
SSR Linaria support
### Storybook & Testing
- Removed `ThemeProvider` from Emotion in Storybook previews
(`twenty-front`, `twenty-sdk`)
- Now relies solely on `ThemeContextProvider` for theme injection
### Documentation
- Removed the temporary `docs/emotion-to-linaria-migration-plan.md`
(migration complete)
- Updated `CLAUDE.md` and `README.md` to reflect Linaria as the styling
stack
- Updated frontend style guide docs across all locales
## How it works
Linaria extracts styles at build time via the `@wyw-in-js/vite` plugin.
All expressions in `styled` template literals must be **statically
evaluable** — no runtime theme objects or closures over component state.
- **Static styles** use `themeCssVariables` which map to CSS custom
properties (`var(--theme-color-x)`)
- **Runtime theme access** (for non-CSS use cases like icon `color`
props) uses `useContext(ThemeContext)` instead of Emotion's `useTheme()`
2026-03-03 23:50:06 +00:00
|
|
|
"style": "@linaria/react"
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
}
|
2024-01-29 09:17:12 +00:00
|
|
|
}
|
2024-03-13 13:21:18 +00:00
|
|
|
},
|
|
|
|
|
"tasksRunnerOptions": {
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
"default": {
|
|
|
|
|
"options": {
|
2026-01-17 06:37:17 +00:00
|
|
|
"cacheableOperations": ["storybook:build"]
|
Refactor usePersistField (#13775)
This PR introduces refactors and tradeoffs in the API around the events
of field input.
# Refactored usePersistField
The hook `usePersistField` has been refactored to be used anywhere in
the app, not just inside a FieldContext.
This was meant to solve a bug at the beginning but now it is just used
once in `RecordDetailRelationSection` outside of the context, still this
is better to have this hook like that for future use cases.
We also introduce `usePersistFieldFromFieldInputContext`, for an easier
API inside a FieldContext.
# Introduced a new `FieldInputEventContext`
To remove the drill-down of events, we introduce
`FieldInputEventContext`, this allows to set only once the handlers /
events. In practice it allows to have an easier time maintaining the
events for the many different field inputs, because it matches the
pattern we already use of taking everything from a context
(`FieldContext`).
# Removed drill-down from FieldInput
The heavy drill-down in FieldInput has been completely removed, since
everything can be derived from `FieldContext` and
`FieldInputEventContext`.
Also there was some readonly and other specific props, but they were all
drilled down from FieldContext, so it was easier to just use
FieldContext where needed.
# Refactored events of `MultiItemFieldInputProps`
The component `MultiItemFieldInputProps` has a contrived API, here we
just remove the complex part that was persisting from inside.
We now only give a classic API with `onChange` and `onEscape` the rest
is left to higher levels, where it should be, because this generic
component shouldn't be aware of persisting things.
# Extracted the parsing logic of persisted values
For each input field component, we now have a clear util that was before
bound to the persist call,
# Tradeoff with persist times
The tradeoff before was that persistField was called anywhere, before
exiting the component sometimes, now it is only called by the higher
levels like table or show page, which handles this abstraction.
This could be challenged, however I think that having a lot of different
events, and not just `handleSubmit` and `handleCancel`, convey enough
meaning for the higher levels to decide what to do in each case.
A `skipPersist` argument was added in events in the rare edge cases
where we want to voluntarily skip persisting even with a submit or
escape, but that could be challenged because we could also say that we
should use cancel for that and stick to that convention.
# Handling of the bug in `ActivityRichTextEditor`
Initially this refactor was prioritized for solving this bug, which was
very annoying for the users.
But while fixing it with the new persistField hook I just understood
that the problem is not just for record title cells but for anything
that is open when we click on a rich text editor.
The issue is described here :
https://github.com/twentyhq/core-team-issues/issues/1317
So right now I just let it as is.
# Stories
The stories were checking that a request was sent in some cases where
persist was called before a component exiting, now that persist is only
called by higher-levels I just removed those tests from the stories,
because that should be the responsibility of higher levels.
Also a helper `getFieldInputEventContextProviderWithJestMocks` was
created that exposes a context and jest mock functions for testing this
new API in stories.
# Miscellaneous
Deactivated tui with nx by default, because it can be annoying.
2025-08-12 08:42:13 +00:00
|
|
|
}
|
|
|
|
|
}
|
2024-04-01 11:16:50 +00:00
|
|
|
},
|
|
|
|
|
"useInferencePlugins": false,
|
2025-08-13 15:50:10 +00:00
|
|
|
"defaultBase": "main",
|
|
|
|
|
"tui": {
|
|
|
|
|
"enabled": false
|
|
|
|
|
}
|
2025-08-19 16:10:35 +00:00
|
|
|
}
|