@dhis2/analytics 26.2.0-alpha.2 → 26.2.0-cumulative-values-alpha.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.
- package/build/cjs/__demo__/PivotTable.stories.js +69 -29
- package/build/cjs/api/analytics/Analytics.js +0 -7
- package/build/cjs/api/analytics/AnalyticsBase.js +6 -24
- package/build/cjs/api/analytics/AnalyticsRequest.js +10 -33
- package/build/cjs/api/analytics/AnalyticsRequestBase.js +1 -3
- package/build/cjs/api/analytics/AnalyticsRequestPropertiesMixin.js +0 -19
- package/build/cjs/api/analytics/__tests__/AnalyticsTrackedEntities.spec.js +44 -0
- package/build/cjs/api/analytics/__tests__/__snapshots__/AnalyticsTrackedEntities.spec.js.snap +3 -0
- package/build/cjs/api/analytics/utils.js +2 -23
- package/build/cjs/components/Options/VisualizationOptions.js +1 -1
- package/build/cjs/components/Options/styles/VisualizationOptions.style.js +8 -1
- package/build/cjs/components/RichText/Editor.bk/Editor.js +40 -0
- package/build/cjs/components/RichText/Editor.bk/__tests__/Editor.spec.js +29 -0
- package/build/cjs/components/RichText/Editor.bk/__tests__/convertCtrlKey.spec.js +205 -0
- package/build/cjs/components/RichText/Editor.bk/convertCtrlKey.js +87 -0
- package/build/cjs/components/RichText/Parser.bk/MdParser.js +107 -0
- package/build/cjs/components/RichText/Parser.bk/Parser.js +34 -0
- package/build/cjs/components/RichText/Parser.bk/__tests__/MdParser.spec.js +34 -0
- package/build/cjs/components/RichText/Parser.bk/__tests__/Parser.spec.js +41 -0
- package/build/cjs/modules/layout/dimension.js +2 -9
- package/build/cjs/modules/layout/dimensionCreate.js +0 -3
- package/build/cjs/modules/pivotTable/PivotTableEngine.js +119 -57
- package/build/cjs/visualizations/config/generators/dhis/singleValue.js.xp1 +478 -0
- package/build/es/__demo__/PivotTable.stories.js +69 -29
- package/build/es/api/analytics/Analytics.js +0 -7
- package/build/es/api/analytics/AnalyticsBase.js +6 -24
- package/build/es/api/analytics/AnalyticsRequest.js +10 -33
- package/build/es/api/analytics/AnalyticsRequestBase.js +1 -3
- package/build/es/api/analytics/AnalyticsRequestPropertiesMixin.js +0 -19
- package/build/es/api/analytics/__tests__/AnalyticsTrackedEntities.spec.js +41 -0
- package/build/es/api/analytics/__tests__/__snapshots__/AnalyticsTrackedEntities.spec.js.snap +3 -0
- package/build/es/api/analytics/utils.js +1 -20
- package/build/es/components/Options/VisualizationOptions.js +2 -2
- package/build/es/components/Options/styles/VisualizationOptions.style.js +6 -0
- package/build/es/components/RichText/Editor.bk/Editor.js +30 -0
- package/build/es/components/RichText/Editor.bk/__tests__/Editor.spec.js +26 -0
- package/build/es/components/RichText/Editor.bk/__tests__/convertCtrlKey.spec.js +202 -0
- package/build/es/components/RichText/Editor.bk/convertCtrlKey.js +80 -0
- package/build/es/components/RichText/Parser.bk/MdParser.js +99 -0
- package/build/es/components/RichText/Parser.bk/Parser.js +24 -0
- package/build/es/components/RichText/Parser.bk/__tests__/MdParser.spec.js +31 -0
- package/build/es/components/RichText/Parser.bk/__tests__/Parser.spec.js +38 -0
- package/build/es/modules/layout/dimension.js +1 -7
- package/build/es/modules/layout/dimensionCreate.js +1 -4
- package/build/es/modules/pivotTable/PivotTableEngine.js +119 -57
- package/build/es/visualizations/config/generators/dhis/singleValue.js.xp1 +478 -0
- package/package.json +1 -1
- package/build/cjs/api/analytics/AnalyticsTrackedEntities.js +0 -31
- package/build/es/api/analytics/AnalyticsTrackedEntities.js +0 -24
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const state = {
|
|
2
|
+
boldMode: false,
|
|
3
|
+
italicMode: false,
|
|
4
|
+
element: null
|
|
5
|
+
};
|
|
6
|
+
const markerMap = {
|
|
7
|
+
italic: '_',
|
|
8
|
+
bold: '*'
|
|
9
|
+
};
|
|
10
|
+
const trim = str => {
|
|
11
|
+
const leftSpaces = /^\s+/;
|
|
12
|
+
const rightSpaces = /\s+$/;
|
|
13
|
+
return str.replace(leftSpaces, '').replace(rightSpaces, '');
|
|
14
|
+
};
|
|
15
|
+
const toggleMode = mode => {
|
|
16
|
+
const prop = `${mode}Mode`;
|
|
17
|
+
state[prop] = !state[prop];
|
|
18
|
+
};
|
|
19
|
+
const insertMarkers = (mode, cb) => {
|
|
20
|
+
const {
|
|
21
|
+
selectionStart: start,
|
|
22
|
+
selectionEnd: end,
|
|
23
|
+
value
|
|
24
|
+
} = state.element;
|
|
25
|
+
const marker = markerMap[mode] || null;
|
|
26
|
+
if (!marker || !cb || start < 0) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
toggleMode(mode);
|
|
30
|
+
let newValue;
|
|
31
|
+
let caretPos = end + 1;
|
|
32
|
+
const padMarkers = text => {
|
|
33
|
+
// is caret between two markers (i.e., "**" or "__")? Then do not add padding
|
|
34
|
+
if (start === end && value.length && start > 0) {
|
|
35
|
+
if (value[start - 1] === markerMap.bold && value[start] === markerMap.bold || value[start - 1] === markerMap.italic && value[start] === markerMap.italic) {
|
|
36
|
+
return text;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (value.length && start > 0 && value[start - 1] !== ' ') {
|
|
40
|
+
text = ` ${text}`;
|
|
41
|
+
++caretPos;
|
|
42
|
+
}
|
|
43
|
+
if (value.length && end !== value.length && value[end] !== ' ') {
|
|
44
|
+
text = `${text} `;
|
|
45
|
+
}
|
|
46
|
+
return text;
|
|
47
|
+
};
|
|
48
|
+
if (start === end) {
|
|
49
|
+
//no text
|
|
50
|
+
const valueArr = value.split('');
|
|
51
|
+
valueArr.splice(start, 0, padMarkers(`${marker}${marker}`));
|
|
52
|
+
newValue = valueArr.join('');
|
|
53
|
+
} else {
|
|
54
|
+
const text = value.slice(start, end);
|
|
55
|
+
const trimmedText = trim(text);
|
|
56
|
+
|
|
57
|
+
// adjust caretPos based on trimmed text selection
|
|
58
|
+
caretPos = caretPos - (text.length - trimmedText.length) + 1;
|
|
59
|
+
newValue = [value.slice(0, start), padMarkers(`${marker}${trimmedText}${marker}`), value.slice(end)].join('');
|
|
60
|
+
toggleMode(mode);
|
|
61
|
+
}
|
|
62
|
+
cb(newValue, caretPos);
|
|
63
|
+
};
|
|
64
|
+
const convertCtrlKey = (event, cb) => {
|
|
65
|
+
const {
|
|
66
|
+
key,
|
|
67
|
+
ctrlKey,
|
|
68
|
+
metaKey
|
|
69
|
+
} = event;
|
|
70
|
+
const element = event.target;
|
|
71
|
+
state.element = element;
|
|
72
|
+
if (key === 'b' && (ctrlKey || metaKey)) {
|
|
73
|
+
event.preventDefault();
|
|
74
|
+
insertMarkers('bold', cb);
|
|
75
|
+
} else if (key === 'i' && (ctrlKey || metaKey)) {
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
insertMarkers('italic', cb);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
export default convertCtrlKey;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import MarkdownIt from 'markdown-it';
|
|
2
|
+
const emojiDb = {
|
|
3
|
+
':-)': '\u{1F642}',
|
|
4
|
+
':)': '\u{1F642}',
|
|
5
|
+
':-(': '\u{1F641}',
|
|
6
|
+
':(': '\u{1F641}',
|
|
7
|
+
':+1': '\u{1F44D}',
|
|
8
|
+
':-1': '\u{1F44E}'
|
|
9
|
+
};
|
|
10
|
+
const codes = {
|
|
11
|
+
bold: {
|
|
12
|
+
name: 'bold',
|
|
13
|
+
char: '*',
|
|
14
|
+
domEl: 'strong',
|
|
15
|
+
encodedChar: 0x2a,
|
|
16
|
+
// see https://regex101.com/r/evswdV/8 for explanation of regexp
|
|
17
|
+
regexString: '\\B\\*((?!\\s)[^*]+(?:\\b|[^*\\s]))\\*\\B',
|
|
18
|
+
contentFn: val => val
|
|
19
|
+
},
|
|
20
|
+
italic: {
|
|
21
|
+
name: 'italic',
|
|
22
|
+
char: '_',
|
|
23
|
+
domEl: 'em',
|
|
24
|
+
encodedChar: 0x5f,
|
|
25
|
+
// see https://regex101.com/r/p6LpjK/6 for explanation of regexp
|
|
26
|
+
regexString: '\\b_((?!\\s)[^_]+(?:\\B|[^_\\s]))_\\b',
|
|
27
|
+
contentFn: val => val
|
|
28
|
+
},
|
|
29
|
+
emoji: {
|
|
30
|
+
name: 'emoji',
|
|
31
|
+
char: ':',
|
|
32
|
+
domEl: 'span',
|
|
33
|
+
encodedChar: 0x3a,
|
|
34
|
+
regexString: '^(:-\\)|:\\)|:\\(|:-\\(|:\\+1|:-1)',
|
|
35
|
+
contentFn: val => emojiDb[val]
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
let md;
|
|
39
|
+
let linksInText;
|
|
40
|
+
const markerIsInLinkText = pos => linksInText.some(link => pos >= link.index && pos <= link.lastIndex);
|
|
41
|
+
const parse = code => (state, silent) => {
|
|
42
|
+
if (silent) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
const start = state.pos;
|
|
46
|
+
|
|
47
|
+
// skip parsing emphasis if marker is within a link
|
|
48
|
+
if (markerIsInLinkText(start)) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const marker = state.src.charCodeAt(start);
|
|
52
|
+
|
|
53
|
+
// marker character: "_", "*", ":"
|
|
54
|
+
if (marker !== codes[code].encodedChar) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
const MARKER_REGEX = new RegExp(codes[code].regexString);
|
|
58
|
+
const token = state.src.slice(start);
|
|
59
|
+
if (MARKER_REGEX.test(token)) {
|
|
60
|
+
const markerMatch = token.match(MARKER_REGEX);
|
|
61
|
+
|
|
62
|
+
// skip parsing sections where the marker is not at the start of the token
|
|
63
|
+
if (markerMatch.index !== 0) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
const text = markerMatch[1];
|
|
67
|
+
state.push(`${codes[code].domEl}_open`, codes[code].domEl, 1);
|
|
68
|
+
const t = state.push('text', '', 0);
|
|
69
|
+
t.content = codes[code].contentFn(text);
|
|
70
|
+
state.push(`${codes.bold.domEl}_close`, codes[code].domEl, -1);
|
|
71
|
+
state.pos += markerMatch[0].length;
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
};
|
|
76
|
+
class MdParser {
|
|
77
|
+
constructor() {
|
|
78
|
+
// disable all rules, enable autolink for URLs and email addresses
|
|
79
|
+
md = new MarkdownIt('zero', {
|
|
80
|
+
linkify: true
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// *bold* -> <strong>bold</strong>
|
|
84
|
+
md.inline.ruler.push('strong', parse(codes.bold.name));
|
|
85
|
+
|
|
86
|
+
// _italic_ -> <em>italic</em>
|
|
87
|
+
md.inline.ruler.push('italic', parse(codes.italic.name));
|
|
88
|
+
|
|
89
|
+
// :-) :) :-( :( :+1 :-1 -> <span>[unicode]</span>
|
|
90
|
+
md.inline.ruler.push('emoji', parse(codes.emoji.name));
|
|
91
|
+
md.enable(['link', 'linkify', 'strong', 'italic', 'emoji']);
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
render(text) {
|
|
95
|
+
linksInText = md.linkify.match(text) || [];
|
|
96
|
+
return md.renderInline(text);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
export default MdParser;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
import React, { useMemo } from 'react';
|
|
3
|
+
import MdParserClass from './MdParser.js';
|
|
4
|
+
const Parser = _ref => {
|
|
5
|
+
let {
|
|
6
|
+
children,
|
|
7
|
+
style
|
|
8
|
+
} = _ref;
|
|
9
|
+
const MdParser = useMemo(() => new MdParserClass(), []);
|
|
10
|
+
return children ? /*#__PURE__*/React.createElement("p", {
|
|
11
|
+
style: style,
|
|
12
|
+
dangerouslySetInnerHTML: {
|
|
13
|
+
__html: MdParser.render(children)
|
|
14
|
+
}
|
|
15
|
+
}) : null;
|
|
16
|
+
};
|
|
17
|
+
Parser.defaultProps = {
|
|
18
|
+
style: null
|
|
19
|
+
};
|
|
20
|
+
Parser.propTypes = {
|
|
21
|
+
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
|
|
22
|
+
style: PropTypes.object
|
|
23
|
+
};
|
|
24
|
+
export default Parser;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import MdParser from '../MdParser.js';
|
|
2
|
+
const Parser = new MdParser();
|
|
3
|
+
describe('MdParser class', () => {
|
|
4
|
+
it('converts text into HTML', () => {
|
|
5
|
+
const tests = [['_italic_', '<em>italic</em>'], ['*bold*', '<strong>bold</strong>'], ['* not bold because there is a space *', '* not bold because there is a space *'], ['_ 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>'],
|
|
6
|
+
// italic marker followed by : should work
|
|
7
|
+
['_italic_:', '<em>italic</em>:'], ['_italic_: some text, *bold*: some other text', '<em>italic</em>: some text, <strong>bold</strong>: some other text'],
|
|
8
|
+
// bold marker followed by : should work
|
|
9
|
+
['*bold*:', '<strong>bold</strong>:'], ['*bold*: some text, _italic_: some other text', '<strong>bold</strong>: some text, <em>italic</em>: some other text'],
|
|
10
|
+
// italic marker inside an italic string not allowed
|
|
11
|
+
['_italic with _ inside_', '_italic with _ inside_'],
|
|
12
|
+
// bold marker inside a bold string not allowed
|
|
13
|
+
['*bold with * inside*', '*bold with * inside*'], ['_multiple_ italic in the _same line_', '<em>multiple</em> italic in the <em>same line</em>'],
|
|
14
|
+
// nested italic/bold combinations not allowed
|
|
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
|
+
// links
|
|
17
|
+
['example.com/path', '<a href="http://example.com/path">example.com/path</a>'],
|
|
18
|
+
// not recognized links with italic marker inside not converted
|
|
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
|
+
// markers around non-recognized links
|
|
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
|
+
// 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>'],
|
|
24
|
+
// italic/bold markers right after non-word characters
|
|
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
|
+
tests.forEach(test => {
|
|
27
|
+
const renderedText = Parser.render(test[0]);
|
|
28
|
+
expect(renderedText).toEqual(test[1]);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { shallow } from 'enzyme';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import Parser from '../Parser.js';
|
|
4
|
+
import '../MdParser.js';
|
|
5
|
+
jest.mock('../MdParser', () => {
|
|
6
|
+
return jest.fn().mockImplementation(() => {
|
|
7
|
+
return {
|
|
8
|
+
render: () => 'converted text'
|
|
9
|
+
};
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
describe('RichText: Parser component', () => {
|
|
13
|
+
let richTextParser;
|
|
14
|
+
const defaultProps = {
|
|
15
|
+
style: {
|
|
16
|
+
color: 'blue'
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const renderComponent = (props, text) => {
|
|
20
|
+
return shallow( /*#__PURE__*/React.createElement(Parser, props, text));
|
|
21
|
+
};
|
|
22
|
+
it('should have rendered a result', () => {
|
|
23
|
+
richTextParser = renderComponent({}, 'test');
|
|
24
|
+
expect(richTextParser).toHaveLength(1);
|
|
25
|
+
});
|
|
26
|
+
it('should have rendered a result with the style prop', () => {
|
|
27
|
+
richTextParser = renderComponent(defaultProps, 'test prop');
|
|
28
|
+
expect(richTextParser.props().style).toEqual(defaultProps.style);
|
|
29
|
+
});
|
|
30
|
+
it('should have rendered content', () => {
|
|
31
|
+
richTextParser = renderComponent({}, 'plain text');
|
|
32
|
+
expect(richTextParser.html()).toEqual('<p>converted text</p>');
|
|
33
|
+
});
|
|
34
|
+
it('should return null if no children is passed', () => {
|
|
35
|
+
richTextParser = renderComponent({}, undefined);
|
|
36
|
+
expect(richTextParser.html()).toBe(null);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -33,12 +33,6 @@ export const DIMENSION_PROP_LEGEND_SET = {
|
|
|
33
33
|
required: false,
|
|
34
34
|
isValid: prop => isString(prop)
|
|
35
35
|
};
|
|
36
|
-
export const DIMENSION_PROP_PROGRAM = {
|
|
37
|
-
name: 'program',
|
|
38
|
-
defaultValue: {},
|
|
39
|
-
required: false,
|
|
40
|
-
isValid: prop => isObject(prop)
|
|
41
|
-
};
|
|
42
36
|
export const DIMENSION_PROP_PROGRAM_STAGE = {
|
|
43
37
|
name: 'programStage',
|
|
44
38
|
defaultValue: {},
|
|
@@ -51,4 +45,4 @@ export const DIMENSION_PROP_REPETITION = {
|
|
|
51
45
|
required: false,
|
|
52
46
|
isValid: prop => Array.isArray(prop)
|
|
53
47
|
};
|
|
54
|
-
export const DIMENSION_PROPS = [DIMENSION_PROP_ID, DIMENSION_PROP_ITEMS, DIMENSION_PROP_FILTER, DIMENSION_PROP_LEGEND_SET,
|
|
48
|
+
export const DIMENSION_PROPS = [DIMENSION_PROP_ID, DIMENSION_PROP_ITEMS, DIMENSION_PROP_FILTER, DIMENSION_PROP_LEGEND_SET, DIMENSION_PROP_PROGRAM_STAGE, DIMENSION_PROP_REPETITION];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DIMENSION_PROP_ID, DIMENSION_PROP_ITEMS, DIMENSION_PROP_FILTER, DIMENSION_PROP_LEGEND_SET,
|
|
1
|
+
import { DIMENSION_PROP_ID, DIMENSION_PROP_ITEMS, DIMENSION_PROP_FILTER, DIMENSION_PROP_LEGEND_SET, DIMENSION_PROP_PROGRAM_STAGE, DIMENSION_PROP_REPETITION } from './dimension.js';
|
|
2
2
|
export const dimensionCreate = function (dimensionId) {
|
|
3
3
|
let itemIds = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
4
4
|
let args = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
@@ -15,9 +15,6 @@ export const dimensionCreate = function (dimensionId) {
|
|
|
15
15
|
...(args.legendSet && {
|
|
16
16
|
[DIMENSION_PROP_LEGEND_SET.name]: args.legendSet
|
|
17
17
|
}),
|
|
18
|
-
...(args.program && {
|
|
19
|
-
[DIMENSION_PROP_PROGRAM.name]: args.program
|
|
20
|
-
}),
|
|
21
18
|
...(args.programStage && {
|
|
22
19
|
[DIMENSION_PROP_PROGRAM_STAGE.name]: args.programStage
|
|
23
20
|
}),
|
|
@@ -20,7 +20,8 @@ const defaultOptions = {
|
|
|
20
20
|
showRowSubtotals: false,
|
|
21
21
|
showColumnSubtotals: false,
|
|
22
22
|
fixColumnHeaders: false,
|
|
23
|
-
fixRowHeaders: false
|
|
23
|
+
fixRowHeaders: false,
|
|
24
|
+
cumulativeValues: false
|
|
24
25
|
};
|
|
25
26
|
const defaultVisualizationProps = {
|
|
26
27
|
fontSize: FONT_SIZE_OPTION_NORMAL,
|
|
@@ -175,6 +176,9 @@ export class PivotTableEngine {
|
|
|
175
176
|
_defineProperty(this, "data", []);
|
|
176
177
|
_defineProperty(this, "rowMap", []);
|
|
177
178
|
_defineProperty(this, "columnMap", []);
|
|
179
|
+
_defineProperty(this, "accumulators", {
|
|
180
|
+
rows: {}
|
|
181
|
+
});
|
|
178
182
|
this.visualization = Object.assign({}, defaultVisualizationProps, visualization);
|
|
179
183
|
this.legendSets = (legendSets || []).reduce((sets, set) => {
|
|
180
184
|
sets[set.id] = set;
|
|
@@ -194,7 +198,8 @@ export class PivotTableEngine {
|
|
|
194
198
|
subtitle: visualization.hideSubtitle ? undefined : visualization.subtitle,
|
|
195
199
|
// turn on fixed headers only when there are dimensions
|
|
196
200
|
fixColumnHeaders: this.dimensionLookup.columns.length ? visualization.fixColumnHeaders : false,
|
|
197
|
-
fixRowHeaders: this.dimensionLookup.rows.length ? visualization.fixRowHeaders : false
|
|
201
|
+
fixRowHeaders: this.dimensionLookup.rows.length ? visualization.fixRowHeaders : false,
|
|
202
|
+
cumulativeValues: visualization.cumulativeValues
|
|
198
203
|
};
|
|
199
204
|
this.adaptiveClippingController = new AdaptiveClippingController(this);
|
|
200
205
|
const doColumnSubtotals = this.options.showColumnSubtotals && this.dimensionLookup.rows.length > 1;
|
|
@@ -218,51 +223,68 @@ export class PivotTableEngine {
|
|
|
218
223
|
row,
|
|
219
224
|
column
|
|
220
225
|
});
|
|
226
|
+
const valueType = (dxDimension === null || dxDimension === void 0 ? void 0 : dxDimension.valueType) || VALUE_TYPE_TEXT;
|
|
221
227
|
const headers = [...this.getRawRowHeader(row), ...this.getRawColumnHeader(column)];
|
|
222
228
|
const peId = (_headers$find = headers.find(header => (header === null || header === void 0 ? void 0 : header.dimensionItemType) === DIMENSION_TYPE_PERIOD)) === null || _headers$find === void 0 ? void 0 : _headers$find.uid;
|
|
223
229
|
const ouId = (_headers$find2 = headers.find(header => (header === null || header === void 0 ? void 0 : header.dimensionItemType) === DIMENSION_TYPE_ORGANISATION_UNIT)) === null || _headers$find2 === void 0 ? void 0 : _headers$find2.uid;
|
|
224
|
-
|
|
225
|
-
return {
|
|
226
|
-
cellType,
|
|
227
|
-
empty: true,
|
|
228
|
-
ouId,
|
|
229
|
-
peId
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
const dataRow = this.data[row][column];
|
|
233
|
-
let rawValue = cellType === CELL_TYPE_VALUE ? dataRow[this.dimensionLookup.dataHeaders.value] : dataRow.value;
|
|
234
|
-
let renderedValue = rawValue;
|
|
235
|
-
const valueType = (dxDimension === null || dxDimension === void 0 ? void 0 : dxDimension.valueType) || VALUE_TYPE_TEXT;
|
|
236
|
-
if (valueType === VALUE_TYPE_NUMBER) {
|
|
237
|
-
rawValue = parseValue(rawValue);
|
|
238
|
-
switch (this.visualization.numberType) {
|
|
239
|
-
case NUMBER_TYPE_ROW_PERCENTAGE:
|
|
240
|
-
renderedValue = rawValue / this.percentageTotals[row].value;
|
|
241
|
-
break;
|
|
242
|
-
case NUMBER_TYPE_COLUMN_PERCENTAGE:
|
|
243
|
-
renderedValue = rawValue / this.percentageTotals[column].value;
|
|
244
|
-
break;
|
|
245
|
-
default:
|
|
246
|
-
break;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
renderedValue = renderValue(renderedValue, valueType, this.visualization);
|
|
250
|
-
return {
|
|
230
|
+
const rawCell = {
|
|
251
231
|
cellType,
|
|
252
|
-
empty: false,
|
|
253
232
|
valueType,
|
|
254
|
-
rawValue,
|
|
255
|
-
renderedValue,
|
|
256
|
-
dxDimension,
|
|
257
233
|
ouId,
|
|
258
234
|
peId
|
|
259
235
|
};
|
|
236
|
+
if (!this.data[row] || !this.data[row][column]) {
|
|
237
|
+
rawCell.empty = true;
|
|
238
|
+
} else {
|
|
239
|
+
const dataRow = this.data[row][column];
|
|
240
|
+
let rawValue = cellType === CELL_TYPE_VALUE ? dataRow[this.dimensionLookup.dataHeaders.value] : dataRow.value;
|
|
241
|
+
let renderedValue = rawValue;
|
|
242
|
+
if (valueType === VALUE_TYPE_NUMBER) {
|
|
243
|
+
rawValue = parseValue(rawValue);
|
|
244
|
+
switch (this.visualization.numberType) {
|
|
245
|
+
case NUMBER_TYPE_ROW_PERCENTAGE:
|
|
246
|
+
renderedValue = rawValue / this.percentageTotals[row].value;
|
|
247
|
+
break;
|
|
248
|
+
case NUMBER_TYPE_COLUMN_PERCENTAGE:
|
|
249
|
+
renderedValue = rawValue / this.percentageTotals[column].value;
|
|
250
|
+
break;
|
|
251
|
+
default:
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
renderedValue = renderValue(renderedValue, valueType, this.visualization);
|
|
256
|
+
rawCell.dxDimension = dxDimension;
|
|
257
|
+
rawCell.empty = false;
|
|
258
|
+
rawCell.rawValue = rawValue;
|
|
259
|
+
rawCell.renderedValue = renderedValue;
|
|
260
|
+
}
|
|
261
|
+
if (this.options.cumulativeValues) {
|
|
262
|
+
const cumulativeValue = this.getCumulative({
|
|
263
|
+
row,
|
|
264
|
+
column
|
|
265
|
+
});
|
|
266
|
+
if (cumulativeValue !== undefined && cumulativeValue !== null) {
|
|
267
|
+
// force to NUMBER for accumulated values
|
|
268
|
+
rawCell.valueType = valueType === undefined || valueType === null ? VALUE_TYPE_NUMBER : valueType;
|
|
269
|
+
rawCell.empty = false;
|
|
270
|
+
rawCell.rawValue = cumulativeValue;
|
|
271
|
+
rawCell.renderedValue = renderValue(cumulativeValue, valueType, this.visualization);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return rawCell;
|
|
260
275
|
}
|
|
261
|
-
|
|
276
|
+
getCumulative(_ref4) {
|
|
262
277
|
let {
|
|
263
278
|
row,
|
|
264
279
|
column
|
|
265
280
|
} = _ref4;
|
|
281
|
+
return this.accumulators.rows[row][column];
|
|
282
|
+
}
|
|
283
|
+
get(_ref5) {
|
|
284
|
+
let {
|
|
285
|
+
row,
|
|
286
|
+
column
|
|
287
|
+
} = _ref5;
|
|
266
288
|
const mappedRow = this.rowMap[row],
|
|
267
289
|
mappedColumn = this.columnMap[column];
|
|
268
290
|
if (!mappedRow && mappedRow !== 0 || !mappedColumn && mappedColumn !== 0) {
|
|
@@ -273,11 +295,11 @@ export class PivotTableEngine {
|
|
|
273
295
|
column: mappedColumn
|
|
274
296
|
});
|
|
275
297
|
}
|
|
276
|
-
getRawCellType(
|
|
298
|
+
getRawCellType(_ref6) {
|
|
277
299
|
let {
|
|
278
300
|
row,
|
|
279
301
|
column
|
|
280
|
-
} =
|
|
302
|
+
} = _ref6;
|
|
281
303
|
const isRowTotal = this.doRowTotals && column === this.dataWidth - 1;
|
|
282
304
|
const isColumnTotal = this.doColumnTotals && row === this.dataHeight - 1;
|
|
283
305
|
if (isRowTotal || isColumnTotal) {
|
|
@@ -290,11 +312,11 @@ export class PivotTableEngine {
|
|
|
290
312
|
}
|
|
291
313
|
return CELL_TYPE_VALUE;
|
|
292
314
|
}
|
|
293
|
-
getCellType(
|
|
315
|
+
getCellType(_ref7) {
|
|
294
316
|
let {
|
|
295
317
|
row,
|
|
296
318
|
column
|
|
297
|
-
} =
|
|
319
|
+
} = _ref7;
|
|
298
320
|
row = this.rowMap[row];
|
|
299
321
|
column = this.columnMap[column];
|
|
300
322
|
return this.getRawCellType({
|
|
@@ -324,29 +346,26 @@ export class PivotTableEngine {
|
|
|
324
346
|
return i18n.t(this.dimensionLookup.rows[rowLevel].meta.name);
|
|
325
347
|
}
|
|
326
348
|
}
|
|
327
|
-
getCellDxDimension(
|
|
349
|
+
getCellDxDimension(_ref8) {
|
|
328
350
|
let {
|
|
329
351
|
row,
|
|
330
352
|
column
|
|
331
|
-
} =
|
|
353
|
+
} = _ref8;
|
|
332
354
|
return this.getRawCellDxDimension({
|
|
333
355
|
row: this.rowMap[row],
|
|
334
356
|
column: this.columnMap[column]
|
|
335
357
|
});
|
|
336
358
|
}
|
|
337
|
-
getRawCellDxDimension(
|
|
359
|
+
getRawCellDxDimension(_ref9) {
|
|
338
360
|
let {
|
|
339
361
|
row,
|
|
340
362
|
column
|
|
341
|
-
} =
|
|
363
|
+
} = _ref9;
|
|
342
364
|
if (!this.data[row]) {
|
|
343
365
|
return undefined;
|
|
344
366
|
}
|
|
345
367
|
const cellValue = this.data[row][column];
|
|
346
|
-
if (!cellValue) {
|
|
347
|
-
return undefined;
|
|
348
|
-
}
|
|
349
|
-
if (!Array.isArray(cellValue)) {
|
|
368
|
+
if (cellValue && !Array.isArray(cellValue)) {
|
|
350
369
|
// This is a total cell
|
|
351
370
|
return {
|
|
352
371
|
valueType: cellValue.valueType,
|
|
@@ -373,6 +392,11 @@ export class PivotTableEngine {
|
|
|
373
392
|
};
|
|
374
393
|
}
|
|
375
394
|
|
|
395
|
+
// Empty cell
|
|
396
|
+
// The cell still needs to get the valueType to render correctly 0 and cumulative values
|
|
397
|
+
//
|
|
398
|
+
// OR
|
|
399
|
+
//
|
|
376
400
|
// Data is in Filter
|
|
377
401
|
// TODO : This assumes the server ignores text types, we should confirm this is the case
|
|
378
402
|
return {
|
|
@@ -384,7 +408,7 @@ export class PivotTableEngine {
|
|
|
384
408
|
return !this.data[row] || this.data[row].length === 0;
|
|
385
409
|
}
|
|
386
410
|
columnIsEmpty(column) {
|
|
387
|
-
return !this.
|
|
411
|
+
return !this.rowMap.some(row => this.data[row][column]);
|
|
388
412
|
}
|
|
389
413
|
getRawColumnHeader(column) {
|
|
390
414
|
if (this.doRowTotals && column === this.dataWidth - 1) {
|
|
@@ -430,12 +454,12 @@ export class PivotTableEngine {
|
|
|
430
454
|
getRowHeader(row) {
|
|
431
455
|
return this.getRawRowHeader(this.rowMap[row]);
|
|
432
456
|
}
|
|
433
|
-
getDependantTotalCells(
|
|
457
|
+
getDependantTotalCells(_ref10) {
|
|
434
458
|
var _this$dimensionLookup, _this$dimensionLookup2;
|
|
435
459
|
let {
|
|
436
460
|
row,
|
|
437
461
|
column
|
|
438
|
-
} =
|
|
462
|
+
} = _ref10;
|
|
439
463
|
const rowSubtotalSize = ((_this$dimensionLookup = this.dimensionLookup.columns[0]) === null || _this$dimensionLookup === void 0 ? void 0 : _this$dimensionLookup.size) + 1;
|
|
440
464
|
const rowSubtotal = rowSubtotalSize && this.doRowSubtotals && {
|
|
441
465
|
row,
|
|
@@ -610,11 +634,11 @@ export class PivotTableEngine {
|
|
|
610
634
|
}
|
|
611
635
|
}
|
|
612
636
|
}
|
|
613
|
-
finalizeTotal(
|
|
637
|
+
finalizeTotal(_ref11) {
|
|
614
638
|
let {
|
|
615
639
|
row,
|
|
616
640
|
column
|
|
617
|
-
} =
|
|
641
|
+
} = _ref11;
|
|
618
642
|
if (!this.data[row]) {
|
|
619
643
|
return;
|
|
620
644
|
}
|
|
@@ -710,6 +734,40 @@ export class PivotTableEngine {
|
|
|
710
734
|
resetColumnMap() {
|
|
711
735
|
this.columnMap = this.options.hideEmptyColumns ? times(this.dataWidth, n => n).filter(idx => !this.columnIsEmpty(idx)) : times(this.dataWidth, n => n);
|
|
712
736
|
}
|
|
737
|
+
resetAccumulators() {
|
|
738
|
+
if (this.options.cumulativeValues) {
|
|
739
|
+
this.rowMap.forEach(row => {
|
|
740
|
+
this.accumulators.rows[row] = {};
|
|
741
|
+
this.columnMap.reduce((acc, column) => {
|
|
742
|
+
const cellType = this.getRawCellType({
|
|
743
|
+
row,
|
|
744
|
+
column
|
|
745
|
+
});
|
|
746
|
+
const dxDimension = this.getRawCellDxDimension({
|
|
747
|
+
row,
|
|
748
|
+
column
|
|
749
|
+
});
|
|
750
|
+
const valueType = (dxDimension === null || dxDimension === void 0 ? void 0 : dxDimension.valueType) || VALUE_TYPE_TEXT;
|
|
751
|
+
|
|
752
|
+
// only accumulate numeric values
|
|
753
|
+
// accumulating text values does not make sense
|
|
754
|
+
if (valueType === VALUE_TYPE_NUMBER) {
|
|
755
|
+
if (this.data[row] && this.data[row][column]) {
|
|
756
|
+
const dataRow = this.data[row][column];
|
|
757
|
+
const rawValue = cellType === CELL_TYPE_VALUE ? dataRow[this.dimensionLookup.dataHeaders.value] : dataRow.value;
|
|
758
|
+
acc += parseValue(rawValue);
|
|
759
|
+
}
|
|
760
|
+
this.accumulators.rows[row][column] = acc;
|
|
761
|
+
}
|
|
762
|
+
return acc;
|
|
763
|
+
}, 0);
|
|
764
|
+
});
|
|
765
|
+
} else {
|
|
766
|
+
this.accumulators = {
|
|
767
|
+
rows: {}
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
}
|
|
713
771
|
get cellPadding() {
|
|
714
772
|
switch (this.visualization.displayDensity) {
|
|
715
773
|
case DISPLAY_DENSITY_OPTION_COMPACT:
|
|
@@ -780,14 +838,18 @@ export class PivotTableEngine {
|
|
|
780
838
|
}
|
|
781
839
|
});
|
|
782
840
|
this.finalizeTotals();
|
|
783
|
-
this.rawData.rows.forEach(dataRow => {
|
|
784
|
-
const pos = lookup(dataRow, this.dimensionLookup, this);
|
|
785
|
-
if (pos) {
|
|
786
|
-
this.adaptiveClippingController.add(pos, this.getRaw(pos).renderedValue);
|
|
787
|
-
}
|
|
788
|
-
});
|
|
789
841
|
this.resetRowMap();
|
|
790
842
|
this.resetColumnMap();
|
|
843
|
+
this.resetAccumulators();
|
|
844
|
+
this.rowMap.forEach(row => {
|
|
845
|
+
this.columnMap.forEach(column => {
|
|
846
|
+
const pos = {
|
|
847
|
+
row,
|
|
848
|
+
column
|
|
849
|
+
};
|
|
850
|
+
this.adaptiveClippingController.add(pos, this.getRaw(pos).renderedValue);
|
|
851
|
+
});
|
|
852
|
+
});
|
|
791
853
|
this.height = this.rowMap.length;
|
|
792
854
|
this.width = this.columnMap.length;
|
|
793
855
|
this.adaptiveClippingController.finalize();
|