Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 12 additions & 4 deletions pages/code-view/with-line-numbers.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,33 @@

import { Button, SpaceBetween } from "@cloudscape-design/components";

import { CodeView } from "../../lib/components";
import { CodeView, CodeViewProps } from "../../lib/components";
import { ScreenshotArea } from "../screenshot-area";

const i18nStrings: CodeViewProps.I18nStrings = {
lineNumberLabel: `Line number`,
codeLabel: `Code`,
};

export default function CodeViewPage() {
return (
<ScreenshotArea>
<h1>Code View</h1>
<SpaceBetween direction="vertical" size="l">
<CodeView lineNumbers={true} content={`# Hello World`} />
<CodeView lineNumbers={true} content={`# Hello World\n\nThis is Cloudscape.`} />
<CodeView lineNumbers={true} i18nStrings={i18nStrings} content={`# Hello World`} />
<CodeView lineNumbers={true} i18nStrings={i18nStrings} content={`# Hello World\n\nThis is Cloudscape.`} />
<CodeView
lineNumbers={true}
i18nStrings={i18nStrings}
content={`# Hello World`}
actions={<Button ariaLabel="Copy code" iconName="copy"></Button>}
/>
{/* Wrapping should not be affected by the parent's word-break property. */}
<div style={{ wordBreak: "break-word" }}>
<CodeView
lineNumbers={true}
content={[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map((i) => `This is line number #${i + 1}.`).join("\n")}
i18nStrings={i18nStrings}
content={[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map((i) => `This is line number #${i}.`).join("\n")}
/>
</div>
</SpaceBetween>
Expand Down
25 changes: 25 additions & 0 deletions src/__tests__/__snapshots__/documenter.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,31 @@ exports[`definition for 'code-view' matches the snapshot 1`] = `
"optional": true,
"type": "((code: string) => React.ReactNode)",
},
{
"description": "An object containing all the necessary localized strings required by the component. The object should contain:

* \`lineNumberLabel\` - Label for the column that displays line numbers (when line numbers are visible)
* \`codeLabel\` - Label for the column that displays the code content (when line numbers are visible)",
"inlineType": {
"name": "CodeViewProps.I18nStrings",
"properties": [
{
"name": "codeLabel",
"optional": true,
"type": "string",
},
{
"name": "lineNumberLabel",
"optional": true,
"type": "string",
},
],
"type": "object",
},
"name": "i18nStrings",
"optional": true,
"type": "CodeViewProps.I18nStrings",
},
{
"description": "Controls the display of line numbers.

Expand Down
19 changes: 19 additions & 0 deletions src/code-view/__tests__/code-view.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,25 @@ describe("CodeView", () => {
expect(content.getElement()).toHaveTextContent("# Hello World This is a markdown example.");
});

test("correctly renders table markup with line numbers", () => {
render(
<CodeView
lineNumbers={true}
i18nStrings={{ lineNumberLabel: "Line number", codeLabel: "Code" }}
content={`# Hello World\n\nThis is a markdown example.`}
/>,
);
const wrapper = createWrapper()!.findCodeView()!;
const table = wrapper.find("table")!.getElement();
expect(table).not.toHaveAttribute("role");
const lineNumberColumn = wrapper.find("th:nth-child(1)")!.getElement();
expect(lineNumberColumn).toHaveTextContent("Line number");
const contentColumn = wrapper.find("th:nth-child(2)")!.getElement();
expect(contentColumn).toHaveTextContent("Code");
const lineNumberCell = wrapper.find("td:nth-child(1)")!.getElement();
expect(lineNumberCell).not.toHaveAttribute("aria-hidden", "true");
});

test("correctly renders copy button slot", () => {
render(<CodeView content={"Hello World"} actions={<button>Copy</button>}></CodeView>);
const wrapper = createWrapper()!.findCodeView();
Expand Down
16 changes: 15 additions & 1 deletion src/code-view/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,21 @@ export interface CodeViewProps {

/**
* A function to perform custom syntax highlighting.
*
*/
highlight?: (code: string) => React.ReactNode;

/**
* An object containing all the necessary localized strings required by the component. The object should contain:
*
* * `lineNumberLabel` - Label for the column that displays line numbers (when line numbers are visible)
* * `codeLabel` - Label for the column that displays the code content (when line numbers are visible)
*/
i18nStrings?: CodeViewProps.I18nStrings;
}

export namespace CodeViewProps {
export interface I18nStrings {
lineNumberLabel?: string;
codeLabel?: string;
}
}
17 changes: 15 additions & 2 deletions src/code-view/internal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export function InternalCodeView({
highlight,
ariaLabel,
ariaLabelledby,
i18nStrings,
__internalRootRef = null,
...props
}: InternalCodeViewProps) {
Expand All @@ -47,6 +48,7 @@ export function InternalCodeView({
const darkMode = useCurrentMode(containerRef) === "dark";

const regionProps = ariaLabel || ariaLabelledby ? { role: "region" } : {};
const accessibleLineNumbers = lineNumbers && i18nStrings?.lineNumberLabel && i18nStrings?.codeLabel;

// Create tokenized React nodes of the content.
const code = highlight ? highlight(content) : textHighlight(content);
Expand All @@ -66,7 +68,7 @@ export function InternalCodeView({
>
<div className={styles["scroll-container"]} ref={containerRef}>
<table
role="presentation"
role={!accessibleLineNumbers ? "presentation" : undefined}
className={clsx(
styles["code-table"],
actions && styles["code-table-with-actions"],
Expand All @@ -77,12 +79,23 @@ export function InternalCodeView({
<col style={{ width: 1 } /* shrink to fit content */} />
<col style={{ width: "auto" }} />
</colgroup>
{accessibleLineNumbers && (
<thead className={styles["screenreader-only"]}>
<tr>
{lineNumbers && <th>{i18nStrings.lineNumberLabel}</th>}
<th>{i18nStrings.codeLabel}</th>
</tr>
</thead>
)}
<tbody>
{Children.map(codeElement.props.children, (child, index) => {
return (
<tr key={index}>
{lineNumbers && (
<td className={clsx(styles["line-number"], styles.unselectable)} aria-hidden={true}>
<td
className={clsx(styles["line-number"], styles.unselectable)}
aria-hidden={!accessibleLineNumbers}
>
<Box variant="code" color="text-status-inactive" fontSize="body-m">
{index + 1}
</Box>
Expand Down
8 changes: 7 additions & 1 deletion src/code-view/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ $color-background-code-view-dark: #282c34;
border-start-end-radius: cs.$border-radius-tiles;
border-end-start-radius: cs.$border-radius-tiles;
border-end-end-radius: cs.$border-radius-tiles;

background-color: $color-background-code-view-light;
:global(.awsui-dark-mode) &,
:global(.awsui-polaris-dark-mode) & {
Expand Down Expand Up @@ -43,6 +43,12 @@ $color-background-code-view-dark: #282c34;
padding-inline-end: cs.$space-static-xxl;
}

.screenreader-only {
position: absolute;
inset-block-start: -9999px;
inset-inline-start: -9999px;
}

.line-number {
vertical-align: text-top;
white-space: nowrap;
Expand Down
Loading