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(Dimmer): Add component #447

Merged
merged 14 commits into from
Oct 31, 2016
Merged

feat(Dimmer): Add component #447

merged 14 commits into from
Oct 31, 2016

Conversation

layershifter
Copy link
Member

@layershifter layershifter commented Aug 28, 2016

Fixes #190

@layershifter
Copy link
Member Author

layershifter commented Aug 28, 2016

Component

It seems that Dimmer component might be AutocontrolleComponent, if we want add events from settings.

Simple dimmer

SUI offers Simple dimmer in native CSS, but seems it's not part of Dimmer component.

Dimmer events

Any ideas how we can handle events in automatic mode like there?

Usage

I think that in this part there is nothing that should be in the component.

Settings

I think we need take following from settings part:

  • closable prop, looks usable with page dimmer;
  • events onShow, onHide, onChange - very debatable idea, if we skip these, we can leave component stateless.

@levithomason Other ideas?

@layershifter
Copy link
Member Author

ping @levithomason

@levithomason
Copy link
Member

Responses

Component

I think we first go with a stateless component here. The active prop will be controlled from the parent component. Once we get that, then we can consider if anything in the Dimmer warrants auto controlling its own state, but I don't think it will.

Simple dimmer

There is actually no difference in markup between the "simple css dimmer" and a regular dimmer. The only difference is in how you use it. The jQuery dimmer simply adds the dimmable dimmed classes to the parent of the dimmer and the active classes to the dimmer itself. A simple css dimmer just means the user added the classes to the parent and dimmer child manually.

Dimmer events

Since the component will be controlled by parent state, I don't think we need events. Let's leave those for future feature requests, if needed.

Usage

Agreed, let's leave these out.

Settings

closable - How about closeOnClick? This way we can add other close settings like closeOnEscape if we want.

events - yes, let's skip and go stateless

API Ideas

The parent component can be any component and also needs dimmable dimmed classes. It must have a <div class="ui dimmer" /> as an immediate child for the CSS to work.

The dimmer itself can also have child content, which means we'll need an API for specifying the dimmed component's children and the dimmer's children.

The dimmer centers content horizontally and vertically. Vertical centering requires two child divs. I think we include these two divs in all dimmer content.

Content must be included inside .content .center to display centered correctly in the dimmer.

I'm proposing that the Dimmer have an API to control both the dimmed parent component as well as the dimmer child.

Content Dimmer

Proposing the dimmable/dimmed props be used strictly for the parent component.

<Dimmer dimmable={Segment} dimmed>
  <Header>Overlayable Section</Header>
  <Dimmer active>
    <Header inverted icon='heart' content='Dimmed Message!' />
  </Dimmer>
</Dimmer>
<div class="ui segment dimmable dimmed">
  <h3 class="ui header">
    Overlayable Section
  </h3>
  <div class="ui dimmer transition visible active">
    <div class="content">
      <div class="center">
        <h2 class="ui inverted icon header">
          <i class="heart icon"></i>
          Dimmed Message!
        </h2>
      </div>
    </div>
  </div>
</div>

Page Dimmer

In this case the body must receive the dimmable dimmed classes and the ui dimmer must an immediate child to the body. This would use a Portal as was done in the Modal dimmer. The Portal provides a div as an immediate child to the body.

<Dimmer page active>
  <Header inverted icon='mail' content='Dimmer Message' subheader='Dimmer sub-header' />
</Dimmer>
<body>
  <div class="ui page dimmer transition visible active">
    <div class="content">
      <div class="center">
        <h2 class="ui inverted icon header">
          <i class="mail icon"></i>
          Dimmer Message
          <div class="sub header">Dimmer sub-header</div>
        </h2>
      </div>
    </div>
  </div>
</body>

@layershifter
Copy link
Member Author

I will start on it next week 🍰

@layershifter
Copy link
Member Author

layershifter commented Oct 6, 2016

Component

After use in production, I can agree about stateless Dimmer 👍

I think that it will be class (will have handleClick, handleEscape), but will not have real state:

export default class Dimmer {
  handleClick = () => {}
  handleEscape = () => {}
  render () {}
}

Dimmable

In this case the body must receive the dimmable dimmed classes

It's real pain there, any ideas about body? I want following syntax, but it looks like impossible mission 😮

<Segment>
  <Dimmer active={false}>...</Dimmer>
  ...
</Segment>

There is simple solution, but it's pretty shitty:

const active = true;

<Segment className={Dimmer.dimmable(active)}>
  <Dimmer active={active}>...</Dimmer>
  ...
</Segment>

@levithomason @jcarbo Please advise me something better 🤐

@levithomason
Copy link
Member

levithomason commented Oct 6, 2016

It's real pain there, any ideas about body? I want following syntax, but it looks like impossible mission 😮

Note, there are 2 types of Dimmers. A "Content Dimmer" and a "Page Dimmer".

The "Page Dimmer" has the body as it's parent and needs to add classes to the body. However, the "Content Dimmer" has any component as it's parent and adds classes to it's parent (your use case above).

I've proposed an API for each use case in #447 (comment) above. See above for more, specifics are here:

Content Dimmer

Renders the dimmable component in place, much like a trigger for a Modal. You can then define a Dimmer as a child, which is shown when the dimmed prop is true.

<Dimmer dimmable={Segment} dimmed />

Page Dimmer

Renders to body.

<Dimmer page active />

Conclusion

I think we'll need to render with these two different approaches based on props:

  1. page - renders to body as a page level dimmer
  2. dimmable - renders the dimmable component in place, passing extra props to it
  3. otherwise - renders the Dimmer markup

Alexander Fedyashov added 2 commits October 10, 2016 21:08
@codecov-io
Copy link

codecov-io commented Oct 10, 2016

Current coverage is 99.48% (diff: 100%)

No coverage report found for master at 99f2bbf.

Powered by Codecov. Last update 99f2bbf...052aba9

@layershifter layershifter mentioned this pull request Oct 11, 2016
15 tasks
@layershifter
Copy link
Member Author

Page

Dimmable/Dimmed

SUI adds dimmable dimmed classes to body, it makes window non-scrollable.

_414

I've done this, any better solution there?

handlePageMount = () => {
    debug('handleMount()')
    document.body.classList.add('dimmed', 'dimmable')
  }

  handlePageUnmount = () => {
    debug('handleUnmount()')
    document.body.classList.remove('dimmed', 'dimmable')
  }
}
<Portal onMount={this.handlePageMount} onUnmount={this.handlePageUnmount} />

Handle clickOutside

There is problem with handling outside click. I can't use there Portal's closeOnDocumentClick because Dimmer has 100% width and 100% height.

So, I need handle it inside component, but I can't simply write Dimmer onClick={()} because this handler must omit Dimmer's children (according to SUI examples). Any ideas there?

_415

@jeffcarbs
Copy link
Member

@layershifter - closeOnDocumentClick actually includes the dimmer. It basically means "any click not within the portal content".

Also once #666 is merged you will have a little more control in that regard. You can either specify closeOnDocumentClick which is the default, or closeOnRootNodeClick which basically means "click on the portal wrapper/root node".

@levithomason
Copy link
Member

levithomason commented Oct 14, 2016

closeOnDocumentClick actually includes the dimmer.

@jcarbo is this due to event bubbling? If so, that makes sense because the Dimmer click bubbles to the document where the event handler is actually attached. If however the portal node (div appended to the body) is receiving the click, I think we should go with something like closeOnClickOutside, if the actual document object is not where the event listener is attached.

Could you clarify?

P.S. I also left a comment on #666 regarding closing multiple Modals, LMK.

@layershifter
Copy link
Member Author

@levithomason I'll added dimmable, I've splitted it into separate component. Can you take a look there (DimmerDimmable and blurring example)?

I don't like that we need handle also dimmed prop, any ideas there?


const DimmerExampleDisabled = () => (
<Segment>
<Dimmer disabled />
Copy link
Member

Choose a reason for hiding this comment

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

Hm, do you know what this is supposed to do? The dimmer still is made active when I add:

<Dimmer active disabled />

Though, the description says:

A disabled dimmer cannot be activated

Perhaps this is only applicable to the jQuery plugin in SUI core? Thoughts?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think we can remove disabled prop, it's quite strange to use disabled when you have control of active.

Copy link
Member

Choose a reason for hiding this comment

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

Agreed, especially since it does not override the active prop. Let's remove it.


return (
<div>
<Dimmer blurring dimmable={Segment} dimmed={active}>
Copy link
Member

Choose a reason for hiding this comment

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

This currently does not pass extra props to the Segment. Example, adding color='red' should pass color to the Segment. What if we went with this API, it does pass extra props:

<Dimmer.Dimmable as={Segment} blurring dimmed={active} color='red'>
  <Dimmer active={active} />

  <p>
    <img src='http://semantic-ui.com/images/wireframe/short-paragraph.png' />
  </p>
  <p>
    <img src='http://semantic-ui.com/images/wireframe/short-paragraph.png' />
  </p>
</Dimmer.Dimmable>

I tested the above with only these changes required:

  1. Expose Dimmer.Dimmable = DimmerDimmable
  2. Rename DimmmerDimmable prop Component to as and use our standard getElementType() util and render <ElementType /> instead.
  3. I did not do this, but we should then remove all dimmable prop reference and logic from the main Dimmer.

This might be more in line with our existing patterns.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, you said that I thinked 👍

handlePageMount = () => {
debug('handleMount()')

document.body.classList.add('dimmed', 'dimmable')
Copy link
Member

Choose a reason for hiding this comment

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

I believe we only want to add dimmed if active is true. Is that correct?

Copy link
Member Author

Choose a reason for hiding this comment

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

Not exactly, it added on Portals mount, while Portal will mount on active. This was plan.

@levithomason
Copy link
Member

Left some comments. I think we must handle dimmed since it is on a child component that can appear anywhere in the child tree.

@layershifter
Copy link
Member Author

I've made cleanup there, all examples where added, except example with Image. I was right when I thought that we will need children on Image.

Seems, we need initial review there, I'll add tests after it.

@layershifter layershifter changed the title feat (Dimmer): Add component feat(Dimmer): Add component Oct 23, 2016
@layershifter
Copy link
Member Author

Updated to latest master, completed TODOs and added tests 😸

@levithomason waiting for review 😄 All is ready except, common.rendersChildren(), can yoo take a look there?

@layershifter
Copy link
Member Author

layershifter commented Oct 25, 2016

Added new example with Image and shorthands for it (some style refactor on Image, too).

import ImageGroup from './ImageGroup'
import Dimmer from '../../modules/Dimmer/Dimmer'
import Label from '../Label/Label'
Copy link
Member

@levithomason levithomason Oct 26, 2016

Choose a reason for hiding this comment

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

Just an FYI. There should be index files for all components so we can import ../Label instead of ../Label/Label. We can merge this as is still.

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.

Looks good, left one question regarding stopPropagation. I'm also not sure what you'd like to review on rendersChildren(), please expound.

</DimmerDimmable>
)
_.invoke(children, 'props.onClick', e)
e.stopPropagation()
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to stop propagation here? Would prefer we not prevent handlers above this component from firing.

If we're trying to prevent our own handlers from firing, perhaps we should instead detect if the click was inside rather than stop the event.

common.hasUIClassName(Dimmer)

// TODO: Renders children / content
// common.rendersChildren()
Copy link
Member

Choose a reason for hiding this comment

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

I saw your request to take a look here, but I'm not sure what the issue is. Did this test not work for this component?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, this test is broken. I think problem in cloneElement that I use for handling click. I'll try rework click handling without stopPropagation.

@layershifter
Copy link
Member Author

layershifter commented Oct 26, 2016

stopPropagation replaced with ref, fixed example and corrected tests. Ready to go after review 😄


if (onClick) onClick(e, this.props)
if (this.center && (this.center !== e.target && this.center.contains(e.target))) return
if (onClickOutside) onClickOutside(e, this.props)
Copy link
Member Author

Choose a reason for hiding this comment

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

onClickOutside must be called outside children like in SUI.

@layershifter
Copy link
Member Author

@levithomason Any chance for review? 😺

@levithomason levithomason merged commit 5bb04d2 into master Oct 31, 2016
@levithomason levithomason deleted the feat/dimmer branch October 31, 2016 18:30
@levithomason
Copy link
Member

Thanks much!

@levithomason
Copy link
Member

Released in [email protected]

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