Skip to content

[docs] Full screen demos in new tab #46088

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e97ca16
Initial implememtation
Janpot Apr 1, 2025
b5418ad
Update generateBaseUiApiPages.ts
Janpot Apr 1, 2025
5ed76c1
do not render app bar in scoped demo
cherniavskii Apr 30, 2025
c8a449b
add "Open in new tab" button in demo toolbar
cherniavskii Apr 30, 2025
d36846a
add log
cherniavskii Apr 30, 2025
3b26fe1
Merge branch 'master' into full-screen-demo
cherniavskii Apr 30, 2025
b35637c
fix merge issue
cherniavskii Apr 30, 2025
cebcf23
remove base-ui pages
cherniavskii Apr 30, 2025
d01df2b
fix demos width
cherniavskii May 1, 2025
928ce23
log
cherniavskii May 1, 2025
9a1672d
fall back to window.location when router is not ready yet
cherniavskii May 2, 2025
bd115b1
revert unnecessary changes
cherniavskii May 2, 2025
db12e0c
Merge branch 'master' into full-screen-demo
cherniavskii May 2, 2025
f0e5822
query params fallback
cherniavskii May 5, 2025
b5487a1
Merge branch 'master' into full-screen-demo
cherniavskii May 5, 2025
c708df7
make body invisible if scopedDemo URL param is present
cherniavskii May 6, 2025
52338d8
fix
cherniavskii May 6, 2025
ec12446
add enableOpenInNewTab option to markdown loader
cherniavskii May 6, 2025
256bb18
port scopedDemo logic to MarkdownDocs
cherniavskii May 6, 2025
d84a5eb
lint
cherniavskii May 6, 2025
880c5ba
Merge branch 'master' into full-screen-demo
cherniavskii May 6, 2025
b637287
Merge branch 'master' into full-screen-demo
cherniavskii May 7, 2025
1331a1c
Merge branch 'master' into full-screen-demo
cherniavskii May 27, 2025
3765168
update control indices
cherniavskii May 27, 2025
a203982
revert getLayout removal
cherniavskii May 28, 2025
213c031
do not render page layout in scoped demos
cherniavskii May 28, 2025
d98f4bc
Merge branch 'master' into full-screen-demo
cherniavskii May 28, 2025
1b203e8
update MarkdownDocs
cherniavskii May 28, 2025
b8a8d6a
add DemoPageThemeProvider to old MarkdownDocs
cherniavskii Jun 4, 2025
62042bc
move body visibility change to useScopedDemo
cherniavskii Jun 4, 2025
0754449
Merge branch 'master' into full-screen-demo
cherniavskii Jun 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export default withDocsInfra({
{
loader: require.resolve('@mui/internal-markdown/loader'),
options: {
enableOpenInNewTab: true,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made it opt-in; the "Open in new tab" button in the demo toolbar is not shown by default.
But the ?scopedDemo URL is supported regardless of this setting.

I don't know if the Core wants to have this enabled for the whole repo.
You can disable enableOpenInNewTab and enable it for specific pages like this:

import * as pageProps from 'docs/data/material/components/drawers/drawers.md?muiMarkdown';

export default function Page() {
  return <MarkdownDocs {...pageProps} enableOpenInNewTab />;
}

This could be particularly useful for pages like https://mui.com/material-ui/react-drawer/

Let me know if you have a preference for this setting, and I'll update the PR accordingly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume it's a question for @DiegoAndai

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay. Let's not enable this on the Core's demos for now. I agree its useful, but we're stretched a bit thin with other projects' development and I'm not keen in receiving issues because some demo doesn't work on the "new tab mode". Just trying to keep things with as few changes as possible for now.

workspaceRoot,
ignoreLanguagePages: LANGUAGES_IGNORE_PAGES,
languagesInProgress: LANGUAGES_IN_PROGRESS,
Expand Down
10 changes: 10 additions & 0 deletions docs/pages/_document.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,16 @@ export default class MyDocument extends Document {
<MuiInitColorSchemeScript defaultMode="system" />
<JoyInitColorSchemeScript defaultMode="system" />
<Main />
<script
// Avoid doc page being rendered before the page is hydrated and the demo is rendered
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
__html: `
if ((new URLSearchParams(window.location.search)).get('scopedDemo')) {
document.body.style.visibility = 'hidden';
}`,
}}
/>
<script
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
Expand Down
136 changes: 73 additions & 63 deletions docs/src/modules/components/AppFrame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import LogoWithCopyMenu from 'docs/src/components/action/LogoWithCopyMenu';
import AppFrameBanner from 'docs/src/components/banner/AppFrameBanner';
import { DemoPageThemeProvider } from 'docs/src/theming';
import { pathnameToLanguage } from 'docs/src/modules/utils/helpers';
import useScopedDemo from '../utils/useScopedDemo';

const nProgressStart = debounce(() => {
NProgress.start();
Expand Down Expand Up @@ -187,73 +188,82 @@ export default function AppFrame(props: AppFrameProps) {
const disablePermanent = activePage?.disableDrawer === true || disableDrawer === true;
const isJoy = canonicalAs.startsWith('/joy-ui/');

const scopedDemo = useScopedDemo();

return (
<DemoPageThemeProvider hasJoy={isJoy}>
<RootDiv className={className}>
<StyledAppBar
disablePermanent={disablePermanent}
sx={{ minHeight: 'var(--MuiDocs-header-height)' }}
>
<GlobalStyles
styles={{
':root': {
'--MuiDocs-header-height': `${HEIGHT}px`,
},
}}
/>
<Stack direction="row" sx={{ alignItems: 'center', position: 'relative', width: '100%' }}>
<NavIconButton
edge="start"
color="primary"
size="small"
aria-label={t('appFrame.openDrawer')}
disablePermanent={disablePermanent}
onClick={() => setMobileOpen(true)}
sx={{ ml: '1px' }}
{scopedDemo ? (
children
) : (
<RootDiv className={className}>
<StyledAppBar
disablePermanent={disablePermanent}
sx={{ minHeight: 'var(--MuiDocs-header-height)' }}
>
<GlobalStyles
styles={{
':root': {
'--MuiDocs-header-height': `${HEIGHT}px`,
},
}}
/>
<Stack
direction="row"
sx={{ alignItems: 'center', position: 'relative', width: '100%' }}
>
<SvgHamburgerMenu />
</NavIconButton>
<Box sx={{ display: { xs: 'flex', md: 'flex', lg: 'none' } }}>
<LogoWithCopyMenu
logo={productIdentifier.logo}
logoSvgString={productIdentifier.logoSvg}
wordmarkSvgString={productIdentifier.wordmarkSvg}
marginLeft
/>
</Box>
<Stack direction="row" spacing={1} useFlexGap sx={{ ml: 'auto' }}>
<BannerComponent />
<DeferredAppSearch />
<Tooltip title={t('appFrame.github')} enterDelay={300}>
<IconButton
component="a"
color="primary"
size="small"
href={process.env.SOURCE_CODE_REPO}
data-ga-event-category="header"
data-ga-event-action="github"
>
<GitHubIcon fontSize="small" />
</IconButton>
</Tooltip>
<Notifications />
<Tooltip title={t('appFrame.toggleSettings')} enterDelay={300}>
<IconButton color="primary" size="small" onClick={() => setSettingsOpen(true)}>
<SettingsIcon fontSize="small" />
</IconButton>
</Tooltip>
<NavIconButton
edge="start"
color="primary"
size="small"
aria-label={t('appFrame.openDrawer')}
disablePermanent={disablePermanent}
onClick={() => setMobileOpen(true)}
sx={{ ml: '1px' }}
>
<SvgHamburgerMenu />
</NavIconButton>
<Box sx={{ display: { xs: 'flex', md: 'flex', lg: 'none' } }}>
<LogoWithCopyMenu
logo={productIdentifier.logo}
logoSvgString={productIdentifier.logoSvg}
wordmarkSvgString={productIdentifier.wordmarkSvg}
marginLeft
/>
</Box>
<Stack direction="row" spacing={1} useFlexGap sx={{ ml: 'auto' }}>
<BannerComponent />
<DeferredAppSearch />
<Tooltip title={t('appFrame.github')} enterDelay={300}>
<IconButton
component="a"
color="primary"
size="small"
href={process.env.SOURCE_CODE_REPO}
data-ga-event-category="header"
data-ga-event-action="github"
>
<GitHubIcon fontSize="small" />
</IconButton>
</Tooltip>
<Notifications />
<Tooltip title={t('appFrame.toggleSettings')} enterDelay={300}>
<IconButton color="primary" size="small" onClick={() => setSettingsOpen(true)}>
<SettingsIcon fontSize="small" />
</IconButton>
</Tooltip>
</Stack>
</Stack>
</Stack>
</StyledAppBar>
<StyledAppNavDrawer
disablePermanent={disablePermanent}
onClose={closeDrawer}
onOpen={openDrawer}
mobileOpen={mobileOpen}
/>
{children}
<AppSettingsDrawer onClose={() => setSettingsOpen(false)} open={settingsOpen} />
</RootDiv>
</StyledAppBar>
<StyledAppNavDrawer
disablePermanent={disablePermanent}
onClose={closeDrawer}
onOpen={openDrawer}
mobileOpen={mobileOpen}
/>
{children}
<AppSettingsDrawer onClose={() => setSettingsOpen(false)} open={settingsOpen} />
</RootDiv>
)}
</DemoPageThemeProvider>
);
}
Expand Down
4 changes: 3 additions & 1 deletion docs/src/modules/components/Demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ const selectionOverride = (theme) => ({
});

export default function Demo(props) {
const { demo, demoOptions, disableAd, githubLocation } = props;
const { demo, demoOptions, disableAd, githubLocation, enableOpenInNewTab } = props;

if (process.env.NODE_ENV !== 'production') {
if (demoOptions.hideToolbar === false) {
Expand Down Expand Up @@ -552,6 +552,7 @@ export default function Demo(props) {
onResetDemoClick={resetDemo}
openDemoSource={openDemoSource}
showPreview={showPreview}
enableOpenInNewTab={enableOpenInNewTab}
/>
</React.Suspense>
</NoSsr>
Expand Down Expand Up @@ -636,5 +637,6 @@ Demo.propTypes = {
*/
demoOptions: PropTypes.object.isRequired,
disableAd: PropTypes.bool.isRequired,
enableOpenInNewTab: PropTypes.bool,
githubLocation: PropTypes.string.isRequired,
};
30 changes: 26 additions & 4 deletions docs/src/modules/components/DemoToolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Tooltip from '@mui/material/Tooltip';
import Divider from '@mui/material/Divider';
import RefreshRoundedIcon from '@mui/icons-material/RefreshRounded';
import ResetFocusIcon from '@mui/icons-material/CenterFocusWeak';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import { useRouter } from 'next/router';
import { CODE_VARIANTS } from 'docs/src/modules/constants';
import { useSetCodeVariant } from 'docs/src/modules/utils/codeVariant';
Expand Down Expand Up @@ -286,6 +287,7 @@ export default function DemoToolbar(props) {
onResetDemoClick,
openDemoSource,
showPreview,
enableOpenInNewTab,
} = props;

const setCodeVariant = useSetCodeVariant();
Expand Down Expand Up @@ -520,13 +522,32 @@ export default function DemoToolbar(props) {
</DemoTooltip>
</React.Fragment>
)}
{enableOpenInNewTab && (
<DemoTooltip title={t('openInNewTab')} placement="bottom">
<IconButton
data-ga-event-category="demo"
data-ga-event-label={demo.gaLabel}
data-ga-event-action="openInNewTab"
onClick={() => {
const url = new URL(window.location.href);
url.hash = '';
url.searchParams.set('scopedDemo', demoOptions.demo);
window.open(url.toString(), '_blank');
}}
{...getControlProps(6)}
sx={{ borderRadius: 1 }}
>
<OpenInNewIcon />
</IconButton>
</DemoTooltip>
)}
Comment on lines +525 to +543
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

I think it would be better to use label "Open standalone demo".

When I first see "Open in new tab", I have no clue on what to expect and not sure why should I open a new tab.

<DemoTooltip title={t('copySource')} placement="bottom">
<IconButton
data-ga-event-category="demo"
data-ga-event-label={demo.gaLabel}
data-ga-event-action="copy"
onClick={copyButtonOnClick}
{...getControlProps(6)}
{...getControlProps(7)}
sx={{ borderRadius: 1 }}
>
{copyIcon}
Expand All @@ -538,7 +559,7 @@ export default function DemoToolbar(props) {
data-ga-event-label={demo.gaLabel}
data-ga-event-action="reset-focus"
onClick={handleResetFocusClick}
{...getControlProps(7)}
{...getControlProps(8)}
sx={{ borderRadius: 1 }}
>
<ResetFocusIcon />
Expand All @@ -551,7 +572,7 @@ export default function DemoToolbar(props) {
data-ga-event-label={demo.gaLabel}
data-ga-event-action="reset"
onClick={onResetDemoClick}
{...getControlProps(8)}
{...getControlProps(9)}
sx={{ borderRadius: 1 }}
>
<RefreshRoundedIcon />
Expand All @@ -562,7 +583,7 @@ export default function DemoToolbar(props) {
aria-label={t('seeMore')}
aria-owns={anchorEl ? 'demo-menu-more' : undefined}
aria-haspopup="true"
{...getControlProps(9)}
{...getControlProps(10)}
sx={{ borderRadius: 1 }}
>
<MoreVertIcon />
Expand Down Expand Up @@ -634,6 +655,7 @@ DemoToolbar.propTypes = {
demoName: PropTypes.string.isRequired,
demoOptions: PropTypes.object.isRequired,
demoSourceId: PropTypes.string,
enableOpenInNewTab: PropTypes.bool,
hasNonSystemDemos: PropTypes.string,
initialFocusRef: PropTypes.shape({ current: PropTypes.object }).isRequired,
onCodeOpenChange: PropTypes.func.isRequired,
Expand Down
32 changes: 32 additions & 0 deletions docs/src/modules/components/MarkdownDocs.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { Ad, AdGuest } from '@mui/docs/Ad';
import RichMarkdownElement from 'docs/src/modules/components/RichMarkdownElement';
import AppLayoutDocs from 'docs/src/modules/components/AppLayoutDocs';
import { useUserLanguage } from '@mui/docs/i18n';
import { useRouter } from 'next/router';
import { DemoPageThemeProvider } from 'docs/src/theming';
import useScopedDemo from '../utils/useScopedDemo';

export default function MarkdownDocs(props) {
const {
Expand All @@ -14,11 +17,38 @@ export default function MarkdownDocs(props) {
docs,
demoComponents,
srcComponents,
enableOpenInNewTab = false,
} = props;

const userLanguage = useUserLanguage();
const localizedDoc = docs[userLanguage] || docs.en;

const router = useRouter();
const scopedDemo = useScopedDemo();

if (scopedDemo) {
const canonicalAs = router.asPath || '';
const isJoy = canonicalAs.startsWith('/joy-ui/');
return (
<DemoPageThemeProvider hasJoy={isJoy}>
<div style={{ width: '100%', height: '100vh', padding: '4px' }}>
<RichMarkdownElement
demoComponents={demoComponents}
demos={demos}
disableAd={disableAd}
localizedDoc={localizedDoc}
srcComponents={srcComponents}
renderedMarkdownOrDemo={{
demo: scopedDemo,
hideToolbar: true,
bg: false,
}}
/>
</div>
</DemoPageThemeProvider>
);
}

return (
<AppLayoutDocs
cardOptions={{
Expand Down Expand Up @@ -46,6 +76,7 @@ export default function MarkdownDocs(props) {
localizedDoc={localizedDoc}
renderedMarkdownOrDemo={renderedMarkdownOrDemo}
srcComponents={srcComponents}
enableOpenInNewTab={enableOpenInNewTab}
/>
))}
</AppLayoutDocs>
Expand All @@ -58,6 +89,7 @@ MarkdownDocs.propTypes = {
disableAd: PropTypes.bool,
disableToc: PropTypes.bool,
docs: PropTypes.object.isRequired,
enableOpenInNewTab: PropTypes.bool,
srcComponents: PropTypes.object,
};

Expand Down
Loading