Skip to content

Commit

Permalink
Merge pull request #24 from Eyas/direct-script
Browse files Browse the repository at this point in the history
Support using <script> tags directly.
  • Loading branch information
Eyas committed Jul 20, 2020
2 parents 38e3245 + a048a6c commit 796fa93
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 41 deletions.
33 changes: 33 additions & 0 deletions README.md
Expand Up @@ -50,6 +50,39 @@ export function GraceHopper() {
}
```

### Directly creating `<script>` tags (for `next/head` and elsewhere)

Certain `<head>` management libraries require `<script>` tags to be directly
included, rather than wrapped in a component. This includes NextJS's
`next/head`, and `react-helmet`. With these, we can use the `jsonLdScriptProps`
export to do the same thing:

```tsx
import { Person } from "schema-dts";
import { helmetJsonLdProp } from "react-schemaorg";
import Head from "next/head";

export default function MyPage() {
return (
<Head>
<script
{...jsonLdScriptProps<Person>({
"@context": "https://schema.org",
"@type": "Person",
name: "Grace Hopper",
alternateName: "Grace Brewster Murray Hopper",
alumniOf: {
"@type": "CollegeOrUniversity",
name: ["Yale University", "Vassar College"],
},
knowsAbout: ["Compilers", "Computer Science"],
})}
/>
</Head>
);
}
```

### [React Helmet](https://github.com/nfl/react-helmet) Usage

To set JSON-LD in React Helmet, you need to pass it to the `script={[...]}` prop
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Expand Up @@ -14,4 +14,4 @@
* limitations under the License.
*/

export { helmetJsonLdProp, JsonLd } from "./json-ld";
export { helmetJsonLdProp, JsonLd, jsonLdScriptProps } from "./json-ld";
44 changes: 32 additions & 12 deletions src/json-ld.tsx
Expand Up @@ -50,21 +50,41 @@ export class JsonLd<T extends Thing> extends React.Component<
}
> {
render() {
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(
this.props.item,
safeJsonLdReplacer,
this.props.space
),
}}
/>
);
return <script {...jsonLdScriptProps<T>(this.props.item, this.props)} />;
}
}

/**
* Produces necessary props for a JSX <script> tag that includes JSON-LD.
*
* Can be used by spreading the props into a <script> JSX tag:
*
* ```tsx
* <script {...jsonLdScriptProps<Person>({
* "@context": "https://schema.org",
* "@type": "Person",
* name: "Grace Hopper",
* alternateName: "Grace Brewster Murray Hopper",
* alumniOf: {
* "@type": "CollegeOrUniversity",
* name: ["Yale University", "Vassar College"]
* },
* knowsAbout: ["Compilers", "Computer Science"]
* })} />
* ```
*/
export function jsonLdScriptProps<T extends Thing>(
item: WithContext<T>,
options: JsonLdOptions = {}
): JSX.IntrinsicElements["script"] {
return {
type: "application/ld+json",
dangerouslySetInnerHTML: {
__html: JSON.stringify(item, safeJsonLdReplacer, options.space),
},
};
}

/**
* Produces a Helmet-style <script> prop for a given JSON-LD datum.
*
Expand Down
166 changes: 138 additions & 28 deletions test/jsonld_func_test.ts
@@ -1,47 +1,119 @@
import {Person} from 'schema-dts';
import { Person } from "schema-dts";

import {helmetJsonLdProp} from '../src';
import { helmetJsonLdProp, jsonLdScriptProps } from "../src";

test('works', () => {
expect(helmetJsonLdProp<Person>({
'@context': 'https://schema.org',
'@type': 'Person',
})).toMatchInlineSnapshot(`
test("works", () => {
expect(
helmetJsonLdProp<Person>({
"@context": "https://schema.org",
"@type": "Person",
})
).toMatchInlineSnapshot(`
Object {
"innerHTML": "{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Person\\"}",
"type": "application/ld+json",
}
`);

expect(
jsonLdScriptProps<Person>({
"@context": "https://schema.org",
"@type": "Person",
})
).toMatchInlineSnapshot(`
Object {
"dangerouslySetInnerHTML": Object {
"__html": "{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Person\\"}",
},
"type": "application/ld+json",
}
`);

expect(
jsonLdScriptProps<Person>(
{
"@context": "https://schema.org",
"@type": "Person",
},
/* options=*/ {}
)
).toMatchInlineSnapshot(`
Object {
"dangerouslySetInnerHTML": Object {
"__html": "{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Person\\"}",
},
"type": "application/ld+json",
}
`);

expect(
jsonLdScriptProps<Person>(
{
"@context": "https://schema.org",
"@type": "Person",
},
/* options=*/ { space: 2 }
)
).toMatchInlineSnapshot(`
Object {
"dangerouslySetInnerHTML": Object {
"__html": "{
\\"@context\\": \\"https://schema.org\\",
\\"@type\\": \\"Person\\"
}",
},
"type": "application/ld+json",
}
`);
});

test('escapes JSON-LD-illegal chars', () => {
expect(helmetJsonLdProp<Person>({
'@context': 'https://schema.org',
'@type': 'Person',
name: 'Foo</script>',
})).toMatchInlineSnapshot(`
test("escapes JSON-LD-illegal chars", () => {
expect(
helmetJsonLdProp<Person>({
"@context": "https://schema.org",
"@type": "Person",
name: "Foo</script>",
})
).toMatchInlineSnapshot(`
Object {
"innerHTML": "{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Person\\",\\"name\\":\\"Foo&lt;/script&gt;\\"}",
"type": "application/ld+json",
}
`);

expect(
jsonLdScriptProps<Person>({
"@context": "https://schema.org",
"@type": "Person",
name: "Foo</script>",
})
).toMatchInlineSnapshot(`
Object {
"dangerouslySetInnerHTML": Object {
"__html": "{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Person\\",\\"name\\":\\"Foo&lt;/script&gt;\\"}",
},
"type": "application/ld+json",
}
`);
});

test('escapes JSON-LD-illegal chars', () => {
expect(helmetJsonLdProp<Person>(
{
'@context': 'https://schema.org',
'@type': 'Person',
name: ['Foo</script>', null!, undefined!],
knows: [],
knowsAbout: {
'@type': 'CreativeWork',
name: 'Foo',
copyrightYear: 2020,
},
},
{space: 2}))
.toMatchInlineSnapshot(`
test("escapes JSON-LD-illegal chars", () => {
expect(
helmetJsonLdProp<Person>(
{
"@context": "https://schema.org",
"@type": "Person",
name: ["Foo</script>", null!, undefined!],
knows: [],
knowsAbout: {
"@type": "CreativeWork",
name: "Foo",
copyrightYear: 2020,
},
},
{ space: 2 }
)
).toMatchInlineSnapshot(`
Object {
"innerHTML": "{
\\"@context\\": \\"https://schema.org\\",
Expand All @@ -61,4 +133,42 @@ test('escapes JSON-LD-illegal chars', () => {
"type": "application/ld+json",
}
`);

expect(
jsonLdScriptProps<Person>(
{
"@context": "https://schema.org",
"@type": "Person",
name: ["Foo</script>", null!, undefined!],
knows: [],
knowsAbout: {
"@type": "CreativeWork",
name: "Foo",
copyrightYear: 2020,
},
},
{ space: 2 }
)
).toMatchInlineSnapshot(`
Object {
"dangerouslySetInnerHTML": Object {
"__html": "{
\\"@context\\": \\"https://schema.org\\",
\\"@type\\": \\"Person\\",
\\"name\\": [
\\"Foo&lt;/script&gt;\\",
null,
null
],
\\"knows\\": [],
\\"knowsAbout\\": {
\\"@type\\": \\"CreativeWork\\",
\\"name\\": \\"Foo\\",
\\"copyrightYear\\": 2020
}
}",
},
"type": "application/ld+json",
}
`);
});

0 comments on commit 796fa93

Please sign in to comment.