Skip to content

Commit

Permalink
Fluent: integrate webchat focus management (#5150)
Browse files Browse the repository at this point in the history
* Fluent: focus management integration

* Self review

* Update use-propagate

* Remove deprecated functionality

* Update test to use useFocus() hook

* Fix: itemEffector should pass cleanup callbacks

* Update changelog

* Fix changelog

* Update wordings

---------

Co-authored-by: William Wong <compulim@users.noreply.github.com>
  • Loading branch information
OEvgeny and compulim authored May 1, 2024
1 parent 73ee44a commit 533c151
Show file tree
Hide file tree
Showing 27 changed files with 230 additions and 114 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- `useSuggestedActions` type is updated to align with its actual implementation, by [@OEvgeny](https://github.com/OEvgeny), in PR [#5122](https://github.com/microsoft/BotFramework-WebChat/pull/5122)
- Removed deprecated code: `connect*`, `useRenderActivity`, `useRenderActivityStatus`, `useRenderAvatar`, in PR [#5148](https://github.com/microsoft/BotFramework-WebChat/pull/5148), by [@compulim](https://github.com/compulim)
- Added named exports in both CommonJS and ES Modules module format, in PR [#5148](https://github.com/microsoft/BotFramework-WebChat/pull/5148), by [@compulim](https://github.com/compulim)
- Removed deprecated `useFocusSendBox()` hook, please use `useFocus('sendBox')` instead, in PR [#5150](https://github.com/microsoft/BotFramework-WebChat/pull/5150), by [@OEvgeny](https://github.com/OEvgeny)

### Added

Expand All @@ -53,6 +54,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Added dark theme support, in PR [#5138](https://github.com/microsoft/BotFramework-WebChat/pull/5138)
- Added an information message to the telephone keypad, in PR [#5140](https://github.com/microsoft/BotFramework-WebChat/pull/5140)
- Added animation to focus indicator and pixel-perfected, in PR [#5143](https://github.com/microsoft/BotFramework-WebChat/pull/5143)
- Integrated focus management for send box, in PR [#5150](https://github.com/microsoft/BotFramework-WebChat/pull/5150), by [@OEvgeny](https://github.com/OEvgeny)
- (Experimental) Added `<LocalizeString />` component which can be used to localize strings, by [@OEvgeny](https://github.com/OEvgeny) in PR [#5140](https://github.com/microsoft/BotFramework-WebChat/pull/5140)
- Added `<ThemeProvider>` component to apply theme pack to Web Chat, by [@compulim](https://github.com/compulim), in PR [#5120](https://github.com/microsoft/BotFramework-WebChat/pull/5120)
- Added `useMakeThumbnail` hook option to create a thumbnail from the file given, by [@compulim](https://github.com/compulim), in PR [#5123](https://github.com/microsoft/BotFramework-WebChat/pull/5123) and [#5122](https://github.com/microsoft/BotFramework-WebChat/pull/5122)
Expand All @@ -66,6 +68,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Fixes [#5146](https://github.com/microsoft/BotFramework-WebChat/issues/5146). Fixed chat history focus indicator should not show up on tap, by [@OEvgeny](https://github.com/OEvgeny), in PR [#5145](https://github.com/microsoft/BotFramework-WebChat/pull/5145)
- Fixes type portability issues by exporting types from all exported code, in PR [#5148](https://github.com/microsoft/BotFramework-WebChat/pull/5148), by [@compulim](https://github.com/compulim)
- Fixes missing exports of `useNotifications`, in PR [#5148](https://github.com/microsoft/BotFramework-WebChat/pull/5148), by [@compulim](https://github.com/compulim)
- Fixes suggested actions keyboard navigation skips actions after suggested actions got updated, in PR [#5150](https://github.com/microsoft/BotFramework-WebChat/pull/5150), by [@OEvgeny](https://github.com/OEvgeny)

### Changed

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion __tests__/hooks/useFocusSendBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ test('calling emitTypingIndicator should send a typing activity', async () => {

await driver.wait(uiConnected(), timeouts.directLine);

await pageObjects.runHook('useFocusSendBox', [], fn => fn());
await pageObjects.runHook('useFocus', [], fn => fn('sendBox'));

await driver.wait(sendBoxTextBoxFocused(), timeouts.ui);
});
67 changes: 67 additions & 0 deletions __tests__/html/fluentTheme/focusManagement.backToSendBox.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<script crossorigin="anonymous" src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script crossorigin="anonymous" src="https://unpkg.com/react@16.8.6/umd/react.production.min.js"></script>
<script crossorigin="anonymous" src="https://unpkg.com/react-dom@16.8.6/umd/react-dom.production.min.js"></script>
<script crossorigin="anonymous" src="/test-harness.js"></script>
<script crossorigin="anonymous" src="/test-page-object.js"></script>
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
<script crossorigin="anonymous" src="/__dist__/botframework-webchat-fluent-theme.production.min.js"></script>
</head>
<body>
<main id="webchat"></main>
<script type="text/babel">
run(async function () {
const {
React,
ReactDOM: { render },
WebChat: { FluentThemeProvider, ReactWebChat }
} = window; // Imports in UMD fashion.

const { directLine, store } = testHelpers.createDirectLineEmulator();

const App = () => (
<ReactWebChat directLine={directLine} store={store} styleOptions={{ hideTelephoneKeypadButton: false }} />
);

render(
<FluentThemeProvider>
<App />
</FluentThemeProvider>,
document.getElementById('webchat')
);

await pageConditions.uiConnected();

await directLine.emulateIncomingActivity(
'Eiusmod anim adipisicing cupidatat adipisicing officia sint qui consequat veniam id aute.'
);

await pageConditions.numActivitiesShown(1);

document.querySelector(`[data-testid="${WebChat.testIds.sendBoxTextBox}"]`).focus();

// WHEN: SHIFT-TAB key is pressed.
await host.sendShiftTab();

// THEN: Should focus on the chat history.
await host.snapshot();

// WHEN: A key is pressed.
await host.sendKeys('The quick brown fox jumps over the lazy dog');

// THEN: Should focus on the SendBox
await host.snapshot();

await (await directLine.actPostActivity(() => host.sendKeys('\n'))).resolveAll();

// THEN: Should send the activity.
await pageConditions.numActivitiesShown(2);
await pageConditions.allOutgoingActivitiesSent();
await host.snapshot();
});
</script>
</body>
</html>
5 changes: 5 additions & 0 deletions __tests__/html/fluentTheme/focusManagement.backToSendBox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */

describe('Fluent theme applied', () => {
test('focus moves back to sendbox when letter pressed', () => runHTML('fluentTheme/focusManagement.backToSendBox'));
});
2 changes: 1 addition & 1 deletion __tests__/html/useFocus.main.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
await pageConditions.uiConnected();

// WHEN: The callback of "useFocus()" is called.
await renderWebChatWithHook(() => window.WebChat.hooks.useFocus()());
await renderWebChatWithHook(() => Promise.resolve(window.WebChat.hooks.useFocus()).then(focus => focus()));

// THEN: It should focus on the (blank) transcript.
await host.snapshot();
Expand Down
2 changes: 1 addition & 1 deletion __tests__/html/useFocus.sendBox.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
);

await pageConditions.uiConnected();
await pageObjects.runHook(({ useFocus }) => useFocus()('sendBox'));
await pageObjects.runHook(({ useFocus }) => Promise.resolve(useFocus()).then(focus => focus('sendBox')));

await host.snapshot();
});
Expand Down
2 changes: 1 addition & 1 deletion __tests__/html/useFocus.sendBoxWithoutKeyboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

// Since the keyboard is controlled by the browser and OS, we cannot test if the keyboard activated or not.
// This is our best-effort to check if focus is being set correctly or not.
await pageObjects.runHook(({ useFocus }) => useFocus()('sendBoxWithoutKeyboard'));
await pageObjects.runHook(({ useFocus }) => Promise.resolve(useFocus()).then(focus => focus('sendBoxWithoutKeyboard')));

await host.snapshot();
});
Expand Down
41 changes: 0 additions & 41 deletions __tests__/html/useFocusSendBox.html

This file was deleted.

3 changes: 0 additions & 3 deletions __tests__/html/useFocusSendBox.js

This file was deleted.

2 changes: 2 additions & 0 deletions docs/HOOKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,8 @@ useFocusSendBox(): () => void

> This function is deprecated. Developers should migrate to [`useFocus`](#usefocus).
> This function was removed in `botframework-webchat@4.17.0`.
When called, this function will send focus to the send box.

## `useGetSendTimeoutForActivity`
Expand Down
78 changes: 78 additions & 0 deletions packages/component/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
"react-scroll-to-bottom": "4.2.0",
"redux": "5.0.0",
"simple-update-in": "2.2.0",
"use-propagate": "^0.1.0",
"use-ref-from": "^0.1.0",
"valibot": "^0.30.0"
},
Expand Down
4 changes: 0 additions & 4 deletions packages/component/src/Composer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ import createDefaultSendBoxMiddleware from './SendBox/createMiddleware';
import createDefaultSendBoxToolbarMiddleware from './SendBoxToolbar/createMiddleware';
import createStyleSet from './Styles/createStyleSet';
import { type ContextOf } from './types/ContextOf';
import { type FocusSendBoxInit } from './types/internal/FocusSendBoxInit';
import { type FocusTranscriptInit } from './types/internal/FocusTranscriptInit';
import addTargetBlankToHyperlinksMarkdown from './Utils/addTargetBlankToHyperlinksMarkdown';
import createCSSKey from './Utils/createCSSKey';
Expand Down Expand Up @@ -111,7 +110,6 @@ const ComposerCore = ({
const [dictateAbortable, setDictateAbortable] = useState();
const [referenceGrammarID] = useReferenceGrammarID();
const [styleOptions] = useStyleOptions();
const focusSendBoxCallbacksRef = useRef<((init: FocusSendBoxInit) => Promise<void>)[]>([]);
const focusTranscriptCallbacksRef = useRef<((init: FocusTranscriptInit) => Promise<void>)[]>([]);
const internalMarkdownIt = useMemo(() => new MarkdownIt(), []);
const scrollToCallbacksRef = useRef([]);
Expand Down Expand Up @@ -221,7 +219,6 @@ const ComposerCore = ({
dictateAbortable,
dispatchScrollPosition,
dispatchTranscriptFocusByActivityKey,
focusSendBoxCallbacksRef,
focusTranscriptCallbacksRef,
internalMarkdownItState: [internalMarkdownIt],
internalRenderMarkdownInline,
Expand All @@ -243,7 +240,6 @@ const ComposerCore = ({
dictateAbortable,
dispatchScrollPosition,
dispatchTranscriptFocusByActivityKey,
focusSendBoxCallbacksRef,
focusTranscriptCallbacksRef,
internalMarkdownIt,
internalRenderMarkdownInline,
Expand Down
6 changes: 3 additions & 3 deletions packages/component/src/SendBox/TextBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React, { useCallback, useMemo, useRef } from 'react';
import AccessibleInputText from '../Utils/AccessibleInputText';
import navigableEvent from '../Utils/TypeFocusSink/navigableEvent';
import { ie11 } from '../Utils/detectBrowser';
import useRegisterFocusSendBox from '../hooks/internal/useRegisterFocusSendBox';
import { useRegisterFocusSendBox, type SendBoxFocusOptions } from '../hooks/sendBoxFocus';
import useStyleToEmotionObject from '../hooks/internal/useStyleToEmotionObject';
import useScrollDown from '../hooks/useScrollDown';
import useScrollUp from '../hooks/useScrollUp';
Expand Down Expand Up @@ -162,8 +162,8 @@ const TextBox = ({ className = '' }: Readonly<{ className?: string | undefined }
[scrollDown, scrollUp]
);

const focusCallback = useCallback<Parameters<typeof useRegisterFocusSendBox>[0]>(
options => {
const focusCallback = useCallback(
(options: SendBoxFocusOptions) => {
const { noKeyboard } = options;
const { current } = inputElementRef;

Expand Down
7 changes: 4 additions & 3 deletions packages/component/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import useDictateAbortable from './useDictateAbortable';
import useFocus from './useFocus';
import useFocusSendBox from './useFocusSendBox';
import useMakeThumbnail from './useMakeThumbnail';
import useObserveScrollPosition from './useObserveScrollPosition';
import useObserveTranscriptFocus from './useObserveTranscriptFocus';
Expand All @@ -18,17 +17,19 @@ import { useTypingIndicatorVisible } from '../BasicTypingIndicator';
import { useSendBoxSpeechInterimsVisible } from '../SendBox/BasicSendBox';
import { useMicrophoneButtonClick, useMicrophoneButtonDisabled } from '../SendBox/MicrophoneButton';
import { useTextBoxSubmit, useTextBoxValue } from '../SendBox/TextBox';
import { useRegisterFocusSendBox, type SendBoxFocusOptions } from './sendBoxFocus';

export { type SendBoxFocusOptions };

export {
useDictateAbortable,
useFocus,
/** @deprecated Please use `useFocus('sendBox')` instead. */
useFocusSendBox,
useMakeThumbnail,
useMicrophoneButtonClick,
useMicrophoneButtonDisabled,
useObserveScrollPosition,
useObserveTranscriptFocus,
useRegisterFocusSendBox,
useRenderMarkdownAsHTML,
useScrollDown,
useScrollTo,
Expand Down
2 changes: 0 additions & 2 deletions packages/component/src/hooks/internal/WebChatUIContext.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { createContext, type MutableRefObject } from 'react';

import { type FocusSendBoxInit } from '../../types/internal/FocusSendBoxInit';
import { type FocusTranscriptInit } from '../../types/internal/FocusTranscriptInit';

export type ContextType = {
focusSendBoxCallbacksRef: MutableRefObject<((init: FocusSendBoxInit) => Promise<void>)[]>;
focusTranscriptCallbacksRef: MutableRefObject<((init: FocusTranscriptInit) => Promise<void>)[]>;
};

Expand Down
Loading

0 comments on commit 533c151

Please sign in to comment.