@khanacademy/perseus 17.0.0 → 17.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/dist/dependencies.d.ts +1 -0
  3. package/dist/es/index.css +20 -0
  4. package/dist/es/index.js +443 -338
  5. package/dist/es/index.js.map +1 -1
  6. package/dist/index.css +20 -0
  7. package/dist/index.js +444 -340
  8. package/dist/index.js.map +1 -1
  9. package/dist/perseus-types.d.ts +1 -0
  10. package/dist/strings.js +1 -0
  11. package/dist/widgets/label-image/answer-pill.d.ts +10 -0
  12. package/dist/widgets/label-image/hide-answers-toggle.d.ts +6 -0
  13. package/dist/widgets/label-image/marker.d.ts +6 -9
  14. package/dist/widgets/label-image/types.d.ts +1 -0
  15. package/dist/widgets/label-image.d.ts +101 -12
  16. package/package.json +30 -22
  17. package/src/__tests__/__snapshots__/renderer.test.tsx.snap +3 -3
  18. package/src/dependencies.ts +4 -0
  19. package/src/perseus-types.ts +2 -0
  20. package/src/styles/widgets/label-image.less +32 -0
  21. package/src/widgets/__stories__/label-image.stories.tsx +73 -3
  22. package/src/widgets/__testdata__/label-image.testdata.ts +118 -1
  23. package/src/widgets/__tests__/__snapshots__/dropdown.test.ts.snap +2 -2
  24. package/src/widgets/label-image/__stories__/answer-pill.stories.tsx +62 -0
  25. package/src/widgets/label-image/__tests__/answer-pill.test.tsx +83 -0
  26. package/src/widgets/label-image/__tests__/hide-answers-toggle.test.tsx +39 -0
  27. package/src/widgets/label-image/answer-choices.tsx +7 -1
  28. package/src/widgets/label-image/answer-pill.tsx +89 -0
  29. package/src/widgets/label-image/hide-answers-toggle.tsx +40 -0
  30. package/src/widgets/label-image/marker.tsx +116 -174
  31. package/src/widgets/label-image/types.ts +1 -0
  32. package/src/widgets/label-image.test.ts +142 -52
  33. package/src/widgets/label-image.tsx +212 -135
  34. package/tsconfig-build.tsbuildinfo +1 -1
@@ -36,6 +36,7 @@ export type PerseusAnswerArea = {
36
36
  tTable: boolean;
37
37
  calculator: boolean;
38
38
  periodicTable: boolean;
39
+ periodicTableWithKey?: boolean;
39
40
  };
40
41
  type Widget<Type extends string, Options> = {
41
42
  type: Type;
package/dist/strings.js CHANGED
@@ -20,6 +20,7 @@ module.exports = [
20
20
  i18n.$_("[Marker for question %(number)s]", {}),
21
21
  i18n.$_("[Sentence %(number)s]", {}),
22
22
  i18n._("**Your answer should be** ", {}),
23
+ i18n.ngettext("%(num)s answer","%(num)s answers", {}),
23
24
  i18n._("a *proper* fraction, like $1/2$ or $6/10$", {}),
24
25
  i18n._("a *simplified improper* fraction, like $7/4$", {}),
25
26
  i18n._("a *simplified proper* fraction, like $3/5$", {}),
@@ -0,0 +1,10 @@
1
+ /// <reference types="react" />
2
+ import { type CSSProperties } from "aphrodite";
3
+ export declare const AnswerPill: (props: {
4
+ selectedAnswers: readonly string[];
5
+ showCorrectness?: "correct" | "incorrect";
6
+ markerRef?: HTMLElement;
7
+ side: "top" | "bottom" | "left" | "right";
8
+ onClick: () => void;
9
+ style?: CSSProperties;
10
+ }) => JSX.Element;
@@ -0,0 +1,6 @@
1
+ import Switch from "@khanacademy/wonder-blocks-switch";
2
+ import * as React from "react";
3
+ export declare const HideAnswersToggle: (props: {
4
+ areAnswersHidden: boolean;
5
+ onChange: React.ComponentPropsWithoutRef<typeof Switch>["onChange"];
6
+ }) => JSX.Element;
@@ -8,20 +8,17 @@ import type { InteractiveMarkerType } from "./types";
8
8
  type Props = InteractiveMarkerType & {
9
9
  showSelected: boolean;
10
10
  showPulsate: boolean;
11
- onClick: (e: MouseEvent) => void;
12
- onKeyDown: (e: KeyboardEvent) => void;
11
+ onClick: (e: React.MouseEvent) => void;
12
+ onKeyDown: (e: React.KeyboardEvent) => void;
13
+ onFocus: () => void;
14
+ onBlur: () => void;
15
+ focused: boolean;
13
16
  };
14
- type State = {
15
- isFocused: boolean;
16
- };
17
- export default class Marker extends React.Component<Props, State> {
17
+ export default class Marker extends React.Component<Props> {
18
18
  _icon: HTMLElement | null | undefined;
19
19
  static defaultProps: {
20
20
  selected: ReadonlyArray<any>;
21
21
  };
22
- state: State;
23
- handleFocus(): void;
24
- handleBlur(): void;
25
22
  renderIcon(): React.ReactElement<React.ComponentProps<"div">>;
26
23
  render(): React.ReactNode;
27
24
  }
@@ -7,6 +7,7 @@ export type MarkerType = {
7
7
  export type InteractiveMarkerType = MarkerType & {
8
8
  selected?: ReadonlyArray<string>;
9
9
  showCorrectness?: "correct" | "incorrect";
10
+ focused?: boolean;
10
11
  };
11
12
  export type InteractiveMarkerScore = {
12
13
  hasAnswers: boolean;
@@ -10,8 +10,10 @@ import * as React from "react";
10
10
  import AnswerChoices from "./label-image/answer-choices";
11
11
  import Marker from "./label-image/marker";
12
12
  import type { InteractiveMarkerType, InteractiveMarkerScore } from "./label-image/types";
13
+ import type { DependencyProps } from "../dependencies";
13
14
  import type { ChangeableProps } from "../mixins/changeable";
14
15
  import type { APIOptions, PerseusScore } from "../types";
16
+ export type PreferredPopoverDirection = "NONE" | "UP" | "DOWN" | "LEFT" | "RIGHT";
15
17
  type MarkersState = {
16
18
  markers: ReadonlyArray<InteractiveMarkerType>;
17
19
  };
@@ -33,7 +35,7 @@ type Point = {
33
35
  x: number;
34
36
  y: number;
35
37
  };
36
- type LabelImageProps = ChangeableProps & {
38
+ type LabelImageProps = ChangeableProps & DependencyProps & {
37
39
  apiOptions: APIOptions;
38
40
  choices: ReadonlyArray<string>;
39
41
  imageAlt: string;
@@ -44,12 +46,15 @@ type LabelImageProps = ChangeableProps & {
44
46
  multipleAnswers: boolean;
45
47
  hideChoicesFromInstructions: boolean;
46
48
  questionCompleted: boolean;
49
+ preferredPopoverDirection?: PreferredPopoverDirection;
47
50
  };
48
51
  type LabelImageState = {
49
- selectedMarkerIndex: number;
52
+ activeMarkerIndex: number;
50
53
  markersInteracted: boolean;
54
+ focusedMarkerIndex: number;
55
+ hideAnswers: boolean;
51
56
  };
52
- declare class LabelImage extends React.Component<LabelImageProps, LabelImageState> {
57
+ export declare class LabelImage extends React.Component<LabelImageProps, LabelImageState> {
53
58
  _markers: Array<Marker | null | undefined>;
54
59
  _selectedMarkerPopup: Popover | null | undefined;
55
60
  _answerChoices: AnswerChoices | null | undefined;
@@ -64,7 +69,7 @@ declare class LabelImage extends React.Component<LabelImageProps, LabelImageStat
64
69
  /**
65
70
  * Determine the image side given a marker position (as percent of size).
66
71
  */
67
- static imageSideForMarkerPosition(x: number, y: number): "bottom" | "left" | "right" | "top" | "center";
72
+ static imageSideForMarkerPosition(x: number, y: number, preferredDirection: PreferredPopoverDirection | undefined): "bottom" | "left" | "right" | "top" | "center";
68
73
  /**
69
74
  * Calculate the next marker to navigate to, from the "this marker".
70
75
  *
@@ -73,17 +78,13 @@ declare class LabelImage extends React.Component<LabelImageProps, LabelImageStat
73
78
  */
74
79
  static navigateToMarkerIndex(navigateDirection: Direction, markers: ReadonlyArray<InteractiveMarkerType>, thisIndex: number): number;
75
80
  constructor(props: LabelImageProps);
76
- componentDidMount(): void;
77
- componentWillUnmount(): void;
78
81
  simpleValidate(rubric: LabelImageProps): PerseusScore;
79
82
  getUserInput(): MarkersState;
80
83
  showRationalesForCurrentlySelectedChoices(rubric: LabelImageProps): void;
81
84
  dismissMarkerPopup(): void;
82
- handleDocumentClick: (e: MouseEvent) => void;
83
- handleDocumentKeyDown: (e: KeyboardEvent) => void;
84
85
  handleMarkerChange(index: number, marker: InteractiveMarkerType): void;
85
- handleMarkerClick(index: number, e: MouseEvent): void;
86
- handleMarkerKeyDown(index: number, e: KeyboardEvent): void;
86
+ activateMarker(index: number): void;
87
+ handleMarkerKeyDown(index: number, e: React.KeyboardEvent): void;
87
88
  handleAnswerChoicesChangeForMarker(index: number, selection: ReadonlyArray<boolean>): void;
88
89
  renderAnswerChoicesForMarker(index: number, marker: InteractiveMarkerType): React.ReactElement<React.ComponentProps<"div">>;
89
90
  renderMarkers(): ReadonlyArray<React.ReactNode>;
@@ -93,7 +94,51 @@ declare class LabelImage extends React.Component<LabelImageProps, LabelImageStat
93
94
  declare const _default: Readonly<{
94
95
  name: string;
95
96
  displayName: string;
96
- getWidget?: (() => typeof LabelImage) | undefined;
97
+ getWidget?: (() => React.ForwardRefExoticComponent<Omit<ChangeableProps & Partial<import("../types").PerseusDependenciesV2> & {
98
+ apiOptions: Readonly<{
99
+ isArticle?: boolean | undefined;
100
+ onInputError?: ((widgetId: any, value: string, message?: string | null | undefined) => unknown) | undefined;
101
+ onFocusChange?: ((newFocusPath: import("../types").FocusPath, oldFocusPath: import("../types").FocusPath, keypadElement?: Element | Text | null | undefined, focusedElement?: HTMLElement | undefined) => unknown) | undefined;
102
+ GroupMetadataEditor?: React.ComponentType<any> | undefined;
103
+ showAlignmentOptions?: boolean | undefined;
104
+ readOnly?: boolean | undefined;
105
+ answerableCallback?: ((arg1: boolean) => unknown) | undefined;
106
+ getAnotherHint?: (() => unknown) | undefined;
107
+ interactionCallback?: (() => unknown) | undefined;
108
+ groupAnnotator?: ((groupNumber: number, widgetId: string) => React.ReactNode) | undefined;
109
+ imagePlaceholder?: React.ReactNode;
110
+ widgetPlaceholder?: React.ReactNode;
111
+ baseElements?: {
112
+ Link: React.ComponentType<any>;
113
+ } | undefined;
114
+ imagePreloader?: ((dimensions: import("../types").Dimensions) => React.ReactNode) | undefined;
115
+ trackInteraction?: ((args: {
116
+ type: string;
117
+ id: string;
118
+ correct?: boolean | undefined;
119
+ } & Partial<import("../types").TrackingGradedGroupExtraArguments> & Partial<import("../types").TrackingSequenceExtraArguments>) => void) | undefined;
120
+ customKeypad?: boolean | undefined;
121
+ nativeKeypadProxy?: ((blur: () => void) => import("@khanacademy/math-input").KeypadAPI) | undefined;
122
+ isMobile?: boolean | undefined;
123
+ setDrawingAreaAvailable?: ((arg1: boolean) => unknown) | undefined;
124
+ useDraftEditor?: boolean | undefined;
125
+ hintProgressColor?: string | undefined;
126
+ canScrollPage?: boolean | undefined;
127
+ inModal?: boolean | undefined;
128
+ crossOutEnabled?: boolean | undefined;
129
+ editorChangeDelay?: number | undefined;
130
+ }>;
131
+ choices: readonly string[];
132
+ imageAlt: string;
133
+ imageUrl: string;
134
+ imageWidth: number;
135
+ imageHeight: number;
136
+ markers: readonly InteractiveMarkerType[];
137
+ multipleAnswers: boolean;
138
+ hideChoicesFromInstructions: boolean;
139
+ questionCompleted: boolean;
140
+ preferredPopoverDirection?: PreferredPopoverDirection | undefined;
141
+ }, "analytics"> & React.RefAttributes<LabelImage>>) | undefined;
97
142
  accessible?: boolean | ((props: any) => boolean) | undefined;
98
143
  hidden?: boolean | undefined;
99
144
  version?: import("../types").Version | undefined;
@@ -108,6 +153,50 @@ declare const _default: Readonly<{
108
153
  propUpgrades?: {
109
154
  [key: string]: (arg1: any) => any;
110
155
  } | undefined;
111
- widget: typeof LabelImage;
156
+ widget: React.ForwardRefExoticComponent<Omit<ChangeableProps & Partial<import("../types").PerseusDependenciesV2> & {
157
+ apiOptions: Readonly<{
158
+ isArticle?: boolean | undefined;
159
+ onInputError?: ((widgetId: any, value: string, message?: string | null | undefined) => unknown) | undefined;
160
+ onFocusChange?: ((newFocusPath: import("../types").FocusPath, oldFocusPath: import("../types").FocusPath, keypadElement?: Element | Text | null | undefined, focusedElement?: HTMLElement | undefined) => unknown) | undefined;
161
+ GroupMetadataEditor?: React.ComponentType<any> | undefined;
162
+ showAlignmentOptions?: boolean | undefined;
163
+ readOnly?: boolean | undefined;
164
+ answerableCallback?: ((arg1: boolean) => unknown) | undefined;
165
+ getAnotherHint?: (() => unknown) | undefined;
166
+ interactionCallback?: (() => unknown) | undefined;
167
+ groupAnnotator?: ((groupNumber: number, widgetId: string) => React.ReactNode) | undefined;
168
+ imagePlaceholder?: React.ReactNode;
169
+ widgetPlaceholder?: React.ReactNode;
170
+ baseElements?: {
171
+ Link: React.ComponentType<any>;
172
+ } | undefined;
173
+ imagePreloader?: ((dimensions: import("../types").Dimensions) => React.ReactNode) | undefined;
174
+ trackInteraction?: ((args: {
175
+ type: string;
176
+ id: string;
177
+ correct?: boolean | undefined;
178
+ } & Partial<import("../types").TrackingGradedGroupExtraArguments> & Partial<import("../types").TrackingSequenceExtraArguments>) => void) | undefined;
179
+ customKeypad?: boolean | undefined;
180
+ nativeKeypadProxy?: ((blur: () => void) => import("@khanacademy/math-input").KeypadAPI) | undefined;
181
+ isMobile?: boolean | undefined;
182
+ setDrawingAreaAvailable?: ((arg1: boolean) => unknown) | undefined;
183
+ useDraftEditor?: boolean | undefined;
184
+ hintProgressColor?: string | undefined;
185
+ canScrollPage?: boolean | undefined;
186
+ inModal?: boolean | undefined;
187
+ crossOutEnabled?: boolean | undefined;
188
+ editorChangeDelay?: number | undefined;
189
+ }>;
190
+ choices: readonly string[];
191
+ imageAlt: string;
192
+ imageUrl: string;
193
+ imageWidth: number;
194
+ imageHeight: number;
195
+ markers: readonly InteractiveMarkerType[];
196
+ multipleAnswers: boolean;
197
+ hideChoicesFromInstructions: boolean;
198
+ questionCompleted: boolean;
199
+ preferredPopoverDirection?: PreferredPopoverDirection | undefined;
200
+ }, "analytics"> & React.RefAttributes<LabelImage>>;
112
201
  }>;
113
202
  export default _default;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Core Perseus API (includes renderers and widgets)",
4
4
  "author": "Khan Academy",
5
5
  "license": "MIT",
6
- "version": "17.0.0",
6
+ "version": "17.1.0",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -22,33 +22,36 @@
22
22
  "test": "bash -c 'yarn --silent --cwd \"../..\" test ${@:0} $($([[ ${@: -1} = -* ]] || [[ ${@: -1} = bash ]]) && echo $PWD)'"
23
23
  },
24
24
  "dependencies": {
25
- "@khanacademy/kas": "^0.3.6",
26
- "@khanacademy/kmath": "^0.1.7",
27
- "@khanacademy/math-input": "^16.1.1",
28
- "@khanacademy/perseus-core": "1.4.0",
29
- "@khanacademy/perseus-linter": "^0.3.9",
30
- "@khanacademy/pure-markdown": "^0.2.12",
31
- "@khanacademy/simple-markdown": "^0.11.1"
25
+ "@khanacademy/kas": "^0.3.7",
26
+ "@khanacademy/kmath": "^0.1.8",
27
+ "@khanacademy/math-input": "^16.1.2",
28
+ "@khanacademy/perseus-core": "1.4.1",
29
+ "@khanacademy/perseus-linter": "^0.3.10",
30
+ "@khanacademy/pure-markdown": "^0.2.13",
31
+ "@khanacademy/simple-markdown": "^0.11.2"
32
32
  },
33
33
  "devDependencies": {
34
- "@khanacademy/wonder-blocks-button": "^6.2.2",
34
+ "@khanacademy/wonder-blocks-button": "^6.2.4",
35
35
  "@khanacademy/wonder-blocks-clickable": "^4.0.11",
36
36
  "@khanacademy/wonder-blocks-color": "^3.0.0",
37
37
  "@khanacademy/wonder-blocks-core": "^6.3.0",
38
38
  "@khanacademy/wonder-blocks-data": "^13.0.5",
39
- "@khanacademy/wonder-blocks-dropdown": "^3.1.8",
40
- "@khanacademy/wonder-blocks-form": "^4.3.16",
39
+ "@khanacademy/wonder-blocks-dropdown": "^4.0.0",
40
+ "@khanacademy/wonder-blocks-form": "^4.3.18",
41
41
  "@khanacademy/wonder-blocks-i18n": "^2.0.2",
42
42
  "@khanacademy/wonder-blocks-layout": "^2.0.24",
43
- "@khanacademy/wonder-blocks-link": "^6.0.2",
44
- "@khanacademy/wonder-blocks-modal": "^4.0.35",
45
- "@khanacademy/wonder-blocks-popover": "^3.0.16",
43
+ "@khanacademy/wonder-blocks-link": "^6.0.4",
44
+ "@khanacademy/wonder-blocks-modal": "^4.0.38",
45
+ "@khanacademy/wonder-blocks-pill": "^1.0.14",
46
+ "@khanacademy/wonder-blocks-popover": "^3.0.19",
46
47
  "@khanacademy/wonder-blocks-progress-spinner": "^2.0.24",
47
48
  "@khanacademy/wonder-blocks-spacing": "^4.0.1",
48
- "@khanacademy/wonder-blocks-tooltip": "^2.1.18",
49
+ "@khanacademy/wonder-blocks-switch": "^1.1.8",
50
+ "@khanacademy/wonder-blocks-tooltip": "^2.1.21",
49
51
  "@khanacademy/wonder-blocks-typography": "^2.1.9",
50
52
  "@khanacademy/wonder-stuff-core": "^1.5.1",
51
53
  "@phosphor-icons/core": "^2.0.2",
54
+ "@popperjs/core": "^2.10.1",
52
55
  "aphrodite": "^1.2.5",
53
56
  "classnames": "^1.1.4",
54
57
  "create-react-class": "^15.6.3",
@@ -63,27 +66,31 @@
63
66
  "prop-types": "^15.6.1",
64
67
  "react": "16.14.0",
65
68
  "react-dom": "16.14.0",
69
+ "react-popper": "^2.0.0",
66
70
  "underscore": "^1.4.4"
67
71
  },
68
72
  "peerDependencies": {
69
- "@khanacademy/wonder-blocks-button": "^6.2.2",
73
+ "@khanacademy/wonder-blocks-button": "^6.2.4",
70
74
  "@khanacademy/wonder-blocks-clickable": "^4.0.11",
71
75
  "@khanacademy/wonder-blocks-color": "^3.0.0",
72
76
  "@khanacademy/wonder-blocks-core": "^6.3.0",
73
77
  "@khanacademy/wonder-blocks-data": "^13.0.5",
74
- "@khanacademy/wonder-blocks-dropdown": "^3.1.8",
75
- "@khanacademy/wonder-blocks-form": "^4.3.16",
78
+ "@khanacademy/wonder-blocks-dropdown": "^4.0.0",
79
+ "@khanacademy/wonder-blocks-form": "^4.3.18",
76
80
  "@khanacademy/wonder-blocks-i18n": "^2.0.2",
77
81
  "@khanacademy/wonder-blocks-layout": "^2.0.24",
78
- "@khanacademy/wonder-blocks-link": "^6.0.2",
79
- "@khanacademy/wonder-blocks-modal": "^4.0.35",
80
- "@khanacademy/wonder-blocks-popover": "^3.0.16",
82
+ "@khanacademy/wonder-blocks-link": "^6.0.4",
83
+ "@khanacademy/wonder-blocks-modal": "^4.0.38",
84
+ "@khanacademy/wonder-blocks-pill": "^1.0.14",
85
+ "@khanacademy/wonder-blocks-popover": "^3.0.19",
81
86
  "@khanacademy/wonder-blocks-progress-spinner": "^2.0.24",
82
87
  "@khanacademy/wonder-blocks-spacing": "^4.0.1",
83
- "@khanacademy/wonder-blocks-tooltip": "^2.1.18",
88
+ "@khanacademy/wonder-blocks-switch": "^1.1.8",
89
+ "@khanacademy/wonder-blocks-tooltip": "^2.1.21",
84
90
  "@khanacademy/wonder-blocks-typography": "^2.1.9",
85
91
  "@khanacademy/wonder-stuff-core": "^1.5.1",
86
92
  "@phosphor-icons/core": "^2.0.2",
93
+ "@popperjs/core": "^2.10.1",
87
94
  "aphrodite": "^1.2.5",
88
95
  "classnames": "^1.1.4",
89
96
  "create-react-class": "^15.6.3",
@@ -93,6 +100,7 @@
93
100
  "prop-types": "^15.6.1",
94
101
  "react": "16.14.0",
95
102
  "react-dom": "16.14.0",
103
+ "react-popper": "^2.0.0",
96
104
  "underscore": "^1.4.4"
97
105
  },
98
106
  "keywords": []
@@ -369,7 +369,7 @@ exports[`renderer snapshots correct answer: correct answer 1`] = `
369
369
  </span>
370
370
  <span
371
371
  aria-hidden="true"
372
- class="svg_aeiopc-o_O-caret_ntkmq-o_O-inlineStyles_13xtep5"
372
+ class="svg_1q6jc65-o_O-icon_eocf2p-o_O-caret_ntkmq-o_O-inlineStyles_u7ovp6"
373
373
  />
374
374
  </button>
375
375
  </div>
@@ -434,7 +434,7 @@ exports[`renderer snapshots incorrect answer: incorrect answer 1`] = `
434
434
  </span>
435
435
  <span
436
436
  aria-hidden="true"
437
- class="svg_aeiopc-o_O-caret_ntkmq-o_O-inlineStyles_13xtep5"
437
+ class="svg_1q6jc65-o_O-icon_eocf2p-o_O-caret_ntkmq-o_O-inlineStyles_u7ovp6"
438
438
  />
439
439
  </button>
440
440
  </div>
@@ -499,7 +499,7 @@ exports[`renderer snapshots initial render: initial render 1`] = `
499
499
  </span>
500
500
  <span
501
501
  aria-hidden="true"
502
- class="svg_aeiopc-o_O-caret_ntkmq-o_O-inlineStyles_13xtep5"
502
+ class="svg_1q6jc65-o_O-icon_eocf2p-o_O-caret_ntkmq-o_O-inlineStyles_u7ovp6"
503
503
  />
504
504
  </button>
505
505
  </div>
@@ -22,6 +22,10 @@ export const getDependencies = (): PerseusDependencies => {
22
22
  );
23
23
  };
24
24
 
25
+ export type DependencyProps = Partial<
26
+ React.ContextType<typeof DependenciesContext>
27
+ >;
28
+
25
29
  export const DependenciesContext = React.createContext<PerseusDependenciesV2>({
26
30
  analytics: {onAnalyticsEvent: async () => {}},
27
31
  });
@@ -73,6 +73,8 @@ export type PerseusAnswerArea = {
73
73
  calculator: boolean;
74
74
  // The user might benefit from using a Periodic Table of Elements. Provided on Khan Academy when true
75
75
  periodicTable: boolean;
76
+ // The user might benefit from using a Periodic Table of Elements with key. Provided on Khan Academy when true
77
+ periodicTableWithKey?: boolean;
76
78
  };
77
79
 
78
80
  type Widget<Type extends string, Options> = {
@@ -5,6 +5,38 @@
5
5
  margin: 0;
6
6
  }
7
7
  }
8
+ button[id*="perseus-label-image-widget-answer-pill"],
9
+ div[id*="perseus-label-image-widget-answer-pill"] {
10
+ // Reset text content.
11
+ div.paragraph {
12
+ all: revert;
13
+ }
14
+ // Allow for padding around math content. Accounting for the negative margin here:
15
+ // https://github.com/Khan/perseus/blob/bafd35daee723456bf259a39b88e7638d91c8c3b/packages/perseus/src/renderer.tsx#L1176
16
+ div.perseus-block-math-inner {
17
+ margin: -5px 0 !important;
18
+ }
19
+ }
20
+ }
21
+
22
+ // Reset mobile text content for answer pills.
23
+ @media (max-width: 767px) {
24
+ [id*="perseus-label-image-widget-answer-pill"] {
25
+ div.paragraph {
26
+ all: revert;
27
+ }
28
+ > div.perseus-renderer-responsive {
29
+ all: revert;
30
+ }
31
+ }
32
+
33
+ .framework-perseus.perseus-mobile
34
+ [id*="perseus-label-image-widget-answer-pill"] {
35
+ .perseus-renderer > .paragraph,
36
+ .perseus-renderer > .paragraph .paragraph {
37
+ all: revert;
38
+ }
39
+ }
8
40
  }
9
41
 
10
42
  .framework-perseus:not(.perseus-article).perseus-mobile {
@@ -1,12 +1,44 @@
1
1
  import * as React from "react";
2
2
 
3
3
  import {RendererWithDebugUI} from "../../../../../testing/renderer-with-debug-ui";
4
- import {question} from "../__testdata__/label-image.testdata";
4
+ import {
5
+ textQuestion,
6
+ mathQuestion,
7
+ numberline,
8
+ } from "../__testdata__/label-image.testdata";
5
9
 
10
+ import type {PerseusRenderer} from "../../perseus-types";
6
11
  import type {APIOptions} from "../../types";
7
12
 
13
+ const applyStoryArgs = (
14
+ question: PerseusRenderer,
15
+ args: StoryArgs,
16
+ ): PerseusRenderer => {
17
+ const q = {
18
+ ...question,
19
+ widgets: {},
20
+ } as const;
21
+
22
+ for (const [widgetId, widget] of Object.entries(question.widgets)) {
23
+ const modified = {
24
+ ...widget,
25
+ options: {
26
+ ...widget.options,
27
+ preferredPopoverDirection: args.preferredPopoverDirection,
28
+ },
29
+ };
30
+
31
+ q.widgets[widgetId] = {
32
+ ...modified,
33
+ };
34
+ }
35
+
36
+ return q;
37
+ };
38
+
8
39
  type StoryArgs = {
9
40
  isMobile: boolean;
41
+ preferredPopoverDirection: string;
10
42
  };
11
43
 
12
44
  type ImageStory = {
@@ -14,17 +46,55 @@ type ImageStory = {
14
46
  args: StoryArgs;
15
47
  };
16
48
 
17
- export const Question1 = (args: StoryArgs): React.ReactElement => {
49
+ export const LabelWidgetWithText = (args: StoryArgs): React.ReactElement => {
50
+ const apiOptions: APIOptions = {
51
+ isMobile: args.isMobile,
52
+ };
53
+
54
+ return (
55
+ <RendererWithDebugUI
56
+ question={applyStoryArgs(textQuestion, args)}
57
+ apiOptions={apiOptions}
58
+ />
59
+ );
60
+ };
61
+
62
+ export const LabelWidgetWithMath = (args: StoryArgs): React.ReactElement => {
18
63
  const apiOptions: APIOptions = {
19
64
  isMobile: args.isMobile,
20
65
  };
21
66
 
22
- return <RendererWithDebugUI question={question} apiOptions={apiOptions} />;
67
+ return (
68
+ <RendererWithDebugUI
69
+ question={applyStoryArgs(mathQuestion, args)}
70
+ apiOptions={apiOptions}
71
+ />
72
+ );
73
+ };
74
+
75
+ export const LabelImageNumberline = (args: StoryArgs): React.ReactElement => {
76
+ const apiOptions: APIOptions = {
77
+ isMobile: args.isMobile,
78
+ };
79
+
80
+ return (
81
+ <RendererWithDebugUI
82
+ question={applyStoryArgs(numberline, args)}
83
+ apiOptions={apiOptions}
84
+ />
85
+ );
23
86
  };
24
87
 
25
88
  export default {
26
89
  title: "Perseus/Widgets/Label Image",
27
90
  args: {
28
91
  isMobile: false,
92
+ preferredPopoverDirection: "NONE",
93
+ },
94
+ argTypes: {
95
+ preferredPopoverDirection: {
96
+ control: "select",
97
+ options: ["NONE", "UP", "DOWN", "LEFT", "RIGHT"],
98
+ },
29
99
  },
30
100
  } as ImageStory;