diff --git a/__tests__/html/focusManagement.disableHeroCard.obsolete.js b/__tests__/html/focusManagement.disableHeroCard.obsolete.js deleted file mode 100644 index ff0a0e1726..0000000000 --- a/__tests__/html/focusManagement.disableHeroCard.obsolete.js +++ /dev/null @@ -1,6 +0,0 @@ -/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */ - -describe('focus management', () => { - test('focus should not move after hero card is disable after obsolete', () => - runHTML('focusManagement.disableHeroCard.obsolete.html')); -}); diff --git a/__tests__/html/focusManagement.disableHeroCard.obsolete.html b/__tests__/html2/focusManagement/disableHeroCard.obsolete.html similarity index 96% rename from __tests__/html/focusManagement.disableHeroCard.obsolete.html rename to __tests__/html2/focusManagement/disableHeroCard.obsolete.html index 9f8ec98ede..8710fa4800 100644 --- a/__tests__/html/focusManagement.disableHeroCard.obsolete.html +++ b/__tests__/html2/focusManagement/disableHeroCard.obsolete.html @@ -64,7 +64,7 @@ firstButton.focus(); // THEN: It should show a focus indicator around the first button. - await host.snapshot(); + await host.snapshot('local'); // WHEN: The first button is clicked. await firstButton.click(); @@ -79,7 +79,7 @@ expect(secondButton.getAttribute('aria-disabled')).toBe('true'); // THEN: The second button should be grayed out. - await host.snapshot(); + await host.snapshot('local'); // THEN: The first button should still be kept as focused. expect(document.activeElement).toBe(firstButton); @@ -94,7 +94,7 @@ expect(pageElements.focusedActivity()).toBe(pageElements.activities()[1]); // THEN: It should focus on the transcript with the first hero card selected. - await host.snapshot(); + await host.snapshot('local'); // WHEN: ENTER key is pressed. It should not focus in the activity because the activity no longer contains any interactive elements. // All buttons in the activity is now disabled. @@ -107,7 +107,7 @@ expect(pageElements.focusedActivity()).toBe(pageElements.activities()[1]); // THEN: It should focus on the transcript with the first hero card selected. - await host.snapshot(); + await host.snapshot('local'); // WHEN: Using arrow keys to move to the second hero card, then focus and press on the first button. await host.sendKeys('ARROW_DOWN', 'ARROW_DOWN', 'ENTER', 'ENTER'); @@ -119,7 +119,7 @@ await pageConditions.scrollToBottomCompleted(); // THEN: It should submit the hero card and scroll to the bottom. - await host.snapshot(); + await host.snapshot('local'); }); diff --git a/__tests__/__image_snapshots__/html/focus-management-disable-hero-card-obsolete-js-focus-management-focus-should-not-move-after-hero-card-is-disable-after-obsolete-1-snap.png b/__tests__/html2/focusManagement/disableHeroCard.obsolete.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/html/focus-management-disable-hero-card-obsolete-js-focus-management-focus-should-not-move-after-hero-card-is-disable-after-obsolete-1-snap.png rename to __tests__/html2/focusManagement/disableHeroCard.obsolete.html.snap-1.png diff --git a/__tests__/__image_snapshots__/html/focus-management-disable-hero-card-obsolete-js-focus-management-focus-should-not-move-after-hero-card-is-disable-after-obsolete-2-snap.png b/__tests__/html2/focusManagement/disableHeroCard.obsolete.html.snap-2.png similarity index 100% rename from __tests__/__image_snapshots__/html/focus-management-disable-hero-card-obsolete-js-focus-management-focus-should-not-move-after-hero-card-is-disable-after-obsolete-2-snap.png rename to __tests__/html2/focusManagement/disableHeroCard.obsolete.html.snap-2.png diff --git a/__tests__/__image_snapshots__/html/focus-management-disable-hero-card-obsolete-js-focus-management-focus-should-not-move-after-hero-card-is-disable-after-obsolete-3-snap.png b/__tests__/html2/focusManagement/disableHeroCard.obsolete.html.snap-3.png similarity index 100% rename from __tests__/__image_snapshots__/html/focus-management-disable-hero-card-obsolete-js-focus-management-focus-should-not-move-after-hero-card-is-disable-after-obsolete-3-snap.png rename to __tests__/html2/focusManagement/disableHeroCard.obsolete.html.snap-3.png diff --git a/__tests__/__image_snapshots__/html/focus-management-disable-hero-card-obsolete-js-focus-management-focus-should-not-move-after-hero-card-is-disable-after-obsolete-4-snap.png b/__tests__/html2/focusManagement/disableHeroCard.obsolete.html.snap-4.png similarity index 100% rename from __tests__/__image_snapshots__/html/focus-management-disable-hero-card-obsolete-js-focus-management-focus-should-not-move-after-hero-card-is-disable-after-obsolete-4-snap.png rename to __tests__/html2/focusManagement/disableHeroCard.obsolete.html.snap-4.png diff --git a/__tests__/__image_snapshots__/html/focus-management-disable-hero-card-obsolete-js-focus-management-focus-should-not-move-after-hero-card-is-disable-after-obsolete-5-snap.png b/__tests__/html2/focusManagement/disableHeroCard.obsolete.html.snap-5.png similarity index 100% rename from __tests__/__image_snapshots__/html/focus-management-disable-hero-card-obsolete-js-focus-management-focus-should-not-move-after-hero-card-is-disable-after-obsolete-5-snap.png rename to __tests__/html2/focusManagement/disableHeroCard.obsolete.html.snap-5.png diff --git a/packages/component/src/Middleware/ActivityGrouping/ui/RenderActivityGrouping.tsx b/packages/component/src/Middleware/ActivityGrouping/ui/RenderActivityGrouping.tsx index 26337165c6..3b8e9a365e 100644 --- a/packages/component/src/Middleware/ActivityGrouping/ui/RenderActivityGrouping.tsx +++ b/packages/component/src/Middleware/ActivityGrouping/ui/RenderActivityGrouping.tsx @@ -1,27 +1,30 @@ import { hooks } from 'botframework-webchat-api'; import { type WebChatActivity } from 'botframework-webchat-core'; +import { validateProps } from 'botframework-webchat-react-valibot'; import React, { Fragment, memo } from 'react'; -import useGetRenderActivityCallback from '../../../providers/RenderingActivities/useGetRenderActivityCallback'; +import { array, custom, object, pipe, readonly, safeParse, type InferInput } from 'valibot'; + import TranscriptActivity from '../../../Transcript/TranscriptActivity'; const { useGetKeyByActivity } = hooks; -type RenderActivityGroupingProps = Readonly<{ - activities: readonly WebChatActivity[]; -}>; +const renderActivityGroupingPropsSchema = pipe( + object({ + activities: pipe(array(custom(value => safeParse(object({}), value).success)), readonly()) + }), + readonly() +); + +type RenderActivityGroupingProps = InferInput; -const RenderActivityGrouping = ({ activities }: RenderActivityGroupingProps) => { +const RenderActivityGrouping = (props: RenderActivityGroupingProps) => { + const { activities } = validateProps(renderActivityGroupingPropsSchema, props); const getKeyByActivity = useGetKeyByActivity(); - const getRenderActivityCallback = useGetRenderActivityCallback(); return ( {activities.map(activity => ( - + ))} ); diff --git a/packages/component/src/Transcript/TranscriptActivity.tsx b/packages/component/src/Transcript/TranscriptActivity.tsx index fec94f1286..1d6ab0be7e 100644 --- a/packages/component/src/Transcript/TranscriptActivity.tsx +++ b/packages/component/src/Transcript/TranscriptActivity.tsx @@ -1,23 +1,31 @@ -import { hooks, type ActivityComponentFactory } from 'botframework-webchat-api'; +import { hooks } from 'botframework-webchat-api'; import { type WebChatActivity } from 'botframework-webchat-core'; +import { validateProps } from 'botframework-webchat-react-valibot'; import React, { memo, useCallback, useMemo } from 'react'; +import { custom, object, pipe, readonly, safeParse, type InferInput } from 'valibot'; import useFirstActivityInSenderGroup from '../Middleware/ActivityGrouping/ui/SenderGrouping/useFirstActivity'; import useLastActivityInSenderGroup from '../Middleware/ActivityGrouping/ui/SenderGrouping/useLastActivity'; import useFirstActivityInStatusGroup from '../Middleware/ActivityGrouping/ui/StatusGrouping/useFirstActivity'; import useLastActivityInStatusGroup from '../Middleware/ActivityGrouping/ui/StatusGrouping/useLastActivity'; import useActivityElementMapRef from '../providers/ChatHistoryDOM/useActivityElementRef'; +import useGetRenderActivityCallback from '../providers/RenderingActivities/useGetRenderActivityCallback'; import isZeroOrPositive from '../Utils/isZeroOrPositive'; import ActivityRow from './ActivityRow'; const { useCreateActivityStatusRenderer, useCreateAvatarRenderer, useGetKeyByActivity, useStyleOptions } = hooks; -type TranscriptActivityProps = Readonly<{ - activity: WebChatActivity; - renderActivity: Exclude, false>; -}>; +const transcriptActivityPropsSchema = pipe( + object({ + activity: custom(value => safeParse(object({}), value).success) + }), + readonly() +); -const TranscriptActivity = ({ activity, renderActivity }: TranscriptActivityProps) => { +type TranscriptActivityProps = InferInput; + +const TranscriptActivity = (props: TranscriptActivityProps) => { + const { activity } = validateProps(transcriptActivityPropsSchema, props); const [{ bubbleFromUserNubOffset, bubbleNubOffset, groupTimestamp, showAvatarInGroup }] = useStyleOptions(); const [firstActivityInSenderGroup] = useFirstActivityInSenderGroup(); const [firstActivityInStatusGroup] = useFirstActivityInStatusGroup(); @@ -26,6 +34,7 @@ const TranscriptActivity = ({ activity, renderActivity }: TranscriptActivityProp const activityElementMapRef = useActivityElementMapRef(); const createActivityStatusRenderer = useCreateActivityStatusRenderer(); const getKeyByActivity = useGetKeyByActivity(); + const getRenderActivityCallback = useGetRenderActivityCallback(); const renderAvatar = useCreateAvatarRenderer(); const activityKey: string = useMemo(() => getKeyByActivity(activity), [activity, getKeyByActivity]); @@ -83,6 +92,8 @@ const TranscriptActivity = ({ activity, renderActivity }: TranscriptActivityProp showCallout = true; } + const renderActivity = getRenderActivityCallback(activity); + const children = useMemo( () => renderActivity({