@khanacademy/wonder-blocks-popover 7.0.0 → 7.0.2

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 CHANGED
@@ -1,5 +1,22 @@
1
1
  # @khanacademy/wonder-blocks-popover
2
2
 
3
+ ## 7.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 248e2f5: Make popover content scroll when it doesn't fit in the available space (e.g. on small screens or at up to 400% zoom). The content fades out at the edges to indicate there's more content, and the scrollable area becomes keyboard focusable while it overflows. It is named after the popover (its title or `aria-label`), so screen readers announce it when it receives focus.
8
+ - Updated dependencies [7d414bb]
9
+ - Updated dependencies [248e2f5]
10
+ - Updated dependencies [8b93331]
11
+ - @khanacademy/wonder-blocks-floating@0.2.0
12
+ - @khanacademy/wonder-blocks-layout@3.2.0
13
+
14
+ ## 7.0.1
15
+
16
+ ### Patch Changes
17
+
18
+ - 51600dd: Convert `CloseButton` from a class component to a functional component
19
+
3
20
  ## 7.0.0
4
21
 
5
22
  ### Major Changes
@@ -10,16 +10,10 @@ type Props = AriaProps & {
10
10
  */
11
11
  testId?: string;
12
12
  };
13
- type DefaultProps = {
14
- ["aria-label"]: Props["aria-label"];
15
- };
16
13
  /**
17
14
  * This is the visual component rendering the close button that is rendered
18
15
  * inside the PopoverContentCore. It’s rendered if closeButtonVisible is set
19
16
  * true.
20
17
  */
21
- export default class CloseButton extends React.Component<Props> {
22
- static defaultProps: DefaultProps;
23
- render(): React.ReactNode;
24
- }
25
- export {};
18
+ declare const CloseButton: (props: Props) => React.JSX.Element;
19
+ export default CloseButton;
@@ -16,6 +16,17 @@ import type { StyleType } from "@khanacademy/wonder-blocks-core";
16
16
  * no bubble around it and you need to supply your own container styling. Inside
17
17
  * a `Popover` it looks like a popover with no extra work.
18
18
  *
19
+ * ### Overflow
20
+ *
21
+ * Inside a `Popover`, the content is constrained to the space available in the
22
+ * viewport (or document). When it doesn't fit (e.g. on small screens or at
23
+ * high zoom levels), the content (including the close button) scrolls, and
24
+ * it fades out at the edges to hint that there's more content to scroll to.
25
+ * The scrollable area becomes keyboard focusable while it overflows, so
26
+ * keyboard users can scroll it too. It's named after the popover (via its
27
+ * title or `aria-label`), so screen readers announce it when it's focused.
28
+ * Pass an `aria-label` to `PopoverContentCore` to use a different name.
29
+ *
19
30
  * ### Usage
20
31
  *
21
32
  * ```jsx
@@ -13,6 +13,14 @@ export type PopoverContextType = {
13
13
  * element changes its placement.
14
14
  */
15
15
  placement?: Placement;
16
+ /**
17
+ * The accessible name of the popover dialog (either an `aria-label`, or
18
+ * the id of the element that labels it). This is used to name the
19
+ * scrollable area of the popover content, so screen readers can announce
20
+ * it when it receives focus.
21
+ */
22
+ "aria-label"?: string;
23
+ "aria-labelledby"?: string;
16
24
  };
17
25
  /**
18
26
  * This context is being used for two reasons:
@@ -23,6 +31,9 @@ export type PopoverContextType = {
23
31
  *
24
32
  * 2. Keeps a reference of the TooltipPopper's `placement` value. It can be one
25
33
  * of the following values: "top", "bottom", "left" or "right".
34
+ *
35
+ * 3. Passes down the accessible name of the popover dialog, so
36
+ * `PopoverContentCore` can name its scrollable area.
26
37
  */
27
38
  declare const PopoverContext: React.Context<PopoverContextType>;
28
39
  export default PopoverContext;
package/dist/es/index.js CHANGED
@@ -3,8 +3,9 @@ import * as React from 'react';
3
3
  import { useMergeRefs, getElementRef, FloatingReferenceAttributeName, canAcceptRef, Floating } from '@khanacademy/wonder-blocks-floating';
4
4
  import { View, addStyle } from '@khanacademy/wonder-blocks-core';
5
5
  import { StyleSheet } from 'aphrodite';
6
- import { sizing } from '@khanacademy/wonder-blocks-tokens';
6
+ import { border, sizing } from '@khanacademy/wonder-blocks-tokens';
7
7
  import { Heading, BodyText } from '@khanacademy/wonder-blocks-typography';
8
+ import { ScrollableView } from '@khanacademy/wonder-blocks-layout';
8
9
  import { actionStyles } from '@khanacademy/wonder-blocks-styles';
9
10
  import xIcon from '@phosphor-icons/core/regular/x.svg';
10
11
  import IconButton from '@khanacademy/wonder-blocks-icon-button';
@@ -15,11 +16,11 @@ const PopoverAnchor=React.forwardRef(function PopoverAnchor(props,ref){const{chi
15
16
 
16
17
  const PopoverDialog=React.forwardRef(function PopoverDialog(props,ref){const{children,id,placement,"aria-describedby":ariaDescribedby,"aria-labelledby":ariaLabelledBy,"aria-label":ariaLabel}=props;return jsx(View,{"aria-label":ariaLabel,"aria-describedby":ariaDescribedby,"aria-labelledby":ariaLabelledBy,id:id,role:"dialog",ref:ref,"data-placement":placement,children:children})});PopoverDialog.displayName="PopoverDialog";
17
18
 
18
- function Popover(props){const{children,content,placement:placementProp="top",dismissEnabled,id,closedFocusId,initialFocusId,opened:openedProp,onClose,showTail=true,portal=true,rootBoundary="viewport",viewportPadding,testId,"aria-label":ariaLabel,"aria-describedby":ariaDescribedBy}=props;const isControlled=typeof openedProp==="boolean";const[internalOpened,setInternalOpened]=React.useState(!!openedProp);const opened=isControlled?openedProp:internalOpened;const[placement,setPlacement]=React.useState(placementProp);const initialFocusRef=React.useRef(null);const returnFocusRef=React.useRef(null);const closeFiredRef=React.useRef(false);const generatedUniqueId=React.useId();const uniqueId=id??generatedUniqueId;React.useEffect(()=>{if(opened&&closedFocusId){returnFocusRef.current=document.getElementById(closedFocusId);}},[opened,closedFocusId]);const handleOpenChange=React.useCallback(nextOpened=>{if(!isControlled){setInternalOpened(nextOpened);}if(nextOpened){closeFiredRef.current=false;}else if(!closeFiredRef.current){closeFiredRef.current=true;onClose?.();}},[isControlled,onClose]);const handleClose=React.useCallback(()=>{handleOpenChange(false);},[handleOpenChange]);const handleAnchorClick=React.useCallback(()=>{if(!opened){handleOpenChange(true);}else if(dismissEnabled){handleOpenChange(false);}},[opened,dismissEnabled,handleOpenChange]);const handlePlacementChange=React.useCallback(nextPlacement=>{setPlacement(nextPlacement.split("-")[0]);},[]);const handleDialogRef=React.useCallback(node=>{if(node&&initialFocusId){initialFocusRef.current=node.querySelector(`#${initialFocusId}`);}},[initialFocusId]);const renderContent=()=>{const popoverContents=typeof content==="function"?content({close:handleClose}):content;return React.cloneElement(popoverContents,{uniqueId})};const describedBy=ariaDescribedBy||`${uniqueId}-content`;const ariaLabelledBy=ariaLabel?undefined:`${uniqueId}-title`;const dialog=jsx(PopoverDialog,{ref:handleDialogRef,id:uniqueId,placement:placement,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy,"aria-describedby":describedBy,children:renderContent()});return jsx(PopoverContext.Provider,{value:{close:handleClose,placement:placement},children:jsx(Floating,{open:opened,strategy:"fixed",onOpenChange:handleOpenChange,placement:placementProp,onPlacementChange:handlePlacementChange,content:dialog,dismissEnabled:!!dismissEnabled,closeOnFocusOut:!!dismissEnabled,showArrow:showTail,portal:portal,rootBoundary:rootBoundary,shiftPadding:viewportPadding,initialFocusRef:initialFocusId?initialFocusRef:undefined,returnFocus:closedFocusId?returnFocusRef:true,testId:testId,children:jsx(PopoverAnchor,{id:`${uniqueId}-anchor`,"aria-controls":uniqueId,"aria-expanded":opened?"true":"false",onClick:handleAnchorClick,children:children})})})}
19
+ function Popover(props){const{children,content,placement:placementProp="top",dismissEnabled,id,closedFocusId,initialFocusId,opened:openedProp,onClose,showTail=true,portal=true,rootBoundary="viewport",viewportPadding,testId,"aria-label":ariaLabel,"aria-describedby":ariaDescribedBy}=props;const isControlled=typeof openedProp==="boolean";const[internalOpened,setInternalOpened]=React.useState(!!openedProp);const opened=isControlled?openedProp:internalOpened;const[placement,setPlacement]=React.useState(placementProp);const initialFocusRef=React.useRef(null);const returnFocusRef=React.useRef(null);const closeFiredRef=React.useRef(false);const generatedUniqueId=React.useId();const uniqueId=id??generatedUniqueId;React.useEffect(()=>{if(opened&&closedFocusId){returnFocusRef.current=document.getElementById(closedFocusId);}},[opened,closedFocusId]);const handleOpenChange=React.useCallback(nextOpened=>{if(!isControlled){setInternalOpened(nextOpened);}if(nextOpened){closeFiredRef.current=false;}else if(!closeFiredRef.current){closeFiredRef.current=true;onClose?.();}},[isControlled,onClose]);const handleClose=React.useCallback(()=>{handleOpenChange(false);},[handleOpenChange]);const handleAnchorClick=React.useCallback(()=>{if(!opened){handleOpenChange(true);}else if(dismissEnabled){handleOpenChange(false);}},[opened,dismissEnabled,handleOpenChange]);const handlePlacementChange=React.useCallback(nextPlacement=>{setPlacement(nextPlacement.split("-")[0]);},[]);const handleDialogRef=React.useCallback(node=>{if(node&&initialFocusId){initialFocusRef.current=node.querySelector(`#${initialFocusId}`);}},[initialFocusId]);const renderContent=()=>{const popoverContents=typeof content==="function"?content({close:handleClose}):content;return React.cloneElement(popoverContents,{uniqueId})};const describedBy=ariaDescribedBy||`${uniqueId}-content`;const ariaLabelledBy=ariaLabel?undefined:`${uniqueId}-title`;const dialog=jsx(PopoverDialog,{ref:handleDialogRef,id:uniqueId,placement:placement,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy,"aria-describedby":describedBy,children:renderContent()});return jsx(PopoverContext.Provider,{value:{close:handleClose,placement:placement,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy},children:jsx(Floating,{open:opened,strategy:"fixed",onOpenChange:handleOpenChange,placement:placementProp,onPlacementChange:handlePlacementChange,content:dialog,constrainSize:true,dismissEnabled:!!dismissEnabled,closeOnFocusOut:!!dismissEnabled,showArrow:showTail,portal:portal,rootBoundary:rootBoundary,shiftPadding:viewportPadding,initialFocusRef:initialFocusId?initialFocusRef:undefined,returnFocus:closedFocusId?returnFocusRef:true,testId:testId,children:jsx(PopoverAnchor,{id:`${uniqueId}-anchor`,"aria-controls":uniqueId,"aria-expanded":opened?"true":"false",onClick:handleAnchorClick,children:children})})})}
19
20
 
20
- class CloseButton extends React.Component{render(){const{"aria-label":ariaLabel="Close Popover",style,testId}=this.props;return jsx(PopoverContext.Consumer,{children:({close})=>{return jsx(IconButton,{icon:xIcon,"aria-label":ariaLabel,onClick:close,kind:"tertiary",actionType:"neutral",style:style,testId:testId})}})}}CloseButton.defaultProps={"aria-label":"Close Popover"};
21
+ const CloseButton=props=>{const{"aria-label":ariaLabel="Close Popover",style,testId}=props;const{close}=React.useContext(PopoverContext);return jsx(IconButton,{icon:xIcon,"aria-label":ariaLabel,onClick:close,kind:"tertiary",actionType:"neutral",style:style,testId:testId})};
21
22
 
22
- const PopoverContentCore=React.forwardRef(function PopoverContentCore({"aria-label":ariaLabel,children,closeButtonLight=false,closeButtonLabel,closeButtonVisible=false,style,testId},ref){return jsxs(View,{testId:testId,style:[styles$1.content,style],"aria-label":ariaLabel,ref:ref,children:[closeButtonVisible&&jsx(CloseButton,{"aria-label":closeButtonLabel,style:[styles$1.closeButton,closeButtonLight&&actionStyles.inverse],testId:`${testId||"popover"}-close-btn`}),children]})});PopoverContentCore.displayName="PopoverContentCore";const styles$1=StyleSheet.create({content:{margin:0,maxInlineSize:`calc(${sizing.size_160} * 18)`,padding:sizing.size_240,overflow:"hidden",justifyContent:"center"},closeButton:{margin:0,position:"absolute",insetInlineEnd:sizing.size_040,insetBlockStart:sizing.size_040,zIndex:1}});
23
+ const PopoverContentCore=React.forwardRef(function PopoverContentCore({"aria-label":ariaLabel,children,closeButtonLight=false,closeButtonLabel,closeButtonVisible=false,style,testId},ref){const popoverContext=React.useContext(PopoverContext);const labelProps=ariaLabel?{"aria-label":ariaLabel}:{"aria-label":popoverContext["aria-label"],"aria-labelledby":popoverContext["aria-labelledby"]};return jsx(ScrollableView,{...labelProps,style:styles$1.scrollableView,children:jsxs(View,{testId:testId,style:[styles$1.content,style],ref:ref,children:[closeButtonVisible&&jsx(CloseButton,{"aria-label":closeButtonLabel,style:[styles$1.closeButton,closeButtonLight&&actionStyles.inverse],testId:`${testId||"popover"}-close-btn`}),children]})})});PopoverContentCore.displayName="PopoverContentCore";const styles$1=StyleSheet.create({scrollableView:{borderRadius:`calc(${border.radius.radius_040} - ${border.width.thin})`},content:{margin:0,maxInlineSize:`calc(${sizing.size_160} * 18)`,padding:sizing.size_240,overflow:"hidden",justifyContent:"center",flexShrink:0},closeButton:{margin:0,position:"absolute",insetInlineEnd:sizing.size_040,insetBlockStart:sizing.size_040,zIndex:1}});
23
24
 
24
25
  const StyledImg=addStyle("img");const PopoverContent=React.forwardRef(function PopoverContent(props,ref){const{actions,closeButtonLabel,closeButtonVisible=false,content,icon,iconAlt,image,style,title,titleHeadingTag,testId,uniqueId}=props;const{close,placement}=React.useContext(PopoverContext);React.useEffect(()=>{if(image&&icon){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.")}},[image,icon]);const validateProps=React.useCallback(({placement})=>{if(image&&(placement==="left"||placement==="right")){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`.")}},[image]);const maybeRenderImage=context=>{if(!image){return null}return jsx(View,{style:[styles.image,context.placement==="bottom"&&styles.imageToBottom],children:image})};const maybeRenderIcon=()=>{if(!icon){return null}return jsx(View,{style:styles.iconContainer,children:typeof icon!=="string"?icon:jsx(StyledImg,{src:icon,style:styles.icon,alt:iconAlt||""})})};const maybeRenderActions=close=>{if(!actions){return null}return jsx(View,{style:styles.actions,children:typeof actions==="function"?actions({close:close}):actions})};validateProps({close,placement});return jsxs(PopoverContentCore,{closeButtonLight:!!image&&placement==="top",closeButtonLabel:closeButtonLabel,closeButtonVisible:closeButtonVisible,style:style,testId:testId,ref:ref,children:[jsxs(View,{style:!!icon&&styles.withIcon,children:[maybeRenderImage({placement}),maybeRenderIcon(),jsxs(View,{style:styles.text,children:[jsx(Heading,{size:"medium",tag:titleHeadingTag,id:`${uniqueId}-title`,style:styles.title,children:title}),jsx(BodyText,{tag:"span",id:`${uniqueId}-content`,children:content})]})]}),maybeRenderActions(close)]})});PopoverContent.displayName="PopoverContent";const styles=StyleSheet.create({actions:{marginBlockStart:sizing.size_240,flexDirection:"row",alignItems:"center",justifyContent:"flex-end"},text:{justifyContent:"center"},title:{marginBlockEnd:sizing.size_080},iconContainer:{alignItems:"center",justifyContent:"center",blockSize:sizing.size_640,inlineSize:sizing.size_640,minInlineSize:sizing.size_640,marginInlineEnd:sizing.size_160,overflow:"hidden"},icon:{width:"100%"},withIcon:{flexDirection:"row"},image:{marginBlockEnd:sizing.size_240,marginBlockStart:`calc(-1 * ${sizing.size_240})`,marginInline:`calc(-1 * ${sizing.size_240})`,inlineSize:`calc(100% + ${sizing.size_480})`},imageToBottom:{marginBlockEnd:`calc(-1 * ${sizing.size_240})`,marginBlockStart:sizing.size_240,order:1}});
25
26
 
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ var wonderBlocksCore = require('@khanacademy/wonder-blocks-core');
9
9
  var aphrodite = require('aphrodite');
10
10
  var wonderBlocksTokens = require('@khanacademy/wonder-blocks-tokens');
11
11
  var wonderBlocksTypography = require('@khanacademy/wonder-blocks-typography');
12
+ var wonderBlocksLayout = require('@khanacademy/wonder-blocks-layout');
12
13
  var wonderBlocksStyles = require('@khanacademy/wonder-blocks-styles');
13
14
  var xIcon = require('@phosphor-icons/core/regular/x.svg');
14
15
  var IconButton = require('@khanacademy/wonder-blocks-icon-button');
@@ -43,11 +44,11 @@ const PopoverAnchor=React__namespace.forwardRef(function PopoverAnchor(props,ref
43
44
 
44
45
  const PopoverDialog=React__namespace.forwardRef(function PopoverDialog(props,ref){const{children,id,placement,"aria-describedby":ariaDescribedby,"aria-labelledby":ariaLabelledBy,"aria-label":ariaLabel}=props;return jsxRuntime.jsx(wonderBlocksCore.View,{"aria-label":ariaLabel,"aria-describedby":ariaDescribedby,"aria-labelledby":ariaLabelledBy,id:id,role:"dialog",ref:ref,"data-placement":placement,children:children})});PopoverDialog.displayName="PopoverDialog";
45
46
 
46
- function Popover(props){const{children,content,placement:placementProp="top",dismissEnabled,id,closedFocusId,initialFocusId,opened:openedProp,onClose,showTail=true,portal=true,rootBoundary="viewport",viewportPadding,testId,"aria-label":ariaLabel,"aria-describedby":ariaDescribedBy}=props;const isControlled=typeof openedProp==="boolean";const[internalOpened,setInternalOpened]=React__namespace.useState(!!openedProp);const opened=isControlled?openedProp:internalOpened;const[placement,setPlacement]=React__namespace.useState(placementProp);const initialFocusRef=React__namespace.useRef(null);const returnFocusRef=React__namespace.useRef(null);const closeFiredRef=React__namespace.useRef(false);const generatedUniqueId=React__namespace.useId();const uniqueId=id??generatedUniqueId;React__namespace.useEffect(()=>{if(opened&&closedFocusId){returnFocusRef.current=document.getElementById(closedFocusId);}},[opened,closedFocusId]);const handleOpenChange=React__namespace.useCallback(nextOpened=>{if(!isControlled){setInternalOpened(nextOpened);}if(nextOpened){closeFiredRef.current=false;}else if(!closeFiredRef.current){closeFiredRef.current=true;onClose?.();}},[isControlled,onClose]);const handleClose=React__namespace.useCallback(()=>{handleOpenChange(false);},[handleOpenChange]);const handleAnchorClick=React__namespace.useCallback(()=>{if(!opened){handleOpenChange(true);}else if(dismissEnabled){handleOpenChange(false);}},[opened,dismissEnabled,handleOpenChange]);const handlePlacementChange=React__namespace.useCallback(nextPlacement=>{setPlacement(nextPlacement.split("-")[0]);},[]);const handleDialogRef=React__namespace.useCallback(node=>{if(node&&initialFocusId){initialFocusRef.current=node.querySelector(`#${initialFocusId}`);}},[initialFocusId]);const renderContent=()=>{const popoverContents=typeof content==="function"?content({close:handleClose}):content;return React__namespace.cloneElement(popoverContents,{uniqueId})};const describedBy=ariaDescribedBy||`${uniqueId}-content`;const ariaLabelledBy=ariaLabel?undefined:`${uniqueId}-title`;const dialog=jsxRuntime.jsx(PopoverDialog,{ref:handleDialogRef,id:uniqueId,placement:placement,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy,"aria-describedby":describedBy,children:renderContent()});return jsxRuntime.jsx(PopoverContext.Provider,{value:{close:handleClose,placement:placement},children:jsxRuntime.jsx(wonderBlocksFloating.Floating,{open:opened,strategy:"fixed",onOpenChange:handleOpenChange,placement:placementProp,onPlacementChange:handlePlacementChange,content:dialog,dismissEnabled:!!dismissEnabled,closeOnFocusOut:!!dismissEnabled,showArrow:showTail,portal:portal,rootBoundary:rootBoundary,shiftPadding:viewportPadding,initialFocusRef:initialFocusId?initialFocusRef:undefined,returnFocus:closedFocusId?returnFocusRef:true,testId:testId,children:jsxRuntime.jsx(PopoverAnchor,{id:`${uniqueId}-anchor`,"aria-controls":uniqueId,"aria-expanded":opened?"true":"false",onClick:handleAnchorClick,children:children})})})}
47
+ function Popover(props){const{children,content,placement:placementProp="top",dismissEnabled,id,closedFocusId,initialFocusId,opened:openedProp,onClose,showTail=true,portal=true,rootBoundary="viewport",viewportPadding,testId,"aria-label":ariaLabel,"aria-describedby":ariaDescribedBy}=props;const isControlled=typeof openedProp==="boolean";const[internalOpened,setInternalOpened]=React__namespace.useState(!!openedProp);const opened=isControlled?openedProp:internalOpened;const[placement,setPlacement]=React__namespace.useState(placementProp);const initialFocusRef=React__namespace.useRef(null);const returnFocusRef=React__namespace.useRef(null);const closeFiredRef=React__namespace.useRef(false);const generatedUniqueId=React__namespace.useId();const uniqueId=id??generatedUniqueId;React__namespace.useEffect(()=>{if(opened&&closedFocusId){returnFocusRef.current=document.getElementById(closedFocusId);}},[opened,closedFocusId]);const handleOpenChange=React__namespace.useCallback(nextOpened=>{if(!isControlled){setInternalOpened(nextOpened);}if(nextOpened){closeFiredRef.current=false;}else if(!closeFiredRef.current){closeFiredRef.current=true;onClose?.();}},[isControlled,onClose]);const handleClose=React__namespace.useCallback(()=>{handleOpenChange(false);},[handleOpenChange]);const handleAnchorClick=React__namespace.useCallback(()=>{if(!opened){handleOpenChange(true);}else if(dismissEnabled){handleOpenChange(false);}},[opened,dismissEnabled,handleOpenChange]);const handlePlacementChange=React__namespace.useCallback(nextPlacement=>{setPlacement(nextPlacement.split("-")[0]);},[]);const handleDialogRef=React__namespace.useCallback(node=>{if(node&&initialFocusId){initialFocusRef.current=node.querySelector(`#${initialFocusId}`);}},[initialFocusId]);const renderContent=()=>{const popoverContents=typeof content==="function"?content({close:handleClose}):content;return React__namespace.cloneElement(popoverContents,{uniqueId})};const describedBy=ariaDescribedBy||`${uniqueId}-content`;const ariaLabelledBy=ariaLabel?undefined:`${uniqueId}-title`;const dialog=jsxRuntime.jsx(PopoverDialog,{ref:handleDialogRef,id:uniqueId,placement:placement,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy,"aria-describedby":describedBy,children:renderContent()});return jsxRuntime.jsx(PopoverContext.Provider,{value:{close:handleClose,placement:placement,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy},children:jsxRuntime.jsx(wonderBlocksFloating.Floating,{open:opened,strategy:"fixed",onOpenChange:handleOpenChange,placement:placementProp,onPlacementChange:handlePlacementChange,content:dialog,constrainSize:true,dismissEnabled:!!dismissEnabled,closeOnFocusOut:!!dismissEnabled,showArrow:showTail,portal:portal,rootBoundary:rootBoundary,shiftPadding:viewportPadding,initialFocusRef:initialFocusId?initialFocusRef:undefined,returnFocus:closedFocusId?returnFocusRef:true,testId:testId,children:jsxRuntime.jsx(PopoverAnchor,{id:`${uniqueId}-anchor`,"aria-controls":uniqueId,"aria-expanded":opened?"true":"false",onClick:handleAnchorClick,children:children})})})}
47
48
 
48
- class CloseButton extends React__namespace.Component{render(){const{"aria-label":ariaLabel="Close Popover",style,testId}=this.props;return jsxRuntime.jsx(PopoverContext.Consumer,{children:({close})=>{return jsxRuntime.jsx(IconButton__default["default"],{icon:xIcon__default["default"],"aria-label":ariaLabel,onClick:close,kind:"tertiary",actionType:"neutral",style:style,testId:testId})}})}}CloseButton.defaultProps={"aria-label":"Close Popover"};
49
+ const CloseButton=props=>{const{"aria-label":ariaLabel="Close Popover",style,testId}=props;const{close}=React__namespace.useContext(PopoverContext);return jsxRuntime.jsx(IconButton__default["default"],{icon:xIcon__default["default"],"aria-label":ariaLabel,onClick:close,kind:"tertiary",actionType:"neutral",style:style,testId:testId})};
49
50
 
50
- const PopoverContentCore=React__namespace.forwardRef(function PopoverContentCore({"aria-label":ariaLabel,children,closeButtonLight=false,closeButtonLabel,closeButtonVisible=false,style,testId},ref){return jsxRuntime.jsxs(wonderBlocksCore.View,{testId:testId,style:[styles$1.content,style],"aria-label":ariaLabel,ref:ref,children:[closeButtonVisible&&jsxRuntime.jsx(CloseButton,{"aria-label":closeButtonLabel,style:[styles$1.closeButton,closeButtonLight&&wonderBlocksStyles.actionStyles.inverse],testId:`${testId||"popover"}-close-btn`}),children]})});PopoverContentCore.displayName="PopoverContentCore";const styles$1=aphrodite.StyleSheet.create({content:{margin:0,maxInlineSize:`calc(${wonderBlocksTokens.sizing.size_160} * 18)`,padding:wonderBlocksTokens.sizing.size_240,overflow:"hidden",justifyContent:"center"},closeButton:{margin:0,position:"absolute",insetInlineEnd:wonderBlocksTokens.sizing.size_040,insetBlockStart:wonderBlocksTokens.sizing.size_040,zIndex:1}});
51
+ const PopoverContentCore=React__namespace.forwardRef(function PopoverContentCore({"aria-label":ariaLabel,children,closeButtonLight=false,closeButtonLabel,closeButtonVisible=false,style,testId},ref){const popoverContext=React__namespace.useContext(PopoverContext);const labelProps=ariaLabel?{"aria-label":ariaLabel}:{"aria-label":popoverContext["aria-label"],"aria-labelledby":popoverContext["aria-labelledby"]};return jsxRuntime.jsx(wonderBlocksLayout.ScrollableView,{...labelProps,style:styles$1.scrollableView,children:jsxRuntime.jsxs(wonderBlocksCore.View,{testId:testId,style:[styles$1.content,style],ref:ref,children:[closeButtonVisible&&jsxRuntime.jsx(CloseButton,{"aria-label":closeButtonLabel,style:[styles$1.closeButton,closeButtonLight&&wonderBlocksStyles.actionStyles.inverse],testId:`${testId||"popover"}-close-btn`}),children]})})});PopoverContentCore.displayName="PopoverContentCore";const styles$1=aphrodite.StyleSheet.create({scrollableView:{borderRadius:`calc(${wonderBlocksTokens.border.radius.radius_040} - ${wonderBlocksTokens.border.width.thin})`},content:{margin:0,maxInlineSize:`calc(${wonderBlocksTokens.sizing.size_160} * 18)`,padding:wonderBlocksTokens.sizing.size_240,overflow:"hidden",justifyContent:"center",flexShrink:0},closeButton:{margin:0,position:"absolute",insetInlineEnd:wonderBlocksTokens.sizing.size_040,insetBlockStart:wonderBlocksTokens.sizing.size_040,zIndex:1}});
51
52
 
52
53
  const StyledImg=wonderBlocksCore.addStyle("img");const PopoverContent=React__namespace.forwardRef(function PopoverContent(props,ref){const{actions,closeButtonLabel,closeButtonVisible=false,content,icon,iconAlt,image,style,title,titleHeadingTag,testId,uniqueId}=props;const{close,placement}=React__namespace.useContext(PopoverContext);React__namespace.useEffect(()=>{if(image&&icon){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.")}},[image,icon]);const validateProps=React__namespace.useCallback(({placement})=>{if(image&&(placement==="left"||placement==="right")){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`.")}},[image]);const maybeRenderImage=context=>{if(!image){return null}return jsxRuntime.jsx(wonderBlocksCore.View,{style:[styles.image,context.placement==="bottom"&&styles.imageToBottom],children:image})};const maybeRenderIcon=()=>{if(!icon){return null}return jsxRuntime.jsx(wonderBlocksCore.View,{style:styles.iconContainer,children:typeof icon!=="string"?icon:jsxRuntime.jsx(StyledImg,{src:icon,style:styles.icon,alt:iconAlt||""})})};const maybeRenderActions=close=>{if(!actions){return null}return jsxRuntime.jsx(wonderBlocksCore.View,{style:styles.actions,children:typeof actions==="function"?actions({close:close}):actions})};validateProps({close,placement});return jsxRuntime.jsxs(PopoverContentCore,{closeButtonLight:!!image&&placement==="top",closeButtonLabel:closeButtonLabel,closeButtonVisible:closeButtonVisible,style:style,testId:testId,ref:ref,children:[jsxRuntime.jsxs(wonderBlocksCore.View,{style:!!icon&&styles.withIcon,children:[maybeRenderImage({placement}),maybeRenderIcon(),jsxRuntime.jsxs(wonderBlocksCore.View,{style:styles.text,children:[jsxRuntime.jsx(wonderBlocksTypography.Heading,{size:"medium",tag:titleHeadingTag,id:`${uniqueId}-title`,style:styles.title,children:title}),jsxRuntime.jsx(wonderBlocksTypography.BodyText,{tag:"span",id:`${uniqueId}-content`,children:content})]})]}),maybeRenderActions(close)]})});PopoverContent.displayName="PopoverContent";const styles=aphrodite.StyleSheet.create({actions:{marginBlockStart:wonderBlocksTokens.sizing.size_240,flexDirection:"row",alignItems:"center",justifyContent:"flex-end"},text:{justifyContent:"center"},title:{marginBlockEnd:wonderBlocksTokens.sizing.size_080},iconContainer:{alignItems:"center",justifyContent:"center",blockSize:wonderBlocksTokens.sizing.size_640,inlineSize:wonderBlocksTokens.sizing.size_640,minInlineSize:wonderBlocksTokens.sizing.size_640,marginInlineEnd:wonderBlocksTokens.sizing.size_160,overflow:"hidden"},icon:{width:"100%"},withIcon:{flexDirection:"row"},image:{marginBlockEnd:wonderBlocksTokens.sizing.size_240,marginBlockStart:`calc(-1 * ${wonderBlocksTokens.sizing.size_240})`,marginInline:`calc(-1 * ${wonderBlocksTokens.sizing.size_240})`,inlineSize:`calc(100% + ${wonderBlocksTokens.sizing.size_480})`},imageToBottom:{marginBlockEnd:`calc(-1 * ${wonderBlocksTokens.sizing.size_240})`,marginBlockStart:wonderBlocksTokens.sizing.size_240,order:1}});
53
54
 
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "",
4
4
  "author": "Khan Academy",
5
5
  "license": "MIT",
6
- "version": "7.0.0",
6
+ "version": "7.0.2",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -21,9 +21,10 @@
21
21
  "types": "dist/index.d.ts",
22
22
  "dependencies": {
23
23
  "@khanacademy/wonder-blocks-core": "12.5.0",
24
- "@khanacademy/wonder-blocks-floating": "0.1.0",
25
- "@khanacademy/wonder-blocks-icon-button": "12.0.1",
24
+ "@khanacademy/wonder-blocks-floating": "0.2.0",
25
+ "@khanacademy/wonder-blocks-layout": "3.2.0",
26
26
  "@khanacademy/wonder-blocks-styles": "0.4.0",
27
+ "@khanacademy/wonder-blocks-icon-button": "12.0.1",
27
28
  "@khanacademy/wonder-blocks-tokens": "18.0.0",
28
29
  "@khanacademy/wonder-blocks-typography": "5.0.5"
29
30
  },