diff --git a/components/TabbedView/TabbedView.jsx b/components/TabbedView/TabbedView.jsx index f44440944..520d4ce86 100644 --- a/components/TabbedView/TabbedView.jsx +++ b/components/TabbedView/TabbedView.jsx @@ -1,4 +1,4 @@ -import { Component, Children } from 'react'; +import { useState, useEffect, Children } from 'react'; import PropTypes from 'prop-types'; import styled, { css } from 'styled-components'; import { @@ -159,86 +159,88 @@ const TabPanel = styled.div` const RenderIf = ({ conditional, children }) => conditional && children; -class TabbedView extends Component { - static Tab = Tab; - - constructor(props) { - super(props); - - const { children, activeTab } = props; +function TabbedView({ + children, + activeTab = undefined, + skin = 'neutral', + theme = { + components, + baseFontSize: defaultBaseFontSize, + breakpoints: defaultBreakpoints, + spacing: defaultSpacing, + }, + fluid = false, + onTabClick = () => {}, +}) { + const [currentTab, setCurrentTab] = useState(activeTab); + const tabTitles = Children.toArray(children).map((tab) => tab.props.title); + function setDefaultTab() { + setCurrentTab(tabTitles[0]); + } - if (activeTab) { - this.state = { activeTab }; + useEffect(() => { + if (tabTitles.includes(activeTab)) { + setCurrentTab(activeTab); } else { - const [ - { - props: { title }, - }, - ] = Children.toArray(children); - this.state = { activeTab: title }; + setDefaultTab(); } - } + }, [activeTab]); - onTabClick = (tab, onTabClick) => { - this.setState({ activeTab: tab }); + const handleTabClick = (tab) => { + setCurrentTab(tab); onTabClick(tab); }; - sanitize = (str) => + const sanitize = (str) => str .normalize('NFD') .replace(/[\u0300-\u036f]/g, '') .replace(' ', '-') .toLowerCase(); - render() { - const { children, skin, theme, fluid, onTabClick } = this.props; - const { activeTab } = this.state; - - return ( - <> - - {Children.map(children, ({ props: { title, badge, icon } }) => ( - this.onTabClick(title, onTabClick)} - skin={skin} - theme={theme} - id={`${this.sanitize(title)}-tab`} - aria-controls={`${this.sanitize(title)}-panel`} - aria-selected={title === activeTab} - > - {icon && {icon}} - {title && {title}} - {badge && {badge}} - - ))} - - - {Children.map( - children, - ({ props: { title, children: tabContent } }) => ( - - - {tabContent} - - - ), - )} - - ); - } + const quantityOfItems = Children.count(children); + + return ( + <> + + {Children.map(children, ({ props: { title, badge, icon } }) => ( + handleTabClick(title)} + skin={skin} + theme={theme} + id={`${sanitize(title)}-tab`} + aria-controls={`${sanitize(title)}-panel`} + aria-selected={title === currentTab} + > + {icon && {icon}} + {title && {title}} + {badge && {badge}} + + ))} + + + {Children.map(children, ({ props: { title, children: tabContent } }) => ( + + + {tabContent} + + + ))} + + ); } +TabbedView.Tab = Tab; + TabbedView.propTypes = { - /** Used to expand all tabs along all the parent width */ fluid: PropTypes.bool, children: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.node), @@ -257,19 +259,6 @@ TabbedView.propTypes = { onTabClick: PropTypes.func, }; -TabbedView.defaultProps = { - fluid: false, - activeTab: undefined, - skin: 'neutral', - theme: { - components, - baseFontSize: defaultBaseFontSize, - breakpoints: defaultBreakpoints, - spacing: defaultSpacing, - }, - onTabClick: () => {}, -}; - TabbedView.displayName = 'TabbedView'; export default TabbedView; diff --git a/components/TabbedView/TabbedView.unit.test.jsx b/components/TabbedView/TabbedView.unit.test.jsx index c1d4b72d2..6febaeec2 100644 --- a/components/TabbedView/TabbedView.unit.test.jsx +++ b/components/TabbedView/TabbedView.unit.test.jsx @@ -117,6 +117,50 @@ describe(' ', () => { render(Candidates content); expect(screen.getByText('Candidates content')).toBeInTheDocument(); }); + it('should be able to change active tab when activeTab props change', () => { + const { rerender } = render( + + Candidatos content + Empresas content + Educação content + , + ); + expect(screen.queryByText('Candidatos content')).not.toBeInTheDocument(); + expect(screen.getByText('Empresas content')).toBeInTheDocument(); + expect(screen.queryByText('Educação content')).not.toBeInTheDocument(); + rerender( + + Candidatos content + Empresas content + Educação content + , + ); + expect(screen.getByText('Candidatos content')).toBeInTheDocument(); + expect(screen.queryByText('Empresas content')).not.toBeInTheDocument(); + expect(screen.queryByText('Educação content')).not.toBeInTheDocument(); + }); + it('should not be able to change the active tab when active tab props change to a invalid tab', () => { + const { rerender } = render( + + Candidatos content + Empresas content + Educação content + , + ); + expect(screen.queryByText('Candidatos content')).not.toBeInTheDocument(); + expect(screen.getByText('Empresas content')).toBeInTheDocument(); + expect(screen.queryByText('Educação content')).not.toBeInTheDocument(); + rerender( + + Candidatos content + Empresas content + Educação content + , + ); + expect(screen.getByText('Candidatos content')).toBeInTheDocument(); + expect(screen.queryByText('Empresas content')).not.toBeInTheDocument(); + expect(screen.queryByText('Educação content')).not.toBeInTheDocument(); + }); }); describe('events', () => {