mirror of
https://github.com/mastodon/mastodon.git
synced 2025-10-06 17:12:44 +00:00
adds element handler prop with new props arg
This commit is contained in:
parent
f046106612
commit
5a9d9d1132
|
@ -4,6 +4,7 @@ import classNames from 'classnames';
|
||||||
|
|
||||||
import type { CustomEmojiMapArg } from '@/mastodon/features/emoji/types';
|
import type { CustomEmojiMapArg } from '@/mastodon/features/emoji/types';
|
||||||
import { isModernEmojiEnabled } from '@/mastodon/utils/environment';
|
import { isModernEmojiEnabled } from '@/mastodon/utils/environment';
|
||||||
|
import type { OnElementHandler } from '@/mastodon/utils/html';
|
||||||
import { htmlStringToComponents } from '@/mastodon/utils/html';
|
import { htmlStringToComponents } from '@/mastodon/utils/html';
|
||||||
import { polymorphicForwardRef } from '@/types/polymorphic';
|
import { polymorphicForwardRef } from '@/types/polymorphic';
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ interface EmojiHTMLProps {
|
||||||
htmlString: string;
|
htmlString: string;
|
||||||
extraEmojis?: CustomEmojiMapArg;
|
extraEmojis?: CustomEmojiMapArg;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
onElement?: OnElementHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ModernEmojiHTML = polymorphicForwardRef<'div', EmojiHTMLProps>(
|
export const ModernEmojiHTML = polymorphicForwardRef<'div', EmojiHTMLProps>(
|
||||||
|
@ -23,13 +25,15 @@ export const ModernEmojiHTML = polymorphicForwardRef<'div', EmojiHTMLProps>(
|
||||||
htmlString,
|
htmlString,
|
||||||
as: asProp = 'div', // Rename for syntax highlighting
|
as: asProp = 'div', // Rename for syntax highlighting
|
||||||
className = '',
|
className = '',
|
||||||
|
onElement,
|
||||||
...props
|
...props
|
||||||
},
|
},
|
||||||
ref,
|
ref,
|
||||||
) => {
|
) => {
|
||||||
const contents = useMemo(
|
const contents = useMemo(
|
||||||
() => htmlStringToComponents(htmlString, { onText: textToEmojis }),
|
() =>
|
||||||
[htmlString],
|
htmlStringToComponents(htmlString, { onText: textToEmojis, onElement }),
|
||||||
|
[htmlString, onElement],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -46,6 +50,7 @@ export const ModernEmojiHTML = polymorphicForwardRef<'div', EmojiHTMLProps>(
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
ModernEmojiHTML.displayName = 'ModernEmojiHTML';
|
||||||
|
|
||||||
export const LegacyEmojiHTML = polymorphicForwardRef<'div', EmojiHTMLProps>(
|
export const LegacyEmojiHTML = polymorphicForwardRef<'div', EmojiHTMLProps>(
|
||||||
(props, ref) => {
|
(props, ref) => {
|
||||||
|
@ -54,6 +59,7 @@ export const LegacyEmojiHTML = polymorphicForwardRef<'div', EmojiHTMLProps>(
|
||||||
htmlString,
|
htmlString,
|
||||||
extraEmojis,
|
extraEmojis,
|
||||||
className,
|
className,
|
||||||
|
onElement,
|
||||||
...rest
|
...rest
|
||||||
} = props;
|
} = props;
|
||||||
const Wrapper = asElement ?? 'div';
|
const Wrapper = asElement ?? 'div';
|
||||||
|
@ -67,6 +73,7 @@ export const LegacyEmojiHTML = polymorphicForwardRef<'div', EmojiHTMLProps>(
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
LegacyEmojiHTML.displayName = 'LegacyEmojiHTML';
|
||||||
|
|
||||||
export const EmojiHTML = isModernEmojiEnabled()
|
export const EmojiHTML = isModernEmojiEnabled()
|
||||||
? ModernEmojiHTML
|
? ModernEmojiHTML
|
||||||
|
|
|
@ -53,13 +53,19 @@ describe('html', () => {
|
||||||
|
|
||||||
it('calls onElement callback', () => {
|
it('calls onElement callback', () => {
|
||||||
const input = '<p>lorem ipsum</p>';
|
const input = '<p>lorem ipsum</p>';
|
||||||
const onElement = vi.fn(
|
const onElement = vi.fn<html.OnElementHandler>(
|
||||||
(element: HTMLElement, children: React.ReactNode[]) =>
|
(element, props, children) =>
|
||||||
React.createElement(element.tagName.toLowerCase(), {}, ...children),
|
React.createElement(
|
||||||
|
element.tagName.toLowerCase(),
|
||||||
|
props,
|
||||||
|
...children,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
html.htmlStringToComponents(input, { onElement });
|
html.htmlStringToComponents(input, { onElement });
|
||||||
expect(onElement).toHaveBeenCalledExactlyOnceWith(
|
expect(onElement).toHaveBeenCalledExactlyOnceWith(
|
||||||
expect.objectContaining({ tagName: 'P' }),
|
expect.objectContaining({ tagName: 'P' }),
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
expect.objectContaining({ key: expect.any(String) }),
|
||||||
expect.arrayContaining(['lorem ipsum']),
|
expect.arrayContaining(['lorem ipsum']),
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
|
@ -71,6 +77,8 @@ describe('html', () => {
|
||||||
const output = html.htmlStringToComponents(input, { onElement });
|
const output = html.htmlStringToComponents(input, { onElement });
|
||||||
expect(onElement).toHaveBeenCalledExactlyOnceWith(
|
expect(onElement).toHaveBeenCalledExactlyOnceWith(
|
||||||
expect.objectContaining({ tagName: 'P' }),
|
expect.objectContaining({ tagName: 'P' }),
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
expect.objectContaining({ key: expect.any(String) }),
|
||||||
expect.arrayContaining(['lorem ipsum']),
|
expect.arrayContaining(['lorem ipsum']),
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
|
|
|
@ -32,14 +32,21 @@ interface QueueItem {
|
||||||
depth: number;
|
depth: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HTMLToStringOptions<Arg extends Record<string, unknown>> {
|
export type OnElementHandler<
|
||||||
maxDepth?: number;
|
Arg extends Record<string, unknown> = Record<string, unknown>,
|
||||||
onText?: (text: string, extra: Arg) => React.ReactNode;
|
> = (
|
||||||
onElement?: (
|
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
|
props: Record<string, unknown>,
|
||||||
children: React.ReactNode[],
|
children: React.ReactNode[],
|
||||||
extra: Arg,
|
extra: Arg,
|
||||||
) => React.ReactNode;
|
) => React.ReactNode;
|
||||||
|
|
||||||
|
export interface HTMLToStringOptions<
|
||||||
|
Arg extends Record<string, unknown> = Record<string, unknown>,
|
||||||
|
> {
|
||||||
|
maxDepth?: number;
|
||||||
|
onText?: (text: string, extra: Arg) => React.ReactNode;
|
||||||
|
onElement?: OnElementHandler<Arg>;
|
||||||
onAttribute?: (
|
onAttribute?: (
|
||||||
name: string,
|
name: string,
|
||||||
value: string,
|
value: string,
|
||||||
|
@ -125,20 +132,9 @@ export function htmlStringToComponents<Arg extends Record<string, unknown>>(
|
||||||
const children: React.ReactNode[] = [];
|
const children: React.ReactNode[] = [];
|
||||||
let element: React.ReactNode = undefined;
|
let element: React.ReactNode = undefined;
|
||||||
|
|
||||||
// If onElement is provided, use it to create the element.
|
// Generate props from attributes.
|
||||||
if (onElement) {
|
const key = `html-${uniqueIdCounter++}`; // Get the current key and then increment it.
|
||||||
const component = onElement(node, children, extraArgs);
|
const props: Record<string, unknown> = { key };
|
||||||
|
|
||||||
// Check for undefined to allow returning null.
|
|
||||||
if (component !== undefined) {
|
|
||||||
element = component;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the element wasn't created, use the default conversion.
|
|
||||||
if (element === undefined) {
|
|
||||||
const props: Record<string, unknown> = {};
|
|
||||||
props.key = `html-${uniqueIdCounter++}`; // Get the current key and then increment it.
|
|
||||||
for (const attr of node.attributes) {
|
for (const attr of node.attributes) {
|
||||||
let name = attr.name.toLowerCase();
|
let name = attr.name.toLowerCase();
|
||||||
|
|
||||||
|
@ -184,6 +180,18 @@ export function htmlStringToComponents<Arg extends Record<string, unknown>>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If onElement is provided, use it to create the element.
|
||||||
|
if (onElement) {
|
||||||
|
const component = onElement(node, props, children, extraArgs);
|
||||||
|
|
||||||
|
// Check for undefined to allow returning null.
|
||||||
|
if (component !== undefined) {
|
||||||
|
element = component;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the element wasn't created, use the default conversion.
|
||||||
|
if (element === undefined) {
|
||||||
element = React.createElement(
|
element = React.createElement(
|
||||||
tagName,
|
tagName,
|
||||||
props,
|
props,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user