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

fix: apostrophe-issue-richtext #1314

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
31 changes: 24 additions & 7 deletions playground/react/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
import { StoryblokComponent, useStoryblok } from '@storyblok/react';
import React from 'react';
import { BrowserRouter, Link, Route, Routes } from 'react-router';
import Home from './pages/Home';
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure we want to make our React playground dependent on react-router honestly, is this needed?

import RichtextPage from './pages/RichtextPage';

function App() {
const story = useStoryblok('home', { version: 'draft' });
return (
<BrowserRouter>
<div>
<nav>
<ul>
<li>
<Link to="/react">Home</Link>
</li>
<li>
<Link to="/react/test-richtext">Richtext</Link>
</li>
</ul>
</nav>

if (!story?.content) {
return <div>Loading...</div>;
}

return <StoryblokComponent blok={story.content} />;
<Routes>
<Route path="/" element={<Home />} />
<Route path="react" element={<Home />} />
<Route path="react/test-richtext" element={<RichtextPage />} />
</Routes>
</div>
</BrowserRouter>
);
}

export default App;
2 changes: 1 addition & 1 deletion playground/react/index.tsx
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ import { apiPlugin, storyblokInit } from '@storyblok/react';
import IFrameEmbed from './components/iframe-embed';

storyblokInit({
accessToken: 'd6IKUtAUDiKyAhpJtrLFcwtt',
accessToken: 'OurklwV5XsDJTIE1NJaD2wtt',
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we need to document which space is associated with these demo token, or it would be a mess to test them later

use: [apiPlugin],
components: {
'teaser': Teaser,
4 changes: 3 additions & 1 deletion playground/react/package.json
Original file line number Diff line number Diff line change
@@ -9,11 +9,13 @@
"dependencies": {
"@storyblok/react": "workspace:^",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"react-router": "^7.1.1"
},
"devDependencies": {
"@types/node": "^20.17.10",
"@types/react": "18.3.4",
"@vitejs/plugin-basic-ssl": "^1.2.0",
"vite": "^5.4.3"
}
}
19 changes: 19 additions & 0 deletions playground/react/pages/Home.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { StoryblokComponent, useStoryblok } from '@storyblok/react';
import React from 'react';

function Home() {
const story = useStoryblok('react', { version: 'draft' });

if (!story?.content) {
return <div>Loading...</div>;
}

return (
<div>
<h1>Home</h1>
<StoryblokComponent blok={story.content} />
</div>
);
}

export default Home;
16 changes: 16 additions & 0 deletions playground/react/pages/RichtextPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { StoryblokRichText, useStoryblok } from '@storyblok/react';
import React from 'react';

function RichtextPage() {
const story = useStoryblok('react/test-richtext', { version: 'draft' });

if (!story?.content) {
return <div>Loading...</div>;
}

return (
story.content.richText && <StoryblokRichText doc={story.content.richText} />
);
}

export default RichtextPage;
6 changes: 5 additions & 1 deletion playground/react/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'node:path';
import basicSsl from '@vitejs/plugin-basic-ssl';

export default defineConfig({
plugins: [react()],
plugins: [
react(),
basicSsl(),
],
resolve: {
alias: {
'@storyblok/react': resolve(__dirname, '../../src/index.ts'),
63 changes: 60 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 17 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import React from 'react';

function decodeHtmlEntities(text: string): string {
const textarea = document.createElement('textarea');
Copy link
Contributor

Choose a reason for hiding this comment

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

this would break in SSR because of the use of document on the server

textarea.innerHTML = text;
return textarea.value;
}

function camelCase(str: string) {
return str.replace(/-([a-z])/g, g => g[1].toUpperCase());
}
@@ -29,6 +35,10 @@ export function convertAttributesInElement(element: React.ReactElement | React.R

// Base case: if the element is not a React element, return it unchanged.
if (!React.isValidElement(element)) {
// If it's a text node, decode any HTML entities
if (typeof element === 'string') {
return decodeHtmlEntities(element) as unknown as React.ReactElement;
}
return element;
}

@@ -55,7 +65,13 @@ export function convertAttributesInElement(element: React.ReactElement | React.R
newProps.key = (element.key as string);

// Process children recursively.
const children = React.Children.map((element.props as React.PropsWithChildren).children, child => convertAttributesInElement(child as React.ReactElement));
const children = React.Children.map((element.props as React.PropsWithChildren).children, (child) => {
if (typeof child === 'string') {
return decodeHtmlEntities(child);
}
return convertAttributesInElement(child as React.ReactElement);
});

const newElement = React.createElement(element.type, newProps, children);
// Clone the element with the new properties and updated children.
return newElement;
Loading