From 5054e19e9367bfd0f9424b75859ce74a02bfcb36 Mon Sep 17 00:00:00 2001 From: Adrian Cowan Date: Fri, 13 Jan 2023 23:37:57 +1100 Subject: [PATCH] website: Remove next task button and load new task immediately Also disable the task inputs while the task is submitted and before the new task is ready in case the users network is slow or something. --- website/cypress/e2e/tasks/random.cy.ts | 4 -- website/src/components/CollapsableText.tsx | 17 ++++++-- .../components/Sortable/Sortable.stories.tsx | 33 ++++++++++++++++ website/src/components/Sortable/Sortable.tsx | 9 +++-- .../src/components/Sortable/SortableItem.tsx | 13 ++++--- .../src/components/Survey/TaskControls.tsx | 25 +++++------- website/src/components/Tasks/CreateTask.tsx | 6 +-- website/src/components/Tasks/EvaluateTask.tsx | 4 +- website/src/components/Tasks/LabelTask.tsx | 10 +++-- website/src/components/Tasks/Task.tsx | 39 ++++++++++++++----- website/src/hooks/tasks/useGenericTaskAPI.tsx | 1 + 11 files changed, 110 insertions(+), 51 deletions(-) create mode 100644 website/src/components/Sortable/Sortable.stories.tsx diff --git a/website/cypress/e2e/tasks/random.cy.ts b/website/cypress/e2e/tasks/random.cy.ts index d0d3b44a..337ed331 100644 --- a/website/cypress/e2e/tasks/random.cy.ts +++ b/website/cypress/e2e/tasks/random.cy.ts @@ -17,8 +17,6 @@ describe("handles random tasks", () => { cy.get('[data-cy="submit"]').click(); - cy.get('[data-cy="next-task"]').click(); - cy.get('[data-cy="task-id]"').should((taskIdElement) => { expect(taskIdElement.text()).not.to.eq(taskId); }); @@ -42,8 +40,6 @@ describe("handles random tasks", () => { cy.get('[data-cy="submit"]').click(); - cy.get('[data-cy="next-task"]').click(); - cy.get('[data-cy="task-id"]').should((taskIdElement) => { expect(taskIdElement.text()).not.to.eq(taskId); }); diff --git a/website/src/components/CollapsableText.tsx b/website/src/components/CollapsableText.tsx index 5dcab595..ca0177f9 100644 --- a/website/src/components/CollapsableText.tsx +++ b/website/src/components/CollapsableText.tsx @@ -8,17 +8,26 @@ import { ModalOverlay, useDisclosure, } from "@chakra-ui/react"; -import React from "react"; +import React, { ReactNode } from "react"; -export const CollapsableText = ({ text, maxLength = 220 }) => { +export const CollapsableText = ({ + text, + maxLength = 220, + isDisabled, +}: { + text: ReactNode; + maxLength?: number; + isDisabled?: boolean; +}) => { const { isOpen, onOpen, onClose } = useDisclosure(); + if (typeof text !== "string" || text.length <= maxLength) { - return text; + return <>{text}; } else { return ( <> {text.substring(0, maxLength - 3)} - diff --git a/website/src/components/Sortable/Sortable.stories.tsx b/website/src/components/Sortable/Sortable.stories.tsx new file mode 100644 index 00000000..93c6510b --- /dev/null +++ b/website/src/components/Sortable/Sortable.stories.tsx @@ -0,0 +1,33 @@ +import React from "react"; + +import { Sortable } from "./Sortable"; + +// eslint-disable-next-line import/no-anonymous-default-export +export default { + title: "Sortable/Sortable", + component: Sortable, +}; + +const Template = ({ items, isDisabled }) => { + return ; +}; + +export const Default = Template.bind({}); +Default.args = { + items: [ + "Who were the 8 presidents before George Washington?", + "euirdteunvglfe23908230892309832098 AAAAAAAA", + "Sorry, my cat sat on my keyboard. Can you print a cat in ASCII art?", + ], + isDisabled: false, +}; + +export const LongText = Template.bind({}); +LongText.args = { + items: [ + "Okay, here\u2019s my answer. The thing is, in a job interview you are applying to the position, right? Which means that you need to try to make yourself sound as good a fit as possible. So that means it\u2019s a mistake to wear something that people would think of as a silly or stupid outfit. It\u2019s good to wear something in line with what people expect the job to require, and ideally you want to wear something that you can walk around comfortably in. At the same time, if you wear something that looks kind of dorky, you might not stand out as much as someone who doesn\u2019t, because the other candidates probably all have something slightly weird or wacky about them too. So, I\u2019d say, do you like your outfit? Wear something that you\u2019re comfortable in, that you think you look good in, and is in line with what the job would require. Also, for interview prep, I would focus on making yourself look as professional as possible, from your hair to your outfit. What do you think of this?", + "Assistant: Yes, I think they can be helpful when the child misbehaves, but they should be used with a little bit of compassion and understanding that it\u2019s not the natural state of things to have an adult yelling at them. Time outs are also often used without letting the child know how they\u2019re getting out of the time out, which can make it feel arbitrary or like a punishment, rather than a consequence for something they did. It\u2019s really easy for adults to do this kind of thing unconsciously. It\u2019s easy to get caught up in the notion that \u201cThey\u2019re in time out, and that\u2019s the end of it!\u201d but kids can be pretty imaginative, and they can use their own creativity to make their way out of time outs. A compassionate time out ends when the child shows a sign of understanding what they\u2019ve done wrong, and are ready to begin again. That way the child knows they\u2019re learning, and that the parent is seeing them as an intelligent person, even if they sometimes mess up. You can still use the other techniques you were using to be tough when necessary, but using a compassionate approach will let you use them without actually using them!", + "Assistant: No. The USA was founded by a Puritan group of Protestants, but it didn\u2019t adopt the religion of the Puritans until much later, and it was always a secular state. The Puritans observed the Sabbath on Sunday, and the Puritans only had a small influence in the early history of the USA. It\u2019s difficult to trace the origins of closing stores on Sunday, but one early and short-lived attempt at forcing the Sabbath on people in the 1800s was motivated by the Protestant ideal that people should spend Sunday focusing on spiritual activities. By the mid-1800s, when the Sunday closing law was made, there was not a lot of pressure from that standpoint, but the church had begun to advocate for Sunday closing laws as a way of counteracting the negative effects of industrialization on the day of rest. Even after that shift, closing stores on Sunday was not always possible, since the religious Sunday was not always chosen for observance. And as industrialization accelerated and mechanization made it possible to operate stores on Sunday, the law was not enforced as much as people liked. The day of rest was also being violated by stores that stayed open all day on Sunday, so closing stores on Sundays became an effort to protect the Sabbath for all citizens.", + ], + isDisabled: false, +}; diff --git a/website/src/components/Sortable/Sortable.tsx b/website/src/components/Sortable/Sortable.tsx index 270c67e7..48c2916b 100644 --- a/website/src/components/Sortable/Sortable.tsx +++ b/website/src/components/Sortable/Sortable.tsx @@ -23,7 +23,8 @@ import { SortableItem } from "./SortableItem"; export interface SortableProps { items: ReactNode[]; - onChange: (newSortedIndices: number[]) => void; + onChange?: (newSortedIndices: number[]) => void; + isDisabled?: boolean; className?: string; } @@ -64,8 +65,8 @@ export const Sortable = (props: SortableProps) => { {itemsWithIds.map(({ id, item }) => ( - - + + ))} @@ -82,7 +83,7 @@ export const Sortable = (props: SortableProps) => { const oldIndex = items.findIndex((x) => x.id === active.id); const newIndex = items.findIndex((x) => x.id === over.id); const newArray = arrayMove(items, oldIndex, newIndex); - props.onChange(newArray.map((item) => item.originalIndex)); + props.onChange && props.onChange(newArray.map((item) => item.originalIndex)); return newArray; }); } diff --git a/website/src/components/Sortable/SortableItem.tsx b/website/src/components/Sortable/SortableItem.tsx index d626b3a9..811cef02 100644 --- a/website/src/components/Sortable/SortableItem.tsx +++ b/website/src/components/Sortable/SortableItem.tsx @@ -4,11 +4,12 @@ import { CSS } from "@dnd-kit/utilities"; import { PropsWithChildren, useState } from "react"; import { RxDragHandleDots2 } from "react-icons/rx"; -export const SortableItem = ({ children, id }: PropsWithChildren<{ id: number }>) => { +export const SortableItem = ({ children, id, isDisabled }: PropsWithChildren<{ id: number; isDisabled: boolean }>) => { const backgroundColor = useColorModeValue("gray.700", "gray.500"); + const disabledBackgroundColor = useColorModeValue("gray.400", "gray.700"); const textColor = useColorModeValue("white", "white"); - const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id }); + const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id, disabled: isDisabled }); const style = { transform: CSS.Translate.toString(transform), @@ -22,12 +23,14 @@ export const SortableItem = ({ children, id }: PropsWithChildren<{ id: number }> setGrabbing(true)} + cursor={isDisabled ? "auto" : grabbing ? "grabbing" : "grab"} + onMouseDown={() => { + setGrabbing(true); + }} onMouseUp={() => setGrabbing(false)} {...attributes} {...listeners} diff --git a/website/src/components/Survey/TaskControls.tsx b/website/src/components/Survey/TaskControls.tsx index 91d6ffda..81b2c775 100644 --- a/website/src/components/Survey/TaskControls.tsx +++ b/website/src/components/Survey/TaskControls.tsx @@ -12,7 +12,6 @@ export interface TaskControlsProps { taskStatus: TaskStatus; onSubmit: () => void; onSkip: (reason: string) => void; - onNextTask: () => void; } export const TaskControls = (props: TaskControlsProps) => { @@ -31,21 +30,15 @@ export const TaskControls = (props: TaskControlsProps) => { > - - {props.taskStatus !== "SUBMITTED" ? ( - - Submit - - ) : ( - - Next Task - - )} + + + Submit + ); diff --git a/website/src/components/Tasks/CreateTask.tsx b/website/src/components/Tasks/CreateTask.tsx index b66eebb0..7a1363a7 100644 --- a/website/src/components/Tasks/CreateTask.tsx +++ b/website/src/components/Tasks/CreateTask.tsx @@ -1,11 +1,11 @@ import { Box, Stack, Text, useColorModeValue } from "@chakra-ui/react"; import { useState } from "react"; +import { MessageTable } from "src/components/Messages/MessageTable"; import { TrackedTextarea } from "src/components/Survey/TrackedTextarea"; import { TwoColumnsWithCards } from "src/components/Survey/TwoColumnsWithCards"; import { TaskSurveyProps } from "src/components/Tasks/Task"; -import { MessageTable } from "../Messages/MessageTable"; -export const CreateTask = ({ task, taskType, onReplyChanged }: TaskSurveyProps<{ text: string }>) => { +export const CreateTask = ({ task, taskType, isDisabled, onReplyChanged }: TaskSurveyProps<{ text: string }>) => { const cardColor = useColorModeValue("gray.100", "gray.700"); const titleColor = useColorModeValue("gray.800", "gray.300"); const labelColor = useColorModeValue("gray.600", "gray.400"); @@ -45,7 +45,7 @@ export const CreateTask = ({ task, taskType, onReplyChanged }: TaskSurveyProps<{ text={inputText} onTextChange={textChangeHandler} thresholds={{ low: 20, medium: 40, goal: 50 }} - textareaProps={{ placeholder: "Write your prompt here..." }} + textareaProps={{ placeholder: "Write your prompt here...", isDisabled }} /> diff --git a/website/src/components/Tasks/EvaluateTask.tsx b/website/src/components/Tasks/EvaluateTask.tsx index a85e9abd..87ee86ea 100644 --- a/website/src/components/Tasks/EvaluateTask.tsx +++ b/website/src/components/Tasks/EvaluateTask.tsx @@ -5,7 +5,7 @@ import { Sortable } from "src/components/Sortable/Sortable"; import { SurveyCard } from "src/components/Survey/SurveyCard"; import { TaskSurveyProps } from "src/components/Tasks/Task"; -export const EvaluateTask = ({ task, onReplyChanged }: TaskSurveyProps<{ ranking: number[] }>) => { +export const EvaluateTask = ({ task, isDisabled, onReplyChanged }: TaskSurveyProps<{ ranking: number[] }>) => { const cardColor = useColorModeValue("gray.100", "gray.700"); const titleColor = useColorModeValue("gray.800", "gray.300"); const labelColor = useColorModeValue("gray.600", "gray.400"); @@ -46,7 +46,7 @@ export const EvaluateTask = ({ task, onReplyChanged }: TaskSurveyProps<{ ranking - + diff --git a/website/src/components/Tasks/LabelTask.tsx b/website/src/components/Tasks/LabelTask.tsx index 07a8c2ea..ea56ab34 100644 --- a/website/src/components/Tasks/LabelTask.tsx +++ b/website/src/components/Tasks/LabelTask.tsx @@ -12,6 +12,7 @@ export const LabelTask = ({ task, taskType, onReplyChanged, + isDisabled, }: TaskSurveyProps<{ text: string; labels: { [k: string]: number }; message_id: string }>) => { const valid_labels = task.valid_labels; const [sliderValues, setSliderValues] = useState(new Array(valid_labels.length).fill(0)); @@ -64,7 +65,7 @@ export const LabelTask = ({ )} - + ); @@ -74,9 +75,10 @@ export const LabelTask = ({ interface LabelSliderGroupProps { labelIDs: Array; onChange: (sliderValues: number[]) => unknown; + isDisabled?: boolean; } -export const LabelSliderGroup = ({ labelIDs, onChange }: LabelSliderGroupProps) => { +export const LabelSliderGroup = ({ labelIDs, onChange, isDisabled }: LabelSliderGroupProps) => { const [sliderValues, setSliderValues] = useState(Array.from({ length: labelIDs.length }).map(() => 0)); return ( @@ -92,6 +94,7 @@ export const LabelSliderGroup = ({ labelIDs, onChange }: LabelSliderGroupProps) onChange(sliderValues); setSliderValues(newState); }} + isDisabled={isDisabled} /> ))} @@ -102,6 +105,7 @@ function CheckboxSliderItem(props: { labelId: string; sliderValue: number; sliderHandler: (newVal: number) => unknown; + isDisabled: boolean; }) { const id = useId(); const { colorMode } = useColorMode(); @@ -114,7 +118,7 @@ function CheckboxSliderItem(props: { {/* TODO: display real text instead of just the id */} {props.labelId} - props.sliderHandler(val / 100)}> + props.sliderHandler(val / 100)}> diff --git a/website/src/components/Tasks/Task.tsx b/website/src/components/Tasks/Task.tsx index 1ccef66c..24a95d78 100644 --- a/website/src/components/Tasks/Task.tsx +++ b/website/src/components/Tasks/Task.tsx @@ -17,6 +17,7 @@ export interface TaskSurveyProps { // eslint-disable-next-line @typescript-eslint/no-explicit-any task: any; taskType: TaskInfo; + isDisabled?: boolean; onReplyChanged: (state: TaskReplyState) => void; } @@ -77,24 +78,42 @@ export const Task = ({ frontendId, task, trigger, mutate }) => { function taskTypeComponent() { switch (taskType.category) { case TaskCategory.Create: - return ; + return ( + + ); case TaskCategory.Evaluate: - return ; + return ( + + ); case TaskCategory.Label: - return ; + return ( + + ); } } return (
{taskTypeComponent()} - + (taskApiEndpoint: st onSuccess: async (response) => { const newTask: ConcreteTaskResponse = await response.json(); setTasks((oldTasks) => [...oldTasks, newTask]); + mutate(); }, });