@apify/ui-library 0.58.0 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apify/ui-library",
3
- "version": "0.58.0",
3
+ "version": "0.59.0",
4
4
  "description": "React UI library used by apify.com",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -41,7 +41,9 @@
41
41
  "rehype-sanitize": "^6.0.0",
42
42
  "remark-gfm": "^3.0.1",
43
43
  "remark-toc": "8.0.1",
44
- "slugify": "^1.6.6"
44
+ "slugify": "^1.6.6",
45
+ "unified": "^10.0.0",
46
+ "unist-util-visit": "^5.0.0"
45
47
  },
46
48
  "peerDependencies": {
47
49
  "react": "17.x || 18.x",
@@ -65,5 +67,5 @@
65
67
  "recast": "^0.23.9",
66
68
  "typescript": "^5.1.6"
67
69
  },
68
- "gitHead": "256677147856d54ab9872ac71372f6c0601d5d48"
70
+ "gitHead": "aff5f2f1a3db4fa021ade9588f4d923e59527e8b"
69
71
  }
@@ -1,3 +1,4 @@
1
+ import type { Root, Element, Text as TextNode } from 'hast';
1
2
  import React, { useCallback, useMemo, useRef } from 'react';
2
3
  import type { Components } from 'react-markdown';
3
4
  import ReactMarkdown, { uriTransformer } from 'react-markdown';
@@ -6,6 +7,8 @@ import rehypeRaw from 'rehype-raw';
6
7
  import rehypeSanitize from 'rehype-sanitize';
7
8
  import remarkGfm from 'remark-gfm';
8
9
  import styled from 'styled-components';
10
+ import type { Pluggable, PluggableList } from 'unified';
11
+ import { visit } from 'unist-util-visit';
9
12
 
10
13
  import { theme } from '../../design_system/theme.js';
11
14
  import { useSharedUiDependencies } from '../../ui_dependency_provider.js';
@@ -43,8 +46,33 @@ const StyledMarkdown = styled(ReactMarkdown)`
43
46
  }
44
47
  `;
45
48
 
46
- export const defaultRehypePlugins = [rehypeRaw];
47
- export const defaultRemarkPlugins = [remarkGfm];
49
+ // Function to recursively process unsupported elements
50
+ const unwrapUnsupportedElement = (node: Element): (Element | TextNode)[] => {
51
+ return node.children.flatMap((child) => {
52
+ if (child.type === 'text') {
53
+ return child; // Keep text as is
54
+ }
55
+ if (child.type === 'element') {
56
+ if (defaultAllowedElements.includes(child.tagName)) {
57
+ return child; // Keep supported elements
58
+ }
59
+ return unwrapUnsupportedElement(child as Element);
60
+ }
61
+ return []; // Ignore other node types
62
+ });
63
+ };
64
+
65
+ const getUnsupportedRehypeTagsSanitationPlugIn = (
66
+ allowedElements: string[],
67
+ ): Pluggable<any[]> => () => (tree: Root) => {
68
+ visit(tree, 'element', (node, index, parent) => {
69
+ if (!allowedElements.includes(node.tagName) && parent && typeof index === 'number') {
70
+ // Replace unsupported element with its valid children
71
+ parent.children.splice(index, 1, ...unwrapUnsupportedElement(node));
72
+ }
73
+ });
74
+ };
75
+
48
76
  export const defaultAllowedElements = [
49
77
  'a',
50
78
  'b',
@@ -79,6 +107,9 @@ export const defaultAllowedElements = [
79
107
  'ul',
80
108
  ];
81
109
 
110
+ export const defaultRehypePlugins = [rehypeRaw];
111
+ export const defaultRemarkPlugins = [remarkGfm];
112
+
82
113
  // TODO: Add Image component
83
114
  const regularMarkdownSizeComponents: Components = {
84
115
  h1: ({ children }) => <HeadingContent mt='space32' mb='space16' type='heading1'>{children}</HeadingContent>,
@@ -151,13 +182,15 @@ export const SimpleMarkdown: React.FC<SimpleMarkdownProps> = ({
151
182
  }) => {
152
183
  const cleanedMarkdown = useMemo(() => cleanMarkdown(markdown), [markdown]);
153
184
 
185
+ const effectiveAllowedElements = useRef(allowedElements || defaultAllowedElements);
186
+
154
187
  const effectiveComponents = useRef({
155
188
  ...(size === 'regular' ? regularMarkdownSizeComponents : smallMarkdownSizeComponents),
156
189
  ...components,
157
190
  });
158
191
 
159
- const rehypePluginsRef = useRef([
160
- ...(rehypePlugins || defaultRehypePlugins),
192
+ const rehypePluginsRef = useRef<PluggableList>([
193
+ ...(rehypePlugins || [...defaultRehypePlugins, getUnsupportedRehypeTagsSanitationPlugIn(effectiveAllowedElements.current)]),
161
194
  rehypeSanitize, // lets always sanitize the output
162
195
  ]);
163
196
 
@@ -168,7 +201,7 @@ export const SimpleMarkdown: React.FC<SimpleMarkdownProps> = ({
168
201
  components={effectiveComponents.current}
169
202
  rehypePlugins={rehypePluginsRef.current}
170
203
  remarkPlugins={remarkPlugins || defaultRemarkPlugins}
171
- allowedElements={allowedElements || defaultAllowedElements}
204
+ allowedElements={effectiveAllowedElements.current}
172
205
  transformLinkUri={(href, children, title) => {
173
206
  const transformed = transformLinkUri ? transformLinkUri(href, children, title) : defaultUrlTransform(href);
174
207
  return uriTransformer(transformed);