diff --git a/cog.toml b/cog.toml index 7975d8797..8d51064d4 100644 --- a/cog.toml +++ b/cog.toml @@ -4,7 +4,7 @@ disable_changelog = false # we never tag any code outside the plugins/ directory. Everything else is build glue. generate_mono_repository_global_tag = false # limit which branches to perform bumps from -branch_whitelist = [ "main" ] +branch_whitelist = [ "main", "release/*" ] # we don't really use [skip ci] action filtering, but leaving here in case we decide to someday skip_ci = "[skip ci]" skip_untracked = false diff --git a/lerna.json b/lerna.json index 00e1ac584..50caeec0c 100644 --- a/lerna.json +++ b/lerna.json @@ -3,5 +3,10 @@ "useWorkspaces": true, "useNx": false, "version": "independent", - "packages": ["plugins/*/src/js/"] + "packages": ["plugins/*/src/js/"], + "command": { + "version": { + "changelogPreset": "conventionalcommits" + } + } } diff --git a/package-lock.json b/package-lock.json index d2252d2cd..b05823bd9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "@types/node": "^20.11.17", "@types/prop-types": "^15.7.10", "@vitejs/plugin-react-swc": "^3.7.0", + "conventional-changelog-conventionalcommits": "^7.0.0", "eslint": "^8.37.0", "identity-obj-proxy": "^3.0.0", "jest": "^29.6.2", @@ -13483,6 +13484,18 @@ "node": ">=10" } }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", + "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/conventional-changelog-core": { "version": "4.2.4", "dev": true, @@ -32333,7 +32346,7 @@ }, "plugins/ui/src/js": { "name": "@deephaven/js-plugin-ui", - "version": "0.24.0", + "version": "0.25.0", "license": "Apache-2.0", "dependencies": { "@deephaven/chart": "^0.101.0", @@ -163184,6 +163197,15 @@ "q": "^1.5.1" } }, + "conventional-changelog-conventionalcommits": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", + "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", + "dev": true, + "requires": { + "compare-func": "^2.0.0" + } + }, "conventional-changelog-core": { "version": "4.2.4", "dev": true, diff --git a/package.json b/package.json index 68cbeb778..df3c2a606 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@types/node": "^20.11.17", "@types/prop-types": "^15.7.10", "@vitejs/plugin-react-swc": "^3.7.0", + "conventional-changelog-conventionalcommits": "^7.0.0", "eslint": "^8.37.0", "identity-obj-proxy": "^3.0.0", "jest": "^29.6.2", diff --git a/plugins/plotly-express/docs/sidebar.json b/plugins/plotly-express/docs/sidebar.json index a19f52bf9..b0276dc7e 100644 --- a/plugins/plotly-express/docs/sidebar.json +++ b/plugins/plotly-express/docs/sidebar.json @@ -135,6 +135,10 @@ { "label": "Multiple axes", "path": "multiple-axes.md" + }, + { + "label": "`unsafe_update_figure` Chart Customization", + "path": "unsafe_update_figure.md" } ] } diff --git a/plugins/plotly-express/docs/unsafe-update-figure.md b/plugins/plotly-express/docs/unsafe-update-figure.md new file mode 100644 index 000000000..e0bdc82f1 --- /dev/null +++ b/plugins/plotly-express/docs/unsafe-update-figure.md @@ -0,0 +1,107 @@ +# `unsafe_update_figure` Chart Customization + +To customize a chart in a way that is not directly supported by Deephaven Plotly Express (`dx`), use the `unsafe_update_figure` parameter. +Every `dx` chart is backed by a Plotly [`Figure`](https://plotly.com/python/figure-structure/). This object gets passed to `unsafe_update_figure` where it can be modified directly. See the [Plotly Figure Reference](https://plotly.com/python/reference/) docs for details on available `Figure` properties. + +> [!WARNING] +> Update figure is marked "unsafe" because some modifications can entirely break your figure, and care must be taken. +> `dx` maps `Table` columns to an index of a trace within `Figure.data` which will break if the trace order changes. Do not remove traces. Add new traces at the end of the list. + +`unsafe_update_figure` accepts a function that takes a Plotly `Figure` object as input and optionally returns a modified `Figure` object. If a `Figure` is not returned, it is assumed that the input `Figure` has been modified in place. + +## Examples + +### Bar Line + +Add a line to bars in a bar plot with `update_traces`. + +```python +import deephaven.plot.express as dx + +tips = dx.data.tips() + + +def update(figure): + # Add a gray line to the bars + figure.update_traces(marker_line_width=3, marker_line_color="gray") + + +bar_lined_plot = dx.bar(tips, x="Day", unsafe_update_figure=update) +``` + +### Legend Location + +Change the location of the legend to the bottom of the plot by updating the layout. + +```python +import deephaven.plot.express as dx + +tips = dx.data.tips() + + +def update(figure): + # Update the layout to move the legend to the bottom + # y is negative to move the legend outside the plot area + figure.update_layout( + legend=dict(orientation="h", yanchor="bottom", y=-0.3, xanchor="left", x=0.3) + ) + + +legend_bottom_plot = dx.scatter( + tips, x="TotalBill", y="Tip", color="Day", unsafe_update_figure=update +) +``` + +### Vertical Line + +Add a vertical line to a plot with `add_vline`. + +```python +import deephaven.plot.express as dx + +tips = dx.data.tips() + + +def update(figure): + # Add a dashed orange vertical line at x=20 + figure.add_vline(x=20, line_width=3, line_dash="dash", line_color="orange") + + +scatter_vline_plot = dx.scatter( + tips, x="TotalBill", y="Tip", unsafe_update_figure=update +) +``` + +### Between Line Fill + +Fill the area between lines in a line plot with `fill="tonexty"`. + +```python +import deephaven.plot.express as dx + +my_table = dx.data.stocks() + +# subset data for just DOG transactions and add upper and lower bounds +dog_prices = my_table.where("Sym = `DOG`").update_view( + ["UpperPrice = Price + 5", "LowerPrice = Price - 5"] +) + + +def update(figure): + # tonexty fills the area between the trace and the previous trace in the list + figure.update_traces( + fill="tonexty", fillcolor="rgba(123,67,0,0.3)", selector={"name": "LowerPrice"} + ) + figure.update_traces( + fill="tonexty", fillcolor="rgba(123,67,0,0.3)", selector={"name": "Price"} + ) + + +# Order matters for y since the fill is between the trace and the previous trace in the list +filled_line_plot = dx.line( + dog_prices, + x="Timestamp", + y=["UpperPrice", "Price", "LowerPrice"], + unsafe_update_figure=update, +) +``` \ No newline at end of file diff --git a/plugins/ui/CHANGELOG.md b/plugins/ui/CHANGELOG.md index 557e10ac6..4ff318bf7 100644 --- a/plugins/ui/CHANGELOG.md +++ b/plugins/ui/CHANGELOG.md @@ -2,6 +2,29 @@ All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines. - - - +## ui-v0.25.0 - 2025-01-15 +#### Bug Fixes +- add menu and menu_trigger to sidebar (#1078) - (49ad632) - dgodinez-dh +- Allow autodoc functions to have no parameters (#1072) - (6b261d6) - Joe +- label prop typing (#1071) - (716b3d9) - Steven Wu +#### Documentation +- use_render_queue, use_liveness_scope, use_table_listener docs (#1044) - (abd691e) - mofojed +- Expand sidebars by default for certain categories, add link to flexbox froggy (#1073) - (e76591d) - Don +- ui.list_view selection_mode (#1070) - (b51373f) - bmingles +- ui as a tree (#1067) - (2e1a725) - dgodinez-dh +- Render Lists (#1061) - (30a1f9f) - dgodinez-dh +- Pure Components (#1064) - (30f3730) - dgodinez-dh +- Using Hooks (#1056) - (28b5a51) - dgodinez-dh +- Component Rules (#1055) - (21e8c5d) - dgodinez-dh +- Update First Component (#1065) - (a6d1aad) - dgodinez-dh +- Conditional Rendering (#1060) - (0ce7634) - dgodinez-dh +#### Features +- ui.menu component (#1076) - (cf23da1) - dgodinez-dh +- ui.logic button (#1050) - (7c83ec2) - ethanalvizo +- add undefined type option (#1026) - (ef7e741) - Steven Wu + +- - - + ## ui-v0.24.0 - 2024-12-12 #### Bug Fixes - UI loading duplicate panels in embed iframe (#1043) - (e1559a4) - Matthew Runyon diff --git a/plugins/ui/docs/components/divider.md b/plugins/ui/docs/components/divider.md new file mode 100644 index 000000000..4e1c39ac6 --- /dev/null +++ b/plugins/ui/docs/components/divider.md @@ -0,0 +1,68 @@ +# Divider + +Dividers enhance layout clarity by grouping and separating nearby content, helping to establish structure and hierarchy. + +## Example + +```python +from deephaven import ui + + +@ui.component +def ui_divider_basic_example(): + return ["Content above", ui.divider(), "Content below"] + + +my_divider_basic_example = ui_divider_basic_example() +``` + +## Orientation + +While aligned horizontally by default, the alignment of the divider can be set using the `orientation` prop. + +```python +from deephaven import ui + + +@ui.component +def ui_divider_orientation_example(): + return ui.flex( + "Content before", + ui.divider(orientation="vertical"), + "Content after", + flex_grow=0, + ) + + +my_ui_divider_orientation_example = ui_divider_orientation_example() +``` + +## Sizing + +The thickness of the divider can be set using the `size` prop. + +```python +from deephaven import ui + + +@ui.component +def ui_divider_size_example(): + return ui.flex( + "Content below", + ui.divider(size="L"), + "Content above", + ui.divider(size="M"), + "More content above", + ui.divider(size="S"), + direction="column", + ) + + +my_divider_size_example = ui_divider_size_example() +``` + +## API reference + +```{eval-rst} +.. dhautofunction:: deephaven.ui.divider +``` diff --git a/plugins/ui/docs/sidebar.json b/plugins/ui/docs/sidebar.json index 972ff60c5..5a15aadd2 100644 --- a/plugins/ui/docs/sidebar.json +++ b/plugins/ui/docs/sidebar.json @@ -139,6 +139,10 @@ "label": "dialog_trigger", "path": "components/dialog_trigger.md" }, + { + "label": "divider", + "path": "components/divider.md" + }, { "label": "flex", "path": "components/flex.md" diff --git a/plugins/ui/setup.cfg b/plugins/ui/setup.cfg index 6af963036..767baf775 100644 --- a/plugins/ui/setup.cfg +++ b/plugins/ui/setup.cfg @@ -3,7 +3,7 @@ name = deephaven-plugin-ui description = deephaven.ui plugin long_description = file: README.md long_description_content_type = text/markdown -version = 0.24.0.dev0 +version = 0.25.0.dev0 url = https://github.com/deephaven/deephaven-plugins project_urls = Source Code = https://github.com/deephaven/deephaven-plugins diff --git a/plugins/ui/src/deephaven/ui/components/__init__.py b/plugins/ui/src/deephaven/ui/components/__init__.py index aa00d512f..7ccdc8901 100644 --- a/plugins/ui/src/deephaven/ui/components/__init__.py +++ b/plugins/ui/src/deephaven/ui/components/__init__.py @@ -23,6 +23,7 @@ from .date_range_picker import date_range_picker from .dialog import dialog from .dialog_trigger import dialog_trigger +from .divider import divider from .flex import flex from .form import form from .fragment import fragment @@ -101,6 +102,7 @@ "date_range_picker", "dialog", "dialog_trigger", + "divider", "flex", "form", "fragment", diff --git a/plugins/ui/src/deephaven/ui/components/divider.py b/plugins/ui/src/deephaven/ui/components/divider.py new file mode 100644 index 000000000..ff13ea186 --- /dev/null +++ b/plugins/ui/src/deephaven/ui/components/divider.py @@ -0,0 +1,152 @@ +from __future__ import annotations + +from .types import ( + Orientation, + AlignSelf, + CSSProperties, + DimensionValue, + JustifySelf, + LayoutFlex, + Position, +) +from ..types import DividerSize +from .basic import component_element +from ..elements import Element + + +def divider( + size: DividerSize | None = "L", + orientation: Orientation = "horizontal", + flex: LayoutFlex | None = None, + flex_grow: float | None = None, + flex_shrink: float | None = None, + flex_basis: DimensionValue | None = None, + align_self: AlignSelf | None = None, + justify_self: JustifySelf | None = None, + order: int | None = None, + grid_area: str | None = None, + grid_row: str | None = None, + grid_row_start: str | None = None, + grid_row_end: str | None = None, + grid_column: str | None = None, + grid_column_start: str | None = None, + grid_column_end: str | None = None, + margin: DimensionValue | None = None, + margin_top: DimensionValue | None = None, + margin_bottom: DimensionValue | None = None, + margin_start: DimensionValue | None = None, + margin_end: DimensionValue | None = None, + margin_x: DimensionValue | None = None, + margin_y: DimensionValue | None = None, + width: DimensionValue | None = None, + height: DimensionValue | None = None, + min_width: DimensionValue | None = None, + min_height: DimensionValue | None = None, + max_width: DimensionValue | None = None, + max_height: DimensionValue | None = None, + position: Position | None = None, + top: DimensionValue | None = None, + bottom: DimensionValue | None = None, + start: DimensionValue | None = None, + end: DimensionValue | None = None, + left: DimensionValue | None = None, + right: DimensionValue | None = None, + z_index: int | None = None, + is_hidden: bool | None = None, + id: str | None = None, + UNSAFE_class_name: str | None = None, + UNSAFE_style: CSSProperties | None = None, +) -> Element: + """ + Dividers bring clarity to a layout by grouping and dividing content in close proximity. + + Args: + size: How thick the Divider should be. + orientation: The axis the Divider should align with. + flex: When used in a flex layout, specifies how the element will grow or shrink to fit the space available. + 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. + flex_basis: When used in a flex layout, specifies the initial main size of the element. + align_self: Overrides the alignItems property of a flex or grid container. + justify_self: Species how the element is justified inside a flex or grid container. + order: The layout order for the element within a flex or grid container. + 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_row: When used in a grid layout, specifies the row 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_row_start: When used in a grid layout, specifies the starting row to span within the grid. + grid_row_end: When used in a grid layout, specifies the ending row to span within the grid. + grid_column_start: When used in a grid layout, specifies the starting column to span within the grid. + grid_column_end: When used in a grid layout, specifies the ending column to span within the grid. + margin: The margin for all four sides of the element. + margin_top: The margin for the top side of the element. + margin_bottom: The margin for the bottom side of the element. + margin_start: The margin for the logical start side of the element, depending on layout direction. + margin_end: The margin for the logical end side of the element, depending on layout direction. + 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. + width: The width of the element. + min_width: The minimum width of the element. + max_width: The maximum width of the element. + height: The height of the element. + min_height: The minimum height of the element. + max_height: The maximum height of the element. + position: The position of the element. + top: The distance from the top of the containing element. + bottom: The distance from the bottom of the containing element. + left: The distance from the left of the containing element. + right: The distance from the right of the containing element. + start: The distance from the start of the containing element, depending on layout direction. + end: The distance from the end of the containing element, depending on layout direction. + z_index: The stack order of the element. + is_hidden: Whether the element is hidden. + 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. + + Returns: + The rendered divider element. + """ + return component_element( + "Divider", + size=size, + orientation=orientation, + flex=flex, + flex_grow=flex_grow, + flex_shrink=flex_shrink, + flex_basis=flex_basis, + align_self=align_self, + justify_self=justify_self, + order=order, + grid_area=grid_area, + grid_row=grid_row, + grid_row_start=grid_row_start, + grid_row_end=grid_row_end, + grid_column=grid_column, + grid_column_start=grid_column_start, + grid_column_end=grid_column_end, + margin=margin, + margin_top=margin_top, + margin_bottom=margin_bottom, + margin_start=margin_start, + margin_end=margin_end, + margin_x=margin_x, + margin_y=margin_y, + width=width, + height=height, + min_width=min_width, + min_height=min_height, + max_width=max_width, + max_height=max_height, + position=position, + top=top, + bottom=bottom, + start=start, + end=end, + left=left, + right=right, + z_index=z_index, + is_hidden=is_hidden, + id=id, + UNSAFE_class_name=UNSAFE_class_name, + UNSAFE_style=UNSAFE_style, + ) diff --git a/plugins/ui/src/deephaven/ui/types/types.py b/plugins/ui/src/deephaven/ui/types/types.py index 314dd5527..21133f291 100644 --- a/plugins/ui/src/deephaven/ui/types/types.py +++ b/plugins/ui/src/deephaven/ui/types/types.py @@ -503,6 +503,7 @@ class SliderChange(TypedDict): ] Granularity = Literal["DAY", "HOUR", "MINUTE", "SECOND"] ListViewDensity = Literal["COMPACT", "NORMAL", "SPACIOUS"] +DividerSize = Literal["S", "M", "L"] ListViewOverflowMode = Literal["truncate", "wrap"] ActionGroupDensity = Literal["compact", "regular"] TabDensity = Literal["compact", "regular"] diff --git a/plugins/ui/src/js/package.json b/plugins/ui/src/js/package.json index f5b8d4ab3..7ad9b090b 100644 --- a/plugins/ui/src/js/package.json +++ b/plugins/ui/src/js/package.json @@ -1,6 +1,6 @@ { "name": "@deephaven/js-plugin-ui", - "version": "0.24.0", + "version": "0.25.0", "description": "Deephaven UI plugin", "keywords": [ "Deephaven", diff --git a/plugins/ui/src/js/src/elements/model/ElementConstants.ts b/plugins/ui/src/js/src/elements/model/ElementConstants.ts index 9816bee92..3aae4d63b 100644 --- a/plugins/ui/src/js/src/elements/model/ElementConstants.ts +++ b/plugins/ui/src/js/src/elements/model/ElementConstants.ts @@ -43,6 +43,7 @@ export const ELEMENT_NAME = { dateRangePicker: uiComponentName('DateRangePicker'), dialog: uiComponentName('Dialog'), dialogTrigger: uiComponentName('DialogTrigger'), + divider: uiComponentName('Divider'), flex: uiComponentName('Flex'), form: uiComponentName('Form'), fragment: uiComponentName('Fragment'), diff --git a/plugins/ui/src/js/src/widget/WidgetUtils.tsx b/plugins/ui/src/js/src/widget/WidgetUtils.tsx index 6583f6195..95cf5bbf1 100644 --- a/plugins/ui/src/js/src/widget/WidgetUtils.tsx +++ b/plugins/ui/src/js/src/widget/WidgetUtils.tsx @@ -13,6 +13,7 @@ import { Content, ContextualHelpTrigger, DialogTrigger, + Divider, Heading, Item, Link, @@ -142,6 +143,7 @@ export const elementComponentMap: Record, unknown> = { [ELEMENT_NAME.dateRangePicker]: DateRangePicker, [ELEMENT_NAME.dialog]: Dialog, [ELEMENT_NAME.dialogTrigger]: DialogTrigger, + [ELEMENT_NAME.divider]: Divider, [ELEMENT_NAME.flex]: Flex, [ELEMENT_NAME.form]: Form, [ELEMENT_NAME.fragment]: React.Fragment, diff --git a/tests/app.d/ui_render_all.py b/tests/app.d/ui_render_all.py index 093ee8f42..36bf2ee3c 100644 --- a/tests/app.d/ui_render_all.py +++ b/tests/app.d/ui_render_all.py @@ -62,6 +62,7 @@ def ui_components1(): label="Date Range Picker", value={"start": "2021-01-01", "end": "2021-01-02"}, ), + ui.flex("Content before", ui.divider(orientation="vertical"), "Content after"), ui.flex("Flex default child A", "Flex default child B"), ui.flex("Flex column child A", "Flex column child B", direction="column"), ui.form("Form"), diff --git a/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-chromium-linux.png b/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-chromium-linux.png index 7150458c7..a80469299 100644 Binary files a/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-chromium-linux.png and b/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-chromium-linux.png differ diff --git a/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-firefox-linux.png b/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-firefox-linux.png index ed838b361..8337ff61c 100644 Binary files a/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-firefox-linux.png and b/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-firefox-linux.png differ diff --git a/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-webkit-linux.png b/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-webkit-linux.png index ba00b3cd4..06c1639df 100644 Binary files a/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-webkit-linux.png and b/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-webkit-linux.png differ