Skip to content
Open
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
50 changes: 49 additions & 1 deletion components/JsonEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ type Meta = {
valid?: boolean;
isSchema?: boolean;
indent?: boolean;
highlightLines?: string;
highlightLineStart?: number;
highlightLineEnd?: number;
};

type Path = number[];
Expand All @@ -60,6 +63,43 @@ type MultipathDecoration = {
syntaxPart: SyntaxPart;
};

function parseHighlightLines(meta: Meta | null): Set<number> {
const out = new Set<number>();
if (!meta) return out;

// Support start/end range
if (
typeof meta.highlightLineStart === 'number' &&
typeof meta.highlightLineEnd === 'number'
) {
const start = Math.min(meta.highlightLineStart, meta.highlightLineEnd);
const end = Math.max(meta.highlightLineStart, meta.highlightLineEnd);
for (let i = start; i <= end; i++) out.add(i);
return out;
}

// Support highlightLines string: "5" or "2-9" or "2-5,7,9-10"
const input = meta.highlightLines;
if (!input) return out;

for (const part of input
.split(',')
.map((p) => p.trim())
.filter(Boolean)) {
const m = part.match(/^(\d+)(?:-(\d+))?$/);
if (!m) continue;

const start = Number(m[1]);
const end = m[2] ? Number(m[2]) : start;

const lo = Math.min(start, end);
const hi = Math.max(start, end);

for (let i = lo; i <= hi; i++) out.add(i);
}

return out;
}
const META_REGEX = /^\s*\/\/ props (?<meta>{.*}).*\n/;

// Prevent annoying error messages because slate is not SSR ready
Expand Down Expand Up @@ -358,6 +398,10 @@ export default function JsonEditor({
return null;
}
}, [codeContent, isJsonMode]);
const highlightedLines = React.useMemo(
() => parseHighlightLines(meta),
[meta],
);

const parsedCode: null | any = React.useMemo(() => {
try {
Expand Down Expand Up @@ -698,9 +742,13 @@ export default function JsonEditor({
const line = path[0] + 1;
/* istanbul ignore else : no else block to test */
if (element.type === 'paragraph') {
const isHighlighted = highlightedLines.has(line);
return (
<span
className='relative flex flex-row first:pt-4 last:pb-4 '
className={cn(
'relative flex flex-row first:pt-4 last:pb-4',
isHighlighted && 'bg-white/10',
)}
{...attributes}
>
<span
Expand Down
71 changes: 71 additions & 0 deletions cypress/components/JsonEditor.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,77 @@ describe('JSON Editor Component', () => {
});
});

it('should highlight a single line when highlightLines is provided', () => {
const code = `// props { "highlightLines": "2" }
{
"a": 1,
"b": 2
}`;

cy.mount(<JsonEditor initialCode={code} />);

// line 2 is the `"a": 1,` line
cy.get('[data-test="json-editor"]')
.contains('"a"')
.closest('span.relative')
.should('have.class', 'bg-white/10');

// line 3 should NOT be highlighted
cy.get('[data-test="json-editor"]')
.contains('"b"')
.closest('span.relative')
.should('not.have.class', 'bg-white/10');
});

it('should highlight a range of lines when highlightLines is a range', () => {
const code = `// props { "highlightLines": "1-2" }
{
"a": 1,
"b": 2
}`;

cy.mount(<JsonEditor initialCode={code} />);

// line 1 is "{"
cy.get('[data-test="json-editor"]')
.contains('{')
.closest('span.relative')
.should('have.class', 'bg-white/10');

// line 2 is `"a": 1,`
cy.get('[data-test="json-editor"]')
.contains('"a"')
.closest('span.relative')
.should('have.class', 'bg-white/10');
});
it('should highlight lines using highlightLineStart and highlightLineEnd', () => {
const code = `// props { "highlightLineStart": 1, "highlightLineEnd": 2 }
{
"a": 1,
"b": 2
}`;

cy.mount(<JsonEditor initialCode={code} />);

// line 1 "{"
cy.get('[data-test="json-editor"]')
.contains('{')
.closest('span.relative')
.should('have.class', 'bg-white/10');

// line 2 `"a": 1,`
cy.get('[data-test="json-editor"]')
.contains('"a"')
.closest('span.relative')
.should('have.class', 'bg-white/10');

// line 3 `"b": 2` should NOT be highlighted
cy.get('[data-test="json-editor"]')
.contains('"b"')
.closest('span.relative')
.should('not.have.class', 'bg-white/10');
});

// should render with meta regex props and check schema compliant
it('should render with meta regex props', () => {
const schemaCompliantMetaProps =
Expand Down
4 changes: 2 additions & 2 deletions pages/understanding-json-schema/structuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ The URI `https://example.com/schemas/address#/properties/street_address`
identifies the highlighted subschema in the following schema.

```json
// props { "isSchema": true }
// props { "isSchema": true, "highlightLines": "5" }
{
"$id": "https://example.com/schemas/address",
"type": "object",
Expand Down Expand Up @@ -253,7 +253,7 @@ The URI `https://example.com/schemas/address#street_address` identifies
the subschema on the highlighted part of the following schema.

```json
// props { "isSchema": true }
// props { "isSchema": true, "highlightLines": "5" }
{
"$id": "https://example.com/schemas/address",
"type": "object",
Expand Down
Loading