@camunda/camunda-composite-components 0.24.0 → 0.25.1

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/camunda-composite-components",
3
- "version": "0.24.0",
3
+ "version": "0.25.1",
4
4
  "description": "Camunda Composite Components",
5
5
  "bugs": {
6
6
  "url": "https://github.com/camunda/camunda-cloud-management-apps/issues"
@@ -50,42 +50,42 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "jwt-decode": "4.0.0",
53
- "react-error-boundary": "6.1.1",
53
+ "react-error-boundary": "6.1.2",
54
54
  "react-markdown": "10.1.0",
55
- "semver": "7.8.0"
55
+ "semver": "7.8.4"
56
56
  },
57
57
  "devDependencies": {
58
- "@auth0/auth0-spa-js": "2.19.3",
58
+ "@auth0/auth0-spa-js": "2.21.1",
59
59
  "@camunda/ccma-shared-types": "workspace:*",
60
- "@carbon/react": "1.107.1",
60
+ "@carbon/react": "1.109.0",
61
61
  "@chromatic-com/storybook": "5.2.1",
62
62
  "@mdx-js/react": "3.1.1",
63
- "@playwright/test": "1.60.0",
64
- "@storybook/addon-a11y": "10.4.0",
65
- "@storybook/addon-docs": "10.4.0",
66
- "@storybook/addon-links": "10.4.0",
67
- "@storybook/addon-vitest": "10.4.0",
68
- "@storybook/react": "10.4.0",
69
- "@storybook/react-vite": "10.4.0",
70
- "@vitest/browser": "4.1.7",
71
- "@vitest/browser-playwright": "4.1.7",
72
- "vitest": "4.1.7",
63
+ "@playwright/test": "1.61.0",
64
+ "@storybook/addon-a11y": "10.4.6",
65
+ "@storybook/addon-docs": "10.4.6",
66
+ "@storybook/addon-links": "10.4.6",
67
+ "@storybook/addon-vitest": "10.4.6",
68
+ "@storybook/react": "10.4.6",
69
+ "@storybook/react-vite": "10.4.6",
70
+ "@vitest/browser": "4.1.9",
71
+ "@vitest/browser-playwright": "4.1.9",
72
+ "vitest": "4.1.9",
73
73
  "conventional-changelog-conventionalcommits": "9.3.1",
74
- "eslint-import-resolver-typescript": "4.4.4",
74
+ "eslint-import-resolver-typescript": "4.4.5",
75
75
  "eslint-plugin-react": "7.37.5",
76
76
  "eslint-plugin-react-hooks": "7.1.1",
77
- "eslint-plugin-storybook": "10.4.0",
77
+ "eslint-plugin-storybook": "10.4.6",
78
78
  "event-source-polyfill": "1.0.31",
79
- "mixpanel-browser": "2.79.0",
80
- "playwright": "1.60.0",
81
- "react": "19.2.6",
82
- "react-dom": "19.2.6",
83
- "react-is": "19.2.6",
79
+ "mixpanel-browser": "2.80.0",
80
+ "playwright": "1.61.0",
81
+ "react": "19.2.7",
82
+ "react-dom": "19.2.7",
83
+ "react-is": "19.2.7",
84
84
  "rimraf": "6.1.3",
85
85
  "serve": "14.2.6",
86
- "storybook": "10.4.0",
86
+ "storybook": "10.4.6",
87
87
  "styled-components": "6.4.2",
88
- "typescript-eslint": "8.59.4",
88
+ "typescript-eslint": "8.61.1",
89
89
  "wait-on": "9.0.10"
90
90
  },
91
91
  "peerDependencies": {
@@ -1,5 +1,5 @@
1
1
  import type { C3IconProps } from './c3-icons.types';
2
- export declare const C3AppMenuIcon: ({ size }: C3IconProps) => import("react/jsx-runtime").JSX.Element;
3
- export declare const C3NotificationsUnreadIcon: ({ size }: C3IconProps) => import("react/jsx-runtime").JSX.Element;
4
- export declare const C3BellIcon: ({ size }: C3IconProps) => import("react/jsx-runtime").JSX.Element;
5
- export declare const CamundaLogo: () => import("react/jsx-runtime").JSX.Element;
2
+ export declare const C3AppMenuIcon: ({ size }: C3IconProps) => import("react").JSX.Element;
3
+ export declare const C3NotificationsUnreadIcon: ({ size }: C3IconProps) => import("react").JSX.Element;
4
+ export declare const C3BellIcon: ({ size }: C3IconProps) => import("react").JSX.Element;
5
+ export declare const CamundaLogo: () => import("react").JSX.Element;
@@ -13,5 +13,5 @@ type AppTeaserCardsProps = {
13
13
  };
14
14
  subtext: string;
15
15
  };
16
- export declare const AppTeaserCards: ({ title, subtitle, cards, cta, subtext, }: AppTeaserCardsProps) => import("react/jsx-runtime").JSX.Element;
16
+ export declare const AppTeaserCards: ({ title, subtitle, cards, cta, subtext, }: AppTeaserCardsProps) => JSX.Element;
17
17
  export {};
@@ -1,4 +1,4 @@
1
1
  import type { JSX } from 'react';
2
2
  export declare const HelpCenterHint: ({ children }: {
3
3
  children: JSX.Element;
4
- }) => import("react/jsx-runtime").JSX.Element;
4
+ }) => JSX.Element;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /*
3
3
  * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
4
4
  * under one or more contributor license agreements. Licensed under a commercial license.
@@ -82,5 +82,9 @@ export const C3LicenseTag = (props) => {
82
82
  const variants = getTagVariants(props);
83
83
  if (variants.length === 0)
84
84
  return null;
85
- return _jsx(_Fragment, { children: variants.map(renderVariant) });
85
+ return (_jsx("span", { style: {
86
+ display: 'inline-flex',
87
+ alignItems: 'center',
88
+ gap: 'var(--cds-spacing-02)',
89
+ }, children: variants.map(renderVariant) }));
86
90
  };
@@ -173,7 +173,7 @@ const Separator = styled.span `
173
173
  const DropdownEntry = ({ item, onSelect, linkComponent, }) => {
174
174
  const Icon = item.icon;
175
175
  return (_jsxs(DropdownItem, { as: item.linkProps?.href !== undefined
176
- ? (linkComponent ?? 'a')
176
+ ? 'a'
177
177
  : item.linkProps
178
178
  ? linkComponent
179
179
  : undefined, ...(item.linkProps ?? {}), "$isSelected": !!item.isSelected, onClick: () => onSelect(item), role: 'option', tabIndex: -1, "aria-selected": !!item.isSelected, children: [Icon && (_jsx(Icon, { size: 16, style: { color: 'var(--cds-icon-secondary)', flexShrink: 0 } })), _jsx(DropdownItemLabel, { children: item.label }), item.trailingElement, item.isSelected && (_jsx(Checkmark, { size: 16, style: { color: 'var(--cds-icon-primary)', flexShrink: 0 } }))] }));
@@ -345,7 +345,7 @@ const BreadcrumbSegmentComponent = ({ segment, isLast, linkComponent, }) => {
345
345
  }, [close]);
346
346
  const handleDropdownKeyDown = usePanelKeyboard(dropdownPanelRef, close, chevronRef, isOpen, '[role="option"]');
347
347
  return (_jsxs(_Fragment, { children: [_jsxs(SegmentWrapper, { ref: wrapperRef, children: [_jsxs(SegmentButton, { ref: segmentButtonRef, as: segment.linkProps?.href !== undefined
348
- ? (linkComponent ?? 'a')
348
+ ? 'a'
349
349
  : segment.linkProps
350
350
  ? linkComponent
351
351
  : undefined, ...(segment.linkProps ?? {}), "$isInteractive": !!(segment.onClick || segment.linkProps), "$isLast": isLast, onClick: segment.onClick, "aria-label": segment.label, title: segment.label, children: [Icon && _jsx(Icon, { size: 16, style: { flexShrink: 0 } }), _jsx(SegmentLabel, { children: segment.label }), segment.trailingElement] }), segment.menuElement, segment.actions && segment.actions.length > 0 && (_jsx(ActionsMenu, { actions: segment.actions, ariaLabel: `${segment.label} actions` })), hasDropdown && (_jsx(ChevronButton, { ref: chevronRef, "$isOpen": isOpen, onClick: toggle, onKeyDown: (e) => {
@@ -18,9 +18,13 @@ const StyledHeader = styled(Header) `
18
18
  z-index: 8001 !important;
19
19
 
20
20
  // Carbon defaults the tools area to flex: 1, which splits the row
21
- // 50/50 with breadcrumbs. Make it intrinsic instead.
21
+ // 50/50 with breadcrumbs. Make it intrinsic and pin it to the right
22
+ // (margin-inline-start: auto) so the tools stay flush right even
23
+ // when the breadcrumb bar is absent — loading state, or routes that
24
+ // don't publish breadcrumbs.
22
25
  .cds--header__global {
23
26
  flex: 0 0 auto;
27
+ margin-inline-start: auto;
24
28
  }
25
29
  `;
26
30
  const LogoSection = styled.a `
@@ -32,9 +32,7 @@ function buildClusterSidebarEntries(clusters, { isAppVisible, resolveClusterLink
32
32
  const healthKey = isAdminApp(app)
33
33
  ? 'zeebe'
34
34
  : resolvedApp;
35
- const linkProps = cluster.status?.[healthKey] === 'Healthy'
36
- ? (teaser ?? { href: endpoint })
37
- : teaser;
35
+ const linkProps = cluster.status?.[healthKey] === 'Healthy' ? { href: endpoint } : teaser;
38
36
  if (!linkProps)
39
37
  return [];
40
38
  return [
@@ -3,6 +3,12 @@ import type { BreadcrumbDescriptor } from './use-c3-navigation-v2';
3
3
  export interface UseClusterWebappBreadcrumbsOptions {
4
4
  /** The app currently being viewed. */
5
5
  currentApp: CamundaApp;
6
+ /**
7
+ * Per-app URL map matching the shape of `Cluster['endpoints']`. Source for
8
+ * the modeler back-links on the org/cluster crumb labels, and the sole
9
+ * source for the app-switcher dropdown when no SaaS cluster is available.
10
+ */
11
+ webappLinks?: Partial<Record<CamundaApp, string>>;
6
12
  }
7
13
  /**
8
14
  * Builds the standard breadcrumb chain for a cluster-scoped Camunda webapp:
@@ -22,6 +22,11 @@ const SWITCHABLE_APPS = [
22
22
  'optimize',
23
23
  'admin',
24
24
  ];
25
+ /** Healthy on the cluster, using zeebe as the admin/identity health proxy. */
26
+ function isAppHealthyOnCluster(cluster, app) {
27
+ const healthKey = isAdminApp(app) ? 'zeebe' : app;
28
+ return cluster.status?.[healthKey] === 'Healthy';
29
+ }
25
30
  /**
26
31
  * Replaces the path of `modelerEndpoint` with `/org/{orgId}`. Used to derive
27
32
  * the modeler URL for sibling orgs from the current cluster's modeler
@@ -64,14 +69,15 @@ function buildModelerOrgUrl(modelerEndpoint, orgId) {
64
69
  * crumb) without any environment flag.
65
70
  */
66
71
  export function useClusterWebappBreadcrumbs(options) {
67
- const { currentApp } = options;
72
+ const { currentApp, webappLinks } = options;
68
73
  const { clusters, activeOrg, orgs } = useC3Profile();
69
74
  const config = useC3UserConfiguration();
70
75
  const currentClusterUuid = config.currentClusterUuid;
71
76
  const currentCluster = useMemo(() => clusters?.find((c) => c.uuid === currentClusterUuid) ?? null, [clusters, currentClusterUuid]);
72
77
  return useMemo(() => {
73
78
  const crumbs = [];
74
- const modelerUrl = currentCluster?.endpoints?.modeler;
79
+ const modelerUrl = webappLinks?.modeler ??
80
+ (config.domain ? `https://modeler.${config.domain}` : undefined);
75
81
  // ── Org crumb ──────────────────────────────────────────────────────────
76
82
  if (activeOrg) {
77
83
  const allOrgs = orgs ?? [];
@@ -123,7 +129,8 @@ export function useClusterWebappBreadcrumbs(options) {
123
129
  ? `${modelerUrl}?fromCluster=${currentCluster.uuid}`
124
130
  : undefined;
125
131
  const reachableClusters = (clusters ?? []).filter((c) => c.uuid === currentCluster.uuid ||
126
- getEndpointForApp(currentApp, c.endpoints));
132
+ (getEndpointForApp(currentApp, c.endpoints) &&
133
+ isAppHealthyOnCluster(c, currentApp)));
127
134
  const hasSiblings = reachableClusters.filter((c) => c.uuid !== currentCluster.uuid).length >
128
135
  0;
129
136
  const clusterDropdownItems = hasSiblings
@@ -170,21 +177,25 @@ export function useClusterWebappBreadcrumbs(options) {
170
177
  // that pass `currentApp: 'identity'` (legacy code path) still match the
171
178
  // switcher row via this normalisation, otherwise the current entry
172
179
  // would be missing from the dropdown and nothing would be selected.
173
- const switcherKey = isAdminApp(currentApp) ? 'admin' : currentApp;
180
+ const switcherKey = isAdminApp(currentApp)
181
+ ? 'admin'
182
+ : currentApp;
174
183
  const resolvedCurrentApp = isAdminApp(currentApp) && currentCluster
175
184
  ? resolveAdminAppName(currentCluster)
176
185
  : currentApp;
177
186
  const appLabel = getReadableAppName(resolvedCurrentApp);
178
187
  const appIcon = APP_ICONS[resolvedCurrentApp];
179
- const reachableApps = currentCluster
188
+ const sourceEndpoints = currentCluster?.endpoints ?? webappLinks;
189
+ const reachableApps = sourceEndpoints
180
190
  ? SWITCHABLE_APPS.filter((app) => app === switcherKey ||
181
- getEndpointForApp(app, currentCluster.endpoints))
191
+ (getEndpointForApp(app, sourceEndpoints) &&
192
+ (!currentCluster || isAppHealthyOnCluster(currentCluster, app))))
182
193
  : [switcherKey];
183
194
  const hasSiblingApps = reachableApps.filter((app) => app !== switcherKey).length > 0;
184
- const dropdownItems = hasSiblingApps && currentCluster
195
+ const dropdownItems = hasSiblingApps && sourceEndpoints
185
196
  ? reachableApps.map((app) => {
186
197
  const isCurrent = app === switcherKey;
187
- const resolvedApp = isAdminApp(app)
198
+ const resolvedApp = isAdminApp(app) && currentCluster
188
199
  ? resolveAdminAppName(currentCluster)
189
200
  : app;
190
201
  return {
@@ -196,7 +207,7 @@ export function useClusterWebappBreadcrumbs(options) {
196
207
  ? {}
197
208
  : {
198
209
  linkProps: {
199
- href: getEndpointForApp(app, currentCluster.endpoints) ?? '',
210
+ href: getEndpointForApp(app, sourceEndpoints) ?? '',
200
211
  },
201
212
  }),
202
213
  };
@@ -215,5 +226,13 @@ export function useClusterWebappBreadcrumbs(options) {
215
226
  : {}),
216
227
  });
217
228
  return crumbs;
218
- }, [activeOrg, orgs, clusters, currentCluster, currentApp]);
229
+ }, [
230
+ activeOrg,
231
+ orgs,
232
+ clusters,
233
+ currentCluster,
234
+ currentApp,
235
+ webappLinks,
236
+ config.domain,
237
+ ]);
219
238
  }
@@ -22,5 +22,5 @@ export type C3ProfileContextValue = {
22
22
  export declare const C3ProfileContext: React.Context<C3ProfileContextValue>;
23
23
  export declare const C3ProfileProvider: ({ children }: {
24
24
  children: JSX.Element;
25
- }) => import("react/jsx-runtime").JSX.Element;
25
+ }) => JSX.Element;
26
26
  export declare const useC3Profile: () => C3ProfileContextValue;
@@ -5,7 +5,7 @@ type C3ClusterUpdateManagerProps = {
5
5
  stage: Stage;
6
6
  userToken: string;
7
7
  };
8
- declare const C3ClusterUpdateManager: ({ children, stage, userToken, }: C3ClusterUpdateManagerProps) => import("react/jsx-runtime").JSX.Element;
8
+ declare const C3ClusterUpdateManager: ({ children, stage, userToken, }: C3ClusterUpdateManagerProps) => React.JSX.Element;
9
9
  declare const useClusterUpdate: ({ onUpdate }: {
10
10
  onUpdate: (data: any) => void;
11
11
  }) => void;
@@ -25,7 +25,7 @@ export declare const getReadableAppName: (appName: CamundaApp) => string;
25
25
  /**
26
26
  * Gets the endpoint URL for an app, handling identity/admin aliasing.
27
27
  */
28
- export declare const getEndpointForApp: (app: CamundaApp, endpoints: Cluster["endpoints"]) => string | undefined;
28
+ export declare const getEndpointForApp: (app: CamundaApp, endpoints: Partial<Cluster["endpoints"]>) => string | undefined;
29
29
  /**
30
30
  * Checks if user has read permission for an app, handling identity/admin aliasing.
31
31
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/camunda-composite-components",
3
- "version": "0.24.0",
3
+ "version": "0.25.1",
4
4
  "description": "Camunda Composite Components",
5
5
  "bugs": {
6
6
  "url": "https://github.com/camunda/camunda-cloud-management-apps/issues"
@@ -24,43 +24,43 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "jwt-decode": "4.0.0",
27
- "react-error-boundary": "6.1.1",
27
+ "react-error-boundary": "6.1.2",
28
28
  "react-markdown": "10.1.0",
29
- "semver": "7.8.0"
29
+ "semver": "7.8.4"
30
30
  },
31
31
  "devDependencies": {
32
- "@auth0/auth0-spa-js": "2.19.3",
33
- "@carbon/react": "1.107.1",
32
+ "@auth0/auth0-spa-js": "2.21.1",
33
+ "@carbon/react": "1.109.0",
34
34
  "@chromatic-com/storybook": "5.2.1",
35
35
  "@mdx-js/react": "3.1.1",
36
- "@playwright/test": "1.60.0",
37
- "@storybook/addon-a11y": "10.4.0",
38
- "@storybook/addon-docs": "10.4.0",
39
- "@storybook/addon-links": "10.4.0",
40
- "@storybook/addon-vitest": "10.4.0",
41
- "@storybook/react": "10.4.0",
42
- "@storybook/react-vite": "10.4.0",
43
- "@vitest/browser": "4.1.7",
44
- "@vitest/browser-playwright": "4.1.7",
45
- "vitest": "4.1.7",
36
+ "@playwright/test": "1.61.0",
37
+ "@storybook/addon-a11y": "10.4.6",
38
+ "@storybook/addon-docs": "10.4.6",
39
+ "@storybook/addon-links": "10.4.6",
40
+ "@storybook/addon-vitest": "10.4.6",
41
+ "@storybook/react": "10.4.6",
42
+ "@storybook/react-vite": "10.4.6",
43
+ "@vitest/browser": "4.1.9",
44
+ "@vitest/browser-playwright": "4.1.9",
45
+ "vitest": "4.1.9",
46
46
  "conventional-changelog-conventionalcommits": "9.3.1",
47
- "eslint-import-resolver-typescript": "4.4.4",
47
+ "eslint-import-resolver-typescript": "4.4.5",
48
48
  "eslint-plugin-react": "7.37.5",
49
49
  "eslint-plugin-react-hooks": "7.1.1",
50
- "eslint-plugin-storybook": "10.4.0",
50
+ "eslint-plugin-storybook": "10.4.6",
51
51
  "event-source-polyfill": "1.0.31",
52
- "mixpanel-browser": "2.79.0",
53
- "playwright": "1.60.0",
54
- "react": "19.2.6",
55
- "react-dom": "19.2.6",
56
- "react-is": "19.2.6",
52
+ "mixpanel-browser": "2.80.0",
53
+ "playwright": "1.61.0",
54
+ "react": "19.2.7",
55
+ "react-dom": "19.2.7",
56
+ "react-is": "19.2.7",
57
57
  "rimraf": "6.1.3",
58
58
  "serve": "14.2.6",
59
- "storybook": "10.4.0",
59
+ "storybook": "10.4.6",
60
60
  "styled-components": "6.4.2",
61
- "typescript-eslint": "8.59.4",
61
+ "typescript-eslint": "8.61.1",
62
62
  "wait-on": "9.0.10",
63
- "@camunda/ccma-shared-types": "0.1.1"
63
+ "@camunda/ccma-shared-types": "0.1.3"
64
64
  },
65
65
  "peerDependencies": {
66
66
  "@carbon/react": "1.x",