From 4987748f4e699448dad5d5d4542495daab1abacf Mon Sep 17 00:00:00 2001 From: yatishgoel Date: Sat, 31 Jan 2026 12:39:46 +0530 Subject: [PATCH] Add simplified code handling and toggle functionality in Source components - Updated Source and SourceContainer to include `simplifiedCode` and `simplifiedSource` properties. - Enhanced Source component to toggle between simplified and full code display. - Modified emitTransformCode to handle simplified source transformation. - Updated renderJsx to support simplified names for better UI representation. --- code/addons/docs/src/blocks/blocks/Source.tsx | 1 + .../src/blocks/blocks/SourceContainer.tsx | 10 ++- .../docs/src/blocks/components/Source.tsx | 75 ++++++++++++++++--- .../modules/preview-web/emitTransformCode.ts | 10 ++- .../renderers/react/src/docs/jsxDecorator.tsx | 46 ++++++++++-- 5 files changed, 123 insertions(+), 19 deletions(-) diff --git a/code/addons/docs/src/blocks/blocks/Source.tsx b/code/addons/docs/src/blocks/blocks/Source.tsx index 135ec93cec76..4b44e7968071 100644 --- a/code/addons/docs/src/blocks/blocks/Source.tsx +++ b/code/addons/docs/src/blocks/blocks/Source.tsx @@ -163,6 +163,7 @@ export const useSourceProps = ( format, language, dark, + simplifiedCode: source?.simplifiedCode, }; }; diff --git a/code/addons/docs/src/blocks/blocks/SourceContainer.tsx b/code/addons/docs/src/blocks/blocks/SourceContainer.tsx index f6f6c87ea523..fac5ce0c715f 100644 --- a/code/addons/docs/src/blocks/blocks/SourceContainer.tsx +++ b/code/addons/docs/src/blocks/blocks/SourceContainer.tsx @@ -15,6 +15,7 @@ export function argsHash(args: Args): ArgsHash { export interface SourceItem { code: string; format?: SyntaxHighlighterFormatTypes; + simplifiedCode?: string; } export type StorySources = Record>; @@ -31,6 +32,7 @@ type SnippetRenderedEvent = { source: string; args?: Args; format?: SyntaxHighlighterFormatTypes; + simplifiedSource?: string; }; export const UNKNOWN_ARGS_HASH = '--unknown--'; @@ -52,11 +54,13 @@ export const SourceContainer: FC ({ + position: 'absolute', + top: 10, + right: 10, + zIndex: 1, + padding: '6px 12px', + fontSize: 12, + fontWeight: 600, + lineHeight: '16px', + color: theme.color.lightest, + background: 'rgba(255, 255, 255, 0.1)', + border: `1px solid rgba(255, 255, 255, 0.2)`, + borderRadius: theme.appBorderRadius, + cursor: 'pointer', + transition: 'all 150ms ease-out', + '&:hover': { + background: 'rgba(255, 255, 255, 0.2)', + borderColor: 'rgba(255, 255, 255, 0.3)', + }, + '&:focus': { + outline: 'none', + boxShadow: `0 0 0 1px ${theme.color.secondary}`, + }, +})); + const SourceSkeletonWrapper = styled.div(({ theme }) => ({ background: theme.background.content, borderRadius: theme.appBorderRadius, @@ -91,9 +121,12 @@ const Source: FunctionComponent = ({ code, dark, format = true, + simplifiedCode, ...rest }) => { const { typography } = useTheme(); + const [showSimplified, setShowSimplified] = useState(true); + if (isLoading) { return ; } @@ -101,17 +134,37 @@ const Source: FunctionComponent = ({ return {error}; } + // Determine which code to display based on toggle state + const displayCode = showSimplified && simplifiedCode ? simplifiedCode : code; + + // Only show toggle if we have both versions and they're different + const showToggle = simplifiedCode && simplifiedCode !== code; + const syntaxHighlighter = ( - - {code} - + + {showToggle && ( + setShowSimplified(!showSimplified)} + title={ + showSimplified + ? 'Expand to show full component names (e.g., Card.Header)' + : 'Collapse to show simplified names (e.g., CardHeader)' + } + > + {showSimplified ? 'Expand code' : 'Collapse code'} + + )} + + {displayCode} + + ); if (typeof dark === 'undefined') { return syntaxHighlighter; diff --git a/code/core/src/preview-api/modules/preview-web/emitTransformCode.ts b/code/core/src/preview-api/modules/preview-web/emitTransformCode.ts index 0240d738ee47..7d65ece7419c 100644 --- a/code/core/src/preview-api/modules/preview-web/emitTransformCode.ts +++ b/code/core/src/preview-api/modules/preview-web/emitTransformCode.ts @@ -12,16 +12,24 @@ type Transformer = | ((code: string, storyContext: ReducedStoryContext) => string | Promise) | undefined; -export async function emitTransformCode(source: string | undefined, context: ReducedStoryContext) { +export async function emitTransformCode( + source: string | undefined, + context: ReducedStoryContext, + simplifiedSource?: string +) { const transform = context.parameters?.docs?.source?.transform as Transformer; const { id, unmappedArgs } = context; const transformed = transform && source ? transform?.(source, context) : source; const result = transformed ? await transformed : undefined; + const transformedSimplified = + transform && simplifiedSource ? transform?.(simplifiedSource, context) : simplifiedSource; + const simplifiedResult = transformedSimplified ? await transformedSimplified : undefined; addons.getChannel().emit(SNIPPET_RENDERED, { id, source: result, args: unmappedArgs, + simplifiedSource: simplifiedResult, }); } diff --git a/code/renderers/react/src/docs/jsxDecorator.tsx b/code/renderers/react/src/docs/jsxDecorator.tsx index 1d1048c2e624..6a60e0bba30e 100644 --- a/code/renderers/react/src/docs/jsxDecorator.tsx +++ b/code/renderers/react/src/docs/jsxDecorator.tsx @@ -82,8 +82,11 @@ type JSXOptions = Options & { displayName?: string | Options['displayName']; }; -/** Apply the users parameters and render the jsx for a story */ -export const renderJsx = (code: React.ReactElement, options?: JSXOptions) => { +/** Apply the users parameters and render the jsx for a story, with option to use simplified names */ +export const renderJsx = ( + code: React.ReactElement, + options?: JSXOptions & { useSimplifiedNames?: boolean } +) => { if (typeof code === 'undefined') { logger.warn('Too many skip or undefined component'); return null; @@ -134,6 +137,27 @@ export const renderJsx = (code: React.ReactElement, options?: JSXOptions) => { displayNameDefaults = { // To get exotic component names resolving properly displayName: (el: any): string => { + // This is used to generate the "simplified" version for the toggle UI + if (options?.useSimplifiedNames) { + if (el.type.name && el.type.name !== '_default') { + return el.type.name; + } else if (typeof el.type === 'function') { + return 'No Display Name'; + } else if (isForwardRef(el.type)) { + return el.type.render.name; + } else if (isMemo(el.type)) { + return el.type.type.name; + } else if ( + typeof el.type === 'symbol' || + (el.type.$$typeof && typeof el.type.$$typeof === 'symbol') + ) { + return getReactSymbolName(el.type); + } else { + return el.type; + } + } + + // Default behavior: use displayName if available (preserves dot notation) if (el.type.displayName) { return el.type.displayName; } else if (getDocgenSection(el.type, 'displayName')) { @@ -236,6 +260,7 @@ export const jsxDecorator = ( context: StoryContext ) => { const jsx = useRef(undefined); + const simplifiedJsx = useRef(undefined); const story = storyFn(); const skip = skipJsxRender(context); @@ -254,10 +279,19 @@ export const jsxDecorator = ( const sourceJsx = mdxToJsx(storyJsx); - const rendered = renderJsx(sourceJsx, options); - if (rendered && jsx.current !== rendered) { - emitTransformCode(rendered, context); - jsx.current = rendered; + const renderedFull = renderJsx(sourceJsx, options); + const renderedSimplified = renderJsx(sourceJsx, { ...options, useSimplifiedNames: true }); + + if (renderedFull && jsx.current !== renderedFull) { + const hasCompoundComponents = renderedFull !== renderedSimplified; + + emitTransformCode( + renderedFull, + context, + hasCompoundComponents && renderedSimplified ? renderedSimplified : undefined + ); + jsx.current = renderedFull; + simplifiedJsx.current = renderedSimplified ?? undefined; } });