@khanacademy/perseus 1.1.1 → 1.2.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/CHANGELOG.md +20 -0
- package/dist/es/index.css +2 -2
- package/dist/es/index.js +14997 -15079
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +2 -2
- package/dist/index.js +15033 -15113
- package/dist/index.js.map +1 -1
- package/dist/strings.js +1 -0
- package/package.json +7 -7
- package/src/__stories__/hints-renderer.stories.jsx +52 -0
- package/src/__tests__/__snapshots__/renderer_test.jsx.snap +21 -3
- package/src/components/highlighting/ui/highlight-tooltip.jsx +29 -23
- package/src/components/highlighting/ui/selection-tracker.jsx +17 -28
- package/src/hints-renderer.jsx +13 -4
- package/src/mixins/__tests__/widget-prop-blacklist_test.js +23 -0
- package/src/mixins/widget-prop-blacklist.js +15 -1
- package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +30 -10
- package/src/styles/articles.less +0 -2
- package/src/styles/perseus-renderer.less +1 -1
- package/src/styles/widgets/passage.less +1 -0
- package/src/types.js +1 -1
- package/src/widgets/__tests__/__snapshots__/dropdown_test.jsx.snap +16 -2
- package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt_test.jsx.snap +3 -3
- package/src/widgets/__tests__/__snapshots__/graded-group-set_test.jsx.snap +7 -1
- package/src/widgets/__tests__/__snapshots__/graded-group_test.jsx.snap +2 -2
- package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +30 -10
- package/src/widgets/__tests__/__snapshots__/passage-ref_test.jsx.snap +3 -3
- package/src/widgets/__tests__/__snapshots__/passage_test.jsx.snap +6 -6
- package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +300 -218
- package/src/widgets/__tests__/radio/base-radio_test.jsx +90 -6
- package/src/widgets/__tests__/radio/choice-icon_test.jsx +74 -0
- package/src/widgets/__tests__/radio/choice_test.jsx +39 -30
- package/src/widgets/__tests__/radio_test.jsx +228 -22
- package/src/widgets/graded-group-set.jsx +8 -1
- package/src/widgets/graded-group.jsx +2 -1
- package/src/widgets/passage-ref.jsx +21 -5
- package/src/widgets/passage.jsx +3 -1
- package/src/widgets/radio/__stories__/base-radio.stories.jsx +7 -3
- package/src/widgets/radio/base-radio.jsx +3 -1
- package/src/widgets/radio/choice-icon/cross-out-line.jsx +3 -2
- package/src/widgets/radio/choice-icon/library-choice-icon.jsx +17 -6
- package/src/widgets/radio/choice.jsx +2 -1
- package/src/components/new-tooltip/README.md +0 -3
- package/src/components/new-tooltip/__stories__/new-tooltip.stories.jsx +0 -665
- package/src/components/new-tooltip/__tests__/style-constants_test.js +0 -8
- package/src/components/new-tooltip/new-tooltip.jsx +0 -155
- package/src/components/new-tooltip/style-constants.js +0 -13
- package/src/components/new-tooltip/types.js +0 -183
|
@@ -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
|
-
|};
|