Merge pull request #687 from Klotske/add-simple-label

#668 - Add simple label
This commit is contained in:
Keith Stevens
2023-01-14 09:54:06 +09:00
committed by GitHub
4 changed files with 195 additions and 71 deletions
+36 -4
View File
@@ -44,12 +44,44 @@ describe("handles random tasks", () => {
break;
}
case "label-task": {
// Clicking on the slider will set the value to about the middle where it clicks
cy.get('[aria-roledescription="slider"]').first().click();
cy.get('[data-cy="label-group-item"]')
.first()
.invoke("attr", "data-label-type")
.then((label_type) => {
const parent = cy
.get('[data-cy="label-group-item"]')
.first();
cy.log("Label type", label_type);
cy.get('[data-cy="review"]').click();
switch (label_type) {
case "slider": {
// Clicking on the slider will set the value to about the middle where it clicks
parent
.get('[aria-roledescription="slider"]')
.first()
.click();
cy.get('[data-cy="submit"]').click();
cy.get('[data-cy="review"]').click();
cy.get('[data-cy="submit"]').click();
break;
}
case "radio": {
// Clicking on the slider will set the value to about the middle where it clicks
parent
.get('[aria-roledescription="radio-button"]')
.last()
.click();
cy.get('[data-cy="review"]').click();
cy.get('[data-cy="submit"]').click();
break;
}
}
});
break;
}
@@ -0,0 +1,84 @@
import { Box, Button, Flex, useColorMode } from "@chakra-ui/react";
import { useId, useState } from "react";
import { colors } from "src/styles/Theme/colors";
interface LabelRadioGroupProps {
labelIDs: Array<string>;
onChange: (sliderValues: number[]) => unknown;
isEditable?: boolean;
}
export const LabelRadioGroup = (props: LabelRadioGroupProps) => {
const [labelValues, setLabelValues] = useState<number[]>(Array.from({ length: props.labelIDs.length }).map(() => 0));
const [interactionFlag, setInteractionFlag] = useState(false);
return (
<Flex direction="column" justify="center">
{props.labelIDs.map((labelId, idx) => (
<LabelRadioItem
key={idx}
labelId={labelId}
labelValue={labelValues[idx]}
clickHandler={(newValue) => {
const newState = labelValues.slice();
newState[idx] = newValue;
props.onChange(newState);
setLabelValues(newState);
if (!interactionFlag) setInteractionFlag(true);
}}
states={[
{ text: "No", value: 0 },
{ text: "Yes", value: 1 },
]}
isEditable={props.isEditable}
interactionFlag={interactionFlag}
/>
))}
</Flex>
);
};
interface ButtonState {
text: string;
value: number;
colorScheme?: string;
}
interface LabelRadioItemProps {
labelId: string;
labelValue: number;
clickHandler: (newVal: number) => unknown;
states: ButtonState[];
isEditable: boolean;
interactionFlag: boolean;
}
const LabelRadioItem = (props: LabelRadioItemProps) => {
const id = useId();
const { colorMode } = useColorMode();
const labelTextClass = colorMode === "light" ? `text-${colors.light.text}` : `text-${colors.dark.text}`;
return (
<Box data-cy="label-group-item" data-label-type="radio">
<label className="text-sm" htmlFor={id}>
{/* TODO: display real text instead of just the id */}
<span className={labelTextClass}>{props.labelId}</span>
</label>
<Flex direction="row" gap={6} justify="center">
{props.states.map((item, idx) => (
<Button
aria-roledescription="radio-button"
colorScheme={item.value === props.labelValue && props.interactionFlag ? item.colorScheme || "blue" : "gray"}
isDisabled={!props.isEditable}
size="lg"
key={idx}
onClick={() => props.clickHandler(item.value)}
>
{item.text}
</Button>
))}
</Flex>
</Box>
);
};
@@ -0,0 +1,65 @@
import { Box, Grid, Slider, SliderFilledTrack, SliderThumb, SliderTrack, useColorMode } from "@chakra-ui/react";
import { useId, useState } from "react";
import { colors } from "styles/Theme/colors";
// TODO: consolidate with FlaggableElement
interface LabelSliderGroupProps {
labelIDs: Array<string>;
onChange: (sliderValues: number[]) => unknown;
isEditable?: boolean;
}
export const LabelSliderGroup = ({ labelIDs, onChange, isEditable }: LabelSliderGroupProps) => {
const [sliderValues, setSliderValues] = useState<number[]>(Array.from({ length: labelIDs.length }).map(() => 0));
return (
<Grid templateColumns="auto 1fr" rowGap={1} columnGap={3}>
{labelIDs.map((labelId, idx) => (
<CheckboxSliderItem
key={idx}
labelId={labelId}
sliderValue={sliderValues[idx]}
sliderHandler={(sliderValue) => {
const newState = sliderValues.slice();
newState[idx] = sliderValue;
onChange(newState);
setSliderValues(newState);
}}
isEditable={isEditable}
/>
))}
</Grid>
);
};
function CheckboxSliderItem(props: {
labelId: string;
sliderValue: number;
sliderHandler: (newVal: number) => unknown;
isEditable: boolean;
}) {
const id = useId();
const { colorMode } = useColorMode();
const labelTextClass = colorMode === "light" ? `text-${colors.light.text}` : `text-${colors.dark.text}`;
return (
<Box data-cy="label-group-item" data-label-type="slider">
<label className="text-sm" htmlFor={id}>
{/* TODO: display real text instead of just the id */}
<span className={labelTextClass}>{props.labelId}</span>
</label>
<Slider
aria-roledescription="slider"
defaultValue={0}
isDisabled={!props.isEditable}
onChangeEnd={(val) => props.sliderHandler(val / 100)}
>
<SliderTrack>
<SliderFilledTrack />
<SliderThumb />
</SliderTrack>
</Slider>
</Box>
);
}
+10 -67
View File
@@ -1,12 +1,13 @@
import { Box, Grid, Slider, SliderFilledTrack, SliderThumb, SliderTrack } from "@chakra-ui/react";
import { Text, useColorMode, useColorModeValue } from "@chakra-ui/react";
import { useEffect, useId, useState } from "react";
import { Box } from "@chakra-ui/react";
import { Text, useColorModeValue } from "@chakra-ui/react";
import { useEffect, useState } from "react";
import { MessageView } from "src/components/Messages";
import { MessageTable } from "src/components/Messages/MessageTable";
import { TwoColumnsWithCards } from "src/components/Survey/TwoColumnsWithCards";
import { TaskSurveyProps } from "src/components/Tasks/Task";
import { TaskType } from "src/types/Task";
import { colors } from "styles/Theme/colors";
import { LabelSliderGroup } from "src/components/Survey/LabelSliderGroup";
import { LabelRadioGroup } from "src/components/Survey/LabelRadioGroup";
export const LabelTask = ({
task,
@@ -65,70 +66,12 @@ export const LabelTask = ({
</Box>
)}
</>
<LabelSliderGroup labelIDs={task.valid_labels} isEditable={isEditable} onChange={onSliderChange} />
{valid_labels.length === 1 ? (
<LabelRadioGroup labelIDs={task.valid_labels} isEditable={isEditable} onChange={onSliderChange} />
) : (
<LabelSliderGroup labelIDs={task.valid_labels} isEditable={isEditable} onChange={onSliderChange} />
)}
</TwoColumnsWithCards>
</div>
);
};
// TODO: consolidate with FlaggableElement
interface LabelSliderGroupProps {
labelIDs: Array<string>;
onChange: (sliderValues: number[]) => unknown;
isEditable: boolean;
}
export const LabelSliderGroup = ({ labelIDs, onChange, isEditable }: LabelSliderGroupProps) => {
const [sliderValues, setSliderValues] = useState<number[]>(Array.from({ length: labelIDs.length }).map(() => 0));
return (
<Grid templateColumns="auto 1fr" rowGap={1} columnGap={3}>
{labelIDs.map((labelId, idx) => (
<CheckboxSliderItem
key={idx}
labelId={labelId}
sliderValue={sliderValues[idx]}
sliderHandler={(sliderValue) => {
const newState = sliderValues.slice();
newState[idx] = sliderValue;
onChange(sliderValues);
setSliderValues(newState);
}}
isEditable={isEditable}
/>
))}
</Grid>
);
};
function CheckboxSliderItem(props: {
labelId: string;
sliderValue: number;
sliderHandler: (newVal: number) => unknown;
isEditable: boolean;
}) {
const id = useId();
const { colorMode } = useColorMode();
const labelTextClass = colorMode === "light" ? `text-${colors.light.text}` : `text-${colors.dark.text}`;
return (
<>
<label className="text-sm" htmlFor={id}>
{/* TODO: display real text instead of just the id */}
<span className={labelTextClass}>{props.labelId}</span>
</label>
<Slider
aria-roledescription="slider"
defaultValue={0}
isDisabled={!props.isEditable}
onChangeEnd={(val) => props.sliderHandler(val / 100)}
>
<SliderTrack>
<SliderFilledTrack />
<SliderThumb />
</SliderTrack>
</Slider>
</>
);
}