Skip to content

Commit 32c4e5d

Browse files
authored
CSS Modules: convert remaining activity components part 2 (#5669)
* CSS Modules: migrate ActivityFeedback components * CSS Modules: migrate ActivityStatus * Changelog
1 parent 550befa commit 32c4e5d

File tree

13 files changed

+185
-53
lines changed

13 files changed

+185
-53
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ Breaking changes in this release:
313313
- 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)
314314
- 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)
315315
- 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)
316-
- Converted remaining activity components to CSS Modules, in PR [#5668](https://github.com/microsoft/BotFramework-WebChat/pull/5668), by [@OEvgeny](https://github.com/OEvgeny)
316+
- Converted remaining activity components to CSS Modules, in PR [#5668](https://github.com/microsoft/BotFramework-WebChat/pull/5668), in PR [#5669](https://github.com/microsoft/BotFramework-WebChat/pull/5669), by [@OEvgeny](https://github.com/OEvgeny)
317317

318318
### Deprecated
319319

__tests__/html2/citation/claimInterpreter/dangerousLink.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565

6666
expect(markdownLinks[0].getAttribute('href')).toBe('https://aka.ms/claim');
6767

68-
const claimInterpreterElement = pageElements.activities()[0].querySelector('.webchat__activity-status__originator');
68+
const claimInterpreterElement = pageElements.activities()[0].querySelector('.activity-status__originator');
6969

7070
expect(claimInterpreterElement).toHaveProperty('tagName', 'SPAN');
7171
expect(claimInterpreterElement).toHaveProperty('textContent', 'Surfaced with Azure OpenAI');
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
:global(.webchat) .thumb-button {
2+
align-items: center;
3+
border-radius: 2px;
4+
box-sizing: content-box;
5+
display: grid;
6+
grid-template-areas: 'main';
7+
height: 16px;
8+
width: 16px;
9+
10+
.thumb-button__input {
11+
appearance: none;
12+
background: transparent;
13+
border: 0;
14+
grid-area: main;
15+
height: 16px;
16+
margin: 0;
17+
opacity: 0;
18+
padding: 0;
19+
width: 16px;
20+
21+
&:active {
22+
background: #edebe9;
23+
}
24+
}
25+
26+
&:has(.thumb-button__input:focus-visible) {
27+
outline: solid 1px #605e5c; /* <input> has opacity of 0, we need to set the outline in the container. */
28+
}
29+
30+
.thumb-button__image {
31+
color: var(--webchat__color--accent);
32+
grid-area: main;
33+
justify-self: center; /* Unsure why "justifyContent" doesn't work. */
34+
pointer-events: none;
35+
visibility: hidden;
36+
width: 14px;
37+
38+
&.thumb-button__image--is-stroked {
39+
visibility: unset;
40+
}
41+
}
42+
43+
&:has(.thumb-button__input:is(:not([aria-disabled='true']):hover, :checked, [aria-pressed='true'])) {
44+
.thumb-button__image {
45+
&.thumb-button__image--is-stroked {
46+
visibility: hidden;
47+
}
48+
49+
&.thumb-button__image--is-filled {
50+
visibility: unset;
51+
}
52+
}
53+
}
54+
55+
&.thumb-button--large {
56+
border: 1px solid transparent;
57+
border-radius: 4px;
58+
height: 30px;
59+
width: 30px;
60+
61+
.thumb-button__input {
62+
background: currentColor;
63+
height: 30px;
64+
width: 30px;
65+
}
66+
67+
.thumb-button__image {
68+
color: currentColor;
69+
font-size: 20px;
70+
height: 1em;
71+
width: 1em;
72+
}
73+
74+
&:has(.thumb-button__input:is(:hover, :active, :checked, [aria-pressed='true'])) .thumb-button__image {
75+
color: var(--webchat__color--accent);
76+
}
77+
78+
&:has(.thumb-button__input[aria-disabled='true']) .thumb-button__image {
79+
color: var(--webchat__color--subtle);
80+
}
81+
82+
&.thumb-button--has-submitted:has(.thumb-button__input:not(:checked):not([aria-pressed='true']))
83+
.thumb-button__tooltip {
84+
display: none;
85+
}
86+
87+
&.thumb-button--has-submitted .thumb-button__tooltip {
88+
--webchat__tooltip-anchor-inline-start: 20%;
89+
}
90+
}
91+
}

packages/component/src/ActivityFeedback/private/ThumbButton.tsx

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import { validateProps } from '@msinternal/botframework-webchat-react-valibot';
2+
import { useStyles } from '@msinternal/botframework-webchat-styles/react';
23
import { hooks } from 'botframework-webchat-api';
3-
import classNames from 'classnames';
4+
import cx from 'classnames';
45
import React, { forwardRef, memo, useCallback, useMemo, type ForwardedRef, type KeyboardEventHandler } from 'react';
56
import { useRefFrom } from 'use-ref-from';
67
import { boolean, function_, literal, object, optional, pipe, readonly, string, union, type InferInput } from 'valibot';
78

8-
import useStyleSet from '../../hooks/useStyleSet';
99
import testIds from '../../testIds';
1010
import { Tooltip } from '../../Tooltip';
1111
import ThumbButtonImage from './ThumbButton.Image';
1212

13+
import styles from './ThumbButton.module.css';
14+
1315
const { useLocalizer } = hooks;
1416

1517
const thumbButtonPropsSchema = pipe(
@@ -36,9 +38,9 @@ function ThumbButton(props: ThumbButtonProps, ref: ForwardedRef<HTMLInputElement
3638
props
3739
);
3840

39-
const [{ thumbButton }] = useStyleSet();
4041
const localize = useLocalizer();
4142
const onClickRef = useRefFrom(onClick);
43+
const classNames = useStyles(styles);
4244

4345
const buttonTitle = useMemo(
4446
() => title ?? localize(direction === 'down' ? 'VOTE_DISLIKE_ALT' : 'VOTE_LIKE_ALT'),
@@ -54,25 +56,19 @@ function ThumbButton(props: ThumbButtonProps, ref: ForwardedRef<HTMLInputElement
5456

5557
return (
5658
<div
57-
className={classNames(
58-
'webchat__thumb-button',
59+
className={cx(
60+
classNames['thumb-button'],
5961
{
60-
'webchat__thumb-button--large': size === 'large',
61-
'webchat__thumb-button--has-submitted': submitted
62+
[classNames['thumb-button--large']]: size === 'large',
63+
[classNames['thumb-button--has-submitted']]: submitted
6264
},
63-
className,
64-
thumbButton + ''
65+
className
6566
)}
6667
>
6768
<input
6869
aria-disabled={disabled ? 'true' : undefined}
6970
aria-label={buttonTitle}
70-
className={classNames(
71-
'webchat__thumb-button__input',
72-
{ 'webchat__thumb-button__input--is-pressed': pressed },
73-
className,
74-
thumbButton + ''
75-
)}
71+
className={cx(classNames['thumb-button__input'], className)}
7672
data-testid={testIds.feedbackButton}
7773
name={name}
7874
onKeyDown={handleKeyDown}
@@ -90,19 +86,15 @@ function ThumbButton(props: ThumbButtonProps, ref: ForwardedRef<HTMLInputElement
9086
})}
9187
/>
9288
<ThumbButtonImage
93-
className={classNames('webchat__thumb-button__image', 'webchat__thumb-button__image--is-stroked', {
94-
'webchat__thumb-button__image--is-down': direction === 'down'
95-
})}
89+
className={cx(classNames['thumb-button__image'], classNames['thumb-button__image--is-stroked'])}
9690
direction={direction}
9791
/>
9892
<ThumbButtonImage
99-
className={classNames('webchat__thumb-button__image', 'webchat__thumb-button__image--is-filled', {
100-
'webchat__thumb-button__image--is-down': direction === 'down'
101-
})}
93+
className={cx(classNames['thumb-button__image'], classNames['thumb-button__image--is-filled'])}
10294
direction={direction}
10395
filled={true}
10496
/>
105-
<Tooltip className="webchat__thumb-button__tooltip">{buttonTitle}</Tooltip>
97+
<Tooltip className={classNames['thumb-button__tooltip']}>{buttonTitle}</Tooltip>
10698
</div>
10799
);
108100
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
:global(.webchat) .activity-status {
2+
color: var(--webchat__color--timestamp);
3+
font-family: var(--webchat__font--primary);
4+
font-size: var(--webchat__font-size--small);
5+
margin-block-start: calc(var(--webchat__padding--regular) / 2);
6+
7+
&.activity-status--slotted {
8+
display: inline-flex;
9+
gap: 4px;
10+
}
11+
}
12+
13+
:global(.webchat) .activity-status-slot {
14+
display: contents;
15+
16+
&:not(:first-child)::before {
17+
content: '|';
18+
}
19+
20+
&:empty {
21+
display: none;
22+
}
23+
}
24+
25+
:global(.webchat) .activity-status__originator {
26+
align-items: center;
27+
28+
&.activity-status__originator--has-link {
29+
color: var(--webchat__color--accent);
30+
}
31+
}

packages/component/src/ActivityStatus/OthersActivityStatus.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { warnOnce } from '@msinternal/botframework-webchat-base/utils';
2+
import { useStyles } from '@msinternal/botframework-webchat-styles/react';
23
import { hooks } from 'botframework-webchat-api';
34
import {
45
getOrgSchemaMessage,
@@ -7,27 +8,28 @@ import {
78
parseClaim,
89
type WebChatActivity
910
} from 'botframework-webchat-core';
10-
import classNames from 'classnames';
11+
import cx from 'classnames';
1112
import React, { memo, useMemo } from 'react';
1213

1314
import ActivityFeedback from '../ActivityFeedback/ActivityFeedback';
14-
import useStyleSet from '../hooks/useStyleSet';
1515
import dereferenceBlankNodes from '../Utils/JSONLinkedData/dereferenceBlankNodes';
1616
import Originator from './private/Originator';
1717
import StatusSlot from './StatusSlot';
1818
import Timestamp from './Timestamp';
1919

20+
import styles from './ActivityStatus.module.css';
21+
2022
const { useStyleOptions } = hooks;
2123

22-
type Props = Readonly<{ activity: WebChatActivity; className?: string | undefined }>;
24+
type Props = Readonly<{ activity: WebChatActivity; className?: string | undefined; slotted?: boolean }>;
2325

2426
const warnRootLevelThings = warnOnce(
2527
'Root-level things are being deprecated, please relate all things to `entities[@id=""]` instead. This feature will be removed in 2025-03-06.'
2628
);
2729

28-
const OthersActivityStatus = memo(({ activity, className }: Props) => {
30+
const OthersActivityStatus = memo(({ activity, className, slotted }: Props) => {
2931
const [{ feedbackActionsPlacement }] = useStyleOptions();
30-
const [{ sendStatus }] = useStyleSet();
32+
const classNames = useStyles(styles);
3133
const { timestamp } = activity;
3234
const graph = useMemo(() => dereferenceBlankNodes(activity.entities || []), [activity.entities]);
3335

@@ -60,7 +62,9 @@ const OthersActivityStatus = memo(({ activity, className }: Props) => {
6062
}, [graph, messageThing]);
6163

6264
return (
63-
<span className={classNames('webchat__activity-status', className, sendStatus + '')}>
65+
<span
66+
className={cx(classNames['activity-status'], { [classNames['activity-status--slotted']]: slotted }, className)}
67+
>
6468
{timestamp && (
6569
<StatusSlot>
6670
<Timestamp key="timestamp" timestamp={timestamp} />

packages/component/src/ActivityStatus/SelfActivityStatus.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1+
import { useStyles } from '@msinternal/botframework-webchat-styles/react';
12
import { type WebChatActivity } from 'botframework-webchat-core';
2-
import classNames from 'classnames';
3+
import cx from 'classnames';
34
import React, { memo } from 'react';
45

56
import Timestamp from './Timestamp';
6-
import useStyleSet from '../hooks/useStyleSet';
77

8-
type Props = Readonly<{ activity: WebChatActivity; className?: string | undefined }>;
8+
import styleClassNames from './ActivityStatus.module.css';
99

10-
const SelftActivityStatus = memo(({ activity, className }: Props) => {
11-
const [{ sendStatus }] = useStyleSet();
10+
type Props = Readonly<{ activity: WebChatActivity; className?: string | undefined; slotted?: boolean }>;
11+
12+
const SelftActivityStatus = memo(({ activity, className, slotted }: Props) => {
13+
const classNames = useStyles(styleClassNames);
1214
const { timestamp } = activity;
1315

1416
return timestamp ? (
1517
<span
16-
className={classNames('webchat__activity-status', 'webchat__activity-status--self', className, sendStatus + '')}
18+
className={cx(classNames['activity-status'], { [classNames['activity-status--slotted']]: slotted }, className)}
1719
>
1820
<Timestamp timestamp={timestamp} />
1921
</span>

packages/component/src/ActivityStatus/SendStatus/SendStatus.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import { validateProps } from '@msinternal/botframework-webchat-react-valibot';
22
import { hooks } from 'botframework-webchat-api';
3-
import classNames from 'classnames';
3+
import { useStyles } from '@msinternal/botframework-webchat-styles/react';
44
import React, { memo, useCallback } from 'react';
55
import { any, literal, object, pipe, readonly, union, type InferInput } from 'valibot';
66

77
import useFocus from '../../hooks/useFocus';
8-
import useStyleSet from '../../hooks/useStyleSet';
98
import { SENDING, SEND_FAILED, SENT } from '../../types/internal/SendStatus';
109
import SendFailedRetry from './private/SendFailedRetry';
1110

11+
import styles from '../ActivityStatus.module.css';
12+
1213
const { useLocalizer, usePostActivity } = hooks;
1314

1415
const sendStatusPropsSchema = pipe(
@@ -24,10 +25,10 @@ type SendStatusProps = InferInput<typeof sendStatusPropsSchema>;
2425
function SendStatus(props: SendStatusProps) {
2526
const { activity, sendStatus } = validateProps(sendStatusPropsSchema, props);
2627

27-
const [{ sendStatus: sendStatusStyleSet }] = useStyleSet();
2828
const focus = useFocus();
2929
const localize = useLocalizer();
3030
const postActivity = usePostActivity();
31+
const classNames = useStyles(styles);
3132

3233
const handleRetryClick = useCallback(() => {
3334
postActivity(activity);
@@ -40,7 +41,7 @@ function SendStatus(props: SendStatusProps) {
4041

4142
return (
4243
<React.Fragment>
43-
<span className={classNames('webchat__activity-status', 'webchat__activity-status--sending', sendStatusStyleSet)}>
44+
<span className={classNames['activity-status']}>
4445
{sendStatus === SENDING ? (
4546
sendingText
4647
) : sendStatus === SEND_FAILED ? (

packages/component/src/ActivityStatus/StatusSlot.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1+
import { useStyles } from '@msinternal/botframework-webchat-styles/react';
12
import React, { memo, ReactNode } from 'react';
2-
import classNames from 'classnames';
3+
import cx from 'classnames';
4+
5+
import styles from './ActivityStatus.module.css';
36

47
type Props = Readonly<{ className?: string; children?: ReactNode | undefined }>;
58

6-
const StatusSlot = ({ children, className }: Props) => (
7-
<span className={classNames('webchat__activity-status-slot', className)}>{children}</span>
8-
);
9+
const StatusSlot = ({ children, className }: Props) => {
10+
const classNames = useStyles(styles);
11+
12+
return <span className={cx(classNames['activity-status-slot'], className)}>{children}</span>;
13+
};
914

1015
StatusSlot.displayName = 'StatusSlot';
1116

0 commit comments

Comments
 (0)