Skip to content

feat: DH-19000: Persist deephaven UI table client-side state#1152

Merged
mattrunyon merged 8 commits into
deephaven:mainfrom
mattrunyon:mattrunyon_dh-19000
May 20, 2025
Merged

feat: DH-19000: Persist deephaven UI table client-side state#1152
mattrunyon merged 8 commits into
deephaven:mainfrom
mattrunyon:mattrunyon_dh-19000

Conversation

@mattrunyon

@mattrunyon mattrunyon commented Mar 27, 2025

Copy link
Copy Markdown
Collaborator

Needs to run with un-released web-client-ui.

Simple example that uses both regular and ui.table to show persistence.

Note that there is an issue with not many props in IrisGrid responding to changes, so the merging of client and server state is a best guess for what we'll want when it does respond to prop changes on those other props (sort, filters, etc.)

from deephaven import ui
import deephaven.plot.express as dx

_stocks = dx.data.stocks(False)


@ui.component
def my_comp():
    key, set_key = ui.use_state(0)
    return [
        ui.fragment(_stocks, key="base" + str(key)),
        ui.button("Reset", on_press=lambda: set_key(key + 1)),
        ui.table(_stocks, key="table" + str(key)),
    ]


tables = my_comp()

Should probably add

  • Ability to reload panel and clear initial state if there is a problem re-hydrating like migration issues. Right now it just throws and you cannot recover other than closing and re-opening the tab.

@github-actions

Copy link
Copy Markdown

ui docs preview (Available for 14 days)

@mattrunyon mattrunyon changed the title DH-19000: Persist deephaven UI table client-side state feat: DH-19000: Persist deephaven UI table client-side state Mar 31, 2025
@github-actions

Copy link
Copy Markdown

ui docs preview (Available for 14 days)

@mattrunyon mattrunyon force-pushed the mattrunyon_dh-19000 branch from 7e6d8be to 45cf06a Compare May 1, 2025 20:22
@mattrunyon mattrunyon requested review from Copilot and mofojed and removed request for mofojed May 1, 2025 20:23
@mattrunyon mattrunyon self-assigned this May 1, 2025

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds client-side persistence for the Deephaven UI table and widget panel state.

  • Introduces a new optional property (panelStates) in the widget data type.
  • Adds onDataChange and getInitialData callbacks to the React panel manager components, updating their interfaces and usage in DocumentHandler, ReactPanel, and associated tests.
  • Integrates persistent state support in the ReactPanel and UITable components.

Reviewed Changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
plugins/ui/src/js/src/widget/WidgetTypes.ts Added an optional panelStates property to support persisting panel state
plugins/ui/src/js/src/widget/DocumentHandler.tsx Added onDataChange and getInitialData callbacks to propagate panel state changes
plugins/ui/src/js/src/layout/ReactPanelManager.ts Updated ReactPanelManager and ReactPanelControl interfaces to include state persistence methods
plugins/ui/src/js/src/layout/ReactPanel.tsx Wrapped children in PersistentStateProvider for persisted state updates
plugins/ui/src/js/src/layout/ReactPanel.test.tsx Updated tests to include mocks for the new persistence callbacks
plugins/ui/src/js/src/elements/UITable/UITable.tsx Integrated client-side state persistence by using usePersistentState and merging server and hydrated client state
Files not reviewed (1)
  • plugins/ui/src/js/package.json: Language not supported

* In the future we may want to do a smarter merge of these.
*/
const mergedIrisGridProps = useMemo(() => {
if (initialIrisGridServerProps.current === irisGridServerProps) {

Copilot AI May 1, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing initialIrisGridServerProps.current with irisGridServerProps using reference equality may be unreliable if the server state object is recreated on each render. Consider using a deep comparison utility or ensuring that the server props maintain stable references so that rehydration logic behaves as intended.

Copilot uses AI. Check for mistakes.
@mattrunyon mattrunyon marked this pull request as ready for review May 1, 2025 20:26
@github-actions

github-actions Bot commented May 1, 2025

Copy link
Copy Markdown

ui docs preview (Available for 14 days)

1 similar comment
@github-actions

github-actions Bot commented May 1, 2025

Copy link
Copy Markdown

ui docs preview (Available for 14 days)

@github-actions

github-actions Bot commented May 2, 2025

Copy link
Copy Markdown

ui docs preview (Available for 14 days)

@mofojed mofojed left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update the client packages

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Published new web-client-ui packages so you should be able to use this now

@mattrunyon mattrunyon requested a review from mofojed May 12, 2025 20:27
@github-actions

Copy link
Copy Markdown

ui docs preview (Available for 14 days)

@mattrunyon

mattrunyon commented May 12, 2025

Copy link
Copy Markdown
Collaborator Author

This needs a nightly build of DHC with the latest web-client-ui for e2e tests to work because of the new externalized imports

mofojed
mofojed previously approved these changes May 13, 2025

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces client-side state persistence for Deephaven UI tables by integrating persistent state handling into panel management and table components.

  • Adds a new panelStates field to widget data and updates the panel manager interface with onDataChange and getInitialData methods.
  • Integrates a PersistentStateProvider in ReactPanel and updates UITable to merge client and server state for rehydration.

Reviewed Changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
plugins/ui/src/js/src/widget/WidgetTypes.ts Added panelStates support to WidgetData.
plugins/ui/src/js/src/widget/DocumentHandler.tsx Introduced onDataChange handler for state persistence.
plugins/ui/src/js/src/layout/ReactPanelManager.ts Updated interface and hooks to include data persistence functions.
plugins/ui/src/js/src/layout/ReactPanelErrorBoundary.tsx Enhanced error handling to provide a reset action via onReset.
plugins/ui/src/js/src/layout/ReactPanel.tsx Integrated PersistentStateProvider and error reset logic for rehydration.
plugins/ui/src/js/src/layout/ReactPanel.test.tsx Updated tests to accommodate new data persistence behaviors.
plugins/ui/src/js/src/elements/UITable/UITable.tsx Merged persistent client state with server state for the table component.
plugins/ui/src/js/package.json Updated dependencies to newer versions.

* Otherwise, we have received changes from the server and we should use those over client state.
* In the future we may want to do a smarter merge of these.
*/
const mergedIrisGridProps = useMemo(() => {

Copilot AI May 14, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The conditional merge using reference equality to compare irisGridServerProps may be fragile. Consider using a more robust state comparison (or an explicit rehydration flag) to ensure the correct precedence of client vs server state.

Copilot uses AI. Check for mistakes.
Comment on lines +105 to +108
const onErrorReset = useCallback(() => {
// Not EMPTY_ARRAY, because we always want to trigger a re-render
// in case a panel is reloaded and errors again
setInitialData([]);

Copilot AI May 14, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Using a literal empty array to reset state may be less clear in intent. Consider extracting this value to a named constant or adding an inline comment to clarify that an empty array forces a re-render for error recovery.

Suggested change
const onErrorReset = useCallback(() => {
// Not EMPTY_ARRAY, because we always want to trigger a re-render
// in case a panel is reloaded and errors again
setInitialData([]);
const EMPTY_ARRAY: never[] = []; // Used to reset state and trigger a re-render
const onErrorReset = useCallback(() => {
// Always use EMPTY_ARRAY to ensure a re-render for error recovery
setInitialData(EMPTY_ARRAY);

Copilot uses AI. Check for mistakes.
@mattrunyon

Copy link
Copy Markdown
Collaborator Author

Added an onReset to the ReactPanelErrorBoundary so we can trigger a reset from any child error

@github-actions

Copy link
Copy Markdown

ui docs preview (Available for 14 days)

@github-actions

Copy link
Copy Markdown

ui docs preview (Available for 14 days)

log.error('Error caught by ErrorBoundary', error, errorInfo);
}

getError(): WidgetError | undefined {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably memoize this.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can, although it probably won't provide much benefit. The object is cheap to create and this component should basically never re-render until the action button is clicked

@mofojed mofojed left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Memoize

@mattrunyon mattrunyon requested a review from mofojed May 19, 2025 18:48
@mattrunyon

Copy link
Copy Markdown
Collaborator Author

Added memoization (although I don't think it's really necessary for this case)

@github-actions

Copy link
Copy Markdown

ui docs preview (Available for 14 days)

@mattrunyon mattrunyon merged commit e1a9971 into deephaven:main May 20, 2025
16 checks passed
@mattrunyon mattrunyon deleted the mattrunyon_dh-19000 branch May 20, 2025 19:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants