Skip to content

Commit

Permalink
[docs-infra] Limit the copy button to the visible code block (#42115)
Browse files Browse the repository at this point in the history
  • Loading branch information
danilo-leal committed May 13, 2024
1 parent 66f065c commit 8dd01ec
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 85 deletions.
30 changes: 27 additions & 3 deletions docs/src/modules/components/Demo.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import copy from 'clipboard-copy';
import { useRouter } from 'next/router';
import { debounce } from '@mui/material/utils';
import { alpha, styled } from '@mui/material/styles';
Expand All @@ -12,6 +13,8 @@ import Box from '@mui/material/Box';
import Collapse from '@mui/material/Collapse';
import NoSsr from '@mui/material/NoSsr';
import { HighlightedCode } from '@mui/docs/HighlightedCode';
import ContentCopyRoundedIcon from '@mui/icons-material/ContentCopyRounded';
import LibraryAddCheckRoundedIcon from '@mui/icons-material/LibraryAddCheckRounded';
import DemoSandbox from 'docs/src/modules/components/DemoSandbox';
import ReactRunner from 'docs/src/modules/components/ReactRunner';
import DemoEditor from 'docs/src/modules/components/DemoEditor';
Expand Down Expand Up @@ -350,9 +353,6 @@ const DemoCodeViewer = styled(HighlightedCode)(() => ({
borderBottomLeftRadius: 12,
borderBottomRightRadius: 12,
},
'& .MuiCode-copy': {
display: 'none',
},
}));

const AnchorLink = styled('div')({
Expand Down Expand Up @@ -557,6 +557,21 @@ export default function Demo(props) {
demoData.relativeModules,
]);

const [copiedContent, setCopiedContent] = React.useState(false);

const handleCopyClick = async () => {
try {
const activeTabData = tabs[activeTab];
await copy(activeTabData.raw);
setCopiedContent(true);
setTimeout(() => {
setCopiedContent(false);
}, 1000);
} catch (error) {
console.error('Code content not copied', error);
}
};

return (
<Root>
<AnchorLink id={demoName} />
Expand Down Expand Up @@ -596,6 +611,10 @@ export default function Demo(props) {
<DemoToolbar
codeOpen={codeOpen}
codeVariant={codeVariant}
copyIcon={
copiedContent ? <LibraryAddCheckRoundedIcon /> : <ContentCopyRoundedIcon />
}
copyButtonOnClick={handleCopyClick}
hasNonSystemDemos={hasNonSystemDemos}
demo={demo}
demoData={demoData}
Expand Down Expand Up @@ -646,6 +665,11 @@ export default function Demo(props) {
'data-ga-event-label': demo.gaLabel,
'data-ga-event-action': 'copy-click',
}}
sx={{
'& .MuiCode-copy': {
display: 'none',
},
}}
/>
) : (
<DemoEditor
Expand Down
44 changes: 6 additions & 38 deletions docs/src/modules/components/DemoToolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ 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 ContentCopyRoundedIcon from '@mui/icons-material/ContentCopyRounded';
import { useRouter } from 'next/router';
import { CODE_VARIANTS, CODE_STYLING } from 'docs/src/modules/constants';
import { useSetCodeVariant } from 'docs/src/modules/utils/codeVariant';
Expand Down Expand Up @@ -272,35 +271,12 @@ function useToolbar(controlRefs, options = {}) {
};
}

function copyWithRelativeModules(raw, relativeModules) {
if (relativeModules) {
relativeModules.forEach(({ module, raw: content }) => {
// remove exports from relative module
content = content.replace(/export( )*(default)*( )*\w+;|export default|export/gm, '');
// replace import statement with relative module content
// the module might be imported with or without extension, so we need to cover all cases
// E.g.: /import .* from '(.\/top100Films.js|.\/top100Films)';/
const extensions = ['', '.js', '.jsx', '.ts', '.tsx', '.css', '.json'];
const patterns = extensions
.map((ext) => {
if (module.endsWith(ext)) {
return module.replace(ext, '');
}
return '';
})
.filter(Boolean)
.join('|');
const importPattern = new RegExp(`import .* from '(${patterns})';`);
raw = raw.replace(importPattern, content);
});
}
return copy(raw);
}

export default function DemoToolbar(props) {
const {
codeOpen,
codeVariant,
copyButtonOnClick,
copyIcon,
hasNonSystemDemos,
demo,
demoData,
Expand Down Expand Up @@ -349,16 +325,6 @@ export default function DemoToolbar(props) {
setSnackbarOpen(false);
};

const handleCopyClick = async () => {
try {
await copyWithRelativeModules(demoData.raw, demoData.relativeModules);
setSnackbarMessage(t('copiedSource'));
setSnackbarOpen(true);
} finally {
handleMoreClose();
}
};

const createHandleCodeSourceLink = (anchor, codeVariantParam, stylingSolution) => async () => {
try {
await copy(
Expand Down Expand Up @@ -601,11 +567,11 @@ export default function DemoToolbar(props) {
data-ga-event-category="demo"
data-ga-event-label={demo.gaLabel}
data-ga-event-action="copy"
onClick={handleCopyClick}
onClick={copyButtonOnClick}
{...getControlProps(6)}
sx={{ borderRadius: 1 }}
>
<ContentCopyRoundedIcon />
{copyIcon}
</IconButton>
</DemoTooltip>
<DemoTooltip title={t('resetFocus')} placement="bottom">
Expand Down Expand Up @@ -751,6 +717,8 @@ export default function DemoToolbar(props) {
DemoToolbar.propTypes = {
codeOpen: PropTypes.bool.isRequired,
codeVariant: PropTypes.string.isRequired,
copyButtonOnClick: PropTypes.object.isRequired,
copyIcon: PropTypes.object.isRequired,
demo: PropTypes.object.isRequired,
demoData: PropTypes.object.isRequired,
demoId: PropTypes.string,
Expand Down
6 changes: 2 additions & 4 deletions packages/markdown/parseMarkdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -423,10 +423,8 @@ function createRender(context) {
escaped ? code : escape(code, true)
}</code></pre>${[
'<button data-ga-event-category="code" data-ga-event-action="copy-click" aria-label="Copy the code" class="MuiCode-copy">',
'<svg focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="ContentCopyRoundedIcon">',
'<use class="MuiCode-copy-icon" xlink:href="#copy-icon" />',
'<use class="MuiCode-copied-icon" xlink:href="#copied-icon" />',
'</svg>',
'<span class="MuiCode-copy-label">Copy</span>',
'<span class="MuiCode-copied-label">Copied</span>',
'<span class="MuiCode-copyKeypress"><span>(or</span> $keyC<span>)</span></span></button></div>',
].join('')}\n`;
};
Expand Down
10 changes: 2 additions & 8 deletions packages/mui-docs/src/CodeCopy/CodeCopyButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import * as React from 'react';
import ContentCopyRoundedIcon from '@mui/icons-material/ContentCopyRounded';
import LibraryAddCheckRoundedIcon from '@mui/icons-material/LibraryAddCheckRounded';
import useClipboardCopy from './useClipboardCopy';

export interface CodeCopyButtonProps {
Expand All @@ -27,12 +25,8 @@ export function CodeCopyButton(props: CodeCopyButtonProps) {
}}
>
{/* material-ui/no-hardcoded-labels */}
{isCopied ? (
<LibraryAddCheckRoundedIcon sx={{ fontSize: 18 }} />
) : (
<ContentCopyRoundedIcon sx={{ fontSize: 18 }} />
)}
<span className="MuiCode-copyKeypress">
{isCopied ? 'Copied' : 'Copy'}
<span className="MuiCode-copyKeypress" style={{ opacity: isCopied ? 0 : 1 }}>
<span>(or</span> {key}C<span>)</span>
</span>
</button>
Expand Down
56 changes: 24 additions & 32 deletions packages/mui-docs/src/MarkdownElement/MarkdownElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -532,44 +532,34 @@ const Root = styled('div')(
top: 0,
},
'& .MuiCode-copy': {
display: 'inline-flex',
flexDirection: 'row-reverse',
alignItems: 'center',
width: 26,
height: 26,
cursor: 'pointer',
position: 'absolute',
top: 12,
right: 12,
display: 'inline-flex',
flexDirection: 'row-reverse',
alignItems: 'center',
height: 24,
padding: theme.spacing(0.5),
fontFamily: 'inherit',
fontWeight: 500,
paddingBottom: '5px', // optical alignment
fontFamily: lightTheme.typography.fontFamily,
fontWeight: lightTheme.typography.fontWeightMedium,
fontSize: lightTheme.typography.pxToRem(12),
borderRadius: 6,
border: 'none',
backgroundColor: 'hsl(210, 35%, 9%)', // using the code block one-off background color (defined in line 23)
color: '#FFF',
border: '1px solid',
borderColor: alpha(lightTheme.palette.primaryDark[600], 0.5),
backgroundColor: alpha(lightTheme.palette.primaryDark[800], 0.5),
color: `var(--muidocs-palette-grey-200, ${lightTheme.palette.grey[200]})`,
transition: theme.transitions.create(['background', 'borderColor', 'display'], {
duration: theme.transitions.duration.shortest,
}),
'& svg': {
userSelect: 'none',
width: theme.typography.pxToRem(16),
height: theme.typography.pxToRem(16),
display: 'inline-block',
fill: 'currentcolor',
flexShrink: 0,
fontSize: '18px',
margin: 'auto',
opacity: 0.5,
},
'& .MuiCode-copied-icon': {
'& .MuiCode-copied-label': {
display: 'none',
},
'&:hover, &:focus': {
backgroundColor: lightTheme.palette.primaryDark[600],
'& svg': {
opacity: 1,
},
borderColor: `var(--muidocs-palette-primaryDark-400, ${lightTheme.palette.primaryDark[400]})`,
backgroundColor: `var(--muidocs-palette-primaryDark-700, ${lightTheme.palette.primaryDark[700]})`,
color: '#FFF',
'& .MuiCode-copyKeypress': {
display: 'block',
// Approximate no hover capabilities with no keyboard
Expand All @@ -582,17 +572,19 @@ const Root = styled('div')(
'& .MuiCode-copyKeypress': {
display: 'none',
position: 'absolute',
right: 26,
right: 34,
},
'&[data-copied]': {
// style of the button when it is in copied state.
borderColor: lightTheme.palette.primary[700],
borderColor: `var(--muidocs-palette-primaryDark-400, ${lightTheme.palette.primaryDark[400]})`,
backgroundColor: `var(--muidocs-palette-primaryDark-700, ${lightTheme.palette.primaryDark[700]})`,
color: '#fff',
backgroundColor: lightTheme.palette.primaryDark[600],
'& .MuiCode-copy-icon': {
'& .MuiCode-copyKeypress': {
opacity: 0,
},
'& .MuiCode-copy-label': {
display: 'none',
},
'& .MuiCode-copied-icon': {
'& .MuiCode-copied-label': {
display: 'block',
},
},
Expand Down

0 comments on commit 8dd01ec

Please sign in to comment.