@khanacademy/perseus 1.3.6 → 1.4.1

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 (43) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/es/index.css +3 -2
  3. package/dist/es/index.js +80 -59
  4. package/dist/es/index.js.map +1 -1
  5. package/dist/index.css +3 -2
  6. package/dist/index.js +76 -49
  7. package/dist/index.js.map +1 -1
  8. package/package.json +37 -37
  9. package/src/components/__stories__/tex.stories.jsx +8 -2
  10. package/src/index.js +1 -1
  11. package/src/interactive2/movable-point.jsx +2 -4
  12. package/src/item-renderer.jsx +3 -6
  13. package/src/mixins/__tests__/{widget-prop-blacklist_test.js → widget-prop-denylist_test.js} +1 -1
  14. package/src/mixins/changeable.jsx +2 -2
  15. package/src/mixins/editor-jsonify.jsx +2 -2
  16. package/src/mixins/provide-keypad.jsx +3 -3
  17. package/src/mixins/widget-jsonify-deprecated.jsx +3 -3
  18. package/src/mixins/{widget-prop-blacklist.js → widget-prop-denylist.js} +2 -3
  19. package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +0 -10
  20. package/src/styles/perseus-renderer.less +0 -3
  21. package/src/styles/widgets/radio.less +4 -0
  22. package/src/types.js +1 -7
  23. package/src/util/react-render.js +27 -0
  24. package/src/util.js +1 -1
  25. package/src/widget-container.jsx +1 -1
  26. package/src/widgets/__stories__/on-screen-keypad.stories.jsx +118 -0
  27. package/src/widgets/__tests__/__snapshots__/categorizer_test.jsx.snap +6 -6
  28. package/src/widgets/__tests__/__snapshots__/definition_test.js.snap +0 -2
  29. package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt_test.jsx.snap +3 -6
  30. package/src/widgets/__tests__/__snapshots__/graded-group-set_test.jsx.snap +15 -13
  31. package/src/widgets/__tests__/__snapshots__/graded-group_test.jsx.snap +6 -7
  32. package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +0 -10
  33. package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +0 -104
  34. package/src/widgets/__tests__/__snapshots__/video_test.jsx.snap +2 -4
  35. package/src/widgets/__tests__/graded-group-set_test.jsx +29 -9
  36. package/src/widgets/categorizer.jsx +6 -1
  37. package/src/widgets/cs-program.jsx +1 -1
  38. package/src/widgets/graded-group-set.jsx +43 -39
  39. package/src/widgets/graded-group.jsx +3 -3
  40. package/src/widgets/iframe.jsx +1 -1
  41. package/src/widgets/passage-ref.jsx +2 -2
  42. package/src/widgets/radio/widget.jsx +7 -6
  43. package/src/widgets/radio.jsx +2 -2
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": "1.3.6",
6
+ "version": "1.4.1",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -23,30 +23,30 @@
23
23
  "dependencies": {
24
24
  "@khanacademy/kas": "^0.2.7",
25
25
  "@khanacademy/kmath": "^0.0.8",
26
- "@khanacademy/math-input": "^0.6.4",
27
- "@khanacademy/perseus-linter": "^0.2.3",
28
- "@khanacademy/pure-markdown": "^0.1.3",
26
+ "@khanacademy/math-input": "^0.6.7",
27
+ "@khanacademy/perseus-linter": "^0.2.5",
28
+ "@khanacademy/pure-markdown": "^0.1.5",
29
29
  "@khanacademy/simple-markdown": "^0.8.6",
30
30
  "mathquill": "git+https://git@github.com/Khan/mathquill.git#a9ae54e057c5c1acc8244a5627acbff29901d992"
31
31
  },
32
32
  "devDependencies": {
33
- "@khanacademy/wonder-blocks-button": "^2.11.5",
34
- "@khanacademy/wonder-blocks-clickable": "^2.2.6",
35
- "@khanacademy/wonder-blocks-color": "^1.1.20",
36
- "@khanacademy/wonder-blocks-core": "^4.3.0",
37
- "@khanacademy/wonder-blocks-data": "^7.0.0",
38
- "@khanacademy/wonder-blocks-dropdown": "^2.10.1",
39
- "@khanacademy/wonder-blocks-form": "^2.4.5",
40
- "@khanacademy/wonder-blocks-i18n": "^1.2.3",
41
- "@khanacademy/wonder-blocks-layout": "^1.4.9",
42
- "@khanacademy/wonder-blocks-link": "^3.8.5",
43
- "@khanacademy/wonder-blocks-modal": "^2.3.1",
44
- "@khanacademy/wonder-blocks-popover": "^1.1.24",
45
- "@khanacademy/wonder-blocks-progress-spinner": "^1.1.31",
33
+ "@khanacademy/wonder-blocks-button": "^3.0.14",
34
+ "@khanacademy/wonder-blocks-clickable": "^2.4.5",
35
+ "@khanacademy/wonder-blocks-color": "^1.2.1",
36
+ "@khanacademy/wonder-blocks-core": "^4.7.0",
37
+ "@khanacademy/wonder-blocks-data": "^10.1.0",
38
+ "@khanacademy/wonder-blocks-dropdown": "^2.10.7",
39
+ "@khanacademy/wonder-blocks-form": "^3.1.11",
40
+ "@khanacademy/wonder-blocks-i18n": "^1.2.4",
41
+ "@khanacademy/wonder-blocks-layout": "^1.4.16",
42
+ "@khanacademy/wonder-blocks-link": "^3.9.1",
43
+ "@khanacademy/wonder-blocks-modal": "^3.0.7",
44
+ "@khanacademy/wonder-blocks-popover": "^1.1.30",
45
+ "@khanacademy/wonder-blocks-progress-spinner": "^1.1.40",
46
46
  "@khanacademy/wonder-blocks-spacing": "^3.0.5",
47
- "@khanacademy/wonder-blocks-tooltip": "^1.4.0",
48
- "@khanacademy/wonder-blocks-typography": "^1.1.31",
49
- "@khanacademy/wonder-stuff-core": "^0.1.2",
47
+ "@khanacademy/wonder-blocks-tooltip": "^1.4.6",
48
+ "@khanacademy/wonder-blocks-typography": "^1.1.38",
49
+ "@khanacademy/wonder-stuff-core": "^1.2.1",
50
50
  "aphrodite": "^1.2.5",
51
51
  "classnames": "^1.1.4",
52
52
  "create-react-class": "^15.6.3",
@@ -63,23 +63,23 @@
63
63
  "underscore": "^1.4.4"
64
64
  },
65
65
  "peerDependencies": {
66
- "@khanacademy/wonder-blocks-button": "^2.11.5",
67
- "@khanacademy/wonder-blocks-clickable": "^2.2.6",
68
- "@khanacademy/wonder-blocks-color": "^1.1.20",
69
- "@khanacademy/wonder-blocks-core": "^4.3.0",
70
- "@khanacademy/wonder-blocks-data": "^7.0.0",
71
- "@khanacademy/wonder-blocks-dropdown": "^2.10.1",
72
- "@khanacademy/wonder-blocks-form": "^2.4.5",
73
- "@khanacademy/wonder-blocks-i18n": "^1.2.3",
74
- "@khanacademy/wonder-blocks-layout": "^1.4.9",
75
- "@khanacademy/wonder-blocks-link": "^3.8.5",
76
- "@khanacademy/wonder-blocks-modal": "^2.3.1",
77
- "@khanacademy/wonder-blocks-popover": "^1.1.24",
78
- "@khanacademy/wonder-blocks-progress-spinner": "^1.1.31",
66
+ "@khanacademy/wonder-blocks-button": "^3.0.14",
67
+ "@khanacademy/wonder-blocks-clickable": "^2.4.5",
68
+ "@khanacademy/wonder-blocks-color": "^1.2.1",
69
+ "@khanacademy/wonder-blocks-core": "^4.7.0",
70
+ "@khanacademy/wonder-blocks-data": "^10.1.0",
71
+ "@khanacademy/wonder-blocks-dropdown": "^2.10.7",
72
+ "@khanacademy/wonder-blocks-form": "^3.1.11",
73
+ "@khanacademy/wonder-blocks-i18n": "^1.2.4",
74
+ "@khanacademy/wonder-blocks-layout": "^1.4.16",
75
+ "@khanacademy/wonder-blocks-link": "^3.9.1",
76
+ "@khanacademy/wonder-blocks-modal": "^3.0.7",
77
+ "@khanacademy/wonder-blocks-popover": "^1.1.30",
78
+ "@khanacademy/wonder-blocks-progress-spinner": "^1.1.40",
79
79
  "@khanacademy/wonder-blocks-spacing": "^3.0.5",
80
- "@khanacademy/wonder-blocks-tooltip": "^1.4.0",
81
- "@khanacademy/wonder-blocks-typography": "^1.1.31",
82
- "@khanacademy/wonder-stuff-core": "^0.1.2",
80
+ "@khanacademy/wonder-blocks-tooltip": "^1.4.6",
81
+ "@khanacademy/wonder-blocks-typography": "^1.1.38",
82
+ "@khanacademy/wonder-stuff-core": "^1.2.1",
83
83
  "aphrodite": "^1.2.5",
84
84
  "classnames": "^1.1.4",
85
85
  "create-react-class": "^15.6.3",
@@ -92,4 +92,4 @@
92
92
  "underscore": "^1.4.4"
93
93
  },
94
94
  "keywords": []
95
- }
95
+ }
@@ -3,16 +3,22 @@ import * as React from "react";
3
3
 
4
4
  import TeX from "../tex.jsx";
5
5
 
6
- type StoryArgs = {||};
6
+ type StoryArgs = {|
7
+ equation: string,
8
+ |};
7
9
 
8
10
  type Story = {|
9
11
  title: string,
12
+ args: StoryArgs,
10
13
  |};
11
14
 
12
15
  export default ({
13
16
  title: "Perseus/Components/Tex",
17
+ args: {
18
+ equation: "f(x) = x + 1",
19
+ },
14
20
  }: Story);
15
21
 
16
22
  export const BasicOperation = (args: StoryArgs): React.Node => {
17
- return <TeX setAssetStatus={() => {}} children="f(x) = x + 1" />;
23
+ return <TeX setAssetStatus={() => {}} children={args.equation} />;
18
24
  };
package/src/index.js CHANGED
@@ -87,7 +87,7 @@ export * as SizingUtils from "./util/sizing-utils.js";
87
87
  export * as Changeable from "./mixins/changeable.jsx";
88
88
  export {default as EditorJsonify} from "./mixins/editor-jsonify.jsx";
89
89
  export {default as WidgetJsonifyDeprecated} from "./mixins/widget-jsonify-deprecated.jsx";
90
- export {default as WIDGET_PROP_BLACKLIST} from "./mixins/widget-prop-blacklist.js";
90
+ export {default as WIDGET_PROP_DENYLIST} from "./mixins/widget-prop-denylist.js";
91
91
 
92
92
  /**
93
93
  * Types
@@ -52,12 +52,12 @@
52
52
  */
53
53
  import {point as kpoint, vector as kvector} from "@khanacademy/kmath";
54
54
  import * as React from "react";
55
- import * as ReactDOM from "react-dom";
56
55
  import _ from "underscore";
57
56
 
58
57
  import InlineIcon from "../components/inline-icon.jsx";
59
58
  import {iconTrash} from "../icon-paths.js";
60
59
  import KhanColors from "../util/colors.js";
60
+ import reactRender from "../util/react-render.js";
61
61
  import Tex from "../util/tex.js";
62
62
 
63
63
  import InteractiveUtil from "./interactive-util.js";
@@ -275,9 +275,7 @@ _.extend(MovablePoint.prototype, {
275
275
 
276
276
  const showTrashTooltip = () => {
277
277
  this._showTooltip((container) => {
278
- // TODO(LP-11406): Replace with React Portal
279
- // eslint-disable-next-line no-restricted-syntax
280
- ReactDOM.render(
278
+ reactRender(
281
279
  <span style={{fontSize: "2em"}}>
282
280
  <InlineIcon
283
281
  {...iconTrash}
@@ -12,6 +12,7 @@ import ProvideKeypad from "./mixins/provide-keypad.jsx";
12
12
  import {ApiOptions} from "./perseus-api.jsx";
13
13
  import Renderer from "./renderer.jsx";
14
14
  import Util from "./util.js";
15
+ import reactRender from "./util/react-render.js";
15
16
 
16
17
  import type {KeypadProps} from "./mixins/provide-keypad.jsx";
17
18
  import type {PerseusItem} from "./perseus-types.js";
@@ -177,9 +178,7 @@ class ItemRenderer extends React.Component<Props, State> {
177
178
  // that have completely different places in the DOM, we have to do this
178
179
  // strangeness instead of relying on React's normal render() method.
179
180
  // TODO(alpert): Figure out how to clean this up somehow
180
- // TODO(LP-11406): Replace with React Portal
181
- // eslint-disable-next-line no-restricted-syntax
182
- ReactDOM.render(
181
+ reactRender(
183
182
  // metadata (from item.question, aka PerseusRenderer)
184
183
  // replace (also item.question, aka PerseusRenderer)
185
184
  // savedState (I _think_ this is serializedState on Renderer)
@@ -218,9 +217,7 @@ class ItemRenderer extends React.Component<Props, State> {
218
217
  workArea,
219
218
  );
220
219
 
221
- // TODO(LP-11406): Replace with React Portal
222
- // eslint-disable-next-line no-restricted-syntax
223
- ReactDOM.render(
220
+ reactRender(
224
221
  <HintsRenderer
225
222
  ref={(node) => (this.hintsRenderer = node)}
226
223
  hints={this.props.item.hints}
@@ -1,6 +1,6 @@
1
1
  // @flow
2
2
 
3
- import {removeDenylistProps} from "../widget-prop-blacklist.js";
3
+ import {removeDenylistProps} from "../widget-prop-denylist.js";
4
4
 
5
5
  describe("removeDenylistProps", () => {
6
6
  it("removes props that are in denylist from input", () => {
@@ -15,7 +15,7 @@ import _ from "underscore";
15
15
  import {Errors} from "../logging/log.js";
16
16
  import {PerseusError} from "../perseus-error.js";
17
17
 
18
- import WIDGET_PROP_BLACKLIST from "./widget-prop-blacklist.js";
18
+ import WIDGET_PROP_DENYLIST from "./widget-prop-denylist.js";
19
19
 
20
20
  import type {ChangeFn} from "../types.js";
21
21
 
@@ -37,7 +37,7 @@ const _changeMultiple = function (component, newProps, callback) {
37
37
  // the conceptual state of our component
38
38
  // onChange comes from our parent to allow this modification,
39
39
  // and doesn't conceptually represent the state of our component
40
- const currProps = _.omit(component.props, WIDGET_PROP_BLACKLIST);
40
+ const currProps = _.omit(component.props, WIDGET_PROP_DENYLIST);
41
41
  const nextProps = _.extend(currProps, newProps);
42
42
  component.props.onChange(nextProps, callback);
43
43
  };
@@ -1,12 +1,12 @@
1
1
  // @flow
2
2
  import _ from "underscore";
3
3
 
4
- import WIDGET_PROP_BLACKLIST from "./widget-prop-blacklist.js";
4
+ import WIDGET_PROP_DENYLIST from "./widget-prop-denylist.js";
5
5
 
6
6
  const EditorJsonify = {
7
7
  serialize: function (): $FlowFixMe {
8
8
  // Omit props that get passed to all widgets
9
- return _.omit(this.props, WIDGET_PROP_BLACKLIST);
9
+ return _.omit(this.props, WIDGET_PROP_DENYLIST);
10
10
  },
11
11
  };
12
12
 
@@ -16,6 +16,8 @@ import PropTypes from "prop-types";
16
16
  import * as React from "react";
17
17
  import ReactDOM from "react-dom";
18
18
 
19
+ import reactRender from "../util/react-render.js";
20
+
19
21
  import type {CSSProperties} from "aphrodite";
20
22
 
21
23
  export type KeypadProps = {|
@@ -83,9 +85,7 @@ const ProvideKeypad = {
83
85
  _this._keypadContainer = document.createElement("div");
84
86
  document.body?.appendChild(_this._keypadContainer);
85
87
 
86
- // TODO(LP-11406): Replace with React Portal
87
- // eslint-disable-next-line no-restricted-syntax
88
- ReactDOM.render(
88
+ reactRender(
89
89
  <Keypad
90
90
  onElementMounted={(element) => {
91
91
  // NOTE(kevinb): The reason why this setState works is
@@ -5,18 +5,18 @@
5
5
  */
6
6
  import _ from "underscore";
7
7
 
8
- import WIDGET_PROP_BLACKLIST from "./widget-prop-blacklist.js";
8
+ import WIDGET_PROP_DENYLIST from "./widget-prop-denylist.js";
9
9
 
10
10
  const WidgetJsonifyDeprecated = {
11
11
  getUserInput: function (): $FlowFixMe {
12
12
  // Omit props that get passed to all widgets
13
- return _.omit(this.props, WIDGET_PROP_BLACKLIST);
13
+ return _.omit(this.props, WIDGET_PROP_DENYLIST);
14
14
  },
15
15
 
16
16
  // Static version of `WidgetJsonifyDeprecated.getUserInput`
17
17
  getUserInputFromProps: function (props: $FlowFixMe): $FlowFixMe {
18
18
  // Omit props that get passed to all widgets
19
- return _.omit(props, WIDGET_PROP_BLACKLIST);
19
+ return _.omit(props, WIDGET_PROP_DENYLIST);
20
20
  },
21
21
  };
22
22
 
@@ -8,10 +8,9 @@
8
8
  * automatically serializing schemaless list of attributes causes issues (e.g.
9
9
  * circular JSON structures sometimes).
10
10
  *
11
- * This blacklists things that we know don't need to be serialized.
12
- *
13
- * TODO(TB): Go through and update usage of BLACKLIST to DENYLIST: https://khanacademy.atlassian.net/browse/LP-13149?atlOrigin=eyJpIjoiNTI0Y2UwNzQ0MzA4NDYzOGI1YWRmNjEwY2E5YzZlZWUiLCJwIjoiaiJ9
11
+ * This blocks things that we know don't need to be serialized.
14
12
  */
13
+
15
14
  const denylist = [
16
15
  // standard props "added" by react
17
16
  // (technically the renderer still adds them)
@@ -214,9 +214,7 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
214
214
  <button
215
215
  aria-disabled="false"
216
216
  aria-hidden="true"
217
- aria-label=""
218
217
  class="button_vr44p2-o_O-reset_152ygtm-o_O-link_13xlah4-o_O-inlineStyles_1jvwfht"
219
- tabindex="0"
220
218
  type="button"
221
219
  >
222
220
  <div
@@ -391,9 +389,7 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
391
389
  <button
392
390
  aria-disabled="false"
393
391
  aria-hidden="true"
394
- aria-label=""
395
392
  class="button_vr44p2-o_O-reset_152ygtm-o_O-link_13xlah4-o_O-inlineStyles_1jvwfht"
396
- tabindex="0"
397
393
  type="button"
398
394
  >
399
395
  <div
@@ -568,9 +564,7 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
568
564
  <button
569
565
  aria-disabled="false"
570
566
  aria-hidden="true"
571
- aria-label=""
572
567
  class="button_vr44p2-o_O-reset_152ygtm-o_O-link_13xlah4-o_O-inlineStyles_1jvwfht"
573
- tabindex="0"
574
568
  type="button"
575
569
  >
576
570
  <div
@@ -745,9 +739,7 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
745
739
  <button
746
740
  aria-disabled="false"
747
741
  aria-hidden="true"
748
- aria-label=""
749
742
  class="button_vr44p2-o_O-reset_152ygtm-o_O-link_13xlah4-o_O-inlineStyles_1jvwfht"
750
- tabindex="0"
751
743
  type="button"
752
744
  >
753
745
  <div
@@ -922,9 +914,7 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
922
914
  <button
923
915
  aria-disabled="false"
924
916
  aria-hidden="true"
925
- aria-label=""
926
917
  class="button_vr44p2-o_O-reset_152ygtm-o_O-link_13xlah4-o_O-inlineStyles_1jvwfht"
927
- tabindex="0"
928
918
  type="button"
929
919
  >
930
920
  <div
@@ -149,9 +149,6 @@
149
149
  .paragraph ul:not(.perseus-widget-radio) {
150
150
  .legacy-typography;
151
151
  padding-left: 35px;
152
- }
153
-
154
- .paragraph ul:not(.perseus-widget-radio) li {
155
152
  list-style-type: disc;
156
153
  }
157
154
 
@@ -50,3 +50,7 @@
50
50
  }
51
51
  }
52
52
  }
53
+
54
+ .perseus-widget-radio-fieldset .instructions {
55
+ font-family: "Lato", sans-serif;
56
+ }
package/src/types.js CHANGED
@@ -9,7 +9,7 @@ import * as React from "react";
9
9
  import type {SerializedHighlightSet} from "./components/highlighting/types.js";
10
10
  import type {ILogger} from "./logging/log.js";
11
11
  import type {Item} from "./multi-items/item-types.js";
12
- import type {PerseusWidget, PerseusRadioChoice} from "./perseus-types.js";
12
+ import type {PerseusWidget} from "./perseus-types.js";
13
13
  import type {SizeClass} from "./util/sizing-utils.js";
14
14
  import type {Result} from "@khanacademy/wonder-blocks-data";
15
15
 
@@ -85,12 +85,6 @@ export type ChoiceState = {|
85
85
  readOnly: boolean,
86
86
  |};
87
87
 
88
- export type RadioChoiceWithMetadata = {|
89
- ...PerseusRadioChoice,
90
- originalIndex: number,
91
- correct: boolean,
92
- |};
93
-
94
88
  export type ChangeHandler = (
95
89
  {|
96
90
  hints?: $ReadOnlyArray<Hint>,
@@ -0,0 +1,27 @@
1
+ // @flow
2
+ import {RenderStateRoot} from "@khanacademy/wonder-blocks-core";
3
+ import * as React from "react";
4
+ import ReactDOM from "react-dom";
5
+
6
+ export default function render<ElementType: React$ElementType>(
7
+ element: React.Node,
8
+ container: HTMLElement,
9
+ callback?: () => void,
10
+ ): React$ElementRef<ElementType> {
11
+ // TODO(LP-11406): Replace this, or callers, with React Portal
12
+ // eslint-disable-next-line no-restricted-syntax
13
+ return ReactDOM.render(
14
+ /**
15
+ * `RenderStateRoot` is responsible for tracking whether it's
16
+ * the initial render or subsequent renders. It's used by
17
+ * `useUniqueId` and will be used by `UniqueIDProvider` and
18
+ * `WithSSRPlaceholder` in the future. We're placing it as
19
+ * high up in the render tree as possible to ensure that any
20
+ * components using that hook or those components will function
21
+ * correctly.
22
+ */
23
+ <RenderStateRoot throwIfNested={false}>{element}</RenderStateRoot>,
24
+ container,
25
+ callback,
26
+ );
27
+ }
package/src/util.js CHANGED
@@ -692,7 +692,7 @@ function widgetShouldHighlight(widget: Widget): boolean {
692
692
  }
693
693
  switch (widget.type) {
694
694
  /**
695
- * Highlight bar blacklist
695
+ * Highlight bar denylist
696
696
  */
697
697
  case "measurer":
698
698
  case "protractor":
@@ -119,7 +119,7 @@ class WidgetContainer extends React.Component<Props, State> {
119
119
 
120
120
  // Note: if you add more props here, please consider whether or not
121
121
  // it should be auto-serialized (e.g. used in scoreInput()). See
122
- // widget-jsonify-deprecated.jsx and widget-prop-blacklist.jsx
122
+ // widget-jsonify-deprecated.jsx and widget-prop-denylist.jsx
123
123
 
124
124
  // We default to an empty object for style instead of null
125
125
  // because of a strange bug where the static styles aren't applied
@@ -0,0 +1,118 @@
1
+ // @flow
2
+ import {Keypad} from "@khanacademy/math-input";
3
+ import {View} from "@khanacademy/wonder-blocks-core";
4
+ import {StyleSheet} from "aphrodite";
5
+ import * as React from "react";
6
+
7
+ import {ItemRenderer} from "../../index.js";
8
+ import KeypadContext from "../../keypad-context.js";
9
+ import {expressionItem3} from "../__testdata__/expression_testdata.js";
10
+
11
+ type StoryArgs = {||};
12
+
13
+ type Story = {|
14
+ title: string,
15
+ |};
16
+
17
+ export default ({
18
+ title: "Perseus/Demos/On Screen Keypad",
19
+ }: Story);
20
+
21
+ const Content = (): React.Node => {
22
+ return (
23
+ <KeypadContext.Consumer>
24
+ {({keypadElement, setRenderer, scrollableElement}) => (
25
+ <>
26
+ <ItemRenderer
27
+ ref={setRenderer}
28
+ problemNum={0}
29
+ apiOptions={{
30
+ customKeypad: true,
31
+ onFocusChange: (
32
+ newFocusPath,
33
+ oldFocusPath,
34
+ keypadElement,
35
+ focusedElement,
36
+ ) => {},
37
+ }}
38
+ item={expressionItem3}
39
+ savedState={null}
40
+ />
41
+ <div id="workarea" />
42
+ <div id="hintsarea" />
43
+ </>
44
+ )}
45
+ </KeypadContext.Consumer>
46
+ );
47
+ };
48
+
49
+ const Footer = (): React.Node => {
50
+ return (
51
+ <View
52
+ style={[
53
+ styles.keypadContainer,
54
+ {
55
+ // NOTE: in webapp we normally get this from problemProgress
56
+ // from the exercise state store.
57
+ height: 240,
58
+ },
59
+ ]}
60
+ >
61
+ <KeypadContext.Consumer>
62
+ {({keypadElement, setKeypadElement, renderer}) => (
63
+ <Keypad
64
+ onElementMounted={setKeypadElement}
65
+ onDismiss={() => renderer && renderer.blur()}
66
+ style={styles.keypad}
67
+ />
68
+ )}
69
+ </KeypadContext.Consumer>
70
+ </View>
71
+ );
72
+ };
73
+
74
+ const Demo = () => {
75
+ const [keypadElement, setKeypadElement] = React.useState(null);
76
+ const [renderer, setRenderer] = React.useState(null);
77
+ const [scrollableElement, setScrollableElement] = React.useState(
78
+ document.body,
79
+ );
80
+
81
+ return (
82
+ <KeypadContext.Provider
83
+ value={{
84
+ setKeypadElement,
85
+ keypadElement,
86
+ setRenderer,
87
+ renderer,
88
+ setScrollableElement,
89
+ scrollableElement,
90
+ }}
91
+ >
92
+ <Content />
93
+ <Footer />
94
+ </KeypadContext.Provider>
95
+ );
96
+ };
97
+
98
+ export const CustomKeypad = (args: StoryArgs): React.Node => <Demo />;
99
+
100
+ const styles = StyleSheet.create({
101
+ keypad: {
102
+ position: "absolute",
103
+ // The keypad itself needs to respond to events even though
104
+ // we've set its container to pointer-events: none;
105
+ pointerEvents: "all",
106
+ },
107
+ keypadContainer: {
108
+ position: "absolute",
109
+ width: "100%",
110
+ bottom: 0,
111
+ // Hide the overflow of this container so when the keypad is
112
+ // dismissed, it doesn't overlap the toolbar.
113
+ overflow: "hidden",
114
+ // Prevent container from swallowing events that the exercise
115
+ // below it needs to respond to.
116
+ pointerEvents: "none",
117
+ },
118
+ });
@@ -62,9 +62,9 @@ exports[`categorizer widget should snapshot on mobile: first mobile render 1`] =
62
62
  >
63
63
  <thead>
64
64
  <tr>
65
- <th>
66
-  
67
- </th>
65
+ <td
66
+ class="emptyHeaderCell_1mttrgd"
67
+ />
68
68
  <th
69
69
  class="header_whrfk4"
70
70
  >
@@ -509,9 +509,9 @@ exports[`categorizer widget should snapshot: first render 1`] = `
509
509
  >
510
510
  <thead>
511
511
  <tr>
512
- <th>
513
-  
514
- </th>
512
+ <td
513
+ class="emptyHeaderCell_1mttrgd"
514
+ />
515
515
  <th
516
516
  class="header_whrfk4"
517
517
  >
@@ -39,7 +39,6 @@ exports[`Definition widget should have a default snapshot: first render 1`] = `
39
39
  aria-disabled="false"
40
40
  class="button_vr44p2-o_O-shared_lwskrm-o_O-default_1hl5pu8"
41
41
  role="button"
42
- tabindex="0"
43
42
  type="button"
44
43
  >
45
44
  <span
@@ -97,7 +96,6 @@ exports[`Definition widget should have an open state snapshot: open state 1`] =
97
96
  aria-disabled="false"
98
97
  class="button_vr44p2-o_O-shared_lwskrm-o_O-default_1hl5pu8"
99
98
  role="button"
100
- tabindex="0"
101
99
  type="button"
102
100
  >
103
101
  <span
@@ -250,7 +250,6 @@ exports[`graded-group-set should render all graded groups 1`] = `
250
250
  aria-disabled="false"
251
251
  class="button_vr44p2-o_O-shared_lwskrm-o_O-default_1w5v67"
252
252
  role="button"
253
- tabindex="0"
254
253
  type="button"
255
254
  >
256
255
  <span
@@ -260,7 +259,7 @@ exports[`graded-group-set should render all graded groups 1`] = `
260
259
  </span>
261
260
  </button>
262
261
  <button
263
- class="showHintLink_112g8fo"
262
+ class="showHintLink_oak3yy"
264
263
  tabindex="0"
265
264
  >
266
265
  Explain
@@ -487,7 +486,6 @@ exports[`graded-group-set should render all graded groups 1`] = `
487
486
  aria-disabled="false"
488
487
  class="button_vr44p2-o_O-shared_lwskrm-o_O-default_1w5v67"
489
488
  role="button"
490
- tabindex="0"
491
489
  type="button"
492
490
  >
493
491
  <span
@@ -497,7 +495,7 @@ exports[`graded-group-set should render all graded groups 1`] = `
497
495
  </span>
498
496
  </button>
499
497
  <button
500
- class="showHintLink_112g8fo"
498
+ class="showHintLink_oak3yy"
501
499
  tabindex="0"
502
500
  >
503
501
  Explain
@@ -724,7 +722,6 @@ exports[`graded-group-set should render all graded groups 1`] = `
724
722
  aria-disabled="false"
725
723
  class="button_vr44p2-o_O-shared_lwskrm-o_O-default_1w5v67"
726
724
  role="button"
727
- tabindex="0"
728
725
  type="button"
729
726
  >
730
727
  <span
@@ -734,7 +731,7 @@ exports[`graded-group-set should render all graded groups 1`] = `
734
731
  </span>
735
732
  </button>
736
733
  <button
737
- class="showHintLink_112g8fo"
734
+ class="showHintLink_oak3yy"
738
735
  tabindex="0"
739
736
  >
740
737
  Explain