Skip to content

Commit 7e3c952

Browse files
fix(tabs): honor hash-linked nav tabs
1 parent f3613a6 commit 7e3c952

File tree

1 file changed

+29
-2
lines changed
  • packages/react-core/src/components/Tabs

1 file changed

+29
-2
lines changed

packages/react-core/src/components/Tabs/Tabs.tsx

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,25 @@ const variantStyle = {
136136
secondary: styles.modifiers.secondary
137137
};
138138

139+
const getHashFromHref = (href?: string) => {
140+
const hashIndex = href?.indexOf('#') ?? -1;
141+
142+
return hashIndex >= 0 ? href.slice(hashIndex) : undefined;
143+
};
144+
145+
const getTabHashActiveKey = ({ children, component, isNav }: Pick<TabsProps, 'children' | 'component' | 'isNav'>) => {
146+
if (!canUseDOM || !(component === TabsComponent.nav || isNav) || !window.location.hash) {
147+
return undefined;
148+
}
149+
150+
return Children.toArray(children)
151+
.filter((child): child is TabElement => isValidElement(child))
152+
.filter(({ props }) => !props.isHidden)
153+
.find(
154+
({ props }) => !props.isDisabled && !props.isAriaDisabled && getHashFromHref(props.href) === window.location.hash
155+
)?.props.eventKey;
156+
};
157+
139158
interface TabsState {
140159
/** Used to signal if the scroll buttons should be used */
141160
enableScrollButtons: boolean;
@@ -164,13 +183,18 @@ class Tabs extends Component<TabsProps, TabsState> {
164183
private direction = 'ltr';
165184
constructor(props: TabsProps) {
166185
super(props);
186+
const hashActiveKey = getTabHashActiveKey(props);
187+
167188
this.state = {
168189
enableScrollButtons: false,
169190
showScrollButtons: false,
170191
renderScrollButtons: false,
171192
disableBackScrollButton: true,
172193
disableForwardScrollButton: true,
173-
shownKeys: this.props.defaultActiveKey !== undefined ? [this.props.defaultActiveKey] : [this.props.activeKey], // only for mountOnEnter case
194+
shownKeys:
195+
this.props.defaultActiveKey !== undefined
196+
? [hashActiveKey ?? this.props.defaultActiveKey]
197+
: [hashActiveKey ?? this.props.activeKey], // only for mountOnEnter case
174198
uncontrolledActiveKey: this.props.defaultActiveKey,
175199
uncontrolledIsExpandedLocal: this.props.defaultIsExpanded,
176200
ouiaStateId: getDefaultOUIAId(Tabs.displayName),
@@ -221,6 +245,7 @@ class Tabs extends Component<TabsProps, TabsState> {
221245
) {
222246
const { shownKeys } = this.state;
223247
const { onSelect, defaultActiveKey } = this.props;
248+
224249
// if defaultActiveKey Tabs are uncontrolled, set new active key internally
225250
if (defaultActiveKey !== undefined) {
226251
this.setState({
@@ -401,6 +426,7 @@ class Tabs extends Component<TabsProps, TabsState> {
401426
const { activeKey, mountOnEnter, isOverflowHorizontal, children, defaultActiveKey } = this.props;
402427
const { shownKeys, overflowingTabCount, enableScrollButtons, uncontrolledActiveKey } = this.state;
403428
const isOnCloseUpdate = !!prevProps.onClose !== !!this.props.onClose;
429+
404430
if (
405431
(defaultActiveKey !== undefined && prevState.uncontrolledActiveKey !== uncontrolledActiveKey) ||
406432
(defaultActiveKey === undefined && prevProps.activeKey !== activeKey) ||
@@ -530,7 +556,8 @@ class Tabs extends Component<TabsProps, TabsState> {
530556
const uniqueId = id || getUniqueId();
531557
const defaultComponent = isNav && !component ? 'nav' : 'div';
532558
const Component: any = component !== undefined ? component : defaultComponent;
533-
const localActiveKey = defaultActiveKey !== undefined ? uncontrolledActiveKey : activeKey;
559+
const hashActiveKey = getTabHashActiveKey({ children, component, isNav });
560+
const localActiveKey = hashActiveKey ?? (defaultActiveKey !== undefined ? uncontrolledActiveKey : activeKey);
534561

535562
const isExpandedLocal = defaultIsExpanded !== undefined ? uncontrolledIsExpandedLocal : isExpanded;
536563
/* Uncontrolled expandable tabs */

0 commit comments

Comments
 (0)