@atlaskit/smart-card 20.0.4 → 20.1.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.
- package/CHANGELOG.md +41 -0
- package/dist/cjs/state/actions/constants.js +4 -2
- package/dist/cjs/state/actions/index.js +51 -19
- package/dist/cjs/state/analytics/useSmartLinkAnalytics.js +26 -6
- package/dist/cjs/version.json +1 -1
- package/dist/cjs/view/BlockCard/index.js +29 -0
- package/dist/cjs/view/CardWithUrl/component.js +7 -2
- package/dist/cjs/view/CardWithUrl/loader.js +7 -9
- package/dist/cjs/view/EmbedCard/index.js +29 -0
- package/dist/cjs/view/FlexibleCard/components/blocks/element-group/index.js +2 -2
- package/dist/cjs/view/FlexibleCard/index.js +34 -7
- package/dist/cjs/view/HoverCard/components/HoverCardComponent.js +143 -0
- package/dist/cjs/view/HoverCard/components/{hover-card-content.js → HoverCardContent.js} +29 -25
- package/dist/cjs/view/HoverCard/index.js +25 -126
- package/dist/cjs/view/HoverCard/styled.js +11 -1
- package/dist/cjs/view/HoverCard/utils.js +3 -3
- package/dist/cjs/view/InlineCard/index.js +29 -0
- package/dist/es2019/state/actions/constants.js +2 -1
- package/dist/es2019/state/actions/index.js +46 -18
- package/dist/es2019/state/analytics/useSmartLinkAnalytics.js +24 -7
- package/dist/es2019/version.json +1 -1
- package/dist/es2019/view/BlockCard/index.js +29 -0
- package/dist/es2019/view/CardWithUrl/component.js +7 -2
- package/dist/es2019/view/CardWithUrl/loader.js +8 -10
- package/dist/es2019/view/EmbedCard/index.js +29 -0
- package/dist/es2019/view/FlexibleCard/components/blocks/element-group/index.js +4 -0
- package/dist/es2019/view/FlexibleCard/index.js +32 -7
- package/dist/es2019/view/HoverCard/components/HoverCardComponent.js +102 -0
- package/dist/es2019/view/HoverCard/components/{hover-card-content.js → HoverCardContent.js} +18 -21
- package/dist/es2019/view/HoverCard/index.js +26 -97
- package/dist/es2019/view/HoverCard/styled.js +8 -0
- package/dist/es2019/view/HoverCard/utils.js +1 -1
- package/dist/es2019/view/InlineCard/index.js +29 -0
- package/dist/esm/state/actions/constants.js +2 -1
- package/dist/esm/state/actions/index.js +51 -20
- package/dist/esm/state/analytics/useSmartLinkAnalytics.js +27 -7
- package/dist/esm/version.json +1 -1
- package/dist/esm/view/BlockCard/index.js +29 -0
- package/dist/esm/view/CardWithUrl/component.js +7 -2
- package/dist/esm/view/CardWithUrl/loader.js +8 -10
- package/dist/esm/view/EmbedCard/index.js +29 -0
- package/dist/esm/view/FlexibleCard/components/blocks/element-group/index.js +2 -2
- package/dist/esm/view/FlexibleCard/index.js +33 -7
- package/dist/esm/view/HoverCard/components/HoverCardComponent.js +114 -0
- package/dist/esm/view/HoverCard/components/{hover-card-content.js → HoverCardContent.js} +20 -22
- package/dist/esm/view/HoverCard/index.js +24 -109
- package/dist/esm/view/HoverCard/styled.js +8 -0
- package/dist/esm/view/HoverCard/utils.js +1 -1
- package/dist/esm/view/InlineCard/index.js +29 -0
- package/dist/types/state/actions/constants.d.ts +1 -0
- package/dist/types/state/actions/index.d.ts +1 -0
- package/dist/types/state/analytics/useSmartLinkAnalytics.d.ts +9 -0
- package/dist/types/state/hooks/useSmartLink.d.ts +3 -0
- package/dist/types/view/BlockCard/types.d.ts +2 -0
- package/dist/types/view/Card/index.d.ts +1 -1
- package/dist/types/view/Card/types.d.ts +2 -1
- package/dist/types/view/CardWithUrl/component.d.ts +1 -1
- package/dist/types/view/CardWithUrl/types.d.ts +2 -1
- package/dist/types/view/EmbedCard/components/styled.d.ts +16 -16
- package/dist/types/view/EmbedCard/types.d.ts +2 -0
- package/dist/types/view/FlexibleCard/types.d.ts +9 -4
- package/dist/types/view/HoverCard/components/HoverCardComponent.d.ts +4 -0
- package/dist/types/view/HoverCard/components/HoverCardContent.d.ts +9 -0
- package/dist/types/view/HoverCard/index.d.ts +2 -4
- package/dist/types/view/HoverCard/styled.d.ts +2 -0
- package/dist/types/view/HoverCard/types.d.ts +8 -0
- package/dist/types/view/HoverCard/utils.d.ts +1 -1
- package/dist/types/view/InlineCard/Frame/styled.d.ts +1 -1
- package/dist/types/view/InlineCard/Icon.d.ts +3 -3
- package/dist/types/view/InlineCard/IconAndTitleLayout/styled.d.ts +9 -9
- package/dist/types/view/InlineCard/ResolvingView/styled.d.ts +13 -5
- package/dist/types/view/InlineCard/styled.d.ts +8 -4
- package/dist/types/view/InlineCard/types.d.ts +2 -1
- package/dist/types/view/types.d.ts +6 -0
- package/package.json +4 -4
- package/dist/types/view/HoverCard/components/hover-card-content.d.ts +0 -4
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { useMemo, useCallback } from 'react';
|
|
2
2
|
import { auth } from '@atlaskit/outbound-auth-flow-client';
|
|
3
|
-
import { APIError } from '@atlaskit/linking-common';
|
|
4
3
|
import { useSmartLinkContext } from '@atlaskit/link-provider';
|
|
5
|
-
import { ACTION_PENDING, ACTION_RESOLVED, ACTION_ERROR, ACTION_ERROR_FALLBACK, ACTION_RELOADING, cardAction } from '@atlaskit/linking-common';
|
|
4
|
+
import { ACTION_PENDING, ACTION_RESOLVED, ACTION_ERROR, ACTION_ERROR_FALLBACK, ACTION_RELOADING, ACTION_UPDATE_METADATA_STATUS, cardAction, APIError } from '@atlaskit/linking-common';
|
|
6
5
|
import { getDefinitionId, getByDefinitionId, getServices, getStatus, getExtensionKey } from '../helpers';
|
|
7
|
-
import { ERROR_MESSAGE_OAUTH, ERROR_MESSAGE_FATAL } from './constants';
|
|
6
|
+
import { ERROR_MESSAGE_OAUTH, ERROR_MESSAGE_FATAL, ERROR_MESSAGE_METADATA } from './constants';
|
|
8
7
|
import * as measure from '../../utils/performance';
|
|
9
8
|
import { getUnauthorizedJsonLd } from '../../utils/jsonld';
|
|
10
9
|
import { addMetadataToExperience } from '../analytics';
|
|
@@ -20,14 +19,26 @@ export const useSmartCardActions = (id, url, analytics) => {
|
|
|
20
19
|
} = store;
|
|
21
20
|
const {
|
|
22
21
|
details,
|
|
23
|
-
status
|
|
22
|
+
status,
|
|
23
|
+
metadataStatus
|
|
24
24
|
} = getState()[url] || {
|
|
25
25
|
status: 'pending',
|
|
26
26
|
details: undefined
|
|
27
27
|
};
|
|
28
28
|
const hasAuthFlowSupported = config.authFlow !== 'disabled';
|
|
29
29
|
const hasData = !!(details && details.data);
|
|
30
|
-
const
|
|
30
|
+
const setMetadataStatus = useCallback(metadataStatus => {
|
|
31
|
+
dispatch(cardAction(ACTION_UPDATE_METADATA_STATUS, {
|
|
32
|
+
url
|
|
33
|
+
}, undefined, undefined, metadataStatus));
|
|
34
|
+
}, [dispatch, url]);
|
|
35
|
+
const handleResolvedLinkError = useCallback((url, error, response, isMetadataRequest) => {
|
|
36
|
+
// If metadata request then set metadata status, return and do not alter link status
|
|
37
|
+
if (isMetadataRequest) {
|
|
38
|
+
setMetadataStatus('errored');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
31
42
|
if (error.kind === 'fatal') {
|
|
32
43
|
// If there's no previous data in the store for this URL, then bail
|
|
33
44
|
// out and let the editor handle fallbacks (returns to a blue link).
|
|
@@ -64,24 +75,32 @@ export const useSmartCardActions = (id, url, analytics) => {
|
|
|
64
75
|
}, undefined, error));
|
|
65
76
|
}
|
|
66
77
|
}
|
|
67
|
-
}, [hasData, status, dispatch, details]);
|
|
68
|
-
const handleResolvedLinkResponse = useCallback((resourceUrl, response, isReloading = false) => {
|
|
78
|
+
}, [setMetadataStatus, hasData, status, dispatch, details]);
|
|
79
|
+
const handleResolvedLinkResponse = useCallback((resourceUrl, response, isReloading = false, isMetadataRequest) => {
|
|
69
80
|
const hostname = new URL(resourceUrl).hostname;
|
|
70
81
|
const nextStatus = response ? getStatus(response) : 'fatal'; // If we require authorization & do not have an authFlow available,
|
|
71
82
|
// throw an error and render as a normal blue link.
|
|
72
83
|
|
|
73
84
|
if ((nextStatus === 'unauthorized' || nextStatus === 'forbidden') && !hasAuthFlowSupported) {
|
|
74
|
-
handleResolvedLinkError(resourceUrl, new APIError('fallback', hostname, ERROR_MESSAGE_OAUTH), response);
|
|
85
|
+
handleResolvedLinkError(resourceUrl, new APIError('fallback', hostname, ERROR_MESSAGE_OAUTH), response, isMetadataRequest);
|
|
75
86
|
return;
|
|
76
87
|
} // Handle any other errors
|
|
77
88
|
|
|
78
89
|
|
|
79
90
|
if (nextStatus === 'fatal') {
|
|
80
|
-
handleResolvedLinkError(resourceUrl, new APIError('fatal', hostname, ERROR_MESSAGE_FATAL));
|
|
91
|
+
handleResolvedLinkError(resourceUrl, new APIError('fatal', hostname, ERROR_MESSAGE_FATAL), undefined, isMetadataRequest);
|
|
81
92
|
return;
|
|
82
|
-
} //
|
|
93
|
+
} // Handle errors of any other kind in the metadata request response
|
|
83
94
|
|
|
84
95
|
|
|
96
|
+
if (isMetadataRequest && nextStatus !== 'resolved') {
|
|
97
|
+
handleResolvedLinkError(resourceUrl, new APIError('error', hostname, ERROR_MESSAGE_METADATA), response, isMetadataRequest);
|
|
98
|
+
return;
|
|
99
|
+
} //if a link resolves normally, metadata will also always be resolved
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
setMetadataStatus('resolved'); // Dispatch Analytics and resolved card action - including unauthorized states.
|
|
103
|
+
|
|
85
104
|
if (isReloading) {
|
|
86
105
|
dispatch(cardAction(ACTION_RELOADING, {
|
|
87
106
|
url: resourceUrl
|
|
@@ -89,15 +108,15 @@ export const useSmartCardActions = (id, url, analytics) => {
|
|
|
89
108
|
} else {
|
|
90
109
|
dispatch(cardAction(ACTION_RESOLVED, {
|
|
91
110
|
url: resourceUrl
|
|
92
|
-
}, response));
|
|
111
|
+
}, response, undefined, undefined, isMetadataRequest));
|
|
93
112
|
}
|
|
94
|
-
}, [dispatch, handleResolvedLinkError, hasAuthFlowSupported]);
|
|
95
|
-
const resolve = useCallback(async (resourceUrl = url, isReloading = false) => {
|
|
113
|
+
}, [dispatch, handleResolvedLinkError, hasAuthFlowSupported, setMetadataStatus]);
|
|
114
|
+
const resolve = useCallback(async (resourceUrl = url, isReloading = false, isMetadataRequest = false) => {
|
|
96
115
|
// Request JSON-LD data for the card from ORS, iff it has extended
|
|
97
116
|
// its cache lifespan OR there is no data for it currently. Once the data
|
|
98
117
|
// has come back asynchronously, dispatch the resolved action for the card.
|
|
99
|
-
if (isReloading || !hasData) {
|
|
100
|
-
return connections.client.fetchData(resourceUrl).then(response => handleResolvedLinkResponse(resourceUrl, response, isReloading)).catch(error => handleResolvedLinkError(resourceUrl, error));
|
|
118
|
+
if (isReloading || !hasData || isMetadataRequest) {
|
|
119
|
+
return connections.client.fetchData(resourceUrl).then(response => handleResolvedLinkResponse(resourceUrl, response, isReloading, isMetadataRequest)).catch(error => handleResolvedLinkError(resourceUrl, error, undefined, isMetadataRequest));
|
|
101
120
|
} else {
|
|
102
121
|
addMetadataToExperience('smart-link-rendered', id, {
|
|
103
122
|
cached: true
|
|
@@ -109,10 +128,11 @@ export const useSmartCardActions = (id, url, analytics) => {
|
|
|
109
128
|
dispatch(cardAction(ACTION_PENDING, {
|
|
110
129
|
url
|
|
111
130
|
}));
|
|
131
|
+
setMetadataStatus('pending');
|
|
112
132
|
}
|
|
113
133
|
|
|
114
134
|
return resolve();
|
|
115
|
-
}, [
|
|
135
|
+
}, [details, resolve, dispatch, url, setMetadataStatus]);
|
|
116
136
|
const reload = useCallback(() => {
|
|
117
137
|
const definitionId = getDefinitionId(details);
|
|
118
138
|
|
|
@@ -122,6 +142,13 @@ export const useSmartCardActions = (id, url, analytics) => {
|
|
|
122
142
|
resolve(url, true);
|
|
123
143
|
}
|
|
124
144
|
}, [url, details, getState, resolve]);
|
|
145
|
+
const loadMetadata = useCallback(() => {
|
|
146
|
+
//metadataStatus will be undefined for SSR links only
|
|
147
|
+
if (metadataStatus === undefined) {
|
|
148
|
+
setMetadataStatus('pending');
|
|
149
|
+
return resolve(url, false, true);
|
|
150
|
+
}
|
|
151
|
+
}, [metadataStatus, resolve, setMetadataStatus, url]);
|
|
125
152
|
const authorize = useCallback(appearance => {
|
|
126
153
|
const definitionId = getDefinitionId(details);
|
|
127
154
|
const extensionKey = getExtensionKey(details);
|
|
@@ -187,6 +214,7 @@ export const useSmartCardActions = (id, url, analytics) => {
|
|
|
187
214
|
register,
|
|
188
215
|
reload,
|
|
189
216
|
authorize,
|
|
190
|
-
invoke
|
|
191
|
-
|
|
217
|
+
invoke,
|
|
218
|
+
loadMetadata
|
|
219
|
+
}), [register, reload, authorize, invoke, loadMetadata]);
|
|
192
220
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import { getUrl } from '@atlaskit/linking-common';
|
|
3
|
-
import { uiAuthEvent, uiAuthAlternateAccountEvent, uiCardClickedEvent, uiActionClickedEvent, uiClosedAuthEvent, uiRenderSuccessEvent, uiHoverCardViewedEvent, uiHoverCardDismissedEvent, uiHoverCardOpenLinkClickedEvent, invokeSucceededEvent, invokeFailedEvent, connectSucceededEvent, connectFailedEvent, trackAppAccountConnected, screenAuthPopupEvent, instrumentEvent } from '../../utils/analytics';
|
|
3
|
+
import { uiAuthEvent, uiAuthAlternateAccountEvent, uiCardClickedEvent, uiActionClickedEvent, uiClosedAuthEvent, uiRenderSuccessEvent, uiRenderFailedEvent, uiHoverCardViewedEvent, uiHoverCardDismissedEvent, uiHoverCardOpenLinkClickedEvent, invokeSucceededEvent, invokeFailedEvent, connectSucceededEvent, connectFailedEvent, trackAppAccountConnected, screenAuthPopupEvent, instrumentEvent } from '../../utils/analytics';
|
|
4
4
|
import { failUfoExperience, startUfoExperience, succeedUfoExperience } from './ufoExperiences';
|
|
5
5
|
import { getDefinitionId, getExtensionKey, getResourceType, getSubproduct, getProduct } from '../helpers';
|
|
6
6
|
import { useSmartLinkContext } from '@atlaskit/link-provider';
|
|
@@ -20,12 +20,13 @@ export const useSmartLinkAnalytics = (url, dispatchAnalytics, id, defaultLocatio
|
|
|
20
20
|
const {
|
|
21
21
|
store
|
|
22
22
|
} = useSmartLinkContext();
|
|
23
|
-
const state = getUrl(store, url);
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
23
|
+
const state = store ? getUrl(store, url) : undefined;
|
|
24
|
+
const details = state ? state.details : undefined;
|
|
25
|
+
const extractedDefinitionId = getDefinitionId(details);
|
|
26
|
+
const extractedExtensionKey = getExtensionKey(details);
|
|
27
|
+
const extractedResourceType = getResourceType(details);
|
|
28
|
+
const extractedSubproduct = getSubproduct(details);
|
|
29
|
+
const extractedProduct = getProduct(details);
|
|
29
30
|
/** Contains all ui analytics events */
|
|
30
31
|
|
|
31
32
|
const ui = useMemo(() => ({
|
|
@@ -124,6 +125,22 @@ export const useSmartLinkAnalytics = (url, dispatchAnalytics, id, defaultLocatio
|
|
|
124
125
|
dispatchAnalytics(uiRenderSuccessEvent(display, status, definitionId, extensionKey));
|
|
125
126
|
},
|
|
126
127
|
|
|
128
|
+
/**
|
|
129
|
+
* This fires an event that represents when a Smart Link renders unsuccessfuly.
|
|
130
|
+
* @param display Whether the card was an Inline, Block, Embed or Flexible UI.
|
|
131
|
+
* @param id The unique ID for this Smart Link.
|
|
132
|
+
* @param error: An error representing why the Smart Link render failed.
|
|
133
|
+
* @param errorInfo: Additional details about the error including the stack trace.
|
|
134
|
+
*/
|
|
135
|
+
renderFailedEvent: (display, id = defaultId, error, errorInfo) => {
|
|
136
|
+
// Start and fail the smart-link-rendered experience. If it has already
|
|
137
|
+
// been started nothing happens.
|
|
138
|
+
startUfoExperience('smart-link-rendered', id);
|
|
139
|
+
failUfoExperience('smart-link-rendered', id);
|
|
140
|
+
failUfoExperience('smart-link-authenticated', id);
|
|
141
|
+
dispatchAnalytics(uiRenderFailedEvent(display, error, errorInfo));
|
|
142
|
+
},
|
|
143
|
+
|
|
127
144
|
/**
|
|
128
145
|
* This fires an event that represents a hover preview being opened.
|
|
129
146
|
* @param hoverDisplay Whether the hover preview was displayed as a card or embed.
|
package/dist/es2019/version.json
CHANGED
|
@@ -30,6 +30,7 @@ export const BlockCard = ({
|
|
|
30
30
|
renderers,
|
|
31
31
|
isSelected,
|
|
32
32
|
onResolve,
|
|
33
|
+
onError,
|
|
33
34
|
testId,
|
|
34
35
|
showActions,
|
|
35
36
|
platform
|
|
@@ -70,6 +71,13 @@ export const BlockCard = ({
|
|
|
70
71
|
}));
|
|
71
72
|
|
|
72
73
|
case 'unauthorized':
|
|
74
|
+
if (onError) {
|
|
75
|
+
onError({
|
|
76
|
+
url,
|
|
77
|
+
status
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
73
81
|
const unauthorizedViewProps = extractBlockProps(data, meta, extractorOpts);
|
|
74
82
|
return /*#__PURE__*/React.createElement(BlockCardUnauthorisedView, _extends({}, unauthorizedViewProps, {
|
|
75
83
|
isSelected: isSelected,
|
|
@@ -80,6 +88,13 @@ export const BlockCard = ({
|
|
|
80
88
|
}));
|
|
81
89
|
|
|
82
90
|
case 'forbidden':
|
|
91
|
+
if (onError) {
|
|
92
|
+
onError({
|
|
93
|
+
url,
|
|
94
|
+
status
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
83
98
|
const forbiddenViewProps = extractBlockProps(data, meta, extractorOpts);
|
|
84
99
|
const cardMetadata = (_details$meta = details === null || details === void 0 ? void 0 : details.meta) !== null && _details$meta !== void 0 ? _details$meta : getUnauthorizedJsonLd().meta;
|
|
85
100
|
const requestAccessContext = extractRequestAccessContext({
|
|
@@ -96,6 +111,13 @@ export const BlockCard = ({
|
|
|
96
111
|
}));
|
|
97
112
|
|
|
98
113
|
case 'not_found':
|
|
114
|
+
if (onError) {
|
|
115
|
+
onError({
|
|
116
|
+
url,
|
|
117
|
+
status
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
99
121
|
const notFoundViewProps = extractBlockProps(data, meta, extractorOpts);
|
|
100
122
|
return /*#__PURE__*/React.createElement(BlockCardNotFoundView, _extends({}, notFoundViewProps, {
|
|
101
123
|
isSelected: isSelected,
|
|
@@ -105,6 +127,13 @@ export const BlockCard = ({
|
|
|
105
127
|
|
|
106
128
|
case 'fallback':
|
|
107
129
|
case 'errored':
|
|
130
|
+
if (onError) {
|
|
131
|
+
onError({
|
|
132
|
+
url,
|
|
133
|
+
status
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
108
137
|
if (authFlow && authFlow === 'disabled') {
|
|
109
138
|
return /*#__PURE__*/React.createElement(CardLinkView, {
|
|
110
139
|
link: url,
|
|
@@ -20,6 +20,7 @@ export function CardWithUrlContent({
|
|
|
20
20
|
appearance,
|
|
21
21
|
dispatchAnalytics,
|
|
22
22
|
onResolve,
|
|
23
|
+
onError,
|
|
23
24
|
testId,
|
|
24
25
|
showActions,
|
|
25
26
|
inheritDimensions,
|
|
@@ -91,7 +92,7 @@ export function CardWithUrlContent({
|
|
|
91
92
|
if (isFinalState(state.status)) {
|
|
92
93
|
analytics.ui.renderSuccessEvent(appearance, state.status, id, definitionId, extensionKey);
|
|
93
94
|
}
|
|
94
|
-
}, [appearance, state.
|
|
95
|
+
}, [appearance, state.status, url, definitionId, extensionKey, analytics.ui, id]);
|
|
95
96
|
|
|
96
97
|
if (isFlexibleUi) {
|
|
97
98
|
let cardState = state;
|
|
@@ -115,7 +116,8 @@ export function CardWithUrlContent({
|
|
|
115
116
|
ui: ui,
|
|
116
117
|
url: url,
|
|
117
118
|
testId: testId,
|
|
118
|
-
onResolve: onResolve
|
|
119
|
+
onResolve: onResolve,
|
|
120
|
+
onError: onError
|
|
119
121
|
}, children);
|
|
120
122
|
} // We have to keep this last to prevent hook order from being violated
|
|
121
123
|
|
|
@@ -135,6 +137,7 @@ export function CardWithUrlContent({
|
|
|
135
137
|
handleFrameClick: handleClickWrapper,
|
|
136
138
|
isSelected: isSelected,
|
|
137
139
|
onResolve: onResolve,
|
|
140
|
+
onError: onError,
|
|
138
141
|
testId: testId,
|
|
139
142
|
inlinePreloaderStyle: inlinePreloaderStyle,
|
|
140
143
|
showHoverPreview: showHoverPreview
|
|
@@ -154,6 +157,7 @@ export function CardWithUrlContent({
|
|
|
154
157
|
handleAnalytics: dispatchAnalytics,
|
|
155
158
|
isSelected: isSelected,
|
|
156
159
|
onResolve: onResolve,
|
|
160
|
+
onError: onError,
|
|
157
161
|
testId: testId,
|
|
158
162
|
showActions: showActions,
|
|
159
163
|
platform: platform
|
|
@@ -173,6 +177,7 @@ export function CardWithUrlContent({
|
|
|
173
177
|
isFrameVisible: isFrameVisible,
|
|
174
178
|
platform: platform,
|
|
175
179
|
onResolve: onResolve,
|
|
180
|
+
onError: onError,
|
|
176
181
|
testId: testId,
|
|
177
182
|
inheritDimensions: inheritDimensions,
|
|
178
183
|
showActions: showActions,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React, { lazy, useEffect, useState, Suspense, useCallback } from 'react';
|
|
2
2
|
import uuid from 'uuid';
|
|
3
|
-
import {
|
|
3
|
+
import { fireSmartLinkEvent } from '../../utils/analytics';
|
|
4
4
|
import { clearMarks, clearMeasures } from '../../utils/performance';
|
|
5
5
|
import { ErrorBoundary } from 'react-error-boundary';
|
|
6
|
-
import {
|
|
6
|
+
import { useSmartLinkAnalytics } from '../../state/analytics';
|
|
7
7
|
import { LoadingCardLink } from './component-lazy/LazyFallback';
|
|
8
8
|
const LazyCardWithUrlContent = /*#__PURE__*/lazy(() => import(
|
|
9
9
|
/* webpackChunkName: "@atlaskit-internal_smartcard-urlcardcontent" */
|
|
@@ -25,6 +25,7 @@ export function CardWithURLRenderer(props) {
|
|
|
25
25
|
onClick,
|
|
26
26
|
container,
|
|
27
27
|
onResolve,
|
|
28
|
+
onError,
|
|
28
29
|
testId,
|
|
29
30
|
showActions,
|
|
30
31
|
inheritDimensions,
|
|
@@ -50,6 +51,7 @@ export function CardWithURLRenderer(props) {
|
|
|
50
51
|
|
|
51
52
|
fireSmartLinkEvent(analyticsPayload, createAnalyticsEvent);
|
|
52
53
|
}, [appearance, createAnalyticsEvent]);
|
|
54
|
+
const analytics = useSmartLinkAnalytics(url !== null && url !== void 0 ? url : '', dispatchAnalytics, id);
|
|
53
55
|
const logError = useCallback((error, info) => {
|
|
54
56
|
const {
|
|
55
57
|
componentStack
|
|
@@ -65,15 +67,10 @@ export function CardWithURLRenderer(props) {
|
|
|
65
67
|
else {
|
|
66
68
|
const errorInfo = {
|
|
67
69
|
componentStack
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
startUfoExperience('smart-link-rendered', id);
|
|
72
|
-
failUfoExperience('smart-link-rendered', id);
|
|
73
|
-
failUfoExperience('smart-link-authenticated', id);
|
|
74
|
-
dispatchAnalytics(uiRenderFailedEvent(appearance, error, errorInfo));
|
|
70
|
+
};
|
|
71
|
+
analytics.ui.renderFailedEvent(appearance, id, error, errorInfo);
|
|
75
72
|
}
|
|
76
|
-
}, [
|
|
73
|
+
}, [analytics.ui, appearance, id]);
|
|
77
74
|
|
|
78
75
|
if (!url) {
|
|
79
76
|
throw new Error('@atlaskit/smart-card: url property is missing.');
|
|
@@ -89,6 +86,7 @@ export function CardWithURLRenderer(props) {
|
|
|
89
86
|
dispatchAnalytics,
|
|
90
87
|
container,
|
|
91
88
|
onResolve,
|
|
89
|
+
onError,
|
|
92
90
|
testId,
|
|
93
91
|
showActions,
|
|
94
92
|
inheritDimensions,
|
|
@@ -29,6 +29,7 @@ export const EmbedCard = /*#__PURE__*/React.forwardRef(({
|
|
|
29
29
|
isFrameVisible,
|
|
30
30
|
platform,
|
|
31
31
|
onResolve,
|
|
32
|
+
onError,
|
|
32
33
|
testId,
|
|
33
34
|
inheritDimensions
|
|
34
35
|
}, iframeRef) => {
|
|
@@ -91,6 +92,13 @@ export const EmbedCard = /*#__PURE__*/React.forwardRef(({
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
case 'unauthorized':
|
|
95
|
+
if (onError) {
|
|
96
|
+
onError({
|
|
97
|
+
url,
|
|
98
|
+
status
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
94
102
|
const unauthorisedViewProps = extractEmbedProps(data, meta, platform);
|
|
95
103
|
return /*#__PURE__*/React.createElement(EmbedCardUnauthorisedView, _extends({}, unauthorisedViewProps, {
|
|
96
104
|
isSelected: isSelected,
|
|
@@ -100,6 +108,13 @@ export const EmbedCard = /*#__PURE__*/React.forwardRef(({
|
|
|
100
108
|
}));
|
|
101
109
|
|
|
102
110
|
case 'forbidden':
|
|
111
|
+
if (onError) {
|
|
112
|
+
onError({
|
|
113
|
+
url,
|
|
114
|
+
status
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
103
118
|
const forbiddenViewProps = extractEmbedProps(data, meta, platform);
|
|
104
119
|
const cardMetadata = (_details$meta = details === null || details === void 0 ? void 0 : details.meta) !== null && _details$meta !== void 0 ? _details$meta : getUnauthorizedJsonLd().meta;
|
|
105
120
|
const requestAccessContext = extractRequestAccessContext({
|
|
@@ -116,6 +131,13 @@ export const EmbedCard = /*#__PURE__*/React.forwardRef(({
|
|
|
116
131
|
}));
|
|
117
132
|
|
|
118
133
|
case 'not_found':
|
|
134
|
+
if (onError) {
|
|
135
|
+
onError({
|
|
136
|
+
url,
|
|
137
|
+
status
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
119
141
|
const notFoundViewProps = extractEmbedProps(data, meta, platform);
|
|
120
142
|
return /*#__PURE__*/React.createElement(EmbedCardNotFoundView, _extends({}, notFoundViewProps, {
|
|
121
143
|
isSelected: isSelected,
|
|
@@ -125,6 +147,13 @@ export const EmbedCard = /*#__PURE__*/React.forwardRef(({
|
|
|
125
147
|
|
|
126
148
|
case 'fallback':
|
|
127
149
|
case 'errored':
|
|
150
|
+
if (onError) {
|
|
151
|
+
onError({
|
|
152
|
+
url,
|
|
153
|
+
status
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
128
157
|
return /*#__PURE__*/React.createElement(EmbedCardErroredView, {
|
|
129
158
|
onRetry: handleErrorRetry,
|
|
130
159
|
inheritDimensions: inheritDimensions,
|
|
@@ -9,6 +9,8 @@ const getAlignmentStyles = align => {
|
|
|
9
9
|
switch (align) {
|
|
10
10
|
case SmartLinkAlignment.Right:
|
|
11
11
|
return css`
|
|
12
|
+
-webkit-box-align: end;
|
|
13
|
+
-ms-flex-align: end;
|
|
12
14
|
justify-content: flex-end;
|
|
13
15
|
text-align: right;
|
|
14
16
|
`;
|
|
@@ -16,6 +18,8 @@ const getAlignmentStyles = align => {
|
|
|
16
18
|
case SmartLinkAlignment.Left:
|
|
17
19
|
default:
|
|
18
20
|
return css`
|
|
21
|
+
-webkit-box-align: start;
|
|
22
|
+
-ms-flex-align: start;
|
|
19
23
|
justify-content: flex-start;
|
|
20
24
|
text-align: left;
|
|
21
25
|
`;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/extends";
|
|
2
2
|
import React, { useEffect } from 'react';
|
|
3
|
+
import { SmartLinkStatus } from '../../constants';
|
|
3
4
|
import Container from './components/container';
|
|
4
5
|
import { FlexibleUiContext } from '../../state/flexible-ui-context';
|
|
5
6
|
import { getContextByStatus, getRetryOptions } from './utils';
|
|
@@ -14,12 +15,13 @@ const FlexibleCard = ({
|
|
|
14
15
|
cardState,
|
|
15
16
|
children,
|
|
16
17
|
onAuthorize,
|
|
18
|
+
onError,
|
|
19
|
+
onResolve,
|
|
17
20
|
renderers,
|
|
18
21
|
ui,
|
|
19
22
|
url,
|
|
20
23
|
onClick,
|
|
21
|
-
testId
|
|
22
|
-
onResolve
|
|
24
|
+
testId
|
|
23
25
|
}) => {
|
|
24
26
|
const {
|
|
25
27
|
status: cardType,
|
|
@@ -28,13 +30,36 @@ const FlexibleCard = ({
|
|
|
28
30
|
const status = cardType;
|
|
29
31
|
const context = getContextByStatus(url, status, details, renderers);
|
|
30
32
|
const retry = getRetryOptions(url, status, details, onAuthorize);
|
|
33
|
+
const {
|
|
34
|
+
title
|
|
35
|
+
} = context || {};
|
|
31
36
|
useEffect(() => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
switch (status) {
|
|
38
|
+
case SmartLinkStatus.Resolved:
|
|
39
|
+
if (onResolve) {
|
|
40
|
+
onResolve({
|
|
41
|
+
title,
|
|
42
|
+
url
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
break;
|
|
47
|
+
|
|
48
|
+
case SmartLinkStatus.Errored:
|
|
49
|
+
case SmartLinkStatus.Fallback:
|
|
50
|
+
case SmartLinkStatus.Forbidden:
|
|
51
|
+
case SmartLinkStatus.NotFound:
|
|
52
|
+
case SmartLinkStatus.Unauthorized:
|
|
53
|
+
if (onError) {
|
|
54
|
+
onError({
|
|
55
|
+
status,
|
|
56
|
+
url
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
break;
|
|
36
61
|
}
|
|
37
|
-
}, [onResolve, status, url]);
|
|
62
|
+
}, [onError, onResolve, status, title, url]);
|
|
38
63
|
return /*#__PURE__*/React.createElement(FlexibleUiContext.Provider, {
|
|
39
64
|
value: context
|
|
40
65
|
}, /*#__PURE__*/React.createElement(Container, _extends({
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
|
|
3
|
+
/** @jsx jsx */
|
|
4
|
+
import Popup from '@atlaskit/popup';
|
|
5
|
+
import { jsx } from '@emotion/core';
|
|
6
|
+
import React, { useCallback, useRef } from 'react';
|
|
7
|
+
import { useSmartLinkActions } from '../../../state/hooks-external/useSmartLinkActions';
|
|
8
|
+
import { useSmartLinkRenderers } from '../../../state/renderers';
|
|
9
|
+
import { useSmartCardState as useLinkState } from '../../../state/store';
|
|
10
|
+
import HoverCardContent from '../components/HoverCardContent';
|
|
11
|
+
import { HoverCardContainer } from '../styled';
|
|
12
|
+
import { SMART_CARD_ANALYTICS_DISPLAY } from '../utils';
|
|
13
|
+
export const hoverCardClassName = 'smart-links-hover-preview';
|
|
14
|
+
export const HoverCardComponent = ({
|
|
15
|
+
children,
|
|
16
|
+
url,
|
|
17
|
+
analyticsHandler,
|
|
18
|
+
analytics
|
|
19
|
+
}) => {
|
|
20
|
+
const delay = 300;
|
|
21
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
22
|
+
const fadeOutTimeoutId = useRef();
|
|
23
|
+
const fadeInTimeoutId = useRef();
|
|
24
|
+
const cardOpenTime = useRef();
|
|
25
|
+
const renderers = useSmartLinkRenderers();
|
|
26
|
+
const linkState = useLinkState(url);
|
|
27
|
+
const hoverDisplay = 'card';
|
|
28
|
+
const invokeMethod = 'mouse_hover';
|
|
29
|
+
const hideCard = useCallback(() => {
|
|
30
|
+
//Check its previously open to avoid firing events when moving between the child and hover card components
|
|
31
|
+
if (isOpen === true && cardOpenTime.current) {
|
|
32
|
+
const hoverTime = Date.now() - cardOpenTime.current;
|
|
33
|
+
analytics.ui.hoverCardDismissedEvent('card', hoverTime, 'mouse_hover');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
setIsOpen(false);
|
|
37
|
+
}, [analytics.ui, isOpen]);
|
|
38
|
+
const initHideCard = useCallback(() => {
|
|
39
|
+
if (fadeInTimeoutId.current) {
|
|
40
|
+
clearTimeout(fadeInTimeoutId.current);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fadeOutTimeoutId.current = setTimeout(() => hideCard(), delay);
|
|
44
|
+
}, [hideCard]);
|
|
45
|
+
const initShowCard = useCallback(() => {
|
|
46
|
+
if (fadeOutTimeoutId.current) {
|
|
47
|
+
clearTimeout(fadeOutTimeoutId.current);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fadeInTimeoutId.current = setTimeout(() => {
|
|
51
|
+
//Check if its previously closed to avoid firing events when moving between the child and hover card components
|
|
52
|
+
if (isOpen === false) {
|
|
53
|
+
cardOpenTime.current = Date.now();
|
|
54
|
+
analytics.ui.hoverCardViewedEvent(hoverDisplay, invokeMethod);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
setIsOpen(true);
|
|
58
|
+
}, delay);
|
|
59
|
+
}, [delay, isOpen, analytics.ui]);
|
|
60
|
+
const linkActions = useSmartLinkActions({
|
|
61
|
+
url,
|
|
62
|
+
appearance: SMART_CARD_ANALYTICS_DISPLAY,
|
|
63
|
+
analyticsHandler
|
|
64
|
+
});
|
|
65
|
+
const onActionClick = useCallback(actionId => {
|
|
66
|
+
if (actionId === 'preview-content') {
|
|
67
|
+
hideCard();
|
|
68
|
+
}
|
|
69
|
+
}, [hideCard]); // Stop hover preview content to propagate event to parent.
|
|
70
|
+
|
|
71
|
+
const onClick = useCallback(e => e.stopPropagation(), []);
|
|
72
|
+
return jsx(Popup, {
|
|
73
|
+
testId: "hover-card",
|
|
74
|
+
isOpen: isOpen,
|
|
75
|
+
onClose: hideCard,
|
|
76
|
+
placement: "bottom-start",
|
|
77
|
+
content: ({
|
|
78
|
+
update
|
|
79
|
+
}) => jsx("div", {
|
|
80
|
+
onMouseEnter: initShowCard,
|
|
81
|
+
onMouseLeave: initHideCard,
|
|
82
|
+
onClick: onClick,
|
|
83
|
+
css: HoverCardContainer // class name for trello to exclude click events
|
|
84
|
+
,
|
|
85
|
+
className: hoverCardClassName
|
|
86
|
+
}, jsx(HoverCardContent, {
|
|
87
|
+
analytics: analytics,
|
|
88
|
+
cardActions: linkActions,
|
|
89
|
+
cardState: linkState,
|
|
90
|
+
onActionClick: onActionClick,
|
|
91
|
+
onResolve: update,
|
|
92
|
+
renderers: renderers,
|
|
93
|
+
url: url
|
|
94
|
+
})),
|
|
95
|
+
trigger: triggerProps => jsx("span", _extends({}, triggerProps, {
|
|
96
|
+
onMouseEnter: initShowCard,
|
|
97
|
+
onMouseLeave: initHideCard
|
|
98
|
+
}), children),
|
|
99
|
+
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
|
+
|
|
101
|
+
});
|
|
102
|
+
};
|