refactor(web): Embed url mapping into Tabs component for general use (#13648)

This commit is contained in:
Anthony Burdi 2025-06-02 14:01:56 -04:00 committed by GitHub
parent 5041677082
commit 7d5519f2e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 109 additions and 2 deletions

View File

@ -25,6 +25,33 @@ const exampleTabs = [
},
];
const urlAwareTabs = [
{
key: 'overview',
name: 'Overview',
tooltip: 'View the overview',
component: <div>This is the overview content</div>,
},
{
key: 'details',
name: 'Details',
tooltip: 'View the details',
component: <div>This is the details content</div>,
},
{
key: 'settings',
name: 'Settings',
tooltip: 'View the settings',
component: <div>This is the settings content</div>,
},
];
const urlMap = {
overview: '/overview',
details: '/details',
settings: '/settings',
};
// Auto Docs
const meta = {
title: 'Components / Tabs',
@ -50,6 +77,19 @@ const meta = {
onChange: {
description: 'The handler called when any tab is clicked',
},
urlMap: {
description:
'A mapping of tab keys to URLs. When provided, the component will sync tab selection with the URL',
},
onUrlChange: {
description: 'A custom handler for URL changes. Defaults to window.history.replaceState',
},
defaultTab: {
description: 'The default tab to select when the URL does not match any tab',
},
getCurrentUrl: {
description: 'A custom function to get the current URL. Defaults to window.location.pathname',
},
},
// Args for the story
@ -71,3 +111,28 @@ export const sandbox: Story = {
</div>
),
};
const UrlAwareTabsDemo = () => {
const [selectedTab, setSelectedTab] = React.useState('overview');
return (
<div style={{ width: 225 }}>
<div style={{ marginBottom: '1rem' }}>
<p>Current URL: {window.location.pathname}</p>
<p>Selected Tab: {selectedTab}</p>
</div>
<Tabs
tabs={urlAwareTabs}
selectedTab={selectedTab}
onChange={setSelectedTab}
urlMap={urlMap}
defaultTab="overview"
/>
</div>
);
};
export const urlAware: Story = {
tags: ['dev'],
render: () => <UrlAwareTabsDemo />,
};

View File

@ -1,5 +1,5 @@
import { Tabs as AntTabs } from 'antd';
import React from 'react';
import React, { useEffect } from 'react';
import styled from 'styled-components';
import { Pill } from '@components/components/Pills';
@ -74,15 +74,57 @@ export interface Props {
tabs: Tab[];
selectedTab?: string;
onChange?: (selectedTabKey: string) => void;
urlMap?: Record<string, string>;
onUrlChange?: (url: string) => void;
defaultTab?: string;
getCurrentUrl?: () => string;
}
export function Tabs({ tabs, selectedTab, onChange }: Props) {
export function Tabs({
tabs,
selectedTab,
onChange,
urlMap,
onUrlChange = (url) => window.history.replaceState({}, '', url),
defaultTab,
getCurrentUrl = () => window.location.pathname,
}: Props) {
const { TabPane } = AntTabs;
// Create reverse mapping from URLs to tab keys if urlMap is provided
const urlToTabMap = React.useMemo(() => {
if (!urlMap) return {};
const map: Record<string, string> = {};
Object.entries(urlMap).forEach(([tabKey, url]) => {
map[url] = tabKey;
});
return map;
}, [urlMap]);
// Handle initial tab selection based on URL if urlMap is provided
useEffect(() => {
if (!urlMap || !onChange) return;
const currentUrl = getCurrentUrl();
const tabFromUrl = urlToTabMap[currentUrl];
if (tabFromUrl && tabFromUrl !== selectedTab) {
onChange(tabFromUrl);
} else if (!tabFromUrl && defaultTab && defaultTab !== selectedTab) {
onChange(defaultTab);
onUrlChange(urlMap[defaultTab]);
}
}, [getCurrentUrl, onChange, onUrlChange, selectedTab, urlMap, urlToTabMap, defaultTab]);
function handleTabClick(key: string) {
onChange?.(key);
const newTab = tabs.find((t) => t.key === key);
newTab?.onSelectTab?.();
// Update URL if urlMap is provided
if (urlMap && urlMap[key]) {
onUrlChange(urlMap[key]);
}
}
return (