@khanacademy/perseus 10.0.1 → 10.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.
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": "10.0.1",
6
+ "version": "10.1.0",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -23,7 +23,7 @@
23
23
  "dependencies": {
24
24
  "@khanacademy/kas": "^0.3.1",
25
25
  "@khanacademy/kmath": "^0.1.2",
26
- "@khanacademy/math-input": "^10.1.0",
26
+ "@khanacademy/math-input": "^10.1.1",
27
27
  "@khanacademy/perseus-core": "0.2.0",
28
28
  "@khanacademy/perseus-linter": "^0.3.2",
29
29
  "@khanacademy/pure-markdown": "^0.2.4",
@@ -230,7 +230,7 @@ exports[`renderer rendering should render block math on mobile 1`] = `
230
230
  style="overflow-x: auto; overflow-y: hidden; margin-top: -10px; margin-bottom: -10px; transform: translate3d(0,0,0); padding: 10px 16px 10px 16px;"
231
231
  >
232
232
  <span
233
- style="display: block; width: 100%; transform: scale(1, 1) translate(0, 8px); transform-origin: 0 0; opacity: 0;"
233
+ style="display: block; width: 100%; transform: scale(1, 1); transform-origin: 0 0; opacity: 1;"
234
234
  >
235
235
  <span
236
236
  class="mock-TeX"
@@ -10,6 +10,11 @@ type Story = {
10
10
 
11
11
  export default {
12
12
  title: "Perseus/Components/Zoomable",
13
+ argTypes: {
14
+ disableEntranceAnimation: {
15
+ control: {type: "boolean"},
16
+ },
17
+ },
13
18
  } as Story;
14
19
 
15
20
  type Bounds = {
@@ -29,7 +34,10 @@ const computeChildBounds = (
29
34
 
30
35
  export const ZoomableExample = (args: StoryArgs): React.ReactElement => {
31
36
  return (
32
- <Zoomable computeChildBounds={computeChildBounds}>
37
+ <Zoomable
38
+ computeChildBounds={computeChildBounds}
39
+ disableEntranceAnimation={!!args.disableEntranceAnimation}
40
+ >
33
41
  <span>
34
42
  Here's some zoomed-out content.
35
43
  <br />
@@ -59,6 +59,8 @@ export default class ZoomableTeX extends React.Component<Props, State> {
59
59
  <Zoomable
60
60
  readyToMeasure={this.state.isRendered}
61
61
  computeChildBounds={computeMathBounds}
62
+ animateHeight={false}
63
+ disableEntranceAnimation={true}
62
64
  >
63
65
  <AssetContext.Consumer>
64
66
  {({setAssetStatus}) => (
@@ -14,6 +14,8 @@ type Bounds = {
14
14
  type Props = {
15
15
  children: React.ReactNode;
16
16
  animateHeight: boolean;
17
+
18
+ disableEntranceAnimation: boolean;
17
19
  /**
18
20
  * Optional function that allows customizations in zooming.
19
21
  *
@@ -35,6 +37,7 @@ type DefaultProps = {
35
37
  animateHeight: Props["animateHeight"];
36
38
  computeChildBounds: Props["computeChildBounds"];
37
39
  readyToMeasure: Props["readyToMeasure"];
40
+ disableEntranceAnimation: Props["disableEntranceAnimation"];
38
41
  };
39
42
 
40
43
  type State = {
@@ -60,6 +63,7 @@ class Zoomable extends React.Component<Props, State> {
60
63
  static defaultProps: DefaultProps = {
61
64
  animateHeight: false,
62
65
  readyToMeasure: true,
66
+ disableEntranceAnimation: false,
63
67
  computeChildBounds: (parentNode) => {
64
68
  const firstChild = parentNode.firstElementChild;
65
69
 
@@ -245,7 +249,7 @@ class Zoomable extends React.Component<Props, State> {
245
249
  render(): React.ReactNode {
246
250
  const {visible, scale, compactHeight, expandedHeight, zoomed} =
247
251
  this.state;
248
- const {animateHeight} = this.props;
252
+ const {animateHeight, disableEntranceAnimation} = this.props;
249
253
 
250
254
  const property = animateHeight
251
255
  ? "opacity transform height"
@@ -261,7 +265,10 @@ class Zoomable extends React.Component<Props, State> {
261
265
  : {};
262
266
 
263
267
  // Do a fancy little slide as we fade the contents in the first time.
264
- const translateOffset = visible ? "" : " translate(0, 8px)";
268
+ const shouldSlide = !disableEntranceAnimation && !visible;
269
+ const translateOffset = shouldSlide ? " translate(0, 8px)" : "";
270
+ const opacity = shouldSlide ? 0 : 1;
271
+
265
272
  const scaleString = (scale ?? 1).toString();
266
273
  const transform = zoomed
267
274
  ? `scale(1, 1) ${translateOffset}`
@@ -273,7 +280,7 @@ class Zoomable extends React.Component<Props, State> {
273
280
  height: zoomed ? expandedHeight : compactHeight,
274
281
  transform: transform,
275
282
  transformOrigin: "0 0",
276
- opacity: visible ? 1 : 0,
283
+ opacity: opacity,
277
284
  WebkitTapHighlightColor: "transparent",
278
285
  ...transitionStyle,
279
286
  } as const;
@@ -1,7 +1,39 @@
1
1
  .perseus-image-widget {
2
+ margin: 0;
3
+
2
4
  .perseus-image-title {
3
5
  text-align: center;
4
6
  }
7
+
8
+ .perseus-image-caption {
9
+ color: rgba(33, 36, 44, 0.7);
10
+ max-width: 420px;
11
+ padding-left: 16px;
12
+ position: relative;
13
+
14
+ // This offsets the paragraph's padding
15
+ // So that the caption sits much closer
16
+ // to the image
17
+ top: -12px;
18
+
19
+ &::before {
20
+ content: "";
21
+ position: absolute;
22
+ top: 0;
23
+ bottom: 0;
24
+ left: 0;
25
+ margin-left: 4px;
26
+ width: 1px;
27
+ background-color: rgba(33, 36, 44, 0.5);
28
+
29
+ border: 1px solid rgba(33, 36, 44, 0.5);
30
+ border-radius: 2px;
31
+ }
32
+ .paragraph {
33
+ font-size: 14px;
34
+ padding-right: 12px;
35
+ }
36
+ }
5
37
  }
6
38
 
7
39
  .perseus-image-editor {
@@ -43,6 +43,7 @@ export const Question1 = (args: StoryArgs): React.ReactElement => {
43
43
  },
44
44
  },
45
45
  } as const;
46
+
46
47
  return (
47
48
  <RendererWithDebugUI
48
49
  question={questionWithArgs}
@@ -51,6 +52,42 @@ export const Question1 = (args: StoryArgs): React.ReactElement => {
51
52
  );
52
53
  };
53
54
 
55
+ export const Question2 = (args: StoryArgs): React.ReactElement => {
56
+ const apiOptions: APIOptions = {
57
+ isMobile: args.isMobile,
58
+ };
59
+ const imageOptions = question.widgets["image 1"].options;
60
+
61
+ const questionWithCaptionAndArgs = {
62
+ ...question,
63
+ widgets: {
64
+ ...question.widgets,
65
+ "image 1": {
66
+ ...question.widgets["image 1"],
67
+ options:
68
+ // `options` is nullable so we have handle it explicitly
69
+ // to avoid spreading `null` and generating
70
+ // hard-to-understand type errors about missing keys
71
+ // in object literal.
72
+ imageOptions != null
73
+ ? {
74
+ ...imageOptions,
75
+ title: args.title,
76
+ caption:
77
+ "There is neither happiness nor unhappiness in this world; there is only the comparison of one state with another. Only a man who has felt ultimate despair is capable of feeling ultimate bliss. It is necessary to have wished for death in order to know how good it is to live.....the sum of all human wisdom will be contained in these two words: Wait and Hope",
78
+ }
79
+ : null,
80
+ },
81
+ },
82
+ } as const;
83
+ return (
84
+ <RendererWithDebugUI
85
+ question={questionWithCaptionAndArgs}
86
+ apiOptions={apiOptions}
87
+ />
88
+ );
89
+ };
90
+
54
91
  export default {
55
92
  title: "Perseus/Widgets/Image",
56
93
  args: {
@@ -30,7 +30,7 @@ exports[`categorizer widget should snapshot on mobile: first mobile render 1`] =
30
30
  style="overflow-x: auto; overflow-y: hidden; margin-top: -10px; margin-bottom: -10px; transform: translate3d(0,0,0); padding: 10px 16px 10px 16px;"
31
31
  >
32
32
  <span
33
- style="display: block; width: 100%; transform: scale(1, 1) translate(0, 8px); transform-origin: 0 0; opacity: 0;"
33
+ style="display: block; width: 100%; transform: scale(1, 1); transform-origin: 0 0; opacity: 1;"
34
34
  >
35
35
  <span
36
36
  class="mock-TeX"
@@ -1144,7 +1144,7 @@ exports[`group widget should snapshot: initial render 1`] = `
1144
1144
  <div
1145
1145
  class="perseus-widget-container widget-nohighlight widget-block"
1146
1146
  >
1147
- <div
1147
+ <figure
1148
1148
  class="perseus-image-widget"
1149
1149
  >
1150
1150
  <div
@@ -1193,7 +1193,7 @@ exports[`group widget should snapshot: initial render 1`] = `
1193
1193
  </div>
1194
1194
  </div>
1195
1195
  </span>
1196
- </div>
1196
+ </figure>
1197
1197
  </div>
1198
1198
  </div>
1199
1199
  </div>
@@ -24,7 +24,7 @@ exports[`image widget - isMobile %b should snapshot: first render 1`] = `
24
24
  <div
25
25
  class="perseus-widget-container widget-nohighlight widget-block"
26
26
  >
27
- <div
27
+ <figure
28
28
  class="perseus-image-widget"
29
29
  >
30
30
  <div
@@ -73,12 +73,10 @@ exports[`image widget - isMobile %b should snapshot: first render 1`] = `
73
73
  </div>
74
74
  </div>
75
75
  </span>
76
- <div
76
+ <figcaption
77
77
  class="perseus-image-caption has-title"
78
78
  >
79
- <div
80
- class="caption_4bstk7"
81
- >
79
+ <div>
82
80
  <div
83
81
  class="perseus-renderer perseus-renderer-responsive"
84
82
  >
@@ -97,8 +95,8 @@ exports[`image widget - isMobile %b should snapshot: first render 1`] = `
97
95
  </div>
98
96
  </div>
99
97
  </div>
100
- </div>
101
- </div>
98
+ </figcaption>
99
+ </figure>
102
100
  </div>
103
101
  </div>
104
102
  </div>
@@ -206,7 +204,7 @@ exports[`image widget - isMobile %b should snapshot: first render 2`] = `
206
204
  <div
207
205
  class="perseus-widget-container widget-nohighlight widget-block"
208
206
  >
209
- <div
207
+ <figure
210
208
  class="perseus-image-widget"
211
209
  >
212
210
  <div
@@ -273,7 +271,7 @@ exports[`image widget - isMobile %b should snapshot: first render 2`] = `
273
271
  </div>
274
272
  </div>
275
273
  </span>
276
- <div
274
+ <figcaption
277
275
  class="perseus-image-caption"
278
276
  >
279
277
  <div
@@ -290,8 +288,8 @@ exports[`image widget - isMobile %b should snapshot: first render 2`] = `
290
288
  </div>
291
289
  </div>
292
290
  </div>
293
- </div>
294
- </div>
291
+ </figcaption>
292
+ </figure>
295
293
  </div>
296
294
  </div>
297
295
  </div>
@@ -42,7 +42,7 @@ exports[`matrix widget should snapshot on mobile: first mobile render 1`] = `
42
42
  style="overflow-x: auto; overflow-y: hidden; margin-top: -10px; margin-bottom: -10px; transform: translate3d(0,0,0); padding: 10px 16px 10px 16px;"
43
43
  >
44
44
  <span
45
- style="display: block; width: 100%; transform: scale(1, 1) translate(0, 8px); transform-origin: 0 0; opacity: 0;"
45
+ style="display: block; width: 100%; transform: scale(1, 1); transform-origin: 0 0; opacity: 1;"
46
46
  >
47
47
  <span
48
48
  class="mock-TeX"
@@ -18,7 +18,7 @@ exports[`number-line widget should snapshot on mobile: first mobile render 1`] =
18
18
  style="overflow-x: auto; overflow-y: hidden; margin-top: -10px; margin-bottom: -10px; transform: translate3d(0,0,0); padding: 10px 16px 10px 16px;"
19
19
  >
20
20
  <span
21
- style="display: block; width: 100%; transform: scale(1, 1) translate(0, 8px); transform-origin: 0 0; opacity: 0;"
21
+ style="display: block; width: 100%; transform: scale(1, 1); transform-origin: 0 0; opacity: 1;"
22
22
  >
23
23
  <span
24
24
  class="mock-TeX"
@@ -1,6 +1,5 @@
1
1
  /* eslint-disable react/sort-comp */
2
2
  import {linterContextDefault} from "@khanacademy/perseus-linter";
3
- import {StyleSheet, css} from "aphrodite";
4
3
  import classNames from "classnames";
5
4
  import * as React from "react";
6
5
  import _ from "underscore";
@@ -8,8 +7,6 @@ import _ from "underscore";
8
7
  import SvgImage from "../components/svg-image";
9
8
  import * as Changeable from "../mixins/changeable";
10
9
  import Renderer from "../renderer";
11
- import {baseUnitPx} from "../styles/constants";
12
- import mediaQueries from "../styles/media-queries";
13
10
 
14
11
  import type {Range, PerseusImageWidgetOptions} from "../perseus-types";
15
12
  import type {
@@ -200,9 +197,8 @@ class ImageWidget extends React.Component<Props> {
200
197
  const minWidth = isImageFullWidth ? null : "0 !important";
201
198
 
202
199
  titleAndCaption = (
203
- <div className={className}>
200
+ <figcaption className={className}>
204
201
  <div
205
- className={css(styles.caption)}
206
202
  style={{
207
203
  // @ts-expect-error - TS2322 - Type 'string | null' is not assignable to type 'MinWidth<string | number> | undefined'.
208
204
  minWidth: minWidth,
@@ -214,16 +210,16 @@ class ImageWidget extends React.Component<Props> {
214
210
  linterContext={this.props.linterContext}
215
211
  />
216
212
  </div>
217
- </div>
213
+ </figcaption>
218
214
  );
219
215
  }
220
216
 
221
217
  return (
222
- <div className="perseus-image-widget">
218
+ <figure className="perseus-image-widget">
223
219
  {image}
224
220
  {alt}
225
221
  {titleAndCaption}
226
- </div>
222
+ </figure>
227
223
  );
228
224
  }
229
225
  let title;
@@ -243,23 +239,23 @@ class ImageWidget extends React.Component<Props> {
243
239
 
244
240
  if (this.props.caption) {
245
241
  caption = (
246
- <div className="perseus-image-caption">
242
+ <figcaption className="perseus-image-caption">
247
243
  <Renderer
248
244
  content={this.props.caption}
249
245
  apiOptions={apiOptions}
250
246
  linterContext={this.props.linterContext}
251
247
  />
252
- </div>
248
+ </figcaption>
253
249
  );
254
250
  }
255
251
 
256
252
  return (
257
- <div className="perseus-image-widget">
253
+ <figure className="perseus-image-widget">
258
254
  {title}
259
255
  {image}
260
256
  {alt}
261
257
  {caption}
262
- </div>
258
+ </figure>
263
259
  );
264
260
  }
265
261
 
@@ -273,25 +269,6 @@ class ImageWidget extends React.Component<Props> {
273
269
  }
274
270
  }
275
271
 
276
- const styles = StyleSheet.create({
277
- caption: {
278
- display: "inline-block",
279
- marginTop: baseUnitPx,
280
- maxWidth: 640,
281
-
282
- [mediaQueries.lgOrSmaller]: {
283
- // TODO(david): This maxWidth is not being used because
284
- // it's overriden by the 512px max-width we have on
285
- // paragraphs.
286
- maxWidth: 540,
287
- },
288
-
289
- [mediaQueries.smOrSmaller]: {
290
- maxWidth: 450,
291
- },
292
- },
293
- });
294
-
295
272
  export default {
296
273
  name: "image",
297
274
  // This widget's accessibility depends on its contents: if the image has