@atlaskit/smart-card 43.11.2 → 43.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/analytics.spec.yaml +7 -1
  3. package/dist/cjs/state/actions/index.js +1 -4
  4. package/dist/cjs/state/hooks/use-resolve/index.js +3 -13
  5. package/dist/cjs/state/hooks/use-response/index.js +0 -2
  6. package/dist/cjs/utils/analytics/analytics.js +1 -1
  7. package/dist/cjs/utils/analytics/click.js +0 -1
  8. package/dist/cjs/view/CardWithUrl/component.js +327 -3
  9. package/dist/cjs/view/EmbedCard/components/ExpandedFrame.js +93 -2
  10. package/dist/cjs/view/EmbedCard/components/Frame.js +121 -3
  11. package/dist/cjs/view/EmbedCard/components/IframeDwellTracker.js +25 -4
  12. package/dist/cjs/view/EmbedCard/index.js +204 -1
  13. package/dist/cjs/view/EmbedCard/views/ResolvedView.js +95 -2
  14. package/dist/cjs/view/FlexibleCard/components/actions/action/index.js +0 -1
  15. package/dist/cjs/view/FlexibleCard/components/actions/automation-action/automation-manual-triggers/manual-triggers-container/manual-triggers-form/boolean-prompt/main.js +9 -13
  16. package/dist/cjs/view/LinkUrl/Hyperlink/index.js +0 -1
  17. package/dist/cjs/view/LinkUrl/index.js +1 -3
  18. package/dist/es2019/state/actions/index.js +1 -4
  19. package/dist/es2019/state/hooks/use-resolve/index.js +1 -10
  20. package/dist/es2019/state/hooks/use-response/index.js +0 -2
  21. package/dist/es2019/utils/analytics/analytics.js +1 -1
  22. package/dist/es2019/utils/analytics/click.js +0 -1
  23. package/dist/es2019/view/CardWithUrl/component.js +324 -2
  24. package/dist/es2019/view/EmbedCard/components/ExpandedFrame.js +87 -2
  25. package/dist/es2019/view/EmbedCard/components/Frame.js +112 -2
  26. package/dist/es2019/view/EmbedCard/components/IframeDwellTracker.js +25 -4
  27. package/dist/es2019/view/EmbedCard/index.js +208 -0
  28. package/dist/es2019/view/EmbedCard/views/ResolvedView.js +91 -3
  29. package/dist/es2019/view/FlexibleCard/components/actions/action/index.js +0 -1
  30. package/dist/es2019/view/FlexibleCard/components/actions/automation-action/automation-manual-triggers/manual-triggers-container/manual-triggers-form/boolean-prompt/main.js +8 -12
  31. package/dist/es2019/view/LinkUrl/Hyperlink/index.js +0 -1
  32. package/dist/es2019/view/LinkUrl/index.js +1 -3
  33. package/dist/esm/state/actions/index.js +1 -4
  34. package/dist/esm/state/hooks/use-resolve/index.js +3 -13
  35. package/dist/esm/state/hooks/use-response/index.js +0 -2
  36. package/dist/esm/utils/analytics/analytics.js +1 -1
  37. package/dist/esm/utils/analytics/click.js +0 -1
  38. package/dist/esm/view/CardWithUrl/component.js +326 -2
  39. package/dist/esm/view/EmbedCard/components/ExpandedFrame.js +95 -2
  40. package/dist/esm/view/EmbedCard/components/Frame.js +122 -2
  41. package/dist/esm/view/EmbedCard/components/IframeDwellTracker.js +25 -4
  42. package/dist/esm/view/EmbedCard/index.js +203 -0
  43. package/dist/esm/view/EmbedCard/views/ResolvedView.js +97 -3
  44. package/dist/esm/view/FlexibleCard/components/actions/action/index.js +0 -1
  45. package/dist/esm/view/FlexibleCard/components/actions/automation-action/automation-manual-triggers/manual-triggers-container/manual-triggers-form/boolean-prompt/main.js +9 -13
  46. package/dist/esm/view/LinkUrl/Hyperlink/index.js +0 -1
  47. package/dist/esm/view/LinkUrl/index.js +1 -3
  48. package/dist/types/common/analytics/generated/analytics.types.d.ts +1 -0
  49. package/dist/types/view/EmbedCard/components/ExpandedFrame.d.ts +8 -1
  50. package/dist/types/view/EmbedCard/components/Frame.d.ts +6 -0
  51. package/dist/types/view/EmbedCard/index.d.ts +4 -0
  52. package/dist/types/view/EmbedCard/types.d.ts +4 -0
  53. package/dist/types/view/EmbedCard/views/ResolvedView.d.ts +6 -1
  54. package/dist/types-ts4.5/common/analytics/generated/analytics.types.d.ts +1 -0
  55. package/dist/types-ts4.5/view/EmbedCard/components/ExpandedFrame.d.ts +8 -1
  56. package/dist/types-ts4.5/view/EmbedCard/components/Frame.d.ts +6 -0
  57. package/dist/types-ts4.5/view/EmbedCard/index.d.ts +4 -0
  58. package/dist/types-ts4.5/view/EmbedCard/types.d.ts +4 -0
  59. package/dist/types-ts4.5/view/EmbedCard/views/ResolvedView.d.ts +6 -1
  60. package/package.json +8 -4
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useMemo } 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';
5
+ import { componentWithFG } from '@atlaskit/platform-feature-flags-react';
5
6
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
6
7
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
7
8
  import { useAnalyticsEvents } from '../../common/analytics/generated/use-analytics-events';
@@ -17,11 +18,12 @@ import { SmartLinkAnalyticsContext } from '../../utils/analytics/SmartLinkAnalyt
17
18
  import { isFlexibleUiCard } from '../../utils/flexible';
18
19
  import * as measure from '../../utils/performance';
19
20
  import { BlockCard } from '../BlockCard';
20
- import { EmbedCard } from '../EmbedCard';
21
+ import { EmbedCard, EmbedCardUpdated } from '../EmbedCard';
21
22
  import FlexibleCard from '../FlexibleCard';
22
23
  import { InlineCard } from '../InlineCard';
23
24
  import { useFire3PWorkflowsClickEvent } from '../SmartLinkEvents/useSmartLinkEvents';
24
25
  var thirdPartyARIPrefix = 'ari:third-party';
26
+ var EmbedCardComponent = componentWithFG('rovo_chat_embed_card_dwell_and_hover_metrics', EmbedCardUpdated, EmbedCard);
25
27
  function Component(_ref) {
26
28
  var id = _ref.id,
27
29
  url = _ref.url,
@@ -324,11 +326,333 @@ function Component(_ref) {
324
326
  });
325
327
  }
326
328
  }
329
+ function ComponentUpdated(_ref2) {
330
+ var id = _ref2.id,
331
+ url = _ref2.url,
332
+ isSelected = _ref2.isSelected,
333
+ isHovered = _ref2.isHovered,
334
+ frameStyle = _ref2.frameStyle,
335
+ platform = _ref2.platform,
336
+ onClick = _ref2.onClick,
337
+ appearance = _ref2.appearance,
338
+ onResolve = _ref2.onResolve,
339
+ onError = _ref2.onError,
340
+ testId = _ref2.testId,
341
+ actionOptionsProp = _ref2.actionOptions,
342
+ inheritDimensions = _ref2.inheritDimensions,
343
+ embedIframeRef = _ref2.embedIframeRef,
344
+ embedIframeUrlType = _ref2.embedIframeUrlType,
345
+ inlinePreloaderStyle = _ref2.inlinePreloaderStyle,
346
+ ui = _ref2.ui,
347
+ children = _ref2.children,
348
+ showHoverPreview = _ref2.showHoverPreview,
349
+ hoverPreviewOptions = _ref2.hoverPreviewOptions,
350
+ removeTextHighlightingFromTitle = _ref2.removeTextHighlightingFromTitle,
351
+ resolvingPlaceholder = _ref2.resolvingPlaceholder,
352
+ truncateInline = _ref2.truncateInline,
353
+ CompetitorPrompt = _ref2.CompetitorPrompt,
354
+ hideIconLoadingSkeleton = _ref2.hideIconLoadingSkeleton,
355
+ disablePreviewPanel = _ref2.disablePreviewPanel,
356
+ placeholderData = _ref2.placeholderData;
357
+ var _useAnalyticsEventsNe2 = useAnalyticsEventsNext(),
358
+ createAnalyticsEvent = _useAnalyticsEventsNe2.createAnalyticsEvent;
359
+ var _useAnalyticsEvents2 = useAnalyticsEvents(),
360
+ fireEvent = _useAnalyticsEvents2.fireEvent;
361
+ var isFlexibleUi = useMemo(function () {
362
+ return isFlexibleUiCard(children, ui);
363
+ }, [children, ui]);
364
+
365
+ // Get state, actions for this card.
366
+ var _useSmartLink2 = useSmartLink(id, url),
367
+ state = _useSmartLink2.state,
368
+ actions = _useSmartLink2.actions,
369
+ config = _useSmartLink2.config,
370
+ renderers = _useSmartLink2.renderers,
371
+ error = _useSmartLink2.error,
372
+ isPreviewPanelAvailable = _useSmartLink2.isPreviewPanelAvailable,
373
+ openPreviewPanel = _useSmartLink2.openPreviewPanel;
374
+ var ari = getObjectAri(state.details);
375
+ var name = getObjectName(state.details);
376
+ var definitionId = getDefinitionId(state.details);
377
+ var extensionKey = getExtensionKey(state.details);
378
+ var resourceType = getResourceType(state.details);
379
+ var services = getServices(state.details);
380
+ var thirdPartyARI = getThirdPartyARI(state.details);
381
+ var firstPartyIdentifier = getFirstPartyIdentifier();
382
+ var actionOptions = combineActionOptions({
383
+ actionOptions: actionOptionsProp,
384
+ platform: platform
385
+ });
386
+ var fire3PClickEvent = fg('platform_smartlink_3pclick_analytics') ?
387
+ // eslint-disable-next-line react-hooks/rules-of-hooks
388
+ useFire3PWorkflowsClickEvent(firstPartyIdentifier, thirdPartyARI) : undefined;
389
+
390
+ // Setup UI handlers.
391
+ var handleClickWrapper = useCallback(function (event) {
392
+ var isModifierKeyPressed = isSpecialKey(event) || isSpecialClick(event);
393
+ fireEvent('ui.smartLink.clicked', {
394
+ id: id,
395
+ display: isFlexibleUi ? CardDisplay.Flexible : appearance,
396
+ definitionId: definitionId !== null && definitionId !== void 0 ? definitionId : null,
397
+ isModifierKeyPressed: isModifierKeyPressed
398
+ });
399
+ if (fg('platform_smartlink_3pclick_analytics')) {
400
+ if (thirdPartyARI && thirdPartyARI.startsWith(thirdPartyARIPrefix)) {
401
+ var clickURL = getClickUrl(url, state.details);
402
+ if (clickURL === url && fire3PClickEvent) {
403
+ // For questions or concerns about this event,
404
+ // please reach out to the 3P Workflows Team via Slack in #help-3p-connector-workflow
405
+ fire3PClickEvent();
406
+ }
407
+ }
408
+ }
409
+ var isDisablePreviewPanel = disablePreviewPanel && editorExperiment('platform_editor_preview_panel_linking_exp', true, {
410
+ exposure: true
411
+ });
412
+
413
+ // If preview panel is available and the user clicked on the link,
414
+ // delegate the click to the preview panel handler
415
+ if (!isModifierKeyPressed && ari && name && openPreviewPanel && isPreviewPanelAvailable !== null && isPreviewPanelAvailable !== void 0 && isPreviewPanelAvailable({
416
+ ari: ari
417
+ }) && !isDisablePreviewPanel) {
418
+ var _extractSmartLinkEmbe2;
419
+ event.preventDefault();
420
+ event.stopPropagation();
421
+ openPreviewPanel({
422
+ url: url,
423
+ ari: ari,
424
+ name: name,
425
+ iconUrl: getObjectIconUrl(state.details),
426
+ panelData: {
427
+ embedUrl: expValEquals('platform_hover_card_preview_panel', 'cohort', 'test') ? (_extractSmartLinkEmbe2 = extractSmartLinkEmbed(state.details)) === null || _extractSmartLinkEmbe2 === void 0 ? void 0 : _extractSmartLinkEmbe2.src : undefined
428
+ }
429
+ });
430
+ fireLinkClickedEvent(createAnalyticsEvent)(event, {
431
+ attributes: {
432
+ clickOutcome: 'previewPanel'
433
+ }
434
+ });
435
+ return;
436
+ } else if (!onClick && !isFlexibleUi) {
437
+ var clickUrl = getClickUrl(url, state.details);
438
+
439
+ // Ctrl+left click on mac typically doesn't trigger onClick
440
+ // The event could have potentially had `e.preventDefault()` called on it by now
441
+ // event by smart card internally
442
+ // If it has been called then only then can `isSpecialEvent` be true.
443
+ var target = isSpecialEvent(event) ? '_blank' : '_self';
444
+ window.open(clickUrl, target);
445
+ fireLinkClickedEvent(createAnalyticsEvent)(event, {
446
+ attributes: {
447
+ clickOutcome: target === '_blank' ? 'clickThroughNewTabOrWindow' : 'clickThrough'
448
+ }
449
+ });
450
+ } else {
451
+ if (onClick) {
452
+ onClick(event);
453
+ }
454
+ fireLinkClickedEvent(createAnalyticsEvent)(event);
455
+ }
456
+ }, [fireEvent, id, isFlexibleUi, appearance, definitionId, onClick, url, state.details, ari, name, fire3PClickEvent, isPreviewPanelAvailable, openPreviewPanel, createAnalyticsEvent, thirdPartyARI, disablePreviewPanel]);
457
+ var handleAuthorize = useCallback(function () {
458
+ return actions.authorize(appearance);
459
+ }, [actions, appearance]);
460
+ var handleRetry = useCallback(function () {
461
+ actions.reload();
462
+ }, [actions]);
463
+ var handleInvoke = useCallback(function (opts) {
464
+ return actions.invoke(opts, appearance);
465
+ }, [actions, appearance]);
466
+
467
+ // NB: for each status change in a Smart Link, a performance mark is created.
468
+ // Measures are sent relative to the first mark, matching what a user sees.
469
+ useEffect(function () {
470
+ measure.mark(id, state.status);
471
+ if (state.status !== 'pending' && state.status !== 'resolving') {
472
+ var _state$error3, _state$error4;
473
+ measure.create(id, state.status);
474
+ if (state.status === 'resolved') {
475
+ var _measure$getMeasure$d2, _measure$getMeasure2;
476
+ fireEvent('operational.smartLink.resolved', {
477
+ definitionId: definitionId !== null && definitionId !== void 0 ? definitionId : null,
478
+ duration: (_measure$getMeasure$d2 = (_measure$getMeasure2 = measure.getMeasure(id, state.status)) === null || _measure$getMeasure2 === void 0 ? void 0 : _measure$getMeasure2.duration) !== null && _measure$getMeasure$d2 !== void 0 ? _measure$getMeasure$d2 : null
479
+ });
480
+ } else if (((_state$error3 = state.error) === null || _state$error3 === void 0 ? void 0 : _state$error3.type) !== 'ResolveUnsupportedError' && ((_state$error4 = state.error) === null || _state$error4 === void 0 ? void 0 : _state$error4.type) !== 'UnsupportedError') {
481
+ fireEvent('operational.smartLink.unresolved', {
482
+ definitionId: definitionId !== null && definitionId !== void 0 ? definitionId : null,
483
+ reason: state.status,
484
+ error: state.error === undefined ? null : {
485
+ name: state.error.name,
486
+ kind: state.error.kind,
487
+ type: state.error.type
488
+ }
489
+ });
490
+ }
491
+ }
492
+ }, [id, appearance, state.status, state.error, definitionId, extensionKey, resourceType, fireEvent]);
493
+
494
+ // NB: once the smart-card has rendered into an end state, we capture
495
+ // this as a successful render. These can be one of:
496
+ // - the resolved state: when metadata is shown;
497
+ // - the unresolved states: viz. forbidden, not_found, unauthorized, errored.
498
+ useEffect(function () {
499
+ if (isFinalState(state.status)) {
500
+ succeedUfoExperience('smart-link-rendered', id || 'NULL', {
501
+ extensionKey: extensionKey,
502
+ display: isFlexibleUi ? 'flexible' : appearance
503
+ });
504
+
505
+ // UFO will disregard this if authentication experience has not yet been started
506
+ succeedUfoExperience('smart-link-authenticated', id || 'NULL', {
507
+ display: isFlexibleUi ? 'flexible' : appearance
508
+ });
509
+ fireEvent('ui.smartLink.renderSuccess', {
510
+ display: isFlexibleUi ? 'flexible' : appearance
511
+ });
512
+ }
513
+ }, [appearance, extensionKey, fireEvent, id, isFlexibleUi, state.status]);
514
+ var onIframeDwell = useCallback(function (dwellTime, dwellPercentVisible) {
515
+ fireEvent('ui.smartLinkIframe.dwelled', {
516
+ id: id,
517
+ definitionId: definitionId !== null && definitionId !== void 0 ? definitionId : null,
518
+ display: isFlexibleUi ? 'flexible' : appearance,
519
+ dwellPercentVisible: dwellPercentVisible,
520
+ dwellTime: dwellTime
521
+ });
522
+ }, [id, appearance, definitionId, isFlexibleUi, fireEvent]);
523
+ var onIframeFocus = useCallback(function () {
524
+ fireEvent('ui.smartLinkIframe.focused', {
525
+ id: id,
526
+ definitionId: definitionId !== null && definitionId !== void 0 ? definitionId : null,
527
+ display: isFlexibleUi ? 'flexible' : appearance,
528
+ interactionType: 'focus'
529
+ });
530
+ }, [id, appearance, definitionId, isFlexibleUi, fireEvent]);
531
+ var onIframeMouseEnter = useCallback(function () {
532
+ fireEvent('ui.smartLinkIframe.focused', {
533
+ id: id,
534
+ definitionId: definitionId !== null && definitionId !== void 0 ? definitionId : null,
535
+ display: isFlexibleUi ? 'flexible' : appearance,
536
+ interactionType: 'mouseenter'
537
+ });
538
+ }, [id, appearance, definitionId, isFlexibleUi, fireEvent]);
539
+ var onIframeMouseLeave = useCallback(function () {
540
+ fireEvent('ui.smartLinkIframe.focused', {
541
+ id: id,
542
+ definitionId: definitionId !== null && definitionId !== void 0 ? definitionId : null,
543
+ display: isFlexibleUi ? 'flexible' : appearance,
544
+ interactionType: 'mouseleave'
545
+ });
546
+ }, [id, appearance, definitionId, isFlexibleUi, fireEvent]);
547
+ if (isFlexibleUi) {
548
+ var cardState = state;
549
+ if (error) {
550
+ if ((error === null || error === void 0 ? void 0 : error.name) === 'APIError') {
551
+ cardState = {
552
+ status: 'errored'
553
+ };
554
+ } else {
555
+ throw error;
556
+ }
557
+ }
558
+ return /*#__PURE__*/React.createElement(FlexibleCard, {
559
+ id: id,
560
+ cardState: cardState,
561
+ placeholderData: fg('platform_initial_data_for_smart_cards') ? placeholderData : undefined,
562
+ onAuthorize: services.length && handleAuthorize || undefined,
563
+ onClick: handleClickWrapper,
564
+ origin: "smartLinkCard",
565
+ renderers: renderers,
566
+ ui: ui,
567
+ showHoverPreview: showHoverPreview,
568
+ hoverPreviewOptions: hoverPreviewOptions,
569
+ actionOptions: actionOptions,
570
+ url: url,
571
+ testId: testId,
572
+ onResolve: onResolve,
573
+ onError: onError
574
+ }, children);
575
+ }
576
+
577
+ // We have to keep this last to prevent hook order from being violated
578
+ if (error) {
579
+ throw error;
580
+ }
581
+ switch (appearance) {
582
+ case 'inline':
583
+ return /*#__PURE__*/React.createElement(InlineCard, {
584
+ id: id,
585
+ url: url,
586
+ renderers: renderers,
587
+ cardState: state,
588
+ handleAuthorize: services.length && handleAuthorize || undefined,
589
+ handleFrameClick: handleClickWrapper,
590
+ isSelected: isSelected,
591
+ isHovered: isHovered,
592
+ onResolve: onResolve,
593
+ onError: onError,
594
+ testId: testId,
595
+ inlinePreloaderStyle: inlinePreloaderStyle,
596
+ showHoverPreview: showHoverPreview,
597
+ hoverPreviewOptions: hoverPreviewOptions,
598
+ actionOptions: actionOptions,
599
+ removeTextHighlightingFromTitle: removeTextHighlightingFromTitle,
600
+ resolvingPlaceholder: resolvingPlaceholder,
601
+ truncateInline: truncateInline,
602
+ hideIconLoadingSkeleton: hideIconLoadingSkeleton
603
+ });
604
+ case 'block':
605
+ return /*#__PURE__*/React.createElement(BlockCard, {
606
+ id: id,
607
+ url: url,
608
+ renderers: renderers,
609
+ authFlow: config && config.authFlow,
610
+ cardState: state,
611
+ handleAuthorize: services.length && handleAuthorize || undefined,
612
+ handleFrameClick: handleClickWrapper,
613
+ isSelected: isSelected,
614
+ onResolve: onResolve,
615
+ onError: onError,
616
+ testId: testId,
617
+ actionOptions: actionOptions,
618
+ CompetitorPrompt: CompetitorPrompt,
619
+ hideIconLoadingSkeleton: hideIconLoadingSkeleton
620
+ });
621
+ case 'embed':
622
+ return /*#__PURE__*/React.createElement(EmbedCardComponent, {
623
+ id: id,
624
+ url: url,
625
+ renderers: renderers,
626
+ cardState: state,
627
+ iframeUrlType: embedIframeUrlType,
628
+ handleAuthorize: services.length && handleAuthorize || undefined,
629
+ handleErrorRetry: handleRetry,
630
+ handleFrameClick: handleClickWrapper,
631
+ handleInvoke: handleInvoke,
632
+ isSelected: isSelected,
633
+ frameStyle: frameStyle,
634
+ platform: platform,
635
+ onResolve: onResolve,
636
+ onError: onError,
637
+ testId: testId,
638
+ inheritDimensions: inheritDimensions,
639
+ actionOptions: actionOptions,
640
+ ref: embedIframeRef,
641
+ onIframeDwell: onIframeDwell,
642
+ onIframeFocus: onIframeFocus,
643
+ onIframeMouseEnter: onIframeMouseEnter,
644
+ onIframeMouseLeave: onIframeMouseLeave,
645
+ CompetitorPrompt: CompetitorPrompt,
646
+ hideIconLoadingSkeleton: hideIconLoadingSkeleton
647
+ });
648
+ }
649
+ }
650
+ var CardWithUrlContentComponent = componentWithFG('rovo_chat_embed_card_dwell_and_hover_metrics', ComponentUpdated, Component);
327
651
  export var CardWithUrlContent = function CardWithUrlContent(props) {
328
652
  var display = isFlexibleUiCard(props.children, props === null || props === void 0 ? void 0 : props.ui) ? CardDisplay.Flexible : props.appearance;
329
653
  return /*#__PURE__*/React.createElement(SmartLinkModalProvider, null, /*#__PURE__*/React.createElement(SmartLinkAnalyticsContext, {
330
654
  url: props.url,
331
655
  id: props.id,
332
656
  display: display
333
- }, /*#__PURE__*/React.createElement(Component, props)));
657
+ }, /*#__PURE__*/React.createElement(CardWithUrlContentComponent, props)));
334
658
  };
@@ -3,12 +3,15 @@ import _extends from "@babel/runtime/helpers/extends";
3
3
  import "./ExpandedFrame.compiled.css";
4
4
  import * as React from 'react';
5
5
  import { ax, ix } from "@compiled/react/runtime";
6
+ // eslint-disable-next-line no-unused-vars
7
+
6
8
  import { fg } from '@atlaskit/platform-feature-flags';
9
+ import { componentWithFG } from '@atlaskit/platform-feature-flags-react';
7
10
  import Tooltip from '@atlaskit/tooltip';
8
11
  import { useMouseDownEvent } from '../../../state/analytics/useLinkClicked';
9
12
  import { handleClickCommon } from '../../common/utils';
10
13
  import { className } from './styled';
11
- export var ExpandedFrame = function ExpandedFrame(_ref) {
14
+ var ExpandedFrame = function ExpandedFrame(_ref) {
12
15
  var _ref$isPlaceholder = _ref.isPlaceholder,
13
16
  isPlaceholder = _ref$isPlaceholder === void 0 ? false : _ref$isPlaceholder,
14
17
  children = _ref.children,
@@ -104,4 +107,94 @@ var styles = {
104
107
  contentStyle: "_19itdlqj _2rko12b0 _1reo15vq _18m915vq _v56414au _bfhkhp5a _16jlkb7n _4t3i1osq _1pbykb7n",
105
108
  contentInteractiveActiveBorder: "_1jhm1tt7",
106
109
  contentOverflowAuto: "_1reo1wug _18m91wug"
107
- };
110
+ };
111
+ var ExpandedFrameUpdated = function ExpandedFrameUpdated(_ref2) {
112
+ var _ref2$isPlaceholder = _ref2.isPlaceholder,
113
+ isPlaceholder = _ref2$isPlaceholder === void 0 ? false : _ref2$isPlaceholder,
114
+ children = _ref2.children,
115
+ onClick = _ref2.onClick,
116
+ icon = _ref2.icon,
117
+ text = _ref2.text,
118
+ isSelected = _ref2.isSelected,
119
+ _ref2$frameStyle = _ref2.frameStyle,
120
+ frameStyle = _ref2$frameStyle === void 0 ? 'showOnHover' : _ref2$frameStyle,
121
+ href = _ref2.href,
122
+ minWidth = _ref2.minWidth,
123
+ maxWidth = _ref2.maxWidth,
124
+ _ref2$testId = _ref2.testId,
125
+ testId = _ref2$testId === void 0 ? 'expanded-frame' : _ref2$testId,
126
+ inheritDimensions = _ref2.inheritDimensions,
127
+ _ref2$allowScrollBar = _ref2.allowScrollBar,
128
+ allowScrollBar = _ref2$allowScrollBar === void 0 ? false : _ref2$allowScrollBar,
129
+ _ref2$setOverflow = _ref2.setOverflow,
130
+ setOverflow = _ref2$setOverflow === void 0 ? true : _ref2$setOverflow,
131
+ CompetitorPrompt = _ref2.CompetitorPrompt,
132
+ onContentMouseEnter = _ref2.onContentMouseEnter,
133
+ onContentMouseLeave = _ref2.onContentMouseLeave;
134
+ var isInteractive = function isInteractive() {
135
+ return !isPlaceholder && (Boolean(href) || Boolean(onClick));
136
+ };
137
+ var handleClick = function handleClick(event) {
138
+ return handleClickCommon(event, onClick);
139
+ };
140
+ var handleMouseDown = useMouseDownEvent();
141
+
142
+ // Note: cleanup fg based on results of prompt_whiteboard_competitor_link experiment
143
+ var CompetitorPromptComponent = CompetitorPrompt && href && fg('prompt_whiteboard_competitor_link_gate') ? /*#__PURE__*/React.createElement(CompetitorPrompt, {
144
+ sourceUrl: href,
145
+ linkType: "embed"
146
+ }) : null;
147
+ var renderHeader = function renderHeader() {
148
+ return frameStyle !== 'hide' && /*#__PURE__*/React.createElement("div", {
149
+ className: ax([styles.header, "embed-header"])
150
+ }, /*#__PURE__*/React.createElement("div", {
151
+ className: ax([styles.leftSection])
152
+ }, /*#__PURE__*/React.createElement("div", {
153
+ className: ax([styles.headerIcon])
154
+ }, icon), /*#__PURE__*/React.createElement("div", {
155
+ className: ax([styles.tooltipWrapper])
156
+ }, !isPlaceholder && /*#__PURE__*/React.createElement(Tooltip, {
157
+ content: text,
158
+ hideTooltipOnMouseDown: true
159
+ }, /*#__PURE__*/React.createElement("a", {
160
+ href: href,
161
+ onClick: handleClick,
162
+ onMouseDown: handleMouseDown,
163
+ className: ax([styles.headerAnchor])
164
+ }, text)))), CompetitorPromptComponent);
165
+ };
166
+ var interactive = isInteractive();
167
+ var showBackgroundAlways = frameStyle === 'show' || isSelected && frameStyle !== 'hide';
168
+ var showBackgroundOnHover = interactive && frameStyle !== 'hide';
169
+ var renderContent = function renderContent() {
170
+ return /*#__PURE__*/React.createElement("div", {
171
+ "data-testid": "embed-content-wrapper",
172
+ // This fixes an issue with input fields in cross domain iframes (ie. databases and jira fields from different domains)
173
+ // See: HOT-107830
174
+ contentEditable: false,
175
+ onMouseEnter: onContentMouseEnter,
176
+ onMouseLeave: onContentMouseLeave,
177
+ onFocus: onContentMouseEnter,
178
+ onBlur: onContentMouseLeave,
179
+ className: ax([styles.contentStyle, setOverflow && allowScrollBar && styles.contentOverflowAuto, interactive && !showBackgroundAlways && !showBackgroundOnHover && styles.contentInteractiveActiveBorder])
180
+ }, children);
181
+ };
182
+ return /*#__PURE__*/React.createElement("div", _extends({
183
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
184
+ className: ax([styles.linkWrapper, inheritDimensions && styles.linkWrapperInheritDimensions, isSelected && frameStyle !== 'hide' && styles.linkWrapperSelected, showBackgroundAlways && styles.linkWrapperBorderAndBackground, showBackgroundOnHover && !showBackgroundAlways && styles.linkWrapperInteractiveNotHidden, className]),
185
+ style: {
186
+ minWidth: minWidth ? "".concat(minWidth, "px") : '',
187
+ maxWidth: maxWidth ? "".concat(maxWidth, "px") : ''
188
+ },
189
+ "data-testid": testId,
190
+ "data-trello-do-not-use-override": testId
191
+ // Due to limitations of testing library, we can't assert ::after
192
+ ,
193
+ "data-is-selected": isSelected
194
+ }, (isPlaceholder || !href) && {
195
+ 'data-wrapper-type': 'default',
196
+ 'data-is-interactive': isInteractive()
197
+ }), renderHeader(), renderContent());
198
+ };
199
+ var ExpandedFrameWithFG = componentWithFG('rovo_chat_embed_card_dwell_and_hover_metrics', ExpandedFrameUpdated, ExpandedFrame);
200
+ export { ExpandedFrameWithFG as ExpandedFrame };
@@ -3,6 +3,9 @@ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
3
  import "./Frame.compiled.css";
4
4
  import { ax, ix } from "@compiled/react/runtime";
5
5
  import React, { useEffect, useRef, useState } from 'react';
6
+
7
+ // eslint-disable-next-line no-unused-vars
8
+
6
9
  import { di } from 'react-magnetic-di';
7
10
  import { getIframeSandboxAttribute } from '../../../utils';
8
11
  import { IFrame } from './IFrame';
@@ -103,10 +106,127 @@ export var Frame = /*#__PURE__*/React.forwardRef(function (_ref, iframeRef) {
103
106
  "data-testid": "".concat(testId, "-frame"),
104
107
  "data-iframe-loaded": isIframeLoaded,
105
108
  onMouseEnter: function onMouseEnter() {
106
- return setMouseOver(true);
109
+ setMouseOver(true);
110
+ },
111
+ onMouseLeave: function onMouseLeave() {
112
+ setMouseOver(false);
113
+ },
114
+ allowFullScreen: true,
115
+ scrolling: "yes",
116
+ allow: "autoplay; encrypted-media; clipboard-write",
117
+ onLoad: function onLoad() {
118
+ setIframeLoaded(true);
119
+ },
120
+ sandbox: getIframeSandboxAttribute(isTrusted),
121
+ title: title,
122
+ extensionKey: extensionKey,
123
+ className: ax(["_19itidpf _1reo15vq _18m915vq _2rkofajl _154iidpf _1ltvidpf _1bsb1osq _4t3i1osq _kqswh2mm"])
124
+ }));
125
+ });
126
+ export var FrameUpdated = /*#__PURE__*/React.forwardRef(function (_ref2, iframeRef) {
127
+ var url = _ref2.url,
128
+ _ref2$isTrusted = _ref2.isTrusted,
129
+ isTrusted = _ref2$isTrusted === void 0 ? false : _ref2$isTrusted,
130
+ testId = _ref2.testId,
131
+ onIframeDwell = _ref2.onIframeDwell,
132
+ onIframeFocus = _ref2.onIframeFocus,
133
+ onIframeMouseEnter = _ref2.onIframeMouseEnter,
134
+ onIframeMouseLeave = _ref2.onIframeMouseLeave,
135
+ isMouseOverProp = _ref2.isMouseOver,
136
+ title = _ref2.title,
137
+ extensionKey = _ref2.extensionKey;
138
+ var _useState1 = useState(false),
139
+ _useState10 = _slicedToArray(_useState1, 2),
140
+ isIframeLoaded = _useState10[0],
141
+ setIframeLoaded = _useState10[1];
142
+ var _useState11 = useState(false),
143
+ _useState12 = _slicedToArray(_useState11, 2),
144
+ isMouseOver = _useState12[0],
145
+ setMouseOver = _useState12[1];
146
+ var _useState13 = useState(document.hasFocus()),
147
+ _useState14 = _slicedToArray(_useState13, 2),
148
+ isWindowFocused = _useState14[0],
149
+ setWindowFocused = _useState14[1];
150
+
151
+ // Use prop if provided (from wrapper), otherwise use local state (for backward compatibility)
152
+ var effectiveMouseOver = isMouseOverProp !== undefined ? isMouseOverProp : isMouseOver;
153
+ var ref = useRef();
154
+ var mergedRef = mergeRefs([iframeRef, ref]);
155
+ var _useState15 = useState(0),
156
+ _useState16 = _slicedToArray(_useState15, 2),
157
+ percentVisible = _useState16[0],
158
+ setPercentVisible = _useState16[1];
159
+
160
+ /**
161
+ * These are the 'percent visible' thresholds at which the intersectionObserver will
162
+ * trigger a state change. Eg. when the user scrolls and moves from 74% to 76%, or
163
+ * vice versa. It's in a state object so that its static for the useEffect
164
+ */
165
+ var _useState17 = useState([0.75, 0.8, 0.85, 0.9, 0.95, 1]),
166
+ _useState18 = _slicedToArray(_useState17, 1),
167
+ threshold = _useState18[0];
168
+ useEffect(function () {
169
+ if (!ref || !ref.current) {
170
+ return;
171
+ }
172
+ var observer = new IntersectionObserver(function (entries) {
173
+ entries.forEach(function (entry) {
174
+ setPercentVisible(entry === null || entry === void 0 ? void 0 : entry.intersectionRatio);
175
+ });
176
+ }, {
177
+ threshold: threshold
178
+ });
179
+ observer.observe(ref.current);
180
+ return function () {
181
+ observer.disconnect();
182
+ };
183
+ }, [threshold, mergedRef]);
184
+ useEffect(function () {
185
+ // Initialize with current focus state
186
+ setWindowFocused(document.hasFocus());
187
+ var onBlur = function onBlur() {
188
+ setWindowFocused(false);
189
+ if (document.activeElement === ref.current) {
190
+ onIframeFocus && onIframeFocus();
191
+ }
192
+ };
193
+ var onFocus = function onFocus() {
194
+ setWindowFocused(true);
195
+ };
196
+ window.addEventListener('blur', onBlur);
197
+ window.addEventListener('focus', onFocus);
198
+ return function () {
199
+ window.removeEventListener('blur', onBlur);
200
+ window.removeEventListener('focus', onFocus);
201
+ };
202
+ }, [ref, onIframeFocus]);
203
+ if (!url) {
204
+ return null;
205
+ }
206
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(IframeDwellTracker, {
207
+ isIframeLoaded: isIframeLoaded,
208
+ isMouseOver: effectiveMouseOver,
209
+ isWindowFocused: isWindowFocused,
210
+ iframePercentVisible: percentVisible,
211
+ onIframeDwell: onIframeDwell
212
+ }), /*#__PURE__*/React.createElement(IFrame, {
213
+ childRef: mergedRef,
214
+ src: url,
215
+ "data-testid": "".concat(testId, "-frame"),
216
+ "data-iframe-loaded": isIframeLoaded,
217
+ onMouseEnter: function onMouseEnter() {
218
+ onIframeMouseEnter === null || onIframeMouseEnter === void 0 || onIframeMouseEnter();
219
+ // Use local state if prop not provided, otherwise prop takes precedence
220
+ if (isMouseOverProp === undefined) {
221
+ setMouseOver(true);
222
+ }
107
223
  },
108
224
  onMouseLeave: function onMouseLeave() {
109
- return setMouseOver(false);
225
+ onIframeMouseLeave === null || onIframeMouseLeave === void 0 || onIframeMouseLeave();
226
+ // Use local state if prop not provided, otherwise prop takes precedence
227
+ if (isMouseOverProp === undefined) {
228
+ setMouseOver(false);
229
+ }
110
230
  },
111
231
  allowFullScreen: true,
112
232
  scrolling: "yes",
@@ -4,6 +4,7 @@ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
4
4
  * @jsx jsx
5
5
  */
6
6
  import { useEffect, useRef, useState } from 'react';
7
+ import { fg } from '@atlaskit/platform-feature-flags';
7
8
  /**
8
9
  * A kind of cheap logarithmic backoff. Fire analytics after the user has
9
10
  * dwelled for 5 seconds, then 10 seconds, and so on.
@@ -44,11 +45,31 @@ export var IframeDwellTracker = function IframeDwellTracker(_ref) {
44
45
  };
45
46
  });
46
47
  };
47
- if (isIframeLoaded && isMouseOver && isWindowFocused && iframePercentVisible > 0.75) {
48
- if (dwellTimeoutId.current) {
49
- clearInterval(dwellTimeoutId.current);
48
+
49
+ // Require: iframe loaded, mouse over, and >75% visible
50
+ var isDwellAndHoverMetricsEnabled = fg('rovo_chat_embed_card_dwell_and_hover_metrics');
51
+ if (isDwellAndHoverMetricsEnabled) {
52
+ // Note: Removed isWindowFocused requirement as it's unreliable and prevents tracking
53
+ // The mouse over check is sufficient to indicate user engagement
54
+ var shouldTrack = isIframeLoaded && isMouseOver && iframePercentVisible > 0.75;
55
+ if (shouldTrack) {
56
+ if (dwellTimeoutId.current) {
57
+ clearInterval(dwellTimeoutId.current);
58
+ }
59
+ dwellTimeoutId.current = setInterval(incrementDwellTime, 1000);
60
+ } else {
61
+ if (dwellTimeoutId.current) {
62
+ clearInterval(dwellTimeoutId.current);
63
+ dwellTimeoutId.current = undefined;
64
+ }
65
+ }
66
+ } else {
67
+ if (isIframeLoaded && isMouseOver && isWindowFocused && iframePercentVisible > 0.75) {
68
+ if (dwellTimeoutId.current) {
69
+ clearInterval(dwellTimeoutId.current);
70
+ }
71
+ dwellTimeoutId.current = setInterval(incrementDwellTime, 1000);
50
72
  }
51
- dwellTimeoutId.current = setInterval(incrementDwellTime, 1000);
52
73
  }
53
74
  return function () {
54
75
  if (dwellTimeoutId.current) {