carbon-react 114.12.3 → 114.13.1

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.
@@ -1,8 +1,9 @@
1
- import React, { useState, useRef, useEffect, useMemo, useCallback, createRef } from "react";
1
+ import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
2
2
  import PropTypes from "prop-types";
3
3
  import { isFragment } from "react-is";
4
4
  import invariant from "invariant";
5
5
  import throttle from "lodash/throttle";
6
+ import { defaultFocusableSelectors } from "../../__internal__/focus-trap/focus-trap-utils";
6
7
  import Event from "../../__internal__/utils/helpers/events";
7
8
  import { StyledAnchorNavigation, StyledNavigation, StyledContent } from "./anchor-navigation.style";
8
9
  import AnchorNavigationItem from "./anchor-navigation-item/anchor-navigation-item.component";
@@ -23,9 +24,6 @@ const AnchorNavigation = ({
23
24
  !hasCorrectItemStructure ? process.env.NODE_ENV !== "production" ? invariant(false, `\`stickyNavigation\` prop in \`AnchorNavigation\` should be a React Fragment that only contains children of type \`${AnchorNavigationItem.displayName}\``) : invariant(false) : void 0;
24
25
  const [selectedIndex, setSelectedIndex] = useState(0);
25
26
  const sectionRefs = useRef(React.Children.map(stickyNavigation.props.children, child => child.props.target));
26
- const anchorRefs = useRef(Array.from({
27
- length: React.Children.count(stickyNavigation.props.children)
28
- }, () => /*#__PURE__*/createRef()));
29
27
  const contentRef = useRef(null);
30
28
  const navigationRef = useRef(null);
31
29
  const isUserScroll = useRef(true);
@@ -51,8 +49,13 @@ const AnchorNavigation = ({
51
49
  if (isUserScroll.current) {
52
50
  setSelectedAnchorBasedOnScroll();
53
51
  } else {
54
- if (isUserScrollTimer.current !== undefined) window.clearTimeout(isUserScrollTimer.current);
55
- isUserScrollTimer.current = setTimeout(() => {
52
+ if (isUserScrollTimer.current !== undefined) {
53
+ window.clearTimeout(isUserScrollTimer.current);
54
+ }
55
+
56
+ isUserScrollTimer.current = setTimeout(
57
+ /* istanbul ignore next */
58
+ () => {
56
59
  isUserScroll.current = true;
57
60
  }, SCROLL_THROTTLE + 50);
58
61
  }
@@ -62,23 +65,30 @@ const AnchorNavigation = ({
62
65
  return () => window.removeEventListener("scroll", scrollHandler, true);
63
66
  }, [scrollHandler]);
64
67
 
65
- const focusFirstFocusableChild = section => {
66
- const defaultFocusableSelectors = 'button, [href], input:not([type="hidden"]), select, textarea, [tabindex]:not([tabindex="-1"])';
67
- const firstFocusableElement = section.querySelector(defaultFocusableSelectors);
68
-
69
- if (firstFocusableElement) {
70
- firstFocusableElement.focus({
71
- preventScroll: true
72
- });
68
+ const focusSection = section => {
69
+ if (!section.matches(defaultFocusableSelectors)) {
70
+ section.setAttribute("tabindex", "-1");
73
71
  }
72
+
73
+ section.focus({
74
+ preventScroll: true
75
+ });
74
76
  };
75
77
 
76
78
  const scrollToSection = index => {
77
79
  const sectionToScroll = sectionRefs.current[index].current; // istanbul ignore if
78
80
  // function is called only after component is rendered, so ref cannot hold a null value
79
81
 
80
- if (sectionToScroll === null) return;
81
- focusFirstFocusableChild(sectionToScroll); // workaround due to preventScroll focus method option on firefox not working consistently
82
+ if (sectionToScroll === null) return; // ensure section has the appropriate element to remove the default focus styles.
83
+ // Can ignore else branch because there's no harm in setting this to "true" twice (it can't hold any other value),
84
+ // but it's probably more efficient not to.
85
+ // istanbul ignore else
86
+
87
+ if (!sectionToScroll.dataset.carbonAnchornavRef) {
88
+ sectionToScroll.dataset.carbonAnchornavRef = "true";
89
+ }
90
+
91
+ focusSection(sectionToScroll); // workaround due to preventScroll focus method option on firefox not working consistently
82
92
 
83
93
  window.setTimeout(() => {
84
94
  isUserScroll.current = false;
@@ -91,26 +101,13 @@ const AnchorNavigation = ({
91
101
  }, 10);
92
102
  };
93
103
 
94
- const focusNavItem = index => {
95
- var _anchorRefs$current$c;
96
-
97
- const noOfRefs = anchorRefs.current.length;
98
- (_anchorRefs$current$c = anchorRefs.current[(index % noOfRefs + noOfRefs) % noOfRefs].current) === null || _anchorRefs$current$c === void 0 ? void 0 : _anchorRefs$current$c.focus();
99
- };
100
-
101
104
  const handleClick = (event, index) => {
102
105
  event.preventDefault();
103
106
  scrollToSection(index);
104
107
  };
105
108
 
106
109
  const handleKeyDown = (event, index) => {
107
- event.preventDefault();
108
-
109
- if (Event.isUpKey(event)) {
110
- focusNavItem(index - 1);
111
- } else if (Event.isDownKey(event)) {
112
- focusNavItem(index + 1);
113
- } else if (Event.isEnterKey(event) || Event.isSpaceKey(event)) {
110
+ if (Event.isEnterKey(event)) {
114
111
  scrollToSection(index);
115
112
  }
116
113
  };
@@ -122,11 +119,11 @@ const AnchorNavigation = ({
122
119
  ref: navigationRef,
123
120
  "data-element": "anchor-sticky-navigation"
124
121
  }, React.Children.map(stickyNavigation.props.children, (child, index) => /*#__PURE__*/React.cloneElement(child, {
122
+ href: child.props.href || "#",
123
+ // need to pass an href to ensure the link is tabbable by default
125
124
  isSelected: index === selectedIndex,
126
- tabIndex: index === selectedIndex ? 0 : -1,
127
125
  onClick: event => handleClick(event, index),
128
- onKeyDown: event => handleKeyDown(event, index),
129
- ref: anchorRefs.current[index]
126
+ onKeyDown: event => handleKeyDown(event, index)
130
127
  }))), /*#__PURE__*/React.createElement(StyledContent, null, children));
131
128
  };
132
129
 
@@ -15,5 +15,9 @@ const StyledNavigation = styled.ul`
15
15
  const StyledContent = styled.div`
16
16
  flex: 1;
17
17
  margin-left: 32px;
18
+
19
+ [data-carbon-anchornav-ref="true"]:focus {
20
+ outline: none;
21
+ }
18
22
  `;
19
23
  export { StyledAnchorNavigation, StyledNavigation, StyledContent };
@@ -46,13 +46,17 @@ const DatePicker = /*#__PURE__*/React.forwardRef(({
46
46
  const weekdaysLong = useMemo(() => Array.from({
47
47
  length: 7
48
48
  }).map((_, i) => localize.day(i)), [localize]);
49
- const weekdaysShort = useMemo(() => Array.from({
50
- length: 7
51
- }).map((_, i) => localize.day(i, ["de", "pl"].filter(str => l.locale().includes(str)).length ? {
52
- width: "wide"
53
- } : {
54
- width: "abbreviated"
55
- }).substring(0, 3)), [l, localize]);
49
+ const weekdaysShort = useMemo(() => {
50
+ const isGivenLocale = str => l.locale().includes(str);
51
+
52
+ return Array.from({
53
+ length: 7
54
+ }).map((_, i) => localize.day(i, ["de", "pl"].some(isGivenLocale) ? {
55
+ width: "wide"
56
+ } : {
57
+ width: "abbreviated"
58
+ }).substring(0, isGivenLocale("de") ? 2 : 3));
59
+ }, [l, localize]);
56
60
 
57
61
  const handleDayClick = (date, {
58
62
  disabled
@@ -15,6 +15,8 @@ var _invariant = _interopRequireDefault(require("invariant"));
15
15
 
16
16
  var _throttle = _interopRequireDefault(require("lodash/throttle"));
17
17
 
18
+ var _focusTrapUtils = require("../../__internal__/focus-trap/focus-trap-utils");
19
+
18
20
  var _events = _interopRequireDefault(require("../../__internal__/utils/helpers/events"));
19
21
 
20
22
  var _anchorNavigation = require("./anchor-navigation.style");
@@ -45,9 +47,6 @@ const AnchorNavigation = ({
45
47
  !hasCorrectItemStructure ? process.env.NODE_ENV !== "production" ? (0, _invariant.default)(false, `\`stickyNavigation\` prop in \`AnchorNavigation\` should be a React Fragment that only contains children of type \`${_anchorNavigationItem.default.displayName}\``) : (0, _invariant.default)(false) : void 0;
46
48
  const [selectedIndex, setSelectedIndex] = (0, _react.useState)(0);
47
49
  const sectionRefs = (0, _react.useRef)(_react.default.Children.map(stickyNavigation.props.children, child => child.props.target));
48
- const anchorRefs = (0, _react.useRef)(Array.from({
49
- length: _react.default.Children.count(stickyNavigation.props.children)
50
- }, () => /*#__PURE__*/(0, _react.createRef)()));
51
50
  const contentRef = (0, _react.useRef)(null);
52
51
  const navigationRef = (0, _react.useRef)(null);
53
52
  const isUserScroll = (0, _react.useRef)(true);
@@ -73,8 +72,13 @@ const AnchorNavigation = ({
73
72
  if (isUserScroll.current) {
74
73
  setSelectedAnchorBasedOnScroll();
75
74
  } else {
76
- if (isUserScrollTimer.current !== undefined) window.clearTimeout(isUserScrollTimer.current);
77
- isUserScrollTimer.current = setTimeout(() => {
75
+ if (isUserScrollTimer.current !== undefined) {
76
+ window.clearTimeout(isUserScrollTimer.current);
77
+ }
78
+
79
+ isUserScrollTimer.current = setTimeout(
80
+ /* istanbul ignore next */
81
+ () => {
78
82
  isUserScroll.current = true;
79
83
  }, SCROLL_THROTTLE + 50);
80
84
  }
@@ -84,23 +88,30 @@ const AnchorNavigation = ({
84
88
  return () => window.removeEventListener("scroll", scrollHandler, true);
85
89
  }, [scrollHandler]);
86
90
 
87
- const focusFirstFocusableChild = section => {
88
- const defaultFocusableSelectors = 'button, [href], input:not([type="hidden"]), select, textarea, [tabindex]:not([tabindex="-1"])';
89
- const firstFocusableElement = section.querySelector(defaultFocusableSelectors);
90
-
91
- if (firstFocusableElement) {
92
- firstFocusableElement.focus({
93
- preventScroll: true
94
- });
91
+ const focusSection = section => {
92
+ if (!section.matches(_focusTrapUtils.defaultFocusableSelectors)) {
93
+ section.setAttribute("tabindex", "-1");
95
94
  }
95
+
96
+ section.focus({
97
+ preventScroll: true
98
+ });
96
99
  };
97
100
 
98
101
  const scrollToSection = index => {
99
102
  const sectionToScroll = sectionRefs.current[index].current; // istanbul ignore if
100
103
  // function is called only after component is rendered, so ref cannot hold a null value
101
104
 
102
- if (sectionToScroll === null) return;
103
- focusFirstFocusableChild(sectionToScroll); // workaround due to preventScroll focus method option on firefox not working consistently
105
+ if (sectionToScroll === null) return; // ensure section has the appropriate element to remove the default focus styles.
106
+ // Can ignore else branch because there's no harm in setting this to "true" twice (it can't hold any other value),
107
+ // but it's probably more efficient not to.
108
+ // istanbul ignore else
109
+
110
+ if (!sectionToScroll.dataset.carbonAnchornavRef) {
111
+ sectionToScroll.dataset.carbonAnchornavRef = "true";
112
+ }
113
+
114
+ focusSection(sectionToScroll); // workaround due to preventScroll focus method option on firefox not working consistently
104
115
 
105
116
  window.setTimeout(() => {
106
117
  isUserScroll.current = false;
@@ -113,26 +124,13 @@ const AnchorNavigation = ({
113
124
  }, 10);
114
125
  };
115
126
 
116
- const focusNavItem = index => {
117
- var _anchorRefs$current$c;
118
-
119
- const noOfRefs = anchorRefs.current.length;
120
- (_anchorRefs$current$c = anchorRefs.current[(index % noOfRefs + noOfRefs) % noOfRefs].current) === null || _anchorRefs$current$c === void 0 ? void 0 : _anchorRefs$current$c.focus();
121
- };
122
-
123
127
  const handleClick = (event, index) => {
124
128
  event.preventDefault();
125
129
  scrollToSection(index);
126
130
  };
127
131
 
128
132
  const handleKeyDown = (event, index) => {
129
- event.preventDefault();
130
-
131
- if (_events.default.isUpKey(event)) {
132
- focusNavItem(index - 1);
133
- } else if (_events.default.isDownKey(event)) {
134
- focusNavItem(index + 1);
135
- } else if (_events.default.isEnterKey(event) || _events.default.isSpaceKey(event)) {
133
+ if (_events.default.isEnterKey(event)) {
136
134
  scrollToSection(index);
137
135
  }
138
136
  };
@@ -144,11 +142,11 @@ const AnchorNavigation = ({
144
142
  ref: navigationRef,
145
143
  "data-element": "anchor-sticky-navigation"
146
144
  }, _react.default.Children.map(stickyNavigation.props.children, (child, index) => /*#__PURE__*/_react.default.cloneElement(child, {
145
+ href: child.props.href || "#",
146
+ // need to pass an href to ensure the link is tabbable by default
147
147
  isSelected: index === selectedIndex,
148
- tabIndex: index === selectedIndex ? 0 : -1,
149
148
  onClick: event => handleClick(event, index),
150
- onKeyDown: event => handleKeyDown(event, index),
151
- ref: anchorRefs.current[index]
149
+ onKeyDown: event => handleKeyDown(event, index)
152
150
  }))), /*#__PURE__*/_react.default.createElement(_anchorNavigation.StyledContent, null, children));
153
151
  };
154
152
 
@@ -27,5 +27,9 @@ exports.StyledNavigation = StyledNavigation;
27
27
  const StyledContent = _styledComponents.default.div`
28
28
  flex: 1;
29
29
  margin-left: 32px;
30
+
31
+ [data-carbon-anchornav-ref="true"]:focus {
32
+ outline: none;
33
+ }
30
34
  `;
31
35
  exports.StyledContent = StyledContent;
@@ -70,13 +70,17 @@ const DatePicker = /*#__PURE__*/_react.default.forwardRef(({
70
70
  const weekdaysLong = (0, _react.useMemo)(() => Array.from({
71
71
  length: 7
72
72
  }).map((_, i) => localize.day(i)), [localize]);
73
- const weekdaysShort = (0, _react.useMemo)(() => Array.from({
74
- length: 7
75
- }).map((_, i) => localize.day(i, ["de", "pl"].filter(str => l.locale().includes(str)).length ? {
76
- width: "wide"
77
- } : {
78
- width: "abbreviated"
79
- }).substring(0, 3)), [l, localize]);
73
+ const weekdaysShort = (0, _react.useMemo)(() => {
74
+ const isGivenLocale = str => l.locale().includes(str);
75
+
76
+ return Array.from({
77
+ length: 7
78
+ }).map((_, i) => localize.day(i, ["de", "pl"].some(isGivenLocale) ? {
79
+ width: "wide"
80
+ } : {
81
+ width: "abbreviated"
82
+ }).substring(0, isGivenLocale("de") ? 2 : 3));
83
+ }, [l, localize]);
80
84
 
81
85
  const handleDayClick = (date, {
82
86
  disabled
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carbon-react",
3
- "version": "114.12.3",
3
+ "version": "114.13.1",
4
4
  "description": "A library of reusable React components for easily building user interfaces.",
5
5
  "files": [
6
6
  "lib",