-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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(Popup): add component #570
Conversation
Current coverage is 100% (diff: 100%)@@ master #570 diff @@
====================================
Files 119 122 +3
Lines 1890 2000 +110
Methods 0 0
Messages 0 0
Branches 0 0
====================================
+ Hits 1890 2000 +110
Misses 0 0
Partials 0 0
|
Hey @levithomason, I know that's not your initial design but I feel like adding an anchor element and an open state would simplify the implementation and open the way to additional feature (like a click trigger instead of a hover). The counter argument would be that it would complicate usage, as the user would have to maintain a state for the open status. The anchor element could be transmitted using Something among those lines: export default class PopupOnClick extends Component {
buttonClick = (event) =>
this.setState({
open: true,
target: event.currentTarget,
})
render() {
return <div>
<button onClick={this.buttonClick} >Click me</button>
<Popup
content="foo bar"
open={this.state.open}
target={this.state.target}
/>
</div>
}
} I don't want to impose my style, I came here to help and I'm totally open to your opinion. |
import React, { Component } from 'react' | ||
import { Button, Popup } from 'stardust' | ||
|
||
export default class PopupExample extends Component { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can keep example stateless there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll keep in mind to use stateless function when no state is required 👍
) | ||
|
||
this.setState({ popup }) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Popup will be rerendered on each show.
I've made a few updates to the handling part of the positioning without introducing While having an |
Hey so I wound up writing our own
If you have any feedback on some of the open items on that issue I'd appreciate it. Assuming we like that approach, the sooner we can work out the kinks the sooner we can apply it here. |
@jcarbo this could indeed resolve my initial problem. I would need an additional PS: I believe there could be value in having the popup on focus (help on an input field), so maybe adding onFocus and onBlur event could be nice to have. |
@Debrice - thanks for the suggestions! Wound up implementing all three options (closing |
This is great, thank you @jcarbo , I'll pull your branch in and start implementing today. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is a WIP, here are few comments along the way. Thanks much for getting this going!
import React from 'react' | ||
import { Button, Icon, Popup } from 'stardust' | ||
|
||
export default function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In light of #564, let's name example classes/functions PopupExampleClick
. Both the filename and default export should have the same name as well.
<Button.Group> | ||
<Button color='red'>Activate doomsday device</Button> | ||
<Popup | ||
trigger={<Button color='black' icon><Icon name='question' /></Button>} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The button can be simplified with shorthand syntax here. Shorthand is very useful for cases exactly like this, passing components through props:
trigger={<Button color='black' icon='question' />}
Then, you can also remove the Icon
import.
import React from 'react' | ||
import { Popup, Card, Rating, Image } from 'stardust' | ||
|
||
const users = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused var here, npm run lint
to see lint issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I use the linter (also in my editor), all those variables are being used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I believe I'm getting confused on the new GH review flow. This comment thread does not show it in use for some reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And you are right, I'm so confused 😞 It was a left over from a copy/paste and indeed my linter catch it too. I'm trying to do too many things at the same time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, been there :) Onward!
trigger={IndividualCard} | ||
content={content} | ||
hover | ||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Few points on this one:
- Let's use
children
for defining full markup - Return multi-line JSX inside of parens (
npm run lint
to see all issues) - Use sub components for SUI component parts. See this section in the CONTRIBUTING.md for more details on API design.
Given our design and style patterns noted here ^, this example would look more like this:
return (
<Popup trigger={IndividualCard} hover>
<Popup.Header>User Rating</Popup.Header>
<Popup.Content>
<Rating icon='star' defaultRating={3} maxRating={4} />
</Popup.Content>
</Popup>
)
Note, I also realize now that there is a name conflict between the data-title
prop and the class="header"
component part. Let's go with the Popup class names (header
, etc) for our prop names and sub component names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds good, I was also confused about title and header and was about to ask for confirmation 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Somehow the linter does not complain about those being multi-line.
Great, going to implement those sub rendered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I get this in my editor:
And this from the CLI:
Maybe double check the linter setup and that the latest modules are installed? I know NPM has had some issues as of late, sometimes requiring multiple installs to get all the modules you should've gotten the first go. LMK if you have further issues and we can investigate deeper.
<div> | ||
<Popup | ||
trigger={<Icon circular name='heart' />} | ||
content='Hello. This is a wide pop-up which allows for lots of content |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Single quotes can't handle line breaks. Though we could use backticks to preserve the line breaks, I think we should instead use children
here:
<Popup trigger={<Icon circular name='heart' />} width='wide' hover>
Hello. This is a very wide pop-up which allows for lots of content with additional space.
You can fit a lot of words here and the paragraphs will be pretty wide.
</Popup>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I somehow believed you were against using props.children
, my bad.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries. Our goal is to provide two APIs 1) a "Shorthand Props API" for the majority simplified use cases and 2) a sub component API as an escape hatch when you need access and control over the full markup.
It is worth noting, the two are mutually exclusive. So, you cannot mix a shorthand header
prop while also having children
defined. More here in the CONTRIBUTING.md. Also, I plan to add more info to the docs on this, #561.
Here are some equivalent examples:
<Popup content='The content' />
<Popup>
The content
<Popup>
<Popup header='My Header' content='The content' />
<Popup>
<Popup.Header>My header</Popup.Header>
<Popup.Content>The content</Popup.Content>
<Popup>
Most components have shorthand capabilities you can reference for more examples.
@@ -1,7 +1,8 @@ | |||
import React, { Component, PropTypes } from 'react' | |||
import { findDOMNode } from 'react-dom' | |||
import { META } from '../../lib' | |||
import Portal from 'react-portal' | |||
import Portal from '../../addons/Portal/Portal' | |||
import classnames from 'classnames' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're using cx
as the import name for classnames
. Let's update for consistency.
const positions = (this.props.position || 'top left').split(' ') | ||
if (!coords) return | ||
|
||
const positions = this.props.position.split(' ') | ||
const style = { position: 'absolute' } | ||
|
||
if (positions.includes('right')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
includes
has limited browser support. Let's use _.includes(positions, 'right')
instead here.
Left an interim review for now. LMK when it's stable enough for a full review and I'll pull it down, try it out, and offer more input. Cheers! |
@levithomason I'm at a place where I could use some feedback from a UI experience. I'm not familiar with animation for example and how you expect them to be handled. Also I'm using Positioning will need to have the "center" attribute too, but I can't think of a way to read the size of the popup without using ref with a function (...or //...
popupMounted = (ref) => {
this.setState({'popupBoundingRect': ref.getBoundingClientRect()})
}
render(){
return <Popover ref={this.popupMounted} />
}
} Thank you for your help and patience! |
import { META } from '../../lib' | ||
import Portal from '../../addons/Portal/Portal' | ||
import cx from 'classnames' | ||
import isString from 'lodash/isString' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The standard pattern in this project has been to include lodash as _
as the very first import within the file. We use the babel-plugin-lodash
plugin which handles cherry-picking only the functions you use (see https://github.com/lodash/babel-plugin-lodash#example)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, I got into the habit of cherry picking method of the any library for size concerns. I was wondering if the same applies to stardust element then? If someone only wanted to import the Popup functionality, then if this person was using:
import Popup from 'stardust/modules/Popup'
the way we import Portal in Popup:
import Portal from '../../addons/Portal/Portal'
instead of
import { Portal } from '../../addons'
wouldn't it impact the size of the import by loading the whole addons
content ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, let's use the direct filename import. I've commented in #524 to reflect this issue.
We're forgoing animations until we get all components and APIs done. We are just adding/removing the visibility classes as needed for now. Later, we'll add a utility that reuses SUI CSS transitions by adding these classes (and styles) in sequence to trigger animations. See #200 for more notes on that.
👍 Looks like the Portal is now passing the necessary events, see https://github.com/TechnologyAdvice/stardust/pull/571#issuecomment-250891167
Measuring is one of those things (focus being the other) that you just have to use a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left another round of comments for you. Thanks for putting up with it! I'll try to focus on getting the Portal merged before doing a final here. That way this PR only includes the Popup work.
fluid: PropTypes.bool, | ||
|
||
/** Display the popup on trigger focus */ | ||
focus: PropTypes.bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Propose we pull these into an on
enum prop to keep in line with SUI, but also avoid the confusion of focus={true}
not actually setting focus, but setting the event to open on.
<Popup on='focus' />
<Popup on='hover' />
<Popup on='click' />
} | ||
|
||
/** | ||
* A <Popup /> displays additional information on top of a page. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's stick with the plain text name, Popup
, here to be consistent with other component descriptions.
header: PropTypes.string, | ||
|
||
/** Display the popup on mouse over the trigger */ | ||
hover: PropTypes.bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const { header, content } = this.props | ||
|
||
if (header) { | ||
children.push(<PopupHeader key={index++}>{header}</PopupHeader>) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method can go away in favor of shorthand factories suggested in the main render function. However, for future reference avoid using index
in keys. We have a few in the codebase still that we're removing. It is an anti pattern that breaks React's ability to track/update children.
width, | ||
} = this.props | ||
|
||
const { coords } = this.state |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add portalProps
used below as well. I should add some lint rules for this since we destructure props/state always.
return ( | ||
<Portal trigger={trigger} {...this.state.portalProps}> | ||
<div | ||
style={this.getPosition(coords)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest renaming to this.getStyle()
given the usage.
style={this.getPosition(coords)} | ||
className={classes} | ||
> | ||
{ children || this.renderContent() } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pending adding shorthand create()
methods to the sub components, this.renderChildren()
here can be replaced with this:
render() {
return (
<Portal trigger={trigger} {...portalProps}>
<div style={this.getStyle(coords)} className={classes}>
{children}
{!children && PopupHeader.create(header)}
{!children && PopupContent.create(content)}
</div>
</Portal>
)
}
The create()
shorthand can be created by importing createShorthandFactory()
and adding a static method to each component:
import { createShorthandFactory } from '../../lib'
// PopupHeader.js
PopupHeader.create = createShorthandFactory(PopupHeader, value => ({ children: value }))
// PopupContent.js
PopupContent.create = createShorthandFactory(PopupContent, value => ({ children: value }))
The second argument is the mapValueToProps()
function. When PopupHeader.create('my header')
is called, that function will map the string to children
. Factories also know how to handle numbers, props objects, and elements as a value. So, your props will be able to handle many types of data. You can read more in factories.js
and see it in other components. Again, more docs on the way for this, #561!
import { META } from '../../lib' | ||
|
||
/** | ||
* A <PopupContent /> displays the content body of a Popover. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency <PopupContent />
=> PopupContent
} | ||
|
||
PopupContent.propTypes = { | ||
/** The header of the Popup */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copy pasta? Probably should be content
instead of header
.
import { META } from '../../lib' | ||
|
||
/** | ||
* A <PopupHeader /> displays a header in a Popover. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<PopupHeader />
=> PopupHeader
import React, { Component, PropTypes } from 'react' | ||
import { findDOMNode } from 'react-dom' | ||
import { META } from '../../lib' | ||
import Portal from '../../addons/Portal/Portal' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't require a change, but FYI you can import components as named exports from the category folder index if you prefer:
import { Portal } from '../../addons'
Especially handy when there are several imports:
import { Button, Icon, Label } from '../../elements'
// vs
import Button from '../../elements/Button/Button'
import Icon from '../../elements/Icon/Icon'
import Label from '../../elements/Label/Label'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the case of the second example vs the first, wouldn't that impact the size of the final bundle if someone was to cherry pick a single component for a project ?
import Button from 'stardust/elements/Button'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed you are correct. I hadn't thought that through... It would automatically bundle all "elements". We're targeting small custom builds as part of our goals, #524. So from now on let's stick with directly importing files. Thanks for pointing it out.
@Debrice - could you rebase this PR against the most recent |
@jcarbo I've never rebased any code in the past, so that was quite an adventure (100 commits now). |
@levithomason @jcarbo I'm a bit unfamiliar with rebasing/cleaning up a PR . I believe I have 2 options now:
Any advice ? |
I'd squash down the current branch to one commit. So assuming 2c144d1 is the commit you're rebased off of you would do:
All commits on a feature branch wind up getting squashed down to one anyway when they're merged into master but this would just clean things up a bit for now. |
@jcarbo thanks for the help, force push is done 🎊 Regarding Portal, I need to build Should that be a feature of the Portal or the Popup? For it to be on the Popup, I believe I would still need some changes on the Portal to grant more control on the event. |
Regarding the hoverable prop, I'd think that would set the Portal This would leave the Popup open with no way to close it, since blur (mouseLeave) doesn't. Perhaps we'd use some kind of timer that would close the Popup but reset on hover. This way the user can move the mouse off the trigger (timer starts), mouse enters the Popup (timer cancelled), mouse leaves Popup (timer starts again), finally after X seconds the timer expires and the Popup closes. |
I think we can handle it by modifying one prop and adding another:
I can also see adding two other props for symmetry:
I'll open up a quick PR with these additions unless there are any objections. |
Indeed, my branch was old, apologies! @jcarbo agreed, let's feature freeze this where it is, cleanup and merge. Then any extras can come later. I'll start a final review now. |
@levithomason in that case I'll work on spec ASAP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If any of these comments seem like adding features, let's comment out the feature with a TODO and push them off to another PR. This way we can break down this work into smaller pieces 👍
import React from 'react' | ||
import { Button, Popup } from 'semantic-ui-react' | ||
|
||
export default function PopupClickExample() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In keeping with other exmples, let's update the example syntax in this PR from:
export default function PopupClickExample() {
return (
// ...
)
}
To:
export default const PopupClickExample = () => (
// ...
)
import React from 'react' | ||
import { Input, Popup } from 'semantic-ui-react' | ||
|
||
export default function PopupFocusExample() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Commenting here, but for all example filenames and default export names. Let's use the latest pattern PopupExample___.
:
PopupExampleFocus.js
export default PopupExampleFocus
PopupExampleHideOnScroll.js
export default PopupExampleHideOnScroll
...
return ( | ||
<div> | ||
<Popup | ||
trigger={<Button><Icon name='add' />Add a friend</Button>} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer shorthand icon button here for brevity and less imports (no Icon import required):
trigger={<Button icon='add' content='Add a friend' />}
import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' | ||
|
||
export default function () { | ||
return ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All files' default export should match the filename. Since index.js files don't make sense in this pattern, we're using the "Component + Directory + Examples". Also note the const vs function, let's update to use:
const PopupTriggersExamples = () => (
// ...
)
export default PopupTriggersExamples
return ( | ||
<div> | ||
<Popup | ||
trigger={<Button icon><Icon name='add' /></Button>} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use shorthand icon button:
<Button icon='add' />
flowing: PropTypes.bool, | ||
|
||
/** Takes up the entire width of its offset container */ | ||
fluid: PropTypes.bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this example is not working, let's comment this out with a TODO.
trigger: PropTypes.node, | ||
|
||
/** Popup width */ | ||
width: PropTypes.string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are fixed widths, though I think we should update this to wide
and wide='very'
given the SUI classes at hand.
_meta = {
props: {
wide: ['very']
},
}
wide: customPropTypes.some([
PropTypes.bool,
PropTypes.oneOf(_meta.props.wide)
]),
Here's more context as to why the rename. The
width
is primarily used for components that have grid column style widths, or a group component that defines the widths of its children. It takes a number or a word (width={4}
width='four'
). In this case, there it is more like thepadded
orrelaxed
prop, where it is either boolean or takes avery
value.
export default function PopupOffsetExample() { | ||
return ( | ||
<div> | ||
<Popup |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can remove the extra div
wrapper here and just return the <Popup />
. As long as a single element is returned, React is happy. React only requires an extra containing element (such as a div
) if multiple siblings need to be returned.
style.right -= offset | ||
} else { | ||
style.left += offset | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Expected Behavior
This logic seems to always offset to the right. I'd expect the offset to be in the opposite direction of the position:
position='top center' // offset upward
position='left center' // offset to the left
position='bottom center' // offset downward
Or, provide an offset object with +/- horizontal
and vertical
values that would be applied:
position='top center' // offset upward
offset={{ vertical: -5 }}
position='top center' // offset downward
offset={{ vertical: 5 }}
Current Behavior
position='top center'
position='left center'
position='bottom center'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought of the same thing, but it seems like SUI offset is only acting on the X axis and from left to right. I can change to one of your suggestion if you want me to
|
||
return ( | ||
<Portal trigger={trigger} {...this.getPortalProps()}> | ||
<div ref={this.popupMounted} style={style} className={classes} > |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra space here before closing angle: className={classes} >
|
||
return ( | ||
<Portal trigger={trigger} {...this.getPortalProps()}> | ||
<div ref={this.popupMounted} style={style} className={classes} > |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there might be ElementType
from getElementType
instead of div
.
const ElementType = getElementType(Popup, props)
<ElementType {...rest} ...>...</ElementType>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch
* A PopupContent displays the content body of a Popover. | ||
*/ | ||
export default function PopupContent(props) { | ||
return <div className='content'>{ props.children }</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use there classnames
, ElementType
and getUnhandledProps
.
const { children, className } = props
const classes = cx('content', className)
const rest = getUnhandledProps(PopupContent, props)
const ElementType = getElementType(PopupContent, props)
return <ElementType {...rest} className={classes}>{children}</ElementType>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, nice catch. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the guidance guys, your reviews are super helpful! 👍
@levithomason, @layershifter I addressed the PR comments, will start working on the specs now. Also @jcarbo I can't get the focus through click to behave on the trigger (the popup appears and disappear almost instantly). Would you mind checking that my Portal config https://github.com/TechnologyAdvice/stardust/pull/570/files#diff-daca312d5932d8a0a079b86773c0ee3cR206 ? |
Hey @Debrice that was "unfixed" when I added --- a/src/addons/Portal/Portal.js
+++ b/src/addons/Portal/Portal.js
@@ -205,14 +205,14 @@ class Portal extends Component {
e.stopPropagation()
this.close(e)
} else if (!open && openOnTriggerClick) {
- // Prevents closeOnDocumentClick from closing the portal when
- // openOnTriggerFocus is set. Focus shifts on mousedown so the portal opens
- // before the click finishes so it may actually wind up on the document.
- e.nativeEvent.stopImmediatePropagation()
-
e.stopPropagation()
this.open(e)
}
+
+ // Prevents closeOnDocumentClick from closing the portal when
+ // openOnTriggerFocus is set. Focus shifts on mousedown so the portal opens
+ // before the click finishes so it may actually wind up on the document.
+ e.nativeEvent.stopImmediatePropagation()
}
handleTriggerFocus = (e) => { Also, can you squash your commits back down again? The reason your Once you squash again, any time you want to pull in the latest changes from master if you use rebase you shouldn't have any conflicts and your work will be back on top without any merge commits. |
Great, thank you very much @jcarbo, this fixes the issue indeed! I will do the rebase very soon, once I'm done wrapping up the unit test. |
@levithomason I think we're ready for the final review. |
|
||
const style = _.assign({}, this.state.style, this.props.style) | ||
|
||
const classes = cx( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update usage of cx
to follow the standard pattern of this lib (see https://github.com/TechnologyAdvice/stardust/blob/master/src/collections/Grid/Grid.js for an example). There are helpers to set the appropriate class based on the prop.
For example, wide
here can use the useKeyOrValueAndKey (see similar prop usage: https://github.com/TechnologyAdvice/stardust/blob/master/src/collections/Grid/Grid.js#L34-L35)
@jcarbo sounds good, I'll work on getting 100% coverage. Thanks for the heads up |
@jcarbo I'm almost done. I'm missing some positioning/sizing tests for which I haven't yet find a solution. |
Coverage ReportYou can also get a killer coverage report by opening:
Investigating CoverageYou can sort, drill down to directories/files, and see specific lines: TYPO: I meant, when positions does NOT include 'right' :) |
Thanks @levithomason, I found the coverage yesterday evening after @jcarbo comment but I really appreciate how all of you go above and beyond to guide and help. I know it's a bit late but, is there some guide I should read ? Also, which element would incarnate the project code style (including test and example) ? For example, many example are using Components while being stateless which turns out to be something we don't want. If we could illustrate guidance through an exemplary implementation of a SUI element I believe a new contributor (like myself) could save you a lot of time. Finally, testing the positioning logic turns out to be quite a puzzle, I really want to to cover it fully prior to release but it might take a few more days. |
Not sure if you've read the CONTRIBUTING.md yet. It is a walkthrough from cloning the repo, to documenting and testing your component. So, it is meant to be read like a book, from top to bottom. It is fairly comprehensive, but could always be better. LMK if you feel something is missing from there. |
@levithomason did the CONTRIBUTING.md get deleted? |
Whoops, no it moved to |
Current coverage is 100% (diff: 100%)@@ master #570 diff @@
====================================
Files 119 122 +3
Lines 1890 2000 +110
Methods 0 0
Messages 0 0
Branches 0 0
====================================
+ Hits 1890 2000 +110
Misses 0 0
Partials 0 0
|
@jcarbo and @levithomason, the branch finally got a 100% code coverage 🎉 I believe I could test the library more but I need more research on phantomJS ( |
@levithomason I think we are good for the final review (unless there is something else I didn't see) 😄 |
Cool, this looks like a great first shipment for this component. Thanks for hanging in there and pioneering this stuff! |
Released in |
Thank you guys for your patience and especially @jcarbo for his great work on porting and upgrading the Portal lib! I had a blast 👍 |
Port of SUI Popup module
Fixes #193
To be done later: