diff --git a/webapp/channels/src/components/code_block/__snapshots__/code_block.test.tsx.snap b/webapp/channels/src/components/code_block/__snapshots__/code_block.test.tsx.snap
index 5dd6346344..f21ebbbe17 100644
--- a/webapp/channels/src/components/code_block/__snapshots__/code_block.test.tsx.snap
+++ b/webapp/channels/src/components/code_block/__snapshots__/code_block.test.tsx.snap
@@ -1,119 +1,68 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`codeBlock should render html code block with proper indentation after syntax highlighting 1`] = `
-
-
+
-
-
+
-
-
-
- }
placement="top"
- shouldUpdatePosition={true}
- trigger={
- Array [
- "hover",
- "focus",
- ]
- }
>
-
+
}
placement="top"
shouldUpdatePosition={true}
@@ -124,225 +73,186 @@ exports[`codeBlock should render html code block with proper indentation after s
]
}
>
-
+
+
+ }
+ placement="top"
+ shouldUpdatePosition={true}
+ trigger={
+ Array [
+ "hover",
+ "focus",
+ ]
+ }
>
-
-
+
+
+
+
-
-
-
- HTML, XML
-
-
-
+
+
+ HTML, XML
+
+
- 1
+
+ 1
2
3
4
5
6
-
-
+ <div className='myClass'>
<a href='https://randomgibberishurl.com'>ClickMe</a>
</div>
\`\`\`
",
+ }
}
- }
- />
+ />
+
-
-
-
+
+
+
`;
exports[`codeBlock should render html code block with proper indentation before syntax highlighting 1`] = `
-
@@ -82,12 +97,17 @@ const myFunction = () => {
\`\`\`
`;
- const wrapper = shallow(
-
,
+ const wrapper = mount(
+
+
+
+
+ ,
);
+ await actImmediate(wrapper);
const languageHeader = wrapper.find('span.post-code__language').text();
const lineNumbersDiv = wrapper.find('.post-code__line-numbers').exists();
@@ -107,12 +127,14 @@ const myFunction = () => {
`;
const wrapper = mount(
-
-
- ,
+
+
+
+
+ ,
);
await actImmediate(wrapper);
@@ -124,7 +146,7 @@ const myFunction = () => {
expect(wrapper).toMatchSnapshot();
});
- test('should render unknown language before syntax highlighting', () => {
+ test('should render unknown language before syntax highlighting', async () => {
const language = 'unknownLanguage';
const input = `\`\`\`${language}
this is my unknown language
@@ -132,12 +154,17 @@ it shouldn't highlight, it's just garbage
\`\`\`
`;
- const wrapper = shallow(
-
,
+ const wrapper = mount(
+
+
+
+
+ ,
);
+ await actImmediate(wrapper);
const languageHeader = wrapper.find('span.post-code__language').exists();
const lineNumbersDiv = wrapper.find('.post-code__line-numbers').exists();
@@ -156,12 +183,14 @@ it shouldn't highlight, it's just garbage
`;
const wrapper = mount(
-
-
- ,
+
+
+
+
+ ,
);
await actImmediate(wrapper);
diff --git a/webapp/channels/src/components/code_block/code_block.tsx b/webapp/channels/src/components/code_block/code_block.tsx
index 0841320b83..89138a77ab 100644
--- a/webapp/channels/src/components/code_block/code_block.tsx
+++ b/webapp/channels/src/components/code_block/code_block.tsx
@@ -3,6 +3,10 @@
import React, {useCallback, useEffect, useState} from 'react';
+import {useSelector} from 'react-redux';
+
+import {GlobalState} from 'types/store';
+
import CopyButton from 'components/copy_button';
import * as SyntaxHighlighting from 'utils/syntax_highlighting';
@@ -67,10 +71,27 @@ const CodeBlock: React.FC
= ({code, language, searchedContent}: Props) =>
htmlContent = `${searchedContent} ${content}`;
}
+ const codeBlockActions = useSelector((state: GlobalState) => state.plugins.components.CodeBlockAction);
+ const pluginItems = codeBlockActions?.
+ map((item) => {
+ if (!item.component) {
+ return null;
+ }
+
+ const Component = item.component as any;
+ return (
+
+ );
+ });
+
return (
+ {pluginItems}
{header}
diff --git a/webapp/channels/src/plugins/registry.ts b/webapp/channels/src/plugins/registry.ts
index 46f7c224c7..3b07956f02 100644
--- a/webapp/channels/src/plugins/registry.ts
+++ b/webapp/channels/src/plugins/registry.ts
@@ -508,6 +508,12 @@ export default class PluginRegistry {
return dispatchPluginComponentAction('PostEditorAction', this.id, component);
});
+ // Register a component to the add to the code block header.
+ // Accepts a React component. Returns a unique identifier.
+ registerCodeBlockActionComponent = reArg(['component'], ({component}: DPluginComponentProp) => {
+ return dispatchPluginComponentAction('CodeBlockAction', this.id, component);
+ });
+
// Register a component to the add to the new messages separator.
// Accepts a React component. Returns a unique identifier.
registerNewMessagesSeparatorActionComponent = reArg(['component'], ({component}: DPluginComponentProp) => {
diff --git a/webapp/channels/src/reducers/plugins/index.ts b/webapp/channels/src/reducers/plugins/index.ts
index 8b6dcc1023..513eab486a 100644
--- a/webapp/channels/src/reducers/plugins/index.ts
+++ b/webapp/channels/src/reducers/plugins/index.ts
@@ -183,6 +183,7 @@ const initialComponents: PluginsState['components'] = {
PostDropdownMenu: [],
PostAction: [],
PostEditorAction: [],
+ CodeBlockAction: [],
NewMessagesSeparatorAction: [],
Product: [],
RightHandSidebarComponent: [],
diff --git a/webapp/channels/src/types/store/plugins.ts b/webapp/channels/src/types/store/plugins.ts
index 633a07b23b..b09083809e 100644
--- a/webapp/channels/src/types/store/plugins.ts
+++ b/webapp/channels/src/types/store/plugins.ts
@@ -29,6 +29,7 @@ export type PluginsState = {
PostDropdownMenu: PluginComponent[];
PostAction: PluginComponent[];
PostEditorAction: PluginComponent[];
+ CodeBlockAction: PluginComponent[];
NewMessagesSeparatorAction: PluginComponent[];
FilePreview: PluginComponent[];
MainMenu: PluginComponent[];