@shipfox/client-integrations 22.0.2 → 23.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/preview.tsx +2 -2
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/CHANGELOG.md +33 -0
- package/dist/components/integration-gallery.stories.d.ts.map +1 -1
- package/dist/components/integration-gallery.stories.js +17 -6
- package/dist/components/integration-gallery.stories.js.map +1 -1
- package/dist/components/integration-usage-modal.d.ts +1 -1
- package/dist/components/integration-usage-modal.d.ts.map +1 -1
- package/dist/components/integration-usage-modal.js +13 -9
- package/dist/components/integration-usage-modal.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +14 -14
- package/src/components/integration-gallery.stories.tsx +16 -4
- package/src/components/integration-usage-modal.tsx +17 -10
- package/tsconfig.build.tsbuildinfo +1 -1
package/.storybook/preview.tsx
CHANGED
|
@@ -4,8 +4,8 @@ import type {Decorator, Preview} from '@storybook/react';
|
|
|
4
4
|
|
|
5
5
|
if (typeof document !== 'undefined' && document.fonts) {
|
|
6
6
|
void Promise.all([
|
|
7
|
-
document.fonts.load("16px '
|
|
8
|
-
document.fonts.load("italic 16px '
|
|
7
|
+
document.fonts.load("16px 'IBM Plex Sans'"),
|
|
8
|
+
document.fonts.load("italic 16px 'IBM Plex Sans'"),
|
|
9
9
|
document.fonts.load("16px 'Commit Mono'"),
|
|
10
10
|
document.fonts.load("bold 16px 'Commit Mono'"),
|
|
11
11
|
]);
|
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
$ shipfox-swc
|
|
2
|
-
Successfully compiled: 59 files with swc (
|
|
2
|
+
Successfully compiled: 59 files with swc (626.82ms)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @shipfox/client-integrations
|
|
2
2
|
|
|
3
|
+
## 23.0.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [693e656]
|
|
8
|
+
- Updated dependencies [18e9bad]
|
|
9
|
+
- Updated dependencies [2323e2e]
|
|
10
|
+
- Updated dependencies [c44641f]
|
|
11
|
+
- Updated dependencies [1b71a66]
|
|
12
|
+
- @shipfox/client-shell@23.0.0
|
|
13
|
+
- @shipfox/api-integration-core-dto@14.0.0
|
|
14
|
+
- @shipfox/api-integration-linear-dto@14.0.0
|
|
15
|
+
- @shipfox/api-integration-slack-dto@14.0.0
|
|
16
|
+
- @shipfox/api-integration-jira-dto@14.0.0
|
|
17
|
+
- @shipfox/api-integration-webhook-dto@14.0.0
|
|
18
|
+
- @shipfox/api-integration-gitea-dto@14.0.0
|
|
19
|
+
- @shipfox/client-auth@23.0.0
|
|
20
|
+
- @shipfox/api-integration-github-dto@14.0.0
|
|
21
|
+
- @shipfox/api-integration-sentry-dto@14.0.0
|
|
22
|
+
|
|
23
|
+
## 22.0.3
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- ddcc546: Closes the integration usage modal through the shared Modal lifecycle instead of unmounting it directly, so it completes its exit animation.
|
|
28
|
+
- Updated dependencies [a7fb52d]
|
|
29
|
+
- Updated dependencies [ddcc546]
|
|
30
|
+
- @shipfox/client-auth@22.0.3
|
|
31
|
+
- @shipfox/react-ui@2.2.0
|
|
32
|
+
- @shipfox/client-shell@22.0.3
|
|
33
|
+
- @shipfox/client-ui@22.0.3
|
|
34
|
+
- @shipfox/integration-icons@0.3.7
|
|
35
|
+
|
|
3
36
|
## 22.0.2
|
|
4
37
|
|
|
5
38
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration-gallery.stories.d.ts","sourceRoot":"","sources":["../../src/components/integration-gallery.stories.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAO,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AA0BrD,KAAK,QAAQ,GACT,OAAO,GACP,mBAAmB,GACnB,SAAS,GACT,mBAAmB,GACnB,iBAAiB,GACjB,cAAc,GACd,YAAY,CAAC;AAEjB,UAAU,4BAA4B;IACpC,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAWD,iBAAS,uBAAuB,CAAC,EAAC,QAAQ,EAAC,EAAE,4BAA4B,+BA0DxE;AAED,QAAA,MAAM,IAAI;;;;QAGK,MAAM;;;;;CAE2B,CAAC;eAElC,IAAI;AACnB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,UAAU,EAAE,KAAU,CAAC;AAEpC,eAAO,MAAM,gBAAgB,EAAE,KAE9B,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAE9B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,KAE5B,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,KAElC,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAEvB,CAAC;AAEF,eAAO,MAAM,kCAAkC,EAAE,
|
|
1
|
+
{"version":3,"file":"integration-gallery.stories.d.ts","sourceRoot":"","sources":["../../src/components/integration-gallery.stories.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAO,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AA0BrD,KAAK,QAAQ,GACT,OAAO,GACP,mBAAmB,GACnB,SAAS,GACT,mBAAmB,GACnB,iBAAiB,GACjB,cAAc,GACd,YAAY,CAAC;AAEjB,UAAU,4BAA4B;IACpC,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAWD,iBAAS,uBAAuB,CAAC,EAAC,QAAQ,EAAC,EAAE,4BAA4B,+BA0DxE;AAED,QAAA,MAAM,IAAI;;;;QAGK,MAAM;;;;;CAE2B,CAAC;eAElC,IAAI;AACnB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,UAAU,EAAE,KAAU,CAAC;AAEpC,eAAO,MAAM,gBAAgB,EAAE,KAE9B,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAE9B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,KAE5B,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,KAElC,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAEvB,CAAC;AAEF,eAAO,MAAM,kCAAkC,EAAE,KAkChD,CAAC"}
|
|
@@ -195,15 +195,26 @@ export const UsageModalCloseRestoresInteraction = {
|
|
|
195
195
|
await user.click(await screen.findByRole('button', {
|
|
196
196
|
name: 'Done'
|
|
197
197
|
}));
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
198
|
+
// The dialog stays mounted while it animates out, which is what keeps the
|
|
199
|
+
// close from being an abrupt unmount.
|
|
200
|
+
await waitFor(()=>{
|
|
201
|
+
expect(screen.queryByRole('dialog', {
|
|
202
|
+
name: 'Use acme-corp'
|
|
203
|
+
})).toHaveAttribute('data-state', 'closed');
|
|
204
|
+
});
|
|
205
|
+
// Both the removal and the body pointer-events release therefore land after
|
|
206
|
+
// the click rather than on the same tick.
|
|
204
207
|
await waitFor(()=>{
|
|
208
|
+
expect(screen.queryByRole('dialog', {
|
|
209
|
+
name: 'Use acme-corp'
|
|
210
|
+
})).not.toBeInTheDocument();
|
|
205
211
|
expect(document.body.style.pointerEvents).not.toBe('none');
|
|
206
212
|
});
|
|
213
|
+
// Radix dismisses on the first outside pointerdown while the previous menu is
|
|
214
|
+
// still mounted, so let it finish leaving before reopening.
|
|
215
|
+
await waitFor(()=>{
|
|
216
|
+
expect(document.querySelectorAll('[role="menu"]')).toHaveLength(0);
|
|
217
|
+
});
|
|
207
218
|
await user.click(actionsButton);
|
|
208
219
|
await screen.findByRole('menuitem', {
|
|
209
220
|
name: 'Use this integration'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/integration-gallery.stories.tsx"],"sourcesContent":["import type {\n IntegrationConnectionDto,\n IntegrationProviderDto,\n} from '@shipfox/api-integration-core-dto';\nimport {configureApiClient} from '@shipfox/client-api';\nimport {authStateAtom} from '@shipfox/client-auth';\nimport {Toaster} from '@shipfox/react-ui/toast';\nimport type {Meta, StoryObj} from '@storybook/react';\nimport {QueryClient, QueryClientProvider} from '@tanstack/react-query';\nimport {\n createMemoryHistory,\n createRootRoute,\n createRoute,\n createRouter,\n Outlet,\n RouterProvider,\n} from '@tanstack/react-router';\nimport {createStore, Provider as JotaiProvider} from 'jotai';\nimport {useMemo} from 'react';\nimport {expect, screen, userEvent, waitFor, within} from 'storybook/test';\nimport {IntegrationGallery} from './integration-gallery.js';\n\nconst WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';\nconst WORKSPACE_PATH = '/w/acme/settings/integrations';\nconst SETUP_PATHS = [\n '/w/$workspaceSlug/integrations/github',\n '/w/$workspaceSlug/integrations/sentry',\n '/w/$workspaceSlug/integrations/linear',\n '/w/$workspaceSlug/integrations/slack',\n '/w/$workspaceSlug/integrations/gitea',\n '/w/$workspaceSlug/settings/events',\n] as const;\n\ntype Scenario =\n | 'mixed'\n | 'empty-connections'\n | 'loading'\n | 'connections-error'\n | 'providers-error'\n | 'no-providers'\n | 'long-names';\n\ninterface IntegrationGalleryStoryProps {\n scenario: Scenario;\n}\n\nconst PROVIDERS: IntegrationProviderDto[] = [\n {provider: 'github', display_name: 'GitHub', capabilities: ['source_control']},\n {provider: 'sentry', display_name: 'Sentry', capabilities: []},\n {provider: 'linear', display_name: 'Linear', capabilities: ['agent_tools']},\n {provider: 'slack', display_name: 'Slack', capabilities: ['agent_tools']},\n {provider: 'gitea', display_name: 'Gitea', capabilities: ['source_control']},\n {provider: 'webhook', display_name: 'Webhook', capabilities: []},\n];\n\nfunction IntegrationGalleryStory({scenario}: IntegrationGalleryStoryProps) {\n configureApiClient({\n baseUrl: 'https://api.example.test',\n fetchImpl: fetchForScenario(scenario),\n });\n\n const queryClient = useMemo(\n () => new QueryClient({defaultOptions: {queries: {retry: false}}}),\n [],\n );\n const store = useMemo(() => {\n const nextStore = createStore();\n nextStore.set(authStateAtom, {\n status: 'authenticated',\n workspaces: [{id: WORKSPACE_ID, name: 'Acme', slug: 'acme', membershipId: 'membership-1'}],\n });\n return nextStore;\n }, []);\n const router = useMemo(() => {\n const rootRoute = createRootRoute({component: Outlet});\n const workspaceRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/w/$workspaceSlug',\n component: Outlet,\n });\n const galleryRoute = createRoute({\n getParentRoute: () => workspaceRoute,\n path: 'settings/integrations',\n component: () => (\n <div className=\"mx-auto w-full max-w-[760px] bg-background-subtle-base p-24\">\n <IntegrationGallery workspaceId={WORKSPACE_ID} />\n </div>\n ),\n });\n const setupRoutes = SETUP_PATHS.map((path) =>\n createRoute({\n getParentRoute: () => workspaceRoute,\n path: path.replace('/w/$workspaceSlug/', ''),\n component: () => <div />,\n }),\n );\n\n return createRouter({\n history: createMemoryHistory({initialEntries: [WORKSPACE_PATH]}),\n routeTree: rootRoute.addChildren([\n workspaceRoute.addChildren([galleryRoute, ...setupRoutes]),\n ]),\n });\n }, []);\n\n return (\n <QueryClientProvider client={queryClient}>\n <JotaiProvider store={store}>\n <RouterProvider router={router} />\n <Toaster />\n </JotaiProvider>\n </QueryClientProvider>\n );\n}\n\nconst meta = {\n title: 'Integrations/Gallery',\n component: IntegrationGalleryStory,\n parameters: {layout: 'fullscreen'},\n args: {scenario: 'mixed'},\n} satisfies Meta<typeof IntegrationGalleryStory>;\n\nexport default meta;\ntype Story = StoryObj<typeof meta>;\n\nexport const Playground: Story = {};\n\nexport const EmptyConnections: Story = {\n args: {scenario: 'empty-connections'},\n};\n\nexport const Loading: Story = {\n args: {scenario: 'loading'},\n};\n\nexport const ConnectionsError: Story = {\n args: {scenario: 'connections-error'},\n};\n\nexport const ProvidersError: Story = {\n args: {scenario: 'providers-error'},\n};\n\nexport const NoProvidersAvailable: Story = {\n args: {scenario: 'no-providers'},\n};\n\nexport const LongNames: Story = {\n args: {scenario: 'long-names'},\n};\n\nexport const UsageModalCloseRestoresInteraction: Story = {\n play: async ({canvasElement}) => {\n const user = userEvent.setup();\n const canvas = within(canvasElement);\n const actionsButton = await canvas.findByRole('button', {\n name: 'Open acme-corp integration actions',\n });\n\n await user.click(actionsButton);\n await user.click(await screen.findByRole('menuitem', {name: 'Use this integration'}));\n await screen.findByRole('dialog', {name: 'Use acme-corp'});\n await user.click(await screen.findByRole('button', {name: 'Done'}));\n expect(screen.queryByRole('dialog', {name: 'Use acme-corp'})).not.toBeInTheDocument();\n // The dialog unmounts before its dismissable layer releases the body, so\n // wait for the release rather than asserting it on the same tick: clicking\n // while `pointer-events: none` is still set is swallowed silently.\n await waitFor(() => {\n expect(document.body.style.pointerEvents).not.toBe('none');\n });\n await user.click(actionsButton);\n await screen.findByRole('menuitem', {name: 'Use this integration'});\n },\n};\n\nfunction fetchForScenario(scenario: Scenario): typeof fetch {\n return (input, init) => {\n const url = requestUrl(input);\n const method = init?.method ?? 'GET';\n if (scenario === 'loading') return new Promise<Response>(() => undefined);\n if (url.pathname === '/integration-providers') {\n if (scenario === 'providers-error') return Promise.resolve(errorResponse());\n return Promise.resolve(\n jsonResponse({providers: scenario === 'no-providers' ? [] : PROVIDERS}),\n );\n }\n if (url.pathname.startsWith('/integration-connections/') && method === 'PATCH') {\n return Promise.resolve(\n jsonResponse({\n ...connection(),\n id: url.pathname.split('/').at(-1),\n lifecycle_status: 'disabled',\n }),\n );\n }\n if (url.pathname.startsWith('/integration-connections/') && method === 'DELETE') {\n return Promise.resolve(jsonResponse(undefined, {status: 204}));\n }\n if (url.pathname.startsWith('/integrations/webhook/connections/') && method === 'PATCH') {\n return Promise.resolve(\n jsonResponse({\n id: url.pathname.split('/').at(-1),\n workspace_id: WORKSPACE_ID,\n name: 'Stripe production',\n slug: 'stripe-prod',\n lifecycle_status: 'disabled',\n inbound_url: 'https://api.example.test/webhook/77777777-7777-4777-8777-777777777777',\n created_at: '2026-04-12T00:00:00.000Z',\n updated_at: '2026-04-12T00:00:00.000Z',\n }),\n );\n }\n if (url.pathname.startsWith('/integrations/webhook/connections/') && method === 'DELETE') {\n return Promise.resolve(jsonResponse(undefined, {status: 204}));\n }\n if (url.pathname === '/integration-connections') {\n if (scenario === 'connections-error') return Promise.resolve(errorResponse());\n return Promise.resolve(jsonResponse({connections: connectionsForScenario(scenario)}));\n }\n if (url.pathname === '/integrations/webhook/connections') {\n return Promise.resolve(\n jsonResponse({\n connections: [\n {\n id: '77777777-7777-4777-8777-777777777777',\n workspace_id: WORKSPACE_ID,\n name: 'Stripe production',\n slug: 'stripe-prod',\n lifecycle_status: 'active',\n inbound_url: 'https://api.example.test/webhook/77777777-7777-4777-8777-777777777777',\n created_at: '2026-04-12T00:00:00.000Z',\n updated_at: '2026-04-12T00:00:00.000Z',\n },\n ],\n }),\n );\n }\n return Promise.resolve(jsonResponse({}, {status: 404}));\n };\n}\n\nfunction connectionsForScenario(scenario: Scenario): IntegrationConnectionDto[] {\n if (scenario === 'empty-connections' || scenario === 'no-providers') return [];\n if (scenario === 'long-names') {\n return [\n connection({\n display_name: 'acme-production-observability-and-source-control-organization',\n lifecycle_status: 'active',\n }),\n connection({\n id: '55555555-5555-4555-8555-555555555555',\n provider: 'sentry',\n display_name: 'sentry-team-with-a-very-long-connected-account-name',\n lifecycle_status: 'error',\n }),\n ];\n }\n return [\n connection({display_name: 'acme-corp', lifecycle_status: 'active'}),\n connection({\n id: '99999999-9999-4999-8999-999999999999',\n provider: 'slack',\n display_name: 'Slack Acme',\n lifecycle_status: 'active',\n capabilities: ['agent_tools'],\n external_url: 'https://app.slack.com/client/T123',\n }),\n connection({\n id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',\n provider: 'slack',\n display_name: 'Slack disabled',\n lifecycle_status: 'disabled',\n capabilities: ['agent_tools'],\n external_url: undefined,\n }),\n connection({\n id: '88888888-8888-4888-8888-888888888888',\n provider: 'linear',\n display_name: 'linear-acme',\n lifecycle_status: 'active',\n external_url: 'https://linear.app/acme/settings',\n }),\n connection({\n id: '55555555-5555-4555-8555-555555555555',\n provider: 'linear',\n display_name: 'linear-disabled',\n lifecycle_status: 'disabled',\n external_url: undefined,\n }),\n connection({\n id: '66666666-6666-4666-8666-666666666666',\n provider: 'linear',\n display_name: 'linear-error',\n lifecycle_status: 'error',\n external_url: undefined,\n }),\n connection({\n id: '77777777-7777-4777-8777-777777777777',\n provider: 'webhook',\n external_account_id: 'stripe-prod',\n slug: 'stripe-prod',\n display_name: 'Stripe production',\n capabilities: [],\n lifecycle_status: 'active',\n external_url: undefined,\n created_at: '2026-04-12T00:00:00.000Z',\n updated_at: '2026-04-12T00:00:00.000Z',\n }),\n ];\n}\n\nfunction connection(overrides: Partial<IntegrationConnectionDto> = {}): IntegrationConnectionDto {\n return {\n id: '44444444-4444-4444-8444-444444444444',\n workspace_id: WORKSPACE_ID,\n provider: 'github',\n external_account_id: 'installation-1',\n slug: 'github_acme_corp',\n display_name: 'acme-corp',\n lifecycle_status: 'active',\n capabilities: ['source_control'],\n external_url: 'https://github.com/organizations/acme-corp/settings/installations/1',\n created_at: '2026-03-12T00:00:00.000Z',\n updated_at: '2026-03-12T00:00:00.000Z',\n ...overrides,\n };\n}\n\nfunction requestUrl(input: RequestInfo | URL): URL {\n if (typeof input === 'string') return new URL(input);\n if (input instanceof URL) return input;\n return new URL(input.url);\n}\n\nfunction jsonResponse(body: unknown, init: ResponseInit = {}) {\n return new Response(JSON.stringify(body), {\n status: 200,\n headers: {'content-type': 'application/json'},\n ...init,\n });\n}\n\nfunction errorResponse() {\n return jsonResponse({code: 'server-error'}, {status: 500, statusText: 'Server error'});\n}\n"],"names":["configureApiClient","authStateAtom","Toaster","QueryClient","QueryClientProvider","createMemoryHistory","createRootRoute","createRoute","createRouter","Outlet","RouterProvider","createStore","Provider","JotaiProvider","useMemo","expect","screen","userEvent","waitFor","within","IntegrationGallery","WORKSPACE_ID","WORKSPACE_PATH","SETUP_PATHS","PROVIDERS","provider","display_name","capabilities","IntegrationGalleryStory","scenario","baseUrl","fetchImpl","fetchForScenario","queryClient","defaultOptions","queries","retry","store","nextStore","set","status","workspaces","id","name","slug","membershipId","router","rootRoute","component","workspaceRoute","getParentRoute","path","galleryRoute","div","className","workspaceId","setupRoutes","map","replace","history","initialEntries","routeTree","addChildren","client","meta","title","parameters","layout","args","Playground","EmptyConnections","Loading","ConnectionsError","ProvidersError","NoProvidersAvailable","LongNames","UsageModalCloseRestoresInteraction","play","canvasElement","user","setup","canvas","actionsButton","findByRole","click","queryByRole","not","toBeInTheDocument","document","body","style","pointerEvents","toBe","input","init","url","requestUrl","method","Promise","undefined","pathname","resolve","errorResponse","jsonResponse","providers","startsWith","connection","split","at","lifecycle_status","workspace_id","inbound_url","created_at","updated_at","connections","connectionsForScenario","external_url","external_account_id","overrides","URL","Response","JSON","stringify","headers","code","statusText"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/components/integration-gallery.stories.tsx"],"sourcesContent":["import type {\n IntegrationConnectionDto,\n IntegrationProviderDto,\n} from '@shipfox/api-integration-core-dto';\nimport {configureApiClient} from '@shipfox/client-api';\nimport {authStateAtom} from '@shipfox/client-auth';\nimport {Toaster} from '@shipfox/react-ui/toast';\nimport type {Meta, StoryObj} from '@storybook/react';\nimport {QueryClient, QueryClientProvider} from '@tanstack/react-query';\nimport {\n createMemoryHistory,\n createRootRoute,\n createRoute,\n createRouter,\n Outlet,\n RouterProvider,\n} from '@tanstack/react-router';\nimport {createStore, Provider as JotaiProvider} from 'jotai';\nimport {useMemo} from 'react';\nimport {expect, screen, userEvent, waitFor, within} from 'storybook/test';\nimport {IntegrationGallery} from './integration-gallery.js';\n\nconst WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';\nconst WORKSPACE_PATH = '/w/acme/settings/integrations';\nconst SETUP_PATHS = [\n '/w/$workspaceSlug/integrations/github',\n '/w/$workspaceSlug/integrations/sentry',\n '/w/$workspaceSlug/integrations/linear',\n '/w/$workspaceSlug/integrations/slack',\n '/w/$workspaceSlug/integrations/gitea',\n '/w/$workspaceSlug/settings/events',\n] as const;\n\ntype Scenario =\n | 'mixed'\n | 'empty-connections'\n | 'loading'\n | 'connections-error'\n | 'providers-error'\n | 'no-providers'\n | 'long-names';\n\ninterface IntegrationGalleryStoryProps {\n scenario: Scenario;\n}\n\nconst PROVIDERS: IntegrationProviderDto[] = [\n {provider: 'github', display_name: 'GitHub', capabilities: ['source_control']},\n {provider: 'sentry', display_name: 'Sentry', capabilities: []},\n {provider: 'linear', display_name: 'Linear', capabilities: ['agent_tools']},\n {provider: 'slack', display_name: 'Slack', capabilities: ['agent_tools']},\n {provider: 'gitea', display_name: 'Gitea', capabilities: ['source_control']},\n {provider: 'webhook', display_name: 'Webhook', capabilities: []},\n];\n\nfunction IntegrationGalleryStory({scenario}: IntegrationGalleryStoryProps) {\n configureApiClient({\n baseUrl: 'https://api.example.test',\n fetchImpl: fetchForScenario(scenario),\n });\n\n const queryClient = useMemo(\n () => new QueryClient({defaultOptions: {queries: {retry: false}}}),\n [],\n );\n const store = useMemo(() => {\n const nextStore = createStore();\n nextStore.set(authStateAtom, {\n status: 'authenticated',\n workspaces: [{id: WORKSPACE_ID, name: 'Acme', slug: 'acme', membershipId: 'membership-1'}],\n });\n return nextStore;\n }, []);\n const router = useMemo(() => {\n const rootRoute = createRootRoute({component: Outlet});\n const workspaceRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/w/$workspaceSlug',\n component: Outlet,\n });\n const galleryRoute = createRoute({\n getParentRoute: () => workspaceRoute,\n path: 'settings/integrations',\n component: () => (\n <div className=\"mx-auto w-full max-w-[760px] bg-background-subtle-base p-24\">\n <IntegrationGallery workspaceId={WORKSPACE_ID} />\n </div>\n ),\n });\n const setupRoutes = SETUP_PATHS.map((path) =>\n createRoute({\n getParentRoute: () => workspaceRoute,\n path: path.replace('/w/$workspaceSlug/', ''),\n component: () => <div />,\n }),\n );\n\n return createRouter({\n history: createMemoryHistory({initialEntries: [WORKSPACE_PATH]}),\n routeTree: rootRoute.addChildren([\n workspaceRoute.addChildren([galleryRoute, ...setupRoutes]),\n ]),\n });\n }, []);\n\n return (\n <QueryClientProvider client={queryClient}>\n <JotaiProvider store={store}>\n <RouterProvider router={router} />\n <Toaster />\n </JotaiProvider>\n </QueryClientProvider>\n );\n}\n\nconst meta = {\n title: 'Integrations/Gallery',\n component: IntegrationGalleryStory,\n parameters: {layout: 'fullscreen'},\n args: {scenario: 'mixed'},\n} satisfies Meta<typeof IntegrationGalleryStory>;\n\nexport default meta;\ntype Story = StoryObj<typeof meta>;\n\nexport const Playground: Story = {};\n\nexport const EmptyConnections: Story = {\n args: {scenario: 'empty-connections'},\n};\n\nexport const Loading: Story = {\n args: {scenario: 'loading'},\n};\n\nexport const ConnectionsError: Story = {\n args: {scenario: 'connections-error'},\n};\n\nexport const ProvidersError: Story = {\n args: {scenario: 'providers-error'},\n};\n\nexport const NoProvidersAvailable: Story = {\n args: {scenario: 'no-providers'},\n};\n\nexport const LongNames: Story = {\n args: {scenario: 'long-names'},\n};\n\nexport const UsageModalCloseRestoresInteraction: Story = {\n play: async ({canvasElement}) => {\n const user = userEvent.setup();\n const canvas = within(canvasElement);\n const actionsButton = await canvas.findByRole('button', {\n name: 'Open acme-corp integration actions',\n });\n\n await user.click(actionsButton);\n await user.click(await screen.findByRole('menuitem', {name: 'Use this integration'}));\n await screen.findByRole('dialog', {name: 'Use acme-corp'});\n await user.click(await screen.findByRole('button', {name: 'Done'}));\n // The dialog stays mounted while it animates out, which is what keeps the\n // close from being an abrupt unmount.\n await waitFor(() => {\n expect(screen.queryByRole('dialog', {name: 'Use acme-corp'})).toHaveAttribute(\n 'data-state',\n 'closed',\n );\n });\n // Both the removal and the body pointer-events release therefore land after\n // the click rather than on the same tick.\n await waitFor(() => {\n expect(screen.queryByRole('dialog', {name: 'Use acme-corp'})).not.toBeInTheDocument();\n expect(document.body.style.pointerEvents).not.toBe('none');\n });\n // Radix dismisses on the first outside pointerdown while the previous menu is\n // still mounted, so let it finish leaving before reopening.\n await waitFor(() => {\n expect(document.querySelectorAll('[role=\"menu\"]')).toHaveLength(0);\n });\n await user.click(actionsButton);\n await screen.findByRole('menuitem', {name: 'Use this integration'});\n },\n};\n\nfunction fetchForScenario(scenario: Scenario): typeof fetch {\n return (input, init) => {\n const url = requestUrl(input);\n const method = init?.method ?? 'GET';\n if (scenario === 'loading') return new Promise<Response>(() => undefined);\n if (url.pathname === '/integration-providers') {\n if (scenario === 'providers-error') return Promise.resolve(errorResponse());\n return Promise.resolve(\n jsonResponse({providers: scenario === 'no-providers' ? [] : PROVIDERS}),\n );\n }\n if (url.pathname.startsWith('/integration-connections/') && method === 'PATCH') {\n return Promise.resolve(\n jsonResponse({\n ...connection(),\n id: url.pathname.split('/').at(-1),\n lifecycle_status: 'disabled',\n }),\n );\n }\n if (url.pathname.startsWith('/integration-connections/') && method === 'DELETE') {\n return Promise.resolve(jsonResponse(undefined, {status: 204}));\n }\n if (url.pathname.startsWith('/integrations/webhook/connections/') && method === 'PATCH') {\n return Promise.resolve(\n jsonResponse({\n id: url.pathname.split('/').at(-1),\n workspace_id: WORKSPACE_ID,\n name: 'Stripe production',\n slug: 'stripe-prod',\n lifecycle_status: 'disabled',\n inbound_url: 'https://api.example.test/webhook/77777777-7777-4777-8777-777777777777',\n created_at: '2026-04-12T00:00:00.000Z',\n updated_at: '2026-04-12T00:00:00.000Z',\n }),\n );\n }\n if (url.pathname.startsWith('/integrations/webhook/connections/') && method === 'DELETE') {\n return Promise.resolve(jsonResponse(undefined, {status: 204}));\n }\n if (url.pathname === '/integration-connections') {\n if (scenario === 'connections-error') return Promise.resolve(errorResponse());\n return Promise.resolve(jsonResponse({connections: connectionsForScenario(scenario)}));\n }\n if (url.pathname === '/integrations/webhook/connections') {\n return Promise.resolve(\n jsonResponse({\n connections: [\n {\n id: '77777777-7777-4777-8777-777777777777',\n workspace_id: WORKSPACE_ID,\n name: 'Stripe production',\n slug: 'stripe-prod',\n lifecycle_status: 'active',\n inbound_url: 'https://api.example.test/webhook/77777777-7777-4777-8777-777777777777',\n created_at: '2026-04-12T00:00:00.000Z',\n updated_at: '2026-04-12T00:00:00.000Z',\n },\n ],\n }),\n );\n }\n return Promise.resolve(jsonResponse({}, {status: 404}));\n };\n}\n\nfunction connectionsForScenario(scenario: Scenario): IntegrationConnectionDto[] {\n if (scenario === 'empty-connections' || scenario === 'no-providers') return [];\n if (scenario === 'long-names') {\n return [\n connection({\n display_name: 'acme-production-observability-and-source-control-organization',\n lifecycle_status: 'active',\n }),\n connection({\n id: '55555555-5555-4555-8555-555555555555',\n provider: 'sentry',\n display_name: 'sentry-team-with-a-very-long-connected-account-name',\n lifecycle_status: 'error',\n }),\n ];\n }\n return [\n connection({display_name: 'acme-corp', lifecycle_status: 'active'}),\n connection({\n id: '99999999-9999-4999-8999-999999999999',\n provider: 'slack',\n display_name: 'Slack Acme',\n lifecycle_status: 'active',\n capabilities: ['agent_tools'],\n external_url: 'https://app.slack.com/client/T123',\n }),\n connection({\n id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',\n provider: 'slack',\n display_name: 'Slack disabled',\n lifecycle_status: 'disabled',\n capabilities: ['agent_tools'],\n external_url: undefined,\n }),\n connection({\n id: '88888888-8888-4888-8888-888888888888',\n provider: 'linear',\n display_name: 'linear-acme',\n lifecycle_status: 'active',\n external_url: 'https://linear.app/acme/settings',\n }),\n connection({\n id: '55555555-5555-4555-8555-555555555555',\n provider: 'linear',\n display_name: 'linear-disabled',\n lifecycle_status: 'disabled',\n external_url: undefined,\n }),\n connection({\n id: '66666666-6666-4666-8666-666666666666',\n provider: 'linear',\n display_name: 'linear-error',\n lifecycle_status: 'error',\n external_url: undefined,\n }),\n connection({\n id: '77777777-7777-4777-8777-777777777777',\n provider: 'webhook',\n external_account_id: 'stripe-prod',\n slug: 'stripe-prod',\n display_name: 'Stripe production',\n capabilities: [],\n lifecycle_status: 'active',\n external_url: undefined,\n created_at: '2026-04-12T00:00:00.000Z',\n updated_at: '2026-04-12T00:00:00.000Z',\n }),\n ];\n}\n\nfunction connection(overrides: Partial<IntegrationConnectionDto> = {}): IntegrationConnectionDto {\n return {\n id: '44444444-4444-4444-8444-444444444444',\n workspace_id: WORKSPACE_ID,\n provider: 'github',\n external_account_id: 'installation-1',\n slug: 'github_acme_corp',\n display_name: 'acme-corp',\n lifecycle_status: 'active',\n capabilities: ['source_control'],\n external_url: 'https://github.com/organizations/acme-corp/settings/installations/1',\n created_at: '2026-03-12T00:00:00.000Z',\n updated_at: '2026-03-12T00:00:00.000Z',\n ...overrides,\n };\n}\n\nfunction requestUrl(input: RequestInfo | URL): URL {\n if (typeof input === 'string') return new URL(input);\n if (input instanceof URL) return input;\n return new URL(input.url);\n}\n\nfunction jsonResponse(body: unknown, init: ResponseInit = {}) {\n return new Response(JSON.stringify(body), {\n status: 200,\n headers: {'content-type': 'application/json'},\n ...init,\n });\n}\n\nfunction errorResponse() {\n return jsonResponse({code: 'server-error'}, {status: 500, statusText: 'Server error'});\n}\n"],"names":["configureApiClient","authStateAtom","Toaster","QueryClient","QueryClientProvider","createMemoryHistory","createRootRoute","createRoute","createRouter","Outlet","RouterProvider","createStore","Provider","JotaiProvider","useMemo","expect","screen","userEvent","waitFor","within","IntegrationGallery","WORKSPACE_ID","WORKSPACE_PATH","SETUP_PATHS","PROVIDERS","provider","display_name","capabilities","IntegrationGalleryStory","scenario","baseUrl","fetchImpl","fetchForScenario","queryClient","defaultOptions","queries","retry","store","nextStore","set","status","workspaces","id","name","slug","membershipId","router","rootRoute","component","workspaceRoute","getParentRoute","path","galleryRoute","div","className","workspaceId","setupRoutes","map","replace","history","initialEntries","routeTree","addChildren","client","meta","title","parameters","layout","args","Playground","EmptyConnections","Loading","ConnectionsError","ProvidersError","NoProvidersAvailable","LongNames","UsageModalCloseRestoresInteraction","play","canvasElement","user","setup","canvas","actionsButton","findByRole","click","queryByRole","toHaveAttribute","not","toBeInTheDocument","document","body","style","pointerEvents","toBe","querySelectorAll","toHaveLength","input","init","url","requestUrl","method","Promise","undefined","pathname","resolve","errorResponse","jsonResponse","providers","startsWith","connection","split","at","lifecycle_status","workspace_id","inbound_url","created_at","updated_at","connections","connectionsForScenario","external_url","external_account_id","overrides","URL","Response","JSON","stringify","headers","code","statusText"],"mappings":"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"}
|
|
@@ -11,6 +11,6 @@ interface IntegrationUsageModalProps {
|
|
|
11
11
|
onOpenChange: (open: boolean) => void;
|
|
12
12
|
children?: ReactNode;
|
|
13
13
|
}
|
|
14
|
-
export declare function IntegrationUsageModal({ connection, events, open, onOpenChange, children, }: IntegrationUsageModalProps): import("react").JSX.Element
|
|
14
|
+
export declare function IntegrationUsageModal({ connection, events, open, onOpenChange, children, }: IntegrationUsageModalProps): import("react").JSX.Element;
|
|
15
15
|
export {};
|
|
16
16
|
//# sourceMappingURL=integration-usage-modal.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration-usage-modal.d.ts","sourceRoot":"","sources":["../../src/components/integration-usage-modal.tsx"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAGrC,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,iBAAiB,CAAC;AAE3D,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,0BAA0B;IAClC,UAAU,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACzC,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAID,wBAAgB,qBAAqB,CAAC,EACpC,UAAU,EACV,MAAM,EACN,IAAI,EACJ,YAAY,EACZ,QAAQ,GACT,EAAE,0BAA0B
|
|
1
|
+
{"version":3,"file":"integration-usage-modal.d.ts","sourceRoot":"","sources":["../../src/components/integration-usage-modal.tsx"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAGrC,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,iBAAiB,CAAC;AAE3D,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,0BAA0B;IAClC,UAAU,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACzC,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAID,wBAAgB,qBAAqB,CAAC,EACpC,UAAU,EACV,MAAM,EACN,IAAI,EACJ,YAAY,EACZ,QAAQ,GACT,EAAE,0BAA0B,+BAwH5B"}
|
|
@@ -4,7 +4,7 @@ import { CodeBlock, CodeBlockBody, CodeBlockContent, CodeBlockCopyButton, CodeBl
|
|
|
4
4
|
import { Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, ModalTitle } from '@shipfox/react-ui/modal';
|
|
5
5
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@shipfox/react-ui/select';
|
|
6
6
|
import { Text } from '@shipfox/react-ui/typography';
|
|
7
|
-
import { useEffect, useMemo, useState } from 'react';
|
|
7
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
8
8
|
import { ConnectionStatusBadge } from '#connection-status-badge.js';
|
|
9
9
|
const WORKFLOW_FILENAME = '.shipfox/workflows/integration.yml';
|
|
10
10
|
export function IntegrationUsageModal({ connection, events, open, onOpenChange, children }) {
|
|
@@ -16,8 +16,13 @@ export function IntegrationUsageModal({ connection, events, open, onOpenChange,
|
|
|
16
16
|
}, [
|
|
17
17
|
eventValuesKey
|
|
18
18
|
]);
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
// The caller clears the connection in the same render that closes the modal,
|
|
20
|
+
// so keep the last one to render against while the surface animates out.
|
|
21
|
+
const lastConnection = useRef(connection);
|
|
22
|
+
if (connection !== null) lastConnection.current = connection;
|
|
23
|
+
const shownConnection = connection ?? lastConnection.current;
|
|
24
|
+
const workflowExample = shownConnection ? buildWorkflowExample({
|
|
25
|
+
source: shownConnection.slug,
|
|
21
26
|
event: selectedEvent
|
|
22
27
|
}) : '';
|
|
23
28
|
const data = useMemo(()=>[
|
|
@@ -29,10 +34,9 @@ export function IntegrationUsageModal({ connection, events, open, onOpenChange,
|
|
|
29
34
|
], [
|
|
30
35
|
workflowExample
|
|
31
36
|
]);
|
|
32
|
-
const title =
|
|
33
|
-
if (!open || connection === null) return null;
|
|
37
|
+
const title = shownConnection ? `Use ${shownConnection.displayName}` : 'Use integration';
|
|
34
38
|
return /*#__PURE__*/ _jsx(Modal, {
|
|
35
|
-
open: open,
|
|
39
|
+
open: open && connection !== null,
|
|
36
40
|
onOpenChange: onOpenChange,
|
|
37
41
|
children: /*#__PURE__*/ _jsxs(ModalContent, {
|
|
38
42
|
"aria-describedby": undefined,
|
|
@@ -55,8 +59,8 @@ export function IntegrationUsageModal({ connection, events, open, onOpenChange,
|
|
|
55
59
|
className: "truncate",
|
|
56
60
|
children: title
|
|
57
61
|
}),
|
|
58
|
-
|
|
59
|
-
status:
|
|
62
|
+
shownConnection ? /*#__PURE__*/ _jsx(ConnectionStatusBadge, {
|
|
63
|
+
status: shownConnection.lifecycleStatus,
|
|
60
64
|
className: "shrink-0"
|
|
61
65
|
}) : null
|
|
62
66
|
]
|
|
@@ -69,7 +73,7 @@ export function IntegrationUsageModal({ connection, events, open, onOpenChange,
|
|
|
69
73
|
]
|
|
70
74
|
})
|
|
71
75
|
}),
|
|
72
|
-
|
|
76
|
+
shownConnection ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
73
77
|
children: [
|
|
74
78
|
/*#__PURE__*/ _jsxs(ModalBody, {
|
|
75
79
|
className: "min-h-0 flex-1 gap-section overflow-y-auto overflow-x-clip scrollbar",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/integration-usage-modal.tsx"],"sourcesContent":["import {Button} from '@shipfox/react-ui/button';\nimport {\n CodeBlock,\n CodeBlockBody,\n CodeBlockContent,\n CodeBlockCopyButton,\n CodeBlockFilename,\n CodeBlockFiles,\n CodeBlockHeader,\n CodeBlockItem,\n} from '@shipfox/react-ui/code-block';\nimport {\n Modal,\n ModalBody,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalTitle,\n} from '@shipfox/react-ui/modal';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@shipfox/react-ui/select';\nimport {Text} from '@shipfox/react-ui/typography';\nimport type {ReactNode} from 'react';\nimport {useEffect, useMemo, useState} from 'react';\nimport {ConnectionStatusBadge} from '#connection-status-badge.js';\nimport type {IntegrationConnection} from '#core/models.js';\n\nexport interface IntegrationUsageEvent {\n value: string;\n label: string;\n}\n\ninterface IntegrationUsageModalProps {\n connection: IntegrationConnection | null;\n events: IntegrationUsageEvent[];\n open: boolean;\n onOpenChange: (open: boolean) => void;\n children?: ReactNode;\n}\n\nconst WORKFLOW_FILENAME = '.shipfox/workflows/integration.yml';\n\nexport function IntegrationUsageModal({\n connection,\n events,\n open,\n onOpenChange,\n children,\n}: IntegrationUsageModalProps) {\n const [selectedEvent, setSelectedEvent] = useState('');\n const eventValuesKey = events.map((event) => event.value).join('\\u0000');\n\n useEffect(() => {\n const eventValues = eventValuesKey ? eventValuesKey.split('\\u0000') : [];\n setSelectedEvent((current) =>\n eventValues.includes(current) ? current : (eventValues[0] ?? ''),\n );\n }, [eventValuesKey]);\n\n const workflowExample = connection\n ? buildWorkflowExample({source: connection.slug, event: selectedEvent})\n : '';\n const data = useMemo(\n () => [{language: 'yaml', filename: WORKFLOW_FILENAME, code: workflowExample}],\n [workflowExample],\n );\n const title = connection ? `Use ${connection.displayName}` : 'Use integration';\n\n if (!open || connection === null) return null;\n\n return (\n <Modal open={open} onOpenChange={onOpenChange}>\n <ModalContent aria-describedby={undefined} className=\"max-h-[calc(100vh-32px)] max-w-[640px]\">\n <ModalTitle className=\"sr-only\">{title}</ModalTitle>\n <ModalHeader>\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <div className=\"flex min-w-0 items-center gap-inline\">\n <Text size=\"lg\" aria-hidden=\"true\" className=\"truncate\">\n {title}\n </Text>\n {connection ? (\n <ConnectionStatusBadge status={connection.lifecycleStatus} className=\"shrink-0\" />\n ) : null}\n </div>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Reference this connection from a workflow trigger.\n </Text>\n </div>\n </ModalHeader>\n {connection ? (\n <>\n <ModalBody className=\"min-h-0 flex-1 gap-section overflow-y-auto overflow-x-clip scrollbar\">\n <section className=\"flex w-full flex-col gap-cluster\">\n <Text size=\"md\" bold>\n Usage\n </Text>\n {events.length > 1 ? (\n <div className=\"flex flex-col gap-inline\">\n <label\n htmlFor=\"integration-usage-event\"\n className=\"font-display text-sm font-medium text-foreground-neutral-base\"\n >\n Event\n </label>\n <Select value={selectedEvent} onValueChange={setSelectedEvent}>\n <SelectTrigger id=\"integration-usage-event\">\n <SelectValue placeholder=\"Select event\" />\n </SelectTrigger>\n <SelectContent>\n {events.map((event) => (\n <SelectItem key={event.value} value={event.value}>\n {event.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n <CodeBlock data={data} className=\"h-auto min-h-0 rounded-8\">\n <CodeBlockHeader>\n <CodeBlockFiles>\n {(item) => (\n <CodeBlockFilename value={item.filename}>{item.filename}</CodeBlockFilename>\n )}\n </CodeBlockFiles>\n <CodeBlockCopyButton />\n </CodeBlockHeader>\n <CodeBlockBody>\n {(item) => (\n <CodeBlockItem value={item.filename}>\n <CodeBlockContent\n language=\"yaml\"\n syntaxHighlighting\n highlightedLineRange={{startLine: 3, endLine: 4}}\n >\n {item.code}\n </CodeBlockContent>\n </CodeBlockItem>\n )}\n </CodeBlockBody>\n </CodeBlock>\n </section>\n {children ? (\n <section className=\"flex w-full flex-col gap-cluster\">\n <Text size=\"md\" bold>\n Details\n </Text>\n {children}\n </section>\n ) : null}\n </ModalBody>\n <ModalFooter>\n <Button type=\"button\" onClick={() => onOpenChange(false)}>\n Done\n </Button>\n </ModalFooter>\n </>\n ) : null}\n </ModalContent>\n </Modal>\n );\n}\n\nfunction buildWorkflowExample({source, event}: {source: string; event: string}) {\n return [\n 'triggers:',\n ' integration:',\n ` source: ${source}`,\n ` event: ${event}`,\n 'jobs:',\n ' handle-event:',\n ' steps:',\n ' - run: echo \"Received event\"',\n ].join('\\n');\n}\n"],"names":["Button","CodeBlock","CodeBlockBody","CodeBlockContent","CodeBlockCopyButton","CodeBlockFilename","CodeBlockFiles","CodeBlockHeader","CodeBlockItem","Modal","ModalBody","ModalContent","ModalFooter","ModalHeader","ModalTitle","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","Text","useEffect","useMemo","useState","ConnectionStatusBadge","WORKFLOW_FILENAME","IntegrationUsageModal","connection","events","open","onOpenChange","children","selectedEvent","setSelectedEvent","eventValuesKey","map","event","value","join","eventValues","split","current","includes","workflowExample","buildWorkflowExample","source","slug","data","language","filename","code","title","displayName","aria-describedby","undefined","className","div","size","aria-hidden","status","lifecycleStatus","section","bold","length","label","htmlFor","onValueChange","id","placeholder","item","syntaxHighlighting","highlightedLineRange","startLine","endLine","type","onClick"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/components/integration-usage-modal.tsx"],"sourcesContent":["import {Button} from '@shipfox/react-ui/button';\nimport {\n CodeBlock,\n CodeBlockBody,\n CodeBlockContent,\n CodeBlockCopyButton,\n CodeBlockFilename,\n CodeBlockFiles,\n CodeBlockHeader,\n CodeBlockItem,\n} from '@shipfox/react-ui/code-block';\nimport {\n Modal,\n ModalBody,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalTitle,\n} from '@shipfox/react-ui/modal';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@shipfox/react-ui/select';\nimport {Text} from '@shipfox/react-ui/typography';\nimport type {ReactNode} from 'react';\nimport {useEffect, useMemo, useRef, useState} from 'react';\nimport {ConnectionStatusBadge} from '#connection-status-badge.js';\nimport type {IntegrationConnection} from '#core/models.js';\n\nexport interface IntegrationUsageEvent {\n value: string;\n label: string;\n}\n\ninterface IntegrationUsageModalProps {\n connection: IntegrationConnection | null;\n events: IntegrationUsageEvent[];\n open: boolean;\n onOpenChange: (open: boolean) => void;\n children?: ReactNode;\n}\n\nconst WORKFLOW_FILENAME = '.shipfox/workflows/integration.yml';\n\nexport function IntegrationUsageModal({\n connection,\n events,\n open,\n onOpenChange,\n children,\n}: IntegrationUsageModalProps) {\n const [selectedEvent, setSelectedEvent] = useState('');\n const eventValuesKey = events.map((event) => event.value).join('\\u0000');\n\n useEffect(() => {\n const eventValues = eventValuesKey ? eventValuesKey.split('\\u0000') : [];\n setSelectedEvent((current) =>\n eventValues.includes(current) ? current : (eventValues[0] ?? ''),\n );\n }, [eventValuesKey]);\n\n // The caller clears the connection in the same render that closes the modal,\n // so keep the last one to render against while the surface animates out.\n const lastConnection = useRef(connection);\n if (connection !== null) lastConnection.current = connection;\n const shownConnection = connection ?? lastConnection.current;\n\n const workflowExample = shownConnection\n ? buildWorkflowExample({source: shownConnection.slug, event: selectedEvent})\n : '';\n const data = useMemo(\n () => [{language: 'yaml', filename: WORKFLOW_FILENAME, code: workflowExample}],\n [workflowExample],\n );\n const title = shownConnection ? `Use ${shownConnection.displayName}` : 'Use integration';\n\n return (\n <Modal open={open && connection !== null} onOpenChange={onOpenChange}>\n <ModalContent aria-describedby={undefined} className=\"max-h-[calc(100vh-32px)] max-w-[640px]\">\n <ModalTitle className=\"sr-only\">{title}</ModalTitle>\n <ModalHeader>\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <div className=\"flex min-w-0 items-center gap-inline\">\n <Text size=\"lg\" aria-hidden=\"true\" className=\"truncate\">\n {title}\n </Text>\n {shownConnection ? (\n <ConnectionStatusBadge\n status={shownConnection.lifecycleStatus}\n className=\"shrink-0\"\n />\n ) : null}\n </div>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Reference this connection from a workflow trigger.\n </Text>\n </div>\n </ModalHeader>\n {shownConnection ? (\n <>\n <ModalBody className=\"min-h-0 flex-1 gap-section overflow-y-auto overflow-x-clip scrollbar\">\n <section className=\"flex w-full flex-col gap-cluster\">\n <Text size=\"md\" bold>\n Usage\n </Text>\n {events.length > 1 ? (\n <div className=\"flex flex-col gap-inline\">\n <label\n htmlFor=\"integration-usage-event\"\n className=\"font-display text-sm font-medium text-foreground-neutral-base\"\n >\n Event\n </label>\n <Select value={selectedEvent} onValueChange={setSelectedEvent}>\n <SelectTrigger id=\"integration-usage-event\">\n <SelectValue placeholder=\"Select event\" />\n </SelectTrigger>\n <SelectContent>\n {events.map((event) => (\n <SelectItem key={event.value} value={event.value}>\n {event.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n <CodeBlock data={data} className=\"h-auto min-h-0 rounded-8\">\n <CodeBlockHeader>\n <CodeBlockFiles>\n {(item) => (\n <CodeBlockFilename value={item.filename}>{item.filename}</CodeBlockFilename>\n )}\n </CodeBlockFiles>\n <CodeBlockCopyButton />\n </CodeBlockHeader>\n <CodeBlockBody>\n {(item) => (\n <CodeBlockItem value={item.filename}>\n <CodeBlockContent\n language=\"yaml\"\n syntaxHighlighting\n highlightedLineRange={{startLine: 3, endLine: 4}}\n >\n {item.code}\n </CodeBlockContent>\n </CodeBlockItem>\n )}\n </CodeBlockBody>\n </CodeBlock>\n </section>\n {children ? (\n <section className=\"flex w-full flex-col gap-cluster\">\n <Text size=\"md\" bold>\n Details\n </Text>\n {children}\n </section>\n ) : null}\n </ModalBody>\n <ModalFooter>\n <Button type=\"button\" onClick={() => onOpenChange(false)}>\n Done\n </Button>\n </ModalFooter>\n </>\n ) : null}\n </ModalContent>\n </Modal>\n );\n}\n\nfunction buildWorkflowExample({source, event}: {source: string; event: string}) {\n return [\n 'triggers:',\n ' integration:',\n ` source: ${source}`,\n ` event: ${event}`,\n 'jobs:',\n ' handle-event:',\n ' steps:',\n ' - run: echo \"Received event\"',\n ].join('\\n');\n}\n"],"names":["Button","CodeBlock","CodeBlockBody","CodeBlockContent","CodeBlockCopyButton","CodeBlockFilename","CodeBlockFiles","CodeBlockHeader","CodeBlockItem","Modal","ModalBody","ModalContent","ModalFooter","ModalHeader","ModalTitle","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","Text","useEffect","useMemo","useRef","useState","ConnectionStatusBadge","WORKFLOW_FILENAME","IntegrationUsageModal","connection","events","open","onOpenChange","children","selectedEvent","setSelectedEvent","eventValuesKey","map","event","value","join","eventValues","split","current","includes","lastConnection","shownConnection","workflowExample","buildWorkflowExample","source","slug","data","language","filename","code","title","displayName","aria-describedby","undefined","className","div","size","aria-hidden","status","lifecycleStatus","section","bold","length","label","htmlFor","onValueChange","id","placeholder","item","syntaxHighlighting","highlightedLineRange","startLine","endLine","type","onClick"],"mappings":"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"}
|