@envive-ai/react-toolkit-v3 0.3.22 → 0.3.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AnimatedText/AnimatedText.d.cts +3 -3
- package/dist/AnimatedText/AnimatedText.d.ts +3 -3
- package/dist/CSSVariablesEditor/CssVariablesEditorComponent.d.cts +2 -2
- package/dist/CSSVariablesEditor/CssVariablesEditorComponent.d.ts +2 -2
- package/dist/CSSVariablesEditor/hooks/useGetCssVariablesOptions.cjs +1 -1
- package/dist/CSSVariablesEditor/hooks/useGetCssVariablesOptions.js +1 -1
- package/dist/CSSVariablesEditor/hooks/useGetCurrentMerchantColors.cjs +1 -1
- package/dist/CSSVariablesEditor/hooks/useGetCurrentMerchantColors.js +1 -1
- package/dist/CSSVariablesEditor/hooks/useGetDefaultCssVariables.cjs +4 -4
- package/dist/CSSVariablesEditor/hooks/useGetDefaultCssVariables.js +4 -4
- package/dist/CSSVariablesEditor/hooks/useHandleUpdateCssVars.cjs +3 -3
- package/dist/CSSVariablesEditor/hooks/useHandleUpdateCssVars.js +3 -3
- package/dist/Carousel/Carousel.d.cts +2 -2
- package/dist/Carousel/Carousel.d.ts +2 -2
- package/dist/Carousel/components/Badge.cjs +1 -1
- package/dist/Carousel/components/Badge.js +1 -1
- package/dist/Carousel/components/Container.cjs +1 -1
- package/dist/Carousel/components/Container.js +1 -1
- package/dist/ChatFooter/ChatFooter.cjs +7 -3
- package/dist/ChatFooter/ChatFooter.d.cts +7 -3
- package/dist/ChatFooter/ChatFooter.d.ts +7 -3
- package/dist/ChatFooter/ChatFooter.js +7 -3
- package/dist/ChatFooter/components/Layout.cjs +1 -1
- package/dist/ChatFooter/components/Layout.js +1 -1
- package/dist/ChatFooter/components/index.d.cts +10 -6
- package/dist/ChatFooter/components/index.d.ts +10 -6
- package/dist/ChatFooter/types/types.d.cts +16 -0
- package/dist/ChatFooter/types/types.d.ts +16 -0
- package/dist/ChatHeader/ChatHeader.cjs +2 -1
- package/dist/ChatHeader/ChatHeader.d.cts +2 -2
- package/dist/ChatHeader/ChatHeader.d.ts +2 -2
- package/dist/ChatHeader/ChatHeader.js +2 -1
- package/dist/ChatHeader/components/Handle.cjs +2 -2
- package/dist/ChatHeader/components/Handle.js +2 -2
- package/dist/ChatHeader/hooks/useGetHandleProperties.cjs +4 -2
- package/dist/ChatHeader/hooks/useGetHandleProperties.js +4 -2
- package/dist/ChatHeader/hooks/useGetLayoutProperties.cjs +1 -1
- package/dist/ChatHeader/hooks/useGetLayoutProperties.js +1 -1
- package/dist/ChatHeader/hooks/useGetToggleOptionProperties.cjs +1 -1
- package/dist/ChatHeader/hooks/useGetToggleOptionProperties.js +1 -1
- package/dist/ChatPreview/ChatPreview.cjs +6 -3
- package/dist/ChatPreview/ChatPreview.d.cts +2 -2
- package/dist/ChatPreview/ChatPreview.d.ts +2 -2
- package/dist/ChatPreview/ChatPreview.js +6 -3
- package/dist/ChatPreview/index.d.cts +2 -2
- package/dist/ChatPreview/index.d.ts +2 -2
- package/dist/ChatPreview/types/types.d.cts +13 -1
- package/dist/ChatPreview/types/types.d.ts +13 -1
- package/dist/ChatPreviewComparison/ChatPreviewComparison.cjs +6 -3
- package/dist/ChatPreviewComparison/ChatPreviewComparison.d.cts +2 -2
- package/dist/ChatPreviewComparison/ChatPreviewComparison.d.ts +2 -2
- package/dist/ChatPreviewComparison/ChatPreviewComparison.js +6 -3
- package/dist/ChatPreviewComparison/components/Layout.cjs +2 -2
- package/dist/ChatPreviewComparison/components/Layout.js +2 -2
- package/dist/ChatPreviewComparison/index.d.cts +2 -2
- package/dist/ChatPreviewComparison/index.d.ts +2 -2
- package/dist/ChatPreviewComparison/types/types.d.cts +13 -1
- package/dist/ChatPreviewComparison/types/types.d.ts +13 -1
- package/dist/ChatPreviewLoading/ChatPreviewLoading.cjs +7 -2
- package/dist/ChatPreviewLoading/ChatPreviewLoading.d.cts +4 -3
- package/dist/ChatPreviewLoading/ChatPreviewLoading.d.ts +2 -1
- package/dist/ChatPreviewLoading/ChatPreviewLoading.js +7 -2
- package/dist/ChatPreviewLoading/types/types.d.cts +4 -0
- package/dist/ChatPreviewLoading/types/types.d.ts +4 -0
- package/dist/Container/Container.d.cts +174 -174
- package/dist/Container/Container.d.ts +174 -174
- package/dist/DesignTokens/DesignTokensComponent.d.cts +2 -2
- package/dist/DesignTokens/DesignTokensComponent.d.ts +2 -2
- package/dist/DesignTokens/components/FontFamily.cjs +1 -1
- package/dist/DesignTokens/components/FontFamily.js +1 -1
- package/dist/DesignTokens/components/FontSize.cjs +1 -1
- package/dist/DesignTokens/components/FontSize.js +1 -1
- package/dist/DesignTokens/components/FontWeight.cjs +1 -1
- package/dist/DesignTokens/components/FontWeight.js +1 -1
- package/dist/DesignTokens/components/LetterSpacing.cjs +1 -1
- package/dist/DesignTokens/components/LetterSpacing.js +1 -1
- package/dist/DesignTokens/components/LineHeight.cjs +1 -1
- package/dist/DesignTokens/components/LineHeight.js +1 -1
- package/dist/DesignTokens/components/Typography.cjs +1 -1
- package/dist/DesignTokens/components/Typography.js +1 -1
- package/dist/Disclaimer/components/Container.cjs +1 -1
- package/dist/Disclaimer/components/Container.js +1 -1
- package/dist/DocumentRetrievalCard/DocumentRetrievalCard.d.cts +2 -2
- package/dist/DocumentRetrievalCard/DocumentRetrievalCard.d.ts +2 -2
- package/dist/DocumentRetrievalCard/components/Image.cjs +1 -1
- package/dist/DocumentRetrievalCard/components/Image.js +1 -1
- package/dist/DocumentRetrievalCard/components/Layout.cjs +1 -1
- package/dist/DocumentRetrievalCard/components/Layout.js +1 -1
- package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Icon.cjs +1 -1
- package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Icon.js +1 -1
- package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Label.cjs +1 -1
- package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Label.js +1 -1
- package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Layout.cjs +1 -1
- package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Layout.js +1 -1
- package/dist/FloatingButton/FloatingButton.d.cts +2 -2
- package/dist/FloatingButton/FloatingButton.d.ts +2 -2
- package/dist/FloatingButton/components/Button.cjs +1 -1
- package/dist/FloatingButton/components/Button.js +1 -1
- package/dist/FloatingButton/components/Container.cjs +1 -1
- package/dist/FloatingButton/components/Container.js +1 -1
- package/dist/FloatingButton/components/Wrapper.cjs +1 -1
- package/dist/FloatingButton/components/Wrapper.js +1 -1
- package/dist/FloatingChat/FloatingChat.cjs +27 -4
- package/dist/FloatingChat/FloatingChat.d.cts +2 -2
- package/dist/FloatingChat/FloatingChat.d.ts +2 -2
- package/dist/FloatingChat/FloatingChat.js +28 -5
- package/dist/FloatingChat/components/AgentMessage.cjs +2 -2
- package/dist/FloatingChat/components/AgentMessage.js +2 -2
- package/dist/FloatingChat/components/ChatMessages.cjs +1 -1
- package/dist/FloatingChat/components/ChatMessages.js +1 -1
- package/dist/FloatingChat/components/Layout.cjs +1 -1
- package/dist/FloatingChat/components/Layout.js +1 -1
- package/dist/FloatingChat/components/ProductResultsModal.cjs +1 -1
- package/dist/FloatingChat/components/ProductResultsModal.js +1 -1
- package/dist/FloatingChat/components/ResultsGridView.cjs +1 -1
- package/dist/FloatingChat/components/ResultsGridView.js +1 -1
- package/dist/FloatingChat/components/SalesAgentBadgeContent.cjs +1 -1
- package/dist/FloatingChat/components/SalesAgentBadgeContent.js +1 -1
- package/dist/FloatingChat/hooks/useChatSuggestions.cjs +3 -5
- package/dist/FloatingChat/hooks/useChatSuggestions.js +4 -6
- package/dist/Form/Form.cjs +1 -1
- package/dist/Form/Form.js +1 -1
- package/dist/Form/components/Layout.cjs +1 -1
- package/dist/Form/components/Layout.js +1 -1
- package/dist/Form/components/SubmitButtonItem.cjs +1 -1
- package/dist/Form/components/SubmitButtonItem.js +1 -1
- package/dist/Form/components/TextFieldItem.cjs +1 -1
- package/dist/Form/components/TextFieldItem.js +1 -1
- package/dist/FullPageSalesAgent/FullPageSalesAgent.cjs +24 -2
- package/dist/FullPageSalesAgent/FullPageSalesAgent.d.cts +5 -3
- package/dist/FullPageSalesAgent/FullPageSalesAgent.d.ts +5 -3
- package/dist/FullPageSalesAgent/FullPageSalesAgent.js +25 -3
- package/dist/FullPageSalesAgent/components/Layout.cjs +1 -1
- package/dist/FullPageSalesAgent/components/Layout.js +1 -1
- package/dist/Image/Image.cjs +1 -1
- package/dist/Image/Image.d.cts +2 -2
- package/dist/Image/Image.d.ts +2 -2
- package/dist/Image/Image.js +1 -1
- package/dist/ImageGallery/ImageGallery.d.cts +2 -2
- package/dist/ImageGallery/components/Layout.cjs +1 -1
- package/dist/ImageGallery/components/Layout.js +1 -1
- package/dist/ImageGallery/utils/functions.cjs +1 -1
- package/dist/ImageGallery/utils/functions.js +1 -1
- package/dist/MarkdownProcessor/MarkdownProcessor.d.cts +2 -2
- package/dist/MarkdownProcessor/MarkdownProcessor.d.ts +2 -2
- package/dist/Message/components/Layout.cjs +1 -1
- package/dist/Message/components/Layout.js +1 -1
- package/dist/OrderLookupCard/OrderLookupCard.cjs +1 -1
- package/dist/OrderLookupCard/OrderLookupCard.js +1 -1
- package/dist/OrderLookupCard/components/Layout.cjs +1 -1
- package/dist/OrderLookupCard/components/Layout.js +1 -1
- package/dist/OrderLookupCard/components/MoreProductsOverlay.cjs +1 -1
- package/dist/OrderLookupCard/components/MoreProductsOverlay.js +1 -1
- package/dist/OrderLookupCard/components/ProductImageGridItem.cjs +1 -1
- package/dist/OrderLookupCard/components/ProductImageGridItem.js +1 -1
- package/dist/OrderLookupCard/components/ProductImageItem.cjs +1 -1
- package/dist/OrderLookupCard/components/ProductImageItem.js +1 -1
- package/dist/OrderLookupCard/components/ProductImagesGrid.cjs +1 -1
- package/dist/OrderLookupCard/components/ProductImagesGrid.js +1 -1
- package/dist/OrderLookupCard/components/StatusLabel.cjs +1 -1
- package/dist/OrderLookupCard/components/StatusLabel.js +1 -1
- package/dist/OrderLookupCard/components/TrackOrderLink.cjs +1 -1
- package/dist/OrderLookupCard/components/TrackOrderLink.js +1 -1
- package/dist/ProductCard/ProductCard.cjs +6 -2
- package/dist/ProductCard/ProductCard.d.cts +6 -3
- package/dist/ProductCard/ProductCard.d.ts +6 -3
- package/dist/ProductCard/ProductCard.js +6 -2
- package/dist/ProductCard/index.d.cts +2 -2
- package/dist/ProductCard/index.d.ts +2 -2
- package/dist/ProductCard/types/index.d.cts +7 -1
- package/dist/ProductCard/types/index.d.ts +7 -1
- package/dist/PromptButton/PromptButton.cjs +1 -1
- package/dist/PromptButton/PromptButton.d.cts +2 -2
- package/dist/PromptButton/PromptButton.d.ts +2 -2
- package/dist/PromptButton/PromptButton.js +1 -1
- package/dist/PromptButton/components/Layout.cjs +1 -1
- package/dist/PromptButton/components/Layout.js +1 -1
- package/dist/PromptButton/components/Loading.cjs +1 -1
- package/dist/PromptButton/components/Loading.js +1 -1
- package/dist/PromptButtonCarouselWithImage/PromptButtonCarouselWithImage.cjs +5 -2
- package/dist/PromptButtonCarouselWithImage/PromptButtonCarouselWithImage.d.cts +6 -3
- package/dist/PromptButtonCarouselWithImage/PromptButtonCarouselWithImage.d.ts +6 -3
- package/dist/PromptButtonCarouselWithImage/PromptButtonCarouselWithImage.js +5 -2
- package/dist/PromptButtonCarouselWithImage/components/Layout.cjs +1 -1
- package/dist/PromptButtonCarouselWithImage/components/Layout.js +1 -1
- package/dist/PromptButtonCarouselWithImage/components/Skeleton.cjs +1 -1
- package/dist/PromptButtonCarouselWithImage/components/Skeleton.js +1 -1
- package/dist/PromptButtonCarouselWithImage/types/types.d.cts +12 -0
- package/dist/PromptButtonCarouselWithImage/types/types.d.ts +12 -0
- package/dist/PromptCarousel/PromptCarousel.cjs +1 -1
- package/dist/PromptCarousel/PromptCarousel.d.cts +2 -2
- package/dist/PromptCarousel/PromptCarousel.d.ts +2 -2
- package/dist/PromptCarousel/PromptCarousel.js +1 -1
- package/dist/ReviewCard/ReviewCard.d.cts +2 -2
- package/dist/ReviewCard/ReviewCard.d.ts +2 -2
- package/dist/ReviewCard/components/Container.cjs +1 -1
- package/dist/ReviewCard/components/Container.js +1 -1
- package/dist/ReviewCard/components/Rating.cjs +1 -2
- package/dist/ReviewCard/components/Rating.js +1 -2
- package/dist/ReviewCard/components/ReadMoreButton.cjs +1 -1
- package/dist/ReviewCard/components/ReadMoreButton.js +1 -1
- package/dist/ReviewCard/components/index.d.cts +6 -6
- package/dist/ReviewCard/components/index.d.ts +6 -6
- package/dist/SalesAgentProductCard/SalesAgentProductCard.d.cts +2 -2
- package/dist/SalesAgentProductCard/SalesAgentProductCard.d.ts +2 -2
- package/dist/SalesAgentProductCard/components/Container.cjs +1 -1
- package/dist/SalesAgentProductCard/components/Container.js +1 -1
- package/dist/SalesAgentProductCard/components/ProductImage.cjs +1 -1
- package/dist/SalesAgentProductCard/components/ProductImage.js +1 -1
- package/dist/SalesAgentProductCard/components/ProductName.cjs +1 -1
- package/dist/SalesAgentProductCard/components/ProductName.js +1 -1
- package/dist/SalesAgentProductCard/components/index.d.cts +8 -8
- package/dist/SalesAgentProductCard/components/index.d.ts +8 -8
- package/dist/SocialProof/SocialProof.cjs +6 -3
- package/dist/SocialProof/SocialProof.d.cts +2 -2
- package/dist/SocialProof/SocialProof.d.ts +2 -2
- package/dist/SocialProof/SocialProof.js +6 -3
- package/dist/SocialProof/components/Headline.cjs +1 -1
- package/dist/SocialProof/components/Headline.js +1 -1
- package/dist/SocialProof/components/LayoutFourHorizontal.cjs +1 -1
- package/dist/SocialProof/components/LayoutFourHorizontal.js +1 -1
- package/dist/SocialProof/components/Textfield.cjs +5 -2
- package/dist/SocialProof/components/Textfield.js +5 -2
- package/dist/SocialProof/index.d.cts +2 -2
- package/dist/SocialProof/index.d.ts +2 -2
- package/dist/SocialProof/types/types.d.cts +13 -1
- package/dist/SocialProof/types/types.d.ts +13 -1
- package/dist/SparkleAnimation/SparkleAnimation.d.cts +2 -2
- package/dist/SparkleAnimation/SparkleAnimation.d.ts +2 -2
- package/dist/Stack/Stack.d.cts +2 -2
- package/dist/Stack/Stack.d.ts +2 -2
- package/dist/TextField/TextField.cjs +35 -3
- package/dist/TextField/TextField.d.cts +5 -1
- package/dist/TextField/TextField.d.ts +5 -1
- package/dist/TextField/TextField.js +35 -3
- package/dist/TextField/components/Input.cjs +1 -1
- package/dist/TextField/components/Input.js +1 -1
- package/dist/TextField/components/Layout.cjs +11 -8
- package/dist/TextField/components/Layout.js +11 -8
- package/dist/TextField/components/SendIcon.cjs +1 -1
- package/dist/TextField/components/SendIcon.js +1 -1
- package/dist/TextField/components/VoiceInputButton.cjs +45 -0
- package/dist/TextField/components/VoiceInputButton.js +39 -0
- package/dist/TextField/components/index.cjs +3 -1
- package/dist/TextField/components/index.js +3 -1
- package/dist/TextField/hooks/useGetMicButtonContainerProperties.cjs +20 -0
- package/dist/TextField/hooks/useGetMicButtonContainerProperties.js +19 -0
- package/dist/TextField/hooks/useGetSkeletonProperties.cjs +1 -1
- package/dist/TextField/hooks/useGetSkeletonProperties.js +1 -1
- package/dist/TextField/hooks/useVoiceInput.cjs +59 -0
- package/dist/TextField/hooks/useVoiceInput.js +57 -0
- package/dist/TextField/types/index.d.cts +11 -0
- package/dist/TextField/types/index.d.ts +11 -0
- package/dist/TextField/utils/getLayoutStateProperties.cjs +9 -1
- package/dist/TextField/utils/getLayoutStateProperties.js +8 -1
- package/dist/Title/Title.cjs +1 -1
- package/dist/Title/Title.js +1 -1
- package/dist/Title/components/Layout.cjs +1 -1
- package/dist/Title/components/Layout.js +1 -1
- package/dist/TitledPromptCarousel/TitledPromptCarousel.cjs +1 -1
- package/dist/TitledPromptCarousel/TitledPromptCarousel.d.cts +2 -2
- package/dist/TitledPromptCarousel/TitledPromptCarousel.d.ts +2 -2
- package/dist/TitledPromptCarousel/TitledPromptCarousel.js +1 -1
- package/dist/Tokens/index.cjs +6 -6
- package/dist/Tokens/index.js +6 -6
- package/dist/TypingAnimation/TypingAnimation.cjs +7 -3
- package/dist/TypingAnimation/TypingAnimation.d.cts +2 -2
- package/dist/TypingAnimation/TypingAnimation.d.ts +2 -2
- package/dist/TypingAnimation/TypingAnimation.js +7 -3
- package/dist/TypingAnimation/index.d.cts +2 -2
- package/dist/TypingAnimation/index.d.ts +2 -2
- package/dist/TypingAnimation/types/index.d.cts +13 -1
- package/dist/TypingAnimation/types/index.d.ts +13 -1
- package/dist/Typography/Typography.d.cts +4 -4
- package/dist/Typography/Typography.d.ts +4 -4
- package/dist/WelcomeMessage/components/Container.cjs +1 -1
- package/dist/WelcomeMessage/components/Container.js +1 -1
- package/dist/WidgetTextField/WidgetTextField.cjs +39 -7
- package/dist/WidgetTextField/WidgetTextField.d.cts +7 -3
- package/dist/WidgetTextField/WidgetTextField.d.ts +7 -3
- package/dist/WidgetTextField/WidgetTextField.js +33 -2
- package/dist/WidgetTextField/components/Container.cjs +32 -26
- package/dist/WidgetTextField/components/Container.js +32 -26
- package/dist/WidgetTextField/components/Skeleton.cjs +1 -1
- package/dist/WidgetTextField/components/Skeleton.js +1 -1
- package/dist/WidgetTextField/hooks/useGetContainerProperties.cjs +5 -3
- package/dist/WidgetTextField/hooks/useGetContainerProperties.js +5 -3
- package/dist/WidgetTextField/hooks/useGetMicWidgetButtonProperties.cjs +20 -0
- package/dist/WidgetTextField/hooks/useGetMicWidgetButtonProperties.js +19 -0
- package/dist/WidgetTextField/types/types.d.cts +21 -0
- package/dist/WidgetTextField/types/types.d.ts +21 -0
- package/dist/WidgetWrapper/WidgetWrapper.cjs +1 -1
- package/dist/WidgetWrapper/WidgetWrapper.d.cts +2 -2
- package/dist/WidgetWrapper/WidgetWrapper.d.ts +2 -2
- package/dist/WidgetWrapper/WidgetWrapper.js +1 -1
- package/dist/WidgetWrapper/hooks/useGetWrapperProperties.cjs +1 -1
- package/dist/WidgetWrapper/hooks/useGetWrapperProperties.js +1 -1
- package/dist/WidgetWrapperWithTitle/WidgetWrapperWithTitle.d.cts +2 -2
- package/dist/WidgetWrapperWithTitle/WidgetWrapperWithTitle.d.ts +2 -2
- package/dist/node_modules/jotai/esm/react.cjs +87 -0
- package/dist/node_modules/jotai/esm/react.js +88 -2
- package/dist/node_modules/jotai/esm/vanilla/internals.cjs +2 -1
- package/dist/node_modules/jotai/esm/vanilla/internals.js +1 -1
- package/dist/packages/components-v3/tokens/typography/typography.cjs +1 -1
- package/dist/packages/components-v3/tokens/typography/typography.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/utils/resolveTheme.cjs +1 -1
- package/dist/utils/resolveTheme.js +1 -1
- package/package.json +2 -1
- package/src/components/ChatFooter/ChatFooter.tsx +8 -0
- package/src/components/ChatFooter/__tests__/ChatFooter.test.tsx +43 -0
- package/src/components/ChatFooter/components/TextField.tsx +12 -0
- package/src/components/ChatFooter/types/types.ts +17 -0
- package/src/components/ChatHeader/ChatHeader.tsx +1 -0
- package/src/components/ChatHeader/components/Handle.tsx +7 -2
- package/src/components/ChatHeader/hooks/useGetHandleProperties.ts +5 -1
- package/src/components/ChatHeader/hooks/useGetToggleOptionProperties.ts +1 -1
- package/src/components/ChatHeader/types/index.ts +1 -0
- package/src/components/ChatPreview/ChatPreview.tsx +13 -2
- package/src/components/ChatPreview/__tests__/ChatPreview.test.tsx +44 -0
- package/src/components/ChatPreview/index.ts +1 -1
- package/src/components/ChatPreview/types/types.ts +13 -0
- package/src/components/ChatPreviewComparison/ChatPreviewComparison.tsx +6 -0
- package/src/components/ChatPreviewComparison/__tests__/ChatPreviewComparison.test.tsx +44 -0
- package/src/components/ChatPreviewComparison/index.ts +1 -1
- package/src/components/ChatPreviewComparison/types/types.ts +13 -0
- package/src/components/ChatPreviewLoading/ChatPreviewLoading.tsx +5 -3
- package/src/components/ChatPreviewLoading/__tests__/ChatPreviewLoading.test.tsx +40 -0
- package/src/components/ChatPreviewLoading/types/types.ts +5 -0
- package/src/components/FloatingChat/FloatingChat.tsx +38 -7
- package/src/components/FloatingChat/hooks/useChatSuggestions.ts +8 -12
- package/src/components/FullPageSalesAgent/FullPageSalesAgent.tsx +32 -1
- package/src/components/ProductCard/ProductCard.tsx +7 -0
- package/src/components/ProductCard/__tests__/ProductCard.test.tsx +33 -0
- package/src/components/ProductCard/index.ts +1 -1
- package/src/components/ProductCard/types/index.ts +6 -0
- package/src/components/PromptButtonCarouselWithImage/PromptButtonCarouselWithImage.tsx +6 -0
- package/src/components/PromptButtonCarouselWithImage/__tests__/PromptButtonCarouselWithImage.test.tsx +34 -0
- package/src/components/PromptButtonCarouselWithImage/types/types.ts +12 -0
- package/src/components/ReviewCard/components/Rating.tsx +0 -1
- package/src/components/SocialProof/SocialProof.tsx +6 -0
- package/src/components/SocialProof/__tests__/SocialProof.test.tsx +58 -0
- package/src/components/SocialProof/components/Textfield.tsx +9 -0
- package/src/components/SocialProof/index.ts +1 -1
- package/src/components/SocialProof/types/types.ts +13 -0
- package/src/components/TextField/TextField.tsx +49 -0
- package/src/components/TextField/__tests__/TextField.test.tsx +3 -3
- package/src/components/TextField/__tests__/VoiceInputButton.test.tsx +175 -0
- package/src/components/TextField/components/Layout.tsx +24 -17
- package/src/components/TextField/components/VoiceInputButton.tsx +69 -0
- package/src/components/TextField/components/index.ts +2 -0
- package/src/components/TextField/hooks/useGetMicButtonContainerProperties.ts +38 -0
- package/src/components/TextField/hooks/useGetSkeletonProperties.ts +1 -1
- package/src/components/TextField/hooks/useVoiceInput.ts +77 -0
- package/src/components/TextField/types/index.ts +11 -0
- package/src/components/TextField/utils/getLayoutStateProperties.ts +8 -0
- package/src/components/TypingAnimation/TypingAnimation.tsx +7 -0
- package/src/components/TypingAnimation/__tests__/TypingAnimation.test.tsx +47 -0
- package/src/components/TypingAnimation/index.ts +1 -1
- package/src/components/TypingAnimation/types/index.ts +14 -1
- package/src/components/WidgetTextField/WidgetTextField.tsx +47 -0
- package/src/components/WidgetTextField/__tests__/WidgetTextField.test.tsx +119 -4
- package/src/components/WidgetTextField/components/Container.tsx +40 -27
- package/src/components/WidgetTextField/hooks/useGetContainerProperties.ts +16 -4
- package/src/components/WidgetTextField/hooks/useGetMicWidgetButtonProperties.ts +38 -0
- package/src/components/WidgetTextField/types/types.ts +21 -0
- package/src/components/WidgetWrapper/hooks/useGetWrapperProperties.ts +1 -1
|
@@ -59,6 +59,10 @@ export type PromptButtonCarouselWithImageProps = {
|
|
|
59
59
|
* Whether to hide the text field.
|
|
60
60
|
*/
|
|
61
61
|
hideTextField?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Whether to enable voice input.
|
|
64
|
+
*/
|
|
65
|
+
voiceInputEnabled?: boolean;
|
|
62
66
|
/**
|
|
63
67
|
* Callback function invoked when a prompt button is clicked.
|
|
64
68
|
* Receives the button's text as a parameter.
|
|
@@ -97,4 +101,12 @@ export type PromptButtonCarouselWithImageProps = {
|
|
|
97
101
|
* Receives the placeholder text as a parameter.
|
|
98
102
|
*/
|
|
99
103
|
handleTextFieldClick: (text: string) => void;
|
|
104
|
+
/**
|
|
105
|
+
* Callback function invoked when voice transcription starts.
|
|
106
|
+
*/
|
|
107
|
+
onTranscriptionStarted?: () => void;
|
|
108
|
+
/**
|
|
109
|
+
* Callback function invoked when voice transcription completes.
|
|
110
|
+
*/
|
|
111
|
+
onTranscriptionCompleted?: (transcript: string) => void;
|
|
100
112
|
};
|
|
@@ -44,6 +44,7 @@ export const SocialProof = ({
|
|
|
44
44
|
images,
|
|
45
45
|
logoSrc,
|
|
46
46
|
titleLabel,
|
|
47
|
+
voiceInputEnabled,
|
|
47
48
|
} = widgetContentProps ?? {};
|
|
48
49
|
|
|
49
50
|
const numberOfCustomersText = useSocialProofCount({
|
|
@@ -63,6 +64,8 @@ export const SocialProof = ({
|
|
|
63
64
|
handleSecondaryButtonTouchStart,
|
|
64
65
|
handleSecondaryButtonTouchEnd,
|
|
65
66
|
handleTextFieldClick,
|
|
67
|
+
onTranscriptionStarted,
|
|
68
|
+
onTranscriptionCompleted,
|
|
66
69
|
} = widgetEventProps ?? {};
|
|
67
70
|
|
|
68
71
|
const finalTheme = resolveTheme(theme);
|
|
@@ -120,6 +123,9 @@ export const SocialProof = ({
|
|
|
120
123
|
placeholder={textFieldPlaceholderText}
|
|
121
124
|
handleTextFieldClick={handleTextFieldClick}
|
|
122
125
|
iconVariant={hasDynamicLayout ? IconVariant.DEFAULT : IconVariant.SEARCH}
|
|
126
|
+
voiceInputEnabled={voiceInputEnabled}
|
|
127
|
+
onTranscriptionStarted={onTranscriptionStarted}
|
|
128
|
+
onTranscriptionCompleted={onTranscriptionCompleted}
|
|
123
129
|
/>
|
|
124
130
|
);
|
|
125
131
|
|
|
@@ -531,4 +531,62 @@ describe('SocialProof', () => {
|
|
|
531
531
|
expect(screen.getByText(/\d+ customers/)).toBeInTheDocument();
|
|
532
532
|
});
|
|
533
533
|
});
|
|
534
|
+
|
|
535
|
+
describe('Voice input', () => {
|
|
536
|
+
it('should render with voiceInputEnabled set to true', async () => {
|
|
537
|
+
render(
|
|
538
|
+
<SocialProof
|
|
539
|
+
{...defaultProps}
|
|
540
|
+
widgetContentProps={{
|
|
541
|
+
...defaultProps.widgetContentProps,
|
|
542
|
+
voiceInputEnabled: true,
|
|
543
|
+
}}
|
|
544
|
+
widgetStyleProps={{
|
|
545
|
+
hideTextField: false,
|
|
546
|
+
imageGalleryLayout: ImageGalleryLayout.FOUR_GRID,
|
|
547
|
+
}}
|
|
548
|
+
widgetEventProps={{ handleTextFieldClick: vi.fn() }}
|
|
549
|
+
/>,
|
|
550
|
+
);
|
|
551
|
+
await waitFor(() => {
|
|
552
|
+
expect(screen.getByText('What can I help you find?')).toBeInTheDocument();
|
|
553
|
+
});
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it('should render with voiceInputEnabled set to false', () => {
|
|
557
|
+
render(
|
|
558
|
+
<SocialProof
|
|
559
|
+
{...defaultProps}
|
|
560
|
+
widgetContentProps={{
|
|
561
|
+
...defaultProps.widgetContentProps,
|
|
562
|
+
voiceInputEnabled: false,
|
|
563
|
+
}}
|
|
564
|
+
widgetStyleProps={{
|
|
565
|
+
hideTextField: false,
|
|
566
|
+
imageGalleryLayout: ImageGalleryLayout.FOUR_GRID,
|
|
567
|
+
}}
|
|
568
|
+
widgetEventProps={{ handleTextFieldClick: vi.fn() }}
|
|
569
|
+
/>,
|
|
570
|
+
);
|
|
571
|
+
expect(screen.getByText(/\d+ customers/)).toBeInTheDocument();
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
it('should render with voiceInputEnabled undefined (default)', () => {
|
|
575
|
+
render(
|
|
576
|
+
<SocialProof
|
|
577
|
+
{...defaultProps}
|
|
578
|
+
widgetContentProps={{
|
|
579
|
+
...defaultProps.widgetContentProps,
|
|
580
|
+
voiceInputEnabled: undefined,
|
|
581
|
+
}}
|
|
582
|
+
widgetStyleProps={{
|
|
583
|
+
hideTextField: false,
|
|
584
|
+
imageGalleryLayout: ImageGalleryLayout.FOUR_GRID,
|
|
585
|
+
}}
|
|
586
|
+
widgetEventProps={{ handleTextFieldClick: vi.fn() }}
|
|
587
|
+
/>,
|
|
588
|
+
);
|
|
589
|
+
expect(screen.getByText(/\d+ customers/)).toBeInTheDocument();
|
|
590
|
+
});
|
|
591
|
+
});
|
|
534
592
|
});
|
|
@@ -8,6 +8,9 @@ export type TextfieldProps = {
|
|
|
8
8
|
placeholder: string;
|
|
9
9
|
handleTextFieldClick: (text: string) => void;
|
|
10
10
|
iconVariant?: IconVariant;
|
|
11
|
+
voiceInputEnabled?: boolean;
|
|
12
|
+
onTranscriptionStarted?: () => void;
|
|
13
|
+
onTranscriptionCompleted?: (transcript: string) => void;
|
|
11
14
|
};
|
|
12
15
|
|
|
13
16
|
export const Textfield = ({
|
|
@@ -15,6 +18,9 @@ export const Textfield = ({
|
|
|
15
18
|
placeholder,
|
|
16
19
|
handleTextFieldClick,
|
|
17
20
|
iconVariant = IconVariant.DEFAULT,
|
|
21
|
+
voiceInputEnabled,
|
|
22
|
+
onTranscriptionStarted,
|
|
23
|
+
onTranscriptionCompleted,
|
|
18
24
|
}: TextfieldProps) => {
|
|
19
25
|
const finalTheme = resolveTheme(theme);
|
|
20
26
|
|
|
@@ -24,6 +30,9 @@ export const Textfield = ({
|
|
|
24
30
|
placeholder={placeholder}
|
|
25
31
|
iconVariant={iconVariant}
|
|
26
32
|
onClick={() => handleTextFieldClick(placeholder)}
|
|
33
|
+
enableVoiceInput={voiceInputEnabled}
|
|
34
|
+
onTranscriptionStarted={onTranscriptionStarted}
|
|
35
|
+
onTranscriptionCompleted={onTranscriptionCompleted}
|
|
27
36
|
/>
|
|
28
37
|
);
|
|
29
38
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { SocialProof } from './SocialProof';
|
|
2
|
-
export type { SocialProofProps } from './types/types';
|
|
2
|
+
export type { SocialProofProps, WidgetEventProps } from './types/types';
|
|
3
3
|
export { DynamicLayout, PageVariant, WidgetKind } from './types/types';
|
|
4
4
|
export { useSocialProofCount } from './hooks';
|
|
@@ -114,6 +114,11 @@ export type WidgetContentProps = {
|
|
|
114
114
|
* - `FOUR_HORIZONTAL` and `FOUR_GRID`: require 4 images
|
|
115
115
|
*/
|
|
116
116
|
images?: ImageGalleryImage[];
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Whether to enable voice input.
|
|
120
|
+
*/
|
|
121
|
+
voiceInputEnabled?: boolean;
|
|
117
122
|
};
|
|
118
123
|
|
|
119
124
|
/**
|
|
@@ -281,6 +286,14 @@ export type WidgetEventProps = {
|
|
|
281
286
|
* @param text - The placeholder text of the text field
|
|
282
287
|
*/
|
|
283
288
|
handleTextFieldClick?: (text: string) => void;
|
|
289
|
+
/**
|
|
290
|
+
* Callback function invoked when voice transcription starts.
|
|
291
|
+
*/
|
|
292
|
+
onTranscriptionStarted?: () => void;
|
|
293
|
+
/**
|
|
294
|
+
* Callback function invoked when voice transcription completes.
|
|
295
|
+
*/
|
|
296
|
+
onTranscriptionCompleted?: (transcript: string) => void;
|
|
284
297
|
};
|
|
285
298
|
|
|
286
299
|
/**
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
|
+
import { useCallback } from 'react';
|
|
2
3
|
import { Theme } from '../../../tokens/theme/theme';
|
|
3
4
|
import { resolveTheme } from '../utils/resolveTheme';
|
|
4
5
|
import { TextFieldComponents } from './components';
|
|
6
|
+
import { useGetMicButtonContainerProperties } from './hooks/useGetMicButtonContainerProperties';
|
|
5
7
|
import { useGetSkeletonProperties } from './hooks/useGetSkeletonProperties';
|
|
6
8
|
import { useTextFieldFocus } from './hooks/useTextFieldFocus';
|
|
7
9
|
import { useTextFieldSubmit } from './hooks/useTextFieldSubmit';
|
|
8
10
|
import { useTextFieldValue } from './hooks/useTextFieldValue';
|
|
11
|
+
import { useVoiceInput } from './hooks/useVoiceInput';
|
|
9
12
|
import type { TextFieldProps } from './types';
|
|
10
13
|
|
|
11
14
|
/**
|
|
@@ -23,9 +26,13 @@ export const TextField = ({
|
|
|
23
26
|
id,
|
|
24
27
|
testId,
|
|
25
28
|
className,
|
|
29
|
+
inputClassName,
|
|
26
30
|
style,
|
|
27
31
|
ariaLabel,
|
|
28
32
|
isLoading = false,
|
|
33
|
+
enableVoiceInput = false,
|
|
34
|
+
onTranscriptionStarted,
|
|
35
|
+
onTranscriptionCompleted,
|
|
29
36
|
}: TextFieldProps): JSX.Element => {
|
|
30
37
|
const { currentValue, hasValue, handleChange, resetValue } = useTextFieldValue(
|
|
31
38
|
controlledValue,
|
|
@@ -41,9 +48,30 @@ export const TextField = ({
|
|
|
41
48
|
resetValue,
|
|
42
49
|
);
|
|
43
50
|
|
|
51
|
+
const handleVoiceTranscript = useCallback(
|
|
52
|
+
(transcript: string) => {
|
|
53
|
+
resetValue();
|
|
54
|
+
handleChange(transcript);
|
|
55
|
+
onTranscriptionCompleted?.(transcript);
|
|
56
|
+
},
|
|
57
|
+
[handleChange, resetValue, onTranscriptionCompleted],
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const {
|
|
61
|
+
isListening,
|
|
62
|
+
handleToggleListening,
|
|
63
|
+
handleAbortListening,
|
|
64
|
+
browserSupportsSpeechRecognition,
|
|
65
|
+
} = useVoiceInput({
|
|
66
|
+
onTranscriptionStarted,
|
|
67
|
+
onTranscriptionCompleted: handleVoiceTranscript,
|
|
68
|
+
disabled,
|
|
69
|
+
});
|
|
70
|
+
|
|
44
71
|
const handleInternalFocus = () => {
|
|
45
72
|
handleFocus();
|
|
46
73
|
onFocus?.();
|
|
74
|
+
handleAbortListening();
|
|
47
75
|
};
|
|
48
76
|
const { skeletonClass } = useGetSkeletonProperties(isLoading);
|
|
49
77
|
|
|
@@ -60,6 +88,7 @@ export const TextField = ({
|
|
|
60
88
|
onBlur={handleBlur}
|
|
61
89
|
disabled={disabled || isLoading}
|
|
62
90
|
ariaLabel={ariaLabel}
|
|
91
|
+
className={inputClassName}
|
|
63
92
|
/>
|
|
64
93
|
);
|
|
65
94
|
|
|
@@ -73,11 +102,31 @@ export const TextField = ({
|
|
|
73
102
|
/>
|
|
74
103
|
) : null;
|
|
75
104
|
|
|
105
|
+
const { micButtonContainerClasses } = useGetMicButtonContainerProperties({
|
|
106
|
+
theme: resolvedTheme,
|
|
107
|
+
isListening,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const voiceInputButtonClassName = classNames(micButtonContainerClasses, skeletonClass);
|
|
111
|
+
|
|
112
|
+
const voiceInputButton =
|
|
113
|
+
enableVoiceInput && browserSupportsSpeechRecognition ? (
|
|
114
|
+
<TextFieldComponents.VoiceInputButton
|
|
115
|
+
theme={resolvedTheme}
|
|
116
|
+
isListening={isListening}
|
|
117
|
+
onToggleListening={handleToggleListening}
|
|
118
|
+
ariaLabel={ariaLabel}
|
|
119
|
+
isLoading={isLoading}
|
|
120
|
+
className={voiceInputButtonClassName}
|
|
121
|
+
/>
|
|
122
|
+
) : null;
|
|
123
|
+
|
|
76
124
|
return (
|
|
77
125
|
<TextFieldComponents.Layout
|
|
78
126
|
theme={resolvedTheme}
|
|
79
127
|
input={input}
|
|
80
128
|
sendIcon={sendIcon}
|
|
129
|
+
voiceInputButton={voiceInputButton}
|
|
81
130
|
isFocused={isFocused}
|
|
82
131
|
hasValue={hasValue}
|
|
83
132
|
disabled={disabled}
|
|
@@ -69,7 +69,7 @@ describe('TextField', () => {
|
|
|
69
69
|
placeholder="Ask me anything..."
|
|
70
70
|
/>,
|
|
71
71
|
);
|
|
72
|
-
const element = container.
|
|
72
|
+
const element = container.querySelector('.envive-tw-flex-1') as HTMLElement;
|
|
73
73
|
expect(element).toHaveStyle({ marginTop: '20px' });
|
|
74
74
|
});
|
|
75
75
|
});
|
|
@@ -410,7 +410,7 @@ describe('TextField', () => {
|
|
|
410
410
|
placeholder="Ask me anything..."
|
|
411
411
|
/>,
|
|
412
412
|
);
|
|
413
|
-
const element = container.
|
|
413
|
+
const element = container.querySelector('.envive-tw-flex-1') as HTMLElement;
|
|
414
414
|
expect(element.className).toContain('envive-tw-animate-pulse');
|
|
415
415
|
});
|
|
416
416
|
|
|
@@ -422,7 +422,7 @@ describe('TextField', () => {
|
|
|
422
422
|
placeholder="Ask me anything..."
|
|
423
423
|
/>,
|
|
424
424
|
);
|
|
425
|
-
const element = container.
|
|
425
|
+
const element = container.querySelector('.envive-tw-flex-1') as HTMLElement;
|
|
426
426
|
expect(element.className).toContain('envive-tw-h-[40px]');
|
|
427
427
|
});
|
|
428
428
|
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VoiceInputButton Tests
|
|
3
|
+
*
|
|
4
|
+
* These tests validate the voice input functionality used by the VoiceInputButton component.
|
|
5
|
+
* The useVoiceInput hook handles speech recognition, including starting/stopping listening,
|
|
6
|
+
* managing mic permissions, and clearing previous transcriptions.
|
|
7
|
+
*/
|
|
8
|
+
import { act, renderHook } from '@testing-library/react';
|
|
9
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
10
|
+
import { useVoiceInput } from '../hooks/useVoiceInput';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Mock setup using vi.hoisted() to create spies that can be referenced before the mock is called.
|
|
14
|
+
* This is necessary because vi.mock() is hoisted to the top of the file and needs these
|
|
15
|
+
* function references to be available at module evaluation time.
|
|
16
|
+
*/
|
|
17
|
+
const spies = vi.hoisted(() => ({
|
|
18
|
+
// Mock functions for SpeechRecognition API methods
|
|
19
|
+
mockStartListening: vi.fn(() => Promise.resolve()),
|
|
20
|
+
mockStopListening: vi.fn(() => Promise.resolve()),
|
|
21
|
+
mockAbortListening: vi.fn(() => Promise.resolve()),
|
|
22
|
+
// Mock for the resetTranscript function from useSpeechRecognition
|
|
23
|
+
mockResetTranscript: vi.fn(),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Mock the react-speech-recognition module to control its behavior in tests.
|
|
28
|
+
* This allows us to:
|
|
29
|
+
* - Control whether the browser supports speech recognition
|
|
30
|
+
* - Control what transcript value is returned
|
|
31
|
+
* - Verify that SpeechRecognition methods are called correctly
|
|
32
|
+
*/
|
|
33
|
+
vi.mock('react-speech-recognition', () => ({
|
|
34
|
+
// The default export contains the static methods like startListening, stopListening, abortListening
|
|
35
|
+
default: {
|
|
36
|
+
startListening: spies.mockStartListening,
|
|
37
|
+
stopListening: spies.mockStopListening,
|
|
38
|
+
abortListening: spies.mockAbortListening,
|
|
39
|
+
},
|
|
40
|
+
// useSpeechRecognition hook provides transcript state and resetTranscript function
|
|
41
|
+
useSpeechRecognition: vi.fn(() => ({
|
|
42
|
+
transcript: '',
|
|
43
|
+
browserSupportsSpeechRecognition: true,
|
|
44
|
+
resetTranscript: spies.mockResetTranscript,
|
|
45
|
+
})),
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
describe('useVoiceInput', () => {
|
|
49
|
+
// Reset mocks before each test to ensure clean state
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
vi.clearAllMocks();
|
|
52
|
+
// Reset the implementation to default successful behavior
|
|
53
|
+
spies.mockStartListening.mockImplementation(() => Promise.resolve());
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('Basic initialization', () => {
|
|
57
|
+
it('should initialize with isListening as false', () => {
|
|
58
|
+
const { result } = renderHook(() => useVoiceInput({}));
|
|
59
|
+
// Voice input should not be active when the hook first mounts
|
|
60
|
+
expect(result.current.isListening).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should report browser supports speech recognition', () => {
|
|
64
|
+
const { result } = renderHook(() => useVoiceInput({}));
|
|
65
|
+
// Check if the browser has speech recognition support
|
|
66
|
+
expect(result.current.browserSupportsSpeechRecognition).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('Start listening', () => {
|
|
71
|
+
it('should start listening when button is clicked', async () => {
|
|
72
|
+
const { result } = renderHook(() => useVoiceInput({}));
|
|
73
|
+
|
|
74
|
+
await act(async () => {
|
|
75
|
+
// Call handleToggleListening to start listening
|
|
76
|
+
result.current.handleToggleListening();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Verify that listening state changed to active
|
|
80
|
+
expect(result.current.isListening).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should delete previous transcription when starting to listen', async () => {
|
|
84
|
+
const { result } = renderHook(() => useVoiceInput({}));
|
|
85
|
+
|
|
86
|
+
await act(async () => {
|
|
87
|
+
result.current.handleToggleListening();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Verify resetTranscript was called BEFORE starting new listening session
|
|
91
|
+
// This ensures previous transcription is cleared when starting fresh
|
|
92
|
+
expect(spies.mockResetTranscript).toHaveBeenCalled();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('Stop listening', () => {
|
|
97
|
+
it('should stop listening and call onTranscript with current transcript', async () => {
|
|
98
|
+
const onTranscriptionCompleted = vi.fn();
|
|
99
|
+
|
|
100
|
+
const { result } = renderHook(() =>
|
|
101
|
+
useVoiceInput({
|
|
102
|
+
onTranscriptionCompleted,
|
|
103
|
+
}),
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// First toggle: start listening
|
|
107
|
+
await act(async () => {
|
|
108
|
+
result.current.handleToggleListening();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Second toggle: stop listening - this should call onTranscript callback
|
|
112
|
+
await act(async () => {
|
|
113
|
+
result.current.handleToggleListening();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Verify that stopListening was called to stop speech recognition
|
|
117
|
+
expect(spies.mockStopListening).toHaveBeenCalled();
|
|
118
|
+
// Verify that the onTranscript callback was called with current transcript
|
|
119
|
+
expect(onTranscriptionCompleted).toHaveBeenCalledWith('');
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe('Mic permission handling', () => {
|
|
124
|
+
it('should handle mic permission denied error', async () => {
|
|
125
|
+
// Configure the mock to throw an error simulating permission denial
|
|
126
|
+
spies.mockStartListening.mockImplementationOnce(() => {
|
|
127
|
+
throw new Error('Permission denied');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const { result } = renderHook(() => useVoiceInput({}));
|
|
131
|
+
|
|
132
|
+
await act(async () => {
|
|
133
|
+
result.current.handleToggleListening();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// The hook should catch the error and set isListening back to false
|
|
137
|
+
expect(result.current.isListening).toBe(false);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should not start listening when disabled', () => {
|
|
141
|
+
// When disabled prop is true, the hook should not attempt to start listening
|
|
142
|
+
const { result } = renderHook(() => useVoiceInput({ disabled: true }));
|
|
143
|
+
|
|
144
|
+
result.current.handleToggleListening();
|
|
145
|
+
|
|
146
|
+
// isListening should remain false
|
|
147
|
+
expect(result.current.isListening).toBe(false);
|
|
148
|
+
// Verify that startListening was never called
|
|
149
|
+
expect(spies.mockStartListening).not.toHaveBeenCalled();
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('Abort listening', () => {
|
|
154
|
+
it('should abort listening and reset state', async () => {
|
|
155
|
+
const { result } = renderHook(() => useVoiceInput({}));
|
|
156
|
+
|
|
157
|
+
// Start listening first
|
|
158
|
+
await act(async () => {
|
|
159
|
+
result.current.handleToggleListening();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
expect(result.current.isListening).toBe(true);
|
|
163
|
+
|
|
164
|
+
// Call abort to stop listening immediately without collecting transcript
|
|
165
|
+
await act(async () => {
|
|
166
|
+
result.current.handleAbortListening();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Verify abortListening was called to immediately stop speech recognition
|
|
170
|
+
expect(spies.mockAbortListening).toHaveBeenCalled();
|
|
171
|
+
// Verify state is reset to false
|
|
172
|
+
expect(result.current.isListening).toBe(false);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
});
|
|
@@ -7,6 +7,7 @@ type LayoutProps = {
|
|
|
7
7
|
theme: Theme;
|
|
8
8
|
input: React.ReactNode;
|
|
9
9
|
sendIcon: React.ReactNode;
|
|
10
|
+
voiceInputButton?: React.ReactNode;
|
|
10
11
|
isFocused: boolean;
|
|
11
12
|
hasValue: boolean;
|
|
12
13
|
disabled?: boolean;
|
|
@@ -20,6 +21,7 @@ export const Layout = ({
|
|
|
20
21
|
theme,
|
|
21
22
|
input,
|
|
22
23
|
sendIcon,
|
|
24
|
+
voiceInputButton,
|
|
23
25
|
isFocused,
|
|
24
26
|
hasValue,
|
|
25
27
|
disabled = false,
|
|
@@ -37,29 +39,34 @@ export const Layout = ({
|
|
|
37
39
|
containerHoverClasses,
|
|
38
40
|
containerDisabledClasses,
|
|
39
41
|
} = useGetContainerProperties({ theme });
|
|
40
|
-
const { isActive, shouldShowFocus, shouldShowHover } = getLayoutStateProperties(
|
|
42
|
+
const { isActive, shouldShowFocus, shouldShowHover, containerClasses } = getLayoutStateProperties(
|
|
43
|
+
theme,
|
|
41
44
|
hasValue,
|
|
42
45
|
isFocused,
|
|
43
46
|
disabled,
|
|
44
47
|
);
|
|
45
48
|
|
|
46
49
|
return (
|
|
47
|
-
<div
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
50
|
+
<div className={containerClasses}>
|
|
51
|
+
<div
|
|
52
|
+
id={id}
|
|
53
|
+
data-testid={testId}
|
|
54
|
+
className={classNames(
|
|
55
|
+
'envive-tw-flex-1',
|
|
56
|
+
containerLayoutClasses,
|
|
57
|
+
containerDefaultClasses,
|
|
58
|
+
isActive ? containerBorderActiveClasses : containerBorderClasses,
|
|
59
|
+
shouldShowFocus && containerFocusClasses,
|
|
60
|
+
shouldShowHover && containerHoverClasses,
|
|
61
|
+
disabled && containerDisabledClasses,
|
|
62
|
+
className,
|
|
63
|
+
)}
|
|
64
|
+
style={style}
|
|
65
|
+
>
|
|
66
|
+
{input}
|
|
67
|
+
{sendIcon}
|
|
68
|
+
</div>
|
|
69
|
+
{voiceInputButton}
|
|
63
70
|
</div>
|
|
64
71
|
);
|
|
65
72
|
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import Mic from '@envive-ai/react-icons/Mic';
|
|
2
|
+
import MicThin from '@envive-ai/react-icons/MicThin';
|
|
3
|
+
import Stop from '@envive-ai/react-icons/Stop';
|
|
4
|
+
import StopSharp from '@envive-ai/react-icons/StopSharp';
|
|
5
|
+
import StopThin from '@envive-ai/react-icons/StopThin';
|
|
6
|
+
import { Theme } from '../../../../tokens/theme/theme';
|
|
7
|
+
|
|
8
|
+
type VoiceInputButtonProps = {
|
|
9
|
+
theme: Theme;
|
|
10
|
+
isListening?: boolean;
|
|
11
|
+
onToggleListening?: () => void;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
className?: string;
|
|
14
|
+
style?: React.CSSProperties;
|
|
15
|
+
ariaLabel?: string;
|
|
16
|
+
isLoading?: boolean;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const VoiceInputButton = ({
|
|
20
|
+
theme,
|
|
21
|
+
isListening,
|
|
22
|
+
onToggleListening,
|
|
23
|
+
disabled = false,
|
|
24
|
+
className,
|
|
25
|
+
style,
|
|
26
|
+
ariaLabel,
|
|
27
|
+
isLoading = false,
|
|
28
|
+
}: VoiceInputButtonProps): JSX.Element => {
|
|
29
|
+
const getMicIcon = (iconTheme: Theme) => {
|
|
30
|
+
switch (iconTheme) {
|
|
31
|
+
case Theme.MODERN:
|
|
32
|
+
return MicThin;
|
|
33
|
+
default:
|
|
34
|
+
return Mic;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const getStopIcon = (iconTheme: Theme) => {
|
|
39
|
+
switch (iconTheme) {
|
|
40
|
+
case Theme.MODERN:
|
|
41
|
+
return StopThin;
|
|
42
|
+
case Theme.MINIMAL:
|
|
43
|
+
return StopSharp;
|
|
44
|
+
default:
|
|
45
|
+
return Stop;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const MicIcon = getMicIcon(theme);
|
|
50
|
+
const StopIcon = getStopIcon(theme);
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<button
|
|
54
|
+
type="button"
|
|
55
|
+
onClick={onToggleListening}
|
|
56
|
+
disabled={disabled || isLoading}
|
|
57
|
+
className={className}
|
|
58
|
+
style={style}
|
|
59
|
+
aria-label={ariaLabel}
|
|
60
|
+
tabIndex={disabled || isLoading ? -1 : 0}
|
|
61
|
+
>
|
|
62
|
+
{isListening ? (
|
|
63
|
+
<StopIcon className="envive-tw-h-[24px]" />
|
|
64
|
+
) : (
|
|
65
|
+
<MicIcon className="envive-tw-h-[24px]" />
|
|
66
|
+
)}
|
|
67
|
+
</button>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Layout } from './Layout';
|
|
2
2
|
import { Input } from './Input';
|
|
3
3
|
import { SendIcon } from './SendIcon';
|
|
4
|
+
import { VoiceInputButton } from './VoiceInputButton';
|
|
4
5
|
|
|
5
6
|
export const TextFieldComponents = {
|
|
6
7
|
Layout,
|
|
7
8
|
Input,
|
|
8
9
|
SendIcon,
|
|
10
|
+
VoiceInputButton,
|
|
9
11
|
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { Theme } from '../../../../tokens/theme/theme';
|
|
3
|
+
|
|
4
|
+
export type UseGetMicButtonContainerPropertiesProps = {
|
|
5
|
+
theme: Theme;
|
|
6
|
+
isListening?: boolean;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type MicButtonContainerProperties = {
|
|
10
|
+
micButtonContainerClasses: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const useGetMicButtonContainerProperties = ({
|
|
14
|
+
theme,
|
|
15
|
+
isListening = false,
|
|
16
|
+
}: UseGetMicButtonContainerPropertiesProps): MicButtonContainerProperties => {
|
|
17
|
+
const micButtonContainerClasses = useMemo(() => {
|
|
18
|
+
const micButtonContainerClassesMap: Record<Theme, string> = {
|
|
19
|
+
[Theme.STANDARD]:
|
|
20
|
+
'envive-tw-flex envive-tw-h-[40px] envive-tw-w-[40px] envive-tw-py-2 envive-tw-px-2 envive-tw-justify-center envive-tw-items-center envive-tw-flex-shrink-0 envive-tw-border envive-tw-border-solid envive-tw-rounded-global-custom envive-tw-border-border-light hover:envive-tw-border-border-dark',
|
|
21
|
+
[Theme.MODERN]:
|
|
22
|
+
'envive-tw-flex envive-tw-h-[40px] envive-tw-w-[40px] envive-tw-py-2 envive-tw-px-2 envive-tw-justify-center envive-tw-items-center envive-tw-flex-shrink-0 envive-tw-border envive-tw-border-solid envive-tw-rounded-[4px] envive-tw-border-border-light hover:envive-tw-border-border-dark',
|
|
23
|
+
[Theme.MINIMAL]:
|
|
24
|
+
'envive-tw-flex envive-tw-h-[40px] envive-tw-w-[40px] envive-tw-justify-center envive-tw-items-center envive-tw-flex-shrink-0 envive-tw-border-b envive-tw-border-solid envive-tw-border-b-border-medium hover:envive-tw-border-b-border-dark',
|
|
25
|
+
[Theme.GLOBAL_CUSTOM]:
|
|
26
|
+
'envive-tw-flex envive-tw-h-[40px] envive-tw-w-[40px] envive-tw-py-2 envive-tw-px-2 envive-tw-justify-center envive-tw-items-center envive-tw-flex-shrink-0 envive-tw-border envive-tw-border-solid envive-tw-rounded-global-custom envive-tw-border-border-light hover:envive-tw-border-border-dark',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const baseClasses =
|
|
30
|
+
micButtonContainerClassesMap[theme] ?? micButtonContainerClassesMap[Theme.STANDARD];
|
|
31
|
+
|
|
32
|
+
return isListening
|
|
33
|
+
? `${baseClasses} envive-tw-bg-[--envive-colors-text-accent]`
|
|
34
|
+
: `${baseClasses} envive-tw-bg-background-light`;
|
|
35
|
+
}, [theme, isListening]);
|
|
36
|
+
|
|
37
|
+
return { micButtonContainerClasses };
|
|
38
|
+
};
|
|
@@ -4,7 +4,7 @@ export const useGetSkeletonProperties = (isLoading?: boolean) => {
|
|
|
4
4
|
const skeletonClass = useMemo(() => {
|
|
5
5
|
if (!isLoading) return undefined;
|
|
6
6
|
|
|
7
|
-
return 'envive-tw-animate-pulse';
|
|
7
|
+
return 'envive-tw-animate-pulse envive-tw-h-[40px]';
|
|
8
8
|
}, [isLoading]);
|
|
9
9
|
|
|
10
10
|
return { skeletonClass };
|