diff --git a/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.spec.ts b/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.spec.ts index f2e9b4614b4..45fde2b96b5 100644 --- a/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.spec.ts +++ b/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.spec.ts @@ -109,7 +109,10 @@ describe('DocViewer', () => { expect(exampleViewer).not.toBeNull(); expect(exampleViewer.componentInstance.view()).toBe(CodeExampleViewMode.SNIPPET); - const checkIcon = fixture.debugElement.query(By.directive(IconComponent)); + const copySourceCodeButton = fixture.debugElement.query(By.directive(CopySourceCodeButton)); + expect(copySourceCodeButton).not.toBeNull(); + + const checkIcon = copySourceCodeButton.query(By.directive(IconComponent)); expect((checkIcon.nativeElement as HTMLElement).classList).toContain( `material-symbols-outlined`, ); diff --git a/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.ts b/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.ts index d11cd1e7cb4..956ce0e9352 100644 --- a/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.ts +++ b/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.ts @@ -188,6 +188,7 @@ export class DocViewer { path: string, ): Promise { const preview = Boolean(placeholder.getAttribute('preview')); + const hideCode = Boolean(placeholder.getAttribute('hideCode')); const title = placeholder.getAttribute('header') ?? undefined; const firstCodeSnippetTitle = snippets.length > 0 ? (snippets[0].title ?? snippets[0].name) : undefined; @@ -199,6 +200,7 @@ export class DocViewer { path, files: snippets, preview, + hideCode, id: this.countOfExamples, }); diff --git a/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.html b/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.html index 9645ec2a766..76de5e30765 100644 --- a/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.html +++ b/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.html @@ -1,18 +1,35 @@
- @if (view() === CodeExampleViewMode.SNIPPET) { - {{ exampleMetadata()?.title }} - } + @if (showCode()) { + @if (view() === CodeExampleViewMode.SNIPPET) { + {{ exampleMetadata()?.title }} + } - @if (view() === CodeExampleViewMode.MULTI_FILE) { - - @for (tab of tabs(); track tab) { - - } - + @if (view() === CodeExampleViewMode.MULTI_FILE) { + + @for (tab of tabs(); track tab) { + + } + + } + } @else { + + }
+
-
- - @if (snippetCode()?.sanitizedContent; as content) { -
- } -
+ @if (showCode()) { +
+ + @if (snippetCode()?.sanitizedContent; as content) { +
+ } +
+ } @if (exampleComponent) {
diff --git a/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.scss b/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.scss index f5054b4c819..00725167c39 100644 --- a/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.scss +++ b/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.scss @@ -57,25 +57,25 @@ display: flex; gap: 0.75rem; - svg { - fill: var(--gray-400); - } - } + a, + button { + padding: 0; + margin: 0; + cursor: pointer; + height: 24px; + width: 24px; + color: var(--gray-400); - a, - button { - padding: 0; - margin: 0; - cursor: pointer; - height: 24px; - width: 24px; - path { - transition: fill 0.3s ease; - } + path { + transition: fill 0.3s ease; + } - &:hover { svg { - fill: var(--tertiary-contrast); + fill: currentColor; + } + + &:hover { + color: var(--tertiary-contrast); } } } diff --git a/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.spec.ts b/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.spec.ts index 59c0fd2d265..873d37e6fc9 100644 --- a/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.spec.ts +++ b/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.spec.ts @@ -272,6 +272,50 @@ describe('ExampleViewer', () => { button.click(); expect(spy.calls.argsFor(0)[0].trim()).toBe(`${window.location.href}#example-1`); }); + + it('should hide code content when `hideCode` is true', async () => { + componentRef.setInput( + 'metadata', + getMetadata({ + hideCode: true, + }), + ); + + await component.renderExample(); + fixture.detectChanges(); + + // Initially, the code should be hidden. + expect(component.showCode()).toBeFalse(); + let codeContainer = fixture.debugElement.query(By.css('.docs-example-viewer-code-wrapper')); + expect(codeContainer).toBeNull(); + }); + + it('should expand/collapse code content with toggle button.', async () => { + componentRef.setInput('metadata', getMetadata()); + + await component.renderExample(); + fixture.detectChanges(); + + // Initially, the code should be visible. + expect(component.showCode()).toBeTrue(); + let codeContainer = fixture.debugElement.query(By.css('.docs-example-viewer-code-wrapper')); + expect(codeContainer).not.toBeNull(); + + const codeToggleButton = fixture.debugElement.query(By.css('.docs-example-code-toggle')); + codeToggleButton.nativeElement.click(); + fixture.detectChanges(); + + expect(component.showCode()).toBeFalse(); + codeContainer = fixture.debugElement.query(By.css('.docs-example-viewer-code-wrapper')); + expect(codeContainer).toBeNull(); + + codeToggleButton.nativeElement.click(); + fixture.detectChanges(); + + expect(component.showCode()).toBeTrue(); + codeContainer = fixture.debugElement.query(By.css('.docs-example-viewer-code-wrapper')); + expect(codeContainer).not.toBeNull(); + }); }); const getMetadata = (value: Partial = {}): ExampleMetadata => { @@ -282,6 +326,7 @@ const getMetadata = (value: Partial = {}): ExampleMetadata => { {name: 'example.css', sanitizedContent: ''}, ], preview: false, + hideCode: false, ...value, }; }; diff --git a/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.ts b/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.ts index 690c4360627..9a748bf0ebd 100644 --- a/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.ts +++ b/adev/shared-docs/components/viewers/example-viewer/example-viewer.component.ts @@ -25,6 +25,7 @@ import {CommonModule, DOCUMENT} from '@angular/common'; import {MatTabGroup, MatTabsModule} from '@angular/material/tabs'; import {Clipboard} from '@angular/cdk/clipboard'; import {CopySourceCodeButton} from '../../copy-source-code-button/copy-source-code-button.component'; +import {IconComponent} from '../../icon/icon.component'; import {ExampleMetadata, Snippet} from '../../../interfaces/index'; import {EXAMPLE_VIEWER_CONTENT_LOADER} from '../../../providers/index'; import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; @@ -42,7 +43,7 @@ export const HIDDEN_CLASS_NAME = 'hidden'; @Component({ selector: 'docs-example-viewer', - imports: [CommonModule, CopySourceCodeButton, MatTabsModule, MatTooltipModule], + imports: [CommonModule, CopySourceCodeButton, MatTabsModule, MatTooltipModule, IconComponent], templateUrl: './example-viewer.component.html', styleUrls: ['./example-viewer.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, @@ -75,6 +76,7 @@ export class ExampleViewer { readonly expandable = signal(false); readonly expanded = signal(false); readonly snippetCode = signal(undefined); + readonly showCode = signal(true); readonly tabs = computed(() => this.exampleMetadata()?.files.map((file) => ({ name: @@ -98,6 +100,10 @@ export class ExampleViewer { this.snippetCode.set(this.exampleMetadata()?.files[0]); + if (this.exampleMetadata()?.hideCode) { + this.showCode.set(false); + } + afterNextRender( () => { // Several function below query the DOM directly, we need to wait until the DOM is rendered. diff --git a/adev/shared-docs/interfaces/code-example.ts b/adev/shared-docs/interfaces/code-example.ts index 4c521a8267a..1643a205faf 100644 --- a/adev/shared-docs/interfaces/code-example.ts +++ b/adev/shared-docs/interfaces/code-example.ts @@ -38,4 +38,6 @@ export interface ExampleMetadata { files: Snippet[]; /** True when ExampleViewer should have preview */ preview: boolean; + /** Whether to hide code example by default. */ + hideCode: boolean; } diff --git a/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/docs-code-multifile.mts b/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/docs-code-multifile.mts index 960e0e86081..2e0df55a6a9 100644 --- a/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/docs-code-multifile.mts +++ b/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/docs-code-multifile.mts @@ -20,6 +20,8 @@ export interface DocsCodeMultifileToken extends Tokens.Generic { paneTokens: Token[]; // True if we should display preview preview: boolean; + /** Whether to hide code example by default. */ + hideCode: boolean; } // Capture group 1: all attributes on the opening tag @@ -28,6 +30,7 @@ const multiFileCodeRule = /^\s*(.*?)<\/docs-code-multi const pathRule = /path="([^"]*)"/; const previewRule = /preview/; +const hideCodeRule = /hideCode/; export const docsCodeMultifileExtension = { name: 'docs-code-multifile', @@ -42,6 +45,7 @@ export const docsCodeMultifileExtension = { const attr = match[1].trim(); const path = pathRule.exec(attr); const preview = previewRule.exec(attr) ? true : false; + const hideCode = hideCodeRule.exec(attr) ? true : false; const token: DocsCodeMultifileToken = { type: 'docs-code-multifile', @@ -50,6 +54,7 @@ export const docsCodeMultifileExtension = { panes: match[2].trim(), paneTokens: [], preview: preview, + hideCode, }; this.lexer.blockTokens(token.panes, token.paneTokens); return token; @@ -69,6 +74,9 @@ export const docsCodeMultifileExtension = { if (token.preview) { el.setAttribute('preview', `${token.preview}`); } + if (token.hideCode) { + el.setAttribute('hideCode', 'true'); + } return el.outerHTML; }, diff --git a/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/docs-code.mts b/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/docs-code.mts index b8d32b9d354..4d3ff8beb8f 100644 --- a/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/docs-code.mts +++ b/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/docs-code.mts @@ -32,6 +32,7 @@ const languageRule = /language="([^"]*)"/; const visibleLinesRule = /visibleLines="([^"]*)"/; const visibleRegionRule = /visibleRegion="([^"]*)"/; const previewRule = /preview/; +const hideCodeRule = /hideCode/; export const docsCodeExtension = { name: 'docs-code', @@ -53,6 +54,7 @@ export const docsCodeExtension = { const visibleLines = visibleLinesRule.exec(attr); const visibleRegion = visibleRegionRule.exec(attr); const preview = previewRule.exec(attr) ? true : false; + const hideCode = hideCodeRule.exec(attr) ? true : false; const classes = classRule.exec(attr); let code = match[2]?.trim() ?? ''; @@ -76,6 +78,7 @@ export const docsCodeExtension = { visibleLines: visibleLines?.[1], visibleRegion: visibleRegion?.[1], preview: preview, + hideCode, classes: classes?.[1]?.split(' '), }; return token; diff --git a/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/format/index.mts b/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/format/index.mts index 1eb2e6d1c77..95bafb19111 100644 --- a/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/format/index.mts +++ b/adev/shared-docs/pipeline/shared/marked/extensions/docs-code/format/index.mts @@ -36,6 +36,8 @@ export interface CodeToken extends Tokens.Generic { visibleRegion?: string; /* Whether we should display preview */ preview?: boolean; + /** Whether to hide code example by default. */ + hideCode?: boolean; /* The lines to display highlighting on */ highlight?: string; @@ -121,6 +123,9 @@ function applyContainerAttributesAndClasses(el: Element, token: CodeToken) { if (token.preview) { el.setAttribute('preview', 'true'); } + if (token.hideCode) { + el.setAttribute('hideCode', 'true'); + } if (token.language === 'mermaid') { el.setAttribute('mermaid', 'true'); } diff --git a/adev/src/content/kitchen-sink.md b/adev/src/content/kitchen-sink.md index 317e2b75f3a..fb2f194eb9e 100644 --- a/adev/src/content/kitchen-sink.md +++ b/adev/src/content/kitchen-sink.md @@ -175,6 +175,7 @@ We also have styling for the terminal, just set the language as `shell`: | `visibleLines` | `string of number[]` | range of lines for collapse mode | | `visibleRegion` | `string` | **DEPRECATED** FOR `visibleLines` | | `preview` | `boolean` | (False) display preview | +| `hideCode` | `boolean` | (False) Whether to collapse code example by default. | ### Multifile examples @@ -198,11 +199,12 @@ You can create multifile examples by wrapping the examples inside a `` Attributes -| Attributes | Type | Details | -|:--- |:--- |:--- | -| body contents | `string` | nested tabs of `docs-code` examples | -| `path` | `string` | Path to code example for preview and external link | -| `preview` | `boolean` | (False) display preview | +| Attributes | Type | Details | +|:--- |:--- |:--- | +| body contents | `string` | nested tabs of `docs-code` examples | +| `path` | `string` | Path to code example for preview and external link | +| `preview` | `boolean` | (False) display preview | +| `hideCode` | `boolean` | (False) Whether to collapse code example by default. | ### Adding `preview` to your code example