mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-19 17:22:48 -05:00
Rewrite scrollablesplitview (#11566)
* fix issues with scrollable, maybe * add debounce * remove scrollable * fix events * perf improvements * fix compile errors * fix more compile * add tests * maybe fix tests * 💄 * 💄 * maybe this will work * fix compile * try this * remove some unneeded functionality * fix comment
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IView, ScrollableView } from 'sql/base/browser/ui/scrollableView/scrollableView';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import * as assert from 'assert';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
|
||||
class TestView extends Disposable implements IView {
|
||||
|
||||
constructor(public minimumSize: number, public maximumSize: number) { super(); }
|
||||
|
||||
private readonly _onDidLayout = this._register(new Emitter<{ height: number, width: number }>());
|
||||
public readonly onDidLayout = this._onDidLayout.event;
|
||||
layout(height: number, width: number): void {
|
||||
this._size = height;
|
||||
this._onDidLayout.fire({ height, width });
|
||||
}
|
||||
|
||||
private _size: number;
|
||||
public get size(): number {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
public readonly onDidChangeEmitter = this._register(new Emitter<number>());
|
||||
public readonly onDidChange = this.onDidChangeEmitter.event;
|
||||
|
||||
public readonly element = document.createElement('div');
|
||||
|
||||
private readonly _onDidInsertEmitter = this._register(new Emitter<void>());
|
||||
public readonly onDidInsertEvent = this._onDidInsertEmitter.event;
|
||||
onDidInsert?(): void {
|
||||
this._onDidInsertEmitter.fire();
|
||||
}
|
||||
|
||||
private readonly _onDidRemoveEmitter = this._register(new Emitter<void>());
|
||||
public readonly onDidRemoveEvent = this._onDidRemoveEmitter.event;
|
||||
onDidRemove?(): void {
|
||||
this._onDidRemoveEmitter.fire();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
suite('ScrollableView', () => {
|
||||
let container: HTMLElement;
|
||||
|
||||
setup(() => {
|
||||
container = document.createElement('div');
|
||||
container.style.position = 'absolute';
|
||||
container.style.width = `${200}px`;
|
||||
container.style.height = `${200}px`;
|
||||
});
|
||||
|
||||
test('creates empty view', () => {
|
||||
const scrollableView = new ScrollableView(container);
|
||||
scrollableView.layout(200, 200);
|
||||
assert.equal(container.firstElementChild!.firstElementChild!.firstElementChild!.childElementCount, 0, 'view should be empty');
|
||||
scrollableView.dispose();
|
||||
});
|
||||
|
||||
test('adds and removes views correctly', async () => {
|
||||
const view1 = new TestView(20, 20);
|
||||
const view2 = new TestView(20, 20);
|
||||
const view3 = new TestView(20, 20);
|
||||
const scrollableView = new ScrollableView(container);
|
||||
|
||||
scrollableView.layout(200, 200);
|
||||
|
||||
scrollableView.addView(view1);
|
||||
scrollableView.addView(view2);
|
||||
scrollableView.addView(view3);
|
||||
|
||||
// we only update the scroll dimensions asynchronously
|
||||
await waitForAnimation();
|
||||
|
||||
let viewQuery = getViewChildren(container);
|
||||
assert.equal(viewQuery.length, 3, 'split view should have 3 views');
|
||||
|
||||
scrollableView.clear();
|
||||
|
||||
viewQuery = getViewChildren(container);
|
||||
assert.equal(viewQuery.length, 0, 'split view should have no views');
|
||||
|
||||
view1.dispose();
|
||||
view2.dispose();
|
||||
view3.dispose();
|
||||
scrollableView.dispose();
|
||||
});
|
||||
|
||||
test('shrinks views correctly', async () => {
|
||||
const view1 = new TestView(20, Number.POSITIVE_INFINITY);
|
||||
const view2 = new TestView(20, Number.POSITIVE_INFINITY);
|
||||
const view3 = new TestView(20, Number.POSITIVE_INFINITY);
|
||||
const scrollableView = new ScrollableView(container);
|
||||
|
||||
scrollableView.layout(200, 200);
|
||||
|
||||
scrollableView.addView(view1);
|
||||
|
||||
await waitForAnimation();
|
||||
|
||||
assert.equal(view1.size, 200, 'view1 is entire size');
|
||||
|
||||
scrollableView.addView(view2);
|
||||
|
||||
await waitForAnimation();
|
||||
|
||||
assert.equal(view1.size, 100, 'view1 is half size');
|
||||
assert.equal(view2.size, 100, 'view2 is half size');
|
||||
|
||||
scrollableView.addView(view3);
|
||||
|
||||
await waitForAnimation();
|
||||
|
||||
assert.equal(view1.size, 66, 'view1 is third size');
|
||||
assert.equal(view2.size, 67, 'view2 is third size');
|
||||
assert.equal(view3.size, 67, 'view3 is third size');
|
||||
});
|
||||
|
||||
test('honors minimum size', async () => {
|
||||
const view1 = new TestView(100, Number.POSITIVE_INFINITY);
|
||||
const view2 = new TestView(100, Number.POSITIVE_INFINITY);
|
||||
const view3 = new TestView(100, Number.POSITIVE_INFINITY);
|
||||
const scrollableView = new ScrollableView(container);
|
||||
|
||||
scrollableView.layout(200, 200);
|
||||
|
||||
scrollableView.addViews([view1, view2, view3]);
|
||||
|
||||
await waitForAnimation();
|
||||
|
||||
assert.equal(view1.size, 100, 'view1 is minimum size');
|
||||
assert.equal(view2.size, 100, 'view2 is minimum size');
|
||||
assert.equal(view3.size, undefined, 'view3 should not have been layout yet');
|
||||
});
|
||||
|
||||
test('reacts to changes in views', async () => {
|
||||
const view1 = new TestView(100, Number.POSITIVE_INFINITY);
|
||||
const view2 = new TestView(100, Number.POSITIVE_INFINITY);
|
||||
const view3 = new TestView(100, Number.POSITIVE_INFINITY);
|
||||
const scrollableView = new ScrollableView(container);
|
||||
|
||||
scrollableView.layout(200, 200);
|
||||
|
||||
scrollableView.addViews([view1, view2, view3]);
|
||||
|
||||
await waitForAnimation();
|
||||
|
||||
view1.minimumSize = 130;
|
||||
view1.onDidChangeEmitter.fire(0);
|
||||
|
||||
await waitForAnimation();
|
||||
|
||||
assert.equal(view1.size, 130, 'view1 should be 130');
|
||||
assert.equal(view2.size, 100, 'view2 should still be minimum size');
|
||||
assert.equal(view3.size, undefined, 'view3 should not have been layout yet');
|
||||
});
|
||||
|
||||
test('programmatically scrolls', async () => {
|
||||
const view1 = new TestView(100, Number.POSITIVE_INFINITY);
|
||||
const view2 = new TestView(100, Number.POSITIVE_INFINITY);
|
||||
const view3 = new TestView(100, Number.POSITIVE_INFINITY);
|
||||
const scrollableView = new ScrollableView(container);
|
||||
|
||||
scrollableView.layout(200, 200);
|
||||
|
||||
scrollableView.addViews([view1, view2, view3]);
|
||||
|
||||
await waitForAnimation();
|
||||
|
||||
assert.equal(view1.size, 100, 'view1 is minimum size');
|
||||
assert.equal(view2.size, 100, 'view2 is minimum size');
|
||||
assert.equal(view3.size, undefined, 'view3 should not have been layout yet');
|
||||
assert.equal(getViewChildren(container).length, 2, 'only 2 views are rendered');
|
||||
|
||||
scrollableView.setScrollTop(100);
|
||||
|
||||
await waitForAnimation();
|
||||
assert.equal(view2.size, 100, 'view2 is minimum size');
|
||||
assert.equal(view3.size, 100, 'view3 is minimum size');
|
||||
assert.equal(getViewChildren(container).length, 2, 'only 2 views are rendered');
|
||||
});
|
||||
});
|
||||
|
||||
function waitForAnimation(): Promise<void> {
|
||||
return timeout(200);
|
||||
}
|
||||
|
||||
function getViewChildren(container: HTMLElement): NodeListOf<Element> {
|
||||
return container.querySelectorAll('.scrollable-view .scrollable-view-container > .scrollable-view-child');
|
||||
}
|
||||
Reference in New Issue
Block a user