From b4e2987212e1656f6b06fea5e8833240b2a6116b Mon Sep 17 00:00:00 2001 From: fermarinsanchez Date: Wed, 18 Jun 2025 10:15:43 +0200 Subject: [PATCH 1/2] fix: extract state and refactor on tabindex and storybook --- src/tabGroup/TabGroup.tsx | 42 ++- stories/TabGroup/ClassBased.stories.js | 341 ++++++++++++++----------- stories/TabGroup/Documentation.mdx | 10 +- stories/TabGroup/UnStyled.stories.js | 32 ++- stories/liveEdit/TabGroupLive.tsx | 13 +- 5 files changed, 242 insertions(+), 196 deletions(-) diff --git a/src/tabGroup/TabGroup.tsx b/src/tabGroup/TabGroup.tsx index de927132..99c5d972 100644 --- a/src/tabGroup/TabGroup.tsx +++ b/src/tabGroup/TabGroup.tsx @@ -5,7 +5,6 @@ import React, { useEffect, useImperativeHandle, useRef, - useState, } from 'react'; import { classNames, Roles, useHydrated } from '../common'; @@ -72,7 +71,7 @@ type TabContextProps = { /** * Tab next tab */ - nextTab?: string + nextTab?: string; /** * Tab Context update selected tab */ @@ -111,29 +110,18 @@ export const TabGroup = forwardRef( const initialMount = useRef(true); - const defaultActiveTabKey = activeKey || children[0].props.eventKey; - const defaultPreviousTabKey = children[children.findIndex((child: JSX.Element) => child.props.eventKey === defaultActiveTabKey) - 1]?.props.eventKey; - const defaultNextTabKey = children[children.findIndex((child: JSX.Element) => child.props.eventKey === defaultActiveTabKey) + 1]?.props.eventKey; - const [activeTab, setActiveTab] = useState(defaultActiveTabKey); - const [previousTab, setPreviousTab] = useState(defaultPreviousTabKey); - const [nextTab, setNextTab] = useState(defaultNextTabKey); + const currentActiveKey = activeKey || children[0].props.eventKey; - const onClickHandler: (id: string) => void = (id: string) => - updateActiveTab(id); + const onClickHandler: (id: string) => void = (id: string) => { + onSelect && onSelect(id); + }; const updateActiveTab: (id: string) => boolean = (id: string) => { - const index = children.findIndex((child: JSX.Element) => child.props.eventKey === id); - if (index < 0) { - return false; - } - - if (!children[index].props.disabled) { - setActiveTab(id); + if (children.some((child: JSX.Element) => child.props.eventKey === id)) { + onSelect && onSelect(id); + return true; } - - setPreviousTab(children[index - 1]?.props.eventKey); - setNextTab(children[index + 1]?.props.eventKey); - return true; + return false; }; useImperativeHandle(ref, () => ({ @@ -141,12 +129,12 @@ export const TabGroup = forwardRef( })); useEffect(() => { - if (!initialMount.current) onSelect && onSelect(activeTab); + if (!initialMount.current) onSelect && onSelect(currentActiveKey); else initialMount.current = false; - }, [activeTab]); + }, [currentActiveKey]); const activeTabElement = children.find( - (child: JSX.Element) => activeTab === child.props.eventKey + (child: JSX.Element) => currentActiveKey === child.props.eventKey ); const hydrated = useHydrated(); @@ -162,7 +150,10 @@ export const TabGroup = forwardRef( aria-label={ariaLabelTabList} > {children.map((child: JSX.Element, index: number) => { const classes: string = classNames([ @@ -192,7 +183,6 @@ export const TabGroup = forwardRef( key={index} role={Roles.tabpanel} className={contentClassName} - tabIndex={0} aria-labelledby={tabPanel.props.eventKey} > {tabPanel.props.children} diff --git a/stories/TabGroup/ClassBased.stories.js b/stories/TabGroup/ClassBased.stories.js index 36388835..2847b8ef 100644 --- a/stories/TabGroup/ClassBased.stories.js +++ b/stories/TabGroup/ClassBased.stories.js @@ -1,6 +1,6 @@ import { TabGroup, Tab } from '../../src/tabGroup'; import { Button } from '../../src/button'; -import React, { useRef } from 'react'; +import React, { useRef, useState } from 'react'; import './style.css'; /** * In this section we're using the TabGroup component providing the **GovUk style** passing the relative `className. @@ -17,114 +17,215 @@ export default { tags: ['autodocs'], }; -export const Basic = { - name: 'Basic', - render: function () { - return ( +const BasicTabs = () => { + const [activeTab, setActiveTab] = useState('tab-1'); + + return ( + + +

Past day

+ This the content for Past day +
+ +

Past week

+ This the content for Past week +
+ +

Past month

+ This the content for Past month +
+ +

Past year

+ This the content for Past year +
+
+ ); +}; + +const DisabledTabs = () => { + const [activeTab, setActiveTab] = useState('tab-1'); + + return ( + + + This the content for tab 1 + + + This the content for tab 2 + + + This the content for tab 3 + + + ); +}; + +const PreselectedTabs = () => { + const [activeTab, setActiveTab] = useState('tab-2'); + + return ( + + + This the content for tab 1 + + + This the content for tab 2 + + + This the content for tab 3 + + + ); +}; + +const EventTabs = () => { + const [activeTab, setActiveTab] = useState('tab-1'); + const handleChange = (eventKey) => { + setActiveTab(eventKey); + alert(`${eventKey} selected`); + }; + + return ( + + + This the content for tab 1 + + + This the content for tab 2 + + + This the content for tab 3 + + + ); +}; + +const ProgrammaticTabs = () => { + const [activeTab, setActiveTab] = useState('tab-pane-1-id'); + const tabRef = useRef(); + + return ( + <> - -

Past day

- This the content for Past day + + This is content for tab 1 + + + This is content for tab 2 - -

Past week

- This the content for Past week + + This is content for tab 3 - -

Past month

- This the content for Past month + + This is content for tab 4 - -

Past year

- This the content for Past year + + This is content for tab 5
- ); - }, +
+