Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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