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

Fix overlay issues #276

Merged
merged 27 commits into from
Dec 2, 2016
Merged

Fix overlay issues #276

merged 27 commits into from
Dec 2, 2016

Conversation

cmslewis
Copy link
Contributor

@cmslewis cmslewis commented Nov 30, 2016

PR checklist

What changes did you make?

  • Introduce a pt-overlay class to serve as the top-level wrapper descriptor for the overlay component.
    As a consumer of Blueprint in the past, I was always much happier when everything had a clear CSS class attached to it. CSS rules downstream are much easier to grok and maintain when applied to meaningfully named elements, rather than opaque HTML tag names.

  • Introduce a .pt-overlay-inline class and apply it to the .pt-overlay element iff the overlay is being rendered inline.
    This allows us to set .pt-overlay-backdrop and/or overlay content to position: absolute when the overlay is rendered inline, so that the overlay intuitively positions itself relative to its parent in that case.

  • Introduce a .pt-overlay-content class that we apply under-the-hood to overlay content containers to manage CSS position mode on behalf of the developer.
    Otherwise, Blueprint users would have to explicitly set position:fixed for non-inline and position:absolute for inline rendering. That'd be brittle.

  • Set .pt-overlay-backdrop to position:fixed if the overlay is not rendered inline.
    This is the crucial fix for Overlay doesn't stretch 100% height #183.

  • Disable document scrolling beneath the overlay backdrop when hasBackdrop is true.

  • Reorganize CSS a bit to ensure that overflow-scrolling still works.

  • Tweaked unit tests:

    • Added tests for the new body scrolling behavior
    • Explicitly unmount an errant tooltip that was causing .pt-overlay-open to stay on the body after all the tests ran via gulp karma-unit-core
    • Bonus: Noticed that some <Portal> tests aren't properly detaching containers after they run. Fixed.

Abandoned Changes:

  • Introduce a backgroundScrollingDisabled prop on IBackdropProps that, when true, disables scrolling of content beneath the overlay backdrop. Underneath the hood, this adds a .pt-overlay-open class to the document body that sets overflow: hidden when applied.

Is there anything you'd like reviewers to focus on?

  • Does this approach make sense?
  • Could anything be named more clearly?

top: $overlay-example-width / 2;
left: calc(50% - #{$overlay-example-width / 2});
z-index: $pt-z-index-overlay + 1;
width: $overlay-example-width;

.pt-overlay-inline & {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this necessary? Looks like line 105 in _overlay.scss does the same thing

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah this is necessary. Line 105 applies this style to .pt-overlay-backdrop; this .doc-overlay-example-transition container is a sibling of .pt-overlay-backdrop, and it needs to be styled separately.

This does bring up a broader annoyance that users will need to explicit;y style overlay content with position: absolute if they want it to render properly inline. Maybe we should add a class (e.g. pt-overlay-content) on all siblings of .pt-overlay-backdrop that handles this position styling automatically?

Copy link
Contributor

Choose a reason for hiding this comment

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

Indeed, users shouldn't have to explicitly style their overlay content, we should handle that automatically for them

Copy link
Contributor Author

@cmslewis cmslewis Nov 30, 2016

Choose a reason for hiding this comment

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

Done. 👍 See the updated PR description. I've introduced a .pt-overlay-content class that we add to the overlay content container(s) automatically, to govern fixed/absolute position mode on behalf of Blueprint users.

@@ -84,6 +84,11 @@ export interface IBackdropProps {
backdropProps?: React.HTMLProps<HTMLDivElement>;

/**
* Whether underlying content should be scrollable while the backdrop is visible.
*/
backgroundScrollingDisabled?: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this configurable? Have you seen (or can you imagine) any use case where false would make sense?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not off the top of my head, no. @giladgray said something in #183 about making this class prop-configurable. Not quite sure in what that meant - Gilad can you clarify? Happy to get rid of this if we want.

Copy link
Contributor

Choose a reason for hiding this comment

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

For Overlay, I think it should be configurable because it is a low level component. I can imagine a sidebar created with Overlay which allows scrolling on the main content. It should be set to false for Dialogs, though.

Copy link
Contributor

Choose a reason for hiding this comment

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

i don't see a need for this prop. an overlay with a backdrop should always prevent scrolling. i can't see a case where you'd want a backdrop and the ability to scroll the document underneath.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, I feel like we already have a prop for that: hasBackdrop. If it's set to true, you have a backdrop behind and you can't scroll. If it's false, you don't have a background behind and you can scroll.

Enabling this extra backgroundScrollingDisabled prop means that we think there's a use case for having a backdrop behind and being able to scroll what's beneath it. I'm skeptical about that but open to it.

What about canOutsideClickClose found in Overlay, and isModal found in Popover? I feel like there's a lot of repetition going on... Maybe isModal is what we want instead of backgroundScrollingDisabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Killing this prop. 👍

Copy link
Contributor Author

@cmslewis cmslewis Nov 30, 2016

Choose a reason for hiding this comment

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

Done. FWIW we can easily add this back later if we observe a need for it.

EDIT: Relying on hasBackdrop instead. If true, disable document scrolling beneath the backdrop; if false, don't.

Copy link
Contributor

Choose a reason for hiding this comment

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

cool, you're right, this makes sense @giladgray @llorca

@blueprint-bot
Copy link

Unmount lingering portal elements after their unit tests complete

Preview: docs | table Coverage: core | datetime

@blueprint-bot
Copy link

Delete superfluous call to portal.unmount()

Preview: docs | table Coverage: core | datetime

@@ -72,9 +72,14 @@ Styleguide components.overlay.scroll

$overlay-background-color: rgba($black, 0.7);

// restricts scrolling of underlying content while the overlay is open
Copy link
Contributor

Choose a reason for hiding this comment

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

this feels a little invasive. is it absolutely necessary?

Copy link
Contributor

Choose a reason for hiding this comment

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

nvm, ignore me, I caught up on the linked thread

Copy link
Contributor Author

@cmslewis cmslewis Nov 30, 2016

Choose a reason for hiding this comment

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

Done (no changes made)

@@ -84,6 +84,11 @@ export interface IBackdropProps {
backdropProps?: React.HTMLProps<HTMLDivElement>;

/**
* Whether underlying content should be scrollable while the backdrop is visible.
*/
backgroundScrollingDisabled?: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

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

For Overlay, I think it should be configurable because it is a low level component. I can imagine a sidebar created with Overlay which allows scrolling on the main content. It should be set to false for Dialogs, though.

// when rendered inline, we want the overlay to position itself relative to
// its parent container, not relative to the whole window.
.pt-overlay-inline & {
@include position(absolute, 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

position: absolute should be enough.
this mixin generates five lines, four of which are the same as line 92.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done 👍

@@ -172,8 +178,14 @@ export class Overlay extends React.Component<IOverlayProps, IOverlayState> {
</CSSTransitionGroup>
);

const mergedClassName = classNames({
[Classes.OVERLAY]: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

classNames(Classes.OVERLAY, { ... }, className)

no need to include it in the object if it's always true.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah realized that after opening the PR but forgot to change it. Good catch. Done. 👍

@@ -19,6 +19,7 @@ describe("<Overlay>", () => {
let wrapper: ReactWrapper<IOverlayProps, any>;

afterEach(() => {
document.body.classList.remove(Classes.OVERLAY_OPEN);
Copy link
Contributor

Choose a reason for hiding this comment

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

why in the test? i'd expect the component to handle removing the class.
probably means you need to also be removing it in componentWillUnmount()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, yes, I noticed that when I was fixing other tests but didn't come back to omit it here. Done. 👍

@llorca
Copy link
Contributor

llorca commented Nov 30, 2016

Besides the few comments, seems to work like wonder! As I understand, this solves #183 entirely and makes #187 irrelevant, correct? If that's the case, it's pretty great that we're able to keep body as the scroll container.

@cmslewis
Copy link
Contributor Author

cmslewis commented Nov 30, 2016

@llorca yep, this completely solves #183 and also supersedes #187...to some extent. But that PR also fixes #231, #240, and #273 IIRC—this PR does NOT fix those issues, so there's still some work to do after we merge this.

@cmslewis
Copy link
Contributor Author

@giladgray @adidahiya @llorca ready for re-review. See the updated PR description for an overview of the new scope of changes.

// so that our class will override the position style of other classes that might
// have set unnecessary position rules (note that CSS specificity plays a role here;
// our class will only override an existing position rule if it has the same or
// greater selector specificity).
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we proceed with this approach, might be worth mentioning in the docs that there's no need to explicitly set position on your overlay content container, as we manage that ourselves.

Copy link
Contributor

Choose a reason for hiding this comment

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

yes definitely worth mentioning

@@ -263,7 +263,6 @@
.docs-overlay-example-transition {
$overlay-example-width: $pt-grid-size * 40;

position: absolute;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

No longer necessary now that we manage the position mode internally (via .pt-overlay-content).


.pt-overlay-backdrop {
// fixed position so it won't scroll under the overlay
position: fixed;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Deleted this rule (and therefore this .pt-overlay-backdrop block), because the backdrop is set to position: fixed by default now.

@@ -72,19 +72,14 @@ Styleguide components.overlay.scroll

$overlay-background-color: rgba($black, 0.7);

// scroll container for non-inline overlay
.pt-overlay-scroll-container.pt-overlay-open {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved this down to the bottom of the file because it's a special case.

@blueprint-bot
Copy link

Merge branch 'master' into cl/overlay-fixed

Preview: docs
Coverage: core | datetime

Copy link
Contributor

@giladgray giladgray left a comment

Choose a reason for hiding this comment

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

mostly comments on comments 😄

}

// fixed position so it won't scroll underneath the overlay content
Copy link
Contributor

Choose a reason for hiding this comment

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

not true... fixed position so it always covers the entire viewport (irrespective of page scroll position)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was a relic from line 81 in the old code. Will remove. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


// scroll container for non-inline overlay. it needs an explicit z-index to
// ensure that the backdrop renders on top of any navbars.
.pt-overlay-scroll-container.pt-overlay-open {
Copy link
Contributor

Choose a reason for hiding this comment

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

i wonder if we still need this class? but that's another issue, about scrolling dialogs etc

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Possibly not, can look into that more after this PR if that sounds good.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done (no change for now)

Copy link
Contributor

Choose a reason for hiding this comment

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

yes not in this PR

@@ -159,6 +159,19 @@ export class Overlay extends React.Component<IOverlayProps, IOverlayState> {

const { children, className, inline, isOpen, transitionDuration, transitionName } = this.props;

// add a special class to each child that will automatically set the appropriate
// CSS position mode under the hood. we add this class *after* the existing classes
// so that our class will override the position style of other classes that might
Copy link
Contributor

Choose a reason for hiding this comment

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

this is not how CSS works, which you call out later... it's entirely about specificity. if the user sets position in a selector with three classes it'll win out over ours every time.

i think you could safely remove all but the first sentence from this comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah this comment felt a little ham-handed. Will edit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

// so that our class will override the position style of other classes that might
// have set unnecessary position rules (note that CSS specificity plays a role here;
// our class will only override an existing position rule if it has the same or
// greater selector specificity).
Copy link
Contributor

Choose a reason for hiding this comment

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

yes definitely worth mentioning

// our class will only override an existing position rule if it has the same or
// greater selector specificity).
const decoratedChildren = React.Children.map(children, (child: React.ReactChild) => {
const childEl = child as React.ReactElement<any>;
Copy link
Contributor

Choose a reason for hiding this comment

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

can you do this cast in the function signature? (child: React.ReactElement<any>) => {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure thing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

assert.isFalse(isBodyScrollingDisabled());
});

function mountOverlay(inline?: boolean, hasBackdrop?: boolean) {
Copy link
Contributor

Choose a reason for hiding this comment

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

no need for ? on either param, they're always specified

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh does TS not freak when I pass undefined if a boolean is expected?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Contributor

Choose a reason for hiding this comment

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

mm probably would. nvm then... inline?: boolean === inline: boolean | undefined

.pt-overlay-content {
position: absolute;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

this file looks great now! excellent refactor and comments.

Copy link
Contributor

Choose a reason for hiding this comment

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

though it should end with a newline...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, I thought my editor handled that. Will add.

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah that's why i asked about EditorConfig 😄

@@ -72,19 +72,14 @@ Styleguide components.overlay.scroll

Copy link
Contributor

@giladgray giladgray Dec 1, 2016

Choose a reason for hiding this comment

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

the words "absolute positioning" are used a few times in these docs, please update accordingly.

see line 59 above

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@cmslewis
Copy link
Contributor Author

cmslewis commented Dec 1, 2016

@giladgray is this too verbose, or does this look okay?

image

@blueprint-bot
Copy link

Fix stuff per PR comments

Preview: docs | table
Coverage: core | datetime

@giladgray
Copy link
Contributor

  1. yes too verbose. say it chooses fixed or absolute based on inline.
  2. make it pt-intent-primary

@cmslewis
Copy link
Contributor Author

cmslewis commented Dec 1, 2016

Eh?

image

@giladgray
Copy link
Contributor

remove the first sentence? and clarify that it's absolute when inline to respect document flow and fixed otherwise to cover the entire viewport.

@blueprint-bot
Copy link

Reword the callout text

Preview: docs | table
Coverage: core | datetime

@cmslewis
Copy link
Contributor Author

cmslewis commented Dec 2, 2016

@giladgray v3

image

@blueprint-bot
Copy link

Reword the callout text v3

Preview: docs | table
Coverage: core | datetime

@adidahiya
Copy link
Contributor

Unrelated: it would be nice to make the inline rendering examples look nicer in a separate PR.

image
image

@blueprint-bot
Copy link

spaces after colons

Preview: docs | table
Coverage: core | datetime

@cmslewis cmslewis merged commit 7a827bc into master Dec 2, 2016
@cmslewis cmslewis deleted the cl/overlay-fixed branch December 2, 2016 19:01
greglo pushed a commit to greglo/blueprint that referenced this pull request Dec 12, 2016
@leebyp leebyp mentioned this pull request Jan 18, 2017
1 task
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.

None yet

5 participants