Skip to content

Commit 4d636f7

Browse files
authored
Merge pull request #157 from highcharts-for-python/enhance/146-add-support-for-highcharts-core-js-v11-3
Enhance/146 add support for highcharts core js v11 3
2 parents 923a195 + fd37cec commit 4d636f7

File tree

11 files changed

+408
-40
lines changed

11 files changed

+408
-40
lines changed

CHANGES.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22
Release 1.6.0
33
=========================================
44

5+
* **ENHANCEMENT:** Align the API to **Highcharts (JS) v.11.3** (#146). In particular, this includes:
6+
7+
* Added ``ChartOptions.axis_layout_runs`` property.
8+
* Added ``ColorAxis.height`` property.
9+
* Added ``ColorAxis.width`` property.
10+
* Added ``Data.column_types`` property.
11+
* Added ``Exporting.fetch_options`` property.
12+
* Implemented support for verbose axis date-time unit labelling configuration (see: ``DateTimeLabelFormats``).
13+
14+
515
* **BUGFIX:** Added support for ``nodeFormat`` and ``nodeFormatter`` to tooltip properties for
616
diagram series (Organization, Dependency Wheel, and Sankey). (#148)
717
* **ENHANCEMENT:** Added ability to remove or override the JavaScript event listener when

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ visualization library, with full integration into the robust Python ecosystem, i
1515
dataframe.
1616
* ...and even more use-case specific integrations across the broader toolkit.
1717

18-
The library supports Highcharts (JS) v.10.2 and higher, including Highcharts (JS) v.11.2.0.
18+
The library supports Highcharts (JS) v.10.2 and higher, including Highcharts (JS) v.11.3.0.
1919

2020
**COMPLETE DOCUMENTATION:** https://core-docs.highchartspython.com/en/latest/index.html
2121

docs/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Highcharts Core for Python
3737

3838
.. sidebar:: Version Compatibility
3939

40-
**Latest Highcharts (JS) version supported:** v.11.2.0
40+
**Latest Highcharts (JS) version supported:** v.11.3.0
4141

4242
**Highcharts Core for Python** is designed to be compatible with:
4343

highcharts_core/chart.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ def module_url(self) -> str:
451451
452452
.. warning::
453453
454-
Module paths wlil be appended to this value without checking that
454+
Module paths will be appended to this value without checking that
455455
they resolve to an actual file, e.g. the module
456456
``module/accessibility.js`` will get appended as
457457
``'https://code.highcharts.com/module/accessibility.js'``. Be sure

highcharts_core/constants.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -953,4 +953,12 @@ def __repr__(self):
953953

954954
EMPTY_STRING_CONTEXTS = [
955955
'Annotation.draggable',
956+
]
957+
958+
959+
DATA_COLUMN_TYPES = [
960+
'string',
961+
'number',
962+
'float',
963+
'date',
956964
]

highcharts_core/options/axes/color_axis.py

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,27 @@ class ColorAxis(GenericAxis):
5555
def __init__(self, **kwargs):
5656
self._data_class_color = None
5757
self._data_classes = None
58+
self._height = None
5859
self._layout = None
5960
self._line_color = None
6061
self._marker = None
6162
self._max_color = None
6263
self._min_color = None
6364
self._show_in_legend = None
6465
self._stops = None
66+
self._width = None
6567

6668
self.data_class_color = kwargs.get('data_class_color', None)
6769
self.data_classes = kwargs.get('data_classes', None)
70+
self.height = kwargs.get('height', None)
6871
self.layout = kwargs.get('layout', None)
6972
self.line_color = kwargs.get('line_color', None)
7073
self.marker = kwargs.get('marker', None)
7174
self.max_color = kwargs.get('max_color', None)
7275
self.min_color = kwargs.get('min_color', None)
7376
self.show_in_legend = kwargs.get('show_in_legend', None)
7477
self.stops = kwargs.get('stops', None)
78+
self.width = kwargs.get('width', None)
7579

7680
super().__init__(**kwargs)
7781

@@ -118,6 +122,30 @@ def data_classes(self) -> Optional[List[DataClass]]:
118122
def data_classes(self, value):
119123
self._data_classes = value
120124

125+
@property
126+
def height(self) -> Optional[str | int | float | Decimal]:
127+
"""The height of the color axis, expressed either in pixels or as a
128+
percentage of the total plot height. Defaults to
129+
:obj:`None <python:None>`.
130+
131+
:rtype: numeric or :class:`str <python:str>` or :obj:`None <python:None>`
132+
"""
133+
return self._height
134+
135+
@height.setter
136+
def height(self, value):
137+
if value is None:
138+
self._height = None
139+
else:
140+
try:
141+
value = validators.string(value)
142+
if "%" not in value:
143+
raise ValueError
144+
except (TypeError, ValueError):
145+
value = validators.numeric(value, minimum=0)
146+
147+
self._height = value
148+
121149
@property
122150
def layout(self) -> Optional[str]:
123151
"""The layout of the color axis. Defaults to :obj:`None <python:None>`.
@@ -273,6 +301,30 @@ def stops(self, value):
273301

274302
self._stops = processed_items
275303

304+
@property
305+
def width(self) -> Optional[str | int | float | Decimal]:
306+
"""The width of the color axis, expressed either in
307+
pixels or as a percentage of the total plot width. Defaults to
308+
:obj:`None <python:None>`.
309+
310+
:rtype: numeric or :class:`str <python:str>` or :obj:`None <python:None>`
311+
"""
312+
return self._width
313+
314+
@width.setter
315+
def width(self, value):
316+
if value is None:
317+
self._width = None
318+
else:
319+
try:
320+
value = validators.string(value)
321+
if "%" not in value:
322+
raise ValueError
323+
except (TypeError, ValueError):
324+
value = validators.numeric(value, minimum=0)
325+
326+
self._width = value
327+
276328
@classmethod
277329
def _get_kwargs_from_dict(cls, as_dict):
278330
kwargs = {
@@ -330,13 +382,15 @@ def _get_kwargs_from_dict(cls, as_dict):
330382

331383
'data_class_color': as_dict.get('dataClassColor', None),
332384
'data_classes': as_dict.get('dataClasses', None),
385+
'height': as_dict.get('height', None),
333386
'layout': as_dict.get('layout', None),
334387
'line_color': as_dict.get('lineColor', None),
335388
'marker': as_dict.get('marker', None),
336389
'max_color': as_dict.get('maxColor', None),
337390
'min_color': as_dict.get('minColor', None),
338391
'show_in_legend': as_dict.get('showInLegend', None),
339-
'stops': as_dict.get('stops', None)
392+
'stops': as_dict.get('stops', None),
393+
'width': as_dict.get('width', None),
340394
}
341395

342396
return kwargs
@@ -397,13 +451,15 @@ def _to_untrimmed_dict(self, in_cls = None) -> dict:
397451

398452
'dataClassColor': self.data_class_color,
399453
'dataClasses': self.data_classes,
454+
'height': self.height,
400455
'layout': self.layout,
401456
'lineColor': self.line_color,
402457
'marker': self.marker,
403458
'maxColor': self.max_color,
404459
'minColor': self.min_color,
405460
'showInLegend': self.show_in_legend,
406-
'stops': self.stops
461+
'stops': self.stops,
462+
'width': self.width,
407463
}
408464

409465
return untrimmed

highcharts_core/options/chart/__init__.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ def __init__(self, **kwargs):
108108
self._align_ticks = None
109109
self._allow_mutating_data = None
110110
self._animation = None
111+
self._axis_layout_runs = None
111112
self._background_color = None
112113
self._border_color = None
113114
self._border_radius = None
@@ -155,6 +156,7 @@ def __init__(self, **kwargs):
155156
self.align_ticks = kwargs.get('align_ticks', None)
156157
self.allow_mutating_data = kwargs.get('allow_mutating_data', None)
157158
self.animation = kwargs.get('animation', None)
159+
self.axis_layout_runs = kwargs.get('axis_layout_runs', None)
158160
self.background_color = kwargs.get('background_color', None)
159161
self.border_color = kwargs.get('border_color', None)
160162
self.border_radius = kwargs.get('border_radius', None)
@@ -337,6 +339,38 @@ def animation(self, value):
337339
self._animation = validate_types(value,
338340
types = AnimationOptions)
339341

342+
@property
343+
def axis_layout_runs(self) -> Optional[int]:
344+
"""The number of axis layout runs to execute when rendering the chart. Defaults to ``2``.
345+
346+
.. note::
347+
348+
When a chart with an x and a y-axis is rendered, we first pre-render the labels of both
349+
in order to measure them. Then, if either of the axis labels take up so much space that
350+
it significantly affects the length of the other axis, we repeat the process.
351+
352+
By default we stop at two axis layout runs, but it may be that the second run also alters
353+
the space required by either axis, for example if it causes the labels to rotate. In this
354+
situation, a subsequent redraw of the chart may cause the tick and label placement to
355+
change for apparently no reason.
356+
357+
Use the ``.axis_layout_runs`` property to set the maximum allowed number of repetitions.
358+
359+
.. warning::
360+
361+
Keep in mind that the default value of ``2`` is set because every run costs performance time.
362+
Changing this value option to higher than the default might decrease performance significantly,
363+
especially with bigger sets of data.
364+
365+
:returns: The maximum allowed number of axis layout runs.
366+
:rtype: :class:`int <python:int>`
367+
"""
368+
return self._axis_layout_runs
369+
370+
@axis_layout_runs.setter
371+
def axis_layout_runs(self, value):
372+
self._axis_layout_runs = validators.integer(value, allow_empty = True)
373+
340374
@property
341375
def background_color(self) -> Optional[str | Gradient | Pattern]:
342376
"""The background color or gradient for the outer chart area. Defaults to
@@ -1349,6 +1383,7 @@ def _get_kwargs_from_dict(cls, as_dict):
13491383
'align_ticks': as_dict.get('alignTicks', None),
13501384
'allow_mutating_data': as_dict.get('allowMutatingData', None),
13511385
'animation': as_dict.get('animation', None),
1386+
'axis_layout_runs': as_dict.get('axisLayoutRuns', None),
13521387
'background_color': as_dict.get('backgroundColor', None),
13531388
'border_color': as_dict.get('borderColor', None),
13541389
'border_radius': as_dict.get('borderRadius', None),
@@ -1403,6 +1438,7 @@ def _to_untrimmed_dict(self, in_cls = None) -> dict:
14031438
'alignTicks': self.align_ticks,
14041439
'allowMutatingData': self.allow_mutating_data,
14051440
'animation': self.animation,
1441+
'axisLayoutRuns': self.axis_layout_runs,
14061442
'backgroundColor': self.background_color,
14071443
'borderColor': self.border_color,
14081444
'borderRadius': self.border_radius,

highcharts_core/options/data.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def __init__(self, **kwargs):
3131
self._before_parse = None
3232
self._columns = None
3333
self._columns_url = None
34+
self._column_types = None
3435
self._complete = None
3536
self._csv = None
3637
self._csv_url = None
@@ -59,6 +60,7 @@ def __init__(self, **kwargs):
5960
self.before_parse = kwargs.get('before_parse', None)
6061
self.columns = kwargs.get('columns', None)
6162
self.columns_url = kwargs.get('columns_url', None)
63+
self.column_types = kwargs.get('column_types', None)
6264
self.complete = kwargs.get('complete', None)
6365
self.csv = kwargs.get('csv', None)
6466
self.csv_url = kwargs.get('csv_url', None)
@@ -185,6 +187,46 @@ def columns_url(self, value):
185187
except ValueError:
186188
raise error
187189

190+
@property
191+
def column_types(self) -> Optional[List[str]]:
192+
"""The data types for each column in the related CSV data.
193+
194+
This property expects an iterable of :class:`str <python:str>` values,
195+
where each item in the iterable corresponds to a column in the underlying
196+
CSV data. The type specified for each column may be one of the following values:
197+
198+
* ``'string'``
199+
* ``'number'``
200+
* ``'float'``
201+
* ``'date'``
202+
203+
Defaults to :obj:`None <python:None>`.
204+
205+
:returns: The data types for each column in the related CSV data.
206+
:rtype: :class:`list <python:list>` of :class:`str <python:str>` or
207+
:obj:`None <python:None>`
208+
"""
209+
return self._column_types
210+
211+
@column_types.setter
212+
def column_types(self, value):
213+
if not value:
214+
self._column_types = None
215+
else:
216+
values = []
217+
value = validators.iterable(value,
218+
forbid_literals = (str, bytes))
219+
for item in value:
220+
item = str(item).lower()
221+
if item not in constants.DATA_COLUMN_TYPES:
222+
raise errors.HighchartsValueError(
223+
f'column_types expects one of {constants.DATA_COLUMN_TYPES}. '
224+
f'Received: {item}'
225+
)
226+
values.append(item)
227+
228+
self._column_types = values
229+
188230
@property
189231
def complete(self) -> Optional[CallbackFunction]:
190232
"""The JavaScript callback function that is evaluated when the data has finished
@@ -716,6 +758,7 @@ def _get_kwargs_from_dict(cls, as_dict):
716758
'before_parse': as_dict.get('beforeParse', None),
717759
'columns': as_dict.get('columns', None),
718760
'columns_url': as_dict.get('columnsURL', None),
761+
'column_types': as_dict.get('columnTypes', None),
719762
'complete': as_dict.get('complete', None),
720763
'csv': as_dict.get('csv', None),
721764
'csv_url': as_dict.get('csvURL', None),
@@ -749,6 +792,7 @@ def _to_untrimmed_dict(self, in_cls = None) -> dict:
749792
'beforeParse': self.before_parse,
750793
'columns': self.columns,
751794
'columnsURL': self.columns_url,
795+
'columnTypes': self.column_types,
752796
'complete': self.complete,
753797
'csv': self.csv,
754798
'csvURL': self.csv_url,

highcharts_core/options/exporting/__init__.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ def __init__(self, **kwargs):
7878
self._enabled = None
7979
self._error = None
8080
self._fallback_to_export_server = None
81+
self._fetch_options = None
8182
self._filename = None
8283
self._form_attributes = None
8384
self._lib_url = None
@@ -104,6 +105,7 @@ def __init__(self, **kwargs):
104105
self.enabled = kwargs.get('enabled', None)
105106
self.error = kwargs.get('error', None)
106107
self.fallback_to_export_server = kwargs.get('fallback_to_export_server', None)
108+
self.fetch_options = kwargs.get('fetch_options', None)
107109
self.filename = kwargs.get('filename', None)
108110
self.form_attributes = kwargs.get('form_attributes', None)
109111
self.lib_url = kwargs.get('lib_url', None)
@@ -339,6 +341,24 @@ def fallback_to_export_server(self, value):
339341
else:
340342
self._fallback_to_export_server = bool(value)
341343

344+
@property
345+
def fetch_options(self) -> Optional[dict]:
346+
"""Options for the fetch request used when sending the SVG to the export server.
347+
Defaults to :obj:`None <python:None>`.
348+
349+
.. seealso::
350+
351+
* `MDN: Fetch <https://developer.mozilla.org/en-US/docs/Web/API/fetch>`__ for more information
352+
353+
:returns: The options for the fetch request, expressed as a Python :class:`dict <python:dict>`
354+
:rtype: :class:`dict <python:dict>` or :obj:`None <python:None>`
355+
"""
356+
return self._fetch_options
357+
358+
@fetch_options.setter
359+
def fetch_options(self, value):
360+
self._fetch_options = validators.dict(value, allow_empty = True)
361+
342362
@property
343363
def filename(self) -> Optional[str]:
344364
"""The filename (without file type extension) to use for the exported chart.
@@ -717,6 +737,7 @@ def _get_kwargs_from_dict(cls, as_dict):
717737
'enabled': as_dict.get('enabled', None),
718738
'error': as_dict.get('error', None),
719739
'fallback_to_export_server': as_dict.get('fallbackToExportServer', None),
740+
'fetch_options': as_dict.get('fetchOptions', None),
720741
'filename': as_dict.get('filename', None),
721742
'form_attributes': as_dict.get('formAttributes', None),
722743
'lib_url': as_dict.get('libURL', None),
@@ -748,6 +769,7 @@ def _to_untrimmed_dict(self, in_cls = None) -> dict:
748769
'enabled': self.enabled,
749770
'error': self.error,
750771
'fallbackToExportServer': self.fallback_to_export_server,
772+
'fetchOptions': self.fetch_options,
751773
'filename': self.filename,
752774
'formAttributes': self.form_attributes,
753775
'libURL': self.lib_url,

0 commit comments

Comments
 (0)