Skip to content

Commit

Permalink
[Dev UI] Display info about background image of current question (#1222)
Browse files Browse the repository at this point in the history
## Summary:

With our migration to Mafs, we still need to draw any `interactive-graph` backgrounds. But, when we do, it can become difficult to determine which UI elements are coming from the background and which are drawn by Mafs. 

This PR introduces a little "status" icon that, when hovered, shows the background image, if there is one. If it is a Graphie, it provides a link to view/edit the Graphie in the graphie-to-png editor.

<img width="562" alt="image" src="https://github.com/Khan/perseus/assets/77138/81d8b2ee-e2de-4012-88a3-cb3c85aaa685">


Issue: "none"

## Test plan:

`yarn dev` 

Paste the following into the Flipbook:

```json 
{"content":"The graph of $y=h(x)$ is a line segment joining the points $(1,9)$ and $(3,2)$. \n\n**Drag the endpoints of the segment below to graph $y=h^{-1}(x)$.\n **\n\n[[☃ interactive-graph 1]]","images":{},"widgets":{"interactive-graph 1":{"alignment":"default","graded":true,"options":{"backgroundImage":{"bottom":0,"height":425,"left":0,"scale":1,"url":"web+graphie://ka-perseus-graphie.s3.amazonaws.com/a82b4098da55c75b68d2c1fd9f348923b8935ccb","width":425},"correct":{"coords":[[[9,1],[2,3]]],"type":"segment"},"graph":{"type":"segment"},"gridStep":[1,1],"labels":["x","y"],"markings":"none","range":[[-10,10],[-10,10]],"rulerLabel":"","rulerTicks":10,"showProtractor":false,"showRuler":false,"snapStep":[0.5,0.5],"step":[1,1]},"type":"interactive-graph","version":{"major":0,"minor":0}}}}
```

Hover over the little icon below the <textarea> on the right side. 

Paste the following into the Flipbook:

```json
{"content":"$f$ is a finite function whose domain is the letters $a$ to $e$. The following table lists the output for each input in $f$'s domain.\n\n$x$|$a$|$b$|$c$|$d$|$e$\n:-|-:|-:|-:|-:\n$f(x)$|$1$|$2$|$2$|$3$|$4$\n\n**Build the mapping diagram of $f$ by dragging the endpoints of the segments in the graph below so that they pair each domain element with its correct range element. **\n\n**Then, determine if $f$ is invertible.**\n\n[[☃ interactive-graph 1]]\n\n\n\n[[☃ radio 2]]\n\n\n","images":{},"widgets":{"interactive-graph 1":{"type":"interactive-graph","alignment":"default","static":false,"graded":true,"options":{"step":[1,1],"backgroundImage":{"url":"https://ka-perseus-graphie.s3.amazonaws.com/a86c03713a29d33e2a5f8d2abfa578594dbba2e0.png","scale":1,"bottom":0,"left":0,"width":400,"height":400},"markings":"none","labels":["x","y"],"showProtractor":false,"showRuler":false,"rulerLabel":"","rulerTicks":10,"range":[[-10,10],[-10,10]],"gridStep":[1,1],"snapStep":[1,1],"graph":{"type":"segment","numSegments":5},"correct":{"type":"segment","numSegments":5,"coords":[[[-5,6],[5,4]],[[-5,3],[5,1]],[[-5,0],[5,1]],[[-5,-3],[5,-2]],[[-5,-6],[5,-5]]]}},"version":{"major":0,"minor":0}},"radio 2":{"type":"radio","alignment":"default","static":false,"graded":true,"options":{"choices":[{"correct":false,"content":"$f$ is invertible"},{"correct":true,"content":"$f$ is not invertible"}],"randomize":false,"multipleSelect":false,"displayCount":null,"hasNoneOfTheAbove":false,"onePerLine":true,"deselectEnabled":false},"version":{"major":1,"minor":0}}}}
```

Note that the icon changes and it says the image is a "regular image" background.

Author: jeremywiebe

Reviewers: benchristel, jeremywiebe, SonicScrewdriver, nishasy, mark-fitzgerald, handeyeco, Myranae

Required Reviewers:

Approved By: benchristel

Checks: ✅ codecov/project, ✅ codecov/patch, ✅ Upload Coverage, ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Lint, Typecheck, Format, and Test (ubuntu-latest, 20.x), ✅ Cypress (ubuntu-latest, 20.x), ✅ Jest Coverage (ubuntu-latest, 20.x), ✅ Check builds for changes in size (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ Publish Storybook to Chromatic (ubuntu-latest, 20.x), ✅ gerald

Pull Request URL: #1222
  • Loading branch information
jeremywiebe committed May 6, 2024
1 parent bada382 commit 44cf734
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/eighty-items-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khanacademy/perseus": patch
---

Fix @phosphor-icon paths in `explanation` widget
5 changes: 5 additions & 0 deletions .changeset/six-melons-knock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khanacademy/perseus-dev-ui": patch
---

✨ Display image background info in Dev UI
1 change: 1 addition & 0 deletions config/test/test.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const pkgMap = fs
};
}, {});

/** @type {import('jest').Config} */
module.exports = {
rootDir: path.join(__dirname, "../../"),
transform: {
Expand Down
100 changes: 99 additions & 1 deletion dev/flipbook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@
import Banner from "@khanacademy/wonder-blocks-banner";
import Button from "@khanacademy/wonder-blocks-button";
import {View} from "@khanacademy/wonder-blocks-core";
import IconButton from "@khanacademy/wonder-blocks-icon-button";
import {Strut} from "@khanacademy/wonder-blocks-layout";
import Link from "@khanacademy/wonder-blocks-link";
import {useTimeout} from "@khanacademy/wonder-blocks-timing";
import {color, spacing} from "@khanacademy/wonder-blocks-tokens";
import Toolbar from "@khanacademy/wonder-blocks-toolbar";
import Tooltip from "@khanacademy/wonder-blocks-tooltip";
import {UnreachableCaseError} from "@khanacademy/wonder-stuff-core";
import cameraSlashIcon from "@phosphor-icons/core/regular/camera-slash.svg";
import graphIcon from "@phosphor-icons/core/regular/graph.svg";
import imageIcon from "@phosphor-icons/core/regular/image.svg";
import * as React from "react";
import {useEffect, useReducer, useRef, useState} from "react";
import {useEffect, useMemo, useReducer, useRef, useState} from "react";

import {Renderer} from "../packages/perseus/src";
import {SvgImage} from "../packages/perseus/src/components";
import {mockStrings} from "../packages/perseus/src/strings";
import {isCorrect} from "../packages/perseus/src/util";
import {trueForAllMafsSupportedGraphTypes} from "../packages/perseus/src/widgets/interactive-graphs/mafs-supported-graph-types";
Expand All @@ -35,7 +42,9 @@ import type {
APIOptions,
PerseusRenderer,
PerseusScore,
PerseusWidget,
} from "../packages/perseus/src";
import type {InteractiveGraphWidget} from "../packages/perseus/src/perseus-types";
import type {PropsFor} from "@khanacademy/wonder-blocks-core";

import "../packages/perseus/src/styles/perseus-renderer.less";
Expand All @@ -50,6 +59,16 @@ grep -rl '"type":"segment"' data/questions/ | xargs cat | pbcopy

const LS_QUESTIONS_KEY = "FLIPBOOK-QUESTIONS-JSON";

function isInteractiveGraph(
widget: PerseusWidget,
): widget is InteractiveGraphWidget {
return widget.type === "interactive-graph";
}

function isGraphieUrl(url: string) {
return url.startsWith("web+graphie://");
}

export function Flipbook() {
const [state, dispatch] = useReducer(flipbookModelReducer, {
questions: "",
Expand All @@ -64,6 +83,15 @@ export function Flipbook() {
const questionsState = state.questions.trim();
const noTextEntered = questionsState === "";

const imageUrls = useMemo<ReadonlyArray<string>>(
() =>
Object.values(question?.widgets ?? {})
.filter(isInteractiveGraph)
.map((w) => w.options.backgroundImage?.url ?? "")
.filter((url) => url.length > 0),
[question],
);

useEffect(() => {
const localStorageQuestions =
localStorage.getItem(LS_QUESTIONS_KEY) || "";
Expand Down Expand Up @@ -130,6 +158,35 @@ export function Flipbook() {
</Button>
</>
}
rightContent={
<View>
{imageUrls?.map((url) => (
<Tooltip
key={url}
placement="right"
content={<GraphiePreview url={url} />}
>
<IconButton
icon={
isGraphieUrl(url)
? graphIcon
: imageIcon
}
/>
</Tooltip>
))}
{(imageUrls?.length ?? 0) === 0 && (
<Tooltip
placement="right"
content={
"This graph does not specify a background image"
}
>
<IconButton icon={cameraSlashIcon} />
</Tooltip>
)}
</View>
}
/>

<Strut size={spacing.small_12} />
Expand Down Expand Up @@ -306,3 +363,44 @@ function Progress(props: ProgressProps) {
</div>
);
}

type GraphiePreviewProps = {url: string};

function GraphiePreview({url}: GraphiePreviewProps) {
return (
<>
<Toolbar
leftContent={
<View style={{display: "flex", flexDirection: "row"}}>
This question uses a
{isGraphieUrl(url) ? (
<Link
href={`http://graphie-to-png.khanacademy.systems?preload=${encodeURIComponent(url)}`}
target="_blank"
style={{
marginLeft: spacing.xxSmall_6,
marginRight: spacing.xxSmall_6,
}}
>
Graphie
</Link>
) : (
" regular image "
)}
background.
</View>
}
rightContent={<></>}
/>
<View
className="framework-perseus"
style={{margin: spacing.medium_16, border: "solid 1px grey"}}
>
<SvgImage
alt={"The background image for this graph question"}
src={url}
/>
</View>
</>
);
}
8 changes: 7 additions & 1 deletion dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,16 @@
"@khanacademy/pure-markdown": "^0.3.3",
"@khanacademy/simple-markdown": "^0.11.4",
"@khanacademy/wonder-blocks-banner": "^3.0.41",
"@khanacademy/wonder-blocks-icon": "^4.1.0",
"@khanacademy/wonder-blocks-icon-button": "^5.2.0",
"@khanacademy/wonder-blocks-link": "^6.1.0",
"@khanacademy/wonder-blocks-search-field": "^2.2.9",
"@khanacademy/wonder-blocks-timing": "^4.0.2",
"@khanacademy/wonder-blocks-tokens": "^1.0.0",
"@khanacademy/wonder-blocks-toolbar": "^3.0.30",
"@khanacademy/wonder-stuff-core": "^1.5.2"
"@khanacademy/wonder-blocks-tooltip": "^2.2.1",
"@khanacademy/wonder-stuff-core": "^1.5.2",
"@phosphor-icons/core": "^2.0.2"
},
"devDependencies": {
"vite": "^5.1.0"
Expand Down
4 changes: 2 additions & 2 deletions packages/perseus/src/widgets/explanation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import {linterContextDefault} from "@khanacademy/perseus-linter";
import Button from "@khanacademy/wonder-blocks-button";
import {UniqueIDProvider, View} from "@khanacademy/wonder-blocks-core";
import caretDown from "@phosphor-icons/core/assets/regular/caret-down.svg";
import caretUp from "@phosphor-icons/core/assets/regular/caret-up.svg";
import caretDown from "@phosphor-icons/core/regular/caret-down.svg";
import caretUp from "@phosphor-icons/core/regular/caret-up.svg";
import {StyleSheet} from "aphrodite";
import * as React from "react";
import _ from "underscore";
Expand Down

0 comments on commit 44cf734

Please sign in to comment.