Skip to content

Commit f31ae5e

Browse files
committed
Update and add tests
1 parent 10e4036 commit f31ae5e

8 files changed

Lines changed: 249 additions & 70 deletions

File tree

plugins/ui/src/js/src/elements/ObjectView.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
import React, { useCallback, useMemo, useState } from 'react';
1+
import React, { useCallback, useMemo } from 'react';
22
import Log from '@deephaven/log';
33
import { isWidgetPlugin, usePlugins } from '@deephaven/plugin';
44
import type { dh } from '@deephaven/jsapi-types';
5-
import { nanoid } from 'nanoid';
65

76
const log = Log.module('@deephaven/js-plugin-ui/ObjectView');
87

9-
export type ObjectViewProps = { object: dh.WidgetExportedObject };
8+
export type ObjectViewProps = {
9+
object: dh.WidgetExportedObject;
10+
__dhId?: string;
11+
};
12+
1013
function ObjectView(props: ObjectViewProps): JSX.Element {
11-
const { object } = props;
14+
const { object, __dhId } = props;
1215
log.info('Object is', object);
1316
const { type } = object;
14-
const [__dhId] = useState(() => nanoid());
1517

1618
const fetch = useCallback(async () => {
1719
// We re-export the object in case this object is used in multiple places or closed/opened multiple times

plugins/ui/src/js/src/elements/UITable/UITable.tsx

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
useTheme,
2828
viewStyleProps,
2929
} from '@deephaven/components';
30-
import { useGlobalFilters } from '@deephaven/dashboard-core-plugins';
30+
import { useDashboardColumnFilters } from '@deephaven/dashboard-core-plugins';
3131
import { useApi } from '@deephaven/jsapi-bootstrap';
3232
import type { dh as DhType } from '@deephaven/jsapi-types';
3333
import Log from '@deephaven/log';
@@ -48,7 +48,6 @@ import UITableContextMenuHandler, {
4848
} from './UITableContextMenuHandler';
4949
import UITableModel, { makeUiTableModel } from './UITableModel';
5050
import { UITableLayoutHints } from './JsTableProxy';
51-
import { nanoid } from 'nanoid';
5251

5352
const log = Log.module('@deephaven/js-plugin-ui/UITable');
5453

@@ -148,7 +147,7 @@ function useUITableModel({
148147
return model;
149148
}
150149

151-
export function UITableInner({
150+
export function UITable({
152151
format_: formatProp = EMPTY_ARRAY as unknown as FormattingRule[],
153152
onCellPress,
154153
onCellDoublePress,
@@ -529,7 +528,7 @@ export function UITableInner({
529528
};
530529
}, [irisGridServerProps, initialHydratedState]);
531530

532-
const inputFilters = useGlobalFilters(model?.columns ?? EMPTY_ARRAY);
531+
const inputFilters = useDashboardColumnFilters(model?.columns ?? EMPTY_ARRAY);
533532

534533
return model ? (
535534
<div
@@ -551,9 +550,4 @@ export function UITableInner({
551550

552551
UITable.displayName = 'TableElementView';
553552

554-
export function UITable(props: UITableProps): JSX.Element {
555-
const [__dhId] = useState(() => nanoid());
556-
return <UITableInner {...props} __dhId={__dhId} />;
557-
}
558-
559553
export default UITable;

plugins/ui/src/js/src/elements/utils/ElementUtils.test.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,32 +68,41 @@ describe('wrapElementChildren', () => {
6868

6969
it.each([
7070
[
71+
'single',
7172
mock.exportedA1,
72-
<ObjectView key={`${mock.exportedA1.type}-0`} object={mock.exportedA1} />,
73+
<ObjectView
74+
key={`${mock.exportedA1.type}-0`}
75+
object={mock.exportedA1}
76+
__dhId={`test-root/${mock.exportedA1.type}-0`}
77+
/>,
7378
],
7479
[
80+
'multiple',
7581
[mock.exportedA1, mock.exportedA2, mock.exportedB1],
7682
[
7783
<ObjectView
7884
key={`${mock.exportedA1.type}-0`}
7985
object={mock.exportedA1}
86+
__dhId={`test-root/${mock.exportedA1.type}-0`}
8087
/>,
8188
<ObjectView
8289
key={`${mock.exportedA1.type}-1`}
8390
object={mock.exportedA1}
91+
__dhId={`test-root/${mock.exportedA1.type}-1`}
8492
/>,
8593
<ObjectView
8694
key={`${mock.exportedB1.type}-0`}
8795
object={mock.exportedB1}
96+
__dhId={`test-root/${mock.exportedB1.type}-0`}
8897
/>,
8998
],
9099
],
91100
])(
92-
'should wrap exported object children in ObjectView: %s, %s',
93-
(children, expectedChildren) => {
101+
'should wrap exported object children in ObjectView: %s',
102+
(testName, children, expectedChildren) => {
94103
const actual = wrapElementChildren({
95104
[ELEMENT_KEY]: 'mock.element',
96-
props: { children },
105+
props: { children, __dhId: 'test-root' },
97106
});
98107

99108
expect(actual.props?.children).toEqual(expectedChildren);
@@ -119,7 +128,7 @@ describe('wrapElementChildren', () => {
119128
],
120129
],
121130
])(
122-
'should wrap primitive item element children in Text elements: %s, %s',
131+
'should wrap primitive item element children in Text elements: %s',
123132
(children, expectedChildren) => {
124133
const givenProps: Record<string, unknown> = { children };
125134
if (textValue != null) {

plugins/ui/src/js/src/elements/utils/ElementUtils.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,15 @@ export function wrapElementChildren(element: ElementNode): ElementNode {
191191
const wrappedChildren = children.map(child => {
192192
// Exported objects need to be converted to `ObjectView` to be rendered
193193
if (isExportedObject(child)) {
194-
return <ObjectView key={getChildKey(child.type)} object={child} />;
194+
const key = getChildKey(child.type);
195+
return (
196+
<ObjectView
197+
key={key}
198+
object={child}
199+
// eslint-disable-next-line no-underscore-dangle
200+
__dhId={`${element.props?.__dhId}/${key}`}
201+
/>
202+
);
195203
}
196204

197205
// Auto wrap primitive children of `Item` elements in `Text` elements

plugins/ui/src/js/src/widget/DashboardWidgetHandler.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ function DashboardWidgetHandler({
4141

4242
return (
4343
<WidgetHandler
44+
id={id}
4445
onDataChange={handleDataChange}
4546
onClose={handleClose}
4647
// eslint-disable-next-line react/jsx-props-no-spreading

plugins/ui/src/js/src/widget/WidgetHandler.tsx

Lines changed: 53 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ export interface WidgetHandlerProps {
5858
/** Widget for this to handle */
5959
widgetDescriptor: WidgetDescriptor;
6060

61+
/** Widget ID maintained by the DashboardPlugin */
62+
id: string;
63+
6164
/** Widget data to display */
6265
initialData?: ReadonlyWidgetData;
6366

@@ -73,6 +76,7 @@ function WidgetHandler({
7376
onDataChange = EMPTY_FUNCTION,
7477
widgetDescriptor,
7578
initialData: initialDataProp,
79+
id,
7680
}: WidgetHandlerProps): JSX.Element | null {
7781
const { widget, error: widgetError } = useWidget(widgetDescriptor);
7882
const [isLoading, setIsLoading] = useState(true);
@@ -208,49 +212,53 @@ function WidgetHandler({
208212
// We close those objects that are no longer referenced, as they will never be referenced again.
209213
const deadObjectMap = new Map(exportedObjectMap.current);
210214
const deadCallableMap = new Map(renderedCallableMap.current);
211-
const hydratedDocument = transformNode(doc, (key, value) => {
212-
// Need to re-hydrate any objects that are defined
213-
if (isCallableNode(value)) {
214-
const callableId = value[CALLABLE_KEY];
215-
deadCallableMap.delete(callableId);
216-
if (renderedCallableMap.current.has(callableId)) {
217-
log.debug2('Reusing callableId', callableId);
218-
return renderedCallableMap.current.get(callableId);
215+
const hydratedDocument = transformNode(
216+
doc,
217+
(key, value) => {
218+
// Need to re-hydrate any objects that are defined
219+
if (isCallableNode(value)) {
220+
const callableId = value[CALLABLE_KEY];
221+
deadCallableMap.delete(callableId);
222+
if (renderedCallableMap.current.has(callableId)) {
223+
log.debug2('Reusing callableId', callableId);
224+
return renderedCallableMap.current.get(callableId);
225+
}
226+
log.debug2('Registering callableId', callableId);
227+
const callable = wrapCallable(
228+
jsonClient,
229+
callableId,
230+
callableFinalizationRegistry,
231+
false
232+
);
233+
renderedCallableMap.current.set(callableId, callable);
234+
return callable;
219235
}
220-
log.debug2('Registering callableId', callableId);
221-
const callable = wrapCallable(
222-
jsonClient,
223-
callableId,
224-
callableFinalizationRegistry,
225-
false
226-
);
227-
renderedCallableMap.current.set(callableId, callable);
228-
return callable;
229-
}
230-
if (isObjectNode(value)) {
231-
// Replace this node with the exported object
232-
const objectKey = value[OBJECT_KEY];
233-
const exportedObject = exportedObjectMap.current.get(objectKey);
234-
if (exportedObject === undefined) {
235-
// The map should always have the exported object for a key, otherwise the protocol is broken
236-
throw new Error(`Invalid exported object key ${objectKey}`);
236+
if (isObjectNode(value)) {
237+
// Replace this node with the exported object
238+
const objectKey = value[OBJECT_KEY];
239+
const exportedObject = exportedObjectMap.current.get(objectKey);
240+
if (exportedObject === undefined) {
241+
// The map should always have the exported object for a key, otherwise the protocol is broken
242+
throw new Error(`Invalid exported object key ${objectKey}`);
243+
}
244+
deadObjectMap.delete(objectKey);
245+
return exportedObject;
237246
}
238-
deadObjectMap.delete(objectKey);
239-
return exportedObject;
240-
}
241247

242-
if (isElementNode(value)) {
243-
// Replace the elements node with the Component it maps to
244-
try {
245-
return getComponentForElement(value);
246-
} catch (e) {
247-
log.warn('Error getting component for element', e);
248-
return value;
248+
if (isElementNode(value)) {
249+
// Replace the elements node with the Component it maps to
250+
try {
251+
return getComponentForElement(value);
252+
} catch (e) {
253+
log.warn('Error getting component for element', e);
254+
return value;
255+
}
249256
}
250-
}
251257

252-
return value;
253-
});
258+
return value;
259+
},
260+
id
261+
);
254262

255263
// Close any objects that are no longer referenced
256264
deadObjectMap.forEach((deadObject, objectKey) => {
@@ -275,7 +283,13 @@ function WidgetHandler({
275283
);
276284
return hydratedDocument;
277285
},
278-
[callableFinalizationRegistry, document, jsonClient, renderEmptyDocument]
286+
[
287+
callableFinalizationRegistry,
288+
document,
289+
jsonClient,
290+
renderEmptyDocument,
291+
id,
292+
]
279293
);
280294

281295
const updateExportedObjects = useCallback(

0 commit comments

Comments
 (0)