Skip to content

Commit

Permalink
Merge pull request #97 from RasaHQ/rating-component
Browse files Browse the repository at this point in the history
Add initial implementation (tsx, css) for rating component
  • Loading branch information
rasa-jmac authored Feb 4, 2025
2 parents 984c219 + 947ff12 commit 6642ec8
Show file tree
Hide file tree
Showing 21 changed files with 455 additions and 5 deletions.
10 changes: 10 additions & 0 deletions e2e/cypress/fixtures/chatbotWidgetData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const userInputs = {
videoMessage: 'Video',
quickRepliesUserReply: 'hyperlink',
errorConnectionSocketioTest: 'Connection',
ratingMessage: 'Rating',
} as const;

type TUserInputsType = typeof userInputs;
Expand Down Expand Up @@ -139,6 +140,15 @@ const botResponses = {
hyperlink: {
text: 'For more information, visit [Google Website](https://www.google.com/).',
},
Rating: {
type: 'rating',
text: 'How would you rate this answer?',
options: [
{ value: 'positive', icon: '😊', label: 'Positive' },
{ value: 'neutral', icon: '😐', label: 'Neutral' },
{ value: 'negative', icon: '☹️', label: 'Negative' },
],
},
};

const widgetProps = {
Expand Down
13 changes: 13 additions & 0 deletions packages/react/dist/RasaRating.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/react/dist/RasaRating.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions packages/react/dist/types/RasaRating.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { RasaRating as RasaRatingElement } from "@rasahq/chat-widget-ui/dist/components/rasa-rating.js";
import type { EventName, StencilReactComponent } from '@stencil/react-output-target/runtime';
type RasaRatingEvents = {
onRatingSelected: EventName<CustomEvent<{
value: string;
}>>;
};
declare const RasaRating: StencilReactComponent<RasaRatingElement, RasaRatingEvents>;
export default RasaRating;
25 changes: 25 additions & 0 deletions packages/react/lib/components/stencil-generated/RasaRating.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use client';

/**
* This file was automatically generated by the Stencil React Output Target.
* Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
*/

/* eslint-disable */

import { RasaRating as RasaRatingElement, defineCustomElement as defineRasaRating } from "@rasahq/chat-widget-ui/dist/components/rasa-rating.js";
import type { EventName, StencilReactComponent } from '@stencil/react-output-target/runtime';
import { createComponent } from '@stencil/react-output-target/runtime';
import React from 'react';

type RasaRatingEvents = { onRatingSelected: EventName<CustomEvent<{ value: string }>> };

const RasaRating: StencilReactComponent<RasaRatingElement, RasaRatingEvents> = /*@__PURE__*/ createComponent<RasaRatingElement, RasaRatingEvents>({
tagName: 'rasa-rating',
elementClass: RasaRatingElement,
react: React,
events: { onRatingSelected: 'ratingSelected' } as RasaRatingEvents,
defineCustomElement: defineRasaRating
});

export default RasaRating;
6 changes: 4 additions & 2 deletions packages/sdk/src/connection-strategy/HTTPConnection.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
HttpResponse,
HttpTextResponse,
HttpVideoResponse,
HttpRatingResponse,
} from '../types/server-response.types';

import { RESPONSE_MESSAGE_TYPES } from '../constants';
Expand Down Expand Up @@ -40,14 +41,15 @@ export function isHttpQuickReplyResponse(response: HttpResponse): response is Ht

export function hasCustomAttribute(
response: HttpResponse,
): response is HttpCarouselResponse | HttpVideoResponse | HttpAccordionResponse | HttpFileDownloadResponse {
): response is HttpCarouselResponse | HttpVideoResponse | HttpAccordionResponse | HttpFileDownloadResponse | HttpRatingResponse {
return (
'custom' in response &&
response.custom !== undefined &&
(response.custom.type === RESPONSE_MESSAGE_TYPES.CAROUSEL ||
response.custom.type === RESPONSE_MESSAGE_TYPES.VIDEO ||
response.custom.type === RESPONSE_MESSAGE_TYPES.ACCORDION ||
response.custom.type === RESPONSE_MESSAGE_TYPES.FILE_DOWNLOAD)
response.custom.type === RESPONSE_MESSAGE_TYPES.FILE_DOWNLOAD ||
response.custom.type === RESPONSE_MESSAGE_TYPES.RATING)
);
}

Expand Down
1 change: 1 addition & 0 deletions packages/sdk/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const RESPONSE_MESSAGE_TYPES = {
FILE_DOWNLOAD: "file_download",
VIDEO: "video",
IMAGE: "image",
RATING: "rating",
} as const;

export const SESSION_STORAGE_KEYS = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export const MESSAGE_TYPES = {
TEXT: "text",
QUICK_REPLY: "quickReply",
SESSION_DIVIDER: "sessionDivider",
RATING: "rating",
} as const;
10 changes: 9 additions & 1 deletion packages/sdk/src/message-parser/types/parsed-message.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ export interface AccordionMessage extends BaseMessage {
elements: { title: string; text: string; link?: string }[];
}

export interface RatingMessage extends BaseMessage {
type: typeof MESSAGE_TYPES.RATING;
text: string;
options: { value: string; icon: string; label: string }[];
}


export type Message =
| AccordionMessage
| CarouselMessage
Expand All @@ -71,4 +78,5 @@ export type Message =
| SessionDivider
| QuickReplyMessage
| TextMessage
| VideoMessage;
| VideoMessage
| RatingMessage;
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
QuickReplyResponse,
TextResponse,
VideoResponse,
RatingResponse,
} from '../../types/server-response.types';
import { CustomErrorClass, ErrorSeverity } from '../../errors';

Expand All @@ -21,6 +22,7 @@ const messageTypeMap = {
fileDownload: (msg: any): msg is FileDownloadResponse => msg?.type === RESPONSE_MESSAGE_TYPES.FILE_DOWNLOAD,
video: (msg: any): msg is VideoResponse => msg?.type === RESPONSE_MESSAGE_TYPES.VIDEO,
text: (msg: any): msg is TextResponse => msg.text && msg.type === undefined,
rating: (msg: any): msg is RatingResponse => msg?.type === RESPONSE_MESSAGE_TYPES.RATING,
};

export const determineMessageType = (message: unknown): keyof MessageParsersReturnTypes => {
Expand Down
26 changes: 26 additions & 0 deletions packages/sdk/src/message-parser/utils/message-parsers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
QuickReplyMessage,
TextMessage,
VideoMessage,
RatingMessage,
} from '../types';
import {
AccordionResponse,
Expand All @@ -15,6 +16,7 @@ import {
QuickReplyResponse,
TextResponse,
VideoResponse,
RatingResponse,
} from '../../types/server-response.types';

import { MESSAGE_TYPES } from '../constants';
Expand Down Expand Up @@ -177,4 +179,28 @@ describe('MessageParsers', () => {

expect(MessageParsers.video(videoResponse, sender)).toEqual(expected);
});

it('rating message correctly parsed', () => {
const ratingResponse: RatingResponse = {
type: RESPONSE_MESSAGE_TYPES.RATING,
text: 'How would you rate this?',
options: [
{ value: 'positive', icon: '😊', label: 'Positive' },
{ value: 'neutral', icon: '😐', label: 'Neutral' },
{ value: 'negative', icon: '☹️', label: 'Negative' },
],
};
const expected: RatingMessage = {
sender,
type: MESSAGE_TYPES.RATING,
text: 'How would you rate this?',
options: [
{ value: 'positive', icon: '😊', label: 'Positive' },
{ value: 'neutral', icon: '😐', label: 'Neutral' },
{ value: 'negative', icon: '☹️', label: 'Negative' },
],
};

expect(MessageParsers.rating(ratingResponse, sender)).toEqual(expected);
});
});
9 changes: 9 additions & 0 deletions packages/sdk/src/message-parser/utils/message-parsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
QuickReplyMessage,
TextMessage,
VideoMessage,
RatingMessage,
} from '../types/parsed-message.types';
import {
AccordionResponse,
Expand All @@ -15,6 +16,7 @@ import {
QuickReplyResponse,
TextResponse,
VideoResponse,
RatingResponse,
} from '../../types/server-response.types';

import { MESSAGE_TYPES } from '../constants/message.constants';
Expand Down Expand Up @@ -82,6 +84,13 @@ export const MessageParsers = {
src: video_url,
timestamp,
}),
rating: (message: RatingResponse, sender: SenderType): RatingMessage => ({
sender,
type: MESSAGE_TYPES.RATING,
text: message.text,
options: message.options,
timestamp: message.timestamp,
}),
};

export type MessageParsersType = typeof MessageParsers;
Expand Down
17 changes: 15 additions & 2 deletions packages/sdk/src/types/server-response.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ export interface CarouselResponse extends BaseMessageResponse {
elements: { image_url: string; text: string; link?: string }[];
}

export interface RatingResponse extends BaseMessageResponse {
type: typeof RESPONSE_MESSAGE_TYPES.RATING;
text: string;
options: { value: string; icon: string; label: string }[];
}

export interface QuickReplyResponse extends BaseMessageResponse {
text?: string;
quick_replies: { content_type: string; payload: string; title: string; isSelected?: boolean }[];
Expand Down Expand Up @@ -82,14 +88,20 @@ export interface HttpQuickReplyResponse extends BaseMessageResponse {
buttons: { payload: string; title: string }[];
}

export interface HttpRatingResponse extends BaseMessageResponse {
recipient_id: string;
custom: RatingResponse;
}

export type HttpResponse =
| HttpTextResponse
| HttpImageResponse
| HttpCarouselResponse
| HttpVideoResponse
| HttpAccordionResponse
| HttpFileDownloadResponse
| HttpQuickReplyResponse;
| HttpQuickReplyResponse
| HttpRatingResponse;

export type MessageResponse =
| TextResponse
Expand All @@ -98,4 +110,5 @@ export type MessageResponse =
| CarouselResponse
| QuickReplyResponse
| FileDownloadResponse
| VideoResponse;
| VideoResponse
| RatingResponse;
48 changes: 48 additions & 0 deletions packages/ui/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,16 @@ export namespace Components {
*/
"quickReplyId": string;
}
interface RasaRating {
/**
* List of rating options
*/
"options": string | { value: string; icon: string; label: string }[];
/**
* Instructional text for the rating component
*/
"text": string;
}
interface RasaSessionDivider {
/**
* Session start datetime
Expand Down Expand Up @@ -509,6 +519,10 @@ export interface RasaQuickReplyCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLRasaQuickReplyElement;
}
export interface RasaRatingCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLRasaRatingElement;
}
export interface RasaTextCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLRasaTextElement;
Expand Down Expand Up @@ -722,6 +736,23 @@ declare global {
prototype: HTMLRasaQuickReplyElement;
new (): HTMLRasaQuickReplyElement;
};
interface HTMLRasaRatingElementEventMap {
"ratingSelected": { value: string };
}
interface HTMLRasaRatingElement extends Components.RasaRating, HTMLStencilElement {
addEventListener<K extends keyof HTMLRasaRatingElementEventMap>(type: K, listener: (this: HTMLRasaRatingElement, ev: RasaRatingCustomEvent<HTMLRasaRatingElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof HTMLRasaRatingElementEventMap>(type: K, listener: (this: HTMLRasaRatingElement, ev: RasaRatingCustomEvent<HTMLRasaRatingElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
}
var HTMLRasaRatingElement: {
prototype: HTMLRasaRatingElement;
new (): HTMLRasaRatingElement;
};
interface HTMLRasaSessionDividerElement extends Components.RasaSessionDivider, HTMLStencilElement {
}
var HTMLRasaSessionDividerElement: {
Expand Down Expand Up @@ -787,6 +818,7 @@ declare global {
"rasa-image": HTMLRasaImageElement;
"rasa-image-message": HTMLRasaImageMessageElement;
"rasa-quick-reply": HTMLRasaQuickReplyElement;
"rasa-rating": HTMLRasaRatingElement;
"rasa-session-divider": HTMLRasaSessionDividerElement;
"rasa-text": HTMLRasaTextElement;
"rasa-text-message": HTMLRasaTextMessageElement;
Expand Down Expand Up @@ -1262,6 +1294,20 @@ declare namespace LocalJSX {
*/
"quickReplyId"?: string;
}
interface RasaRating {
/**
* Event emitted when a rating is selected
*/
"onRatingSelected"?: (event: RasaRatingCustomEvent<{ value: string }>) => void;
/**
* List of rating options
*/
"options"?: string | { value: string; icon: string; label: string }[];
/**
* Instructional text for the rating component
*/
"text"?: string;
}
interface RasaSessionDivider {
/**
* Session start datetime
Expand Down Expand Up @@ -1355,6 +1401,7 @@ declare namespace LocalJSX {
"rasa-image": RasaImage;
"rasa-image-message": RasaImageMessage;
"rasa-quick-reply": RasaQuickReply;
"rasa-rating": RasaRating;
"rasa-session-divider": RasaSessionDivider;
"rasa-text": RasaText;
"rasa-text-message": RasaTextMessage;
Expand Down Expand Up @@ -1388,6 +1435,7 @@ declare module "@stencil/core" {
"rasa-image": LocalJSX.RasaImage & JSXBase.HTMLAttributes<HTMLRasaImageElement>;
"rasa-image-message": LocalJSX.RasaImageMessage & JSXBase.HTMLAttributes<HTMLRasaImageMessageElement>;
"rasa-quick-reply": LocalJSX.RasaQuickReply & JSXBase.HTMLAttributes<HTMLRasaQuickReplyElement>;
"rasa-rating": LocalJSX.RasaRating & JSXBase.HTMLAttributes<HTMLRasaRatingElement>;
"rasa-session-divider": LocalJSX.RasaSessionDivider & JSXBase.HTMLAttributes<HTMLRasaSessionDividerElement>;
"rasa-text": LocalJSX.RasaText & JSXBase.HTMLAttributes<HTMLRasaTextElement>;
"rasa-text-message": LocalJSX.RasaTextMessage & JSXBase.HTMLAttributes<HTMLRasaTextMessageElement>;
Expand Down
Loading

0 comments on commit 6642ec8

Please sign in to comment.