Skip to content

Commit 2e1ddee

Browse files
authored
breaking(Modal): update shorthand functionality (#1599)
* fix(Modal): update actions prop type * fix(Modal): update closeIcon prop types * wip * feat(Modal): add onItemClick prop * docs(ModalExampleShorthand): fix default export name * fix(customPropTypes): allow itemShorthand arrays * fix(Modal): fix actions shorthand types and tests * fix(Modal): fix closeIcon type * typings(ModalActions): run lint fix
1 parent e3c4972 commit 2e1ddee

File tree

9 files changed

+69
-42
lines changed

9 files changed

+69
-42
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import React from 'react'
22
import { Button, Modal } from 'semantic-ui-react'
33

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

16-
export default ModalShorthandExample
16+
export default ModalExampleShorthand

docs/app/Examples/modules/Modal/Variations/ModalExampleCloseIcon.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react'
22
import { Button, Header, Icon, Modal } from 'semantic-ui-react'
33

44
const ModalExampleCloseIcon = () => (
5-
<Modal trigger={<Button>Show Modal</Button>} closeIcon='close'>
5+
<Modal trigger={<Button>Show Modal</Button>} closeIcon>
66
<Header icon='archive' content='Archive Old Messages' />
77
<Modal.Content>
88
<p>Your inbox is getting full, would you like us to enable automatic archiving of old messages?</p>

src/lib/customPropTypes.js

+6
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,12 @@ export const itemShorthand = (...args) => every([
270270
PropTypes.oneOfType([
271271
PropTypes.node,
272272
PropTypes.object,
273+
PropTypes.arrayOf(
274+
PropTypes.oneOfType([
275+
PropTypes.node,
276+
PropTypes.object,
277+
]),
278+
),
273279
]),
274280
])(...args)
275281

src/modules/Modal/Modal.d.ts

+11-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as React from 'react';
22

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

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

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

52+
/**
53+
* Action onClick handler when using shorthand `actions`.
54+
*
55+
* @param {SyntheticEvent} event - React's original SyntheticEvent.
56+
* @param {object} data - All props.
57+
*/
58+
onActionClick?: (event: React.MouseEvent<HTMLElement>, data: ModalProps) => void;
59+
5260
/**
5361
* Called when a close event happens.
5462
*

src/modules/Modal/Modal.js

+14-6
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ class Modal extends Component {
3333
/** An element type to render as (string or function). */
3434
as: customPropTypes.as,
3535

36-
/** Elements to render as Modal action buttons. */
37-
actions: PropTypes.arrayOf(customPropTypes.itemShorthand),
36+
/** Shorthand for Modal.Actions. Typically an array of button shorthand. */
37+
actions: customPropTypes.itemShorthand,
3838

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

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

79+
/**
80+
* Action onClick handler when using shorthand `actions`.
81+
*
82+
* @param {SyntheticEvent} event - React's original SyntheticEvent.
83+
* @param {object} data - All props.
84+
*/
85+
onActionClick: PropTypes.func,
86+
7987
/**
8088
* Called when a close event happens.
8189
*
@@ -153,10 +161,10 @@ class Modal extends Component {
153161

154162
handleActionsOverrides = predefinedProps => ({
155163
onActionClick: (e, actionProps) => {
156-
const { triggerClose } = actionProps
157-
158164
_.invoke(predefinedProps, 'onActionClick', e, actionProps)
159-
if (triggerClose) this.handleClose(e)
165+
_.invoke(this.props, 'onActionClick', e, this.props)
166+
167+
this.handleClose(e)
160168
},
161169
})
162170

src/modules/Modal/ModalActions.d.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import * as React from 'react';
22
import { ButtonProps } from '../../elements/Button';
3+
import { SemanticShorthandCollection } from '../..';
34

45
export interface ModalActionsProps {
56
[key: string]: any;
67

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

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

1314
/** Primary content. */
1415
children?: React.ReactNode;

src/modules/Modal/ModalActions.js

+4-7
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,8 @@ export default class ModalActions extends Component {
2121
/** An element type to render as (string or function). */
2222
as: customPropTypes.as,
2323

24-
/** Elements to render as Modal action buttons. */
25-
actions: customPropTypes.every([
26-
customPropTypes.disallow(['children']),
27-
PropTypes.arrayOf(customPropTypes.itemShorthand),
28-
]),
24+
/** Array of shorthand buttons. */
25+
actions: customPropTypes.collectionShorthand,
2926

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

3633
/**
37-
* onClick handler for an action. Mutually exclusive with children.
34+
* Action onClick handler when using shorthand `actions`.
3835
*
3936
* @param {SyntheticEvent} event - React's original SyntheticEvent.
40-
* @param {object} data - All item props.
37+
* @param {object} data - All props from the clicked action.
4138
*/
4239
onActionClick: customPropTypes.every([
4340
customPropTypes.disallow(['children']),

test/specs/modules/Modal/Modal-test.js

+23-16
Original file line numberDiff line numberDiff line change
@@ -104,29 +104,36 @@ describe('Modal', () => {
104104
})
105105

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

112-
it('handles onItemClick', () => {
113-
const onActionClick = sandbox.spy()
114-
const event = { target: null }
110+
assertBodyContains('.ui.modal')
111+
domEvent.click('.ui.modal .actions .button')
112+
assertBodyContains('.ui.modal', false)
113+
})
115114

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

118-
domEvent.click('.button:last-child')
120+
onActionClick.should.not.have.been.called()
121+
domEvent.click('.ui.modal .actions .button')
119122
onActionClick.should.have.been.calledOnce()
120-
onActionClick.should.have.been.calledWithMatch(event, { content: 'OK' })
121123
})
124+
})
122125

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

126-
domEvent.click('.button:first-child')
127-
assertBodyContains('.ui.modal')
128-
domEvent.click('.button:last-child')
129-
assertBodyContains('.ui.modal', false)
132+
wrapperMount(<Modal {...props} />)
133+
domEvent.click('.ui.modal .actions .button')
134+
135+
onActionClick.should.have.been.calledOnce()
136+
onActionClick.should.have.been.calledWithMatch(event, props)
130137
})
131138
})
132139

test/specs/modules/Modal/ModalActions-test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react'
2-
import ModalActions from 'src/modules/Modal/ModalActions'
32

3+
import ModalActions from 'src/modules/Modal/ModalActions'
44
import * as common from 'test/specs/commonTests'
55
import { sandbox } from 'test/utils'
66

0 commit comments

Comments
 (0)