From 88f4c78cde234f63eb15349c493ddc1eb0b3e51e Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Thu, 29 Jul 2021 17:43:54 +0200 Subject: [PATCH] chore(Tab): use React.forwardRef() (#4265) --- src/modules/Tab/Tab.js | 77 +++++++++++++------------- src/modules/Tab/TabPane.js | 8 ++- test/specs/modules/Tab/Tab-test.js | 2 + test/specs/modules/Tab/TabPane-test.js | 1 + 4 files changed, 48 insertions(+), 40 deletions(-) diff --git a/src/modules/Tab/Tab.js b/src/modules/Tab/Tab.js index 9e2a57973f..3004f4ceda 100644 --- a/src/modules/Tab/Tab.js +++ b/src/modules/Tab/Tab.js @@ -3,10 +3,10 @@ import PropTypes from 'prop-types' import React from 'react' import { - ModernAutoControlledComponent as Component, customPropTypes, getElementType, getUnhandledProps, + useAutoControlledValue, } from '../../lib' import Grid from '../../collections/Grid/Grid' import GridColumn from '../../collections/Grid/GridColumn' @@ -18,21 +18,25 @@ import TabPane from './TabPane' * @see Menu * @see Segment */ -class Tab extends Component { - getInitialAutoControlledState() { - return { activeIndex: 0 } +const Tab = React.forwardRef(function (props, ref) { + const { grid, menu, panes, menuPosition, renderActiveOnly } = props + + const [activeIndex, setActiveIndex] = useAutoControlledValue({ + state: props.activeIndex, + defaultState: props.defaultActiveIndex, + initialState: 0, + }) + + const handleItemClick = (e, { index }) => { + _.invoke(props, 'onTabChange', e, { ...props, activeIndex: index }) + setActiveIndex(index) } - handleItemClick = (e, { index }) => { - _.invoke(this.props, 'onTabChange', e, { ...this.props, activeIndex: index }) - this.setState({ activeIndex: index }) - } - - renderItems() { - const { panes, renderActiveOnly } = this.props - const { activeIndex } = this.state + const renderItems = () => { + if (renderActiveOnly) { + return _.invoke(_.get(panes, `[${activeIndex}]`), 'render', props) + } - if (renderActiveOnly) return _.invoke(_.get(panes, `[${activeIndex}]`), 'render', this.props) return _.map(panes, ({ pane }, index) => TabPane.create(pane, { overrideProps: { @@ -42,10 +46,7 @@ class Tab extends Component { ) } - renderMenu() { - const { menu, panes, menuPosition } = this.props - const { activeIndex } = this.state - + const renderMenu = () => { if (menu.tabular === true && menuPosition === 'right') { menu.tabular = 'right' } @@ -54,55 +55,57 @@ class Tab extends Component { autoGenerateKey: false, overrideProps: { items: _.map(panes, 'menuItem'), - onItemClick: this.handleItemClick, + onItemClick: handleItemClick, activeIndex, }, }) } - renderVertical(menu) { - const { grid, menuPosition } = this.props + const renderVertical = (menuElement) => { const { paneWidth, tabWidth, ...gridProps } = grid - const position = menuPosition || (menu.props.tabular === 'right' && 'right') || 'left' + const position = menuPosition || (menuElement.props.tabular === 'right' && 'right') || 'left' return ( {position === 'left' && - GridColumn.create({ width: tabWidth, children: menu }, { autoGenerateKey: false })} + GridColumn.create({ width: tabWidth, children: menuElement }, { autoGenerateKey: false })} {GridColumn.create( { width: paneWidth, - children: this.renderItems(), + children: renderItems(), stretched: true, }, { autoGenerateKey: false }, )} {position === 'right' && - GridColumn.create({ width: tabWidth, children: menu }, { autoGenerateKey: false })} + GridColumn.create({ width: tabWidth, children: menuElement }, { autoGenerateKey: false })} ) } - render() { - const menu = this.renderMenu() - const rest = getUnhandledProps(Tab, this.props) - const ElementType = getElementType(Tab, this.props) - - if (menu.props.vertical) { - return {this.renderVertical(menu)} - } + const menuElement = renderMenu() + const rest = getUnhandledProps(Tab, props) + const ElementType = getElementType(Tab, props) + if (menuElement.props.vertical) { return ( - - {menu.props.attached !== 'bottom' && menu} - {this.renderItems()} - {menu.props.attached === 'bottom' && menu} + + {renderVertical(menuElement)} ) } -} + return ( + + {menuElement.props.attached !== 'bottom' && menuElement} + {renderItems()} + {menuElement.props.attached === 'bottom' && menuElement} + + ) +}) + +Tab.displayName = 'Tab' Tab.propTypes = { /** An element type to render as (string or function). */ as: PropTypes.elementType, diff --git a/src/modules/Tab/TabPane.js b/src/modules/Tab/TabPane.js index 0ecd3d2e73..4551dfb497 100644 --- a/src/modules/Tab/TabPane.js +++ b/src/modules/Tab/TabPane.js @@ -15,7 +15,7 @@ import Segment from '../../elements/Segment/Segment' /** * A tab pane holds the content of a tab. */ -function TabPane(props) { +const TabPane = React.forwardRef(function (props, ref) { const { active, children, className, content, loading } = props const classes = cx(useKeyOnly(active, 'active'), useKeyOnly(loading, 'loading'), 'tab', className) @@ -23,22 +23,24 @@ function TabPane(props) { const ElementType = getElementType(TabPane, props) const calculatedDefaultProps = {} + if (ElementType === Segment) { calculatedDefaultProps.attached = 'bottom' } return ( - + {childrenUtils.isNil(children) ? content : children} ) -} +}) TabPane.defaultProps = { as: Segment, active: true, } +TabPane.displayName = 'TabPane' TabPane.propTypes = { /** An element type to render as (string or function). */ as: PropTypes.elementType, diff --git a/test/specs/modules/Tab/Tab-test.js b/test/specs/modules/Tab/Tab-test.js index 5350952638..14bfcf1003 100644 --- a/test/specs/modules/Tab/Tab-test.js +++ b/test/specs/modules/Tab/Tab-test.js @@ -7,6 +7,8 @@ import { sandbox } from 'test/utils' describe('Tab', () => { common.isConformant(Tab) + common.forwardsRef(Tab) + common.forwardsRef(Tab, { requiredProps: { menu: { vertical: true } } }) common.hasSubcomponents(Tab, [TabPane]) const panes = [ diff --git a/test/specs/modules/Tab/TabPane-test.js b/test/specs/modules/Tab/TabPane-test.js index 8bdb642cc6..5e69a69f68 100644 --- a/test/specs/modules/Tab/TabPane-test.js +++ b/test/specs/modules/Tab/TabPane-test.js @@ -5,6 +5,7 @@ import * as common from 'test/specs/commonTests' describe('TabPane', () => { common.isConformant(TabPane) + common.forwardsRef(TabPane) common.implementsCreateMethod(TabPane)