chore(site): In order to better debug the online demo (#56072)

This commit is contained in:
𝑾𝒖𝒙𝒉
2025-12-24 10:19:26 +08:00
committed by GitHub
parent d680ea3d30
commit 742ecee166
5 changed files with 112 additions and 36 deletions

View File

@@ -1,7 +1,8 @@
import React, { Suspense, useRef } from 'react';
import { LinkOutlined, ThunderboltOutlined } from '@ant-design/icons';
import { BugOutlined, ThunderboltOutlined } from '@ant-design/icons';
import stackblitzSdk from '@stackblitz/sdk';
import { Flex, Tooltip } from 'antd';
import type { MenuProps } from 'antd';
import { Button, Dropdown, Flex, Tooltip } from 'antd';
import { FormattedMessage, useSiteData } from 'dumi';
import LZString from 'lz-string';
@@ -28,8 +29,6 @@ function compress(string: string): string {
}
interface ActionsProps {
showOnlineUrl: boolean;
docsOnlineUrl?: string;
assetId: string;
title?: string;
pkgDependencyList: Record<PropertyKey, string>;
@@ -39,11 +38,10 @@ interface ActionsProps {
onCodeExpand: () => void;
entryCode: string;
styleCode: string;
debugOptions?: MenuProps['items'];
}
const Actions: React.FC<ActionsProps> = ({
showOnlineUrl,
docsOnlineUrl,
assetId,
title,
jsx,
@@ -53,6 +51,7 @@ const Actions: React.FC<ActionsProps> = ({
pkgDependencyList,
entryCode,
styleCode,
debugOptions,
}) => {
const [, lang] = useLocale();
const isZhCN = lang === 'cn';
@@ -213,21 +212,15 @@ createRoot(document.getElementById('container')).render(<Demo />);
});
return (
<Flex wrap gap="middle" className="code-box-actions">
{/* 在线文档按钮 */}
{showOnlineUrl && (
<Tooltip title={<FormattedMessage id="app.demo.online" />}>
<a
className="code-box-code-action"
aria-label="open in new tab"
target="_blank"
rel="noreferrer"
href={docsOnlineUrl || ''}
>
<LinkOutlined className="code-box-online" />
</a>
</Tooltip>
)}
<Flex wrap gap="middle" className="code-box-actions" align="center">
{
// 调试选项
debugOptions?.length ? (
<Dropdown menu={{ items: debugOptions }} arrow={{ pointAtCenter: true }}>
<Button icon={<BugOutlined />} color="purple" variant="filled" size="small" />
</Dropdown>
) : null
}
{/* CodeSandbox 按钮 */}
<form
className="code-box-code-action"

View File

@@ -1,10 +1,11 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { UpOutlined } from '@ant-design/icons';
import { Badge, Tooltip } from 'antd';
import type { MenuProps } from 'antd';
import { Badge, Tag, Tooltip } from 'antd';
import { createStyles, css } from 'antd-style';
import { clsx } from 'clsx';
import { FormattedMessage, useLiveDemo } from 'dumi';
import { FormattedMessage, useLiveDemo, useSiteData } from 'dumi';
import { major, minVersion } from 'semver';
import type { AntdPreviewerProps } from '.';
import useLocation from '../../../hooks/useLocation';
import BrowserFrame from '../../common/BrowserFrame';
@@ -12,6 +13,8 @@ import ClientOnly from '../../common/ClientOnly';
import CodePreview from '../../common/CodePreview';
import EditButton from '../../common/EditButton';
import SiteContext from '../../slots/SiteContext';
import DemoContext from '../../slots/DemoContext';
import { isOfficialHost } from '../../utils';
import Actions from './Actions';
const useStyle = createStyles(({ cssVar }) => {
@@ -61,6 +64,9 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
clientOnly,
pkgDependencyList,
} = props;
const { showDebug } = React.use(DemoContext);
const { pkg } = useSiteData();
const location = useLocation();
const { styles } = useStyle();
@@ -79,17 +85,15 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
const anchorRef = useRef<HTMLAnchorElement>(null);
const [codeExpand, setCodeExpand] = useState<boolean>(false);
const { isDark } = React.use(SiteContext);
const { hash, pathname, search } = location;
const docsOnlineUrl = `https://ant.design${pathname ?? ''}${search ?? ''}#${asset.id}`;
const [showOnlineUrl, setShowOnlineUrl] = useState<boolean>(false);
/**
* Record whether it is deployed on the official domain name.
* Note that window.location.hostname is not available on the server side due to hydration issues
*/
const [deployedOnOfficialHost, setDeployedOnOfficialHost] = useState<boolean>(true);
useEffect(() => {
const regexp = /preview-(\d+)-ant-design/; // matching PR preview addresses
setShowOnlineUrl(
process.env.NODE_ENV === 'development' || regexp.test(window.location.hostname),
);
setDeployedOnOfficialHost(isOfficialHost(window.location.hostname));
}, []);
useEffect(() => {
@@ -102,6 +106,33 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
setCodeExpand(expand);
}, [expand]);
const generateDocUrl = (domain = 'https://ant.design') =>
`${domain}${pathname ?? ''}${search ?? ''}#${asset.id}`;
// Enable "Go Online Docs" only when deployed on non-official domains
const enableDocsOnlineUrl = process.env.NODE_ENV === 'development' || !deployedOnOfficialHost;
// Previous version demos are only available during the maintenance window
const [supportsPreviousVersionDemo, previousVersionDomain, previousVersion] = useMemo(() => {
const maintenanceDeadline = new Date('2026/12/31');
if (new Date() > maintenanceDeadline) {
return [false, undefined, -1] as const;
}
const currentMajor = major(pkg.version);
const previousMajor = Math.min(currentMajor - 1, 5);
let enabled = true;
// If the demo specifies a version, perform an additional check;
if (version) {
const minVer = minVersion(version);
enabled = minVer?.major ? minVer.major < currentMajor : true;
}
return [enabled, `https://${previousMajor}x.ant.design`, previousMajor];
}, [version, pkg.version]);
const mergedChildren = !iframe && clientOnly ? <ClientOnly>{children}</ClientOnly> : children;
const demoUrlWithTheme = useMemo(() => {
return `${demoUrl}${isDark ? '?theme=dark' : ''}`;
@@ -143,6 +174,47 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
backgroundColor: background === 'grey' ? backgroundGrey : undefined,
};
const debugOptions: MenuProps['items'] = [
{
key: 'online',
label: (
<a
aria-label="Go to online documentation"
href={generateDocUrl()}
target="_blank"
rel="noreferrer"
>
<FormattedMessage id="app.demo.online" />
</a>
),
icon: (
<Tag variant="filled" color="blue">
ant.design
</Tag>
),
enabled: enableDocsOnlineUrl,
},
{
key: 'previousVersion',
label: (
<a
aria-label="Go to previous version documentation"
href={generateDocUrl(previousVersionDomain)}
target="_blank"
rel="noreferrer"
>
<FormattedMessage id="app.demo.previousVersion" values={{ version: previousVersion }} />
</a>
),
icon: (
<Tag variant="filled" color="purple">
v{previousVersion}
</Tag>
),
enabled: supportsPreviousVersionDemo,
},
].filter(({ enabled }) => showDebug && enabled);
const codeBox: React.ReactNode = (
<section className={codeBoxClass} id={asset.id}>
<section
@@ -174,8 +246,7 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
/>
)}
<Actions
showOnlineUrl={showOnlineUrl}
docsOnlineUrl={docsOnlineUrl}
debugOptions={debugOptions}
entryCode={entryCode}
styleCode={style}
pkgDependencyList={pkgDependencyList}

View File

@@ -36,7 +36,8 @@
"app.demo.stackblitz": "Open in Stackblitz",
"app.demo.codeblock": "Open in Hitu (This feature is only available in the internal network environment)",
"app.demo.separate": "Open in a new window",
"app.demo.online": "Online Address",
"app.demo.online": "Official demo",
"app.demo.previousVersion": "Previous version",
"app.home.introduce": "A design system for enterprise-level products. Create an efficient and enjoyable work experience.",
"app.home.pr-welcome": "💡 It is an alpha version and still in progress. Contribution from community is welcome!",
"app.home.recommend": "Recommended",

View File

@@ -36,7 +36,8 @@
"app.demo.stackblitz": "在 Stackblitz 中打开",
"app.demo.codeblock": "在海兔中打开(此功能仅在内网环境可用)",
"app.demo.separate": "在新窗口打开",
"app.demo.online": "线上地址",
"app.demo.online": "官网示例",
"app.demo.previousVersion": "历史版本",
"app.home.introduce": "企业级产品设计体系,创造高效愉悦的工作体验",
"app.home.pr-welcome": "💡 当前为 alpha 版本,仍在开发中。欢迎社区一起共建,让 Ant Design 变得更好!",
"app.home.recommend": "精彩推荐",

View File

@@ -206,4 +206,14 @@ export function matchDeprecated(v: string): MatchDeprecatedResult {
};
}
/**
* Determine if a hostname is an official domain.
* antd creates a temporary preview site for each PR for convenient preview and testing.
* Usually on platforms like surge.sh or Cloudflare Pages.
*/
export function isOfficialHost(hostname: string) {
const officialHostnames = ['ant.design', 'antgroup.com'];
return officialHostnames.some((official) => hostname.includes(official));
}
export const getThemeConfig = () => themeConfig;