Skip to content

Commit

Permalink
Merge pull request #289 from vizzuhq/plugin
Browse files Browse the repository at this point in the history
Added: `chart.plugin` method
  • Loading branch information
veghdev authored Dec 21, 2023
2 parents 7906c54 + 5bf28e0 commit ecd7096
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 37 deletions.
37 changes: 33 additions & 4 deletions src/ipyvizzu/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@

from ipyvizzu.animation import AbstractAnimation, Snapshot, AnimationMerger
from ipyvizzu.animationcontrol import AnimationControl
from ipyvizzu.method import Animate, Feature, Store, EventOn, EventOff, Log
from ipyvizzu.template import ChartProperty, DisplayTarget, DisplayTemplate
from ipyvizzu.method import Animate, Feature, Plugin, Store, EventOn, EventOff, Log
from ipyvizzu.template import (
ChartProperty,
DisplayTarget,
DisplayTemplate,
VIZZU as VIZZU_URL,
)
from ipyvizzu.event import EventHandler
from ipyvizzu.__version__ import __version__

Expand All @@ -20,8 +25,8 @@ class Chart:

# pylint: disable=too-many-instance-attributes

VIZZU: str = "https://cdn.jsdelivr.net/npm/[email protected]/dist/vizzu.min.js"
"""A variable for storing the default url of vizzu package."""
VIZZU: str = VIZZU_URL
"""A variable for storing the default url of the `vizzu` package."""

def __init__(
self,
Expand Down Expand Up @@ -225,6 +230,30 @@ def feature(self, name: str, enabled: bool) -> None:
)
)

def plugin(
self,
plugin: str,
options: Optional[dict] = None,
name: str = "default",
enabled: bool = True,
) -> None:
"""
A method for register/unregister plugins of the chart.
Args:
plugin: The package name or the url of the plugin.
options: The plugin constructor options.
name: The name of the plugin (default `default`).
enabled: The state of the plugin (default `True`).
"""

self._display(
DisplayTemplate.PLUGIN.format(
chart_id=self._chart_id,
**Plugin(plugin, options, name, enabled).dump(),
)
)

def store(self) -> Snapshot:
"""
A method for saving and storing the actual state of the chart.
Expand Down
90 changes: 64 additions & 26 deletions src/ipyvizzu/method.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@

from ipyvizzu.animation import AbstractAnimation, PlainAnimation
from ipyvizzu.event import EventHandler
from ipyvizzu.template import ChartProperty
from ipyvizzu.json import RawJavaScriptEncoder
from ipyvizzu.template import ChartProperty, VIZZU_VERSION


class Method:
"""A class for storing and dumping any kind of data."""
"""
A class for dumping chart independent parameters to
[DisplayTemplate.STORE][ipyvizzu.template.DisplayTemplate] template.
"""

# pylint: disable=too-few-public-methods

Expand All @@ -28,8 +32,7 @@ def dump(self) -> dict:

class Animate(Method):
"""
A class for dumping chart independent parameters to
[DisplayTemplate.ANIMATE][ipyvizzu.template.DisplayTemplate] template.
It stores and dumps `chart_target` and `chart_anim_opts` parameters.
"""

# pylint: disable=too-few-public-methods
Expand All @@ -42,8 +45,6 @@ def __init__(
"""
Animate constructor.
It stores and dumps `chart_target` and `chart_anim_opts` parameters.
Args:
chart_target:
AbstractAnimation inherited object such as
Expand All @@ -64,8 +65,7 @@ def __init__(

class Feature(Method):
"""
A class for dumping chart independent parameters to
[DisplayTemplate.FEATURE][ipyvizzu.template.DisplayTemplate] template.
It stores and dumps `name` and `enabled` parameters.
"""

# pylint: disable=too-few-public-methods
Expand All @@ -74,8 +74,6 @@ def __init__(self, name: str, enabled: bool):
"""
Feature constructor.
It stores and dumps `name` and `enabled` parameters.
Args:
name: The name of a chart feature.
enabled: The new state of a chart feature.
Expand All @@ -84,10 +82,61 @@ def __init__(self, name: str, enabled: bool):
self._data = {"name": name, "enabled": json.dumps(enabled)}


class Plugin(Method):
"""
It stores and dumps `plugin`, `options` and `name` parameters.
"""

def __init__(self, plugin: str, options: Optional[dict], name: str, enabled: bool):
"""
Plugin constructor.
Args:
plugin: The package name or the url of the plugin.
options: The plugin constructor options.
name: The name of the plugin (default `default`).
enabled: The state of the plugin (default `True`).
"""

self._data = {
"plugin": Plugin.resolve_url(plugin),
"options": {}
if options is None
else json.dumps(options, cls=RawJavaScriptEncoder),
"name": name,
"enabled": json.dumps(enabled),
}

@staticmethod
def resolve_url(plugin: str) -> str:
"""
A static method for resolving the url of the plugin.
Args:
plugin: The package name or the url of the plugin.
Returns:
The url of the plugin.
"""

if Plugin._is_url(plugin):
return plugin
return Plugin._get_url(plugin)

@staticmethod
def _is_url(plugin: str) -> bool:
return "/" in plugin

@staticmethod
def _get_url(plugin: str) -> str:
jsdelivr = "https://cdn.jsdelivr.net/npm/@vizzu"
tag = f"vizzu-{VIZZU_VERSION}"
return f"{jsdelivr}/{plugin}@{tag}/dist/mjs/index.min.js"


class Store(Method):
"""
A class for dumping chart independent parameters to
[DisplayTemplate.STORE][ipyvizzu.template.DisplayTemplate] template.
It stores and dumps `snapshot_id` parameter.
"""

# pylint: disable=too-few-public-methods
Expand All @@ -96,8 +145,6 @@ def __init__(self, snapshot_id: str):
"""
Store constructor.
It stores and dumps `snapshot_id` parameter.
Args:
snapshot_id: The id of snapshot object.
"""
Expand All @@ -107,8 +154,7 @@ def __init__(self, snapshot_id: str):

class EventOn(Method):
"""
A class for dumping chart independent parameters to
[DisplayTemplate.SET_EVENT][ipyvizzu.template.DisplayTemplate] template.
It stores and dumps the `id`, the `event` and the `handler` of the event handler object.
"""

# pylint: disable=too-few-public-methods
Expand All @@ -117,8 +163,6 @@ def __init__(self, event_handler: EventHandler):
"""
EventOn constructor.
It stores and dumps the `id`, the `event` and the `handler` of the event handler object.
Args:
event_handler: An event handler object.
"""
Expand All @@ -132,8 +176,7 @@ def __init__(self, event_handler: EventHandler):

class EventOff(Method):
"""
A class for dumping chart independent parameters to
[DisplayTemplate.CLEAR_EVENT][ipyvizzu.template.DisplayTemplate] template.
It stores and dumps the `id` and the `event` of the event handler object.
"""

# pylint: disable=too-few-public-methods
Expand All @@ -142,8 +185,6 @@ def __init__(self, event_handler: EventHandler):
"""
EventOff constructor.
It stores and dumps the `id` and the `event` of the event handler object.
Args:
event_handler: An event handler object.
"""
Expand All @@ -153,8 +194,7 @@ def __init__(self, event_handler: EventHandler):

class Log(Method):
"""
A class for dumping chart independent parameters to
[DisplayTemplate.LOG][ipyvizzu.template.DisplayTemplate] template.
It stores and dumps the value of the chart property object.
"""

# pylint: disable=too-few-public-methods
Expand All @@ -163,8 +203,6 @@ def __init__(self, chart_property: ChartProperty):
"""
Log constructor.
It stores and dumps the value of the chart property object.
Args:
chart_property:
A chart property such as
Expand Down
12 changes: 12 additions & 0 deletions src/ipyvizzu/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

from enum import Enum

VIZZU_VERSION: str = "0.9"
"""A variable for storing the default version of the `vizzu` package."""

VIZZU: str = f"https://cdn.jsdelivr.net/npm/vizzu@{VIZZU_VERSION}/dist/vizzu.min.js"
"""A variable for storing the default url of the `vizzu` package."""


class ChartProperty(Enum):
"""An enum class for storing chart properties."""
Expand Down Expand Up @@ -60,6 +66,12 @@ class DisplayTemplate:
)
"""Call feature JavaScript method."""

PLUGIN: str = (
"window.ipyvizzu.plugin(element, "
+ "'{chart_id}', '{plugin}', {options}, '{name}', {enabled});"
)
"""Call plugin JavaScript method."""

STORE: str = "window.ipyvizzu.store(element, '{chart_id}', '{id}');"
"""Call store JavaScript method."""

Expand Down
25 changes: 25 additions & 0 deletions src/ipyvizzu/templates/ipyvizzu.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ if (window.IpyVizzu?.version !== '__version__') {
this.events = {}
this.loaded = {}
this.libs = {}
this.plugins = {}
}

static clearInhibitScroll(element) {
Expand All @@ -55,6 +56,30 @@ if (window.IpyVizzu?.version !== '__version__') {
this._moveHere(chartId, element)
}

plugin(element, chartId, plugin, options, name, enabled) {
this.charts[chartId] = this.charts[chartId].then((chart) => {
if (!this.plugins[plugin]) {
this.plugins[plugin] = import(plugin).catch((error) => {
console.error('Error importing plugin:', plugin, error)
return null
})
}

return this.plugins[plugin].then((pluginModule) => {
if (pluginModule) {
const pluginInstance = new pluginModule[name](options)
if (enabled) {
chart.feature(pluginInstance, true)
} else {
chart.feature(pluginInstance.meta.name, false)
}
}

return chart
})
})
}

animate(
element,
chartId,
Expand Down
56 changes: 53 additions & 3 deletions tests/test_chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ class IPy:
)


class TestChartMethods(TestChart):
class TestChartAnimateMethod(TestChart):
def test_animate_chart_target_has_to_be_passed(self) -> None:
with self.assertRaises(ValueError):
self.chart.animate()
Expand Down Expand Up @@ -312,6 +312,54 @@ def test_animate_with_not_default_scroll_into_view(self) -> None:
+ "undefined);",
)


class TestChartPluginMethod(TestChart):
URL = "https://cdn.jsdelivr.net/npm/@vizzu/[email protected]/dist/mjs/index.min.js"

def test_plugin_with_package_name(self) -> None:
with unittest.mock.patch(self.mock) as output:
self.chart.plugin("marker-dropshadow")
self.assertEqual(
self.normalizer.normalize_output(output),
f"window.ipyvizzu.plugin(element, id, '{self.URL}', {{}}, 'default', true);",
)

def test_plugin_with_package_url(self) -> None:
with unittest.mock.patch(self.mock) as output:
self.chart.plugin(self.URL)
self.assertEqual(
self.normalizer.normalize_output(output),
f"window.ipyvizzu.plugin(element, id, '{self.URL}', {{}}, 'default', true);",
)

def test_plugin_with_options(self) -> None:
with unittest.mock.patch(self.mock) as output:
self.chart.plugin("marker-dropshadow", options={"debug": True})
self.assertEqual(
self.normalizer.normalize_output(output),
"window.ipyvizzu.plugin(element, id, "
+ f"'{self.URL}', {{\"debug\": true}}, 'default', true);",
)

def test_plugin_with_name(self) -> None:
with unittest.mock.patch(self.mock) as output:
name = "MarkerDropshadow"
self.chart.plugin("marker-dropshadow", name=name)
self.assertEqual(
self.normalizer.normalize_output(output),
f"window.ipyvizzu.plugin(element, id, '{self.URL}', {{}}, '{name}', true);",
)

def test_plugin_with_enabled(self) -> None:
with unittest.mock.patch(self.mock) as output:
self.chart.plugin("marker-dropshadow", enabled=False)
self.assertEqual(
self.normalizer.normalize_output(output),
f"window.ipyvizzu.plugin(element, id, '{self.URL}', {{}}, 'default', false);",
)


class TestChartFeatureMethod(TestChart):
def test_feature(self) -> None:
with unittest.mock.patch(self.mock) as output:
self.chart.feature("tooltip", True)
Expand All @@ -320,6 +368,8 @@ def test_feature(self) -> None:
"window.ipyvizzu.feature(element, id, 'tooltip', true);",
)


class TestChartStoreMethod(TestChart):
def test_store(self) -> None:
with unittest.mock.patch(self.mock) as output:
self.chart.store()
Expand All @@ -329,7 +379,7 @@ def test_store(self) -> None:
)


class TestChartEvents(TestChart):
class TestChartEventMethods(TestChart):
def test_on(self) -> None:
with unittest.mock.patch(self.mock) as output:
handler_method = """event.renderingContext.fillStyle =
Expand All @@ -355,7 +405,7 @@ def test_off(self) -> None:
)


class TestChartLogs(TestChart):
class TestChartLogMethod(TestChart):
def test_log_config(self) -> None:
with unittest.mock.patch(self.mock) as output:
self.chart.log(ChartProperty.CONFIG)
Expand Down
Loading

0 comments on commit ecd7096

Please sign in to comment.