diff --git a/docs/source/examples/material_ui_button_no_action.py b/docs/source/examples/material_ui_button_no_action.py index 54c0cee00..ac3488e00 100644 --- a/docs/source/examples/material_ui_button_no_action.py +++ b/docs/source/examples/material_ui_button_no_action.py @@ -1,11 +1,10 @@ import idom -material_ui = idom.install("@material-ui/core") -MaterialButton = material_ui.define("Button", fallback="loading...") +material_ui = idom.install("@material-ui/core", fallback="loading...") idom.run( idom.element( - lambda: MaterialButton( + lambda: material_ui.Button( {"color": "primary", "variant": "contained"}, "Hello World!" ) ) diff --git a/docs/source/examples/material_ui_button_on_click.py b/docs/source/examples/material_ui_button_on_click.py index aeb646142..808816da0 100644 --- a/docs/source/examples/material_ui_button_on_click.py +++ b/docs/source/examples/material_ui_button_on_click.py @@ -3,8 +3,7 @@ import idom -material_ui = idom.install("@material-ui/core") -MaterialButton = material_ui.define("Button", fallback="loading...") +material_ui = idom.install("@material-ui/core", fallback="loading...") @idom.element @@ -12,7 +11,7 @@ def ViewSliderEvents(): event, set_event = idom.hooks.use_state(None) return idom.html.div( - MaterialButton( + material_ui.Button( { "color": "primary", "variant": "contained", diff --git a/docs/source/examples/material_ui_slider.py b/docs/source/examples/material_ui_slider.py index 645ce4386..56dc867a6 100644 --- a/docs/source/examples/material_ui_slider.py +++ b/docs/source/examples/material_ui_slider.py @@ -3,8 +3,7 @@ import idom -material_ui = idom.install("@material-ui/core") -MaterialSlider = material_ui.define("Slider", fallback="loading...") +material_ui = idom.install("@material-ui/core", fallback="loading...") @idom.element @@ -12,7 +11,7 @@ def ViewSliderEvents(): event, set_event = idom.hooks.use_state(None) return idom.html.div( - MaterialSlider( + material_ui.Slider( { "color": "primary", "step": 10, diff --git a/docs/source/examples/simple_dashboard.py b/docs/source/examples/simple_dashboard.py index fa1d15f9d..87db2e9ae 100644 --- a/docs/source/examples/simple_dashboard.py +++ b/docs/source/examples/simple_dashboard.py @@ -6,7 +6,7 @@ from idom.widgets.html import Input -VictoryLine = idom.install("victory").define("VictoryLine", fallback="loading...") +victory = idom.install("victory", fallback="loading...") @idom.element @@ -53,7 +53,7 @@ async def animate(): } set_data(data[1:] + [next_data_point]) - return VictoryLine({"data": data, "style": {"parent": {"width": "500px"}}}) + return victory.VictoryLine({"data": data, "style": {"parent": {"width": "500px"}}}) @idom.element diff --git a/docs/source/examples/super_simple_chart.py b/docs/source/examples/super_simple_chart.py index bd7f95ea8..935de6050 100644 --- a/docs/source/examples/super_simple_chart.py +++ b/docs/source/examples/super_simple_chart.py @@ -4,13 +4,12 @@ path_to_source_file = Path(__file__).parent / "super_simple_chart.js" -super_simple_chart = idom.Module("super-simple-chart", source_file=path_to_source_file) -SuperSimpleChart = super_simple_chart.define("SuperSimpleChart") +ssc = idom.Module("super-simple-chart", source_file=path_to_source_file) idom.run( idom.element( - lambda: SuperSimpleChart( + lambda: ssc.SuperSimpleChart( { "data": [ {"x": 1, "y": 2}, diff --git a/docs/source/examples/victory_chart.py b/docs/source/examples/victory_chart.py index 7bbbe5ef9..234a32175 100644 --- a/docs/source/examples/victory_chart.py +++ b/docs/source/examples/victory_chart.py @@ -1,10 +1,9 @@ import idom -victory = idom.install("victory") -VictoryBar = victory.define("VictoryBar", fallback="loading...") +victory = idom.install("victory", fallback="loading...") idom.run( idom.element( - lambda: VictoryBar({"style": {"parent": {"width": "500px"}}}), + lambda: victory.VictoryBar({"style": {"parent": {"width": "500px"}}}), ) ) diff --git a/idom/client/app/package-lock.json b/idom/client/app/package-lock.json index e6f1b26fa..52a390a47 100644 --- a/idom/client/app/package-lock.json +++ b/idom/client/app/package-lock.json @@ -1,6 +1,6 @@ { "name": "idom-client-react", - "version": "0.5.1", + "version": "0.5.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/idom/client/module.py b/idom/client/module.py index d3fb5dd5d..de70ec7a4 100644 --- a/idom/client/module.py +++ b/idom/client/module.py @@ -10,17 +10,27 @@ @overload -def install(packages: str) -> "Module": +def install( + packages: str, + ignore_installed: bool, + fallback: Optional[str], +) -> "Module": ... @overload -def install(packages: Union[List[str], Tuple[str]]) -> List["Module"]: +def install( + packages: Union[List[str], Tuple[str]], + ignore_installed: bool, + fallback: Optional[str], +) -> List["Module"]: ... def install( - packages: Union[str, List[str], Tuple[str]], ignore_installed: bool = False + packages: Union[str, List[str], Tuple[str]], + ignore_installed: bool = False, + fallback: Optional[str] = None, ) -> Union["Module", List["Module"]]: return_one = False if isinstance(packages, str): @@ -38,7 +48,11 @@ def install( f"{client.current} failed to discover {list(not_discovered)}." ) - return Module(pkg_names.pop()) if return_one else [Module(pkg) for pkg in pkg_names] + return ( + Module(pkg_names.pop(), fallback=fallback) + if return_one + else [Module(pkg, fallback=fallback) for pkg in pkg_names] + ) class Module: @@ -63,7 +77,7 @@ class Module: The URL this module will be imported from. """ - __slots__ = "url", "exports", "fallback" + __slots__ = "url", "fallback", "exports", "_export_names" def __init__( self, @@ -73,7 +87,7 @@ def __init__( check_exports: bool = True, ) -> None: self.fallback = fallback - self.exports: Optional[List[str]] = None + self._export_names: Optional[List[str]] = None if source_file is not None: self.url = ( client.current.web_module_url(url_or_name) @@ -81,17 +95,18 @@ def __init__( else client.current.add_web_module(url_or_name, source_file) ) if check_exports: - self.exports = client.current.web_module_exports(url_or_name) + self._export_names = client.current.web_module_exports(url_or_name) elif client.current.web_module_exists(url_or_name): self.url = client.current.web_module_url(url_or_name) if check_exports: - self.exports = client.current.web_module_exports(url_or_name) + self._export_names = client.current.web_module_exports(url_or_name) elif _is_url(url_or_name): self.url = url_or_name else: raise ValueError(f"{url_or_name!r} is not installed or is not a URL") + self.exports = {name: self.declare(name) for name in (self._export_names or [])} - def define( + def declare( self, name: str, has_children: bool = True, @@ -109,16 +124,19 @@ def define( this :class:`Module` instance. """ if ( - self.exports is not None + self._export_names is not None # if 'default' is exported there's not much we can infer - and "default" not in self.exports + and "default" not in self._export_names ): - if name not in self.exports: + if name not in self._export_names: raise ValueError( - f"{self} does not export {name!r}, available options are {self.exports}" + f"{self} does not export {name!r}, available options are {self._export_names}" ) return Import(self.url, name, has_children, fallback=fallback or self.fallback) + def __getattr__(self, name: str) -> "Import": + return self.exports.get(name) or self.declare(name) + def __repr__(self) -> str: return f"{type(self).__name__}({self.url})" diff --git a/tests/test_client/test_module.py b/tests/test_client/test_module.py index fb0031a2e..a2d34d7ab 100644 --- a/tests/test_client/test_module.py +++ b/tests/test_client/test_module.py @@ -16,7 +16,7 @@ def test_any_relative_or_abolute_url_allowed(): def test_module_import_repr(): assert ( - repr(Module("/absolute/url/module").define("SomeComponent")) + repr(Module("/absolute/url/module").declare("SomeComponent")) == "Import(name='SomeComponent', source='/absolute/url/module', fallback=None)" ) @@ -27,7 +27,7 @@ def test_module_does_not_exist(): def test_installed_module(driver, display, victory): - display(victory.define("VictoryBar")) + display(victory.VictoryBar) driver.find_element_by_class_name("VictoryContainer") @@ -79,25 +79,21 @@ def add_web_module(self, package_name, source): fake = Module("fake-name") assert fake.url == "./mock/url/to/module-fake-name.js" - assert fake.exports == ["x", "y", "z"] + assert list(fake.exports) == ["x", "y", "z"] assert fake.fallback is None with pytest.raises(ValueError, match="does not export 'DoesNotExist'"): - fake.define("DoesNotExist") - - for name in fake.exports: - fake.define(name) + fake.declare("DoesNotExist") def test_module_from_source(driver, driver_wait, display): test_module = Module("test-module", source_file=HERE / "test_js_module.js") - test_button = test_module.define("TestButton") response_data = idom.Ref(None) @idom.element def ShowButton(): - return test_button( + return test_module.TestButton( { "id": "test-button", "onClick": lambda event: response_data.set_current(event["data"]),