@atlaskit/media-card 81.7.1 → 81.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/cjs/card/card.js +1 -1
  3. package/dist/cjs/card/cardView.js +2 -0
  4. package/dist/cjs/card/externalImageCard.js +12 -1
  5. package/dist/cjs/card/fileCard.js +12 -0
  6. package/dist/cjs/card/media-card-analytics-error-boundary.js +1 -1
  7. package/dist/cjs/card/ui/wrapper/wrapper-compiled.js +4 -4
  8. package/dist/cjs/inline/loader.js +1 -1
  9. package/dist/cjs/utils/isKeyboardFocusEnteringElement.js +29 -0
  10. package/dist/cjs/utils/ufoExperiences.js +1 -1
  11. package/dist/es2019/card/card.js +1 -1
  12. package/dist/es2019/card/cardView.js +2 -0
  13. package/dist/es2019/card/externalImageCard.js +12 -1
  14. package/dist/es2019/card/fileCard.js +12 -0
  15. package/dist/es2019/card/media-card-analytics-error-boundary.js +1 -1
  16. package/dist/es2019/card/ui/wrapper/wrapper-compiled.js +4 -4
  17. package/dist/es2019/inline/loader.js +1 -1
  18. package/dist/es2019/utils/isKeyboardFocusEnteringElement.js +23 -0
  19. package/dist/es2019/utils/ufoExperiences.js +1 -1
  20. package/dist/esm/card/card.js +1 -1
  21. package/dist/esm/card/cardView.js +2 -0
  22. package/dist/esm/card/externalImageCard.js +12 -1
  23. package/dist/esm/card/fileCard.js +12 -0
  24. package/dist/esm/card/media-card-analytics-error-boundary.js +1 -1
  25. package/dist/esm/card/ui/wrapper/wrapper-compiled.js +4 -4
  26. package/dist/esm/inline/loader.js +1 -1
  27. package/dist/esm/utils/isKeyboardFocusEnteringElement.js +23 -0
  28. package/dist/esm/utils/ufoExperiences.js +1 -1
  29. package/dist/types/card/cardView.d.ts +3 -2
  30. package/dist/types/card/externalImageCard.d.ts +1 -1
  31. package/dist/types/card/fileCard.d.ts +1 -1
  32. package/dist/types/card/ui/wrapper/types.d.ts +2 -1
  33. package/dist/types/types.d.ts +6 -1
  34. package/dist/types/utils/isKeyboardFocusEnteringElement.d.ts +11 -0
  35. package/example-helpers/index.tsx +6 -0
  36. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @atlaskit/media-card
2
2
 
3
+ ## 81.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`8808c4948a2a1`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/8808c4948a2a1) -
8
+ [BMPT-8191] Add an `onFocus` callback to media Card. The prop is threaded through `CardView` and
9
+ the `Wrapper` component so consumers can react to focus moving onto a media card. The callback
10
+ receives a `CardFocusEvent` (`{ event, mediaItemDetails? }`), mirroring the existing
11
+ `onMouseEnter` behaviour.
12
+
13
+ `onFocus` fires only for keyboard focus entering the card from outside. Focus caused by a pointer
14
+ interaction is ignored, so mouse users keep exactly the existing `onMouseEnter`/`onClick`
15
+ behaviour. Because focus events bubble, focus moving between elements within the same card is also
16
+ ignored, so the callback fires once per card entry rather than once per focusable descendant.
17
+
18
+ ### Patch Changes
19
+
20
+ - Updated dependencies
21
+
22
+ ## 81.7.2
23
+
24
+ ### Patch Changes
25
+
26
+ - Updated dependencies
27
+
3
28
  ## 81.7.1
4
29
 
5
30
  ### Patch Changes
@@ -20,7 +20,7 @@ var _label = _interopRequireDefault(require("@atlaskit/react-ufo/label"));
20
20
  var _excluded = ["identifier"];
21
21
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
22
22
  var packageName = "@atlaskit/media-card";
23
- var packageVersion = "81.7.0";
23
+ var packageVersion = "81.7.2";
24
24
  var CardBase = exports.CardBase = function CardBase(_ref) {
25
25
  var identifier = _ref.identifier,
26
26
  otherProps = (0, _objectWithoutProperties2.default)(_ref, _excluded);
@@ -64,6 +64,7 @@ var CardViewBase = exports.CardViewBase = function CardViewBase(_ref) {
64
64
  dimensions = _ref.dimensions,
65
65
  onClick = _ref.onClick,
66
66
  onMouseEnter = _ref.onMouseEnter,
67
+ onFocus = _ref.onFocus,
67
68
  testId = _ref.testId,
68
69
  metadata = _ref.metadata,
69
70
  status = _ref.status,
@@ -374,6 +375,7 @@ var CardViewBase = exports.CardViewBase = function CardViewBase(_ref) {
374
375
  onClick: onClick,
375
376
  ariaLabel: name || 'Media Card',
376
377
  onMouseEnter: onMouseEnter,
378
+ onFocus: onFocus,
377
379
  innerRef: mergedRef,
378
380
  breakpoint: breakpoint,
379
381
  mediaCardCursor: mediaCardCursor,
@@ -17,6 +17,7 @@ var _reactDom = _interopRequireDefault(require("react-dom"));
17
17
  var _errors = require("../errors");
18
18
  var _generateUniqueId = require("../utils/generateUniqueId");
19
19
  var _getMediaCardCursor = require("../utils/getMediaCardCursor");
20
+ var _isKeyboardFocusEnteringElement = require("../utils/isKeyboardFocusEnteringElement");
20
21
  var _ufoExperiences = require("../utils/ufoExperiences");
21
22
  var _useCurrentValueRef = require("../utils/useCurrentValueRef");
22
23
  var _cardDimensions = require("../utils/cardDimensions");
@@ -51,7 +52,8 @@ var ExternalImageCard = exports.ExternalImageCard = function ExternalImageCard(_
51
52
  shouldHideTooltip = _ref.shouldHideTooltip,
52
53
  mediaViewerItems = _ref.mediaViewerItems,
53
54
  _onClick = _ref.onClick,
54
- _onMouseEnter = _ref.onMouseEnter;
55
+ _onMouseEnter = _ref.onMouseEnter,
56
+ _onFocus = _ref.onFocus;
55
57
  var _useAnalyticsEvents = (0, _useAnalyticsEvents2.useAnalyticsEvents)(),
56
58
  createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent;
57
59
  var cardDimensions = dimensions || (0, _cardDimensions.getDefaultCardDimensions)(appearance);
@@ -239,6 +241,15 @@ var ExternalImageCard = exports.ExternalImageCard = function ExternalImageCard(_
239
241
  mediaItemDetails: metadata
240
242
  });
241
243
  },
244
+ onFocus: function onFocus(event) {
245
+ if (!(0, _isKeyboardFocusEnteringElement.isKeyboardFocusEnteringElement)(event)) {
246
+ return;
247
+ }
248
+ _onFocus === null || _onFocus === void 0 || _onFocus({
249
+ event: event,
250
+ mediaItemDetails: metadata
251
+ });
252
+ },
242
253
  disableOverlay: disableOverlay,
243
254
  onDisplayImage: function onDisplayImage() {
244
255
  var payloadPart = {
@@ -25,6 +25,7 @@ var _types = require("../types");
25
25
  var _generateUniqueId = require("../utils/generateUniqueId");
26
26
  var _getDataURIDimension = require("../utils/getDataURIDimension");
27
27
  var _getMediaCardCursor = require("../utils/getMediaCardCursor");
28
+ var _isKeyboardFocusEnteringElement = require("../utils/isKeyboardFocusEnteringElement");
28
29
  var _metadata = require("../utils/metadata");
29
30
  var _ufoExperiences = require("../utils/ufoExperiences");
30
31
  var _useCurrentValueRef = require("../utils/useCurrentValueRef");
@@ -95,6 +96,7 @@ var FileCard = exports.FileCard = function FileCard(_ref) {
95
96
  mediaViewerItems = _ref.mediaViewerItems,
96
97
  onClick = _ref.onClick,
97
98
  onMouseEnter = _ref.onMouseEnter,
99
+ onFocus = _ref.onFocus,
98
100
  videoControlsWrapperRef = _ref.videoControlsWrapperRef,
99
101
  viewerOptions = _ref.viewerOptions,
100
102
  includeHashForDuplicateFiles = _ref.includeHashForDuplicateFiles,
@@ -716,6 +718,15 @@ var FileCard = exports.FileCard = function FileCard(_ref) {
716
718
  mediaItemDetails: metadata
717
719
  });
718
720
  };
721
+ var onImageFocus = function onImageFocus(event) {
722
+ if (!(0, _isKeyboardFocusEnteringElement.isKeyboardFocusEnteringElement)(event)) {
723
+ return;
724
+ }
725
+ onFocus === null || onFocus === void 0 || onFocus({
726
+ event: event,
727
+ mediaItemDetails: metadata
728
+ });
729
+ };
719
730
 
720
731
  //----------------------------------------------------------------//
721
732
  //---------------------- Render Card Function --------------------//
@@ -754,6 +765,7 @@ var FileCard = exports.FileCard = function FileCard(_ref) {
754
765
  openMediaViewerButtonRef: mediaViewerButtonRef,
755
766
  onClick: withCallbacks ? onCardViewClick : undefined,
756
767
  onMouseEnter: withCallbacks ? onImageMouseEnter : undefined,
768
+ onFocus: withCallbacks ? onImageFocus : undefined,
757
769
  disableOverlay: disableOverlay,
758
770
  progress: uploadProgressRef.current,
759
771
  onDisplayImage: withCallbacks ? function () {
@@ -87,7 +87,7 @@ var WrappedMediaCardAnalyticsErrorBoundary = /*#__PURE__*/function (_React$Compo
87
87
  }(_react.default.Component);
88
88
  (0, _defineProperty2.default)(WrappedMediaCardAnalyticsErrorBoundary, "displayName", 'MediaCardAnalyticsErrorBoundary');
89
89
  var packageName = "@atlaskit/media-card";
90
- var packageVersion = "81.7.0";
90
+ var packageVersion = "81.7.2";
91
91
 
92
92
  // @ts-ignore: [PIT-1685] Fails in post-office due to backwards incompatibility issue with React 18
93
93
  var MediaCardAnalyticsErrorBoundary = (0, _mediaCommon.withMediaAnalyticsContext)({
@@ -60,6 +60,7 @@ var Wrapper = exports.Wrapper = function Wrapper(props) {
60
60
  appearance = props.appearance,
61
61
  onClick = props.onClick,
62
62
  onMouseEnter = props.onMouseEnter,
63
+ onFocus = props.onFocus,
63
64
  innerRef = props.innerRef,
64
65
  breakpoint = props.breakpoint,
65
66
  disableOverlay = props.disableOverlay,
@@ -80,10 +81,9 @@ var Wrapper = exports.Wrapper = function Wrapper(props) {
80
81
  "data-testid": testId,
81
82
  style: _objectSpread((0, _defineProperty2.default)((0, _defineProperty2.default)({}, LOCAL_WIDTH_VARIABLE, width), LOCAL_HEIGHT_VARIABLE, height), getResponsiveStyles(breakpoint)),
82
83
  ref: innerRef,
83
- onClick: onClick
84
- // eslint-disable-next-line @atlassian/a11y/mouse-events-have-key-events
85
- ,
86
- onMouseEnter: onMouseEnter
84
+ onClick: onClick,
85
+ onMouseEnter: onMouseEnter,
86
+ onFocus: onFocus
87
87
  }, _vcMedia.VcMediaWrapperProps, {
88
88
  role: "none"
89
89
  }), /*#__PURE__*/React.createElement(_customData.default, {
@@ -116,7 +116,7 @@ var MediaInlineCardLoader = exports.default = /*#__PURE__*/function (_React$Pure
116
116
  ErrorBoundary = _this$state.ErrorBoundary;
117
117
  var analyticsContext = {
118
118
  packageVersion: "@atlaskit/media-card",
119
- packageName: "81.7.0",
119
+ packageName: "81.7.2",
120
120
  componentName: 'mediaInlineCard',
121
121
  component: 'mediaInlineCard'
122
122
  };
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isKeyboardFocusEnteringElement = void 0;
7
+ /**
8
+ * Returns `true` only for *keyboard* focus entering an element from outside it.
9
+ *
10
+ * `onFocus` alone is unsuitable because:
11
+ * 1. It bubbles (native `focusin`), firing on internal focus moves. We ignore these via
12
+ * `relatedTarget` when it's contained by the current target.
13
+ * 2. Clicking an interactive descendant also focuses it, duplicating pointer events. We use
14
+ * `:focus-visible` to keep pointer behaviour unchanged.
15
+ */
16
+ var isKeyboardFocusEnteringElement = exports.isKeyboardFocusEnteringElement = function isKeyboardFocusEnteringElement(event) {
17
+ // Ignore focus moving between elements inside the same element.
18
+ if (event.currentTarget.contains(event.relatedTarget)) {
19
+ return false;
20
+ }
21
+ try {
22
+ if (!event.target.matches(':focus-visible')) {
23
+ return false;
24
+ }
25
+ } catch (_unused) {
26
+ // Ignore errors from environments that don't support :focus-visible.
27
+ }
28
+ return true;
29
+ };
@@ -22,7 +22,7 @@ var _globalScope = require("./globalScope/globalScope");
22
22
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
23
23
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
24
24
  var packageName = "@atlaskit/media-card";
25
- var packageVersion = "81.7.0";
25
+ var packageVersion = "81.7.2";
26
26
  var SAMPLE_RATE = 0.05;
27
27
 
28
28
  /**
@@ -9,7 +9,7 @@ import { startResourceObserver, setAnalyticsContext } from '../utils/mediaPerfor
9
9
  import { useAnalyticsEvents } from '@atlaskit/analytics-next/useAnalyticsEvents';
10
10
  import UFOLabel from '@atlaskit/react-ufo/label';
11
11
  const packageName = "@atlaskit/media-card";
12
- const packageVersion = "81.7.0";
12
+ const packageVersion = "81.7.2";
13
13
  export const CardBase = ({
14
14
  identifier,
15
15
  ...otherProps
@@ -50,6 +50,7 @@ export const CardViewBase = ({
50
50
  dimensions,
51
51
  onClick,
52
52
  onMouseEnter,
53
+ onFocus,
53
54
  testId,
54
55
  metadata,
55
56
  status,
@@ -363,6 +364,7 @@ export const CardViewBase = ({
363
364
  onClick: onClick,
364
365
  ariaLabel: name || 'Media Card',
365
366
  onMouseEnter: onMouseEnter,
367
+ onFocus: onFocus,
366
368
  innerRef: mergedRef,
367
369
  breakpoint: breakpoint,
368
370
  mediaCardCursor: mediaCardCursor,
@@ -7,6 +7,7 @@ import ReactDOM from 'react-dom';
7
7
  import { ImageLoadError } from '../errors';
8
8
  import { generateUniqueId } from '../utils/generateUniqueId';
9
9
  import { getMediaCardCursor } from '../utils/getMediaCardCursor';
10
+ import { isKeyboardFocusEnteringElement } from '../utils/isKeyboardFocusEnteringElement';
10
11
  import { shouldPerformanceBeSampled, useMediaCardUfoExperience } from '../utils/ufoExperiences';
11
12
  import { useCurrentValueRef } from '../utils/useCurrentValueRef';
12
13
  import { getDefaultCardDimensions } from '../utils/cardDimensions';
@@ -35,7 +36,8 @@ export const ExternalImageCard = ({
35
36
  shouldHideTooltip,
36
37
  mediaViewerItems,
37
38
  onClick,
38
- onMouseEnter
39
+ onMouseEnter,
40
+ onFocus
39
41
  }) => {
40
42
  const {
41
43
  createAnalyticsEvent
@@ -203,6 +205,15 @@ export const ExternalImageCard = ({
203
205
  mediaItemDetails: metadata
204
206
  });
205
207
  },
208
+ onFocus: event => {
209
+ if (!isKeyboardFocusEnteringElement(event)) {
210
+ return;
211
+ }
212
+ onFocus === null || onFocus === void 0 ? void 0 : onFocus({
213
+ event,
214
+ mediaItemDetails: metadata
215
+ });
216
+ },
206
217
  disableOverlay: disableOverlay,
207
218
  onDisplayImage: () => {
208
219
  const payloadPart = {
@@ -12,6 +12,7 @@ import { isSSRPreview } from '../types';
12
12
  import { generateUniqueId } from '../utils/generateUniqueId';
13
13
  import { resolveCardPreviewDimensions } from '../utils/getDataURIDimension';
14
14
  import { getMediaCardCursor } from '../utils/getMediaCardCursor';
15
+ import { isKeyboardFocusEnteringElement } from '../utils/isKeyboardFocusEnteringElement';
15
16
  import { getFileDetails } from '../utils/metadata';
16
17
  import { shouldPerformanceBeSampled, useMediaCardUfoExperience } from '../utils/ufoExperiences';
17
18
  import { useCurrentValueRef } from '../utils/useCurrentValueRef';
@@ -76,6 +77,7 @@ export const FileCard = ({
76
77
  mediaViewerItems,
77
78
  onClick,
78
79
  onMouseEnter,
80
+ onFocus,
79
81
  videoControlsWrapperRef,
80
82
  viewerOptions,
81
83
  includeHashForDuplicateFiles,
@@ -633,6 +635,15 @@ export const FileCard = ({
633
635
  mediaItemDetails: metadata
634
636
  });
635
637
  };
638
+ const onImageFocus = event => {
639
+ if (!isKeyboardFocusEnteringElement(event)) {
640
+ return;
641
+ }
642
+ onFocus === null || onFocus === void 0 ? void 0 : onFocus({
643
+ event,
644
+ mediaItemDetails: metadata
645
+ });
646
+ };
636
647
 
637
648
  //----------------------------------------------------------------//
638
649
  //---------------------- Render Card Function --------------------//
@@ -670,6 +681,7 @@ export const FileCard = ({
670
681
  openMediaViewerButtonRef: mediaViewerButtonRef,
671
682
  onClick: withCallbacks ? onCardViewClick : undefined,
672
683
  onMouseEnter: withCallbacks ? onImageMouseEnter : undefined,
684
+ onFocus: withCallbacks ? onImageFocus : undefined,
673
685
  disableOverlay: disableOverlay,
674
686
  progress: uploadProgressRef.current,
675
687
  onDisplayImage: withCallbacks ? () => {
@@ -66,7 +66,7 @@ class WrappedMediaCardAnalyticsErrorBoundary extends React.Component {
66
66
  }
67
67
  _defineProperty(WrappedMediaCardAnalyticsErrorBoundary, "displayName", 'MediaCardAnalyticsErrorBoundary');
68
68
  const packageName = "@atlaskit/media-card";
69
- const packageVersion = "81.7.0";
69
+ const packageVersion = "81.7.2";
70
70
 
71
71
  // @ts-ignore: [PIT-1685] Fails in post-office due to backwards incompatibility issue with React 18
72
72
  const MediaCardAnalyticsErrorBoundary = withMediaAnalyticsContext({
@@ -55,6 +55,7 @@ export const Wrapper = props => {
55
55
  appearance,
56
56
  onClick,
57
57
  onMouseEnter,
58
+ onFocus,
58
59
  innerRef,
59
60
  breakpoint,
60
61
  disableOverlay,
@@ -81,10 +82,9 @@ export const Wrapper = props => {
81
82
  ...getResponsiveStyles(breakpoint)
82
83
  },
83
84
  ref: innerRef,
84
- onClick: onClick
85
- // eslint-disable-next-line @atlassian/a11y/mouse-events-have-key-events
86
- ,
87
- onMouseEnter: onMouseEnter
85
+ onClick: onClick,
86
+ onMouseEnter: onMouseEnter,
87
+ onFocus: onFocus
88
88
  }, VcMediaWrapperProps, {
89
89
  role: "none"
90
90
  }), /*#__PURE__*/React.createElement(UFOCustomData, {
@@ -37,7 +37,7 @@ export default class MediaInlineCardLoader extends React.PureComponent {
37
37
  } = this.state;
38
38
  const analyticsContext = {
39
39
  packageVersion: "@atlaskit/media-card",
40
- packageName: "81.7.0",
40
+ packageName: "81.7.2",
41
41
  componentName: 'mediaInlineCard',
42
42
  component: 'mediaInlineCard'
43
43
  };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Returns `true` only for *keyboard* focus entering an element from outside it.
3
+ *
4
+ * `onFocus` alone is unsuitable because:
5
+ * 1. It bubbles (native `focusin`), firing on internal focus moves. We ignore these via
6
+ * `relatedTarget` when it's contained by the current target.
7
+ * 2. Clicking an interactive descendant also focuses it, duplicating pointer events. We use
8
+ * `:focus-visible` to keep pointer behaviour unchanged.
9
+ */
10
+ export const isKeyboardFocusEnteringElement = event => {
11
+ // Ignore focus moving between elements inside the same element.
12
+ if (event.currentTarget.contains(event.relatedTarget)) {
13
+ return false;
14
+ }
15
+ try {
16
+ if (!event.target.matches(':focus-visible')) {
17
+ return false;
18
+ }
19
+ } catch {
20
+ // Ignore errors from environments that don't support :focus-visible.
21
+ }
22
+ return true;
23
+ };
@@ -11,7 +11,7 @@ import { getMediaEnvironment, getMediaRegion } from '@atlaskit/media-client';
11
11
  import { getActiveInteraction } from '@atlaskit/react-ufo/interaction-metrics';
12
12
  import { getMediaGlobalScope } from './globalScope/globalScope';
13
13
  const packageName = "@atlaskit/media-card";
14
- const packageVersion = "81.7.0";
14
+ const packageVersion = "81.7.2";
15
15
  const SAMPLE_RATE = 0.05;
16
16
 
17
17
  /**
@@ -11,7 +11,7 @@ import { startResourceObserver, setAnalyticsContext } from '../utils/mediaPerfor
11
11
  import { useAnalyticsEvents } from '@atlaskit/analytics-next/useAnalyticsEvents';
12
12
  import UFOLabel from '@atlaskit/react-ufo/label';
13
13
  var packageName = "@atlaskit/media-card";
14
- var packageVersion = "81.7.0";
14
+ var packageVersion = "81.7.2";
15
15
  export var CardBase = function CardBase(_ref) {
16
16
  var identifier = _ref.identifier,
17
17
  otherProps = _objectWithoutProperties(_ref, _excluded);
@@ -55,6 +55,7 @@ export var CardViewBase = function CardViewBase(_ref) {
55
55
  dimensions = _ref.dimensions,
56
56
  onClick = _ref.onClick,
57
57
  onMouseEnter = _ref.onMouseEnter,
58
+ onFocus = _ref.onFocus,
58
59
  testId = _ref.testId,
59
60
  metadata = _ref.metadata,
60
61
  status = _ref.status,
@@ -365,6 +366,7 @@ export var CardViewBase = function CardViewBase(_ref) {
365
366
  onClick: onClick,
366
367
  ariaLabel: name || 'Media Card',
367
368
  onMouseEnter: onMouseEnter,
369
+ onFocus: onFocus,
368
370
  innerRef: mergedRef,
369
371
  breakpoint: breakpoint,
370
372
  mediaCardCursor: mediaCardCursor,
@@ -11,6 +11,7 @@ import ReactDOM from 'react-dom';
11
11
  import { ImageLoadError } from '../errors';
12
12
  import { generateUniqueId } from '../utils/generateUniqueId';
13
13
  import { getMediaCardCursor } from '../utils/getMediaCardCursor';
14
+ import { isKeyboardFocusEnteringElement } from '../utils/isKeyboardFocusEnteringElement';
14
15
  import { shouldPerformanceBeSampled, useMediaCardUfoExperience } from '../utils/ufoExperiences';
15
16
  import { useCurrentValueRef } from '../utils/useCurrentValueRef';
16
17
  import { getDefaultCardDimensions } from '../utils/cardDimensions';
@@ -42,7 +43,8 @@ export var ExternalImageCard = function ExternalImageCard(_ref) {
42
43
  shouldHideTooltip = _ref.shouldHideTooltip,
43
44
  mediaViewerItems = _ref.mediaViewerItems,
44
45
  _onClick = _ref.onClick,
45
- _onMouseEnter = _ref.onMouseEnter;
46
+ _onMouseEnter = _ref.onMouseEnter,
47
+ _onFocus = _ref.onFocus;
46
48
  var _useAnalyticsEvents = useAnalyticsEvents(),
47
49
  createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent;
48
50
  var cardDimensions = dimensions || getDefaultCardDimensions(appearance);
@@ -230,6 +232,15 @@ export var ExternalImageCard = function ExternalImageCard(_ref) {
230
232
  mediaItemDetails: metadata
231
233
  });
232
234
  },
235
+ onFocus: function onFocus(event) {
236
+ if (!isKeyboardFocusEnteringElement(event)) {
237
+ return;
238
+ }
239
+ _onFocus === null || _onFocus === void 0 || _onFocus({
240
+ event: event,
241
+ mediaItemDetails: metadata
242
+ });
243
+ },
233
244
  disableOverlay: disableOverlay,
234
245
  onDisplayImage: function onDisplayImage() {
235
246
  var payloadPart = {
@@ -19,6 +19,7 @@ import { isSSRPreview } from '../types';
19
19
  import { generateUniqueId } from '../utils/generateUniqueId';
20
20
  import { resolveCardPreviewDimensions } from '../utils/getDataURIDimension';
21
21
  import { getMediaCardCursor } from '../utils/getMediaCardCursor';
22
+ import { isKeyboardFocusEnteringElement } from '../utils/isKeyboardFocusEnteringElement';
22
23
  import { getFileDetails } from '../utils/metadata';
23
24
  import { shouldPerformanceBeSampled, useMediaCardUfoExperience } from '../utils/ufoExperiences';
24
25
  import { useCurrentValueRef } from '../utils/useCurrentValueRef';
@@ -87,6 +88,7 @@ export var FileCard = function FileCard(_ref) {
87
88
  mediaViewerItems = _ref.mediaViewerItems,
88
89
  onClick = _ref.onClick,
89
90
  onMouseEnter = _ref.onMouseEnter,
91
+ onFocus = _ref.onFocus,
90
92
  videoControlsWrapperRef = _ref.videoControlsWrapperRef,
91
93
  viewerOptions = _ref.viewerOptions,
92
94
  includeHashForDuplicateFiles = _ref.includeHashForDuplicateFiles,
@@ -708,6 +710,15 @@ export var FileCard = function FileCard(_ref) {
708
710
  mediaItemDetails: metadata
709
711
  });
710
712
  };
713
+ var onImageFocus = function onImageFocus(event) {
714
+ if (!isKeyboardFocusEnteringElement(event)) {
715
+ return;
716
+ }
717
+ onFocus === null || onFocus === void 0 || onFocus({
718
+ event: event,
719
+ mediaItemDetails: metadata
720
+ });
721
+ };
711
722
 
712
723
  //----------------------------------------------------------------//
713
724
  //---------------------- Render Card Function --------------------//
@@ -746,6 +757,7 @@ export var FileCard = function FileCard(_ref) {
746
757
  openMediaViewerButtonRef: mediaViewerButtonRef,
747
758
  onClick: withCallbacks ? onCardViewClick : undefined,
748
759
  onMouseEnter: withCallbacks ? onImageMouseEnter : undefined,
760
+ onFocus: withCallbacks ? onImageFocus : undefined,
749
761
  disableOverlay: disableOverlay,
750
762
  progress: uploadProgressRef.current,
751
763
  onDisplayImage: withCallbacks ? function () {
@@ -80,7 +80,7 @@ var WrappedMediaCardAnalyticsErrorBoundary = /*#__PURE__*/function (_React$Compo
80
80
  }(React.Component);
81
81
  _defineProperty(WrappedMediaCardAnalyticsErrorBoundary, "displayName", 'MediaCardAnalyticsErrorBoundary');
82
82
  var packageName = "@atlaskit/media-card";
83
- var packageVersion = "81.7.0";
83
+ var packageVersion = "81.7.2";
84
84
 
85
85
  // @ts-ignore: [PIT-1685] Fails in post-office due to backwards incompatibility issue with React 18
86
86
  var MediaCardAnalyticsErrorBoundary = withMediaAnalyticsContext({
@@ -51,6 +51,7 @@ export var Wrapper = function Wrapper(props) {
51
51
  appearance = props.appearance,
52
52
  onClick = props.onClick,
53
53
  onMouseEnter = props.onMouseEnter,
54
+ onFocus = props.onFocus,
54
55
  innerRef = props.innerRef,
55
56
  breakpoint = props.breakpoint,
56
57
  disableOverlay = props.disableOverlay,
@@ -71,10 +72,9 @@ export var Wrapper = function Wrapper(props) {
71
72
  "data-testid": testId,
72
73
  style: _objectSpread(_defineProperty(_defineProperty({}, LOCAL_WIDTH_VARIABLE, width), LOCAL_HEIGHT_VARIABLE, height), getResponsiveStyles(breakpoint)),
73
74
  ref: innerRef,
74
- onClick: onClick
75
- // eslint-disable-next-line @atlassian/a11y/mouse-events-have-key-events
76
- ,
77
- onMouseEnter: onMouseEnter
75
+ onClick: onClick,
76
+ onMouseEnter: onMouseEnter,
77
+ onFocus: onFocus
78
78
  }, VcMediaWrapperProps, {
79
79
  role: "none"
80
80
  }), /*#__PURE__*/React.createElement(UFOCustomData, {
@@ -101,7 +101,7 @@ var MediaInlineCardLoader = /*#__PURE__*/function (_React$PureComponent) {
101
101
  ErrorBoundary = _this$state.ErrorBoundary;
102
102
  var analyticsContext = {
103
103
  packageVersion: "@atlaskit/media-card",
104
- packageName: "81.7.0",
104
+ packageName: "81.7.2",
105
105
  componentName: 'mediaInlineCard',
106
106
  component: 'mediaInlineCard'
107
107
  };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Returns `true` only for *keyboard* focus entering an element from outside it.
3
+ *
4
+ * `onFocus` alone is unsuitable because:
5
+ * 1. It bubbles (native `focusin`), firing on internal focus moves. We ignore these via
6
+ * `relatedTarget` when it's contained by the current target.
7
+ * 2. Clicking an interactive descendant also focuses it, duplicating pointer events. We use
8
+ * `:focus-visible` to keep pointer behaviour unchanged.
9
+ */
10
+ export var isKeyboardFocusEnteringElement = function isKeyboardFocusEnteringElement(event) {
11
+ // Ignore focus moving between elements inside the same element.
12
+ if (event.currentTarget.contains(event.relatedTarget)) {
13
+ return false;
14
+ }
15
+ try {
16
+ if (!event.target.matches(':focus-visible')) {
17
+ return false;
18
+ }
19
+ } catch (_unused) {
20
+ // Ignore errors from environments that don't support :focus-visible.
21
+ }
22
+ return true;
23
+ };
@@ -15,7 +15,7 @@ import { getMediaEnvironment, getMediaRegion } from '@atlaskit/media-client';
15
15
  import { getActiveInteraction } from '@atlaskit/react-ufo/interaction-metrics';
16
16
  import { getMediaGlobalScope } from './globalScope/globalScope';
17
17
  var packageName = "@atlaskit/media-card";
18
- var packageVersion = "81.7.0";
18
+ var packageVersion = "81.7.2";
19
19
  var SAMPLE_RATE = 0.05;
20
20
 
21
21
  /**
@@ -1,4 +1,4 @@
1
- import React, { type MouseEvent } from 'react';
1
+ import React, { type FocusEvent, type MouseEvent } from 'react';
2
2
  import { type MessageDescriptor } from 'react-intl';
3
3
  import { type MediaItemType, type FileDetails, type ImageResizeMode, type Identifier } from '@atlaskit/media-client';
4
4
  import { type WithAnalyticsEventsProps } from '@atlaskit/analytics-next/withAnalyticsEvents';
@@ -29,6 +29,7 @@ export interface CardViewProps {
29
29
  readonly openMediaViewerButtonRef?: React.Ref<HTMLButtonElement>;
30
30
  readonly shouldOpenMediaViewer?: boolean;
31
31
  readonly onMouseEnter?: (event: MouseEvent<HTMLDivElement>) => void;
32
+ readonly onFocus?: (event: FocusEvent<HTMLDivElement>) => void;
32
33
  readonly onDisplayImage?: () => void;
33
34
  readonly cardPreview?: MediaFilePreview;
34
35
  readonly progress?: number;
@@ -65,6 +66,6 @@ export interface RenderConfigByStatus {
65
66
  customTitleMessage?: MessageDescriptor;
66
67
  traceTooltipVariant?: TraceTooltipVariant;
67
68
  }
68
- export declare const CardViewBase: ({ identifier, innerRef, onImageLoad, onImageError, dimensions, onClick, onMouseEnter, testId, metadata, status, selected, selectable, cardPreview, mediaCardCursor, shouldHideTooltip, progress, alt, resizeMode, onDisplayImage, nativeLazyLoad, forceSyncDisplay, actions, disableOverlay, titleBoxBgColor, titleBoxIcon, error, disableAnimation, openMediaViewerButtonRef, shouldOpenMediaViewer, overriddenCreationDate, onSvgError, onSvgLoad, traceId, isAIGenerating, isCWR, backgroundColor, }: CardViewBaseProps) => React.JSX.Element;
69
+ export declare const CardViewBase: ({ identifier, innerRef, onImageLoad, onImageError, dimensions, onClick, onMouseEnter, onFocus, testId, metadata, status, selected, selectable, cardPreview, mediaCardCursor, shouldHideTooltip, progress, alt, resizeMode, onDisplayImage, nativeLazyLoad, forceSyncDisplay, actions, disableOverlay, titleBoxBgColor, titleBoxIcon, error, disableAnimation, openMediaViewerButtonRef, shouldOpenMediaViewer, overriddenCreationDate, onSvgError, onSvgLoad, traceId, isAIGenerating, isCWR, backgroundColor, }: CardViewBaseProps) => React.JSX.Element;
69
70
  export declare const CardView: React.ForwardRefExoticComponent<Omit<CardViewProps, keyof WithAnalyticsEventsProps> & React.RefAttributes<any>>;
70
71
  export {};
@@ -9,4 +9,4 @@ export interface ExternalImageCardProps extends SharedCardProps, CardEventProps
9
9
  readonly contextId?: string;
10
10
  readonly shouldHideTooltip?: boolean;
11
11
  }
12
- export declare const ExternalImageCard: ({ mediaClient, appearance, resizeMode, disableOverlay, featureFlags, identifier, dimensions, contextId, alt, actions, shouldOpenMediaViewer, selectable, selected, testId, titleBoxBgColor, titleBoxIcon, shouldHideTooltip, mediaViewerItems, onClick, onMouseEnter, }: ExternalImageCardProps) => React.JSX.Element;
12
+ export declare const ExternalImageCard: ({ mediaClient, appearance, resizeMode, disableOverlay, featureFlags, identifier, dimensions, contextId, alt, actions, shouldOpenMediaViewer, selectable, selected, testId, titleBoxBgColor, titleBoxIcon, shouldHideTooltip, mediaViewerItems, onClick, onMouseEnter, onFocus, }: ExternalImageCardProps) => React.JSX.Element;
@@ -86,4 +86,4 @@ export interface FileCardProps extends CardEventProps {
86
86
  */
87
87
  readonly fallbackMediaNameFetcher?: (id: string) => Promise<string>;
88
88
  }
89
- export declare const FileCard: ({ appearance, resizeMode, isLazy, disableOverlay, featureFlags, identifier, ssr, dimensions, originalDimensions, contextId, alt, actions, shouldEnableDownloadButton, useInlinePlayer, shouldOpenMediaViewer, onFullscreenChange, selectable, selected, testId, titleBoxBgColor, titleBoxIcon, backgroundColor, isAIGenerating, isCWR, onPreviewRender, shouldHideTooltip, mediaViewerItems, onClick, onMouseEnter, videoControlsWrapperRef, viewerOptions, includeHashForDuplicateFiles, ssrItemDetails, ssrFileState, onError, mediaViewerExtensions, fallbackMediaNameFetcher, }: FileCardProps) => React.JSX.Element;
89
+ export declare const FileCard: ({ appearance, resizeMode, isLazy, disableOverlay, featureFlags, identifier, ssr, dimensions, originalDimensions, contextId, alt, actions, shouldEnableDownloadButton, useInlinePlayer, shouldOpenMediaViewer, onFullscreenChange, selectable, selected, testId, titleBoxBgColor, titleBoxIcon, backgroundColor, isAIGenerating, isCWR, onPreviewRender, shouldHideTooltip, mediaViewerItems, onClick, onMouseEnter, onFocus, videoControlsWrapperRef, viewerOptions, includeHashForDuplicateFiles, ssrItemDetails, ssrFileState, onError, mediaViewerExtensions, fallbackMediaNameFetcher, }: FileCardProps) => React.JSX.Element;
@@ -1,4 +1,4 @@
1
- import { type MouseEvent } from 'react';
1
+ import { type FocusEvent, type MouseEvent } from 'react';
2
2
  import type React from 'react';
3
3
  import type UIAnalyticsEvent from '@atlaskit/analytics-next/UIAnalyticsEvent';
4
4
  import { type CardDimensions, type CardAppearance } from '../../../types';
@@ -12,6 +12,7 @@ export interface WrapperProps {
12
12
  appearance?: CardAppearance;
13
13
  onClick?: (event: React.MouseEvent<HTMLDivElement>, analyticsEvent?: UIAnalyticsEvent) => void;
14
14
  onMouseEnter?: (event: MouseEvent<HTMLDivElement>) => void;
15
+ onFocus?: (event: FocusEvent<HTMLDivElement>) => void;
15
16
  mediaType?: string;
16
17
  disableOverlay: boolean;
17
18
  displayBackground: boolean;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Entry Point: @atlaskit/media-card/types
3
3
  */
4
- import { type MouseEvent } from 'react';
4
+ import { type FocusEvent, type MouseEvent } from 'react';
5
5
  import { type FileDetails, type MediaClient, type Identifier, type ImageResizeMode, type FileState } from '@atlaskit/media-client';
6
6
  import type { MediaSettings } from '@atlaskit/media-client-react/media-parsed-settings';
7
7
  import type UIAnalyticsEvent from '@atlaskit/analytics-next/UIAnalyticsEvent';
@@ -48,6 +48,10 @@ export interface CardEvent {
48
48
  event: MouseEvent<HTMLElement>;
49
49
  mediaItemDetails?: FileDetails;
50
50
  }
51
+ export interface CardFocusEvent {
52
+ event: FocusEvent<HTMLElement>;
53
+ mediaItemDetails?: FileDetails;
54
+ }
51
55
  export interface InlineCardEvent {
52
56
  event: MouseEvent<HTMLElement> | React.KeyboardEvent;
53
57
  mediaItemDetails?: FileDetails;
@@ -83,6 +87,7 @@ export interface InlineCardOnClickCallback {
83
87
  export interface CardEventProps {
84
88
  readonly onClick?: CardOnClickCallback;
85
89
  readonly onMouseEnter?: (result: CardEvent) => void;
90
+ readonly onFocus?: (result: CardFocusEvent) => void;
86
91
  /** Callback function to be called when video enters and exit fullscreen.
87
92
  * `fullscreen = true` indicates video enters fullscreen
88
93
  * `fullscreen = false` indicates video exits fullscreen
@@ -0,0 +1,11 @@
1
+ import { type FocusEvent } from 'react';
2
+ /**
3
+ * Returns `true` only for *keyboard* focus entering an element from outside it.
4
+ *
5
+ * `onFocus` alone is unsuitable because:
6
+ * 1. It bubbles (native `focusin`), firing on internal focus moves. We ignore these via
7
+ * `relatedTarget` when it's contained by the current target.
8
+ * 2. Clicking an interactive descendant also focuses it, duplicating pointer events. We use
9
+ * `:focus-visible` to keep pointer behaviour unchanged.
10
+ */
11
+ export declare const isKeyboardFocusEnteringElement: (event: FocusEvent<HTMLElement>) => boolean;
@@ -19,6 +19,7 @@ import { payloadPublisher } from '@atlassian/ufo';
19
19
  import AnnotateIcon from '@atlaskit/icon/core/edit';
20
20
  import { SelectableCard } from './selectableCard';
21
21
  import { Card, type CardAppearance, type CardEvent, type CardAction } from '../src';
22
+ import { type CardFocusEvent } from '../src/types';
22
23
  import { MediaCardError } from '../src/errors';
23
24
  import DevelopmentUseMessage from './developmentUseMessage';
24
25
 
@@ -34,6 +35,10 @@ export const mouseEnterHandler = (result: CardEvent): void => {
34
35
  console.log('mouseEnter', result.mediaItemDetails);
35
36
  };
36
37
 
38
+ export const focusHandler = (result: CardFocusEvent): void => {
39
+ console.log('focus', result.mediaItemDetails);
40
+ };
41
+
37
42
  export const createApiCards = (
38
43
  appearance: CardAppearance,
39
44
  identifier: Identifier,
@@ -52,6 +57,7 @@ export const createApiCards = (
52
57
  identifier={identifier}
53
58
  onClick={clickHandler}
54
59
  onMouseEnter={mouseEnterHandler}
60
+ onFocus={focusHandler}
55
61
  />
56
62
  ),
57
63
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/media-card",
3
- "version": "81.7.1",
3
+ "version": "81.8.0",
4
4
  "description": "Includes all media card related components, CardView, CardViewSmall, Card...",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -52,7 +52,7 @@
52
52
  "@atlaskit/section-message": "^10.1.0",
53
53
  "@atlaskit/spinner": "^20.3.0",
54
54
  "@atlaskit/theme": "^28.1.0",
55
- "@atlaskit/tmp-editor-statsig": "^158.0.0",
55
+ "@atlaskit/tmp-editor-statsig": "^159.0.0",
56
56
  "@atlaskit/tokens": "^16.8.0",
57
57
  "@atlaskit/tooltip": "^24.2.0",
58
58
  "@atlaskit/ufo": "^1.0.0",