snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 3s

This commit is contained in:
upstream-mirror
2026-09-17 23:28:13 +05:00
commit e7da97194a
32688 changed files with 4208681 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
import type { StorybookConfig } from '@storybook/react-vite';
const computeStoriesGlob = () => {
if (process.env.STORYBOOK_SCOPE === 'pages') {
return [
'../src/pages/**/*.stories.@(js|jsx|ts|tsx)',
'../src/__stories__/*.stories.@(js|jsx|ts|tsx)',
'../src/pages/**/*.docs.mdx',
'../src/__stories__/*.docs.mdx',
];
}
if (process.env.STORYBOOK_SCOPE === 'modules') {
return [
'../src/modules/**/!(perf)/*.stories.@(js|jsx|ts|tsx)',
'../src/modules/**/*.docs.mdx',
];
}
if (process.env.STORYBOOK_SCOPE === 'performance') {
return ['../src/modules/**/perf/*.perf.stories.@(js|jsx|ts|tsx)'];
}
if (process.env.STORYBOOK_SCOPE === 'ui-docs') {
return ['../src/modules/ui/**/*.docs.mdx'];
}
return ['../src/**/*.docs.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'];
};
const config: StorybookConfig = {
stories: computeStoriesGlob(),
build: {
test: {
disabledAddons: [
'@storybook/addon-docs',
'@storybook/addon-essentials/docs',
],
},
},
addons: [
'@storybook-community/storybook-addon-cookie',
'@storybook/addon-links',
'@storybook/addon-coverage',
'storybook-addon-pseudo-states',
'storybook-addon-mock-date',
'@storybook/addon-vitest',
],
framework: '@storybook/react-vite',
viteFinal: async (viteConfig) => {
const { mergeConfig } = await import('vite');
return mergeConfig(viteConfig, {
logLevel: 'warn',
});
},
logLevel: 'error',
docs: {},
};
export default config;

View File

@@ -0,0 +1,8 @@
<style>
[data-is-storybook='true'] {
background-color: transparent !important;
}
:has(#storybook-panel-root) {
z-index: 100;
}
</style>

View File

@@ -0,0 +1,27 @@
<style type="text/css">
body {
margin: 0;
font-family: 'Inter', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
html {
font-size: 13px;
}
.sbdocs-wrapper {
padding: 0 !important;
}
*::-webkit-scrollbar {
height: 4px;
width: 4px;
}
*::-webkit-scrollbar-corner {
background-color: transparent;
}
*::-webkit-scrollbar-thumb {
background-color: transparent;
border-radius: 2px;
}
</style>

View File

@@ -0,0 +1,169 @@
import '@fontsource/dm-mono/400.css';
import '@fontsource/dm-mono/500.css';
import '@fontsource/inter/400.css';
import '@fontsource/inter/500.css';
import '@fontsource/inter/600.css';
import '@fontsource/inter/700.css';
import { i18n } from '@lingui/core';
import { I18nProvider } from '@lingui/react';
import { type Preview } from '@storybook/react-vite';
import { http, HttpResponse } from 'msw';
import { initialize, mswLoader } from 'msw-storybook-addon';
import { SOURCE_LOCALE } from 'twenty-shared/translations';
// oxlint-disable-next-line no-restricted-imports
import { DateFormat } from '../src/modules/localization/constants/DateFormat';
// oxlint-disable-next-line no-restricted-imports
import { TimeFormat } from '../src/modules/localization/constants/TimeFormat';
// oxlint-disable-next-line no-restricted-imports
import { FileUploadProvider } from '../src/modules/file-upload/components/FileUploadProvider';
// oxlint-disable-next-line no-restricted-imports
import { RootDecorator } from '../src/testing/decorators/RootDecorator';
// oxlint-disable-next-line no-restricted-imports
import { resetJotaiStore } from '../src/modules/ui/utilities/state/jotai/jotaiStore';
// oxlint-disable-next-line no-restricted-imports
import { UserContext } from '../src/modules/users/contexts/UserContext';
// Stories rendering CodeEditor / GraphiQL need Monaco's worker factory, which
// the app normally sets up in src/index.tsx.
// oxlint-disable-next-line no-restricted-imports
import '../src/modules/app/utils/setupMonacoEnvironment';
import 'react-loading-skeleton/dist/skeleton.css';
import 'twenty-ui/style.css';
import 'twenty-ui/theme-light.css';
import 'twenty-ui/theme-dark.css';
import { ThemeProvider } from 'twenty-ui/theme-constants';
// oxlint-disable-next-line no-restricted-imports
import { messages as enMessages } from '../src/locales/generated/en';
i18n.load({ [SOURCE_LOCALE]: enMessages });
i18n.activate(SOURCE_LOCALE);
// oxlint-disable-next-line no-restricted-imports
import { ClickOutsideListenerContext } from '../src/modules/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext';
const MOCK_IMAGE_SVG =
'<svg xmlns="http://www.w3.org/2000/svg" width="192" height="192"><rect width="192" height="192" fill="#d9d9d9"/></svg>';
const respondWithMockImage = () =>
new HttpResponse(MOCK_IMAGE_SVG, {
headers: { 'Content-Type': 'image/svg+xml' },
});
const remoteImageMockHandlers = [
http.get('https://picsum.photos/*', respondWithMockImage),
http.get('https://twenty-icons.com/*', respondWithMockImage),
http.get('https://twentyhq.github.io/*', respondWithMockImage),
http.get('https://via.placeholder.com/*', respondWithMockImage),
http.get(
'https://twenty-front-screenshots.s3.eu-west-3.amazonaws.com/*',
respondWithMockImage,
),
];
initialize(
{
onUnhandledRequest: async (request: Request) => {
const fileExtensionsToIgnore =
/\.(ts|tsx|js|jsx|svg|css|png|woff2)(\?v=[a-zA-Z0-9]+)?/;
if (fileExtensionsToIgnore.test(request.url)) {
return;
}
if (request.url.startsWith('http://localhost:3000/files/')) {
return;
}
try {
const requestBody = await request.json();
// oxlint-disable-next-line no-console
console.warn(`Unhandled ${request.method} request to ${request.url}
with payload ${JSON.stringify(requestBody)}\n
This request should be mocked with MSW`);
} catch (error) {
// oxlint-disable-next-line no-console
console.error(`Cannot parse msw request body : ${error}`);
}
// oxlint-disable-next-line no-console
console.warn(
`Unhandled ${request.method} request to ${request.url} \n This request should be mocked with MSW`,
);
},
quiet: true,
},
remoteImageMockHandlers,
);
// Mirrors production's MinimalMetadataGater so any story rendering a
// date-aware component (DateTimeDisplay, etc.) sees a real IANA timeZone
// instead of UserContext's default `{}`. Stories needing a specific timezone
// can still override by nesting their own UserContext.Provider.
const STORYBOOK_DEFAULT_USER_CONTEXT = {
dateFormat: DateFormat.DAY_FIRST,
timeFormat: TimeFormat.HOUR_24,
timeZone: 'UTC',
};
const INTER_FONT_WEIGHTS = ['400', '500', '600'];
const waitForInterFontLoadedBeforeScreenshot = async () => {
await Promise.all(
INTER_FONT_WEIGHTS.map((weight) =>
document.fonts.load(`${weight} 1em Inter`),
),
);
await document.fonts.ready;
};
const preview: Preview = {
decorators: [
(Story) => {
return (
<I18nProvider i18n={i18n}>
<ThemeProvider colorScheme="light">
<FileUploadProvider>
<ClickOutsideListenerContext.Provider
value={{ excludedClickOutsideId: undefined }}
>
<UserContext.Provider value={STORYBOOK_DEFAULT_USER_CONTEXT}>
<Story />
</UserContext.Provider>
</ClickOutsideListenerContext.Provider>
</FileUploadProvider>
</ThemeProvider>
</I18nProvider>
);
},
RootDecorator,
],
beforeEach: () => {
resetJotaiStore();
},
loaders: [mswLoader, waitForInterFontLoadedBeforeScreenshot],
parameters: {
argos: {
// CSS zoom combined with viewport-sized stories makes the iframe grow during capture.
fitToContent: { zoom: 1 },
},
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
mockingDate: new Date('2024-03-12T09:30:00.000Z'),
options: {
storySort: {
order: ['UI', 'Modules', 'Pages'],
},
},
},
};
export default preview;

View File

@@ -0,0 +1,40 @@
import { Globals } from '@react-spring/web';
import { setProjectAnnotations } from '@storybook/react-vite';
import { MotionGlobalConfig } from 'framer-motion';
import * as projectAnnotations from './preview';
import { waitForServiceWorkerToControlPage } from './waitForServiceWorkerToControlPage';
MotionGlobalConfig.skipAnimations = true;
Globals.assign({ skipAnimation: true });
const disableCssAnimationsStyle = document.createElement('style');
disableCssAnimationsStyle.innerHTML = `*, *::before, *::after {
animation-duration: 0s !important;
animation-delay: 0s !important;
transition-duration: 0s !important;
transition-delay: 0s !important;
}`;
document.head.appendChild(disableCssAnimationsStyle);
// msw registers its service worker while ./preview evaluates, and it reloads
// any page that has a registration without being controlled by it, which
// vitest reports as "the iframe was reloaded during a test" and which fails
// the whole shard. Holding every file here until the worker controls its page
// (including files that run zero tests, which otherwise complete instantly)
// means no test iframe is ever created while the worker is still activating.
await waitForServiceWorkerToControlPage();
// Pre-warm the dynamic imports used by lazy() story components so the
// modules are cached before any test runs (avoids flaky timeouts and Argos
// screenshots that capture a Suspense skeleton instead of the loaded content).
import('~/testing/utils/getTestEnrichedObjectMetadataItemsMock');
import('react-markdown');
import('remark-gfm');
import('@/activities/components/ActivityRichTextEditor');
import('@/object-record/record-field/ui/meta-types/input/components/RichTextFieldEditor');
import('@/navigation-menu-item/display/sections/favorites/components/FavoritesSectionDispatcher');
import('@/navigation-menu-item/display/sections/workspace/components/WorkspaceSectionDispatcher');
// This is an important step to apply the right configuration when testing your stories.
// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations
setProjectAnnotations([projectAnnotations]);

View File

@@ -0,0 +1,38 @@
import { isDefined } from 'twenty-shared/utils';
// The worker takes tens of seconds to activate on CI, because its script
// request queues behind the story module transforms. serviceWorker.ready is
// used rather than getRegistration() because msw registers the worker
// asynchronously while ./preview evaluates, so the registration may not exist
// yet when this runs. The backstop covers a worker that never activates.
const SERVICE_WORKER_CONTROL_BACKSTOP_MS = 60_000;
export const waitForServiceWorkerToControlPage = async () => {
if (isDefined(navigator.serviceWorker.controller)) {
return;
}
const backstop = new Promise<void>((resolve) => {
setTimeout(resolve, SERVICE_WORKER_CONTROL_BACKSTOP_MS);
});
await Promise.race([
navigator.serviceWorker.ready.then(() => undefined),
backstop,
]);
if (isDefined(navigator.serviceWorker.controller)) {
return;
}
await Promise.race([
new Promise<void>((resolve) => {
navigator.serviceWorker.addEventListener(
'controllerchange',
() => resolve(),
{ once: true },
);
}),
backstop,
]);
};