@atlaskit/smart-card 44.3.1 → 44.3.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/analytics.spec.yaml +9 -0
  3. package/dist/cjs/__tests__/vr-tests/__snapshots__/hover-card/hover-card-with-generic-3p-rovo-chat-action--default.png +0 -0
  4. package/dist/cjs/messages.js +0 -10
  5. package/dist/cjs/ssr.js +28 -1
  6. package/dist/cjs/state/hooks/use-intersection-observer/index.js +49 -0
  7. package/dist/cjs/view/CardWithUrl/card-error-boundary/index.js +28 -0
  8. package/dist/cjs/view/CardWithUrl/card-intersection-observer/index.js +46 -0
  9. package/dist/cjs/view/CardWithUrl/card-loader-wrapper/index.js +23 -0
  10. package/dist/cjs/view/CardWithUrl/component-lazy/LazyIntersectionObserverCard.js +43 -3
  11. package/dist/cjs/view/CardWithUrl/component.js +34 -2
  12. package/dist/cjs/view/FlexibleCard/components/actions/rovo-chat-action/index.js +3 -2
  13. package/dist/es2019/__tests__/vr-tests/__snapshots__/hover-card/hover-card-with-generic-3p-rovo-chat-action--default.png +0 -0
  14. package/dist/es2019/messages.js +0 -10
  15. package/dist/es2019/ssr.js +28 -2
  16. package/dist/es2019/state/hooks/use-intersection-observer/index.js +41 -0
  17. package/dist/es2019/view/CardWithUrl/card-error-boundary/index.js +20 -0
  18. package/dist/es2019/view/CardWithUrl/card-intersection-observer/index.js +25 -0
  19. package/dist/es2019/view/CardWithUrl/card-loader-wrapper/index.js +16 -0
  20. package/dist/es2019/view/CardWithUrl/component-lazy/LazyIntersectionObserverCard.js +39 -2
  21. package/dist/es2019/view/CardWithUrl/component.js +34 -2
  22. package/dist/es2019/view/FlexibleCard/components/actions/rovo-chat-action/index.js +3 -2
  23. package/dist/esm/__tests__/vr-tests/__snapshots__/hover-card/hover-card-with-generic-3p-rovo-chat-action--default.png +0 -0
  24. package/dist/esm/messages.js +0 -10
  25. package/dist/esm/ssr.js +29 -2
  26. package/dist/esm/state/hooks/use-intersection-observer/index.js +44 -0
  27. package/dist/esm/view/CardWithUrl/card-error-boundary/index.js +21 -0
  28. package/dist/esm/view/CardWithUrl/card-intersection-observer/index.js +37 -0
  29. package/dist/esm/view/CardWithUrl/card-loader-wrapper/index.js +15 -0
  30. package/dist/esm/view/CardWithUrl/component-lazy/LazyIntersectionObserverCard.js +42 -2
  31. package/dist/esm/view/CardWithUrl/component.js +34 -2
  32. package/dist/esm/view/FlexibleCard/components/actions/rovo-chat-action/index.js +3 -2
  33. package/dist/types/common/analytics/generated/analytics.types.d.ts +7 -1
  34. package/dist/types/messages.d.ts +1 -1
  35. package/dist/types/state/hooks/use-intersection-observer/index.d.ts +6 -0
  36. package/dist/types/view/CardWithUrl/card-error-boundary/index.d.ts +5 -0
  37. package/dist/types/view/CardWithUrl/card-intersection-observer/index.d.ts +7 -0
  38. package/dist/types/view/CardWithUrl/card-loader-wrapper/index.d.ts +7 -0
  39. package/dist/types/view/CardWithUrl/component-lazy/LazyIntersectionObserverCard.d.ts +1 -1
  40. package/dist/types/view/CardWithUrl/component.d.ts +1 -0
  41. package/dist/types/view/CardWithUrl/types.d.ts +1 -0
  42. package/dist/types-ts4.5/common/analytics/generated/analytics.types.d.ts +7 -1
  43. package/dist/types-ts4.5/messages.d.ts +1 -1
  44. package/dist/types-ts4.5/state/hooks/use-intersection-observer/index.d.ts +6 -0
  45. package/dist/types-ts4.5/view/CardWithUrl/card-error-boundary/index.d.ts +5 -0
  46. package/dist/types-ts4.5/view/CardWithUrl/card-intersection-observer/index.d.ts +7 -0
  47. package/dist/types-ts4.5/view/CardWithUrl/card-loader-wrapper/index.d.ts +7 -0
  48. package/dist/types-ts4.5/view/CardWithUrl/component-lazy/LazyIntersectionObserverCard.d.ts +1 -1
  49. package/dist/types-ts4.5/view/CardWithUrl/component.d.ts +1 -0
  50. package/dist/types-ts4.5/view/CardWithUrl/types.d.ts +1 -0
  51. package/package.json +7 -4
@@ -1,7 +1,11 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
1
2
  import React, { useCallback, useEffect, useRef, useState } from 'react';
3
+ import { componentWithFG } from '@atlaskit/platform-feature-flags-react';
2
4
  import { usePrefetch } from '../../../state';
3
5
  import { startUfoExperience } from '../../../state/analytics/ufoExperiences';
6
+ import useIntersectionObserver from '../../../state/hooks/use-intersection-observer';
4
7
  import { shouldSample } from '../../../utils/shouldSample';
8
+ import CardLoaderWrapper from '../card-loader-wrapper';
5
9
  import { CardWithUrlContent } from '../component';
6
10
  import { LoadingCardLink } from './LoadingCardLink';
7
11
 
@@ -10,7 +14,7 @@ import { LoadingCardLink } from './LoadingCardLink';
10
14
  // being compared to. Since the default container is the `document`, we set this
11
15
  // up to check once a Smart Link is within `X` px from the bottom of the viewport.
12
16
  const ROOT_MARGIN_VERTICAL = '360px';
13
- export function LazyIntersectionObserverCard(props) {
17
+ function LazyIntersectionObserverCardOld(props) {
14
18
  const ref = useRef(null);
15
19
  const [isIntersecting, setIsIntersecting] = useState(false);
16
20
  const [shouldSendRenderedUFOEvent] = useState(shouldSample());
@@ -52,4 +56,37 @@ export function LazyIntersectionObserverCard(props) {
52
56
  return /*#__PURE__*/React.createElement(Component, {
53
57
  className: "loader-wrapper"
54
58
  }, content);
55
- }
59
+ }
60
+ const LazyIntersectionObserverCardNew = props => {
61
+ const [isIntersected, setIsIntersected] = useState(false);
62
+ const [shouldSendRenderedUFOEvent] = useState(shouldSample());
63
+ const {
64
+ appearance,
65
+ url,
66
+ id
67
+ } = props;
68
+ const prefetch = usePrefetch(url);
69
+ const ComponentObserver = appearance === 'inline' ? 'span' : 'div';
70
+ const onIntersection = useCallback(isIntersecting => {
71
+ if (isIntersecting) {
72
+ if (shouldSendRenderedUFOEvent) {
73
+ startUfoExperience('smart-link-rendered', id);
74
+ }
75
+ setIsIntersected(true);
76
+ } else {
77
+ prefetch();
78
+ }
79
+ }, [id, prefetch, shouldSendRenderedUFOEvent]);
80
+ const ref = useIntersectionObserver({
81
+ onIntersection
82
+ });
83
+ const content = isIntersected ? /*#__PURE__*/React.createElement(CardWithUrlContent, _extends({}, props, {
84
+ isIntersected: true
85
+ })) : /*#__PURE__*/React.createElement(ComponentObserver, {
86
+ ref: ref
87
+ }, /*#__PURE__*/React.createElement(LoadingCardLink, props));
88
+ return /*#__PURE__*/React.createElement(CardLoaderWrapper, {
89
+ appearance: appearance
90
+ }, content);
91
+ };
92
+ export const LazyIntersectionObserverCard = componentWithFG('platform_sl_event_ui_seen', LazyIntersectionObserverCardNew, LazyIntersectionObserverCardOld);
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect, useMemo } from 'react';
1
+ import React, { useCallback, useEffect, useMemo, useRef } from 'react';
2
2
  import { useAnalyticsEvents as useAnalyticsEventsNext } from '@atlaskit/analytics-next';
3
3
  import { extractSmartLinkEmbed } from '@atlaskit/link-extractors';
4
4
  import { fg } from '@atlaskit/platform-feature-flags';
@@ -22,6 +22,7 @@ import { EmbedCard, EmbedCardUpdated } from '../EmbedCard';
22
22
  import FlexibleCard from '../FlexibleCard';
23
23
  import { InlineCard } from '../InlineCard';
24
24
  import { useFire3PWorkflowsClickEvent } from '../SmartLinkEvents/useSmartLinkEvents';
25
+ import withCardIntersectionObserver from './card-intersection-observer';
25
26
  const thirdPartyARIPrefix = 'ari:third-party';
26
27
  const EmbedCardComponent = componentWithFG('rovo_chat_embed_card_dwell_and_hover_metrics', EmbedCardUpdated, EmbedCard);
27
28
  function Component({
@@ -29,6 +30,7 @@ function Component({
29
30
  url,
30
31
  isSelected,
31
32
  isHovered,
33
+ isIntersected,
32
34
  frameStyle,
33
35
  platform,
34
36
  onClick,
@@ -59,6 +61,7 @@ function Component({
59
61
  const {
60
62
  fireEvent
61
63
  } = useAnalyticsEvents();
64
+ const hasFiredSeenRef = useRef(false);
62
65
  let isFlexibleUi = useMemo(() => isFlexibleUiCard(children, ui), [children, ui]);
63
66
 
64
67
  // Get state, actions for this card.
@@ -207,6 +210,19 @@ function Component({
207
210
  });
208
211
  }
209
212
  }, [appearance, extensionKey, fireEvent, id, isFlexibleUi, state.status]);
213
+
214
+ // Fire smartLink seen event once on a successful render card is in the viewport
215
+ useEffect(() => {
216
+ if (fg('platform_sl_event_ui_seen')) {
217
+ if (!isFinalState(state.status)) return;
218
+ if (isIntersected && !hasFiredSeenRef.current) {
219
+ fireEvent('ui.smartLink.seen', {
220
+ display: appearance
221
+ });
222
+ hasFiredSeenRef.current = true;
223
+ }
224
+ }
225
+ }, [state.status, isIntersected, fireEvent, appearance]);
210
226
  const onIframeDwell = useCallback((dwellTime, dwellPercentVisible) => {
211
227
  fireEvent('ui.smartLinkIframe.dwelled', {
212
228
  id,
@@ -329,6 +345,7 @@ function ComponentUpdated({
329
345
  url,
330
346
  isSelected,
331
347
  isHovered,
348
+ isIntersected,
332
349
  frameStyle,
333
350
  platform,
334
351
  onClick,
@@ -359,6 +376,7 @@ function ComponentUpdated({
359
376
  const {
360
377
  fireEvent
361
378
  } = useAnalyticsEvents();
379
+ const hasFiredSeenRef = useRef(false);
362
380
  let isFlexibleUi = useMemo(() => isFlexibleUiCard(children, ui), [children, ui]);
363
381
 
364
382
  // Get state, actions for this card.
@@ -507,6 +525,19 @@ function ComponentUpdated({
507
525
  });
508
526
  }
509
527
  }, [appearance, extensionKey, fireEvent, id, isFlexibleUi, state.status]);
528
+
529
+ // Fire smartLink seen event once on a successful render card is in the viewport
530
+ useEffect(() => {
531
+ if (fg('platform_sl_event_ui_seen')) {
532
+ if (!isFinalState(state.status)) return;
533
+ if (isIntersected && !hasFiredSeenRef.current) {
534
+ fireEvent('ui.smartLink.seen', {
535
+ display: appearance
536
+ });
537
+ hasFiredSeenRef.current = true;
538
+ }
539
+ }
540
+ }, [state.status, isIntersected, fireEvent, appearance]);
510
541
  const onIframeDwell = useCallback((dwellTime, dwellPercentVisible) => {
511
542
  fireEvent('ui.smartLinkIframe.dwelled', {
512
543
  id,
@@ -651,4 +682,5 @@ export const CardWithUrlContent = props => {
651
682
  id: props.id,
652
683
  display: display
653
684
  }, /*#__PURE__*/React.createElement(CardWithUrlContentComponent, props)));
654
- };
685
+ };
686
+ export const CardWithUrl = withCardIntersectionObserver(CardWithUrlContent);
@@ -135,8 +135,9 @@ const getPromptAction = (promptKey, intl, url = '', product) => {
135
135
  }
136
136
  };
137
137
  case RovoChatPromptKey.KEY_HIGHLIGHTS:
138
- const label_key_highlights = intl.formatMessage(messages.rovo_prompt_button_key_highlights);
139
- const html_key_highlights = intl.formatMessage(messages.rovo_prompt_message_key_highlights, {
138
+ const label_key_highlights = intl.formatMessage(messages.rovo_prompt_button_highlight_relevant_content);
139
+ const html_key_highlights = intl.formatMessage(messages.rovo_prompt_message_highlight_relevant_content, {
140
+ context: contextLong,
140
141
  url
141
142
  }, {
142
143
  ignoreTag: true
@@ -951,16 +951,6 @@ export var messages = defineMessages({
951
951
  defaultMessage: '<p>Summarize the main ideas and key points of <a>{url}</a> in 3-5 clear, complete sentences.</p><p>Preserve any important details such as names, dates, and key decisions.</p>',
952
952
  description: 'The prompt message to send to Rovo Chat. {url} refers to Smart Link that the user triggers this action from. (Please make sure all html tags remain the same.)'
953
953
  },
954
- rovo_prompt_button_key_highlights: {
955
- id: 'fabric.linking.rovo_prompt_button_key_highlights.non-final',
956
- defaultMessage: 'Key highlights',
957
- description: 'The name of the action to send a key highlights prompt message to Rovo Chat in relation to current Smart Link'
958
- },
959
- rovo_prompt_message_key_highlights: {
960
- id: 'fabric.linking.rovo_prompt_message_key_highlights.non-final',
961
- defaultMessage: "<p>Based on <a>{url}</a> and the page or ticket I'm currently viewing, highlight the parts of the linked content that are most relevant to this work. Explain briefly why each part is relevant.</p>",
962
- description: 'The prompt message to send to Rovo Chat. {url} refers to Smart Link that the user triggers this action from. (Please make sure all html tags remain the same.)'
963
- },
964
954
  rovo_prompt_button_ask_rovo_anything: {
965
955
  id: 'fabric.linking.rovo_prompt_button_ask_rovo.non-final',
966
956
  defaultMessage: 'Ask Rovo',
package/dist/esm/ssr.js CHANGED
@@ -7,13 +7,15 @@ import { ErrorBoundary } from 'react-error-boundary';
7
7
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
8
8
  import uuid from 'uuid';
9
9
  import { AnalyticsContext } from '@atlaskit/analytics-next';
10
+ import { fg } from '@atlaskit/platform-feature-flags';
10
11
  import { context } from './utils/analytics/analytics';
11
- import { CardWithUrlContent } from './view/CardWithUrl/component';
12
+ import CardErrorBoundary from './view/CardWithUrl/card-error-boundary';
13
+ import { CardWithUrl, CardWithUrlContent } from './view/CardWithUrl/component';
12
14
  import { LoadingCardLink } from './view/CardWithUrl/component-lazy/LoadingCardLink';
13
15
  // SSR friendly version of smart-card
14
16
  // simplifies the logic around rendering and loading placeholders and
15
17
  // only contains whats necessary to render the card on SSR mode
16
- export var CardSSR = function CardSSR(props) {
18
+ var CardSSROld = function CardSSROld(props) {
17
19
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
18
20
  var _useState = useState(function () {
19
21
  var _props$id;
@@ -39,4 +41,29 @@ export var CardSSR = function CardSSR(props) {
39
41
  }, /*#__PURE__*/React.createElement(Component, {
40
42
  className: "loader-wrapper"
41
43
  }, /*#__PURE__*/React.createElement(CardWithUrlContent, cardProps))));
44
+ };
45
+
46
+ // SSR friendly version of smart-card
47
+ // simplifies the logic around rendering and loading placeholders and
48
+ // only contains whats necessary to render the card on SSR mode
49
+ var CardSSRNew = function CardSSRNew(props) {
50
+ // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
51
+ var _useState3 = useState(function () {
52
+ var _props$id2;
53
+ return (_props$id2 = props.id) !== null && _props$id2 !== void 0 ? _props$id2 : uuid();
54
+ }),
55
+ _useState4 = _slicedToArray(_useState3, 1),
56
+ id = _useState4[0];
57
+ var propsWithId = _objectSpread(_objectSpread({}, props), {}, {
58
+ id: id
59
+ });
60
+ return /*#__PURE__*/React.createElement(AnalyticsContext, {
61
+ data: context
62
+ }, /*#__PURE__*/React.createElement(CardErrorBoundary, propsWithId, /*#__PURE__*/React.createElement(CardWithUrl, propsWithId)));
63
+ };
64
+ export var CardSSR = function CardSSR(props) {
65
+ if (fg('platform_sl_event_ui_seen')) {
66
+ return /*#__PURE__*/React.createElement(CardSSRNew, props);
67
+ }
68
+ return /*#__PURE__*/React.createElement(CardSSROld, props);
42
69
  };
@@ -0,0 +1,44 @@
1
+ import { useCallback, useEffect, useRef } from 'react';
2
+
3
+ // This property enables the intersection observer to be run once the
4
+ // HTML element being observed is within `X` px of the target container it is
5
+ // being compared to. Since the default container is the `document`, we set this
6
+ // up to check once a Smart Link is within `X` px from the bottom of the viewport.
7
+ var ROOT_MARGIN_VERTICAL = '360px';
8
+ var useIntersectionObserver = function useIntersectionObserver(_ref) {
9
+ var onIntersectingCallback = _ref.onIntersecting,
10
+ onIntersectionCallback = _ref.onIntersection;
11
+ var ref = useRef(null);
12
+ var onIntersectingCallbackRef = useRef(onIntersectingCallback);
13
+ var onIntersectionCallbackRef = useRef(onIntersectionCallback);
14
+ useEffect(function () {
15
+ onIntersectingCallbackRef.current = onIntersectingCallback;
16
+ onIntersectionCallbackRef.current = onIntersectionCallback;
17
+ }, [onIntersectingCallback, onIntersectionCallback]);
18
+ var onIntersection = useCallback(function (entries, observer) {
19
+ var _onIntersectionCallba;
20
+ var isIntersecting = entries.some(function (entry) {
21
+ return entry.isIntersecting;
22
+ });
23
+ (_onIntersectionCallba = onIntersectionCallbackRef.current) === null || _onIntersectionCallba === void 0 || _onIntersectionCallba.call(onIntersectionCallbackRef, isIntersecting);
24
+ if (isIntersecting) {
25
+ var _onIntersectingCallba;
26
+ (_onIntersectingCallba = onIntersectingCallbackRef.current) === null || _onIntersectingCallba === void 0 || _onIntersectingCallba.call(onIntersectingCallbackRef);
27
+ observer.disconnect();
28
+ }
29
+ }, []);
30
+ useEffect(function () {
31
+ if (!ref.current) {
32
+ return;
33
+ }
34
+ var intersectionObserver = new IntersectionObserver(onIntersection, {
35
+ rootMargin: "".concat(ROOT_MARGIN_VERTICAL, " 0px ").concat(ROOT_MARGIN_VERTICAL, " 0px")
36
+ });
37
+ intersectionObserver.observe(ref.current);
38
+ return function () {
39
+ return intersectionObserver.disconnect();
40
+ };
41
+ }, [ref, onIntersection]);
42
+ return ref;
43
+ };
44
+ export default useIntersectionObserver;
@@ -0,0 +1,21 @@
1
+ import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
2
+ var _excluded = ["children"];
3
+ import React from 'react';
4
+ import { ErrorBoundary } from 'react-error-boundary';
5
+ import { LoadingCardLink } from '../component-lazy/LoadingCardLink';
6
+ var CardErrorBoundary = function CardErrorBoundary(_ref) {
7
+ var children = _ref.children,
8
+ props = _objectWithoutProperties(_ref, _excluded);
9
+ // TODO: NAVX-4682: Add feature parity to CardWithURLRenderer
10
+ var ErrorFallbackComponent = props.fallbackComponent;
11
+ var errorBoundaryFallbackComponent = function errorBoundaryFallbackComponent() {
12
+ if (ErrorFallbackComponent) {
13
+ return /*#__PURE__*/React.createElement(ErrorFallbackComponent, null);
14
+ }
15
+ return /*#__PURE__*/React.createElement(LoadingCardLink, props);
16
+ };
17
+ return /*#__PURE__*/React.createElement(ErrorBoundary, {
18
+ FallbackComponent: errorBoundaryFallbackComponent
19
+ }, children);
20
+ };
21
+ export default CardErrorBoundary;
@@ -0,0 +1,37 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
+ import React, { useCallback, useState } from 'react';
4
+ import useIntersectionObserver from '../../../state/hooks/use-intersection-observer';
5
+ import { isIntersectionObserverSupported } from '../../../utils';
6
+ import CardLoaderWrapper from '../card-loader-wrapper';
7
+ var withCardIntersectionObserver = function withCardIntersectionObserver(Component) {
8
+ return function (props) {
9
+ // TODO: NAVX-4682: Add feature parity to LazyIntersectionObserverCard for no lazy loading
10
+ var _useState = useState(false),
11
+ _useState2 = _slicedToArray(_useState, 2),
12
+ isIntersected = _useState2[0],
13
+ setIsIntersected = _useState2[1];
14
+ var onIntersecting = useCallback(function () {
15
+ setIsIntersected(true);
16
+ }, []);
17
+ var ref = useIntersectionObserver({
18
+ onIntersecting: onIntersecting
19
+ });
20
+ return /*#__PURE__*/React.createElement(CardLoaderWrapper, {
21
+ appearance: props.appearance,
22
+ ref: ref
23
+ }, /*#__PURE__*/React.createElement(Component, _extends({}, props, {
24
+ isIntersected: isIntersected
25
+ })));
26
+ };
27
+ };
28
+ var withCardIntersectionObserverFallback = function withCardIntersectionObserverFallback(Component) {
29
+ return function (props) {
30
+ return /*#__PURE__*/React.createElement(CardLoaderWrapper, {
31
+ appearance: props.appearance
32
+ }, /*#__PURE__*/React.createElement(Component, props));
33
+ };
34
+ };
35
+ export default (function (Component) {
36
+ return isIntersectionObserverSupported() ? withCardIntersectionObserver(Component) : withCardIntersectionObserverFallback(Component);
37
+ });
@@ -0,0 +1,15 @@
1
+ import React, { forwardRef } from 'react';
2
+ var CardLoaderWrapper = /*#__PURE__*/forwardRef(function (_ref, ref) {
3
+ var appearance = _ref.appearance,
4
+ children = _ref.children;
5
+ var Component = appearance === 'inline' ? 'span' : 'div';
6
+ return (
7
+ /*#__PURE__*/
8
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
9
+ React.createElement(Component, {
10
+ className: "loader-wrapper",
11
+ ref: ref
12
+ }, children)
13
+ );
14
+ });
15
+ export default CardLoaderWrapper;
@@ -1,8 +1,12 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
1
2
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
3
  import React, { useCallback, useEffect, useRef, useState } from 'react';
4
+ import { componentWithFG } from '@atlaskit/platform-feature-flags-react';
3
5
  import { usePrefetch } from '../../../state';
4
6
  import { startUfoExperience } from '../../../state/analytics/ufoExperiences';
7
+ import useIntersectionObserver from '../../../state/hooks/use-intersection-observer';
5
8
  import { shouldSample } from '../../../utils/shouldSample';
9
+ import CardLoaderWrapper from '../card-loader-wrapper';
6
10
  import { CardWithUrlContent } from '../component';
7
11
  import { LoadingCardLink } from './LoadingCardLink';
8
12
 
@@ -11,7 +15,7 @@ import { LoadingCardLink } from './LoadingCardLink';
11
15
  // being compared to. Since the default container is the `document`, we set this
12
16
  // up to check once a Smart Link is within `X` px from the bottom of the viewport.
13
17
  var ROOT_MARGIN_VERTICAL = '360px';
14
- export function LazyIntersectionObserverCard(props) {
18
+ function LazyIntersectionObserverCardOld(props) {
15
19
  var ref = useRef(null);
16
20
  var _useState = useState(false),
17
21
  _useState2 = _slicedToArray(_useState, 2),
@@ -60,4 +64,40 @@ export function LazyIntersectionObserverCard(props) {
60
64
  return /*#__PURE__*/React.createElement(Component, {
61
65
  className: "loader-wrapper"
62
66
  }, content);
63
- }
67
+ }
68
+ var LazyIntersectionObserverCardNew = function LazyIntersectionObserverCardNew(props) {
69
+ var _useState5 = useState(false),
70
+ _useState6 = _slicedToArray(_useState5, 2),
71
+ isIntersected = _useState6[0],
72
+ setIsIntersected = _useState6[1];
73
+ var _useState7 = useState(shouldSample()),
74
+ _useState8 = _slicedToArray(_useState7, 1),
75
+ shouldSendRenderedUFOEvent = _useState8[0];
76
+ var appearance = props.appearance,
77
+ url = props.url,
78
+ id = props.id;
79
+ var prefetch = usePrefetch(url);
80
+ var ComponentObserver = appearance === 'inline' ? 'span' : 'div';
81
+ var onIntersection = useCallback(function (isIntersecting) {
82
+ if (isIntersecting) {
83
+ if (shouldSendRenderedUFOEvent) {
84
+ startUfoExperience('smart-link-rendered', id);
85
+ }
86
+ setIsIntersected(true);
87
+ } else {
88
+ prefetch();
89
+ }
90
+ }, [id, prefetch, shouldSendRenderedUFOEvent]);
91
+ var ref = useIntersectionObserver({
92
+ onIntersection: onIntersection
93
+ });
94
+ var content = isIntersected ? /*#__PURE__*/React.createElement(CardWithUrlContent, _extends({}, props, {
95
+ isIntersected: true
96
+ })) : /*#__PURE__*/React.createElement(ComponentObserver, {
97
+ ref: ref
98
+ }, /*#__PURE__*/React.createElement(LoadingCardLink, props));
99
+ return /*#__PURE__*/React.createElement(CardLoaderWrapper, {
100
+ appearance: appearance
101
+ }, content);
102
+ };
103
+ export var LazyIntersectionObserverCard = componentWithFG('platform_sl_event_ui_seen', LazyIntersectionObserverCardNew, LazyIntersectionObserverCardOld);
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect, useMemo } from 'react';
1
+ import React, { useCallback, useEffect, useMemo, useRef } from 'react';
2
2
  import { useAnalyticsEvents as useAnalyticsEventsNext } from '@atlaskit/analytics-next';
3
3
  import { extractSmartLinkEmbed } from '@atlaskit/link-extractors';
4
4
  import { fg } from '@atlaskit/platform-feature-flags';
@@ -22,6 +22,7 @@ import { EmbedCard, EmbedCardUpdated } from '../EmbedCard';
22
22
  import FlexibleCard from '../FlexibleCard';
23
23
  import { InlineCard } from '../InlineCard';
24
24
  import { useFire3PWorkflowsClickEvent } from '../SmartLinkEvents/useSmartLinkEvents';
25
+ import withCardIntersectionObserver from './card-intersection-observer';
25
26
  var thirdPartyARIPrefix = 'ari:third-party';
26
27
  var EmbedCardComponent = componentWithFG('rovo_chat_embed_card_dwell_and_hover_metrics', EmbedCardUpdated, EmbedCard);
27
28
  function Component(_ref) {
@@ -29,6 +30,7 @@ function Component(_ref) {
29
30
  url = _ref.url,
30
31
  isSelected = _ref.isSelected,
31
32
  isHovered = _ref.isHovered,
33
+ isIntersected = _ref.isIntersected,
32
34
  frameStyle = _ref.frameStyle,
33
35
  platform = _ref.platform,
34
36
  onClick = _ref.onClick,
@@ -56,6 +58,7 @@ function Component(_ref) {
56
58
  createAnalyticsEvent = _useAnalyticsEventsNe.createAnalyticsEvent;
57
59
  var _useAnalyticsEvents = useAnalyticsEvents(),
58
60
  fireEvent = _useAnalyticsEvents.fireEvent;
61
+ var hasFiredSeenRef = useRef(false);
59
62
  var isFlexibleUi = useMemo(function () {
60
63
  return isFlexibleUiCard(children, ui);
61
64
  }, [children, ui]);
@@ -209,6 +212,19 @@ function Component(_ref) {
209
212
  });
210
213
  }
211
214
  }, [appearance, extensionKey, fireEvent, id, isFlexibleUi, state.status]);
215
+
216
+ // Fire smartLink seen event once on a successful render card is in the viewport
217
+ useEffect(function () {
218
+ if (fg('platform_sl_event_ui_seen')) {
219
+ if (!isFinalState(state.status)) return;
220
+ if (isIntersected && !hasFiredSeenRef.current) {
221
+ fireEvent('ui.smartLink.seen', {
222
+ display: appearance
223
+ });
224
+ hasFiredSeenRef.current = true;
225
+ }
226
+ }
227
+ }, [state.status, isIntersected, fireEvent, appearance]);
212
228
  var onIframeDwell = useCallback(function (dwellTime, dwellPercentVisible) {
213
229
  fireEvent('ui.smartLinkIframe.dwelled', {
214
230
  id: id,
@@ -331,6 +347,7 @@ function ComponentUpdated(_ref2) {
331
347
  url = _ref2.url,
332
348
  isSelected = _ref2.isSelected,
333
349
  isHovered = _ref2.isHovered,
350
+ isIntersected = _ref2.isIntersected,
334
351
  frameStyle = _ref2.frameStyle,
335
352
  platform = _ref2.platform,
336
353
  onClick = _ref2.onClick,
@@ -358,6 +375,7 @@ function ComponentUpdated(_ref2) {
358
375
  createAnalyticsEvent = _useAnalyticsEventsNe2.createAnalyticsEvent;
359
376
  var _useAnalyticsEvents2 = useAnalyticsEvents(),
360
377
  fireEvent = _useAnalyticsEvents2.fireEvent;
378
+ var hasFiredSeenRef = useRef(false);
361
379
  var isFlexibleUi = useMemo(function () {
362
380
  return isFlexibleUiCard(children, ui);
363
381
  }, [children, ui]);
@@ -511,6 +529,19 @@ function ComponentUpdated(_ref2) {
511
529
  });
512
530
  }
513
531
  }, [appearance, extensionKey, fireEvent, id, isFlexibleUi, state.status]);
532
+
533
+ // Fire smartLink seen event once on a successful render card is in the viewport
534
+ useEffect(function () {
535
+ if (fg('platform_sl_event_ui_seen')) {
536
+ if (!isFinalState(state.status)) return;
537
+ if (isIntersected && !hasFiredSeenRef.current) {
538
+ fireEvent('ui.smartLink.seen', {
539
+ display: appearance
540
+ });
541
+ hasFiredSeenRef.current = true;
542
+ }
543
+ }
544
+ }, [state.status, isIntersected, fireEvent, appearance]);
514
545
  var onIframeDwell = useCallback(function (dwellTime, dwellPercentVisible) {
515
546
  fireEvent('ui.smartLinkIframe.dwelled', {
516
547
  id: id,
@@ -655,4 +686,5 @@ export var CardWithUrlContent = function CardWithUrlContent(props) {
655
686
  id: props.id,
656
687
  display: display
657
688
  }, /*#__PURE__*/React.createElement(CardWithUrlContentComponent, props)));
658
- };
689
+ };
690
+ export var CardWithUrl = withCardIntersectionObserver(CardWithUrlContent);
@@ -143,8 +143,9 @@ var getPromptAction = function getPromptAction(promptKey, intl) {
143
143
  }
144
144
  };
145
145
  case RovoChatPromptKey.KEY_HIGHLIGHTS:
146
- var label_key_highlights = intl.formatMessage(messages.rovo_prompt_button_key_highlights);
147
- var html_key_highlights = intl.formatMessage(messages.rovo_prompt_message_key_highlights, {
146
+ var label_key_highlights = intl.formatMessage(messages.rovo_prompt_button_highlight_relevant_content);
147
+ var html_key_highlights = intl.formatMessage(messages.rovo_prompt_message_highlight_relevant_content, {
148
+ context: contextLong,
148
149
  url: url
149
150
  }, {
150
151
  ignoreTag: true
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Generates Typescript types for analytics events from analytics.spec.yaml
5
5
  *
6
- * @codegen <<SignedSource::1c5f802d8b5609627e5ffbebf73bad6e>>
6
+ * @codegen <<SignedSource::4d978bf4946aee0ad737d8305df7a6a0>>
7
7
  * @codegenCommand yarn workspace @atlassian/analytics-tooling run analytics:codegen smart-card
8
8
  */
9
9
  export type PackageMetaDataContextType = {
@@ -263,6 +263,9 @@ export type SmartLinkRenderFailedAttributesType = {
263
263
  id: string | null;
264
264
  };
265
265
  export type ButtonClickedDismissAttributesType = {};
266
+ export type SmartLinkSeenAttributesType = {
267
+ display: 'inline' | 'block' | 'embed' | 'flexible';
268
+ };
266
269
  export type SmartLinkClickedSmartlinkClickAnalyticsWorkflowsAttributesType = {
267
270
  thirdPartyARI: string;
268
271
  eventName: string;
@@ -439,6 +442,9 @@ export type AnalyticsEventAttributes = {
439
442
  /**
440
443
  * fires an event that represents when a Smart Link renders unsuccessfully. */
441
444
  'ui.smartLink.renderFailed': SmartLinkRenderFailedAttributesType;
445
+ /**
446
+ * fires an event that represents when a rendered Smart Link is visible in the viewport for the first time */
447
+ 'ui.smartLink.seen': SmartLinkSeenAttributesType;
442
448
  /**
443
449
  * fires an event that represents when a user clicks on a Smart Link to track analytics for the 3P Workflows team */
444
450
  'ui.smartLink.clicked.smartlinkClickAnalyticsWorkflows': SmartLinkClickedSmartlinkClickAnalyticsWorkflowsAttributesType;
@@ -1,6 +1,6 @@
1
1
  import { type MessageDescriptor } from 'react-intl';
2
2
  export type RequestAccessMessageKey = 'click_to_join' | 'click_to_join_description' | 'forbidden_description' | 'request_access' | 'request_access_description' | 'request_access_pending' | 'request_access_pending_title' | 'request_access_pending_description' | 'request_denied_description' | 'default_no_access_title' | 'direct_access_title' | 'direct_access_description' | 'direct_access' | 'access_exists_description' | 'not_found_description' | 'not_found_title';
3
- export type RovoChatActionMessageKey = 'rovo_prompt_context_generic' | 'rovo_prompt_context_generic_plural' | 'rovo_prompt_context_confluence_page' | 'rovo_prompt_context_confluence_page_short' | 'rovo_prompt_context_jira_work_item' | 'rovo_prompt_context_jira_work_item_short' | 'rovo_prompt_button_recommend_other_sources' | 'rovo_prompt_message_recommend_other_sources' | 'rovo_prompt_button_show_other_mentions' | 'rovo_prompt_message_show_other_mentions' | 'rovo_prompt_button_suggest_improvement' | 'rovo_prompt_message_suggest_improvement' | 'rovo_prompt_message_summarize' | 'rovo_prompt_button_key_highlights' | 'rovo_prompt_message_key_highlights' | 'rovo_prompt_button_ask_rovo_anything' | 'rovo_prompt_message_ask_rovo_anything' | 'rovo_prompt_button_highlight_relevant_content' | 'rovo_prompt_message_highlight_relevant_content' | 'rovo_prompt_button_identify_key_trends' | 'rovo_prompt_message_identify_key_trends' | 'rovo_prompt_button_identify_key_points' | 'rovo_prompt_message_identify_key_points' | 'rovo_prompt_button_find_open_questions' | 'rovo_prompt_message_find_open_questions';
3
+ export type RovoChatActionMessageKey = 'rovo_prompt_context_generic' | 'rovo_prompt_context_generic_plural' | 'rovo_prompt_context_confluence_page' | 'rovo_prompt_context_confluence_page_short' | 'rovo_prompt_context_jira_work_item' | 'rovo_prompt_context_jira_work_item_short' | 'rovo_prompt_button_recommend_other_sources' | 'rovo_prompt_message_recommend_other_sources' | 'rovo_prompt_button_show_other_mentions' | 'rovo_prompt_message_show_other_mentions' | 'rovo_prompt_button_suggest_improvement' | 'rovo_prompt_message_suggest_improvement' | 'rovo_prompt_message_summarize' | 'rovo_prompt_button_ask_rovo_anything' | 'rovo_prompt_message_ask_rovo_anything' | 'rovo_prompt_button_highlight_relevant_content' | 'rovo_prompt_message_highlight_relevant_content' | 'rovo_prompt_button_identify_key_trends' | 'rovo_prompt_message_identify_key_trends' | 'rovo_prompt_button_identify_key_points' | 'rovo_prompt_message_identify_key_points' | 'rovo_prompt_button_find_open_questions' | 'rovo_prompt_message_find_open_questions';
4
4
  export type MessageKey = 'assigned_to' | 'ai_summarize' | 'change_status' | 'ai_summarized' | 'ai_summarized_abbreviation' | 'ai_summarized_info' | 'ai_summarized_info_short' | 'ai_summarizing' | 'ai_summary_error_generic' | 'ai_summary_error_acceptable_use_violation' | 'ai_summary_error_hipaa_content_detected' | 'ai_summary_error_exceeding_context_length_error' | 'ai_summary_action' | 'ai_summary_action_description' | 'automation_action_title' | 'automation_action_tooltip' | 'automation_action_icon_label' | 'automation_action_confluence_page_modal_title' | 'automation_action_confluence_page_modal_description' | 'copy_summary_action' | 'copy_summary_action_description' | 'copied_summary_action_description' | 'beta' | 'cannot_find_link' | 'compass_applied_components_count' | 'connect_link_account_card' | 'connect_link_account_card_name' | 'connect_link_account_card_description' | 'connect_unauthorised_account_action' | 'connect_unauthorised_account_description' | 'connect_unauthorised_account_description_no_provider' | 'continue' | 'copy_url_to_clipboard' | 'copied_url_to_clipboard' | 'could_not_load_link' | 'download' | 'download_description' | 'download_file' | 'follow' | 'follow_project_description' | 'follow_project_descriptionGalaxia' | 'follow_project' | 'follow_goal' | 'follow_goal_description' | 'follow_project_error' | 'follow_project_errorGalaxia' | 'follow_goal_error' | 'go_back' | 'invalid_permissions' | 'invalid_permissions_description' | 'join_to_view' | 'connect_link_account' | 'created_by' | 'created_on_relative' | 'created_on_absolute' | 'check_this_link' | 'delete' | 'edit' | 'learn_more_about_smart_links' | 'learn_more_about_connecting_account' | 'loading' | 'link_safety_warning_message' | 'modified_by' | 'modified_on_relative' | 'modified_on_absolute' | 'more_actions' | 'not_found_title' | 'not_found_description' | 'open_issue_in_jira' | 'open_link_in_a_new_tab' | 'owned_by' | 'owned_by_override' | 'preview_description' | 'preview_improved' | 'preview_modal' | 'preview_panel' | 'preview_close' | 'preview_max_size' | 'preview_min_size' | 'priority_blocker' | 'priority_critical' | 'priority_high' | 'priority_highest' | 'priority_low' | 'priority_lowest' | 'priority_major' | 'priority_medium' | 'priority_minor' | 'priority_trivial' | 'priority_undefined' | 'forbidden_access' | 'pending_request' | 'read_time' | 'restricted_link' | 'request_access_to_view' | 'request_denied' | 'sent_on_relative' | 'sent_on_absolute' | 'status_change_load_error' | 'status_change_permission_error' | 'status_change_update_error' | 'try_again' | 'try_another_account' | 'unauthorised_account_description' | 'unauthorised_account_description_no_provider' | 'unauthorised_account_name' | 'unauthorised_account_name_no_provider' | 'rovo_actions_explore' | 'unassigned' | 'unfollow' | 'unfollow_project_description' | 'unfollow_project_descriptionGalaxia' | 'unfollow_project' | 'unfollow_project_error' | 'unfollow_project_errorGalaxia' | 'unfollow_goal' | 'unfollow_goal_description' | 'unfollow_goal_error' | 'user_attributes' | 'view' | 'viewIn' | 'viewOriginal' | 'actions' | 'add_account' | 'cancel' | 'close' | 'connect_to' | 'connect_account_description' | 'retry' | 'save' | 'unlink_account' | RequestAccessMessageKey | 'related' | 'generic_error_message' | 'related_links_modal_error_title' | 'related_links_modal_error_description' | 'related_links_modal_unavailable_title' | 'related_links_modal_unavailable_description' | 'related_links_modal_title' | 'related_links_view_related_urls' | 'related_links_view_related_links' | 'related_links_found_in' | 'related_links_includes_links_to' | 'related_links_not_found' | 'join_to_viewIssueTermRefresh' | 'open_issue_in_jiraIssueTermRefresh' | 'request_access_to_viewIssueTermRefresh' | 'team_members_count' | 'status_change_permission_errorIssueTermRefresh' | 'connect_unauthorised_account_description_appify' | 'connect_unauthorised_account_description_no_provider_appify' | 'learn_more_about_connecting_account_experiment_shorter' | 'learn_more_about_connecting_account_appify' | 'rovo_summary_loading' | 'ai_disclaimer' | 'rovo_unauthorised_title' | 'rovo_unauthorised_title_no_provider' | 'rovo_unauthorised_feature_clear_link_names' | 'rovo_unauthorised_feature_understand_linked_docs' | 'rovo_unauthorised_feature_go_deeper_smart_suggestions' | 'rovo_unauthorised_connect_account' | 'rovo_unauthorised_not_now' | RovoChatActionMessageKey;
5
5
  type Messages = {
6
6
  [K in MessageKey]: MessageDescriptor;
@@ -0,0 +1,6 @@
1
+ type UseIntersectionObserverProps = {
2
+ onIntersecting?: () => void;
3
+ onIntersection?: (isIntersecting: boolean) => void;
4
+ };
5
+ declare const useIntersectionObserver: ({ onIntersecting: onIntersectingCallback, onIntersection: onIntersectionCallback, }: UseIntersectionObserverProps) => import("react").MutableRefObject<HTMLDivElement | null>;
6
+ export default useIntersectionObserver;
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import type { CardProps } from '../../Card';
3
+ type CardErrorBoundaryProps = CardProps & Required<Pick<CardProps, 'id' | 'url'>>;
4
+ declare const CardErrorBoundary: ({ children, ...props }: React.PropsWithChildren<CardErrorBoundaryProps>) => React.JSX.Element;
5
+ export default CardErrorBoundary;
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import type { CardProps } from '../../Card';
3
+ type WithCardIntersectionObserverProps = {
4
+ appearance: CardProps['appearance'];
5
+ };
6
+ declare const _default: <T extends WithCardIntersectionObserverProps>(Component: React.ComponentType<T>) => (props: T) => React.JSX.Element;
7
+ export default _default;
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import type { CardProps } from '../../Card';
3
+ type CardLoaderWrapperProps = Pick<CardProps, 'appearance'>;
4
+ declare const CardLoaderWrapper: React.ForwardRefExoticComponent<CardLoaderWrapperProps & {
5
+ children?: React.ReactNode | undefined;
6
+ } & React.RefAttributes<HTMLDivElement>>;
7
+ export default CardLoaderWrapper;
@@ -1,3 +1,3 @@
1
1
  import React from 'react';
2
2
  import { type CardWithUrlContentProps } from '../types';
3
- export declare function LazyIntersectionObserverCard(props: CardWithUrlContentProps): React.JSX.Element;
3
+ export declare const LazyIntersectionObserverCard: React.FC<CardWithUrlContentProps>;
@@ -1,3 +1,4 @@
1
1
  import React from 'react';
2
2
  import { type CardWithUrlContentProps } from './types';
3
3
  export declare const CardWithUrlContent: (props: CardWithUrlContentProps) => React.JSX.Element;
4
+ export declare const CardWithUrl: (props: CardWithUrlContentProps) => React.JSX.Element;
@@ -24,6 +24,7 @@ export type CardWithUrlContentProps = {
24
24
  inheritDimensions?: boolean;
25
25
  inlinePreloaderStyle?: InlinePreloaderStyle;
26
26
  isHovered?: boolean;
27
+ isIntersected?: boolean;
27
28
  isSelected?: boolean;
28
29
  onClick?: EventHandler<MouseEvent | KeyboardEvent>;
29
30
  onError?: OnErrorCallback;