@dhis2/analytics 26.13.2 → 26.13.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/build/cjs/components/FileMenu/GetLinkDialog.js +6 -7
- package/build/cjs/components/FileMenu/__tests__/GetLinkDialog.spec.js +65 -16
- package/build/cjs/components/FileMenu/utils.js +4 -3
- package/build/cjs/components/RichText/Parser/MdParser.js +18 -0
- package/build/cjs/components/RichText/Parser/__tests__/MdParser.spec.js +2 -2
- package/build/es/components/FileMenu/GetLinkDialog.js +6 -7
- package/build/es/components/FileMenu/__tests__/GetLinkDialog.spec.js +65 -16
- package/build/es/components/FileMenu/utils.js +4 -3
- package/build/es/components/RichText/Parser/MdParser.js +18 -0
- package/build/es/components/RichText/Parser/__tests__/MdParser.spec.js +2 -2
- package/package.json +1 -1
|
@@ -20,12 +20,11 @@ const GetLinkDialog = _ref => {
|
|
|
20
20
|
onClose
|
|
21
21
|
} = _ref;
|
|
22
22
|
const {
|
|
23
|
+
apiVersion,
|
|
23
24
|
baseUrl
|
|
24
25
|
} = (0, _appRuntime.useConfig)();
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const appBaseUrl = new URL(baseUrl, self.location.href);
|
|
28
|
-
const appUrl = new URL((0, _utils.appPathFor)(type, id), appBaseUrl);
|
|
26
|
+
const appBaseUrl = new URL(baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`, self.location.href).href;
|
|
27
|
+
const appUrl = new URL((0, _utils.appPathFor)(type, id, apiVersion), appBaseUrl).href;
|
|
29
28
|
return /*#__PURE__*/_react.default.createElement(_ui.Modal, {
|
|
30
29
|
onClose: onClose
|
|
31
30
|
}, /*#__PURE__*/_react.default.createElement(_style.default, {
|
|
@@ -35,12 +34,12 @@ const GetLinkDialog = _ref => {
|
|
|
35
34
|
}, _index.default.t('Open in this app')), /*#__PURE__*/_react.default.createElement("div", {
|
|
36
35
|
className: `jsx-${_GetLinkDialogStyles.styles.__hash}` + " " + "link-container"
|
|
37
36
|
}, /*#__PURE__*/_react.default.createElement("a", {
|
|
38
|
-
href: appUrl
|
|
37
|
+
href: appUrl,
|
|
39
38
|
className: `jsx-${_GetLinkDialogStyles.styles.__hash}`
|
|
40
|
-
}, appUrl
|
|
39
|
+
}, appUrl), /*#__PURE__*/_react.default.createElement(_ui.Button, {
|
|
41
40
|
icon: /*#__PURE__*/_react.default.createElement(_ui.IconCopy24, null),
|
|
42
41
|
small: true,
|
|
43
|
-
onClick: () => navigator.clipboard.writeText(appUrl
|
|
42
|
+
onClick: () => navigator.clipboard.writeText(appUrl)
|
|
44
43
|
}))), /*#__PURE__*/_react.default.createElement(_ui.ModalActions, null, /*#__PURE__*/_react.default.createElement(_ui.ButtonStrip, null, /*#__PURE__*/_react.default.createElement(_ui.Button, {
|
|
45
44
|
onClick: onClose,
|
|
46
45
|
secondary: true
|
|
@@ -4,17 +4,49 @@ var _ui = require("@dhis2/ui");
|
|
|
4
4
|
var _enzyme = require("enzyme");
|
|
5
5
|
var _react = _interopRequireDefault(require("react"));
|
|
6
6
|
var _GetLinkDialog = require("../GetLinkDialog.js");
|
|
7
|
-
var _utils = require("../utils.js");
|
|
8
7
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
-
const testBaseUrl = 'http://
|
|
8
|
+
const testBaseUrl = 'http://host.tld/test/';
|
|
9
|
+
const mockUseConfig = jest.fn(() => ({
|
|
10
|
+
apiVersion: 42,
|
|
11
|
+
baseUrl: testBaseUrl
|
|
12
|
+
}));
|
|
10
13
|
jest.mock('@dhis2/app-runtime', () => ({
|
|
11
|
-
useConfig: () => (
|
|
12
|
-
baseUrl: testBaseUrl
|
|
13
|
-
})
|
|
14
|
+
useConfig: () => mockUseConfig()
|
|
14
15
|
}));
|
|
16
|
+
const tests = [{
|
|
17
|
+
type: 'visualization',
|
|
18
|
+
baseUrl: 'http://host.tld',
|
|
19
|
+
id: 'dv-id-1',
|
|
20
|
+
expected: 'http://host.tld/dhis-web-data-visualizer/#/dv-id-1'
|
|
21
|
+
}, {
|
|
22
|
+
type: 'visualization',
|
|
23
|
+
baseUrl: testBaseUrl,
|
|
24
|
+
id: 'dv-id-2',
|
|
25
|
+
expected: 'http://host.tld/test/dhis-web-data-visualizer/#/dv-id-2'
|
|
26
|
+
}, {
|
|
27
|
+
type: 'eventVisualization',
|
|
28
|
+
baseUrl: 'http://host.tld/other-path/',
|
|
29
|
+
id: 'll-id-1',
|
|
30
|
+
expected: 'http://host.tld/other-path/dhis-web-line-listing/#/ll-id-1'
|
|
31
|
+
}, {
|
|
32
|
+
type: 'eventVisualization',
|
|
33
|
+
apiVersion: 41,
|
|
34
|
+
baseUrl: 'http://host.tld/other-path',
|
|
35
|
+
id: 'll-id-2',
|
|
36
|
+
expected: 'http://host.tld/other-path/api/apps/line-listing/#/ll-id-2'
|
|
37
|
+
}, {
|
|
38
|
+
type: 'map',
|
|
39
|
+
baseUrl: testBaseUrl,
|
|
40
|
+
id: 'map-id-1',
|
|
41
|
+
expected: 'http://host.tld/test/dhis-web-maps/#/map-id-1'
|
|
42
|
+
}, {
|
|
43
|
+
type: 'map',
|
|
44
|
+
baseUrl: '../',
|
|
45
|
+
id: 'map-id-2',
|
|
46
|
+
expected: 'http://localhost/dhis-web-maps/#/map-id-2'
|
|
47
|
+
}];
|
|
15
48
|
describe('The FileMenu - GetLinkDialog component', () => {
|
|
16
49
|
let shallowGetLinkDialog;
|
|
17
|
-
let props;
|
|
18
50
|
const onClose = jest.fn();
|
|
19
51
|
const getGetLinkDialogComponent = props => {
|
|
20
52
|
if (!shallowGetLinkDialog) {
|
|
@@ -24,21 +56,38 @@ describe('The FileMenu - GetLinkDialog component', () => {
|
|
|
24
56
|
};
|
|
25
57
|
beforeEach(() => {
|
|
26
58
|
shallowGetLinkDialog = undefined;
|
|
27
|
-
props = {
|
|
28
|
-
type: 'visualization',
|
|
29
|
-
id: 'get-link-test-id',
|
|
30
|
-
onClose
|
|
31
|
-
};
|
|
32
59
|
});
|
|
33
60
|
it('renders a Modal component', () => {
|
|
34
|
-
expect(getGetLinkDialogComponent(
|
|
61
|
+
expect(getGetLinkDialogComponent({
|
|
62
|
+
type: tests[0].type,
|
|
63
|
+
id: tests[0].id
|
|
64
|
+
}).find(_ui.Modal)).toHaveLength(1);
|
|
35
65
|
});
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
66
|
+
test.each(tests)('renders a <a> tag containing the correct app path and id', _ref => {
|
|
67
|
+
let {
|
|
68
|
+
apiVersion,
|
|
69
|
+
baseUrl,
|
|
70
|
+
type,
|
|
71
|
+
id,
|
|
72
|
+
expected
|
|
73
|
+
} = _ref;
|
|
74
|
+
mockUseConfig.mockReturnValueOnce({
|
|
75
|
+
apiVersion: apiVersion || 42,
|
|
76
|
+
baseUrl
|
|
77
|
+
});
|
|
78
|
+
const href = getGetLinkDialogComponent({
|
|
79
|
+
type,
|
|
80
|
+
id,
|
|
81
|
+
onClose
|
|
82
|
+
}).find('a').prop('href');
|
|
83
|
+
expect(href).toMatch(expected);
|
|
39
84
|
});
|
|
40
85
|
it('calls the onClose callback when the Close button is clicked', () => {
|
|
41
|
-
getGetLinkDialogComponent(
|
|
86
|
+
getGetLinkDialogComponent({
|
|
87
|
+
type: tests[0].type,
|
|
88
|
+
id: tests[0].id,
|
|
89
|
+
onClose
|
|
90
|
+
}).find(_ui.Button).at(1).simulate('click');
|
|
42
91
|
expect(onClose).toHaveBeenCalled();
|
|
43
92
|
});
|
|
44
93
|
});
|
|
@@ -28,14 +28,15 @@ const labelForFileType = fileType => {
|
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
30
|
exports.labelForFileType = labelForFileType;
|
|
31
|
-
const appPathFor = (fileType, id) => {
|
|
31
|
+
const appPathFor = (fileType, id, apiVersion) => {
|
|
32
32
|
switch (fileType) {
|
|
33
33
|
case FILE_TYPE_VISUALIZATION:
|
|
34
34
|
return `dhis-web-data-visualizer/#/${id}`;
|
|
35
35
|
case FILE_TYPE_MAP:
|
|
36
|
-
return `dhis-web-maps
|
|
36
|
+
return `dhis-web-maps/#/${id}`;
|
|
37
37
|
case FILE_TYPE_EVENT_VISUALIZATION:
|
|
38
|
-
|
|
38
|
+
// VERSION-TOGGLE: remove when 42 is the lowest supported version
|
|
39
|
+
return apiVersion >= 42 ? `dhis-web-line-listing/#/${id}` : `api/apps/line-listing/#/${id}`;
|
|
39
40
|
default:
|
|
40
41
|
return `${window.location.search}${window.location.hash}`;
|
|
41
42
|
}
|
|
@@ -87,6 +87,24 @@ class MdParser {
|
|
|
87
87
|
breaks: true
|
|
88
88
|
});
|
|
89
89
|
|
|
90
|
+
// From: https://github.com/markdown-it/markdown-it/blob/master/docs/architecture.md#renderer
|
|
91
|
+
// Remember the old renderer if overridden, or proxy to the default renderer.
|
|
92
|
+
const defaultRender = md.renderer.rules.link_open || (
|
|
93
|
+
// eslint-disable-next-line max-params
|
|
94
|
+
(tokens, idx, options, env, self) => {
|
|
95
|
+
return self.renderToken(tokens, idx, options);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// eslint-disable-next-line max-params
|
|
99
|
+
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
|
|
100
|
+
// Add a new 'target' and 'rel' attributes, or replace the value of the existing ones.
|
|
101
|
+
tokens[idx].attrSet('target', '_blank');
|
|
102
|
+
tokens[idx].attrSet('rel', 'noreferrer');
|
|
103
|
+
|
|
104
|
+
// Pass the token to the default renderer.
|
|
105
|
+
return defaultRender(tokens, idx, options, env, self);
|
|
106
|
+
};
|
|
107
|
+
|
|
90
108
|
// *bold* -> <strong>bold</strong>
|
|
91
109
|
md.inline.ruler.push('strong', parse(codes.bold.name));
|
|
92
110
|
|
|
@@ -16,13 +16,13 @@ describe('MdParser class', () => {
|
|
|
16
16
|
// nested italic/bold combinations not allowed
|
|
17
17
|
['_italic with *bold* inside_', '<em>italic with *bold* inside</em>'], ['*bold with _italic_ inside*', '<strong>bold with _italic_ inside</strong>'], ['text with : and :)', 'text with : and <span>\u{1F642}</span>'], ['(parenthesis and :))', '(parenthesis and <span>\u{1F642}</span>)'], [':((parenthesis:))', '<span>\u{1F641}</span>(parenthesis<span>\u{1F642}</span>)'], [':+1+1', '<span>\u{1F44D}</span>+1'], ['-1:-1', '-1<span>\u{1F44E}</span>'],
|
|
18
18
|
// links
|
|
19
|
-
['example.com/path', '<a href="http://example.com/path">example.com/path</a>'],
|
|
19
|
+
['[Test link](https://host.tld/path/link)', '<a href="https://host.tld/path/link" target="_blank" rel="noreferrer">Test link</a>'], ['example.com/path', '<a href="http://example.com/path" target="_blank" rel="noreferrer">example.com/path</a>'],
|
|
20
20
|
// not recognized links with italic marker inside not converted
|
|
21
21
|
['example_with_underscore.com/path', 'example_with_underscore.com/path'], ['example_with_underscore.com/path_with_underscore', 'example_with_underscore.com/path_with_underscore'],
|
|
22
22
|
// markers around non-recognized links
|
|
23
23
|
['link example_with_underscore.com/path should _not_ be converted', 'link example_with_underscore.com/path should <em>not</em> be converted'], ['link example_with_underscore.com/path should *not* be converted', 'link example_with_underscore.com/path should <strong>not</strong> be converted'],
|
|
24
24
|
// italic marker inside links not converted
|
|
25
|
-
['example.com/path_with_underscore', '<a href="http://example.com/path_with_underscore">example.com/path_with_underscore</a>'], ['_italic_ and *bold* with a example.com/link_with_underscore', '<em>italic</em> and <strong>bold</strong> with a <a href="http://example.com/link_with_underscore">example.com/link_with_underscore</a>'], ['example.com/path with *bold* after :)', '<a href="http://example.com/path">example.com/path</a> with <strong>bold</strong> after <span>\u{1F642}</span>'], ['_before_ example.com/path_with_underscore *after* :)', '<em>before</em> <a href="http://example.com/path_with_underscore">example.com/path_with_underscore</a> <strong>after</strong> <span>\u{1F642}</span>'],
|
|
25
|
+
['example.com/path_with_underscore', '<a href="http://example.com/path_with_underscore" target="_blank" rel="noreferrer">example.com/path_with_underscore</a>'], ['_italic_ and *bold* with a example.com/link_with_underscore', '<em>italic</em> and <strong>bold</strong> with a <a href="http://example.com/link_with_underscore" target="_blank" rel="noreferrer">example.com/link_with_underscore</a>'], ['example.com/path with *bold* after :)', '<a href="http://example.com/path" target="_blank" rel="noreferrer">example.com/path</a> with <strong>bold</strong> after <span>\u{1F642}</span>'], ['_before_ example.com/path_with_underscore *after* :)', '<em>before</em> <a href="http://example.com/path_with_underscore" target="_blank" rel="noreferrer">example.com/path_with_underscore</a> <strong>after</strong> <span>\u{1F642}</span>'],
|
|
26
26
|
// italic/bold markers right after non-word characters
|
|
27
27
|
['_If % of ART retention rate after 12 months >90(%)_: Sustain the efforts.', '<em>If % of ART retention rate after 12 months >90(%)</em>: Sustain the efforts.'], ['*If % of ART retention rate after 12 months >90(%)*: Sustain the efforts.', '<strong>If % of ART retention rate after 12 months >90(%)</strong>: Sustain the efforts.']];
|
|
28
28
|
inlineTests.forEach(test => {
|
|
@@ -13,12 +13,11 @@ export const GetLinkDialog = _ref => {
|
|
|
13
13
|
onClose
|
|
14
14
|
} = _ref;
|
|
15
15
|
const {
|
|
16
|
+
apiVersion,
|
|
16
17
|
baseUrl
|
|
17
18
|
} = useConfig();
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const appBaseUrl = new URL(baseUrl, self.location.href);
|
|
21
|
-
const appUrl = new URL(appPathFor(type, id), appBaseUrl);
|
|
19
|
+
const appBaseUrl = new URL(baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`, self.location.href).href;
|
|
20
|
+
const appUrl = new URL(appPathFor(type, id, apiVersion), appBaseUrl).href;
|
|
22
21
|
return /*#__PURE__*/React.createElement(Modal, {
|
|
23
22
|
onClose: onClose
|
|
24
23
|
}, /*#__PURE__*/React.createElement(_JSXStyle, {
|
|
@@ -28,12 +27,12 @@ export const GetLinkDialog = _ref => {
|
|
|
28
27
|
}, i18n.t('Open in this app')), /*#__PURE__*/React.createElement("div", {
|
|
29
28
|
className: `jsx-${styles.__hash}` + " " + "link-container"
|
|
30
29
|
}, /*#__PURE__*/React.createElement("a", {
|
|
31
|
-
href: appUrl
|
|
30
|
+
href: appUrl,
|
|
32
31
|
className: `jsx-${styles.__hash}`
|
|
33
|
-
}, appUrl
|
|
32
|
+
}, appUrl), /*#__PURE__*/React.createElement(Button, {
|
|
34
33
|
icon: /*#__PURE__*/React.createElement(IconCopy24, null),
|
|
35
34
|
small: true,
|
|
36
|
-
onClick: () => navigator.clipboard.writeText(appUrl
|
|
35
|
+
onClick: () => navigator.clipboard.writeText(appUrl)
|
|
37
36
|
}))), /*#__PURE__*/React.createElement(ModalActions, null, /*#__PURE__*/React.createElement(ButtonStrip, null, /*#__PURE__*/React.createElement(Button, {
|
|
38
37
|
onClick: onClose,
|
|
39
38
|
secondary: true
|
|
@@ -2,16 +2,48 @@ import { Button, Modal } from '@dhis2/ui';
|
|
|
2
2
|
import { shallow } from 'enzyme';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { GetLinkDialog } from '../GetLinkDialog.js';
|
|
5
|
-
|
|
6
|
-
const
|
|
5
|
+
const testBaseUrl = 'http://host.tld/test/';
|
|
6
|
+
const mockUseConfig = jest.fn(() => ({
|
|
7
|
+
apiVersion: 42,
|
|
8
|
+
baseUrl: testBaseUrl
|
|
9
|
+
}));
|
|
7
10
|
jest.mock('@dhis2/app-runtime', () => ({
|
|
8
|
-
useConfig: () => (
|
|
9
|
-
baseUrl: testBaseUrl
|
|
10
|
-
})
|
|
11
|
+
useConfig: () => mockUseConfig()
|
|
11
12
|
}));
|
|
13
|
+
const tests = [{
|
|
14
|
+
type: 'visualization',
|
|
15
|
+
baseUrl: 'http://host.tld',
|
|
16
|
+
id: 'dv-id-1',
|
|
17
|
+
expected: 'http://host.tld/dhis-web-data-visualizer/#/dv-id-1'
|
|
18
|
+
}, {
|
|
19
|
+
type: 'visualization',
|
|
20
|
+
baseUrl: testBaseUrl,
|
|
21
|
+
id: 'dv-id-2',
|
|
22
|
+
expected: 'http://host.tld/test/dhis-web-data-visualizer/#/dv-id-2'
|
|
23
|
+
}, {
|
|
24
|
+
type: 'eventVisualization',
|
|
25
|
+
baseUrl: 'http://host.tld/other-path/',
|
|
26
|
+
id: 'll-id-1',
|
|
27
|
+
expected: 'http://host.tld/other-path/dhis-web-line-listing/#/ll-id-1'
|
|
28
|
+
}, {
|
|
29
|
+
type: 'eventVisualization',
|
|
30
|
+
apiVersion: 41,
|
|
31
|
+
baseUrl: 'http://host.tld/other-path',
|
|
32
|
+
id: 'll-id-2',
|
|
33
|
+
expected: 'http://host.tld/other-path/api/apps/line-listing/#/ll-id-2'
|
|
34
|
+
}, {
|
|
35
|
+
type: 'map',
|
|
36
|
+
baseUrl: testBaseUrl,
|
|
37
|
+
id: 'map-id-1',
|
|
38
|
+
expected: 'http://host.tld/test/dhis-web-maps/#/map-id-1'
|
|
39
|
+
}, {
|
|
40
|
+
type: 'map',
|
|
41
|
+
baseUrl: '../',
|
|
42
|
+
id: 'map-id-2',
|
|
43
|
+
expected: 'http://localhost/dhis-web-maps/#/map-id-2'
|
|
44
|
+
}];
|
|
12
45
|
describe('The FileMenu - GetLinkDialog component', () => {
|
|
13
46
|
let shallowGetLinkDialog;
|
|
14
|
-
let props;
|
|
15
47
|
const onClose = jest.fn();
|
|
16
48
|
const getGetLinkDialogComponent = props => {
|
|
17
49
|
if (!shallowGetLinkDialog) {
|
|
@@ -21,21 +53,38 @@ describe('The FileMenu - GetLinkDialog component', () => {
|
|
|
21
53
|
};
|
|
22
54
|
beforeEach(() => {
|
|
23
55
|
shallowGetLinkDialog = undefined;
|
|
24
|
-
props = {
|
|
25
|
-
type: 'visualization',
|
|
26
|
-
id: 'get-link-test-id',
|
|
27
|
-
onClose
|
|
28
|
-
};
|
|
29
56
|
});
|
|
30
57
|
it('renders a Modal component', () => {
|
|
31
|
-
expect(getGetLinkDialogComponent(
|
|
58
|
+
expect(getGetLinkDialogComponent({
|
|
59
|
+
type: tests[0].type,
|
|
60
|
+
id: tests[0].id
|
|
61
|
+
}).find(Modal)).toHaveLength(1);
|
|
32
62
|
});
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
63
|
+
test.each(tests)('renders a <a> tag containing the correct app path and id', _ref => {
|
|
64
|
+
let {
|
|
65
|
+
apiVersion,
|
|
66
|
+
baseUrl,
|
|
67
|
+
type,
|
|
68
|
+
id,
|
|
69
|
+
expected
|
|
70
|
+
} = _ref;
|
|
71
|
+
mockUseConfig.mockReturnValueOnce({
|
|
72
|
+
apiVersion: apiVersion || 42,
|
|
73
|
+
baseUrl
|
|
74
|
+
});
|
|
75
|
+
const href = getGetLinkDialogComponent({
|
|
76
|
+
type,
|
|
77
|
+
id,
|
|
78
|
+
onClose
|
|
79
|
+
}).find('a').prop('href');
|
|
80
|
+
expect(href).toMatch(expected);
|
|
36
81
|
});
|
|
37
82
|
it('calls the onClose callback when the Close button is clicked', () => {
|
|
38
|
-
getGetLinkDialogComponent(
|
|
83
|
+
getGetLinkDialogComponent({
|
|
84
|
+
type: tests[0].type,
|
|
85
|
+
id: tests[0].id,
|
|
86
|
+
onClose
|
|
87
|
+
}).find(Button).at(1).simulate('click');
|
|
39
88
|
expect(onClose).toHaveBeenCalled();
|
|
40
89
|
});
|
|
41
90
|
});
|
|
@@ -19,14 +19,15 @@ export const labelForFileType = fileType => {
|
|
|
19
19
|
return fileType;
|
|
20
20
|
}
|
|
21
21
|
};
|
|
22
|
-
export const appPathFor = (fileType, id) => {
|
|
22
|
+
export const appPathFor = (fileType, id, apiVersion) => {
|
|
23
23
|
switch (fileType) {
|
|
24
24
|
case FILE_TYPE_VISUALIZATION:
|
|
25
25
|
return `dhis-web-data-visualizer/#/${id}`;
|
|
26
26
|
case FILE_TYPE_MAP:
|
|
27
|
-
return `dhis-web-maps
|
|
27
|
+
return `dhis-web-maps/#/${id}`;
|
|
28
28
|
case FILE_TYPE_EVENT_VISUALIZATION:
|
|
29
|
-
|
|
29
|
+
// VERSION-TOGGLE: remove when 42 is the lowest supported version
|
|
30
|
+
return apiVersion >= 42 ? `dhis-web-line-listing/#/${id}` : `api/apps/line-listing/#/${id}`;
|
|
30
31
|
default:
|
|
31
32
|
return `${window.location.search}${window.location.hash}`;
|
|
32
33
|
}
|
|
@@ -80,6 +80,24 @@ export class MdParser {
|
|
|
80
80
|
breaks: true
|
|
81
81
|
});
|
|
82
82
|
|
|
83
|
+
// From: https://github.com/markdown-it/markdown-it/blob/master/docs/architecture.md#renderer
|
|
84
|
+
// Remember the old renderer if overridden, or proxy to the default renderer.
|
|
85
|
+
const defaultRender = md.renderer.rules.link_open || (
|
|
86
|
+
// eslint-disable-next-line max-params
|
|
87
|
+
(tokens, idx, options, env, self) => {
|
|
88
|
+
return self.renderToken(tokens, idx, options);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// eslint-disable-next-line max-params
|
|
92
|
+
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
|
|
93
|
+
// Add a new 'target' and 'rel' attributes, or replace the value of the existing ones.
|
|
94
|
+
tokens[idx].attrSet('target', '_blank');
|
|
95
|
+
tokens[idx].attrSet('rel', 'noreferrer');
|
|
96
|
+
|
|
97
|
+
// Pass the token to the default renderer.
|
|
98
|
+
return defaultRender(tokens, idx, options, env, self);
|
|
99
|
+
};
|
|
100
|
+
|
|
83
101
|
// *bold* -> <strong>bold</strong>
|
|
84
102
|
md.inline.ruler.push('strong', parse(codes.bold.name));
|
|
85
103
|
|
|
@@ -14,13 +14,13 @@ describe('MdParser class', () => {
|
|
|
14
14
|
// nested italic/bold combinations not allowed
|
|
15
15
|
['_italic with *bold* inside_', '<em>italic with *bold* inside</em>'], ['*bold with _italic_ inside*', '<strong>bold with _italic_ inside</strong>'], ['text with : and :)', 'text with : and <span>\u{1F642}</span>'], ['(parenthesis and :))', '(parenthesis and <span>\u{1F642}</span>)'], [':((parenthesis:))', '<span>\u{1F641}</span>(parenthesis<span>\u{1F642}</span>)'], [':+1+1', '<span>\u{1F44D}</span>+1'], ['-1:-1', '-1<span>\u{1F44E}</span>'],
|
|
16
16
|
// links
|
|
17
|
-
['example.com/path', '<a href="http://example.com/path">example.com/path</a>'],
|
|
17
|
+
['[Test link](https://host.tld/path/link)', '<a href="https://host.tld/path/link" target="_blank" rel="noreferrer">Test link</a>'], ['example.com/path', '<a href="http://example.com/path" target="_blank" rel="noreferrer">example.com/path</a>'],
|
|
18
18
|
// not recognized links with italic marker inside not converted
|
|
19
19
|
['example_with_underscore.com/path', 'example_with_underscore.com/path'], ['example_with_underscore.com/path_with_underscore', 'example_with_underscore.com/path_with_underscore'],
|
|
20
20
|
// markers around non-recognized links
|
|
21
21
|
['link example_with_underscore.com/path should _not_ be converted', 'link example_with_underscore.com/path should <em>not</em> be converted'], ['link example_with_underscore.com/path should *not* be converted', 'link example_with_underscore.com/path should <strong>not</strong> be converted'],
|
|
22
22
|
// italic marker inside links not converted
|
|
23
|
-
['example.com/path_with_underscore', '<a href="http://example.com/path_with_underscore">example.com/path_with_underscore</a>'], ['_italic_ and *bold* with a example.com/link_with_underscore', '<em>italic</em> and <strong>bold</strong> with a <a href="http://example.com/link_with_underscore">example.com/link_with_underscore</a>'], ['example.com/path with *bold* after :)', '<a href="http://example.com/path">example.com/path</a> with <strong>bold</strong> after <span>\u{1F642}</span>'], ['_before_ example.com/path_with_underscore *after* :)', '<em>before</em> <a href="http://example.com/path_with_underscore">example.com/path_with_underscore</a> <strong>after</strong> <span>\u{1F642}</span>'],
|
|
23
|
+
['example.com/path_with_underscore', '<a href="http://example.com/path_with_underscore" target="_blank" rel="noreferrer">example.com/path_with_underscore</a>'], ['_italic_ and *bold* with a example.com/link_with_underscore', '<em>italic</em> and <strong>bold</strong> with a <a href="http://example.com/link_with_underscore" target="_blank" rel="noreferrer">example.com/link_with_underscore</a>'], ['example.com/path with *bold* after :)', '<a href="http://example.com/path" target="_blank" rel="noreferrer">example.com/path</a> with <strong>bold</strong> after <span>\u{1F642}</span>'], ['_before_ example.com/path_with_underscore *after* :)', '<em>before</em> <a href="http://example.com/path_with_underscore" target="_blank" rel="noreferrer">example.com/path_with_underscore</a> <strong>after</strong> <span>\u{1F642}</span>'],
|
|
24
24
|
// italic/bold markers right after non-word characters
|
|
25
25
|
['_If % of ART retention rate after 12 months >90(%)_: Sustain the efforts.', '<em>If % of ART retention rate after 12 months >90(%)</em>: Sustain the efforts.'], ['*If % of ART retention rate after 12 months >90(%)*: Sustain the efforts.', '<strong>If % of ART retention rate after 12 months >90(%)</strong>: Sustain the efforts.']];
|
|
26
26
|
inlineTests.forEach(test => {
|