From 72df595898b663531cf9a8a45662ede1471436cb Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Fri, 6 Sep 2024 13:53:10 -0400 Subject: [PATCH 01/25] fix: allows keys to be set in props --- .../deephaven/ui/components/make_component.py | 5 ++- .../src/deephaven/ui/elements/BaseElement.py | 17 ++++++++-- .../ui/src/deephaven/ui/elements/Element.py | 20 ++++++++++++ .../deephaven/ui/elements/FunctionElement.py | 14 +++++++- .../ui/src/deephaven/ui/renderer/Renderer.py | 32 +++++++++++++++++-- 5 files changed, 80 insertions(+), 8 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/components/make_component.py b/plugins/ui/src/deephaven/ui/components/make_component.py index b7e43c652..c96634282 100644 --- a/plugins/ui/src/deephaven/ui/components/make_component.py +++ b/plugins/ui/src/deephaven/ui/components/make_component.py @@ -17,9 +17,8 @@ def make_component(func: Callable[..., Any]): """ @functools.wraps(func) - def make_component_node(*args: Any, **kwargs: Any): + def make_component_node(*args: Any, key: str | None = None, **kwargs: Any): component_type = get_component_qualname(func) - - return FunctionElement(component_type, lambda: func(*args, **kwargs)) + return FunctionElement(component_type, lambda: func(*args, **kwargs), key=key) return make_component_node diff --git a/plugins/ui/src/deephaven/ui/elements/BaseElement.py b/plugins/ui/src/deephaven/ui/elements/BaseElement.py index e23cf7d9e..97f8ae459 100644 --- a/plugins/ui/src/deephaven/ui/elements/BaseElement.py +++ b/plugins/ui/src/deephaven/ui/elements/BaseElement.py @@ -11,22 +11,35 @@ class BaseElement(Element): Must provide a name for the element. """ - def __init__(self, name: str, /, *children: Any, **props: Any): + def __init__( + self, name: str, /, *children: Any, key: str | None = None, **props: Any + ): self._name = name + self._keyProp = key + if len(children) > 0 and props.get("children") is not None: raise ValueError("Cannot provide both children and props.children") - if len(children) > 1: props["children"] = list(children) if len(children) == 1: # If there's only one child, we pass it as a single child, not a list # There are many React elements that expect only a single child, and will fail if they get a list (even if it only has one element) props["children"] = children[0] + self._props = dict_to_camel_case(props) @property def name(self) -> str: return self._name + @property + def key_prop(self) -> str | None: + return self._keyProp + + def generate_key(self, index_key: str) -> str: + if self._keyProp is not None: + return self._keyProp + return f"{index_key}-{self._name}" + def render(self, context: RenderContext) -> dict[str, Any]: return self._props diff --git a/plugins/ui/src/deephaven/ui/elements/Element.py b/plugins/ui/src/deephaven/ui/elements/Element.py index 810f52fea..6a3fa8e4e 100644 --- a/plugins/ui/src/deephaven/ui/elements/Element.py +++ b/plugins/ui/src/deephaven/ui/elements/Element.py @@ -22,6 +22,26 @@ def name(self) -> str: """ return "deephaven.ui.Element" + @property + def key_prop(self) -> str | None: + """ + Get the key prop of this element. Useful to check if a key prop was provided. + + Returns: + The unique key prop of this element. + """ + return None + + @abstractmethod + def generate_key(self, index_key: str) -> str: + """ + Get the key of this element. Subclasses should access the key prop if it exists or generate their own unique key. + + Returns: + The unique key of this element. + """ + pass + @abstractmethod def render(self, context: RenderContext) -> PropsType: """ diff --git a/plugins/ui/src/deephaven/ui/elements/FunctionElement.py b/plugins/ui/src/deephaven/ui/elements/FunctionElement.py index bcf63188c..24095de80 100644 --- a/plugins/ui/src/deephaven/ui/elements/FunctionElement.py +++ b/plugins/ui/src/deephaven/ui/elements/FunctionElement.py @@ -8,7 +8,9 @@ class FunctionElement(Element): - def __init__(self, name: str, render: Callable[[], list[Element]]): + def __init__( + self, name: str, render: Callable[[], list[Element]], key: str | None = None + ): """ Create an element that takes a function to render. @@ -18,11 +20,21 @@ def __init__(self, name: str, render: Callable[[], list[Element]]): """ self._name = name self._render = render + self._keyProp = key @property def name(self): return self._name + @property + def key_prop(self) -> str | None: + return self._keyProp + + def generate_key(self, index_key: str) -> str: + if self._keyProp is not None: + return self._keyProp + return f"{index_key}-{self._name}" + def render(self, context: RenderContext) -> PropsType: """ Render the component. Should only be called when actually rendering the component, e.g. exporting it to the client. diff --git a/plugins/ui/src/deephaven/ui/renderer/Renderer.py b/plugins/ui/src/deephaven/ui/renderer/Renderer.py index 496f5e7ed..62345b197 100644 --- a/plugins/ui/src/deephaven/ui/renderer/Renderer.py +++ b/plugins/ui/src/deephaven/ui/renderer/Renderer.py @@ -25,8 +25,8 @@ def _get_context_key(item: Any, index_key: str) -> Union[str, None]: - TODO #731: use a `key` prop if it exists on the `Element`. """ if isinstance(item, Element): - return f"{index_key}-{item.name}" - if isinstance(item, (Dict, List, Tuple)): + return item.generate_key(index_key) + if isinstance(item, (Dict, List, Tuple, map)): return index_key return None @@ -61,6 +61,8 @@ def _render_item(item: Any, context: RenderContext) -> Any: The rendered item. """ logger.debug("_render_item context is %s", context) + if isinstance(item, map): + return _render_map(item, context) if isinstance(item, (list, tuple)): # I couldn't figure out how to map a `list[Unknown]` to a `list[Any]`, or to accept a `list[Unknown]` as a parameter return _render_list(item, context) # type: ignore @@ -78,6 +80,32 @@ def _render_item(item: Any, context: RenderContext) -> Any: return item +def _render_map(item: map, context: RenderContext) -> list[Any]: + """ + Renders a map object. Checks if each child has a unique key and then the map as a list + + Args: + item: The map to render. + context: The context to render the map in. + + Returns: + The rendered map. + """ + has_sent_error = False # Only throw the error once + logger.debug("_render_map %s", item) + with context.open(): + result = [] + used_keys = set() + for key, value in enumerate(item): + if isinstance(value, Element): + if value.key_prop in used_keys and not has_sent_error: + logger.error('Each child in a map should have a unique "key" prop.') + has_sent_error = True + used_keys.add(value.key_prop) + result.append(_render_child_item(value, str(key), context)) + return result + + def _render_list( item: list[Any] | tuple[Any, ...], context: RenderContext ) -> list[Any]: From 580a72eba1d9a4da0014fb20af5a24100845566e Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Fri, 6 Sep 2024 14:06:58 -0400 Subject: [PATCH 02/25] fix: define generate_key for UITable --- plugins/ui/src/deephaven/ui/elements/UITable.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/ui/src/deephaven/ui/elements/UITable.py b/plugins/ui/src/deephaven/ui/elements/UITable.py index 77680f052..9ce549557 100644 --- a/plugins/ui/src/deephaven/ui/elements/UITable.py +++ b/plugins/ui/src/deephaven/ui/elements/UITable.py @@ -185,6 +185,9 @@ def _with_dict_prop(self, key: str, value: dict[str, Any]) -> "UITable": ) # Turn missing or explicit None into empty dict return UITable(**{**self._props, key: {**existing, **value}}) # type: ignore + def generate_key(self, index_key: str) -> str: + return f"{index_key}-{self.name}" + def render(self, context: RenderContext) -> dict[str, Any]: logger.debug("Returning props %s", self._props) return dict_to_camel_case({**self._props}) From ceb0a54781330662f3b8af737ffb642d25ae0d53 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Fri, 6 Sep 2024 14:18:50 -0400 Subject: [PATCH 03/25] fix: use Optional for 3.8 and 3.9 --- plugins/ui/src/deephaven/ui/components/make_component.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/components/make_component.py b/plugins/ui/src/deephaven/ui/components/make_component.py index c96634282..15fc8bee8 100644 --- a/plugins/ui/src/deephaven/ui/components/make_component.py +++ b/plugins/ui/src/deephaven/ui/components/make_component.py @@ -1,6 +1,6 @@ import functools import logging -from typing import Any, Callable +from typing import Any, Callable, Optional from .._internal import get_component_qualname from ..elements import FunctionElement @@ -17,7 +17,7 @@ def make_component(func: Callable[..., Any]): """ @functools.wraps(func) - def make_component_node(*args: Any, key: str | None = None, **kwargs: Any): + def make_component_node(*args: Any, key: Optional[str] = None, **kwargs: Any): component_type = get_component_qualname(func) return FunctionElement(component_type, lambda: func(*args, **kwargs), key=key) From 90625899a91949185bba9d7e2b07edff882ac3f1 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Fri, 6 Sep 2024 15:31:53 -0400 Subject: [PATCH 04/25] feat: experimental key prop decorator --- plugins/ui/src/deephaven/ui/components/flex.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/ui/src/deephaven/ui/components/flex.py b/plugins/ui/src/deephaven/ui/components/flex.py index a015bc51d..17025709b 100644 --- a/plugins/ui/src/deephaven/ui/components/flex.py +++ b/plugins/ui/src/deephaven/ui/components/flex.py @@ -1,5 +1,6 @@ from __future__ import annotations from typing import Any +from functools import wraps from .basic import component_element from .types import ( LayoutFlex, @@ -12,6 +13,15 @@ ) +def add_key_prop(f: Any) -> Any: + @wraps(f) + def wrapper(*args, key: str | None = None, **kwargs): + return f(*args, key=key, **kwargs) + + return wrapper + + +@add_key_prop def flex( *children: Any, flex: LayoutFlex | None = "auto", From 44e7d6c49550ae214c1e5904aa3d6e041b0b0a24 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Fri, 6 Sep 2024 17:18:26 -0400 Subject: [PATCH 05/25] feat: experimental key prop decorator v2 --- .../ui/src/deephaven/ui/components/flex.py | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/components/flex.py b/plugins/ui/src/deephaven/ui/components/flex.py index 17025709b..79d6b4092 100644 --- a/plugins/ui/src/deephaven/ui/components/flex.py +++ b/plugins/ui/src/deephaven/ui/components/flex.py @@ -1,5 +1,6 @@ from __future__ import annotations from typing import Any +from inspect import signature, Parameter from functools import wraps from .basic import component_element from .types import ( @@ -13,14 +14,36 @@ ) +def wrap_prop(f: Any, wrapper: Any, desc: str) -> Any: + sig = signature(f) + params = list(sig.parameters.values()) + wrapper_params = list(signature(wrapper).parameters.values())[1:-1] + + for wrapper_param in wrapper_params: + params.insert(1, wrapper_param) + + wrapper = wraps(f)(wrapper) + wrapper.__signature__ = sig.replace(parameters=params) + wrapper.__doc__ += f" {desc}\n" + + return wrapper + + def add_key_prop(f: Any) -> Any: - @wraps(f) def wrapper(*args, key: str | None = None, **kwargs): return f(*args, key=key, **kwargs) - return wrapper + return wrap_prop(f, wrapper, "key: A unique key for the component.") + + +def add_qwerty_prop(f: Any) -> Any: + def wrapper(*args, qwerty: int | None = None, **kwargs): + return f(*args, qwerty=qwerty, **kwargs) + + return wrap_prop(f, wrapper, "qwerty: qwerty.") +@add_qwerty_prop @add_key_prop def flex( *children: Any, From 1c17db0a138317b77679f102f50a39a216910523 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Mon, 9 Sep 2024 14:35:31 -0400 Subject: [PATCH 06/25] feat: experimental element maker --- .../ui/src/deephaven/ui/components/_maker.py | 47 ++++++++++++ .../ui/src/deephaven/ui/components/flex.py | 75 ++++--------------- 2 files changed, 60 insertions(+), 62 deletions(-) create mode 100644 plugins/ui/src/deephaven/ui/components/_maker.py diff --git a/plugins/ui/src/deephaven/ui/components/_maker.py b/plugins/ui/src/deephaven/ui/components/_maker.py new file mode 100644 index 000000000..1b2e12e95 --- /dev/null +++ b/plugins/ui/src/deephaven/ui/components/_maker.py @@ -0,0 +1,47 @@ +from typing import Any +from inspect import signature +from functools import wraps +from ..elements import BaseElement + +params = { + "wrap": "Whether children should wrap when they exceed the panel's width.", + "justify_content": "The distribution of space around items along the main axis.", + "align_content": "The distribution of space between and around items along the cross axis.", + "align_items": "The alignment of children within their container.", + "gap": "The space to display between both rows and columns of children.", + "column_gap": "The space to display between columns of children.", + "row_gap": "The space to display between rows of children.", + "key": "A unique key for the element", +} + + +def make_element( + description: str, + override_params: dict[str, str] = {}, + return_string: str | None = None, +): + def decorator(f: Any): + nonlocal return_string + + @wraps(f) + def wrapper(*args, **kwargs): + props = f(*args, **kwargs) + return BaseElement(f"deephaven.ui.components.{f.__name__}", **props) + + sig = signature(f) + if return_string is None: + return_string = f"The rendered {f.__name__.replace('_', ' ')} component." + + wrapper.__doc__ = f"{description}\n\nArgs:\n" + for name in (param.name for param in sig.parameters.values()): + if name in override_params: + wrapper.__doc__ += f" {name}: {override_params[name]}\n" + elif name in params: + wrapper.__doc__ += f" {name}: {params[name]}\n" + else: + raise ValueError(f'Docs of parameter "{name}" not specified.') + wrapper.__doc__ += f"\nReturns:\n {return_string}\n" + + return wrapper + + return decorator diff --git a/plugins/ui/src/deephaven/ui/components/flex.py b/plugins/ui/src/deephaven/ui/components/flex.py index 79d6b4092..72c445726 100644 --- a/plugins/ui/src/deephaven/ui/components/flex.py +++ b/plugins/ui/src/deephaven/ui/components/flex.py @@ -1,8 +1,7 @@ from __future__ import annotations from typing import Any -from inspect import signature, Parameter +from inspect import signature from functools import wraps -from .basic import component_element from .types import ( LayoutFlex, Direction, @@ -12,41 +11,22 @@ AlignItems, DimensionValue, ) +from ._maker import make_element +_override_params = { + "children": "Elements to render in the flexbox.", + "flex": "The flex property of the flexbox.", + "direction": "The direction of the flexbox.", +} -def wrap_prop(f: Any, wrapper: Any, desc: str) -> Any: - sig = signature(f) - params = list(sig.parameters.values()) - wrapper_params = list(signature(wrapper).parameters.values())[1:-1] - for wrapper_param in wrapper_params: - params.insert(1, wrapper_param) - - wrapper = wraps(f)(wrapper) - wrapper.__signature__ = sig.replace(parameters=params) - wrapper.__doc__ += f" {desc}\n" - - return wrapper - - -def add_key_prop(f: Any) -> Any: - def wrapper(*args, key: str | None = None, **kwargs): - return f(*args, key=key, **kwargs) - - return wrap_prop(f, wrapper, "key: A unique key for the component.") - - -def add_qwerty_prop(f: Any) -> Any: - def wrapper(*args, qwerty: int | None = None, **kwargs): - return f(*args, qwerty=qwerty, **kwargs) - - return wrap_prop(f, wrapper, "qwerty: qwerty.") - - -@add_qwerty_prop -@add_key_prop +@make_element( + "Base Flex component for laying out children in a flexbox.", + override_params=_override_params, +) def flex( *children: Any, + key: str | None = None, flex: LayoutFlex | None = "auto", direction: Direction | None = None, wrap: Wrap | None = None, @@ -56,34 +36,5 @@ def flex( gap: DimensionValue | None = "size-100", column_gap: DimensionValue | None = None, row_gap: DimensionValue | None = None, - **props: Any, ): - """ - Base Flex component for laying out children in a flexbox. - - Args: - *children: Elements to render in the flexbox. - flex: The flex property of the flexbox. - direction: The direction in which to layout children. - wrap: Whether children should wrap when they exceed the panel's width. - justify_content: The distribution of space around items along the main axis. - align_content: The distribution of space between and around items along the cross axis. - align_items: The alignment of children within their container. - gap: The space to display between both rows and columns of children. - column_gap: The space to display between columns of children. - row_gap: The space to display between rows of children. - """ - return component_element( - "Flex", - *children, - flex=flex, - direction=direction, - wrap=wrap, - justify_content=justify_content, - align_content=align_content, - align_items=align_items, - gap=gap, - column_gap=column_gap, - row_gap=row_gap, - **props, - ) + return locals() From 9261c589da10ce03f6dbcd2c03fb47debcd61ac4 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Mon, 9 Sep 2024 14:38:41 -0400 Subject: [PATCH 07/25] fix: use Dict for 3.8 and 3.9 --- plugins/ui/src/deephaven/ui/components/_maker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/components/_maker.py b/plugins/ui/src/deephaven/ui/components/_maker.py index 1b2e12e95..ec55cab39 100644 --- a/plugins/ui/src/deephaven/ui/components/_maker.py +++ b/plugins/ui/src/deephaven/ui/components/_maker.py @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, Dict from inspect import signature from functools import wraps from ..elements import BaseElement @@ -17,7 +17,7 @@ def make_element( description: str, - override_params: dict[str, str] = {}, + override_params: Dict[str, str] = {}, return_string: str | None = None, ): def decorator(f: Any): From 3d2d7d1ed13f26d27d3e2ba2b935c450c4577762 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Mon, 9 Sep 2024 14:41:43 -0400 Subject: [PATCH 08/25] fix: use Optional for 3.8 and 3.9 --- plugins/ui/src/deephaven/ui/components/_maker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/components/_maker.py b/plugins/ui/src/deephaven/ui/components/_maker.py index ec55cab39..7124c727f 100644 --- a/plugins/ui/src/deephaven/ui/components/_maker.py +++ b/plugins/ui/src/deephaven/ui/components/_maker.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any, Dict, Optional from inspect import signature from functools import wraps from ..elements import BaseElement @@ -18,7 +18,7 @@ def make_element( description: str, override_params: Dict[str, str] = {}, - return_string: str | None = None, + return_string: Optional[str] = None, ): def decorator(f: Any): nonlocal return_string From 666052b112d97e8ef311741c86b313269df08a14 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Tue, 10 Sep 2024 09:43:08 -0400 Subject: [PATCH 09/25] fix: use correct element name --- .../ui/src/deephaven/ui/components/_maker.py | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/components/_maker.py b/plugins/ui/src/deephaven/ui/components/_maker.py index 7124c727f..4d7d5595f 100644 --- a/plugins/ui/src/deephaven/ui/components/_maker.py +++ b/plugins/ui/src/deephaven/ui/components/_maker.py @@ -1,7 +1,9 @@ from typing import Any, Dict, Optional from inspect import signature from functools import wraps + from ..elements import BaseElement +from .._internal.utils import create_props params = { "wrap": "Whether children should wrap when they exceed the panel's width.", @@ -22,24 +24,26 @@ def make_element( ): def decorator(f: Any): nonlocal return_string + # Get name here so it's run once + name = "".join(word.capitalize() for word in f.__name__.split("_")) @wraps(f) def wrapper(*args, **kwargs): - props = f(*args, **kwargs) - return BaseElement(f"deephaven.ui.components.{f.__name__}", **props) + children, props = create_props(f(*args, **kwargs)) + return BaseElement(f"deephaven.ui.components.{name}", *children, **props) sig = signature(f) if return_string is None: - return_string = f"The rendered {f.__name__.replace('_', ' ')} component." + return_string = f"The rendered {name} component." wrapper.__doc__ = f"{description}\n\nArgs:\n" - for name in (param.name for param in sig.parameters.values()): - if name in override_params: - wrapper.__doc__ += f" {name}: {override_params[name]}\n" - elif name in params: - wrapper.__doc__ += f" {name}: {params[name]}\n" + for param_name in (param.name for param in sig.parameters.values()): + if param_name in override_params: + wrapper.__doc__ += f" {param_name}: {override_params[param_name]}\n" + elif param_name in params: + wrapper.__doc__ += f" {param_name}: {params[param_name]}\n" else: - raise ValueError(f'Docs of parameter "{name}" not specified.') + raise ValueError(f'Docs of parameter "{param_name}" not specified.') wrapper.__doc__ += f"\nReturns:\n {return_string}\n" return wrapper From 5e90fedba62427b4f33a91a5dba0d35e14e3b869 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Wed, 11 Sep 2024 10:32:40 -0400 Subject: [PATCH 10/25] feat: add docstring and key prop --- .../ui/src/deephaven/ui/components/_maker.py | 58 +++++++++++++------ .../ui/src/deephaven/ui/components/flex.py | 3 +- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/components/_maker.py b/plugins/ui/src/deephaven/ui/components/_maker.py index 4d7d5595f..b30e2f447 100644 --- a/plugins/ui/src/deephaven/ui/components/_maker.py +++ b/plugins/ui/src/deephaven/ui/components/_maker.py @@ -1,11 +1,11 @@ from typing import Any, Dict, Optional -from inspect import signature +from inspect import signature, Parameter from functools import wraps from ..elements import BaseElement -from .._internal.utils import create_props +from .._internal.utils import create_props, to_camel_case -params = { +param_descs = { "wrap": "Whether children should wrap when they exceed the panel's width.", "justify_content": "The distribution of space around items along the main axis.", "align_content": "The distribution of space between and around items along the cross axis.", @@ -19,32 +19,52 @@ def make_element( description: str, - override_params: Dict[str, str] = {}, - return_string: Optional[str] = None, + override_name: Optional[str] = None, + override_param_descs: Dict[str, str] = {}, + override_return: Optional[str] = None, ): + """ + A decorator creator thats turns a function (that returns `global() of its props`) into a component. + + Args: + description: The description of the component. + override_name: The name of the component that will override the default (the function name in camel case). + override_param_descs: A dictionary of prop descriptions that will override the default ones. + override_return: The string that will override the default ("The rendered {name} component.") return value. + """ + def decorator(f: Any): - nonlocal return_string - # Get name here so it's run once - name = "".join(word.capitalize() for word in f.__name__.split("_")) + nonlocal override_name + nonlocal override_return + + sig = signature(f) + + # Run here so it's run once + if override_name is None: + override_name = to_camel_case(f.__name__) + if override_return is None: + override_return = f"The rendered {override_name} component." @wraps(f) - def wrapper(*args, **kwargs): + def wrapper(*args, key: str | None = None, **kwargs): children, props = create_props(f(*args, **kwargs)) - return BaseElement(f"deephaven.ui.components.{name}", *children, **props) - - sig = signature(f) - if return_string is None: - return_string = f"The rendered {name} component." + props["key"] = key + return BaseElement( + f"deephaven.ui.components.{override_name}", *children, **props + ) wrapper.__doc__ = f"{description}\n\nArgs:\n" for param_name in (param.name for param in sig.parameters.values()): - if param_name in override_params: - wrapper.__doc__ += f" {param_name}: {override_params[param_name]}\n" - elif param_name in params: - wrapper.__doc__ += f" {param_name}: {params[param_name]}\n" + if param_name in override_param_descs: + wrapper.__doc__ += ( + f" {param_name}: {override_param_descs[param_name]}\n" + ) + elif param_name in param_descs: + wrapper.__doc__ += f" {param_name}: {param_descs[param_name]}\n" else: raise ValueError(f'Docs of parameter "{param_name}" not specified.') - wrapper.__doc__ += f"\nReturns:\n {return_string}\n" + wrapper.__doc__ += f" key: A unique key for the element\n" + wrapper.__doc__ += f"\nReturns:\n {override_return}\n" return wrapper diff --git a/plugins/ui/src/deephaven/ui/components/flex.py b/plugins/ui/src/deephaven/ui/components/flex.py index 72c445726..6a46a932c 100644 --- a/plugins/ui/src/deephaven/ui/components/flex.py +++ b/plugins/ui/src/deephaven/ui/components/flex.py @@ -22,11 +22,10 @@ @make_element( "Base Flex component for laying out children in a flexbox.", - override_params=_override_params, + override_param_descs=_override_params, ) def flex( *children: Any, - key: str | None = None, flex: LayoutFlex | None = "auto", direction: Direction | None = None, wrap: Wrap | None = None, From 97a4cb562c81f46f14d178863ecf3b1a0ddc2961 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Wed, 11 Sep 2024 10:36:25 -0400 Subject: [PATCH 11/25] fix: use future annotations --- plugins/ui/src/deephaven/ui/components/_maker.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/components/_maker.py b/plugins/ui/src/deephaven/ui/components/_maker.py index b30e2f447..214a809d1 100644 --- a/plugins/ui/src/deephaven/ui/components/_maker.py +++ b/plugins/ui/src/deephaven/ui/components/_maker.py @@ -1,5 +1,6 @@ -from typing import Any, Dict, Optional -from inspect import signature, Parameter +from __future__ import annotations +from typing import Any +from inspect import signature from functools import wraps from ..elements import BaseElement @@ -19,9 +20,9 @@ def make_element( description: str, - override_name: Optional[str] = None, - override_param_descs: Dict[str, str] = {}, - override_return: Optional[str] = None, + override_name: str | None = None, + override_param_descs: dict[str, str] = {}, + override_return: str | None = None, ): """ A decorator creator thats turns a function (that returns `global() of its props`) into a component. From 979731d6701090b02fb363cd6d5bf6ebae982f7b Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Wed, 11 Sep 2024 10:52:36 -0400 Subject: [PATCH 12/25] feat: add all props --- .../ui/src/deephaven/ui/components/_maker.py | 264 +++++++++++++++++- 1 file changed, 256 insertions(+), 8 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/components/_maker.py b/plugins/ui/src/deephaven/ui/components/_maker.py index 214a809d1..f3a2540fd 100644 --- a/plugins/ui/src/deephaven/ui/components/_maker.py +++ b/plugins/ui/src/deephaven/ui/components/_maker.py @@ -7,14 +7,262 @@ from .._internal.utils import create_props, to_camel_case param_descs = { - "wrap": "Whether children should wrap when they exceed the panel's width.", - "justify_content": "The distribution of space around items along the main axis.", - "align_content": "The distribution of space between and around items along the cross axis.", - "align_items": "The alignment of children within their container.", - "gap": "The space to display between both rows and columns of children.", - "column_gap": "The space to display between columns of children.", - "row_gap": "The space to display between rows of children.", - "key": "A unique key for the element", + "UNSAFE_class_name": "default", + "UNSAFE_style": "default", + "action": "default", + "actions": "default", + "align": "default", + "align_content": "default", + "align_items": "default", + "align_self": "default", + "alignment": "default", + "allows_custom_value": "default", + "allows_non_contiguous_ranges": "default", + "alt": "default", + "areas": "default", + "aria_active_descendant": "default", + "aria_auto_complete": "default", + "aria_controls": "default", + "aria_described_by": "default", + "aria_describedby": "default", + "aria_details": "default", + "aria_errormessage": "default", + "aria_expanded": "default", + "aria_has_popup": "default", + "aria_haspopup": "default", + "aria_label": "default", + "aria_labelled_by": "default", + "aria_labelledby": "default", + "aria_pressed": "default", + "auto_capitalize": "default", + "auto_columns": "default", + "auto_complete": "default", + "auto_flow": "default", + "auto_focus": "default", + "auto_rows": "default", + "back_columns": "default", + "background_color": "default", + "border_bottom_color": "default", + "border_bottom_end_radius": "default", + "border_bottom_start_radius": "default", + "border_bottom_width": "default", + "border_color": "default", + "border_end_color": "default", + "border_end_width": "default", + "border_radius": "default", + "border_start_color": "default", + "border_start_width": "default", + "border_top_color": "default", + "border_top_end_radius": "default", + "border_top_start_radius": "default", + "border_top_width": "default", + "border_width": "default", + "border_x_color": "default", + "border_x_width": "default", + "border_y_color": "default", + "border_y_width": "default", + "bottom": "default", + "button_label_behaviour": "default", + "children": "default", + "close_on_select": "default", + "color": "default", + "column_gap": "default", + "column_groups": "default", + "columns": "default", + "container_padding": "default", + "context_header_menu": "default", + "context_menu": "default", + "contextual_help": "default", + "cross_offset": "default", + "decrement_aria_label": "default", + "default_input_value": "default", + "default_open": "default", + "default_selected": "default", + "default_selected_key": "default", + "default_selected_keys": "default", + "default_value": "default", + "density": "default", + "description": "default", + "description_column": "default", + "direction": "default", + "disabled_keys": "default", + "disallow_empty_selection": "default", + "element": "default", + "element_type": "default", + "enc_type": "default", + "end": "default", + "end_name": "default", + "error_message": "default", + "exclude_from_tab_order": "default", + "fill_offset": "default", + "flex": "default", + "flex_basis": "default", + "flex_grow": "default", + "flex_shrink": "default", + "form_value": "default", + "front_columns": "default", + "frozen_columns": "default", + "gap": "default", + "granularity": "default", + "grid_area": "default", + "grid_column": "default", + "grid_column_end": "default", + "grid_column_start": "default", + "grid_row": "default", + "grid_row_end": "default", + "grid_row_start": "default", + "height": "default", + "hidden_columns": "default", + "hide_stepper": "default", + "hide_time_zone": "default", + "hour_cycle": "default", + "href": "default", + "icon": "default", + "icon_column": "default", + "id": "default", + "increment_aria_label": "default", + "input_mode": "default", + "input_value": "default", + "is_disabled": "default", + "is_emphasized": "default", + "is_filled": "default", + "is_hidden": "default", + "is_indeterminate": "default", + "is_invalid": "default", + "is_justified": "default", + "is_loading": "default", + "is_open": "default", + "is_pending": "default", + "is_quiet": "default", + "is_read_only": "default", + "is_required": "default", + "is_selected": "default", + "is_wheel_disabled": "default", + "justify_content": "default", + "justify_items": "default", + "justify_self": "default", + "key": "default", + "key_column": "default", + "keyboard_activation": "default", + "label": "default", + "label_align": "default", + "label_alignment": "default", + "label_column": "default", + "label_position": "default", + "left": "default", + "level": "default", + "loading_state": "default", + "margin": "default", + "margin_bottom": "default", + "margin_end": "default", + "margin_start": "default", + "margin_top": "default", + "margin_x": "default", + "margin_y": "default", + "max_height": "default", + "max_length": "default", + "max_value": "default", + "max_visible_months": "default", + "max_width": "default", + "menu_trigger": "default", + "menu_width": "default", + "method": "default", + "min_height": "default", + "min_length": "default", + "min_value": "default", + "min_width": "default", + "name": "default", + "necessity_indicator": "default", + "object_fit": "default", + "offset": "default", + "on_action": "default", + "on_blur": "default", + "on_cell_double_press": "default", + "on_cell_press": "default", + "on_change": "default", + "on_change_end": "default", + "on_column_double_press": "default", + "on_column_press": "default", + "on_error": "default", + "on_focus": "default", + "on_focus_change": "default", + "on_input_change": "default", + "on_invalid": "default", + "on_key_down": "default", + "on_key_up": "default", + "on_load": "default", + "on_open_change": "default", + "on_press": "default", + "on_press_change": "default", + "on_press_end": "default", + "on_press_start": "default", + "on_press_up": "default", + "on_reset": "default", + "on_row_double_press": "default", + "on_row_press": "default", + "on_selection_change": "default", + "on_submit": "default", + "order": "default", + "orientation": "default", + "overflow": "default", + "overflow_mode": "default", + "padding": "default", + "padding_bottom": "default", + "padding_end": "default", + "padding_start": "default", + "padding_top": "default", + "padding_x": "default", + "padding_y": "default", + "page_behavior": "default", + "pattern": "default", + "placeholder": "default", + "placeholder_value": "default", + "placement": "default", + "position": "default", + "quick_filters": "default", + "rel": "default", + "render_empty_state": "default", + "reverse": "default", + "right": "default", + "row_gap": "default", + "rows": "default", + "selected_key": "default", + "selected_keys": "default", + "selection_mode": "default", + "should_flip": "default", + "should_focus_wrap": "default", + "should_force_leading_zeros": "default", + "show_error_icon": "default", + "show_format_help_text": "default", + "show_quick_filters": "default", + "show_search": "default", + "show_value_label": "default", + "size": "default", + "slot": "default", + "src": "default", + "start": "default", + "start_name": "default", + "static_color": "default", + "step": "default", + "style": "default", + "summary_icon": "default", + "table": "default", + "target": "default", + "text_value": "default", + "title": "default", + "title_column": "default", + "top": "default", + "track_gradient": "default", + "trigger": "default", + "type": "default", + "validation_behavior": "default", + "validation_errors": "default", + "validation_state": "default", + "value": "default", + "variant": "default", + "width": "default", + "wrap": "default", + "z_index": "default", } From 653c6af9d9a81fed36bfd1dd65b4bb350a487c30 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Wed, 11 Sep 2024 11:13:34 -0400 Subject: [PATCH 13/25] fix: use title case instead of snake --- plugins/ui/src/deephaven/ui/_internal/utils.py | 13 +++++++++++++ plugins/ui/src/deephaven/ui/components/_maker.py | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/_internal/utils.py b/plugins/ui/src/deephaven/ui/_internal/utils.py index 691abe679..2e83954cd 100644 --- a/plugins/ui/src/deephaven/ui/_internal/utils.py +++ b/plugins/ui/src/deephaven/ui/_internal/utils.py @@ -68,6 +68,19 @@ def to_camel_case(snake_case_text: str) -> str: return components[0] + "".join((x[0].upper() + x[1:]) for x in components[1:]) +def to_title_case(snake_case_text: str) -> str: + """ + Convert a snake_case string to TitleCase. + + Args: + snake_case_text: The snake_case string to convert. + + Returns: + The TitleCase string. + """ + return "".join(n.capitalize() for n in snake_case_text.split("_")) + + def to_react_prop_case(snake_case_text: str) -> str: """ Convert a snake_case string to camelCase, with exceptions for special props like `UNSAFE_` or `aria_` props. diff --git a/plugins/ui/src/deephaven/ui/components/_maker.py b/plugins/ui/src/deephaven/ui/components/_maker.py index f3a2540fd..f126e9751 100644 --- a/plugins/ui/src/deephaven/ui/components/_maker.py +++ b/plugins/ui/src/deephaven/ui/components/_maker.py @@ -4,7 +4,7 @@ from functools import wraps from ..elements import BaseElement -from .._internal.utils import create_props, to_camel_case +from .._internal.utils import create_props, to_title_case param_descs = { "UNSAFE_class_name": "default", @@ -290,7 +290,7 @@ def decorator(f: Any): # Run here so it's run once if override_name is None: - override_name = to_camel_case(f.__name__) + override_name = to_title_case(f.__name__) if override_return is None: override_return = f"The rendered {override_name} component." From 1890bfe40404ae747ddbd95c3e926a87a2a8a4c8 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Tue, 17 Sep 2024 16:14:34 -0400 Subject: [PATCH 14/25] update prop description --- .../ui/src/deephaven/ui/components/_maker.py | 510 +++++++++--------- 1 file changed, 254 insertions(+), 256 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/components/_maker.py b/plugins/ui/src/deephaven/ui/components/_maker.py index f126e9751..25db1eb15 100644 --- a/plugins/ui/src/deephaven/ui/components/_maker.py +++ b/plugins/ui/src/deephaven/ui/components/_maker.py @@ -7,262 +7,260 @@ from .._internal.utils import create_props, to_title_case param_descs = { - "UNSAFE_class_name": "default", - "UNSAFE_style": "default", - "action": "default", - "actions": "default", - "align": "default", - "align_content": "default", - "align_items": "default", - "align_self": "default", - "alignment": "default", - "allows_custom_value": "default", - "allows_non_contiguous_ranges": "default", - "alt": "default", - "areas": "default", - "aria_active_descendant": "default", - "aria_auto_complete": "default", - "aria_controls": "default", - "aria_described_by": "default", - "aria_describedby": "default", - "aria_details": "default", - "aria_errormessage": "default", - "aria_expanded": "default", - "aria_has_popup": "default", - "aria_haspopup": "default", - "aria_label": "default", - "aria_labelled_by": "default", - "aria_labelledby": "default", - "aria_pressed": "default", - "auto_capitalize": "default", - "auto_columns": "default", - "auto_complete": "default", - "auto_flow": "default", - "auto_focus": "default", - "auto_rows": "default", - "back_columns": "default", - "background_color": "default", - "border_bottom_color": "default", - "border_bottom_end_radius": "default", - "border_bottom_start_radius": "default", - "border_bottom_width": "default", - "border_color": "default", - "border_end_color": "default", - "border_end_width": "default", - "border_radius": "default", - "border_start_color": "default", - "border_start_width": "default", - "border_top_color": "default", - "border_top_end_radius": "default", - "border_top_start_radius": "default", - "border_top_width": "default", - "border_width": "default", - "border_x_color": "default", - "border_x_width": "default", - "border_y_color": "default", - "border_y_width": "default", - "bottom": "default", - "button_label_behaviour": "default", - "children": "default", - "close_on_select": "default", - "color": "default", - "column_gap": "default", - "column_groups": "default", - "columns": "default", - "container_padding": "default", - "context_header_menu": "default", - "context_menu": "default", - "contextual_help": "default", - "cross_offset": "default", - "decrement_aria_label": "default", - "default_input_value": "default", - "default_open": "default", - "default_selected": "default", - "default_selected_key": "default", - "default_selected_keys": "default", - "default_value": "default", - "density": "default", - "description": "default", - "description_column": "default", - "direction": "default", - "disabled_keys": "default", - "disallow_empty_selection": "default", - "element": "default", - "element_type": "default", - "enc_type": "default", - "end": "default", - "end_name": "default", - "error_message": "default", - "exclude_from_tab_order": "default", - "fill_offset": "default", - "flex": "default", - "flex_basis": "default", - "flex_grow": "default", - "flex_shrink": "default", - "form_value": "default", - "front_columns": "default", - "frozen_columns": "default", - "gap": "default", - "granularity": "default", - "grid_area": "default", - "grid_column": "default", - "grid_column_end": "default", - "grid_column_start": "default", - "grid_row": "default", - "grid_row_end": "default", - "grid_row_start": "default", - "height": "default", - "hidden_columns": "default", - "hide_stepper": "default", - "hide_time_zone": "default", - "hour_cycle": "default", - "href": "default", - "icon": "default", - "icon_column": "default", - "id": "default", - "increment_aria_label": "default", - "input_mode": "default", - "input_value": "default", - "is_disabled": "default", - "is_emphasized": "default", - "is_filled": "default", - "is_hidden": "default", - "is_indeterminate": "default", - "is_invalid": "default", - "is_justified": "default", - "is_loading": "default", - "is_open": "default", - "is_pending": "default", - "is_quiet": "default", - "is_read_only": "default", - "is_required": "default", - "is_selected": "default", - "is_wheel_disabled": "default", - "justify_content": "default", - "justify_items": "default", - "justify_self": "default", - "key": "default", - "key_column": "default", - "keyboard_activation": "default", - "label": "default", - "label_align": "default", - "label_alignment": "default", - "label_column": "default", - "label_position": "default", - "left": "default", - "level": "default", - "loading_state": "default", - "margin": "default", - "margin_bottom": "default", - "margin_end": "default", - "margin_start": "default", - "margin_top": "default", - "margin_x": "default", - "margin_y": "default", - "max_height": "default", - "max_length": "default", - "max_value": "default", - "max_visible_months": "default", - "max_width": "default", - "menu_trigger": "default", - "menu_width": "default", - "method": "default", - "min_height": "default", - "min_length": "default", - "min_value": "default", - "min_width": "default", - "name": "default", - "necessity_indicator": "default", - "object_fit": "default", - "offset": "default", - "on_action": "default", - "on_blur": "default", - "on_cell_double_press": "default", - "on_cell_press": "default", - "on_change": "default", - "on_change_end": "default", - "on_column_double_press": "default", - "on_column_press": "default", - "on_error": "default", - "on_focus": "default", - "on_focus_change": "default", - "on_input_change": "default", - "on_invalid": "default", - "on_key_down": "default", - "on_key_up": "default", - "on_load": "default", - "on_open_change": "default", - "on_press": "default", - "on_press_change": "default", - "on_press_end": "default", - "on_press_start": "default", - "on_press_up": "default", - "on_reset": "default", - "on_row_double_press": "default", - "on_row_press": "default", - "on_selection_change": "default", - "on_submit": "default", - "order": "default", - "orientation": "default", - "overflow": "default", - "overflow_mode": "default", - "padding": "default", - "padding_bottom": "default", - "padding_end": "default", - "padding_start": "default", - "padding_top": "default", - "padding_x": "default", - "padding_y": "default", - "page_behavior": "default", - "pattern": "default", - "placeholder": "default", - "placeholder_value": "default", - "placement": "default", - "position": "default", - "quick_filters": "default", - "rel": "default", - "render_empty_state": "default", - "reverse": "default", - "right": "default", - "row_gap": "default", - "rows": "default", - "selected_key": "default", - "selected_keys": "default", - "selection_mode": "default", - "should_flip": "default", - "should_focus_wrap": "default", - "should_force_leading_zeros": "default", - "show_error_icon": "default", - "show_format_help_text": "default", - "show_quick_filters": "default", - "show_search": "default", - "show_value_label": "default", - "size": "default", - "slot": "default", - "src": "default", - "start": "default", - "start_name": "default", - "static_color": "default", - "step": "default", - "style": "default", - "summary_icon": "default", - "table": "default", - "target": "default", - "text_value": "default", - "title": "default", - "title_column": "default", - "top": "default", - "track_gradient": "default", - "trigger": "default", - "type": "default", - "validation_behavior": "default", - "validation_errors": "default", - "validation_state": "default", - "value": "default", - "variant": "default", - "width": "default", - "wrap": "default", - "z_index": "default", + "UNSAFE_class_name": "Set the CSS className for the element. Only use as a last resort. Use style props instead.", + "UNSAFE_style": "Set the inline style for the element. Only use as a last resort when suitable style props aren't available.", + "action": "The URL to submit the form data to.", + "actions": "The action group or menus to render for all elements within the component, if supported.", + "align": "Alignment of the menu relative to the trigger.", + "align_content": "The distribution of space between and around items along the cross axis.", + "align_items": "The alignment of children within their container.", + "align_self": "Overrides the align_items property of a flex or grid container.", + "alignment": "The alignment of the buttons within the button_group.", + "allows_custom_value": "Whether the combo_box allows a non-item matching input value to be set.", + "allows_non_contiguous_ranges": "When combined with unavailable_values, determines whether non-contiguous ranges, i.e. ranges containing unavailable dates, may be selected.", + "alt": "Text description of the image.", + "areas": "The named grid areas to use for the grid.", + "aria_active_descendant": "Identifies the currently active element when DOM focus is on a composite widget, textbox, group, or application.", + "aria_auto_complete": "Indicates whether inputting text could trigger display of one or more predictions of the user's intended value for an input and specifies how predictions would be presented if they are made.", + "aria_controls": "The id of the element that the element controls.", + "aria_described_by": "The id of the element that describes the current element.", + "aria_describedby": "The id of the element that describes the element.", + "aria_details": "The id of the element that provides additional information about the current element.", + "aria_errormessage": "The id of the element that provides an error message for the current element.", + "aria_expanded": "Whether the element is expanded.", + "aria_has_popup": "Whether the element has a popup.", + "aria_haspopup": "Whether the element has a popup.", + "aria_label": "Defines a string value that labels the current element.", + "aria_labelled_by": "The id of the element that labels the current element.", + "aria_labelledby": "The id of the element that labels the element.", + "aria_pressed": "Whether the element is pressed.", + "auto_capitalize": "Controls whether inputted text is automatically capitalized and, if so, in what manner.", + "auto_columns": "The size of auto-generated columns.", + "auto_complete": "Describes the type of autocomplete functionality the input should provide", + "auto_flow": "The flow direction for auto-generated grid items.", + "auto_focus": "Whether the element should receive focus on render.", + "auto_rows": "The size of auto-generated rows.", + "back_columns": "The columns to pin to the back of the table. These will not be movable by the user.", + "background_color": "The background color of the element.", + "border_bottom_color": "The color of the border for the bottom side of the element.", + "border_bottom_end_radius": "The radius of the border for the bottom end corner of the element.", + "border_bottom_start_radius": "The radius of the border for the bottom start corner of the element.", + "border_bottom_width": "The width of the border for the bottom side of the element.", + "border_color": "The color of the border for all four sides of the element.", + "border_end_color": "The color of the border for the end side of the element, depending on layout direction.", + "border_end_width": "The width of the border for the end side of the element, depending on layout direction.", + "border_radius": "The radius of the border for all four corners of the element.", + "border_start_color": "The color of the border for the start side of the element, depending on layout direction.", + "border_start_width": "The width of the border for the start side of the element, depending on layout direction.", + "border_top_color": "The color of the border for the top side of the element.", + "border_top_end_radius": "The radius of the border for the top end corner of the element.", + "border_top_start_radius": "The radius of the border for the top start corner of the element.", + "border_top_width": "The width of the border for the top side of the element.", + "border_width": "The width of the border for all four sides of the element.", + "border_x_color": "The color of the border for the left and right sides of the element.", + "border_x_width": "The width of the border for the left and right sides of the element.", + "border_y_color": "The color of the border for the top and bottom sides of the element.", + "border_y_width": "The width of the border for the top and bottom sides of the element.", + "bottom": "The distance from the bottom of the containing element.", + "button_label_behaviour": "Defines when the text within the buttons should be hidden and only the icon should be shown.", + "close_on_select": "Whether the Menu closes when a selection is made.", + "color": "The color of the text.", + "column_gap": "The gap between columns.", + "column_groups": "Columns to group together by default. The groups will be shown in the table header. Group names must be unique within the column and group names. Groups may be nested by providing the group name as a child of another group.", + "columns": "The column sizes for the grid.", + "container_padding": "The placement padding that should be applied between the element and its surrounding container.", + "context_header_menu": "The context menu items to show when a column header is right clicked. May contain action items or submenu items. May also be a function that receives the column header data and returns the context menu items or None.", + "context_menu": "The context menu items to show when a cell is right clicked. May contain action items or submenu items. May also be a function that receives the cell data and returns the context menu items or None.", + "contextual_help": "A contextual_help element to place next to the label.", + "cross_offset": "The additional offset applied along the cross axis between the element and its anchor element.", + "decrement_aria_label": 'The aria label for the decrement stepper button. If not provided, the default is "Decrement"', + "default_input_value": "The default value of the search input (uncontrolled).", + "default_open": "Whether the overlay is open by default (uncontrolled).", + "default_selected": "Whether the element should be selected (uncontrolled).", + "default_selected_key": "The initial selected key in the collection (uncontrolled).", + "default_selected_keys": "The initial selected keys in the collection (uncontrolled).", + "default_value": "The default value (uncontrolled).", + "density": "Sets the amount of space between buttons.", + "description": "A description for the field. Provides a hint such as specific requirements for what to choose.", + "description_column": "The column of values to display as descriptions.", + "direction": "Where the Menu opens relative to its trigger.", + "disabled_keys": "The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with.", + "disallow_empty_selection": "Whether the collection allows empty selection.", + "element": "Element to render as the dashboard. The element should render a layout that contains 1 root column or row.", + "element_type": "The type of element to render.", + "enc_type": "The enctype attribute specifies how the form-data should be encoded when submitting it to the server.", + "end": "The distance from the end of the containing element, depending on layout direction.", + "end_name": "The name of the end date input element, used when submitting an HTML form.", + "error_message": "An error message for the field.", + "exclude_from_tab_order": "Whether the element should be excluded from the tab order. If true, the element will not be focusable via the keyboard by tabbing.", + "fill_offset": "The offset of the filled area from the start of the slider.", + "flex": "When used in a flex layout, specifies how the element will grow or shrink to fit the space available.", + "flex_basis": "When used in a flex layout, specifies the initial main size of the element.", + "flex_grow": "When used in a flex layout, specifies how the element will grow to fit the space available.", + "flex_shrink": "When used in a flex layout, specifies how the element will shrink to fit the space available.", + "form_value": "Whether the text or key of the selected item is submitted as part of an HTML form. When allows_custom_value is true, this option does not apply and the text is always submitted.", + "front_columns": "The columns to pin to the front of the table. These will not be movable by the user.", + "frozen_columns": "The columns to freeze by default at the front of the table. These will always be visible regardless of horizontal scrolling. The user may unfreeze columns or freeze additional columns.", + "gap": "The space to display between both rows and columns of children.", + "granularity": 'Determines the smallest unit that is displayed in the date picker. By default, this is `"DAY"` for `LocalDate`, and `"SECOND"` otherwise.', + "grid_area": "When used in a grid layout specifies, specifies the named grid area that the element should be placed in within the grid.", + "grid_column": "When used in a grid layout, specifies the column the element should be placed in within the grid.", + "grid_column_end": "When used in a grid layout, specifies the ending column to span within the grid.", + "grid_column_start": "When used in a grid layout, specifies the starting column to span within the grid.", + "grid_row": "When used in a grid layout, specifies the row the element should be placed in within the grid.", + "grid_row_end": "When used in a grid layout, specifies the ending row to span within the grid.", + "grid_row_start": "When used in a grid layout, specifies the starting row to span within the grid.", + "height": "The height of the element.", + "hidden_columns": "The columns to hide by default. Users may show the columns by expanding them.", + "hide_stepper": "Whether to hide the increment and decrement stepper buttons", + "hide_time_zone": "Whether to hide the time zone abbreviation.", + "hour_cycle": "Whether to display the time in 12 or 24 hour format. By default, this is determined by the user's locale.", + "href": "A URL to link to when the button is pressed.", + "icon": "An icon to display at the start of the input", + "icon_column": "The column of values to map to icons.", + "id": "The unique identifier of the element.", + "increment_aria_label": 'The aria label for the increment stepper button. If not provided, the default is "Increment"', + "input_mode": "Hints at the tpye of data that might be entered by the user while editing the element or its contents", + "input_value": "The value of the search input (controlled).", + "is_disabled": "Whether the button is disabled.", + "is_emphasized": "This prop Sets the emphasized style which provides visual prominence.", + "is_filled": "Whether the slider should be filled between the start of the slider and the current value.", + "is_hidden": "Whether the element is hidden.", + "is_indeterminate": "Indeterminism is presentational only. The indeterminate visual representation remains regardless of user interaction.", + "is_invalid": "Whether the element is invalid.", + "is_justified": "Whether the action buttons should be justified in their container.", + "is_loading": "Whether the picker is in a loading state.", + "is_open": "Whether the overlay is open by default (controlled).", + "is_pending": "Whether to disable events immediately and display a loading spinner after a 1 second delay.", + "is_quiet": "Whether the input should be displayed with a quiet style", + "is_read_only": "Whether the input can be selected but not changed by the user.", + "is_required": "Whether the element is required before form submission.", + "is_selected": "Whether the element should be selected (controlled).", + "is_wheel_disabled": "Whether the input should change with scroll", + "justify_content": "The distribution of space around items along the main axis.", + "justify_items": "The defailt justify_self for all items in the grid.", + "justify_self": "Specifies how the element is justified inside a flex or grid container.", + "key": "The unique key of the tab item. Defaults to title.", + "key_column": "The column of values to use as item keys. Defaults to the first column.", + "keyboard_activation": "Whether tabs are activated automatically on focus or manually.", + "label": "The content to display as the label.", + "label_align": "The label's horizontal alignment relative to the element it is labeling.", + "label_column": "The column of values to display as primary text. Defaults to the key_column value.", + "label_position": "The label's overall position relative to the element it is labeling.", + "left": "The distance from the left of the containing element.", + "level": "Sets heading level, h1 through h6. Defaults to 3.", + "loading_state": "The current loading state of the ComboBox. Determines whether or not the progress circle should be shown.", + "margin": "The margin for all four sides of the element.", + "margin_bottom": "The margin for the bottom side of the element.", + "margin_end": "The margin for the logical end side of the element, depending on layout direction.", + "margin_start": "The margin for the logical start side of the element, depending on layout direction.", + "margin_top": "The margin for the top side of the element.", + "margin_x": "The margin for the left and right sides of the element.", + "margin_y": "The margin for the top and bottom sides of the element.", + "max_height": "The maximum height of the element.", + "max_length": "The maximum number of characters the input can accept", + "max_value": "The maximum allowed date that a user may select.", + "max_visible_months": "The maximum number of months to display at once in the calendar popover, if screen space permits.", + "max_width": "The maximum width of the element.", + "menu_trigger": "The interaction required to display the combo box menu.", + "menu_width": "Width of the menu. By default, matches width of the combobox. Note that the minimum width of the dropdown is always equal to the combobox's width.", + "method": "The HTTP method of the form.", + "min_height": "The minimum height of the element.", + "min_length": "The minimum number of characters the input can accept", + "min_value": "The minimum allowed date that a user may select.", + "min_width": "The minimum width of the element.", + "name": "The name of the input element, used when submitting an HTML form.", + "necessity_indicator": "Whether the required state should be shown as an icon or text.", + "object_fit": "How the image should be resized to fit its container.", + "offset": "The additional offset applied along the main axis between the element and its anchor element.", + "on_action": "Invoked when an action is taken on a child. Especially useful when selectionMode is none. The sole argument key is the key for the item.", + "on_blur": "Handler that is called when the element loses focus.", + "on_cell_double_press": "The callback function to run when a cell is double clicked. The callback is invoked with the cell data.", + "on_cell_press": "The callback function to run when a cell is clicked. The callback is invoked with the cell data.", + "on_change": "Function called when the input value changes", + "on_change_end": "Function called when the slider stops moving", + "on_column_double_press": "The callback function to run when a column is double clicked. The callback is invoked with the column name.", + "on_column_press": "The callback function to run when a column is clicked. The callback is invoked with the column name.", + "on_error": "A callback function to run when the image fails to load.", + "on_focus": "Handler that is called when the element receives focus.", + "on_focus_change": "Function called when the focus state changes.", + "on_input_change": "Handler that is called when the combo box input value changes.", + "on_invalid": "The function to call when the form is invalid.", + "on_key_down": "Function called when a key is pressed.", + "on_key_up": "Function called when a key is released.", + "on_load": "A callback function to run when the image has loaded.", + "on_open_change": "Handler that is called when the overlay's open state changes.", + "on_press": "Function called when the button is pressed.", + "on_press_change": "Function called when the pressed state changes.", + "on_press_end": "Function called when a press interaction ends, either over the target or when the pointer leaves the target.", + "on_press_start": "Function called when the button is pressed.", + "on_press_up": "Function called when the button is released.", + "on_reset": "The function to call when the form is reset.", + "on_row_double_press": "The callback function to run when a row is double clicked. The callback is invoked with the visible row data provided in a dictionary where the column names are the keys.", + "on_row_press": "The callback function to run when a row is clicked. The callback is invoked with the visible row data provided in a dictionary where the column names are the keys.", + "on_selection_change": "Handler that is called when the selection changes.", + "on_submit": "The function to call when the form is submitted.", + "order": "The layout order for the element within a flex or grid container.", + "orientation": "The axis the ActionGroup should align with.", + "overflow": "Specifies what to do when the elment's content is too long to fit its size.", + "overflow_mode": "The behavior of the ActionGroup when the buttons do not fit in the available space.", + "padding": "The padding for all four sides of the element.", + "padding_bottom": "The padding for the bottom side of the element.", + "padding_end": "The padding for the logical end side of the element, depending on layout direction.", + "padding_start": "The padding for the logical start side of the element, depending on layout direction.", + "padding_top": "The padding for the top side of the element.", + "padding_x": "The padding for the left and right sides of the element.", + "padding_y": "The padding for the top and bottom sides of the element.", + "page_behavior": "Controls the behavior of paging. Pagination either works by advancing the visible page by visibleDuration (default) or one unit of visibleDuration.", + "pattern": "A regular expression that the input's value must match", + "placeholder": "Placeholder text for the input.", + "placeholder_value": "A placeholder date that influences the format of the placeholder shown when no value is selected. Defaults to today at midnight in the user's timezone.", + "placement": "The placement of the popover relative to the action button.", + "position": "The position of the element.", + "quick_filters": "The quick filters to apply to the table. Dictionary of column name to filter value.", + "rel": "The relationship between the current document and the linked URL.", + "render_empty_state": "Sets what the `list_view` should render when there is no content to display.", + "reverse": "Whether to reverse the table rows. Applied after any sorts.", + "right": "The distance from the right of the containing element.", + "row_gap": "The space to display between rows of children.", + "rows": "The row sizes for the grid.", + "selected_key": "The currently selected key in the collection (controlled).", + "selected_keys": "The currently selected keys in the collection (controlled).", + "selection_mode": "The type of selection that is allowed in the collection.", + "should_flip": "Whether the element should flip its orientation when there is insufficient room for it to render completely.", + "should_focus_wrap": "Whether keyboard navigation is circular.", + "should_force_leading_zeros": "Whether to always show leading zeros in the month, day, and hour fields. By default, this is determined by the user's locale.", + "show_error_icon": "Whether an error icon is rendered.", + "show_format_help_text": "Whether to show the localized date format as help text below the field.", + "show_quick_filters": "Whether to show the quick filter bar by default.", + "show_search": "Whether to show the search bar by default.", + "show_value_label": "Whether the value label should be displayed. True by default if the label is provided.", + "size": "The size of the icon (changes based on scale).", + "slot": "A slot to place the icon in.", + "src": "The URL of the image.", + "start": "The distance from the start of the containing element.", + "start_name": "The name of the start date input element, used when submitting an HTML form.", + "static_color": "The static color style to apply. Useful when the button appears over a color background.", + "step": "The step value for the slider.", + "style": "The background style of the button.", + "summary_icon": "The icon displayed in the dropdown menu button when a selectable ActionGroup is collapsed.", + "table": "The table to use as the source of items.", + "target": "The target window or tab to open the linked URL in.", + "text_value": "A string representation of the item's contents, used for features like typeahead.", + "title": "Rendered contents of the item if `children` contains child items.", + "title_column": "Only valid if table is of type PartitionedTable. The column of values to display as section names. Should be the same for all values in the constituent Table. If not specified, the section titles will be created from the key_columns of the PartitionedTable.", + "top": "The distance from the top of the containing element.", + "track_gradient": "The background of the track, specified as the stops for a CSS background: linear-gradient(to right/left, ...trackGradient).", + "trigger": "How the menu is triggered.", + "type": 'The type of button to render. (default: "button")', + "validation_behavior": "Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA.", + "validation_errors": "The validation errors for the form.", + "validation_state": 'Whether the input should display its "valid" or "invalid" visual styling.', + "value": "The current value (controlled).", + "variant": "The visual style of the button.", + "width": "The width of the element.", + "wrap": "Whether children should wrap when they exceed the panel's width.", + "z_index": "The stack order of the element.", } From 0a0cf377a3e3df0de05d8d3c2de46c6a7e22ca62 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Tue, 17 Sep 2024 16:15:41 -0400 Subject: [PATCH 15/25] revert flex --- .../ui/src/deephaven/ui/components/flex.py | 45 +++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/components/flex.py b/plugins/ui/src/deephaven/ui/components/flex.py index 6a46a932c..a015bc51d 100644 --- a/plugins/ui/src/deephaven/ui/components/flex.py +++ b/plugins/ui/src/deephaven/ui/components/flex.py @@ -1,7 +1,6 @@ from __future__ import annotations from typing import Any -from inspect import signature -from functools import wraps +from .basic import component_element from .types import ( LayoutFlex, Direction, @@ -11,19 +10,8 @@ AlignItems, DimensionValue, ) -from ._maker import make_element -_override_params = { - "children": "Elements to render in the flexbox.", - "flex": "The flex property of the flexbox.", - "direction": "The direction of the flexbox.", -} - -@make_element( - "Base Flex component for laying out children in a flexbox.", - override_param_descs=_override_params, -) def flex( *children: Any, flex: LayoutFlex | None = "auto", @@ -35,5 +23,34 @@ def flex( gap: DimensionValue | None = "size-100", column_gap: DimensionValue | None = None, row_gap: DimensionValue | None = None, + **props: Any, ): - return locals() + """ + Base Flex component for laying out children in a flexbox. + + Args: + *children: Elements to render in the flexbox. + flex: The flex property of the flexbox. + direction: The direction in which to layout children. + wrap: Whether children should wrap when they exceed the panel's width. + justify_content: The distribution of space around items along the main axis. + align_content: The distribution of space between and around items along the cross axis. + align_items: The alignment of children within their container. + gap: The space to display between both rows and columns of children. + column_gap: The space to display between columns of children. + row_gap: The space to display between rows of children. + """ + return component_element( + "Flex", + *children, + flex=flex, + direction=direction, + wrap=wrap, + justify_content=justify_content, + align_content=align_content, + align_items=align_items, + gap=gap, + column_gap=column_gap, + row_gap=row_gap, + **props, + ) From fcf64d23ce04dd964c6ee45034d493285bcb896c Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Tue, 17 Sep 2024 16:19:04 -0400 Subject: [PATCH 16/25] revert experiments --- .../ui/src/deephaven/ui/_internal/utils.py | 13 - .../ui/src/deephaven/ui/components/_maker.py | 318 ------------------ .../src/deephaven/ui/elements/BaseElement.py | 2 +- 3 files changed, 1 insertion(+), 332 deletions(-) delete mode 100644 plugins/ui/src/deephaven/ui/components/_maker.py diff --git a/plugins/ui/src/deephaven/ui/_internal/utils.py b/plugins/ui/src/deephaven/ui/_internal/utils.py index 2e83954cd..691abe679 100644 --- a/plugins/ui/src/deephaven/ui/_internal/utils.py +++ b/plugins/ui/src/deephaven/ui/_internal/utils.py @@ -68,19 +68,6 @@ def to_camel_case(snake_case_text: str) -> str: return components[0] + "".join((x[0].upper() + x[1:]) for x in components[1:]) -def to_title_case(snake_case_text: str) -> str: - """ - Convert a snake_case string to TitleCase. - - Args: - snake_case_text: The snake_case string to convert. - - Returns: - The TitleCase string. - """ - return "".join(n.capitalize() for n in snake_case_text.split("_")) - - def to_react_prop_case(snake_case_text: str) -> str: """ Convert a snake_case string to camelCase, with exceptions for special props like `UNSAFE_` or `aria_` props. diff --git a/plugins/ui/src/deephaven/ui/components/_maker.py b/plugins/ui/src/deephaven/ui/components/_maker.py deleted file mode 100644 index 25db1eb15..000000000 --- a/plugins/ui/src/deephaven/ui/components/_maker.py +++ /dev/null @@ -1,318 +0,0 @@ -from __future__ import annotations -from typing import Any -from inspect import signature -from functools import wraps - -from ..elements import BaseElement -from .._internal.utils import create_props, to_title_case - -param_descs = { - "UNSAFE_class_name": "Set the CSS className for the element. Only use as a last resort. Use style props instead.", - "UNSAFE_style": "Set the inline style for the element. Only use as a last resort when suitable style props aren't available.", - "action": "The URL to submit the form data to.", - "actions": "The action group or menus to render for all elements within the component, if supported.", - "align": "Alignment of the menu relative to the trigger.", - "align_content": "The distribution of space between and around items along the cross axis.", - "align_items": "The alignment of children within their container.", - "align_self": "Overrides the align_items property of a flex or grid container.", - "alignment": "The alignment of the buttons within the button_group.", - "allows_custom_value": "Whether the combo_box allows a non-item matching input value to be set.", - "allows_non_contiguous_ranges": "When combined with unavailable_values, determines whether non-contiguous ranges, i.e. ranges containing unavailable dates, may be selected.", - "alt": "Text description of the image.", - "areas": "The named grid areas to use for the grid.", - "aria_active_descendant": "Identifies the currently active element when DOM focus is on a composite widget, textbox, group, or application.", - "aria_auto_complete": "Indicates whether inputting text could trigger display of one or more predictions of the user's intended value for an input and specifies how predictions would be presented if they are made.", - "aria_controls": "The id of the element that the element controls.", - "aria_described_by": "The id of the element that describes the current element.", - "aria_describedby": "The id of the element that describes the element.", - "aria_details": "The id of the element that provides additional information about the current element.", - "aria_errormessage": "The id of the element that provides an error message for the current element.", - "aria_expanded": "Whether the element is expanded.", - "aria_has_popup": "Whether the element has a popup.", - "aria_haspopup": "Whether the element has a popup.", - "aria_label": "Defines a string value that labels the current element.", - "aria_labelled_by": "The id of the element that labels the current element.", - "aria_labelledby": "The id of the element that labels the element.", - "aria_pressed": "Whether the element is pressed.", - "auto_capitalize": "Controls whether inputted text is automatically capitalized and, if so, in what manner.", - "auto_columns": "The size of auto-generated columns.", - "auto_complete": "Describes the type of autocomplete functionality the input should provide", - "auto_flow": "The flow direction for auto-generated grid items.", - "auto_focus": "Whether the element should receive focus on render.", - "auto_rows": "The size of auto-generated rows.", - "back_columns": "The columns to pin to the back of the table. These will not be movable by the user.", - "background_color": "The background color of the element.", - "border_bottom_color": "The color of the border for the bottom side of the element.", - "border_bottom_end_radius": "The radius of the border for the bottom end corner of the element.", - "border_bottom_start_radius": "The radius of the border for the bottom start corner of the element.", - "border_bottom_width": "The width of the border for the bottom side of the element.", - "border_color": "The color of the border for all four sides of the element.", - "border_end_color": "The color of the border for the end side of the element, depending on layout direction.", - "border_end_width": "The width of the border for the end side of the element, depending on layout direction.", - "border_radius": "The radius of the border for all four corners of the element.", - "border_start_color": "The color of the border for the start side of the element, depending on layout direction.", - "border_start_width": "The width of the border for the start side of the element, depending on layout direction.", - "border_top_color": "The color of the border for the top side of the element.", - "border_top_end_radius": "The radius of the border for the top end corner of the element.", - "border_top_start_radius": "The radius of the border for the top start corner of the element.", - "border_top_width": "The width of the border for the top side of the element.", - "border_width": "The width of the border for all four sides of the element.", - "border_x_color": "The color of the border for the left and right sides of the element.", - "border_x_width": "The width of the border for the left and right sides of the element.", - "border_y_color": "The color of the border for the top and bottom sides of the element.", - "border_y_width": "The width of the border for the top and bottom sides of the element.", - "bottom": "The distance from the bottom of the containing element.", - "button_label_behaviour": "Defines when the text within the buttons should be hidden and only the icon should be shown.", - "close_on_select": "Whether the Menu closes when a selection is made.", - "color": "The color of the text.", - "column_gap": "The gap between columns.", - "column_groups": "Columns to group together by default. The groups will be shown in the table header. Group names must be unique within the column and group names. Groups may be nested by providing the group name as a child of another group.", - "columns": "The column sizes for the grid.", - "container_padding": "The placement padding that should be applied between the element and its surrounding container.", - "context_header_menu": "The context menu items to show when a column header is right clicked. May contain action items or submenu items. May also be a function that receives the column header data and returns the context menu items or None.", - "context_menu": "The context menu items to show when a cell is right clicked. May contain action items or submenu items. May also be a function that receives the cell data and returns the context menu items or None.", - "contextual_help": "A contextual_help element to place next to the label.", - "cross_offset": "The additional offset applied along the cross axis between the element and its anchor element.", - "decrement_aria_label": 'The aria label for the decrement stepper button. If not provided, the default is "Decrement"', - "default_input_value": "The default value of the search input (uncontrolled).", - "default_open": "Whether the overlay is open by default (uncontrolled).", - "default_selected": "Whether the element should be selected (uncontrolled).", - "default_selected_key": "The initial selected key in the collection (uncontrolled).", - "default_selected_keys": "The initial selected keys in the collection (uncontrolled).", - "default_value": "The default value (uncontrolled).", - "density": "Sets the amount of space between buttons.", - "description": "A description for the field. Provides a hint such as specific requirements for what to choose.", - "description_column": "The column of values to display as descriptions.", - "direction": "Where the Menu opens relative to its trigger.", - "disabled_keys": "The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with.", - "disallow_empty_selection": "Whether the collection allows empty selection.", - "element": "Element to render as the dashboard. The element should render a layout that contains 1 root column or row.", - "element_type": "The type of element to render.", - "enc_type": "The enctype attribute specifies how the form-data should be encoded when submitting it to the server.", - "end": "The distance from the end of the containing element, depending on layout direction.", - "end_name": "The name of the end date input element, used when submitting an HTML form.", - "error_message": "An error message for the field.", - "exclude_from_tab_order": "Whether the element should be excluded from the tab order. If true, the element will not be focusable via the keyboard by tabbing.", - "fill_offset": "The offset of the filled area from the start of the slider.", - "flex": "When used in a flex layout, specifies how the element will grow or shrink to fit the space available.", - "flex_basis": "When used in a flex layout, specifies the initial main size of the element.", - "flex_grow": "When used in a flex layout, specifies how the element will grow to fit the space available.", - "flex_shrink": "When used in a flex layout, specifies how the element will shrink to fit the space available.", - "form_value": "Whether the text or key of the selected item is submitted as part of an HTML form. When allows_custom_value is true, this option does not apply and the text is always submitted.", - "front_columns": "The columns to pin to the front of the table. These will not be movable by the user.", - "frozen_columns": "The columns to freeze by default at the front of the table. These will always be visible regardless of horizontal scrolling. The user may unfreeze columns or freeze additional columns.", - "gap": "The space to display between both rows and columns of children.", - "granularity": 'Determines the smallest unit that is displayed in the date picker. By default, this is `"DAY"` for `LocalDate`, and `"SECOND"` otherwise.', - "grid_area": "When used in a grid layout specifies, specifies the named grid area that the element should be placed in within the grid.", - "grid_column": "When used in a grid layout, specifies the column the element should be placed in within the grid.", - "grid_column_end": "When used in a grid layout, specifies the ending column to span within the grid.", - "grid_column_start": "When used in a grid layout, specifies the starting column to span within the grid.", - "grid_row": "When used in a grid layout, specifies the row the element should be placed in within the grid.", - "grid_row_end": "When used in a grid layout, specifies the ending row to span within the grid.", - "grid_row_start": "When used in a grid layout, specifies the starting row to span within the grid.", - "height": "The height of the element.", - "hidden_columns": "The columns to hide by default. Users may show the columns by expanding them.", - "hide_stepper": "Whether to hide the increment and decrement stepper buttons", - "hide_time_zone": "Whether to hide the time zone abbreviation.", - "hour_cycle": "Whether to display the time in 12 or 24 hour format. By default, this is determined by the user's locale.", - "href": "A URL to link to when the button is pressed.", - "icon": "An icon to display at the start of the input", - "icon_column": "The column of values to map to icons.", - "id": "The unique identifier of the element.", - "increment_aria_label": 'The aria label for the increment stepper button. If not provided, the default is "Increment"', - "input_mode": "Hints at the tpye of data that might be entered by the user while editing the element or its contents", - "input_value": "The value of the search input (controlled).", - "is_disabled": "Whether the button is disabled.", - "is_emphasized": "This prop Sets the emphasized style which provides visual prominence.", - "is_filled": "Whether the slider should be filled between the start of the slider and the current value.", - "is_hidden": "Whether the element is hidden.", - "is_indeterminate": "Indeterminism is presentational only. The indeterminate visual representation remains regardless of user interaction.", - "is_invalid": "Whether the element is invalid.", - "is_justified": "Whether the action buttons should be justified in their container.", - "is_loading": "Whether the picker is in a loading state.", - "is_open": "Whether the overlay is open by default (controlled).", - "is_pending": "Whether to disable events immediately and display a loading spinner after a 1 second delay.", - "is_quiet": "Whether the input should be displayed with a quiet style", - "is_read_only": "Whether the input can be selected but not changed by the user.", - "is_required": "Whether the element is required before form submission.", - "is_selected": "Whether the element should be selected (controlled).", - "is_wheel_disabled": "Whether the input should change with scroll", - "justify_content": "The distribution of space around items along the main axis.", - "justify_items": "The defailt justify_self for all items in the grid.", - "justify_self": "Specifies how the element is justified inside a flex or grid container.", - "key": "The unique key of the tab item. Defaults to title.", - "key_column": "The column of values to use as item keys. Defaults to the first column.", - "keyboard_activation": "Whether tabs are activated automatically on focus or manually.", - "label": "The content to display as the label.", - "label_align": "The label's horizontal alignment relative to the element it is labeling.", - "label_column": "The column of values to display as primary text. Defaults to the key_column value.", - "label_position": "The label's overall position relative to the element it is labeling.", - "left": "The distance from the left of the containing element.", - "level": "Sets heading level, h1 through h6. Defaults to 3.", - "loading_state": "The current loading state of the ComboBox. Determines whether or not the progress circle should be shown.", - "margin": "The margin for all four sides of the element.", - "margin_bottom": "The margin for the bottom side of the element.", - "margin_end": "The margin for the logical end side of the element, depending on layout direction.", - "margin_start": "The margin for the logical start side of the element, depending on layout direction.", - "margin_top": "The margin for the top side of the element.", - "margin_x": "The margin for the left and right sides of the element.", - "margin_y": "The margin for the top and bottom sides of the element.", - "max_height": "The maximum height of the element.", - "max_length": "The maximum number of characters the input can accept", - "max_value": "The maximum allowed date that a user may select.", - "max_visible_months": "The maximum number of months to display at once in the calendar popover, if screen space permits.", - "max_width": "The maximum width of the element.", - "menu_trigger": "The interaction required to display the combo box menu.", - "menu_width": "Width of the menu. By default, matches width of the combobox. Note that the minimum width of the dropdown is always equal to the combobox's width.", - "method": "The HTTP method of the form.", - "min_height": "The minimum height of the element.", - "min_length": "The minimum number of characters the input can accept", - "min_value": "The minimum allowed date that a user may select.", - "min_width": "The minimum width of the element.", - "name": "The name of the input element, used when submitting an HTML form.", - "necessity_indicator": "Whether the required state should be shown as an icon or text.", - "object_fit": "How the image should be resized to fit its container.", - "offset": "The additional offset applied along the main axis between the element and its anchor element.", - "on_action": "Invoked when an action is taken on a child. Especially useful when selectionMode is none. The sole argument key is the key for the item.", - "on_blur": "Handler that is called when the element loses focus.", - "on_cell_double_press": "The callback function to run when a cell is double clicked. The callback is invoked with the cell data.", - "on_cell_press": "The callback function to run when a cell is clicked. The callback is invoked with the cell data.", - "on_change": "Function called when the input value changes", - "on_change_end": "Function called when the slider stops moving", - "on_column_double_press": "The callback function to run when a column is double clicked. The callback is invoked with the column name.", - "on_column_press": "The callback function to run when a column is clicked. The callback is invoked with the column name.", - "on_error": "A callback function to run when the image fails to load.", - "on_focus": "Handler that is called when the element receives focus.", - "on_focus_change": "Function called when the focus state changes.", - "on_input_change": "Handler that is called when the combo box input value changes.", - "on_invalid": "The function to call when the form is invalid.", - "on_key_down": "Function called when a key is pressed.", - "on_key_up": "Function called when a key is released.", - "on_load": "A callback function to run when the image has loaded.", - "on_open_change": "Handler that is called when the overlay's open state changes.", - "on_press": "Function called when the button is pressed.", - "on_press_change": "Function called when the pressed state changes.", - "on_press_end": "Function called when a press interaction ends, either over the target or when the pointer leaves the target.", - "on_press_start": "Function called when the button is pressed.", - "on_press_up": "Function called when the button is released.", - "on_reset": "The function to call when the form is reset.", - "on_row_double_press": "The callback function to run when a row is double clicked. The callback is invoked with the visible row data provided in a dictionary where the column names are the keys.", - "on_row_press": "The callback function to run when a row is clicked. The callback is invoked with the visible row data provided in a dictionary where the column names are the keys.", - "on_selection_change": "Handler that is called when the selection changes.", - "on_submit": "The function to call when the form is submitted.", - "order": "The layout order for the element within a flex or grid container.", - "orientation": "The axis the ActionGroup should align with.", - "overflow": "Specifies what to do when the elment's content is too long to fit its size.", - "overflow_mode": "The behavior of the ActionGroup when the buttons do not fit in the available space.", - "padding": "The padding for all four sides of the element.", - "padding_bottom": "The padding for the bottom side of the element.", - "padding_end": "The padding for the logical end side of the element, depending on layout direction.", - "padding_start": "The padding for the logical start side of the element, depending on layout direction.", - "padding_top": "The padding for the top side of the element.", - "padding_x": "The padding for the left and right sides of the element.", - "padding_y": "The padding for the top and bottom sides of the element.", - "page_behavior": "Controls the behavior of paging. Pagination either works by advancing the visible page by visibleDuration (default) or one unit of visibleDuration.", - "pattern": "A regular expression that the input's value must match", - "placeholder": "Placeholder text for the input.", - "placeholder_value": "A placeholder date that influences the format of the placeholder shown when no value is selected. Defaults to today at midnight in the user's timezone.", - "placement": "The placement of the popover relative to the action button.", - "position": "The position of the element.", - "quick_filters": "The quick filters to apply to the table. Dictionary of column name to filter value.", - "rel": "The relationship between the current document and the linked URL.", - "render_empty_state": "Sets what the `list_view` should render when there is no content to display.", - "reverse": "Whether to reverse the table rows. Applied after any sorts.", - "right": "The distance from the right of the containing element.", - "row_gap": "The space to display between rows of children.", - "rows": "The row sizes for the grid.", - "selected_key": "The currently selected key in the collection (controlled).", - "selected_keys": "The currently selected keys in the collection (controlled).", - "selection_mode": "The type of selection that is allowed in the collection.", - "should_flip": "Whether the element should flip its orientation when there is insufficient room for it to render completely.", - "should_focus_wrap": "Whether keyboard navigation is circular.", - "should_force_leading_zeros": "Whether to always show leading zeros in the month, day, and hour fields. By default, this is determined by the user's locale.", - "show_error_icon": "Whether an error icon is rendered.", - "show_format_help_text": "Whether to show the localized date format as help text below the field.", - "show_quick_filters": "Whether to show the quick filter bar by default.", - "show_search": "Whether to show the search bar by default.", - "show_value_label": "Whether the value label should be displayed. True by default if the label is provided.", - "size": "The size of the icon (changes based on scale).", - "slot": "A slot to place the icon in.", - "src": "The URL of the image.", - "start": "The distance from the start of the containing element.", - "start_name": "The name of the start date input element, used when submitting an HTML form.", - "static_color": "The static color style to apply. Useful when the button appears over a color background.", - "step": "The step value for the slider.", - "style": "The background style of the button.", - "summary_icon": "The icon displayed in the dropdown menu button when a selectable ActionGroup is collapsed.", - "table": "The table to use as the source of items.", - "target": "The target window or tab to open the linked URL in.", - "text_value": "A string representation of the item's contents, used for features like typeahead.", - "title": "Rendered contents of the item if `children` contains child items.", - "title_column": "Only valid if table is of type PartitionedTable. The column of values to display as section names. Should be the same for all values in the constituent Table. If not specified, the section titles will be created from the key_columns of the PartitionedTable.", - "top": "The distance from the top of the containing element.", - "track_gradient": "The background of the track, specified as the stops for a CSS background: linear-gradient(to right/left, ...trackGradient).", - "trigger": "How the menu is triggered.", - "type": 'The type of button to render. (default: "button")', - "validation_behavior": "Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA.", - "validation_errors": "The validation errors for the form.", - "validation_state": 'Whether the input should display its "valid" or "invalid" visual styling.', - "value": "The current value (controlled).", - "variant": "The visual style of the button.", - "width": "The width of the element.", - "wrap": "Whether children should wrap when they exceed the panel's width.", - "z_index": "The stack order of the element.", -} - - -def make_element( - description: str, - override_name: str | None = None, - override_param_descs: dict[str, str] = {}, - override_return: str | None = None, -): - """ - A decorator creator thats turns a function (that returns `global() of its props`) into a component. - - Args: - description: The description of the component. - override_name: The name of the component that will override the default (the function name in camel case). - override_param_descs: A dictionary of prop descriptions that will override the default ones. - override_return: The string that will override the default ("The rendered {name} component.") return value. - """ - - def decorator(f: Any): - nonlocal override_name - nonlocal override_return - - sig = signature(f) - - # Run here so it's run once - if override_name is None: - override_name = to_title_case(f.__name__) - if override_return is None: - override_return = f"The rendered {override_name} component." - - @wraps(f) - def wrapper(*args, key: str | None = None, **kwargs): - children, props = create_props(f(*args, **kwargs)) - props["key"] = key - return BaseElement( - f"deephaven.ui.components.{override_name}", *children, **props - ) - - wrapper.__doc__ = f"{description}\n\nArgs:\n" - for param_name in (param.name for param in sig.parameters.values()): - if param_name in override_param_descs: - wrapper.__doc__ += ( - f" {param_name}: {override_param_descs[param_name]}\n" - ) - elif param_name in param_descs: - wrapper.__doc__ += f" {param_name}: {param_descs[param_name]}\n" - else: - raise ValueError(f'Docs of parameter "{param_name}" not specified.') - wrapper.__doc__ += f" key: A unique key for the element\n" - wrapper.__doc__ += f"\nReturns:\n {override_return}\n" - - return wrapper - - return decorator diff --git a/plugins/ui/src/deephaven/ui/elements/BaseElement.py b/plugins/ui/src/deephaven/ui/elements/BaseElement.py index 97f8ae459..ec42bba65 100644 --- a/plugins/ui/src/deephaven/ui/elements/BaseElement.py +++ b/plugins/ui/src/deephaven/ui/elements/BaseElement.py @@ -19,13 +19,13 @@ def __init__( if len(children) > 0 and props.get("children") is not None: raise ValueError("Cannot provide both children and props.children") + if len(children) > 1: props["children"] = list(children) if len(children) == 1: # If there's only one child, we pass it as a single child, not a list # There are many React elements that expect only a single child, and will fail if they get a list (even if it only has one element) props["children"] = children[0] - self._props = dict_to_camel_case(props) @property From 74ce8e27a59b253564d910add6822cc7d1e852b5 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Tue, 17 Sep 2024 16:30:58 -0400 Subject: [PATCH 17/25] use future annotation instead of optional --- plugins/ui/src/deephaven/ui/components/make_component.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/components/make_component.py b/plugins/ui/src/deephaven/ui/components/make_component.py index 15fc8bee8..4d045b349 100644 --- a/plugins/ui/src/deephaven/ui/components/make_component.py +++ b/plugins/ui/src/deephaven/ui/components/make_component.py @@ -1,6 +1,7 @@ +from __future__ import annotations import functools import logging -from typing import Any, Callable, Optional +from typing import Any, Callable from .._internal import get_component_qualname from ..elements import FunctionElement @@ -17,7 +18,7 @@ def make_component(func: Callable[..., Any]): """ @functools.wraps(func) - def make_component_node(*args: Any, key: Optional[str] = None, **kwargs: Any): + def make_component_node(*args: Any, key: str | None = None, **kwargs: Any): component_type = get_component_qualname(func) return FunctionElement(component_type, lambda: func(*args, **kwargs), key=key) From d840e109d63526435b56d607bc5ee31e185465c0 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Wed, 18 Sep 2024 09:54:27 -0400 Subject: [PATCH 18/25] add key to props --- plugins/ui/src/deephaven/ui/elements/BaseElement.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/ui/src/deephaven/ui/elements/BaseElement.py b/plugins/ui/src/deephaven/ui/elements/BaseElement.py index ec42bba65..68c381c6d 100644 --- a/plugins/ui/src/deephaven/ui/elements/BaseElement.py +++ b/plugins/ui/src/deephaven/ui/elements/BaseElement.py @@ -16,6 +16,7 @@ def __init__( ): self._name = name self._keyProp = key + props["key"] = key if len(children) > 0 and props.get("children") is not None: raise ValueError("Cannot provide both children and props.children") From ff31aaede5fbd3154c514e4dd27909d0549110db Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Wed, 18 Sep 2024 10:36:18 -0400 Subject: [PATCH 19/25] add key prop to components --- plugins/ui/src/deephaven/ui/components/action_button.py | 3 +++ plugins/ui/src/deephaven/ui/components/action_group.py | 2 ++ plugins/ui/src/deephaven/ui/components/action_menu.py | 2 ++ plugins/ui/src/deephaven/ui/components/button.py | 3 +++ plugins/ui/src/deephaven/ui/components/button_group.py | 2 ++ plugins/ui/src/deephaven/ui/components/checkbox.py | 3 +++ plugins/ui/src/deephaven/ui/components/column.py | 7 +++++-- plugins/ui/src/deephaven/ui/components/combo_box.py | 2 ++ plugins/ui/src/deephaven/ui/components/content.py | 3 +++ plugins/ui/src/deephaven/ui/components/contextual_help.py | 2 ++ plugins/ui/src/deephaven/ui/components/date_field.py | 2 ++ plugins/ui/src/deephaven/ui/components/date_picker.py | 2 ++ .../ui/src/deephaven/ui/components/date_range_picker.py | 2 ++ plugins/ui/src/deephaven/ui/components/flex.py | 5 +++-- plugins/ui/src/deephaven/ui/components/form.py | 3 +++ plugins/ui/src/deephaven/ui/components/fragment.py | 4 ++-- plugins/ui/src/deephaven/ui/components/grid.py | 3 +++ plugins/ui/src/deephaven/ui/components/heading.py | 3 +++ plugins/ui/src/deephaven/ui/components/icon.py | 1 + .../ui/src/deephaven/ui/components/illustrated_message.py | 2 ++ plugins/ui/src/deephaven/ui/components/image.py | 3 +++ plugins/ui/src/deephaven/ui/components/item.py | 2 ++ .../ui/src/deephaven/ui/components/item_table_source.py | 3 +++ .../ui/src/deephaven/ui/components/list_action_group.py | 2 ++ plugins/ui/src/deephaven/ui/components/list_action_menu.py | 2 ++ plugins/ui/src/deephaven/ui/components/list_view.py | 3 +++ plugins/ui/src/deephaven/ui/components/number_field.py | 3 +++ plugins/ui/src/deephaven/ui/components/panel.py | 2 ++ plugins/ui/src/deephaven/ui/components/picker.py | 2 ++ plugins/ui/src/deephaven/ui/components/radio.py | 1 + plugins/ui/src/deephaven/ui/components/radio_group.py | 1 + plugins/ui/src/deephaven/ui/components/range_slider.py | 3 +++ plugins/ui/src/deephaven/ui/components/row.py | 7 +++++-- plugins/ui/src/deephaven/ui/components/section.py | 5 ++++- plugins/ui/src/deephaven/ui/components/slider.py | 3 +++ plugins/ui/src/deephaven/ui/components/stack.py | 3 +++ plugins/ui/src/deephaven/ui/components/switch.py | 3 +++ plugins/ui/src/deephaven/ui/components/tab.py | 2 +- plugins/ui/src/deephaven/ui/components/tab_list.py | 2 ++ plugins/ui/src/deephaven/ui/components/tab_panels.py | 2 ++ plugins/ui/src/deephaven/ui/components/table.py | 2 ++ plugins/ui/src/deephaven/ui/components/tabs.py | 2 ++ plugins/ui/src/deephaven/ui/components/text.py | 3 +++ plugins/ui/src/deephaven/ui/components/text_area.py | 3 +++ plugins/ui/src/deephaven/ui/components/text_field.py | 3 +++ plugins/ui/src/deephaven/ui/components/toggle_button.py | 3 +++ plugins/ui/src/deephaven/ui/components/view.py | 3 +++ 47 files changed, 119 insertions(+), 10 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/components/action_button.py b/plugins/ui/src/deephaven/ui/components/action_button.py index dd61e5dfc..e87669ebe 100644 --- a/plugins/ui/src/deephaven/ui/components/action_button.py +++ b/plugins/ui/src/deephaven/ui/components/action_button.py @@ -91,6 +91,7 @@ def action_button( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> ActionButtonElement: """ ActionButtons allow users to perform an action. They're used for similar, task-based options within a workflow, and are ideal for interfaces where buttons aren't meant to draw a lot of attention. @@ -161,6 +162,7 @@ def action_button( aria_details: The details for the element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. Returns: The rendered ActionButton element. @@ -231,4 +233,5 @@ def action_button( aria_details=aria_details, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/action_group.py b/plugins/ui/src/deephaven/ui/components/action_group.py index 6e4bc2024..018fac334 100644 --- a/plugins/ui/src/deephaven/ui/components/action_group.py +++ b/plugins/ui/src/deephaven/ui/components/action_group.py @@ -84,6 +84,7 @@ def action_group( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ An action grouping of action items that are related to each other. @@ -217,4 +218,5 @@ def action_group( aria_details=aria_details, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/action_menu.py b/plugins/ui/src/deephaven/ui/components/action_menu.py index 4529b4e4f..484da225c 100644 --- a/plugins/ui/src/deephaven/ui/components/action_menu.py +++ b/plugins/ui/src/deephaven/ui/components/action_menu.py @@ -85,6 +85,7 @@ def action_menu( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ ActionMenu combines an ActionButton with a Menu for simple "more actions" use cases. @@ -207,4 +208,5 @@ def action_menu( aria_details=aria_details, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/button.py b/plugins/ui/src/deephaven/ui/components/button.py index 7d7ed8f73..003ebb659 100644 --- a/plugins/ui/src/deephaven/ui/components/button.py +++ b/plugins/ui/src/deephaven/ui/components/button.py @@ -95,6 +95,7 @@ def button( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ Buttons allow users to perform an action or to navigate to another page. They have multiple styles for various needs, and are ideal for calling attention to where a user needs to do something in order to move forward in a flow. @@ -170,6 +171,7 @@ def button( aria_details: The details of the current element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. Returns: The rendered button component. @@ -248,4 +250,5 @@ def button( aria_details=aria_details, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/button_group.py b/plugins/ui/src/deephaven/ui/components/button_group.py index d88d1a09a..009a9b253 100644 --- a/plugins/ui/src/deephaven/ui/components/button_group.py +++ b/plugins/ui/src/deephaven/ui/components/button_group.py @@ -59,6 +59,7 @@ def button_group( id: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ A button group is a grouping of button whose actions are related to each other. @@ -147,4 +148,5 @@ def button_group( id=id, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/checkbox.py b/plugins/ui/src/deephaven/ui/components/checkbox.py index be74ab9e4..9450ffb87 100644 --- a/plugins/ui/src/deephaven/ui/components/checkbox.py +++ b/plugins/ui/src/deephaven/ui/components/checkbox.py @@ -84,6 +84,7 @@ def checkbox( aria_errormessage: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ Checkboxes allow users to select multiple items from a list of individual items, or to mark one individual item as selected. @@ -153,6 +154,7 @@ def checkbox( aria_errormessage: The id of the element that provides error information for the current element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. Returns: The rendered checkbox. @@ -226,4 +228,5 @@ def checkbox( aria_errormessage=aria_errormessage, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/column.py b/plugins/ui/src/deephaven/ui/components/column.py index a869753ee..c023d23da 100644 --- a/plugins/ui/src/deephaven/ui/components/column.py +++ b/plugins/ui/src/deephaven/ui/components/column.py @@ -4,7 +4,9 @@ from .basic import component_element -def column(*children: Any, width: float | None = None, **kwargs: Any): +def column( + *children: Any, width: float | None = None, key: str | None = None, **kwargs: Any +): """ A column is a container that can be used to group elements. Each element will be placed below its prior sibling. @@ -12,5 +14,6 @@ def column(*children: Any, width: float | None = None, **kwargs: Any): Args: children: Elements to render in the column. width: The percent width of the column relative to other children of its parent. If not provided, the column will be sized automatically. + key: A unique identifier used by React to render elements in a list. """ - return component_element("Column", *children, width=width, **kwargs) + return component_element("Column", *children, width=width, key=key, **kwargs) diff --git a/plugins/ui/src/deephaven/ui/components/combo_box.py b/plugins/ui/src/deephaven/ui/components/combo_box.py index a5327583b..7fe4ef89e 100644 --- a/plugins/ui/src/deephaven/ui/components/combo_box.py +++ b/plugins/ui/src/deephaven/ui/components/combo_box.py @@ -126,6 +126,7 @@ def combo_box( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> ComboBoxElement: """ A combo box that can be used to search or select from a list. Children should be one of five types: @@ -231,6 +232,7 @@ def combo_box( aria_details: The details for the element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. Returns: The rendered ComboBox. diff --git a/plugins/ui/src/deephaven/ui/components/content.py b/plugins/ui/src/deephaven/ui/components/content.py index e496b2997..b3d4bc0c5 100644 --- a/plugins/ui/src/deephaven/ui/components/content.py +++ b/plugins/ui/src/deephaven/ui/components/content.py @@ -54,6 +54,7 @@ def content( id: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ Content represents the primary content within a Spectrum container. @@ -99,6 +100,7 @@ def content( id: The unique identifier of the element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. """ return component_element( "Content", @@ -142,4 +144,5 @@ def content( id=id, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/contextual_help.py b/plugins/ui/src/deephaven/ui/components/contextual_help.py index c509b23a8..4b734d54c 100644 --- a/plugins/ui/src/deephaven/ui/components/contextual_help.py +++ b/plugins/ui/src/deephaven/ui/components/contextual_help.py @@ -70,6 +70,7 @@ def contextual_help( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ A contextual help is a quiet action button that triggers an informational popover. @@ -183,4 +184,5 @@ def contextual_help( aria_details=aria_details, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/date_field.py b/plugins/ui/src/deephaven/ui/components/date_field.py index 7426b8f5d..b24451fdb 100644 --- a/plugins/ui/src/deephaven/ui/components/date_field.py +++ b/plugins/ui/src/deephaven/ui/components/date_field.py @@ -155,6 +155,7 @@ def date_field( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> DateFieldElement: """ A date field allows the user to select a date. @@ -251,6 +252,7 @@ def date_field( aria_details: The details for the element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. Returns: The date field element. diff --git a/plugins/ui/src/deephaven/ui/components/date_picker.py b/plugins/ui/src/deephaven/ui/components/date_picker.py index f2182b9f3..1763f8c8b 100644 --- a/plugins/ui/src/deephaven/ui/components/date_picker.py +++ b/plugins/ui/src/deephaven/ui/components/date_picker.py @@ -162,6 +162,7 @@ def date_picker( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> DatePickerElement: """ A date picker allows the user to select a date. @@ -265,6 +266,7 @@ def date_picker( aria_details: The details for the element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. Returns: The date picker element. diff --git a/plugins/ui/src/deephaven/ui/components/date_range_picker.py b/plugins/ui/src/deephaven/ui/components/date_range_picker.py index a852bed2d..86768abbc 100644 --- a/plugins/ui/src/deephaven/ui/components/date_range_picker.py +++ b/plugins/ui/src/deephaven/ui/components/date_range_picker.py @@ -162,6 +162,7 @@ def date_range_picker( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> DatePickerElement: """ A date range picker allows the user to select a range of dates. @@ -268,6 +269,7 @@ def date_range_picker( aria_details: The details for the element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. Returns: The date range picker element. diff --git a/plugins/ui/src/deephaven/ui/components/flex.py b/plugins/ui/src/deephaven/ui/components/flex.py index a015bc51d..91832047e 100644 --- a/plugins/ui/src/deephaven/ui/components/flex.py +++ b/plugins/ui/src/deephaven/ui/components/flex.py @@ -23,7 +23,7 @@ def flex( gap: DimensionValue | None = "size-100", column_gap: DimensionValue | None = None, row_gap: DimensionValue | None = None, - **props: Any, + key: str | None = None, ): """ Base Flex component for laying out children in a flexbox. @@ -39,6 +39,7 @@ def flex( gap: The space to display between both rows and columns of children. column_gap: The space to display between columns of children. row_gap: The space to display between rows of children. + key: A unique identifier used by React to render elements in a list """ return component_element( "Flex", @@ -52,5 +53,5 @@ def flex( gap=gap, column_gap=column_gap, row_gap=row_gap, - **props, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/form.py b/plugins/ui/src/deephaven/ui/components/form.py index 3ea71cabf..ad107ef5e 100644 --- a/plugins/ui/src/deephaven/ui/components/form.py +++ b/plugins/ui/src/deephaven/ui/components/form.py @@ -88,6 +88,7 @@ def form( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ Forms allow users to enter data that can be submitted while providing alignment and styling for form fields @@ -158,6 +159,7 @@ def form( aria_details: The details for the element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. """ return component_element( "Form", @@ -226,4 +228,5 @@ def form( aria_details=aria_details, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/fragment.py b/plugins/ui/src/deephaven/ui/components/fragment.py index dd963d2fc..a67fb1492 100644 --- a/plugins/ui/src/deephaven/ui/components/fragment.py +++ b/plugins/ui/src/deephaven/ui/components/fragment.py @@ -4,7 +4,7 @@ from .basic import component_element -def fragment(*children: Any): +def fragment(*children: Any, key: str | None = None): """ A React.Fragment: https://react.dev/reference/react/Fragment. Used to group elements together without a wrapper node. @@ -12,4 +12,4 @@ def fragment(*children: Any): Args: *children: The children in the fragment. """ - return component_element("Fragment", children=children) + return component_element("Fragment", children=children, key=key) diff --git a/plugins/ui/src/deephaven/ui/components/grid.py b/plugins/ui/src/deephaven/ui/components/grid.py index 86b4480d1..c82cef999 100644 --- a/plugins/ui/src/deephaven/ui/components/grid.py +++ b/plugins/ui/src/deephaven/ui/components/grid.py @@ -72,6 +72,7 @@ def grid( id: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ A layout container using CSS grid. Supports Spectrum dimensions as values to ensure consistent and adaptive sizing and spacing. @@ -130,6 +131,7 @@ def grid( id: The unique identifier of the element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. """ return component_element( "Grid", @@ -186,4 +188,5 @@ def grid( id=id, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/heading.py b/plugins/ui/src/deephaven/ui/components/heading.py index 1e1e9f3e0..3f770694d 100644 --- a/plugins/ui/src/deephaven/ui/components/heading.py +++ b/plugins/ui/src/deephaven/ui/components/heading.py @@ -58,6 +58,7 @@ def heading( id: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ A layout container using CSS grid. Supports Spectrum dimensions as values to ensure consistent and adaptive sizing and spacing. @@ -105,6 +106,7 @@ def heading( id: The unique identifier of the element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. """ return component_element( "Heading", @@ -150,4 +152,5 @@ def heading( id=id, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/icon.py b/plugins/ui/src/deephaven/ui/components/icon.py index caa469a35..f2d8d5f01 100644 --- a/plugins/ui/src/deephaven/ui/components/icon.py +++ b/plugins/ui/src/deephaven/ui/components/icon.py @@ -65,6 +65,7 @@ def icon( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ Get a Deephaven icon by name. diff --git a/plugins/ui/src/deephaven/ui/components/illustrated_message.py b/plugins/ui/src/deephaven/ui/components/illustrated_message.py index 9d0317ff8..27c86f36d 100644 --- a/plugins/ui/src/deephaven/ui/components/illustrated_message.py +++ b/plugins/ui/src/deephaven/ui/components/illustrated_message.py @@ -53,6 +53,7 @@ def illustrated_message( id: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ An IllustratedMessage displays an illustration and a message, usually for an empty state or an error page. @@ -145,4 +146,5 @@ def illustrated_message( id=id, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/image.py b/plugins/ui/src/deephaven/ui/components/image.py index 0e885d0c0..99a75d81e 100644 --- a/plugins/ui/src/deephaven/ui/components/image.py +++ b/plugins/ui/src/deephaven/ui/components/image.py @@ -59,6 +59,7 @@ def image( id: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ Image is used to insert and display an image within a component. @@ -108,6 +109,7 @@ def image( id: The unique identifier of the element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. Returns: The rendered Image element. @@ -160,4 +162,5 @@ def image( id=id, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/item.py b/plugins/ui/src/deephaven/ui/components/item.py index 282f4c18d..b12aa953a 100644 --- a/plugins/ui/src/deephaven/ui/components/item.py +++ b/plugins/ui/src/deephaven/ui/components/item.py @@ -16,6 +16,7 @@ def item( title: str | None = None, text_value: str | None = None, aria_label: str | None = None, + key: str | None = None, **props: Any, ) -> ItemElement: """ @@ -26,6 +27,7 @@ def item( title: Rendered contents of the item if `children` contains child items. text_value: A string representation of the item's contents, used for features like typeahead. aria_label: An accessibility label for this item. + key: A unique identifier used by React to render elements in a list. **props: Any other Item prop. """ children, props = create_props(locals()) diff --git a/plugins/ui/src/deephaven/ui/components/item_table_source.py b/plugins/ui/src/deephaven/ui/components/item_table_source.py index f9d580396..c26947ed6 100644 --- a/plugins/ui/src/deephaven/ui/components/item_table_source.py +++ b/plugins/ui/src/deephaven/ui/components/item_table_source.py @@ -32,6 +32,7 @@ def item_table_source( icon_column: ColumnName | None = None, title_column: ColumnName | None = None, actions: ListActionGroupElement | ListActionMenuElement | None = None, + key: str | None = None, ) -> ItemTableSource: """ An item table source wraps a Table or PartitionedTable to provide additional information for @@ -58,6 +59,8 @@ def item_table_source( If not specified, the section titles will be created from the key_columns of the PartitionedTable. actions: The action group or menus to render for all elements within the component, if supported. + key: + A unique identifier used by React to render elements in a list. Returns: The item table source to pass as a child to a component that supports it. diff --git a/plugins/ui/src/deephaven/ui/components/list_action_group.py b/plugins/ui/src/deephaven/ui/components/list_action_group.py index 45831594c..270ece181 100644 --- a/plugins/ui/src/deephaven/ui/components/list_action_group.py +++ b/plugins/ui/src/deephaven/ui/components/list_action_group.py @@ -93,6 +93,7 @@ def list_action_group( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> ListActionGroupElement: """ A group of action buttons that can be used to create a list of actions. @@ -230,4 +231,5 @@ def list_action_group( aria_details=aria_details, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/list_action_menu.py b/plugins/ui/src/deephaven/ui/components/list_action_menu.py index d61d18bf1..ae4ab3c38 100644 --- a/plugins/ui/src/deephaven/ui/components/list_action_menu.py +++ b/plugins/ui/src/deephaven/ui/components/list_action_menu.py @@ -81,6 +81,7 @@ def list_action_menu( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> ListActionMenuElement: """ A menu of action buttons that can be used to create a list of actions. @@ -209,4 +210,5 @@ def list_action_menu( aria_details=aria_details, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/list_view.py b/plugins/ui/src/deephaven/ui/components/list_view.py index 173cedb3a..365a40385 100644 --- a/plugins/ui/src/deephaven/ui/components/list_view.py +++ b/plugins/ui/src/deephaven/ui/components/list_view.py @@ -31,6 +31,7 @@ def list_view( render_empty_state: Element | None = None, on_selection_change: Callable[[Selection], None] | None = None, on_change: Callable[[Selection], None] | None = None, + key: str | None = None, **props: Any, ) -> ListViewElement: """ @@ -61,6 +62,8 @@ def list_view( Handler that is called when the selection changes. on_change: Alias of `on_selection_change`. Handler that is called when the selection changes. + key: + A unique identifier used by React to render elements in a list. **props: Any other ListView prop, except items, dragAndDropHooks, and onLoadMore. diff --git a/plugins/ui/src/deephaven/ui/components/number_field.py b/plugins/ui/src/deephaven/ui/components/number_field.py index 8fff0e919..b57b29d40 100644 --- a/plugins/ui/src/deephaven/ui/components/number_field.py +++ b/plugins/ui/src/deephaven/ui/components/number_field.py @@ -97,6 +97,7 @@ def number_field( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, # missing properties that are clipboard or composition events ) -> Element: """ @@ -175,6 +176,7 @@ def number_field( aria_details: The id of the element that provides additional information about the current element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. """ return component_element( @@ -251,4 +253,5 @@ def number_field( aria_details=aria_details, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/panel.py b/plugins/ui/src/deephaven/ui/components/panel.py index db538aa1b..9faead210 100644 --- a/plugins/ui/src/deephaven/ui/components/panel.py +++ b/plugins/ui/src/deephaven/ui/components/panel.py @@ -31,6 +31,7 @@ def panel( padding_end: DimensionValue | None = None, padding_x: DimensionValue | None = None, padding_y: DimensionValue | None = None, + key: str | None = None, **props: Any, ): """ @@ -54,6 +55,7 @@ def panel( padding_end: The padding to apply after the element. padding_x: The padding to apply to the left and right of the element. padding_y: The padding to apply to the top and bottom of the element. + key: A unique identifier used by React to render elements in a list. """ diff --git a/plugins/ui/src/deephaven/ui/components/picker.py b/plugins/ui/src/deephaven/ui/components/picker.py index b752b7e18..214ef5e13 100644 --- a/plugins/ui/src/deephaven/ui/components/picker.py +++ b/plugins/ui/src/deephaven/ui/components/picker.py @@ -115,6 +115,7 @@ def picker( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> PickerElement: """ A picker that can be used to select from a list. Children should be one of five types: @@ -215,6 +216,7 @@ def picker( aria_details: The details for the element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. Returns: diff --git a/plugins/ui/src/deephaven/ui/components/radio.py b/plugins/ui/src/deephaven/ui/components/radio.py index baf245175..c3a08b749 100644 --- a/plugins/ui/src/deephaven/ui/components/radio.py +++ b/plugins/ui/src/deephaven/ui/components/radio.py @@ -68,6 +68,7 @@ def radio( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ Radio buttons allow users to select a single option from a list of mutually diff --git a/plugins/ui/src/deephaven/ui/components/radio_group.py b/plugins/ui/src/deephaven/ui/components/radio_group.py index d699a00f9..9b696f035 100644 --- a/plugins/ui/src/deephaven/ui/components/radio_group.py +++ b/plugins/ui/src/deephaven/ui/components/radio_group.py @@ -91,6 +91,7 @@ def radio_group( aria_errormessage: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ Radio buttons allow users to select a single option from a list of mutually diff --git a/plugins/ui/src/deephaven/ui/components/range_slider.py b/plugins/ui/src/deephaven/ui/components/range_slider.py index b6aab50ae..6e69e071f 100644 --- a/plugins/ui/src/deephaven/ui/components/range_slider.py +++ b/plugins/ui/src/deephaven/ui/components/range_slider.py @@ -80,6 +80,7 @@ def range_slider( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ Sliders allow users to quickly select a value within a range. They should be used when the upper and lower bounds to the range are invariable. @@ -144,6 +145,7 @@ def range_slider( aria_details: The id of the element that provides additional information about the current element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. Returns: The rendered range slider component. @@ -210,4 +212,5 @@ def range_slider( aria_details=aria_details, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/row.py b/plugins/ui/src/deephaven/ui/components/row.py index 33f7bfd1b..da8621c92 100644 --- a/plugins/ui/src/deephaven/ui/components/row.py +++ b/plugins/ui/src/deephaven/ui/components/row.py @@ -4,7 +4,9 @@ from .basic import component_element -def row(*children: Any, height: float | None = None, **kwargs: Any): +def row( + *children: Any, height: float | None = None, key: str | None = None, **kwargs: Any +): """ A row is a container that can be used to group elements. Each element will be placed to the right of its prior sibling. @@ -12,5 +14,6 @@ def row(*children: Any, height: float | None = None, **kwargs: Any): Args: *children: Elements to render in the row. height: The percent height of the row relative to other children of its parent. If not provided, the row will be sized automatically. + key: A unique identifier used by React to render elements in a list. """ - return component_element("Row", *children, height=height, **kwargs) + return component_element("Row", *children, height=height, key=key, **kwargs) diff --git a/plugins/ui/src/deephaven/ui/components/section.py b/plugins/ui/src/deephaven/ui/components/section.py index 3aa17674a..bd80b5ccd 100644 --- a/plugins/ui/src/deephaven/ui/components/section.py +++ b/plugins/ui/src/deephaven/ui/components/section.py @@ -10,13 +10,16 @@ SectionElement = Element -def section(*children: Item, title: str | None = None, **props: Any) -> SectionElement: +def section( + *children: Item, title: str | None = None, key: str | None = None, **props: Any +) -> SectionElement: """ A section that can be added to a menu, such as a picker. Children are the dropdown options. Args: *children: The options to render within the section. title: The title of the section. + key: A unique identifier used by React to render elements in a list. **props: Any other Section prop. Returns: diff --git a/plugins/ui/src/deephaven/ui/components/slider.py b/plugins/ui/src/deephaven/ui/components/slider.py index 36ea10800..71a679286 100644 --- a/plugins/ui/src/deephaven/ui/components/slider.py +++ b/plugins/ui/src/deephaven/ui/components/slider.py @@ -79,6 +79,7 @@ def slider( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ Sliders allow users to quickly select a value within a range. They should be used when the upper and lower bounds to the range are invariable. @@ -144,6 +145,7 @@ def slider( aria_details: The id of the element that provides additional information about the current element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. Returns: The rendered slider component. @@ -211,4 +213,5 @@ def slider( aria_details=aria_details, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/stack.py b/plugins/ui/src/deephaven/ui/components/stack.py index b3e2f3779..b5cd93e40 100644 --- a/plugins/ui/src/deephaven/ui/components/stack.py +++ b/plugins/ui/src/deephaven/ui/components/stack.py @@ -9,6 +9,7 @@ def stack( height: float | None = None, width: float | None = None, activeItemIndex: int | None = None, + key: str | None = None, **kwargs: Any, ): """ @@ -19,6 +20,7 @@ def stack( *children: Elements to render in the row. height: The percent height of the stack relative to other children of its parent. If not provided, the stack will be sized automatically. width: The percent width of the stack relative to other children of its parent. If not provided, the stack will be sized automatically. + key: A unique identifier used by React to render elements in a list. """ return component_element( "Stack", @@ -26,5 +28,6 @@ def stack( height=height, width=width, activeItemIndex=activeItemIndex, + key=key, **kwargs, ) diff --git a/plugins/ui/src/deephaven/ui/components/switch.py b/plugins/ui/src/deephaven/ui/components/switch.py index 2e8c78f56..ff5642a50 100644 --- a/plugins/ui/src/deephaven/ui/components/switch.py +++ b/plugins/ui/src/deephaven/ui/components/switch.py @@ -76,6 +76,7 @@ def switch( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ Switches allow users to turn an individual option on or off. They are usually used to activate or deactivate a specific setting. @@ -142,6 +143,7 @@ def switch( aria_details: The details for the element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. Returns: The rendered switch element. @@ -210,4 +212,5 @@ def switch( aria_details=aria_details, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/tab.py b/plugins/ui/src/deephaven/ui/components/tab.py index abaf467b1..3c3cb1413 100644 --- a/plugins/ui/src/deephaven/ui/components/tab.py +++ b/plugins/ui/src/deephaven/ui/components/tab.py @@ -21,7 +21,7 @@ def tab( Args: *children: Content of the tab item. title: The title of the tab item. - key: The unique key of the tab item. Defaults to title. + key: The unique key of the tab item. Defaults to title. Also a unique identifier used by React to render elements in a list (but will not default to title). icon: The icon of the tab item. text_value: The text value of the tab item. """ diff --git a/plugins/ui/src/deephaven/ui/components/tab_list.py b/plugins/ui/src/deephaven/ui/components/tab_list.py index 15e75c8fb..ecef0956a 100644 --- a/plugins/ui/src/deephaven/ui/components/tab_list.py +++ b/plugins/ui/src/deephaven/ui/components/tab_list.py @@ -54,6 +54,7 @@ def tab_list( id: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ): """ Python implementation for the Adobe React Spectrum TabList component. @@ -140,4 +141,5 @@ def tab_list( id=id, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/tab_panels.py b/plugins/ui/src/deephaven/ui/components/tab_panels.py index 604a5117b..5401ca228 100644 --- a/plugins/ui/src/deephaven/ui/components/tab_panels.py +++ b/plugins/ui/src/deephaven/ui/components/tab_panels.py @@ -54,6 +54,7 @@ def tab_panels( id: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ): """ Python implementation for the Adobe React Spectrum TabPanels component. @@ -140,4 +141,5 @@ def tab_panels( id=id, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/table.py b/plugins/ui/src/deephaven/ui/components/table.py index 2e8781fe1..96f274528 100644 --- a/plugins/ui/src/deephaven/ui/components/table.py +++ b/plugins/ui/src/deephaven/ui/components/table.py @@ -42,6 +42,7 @@ def table( ResolvableContextMenuItem | list[ResolvableContextMenuItem] | None ) = None, databars: list[DatabarConfig] | None = None, + key: str | None = None, ) -> UITable: """ Customization to how a table is displayed, how it behaves, and listen to UI events. @@ -86,6 +87,7 @@ def table( May contain action items or submenu items. May also be a function that receives the column header data and returns the context menu items or None. databars: Databars are experimental and will be moved to column_formatting in the future. + key: A unique identifier used by React to render elements in a list. Returns: The rendered Table. diff --git a/plugins/ui/src/deephaven/ui/components/tabs.py b/plugins/ui/src/deephaven/ui/components/tabs.py index a4350cf02..8fb2dabed 100644 --- a/plugins/ui/src/deephaven/ui/components/tabs.py +++ b/plugins/ui/src/deephaven/ui/components/tabs.py @@ -74,6 +74,7 @@ def tabs( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ): """ Python implementation for the Adobe React Spectrum Tabs component. @@ -215,4 +216,5 @@ def tabs( aria_details=aria_details, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/text.py b/plugins/ui/src/deephaven/ui/components/text.py index 15740a9e6..7b5e7a5af 100644 --- a/plugins/ui/src/deephaven/ui/components/text.py +++ b/plugins/ui/src/deephaven/ui/components/text.py @@ -56,6 +56,7 @@ def text( id: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ Text represents text with no specific semantic meaning. @@ -103,6 +104,7 @@ def text( id: The unique identifier of the element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. """ return component_element( "Text", @@ -148,4 +150,5 @@ def text( id=id, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/text_area.py b/plugins/ui/src/deephaven/ui/components/text_area.py index 727b6b7d9..69f1b204d 100644 --- a/plugins/ui/src/deephaven/ui/components/text_area.py +++ b/plugins/ui/src/deephaven/ui/components/text_area.py @@ -105,6 +105,7 @@ def text_area( aria_errormessage: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, # missing properties that are clipboard or composition events ) -> Element: """ @@ -186,6 +187,7 @@ def text_area( aria_errormessage: The id of the element that provides an error message for the current element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. Returns: The element representing the text area @@ -268,4 +270,5 @@ def text_area( aria_errormessage=aria_errormessage, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/text_field.py b/plugins/ui/src/deephaven/ui/components/text_field.py index bb4113f59..83f3b1a26 100644 --- a/plugins/ui/src/deephaven/ui/components/text_field.py +++ b/plugins/ui/src/deephaven/ui/components/text_field.py @@ -104,6 +104,7 @@ def text_field( aria_errormessage: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, # missing properties that are clipboard or composition events ) -> Element: """ @@ -187,6 +188,7 @@ def text_field( aria_errormessage: The id of the element that provides an error message for the current element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. Returns: The rendered text field element. @@ -271,4 +273,5 @@ def text_field( aria_errormessage=aria_errormessage, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/toggle_button.py b/plugins/ui/src/deephaven/ui/components/toggle_button.py index a9a16c018..ebd49984c 100644 --- a/plugins/ui/src/deephaven/ui/components/toggle_button.py +++ b/plugins/ui/src/deephaven/ui/components/toggle_button.py @@ -92,6 +92,7 @@ def toggle_button( aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ ToggleButtons allow users to toggle a selection on or off, for example switching between two states or modes. @@ -165,6 +166,7 @@ def toggle_button( aria_details: The details for the element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. """ return component_element( @@ -237,4 +239,5 @@ def toggle_button( aria_details=aria_details, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) diff --git a/plugins/ui/src/deephaven/ui/components/view.py b/plugins/ui/src/deephaven/ui/components/view.py index 1331ce1e6..0511048d5 100644 --- a/plugins/ui/src/deephaven/ui/components/view.py +++ b/plugins/ui/src/deephaven/ui/components/view.py @@ -86,6 +86,7 @@ def view( id: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: CSSProperties | None = None, + key: str | None = None, ) -> Element: """ View is a general purpose container with no specific semantics that can be used for custom styling purposes. It supports Spectrum style props to ensure consistency with other Spectrum components. @@ -162,6 +163,7 @@ def view( id: The unique identifier of the element. UNSAFE_class_name: A CSS class to apply to the element. UNSAFE_style: A CSS style to apply to the element. + key: A unique identifier used by React to render elements in a list. Returns: The rendered view. @@ -238,4 +240,5 @@ def view( id=id, UNSAFE_class_name=UNSAFE_class_name, UNSAFE_style=UNSAFE_style, + key=key, ) From a36ae2367d020bd57b1748d80c708842282b747e Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Wed, 18 Sep 2024 10:43:57 -0400 Subject: [PATCH 20/25] add missing docs --- plugins/ui/src/deephaven/ui/components/action_group.py | 1 + plugins/ui/src/deephaven/ui/components/action_menu.py | 1 + plugins/ui/src/deephaven/ui/components/button_group.py | 1 + plugins/ui/src/deephaven/ui/components/contextual_help.py | 1 + plugins/ui/src/deephaven/ui/components/icon.py | 1 + plugins/ui/src/deephaven/ui/components/illustrated_message.py | 1 + plugins/ui/src/deephaven/ui/components/list_action_group.py | 1 + plugins/ui/src/deephaven/ui/components/list_action_menu.py | 1 + plugins/ui/src/deephaven/ui/components/radio.py | 1 + plugins/ui/src/deephaven/ui/components/radio_group.py | 1 + plugins/ui/src/deephaven/ui/components/tab_list.py | 1 + plugins/ui/src/deephaven/ui/components/tab_panels.py | 1 + plugins/ui/src/deephaven/ui/components/tabs.py | 1 + 13 files changed, 13 insertions(+) diff --git a/plugins/ui/src/deephaven/ui/components/action_group.py b/plugins/ui/src/deephaven/ui/components/action_group.py index 018fac334..04c6d01f4 100644 --- a/plugins/ui/src/deephaven/ui/components/action_group.py +++ b/plugins/ui/src/deephaven/ui/components/action_group.py @@ -153,6 +153,7 @@ def action_group( aria-details: Identifies the element (or elements) that provide a detailed, extended description for the object. UNSAFE_class_name: Set the CSS className for the element. Only use as a last resort. Use style props instead. UNSAFE_style: Set the inline style for the element. Only use as a last resort. Use style props instead. + key: A unique identifier used by React to render elements in a list. """ return component_element( "ActionGroup", diff --git a/plugins/ui/src/deephaven/ui/components/action_menu.py b/plugins/ui/src/deephaven/ui/components/action_menu.py index 484da225c..557e1f532 100644 --- a/plugins/ui/src/deephaven/ui/components/action_menu.py +++ b/plugins/ui/src/deephaven/ui/components/action_menu.py @@ -148,6 +148,7 @@ def action_menu( aria-details: Identifies the element (or elements) that provide a detailed, extended description for the object. UNSAFE_class_name: Set the CSS className for the element. Only use as a last resort. Use style props instead. UNSAFE_style: Set the inline style for the element. Only use as a last resort. Use style props instead. + key: A unique identifier used by React to render elements in a list. """ return component_element( f"ActionMenu", diff --git a/plugins/ui/src/deephaven/ui/components/button_group.py b/plugins/ui/src/deephaven/ui/components/button_group.py index 009a9b253..484a2eeb3 100644 --- a/plugins/ui/src/deephaven/ui/components/button_group.py +++ b/plugins/ui/src/deephaven/ui/components/button_group.py @@ -102,6 +102,7 @@ def button_group( id: The unique identifier of the element. UNSAFE_class_name: Set the CSS className for the element. Only use as a last resort. Use style props instead. UNSAFE_style: Set the inline style for the element. Only use as a last resort. Use style props instead. + key: A unique identifier used by React to render elements in a list. """ return component_element( "ButtonGroup", diff --git a/plugins/ui/src/deephaven/ui/components/contextual_help.py b/plugins/ui/src/deephaven/ui/components/contextual_help.py index 4b734d54c..8de37b75e 100644 --- a/plugins/ui/src/deephaven/ui/components/contextual_help.py +++ b/plugins/ui/src/deephaven/ui/components/contextual_help.py @@ -128,6 +128,7 @@ def contextual_help( aria-details: Identifies the element (or elements) that provide a detailed, extended description for the object. UNSAFE_class_name: Set the CSS className for the element. Only use as a last resort. Use style props instead. UNSAFE_style: Set the inline style for the element. Only use as a last resort. Use style props instead. + key: A unique identifier used by React to render elements in a list. """ return component_element( "ContextualHelp", diff --git a/plugins/ui/src/deephaven/ui/components/icon.py b/plugins/ui/src/deephaven/ui/components/icon.py index f2d8d5f01..e3b0c4981 100644 --- a/plugins/ui/src/deephaven/ui/components/icon.py +++ b/plugins/ui/src/deephaven/ui/components/icon.py @@ -114,6 +114,7 @@ def icon( id: The unique identifier of the element. UNSAFE_class_name: Set the CSS className for the element. Only use as a last resort. Use style props instead. UNSAFE_style: Set the inline style for the element. Only use as a last resort. Use style props instead. + key: A unique identifier used by React to render elements in a list. """ children, props = create_props(locals()) diff --git a/plugins/ui/src/deephaven/ui/components/illustrated_message.py b/plugins/ui/src/deephaven/ui/components/illustrated_message.py index 27c86f36d..4e284c6d7 100644 --- a/plugins/ui/src/deephaven/ui/components/illustrated_message.py +++ b/plugins/ui/src/deephaven/ui/components/illustrated_message.py @@ -99,6 +99,7 @@ def illustrated_message( id: The unique identifier of the element. UNSAFE_class_name: Set the CSS className for the element. Only use as a last resort. Use style props instead. UNSAFE_style: Set the inline style for the element. Only use as a last resort. Use style props instead. + key: A unique identifier used by React to render elements in a list. Returns: The rendered IllustratedMessage component. diff --git a/plugins/ui/src/deephaven/ui/components/list_action_group.py b/plugins/ui/src/deephaven/ui/components/list_action_group.py index 270ece181..6293c9a67 100644 --- a/plugins/ui/src/deephaven/ui/components/list_action_group.py +++ b/plugins/ui/src/deephaven/ui/components/list_action_group.py @@ -163,6 +163,7 @@ def list_action_group( aria-details: Identifies the element (or elements) that provide a detailed, extended description for the object. UNSAFE_class_name: Set the CSS className for the element. Only use as a last resort. Use style props instead. UNSAFE_style: Set the inline style for the element. Only use as a last resort. Use style props instead. + key: A unique identifier used by React to render elements in a list. Returns: A ListActionGroup that can be used within the actions prop of a `ui.list_view` component. diff --git a/plugins/ui/src/deephaven/ui/components/list_action_menu.py b/plugins/ui/src/deephaven/ui/components/list_action_menu.py index ae4ab3c38..899bedc49 100644 --- a/plugins/ui/src/deephaven/ui/components/list_action_menu.py +++ b/plugins/ui/src/deephaven/ui/components/list_action_menu.py @@ -147,6 +147,7 @@ def list_action_menu( aria-details: Identifies the element (or elements) that provide a detailed, extended description for the object. UNSAFE_class_name: Set the CSS className for the element. Only use as a last resort. Use style props instead. UNSAFE_style: Set the inline style for the element. Only use as a last resort. Use style props instead. + key: A unique identifier used by React to render elements in a list. Returns: A ListActionMenu that can be used within the actions prop of a `ui.list_view` component. """ diff --git a/plugins/ui/src/deephaven/ui/components/radio.py b/plugins/ui/src/deephaven/ui/components/radio.py index c3a08b749..0c8b095f2 100644 --- a/plugins/ui/src/deephaven/ui/components/radio.py +++ b/plugins/ui/src/deephaven/ui/components/radio.py @@ -128,6 +128,7 @@ def radio( aria-details: Identifies the element (or elements) that provide a detailed, extended description for the object. UNSAFE_class_name: Set the CSS className for the element. Only use as a last resort. Use style props instead. UNSAFE_style: Set the inline style for the element. Only use as a last resort. Use style props instead. + key: A unique identifier used by React to render elements in a list. """ diff --git a/plugins/ui/src/deephaven/ui/components/radio_group.py b/plugins/ui/src/deephaven/ui/components/radio_group.py index 9b696f035..1ae0a1849 100644 --- a/plugins/ui/src/deephaven/ui/components/radio_group.py +++ b/plugins/ui/src/deephaven/ui/components/radio_group.py @@ -166,6 +166,7 @@ def radio_group( aria_errormessage: Identifies the element that provides an error message for the object. UNSAFE_class_name: Set the CSS className for the element. Only use as a last resort. Use style props instead. UNSAFE_style: Set the inline style for the element. Only use as a last resort. Use style props instead. + key: A unique identifier used by React to render elements in a list. Returns: The rendered radio group component. diff --git a/plugins/ui/src/deephaven/ui/components/tab_list.py b/plugins/ui/src/deephaven/ui/components/tab_list.py index ecef0956a..941611a33 100644 --- a/plugins/ui/src/deephaven/ui/components/tab_list.py +++ b/plugins/ui/src/deephaven/ui/components/tab_list.py @@ -95,6 +95,7 @@ def tab_list( id: The unique identifier of the element. UNSAFE_class_name: Set the CSS className for the element. Only use as a last resort. Use style props instead. UNSAFE_style: Set the inline style for the element. Only use as a last resort. Use style props instead. + key: A unique identifier used by React to render elements in a list. """ if not children: raise ValueError("Tab Lists must have at least one child.") diff --git a/plugins/ui/src/deephaven/ui/components/tab_panels.py b/plugins/ui/src/deephaven/ui/components/tab_panels.py index 5401ca228..e6b80a1e5 100644 --- a/plugins/ui/src/deephaven/ui/components/tab_panels.py +++ b/plugins/ui/src/deephaven/ui/components/tab_panels.py @@ -95,6 +95,7 @@ def tab_panels( id: The unique identifier of the element. UNSAFE_class_name: Set the CSS className for the element. Only use as a last resort. Use style props instead. UNSAFE_style: Set the inline style for the element. Only use as a last resort. Use style props instead. + key: A unique identifier used by React to render elements in a list. """ if not children: raise ValueError("Tab Panels must have at least one child.") diff --git a/plugins/ui/src/deephaven/ui/components/tabs.py b/plugins/ui/src/deephaven/ui/components/tabs.py index 8fb2dabed..29832f75e 100644 --- a/plugins/ui/src/deephaven/ui/components/tabs.py +++ b/plugins/ui/src/deephaven/ui/components/tabs.py @@ -134,6 +134,7 @@ def tabs( aria_details: Identifies the element (or elements) that provide a detailed, extended description for the object. UNSAFE_class_name: Set the CSS className for the element. Only use as a last resort. Use style props instead. UNSAFE_style: Set the inline style for the element. Only use as a last resort. Use style props instead. + key: A unique identifier used by React to render elements in a list. """ if not children: raise ValueError("Tabs must have at least one child.") From b57ea8bd0e6cfd5c6ce4305c680c14c8b0e15a9e Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Wed, 18 Sep 2024 14:23:28 -0400 Subject: [PATCH 21/25] add key desc for fragment --- plugins/ui/src/deephaven/ui/components/fragment.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/ui/src/deephaven/ui/components/fragment.py b/plugins/ui/src/deephaven/ui/components/fragment.py index a67fb1492..1086407a0 100644 --- a/plugins/ui/src/deephaven/ui/components/fragment.py +++ b/plugins/ui/src/deephaven/ui/components/fragment.py @@ -11,5 +11,6 @@ def fragment(*children: Any, key: str | None = None): Args: *children: The children in the fragment. + key: A unique identifier used by React to render elements in a list. """ return component_element("Fragment", children=children, key=key) From ec3290b55b4376585a7169fa48fda8a7c2919d15 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Wed, 18 Sep 2024 17:38:35 -0400 Subject: [PATCH 22/25] remove kwargs --- plugins/ui/src/deephaven/ui/components/column.py | 6 ++---- plugins/ui/src/deephaven/ui/components/row.py | 6 ++---- plugins/ui/src/deephaven/ui/components/section.py | 5 ++--- plugins/ui/src/deephaven/ui/components/tab.py | 2 +- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/components/column.py b/plugins/ui/src/deephaven/ui/components/column.py index c023d23da..ef6868097 100644 --- a/plugins/ui/src/deephaven/ui/components/column.py +++ b/plugins/ui/src/deephaven/ui/components/column.py @@ -4,9 +4,7 @@ from .basic import component_element -def column( - *children: Any, width: float | None = None, key: str | None = None, **kwargs: Any -): +def column(*children: Any, width: float | None = None, key: str | None = None): """ A column is a container that can be used to group elements. Each element will be placed below its prior sibling. @@ -16,4 +14,4 @@ def column( width: The percent width of the column relative to other children of its parent. If not provided, the column will be sized automatically. key: A unique identifier used by React to render elements in a list. """ - return component_element("Column", *children, width=width, key=key, **kwargs) + return component_element("Column", *children, width=width, key=key) diff --git a/plugins/ui/src/deephaven/ui/components/row.py b/plugins/ui/src/deephaven/ui/components/row.py index da8621c92..801ef063d 100644 --- a/plugins/ui/src/deephaven/ui/components/row.py +++ b/plugins/ui/src/deephaven/ui/components/row.py @@ -4,9 +4,7 @@ from .basic import component_element -def row( - *children: Any, height: float | None = None, key: str | None = None, **kwargs: Any -): +def row(*children: Any, height: float | None = None, key: str | None = None): """ A row is a container that can be used to group elements. Each element will be placed to the right of its prior sibling. @@ -16,4 +14,4 @@ def row( height: The percent height of the row relative to other children of its parent. If not provided, the row will be sized automatically. key: A unique identifier used by React to render elements in a list. """ - return component_element("Row", *children, height=height, key=key, **kwargs) + return component_element("Row", *children, height=height, key=key) diff --git a/plugins/ui/src/deephaven/ui/components/section.py b/plugins/ui/src/deephaven/ui/components/section.py index bd80b5ccd..058ec76a8 100644 --- a/plugins/ui/src/deephaven/ui/components/section.py +++ b/plugins/ui/src/deephaven/ui/components/section.py @@ -11,7 +11,7 @@ def section( - *children: Item, title: str | None = None, key: str | None = None, **props: Any + *children: Item, title: str | None = None, key: str | None = None ) -> SectionElement: """ A section that can be added to a menu, such as a picker. Children are the dropdown options. @@ -20,7 +20,6 @@ def section( *children: The options to render within the section. title: The title of the section. key: A unique identifier used by React to render elements in a list. - **props: Any other Section prop. Returns: The rendered Section. @@ -28,4 +27,4 @@ def section( children, props = create_props(locals()) - return component_element("Section", *children, **props) + return component_element("Section", *children) diff --git a/plugins/ui/src/deephaven/ui/components/tab.py b/plugins/ui/src/deephaven/ui/components/tab.py index 3c3cb1413..abaf467b1 100644 --- a/plugins/ui/src/deephaven/ui/components/tab.py +++ b/plugins/ui/src/deephaven/ui/components/tab.py @@ -21,7 +21,7 @@ def tab( Args: *children: Content of the tab item. title: The title of the tab item. - key: The unique key of the tab item. Defaults to title. Also a unique identifier used by React to render elements in a list (but will not default to title). + key: The unique key of the tab item. Defaults to title. icon: The icon of the tab item. text_value: The text value of the tab item. """ From 2516177483e7938bee56ee74a929a7a34b7dce84 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Wed, 18 Sep 2024 17:49:06 -0400 Subject: [PATCH 23/25] remove render map --- .../ui/src/deephaven/ui/renderer/Renderer.py | 30 +------------------ 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/renderer/Renderer.py b/plugins/ui/src/deephaven/ui/renderer/Renderer.py index 62345b197..2825d1b78 100644 --- a/plugins/ui/src/deephaven/ui/renderer/Renderer.py +++ b/plugins/ui/src/deephaven/ui/renderer/Renderer.py @@ -61,9 +61,7 @@ def _render_item(item: Any, context: RenderContext) -> Any: The rendered item. """ logger.debug("_render_item context is %s", context) - if isinstance(item, map): - return _render_map(item, context) - if isinstance(item, (list, tuple)): + if isinstance(item, (list, map, tuple)): # I couldn't figure out how to map a `list[Unknown]` to a `list[Any]`, or to accept a `list[Unknown]` as a parameter return _render_list(item, context) # type: ignore if isinstance(item, dict): @@ -80,32 +78,6 @@ def _render_item(item: Any, context: RenderContext) -> Any: return item -def _render_map(item: map, context: RenderContext) -> list[Any]: - """ - Renders a map object. Checks if each child has a unique key and then the map as a list - - Args: - item: The map to render. - context: The context to render the map in. - - Returns: - The rendered map. - """ - has_sent_error = False # Only throw the error once - logger.debug("_render_map %s", item) - with context.open(): - result = [] - used_keys = set() - for key, value in enumerate(item): - if isinstance(value, Element): - if value.key_prop in used_keys and not has_sent_error: - logger.error('Each child in a map should have a unique "key" prop.') - has_sent_error = True - used_keys.add(value.key_prop) - result.append(_render_child_item(value, str(key), context)) - return result - - def _render_list( item: list[Any] | tuple[Any, ...], context: RenderContext ) -> list[Any]: From 28044084eaed8f7ed48569991b0d4c255efc7423 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Thu, 19 Sep 2024 09:28:03 -0400 Subject: [PATCH 24/25] remove generate_key method --- plugins/ui/src/deephaven/ui/elements/BaseElement.py | 11 +++-------- plugins/ui/src/deephaven/ui/elements/Element.py | 12 +----------- .../ui/src/deephaven/ui/elements/FunctionElement.py | 11 +++-------- plugins/ui/src/deephaven/ui/elements/UITable.py | 8 +++++--- plugins/ui/src/deephaven/ui/renderer/Renderer.py | 4 ++-- 5 files changed, 14 insertions(+), 32 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/elements/BaseElement.py b/plugins/ui/src/deephaven/ui/elements/BaseElement.py index 68c381c6d..ea6bde2bb 100644 --- a/plugins/ui/src/deephaven/ui/elements/BaseElement.py +++ b/plugins/ui/src/deephaven/ui/elements/BaseElement.py @@ -15,7 +15,7 @@ def __init__( self, name: str, /, *children: Any, key: str | None = None, **props: Any ): self._name = name - self._keyProp = key + self._key = key props["key"] = key if len(children) > 0 and props.get("children") is not None: @@ -34,13 +34,8 @@ def name(self) -> str: return self._name @property - def key_prop(self) -> str | None: - return self._keyProp - - def generate_key(self, index_key: str) -> str: - if self._keyProp is not None: - return self._keyProp - return f"{index_key}-{self._name}" + def key(self) -> str | None: + return self._key def render(self, context: RenderContext) -> dict[str, Any]: return self._props diff --git a/plugins/ui/src/deephaven/ui/elements/Element.py b/plugins/ui/src/deephaven/ui/elements/Element.py index 6a3fa8e4e..fe6a168f9 100644 --- a/plugins/ui/src/deephaven/ui/elements/Element.py +++ b/plugins/ui/src/deephaven/ui/elements/Element.py @@ -23,7 +23,7 @@ def name(self) -> str: return "deephaven.ui.Element" @property - def key_prop(self) -> str | None: + def key(self) -> str | None: """ Get the key prop of this element. Useful to check if a key prop was provided. @@ -32,16 +32,6 @@ def key_prop(self) -> str | None: """ return None - @abstractmethod - def generate_key(self, index_key: str) -> str: - """ - Get the key of this element. Subclasses should access the key prop if it exists or generate their own unique key. - - Returns: - The unique key of this element. - """ - pass - @abstractmethod def render(self, context: RenderContext) -> PropsType: """ diff --git a/plugins/ui/src/deephaven/ui/elements/FunctionElement.py b/plugins/ui/src/deephaven/ui/elements/FunctionElement.py index 24095de80..6b1ea35f9 100644 --- a/plugins/ui/src/deephaven/ui/elements/FunctionElement.py +++ b/plugins/ui/src/deephaven/ui/elements/FunctionElement.py @@ -20,20 +20,15 @@ def __init__( """ self._name = name self._render = render - self._keyProp = key + self._key = key @property def name(self): return self._name @property - def key_prop(self) -> str | None: - return self._keyProp - - def generate_key(self, index_key: str) -> str: - if self._keyProp is not None: - return self._keyProp - return f"{index_key}-{self._name}" + def key(self) -> str | None: + return self._key def render(self, context: RenderContext) -> PropsType: """ diff --git a/plugins/ui/src/deephaven/ui/elements/UITable.py b/plugins/ui/src/deephaven/ui/elements/UITable.py index 9ce549557..24e909984 100644 --- a/plugins/ui/src/deephaven/ui/elements/UITable.py +++ b/plugins/ui/src/deephaven/ui/elements/UITable.py @@ -124,11 +124,16 @@ def __init__( # Store all the props that were passed in self._props = UITableProps(**props, table=table) + self._key = props.get("key") @property def name(self): return "deephaven.ui.elements.UITable" + @property + def key(self) -> str | None: + return self._key + def _with_prop(self, key: str, value: Any) -> "UITable": """ Create a new UITable with the passed in prop added to the existing props @@ -185,9 +190,6 @@ def _with_dict_prop(self, key: str, value: dict[str, Any]) -> "UITable": ) # Turn missing or explicit None into empty dict return UITable(**{**self._props, key: {**existing, **value}}) # type: ignore - def generate_key(self, index_key: str) -> str: - return f"{index_key}-{self.name}" - def render(self, context: RenderContext) -> dict[str, Any]: logger.debug("Returning props %s", self._props) return dict_to_camel_case({**self._props}) diff --git a/plugins/ui/src/deephaven/ui/renderer/Renderer.py b/plugins/ui/src/deephaven/ui/renderer/Renderer.py index 2825d1b78..f811fb375 100644 --- a/plugins/ui/src/deephaven/ui/renderer/Renderer.py +++ b/plugins/ui/src/deephaven/ui/renderer/Renderer.py @@ -22,10 +22,10 @@ def _get_context_key(item: Any, index_key: str) -> Union[str, None]: - If `item` is an `Element` generate a key based on the `index_key` and the `name` of the `Element`. - If the item is another iterable, just return the `index_key`. - Otherwise, return `None` as the key. - - TODO #731: use a `key` prop if it exists on the `Element`. """ if isinstance(item, Element): - return item.generate_key(index_key) + key = item.key + return key if key is not None else f"{index_key}-{item.name}" if isinstance(item, (Dict, List, Tuple, map)): return index_key return None From 08b89937ffcdafbe1ad86f69ee069780b3a1af42 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Thu, 19 Sep 2024 10:57:37 -0400 Subject: [PATCH 25/25] add e2e --- tests/app.d/ui.py | 39 +++++++++++++++++++++++++++++++++++++++ tests/ui.spec.ts | 19 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/tests/app.d/ui.py b/tests/app.d/ui.py index 290ec12d8..61990a1a5 100644 --- a/tests/app.d/ui.py +++ b/tests/app.d/ui.py @@ -1,4 +1,5 @@ from deephaven import ui +from itertools import count @ui.component @@ -31,6 +32,44 @@ def ui_boom_counter_component(): return ui.button(f"Count is {value}", on_press=lambda _: set_value(value + 1)) +@ui.component +def ui_cell(label: str = "Cell"): + text, set_text = ui.use_state("") + + return ui.text_field(label=label, value=text, on_change=set_text) + + +@ui.component +def ui_cells(): + id_iter, _ = ui.use_state(lambda: count()) + cells, set_cells = ui.use_state(lambda: [next(id_iter)]) + + def add_cell(): + set_cells(lambda old_cells: old_cells + [next(id_iter)]) + + def delete_cell(delete_id: int): + set_cells(lambda old_cells: [c for c in old_cells if c != delete_id]) + + return ui.view( + map( + lambda i: ui.flex( + ui_cell(label=f"Cell {i}"), + ui.action_button( + ui.icon("vsTrash"), + aria_label="Delete cell", + on_press=lambda: delete_cell(i), + ), + align_items="end", + key=str(i), + ), + cells, + ), + ui.action_button(ui.icon("vsAdd"), "Add cell", on_press=add_cell), + overflow="auto", + ) + + ui_component = ui_basic_component() ui_boom = ui_boom_component() ui_boom_counter = ui_boom_counter_component() +ui_cells = ui_cells() diff --git a/tests/ui.spec.ts b/tests/ui.spec.ts index c22138e73..173e97e63 100644 --- a/tests/ui.spec.ts +++ b/tests/ui.spec.ts @@ -48,6 +48,25 @@ test('boom counter component shows error overlay after clicking the button twice await expect(panelLocator.getByText('BOOM! Value too big.')).toBeVisible(); }); +test('Using keys for lists works', async ({ page }) => { + await gotoPage(page, ''); + await openPanel(page, 'ui_cells', selector.REACT_PANEL_VISIBLE); + + // setup cells + await page.getByRole('button', { name: 'Add cell' }).click(); + await page.getByRole('button', { name: 'Add cell' }).click(); + await page.getByRole('textbox', { name: 'Cell 0' }).fill('a'); + await page.getByRole('textbox', { name: 'Cell 1' }).fill('b'); + await page.getByRole('textbox', { name: 'Cell 2' }).fill('c'); + await page.getByRole('button', { name: 'Delete cell' }).nth(1).click(); + + expect(page.getByRole('textbox', { name: 'Cell 0' })).toBeVisible(); + expect(page.getByRole('textbox', { name: 'Cell 0' })).toHaveValue('a'); + expect(page.getByRole('textbox', { name: 'Cell 1' })).not.toBeVisible(); + expect(page.getByRole('textbox', { name: 'Cell 2' })).toBeVisible(); + expect(page.getByRole('textbox', { name: 'Cell 2' })).toHaveValue('c'); +}); + test('UI all components render 1', async ({ page }) => { await gotoPage(page, ''); await openPanel(page, 'ui_render_all1', selector.REACT_PANEL_VISIBLE);