From bb5898ea232817d487c49cb5c16a100cedb83506 Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 14 Sep 2023 17:02:47 -0700 Subject: [PATCH] [HIGH] Incorporated PR comments of 4846 (#4857) * Rename to OrgSchema from SchemaOrg * Rename to OrgSchema * Remove unnecessary handleXXX * Remove unnecessary typings * Remove unnecessary undefined * Remove commented CSS * Remove unnecessary handleXXX * Add CSSTokens and rename to CSS custom properties * Rename to CSSTokens and CSS custom properties * Type out useStyleSet * Add entry * Freeze arrays * Typo * Remove as const * Move typecasting outside --- CHANGELOG.md | 1 + .../Attachment/AdaptiveCardRenderer.tsx | 3 +- .../Attachment/AnimationCardContent.tsx | 4 +- .../Attachment/AudioCardContent.tsx | 4 +- .../Attachment/SignInCardContent.tsx | 4 +- .../Attachment/VideoCardContent.tsx | 4 +- packages/bundle/src/hooks/useStyleSet.ts | 13 +++++ packages/bundle/src/index.ts | 4 +- .../ActivityStatus/OthersActivityStatus.tsx | 6 +-- .../private/Feedback/Feedback.tsx | 13 ++--- .../private/Feedback/private/VoteButton.tsx | 2 +- .../src/ActivityStatus/private/Originator.tsx | 7 +-- .../Text/private/MarkdownTextContent.tsx | 4 +- packages/component/src/Composer.tsx | 4 +- packages/component/src/Styles/CSSTokens.ts | 21 ++++++++ .../src/Styles/CustomPropertyNames.ts | 16 ++++++ .../Styles/StyleSet/CSSCustomProperties.ts | 45 ++++++++++++++++ .../src/Styles/StyleSet/CSSVariables.ts | 40 -------------- .../src/Styles/StyleSet/LinkDefinitions.ts | 9 ++-- .../src/Styles/StyleSet/ModalDialog.ts | 12 +++-- .../StyleSet/OriginatorActivityStatus.ts | 14 ----- .../src/Styles/StyleSet/RenderMarkdown.ts | 7 ++- .../src/Styles/StyleSet/SendStatus.ts | 12 +++-- .../Styles/StyleSet/SlottedActivityStatus.ts | 6 ++- .../src/Styles/StyleSet/TextContent.ts | 12 ++--- .../src/Styles/StyleSet/ThumbButton.ts | 54 ++----------------- .../component/src/Styles/createStyleSet.ts | 8 +-- packages/component/src/hooks/useStyleSet.ts | 6 ++- .../ModalDialog/ModalDialogComposer.tsx | 2 +- .../providers/ModalDialog/private/Popover.tsx | 5 +- .../{SchemaOrg => OrgSchema}/Claim.spec.ts | 0 .../{SchemaOrg => OrgSchema}/Claim.ts | 0 .../{SchemaOrg => OrgSchema}/Project.spec.ts | 0 .../{SchemaOrg => OrgSchema}/Project.ts | 0 .../ReplyAction.spec.ts | 0 .../{SchemaOrg => OrgSchema}/ReplyAction.ts | 0 .../{SchemaOrg => OrgSchema}/Thing.spec.ts | 0 .../{SchemaOrg => OrgSchema}/Thing.ts | 4 +- .../VoteAction.spec.ts | 0 .../{SchemaOrg => OrgSchema}/VoteAction.ts | 0 .../src/types/internal/TypeOfArray.ts | 4 +- packages/core/src/index.ts | 4 +- packages/core/src/types/WebChatActivity.ts | 4 +- .../{SchemaOrgThing.ts => OrgSchemaThing.ts} | 2 +- 44 files changed, 173 insertions(+), 187 deletions(-) create mode 100644 packages/bundle/src/hooks/useStyleSet.ts create mode 100644 packages/component/src/Styles/CSSTokens.ts create mode 100644 packages/component/src/Styles/CustomPropertyNames.ts create mode 100644 packages/component/src/Styles/StyleSet/CSSCustomProperties.ts delete mode 100644 packages/component/src/Styles/StyleSet/CSSVariables.ts delete mode 100644 packages/component/src/Styles/StyleSet/OriginatorActivityStatus.ts rename packages/component/src/types/external/{SchemaOrg => OrgSchema}/Claim.spec.ts (100%) rename packages/component/src/types/external/{SchemaOrg => OrgSchema}/Claim.ts (100%) rename packages/component/src/types/external/{SchemaOrg => OrgSchema}/Project.spec.ts (100%) rename packages/component/src/types/external/{SchemaOrg => OrgSchema}/Project.ts (100%) rename packages/component/src/types/external/{SchemaOrg => OrgSchema}/ReplyAction.spec.ts (100%) rename packages/component/src/types/external/{SchemaOrg => OrgSchema}/ReplyAction.ts (100%) rename packages/component/src/types/external/{SchemaOrg => OrgSchema}/Thing.spec.ts (100%) rename packages/component/src/types/external/{SchemaOrg => OrgSchema}/Thing.ts (90%) rename packages/component/src/types/external/{SchemaOrg => OrgSchema}/VoteAction.spec.ts (100%) rename packages/component/src/types/external/{SchemaOrg => OrgSchema}/VoteAction.ts (100%) rename packages/core/src/types/external/{SchemaOrgThing.ts => OrgSchemaThing.ts} (80%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ff53a503e..47fa6ce457 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Resolves [#4840](https://github.com/microsoft/BotFramework-WebChat/issues/4840). Added feedback buttons in activity status, by [@compulim](https://github.com/compulim), in PR [#4846](https://github.com/microsoft/BotFramework-WebChat/pull/4846) - Resolves [#4841](https://github.com/microsoft/BotFramework-WebChat/issues/4841). Added link definitions UI in Markdown, by [@compulim](https://github.com/compulim), in PR [#4846](https://github.com/microsoft/BotFramework-WebChat/pull/4846) - Resolves [#4842](https://github.com/microsoft/BotFramework-WebChat/issues/4842). Added provenance in activity status, by [@compulim](https://github.com/compulim), in PR [#4846](https://github.com/microsoft/BotFramework-WebChat/pull/4846) +- Resolves [#4856](https://github.com/microsoft/BotFramework-WebChat/issues/4856). Added types for `useStyleSet`, by [@compulim](https://github.com/compulim), in PR [#4857](https://github.com/microsoft/BotFramework-WebChat/pull/4857) ## [4.15.9] - 2023-08-25 diff --git a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx index 7d20ef4144..9402f13f87 100644 --- a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx +++ b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx @@ -25,10 +25,11 @@ import useAdaptiveCardsPackage from '../hooks/useAdaptiveCardsPackage'; import useDisabledModEffect from './AdaptiveCardHacks/useDisabledModEffect'; import usePersistValuesModEffect from './AdaptiveCardHacks/usePersistValuesModEffect'; import useRoleModEffect from './AdaptiveCardHacks/useRoleModEffect'; +import useStyleSet from '../../hooks/useStyleSet'; import useValueRef from './AdaptiveCardHacks/private/useValueRef'; const { ErrorBox } = Components; -const { useDisabled, useLocalizer, usePerformCardAction, useRenderMarkdownAsHTML, useScrollToEnd, useStyleSet } = hooks; +const { useDisabled, useLocalizer, usePerformCardAction, useRenderMarkdownAsHTML, useScrollToEnd } = hooks; const node_env = process.env.node_env || process.env.NODE_ENV; diff --git a/packages/bundle/src/adaptiveCards/Attachment/AnimationCardContent.tsx b/packages/bundle/src/adaptiveCards/Attachment/AnimationCardContent.tsx index 041f43c6e5..943d9300ce 100644 --- a/packages/bundle/src/adaptiveCards/Attachment/AnimationCardContent.tsx +++ b/packages/bundle/src/adaptiveCards/Attachment/AnimationCardContent.tsx @@ -1,14 +1,14 @@ /* eslint react/no-array-index-key: "off" */ -import { Components, hooks } from 'botframework-webchat-component'; +import { Components } from 'botframework-webchat-component'; import PropTypes from 'prop-types'; import React, { FC } from 'react'; import type { DirectLineAnimationCard } from 'botframework-webchat-core'; import CommonCard from './CommonCard'; +import useStyleSet from '../../hooks/useStyleSet'; const { ImageContent, VideoContent } = Components; -const { useStyleSet } = hooks; type AnimationCardContentProps = { actionPerformedClassName?: string; diff --git a/packages/bundle/src/adaptiveCards/Attachment/AudioCardContent.tsx b/packages/bundle/src/adaptiveCards/Attachment/AudioCardContent.tsx index c33d2f0cd0..a280290e4b 100644 --- a/packages/bundle/src/adaptiveCards/Attachment/AudioCardContent.tsx +++ b/packages/bundle/src/adaptiveCards/Attachment/AudioCardContent.tsx @@ -1,14 +1,14 @@ /* eslint react/no-array-index-key: "off" */ -import { Components, hooks } from 'botframework-webchat-component'; +import { Components } from 'botframework-webchat-component'; import PropTypes from 'prop-types'; import React, { FC } from 'react'; import type { DirectLineAudioCard } from 'botframework-webchat-core'; import CommonCard from './CommonCard'; +import useStyleSet from '../../hooks/useStyleSet'; const { AudioContent } = Components; -const { useStyleSet } = hooks; type AudioCardContentProps = { actionPerformedClassName?: string; diff --git a/packages/bundle/src/adaptiveCards/Attachment/SignInCardContent.tsx b/packages/bundle/src/adaptiveCards/Attachment/SignInCardContent.tsx index e0d80da625..637fd73981 100644 --- a/packages/bundle/src/adaptiveCards/Attachment/SignInCardContent.tsx +++ b/packages/bundle/src/adaptiveCards/Attachment/SignInCardContent.tsx @@ -1,11 +1,9 @@ -import { hooks } from 'botframework-webchat-component'; import PropTypes from 'prop-types'; import React, { FC } from 'react'; import type { DirectLineSignInCard } from 'botframework-webchat-core'; import CommonCard from './CommonCard'; - -const { useStyleSet } = hooks; +import useStyleSet from '../../hooks/useStyleSet'; type SignInCardContentProps = { actionPerformedClassName?: string; diff --git a/packages/bundle/src/adaptiveCards/Attachment/VideoCardContent.tsx b/packages/bundle/src/adaptiveCards/Attachment/VideoCardContent.tsx index b21c041c17..d2e24cfbe6 100644 --- a/packages/bundle/src/adaptiveCards/Attachment/VideoCardContent.tsx +++ b/packages/bundle/src/adaptiveCards/Attachment/VideoCardContent.tsx @@ -1,13 +1,13 @@ /* eslint react/no-array-index-key: "off" */ -import { Components, hooks } from 'botframework-webchat-component'; +import { Components } from 'botframework-webchat-component'; import PropTypes from 'prop-types'; import React, { FC } from 'react'; import type { DirectLineVideoCard } from 'botframework-webchat-core'; import CommonCard from './CommonCard'; +import useStyleSet from '../../hooks/useStyleSet'; -const { useStyleSet } = hooks; const { VideoContent } = Components; type VideoCardContentProps = { diff --git a/packages/bundle/src/hooks/useStyleSet.ts b/packages/bundle/src/hooks/useStyleSet.ts new file mode 100644 index 0000000000..766b4ddb18 --- /dev/null +++ b/packages/bundle/src/hooks/useStyleSet.ts @@ -0,0 +1,13 @@ +import { hooks } from 'botframework-webchat-component'; + +import type AdaptiveCardsStyleSet from '../adaptiveCards/AdaptiveCardsStyleSet'; + +const useMinimalStyleSet = hooks.useStyleSet; + +type MinimalStyleSet = ReturnType[0]; + +export default function useStyleSet(): readonly [MinimalStyleSet & AdaptiveCardsStyleSet] { + const [styleOptions] = useMinimalStyleSet(); + + return Object.freeze([styleOptions as MinimalStyleSet & AdaptiveCardsStyleSet] as const); +} diff --git a/packages/bundle/src/index.ts b/packages/bundle/src/index.ts index 43cd13288a..8f8c55c8a5 100644 --- a/packages/bundle/src/index.ts +++ b/packages/bundle/src/index.ts @@ -28,6 +28,7 @@ import ThumbnailCardContent from './adaptiveCards/Attachment/ThumbnailCardConten import useAdaptiveCardsHostConfig from './adaptiveCards/hooks/useAdaptiveCardsHostConfig'; import useAdaptiveCardsPackage from './adaptiveCards/hooks/useAdaptiveCardsPackage'; import useStyleOptions from './hooks/useStyleOptions'; +import useStyleSet from './hooks/useStyleSet'; import VideoCardContent from './adaptiveCards/Attachment/VideoCardContent'; const renderWebChat = coreRenderWebChat.bind(null, ReactWebChat); @@ -56,7 +57,8 @@ const patchedHooks = { ...hooks, useAdaptiveCardsHostConfig, useAdaptiveCardsPackage, - useStyleOptions + useStyleOptions, + useStyleSet }; const AdditionalComponents = { diff --git a/packages/component/src/ActivityStatus/OthersActivityStatus.tsx b/packages/component/src/ActivityStatus/OthersActivityStatus.tsx index 4a58e77ee7..b6be4331d6 100644 --- a/packages/component/src/ActivityStatus/OthersActivityStatus.tsx +++ b/packages/component/src/ActivityStatus/OthersActivityStatus.tsx @@ -2,9 +2,9 @@ import { type WebChatActivity } from 'botframework-webchat-core'; import classNames from 'classnames'; import React, { memo, type ReactNode, useMemo } from 'react'; -import { isReplyAction, type ReplyAction } from '../types/external/SchemaOrg/ReplyAction'; -import { isThing, type Thing } from '../types/external/SchemaOrg/Thing'; -import { isVoteAction, type VoteAction } from '../types/external/SchemaOrg/VoteAction'; +import { isReplyAction, type ReplyAction } from '../types/external/OrgSchema/ReplyAction'; +import { isThing, type Thing } from '../types/external/OrgSchema/Thing'; +import { isVoteAction, type VoteAction } from '../types/external/OrgSchema/VoteAction'; import { type TypeOfArray } from '../types/internal/TypeOfArray'; import Feedback from './private/Feedback/Feedback'; import Originator from './private/Originator'; diff --git a/packages/component/src/ActivityStatus/private/Feedback/Feedback.tsx b/packages/component/src/ActivityStatus/private/Feedback/Feedback.tsx index eedbf5453d..b40c85d9ee 100644 --- a/packages/component/src/ActivityStatus/private/Feedback/Feedback.tsx +++ b/packages/component/src/ActivityStatus/private/Feedback/Feedback.tsx @@ -1,8 +1,8 @@ import { hooks } from 'botframework-webchat-api'; import { useRefFrom } from 'use-ref-from'; -import React, { Fragment, memo, type PropsWithChildren, useCallback, useState, useEffect } from 'react'; +import React, { Fragment, memo, type PropsWithChildren, useState, useEffect } from 'react'; -import { type VoteAction } from '../../../types/external/SchemaOrg/VoteAction'; +import { type VoteAction } from '../../../types/external/OrgSchema/VoteAction'; import FeedbackVoteButton from './private/VoteButton'; const { usePonyfill, usePostActivity } = hooks; @@ -15,14 +15,9 @@ const DEBOUNCE_TIMEOUT = 500; const Feedback = memo(({ voteActions }: Props) => { const [{ clearTimeout, setTimeout }] = usePonyfill(); - const [selectedVoteAction, setSelectedVoteAction] = useState(undefined); + const [selectedVoteAction, setSelectedVoteAction] = useState(); const postActivity = usePostActivity(); - const handleChange = useCallback<(voteAction: VoteAction) => void>( - voteAction => setSelectedVoteAction(voteAction), - [setSelectedVoteAction] - ); - const postActivityRef = useRefFrom(postActivity); useEffect(() => { @@ -48,7 +43,7 @@ const Feedback = memo(({ voteActions }: Props) => { {Array.from(voteActions).map((voteAction, index) => ( diff --git a/packages/component/src/ActivityStatus/private/Feedback/private/VoteButton.tsx b/packages/component/src/ActivityStatus/private/Feedback/private/VoteButton.tsx index dd738c31d2..dd8819a473 100644 --- a/packages/component/src/ActivityStatus/private/Feedback/private/VoteButton.tsx +++ b/packages/component/src/ActivityStatus/private/Feedback/private/VoteButton.tsx @@ -1,7 +1,7 @@ import React, { memo, useCallback } from 'react'; import { useRefFrom } from 'use-ref-from'; -import { type VoteAction } from '../../../../types/external/SchemaOrg/VoteAction'; +import { type VoteAction } from '../../../../types/external/OrgSchema/VoteAction'; import ThumbsButton from './ThumbButton'; type Props = { diff --git a/packages/component/src/ActivityStatus/private/Originator.tsx b/packages/component/src/ActivityStatus/private/Originator.tsx index c49c544c90..c73dcc2ede 100644 --- a/packages/component/src/ActivityStatus/private/Originator.tsx +++ b/packages/component/src/ActivityStatus/private/Originator.tsx @@ -1,13 +1,10 @@ -import classNames from 'classnames'; import React, { memo } from 'react'; -import { type ReplyAction } from '../../types/external/SchemaOrg/ReplyAction'; -import useStyleSet from '../../hooks/useStyleSet'; +import { type ReplyAction } from '../../types/external/OrgSchema/ReplyAction'; type Props = { replyAction: ReplyAction }; const Originator = memo(({ replyAction }: Props) => { - const [{ originatorActivityStatus }] = useStyleSet(); const { description, provider } = replyAction; const text = description || provider?.name; @@ -23,7 +20,7 @@ const Originator = memo(({ replyAction }: Props) => { {text} ) : ( - {text} + {text} ); }); diff --git a/packages/component/src/Attachment/Text/private/MarkdownTextContent.tsx b/packages/component/src/Attachment/Text/private/MarkdownTextContent.tsx index 3737165416..5f478a9000 100644 --- a/packages/component/src/Attachment/Text/private/MarkdownTextContent.tsx +++ b/packages/component/src/Attachment/Text/private/MarkdownTextContent.tsx @@ -3,8 +3,8 @@ import { useRefFrom } from 'use-ref-from'; import classNames from 'classnames'; import React, { memo, type MouseEventHandler, useCallback, useMemo } from 'react'; -import { isClaim, type Claim } from '../../../types/external/SchemaOrg/Claim'; -import { isThing } from '../../../types/external/SchemaOrg/Thing'; +import { isClaim, type Claim } from '../../../types/external/OrgSchema/Claim'; +import { isThing } from '../../../types/external/OrgSchema/Thing'; import { type PropsOf } from '../../../types/PropsOf'; import { type WebChatActivity } from 'botframework-webchat-core'; import isHTMLButtonElement from './isHTMLButtonElement'; diff --git a/packages/component/src/Composer.tsx b/packages/component/src/Composer.tsx index 7fc814deee..9eb1a5b88f 100644 --- a/packages/component/src/Composer.tsx +++ b/packages/component/src/Composer.tsx @@ -53,14 +53,14 @@ type ComposerCoreUIProps = { }; const ComposerCoreUI = memo(({ children }: ComposerCoreUIProps) => { - const [{ cssVariables }] = useStyleSet(); + const [{ cssCustomProperties }] = useStyleSet(); const dictationOnError = useCallback(err => { console.error(err); }, []); return ( -
+
{/* When is finalized, it will be using an independent instance that lives inside . */} diff --git a/packages/component/src/Styles/CSSTokens.ts b/packages/component/src/Styles/CSSTokens.ts new file mode 100644 index 0000000000..fa297fc3a9 --- /dev/null +++ b/packages/component/src/Styles/CSSTokens.ts @@ -0,0 +1,21 @@ +import CustomPropertyNames from './CustomPropertyNames'; + +type CustomPropertyNamesType = typeof CustomPropertyNames; + +type CSSTokensType>> = { + [K in keyof T]: `var(${T[K]})`; +}; + +// To add/remove/update a token, go to `CustomPropertyName.ts`. +const CSSTokens = new Proxy( + {}, + { + get(_, key: keyof CustomPropertyNamesType) { + // We already checked in the `CustomPropertyName`. + // eslint-disable-next-line security/detect-object-injection + return `var(${CustomPropertyNames[key]})`; + } + } +) as CSSTokensType; + +export default CSSTokens; diff --git a/packages/component/src/Styles/CustomPropertyNames.ts b/packages/component/src/Styles/CustomPropertyNames.ts new file mode 100644 index 0000000000..fa9d9667de --- /dev/null +++ b/packages/component/src/Styles/CustomPropertyNames.ts @@ -0,0 +1,16 @@ +const CustomPropertyNames = Object.freeze({ + // Make sure key names does not have JavaScript forbidden names. + ColorAccent: '--webchat__color--accent', + ColorTimestamp: '--webchat__color--timestamp', + FontPrimary: '--webchat__font--primary', + FontSizeSmall: '--webchat__font-size--small', + IconURLExternalLink: '--webchat__icon-url--external-link', + MaxWidthBubble: '--webchat__max-width--bubble', + MinHeightBubble: '--webchat__min-height--bubble', + PaddingRegular: '--webchat__padding--regular' +}); + +// This is for type-checking only to make sure the CSS custom property names is `--webchat__${string}`. +const _TypeChecking: Readonly> = CustomPropertyNames; + +export default CustomPropertyNames; diff --git a/packages/component/src/Styles/StyleSet/CSSCustomProperties.ts b/packages/component/src/Styles/StyleSet/CSSCustomProperties.ts new file mode 100644 index 0000000000..a97816d6e6 --- /dev/null +++ b/packages/component/src/Styles/StyleSet/CSSCustomProperties.ts @@ -0,0 +1,45 @@ +import { StrictStyleOptions } from 'botframework-webchat-api'; + +import CustomPropertyNames from '../CustomPropertyNames'; + +export default function createCSSCustomPropertiesStyle({ + accent, + bubbleMaxWidth, + bubbleMinHeight, + fontSizeSmall, + markdownExternalLinkIconImage, + paddingRegular, + primaryFont, + subtle, + timestampColor +}: StrictStyleOptions) { + return { + '&.webchat__css-custom-properties': { + display: 'contents', + + // TODO: Should we register the CSS property for inheritance, type checking, and initial value? + // Registrations need to be done on global level, and duplicate registration will throw. + // https://developer.mozilla.org/en-US/docs/Web/CSS/@property + + // TODO: This is ongoing work. We are slowly adding CSS variables to ease calculations and stuff. + // + // We need to build a story to let web devs override these CSS variables. + // + // Candy points: + // - They should be able to override CSS variables for certain things (say, padding of popover) without affecting much. + // + // House rules: + // - We should put styling varibles here, e.g. paddingRegular + // - We MUST NOT put runtime variables here, e.g. sendTimeout + // - This is because we cannot programmatically know when the sendTimeout change + [CustomPropertyNames.ColorAccent]: accent, + [CustomPropertyNames.ColorTimestamp]: timestampColor || subtle, // Maybe we should not need this if we allow web devs to override CSS variables for certain components. + [CustomPropertyNames.FontPrimary]: primaryFont, + [CustomPropertyNames.FontSizeSmall]: fontSizeSmall, + [CustomPropertyNames.IconURLExternalLink]: markdownExternalLinkIconImage, + [CustomPropertyNames.MaxWidthBubble]: bubbleMaxWidth + 'px', + [CustomPropertyNames.MinHeightBubble]: bubbleMinHeight + 'px', + [CustomPropertyNames.PaddingRegular]: paddingRegular + 'px' + } + }; +} diff --git a/packages/component/src/Styles/StyleSet/CSSVariables.ts b/packages/component/src/Styles/StyleSet/CSSVariables.ts deleted file mode 100644 index 5dc5f18fc4..0000000000 --- a/packages/component/src/Styles/StyleSet/CSSVariables.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { StrictStyleOptions } from 'botframework-webchat-api'; - -export default function createCSSVariablesStyle({ - accent, - bubbleMaxWidth, - bubbleMinHeight, - fontSizeSmall, - markdownExternalLinkIconImage, - paddingRegular, - primaryFont, - subtle, - timestampColor -}: StrictStyleOptions) { - return { - '&.webchat__css-variables': { - display: 'contents', - - // TODO: This is ongoing work. We are slowly adding CSS variables to ease calculations and stuff. - // - // We need to build a story to let web devs override these CSS variables. - // - // Candy points: - // - They should be able to override CSS variables for certain things (say, padding of popover) without affecting much. - // - // House rules: - // - We should put styling varibles here, e.g. paddingRegular - // - We MUST NOT put runtime variables here, e.g. sendTimeout - // - This is because we cannot programmatically know when the sendTimeout change - - '--webchat__color--accent': accent, - '--webchat__color--timestamp': timestampColor || subtle, // Maybe we should not need this if we allow web devs to override CSS variables for certain components. - '--webchat__font--primary': primaryFont, - '--webchat__font-size--small': fontSizeSmall, - '--webchat__icon-url--external-link': markdownExternalLinkIconImage, - '--webchat__max-width--bubble': bubbleMaxWidth + 'px', - '--webchat__min-height--bubble': bubbleMinHeight + 'px', - '--webchat__padding--regular': paddingRegular + 'px' - } - }; -} diff --git a/packages/component/src/Styles/StyleSet/LinkDefinitions.ts b/packages/component/src/Styles/StyleSet/LinkDefinitions.ts index 11a02b68cd..9186ccdddc 100644 --- a/packages/component/src/Styles/StyleSet/LinkDefinitions.ts +++ b/packages/component/src/Styles/StyleSet/LinkDefinitions.ts @@ -5,7 +5,8 @@ import { NOT_FORCED_COLORS_SELECTOR } from './Constants'; -// TODO: Fix CSS var. +import CSSTokens from '../CSSTokens'; + export default function createLinkDefinitionsStyleSet() { return { '&.webchat__link-definitions': { @@ -28,10 +29,6 @@ export default function createLinkDefinitionsStyleSet() { } }, - // '.webchat__link-definitions__header-chevron': { - // verticalAlign: 'middle' - // }, - '&:not([open]) .webchat__link-definitions__header-chevron': { marginBottom: '-0.1em', transform: 'rotate(-180deg)' @@ -130,7 +127,7 @@ export default function createLinkDefinitionsStyleSet() { padding: 4, [NOT_FORCED_COLORS_SELECTOR]: { - color: 'var(--webchat__color--accent)' + color: CSSTokens.ColorAccent } }, diff --git a/packages/component/src/Styles/StyleSet/ModalDialog.ts b/packages/component/src/Styles/StyleSet/ModalDialog.ts index ceac1f2e6a..be7a5e2ee2 100644 --- a/packages/component/src/Styles/StyleSet/ModalDialog.ts +++ b/packages/component/src/Styles/StyleSet/ModalDialog.ts @@ -5,10 +5,12 @@ import { NOT_FORCED_COLORS_SELECTOR } from './Constants'; +import CSSTokens from '../CSSTokens'; + export default function createModalDialogStyleSet() { return { '&.webchat__modal-dialog': { - fontFamily: 'var(--webchat__font--primary)', + fontFamily: CSSTokens.FontPrimary, width: '100%', [NOT_FORCED_COLORS_SELECTOR]: { @@ -18,11 +20,11 @@ export default function createModalDialogStyleSet() { '& .webchat__modal-dialog__box': { borderRadius: 2, - height: 'calc(100% - var(--webchat__padding--regular) * 2)', + height: `calc(100% - ${CSSTokens.PaddingRegular} * 2)`, overflow: 'hidden', margin: 'auto', maxWidth: '60%', - width: 'calc(100% - var(--webchat__padding--regular) * 2)', + width: `calc(100% - ${CSSTokens.PaddingRegular} * 2)`, [LIGHT_THEME_SELECTOR]: { // From Power BI: @@ -49,7 +51,7 @@ export default function createModalDialogStyleSet() { '& .webchat__modal-dialog__close-button-layout': { float: 'right', - padding: 'var(--webchat__padding--regular)' + padding: CSSTokens.PaddingRegular }, '& .webchat__modal-dialog__close-button': { @@ -112,7 +114,7 @@ export default function createModalDialogStyleSet() { }, '& .webchat__modal-dialog__body': { - margin: 'calc(var(--webchat__padding--regular) * 2)' + margin: `calc(${CSSTokens.PaddingRegular} * 2)` } } }; diff --git a/packages/component/src/Styles/StyleSet/OriginatorActivityStatus.ts b/packages/component/src/Styles/StyleSet/OriginatorActivityStatus.ts deleted file mode 100644 index 12483eef42..0000000000 --- a/packages/component/src/Styles/StyleSet/OriginatorActivityStatus.ts +++ /dev/null @@ -1,14 +0,0 @@ -export default function createOriginatorActivityStatusStyle() { - return { - '&.webchat__originator-activity-status': { - alignItems: 'center', - /* These are the fonts used in Web Chat default style options. */ - fontFamily: 'var(--webchat__font--primary)', - fontSize: '80%', - - '&.webchat__originator-activity-status--link': { - color: 'var(--webchat__color--accent)' - } - } - }; -} diff --git a/packages/component/src/Styles/StyleSet/RenderMarkdown.ts b/packages/component/src/Styles/StyleSet/RenderMarkdown.ts index 92058a335f..152cddc2f3 100644 --- a/packages/component/src/Styles/StyleSet/RenderMarkdown.ts +++ b/packages/component/src/Styles/StyleSet/RenderMarkdown.ts @@ -1,6 +1,5 @@ -/* eslint no-magic-numbers: "off" */ - import { FORCED_COLORS_SELECTOR, NOT_FORCED_COLORS_SELECTOR } from './Constants'; +import CSSTokens from '../CSSTokens'; // This style is for accompanying result of `renderMarkdown()`. // Mostly, it should only styles elements that are generated/modified during `renderMarkdown()`. @@ -9,7 +8,7 @@ export default function createMarkdownStyle() { return { '&.webchat__render-markdown': { '& .webchat__render-markdown__external-link-icon': { - backgroundImage: 'var(--webchat__icon-url--external-link)', + backgroundImage: CSSTokens.IconURLExternalLink, height: '.75em', marginLeft: '.25em' }, @@ -27,7 +26,7 @@ export default function createMarkdownStyle() { }, [NOT_FORCED_COLORS_SELECTOR]: { - color: 'var(--webchat__color--accent)' + color: CSSTokens.ColorAccent } }, diff --git a/packages/component/src/Styles/StyleSet/SendStatus.ts b/packages/component/src/Styles/StyleSet/SendStatus.ts index 0b26232b2d..7d585747a9 100644 --- a/packages/component/src/Styles/StyleSet/SendStatus.ts +++ b/packages/component/src/Styles/StyleSet/SendStatus.ts @@ -1,10 +1,12 @@ +import CSSTokens from '../CSSTokens'; + export default function createSendStatusStyle() { return { '&.webchat__activity-status': { - color: 'var(--webchat__color--timestamp)', - fontFamily: 'var(--webchat__font--primary)', - fontSize: 'var(--webchat__font-size--small)', - marginTop: 'calc(var(--webchat__padding--regular) / 2)' + color: CSSTokens.ColorTimestamp, + fontFamily: CSSTokens.FontPrimary, + fontSize: CSSTokens.FontSizeSmall, + marginTop: `calc(${CSSTokens.PaddingRegular} / 2)` }, '&.webchat__activity-status--slotted': { @@ -16,7 +18,7 @@ export default function createSendStatusStyle() { alignItems: 'center', '&.webchat__activity-status__originator--has-link': { - color: 'var(--webchat__color--accent)' + color: CSSTokens.ColorAccent } } }; diff --git a/packages/component/src/Styles/StyleSet/SlottedActivityStatus.ts b/packages/component/src/Styles/StyleSet/SlottedActivityStatus.ts index cc6feff4c9..df5f000e59 100644 --- a/packages/component/src/Styles/StyleSet/SlottedActivityStatus.ts +++ b/packages/component/src/Styles/StyleSet/SlottedActivityStatus.ts @@ -1,13 +1,15 @@ +import CSSTokens from '../CSSTokens'; + export default function createSlottedActivityStatus() { return { '&.webchat__slotted-activity-status': { alignItems: 'center', display: 'inline-flex', gap: 4, - marginTop: 'calc(var(--webchat__padding--regular) / 2)', + marginTop: `calc(${CSSTokens.PaddingRegular} / 2)`, '& .webchat__slotted-activity-status__pipe': { - fontSize: 'var(--webchat__font-size--small)' + fontSize: CSSTokens.FontSizeSmall } } }; diff --git a/packages/component/src/Styles/StyleSet/TextContent.ts b/packages/component/src/Styles/StyleSet/TextContent.ts index 6fa8195ea2..f871c522d2 100644 --- a/packages/component/src/Styles/StyleSet/TextContent.ts +++ b/packages/component/src/Styles/StyleSet/TextContent.ts @@ -1,17 +1,17 @@ -/* eslint no-magic-numbers: "off" */ +import CSSTokens from '../CSSTokens'; export default function createTextContentStyle() { return { '&.webchat__text-content': { - fontFamily: 'var(--webchat__font--primary)', + fontFamily: CSSTokens.FontPrimary, margin: 0, - minHeight: 'calc(var(--webchat__min-height--bubble) - var(--webchat__padding--regular) * 2)', - padding: 'var(--webchat__padding--regular)', + minHeight: `calc(${CSSTokens.MinHeightBubble} - ${CSSTokens.PaddingRegular} * 2)`, + padding: CSSTokens.PaddingRegular, '&.webchat__text-content--is-markdown': { display: 'flex', flexDirection: 'column', - gap: 'var(--webchat__padding--regular)' + gap: CSSTokens.PaddingRegular }, '& .webchat__text-content__markdown > :first-child': { @@ -23,7 +23,7 @@ export default function createTextContentStyle() { }, '& .webchat__text-content__markdown img:not(.webchat__render-markdown__external-link-icon)': { - maxWidth: 'var(--webchat__max-width--bubble)', + maxWidth: CSSTokens.MaxWidthBubble, width: '100%' }, diff --git a/packages/component/src/Styles/StyleSet/ThumbButton.ts b/packages/component/src/Styles/StyleSet/ThumbButton.ts index 56a63dac79..7f5eba727e 100644 --- a/packages/component/src/Styles/StyleSet/ThumbButton.ts +++ b/packages/component/src/Styles/StyleSet/ThumbButton.ts @@ -1,3 +1,5 @@ +import CSSTokens from '../CSSTokens'; + export default function () { return { '&.webchat__thumb-button': { @@ -13,20 +15,15 @@ export default function () { width: 16, '&:active': { - // background: 'var(--pva__palette__neutral-light)' background: '#EDEBE9' }, '&:focus': { - /* TODO: Verify with designer. This was #767676 (Gray ~120), this is now #8A8886 (Gray 110). */ - - // border: 'solid 1px var(--pva__palette__neutral-secondary-alt)' outline: 'solid 1px #605E5C' }, '& .webchat__thumb-button__image': { - /* TODO: Remove "color" if we want a different hover color. */ - color: 'var(--webchat__color--accent)', + color: CSSTokens.ColorAccent, width: 14 }, @@ -46,48 +43,3 @@ export default function () { } }; } - -// .webchat__thumb-button { -// appearance: none; -// background: Transparent; -// border: 0; -// border-radius: 2px; -// height: 16px; -// line-height: 0; -// /* The Fluent icon is larger than the button. We need to clip it. -// Without clipping, hover effect will appear on the edge of the button but not possible to click. */ -// overflow: hidden; -// padding: 0; -// width: 16px; -// } - -// .webchat__thumb-button:active { -// background: var(--pva__palette__neutral-light); -// } - -// .webchat__thumb-button:focus-visible { -// /* TODO: Verify with designer. This was #767676 (Gray ~120), this is now #8A8886 (Gray 110). */ - -// outline: solid 1px var(--pva__palette__neutral-secondary-alt); -// } - -// .webchat__thumb-button__image { -// /* TODO: Remove "color" if we want a different hover color. */ -// color: var(--pva__semantic-colors__link); -// width: 14px; -// } - -// .webchat__thumb-button:hover -// .webchat__thumb-button__image:not(.webchat__thumb-button__image--is-filled) { -// display: none; -// } - -// .webchat__thumb-button--is-pressed -// .webchat__thumb-button__image:not(.webchat__thumb-button__image--is-filled) { -// display: none; -// } - -// .webchat__thumb-button:not(:hover):not(.webchat__thumb-button--is-pressed) -// .webchat__thumb-button__image--is-filled { -// display: none; -// } diff --git a/packages/component/src/Styles/createStyleSet.ts b/packages/component/src/Styles/createStyleSet.ts index 74907fb81c..361ed1368d 100644 --- a/packages/component/src/Styles/createStyleSet.ts +++ b/packages/component/src/Styles/createStyleSet.ts @@ -11,7 +11,7 @@ import createCarouselFilmStrip from './StyleSet/CarouselFilmStrip'; import createCarouselFilmStripAttachment from './StyleSet/CarouselFilmStripAttachment'; import createCarouselFlipper from './StyleSet/CarouselFlipper'; import createConnectivityNotification from './StyleSet/ConnectivityNotification'; -import createCSSVariablesStyle from './StyleSet/CSSVariables'; +import createCSSCustomPropertiesStyle from './StyleSet/CSSCustomProperties'; import createDictationInterimsStyle from './StyleSet/DictationInterims'; import createErrorBoxStyle from './StyleSet/ErrorBox'; import createErrorNotificationStyle from './StyleSet/ErrorNotification'; @@ -55,7 +55,7 @@ import createYouTubeContentStyle from './StyleSet/YouTubeContent'; export default function createStyleSet(styleOptions: StyleOptions) { const strictStyleOptions = normalizeStyleOptions(styleOptions); - return { + return Object.freeze({ activities: createActivitiesStyle(), audioAttachment: createAudioAttachmentStyle(strictStyleOptions), audioContent: createAudioContentStyle(), @@ -99,7 +99,7 @@ export default function createStyleSet(styleOptions: StyleOptions) { // Following styles follows new house rules: // - Use CSS var instead of strictStyleOptions - cssVariables: createCSSVariablesStyle(strictStyleOptions), + cssCustomProperties: createCSSCustomPropertiesStyle(strictStyleOptions), linkDefinitions: createLinkDefinitionsStyle(), modalDialog: createModalDialogStyle(), renderMarkdown: createRenderMarkdownStyle(), @@ -107,5 +107,5 @@ export default function createStyleSet(styleOptions: StyleOptions) { slottedActivityStatus: createSlottedActivityStatusStyle(), textContent: createTextContentStyle(), thumbButton: createThumbButtonStyle() - }; + } as const); } diff --git a/packages/component/src/hooks/useStyleSet.ts b/packages/component/src/hooks/useStyleSet.ts index 980598630e..f876e7a9e7 100644 --- a/packages/component/src/hooks/useStyleSet.ts +++ b/packages/component/src/hooks/useStyleSet.ts @@ -1,5 +1,7 @@ +import type createStyleSet from '../Styles/createStyleSet'; import useWebChatUIContext from './internal/useWebChatUIContext'; -export default function useStyleSet(): [any] { - return [useWebChatUIContext().styleSet]; +// TODO: Seems type of value is `string`. +export default function useStyleSet(): readonly [Record, any>] { + return Object.freeze([useWebChatUIContext().styleSet] as const); } diff --git a/packages/component/src/providers/ModalDialog/ModalDialogComposer.tsx b/packages/component/src/providers/ModalDialog/ModalDialogComposer.tsx index 26c0e26afd..070de8c461 100644 --- a/packages/component/src/providers/ModalDialog/ModalDialogComposer.tsx +++ b/packages/component/src/providers/ModalDialog/ModalDialogComposer.tsx @@ -18,7 +18,7 @@ type Props = { children?: ReactNode }; const ModalDialogComposer = memo(({ children }: Props) => { const [renderFunctionAndDialogInit, setRenderFunctionAndDialogInit] = useState< RenderFunctionAndDialogInit | undefined - >(undefined); + >(); const close = useCallback(() => setRenderFunctionAndDialogInit(undefined), [setRenderFunctionAndDialogInit]); const showModal = useCallback<(render: RenderFunction, init?: DialogInit) => void>( diff --git a/packages/component/src/providers/ModalDialog/private/Popover.tsx b/packages/component/src/providers/ModalDialog/private/Popover.tsx index 2f8b7629b8..55963aa00d 100644 --- a/packages/component/src/providers/ModalDialog/private/Popover.tsx +++ b/packages/component/src/providers/ModalDialog/private/Popover.tsx @@ -1,5 +1,4 @@ import { hooks } from 'botframework-webchat-api'; -import { useRefFrom } from 'use-ref-from'; import classNames from 'classnames'; import React, { memo, type PropsWithChildren, useCallback, useEffect, useRef } from 'react'; @@ -27,12 +26,10 @@ const ModalDialog = memo( const [{ modalDialog: modalDialogStyleSet }] = useStyleSet(); const dialogRef = useRef(null); const localize = useLocalizer(); - const onDismissRef = useRefFrom(onDismiss); const closeButtonAlt = localize('KEYBOARD_HELP_CLOSE_BUTTON_ALT'); const handleCloseButtonClick = useCallback(() => dialogRef.current?.close(), [dialogRef]); - const handleClose = useCallback(() => onDismissRef.current?.(), [onDismissRef]); useEffect(() => dialogRef.current?.showModal(), [dialogRef]); @@ -42,7 +39,7 @@ const ModalDialog = memo( aria-label={!ariaLabelledBy ? ariaLabel : undefined} aria-labelledby={ariaLabelledBy} className={classNames('webchat__modal-dialog', className, modalDialogStyleSet + '')} - onClose={handleClose} + onClose={onDismiss} open={false} ref={dialogRef} role="dialog" diff --git a/packages/component/src/types/external/SchemaOrg/Claim.spec.ts b/packages/component/src/types/external/OrgSchema/Claim.spec.ts similarity index 100% rename from packages/component/src/types/external/SchemaOrg/Claim.spec.ts rename to packages/component/src/types/external/OrgSchema/Claim.spec.ts diff --git a/packages/component/src/types/external/SchemaOrg/Claim.ts b/packages/component/src/types/external/OrgSchema/Claim.ts similarity index 100% rename from packages/component/src/types/external/SchemaOrg/Claim.ts rename to packages/component/src/types/external/OrgSchema/Claim.ts diff --git a/packages/component/src/types/external/SchemaOrg/Project.spec.ts b/packages/component/src/types/external/OrgSchema/Project.spec.ts similarity index 100% rename from packages/component/src/types/external/SchemaOrg/Project.spec.ts rename to packages/component/src/types/external/OrgSchema/Project.spec.ts diff --git a/packages/component/src/types/external/SchemaOrg/Project.ts b/packages/component/src/types/external/OrgSchema/Project.ts similarity index 100% rename from packages/component/src/types/external/SchemaOrg/Project.ts rename to packages/component/src/types/external/OrgSchema/Project.ts diff --git a/packages/component/src/types/external/SchemaOrg/ReplyAction.spec.ts b/packages/component/src/types/external/OrgSchema/ReplyAction.spec.ts similarity index 100% rename from packages/component/src/types/external/SchemaOrg/ReplyAction.spec.ts rename to packages/component/src/types/external/OrgSchema/ReplyAction.spec.ts diff --git a/packages/component/src/types/external/SchemaOrg/ReplyAction.ts b/packages/component/src/types/external/OrgSchema/ReplyAction.ts similarity index 100% rename from packages/component/src/types/external/SchemaOrg/ReplyAction.ts rename to packages/component/src/types/external/OrgSchema/ReplyAction.ts diff --git a/packages/component/src/types/external/SchemaOrg/Thing.spec.ts b/packages/component/src/types/external/OrgSchema/Thing.spec.ts similarity index 100% rename from packages/component/src/types/external/SchemaOrg/Thing.spec.ts rename to packages/component/src/types/external/OrgSchema/Thing.spec.ts diff --git a/packages/component/src/types/external/SchemaOrg/Thing.ts b/packages/component/src/types/external/OrgSchema/Thing.ts similarity index 90% rename from packages/component/src/types/external/SchemaOrg/Thing.ts rename to packages/component/src/types/external/OrgSchema/Thing.ts index 1f4aebd97a..7aae6df6ee 100644 --- a/packages/component/src/types/external/SchemaOrg/Thing.ts +++ b/packages/component/src/types/external/OrgSchema/Thing.ts @@ -1,4 +1,4 @@ -import { type SchemaOrgThing } from 'botframework-webchat-core'; +import { type OrgSchemaThing } from 'botframework-webchat-core'; /** * The most generic type of item. @@ -7,7 +7,7 @@ import { type SchemaOrgThing } from 'botframework-webchat-core'; * * @see https://schema.org/Thing */ -export type Thing = SchemaOrgThing & { +export type Thing = OrgSchemaThing & { '@id'?: string; /** An alias for the item. */ diff --git a/packages/component/src/types/external/SchemaOrg/VoteAction.spec.ts b/packages/component/src/types/external/OrgSchema/VoteAction.spec.ts similarity index 100% rename from packages/component/src/types/external/SchemaOrg/VoteAction.spec.ts rename to packages/component/src/types/external/OrgSchema/VoteAction.spec.ts diff --git a/packages/component/src/types/external/SchemaOrg/VoteAction.ts b/packages/component/src/types/external/OrgSchema/VoteAction.ts similarity index 100% rename from packages/component/src/types/external/SchemaOrg/VoteAction.ts rename to packages/component/src/types/external/OrgSchema/VoteAction.ts diff --git a/packages/component/src/types/internal/TypeOfArray.ts b/packages/component/src/types/internal/TypeOfArray.ts index 5f6592efd7..3c02b91b10 100644 --- a/packages/component/src/types/internal/TypeOfArray.ts +++ b/packages/component/src/types/internal/TypeOfArray.ts @@ -1,7 +1,5 @@ export type TypeOfArray | ReadonlyArray> = T extends (infer I)[] ? I - : T extends Array - ? I - : T extends ReadonlyArray + : T extends readonly (infer I)[] ? I : T; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 048405ea50..f8295b8d10 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -48,7 +48,7 @@ import type { DirectLineSuggestedAction } from './types/external/DirectLineSugge import type { DirectLineThumbnailCard } from './types/external/DirectLineThumbnailCard'; import type { DirectLineVideoCard } from './types/external/DirectLineVideoCard'; import type { GlobalScopePonyfill } from './types/GlobalScopePonyfill'; -import type { SchemaOrgThing } from './types/external/SchemaOrgThing'; +import type { OrgSchemaThing } from './types/external/OrgSchemaThing'; import type { WebChatActivity } from './types/WebChatActivity'; const Constants = { ActivityClientState, DictateState }; @@ -106,6 +106,6 @@ export type { DirectLineVideoCard, OneOrMany, GlobalScopePonyfill, - SchemaOrgThing, + OrgSchemaThing, WebChatActivity }; diff --git a/packages/core/src/types/WebChatActivity.ts b/packages/core/src/types/WebChatActivity.ts index 3cf0052329..9bd2cab842 100644 --- a/packages/core/src/types/WebChatActivity.ts +++ b/packages/core/src/types/WebChatActivity.ts @@ -10,7 +10,7 @@ import type { AnyAnd } from './AnyAnd'; import type { DirectLineAttachment } from './external/DirectLineAttachment'; import type { DirectLineSuggestedAction } from './external/DirectLineSuggestedAction'; -import type { SchemaOrgThing } from './external/SchemaOrgThing'; +import type { OrgSchemaThing } from './external/OrgSchemaThing'; type SupportedRole = 'bot' | 'channel' | 'user'; type SupportedSendStatus = 'sending' | 'send failed' | 'sent'; @@ -111,7 +111,7 @@ type ClientCapabilitiesEntity = { type Entity = | ClientCapabilitiesEntity - | SchemaOrgThing + | OrgSchemaThing | AnyAnd<{ type: Exclude }>; // Channel account - https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#channel-account diff --git a/packages/core/src/types/external/SchemaOrgThing.ts b/packages/core/src/types/external/OrgSchemaThing.ts similarity index 80% rename from packages/core/src/types/external/SchemaOrgThing.ts rename to packages/core/src/types/external/OrgSchemaThing.ts index 8f795abf6b..83aefa66d3 100644 --- a/packages/core/src/types/external/SchemaOrgThing.ts +++ b/packages/core/src/types/external/OrgSchemaThing.ts @@ -5,7 +5,7 @@ * * @see https://schema.org/Thing */ -export type SchemaOrgThing = { +export type OrgSchemaThing = { '@context': 'https://schema.org'; '@type': T; type: `https://schema.org/${T}`;