@khanacademy/wonder-blocks-modal 8.1.9 → 8.2.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @khanacademy/wonder-blocks-modal@8.1.9 build:css /home/runner/work/wonder-blocks/wonder-blocks/packages/wonder-blocks-modal
2
+ > @khanacademy/wonder-blocks-modal@8.2.1 build:css /home/runner/work/wonder-blocks/wonder-blocks/packages/wonder-blocks-modal
3
3
  > pnpm exec wonder-blocks-tokens .
4
4
 
5
5
  CSS variables generated successfully in: /home/runner/work/wonder-blocks/wonder-blocks/packages/wonder-blocks-modal/dist/css
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @khanacademy/wonder-blocks-modal
2
2
 
3
+ ## 8.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [1334e74]
8
+ - @khanacademy/wonder-blocks-core@12.4.0
9
+ - @khanacademy/wonder-blocks-breadcrumbs@3.1.32
10
+ - @khanacademy/wonder-blocks-icon-button@10.3.16
11
+ - @khanacademy/wonder-blocks-layout@3.1.32
12
+ - @khanacademy/wonder-blocks-typography@4.2.17
13
+
14
+ ## 8.2.0
15
+
16
+ ### Minor Changes
17
+
18
+ - 72e9eed: Add new FlexibleModal variant for more flexible implementations
19
+
3
20
  ## 8.1.9
4
21
 
5
22
  ### Patch Changes
@@ -0,0 +1,99 @@
1
+ import * as React from "react";
2
+ import type { StyleType } from "@khanacademy/wonder-blocks-core";
3
+ type AccessibleDialogProps = {
4
+ title: React.ReactElement | string;
5
+ "aria-label"?: never;
6
+ "aria-labelledby"?: string;
7
+ } | {
8
+ title?: never;
9
+ "aria-label": string;
10
+ "aria-labelledby"?: never;
11
+ } | {
12
+ title?: never;
13
+ "aria-label"?: never;
14
+ "aria-labelledby": string;
15
+ };
16
+ type Props = AccessibleDialogProps & {
17
+ /**
18
+ * An optional id parameter for the main heading. If one is not provided,
19
+ * an ID will be generated.
20
+ */
21
+ titleId?: string;
22
+ /**
23
+ * The content of the modal. Supports a render prop for placing the title in a slot.
24
+ */
25
+ content: React.ReactElement | ((slots: RenderProps) => React.ReactElement);
26
+ /**
27
+ * Called when the close button is clicked.
28
+ *
29
+ * If you're using `ModalLauncher`, you probably shouldn't use this prop!
30
+ * Instead, to listen for when the modal closes, add an `onClose` handler
31
+ * to the `ModalLauncher`. Doing so will result in a console.warn().
32
+ */
33
+ onClose?: () => unknown;
34
+ /**
35
+ * When true, the close button is shown; otherwise, the close button is not shown.
36
+ */
37
+ closeButtonVisible?: boolean;
38
+ /**
39
+ * When set, overrides the default role value. Default role is "dialog"
40
+ * Roles other than dialog and alertdialog aren't appropriate for this
41
+ * component
42
+ */
43
+ role?: "dialog" | "alertdialog";
44
+ /**
45
+ * Optional custom styles.
46
+ */
47
+ styles?: {
48
+ root?: StyleType;
49
+ panel?: StyleType;
50
+ closeButton?: StyleType;
51
+ };
52
+ /**
53
+ * Test ID used for e2e testing. This ID will be passed down to the Dialog.
54
+ */
55
+ testId?: string;
56
+ /**
57
+ * The ID of the content describing this dialog, if applicable.
58
+ */
59
+ "aria-describedby"?: string;
60
+ };
61
+ type RenderProps = {
62
+ title: React.ReactNode | string;
63
+ };
64
+ /**
65
+ * A more flexible modal variant with fewer layout constraints. It can receive
66
+ * a custom background (image or color), a title for the main heading, and that
67
+ * title can optionally render in the content area through a render prop.
68
+ *
69
+ * One of the following is required for labeling the dialog:
70
+ * - title content (React element or string)
71
+ * - aria-label (string)
72
+ * - aria-labelledby (string ID reference)
73
+ *
74
+ * ### Usage
75
+ *
76
+ * ```jsx
77
+ * import {FlexibleDialog} from "@khanacademy/wonder-blocks-modal";
78
+ * import {BodyText} from "@khanacademy/wonder-blocks-typography";
79
+ *
80
+ * <FlexibleDialog
81
+ * title={<Heading size="xxlarge" id="main-heading">Select mission</Heading>}
82
+ * content={
83
+ * <BodyText>
84
+ * {`Lorem ipsum dolor sit amet, consectetur adipiscing
85
+ * elit, sed do eiusmod tempor incididunt ut labore et
86
+ * dolore magna aliqua. Ut enim ad minim veniam,
87
+ * quis nostrud exercitation ullamco laboris nisi ut
88
+ * aliquip ex ea commodo consequat. Duis aute irure
89
+ * dolor in reprehenderit in voluptate velit esse
90
+ * cillum dolore eu fugiat nulla pariatur. Excepteur
91
+ * sint occaecat cupidatat non proident, sunt in culpa
92
+ * qui officia deserunt mollit anim id est.`}
93
+ * </BodyText>
94
+ * }
95
+ * />
96
+ * ```
97
+ */
98
+ declare const FlexibleDialog: ({ onClose, title, content, styles, closeButtonVisible, testId, titleId, role, ...accessibilityProps }: Props) => React.ReactElement;
99
+ export default FlexibleDialog;
@@ -0,0 +1,70 @@
1
+ import * as React from "react";
2
+ import { PropsFor } from "@khanacademy/wonder-blocks-core";
3
+ import type { StyleType } from "@khanacademy/wonder-blocks-core";
4
+ import ModalContent from "./modal-content";
5
+ type RenderProps = {
6
+ title: React.ReactNode | string;
7
+ };
8
+ type Props = {
9
+ /**
10
+ * The main heading of the FlexiblePanel. Used to label the dialog.
11
+ */
12
+ title?: React.ReactNode | string;
13
+ /**
14
+ * The main contents of the FlexiblePanel. All other parts of the panel
15
+ * are positioned around it.
16
+ */
17
+ content: React.ReactElement<PropsFor<typeof ModalContent>> | ((slots: RenderProps) => React.ReactElement) | React.ReactElement;
18
+ /**
19
+ * When true, the close button is shown; otherwise, the close button is not shown.
20
+ */
21
+ closeButtonVisible: boolean;
22
+ /**
23
+ * Any optional styling to apply to the root (panel background) and close button.
24
+ */
25
+ styles?: {
26
+ root?: StyleType;
27
+ closeButton?: StyleType;
28
+ };
29
+ /**
30
+ * Called when the close button is clicked.
31
+ *
32
+ * If you're using `ModalLauncher`, you should not use this prop!
33
+ * Instead, to listen for when the modal closes, add an `onClose` handler
34
+ * to the `ModalLauncher`. Doing so will throw an error.
35
+ */
36
+ onClose?: () => unknown;
37
+ /**
38
+ * Test ID used for e2e testing.
39
+ *
40
+ * In this case, this `testId` comes from the `testId` prop defined in the
41
+ * Dialog variant (e.g. FlexibleDialog).
42
+ */
43
+ testId?: string;
44
+ };
45
+ /**
46
+ * FlexiblePanel is the content container.
47
+ *
48
+ * **Implementation notes:**
49
+ *
50
+ * If you are creating a custom Dialog, make sure to follow these guidelines:
51
+ * - Make sure to add this component inside the [FlexibleDialog](/#flexibledialog).
52
+ * - If you need to create e2e tests, make sure to pass a `testId` prop. This
53
+ * will be passed down to this component using a sufix: e.g.
54
+ * `some-random-id-FlexiblePanel`. This scope will be propagated to the
55
+ * CloseButton element as well: e.g. `some-random-id-CloseButton`.
56
+ *
57
+ * ```js
58
+ * <FlexibleDialog>
59
+ * <FlexiblePanel content={"custom content goes here"} />
60
+ * </FlexibleDialog>
61
+ * ```
62
+ */
63
+ declare function FlexiblePanel({ closeButtonVisible, content, title, onClose, styles, testId, }: Props): React.JSX.Element;
64
+ declare namespace FlexiblePanel {
65
+ var defaultProps: {
66
+ closeButtonVisible: boolean;
67
+ scrollOverflow: boolean;
68
+ };
69
+ }
70
+ export default FlexiblePanel;
@@ -31,6 +31,11 @@ type Props = {
31
31
  * Test ID used for e2e testing.
32
32
  */
33
33
  testId?: string;
34
+ /**
35
+ * The accessible name of dialog.
36
+ * See WCAG 2.1: 4.1.2 Name, Role, Value
37
+ */
38
+ "aria-label"?: string;
34
39
  /**
35
40
  * The ID of the title labelling this dialog. Required.
36
41
  * See WCAG 2.1: 4.1.2 Name, Role, Value
package/dist/es/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
1
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
3
  import { View, Id } from '@khanacademy/wonder-blocks-core';
4
4
  import { StyleSheet } from 'aphrodite';
5
- import { border, sizing, mapValuesToCssVars, semanticColor } from '@khanacademy/wonder-blocks-tokens';
5
+ import { border, sizing, mapValuesToCssVars, semanticColor, breakpoint } from '@khanacademy/wonder-blocks-tokens';
6
6
  import { Heading, BodyText } from '@khanacademy/wonder-blocks-typography';
7
7
  import * as ReactDOM from 'react-dom';
8
8
  import { withActionScheduler } from '@khanacademy/wonder-blocks-timing';
@@ -15,7 +15,7 @@ const theme$1={root:{border:{radius:border.radius.radius_040}},dialog:{layout:{p
15
15
 
16
16
  var theme = mapValuesToCssVars(theme$1,"--wb-c-modal-");
17
17
 
18
- const ModalDialog=React.forwardRef(function ModalDialog(props,ref){const{above,below,role="dialog",style,children,testId,"aria-labelledby":ariaLabelledBy,"aria-describedby":ariaDescribedBy}=props;return jsxs(View,{style:[styles$6.wrapper,style],children:[below&&jsx(View,{style:styles$6.below,children:below}),jsx(View,{role:role,"aria-modal":"true","aria-labelledby":ariaLabelledBy,"aria-describedby":ariaDescribedBy,ref:ref,style:styles$6.dialog,testId:testId,children:children}),above&&jsx(View,{style:styles$6.above,children:above})]})});const small$2="@media (max-width: 767px)";const styles$6=StyleSheet.create({wrapper:{color:semanticColor.core.foreground.neutral.strong,flexDirection:"row",alignItems:"stretch",width:"100%",height:"100%",position:"relative",boxShadow:theme.dialog.shadow.default,borderRadius:theme.root.border.radius,[small$2]:{padding:theme.dialog.layout.padding,flexDirection:"column"}},dialog:{width:"100%",height:"100%",borderRadius:theme.root.border.radius,overflow:"hidden"},above:{pointerEvents:"none",position:"absolute",top:0,left:0,bottom:0,right:0,zIndex:1},below:{pointerEvents:"none",position:"absolute",top:0,left:0,bottom:0,right:0,zIndex:-1}});ModalDialog.displayName="ModalDialog";
18
+ const ModalDialog=React.forwardRef(function ModalDialog(props,ref){const{above,below,role="dialog",style,children,testId,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy,"aria-describedby":ariaDescribedBy}=props;return jsxs(View,{style:[styles$6.wrapper,style],children:[below&&jsx(View,{style:styles$6.below,children:below}),jsx(View,{role:role,"aria-modal":"true","aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy,"aria-describedby":ariaDescribedBy,ref:ref,style:styles$6.dialog,testId:testId,children:children}),above&&jsx(View,{style:styles$6.above,children:above})]})});const small$2="@media (max-width: 767px)";const styles$6=StyleSheet.create({wrapper:{color:semanticColor.core.foreground.neutral.strong,flexDirection:"row",alignItems:"stretch",width:"100%",height:"100%",position:"relative",boxShadow:theme.dialog.shadow.default,borderRadius:theme.root.border.radius,[small$2]:{padding:theme.dialog.layout.padding,flexDirection:"column"}},dialog:{width:"100%",height:"100%",borderRadius:theme.root.border.radius,overflow:"hidden"},above:{pointerEvents:"none",position:"absolute",top:0,left:0,bottom:0,right:0,zIndex:1},below:{pointerEvents:"none",position:"absolute",top:0,left:0,bottom:0,right:0,zIndex:-1}});ModalDialog.displayName="ModalDialog";
19
19
 
20
20
  function ModalFooter({children}){return jsx(View,{style:styles$5.footer,children:children})}ModalFooter.__IS_MODAL_FOOTER__=true;ModalFooter.isComponentOf=instance=>{return instance&&instance.type&&instance.type.__IS_MODAL_FOOTER__};const styles$5=StyleSheet.create({footer:{flex:"0 0 auto",boxSizing:"border-box",minHeight:sizing.size_640,paddingInline:theme.footer.layout.padding.inline,paddingBlock:theme.footer.layout.padding.block,display:"flex",flexDirection:"row",alignItems:"center",justifyContent:"flex-end",boxShadow:`0px -1px 0px ${semanticColor.core.border.neutral.subtle}`}});
21
21
 
@@ -43,6 +43,10 @@ function ModalPanel({closeButtonVisible=true,scrollOverflow=true,content,footer,
43
43
 
44
44
  class OnePaneDialog extends React.Component{renderHeader(uniqueId){const{title,breadcrumbs=undefined,subtitle=undefined,testId}=this.props;if(breadcrumbs){return jsx(ModalHeader,{title:title,breadcrumbs:breadcrumbs,titleId:uniqueId,testId:testId&&`${testId}-header`})}else if(subtitle){return jsx(ModalHeader,{title:title,subtitle:subtitle,titleId:uniqueId,testId:testId&&`${testId}-header`})}else {return jsx(ModalHeader,{title:title,titleId:uniqueId,testId:testId&&`${testId}-header`})}}render(){const{onClose,footer,content,above,below,style,closeButtonVisible,testId,titleId,role,"aria-describedby":ariaDescribedBy}=this.props;return jsx(MediaLayout,{styleSheets:styleSheets,children:({styles})=>jsx(Id,{id:titleId,children:uniqueId=>jsx(ModalDialog,{style:[styles.dialog,style],above:above,below:below,testId:testId,"aria-labelledby":uniqueId,"aria-describedby":ariaDescribedBy,role:role,children:jsx(ModalPanel,{onClose:onClose,header:this.renderHeader(uniqueId),content:content,footer:footer,closeButtonVisible:closeButtonVisible,testId:testId})})})})}}OnePaneDialog.defaultProps={closeButtonVisible:true};const styleSheets={small:StyleSheet.create({dialog:{width:"100%",height:"100%",overflow:"hidden"}}),mdOrLarger:StyleSheet.create({dialog:{width:"93.75%",maxWidth:576,height:"81.25%",maxHeight:624}})};
45
45
 
46
+ function FlexiblePanel({closeButtonVisible=true,content,title,onClose,styles,testId}){const renderMainContent=React.useCallback(()=>{const contentNode=typeof content==="function"?content({title}):jsxs(Fragment,{children:[title,content]});const mainContent=ModalContent.isComponentOf(contentNode)?contentNode:jsx(ModalContent,{children:contentNode});if(!mainContent){return mainContent}return React.cloneElement(mainContent,{style:[mainContent.props.style]})},[title,content]);const mainContent=renderMainContent();const defaultBackgroundStyle={backgroundColor:semanticColor.surface.primary};const combinedBackgroundStyles={...defaultBackgroundStyle,...styles?.root};return jsxs(View,{style:[componentStyles$1.wrapper,combinedBackgroundStyles],testId:testId&&`${testId}-panel`,children:[closeButtonVisible&&jsx(CloseButton,{onClick:onClose,style:[componentStyles$1.closeButton,styles?.closeButton],testId:testId&&`${testId}-close`}),mainContent]})}FlexiblePanel.defaultProps={closeButtonVisible:true,scrollOverflow:true};const componentStyles$1=StyleSheet.create({wrapper:{flex:"1 1 auto",flexDirection:"column",boxSizing:"border-box",overflow:"hidden",height:"100%",width:"100%"},closeButton:{position:"absolute",right:theme.closeButton.layout.gapRight,top:theme.closeButton.layout.gapTop,zIndex:1,":focus":focusStyles.focus[":focus-visible"]}});
47
+
48
+ const FlexibleDialog=({onClose,title,content,styles,closeButtonVisible=true,testId,titleId,role,...accessibilityProps})=>{const uniqueId=React.useId();const headingId=titleId??uniqueId;const renderedTitle=title==null?null:typeof title==="string"?jsx(Heading,{id:headingId,children:title}):React.cloneElement(title,{id:headingId,testId:"title-heading-wrapper"});return jsx(ModalDialog,{style:[componentStyles.dialog,styles?.root],testId:testId,"aria-label":accessibilityProps["aria-label"],"aria-labelledby":headingId,"aria-describedby":accessibilityProps["aria-describedby"],role:role,children:jsx(FlexiblePanel,{styles:{root:styles?.panel},onClose:onClose,title:renderedTitle,content:content,closeButtonVisible:closeButtonVisible,testId:testId})})};const componentStyles=StyleSheet.create({dialog:{width:"93.75%",maxWidth:576,height:"auto",maxHeight:"100vh",position:"relative",overflow:"auto",[breakpoint.mediaQuery.sm]:{width:"100%",height:"100vh",maxHeight:"100vh"}}});
49
+
46
50
  function maybeGetNextAncestorModalLauncherPortal(element){let candidateElement=element&&element.parentElement;while(candidateElement&&!candidateElement.hasAttribute(ModalLauncherPortalAttributeName)){candidateElement=candidateElement.parentElement;}return candidateElement}function maybeGetPortalMountedModalHostElement(element){return maybeGetNextAncestorModalLauncherPortal(element)}
47
51
 
48
- export { ModalDialog, ModalFooter, ModalHeader, modalLauncher as ModalLauncher, ModalPanel, OnePaneDialog, maybeGetPortalMountedModalHostElement };
52
+ export { FlexibleDialog, ModalDialog, ModalFooter, ModalHeader, modalLauncher as ModalLauncher, ModalPanel, OnePaneDialog, maybeGetPortalMountedModalHostElement };
package/dist/index.d.ts CHANGED
@@ -4,5 +4,6 @@ import ModalHeader from "./components/modal-header";
4
4
  import ModalLauncher from "./components/modal-launcher";
5
5
  import ModalPanel from "./components/modal-panel";
6
6
  import OnePaneDialog from "./components/one-pane-dialog";
7
+ import FlexibleDialog from "./components/flexible-dialog";
7
8
  import maybeGetPortalMountedModalHostElement from "./util/maybe-get-portal-mounted-modal-host-element";
8
- export { ModalHeader, ModalFooter, ModalDialog, ModalPanel, ModalLauncher, OnePaneDialog, maybeGetPortalMountedModalHostElement, };
9
+ export { ModalHeader, ModalFooter, ModalDialog, ModalPanel, ModalLauncher, OnePaneDialog, FlexibleDialog, maybeGetPortalMountedModalHostElement, };
package/dist/index.js CHANGED
@@ -44,7 +44,7 @@ const theme$1={root:{border:{radius:wonderBlocksTokens.border.radius.radius_040}
44
44
 
45
45
  var theme = wonderBlocksTokens.mapValuesToCssVars(theme$1,"--wb-c-modal-");
46
46
 
47
- const ModalDialog=React__namespace.forwardRef(function ModalDialog(props,ref){const{above,below,role="dialog",style,children,testId,"aria-labelledby":ariaLabelledBy,"aria-describedby":ariaDescribedBy}=props;return jsxRuntime.jsxs(wonderBlocksCore.View,{style:[styles$6.wrapper,style],children:[below&&jsxRuntime.jsx(wonderBlocksCore.View,{style:styles$6.below,children:below}),jsxRuntime.jsx(wonderBlocksCore.View,{role:role,"aria-modal":"true","aria-labelledby":ariaLabelledBy,"aria-describedby":ariaDescribedBy,ref:ref,style:styles$6.dialog,testId:testId,children:children}),above&&jsxRuntime.jsx(wonderBlocksCore.View,{style:styles$6.above,children:above})]})});const small$2="@media (max-width: 767px)";const styles$6=aphrodite.StyleSheet.create({wrapper:{color:wonderBlocksTokens.semanticColor.core.foreground.neutral.strong,flexDirection:"row",alignItems:"stretch",width:"100%",height:"100%",position:"relative",boxShadow:theme.dialog.shadow.default,borderRadius:theme.root.border.radius,[small$2]:{padding:theme.dialog.layout.padding,flexDirection:"column"}},dialog:{width:"100%",height:"100%",borderRadius:theme.root.border.radius,overflow:"hidden"},above:{pointerEvents:"none",position:"absolute",top:0,left:0,bottom:0,right:0,zIndex:1},below:{pointerEvents:"none",position:"absolute",top:0,left:0,bottom:0,right:0,zIndex:-1}});ModalDialog.displayName="ModalDialog";
47
+ const ModalDialog=React__namespace.forwardRef(function ModalDialog(props,ref){const{above,below,role="dialog",style,children,testId,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy,"aria-describedby":ariaDescribedBy}=props;return jsxRuntime.jsxs(wonderBlocksCore.View,{style:[styles$6.wrapper,style],children:[below&&jsxRuntime.jsx(wonderBlocksCore.View,{style:styles$6.below,children:below}),jsxRuntime.jsx(wonderBlocksCore.View,{role:role,"aria-modal":"true","aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy,"aria-describedby":ariaDescribedBy,ref:ref,style:styles$6.dialog,testId:testId,children:children}),above&&jsxRuntime.jsx(wonderBlocksCore.View,{style:styles$6.above,children:above})]})});const small$2="@media (max-width: 767px)";const styles$6=aphrodite.StyleSheet.create({wrapper:{color:wonderBlocksTokens.semanticColor.core.foreground.neutral.strong,flexDirection:"row",alignItems:"stretch",width:"100%",height:"100%",position:"relative",boxShadow:theme.dialog.shadow.default,borderRadius:theme.root.border.radius,[small$2]:{padding:theme.dialog.layout.padding,flexDirection:"column"}},dialog:{width:"100%",height:"100%",borderRadius:theme.root.border.radius,overflow:"hidden"},above:{pointerEvents:"none",position:"absolute",top:0,left:0,bottom:0,right:0,zIndex:1},below:{pointerEvents:"none",position:"absolute",top:0,left:0,bottom:0,right:0,zIndex:-1}});ModalDialog.displayName="ModalDialog";
48
48
 
49
49
  function ModalFooter({children}){return jsxRuntime.jsx(wonderBlocksCore.View,{style:styles$5.footer,children:children})}ModalFooter.__IS_MODAL_FOOTER__=true;ModalFooter.isComponentOf=instance=>{return instance&&instance.type&&instance.type.__IS_MODAL_FOOTER__};const styles$5=aphrodite.StyleSheet.create({footer:{flex:"0 0 auto",boxSizing:"border-box",minHeight:wonderBlocksTokens.sizing.size_640,paddingInline:theme.footer.layout.padding.inline,paddingBlock:theme.footer.layout.padding.block,display:"flex",flexDirection:"row",alignItems:"center",justifyContent:"flex-end",boxShadow:`0px -1px 0px ${wonderBlocksTokens.semanticColor.core.border.neutral.subtle}`}});
50
50
 
@@ -72,8 +72,13 @@ function ModalPanel({closeButtonVisible=true,scrollOverflow=true,content,footer,
72
72
 
73
73
  class OnePaneDialog extends React__namespace.Component{renderHeader(uniqueId){const{title,breadcrumbs=undefined,subtitle=undefined,testId}=this.props;if(breadcrumbs){return jsxRuntime.jsx(ModalHeader,{title:title,breadcrumbs:breadcrumbs,titleId:uniqueId,testId:testId&&`${testId}-header`})}else if(subtitle){return jsxRuntime.jsx(ModalHeader,{title:title,subtitle:subtitle,titleId:uniqueId,testId:testId&&`${testId}-header`})}else {return jsxRuntime.jsx(ModalHeader,{title:title,titleId:uniqueId,testId:testId&&`${testId}-header`})}}render(){const{onClose,footer,content,above,below,style,closeButtonVisible,testId,titleId,role,"aria-describedby":ariaDescribedBy}=this.props;return jsxRuntime.jsx(wonderBlocksLayout.MediaLayout,{styleSheets:styleSheets,children:({styles})=>jsxRuntime.jsx(wonderBlocksCore.Id,{id:titleId,children:uniqueId=>jsxRuntime.jsx(ModalDialog,{style:[styles.dialog,style],above:above,below:below,testId:testId,"aria-labelledby":uniqueId,"aria-describedby":ariaDescribedBy,role:role,children:jsxRuntime.jsx(ModalPanel,{onClose:onClose,header:this.renderHeader(uniqueId),content:content,footer:footer,closeButtonVisible:closeButtonVisible,testId:testId})})})})}}OnePaneDialog.defaultProps={closeButtonVisible:true};const styleSheets={small:aphrodite.StyleSheet.create({dialog:{width:"100%",height:"100%",overflow:"hidden"}}),mdOrLarger:aphrodite.StyleSheet.create({dialog:{width:"93.75%",maxWidth:576,height:"81.25%",maxHeight:624}})};
74
74
 
75
+ function FlexiblePanel({closeButtonVisible=true,content,title,onClose,styles,testId}){const renderMainContent=React__namespace.useCallback(()=>{const contentNode=typeof content==="function"?content({title}):jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[title,content]});const mainContent=ModalContent.isComponentOf(contentNode)?contentNode:jsxRuntime.jsx(ModalContent,{children:contentNode});if(!mainContent){return mainContent}return React__namespace.cloneElement(mainContent,{style:[mainContent.props.style]})},[title,content]);const mainContent=renderMainContent();const defaultBackgroundStyle={backgroundColor:wonderBlocksTokens.semanticColor.surface.primary};const combinedBackgroundStyles={...defaultBackgroundStyle,...styles?.root};return jsxRuntime.jsxs(wonderBlocksCore.View,{style:[componentStyles$1.wrapper,combinedBackgroundStyles],testId:testId&&`${testId}-panel`,children:[closeButtonVisible&&jsxRuntime.jsx(CloseButton,{onClick:onClose,style:[componentStyles$1.closeButton,styles?.closeButton],testId:testId&&`${testId}-close`}),mainContent]})}FlexiblePanel.defaultProps={closeButtonVisible:true,scrollOverflow:true};const componentStyles$1=aphrodite.StyleSheet.create({wrapper:{flex:"1 1 auto",flexDirection:"column",boxSizing:"border-box",overflow:"hidden",height:"100%",width:"100%"},closeButton:{position:"absolute",right:theme.closeButton.layout.gapRight,top:theme.closeButton.layout.gapTop,zIndex:1,":focus":wonderBlocksStyles.focusStyles.focus[":focus-visible"]}});
76
+
77
+ const FlexibleDialog=({onClose,title,content,styles,closeButtonVisible=true,testId,titleId,role,...accessibilityProps})=>{const uniqueId=React__namespace.useId();const headingId=titleId??uniqueId;const renderedTitle=title==null?null:typeof title==="string"?jsxRuntime.jsx(wonderBlocksTypography.Heading,{id:headingId,children:title}):React__namespace.cloneElement(title,{id:headingId,testId:"title-heading-wrapper"});return jsxRuntime.jsx(ModalDialog,{style:[componentStyles.dialog,styles?.root],testId:testId,"aria-label":accessibilityProps["aria-label"],"aria-labelledby":headingId,"aria-describedby":accessibilityProps["aria-describedby"],role:role,children:jsxRuntime.jsx(FlexiblePanel,{styles:{root:styles?.panel},onClose:onClose,title:renderedTitle,content:content,closeButtonVisible:closeButtonVisible,testId:testId})})};const componentStyles=aphrodite.StyleSheet.create({dialog:{width:"93.75%",maxWidth:576,height:"auto",maxHeight:"100vh",position:"relative",overflow:"auto",[wonderBlocksTokens.breakpoint.mediaQuery.sm]:{width:"100%",height:"100vh",maxHeight:"100vh"}}});
78
+
75
79
  function maybeGetNextAncestorModalLauncherPortal(element){let candidateElement=element&&element.parentElement;while(candidateElement&&!candidateElement.hasAttribute(ModalLauncherPortalAttributeName)){candidateElement=candidateElement.parentElement;}return candidateElement}function maybeGetPortalMountedModalHostElement(element){return maybeGetNextAncestorModalLauncherPortal(element)}
76
80
 
81
+ exports.FlexibleDialog = FlexibleDialog;
77
82
  exports.ModalDialog = ModalDialog;
78
83
  exports.ModalFooter = ModalFooter;
79
84
  exports.ModalHeader = ModalHeader;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-modal",
3
- "version": "8.1.9",
3
+ "version": "8.2.1",
4
4
  "design": "v2",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -20,14 +20,14 @@
20
20
  "author": "",
21
21
  "license": "MIT",
22
22
  "dependencies": {
23
- "@khanacademy/wonder-blocks-breadcrumbs": "3.1.31",
24
- "@khanacademy/wonder-blocks-core": "12.3.0",
25
- "@khanacademy/wonder-blocks-icon-button": "10.3.15",
26
- "@khanacademy/wonder-blocks-layout": "3.1.31",
23
+ "@khanacademy/wonder-blocks-breadcrumbs": "3.1.32",
24
+ "@khanacademy/wonder-blocks-core": "12.4.0",
25
+ "@khanacademy/wonder-blocks-icon-button": "10.3.16",
26
+ "@khanacademy/wonder-blocks-layout": "3.1.32",
27
27
  "@khanacademy/wonder-blocks-styles": "0.2.27",
28
28
  "@khanacademy/wonder-blocks-timing": "7.0.2",
29
29
  "@khanacademy/wonder-blocks-tokens": "12.0.2",
30
- "@khanacademy/wonder-blocks-typography": "4.2.16"
30
+ "@khanacademy/wonder-blocks-typography": "4.2.17"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@phosphor-icons/core": "^2.0.2",