@dhis2/analytics 26.6.14 → 26.7.1

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.
Files changed (54) hide show
  1. package/build/cjs/components/AboutAOUnit/AboutAOUnit.js +5 -3
  2. package/build/cjs/components/Interpretations/InterpretationModal/CommentAddForm.js +5 -4
  3. package/build/cjs/components/Interpretations/InterpretationModal/CommentUpdateForm.js +4 -3
  4. package/build/cjs/components/Interpretations/InterpretationsUnit/InterpretationForm.js +5 -4
  5. package/build/cjs/components/Interpretations/common/Interpretation/InterpretationUpdateForm.js +5 -4
  6. package/build/cjs/components/Interpretations/common/Message/Message.js +2 -2
  7. package/build/cjs/components/Interpretations/common/Message/MessageEditorContainer.js +5 -5
  8. package/build/cjs/components/Interpretations/common/index.js +0 -11
  9. package/build/cjs/components/{Interpretations/common/RichTextEditor/RichTextEditor.js → RichText/Editor/Editor.js} +76 -53
  10. package/build/cjs/components/RichText/Editor/__tests__/Editor.spec.js +38 -0
  11. package/build/cjs/components/RichText/Editor/__tests__/convertCtrlKey.spec.js +204 -0
  12. package/build/cjs/components/{Interpretations/common/RichTextEditor → RichText/Editor}/markdownHandler.js +12 -6
  13. package/build/cjs/components/{Interpretations/common/RichTextEditor/styles/RichTextEditor.style.js → RichText/Editor/styles/Editor.style.js} +2 -2
  14. package/build/cjs/components/RichText/Parser/MdParser.js +106 -0
  15. package/build/cjs/components/RichText/Parser/Parser.js +35 -0
  16. package/build/cjs/components/RichText/Parser/__tests__/MdParser.spec.js +42 -0
  17. package/build/cjs/components/RichText/Parser/__tests__/Parser.spec.js +41 -0
  18. package/build/cjs/components/RichText/index.js +26 -0
  19. package/build/cjs/components/{Interpretations/common/UserMention → UserMention}/UserMentionWrapper.js +19 -9
  20. package/build/cjs/components/{Interpretations/common/UserMention → UserMention}/styles/UserMentionWrapper.style.js +2 -2
  21. package/build/cjs/components/{Interpretations/common/UserMention → UserMention}/useUserSearchResults.js +2 -2
  22. package/build/cjs/index.js +58 -46
  23. package/build/cjs/locales/en/translations.json +11 -11
  24. package/build/cjs/locales/ne/translations.json +15 -15
  25. package/build/es/components/AboutAOUnit/AboutAOUnit.js +5 -3
  26. package/build/es/components/Interpretations/InterpretationModal/CommentAddForm.js +2 -1
  27. package/build/es/components/Interpretations/InterpretationModal/CommentUpdateForm.js +2 -1
  28. package/build/es/components/Interpretations/InterpretationsUnit/InterpretationForm.js +3 -2
  29. package/build/es/components/Interpretations/common/Interpretation/InterpretationUpdateForm.js +2 -1
  30. package/build/es/components/Interpretations/common/Message/Message.js +1 -1
  31. package/build/es/components/Interpretations/common/Message/MessageEditorContainer.js +5 -5
  32. package/build/es/components/Interpretations/common/index.js +0 -1
  33. package/build/es/components/{Interpretations/common/RichTextEditor/RichTextEditor.js → RichText/Editor/Editor.js} +51 -28
  34. package/build/es/components/RichText/Editor/__tests__/Editor.spec.js +35 -0
  35. package/build/es/components/RichText/Editor/__tests__/convertCtrlKey.spec.js +202 -0
  36. package/build/es/components/{Interpretations/common/RichTextEditor → RichText/Editor}/markdownHandler.js +12 -6
  37. package/build/es/components/{Interpretations/common/RichTextEditor/styles/RichTextEditor.style.js → RichText/Editor/styles/Editor.style.js} +2 -2
  38. package/build/es/components/RichText/Parser/MdParser.js +98 -0
  39. package/build/es/components/RichText/Parser/Parser.js +25 -0
  40. package/build/es/components/RichText/Parser/__tests__/MdParser.spec.js +40 -0
  41. package/build/es/components/RichText/Parser/__tests__/Parser.spec.js +38 -0
  42. package/build/es/components/RichText/index.js +3 -0
  43. package/build/es/components/{Interpretations/common/UserMention → UserMention}/UserMentionWrapper.js +19 -8
  44. package/build/es/components/UserMention/styles/UserMentionWrapper.style.js +16 -0
  45. package/build/es/components/{Interpretations/common/UserMention → UserMention}/useUserSearchResults.js +2 -2
  46. package/build/es/index.js +1 -0
  47. package/build/es/locales/en/translations.json +11 -11
  48. package/build/es/locales/ne/translations.json +15 -15
  49. package/package.json +2 -2
  50. package/build/cjs/components/Interpretations/common/RichTextEditor/index.js +0 -12
  51. package/build/es/components/Interpretations/common/RichTextEditor/index.js +0 -1
  52. package/build/es/components/Interpretations/common/UserMention/styles/UserMentionWrapper.style.js +0 -16
  53. /package/build/cjs/components/{Interpretations/common/UserMention → UserMention}/UserList.js +0 -0
  54. /package/build/es/components/{Interpretations/common/UserMention → UserMention}/UserList.js +0 -0
@@ -0,0 +1,204 @@
1
+ "use strict";
2
+
3
+ var _markdownHandler = require("../markdownHandler.js");
4
+ describe('convertCtrlKey', () => {
5
+ it('does not trigger callback if no ctrl key', () => {
6
+ const cb = jest.fn();
7
+ const e = {
8
+ key: 'j',
9
+ preventDefault: () => {}
10
+ };
11
+ (0, _markdownHandler.convertCtrlKey)(e, cb);
12
+ expect(cb).not.toHaveBeenCalled();
13
+ });
14
+ describe('when ctrl key + "b" pressed', () => {
15
+ it('triggers callback with open/close markers and caret pos in between', () => {
16
+ const cb = jest.fn();
17
+ const e = {
18
+ key: 'b',
19
+ ctrlKey: true,
20
+ target: {
21
+ selectionStart: 0,
22
+ selectionEnd: 0,
23
+ value: 'rainbow dash'
24
+ },
25
+ preventDefault: () => {}
26
+ };
27
+ (0, _markdownHandler.convertCtrlKey)(e, cb);
28
+ expect(cb).toHaveBeenCalled();
29
+ expect(cb).toHaveBeenCalledWith('** rainbow dash', 1);
30
+ });
31
+ it('triggers callback with open/close markers and caret pos in between (end of text)', () => {
32
+ const cb = jest.fn();
33
+ const e = {
34
+ key: 'b',
35
+ ctrlKey: true,
36
+ target: {
37
+ selectionStart: 22,
38
+ selectionEnd: 22,
39
+ value: 'rainbow dash is purple'
40
+ },
41
+ preventDefault: () => {}
42
+ };
43
+ (0, _markdownHandler.convertCtrlKey)(e, cb);
44
+ expect(cb).toHaveBeenCalled();
45
+ expect(cb).toHaveBeenCalledWith('rainbow dash is purple **', 24);
46
+ });
47
+ it('triggers callback with open/close markers mid-text with surrounding spaces (1)', () => {
48
+ const cb = jest.fn();
49
+ const e = {
50
+ key: 'b',
51
+ metaKey: true,
52
+ target: {
53
+ selectionStart: 4,
54
+ // caret located just before "quick"
55
+ selectionEnd: 4,
56
+ value: 'the quick brown fox'
57
+ },
58
+ preventDefault: () => {}
59
+ };
60
+ (0, _markdownHandler.convertCtrlKey)(e, cb);
61
+ expect(cb).toHaveBeenCalled();
62
+ expect(cb).toHaveBeenCalledWith('the ** quick brown fox', 5);
63
+ });
64
+ it('triggers callback with open/close markers mid-text with surrounding spaces (2)', () => {
65
+ const cb = jest.fn();
66
+ const e = {
67
+ key: 'b',
68
+ metaKey: true,
69
+ target: {
70
+ selectionStart: 3,
71
+ // caret located just after "the"
72
+ selectionEnd: 3,
73
+ value: 'the quick brown fox'
74
+ },
75
+ preventDefault: () => {}
76
+ };
77
+ (0, _markdownHandler.convertCtrlKey)(e, cb);
78
+ expect(cb).toHaveBeenCalled();
79
+ expect(cb).toHaveBeenCalledWith('the ** quick brown fox', 5);
80
+ });
81
+ it('triggers callback with correct double markers and padding', () => {
82
+ const cb = jest.fn();
83
+ const e = {
84
+ key: 'b',
85
+ metaKey: true,
86
+ target: {
87
+ selectionStart: 9,
88
+ // between the underscores
89
+ selectionEnd: 9,
90
+ value: 'rainbow __'
91
+ },
92
+ preventDefault: () => {}
93
+ };
94
+ (0, _markdownHandler.convertCtrlKey)(e, cb);
95
+ expect(cb).toHaveBeenCalled();
96
+ expect(cb).toHaveBeenCalledWith('rainbow _**_', 10);
97
+ });
98
+ describe('selected text', () => {
99
+ it('triggers callback with open/close markers around text and caret pos after closing marker', () => {
100
+ const cb = jest.fn();
101
+ const e = {
102
+ key: 'b',
103
+ metaKey: true,
104
+ target: {
105
+ selectionStart: 5,
106
+ // "ow da" is selected
107
+ selectionEnd: 10,
108
+ value: 'rainbow dash is purple'
109
+ },
110
+ preventDefault: () => {}
111
+ };
112
+ (0, _markdownHandler.convertCtrlKey)(e, cb);
113
+ expect(cb).toHaveBeenCalled();
114
+ expect(cb).toHaveBeenCalledWith('rainb *ow da* sh is purple', 13);
115
+ });
116
+ it('triggers callback with open/close markers around text when starting at beginning of line', () => {
117
+ const cb = jest.fn();
118
+ const e = {
119
+ key: 'b',
120
+ metaKey: true,
121
+ target: {
122
+ selectionStart: 0,
123
+ // "rainbow" is selected
124
+ selectionEnd: 7,
125
+ value: 'rainbow dash is purple'
126
+ },
127
+ preventDefault: () => {}
128
+ };
129
+ (0, _markdownHandler.convertCtrlKey)(e, cb);
130
+ expect(cb).toHaveBeenCalled();
131
+ expect(cb).toHaveBeenCalledWith('*rainbow* dash is purple', 9);
132
+ });
133
+ it('triggers callback with open/close markers around text when ending at end of line', () => {
134
+ const cb = jest.fn();
135
+ const e = {
136
+ key: 'b',
137
+ metaKey: true,
138
+ target: {
139
+ selectionStart: 16,
140
+ // "purple" is selected
141
+ selectionEnd: 22,
142
+ value: 'rainbow dash is purple'
143
+ },
144
+ preventDefault: () => {}
145
+ };
146
+ (0, _markdownHandler.convertCtrlKey)(e, cb);
147
+ expect(cb).toHaveBeenCalled();
148
+ expect(cb).toHaveBeenCalledWith('rainbow dash is *purple*', 24);
149
+ });
150
+ it('triggers callback with open/close markers around word', () => {
151
+ const cb = jest.fn();
152
+ const e = {
153
+ key: 'b',
154
+ metaKey: true,
155
+ target: {
156
+ selectionStart: 8,
157
+ // "dash" is selected
158
+ selectionEnd: 12,
159
+ value: 'rainbow dash is purple'
160
+ },
161
+ preventDefault: () => {}
162
+ };
163
+ (0, _markdownHandler.convertCtrlKey)(e, cb);
164
+ expect(cb).toHaveBeenCalled();
165
+ expect(cb).toHaveBeenCalledWith('rainbow *dash* is purple', 14);
166
+ });
167
+ it('triggers callback with leading/trailing spaces trimmed from selection', () => {
168
+ const cb = jest.fn();
169
+ const e = {
170
+ key: 'b',
171
+ metaKey: true,
172
+ target: {
173
+ selectionStart: 8,
174
+ // " dash " is selected (note leading and trailing space)
175
+ selectionEnd: 13,
176
+ value: 'rainbow dash is purple'
177
+ },
178
+ preventDefault: () => {}
179
+ };
180
+ (0, _markdownHandler.convertCtrlKey)(e, cb);
181
+ expect(cb).toHaveBeenCalled();
182
+ expect(cb).toHaveBeenCalledWith('rainbow *dash* is purple', 14);
183
+ });
184
+ });
185
+ });
186
+ describe('when ctrl key + "i" pressed', () => {
187
+ it('triggers callback with open/close italics markers and caret pos in between', () => {
188
+ const cb = jest.fn();
189
+ const e = {
190
+ key: 'i',
191
+ ctrlKey: true,
192
+ target: {
193
+ selectionStart: 0,
194
+ selectionEnd: 0,
195
+ value: ''
196
+ },
197
+ preventDefault: () => {}
198
+ };
199
+ (0, _markdownHandler.convertCtrlKey)(e, cb);
200
+ expect(cb).toHaveBeenCalled();
201
+ expect(cb).toHaveBeenCalledWith('__', 1);
202
+ });
203
+ });
204
+ });
@@ -92,23 +92,29 @@ const insertMarkdown = (markdown, target, cb) => {
92
92
  if (start === end) {
93
93
  //no text
94
94
  const valueArr = value.split('');
95
- let markdown = marker.prefix;
95
+ let markdownString = marker.prefix;
96
96
  if (marker.postfix) {
97
- markdown += marker.postfix;
97
+ markdownString += marker.postfix;
98
98
  }
99
- valueArr.splice(start, 0, padMarkers(markdown));
99
+ valueArr.splice(start, 0, padMarkers(markdownString));
100
100
  newValue = valueArr.join('');
101
+
102
+ // for smileys, put the caret after a space
103
+ if (Object.keys(emojis).includes(markdown)) {
104
+ newValue += ' ';
105
+ caretPos = caretPos + newValue.length - 1;
106
+ }
101
107
  } else {
102
108
  const text = value.slice(start, end);
103
109
  const trimmedText = trim(text); // TODO really needed?
104
110
 
105
111
  // adjust caretPos based on trimmed text selection
106
112
  caretPos = caretPos - (text.length - trimmedText.length) + 1;
107
- let markdown = `${marker.prefix}${trimmedText}`;
113
+ let markdownString = `${marker.prefix}${trimmedText}`;
108
114
  if (marker.postfix) {
109
- markdown += marker.postfix;
115
+ markdownString += marker.postfix;
110
116
  }
111
- newValue = [value.slice(0, start), padMarkers(markdown), value.slice(end)].join('');
117
+ newValue = [value.slice(0, start), padMarkers(markdownString), value.slice(end)].join('');
112
118
  }
113
119
  cb(newValue, caretPos);
114
120
  };
@@ -5,9 +5,9 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.tooltipAnchorClasses = exports.toolbarClasses = exports.mainClasses = exports.emojisPopoverClasses = void 0;
7
7
  var _ui = require("@dhis2/ui");
8
- const mainClasses = [".container.jsx-1273817287{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;width:100%;}", `.preview.jsx-1273817287{font-size:14px;line-height:19px;color:${_ui.colors.grey900};}`, `.textarea.jsx-1273817287{width:100%;box-sizing:border-box;padding:${_ui.spacers.dp8} ${_ui.spacers.dp12};color:${_ui.colors.grey900};background-color:${_ui.colors.white};border:1px solid ${_ui.colors.grey500};border-radius:3px;box-shadow:inset 0 0 0 1px rgba(102,113,123,0.15), inset 0 1px 2px 0 rgba(102,113,123,0.1);outline:0;font-size:14px;line-height:${_ui.spacers.dp16};-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;}`, `.textarea.jsx-1273817287:focus{outline:none;box-shadow:0 0 0 3px ${_ui.theme.focus};width:calc(100% - 3px);}`, `.textarea.jsx-1273817287:disabled{background-color:${_ui.colors.grey100};border-color:${_ui.colors.grey500};color:${_ui.theme.disabled};cursor:not-allowed;}`];
8
+ const mainClasses = [".container.jsx-185829738{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;width:100%;height:100%;}", `.preview.jsx-185829738{padding:${_ui.spacers.dp8} ${_ui.spacers.dp12};font-size:14px;line-height:${_ui.spacers.dp16};color:${_ui.colors.grey900};overflow-y:auto;-webkit-scroll-behavior:smooth;-moz-scroll-behavior:smooth;-ms-scroll-behavior:smooth;scroll-behavior:smooth;}`, ".edit.jsx-185829738{width:100%;height:100%;-webkit-scroll-behavior:smooth;-moz-scroll-behavior:smooth;-ms-scroll-behavior:smooth;scroll-behavior:smooth;}", `.textarea.jsx-185829738{width:100%;height:100%;box-sizing:border-box;padding:${_ui.spacers.dp8} 15px;color:${_ui.colors.grey900};background-color:${_ui.colors.white};border:1px solid ${_ui.colors.grey500};border-radius:3px;box-shadow:inset 0 0 0 1px rgba(102,113,123,0.15), inset 0 1px 2px 0 rgba(102,113,123,0.1);outline:0;font-size:14px;line-height:${_ui.spacers.dp16};-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;resize:none;}`, ".textarea.resizable.jsx-185829738{resize:vertical;}", `.textarea.jsx-185829738:focus{outline:none;box-shadow:0 0 0 3px ${_ui.theme.focus};width:calc(100% - 6px);height:calc(100% - 3px);padding:${_ui.spacers.dp8} ${_ui.spacers.dp12};margin-left:3px;}`, `.textarea.jsx-185829738:disabled{background-color:${_ui.colors.grey100};border-color:${_ui.colors.grey500};color:${_ui.theme.disabled};cursor:not-allowed;}`];
9
9
  exports.mainClasses = mainClasses;
10
- mainClasses.__hash = "1273817287";
10
+ mainClasses.__hash = "185829738";
11
11
  const toolbarClasses = [`.toolbar.jsx-2267496677{background:${_ui.colors.grey050};border-radius:3px;border:1px solid ${_ui.colors.grey300};margin-bottom:${_ui.spacers.dp4};}`, `.actionsWrapper.jsx-2267496677{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;gap:${_ui.spacers.dp4};-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;padding:${_ui.spacers.dp4};}`, `.mainActions.jsx-2267496677{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;gap:${_ui.spacers.dp4};margin-top:${_ui.spacers.dp2};}`, ".sideActions.jsx-2267496677{-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;}", `.previewWrapper.jsx-2267496677{margin:${_ui.spacers.dp4};text-align:right;}`];
12
12
  exports.toolbarClasses = toolbarClasses;
13
13
  toolbarClasses.__hash = "2267496677";
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.MdParser = void 0;
7
+ var _markdownIt = _interopRequireDefault(require("markdown-it"));
8
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
9
+ const emojiDb = {
10
+ ':-)': '\u{1F642}',
11
+ ':)': '\u{1F642}',
12
+ ':-(': '\u{1F641}',
13
+ ':(': '\u{1F641}',
14
+ ':+1': '\u{1F44D}',
15
+ ':-1': '\u{1F44E}'
16
+ };
17
+ const codes = {
18
+ bold: {
19
+ name: 'bold',
20
+ char: '*',
21
+ domEl: 'strong',
22
+ encodedChar: 0x2a,
23
+ // see https://regex101.com/r/evswdV/8 for explanation of regexp
24
+ regexString: '\\B\\*((?!\\s)[^*]+(?:\\b|[^*\\s]))\\*\\B',
25
+ contentFn: val => val
26
+ },
27
+ italic: {
28
+ name: 'italic',
29
+ char: '_',
30
+ domEl: 'em',
31
+ encodedChar: 0x5f,
32
+ // see https://regex101.com/r/p6LpjK/6 for explanation of regexp
33
+ regexString: '\\b_((?!\\s)[^_]+(?:\\B|[^_\\s]))_\\b',
34
+ contentFn: val => val
35
+ },
36
+ emoji: {
37
+ name: 'emoji',
38
+ char: ':',
39
+ domEl: 'span',
40
+ encodedChar: 0x3a,
41
+ regexString: '^(:-\\)|:\\)|:\\(|:-\\(|:\\+1|:-1)',
42
+ contentFn: val => emojiDb[val]
43
+ }
44
+ };
45
+ let linksInText;
46
+ const markerIsInLinkText = pos => linksInText.some(link => pos >= link.index && pos <= link.lastIndex);
47
+ const parse = code => (state, silent) => {
48
+ if (silent) {
49
+ return false;
50
+ }
51
+ const start = state.pos;
52
+
53
+ // skip parsing emphasis if marker is within a link
54
+ if (markerIsInLinkText(start)) {
55
+ return false;
56
+ }
57
+ const marker = state.src.charCodeAt(start);
58
+
59
+ // marker character: "_", "*", ":"
60
+ if (marker !== codes[code].encodedChar) {
61
+ return false;
62
+ }
63
+ const MARKER_REGEX = new RegExp(codes[code].regexString);
64
+ const token = state.src.slice(start);
65
+ if (MARKER_REGEX.test(token)) {
66
+ const markerMatch = token.match(MARKER_REGEX);
67
+
68
+ // skip parsing sections where the marker is not at the start of the token
69
+ if (markerMatch.index !== 0) {
70
+ return false;
71
+ }
72
+ const text = markerMatch[1];
73
+ state.push(`${codes[code].domEl}_open`, codes[code].domEl, 1);
74
+ const t = state.push('text', '', 0);
75
+ t.content = codes[code].contentFn(text);
76
+ state.push(`${codes.bold.domEl}_close`, codes[code].domEl, -1);
77
+ state.pos += markerMatch[0].length;
78
+ return true;
79
+ }
80
+ return false;
81
+ };
82
+ class MdParser {
83
+ constructor() {
84
+ // disable all rules, enable autolink for URLs and email addresses
85
+ const md = new _markdownIt.default('zero', {
86
+ linkify: true,
87
+ breaks: true
88
+ });
89
+
90
+ // *bold* -> <strong>bold</strong>
91
+ md.inline.ruler.push('strong', parse(codes.bold.name));
92
+
93
+ // _italic_ -> <em>italic</em>
94
+ md.inline.ruler.push('italic', parse(codes.italic.name));
95
+
96
+ // :-) :) :-( :( :+1 :-1 -> <span>[unicode]</span>
97
+ md.inline.ruler.push('emoji', parse(codes.emoji.name));
98
+ md.enable(['heading', 'link', 'linkify', 'list', 'newline', 'strong', 'italic', 'emoji']);
99
+ this.md = md;
100
+ }
101
+ render(text) {
102
+ linksInText = this.md.linkify.match(text) || [];
103
+ return this.md.render(text);
104
+ }
105
+ }
106
+ exports.MdParser = MdParser;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Parser = void 0;
7
+ var _propTypes = _interopRequireDefault(require("prop-types"));
8
+ var _react = _interopRequireWildcard(require("react"));
9
+ var _MdParser = require("./MdParser.js");
10
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
11
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
12
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
+ const Parser = _ref => {
14
+ let {
15
+ children,
16
+ style
17
+ } = _ref;
18
+ const MdParserInstance = (0, _react.useMemo)(() => new _MdParser.MdParser(), []);
19
+ return children ? /*#__PURE__*/_react.default.createElement("div", {
20
+ style: {
21
+ ...style
22
+ },
23
+ dangerouslySetInnerHTML: {
24
+ __html: MdParserInstance.render(children)
25
+ }
26
+ }) : null;
27
+ };
28
+ exports.Parser = Parser;
29
+ Parser.defaultProps = {
30
+ style: null
31
+ };
32
+ Parser.propTypes = {
33
+ children: _propTypes.default.oneOfType([_propTypes.default.arrayOf(_propTypes.default.node), _propTypes.default.node]),
34
+ style: _propTypes.default.object
35
+ };
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+
3
+ var _MdParser = require("../MdParser.js");
4
+ const Parser = new _MdParser.MdParser();
5
+ describe('MdParser class', () => {
6
+ it('converts text into HTML', () => {
7
+ const inlineTests = [['_italic_', '<em>italic</em>'], ['*bold*', '<strong>bold</strong>'], ['_ not italic because there is a space _', '_ not italic because there is a space _'], [':-)', '<span>\u{1F642}</span>'], [':)', '<span>\u{1F642}</span>'], [':-(', '<span>\u{1F641}</span>'], [':(', '<span>\u{1F641}</span>'], [':+1', '<span>\u{1F44D}</span>'], [':-1', '<span>\u{1F44E}</span>'], ['mixed _italic_ *bold* and :+1', 'mixed <em>italic</em> <strong>bold</strong> and <span>\u{1F44D}</span>'], ['_italic with * inside_', '<em>italic with * inside</em>'], ['*bold with _ inside*', '<strong>bold with _ inside</strong>'],
8
+ // italic marker followed by : should work
9
+ ['_italic_:', '<em>italic</em>:'], ['_italic_: some text, *bold*: some other text', '<em>italic</em>: some text, <strong>bold</strong>: some other text'],
10
+ // bold marker followed by : should work
11
+ ['*bold*:', '<strong>bold</strong>:'], ['*bold*: some text, _italic_: some other text', '<strong>bold</strong>: some text, <em>italic</em>: some other text'],
12
+ // italic marker inside an italic string not allowed
13
+ ['_italic with _ inside_', '_italic with _ inside_'],
14
+ // bold marker inside a bold string not allowed
15
+ ['*bold with * inside*', '*bold with * inside*'], ['_multiple_ italic in the _same line_', '<em>multiple</em> italic in the <em>same line</em>'],
16
+ // nested italic/bold combinations not allowed
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
+ // links
19
+ ['example.com/path', '<a href="http://example.com/path">example.com/path</a>'],
20
+ // not recognized links with italic marker inside not converted
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
+ // markers around non-recognized links
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
+ // 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>'],
26
+ // italic/bold markers right after non-word characters
27
+ ['_If % of ART retention rate after 12 months >90(%)_: Sustain the efforts.', '<em>If % of ART retention rate after 12 months &gt;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 &gt;90(%)</strong>: Sustain the efforts.']];
28
+ inlineTests.forEach(test => {
29
+ const renderedText = Parser.render(test[0]);
30
+ expect(renderedText).toEqual(`<p>${test[1]}</p>\n`);
31
+ });
32
+ const blockTests = [
33
+ // heading
34
+ ['# Heading 1', '<h1>Heading 1</h1>'], ['## Heading 2', '<h2>Heading 2</h2>'], ['### Heading 3', '<h3>Heading 3</h3>'], ['#### Heading 4', '<h4>Heading 4</h4>'], ['##### Heading 5', '<h5>Heading 5</h5>'], ['###### Heading 6', '<h6>Heading 6</h6>'], ['# *Bold head*', '<h1><strong>Bold head</strong></h1>'], ['## _Italic title_', '<h2><em>Italic title</em></h2>'], ['### *Bold* and _italic_ title', '<h3><strong>Bold</strong> and <em>italic</em> title</h3>'],
35
+ // lists
36
+ ['* first\n* second\n* third', '<ul>\n<li>first</li>\n<li>second</li>\n<li>third</li>\n</ul>'], ['1. one\n1. two\n1. three\n', '<ol>\n<li>one</li>\n<li>two</li>\n<li>three</li>\n</ol>'], ['* *first*\n* second\n* _third_', '<ul>\n<li><strong>first</strong></li>\n<li>second</li>\n<li><em>third</em></li>\n</ul>']];
37
+ blockTests.forEach(test => {
38
+ const renderedText = Parser.render(test[0]);
39
+ expect(renderedText).toEqual(`${test[1]}\n`);
40
+ });
41
+ });
42
+ });
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+
3
+ var _enzyme = require("enzyme");
4
+ var _react = _interopRequireDefault(require("react"));
5
+ var _Parser = require("../Parser.js");
6
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
7
+ jest.mock('../MdParser.js', () => ({
8
+ MdParser: jest.fn().mockImplementation(() => {
9
+ return {
10
+ render: () => 'converted text'
11
+ };
12
+ })
13
+ }));
14
+ describe('RichText: Parser component', () => {
15
+ let richTextParser;
16
+ const defaultProps = {
17
+ style: {
18
+ color: 'blue',
19
+ whiteSpace: 'pre-line'
20
+ }
21
+ };
22
+ const renderComponent = (props, text) => {
23
+ return (0, _enzyme.shallow)( /*#__PURE__*/_react.default.createElement(_Parser.Parser, props, text));
24
+ };
25
+ it('should have rendered a result', () => {
26
+ richTextParser = renderComponent({}, 'test');
27
+ expect(richTextParser).toHaveLength(1);
28
+ });
29
+ it('should have rendered a result with the style prop', () => {
30
+ richTextParser = renderComponent(defaultProps, 'test prop');
31
+ expect(richTextParser.props().style).toEqual(defaultProps.style);
32
+ });
33
+ it('should have rendered content', () => {
34
+ richTextParser = renderComponent({}, 'plain text');
35
+ expect(richTextParser.html()).toEqual('<div>converted text</div>');
36
+ });
37
+ it('should return null if no children is passed', () => {
38
+ richTextParser = renderComponent({}, undefined);
39
+ expect(richTextParser.html()).toBe(null);
40
+ });
41
+ });
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "RichTextEditor", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _Editor.Editor;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "RichTextMdParser", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _MdParser.MdParser;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "RichTextParser", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _Parser.Parser;
22
+ }
23
+ });
24
+ var _Editor = require("./Editor/Editor.js");
25
+ var _Parser = require("./Parser/Parser.js");
26
+ var _MdParser = require("./Parser/MdParser.js");
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = exports.UserMentionWrapper = void 0;
6
+ exports.UserMentionWrapper = void 0;
7
7
  var _style = _interopRequireDefault(require("styled-jsx/style"));
8
8
  var _d2I18n = _interopRequireDefault(require("@dhis2/d2-i18n"));
9
9
  var _ui = require("@dhis2/ui");
@@ -38,6 +38,7 @@ const UserMentionWrapper = _ref => {
38
38
  inputReference,
39
39
  onUserSelect
40
40
  } = _ref;
41
+ const [listIsOpen, setListIsOpen] = (0, _react.useState)(false);
41
42
  const [captureText, setCaptureText] = (0, _react.useState)(false);
42
43
  const [capturedText, setCapturedText] = (0, _react.useState)('');
43
44
  const [cloneText, setCloneText] = (0, _react.useState)('');
@@ -53,6 +54,7 @@ const UserMentionWrapper = _ref => {
53
54
  searchText: capturedText
54
55
  });
55
56
  const reset = () => {
57
+ setListIsOpen(false);
56
58
  setCaptureText(false);
57
59
  setCapturedText('');
58
60
  setCloneText('');
@@ -61,6 +63,12 @@ const UserMentionWrapper = _ref => {
61
63
  clear();
62
64
  };
63
65
 
66
+ // focus the input/textarea when the user list is closed by clicking above the input/textarea
67
+ const onClick = () => inputReference.current.focus();
68
+
69
+ // close the user list when clicking in the input/textarea or outside of it (input/textarea blur)
70
+ const onUserListClose = () => reset();
71
+
64
72
  // event bubbles up from the input/textarea
65
73
  const onInput = _ref2 => {
66
74
  let {
@@ -73,7 +81,7 @@ const UserMentionWrapper = _ref => {
73
81
  if (captureText) {
74
82
  clear();
75
83
  const spacePosition = value.indexOf(' ', captureStartPosition - 1);
76
- const filterValue = value.substring(captureStartPosition, spacePosition > 0 ? spacePosition : selectionEnd + 1);
84
+ const filterValue = value.substring(captureStartPosition, spacePosition > 0 ? spacePosition : selectionEnd + 1).replace(/\n+/, '');
77
85
  if (filterValue !== capturedText) {
78
86
  setCapturedText(filterValue);
79
87
  } else if (filterValue.length === 0) {
@@ -93,6 +101,7 @@ const UserMentionWrapper = _ref => {
93
101
  selectionStart
94
102
  } = target;
95
103
  if (!captureText && key === '@') {
104
+ setListIsOpen(true);
96
105
  setCaptureText(true);
97
106
  setCaptureStartPosition(selectionStart + 1);
98
107
  setCloneText(target.value.substring(0, selectionStart) + '@');
@@ -144,17 +153,20 @@ const UserMentionWrapper = _ref => {
144
153
  // position the cursor at the end
145
154
  requestAnimationFrame(() => inputReference.current.setSelectionRange(-1, -1), 0);
146
155
  };
147
- const onClick = user => () => onSelect(user);
156
+ const onUserClick = user => () => onSelect(user);
148
157
  return /*#__PURE__*/_react.default.createElement("div", {
149
158
  onKeyDown: onKeyDown,
150
159
  onInput: onInput,
160
+ onClick: onClick,
151
161
  className: `jsx-${_UserMentionWrapperStyle.userMentionWrapperClasses.__hash}` + " " + "wrapper"
152
162
  }, children, /*#__PURE__*/_react.default.createElement("div", {
153
163
  className: `jsx-${_UserMentionWrapperStyle.userMentionWrapperClasses.__hash}` + " " + "clone"
154
- }, /*#__PURE__*/_react.default.createElement("pre", {
164
+ }, /*#__PURE__*/_react.default.createElement("p", {
155
165
  ref: cloneRef,
156
166
  className: `jsx-${_UserMentionWrapperStyle.userMentionWrapperClasses.__hash}`
157
- }, cloneText)), captureText && /*#__PURE__*/_react.default.createElement(_ui.Portal, null, /*#__PURE__*/_react.default.createElement(_ui.Popper, {
167
+ }, cloneText)), listIsOpen && /*#__PURE__*/_react.default.createElement(_ui.Layer, {
168
+ onBackdropClick: onUserListClose
169
+ }, /*#__PURE__*/_react.default.createElement(_ui.Popper, {
158
170
  reference: getVirtualPopperReference(cloneRef),
159
171
  placement: "top-start"
160
172
  }, /*#__PURE__*/_react.default.createElement(_ui.Card, null, /*#__PURE__*/_react.default.createElement("div", {
@@ -175,7 +187,7 @@ const UserMentionWrapper = _ref => {
175
187
  }), !fetching && users.length > 0 && /*#__PURE__*/_react.default.createElement(_UserList.UserList, {
176
188
  users: users,
177
189
  selectedUserIndex: selectedUserIndex,
178
- onUserClick: onClick,
190
+ onUserClick: onUserClick,
179
191
  pager: pager
180
192
  }), capturedText && !fetching && users.length === 0 && /*#__PURE__*/_react.default.createElement(_ui.MenuItem, {
181
193
  dense: true,
@@ -193,6 +205,4 @@ UserMentionWrapper.propTypes = {
193
205
  inputReference: _propTypes.default.object.isRequired,
194
206
  onUserSelect: _propTypes.default.func.isRequired,
195
207
  children: _propTypes.default.node
196
- };
197
- var _default = UserMentionWrapper;
198
- exports.default = _default;
208
+ };
@@ -13,9 +13,9 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
13
13
  * to emulate the styles of the textarea. If we decide to make
14
14
  * changes there, they should be refelcted here too.
15
15
  */
16
- const userMentionWrapperClasses = [".wrapper.jsx-1289989717{position:relative;}", `.clone.jsx-1289989717{position:absolute;visibility:hidden;inset:0;box-sizing:border-box;padding:${_ui.spacers.dp8} ${_ui.spacers.dp12};border:1px solid ${_ui.colors.grey500};font-size:14px;line-height:${_ui.spacers.dp16};z-index:1;pointer-events:none;}`, ".clone.jsx-1289989717>pre.jsx-1289989717{display:inline;word-wrap:break-word;overflow-wrap:break-word;font:inherit;margin:0;}", `.container.jsx-1289989717{background-color:${_ui.colors.white};max-height:180px;overflow:auto;}`];
16
+ const userMentionWrapperClasses = [".wrapper.jsx-2386066169{width:100%;height:100%;position:relative;}", `.clone.jsx-2386066169{position:absolute;visibility:hidden;inset:0;box-sizing:border-box;padding:${_ui.spacers.dp8} 15px;border:1px solid ${_ui.colors.grey500};font-size:14px;line-height:${_ui.spacers.dp16};z-index:1;pointer-events:none;}`, ".clone.jsx-2386066169>p.jsx-2386066169{display:inline;word-wrap:break-word;overflow-wrap:break-word;font:inherit;margin:0;white-space:break-spaces;}", `.container.jsx-2386066169{background-color:${_ui.colors.white};max-height:180px;overflow:auto;}`];
17
17
  exports.userMentionWrapperClasses = userMentionWrapperClasses;
18
- userMentionWrapperClasses.__hash = "1289989717";
18
+ userMentionWrapperClasses.__hash = "2386066169";
19
19
  const resolvedHeaderStyle = {
20
20
  styles: /*#__PURE__*/_react.default.createElement(_style.default, {
21
21
  id: "4275958396"
@@ -53,10 +53,10 @@ const useUserSearchResults = _ref2 => {
53
53
  return () => debouncedRefetch.cancel();
54
54
  }, [searchText, debouncedRefetch]);
55
55
  (0, _react.useEffect)(() => {
56
- if (data) {
56
+ if (fetching === false && data) {
57
57
  setData(data.users);
58
58
  }
59
- }, [data]);
59
+ }, [data, fetching]);
60
60
  return {
61
61
  users,
62
62
  pager,