Skip to content

Commit

Permalink
Thumbs up/down sparkle component in ConversationMessagesActions (#8804)
Browse files Browse the repository at this point in the history
* First commit - thumbs up/down component

* Move to messageThumb arguments

* Update sparkle/src/components/ConversationMessageActions.tsx

Co-authored-by: Jules Belveze <[email protected]>

* Update sparkle/src/components/ConversationMessageActions.tsx

Co-authored-by: Jules Belveze <[email protected]>

* Update sparkle/src/components/ConversationMessageActions.tsx

Co-authored-by: Jules Belveze <[email protected]>

* Update sparkle/src/components/ConversationMessageActions.tsx

Co-authored-by: Jules Belveze <[email protected]>

* Update sparkle/src/components/ConversationMessageActions.tsx

Co-authored-by: Jules Belveze <[email protected]>

* Update sparkle/src/components/ConversationMessageActions.tsx

Co-authored-by: Jules Belveze <[email protected]>

* Lint

* Use popover for thumbs action

* Bump sparkle

* lint

* liiiiint

* Use splitbutton equivalent

* Update sparkle/src/components/ConversationMessageActions.tsx

Co-authored-by: Jules Belveze <[email protected]>

* Update sparkle/src/components/ConversationMessageActions.tsx

Co-authored-by: Jules Belveze <[email protected]>

* Integrate feedback

* Less rows

---------

Co-authored-by: Jules Belveze <[email protected]>
  • Loading branch information
albandum and JulesBelveze authored Nov 21, 2024
1 parent ae9f7a8 commit d846b6c
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 5 deletions.
4 changes: 4 additions & 0 deletions sparkle/src/components/ConversationMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Button } from "@sparkle/components";
import {
ConversationMessageActions,
ConversationMessageEmojiSelectorProps,
ConversationMessageThumbSelectorProps,
} from "@sparkle/components/ConversationMessageActions";
import { ConversationMessageContent } from "@sparkle/components/ConversationMessageContent";
import { ConversationMessageHeader } from "@sparkle/components/ConversationMessageHeader";
Expand All @@ -29,6 +30,7 @@ type ConversationMessageProps = {
children?: React.ReactNode;
citations?: React.ReactElement[];
messageEmoji?: ConversationMessageEmojiSelectorProps;
messageThumb?: ConversationMessageThumbSelectorProps;
name: string | null;
pictureUrl?: string | React.ReactNode | null;
renderName?: (name: string | null) => React.ReactNode;
Expand All @@ -46,6 +48,7 @@ export function ConversationMessage({
children,
citations,
messageEmoji,
messageThumb,
name,
pictureUrl,
renderName = (name) => (
Expand Down Expand Up @@ -77,6 +80,7 @@ export function ConversationMessage({
<ConversationMessageActions
buttons={buttons}
messageEmoji={messageEmoji}
messageThumb={messageThumb}
/>
</div>
);
Expand Down
138 changes: 136 additions & 2 deletions sparkle/src/components/ConversationMessageActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,42 @@ import {
EmojiMartData,
EmojiPicker,
} from "@sparkle/components/EmojiPicker";
import { Popover } from "@sparkle/components/Popover";
import { ReactionIcon } from "@sparkle/icons/solid";
import { Page } from "@sparkle/components/Page";
import {
Popover,
PopoverContent,
PopoverRoot,
PopoverTrigger,
} from "@sparkle/components/Popover";
import { TextArea } from "@sparkle/components/TextArea";
import {
HandThumbDownIcon,
HandThumbUpIcon,
ReactionIcon,
} from "@sparkle/icons/solid";
import { cn } from "@sparkle/lib/utils";

type ConversationMessageActionsProps = {
buttons?: React.ReactElement<typeof Button>[];
messageEmoji?: ConversationMessageEmojiSelectorProps;
messageThumb?: ConversationMessageThumbSelectorProps;
};

export function ConversationMessageActions({
buttons = [],
messageEmoji,
messageThumb,
}: ConversationMessageActionsProps) {
if (messageThumb) {
buttons.push(
<ConversationMessageThumbsSelector
key="thumbs-selector"
onSubmitThumb={messageThumb.onSubmitThumb}
isSubmittingThumb={messageThumb.isSubmittingThumb}
/>
);
}

if (messageEmoji) {
buttons.push(
<ConversationMessageEmojiSelector
Expand Down Expand Up @@ -50,6 +73,16 @@ export interface ConversationMessageEmojiSelectorProps {
isSubmittingEmoji: boolean;
}

export type ThumbReaction = "up" | "down";
export interface ConversationMessageThumbSelectorProps {
onSubmitThumb: (p: {
thumb: string;
isToRemove: boolean;
feedback?: string | null;
}) => Promise<void>;
isSubmittingThumb: boolean;
}

function ConversationMessageEmojiSelector({
reactions,
onSubmitEmoji,
Expand Down Expand Up @@ -174,3 +207,104 @@ function EmojiSelector({
/>
);
}

function ConversationMessageThumbsSelector({
onSubmitThumb,
isSubmittingThumb,
}: ConversationMessageThumbSelectorProps) {
return (
<ThumbsSelector
isSubmittingThumb={isSubmittingThumb}
onSubmitThumb={onSubmitThumb}
/>
);
}

function ThumbsSelector({
isSubmittingThumb = false,
onSubmitThumb,
}: ConversationMessageThumbSelectorProps) {
const [selectedThumb, setSelectedThumb] =
React.useState<ThumbReaction | null>(null);
const [feedback, setFeedback] = React.useState<string | null>(null);
const [isPopoverOpen, setIsPopoverOpen] = React.useState(false);
const containerRef = useRef<HTMLDivElement>(null);

const selectThumb = async (thumb: ThumbReaction) => {
if (selectedThumb === thumb) {
setSelectedThumb(null);
setIsPopoverOpen(false);
await onSubmitThumb({ thumb, isToRemove: true });
return;
}
setSelectedThumb(thumb);
setIsPopoverOpen(true);
await onSubmitThumb({ thumb, isToRemove: false });
};

return (
<div ref={containerRef} className="s-flex s-items-center">
<PopoverRoot open={isPopoverOpen}>
<PopoverTrigger asChild>
<div className="s-flex s-items-center">
<Button
variant={selectedThumb === "up" ? "highlight" : "outline"}
size="xs"
disabled={isSubmittingThumb}
onClick={() => selectThumb("up")}
className={"s-rounded-r-none s-border-r-0"}
icon={HandThumbUpIcon}
/>
<Button
variant={selectedThumb === "down" ? "highlight" : "outline"}
size="xs"
disabled={isSubmittingThumb}
onClick={() => selectThumb("down")}
className={"s-rounded-l-none s-border-l-0"}
icon={HandThumbDownIcon}
/>
</div>
</PopoverTrigger>
<PopoverContent fullWidth={true}>
<div className="s-w-80 s-p-4">
<Page.H variant="h6">
{selectedThumb === "up"
? "🎉 Glad you liked it! Tell us more?"
: "🫠 Help make the answers better!"}
</Page.H>
<TextArea
placeholder={
selectedThumb === "up"
? "What did you like?"
: "Tell us what went wrong so we can make this assistant better."
}
className="s-mt-4"
rows={3}
value={feedback ?? ""}
onChange={(e) => setFeedback(e.target.value)}
/>
<div className="s-mt-4 s-flex s-justify-between s-gap-2">
<Button
variant="primary"
label="Submit feedback"
onClick={async () => {
await onSubmitThumb({
thumb: selectedThumb ?? "up",
isToRemove: false,
feedback: feedback,
});
setIsPopoverOpen(false);
}}
/>
<Button
variant="ghost"
label="Cancel"
onClick={() => setIsPopoverOpen(false)}
/>
</div>
</div>
</PopoverContent>
</PopoverRoot>
</div>
);
}
21 changes: 18 additions & 3 deletions sparkle/src/stories/ConversationMessageActions.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import type { Meta, StoryObj } from "@storybook/react";
import React from "react";

import { ConversationMessageEmojiSelectorProps } from "@sparkle/components/ConversationMessageActions";
import {
ConversationMessageEmojiSelectorProps,
ConversationMessageThumbSelectorProps,
} from "@sparkle/components/ConversationMessageActions";

import {
ArrowPathIcon,
Expand Down Expand Up @@ -46,6 +49,13 @@ const messageEmoji: ConversationMessageEmojiSelectorProps = {
isSubmittingEmoji: false,
};

const messageThumb: ConversationMessageThumbSelectorProps = {
onSubmitThumb: async (element) => {
console.log("Thumb clicked", element);
},
isSubmittingThumb: false,
};

const meta = {
title: "Primitives/ConversationMessageActions",
component: ConversationMessageActions,
Expand All @@ -60,6 +70,12 @@ const meta = {
type: "object",
},
},
messageThumb: {
description: "Whether to show the thumbs selector",
control: {
type: "object",
},
},
},
} satisfies Meta<React.ComponentProps<typeof ConversationMessageActions>>;

Expand All @@ -70,7 +86,6 @@ export const ExamplePicker: Story = {
args: {
buttons,
messageEmoji,
messageThumb,
},
};

<ConversationMessageActions buttons={buttons} messageEmoji={messageEmoji} />;

0 comments on commit d846b6c

Please sign in to comment.