From 5cbbeebdf368a06c318940f477af08fd73a84e66 Mon Sep 17 00:00:00 2001 From: anakaren-rojas Date: Fri, 18 Oct 2024 09:41:33 -0700 Subject: [PATCH] feat(LEMS-2526): update aria label to include appearance descriptions (#1745) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary: Updates the aria description to include visual traits New aria label structure will be: `Element Type` `Visible Label(s)` `Math Details` `Visual Traits` Note: - Other updates to aria descriptions will be submitted in separate PRs Issue: LEMS-2526 ## Test plan: 1. Navigate to[ storybook ](https://650db21c3f5d1b2f13c02952-kiuwtyujyg.chromatic.com/?path=/docs/perseuseditor-editorpage--docs) 2. Add interactive graph widget using the add widget dropdown 3. Scroll down to add locked figure - select any from the dropdown 4. Click auto generate button to see basic auto generated aria label (AL) 5. Update color / line style / fill 6. Re-generate AL by clicking auto generate button to see updated label text ### Expected Behavior `Visual Traits` of each of the locked figures should change when an input (color, line style, fill) is updated and the generate aria label button is pressed ## Screen recording https://github.com/user-attachments/assets/9fb228d9-a86e-4c3b-b288-398ced6fb6f2 Author: anakaren-rojas Reviewers: benchristel, catandthemachines Required Reviewers: Approved By: benchristel, catandthemachines Checks: ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ Cypress (ubuntu-latest, 20.x), ✅ Lint, Typecheck, Format, and Test (ubuntu-latest, 20.x), ✅ Check builds for changes in size (ubuntu-latest, 20.x), ✅ Publish Storybook to Chromatic (ubuntu-latest, 20.x), ✅ gerald Pull Request URL: https://github.com/Khan/perseus/pull/1745 --- .changeset/tender-badgers-brush.md | 6 + .../locked-ellipse-settings.test.tsx | 16 +- .../locked-ellipse-settings.tsx | 13 +- .../locked-function-settings.test.tsx | 17 +- .../locked-function-settings.tsx | 11 +- .../locked-line-settings.test.tsx | 11 +- .../locked-figures/locked-line-settings.tsx | 13 +- .../locked-point-settings.test.tsx | 6 +- .../locked-figures/locked-point-settings.tsx | 10 +- .../locked-polygon-settings.test.tsx | 6 +- .../locked-polygon-settings.tsx | 11 +- .../locked-vector-settings.test.tsx | 9 +- .../locked-figures/locked-vector-settings.tsx | 12 +- .../locked-figures/util.test.ts | 160 +++++++++++++++++- .../locked-figures/util.ts | 25 +++ packages/perseus/src/index.ts | 1 + 16 files changed, 290 insertions(+), 37 deletions(-) create mode 100644 .changeset/tender-badgers-brush.md diff --git a/.changeset/tender-badgers-brush.md b/.changeset/tender-badgers-brush.md new file mode 100644 index 0000000000..badabb2af9 --- /dev/null +++ b/.changeset/tender-badgers-brush.md @@ -0,0 +1,6 @@ +--- +"@khanacademy/perseus": minor +"@khanacademy/perseus-editor": minor +--- + +adds appearance description to aria label diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-ellipse-settings.test.tsx b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-ellipse-settings.test.tsx index 986ab37520..624db50a5f 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-ellipse-settings.test.tsx +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-ellipse-settings.test.tsx @@ -382,7 +382,8 @@ describe("LockedEllipseSettings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Circle with radius 2, centered at (0, 0)", + ariaLabel: + "Circle with radius 2, centered at (0, 0). Appearance solid gray border, with no fill.", }); }); @@ -409,7 +410,8 @@ describe("LockedEllipseSettings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Circle with radius 2, centered at (0, 0)", + ariaLabel: + "Circle with radius 2, centered at (0, 0). Appearance solid gray border, with no fill.", }); }); @@ -436,7 +438,7 @@ describe("LockedEllipseSettings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ ariaLabel: - "Ellipse with x radius 2 and y radius 3, centered at (0, 0)", + "Ellipse with x radius 2 and y radius 3, centered at (0, 0). Appearance solid gray border, with no fill.", }); }); @@ -464,7 +466,7 @@ describe("LockedEllipseSettings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ ariaLabel: - "Ellipse with x radius 2 and y radius 3, centered at (0, 0), rotated by 90 degrees", + "Ellipse with x radius 2 and y radius 3, centered at (0, 0), rotated by 90 degrees. Appearance solid gray border, with no fill.", }); }); @@ -495,7 +497,8 @@ describe("LockedEllipseSettings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Circle A with radius 2, centered at (0, 0)", + ariaLabel: + "Circle A with radius 2, centered at (0, 0). Appearance solid gray border, with no fill.", }); }); @@ -530,7 +533,8 @@ describe("LockedEllipseSettings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Circle A B with radius 2, centered at (0, 0)", + ariaLabel: + "Circle A, B with radius 2, centered at (0, 0). Appearance solid gray border, with no fill.", }); }); }); diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-ellipse-settings.tsx b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-ellipse-settings.tsx index 3bb6601a8e..e3c21686da 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-ellipse-settings.tsx +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-ellipse-settings.tsx @@ -20,7 +20,10 @@ import LineStrokeSelect from "./line-stroke-select"; import LockedFigureAria from "./locked-figure-aria"; import LockedFigureSettingsActions from "./locked-figure-settings-actions"; import LockedLabelSettings from "./locked-label-settings"; -import {getDefaultFigureForType} from "./util"; +import { + generateLockedFigureAppearanceDescription, + getDefaultFigureForType, +} from "./util"; import type {LockedFigureSettingsCommonProps} from "./locked-figure-settings"; import type { @@ -62,7 +65,7 @@ const LockedEllipseSettings = (props: Props) => { function getPrepopulatedAriaLabel() { let visiblelabel = ""; if (labels && labels.length > 0) { - visiblelabel += ` ${labels.map((l) => l.text).join(" ")}`; + visiblelabel += ` ${labels.map((l) => l.text).join(", ")}`; } const isCircle = radius[0] === radius[1]; @@ -80,6 +83,12 @@ const LockedEllipseSettings = (props: Props) => { str += `, rotated by ${radianToDegree(angle)} degrees`; } + const ellipseAppearance = generateLockedFigureAppearanceDescription( + color, + strokeStyle, + fillStyle, + ); + str += ellipseAppearance; return str; } diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-function-settings.test.tsx b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-function-settings.test.tsx index 2e547de16e..1574951b80 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-function-settings.test.tsx +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-function-settings.test.tsx @@ -678,7 +678,8 @@ describe("Locked Function Settings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Function with equation y=x^2", + ariaLabel: + "Function with equation y=x^2. Appearance solid gray.", }); }); @@ -704,7 +705,8 @@ describe("Locked Function Settings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Function with equation x=y^2", + ariaLabel: + "Function with equation x=y^2. Appearance solid gray.", }); }); @@ -730,7 +732,7 @@ describe("Locked Function Settings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ ariaLabel: - "Function with equation y=x^2, domain from 1 to 2", + "Function with equation y=x^2, domain from 1 to 2. Appearance solid gray.", }); }); @@ -755,7 +757,8 @@ describe("Locked Function Settings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Function with equation y=x^2", + ariaLabel: + "Function with equation y=x^2. Appearance solid gray.", }); }); @@ -785,7 +788,8 @@ describe("Locked Function Settings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Function A with equation y=x^2", + ariaLabel: + "Function A with equation y=x^2. Appearance solid gray.", }); }); @@ -819,7 +823,8 @@ describe("Locked Function Settings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Function A, B with equation y=x^2", + ariaLabel: + "Function A, B with equation y=x^2. Appearance solid gray.", }); }); }); diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-function-settings.tsx b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-function-settings.tsx index 6d04a27695..e21149ab82 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-function-settings.tsx +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-function-settings.tsx @@ -28,7 +28,10 @@ import LockedFigureAria from "./locked-figure-aria"; import LockedFigureSettingsActions from "./locked-figure-settings-actions"; import examples from "./locked-function-examples"; import LockedLabelSettings from "./locked-label-settings"; -import {getDefaultFigureForType} from "./util"; +import { + generateLockedFigureAppearanceDescription, + getDefaultFigureForType, +} from "./util"; import type {LockedFigureSettingsCommonProps} from "./locked-figure-settings"; import type { @@ -95,6 +98,12 @@ const LockedFunctionSettings = (props: Props) => { str += `, domain from ${domain[0]} to ${domain[1]}`; } + const functionAppearance = generateLockedFigureAppearanceDescription( + lineColor, + strokeStyle, + ); + str += functionAppearance; + return str; } diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-line-settings.test.tsx b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-line-settings.test.tsx index 39e8c19658..ec3f205167 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-line-settings.test.tsx +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-line-settings.test.tsx @@ -614,7 +614,8 @@ describe("LockedLineSettings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Segment from (0, 0) to (2, 2)", + ariaLabel: + "Segment from (0, 0) to (2, 2). Appearance solid gray.", }); }); @@ -639,7 +640,7 @@ describe("LockedLineSettings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Line from (0, 0) to (2, 2)", + ariaLabel: "Line from (0, 0) to (2, 2). Appearance solid gray.", }); }); @@ -669,7 +670,8 @@ describe("LockedLineSettings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Line A from (0, 0) to (2, 2)", + ariaLabel: + "Line A from (0, 0) to (2, 2). Appearance solid gray.", }); }); @@ -703,7 +705,8 @@ describe("LockedLineSettings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Line A, B from (0, 0) to (2, 2)", + ariaLabel: + "Line A, B from (0, 0) to (2, 2). Appearance solid gray.", }); }); }); diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-line-settings.tsx b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-line-settings.tsx index c1f3a5400d..92aa892a86 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-line-settings.tsx +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-line-settings.tsx @@ -25,7 +25,10 @@ import LockedFigureAria from "./locked-figure-aria"; import LockedFigureSettingsActions from "./locked-figure-settings-actions"; import LockedLabelSettings from "./locked-label-settings"; import LockedPointSettings from "./locked-point-settings"; -import {getDefaultFigureForType} from "./util"; +import { + generateLockedFigureAppearanceDescription, + getDefaultFigureForType, +} from "./util"; import type {LockedFigureSettingsCommonProps} from "./locked-figure-settings"; import type { @@ -76,8 +79,12 @@ const LockedLineSettings = (props: Props) => { if (labels && labels.length > 0) { visiblelabel += ` ${labels.map((l) => l.text).join(", ")}`; } - - const str = `${capitalizeKind}${visiblelabel} from (${point1.coord[0]}, ${point1.coord[1]}) to (${point2.coord[0]}, ${point2.coord[1]})`; + let str = `${capitalizeKind}${visiblelabel} from (${point1.coord[0]}, ${point1.coord[1]}) to (${point2.coord[0]}, ${point2.coord[1]})`; + const lineAppearance = generateLockedFigureAppearanceDescription( + lineColor, + lineStyle, + ); + str += lineAppearance; return str; } diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-point-settings.test.tsx b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-point-settings.test.tsx index c06457e535..1538b7341f 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-point-settings.test.tsx +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-point-settings.test.tsx @@ -410,7 +410,7 @@ describe("LockedPointSettings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Point at (0, 0)", + ariaLabel: "Point at (0, 0). Appearance solid gray.", }); }); @@ -440,7 +440,7 @@ describe("LockedPointSettings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Point A at (0, 0)", + ariaLabel: "Point A at (0, 0). Appearance solid gray.", }); }); @@ -474,7 +474,7 @@ describe("LockedPointSettings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Point A, B at (0, 0)", + ariaLabel: "Point A, B at (0, 0). Appearance solid gray.", }); }); }); diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-point-settings.tsx b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-point-settings.tsx index 97fd099a82..28478fcab0 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-point-settings.tsx +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-point-settings.tsx @@ -22,7 +22,10 @@ import LabeledSwitch from "./labeled-switch"; import LockedFigureAria from "./locked-figure-aria"; import LockedFigureSettingsActions from "./locked-figure-settings-actions"; import LockedLabelSettings from "./locked-label-settings"; -import {getDefaultFigureForType} from "./util"; +import { + generateLockedFigureAppearanceDescription, + getDefaultFigureForType, +} from "./util"; import type {LockedFigureSettingsMovementType} from "./locked-figure-settings-actions"; import type { @@ -115,8 +118,11 @@ const LockedPointSettings = (props: Props) => { if (labels && labels.length > 0) { visiblelabel += ` ${labels.map((l) => l.text).join(", ")}`; } + let str = `Point${visiblelabel} at (${coord[0]}, ${coord[1]})`; - const str = `Point${visiblelabel} at (${coord[0]}, ${coord[1]})`; + const pointAppearance = + generateLockedFigureAppearanceDescription(pointColor); + str += pointAppearance; return str; } diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-polygon-settings.test.tsx b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-polygon-settings.test.tsx index 6b3ee02d5d..90717d9216 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-polygon-settings.test.tsx +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-polygon-settings.test.tsx @@ -598,7 +598,7 @@ describe("LockedPolygonSettings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ ariaLabel: - "Polygon with 3 sides, vertices at (0, 0), (0, 1), (1, 1)", + "Polygon with 3 sides, vertices at (0, 0), (0, 1), (1, 1). Appearance solid gray border, with no fill.", }); }); @@ -634,7 +634,7 @@ describe("LockedPolygonSettings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ ariaLabel: - "Polygon A with 3 sides, vertices at (0, 0), (0, 1), (1, 1)", + "Polygon A with 3 sides, vertices at (0, 0), (0, 1), (1, 1). Appearance solid gray border, with no fill.", }); }); @@ -674,7 +674,7 @@ describe("LockedPolygonSettings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ ariaLabel: - "Polygon A, B with 3 sides, vertices at (0, 0), (0, 1), (1, 1)", + "Polygon A, B with 3 sides, vertices at (0, 0), (0, 1), (1, 1). Appearance solid gray border, with no fill.", }); }); }); diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-polygon-settings.tsx b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-polygon-settings.tsx index 799ea958dd..e8265af75e 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-polygon-settings.tsx +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-polygon-settings.tsx @@ -32,7 +32,10 @@ import LockedFigureAria from "./locked-figure-aria"; import LockedFigureSettingsActions from "./locked-figure-settings-actions"; import LockedLabelSettings from "./locked-label-settings"; import PolygonSwatch from "./polygon-swatch"; -import {getDefaultFigureForType} from "./util"; +import { + generateLockedFigureAppearanceDescription, + getDefaultFigureForType, +} from "./util"; import type {LockedFigureSettingsCommonProps} from "./locked-figure-settings"; @@ -72,6 +75,12 @@ const LockedPolygonSettings = (props: Props) => { // Add the coordinates of each point to the aria label str += points.map(([x, y]) => `(${x}, ${y})`).join(", "); + const polygonAppearance = generateLockedFigureAppearanceDescription( + color, + strokeStyle, + fillStyle, + ); + str += polygonAppearance; return str; } diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-vector-settings.test.tsx b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-vector-settings.test.tsx index 38c69f46db..61c3f98912 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-vector-settings.test.tsx +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-vector-settings.test.tsx @@ -430,7 +430,8 @@ describe("Locked Vector Settings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Vector from (0, 0) to (2, 2)", + ariaLabel: + "Vector from (0, 0) to (2, 2). Appearance solid gray.", }); }); @@ -460,7 +461,8 @@ describe("Locked Vector Settings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Vector A from (0, 0) to (2, 2)", + ariaLabel: + "Vector A from (0, 0) to (2, 2). Appearance solid gray.", }); }); @@ -494,7 +496,8 @@ describe("Locked Vector Settings", () => { // Assert expect(onChangeProps).toHaveBeenCalledWith({ - ariaLabel: "Vector A, B from (0, 0) to (2, 2)", + ariaLabel: + "Vector A, B from (0, 0) to (2, 2). Appearance solid gray.", }); }); }); diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-vector-settings.tsx b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-vector-settings.tsx index 8090fe90d4..13cde17a8b 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-vector-settings.tsx +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-vector-settings.tsx @@ -23,7 +23,10 @@ import LineSwatch from "./line-swatch"; import LockedFigureAria from "./locked-figure-aria"; import LockedFigureSettingsActions from "./locked-figure-settings-actions"; import LockedLabelSettings from "./locked-label-settings"; -import {getDefaultFigureForType} from "./util"; +import { + generateLockedFigureAppearanceDescription, + getDefaultFigureForType, +} from "./util"; import type {LockedFigureSettingsCommonProps} from "./locked-figure-settings"; import type { @@ -67,7 +70,12 @@ const LockedVectorSettings = (props: Props) => { visiblelabel += ` ${labels.map((l) => l.text).join(", ")}`; } - const str = `Vector${visiblelabel} from (${tail[0]}, ${tail[1]}) to (${tip[0]}, ${tip[1]})`; + let str = `Vector${visiblelabel} from (${tail[0]}, ${tail[1]}) to (${tip[0]}, ${tip[1]})`; + + const vectorAppearance = + generateLockedFigureAppearanceDescription(lineColor); + str += vectorAppearance; + return str; } diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/util.test.ts b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/util.test.ts index b67df04ffd..7d8aaedfc6 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/util.test.ts +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/util.test.ts @@ -1,4 +1,13 @@ -import {getDefaultFigureForType} from "./util"; +import { + generateLockedFigureAppearanceDescription, + getDefaultFigureForType, +} from "./util"; + +import type { + LockedFigureColor, + LockedFigureFillType, + LockedLineStyle, +} from "@khanacademy/perseus"; describe("getDefaultFigureForType", () => { test("should return a point with default values", () => { @@ -100,3 +109,152 @@ describe("getDefaultFigureForType", () => { }); }); }); + +describe("generateLockedFigureAppearanceDescription", () => { + // one argument + test(`should return a string with a gray color and a solid stroke style`, () => { + const description = generateLockedFigureAppearanceDescription("grayH"); + expect(description).toBe(`. Appearance solid gray.`); + }); + + test.each([["red"], ["blue"], ["green"], ["purple"], ["orange"]])( + `should return a string with a %s color and a solid stroke style`, + (color: LockedFigureColor) => { + const description = + generateLockedFigureAppearanceDescription(color); + + expect(description).toBe(`. Appearance solid ${color}.`); + }, + ); + + // two arguments + test.each([ + ["blue", "solid"], + ["green", "dashed"], + ])( + `should return a string with color of %s and a stroke style of %s`, + (color: LockedFigureColor, strokeStyle: LockedLineStyle) => { + const description = generateLockedFigureAppearanceDescription( + color, + strokeStyle, + ); + expect(description).toBe(`. Appearance ${strokeStyle} ${color}.`); + }, + ); + + // three arguments + // no fill + test(`should return a string with a gray color, solid stroke, and no fill`, () => { + const description = generateLockedFigureAppearanceDescription( + "grayH", + undefined, + "none", + ); + expect(description).toBe( + `. Appearance solid gray border, with no fill.`, + ); + }); + + test.each([ + ["blue", "solid", "none"], + ["red", "dashed", "none"], + ])( + `should return a string with a %s color, %s stroke and no fill`, + ( + color: LockedFigureColor, + strokeStyle: LockedLineStyle, + fill: LockedFigureFillType, + ) => { + const description = generateLockedFigureAppearanceDescription( + color, + strokeStyle, + fill, + ); + + expect(description).toBe( + `. Appearance ${strokeStyle} ${color} border, with no fill.`, + ); + }, + ); + + // white fill + test("should return a string with a gray color, solid stroke, and a white fill", () => { + const description = generateLockedFigureAppearanceDescription( + "grayH", + undefined, + "white", + ); + expect(description).toBe( + `. Appearance solid gray border, with a white fill.`, + ); + }); + + test.each([ + ["pink", "solid", "white"], + ["red", "dashed", "white"], + ])( + `should return a string with a %s color, %s stroke and a white fill`, + ( + color: LockedFigureColor, + strokeStyle: LockedLineStyle, + fill: LockedFigureFillType, + ) => { + const description = generateLockedFigureAppearanceDescription( + color, + strokeStyle, + fill, + ); + + expect(description).toBe( + `. Appearance ${strokeStyle} ${color} border, with a white fill.`, + ); + }, + ); + + // solid and translucent fills + test("should return a string with a gray color, solid stroke, and a solid fill", () => { + const description = generateLockedFigureAppearanceDescription( + "grayH", + undefined, + "solid", + ); + expect(description).toBe( + `. Appearance solid gray border, with a solid gray fill.`, + ); + }); + + test("should return a string with a gray color, solid stroke, and a translucent fill", () => { + const description = generateLockedFigureAppearanceDescription( + "pink", + undefined, + "translucent", + ); + expect(description).toBe( + `. Appearance solid pink border, with a translucent pink fill.`, + ); + }); + + test.each([ + ["pink", "solid", "solid"], + ["red", "dashed", "solid"], + ["green", "dashed", "translucent"], + ["purple", "solid", "translucent"], + ])( + `should return a string with a %s color, %s stroke, and a %s fill`, + ( + color: LockedFigureColor, + strokeStyle: LockedLineStyle, + fill: LockedFigureFillType, + ) => { + const description = generateLockedFigureAppearanceDescription( + color, + strokeStyle, + fill, + ); + + expect(description).toBe( + `. Appearance ${strokeStyle} ${color} border, with a ${fill} ${color} fill.`, + ); + }, + ); +}); diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/util.ts b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/util.ts index 06a37874b1..cf4f49ecd6 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/util.ts +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/util.ts @@ -3,6 +3,8 @@ import {UnreachableCaseError} from "@khanacademy/wonder-stuff-core"; import type { LockedEllipseType, LockedFigure, + LockedFigureColor, + LockedFigureFillType, LockedFigureType, LockedFunctionType, LockedLabelType, @@ -10,6 +12,7 @@ import type { LockedPointType, LockedPolygonType, LockedVectorType, + LockedLineStyle, } from "@khanacademy/perseus"; const DEFAULT_COLOR = "grayH"; @@ -99,3 +102,25 @@ export function getDefaultFigureForType(type: LockedFigureType): LockedFigure { throw new UnreachableCaseError(type); } } + +export function generateLockedFigureAppearanceDescription( + color: LockedFigureColor, + strokeStyle: LockedLineStyle = "solid", + fill?: LockedFigureFillType, +) { + const convertedColor = color === "grayH" ? "gray" : color; + + switch (fill) { + case "none": + return `. Appearance ${strokeStyle} ${convertedColor} border, with no fill.`; + case "white": + return `. Appearance ${strokeStyle} ${convertedColor} border, with a white fill.`; + case "solid": + case "translucent": + return `. Appearance ${strokeStyle} ${convertedColor} border, with a ${fill} ${convertedColor} fill.`; + case undefined: + return `. Appearance ${strokeStyle} ${convertedColor}.`; + default: + throw new UnreachableCaseError(fill); + } +} diff --git a/packages/perseus/src/index.ts b/packages/perseus/src/index.ts index 076196d37a..fe3f336f43 100644 --- a/packages/perseus/src/index.ts +++ b/packages/perseus/src/index.ts @@ -181,6 +181,7 @@ export type { LockedPolygonType, LockedFunctionType, LockedLabelType, + LockedLineStyle, PerseusGraphType, PerseusAnswerArea, PerseusExpressionWidgetOptions,