@clayui/tabs 3.86.1 → 3.87.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.d.ts CHANGED
@@ -6,6 +6,10 @@ import React from 'react';
6
6
  export interface IProps extends Omit<React.HTMLAttributes<HTMLLIElement>, 'onClick'> {
7
7
  /**
8
8
  * Flag to indicate if the component is active or not.
9
+ *
10
+ * OBS: The `active` API in the new pattern has uncontrolled behavior,
11
+ * working just like `defaultActive` as in the prop declared in the
12
+ * root component.
9
13
  */
10
14
  active?: boolean;
11
15
  /**
package/lib/List.d.ts CHANGED
@@ -37,12 +37,16 @@ export interface IProps extends React.HTMLAttributes<HTMLUListElement> {
37
37
  * @ignore
38
38
  */
39
39
  onActiveChange?: InternalDispatch<number>;
40
+ /**
41
+ * @ignore
42
+ */
43
+ shouldUseActive?: boolean;
40
44
  /**
41
45
  * @ignore
42
46
  */
43
47
  tabsId?: string;
44
48
  }
45
- export declare function List({ activation, active, children, className, displayType, justified, modern, onActiveChange, tabsId, ...otherProps }: IProps): JSX.Element;
49
+ export declare function List({ activation, active, children, className, displayType, justified, modern, onActiveChange, shouldUseActive, tabsId, ...otherProps }: IProps): JSX.Element;
46
50
  export declare namespace List {
47
51
  var displayName: string;
48
52
  }
package/lib/List.js CHANGED
@@ -13,7 +13,7 @@ var _classnames = _interopRequireDefault(require("classnames"));
13
13
 
14
14
  var _react = _interopRequireWildcard(require("react"));
15
15
 
16
- var _excluded = ["activation", "active", "children", "className", "displayType", "justified", "modern", "onActiveChange", "tabsId"];
16
+ var _excluded = ["activation", "active", "children", "className", "displayType", "justified", "modern", "onActiveChange", "shouldUseActive", "tabsId"];
17
17
 
18
18
  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); }
19
19
 
@@ -42,6 +42,8 @@ function List(_ref) {
42
42
  justified = _ref.justified,
43
43
  modern = _ref.modern,
44
44
  onActiveChange = _ref.onActiveChange,
45
+ _ref$shouldUseActive = _ref.shouldUseActive,
46
+ shouldUseActive = _ref$shouldUseActive === void 0 ? false : _ref$shouldUseActive,
45
47
  tabsId = _ref.tabsId,
46
48
  otherProps = _objectWithoutProperties(_ref, _excluded);
47
49
 
@@ -54,6 +56,27 @@ function List(_ref) {
54
56
  }),
55
57
  navigationProps = _useNavigation.navigationProps;
56
58
 
59
+ (0, _react.useEffect)(function () {
60
+ // Internal API to maintain compatibility with the old Tabs pattern and to
61
+ // only update the initial state when the component is in
62
+ // uncontrolled mode.
63
+ if (!shouldUseActive) {
64
+ return;
65
+ }
66
+
67
+ var childrenArray = _react.default.Children.toArray(children); // The `active` API in the new pattern has uncontrolled behavior, working
68
+ // just like defaultActive as in the prop declared in the root component.
69
+
70
+
71
+ for (var index = 0; index < childrenArray.length; index++) {
72
+ var child = childrenArray[index];
73
+
74
+ if ( /*#__PURE__*/_react.default.isValidElement(child) && child.props.active) {
75
+ onActiveChange(index);
76
+ break;
77
+ }
78
+ }
79
+ }, []);
57
80
  return /*#__PURE__*/_react.default.createElement("ul", _extends({}, otherProps, navigationProps, {
58
81
  className: (0, _classnames.default)('nav', {
59
82
  'nav-justified': justified
@@ -74,7 +97,7 @@ function List(_ref) {
74
97
  }
75
98
 
76
99
  return /*#__PURE__*/_react.default.cloneElement(child, {
77
- active: child.props.active !== undefined ? child.props.active : active === index,
100
+ active: !shouldUseActive && child.props.active !== undefined ? child.props.active : active === index,
78
101
  innerProps: _objectSpread({
79
102
  'aria-controls': tabsId && "".concat(tabsId, "-tabpanel-").concat(index),
80
103
  id: tabsId && "".concat(tabsId, "-tab-").concat(index)
package/lib/index.js CHANGED
@@ -66,9 +66,10 @@ function ClayTabs(_ref) {
66
66
  onChange: onActiveChange,
67
67
  value: externalActive
68
68
  }),
69
- _useInternalState2 = _slicedToArray(_useInternalState, 2),
69
+ _useInternalState2 = _slicedToArray(_useInternalState, 3),
70
70
  active = _useInternalState2[0],
71
- setActive = _useInternalState2[1];
71
+ setActive = _useInternalState2[1],
72
+ isUncontrolled = _useInternalState2[2];
72
73
 
73
74
  var _React$Children$toArr = _react.default.Children.toArray(children),
74
75
  _React$Children$toArr2 = _slicedToArray(_React$Children$toArr, 2),
@@ -85,6 +86,7 @@ function ClayTabs(_ref) {
85
86
  justified: justified,
86
87
  modern: modern,
87
88
  onActiveChange: setActive,
89
+ shouldUseActive: isUncontrolled,
88
90
  tabsId: tabsId
89
91
  }), /*#__PURE__*/_react.default.isValidElement(right) && /*#__PURE__*/_react.default.cloneElement(right, {
90
92
  active: active,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clayui/tabs",
3
- "version": "3.86.1",
3
+ "version": "3.87.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.86.1",
29
+ "@clayui/shared": "^3.87.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": "9437a1f6f7c2845824e9b63b8251e6f7f366323f"
40
+ "gitHead": "f071126e11f2c0ddeab525b5c14eb83f984f6f37"
41
41
  }
package/src/Item.tsx CHANGED
@@ -11,6 +11,10 @@ export interface IProps
11
11
  extends Omit<React.HTMLAttributes<HTMLLIElement>, 'onClick'> {
12
12
  /**
13
13
  * Flag to indicate if the component is active or not.
14
+ *
15
+ * OBS: The `active` API in the new pattern has uncontrolled behavior,
16
+ * working just like `defaultActive` as in the prop declared in the
17
+ * root component.
14
18
  */
15
19
  active?: boolean;
16
20
 
package/src/List.tsx CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  import {InternalDispatch, useNavigation} from '@clayui/shared';
7
7
  import classNames from 'classnames';
8
- import React, {useRef} from 'react';
8
+ import React, {useEffect, useRef} from 'react';
9
9
 
10
10
  export interface IProps extends React.HTMLAttributes<HTMLUListElement> {
11
11
  /**
@@ -48,6 +48,11 @@ export interface IProps extends React.HTMLAttributes<HTMLUListElement> {
48
48
  */
49
49
  onActiveChange?: InternalDispatch<number>;
50
50
 
51
+ /**
52
+ * @ignore
53
+ */
54
+ shouldUseActive?: boolean;
55
+
51
56
  /**
52
57
  * @ignore
53
58
  */
@@ -63,6 +68,7 @@ export function List({
63
68
  justified,
64
69
  modern,
65
70
  onActiveChange,
71
+ shouldUseActive = false,
66
72
  tabsId,
67
73
  ...otherProps
68
74
  }: IProps) {
@@ -74,6 +80,28 @@ export function List({
74
80
  orientation: 'horizontal',
75
81
  });
76
82
 
83
+ useEffect(() => {
84
+ // Internal API to maintain compatibility with the old Tabs pattern and to
85
+ // only update the initial state when the component is in
86
+ // uncontrolled mode.
87
+ if (!shouldUseActive) {
88
+ return;
89
+ }
90
+
91
+ const childrenArray = React.Children.toArray(children);
92
+
93
+ // The `active` API in the new pattern has uncontrolled behavior, working
94
+ // just like defaultActive as in the prop declared in the root component.
95
+ for (let index = 0; index < childrenArray.length; index++) {
96
+ const child = childrenArray[index];
97
+
98
+ if (React.isValidElement(child) && child.props.active) {
99
+ onActiveChange!(index);
100
+ break;
101
+ }
102
+ }
103
+ }, []);
104
+
77
105
  return (
78
106
  <ul
79
107
  {...otherProps}
@@ -103,6 +131,7 @@ export function List({
103
131
 
104
132
  return React.cloneElement(child as React.ReactElement, {
105
133
  active:
134
+ !shouldUseActive &&
106
135
  (child as React.ReactElement).props.active !== undefined
107
136
  ? (child as React.ReactElement).props.active
108
137
  : active === index,
@@ -192,4 +192,25 @@ describe('ClayTabs', () => {
192
192
  expect(getAllByRole('tab').length).toBe(3);
193
193
  expect(getAllByRole('tabpanel').length).toBe(3);
194
194
  });
195
+
196
+ it('renders the tab item active when `active` is set on uncontrolled state', () => {
197
+ const {getByRole} = render(
198
+ <ClayTabs>
199
+ <ClayTabs.List>
200
+ <ClayTabs.Item>Tab 1</ClayTabs.Item>
201
+ <ClayTabs.Item active>Tab 2</ClayTabs.Item>
202
+ <ClayTabs.Item>Tab 3</ClayTabs.Item>
203
+ </ClayTabs.List>
204
+ <ClayTabs.Panels>
205
+ <ClayTabs.TabPanel>Tab Content 1</ClayTabs.TabPanel>
206
+ <ClayTabs.TabPanel>Tab Content 2</ClayTabs.TabPanel>
207
+ <ClayTabs.TabPanel>Tab Content 3</ClayTabs.TabPanel>
208
+ </ClayTabs.Panels>
209
+ </ClayTabs>
210
+ );
211
+
212
+ const activeTab = getByRole('tab', {selected: true});
213
+
214
+ expect(activeTab.innerHTML).toBe('Tab 2');
215
+ });
195
216
  });
package/src/index.tsx CHANGED
@@ -82,7 +82,7 @@ function ClayTabs({
82
82
  onActiveChange,
83
83
  ...otherProps
84
84
  }: IProps) {
85
- const [active, setActive] = useInternalState({
85
+ const [active, setActive, isUncontrolled] = useInternalState({
86
86
  defaultName: 'defaultActive',
87
87
  defaultValue: defaultActive,
88
88
  handleName: 'onActiveChange',
@@ -106,6 +106,7 @@ function ClayTabs({
106
106
  justified,
107
107
  modern,
108
108
  onActiveChange: setActive,
109
+ shouldUseActive: isUncontrolled,
109
110
  tabsId,
110
111
  })}
111
112