diff --git a/apps/react-storybook/package.json b/apps/react-storybook/package.json index 09be6de05ae9..4f669c489143 100644 --- a/apps/react-storybook/package.json +++ b/apps/react-storybook/package.json @@ -13,7 +13,8 @@ "dependencies": { "devextreme": "workspace:~", "devextreme-react": "workspace:~", - "inferno": "^7.4.9" + "inferno": "^7.4.9", + "html-react-parser": "^5.2.2" }, "devDependencies": { "@storybook/addon-essentials": "7.6.19", diff --git a/apps/react-storybook/stories/chat/Chat.stories.tsx b/apps/react-storybook/stories/chat/Chat.stories.tsx index 324cb7e50ec2..9405f051109c 100644 --- a/apps/react-storybook/stories/chat/Chat.stories.tsx +++ b/apps/react-storybook/stories/chat/Chat.stories.tsx @@ -1,10 +1,24 @@ import React, { useState, useCallback, useEffect, useMemo } from 'react'; -import {Chat, ChatTypes} from 'devextreme-react/chat' -import type {Meta, StoryObj} from '@storybook/react'; +import { Chat, ChatTypes } from 'devextreme-react/chat' +import { Button } from "devextreme-react"; +import type { Meta, StoryObj } from '@storybook/react'; import DataSource from 'devextreme/data/data_source'; import CustomStore from 'devextreme/data/custom_store'; -import { firstAuthor, secondAuthor, thirdAuthor, fourthAuthor, initialMessages, longError } from './data'; +import { + firstAuthor, + secondAuthor, + thirdAuthor, + fourthAuthor, + initialMessages, + longError, + REGENERATION_TEXT, + assistantReplies, + userRequest, + regenerationMessage, + assistant, +} from './data'; import { Popup } from 'devextreme-react/popup'; +import HTMLReactParser from 'html-react-parser'; import './styles.css'; @@ -477,3 +491,99 @@ export const TypingUsers: Story = { ); } } + +export const AIBotIntegration: Story = { + args: { + alerts: 'No alerts', + }, + argTypes: { + alerts: { + control: 'select', + defaultValue: 'No alerts', + options: [ + 'No alerts', + 'Limit reached', + ], + mapping: { + ['No alerts']: [], + ['Limit reached']: [{ message: 'Request limit reached, try again in a minute.' }], + }, + }, + }, + render: ({ alerts }) => { + const [isProcessing, setIsProcessing] = useState(false); + const [assistantReplyIndex, setAssistantReplyIndex] = useState(0); + const [copyButtonIcon, setCopyButtonIcon] = useState('copy'); + + const items = useMemo(() => { + const repliesCount = assistantReplies.length; + const assistantReply = isProcessing ? regenerationMessage : assistantReplies[assistantReplyIndex % repliesCount]; + + return [userRequest, assistantReply]; + }, [assistantReplyIndex, isProcessing]); + + const onRegenerateButtonClick = useCallback(async (): Promise => { + setIsProcessing(true); + + await new Promise((resolve) => { + setTimeout(resolve, 300); + }); + setAssistantReplyIndex((prev: number) => prev + 1); + + setIsProcessing(false); + }, []); + + const messageRender = useCallback(({ message }: { message: ChatTypes.Message }) => { + const { text = '', author } = message; + + const onCopyButtonClick = () => { + navigator.clipboard?.writeText(text); + setCopyButtonIcon('check'); + + setTimeout(() => { + setCopyButtonIcon('copy'); + }, 2500); + }; + + if (text === REGENERATION_TEXT || author !== assistant) { + return {text}; + } + + return ( + <> +
+ {HTMLReactParser(text)} +
+
+
+ + ); + }, [copyButtonIcon, onRegenerateButtonClick]); + + return ( +
+ +
+ ); + } +} diff --git a/apps/react-storybook/stories/chat/data.ts b/apps/react-storybook/stories/chat/data.ts index c5e7b209480a..360dd1284693 100644 --- a/apps/react-storybook/stories/chat/data.ts +++ b/apps/react-storybook/stories/chat/data.ts @@ -1,6 +1,7 @@ -import { SchedulerTypes } from 'devextreme-react/scheduler'; import { ChatTypes } from 'devextreme-react/chat'; +export const REGENERATION_TEXT = 'Regeneration...'; + export const firstAuthor: ChatTypes.User = { id: "c94c0e76-fb49-4b9b-8f07-9f93ed93b4f3", name: "John Doe", @@ -22,6 +23,12 @@ export const fourthAuthor: ChatTypes.User = { name: "Crash Bandicoot" }; +export const assistant: ChatTypes.User = { + id: 'assistant', + name: 'Virtual Assistant', + avatarUrl: "https://devexpress.github.io/DevExtreme/images/icons/bot.png", +}; + const todayDate = new Date(); const date = new Date(); @@ -86,3 +93,133 @@ export const initialMessages: ChatTypes.Message[] = [ ]; export const longError = { id: '1234', message: 'Error Message. An unexpected issue occurred while processing your request. Please check your internet connection or contact support for further assistance.' }; + +export const userRequest: ChatTypes.Message = + { + timestamp: todayDate, + author: secondAuthor, + text: "What is AI?" + } +; + +export const regenerationMessage: ChatTypes.Message = { + timestamp: todayDate, + author: assistant, + text: REGENERATION_TEXT, +}; + +export const assistantReplies: ChatTypes.Message[] = [ + { + timestamp: todayDate, + author: assistant, + text: `

Artificial Intelligence (AI) is a branch of computer science that focuses on + creating systems capable of performing tasks that typically require human intelligence. + These tasks include learning, reasoning, problem-solving, understanding natural + language, recognizing patterns, and even making decisions.

+

AI can be divided into several subfields and categories:

+

Types of AI Based on Capability

+ +

AI continues to evolve, impacting nearly every aspect of modern life while raising + ethical, societal, and technological challenges.

`.replace(/\s{2,}/gm, ''), + }, + { + timestamp: todayDate, + author: assistant, + text: `

Artificial Intelligence (AI) is a branch of computer science that focuses on + creating systems capable of performing tasks that typically require human intelligence. + These tasks include learning, reasoning, problem-solving, understanding natural + language, recognizing patterns, and even making decisions.

+

AI can be divided into several subfields and categories:

+

Types of AI Based on Functionality

+
    +
  1. Reactive Machines: AI systems that respond to specific inputs + with predefined outputs, without memory or past experiences influencing their + decisions (e.g., IBM's Deep Blue chess-playing computer). +
  2. +
  3. Limited Memory: AI systems that can use past data for a short + time to inform decisions, such as self-driving cars. +
  4. +
  5. Theory of Mind: A more advanced concept where AI would + understand emotions, beliefs, and intentions, allowing it to interact more + naturally with humans. This remains largely in the research phase. +
  6. +
  7. Self-Aware AI: A theoretical AI that has self-awareness and + consciousness. +
  8. +
+

AI continues to evolve, impacting nearly every aspect of modern life while raising + ethical, societal, and technological challenges.

`.replace(/\s{2,}/gm, ''), + }, + { + timestamp: todayDate, + author: assistant, + text: `

Artificial Intelligence (AI) is a branch of computer science that focuses on + creating systems capable of performing tasks that typically require human intelligence. + These tasks include learning, reasoning, problem-solving, understanding natural + language, recognizing patterns, and even making decisions.

+

AI can be divided into several subfields and categories:

+

Key Subfields of AI

+ +

AI continues to evolve, impacting nearly every aspect of modern life while raising + ethical, societal, and technological challenges.

`.replace(/\s{2,}/gm, ''), + }, + { + timestamp: todayDate, + author: assistant, + text: `

Artificial Intelligence (AI) is a branch of computer science that focuses on + creating systems capable of performing tasks that typically require human intelligence. + These tasks include learning, reasoning, problem-solving, understanding natural + language, recognizing patterns, and even making decisions.

+

AI can be divided into several subfields and categories:

+

Applications of AI

+
    +
  1. Healthcare: Diagnosing diseases, drug discovery, and patient + care. +
  2. +
  3. Finance: Fraud detection, stock market analysis, and personalized + financial advice. +
  4. +
  5. Transportation: Autonomous vehicles and traffic management.
  6. +
  7. Entertainment: Personalized content recommendations on platforms + like Netflix or Spotify. +
  8. +
  9. Customer Service: Chatbots and virtual assistants like Siri, + Alexa, or Google Assistant. +
  10. +
+

AI continues to evolve, impacting nearly every aspect of modern life while raising + ethical, societal, and technological challenges.

`.replace(/\s{2,}/gm, ''), + } +]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d3bf8520527f..b5765bb2df48 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -593,6 +593,9 @@ importers: devextreme-react: specifier: workspace:~ version: link:../../packages/devextreme-react/npm + html-react-parser: + specifier: ^5.2.2 + version: 5.2.2(@types/react@18.0.38)(react@18.0.0) inferno: specifier: ^7.4.9 version: 7.4.11 @@ -8215,8 +8218,8 @@ packages: domutils@2.8.0: resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} - domutils@3.1.0: - resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + domutils@3.2.1: + resolution: {integrity: sha512-xWXmuRnN9OMP6ptPd2+H0cCbcYBULa5YDTbMm/2lvkWvNA3O4wcW+GvzooqBuNM8yy6pl3VIAeJTUUWUbfI5Fw==} dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} @@ -8379,6 +8382,10 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + entities@6.0.0: + resolution: {integrity: sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==} + engines: {node: '>=0.12'} + env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} @@ -10025,6 +10032,9 @@ packages: html-dom-parser@1.2.0: resolution: {integrity: sha512-2HIpFMvvffsXHFUFjso0M9LqM+1Lm22BF+Df2ba+7QHJXjk63pWChEnI6YG27eaWqUdfnh5/Vy+OXrNTtepRsg==} + html-dom-parser@5.0.13: + resolution: {integrity: sha512-B7JonBuAfG32I7fDouUQEogBrz3jK9gAuN1r1AaXpED6dIhtg/JwiSRhjGL7aOJwRz3HU4efowCjQBaoXiREqg==} + html-element-map@1.3.1: resolution: {integrity: sha512-6XMlxrAFX4UEEGxctfFnmrFaaZFNf9i5fNuV5wZ3WWQ4FVaNP1aX1LkX9j2mfEx1NpjeE/rL3nmgEn23GdFmrg==} @@ -10048,6 +10058,15 @@ packages: peerDependencies: react: 0.14 || 15 || 16 || 17 || 18 + html-react-parser@5.2.2: + resolution: {integrity: sha512-yA5012CJGSFWYZsgYzfr6HXJgDap38/AEP4ra8Cw+WHIi2ZRDXRX/QVYdumRf1P8zKyScKd6YOrWYvVEiPfGKg==} + peerDependencies: + '@types/react': 0.14 || 15 || 16 || 17 || 18 || 19 + react: 0.14 || 15 || 16 || 17 || 18 || 19 + peerDependenciesMeta: + '@types/react': + optional: true + html-tags@3.3.1: resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} engines: {node: '>=8'} @@ -10104,6 +10123,9 @@ packages: uncss: optional: true + htmlparser2@10.0.0: + resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} + htmlparser2@6.1.0: resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} @@ -10362,6 +10384,9 @@ packages: inline-style-parser@0.1.1: resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==} + inline-style-parser@0.2.4: + resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} + inquirer@9.2.15: resolution: {integrity: sha512-vI2w4zl/mDluHt9YEQ/543VTCwPKWiHzKtm9dM2V0NdFcqEexDAjUHzO1oA60HRNaVifGXXM1tRRNluLVHa0Kg==} engines: {node: '>=18'} @@ -13453,6 +13478,9 @@ packages: react-property@2.0.0: resolution: {integrity: sha512-kzmNjIgU32mO4mmH5+iUyrqlpFQhF8K2k7eZ4fdLSOPFrD1XgEuSBv9LDEgxRXTMBqMd8ppT0x6TIzqE5pdGdw==} + react-property@2.0.2: + resolution: {integrity: sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==} + react-refresh@0.14.2: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} @@ -14701,9 +14729,15 @@ packages: style-to-js@1.1.1: resolution: {integrity: sha512-RJ18Z9t2B02sYhZtfWKQq5uplVctgvjTfLWT7+Eb1zjUjIrWzX5SdlkwLGQozrqarTmEzJJ/YmdNJCUNI47elg==} + style-to-js@1.1.16: + resolution: {integrity: sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==} + style-to-object@0.3.0: resolution: {integrity: sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==} + style-to-object@1.0.8: + resolution: {integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==} + stylelint-config-html@1.1.0: resolution: {integrity: sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==} engines: {node: ^12 || >=14} @@ -24927,7 +24961,7 @@ snapshots: boolbase: 1.0.0 css-what: 6.1.0 domhandler: 5.0.3 - domutils: 3.1.0 + domutils: 3.2.1 nth-check: 2.1.1 css-selector-tokenizer@0.7.3: @@ -25499,7 +25533,7 @@ snapshots: domelementtype: 2.3.0 domhandler: 4.3.1 - domutils@3.1.0: + domutils@3.2.1: dependencies: dom-serializer: 2.0.0 domelementtype: 2.3.0 @@ -25672,6 +25706,8 @@ snapshots: entities@4.5.0: {} + entities@6.0.0: {} + env-paths@2.2.1: {} envinfo@7.14.0: {} @@ -28177,6 +28213,11 @@ snapshots: domhandler: 4.3.1 htmlparser2: 7.2.0 + html-dom-parser@5.0.13: + dependencies: + domhandler: 5.0.3 + htmlparser2: 10.0.0 + html-element-map@1.3.1: dependencies: array.prototype.filter: 1.0.4 @@ -28208,6 +28249,16 @@ snapshots: react-property: 2.0.0 style-to-js: 1.1.1 + html-react-parser@5.2.2(@types/react@18.0.38)(react@18.0.0): + dependencies: + domhandler: 5.0.3 + html-dom-parser: 5.0.13 + react: 18.0.0 + react-property: 2.0.2 + style-to-js: 1.1.16 + optionalDependencies: + '@types/react': 18.0.38 + html-tags@3.3.1: {} html-void-elements@3.0.0: {} @@ -28254,6 +28305,13 @@ snapshots: transitivePeerDependencies: - typescript + htmlparser2@10.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.1 + entities: 6.0.0 + htmlparser2@6.1.0: dependencies: domelementtype: 2.3.0 @@ -28272,7 +28330,7 @@ snapshots: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 - domutils: 3.1.0 + domutils: 3.2.1 entities: 4.5.0 http-cache-semantics@4.1.1: {} @@ -28531,6 +28589,8 @@ snapshots: inline-style-parser@0.1.1: {} + inline-style-parser@0.2.4: {} + inquirer@9.2.15: dependencies: '@ljharb/through': 2.3.13 @@ -32385,6 +32445,8 @@ snapshots: react-property@2.0.0: {} + react-property@2.0.2: {} + react-refresh@0.14.2: {} react-refresh@0.9.0: {} @@ -33827,10 +33889,18 @@ snapshots: dependencies: style-to-object: 0.3.0 + style-to-js@1.1.16: + dependencies: + style-to-object: 1.0.8 + style-to-object@0.3.0: dependencies: inline-style-parser: 0.1.1 + style-to-object@1.0.8: + dependencies: + inline-style-parser: 0.2.4 + stylelint-config-html@1.1.0(postcss-html@1.7.0)(stylelint@16.5.0(typescript@5.4.5)): dependencies: postcss-html: 1.7.0