@webex/widgets 1.28.1 → 1.28.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,36 @@ import Webex from 'webex';
4
4
  import require$$2, { createPortal } from 'react-dom';
5
5
  import { deconstructHydraId, SDK_EVENT, constructHydraId } from '@webex/common';
6
6
 
7
+ function _arrayLikeToArray(r, a) {
8
+ (null == a || a > r.length) && (a = r.length);
9
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
10
+ return n;
11
+ }
12
+
13
+ function _arrayWithoutHoles(r) {
14
+ if (Array.isArray(r)) return _arrayLikeToArray(r);
15
+ }
16
+
17
+ function _iterableToArray(r) {
18
+ if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r);
19
+ }
20
+
21
+ function _unsupportedIterableToArray(r, a) {
22
+ if (r) {
23
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
24
+ var t = {}.toString.call(r).slice(8, -1);
25
+ return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
26
+ }
27
+ }
28
+
29
+ function _nonIterableSpread() {
30
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
31
+ }
32
+
33
+ function _toConsumableArray(r) {
34
+ return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
35
+ }
36
+
7
37
  function _classCallCheck(a, n) {
8
38
  if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function");
9
39
  }
@@ -3235,36 +3265,6 @@ var TapSubscriber = /*@__PURE__*/function (_super) {
3235
3265
  return TapSubscriber;
3236
3266
  }(Subscriber);
3237
3267
 
3238
- function _arrayLikeToArray(r, a) {
3239
- (null == a || a > r.length) && (a = r.length);
3240
- for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
3241
- return n;
3242
- }
3243
-
3244
- function _arrayWithoutHoles(r) {
3245
- if (Array.isArray(r)) return _arrayLikeToArray(r);
3246
- }
3247
-
3248
- function _iterableToArray(r) {
3249
- if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r);
3250
- }
3251
-
3252
- function _unsupportedIterableToArray(r, a) {
3253
- if (r) {
3254
- if ("string" == typeof r) return _arrayLikeToArray(r, a);
3255
- var t = {}.toString.call(r).slice(8, -1);
3256
- return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
3257
- }
3258
- }
3259
-
3260
- function _nonIterableSpread() {
3261
- throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
3262
- }
3263
-
3264
- function _toConsumableArray(r) {
3265
- return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
3266
- }
3267
-
3268
3268
  function _arrayWithHoles(r) {
3269
3269
  if (Array.isArray(r)) return r;
3270
3270
  }
@@ -56954,11 +56954,159 @@ var WebexMeetingsWidget = /*#__PURE__*/function (_Component) {
56954
56954
  _classCallCheck(this, WebexMeetingsWidget);
56955
56955
  return _callSuper(this, WebexMeetingsWidget, [props]);
56956
56956
  }
56957
+ /**
56958
+ * Temporary custom accessibility fix:
56959
+ * - Redirects focus to the first actionable control inside the meeting
56960
+ * - Makes video layout focusable and supports left/right arrow navigation
56961
+ * - Prevents focus from escaping to the browser URL bar
56962
+ *
56963
+ * NOTE: This is a workaround because the base @webex/components WebexMeeting
56964
+ * does not yet support these accessibility features.
56965
+ * Once the upstream component is fixed, we must remove this custom code
56966
+ * from our repo to avoid duplication and ensure long-term maintainability.
56967
+ */
56957
56968
  _inherits(WebexMeetingsWidget, _Component);
56958
56969
  return _createClass(WebexMeetingsWidget, [{
56970
+ key: "componentDidMount",
56971
+ value: function componentDidMount() {
56972
+ var _this = this;
56973
+ // When focus comes to the widget container, move to the correct media container before and after joining
56974
+ if (this.widgetDiv) {
56975
+ if (!this._mediaContainerTabHandler) {
56976
+ this._mediaContainerTabHandler = function (evt) {
56977
+ var mediaContainer = evt.currentTarget;
56978
+ // Only handle if the media container itself is focused
56979
+ if ((evt.code === 'Tab' || evt.key === 'Tab') && document.activeElement === mediaContainer) {
56980
+ if (!evt.shiftKey) {
56981
+ evt.preventDefault();
56982
+ var joinButton = _this.widgetDiv.querySelector('button[aria-label="Join meeting"]');
56983
+ if (!joinButton) {
56984
+ joinButton = _this.widgetDiv.querySelector('.wxc-meeting-control button, .wxc-meeting-control [tabindex]:not([tabindex="-1"])');
56985
+ }
56986
+ if (joinButton) {
56987
+ joinButton.focus();
56988
+ }
56989
+ } else {
56990
+ evt.preventDefault();
56991
+ // Move focus back to the widget container
56992
+ if (_this.widgetDiv) {
56993
+ _this.widgetDiv.tabIndex = 0;
56994
+ _this.widgetDiv.focus();
56995
+ }
56996
+ }
56997
+ }
56998
+ };
56999
+ }
57000
+ this.widgetDiv.addEventListener('focus', function () {
57001
+ setTimeout(function () {
57002
+ // Attach handler to both possible media containers if they exist
57003
+ var containers = _toConsumableArray(_this.widgetDiv.querySelectorAll('.wxc-interstitial-meeting__media-container, .wxc-in-meeting__media-container'));
57004
+ if (containers.length > 0) {
57005
+ containers.forEach(function (mediaContainer) {
57006
+ mediaContainer.tabIndex = 0;
57007
+ mediaContainer.removeEventListener('keydown', _this._mediaContainerTabHandler, true);
57008
+ mediaContainer.addEventListener('keydown', _this._mediaContainerTabHandler, true);
57009
+ });
57010
+ } else {
57011
+ // fallback to Join meeting button or first .wxc-meeting-control button
57012
+ var joinButton = _this.widgetDiv.querySelector('button[aria-label="Join meeting"]');
57013
+ if (!joinButton) {
57014
+ joinButton = _this.widgetDiv.querySelector('.wxc-meeting-control button, .wxc-meeting-control [tabindex]:not([tabindex="-1"])');
57015
+ }
57016
+ if (joinButton) {
57017
+ joinButton.focus();
57018
+ }
57019
+ }
57020
+ }, 0);
57021
+ });
57022
+
57023
+ // Arrow key navigation for all meeting control buttons, with MutationObserver
57024
+ var attachArrowNav = function attachArrowNav() {
57025
+ // Gather all focusable meeting controls, including Join meeting button if present
57026
+ var buttons = [];
57027
+ var controlBar = _this.widgetDiv.querySelector('.wxc-meeting-control-bar__controls');
57028
+ if (controlBar) {
57029
+ buttons = Array.from(controlBar.querySelectorAll('button, [tabindex]:not([tabindex="-1"])'));
57030
+ }
57031
+ // Add Join meeting button if present and not already in the list
57032
+ var joinButton = _this.widgetDiv.querySelector('button[aria-label="Join meeting"]');
57033
+ if (joinButton && !buttons.includes(joinButton)) {
57034
+ buttons = [joinButton].concat(_toConsumableArray(buttons));
57035
+ }
57036
+ // Remove any previous listeners to avoid duplicates
57037
+ buttons.forEach(function (btn) {
57038
+ btn.onkeydown = null;
57039
+ });
57040
+ // Attach arrow key listeners to all buttons (including Join meeting)
57041
+ buttons.forEach(function (btn, idx) {
57042
+ btn.onkeydown = function (evt) {
57043
+ if (evt.key === 'ArrowRight') {
57044
+ evt.preventDefault();
57045
+ var next = buttons[(idx + 1) % buttons.length];
57046
+ if (next) next.focus();
57047
+ } else if (evt.key === 'ArrowLeft') {
57048
+ evt.preventDefault();
57049
+ var prev = buttons[(idx - 1 + buttons.length) % buttons.length];
57050
+ if (prev) prev.focus();
57051
+ }
57052
+ };
57053
+ });
57054
+ };
57055
+ // Initial setup
57056
+ setTimeout(attachArrowNav, 700);
57057
+
57058
+ // Observe DOM changes to re-attach listeners if buttons change
57059
+ var observer = new window.MutationObserver(function () {
57060
+ attachArrowNav();
57061
+ });
57062
+ observer.observe(this.widgetDiv, {
57063
+ childList: true,
57064
+ subtree: true
57065
+ });
57066
+ // Clean up observer on unmount
57067
+ this._arrowNavObserver = observer;
57068
+ }
57069
+
57070
+ // When focus comes to the content, wait for .wxc-meeting__inner-meeting and move focus
57071
+ if (this.widgetDiv) {
57072
+ var contentDiv = this.widgetDiv.querySelector('.webex-meetings-widget__content');
57073
+ if (contentDiv) {
57074
+ contentDiv.addEventListener('focus', function () {
57075
+ // Poll for .wxc-meeting__inner-meeting up to 500ms
57076
+ var attempts = 0;
57077
+ var tryFocusInnerMeeting = function tryFocusInnerMeeting() {
57078
+ var innerMeeting = contentDiv.querySelector('.wxc-in-meeting__media-container');
57079
+ if (innerMeeting) {
57080
+ innerMeeting.tabIndex = 0;
57081
+ innerMeeting.focus();
57082
+ // On Tab, move to first interactive element
57083
+ var handleTab = function handleTab(evt) {
57084
+ if (evt.key === 'Tab' && !evt.shiftKey) {
57085
+ evt.preventDefault();
57086
+ var nextInteractive = innerMeeting.querySelector('button, [tabindex]:not([tabindex="-1"])');
57087
+ if (nextInteractive) {
57088
+ nextInteractive.focus();
57089
+ }
57090
+ innerMeeting.removeEventListener('keydown', handleTab);
57091
+ }
57092
+ };
57093
+ innerMeeting.addEventListener('keydown', handleTab);
57094
+ } else if (attempts < 10) {
57095
+ attempts++;
57096
+ setTimeout(tryFocusInnerMeeting, 50);
57097
+ }
57098
+ };
57099
+ tryFocusInnerMeeting();
57100
+ });
57101
+ }
57102
+ }
57103
+ }
57104
+ }, {
56959
57105
  key: "render",
56960
57106
  value: function render() {
56961
- var _meeting$localAudio, _meeting$localVideo;
57107
+ var _meeting$localAudio,
57108
+ _meeting$localVideo,
57109
+ _this2 = this;
56962
57110
  var meeting = this.props.meeting;
56963
57111
  var audioPermission = (_meeting$localAudio = meeting.localAudio) === null || _meeting$localAudio === void 0 ? void 0 : _meeting$localAudio.permission;
56964
57112
  var videoPermission = (_meeting$localVideo = meeting.localVideo) === null || _meeting$localVideo === void 0 ? void 0 : _meeting$localVideo.permission;
@@ -56994,9 +57142,22 @@ var WebexMeetingsWidget = /*#__PURE__*/function (_Component) {
56994
57142
  }
56995
57143
  return /*#__PURE__*/S.createElement("div", {
56996
57144
  className: "webex-meetings-widget ".concat(this.props.className),
56997
- style: this.props.style
57145
+ style: this.props.style,
57146
+ ref: function ref(div) {
57147
+ _this2.widgetDiv = div;
57148
+ },
57149
+ tabIndex: 0
56998
57150
  }, content);
56999
57151
  }
57152
+ }, {
57153
+ key: "componentWillUnmount",
57154
+ value: function componentWillUnmount() {
57155
+ // Clean up MutationObserver if present
57156
+ if (this._arrowNavObserver) {
57157
+ this._arrowNavObserver.disconnect();
57158
+ this._arrowNavObserver = null;
57159
+ }
57160
+ }
57000
57161
  }]);
57001
57162
  }(Component);
57002
57163
  WebexMeetingsWidget.propTypes = {
@@ -57031,7 +57192,7 @@ var WebexMeetings = Ic(Pc(WebexMeetingsWidget), function (props) {
57031
57192
  },
57032
57193
  config: {
57033
57194
  appName: appName,
57034
- appVersion: "1.28.0",
57195
+ appVersion: "1.28.1",
57035
57196
  fedramp: props.fedramp,
57036
57197
  meetings: {
57037
57198
  experimental: {