mirror of
https://github.com/glitch-soc/mastodon.git
synced 2025-12-27 21:26:41 +00:00
[Glitch] Adds new HTMLBlock component
Port e07b9dfdc1 to glitch-soc
Signed-off-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||
import { expect } from 'storybook/test';
|
||||
|
||||
import { HTMLBlock } from './index';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/HTMLBlock',
|
||||
component: HTMLBlock,
|
||||
args: {
|
||||
contents:
|
||||
'<p>Hello, world!</p>\n<p><a href="#">A link</a></p>\n<p>This should be filtered out: <button>Bye!</button></p>',
|
||||
},
|
||||
render(args) {
|
||||
return (
|
||||
// Just for visual clarity in Storybook.
|
||||
<div
|
||||
style={{
|
||||
border: '1px solid black',
|
||||
padding: '1rem',
|
||||
minWidth: '300px',
|
||||
}}
|
||||
>
|
||||
<HTMLBlock {...args} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
} satisfies Meta<typeof HTMLBlock>;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {
|
||||
async play({ canvas }) {
|
||||
const link = canvas.queryByRole('link');
|
||||
await expect(link).toBeInTheDocument();
|
||||
const button = canvas.queryByRole('button');
|
||||
await expect(button).not.toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
import type { FC, ReactNode } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { cleanExtraEmojis } from '@/flavours/glitch/features/emoji/normalize';
|
||||
import type { CustomEmojiMapArg } from '@/flavours/glitch/features/emoji/types';
|
||||
import { createLimitedCache } from '@/flavours/glitch/utils/cache';
|
||||
|
||||
import { htmlStringToComponents } from '../../utils/html';
|
||||
|
||||
// Use a module-level cache to avoid re-rendering the same HTML multiple times.
|
||||
const cache = createLimitedCache<ReactNode>({ maxSize: 1000 });
|
||||
|
||||
interface HTMLBlockProps {
|
||||
contents: string;
|
||||
extraEmojis?: CustomEmojiMapArg;
|
||||
}
|
||||
|
||||
export const HTMLBlock: FC<HTMLBlockProps> = ({
|
||||
contents: raw,
|
||||
extraEmojis,
|
||||
}) => {
|
||||
const customEmojis = useMemo(
|
||||
() => cleanExtraEmojis(extraEmojis),
|
||||
[extraEmojis],
|
||||
);
|
||||
const contents = useMemo(() => {
|
||||
const key = JSON.stringify({ raw, customEmojis });
|
||||
if (cache.has(key)) {
|
||||
return cache.get(key);
|
||||
}
|
||||
|
||||
const rendered = htmlStringToComponents(raw, {
|
||||
onText,
|
||||
extraArgs: { customEmojis },
|
||||
});
|
||||
|
||||
cache.set(key, rendered);
|
||||
return rendered;
|
||||
}, [raw, customEmojis]);
|
||||
|
||||
return contents;
|
||||
};
|
||||
|
||||
function onText(
|
||||
text: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- Doesn't do anything, just showing how typing would work.
|
||||
{ customEmojis }: { customEmojis: CustomEmojiMapArg | null },
|
||||
) {
|
||||
return text;
|
||||
}
|
||||
Reference in New Issue
Block a user