Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into 1949-errors-not-visible
Browse files Browse the repository at this point in the history
  • Loading branch information
mofojed committed May 10, 2024
2 parents 6ff6810 + ff7f769 commit 8e31040
Show file tree
Hide file tree
Showing 14 changed files with 4,101 additions and 13,507 deletions.
17,248 changes: 3,785 additions & 13,463 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"e2e:ui": "playwright test --ui",
"e2e:docker": "DEEPHAVEN_PORT=10001 ./tools/run_docker.sh e2e-tests",
"e2e:update-snapshots": "./tools/run_docker.sh update-snapshots",
"update-dh-packages": "lerna run --concurrency 1 update-dh-packages"
"update-dh-packages": "lerna run --concurrency 1 update-dh-packages",
"update-dh-packages:ui": "npm run update-dh-packages -- --scope=@deephaven/js-plugin-ui --"
},
"devDependencies": {
"@deephaven/babel-preset": "^0.72.0",
Expand Down
2 changes: 2 additions & 0 deletions plugins/ui/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -1247,11 +1247,13 @@ ui.list_view(
*children: Item | Table,
key_column: ColumnName | None = None,
label_column: ColumnName | None = None,
density: Density | None = "COMPACT",
description_column: ColumnName | None = None,
icon_column: ColumnName | None = None,
actions: ListActionGroupElement | ListActionMenuElement | None = None,
default_selected_keys: Selection | None = None,
selected_keys: Selection | None = None,
selection_mode: SelectionMode | None = "MULTIPLE",
render_empty_state: Element | None = None,
on_selection_change: Callable[[Selection], None] | None = None,
on_change: Callable[[Selection], None] | None = None,
Expand Down
86 changes: 86 additions & 0 deletions plugins/ui/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,92 @@ my_picker = ui_picker()

![Use a picker to select from a list of items](_assets/picker.png)

## ListView (string values)
A list view that can be used to create a list of selectable items. Here's a basic example for selecting from a list of string values and displaying the selected key in a text field.

```python
from deephaven import ui


@ui.component
def ui_list_view():
value, set_value = ui.use_state(["Text 2"])

# list_view with text children
lv = ui.list_view(
"Text 1",
"Text 2",
"Text 3",
aria_label="List View - Basic",
on_change=set_value,
selected_keys=value,
)

# list_view with item children
lv2 = ui.list_view(
ui.item("Item 1", key="Text 1"),
ui.item("Item 2", key="Text 2"),
ui.item("Item 3", key="Text 3"),
aria_label="List View - Basic",
on_change=set_value,
selected_keys=value,
)

text = ui.text("Selection: " + ", ".join(map(str, value)), grid_column="span 2")

return text, lv, lv2


lv = ui_list_view()
```

## ListView (table)
```python
from deephaven import time_table, ui
import datetime

# Ticking table with initial row count of 200 that adds a row every second
initial_row_count = 200
column_types = time_table(
"PT1S",
start_time=datetime.datetime.now() - datetime.timedelta(seconds=initial_row_count),
).update(
[
"Id=new Integer(i)",
"Display=new String(`Display `+i)",
]
)


@ui.component
def ui_list_view_table():
value, set_value = ui.use_state([2, 4, 5])

lv = ui.list_view(
column_types,
key_column="Id",
label_column="Display",
aria_label="List View",
on_change=set_value,
selected_keys=value,
)

text = ui.text("Selection: " + ", ".join(map(str, value)))

return ui.flex(
lv,
text,
direction="column",
margin=10,
gap=10,
# necessary to avoid overflowing container height
min_height=0,
)


lv_table = ui_list_view_table()
```

## Form (two variables)

You can have state with multiple different variables in one component. This example creates a [text field](https://react-spectrum.adobe.com/react-spectrum/TextField.html) and a [slider](https://react-spectrum.adobe.com/react-spectrum/Slider.html), and we display the values of both of them.
Expand Down
6 changes: 5 additions & 1 deletion plugins/ui/src/deephaven/ui/components/list_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from .list_action_menu import ListActionMenuElement
from ..elements import BaseElement, Element
from .._internal.utils import create_props
from ..types import ColumnName, Stringable, Selection
from ..types import ColumnName, Density, Stringable, Selection, SelectionMode

ListViewItem = Union[Stringable, ItemElement]
ListViewElement = Element
Expand All @@ -19,11 +19,13 @@ def list_view(
*children: ListViewItem | Table,
key_column: ColumnName | None = None,
label_column: ColumnName | None = None,
density: Density | None = "COMPACT",
description_column: ColumnName | None = None,
icon_column: ColumnName | None = None,
actions: ListActionGroupElement | ListActionMenuElement | None = None,
default_selected_keys: Selection | None = None,
selected_keys: Selection | None = None,
selection_mode: SelectionMode | None = "MULTIPLE",
render_empty_state: Element | None = None,
on_selection_change: Callable[[Selection], None] | None = None,
on_change: Callable[[Selection], None] | None = None,
Expand All @@ -44,6 +46,8 @@ def list_view(
label_column:
Only valid if children are of type Table.
The column of values to display as primary text. Defaults to the key_column value.
density:
Sets the amount of vertical padding within each cell.
description_column:
Only valid if children are of type Table.
The column of values to display as descriptions.
Expand Down
5 changes: 3 additions & 2 deletions plugins/ui/src/deephaven/ui/types/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ class RowDataValue(CellData):
ColumnData = List[Any]
TableData = Dict[ColumnName, ColumnData]
SearchMode = Literal["SHOW", "HIDE", "DEFAULT"]
SelectionMode = Literal["CELL", "ROW", "COLUMN"]
SelectionArea = Literal["CELL", "ROW", "COLUMN"]
SelectionMode = Literal["SINGLE", "MULTIPLE"]
Sentinel = Any
TransformedData = Any
StringSortDirection = Literal["ASC", "DESC"]
Expand Down Expand Up @@ -138,6 +139,6 @@ class RowDataValue(CellData):
ZonedDateTimeConvertible,
]
Granularity = Literal["DAY", "HOUR", "MINUTE", "SECOND"]

Density = Literal["COMPACT", "NORMAL", "SPACIOUS"]
Dependencies = Union[Tuple[Any], List[Any]]
Selection = Sequence[Key]
1 change: 1 addition & 0 deletions plugins/ui/src/js/src/elements/ElementConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export type HTMLElementType =
export const FRAGMENT_ELEMENT_NAME =
`${UI_COMPONENTS_NAMESPACE}.Fragment` as const;
export const ITEM_ELEMENT_NAME = `${UI_COMPONENTS_NAMESPACE}.Item` as const;
export const LIST_VIEW_NAME = `${UI_COMPONENTS_NAMESPACE}.ListView` as const;
export const PICKER_ELEMENT_NAME = `${UI_COMPONENTS_NAMESPACE}.Picker` as const;
export const SECTION_ELEMENT_NAME =
`${UI_COMPONENTS_NAMESPACE}.Section` as const;
40 changes: 40 additions & 0 deletions plugins/ui/src/js/src/elements/ListView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useSelector } from 'react-redux';
import { isElementOfType } from '@deephaven/react-hooks';
import { getSettings, RootState } from '@deephaven/redux';
import { ListView as DHListView } from '@deephaven/components';
import { ListView as DHListViewJSApi } from '@deephaven/jsapi-components';
import { SerializedListViewProps, useListViewProps } from './useListViewProps';
import ObjectView from './ObjectView';
import useReExportedTable from './useReExportedTable';

function ListView(props: SerializedListViewProps): JSX.Element | null {
const settings = useSelector(getSettings<RootState>);
const { children, ...listViewProps } = useListViewProps(props);

const isObjectView = isElementOfType(children, ObjectView);
const table = useReExportedTable(children);

if (isObjectView) {
return (
table && (
<DHListViewJSApi
// eslint-disable-next-line react/jsx-props-no-spreading
{...listViewProps}
table={table}
settings={settings}
/>
)
);
}

return (
<DHListView
// eslint-disable-next-line react/jsx-props-no-spreading
{...listViewProps}
>
{children}
</DHListView>
);
}

export default ListView;
43 changes: 9 additions & 34 deletions plugins/ui/src/js/src/elements/Picker.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,18 @@
import React, { ReactElement } from 'react';
import { useSelector } from 'react-redux';
import {
Picker as DHPicker,
PickerProps as DHPickerProps,
} from '@deephaven/components';
import {
Picker as DHPickerJSApi,
PickerProps as DHPickerJSApiProps,
useTableClose,
} from '@deephaven/jsapi-components';
import { isElementOfType, usePromiseFactory } from '@deephaven/react-hooks';
import { Picker as DHPicker } from '@deephaven/components';
import { Picker as DHPickerJSApi } from '@deephaven/jsapi-components';
import { isElementOfType } from '@deephaven/react-hooks';
import { getSettings, RootState } from '@deephaven/redux';
import { SerializedPickerEventProps, usePickerProps } from './usePickerProps';
import ObjectView, { ObjectViewProps } from './ObjectView';
import { fetchReexportedTable } from './ElementUtils';
import { SerializedPickerProps, usePickerProps } from './usePickerProps';
import ObjectView from './ObjectView';
import useReExportedTable from './useReExportedTable';

type WrappedDHPickerJSApiProps = Omit<DHPickerJSApiProps, 'table'> & {
children: ReactElement<ObjectViewProps>;
};

export type PickerProps = (DHPickerProps | WrappedDHPickerJSApiProps) &
SerializedPickerEventProps;

function Picker({ children, ...props }: PickerProps): JSX.Element {
function Picker(props: SerializedPickerProps): JSX.Element | null {
const settings = useSelector(getSettings<RootState>);
const pickerProps = usePickerProps(props);
const { children, ...pickerProps } = usePickerProps(props);

const isObjectView = isElementOfType(children, ObjectView);

const maybeExportedTable =
isObjectView && children.props.object.type === 'Table'
? children.props.object
: null;

const { data: table } = usePromiseFactory(fetchReexportedTable, [
maybeExportedTable,
]);

useTableClose(table);
const table = useReExportedTable(children);

if (isObjectView) {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { ItemKey, ItemSelection } from '@deephaven/components';
import { useCallback } from 'react';

export type SerializedSelection = 'all' | ItemKey[];

export type SerializedSelectionEventCallback = (
event: SerializedSelection
) => void;

/**
* Selection can be 'all' or a Set of keys. If it is a Set, serialize it as an
* array.
* @param selection Selection to serialize
* @returns Serialized selection
*/
export function serializeSelectionEvent(
selection: ItemSelection
): SerializedSelection {
if (selection instanceof Set) {
return [...selection];
}

return selection;
}

/**
* Get a callback function that can be passed to selection change event handler
* props of Spectrum components.
* @param callback Callback to be called with the serialized selection
* @returns A callback to be passed into the Spectrum component that transforms
* the selection and calls the provided callback
*/
export function useSelectionEventCallback(
callback?: SerializedSelectionEventCallback
): (selection: ItemSelection) => void {
return useCallback(
(selection: ItemSelection) => {
callback?.(serializeSelectionEvent(selection));
},
[callback]
);
}
76 changes: 76 additions & 0 deletions plugins/ui/src/js/src/elements/useListViewProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { ReactElement } from 'react';
import type { SelectionMode } from '@react-types/shared';
import { ListViewProps as DHListViewProps } from '@deephaven/components';
import { ListViewProps as DHListViewJSApiProps } from '@deephaven/jsapi-components';
import { ObjectViewProps } from './ObjectView';
import {
SerializedSelectionEventCallback,
useSelectionEventCallback,
} from './spectrum/useSelectionEventCallback';

type Density = Required<DHListViewProps>['density'];

type WrappedDHListViewJSApiProps = Omit<DHListViewJSApiProps, 'table'> & {
children: ReactElement<ObjectViewProps>;
};

type WrappedDHListViewProps = Omit<
DHListViewProps,
'density' | 'selectionMode'
> & {
// The dh UI spec specifies that density and selectionMode should be uppercase,
// but the Spectrum props are lowercase. We'll accept either to keep things
// more flexible.
density?: Density | Uppercase<Density>;
selectionMode?: SelectionMode | Uppercase<SelectionMode>;
};

export interface SerializedListViewEventProps {
/** Handler that is called when selection changes */
onChange?: SerializedSelectionEventCallback;

/**
* Handler that is called when the selection changes.
* @deprecated Use `onChange` instead
*/
onSelectionChange?: SerializedSelectionEventCallback;
}

export type SerializedListViewProps = (
| WrappedDHListViewProps
| WrappedDHListViewJSApiProps
) &
SerializedListViewEventProps;

/**
* Wrap ListView props with the appropriate serialized event callbacks.
* @param props Props to wrap
* @returns Wrapped props
*/
export function useListViewProps({
density,
selectionMode,
onChange,
onSelectionChange,
...otherProps
}: SerializedListViewProps): DHListViewProps | WrappedDHListViewJSApiProps {
const densityLc = density?.toLowerCase() as Density;
const selectionModeLc = selectionMode?.toLowerCase() as SelectionMode;

const serializedOnChange = useSelectionEventCallback(onChange);
const serializedOnSelectionChange =
useSelectionEventCallback(onSelectionChange);

return {
density: densityLc,
selectionMode: selectionModeLc,
onChange: onChange == null ? undefined : serializedOnChange,
onSelectionChange:
onSelectionChange == null ? undefined : serializedOnSelectionChange,
// The @deephaven/components `ListView` has its own normalization logic that
// handles primitive children types (string, number, boolean). It also
// handles nested children inside of `Item` and `Section` components, so
// we are intentionally not wrapping `otherProps` in `mapSpectrumProps`
...otherProps,
};
}
Loading

0 comments on commit 8e31040

Please sign in to comment.