Skip to content

Commit

Permalink
[ui] Modal (#210)
Browse files Browse the repository at this point in the history
* [ui] Modal: initial commit for new modal

* [ui] Modal: add and use ModalFooter component

* [ui] MOdal: add more docstrings, handle confirm click

* [ui] MOdal: add most styling, api improvements

* user can pass custom children to ModalFooter, a minimal container willbe rendered to ensure stylistic integrity
* user can pass custom classNames to specify flex layout
* add sizing (widths only for now)
* add comments open TODOs, questions

* [ui] Modal: formatting, TODOs

* [ui] Modal: minor story improvements

* [ui] Modal: update comments, fix console error

* text in ‘<>’ inside comments/docstrings will throw error, use backticks around these

* [ui] Modal: use Stack and ButtonRow

* [ui] Modal: add default padding to content, add `unpad` prop

* set `unpad` to remove the default padding fromt he modal content container

* [ui] Modal: remove obsolete comment

* [ui] Modal: allow for custom Cancel button label

* [ui] ButtonRow: Use Stack internally

* [ui] Modal: add Button icons

* [ui] MOdalFooter: remove dedicated CloseButton

* will be covered by cancelButton

* [ui] Modal: Update TODOs

* [ui] Modal: Update TODOs again

* [ui] Modal: add className prop

* [ui] Modal: pen and close the Modal

* [ui] MOdal: add backdrop,center modal

* [ui] ModalFooter: remove obsolete prop

* [ui] Modal: update styles, comments

* [ui] ModalFooter: add tests

* [ui] Modal: add first tests

* [ui] Modal, ModalFooter: add comments, simplify stories

* [ui] ModalFooter: export component

* [ui] Modal: remove obsolete

* [ui] Modal: bump version to 0.7.0
  • Loading branch information
franzheidl authored Oct 12, 2022
1 parent bb2a5ae commit 22805b2
Show file tree
Hide file tree
Showing 14 changed files with 638 additions and 288 deletions.
2 changes: 1 addition & 1 deletion libs/juno-ui-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"module": "lib/index.js",
"source": "src/index.js",
"style": "lib/esm/styles.css",
"version": "0.6.0",
"version": "0.7.0",
"files": [
"lib",
"tailwind.config.js"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react"
import PropTypes from "prop-types"
import { Stack } from "../Stack"

const buttonrowstyles = `
jn-flex
Expand All @@ -8,16 +9,16 @@ const buttonrowstyles = `
jn-gap-2
`

/** A container to hold one or multiple buttons and align them. Obsolete - promote using Stack instead? */
/** A container to hold one or multiple buttons and space and align them. */
export const ButtonRow = ({
children,
className,
...props
}) => {
return (
<div className={`juno-button-row ${buttonrowstyles} ${className}`} {...props} >
<Stack gap="2" className={`juno-button-row ${className}`} {...props} >
{children}
</div>
</Stack>
)
}

Expand Down
205 changes: 205 additions & 0 deletions libs/juno-ui-components/src/components/Modal/Modal.component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import React, { useState, useEffect } from "react"
import PropTypes from "prop-types"
import { ModalFooter } from "../ModalFooter/index"
import { knownIcons } from "../Icon/Icon.component.js"
import { Icon } from "../Icon"

/*
* optional title ✓
* closeable by default ✓
* padding content? -> 'unpad' ✓
* min-height for content? ✓
* allow for creating modals without buttons? ✓
* always show header bar regardless whether there is title and/or close button? ✓
* SM/LG sizes (widths for now). ✓
* confirmButtonIcon prop ✓
* cancelButtonIcon prop ✓
* backdrop ✓
* open programmatically TODO
* handle height/scrolling TODO -> allow optional constrainHeight=false prop?
* Optional CloseOnBackdropClick -> NOT FOR NOW ✓
* Spare "variant" prop for semantic variants later. TODO
* a11y (voicereader, keyboard accessibilty) TODO
* autofocus ?
* icon TODO
* trap focus TODO
* render in Portal (how to make sure we're always in scope of StyleProvider? TODO -> add element to styleprovider TODO, what if there are several StyleProvider on the page?
* Tests TODO
*/

const modalcontainerstyles = `
jn-fixed
jn-inset-0
jn-flex
jn-items-center
jn-bg-theme-modal-backdrop/60
jn-backdrop-blur-[2px]
`

const modalstyles = `
jn-bg-theme-background-lvl-2
jn-relative
jn-m-auto
jn-overflow-y-auto
jn-max-h-[90%]
`

const headerstyles = `
jn-flex
jn-py-2
jn-px-8
jn-border-b
jn-border-theme-background-lvl-4
`

const titlestyles = `
jn-text-xl
jn-font-bold
`

const contentstyles = `
jn-min-h-[5rem]
`

const contentpaddingstyles = `
jn-py-4
jn-px-8
`

const sizeClass = (size) => {
switch (size) {
case "large":
return `jn-w-[40rem]`
default:
return `jn-w-[33.625rem]`
}
}

/**
A generic Modal component.
*/
export const Modal = ({
size,
title,
heading,
confirmButtonLabel,
cancelButtonLabel,
confirmButtonIcon,
cancelButtonIcon,
open,
children,
modalFooter,
closeable,
unpad,
onConfirm,
onCancel,
className,
...props
}) => {

const [isOpen, setIsOpen] = useState(open)
const [isCloseable, setIsCloseable] = useState(closeable)

useEffect(() => {
setIsOpen(open)
}, [open])

useEffect(() => {
setIsCloseable(closeable)
}, [closeable])

const handleConfirmClick = (event) => {
setIsOpen(false)
onConfirm && onConfirm(event)
}

const handleCancelClick = (event) => {
setIsOpen(false)
onCancel && onCancel(event)
}

return (
<>
{ isOpen && (
<div className={`juno-modal-container ${modalcontainerstyles}`} >
<div className={`juno-modal ${sizeClass(size)} ${modalstyles} ${className}`} role="dialog" {...props} >
<div className={`juno-modal-header ${headerstyles} ${ title || heading ? `jn-justify-between` : `jn-justify-end` }`}>
{ title || heading ? <h1 className={`juno-modal-title ${titlestyles}`} >{ title || heading }</h1> : null }
{ isCloseable ? <Icon icon="close" onClick={ handleCancelClick }/> : null }
</div>
<div className={`juno-modal-content ${contentstyles} ${ unpad ? "" : contentpaddingstyles }`} >
{ children }
</div>
{ isCloseable ?
modalFooter ?
modalFooter
:
<ModalFooter
confirmButtonLabel={ confirmButtonLabel }
cancelButtonLabel={ cancelButtonLabel }
confirmButtonIcon={ confirmButtonIcon }
cancelButtonIcon={ cancelButtonIcon }
onConfirm={ handleConfirmClick }
onCancel={ handleCancelClick }
/>
:
null
}
</div>
</div>
)
}
</>
)
}

Modal.propTypes = {
/** The Modal size */
size: PropTypes.oneOf(["small", "large"]),
/** The title of the modal */
title: PropTypes.string,
/** Also the title of the modal, just for API flexibility. If both `title` and `heading` are passed, `title` will win. */
heading: PropTypes.string,
/** Pass a label to render a confirm button and a Cancel button */
confirmButtonLabel: PropTypes.string,
/** Pass a label for the cancel button. Defaults to "Cancel" */
cancelButtonLabel: PropTypes.string,
/** Pass an Icon name to show on the confirming action button */
confirmButtonIcon: PropTypes.oneOf(knownIcons),
/** Pass an icon name to show on the cancelling button */
cancelButtonIcon: PropTypes.oneOf(knownIcons),
/** Whether the modal will be open */
open: PropTypes.bool,
/** The children of the modal. These will be rendered as the modal content. To render custom buttons at the bottom, see `modalFooter` below.*/
children: PropTypes.node,
/** Optional. Pass a `<ModalFooter />` component with custom content as required. Will default to using the `<ModalFooter/>` component internally. */
modalFooter: PropTypes.element,
/** Whether the modal can be closed using an "X"-Button at the top right. Defaults to true. */
closeable: PropTypes.bool,
/** Pass to remove default padding from the content area of the modal */
unpad: PropTypes.bool,
/** Custom className to add to the modal */
className: PropTypes.string,
/** A handler to execute once the modal is confirmed */
//onConfirm: PropTypes.func,
/** A handler to execute once the modal is cancelled or dismissed using the x-Close button, Cancel-button or pressing ESC */
//onCancel: PropTypes.func,
}

Modal.defaultProps = {
size: "small",
title: "",
heading: "",
confirmButtonLabel: "",
cancelButtonLabel: "",
confirmButtonIcon: null,
cancelButtonIcon: null,
open: false,
children: null,
modalFooter: null,
closeable: true,
unpad: false,
className: "",
onConfirm: undefined,
onCancel: undefined,
}
Loading

0 comments on commit 22805b2

Please sign in to comment.