@atlaskit/smart-card 44.23.2 → 44.23.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @atlaskit/smart-card
2
2
 
3
+ ## 44.23.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`6cb1e43229040`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/6cb1e43229040) -
8
+ Append url with cross-product analytics params on link click
9
+
3
10
  ## 44.23.2
4
11
 
5
12
  ### Patch Changes
@@ -11,7 +11,7 @@ var ANALYTICS_CHANNEL = exports.ANALYTICS_CHANNEL = 'media';
11
11
  var context = exports.context = {
12
12
  componentName: 'smart-cards',
13
13
  packageName: "@atlaskit/smart-card" || '',
14
- packageVersion: "44.23.1" || ''
14
+ packageVersion: "44.23.2" || ''
15
15
  };
16
16
  var TrackQuickActionType = exports.TrackQuickActionType = /*#__PURE__*/function (TrackQuickActionType) {
17
17
  TrackQuickActionType["StatusUpdate"] = "StatusUpdate";
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.isAuxClick = void 0;
6
+ exports.isAuxClick = exports.getAnchorAttributesFromEvent = void 0;
7
7
  /**
8
8
  * Returns true for genuine middle-clicks (button === 1).
9
9
  * Filters out Windows right-clicks, which fire onAuxClick with button === 2
@@ -11,4 +11,33 @@ exports.isAuxClick = void 0;
11
11
  */
12
12
  var isAuxClick = exports.isAuxClick = function isAuxClick(e) {
13
13
  return e.button === 1;
14
+ };
15
+
16
+ /**
17
+ * Extracts `href` and `target` from the anchor element that is the event's `currentTarget`.
18
+ *
19
+ * Smart Link click handlers are attached to multiple card renderers (InlineCard, BlockCard,
20
+ * EmbedCard, FlexibleCard). When the handler needs to manually open a link — for example,
21
+ * when native anchor navigation has been prevented — it uses this helper to read the
22
+ * anchor's resolved URL and intended target from the event rather than re-deriving them.
23
+ *
24
+ * Returns `{ href: undefined, target: '_self' }` when `currentTarget` is not an anchor
25
+ * element (e.g. a button or wrapper div), so callers can safely fall back to a default target.
26
+ *
27
+ * @param event - A React mouse or keyboard event whose `currentTarget` may be an anchor.
28
+ * @returns The resolved absolute `href` and `target` attribute of the anchor, or safe
29
+ * defaults if `currentTarget` is not an anchor.
30
+ */
31
+ var getAnchorAttributesFromEvent = exports.getAnchorAttributesFromEvent = function getAnchorAttributesFromEvent(event) {
32
+ var currentTarget = event.currentTarget;
33
+ if (!(currentTarget instanceof HTMLAnchorElement)) {
34
+ return {
35
+ href: undefined,
36
+ target: '_self'
37
+ };
38
+ }
39
+ return {
40
+ href: currentTarget.href,
41
+ target: currentTarget.target || '_self'
42
+ };
14
43
  };
@@ -93,7 +93,7 @@ function Component(_ref) {
93
93
  var services = (0, _helpers.getServices)(state.details);
94
94
  var thirdPartyARI = (0, _helpers.getThirdPartyARI)(state.details);
95
95
  var firstPartyIdentifier = (0, _helpers.getFirstPartyIdentifier)();
96
- var wrapUrlWithCrossProductAnalytics = (0, _useSmartLinkCrossProductUrlWrapper.useSmartLinkCrossProductUrlWrapperGated)({
96
+ var appendCrossProductAnalyticsParams = (0, _useSmartLinkCrossProductUrlWrapper.useSmartLinkCrossProductUrlWrapperGated)({
97
97
  details: state.details
98
98
  });
99
99
  var actionOptions = (0, _combineActionOptions.combineActionOptions)({
@@ -127,56 +127,120 @@ function Component(_ref) {
127
127
  var isDisablePreviewPanel = disablePreviewPanel && (0, _experiments.editorExperiment)('platform_editor_preview_panel_linking_exp', true, {
128
128
  exposure: true
129
129
  });
130
+ if ((0, _platformFeatureFlags.fg)('platform_smartlink_xpc_url_wrapping')) {
131
+ var _appendCrossProductAn;
132
+ // FIXME: InlineCard, BlockCard and EmbedCard call event.preventDefault() internally
133
+ // before the event bubbles up to this handler. This forces us to snapshot
134
+ // event.defaultPrevented before calling onClick to detect whether the consumer
135
+ // specifically prevented navigation. Ideally those components should not call
136
+ // preventDefault so this workaround can be removed.
137
+ var isEventDefaultPrevented = event.defaultPrevented;
138
+ var canOpenPreviewPanel = !isModifierKeyPressed && ari && name && openPreviewPanel && (isPreviewPanelAvailable === null || isPreviewPanelAvailable === void 0 ? void 0 : isPreviewPanelAvailable({
139
+ ari: ari
140
+ })) && !isDisablePreviewPanel;
130
141
 
131
- // If preview panel is available and the user clicked on the link,
132
- // delegate the click to the preview panel handler
133
- if (!isModifierKeyPressed && ari && name && openPreviewPanel && isPreviewPanelAvailable !== null && isPreviewPanelAvailable !== void 0 && isPreviewPanelAvailable({
134
- ari: ari
135
- }) && !isDisablePreviewPanel) {
136
- var _extractSmartLinkEmbe;
137
- event.preventDefault();
138
- event.stopPropagation();
139
- openPreviewPanel({
140
- url: url,
141
- ari: ari,
142
- name: name,
143
- iconUrl: (0, _helpers.getObjectIconUrl)(state.details),
144
- panelData: {
145
- embedUrl: (0, _expValEquals.expValEquals)('platform_hover_card_preview_panel', 'cohort', 'test') ? (_extractSmartLinkEmbe = (0, _linkExtractors.extractSmartLinkEmbed)(state.details)) === null || _extractSmartLinkEmbe === void 0 ? void 0 : _extractSmartLinkEmbe.src : undefined
146
- }
147
- });
148
- (0, _click.fireLinkClickedEvent)(createAnalyticsEvent)(event, {
149
- attributes: {
150
- clickOutcome: 'previewPanel'
151
- }
152
- });
153
- return;
154
- } else if (!onClick && !isFlexibleUi) {
155
- var clickUrl = (0, _helpers.getClickUrl)(url, state.details);
142
+ // Preview panel takes priority over link navigation when available.
143
+ if (canOpenPreviewPanel) {
144
+ var _extractSmartLinkEmbe;
145
+ event.preventDefault();
146
+ event.stopPropagation();
147
+ openPreviewPanel({
148
+ url: url,
149
+ ari: ari,
150
+ name: name,
151
+ iconUrl: (0, _helpers.getObjectIconUrl)(state.details),
152
+ panelData: {
153
+ embedUrl: (0, _expValEquals.expValEquals)('platform_hover_card_preview_panel', 'cohort', 'test') ? (_extractSmartLinkEmbe = (0, _linkExtractors.extractSmartLinkEmbed)(state.details)) === null || _extractSmartLinkEmbe === void 0 ? void 0 : _extractSmartLinkEmbe.src : undefined
154
+ }
155
+ });
156
+ (0, _click.fireLinkClickedEvent)(createAnalyticsEvent)(event, {
157
+ attributes: {
158
+ clickOutcome: 'previewPanel'
159
+ }
160
+ });
161
+ return;
162
+ }
156
163
 
157
- // Ctrl+left click on mac typically doesn't trigger onClick
158
- // The event could have potentially had `e.preventDefault()` called on it by now
159
- // event by smart card internally
160
- // If it has been called then only then can `isSpecialEvent` be true.
161
- var target = (0, _utils.isSpecialEvent)(event) ? '_blank' : '_self';
162
- if ((0, _platformFeatureFlags.fg)('platform_smartlink_xpc_url_wrapping')) {
163
- var wrappedUrl = wrapUrlWithCrossProductAnalytics(clickUrl);
164
- window.open(wrappedUrl, target);
165
- } else {
166
- window.open(clickUrl, target);
164
+ // For FlexibleCard, read target from the clicked anchor element (e.g. _blank for links
165
+ // rendered with explicit target). For classic cards, default to _self
166
+ var _getAnchorAttributesF = (0, _clickHelpers.getAnchorAttributesFromEvent)(event),
167
+ anchorTarget = _getAnchorAttributesF.target;
168
+ var target = (0, _utils.isSpecialEvent)(event) ? '_blank' : isFlexibleUi ? anchorTarget : '_self';
169
+
170
+ // FIXME: preferredUrl should be rendered in the DOM anchor href instead of derived at click time
171
+ var preferredUrl = (0, _helpers.getClickUrl)(url, state.details);
172
+ var destinationUrl = (_appendCrossProductAn = appendCrossProductAnalyticsParams(preferredUrl)) !== null && _appendCrossProductAn !== void 0 ? _appendCrossProductAn : preferredUrl;
173
+
174
+ // FIXME: Consumer that handle click even themselves via callback won't have the decorated URL
175
+ onClick === null || onClick === void 0 || onClick(event);
176
+
177
+ // Check if the event is prevented via onClick callback
178
+ var consumerPreventedNavigation = event.defaultPrevented && !isEventDefaultPrevented;
179
+
180
+ // Classic cards (InlineCard, BlockCard, EmbedCard) rely on their own anchor navigation
181
+ // when onClick is provided, so this handler should not open the link for them.
182
+ // FlexibleCard's anchor is prevented from native navigation, so this handler always
183
+ // opens the link for FlexibleCard unless the consumer's onClick called preventDefault.
184
+ var shouldOpenLink = isFlexibleUi || !onClick;
185
+ var doOpenLink = shouldOpenLink && !consumerPreventedNavigation;
186
+ if (doOpenLink) {
187
+ event.preventDefault();
188
+ window.open(destinationUrl, target);
167
189
  }
168
- (0, _click.fireLinkClickedEvent)(createAnalyticsEvent)(event, {
190
+
191
+ // Only set clickOutcome when this handler actually opened the link.
192
+ // If a parent onClick handled navigation, fire a generic click event instead.
193
+ (0, _click.fireLinkClickedEvent)(createAnalyticsEvent)(event, doOpenLink ? {
169
194
  attributes: {
170
195
  clickOutcome: target === '_blank' ? 'clickThroughNewTabOrWindow' : 'clickThrough'
171
196
  }
172
- });
197
+ } : undefined);
173
198
  } else {
174
- if (onClick) {
175
- onClick(event);
199
+ // If preview panel is available and the user clicked on the link,
200
+ // delegate the click to the preview panel handler
201
+ if (!isModifierKeyPressed && ari && name && openPreviewPanel && isPreviewPanelAvailable !== null && isPreviewPanelAvailable !== void 0 && isPreviewPanelAvailable({
202
+ ari: ari
203
+ }) && !isDisablePreviewPanel) {
204
+ var _extractSmartLinkEmbe2;
205
+ event.preventDefault();
206
+ event.stopPropagation();
207
+ openPreviewPanel({
208
+ url: url,
209
+ ari: ari,
210
+ name: name,
211
+ iconUrl: (0, _helpers.getObjectIconUrl)(state.details),
212
+ panelData: {
213
+ embedUrl: (0, _expValEquals.expValEquals)('platform_hover_card_preview_panel', 'cohort', 'test') ? (_extractSmartLinkEmbe2 = (0, _linkExtractors.extractSmartLinkEmbed)(state.details)) === null || _extractSmartLinkEmbe2 === void 0 ? void 0 : _extractSmartLinkEmbe2.src : undefined
214
+ }
215
+ });
216
+ (0, _click.fireLinkClickedEvent)(createAnalyticsEvent)(event, {
217
+ attributes: {
218
+ clickOutcome: 'previewPanel'
219
+ }
220
+ });
221
+ return;
222
+ } else if (!onClick && !isFlexibleUi) {
223
+ var clickUrl = (0, _helpers.getClickUrl)(url, state.details);
224
+
225
+ // Ctrl+left click on mac typically doesn't trigger onClick
226
+ // The event could have potentially had `e.preventDefault()` called on it by now
227
+ // event by smart card internally
228
+ // If it has been called then only then can `isSpecialEvent` be true.
229
+ var _target = (0, _utils.isSpecialEvent)(event) ? '_blank' : '_self';
230
+ window.open(clickUrl, _target);
231
+ (0, _click.fireLinkClickedEvent)(createAnalyticsEvent)(event, {
232
+ attributes: {
233
+ clickOutcome: _target === '_blank' ? 'clickThroughNewTabOrWindow' : 'clickThrough'
234
+ }
235
+ });
236
+ } else {
237
+ if (onClick) {
238
+ onClick(event);
239
+ }
240
+ (0, _click.fireLinkClickedEvent)(createAnalyticsEvent)(event);
176
241
  }
177
- (0, _click.fireLinkClickedEvent)(createAnalyticsEvent)(event);
178
242
  }
179
- }, [fireEvent, id, isFlexibleUi, appearance, definitionId, onClick, url, wrapUrlWithCrossProductAnalytics, state.details, ari, name, fire3PClickEvent, shouldFire3PClickEvent, isPreviewPanelAvailable, openPreviewPanel, createAnalyticsEvent, disablePreviewPanel]);
243
+ }, [fireEvent, id, isFlexibleUi, appearance, definitionId, onClick, url, appendCrossProductAnalyticsParams, state.details, ari, name, fire3PClickEvent, shouldFire3PClickEvent, isPreviewPanelAvailable, openPreviewPanel, createAnalyticsEvent, disablePreviewPanel]);
180
244
 
181
245
  // Exposure fires once per eligible mount; click-time reads use no-exposure variant.
182
246
  (0, _react.useEffect)(function () {
@@ -20,10 +20,12 @@ var _constants = require("../../../constants");
20
20
  var _extractRovoChatAction = _interopRequireDefault(require("../../../extractors/flexible/actions/extract-rovo-chat-action"));
21
21
  var _helpers = require("../../../state/helpers");
22
22
  var _useRovoConfig2 = _interopRequireDefault(require("../../../state/hooks/use-rovo-config"));
23
+ var _useSmartLinkCrossProductUrlWrapper = require("../../../state/hooks/use-smart-link-cross-product-url-wrapper");
23
24
  var _store = require("../../../state/store");
24
25
  var _utils = require("../../../utils");
25
26
  var _aiSummary = require("../../../utils/ai-summary");
26
27
  var _click = require("../../../utils/analytics/click");
28
+ var _clickHelpers = require("../../../utils/click-helpers");
27
29
  var _styled = require("../styled");
28
30
  var _utils2 = require("../utils");
29
31
  var _ContentContainer = _interopRequireDefault(require("./ContentContainer"));
@@ -95,6 +97,9 @@ var HoverCardContent = function HoverCardContent(_ref4) {
95
97
  product = _useSmartLinkContext.product;
96
98
  var isAISummaryEnabled = (0, _aiSummary.getIsAISummaryEnabled)(isAdminHubAIEnabled, cardState.details);
97
99
  var services = (0, _helpers.getServices)(linkState.details);
100
+ var appendCrossProductAnalyticsParams = (0, _useSmartLinkCrossProductUrlWrapper.useSmartLinkCrossProductUrlWrapperGated)({
101
+ details: linkState.details
102
+ });
98
103
  var statusRef = (0, _react.useRef)(linkStatus);
99
104
  var fireEventRef = (0, _react.useRef)(fireEvent);
100
105
  var definitionIdRef = (0, _react.useRef)(definitionId);
@@ -136,6 +141,24 @@ var HoverCardContent = function HoverCardContent(_ref4) {
136
141
  };
137
142
  }, []);
138
143
  var onClick = (0, _react.useCallback)(function (event) {
144
+ if ((0, _platformFeatureFlags.fg)('platform_smartlink_xpc_url_wrapping')) {
145
+ // Prevent the anchor's native navigation so we can open the destination URL
146
+ // with cross-product analytics query parameters appended.
147
+ // The href is read from the anchor element at click time rather than at render
148
+ // time because the URL for the anchor may be different from original URL.
149
+ // The component may use the URL from the resolved response which can be a redirect URL
150
+ // or other preferred URL based on link extractor.
151
+ // The cross-product params are client-only and cannot be rendered
152
+ // server-side. Falls back to the original `url` prop if the event target is
153
+ // not an anchor element.
154
+ event.preventDefault();
155
+ var _getAnchorAttributesF = (0, _clickHelpers.getAnchorAttributesFromEvent)(event),
156
+ _getAnchorAttributesF2 = _getAnchorAttributesF.href,
157
+ href = _getAnchorAttributesF2 === void 0 ? url : _getAnchorAttributesF2,
158
+ target = _getAnchorAttributesF.target;
159
+ var destinationUrl = appendCrossProductAnalyticsParams(href);
160
+ window.open(destinationUrl, target);
161
+ }
139
162
  var isModifierKeyPressed = (0, _utils.isSpecialEvent)(event);
140
163
  fireEvent('ui.smartLink.clicked.titleGoToLink', {
141
164
  id: id,
@@ -144,7 +167,7 @@ var HoverCardContent = function HoverCardContent(_ref4) {
144
167
  definitionId: definitionId !== null && definitionId !== void 0 ? definitionId : null
145
168
  });
146
169
  (0, _click.fireLinkClickedEvent)(createAnalyticsEvent)(event);
147
- }, [createAnalyticsEvent, id, fireEvent, definitionId]);
170
+ }, [createAnalyticsEvent, appendCrossProductAnalyticsParams, id, fireEvent, definitionId, url]);
148
171
  var data = (_cardState$details = cardState.details) === null || _cardState$details === void 0 ? void 0 : _cardState$details.data;
149
172
  var _getMetadata = (0, _utils2.getMetadata)(extensionKey, data),
150
173
  subtitle = _getMetadata.subtitle;
@@ -19,7 +19,7 @@ var _excluded = ["href", "children", "checkSafety", "onClick", "testId", "isLink
19
19
  _excluded2 = ["isLinkSafe", "showSafetyWarningModal"];
20
20
  var PACKAGE_DATA = {
21
21
  packageName: "@atlaskit/smart-card",
22
- packageVersion: "44.23.1",
22
+ packageVersion: "44.23.2",
23
23
  componentName: 'linkUrl'
24
24
  };
25
25
  var LinkUrl = function LinkUrl(_ref) {
@@ -2,7 +2,7 @@ export const ANALYTICS_CHANNEL = 'media';
2
2
  export const context = {
3
3
  componentName: 'smart-cards',
4
4
  packageName: "@atlaskit/smart-card" || '',
5
- packageVersion: "44.23.1" || ''
5
+ packageVersion: "44.23.2" || ''
6
6
  };
7
7
  export let TrackQuickActionType = /*#__PURE__*/function (TrackQuickActionType) {
8
8
  TrackQuickActionType["StatusUpdate"] = "StatusUpdate";
@@ -5,4 +5,33 @@
5
5
  */
6
6
  export const isAuxClick = e => {
7
7
  return e.button === 1;
8
+ };
9
+
10
+ /**
11
+ * Extracts `href` and `target` from the anchor element that is the event's `currentTarget`.
12
+ *
13
+ * Smart Link click handlers are attached to multiple card renderers (InlineCard, BlockCard,
14
+ * EmbedCard, FlexibleCard). When the handler needs to manually open a link — for example,
15
+ * when native anchor navigation has been prevented — it uses this helper to read the
16
+ * anchor's resolved URL and intended target from the event rather than re-deriving them.
17
+ *
18
+ * Returns `{ href: undefined, target: '_self' }` when `currentTarget` is not an anchor
19
+ * element (e.g. a button or wrapper div), so callers can safely fall back to a default target.
20
+ *
21
+ * @param event - A React mouse or keyboard event whose `currentTarget` may be an anchor.
22
+ * @returns The resolved absolute `href` and `target` attribute of the anchor, or safe
23
+ * defaults if `currentTarget` is not an anchor.
24
+ */
25
+ export const getAnchorAttributesFromEvent = event => {
26
+ const currentTarget = event.currentTarget;
27
+ if (!(currentTarget instanceof HTMLAnchorElement)) {
28
+ return {
29
+ href: undefined,
30
+ target: '_self'
31
+ };
32
+ }
33
+ return {
34
+ href: currentTarget.href,
35
+ target: currentTarget.target || '_self'
36
+ };
8
37
  };
@@ -16,7 +16,7 @@ import { isSpecialClick, isSpecialEvent, isSpecialKey } from '../../utils';
16
16
  import { combineActionOptions } from '../../utils/actions/combine-action-options';
17
17
  import { fireLinkClickedEvent } from '../../utils/analytics/click';
18
18
  import { SmartLinkAnalyticsContext } from '../../utils/analytics/SmartLinkAnalyticsContext';
19
- import { isAuxClick } from '../../utils/click-helpers';
19
+ import { getAnchorAttributesFromEvent, isAuxClick } from '../../utils/click-helpers';
20
20
  import { isFlexibleUiCard } from '../../utils/flexible';
21
21
  import * as measure from '../../utils/performance';
22
22
  import { BlockCard } from '../BlockCard';
@@ -83,7 +83,7 @@ function Component({
83
83
  const services = getServices(state.details);
84
84
  const thirdPartyARI = getThirdPartyARI(state.details);
85
85
  const firstPartyIdentifier = getFirstPartyIdentifier();
86
- const wrapUrlWithCrossProductAnalytics = useSmartLinkCrossProductUrlWrapperGated({
86
+ const appendCrossProductAnalyticsParams = useSmartLinkCrossProductUrlWrapperGated({
87
87
  details: state.details
88
88
  });
89
89
  const actionOptions = combineActionOptions({
@@ -117,56 +117,121 @@ function Component({
117
117
  const isDisablePreviewPanel = disablePreviewPanel && editorExperiment('platform_editor_preview_panel_linking_exp', true, {
118
118
  exposure: true
119
119
  });
120
+ if (fg('platform_smartlink_xpc_url_wrapping')) {
121
+ var _appendCrossProductAn;
122
+ // FIXME: InlineCard, BlockCard and EmbedCard call event.preventDefault() internally
123
+ // before the event bubbles up to this handler. This forces us to snapshot
124
+ // event.defaultPrevented before calling onClick to detect whether the consumer
125
+ // specifically prevented navigation. Ideally those components should not call
126
+ // preventDefault so this workaround can be removed.
127
+ const isEventDefaultPrevented = event.defaultPrevented;
128
+ const canOpenPreviewPanel = !isModifierKeyPressed && ari && name && openPreviewPanel && (isPreviewPanelAvailable === null || isPreviewPanelAvailable === void 0 ? void 0 : isPreviewPanelAvailable({
129
+ ari
130
+ })) && !isDisablePreviewPanel;
120
131
 
121
- // If preview panel is available and the user clicked on the link,
122
- // delegate the click to the preview panel handler
123
- if (!isModifierKeyPressed && ari && name && openPreviewPanel && isPreviewPanelAvailable !== null && isPreviewPanelAvailable !== void 0 && isPreviewPanelAvailable({
124
- ari
125
- }) && !isDisablePreviewPanel) {
126
- var _extractSmartLinkEmbe;
127
- event.preventDefault();
128
- event.stopPropagation();
129
- openPreviewPanel({
130
- url,
131
- ari,
132
- name,
133
- iconUrl: getObjectIconUrl(state.details),
134
- panelData: {
135
- embedUrl: expValEquals('platform_hover_card_preview_panel', 'cohort', 'test') ? (_extractSmartLinkEmbe = extractSmartLinkEmbed(state.details)) === null || _extractSmartLinkEmbe === void 0 ? void 0 : _extractSmartLinkEmbe.src : undefined
136
- }
137
- });
138
- fireLinkClickedEvent(createAnalyticsEvent)(event, {
139
- attributes: {
140
- clickOutcome: 'previewPanel'
141
- }
142
- });
143
- return;
144
- } else if (!onClick && !isFlexibleUi) {
145
- const clickUrl = getClickUrl(url, state.details);
132
+ // Preview panel takes priority over link navigation when available.
133
+ if (canOpenPreviewPanel) {
134
+ var _extractSmartLinkEmbe;
135
+ event.preventDefault();
136
+ event.stopPropagation();
137
+ openPreviewPanel({
138
+ url,
139
+ ari,
140
+ name,
141
+ iconUrl: getObjectIconUrl(state.details),
142
+ panelData: {
143
+ embedUrl: expValEquals('platform_hover_card_preview_panel', 'cohort', 'test') ? (_extractSmartLinkEmbe = extractSmartLinkEmbed(state.details)) === null || _extractSmartLinkEmbe === void 0 ? void 0 : _extractSmartLinkEmbe.src : undefined
144
+ }
145
+ });
146
+ fireLinkClickedEvent(createAnalyticsEvent)(event, {
147
+ attributes: {
148
+ clickOutcome: 'previewPanel'
149
+ }
150
+ });
151
+ return;
152
+ }
146
153
 
147
- // Ctrl+left click on mac typically doesn't trigger onClick
148
- // The event could have potentially had `e.preventDefault()` called on it by now
149
- // event by smart card internally
150
- // If it has been called then only then can `isSpecialEvent` be true.
151
- const target = isSpecialEvent(event) ? '_blank' : '_self';
152
- if (fg('platform_smartlink_xpc_url_wrapping')) {
153
- const wrappedUrl = wrapUrlWithCrossProductAnalytics(clickUrl);
154
- window.open(wrappedUrl, target);
155
- } else {
156
- window.open(clickUrl, target);
154
+ // For FlexibleCard, read target from the clicked anchor element (e.g. _blank for links
155
+ // rendered with explicit target). For classic cards, default to _self
156
+ const {
157
+ target: anchorTarget
158
+ } = getAnchorAttributesFromEvent(event);
159
+ const target = isSpecialEvent(event) ? '_blank' : isFlexibleUi ? anchorTarget : '_self';
160
+
161
+ // FIXME: preferredUrl should be rendered in the DOM anchor href instead of derived at click time
162
+ const preferredUrl = getClickUrl(url, state.details);
163
+ const destinationUrl = (_appendCrossProductAn = appendCrossProductAnalyticsParams(preferredUrl)) !== null && _appendCrossProductAn !== void 0 ? _appendCrossProductAn : preferredUrl;
164
+
165
+ // FIXME: Consumer that handle click even themselves via callback won't have the decorated URL
166
+ onClick === null || onClick === void 0 ? void 0 : onClick(event);
167
+
168
+ // Check if the event is prevented via onClick callback
169
+ const consumerPreventedNavigation = event.defaultPrevented && !isEventDefaultPrevented;
170
+
171
+ // Classic cards (InlineCard, BlockCard, EmbedCard) rely on their own anchor navigation
172
+ // when onClick is provided, so this handler should not open the link for them.
173
+ // FlexibleCard's anchor is prevented from native navigation, so this handler always
174
+ // opens the link for FlexibleCard unless the consumer's onClick called preventDefault.
175
+ const shouldOpenLink = isFlexibleUi || !onClick;
176
+ const doOpenLink = shouldOpenLink && !consumerPreventedNavigation;
177
+ if (doOpenLink) {
178
+ event.preventDefault();
179
+ window.open(destinationUrl, target);
157
180
  }
158
- fireLinkClickedEvent(createAnalyticsEvent)(event, {
181
+
182
+ // Only set clickOutcome when this handler actually opened the link.
183
+ // If a parent onClick handled navigation, fire a generic click event instead.
184
+ fireLinkClickedEvent(createAnalyticsEvent)(event, doOpenLink ? {
159
185
  attributes: {
160
186
  clickOutcome: target === '_blank' ? 'clickThroughNewTabOrWindow' : 'clickThrough'
161
187
  }
162
- });
188
+ } : undefined);
163
189
  } else {
164
- if (onClick) {
165
- onClick(event);
190
+ // If preview panel is available and the user clicked on the link,
191
+ // delegate the click to the preview panel handler
192
+ if (!isModifierKeyPressed && ari && name && openPreviewPanel && isPreviewPanelAvailable !== null && isPreviewPanelAvailable !== void 0 && isPreviewPanelAvailable({
193
+ ari
194
+ }) && !isDisablePreviewPanel) {
195
+ var _extractSmartLinkEmbe2;
196
+ event.preventDefault();
197
+ event.stopPropagation();
198
+ openPreviewPanel({
199
+ url,
200
+ ari,
201
+ name,
202
+ iconUrl: getObjectIconUrl(state.details),
203
+ panelData: {
204
+ embedUrl: expValEquals('platform_hover_card_preview_panel', 'cohort', 'test') ? (_extractSmartLinkEmbe2 = extractSmartLinkEmbed(state.details)) === null || _extractSmartLinkEmbe2 === void 0 ? void 0 : _extractSmartLinkEmbe2.src : undefined
205
+ }
206
+ });
207
+ fireLinkClickedEvent(createAnalyticsEvent)(event, {
208
+ attributes: {
209
+ clickOutcome: 'previewPanel'
210
+ }
211
+ });
212
+ return;
213
+ } else if (!onClick && !isFlexibleUi) {
214
+ const clickUrl = getClickUrl(url, state.details);
215
+
216
+ // Ctrl+left click on mac typically doesn't trigger onClick
217
+ // The event could have potentially had `e.preventDefault()` called on it by now
218
+ // event by smart card internally
219
+ // If it has been called then only then can `isSpecialEvent` be true.
220
+ const target = isSpecialEvent(event) ? '_blank' : '_self';
221
+ window.open(clickUrl, target);
222
+ fireLinkClickedEvent(createAnalyticsEvent)(event, {
223
+ attributes: {
224
+ clickOutcome: target === '_blank' ? 'clickThroughNewTabOrWindow' : 'clickThrough'
225
+ }
226
+ });
227
+ } else {
228
+ if (onClick) {
229
+ onClick(event);
230
+ }
231
+ fireLinkClickedEvent(createAnalyticsEvent)(event);
166
232
  }
167
- fireLinkClickedEvent(createAnalyticsEvent)(event);
168
233
  }
169
- }, [fireEvent, id, isFlexibleUi, appearance, definitionId, onClick, url, wrapUrlWithCrossProductAnalytics, state.details, ari, name, fire3PClickEvent, shouldFire3PClickEvent, isPreviewPanelAvailable, openPreviewPanel, createAnalyticsEvent, disablePreviewPanel]);
234
+ }, [fireEvent, id, isFlexibleUi, appearance, definitionId, onClick, url, appendCrossProductAnalyticsParams, state.details, ari, name, fire3PClickEvent, shouldFire3PClickEvent, isPreviewPanelAvailable, openPreviewPanel, createAnalyticsEvent, disablePreviewPanel]);
170
235
 
171
236
  // Exposure fires once per eligible mount; click-time reads use no-exposure variant.
172
237
  useEffect(() => {
@@ -11,10 +11,12 @@ import { CardDisplay, SmartLinkPosition, SmartLinkSize } from '../../../constant
11
11
  import extractRovoChatAction from '../../../extractors/flexible/actions/extract-rovo-chat-action';
12
12
  import { getDefinitionId, getExtensionKey, getServices } from '../../../state/helpers';
13
13
  import useRovoConfig from '../../../state/hooks/use-rovo-config';
14
+ import { useSmartLinkCrossProductUrlWrapperGated } from '../../../state/hooks/use-smart-link-cross-product-url-wrapper';
14
15
  import { useSmartCardState } from '../../../state/store';
15
16
  import { isSpecialEvent } from '../../../utils';
16
17
  import { getIsAISummaryEnabled } from '../../../utils/ai-summary';
17
18
  import { fireLinkClickedEvent } from '../../../utils/analytics/click';
19
+ import { getAnchorAttributesFromEvent } from '../../../utils/click-helpers';
18
20
  import { flexibleUiOptions } from '../styled';
19
21
  import { getMetadata } from '../utils';
20
22
  import ContentContainer from './ContentContainer';
@@ -86,6 +88,9 @@ const HoverCardContent = ({
86
88
  } = useSmartLinkContext();
87
89
  const isAISummaryEnabled = getIsAISummaryEnabled(isAdminHubAIEnabled, cardState.details);
88
90
  const services = getServices(linkState.details);
91
+ const appendCrossProductAnalyticsParams = useSmartLinkCrossProductUrlWrapperGated({
92
+ details: linkState.details
93
+ });
89
94
  const statusRef = useRef(linkStatus);
90
95
  const fireEventRef = useRef(fireEvent);
91
96
  const definitionIdRef = useRef(definitionId);
@@ -127,6 +132,24 @@ const HoverCardContent = ({
127
132
  };
128
133
  }, []);
129
134
  const onClick = useCallback(event => {
135
+ if (fg('platform_smartlink_xpc_url_wrapping')) {
136
+ // Prevent the anchor's native navigation so we can open the destination URL
137
+ // with cross-product analytics query parameters appended.
138
+ // The href is read from the anchor element at click time rather than at render
139
+ // time because the URL for the anchor may be different from original URL.
140
+ // The component may use the URL from the resolved response which can be a redirect URL
141
+ // or other preferred URL based on link extractor.
142
+ // The cross-product params are client-only and cannot be rendered
143
+ // server-side. Falls back to the original `url` prop if the event target is
144
+ // not an anchor element.
145
+ event.preventDefault();
146
+ const {
147
+ href = url,
148
+ target
149
+ } = getAnchorAttributesFromEvent(event);
150
+ const destinationUrl = appendCrossProductAnalyticsParams(href);
151
+ window.open(destinationUrl, target);
152
+ }
130
153
  const isModifierKeyPressed = isSpecialEvent(event);
131
154
  fireEvent('ui.smartLink.clicked.titleGoToLink', {
132
155
  id,
@@ -135,7 +158,7 @@ const HoverCardContent = ({
135
158
  definitionId: definitionId !== null && definitionId !== void 0 ? definitionId : null
136
159
  });
137
160
  fireLinkClickedEvent(createAnalyticsEvent)(event);
138
- }, [createAnalyticsEvent, id, fireEvent, definitionId]);
161
+ }, [createAnalyticsEvent, appendCrossProductAnalyticsParams, id, fireEvent, definitionId, url]);
139
162
  const data = (_cardState$details = cardState.details) === null || _cardState$details === void 0 ? void 0 : _cardState$details.data;
140
163
  const {
141
164
  subtitle
@@ -9,7 +9,7 @@ import LinkWarningModal from './LinkWarningModal';
9
9
  import { useLinkWarningModal } from './LinkWarningModal/hooks/use-link-warning-modal';
10
10
  const PACKAGE_DATA = {
11
11
  packageName: "@atlaskit/smart-card",
12
- packageVersion: "44.23.1",
12
+ packageVersion: "44.23.2",
13
13
  componentName: 'linkUrl'
14
14
  };
15
15
  const LinkUrl = ({
@@ -4,7 +4,7 @@ export var ANALYTICS_CHANNEL = 'media';
4
4
  export var context = {
5
5
  componentName: 'smart-cards',
6
6
  packageName: "@atlaskit/smart-card" || '',
7
- packageVersion: "44.23.1" || ''
7
+ packageVersion: "44.23.2" || ''
8
8
  };
9
9
  export var TrackQuickActionType = /*#__PURE__*/function (TrackQuickActionType) {
10
10
  TrackQuickActionType["StatusUpdate"] = "StatusUpdate";
@@ -5,4 +5,33 @@
5
5
  */
6
6
  export var isAuxClick = function isAuxClick(e) {
7
7
  return e.button === 1;
8
+ };
9
+
10
+ /**
11
+ * Extracts `href` and `target` from the anchor element that is the event's `currentTarget`.
12
+ *
13
+ * Smart Link click handlers are attached to multiple card renderers (InlineCard, BlockCard,
14
+ * EmbedCard, FlexibleCard). When the handler needs to manually open a link — for example,
15
+ * when native anchor navigation has been prevented — it uses this helper to read the
16
+ * anchor's resolved URL and intended target from the event rather than re-deriving them.
17
+ *
18
+ * Returns `{ href: undefined, target: '_self' }` when `currentTarget` is not an anchor
19
+ * element (e.g. a button or wrapper div), so callers can safely fall back to a default target.
20
+ *
21
+ * @param event - A React mouse or keyboard event whose `currentTarget` may be an anchor.
22
+ * @returns The resolved absolute `href` and `target` attribute of the anchor, or safe
23
+ * defaults if `currentTarget` is not an anchor.
24
+ */
25
+ export var getAnchorAttributesFromEvent = function getAnchorAttributesFromEvent(event) {
26
+ var currentTarget = event.currentTarget;
27
+ if (!(currentTarget instanceof HTMLAnchorElement)) {
28
+ return {
29
+ href: undefined,
30
+ target: '_self'
31
+ };
32
+ }
33
+ return {
34
+ href: currentTarget.href,
35
+ target: currentTarget.target || '_self'
36
+ };
8
37
  };
@@ -19,7 +19,7 @@ import { isSpecialClick, isSpecialEvent, isSpecialKey } from '../../utils';
19
19
  import { combineActionOptions } from '../../utils/actions/combine-action-options';
20
20
  import { fireLinkClickedEvent } from '../../utils/analytics/click';
21
21
  import { SmartLinkAnalyticsContext } from '../../utils/analytics/SmartLinkAnalyticsContext';
22
- import { isAuxClick } from '../../utils/click-helpers';
22
+ import { getAnchorAttributesFromEvent, isAuxClick } from '../../utils/click-helpers';
23
23
  import { isFlexibleUiCard } from '../../utils/flexible';
24
24
  import * as measure from '../../utils/performance';
25
25
  import { BlockCard } from '../BlockCard';
@@ -84,7 +84,7 @@ function Component(_ref) {
84
84
  var services = getServices(state.details);
85
85
  var thirdPartyARI = getThirdPartyARI(state.details);
86
86
  var firstPartyIdentifier = getFirstPartyIdentifier();
87
- var wrapUrlWithCrossProductAnalytics = useSmartLinkCrossProductUrlWrapperGated({
87
+ var appendCrossProductAnalyticsParams = useSmartLinkCrossProductUrlWrapperGated({
88
88
  details: state.details
89
89
  });
90
90
  var actionOptions = combineActionOptions({
@@ -118,56 +118,120 @@ function Component(_ref) {
118
118
  var isDisablePreviewPanel = disablePreviewPanel && editorExperiment('platform_editor_preview_panel_linking_exp', true, {
119
119
  exposure: true
120
120
  });
121
+ if (fg('platform_smartlink_xpc_url_wrapping')) {
122
+ var _appendCrossProductAn;
123
+ // FIXME: InlineCard, BlockCard and EmbedCard call event.preventDefault() internally
124
+ // before the event bubbles up to this handler. This forces us to snapshot
125
+ // event.defaultPrevented before calling onClick to detect whether the consumer
126
+ // specifically prevented navigation. Ideally those components should not call
127
+ // preventDefault so this workaround can be removed.
128
+ var isEventDefaultPrevented = event.defaultPrevented;
129
+ var canOpenPreviewPanel = !isModifierKeyPressed && ari && name && openPreviewPanel && (isPreviewPanelAvailable === null || isPreviewPanelAvailable === void 0 ? void 0 : isPreviewPanelAvailable({
130
+ ari: ari
131
+ })) && !isDisablePreviewPanel;
121
132
 
122
- // If preview panel is available and the user clicked on the link,
123
- // delegate the click to the preview panel handler
124
- if (!isModifierKeyPressed && ari && name && openPreviewPanel && isPreviewPanelAvailable !== null && isPreviewPanelAvailable !== void 0 && isPreviewPanelAvailable({
125
- ari: ari
126
- }) && !isDisablePreviewPanel) {
127
- var _extractSmartLinkEmbe;
128
- event.preventDefault();
129
- event.stopPropagation();
130
- openPreviewPanel({
131
- url: url,
132
- ari: ari,
133
- name: name,
134
- iconUrl: getObjectIconUrl(state.details),
135
- panelData: {
136
- embedUrl: expValEquals('platform_hover_card_preview_panel', 'cohort', 'test') ? (_extractSmartLinkEmbe = extractSmartLinkEmbed(state.details)) === null || _extractSmartLinkEmbe === void 0 ? void 0 : _extractSmartLinkEmbe.src : undefined
137
- }
138
- });
139
- fireLinkClickedEvent(createAnalyticsEvent)(event, {
140
- attributes: {
141
- clickOutcome: 'previewPanel'
142
- }
143
- });
144
- return;
145
- } else if (!onClick && !isFlexibleUi) {
146
- var clickUrl = getClickUrl(url, state.details);
133
+ // Preview panel takes priority over link navigation when available.
134
+ if (canOpenPreviewPanel) {
135
+ var _extractSmartLinkEmbe;
136
+ event.preventDefault();
137
+ event.stopPropagation();
138
+ openPreviewPanel({
139
+ url: url,
140
+ ari: ari,
141
+ name: name,
142
+ iconUrl: getObjectIconUrl(state.details),
143
+ panelData: {
144
+ embedUrl: expValEquals('platform_hover_card_preview_panel', 'cohort', 'test') ? (_extractSmartLinkEmbe = extractSmartLinkEmbed(state.details)) === null || _extractSmartLinkEmbe === void 0 ? void 0 : _extractSmartLinkEmbe.src : undefined
145
+ }
146
+ });
147
+ fireLinkClickedEvent(createAnalyticsEvent)(event, {
148
+ attributes: {
149
+ clickOutcome: 'previewPanel'
150
+ }
151
+ });
152
+ return;
153
+ }
147
154
 
148
- // Ctrl+left click on mac typically doesn't trigger onClick
149
- // The event could have potentially had `e.preventDefault()` called on it by now
150
- // event by smart card internally
151
- // If it has been called then only then can `isSpecialEvent` be true.
152
- var target = isSpecialEvent(event) ? '_blank' : '_self';
153
- if (fg('platform_smartlink_xpc_url_wrapping')) {
154
- var wrappedUrl = wrapUrlWithCrossProductAnalytics(clickUrl);
155
- window.open(wrappedUrl, target);
156
- } else {
157
- window.open(clickUrl, target);
155
+ // For FlexibleCard, read target from the clicked anchor element (e.g. _blank for links
156
+ // rendered with explicit target). For classic cards, default to _self
157
+ var _getAnchorAttributesF = getAnchorAttributesFromEvent(event),
158
+ anchorTarget = _getAnchorAttributesF.target;
159
+ var target = isSpecialEvent(event) ? '_blank' : isFlexibleUi ? anchorTarget : '_self';
160
+
161
+ // FIXME: preferredUrl should be rendered in the DOM anchor href instead of derived at click time
162
+ var preferredUrl = getClickUrl(url, state.details);
163
+ var destinationUrl = (_appendCrossProductAn = appendCrossProductAnalyticsParams(preferredUrl)) !== null && _appendCrossProductAn !== void 0 ? _appendCrossProductAn : preferredUrl;
164
+
165
+ // FIXME: Consumer that handle click even themselves via callback won't have the decorated URL
166
+ onClick === null || onClick === void 0 || onClick(event);
167
+
168
+ // Check if the event is prevented via onClick callback
169
+ var consumerPreventedNavigation = event.defaultPrevented && !isEventDefaultPrevented;
170
+
171
+ // Classic cards (InlineCard, BlockCard, EmbedCard) rely on their own anchor navigation
172
+ // when onClick is provided, so this handler should not open the link for them.
173
+ // FlexibleCard's anchor is prevented from native navigation, so this handler always
174
+ // opens the link for FlexibleCard unless the consumer's onClick called preventDefault.
175
+ var shouldOpenLink = isFlexibleUi || !onClick;
176
+ var doOpenLink = shouldOpenLink && !consumerPreventedNavigation;
177
+ if (doOpenLink) {
178
+ event.preventDefault();
179
+ window.open(destinationUrl, target);
158
180
  }
159
- fireLinkClickedEvent(createAnalyticsEvent)(event, {
181
+
182
+ // Only set clickOutcome when this handler actually opened the link.
183
+ // If a parent onClick handled navigation, fire a generic click event instead.
184
+ fireLinkClickedEvent(createAnalyticsEvent)(event, doOpenLink ? {
160
185
  attributes: {
161
186
  clickOutcome: target === '_blank' ? 'clickThroughNewTabOrWindow' : 'clickThrough'
162
187
  }
163
- });
188
+ } : undefined);
164
189
  } else {
165
- if (onClick) {
166
- onClick(event);
190
+ // If preview panel is available and the user clicked on the link,
191
+ // delegate the click to the preview panel handler
192
+ if (!isModifierKeyPressed && ari && name && openPreviewPanel && isPreviewPanelAvailable !== null && isPreviewPanelAvailable !== void 0 && isPreviewPanelAvailable({
193
+ ari: ari
194
+ }) && !isDisablePreviewPanel) {
195
+ var _extractSmartLinkEmbe2;
196
+ event.preventDefault();
197
+ event.stopPropagation();
198
+ openPreviewPanel({
199
+ url: url,
200
+ ari: ari,
201
+ name: name,
202
+ iconUrl: getObjectIconUrl(state.details),
203
+ panelData: {
204
+ embedUrl: expValEquals('platform_hover_card_preview_panel', 'cohort', 'test') ? (_extractSmartLinkEmbe2 = extractSmartLinkEmbed(state.details)) === null || _extractSmartLinkEmbe2 === void 0 ? void 0 : _extractSmartLinkEmbe2.src : undefined
205
+ }
206
+ });
207
+ fireLinkClickedEvent(createAnalyticsEvent)(event, {
208
+ attributes: {
209
+ clickOutcome: 'previewPanel'
210
+ }
211
+ });
212
+ return;
213
+ } else if (!onClick && !isFlexibleUi) {
214
+ var clickUrl = getClickUrl(url, state.details);
215
+
216
+ // Ctrl+left click on mac typically doesn't trigger onClick
217
+ // The event could have potentially had `e.preventDefault()` called on it by now
218
+ // event by smart card internally
219
+ // If it has been called then only then can `isSpecialEvent` be true.
220
+ var _target = isSpecialEvent(event) ? '_blank' : '_self';
221
+ window.open(clickUrl, _target);
222
+ fireLinkClickedEvent(createAnalyticsEvent)(event, {
223
+ attributes: {
224
+ clickOutcome: _target === '_blank' ? 'clickThroughNewTabOrWindow' : 'clickThrough'
225
+ }
226
+ });
227
+ } else {
228
+ if (onClick) {
229
+ onClick(event);
230
+ }
231
+ fireLinkClickedEvent(createAnalyticsEvent)(event);
167
232
  }
168
- fireLinkClickedEvent(createAnalyticsEvent)(event);
169
233
  }
170
- }, [fireEvent, id, isFlexibleUi, appearance, definitionId, onClick, url, wrapUrlWithCrossProductAnalytics, state.details, ari, name, fire3PClickEvent, shouldFire3PClickEvent, isPreviewPanelAvailable, openPreviewPanel, createAnalyticsEvent, disablePreviewPanel]);
234
+ }, [fireEvent, id, isFlexibleUi, appearance, definitionId, onClick, url, appendCrossProductAnalyticsParams, state.details, ari, name, fire3PClickEvent, shouldFire3PClickEvent, isPreviewPanelAvailable, openPreviewPanel, createAnalyticsEvent, disablePreviewPanel]);
171
235
 
172
236
  // Exposure fires once per eligible mount; click-time reads use no-exposure variant.
173
237
  useEffect(function () {
@@ -14,10 +14,12 @@ import { CardDisplay, SmartLinkPosition, SmartLinkSize } from '../../../constant
14
14
  import extractRovoChatAction from '../../../extractors/flexible/actions/extract-rovo-chat-action';
15
15
  import { getDefinitionId, getExtensionKey, getServices } from '../../../state/helpers';
16
16
  import useRovoConfig from '../../../state/hooks/use-rovo-config';
17
+ import { useSmartLinkCrossProductUrlWrapperGated } from '../../../state/hooks/use-smart-link-cross-product-url-wrapper';
17
18
  import { useSmartCardState } from '../../../state/store';
18
19
  import { isSpecialEvent } from '../../../utils';
19
20
  import { getIsAISummaryEnabled } from '../../../utils/ai-summary';
20
21
  import { fireLinkClickedEvent } from '../../../utils/analytics/click';
22
+ import { getAnchorAttributesFromEvent } from '../../../utils/click-helpers';
21
23
  import { flexibleUiOptions } from '../styled';
22
24
  import { getMetadata } from '../utils';
23
25
  import ContentContainer from './ContentContainer';
@@ -86,6 +88,9 @@ var HoverCardContent = function HoverCardContent(_ref4) {
86
88
  product = _useSmartLinkContext.product;
87
89
  var isAISummaryEnabled = getIsAISummaryEnabled(isAdminHubAIEnabled, cardState.details);
88
90
  var services = getServices(linkState.details);
91
+ var appendCrossProductAnalyticsParams = useSmartLinkCrossProductUrlWrapperGated({
92
+ details: linkState.details
93
+ });
89
94
  var statusRef = useRef(linkStatus);
90
95
  var fireEventRef = useRef(fireEvent);
91
96
  var definitionIdRef = useRef(definitionId);
@@ -127,6 +132,24 @@ var HoverCardContent = function HoverCardContent(_ref4) {
127
132
  };
128
133
  }, []);
129
134
  var onClick = useCallback(function (event) {
135
+ if (fg('platform_smartlink_xpc_url_wrapping')) {
136
+ // Prevent the anchor's native navigation so we can open the destination URL
137
+ // with cross-product analytics query parameters appended.
138
+ // The href is read from the anchor element at click time rather than at render
139
+ // time because the URL for the anchor may be different from original URL.
140
+ // The component may use the URL from the resolved response which can be a redirect URL
141
+ // or other preferred URL based on link extractor.
142
+ // The cross-product params are client-only and cannot be rendered
143
+ // server-side. Falls back to the original `url` prop if the event target is
144
+ // not an anchor element.
145
+ event.preventDefault();
146
+ var _getAnchorAttributesF = getAnchorAttributesFromEvent(event),
147
+ _getAnchorAttributesF2 = _getAnchorAttributesF.href,
148
+ href = _getAnchorAttributesF2 === void 0 ? url : _getAnchorAttributesF2,
149
+ target = _getAnchorAttributesF.target;
150
+ var destinationUrl = appendCrossProductAnalyticsParams(href);
151
+ window.open(destinationUrl, target);
152
+ }
130
153
  var isModifierKeyPressed = isSpecialEvent(event);
131
154
  fireEvent('ui.smartLink.clicked.titleGoToLink', {
132
155
  id: id,
@@ -135,7 +158,7 @@ var HoverCardContent = function HoverCardContent(_ref4) {
135
158
  definitionId: definitionId !== null && definitionId !== void 0 ? definitionId : null
136
159
  });
137
160
  fireLinkClickedEvent(createAnalyticsEvent)(event);
138
- }, [createAnalyticsEvent, id, fireEvent, definitionId]);
161
+ }, [createAnalyticsEvent, appendCrossProductAnalyticsParams, id, fireEvent, definitionId, url]);
139
162
  var data = (_cardState$details = cardState.details) === null || _cardState$details === void 0 ? void 0 : _cardState$details.data;
140
163
  var _getMetadata = getMetadata(extensionKey, data),
141
164
  subtitle = _getMetadata.subtitle;
@@ -12,7 +12,7 @@ import LinkWarningModal from './LinkWarningModal';
12
12
  import { useLinkWarningModal } from './LinkWarningModal/hooks/use-link-warning-modal';
13
13
  var PACKAGE_DATA = {
14
14
  packageName: "@atlaskit/smart-card",
15
- packageVersion: "44.23.1",
15
+ packageVersion: "44.23.2",
16
16
  componentName: 'linkUrl'
17
17
  };
18
18
  var LinkUrl = function LinkUrl(_ref) {
@@ -1,3 +1,4 @@
1
+ import { type KeyboardEvent, type MouseEvent } from 'react';
1
2
  /**
2
3
  * Returns true for genuine middle-clicks (button === 1).
3
4
  * Filters out Windows right-clicks, which fire onAuxClick with button === 2
@@ -6,3 +7,22 @@
6
7
  export declare const isAuxClick: (e: {
7
8
  button: number;
8
9
  }) => boolean;
10
+ /**
11
+ * Extracts `href` and `target` from the anchor element that is the event's `currentTarget`.
12
+ *
13
+ * Smart Link click handlers are attached to multiple card renderers (InlineCard, BlockCard,
14
+ * EmbedCard, FlexibleCard). When the handler needs to manually open a link — for example,
15
+ * when native anchor navigation has been prevented — it uses this helper to read the
16
+ * anchor's resolved URL and intended target from the event rather than re-deriving them.
17
+ *
18
+ * Returns `{ href: undefined, target: '_self' }` when `currentTarget` is not an anchor
19
+ * element (e.g. a button or wrapper div), so callers can safely fall back to a default target.
20
+ *
21
+ * @param event - A React mouse or keyboard event whose `currentTarget` may be an anchor.
22
+ * @returns The resolved absolute `href` and `target` attribute of the anchor, or safe
23
+ * defaults if `currentTarget` is not an anchor.
24
+ */
25
+ export declare const getAnchorAttributesFromEvent: (event: MouseEvent | KeyboardEvent) => {
26
+ href?: string;
27
+ target: string;
28
+ };
@@ -1,3 +1,4 @@
1
+ import { type KeyboardEvent, type MouseEvent } from 'react';
1
2
  /**
2
3
  * Returns true for genuine middle-clicks (button === 1).
3
4
  * Filters out Windows right-clicks, which fire onAuxClick with button === 2
@@ -6,3 +7,22 @@
6
7
  export declare const isAuxClick: (e: {
7
8
  button: number;
8
9
  }) => boolean;
10
+ /**
11
+ * Extracts `href` and `target` from the anchor element that is the event's `currentTarget`.
12
+ *
13
+ * Smart Link click handlers are attached to multiple card renderers (InlineCard, BlockCard,
14
+ * EmbedCard, FlexibleCard). When the handler needs to manually open a link — for example,
15
+ * when native anchor navigation has been prevented — it uses this helper to read the
16
+ * anchor's resolved URL and intended target from the event rather than re-deriving them.
17
+ *
18
+ * Returns `{ href: undefined, target: '_self' }` when `currentTarget` is not an anchor
19
+ * element (e.g. a button or wrapper div), so callers can safely fall back to a default target.
20
+ *
21
+ * @param event - A React mouse or keyboard event whose `currentTarget` may be an anchor.
22
+ * @returns The resolved absolute `href` and `target` attribute of the anchor, or safe
23
+ * defaults if `currentTarget` is not an anchor.
24
+ */
25
+ export declare const getAnchorAttributesFromEvent: (event: MouseEvent | KeyboardEvent) => {
26
+ href?: string;
27
+ target: string;
28
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/smart-card",
3
- "version": "44.23.2",
3
+ "version": "44.23.3",
4
4
  "description": "Smart card component",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"