@atlaskit/smart-card 26.5.13 → 26.5.15
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 +12 -0
- package/dist/cjs/version.json +1 -1
- package/dist/cjs/view/HoverCard/components/HoverCardComponent.js +9 -49
- package/dist/cjs/view/HoverCard/components/HoverCardContent.js +13 -16
- package/dist/cjs/view/HoverCard/index.js +4 -33
- package/dist/cjs/view/LinkUrl/LinkWarningModal/hooks/use-link-warning-modal.js +36 -19
- package/dist/cjs/view/LinkUrl/index.js +23 -4
- package/dist/es2019/version.json +1 -1
- package/dist/es2019/view/HoverCard/components/HoverCardComponent.js +6 -46
- package/dist/es2019/view/HoverCard/components/HoverCardContent.js +13 -16
- package/dist/es2019/view/HoverCard/index.js +5 -33
- package/dist/es2019/view/LinkUrl/LinkWarningModal/hooks/use-link-warning-modal.js +36 -19
- package/dist/es2019/view/LinkUrl/index.js +22 -3
- package/dist/esm/version.json +1 -1
- package/dist/esm/view/HoverCard/components/HoverCardComponent.js +7 -49
- package/dist/esm/view/HoverCard/components/HoverCardContent.js +13 -16
- package/dist/esm/view/HoverCard/index.js +5 -33
- package/dist/esm/view/LinkUrl/LinkWarningModal/hooks/use-link-warning-modal.js +36 -19
- package/dist/esm/view/LinkUrl/index.js +23 -4
- package/dist/types/view/Card/index.d.ts +1 -1
- package/dist/types/view/LinkUrl/LinkWarningModal/hooks/use-link-warning-modal.d.ts +4 -3
- package/dist/types-ts4.5/view/Card/index.d.ts +1 -1
- package/dist/types-ts4.5/view/LinkUrl/LinkWarningModal/hooks/use-link-warning-modal.d.ts +4 -3
- package/package.json +6 -6
|
@@ -9,37 +9,46 @@ export const useLinkWarningModal = () => {
|
|
|
9
9
|
const {
|
|
10
10
|
createAnalyticsEvent
|
|
11
11
|
} = useAnalyticsEvents();
|
|
12
|
-
const
|
|
12
|
+
const sendWarningModalViewedEvent = () => {
|
|
13
13
|
createAnalyticsEvent({
|
|
14
|
-
action: '
|
|
15
|
-
actionSubject: '
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
action: 'viewed',
|
|
15
|
+
actionSubject: 'linkSafetyWarningModal',
|
|
16
|
+
eventType: 'screen',
|
|
17
|
+
name: 'linkSafetyWarningModal'
|
|
18
|
+
}).fire(ANALYTICS_CHANNEL);
|
|
19
|
+
};
|
|
20
|
+
const sendContinueClickedEvent = () => {
|
|
21
|
+
createAnalyticsEvent({
|
|
22
|
+
action: 'clicked',
|
|
23
|
+
actionSubject: 'button',
|
|
24
|
+
actionSubjectId: 'linkSafetyWarningModalContinue',
|
|
25
|
+
eventType: 'ui',
|
|
26
|
+
screen: 'linkSafetyWarningModal'
|
|
18
27
|
}).fire(ANALYTICS_CHANNEL);
|
|
19
28
|
};
|
|
20
29
|
|
|
21
30
|
/**
|
|
22
31
|
* It checks and warns if a link text is a URL and different from an actual link destination
|
|
23
32
|
*/
|
|
24
|
-
const
|
|
33
|
+
const isLinkSafe = (event, href) => {
|
|
25
34
|
if (!href) {
|
|
26
|
-
return;
|
|
35
|
+
return true;
|
|
27
36
|
}
|
|
28
37
|
const anchor = event.currentTarget;
|
|
29
38
|
const linkText = anchor.innerText;
|
|
30
39
|
const normalisedUrlFromLinkText = normalizeUrl(linkText);
|
|
31
40
|
if (!normalisedUrlFromLinkText) {
|
|
32
|
-
return;
|
|
41
|
+
return true;
|
|
33
42
|
}
|
|
34
43
|
const anchorLinkRegex = new RegExp(/^#/im);
|
|
35
44
|
const isAnchorLink = anchorLinkRegex.test(linkText);
|
|
36
45
|
if (isAnchorLink) {
|
|
37
|
-
return;
|
|
46
|
+
return true;
|
|
38
47
|
}
|
|
39
48
|
const relativeLinkRegex = new RegExp(/^\//im);
|
|
40
49
|
const isRelativeHrefLink = relativeLinkRegex.test(href);
|
|
41
50
|
if (isRelativeHrefLink) {
|
|
42
|
-
return;
|
|
51
|
+
return true;
|
|
43
52
|
}
|
|
44
53
|
const hrefUrl = new URL(href, window.location.origin);
|
|
45
54
|
const linkTextUrl = new URL(normalisedUrlFromLinkText, hrefUrl.origin);
|
|
@@ -53,12 +62,18 @@ export const useLinkWarningModal = () => {
|
|
|
53
62
|
}
|
|
54
63
|
const areEquivalentLinks = linkTextUrl.hostname === hrefUrl.hostname && linkTextUrl.pathname === hrefUrl.pathname && linkTextUrl.search === hrefUrl.search && linkTextUrl.username === hrefUrl.username && linkTextUrl.password === hrefUrl.password && areProtocolsEquivalent;
|
|
55
64
|
if (!areEquivalentLinks) {
|
|
56
|
-
|
|
57
|
-
setUnsafeLinkText(linkText);
|
|
58
|
-
href && setUrl(href);
|
|
59
|
-
setIsOpen(true);
|
|
60
|
-
sendAnalyticsEvent();
|
|
65
|
+
return false;
|
|
61
66
|
}
|
|
67
|
+
return true;
|
|
68
|
+
};
|
|
69
|
+
const showSafetyWarningModal = (event, href) => {
|
|
70
|
+
event.preventDefault();
|
|
71
|
+
const anchor = event.currentTarget;
|
|
72
|
+
const linkText = anchor.innerText;
|
|
73
|
+
setUnsafeLinkText(linkText);
|
|
74
|
+
href && setUrl(href);
|
|
75
|
+
setIsOpen(true);
|
|
76
|
+
sendWarningModalViewedEvent();
|
|
62
77
|
};
|
|
63
78
|
const onClose = () => {
|
|
64
79
|
setIsOpen(false);
|
|
@@ -67,14 +82,16 @@ export const useLinkWarningModal = () => {
|
|
|
67
82
|
};
|
|
68
83
|
const onContinue = () => {
|
|
69
84
|
onClose();
|
|
70
|
-
|
|
85
|
+
sendContinueClickedEvent();
|
|
86
|
+
url && window.open(url, '_blank', 'noopener noreferrer');
|
|
71
87
|
};
|
|
72
88
|
return {
|
|
73
|
-
|
|
74
|
-
|
|
89
|
+
isLinkSafe,
|
|
90
|
+
isOpen,
|
|
75
91
|
onClose,
|
|
76
92
|
onContinue,
|
|
77
|
-
|
|
93
|
+
showSafetyWarningModal,
|
|
94
|
+
unsafeLinkText,
|
|
78
95
|
url
|
|
79
96
|
};
|
|
80
97
|
};
|
|
@@ -6,6 +6,7 @@ import { withLinkClickedEvent } from '../../utils/analytics/click';
|
|
|
6
6
|
import { LinkAnalyticsContext } from '../../utils/analytics/LinkAnalyticsContext';
|
|
7
7
|
import { useLinkWarningModal } from './LinkWarningModal/hooks/use-link-warning-modal';
|
|
8
8
|
import LinkWarningModal from './LinkWarningModal';
|
|
9
|
+
import { getBooleanFF } from '@atlaskit/platform-feature-flags';
|
|
9
10
|
const PACKAGE_DATA = {
|
|
10
11
|
packageName,
|
|
11
12
|
packageVersion,
|
|
@@ -21,7 +22,8 @@ const LinkUrl = ({
|
|
|
21
22
|
...props
|
|
22
23
|
}) => {
|
|
23
24
|
const {
|
|
24
|
-
|
|
25
|
+
isLinkSafe,
|
|
26
|
+
showSafetyWarningModal,
|
|
25
27
|
...linkWarningModalProps
|
|
26
28
|
} = useLinkWarningModal();
|
|
27
29
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(LinkAnalyticsContext, {
|
|
@@ -31,8 +33,25 @@ const LinkUrl = ({
|
|
|
31
33
|
"data-testid": testId,
|
|
32
34
|
href: href,
|
|
33
35
|
onClick: e => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
if (getBooleanFF('platform.linking-platform.smart-card.should-call-onclick-in-warning-modal-only-when-safe')) {
|
|
37
|
+
if (!checkSafety) {
|
|
38
|
+
onClick && onClick(e);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Only call the onClick if the link is safe
|
|
43
|
+
if (isLinkSafe(e, href)) {
|
|
44
|
+
onClick && onClick(e);
|
|
45
|
+
} else {
|
|
46
|
+
showSafetyWarningModal(e, href);
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
if (checkSafety && !isLinkSafe(e, href)) {
|
|
50
|
+
showSafetyWarningModal(e, href);
|
|
51
|
+
}
|
|
52
|
+
// Call the onClick regardless of the link safety
|
|
53
|
+
onClick && onClick(e);
|
|
54
|
+
}
|
|
36
55
|
}
|
|
37
56
|
}, props), children)), checkSafety && /*#__PURE__*/React.createElement(LinkWarningModal, linkWarningModalProps));
|
|
38
57
|
};
|
package/dist/esm/version.json
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
-
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
3
2
|
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
4
|
-
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
5
|
-
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
6
3
|
/** @jsx jsx */
|
|
7
|
-
import { getBooleanFF } from '@atlaskit/platform-feature-flags';
|
|
8
4
|
import Popup from '@atlaskit/popup';
|
|
9
5
|
import { jsx } from '@emotion/react';
|
|
10
6
|
import React, { useCallback, useMemo, useRef } from 'react';
|
|
@@ -21,7 +17,6 @@ export var HoverCardComponent = function HoverCardComponent(_ref) {
|
|
|
21
17
|
url = _ref.url,
|
|
22
18
|
id = _ref.id,
|
|
23
19
|
analyticsHandler = _ref.analyticsHandler,
|
|
24
|
-
analytics = _ref.analytics,
|
|
25
20
|
_ref$canOpen = _ref.canOpen,
|
|
26
21
|
canOpen = _ref$canOpen === void 0 ? true : _ref$canOpen,
|
|
27
22
|
_ref$closeOnChildClic = _ref.closeOnChildClick,
|
|
@@ -37,15 +32,11 @@ export var HoverCardComponent = function HoverCardComponent(_ref) {
|
|
|
37
32
|
setIsOpen = _React$useState2[1];
|
|
38
33
|
var fadeOutTimeoutId = useRef();
|
|
39
34
|
var fadeInTimeoutId = useRef();
|
|
40
|
-
var cardOpenTime = useRef();
|
|
41
35
|
var mousePos = useRef();
|
|
42
36
|
var popupOffset = useRef();
|
|
43
37
|
var parentSpan = useRef(null);
|
|
44
38
|
var renderers = useSmartLinkRenderers();
|
|
45
39
|
var linkState = useLinkState(url);
|
|
46
|
-
var linkStatus = linkState.status;
|
|
47
|
-
var hoverDisplay = 'card';
|
|
48
|
-
var invokeMethod = 'mouse_hover';
|
|
49
40
|
var setMousePosition = useCallback(function (event) {
|
|
50
41
|
if (isOpen) {
|
|
51
42
|
return;
|
|
@@ -56,24 +47,8 @@ export var HoverCardComponent = function HoverCardComponent(_ref) {
|
|
|
56
47
|
};
|
|
57
48
|
}, [isOpen]);
|
|
58
49
|
var hideCard = useCallback(function () {
|
|
59
|
-
if (getBooleanFF('platform.linking-platform.smart-card.refactor-hover-card-analytics')) {
|
|
60
|
-
/**
|
|
61
|
-
* Defer to inner HoverCardContent to fire this event
|
|
62
|
-
*/
|
|
63
|
-
} else {
|
|
64
|
-
//Check its previously open to avoid firing events when moving between the child and hover card components
|
|
65
|
-
if (isOpen === true && cardOpenTime.current) {
|
|
66
|
-
var hoverTime = Date.now() - cardOpenTime.current;
|
|
67
|
-
analytics === null || analytics === void 0 ? void 0 : analytics.ui.hoverCardDismissedEvent({
|
|
68
|
-
previewDisplay: 'card',
|
|
69
|
-
hoverTime: hoverTime,
|
|
70
|
-
previewInvokeMethod: 'mouse_hover',
|
|
71
|
-
status: linkStatus
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
50
|
setIsOpen(false);
|
|
76
|
-
}, [
|
|
51
|
+
}, []);
|
|
77
52
|
var initHideCard = useCallback(function () {
|
|
78
53
|
if (fadeInTimeoutId.current) {
|
|
79
54
|
clearTimeout(fadeInTimeoutId.current);
|
|
@@ -89,21 +64,6 @@ export var HoverCardComponent = function HoverCardComponent(_ref) {
|
|
|
89
64
|
//Set mouse position in the case it's not already set by onMouseMove, as in the case of scrolling
|
|
90
65
|
setMousePosition(event);
|
|
91
66
|
fadeInTimeoutId.current = setTimeout(function () {
|
|
92
|
-
if (getBooleanFF('platform.linking-platform.smart-card.refactor-hover-card-analytics')) {
|
|
93
|
-
/**
|
|
94
|
-
* Defer to inner HoverCardContent to fire this event
|
|
95
|
-
*/
|
|
96
|
-
} else {
|
|
97
|
-
//Check if its previously closed to avoid firing events when moving between the child and hover card components
|
|
98
|
-
if (isOpen === false) {
|
|
99
|
-
cardOpenTime.current = Date.now();
|
|
100
|
-
analytics === null || analytics === void 0 ? void 0 : analytics.ui.hoverCardViewedEvent({
|
|
101
|
-
previewDisplay: hoverDisplay,
|
|
102
|
-
previewInvokeMethod: invokeMethod,
|
|
103
|
-
status: linkStatus
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
67
|
//If these are undefined then popupOffset is undefined and we fallback to default bottom-start placement
|
|
108
68
|
if (parentSpan.current && mousePos.current) {
|
|
109
69
|
var _parentSpan$current$g = parentSpan.current.getBoundingClientRect(),
|
|
@@ -113,7 +73,7 @@ export var HoverCardComponent = function HoverCardComponent(_ref) {
|
|
|
113
73
|
}
|
|
114
74
|
setIsOpen(true);
|
|
115
75
|
}, delay);
|
|
116
|
-
}, [
|
|
76
|
+
}, [setMousePosition]);
|
|
117
77
|
var linkActions = useSmartLinkActions({
|
|
118
78
|
url: url,
|
|
119
79
|
appearance: CardDisplay.HoverCardPreview,
|
|
@@ -141,7 +101,7 @@ export var HoverCardComponent = function HoverCardComponent(_ref) {
|
|
|
141
101
|
}, [closeOnChildClick, hideCard]);
|
|
142
102
|
var content = useCallback(function (_ref2) {
|
|
143
103
|
var update = _ref2.update;
|
|
144
|
-
var hoverCardContentProps =
|
|
104
|
+
var hoverCardContentProps = {
|
|
145
105
|
onMouseEnter: initShowCard,
|
|
146
106
|
onMouseLeave: initHideCard,
|
|
147
107
|
cardActions: filteredActions,
|
|
@@ -152,16 +112,14 @@ export var HoverCardComponent = function HoverCardComponent(_ref) {
|
|
|
152
112
|
showServerActions: showServerActions,
|
|
153
113
|
url: url,
|
|
154
114
|
id: id
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
});
|
|
158
|
-
return getBooleanFF('platform.linking-platform.smart-card.enable-analytics-context') ? jsx(SmartLinkAnalyticsContext, {
|
|
115
|
+
};
|
|
116
|
+
return jsx(SmartLinkAnalyticsContext, {
|
|
159
117
|
url: url,
|
|
160
118
|
display: CardDisplay.HoverCardPreview,
|
|
161
119
|
id: id,
|
|
162
120
|
source: HOVER_CARD_SOURCE
|
|
163
|
-
}, jsx(HoverCardContent, hoverCardContentProps))
|
|
164
|
-
}, [
|
|
121
|
+
}, jsx(HoverCardContent, hoverCardContentProps));
|
|
122
|
+
}, [initHideCard, initShowCard, filteredActions, linkState, onActionClick, renderers, showServerActions, url, id]);
|
|
165
123
|
var trigger = useCallback(function (triggerProps) {
|
|
166
124
|
return jsx("span", {
|
|
167
125
|
ref: parentSpan
|
|
@@ -3,7 +3,6 @@ import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
|
3
3
|
/** @jsx jsx */
|
|
4
4
|
import { jsx } from '@emotion/react';
|
|
5
5
|
import { useAnalyticsEvents } from '@atlaskit/analytics-next';
|
|
6
|
-
import { getBooleanFF } from '@atlaskit/platform-feature-flags';
|
|
7
6
|
import ShortcutIcon from '@atlaskit/icon/glyph/shortcut';
|
|
8
7
|
import CopyIcon from '@atlaskit/icon/glyph/copy';
|
|
9
8
|
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
@@ -121,25 +120,23 @@ var HoverCardContent = function HoverCardContent(_ref) {
|
|
|
121
120
|
}
|
|
122
121
|
}, [analytics, linkStatus]);
|
|
123
122
|
useEffect(function () {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
123
|
+
var previewDisplay = 'card';
|
|
124
|
+
var previewInvokeMethod = 'mouse_hover';
|
|
125
|
+
var cardOpenTime = Date.now();
|
|
126
|
+
analyticsRef.current.ui.hoverCardViewedEvent({
|
|
127
|
+
previewDisplay: previewDisplay,
|
|
128
|
+
previewInvokeMethod: previewInvokeMethod,
|
|
129
|
+
status: statusRef.current
|
|
130
|
+
});
|
|
131
|
+
return function () {
|
|
132
|
+
var hoverTime = Date.now() - cardOpenTime;
|
|
133
|
+
analyticsRef.current.ui.hoverCardDismissedEvent({
|
|
129
134
|
previewDisplay: previewDisplay,
|
|
130
135
|
previewInvokeMethod: previewInvokeMethod,
|
|
136
|
+
hoverTime: hoverTime,
|
|
131
137
|
status: statusRef.current
|
|
132
138
|
});
|
|
133
|
-
|
|
134
|
-
var hoverTime = Date.now() - cardOpenTime;
|
|
135
|
-
analyticsRef.current.ui.hoverCardDismissedEvent({
|
|
136
|
-
previewDisplay: previewDisplay,
|
|
137
|
-
previewInvokeMethod: previewInvokeMethod,
|
|
138
|
-
hoverTime: hoverTime,
|
|
139
|
-
status: statusRef.current
|
|
140
|
-
});
|
|
141
|
-
};
|
|
142
|
-
}
|
|
139
|
+
};
|
|
143
140
|
}, []);
|
|
144
141
|
var onClick = useCallback(function (event) {
|
|
145
142
|
var isModifierKeyPressed = isSpecialEvent(event);
|
|
@@ -1,27 +1,16 @@
|
|
|
1
|
-
import _extends from "@babel/runtime/helpers/extends";
|
|
2
1
|
/** @jsx jsx */
|
|
3
|
-
import {
|
|
2
|
+
import { withAnalyticsEvents } from '@atlaskit/analytics-next';
|
|
4
3
|
import { jsx } from '@emotion/react';
|
|
5
4
|
import { useCallback } from 'react';
|
|
6
5
|
import { ErrorBoundary } from 'react-error-boundary';
|
|
7
|
-
import { getBooleanFF } from '@atlaskit/platform-feature-flags';
|
|
8
6
|
import { useSmartLinkAnalytics } from '../../state/analytics';
|
|
9
|
-
import { fireSmartLinkEvent } from '../../utils/analytics';
|
|
10
7
|
import { HoverCardComponent } from './components/HoverCardComponent';
|
|
11
8
|
import { CardDisplay } from '../../constants';
|
|
12
9
|
var HoverCardWithErrorBoundary = function HoverCardWithErrorBoundary(props) {
|
|
13
10
|
var url = props.url,
|
|
14
11
|
id = props.id,
|
|
15
|
-
children = props.children
|
|
16
|
-
|
|
17
|
-
var analyticsHandler = useCallback(function (analyticsPayload) {
|
|
18
|
-
fireSmartLinkEvent(analyticsPayload, createAnalyticsEvent);
|
|
19
|
-
}, [createAnalyticsEvent]);
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Feature flag disables prop drilling of analytics handlers
|
|
23
|
-
*/
|
|
24
|
-
var analytics = useSmartLinkAnalytics(url, getBooleanFF('platform.linking-platform.smart-card.refactor-hover-card-analytics') ? undefined : analyticsHandler, id);
|
|
12
|
+
children = props.children;
|
|
13
|
+
var analytics = useSmartLinkAnalytics(url, undefined, id);
|
|
25
14
|
var onError = useCallback(function (error, info) {
|
|
26
15
|
analytics.ui.renderFailedEvent({
|
|
27
16
|
display: CardDisplay.HoverCardPreview,
|
|
@@ -30,29 +19,12 @@ var HoverCardWithErrorBoundary = function HoverCardWithErrorBoundary(props) {
|
|
|
30
19
|
errorInfo: info
|
|
31
20
|
});
|
|
32
21
|
}, [analytics.ui, id]);
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Feature flag disables the prop drilling of analytics handlers to hover card component
|
|
36
|
-
*/
|
|
37
|
-
var analyticsProps = getBooleanFF('platform.linking-platform.smart-card.refactor-hover-card-analytics') ? {} : {
|
|
38
|
-
analyticsHandler: analyticsHandler,
|
|
39
|
-
analytics: analytics
|
|
40
|
-
};
|
|
41
22
|
return jsx(ErrorBoundary, {
|
|
42
23
|
fallback: children,
|
|
43
24
|
onError: onError
|
|
44
|
-
}, jsx(HoverCardComponent,
|
|
25
|
+
}, jsx(HoverCardComponent, props, children));
|
|
45
26
|
};
|
|
46
|
-
var HoverCardWithAnalyticsContext = withAnalyticsContext({
|
|
47
|
-
source: 'smartLinkPreviewHoverCard',
|
|
48
|
-
attributes: {
|
|
49
|
-
display: CardDisplay.HoverCardPreview
|
|
50
|
-
}
|
|
51
|
-
})(withAnalyticsEvents()(HoverCardWithErrorBoundary));
|
|
52
27
|
var HoverCardWithoutAnalyticsContext = withAnalyticsEvents()(HoverCardWithErrorBoundary);
|
|
53
28
|
export var HoverCard = function HoverCard(props) {
|
|
54
|
-
|
|
55
|
-
return jsx(HoverCardWithoutAnalyticsContext, props);
|
|
56
|
-
}
|
|
57
|
-
return jsx(HoverCardWithAnalyticsContext, props);
|
|
29
|
+
return jsx(HoverCardWithoutAnalyticsContext, props);
|
|
58
30
|
};
|
|
@@ -18,37 +18,46 @@ export var useLinkWarningModal = function useLinkWarningModal() {
|
|
|
18
18
|
setIsOpen = _useState6[1];
|
|
19
19
|
var _useAnalyticsEvents = useAnalyticsEvents(),
|
|
20
20
|
createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent;
|
|
21
|
-
var
|
|
21
|
+
var sendWarningModalViewedEvent = function sendWarningModalViewedEvent() {
|
|
22
22
|
createAnalyticsEvent({
|
|
23
|
-
action: '
|
|
24
|
-
actionSubject: '
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
action: 'viewed',
|
|
24
|
+
actionSubject: 'linkSafetyWarningModal',
|
|
25
|
+
eventType: 'screen',
|
|
26
|
+
name: 'linkSafetyWarningModal'
|
|
27
|
+
}).fire(ANALYTICS_CHANNEL);
|
|
28
|
+
};
|
|
29
|
+
var sendContinueClickedEvent = function sendContinueClickedEvent() {
|
|
30
|
+
createAnalyticsEvent({
|
|
31
|
+
action: 'clicked',
|
|
32
|
+
actionSubject: 'button',
|
|
33
|
+
actionSubjectId: 'linkSafetyWarningModalContinue',
|
|
34
|
+
eventType: 'ui',
|
|
35
|
+
screen: 'linkSafetyWarningModal'
|
|
27
36
|
}).fire(ANALYTICS_CHANNEL);
|
|
28
37
|
};
|
|
29
38
|
|
|
30
39
|
/**
|
|
31
40
|
* It checks and warns if a link text is a URL and different from an actual link destination
|
|
32
41
|
*/
|
|
33
|
-
var
|
|
42
|
+
var isLinkSafe = function isLinkSafe(event, href) {
|
|
34
43
|
if (!href) {
|
|
35
|
-
return;
|
|
44
|
+
return true;
|
|
36
45
|
}
|
|
37
46
|
var anchor = event.currentTarget;
|
|
38
47
|
var linkText = anchor.innerText;
|
|
39
48
|
var normalisedUrlFromLinkText = normalizeUrl(linkText);
|
|
40
49
|
if (!normalisedUrlFromLinkText) {
|
|
41
|
-
return;
|
|
50
|
+
return true;
|
|
42
51
|
}
|
|
43
52
|
var anchorLinkRegex = new RegExp(/^#/im);
|
|
44
53
|
var isAnchorLink = anchorLinkRegex.test(linkText);
|
|
45
54
|
if (isAnchorLink) {
|
|
46
|
-
return;
|
|
55
|
+
return true;
|
|
47
56
|
}
|
|
48
57
|
var relativeLinkRegex = new RegExp(/^\//im);
|
|
49
58
|
var isRelativeHrefLink = relativeLinkRegex.test(href);
|
|
50
59
|
if (isRelativeHrefLink) {
|
|
51
|
-
return;
|
|
60
|
+
return true;
|
|
52
61
|
}
|
|
53
62
|
var hrefUrl = new URL(href, window.location.origin);
|
|
54
63
|
var linkTextUrl = new URL(normalisedUrlFromLinkText, hrefUrl.origin);
|
|
@@ -62,12 +71,18 @@ export var useLinkWarningModal = function useLinkWarningModal() {
|
|
|
62
71
|
}
|
|
63
72
|
var areEquivalentLinks = linkTextUrl.hostname === hrefUrl.hostname && linkTextUrl.pathname === hrefUrl.pathname && linkTextUrl.search === hrefUrl.search && linkTextUrl.username === hrefUrl.username && linkTextUrl.password === hrefUrl.password && areProtocolsEquivalent;
|
|
64
73
|
if (!areEquivalentLinks) {
|
|
65
|
-
|
|
66
|
-
setUnsafeLinkText(linkText);
|
|
67
|
-
href && setUrl(href);
|
|
68
|
-
setIsOpen(true);
|
|
69
|
-
sendAnalyticsEvent();
|
|
74
|
+
return false;
|
|
70
75
|
}
|
|
76
|
+
return true;
|
|
77
|
+
};
|
|
78
|
+
var showSafetyWarningModal = function showSafetyWarningModal(event, href) {
|
|
79
|
+
event.preventDefault();
|
|
80
|
+
var anchor = event.currentTarget;
|
|
81
|
+
var linkText = anchor.innerText;
|
|
82
|
+
setUnsafeLinkText(linkText);
|
|
83
|
+
href && setUrl(href);
|
|
84
|
+
setIsOpen(true);
|
|
85
|
+
sendWarningModalViewedEvent();
|
|
71
86
|
};
|
|
72
87
|
var onClose = function onClose() {
|
|
73
88
|
setIsOpen(false);
|
|
@@ -76,14 +91,16 @@ export var useLinkWarningModal = function useLinkWarningModal() {
|
|
|
76
91
|
};
|
|
77
92
|
var onContinue = function onContinue() {
|
|
78
93
|
onClose();
|
|
79
|
-
|
|
94
|
+
sendContinueClickedEvent();
|
|
95
|
+
url && window.open(url, '_blank', 'noopener noreferrer');
|
|
80
96
|
};
|
|
81
97
|
return {
|
|
82
|
-
|
|
83
|
-
|
|
98
|
+
isLinkSafe: isLinkSafe,
|
|
99
|
+
isOpen: isOpen,
|
|
84
100
|
onClose: onClose,
|
|
85
101
|
onContinue: onContinue,
|
|
86
|
-
|
|
102
|
+
showSafetyWarningModal: showSafetyWarningModal,
|
|
103
|
+
unsafeLinkText: unsafeLinkText,
|
|
87
104
|
url: url
|
|
88
105
|
};
|
|
89
106
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/extends";
|
|
2
2
|
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
3
3
|
var _excluded = ["href", "children", "checkSafety", "onClick", "testId"],
|
|
4
|
-
_excluded2 = ["
|
|
4
|
+
_excluded2 = ["isLinkSafe", "showSafetyWarningModal"];
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { withAnalyticsContext } from '@atlaskit/analytics-next';
|
|
7
7
|
import { name as packageName, version as packageVersion } from '../../version.json';
|
|
@@ -9,6 +9,7 @@ import { withLinkClickedEvent } from '../../utils/analytics/click';
|
|
|
9
9
|
import { LinkAnalyticsContext } from '../../utils/analytics/LinkAnalyticsContext';
|
|
10
10
|
import { useLinkWarningModal } from './LinkWarningModal/hooks/use-link-warning-modal';
|
|
11
11
|
import LinkWarningModal from './LinkWarningModal';
|
|
12
|
+
import { getBooleanFF } from '@atlaskit/platform-feature-flags';
|
|
12
13
|
var PACKAGE_DATA = {
|
|
13
14
|
packageName: packageName,
|
|
14
15
|
packageVersion: packageVersion,
|
|
@@ -25,7 +26,8 @@ var LinkUrl = function LinkUrl(_ref) {
|
|
|
25
26
|
testId = _ref$testId === void 0 ? 'link-with-safety' : _ref$testId,
|
|
26
27
|
props = _objectWithoutProperties(_ref, _excluded);
|
|
27
28
|
var _useLinkWarningModal = useLinkWarningModal(),
|
|
28
|
-
|
|
29
|
+
isLinkSafe = _useLinkWarningModal.isLinkSafe,
|
|
30
|
+
showSafetyWarningModal = _useLinkWarningModal.showSafetyWarningModal,
|
|
29
31
|
linkWarningModalProps = _objectWithoutProperties(_useLinkWarningModal, _excluded2);
|
|
30
32
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(LinkAnalyticsContext, {
|
|
31
33
|
url: href,
|
|
@@ -34,8 +36,25 @@ var LinkUrl = function LinkUrl(_ref) {
|
|
|
34
36
|
"data-testid": testId,
|
|
35
37
|
href: href,
|
|
36
38
|
onClick: function onClick(e) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
if (getBooleanFF('platform.linking-platform.smart-card.should-call-onclick-in-warning-modal-only-when-safe')) {
|
|
40
|
+
if (!checkSafety) {
|
|
41
|
+
_onClick && _onClick(e);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Only call the onClick if the link is safe
|
|
46
|
+
if (isLinkSafe(e, href)) {
|
|
47
|
+
_onClick && _onClick(e);
|
|
48
|
+
} else {
|
|
49
|
+
showSafetyWarningModal(e, href);
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
if (checkSafety && !isLinkSafe(e, href)) {
|
|
53
|
+
showSafetyWarningModal(e, href);
|
|
54
|
+
}
|
|
55
|
+
// Call the onClick regardless of the link safety
|
|
56
|
+
_onClick && _onClick(e);
|
|
57
|
+
}
|
|
39
58
|
}
|
|
40
59
|
}, props), children)), checkSafety && /*#__PURE__*/React.createElement(LinkWarningModal, linkWarningModalProps));
|
|
41
60
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { CardAppearance, CardPlatform, CardProps } from './types';
|
|
3
3
|
import { WrappedComponentProps } from 'react-intl-next';
|
|
4
|
-
export declare const Card: React.ForwardRefExoticComponent<Pick<Omit<React.PropsWithChildren<import("react-intl-next").WithIntlProps<CardProps & WrappedComponentProps<"intl">>>, keyof import("@atlaskit/analytics-next").WithAnalyticsEventsProps> & React.RefAttributes<any> & import("@atlaskit/analytics-next").WithContextProps, "url" | "children" | "data" | "key" | "id" | "placeholder" | "onError" | "onClick" | "appearance" | "testId" | "analyticsContext" | "isSelected" | "inheritDimensions" | "
|
|
4
|
+
export declare const Card: React.ForwardRefExoticComponent<Pick<Omit<React.PropsWithChildren<import("react-intl-next").WithIntlProps<CardProps & WrappedComponentProps<"intl">>>, keyof import("@atlaskit/analytics-next").WithAnalyticsEventsProps> & React.RefAttributes<any> & import("@atlaskit/analytics-next").WithContextProps, "url" | "children" | "data" | "key" | "id" | "placeholder" | "onError" | "onClick" | "appearance" | "testId" | "analyticsContext" | "isSelected" | "inheritDimensions" | "showActions" | "showHoverPreview" | "showServerActions" | "showAuthTooltip" | "onResolve" | "ui" | "platform" | "frameStyle" | "embedIframeRef" | "inlinePreloaderStyle" | "isFrameVisible" | "importer" | "container" | "fallbackComponent" | "embedIframeUrlType" | "analyticsEvents" | "forwardedRef"> & React.RefAttributes<any>>;
|
|
5
5
|
export type { CardAppearance, CardProps, CardPlatform };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { MouseEvent } from 'react';
|
|
2
2
|
export declare const useLinkWarningModal: () => {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
isLinkSafe: (event: MouseEvent<HTMLAnchorElement>, href: string | undefined) => boolean;
|
|
4
|
+
isOpen: boolean;
|
|
5
5
|
onClose: () => void;
|
|
6
6
|
onContinue: () => void;
|
|
7
|
-
|
|
7
|
+
showSafetyWarningModal: (event: MouseEvent<HTMLAnchorElement>, href: string | undefined) => void;
|
|
8
|
+
unsafeLinkText: string | null;
|
|
8
9
|
url: string | null;
|
|
9
10
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { CardAppearance, CardPlatform, CardProps } from './types';
|
|
3
3
|
import { WrappedComponentProps } from 'react-intl-next';
|
|
4
|
-
export declare const Card: React.ForwardRefExoticComponent<Pick<Omit<React.PropsWithChildren<import("react-intl-next").WithIntlProps<CardProps & WrappedComponentProps<"intl">>>, keyof import("@atlaskit/analytics-next").WithAnalyticsEventsProps> & React.RefAttributes<any> & import("@atlaskit/analytics-next").WithContextProps, "url" | "children" | "data" | "key" | "id" | "placeholder" | "onError" | "onClick" | "appearance" | "testId" | "analyticsContext" | "isSelected" | "inheritDimensions" | "
|
|
4
|
+
export declare const Card: React.ForwardRefExoticComponent<Pick<Omit<React.PropsWithChildren<import("react-intl-next").WithIntlProps<CardProps & WrappedComponentProps<"intl">>>, keyof import("@atlaskit/analytics-next").WithAnalyticsEventsProps> & React.RefAttributes<any> & import("@atlaskit/analytics-next").WithContextProps, "url" | "children" | "data" | "key" | "id" | "placeholder" | "onError" | "onClick" | "appearance" | "testId" | "analyticsContext" | "isSelected" | "inheritDimensions" | "showActions" | "showHoverPreview" | "showServerActions" | "showAuthTooltip" | "onResolve" | "ui" | "platform" | "frameStyle" | "embedIframeRef" | "inlinePreloaderStyle" | "isFrameVisible" | "importer" | "container" | "fallbackComponent" | "embedIframeUrlType" | "analyticsEvents" | "forwardedRef"> & React.RefAttributes<any>>;
|
|
5
5
|
export type { CardAppearance, CardProps, CardPlatform };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { MouseEvent } from 'react';
|
|
2
2
|
export declare const useLinkWarningModal: () => {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
isLinkSafe: (event: MouseEvent<HTMLAnchorElement>, href: string | undefined) => boolean;
|
|
4
|
+
isOpen: boolean;
|
|
5
5
|
onClose: () => void;
|
|
6
6
|
onContinue: () => void;
|
|
7
|
-
|
|
7
|
+
showSafetyWarningModal: (event: MouseEvent<HTMLAnchorElement>, href: string | undefined) => void;
|
|
8
|
+
unsafeLinkText: string | null;
|
|
8
9
|
url: string | null;
|
|
9
10
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/smart-card",
|
|
3
|
-
"version": "26.5.
|
|
3
|
+
"version": "26.5.15",
|
|
4
4
|
"description": "Smart card component",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@atlaskit/analytics-next": "^9.1.0",
|
|
35
35
|
"@atlaskit/avatar": "^21.3.0",
|
|
36
36
|
"@atlaskit/avatar-group": "^9.3.0",
|
|
37
|
-
"@atlaskit/button": "^16.
|
|
37
|
+
"@atlaskit/button": "^16.8.0",
|
|
38
38
|
"@atlaskit/dropdown-menu": "^11.9.0",
|
|
39
39
|
"@atlaskit/frontend-utilities": "^2.7.0",
|
|
40
40
|
"@atlaskit/icon": "^21.12.0",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@atlaskit/popup": "^1.7.0",
|
|
56
56
|
"@atlaskit/spinner": "^15.5.0",
|
|
57
57
|
"@atlaskit/theme": "^12.5.0",
|
|
58
|
-
"@atlaskit/tokens": "^1.
|
|
58
|
+
"@atlaskit/tokens": "^1.9.0",
|
|
59
59
|
"@atlaskit/tooltip": "^17.8.0",
|
|
60
60
|
"@atlaskit/ufo": "^0.2.0",
|
|
61
61
|
"@babel/runtime": "^7.0.0",
|
|
@@ -141,14 +141,14 @@
|
|
|
141
141
|
"platform.linking-platform.smart-card.enable-analytics-context": {
|
|
142
142
|
"type": "boolean"
|
|
143
143
|
},
|
|
144
|
-
"platform.linking-platform.smart-card.refactor-hover-card-analytics": {
|
|
145
|
-
"type": "boolean"
|
|
146
|
-
},
|
|
147
144
|
"platform.linking-platform.smart-card.remove-dispatch-analytics-as-prop": {
|
|
148
145
|
"type": "boolean"
|
|
149
146
|
},
|
|
150
147
|
"platform.linking-platform.smart-card.enable-block-card-clicks-opening-in-same-tab": {
|
|
151
148
|
"type": "boolean"
|
|
149
|
+
},
|
|
150
|
+
"platform.linking-platform.smart-card.should-call-onclick-in-warning-modal-only-when-safe": {
|
|
151
|
+
"type": "boolean"
|
|
152
152
|
}
|
|
153
153
|
},
|
|
154
154
|
"prettier": "@atlassian/atlassian-frontend-prettier-config-1.0.1",
|