Skip to content

Commit

Permalink
fix(data overlay): add onWidgetClick and onSelectionChange event supp…
Browse files Browse the repository at this point in the history
…ort to data overlays
  • Loading branch information
haweston committed Aug 8, 2023
1 parent b37b926 commit 33e500d
Show file tree
Hide file tree
Showing 11 changed files with 372 additions and 82 deletions.
66 changes: 21 additions & 45 deletions packages/scene-composer/src/components/StateManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,6 @@ import { ThreeEvent } from '@react-three/fiber';
import ab2str from 'arraybuffer-to-string';
import { combineProviders, DataStream, ProviderWithViewport, TimeSeriesData } from '@iot-app-kit/core';

import useLifecycleLogging from '../logger/react-logger/hooks/useLifecycleLogging';
import {
AdditionalComponentData,
ExternalLibraryConfig,
KnownComponentType,
KnownSceneProperty,
SceneComposerInternalProps,
} from '../interfaces';
import {
setDracoDecoder,
setFeatureConfig,
Expand All @@ -20,24 +12,32 @@ import {
setMetricRecorder,
setTwinMakerSceneMetadataModule,
} from '../common/GlobalSettings';
import { useSceneComposerId } from '../common/sceneComposerIdContext';
import { IAnchorComponentInternal, ICameraComponentInternal, RootState, useStore, useViewOptionState } from '../store';
import { createStandardUriModifier } from '../utils/uriModifiers';
import sceneDocumentSnapshotCreator from '../utils/sceneDocumentSnapshotCreator';
import { SceneLayout } from '../layouts/SceneLayout';
import { findComponentByType } from '../utils/nodeUtils';
import { applyDataBindingTemplate } from '../utils/dataBindingTemplateUtils';
import { combineTimeSeriesData, convertDataStreamsToDataInput } from '../utils/dataStreamUtils';
import useActiveCamera from '../hooks/useActiveCamera';
import useMatterportViewer from '../hooks/useMatterportViewer';
import { getCameraSettings } from '../utils/cameraUtils';
import {
MATTERPORT_ACCESS_TOKEN,
MATTERPORT_APPLICATION_KEY,
MATTERPORT_ERROR,
MATTERPORT_SECRET_ARN,
} from '../common/constants';
import { DisplayMessageCategory, IEntityBindingComponentInternal } from '../store/internalInterfaces';
import { DisplayMessageCategory } from '../store/internalInterfaces';
import { useSceneComposerId } from '../common/sceneComposerIdContext';
import useActiveCamera from '../hooks/useActiveCamera';
import useMatterportViewer from '../hooks/useMatterportViewer';
import {
AdditionalComponentData,
ExternalLibraryConfig,
KnownComponentType,
KnownSceneProperty,
SceneComposerInternalProps,
} from '../interfaces';
import { SceneLayout } from '../layouts/SceneLayout';
import useLifecycleLogging from '../logger/react-logger/hooks/useLifecycleLogging';
import { ICameraComponentInternal, RootState, useStore, useViewOptionState } from '../store';
import { getCameraSettings } from '../utils/cameraUtils';
import { getAdditionalComponentData } from '../utils/eventDataUtils';
import { combineTimeSeriesData, convertDataStreamsToDataInput } from '../utils/dataStreamUtils';
import { findComponentByType } from '../utils/nodeUtils';
import sceneDocumentSnapshotCreator from '../utils/sceneDocumentSnapshotCreator';
import { createStandardUriModifier } from '../utils/uriModifiers';

import IntlProvider from './IntlProvider';
import { LoadingProgress } from './three-fiber/LoadingProgress';
Expand Down Expand Up @@ -150,32 +150,8 @@ const StateManager: React.FC<SceneComposerInternalProps> = ({
if (onSelectionChanged) {
const node = getSceneNodeByRef(selectedSceneNodeRef);
const componentTypes = node?.components.map((component) => component.type) ?? [];
const additionalComponentData: AdditionalComponentData[] = getAdditionalComponentData(node, dataBindingTemplate);

const tagComponent = findComponentByType(node, KnownComponentType.Tag) as IAnchorComponentInternal;
const entityBindingComponent = findComponentByType(
node,
KnownComponentType.EntityBinding,
) as IEntityBindingComponentInternal;
const additionalComponentData: AdditionalComponentData[] = [];
if (tagComponent) {
additionalComponentData.push({
chosenColor: tagComponent.chosenColor,
navLink: tagComponent.navLink,
dataBindingContext: !tagComponent.valueDataBinding?.dataBindingContext
? undefined
: applyDataBindingTemplate(tagComponent.valueDataBinding, dataBindingTemplate),
});
}
// Add entityID info part of additional component data
// We assumed IDataBindingMap will have only one mapping as data binding
// will always have only one entity data.
if (entityBindingComponent) {
additionalComponentData.push({
dataBindingContext: !entityBindingComponent?.valueDataBinding?.dataBindingContext
? undefined
: entityBindingComponent?.valueDataBinding.dataBindingContext,
});
}
onSelectionChanged({
componentTypes,
nodeRef: selectedSceneNodeRef,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ describe('AddComponentMenu', () => {
],
});
render(<AddComponentMenu />);
const addButton = screen.getByTestId('add-component-data-binding');
const addButton = screen.getByTestId('add-component-entity-binding');

act(() => {
fireEvent.pointerUp(addButton);
Expand All @@ -105,7 +105,7 @@ describe('AddComponentMenu', () => {
valueDataBinding: { dataBindingContext: '' },
});
expect(mockMetricRecorder.recordClick).toBeCalledTimes(1);
expect(mockMetricRecorder.recordClick).toBeCalledWith('add-component-data-binding');
expect(mockMetricRecorder.recordClick).toBeCalledWith('add-component-entity-binding');
});

it('should add no addition binding to data binding component when clicked', () => {
Expand All @@ -118,11 +118,11 @@ describe('AddComponentMenu', () => {
],
});
render(<AddComponentMenu />);
expect(screen.getByTestId('add-component-data-binding')).not.toBeNull();
screen.getByTestId('add-component-data-binding').click();
expect(screen.getByTestId('add-component-entity-binding')).not.toBeNull();
screen.getByTestId('add-component-entity-binding').click();
fireEvent.mouseOver(screen.getByTestId('add-component'));
expect(addComponentInternal).not.toBeCalled();
expect(mockMetricRecorder.recordClick).not.toBeCalledWith('add-component-data-binding');
expect(mockMetricRecorder.recordClick).not.toBeCalledWith('add-component-entity-binding');
});

it('should not see add data binding item when feature is not enabled', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface AddComponentMenuProps {
enum ObjectTypes {
Component = 'add-component',
Overlay = 'add-component-overlay',
DataBinding = 'add-component-data-binding',
EntityBinding = 'add-component-entity-binding',
}

type AddComponentMenuItem = ToolbarItemOptions & {
Expand All @@ -31,12 +31,12 @@ type AddComponentMenuItem = ToolbarItemOptions & {
const labelStrings: { [key in ObjectTypes]: MessageDescriptor } = defineMessages({
[ObjectTypes.Component]: { defaultMessage: 'Add component', description: 'Menu Item label' },
[ObjectTypes.Overlay]: { defaultMessage: 'Overlay', description: 'Menu Item label' },
[ObjectTypes.DataBinding]: { defaultMessage: 'Add entity binding', description: 'Menu Item label' },
[ObjectTypes.EntityBinding]: { defaultMessage: 'Add entity binding', description: 'Menu Item label' },
});

const textStrings = defineMessages({
[ObjectTypes.Overlay]: { defaultMessage: 'Add overlay', description: 'Menu Item' },
[ObjectTypes.DataBinding]: { defaultMessage: 'Add entity binding', description: 'Menu Item' },
[ObjectTypes.EntityBinding]: { defaultMessage: 'Add entity binding', description: 'Menu Item' },
});

export const AddComponentMenu: React.FC<AddComponentMenuProps> = ({ onSelect }) => {
Expand Down Expand Up @@ -76,10 +76,10 @@ export const AddComponentMenu: React.FC<AddComponentMenuProps> = ({ onSelect })
},
]
: [];
const addDataBindingItem = entityBindingComponentEnabled
const addEntityBindingItem = entityBindingComponentEnabled
? [
{
uuid: ObjectTypes.DataBinding,
uuid: ObjectTypes.EntityBinding,
isDisabled: isEntityBindingComponent,
},
]
Expand All @@ -91,7 +91,7 @@ export const AddComponentMenu: React.FC<AddComponentMenuProps> = ({ onSelect })
uuid: ObjectTypes.Component,
},
...addOverlayItem,
...addDataBindingItem,
...addEntityBindingItem,
].map(mapToMenuItem);
}, [selectedSceneNodeRef, selectedSceneNode, isOverlayComponent, isTagComponent, entityBindingComponentEnabled]);

Expand All @@ -114,7 +114,7 @@ export const AddComponentMenu: React.FC<AddComponentMenuProps> = ({ onSelect })
addComponentInternal(selectedSceneNodeRef, component);
}, [selectedSceneNodeRef, selectedSceneNode]);

const handleAddDataBinding = useCallback(() => {
const handleAddEntityBinding = useCallback(() => {
if (!selectedSceneNodeRef) return;

const entityBindingComponent = findComponentByType(selectedSceneNode, KnownComponentType.EntityBinding);
Expand Down Expand Up @@ -142,8 +142,8 @@ export const AddComponentMenu: React.FC<AddComponentMenuProps> = ({ onSelect })
type='action-select'
onClick={({ uuid }) => {
switch (uuid) {
case ObjectTypes.DataBinding:
handleAddDataBinding();
case ObjectTypes.EntityBinding:
handleAddEntityBinding();
break;
case ObjectTypes.Overlay:
handleAddOverlay();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { KnownComponentType } from '../../interfaces';
import { ToolbarItem } from '../toolbars/common/ToolbarItem';
import { getGlobalSettings } from '../../common/GlobalSettings';
import { Component } from '../../models/SceneModels';
import { IEntityBindingComponentInternal, ISceneComponentInternal } from '../../store/internalInterfaces';
import { ISceneComponentInternal } from '../../store/internalInterfaces';
import { generateUUID } from '../../utils/mathUtils';

interface ComponentEditMenuProps {
Expand Down Expand Up @@ -128,7 +128,7 @@ export const ComponentEditMenu: React.FC<ComponentEditMenuProps> = ({ nodeRef, c
}
}, [nodeRef, currentComponent]);

const handleRemoveAllDataBinding = useCallback(() => {
const handleRemoveEntityBinding = useCallback(() => {
if (currentComponent.type == KnownComponentType.EntityBinding) {
removeComponent(nodeRef, currentComponent.ref);
return;
Expand All @@ -149,7 +149,7 @@ export const ComponentEditMenu: React.FC<ComponentEditMenuProps> = ({ nodeRef, c
handleAddDataBinding();
break;
case ObjectTypes.RemoveEntityBinding:
handleRemoveAllDataBinding();
handleRemoveEntityBinding();
break;
case ObjectTypes.RemoveOverlay:
removeComponent(nodeRef, currentComponent.ref);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { ReactElement, useContext, useRef } from 'react';
import { Html } from '@react-three/drei';
import { useFrame } from '@react-three/fiber';
import { Object3D } from 'three';
import { Group } from 'three';

import { ISceneNodeInternal } from '../../../store';
import { sceneComposerIdContext } from '../../../common/sceneComposerIdContext';
Expand All @@ -18,7 +18,7 @@ export interface DataOverlayComponentProps {
export const DataOverlayComponent = ({ node, component }: DataOverlayComponentProps): ReactElement => {
const sceneComposerId = useContext(sceneComposerIdContext);
const htmlRef = useRef<HTMLDivElement>(null);
const groupRef = useRef<Object3D>();
const groupRef = useRef<Group>();

const getOffsetFromTag = () => {
const tagComponent: IAnchorComponentInternal | undefined = node.components.find(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Component } from '../../../models/SceneModels';
import { useStore, useViewOptionState } from '../../../store';
import { sceneComposerIdContext } from '../../../common/sceneComposerIdContext';
import useCallbackWhenNotPanning from '../../../hooks/useCallbackWhenNotPanning';
import { applyDataBindingTemplate } from '../../../utils/dataBindingTemplateUtils';

import { DataOverlayRows } from './DataOverlayRows';
import {
Expand Down Expand Up @@ -35,6 +36,10 @@ export const DataOverlayContainer = ({ component, node }: DataOverlayContainerPr
const componentVisible = useViewOptionState(sceneComposerId).componentVisibilities[subType];
const initialVisibilitySkipped = useRef(false);

const onWidgetClick = useStore(sceneComposerId)((state) => state.getEditorConfig().onWidgetClick);
const isViewing = useStore(sceneComposerId)((state) => state.isViewing());
const dataBindingTemplate = useStore(sceneComposerId)((state) => state.dataBindingTemplate);

// TODO: config.isPinned is not supported in milestone 1
// const [visible, setVisible] = useState(component.config?.isPinned || componentVisible);
const [visible, setVisible] = useState(componentVisible);
Expand All @@ -58,12 +63,34 @@ export const DataOverlayContainer = ({ component, node }: DataOverlayContainerPr
// Same behavior as other components to select node when clicked on the panel
const [onPointerDown, onPointerUp] = useCallbackWhenNotPanning(
(e) => {
// Anchor only has special onClick handling in viewing mode
if (isViewing) {
if (onWidgetClick) {
const dataBindingContexts: unknown[] = [];
component.valueDataBindings.forEach((item) => {
if (item.valueDataBinding) {
dataBindingContexts.push(applyDataBindingTemplate(item.valueDataBinding, dataBindingTemplate));
}
});
const componentTypes = node.components.map((component) => component.type) ?? [];
onWidgetClick({
componentTypes,
nodeRef: node.ref,
additionalComponentData: [
{
dataBindingContexts,
},
],
});
}
}

e.stopPropagation();
if (selectedSceneNodeRef !== node.ref) {
setSelectedSceneNodeRef(node.ref);
}
},
[selectedSceneNodeRef, node.ref],
[selectedSceneNodeRef, node.ref, onWidgetClick],
);

const onClickCloseButton = useCallback(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import React from 'react';
import { render } from '@testing-library/react';
import { BoxGeometry, Group, Mesh } from 'three';
import { Canvas } from '@react-three/fiber';
import ReactThreeTestRenderer from '@react-three/test-renderer';

import { DataOverlayComponent } from '../DataOverlayComponent';
import { Component } from '../../../../models/SceneModels';
Expand Down
7 changes: 6 additions & 1 deletion packages/scene-composer/src/interfaces/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,15 @@ export interface ITagData {
export interface IEntityBindingInfo {
dataBindingContext?: unknown;
}

export interface IDataOverlayInfo {
dataBindingContexts?: unknown[];
}

/**
* Type that can be represented by different additional component data types such as ITagData | IFutureComponentData
*/
export type AdditionalComponentData = ITagData | IEntityBindingInfo;
export type AdditionalComponentData = ITagData | IEntityBindingInfo | IDataOverlayInfo;

/**
* Callback signature for selection of with Widgets.
Expand Down
Loading

0 comments on commit 33e500d

Please sign in to comment.