@khanacademy/perseus-editor 4.1.5 → 4.1.6

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 (175) hide show
  1. package/dist/es/index.js +1 -1
  2. package/dist/index.js +1 -1
  3. package/package.json +9 -6
  4. package/.babelrc.js +0 -8
  5. package/.eslintrc.js +0 -12
  6. package/CHANGELOG.md +0 -1155
  7. package/src/README.md +0 -7
  8. package/src/__stories__/editor-page.stories.tsx +0 -65
  9. package/src/__stories__/editor.stories.tsx +0 -134
  10. package/src/__stories__/item-editor.stories.tsx +0 -41
  11. package/src/__stories__/tex-error-view.stories.tsx +0 -26
  12. package/src/__testdata__/input-number.testdata.ts +0 -27
  13. package/src/__tests__/editor.test.tsx +0 -142
  14. package/src/__tests__/hint-editor.test.tsx +0 -83
  15. package/src/__tests__/i18n.test.ts +0 -501
  16. package/src/__tests__/integration.test.ts +0 -5
  17. package/src/__tests__/item-extras-editor.test.tsx +0 -85
  18. package/src/__tests__/traversal.test.ts +0 -377
  19. package/src/all-editors.test.ts +0 -16
  20. package/src/all-editors.ts +0 -83
  21. package/src/all-widgets.ts +0 -7
  22. package/src/article-editor.tsx +0 -438
  23. package/src/components/__stories__/blur-input.stories.tsx +0 -22
  24. package/src/components/__stories__/section-control-button.stories.tsx +0 -26
  25. package/src/components/__tests__/blur-input.test.tsx +0 -67
  26. package/src/components/blur-input.tsx +0 -69
  27. package/src/components/checkbox.tsx +0 -194
  28. package/src/components/device-framer.tsx +0 -90
  29. package/src/components/drag-target.tsx +0 -87
  30. package/src/components/dropdown-option.tsx +0 -311
  31. package/src/components/form-wrapped-text-field.tsx +0 -245
  32. package/src/components/graph-points-count-selector.tsx +0 -31
  33. package/src/components/graph-settings.tsx +0 -627
  34. package/src/components/graph-type-selector.tsx +0 -31
  35. package/src/components/hover-behavior.ts +0 -177
  36. package/src/components/json-editor.tsx +0 -121
  37. package/src/components/link.ts +0 -173
  38. package/src/components/section-control-button.tsx +0 -53
  39. package/src/components/segment-count-selector.tsx +0 -27
  40. package/src/components/simple-button.tsx +0 -85
  41. package/src/components/sortable.tsx +0 -187
  42. package/src/components/util.ts +0 -38
  43. package/src/components/viewport-resizer.tsx +0 -72
  44. package/src/components/widget-editor.tsx +0 -222
  45. package/src/components/widget-select.tsx +0 -48
  46. package/src/diffs/__stories__/perseus-diff-wrapper.tsx +0 -11
  47. package/src/diffs/__stories__/structured-item-diff.stories.tsx +0 -855
  48. package/src/diffs/__stories__/tags-diff.stories.tsx +0 -36
  49. package/src/diffs/__stories__/text-diff.stories.tsx +0 -35
  50. package/src/diffs/__tests__/split-diff.test.ts +0 -33
  51. package/src/diffs/__tests__/string-array-diff.test.ts +0 -73
  52. package/src/diffs/__tests__/structured-item-diff.test.ts +0 -433
  53. package/src/diffs/__tests__/widget-diff-performer.test.ts +0 -43
  54. package/src/diffs/article-diff.tsx +0 -69
  55. package/src/diffs/item-diff.tsx +0 -76
  56. package/src/diffs/renderer-diff.tsx +0 -137
  57. package/src/diffs/split-diff.ts +0 -31
  58. package/src/diffs/string-array-diff.ts +0 -74
  59. package/src/diffs/structured-item-diff.tsx +0 -275
  60. package/src/diffs/tags-diff.tsx +0 -49
  61. package/src/diffs/text-diff.tsx +0 -193
  62. package/src/diffs/widget-diff-performer.ts +0 -69
  63. package/src/diffs/widget-diff.tsx +0 -290
  64. package/src/editor-page.tsx +0 -325
  65. package/src/editor.tsx +0 -1099
  66. package/src/hint-editor.tsx +0 -469
  67. package/src/i18n.ts +0 -140
  68. package/src/iframe-content-renderer.tsx +0 -188
  69. package/src/index.ts +0 -30
  70. package/src/item-editor.tsx +0 -158
  71. package/src/item-extras-editor.tsx +0 -230
  72. package/src/katex-mhchem/index.js +0 -73
  73. package/src/katex-mhchem/parser.js +0 -2659
  74. package/src/multirenderer-editor.tsx +0 -1061
  75. package/src/stateful-article-editor.tsx +0 -173
  76. package/src/stateful-editor-page.tsx +0 -88
  77. package/src/styles/perseus-editor.less +0 -1321
  78. package/src/testing-widgets.ts +0 -11
  79. package/src/tex-error-view.tsx +0 -90
  80. package/src/util/points.test.ts +0 -24
  81. package/src/util/points.ts +0 -13
  82. package/src/util/register-all-widgets-and-editors-for-testing.test.ts +0 -17
  83. package/src/util/register-all-widgets-and-editors-for-testing.ts +0 -17
  84. package/src/version.ts +0 -10
  85. package/src/widgets/__stories__/categorizer-editor.stories.tsx +0 -24
  86. package/src/widgets/__stories__/definition-editor.stories.tsx +0 -18
  87. package/src/widgets/__stories__/dropdown-editor.stories.tsx +0 -18
  88. package/src/widgets/__stories__/explanation-editor.stories.tsx +0 -18
  89. package/src/widgets/__stories__/expression-editor.stories.tsx +0 -122
  90. package/src/widgets/__stories__/image-editor.stories.tsx +0 -72
  91. package/src/widgets/__stories__/input-number-editor.stories.tsx +0 -18
  92. package/src/widgets/__stories__/interaction-editor.stories.tsx +0 -37
  93. package/src/widgets/__stories__/label-image-editor.stories.tsx +0 -99
  94. package/src/widgets/__stories__/matcher-editor.stories.tsx +0 -18
  95. package/src/widgets/__stories__/number-line-editor.stories.tsx +0 -18
  96. package/src/widgets/__stories__/numeric-input-editor.stories.tsx +0 -18
  97. package/src/widgets/__stories__/radio-editor.stories.tsx +0 -116
  98. package/src/widgets/__stories__/sorter-editor.stories.tsx +0 -18
  99. package/src/widgets/__tests__/categorizer-editor.test.tsx +0 -51
  100. package/src/widgets/__tests__/definition-editor.test.tsx +0 -41
  101. package/src/widgets/__tests__/dropdown-editor.test.tsx +0 -57
  102. package/src/widgets/__tests__/explanation-editor.test.tsx +0 -59
  103. package/src/widgets/__tests__/expression-editor.test.tsx +0 -403
  104. package/src/widgets/__tests__/input-number-editor.test.tsx +0 -128
  105. package/src/widgets/__tests__/matcher-editor.test.tsx +0 -47
  106. package/src/widgets/__tests__/number-line-editor.test.tsx +0 -144
  107. package/src/widgets/__tests__/numeric-input-editor.test.tsx +0 -177
  108. package/src/widgets/__tests__/radio-editor.test.tsx +0 -118
  109. package/src/widgets/__tests__/sorter-editor.test.tsx +0 -55
  110. package/src/widgets/categorizer-editor.tsx +0 -104
  111. package/src/widgets/cs-program-editor.tsx +0 -256
  112. package/src/widgets/definition-editor.tsx +0 -77
  113. package/src/widgets/deprecated-standin-editor.tsx +0 -29
  114. package/src/widgets/dropdown-editor.tsx +0 -211
  115. package/src/widgets/example-graphie-widget-editor.tsx +0 -70
  116. package/src/widgets/example-graphie-widget.tsx +0 -172
  117. package/src/widgets/example-widget-editor.tsx +0 -62
  118. package/src/widgets/example-widget.tsx +0 -154
  119. package/src/widgets/explanation-editor.tsx +0 -92
  120. package/src/widgets/expression-editor.tsx +0 -557
  121. package/src/widgets/graded-group-editor.tsx +0 -161
  122. package/src/widgets/graded-group-set-editor.tsx +0 -104
  123. package/src/widgets/grapher-editor.tsx +0 -155
  124. package/src/widgets/group-editor.tsx +0 -83
  125. package/src/widgets/iframe-editor.tsx +0 -202
  126. package/src/widgets/image-editor.tsx +0 -367
  127. package/src/widgets/input-number-editor.tsx +0 -288
  128. package/src/widgets/interaction-editor/constraint-editor.tsx +0 -134
  129. package/src/widgets/interaction-editor/function-editor.tsx +0 -108
  130. package/src/widgets/interaction-editor/index.ts +0 -3
  131. package/src/widgets/interaction-editor/interaction-editor.tsx +0 -749
  132. package/src/widgets/interaction-editor/label-editor.tsx +0 -87
  133. package/src/widgets/interaction-editor/line-editor.tsx +0 -125
  134. package/src/widgets/interaction-editor/mathquill-input.tsx +0 -81
  135. package/src/widgets/interaction-editor/movable-line-editor.tsx +0 -121
  136. package/src/widgets/interaction-editor/movable-point-editor.tsx +0 -89
  137. package/src/widgets/interaction-editor/parametric-editor.tsx +0 -117
  138. package/src/widgets/interaction-editor/point-editor.tsx +0 -72
  139. package/src/widgets/interaction-editor/rectangle-editor.tsx +0 -96
  140. package/src/widgets/interactive-graph-editor.tsx +0 -571
  141. package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +0 -82
  142. package/src/widgets/label-image/__stories__/behavior.stories.tsx +0 -31
  143. package/src/widgets/label-image/__stories__/marker.stories.tsx +0 -75
  144. package/src/widgets/label-image/__stories__/question-markers.stories.tsx +0 -107
  145. package/src/widgets/label-image/__stories__/select-image.stories.tsx +0 -59
  146. package/src/widgets/label-image/answer-choices.tsx +0 -231
  147. package/src/widgets/label-image/behavior.tsx +0 -122
  148. package/src/widgets/label-image/marker.tsx +0 -290
  149. package/src/widgets/label-image/question-markers.tsx +0 -176
  150. package/src/widgets/label-image/select-image.tsx +0 -81
  151. package/src/widgets/label-image-editor.tsx +0 -252
  152. package/src/widgets/lights-puzzle-editor.tsx +0 -278
  153. package/src/widgets/matcher-editor.tsx +0 -149
  154. package/src/widgets/matrix-editor.tsx +0 -156
  155. package/src/widgets/measurer-editor.tsx +0 -253
  156. package/src/widgets/molecule-editor.tsx +0 -64
  157. package/src/widgets/number-line-editor.tsx +0 -512
  158. package/src/widgets/numeric-input-editor.tsx +0 -536
  159. package/src/widgets/orderer-editor.tsx +0 -172
  160. package/src/widgets/passage-editor.tsx +0 -117
  161. package/src/widgets/passage-ref-editor.tsx +0 -80
  162. package/src/widgets/passage-ref-target-editor.tsx +0 -49
  163. package/src/widgets/plotter-editor.tsx +0 -542
  164. package/src/widgets/radio/editor.tsx +0 -398
  165. package/src/widgets/reaction-diagram-editor.tsx +0 -131
  166. package/src/widgets/sequence-editor.tsx +0 -205
  167. package/src/widgets/simple-markdown-tester-editor.tsx +0 -86
  168. package/src/widgets/simple-markdown-tester.tsx +0 -92
  169. package/src/widgets/simulator-editor.tsx +0 -146
  170. package/src/widgets/sorter-editor.tsx +0 -103
  171. package/src/widgets/table-editor.tsx +0 -164
  172. package/src/widgets/unit-editor.tsx +0 -299
  173. package/src/widgets/video-editor.tsx +0 -73
  174. package/tsconfig-build.json +0 -22
  175. package/tsconfig-build.tsbuildinfo +0 -1
@@ -1,194 +0,0 @@
1
- /* eslint-disable @khanacademy/ts-no-error-suppressions */
2
- /**
3
- * Reusable checkbox component.
4
- **/
5
- import {globalStyles} from "@khanacademy/perseus";
6
- import {css, StyleSheet} from "aphrodite";
7
- import * as React from "react";
8
-
9
- import HoverBehavior from "./hover-behavior";
10
-
11
- const {borderRadius, colors} = globalStyles;
12
-
13
- export type CheckboxProps = {
14
- // Whether or not the checkbox is checked. The parent responsible for
15
- // responding to onChange events and updating this prop appropriately.
16
- checked: boolean;
17
- // When disable the checkbox will appear faded out and will not
18
- // respond to events and cannot be tabbed to.
19
- disabled?: boolean;
20
- // Appear as if disabled but allow it to respond to events and be
21
- // tabbed to.
22
- // This allows us to hook alternate responses into clicking on a
23
- // disabled checkbox.
24
- appearDisabled?: boolean;
25
- // Called when the value has changed.
26
- onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
27
- tabIndex?: number | string;
28
- style?: {
29
- marginTop: number;
30
- };
31
- // For e2e test
32
- dataTestId?: string;
33
- // Unique identifier for this checkbox.
34
- id?: string;
35
- };
36
-
37
- // NOTE(jangmi): Checkbox may have `onChange` depends on circumstances but lack
38
- // of onChange causes Dev only alert so we provide the no op func as default
39
- // to prevent that.
40
- const onChangeCheckboxNoop = (event: React.ChangeEvent<HTMLInputElement>) => {};
41
-
42
- export default class Checkbox extends React.Component<CheckboxProps> {
43
- static defaultProps: {
44
- checked: boolean;
45
- onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
46
- } = {
47
- checked: false,
48
- onChange: onChangeCheckboxNoop,
49
- };
50
-
51
- render(): React.ReactNode {
52
- const {
53
- checked,
54
- disabled,
55
- appearDisabled,
56
- onChange,
57
- tabIndex,
58
- style,
59
- dataTestId,
60
- id,
61
- } = this.props;
62
-
63
- const checkedColor = colors.gray41;
64
-
65
- return (
66
- <HoverBehavior>
67
- {({focused}, handlers) => (
68
- <div
69
- {...handlers}
70
- className={css(
71
- styles.container,
72
- focused && styles.focused,
73
- )}
74
- style={style}
75
- data-test-id={dataTestId}
76
- data-checked={checked}
77
- >
78
- <svg
79
- className={css(
80
- styles.svg,
81
- (disabled || appearDisabled) && styles.disabled,
82
- )}
83
- width={sizeWithPadding}
84
- height={sizeWithPadding}
85
- /* pad the viewbox to avoid bad clipping when zooming */
86
- viewBox={`-${padding} -${padding}
87
- ${sizeWithPadding} ${sizeWithPadding}`}
88
- >
89
- <g fill="none" fillRule="evenodd">
90
- {checked && (
91
- <g>
92
- <rect
93
- fill={checkedColor}
94
- width={size}
95
- height={size}
96
- x="0"
97
- y="0"
98
- rx={borderRadius}
99
- />
100
- <path
101
- fill={colors.white}
102
- stroke={colors.white}
103
- d="M4.98 7.41a0.58.58 0 1 0-0.81.81L6.47
104
- 10.53c0.23.23.59.23.81
105
- 0l4.55-4.55a0.58.58 0 0
106
- 0-0.81-0.81L6.88 9.31 4.98 7.41z"
107
- />
108
- </g>
109
- )}
110
- {!checked && (
111
- <rect
112
- fill={colors.white}
113
- stroke={colors.gray68}
114
- width={size - 2 * padding}
115
- height={size - 2 * padding}
116
- x={padding}
117
- y={padding}
118
- rx="4"
119
- strokeWidth={borderWidth}
120
- />
121
- )}
122
- </g>
123
- </svg>
124
- <input
125
- type="checkbox"
126
- id={id}
127
- checked={checked}
128
- className={css(
129
- styles.checkbox,
130
- disabled && styles.defaultCursor,
131
- )}
132
- disabled={disabled}
133
- onChange={onChange}
134
- // @ts-expect-error - TS2322 - Type 'string | number | undefined' is not assignable to type 'number | undefined'.
135
- tabIndex={tabIndex}
136
- />
137
- </div>
138
- )}
139
- </HoverBehavior>
140
- );
141
- }
142
- }
143
-
144
- const size = 16;
145
- const padding = 0.5;
146
- const borderWidth = 1;
147
- const sizeWithPadding = size + 2 * padding;
148
-
149
- const styles = StyleSheet.create({
150
- container: {
151
- position: "relative",
152
- display: "inline-block",
153
- verticalAlign: "middle",
154
- lineHeight: 0,
155
- borderRadius: borderRadius,
156
- width: sizeWithPadding,
157
- height: sizeWithPadding,
158
- flexShrink: 0,
159
- },
160
- focused: {
161
- "::before": {
162
- content: '""',
163
- position: "absolute",
164
- top: -2,
165
- right: -2,
166
- bottom: -2,
167
- left: -2,
168
- borderRadius: borderRadius + 2,
169
- backgroundColor: "lightblue", // TODO(kevinb) get real focus ring
170
- },
171
- },
172
- svg: {
173
- position: "absolute",
174
- left: 0,
175
- top: 0,
176
- },
177
- checkbox: {
178
- appearance: "none",
179
- opacity: 0,
180
- position: "absolute",
181
- top: padding,
182
- width: size,
183
- height: size,
184
- margin: 0,
185
- outline: "none",
186
- cursor: "pointer",
187
- },
188
- disabled: {
189
- opacity: 0.5,
190
- },
191
- defaultCursor: {
192
- cursor: "default",
193
- },
194
- });
@@ -1,90 +0,0 @@
1
- /**
2
- * A component that displays its contents inside a device frame.
3
- */
4
-
5
- import {constants} from "@khanacademy/perseus";
6
- import PropTypes from "prop-types";
7
- import * as React from "react";
8
-
9
- const SCREEN_SIZES = {
10
- phone: {
11
- width: 320,
12
- height: 480,
13
- framedWidth: 320,
14
- },
15
- tablet: {
16
- width: 750,
17
- height: 920,
18
- framedWidth: 525,
19
- },
20
- desktop: {
21
- width: 688,
22
- height: 600,
23
- framedWidth: 688,
24
- },
25
- } as const;
26
-
27
- type Props = any;
28
-
29
- class DeviceFramer extends React.Component<Props> {
30
- static propTypes = {
31
- children: PropTypes.element.isRequired,
32
- deviceType: PropTypes.oneOf([
33
- constants.devices.PHONE,
34
- constants.devices.TABLET,
35
- constants.devices.DESKTOP,
36
- ]).isRequired,
37
- // TODO(kevinb) rename to variableHeight
38
- nochrome: PropTypes.bool,
39
- };
40
-
41
- render(): React.ReactNode {
42
- const deviceType = this.props.deviceType;
43
-
44
- if (this.props.nochrome) {
45
- // Render content inside a variable height iframe. Used on the
46
- // "edit" table of the content editor. In this mode, PerseusFrame
47
- // will draw the border and reserve space on the right for
48
- // lint indicators.
49
- return (
50
- <div>
51
- <div
52
- key="screen"
53
- style={{
54
- width:
55
- SCREEN_SIZES[deviceType].framedWidth +
56
- 2 * constants.perseusFrameBorderWidth +
57
- constants.lintGutterWidth,
58
- }}
59
- >
60
- <div>{this.props.children}</div>
61
- </div>
62
- </div>
63
- );
64
- }
65
- const scale =
66
- SCREEN_SIZES[deviceType].framedWidth /
67
- SCREEN_SIZES[deviceType].width;
68
-
69
- // In this mode we draw our own border and don't reserve
70
- // space for a lint gutter.
71
- const screenStyle = {
72
- backgroundColor: "white",
73
- color: "black",
74
- textAlign: "left",
75
- width: SCREEN_SIZES[deviceType].width,
76
- height: SCREEN_SIZES[deviceType].height,
77
- border: "solid 1px #CCC",
78
- margin: 8,
79
- zoom: scale,
80
- } as const;
81
-
82
- return (
83
- <div key="screen" className="screen" style={screenStyle}>
84
- {this.props.children}
85
- </div>
86
- );
87
- }
88
- }
89
-
90
- export default DeviceFramer;
@@ -1,87 +0,0 @@
1
- /* This component makes its children a drag target. Example:
2
- *
3
- * <DragTarget onDrop={this.handleDrop}>Drag to me</DragTarget>
4
- *
5
- * ...
6
- *
7
- * handleDrop: function(e) {
8
- * this.addImages(e.nativeEvent.dataTransfer.files);
9
- * }
10
- *
11
- * Now "Drag to me" will be a drag target - when something is dragged over it,
12
- * the element will become partially transparent as a visual indicator that
13
- * it's a target.
14
- */
15
- // TODO(joel) - indicate before the hover is over the target that it's possible
16
- // to drag into the target. This would (I think) require a high level handler -
17
- // like on Perseus itself, waiting for onDragEnter, then passing down the
18
- // event. Sounds like a pain. Possible workaround - create a div covering the
19
- // entire page...
20
- //
21
- // Other extensions:
22
- // * custom styles for global drag and dragOver
23
- // * only respond to certain types of drags (only images for instance)!
24
-
25
- import createReactClass from "create-react-class";
26
- import PropTypes from "prop-types";
27
- import * as React from "react";
28
-
29
- const DragTarget = createReactClass({
30
- propTypes: {
31
- // All props not listed here are forwarded to the root element without
32
- // modification.
33
- onDrop: PropTypes.func.isRequired,
34
- component: PropTypes.any, // component type
35
- shouldDragHighlight: PropTypes.func,
36
- style: PropTypes.any,
37
- },
38
- getDefaultProps: function () {
39
- return {
40
- component: "div",
41
- shouldDragHighlight: () => true,
42
- };
43
- },
44
- getInitialState: function () {
45
- return {dragHover: false};
46
- },
47
- handleDrop: function (e) {
48
- e.stopPropagation();
49
- e.preventDefault();
50
- this.setState({dragHover: false});
51
- this.props.onDrop(e);
52
- },
53
- handleDragEnd: function () {
54
- this.setState({dragHover: false});
55
- },
56
- handleDragOver: function (e) {
57
- e.preventDefault();
58
- },
59
- handleDragLeave: function () {
60
- this.setState({dragHover: false});
61
- },
62
- handleDragEnter: function (e) {
63
- this.setState({dragHover: this.props.shouldDragHighlight(e)});
64
- },
65
- render: function () {
66
- const opacity = this.state.dragHover ? {opacity: 0.3} : {};
67
- const Component = this.props.component;
68
-
69
- const forwardProps = Object.assign({}, this.props);
70
- delete forwardProps.component;
71
- delete forwardProps.shouldDragHighlight;
72
-
73
- return (
74
- <Component
75
- {...forwardProps}
76
- style={Object.assign({}, this.props.style, opacity)}
77
- onDrop={this.handleDrop}
78
- onDragEnd={this.handleDragEnd}
79
- onDragOver={this.handleDragOver}
80
- onDragEnter={this.handleDragEnter}
81
- onDragLeave={this.handleDragLeave}
82
- />
83
- );
84
- },
85
- });
86
-
87
- export default DragTarget;
@@ -1,311 +0,0 @@
1
- /* eslint-disable @khanacademy/ts-no-error-suppressions */
2
- /**
3
- * The Option is an item to be used in the Select components and the
4
- * derivations such as MultiSelect, CheckList, and CheckBoxList. Also includes
5
- * OptionGroup which is an internal component used in the dropdowns to house
6
- * a list of options.
7
- **/
8
- import {components, globalStyles} from "@khanacademy/perseus";
9
- import {css, StyleSheet} from "aphrodite";
10
- import * as React from "react";
11
- import ReactDOM from "react-dom";
12
-
13
- import {focusWithChromeStickyFocusBugWorkaround} from "./util";
14
-
15
- export type OptionRenderer = (
16
- children: React.ReactElement<any> | null | undefined,
17
- value: string,
18
- selected: boolean,
19
- disabled?: boolean,
20
- ) => React.ReactElement<any>;
21
-
22
- const {Icon} = components;
23
- const {colors} = globalStyles;
24
-
25
- const findAndFocusElement = (component?: Element | null) => {
26
- const DOMNode: Element | Text | null | undefined =
27
- ReactDOM.findDOMNode(component);
28
- const button = DOMNode as HTMLInputElement;
29
- // @ts-expect-error - TS2774 - This condition will always return true since this function is always defined. Did you mean to call it instead?
30
- if (button.focus) {
31
- focusWithChromeStickyFocusBugWorkaround(button);
32
- }
33
- };
34
-
35
- type Props = {
36
- // The value to use when the option is selected
37
- value: string;
38
- // The display of the option
39
- children?: React.ReactNode;
40
- // Is the current option selected?
41
- selected?: boolean;
42
- // Is the current option disabled?
43
- disabled?: boolean;
44
- // An event when an option is clicked
45
- onClick?: () => void;
46
- // An optional rendering function to render the details of the option
47
- optionRenderer?: OptionRenderer;
48
- // An optional function to call when the dropdown should close
49
- // Only relevant if the Option is inside of a dropdown menu
50
- onDropdownClose?: () => void;
51
- // An optional value to override the focus styling of an option.
52
- // Use caution when using this - keyboard users need to know what they're
53
- // focused on in order to interact with the product!
54
- hideFocusState?: boolean;
55
- // An optional test id that can be used to identify this Option in automated tests.
56
- testId?: string;
57
- // Text to provide for assistive tech users
58
- ariaLabel?: string;
59
- };
60
-
61
- const check = `M10,3.8C10,4,9.9,4.2,9.8,4.3L5.1,8.9L4.3,9.8C4.2,9.9,4,10,3.8,10
62
- S3.5,9.9,3.4,9.8L2.5,8.9L0.2,6.6C0.1,6.5,0,6.3,0,6.2s0.1-0.3,0.2-0.4
63
- l0.9-0.9c0.1-0.1,0.3-0.2,0.4-0.2s0.3,0.1,0.4,0.2l1.9,1.9l4.2-4.2c0.1
64
- -0.1,0.3-0.2,0.4-0.2c0.2,0,0.3,0.1,0.4,0.2l0.9,0.9C9.9,3.5,10,3.7,
65
- 10,3.8z`;
66
-
67
- export const optionHeight = 30;
68
-
69
- class Option extends React.Component<Props> {
70
- // @ts-expect-error - TS2564 - Property 'node' has no initializer and is not definitely assigned in the constructor.
71
- node: HTMLDivElement;
72
-
73
- handleKeyDown(event: any): void {
74
- const {onDropdownClose} = this.props;
75
- const pressedKey = event.key;
76
- const focusedElement = event.target;
77
-
78
- if (pressedKey === "ArrowDown" && focusedElement.nextSibling) {
79
- event.preventDefault();
80
- focusedElement.nextSibling.focus();
81
- }
82
- if (pressedKey === "ArrowUp" && focusedElement.previousSibling) {
83
- event.preventDefault();
84
- focusedElement.previousSibling.focus();
85
- }
86
- if (
87
- pressedKey === "ArrowUp" &&
88
- !focusedElement.previousSibling &&
89
- onDropdownClose
90
- ) {
91
- event.preventDefault();
92
- onDropdownClose();
93
- }
94
- if (
95
- (pressedKey === "Escape" || pressedKey === "Tab") &&
96
- onDropdownClose
97
- ) {
98
- onDropdownClose();
99
- }
100
- }
101
-
102
- render(): React.ReactNode {
103
- const {
104
- selected,
105
- value,
106
- onClick,
107
- children,
108
- optionRenderer,
109
- disabled,
110
- hideFocusState,
111
- testId,
112
- ariaLabel,
113
- } = this.props;
114
-
115
- return (
116
- <button
117
- // @ts-expect-error - TS2322 - Type 'HTMLButtonElement | null' is not assignable to type 'HTMLDivElement'.
118
- ref={(node) => (this.node = node)}
119
- value={value}
120
- role="menuitemradio"
121
- aria-checked={selected}
122
- className={css(
123
- styles.notAButton,
124
- disabled && styles.cursorDefault,
125
- hideFocusState && styles.noFocus,
126
- )}
127
- // @ts-expect-error - TS2322 - Type '(value: string) => void' is not assignable to type 'MouseEventHandler<HTMLButtonElement>'.
128
- onClick={(value: string) => {
129
- if (!disabled && onClick) {
130
- // @ts-expect-error - TS2554 - Expected 0 arguments, but got 1.
131
- onClick(value);
132
- }
133
- }}
134
- // @ts-expect-error - TS2322 - Type '(event: KeyboardEvent) => void' is not assignable to type 'KeyboardEventHandler<HTMLButtonElement>'.
135
- onKeyDown={(event: KeyboardEvent) => this.handleKeyDown(event)}
136
- aria-disabled={disabled}
137
- aria-label={ariaLabel}
138
- data-test-id={testId}
139
- >
140
- {optionRenderer &&
141
- optionRenderer(
142
- /**
143
- * This expects a `React.Element<>` but `children` is a
144
- * `React.Node`. We can convert a `React.Node` to a
145
- * `React.Element<>` by wrapping it in a fragment.
146
- */
147
- <>{children}</>,
148
- value || "",
149
- selected || false,
150
- disabled,
151
- )}
152
-
153
- {!optionRenderer && (
154
- <span
155
- className={css(
156
- styles.option,
157
- selected && styles.optionSelected,
158
- disabled && styles.optionDisabled,
159
- )}
160
- >
161
- {children}
162
- {selected && (
163
- <span className={css(styles.check)}>
164
- <Icon icon={check} />
165
- </span>
166
- )}
167
- </span>
168
- )}
169
- </button>
170
- );
171
- }
172
- }
173
-
174
- // TODO(kevinb):
175
- // - keep the the option group within the page bounds
176
- // - do we really want to dismiss this on scroll
177
- // - how does the drop down interact with tooltips? what's the z-index order?
178
- class OptionGroup extends React.Component<{
179
- // An event when an option is selected
180
- onSelected: (value: string) => void;
181
- // The list of options to display
182
- children?: Array<React.ReactElement<React.ComponentProps<typeof Option>>>;
183
- // The currently selected options
184
- selectedValues: Array<string>;
185
- // An optional rendering function to render the details of the options
186
- optionRenderer?: OptionRenderer;
187
- // An override to skip the bit of top/bottom spacing around the group
188
- noMargin?: boolean;
189
- // An optional function to call when the dropdown should close
190
- onDropdownClose?: () => void;
191
- // An optional value to override the focus styling of an option.
192
- // Use caution when using this - keyboard users need to know what
193
- // they're focused on in order to interact with the product!
194
- hideFocusState?: boolean;
195
- }> {
196
- // @ts-expect-error - TS2564 - Property 'focusedElement' has no initializer and is not definitely assigned in the constructor.
197
- focusedElement: Element;
198
-
199
- componentDidMount() {
200
- if (this.focusedElement) {
201
- findAndFocusElement(this.focusedElement);
202
- }
203
- }
204
-
205
- render(): React.ReactNode {
206
- const {
207
- children,
208
- onSelected,
209
- selectedValues,
210
- optionRenderer,
211
- noMargin,
212
- onDropdownClose,
213
- hideFocusState,
214
- } = this.props;
215
-
216
- return (
217
- <div
218
- // @ts-expect-error - TS2322 - Type 'Window | null' is not assignable to type 'Top<string | number> | undefined'.
219
- style={{top}}
220
- className={css(
221
- styles.optionGroup,
222
- noMargin && styles.optionGroupNoMargin,
223
- )}
224
- >
225
- {React.Children.map(children, (child, index) => {
226
- // @ts-expect-error - TS2532 - Object is possibly 'undefined'.
227
- const selected = selectedValues.includes(child.props.value);
228
-
229
- const reference =
230
- selected || index === 0
231
- ? (node: Element) => (this.focusedElement = node)
232
- : null;
233
-
234
- // @ts-expect-error - TS2769 - No overload matches this call.
235
- return React.cloneElement(child, {
236
- // @ts-expect-error - TS2532 - Object is possibly 'undefined'.
237
- ...child.props,
238
- key: index,
239
- selected: selected,
240
- // @ts-expect-error - TS2532 - Object is possibly 'undefined'.
241
- onClick: () => onSelected(child.props.value),
242
- optionRenderer: optionRenderer,
243
- ref: reference,
244
- onDropdownClose: onDropdownClose,
245
- hideFocusState: hideFocusState,
246
- });
247
- })}
248
- </div>
249
- );
250
- }
251
- }
252
-
253
- const styles = StyleSheet.create({
254
- optionGroup: {
255
- margin: "4px 0",
256
- },
257
- optionGroupNoMargin: {
258
- margin: 0,
259
- },
260
- option: {
261
- display: "flex",
262
- flexDirection: "row",
263
- alignItems: "center",
264
- paddingLeft: 32,
265
- paddingRight: 32,
266
- height: optionHeight,
267
- whiteSpace: "nowrap",
268
- overflow: "hidden",
269
- textOverflow: "ellipsis",
270
- color: colors.gray17,
271
- userSelect: "none",
272
-
273
- ":hover": {
274
- backgroundColor: colors.gray95,
275
- },
276
- },
277
- optionSelected: {
278
- backgroundColor: colors.gray95,
279
- color: "#11ACCD",
280
- },
281
- optionDisabled: {
282
- color: colors.gray76,
283
- ":hover": {
284
- backgroundColor: "transparent",
285
- },
286
- },
287
- check: {
288
- position: "absolute",
289
- left: 11,
290
- },
291
- notAButton: {
292
- backgroundColor: "transparent",
293
- border: "none",
294
- display: "block",
295
- padding: 0,
296
- margin: 0,
297
- width: "100%",
298
- // <button> uses user agent stylesheet over inherit so we need to set
299
- // it explicitly.
300
- font: "inherit",
301
- },
302
- noFocus: {
303
- outline: "none",
304
- },
305
- cursorDefault: {
306
- cursor: "default",
307
- },
308
- });
309
-
310
- export {OptionGroup};
311
- export default Option;