[MM-62570] PluginLinkTooltip throws warning that class is invalid dom property (#29858)

Этот коммит содержится в:
M-ZubairAhmed
2025-01-18 19:51:56 +05:30
коммит произвёл GitHub
родитель 7322fa1a3f
Коммит f3d1bc0731
3 изменённых файлов: 90 добавлений и 5 удалений

Просмотреть файл

@@ -147,7 +147,7 @@ exports[`messageHtmlToComponent link with enabled a tooltip plugin 1`] = `
<PluginLinkTooltip
nodeAttributes={
Object {
"class": "theme markdown__link",
"className": "theme markdown__link",
"href": "http://www.dolor.com",
"rel": "noreferrer",
"target": "_blank",

Просмотреть файл

@@ -2,6 +2,7 @@
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import type {AnchorHTMLAttributes} from 'react';
import AtMention from 'components/at_mention';
import MarkdownImage from 'components/markdown_image';
@@ -9,7 +10,7 @@ import MarkdownImage from 'components/markdown_image';
import {renderWithContext, screen} from 'tests/react_testing_utils';
import Constants from 'utils/constants';
import EmojiMap from 'utils/emoji_map';
import messageHtmlToComponent from 'utils/message_html_to_component';
import messageHtmlToComponent, {convertPropsToReactStandard} from 'utils/message_html_to_component';
import * as TextFormatting from 'utils/text_formatting';
const emptyEmojiMap = new EmojiMap(new Map());
@@ -187,3 +188,58 @@ const myFunction = () => {
});
});
});
describe('convertPropsToReactStandard', () => {
test('converts class to className', () => {
const inputProps = {class: 'button-class'} as AnchorHTMLAttributes<HTMLAnchorElement>;
const expected = {className: 'button-class'};
expect(convertPropsToReactStandard(inputProps)).toEqual(expected);
});
test('converts for to htmlFor', () => {
const inputProps = {for: 'input-id'} as AnchorHTMLAttributes<HTMLAnchorElement>;
const expected = {htmlFor: 'input-id'};
expect(convertPropsToReactStandard(inputProps)).toEqual(expected);
});
test('converts tabindex to tabIndex', () => {
const inputProps = {tabindex: '0'} as AnchorHTMLAttributes<HTMLAnchorElement>;
const expected = {tabIndex: '0'};
expect(convertPropsToReactStandard(inputProps)).toEqual(expected);
});
test('converts readonly to readOnly', () => {
const inputProps = {readonly: true} as AnchorHTMLAttributes<HTMLAnchorElement>;
const expected = {readOnly: true};
expect(convertPropsToReactStandard(inputProps)).toEqual(expected);
});
test('keeps other properties unchanged', () => {
const inputProps = {id: 'unique-id', type: 'text'} as AnchorHTMLAttributes<HTMLAnchorElement>;
const expected = {id: 'unique-id', type: 'text'};
expect(convertPropsToReactStandard(inputProps)).toEqual(expected);
});
test('handles multiple conversions and keeps other properties', () => {
const inputProps = {
class: 'button-class',
for: 'input-id',
tabindex: '0',
readonly: true,
id: 'unique-id',
};
const expected = {
className: 'button-class',
htmlFor: 'input-id',
tabIndex: '0',
readOnly: true,
id: 'unique-id',
};
expect(convertPropsToReactStandard(inputProps)).toEqual(expected);
});
});

Просмотреть файл

@@ -2,6 +2,7 @@
// See LICENSE.txt for license information.
import {Parser, ProcessNodeDefinitions} from 'html-to-react';
import type {AllHTMLAttributes} from 'react';
import React from 'react';
import AtMention from 'components/at_mention';
@@ -64,7 +65,7 @@ type ProcessingInstruction = {
* - hasPluginTooltips - If specified, the LinkTooltip component is placed inside links. Defaults to false.
* - channelId = If specified, to be passed along to ProfilePopover via AtMention
*/
export function messageHtmlToComponent(html: string, options: Options = {}) {
export default function messageHtmlToComponent(html: string, options: Options = {}) {
if (!html) {
return null;
}
@@ -112,7 +113,7 @@ export function messageHtmlToComponent(html: string, options: Options = {}) {
shouldProcessNode: (node: any) => node.type === 'tag' && node.name === 'a' && node.attribs.href,
processNode: (node: any, children: any) => {
return (
<PluginLinkTooltip nodeAttributes={node.attribs}>
<PluginLinkTooltip nodeAttributes={convertPropsToReactStandard(node.attribs)}>
{children}
</PluginLinkTooltip>
);
@@ -281,4 +282,32 @@ export function messageHtmlToComponent(html: string, options: Options = {}) {
return parser.parseWithInstructions(html, isValidNode, processingInstructions);
}
export default messageHtmlToComponent;
/**
* This function converts HTML attributes to React-specific props.
* For example, it changes 'class' to 'className'. Note that this function
* is not exhaustive and may not cover all HTML attributes. Add more cases as needed.
*/
export function convertPropsToReactStandard(propsToConvert: AllHTMLAttributes<HTMLElement>): Record<string, unknown> {
const newProps: Record<string, unknown> = {};
for (const [key, value] of Object.entries(propsToConvert)) {
switch (key) {
case 'class':
newProps.className = value;
break;
case 'for':
newProps.htmlFor = value;
break;
case 'tabindex':
newProps.tabIndex = value;
break;
case 'readonly':
newProps.readOnly = value;
break;
default:
newProps[key] = value;
}
}
return newProps;
}