Skip to content

Commit

Permalink
add undefined
Browse files Browse the repository at this point in the history
  • Loading branch information
wusteven815 committed Nov 18, 2024
1 parent 00c2181 commit 22bd83d
Show file tree
Hide file tree
Showing 17 changed files with 292 additions and 60 deletions.
76 changes: 57 additions & 19 deletions plugins/ui/src/deephaven/ui/_internal/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
JavaTime,
LocalDateConvertible,
LocalDate,
Null,
Undefined,
)

T = TypeVar("T")
Expand All @@ -36,6 +38,19 @@
}


def is_nullish(value: Any) -> bool:
"""
Check if a value is nullish (`None`, `Null`, or `Undefined`).
Args:
value: The value to check.
Returns:
Checks if the value is nullish.
"""
return value is None or value is Null or value is Undefined


def get_component_name(component: Any) -> str:
"""
Get the name of the component
Expand Down Expand Up @@ -138,7 +153,9 @@ def dict_to_camel_case(
return convert_dict_keys(dict, to_camel_case)


def dict_to_react_props(dict: dict[str, Any]) -> dict[str, Any]:
def dict_to_react_props(
dict: dict[str, Any], _nullable_props: list[str] = []
) -> dict[str, Any]:
"""
Convert a dict to React-style prop names ready for the web.
Converts snake_case to camelCase with the exception of special props like `UNSAFE_` or `aria_` props.
Expand All @@ -150,10 +167,14 @@ def dict_to_react_props(dict: dict[str, Any]) -> dict[str, Any]:
Returns:
The React props dict.
"""
return convert_dict_keys(remove_empty_keys(dict), to_react_prop_case)
return convert_dict_keys(
remove_empty_keys(dict, _nullable_props), to_react_prop_case
)


def remove_empty_keys(dict: dict[str, Any]) -> dict[str, Any]:
def remove_empty_keys(
dict: dict[str, Any], _nullable_props: list[str] = []
) -> dict[str, Any]:
"""
Remove keys from a dict that have a value of None.
Expand All @@ -163,7 +184,22 @@ def remove_empty_keys(dict: dict[str, Any]) -> dict[str, Any]:
Returns:
The dict with keys removed.
"""
return {k: v for k, v in dict.items() if v is not None}
cleaned = {}
for k, v in dict.items():
if k in _nullable_props:
if v is Null:
cleaned[k] = None
elif v is not Undefined and v is not None:
cleaned[k] = v
else:
if v is Null or v is Undefined:
raise ValueError(
"NullType or UndefinedType found in a non-nullable prop."
)
elif v is not None:
cleaned[k] = v

return cleaned


def _wrapped_callable(
Expand Down Expand Up @@ -478,10 +514,10 @@ def _get_first_set_key(props: dict[str, Any], sequence: Sequence[str]) -> str |
sequence: The sequence to check.
Returns:
The first non-None prop, or None if all props are None.
The first non-nullish prop, or None if all props are None.
"""
for key in sequence:
if props.get(key) is not None:
if not is_nullish(props.get(key)):
return key
return None

Expand Down Expand Up @@ -523,9 +559,10 @@ def _prioritized_date_callable_converter(
"""

first_set_key = _get_first_set_key(props, priority)
# type ignore because pyright is not recognizing the nullish check
return (
_jclass_date_converter(_date_or_range(props[first_set_key]))
if first_set_key is not None
_jclass_date_converter(_date_or_range(props[first_set_key])) # type: ignore
if not is_nullish(first_set_key)
else default_converter
)

Expand All @@ -552,9 +589,10 @@ def _prioritized_time_callable_converter(
"""

first_set_key = _get_first_set_key(props, priority)
# type ignore because pyright is not recognizing the nullish check
return (
_jclass_time_converter(props[first_set_key])
if first_set_key is not None
_jclass_time_converter(props[first_set_key]) # type: ignore
if not is_nullish(first_set_key)
else default_converter
)

Expand Down Expand Up @@ -666,11 +704,11 @@ def convert_date_props(
The converted props.
"""
for key in simple_date_props:
if props.get(key) is not None:
if not is_nullish(props.get(key)):
props[key] = _convert_to_java_date(props[key])

for key in date_range_props:
if props.get(key) is not None:
if not is_nullish(props.get(key)):
props[key] = convert_date_range(props[key], _convert_to_java_date)

# the simple props must be converted before this to simplify the callable conversion
Expand All @@ -680,25 +718,25 @@ def convert_date_props(
# Local Dates will default to DAY but we need to default to SECOND for the other types
if (
granularity_key is not None
and props.get(granularity_key) is None
and is_nullish(props.get(granularity_key))
and converter != to_j_local_date
):
props[granularity_key] = "SECOND"

# now that the converter is set, we can convert simple props to strings
for key in simple_date_props:
if props.get(key) is not None:
if not is_nullish(props.get(key)):
props[key] = str(props[key])

# and convert the date range props to strings
for key in date_range_props:
if props.get(key) is not None:
if not is_nullish(props.get(key)):
props[key] = convert_date_range(props[key], str)

# wrap the date callable with the convert
# if there are date range props, we need to convert as a date range
for key in callable_date_props:
if props.get(key) is not None:
if not is_nullish(props.get(key)):
if not callable(props[key]):
raise TypeError(f"{key} must be a callable")
if len(date_range_props) > 0:
Expand Down Expand Up @@ -730,20 +768,20 @@ def convert_time_props(
The converted props.
"""
for key in simple_time_props:
if props.get(key) is not None:
if not is_nullish(props.get(key)):
props[key] = _convert_to_java_time(props[key])

# the simple props must be converted before this to simplify the callable conversion
converter = _prioritized_time_callable_converter(props, priority, default_converter)

# now that the converter is set, we can convert simple props to strings
for key in simple_time_props:
if props.get(key) is not None:
if not is_nullish(props.get(key)):
props[key] = str(props[key])

# wrap the date callable with the convert
for key in callable_time_props:
if props.get(key) is not None:
if not is_nullish(props.get(key)):
if not callable(props[key]):
raise TypeError(f"{key} must be a callable")
props[key] = _wrap_time_callable(props[key], converter)
Expand Down
11 changes: 6 additions & 5 deletions plugins/ui/src/deephaven/ui/components/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@

from ..elements import Element
from .._internal.utils import create_props, convert_date_props, wrap_local_date_callable
from ..types import Date, LocalDateConvertible
from ..types import Date, LocalDateConvertible, NullType, Undefined, UndefinedType
from .basic import component_element
from .make_component import make_component
from deephaven.time import dh_now

CalendarElement = Element

Expand All @@ -43,6 +42,8 @@
"default_focused_value",
]

_NULLABLE_PROPS = ["value", "default_value"]


def _convert_calendar_props(
props: dict[str, Any],
Expand Down Expand Up @@ -75,8 +76,8 @@ def _convert_calendar_props(

@make_component
def calendar(
value: Date | None = None,
default_value: Date | None = None,
value: Date | UndefinedType | NullType = Undefined,
default_value: Date | UndefinedType | NullType = Undefined,
focused_value: Date | None = None,
default_focused_value: Date | None = None,
min_value: Date | None = None,
Expand Down Expand Up @@ -213,4 +214,4 @@ def calendar(

_convert_calendar_props(props)

return component_element("Calendar", **props)
return component_element("Calendar", _nullable_props=_NULLABLE_PROPS, **props)
10 changes: 7 additions & 3 deletions plugins/ui/src/deephaven/ui/components/combo_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from .item_table_source import ItemTableSource
from ..elements import BaseElement, Element
from .._internal.utils import create_props, unpack_item_table_source
from ..types import Key
from ..types import Key, NullType, Undefined, UndefinedType
from .basic import component_element

ComboBoxElement = BaseElement
Expand All @@ -42,6 +42,8 @@
"title_column",
}

_NULLABLE_PROPS = ["selected_key"]


def combo_box(
*children: Item | SectionElement | Table | PartitionedTable | ItemTableSource,
Expand All @@ -58,7 +60,7 @@ def combo_box(
default_input_value: str | None = None,
allows_custom_value: bool | None = None,
disabled_keys: list[Key] | None = None,
selected_key: Key | None = None,
selected_key: Key | UndefinedType | NullType = Undefined,
default_selected_key: Key | None = None,
is_disabled: bool | None = None,
is_read_only: bool | None = None,
Expand Down Expand Up @@ -241,4 +243,6 @@ def combo_box(

children, props = unpack_item_table_source(children, props, SUPPORTED_SOURCE_ARGS)

return component_element("ComboBox", *children, **props)
return component_element(
"ComboBox", *children, _nullable_props=_NULLABLE_PROPS, **props
)
10 changes: 6 additions & 4 deletions plugins/ui/src/deephaven/ui/components/date_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
create_props,
convert_date_props,
)
from ..types import Date, Granularity
from ..types import Date, Granularity, NullType, Undefined, UndefinedType
from .basic import component_element
from .make_component import make_component
from deephaven.time import dh_now
Expand All @@ -47,6 +47,8 @@
# The priority of the date props to determine the format of the date passed to the callable date props
_DATE_PROPS_PRIORITY = ["value", "default_value", "placeholder_value"]

_NULLABLE_PROPS = ["value", "default_value"]


def _convert_date_field_props(
props: dict[str, Any],
Expand Down Expand Up @@ -76,8 +78,8 @@ def _convert_date_field_props(
@make_component
def date_field(
placeholder_value: Date | None = dh_now(),
value: Date | None = None,
default_value: Date | None = None,
value: Date | UndefinedType | NullType = Undefined,
default_value: Date | UndefinedType | NullType = Undefined,
min_value: Date | None = None,
max_value: Date | None = None,
# TODO (issue # 698) we need to implement unavailable_values
Expand Down Expand Up @@ -261,4 +263,4 @@ def date_field(

_convert_date_field_props(props)

return component_element("DateField", **props)
return component_element("DateField", _nullable_props=_NULLABLE_PROPS, **props)
10 changes: 6 additions & 4 deletions plugins/ui/src/deephaven/ui/components/date_picker.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
convert_date_props,
convert_list_prop,
)
from ..types import Date, Granularity
from ..types import Date, Granularity, NullType, Undefined, UndefinedType
from .basic import component_element
from .make_component import make_component
from deephaven.time import dh_now
Expand All @@ -51,6 +51,8 @@
# The priority of the date props to determine the format of the date passed to the callable date props
_DATE_PROPS_PRIORITY = ["value", "default_value", "placeholder_value"]

_NULLABLE_PROPS = ["value", "default_value"]


def _convert_date_picker_props(
props: dict[str, Any],
Expand Down Expand Up @@ -80,8 +82,8 @@ def _convert_date_picker_props(
@make_component
def date_picker(
placeholder_value: Date | None = dh_now(),
value: Date | None = None,
default_value: Date | None = None,
value: Date | UndefinedType | NullType = Undefined,
default_value: Date | UndefinedType | NullType = Undefined,
min_value: Date | None = None,
max_value: Date | None = None,
# TODO (issue # 698) we need to implement unavailable_values
Expand Down Expand Up @@ -280,4 +282,4 @@ def date_picker(
# [unavailable_values],
# )

return component_element("DatePicker", **props)
return component_element("DatePicker", _nullable_props=_NULLABLE_PROPS, **props)
12 changes: 8 additions & 4 deletions plugins/ui/src/deephaven/ui/components/date_range_picker.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
convert_date_props,
convert_list_prop,
)
from ..types import Date, Granularity, DateRange
from ..types import Date, Granularity, DateRange, NullType, Undefined, UndefinedType
from .basic import component_element
from .make_component import make_component
from deephaven.time import dh_now
Expand All @@ -49,6 +49,8 @@
# The priority of the date props to determine the format of the date passed to the callable date props
_DATE_PROPS_PRIORITY = ["value", "default_value", "placeholder_value"]

_NULLABLE_PROPS = ["value", "default_value"]


def _convert_date_range_picker_props(
props: dict[str, Any],
Expand Down Expand Up @@ -78,8 +80,8 @@ def _convert_date_range_picker_props(
@make_component
def date_range_picker(
placeholder_value: Date | None = dh_now(),
value: DateRange | None = None,
default_value: DateRange | None = None,
value: DateRange | UndefinedType | NullType = Undefined,
default_value: DateRange | UndefinedType | NullType = Undefined,
min_value: Date | None = None,
max_value: Date | None = None,
# TODO (issue # 698) we need to implement unavailable_values
Expand Down Expand Up @@ -278,4 +280,6 @@ def date_range_picker(

_convert_date_range_picker_props(props)

return component_element("DateRangePicker", **props)
return component_element(
"DateRangePicker", _nullable_props=_NULLABLE_PROPS, **props
)
10 changes: 7 additions & 3 deletions plugins/ui/src/deephaven/ui/components/picker.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from .item_table_source import ItemTableSource
from ..elements import BaseElement, Element
from .._internal.utils import create_props, unpack_item_table_source
from ..types import Key
from ..types import Key, NullType, Undefined, UndefinedType
from .types import (
AlignSelf,
CSSProperties,
Expand All @@ -35,11 +35,13 @@
"title_column",
}

_NULLABLE_PROPS = ["selected_key"]


def picker(
*children: Item | SectionElement | Table | PartitionedTable | ItemTableSource,
default_selected_key: Key | None = None,
selected_key: Key | None = None,
selected_key: Key | UndefinedType | NullType = Undefined,
on_selection_change: Callable[[Key], None] | None = None,
on_change: Callable[[Key], None] | None = None,
is_quiet: bool | None = None,
Expand Down Expand Up @@ -227,4 +229,6 @@ def picker(

children, props = unpack_item_table_source(children, props, SUPPORTED_SOURCE_ARGS)

return component_element("Picker", *children, **props)
return component_element(
"Picker", *children, _nullable_props=_NULLABLE_PROPS, **props
)
Loading

0 comments on commit 22bd83d

Please sign in to comment.