@dhis2/analytics 27.0.1 → 28.0.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 (33) hide show
  1. package/build/cjs/__demo__/ScatterPlotPdfExport.stories.js +46 -0
  2. package/build/cjs/__demo__/SingleValue.stories.js +34 -2
  3. package/build/cjs/__fixtures__/scatterPlotData.js +863 -0
  4. package/build/cjs/components/FileMenu/__tests__/utils.spec.js +18 -91
  5. package/build/cjs/components/FileMenu/utils.js +5 -29
  6. package/build/cjs/locales/ar/translations.json +3 -1
  7. package/build/cjs/visualizations/config/adapters/dhis_highcharts/customSVGOptions/singleValue/getSingleValueBackgroundColor.js +2 -1
  8. package/build/cjs/visualizations/config/adapters/dhis_highcharts/events/loadCustomSVG/singleValue/addIconElement.js +2 -2
  9. package/build/cjs/visualizations/config/adapters/dhis_highcharts/events/loadCustomSVG/singleValue/index.js +13 -16
  10. package/build/cjs/visualizations/config/adapters/dhis_highcharts/events/loadCustomSVG/singleValue/positionElements.js +2 -5
  11. package/build/cjs/visualizations/config/adapters/dhis_highcharts/events/loadCustomSVG/singleValue/styles.js +13 -2
  12. package/build/cjs/visualizations/config/adapters/dhis_highcharts/exporting.js +33 -17
  13. package/build/cjs/visualizations/config/adapters/dhis_highcharts/index.js +1 -1
  14. package/build/cjs/visualizations/config/adapters/dhis_highcharts/title/index.js +2 -1
  15. package/build/cjs/visualizations/config/generators/highcharts/index.js +30 -16
  16. package/build/cjs/visualizations/util/colors/colorSets.js +1 -2
  17. package/build/es/__demo__/ScatterPlotPdfExport.stories.js +37 -0
  18. package/build/es/__demo__/SingleValue.stories.js +35 -3
  19. package/build/es/__fixtures__/scatterPlotData.js +857 -0
  20. package/build/es/components/FileMenu/__tests__/utils.spec.js +18 -91
  21. package/build/es/components/FileMenu/utils.js +6 -30
  22. package/build/es/locales/ar/translations.json +3 -1
  23. package/build/es/visualizations/config/adapters/dhis_highcharts/customSVGOptions/singleValue/getSingleValueBackgroundColor.js +2 -1
  24. package/build/es/visualizations/config/adapters/dhis_highcharts/events/loadCustomSVG/singleValue/addIconElement.js +2 -2
  25. package/build/es/visualizations/config/adapters/dhis_highcharts/events/loadCustomSVG/singleValue/index.js +13 -16
  26. package/build/es/visualizations/config/adapters/dhis_highcharts/events/loadCustomSVG/singleValue/positionElements.js +2 -5
  27. package/build/es/visualizations/config/adapters/dhis_highcharts/events/loadCustomSVG/singleValue/styles.js +13 -2
  28. package/build/es/visualizations/config/adapters/dhis_highcharts/exporting.js +33 -17
  29. package/build/es/visualizations/config/adapters/dhis_highcharts/index.js +1 -1
  30. package/build/es/visualizations/config/adapters/dhis_highcharts/title/index.js +2 -1
  31. package/build/es/visualizations/config/generators/highcharts/index.js +30 -16
  32. package/build/es/visualizations/util/colors/colorSets.js +1 -2
  33. package/package.json +4 -3
@@ -89,92 +89,37 @@ describe('utils', () => {
89
89
  });
90
90
  });
91
91
  describe('preparePayloadForSave', () => {
92
- const mockEngine = {
93
- query: jest.fn()
94
- };
95
- beforeEach(() => {
96
- jest.clearAllMocks();
97
- });
98
- it('fetches subscribers and adds them to the visualization', async () => {
99
- const visualization = {
100
- id: '123',
101
- type: 'BAR',
102
- name: 'Existing Name',
103
- description: 'Existing Description'
104
- };
105
- mockEngine.query.mockResolvedValue({
106
- ao: {
107
- subscribers: ['user1', 'user2']
108
- }
109
- });
110
- const result = await (0, _utils.preparePayloadForSave)({
111
- visualization,
112
- engine: mockEngine
113
- });
114
- expect(mockEngine.query).toHaveBeenCalledWith({
115
- ao: {
116
- resource: 'visualizations',
117
- id: expect.any(Function),
118
- params: {
119
- fields: 'subscribers'
120
- }
121
- }
122
- }, {
123
- variables: {
124
- id: '123'
125
- }
126
- });
127
- expect(result.subscribers).toEqual(['user1', 'user2']);
128
- });
129
- it('sets the name to the provided name', async () => {
92
+ it('sets the name to the provided name', () => {
130
93
  const visualization = {
131
94
  id: '123',
132
95
  type: 'MAP',
133
96
  name: 'Existing name'
134
97
  };
135
98
  const name = 'New Name';
136
- mockEngine.query.mockResolvedValue({
137
- ao: {
138
- subscribers: []
139
- }
140
- });
141
- const result = await (0, _utils.preparePayloadForSave)({
99
+ const result = (0, _utils.preparePayloadForSave)({
142
100
  visualization,
143
- name,
144
- engine: mockEngine
101
+ name
145
102
  });
146
103
  expect(result.name).toBe(name);
147
104
  });
148
- it('sets the name to the existing name if no new name is provided', async () => {
105
+ it('sets the name to the existing name if no new name is provided', () => {
149
106
  const visualization = {
150
107
  id: '123',
151
108
  type: 'LINE_LIST',
152
109
  name: 'Existing Name'
153
110
  };
154
- mockEngine.query.mockResolvedValue({
155
- ao: {
156
- subscribers: []
157
- }
158
- });
159
- const result = await (0, _utils.preparePayloadForSave)({
160
- visualization,
161
- engine: mockEngine
111
+ const result = (0, _utils.preparePayloadForSave)({
112
+ visualization
162
113
  });
163
114
  expect(result.name).toBe('Existing Name');
164
115
  });
165
- it('sets the name to a default value if no name is provided', async () => {
116
+ it('sets the name to a default value if no name is provided', () => {
166
117
  const visualization = {
167
118
  id: '123',
168
119
  type: 'BAR'
169
120
  };
170
- mockEngine.query.mockResolvedValue({
171
- ao: {
172
- subscribers: []
173
- }
174
- });
175
- const result = await (0, _utils.preparePayloadForSave)({
176
- visualization,
177
- engine: mockEngine
121
+ const result = (0, _utils.preparePayloadForSave)({
122
+ visualization
178
123
  });
179
124
  const expectedName = `Untitled Bar, ${new Date().toLocaleDateString(undefined, {
180
125
  year: 'numeric',
@@ -183,55 +128,37 @@ describe('utils', () => {
183
128
  })}`;
184
129
  expect(result.name).toBe(expectedName);
185
130
  });
186
- it('sets the description to the provided description', async () => {
131
+ it('sets the description to the provided description', () => {
187
132
  const visualization = {
188
133
  id: '123',
189
134
  type: 'YEAR_OVER_YEAR_LINE',
190
135
  description: 'Existing Description'
191
136
  };
192
137
  const description = 'New Description';
193
- mockEngine.query.mockResolvedValue({
194
- ao: {
195
- subscribers: []
196
- }
197
- });
198
- const result = await (0, _utils.preparePayloadForSave)({
138
+ const result = (0, _utils.preparePayloadForSave)({
199
139
  visualization,
200
- description,
201
- engine: mockEngine
140
+ description
202
141
  });
203
142
  expect(result.description).toBe(description);
204
143
  });
205
- it('keeps the existing description if no new description is provided', async () => {
144
+ it('keeps the existing description if no new description is provided', () => {
206
145
  const visualization = {
207
146
  id: '123',
208
147
  type: 'COLUMN',
209
148
  description: 'Existing Description'
210
149
  };
211
- mockEngine.query.mockResolvedValue({
212
- ao: {
213
- subscribers: []
214
- }
215
- });
216
- const result = await (0, _utils.preparePayloadForSave)({
217
- visualization,
218
- engine: mockEngine
150
+ const result = (0, _utils.preparePayloadForSave)({
151
+ visualization
219
152
  });
220
153
  expect(result.description).toBe('Existing Description');
221
154
  });
222
- it('sets the description to undefined if no description is provided and none exists', async () => {
155
+ it('sets the description to undefined if no description is provided and none exists', () => {
223
156
  const visualization = {
224
157
  id: '123',
225
158
  type: 'BAR'
226
159
  };
227
- mockEngine.query.mockResolvedValue({
228
- ao: {
229
- subscribers: []
230
- }
231
- });
232
- const result = await (0, _utils.preparePayloadForSave)({
233
- visualization,
234
- engine: mockEngine
160
+ const result = (0, _utils.preparePayloadForSave)({
161
+ visualization
235
162
  });
236
163
  expect(result.description).toBeUndefined();
237
164
  });
@@ -65,38 +65,12 @@ const preparePayloadForSaveAs = _ref => {
65
65
  return visualization;
66
66
  };
67
67
  exports.preparePayloadForSaveAs = preparePayloadForSaveAs;
68
- const getSubscriberQuery = type => ({
69
- ao: {
70
- resource: (0, _visTypes.getApiEndpointByVisType)(type),
71
- id: _ref2 => {
72
- let {
73
- id
74
- } = _ref2;
75
- return id;
76
- },
77
- params: {
78
- fields: 'subscribers'
79
- }
80
- }
81
- });
82
- const apiFetchAOSubscribers = (dataEngine, id, type) => {
83
- return dataEngine.query(getSubscriberQuery(type), {
84
- variables: {
85
- id
86
- }
87
- });
88
- };
89
- const preparePayloadForSave = async _ref3 => {
68
+ const preparePayloadForSave = _ref2 => {
90
69
  let {
91
70
  visualization,
92
71
  name,
93
- description,
94
- engine
95
- } = _ref3;
96
- const {
97
- ao
98
- } = await apiFetchAOSubscribers(engine, visualization.id, visualization.type);
99
- visualization.subscribers = ao.subscribers;
72
+ description
73
+ } = _ref2;
100
74
  visualization.name = name || visualization.name || _d2I18n.default.t('Untitled {{visualizationType}}, {{date}}', {
101
75
  visualizationType: (0, _visTypes.getDisplayNameByVisType)(visualization.type),
102
76
  date: new Date().toLocaleDateString(undefined, {
@@ -106,6 +80,8 @@ const preparePayloadForSave = async _ref3 => {
106
80
  })
107
81
  });
108
82
  visualization.description = description !== undefined ? description : visualization.description;
83
+ delete visualization.displayName;
84
+ delete visualization.displayDescription;
109
85
  return visualization;
110
86
  };
111
87
  exports.preparePayloadForSave = preparePayloadForSave;
@@ -26,7 +26,7 @@
26
26
  "About this event chart": "",
27
27
  "About this event report": "",
28
28
  "This app could not retrieve required data.": "",
29
- "Network error": "",
29
+ "Network error": "خطأ في الشبكة",
30
30
  "Data / Edit calculation": "",
31
31
  "Data / New calculation": "",
32
32
  "Remove item": "",
@@ -166,6 +166,7 @@
166
166
  "line list": "",
167
167
  "map": "خريطة",
168
168
  "visualization": "",
169
+ "Untitled {{visualizationType}}, {{date}}": "",
169
170
  "Edit": "تعديل",
170
171
  "Write a reply": "كتابة رد",
171
172
  "Post reply": "",
@@ -471,6 +472,7 @@
471
472
  "Single value": "قيمة مفردة",
472
473
  "Outlier table": "",
473
474
  "All charts": "",
475
+ "Map": "الخريطة",
474
476
  "{{seriesName}} (trend)": "",
475
477
  "Trend": "الاتجاه",
476
478
  "No legend for this series": "لا معلومات مفاتيح إيضاحية لهذه السلسلة",
@@ -7,6 +7,7 @@ exports.getSingleValueBackgroundColor = getSingleValueBackgroundColor;
7
7
  var _legends = require("../../../../../../modules/legends.js");
8
8
  var _getSingleValueLegendColor = require("./getSingleValueLegendColor.js");
9
9
  function getSingleValueBackgroundColor(legendOptions, legendSets, value) {
10
+ let defaultColor = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'transparent';
10
11
  const legendColor = (0, _getSingleValueLegendColor.getSingleValueLegendColor)(legendOptions, legendSets, value);
11
- return legendColor && legendOptions.style === _legends.LEGEND_DISPLAY_STYLE_FILL ? legendColor : 'transparent';
12
+ return legendColor && legendOptions.style === _legends.LEGEND_DISPLAY_STYLE_FILL ? legendColor : defaultColor;
12
13
  }
@@ -11,8 +11,8 @@ function addIconElement(svgString, color) {
11
11
  const iconElWidth = svgIconDocument.documentElement.getAttribute('width');
12
12
  const iconGroup = this.renderer.g('icon').attr({
13
13
  color,
14
- 'data-test': 'visualization-icon'
15
- }).css({
14
+ fill: color,
15
+ 'data-test': 'visualization-icon',
16
16
  visibility: 'hidden'
17
17
  });
18
18
 
@@ -10,19 +10,22 @@ var _getAvailableSpace = require("./getAvailableSpace.js");
10
10
  var _positionElements = require("./positionElements.js");
11
11
  var _styles = require("./styles.js");
12
12
  function loadSingleValueSVG() {
13
+ var _this$userOptions;
13
14
  const {
14
15
  formattedValue,
15
16
  icon,
16
17
  subText,
17
18
  fontColor
18
19
  } = this.userOptions.customSVGOptions;
19
- const dynamicStyles = new _styles.DynamicStyles();
20
- const valueElement = this.renderer.text(formattedValue).attr('data-test', 'visualization-primary-value').css({
21
- color: fontColor,
20
+ const dynamicStyles = new _styles.DynamicStyles((_this$userOptions = this.userOptions) === null || _this$userOptions === void 0 ? void 0 : _this$userOptions.isPdfExport);
21
+ const valueElement = this.renderer.text(formattedValue).attr({
22
+ 'data-test': 'visualization-primary-value',
23
+ fill: fontColor,
22
24
  visibility: 'hidden'
23
25
  }).add();
24
- const subTextElement = subText ? this.renderer.text(subText).attr('data-test', 'visualization-subtext').css({
25
- color: fontColor,
26
+ const subTextElement = subText ? this.renderer.text(subText).attr({
27
+ 'data-test': 'visualization-subtext',
28
+ fill: fontColor,
26
29
  visibility: 'hidden'
27
30
  }).add() : null;
28
31
  const iconElement = icon ? _addIconElement.addIconElement.call(this, icon, fontColor) : null;
@@ -30,18 +33,12 @@ function loadSingleValueSVG() {
30
33
  let styles = {};
31
34
  while (!fitsWithinContainer && dynamicStyles.hasNext()) {
32
35
  styles = dynamicStyles.next();
33
- valueElement.css(styles.value);
34
- subTextElement === null || subTextElement === void 0 ? void 0 : subTextElement.css(styles.subText);
36
+ valueElement.attr(styles.value);
37
+ subTextElement === null || subTextElement === void 0 ? void 0 : subTextElement.attr(styles.subText);
35
38
  fitsWithinContainer = (0, _checkIfFitsWithinContainer.checkIfFitsWithinContainer)(_getAvailableSpace.getAvailableSpace.call(this, styles.spacing.valueTop), valueElement, subTextElement, icon, subText, styles.spacing);
36
39
  }
37
40
  _positionElements.positionElements.call(this, valueElement, subTextElement, iconElement, styles.spacing);
38
- valueElement.css({
39
- visibility: 'visible'
40
- });
41
- iconElement === null || iconElement === void 0 ? void 0 : iconElement.css({
42
- visibility: 'visible'
43
- });
44
- subTextElement === null || subTextElement === void 0 ? void 0 : subTextElement.css({
45
- visibility: 'visible'
46
- });
41
+ valueElement.attr('visibility', 'visible');
42
+ iconElement === null || iconElement === void 0 ? void 0 : iconElement.attr('visibility', 'visible');
43
+ subTextElement === null || subTextElement === void 0 ? void 0 : subTextElement.attr('visibility', 'visible');
47
44
  }
@@ -28,11 +28,8 @@ function positionElements(valueElement, subTextElement, iconElement, spacing) {
28
28
  const iconHeight = height * scale;
29
29
  const valueElementHeight = valueElementBox.height * _constants.ACTUAL_NUMBER_HEIGHT_FACTOR;
30
30
  const translateY = layoutRect.y + (valueElementHeight - iconHeight) / 2;
31
-
32
- /* The icon is a <g> with <path> elements that contain coordinates.
33
- * These path-coordinates only scale correctly when using CSS translate */
34
- iconElement.css({
35
- transform: `translate(${translateX}px, ${translateY}px) scale(${scale})`
31
+ iconElement.attr({
32
+ transform: `translate(${translateX} ${translateY}) scale(${scale})`
36
33
  });
37
34
  }
38
35
  if (subTextElement) {
@@ -75,11 +75,12 @@ const spacings = [{
75
75
  }];
76
76
  const MIN_SIDE_WHITESPACE = exports.MIN_SIDE_WHITESPACE = 4;
77
77
  class DynamicStyles {
78
- constructor() {
78
+ constructor(isPdfExport) {
79
79
  this.currentIndex = 0;
80
+ this.isPdfExport = isPdfExport;
80
81
  }
81
82
  getStyle() {
82
- return {
83
+ const style = {
83
84
  value: {
84
85
  ...valueStyles[this.currentIndex],
85
86
  'font-weight': '300'
@@ -87,6 +88,16 @@ class DynamicStyles {
87
88
  subText: subTextStyles[this.currentIndex],
88
89
  spacing: spacings[this.currentIndex]
89
90
  };
91
+ if (this.isPdfExport) {
92
+ /* font-weight is not supported for offline PDF export and providing
93
+ * a specific value will cause the PDF to show a serif font instead */
94
+ style.value['font-weight'] = 'normal';
95
+ /* letter-spacing is also not supported and providing a specific
96
+ * value will cause misalignment issues in the PDF */
97
+ style.value['letter-spacing'] = 'normal';
98
+ style.subText['letter-spacing'] = 'normal';
99
+ }
100
+ return style;
90
101
  }
91
102
  next() {
92
103
  if (this.currentIndex === valueStyles.length - 1) {
@@ -5,26 +5,42 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = getExporting;
7
7
  var _visTypes = require("../../../../modules/visTypes.js");
8
+ var _getSingleValueBackgroundColor = require("./customSVGOptions/singleValue/getSingleValueBackgroundColor.js");
8
9
  var _index = _interopRequireDefault(require("./events/loadCustomSVG/singleValue/index.js"));
9
10
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
- function getExporting(visType) {
11
- const exporting = {
12
- // disable exporting context menu
13
- enabled: false
14
- };
15
- switch (visType) {
16
- case _visTypes.VIS_TYPE_SINGLE_VALUE:
17
- return {
18
- ...exporting,
19
- chartOptions: {
20
- chart: {
21
- events: {
22
- load: _index.default
23
- }
11
+ const DEFAULT_EXPORT_BACKGROUND_COLOR = '#ffffff';
12
+ const BASE_EXPORTING_CONFIG = {
13
+ // disable exporting context menu
14
+ enabled: false,
15
+ // use offline exporting only
16
+ fallbackToExportServer: false,
17
+ allowHTML: true,
18
+ showExportInProgress: true,
19
+ applyStyleSheets: true,
20
+ sourceHeight: 768,
21
+ sourceWidth: 1024,
22
+ scale: 1,
23
+ chartOptions: {
24
+ chart: {
25
+ backgroundColor: DEFAULT_EXPORT_BACKGROUND_COLOR
26
+ }
27
+ }
28
+ };
29
+ function getExporting(layout, legendSets, series) {
30
+ if (layout.type === _visTypes.VIS_TYPE_SINGLE_VALUE) {
31
+ return {
32
+ ...BASE_EXPORTING_CONFIG,
33
+ chartOptions: {
34
+ ...BASE_EXPORTING_CONFIG.chartOptions,
35
+ chart: {
36
+ ...BASE_EXPORTING_CONFIG.chartOptions.chart,
37
+ backgroundColor: (0, _getSingleValueBackgroundColor.getSingleValueBackgroundColor)(layout.legend, legendSets, series[0], DEFAULT_EXPORT_BACKGROUND_COLOR),
38
+ events: {
39
+ load: _index.default
24
40
  }
25
41
  }
26
- };
27
- default:
28
- return exporting;
42
+ }
43
+ };
29
44
  }
45
+ return BASE_EXPORTING_CONFIG;
30
46
  }
@@ -106,7 +106,7 @@ function _default(_ref) {
106
106
  enabled: false
107
107
  },
108
108
  // exporting
109
- exporting: (0, _exporting.default)(_layout.type),
109
+ exporting: (0, _exporting.default)(_layout, _extraOptions.legendSets, series),
110
110
  /* The config object passed to the Highcharts Chart constructor
111
111
  * can contain arbitrary properties, which are made accessible
112
112
  * under the Chart instance's `userOptions` member. This means
@@ -44,7 +44,8 @@ function _default(layout, metaData, extraOptions, series) {
44
44
  const legendOptions = layout.legend;
45
45
  const fontStyle = (0, _fontStyle.mergeFontStyleWithDefault)(layout.fontStyle && layout.fontStyle[_fontStyle.FONT_STYLE_VISUALIZATION_TITLE], _fontStyle.FONT_STYLE_VISUALIZATION_TITLE);
46
46
  const title = Object.assign({
47
- text: undefined
47
+ text: undefined,
48
+ minScale: 1
48
49
  }, dashboard ? DASHBOARD_TITLE_STYLE : {
49
50
  margin: 30,
50
51
  align: (0, _getTextAlignOption.getTextAlignOption)(fontStyle[_fontStyle.FONT_STYLE_OPTION_TEXT_ALIGN], _fontStyle.FONT_STYLE_VISUALIZATION_TITLE, (0, _visTypes.isVerticalType)(layout.type)),
@@ -5,24 +5,38 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = _default;
7
7
  var _highcharts = _interopRequireDefault(require("highcharts"));
8
- var _highchartsMore = _interopRequireDefault(require("highcharts/highcharts-more"));
9
- var _boost = _interopRequireDefault(require("highcharts/modules/boost"));
10
- var _exporting = _interopRequireDefault(require("highcharts/modules/exporting"));
11
- var _noDataToDisplay = _interopRequireDefault(require("highcharts/modules/no-data-to-display"));
12
- var _patternFill = _interopRequireDefault(require("highcharts/modules/pattern-fill"));
13
- var _solidGauge = _interopRequireDefault(require("highcharts/modules/solid-gauge"));
8
+ require("highcharts/highcharts-more");
9
+ require("highcharts/modules/boost");
10
+ require("highcharts/modules/exporting");
11
+ require("highcharts/modules/no-data-to-display");
12
+ require("highcharts/modules/offline-exporting");
13
+ require("highcharts/modules/pattern-fill");
14
+ require("highcharts/modules/solid-gauge");
14
15
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
- // apply
16
- (0, _highchartsMore.default)(_highcharts.default);
17
- (0, _solidGauge.default)(_highcharts.default);
18
- (0, _noDataToDisplay.default)(_highcharts.default);
19
- (0, _exporting.default)(_highcharts.default);
20
- (0, _patternFill.default)(_highcharts.default);
21
- (0, _boost.default)(_highcharts.default);
16
+ /* Whitelist some additional SVG attributes and tags here. Without this,
17
+ * the PDF export for the SingleValue visualization and charts in boost-mode
18
+ * breaks. For more info about the boost mode issue, see:
19
+ * https://github.com/highcharts/highcharts/issues/8333 */
20
+ _highcharts.default.AST.allowedTags.push('fedropshadow', 'image');
21
+ _highcharts.default.AST.allowedAttributes.push('transform-origin', 'preserveAspectRatio', 'fill-rule', 'clip-rule');
22
22
 
23
- /* Whitelist some additional SVG attributes here. Without this,
24
- * the PDF export for the SingleValue visualization breaks. */
25
- _highcharts.default.AST.allowedAttributes.push('fill-rule', 'clip-rule');
23
+ /* This is a workaround for https://github.com/highcharts/highcharts/issues/22008
24
+ * We add some transparent text in a non-ASCII script to the chart to prevent
25
+ * the chart from being exported in a serif font */
26
+ _highcharts.default.addEvent(_highcharts.default.Chart, 'load', function () {
27
+ this.renderer.text('모', 20, 20).attr({
28
+ opacity: 0
29
+ }).add();
30
+ });
31
+
32
+ /* Workaround for https://github.com/highcharts/highcharts/issues/23049
33
+ * (there happen to be 10 colors and 10 patterns)*/
34
+ const {
35
+ colors
36
+ } = _highcharts.default.getOptions();
37
+ _highcharts.default.patterns.forEach((pattern, i) => {
38
+ pattern.color = colors[i];
39
+ });
26
40
  function drawLegendSymbolWrap() {
27
41
  const pick = _highcharts.default.pick;
28
42
  _highcharts.default.wrap(_highcharts.default.seriesTypes.column.prototype, 'drawLegendSymbol', function (proceed, legend, item) {
@@ -5,9 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.colorSets = exports.COLOR_SET_PATTERNS = exports.COLOR_SET_GRAY = exports.COLOR_SET_EXTENDED = exports.COLOR_SET_DEFAULT = exports.COLOR_SET_DARK = exports.COLOR_SET_COLOR_BLIND = exports.COLOR_SET_BRIGHT = exports.COLOR_SET_BASIC = void 0;
7
7
  var _highcharts = _interopRequireDefault(require("highcharts"));
8
- var _patternFill = _interopRequireDefault(require("highcharts/modules/pattern-fill"));
8
+ require("highcharts/modules/pattern-fill");
9
9
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
- (0, _patternFill.default)(_highcharts.default);
11
10
  const COLOR_SET_DEFAULT = exports.COLOR_SET_DEFAULT = 'DEFAULT';
12
11
  const COLOR_SET_BASIC = exports.COLOR_SET_BASIC = 'BASIC';
13
12
  const COLOR_SET_EXTENDED = exports.COLOR_SET_EXTENDED = 'EXTENDED';
@@ -0,0 +1,37 @@
1
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { extraOptions, responses, visualization } from '../__fixtures__/scatterPlotData.js';
3
+ import { createVisualization } from '../index.js';
4
+ export default {
5
+ title: 'ScatterPlot'
6
+ };
7
+ export const Default = () => {
8
+ const ref = useRef(null);
9
+ const [chart, setChart] = useState(null);
10
+ const exportToPdf = useCallback(() => {
11
+ chart.update({
12
+ exporting: {
13
+ chartOptions: {
14
+ isPdfExport: true
15
+ }
16
+ }
17
+ });
18
+ chart.exportChartLocal({
19
+ filename: 'PDF export',
20
+ type: 'application/pdf'
21
+ });
22
+ }, [chart]);
23
+ useEffect(() => {
24
+ const obj = createVisualization(responses, visualization, ref.current, extraOptions, undefined, undefined, 'highcharts');
25
+ setChart(obj.visualization);
26
+ }, []);
27
+ return /*#__PURE__*/React.createElement(React.Fragment, null, chart && /*#__PURE__*/React.createElement("button", {
28
+ onClick: exportToPdf
29
+ }, "Export PDF"), /*#__PURE__*/React.createElement("div", {
30
+ style: {
31
+ width: 800,
32
+ height: 800,
33
+ border: '2px solid magenta'
34
+ },
35
+ ref: ref
36
+ }));
37
+ };
@@ -1,4 +1,4 @@
1
- import React, { useState, useMemo, useRef, useEffect } from 'react';
1
+ import React, { useState, useMemo, useRef, useEffect, useCallback } from 'react';
2
2
  import { createVisualization } from '../index.js';
3
3
  const constainerStyleBase = {
4
4
  width: 800,
@@ -585,6 +585,7 @@ export const Default = () => {
585
585
  const [dashboard, setDashboard] = useState(false);
586
586
  const [showIcon, setShowIcon] = useState(true);
587
587
  const [indicatorType, setIndicatorType] = useState('plain');
588
+ const [exportAsPdf, setExportAsPdf] = useState(true);
588
589
  const [width, setWidth] = useState(constainerStyleBase.width);
589
590
  const [height, setHeight] = useState(constainerStyleBase.height);
590
591
  const containerStyle = useMemo(() => ({
@@ -592,6 +593,31 @@ export const Default = () => {
592
593
  width,
593
594
  height
594
595
  }), [width, height]);
596
+ const downloadOffline = useCallback(() => {
597
+ if (newChartRef.current) {
598
+ const currentBackgroundColor = newChartRef.current.userOptions.chart.backgroundColor;
599
+ newChartRef.current.update({
600
+ exporting: {
601
+ chartOptions: {
602
+ isPdfExport: exportAsPdf
603
+ }
604
+ }
605
+ });
606
+ newChartRef.current.exportChartLocal({
607
+ sourceHeight: 768,
608
+ sourceWidth: 1024,
609
+ scale: 1,
610
+ fallbackToExportServer: false,
611
+ filename: 'testOfflineDownload',
612
+ showExportInProgress: true,
613
+ type: exportAsPdf ? 'application/pdf' : 'image/png'
614
+ }, {
615
+ chart: {
616
+ backgroundColor: currentBackgroundColor === 'transparent' ? '#ffffff' : currentBackgroundColor
617
+ }
618
+ });
619
+ }
620
+ }, [exportAsPdf]);
595
621
  useEffect(() => {
596
622
  if (newContainerRef.current) {
597
623
  requestAnimationFrame(() => {
@@ -652,13 +678,19 @@ export const Default = () => {
652
678
  checked: showIcon,
653
679
  onChange: () => setShowIcon(!showIcon),
654
680
  type: "checkbox"
655
- }), "\xA0Show icon"), /*#__PURE__*/React.createElement("label", null, "Indicator type\xA0", /*#__PURE__*/React.createElement("select", {
681
+ }), "\xA0Show icon"), /*#__PURE__*/React.createElement("label", null, "Indicator type", ' ', /*#__PURE__*/React.createElement("select", {
656
682
  onChange: event => setIndicatorType(event.target.value)
657
683
  }, indicatorTypes.map((type, index) => {
658
684
  return /*#__PURE__*/React.createElement("option", {
659
685
  key: index
660
686
  }, type);
661
- })))), /*#__PURE__*/React.createElement("div", {
687
+ }))), /*#__PURE__*/React.createElement("label", null, /*#__PURE__*/React.createElement("input", {
688
+ checked: exportAsPdf,
689
+ onChange: () => setExportAsPdf(!exportAsPdf),
690
+ type: "checkbox"
691
+ }), ' ', "Export as PDF"), /*#__PURE__*/React.createElement("button", {
692
+ onClick: downloadOffline
693
+ }, "Download offline")), /*#__PURE__*/React.createElement("div", {
662
694
  style: {
663
695
  display: 'flex',
664
696
  gap: 12