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

feat(Modal): Create a Modal using shorthand props #1508

Merged
merged 21 commits into from
Apr 18, 2017

Conversation

josie11
Copy link
Contributor

@josie11 josie11 commented Mar 25, 2017

  • can pass modal header, content, and actions via shorthand props (#1390)

  • can pass actions props a triggerClose option that will allow buttons to control open/closing modal

@codecov-io
Copy link

codecov-io commented Mar 25, 2017

Codecov Report

Merging #1508 into master will increase coverage by <.01%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1508      +/-   ##
==========================================
+ Coverage   99.74%   99.74%   +<.01%     
==========================================
  Files         141      141              
  Lines        2384     2396      +12     
==========================================
+ Hits         2378     2390      +12     
  Misses          6        6
Impacted Files Coverage Δ
src/modules/Modal/ModalActions.js 100% <100%> (ø) ⬆️
src/modules/Modal/Modal.js 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 573a0da...09db74c. Read the comment docs.

Copy link
Member

@levithomason levithomason left a comment

Choose a reason for hiding this comment

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

Thanks for the work! Here's an initial pass review on progress so far.

@@ -265,6 +279,18 @@ class Modal extends Component {
<ElementType {...rest} className={classes} style={{ marginTop }} ref={this.handleRef}>
{Icon.create(closeIconName, { onClick: this.handleClose })}
{children}
{_.isNil(children) && header && ModalHeader.create(header)}
Copy link
Member

Choose a reason for hiding this comment

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

We can remove the && header check for each shorthand create method as the factories handle falsy values internally.

@@ -37,4 +38,6 @@ ModalActions.propTypes = {
className: PropTypes.string,
}

ModalActions.create = createShorthandFactory(ModalActions, children => ({ children }))
Copy link
Member

Choose a reason for hiding this comment

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

Because there can be many buttons, we need to allow the user to pass a key or our childKey prop. The third parameter to acreateShorthandFactory function is a boolean telling the factory to accept keys:

createShorthandFactory(ModalActions, children => ({ children }), true)

Next, this should probably map its value to an actions prop rather than children. The render method will then map over the actions and create buttons:

createShorthandFactory(ModalActions, actions => ({ actions }), true)

This enables users to use the actions prop on the Modal.Actions themselves as well:

<Modal>
  <Modal.Actions actions={['No', { primary: true, content: 'Yes' }]} />
</Modal>

}
action.onClick = onClick(action.onClick)
}
return Button.create(action, { key: i })
Copy link
Member

Choose a reason for hiding this comment

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

Using index for a key is an antipattern. Instead, we can configure the factory to allow the to pass a key. See comment on ModalActions.create below.

@@ -53,15 +57,21 @@ class Modal extends Component {
/** Whether or not the Modal should close when the document is clicked. */
closeOnDocumentClick: PropTypes.bool,

/** Simple text content for the Modal. */
content: 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.

As we update propTypes, we should also update the typings in the same directory. Example, Modal.d.ts.

@josie11
Copy link
Contributor Author

josie11 commented Mar 27, 2017

I've made the changes.

@@ -27,6 +35,9 @@ ModalActions._meta = {
}

ModalActions.propTypes = {
/** Elements to render as Modal action buttons. */
actions: PropTypes.arrayOf(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.

as is always first in propTypes


/**
* A modal can contain a row of actions.
*/
function ModalActions(props) {
const { children, className } = props
const { children, className, actions } = props
Copy link
Member

Choose a reason for hiding this comment

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

Please sort props in alphabetical order

{children}
{_.isNil(children) && actions.map((action) => Button.create(action, true))}
</ElementType>
)
Copy link
Member

Choose a reason for hiding this comment

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

I think we can make there a short condition:

if(!_.isNil(children)) return <ElementType {...rest} className={classes}>{children}</ElementType>

return (
  <ElementType {...rest} className={classes}>
    {actions.map(action => Button.create(action, true))}
  </ElementType>
)

if (callback) callback(e, this.props)
this.handleClose()
}
action.onClick = onClick(action.onClick)
Copy link
Member

Choose a reason for hiding this comment

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

Can we move event handler to a class method?

@josie11
Copy link
Contributor Author

josie11 commented Mar 28, 2017

I've made the changes, and I added an example.

Copy link
Member

@levithomason levithomason left a comment

Choose a reason for hiding this comment

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

Here is another interim review. We're getting closer here, thanks for continuing on this!

import { Button, Modal } from 'semantic-ui-react'

const ModalShorthandExample = () => (
<Modal trigger={<Button>Show Modal</Button>}
Copy link
Member

Choose a reason for hiding this comment

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

trigger on new line please.


return (
<ElementType {...rest} className={classes}>
{actions.map((action) => Button.create(action, true))}
Copy link
Member

Choose a reason for hiding this comment

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

Ah, this is a new use case. We currently don't have a way to enable keys for a create method during runtime like this. Instead, the create method itself is defined as supporting keys or not. The button currently does not support keys.

The second param here is actually defaultProps. In order to support keys, we need to pass true as the 3rd param to createShorthandFactory for the Button's create method:

-Button.create = createShorthandFactory(Button, value => ({ content: value }))
+Button.create = createShorthandFactory(Button, value => ({ content: value }), true)

We have a PR up for factory updates. I'll enable keys for all factories in light of this issue. We want users to be able to use the create method themselves as well, and, they may need keys.

actions: [],
}

ModalActions.create = createShorthandFactory(ModalActions, actions => ({ actions }), true)
Copy link
Member

Choose a reason for hiding this comment

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

Because there will only be one instance of Modal.Actions, the Modal.Actions component itself won't need to have keys. We can remove true from here.

See above comment regarding Button.create which is what will need the keys, since, there is an array of button elements being created (within Modal.Actions).

@josie11
Copy link
Contributor Author

josie11 commented Mar 29, 2017

Changes are made - I've enjoyed learning whats going on under the hood a bit more.

Copy link
Member

@levithomason levithomason left a comment

Choose a reason for hiding this comment

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

Just one change on this review. I think we're ready to get some tests in here as well 👍

We have a common test for testing shorthand props. Search for implementsShorthandProp in other tests to see how it works. You'll have a bit of a tricky case with actions when using triggerClose as this is not a Button prop and needs to be removed from the object.

/** Primary content. */
children: PropTypes.node,

/** Additional classes. */
className: PropTypes.string,
}

ModalActions.defaultProps = {
actions: [],
}
Copy link
Member

Choose a reason for hiding this comment

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

Since shorthand is disallowed with children, we'll want to avoid setting default values for it. Otherwise, when a user uses children they will also have conflicting actions array defined unawares.

Instead, in the render method, just use _.map which will gracefully handle undefined values:

-{actions.map((action) => Button.create(action))}
+{_.map(actions, action => Button.create(action))}

@josie11
Copy link
Contributor Author

josie11 commented Mar 31, 2017

I added some tests to Modal. Should the situation with triggerClose/Button be throwing an error? I could prevent triggerClose from being passed to ModalActions by using _.omit(action, 'triggerClose') and not using _.forEach.

I had to add _.isPlanObject(action) to prevent implementsShorthandProp from failing for actions:

{_.isNil(children) && ModalActions.create(_.forEach(actions, (action) => {
  if (_.isPlainObject(action) && action.triggerClose) action.onClick this.handleTriggerClose(action.onClick)
}))}

@josie11
Copy link
Contributor Author

josie11 commented Apr 6, 2017

Is there anything I should change/fix here?

Copy link
Member

@layershifter layershifter left a comment

Choose a reason for hiding this comment

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

@josie11 Thanks for PR, again 👍 I shipped some updates there. I'll finish review tomorrow, need more time on tests

@levithomason
Copy link
Member

Thanks, folks! As a heads up, I'll be out on travel for a week starting today. Expect silence from me during this time, however, I will try to catch some mobile updates as I am able.

Copy link
Member

@layershifter layershifter left a comment

Choose a reason for hiding this comment

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

I've updated tests. @levithomason we need your review there 😄

…React into modal

# Conflicts:
#	src/modules/Modal/ModalActions.js
@layershifter
Copy link
Member

@levithomason I've resolved merge conflicts, will be glad to receive your review there 👍

@levithomason
Copy link
Member

Changes look great, merging!

@levithomason levithomason merged commit c397f3e into Semantic-Org:master Apr 18, 2017
@josie11 josie11 deleted the modal branch April 18, 2017 17:51
@levithomason
Copy link
Member

Released in semantic-ui-react@0.68.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants