Skip to content

Commit

Permalink
feat(3D knowledge Graph): selecting entity binding triggers camera mo…
Browse files Browse the repository at this point in the history
…vement
  • Loading branch information
haweston authored and sheilaXu committed Jun 16, 2023
1 parent 98df518 commit f122f1a
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 8 deletions.
44 changes: 39 additions & 5 deletions packages/scene-composer/src/SceneViewer.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jest.doMock('./components/SceneComposerInternal', () => {
});

import * as React from 'react';
import renderer, { act } from 'react-test-renderer';
import { act, create } from 'react-test-renderer';
import { SceneViewer } from './SceneViewer';
import { KnownComponentType } from './interfaces';
import mockComponent from '../__mocks__/mockComponent';
Expand All @@ -40,20 +40,20 @@ describe('SceneViewer', () => {
it('should render correctly', async () => {
let container;
act(() => {
container = renderer.create(<SceneViewer sceneComposerId='123' sceneLoader={mockSceneLoader} />);
container = create(<SceneViewer sceneComposerId='123' sceneLoader={mockSceneLoader} />);
});

expect(container).toMatchSnapshot();
});

it('should call sceneComposerApis to select when selectedDataBinding is available', async () => {
it('should call sceneComposerApis to select when selectedDataBinding is available with component name', async () => {
const mockNodeRef = ['node-123'];
mockSceneComposerApi.findSceneNodeRefBy.mockReturnValueOnce(mockNodeRef);
const mockLabel = { entityId: 'entity', componentName: 'component' };

let container;
act(() => {
container = renderer.create(<SceneViewer sceneLoader={mockSceneLoader} selectedDataBinding={mockLabel} />);
container = create(<SceneViewer sceneLoader={mockSceneLoader} selectedDataBinding={mockLabel} />);
});

// not called before scene is loaded
Expand All @@ -80,13 +80,47 @@ describe('SceneViewer', () => {
expect(mockSceneComposerApi.setCameraTarget).toBeCalledTimes(1);
});

it('should call sceneComposerApis to select when selectedDataBinding is available without component name', async () => {
const mockNodeRef = ['node-123'];
mockSceneComposerApi.findSceneNodeRefBy.mockReturnValueOnce(mockNodeRef);
const mockLabel = { entityId: 'entity' };

let container;
act(() => {
container = create(<SceneViewer sceneLoader={mockSceneLoader} selectedDataBinding={mockLabel} />);
});

// not called before scene is loaded
expect(mockSceneComposerApi.findSceneNodeRefBy).not.toBeCalled();
expect(mockSceneComposerApi.setCameraTarget).not.toBeCalled();

act(() => {
onSceneLoadedCb();
container.update(<SceneViewer sceneLoader={mockSceneLoader} selectedDataBinding={mockLabel} />);
});

// called after scene is loaded
expect(mockSceneComposerApi.findSceneNodeRefBy).toBeCalledTimes(1);
expect(mockSceneComposerApi.findSceneNodeRefBy).toBeCalledWith(mockLabel, [KnownComponentType.DataBinding]);
expect(mockSceneComposerApi.setCameraTarget).toBeCalledTimes(1);
expect(mockSceneComposerApi.setCameraTarget).toBeCalledWith(mockNodeRef[0], 'transition');
expect(mockSceneComposerApi.setSelectedSceneNodeRef).toBeCalledTimes(1);
expect(mockSceneComposerApi.setSelectedSceneNodeRef).toBeCalledWith(mockNodeRef[0]);

// not re-setting camera with same data binding values
container.update(<SceneViewer sceneLoader={mockSceneLoader} selectedDataBinding={mockLabel} />);

expect(mockSceneComposerApi.findSceneNodeRefBy).toBeCalledTimes(1);
expect(mockSceneComposerApi.setCameraTarget).toBeCalledTimes(1);
});

it('should call sceneComposerApis to deselect when selectedDataBinding has no matching', async () => {
mockSceneComposerApi.findSceneNodeRefBy.mockReturnValueOnce(undefined);
const mockLabel = { entityId: 'entity', componentName: 'component' };

let container;
act(() => {
container = renderer.create(<SceneViewer sceneLoader={mockSceneLoader} selectedDataBinding={mockLabel} />);
container = create(<SceneViewer sceneLoader={mockSceneLoader} selectedDataBinding={mockLabel} />);
});

act(() => {
Expand Down
5 changes: 4 additions & 1 deletion packages/scene-composer/src/SceneViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ export const SceneViewer: React.FC<SceneViewerProps> = ({ sceneComposerId, confi
return;
}

const nodeRefs = composerApis.findSceneNodeRefBy(props.selectedDataBinding || '', [KnownComponentType.Tag]);
const filterType = props.selectedDataBinding.componentName
? [KnownComponentType.Tag]
: [KnownComponentType.DataBinding];
const nodeRefs = composerApis.findSceneNodeRefBy(props.selectedDataBinding || '', filterType);
if (nodeRefs && nodeRefs.length > 0) {
// TODO: auto select the first node for now, handle multiple nodes selection later.
composerApis.setCameraTarget(nodeRefs[0], 'transition');
Expand Down
5 changes: 5 additions & 0 deletions packages/scene-composer/src/interfaces/dataBinding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,8 @@ export type IDataBindingConfig = {
fieldMapping: Record<string, string[]>;
template?: IDataBindingTemplate;
};

export interface ISelectedDataBinding {
entityId: string;
componentName?: string;
}
4 changes: 2 additions & 2 deletions packages/scene-composer/src/interfaces/sceneViewer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SceneLoader, TwinMakerSceneMetadataModule } from '@iot-app-kit/source-iottwinmaker';
import { DataStream, TimeQuery, TimeSeriesData, TimeSeriesDataRequest, Viewport } from '@iot-app-kit/core';

import { IDataBindingTemplate } from './dataBinding';
import { IDataBindingTemplate, ISelectedDataBinding } from './dataBinding';
import { SelectionChangedEventCallback, WidgetClickEventCallback } from './components';

export interface DracoDecoderConfig {
Expand Down Expand Up @@ -77,7 +77,7 @@ export interface SceneViewerPropsShared {
* When the selectedDataBinding value is undefined, no action will be taken.
* When there is no matching Tag widget found, the currently selected node will be deselected.
*/
selectedDataBinding?: Record<'entityId' | 'componentName', string>;
selectedDataBinding?: ISelectedDataBinding;

/**
* Sets the camera to view from by Camera name.
Expand Down

0 comments on commit f122f1a

Please sign in to comment.