diff --git a/CHANGELOG.md b/CHANGELOG.md
index b0bacf65d0..135d3f4bf9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -313,6 +313,7 @@ Breaking changes in this release:
- Bumped Chrome in Docker to 141 from 110, in PR [#5619](https://github.com/microsoft/BotFramework-WebChat/pull/5619), by [@compulim](https://github.com/compulim)
- Bumped to [`valibot@1.2.0`](https://npmjs.com/package/valibot/v/1.2.0), in PR [#5650](https://github.com/microsoft/BotFramework-WebChat/pull/5650), by [@compulim](https://github.com/compulim)
- Pinned to [`botframework-directlinespeech-sdk@4.18.1-main.20251208.8ccadd6`](https://npmjs.com/package/botframework-directlinespeech-sdk/v/4.18.1-main.20251208.8ccadd6), by [@OEvgeny](https://github.com/OEvgeny) in PR [#5662](https://github.com/microsoft/BotFramework-WebChat/pull/5662)
+- Converted remaining activity components to CSS Modules, in PR [#5668](https://github.com/microsoft/BotFramework-WebChat/pull/5668), by [@OEvgeny](https://github.com/OEvgeny)
### Deprecated
diff --git a/__tests__/html2/accessibility/attachment/aria.html b/__tests__/html2/accessibility/attachment/aria.html
index e0a0da03f5..abce308204 100644
--- a/__tests__/html2/accessibility/attachment/aria.html
+++ b/__tests__/html2/accessibility/attachment/aria.html
@@ -38,7 +38,7 @@
await pageConditions.scrollToBottomCompleted();
const carouselContainer = document
- .querySelector('.webchat__carousel-filmstrip')
+ .querySelector('.carousel-filmstrip')
.getAttribute('aria-labelledby');
expect(carouselContainer).toBeFalsy();
diff --git a/__tests__/html2/accessibility/attachment/stackedLayoutRole.html b/__tests__/html2/accessibility/attachment/stackedLayoutRole.html
index 85d0ffb165..846f124db9 100644
--- a/__tests__/html2/accessibility/attachment/stackedLayoutRole.html
+++ b/__tests__/html2/accessibility/attachment/stackedLayoutRole.html
@@ -23,7 +23,7 @@
await pageConditions.minNumActivitiesShown(2);
await pageConditions.scrollToBottomCompleted();
- const attachmentRole = document.querySelector('.webchat__carousel-filmstrip-attachment').getAttribute('role');
+ const attachmentRole = document.querySelector('.carousel-filmstrip-attachment').getAttribute('role');
expect(attachmentRole).toBeTruthy();
});
diff --git a/__tests__/html2/accessibility/liveRegion/attachment/animationCard.html b/__tests__/html2/accessibility/liveRegion/attachment/animationCard.html
index 36110130dd..8a575cc51a 100644
--- a/__tests__/html2/accessibility/liveRegion/attachment/animationCard.html
+++ b/__tests__/html2/accessibility/liveRegion/attachment/animationCard.html
@@ -51,7 +51,7 @@
await pageConditions.minNumActivitiesShown(2);
// Replace the animation with a static image.
- for (const imageElement of document.querySelectorAll('.webchat__bubble__content img')) {
+ for (const imageElement of document.querySelectorAll('.bubble__content img')) {
imageElement.setAttribute(
'src',
'https://raw.githubusercontent.com/compulim/BotFramework-MockBot/master/public/assets/surface1.jpg'
diff --git a/__tests__/html2/carousel/flipperButton.html b/__tests__/html2/carousel/flipperButton.html
index ede4d40938..3a8eac17ab 100644
--- a/__tests__/html2/carousel/flipperButton.html
+++ b/__tests__/html2/carousel/flipperButton.html
@@ -23,8 +23,8 @@
await pageConditions.numActivitiesShown(2);
// GIVEN: Carousel is at left most position.
- const carouselLayout = document.querySelector('.webchat__carousel-layout');
- const carouselFilmstrip = carouselLayout.querySelector('.webchat__carousel-filmstrip');
+ const carouselLayout = document.querySelector('.carousel-layout');
+ const carouselFilmstrip = carouselLayout.querySelector('.carousel-filmstrip');
expect(carouselFilmstrip.scrollLeft).toBe(0);
diff --git a/__tests__/html2/carousel/flipperButton.rtl.html b/__tests__/html2/carousel/flipperButton.rtl.html
index 268995d66a..d7a832c3f7 100644
--- a/__tests__/html2/carousel/flipperButton.rtl.html
+++ b/__tests__/html2/carousel/flipperButton.rtl.html
@@ -24,8 +24,8 @@
await pageConditions.numActivitiesShown(2);
// GIVEN: Carousel is at left most position.
- const carouselLayout = document.querySelector('.webchat__carousel-layout');
- const carouselFilmstrip = carouselLayout.querySelector('.webchat__carousel-filmstrip');
+ const carouselLayout = document.querySelector('.carousel-layout');
+ const carouselFilmstrip = carouselLayout.querySelector('.carousel-filmstrip');
expect(carouselFilmstrip.scrollLeft).toBe(0);
diff --git a/__tests__/html2/carousel/navigation.tab.html b/__tests__/html2/carousel/navigation.tab.html
index e8095f8678..ea97187fef 100644
--- a/__tests__/html2/carousel/navigation.tab.html
+++ b/__tests__/html2/carousel/navigation.tab.html
@@ -55,7 +55,7 @@
);
await pageConditions.stabilized(
'carousel',
- () => document.querySelector('.webchat__carousel-filmstrip').scrollLeft,
+ () => document.querySelector('.carousel-filmstrip').scrollLeft,
5,
5000
);
@@ -70,7 +70,7 @@
);
await pageConditions.stabilized(
'carousel',
- () => document.querySelector('.webchat__carousel-filmstrip').scrollLeft,
+ () => document.querySelector('.carousel-filmstrip').scrollLeft,
5,
5000
);
diff --git a/__tests__/html2/fluentTheme/carousel.html b/__tests__/html2/fluentTheme/carousel.html
index 5cdde1ea84..9c9b4e0334 100644
--- a/__tests__/html2/fluentTheme/carousel.html
+++ b/__tests__/html2/fluentTheme/carousel.html
@@ -55,8 +55,8 @@
await pageConditions.numActivitiesShown(2);
// WHEN: Right flipper is clicked.
- const carouselLayout = document.querySelector('.webchat__carousel-layout');
- const carouselFilmstrip = carouselLayout.querySelector('.webchat__carousel-filmstrip');
+ const carouselLayout = document.querySelector('.carousel-layout');
+ const carouselFilmstrip = carouselLayout.querySelector('.carousel-filmstrip');
const rightFlipper = carouselLayout.querySelector('[aria-label="Next"]');
// Improve test reliability by hover before click on flipper button.
diff --git a/__tests__/html2/fluentTheme/carousel.html.snap-1.png b/__tests__/html2/fluentTheme/carousel.html.snap-1.png
index 12128e85b6..078d6ac7bf 100644
Binary files a/__tests__/html2/fluentTheme/carousel.html.snap-1.png and b/__tests__/html2/fluentTheme/carousel.html.snap-1.png differ
diff --git a/__tests__/html2/fluentTheme/carousel.html.snap-2.png b/__tests__/html2/fluentTheme/carousel.html.snap-2.png
index ffd9734dc6..b90e8bd001 100644
Binary files a/__tests__/html2/fluentTheme/carousel.html.snap-2.png and b/__tests__/html2/fluentTheme/carousel.html.snap-2.png differ
diff --git a/__tests__/html2/grouping/fluentTheme.html.snap-1.png b/__tests__/html2/grouping/fluentTheme.html.snap-1.png
index 16011906d8..76ff1a0eae 100644
Binary files a/__tests__/html2/grouping/fluentTheme.html.snap-1.png and b/__tests__/html2/grouping/fluentTheme.html.snap-1.png differ
diff --git a/__tests__/html2/middleware/cardAction/signIn.html b/__tests__/html2/middleware/cardAction/signIn.html
index bb16b38bc4..3648f0a3f5 100644
--- a/__tests__/html2/middleware/cardAction/signIn.html
+++ b/__tests__/html2/middleware/cardAction/signIn.html
@@ -50,7 +50,7 @@
await pageConditions.numActivitiesShown(2);
- const openUrlButton = document.querySelector('.webchat__bubble__content button');
+ const openUrlButton = document.querySelector('.bubble__content button');
await host.click(openUrlButton);
await pageConditions.numActivitiesShown(4);
diff --git a/__tests__/html2/middleware/cardAction/signIn.noGetSessionId.html b/__tests__/html2/middleware/cardAction/signIn.noGetSessionId.html
index 39a3c50b82..4aa34c5285 100644
--- a/__tests__/html2/middleware/cardAction/signIn.noGetSessionId.html
+++ b/__tests__/html2/middleware/cardAction/signIn.noGetSessionId.html
@@ -54,7 +54,7 @@
await pageConditions.numActivitiesShown(2);
- const openUrlButton = document.querySelector('.webchat__bubble__content button');
+ const openUrlButton = document.querySelector('.bubble__content button');
await host.click(openUrlButton);
await pageConditions.numActivitiesShown(4);
diff --git a/packages/component/src/Activity/Bubble.module.css b/packages/component/src/Activity/Bubble.module.css
new file mode 100644
index 0000000000..6c157f084d
--- /dev/null
+++ b/packages/component/src/Activity/Bubble.module.css
@@ -0,0 +1,131 @@
+:global(.webchat) .bubble {
+ display: flex;
+ position: relative;
+
+ --webchat__bubble--background: var(--webchat__background--bubble);
+ --webchat__bubble--border-color: var(--webchat__border-color--bubble);
+ --webchat__bubble--border-radius: var(--webchat__border-radius--bubble);
+ --webchat__bubble--border-style: var(--webchat__border-style--bubble);
+ --webchat__bubble--border-width: var(--webchat__border-width--bubble);
+ --webchat__bubble--border-width: var(--webchat__border-width--bubble);
+ --webchat__bubble--color: var(--webchat__color--bubble);
+ --webchat__bubble--nub-size: var(--webchat__size--bubble-nub);
+ --webchat__bubble--nub-space: var(--webchat__space--bubble-nub);
+
+ --webchat__bubble--nub-border-radius: min(
+ var(--webchat__bubble--border-radius),
+ calc(var(--webchat__bubble--nub-space) * -1)
+ );
+ --webchat__bubble--nub-inset-block: auto calc(-1 * var(--webchat__bubble--nub-space));
+ --webchat__bubble--nub-inset-inline: calc(
+ var(--webchat__bubble--border-width) -
+ var(--webchat__bubble--nub-size) +
+ var(--webchat__padding--regular)
+ )
+ auto;
+
+ &.bubble--nub-on-top {
+ --webchat__bubble--nub-border-radius: min(var(--webchat__bubble--border-radius), var(--webchat__bubble--nub-space));
+ --webchat__bubble--nub-inset-block: var(--webchat__bubble--nub-space) auto;
+ }
+ &.bubble--from-user {
+ --webchat__bubble--background: var(--webchat__background--bubble-user);
+ --webchat__bubble--border-color: var(--webchat__border-color--bubble-user);
+ --webchat__bubble--border-radius: var(--webchat__border-radius--bubble-user);
+ --webchat__bubble--border-style: var(--webchat__border-style--bubble-user);
+ --webchat__bubble--border-width: var(--webchat__border-width--bubble-user);
+ --webchat__bubble--border-width: var(--webchat__border-width--bubble-user);
+ --webchat__bubble--color: var(--webchat__color--bubble-user);
+ --webchat__bubble--nub-size: var(--webchat__size--bubble-nub-user);
+ --webchat__bubble--nub-space: var(--webchat__space--bubble-nub-user);
+
+ --webchat__bubble--nub-inset-inline: auto
+ calc(var(--webchat__bubble--border-width) - var(--webchat__bubble--nub-size) + var(--webchat__padding--regular));
+ }
+
+ .bubble__content {
+ flex-grow: 1;
+ margin-inline: 0;
+ /* This is for hiding content outside of the bubble, for example, content outside of border radius */
+ overflow: hidden;
+ transition-duration: var(--webchat__transition-duration);
+ transition-property: margin-inline-start, margin-inline-end;
+ word-break: var(--webchat__word-break--message-activity);
+ }
+
+ .bubble__nub {
+ overflow: hidden; /* This style is for IE11 because it doesn't respect SVG viewport */
+ position: absolute;
+ }
+
+ &:dir(rtl) .bubble__nub {
+ transform: scale(-1, 1);
+ }
+
+ .bubble__nub-pad {
+ flex-shrink: 0;
+ transition-duration: var(--webchat__transition-duration);
+ transition-property: width;
+ width: 0;
+ }
+
+ &.bubble--hide-nub,
+ &.bubble--show-nub {
+ .bubble__nub-pad {
+ width: var(--webchat__padding--regular);
+ }
+ }
+
+ .bubble__content {
+ background: var(--webchat__bubble--background);
+ border-color: var(--webchat__bubble--border-color);
+ border-radius: var(--webchat__bubble--border-radius);
+ border-style: var(--webchat__bubble--border-style);
+ border-width: var(--webchat__bubble--border-width);
+ color: var(--webchat__bubble--color);
+ min-height: calc(var(--webchat__min-height--bubble) - var(--webchat__bubble--border-width) * 2);
+ }
+
+ .bubble__nub {
+ height: var(--webchat__bubble--nub-size);
+ inset-block: var(--webchat__bubble--nub-inset-block);
+ inset-inline: var(--webchat__bubble--nub-inset-inline);
+ width: var(--webchat__bubble--nub-size);
+ }
+
+ .bubble__nub-outline {
+ fill: var(--webchat__bubble--background);
+ stroke-width: var(--webchat__bubble--border-width);
+ stroke: var(--webchat__bubble--border-color);
+ }
+
+ /* Bot bubble styles (not from user) - nub on inline-start side */
+ &:not(.bubble--from-user) {
+ /* Adjust border radius when nub is shown */
+ &.bubble--show-nub {
+ &:not(.bubble--nub-on-top) .bubble__content {
+ border-end-start-radius: var(--webchat__bubble--nub-border-radius);
+ }
+
+ &.bubble--nub-on-top .bubble__content {
+ border-start-start-radius: var(--webchat__bubble--nub-border-radius);
+ }
+ }
+ }
+
+ /* User bubble styles - nub on inline-end side */
+ &.bubble--from-user {
+ flex-direction: row-reverse;
+
+ /* Adjust border radius when nub is shown */
+ &.bubble--show-nub {
+ &:not(.bubble--nub-on-top) .bubble__content {
+ border-end-end-radius: var(--webchat__bubble--nub-border-radius);
+ }
+
+ &.bubble--nub-on-top .bubble__content {
+ border-start-end-radius: var(--webchat__bubble--nub-border-radius);
+ }
+ }
+ }
+}
diff --git a/packages/component/src/Activity/Bubble.tsx b/packages/component/src/Activity/Bubble.tsx
index e71c8cf317..9e46346c43 100644
--- a/packages/component/src/Activity/Bubble.tsx
+++ b/packages/component/src/Activity/Bubble.tsx
@@ -1,36 +1,19 @@
/* eslint no-magic-numbers: ["error", { "ignore": [-1, 0, 1, 2, 10] }] */
import { reactNode, validateProps } from '@msinternal/botframework-webchat-react-valibot';
+import { useStyles } from '@msinternal/botframework-webchat-styles/react';
import { hooks } from 'botframework-webchat-api';
-import classNames from 'classnames';
+import cx from 'classnames';
import React, { memo } from 'react';
import { boolean, literal, object, optional, pipe, readonly, string, union, type InferInput } from 'valibot';
-import { useStyleToEmotionObject } from '../hooks/internal/styleToEmotionObject';
-import useStyleSet from '../hooks/useStyleSet';
import isZeroOrPositive from '../Utils/isZeroOrPositive';
-const { useDirection, useStyleOptions } = hooks;
+import styles from './Bubble.module.css';
-const ROOT_STYLE = {
- '&.webchat__bubble': {
- display: 'flex',
- position: 'relative',
+const { useStyleOptions } = hooks;
- '& .webchat__bubble__nub-pad': {
- flexShrink: 0
- },
-
- '& .webchat__bubble__content': {
- flexGrow: 1,
-
- // This is for hiding content outside of the bubble, for example, content outside of border radius
- overflow: 'hidden'
- }
- }
-};
-
-function acuteNubSVG(nubSize, strokeWidth, side, upSideDown = false) {
+function acuteNubSVG(nubSize, strokeWidth, side, upSideDown = false, classNames) {
if (typeof nubSize !== 'number') {
return false;
}
@@ -51,13 +34,13 @@ function acuteNubSVG(nubSize, strokeWidth, side, upSideDown = false) {
return (
);
@@ -85,8 +68,6 @@ function Bubble(props: BubbleProps) {
nub = false
} = validateProps(bubblePropsSchema, props);
- const [{ bubble: bubbleStyleSet }] = useStyleSet();
- const [direction] = useDirection();
const [
{
bubbleBorderWidth,
@@ -97,7 +78,7 @@ function Bubble(props: BubbleProps) {
bubbleFromUserNubOffset
}
] = useStyleOptions();
- const rootClassName = useStyleToEmotionObject()(ROOT_STYLE) + '';
+ const classNames = useStyles(styles);
const { borderWidth, nubOffset, nubSize, side } = fromUser
? {
@@ -116,23 +97,20 @@ function Bubble(props: BubbleProps) {
return (
-
-
{children}
- {nub === true && acuteNubSVG(nubSize, borderWidth, side, !isZeroOrPositive(nubOffset))}
+
+
{children}
+ {nub === true && acuteNubSVG(nubSize, borderWidth, side, !isZeroOrPositive(nubOffset), classNames)}
);
}
diff --git a/packages/component/src/Activity/CarouselFilmStrip.js b/packages/component/src/Activity/CarouselFilmStrip.js
index aabe0377a9..48e79421e3 100644
--- a/packages/component/src/Activity/CarouselFilmStrip.js
+++ b/packages/component/src/Activity/CarouselFilmStrip.js
@@ -1,8 +1,9 @@
/* eslint complexity: ["error", 30] */
import { hooks } from 'botframework-webchat-api';
+import { useStyles } from '@msinternal/botframework-webchat-styles/react';
import { useItemContainerCallbackRef, useScrollableCallbackRef } from 'react-film';
-import classNames from 'classnames';
+import cx from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
@@ -11,89 +12,10 @@ import CarouselFilmStripAttachment from './CarouselFilmStripAttachment';
import isZeroOrPositive from '../Utils/isZeroOrPositive';
import ScreenReaderText from '../ScreenReaderText';
import textFormatToContentType from '../Utils/textFormatToContentType';
-import useStyleSet from '../hooks/useStyleSet';
-import { useStyleToEmotionObject } from '../hooks/internal/styleToEmotionObject';
-const { useAvatarForBot, useAvatarForUser, useDirection, useLocalizer, useStyleOptions } = hooks;
+import styles from './CarouselFilmStrip.module.css';
-const ROOT_STYLE = {
- '&.webchat__carousel-filmstrip': {
- display: 'flex',
- flexDirection: 'column',
- MsOverflowStyle: 'none',
- overflowX: 'scroll',
- overflowY: 'hidden',
- position: 'relative', // This is to keep screen reader text in the destinated area.
- touchAction: 'manipulation',
- WebkitOverflowScrolling: 'touch',
-
- '&::-webkit-scrollbar': {
- display: 'none'
- },
-
- '& .webchat__carousel-filmstrip__alignment-pad': {
- flexShrink: 0
- },
-
- '& .webchat__carousel-filmstrip-attachment': {
- flex: 1
- },
-
- '& .webchat__carousel-filmstrip__attachments': {
- display: 'flex',
- listStyleType: 'none',
- margin: 0,
- padding: 0
- },
-
- '& .webchat__carousel-filmstrip__avatar': {
- flexShrink: 0
- },
-
- '& .webchat__carousel-filmstrip__avatar-gutter': {
- display: 'flex',
- flexDirection: 'column',
- flexShrink: 0
- },
-
- '& .webchat__carousel-filmstrip__complimentary': {
- display: 'flex'
- },
-
- '& .webchat__carousel-filmstrip__complimentary-content': {
- display: 'flex',
- flexGrow: 1,
- flexDirection: 'column'
- },
-
- '& .webchat__carousel-filmstrip__content': {
- display: 'flex',
- flexGrow: 1,
- flexDirection: 'column'
- },
-
- '& .webchat__carousel-filmstrip__filler': {
- flexGrow: 10000,
- flexShrink: 1
- },
-
- '& .webchat__carousel-filmstrip__main': {
- display: 'flex'
- },
-
- '& .webchat__carousel-filmstrip__message': {
- display: 'flex'
- },
-
- '& .webchat__carousel-filmstrip__nub-pad': {
- flexShrink: 0
- },
-
- '& .webchat__carousel-filmstrip__status': {
- display: 'flex'
- }
- }
-};
+const { useAvatarForBot, useAvatarForUser, useLocalizer, useStyleOptions } = hooks;
const CarouselFilmStrip = ({
activity,
@@ -105,13 +27,11 @@ const CarouselFilmStrip = ({
showCallout
}) => {
const [{ bubbleNubOffset, bubbleNubSize, bubbleFromUserNubOffset, bubbleFromUserNubSize }] = useStyleOptions();
- const [{ carouselFilmStrip: carouselFilmStripStyleSet }] = useStyleSet();
const [{ initials: botInitials }] = useAvatarForBot();
const [{ initials: userInitials }] = useAvatarForUser();
- const [direction] = useDirection();
const localize = useLocalizer();
- const rootClassName = useStyleToEmotionObject()(ROOT_STYLE) + '';
const showActivityStatus = typeof renderActivityStatus === 'function';
+ const classNames = useStyles(styles);
const itemContainerCallbackRef = useItemContainerCallbackRef();
const scrollableCallbackRef = useScrollableCallbackRef();
@@ -152,33 +72,32 @@ const CarouselFilmStrip = ({
return (
-
-
{showAvatar && renderAvatar({ activity })}
-
+
+
+ {showAvatar && renderAvatar({ activity })}
+
+
{!!activityDisplayText && (
-
+
@@ -190,14 +109,14 @@ const CarouselFilmStrip = ({
}
})}
-
+
)}
-
-
-
+
+
+
{attachments.map((attachment, index) => (
@@ -220,12 +139,12 @@ const CarouselFilmStrip = ({
-
+
{showActivityStatus && (
-
-
-
+
+
+
{renderActivityStatus({ hideTimestamp })}
)}
diff --git a/packages/component/src/Activity/CarouselFilmStrip.module.css b/packages/component/src/Activity/CarouselFilmStrip.module.css
new file mode 100644
index 0000000000..db44ceea78
--- /dev/null
+++ b/packages/component/src/Activity/CarouselFilmStrip.module.css
@@ -0,0 +1,155 @@
+:global(.webchat) .carousel-filmstrip {
+ display: flex;
+ flex-direction: column;
+ -ms-overflow-style: none;
+ overflow-x: scroll;
+ overflow-y: hidden;
+ position: relative; /* This is to keep screen reader text in the destinated area. */
+ touch-action: manipulation;
+ -webkit-overflow-scrolling: touch;
+
+ /* Browser quirks: Firefox has no way to hide scrollbar and while keeping it in function */
+ /* https://developer.mozilla.org/en-US/docs/Web/CSS/overflow */
+ @supports (-moz-appearance: none) {
+ margin-block-end: -17px;
+ }
+
+ &::-webkit-scrollbar {
+ display: none;
+ }
+
+ .carousel-filmstrip__bubble {
+ max-width: var(--webchat__max-width--message-bubble);
+ min-width: var(--webchat__min-width--message-bubble);
+ transition-duration: var(--webchat__transition-duration);
+ transition-property: max-width;
+ }
+
+ &.carousel-filmstrip--hide-nub,
+ &.carousel-filmstrip--show-nub,
+ &.carousel-filmstrip--hide-avatar,
+ &.carousel-filmstrip--show-avatar {
+ .carousel-filmstrip__bubble {
+ max-width: calc(var(--webchat__max-width--message-bubble) + var(--webchat__padding--regular));
+ }
+ }
+
+ .carousel-filmstrip__alignment-pad {
+ flex-shrink: 0;
+ transition-duration: var(--webchat__transition-duration);
+ transition-property: width;
+ width: var(--webchat__padding--regular);
+ }
+
+ &.carousel-filmstrip--extra-trailing .carousel-filmstrip__alignment-pad {
+ width: calc(var(--webchat__padding--regular) * 2);
+ }
+
+ &:not(.carousel-filmstrip--no-message) .carousel-filmstrip__attachments {
+ margin-block-start: var(--webchat__padding--regular);
+ }
+
+ .carousel-filmstrip__attachments {
+ display: flex;
+ list-style-type: none;
+ margin: 0;
+ padding: 0;
+ }
+
+ .carousel-filmstrip__avatar {
+ flex-shrink: 0;
+ }
+
+ .carousel-filmstrip__avatar-gutter {
+ align-items: flex-end;
+ display: flex;
+ flex-direction: column;
+ flex-shrink: 0;
+ transition-duration: var(--webchat__transition-duration);
+ transition-property: width;
+ }
+
+ &.carousel-filmstrip--hide-avatar,
+ &.carousel-filmstrip--show-avatar {
+ .carousel-filmstrip__avatar-gutter {
+ width: var(--webchat__size--avatar);
+ }
+ }
+
+ &:not(.carousel-filmstrip--top-callout) .carousel-filmstrip__avatar-gutter {
+ justify-content: flex-end;
+ }
+
+ .carousel-filmstrip__complimentary {
+ display: flex;
+ }
+
+ .carousel-filmstrip__complimentary-content {
+ display: flex;
+ flex-grow: 1;
+ flex-direction: column;
+ }
+
+ .carousel-filmstrip__content {
+ display: flex;
+ flex-grow: 1;
+ flex-direction: column;
+ }
+
+ .carousel-filmstrip__filler {
+ flex-grow: 10000;
+ flex-shrink: 1;
+ }
+
+ .carousel-filmstrip__main {
+ display: flex;
+ }
+
+ .carousel-filmstrip__message {
+ display: flex;
+ }
+
+ .carousel-filmstrip__nub-pad {
+ flex-shrink: 0;
+ transition-duration: var(--webchat__transition-duration);
+ transition-property: width;
+ width: 0;
+ }
+
+ &.carousel-filmstrip--hide-avatar,
+ &.carousel-filmstrip--show-avatar,
+ &.carousel-filmstrip--hide-nub,
+ &.carousel-filmstrip--show-nub {
+ .carousel-filmstrip__nub-pad {
+ width: var(--webchat__padding--regular);
+ }
+ }
+
+ .carousel-filmstrip__status {
+ display: flex;
+ }
+
+ .carousel-filmstrip__avatar-gutter {
+ margin-inline-start: var(--webchat__padding--regular);
+ }
+
+ &.carousel-filmstrip .carousel-filmstrip__attachments {
+ margin-inline-start: calc(-1 * var(--webchat__padding--regular));
+ }
+
+ &.carousel-filmstrip--hide-avatar,
+ &.carousel-filmstrip--show-avatar {
+ .carousel-filmstrip__attachments {
+ margin-inline-start: calc(-1 * (var(--webchat__size--avatar) + var(--webchat__padding--regular) * 2));
+ }
+ }
+
+ &.carousel-filmstrip--hide-nub,
+ &.carousel-filmstrip--show-nub {
+ &:not(.carousel-filmstrip--hide-avatar.carousel-filmstrip--show-avatar) {
+ .carousel-filmstrip__attachments {
+ margin-inline-start: calc(-1 * var(--webchat__padding--regular) * 2);
+ }
+ }
+ }
+}
diff --git a/packages/component/src/Activity/CarouselFilmStripAttachment.js b/packages/component/src/Activity/CarouselFilmStripAttachment.js
index 853ba916f6..fc00369cdf 100644
--- a/packages/component/src/Activity/CarouselFilmStripAttachment.js
+++ b/packages/component/src/Activity/CarouselFilmStripAttachment.js
@@ -1,13 +1,15 @@
import { hooks } from 'botframework-webchat-api';
-import classNames from 'classnames';
+import { useStyles } from '@msinternal/botframework-webchat-styles/react';
+import cx from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import Bubble from './Bubble';
import ScreenReaderText from '../ScreenReaderText';
-import useStyleSet from '../hooks/useStyleSet';
-const { useDirection, useLocalizer } = hooks;
+import styles from './CarouselFilmStripAttachment.module.css';
+
+const { useLocalizer } = hooks;
const CarouselFilmStripAttachment = ({
activity,
@@ -21,27 +23,24 @@ const CarouselFilmStripAttachment = ({
showAvatar,
showNub
}) => {
- const [direction] = useDirection();
const localize = useLocalizer();
- const [{ carouselFilmStripAttachment: carouselFilmStripAttachmentStyleSet }] = useStyleSet();
+ const classNames = useStyles(styles);
const attachedAlt = localize(fromUser ? 'ACTIVITY_YOU_ATTACHED_ALT' : 'ACTIVITY_BOT_ATTACHED_ALT');
return (
{renderAttachment({ activity, attachment })}
-
+
);
diff --git a/packages/component/src/Activity/CarouselFilmStripAttachment.module.css b/packages/component/src/Activity/CarouselFilmStripAttachment.module.css
new file mode 100644
index 0000000000..454851a066
--- /dev/null
+++ b/packages/component/src/Activity/CarouselFilmStripAttachment.module.css
@@ -0,0 +1,38 @@
+:global(.webchat) .carousel-filmstrip-attachment {
+ max-width: var(--webchat__max-width--attachment-bubble);
+ min-width: var(--webchat__min-width--attachment-bubble);
+ padding-inline-start: var(--webchat__padding--regular);
+ transition-duration: var(--webchat__transition-duration);
+ transition-property: max-width, min-width;
+
+ &:focus {
+ outline: 0;
+ }
+
+ &:focus .carousel-filmstrip-attachment--focus {
+ border-color: var(--webchat__color--transcript-visual-keyboard-indicator);
+ border-style: var(--webchat__border-style--transcript-visual-keyboard-indicator);
+ border-width: var(--webchat__border-width--transcript-visual-keyboard-indicator);
+ box-sizing: border-box;
+ height: calc(100% - var(--webchat__border-width--transcript-visual-keyboard-indicator));
+ inset-block-start: 0;
+ inset-inline-start: 0;
+ pointer-events: none;
+ position: absolute;
+ width: calc(100% - var(--webchat__border-width--transcript-visual-keyboard-indicator));
+ }
+
+ &.carousel-filmstrip-attachment--hide-avatar,
+ &.carousel-filmstrip-attachment--show-avatar {
+ &:first-child {
+ padding-inline-start: calc(var(--webchat__size--avatar) + var(--webchat__padding--regular) * 2);
+ }
+ }
+
+ &.carousel-filmstrip-attachment--hide-nub,
+ &.carousel-filmstrip-attachment--show-nub {
+ &:not(.carousel-filmstrip-attachment--hide-avatar.carousel-filmstrip-attachment--show-avatar):first-child {
+ padding-inline-start: calc(var(--webchat__padding--regular) * 2);
+ }
+ }
+}
diff --git a/packages/component/src/Activity/CarouselLayout.js b/packages/component/src/Activity/CarouselLayout.js
index 1e5d3d9e74..10651e5a86 100644
--- a/packages/component/src/Activity/CarouselLayout.js
+++ b/packages/component/src/Activity/CarouselLayout.js
@@ -1,3 +1,4 @@
+import { useStyles } from '@msinternal/botframework-webchat-styles/react';
import { hooks } from 'botframework-webchat-api';
import {
Composer as FilmComposer,
@@ -8,23 +9,16 @@ import {
useStyleSetClassNames as useReactFilmStyleSetClassNames
} from 'react-film';
-import classNames from 'classnames';
+import cx from 'classnames';
import PropTypes from 'prop-types';
import React, { memo, useMemo } from 'react';
import CarouselFilmStrip from './CarouselFilmStrip';
import useNonce from '../hooks/internal/useNonce';
-import useStyleSet from '../hooks/useStyleSet';
-import { useStyleToEmotionObject } from '../hooks/internal/styleToEmotionObject';
-const { useDirection, useLocalizer, useStyleOptions } = hooks;
+import styles from './CarouselLayout.module.css';
-const ROOT_STYLE = {
- '&.webchat__carousel-layout': {
- overflow: 'hidden',
- position: 'relative'
- }
-};
+const { useDirection, useLocalizer, useStyleOptions } = hooks;
const CarouselLayoutCore = ({
activity,
@@ -34,7 +28,6 @@ const CarouselLayoutCore = ({
renderAvatar,
showCallout
}) => {
- const [{ carouselFlipper: carouselFlipperStyleSet }] = useStyleSet();
const [{ root: filmRootClassName }] = useReactFilmStyleSetClassNames();
const [direction] = useDirection();
const [scrollBarWidth] = useScrollBarWidth();
@@ -42,7 +35,7 @@ const CarouselLayoutCore = ({
const leftSideFlipper = direction === 'rtl' ? '>' : '<';
const localize = useLocalizer();
const rightSideFlipper = direction === 'rtl' ? '<' : '>';
- const rootClassName = useStyleToEmotionObject()(ROOT_STYLE) + '';
+ const classNames = useStyles(styles);
const nextAlt = localize('CAROUSEL_FLIPPER_NEXT_ALT');
const previousAlt = localize('CAROUSEL_FLIPPER_PREVIOUS_ALT');
@@ -51,10 +44,8 @@ const CarouselLayoutCore = ({
const rightFlipperAriaLabel = direction === 'rtl' ? previousAlt : nextAlt;
return (
-
-
+
+
;
const SayAlt = (props: SayAltProps) => {
const { speak } = validateProps(sayAltPropsSchema, props);
- const rootClassName = useStyleToEmotionObject()(ROOT_STYLE) + '';
+ const classNames = useStyles(styles);
- return !!speak && {speak};
+ return !!speak && {speak};
};
export default memo(SayAlt);
diff --git a/packages/component/src/Activity/StackedLayoutRoot.tsx b/packages/component/src/Activity/StackedLayoutRoot.tsx
index 09e12b9855..19e0499d1c 100644
--- a/packages/component/src/Activity/StackedLayoutRoot.tsx
+++ b/packages/component/src/Activity/StackedLayoutRoot.tsx
@@ -47,7 +47,7 @@ const StackedLayoutRoot = memo((props: StackedLayoutRootProps) => {
return (
>;
export default CustomPropertyNames;
diff --git a/packages/fluent-theme/src/components/activity/PartGroupingDecorator.module.css b/packages/fluent-theme/src/components/activity/PartGroupingDecorator.module.css
index f5f4bc8c7d..4f9331e644 100644
--- a/packages/fluent-theme/src/components/activity/PartGroupingDecorator.module.css
+++ b/packages/fluent-theme/src/components/activity/PartGroupingDecorator.module.css
@@ -43,7 +43,7 @@
/* #endregion */
/* #region Generated badge & attachments */
- :global(.stacked-layout .webchat__bubble .webchat__text-content__generated-badge) {
+ :global(.stacked-layout .bubble .webchat__text-content__generated-badge) {
align-items: center;
align-self: flex-start;
background-color: var(--webchat-colorNeutralBackground5);
@@ -70,7 +70,7 @@
}
}
- :global(.stacked-layout .webchat__bubble .webchat__fileContent__badge) {
+ :global(.stacked-layout .bubble .webchat__fileContent__badge) {
cursor: default;
font-size: var(--webchat-fontSizeBase300);
line-height: var(--webchat-lineHeightBase300);
@@ -86,7 +86,7 @@
}
}
- :global(.stacked-layout .webchat__bubble .webchat__fileContent__downloadIcon) {
+ :global(.stacked-layout .bubble .webchat__fileContent__downloadIcon) {
color: var(--webchat-colorBrandForegroundLink);
}
/* #endregion */
@@ -378,7 +378,7 @@
display: none;
}
- :global(.part-grouping-decorator .webchat__bubble .webchat__bubble__content) {
+ :global(.part-grouping-decorator .bubble .bubble__content) {
box-sizing: content-box;
gap: var(--webchat-spacingVerticalXS);
}
@@ -416,7 +416,7 @@
margin-block-start: var(--webchat-part-grouping__message-status--space-block);
}
- :global(.stacked-layout .stacked-layout__content:has(.webchat__bubble)) {
+ :global(.stacked-layout .stacked-layout__content:has(.bubble)) {
max-width: 100%;
overflow: visible;
}
@@ -427,20 +427,30 @@
}
/* #endregion */
+ /* #region Carousel layout */
+ :global(.carousel-layout) {
+ :global(.carousel-filmstrip__avatar-gutter),
+ :global(.carousel-filmstrip__alignment-pad) {
+ margin: 0;
+ width: var(--webchat-spacingHorizontalMNudge);
+ }
+ }
+ /* #endregion */
+
/* #region Bubble surface */
- :global(.stacked-layout .webchat__bubble) {
+ :global(.stacked-layout .bubble) {
max-width: min(var(--webchat-part-grouping__bubble--max-width), 100%);
min-width: var(--webchat-part-grouping__bubble--min-width);
overflow: visible;
}
- :global(.stacked-layout .webchat__bubble):has(:global(.activity-loader)) :global(.webchat__bubble__content) {
+ :global(.stacked-layout .bubble):has(:global(.activity-loader)) :global(.bubble__content) {
background: unset;
box-shadow: unset;
border-color: transparent;
}
- :global(.stacked-layout .webchat__bubble .webchat__bubble__content) {
+ :global(.stacked-layout .bubble .bubble__content) {
background-color: var(--webchat-part-grouping__bubble-content--background);
border-color: var(--webchat-part-grouping__bubble--border-color);
border-radius: var(--webchat-part-grouping__bubble--border-radius);
@@ -452,7 +462,7 @@
min-height: var(--webchat-part-grouping__bubble--min-height);
}
- :global(.stacked-layout .webchat__bubble .webchat__text-content) {
+ :global(.stacked-layout .bubble .webchat__text-content) {
font-size: var(--webchat__font-size--medium);
line-height: var(--webchat__line-height--medium);
min-height: auto;
@@ -469,7 +479,7 @@
/* #endregion */
/* #region Code blocks */
- :global(.stacked-layout .webchat__bubble .code-block-content) {
+ :global(.stacked-layout .bubble .code-block-content) {
border-radius: var(--webchat-borderRadiusXLarge);
border: var(--webchat-strokeWidthThin) solid var(--webchat-colorNeutralStroke1);
font-size: var(--webchat-fontSizeBase300);
@@ -500,7 +510,7 @@
/* #endregion */
/* #region Collapsible content */
- :global(.stacked-layout .webchat__bubble .collapsible-content) {
+ :global(.stacked-layout .bubble .collapsible-content) {
:global(.collapsible-content__summary) {
&:focus-visible {
border-radius: var(--webchat-borderRadiusSmall);
@@ -586,7 +596,7 @@
}
}
- :global(.webchat__bubble) {
+ :global(.bubble) {
position: static;
}
@@ -594,7 +604,7 @@
border-radius: var(--webchat-part-grouping__bubble--border-radius);
}
- :global(.webchat__bubble .webchat__bubble__content) {
+ :global(.bubble .bubble__content) {
margin-block: calc(var(--webchat-spacingVerticalS) * -1);
padding-block: var(--webchat-spacingVerticalS);
}
@@ -630,11 +640,18 @@
position: static;
}
- :global(.webchat__bubble) {
+ :global(.carousel-layout) {
+ :global(.carousel-filmstrip__avatar-gutter),
+ :global(.carousel-filmstrip__alignment-pad) {
+ width: 0;
+ }
+ }
+
+ :global(.bubble) {
position: static;
width: var(--webchat-part-grouping__bubble--max-width);
- :global(.webchat__bubble__content .adaptive-card-renderer > .ac-adaptiveCard) {
+ :global(.bubble__content .adaptive-card-renderer > .ac-adaptiveCard) {
font-family: var(--webchat-fontFamilyBase);
/* Remove double padding for adaptive cards inside bubble */
padding: unset !important;
@@ -642,14 +659,14 @@
}
&:not(.part-grouping-decorator--group) {
- :global(.webchat__bubble__content) {
+ :global(.bubble__content) {
display: flex;
flex-direction: column;
gap: var(--webchat-spacingVerticalS);
}
}
- :global(.webchat__bubble .collapsible-content .collapsible-content__content) {
+ :global(.bubble .collapsible-content .collapsible-content__content) {
margin-block: var(--webchat-spacingVerticalNone);
:global(.stacked-layout__attachment-list) {
@@ -689,7 +706,7 @@
}
}
- :global(.webchat__bubble .webchat__bubble__content) {
+ :global(.bubble .bubble__content) {
margin-block: calc(var(--webchat-spacingVerticalS) * -1);
padding-block: var(--webchat-spacingVerticalS);
@@ -713,19 +730,18 @@
}
}
- &.part-grouping-decorator--from-bot:not(.part-grouping-decorator--group) :global(.webchat__bubble__content) {
+ &.part-grouping-decorator--from-bot:not(.part-grouping-decorator--group) :global(.bubble__content) {
box-sizing: content-box;
padding-block: var(--webchat-spacingVerticalM);
padding-inline: var(--webchat-spacingHorizontalL);
}
- &:not(.part-grouping-decorator--group) :global(.stacked-layout .webchat__bubble .webchat__bubble__content) {
+ &:not(.part-grouping-decorator--group) :global(.stacked-layout .bubble .bubble__content) {
border-width: var(--webchat-strokeWidthThin);
border-style: solid;
}
- &:not(.part-grouping-decorator--group)
- :global(.webchat__bubble:not(.webchat__bubble--from-user) .webchat__bubble__content) {
+ &:not(.part-grouping-decorator--group) :global(.bubble:not(.bubble--from-user) .bubble__content) {
anchor-name: --webchat-flair;
max-width: unset;
}
@@ -738,19 +754,19 @@
order: -1;
}
- &:has(:global(.webchat__bubble--from-user)) {
- :global(.webchat__bubble) {
+ &:has(:global(.bubble--from-user)) {
+ :global(.bubble) {
margin-block-end: var(--webchat-spacingVerticalM);
}
}
- &:has(:global(.webchat__bubble.webchat__bubble--from-user)),
+ &:has(:global(.bubble.bubble--from-user)),
&:has(:global(.pre-chat-message-activity)),
&:has(:global(.liner-message-activity)) {
padding-inline-start: var(--webchat-spacingHorizontalNone);
}
- :global(.webchat__bubble .webchat__text-content .webchat__text-content__generated-badge) {
+ :global(.bubble .webchat__text-content .webchat__text-content__generated-badge) {
display: none;
}
diff --git a/packages/fluent-theme/src/components/theme/Theme.module.css b/packages/fluent-theme/src/components/theme/Theme.module.css
index 4d8a2caf0c..805cf2f13b 100644
--- a/packages/fluent-theme/src/components/theme/Theme.module.css
+++ b/packages/fluent-theme/src/components/theme/Theme.module.css
@@ -324,12 +324,12 @@
}
/* Transcript activity focused directly */
- > :global(.webchat__focus-trap > .webchat__basic-transcript__activity-body .webchat__bubble) {
+ > :global(.webchat__focus-trap > .webchat__basic-transcript__activity-body .bubble) {
display: grid;
grid-template-areas: 'focused-content';
position: static;
- :global(.webchat__bubble__content) {
+ :global(.bubble__content) {
grid-area: focused-content;
}
@@ -358,7 +358,7 @@
position: absolute;
}
- :global(.activity-decorator .webchat__bubble .webchat__bubble__nub-pad) {
+ :global(.activity-decorator .bubble .bubble__nub-pad) {
display: none;
}
diff --git a/packages/test/page-object/src/globals/pageElements/activityContents.js b/packages/test/page-object/src/globals/pageElements/activityContents.js
index 0b4f8e4b4a..bcdcf961cd 100644
--- a/packages/test/page-object/src/globals/pageElements/activityContents.js
+++ b/packages/test/page-object/src/globals/pageElements/activityContents.js
@@ -1,5 +1,5 @@
import activities from './activities';
export default function getActivityContents() {
- return [].map.call(activities(), element => element.querySelector('.webchat__bubble__content'));
+ return [].map.call(activities(), element => element.querySelector('.bubble__content'));
}
diff --git a/packages/test/page-object/src/globals/pageElements/activityStatuses.js b/packages/test/page-object/src/globals/pageElements/activityStatuses.js
index 00f6b8345e..fe98fd805b 100644
--- a/packages/test/page-object/src/globals/pageElements/activityStatuses.js
+++ b/packages/test/page-object/src/globals/pageElements/activityStatuses.js
@@ -2,6 +2,6 @@ import activities from './activities';
export default function getActivityStatuses() {
return [].map.call(activities(), element =>
- element.querySelector('.webchat__carousel-filmstrip__status, .stacked-layout__status')
+ element.querySelector('.carousel-filmstrip__status, .stacked-layout__status')
);
}