@shipfox/client-integrations 21.0.0 → 22.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.
Files changed (50) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +19 -0
  3. package/dist/components/callback-status-shell.d.ts.map +1 -1
  4. package/dist/components/callback-status-shell.js +4 -3
  5. package/dist/components/callback-status-shell.js.map +1 -1
  6. package/dist/components/connection-picker.js +2 -2
  7. package/dist/components/connection-picker.js.map +1 -1
  8. package/dist/components/installed-integrations-section.d.ts.map +1 -1
  9. package/dist/components/installed-integrations-section.js +155 -141
  10. package/dist/components/installed-integrations-section.js.map +1 -1
  11. package/dist/components/integration-gallery.stories.d.ts.map +1 -1
  12. package/dist/components/integration-gallery.stories.js +7 -2
  13. package/dist/components/integration-gallery.stories.js.map +1 -1
  14. package/dist/components/provider-grid.d.ts +0 -2
  15. package/dist/components/provider-grid.d.ts.map +1 -1
  16. package/dist/components/provider-grid.js +88 -104
  17. package/dist/components/provider-grid.js.map +1 -1
  18. package/dist/components/redirect-install-page.d.ts.map +1 -1
  19. package/dist/components/redirect-install-page.js +3 -2
  20. package/dist/components/redirect-install-page.js.map +1 -1
  21. package/dist/components/repository-picker.d.ts +1 -4
  22. package/dist/components/repository-picker.d.ts.map +1 -1
  23. package/dist/components/repository-picker.js +24 -37
  24. package/dist/components/repository-picker.js.map +1 -1
  25. package/dist/pages/gitea-install-page.js +1 -1
  26. package/dist/pages/gitea-install-page.js.map +1 -1
  27. package/dist/pages/jira-callback-page.js +4 -4
  28. package/dist/pages/jira-callback-page.js.map +1 -1
  29. package/dist/pages/sentry-callback-page.js +4 -4
  30. package/dist/pages/sentry-callback-page.js.map +1 -1
  31. package/dist/pages/source-control-onboarding-page.js +1 -1
  32. package/dist/pages/source-control-onboarding-page.js.map +1 -1
  33. package/dist/tsconfig.test.tsbuildinfo +1 -1
  34. package/package.json +6 -6
  35. package/src/components/callback-status-shell.tsx +4 -3
  36. package/src/components/connection-picker.tsx +2 -2
  37. package/src/components/installed-integrations-section.tsx +110 -98
  38. package/src/components/integration-gallery.stories.tsx +7 -2
  39. package/src/components/integration-gallery.test.tsx +3 -2
  40. package/src/components/provider-grid.tsx +69 -79
  41. package/src/components/redirect-install-page.test.tsx +4 -0
  42. package/src/components/redirect-install-page.tsx +3 -2
  43. package/src/components/repository-picker.test.tsx +27 -13
  44. package/src/components/repository-picker.tsx +22 -47
  45. package/src/pages/gitea-install-page.tsx +1 -1
  46. package/src/pages/jira-callback-page.tsx +4 -4
  47. package/src/pages/sentry-callback-page.tsx +3 -3
  48. package/src/pages/slack-callback-page.test.tsx +7 -0
  49. package/src/pages/source-control-onboarding-page.tsx +1 -1
  50. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 59 files with swc (132.01ms)
2
+ Successfully compiled: 59 files with swc (414.74ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @shipfox/client-integrations
2
2
 
3
+ ## 22.0.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 50b3867: Consolidate authentication, onboarding, integration-install, and callback surfaces onto the shared focused frame.
8
+ - 7693eb3: Render empty and load-error states inside bordered panel bodies and keep loading placeholders aligned with their data regions.
9
+ - 00c1cb8: Panels across these surfaces share one hover, focus, and elevation treatment. Grids of openable things, including the integration gallery, the available providers grid, and the harness picker, render as cells inside a single panel divided by hairlines instead of separate bordered tiles.
10
+ - 56f4526: Project settings and project creation now use shared panels, with source integrations presented as button-style radio choices.
11
+ - Updated dependencies [50b3867]
12
+ - Updated dependencies [00c1cb8]
13
+ - Updated dependencies [7693eb3]
14
+ - Updated dependencies [00c1cb8]
15
+ - Updated dependencies [56f4526]
16
+ - @shipfox/client-auth@22.0.0
17
+ - @shipfox/client-shell@22.0.0
18
+ - @shipfox/react-ui@2.1.0
19
+ - @shipfox/client-ui@22.0.0
20
+ - @shipfox/integration-icons@0.3.5
21
+
3
22
  ## 21.0.0
4
23
 
5
24
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"callback-status-shell.d.ts","sourceRoot":"","sources":["../../src/components/callback-status-shell.tsx"],"names":[],"mappings":"AAMA,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,OAAO,EACP,MAAgB,EAChB,SAAS,EACT,aAAa,EACb,aAAa,EACb,WAAW,GACZ,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,EACP,uCAAuC,GACvC,sCAAsC,GACtC,qCAAqC,CAAC;CAC3C,+BAmDA"}
1
+ {"version":3,"file":"callback-status-shell.d.ts","sourceRoot":"","sources":["../../src/components/callback-status-shell.tsx"],"names":[],"mappings":"AAOA,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,OAAO,EACP,MAAgB,EAChB,SAAS,EACT,aAAa,EACb,aAAa,EACb,WAAW,GACZ,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,EACP,uCAAuC,GACvC,sCAAsC,GACtC,qCAAqC,CAAC;CAC3C,+BAmDA"}
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { FocusedFrame } from '@shipfox/client-shell/runtime';
2
3
  import { ButtonLink } from '@shipfox/react-ui/button';
3
4
  import { Callout } from '@shipfox/react-ui/callout';
4
5
  import { Text } from '@shipfox/react-ui/typography';
@@ -30,9 +31,9 @@ export function CallbackStatusShell({ title, message, status = 'error', startOve
30
31
  });
31
32
  const logoutRedirect = workspaceSlug ? installPath.replace('$workspaceSlug', workspaceSlug) : undefined;
32
33
  return /*#__PURE__*/ _jsx("main", {
33
- className: "flex min-h-screen px-row py-frame",
34
- children: /*#__PURE__*/ _jsxs("div", {
35
- className: "mx-auto flex w-full flex-col justify-center gap-section",
34
+ className: "flex min-h-screen px-frame py-frame",
35
+ children: /*#__PURE__*/ _jsxs(FocusedFrame, {
36
+ className: "flex flex-col justify-center gap-section",
36
37
  children: [
37
38
  /*#__PURE__*/ _jsx("h2", {
38
39
  ref: headingRef,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/callback-status-shell.tsx"],"sourcesContent":["import {ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {useEffect, useRef} from 'react';\n\nexport function CallbackStatusShell({\n title,\n message,\n status = 'error',\n startOver,\n switchAccount,\n workspaceSlug,\n installPath,\n}: {\n title: string;\n message: string;\n status?: 'error' | 'success';\n startOver?: boolean;\n switchAccount?: boolean;\n workspaceSlug?: string | undefined;\n installPath:\n | '/w/$workspaceSlug/integrations/linear'\n | '/w/$workspaceSlug/integrations/slack'\n | '/w/$workspaceSlug/integrations/jira';\n}) {\n const headingRef = useRef<HTMLHeadingElement>(null);\n useEffect(() => headingRef.current?.focus(), []);\n const recoveryVariant = startOver || switchAccount ? 'muted' : 'base';\n const settings = workspaceSlug ? (\n <ButtonLink asChild variant={recoveryVariant} className=\"min-h-44 w-full sm:w-fit\">\n <Link to=\"/w/$workspaceSlug/settings/integrations\" params={{workspaceSlug}}>\n Back to integrations\n </Link>\n </ButtonLink>\n ) : (\n <ButtonLink asChild variant={recoveryVariant} className=\"min-h-44 w-full sm:w-fit\">\n <Link to=\"/\">Back to Shipfox</Link>\n </ButtonLink>\n );\n const logoutRedirect = workspaceSlug\n ? installPath.replace('$workspaceSlug', workspaceSlug)\n : undefined;\n\n return (\n <main className=\"flex min-h-screen px-row py-frame\">\n <div className=\"mx-auto flex w-full flex-col justify-center gap-section\">\n <h2 ref={headingRef} tabIndex={-1} className=\"text-24 font-semibold outline-none\">\n {title}\n </h2>\n <Callout role={status === 'error' ? 'alert' : 'status'} type={status}>\n <Text size=\"sm\">{message}</Text>\n </Callout>\n <div className=\"flex flex-col gap-inline sm:flex-row sm:items-center\">\n {switchAccount ? (\n <ButtonLink asChild className=\"min-h-44 w-full sm:w-fit\">\n <Link\n to=\"/auth/logout\"\n search={logoutRedirect ? {redirect: logoutRedirect} : undefined}\n >\n Switch account\n </Link>\n </ButtonLink>\n ) : null}\n {startOver && workspaceSlug ? (\n <ButtonLink asChild className=\"min-h-44 w-full sm:w-fit\">\n <Link to={installPath} params={{workspaceSlug}}>\n Start over\n </Link>\n </ButtonLink>\n ) : null}\n {settings}\n </div>\n </div>\n </main>\n );\n}\n"],"names":["ButtonLink","Callout","Text","Link","useEffect","useRef","CallbackStatusShell","title","message","status","startOver","switchAccount","workspaceSlug","installPath","headingRef","current","focus","recoveryVariant","settings","asChild","variant","className","to","params","logoutRedirect","replace","undefined","main","div","h2","ref","tabIndex","role","type","size","search","redirect"],"mappings":";AAAA,SAAQA,UAAU,QAAO,2BAA2B;AACpD,SAAQC,OAAO,QAAO,4BAA4B;AAClD,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,SAAS,EAAEC,MAAM,QAAO,QAAQ;AAExC,OAAO,SAASC,oBAAoB,EAClCC,KAAK,EACLC,OAAO,EACPC,SAAS,OAAO,EAChBC,SAAS,EACTC,aAAa,EACbC,aAAa,EACbC,WAAW,EAYZ;IACC,MAAMC,aAAaT,OAA2B;IAC9CD,UAAU,IAAMU,WAAWC,OAAO,EAAEC,SAAS,EAAE;IAC/C,MAAMC,kBAAkBP,aAAaC,gBAAgB,UAAU;IAC/D,MAAMO,WAAWN,8BACf,KAACZ;QAAWmB,OAAO;QAACC,SAASH;QAAiBI,WAAU;kBACtD,cAAA,KAAClB;YAAKmB,IAAG;YAA0CC,QAAQ;gBAACX;YAAa;sBAAG;;uBAK9E,KAACZ;QAAWmB,OAAO;QAACC,SAASH;QAAiBI,WAAU;kBACtD,cAAA,KAAClB;YAAKmB,IAAG;sBAAI;;;IAGjB,MAAME,iBAAiBZ,gBACnBC,YAAYY,OAAO,CAAC,kBAAkBb,iBACtCc;IAEJ,qBACE,KAACC;QAAKN,WAAU;kBACd,cAAA,MAACO;YAAIP,WAAU;;8BACb,KAACQ;oBAAGC,KAAKhB;oBAAYiB,UAAU,CAAC;oBAAGV,WAAU;8BAC1Cd;;8BAEH,KAACN;oBAAQ+B,MAAMvB,WAAW,UAAU,UAAU;oBAAUwB,MAAMxB;8BAC5D,cAAA,KAACP;wBAAKgC,MAAK;kCAAM1B;;;8BAEnB,MAACoB;oBAAIP,WAAU;;wBACZV,8BACC,KAACX;4BAAWmB,OAAO;4BAACE,WAAU;sCAC5B,cAAA,KAAClB;gCACCmB,IAAG;gCACHa,QAAQX,iBAAiB;oCAACY,UAAUZ;gCAAc,IAAIE;0CACvD;;6BAID;wBACHhB,aAAaE,8BACZ,KAACZ;4BAAWmB,OAAO;4BAACE,WAAU;sCAC5B,cAAA,KAAClB;gCAAKmB,IAAIT;gCAAaU,QAAQ;oCAACX;gCAAa;0CAAG;;6BAIhD;wBACHM;;;;;;AAKX"}
1
+ {"version":3,"sources":["../../src/components/callback-status-shell.tsx"],"sourcesContent":["import {FocusedFrame} from '@shipfox/client-shell/runtime';\nimport {ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {useEffect, useRef} from 'react';\n\nexport function CallbackStatusShell({\n title,\n message,\n status = 'error',\n startOver,\n switchAccount,\n workspaceSlug,\n installPath,\n}: {\n title: string;\n message: string;\n status?: 'error' | 'success';\n startOver?: boolean;\n switchAccount?: boolean;\n workspaceSlug?: string | undefined;\n installPath:\n | '/w/$workspaceSlug/integrations/linear'\n | '/w/$workspaceSlug/integrations/slack'\n | '/w/$workspaceSlug/integrations/jira';\n}) {\n const headingRef = useRef<HTMLHeadingElement>(null);\n useEffect(() => headingRef.current?.focus(), []);\n const recoveryVariant = startOver || switchAccount ? 'muted' : 'base';\n const settings = workspaceSlug ? (\n <ButtonLink asChild variant={recoveryVariant} className=\"min-h-44 w-full sm:w-fit\">\n <Link to=\"/w/$workspaceSlug/settings/integrations\" params={{workspaceSlug}}>\n Back to integrations\n </Link>\n </ButtonLink>\n ) : (\n <ButtonLink asChild variant={recoveryVariant} className=\"min-h-44 w-full sm:w-fit\">\n <Link to=\"/\">Back to Shipfox</Link>\n </ButtonLink>\n );\n const logoutRedirect = workspaceSlug\n ? installPath.replace('$workspaceSlug', workspaceSlug)\n : undefined;\n\n return (\n <main className=\"flex min-h-screen px-frame py-frame\">\n <FocusedFrame className=\"flex flex-col justify-center gap-section\">\n <h2 ref={headingRef} tabIndex={-1} className=\"text-24 font-semibold outline-none\">\n {title}\n </h2>\n <Callout role={status === 'error' ? 'alert' : 'status'} type={status}>\n <Text size=\"sm\">{message}</Text>\n </Callout>\n <div className=\"flex flex-col gap-inline sm:flex-row sm:items-center\">\n {switchAccount ? (\n <ButtonLink asChild className=\"min-h-44 w-full sm:w-fit\">\n <Link\n to=\"/auth/logout\"\n search={logoutRedirect ? {redirect: logoutRedirect} : undefined}\n >\n Switch account\n </Link>\n </ButtonLink>\n ) : null}\n {startOver && workspaceSlug ? (\n <ButtonLink asChild className=\"min-h-44 w-full sm:w-fit\">\n <Link to={installPath} params={{workspaceSlug}}>\n Start over\n </Link>\n </ButtonLink>\n ) : null}\n {settings}\n </div>\n </FocusedFrame>\n </main>\n );\n}\n"],"names":["FocusedFrame","ButtonLink","Callout","Text","Link","useEffect","useRef","CallbackStatusShell","title","message","status","startOver","switchAccount","workspaceSlug","installPath","headingRef","current","focus","recoveryVariant","settings","asChild","variant","className","to","params","logoutRedirect","replace","undefined","main","h2","ref","tabIndex","role","type","size","div","search","redirect"],"mappings":";AAAA,SAAQA,YAAY,QAAO,gCAAgC;AAC3D,SAAQC,UAAU,QAAO,2BAA2B;AACpD,SAAQC,OAAO,QAAO,4BAA4B;AAClD,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,SAAS,EAAEC,MAAM,QAAO,QAAQ;AAExC,OAAO,SAASC,oBAAoB,EAClCC,KAAK,EACLC,OAAO,EACPC,SAAS,OAAO,EAChBC,SAAS,EACTC,aAAa,EACbC,aAAa,EACbC,WAAW,EAYZ;IACC,MAAMC,aAAaT,OAA2B;IAC9CD,UAAU,IAAMU,WAAWC,OAAO,EAAEC,SAAS,EAAE;IAC/C,MAAMC,kBAAkBP,aAAaC,gBAAgB,UAAU;IAC/D,MAAMO,WAAWN,8BACf,KAACZ;QAAWmB,OAAO;QAACC,SAASH;QAAiBI,WAAU;kBACtD,cAAA,KAAClB;YAAKmB,IAAG;YAA0CC,QAAQ;gBAACX;YAAa;sBAAG;;uBAK9E,KAACZ;QAAWmB,OAAO;QAACC,SAASH;QAAiBI,WAAU;kBACtD,cAAA,KAAClB;YAAKmB,IAAG;sBAAI;;;IAGjB,MAAME,iBAAiBZ,gBACnBC,YAAYY,OAAO,CAAC,kBAAkBb,iBACtCc;IAEJ,qBACE,KAACC;QAAKN,WAAU;kBACd,cAAA,MAACtB;YAAasB,WAAU;;8BACtB,KAACO;oBAAGC,KAAKf;oBAAYgB,UAAU,CAAC;oBAAGT,WAAU;8BAC1Cd;;8BAEH,KAACN;oBAAQ8B,MAAMtB,WAAW,UAAU,UAAU;oBAAUuB,MAAMvB;8BAC5D,cAAA,KAACP;wBAAK+B,MAAK;kCAAMzB;;;8BAEnB,MAAC0B;oBAAIb,WAAU;;wBACZV,8BACC,KAACX;4BAAWmB,OAAO;4BAACE,WAAU;sCAC5B,cAAA,KAAClB;gCACCmB,IAAG;gCACHa,QAAQX,iBAAiB;oCAACY,UAAUZ;gCAAc,IAAIE;0CACvD;;6BAID;wBACHhB,aAAaE,8BACZ,KAACZ;4BAAWmB,OAAO;4BAACE,WAAU;sCAC5B,cAAA,KAAClB;gCAAKmB,IAAIT;gCAAaU,QAAQ;oCAACX;gCAAa;0CAAG;;6BAIhD;wBACHM;;;;;;AAKX"}
@@ -9,7 +9,7 @@ import { useId } from 'react';
9
9
  export function ConnectionPicker({ connections, selectedConnectionId, onSelect }) {
10
10
  const labelId = useId();
11
11
  return /*#__PURE__*/ _jsxs("div", {
12
- className: "flex flex-col gap-inline",
12
+ className: "flex min-w-0 flex-col",
13
13
  children: [
14
14
  /*#__PURE__*/ _jsx(Label, {
15
15
  id: labelId,
@@ -17,10 +17,10 @@ export function ConnectionPicker({ connections, selectedConnectionId, onSelect }
17
17
  children: "Source integration"
18
18
  }),
19
19
  /*#__PURE__*/ _jsx(RadioGroup, {
20
+ variant: "cell",
20
21
  "aria-labelledby": labelId,
21
22
  value: selectedConnectionId ?? '',
22
23
  onValueChange: onSelect,
23
- className: "grid grid-cols-2 gap-inline max-[760px]:grid-cols-1",
24
24
  children: connections.map((connection)=>/*#__PURE__*/ _jsx(RadioGroupItem, {
25
25
  value: connection.id,
26
26
  children: /*#__PURE__*/ _jsx(ConnectionOption, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/connection-picker.tsx"],"sourcesContent":["import {IntegrationIcon} from '@shipfox/integration-icons';\nimport {useIsTextTruncated} from '@shipfox/react-ui/hooks';\nimport {Label} from '@shipfox/react-ui/label';\nimport {RadioGroup, RadioGroupItem} from '@shipfox/react-ui/radio-group';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useId} from 'react';\nimport type {IntegrationConnection} from '#core/models.js';\n\nexport function ConnectionPicker({\n connections,\n selectedConnectionId,\n onSelect,\n}: {\n connections: IntegrationConnection[];\n selectedConnectionId: string | undefined;\n onSelect: (connectionId: string) => void;\n}) {\n const labelId = useId();\n\n return (\n <div className=\"flex flex-col gap-inline\">\n <Label id={labelId} className=\"sr-only\">\n Source integration\n </Label>\n <RadioGroup\n aria-labelledby={labelId}\n value={selectedConnectionId ?? ''}\n onValueChange={onSelect}\n className=\"grid grid-cols-2 gap-inline max-[760px]:grid-cols-1\"\n >\n {connections.map((connection) => (\n <RadioGroupItem key={connection.id} value={connection.id}>\n <ConnectionOption connection={connection} />\n </RadioGroupItem>\n ))}\n </RadioGroup>\n </div>\n );\n}\n\nfunction ConnectionOption({connection}: {connection: IntegrationConnection}) {\n const {ref: nameRef, isTruncated} = useIsTextTruncated<HTMLSpanElement>(connection.displayName);\n\n return (\n <span className=\"flex min-w-0 items-center gap-inline\">\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"shrink-0\">\n <IntegrationIcon\n source={connection.provider}\n aria-hidden\n className=\"size-20 text-foreground-neutral-base\"\n />\n </span>\n </TooltipTrigger>\n <TooltipContent>{integrationName(connection.provider)}</TooltipContent>\n </Tooltip>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <span ref={nameRef} className=\"min-w-0 truncate\">\n <Text as=\"span\" size=\"sm\" bold>\n {connection.displayName}\n </Text>\n </span>\n </TooltipTrigger>\n {isTruncated ? <TooltipContent>{connection.displayName}</TooltipContent> : null}\n </Tooltip>\n </span>\n );\n}\n\nfunction integrationName(provider: string): string {\n switch (provider) {\n case 'github':\n return 'GitHub';\n case 'gitea':\n return 'Gitea';\n case 'sentry':\n return 'Sentry';\n case 'webhook':\n return 'Webhook';\n case 'debug':\n return 'Debug';\n default:\n return provider;\n }\n}\n"],"names":["IntegrationIcon","useIsTextTruncated","Label","RadioGroup","RadioGroupItem","Tooltip","TooltipContent","TooltipTrigger","Text","useId","ConnectionPicker","connections","selectedConnectionId","onSelect","labelId","div","className","id","aria-labelledby","value","onValueChange","map","connection","ConnectionOption","ref","nameRef","isTruncated","displayName","span","asChild","source","provider","aria-hidden","integrationName","as","size","bold"],"mappings":";AAAA,SAAQA,eAAe,QAAO,6BAA6B;AAC3D,SAAQC,kBAAkB,QAAO,0BAA0B;AAC3D,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,UAAU,EAAEC,cAAc,QAAO,gCAAgC;AACzE,SAAQC,OAAO,EAAEC,cAAc,EAAEC,cAAc,QAAO,4BAA4B;AAClF,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,KAAK,QAAO,QAAQ;AAG5B,OAAO,SAASC,iBAAiB,EAC/BC,WAAW,EACXC,oBAAoB,EACpBC,QAAQ,EAKT;IACC,MAAMC,UAAUL;IAEhB,qBACE,MAACM;QAAIC,WAAU;;0BACb,KAACd;gBAAMe,IAAIH;gBAASE,WAAU;0BAAU;;0BAGxC,KAACb;gBACCe,mBAAiBJ;gBACjBK,OAAOP,wBAAwB;gBAC/BQ,eAAeP;gBACfG,WAAU;0BAETL,YAAYU,GAAG,CAAC,CAACC,2BAChB,KAAClB;wBAAmCe,OAAOG,WAAWL,EAAE;kCACtD,cAAA,KAACM;4BAAiBD,YAAYA;;uBADXA,WAAWL,EAAE;;;;AAO5C;AAEA,SAASM,iBAAiB,EAACD,UAAU,EAAsC;IACzE,MAAM,EAACE,KAAKC,OAAO,EAAEC,WAAW,EAAC,GAAGzB,mBAAoCqB,WAAWK,WAAW;IAE9F,qBACE,MAACC;QAAKZ,WAAU;;0BACd,MAACX;;kCACC,KAACE;wBAAesB,OAAO;kCACrB,cAAA,KAACD;4BAAKZ,WAAU;sCACd,cAAA,KAAChB;gCACC8B,QAAQR,WAAWS,QAAQ;gCAC3BC,aAAW;gCACXhB,WAAU;;;;kCAIhB,KAACV;kCAAgB2B,gBAAgBX,WAAWS,QAAQ;;;;0BAGtD,MAAC1B;;kCACC,KAACE;wBAAesB,OAAO;kCACrB,cAAA,KAACD;4BAAKJ,KAAKC;4BAAST,WAAU;sCAC5B,cAAA,KAACR;gCAAK0B,IAAG;gCAAOC,MAAK;gCAAKC,IAAI;0CAC3Bd,WAAWK,WAAW;;;;oBAI5BD,4BAAc,KAACpB;kCAAgBgB,WAAWK,WAAW;yBAAqB;;;;;AAInF;AAEA,SAASM,gBAAgBF,QAAgB;IACvC,OAAQA;QACN,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT;YACE,OAAOA;IACX;AACF"}
1
+ {"version":3,"sources":["../../src/components/connection-picker.tsx"],"sourcesContent":["import {IntegrationIcon} from '@shipfox/integration-icons';\nimport {useIsTextTruncated} from '@shipfox/react-ui/hooks';\nimport {Label} from '@shipfox/react-ui/label';\nimport {RadioGroup, RadioGroupItem} from '@shipfox/react-ui/radio-group';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useId} from 'react';\nimport type {IntegrationConnection} from '#core/models.js';\n\nexport function ConnectionPicker({\n connections,\n selectedConnectionId,\n onSelect,\n}: {\n connections: IntegrationConnection[];\n selectedConnectionId: string | undefined;\n onSelect: (connectionId: string) => void;\n}) {\n const labelId = useId();\n\n return (\n <div className=\"flex min-w-0 flex-col\">\n <Label id={labelId} className=\"sr-only\">\n Source integration\n </Label>\n <RadioGroup\n variant=\"cell\"\n aria-labelledby={labelId}\n value={selectedConnectionId ?? ''}\n onValueChange={onSelect}\n >\n {connections.map((connection) => (\n <RadioGroupItem key={connection.id} value={connection.id}>\n <ConnectionOption connection={connection} />\n </RadioGroupItem>\n ))}\n </RadioGroup>\n </div>\n );\n}\n\nfunction ConnectionOption({connection}: {connection: IntegrationConnection}) {\n const {ref: nameRef, isTruncated} = useIsTextTruncated<HTMLSpanElement>(connection.displayName);\n\n return (\n <span className=\"flex min-w-0 items-center gap-inline\">\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"shrink-0\">\n <IntegrationIcon\n source={connection.provider}\n aria-hidden\n className=\"size-20 text-foreground-neutral-base\"\n />\n </span>\n </TooltipTrigger>\n <TooltipContent>{integrationName(connection.provider)}</TooltipContent>\n </Tooltip>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <span ref={nameRef} className=\"min-w-0 truncate\">\n <Text as=\"span\" size=\"sm\" bold>\n {connection.displayName}\n </Text>\n </span>\n </TooltipTrigger>\n {isTruncated ? <TooltipContent>{connection.displayName}</TooltipContent> : null}\n </Tooltip>\n </span>\n );\n}\n\nfunction integrationName(provider: string): string {\n switch (provider) {\n case 'github':\n return 'GitHub';\n case 'gitea':\n return 'Gitea';\n case 'sentry':\n return 'Sentry';\n case 'webhook':\n return 'Webhook';\n case 'debug':\n return 'Debug';\n default:\n return provider;\n }\n}\n"],"names":["IntegrationIcon","useIsTextTruncated","Label","RadioGroup","RadioGroupItem","Tooltip","TooltipContent","TooltipTrigger","Text","useId","ConnectionPicker","connections","selectedConnectionId","onSelect","labelId","div","className","id","variant","aria-labelledby","value","onValueChange","map","connection","ConnectionOption","ref","nameRef","isTruncated","displayName","span","asChild","source","provider","aria-hidden","integrationName","as","size","bold"],"mappings":";AAAA,SAAQA,eAAe,QAAO,6BAA6B;AAC3D,SAAQC,kBAAkB,QAAO,0BAA0B;AAC3D,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,UAAU,EAAEC,cAAc,QAAO,gCAAgC;AACzE,SAAQC,OAAO,EAAEC,cAAc,EAAEC,cAAc,QAAO,4BAA4B;AAClF,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,KAAK,QAAO,QAAQ;AAG5B,OAAO,SAASC,iBAAiB,EAC/BC,WAAW,EACXC,oBAAoB,EACpBC,QAAQ,EAKT;IACC,MAAMC,UAAUL;IAEhB,qBACE,MAACM;QAAIC,WAAU;;0BACb,KAACd;gBAAMe,IAAIH;gBAASE,WAAU;0BAAU;;0BAGxC,KAACb;gBACCe,SAAQ;gBACRC,mBAAiBL;gBACjBM,OAAOR,wBAAwB;gBAC/BS,eAAeR;0BAEdF,YAAYW,GAAG,CAAC,CAACC,2BAChB,KAACnB;wBAAmCgB,OAAOG,WAAWN,EAAE;kCACtD,cAAA,KAACO;4BAAiBD,YAAYA;;uBADXA,WAAWN,EAAE;;;;AAO5C;AAEA,SAASO,iBAAiB,EAACD,UAAU,EAAsC;IACzE,MAAM,EAACE,KAAKC,OAAO,EAAEC,WAAW,EAAC,GAAG1B,mBAAoCsB,WAAWK,WAAW;IAE9F,qBACE,MAACC;QAAKb,WAAU;;0BACd,MAACX;;kCACC,KAACE;wBAAeuB,OAAO;kCACrB,cAAA,KAACD;4BAAKb,WAAU;sCACd,cAAA,KAAChB;gCACC+B,QAAQR,WAAWS,QAAQ;gCAC3BC,aAAW;gCACXjB,WAAU;;;;kCAIhB,KAACV;kCAAgB4B,gBAAgBX,WAAWS,QAAQ;;;;0BAGtD,MAAC3B;;kCACC,KAACE;wBAAeuB,OAAO;kCACrB,cAAA,KAACD;4BAAKJ,KAAKC;4BAASV,WAAU;sCAC5B,cAAA,KAACR;gCAAK2B,IAAG;gCAAOC,MAAK;gCAAKC,IAAI;0CAC3Bd,WAAWK,WAAW;;;;oBAI5BD,4BAAc,KAACrB;kCAAgBiB,WAAWK,WAAW;yBAAqB;;;;;AAInF;AAEA,SAASM,gBAAgBF,QAAgB;IACvC,OAAQA;QACN,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT;YACE,OAAOA;IACX;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"installed-integrations-section.d.ts","sourceRoot":"","sources":["../../src/components/installed-integrations-section.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAC,KAAK,qBAAqB,EAAqB,MAAM,iBAAiB,CAAC;AAG/E,UAAU,iCAAiC;IACzC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,EAAE,qBAAqB,EAAE,CAAC;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC;IACjC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,WAAW,EAAE,CAAC,UAAU,EAAE,qBAAqB,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1E,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,mBAAmB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CAC/D;AAKD,wBAAgB,4BAA4B,CAAC,EAC3C,aAAa,EACb,WAAW,EACX,SAAS,EACT,UAAU,EACV,KAAK,EACL,OAAO,EACP,UAAU,EACV,KAAK,EACL,WAAW,EACX,QAAQ,EACR,mBAAmB,GACpB,EAAE,iCAAiC,+BAiDnC"}
1
+ {"version":3,"file":"installed-integrations-section.d.ts","sourceRoot":"","sources":["../../src/components/installed-integrations-section.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAAC,KAAK,qBAAqB,EAAqB,MAAM,iBAAiB,CAAC;AAG/E,UAAU,iCAAiC;IACzC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,EAAE,qBAAqB,EAAE,CAAC;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC;IACjC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,WAAW,EAAE,CAAC,UAAU,EAAE,qBAAqB,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1E,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,mBAAmB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CAC/D;AAED,wBAAgB,4BAA4B,CAAC,EAC3C,aAAa,EACb,WAAW,EACX,SAAS,EACT,UAAU,EACV,KAAK,EACL,OAAO,EACP,UAAU,EACV,KAAK,EACL,WAAW,EACX,QAAQ,EACR,mBAAmB,GACpB,EAAE,iCAAiC,+BAuDnC"}
@@ -4,6 +4,7 @@ import { IntegrationIcon } from '@shipfox/integration-icons';
4
4
  import { IconButton } from '@shipfox/react-ui/button';
5
5
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@shipfox/react-ui/dropdown-menu';
6
6
  import { EmptyState } from '@shipfox/react-ui/empty-state';
7
+ import { Panel, PanelBody, PanelRow } from '@shipfox/react-ui/panel';
7
8
  import { Skeleton } from '@shipfox/react-ui/skeleton';
8
9
  import { Header, Text } from '@shipfox/react-ui/typography';
9
10
  import { cn, formatDate } from '@shipfox/react-ui/utils';
@@ -11,7 +12,6 @@ import { Link } from '@tanstack/react-router';
11
12
  import { ConnectionStatusBadge } from '#connection-status-badge.js';
12
13
  import { isUsableConnection } from '#core/models.js';
13
14
  import { usageEventsForConnection } from './integration-usage-events.js';
14
- const INSTALLED_SURFACE_CLASS = 'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';
15
15
  export function InstalledIntegrationsSection({ workspaceSlug, connections, isPending, isFetching, error, onRetry, isMutating, onUse, onSetActive, onDelete, providerDisplayName }) {
16
16
  return /*#__PURE__*/ _jsxs("section", {
17
17
  className: "flex flex-col gap-group",
@@ -34,8 +34,7 @@ export function InstalledIntegrationsSection({ workspaceSlug, connections, isPen
34
34
  isPending ? /*#__PURE__*/ _jsx(InstalledSkeleton, {
35
35
  label: "Loading integrations"
36
36
  }) : null,
37
- error ? /*#__PURE__*/ _jsx("div", {
38
- className: cn(INSTALLED_SURFACE_CLASS, 'px-row'),
37
+ error ? /*#__PURE__*/ _jsx(Panel, {
39
38
  children: /*#__PURE__*/ _jsx(QueryLoadError, {
40
39
  query: {
41
40
  isError: true,
@@ -44,28 +43,33 @@ export function InstalledIntegrationsSection({ workspaceSlug, connections, isPen
44
43
  error,
45
44
  refetch: onRetry
46
45
  },
47
- subject: "integrations"
46
+ subject: "integrations",
47
+ variant: "panel"
48
48
  })
49
49
  }) : null,
50
- !isPending && !error && connections.length === 0 ? /*#__PURE__*/ _jsx("div", {
51
- className: cn(INSTALLED_SURFACE_CLASS, 'px-row'),
50
+ !isPending && !error && connections.length === 0 ? /*#__PURE__*/ _jsx(Panel, {
52
51
  children: /*#__PURE__*/ _jsx(EmptyState, {
53
52
  icon: "componentLine",
54
53
  title: "No integrations installed yet",
55
- description: "Install a provider below to get started."
54
+ description: "Install a provider below to get started.",
55
+ variant: "panel"
56
56
  })
57
57
  }) : null,
58
- connections.length > 0 ? /*#__PURE__*/ _jsx("ul", {
59
- className: cn('divide-y divide-border-neutral-base', INSTALLED_SURFACE_CLASS),
60
- children: connections.map((connection)=>/*#__PURE__*/ _jsx(InstalledRow, {
61
- connection: connection,
62
- workspaceSlug: workspaceSlug,
63
- isMutating: isMutating,
64
- onUse: onUse,
65
- onSetActive: (nextActive)=>onSetActive(connection, nextActive),
66
- onDelete: onDelete,
67
- providerDisplayName: providerDisplayName
68
- }, connection.id))
58
+ connections.length > 0 ? /*#__PURE__*/ _jsx(Panel, {
59
+ children: /*#__PURE__*/ _jsx(PanelBody, {
60
+ asChild: true,
61
+ children: /*#__PURE__*/ _jsx("ul", {
62
+ children: connections.map((connection)=>/*#__PURE__*/ _jsx(InstalledRow, {
63
+ connection: connection,
64
+ workspaceSlug: workspaceSlug,
65
+ isMutating: isMutating,
66
+ onUse: onUse,
67
+ onSetActive: (nextActive)=>onSetActive(connection, nextActive),
68
+ onDelete: onDelete,
69
+ providerDisplayName: providerDisplayName
70
+ }, connection.id))
71
+ })
72
+ })
69
73
  }) : null
70
74
  ]
71
75
  });
@@ -75,136 +79,146 @@ function InstalledRow({ connection, workspaceSlug, isMutating, onUse, onSetActiv
75
79
  const active = isUsableConnection(connection);
76
80
  const recentEventsEvent = usageEventsForConnection(connection)[0]?.value ?? 'received';
77
81
  const providerName = providerDisplayName(connection.provider);
78
- return /*#__PURE__*/ _jsxs("li", {
79
- className: "flex items-center gap-cluster px-row py-row transition-colors hover:bg-background-components-hover",
80
- children: [
81
- /*#__PURE__*/ _jsx(IntegrationIcon, {
82
- source: connection.provider,
83
- "aria-hidden": true,
84
- className: cn('size-24 shrink-0', muted ? 'text-foreground-neutral-disabled' : 'text-foreground-neutral-base')
85
- }),
86
- /*#__PURE__*/ _jsxs("div", {
87
- className: "flex min-w-0 flex-1 flex-col gap-tight",
88
- children: [
89
- /*#__PURE__*/ _jsxs("div", {
90
- className: "flex min-w-0 items-center gap-inline",
91
- children: [
92
- /*#__PURE__*/ _jsx(Text, {
93
- size: "md",
94
- bold: true,
95
- className: cn('truncate', muted ? 'text-foreground-neutral-disabled' : undefined),
96
- children: connection.displayName
97
- }),
98
- /*#__PURE__*/ _jsx(ConnectionStatusBadge, {
99
- status: connection.lifecycleStatus,
100
- className: "shrink-0"
101
- })
102
- ]
103
- }),
104
- /*#__PURE__*/ _jsxs(Text, {
105
- size: "sm",
106
- className: "truncate text-foreground-neutral-muted",
107
- children: [
108
- "Added ",
109
- formatDate(connection.createdAt)
110
- ]
111
- })
112
- ]
113
- }),
114
- /*#__PURE__*/ _jsxs(DropdownMenu, {
115
- children: [
116
- /*#__PURE__*/ _jsx(DropdownMenuTrigger, {
117
- asChild: true,
118
- children: /*#__PURE__*/ _jsx(IconButton, {
82
+ return /*#__PURE__*/ _jsx(PanelRow, {
83
+ asChild: true,
84
+ className: "justify-start gap-cluster",
85
+ children: /*#__PURE__*/ _jsxs("li", {
86
+ children: [
87
+ /*#__PURE__*/ _jsx(IntegrationIcon, {
88
+ source: connection.provider,
89
+ "aria-hidden": true,
90
+ className: cn('size-24 shrink-0', muted ? 'text-foreground-neutral-disabled' : 'text-foreground-neutral-base')
91
+ }),
92
+ /*#__PURE__*/ _jsxs("div", {
93
+ className: "flex min-w-0 flex-1 flex-col gap-tight",
94
+ children: [
95
+ /*#__PURE__*/ _jsxs("div", {
96
+ className: "flex min-w-0 items-center gap-inline",
97
+ children: [
98
+ /*#__PURE__*/ _jsx(Text, {
99
+ size: "md",
100
+ bold: true,
101
+ className: cn('truncate', muted ? 'text-foreground-neutral-disabled' : undefined),
102
+ children: connection.displayName
103
+ }),
104
+ /*#__PURE__*/ _jsx(ConnectionStatusBadge, {
105
+ status: connection.lifecycleStatus,
106
+ className: "shrink-0"
107
+ })
108
+ ]
109
+ }),
110
+ /*#__PURE__*/ _jsxs(Text, {
119
111
  size: "sm",
120
- variant: "transparent",
121
- icon: "more2Line",
122
- "aria-label": `Open ${connection.displayName} integration actions`
112
+ className: "truncate text-foreground-neutral-muted",
113
+ children: [
114
+ "Added ",
115
+ formatDate(connection.createdAt)
116
+ ]
123
117
  })
124
- }),
125
- /*#__PURE__*/ _jsxs(DropdownMenuContent, {
126
- align: "end",
127
- children: [
128
- connection.externalUrl ? /*#__PURE__*/ _jsx(DropdownMenuItem, {
129
- asChild: true,
130
- children: /*#__PURE__*/ _jsx("a", {
131
- href: connection.externalUrl,
132
- target: "_blank",
133
- rel: "noreferrer noopener",
134
- children: providerName ? `Open in ${providerName}` : 'Open provider settings'
135
- })
136
- }) : null,
137
- /*#__PURE__*/ _jsx(DropdownMenuItem, {
138
- onSelect: ()=>onUse(connection.id),
139
- children: "Use this integration"
140
- }),
141
- workspaceSlug ? /*#__PURE__*/ _jsx(DropdownMenuItem, {
142
- asChild: true,
143
- children: /*#__PURE__*/ _jsx(Link, {
144
- to: "/w/$workspaceSlug/settings/events",
145
- params: {
146
- workspaceSlug
147
- },
148
- search: {
149
- source: [
150
- connection.slug
151
- ],
152
- event: [
153
- recentEventsEvent
154
- ]
155
- },
156
- children: "View recent events"
157
- })
158
- }) : null,
159
- /*#__PURE__*/ _jsx(DropdownMenuSeparator, {}),
160
- /*#__PURE__*/ _jsx(DropdownMenuItem, {
161
- disabled: isMutating,
162
- onSelect: ()=>onSetActive(!active),
163
- children: active ? 'Disable integration' : 'Enable integration'
164
- }),
165
- /*#__PURE__*/ _jsx(DropdownMenuItem, {
166
- disabled: isMutating,
167
- onSelect: ()=>onDelete(connection.id),
168
- children: "Delete integration"
118
+ ]
119
+ }),
120
+ /*#__PURE__*/ _jsxs(DropdownMenu, {
121
+ children: [
122
+ /*#__PURE__*/ _jsx(DropdownMenuTrigger, {
123
+ asChild: true,
124
+ children: /*#__PURE__*/ _jsx(IconButton, {
125
+ size: "sm",
126
+ variant: "transparent",
127
+ icon: "more2Line",
128
+ "aria-label": `Open ${connection.displayName} integration actions`
169
129
  })
170
- ]
171
- })
172
- ]
173
- })
174
- ]
130
+ }),
131
+ /*#__PURE__*/ _jsxs(DropdownMenuContent, {
132
+ align: "end",
133
+ children: [
134
+ connection.externalUrl ? /*#__PURE__*/ _jsx(DropdownMenuItem, {
135
+ asChild: true,
136
+ children: /*#__PURE__*/ _jsx("a", {
137
+ href: connection.externalUrl,
138
+ target: "_blank",
139
+ rel: "noreferrer noopener",
140
+ children: providerName ? `Open in ${providerName}` : 'Open provider settings'
141
+ })
142
+ }) : null,
143
+ /*#__PURE__*/ _jsx(DropdownMenuItem, {
144
+ onSelect: ()=>onUse(connection.id),
145
+ children: "Use this integration"
146
+ }),
147
+ workspaceSlug ? /*#__PURE__*/ _jsx(DropdownMenuItem, {
148
+ asChild: true,
149
+ children: /*#__PURE__*/ _jsx(Link, {
150
+ to: "/w/$workspaceSlug/settings/events",
151
+ params: {
152
+ workspaceSlug
153
+ },
154
+ search: {
155
+ source: [
156
+ connection.slug
157
+ ],
158
+ event: [
159
+ recentEventsEvent
160
+ ]
161
+ },
162
+ children: "View recent events"
163
+ })
164
+ }) : null,
165
+ /*#__PURE__*/ _jsx(DropdownMenuSeparator, {}),
166
+ /*#__PURE__*/ _jsx(DropdownMenuItem, {
167
+ disabled: isMutating,
168
+ onSelect: ()=>onSetActive(!active),
169
+ children: active ? 'Disable integration' : 'Enable integration'
170
+ }),
171
+ /*#__PURE__*/ _jsx(DropdownMenuItem, {
172
+ disabled: isMutating,
173
+ onSelect: ()=>onDelete(connection.id),
174
+ children: "Delete integration"
175
+ })
176
+ ]
177
+ })
178
+ ]
179
+ })
180
+ ]
181
+ })
175
182
  });
176
183
  }
177
184
  function InstalledSkeleton({ label }) {
178
- return /*#__PURE__*/ _jsx("ul", {
179
- role: "status",
180
- "aria-label": label,
181
- className: cn('divide-y divide-border-neutral-base', INSTALLED_SURFACE_CLASS),
182
- children: [
183
- 0,
184
- 1,
185
- 2
186
- ].map((row)=>/*#__PURE__*/ _jsxs("li", {
187
- className: "flex items-center gap-cluster px-row py-row",
185
+ return /*#__PURE__*/ _jsx(Panel, {
186
+ children: /*#__PURE__*/ _jsx(PanelBody, {
187
+ asChild: true,
188
+ children: /*#__PURE__*/ _jsx("ul", {
189
+ role: "status",
190
+ "aria-label": label,
188
191
  children: [
189
- /*#__PURE__*/ _jsx(Skeleton, {
190
- className: "size-24 shrink-0"
191
- }),
192
- /*#__PURE__*/ _jsxs("div", {
193
- className: "flex min-w-0 flex-1 flex-col gap-tight",
194
- children: [
195
- /*#__PURE__*/ _jsx(Skeleton, {
196
- className: "h-16 w-120"
197
- }),
198
- /*#__PURE__*/ _jsx(Skeleton, {
199
- className: "h-12 w-80"
200
- })
201
- ]
202
- }),
203
- /*#__PURE__*/ _jsx(Skeleton, {
204
- className: "h-20 w-72 shrink-0"
205
- })
206
- ]
207
- }, row))
192
+ 0,
193
+ 1,
194
+ 2
195
+ ].map((row)=>/*#__PURE__*/ _jsx(PanelRow, {
196
+ asChild: true,
197
+ className: "justify-start gap-cluster hover:bg-background-neutral-base",
198
+ children: /*#__PURE__*/ _jsxs("li", {
199
+ children: [
200
+ /*#__PURE__*/ _jsx(Skeleton, {
201
+ className: "size-24 shrink-0"
202
+ }),
203
+ /*#__PURE__*/ _jsxs("div", {
204
+ className: "flex min-w-0 flex-1 flex-col gap-tight",
205
+ children: [
206
+ /*#__PURE__*/ _jsx(Skeleton, {
207
+ className: "h-16 w-120"
208
+ }),
209
+ /*#__PURE__*/ _jsx(Skeleton, {
210
+ className: "h-12 w-80"
211
+ })
212
+ ]
213
+ }),
214
+ /*#__PURE__*/ _jsx(Skeleton, {
215
+ className: "h-20 w-72 shrink-0"
216
+ })
217
+ ]
218
+ })
219
+ }, row))
220
+ })
221
+ })
208
222
  });
209
223
  }
210
224
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/installed-integrations-section.tsx"],"sourcesContent":["import {QueryLoadError} from '@shipfox/client-ui';\nimport {IntegrationIcon} from '@shipfox/integration-icons';\nimport {IconButton} from '@shipfox/react-ui/button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {cn, formatDate} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport {ConnectionStatusBadge} from '#connection-status-badge.js';\nimport {type IntegrationConnection, isUsableConnection} from '#core/models.js';\nimport {usageEventsForConnection} from './integration-usage-events.js';\n\ninterface InstalledIntegrationsSectionProps {\n workspaceSlug?: string | undefined;\n connections: IntegrationConnection[];\n isPending: boolean;\n isFetching: boolean;\n error?: Error | null | undefined;\n onRetry: () => void;\n isMutating: boolean;\n onUse: (connectionId: string) => void;\n onSetActive: (connection: IntegrationConnection, active: boolean) => void;\n onDelete: (connectionId: string) => void;\n providerDisplayName: (provider: string) => string | undefined;\n}\n\nconst INSTALLED_SURFACE_CLASS =\n 'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';\n\nexport function InstalledIntegrationsSection({\n workspaceSlug,\n connections,\n isPending,\n isFetching,\n error,\n onRetry,\n isMutating,\n onUse,\n onSetActive,\n onDelete,\n providerDisplayName,\n}: InstalledIntegrationsSectionProps) {\n return (\n <section className=\"flex flex-col gap-group\" aria-label=\"Installed integrations\">\n <div className=\"flex flex-col gap-tight\">\n <Header variant=\"h3\">Installed integrations</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Provider accounts installed in this workspace.\n </Text>\n </div>\n\n {isPending ? <InstalledSkeleton label=\"Loading integrations\" /> : null}\n\n {error ? (\n <div className={cn(INSTALLED_SURFACE_CLASS, 'px-row')}>\n <QueryLoadError\n query={{isError: true, isFetching, data: undefined, error, refetch: onRetry}}\n subject=\"integrations\"\n />\n </div>\n ) : null}\n\n {!isPending && !error && connections.length === 0 ? (\n <div className={cn(INSTALLED_SURFACE_CLASS, 'px-row')}>\n <EmptyState\n icon=\"componentLine\"\n title=\"No integrations installed yet\"\n description=\"Install a provider below to get started.\"\n />\n </div>\n ) : null}\n\n {connections.length > 0 ? (\n <ul className={cn('divide-y divide-border-neutral-base', INSTALLED_SURFACE_CLASS)}>\n {connections.map((connection) => (\n <InstalledRow\n key={connection.id}\n connection={connection}\n workspaceSlug={workspaceSlug}\n isMutating={isMutating}\n onUse={onUse}\n onSetActive={(nextActive) => onSetActive(connection, nextActive)}\n onDelete={onDelete}\n providerDisplayName={providerDisplayName}\n />\n ))}\n </ul>\n ) : null}\n </section>\n );\n}\n\nfunction InstalledRow({\n connection,\n workspaceSlug,\n isMutating,\n onUse,\n onSetActive,\n onDelete,\n providerDisplayName,\n}: {\n connection: IntegrationConnection;\n workspaceSlug?: string | undefined;\n isMutating: boolean;\n onUse: (connectionId: string) => void;\n onSetActive: (active: boolean) => void;\n onDelete: (connectionId: string) => void;\n providerDisplayName: (provider: string) => string | undefined;\n}) {\n const muted = connection.lifecycleStatus === 'disabled';\n const active = isUsableConnection(connection);\n const recentEventsEvent = usageEventsForConnection(connection)[0]?.value ?? 'received';\n const providerName = providerDisplayName(connection.provider);\n\n return (\n <li className=\"flex items-center gap-cluster px-row py-row transition-colors hover:bg-background-components-hover\">\n <IntegrationIcon\n source={connection.provider}\n aria-hidden\n className={cn(\n 'size-24 shrink-0',\n muted ? 'text-foreground-neutral-disabled' : 'text-foreground-neutral-base',\n )}\n />\n <div className=\"flex min-w-0 flex-1 flex-col gap-tight\">\n <div className=\"flex min-w-0 items-center gap-inline\">\n <Text\n size=\"md\"\n bold\n className={cn('truncate', muted ? 'text-foreground-neutral-disabled' : undefined)}\n >\n {connection.displayName}\n </Text>\n <ConnectionStatusBadge status={connection.lifecycleStatus} className=\"shrink-0\" />\n </div>\n <Text size=\"sm\" className=\"truncate text-foreground-neutral-muted\">\n Added {formatDate(connection.createdAt)}\n </Text>\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <IconButton\n size=\"sm\"\n variant=\"transparent\"\n icon=\"more2Line\"\n aria-label={`Open ${connection.displayName} integration actions`}\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {connection.externalUrl ? (\n <DropdownMenuItem asChild>\n <a href={connection.externalUrl} target=\"_blank\" rel=\"noreferrer noopener\">\n {providerName ? `Open in ${providerName}` : 'Open provider settings'}\n </a>\n </DropdownMenuItem>\n ) : null}\n <DropdownMenuItem onSelect={() => onUse(connection.id)}>\n Use this integration\n </DropdownMenuItem>\n {workspaceSlug ? (\n <DropdownMenuItem asChild>\n <Link\n to=\"/w/$workspaceSlug/settings/events\"\n params={{workspaceSlug}}\n search={{source: [connection.slug], event: [recentEventsEvent]}}\n >\n View recent events\n </Link>\n </DropdownMenuItem>\n ) : null}\n <DropdownMenuSeparator />\n <DropdownMenuItem disabled={isMutating} onSelect={() => onSetActive(!active)}>\n {active ? 'Disable integration' : 'Enable integration'}\n </DropdownMenuItem>\n <DropdownMenuItem disabled={isMutating} onSelect={() => onDelete(connection.id)}>\n Delete integration\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </li>\n );\n}\n\nfunction InstalledSkeleton({label}: {label: string}) {\n return (\n <ul\n role=\"status\"\n aria-label={label}\n className={cn('divide-y divide-border-neutral-base', INSTALLED_SURFACE_CLASS)}\n >\n {[0, 1, 2].map((row) => (\n <li key={row} className=\"flex items-center gap-cluster px-row py-row\">\n <Skeleton className=\"size-24 shrink-0\" />\n <div className=\"flex min-w-0 flex-1 flex-col gap-tight\">\n <Skeleton className=\"h-16 w-120\" />\n <Skeleton className=\"h-12 w-80\" />\n </div>\n <Skeleton className=\"h-20 w-72 shrink-0\" />\n </li>\n ))}\n </ul>\n );\n}\n"],"names":["QueryLoadError","IntegrationIcon","IconButton","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuSeparator","DropdownMenuTrigger","EmptyState","Skeleton","Header","Text","cn","formatDate","Link","ConnectionStatusBadge","isUsableConnection","usageEventsForConnection","INSTALLED_SURFACE_CLASS","InstalledIntegrationsSection","workspaceSlug","connections","isPending","isFetching","error","onRetry","isMutating","onUse","onSetActive","onDelete","providerDisplayName","section","className","aria-label","div","variant","size","InstalledSkeleton","label","query","isError","data","undefined","refetch","subject","length","icon","title","description","ul","map","connection","InstalledRow","nextActive","id","muted","lifecycleStatus","active","recentEventsEvent","value","providerName","provider","li","source","aria-hidden","bold","displayName","status","createdAt","asChild","align","externalUrl","a","href","target","rel","onSelect","to","params","search","slug","event","disabled","role","row"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/components/installed-integrations-section.tsx"],"sourcesContent":["import {QueryLoadError} from '@shipfox/client-ui';\nimport {IntegrationIcon} from '@shipfox/integration-icons';\nimport {IconButton} from '@shipfox/react-ui/button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Panel, PanelBody, PanelRow} from '@shipfox/react-ui/panel';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {cn, formatDate} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport {ConnectionStatusBadge} from '#connection-status-badge.js';\nimport {type IntegrationConnection, isUsableConnection} from '#core/models.js';\nimport {usageEventsForConnection} from './integration-usage-events.js';\n\ninterface InstalledIntegrationsSectionProps {\n workspaceSlug?: string | undefined;\n connections: IntegrationConnection[];\n isPending: boolean;\n isFetching: boolean;\n error?: Error | null | undefined;\n onRetry: () => void;\n isMutating: boolean;\n onUse: (connectionId: string) => void;\n onSetActive: (connection: IntegrationConnection, active: boolean) => void;\n onDelete: (connectionId: string) => void;\n providerDisplayName: (provider: string) => string | undefined;\n}\n\nexport function InstalledIntegrationsSection({\n workspaceSlug,\n connections,\n isPending,\n isFetching,\n error,\n onRetry,\n isMutating,\n onUse,\n onSetActive,\n onDelete,\n providerDisplayName,\n}: InstalledIntegrationsSectionProps) {\n return (\n <section className=\"flex flex-col gap-group\" aria-label=\"Installed integrations\">\n <div className=\"flex flex-col gap-tight\">\n <Header variant=\"h3\">Installed integrations</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Provider accounts installed in this workspace.\n </Text>\n </div>\n\n {isPending ? <InstalledSkeleton label=\"Loading integrations\" /> : null}\n\n {error ? (\n <Panel>\n <QueryLoadError\n query={{isError: true, isFetching, data: undefined, error, refetch: onRetry}}\n subject=\"integrations\"\n variant=\"panel\"\n />\n </Panel>\n ) : null}\n\n {!isPending && !error && connections.length === 0 ? (\n <Panel>\n <EmptyState\n icon=\"componentLine\"\n title=\"No integrations installed yet\"\n description=\"Install a provider below to get started.\"\n variant=\"panel\"\n />\n </Panel>\n ) : null}\n\n {connections.length > 0 ? (\n <Panel>\n <PanelBody asChild>\n <ul>\n {connections.map((connection) => (\n <InstalledRow\n key={connection.id}\n connection={connection}\n workspaceSlug={workspaceSlug}\n isMutating={isMutating}\n onUse={onUse}\n onSetActive={(nextActive) => onSetActive(connection, nextActive)}\n onDelete={onDelete}\n providerDisplayName={providerDisplayName}\n />\n ))}\n </ul>\n </PanelBody>\n </Panel>\n ) : null}\n </section>\n );\n}\n\nfunction InstalledRow({\n connection,\n workspaceSlug,\n isMutating,\n onUse,\n onSetActive,\n onDelete,\n providerDisplayName,\n}: {\n connection: IntegrationConnection;\n workspaceSlug?: string | undefined;\n isMutating: boolean;\n onUse: (connectionId: string) => void;\n onSetActive: (active: boolean) => void;\n onDelete: (connectionId: string) => void;\n providerDisplayName: (provider: string) => string | undefined;\n}) {\n const muted = connection.lifecycleStatus === 'disabled';\n const active = isUsableConnection(connection);\n const recentEventsEvent = usageEventsForConnection(connection)[0]?.value ?? 'received';\n const providerName = providerDisplayName(connection.provider);\n\n return (\n <PanelRow asChild className=\"justify-start gap-cluster\">\n <li>\n <IntegrationIcon\n source={connection.provider}\n aria-hidden\n className={cn(\n 'size-24 shrink-0',\n muted ? 'text-foreground-neutral-disabled' : 'text-foreground-neutral-base',\n )}\n />\n <div className=\"flex min-w-0 flex-1 flex-col gap-tight\">\n <div className=\"flex min-w-0 items-center gap-inline\">\n <Text\n size=\"md\"\n bold\n className={cn('truncate', muted ? 'text-foreground-neutral-disabled' : undefined)}\n >\n {connection.displayName}\n </Text>\n <ConnectionStatusBadge status={connection.lifecycleStatus} className=\"shrink-0\" />\n </div>\n <Text size=\"sm\" className=\"truncate text-foreground-neutral-muted\">\n Added {formatDate(connection.createdAt)}\n </Text>\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <IconButton\n size=\"sm\"\n variant=\"transparent\"\n icon=\"more2Line\"\n aria-label={`Open ${connection.displayName} integration actions`}\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {connection.externalUrl ? (\n <DropdownMenuItem asChild>\n <a href={connection.externalUrl} target=\"_blank\" rel=\"noreferrer noopener\">\n {providerName ? `Open in ${providerName}` : 'Open provider settings'}\n </a>\n </DropdownMenuItem>\n ) : null}\n <DropdownMenuItem onSelect={() => onUse(connection.id)}>\n Use this integration\n </DropdownMenuItem>\n {workspaceSlug ? (\n <DropdownMenuItem asChild>\n <Link\n to=\"/w/$workspaceSlug/settings/events\"\n params={{workspaceSlug}}\n search={{source: [connection.slug], event: [recentEventsEvent]}}\n >\n View recent events\n </Link>\n </DropdownMenuItem>\n ) : null}\n <DropdownMenuSeparator />\n <DropdownMenuItem disabled={isMutating} onSelect={() => onSetActive(!active)}>\n {active ? 'Disable integration' : 'Enable integration'}\n </DropdownMenuItem>\n <DropdownMenuItem disabled={isMutating} onSelect={() => onDelete(connection.id)}>\n Delete integration\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </li>\n </PanelRow>\n );\n}\n\nfunction InstalledSkeleton({label}: {label: string}) {\n return (\n <Panel>\n <PanelBody asChild>\n <ul role=\"status\" aria-label={label}>\n {[0, 1, 2].map((row) => (\n <PanelRow\n asChild\n className=\"justify-start gap-cluster hover:bg-background-neutral-base\"\n key={row}\n >\n <li>\n <Skeleton className=\"size-24 shrink-0\" />\n <div className=\"flex min-w-0 flex-1 flex-col gap-tight\">\n <Skeleton className=\"h-16 w-120\" />\n <Skeleton className=\"h-12 w-80\" />\n </div>\n <Skeleton className=\"h-20 w-72 shrink-0\" />\n </li>\n </PanelRow>\n ))}\n </ul>\n </PanelBody>\n </Panel>\n );\n}\n"],"names":["QueryLoadError","IntegrationIcon","IconButton","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuSeparator","DropdownMenuTrigger","EmptyState","Panel","PanelBody","PanelRow","Skeleton","Header","Text","cn","formatDate","Link","ConnectionStatusBadge","isUsableConnection","usageEventsForConnection","InstalledIntegrationsSection","workspaceSlug","connections","isPending","isFetching","error","onRetry","isMutating","onUse","onSetActive","onDelete","providerDisplayName","section","className","aria-label","div","variant","size","InstalledSkeleton","label","query","isError","data","undefined","refetch","subject","length","icon","title","description","asChild","ul","map","connection","InstalledRow","nextActive","id","muted","lifecycleStatus","active","recentEventsEvent","value","providerName","provider","li","source","aria-hidden","bold","displayName","status","createdAt","align","externalUrl","a","href","target","rel","onSelect","to","params","search","slug","event","disabled","role","row"],"mappings":"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"}
@@ -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,KAiBhD,CAAC"}
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,KAsBhD,CAAC"}
@@ -6,7 +6,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
6
6
  import { createMemoryHistory, createRootRoute, createRoute, createRouter, Outlet, RouterProvider } from '@tanstack/react-router';
7
7
  import { createStore, Provider as JotaiProvider } from 'jotai';
8
8
  import { useMemo } from 'react';
9
- import { expect, screen, userEvent, within } from 'storybook/test';
9
+ import { expect, screen, userEvent, waitFor, within } from 'storybook/test';
10
10
  import { IntegrationGallery } from './integration-gallery.js';
11
11
  const WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';
12
12
  const WORKSPACE_PATH = '/w/acme/settings/integrations';
@@ -198,7 +198,12 @@ export const UsageModalCloseRestoresInteraction = {
198
198
  expect(screen.queryByRole('dialog', {
199
199
  name: 'Use acme-corp'
200
200
  })).not.toBeInTheDocument();
201
- expect(document.body.style.pointerEvents).not.toBe('none');
201
+ // The dialog unmounts before its dismissable layer releases the body, so
202
+ // wait for the release rather than asserting it on the same tick: clicking
203
+ // while `pointer-events: none` is still set is swallowed silently.
204
+ await waitFor(()=>{
205
+ expect(document.body.style.pointerEvents).not.toBe('none');
206
+ });
202
207
  await user.click(actionsButton);
203
208
  await screen.findByRole('menuitem', {
204
209
  name: 'Use this integration'