@happeouikit/content-renderer 2.0.1 → 3.0.3
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/CHANGELOG.md +17 -0
- package/dist/ContentRenderer.js +54 -30
- package/dist/utils.js +27 -15
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.0.3
|
|
4
|
+
|
|
5
|
+
- [Added] Added prop `forceDefaultLinkTarget` that will force external links with `target="_blank"` and removes `target` from internal links.
|
|
6
|
+
|
|
7
|
+
## 3.0.2
|
|
8
|
+
|
|
9
|
+
- [Fixed] ContentRenderer now applies styling options from Pages
|
|
10
|
+
|
|
11
|
+
## 3.0.1
|
|
12
|
+
|
|
13
|
+
- [Fixed] ContentRenderer should now deal with all kinds of xss
|
|
14
|
+
|
|
15
|
+
## 3.0.0
|
|
16
|
+
|
|
17
|
+
- [Fixed] the xss converter now accepts a whiteList for CSS properties that includes vertical-align
|
|
18
|
+
- [Updated] new dependencies updates: React 17 and and Styled Components 5
|
|
19
|
+
|
|
3
20
|
## 2.0.1
|
|
4
21
|
|
|
5
22
|
- [Added] Changed type to uppercase and to handle both upper and lowercase. Add warning if lowecase is used.
|
package/dist/ContentRenderer.js
CHANGED
|
@@ -68,8 +68,11 @@ import { CopyToClipboardButton } from "@happeouikit/copy-to-clipboard-button";
|
|
|
68
68
|
import { BodyUI, sansFamily } from "@happeouikit/typography";
|
|
69
69
|
import { margin200, media, padding200 } from "@happeouikit/layout";
|
|
70
70
|
import { active, gray04, gray07, gray09, lighten, warn } from "@happeouikit/colors";
|
|
71
|
-
import { toHtml,
|
|
71
|
+
import { toHtml, loadCodeFont, decodeHtmlEntities, removeSpecialCharacters, toSafeText } from "./utils";
|
|
72
72
|
import { ELEMENT_TYPE_PRE, ELEMENT_TYPE_CODE, HIGHLIGHJS_CSS, HIGHLIGHJS_JS, CODE_BLOCK_AVAILABLE_LANGUAGES } from "./constants";
|
|
73
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
74
|
+
import { Fragment as _Fragment } from "react/jsx-runtime";
|
|
75
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
73
76
|
|
|
74
77
|
var ContentRenderer = function ContentRenderer(_ref) {
|
|
75
78
|
var content = _ref.content,
|
|
@@ -84,7 +87,8 @@ var ContentRenderer = function ContentRenderer(_ref) {
|
|
|
84
87
|
useHashAutoscroll = _ref.useHashAutoscroll,
|
|
85
88
|
_ref$hashAutoscrollTo = _ref.hashAutoscrollTopMargin,
|
|
86
89
|
hashAutoscrollTopMargin = _ref$hashAutoscrollTo === void 0 ? 0 : _ref$hashAutoscrollTo,
|
|
87
|
-
decodeHtmlEntitiesOnRender = _ref.decodeHtmlEntitiesOnRender
|
|
90
|
+
decodeHtmlEntitiesOnRender = _ref.decodeHtmlEntitiesOnRender,
|
|
91
|
+
forceDefaultLinkTarget = _ref.forceDefaultLinkTarget;
|
|
88
92
|
var element = useRef({});
|
|
89
93
|
|
|
90
94
|
var _useState = useState([]),
|
|
@@ -132,6 +136,21 @@ var ContentRenderer = function ContentRenderer(_ref) {
|
|
|
132
136
|
});
|
|
133
137
|
}
|
|
134
138
|
|
|
139
|
+
var linkElems = forceDefaultLinkTarget ? element.current.querySelectorAll("a") : [];
|
|
140
|
+
|
|
141
|
+
if (linkElems.length > 0) {
|
|
142
|
+
var currentOrigin = window.origin;
|
|
143
|
+
linkElems.forEach(function (link) {
|
|
144
|
+
var href = link.href;
|
|
145
|
+
|
|
146
|
+
if (href.startsWith(currentOrigin)) {
|
|
147
|
+
delete link.target;
|
|
148
|
+
} else {
|
|
149
|
+
link.target = "_blank";
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
135
154
|
var blockElems = element.current.querySelectorAll(ELEMENT_TYPE_PRE);
|
|
136
155
|
|
|
137
156
|
if (blockElems.length > 0) {
|
|
@@ -171,45 +190,48 @@ var ContentRenderer = function ContentRenderer(_ref) {
|
|
|
171
190
|
if (useHashAutoscroll) {
|
|
172
191
|
scrollAndHighlightHash();
|
|
173
192
|
}
|
|
174
|
-
}, [element, createAnchors, useHashAutoscroll, scrollAndHighlightHash]);
|
|
193
|
+
}, [element, createAnchors, forceDefaultLinkTarget, useHashAutoscroll, scrollAndHighlightHash]);
|
|
175
194
|
var safeText = useMemo(function () {
|
|
176
195
|
var lowercaseType = type && typeof type === "string" ? type.toLowerCase() : "html";
|
|
177
|
-
var transformedContent = lowercaseType === "text" ? "<p>".concat(content, "</p>") : toHtml(content, lowercaseType);
|
|
196
|
+
var transformedContent = lowercaseType === "text" ? toSafeText("<p>".concat(content, "</p>")) : toHtml(content, lowercaseType);
|
|
178
197
|
|
|
179
198
|
if (decodeHtmlEntitiesOnRender) {
|
|
180
199
|
transformedContent = decodeHtmlEntities(transformedContent);
|
|
181
200
|
}
|
|
182
201
|
|
|
183
|
-
return
|
|
202
|
+
return transformedContent;
|
|
184
203
|
}, [content, type, decodeHtmlEntitiesOnRender]); // Use dangerouslySetInnerHTML as there might be nested <p>'s header etc
|
|
185
204
|
// This requires that all content is run through the toSafeText filter
|
|
186
205
|
// before displaying. This will run it through xss filter.
|
|
187
206
|
|
|
188
|
-
return
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
207
|
+
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
208
|
+
children: [/*#__PURE__*/_jsx(Wrapper, {
|
|
209
|
+
className: "content-renderer ".concat(widgetType === "page" ? "fr-view pages-text" : ""),
|
|
210
|
+
ref: element,
|
|
211
|
+
widgetType: widgetType,
|
|
212
|
+
headerFont: headerFont,
|
|
213
|
+
bodyFont: bodyFont,
|
|
214
|
+
createAnchors: createAnchors,
|
|
215
|
+
dangerouslySetInnerHTML: {
|
|
216
|
+
__html: safeText
|
|
217
|
+
}
|
|
218
|
+
}), copyBtns.map(function (el) {
|
|
219
|
+
var clipboardContent = el.textContent || "";
|
|
220
|
+
|
|
221
|
+
if (el.tagName !== ELEMENT_TYPE_PRE) {
|
|
222
|
+
clipboardContent = "".concat(window.location.href.split("#")[0], "#").concat(el.id);
|
|
223
|
+
} // eslint-disable-next-line no-control-regex
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
clipboardContent = removeSpecialCharacters(clipboardContent);
|
|
227
|
+
return /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/_jsx(BtnContainer, {
|
|
228
|
+
className: "content-copy-btn",
|
|
229
|
+
children: /*#__PURE__*/_jsx(CopyToClipboardButton, {
|
|
230
|
+
clipboardContent: clipboardContent
|
|
231
|
+
})
|
|
232
|
+
}), el);
|
|
233
|
+
})]
|
|
234
|
+
});
|
|
213
235
|
};
|
|
214
236
|
|
|
215
237
|
var BtnContainer = styled.div.withConfig({
|
|
@@ -250,6 +272,7 @@ ContentRenderer.propTypes = {
|
|
|
250
272
|
headerFont: PropTypes.string,
|
|
251
273
|
decodeHtmlEntitiesOnRender: PropTypes.bool,
|
|
252
274
|
createAnchors: PropTypes.bool,
|
|
275
|
+
forceDefaultLinkTarget: PropTypes.bool,
|
|
253
276
|
useHashAutoscroll: PropTypes.bool,
|
|
254
277
|
hashAutoscrollTopMargin: PropTypes.number
|
|
255
278
|
};
|
|
@@ -262,6 +285,7 @@ ContentRenderer.defaultProps = {
|
|
|
262
285
|
decodeHtmlEntitiesOnRender: false,
|
|
263
286
|
createAnchors: false,
|
|
264
287
|
useHashAutoscroll: true,
|
|
288
|
+
forceDefaultLinkTarget: false,
|
|
265
289
|
hashAutoscrollTopMargin: 0
|
|
266
290
|
};
|
|
267
291
|
export default ContentRenderer;
|
package/dist/utils.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
|
|
3
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
|
|
4
|
+
|
|
5
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
|
|
6
|
+
|
|
1
7
|
import linkifyHtml from "linkifyjs/html";
|
|
2
8
|
import xss from "xss";
|
|
3
9
|
import { decode as decodeHtmlEntities } from "html-entities";
|
|
@@ -22,6 +28,26 @@ var processMentions = function processMentions() {
|
|
|
22
28
|
var str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
|
|
23
29
|
return str.replace(mentioningRegexGroupCaseInsensitive, processMatch).replace(mentioningHtmlRegexGroupCaseInsensitive, processMatch);
|
|
24
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* @name toSafeText
|
|
33
|
+
* @description Returns filtered text with filterXSS library
|
|
34
|
+
* @author Antero Hanhirova
|
|
35
|
+
* @param {String} - Text to whitelist
|
|
36
|
+
* @returns {String}
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
var toSafeText = function toSafeText(text) {
|
|
41
|
+
return xss(text, {
|
|
42
|
+
whiteList: safeTagsAndAttributes,
|
|
43
|
+
css: {
|
|
44
|
+
whiteList: _objectSpread({}, xss.getDefaultCSSWhiteList(), {
|
|
45
|
+
"vertical-align": true
|
|
46
|
+
})
|
|
47
|
+
},
|
|
48
|
+
stripIgnoreTag: true
|
|
49
|
+
}).replace(/ /g, " ").replace(/\u2060/g, ""); // Replaces a hidden character that resulted in a "?"-character in backend
|
|
50
|
+
};
|
|
25
51
|
/**
|
|
26
52
|
* @name toHtml
|
|
27
53
|
* @description Converts markdown to html, adds space to end of String
|
|
@@ -44,26 +70,12 @@ var toHtml = function toHtml() {
|
|
|
44
70
|
newString = processMentions(newString);
|
|
45
71
|
}
|
|
46
72
|
|
|
73
|
+
newString = toSafeText(newString);
|
|
47
74
|
return linkifyHtml(newString, {
|
|
48
75
|
target: "_blank",
|
|
49
76
|
ignoreTags: ["pre", "code", "script", "style"]
|
|
50
77
|
});
|
|
51
78
|
};
|
|
52
|
-
/**
|
|
53
|
-
* @name toSafeText
|
|
54
|
-
* @description Returns filtered text with filterXSS library
|
|
55
|
-
* @author Antero Hanhirova
|
|
56
|
-
* @param {String} - Text to whitelist
|
|
57
|
-
* @returns {String}
|
|
58
|
-
*/
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
var toSafeText = function toSafeText(text) {
|
|
62
|
-
return xss(text, {
|
|
63
|
-
whiteList: safeTagsAndAttributes,
|
|
64
|
-
stripIgnoreTag: true
|
|
65
|
-
}).replace(/ /g, " ").replace(/\u2060/g, ""); // Replaces a hidden character that resulted in a "?"-character in backend
|
|
66
|
-
};
|
|
67
79
|
|
|
68
80
|
var loadCodeFont = function loadCodeFont() {
|
|
69
81
|
var id = "ibmMonoOnDemand";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happeouikit/content-renderer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"description": "Rendering html and markdown content",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "src/index.js",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"prop-types": "^15.6.2",
|
|
32
|
-
"react": "^
|
|
33
|
-
"styled-components": "^
|
|
32
|
+
"react": "^17.0.2",
|
|
33
|
+
"styled-components": "^5.3.1"
|
|
34
34
|
},
|
|
35
35
|
"publishConfig": {
|
|
36
36
|
"access": "public"
|