@clayui/tabs 3.75.2 → 3.78.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.
package/lib/Item.js CHANGED
@@ -35,9 +35,9 @@ var Item = /*#__PURE__*/_react.default.forwardRef(function (_ref, ref) {
35
35
  otherProps = _objectWithoutProperties(_ref, _excluded);
36
36
 
37
37
  return /*#__PURE__*/_react.default.createElement("li", _extends({}, otherProps, {
38
- className: (0, _classnames.default)('nav-item', className)
38
+ className: (0, _classnames.default)('nav-item', className),
39
+ role: "none"
39
40
  }), /*#__PURE__*/_react.default.createElement(_shared.LinkOrButton, _extends({}, innerProps, {
40
- "aria-disabled": !active,
41
41
  "aria-selected": active,
42
42
  buttonDisplayType: "unstyled",
43
43
  buttonType: "button",
@@ -50,7 +50,7 @@ var Item = /*#__PURE__*/_react.default.forwardRef(function (_ref, ref) {
50
50
  onClick: onClick,
51
51
  ref: ref,
52
52
  role: "tab",
53
- tabIndex: disabled ? -1 : undefined
53
+ tabIndex: !active ? -1 : 0
54
54
  }), children));
55
55
  });
56
56
 
package/lib/TabPane.js CHANGED
@@ -85,7 +85,8 @@ var TabPane = function TabPane(_ref) {
85
85
  fade: fade,
86
86
  show: internalShow
87
87
  }, className),
88
- role: "tabpanel"
88
+ role: "tabpanel",
89
+ tabIndex: 0
89
90
  }), children);
90
91
  };
91
92
 
package/lib/index.d.ts CHANGED
@@ -2,12 +2,29 @@
2
2
  * SPDX-FileCopyrightText: © 2019 Liferay, Inc. <https://liferay.com>
3
3
  * SPDX-License-Identifier: BSD-3-Clause
4
4
  */
5
+ import { InternalDispatch } from '@clayui/shared';
5
6
  import React from 'react';
6
7
  import Content from './Content';
7
8
  import Item from './Item';
8
9
  import TabPane from './TabPane';
9
10
  export declare type DisplayType = null | 'basic' | 'underline';
10
11
  export interface IProps extends React.HTMLAttributes<HTMLUListElement> {
12
+ /**
13
+ * Flag to indicate the navigation behavior in the tab.
14
+ *
15
+ * - manual - it will just move the focus and tab activation is done just
16
+ * by pressing space or enter.
17
+ * - automatic - moves the focus to the tab and activates the tab.
18
+ */
19
+ activation?: 'manual' | 'automatic';
20
+ /**
21
+ * The current tab active (controlled).
22
+ */
23
+ active?: number;
24
+ /**
25
+ * Initial active tab when rendering component (uncontrolled).
26
+ */
27
+ defaultActive?: number;
11
28
  /**
12
29
  * Determines how tab is displayed.
13
30
  */
@@ -20,6 +37,10 @@ export interface IProps extends React.HTMLAttributes<HTMLUListElement> {
20
37
  * Applies a modern style to the tab.
21
38
  */
22
39
  modern?: boolean;
40
+ /**
41
+ * Callback is called when the active tab changes (controlled).
42
+ */
43
+ onActiveChange?: InternalDispatch<number>;
23
44
  }
24
45
  declare function ClayTabs(props: IProps): JSX.Element & {
25
46
  Content: typeof Content;
package/lib/index.js CHANGED
@@ -1,13 +1,17 @@
1
1
  "use strict";
2
2
 
3
+ function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
4
+
3
5
  Object.defineProperty(exports, "__esModule", {
4
6
  value: true
5
7
  });
6
8
  exports.default = void 0;
7
9
 
10
+ var _shared = require("@clayui/shared");
11
+
8
12
  var _classnames = _interopRequireDefault(require("classnames"));
9
13
 
10
- var _react = _interopRequireDefault(require("react"));
14
+ var _react = _interopRequireWildcard(require("react"));
11
15
 
12
16
  var _Content = _interopRequireDefault(require("./Content"));
13
17
 
@@ -15,26 +19,62 @@ var _Item = _interopRequireDefault(require("./Item"));
15
19
 
16
20
  var _TabPane = _interopRequireDefault(require("./TabPane"));
17
21
 
18
- var _excluded = ["children", "className", "displayType", "justified", "modern"];
22
+ var _excluded = ["activation", "active", "children", "className", "defaultActive", "displayType", "justified", "modern", "onActiveChange"];
23
+
24
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
25
+
26
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
19
27
 
20
28
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
21
29
 
22
30
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
23
31
 
32
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
33
+
34
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
35
+
36
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
37
+
38
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
39
+
40
+ function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
41
+
42
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
43
+
24
44
  function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
25
45
 
26
46
  function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
27
47
 
28
48
  function ClayTabs(_ref) {
29
- var children = _ref.children,
49
+ var _ref$activation = _ref.activation,
50
+ activation = _ref$activation === void 0 ? 'manual' : _ref$activation,
51
+ externalActive = _ref.active,
52
+ children = _ref.children,
30
53
  className = _ref.className,
54
+ _ref$defaultActive = _ref.defaultActive,
55
+ defaultActive = _ref$defaultActive === void 0 ? 0 : _ref$defaultActive,
31
56
  displayType = _ref.displayType,
32
57
  justified = _ref.justified,
33
58
  _ref$modern = _ref.modern,
34
59
  modern = _ref$modern === void 0 ? true : _ref$modern,
60
+ onActiveChange = _ref.onActiveChange,
35
61
  otherProps = _objectWithoutProperties(_ref, _excluded);
36
62
 
37
- return /*#__PURE__*/_react.default.createElement("ul", _extends({
63
+ var tabsRef = (0, _react.useRef)(null);
64
+
65
+ var _useInternalState = (0, _shared.useInternalState)({
66
+ defaultName: 'defaultActive',
67
+ defaultValue: defaultActive,
68
+ handleName: 'onActiveChange',
69
+ name: 'active',
70
+ onChange: onActiveChange,
71
+ value: externalActive
72
+ }),
73
+ _useInternalState2 = _slicedToArray(_useInternalState, 2),
74
+ active = _useInternalState2[0],
75
+ setActive = _useInternalState2[1];
76
+
77
+ return /*#__PURE__*/_react.default.createElement("ul", _extends({}, otherProps, {
38
78
  className: (0, _classnames.default)('nav', {
39
79
  'nav-justified': justified
40
80
  }, !displayType ? {
@@ -44,8 +84,43 @@ function ClayTabs(_ref) {
44
84
  'nav-tabs': displayType === 'basic',
45
85
  'nav-underline': displayType === 'underline'
46
86
  }, className),
87
+ onKeyDown: function onKeyDown(event) {
88
+ if (!tabsRef.current) {
89
+ return;
90
+ }
91
+
92
+ if (event.key === _shared.Keys.Left || event.key === _shared.Keys.Right) {
93
+ var tabs = Array.from(tabsRef.current.querySelectorAll(_shared.FOCUSABLE_ELEMENTS.join(',')));
94
+ var activeElement = document.activeElement;
95
+ var position = tabs.indexOf(activeElement);
96
+ var tab = tabs[event.key === _shared.Keys.Left ? position - 1 : position + 1];
97
+
98
+ if (tab) {
99
+ tab.focus();
100
+
101
+ if (activation === 'automatic') {
102
+ var newActive = Array.from(tabsRef.current.querySelectorAll('a, button')).indexOf(tab);
103
+ setActive(newActive);
104
+ }
105
+ }
106
+ }
107
+ },
108
+ ref: tabsRef,
47
109
  role: "tablist"
48
- }, otherProps), children);
110
+ }), _react.default.Children.map(children, function (child, index) {
111
+ return /*#__PURE__*/_react.default.cloneElement(child, {
112
+ active: child.props.active !== undefined ? child.props.active : active === index,
113
+ onClick: function onClick(event) {
114
+ var onClick = child.props.onClick;
115
+
116
+ if (onClick) {
117
+ onClick(event);
118
+ } else {
119
+ setActive(index);
120
+ }
121
+ }
122
+ });
123
+ }));
49
124
  }
50
125
 
51
126
  ClayTabs.Content = _Content.default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clayui/tabs",
3
- "version": "3.75.2",
3
+ "version": "3.78.0",
4
4
  "description": "ClayTabs component",
5
5
  "license": "BSD-3-Clause",
6
6
  "repository": "https://github.com/liferay/clay",
@@ -26,7 +26,7 @@
26
26
  "react"
27
27
  ],
28
28
  "dependencies": {
29
- "@clayui/shared": "^3.75.2",
29
+ "@clayui/shared": "^3.78.0",
30
30
  "classnames": "^2.2.6"
31
31
  },
32
32
  "peerDependencies": {
@@ -37,5 +37,5 @@
37
37
  "browserslist": [
38
38
  "extends browserslist-config-clay"
39
39
  ],
40
- "gitHead": "2b0e308f458bab6c8e4aca763d34197e7bd69cb1"
40
+ "gitHead": "128788059d168c956b392363054933db34c67cab"
41
41
  }
package/src/Item.tsx CHANGED
@@ -49,10 +49,13 @@ const Item = React.forwardRef<any, IProps>(
49
49
  }: IProps,
50
50
  ref
51
51
  ) => (
52
- <li {...otherProps} className={classNames('nav-item', className)}>
52
+ <li
53
+ {...otherProps}
54
+ className={classNames('nav-item', className)}
55
+ role="none"
56
+ >
53
57
  <LinkOrButton
54
58
  {...innerProps}
55
- aria-disabled={!active}
56
59
  aria-selected={active}
57
60
  buttonDisplayType="unstyled"
58
61
  buttonType="button"
@@ -66,7 +69,7 @@ const Item = React.forwardRef<any, IProps>(
66
69
  onClick={onClick}
67
70
  ref={ref}
68
71
  role="tab"
69
- tabIndex={disabled ? -1 : undefined}
72
+ tabIndex={!active ? -1 : 0}
70
73
  >
71
74
  {children}
72
75
  </LinkOrButton>
package/src/TabPane.tsx CHANGED
@@ -68,6 +68,7 @@ const TabPane = ({
68
68
  className
69
69
  )}
70
70
  role="tabpanel"
71
+ tabIndex={0}
71
72
  >
72
73
  {children}
73
74
  </div>
@@ -17,13 +17,14 @@ exports[`ClayTabs renders with items 1`] = `
17
17
  >
18
18
  <li
19
19
  class="nav-item"
20
+ role="none"
20
21
  >
21
22
  <button
22
- aria-disabled="false"
23
23
  aria-selected="true"
24
24
  class="nav-link active btn btn-unstyled"
25
25
  data-testid="tabItem"
26
26
  role="tab"
27
+ tabindex="0"
27
28
  type="button"
28
29
  >
29
30
  Dummy1
@@ -32,13 +33,14 @@ exports[`ClayTabs renders with items 1`] = `
32
33
  <li
33
34
  class="nav-item"
34
35
  data-testid="tabItem2"
36
+ role="none"
35
37
  >
36
38
  <button
37
- aria-disabled="true"
38
39
  aria-selected="false"
39
40
  class="nav-link btn btn-unstyled"
40
41
  data-testid="tabItem"
41
42
  role="tab"
43
+ tabindex="-1"
42
44
  type="button"
43
45
  >
44
46
  Dummy2
@@ -46,13 +48,14 @@ exports[`ClayTabs renders with items 1`] = `
46
48
  </li>
47
49
  <li
48
50
  class="nav-item"
51
+ role="none"
49
52
  >
50
53
  <button
51
- aria-disabled="true"
52
54
  aria-selected="false"
53
55
  class="nav-link btn btn-unstyled"
54
56
  data-testid="tabItem"
55
57
  role="tab"
58
+ tabindex="-1"
56
59
  type="button"
57
60
  >
58
61
  Dummy3
@@ -66,6 +69,7 @@ exports[`ClayTabs renders with items 1`] = `
66
69
  class="tab-pane active show"
67
70
  data-testid="tabPane1"
68
71
  role="tabpanel"
72
+ tabindex="0"
69
73
  >
70
74
  Tab Content 1
71
75
  </div>
@@ -73,12 +77,14 @@ exports[`ClayTabs renders with items 1`] = `
73
77
  class="tab-pane"
74
78
  data-testid="tabPane2"
75
79
  role="tabpanel"
80
+ tabindex="0"
76
81
  >
77
82
  Tab Content 2
78
83
  </div>
79
84
  <div
80
85
  class="tab-pane"
81
86
  role="tabpanel"
87
+ tabindex="0"
82
88
  >
83
89
  Tab Content 3
84
90
  </div>
@@ -9,32 +9,16 @@ import {cleanup, fireEvent, render} from '@testing-library/react';
9
9
  import React from 'react';
10
10
 
11
11
  const ClayTabsWithItems = () => {
12
- const [activeTabKeyValue, setActiveTabKeyValue] = React.useState<number>(0);
12
+ const [active, setActive] = React.useState<number>(0);
13
13
 
14
14
  return (
15
15
  <>
16
- <ClayTabs>
17
- <ClayTabs.Item
18
- active={activeTabKeyValue == 0}
19
- onClick={() => setActiveTabKeyValue(0)}
20
- >
21
- Dummy1
22
- </ClayTabs.Item>
23
- <ClayTabs.Item
24
- active={activeTabKeyValue == 1}
25
- data-testid="tabItem2"
26
- onClick={() => setActiveTabKeyValue(1)}
27
- >
28
- Dummy2
29
- </ClayTabs.Item>
30
- <ClayTabs.Item
31
- active={activeTabKeyValue == 2}
32
- onClick={() => setActiveTabKeyValue(2)}
33
- >
34
- Dummy3
35
- </ClayTabs.Item>
16
+ <ClayTabs active={active} onActiveChange={setActive}>
17
+ <ClayTabs.Item>Dummy1</ClayTabs.Item>
18
+ <ClayTabs.Item data-testid="tabItem2">Dummy2</ClayTabs.Item>
19
+ <ClayTabs.Item>Dummy3</ClayTabs.Item>
36
20
  </ClayTabs>
37
- <ClayTabs.Content activeIndex={activeTabKeyValue}>
21
+ <ClayTabs.Content activeIndex={active}>
38
22
  <ClayTabs.TabPane data-testid="tabPane1">
39
23
  Tab Content 1
40
24
  </ClayTabs.TabPane>
@@ -96,8 +80,9 @@ describe('ClayTabs', () => {
96
80
  const {getAllByTestId} = render(
97
81
  <>
98
82
  <ClayTabs>
99
- <ClayTabs.Item active href="https://clay.dev/foo" />
100
- One
83
+ <ClayTabs.Item active href="https://clay.dev/foo">
84
+ One
85
+ </ClayTabs.Item>
101
86
  <ClayTabs.Item href="https://clay.dev/bar">
102
87
  Two
103
88
  </ClayTabs.Item>
package/src/index.tsx CHANGED
@@ -3,8 +3,14 @@
3
3
  * SPDX-License-Identifier: BSD-3-Clause
4
4
  */
5
5
 
6
+ import {
7
+ FOCUSABLE_ELEMENTS,
8
+ InternalDispatch,
9
+ Keys,
10
+ useInternalState,
11
+ } from '@clayui/shared';
6
12
  import classNames from 'classnames';
7
- import React from 'react';
13
+ import React, {useRef} from 'react';
8
14
 
9
15
  import Content from './Content';
10
16
  import Item from './Item';
@@ -13,6 +19,25 @@ import TabPane from './TabPane';
13
19
  export type DisplayType = null | 'basic' | 'underline';
14
20
 
15
21
  export interface IProps extends React.HTMLAttributes<HTMLUListElement> {
22
+ /**
23
+ * Flag to indicate the navigation behavior in the tab.
24
+ *
25
+ * - manual - it will just move the focus and tab activation is done just
26
+ * by pressing space or enter.
27
+ * - automatic - moves the focus to the tab and activates the tab.
28
+ */
29
+ activation?: 'manual' | 'automatic';
30
+
31
+ /**
32
+ * The current tab active (controlled).
33
+ */
34
+ active?: number;
35
+
36
+ /**
37
+ * Initial active tab when rendering component (uncontrolled).
38
+ */
39
+ defaultActive?: number;
40
+
16
41
  /**
17
42
  * Determines how tab is displayed.
18
43
  */
@@ -27,6 +52,11 @@ export interface IProps extends React.HTMLAttributes<HTMLUListElement> {
27
52
  * Applies a modern style to the tab.
28
53
  */
29
54
  modern?: boolean;
55
+
56
+ /**
57
+ * Callback is called when the active tab changes (controlled).
58
+ */
59
+ onActiveChange?: InternalDispatch<number>;
30
60
  }
31
61
 
32
62
  function ClayTabs(props: IProps): JSX.Element & {
@@ -37,15 +67,31 @@ function ClayTabs(props: IProps): JSX.Element & {
37
67
  };
38
68
 
39
69
  function ClayTabs({
70
+ activation = 'manual',
71
+ active: externalActive,
40
72
  children,
41
73
  className,
74
+ defaultActive = 0,
42
75
  displayType,
43
76
  justified,
44
77
  modern = true,
78
+ onActiveChange,
45
79
  ...otherProps
46
80
  }: IProps) {
81
+ const tabsRef = useRef<HTMLUListElement>(null);
82
+
83
+ const [active, setActive] = useInternalState({
84
+ defaultName: 'defaultActive',
85
+ defaultValue: defaultActive,
86
+ handleName: 'onActiveChange',
87
+ name: 'active',
88
+ onChange: onActiveChange,
89
+ value: externalActive,
90
+ });
91
+
47
92
  return (
48
93
  <ul
94
+ {...otherProps}
49
95
  className={classNames(
50
96
  'nav',
51
97
  {'nav-justified': justified},
@@ -61,10 +107,63 @@ function ClayTabs({
61
107
 
62
108
  className
63
109
  )}
110
+ onKeyDown={(event) => {
111
+ if (!tabsRef.current) {
112
+ return;
113
+ }
114
+
115
+ if (event.key === Keys.Left || event.key === Keys.Right) {
116
+ const tabs = Array.from<HTMLElement>(
117
+ tabsRef.current.querySelectorAll(
118
+ FOCUSABLE_ELEMENTS.join(',')
119
+ )
120
+ );
121
+ const activeElement = document.activeElement as HTMLElement;
122
+
123
+ const position = tabs.indexOf(activeElement);
124
+
125
+ const tab =
126
+ tabs[
127
+ event.key === Keys.Left
128
+ ? position - 1
129
+ : position + 1
130
+ ];
131
+
132
+ if (tab) {
133
+ tab.focus();
134
+
135
+ if (activation === 'automatic') {
136
+ const newActive = Array.from(
137
+ tabsRef.current.querySelectorAll('a, button')
138
+ ).indexOf(tab);
139
+
140
+ setActive(newActive);
141
+ }
142
+ }
143
+ }
144
+ }}
145
+ ref={tabsRef}
64
146
  role="tablist"
65
- {...otherProps}
66
147
  >
67
- {children}
148
+ {React.Children.map(children, (child, index) =>
149
+ React.cloneElement(child as React.ReactElement, {
150
+ active:
151
+ (child as React.ReactElement).props.active !== undefined
152
+ ? (child as React.ReactElement).props.active
153
+ : active === index,
154
+ onClick: (
155
+ event: React.MouseEvent<HTMLButtonElement, MouseEvent>
156
+ ) => {
157
+ const {onClick} = (child as React.ReactElement).props;
158
+
159
+ if (onClick) {
160
+ onClick(event);
161
+ } else {
162
+ setActive(index);
163
+ }
164
+ },
165
+ })
166
+ )}
68
167
  </ul>
69
168
  );
70
169
  }