diff --git a/packages/markups/babel.config.js b/packages/markups/babel.config.js index 5ccad93f9..72b293c63 100644 --- a/packages/markups/babel.config.js +++ b/packages/markups/babel.config.js @@ -11,4 +11,16 @@ module.exports = { '@babel/preset-react', '@emotion/babel-preset-css-prop', ], + env: { + test: { + presets: [ + [ + '@babel/preset-env', + { + modules: 'commonjs', + }, + ], + ], + }, + }, }; diff --git a/packages/markups/jest.config.js b/packages/markups/jest.config.js new file mode 100644 index 000000000..43defaef1 --- /dev/null +++ b/packages/markups/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + testEnvironment: 'node', + transform: { + '^.+\\.[jt]sx?$': 'babel-jest', + }, +}; diff --git a/packages/markups/package.json b/packages/markups/package.json index ab60f023f..7eee2b6b0 100644 --- a/packages/markups/package.json +++ b/packages/markups/package.json @@ -16,6 +16,7 @@ "prebuild": "rimraf dist", "build": "rollup -c --context=window --environment NODE_ENV:production", "watch": "rollup -c --watch --context=window", + "test": "jest", "test:lint": "eslint src/**/*.js", "format": "prettier --write 'src/' ", "format:check": "prettier --check 'src/' ", diff --git a/packages/markups/src/elements/InlineElements.js b/packages/markups/src/elements/InlineElements.js index 2584196e8..a3af83584 100644 --- a/packages/markups/src/elements/InlineElements.js +++ b/packages/markups/src/elements/InlineElements.js @@ -12,6 +12,19 @@ import LinkSpan from './LinkSpan'; import UserMention from '../mentions/UserMention'; import TimestampElement from './TimestampElement'; +const isPlainTextToken = (token) => token?.type === 'PLAIN_TEXT'; + +const isWrappedMention = (items, index) => { + const previous = items[index - 1]; + const next = items[index + 1]; + + if (!isPlainTextToken(previous) || !isPlainTextToken(next)) { + return false; + } + + return /\(\s*$/.test(previous.value) && /^\s*\)/.test(next.value); +}; + const InlineElements = ({ contents }) => contents.map((content, index) => { switch (content.type) { @@ -34,6 +47,9 @@ const InlineElements = ({ contents }) => return ; case 'MENTION_USER': + if (isWrappedMention(contents, index)) { + return ; + } return ; case 'EMOJI': diff --git a/packages/markups/src/elements/InlineElements.test.js b/packages/markups/src/elements/InlineElements.test.js new file mode 100644 index 000000000..e86d1b904 --- /dev/null +++ b/packages/markups/src/elements/InlineElements.test.js @@ -0,0 +1,93 @@ +import React from 'react'; +import { renderToStaticMarkup } from 'react-dom/server'; +import InlineElements from './InlineElements'; + +jest.mock('./ItalicSpan', () => ({ + __esModule: true, + default: () => , +})); +jest.mock('./StrikeSpan', () => ({ + __esModule: true, + default: () => , +})); +jest.mock('./BoldSpan', () => ({ + __esModule: true, + default: () => , +})); +jest.mock('./CodeElement', () => ({ + __esModule: true, + default: () => , +})); +jest.mock('./Emoji', () => ({ + __esModule: true, + default: () => , +})); +jest.mock('../mentions/ChannelMention', () => ({ + __esModule: true, + default: () => , +})); +jest.mock('./ColorElement', () => ({ + __esModule: true, + default: () => , +})); +jest.mock('./LinkSpan', () => ({ + __esModule: true, + default: () => , +})); +jest.mock('./TimestampElement', () => ({ + __esModule: true, + default: () => , +})); + +jest.mock('./PlainSpan', () => ({ + __esModule: true, + default: ({ contents }) => {contents}, +})); + +jest.mock('../mentions/UserMention', () => ({ + __esModule: true, + default: ({ contents }) => ( + {contents.value} + ), +})); + +describe('InlineElements mention rendering', () => { + test('renders wrapped user mentions as plain text', () => { + const contents = [ + { type: 'PLAIN_TEXT', value: '(' }, + { type: 'MENTION_USER', value: { type: 'PLAIN_TEXT', value: 'test' } }, + { type: 'PLAIN_TEXT', value: ')' }, + ]; + + const html = renderToStaticMarkup(); + + expect(html).toContain('data-kind="plain">@test'); + expect(html).not.toContain('data-kind="mention-user">test'); + }); + + test('renders wrapped mentions with spaces as plain text', () => { + const contents = [ + { type: 'PLAIN_TEXT', value: '( ' }, + { type: 'MENTION_USER', value: { type: 'PLAIN_TEXT', value: 'test' } }, + { type: 'PLAIN_TEXT', value: ' )' }, + ]; + + const html = renderToStaticMarkup(); + + expect(html).toContain('data-kind="plain">@test'); + expect(html).not.toContain('data-kind="mention-user">test'); + }); + + test('keeps normal mentions styled when not wrapped', () => { + const contents = [ + { type: 'PLAIN_TEXT', value: 'hello ' }, + { type: 'MENTION_USER', value: { type: 'PLAIN_TEXT', value: 'test' } }, + { type: 'PLAIN_TEXT', value: ' world' }, + ]; + + const html = renderToStaticMarkup(); + + expect(html).toContain('data-kind="mention-user">test'); + expect(html).not.toContain('data-kind="plain">@test'); + }); +});