@khanacademy/perseus 1.1.2 → 1.3.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 +23 -0
  2. package/dist/es/index.css +4 -5
  3. package/dist/es/index.js +1282 -1429
  4. package/dist/es/index.js.map +1 -1
  5. package/dist/index.css +4 -5
  6. package/dist/index.js +1283 -1429
  7. package/dist/index.js.map +1 -1
  8. package/package.json +7 -7
  9. package/src/__tests__/__snapshots__/renderer_test.jsx.snap +21 -3
  10. package/src/components/highlighting/ui/highlight-tooltip.jsx +29 -23
  11. package/src/components/highlighting/ui/selection-tracker.jsx +17 -28
  12. package/src/styles/articles.less +0 -2
  13. package/src/styles/perseus-renderer.less +1 -1
  14. package/src/styles/widgets/passage.less +7 -3
  15. package/src/widgets/__stories__/passage-ref.stories.jsx +6 -2
  16. package/src/widgets/__stories__/passage.stories.jsx +9 -1
  17. package/src/widgets/__stories__/radio.stories.jsx +11 -0
  18. package/src/widgets/__testdata__/passage-ref_testdata.js +62 -0
  19. package/src/widgets/__testdata__/passage_testdata.js +25 -0
  20. package/src/widgets/__testdata__/radio_testdata.js +52 -0
  21. package/src/widgets/__tests__/__snapshots__/dropdown_test.jsx.snap +16 -2
  22. package/src/widgets/__tests__/__snapshots__/passage-ref_test.jsx.snap +18 -18
  23. package/src/widgets/__tests__/__snapshots__/passage_test.jsx.snap +36 -36
  24. package/src/widgets/__tests__/passage_test.jsx +6 -8
  25. package/src/widgets/__tests__/radio_test.jsx +3 -1
  26. package/src/widgets/passage.jsx +12 -41
  27. package/src/widgets/radio/widget.jsx +128 -129
  28. package/src/components/new-tooltip/README.md +0 -3
  29. package/src/components/new-tooltip/__stories__/new-tooltip.stories.jsx +0 -665
  30. package/src/components/new-tooltip/__tests__/style-constants_test.js +0 -8
  31. package/src/components/new-tooltip/new-tooltip.jsx +0 -155
  32. package/src/components/new-tooltip/style-constants.js +0 -13
  33. package/src/components/new-tooltip/types.js +0 -183
  34. package/src/widgets/passage/get-line-height-for-node.js +0 -9
@@ -1,155 +0,0 @@
1
- /* eslint-disable react/no-unsafe */
2
- // @flow
3
- /**
4
- * This component is a wrapper around Wonderblock's tooltip with the API
5
- * that perseus expects
6
- */
7
-
8
- import {Popover, PopoverContentCore} from "@khanacademy/wonder-blocks-popover";
9
- import * as React from "react";
10
-
11
- import {colors} from "../../styles/global-styles.js";
12
-
13
- import type {NewTooltipProps} from "./types.js";
14
-
15
- type NewTooltipState = {|
16
- // Whether the tooltip has been dismissed.
17
- dismissed: boolean,
18
- // Whether the target element is currently being hovered.
19
- hovered: boolean,
20
- |};
21
-
22
- type Props = {|
23
- ...NewTooltipProps,
24
-
25
- children: React$Element<any>,
26
- |};
27
-
28
- type DefaultProps = {|
29
- color: Props["color"],
30
- dismissOnClickClose: Props["dismissOnClickClose"],
31
- inverted: Props["inverted"],
32
- noPadding: Props["noPadding"],
33
- offset: Props["offset"],
34
- offsetFrom: Props["offsetFrom"],
35
- showOnMount: Props["showOnMount"],
36
- side: Props["side"],
37
- toggleOnHover: Props["toggleOnHover"],
38
- tooltipMargin: Props["tooltipMargin"],
39
- |};
40
-
41
- export const DefaultPropValues: DefaultProps = Object.freeze({
42
- side: "top",
43
- offset: 0,
44
- offsetFrom: "bubble",
45
- tooltipMargin: 0,
46
- noPadding: false,
47
- color: colors.gray17,
48
- inverted: false,
49
- dismissOnClickClose: false,
50
- toggleOnHover: true,
51
- showOnMount: false,
52
- });
53
-
54
- class NewTooltip extends React.Component<Props, NewTooltipState> {
55
- static defaultProps: DefaultProps = DefaultPropValues;
56
-
57
- constructor(props: Props) {
58
- super(props);
59
-
60
- this.state = {
61
- dismissed: props.dismissed != null ? props.dismissed : false,
62
- hovered: false,
63
- };
64
- }
65
-
66
- UNSAFE_componentWillReceiveProps(nextProps: Props) {
67
- if (Object.keys(nextProps).includes("dismissed")) {
68
- this.setState({dismissed: nextProps.dismissed});
69
- }
70
- }
71
-
72
- _shouldShowTooltip(): boolean {
73
- const {toggleOnHover, showOnMount} = this.props;
74
- const {dismissed} = this.state;
75
-
76
- // If the tooltip was dismissed, or we're not sure yet, don't show it.
77
- if (dismissed) {
78
- return false;
79
- }
80
-
81
- // If the tooltip should show on mount, and nothing has changed yet
82
- // that would cause it to hide (i.e., the `dismissed` case above fell
83
- // through), show the tooltip.
84
- if (showOnMount) {
85
- return true;
86
- }
87
-
88
- // If the target is hovered, and our visibility is hover-based, show
89
- // the tooltip.
90
- if (toggleOnHover && this.state.hovered) {
91
- return true;
92
- }
93
-
94
- return false;
95
- }
96
-
97
- _handleDismiss: () => void = () => {
98
- if (this.props.dismissed == null) {
99
- // We're serving as an "uncontrolled component": our dismissed
100
- // state is managed by us, not by incoming props.
101
- this.setState({dismissed: true});
102
- }
103
-
104
- this.props.onDismiss && this.props.onDismiss();
105
- };
106
-
107
- _popoverContentColor: () => "white" | "blue" | "darkBlue" = () => {
108
- const {inverted, color} = this.props;
109
- if (inverted) {
110
- return "darkBlue";
111
- } else if (color === "blue") {
112
- return "blue";
113
- }
114
-
115
- return "white";
116
- };
117
-
118
- render(): React.Element<any> {
119
- const {side, children, content, dismissOnClickClose} = this.props;
120
- const {onMouseEnter, onMouseLeave} = this.props;
121
- return (
122
- <Popover
123
- content={
124
- <PopoverContentCore
125
- color={this._popoverContentColor()}
126
- closeButtonVisible={dismissOnClickClose}
127
- style={{}}
128
- >
129
- {content}
130
- </PopoverContentCore>
131
- }
132
- placement={side}
133
- opened={this._shouldShowTooltip()}
134
- onClose={this._handleDismiss}
135
- dismissEnabled={false}
136
- testId="question-id"
137
- >
138
- <div
139
- onMouseEnter={() => {
140
- onMouseEnter && onMouseEnter();
141
- this.setState({hovered: true});
142
- }}
143
- onMouseLeave={() => {
144
- onMouseLeave && onMouseLeave();
145
- this.setState({hovered: false});
146
- }}
147
- >
148
- {children}
149
- </div>
150
- </Popover>
151
- );
152
- }
153
- }
154
-
155
- export default NewTooltip;
@@ -1,13 +0,0 @@
1
- // @flow
2
- /**
3
- * Constant style values used across the tooltip components.
4
- */
5
- import * as constants from "../../styles/global-constants.js";
6
-
7
- export const arrowWidth = 18;
8
- export const arrowHeight = 9;
9
- export const bubbleBorderRadius: number = parseInt(constants.borderRadiusLarge);
10
- export const dropShadowXOffset = 0;
11
- export const dropShadowYOffset = 1;
12
- export const dropShadowOpacity = 0.1;
13
- export const dropShadowRadius = 4;
@@ -1,183 +0,0 @@
1
- // @flow
2
- type AriaLabelledBy = {|
3
- // The unique identifier of a DOM node to use as a label.
4
- // See aria-labelledby
5
- id: string,
6
- |};
7
-
8
- type AriaSupport = {|
9
- // The title of the tooltip.
10
- // See documentation for aria-label and aria-labelledby.
11
- title?: string | AriaLabelledBy,
12
-
13
- // How assertive should the tooltip content be?
14
- // Default is to not provide an aria-live attribute.
15
- // See documentation for aria-live.
16
- assertiveness?: "polite" | "assertive",
17
- |};
18
-
19
- export type NewTooltipSide = "top" | "bottom" | "left" | "right";
20
-
21
- export type OffsetAnchor = "bubble" | "arrow";
22
-
23
- export type NewTooltipProps = {|
24
- // The content of the tooltip bubble.
25
- content: React$Node,
26
-
27
- // Which side of the target element to attach the tooltip to.
28
- //
29
- // This field is optional. In `defaultProps`, its value is "top".
30
- side: NewTooltipSide,
31
-
32
- // How much space to put between the tooltip bubble and the target element.
33
- // (Or, if `offsetFrom` is "arrow", how much space to put between the
34
- // tooltip _arrow_ and the target element.)
35
- //
36
- // Increasing this value moves the tooltip further away from the target
37
- // element. Decreasing this value moves the tooltip closer to the target
38
- // element, perhaps to the point of overlapping!
39
- //
40
- // This field is optional. In `defaultProps`, its value is 0.
41
- offset: number,
42
-
43
- // Whether the `offset` prop measures the distance between the tooltip
44
- // _bubble_ and the target element, or the tooltip _arrow_ and the target
45
- // element.
46
- //
47
- // If `offsetFrom` is "bubble", and `offset` is 0, then the edge of the
48
- // tooltip bubble will exactly touch the edge of the target element, and
49
- // the tooltip arrow will overlap the _inside_ of the target element.
50
- //
51
- // If `offsetFrom` is "arrow", and `offset` is 0, then the tip of the
52
- // tooltip arrow will exactly touch the edge of the target element, and the
53
- // target bubble won't overlap the target element at all.
54
- //
55
- // This field is optional. In `defaultProps`, its value is "bubble".
56
- offsetFrom: OffsetAnchor,
57
-
58
- // An invisible margin around the tooltip bubble, preventing it from
59
- // touching the edges of the screen.
60
- //
61
- // This field is optional. In `defaultProps`, its value is 0.
62
- tooltipMargin: number,
63
-
64
- // Whether to have no padding between the tooltip content and the edge of
65
- // the tooltip bubble.
66
- //
67
- // This field is optional. In `defaultProps`, its value is `false`.
68
- noPadding: boolean,
69
-
70
- // The tooltip's text color - or, if `inverted` is set, the tooltip's
71
- // background color.
72
- //
73
- // This field is optional. In `defaultProps`, its value is gray17.
74
- color: string,
75
-
76
- // Whether to "invert" the colors of the tooltip - that is, swap the text
77
- // color to white, and background color to gray17 (or the value of the
78
- // `color` prop).
79
- //
80
- // This field is optional. In `defaultProps`, its value is `false`.
81
- inverted: boolean,
82
-
83
- // Whether the tooltip is dismissed. Optional.
84
- //
85
- // If provided, this becomes a "controlled component": the caller is
86
- // responsible for managing dismissal state, and using the `onDismiss`
87
- // callback to listen to changes. But the caller also has the option to
88
- // change dismissal state themselves, in case they want to dismiss or
89
- // undismiss the tooltip in response to another action at the call site.
90
- //
91
- // If not provided, this is an "uncontrolled component": dismissal state is
92
- // managed by NewTooltip automatically. Most callers will want this default
93
- // uncontrolled behavior :)
94
- dismissed?: boolean,
95
-
96
- // Whether to show a close button to dismiss the tooltip.
97
- //
98
- // This field is optional. In `defaultProps`, its value is `false`.
99
- dismissOnClickClose: boolean,
100
-
101
- // This callback is called when the tooltip is dismissed. Optional.
102
- onDismiss?: () => mixed,
103
-
104
- // A value passed to the data-test-id attribute of the tooltip bubble
105
- testId?: string,
106
-
107
- // Whether to show the tooltip on hover.
108
- //
109
- // This field is optional. In `defaultProps`, its value is `true`.
110
- toggleOnHover: boolean,
111
-
112
- // Whether to show the tooltip immediately on mount. If this flag is set,
113
- // then the tooltip will remain open until it is dismissed, regardless of
114
- // other state (like hover).
115
- //
116
- // This field is optional. In `defaultProps`, its value is `false`.
117
- showOnMount: boolean,
118
-
119
- // A media query to restrict this tooltip's visibility. If provided, this
120
- // tooltip will only be visible when the media query is satisfied.
121
- //
122
- // This field is optional. If omitted, the tooltip's visibility will not
123
- // depend on any media query.
124
- //
125
- // TODO(mdr): We're trying to solve a general styling API problem for
126
- // shared components. I wonder how conditioning the tooltip on media
127
- // query might fit into that long-term solution!
128
- //
129
- // For now, though, we're sticking this onto the NewTooltip API in a
130
- // simplistic form, instead of waiting for a general solution to exist.
131
- // Otherwise, the way to get media-conditioned tooltips is to render
132
- // multiple copies of NewTooltip _and_ its children, which causes perf,
133
- // a11y, and window-resize-consistency problems.
134
- media?: string,
135
-
136
- // This callback is called when the tooltip is clicked. Optional.
137
- //
138
- // The clickable area includes the tooltip bubble, the tooltip bubble's
139
- // contents, and the tooltip arrow. If a callback is provided, these areas
140
- // will additionally be styled as clickable, i.e., `cursor: pointer`.
141
- onClick?: () => mixed,
142
-
143
- // This callback is called when the cursor enters the tooltip. Optional.
144
- //
145
- // The hoverable area includes the tooltip bubble, the tooltip bubble's
146
- // contents, and the tooltip arrow. If a callback is provided, these areas
147
- // will additionally be styled as clickable, i.e., `cursor: pointer`.
148
- //
149
- // If the cursor moves from the tooltip bubble to the tooltip arrow, or
150
- // vice-versa, you may receive a pair of `onMouseLeave` and `onMouseEnter`
151
- // calls. Beware! (Thankfully, if all you do is call `setState`, React's
152
- // `setState` batching should protect you from unnecessary updates.)
153
- onMouseEnter?: () => mixed,
154
-
155
- // This callback is called when the cursor enters the tooltip. Optional.
156
- //
157
- // The hoverable area includes the tooltip bubble, the tooltip bubble's
158
- // contents, and the tooltip arrow. If a callback is provided, these areas
159
- // will additionally be styled as clickable, i.e., `cursor: pointer`.
160
- //
161
- // If the cursor moves from the tooltip bubble to the tooltip arrow, or
162
- // vice-versa, you may receive a pair of `onMouseLeave` and `onMouseEnter`
163
- // calls. Beware! (Thankfully, if all you do is call `setState`, React's
164
- // `setState` batching should protect you from unnecessary updates.)
165
- onMouseLeave?: () => mixed,
166
-
167
- // Provide various ARIA values to support accessibility of this tooltip.
168
- // All tooltips should probably provide a header label (string or id of
169
- // a node).
170
- //
171
- // If no title or title node id is provided, the tooltip contents are
172
- // specified as the label.
173
- //
174
- // This property can also be used to specify the aria-live assertiveness.
175
- // Permanently visible tooltips that don't change their contents
176
- // may not want this as it will call attention to the tooltip on first
177
- // display, which isn't necessarily valuable.
178
- //
179
- // If the content DOES change but you don't want an initial
180
- // assert on first display, consider using some custom aria-live within the
181
- // content of the tooltip rather than this setting.
182
- a11y?: AriaSupport,
183
- |};
@@ -1,9 +0,0 @@
1
- // @flow
2
- import $ from "jquery";
3
-
4
- export const getLineHeightForNode = (
5
- line1: HTMLDivElement,
6
- line2: HTMLDivElement,
7
- ): number => {
8
- return $(line2).offset().top - $(line1).offset().top;
9
- };