From 4be3167fa811fb75230cd5e827d56141f0bcc93a Mon Sep 17 00:00:00 2001 From: Danielle Whyte Date: Mon, 28 Oct 2024 16:25:14 -0500 Subject: [PATCH 01/21] update widget with label fields --- .../perseus/src/widgets/dropdown/dropdown.tsx | 61 +++++++++++++------ 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/packages/perseus/src/widgets/dropdown/dropdown.tsx b/packages/perseus/src/widgets/dropdown/dropdown.tsx index ddf802db1b..2d9525e3f4 100644 --- a/packages/perseus/src/widgets/dropdown/dropdown.tsx +++ b/packages/perseus/src/widgets/dropdown/dropdown.tsx @@ -1,4 +1,6 @@ +import {IDProvider, View} from "@khanacademy/wonder-blocks-core"; import {SingleSelect, OptionItem} from "@khanacademy/wonder-blocks-dropdown"; +import {LabelSmall} from "@khanacademy/wonder-blocks-typography"; import * as React from "react"; import ReactDOM from "react-dom"; @@ -24,6 +26,7 @@ type DefaultProps = { selected: Props["selected"]; placeholder: Props["placeholder"]; apiOptions: Props["apiOptions"]; + ariaLabel: Props["ariaLabel"]; }; class Dropdown extends React.Component implements Widget { @@ -32,6 +35,7 @@ class Dropdown extends React.Component implements Widget { selected: 0, placeholder: "", apiOptions: ApiOptions.defaults, + ariaLabel: "Choose an answer", }; focus: () => boolean = () => { @@ -80,31 +84,46 @@ class Dropdown extends React.Component implements Widget { ]; return ( -
{ - e.stopPropagation(); - }} - onTouchStart={(e) => { - e.stopPropagation(); - }} - > - this._handleChange(parseInt(value))} - selectedValue={String(this.props.selected)} - disabled={this.props.apiOptions.readOnly} - > - {children} - -
+ + {(uniqueId) => ( + { + e.stopPropagation(); + }} + onTouchStart={(e) => { + e.stopPropagation(); + }} + > + {this.props.visibleLabel && ( + + Dropdown + + )} + + this._handleChange(parseInt(value)) + } + selectedValue={String(this.props.selected)} + disabled={this.props.apiOptions.readOnly} + aria-label={this.props.ariaLabel} + > + {children} + + + )} + ); } } type RenderProps = { - placeholder: string; + placeholder: PerseusDropdownWidgetOptions["placeholder"]; + visibleLabel: PerseusDropdownWidgetOptions["visibleLabel"]; + ariaLabel: PerseusDropdownWidgetOptions["ariaLabel"]; choices: ReadonlyArray; }; @@ -113,6 +132,8 @@ const optionsTransform: (arg1: PerseusDropdownWidgetOptions) => RenderProps = ( ) => { return { placeholder: widgetOptions.placeholder, + visibleLabel: widgetOptions.visibleLabel, + ariaLabel: widgetOptions.ariaLabel, choices: widgetOptions.choices.map((choice) => choice.content), }; }; From feca2ac77ac4315317031bf3056b22ce6096ffb9 Mon Sep 17 00:00:00 2001 From: Danielle Whyte Date: Mon, 28 Oct 2024 16:28:21 -0500 Subject: [PATCH 02/21] update tests and types --- packages/perseus/src/__testdata__/renderer.testdata.ts | 1 + packages/perseus/src/__tests__/extract-perseus-data.test.ts | 1 + packages/perseus/src/perseus-types.ts | 4 ++++ packages/perseus/src/renderer-util.test.ts | 1 + packages/perseus/src/widgets/dropdown/dropdown.testdata.ts | 1 + 5 files changed, 8 insertions(+) diff --git a/packages/perseus/src/__testdata__/renderer.testdata.ts b/packages/perseus/src/__testdata__/renderer.testdata.ts index 66dde7d706..c5b33beb55 100644 --- a/packages/perseus/src/__testdata__/renderer.testdata.ts +++ b/packages/perseus/src/__testdata__/renderer.testdata.ts @@ -13,6 +13,7 @@ export const dropdownWidget: DropdownWidget = { graded: true, options: { static: false, + ariaLabel: "", placeholder: "greater/less than or equal to", choices: [ { diff --git a/packages/perseus/src/__tests__/extract-perseus-data.test.ts b/packages/perseus/src/__tests__/extract-perseus-data.test.ts index 9788dbb8b2..aaf3ddd51f 100644 --- a/packages/perseus/src/__tests__/extract-perseus-data.test.ts +++ b/packages/perseus/src/__tests__/extract-perseus-data.test.ts @@ -292,6 +292,7 @@ describe("ExtractPerseusData", () => { type: "dropdown", options: { placeholder: "Select an option", + ariaLabel: "", static: false, choices: [ { diff --git a/packages/perseus/src/perseus-types.ts b/packages/perseus/src/perseus-types.ts index 6dce437320..779c53ddfe 100644 --- a/packages/perseus/src/perseus-types.ts +++ b/packages/perseus/src/perseus-types.ts @@ -365,6 +365,10 @@ export type PerseusDropdownWidgetOptions = { placeholder: string; // Always false. Not used for this widget static: boolean; + // Translatable Text; visible label for the dropdown + visibleLabel?: string; + // Translatable Text; aria label that screen readers will read + ariaLabel: string; }; export type PerseusDropdownChoice = { diff --git a/packages/perseus/src/renderer-util.test.ts b/packages/perseus/src/renderer-util.test.ts index a9e80c78b7..00c4ced145 100644 --- a/packages/perseus/src/renderer-util.test.ts +++ b/packages/perseus/src/renderer-util.test.ts @@ -8,6 +8,7 @@ import type {UserInputMap} from "./validation.types"; const testDropdownWidget: DropdownWidget = { type: "dropdown", options: { + ariaLabel: "", choices: [ { content: "Test choice 1", diff --git a/packages/perseus/src/widgets/dropdown/dropdown.testdata.ts b/packages/perseus/src/widgets/dropdown/dropdown.testdata.ts index 25ece74de1..39826d52d5 100644 --- a/packages/perseus/src/widgets/dropdown/dropdown.testdata.ts +++ b/packages/perseus/src/widgets/dropdown/dropdown.testdata.ts @@ -13,6 +13,7 @@ export const question1: PerseusRenderer = { options: { static: false, placeholder: "greater/less than or equal to", + ariaLabel: "Choose an answer", choices: [ { content: "greater than or equal to", From 79139e120afbb1d0ca81a05dbfca674261c4aaee Mon Sep 17 00:00:00 2001 From: Danielle Whyte Date: Tue, 12 Nov 2024 15:15:08 -0600 Subject: [PATCH 03/21] add fields to widget editor --- .../src/widgets/dropdown-editor.tsx | 56 ++++++++++++++++--- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/packages/perseus-editor/src/widgets/dropdown-editor.tsx b/packages/perseus-editor/src/widgets/dropdown-editor.tsx index ac47c87c1f..a092a8bbbe 100644 --- a/packages/perseus-editor/src/widgets/dropdown-editor.tsx +++ b/packages/perseus-editor/src/widgets/dropdown-editor.tsx @@ -1,5 +1,7 @@ /* eslint-disable @khanacademy/ts-no-error-suppressions */ import {components, EditorJsonify, iconTrash} from "@khanacademy/perseus"; +import {View} from "@khanacademy/wonder-blocks-core"; +import {LabeledTextField} from "@khanacademy/wonder-blocks-form"; import PropTypes from "prop-types"; import * as React from "react"; import ReactDOM from "react-dom"; @@ -34,11 +36,16 @@ class DropdownEditor extends React.Component { ], }; - onPlaceholderChange: (arg1: React.ChangeEvent) => void = ( - e, - ) => { - const placeholder = e.target.value; - this.props.onChange({placeholder: placeholder}); + onVisibleLabelChange: (arg1: string) => void = (visibleLabel) => { + this.props.onChange({visibleLabel}); + }; + + onAriaLabelChange: (arg1: string) => void = (ariaLabel) => { + this.props.onChange({ariaLabel}); + }; + + onPlaceholderChange: (arg1: string) => void = (placeholder) => { + this.props.onChange({placeholder}); }; onCorrectChange: ( @@ -102,7 +109,7 @@ class DropdownEditor extends React.Component { render(): React.ReactNode { const dropdownGroupName = _.uniqueId("perseus_dropdown_"); return ( -
+
Dropdown @@ -115,9 +122,40 @@ class DropdownEditor extends React.Component {

+
+ + +

Optional visible label

+
+
+
+ + +

+ Label text that's read by screen readers. Highly + recommend adding a label here to ensure your + exercise is accessible. For more information on + writing accessible labels, please see{" "} + + this article. + +

+
+
- { Add a choice{" "}
-
+ ); } } From 86a6ed860a8fe4318e162b8ffd80b2d0be2ba07a Mon Sep 17 00:00:00 2001 From: Danielle Whyte Date: Tue, 12 Nov 2024 15:16:25 -0600 Subject: [PATCH 04/21] update parser --- .../util/parse-perseus-json/perseus-parsers/dropdown-widget.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/perseus/src/util/parse-perseus-json/perseus-parsers/dropdown-widget.ts b/packages/perseus/src/util/parse-perseus-json/perseus-parsers/dropdown-widget.ts index d65ba1c4c7..654aa21ac0 100644 --- a/packages/perseus/src/util/parse-perseus-json/perseus-parsers/dropdown-widget.ts +++ b/packages/perseus/src/util/parse-perseus-json/perseus-parsers/dropdown-widget.ts @@ -4,6 +4,7 @@ import { constant, object, string, + optional, } from "../general-purpose-parsers"; import {parseWidget} from "./widget"; @@ -15,6 +16,8 @@ export const parseDropdownWidget: Parser = parseWidget( constant("dropdown"), object({ placeholder: string, + ariaLabel: string, + visibleLabel: optional(string), static: boolean, choices: array( object({ From 74b237807c3407417003c1006a1173fc67a5c1c7 Mon Sep 17 00:00:00 2001 From: Danielle Whyte Date: Tue, 12 Nov 2024 15:17:39 -0600 Subject: [PATCH 05/21] remove default prop --- packages/perseus/src/widgets/dropdown/dropdown.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/perseus/src/widgets/dropdown/dropdown.tsx b/packages/perseus/src/widgets/dropdown/dropdown.tsx index 2d9525e3f4..7b8fe72302 100644 --- a/packages/perseus/src/widgets/dropdown/dropdown.tsx +++ b/packages/perseus/src/widgets/dropdown/dropdown.tsx @@ -26,7 +26,6 @@ type DefaultProps = { selected: Props["selected"]; placeholder: Props["placeholder"]; apiOptions: Props["apiOptions"]; - ariaLabel: Props["ariaLabel"]; }; class Dropdown extends React.Component implements Widget { @@ -35,7 +34,6 @@ class Dropdown extends React.Component implements Widget { selected: 0, placeholder: "", apiOptions: ApiOptions.defaults, - ariaLabel: "Choose an answer", }; focus: () => boolean = () => { From 1b8e25f819c4bf7f005f937bc3db787195d481ff Mon Sep 17 00:00:00 2001 From: Danielle Whyte Date: Tue, 12 Nov 2024 15:19:25 -0600 Subject: [PATCH 06/21] update tests --- .../src/__testdata__/renderer.testdata.ts | 3 +- .../__snapshots__/renderer.test.tsx.snap | 43 +++++++++++++++---- .../perseus-parsers/widgets-map.test.ts | 2 + .../widget-ai-utils/dropdown/dropdown.test.ts | 2 + .../__snapshots__/dropdown.test.ts.snap | 28 +++++++++--- .../src/widgets/dropdown/dropdown.testdata.ts | 3 +- 6 files changed, 66 insertions(+), 15 deletions(-) diff --git a/packages/perseus/src/__testdata__/renderer.testdata.ts b/packages/perseus/src/__testdata__/renderer.testdata.ts index c5b33beb55..9160ef2a84 100644 --- a/packages/perseus/src/__testdata__/renderer.testdata.ts +++ b/packages/perseus/src/__testdata__/renderer.testdata.ts @@ -13,7 +13,8 @@ export const dropdownWidget: DropdownWidget = { graded: true, options: { static: false, - ariaLabel: "", + ariaLabel: "Test ARIA label", + visibleLabel: "Test visible label", placeholder: "greater/less than or equal to", choices: [ { diff --git a/packages/perseus/src/__tests__/__snapshots__/renderer.test.tsx.snap b/packages/perseus/src/__tests__/__snapshots__/renderer.test.tsx.snap index 444ee98190..cf7968a9ab 100644 --- a/packages/perseus/src/__tests__/__snapshots__/renderer.test.tsx.snap +++ b/packages/perseus/src/__tests__/__snapshots__/renderer.test.tsx.snap @@ -345,7 +345,15 @@ exports[`renderer snapshots correct answer: correct answer 1`] = `
-
+
+
@@ -357,12 +365,13 @@ exports[`renderer snapshots correct answer: correct answer 1`] = ` data-testid="dropdown-live-region" />