@dhis2/analytics 26.13.2 → 26.13.4

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.
@@ -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
- // TODO simply use href from the visualization object?
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.href,
37
+ href: appUrl,
39
38
  className: `jsx-${_GetLinkDialogStyles.styles.__hash}`
40
- }, appUrl.href), /*#__PURE__*/_react.default.createElement(_ui.Button, {
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.href)
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://test.tld/test';
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(props).find(_ui.Modal)).toHaveLength(1);
61
+ expect(getGetLinkDialogComponent({
62
+ type: tests[0].type,
63
+ id: tests[0].id
64
+ }).find(_ui.Modal)).toHaveLength(1);
35
65
  });
36
- it('renders a <a> tag containing the type and id props', () => {
37
- const href = getGetLinkDialogComponent(props).find('a').prop('href');
38
- expect(href).toMatch(new URL((0, _utils.appPathFor)(props.type, props.id), testBaseUrl).href);
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(props).find(_ui.Button).at(1).simulate('click');
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/index.html?id=${id}`;
36
+ return `dhis-web-maps/#/${id}`;
37
37
  case FILE_TYPE_EVENT_VISUALIZATION:
38
- return `api/apps/line-listing/#/${id}`;
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 &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
28
  inlineTests.forEach(test => {
@@ -114,24 +114,20 @@ describe('getFilterText', () => {
114
114
  filters.push({
115
115
  dimension: 'pe',
116
116
  items: [{
117
- id: '_LAST_2_MONTHS_',
118
- name: 'Last 2 months'
117
+ id: '201801'
118
+ }, {
119
+ id: 'LAST_3_MONTHS'
120
+ }, {
121
+ id: 'LAST_MONTH'
119
122
  }]
120
123
  });
121
- metaData.dimensions.pe = ['_201801_', '_201802_'];
122
- metaData.items._LAST_2_MONTHS_ = {
123
- name: 'Last 2 months',
124
- uid: '_LAST_2_MONTHS_'
125
- };
126
- metaData.items._201801_ = {
127
- name: '01 of 2018',
128
- uid: '_201801_'
124
+ metaData.dimensions.pe = ['202501', '202502', '202503', '202504', '201801'];
125
+ metaData.items['201801'] = {
126
+ name: 'January 2018'
129
127
  };
130
- metaData.items._201802_ = {
131
- name: '02 of 2018',
132
- uid: '_201802_'
133
- };
134
- expect((0, _getFilterText.default)(filters, metaData)).toEqual('Clinics, Hospital - 01 of 2018, 02 of 2018');
128
+
129
+ // Relative period names come from relativePeriods.js
130
+ expect((0, _getFilterText.default)(filters, metaData)).toEqual('Clinics, Hospital - January 2018, Last 3 months, Last month');
135
131
  });
136
132
  });
137
133
  });
@@ -4,7 +4,9 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = _default;
7
+ var _relativePeriods = require("../../components/PeriodDimension/utils/relativePeriods.js");
7
8
  var _getOuLevelAndGroupText = require("../../modules/getOuLevelAndGroupText.js");
9
+ var _dimensionGetItemIds = require("../../modules/layout/dimensionGetItemIds.js");
8
10
  var _dimensionGetItems = require("../../modules/layout/dimensionGetItems.js");
9
11
  var _dimensionIs = require("../../modules/layout/dimensionIs.js");
10
12
  var _index = require("../../modules/ouIdHelper/index.js");
@@ -25,6 +27,12 @@ function _default(filters, metaData) {
25
27
  return _index.ouIdHelper.hasGroupPrefix(id) || _index.ouIdHelper.hasLevelPrefix(id);
26
28
  })) {
27
29
  titleFragments.push((0, _getOuLevelAndGroupText.getOuLevelAndGroupText)(filter, metaData));
30
+ } else if ((0, _dimensionIs.dimensionIs)(filter, _predefinedDimensions.DIMENSION_ID_PERIOD)) {
31
+ const relativePeriodNames = (0, _relativePeriods.getRelativePeriodsName)();
32
+ titleFragments.push((0, _dimensionGetItemIds.dimensionGetItemIds)(filter).map(id => {
33
+ var _metaData$items$id;
34
+ return relativePeriodNames[id] || ((_metaData$items$id = metaData.items[id]) === null || _metaData$items$id === void 0 ? void 0 : _metaData$items$id.name) || id;
35
+ }).join(', '));
28
36
  } else {
29
37
  const filterItems = metaData.dimensions[filter.dimension];
30
38
  if (Array.isArray(filterItems)) {
@@ -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
- // TODO simply use href from the visualization object?
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.href,
30
+ href: appUrl,
32
31
  className: `jsx-${styles.__hash}`
33
- }, appUrl.href), /*#__PURE__*/React.createElement(Button, {
32
+ }, appUrl), /*#__PURE__*/React.createElement(Button, {
34
33
  icon: /*#__PURE__*/React.createElement(IconCopy24, null),
35
34
  small: true,
36
- onClick: () => navigator.clipboard.writeText(appUrl.href)
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
- import { appPathFor } from '../utils.js';
6
- const testBaseUrl = 'http://test.tld/test';
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(props).find(Modal)).toHaveLength(1);
58
+ expect(getGetLinkDialogComponent({
59
+ type: tests[0].type,
60
+ id: tests[0].id
61
+ }).find(Modal)).toHaveLength(1);
32
62
  });
33
- it('renders a <a> tag containing the type and id props', () => {
34
- const href = getGetLinkDialogComponent(props).find('a').prop('href');
35
- expect(href).toMatch(new URL(appPathFor(props.type, props.id), testBaseUrl).href);
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(props).find(Button).at(1).simulate('click');
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/index.html?id=${id}`;
27
+ return `dhis-web-maps/#/${id}`;
28
28
  case FILE_TYPE_EVENT_VISUALIZATION:
29
- return `api/apps/line-listing/#/${id}`;
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 &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.']];
26
26
  inlineTests.forEach(test => {
@@ -111,24 +111,20 @@ describe('getFilterText', () => {
111
111
  filters.push({
112
112
  dimension: 'pe',
113
113
  items: [{
114
- id: '_LAST_2_MONTHS_',
115
- name: 'Last 2 months'
114
+ id: '201801'
115
+ }, {
116
+ id: 'LAST_3_MONTHS'
117
+ }, {
118
+ id: 'LAST_MONTH'
116
119
  }]
117
120
  });
118
- metaData.dimensions.pe = ['_201801_', '_201802_'];
119
- metaData.items._LAST_2_MONTHS_ = {
120
- name: 'Last 2 months',
121
- uid: '_LAST_2_MONTHS_'
122
- };
123
- metaData.items._201801_ = {
124
- name: '01 of 2018',
125
- uid: '_201801_'
126
- };
127
- metaData.items._201802_ = {
128
- name: '02 of 2018',
129
- uid: '_201802_'
121
+ metaData.dimensions.pe = ['202501', '202502', '202503', '202504', '201801'];
122
+ metaData.items['201801'] = {
123
+ name: 'January 2018'
130
124
  };
131
- expect(getFilterText(filters, metaData)).toEqual('Clinics, Hospital - 01 of 2018, 02 of 2018');
125
+
126
+ // Relative period names come from relativePeriods.js
127
+ expect(getFilterText(filters, metaData)).toEqual('Clinics, Hospital - January 2018, Last 3 months, Last month');
132
128
  });
133
129
  });
134
130
  });
@@ -1,8 +1,10 @@
1
+ import { getRelativePeriodsName } from '../../components/PeriodDimension/utils/relativePeriods.js';
1
2
  import { getOuLevelAndGroupText } from '../../modules/getOuLevelAndGroupText.js';
3
+ import { dimensionGetItemIds } from '../../modules/layout/dimensionGetItemIds.js';
2
4
  import { dimensionGetItems } from '../../modules/layout/dimensionGetItems.js';
3
5
  import { dimensionIs } from '../../modules/layout/dimensionIs.js';
4
6
  import { ouIdHelper } from '../../modules/ouIdHelper/index.js';
5
- import { DIMENSION_ID_ORGUNIT } from '../../modules/predefinedDimensions.js';
7
+ import { DIMENSION_ID_ORGUNIT, DIMENSION_ID_PERIOD } from '../../modules/predefinedDimensions.js';
6
8
  export default function (filters, metaData) {
7
9
  if (!Array.isArray(filters) || !filters.length) {
8
10
  return '';
@@ -19,6 +21,12 @@ export default function (filters, metaData) {
19
21
  return ouIdHelper.hasGroupPrefix(id) || ouIdHelper.hasLevelPrefix(id);
20
22
  })) {
21
23
  titleFragments.push(getOuLevelAndGroupText(filter, metaData));
24
+ } else if (dimensionIs(filter, DIMENSION_ID_PERIOD)) {
25
+ const relativePeriodNames = getRelativePeriodsName();
26
+ titleFragments.push(dimensionGetItemIds(filter).map(id => {
27
+ var _metaData$items$id;
28
+ return relativePeriodNames[id] || ((_metaData$items$id = metaData.items[id]) === null || _metaData$items$id === void 0 ? void 0 : _metaData$items$id.name) || id;
29
+ }).join(', '));
22
30
  } else {
23
31
  const filterItems = metaData.dimensions[filter.dimension];
24
32
  if (Array.isArray(filterItems)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dhis2/analytics",
3
- "version": "26.13.2",
3
+ "version": "26.13.4",
4
4
  "main": "./build/cjs/index.js",
5
5
  "module": "./build/es/index.js",
6
6
  "exports": {