-
Notifications
You must be signed in to change notification settings - Fork 6.8k
feat(overlay): add scroll blocking strategy #4500
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
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
import {browser, Key, element, by} from 'protractor'; | ||
import {screenshot} from '../../screenshot'; | ||
import {getScrollPosition} from '../../util/query'; | ||
|
||
|
||
describe('scroll blocking', () => { | ||
beforeEach(() => browser.get('/block-scroll-strategy')); | ||
afterEach(() => clickOn('disable')); | ||
|
||
it('should not be able to scroll programmatically along the x axis', async (done) => { | ||
scrollPage(0, 100); | ||
expect((await getScrollPosition()).y).toBe(100, 'Expected the page to be scrollable.'); | ||
|
||
clickOn('enable'); | ||
scrollPage(0, 200); | ||
expect((await getScrollPosition()).y).toBe(100, 'Expected the page not to be scrollable.'); | ||
|
||
clickOn('disable'); | ||
scrollPage(0, 300); | ||
expect((await getScrollPosition()).y).toBe(300, 'Exected page to be scrollable again.'); | ||
|
||
screenshot(); | ||
done(); | ||
}); | ||
|
||
it('should not be able to scroll programmatically along the y axis', async (done) => { | ||
scrollPage(100, 0); | ||
expect((await getScrollPosition()).x).toBe(100, 'Expected the page to be scrollable.'); | ||
|
||
clickOn('enable'); | ||
scrollPage(200, 0); | ||
expect((await getScrollPosition()).x).toBe(100, 'Expected the page not to be scrollable.'); | ||
|
||
clickOn('disable'); | ||
scrollPage(300, 0); | ||
expect((await getScrollPosition()).x).toBe(300, 'Exected page to be scrollable again.'); | ||
|
||
screenshot(); | ||
done(); | ||
}); | ||
|
||
it('should not be able to scroll via the keyboard along the y axis', async (done) => { | ||
const body = element(by.tagName('body')); | ||
|
||
scrollPage(0, 100); | ||
expect((await getScrollPosition()).y).toBe(100, 'Expected the page to be scrollable.'); | ||
|
||
clickOn('enable'); | ||
await body.sendKeys(Key.ARROW_DOWN); | ||
await body.sendKeys(Key.ARROW_DOWN); | ||
await body.sendKeys(Key.ARROW_DOWN); | ||
expect((await getScrollPosition()).y).toBe(100, 'Expected the page not to be scrollable.'); | ||
|
||
clickOn('disable'); | ||
await body.sendKeys(Key.ARROW_DOWN); | ||
await body.sendKeys(Key.ARROW_DOWN); | ||
await body.sendKeys(Key.ARROW_DOWN); | ||
expect((await getScrollPosition()).y) | ||
.toBeGreaterThan(100, 'Expected the page to be scrollable again.'); | ||
|
||
screenshot(); | ||
done(); | ||
}); | ||
|
||
it('should not be able to scroll via the keyboard along the x axis', async (done) => { | ||
const body = element(by.tagName('body')); | ||
|
||
scrollPage(100, 0); | ||
expect((await getScrollPosition()).x).toBe(100, 'Expected the page to be scrollable.'); | ||
|
||
clickOn('enable'); | ||
await body.sendKeys(Key.ARROW_RIGHT); | ||
await body.sendKeys(Key.ARROW_RIGHT); | ||
await body.sendKeys(Key.ARROW_RIGHT); | ||
expect((await getScrollPosition()).x).toBe(100, 'Expected the page not to be scrollable.'); | ||
|
||
clickOn('disable'); | ||
await body.sendKeys(Key.ARROW_RIGHT); | ||
await body.sendKeys(Key.ARROW_RIGHT); | ||
await body.sendKeys(Key.ARROW_RIGHT); | ||
expect((await getScrollPosition()).x) | ||
.toBeGreaterThan(100, 'Expected the page to be scrollable again.'); | ||
|
||
screenshot(); | ||
done(); | ||
}); | ||
|
||
it('should not be able to scroll the page after reaching the end of an element along the y axis', | ||
async (done) => { | ||
const scroller = element(by.id('scroller')); | ||
|
||
browser.executeScript(`document.getElementById('scroller').scrollTop = 200;`); | ||
scrollPage(0, 100); | ||
expect((await getScrollPosition()).y).toBe(100, 'Expected the page to be scrollable.'); | ||
|
||
clickOn('enable'); | ||
scroller.sendKeys(Key.ARROW_DOWN); | ||
scroller.sendKeys(Key.ARROW_DOWN); | ||
scroller.sendKeys(Key.ARROW_DOWN); | ||
expect((await getScrollPosition()).y).toBe(100, 'Expected the page not to have scrolled.'); | ||
|
||
screenshot(); | ||
done(); | ||
}); | ||
|
||
it('should not be able to scroll the page after reaching the end of an element along the x axis', | ||
async (done) => { | ||
const scroller = element(by.id('scroller')); | ||
|
||
browser.executeScript(`document.getElementById('scroller').scrollLeft = 200;`); | ||
scrollPage(100, 0); | ||
expect((await getScrollPosition()).x).toBe(100, 'Expected the page to be scrollable.'); | ||
|
||
clickOn('enable'); | ||
scroller.sendKeys(Key.ARROW_RIGHT); | ||
scroller.sendKeys(Key.ARROW_RIGHT); | ||
scroller.sendKeys(Key.ARROW_RIGHT); | ||
expect((await getScrollPosition()).x).toBe(100, 'Expected the page not to have scrolled.'); | ||
|
||
screenshot(); | ||
done(); | ||
}); | ||
}); | ||
|
||
// Clicks on a button programmatically. Note that we can't use Protractor's `.click`, because | ||
// it performs a real click, which will scroll the button into view. | ||
function clickOn(id: string) { | ||
browser.executeScript(`document.getElementById('${id}').click()`); | ||
} | ||
|
||
// Scrolls the page to the specified coordinates. | ||
function scrollPage(x: number, y: number) { | ||
return browser.executeScript(`window.scrollTo(${x}, ${y});`); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
.spacer { | ||
background: #3f51b5; | ||
margin-bottom: 10px; | ||
} | ||
|
||
.spacer.vertical { | ||
width: 100px; | ||
height: 3000px; | ||
} | ||
|
||
.spacer.horizontal { | ||
width: 3000px; | ||
height: 100px; | ||
} | ||
|
||
.scroller { | ||
width: 100px; | ||
height: 100px; | ||
overflow: auto; | ||
position: absolute; | ||
top: 100px; | ||
left: 200px; | ||
} | ||
|
||
.scroller-spacer { | ||
width: 200px; | ||
height: 200px; | ||
background: #ff4081; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<p> | ||
<button id="enable" (click)="scrollStrategy.enable()">Enable scroll blocking</button> | ||
<button id="disable" (click)="scrollStrategy.disable()">Disable scroll blocking</button> | ||
</p> | ||
<div class="spacer vertical"></div> | ||
<!-- this one needs a tabindex so protractor can trigger key presses inside it --> | ||
<div class="scroller" id="scroller" tabindex="-1"> | ||
<div class="scroller-spacer"></div> | ||
</div> | ||
<div class="spacer horizontal"></div> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import {Component} from '@angular/core'; | ||
import {BlockScrollStrategy, ViewportRuler} from '@angular/material'; | ||
|
||
@Component({ | ||
moduleId: module.id, | ||
selector: 'block-scroll-strategy-e2e', | ||
templateUrl: 'block-scroll-strategy-e2e.html', | ||
styleUrls: ['block-scroll-strategy-e2e.css'], | ||
}) | ||
export class BlockScrollStrategyE2E { | ||
constructor(private _viewportRuler: ViewportRuler) { } | ||
scrollStrategy = new BlockScrollStrategy(this._viewportRuler); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,4 +41,17 @@ | |
.mat-theme-loaded-marker { | ||
display: none; | ||
} | ||
|
||
// Used when disabling global scrolling. | ||
.cdk-global-scrollblock { | ||
position: fixed; | ||
|
||
// Necessary for iOS not to expand past the viewport. | ||
max-width: 100vw; | ||
|
||
// Note: this will always add a scrollbar to whatever element it is on, which can | ||
// potentially result in double scrollbars. It shouldn't be an issue, because we won't | ||
// block scrolling on a page that doesn't have a scrollbar in the first place. | ||
overflow-y: scroll; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Won't adding There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It will, but we don't activate the strategy unless there was a scrollbar in the first place. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should add a comment mentioning that |
||
} | ||
} |
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 should go into the
cdk-overlay
mixin