@webex/widgets 1.28.0 → 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
  }
@@ -4854,7 +4854,7 @@ var z = cjs.exports;
4854
4854
 
4855
4855
  /**
4856
4856
  * Webex Component System.
4857
- * Copyright (c) 2024 Cisco Systems, Inc and its affiliates.
4857
+ * Copyright (c) 2025 Cisco Systems, Inc and its affiliates.
4858
4858
  *
4859
4859
  * This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree.
4860
4860
  */
@@ -5717,8 +5717,7 @@ var We = function (r) {
5717
5717
  tooltip: "Microphone Devices",
5718
5718
  noOptionsMessage: "No available microphones",
5719
5719
  options: null,
5720
- selected: null,
5721
- hint: 'Use arrow keys to navigate between microphone options and hit "Enter" to select.'
5720
+ selected: null
5722
5721
  }), r.complete()) : r.error(new Error('Could not find meeting with ID "'.concat(e, '" to add switch microphone control')));
5723
5722
  }),
5724
5723
  i = n.pipe(concatMap(function (e) {
@@ -5922,8 +5921,7 @@ var ut = function (r) {
5922
5921
  tooltip: it ? "Speaker Devices" : "The current browser does not support changing speakers",
5923
5922
  noOptionsMessage: "No available speakers",
5924
5923
  options: null,
5925
- selected: null,
5926
- hint: 'Use arrow keys to navigate between speaker options and hit "Enter" to select.'
5924
+ selected: null
5927
5925
  }), r.complete()) : r.error(new Error('Could not find meeting with ID "'.concat(e, '" to add switch speaker control')));
5928
5926
  }),
5929
5927
  i = n.pipe(concatMap(function (e) {
@@ -48716,9 +48714,7 @@ function mu(e) {
48716
48714
  className: "".concat(x("selected-option"), " ").concat(v ? x("expanded") : ""),
48717
48715
  id: "".concat(P, "-control"),
48718
48716
  onClick: function () {
48719
- return e = !1, void (i || b(v ? void 0 : {
48720
- withKey: e
48721
- }));
48717
+ return e = !1, void (i || (v ? L() : k(e)));
48722
48718
  var e;
48723
48719
  },
48724
48720
  onKeyDown: function (e) {
@@ -56958,11 +56954,159 @@ var WebexMeetingsWidget = /*#__PURE__*/function (_Component) {
56958
56954
  _classCallCheck(this, WebexMeetingsWidget);
56959
56955
  return _callSuper(this, WebexMeetingsWidget, [props]);
56960
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
+ */
56961
56968
  _inherits(WebexMeetingsWidget, _Component);
56962
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
+ }, {
56963
57105
  key: "render",
56964
57106
  value: function render() {
56965
- var _meeting$localAudio, _meeting$localVideo;
57107
+ var _meeting$localAudio,
57108
+ _meeting$localVideo,
57109
+ _this2 = this;
56966
57110
  var meeting = this.props.meeting;
56967
57111
  var audioPermission = (_meeting$localAudio = meeting.localAudio) === null || _meeting$localAudio === void 0 ? void 0 : _meeting$localAudio.permission;
56968
57112
  var videoPermission = (_meeting$localVideo = meeting.localVideo) === null || _meeting$localVideo === void 0 ? void 0 : _meeting$localVideo.permission;
@@ -56998,9 +57142,22 @@ var WebexMeetingsWidget = /*#__PURE__*/function (_Component) {
56998
57142
  }
56999
57143
  return /*#__PURE__*/S.createElement("div", {
57000
57144
  className: "webex-meetings-widget ".concat(this.props.className),
57001
- style: this.props.style
57145
+ style: this.props.style,
57146
+ ref: function ref(div) {
57147
+ _this2.widgetDiv = div;
57148
+ },
57149
+ tabIndex: 0
57002
57150
  }, content);
57003
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
+ }
57004
57161
  }]);
57005
57162
  }(Component);
57006
57163
  WebexMeetingsWidget.propTypes = {
@@ -57035,7 +57192,7 @@ var WebexMeetings = Ic(Pc(WebexMeetingsWidget), function (props) {
57035
57192
  },
57036
57193
  config: {
57037
57194
  appName: appName,
57038
- appVersion: "1.27.8",
57195
+ appVersion: "1.28.1",
57039
57196
  fedramp: props.fedramp,
57040
57197
  meetings: {
57041
57198
  experimental: {