@clayui/tabs 3.86.1 → 3.88.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/Content.d.ts CHANGED
@@ -26,5 +26,5 @@ export interface IProps extends React.HTMLAttributes<HTMLDivElement> {
26
26
  */
27
27
  tabsId?: string;
28
28
  }
29
- declare const Content: ({ active, activeIndex, children, className, fade, tabsId, ...otherProps }: IProps) => JSX.Element;
29
+ declare const Content: React.ForwardRefExoticComponent<IProps & React.RefAttributes<HTMLDivElement>>;
30
30
  export default Content;
package/lib/Content.js CHANGED
@@ -25,7 +25,7 @@ function _objectWithoutProperties(source, excluded) { if (source == null) return
25
25
 
26
26
  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
27
 
28
- var Content = function Content(_ref) {
28
+ var Content = /*#__PURE__*/_react.default.forwardRef(function Content(_ref, ref) {
29
29
  var active = _ref.active,
30
30
  _ref$activeIndex = _ref.activeIndex,
31
31
  activeIndex = _ref$activeIndex === void 0 ? 0 : _ref$activeIndex,
@@ -38,7 +38,9 @@ var Content = function Content(_ref) {
38
38
 
39
39
  return /*#__PURE__*/_react.default.createElement("div", _extends({
40
40
  className: (0, _classnames.default)('tab-content', className)
41
- }, otherProps), _react.default.Children.map(children, function (child, index) {
41
+ }, otherProps, {
42
+ ref: ref
43
+ }), _react.default.Children.map(children, function (child, index) {
42
44
  if (! /*#__PURE__*/_react.default.isValidElement(child)) {
43
45
  return child;
44
46
  }
@@ -51,7 +53,7 @@ var Content = function Content(_ref) {
51
53
  key: index
52
54
  }));
53
55
  }));
54
- };
56
+ });
55
57
 
56
58
  var _default = Content;
57
59
  exports.default = _default;
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,13 @@ 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;
46
- export declare namespace List {
47
- var displayName: string;
48
- }
49
+ export declare const List: React.ForwardRefExoticComponent<IProps & React.RefAttributes<HTMLUListElement>>;
package/lib/List.js CHANGED
@@ -5,7 +5,7 @@ function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" =
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.List = List;
8
+ exports.List = void 0;
9
9
 
10
10
  var _shared = require("@clayui/shared");
11
11
 
@@ -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
 
@@ -33,7 +33,7 @@ function _objectWithoutProperties(source, excluded) { if (source == null) return
33
33
 
34
34
  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; }
35
35
 
36
- function List(_ref) {
36
+ var List = /*#__PURE__*/_react.default.forwardRef(function List(_ref, ref) {
37
37
  var activation = _ref.activation,
38
38
  active = _ref.active,
39
39
  children = _ref.children,
@@ -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,30 @@ function List(_ref) {
54
56
  }),
55
57
  navigationProps = _useNavigation.navigationProps;
56
58
 
59
+ (0, _react.useImperativeHandle)(ref, function () {
60
+ return tabsRef.current;
61
+ }, [tabsRef]);
62
+ (0, _react.useEffect)(function () {
63
+ // Internal API to maintain compatibility with the old Tabs pattern and to
64
+ // only update the initial state when the component is in
65
+ // uncontrolled mode.
66
+ if (!shouldUseActive) {
67
+ return;
68
+ }
69
+
70
+ var childrenArray = _react.default.Children.toArray(children); // The `active` API in the new pattern has uncontrolled behavior, working
71
+ // just like defaultActive as in the prop declared in the root component.
72
+
73
+
74
+ for (var index = 0; index < childrenArray.length; index++) {
75
+ var child = childrenArray[index];
76
+
77
+ if ( /*#__PURE__*/_react.default.isValidElement(child) && child.props.active) {
78
+ onActiveChange(index);
79
+ break;
80
+ }
81
+ }
82
+ }, []);
57
83
  return /*#__PURE__*/_react.default.createElement("ul", _extends({}, otherProps, navigationProps, {
58
84
  className: (0, _classnames.default)('nav', {
59
85
  'nav-justified': justified
@@ -74,7 +100,7 @@ function List(_ref) {
74
100
  }
75
101
 
76
102
  return /*#__PURE__*/_react.default.cloneElement(child, {
77
- active: child.props.active !== undefined ? child.props.active : active === index,
103
+ active: !shouldUseActive && child.props.active !== undefined ? child.props.active : active === index,
78
104
  innerProps: _objectSpread({
79
105
  'aria-controls': tabsId && "".concat(tabsId, "-tabpanel-").concat(index),
80
106
  id: tabsId && "".concat(tabsId, "-tab-").concat(index)
@@ -90,6 +116,7 @@ function List(_ref) {
90
116
  }
91
117
  });
92
118
  }));
93
- }
119
+ });
94
120
 
121
+ exports.List = List;
95
122
  List.displayName = 'ClayTabsList';
package/lib/TabPane.d.ts CHANGED
@@ -13,5 +13,5 @@ export interface ITabPaneProps extends React.HTMLAttributes<HTMLDivElement> {
13
13
  */
14
14
  fade?: boolean;
15
15
  }
16
- declare const TabPane: ({ active, children, className, fade, tabIndex, ...otherProps }: ITabPaneProps) => JSX.Element;
16
+ declare const TabPane: React.ForwardRefExoticComponent<ITabPaneProps & React.RefAttributes<HTMLDivElement>>;
17
17
  export default TabPane;
package/lib/TabPane.js CHANGED
@@ -38,7 +38,7 @@ var delay = function delay(fn) {
38
38
  }, val);
39
39
  };
40
40
 
41
- var TabPane = function TabPane(_ref) {
41
+ var TabPane = /*#__PURE__*/_react.default.forwardRef(function TabPane(_ref, ref) {
42
42
  var _ref$active = _ref.active,
43
43
  active = _ref$active === void 0 ? false : _ref$active,
44
44
  children = _ref.children,
@@ -87,10 +87,11 @@ var TabPane = function TabPane(_ref) {
87
87
  fade: fade,
88
88
  show: internalShow
89
89
  }, className),
90
+ ref: ref,
90
91
  role: "tabpanel",
91
92
  tabIndex: tabIndex
92
93
  }), children);
93
- };
94
+ });
94
95
 
95
96
  var _default = TabPane;
96
97
  exports.default = _default;
package/lib/index.d.ts CHANGED
@@ -57,11 +57,11 @@ declare function ClayTabs(props: IProps): JSX.Element & {
57
57
  TabPanel: typeof TabPane;
58
58
  };
59
59
  declare namespace ClayTabs {
60
- var Content: ({ active, activeIndex, children, className, fade, tabsId, ...otherProps }: import("./Content").IProps) => JSX.Element;
61
- var Panels: ({ active, activeIndex, children, className, fade, tabsId, ...otherProps }: import("./Content").IProps) => JSX.Element;
60
+ var Content: React.ForwardRefExoticComponent<import("./Content").IProps & React.RefAttributes<HTMLDivElement>>;
61
+ var Panels: React.ForwardRefExoticComponent<import("./Content").IProps & React.RefAttributes<HTMLDivElement>>;
62
62
  var Item: React.ForwardRefExoticComponent<import("./Item").IProps & React.RefAttributes<any>>;
63
- var List: typeof import("./List").List;
64
- var TabPane: ({ active, children, className, fade, tabIndex, ...otherProps }: import("./TabPane").ITabPaneProps) => JSX.Element;
65
- var TabPanel: ({ active, children, className, fade, tabIndex, ...otherProps }: import("./TabPane").ITabPaneProps) => JSX.Element;
63
+ var List: React.ForwardRefExoticComponent<import("./List").IProps & React.RefAttributes<HTMLUListElement>>;
64
+ var TabPane: React.ForwardRefExoticComponent<import("./TabPane").ITabPaneProps & React.RefAttributes<HTMLDivElement>>;
65
+ var TabPanel: React.ForwardRefExoticComponent<import("./TabPane").ITabPaneProps & React.RefAttributes<HTMLDivElement>>;
66
66
  }
67
67
  export default ClayTabs;
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.88.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.88.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": "cf84d2b2f04d7576be26ce3917e40ec9da52bef8"
41
41
  }
package/src/Content.tsx CHANGED
@@ -34,17 +34,24 @@ export interface IProps extends React.HTMLAttributes<HTMLDivElement> {
34
34
  tabsId?: string;
35
35
  }
36
36
 
37
- const Content = ({
38
- active,
39
- activeIndex = 0,
40
- children,
41
- className,
42
- fade = false,
43
- tabsId,
44
- ...otherProps
45
- }: IProps) => {
37
+ const Content = React.forwardRef<HTMLDivElement, IProps>(function Content(
38
+ {
39
+ active,
40
+ activeIndex = 0,
41
+ children,
42
+ className,
43
+ fade = false,
44
+ tabsId,
45
+ ...otherProps
46
+ },
47
+ ref
48
+ ) {
46
49
  return (
47
- <div className={classNames('tab-content', className)} {...otherProps}>
50
+ <div
51
+ className={classNames('tab-content', className)}
52
+ {...otherProps}
53
+ ref={ref}
54
+ >
48
55
  {React.Children.map(children, (child, index) => {
49
56
  if (!React.isValidElement(child)) {
50
57
  return child;
@@ -66,6 +73,6 @@ const Content = ({
66
73
  })}
67
74
  </div>
68
75
  );
69
- };
76
+ });
70
77
 
71
78
  export default Content;
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, useImperativeHandle, useRef} from 'react';
9
9
 
10
10
  export interface IProps extends React.HTMLAttributes<HTMLUListElement> {
11
11
  /**
@@ -48,24 +48,33 @@ 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
  */
54
59
  tabsId?: string;
55
60
  }
56
61
 
57
- export function List({
58
- activation,
59
- active,
60
- children,
61
- className,
62
- displayType,
63
- justified,
64
- modern,
65
- onActiveChange,
66
- tabsId,
67
- ...otherProps
68
- }: IProps) {
62
+ export const List = React.forwardRef<HTMLUListElement, IProps>(function List(
63
+ {
64
+ activation,
65
+ active,
66
+ children,
67
+ className,
68
+ displayType,
69
+ justified,
70
+ modern,
71
+ onActiveChange,
72
+ shouldUseActive = false,
73
+ tabsId,
74
+ ...otherProps
75
+ },
76
+ ref
77
+ ) {
69
78
  const tabsRef = useRef<HTMLUListElement>(null);
70
79
 
71
80
  const {navigationProps} = useNavigation({
@@ -74,6 +83,30 @@ export function List({
74
83
  orientation: 'horizontal',
75
84
  });
76
85
 
86
+ useImperativeHandle(ref, () => tabsRef.current!, [tabsRef]);
87
+
88
+ useEffect(() => {
89
+ // Internal API to maintain compatibility with the old Tabs pattern and to
90
+ // only update the initial state when the component is in
91
+ // uncontrolled mode.
92
+ if (!shouldUseActive) {
93
+ return;
94
+ }
95
+
96
+ const childrenArray = React.Children.toArray(children);
97
+
98
+ // The `active` API in the new pattern has uncontrolled behavior, working
99
+ // just like defaultActive as in the prop declared in the root component.
100
+ for (let index = 0; index < childrenArray.length; index++) {
101
+ const child = childrenArray[index];
102
+
103
+ if (React.isValidElement(child) && child.props.active) {
104
+ onActiveChange!(index);
105
+ break;
106
+ }
107
+ }
108
+ }, []);
109
+
77
110
  return (
78
111
  <ul
79
112
  {...otherProps}
@@ -103,6 +136,7 @@ export function List({
103
136
 
104
137
  return React.cloneElement(child as React.ReactElement, {
105
138
  active:
139
+ !shouldUseActive &&
106
140
  (child as React.ReactElement).props.active !== undefined
107
141
  ? (child as React.ReactElement).props.active
108
142
  : active === index,
@@ -127,6 +161,6 @@ export function List({
127
161
  })}
128
162
  </ul>
129
163
  );
130
- }
164
+ });
131
165
 
132
166
  List.displayName = 'ClayTabsList';
package/src/TabPane.tsx CHANGED
@@ -23,57 +23,63 @@ const delay = (fn: Function, val: number = 150) =>
23
23
  fn();
24
24
  }, val);
25
25
 
26
- const TabPane = ({
27
- active = false,
28
- children,
29
- className,
30
- fade,
31
- tabIndex = 0,
32
- ...otherProps
33
- }: ITabPaneProps) => {
34
- const [internalActive, setInternalActive] = React.useState(active);
35
- const [internalShow, setInternalShow] = React.useState(active);
26
+ const TabPane = React.forwardRef<HTMLDivElement, ITabPaneProps>(
27
+ function TabPane(
28
+ {
29
+ active = false,
30
+ children,
31
+ className,
32
+ fade,
33
+ tabIndex = 0,
34
+ ...otherProps
35
+ },
36
+ ref
37
+ ) {
38
+ const [internalActive, setInternalActive] = React.useState(active);
39
+ const [internalShow, setInternalShow] = React.useState(active);
36
40
 
37
- React.useEffect(() => {
38
- let delayFn = () => {
39
- setInternalActive(true);
41
+ React.useEffect(() => {
42
+ let delayFn = () => {
43
+ setInternalActive(true);
40
44
 
41
- delay(() => setInternalShow(true), 50);
42
- };
45
+ delay(() => setInternalShow(true), 50);
46
+ };
43
47
 
44
- if (!active) {
45
- setInternalShow(false);
48
+ if (!active) {
49
+ setInternalShow(false);
46
50
 
47
- delayFn = () => setInternalActive(false);
48
- }
51
+ delayFn = () => setInternalActive(false);
52
+ }
49
53
 
50
- const timer = delay(delayFn);
54
+ const timer = delay(delayFn);
51
55
 
52
- return () => {
53
- clearTimeout(timer);
56
+ return () => {
57
+ clearTimeout(timer);
54
58
 
55
- setInternalShow(false);
56
- };
57
- }, [active]);
59
+ setInternalShow(false);
60
+ };
61
+ }, [active]);
58
62
 
59
- return (
60
- <div
61
- {...otherProps}
62
- className={classNames(
63
- 'tab-pane',
64
- {
65
- active: internalActive,
66
- fade,
67
- show: internalShow,
68
- },
69
- className
70
- )}
71
- role="tabpanel"
72
- tabIndex={tabIndex}
73
- >
74
- {children}
75
- </div>
76
- );
77
- };
63
+ return (
64
+ <div
65
+ {...otherProps}
66
+ className={classNames(
67
+ 'tab-pane',
68
+ {
69
+ active: internalActive,
70
+ fade,
71
+ show: internalShow,
72
+ },
73
+ className
74
+ )}
75
+ ref={ref}
76
+ role="tabpanel"
77
+ tabIndex={tabIndex}
78
+ >
79
+ {children}
80
+ </div>
81
+ );
82
+ }
83
+ );
78
84
 
79
85
  export default TabPane;
@@ -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