Skip to content

Commit e58fb0a

Browse files
committed
Tab Id prop added to the tab component
1 parent 029dd23 commit e58fb0a

File tree

5 files changed

+93
-46
lines changed

5 files changed

+93
-46
lines changed

apps/website/screens/components/tabs/code/TabsCodePage.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -232,16 +232,22 @@ const sections = [
232232
</td>
233233
</tr>
234234
<tr>
235+
<td>label</td>
235236
<td>
236-
<DxcFlex direction="column" gap="0.25rem" alignItems="baseline">
237-
<StatusBadge status="required" />
238-
label
239-
</DxcFlex>
237+
<TableCode>string</TableCode>
240238
</td>
239+
<td>Tab label text.</td>
240+
<td>-</td>
241+
</tr>
242+
<tr>
243+
<td>tabId</td>
241244
<td>
242245
<TableCode>string</TableCode>
243246
</td>
244-
<td>Tab label text.</td>
247+
<td>
248+
Value used to identify the tab internally. When no label is set this prop is required to identify the
249+
tab.
250+
</td>
245251
<td>-</td>
246252
</tr>
247253
<tr>

packages/lib/src/tabs/Tab.tsx

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const DxcTab = forwardRef(
1212
icon,
1313
label,
1414
title,
15+
tabId = label,
1516
disabled = false,
1617
active,
1718
notificationNumber = false,
@@ -25,33 +26,33 @@ const DxcTab = forwardRef(
2526
const {
2627
iconPosition = "top",
2728
tabIndex = 0,
28-
focusedLabel,
29+
focusedTab,
2930
isControlled,
30-
activeLabel,
31+
activeTab,
3132
hasLabelAndIcon = false,
32-
setActiveLabel,
33+
setActiveTab,
3334
setActiveIndicatorWidth,
3435
setActiveIndicatorLeft,
3536
} = useContext(TabsContext) ?? {};
3637

3738
useEffect(() => {
38-
if (focusedLabel === label) {
39+
if (focusedTab === tabId) {
3940
tabRef?.current?.focus();
4041
}
41-
}, [focusedLabel, label]);
42+
}, [focusedTab, tabId]);
4243

4344
useEffect(() => {
44-
if (activeLabel === label) {
45+
if (activeTab === tabId) {
4546
setActiveIndicatorWidth?.(tabRef.current?.offsetWidth ?? 0);
4647
setActiveIndicatorLeft?.(tabRef.current?.offsetLeft ?? 0);
4748
}
48-
}, [activeLabel, label, setActiveIndicatorWidth, setActiveIndicatorLeft]);
49+
}, [activeTab, tabId, setActiveIndicatorWidth, setActiveIndicatorLeft]);
4950

5051
useEffect(() => {
5152
if (active) {
52-
setActiveLabel?.(label);
53+
setActiveTab?.(tabId);
5354
}
54-
}, [active, label, setActiveLabel]);
55+
}, [active, label, setActiveTab]);
5556

5657
const handleOnKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {
5758
switch (event.key) {
@@ -70,14 +71,14 @@ const DxcTab = forwardRef(
7071
<TabContainer
7172
role="tab"
7273
type="button"
73-
tabIndex={activeLabel === label && !disabled ? tabIndex : -1}
74+
tabIndex={activeTab === tabId && !disabled ? tabIndex : -1}
7475
disabled={disabled}
75-
aria-selected={activeLabel === label}
76+
aria-label={label ?? tabId ?? "tab"}
77+
aria-selected={activeTab === tabId}
7678
hasLabelAndIcon={hasLabelAndIcon}
7779
iconPosition={iconPosition}
7880
ref={(anchorRef) => {
7981
tabRef.current = anchorRef;
80-
8182
if (ref) {
8283
if (typeof ref === "function") {
8384
ref(anchorRef);
@@ -89,7 +90,8 @@ const DxcTab = forwardRef(
8990
}}
9091
onClick={() => {
9192
if (!isControlled) {
92-
setActiveLabel?.(label);
93+
setActiveTab?.(tabId ?? "");
94+
console.log(tabId);
9395
}
9496
onClick();
9597
}}
@@ -107,9 +109,11 @@ const DxcTab = forwardRef(
107109
{typeof icon === "string" ? <DxcIcon icon={icon} /> : icon}
108110
</TabIconContainer>
109111
)}
110-
<Label disabled={disabled} activeLabel={activeLabel} label={label}>
111-
{label}
112-
</Label>
112+
{label && (
113+
<Label disabled={disabled} active={activeTab === tabId} label={label}>
114+
{label}
115+
</Label>
116+
)}
113117
</MainLabelContainer>
114118
{notificationNumber && !disabled && (
115119
<BadgeContainer hasLabelAndIcon={hasLabelAndIcon} iconPosition={iconPosition}>
@@ -224,13 +228,13 @@ const MainLabelContainer = styled.div<{
224228
const Label = styled.span<{
225229
disabled: TabProps["disabled"];
226230
label: TabProps["label"];
227-
activeLabel?: string;
231+
active: boolean;
228232
}>`
229233
display: inline;
230234
color: ${(props) =>
231235
props.disabled
232236
? props.theme.disabledFontColor
233-
: props.activeLabel === props.label
237+
: props.active
234238
? props.theme.selectedFontColor
235239
: props.theme.unselectedFontColor};
236240
font-family: ${(props) => props.theme.fontFamily};

packages/lib/src/tabs/Tabs.stories.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,32 @@ const tabsNotification = (iconPosition?: "top" | "left") => (
117117
);
118118

119119
const tabsIcon = (iconPosition?: "top" | "left") => (
120+
<DxcTabs iconPosition={iconPosition}>
121+
<DxcTabs.Tab tabId="Tab 1" icon={iconSVG}>
122+
<></>
123+
</DxcTabs.Tab>
124+
<DxcTabs.Tab tabId="Tab 2" icon={iconSVG}>
125+
<></>
126+
</DxcTabs.Tab>
127+
<DxcTabs.Tab tabId="Tab 3" icon={iconSVG} disabled>
128+
<></>
129+
</DxcTabs.Tab>
130+
<DxcTabs.Tab tabId="Tab 4" icon={iconSVG}>
131+
<></>
132+
</DxcTabs.Tab>
133+
<DxcTabs.Tab tabId="Tab 5" icon="mail">
134+
<></>
135+
</DxcTabs.Tab>
136+
<DxcTabs.Tab tabId="Tab 6" icon="mail">
137+
<></>
138+
</DxcTabs.Tab>
139+
<DxcTabs.Tab tabId="Tab 7" icon="mail">
140+
<></>
141+
</DxcTabs.Tab>
142+
</DxcTabs>
143+
);
144+
145+
const tabsIconAndLabel = (iconPosition?: "top" | "left") => (
120146
<DxcTabs iconPosition={iconPosition}>
121147
<DxcTabs.Tab label="Tab 1" icon={iconSVG}>
122148
<></>
@@ -207,10 +233,12 @@ const Tabs = () => (
207233
<ExampleContainer>
208234
<Title title="With icon position top" theme="light" level={4} />
209235
{tabsIcon()}
236+
{tabsIconAndLabel()}
210237
</ExampleContainer>
211238
<ExampleContainer>
212239
<Title title="With icon position left" theme="light" level={4} />
213240
{tabsIcon("left")}
241+
{tabsIconAndLabel()}
214242
</ExampleContainer>
215243
<ExampleContainer>
216244
<Title title="With icon and notification number" theme="light" level={4} />

packages/lib/src/tabs/Tabs.tsx

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -73,18 +73,23 @@ const DxcTabs = ({
7373
() => childrenArray.some((child) => isValidElement(child) && child.props.icon && child.props.label),
7474
[childrenArray]
7575
);
76-
77-
const [activeTabLabel, setActiveTabLabel] = useState(() => {
76+
const [activeTab, setActiveTab] = useState(() => {
7877
const hasActiveChild = childrenArray.some(
7978
(child) => isValidElement(child) && (child.props.active || child.props.defaultActive) && !child.props.disabled
8079
);
80+
console.log(`hasActiveChild: ${hasActiveChild}`);
8181
const initialActiveTab = hasActiveChild
8282
? childrenArray.find(
8383
(child) => isValidElement(child) && (child.props.active || child.props.defaultActive) && !child.props.disabled
8484
)
8585
: childrenArray.find((child) => isValidElement(child) && !child.props.disabled);
86+
console.log(initialActiveTab);
87+
88+
console.log(
89+
isValidElement(initialActiveTab) ? (initialActiveTab.props.label ?? initialActiveTab.props.tabId) : "not valid"
90+
);
8691

87-
return isValidElement(initialActiveTab) ? initialActiveTab.props.label : "";
92+
return isValidElement(initialActiveTab) ? (initialActiveTab.props.label ?? initialActiveTab.props.tabId) : "";
8893
});
8994
const [innerFocusIndex, setInnerFocusIndex] = useState<number | null>(null);
9095
const [activeIndicatorWidth, setActiveIndicatorWidth] = useState(0);
@@ -113,15 +118,15 @@ const DxcTabs = ({
113118
return {
114119
iconPosition,
115120
tabIndex,
116-
focusedLabel: isValidElement(focusedChild) ? focusedChild.props.label : "",
121+
focusedTab: isValidElement(focusedChild) ? (focusedChild.props.label ?? focusedChild.props.tabId) : "",
117122
isControlled: childrenArray.some((child) => isValidElement(child) && typeof child.props.active !== "undefined"),
118-
activeLabel: activeTabLabel,
123+
activeTab: activeTab,
119124
hasLabelAndIcon,
120-
setActiveLabel: setActiveTabLabel,
125+
setActiveTab: setActiveTab,
121126
setActiveIndicatorWidth,
122127
setActiveIndicatorLeft,
123128
};
124-
}, [iconPosition, tabIndex, innerFocusIndex, activeTabLabel, childrenArray, hasLabelAndIcon]);
129+
}, [iconPosition, tabIndex, innerFocusIndex, activeTab, childrenArray, hasLabelAndIcon]);
125130

126131
const scrollLeft = () => {
127132
const scrollWidth = (refTabList?.current?.offsetHeight ?? 0) * 0.75;
@@ -157,21 +162,23 @@ const DxcTabs = ({
157162
};
158163

159164
const handleOnKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
160-
const activeTab = childrenArray.findIndex((child: ReactElement) => child.props.label === activeTabLabel);
165+
const active = childrenArray.findIndex(
166+
(child: ReactElement) => child.props.label ?? child.props.tabId === activeTab
167+
);
161168
switch (event.key) {
162169
case "Left":
163170
case "ArrowLeft":
164171
event.preventDefault();
165-
setInnerFocusIndex(getPreviousTabIndex(childrenArray, innerFocusIndex === null ? activeTab : innerFocusIndex));
172+
setInnerFocusIndex(getPreviousTabIndex(childrenArray, innerFocusIndex === null ? active : innerFocusIndex));
166173
break;
167174
case "Right":
168175
case "ArrowRight":
169176
event.preventDefault();
170-
setInnerFocusIndex(getNextTabIndex(childrenArray, innerFocusIndex === null ? activeTab : innerFocusIndex));
177+
setInnerFocusIndex(getNextTabIndex(childrenArray, innerFocusIndex === null ? active : innerFocusIndex));
171178
break;
172179
case "Tab":
173-
if (activeTab !== innerFocusIndex) {
174-
setInnerFocusIndex(activeTab);
180+
if (active !== innerFocusIndex) {
181+
setInnerFocusIndex(active);
175182
}
176183
break;
177184
default:
@@ -204,7 +211,10 @@ const DxcTabs = ({
204211
tabWidth={activeIndicatorWidth}
205212
tabLeft={activeIndicatorLeft}
206213
aria-disabled={childrenArray.some(
207-
(child) => isValidElement(child) && activeTabLabel === child.props.label && child.props.disabled
214+
(child) =>
215+
isValidElement(child) &&
216+
activeTab === (child.props.label ?? child.props.tabId) &&
217+
child.props.disabled
208218
)}
209219
/>
210220
</TabsContentScroll>
@@ -223,7 +233,7 @@ const DxcTabs = ({
223233
</TabsContainer>
224234
</ThemeProvider>
225235
{Children.map(children, (child) => {
226-
if (isValidElement(child) && child.props.label === activeTabLabel) {
236+
if (isValidElement(child) && (child.props.label ?? child.props.tabId) === activeTab) {
227237
return child.props.children;
228238
}
229239
return null;

packages/lib/src/tabs/types.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,18 @@ type TabCommonProps = {
1818
};
1919

2020
export type TabsContextProps = {
21+
activeTab?: string;
22+
focusedTab?: string;
2123
iconPosition: "top" | "left";
2224
tabIndex: number;
23-
focusedLabel: string;
2425
isControlled: boolean;
25-
activeLabel: string;
2626
hasLabelAndIcon: boolean;
27-
setActiveLabel: (_tab: string) => void;
27+
setActiveTab: (_tab: string) => void;
2828
setActiveIndicatorWidth: (_width: number) => void;
2929
setActiveIndicatorLeft: (_left: number) => void;
3030
};
3131

32-
export type TabLabelProps = TabCommonProps & {
32+
export type TabLabelProps = {
3333
/**
3434
* Tab label.
3535
*/
@@ -40,7 +40,7 @@ export type TabLabelProps = TabCommonProps & {
4040
icon?: string | SVG;
4141
};
4242

43-
export type TabIconProps = TabCommonProps & {
43+
export type TabIconProps = {
4444
/**
4545
* Tab label.
4646
*/
@@ -52,7 +52,7 @@ export type TabIconProps = TabCommonProps & {
5252
};
5353

5454
export type TabPropsLegacy = {
55-
tab: TabLabelProps | TabIconProps;
55+
tab: TabCommonProps & (TabLabelProps | TabIconProps);
5656
active: boolean;
5757
tabIndex: number;
5858
hasLabelAndIcon: boolean;
@@ -65,15 +65,14 @@ export type TabPropsLegacy = {
6565
export type TabProps = {
6666
defaultActive?: boolean;
6767
active?: boolean;
68-
icon?: string | SVG;
69-
label: string;
7068
title?: string;
69+
tabId?: string;
7170
disabled?: boolean;
7271
notificationNumber?: boolean | number;
7372
children: ReactNode;
7473
onClick?: () => void;
7574
onHover?: () => void;
76-
};
75+
} & (TabLabelProps | TabIconProps);
7776

7877
type LegacyProps = {
7978
/**

0 commit comments

Comments
 (0)