@atlaskit/smart-card 20.0.6 → 20.1.2

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 (66) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/cjs/state/actions/constants.js +4 -2
  3. package/dist/cjs/state/actions/index.js +51 -19
  4. package/dist/cjs/version.json +1 -1
  5. package/dist/cjs/view/BlockCard/index.js +29 -0
  6. package/dist/cjs/view/CardWithUrl/component.js +7 -2
  7. package/dist/cjs/view/CardWithUrl/loader.js +2 -0
  8. package/dist/cjs/view/EmbedCard/index.js +29 -0
  9. package/dist/cjs/view/FlexibleCard/components/common/loading-skeleton/index.js +6 -4
  10. package/dist/cjs/view/FlexibleCard/index.js +34 -7
  11. package/dist/cjs/view/HoverCard/components/HoverCardComponent.js +27 -4
  12. package/dist/cjs/view/HoverCard/components/HoverCardContent.js +32 -10
  13. package/dist/cjs/view/HoverCard/components/HoverCardLoadingView.js +46 -0
  14. package/dist/cjs/view/HoverCard/styled.js +10 -4
  15. package/dist/cjs/view/InlineCard/index.js +29 -0
  16. package/dist/es2019/state/actions/constants.js +2 -1
  17. package/dist/es2019/state/actions/index.js +46 -18
  18. package/dist/es2019/version.json +1 -1
  19. package/dist/es2019/view/BlockCard/index.js +29 -0
  20. package/dist/es2019/view/CardWithUrl/component.js +7 -2
  21. package/dist/es2019/view/CardWithUrl/loader.js +2 -0
  22. package/dist/es2019/view/EmbedCard/index.js +29 -0
  23. package/dist/es2019/view/FlexibleCard/components/common/loading-skeleton/index.js +38 -31
  24. package/dist/es2019/view/FlexibleCard/index.js +32 -7
  25. package/dist/es2019/view/HoverCard/components/HoverCardComponent.js +27 -4
  26. package/dist/es2019/view/HoverCard/components/HoverCardContent.js +28 -9
  27. package/dist/es2019/view/HoverCard/components/HoverCardLoadingView.js +32 -0
  28. package/dist/es2019/view/HoverCard/styled.js +12 -1
  29. package/dist/es2019/view/InlineCard/index.js +29 -0
  30. package/dist/esm/state/actions/constants.js +2 -1
  31. package/dist/esm/state/actions/index.js +51 -20
  32. package/dist/esm/version.json +1 -1
  33. package/dist/esm/view/BlockCard/index.js +29 -0
  34. package/dist/esm/view/CardWithUrl/component.js +7 -2
  35. package/dist/esm/view/CardWithUrl/loader.js +2 -0
  36. package/dist/esm/view/EmbedCard/index.js +29 -0
  37. package/dist/esm/view/FlexibleCard/components/common/loading-skeleton/index.js +6 -3
  38. package/dist/esm/view/FlexibleCard/index.js +33 -7
  39. package/dist/esm/view/HoverCard/components/HoverCardComponent.js +27 -4
  40. package/dist/esm/view/HoverCard/components/HoverCardContent.js +30 -10
  41. package/dist/esm/view/HoverCard/components/HoverCardLoadingView.js +31 -0
  42. package/dist/esm/view/HoverCard/styled.js +6 -3
  43. package/dist/esm/view/InlineCard/index.js +29 -0
  44. package/dist/types/state/actions/constants.d.ts +1 -0
  45. package/dist/types/state/actions/index.d.ts +1 -0
  46. package/dist/types/state/hooks/useSmartLink.d.ts +1 -0
  47. package/dist/types/view/BlockCard/types.d.ts +2 -0
  48. package/dist/types/view/Card/index.d.ts +1 -1
  49. package/dist/types/view/Card/types.d.ts +2 -1
  50. package/dist/types/view/CardWithUrl/component.d.ts +1 -1
  51. package/dist/types/view/CardWithUrl/types.d.ts +2 -1
  52. package/dist/types/view/EmbedCard/components/styled.d.ts +16 -16
  53. package/dist/types/view/EmbedCard/types.d.ts +2 -0
  54. package/dist/types/view/FlexibleCard/components/common/loading-skeleton/types.d.ts +8 -0
  55. package/dist/types/view/FlexibleCard/types.d.ts +9 -4
  56. package/dist/types/view/HoverCard/components/HoverCardLoadingView.d.ts +4 -0
  57. package/dist/types/view/HoverCard/styled.d.ts +3 -0
  58. package/dist/types/view/HoverCard/types.d.ts +7 -0
  59. package/dist/types/view/InlineCard/Frame/styled.d.ts +1 -1
  60. package/dist/types/view/InlineCard/Icon.d.ts +3 -3
  61. package/dist/types/view/InlineCard/IconAndTitleLayout/styled.d.ts +9 -9
  62. package/dist/types/view/InlineCard/ResolvingView/styled.d.ts +13 -5
  63. package/dist/types/view/InlineCard/styled.d.ts +8 -4
  64. package/dist/types/view/InlineCard/types.d.ts +2 -1
  65. package/dist/types/view/types.d.ts +6 -0
  66. package/package.json +4 -4
@@ -22,6 +22,10 @@ export const HoverCardComponent = ({
22
22
  const fadeOutTimeoutId = useRef();
23
23
  const fadeInTimeoutId = useRef();
24
24
  const cardOpenTime = useRef();
25
+ const mousePos = useRef({
26
+ x: 0,
27
+ y: 0
28
+ });
25
29
  const renderers = useSmartLinkRenderers();
26
30
  const linkState = useLinkState(url);
27
31
  const hoverDisplay = 'card';
@@ -66,7 +70,17 @@ export const HoverCardComponent = ({
66
70
  if (actionId === 'preview-content') {
67
71
  hideCard();
68
72
  }
69
- }, [hideCard]); // Stop hover preview content to propagate event to parent.
73
+ }, [hideCard]);
74
+ const setMousePosition = useCallback(event => {
75
+ if (isOpen) {
76
+ return;
77
+ }
78
+
79
+ mousePos.current = {
80
+ x: event.clientX,
81
+ y: event.clientY
82
+ };
83
+ }, [isOpen]); // Stop hover preview content to propagate event to parent.
70
84
 
71
85
  const onClick = useCallback(e => e.stopPropagation(), []);
72
86
  return jsx(Popup, {
@@ -92,10 +106,19 @@ export const HoverCardComponent = ({
92
106
  renderers: renderers,
93
107
  url: url
94
108
  })),
95
- trigger: triggerProps => jsx("span", _extends({}, triggerProps, {
109
+ trigger: triggerProps => jsx("span", {
96
110
  onMouseEnter: initShowCard,
97
- onMouseLeave: initHideCard
98
- }), children),
111
+ onMouseLeave: initHideCard,
112
+ onMouseMove: setMousePosition
113
+ }, jsx("span", _extends({}, triggerProps, {
114
+ css: {
115
+ position: 'sticky'
116
+ },
117
+ style: {
118
+ left: mousePos.current.x,
119
+ top: mousePos.current.y
120
+ }
121
+ })), children),
99
122
  zIndex: 511 // Temporary fix for Confluence inline comment on editor mod has z-index of 500, Jira issue view has z-index of 510
100
123
 
101
124
  });
@@ -4,12 +4,14 @@ import React, { useCallback, useEffect, useMemo } from 'react';
4
4
  import { ActionName, SmartLinkPosition, SmartLinkSize } from '../../../constants';
5
5
  import extractPreview from '../../../extractors/flexible/extract-preview';
6
6
  import { extractMetadata } from '../../../extractors/hover/extractMetadata';
7
+ import { useSmartCardActions } from '../../../state/actions';
7
8
  import { getExtensionKey } from '../../../state/helpers';
8
9
  import { isSpecialEvent } from '../../../utils';
9
10
  import FlexibleCard from '../../FlexibleCard';
10
11
  import { FooterBlock, MetadataBlock, PreviewBlock, SnippetBlock, TitleBlock } from '../../FlexibleCard/components/blocks';
11
12
  import { flexibleUiOptions, footerBlockCss, metadataBlockCss, titleBlockCss } from '../styled';
12
13
  import { getSimulatedMetadata, SMART_CARD_ANALYTICS_DISPLAY } from '../utils';
14
+ import HoverCardLoadingView from './HoverCardLoadingView';
13
15
  export const getOpenAction = (url, analytics, onActionClick) => ({
14
16
  name: ActionName.CustomAction,
15
17
  icon: /*#__PURE__*/React.createElement(ShortcutIcon, {
@@ -41,6 +43,7 @@ export const toFooterActions = (cardActions, onActionClick) => cardActions.map(a
41
43
  }));
42
44
 
43
45
  const HoverCardContent = ({
46
+ id = '',
44
47
  analytics,
45
48
  cardActions = [],
46
49
  cardState,
@@ -51,6 +54,10 @@ const HoverCardContent = ({
51
54
  }) => {
52
55
  var _cardState$details;
53
56
 
57
+ const actions = useSmartCardActions(id, url, analytics);
58
+ useEffect(() => {
59
+ actions.loadMetadata();
60
+ }, [actions]);
54
61
  useEffect(() => {
55
62
  // Since hover preview only render on resolved status,
56
63
  // there is no need to check for statuses.
@@ -73,21 +80,33 @@ const HoverCardContent = ({
73
80
  } = extractMetadata(getSimulatedMetadata(extensionKey, types));
74
81
  const titleMaxLines = subtitle && subtitle.length > 0 ? 1 : 2;
75
82
  const body = data && extractPreview(data) ? /*#__PURE__*/React.createElement(PreviewBlock, null) : /*#__PURE__*/React.createElement(SnippetBlock, null);
76
- return /*#__PURE__*/React.createElement(FlexibleCard, {
77
- cardState: cardState,
78
- onClick: onClick,
79
- onResolve: onResolve,
80
- renderers: renderers,
81
- ui: flexibleUiOptions,
82
- url: url
83
- }, /*#__PURE__*/React.createElement(TitleBlock, {
83
+ const titleBlockProps = {
84
84
  actions: titleActions,
85
85
  maxLines: titleMaxLines,
86
86
  overrideCss: titleBlockCss,
87
87
  size: SmartLinkSize.Large,
88
88
  position: SmartLinkPosition.Center,
89
89
  subtitle: subtitle
90
- }), /*#__PURE__*/React.createElement(MetadataBlock, {
90
+ };
91
+ const flexibleCardProps = {
92
+ cardState: cardState,
93
+ onClick: onClick,
94
+ onResolve: onResolve,
95
+ renderers: renderers,
96
+ ui: flexibleUiOptions,
97
+ url: url,
98
+ children: {}
99
+ };
100
+
101
+ if (cardState.metadataStatus === 'pending') {
102
+ return /*#__PURE__*/React.createElement(HoverCardLoadingView, {
103
+ flexibleCardProps: flexibleCardProps,
104
+ titleBlockProps: titleBlockProps
105
+ });
106
+ } // if metadataStatus is 'errored' simply render using data available (normal path)
107
+
108
+
109
+ return /*#__PURE__*/React.createElement(FlexibleCard, flexibleCardProps, /*#__PURE__*/React.createElement(TitleBlock, titleBlockProps), /*#__PURE__*/React.createElement(MetadataBlock, {
91
110
  primary: primary,
92
111
  secondary: secondary,
93
112
  size: SmartLinkSize.Large,
@@ -0,0 +1,32 @@
1
+ /** @jsx jsx */
2
+ import { jsx } from '@emotion/core';
3
+ import React from 'react';
4
+ import LoadingSkeleton from '../../FlexibleCard/components/common/loading-skeleton';
5
+ import FlexibleCard from '../../FlexibleCard';
6
+ import { TitleBlock } from '../../FlexibleCard/components/blocks';
7
+ import { CARD_WIDTH_REM, loadingViewContainer, skeletonContainer } from '../styled';
8
+
9
+ const HoverCardLoadingView = ({
10
+ flexibleCardProps,
11
+ titleBlockProps
12
+ }) => {
13
+ const lineHeightRem = 1.25;
14
+ const skeletonWidth = CARD_WIDTH_REM - 2;
15
+ return jsx("div", {
16
+ css: loadingViewContainer,
17
+ "data-testid": "hover-card-loading-view"
18
+ }, jsx(FlexibleCard, flexibleCardProps, jsx(TitleBlock, titleBlockProps)), jsx("div", {
19
+ css: skeletonContainer
20
+ }, jsx("span", null), jsx(LoadingSkeleton, {
21
+ width: skeletonWidth,
22
+ height: lineHeightRem
23
+ }), jsx(LoadingSkeleton, {
24
+ width: skeletonWidth,
25
+ height: lineHeightRem * 3
26
+ }), jsx(LoadingSkeleton, {
27
+ width: skeletonWidth,
28
+ height: lineHeightRem
29
+ })));
30
+ };
31
+
32
+ export default HoverCardLoadingView;
@@ -8,6 +8,7 @@ export const flexibleUiOptions = {
8
8
  theme: SmartLinkTheme.Black,
9
9
  hidePadding: true
10
10
  };
11
+ export const CARD_WIDTH_REM = 24;
11
12
  const blockGap = '0.5rem';
12
13
  const elementGap = '0.5rem';
13
14
  const separatorCss = css`
@@ -19,7 +20,7 @@ export const HoverCardContainer = css`
19
20
  background: none;
20
21
  border-width: 0;
21
22
  box-sizing: border-box;
22
- width: 24rem;
23
+ width: ${CARD_WIDTH_REM}rem;
23
24
  padding: 1rem;
24
25
 
25
26
  .${loadingPlaceholderClassName} {
@@ -70,4 +71,14 @@ export const metadataBlockCss = css`
70
71
  `;
71
72
  export const footerBlockCss = css`
72
73
  padding-top: 0.25rem;
74
+ `;
75
+ export const loadingViewContainer = css`
76
+ display: flex;
77
+ flex-direction: column;
78
+ `;
79
+ export const skeletonContainer = css`
80
+ display: flex;
81
+ flex-direction: column;
82
+ gap: 0.625rem;
83
+ align-items: center;
73
84
  `;
@@ -20,6 +20,7 @@ export const InlineCard = ({
20
20
  isSelected,
21
21
  renderers,
22
22
  onResolve,
23
+ onError,
23
24
  testId,
24
25
  inlinePreloaderStyle,
25
26
  showHoverPreview
@@ -64,6 +65,13 @@ export const InlineCard = ({
64
65
  }));
65
66
 
66
67
  case 'unauthorized':
68
+ if (onError) {
69
+ onError({
70
+ url,
71
+ status
72
+ });
73
+ }
74
+
67
75
  const provider = extractProvider(cardDetails);
68
76
  return /*#__PURE__*/React.createElement(InlineCardUnauthorizedView, {
69
77
  icon: provider && provider.icon,
@@ -75,6 +83,13 @@ export const InlineCard = ({
75
83
  });
76
84
 
77
85
  case 'forbidden':
86
+ if (onError) {
87
+ onError({
88
+ url,
89
+ status
90
+ });
91
+ }
92
+
78
93
  const providerForbidden = extractProvider(cardDetails);
79
94
  const cardMetadata = (_details$meta = details === null || details === void 0 ? void 0 : details.meta) !== null && _details$meta !== void 0 ? _details$meta : getUnauthorizedJsonLd().meta;
80
95
  const requestAccessContext = extractRequestAccessContext({
@@ -94,6 +109,13 @@ export const InlineCard = ({
94
109
  });
95
110
 
96
111
  case 'not_found':
112
+ if (onError) {
113
+ onError({
114
+ url,
115
+ status
116
+ });
117
+ }
118
+
97
119
  const providerNotFound = extractProvider(cardDetails);
98
120
  return /*#__PURE__*/React.createElement(InlineCardErroredView, {
99
121
  url: url,
@@ -106,6 +128,13 @@ export const InlineCard = ({
106
128
 
107
129
  case 'fallback':
108
130
  case 'errored':
131
+ if (onError) {
132
+ onError({
133
+ url,
134
+ status
135
+ });
136
+ }
137
+
109
138
  return /*#__PURE__*/React.createElement(CardLinkView, {
110
139
  link: url,
111
140
  isSelected: isSelected,
@@ -2,4 +2,5 @@ export var ANALYTICS_RESOLVING = 'resolving';
2
2
  export var ANALYTICS_ERROR = 'errored';
3
3
  export var ANALYTICS_FALLBACK = 'fallback';
4
4
  export var ERROR_MESSAGE_OAUTH = 'Provider.authFlow is not set to OAuth2.';
5
- export var ERROR_MESSAGE_FATAL = 'Fatal error resolving URL';
5
+ export var ERROR_MESSAGE_FATAL = 'Fatal error resolving URL';
6
+ export var ERROR_MESSAGE_METADATA = 'Error resolving metadata.';
@@ -2,11 +2,10 @@ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
2
  import _regeneratorRuntime from "@babel/runtime/regenerator";
3
3
  import { useMemo, useCallback } from 'react';
4
4
  import { auth } from '@atlaskit/outbound-auth-flow-client';
5
- import { APIError } from '@atlaskit/linking-common';
6
5
  import { useSmartLinkContext } from '@atlaskit/link-provider';
7
- import { ACTION_PENDING, ACTION_RESOLVED, ACTION_ERROR, ACTION_ERROR_FALLBACK, ACTION_RELOADING, cardAction } from '@atlaskit/linking-common';
6
+ import { ACTION_PENDING, ACTION_RESOLVED, ACTION_ERROR, ACTION_ERROR_FALLBACK, ACTION_RELOADING, ACTION_UPDATE_METADATA_STATUS, cardAction, APIError } from '@atlaskit/linking-common';
8
7
  import { getDefinitionId, getByDefinitionId, getServices, getStatus, getExtensionKey } from '../helpers';
9
- import { ERROR_MESSAGE_OAUTH, ERROR_MESSAGE_FATAL } from './constants';
8
+ import { ERROR_MESSAGE_OAUTH, ERROR_MESSAGE_FATAL, ERROR_MESSAGE_METADATA } from './constants';
10
9
  import * as measure from '../../utils/performance';
11
10
  import { getUnauthorizedJsonLd } from '../../utils/jsonld';
12
11
  import { addMetadataToExperience } from '../analytics';
@@ -24,11 +23,23 @@ export var useSmartCardActions = function useSmartCardActions(id, url, analytics
24
23
  details: undefined
25
24
  },
26
25
  details = _ref.details,
27
- status = _ref.status;
26
+ status = _ref.status,
27
+ metadataStatus = _ref.metadataStatus;
28
28
 
29
29
  var hasAuthFlowSupported = config.authFlow !== 'disabled';
30
30
  var hasData = !!(details && details.data);
31
- var handleResolvedLinkError = useCallback(function (url, error, response) {
31
+ var setMetadataStatus = useCallback(function (metadataStatus) {
32
+ dispatch(cardAction(ACTION_UPDATE_METADATA_STATUS, {
33
+ url: url
34
+ }, undefined, undefined, metadataStatus));
35
+ }, [dispatch, url]);
36
+ var handleResolvedLinkError = useCallback(function (url, error, response, isMetadataRequest) {
37
+ // If metadata request then set metadata status, return and do not alter link status
38
+ if (isMetadataRequest) {
39
+ setMetadataStatus('errored');
40
+ return;
41
+ }
42
+
32
43
  if (error.kind === 'fatal') {
33
44
  // If there's no previous data in the store for this URL, then bail
34
45
  // out and let the editor handle fallbacks (returns to a blue link).
@@ -65,25 +76,34 @@ export var useSmartCardActions = function useSmartCardActions(id, url, analytics
65
76
  }, undefined, error));
66
77
  }
67
78
  }
68
- }, [hasData, status, dispatch, details]);
79
+ }, [setMetadataStatus, hasData, status, dispatch, details]);
69
80
  var handleResolvedLinkResponse = useCallback(function (resourceUrl, response) {
70
81
  var isReloading = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
82
+ var isMetadataRequest = arguments.length > 3 ? arguments[3] : undefined;
71
83
  var hostname = new URL(resourceUrl).hostname;
72
84
  var nextStatus = response ? getStatus(response) : 'fatal'; // If we require authorization & do not have an authFlow available,
73
85
  // throw an error and render as a normal blue link.
74
86
 
75
87
  if ((nextStatus === 'unauthorized' || nextStatus === 'forbidden') && !hasAuthFlowSupported) {
76
- handleResolvedLinkError(resourceUrl, new APIError('fallback', hostname, ERROR_MESSAGE_OAUTH), response);
88
+ handleResolvedLinkError(resourceUrl, new APIError('fallback', hostname, ERROR_MESSAGE_OAUTH), response, isMetadataRequest);
77
89
  return;
78
90
  } // Handle any other errors
79
91
 
80
92
 
81
93
  if (nextStatus === 'fatal') {
82
- handleResolvedLinkError(resourceUrl, new APIError('fatal', hostname, ERROR_MESSAGE_FATAL));
94
+ handleResolvedLinkError(resourceUrl, new APIError('fatal', hostname, ERROR_MESSAGE_FATAL), undefined, isMetadataRequest);
95
+ return;
96
+ } // Handle errors of any other kind in the metadata request response
97
+
98
+
99
+ if (isMetadataRequest && nextStatus !== 'resolved') {
100
+ handleResolvedLinkError(resourceUrl, new APIError('error', hostname, ERROR_MESSAGE_METADATA), response, isMetadataRequest);
83
101
  return;
84
- } // Dispatch Analytics and resolved card action - including unauthorized states.
102
+ } //if a link resolves normally, metadata will also always be resolved
85
103
 
86
104
 
105
+ setMetadataStatus('resolved'); // Dispatch Analytics and resolved card action - including unauthorized states.
106
+
87
107
  if (isReloading) {
88
108
  dispatch(cardAction(ACTION_RELOADING, {
89
109
  url: resourceUrl
@@ -91,12 +111,13 @@ export var useSmartCardActions = function useSmartCardActions(id, url, analytics
91
111
  } else {
92
112
  dispatch(cardAction(ACTION_RESOLVED, {
93
113
  url: resourceUrl
94
- }, response));
114
+ }, response, undefined, undefined, isMetadataRequest));
95
115
  }
96
- }, [dispatch, handleResolvedLinkError, hasAuthFlowSupported]);
116
+ }, [dispatch, handleResolvedLinkError, hasAuthFlowSupported, setMetadataStatus]);
97
117
  var resolve = useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
98
118
  var resourceUrl,
99
119
  isReloading,
120
+ isMetadataRequest,
100
121
  _args = arguments;
101
122
  return _regeneratorRuntime.wrap(function _callee$(_context) {
102
123
  while (1) {
@@ -104,24 +125,25 @@ export var useSmartCardActions = function useSmartCardActions(id, url, analytics
104
125
  case 0:
105
126
  resourceUrl = _args.length > 0 && _args[0] !== undefined ? _args[0] : url;
106
127
  isReloading = _args.length > 1 && _args[1] !== undefined ? _args[1] : false;
128
+ isMetadataRequest = _args.length > 2 && _args[2] !== undefined ? _args[2] : false;
107
129
 
108
- if (!(isReloading || !hasData)) {
109
- _context.next = 6;
130
+ if (!(isReloading || !hasData || isMetadataRequest)) {
131
+ _context.next = 7;
110
132
  break;
111
133
  }
112
134
 
113
135
  return _context.abrupt("return", connections.client.fetchData(resourceUrl).then(function (response) {
114
- return handleResolvedLinkResponse(resourceUrl, response, isReloading);
136
+ return handleResolvedLinkResponse(resourceUrl, response, isReloading, isMetadataRequest);
115
137
  }).catch(function (error) {
116
- return handleResolvedLinkError(resourceUrl, error);
138
+ return handleResolvedLinkError(resourceUrl, error, undefined, isMetadataRequest);
117
139
  }));
118
140
 
119
- case 6:
141
+ case 7:
120
142
  addMetadataToExperience('smart-link-rendered', id, {
121
143
  cached: true
122
144
  });
123
145
 
124
- case 7:
146
+ case 8:
125
147
  case "end":
126
148
  return _context.stop();
127
149
  }
@@ -133,10 +155,11 @@ export var useSmartCardActions = function useSmartCardActions(id, url, analytics
133
155
  dispatch(cardAction(ACTION_PENDING, {
134
156
  url: url
135
157
  }));
158
+ setMetadataStatus('pending');
136
159
  }
137
160
 
138
161
  return resolve();
139
- }, [url, resolve, details, dispatch]);
162
+ }, [details, resolve, dispatch, url, setMetadataStatus]);
140
163
  var reload = useCallback(function () {
141
164
  var definitionId = getDefinitionId(details);
142
165
 
@@ -148,6 +171,13 @@ export var useSmartCardActions = function useSmartCardActions(id, url, analytics
148
171
  resolve(url, true);
149
172
  }
150
173
  }, [url, details, getState, resolve]);
174
+ var loadMetadata = useCallback(function () {
175
+ //metadataStatus will be undefined for SSR links only
176
+ if (metadataStatus === undefined) {
177
+ setMetadataStatus('pending');
178
+ return resolve(url, false, true);
179
+ }
180
+ }, [metadataStatus, resolve, setMetadataStatus, url]);
151
181
  var authorize = useCallback(function (appearance) {
152
182
  var definitionId = getDefinitionId(details);
153
183
  var extensionKey = getExtensionKey(details);
@@ -243,7 +273,8 @@ export var useSmartCardActions = function useSmartCardActions(id, url, analytics
243
273
  register: register,
244
274
  reload: reload,
245
275
  authorize: authorize,
246
- invoke: invoke
276
+ invoke: invoke,
277
+ loadMetadata: loadMetadata
247
278
  };
248
- }, [register, reload, authorize, invoke]);
279
+ }, [register, reload, authorize, invoke, loadMetadata]);
249
280
  };
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/smart-card",
3
- "version": "20.0.6",
3
+ "version": "20.1.2",
4
4
  "sideEffects": false
5
5
  }
@@ -31,6 +31,7 @@ export var BlockCard = function BlockCard(_ref) {
31
31
  renderers = _ref.renderers,
32
32
  isSelected = _ref.isSelected,
33
33
  onResolve = _ref.onResolve,
34
+ onError = _ref.onError,
34
35
  testId = _ref.testId,
35
36
  showActions = _ref.showActions,
36
37
  platform = _ref.platform;
@@ -68,6 +69,13 @@ export var BlockCard = function BlockCard(_ref) {
68
69
  }));
69
70
 
70
71
  case 'unauthorized':
72
+ if (onError) {
73
+ onError({
74
+ url: url,
75
+ status: status
76
+ });
77
+ }
78
+
71
79
  var unauthorizedViewProps = extractBlockProps(data, meta, extractorOpts);
72
80
  return /*#__PURE__*/React.createElement(BlockCardUnauthorisedView, _extends({}, unauthorizedViewProps, {
73
81
  isSelected: isSelected,
@@ -78,6 +86,13 @@ export var BlockCard = function BlockCard(_ref) {
78
86
  }));
79
87
 
80
88
  case 'forbidden':
89
+ if (onError) {
90
+ onError({
91
+ url: url,
92
+ status: status
93
+ });
94
+ }
95
+
81
96
  var forbiddenViewProps = extractBlockProps(data, meta, extractorOpts);
82
97
  var cardMetadata = (_details$meta = details === null || details === void 0 ? void 0 : details.meta) !== null && _details$meta !== void 0 ? _details$meta : getUnauthorizedJsonLd().meta;
83
98
  var requestAccessContext = extractRequestAccessContext({
@@ -94,6 +109,13 @@ export var BlockCard = function BlockCard(_ref) {
94
109
  }));
95
110
 
96
111
  case 'not_found':
112
+ if (onError) {
113
+ onError({
114
+ url: url,
115
+ status: status
116
+ });
117
+ }
118
+
97
119
  var notFoundViewProps = extractBlockProps(data, meta, extractorOpts);
98
120
  return /*#__PURE__*/React.createElement(BlockCardNotFoundView, _extends({}, notFoundViewProps, {
99
121
  isSelected: isSelected,
@@ -103,6 +125,13 @@ export var BlockCard = function BlockCard(_ref) {
103
125
 
104
126
  case 'fallback':
105
127
  case 'errored':
128
+ if (onError) {
129
+ onError({
130
+ url: url,
131
+ status: status
132
+ });
133
+ }
134
+
106
135
  if (authFlow && authFlow === 'disabled') {
107
136
  return /*#__PURE__*/React.createElement(CardLinkView, {
108
137
  link: url,
@@ -20,6 +20,7 @@ export function CardWithUrlContent(_ref) {
20
20
  appearance = _ref.appearance,
21
21
  dispatchAnalytics = _ref.dispatchAnalytics,
22
22
  onResolve = _ref.onResolve,
23
+ onError = _ref.onError,
23
24
  testId = _ref.testId,
24
25
  showActions = _ref.showActions,
25
26
  inheritDimensions = _ref.inheritDimensions,
@@ -97,7 +98,7 @@ export function CardWithUrlContent(_ref) {
97
98
  if (isFinalState(state.status)) {
98
99
  analytics.ui.renderSuccessEvent(appearance, state.status, id, definitionId, extensionKey);
99
100
  }
100
- }, [appearance, state.details, state.status, url, definitionId, extensionKey, analytics.ui, id]);
101
+ }, [appearance, state.status, url, definitionId, extensionKey, analytics.ui, id]);
101
102
 
102
103
  if (isFlexibleUi) {
103
104
  var cardState = state;
@@ -121,7 +122,8 @@ export function CardWithUrlContent(_ref) {
121
122
  ui: ui,
122
123
  url: url,
123
124
  testId: testId,
124
- onResolve: onResolve
125
+ onResolve: onResolve,
126
+ onError: onError
125
127
  }, children);
126
128
  } // We have to keep this last to prevent hook order from being violated
127
129
 
@@ -141,6 +143,7 @@ export function CardWithUrlContent(_ref) {
141
143
  handleFrameClick: handleClickWrapper,
142
144
  isSelected: isSelected,
143
145
  onResolve: onResolve,
146
+ onError: onError,
144
147
  testId: testId,
145
148
  inlinePreloaderStyle: inlinePreloaderStyle,
146
149
  showHoverPreview: showHoverPreview
@@ -160,6 +163,7 @@ export function CardWithUrlContent(_ref) {
160
163
  handleAnalytics: dispatchAnalytics,
161
164
  isSelected: isSelected,
162
165
  onResolve: onResolve,
166
+ onError: onError,
163
167
  testId: testId,
164
168
  showActions: showActions,
165
169
  platform: platform
@@ -179,6 +183,7 @@ export function CardWithUrlContent(_ref) {
179
183
  isFrameVisible: isFrameVisible,
180
184
  platform: platform,
181
185
  onResolve: onResolve,
186
+ onError: onError,
182
187
  testId: testId,
183
188
  inheritDimensions: inheritDimensions,
184
189
  showActions: showActions,
@@ -32,6 +32,7 @@ export function CardWithURLRenderer(props) {
32
32
  onClick = props.onClick,
33
33
  container = props.container,
34
34
  onResolve = props.onResolve,
35
+ onError = props.onError,
35
36
  testId = props.testId,
36
37
  showActions = props.showActions,
37
38
  inheritDimensions = props.inheritDimensions,
@@ -89,6 +90,7 @@ export function CardWithURLRenderer(props) {
89
90
  dispatchAnalytics: dispatchAnalytics,
90
91
  container: container,
91
92
  onResolve: onResolve,
93
+ onError: onError,
92
94
  testId: testId,
93
95
  showActions: showActions,
94
96
  inheritDimensions: inheritDimensions,
@@ -30,6 +30,7 @@ export var EmbedCard = /*#__PURE__*/React.forwardRef(function (_ref, iframeRef)
30
30
  isFrameVisible = _ref.isFrameVisible,
31
31
  platform = _ref.platform,
32
32
  onResolve = _ref.onResolve,
33
+ onError = _ref.onError,
33
34
  testId = _ref.testId,
34
35
  inheritDimensions = _ref.inheritDimensions;
35
36
  var data = details && details.data || getEmptyJsonLd();
@@ -89,6 +90,13 @@ export var EmbedCard = /*#__PURE__*/React.forwardRef(function (_ref, iframeRef)
89
90
  }
90
91
 
91
92
  case 'unauthorized':
93
+ if (onError) {
94
+ onError({
95
+ url: url,
96
+ status: status
97
+ });
98
+ }
99
+
92
100
  var unauthorisedViewProps = extractEmbedProps(data, meta, platform);
93
101
  return /*#__PURE__*/React.createElement(EmbedCardUnauthorisedView, _extends({}, unauthorisedViewProps, {
94
102
  isSelected: isSelected,
@@ -98,6 +106,13 @@ export var EmbedCard = /*#__PURE__*/React.forwardRef(function (_ref, iframeRef)
98
106
  }));
99
107
 
100
108
  case 'forbidden':
109
+ if (onError) {
110
+ onError({
111
+ url: url,
112
+ status: status
113
+ });
114
+ }
115
+
101
116
  var forbiddenViewProps = extractEmbedProps(data, meta, platform);
102
117
  var cardMetadata = (_details$meta = details === null || details === void 0 ? void 0 : details.meta) !== null && _details$meta !== void 0 ? _details$meta : getUnauthorizedJsonLd().meta;
103
118
  var requestAccessContext = extractRequestAccessContext({
@@ -114,6 +129,13 @@ export var EmbedCard = /*#__PURE__*/React.forwardRef(function (_ref, iframeRef)
114
129
  }));
115
130
 
116
131
  case 'not_found':
132
+ if (onError) {
133
+ onError({
134
+ url: url,
135
+ status: status
136
+ });
137
+ }
138
+
117
139
  var notFoundViewProps = extractEmbedProps(data, meta, platform);
118
140
  return /*#__PURE__*/React.createElement(EmbedCardNotFoundView, _extends({}, notFoundViewProps, {
119
141
  isSelected: isSelected,
@@ -123,6 +145,13 @@ export var EmbedCard = /*#__PURE__*/React.forwardRef(function (_ref, iframeRef)
123
145
 
124
146
  case 'fallback':
125
147
  case 'errored':
148
+ if (onError) {
149
+ onError({
150
+ url: url,
151
+ status: status
152
+ });
153
+ }
154
+
126
155
  return /*#__PURE__*/React.createElement(EmbedCardErroredView, {
127
156
  onRetry: handleErrorRetry,
128
157
  inheritDimensions: inheritDimensions,
@@ -6,11 +6,14 @@ var _templateObject, _templateObject2;
6
6
  import React from 'react';
7
7
  import { css, jsx, keyframes } from '@emotion/core';
8
8
  import { token } from '@atlaskit/tokens';
9
- var animationNameStyles = keyframes(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n 0% { background-position: -20px 0; }\n 100% { background-position: 20px 0; }\n"])));
10
- var styles = css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n border-radius: 2px;\n user-select: none;\n background: ", ";\n // TODO: https://product-fabric.atlassian.net/browse/DSP-4236\n background-image: linear-gradient(\n to right,\n ", " 0%,\n ", " 20%,\n ", " 40%,\n ", "100%\n );\n background-repeat: no-repeat;\n background-size: 280% 100%;\n display: inline-block;\n\n animation-duration: 1s;\n animation-fill-mode: forwards;\n animation-iteration-count: infinite;\n animation-name: ", ";\n animation-timing-function: linear;\n"])), token('color.skeleton.subtle', '#f6f7f8'), token('color.skeleton.subtle', '#f6f7f8'), token('color.skeleton', '#edeef1'), token('color.skeleton.subtle', '#f6f7f8'), token('color.skeleton.subtle', '#f6f7f8'), animationNameStyles);
11
9
 
12
10
  var LoadingSkeleton = function LoadingSkeleton(_ref) {
13
- var testId = _ref.testId;
11
+ var testId = _ref.testId,
12
+ width = _ref.width,
13
+ height = _ref.height;
14
+ var animationWidth = width ? "".concat(width, "rem") : '20px';
15
+ var animationNameStyles = keyframes(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n 0% { background-position: -", " 0; }\n 100% { background-position: ", " 0; }\n "])), animationWidth, animationWidth);
16
+ var styles = css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n width: ", "rem;\n height: ", "rem;\n border-radius: 2px;\n user-select: none;\n background: ", ";\n // TODO: https://product-fabric.atlassian.net/browse/DSP-4236\n background-image: linear-gradient(\n to right,\n ", " 0%,\n ", " 20%,\n ", " 40%,\n ", "100%\n );\n background-repeat: no-repeat;\n background-size: 280% 100%;\n display: inline-block;\n\n animation-duration: 1s;\n animation-fill-mode: forwards;\n animation-iteration-count: infinite;\n animation-name: ", ";\n animation-timing-function: linear;\n "])), width, height, token('color.skeleton.subtle', '#f6f7f8'), token('color.skeleton.subtle', '#f6f7f8'), token('color.skeleton', '#edeef1'), token('color.skeleton.subtle', '#f6f7f8'), token('color.skeleton.subtle', '#f6f7f8'), animationNameStyles);
14
17
  return jsx("span", {
15
18
  css: styles,
16
19
  "data-testid": testId