diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e4b3be33e..31c8279c4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixed to keep telephone keypad on-screen on click, in PR [#5132](https://github.com/microsoft/BotFramework-WebChat/pull/5132) - Disabled send button and hid message length when telephone keypad is shown, in PR [#5136](https://github.com/microsoft/BotFramework-WebChat/pull/5136) - 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) +- (Experimental) Added `` 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 `` 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) diff --git a/__tests__/__image_snapshots__/html/fluent-theme-fallback-dark-js-fluent-theme-applied-uses-fluent-dark-theme-if-present-3-snap.png b/__tests__/__image_snapshots__/html/fluent-theme-fallback-dark-js-fluent-theme-applied-uses-fluent-dark-theme-if-present-3-snap.png index 3a386f90ac..fafb23d2a5 100644 Binary files a/__tests__/__image_snapshots__/html/fluent-theme-fallback-dark-js-fluent-theme-applied-uses-fluent-dark-theme-if-present-3-snap.png and b/__tests__/__image_snapshots__/html/fluent-theme-fallback-dark-js-fluent-theme-applied-uses-fluent-dark-theme-if-present-3-snap.png differ diff --git a/__tests__/__image_snapshots__/html/telephone-keypad-custom-input-message-js-fluent-theme-applied-telephone-keypad-should-show-custom-message-1-snap.png b/__tests__/__image_snapshots__/html/telephone-keypad-custom-input-message-js-fluent-theme-applied-telephone-keypad-should-show-custom-message-1-snap.png new file mode 100644 index 0000000000..60301b33cb Binary files /dev/null and b/__tests__/__image_snapshots__/html/telephone-keypad-custom-input-message-js-fluent-theme-applied-telephone-keypad-should-show-custom-message-1-snap.png differ diff --git a/__tests__/__image_snapshots__/html/telephone-keypad-custom-input-message-js-fluent-theme-applied-telephone-keypad-should-show-custom-message-2-snap.png b/__tests__/__image_snapshots__/html/telephone-keypad-custom-input-message-js-fluent-theme-applied-telephone-keypad-should-show-custom-message-2-snap.png new file mode 100644 index 0000000000..bba55b77da Binary files /dev/null and b/__tests__/__image_snapshots__/html/telephone-keypad-custom-input-message-js-fluent-theme-applied-telephone-keypad-should-show-custom-message-2-snap.png differ diff --git a/__tests__/__image_snapshots__/html/telephone-keypad-show-hide-js-fluent-theme-applied-telephone-keypad-should-show-hide-1-snap.png b/__tests__/__image_snapshots__/html/telephone-keypad-show-hide-js-fluent-theme-applied-telephone-keypad-should-show-hide-1-snap.png index 9460243b91..6fb9904279 100644 Binary files a/__tests__/__image_snapshots__/html/telephone-keypad-show-hide-js-fluent-theme-applied-telephone-keypad-should-show-hide-1-snap.png and b/__tests__/__image_snapshots__/html/telephone-keypad-show-hide-js-fluent-theme-applied-telephone-keypad-should-show-hide-1-snap.png differ diff --git a/__tests__/__image_snapshots__/html/telephone-keypad-show-hide-js-fluent-theme-applied-telephone-keypad-should-show-hide-2-snap.png b/__tests__/__image_snapshots__/html/telephone-keypad-show-hide-js-fluent-theme-applied-telephone-keypad-should-show-hide-2-snap.png index 9e405310c5..96d1694236 100644 Binary files a/__tests__/__image_snapshots__/html/telephone-keypad-show-hide-js-fluent-theme-applied-telephone-keypad-should-show-hide-2-snap.png and b/__tests__/__image_snapshots__/html/telephone-keypad-show-hide-js-fluent-theme-applied-telephone-keypad-should-show-hide-2-snap.png differ diff --git a/__tests__/__image_snapshots__/html/telephone-keypad-tap-js-fluent-theme-applied-telephone-keypad-when-tapped-should-send-message-1-snap.png b/__tests__/__image_snapshots__/html/telephone-keypad-tap-js-fluent-theme-applied-telephone-keypad-when-tapped-should-send-message-1-snap.png index 9408d661e5..5dd2b5b185 100644 Binary files a/__tests__/__image_snapshots__/html/telephone-keypad-tap-js-fluent-theme-applied-telephone-keypad-when-tapped-should-send-message-1-snap.png and b/__tests__/__image_snapshots__/html/telephone-keypad-tap-js-fluent-theme-applied-telephone-keypad-when-tapped-should-send-message-1-snap.png differ diff --git a/__tests__/html/fluentTheme/telephoneKeypad.customInputMessage.html b/__tests__/html/fluentTheme/telephoneKeypad.customInputMessage.html new file mode 100644 index 0000000000..1676d7bdd6 --- /dev/null +++ b/__tests__/html/fluentTheme/telephoneKeypad.customInputMessage.html @@ -0,0 +1,68 @@ + + + + + + + + + + + + + +
+ + + diff --git a/__tests__/html/fluentTheme/telephoneKeypad.customInputMessage.js b/__tests__/html/fluentTheme/telephoneKeypad.customInputMessage.js new file mode 100644 index 0000000000..966dc928dd --- /dev/null +++ b/__tests__/html/fluentTheme/telephoneKeypad.customInputMessage.js @@ -0,0 +1,5 @@ +/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */ + +describe('Fluent theme applied', () => { + test('telephone keypad should show custom message', () => runHTML('fluentTheme/telephoneKeypad.customInputMessage')); +}); diff --git a/docs/LOCALIZATION.md b/docs/LOCALIZATION.md index e4c88d8d0f..ffc8b9c418 100644 --- a/docs/LOCALIZATION.md +++ b/docs/LOCALIZATION.md @@ -261,7 +261,9 @@ function convertFromOldStringId(js) { TEXT_INPUT_UPLOAD_BUTTON_ALT: js['Upload file'], TEXT_INPUT_TELEPHON_KEYPAD_BUTTON_ALT: js['Telephone keypad'], TEXT_INPUT_DROP_ZONE: js['Drop files'], - TEXT_INPUT_LENGTH_EXCEEDED_ALT: js['Message lengths exceeded'], + TEXT_INPUT_LENGTH_EXCEEDED_ALT: js['Message length exceeded'], + + TELEPHONE_KEYPAD_INPUT_MESSAGE: js['Only supports single-digit input'], TEXT_INPUT_ATTACHMENTS_FEW: js['attachments'], TEXT_INPUT_ATTACHMENTS_MANY: js['attachments'], diff --git a/packages/api/src/localization/en-US.json b/packages/api/src/localization/en-US.json index d815cf8baa..78950157d4 100644 --- a/packages/api/src/localization/en-US.json +++ b/packages/api/src/localization/en-US.json @@ -125,7 +125,8 @@ "TEXT_INPUT_UPLOAD_BUTTON_ALT": "Upload file", "TEXT_INPUT_TELEPHONE_KEYPAD_BUTTON_ALT": "Telephone keypad", "TEXT_INPUT_DROP_ZONE": "Drop files", - "TEXT_INPUT_LENGTH_EXCEEDED_ALT": "Message lengths exceeded", + "TEXT_INPUT_LENGTH_EXCEEDED_ALT": "Message length exceeded", + "TELEPHONE_KEYPAD_INPUT_MESSAGE": "Only supports single-digit input", "TEXT_INPUT_ATTACHMENTS_FEW": "$1 attachments", "_TEXT_INPUT_ATTACHMENTS_FEW.comment": "$1 is the number of attachments. This is for plural rule of \"few\".", "TEXT_INPUT_ATTACHMENTS_MANY": "$1 attachments", diff --git a/packages/component/src/Utils/LocalizedString.tsx b/packages/component/src/Utils/LocalizedString.tsx new file mode 100644 index 0000000000..517885e18e --- /dev/null +++ b/packages/component/src/Utils/LocalizedString.tsx @@ -0,0 +1,143 @@ +/* eslint react/no-danger: "off" */ + +import { hooks } from 'botframework-webchat-api'; +import { onErrorResumeNext } from 'botframework-webchat-core'; +import MarkdownIt from 'markdown-it'; +import React, { memo, useMemo } from 'react'; +import betterLinks, { type BetterLinkEnv, type LinkOptions } from './betterLinks'; + +const allowedSchemes = ['data', 'http', 'https', 'ftp', 'mailto', 'sip', 'tel']; + +const linkDefinitions = []; + +const externalLinkAlt = ''; + +const defaultDecorateLink = (href: string, textContent: string, linkOptions?: LinkOptions): LinkOptions | undefined => { + const decoration: LinkOptions = { + rel: 'noopener noreferrer', + target: '_blank', + wrapZeroWidthSpace: true, + ...linkOptions + }; + + const ariaLabelSegments: string[] = [textContent]; + const classes: Set = new Set(); + const linkDefinition = linkDefinitions.find(({ url }) => url === href); + const protocol = onErrorResumeNext(() => new URL(href).protocol); + + if (linkDefinition) { + ariaLabelSegments.push( + linkDefinition.title || onErrorResumeNext(() => new URL(linkDefinition.url).host) || linkDefinition.url + ); + + // linkDefinition.identifier is uppercase, while linkDefinition.label is as-is. + linkDefinition.label === textContent && classes.add('webchat__render-markdown__pure-identifier'); + } + + // For links that would be sanitized out, let's turn them into a button so we could handle them later. + if (!allowedSchemes.map(scheme => `${scheme}:`).includes(protocol)) { + decoration.asButton ??= true; + + classes.add('webchat__render-markdown__citation'); + } else if (protocol === 'http:' || protocol === 'https:') { + decoration.iconClassName = [decoration.iconClassName, 'webchat__render-markdown__external-link-icon'] + .filter((className: string | undefined) => className) + .join(' '); + + ariaLabelSegments.push(externalLinkAlt); + } + + // The first segment is textContent. Putting textContent is aria-label is useless. + if (ariaLabelSegments.length > 1) { + // If "aria-label" is already applied, do not overwrite it. + decoration.ariaLabel ??= (value: string) => value || ariaLabelSegments.join(' '); + } + + if (typeof linkOptions?.className === 'string') { + classes.add(linkOptions.className); + } + + // Resolve className + const classNamesString = Array.from(classes).join(' '); + if (linkOptions?.className && linkOptions?.className instanceof Function) { + decoration.className = linkOptions.className(classNamesString); + } else { + decoration.className = classNamesString; + } + + // By default, Markdown-It will set "title" to the link title in link definition. + + // However, "title" may be narrated by screen reader: + // - Edge + // - will narrate "aria-label" but not "title" + // -