@khanacademy/wonder-blocks-popover 1.1.0 → 1.1.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 (2) hide show
  1. package/dist/index.js +897 -821
  2. package/package.json +12 -12
package/dist/index.js CHANGED
@@ -82,7 +82,7 @@ module.exports =
82
82
  /******/
83
83
  /******/
84
84
  /******/ // Load entry module and return exports
85
- /******/ return __webpack_require__(__webpack_require__.s = 12);
85
+ /******/ return __webpack_require__(__webpack_require__.s = 22);
86
86
  /******/ })
87
87
  /************************************************************************/
88
88
  /******/ ([
@@ -117,100 +117,11 @@ module.exports = require("@khanacademy/wonder-blocks-color");
117
117
 
118
118
  /***/ }),
119
119
  /* 5 */
120
- /***/ (function(module, exports) {
121
-
122
- module.exports = require("aphrodite");
123
-
124
- /***/ }),
125
- /* 6 */
126
- /***/ (function(module, exports) {
127
-
128
- function _extends() {
129
- module.exports = _extends = Object.assign || function (target) {
130
- for (var i = 1; i < arguments.length; i++) {
131
- var source = arguments[i];
132
-
133
- for (var key in source) {
134
- if (Object.prototype.hasOwnProperty.call(source, key)) {
135
- target[key] = source[key];
136
- }
137
- }
138
- }
139
-
140
- return target;
141
- };
142
-
143
- module.exports["default"] = module.exports, module.exports.__esModule = true;
144
- return _extends.apply(this, arguments);
145
- }
146
-
147
- module.exports = _extends;
148
- module.exports["default"] = module.exports, module.exports.__esModule = true;
149
-
150
- /***/ }),
151
- /* 7 */
152
- /***/ (function(module, exports) {
153
-
154
- module.exports = require("@khanacademy/wonder-blocks-tooltip");
155
-
156
- /***/ }),
157
- /* 8 */
158
- /***/ (function(module, exports) {
159
-
160
- module.exports = require("@khanacademy/wonder-blocks-typography");
161
-
162
- /***/ }),
163
- /* 9 */
164
- /***/ (function(module, exports) {
165
-
166
- module.exports = require("@khanacademy/wonder-blocks-modal");
167
-
168
- /***/ }),
169
- /* 10 */
170
- /***/ (function(module, exports) {
171
-
172
- module.exports = require("@khanacademy/wonder-blocks-icon");
173
-
174
- /***/ }),
175
- /* 11 */
176
- /***/ (function(module, exports) {
177
-
178
- module.exports = require("@khanacademy/wonder-blocks-icon-button");
179
-
180
- /***/ }),
181
- /* 12 */
182
120
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
183
121
 
184
122
  "use strict";
185
- // ESM COMPAT FLAG
186
- __webpack_require__.r(__webpack_exports__);
187
-
188
- // EXPORTS
189
- __webpack_require__.d(__webpack_exports__, "Popover", function() { return /* reexport */ popover_Popover; });
190
- __webpack_require__.d(__webpack_exports__, "PopoverContent", function() { return /* reexport */ popover_content_PopoverContent; });
191
- __webpack_require__.d(__webpack_exports__, "PopoverContentCore", function() { return /* reexport */ popover_content_core_PopoverContentCore; });
192
-
193
- // EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/extends.js
194
- var helpers_extends = __webpack_require__(6);
195
- var extends_default = /*#__PURE__*/__webpack_require__.n(helpers_extends);
196
-
197
- // EXTERNAL MODULE: external "react"
198
- var external_react_ = __webpack_require__(0);
199
-
200
- // EXTERNAL MODULE: external "react-dom"
201
- var external_react_dom_ = __webpack_require__(3);
202
- var external_react_dom_default = /*#__PURE__*/__webpack_require__.n(external_react_dom_);
203
-
204
- // EXTERNAL MODULE: external "@khanacademy/wonder-blocks-core"
205
- var wonder_blocks_core_ = __webpack_require__(2);
206
-
207
- // EXTERNAL MODULE: external "@khanacademy/wonder-blocks-tooltip"
208
- var wonder_blocks_tooltip_ = __webpack_require__(7);
209
-
210
- // EXTERNAL MODULE: external "@khanacademy/wonder-blocks-modal"
211
- var wonder_blocks_modal_ = __webpack_require__(9);
212
-
213
- // CONCATENATED MODULE: ./packages/wonder-blocks-popover/src/components/popover-context.js
123
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
124
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
214
125
 
215
126
  const defaultContext = {
216
127
  close: undefined,
@@ -227,70 +138,47 @@ const defaultContext = {
227
138
  * of the following values: "top", "bottom", "left" or "right".
228
139
  */
229
140
 
230
- /* harmony default export */ var popover_context = (/*#__PURE__*/external_react_["createContext"](defaultContext));
231
- // CONCATENATED MODULE: ./packages/wonder-blocks-popover/src/components/popover-anchor.js
141
+ /* harmony default export */ __webpack_exports__["a"] = (/*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createContext"](defaultContext));
232
142
 
143
+ /***/ }),
144
+ /* 6 */
145
+ /***/ (function(module, exports) {
233
146
 
147
+ module.exports = require("aphrodite");
234
148
 
149
+ /***/ }),
150
+ /* 7 */
151
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
235
152
 
153
+ "use strict";
154
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return findFocusableNodes; });
236
155
  /**
237
- * The element that triggers the popover dialog. This is also used as reference
238
- * to position the dialog itself.
156
+ * List of elements that can be focused
157
+ * @see https://www.w3.org/TR/html5/editing.html#can-be-focused
239
158
  */
240
- class popover_anchor_PopoverAnchor extends external_react_["Component"] {
241
- componentDidMount() {
242
- const anchorNode = external_react_dom_["findDOMNode"](this);
243
-
244
- if (anchorNode) {
245
- this.props.anchorRef(anchorNode);
246
- }
247
- }
248
-
249
- render() {
250
- const {
251
- children,
252
- id,
253
- onClick,
254
- "aria-controls": ariaControls,
255
- "aria-expanded": ariaExpanded
256
- } = this.props; // props that will be injected to both children versions
257
-
258
- const sharedProps = {
259
- id: id,
260
- "aria-controls": ariaControls,
261
- "aria-expanded": ariaExpanded
262
- };
263
-
264
- if (typeof children === "function") {
265
- const renderedChildren = children({
266
- open: onClick
267
- }); // we clone it to allow injecting the sharedProps defined before
268
-
269
- return /*#__PURE__*/external_react_["cloneElement"](renderedChildren, sharedProps);
270
- } else {
271
- // add onClick handler to automatically open the dialog after
272
- // clicking on this anchor element
273
- return /*#__PURE__*/external_react_["cloneElement"](children, extends_default()({}, children.props, sharedProps, {
274
- onClick: children.props.onClick ? e => {
275
- e.stopPropagation(); // This is done to avoid overriding a custom onClick
276
- // handler inside the children node
277
-
278
- children.props.onClick();
279
- onClick();
280
- } : onClick
281
- }));
282
- }
283
- }
284
-
159
+ const FOCUSABLE_ELEMENTS = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
160
+ function findFocusableNodes(root) {
161
+ return Array.from(root.querySelectorAll(FOCUSABLE_ELEMENTS));
285
162
  }
286
- // EXTERNAL MODULE: external "aphrodite"
287
- var external_aphrodite_ = __webpack_require__(5);
288
163
 
289
- // EXTERNAL MODULE: external "@khanacademy/wonder-blocks-color"
290
- var wonder_blocks_color_ = __webpack_require__(4);
291
- var wonder_blocks_color_default = /*#__PURE__*/__webpack_require__.n(wonder_blocks_color_);
164
+ /***/ }),
165
+ /* 8 */
166
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
167
+
168
+ "use strict";
169
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return PopoverContentCore; });
170
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
171
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
172
+ /* harmony import */ var aphrodite__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6);
173
+ /* harmony import */ var aphrodite__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(aphrodite__WEBPACK_IMPORTED_MODULE_1__);
174
+ /* harmony import */ var _khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);
175
+ /* harmony import */ var _khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_2__);
176
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(2);
177
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_3__);
178
+ /* harmony import */ var _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(1);
179
+ /* harmony import */ var _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_4__);
180
+ /* harmony import */ var _close_button_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(19);
292
181
 
293
- // CONCATENATED MODULE: ./packages/wonder-blocks-popover/src/components/popover-dialog.js
294
182
 
295
183
 
296
184
 
@@ -298,903 +186,1091 @@ var wonder_blocks_color_default = /*#__PURE__*/__webpack_require__.n(wonder_bloc
298
186
 
299
187
 
300
188
  /**
301
- * This is an internal component that we use to render the stuff that appears
302
- * when a popover shows. It's composed by two elements: The popover content,
303
- * that can be of type [PopoverContent](#PopoverContent) or
304
- * [PopoverContentCore](#PopoverContentCore), and the
305
- * [TooltipTail](#TooltipTail).
306
- *
307
- * The main difference with [TooltipBubble](#TooltipBubble) is that bubble
308
- * handles hover states and PopoverDialog doesn't need to handle any states at
309
- * all (for now). Also, PopoverDialog needs to coordinate different background
310
- * colors for the content and tail components.
311
- *
312
- * Note that without explicit positioning, the tail will not be centered.
189
+ * This is the base popover container. It’s used internally by all the variants.
190
+ * Also, it can be used to create flexible popovers.
313
191
  */
314
- class popover_dialog_PopoverDialog extends external_react_["Component"] {
315
- componentDidUpdate(prevProps) {
316
- // if the placement has changed, then we need to notify this to the
317
- // parent component (`Popover`). This way, the context will update its
318
- // `placement` value.
319
- if (prevProps.placement !== this.props.placement) {
320
- this.props.onUpdate(this.props.placement);
321
- }
322
- }
323
-
192
+ class PopoverContentCore extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
324
193
  render() {
325
194
  const {
326
- placement,
327
195
  children,
328
- id,
329
- isReferenceHidden,
330
- updateBubbleRef,
331
- updateTailRef,
332
- tailOffset,
196
+ closeButtonLight,
197
+ closeButtonLabel,
198
+ closeButtonVisible,
199
+ color,
333
200
  style,
334
- "aria-describedby": ariaDescribedby
201
+ testId
335
202
  } = this.props;
336
- const contentProps = children.props; // extract the background color from the popover content
337
-
338
- const color = contentProps.emphasized ? "blue" : contentProps.color;
339
- return /*#__PURE__*/external_react_["createElement"](external_react_["Fragment"], null, /*#__PURE__*/external_react_["createElement"](wonder_blocks_core_["View"], {
340
- "aria-describedby": ariaDescribedby,
341
- id: id,
342
- role: "dialog",
343
- ref: updateBubbleRef,
344
- "data-placement": placement,
345
- style: [isReferenceHidden && styles.hide, styles[`content-${placement}`], style]
346
- }, children, /*#__PURE__*/external_react_["createElement"](wonder_blocks_tooltip_["TooltipTail"], {
347
- color: color,
348
- updateRef: updateTailRef,
349
- placement: placement,
350
- offset: tailOffset
351
- })));
203
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_3__["View"], {
204
+ "data-test-id": testId,
205
+ style: [styles.content, color !== "white" && styles[color], style]
206
+ }, closeButtonVisible && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_close_button_js__WEBPACK_IMPORTED_MODULE_5__[/* default */ "a"], {
207
+ "aria-label": closeButtonLabel,
208
+ light: closeButtonLight || color !== "white",
209
+ style: styles.closeButton,
210
+ testId: `${testId || "popover"}-close-btn`
211
+ }), children);
352
212
  }
353
213
 
354
214
  }
355
- const styles = external_aphrodite_["StyleSheet"].create({
356
- /**
357
- * The hide style ensures that the bounds of the popover stay unchanged.
358
- * This is because popper.js calculates the bubble position based off its
359
- * bounds and if we stopped rendering it entirely, it wouldn't know where to
360
- * place it when it reappeared.
361
- */
362
- hide: {
363
- pointerEvents: "none",
364
- opacity: 0,
365
- backgroundColor: "transparent",
366
- color: "transparent"
215
+ PopoverContentCore.defaultProps = {
216
+ color: "white",
217
+ closeButtonLight: false,
218
+ closeButtonVisible: false
219
+ };
220
+ const styles = aphrodite__WEBPACK_IMPORTED_MODULE_1__["StyleSheet"].create({
221
+ content: {
222
+ borderRadius: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_4___default.a.xxxSmall_4,
223
+ border: `solid 1px ${_khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_2___default.a.offBlack16}`,
224
+ backgroundColor: _khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_2___default.a.white,
225
+ boxShadow: `0 ${_khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_4___default.a.xSmall_8}px ${_khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_4___default.a.xSmall_8}px 0 ${_khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_2___default.a.offBlack8}`,
226
+ margin: 0,
227
+ maxWidth: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_4___default.a.medium_16 * 18,
228
+ // 288px
229
+ padding: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_4___default.a.large_24,
230
+ overflow: "hidden",
231
+ justifyContent: "center"
367
232
  },
368
233
 
369
234
  /**
370
- * Ensure the content and tail are properly arranged.
235
+ * Theming
371
236
  */
372
- "content-top": {
373
- flexDirection: "column"
374
- },
375
- "content-right": {
376
- flexDirection: "row-reverse"
237
+ blue: {
238
+ backgroundColor: _khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_2___default.a.blue,
239
+ color: _khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_2___default.a.white
377
240
  },
378
- "content-bottom": {
379
- flexDirection: "column-reverse"
241
+ darkBlue: {
242
+ backgroundColor: _khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_2___default.a.darkBlue,
243
+ color: _khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_2___default.a.white
380
244
  },
381
- "content-left": {
382
- flexDirection: "row"
245
+
246
+ /**
247
+ * elements
248
+ */
249
+ closeButton: {
250
+ position: "absolute",
251
+ right: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_4___default.a.xSmall_8,
252
+ top: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_4___default.a.xSmall_8,
253
+ // Allows the button to be above the title and/or custom content
254
+ zIndex: 1
383
255
  }
384
256
  });
385
- // CONCATENATED MODULE: ./packages/wonder-blocks-popover/src/util/util.js
386
- /**
387
- * List of elements that can be focused
388
- * @see https://www.w3.org/TR/html5/editing.html#can-be-focused
389
- */
390
- const FOCUSABLE_ELEMENTS = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
391
- function findFocusableNodes(root) {
392
- return Array.from(root.querySelectorAll(FOCUSABLE_ELEMENTS));
393
- }
394
- // CONCATENATED MODULE: ./packages/wonder-blocks-popover/src/components/initial-focus.js
395
257
 
258
+ /***/ }),
259
+ /* 9 */
260
+ /***/ (function(module, exports) {
396
261
 
262
+ module.exports = require("@khanacademy/wonder-blocks-tooltip");
397
263
 
264
+ /***/ }),
265
+ /* 10 */
266
+ /***/ (function(module, exports) {
398
267
 
399
- /**
400
- * This component finds which element (from within the children) needs to
401
- * receive focus. After that, the children is rendered with the focus assigned.
402
- */
403
- class initial_focus_InitialFocus extends external_react_["Component"] {
404
- constructor(...args) {
405
- super(...args);
268
+ module.exports = require("@khanacademy/wonder-blocks-typography");
406
269
 
407
- this.setInitialFocusableElement = node => {
408
- // 1. try to get element specified by the user
409
- // 2. get first occurence from list of focusable elements
410
- // 3. If no focusable elements are found, get the container itself
411
- const firstFocusableElement = this.maybeGetInitialFocusElement(node) || this.maybeGetFirstFocusableElement(node) || node;
270
+ /***/ }),
271
+ /* 11 */
272
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
412
273
 
413
- if (firstFocusableElement === node) {
414
- // add tabIndex to make the container focusable
415
- node.tabIndex = -1;
416
- } // using timeout to prevent page jumps when focusing on this element
274
+ "use strict";
275
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Popover; });
276
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
277
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
278
+ /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
279
+ /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__);
280
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2);
281
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2__);
282
+ /* harmony import */ var _khanacademy_wonder_blocks_tooltip__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9);
283
+ /* harmony import */ var _khanacademy_wonder_blocks_tooltip__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_tooltip__WEBPACK_IMPORTED_MODULE_3__);
284
+ /* harmony import */ var _khanacademy_wonder_blocks_modal__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(13);
285
+ /* harmony import */ var _khanacademy_wonder_blocks_modal__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_modal__WEBPACK_IMPORTED_MODULE_4__);
286
+ /* harmony import */ var _popover_context_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(5);
287
+ /* harmony import */ var _popover_anchor_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(14);
288
+ /* harmony import */ var _popover_dialog_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(15);
289
+ /* harmony import */ var _focus_manager_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(16);
290
+ /* harmony import */ var _popover_event_listener_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(18);
291
+ function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
417
292
 
418
293
 
419
- setTimeout(() => {
420
- firstFocusableElement.focus();
421
- }, 0);
422
- };
423
- }
424
294
 
425
- componentDidMount() {
426
- const node = external_react_dom_["findDOMNode"](this);
427
295
 
428
- if (!node) {
429
- return;
430
- } // try to focus on the first focussable element
431
296
 
432
297
 
433
- this.setInitialFocusableElement(node);
434
- }
435
- /**
436
- * Gets the focusable element and applies focus to it
437
- */
438
298
 
439
299
 
440
- /**
441
- * Returns an element specified by the user
442
- */
443
- maybeGetInitialFocusElement(node) {
444
- const {
445
- initialFocusId
446
- } = this.props;
447
300
 
448
- if (!initialFocusId) {
449
- return null;
450
- }
451
301
 
452
- return node.querySelector(`#${initialFocusId}`);
302
+
303
+
304
+ /**
305
+ * Popovers provide additional information that is related to a particular
306
+ * element and/or content. They can include text, links, icons and
307
+ * illustrations. The main difference with `Tooltip` is that they must be
308
+ * dismissed by clicking an element.
309
+ *
310
+ * This component uses the `PopoverPopper` component to position the
311
+ * `PopoverContentCore` component according to the children it is wrapping.
312
+ */
313
+ class Popover extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
314
+ constructor(...args) {
315
+ super(...args);
316
+ this.state = {
317
+ opened: !!this.props.opened,
318
+ anchorElement: null,
319
+ placement: this.props.placement
320
+ };
321
+ this.contentRef = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createRef"]();
322
+
323
+ this.handleClose = () => {
324
+ this.setState({
325
+ opened: false
326
+ }, () => {
327
+ this.props.onClose && this.props.onClose();
328
+ });
329
+ };
330
+
331
+ this.handleOpen = () => {
332
+ if (this.props.dismissEnabled && this.state.opened) {
333
+ this.setState({
334
+ opened: false
335
+ });
336
+ } else {
337
+ this.setState({
338
+ opened: true
339
+ });
340
+ }
341
+ };
342
+
343
+ this.updateRef = ref => {
344
+ const actualRef = ref && react_dom__WEBPACK_IMPORTED_MODULE_1___default.a.findDOMNode(ref);
345
+
346
+ if (actualRef && this.state.anchorElement !== actualRef) {
347
+ this.setState({
348
+ anchorElement: actualRef
349
+ });
350
+ }
351
+ };
453
352
  }
353
+
454
354
  /**
455
- * Returns the first focusable element found inside the children
355
+ * Used to sync the `opened` state when Popover acts as a controlled
356
+ * component
456
357
  */
358
+ static getDerivedStateFromProps(props, state) {
359
+ return {
360
+ opened: typeof props.opened === "boolean" ? props.opened : state.opened
361
+ };
362
+ }
457
363
 
364
+ renderContent() {
365
+ const {
366
+ content
367
+ } = this.props;
368
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["cloneElement"](typeof content === "function" ? content({
369
+ close: this.handleClose
370
+ }) : content, {
371
+ ref: this.contentRef
372
+ });
373
+ }
458
374
 
459
- maybeGetFirstFocusableElement(node) {
460
- // get a collection of elements that can be focused
461
- const focusableElements = findFocusableNodes(node);
462
-
463
- if (!focusableElements.length) {
464
- return null;
465
- } // if found, return the first focusable element
466
-
375
+ renderPopper(uniqueId) {
376
+ const {
377
+ initialFocusId,
378
+ placement
379
+ } = this.props;
380
+ const {
381
+ anchorElement
382
+ } = this.state;
383
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_focus_manager_js__WEBPACK_IMPORTED_MODULE_8__[/* default */ "a"], {
384
+ anchorElement: anchorElement,
385
+ initialFocusId: initialFocusId
386
+ }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_khanacademy_wonder_blocks_tooltip__WEBPACK_IMPORTED_MODULE_3__["TooltipPopper"], {
387
+ anchorElement: anchorElement,
388
+ placement: placement
389
+ }, props => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_popover_dialog_js__WEBPACK_IMPORTED_MODULE_7__[/* default */ "a"], _extends({}, props, {
390
+ "aria-describedby": `${uniqueId}-anchor`,
391
+ id: uniqueId,
392
+ onUpdate: placement => this.setState({
393
+ placement
394
+ })
395
+ }), this.renderContent())));
396
+ }
467
397
 
468
- return focusableElements[0];
398
+ getHost() {
399
+ // If we are in a modal, we find where we should be portalling the
400
+ // popover by using the helper function from the modal package on the
401
+ // trigger element. If we are not in a modal, we use body as the
402
+ // location to portal to.
403
+ return Object(_khanacademy_wonder_blocks_modal__WEBPACK_IMPORTED_MODULE_4__["maybeGetPortalMountedModalHostElement"])(this.state.anchorElement) || document.body;
469
404
  }
470
405
 
471
406
  render() {
472
- return this.props.children;
407
+ const {
408
+ children,
409
+ dismissEnabled,
410
+ id
411
+ } = this.props;
412
+ const {
413
+ opened,
414
+ placement
415
+ } = this.state;
416
+ const popperHost = this.getHost();
417
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_popover_context_js__WEBPACK_IMPORTED_MODULE_5__[/* default */ "a"].Provider, {
418
+ value: {
419
+ close: this.handleClose,
420
+ placement: placement
421
+ }
422
+ }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2__["IDProvider"], {
423
+ id: id,
424
+ scope: "popover"
425
+ }, uniqueId => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](react__WEBPACK_IMPORTED_MODULE_0__["Fragment"], null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_popover_anchor_js__WEBPACK_IMPORTED_MODULE_6__[/* default */ "a"], {
426
+ anchorRef: this.updateRef,
427
+ id: `${uniqueId}-anchor`,
428
+ "aria-controls": uniqueId,
429
+ "aria-expanded": opened ? "true" : "false",
430
+ onClick: this.handleOpen
431
+ }, children), popperHost && opened && /*#__PURE__*/react_dom__WEBPACK_IMPORTED_MODULE_1___default.a.createPortal(this.renderPopper(uniqueId), popperHost))), dismissEnabled && opened && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_popover_event_listener_js__WEBPACK_IMPORTED_MODULE_9__[/* default */ "a"], {
432
+ onClose: this.handleClose,
433
+ contentRef: this.contentRef
434
+ }));
473
435
  }
474
436
 
475
437
  }
476
- // CONCATENATED MODULE: ./packages/wonder-blocks-popover/src/components/focus-manager.js
438
+ Popover.defaultProps = {
439
+ placement: "top"
440
+ };
441
+
442
+ /***/ }),
443
+ /* 12 */
444
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
445
+
446
+ "use strict";
447
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return PopoverContent; });
448
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
449
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
450
+ /* harmony import */ var aphrodite__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6);
451
+ /* harmony import */ var aphrodite__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(aphrodite__WEBPACK_IMPORTED_MODULE_1__);
452
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2);
453
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2__);
454
+ /* harmony import */ var _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(1);
455
+ /* harmony import */ var _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_3__);
456
+ /* harmony import */ var _khanacademy_wonder_blocks_typography__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(10);
457
+ /* harmony import */ var _khanacademy_wonder_blocks_typography__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_typography__WEBPACK_IMPORTED_MODULE_4__);
458
+ /* harmony import */ var _popover_content_core_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(8);
459
+ /* harmony import */ var _popover_context_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(5);
460
+
477
461
 
478
462
 
479
463
 
480
464
 
481
465
 
466
+
467
+ // Created to add custom styles to the icon or image elements
468
+ const StyledImage = Object(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2__["addStyle"])("img");
482
469
  /**
483
- * This component ensures that focus flows correctly when the popover is open.
484
- *
485
- * Inside the popover:
486
- * - `tab`: Moves focus to the next focusable element.
487
- * - `shift + tab`: Moves focus to the previous focusable element.
488
- *
489
- * After the focus reaches the start/end of the popover, then we handle two
490
- * different scenarios:
491
- *
492
- * 1. If the focus has reached the last focusable element inside the popover,
493
- * the next tab will set focus on the next focusable element that exists
494
- * after the PopoverAnchor.
495
- * 2. If the focus is set to the first focusable element inside the popover, the
496
- * next shift + tab will set focus on the PopoverAnchor element.
497
- *
470
+ * This is the container that is consumed by all the predefined variations. Its
471
+ * main responsibility is populate the contents depending on the variation used.
498
472
  */
499
- class focus_manager_FocusManager extends external_react_["Component"] {
473
+
474
+ class PopoverContent extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
500
475
  constructor(...args) {
501
476
  super(...args);
502
- this.focusableElementsInPopover = [];
503
477
 
504
- this.addEventListeners = () => {
478
+ this.maybeRenderImage = ({
479
+ placement
480
+ }) => {
505
481
  const {
506
- anchorElement
482
+ image
507
483
  } = this.props;
508
484
 
509
- if (anchorElement) {
510
- anchorElement.addEventListener("keydown", this.handleKeydownPreviousFocusableElement, true);
511
- } // tries to get the next focusable element outside of the popover
512
-
513
-
514
- this.nextElementAfterPopover = this.getNextFocusableElement();
515
-
516
- if (this.nextElementAfterPopover) {
517
- this.nextElementAfterPopover.addEventListener("keydown", this.handleKeydownNextFocusableElement, true);
485
+ if (!image) {
486
+ return null;
518
487
  }
488
+
489
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2__["View"], {
490
+ style: [styles.image, placement === "bottom" && styles.imageToBottom]
491
+ }, image);
519
492
  };
520
493
 
521
- this.getNextFocusableElement = () => {
494
+ this.maybeRenderIcon = () => {
522
495
  const {
523
- anchorElement
496
+ icon
524
497
  } = this.props;
525
498
 
526
- if (!anchorElement || this.nextElementAfterPopover) {
527
- return;
528
- } // get the total list of focusable elements within the document
529
-
530
-
531
- const focusableElements = findFocusableNodes(document); // get anchor element index
532
-
533
- const anchorIndex = focusableElements.indexOf(anchorElement);
534
-
535
- if (anchorIndex >= 0) {
536
- // guess next focusable element index
537
- const nextElementIndex = anchorIndex < focusableElements.length - 1 ? anchorIndex + 1 : 0; // get next element's DOM reference
538
-
539
- return focusableElements[nextElementIndex];
499
+ if (!icon) {
500
+ return null;
540
501
  }
541
502
 
542
- return;
503
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2__["View"], {
504
+ style: styles.iconContainer
505
+ }, typeof icon !== "string" ? icon : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](StyledImage, {
506
+ src: icon,
507
+ style: styles.icon
508
+ }));
543
509
  };
544
510
 
545
- this.getComponentRootNode = node => {
546
- if (!node) {
547
- // The component is being umounted
548
- return;
549
- }
550
-
551
- const rootNode = external_react_dom_["findDOMNode"](node);
511
+ this.maybeRenderActions = close => {
512
+ const {
513
+ actions
514
+ } = this.props;
552
515
 
553
- if (!rootNode) {
554
- throw new Error("Assertion error: root node should exist after mount");
516
+ if (!actions) {
517
+ return null;
555
518
  }
556
519
 
557
- this.rootNode = rootNode; // store the list of possible focusable elements inside the popover
558
-
559
- this.focusableElementsInPopover = findFocusableNodes(this.rootNode);
520
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2__["View"], {
521
+ style: styles.actions
522
+ }, typeof actions === "function" ? actions({
523
+ close: close
524
+ }) : actions);
560
525
  };
526
+ }
561
527
 
562
- this.handleFocusPreviousFocusableElement = () => {
563
- if (this.props.anchorElement) {
564
- this.props.anchorElement.focus();
565
- }
566
- };
528
+ componentDidMount() {
529
+ const {
530
+ icon,
531
+ image
532
+ } = this.props; // this runtime check is added to support <svg> and <img> elements
533
+ // inside the image prop
567
534
 
568
- this.handleFocusNextFocusableElement = () => {
569
- if (this.nextElementAfterPopover) {
570
- this.nextElementAfterPopover.focus();
571
- }
572
- };
535
+ if (image && icon) {
536
+ throw new Error("'image' and 'icon' cannot be used at the same time. You can fix this by either removing 'image' or 'icon' from your instance.");
537
+ }
538
+ }
539
+ /**
540
+ * Runtime validation in case we try to use an invalid shape
541
+ */
573
542
 
574
- this.handleKeydownPreviousFocusableElement = e => {
575
- // It will try focus only if the user is pressing `tab`
576
- if (e.key === "Tab" && !e.shiftKey) {
577
- e.preventDefault();
578
- this.focusableElementsInPopover[0].focus();
579
- }
580
- };
581
543
 
582
- this.handleKeydownNextFocusableElement = e => {
583
- // It will try focus only if the user is pressing `Shift+tab`
584
- if (e.key === "Tab" && e.shiftKey) {
585
- e.preventDefault();
586
- const lastElementIndex = this.focusableElementsInPopover.length - 1;
587
- this.focusableElementsInPopover[lastElementIndex].focus();
588
- }
589
- };
544
+ validateProps({
545
+ placement
546
+ }) {
547
+ // illustration popover can't be placed horizontally
548
+ if (this.props.image && (placement === "left" || placement === "right")) {
549
+ throw new Error("'image' can only be vertically placed. You can fix this by either changing `placement` to `top` or `bottom` or removing the `image` prop inside `content`.");
550
+ }
551
+ }
552
+
553
+ render() {
554
+ const {
555
+ closeButtonLabel,
556
+ closeButtonVisible,
557
+ content,
558
+ emphasized = undefined,
559
+ icon,
560
+ image,
561
+ style,
562
+ title,
563
+ testId
564
+ } = this.props;
565
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_popover_context_js__WEBPACK_IMPORTED_MODULE_6__[/* default */ "a"].Consumer, null, ({
566
+ close,
567
+ placement
568
+ }) => {
569
+ // verify if the props are correct
570
+ this.validateProps({
571
+ close,
572
+ placement
573
+ });
574
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_popover_content_core_js__WEBPACK_IMPORTED_MODULE_5__[/* default */ "a"], {
575
+ color: emphasized ? "blue" : "white",
576
+ closeButtonLight: image && placement === "top",
577
+ closeButtonLabel: closeButtonLabel,
578
+ closeButtonVisible: closeButtonVisible,
579
+ style: style,
580
+ testId: testId
581
+ }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2__["View"], {
582
+ style: !!icon && styles.withIcon
583
+ }, this.maybeRenderImage({
584
+ placement
585
+ }), this.maybeRenderIcon(), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2__["View"], {
586
+ style: styles.text
587
+ }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_khanacademy_wonder_blocks_typography__WEBPACK_IMPORTED_MODULE_4__["HeadingSmall"], {
588
+ style: styles.title
589
+ }, title), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_khanacademy_wonder_blocks_typography__WEBPACK_IMPORTED_MODULE_4__["Body"], null, content))), this.maybeRenderActions(close));
590
+ });
590
591
  }
591
592
 
593
+ }
594
+ PopoverContent.defaultProps = {
595
+ closeButtonVisible: false
596
+ };
597
+ const styles = aphrodite__WEBPACK_IMPORTED_MODULE_1__["StyleSheet"].create({
592
598
  /**
593
- * The focusable element that is positioned after the trigger element
599
+ * Shared styles
594
600
  */
601
+ actions: {
602
+ marginTop: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_3___default.a.large_24,
603
+ flexDirection: "row",
604
+ alignItems: "center",
605
+ justifyContent: "flex-end"
606
+ },
607
+ text: {
608
+ justifyContent: "center"
609
+ },
610
+ title: {
611
+ marginBottom: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_3___default.a.xSmall_8
612
+ },
595
613
 
596
614
  /**
597
- * Tabbing is restricted to descendents of this element.
615
+ * Icon styles
598
616
  */
599
- componentDidMount() {
600
- this.addEventListeners();
601
- }
617
+ iconContainer: {
618
+ alignItems: "center",
619
+ justifyContent: "center",
620
+ height: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_3___default.a.xxxLarge_64,
621
+ width: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_3___default.a.xxxLarge_64,
622
+ minWidth: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_3___default.a.xxxLarge_64,
623
+ marginRight: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_3___default.a.medium_16,
624
+ overflow: "hidden"
625
+ },
626
+ icon: {
627
+ width: "100%"
628
+ },
629
+ withIcon: {
630
+ flexDirection: "row"
631
+ },
602
632
 
603
- componentDidUpdate() {
604
- this.addEventListeners();
605
- }
606
633
  /**
607
- * Remove keydown listeners
634
+ * Illustration styles
608
635
  */
636
+ image: {
637
+ marginBottom: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_3___default.a.large_24,
638
+ marginLeft: -_khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_3___default.a.large_24,
639
+ marginRight: -_khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_3___default.a.large_24,
640
+ marginTop: -_khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_3___default.a.large_24,
641
+ width: `calc(100% + ${_khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_3___default.a.large_24 * 2}px)`
642
+ },
643
+ imageToBottom: {
644
+ marginBottom: -_khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_3___default.a.large_24,
645
+ marginTop: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_3___default.a.large_24,
646
+ order: 1
647
+ }
648
+ });
609
649
 
650
+ /***/ }),
651
+ /* 13 */
652
+ /***/ (function(module, exports) {
610
653
 
611
- componentWillUnmount() {
612
- const {
613
- anchorElement
614
- } = this.props;
654
+ module.exports = require("@khanacademy/wonder-blocks-modal");
615
655
 
616
- if (anchorElement) {
617
- // wait for styles to applied, then return the focus to the anchor
618
- setTimeout(() => anchorElement.focus(), 0);
619
- anchorElement.removeEventListener("keydown", this.handleKeydownPreviousFocusableElement, true);
620
- }
656
+ /***/ }),
657
+ /* 14 */
658
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
621
659
 
622
- if (this.nextElementAfterPopover) {
623
- this.nextElementAfterPopover.removeEventListener("keydown", this.handleKeydownNextFocusableElement, true);
660
+ "use strict";
661
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return PopoverAnchor; });
662
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
663
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
664
+ /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
665
+ /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__);
666
+
667
+
668
+
669
+ /**
670
+ * The element that triggers the popover dialog. This is also used as reference
671
+ * to position the dialog itself.
672
+ */
673
+ class PopoverAnchor extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
674
+ componentDidMount() {
675
+ const anchorNode = react_dom__WEBPACK_IMPORTED_MODULE_1__["findDOMNode"](this);
676
+
677
+ if (anchorNode) {
678
+ this.props.anchorRef(anchorNode);
624
679
  }
625
680
  }
626
- /**
627
- * List of focusable elements within the popover content
628
- */
629
-
630
681
 
631
682
  render() {
632
683
  const {
633
- children
634
- } = this.props;
635
- return /*#__PURE__*/external_react_["createElement"](external_react_["Fragment"], null, /*#__PURE__*/external_react_["createElement"]("div", {
636
- tabIndex: "0",
637
- onFocus: this.handleFocusPreviousFocusableElement,
638
- style: {
639
- position: "fixed"
640
- }
641
- }), /*#__PURE__*/external_react_["createElement"]("div", {
642
- ref: this.getComponentRootNode
643
- }, /*#__PURE__*/external_react_["createElement"](initial_focus_InitialFocus, {
644
- initialFocusId: this.props.initialFocusId
645
- }, children)), /*#__PURE__*/external_react_["createElement"]("div", {
646
- tabIndex: "0",
647
- onFocus: this.handleFocusNextFocusableElement,
648
- style: {
649
- position: "fixed"
650
- }
651
- }));
684
+ children,
685
+ id,
686
+ onClick,
687
+ "aria-controls": ariaControls,
688
+ "aria-expanded": ariaExpanded
689
+ } = this.props; // props that will be injected to both children versions
690
+
691
+ const sharedProps = {
692
+ id: id,
693
+ "aria-controls": ariaControls,
694
+ "aria-expanded": ariaExpanded
695
+ };
696
+
697
+ if (typeof children === "function") {
698
+ const renderedChildren = children({
699
+ open: onClick
700
+ }); // we clone it to allow injecting the sharedProps defined before
701
+
702
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["cloneElement"](renderedChildren, sharedProps);
703
+ } else {
704
+ // add onClick handler to automatically open the dialog after
705
+ // clicking on this anchor element
706
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["cloneElement"](children, { ...children.props,
707
+ ...sharedProps,
708
+ onClick: children.props.onClick ? e => {
709
+ e.stopPropagation(); // This is done to avoid overriding a custom onClick
710
+ // handler inside the children node
711
+
712
+ children.props.onClick();
713
+ onClick();
714
+ } : onClick
715
+ });
716
+ }
652
717
  }
653
718
 
654
719
  }
655
- // CONCATENATED MODULE: ./packages/wonder-blocks-popover/src/components/popover-event-listener.js
656
720
 
721
+ /***/ }),
722
+ /* 15 */
723
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
657
724
 
725
+ "use strict";
726
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return PopoverDialog; });
727
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
728
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
729
+ /* harmony import */ var aphrodite__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6);
730
+ /* harmony import */ var aphrodite__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(aphrodite__WEBPACK_IMPORTED_MODULE_1__);
731
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2);
732
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2__);
733
+ /* harmony import */ var _khanacademy_wonder_blocks_tooltip__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9);
734
+ /* harmony import */ var _khanacademy_wonder_blocks_tooltip__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_tooltip__WEBPACK_IMPORTED_MODULE_3__);
735
+ /* harmony import */ var _khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4);
736
+ /* harmony import */ var _khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_4__);
658
737
 
659
- /**
660
- * A component that, when mounted, calls `onClose` when certain events occur.
661
- * This includes when pressing Escape or clicking outside the Popover.
662
- * @see @khanacademy/wonder-blocks-modal/components/modal-launcher.js
663
- */
664
- class popover_event_listener_PopoverEventListener extends external_react_["Component"] {
665
- constructor(...args) {
666
- super(...args);
667
- this.state = {
668
- isFirstClick: true
669
- };
670
738
 
671
- this._handleKeyup = e => {
672
- // We check the key as that's keyboard layout agnostic and also avoids
673
- // the minefield of deprecated number type properties like keyCode and
674
- // which, with the replacement code, which uses a string instead.
675
- if (e.key === "Escape") {
676
- // Stop the event going any further.
677
- // For cancellation events, like the Escape key, we generally should
678
- // air on the side of caution and only allow it to cancel one thing.
679
- // So, it's polite for us to stop propagation of the event.
680
- // Otherwise, we end up with UX where one Escape key press
681
- // unexpectedly cancels multiple things.
682
- e.preventDefault();
683
- e.stopPropagation();
684
- this.props.onClose();
685
- }
686
- };
687
739
 
688
- this._handleClick = e => {
689
- var _this$props$contentRe;
690
740
 
691
- // Prevents the problem where clicking the trigger button
692
- // triggers a click event and immediately closes the popover.
693
- if (this.state.isFirstClick) {
694
- this.setState({
695
- isFirstClick: false
696
- });
697
- return;
698
- }
699
741
 
700
- const node = external_react_dom_default.a.findDOMNode((_this$props$contentRe = this.props.contentRef) == null ? void 0 : _this$props$contentRe.current);
701
742
 
702
- if (node && !node.contains(e.target)) {
703
- // Stop the event going any further.
704
- // Only allow click to cancel one thing at a time.
705
- e.preventDefault();
706
- e.stopPropagation();
707
- this.props.onClose();
708
- }
709
- };
743
+ /**
744
+ * This is an internal component that we use to render the stuff that appears
745
+ * when a popover shows. It's composed by two elements: The popover content,
746
+ * that can be of type [PopoverContent](#PopoverContent) or
747
+ * [PopoverContentCore](#PopoverContentCore), and the
748
+ * [TooltipTail](#TooltipTail).
749
+ *
750
+ * The main difference with [TooltipBubble](#TooltipBubble) is that bubble
751
+ * handles hover states and PopoverDialog doesn't need to handle any states at
752
+ * all (for now). Also, PopoverDialog needs to coordinate different background
753
+ * colors for the content and tail components.
754
+ *
755
+ * Note that without explicit positioning, the tail will not be centered.
756
+ */
757
+ class PopoverDialog extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
758
+ componentDidUpdate(prevProps) {
759
+ // if the placement has changed, then we need to notify this to the
760
+ // parent component (`Popover`). This way, the context will update its
761
+ // `placement` value.
762
+ if (prevProps.placement !== this.props.placement) {
763
+ this.props.onUpdate(this.props.placement);
764
+ }
710
765
  }
711
766
 
712
- componentDidMount() {
713
- window.addEventListener("keyup", this._handleKeyup);
714
- window.addEventListener("click", this._handleClick);
715
- }
767
+ render() {
768
+ const {
769
+ placement,
770
+ children,
771
+ id,
772
+ isReferenceHidden,
773
+ updateBubbleRef,
774
+ updateTailRef,
775
+ tailOffset,
776
+ style,
777
+ "aria-describedby": ariaDescribedby
778
+ } = this.props;
779
+ const contentProps = children.props; // extract the background color from the popover content
716
780
 
717
- componentWillUnmount() {
718
- window.removeEventListener("keyup", this._handleKeyup);
719
- window.removeEventListener("click", this._handleClick);
781
+ const color = contentProps.emphasized ? "blue" : contentProps.color;
782
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](react__WEBPACK_IMPORTED_MODULE_0__["Fragment"], null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2__["View"], {
783
+ "aria-describedby": ariaDescribedby,
784
+ id: id,
785
+ role: "dialog",
786
+ ref: updateBubbleRef,
787
+ "data-placement": placement,
788
+ style: [isReferenceHidden && styles.hide, styles[`content-${placement}`], style]
789
+ }, children, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_khanacademy_wonder_blocks_tooltip__WEBPACK_IMPORTED_MODULE_3__["TooltipTail"], {
790
+ color: color,
791
+ updateRef: updateTailRef,
792
+ placement: placement,
793
+ offset: tailOffset
794
+ })));
720
795
  }
721
796
 
722
- render() {
723
- return null;
797
+ }
798
+ const styles = aphrodite__WEBPACK_IMPORTED_MODULE_1__["StyleSheet"].create({
799
+ /**
800
+ * The hide style ensures that the bounds of the popover stay unchanged.
801
+ * This is because popper.js calculates the bubble position based off its
802
+ * bounds and if we stopped rendering it entirely, it wouldn't know where to
803
+ * place it when it reappeared.
804
+ */
805
+ hide: {
806
+ pointerEvents: "none",
807
+ opacity: 0,
808
+ backgroundColor: "transparent",
809
+ color: "transparent"
810
+ },
811
+
812
+ /**
813
+ * Ensure the content and tail are properly arranged.
814
+ */
815
+ "content-top": {
816
+ flexDirection: "column"
817
+ },
818
+ "content-right": {
819
+ flexDirection: "row-reverse"
820
+ },
821
+ "content-bottom": {
822
+ flexDirection: "column-reverse"
823
+ },
824
+ "content-left": {
825
+ flexDirection: "row"
724
826
  }
827
+ });
828
+
829
+ /***/ }),
830
+ /* 16 */
831
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
832
+
833
+ "use strict";
834
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return FocusManager; });
835
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
836
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
837
+ /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
838
+ /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__);
839
+ /* harmony import */ var _util_util_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7);
840
+ /* harmony import */ var _initial_focus_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(17);
841
+
842
+
843
+
844
+
845
+
846
+ /**
847
+ * This component ensures that focus flows correctly when the popover is open.
848
+ *
849
+ * Inside the popover:
850
+ * - `tab`: Moves focus to the next focusable element.
851
+ * - `shift + tab`: Moves focus to the previous focusable element.
852
+ *
853
+ * After the focus reaches the start/end of the popover, then we handle two
854
+ * different scenarios:
855
+ *
856
+ * 1. If the focus has reached the last focusable element inside the popover,
857
+ * the next tab will set focus on the next focusable element that exists
858
+ * after the PopoverAnchor.
859
+ * 2. If the focus is set to the first focusable element inside the popover, the
860
+ * next shift + tab will set focus on the PopoverAnchor element.
861
+ *
862
+ */
863
+ class FocusManager extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
864
+ constructor(...args) {
865
+ super(...args);
866
+ this.focusableElementsInPopover = [];
867
+
868
+ this.addEventListeners = () => {
869
+ const {
870
+ anchorElement
871
+ } = this.props;
872
+
873
+ if (anchorElement) {
874
+ anchorElement.addEventListener("keydown", this.handleKeydownPreviousFocusableElement, true);
875
+ } // tries to get the next focusable element outside of the popover
876
+
877
+
878
+ this.nextElementAfterPopover = this.getNextFocusableElement();
725
879
 
726
- }
727
- // CONCATENATED MODULE: ./packages/wonder-blocks-popover/src/components/popover.js
880
+ if (this.nextElementAfterPopover) {
881
+ this.nextElementAfterPopover.addEventListener("keydown", this.handleKeydownNextFocusableElement, true);
882
+ }
883
+ };
728
884
 
885
+ this.getNextFocusableElement = () => {
886
+ const {
887
+ anchorElement
888
+ } = this.props;
729
889
 
890
+ if (!anchorElement || this.nextElementAfterPopover) {
891
+ return;
892
+ } // get the total list of focusable elements within the document
730
893
 
731
894
 
895
+ const focusableElements = Object(_util_util_js__WEBPACK_IMPORTED_MODULE_2__[/* findFocusableNodes */ "a"])(document); // get anchor element index
732
896
 
897
+ const anchorIndex = focusableElements.indexOf(anchorElement);
733
898
 
899
+ if (anchorIndex >= 0) {
900
+ // guess next focusable element index
901
+ const nextElementIndex = anchorIndex < focusableElements.length - 1 ? anchorIndex + 1 : 0; // get next element's DOM reference
734
902
 
903
+ return focusableElements[nextElementIndex];
904
+ }
735
905
 
906
+ return;
907
+ };
736
908
 
909
+ this.getComponentRootNode = node => {
910
+ if (!node) {
911
+ // The component is being umounted
912
+ return;
913
+ }
737
914
 
915
+ const rootNode = react_dom__WEBPACK_IMPORTED_MODULE_1__["findDOMNode"](node);
738
916
 
917
+ if (!rootNode) {
918
+ throw new Error("Assertion error: root node should exist after mount");
919
+ }
739
920
 
740
- /**
741
- * Popovers provide additional information that is related to a particular
742
- * element and/or content. They can include text, links, icons and
743
- * illustrations. The main difference with `Tooltip` is that they must be
744
- * dismissed by clicking an element.
745
- *
746
- * This component uses the `PopoverPopper` component to position the
747
- * `PopoverContentCore` component according to the children it is wrapping.
748
- */
749
- class popover_Popover extends external_react_["Component"] {
750
- constructor(...args) {
751
- super(...args);
752
- this.state = {
753
- opened: !!this.props.opened,
754
- anchorElement: null,
755
- placement: this.props.placement
921
+ this.rootNode = rootNode; // store the list of possible focusable elements inside the popover
922
+
923
+ this.focusableElementsInPopover = Object(_util_util_js__WEBPACK_IMPORTED_MODULE_2__[/* findFocusableNodes */ "a"])(this.rootNode);
756
924
  };
757
- this.contentRef = /*#__PURE__*/external_react_["createRef"]();
758
925
 
759
- this.handleClose = () => {
760
- this.setState({
761
- opened: false
762
- }, () => {
763
- this.props.onClose && this.props.onClose();
764
- });
926
+ this.handleFocusPreviousFocusableElement = () => {
927
+ if (this.props.anchorElement) {
928
+ this.props.anchorElement.focus();
929
+ }
765
930
  };
766
931
 
767
- this.handleOpen = () => {
768
- if (this.props.dismissEnabled && this.state.opened) {
769
- this.setState({
770
- opened: false
771
- });
772
- } else {
773
- this.setState({
774
- opened: true
775
- });
932
+ this.handleFocusNextFocusableElement = () => {
933
+ if (this.nextElementAfterPopover) {
934
+ this.nextElementAfterPopover.focus();
776
935
  }
777
936
  };
778
937
 
779
- this.updateRef = ref => {
780
- const actualRef = ref && external_react_dom_default.a.findDOMNode(ref);
938
+ this.handleKeydownPreviousFocusableElement = e => {
939
+ // It will try focus only if the user is pressing `tab`
940
+ if (e.key === "Tab" && !e.shiftKey) {
941
+ e.preventDefault();
942
+ this.focusableElementsInPopover[0].focus();
943
+ }
944
+ };
781
945
 
782
- if (actualRef && this.state.anchorElement !== actualRef) {
783
- this.setState({
784
- anchorElement: actualRef
785
- });
946
+ this.handleKeydownNextFocusableElement = e => {
947
+ // It will try focus only if the user is pressing `Shift+tab`
948
+ if (e.key === "Tab" && e.shiftKey) {
949
+ e.preventDefault();
950
+ const lastElementIndex = this.focusableElementsInPopover.length - 1;
951
+ this.focusableElementsInPopover[lastElementIndex].focus();
786
952
  }
787
953
  };
788
954
  }
789
955
 
790
956
  /**
791
- * Used to sync the `opened` state when Popover acts as a controlled
792
- * component
957
+ * The focusable element that is positioned after the trigger element
793
958
  */
794
- static getDerivedStateFromProps(props, state) {
795
- return {
796
- opened: typeof props.opened === "boolean" ? props.opened : state.opened
797
- };
959
+
960
+ /**
961
+ * Tabbing is restricted to descendents of this element.
962
+ */
963
+ componentDidMount() {
964
+ this.addEventListeners();
798
965
  }
799
966
 
800
- renderContent() {
801
- const {
802
- content
803
- } = this.props;
804
- return /*#__PURE__*/external_react_["cloneElement"](typeof content === "function" ? content({
805
- close: this.handleClose
806
- }) : content, {
807
- ref: this.contentRef
808
- });
967
+ componentDidUpdate() {
968
+ this.addEventListeners();
809
969
  }
970
+ /**
971
+ * Remove keydown listeners
972
+ */
810
973
 
811
- renderPopper(uniqueId) {
812
- const {
813
- initialFocusId,
814
- placement
815
- } = this.props;
974
+
975
+ componentWillUnmount() {
816
976
  const {
817
977
  anchorElement
818
- } = this.state;
819
- return /*#__PURE__*/external_react_["createElement"](focus_manager_FocusManager, {
820
- anchorElement: anchorElement,
821
- initialFocusId: initialFocusId
822
- }, /*#__PURE__*/external_react_["createElement"](wonder_blocks_tooltip_["TooltipPopper"], {
823
- anchorElement: anchorElement,
824
- placement: placement
825
- }, props => /*#__PURE__*/external_react_["createElement"](popover_dialog_PopoverDialog, extends_default()({}, props, {
826
- "aria-describedby": `${uniqueId}-anchor`,
827
- id: uniqueId,
828
- onUpdate: placement => this.setState({
829
- placement
830
- })
831
- }), this.renderContent())));
832
- }
978
+ } = this.props;
833
979
 
834
- getHost() {
835
- // If we are in a modal, we find where we should be portalling the
836
- // popover by using the helper function from the modal package on the
837
- // trigger element. If we are not in a modal, we use body as the
838
- // location to portal to.
839
- return Object(wonder_blocks_modal_["maybeGetPortalMountedModalHostElement"])(this.state.anchorElement) || document.body;
980
+ if (anchorElement) {
981
+ // wait for styles to applied, then return the focus to the anchor
982
+ setTimeout(() => anchorElement.focus(), 0);
983
+ anchorElement.removeEventListener("keydown", this.handleKeydownPreviousFocusableElement, true);
984
+ }
985
+
986
+ if (this.nextElementAfterPopover) {
987
+ this.nextElementAfterPopover.removeEventListener("keydown", this.handleKeydownNextFocusableElement, true);
988
+ }
840
989
  }
990
+ /**
991
+ * List of focusable elements within the popover content
992
+ */
993
+
841
994
 
842
995
  render() {
843
996
  const {
844
- children,
845
- dismissEnabled,
846
- id
997
+ children
847
998
  } = this.props;
848
- const {
849
- opened,
850
- placement
851
- } = this.state;
852
- const popperHost = this.getHost();
853
- return /*#__PURE__*/external_react_["createElement"](popover_context.Provider, {
854
- value: {
855
- close: this.handleClose,
856
- placement: placement
999
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](react__WEBPACK_IMPORTED_MODULE_0__["Fragment"], null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"]("div", {
1000
+ tabIndex: "0",
1001
+ onFocus: this.handleFocusPreviousFocusableElement,
1002
+ style: {
1003
+ position: "fixed"
1004
+ }
1005
+ }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"]("div", {
1006
+ ref: this.getComponentRootNode
1007
+ }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_initial_focus_js__WEBPACK_IMPORTED_MODULE_3__[/* default */ "a"], {
1008
+ initialFocusId: this.props.initialFocusId
1009
+ }, children)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"]("div", {
1010
+ tabIndex: "0",
1011
+ onFocus: this.handleFocusNextFocusableElement,
1012
+ style: {
1013
+ position: "fixed"
857
1014
  }
858
- }, /*#__PURE__*/external_react_["createElement"](wonder_blocks_core_["IDProvider"], {
859
- id: id,
860
- scope: "popover"
861
- }, uniqueId => /*#__PURE__*/external_react_["createElement"](external_react_["Fragment"], null, /*#__PURE__*/external_react_["createElement"](popover_anchor_PopoverAnchor, {
862
- anchorRef: this.updateRef,
863
- id: `${uniqueId}-anchor`,
864
- "aria-controls": uniqueId,
865
- "aria-expanded": opened ? "true" : "false",
866
- onClick: this.handleOpen
867
- }, children), popperHost && opened && /*#__PURE__*/external_react_dom_default.a.createPortal(this.renderPopper(uniqueId), popperHost))), dismissEnabled && opened && /*#__PURE__*/external_react_["createElement"](popover_event_listener_PopoverEventListener, {
868
- onClose: this.handleClose,
869
- contentRef: this.contentRef
870
1015
  }));
871
1016
  }
872
1017
 
873
1018
  }
874
- popover_Popover.defaultProps = {
875
- placement: "top"
876
- };
877
- // EXTERNAL MODULE: external "@khanacademy/wonder-blocks-spacing"
878
- var wonder_blocks_spacing_ = __webpack_require__(1);
879
- var wonder_blocks_spacing_default = /*#__PURE__*/__webpack_require__.n(wonder_blocks_spacing_);
880
-
881
- // EXTERNAL MODULE: external "@khanacademy/wonder-blocks-typography"
882
- var wonder_blocks_typography_ = __webpack_require__(8);
883
1019
 
884
- // EXTERNAL MODULE: external "@khanacademy/wonder-blocks-icon"
885
- var wonder_blocks_icon_ = __webpack_require__(10);
886
-
887
- // EXTERNAL MODULE: external "@khanacademy/wonder-blocks-icon-button"
888
- var wonder_blocks_icon_button_ = __webpack_require__(11);
889
- var wonder_blocks_icon_button_default = /*#__PURE__*/__webpack_require__.n(wonder_blocks_icon_button_);
890
-
891
- // CONCATENATED MODULE: ./packages/wonder-blocks-popover/src/components/close-button.js
1020
+ /***/ }),
1021
+ /* 17 */
1022
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
892
1023
 
1024
+ "use strict";
1025
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return InitialFocus; });
1026
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
1027
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1028
+ /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
1029
+ /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__);
1030
+ /* harmony import */ var _util_util_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7);
893
1031
 
894
1032
 
895
1033
 
896
1034
 
897
1035
  /**
898
- * This is the visual component rendering the close button that is rendered
899
- * inside the PopoverContentCore. It’s rendered if closeButtonVisible is set
900
- * true.
1036
+ * This component finds which element (from within the children) needs to
1037
+ * receive focus. After that, the children is rendered with the focus assigned.
901
1038
  */
902
- class close_button_CloseButton extends external_react_["Component"] {
903
- render() {
904
- const {
905
- light,
906
- "aria-label": ariaLabel,
907
- style,
908
- testId
909
- } = this.props;
910
- return /*#__PURE__*/external_react_["createElement"](popover_context.Consumer, null, ({
911
- close
912
- }) => {
913
- return /*#__PURE__*/external_react_["createElement"](wonder_blocks_icon_button_default.a, {
914
- icon: wonder_blocks_icon_["icons"].dismiss,
915
- "aria-label": ariaLabel,
916
- onClick: close,
917
- kind: light ? "primary" : "tertiary",
918
- light: light,
919
- style: style,
920
- testId: testId
921
- });
922
- });
923
- }
924
-
925
- }
926
- close_button_CloseButton.defaultProps = {
927
- light: true,
928
- "aria-label": "Close Popover"
929
- };
930
- // CONCATENATED MODULE: ./packages/wonder-blocks-popover/src/components/popover-content-core.js
931
-
1039
+ class InitialFocus extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
1040
+ constructor(...args) {
1041
+ super(...args);
932
1042
 
1043
+ this.setInitialFocusableElement = node => {
1044
+ // 1. try to get element specified by the user
1045
+ // 2. get first occurence from list of focusable elements
1046
+ // 3. If no focusable elements are found, get the container itself
1047
+ const firstFocusableElement = this.maybeGetInitialFocusElement(node) || this.maybeGetFirstFocusableElement(node) || node;
933
1048
 
1049
+ if (firstFocusableElement === node) {
1050
+ // add tabIndex to make the container focusable
1051
+ node.tabIndex = -1;
1052
+ } // using timeout to prevent page jumps when focusing on this element
934
1053
 
935
1054
 
1055
+ setTimeout(() => {
1056
+ firstFocusableElement.focus();
1057
+ }, 0);
1058
+ };
1059
+ }
936
1060
 
1061
+ componentDidMount() {
1062
+ const node = react_dom__WEBPACK_IMPORTED_MODULE_1__["findDOMNode"](this);
937
1063
 
938
- /**
939
- * This is the base popover container. It’s used internally by all the variants.
940
- * Also, it can be used to create flexible popovers.
941
- */
942
- class popover_content_core_PopoverContentCore extends external_react_["Component"] {
943
- render() {
944
- const {
945
- children,
946
- closeButtonLight,
947
- closeButtonLabel,
948
- closeButtonVisible,
949
- color,
950
- style,
951
- testId
952
- } = this.props;
953
- return /*#__PURE__*/external_react_["createElement"](wonder_blocks_core_["View"], {
954
- "data-test-id": testId,
955
- style: [popover_content_core_styles.content, color !== "white" && popover_content_core_styles[color], style]
956
- }, closeButtonVisible && /*#__PURE__*/external_react_["createElement"](close_button_CloseButton, {
957
- "aria-label": closeButtonLabel,
958
- light: closeButtonLight || color !== "white",
959
- style: popover_content_core_styles.closeButton,
960
- testId: `${testId || "popover"}-close-btn`
961
- }), children);
1064
+ if (!node) {
1065
+ return;
1066
+ } // try to focus on the first focussable element
1067
+
1068
+
1069
+ this.setInitialFocusableElement(node);
962
1070
  }
1071
+ /**
1072
+ * Gets the focusable element and applies focus to it
1073
+ */
963
1074
 
964
- }
965
- popover_content_core_PopoverContentCore.defaultProps = {
966
- color: "white",
967
- closeButtonLight: false,
968
- closeButtonVisible: false
969
- };
970
- const popover_content_core_styles = external_aphrodite_["StyleSheet"].create({
971
- content: {
972
- borderRadius: wonder_blocks_spacing_default.a.xxxSmall_4,
973
- border: `solid 1px ${wonder_blocks_color_default.a.offBlack16}`,
974
- backgroundColor: wonder_blocks_color_default.a.white,
975
- boxShadow: `0 ${wonder_blocks_spacing_default.a.xSmall_8}px ${wonder_blocks_spacing_default.a.xSmall_8}px 0 ${wonder_blocks_color_default.a.offBlack8}`,
976
- margin: 0,
977
- maxWidth: wonder_blocks_spacing_default.a.medium_16 * 18,
978
- // 288px
979
- padding: wonder_blocks_spacing_default.a.large_24,
980
- overflow: "hidden",
981
- justifyContent: "center"
982
- },
983
1075
 
984
1076
  /**
985
- * Theming
1077
+ * Returns an element specified by the user
986
1078
  */
987
- blue: {
988
- backgroundColor: wonder_blocks_color_default.a.blue,
989
- color: wonder_blocks_color_default.a.white
990
- },
991
- darkBlue: {
992
- backgroundColor: wonder_blocks_color_default.a.darkBlue,
993
- color: wonder_blocks_color_default.a.white
994
- },
1079
+ maybeGetInitialFocusElement(node) {
1080
+ const {
1081
+ initialFocusId
1082
+ } = this.props;
1083
+
1084
+ if (!initialFocusId) {
1085
+ return null;
1086
+ }
995
1087
 
1088
+ return node.querySelector(`#${initialFocusId}`);
1089
+ }
996
1090
  /**
997
- * elements
1091
+ * Returns the first focusable element found inside the children
998
1092
  */
999
- closeButton: {
1000
- position: "absolute",
1001
- right: wonder_blocks_spacing_default.a.xSmall_8,
1002
- top: wonder_blocks_spacing_default.a.xSmall_8,
1003
- // Allows the button to be above the title and/or custom content
1004
- zIndex: 1
1093
+
1094
+
1095
+ maybeGetFirstFocusableElement(node) {
1096
+ // get a collection of elements that can be focused
1097
+ const focusableElements = Object(_util_util_js__WEBPACK_IMPORTED_MODULE_2__[/* findFocusableNodes */ "a"])(node);
1098
+
1099
+ if (!focusableElements.length) {
1100
+ return null;
1101
+ } // if found, return the first focusable element
1102
+
1103
+
1104
+ return focusableElements[0];
1005
1105
  }
1006
- });
1007
- // CONCATENATED MODULE: ./packages/wonder-blocks-popover/src/components/popover-content.js
1008
1106
 
1107
+ render() {
1108
+ return this.props.children;
1109
+ }
1009
1110
 
1111
+ }
1010
1112
 
1113
+ /***/ }),
1114
+ /* 18 */
1115
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1011
1116
 
1117
+ "use strict";
1118
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return PopoverEventListener; });
1119
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
1120
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1121
+ /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
1122
+ /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__);
1012
1123
 
1013
1124
 
1014
1125
 
1015
- // Created to add custom styles to the icon or image elements
1016
- const StyledImage = Object(wonder_blocks_core_["addStyle"])("img");
1017
1126
  /**
1018
- * This is the container that is consumed by all the predefined variations. Its
1019
- * main responsibility is populate the contents depending on the variation used.
1127
+ * A component that, when mounted, calls `onClose` when certain events occur.
1128
+ * This includes when pressing Escape or clicking outside the Popover.
1129
+ * @see @khanacademy/wonder-blocks-modal/components/modal-launcher.js
1020
1130
  */
1021
-
1022
- class popover_content_PopoverContent extends external_react_["Component"] {
1131
+ class PopoverEventListener extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
1023
1132
  constructor(...args) {
1024
1133
  super(...args);
1134
+ this.state = {
1135
+ isFirstClick: true
1136
+ };
1025
1137
 
1026
- this.maybeRenderImage = ({
1027
- placement
1028
- }) => {
1029
- const {
1030
- image
1031
- } = this.props;
1032
-
1033
- if (!image) {
1034
- return null;
1138
+ this._handleKeyup = e => {
1139
+ // We check the key as that's keyboard layout agnostic and also avoids
1140
+ // the minefield of deprecated number type properties like keyCode and
1141
+ // which, with the replacement code, which uses a string instead.
1142
+ if (e.key === "Escape") {
1143
+ // Stop the event going any further.
1144
+ // For cancellation events, like the Escape key, we generally should
1145
+ // air on the side of caution and only allow it to cancel one thing.
1146
+ // So, it's polite for us to stop propagation of the event.
1147
+ // Otherwise, we end up with UX where one Escape key press
1148
+ // unexpectedly cancels multiple things.
1149
+ e.preventDefault();
1150
+ e.stopPropagation();
1151
+ this.props.onClose();
1035
1152
  }
1036
-
1037
- return /*#__PURE__*/external_react_["createElement"](wonder_blocks_core_["View"], {
1038
- style: [popover_content_styles.image, placement === "bottom" && popover_content_styles.imageToBottom]
1039
- }, image);
1040
1153
  };
1041
1154
 
1042
- this.maybeRenderIcon = () => {
1043
- const {
1044
- icon
1045
- } = this.props;
1155
+ this._handleClick = e => {
1156
+ var _this$props$contentRe;
1046
1157
 
1047
- if (!icon) {
1048
- return null;
1158
+ // Prevents the problem where clicking the trigger button
1159
+ // triggers a click event and immediately closes the popover.
1160
+ if (this.state.isFirstClick) {
1161
+ this.setState({
1162
+ isFirstClick: false
1163
+ });
1164
+ return;
1049
1165
  }
1050
1166
 
1051
- return /*#__PURE__*/external_react_["createElement"](wonder_blocks_core_["View"], {
1052
- style: popover_content_styles.iconContainer
1053
- }, typeof icon !== "string" ? icon : /*#__PURE__*/external_react_["createElement"](StyledImage, {
1054
- src: icon,
1055
- style: popover_content_styles.icon
1056
- }));
1057
- };
1058
-
1059
- this.maybeRenderActions = close => {
1060
- const {
1061
- actions
1062
- } = this.props;
1167
+ const node = react_dom__WEBPACK_IMPORTED_MODULE_1___default.a.findDOMNode((_this$props$contentRe = this.props.contentRef) == null ? void 0 : _this$props$contentRe.current);
1063
1168
 
1064
- if (!actions) {
1065
- return null;
1169
+ if (node && !node.contains(e.target)) {
1170
+ // Stop the event going any further.
1171
+ // Only allow click to cancel one thing at a time.
1172
+ e.preventDefault();
1173
+ e.stopPropagation();
1174
+ this.props.onClose();
1066
1175
  }
1067
-
1068
- return /*#__PURE__*/external_react_["createElement"](wonder_blocks_core_["View"], {
1069
- style: popover_content_styles.actions
1070
- }, typeof actions === "function" ? actions({
1071
- close: close
1072
- }) : actions);
1073
1176
  };
1074
1177
  }
1075
1178
 
1076
1179
  componentDidMount() {
1077
- const {
1078
- icon,
1079
- image
1080
- } = this.props; // this runtime check is added to support <svg> and <img> elements
1081
- // inside the image prop
1082
-
1083
- if (image && icon) {
1084
- throw new Error("'image' and 'icon' cannot be used at the same time. You can fix this by either removing 'image' or 'icon' from your instance.");
1085
- }
1180
+ window.addEventListener("keyup", this._handleKeyup);
1181
+ window.addEventListener("click", this._handleClick);
1086
1182
  }
1087
- /**
1088
- * Runtime validation in case we try to use an invalid shape
1089
- */
1090
1183
 
1184
+ componentWillUnmount() {
1185
+ window.removeEventListener("keyup", this._handleKeyup);
1186
+ window.removeEventListener("click", this._handleClick);
1187
+ }
1091
1188
 
1092
- validateProps({
1093
- placement
1094
- }) {
1095
- // illustration popover can't be placed horizontally
1096
- if (this.props.image && (placement === "left" || placement === "right")) {
1097
- throw new Error("'image' can only be vertically placed. You can fix this by either changing `placement` to `top` or `bottom` or removing the `image` prop inside `content`.");
1098
- }
1189
+ render() {
1190
+ return null;
1099
1191
  }
1100
1192
 
1193
+ }
1194
+
1195
+ /***/ }),
1196
+ /* 19 */
1197
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1198
+
1199
+ "use strict";
1200
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return CloseButton; });
1201
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
1202
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1203
+ /* harmony import */ var _khanacademy_wonder_blocks_icon__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(20);
1204
+ /* harmony import */ var _khanacademy_wonder_blocks_icon__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_icon__WEBPACK_IMPORTED_MODULE_1__);
1205
+ /* harmony import */ var _khanacademy_wonder_blocks_icon_button__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(21);
1206
+ /* harmony import */ var _khanacademy_wonder_blocks_icon_button__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_icon_button__WEBPACK_IMPORTED_MODULE_2__);
1207
+ /* harmony import */ var _popover_context_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5);
1208
+
1209
+
1210
+
1211
+
1212
+
1213
+ /**
1214
+ * This is the visual component rendering the close button that is rendered
1215
+ * inside the PopoverContentCore. It’s rendered if closeButtonVisible is set
1216
+ * true.
1217
+ */
1218
+ class CloseButton extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
1101
1219
  render() {
1102
1220
  const {
1103
- closeButtonLabel,
1104
- closeButtonVisible,
1105
- content,
1106
- emphasized = undefined,
1107
- icon,
1108
- image,
1221
+ light,
1222
+ "aria-label": ariaLabel,
1109
1223
  style,
1110
- title,
1111
1224
  testId
1112
1225
  } = this.props;
1113
- return /*#__PURE__*/external_react_["createElement"](popover_context.Consumer, null, ({
1114
- close,
1115
- placement
1226
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_popover_context_js__WEBPACK_IMPORTED_MODULE_3__[/* default */ "a"].Consumer, null, ({
1227
+ close
1116
1228
  }) => {
1117
- // verify if the props are correct
1118
- this.validateProps({
1119
- close,
1120
- placement
1121
- });
1122
- return /*#__PURE__*/external_react_["createElement"](popover_content_core_PopoverContentCore, {
1123
- color: emphasized ? "blue" : "white",
1124
- closeButtonLight: image && placement === "top",
1125
- closeButtonLabel: closeButtonLabel,
1126
- closeButtonVisible: closeButtonVisible,
1229
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_khanacademy_wonder_blocks_icon_button__WEBPACK_IMPORTED_MODULE_2___default.a, {
1230
+ icon: _khanacademy_wonder_blocks_icon__WEBPACK_IMPORTED_MODULE_1__["icons"].dismiss,
1231
+ "aria-label": ariaLabel,
1232
+ onClick: close,
1233
+ kind: light ? "primary" : "tertiary",
1234
+ light: light,
1127
1235
  style: style,
1128
1236
  testId: testId
1129
- }, /*#__PURE__*/external_react_["createElement"](wonder_blocks_core_["View"], {
1130
- style: !!icon && popover_content_styles.withIcon
1131
- }, this.maybeRenderImage({
1132
- placement
1133
- }), this.maybeRenderIcon(), /*#__PURE__*/external_react_["createElement"](wonder_blocks_core_["View"], {
1134
- style: popover_content_styles.text
1135
- }, /*#__PURE__*/external_react_["createElement"](wonder_blocks_typography_["HeadingSmall"], {
1136
- style: popover_content_styles.title
1137
- }, title), /*#__PURE__*/external_react_["createElement"](wonder_blocks_typography_["Body"], null, content))), this.maybeRenderActions(close));
1237
+ });
1138
1238
  });
1139
1239
  }
1140
1240
 
1141
1241
  }
1142
- popover_content_PopoverContent.defaultProps = {
1143
- closeButtonVisible: false
1242
+ CloseButton.defaultProps = {
1243
+ light: true,
1244
+ "aria-label": "Close Popover"
1144
1245
  };
1145
- const popover_content_styles = external_aphrodite_["StyleSheet"].create({
1146
- /**
1147
- * Shared styles
1148
- */
1149
- actions: {
1150
- marginTop: wonder_blocks_spacing_default.a.large_24,
1151
- flexDirection: "row",
1152
- alignItems: "center",
1153
- justifyContent: "flex-end"
1154
- },
1155
- text: {
1156
- justifyContent: "center"
1157
- },
1158
- title: {
1159
- marginBottom: wonder_blocks_spacing_default.a.xSmall_8
1160
- },
1161
1246
 
1162
- /**
1163
- * Icon styles
1164
- */
1165
- iconContainer: {
1166
- alignItems: "center",
1167
- justifyContent: "center",
1168
- height: wonder_blocks_spacing_default.a.xxxLarge_64,
1169
- width: wonder_blocks_spacing_default.a.xxxLarge_64,
1170
- minWidth: wonder_blocks_spacing_default.a.xxxLarge_64,
1171
- marginRight: wonder_blocks_spacing_default.a.medium_16,
1172
- overflow: "hidden"
1173
- },
1174
- icon: {
1175
- width: "100%"
1176
- },
1177
- withIcon: {
1178
- flexDirection: "row"
1179
- },
1247
+ /***/ }),
1248
+ /* 20 */
1249
+ /***/ (function(module, exports) {
1250
+
1251
+ module.exports = require("@khanacademy/wonder-blocks-icon");
1252
+
1253
+ /***/ }),
1254
+ /* 21 */
1255
+ /***/ (function(module, exports) {
1256
+
1257
+ module.exports = require("@khanacademy/wonder-blocks-icon-button");
1258
+
1259
+ /***/ }),
1260
+ /* 22 */
1261
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1262
+
1263
+ "use strict";
1264
+ __webpack_require__.r(__webpack_exports__);
1265
+ /* harmony import */ var _components_popover_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(11);
1266
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Popover", function() { return _components_popover_js__WEBPACK_IMPORTED_MODULE_0__["a"]; });
1267
+
1268
+ /* harmony import */ var _components_popover_content_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12);
1269
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "PopoverContent", function() { return _components_popover_content_js__WEBPACK_IMPORTED_MODULE_1__["a"]; });
1270
+
1271
+ /* harmony import */ var _components_popover_content_core_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(8);
1272
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "PopoverContentCore", function() { return _components_popover_content_core_js__WEBPACK_IMPORTED_MODULE_2__["a"]; });
1180
1273
 
1181
- /**
1182
- * Illustration styles
1183
- */
1184
- image: {
1185
- marginBottom: wonder_blocks_spacing_default.a.large_24,
1186
- marginLeft: -wonder_blocks_spacing_default.a.large_24,
1187
- marginRight: -wonder_blocks_spacing_default.a.large_24,
1188
- marginTop: -wonder_blocks_spacing_default.a.large_24,
1189
- width: `calc(100% + ${wonder_blocks_spacing_default.a.large_24 * 2}px)`
1190
- },
1191
- imageToBottom: {
1192
- marginBottom: -wonder_blocks_spacing_default.a.large_24,
1193
- marginTop: wonder_blocks_spacing_default.a.large_24,
1194
- order: 1
1195
- }
1196
- });
1197
- // CONCATENATED MODULE: ./packages/wonder-blocks-popover/src/index.js
1198
1274
 
1199
1275
 
1200
1276