[MM-62570] PluginLinkTooltip throws warning that class is invalid dom property (#29858)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
7322fa1a3f
Коммит
f3d1bc0731
@@ -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;
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user