Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ Breaking changes in this release:
- Fixed virtual keyboard should be collapsed after being suppressed, in iOS 26.3, by [@compulim](https://github.com/compulim) in PR [#5757](https://github.com/microsoft/BotFramework-WebChat/pull/5757)
- Fixed Fluent/Copilot typing indicator animation background color, in PR [#5770](https://github.com/microsoft/BotFramework-WebChat/pull/5770), by [@OEvgeny](https://github.com/OEvgeny)
- Fixed `<AddFullBundle>` should not re-render when `attachment[ForScreenReader]Middleware` is updated without noticeable different (`iterateEquals`), by [@compulim](https://github.com/compulim), in PR [#5779](https://github.com/microsoft/BotFramework-WebChat/pull/5779)
- Fixed send box should narrate `aria-label` prop, by [@compulim](https://github.com/compulim), in PR [#5805](https://github.com/microsoft/BotFramework-WebChat/pull/5805)

## [4.18.0] - 2024-07-10

Expand Down
91 changes: 91 additions & 0 deletions __tests__/html2/accessibility/sendBox/ariaLabel.fluent.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<style type="text/css">
/* TODO: [P*] Can we eliminate this style? */
.fui-FluentProvider,
.webchat-fluent {
height: 100%;
}
</style>
</head>
<body>
<main id="webchat"></main>
<!-- Redirect packages on esm.sh loaded by `@fluentui/react-components` -->
<script type="importmap">
{
"imports": {
"@fluentui/react-components": "https://esm.sh/@fluentui/react-components?deps=react@18.3.1,react-dom@18.3.1&exports=FluentProvider,createDarkTheme,webLightTheme",
"botframework-webchat": "/__dist__/packages/bundle/static/botframework-webchat.js",
"botframework-webchat/component": "/__dist__/packages/bundle/static/botframework-webchat/component.js",
"botframework-webchat/decorator": "/__dist__/packages/bundle/static/botframework-webchat/decorator.js",
"botframework-webchat/hook": "/__dist__/packages/bundle/static/botframework-webchat/hook.js",
"botframework-webchat/internal": "/__dist__/packages/bundle/static/botframework-webchat/internal.js",
"botframework-webchat-fluent-theme": "/__dist__/packages/fluent-theme/static/botframework-webchat-fluent-theme.js",
"react": "/__dist__/packages/bundle/static/react.js",
"react-dom": "/__dist__/packages/bundle/static/react-dom.js",
"react-dom/client": "/__dist__/packages/bundle/static/react-dom/client.js",
"https://esm.sh/react@18.3.1/es2022/react.mjs": "/__dist__/packages/bundle/static/react.js",
"https://esm.sh/react@18.3.1/es2022/react-dom.mjs": "/__dist__/packages/bundle/static/react-dom.18.js",
"https://esm.sh/react@18.3.1/es2022/react-dom/client.mjs": "/__dist__/packages/bundle/static/react-dom.18/client.js"
}
}
</script>
<script type="module">
import '/test-harness.mjs';
import '/test-page-object.mjs';

import { FluentProvider, webLightTheme } from '@fluentui/react-components';
import { createDirectLine, createStoreWithOptions, hooks, ReactWebChat } from 'botframework-webchat';
import { FluentThemeProvider } from 'botframework-webchat-fluent-theme';
import { createElement } from 'react';
import { createRoot } from 'react-dom/client';

const { useStyleOptions } = hooks;
const {
testHelpers: { createDirectLineEmulator }
} = window;

// TODO: This is for `createDirectLineEmulator` only, should find ways to eliminate this line.
window.WebChat = { createStoreWithOptions };

testHelpers.hideKnownError();

run(async function () {
const { directLine, store } = createDirectLineEmulator();

const fluentTheme = {
...webLightTheme,
// Original is #242424 which is too light for fui-FluentProvider to pass our accessibility checks.
colorNeutralForeground1: '#1b1b1b'
};

createRoot(document.getElementsByTagName('main')[0]).render(
createElement(
FluentProvider,
{ className: 'fui-FluentProvider', theme: fluentTheme },
createElement(
FluentThemeProvider,
{ variant: 'fluent' },
createElement(ReactWebChat, {
directLine,
overrideLocalizedStrings: {
TEXT_INPUT_ALT: 'Hello, World!'
},
store
})
)
)
);

await pageConditions.uiConnected();

const sendBoxTextBox = document.querySelector('[data-testid="send box text area"]');

expect(sendBoxTextBox.tagName).toBe('TEXTAREA');
expect(sendBoxTextBox.getAttribute('aria-label')).toBe('Hello, World!');
});
</script>
</body>
</html>
53 changes: 53 additions & 0 deletions __tests__/html2/accessibility/sendBox/ariaLabel.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
</head>
<body>
<main id="webchat"></main>
<script type="importmap">
{
"imports": {
"botframework-webchat": "/__dist__/packages/bundle/static/botframework-webchat.js",
"react": "/__dist__/packages/bundle/static/react.js",
"react-dom": "/__dist__/packages/bundle/static/react-dom.js"
}
}
</script>
<script type="module">
import '/test-harness.mjs';
import '/test-page-object.mjs';

import { createStoreWithOptions, renderWebChat, testIds } from 'botframework-webchat';

const {
testHelpers: { createDirectLineEmulator }
} = window;

// TODO: Should find ways to eliminate this line.
window.WebChat = { createStoreWithOptions, testIds };

testHelpers.hideKnownError();

run(async function () {
const { directLine, store } = createDirectLineEmulator();

renderWebChat(
{
directLine,
overrideLocalizedStrings: {
TEXT_INPUT_ALT: 'Hello, World!'
},
store
},
document.getElementById('webchat')
);

await pageConditions.uiConnected();

expect(pageElements.sendBoxTextBox().tagName).toBe('INPUT');
expect(pageElements.sendBoxTextBox().getAttribute('aria-label')).toBe('Hello, World!');
});
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
</head>
<body>
<main id="webchat"></main>
<script type="importmap">
{
"imports": {
"botframework-webchat": "/__dist__/packages/bundle/static/botframework-webchat.js",
"react": "/__dist__/packages/bundle/static/react.js",
"react-dom": "/__dist__/packages/bundle/static/react-dom.js"
}
}
</script>
<script type="module">
import '/test-harness.mjs';
import '/test-page-object.mjs';

import { createStoreWithOptions, renderWebChat, testIds } from 'botframework-webchat';

const {
testHelpers: { createDirectLineEmulator }
} = window;

// TODO: Should find ways to eliminate this line.
window.WebChat = { createStoreWithOptions, testIds };

testHelpers.hideKnownError();

run(async function () {
const { directLine, store } = createDirectLineEmulator();

renderWebChat(
{
directLine,
overrideLocalizedStrings: {
TEXT_INPUT_ALT: 'Hello, World!'
},
store,
styleOptions: {
sendBoxTextWrap: true
}
},
document.getElementById('webchat')
);

await pageConditions.uiConnected();

expect(pageElements.sendBoxTextBox().tagName).toBe('TEXTAREA');
expect(pageElements.sendBoxTextBox().getAttribute('aria-label')).toBe('Hello, World!');
});
</script>
</body>
</html>
53 changes: 31 additions & 22 deletions packages/component/src/TextArea/TextArea.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { hooks } from 'botframework-webchat-api';
import { useStyles } from '@msinternal/botframework-webchat-styles/react';
import { hooks } from 'botframework-webchat-api';
import cx from 'classnames';
import React, {
forwardRef,
Expand All @@ -8,23 +8,23 @@ import React, {
useRef,
type FormEventHandler,
type KeyboardEventHandler,
type MouseEventHandler,
type ReactNode
type MouseEventHandler
} from 'react';
import { boolean, custom, number, object, optional, pipe, readonly, string, type InferInput } from 'valibot';

import { reactNode, validateProps } from '@msinternal/botframework-webchat-react-valibot';
import styles from './TextArea.module.css';

const { useUIState } = hooks;

const TextArea = forwardRef<
HTMLTextAreaElement,
Readonly<{
'aria-describedby'?: string | undefined;
'aria-labelledby'?: string | undefined;
className?: string | undefined;
completion?: ReactNode | undefined;
'data-testid'?: string | undefined;

const TextAreaPropsSchema = pipe(
object({
'aria-describedby': optional(string()),
'aria-label': optional(string()),
'aria-labelledby': optional(string()),
className: optional(string()),
completion: optional(reactNode()),
'data-testid': optional(string()),
/**
* `true`, if the text area should be hidden but stay in the DOM, otherwise, `false`.
*
Expand All @@ -33,15 +33,22 @@ const TextArea = forwardRef<
* - When the DTMF keypad is going away, we need to send focus to the text area before we unmount DTMF keypad,
* This ensures the flow of focus did not sent to document body
*/
hidden?: boolean | undefined;
onClick?: MouseEventHandler<HTMLTextAreaElement> | undefined;
onInput?: FormEventHandler<HTMLTextAreaElement> | undefined;
placeholder?: string | undefined;
readOnly?: boolean | undefined;
startRows?: number | undefined;
value?: string | undefined;
}>
>((props, ref) => {
hidden: optional(boolean()),
onClick: optional(custom<MouseEventHandler<HTMLTextAreaElement>>(value => typeof value === 'function')),
onInput: optional(custom<FormEventHandler<HTMLTextAreaElement>>(value => typeof value === 'function')),
placeholder: optional(string()),
readOnly: optional(boolean()),
startRows: optional(number()),
value: optional(string())
}),
readonly()
);

type TextAreaProps = InferInput<typeof TextAreaPropsSchema>;

const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>((rawProps, ref) => {
const props = validateProps(TextAreaPropsSchema, rawProps);

const [uiState] = useUIState();
const classNames = useStyles(styles);
const isInCompositionRef = useRef<boolean>(false);
Expand Down Expand Up @@ -84,11 +91,12 @@ const TextArea = forwardRef<
) : (
<Fragment>
<div className={cx(classNames['text-area-doppelganger'], classNames['text-area-shared'])}>
{props.completion ? props.completion : props.value}{' '}
{props.completion || props.value}{' '}
Comment thread
compulim marked this conversation as resolved.
</div>
<textarea
aria-describedby={props['aria-describedby']}
aria-disabled={disabled}
aria-label={props['aria-label']}
aria-labelledby={props['aria-labelledby']}
aria-placeholder={props.placeholder}
className={cx(classNames['text-area-input'], classNames['text-area-shared'])}
Expand All @@ -115,3 +123,4 @@ const TextArea = forwardRef<
TextArea.displayName = 'TextArea';

export default TextArea;
export { TextAreaPropsSchema, type TextAreaProps };
Loading
Loading