Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add React Component Example To Docs #31

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
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
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
15 changes: 11 additions & 4 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
{
"parser": "babel-eslint",
"extends": ["plugin:react/recommended", "prettier", "prettier/react"],
"plugins": ["react", "prettier"],
"env": {
"browser": true,
"es2021": true,
"node": true
"es6": true,
"node": true,
"es2021": true
},
"settings": {
"react": {
"version": "detect"
}
},
"extends": ["prettier"],
"plugins": ["prettier"],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

.vscode/settings.json
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

Expand Down
149 changes: 135 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,26 +103,147 @@ convertSchemaToHtml(richTextResponse, options)
<li class="text-sm md:text-base">oranges</li>
<li class="text-sm md:text-base">bananas</li>
</ol>
...
```

React/Hydrogen example:
React/Hydrogen Component Example

```javascript
import { convertSchemaToHtml } from '@thebeyondgroup/shopify-rich-text-renderer'
import React, { useEffect, useState } from 'react'

// Default options for the HTML conversion, using Tailwind CSS classes
const defaultOptions = {
scoped: false,
newLineToBreak: false, // convert new line character to <br/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a breaking change or a fix to align the docs with the real code?

classes: {
p: 'mt-3 text-lg', // paragraph classes
h1: 'mb-4 text-2xl md:text-4xl', // heading1 classes
h2: 'mb-4 text-xl md:text-3xl', // heading2 classes
h3: 'mb-3 text-lg md:text-2xl', // heading3 classes
h4: 'mb-3 text-base md:text-lg', // heading4 classes
h5: 'mb-2.5 text-sm md:text-base', // heading5 classes
h6: 'mb-2 text-xs md:text-sm', // heading6 classes
ol: 'my-3 ml-3 flex flex-col gap-y-2', // order list classes
ul: 'my-3 ml-3 flex flex-col gap-y-2', // unordered list classes
li: 'text-sm md:text-base', // list item classes
a: 'underline text-blue-500 hover:text-blue-700', // anchor/link classes
strong: 'font-medium', // bold/strong classes
em: 'font-italic', // italic/em classes
},
}

// schema for demonstration
const schema = {
type: 'root',
children: [
{
type: 'heading',
level: 1,
children: [{ type: 'text', value: 'Heading 1' }],
},
{
type: 'paragraph',
children: [{ type: 'text', value: 'This is a paragraph.' }],
},
{
type: 'list',
listType: 'unordered',
children: [
{ type: 'list-item', children: [{ type: 'text', value: 'Item 1' }] },
{ type: 'list-item', children: [{ type: 'text', value: 'Item 2' }] },
],
},
{
type: 'link',
url: 'https://www.example.com',
children: [{ type: 'text', value: 'Example Link' }],
},
],
}

// Fetch schema function
const fetchSchema = async url => {
const res = await fetch(url) // Replace with your API endpoint
return await res.json()
}

// React component that converts a schema to HTML, has default classes that can be overwritten
// if no schema exists the user csn pass thr spi route as prop
export default function RichTextToHTML({ schema, options, apiRoute }) {
const [currentSchema, setCurrentSchema] = useState(schema || null)

useEffect(async () => {
if (!currentSchema && apiRoute) {
const richTextSchema = await fetchSchema(api)
setCurrentSchema(richTextSchema)
}
}, [currentSchema])

//options passed via props override default options (classes,scoped, newLineToBreak)
const combinedOptions = {
...defaultOptions,
...options,
}

const html = currentSchema ? convertSchemaToHtml(currentSchema, combinedOptions) : ''
return (
<>
<div className="html" dangerouslySetInnerHTML={{ __html: html }} />
</>
)
}
```

Example of the react RichTextToHTML component in use

```javascript
export default RenderedHTML(){
const richTextResponse = await getRichTextFromShopify()
// App.jsx
import React from 'react'
import RichTextToHTML from './RichTextToHTML'

// Custom schema for/mock rich text metaobject get request
const schema = {
type: 'root',
children: [
{
type: 'heading',
level: 2,
children: [{ type: 'text', value: 'Custom Heading 2' }],
},
{
type: 'paragraph',
children: [{ type: 'text', value: 'This is a custom paragraph.' }],
},
],
}

// Custom options for demonstration of overriding defaults.
// Note: you probably wouldn't need to set the scoped class name & apply unique classes per element
const customOptions = {
scoped: 'custom-scope',
classes: {
p: 'text-lg text-slate-800 my-2',
h2: 'text-2xl md:text-4xl font-semibold leading-none tracking-wide mb-2',
},
newLineToBreak: true,
}

// Main React App Component
const App = () => {
return (
<>
<div
className="html"
dangerouslySetInnerHTML={{
__html: convertSchemaToHtml(richTextResponse),
}}
/>
<div>
</>
)
<div className="container flex flex-col gap-4 mx-auto p-4">
<h1 className="text-3xl font-bold mb-6">Shopify Storefront</h1>
<RichTextToHTML schema={schema} options={customOptions} />
{/* Example without being passed a schema to trigger a fetch request from shopify. **Passing the api route doesn't really make sense & is for demonstration purposes only */}
<RichTextToHTML
options={customOptions}
apiRoute="/api/metaobjects/richtext/user-profile?api-key=tH3BeY0ndGr0vp"
/>
</div>
)
}

export default App
```

Here is a [JSFiddle Demo](https://jsfiddle.net/r2d4wsna/) that shows a working example.
25 changes: 20 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
{
"name": "@thebeyondgroup/shopify-rich-text-renderer",
"version": "2.0.3",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this just to bump the docs on npm?

"description": "Convert Shopify's rich text field from a rich text schema to HTML.",
"description": "Convert Shopify's Metafields/Metaobjects rich text type from a rich text AST/schema to HTML. Works great with hydrogen/headless & regular storefronts.",
"main": "src/index.js",
"repository": "https://github.com/TheBeyondGroup/shopify-rich-text-renderer.git",
"author": "Sean ",
"author": {
"name": "Sean McQuaid",
"email": "[email protected]",
"github": "mcqua007"
},
"license": "MIT",
"private": false,
"type": "module",
Expand All @@ -20,20 +24,23 @@
"lint:fix": "npx eslint --fix src/index.js"
},
"devDependencies": {
"@babel/preset-react": "^7.24.7",
"eslint": "^8.37.0",
"eslint-config-prettier": "^8.8.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-prettier": "^5.2.1",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"jsdoc-to-markdown": "^8.0.0",
"prettier": "^2.8.7",
"prettier": "^3.3.3",
"rollup": "^3.20.2",
"rollup-plugin-filesize": "^10.0.0",
"rollup-plugin-terser": "^7.0.2"
},
"keywords": [
"AST",
"convert",
"CSS",
"deserialize",
"deserializer",
"html serializer",
Expand All @@ -44,10 +51,18 @@
"Metaobject",
"rich text to html",
"rich text",
"react",
"hydrogen",
"renderer",
"serialize",
"serializer",
"schema",
"shopify",
"typescript",
"tailwind",
"typings",
"type definitions",
"utility",
"utilities"
],
"files": [
Expand Down
22 changes: 16 additions & 6 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
/* Shopify Metafield & Metaobject Rich Text Editor Schema to HTML Converter */

/**
* Converts a Shopify Richtext schema object to HTML.
*
* @param {Object|string} schema - The schema object or JSON string to convert.
* @param {Object} [options={}] - The conversion options.
* @param {string|boolean} [options.scoped] - The scoped class name or a boolean value indicating whether to use the default scoped class name.
* @returns {string} The converted HTML string.
*/
export function convertSchemaToHtml(schema, options = {}) {
let { scoped } = options
let html = ''
Expand Down Expand Up @@ -73,29 +83,29 @@ function createElement(tag, classes, content, attributes = {}) {
return `<${tag}${outputAttributes(attributes)}>${content}</${tag}>`
}

export function buildParagraph(el, options) {
function buildParagraph(el, options) {
const { classes } = options
return createElement('p', classes, convertSchemaToHtml(el?.children, options))
}

export function buildHeading(el, options) {
function buildHeading(el, options) {
const { classes } = options
const tag = `h${el?.level}`
return createElement(tag, classes, convertSchemaToHtml(el?.children, options))
}

export function buildList(el, options) {
function buildList(el, options) {
const { classes } = options
const tag = el?.listType === 'ordered' ? 'ol' : 'ul'
return createElement(tag, classes, convertSchemaToHtml(el?.children, options))
}

export function buildListItem(el, options) {
function buildListItem(el, options) {
const { classes } = options
return createElement('li', classes, convertSchemaToHtml(el?.children, options))
}

export function buildLink(el, options) {
function buildLink(el, options) {
const { classes } = options
const attributes = {
href: el?.url,
Expand All @@ -105,7 +115,7 @@ export function buildLink(el, options) {
return createElement('a', classes, convertSchemaToHtml(el?.children, options), attributes)
}

export function buildText(el, options) {
function buildText(el, options) {
const { classes, newLineToBreak } = options
if (el?.bold) {
return createElement('strong', classes, el?.value)
Expand Down
Loading
Loading