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

breaking(Modal): update shorthand functionality #1599

Merged
merged 9 commits into from
Aug 27, 2017
12 changes: 6 additions & 6 deletions docs/app/Examples/modules/Modal/Types/ModalExampleShorthand.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import React from 'react'
import { Button, Modal } from 'semantic-ui-react'

const ModalShorthandExample = () => (
const ModalExampleShorthand = () => (
<Modal
trigger={<Button>Show Modal</Button>}
header='Delete Your Account'
content='Are you sure you want to delete your account'
header='Reminder!'
content='Call Benjamin regarding the reports.'
actions={[
{ key: 'no', content: 'No', color: 'red', triggerClose: true },
{ key: 'yes', content: 'Yes', color: 'green', triggerClose: true },
'Snooze',
{ key: 'done', content: 'Done', positive: true },
]}
/>
)

export default ModalShorthandExample
export default ModalExampleShorthand
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react'
import { Button, Header, Icon, Modal } from 'semantic-ui-react'

const ModalExampleCloseIcon = () => (
<Modal trigger={<Button>Show Modal</Button>} closeIcon='close'>
<Modal trigger={<Button>Show Modal</Button>} closeIcon>
<Header icon='archive' content='Archive Old Messages' />
<Modal.Content>
<p>Your inbox is getting full, would you like us to enable automatic archiving of old messages?</p>
Expand Down
6 changes: 6 additions & 0 deletions src/lib/customPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,12 @@ export const itemShorthand = (...args) => every([
PropTypes.oneOfType([
PropTypes.node,
PropTypes.object,
PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.node,
PropTypes.object,
]),
),
]),
])(...args)

Expand Down
14 changes: 11 additions & 3 deletions src/modules/Modal/Modal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';

import { SemanticShorthandItem } from '../..';
import { PortalProps } from '../../addons/Portal';
import { default as ModalActions } from './ModalActions';
import { default as ModalActions, ModalActionsProps } from './ModalActions';
import { default as ModalContent, ModalContentProps } from './ModalContent';
import { default as ModalDescription } from './ModalDescription';
import { default as ModalHeader, ModalHeaderProps } from './ModalHeader';
Expand All @@ -13,8 +13,8 @@ export interface ModalProps extends PortalProps {
/** An element type to render as (string or function). */
as?: any;

/** A Modal can be passed action buttons via shorthand. */
actions?: Array<any>;
/** Shorthand for Modal.Actions. Typically an array of button shorthand. */
actions?: SemanticShorthandItem<ModalActionsProps>;

/** A Modal can reduce its complexity */
basic?: boolean;
Expand Down Expand Up @@ -49,6 +49,14 @@ export interface ModalProps extends PortalProps {
/** The node where the modal should mount. Defaults to document.body. */
mountNode?: any;

/**
* Action onClick handler when using shorthand `actions`.
*
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {object} data - All props.
*/
onActionClick?: (event: React.MouseEvent<HTMLElement>, data: ModalProps) => void;

/**
* Called when a close event happens.
*
Expand Down
20 changes: 14 additions & 6 deletions src/modules/Modal/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ class Modal extends Component {
/** An element type to render as (string or function). */
as: customPropTypes.as,

/** Elements to render as Modal action buttons. */
actions: PropTypes.arrayOf(customPropTypes.itemShorthand),
/** Shorthand for Modal.Actions. Typically an array of button shorthand. */
actions: customPropTypes.itemShorthand,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this change. I think that we should use there customPropTypes.collectionShorthand.


/** A modal can reduce its complexity */
basic: PropTypes.bool,
Expand All @@ -45,7 +45,7 @@ class Modal extends Component {
/** Additional classes. */
className: PropTypes.string,

/** Icon. */
/** Shorthand for the close icon. Closes the modal on click. */
closeIcon: PropTypes.oneOfType([
PropTypes.node,
PropTypes.object,
Expand Down Expand Up @@ -76,6 +76,14 @@ class Modal extends Component {
/** The node where the modal should mount. Defaults to document.body. */
mountNode: PropTypes.any,

/**
* Action onClick handler when using shorthand `actions`.
*
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {object} data - All props.
*/
onActionClick: PropTypes.func,

/**
* Called when a close event happens.
*
Expand Down Expand Up @@ -153,10 +161,10 @@ class Modal extends Component {

handleActionsOverrides = predefinedProps => ({
onActionClick: (e, actionProps) => {
const { triggerClose } = actionProps

_.invoke(predefinedProps, 'onActionClick', e, actionProps)
if (triggerClose) this.handleClose(e)
_.invoke(this.props, 'onActionClick', e, this.props)

this.handleClose(e)
},
})

Expand Down
5 changes: 3 additions & 2 deletions src/modules/Modal/ModalActions.d.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import * as React from 'react';
import { ButtonProps } from '../../elements/Button';
import { SemanticShorthandCollection } from '../..';

export interface ModalActionsProps {
[key: string]: any;

/** An element type to render as (string or function). */
as?: any;

/** An element type to render as (string or function). */
actions?: Array<any>;
/** Array of shorthand buttons. */
actions?: SemanticShorthandCollection<ButtonProps>;

/** Primary content. */
children?: React.ReactNode;
Expand Down
11 changes: 4 additions & 7 deletions src/modules/Modal/ModalActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,8 @@ export default class ModalActions extends Component {
/** An element type to render as (string or function). */
as: customPropTypes.as,

/** Elements to render as Modal action buttons. */
actions: customPropTypes.every([
customPropTypes.disallow(['children']),
PropTypes.arrayOf(customPropTypes.itemShorthand),
]),
/** Array of shorthand buttons. */
actions: customPropTypes.collectionShorthand,

/** Primary content. */
children: PropTypes.node,
Expand All @@ -34,10 +31,10 @@ export default class ModalActions extends Component {
className: PropTypes.string,

/**
* onClick handler for an action. Mutually exclusive with children.
* Action onClick handler when using shorthand `actions`.
*
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {object} data - All item props.
* @param {object} data - All props from the clicked action.
*/
onActionClick: customPropTypes.every([
customPropTypes.disallow(['children']),
Expand Down
39 changes: 23 additions & 16 deletions test/specs/modules/Modal/Modal-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,29 +104,36 @@ describe('Modal', () => {
})

describe('actions', () => {
const actions = [
{ key: 'cancel', content: 'Cancel' },
{ key: 'ok', content: 'OK', triggerClose: true },
]
it('closes the modal on action click', () => {
wrapperMount(<Modal actions={['OK']} defaultOpen />)

it('handles onItemClick', () => {
const onActionClick = sandbox.spy()
const event = { target: null }
assertBodyContains('.ui.modal')
domEvent.click('.ui.modal .actions .button')
assertBodyContains('.ui.modal', false)
})

wrapperMount(<Modal defaultOpen actions={{ actions, onActionClick }} />)
it('calls shorthand onActionClick callback', () => {
const onActionClick = sandbox.spy()
const modalActions = { onActionClick, actions: [{ key: 'ok', content: 'OK' }] }
wrapperMount(<Modal actions={modalActions} defaultOpen />)

domEvent.click('.button:last-child')
onActionClick.should.not.have.been.called()
domEvent.click('.ui.modal .actions .button')
onActionClick.should.have.been.calledOnce()
onActionClick.should.have.been.calledWithMatch(event, { content: 'OK' })
})
})

it('handles triggerClose prop on an action', () => {
wrapperMount(<Modal defaultOpen actions={actions} />)
describe('onActionClick', () => {
it('is called when an action is clicked', () => {
const onActionClick = sandbox.spy()
const event = { target: null }
const props = { actions: ['OK'], defaultOpen: true, onActionClick }

domEvent.click('.button:first-child')
assertBodyContains('.ui.modal')
domEvent.click('.button:last-child')
assertBodyContains('.ui.modal', false)
wrapperMount(<Modal {...props} />)
domEvent.click('.ui.modal .actions .button')

onActionClick.should.have.been.calledOnce()
onActionClick.should.have.been.calledWithMatch(event, props)
})
})

Expand Down
2 changes: 1 addition & 1 deletion test/specs/modules/Modal/ModalActions-test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'
import ModalActions from 'src/modules/Modal/ModalActions'

import ModalActions from 'src/modules/Modal/ModalActions'
import * as common from 'test/specs/commonTests'
import { sandbox } from 'test/utils'

Expand Down