forked from cdonke/BotFramework-Composer
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Added UX to link log items and WC activities (microsoft#8006)
* Added UX to link log items and WC activities * Updated selected visual state of WC activities * Added some tests * Factored out WebChat hooks into separate headless component Co-authored-by: Andy Brown <[email protected]>
- Loading branch information
1 parent
87d1d33
commit c859da0
Showing
8 changed files
with
229 additions
and
4 deletions.
There are no files selected for viewing
47 changes: 47 additions & 0 deletions
47
Composer/packages/client/src/components/WebChat/ActivityHighlightWrapper.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
/** @jsx jsx */ | ||
import { css, jsx } from '@emotion/core'; | ||
import React, { useMemo } from 'react'; | ||
import { useRecoilValue } from 'recoil'; | ||
import { NeutralColors } from '@uifabric/fluent-theme'; | ||
|
||
import { rootBotProjectIdSelector, webChatInspectionDataState } from '../../recoilModel'; | ||
|
||
// apply selected state to the Web Chat message bubbles | ||
const webchatSelectedActivity = css` | ||
div > .webchat__bubble:not(.webchat__bubble--from-user) > .webchat__bubble__content { | ||
border-color: ${NeutralColors.black}; | ||
border-width: 2px; | ||
} | ||
div > .webchat__bubble.webchat__bubble--from-user > .webchat__bubble__content { | ||
border-color: ${NeutralColors.black}; | ||
border-width: 2px; | ||
} | ||
`; | ||
|
||
type ActivityHighlightWrapperProps = { activityId: string }; | ||
export const ActivityHighlightWrapper: React.FC<ActivityHighlightWrapperProps> = (props) => { | ||
const { activityId, children } = props; | ||
const currentProjectId = useRecoilValue(rootBotProjectIdSelector); | ||
const webChatInspectionData = useRecoilValue(webChatInspectionDataState(currentProjectId ?? '')); | ||
|
||
const isSelected = useMemo(() => { | ||
return ( | ||
webChatInspectionData?.item.trafficType === 'activity' && | ||
webChatInspectionData.item.activity.type === 'message' && | ||
webChatInspectionData.item.activity.id === activityId | ||
); | ||
}, [activityId, webChatInspectionData]); | ||
|
||
return ( | ||
<div | ||
css={isSelected ? webchatSelectedActivity : {}} | ||
data-testid={isSelected ? 'composer-wc-activity-selected' : 'composer-wc-activity'} | ||
> | ||
{children} | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
67 changes: 67 additions & 0 deletions
67
Composer/packages/client/src/components/WebChat/__tests__/ActivityHighlightWrapper.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import React from 'react'; | ||
|
||
import { ActivityHighlightWrapper } from '../ActivityHighlightWrapper'; | ||
import { renderWithRecoil } from '../../../../__tests__/testUtils'; | ||
import { | ||
webChatInspectionDataState, | ||
currentProjectIdState, | ||
botProjectIdsState, | ||
projectMetaDataState, | ||
botProjectFileState, | ||
} from '../../../recoilModel'; | ||
|
||
describe('<ActivityHighlightWrapper />', () => { | ||
const projectId = '1234.5678'; | ||
const selectedActivityId = 'activity1'; | ||
|
||
it('should apply a selected class if the item is selected', () => { | ||
const { getByTestId } = renderWithRecoil( | ||
<ActivityHighlightWrapper activityId={selectedActivityId} />, | ||
({ set }) => { | ||
set(currentProjectIdState, projectId); | ||
set(botProjectIdsState, [projectId]); | ||
set(projectMetaDataState(projectId), { isRootBot: true, isRemote: false }); | ||
set(botProjectFileState(projectId), { foo: 'bar' } as any); | ||
set(webChatInspectionDataState(projectId), { | ||
item: { | ||
activity: { | ||
id: selectedActivityId, | ||
type: 'message', | ||
} as any, | ||
id: 'outerId1', | ||
timestamp: Date.now(), | ||
trafficType: 'activity', | ||
}, | ||
}); | ||
} | ||
); | ||
getByTestId('composer-wc-activity-selected'); | ||
}); | ||
|
||
it('should not apply a selected class if the item is not selected', () => { | ||
const { getByTestId } = renderWithRecoil( | ||
<ActivityHighlightWrapper activityId={'someOtherActivity'} />, | ||
({ set }) => { | ||
set(currentProjectIdState, projectId); | ||
set(botProjectIdsState, [projectId]); | ||
set(projectMetaDataState(projectId), { isRootBot: true, isRemote: false }); | ||
set(botProjectFileState(projectId), { foo: 'bar' } as any); | ||
set(webChatInspectionDataState(projectId), { | ||
item: { | ||
activity: { | ||
id: selectedActivityId, | ||
type: 'message', | ||
} as any, | ||
id: 'outerId1', | ||
timestamp: Date.now(), | ||
trafficType: 'activity', | ||
}, | ||
}); | ||
} | ||
); | ||
getByTestId('composer-wc-activity'); | ||
}); | ||
}); |
14 changes: 14 additions & 0 deletions
14
Composer/packages/client/src/components/WebChat/hooks/WebChatHooksContainer.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import React from 'react'; | ||
|
||
import { useActivityInspectionListener } from './useActivityInspectionListener'; | ||
import { useTranscriptFocusListener } from './useTranscriptFocusListener'; | ||
|
||
export const WebChatHooksContainer: React.FC<{}> = () => { | ||
useActivityInspectionListener(); | ||
useTranscriptFocusListener(); | ||
|
||
return null; | ||
}; |
26 changes: 26 additions & 0 deletions
26
Composer/packages/client/src/components/WebChat/hooks/useActivityInspectionListener.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import { useEffect } from 'react'; | ||
import { hooks } from 'botframework-webchat'; | ||
import { useRecoilValue } from 'recoil'; | ||
|
||
import { rootBotProjectIdSelector, webChatInspectionDataState } from '../../../recoilModel'; | ||
|
||
const { useScrollTo } = hooks; | ||
|
||
export const useActivityInspectionListener = () => { | ||
const currentProjectId = useRecoilValue(rootBotProjectIdSelector); | ||
const webChatInspectionData = useRecoilValue(webChatInspectionDataState(currentProjectId ?? '')); | ||
const scrollToActivity = useScrollTo(); | ||
|
||
// listen for when an activity item is inspected in the log, and scroll to the activity | ||
useEffect(() => { | ||
if ( | ||
webChatInspectionData?.item.trafficType === 'activity' && | ||
webChatInspectionData.item.activity.type === 'message' | ||
) { | ||
scrollToActivity({ activityID: webChatInspectionData.item.activity.id }, { behavior: 'smooth' }); | ||
} | ||
}, [webChatInspectionData]); | ||
}; |
45 changes: 45 additions & 0 deletions
45
Composer/packages/client/src/components/WebChat/hooks/useTranscriptFocusListener.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import { useCallback } from 'react'; | ||
import { hooks } from 'botframework-webchat'; | ||
import { Activity } from 'botframework-schema'; | ||
import { useRecoilValue, useSetRecoilState } from 'recoil'; | ||
|
||
import { | ||
debugPanelActiveTabState, | ||
debugPanelExpansionState, | ||
dispatcherState, | ||
rootBotProjectIdSelector, | ||
webChatTrafficState, | ||
} from '../../../recoilModel'; | ||
import { WebChatInspectorTabKey } from '../../../pages/design/DebugPanel/TabExtensions/types'; | ||
|
||
const { useObserveTranscriptFocus } = hooks; | ||
|
||
export const useTranscriptFocusListener = () => { | ||
const currentProjectId = useRecoilValue(rootBotProjectIdSelector); | ||
const rawWebChatTraffic = useRecoilValue(webChatTrafficState(currentProjectId ?? '')); | ||
const setDebugPanelActiveTab = useSetRecoilState(debugPanelActiveTabState); | ||
const setDebugPanelExpansion = useSetRecoilState(debugPanelExpansionState); | ||
const { setWebChatInspectionData } = useRecoilValue(dispatcherState); | ||
|
||
// listen for when an activity is focused and inspect the corresponding log item | ||
const onActivityFocused = useCallback( | ||
({ activity }: { activity: Activity }) => { | ||
const trafficItem = rawWebChatTraffic.find((t) => { | ||
if (t.trafficType === 'activity') { | ||
return t.activity.id === activity?.id; | ||
} | ||
}); | ||
if (trafficItem && currentProjectId) { | ||
setDebugPanelActiveTab(WebChatInspectorTabKey); | ||
setDebugPanelExpansion(true); | ||
setWebChatInspectionData(currentProjectId, { item: trafficItem }); | ||
} | ||
}, | ||
[currentProjectId, rawWebChatTraffic, setDebugPanelActiveTab, setDebugPanelExpansion] | ||
); | ||
|
||
useObserveTranscriptFocus(onActivityFocused, [onActivityFocused]); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters