-
Notifications
You must be signed in to change notification settings - Fork 248
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
831 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
--- | ||
'@shopify/hydrogen-react': minor | ||
--- | ||
|
||
Add a RichText component to easily render \`rich_text_field\` metafields. Thank you @bastienrobert for the original implementation. Example usage: | ||
|
||
```tsx | ||
import {RichText} from '@shopify/hydrogen-react'; | ||
|
||
export function MainRichText({metaFieldData}: {metaFieldData: string}) { | ||
return ( | ||
<RichText | ||
data={metaFieldData} | ||
components={{ | ||
paragraph({node}) { | ||
return <p className="customClass">{node.children}</p>; | ||
}, | ||
}} | ||
/> | ||
); | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import {createElement, type ReactNode} from 'react'; | ||
|
||
export type CustomComponents = { | ||
/** The root node of the rich text. Defaults to `<div>` */ | ||
root?: typeof Root; | ||
/** Customize the headings. Each heading has a `level` property from 1-6. Defaults to `<h1>` to `<h6>` */ | ||
heading?: typeof Heading; | ||
/** Customize paragraphs. Defaults to `<p>` */ | ||
paragraph?: typeof Paragraph; | ||
/** Customize how text nodes. They can either be bold or italic. Defaults to `<em>`, `<strong>` or text. */ | ||
text?: typeof Text; | ||
/** Customize links. Defaults to a React Router `<Link>` component in Hydrogen and a `<a>` in Hydrogen React. */ | ||
link?: typeof RichTextLink; | ||
/** Customize lists. They can be either ordered or unordered. Defaults to `<ol>` or `<ul>` */ | ||
list?: typeof List; | ||
/** Customize list items. Defaults to `<li>`. */ | ||
listItem?: typeof ListItem; | ||
}; | ||
|
||
export const RichTextComponents = { | ||
root: Root, | ||
heading: Heading, | ||
paragraph: Paragraph, | ||
text: Text, | ||
link: RichTextLink, | ||
list: List, | ||
'list-item': ListItem, | ||
}; | ||
|
||
function Root({ | ||
node, | ||
}: { | ||
node: { | ||
type: 'root'; | ||
children?: ReactNode[]; | ||
}; | ||
}): ReactNode { | ||
return <div>{node.children}</div>; | ||
} | ||
|
||
function Heading({ | ||
node, | ||
}: { | ||
node: { | ||
type: 'heading'; | ||
level: number; | ||
children?: ReactNode[]; | ||
}; | ||
}): ReactNode { | ||
return createElement(`h${node.level ?? '1'}`, null, node.children); | ||
} | ||
|
||
function Paragraph({ | ||
node, | ||
}: { | ||
node: { | ||
type: 'paragraph'; | ||
children?: ReactNode[]; | ||
}; | ||
}): ReactNode { | ||
return <p>{node.children}</p>; | ||
} | ||
|
||
function Text({ | ||
node, | ||
}: { | ||
node: { | ||
type: 'text'; | ||
italic?: boolean; | ||
bold?: boolean; | ||
value?: string; | ||
}; | ||
}): ReactNode { | ||
if (node.bold && node.italic) | ||
return ( | ||
<em> | ||
<strong>{node.value}</strong> | ||
</em> | ||
); | ||
|
||
if (node.bold) return <strong>{node.value}</strong>; | ||
if (node.italic) return <em>{node.value}</em>; | ||
|
||
return node.value; | ||
} | ||
|
||
function RichTextLink({ | ||
node, | ||
}: { | ||
node: { | ||
type: 'link'; | ||
url: string; | ||
title?: string; | ||
target?: string; | ||
children?: ReactNode[]; | ||
}; | ||
}): ReactNode { | ||
return ( | ||
<a href={node.url} title={node.title} target={node.target}> | ||
{node.children} | ||
</a> | ||
); | ||
} | ||
|
||
function List({ | ||
node, | ||
}: { | ||
node: { | ||
type: 'list'; | ||
listType: 'unordered' | 'ordered'; | ||
children?: ReactNode[]; | ||
}; | ||
}): ReactNode { | ||
const List = node.listType === 'unordered' ? 'ul' : 'ol'; | ||
return <List>{node.children}</List>; | ||
} | ||
|
||
function ListItem({ | ||
node, | ||
}: { | ||
node: { | ||
type: 'list-item'; | ||
children?: ReactNode[]; | ||
}; | ||
}): ReactNode { | ||
return <li>{node.children}</li>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; | ||
|
||
const data: ReferenceEntityTemplateSchema = { | ||
name: 'RichText', | ||
category: 'components', | ||
isVisualComponent: false, | ||
related: [], | ||
description: `The \`RichText\` component renders a metafield of type \`rich_text_field\`. By default the rendered output uses semantic HTML tags. Customize how nodes are rendered with the \`components\` prop.`, | ||
type: 'component', | ||
defaultExample: { | ||
description: 'I am the default example', | ||
codeblock: { | ||
tabs: [ | ||
{ | ||
title: 'JavaScript', | ||
code: './RichText.example.jsx', | ||
language: 'jsx', | ||
}, | ||
{ | ||
title: 'TypeScript', | ||
code: './RichText.example.tsx', | ||
language: 'tsx', | ||
}, | ||
], | ||
title: 'Example code', | ||
}, | ||
}, | ||
definitions: [ | ||
{ | ||
title: 'Props', | ||
type: 'RichTextPropsForDocs', | ||
description: '', | ||
}, | ||
], | ||
}; | ||
|
||
export default data; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import {RichText} from '@shopify/hydrogen-react'; | ||
|
||
export function MainRichText({metaFieldData}) { | ||
return ( | ||
<RichText | ||
data={metaFieldData} | ||
components={{ | ||
paragraph({node}) { | ||
return <p className="customClass">{node.children}</p>; | ||
}, | ||
}} | ||
/> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import {RichText} from '@shopify/hydrogen-react'; | ||
|
||
export function MainRichText({metaFieldData}: {metaFieldData: string}) { | ||
return ( | ||
<RichText | ||
data={metaFieldData} | ||
components={{ | ||
paragraph({node}) { | ||
return <p className="customClass">{node.children}</p>; | ||
}, | ||
}} | ||
/> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import * as React from 'react'; | ||
import type {Story} from '@ladle/react'; | ||
import {RichText} from './RichText.js'; | ||
import {RICH_TEXT_CONTENT} from './RichText.test.helpers.js'; | ||
|
||
type RichTextProps = React.ComponentProps<typeof RichText>; | ||
|
||
const Template: Story<RichTextProps> = (props: RichTextProps) => { | ||
return <RichText {...props}>Add to cart</RichText>; | ||
}; | ||
|
||
export const Default = Template.bind({}); | ||
Default.args = { | ||
as: 'div', | ||
data: JSON.stringify(RICH_TEXT_CONTENT), | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import {type RichTextASTNode} from './RichText.types.js'; | ||
|
||
export const RICH_TEXT_HEADING_1: RichTextASTNode = { | ||
type: 'heading', | ||
children: [ | ||
{ | ||
type: 'text', | ||
value: 'Heading 1', | ||
}, | ||
], | ||
level: 1, | ||
}; | ||
|
||
export const RICH_TEXT_HEADING_2: RichTextASTNode = { | ||
type: 'heading', | ||
level: 2, | ||
children: [ | ||
{ | ||
type: 'text', | ||
value: 'Heading 2', | ||
}, | ||
], | ||
}; | ||
|
||
export const RICH_TEXT_PARAGRAPH: RichTextASTNode = { | ||
type: 'paragraph', | ||
children: [ | ||
{ | ||
type: 'text', | ||
value: 'Paragraph', | ||
}, | ||
], | ||
}; | ||
|
||
export const RICH_TEXT_COMPLEX_PARAGRAPH: RichTextASTNode = { | ||
type: 'paragraph', | ||
children: [ | ||
{ | ||
type: 'text', | ||
value: 'This', | ||
italic: true, | ||
}, | ||
{ | ||
type: 'text', | ||
value: ' is a ', | ||
}, | ||
{ | ||
type: 'text', | ||
value: 'text', | ||
bold: true, | ||
}, | ||
{ | ||
type: 'text', | ||
value: ' and a ', | ||
}, | ||
{ | ||
url: '/products/foo', | ||
title: 'title', | ||
target: '_blank', | ||
type: 'link', | ||
children: [ | ||
{ | ||
type: 'text', | ||
value: 'link', | ||
}, | ||
], | ||
}, | ||
{ | ||
type: 'text', | ||
value: ' and an ', | ||
}, | ||
{ | ||
url: 'https://shopify.com', | ||
title: 'Title', | ||
target: '_blank', | ||
type: 'link', | ||
children: [ | ||
{ | ||
type: 'text', | ||
value: 'external link', | ||
}, | ||
], | ||
}, | ||
{ | ||
type: 'text', | ||
value: '', | ||
}, | ||
], | ||
}; | ||
|
||
export const RICH_TEXT_ORDERED_LIST: RichTextASTNode = { | ||
listType: 'ordered', | ||
type: 'list', | ||
children: [ | ||
{ | ||
type: 'list-item', | ||
children: [ | ||
{ | ||
type: 'text', | ||
value: 'One', | ||
}, | ||
], | ||
}, | ||
{ | ||
type: 'list-item', | ||
children: [ | ||
{ | ||
type: 'text', | ||
value: 'Two', | ||
}, | ||
], | ||
}, | ||
], | ||
}; | ||
|
||
export const RICH_TEXT_UNORDERED_LIST: RichTextASTNode = { | ||
listType: 'unordered', | ||
type: 'list', | ||
children: [ | ||
{ | ||
type: 'list-item', | ||
children: [ | ||
{ | ||
type: 'text', | ||
value: 'One', | ||
}, | ||
], | ||
}, | ||
{ | ||
type: 'list-item', | ||
children: [ | ||
{ | ||
type: 'text', | ||
value: 'Two', | ||
}, | ||
], | ||
}, | ||
], | ||
}; | ||
|
||
export const RICH_TEXT_CONTENT: RichTextASTNode = { | ||
type: 'root', | ||
children: [ | ||
RICH_TEXT_HEADING_1, | ||
RICH_TEXT_HEADING_2, | ||
RICH_TEXT_PARAGRAPH, | ||
RICH_TEXT_COMPLEX_PARAGRAPH, | ||
RICH_TEXT_ORDERED_LIST, | ||
RICH_TEXT_UNORDERED_LIST, | ||
], | ||
}; |
Oops, something went wrong.