Skip to content
Merged
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +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 [`[email protected]`](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 [`[email protected]`](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)
- 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)

### Deprecated

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@

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

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

expect(claimInterpreterElement).toHaveProperty('tagName', 'SPAN');
expect(claimInterpreterElement).toHaveProperty('textContent', 'Surfaced with Azure OpenAI');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
:global(.webchat) .thumb-button {
align-items: center;
border-radius: 2px;
box-sizing: content-box;
display: grid;
grid-template-areas: 'main';
height: 16px;
width: 16px;

.thumb-button__input {
appearance: none;
background: transparent;
border: 0;
grid-area: main;
height: 16px;
margin: 0;
opacity: 0;
padding: 0;
width: 16px;

&:active {
background: #edebe9;
}
}

&:has(.thumb-button__input:focus-visible) {
outline: solid 1px #605e5c; /* <input> has opacity of 0, we need to set the outline in the container. */
}

.thumb-button__image {
color: var(--webchat__color--accent);
grid-area: main;
justify-self: center; /* Unsure why "justifyContent" doesn't work. */
pointer-events: none;
visibility: hidden;
width: 14px;

&.thumb-button__image--is-stroked {
visibility: unset;
}
}

&:has(.thumb-button__input:is(:not([aria-disabled='true']):hover, :checked, [aria-pressed='true'])) {
.thumb-button__image {
&.thumb-button__image--is-stroked {
visibility: hidden;
}

&.thumb-button__image--is-filled {
visibility: unset;
}
}
}

&.thumb-button--large {
border: 1px solid transparent;
border-radius: 4px;
height: 30px;
width: 30px;

.thumb-button__input {
background: currentColor;
height: 30px;
width: 30px;
}

.thumb-button__image {
color: currentColor;
font-size: 20px;
height: 1em;
width: 1em;
}

&:has(.thumb-button__input:is(:hover, :active, :checked, [aria-pressed='true'])) .thumb-button__image {
color: var(--webchat__color--accent);
}

&:has(.thumb-button__input[aria-disabled='true']) .thumb-button__image {
color: var(--webchat__color--subtle);
}

&.thumb-button--has-submitted:has(.thumb-button__input:not(:checked):not([aria-pressed='true']))
.thumb-button__tooltip {
display: none;
}

&.thumb-button--has-submitted .thumb-button__tooltip {
--webchat__tooltip-anchor-inline-start: 20%;
}
}
}
36 changes: 14 additions & 22 deletions packages/component/src/ActivityFeedback/private/ThumbButton.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { 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, { forwardRef, memo, useCallback, useMemo, type ForwardedRef, type KeyboardEventHandler } from 'react';
import { useRefFrom } from 'use-ref-from';
import { boolean, function_, literal, object, optional, pipe, readonly, string, union, type InferInput } from 'valibot';

import useStyleSet from '../../hooks/useStyleSet';
import testIds from '../../testIds';
import { Tooltip } from '../../Tooltip';
import ThumbButtonImage from './ThumbButton.Image';

import styles from './ThumbButton.module.css';

const { useLocalizer } = hooks;

const thumbButtonPropsSchema = pipe(
Expand All @@ -36,9 +38,9 @@ function ThumbButton(props: ThumbButtonProps, ref: ForwardedRef<HTMLInputElement
props
);

const [{ thumbButton }] = useStyleSet();
const localize = useLocalizer();
const onClickRef = useRefFrom(onClick);
const classNames = useStyles(styles);

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

return (
<div
className={classNames(
'webchat__thumb-button',
className={cx(
classNames['thumb-button'],
{
'webchat__thumb-button--large': size === 'large',
'webchat__thumb-button--has-submitted': submitted
[classNames['thumb-button--large']]: size === 'large',
[classNames['thumb-button--has-submitted']]: submitted
},
className,
thumbButton + ''
className
)}
>
<input
aria-disabled={disabled ? 'true' : undefined}
aria-label={buttonTitle}
className={classNames(
'webchat__thumb-button__input',
{ 'webchat__thumb-button__input--is-pressed': pressed },
className,
thumbButton + ''
)}
className={cx(classNames['thumb-button__input'], className)}
data-testid={testIds.feedbackButton}
name={name}
onKeyDown={handleKeyDown}
Expand All @@ -90,19 +86,15 @@ function ThumbButton(props: ThumbButtonProps, ref: ForwardedRef<HTMLInputElement
})}
/>
<ThumbButtonImage
className={classNames('webchat__thumb-button__image', 'webchat__thumb-button__image--is-stroked', {
'webchat__thumb-button__image--is-down': direction === 'down'
})}
className={cx(classNames['thumb-button__image'], classNames['thumb-button__image--is-stroked'])}
direction={direction}
/>
<ThumbButtonImage
className={classNames('webchat__thumb-button__image', 'webchat__thumb-button__image--is-filled', {
'webchat__thumb-button__image--is-down': direction === 'down'
})}
className={cx(classNames['thumb-button__image'], classNames['thumb-button__image--is-filled'])}
direction={direction}
filled={true}
/>
<Tooltip className="webchat__thumb-button__tooltip">{buttonTitle}</Tooltip>
<Tooltip className={classNames['thumb-button__tooltip']}>{buttonTitle}</Tooltip>
</div>
);
}
Expand Down
31 changes: 31 additions & 0 deletions packages/component/src/ActivityStatus/ActivityStatus.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
:global(.webchat) .activity-status {
color: var(--webchat__color--timestamp);
font-family: var(--webchat__font--primary);
font-size: var(--webchat__font-size--small);
margin-block-start: calc(var(--webchat__padding--regular) / 2);

&.activity-status--slotted {
display: inline-flex;
gap: 4px;
}
}

:global(.webchat) .activity-status-slot {
display: contents;

&:not(:first-child)::before {
content: '|';
}

&:empty {
display: none;
}
}

:global(.webchat) .activity-status__originator {
align-items: center;

&.activity-status__originator--has-link {
color: var(--webchat__color--accent);
}
}
16 changes: 10 additions & 6 deletions packages/component/src/ActivityStatus/OthersActivityStatus.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { warnOnce } from '@msinternal/botframework-webchat-base/utils';
import { useStyles } from '@msinternal/botframework-webchat-styles/react';
import { hooks } from 'botframework-webchat-api';
import {
getOrgSchemaMessage,
Expand All @@ -7,27 +8,28 @@ import {
parseClaim,
type WebChatActivity
} from 'botframework-webchat-core';
import classNames from 'classnames';
import cx from 'classnames';
import React, { memo, useMemo } from 'react';

import ActivityFeedback from '../ActivityFeedback/ActivityFeedback';
import useStyleSet from '../hooks/useStyleSet';
import dereferenceBlankNodes from '../Utils/JSONLinkedData/dereferenceBlankNodes';
import Originator from './private/Originator';
import StatusSlot from './StatusSlot';
import Timestamp from './Timestamp';

import styles from './ActivityStatus.module.css';

const { useStyleOptions } = hooks;

type Props = Readonly<{ activity: WebChatActivity; className?: string | undefined }>;
type Props = Readonly<{ activity: WebChatActivity; className?: string | undefined; slotted?: boolean }>;

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

const OthersActivityStatus = memo(({ activity, className }: Props) => {
const OthersActivityStatus = memo(({ activity, className, slotted }: Props) => {
const [{ feedbackActionsPlacement }] = useStyleOptions();
const [{ sendStatus }] = useStyleSet();
const classNames = useStyles(styles);
const { timestamp } = activity;
const graph = useMemo(() => dereferenceBlankNodes(activity.entities || []), [activity.entities]);

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

return (
<span className={classNames('webchat__activity-status', className, sendStatus + '')}>
<span
className={cx(classNames['activity-status'], { [classNames['activity-status--slotted']]: slotted }, className)}
>
{timestamp && (
<StatusSlot>
<Timestamp key="timestamp" timestamp={timestamp} />
Expand Down
14 changes: 8 additions & 6 deletions packages/component/src/ActivityStatus/SelfActivityStatus.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { useStyles } from '@msinternal/botframework-webchat-styles/react';
import { type WebChatActivity } from 'botframework-webchat-core';
import classNames from 'classnames';
import cx from 'classnames';
import React, { memo } from 'react';

import Timestamp from './Timestamp';
import useStyleSet from '../hooks/useStyleSet';

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

const SelftActivityStatus = memo(({ activity, className }: Props) => {
const [{ sendStatus }] = useStyleSet();
type Props = Readonly<{ activity: WebChatActivity; className?: string | undefined; slotted?: boolean }>;

const SelftActivityStatus = memo(({ activity, className, slotted }: Props) => {
const classNames = useStyles(styleClassNames);
const { timestamp } = activity;

return timestamp ? (
<span
className={classNames('webchat__activity-status', 'webchat__activity-status--self', className, sendStatus + '')}
className={cx(classNames['activity-status'], { [classNames['activity-status--slotted']]: slotted }, className)}
>
<Timestamp timestamp={timestamp} />
</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { validateProps } from '@msinternal/botframework-webchat-react-valibot';
import { hooks } from 'botframework-webchat-api';
import classNames from 'classnames';
import { useStyles } from '@msinternal/botframework-webchat-styles/react';
import React, { memo, useCallback } from 'react';
import { any, literal, object, pipe, readonly, union, type InferInput } from 'valibot';

import useFocus from '../../hooks/useFocus';
import useStyleSet from '../../hooks/useStyleSet';
import { SENDING, SEND_FAILED, SENT } from '../../types/internal/SendStatus';
import SendFailedRetry from './private/SendFailedRetry';

import styles from '../ActivityStatus.module.css';

const { useLocalizer, usePostActivity } = hooks;

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

const [{ sendStatus: sendStatusStyleSet }] = useStyleSet();
const focus = useFocus();
const localize = useLocalizer();
const postActivity = usePostActivity();
const classNames = useStyles(styles);

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

return (
<React.Fragment>
<span className={classNames('webchat__activity-status', 'webchat__activity-status--sending', sendStatusStyleSet)}>
<span className={classNames['activity-status']}>
{sendStatus === SENDING ? (
sendingText
) : sendStatus === SEND_FAILED ? (
Expand Down
13 changes: 9 additions & 4 deletions packages/component/src/ActivityStatus/StatusSlot.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { useStyles } from '@msinternal/botframework-webchat-styles/react';
import React, { memo, ReactNode } from 'react';
import classNames from 'classnames';
import cx from 'classnames';

import styles from './ActivityStatus.module.css';

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

const StatusSlot = ({ children, className }: Props) => (
<span className={classNames('webchat__activity-status-slot', className)}>{children}</span>
);
const StatusSlot = ({ children, className }: Props) => {
const classNames = useStyles(styles);

return <span className={cx(classNames['activity-status-slot'], className)}>{children}</span>;
};

StatusSlot.displayName = 'StatusSlot';

Expand Down
Loading
Loading