Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor/extract expose dialog utility components #1470

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions src/dialog-body/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as DialogBody } from './src/DialogBody'
shreedharbhat98 marked this conversation as resolved.
Show resolved Hide resolved
74 changes: 74 additions & 0 deletions src/dialog-body/src/DialogBody.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React, { memo, forwardRef } from 'react'
import PropTypes from 'prop-types'
import { useStyleConfig } from '../../hooks'
import { Pane } from '../../layers'
import { Paragraph } from '../../typography'

const closeHandler = close => close()

const renderChildren = (children, close) => {
if (typeof children === 'function') {
return children({ close })
}

if (typeof children === 'string') {
return <Paragraph>{children}</Paragraph>
}

return children
}

const DialogBody = memo(
forwardRef(function DialogBody(props, ref) {
const emptyProps = {}
shreedharbhat98 marked this conversation as resolved.
Show resolved Hide resolved

const themedBodyProps = useStyleConfig('DialogBody', emptyProps, emptyProps, emptyProps)

const { contentContainerProps, minHeightContent = 80, onCancel = closeHandler, state, children } = props

return (
<Pane
ref={ref}
data-state={state}
display="flex"
overflow="auto"
flexDirection="column"
minHeight={minHeightContent}
{...themedBodyProps}
{...contentContainerProps}
>
<Pane>{renderChildren(children, onCancel)}</Pane>
</Pane>
)
})
)

DialogBody.propTypes = {
/**
* Children can be a string, node or a function accepting `({ close })`.
* When passing a string, <Paragraph /> is used to wrap the string.
*/
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
/**
* Props that are passed to the content container.
*/
contentContainerProps: PropTypes.object,
/**
* The min height of the body content.
* Makes it less weird when only showing little content.
*/
minHeightContent: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/**
* Function that will be called when the cancel button is clicked.
* This closes the Dialog by default.
*
* `onCancel={(close) => close()}`
*/
onCancel: PropTypes.func,
/**
* State of the dialog passed to dialog.
*/
state: PropTypes.string
shreedharbhat98 marked this conversation as resolved.
Show resolved Hide resolved
}

export default DialogBody
16 changes: 16 additions & 0 deletions src/dialog-body/stories/index.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react'
import { storiesOf } from '@storybook/react'
import Box from 'ui-box'
import { DialogBody } from '../../dialog-body'

storiesOf('dialog-body', module).add('DialogBody', () => (
<Box padding={40}>
{(() => {
document.body.style.margin = '0'
document.body.style.height = '100vh'
})()}
<DialogBody state="dialog body" onCancel={close}>
shreedharbhat98 marked this conversation as resolved.
Show resolved Hide resolved
Its a example of DialogBody
</DialogBody>
</Box>
))
1 change: 1 addition & 0 deletions src/dialog-footer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as DialogFooter } from './src/DialogFooter'
135 changes: 135 additions & 0 deletions src/dialog-footer/src/DialogFooter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import React, { memo, forwardRef } from 'react'
import PropTypes from 'prop-types'
import { Button } from '../../buttons'
import { useStyleConfig } from '../../hooks'
import { Pane } from '../../layers'

const closeHandler = close => close()
const emptyProps = {}

const renderNode = (node, close) => {
if (typeof node === 'function') {
return node({ close })
}

return node
}

const DialogFooter = memo(
forwardRef(function DialogFooter(props, ref) {
const {
footer,
isConfirmDisabled = false,
isConfirmLoading = false,
hasCancel = true,
hasFooter = true,
intent = 'none',
cancelLabel = 'Cancel',
confirmLabel = 'Confirm',
onCancel = closeHandler,
onConfirm = closeHandler
} = props
const themedFooterProps = useStyleConfig('DialogFooter', emptyProps, emptyProps, emptyProps)

if (!footer && !hasFooter) {
return null
}

return (
<Pane ref={ref} display="flex" justifyContent="flex-end" {...themedFooterProps}>
<Pane>
{footer ? (
renderNode(footer, close)
) : (
<>
{/* Cancel should be first to make sure focus gets on it first. */}
{hasCancel && (
<Button tabIndex={0} onClick={() => onCancel(close)}>
{cancelLabel}
</Button>
)}

<Button
tabIndex={0}
marginLeft={8}
appearance="primary"
intent={intent}
isLoading={isConfirmLoading}
disabled={isConfirmDisabled}
onClick={() => onConfirm(close)}
>
{confirmLabel}
</Button>
</>
)}
</Pane>
</Pane>
)
})
)

DialogFooter.propTypes = {
/**
* Function that will be called when the confirm button is clicked.
* This does not close the Dialog. A close function will be passed
* as a paramater you can use to close the dialog.
*
* `onConfirm={(close) => close()}`
*/
onConfirm: PropTypes.func,

/**
* Label of the confirm button.
*/
confirmLabel: PropTypes.string,

/**
* The intent of the Dialog. Used for the button.
*/
intent: PropTypes.string,

/**
* When true, the footer with the cancel and confirm button is shown.
*/
hasFooter: PropTypes.bool,

/**
* You can override the default footer with your own custom component.
*
* This is useful if you want to provide a custom header and footer, while
* also enabling your Dialog's content to scroll.
*
* Footer can either be a React node or a function accepting `({ close })`.
*/
footer: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),

/**
* When true, the cancel button is shown.
*/
hasCancel: PropTypes.bool,

/**
* When true, the confirm button is set to loading.
*/
isConfirmLoading: PropTypes.bool,

/**
* When true, the confirm button is set to disabled.
*/
isConfirmDisabled: PropTypes.bool,

/**
* Function that will be called when the cancel button is clicked.
* This closes the Dialog by default.
*
* `onCancel={(close) => close()}`
*/
onCancel: PropTypes.func,

/**
* Label of the cancel button.
*/
cancelLabel: PropTypes.string
}

export default DialogFooter
52 changes: 52 additions & 0 deletions src/dialog-footer/stories/index.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react'
import { storiesOf } from '@storybook/react'
import Box from 'ui-box'
import { DialogFooter } from '../../dialog-footer'

storiesOf('dialog-footer', module).add('DialogFooter', () => (
<Box padding={40}>
{(() => {
document.body.style.margin = '0'
document.body.style.height = '100vh'
})()}
<DialogFooter
isConfirmDisabled={true}
isConfirmLoading={true}
shreedharbhat98 marked this conversation as resolved.
Show resolved Hide resolved
hasCancel={true}
intent="none"
cancelLabel="Cancel"
confirmLabel="Confirm"
onCancel={close}
onConfirm={close}
/>
<DialogFooter
isConfirmDisabled={true}
isConfirmLoading={false}
hasCancel={true}
intent="none"
cancelLabel="Cancel"
confirmLabel="Confirm"
onCancel={close}
onConfirm={close}
/>
<DialogFooter
isConfirmDisabled={false}
isConfirmLoading={false}
hasCancel={true}
intent="none"
cancelLabel="Cancel"
confirmLabel="Confirm"
onCancel={close}
onConfirm={close}
/>
<DialogFooter
isConfirmDisabled={false}
isConfirmLoading={false}
hasCancel={false}
intent="none"
cancelLabel="Cancel"
confirmLabel="Confirm"
onConfirm={close}
/>
</Box>
))
1 change: 1 addition & 0 deletions src/dialog-header/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as DialogHeader } from './src/DialogHeader'
81 changes: 81 additions & 0 deletions src/dialog-header/src/DialogHeader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React, { memo, forwardRef } from 'react'
import PropTypes from 'prop-types'
import { IconButton } from '../../buttons'
import { useStyleConfig } from '../../hooks'
import { CrossIcon } from '../../icons'
import { Pane } from '../../layers'
import { Heading } from '../../typography'

const renderNode = (node, close) => {
if (typeof node === 'function') {
return node({ close })
}

return node
}

const emptyProps = {}

const DialogHeader = memo(
forwardRef(function DialogHeader(props, ref) {
const { hasClose, hasHeader, header, onCancel, title } = props

const themedHeaderProps = useStyleConfig('DialogHeader', emptyProps, emptyProps, emptyProps)

if (!header && !hasHeader) {
return null
}

return (
<Pane ref={ref} flexShrink={0} display="flex" alignItems="center" {...themedHeaderProps}>
{header ? (
renderNode(header, close)
) : (
<>
<Heading is="h4" size={600} flex="1">
{title}
</Heading>
{hasClose && <IconButton appearance="minimal" icon={CrossIcon} onClick={() => onCancel(close)} />}
</>
)}
</Pane>
)
})
)

DialogHeader.propTypes = {
/**
* When true, the header with the title and close icon button is shown.
*/
hasHeader: PropTypes.bool,

/**
* You can override the default header with your own custom component.
*
* This is useful if you want to provide a custom header and footer, while
* also enabling your Dialog's content to scroll.
*
* Header can either be a React node or a function accepting `({ close })`.
*/
header: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),

/**
* Title of the Dialog. Titles should use Title Case.
*/
title: PropTypes.node,

/**
* When true, the close button is shown
*/
hasClose: PropTypes.bool,

/**
* Function that will be called when the cancel button is clicked.
* This closes the Dialog by default.
*
* `onCancel={(close) => close()}`
*/
onCancel: PropTypes.func
}

export default DialogHeader
15 changes: 15 additions & 0 deletions src/dialog-header/stories/index.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react'
import { storiesOf } from '@storybook/react'
import Box from 'ui-box'
import { DialogHeader } from '../../dialog-header'

storiesOf('dialog-header', module).add('DialogHeader', () => (
<Box padding={40}>
{(() => {
document.body.style.margin = '0'
document.body.style.height = '100vh'
})()}
<DialogHeader hasHeader={true} header="Dialog Header" />
<DialogHeader hasHeader={true} title="Custom title Example" hasClose={true} />
</Box>
))
Loading