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

Add collapse option to StatsWidget #166

Merged
merged 3 commits into from
Oct 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 58 additions & 22 deletions modules/stats-widget/src/stats-widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import {formatMemory, formatTime} from './format-utils';
import {Stats, Stat} from '@probe.gl/stats';

const RIGHT_ARROW = '\u25b6';
const DOWN_ARROW = '\u2b07';

const DEFAULT_CSS = {
css: {
position: 'fixed',
Expand Down Expand Up @@ -42,26 +45,26 @@ export type StatWidgetProps = {
export default class StatsWidget {
stats: Stats;
title: string;
collapsed: boolean = false;
_framesPerUpdate: number;
_formatters;
_formatters = DEFAULT_FORMATTERS;
_resetOnUpdate = {};
_counter = 0;
_css;
_headerCSS;
_itemCSS;
_container = null;
_header = null;
_container: HTMLElement = null;
_innerContainer: HTMLElement = null;
_statsContainer: HTMLElement = null;
_header: HTMLElement = null;
_items = {};
_added: boolean = false;

constructor(stats: Stats, options?: StatWidgetProps) {
stats = stats;
this.title = options?.title || 'Stats';
this.stats = stats;
this.title = options?.title;

this._framesPerUpdate = Math.round(Math.max(options?.framesPerUpdate || 1, 1));
this._formatters = DEFAULT_FORMATTERS;
this._resetOnUpdate = {};

this._counter = 0;

this._initializeFormatters(options);
this._initializeUpdateConfigs(options);
Expand All @@ -74,20 +77,14 @@ export default class StatsWidget {
delete this._css.header;
delete this._css.item;

this._container = null;
this._header = null;
this._items = {};

this._createDOM(options?.container);
this._createDOMHeader();
this._createDOMStats();
}

setStats(stats: Stats): void {
this.stats = stats;
// @ts-ignore
this._createDOMStats(this._container);
this._setHeaderContent(stats.id);
this._createDOMStats();
this._setHeaderContent();

this.update();
}
Expand All @@ -108,6 +105,24 @@ export default class StatsWidget {
this._update();
}

/**
* Remove the stats widget from the container it was added to.
* The stats widget cannot be reused after this is called.
*/
remove(): void {
// if re-adding the stats widget is needed, a code path to
// re-add the _innerContainer should be added, e.g. in _update.
this._container.removeChild(this._innerContainer);
}

setCollapsed(collapsed: boolean): void {
this.collapsed = collapsed;
if (this._statsContainer) {
this._statsContainer.style.display = this.collapsed ? 'none' : 'block';
}
this._setHeaderContent();
}

_update(): void {
// make sure that we clear the old text before drawing new text.
this.stats.forEach(stat => {
Expand Down Expand Up @@ -157,6 +172,20 @@ export default class StatsWidget {
}
document.body.appendChild(this._container);
}

// When adding the widget to an existing element, make sure there
// is a container for this widget specifically, so that multiple widgets
// can be added to the same container.
this._innerContainer = document.createElement('div');
this._container.appendChild(this._innerContainer);

// Create the contents of the stats widget, starting with the header
this._createDOMHeader();

// Create an element for the stats themselves, so we can collapse it later
this._statsContainer = document.createElement('div');
this._statsContainer.style.display = 'block';
this._innerContainer.appendChild(this._statsContainer);
}

_createDOMHeader(): void {
Expand All @@ -166,18 +195,25 @@ export default class StatsWidget {
for (const name in this._headerCSS) {
this._header.style[name] = this._headerCSS[name];
}
this._container.appendChild(this._header);
this._header.onclick = this._toggleCollapsed.bind(this);
this._innerContainer.appendChild(this._header);
}

this._setHeaderContent();
}

_setHeaderContent(title?: string): void {
_setHeaderContent() {
if (this._header) {
this._header.innerText = title || this.title || (this.stats && this.stats.id) || '';
const collapsedState = this.collapsed ? RIGHT_ARROW : DOWN_ARROW;
const title = this.title || (this.stats && this.stats.id) || 'Stats';
this._header.innerText = `${collapsedState} ${title}`;
}
}

_toggleCollapsed() {
this.setCollapsed(!this.collapsed);
}

_createDOMStats(): void {
if (this.stats instanceof Stats) {
this.stats.forEach(stat => {
Expand All @@ -187,7 +223,7 @@ export default class StatsWidget {
}

_createDOMItem(statName: string): void {
if (!this._container) {
if (!this._statsContainer) {
return;
}

Expand All @@ -199,7 +235,7 @@ export default class StatsWidget {
for (const name in this._itemCSS) {
this._items[statName].style[name] = this._itemCSS[name];
}
this._container.appendChild(this._items[statName]);
this._statsContainer.appendChild(this._items[statName]);
}

_getLines(stat: Stat): string[] {
Expand Down
58 changes: 53 additions & 5 deletions modules/stats-widget/test/stats-widget.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,16 @@ test('StatsWidget#Constructor with no stats or options', t => {
t.ok(statsWidget._header, 'Should create a dom header.');
t.equals(statsWidget._container.childNodes.length, 1, 'Should have one child node.');
t.ok(
statsWidget._container.childNodes[0] === statsWidget._header,
'Should append header to container as the first child'
statsWidget._container.childNodes[0] === statsWidget._innerContainer,
'Should append inner container to container as the first child'
);
t.ok(
statsWidget._innerContainer.childNodes[0] === statsWidget._header,
'Should append header to inner container as the first child'
);
t.ok(
statsWidget._innerContainer.childNodes[1] === statsWidget._statsContainer,
'Should append stats container to inner container as the second child'
);
t.end();
});
Expand All @@ -65,12 +73,35 @@ test('StatsWidget#setStats', t => {
statsWidget.setStats(stats);

t.equals(Object.keys(statsWidget._items).length, 3, 'Should have 3 items.');
t.equals(statsWidget._container.childNodes.length, 4, 'Should have 4 child nodes.');
t.equals(statsWidget._container.childNodes.length, 1, 'Should have 2 child nodes.');
t.equals(statsWidget._innerContainer.childNodes.length, 2, 'Should have 2 child nodes.');
t.equals(statsWidget._statsContainer.childNodes.length, 3, 'Should have 3 child nodes.');
t.equals(statsWidget._counter, 1, 'Should call update() and increase _counter.');

t.end();
});

test('StatsWidget#collapse', t => {
const container = _global.document.createElement('div');
container.id = 'test-stats-widget-container';
const statsWidget = new StatsWidget(getStatsObject(), {container});

t.ok(!statsWidget.collapsed, 'Starts uncollapsed');
t.equals(statsWidget._statsContainer.style.display, 'block', 'Starts in block display');

statsWidget.setCollapsed(true);

t.ok(statsWidget.collapsed, 'Collapses');
t.equals(statsWidget._statsContainer.style.display, 'none', 'Collapses to none display');

statsWidget.setCollapsed(false);

t.ok(!statsWidget.collapsed, 'Uncollapses');
t.equals(statsWidget._statsContainer.style.display, 'block', 'Uncollapses to block display');

t.end();
});

/* eslint-disable */
test('StatsWidget#Update stats', t => {
const container = _global.document.createElement('div');
Expand All @@ -86,7 +117,9 @@ test('StatsWidget#Update stats', t => {
// @ts-ignore
t.equals(Object.keys(statsWidget._items).length, 3, 'Should have 3 items.');
// @ts-ignore
t.equals(statsWidget._container.childNodes.length, 4, 'Should have 4 child nodes.');
t.equals(statsWidget._container.childNodes.length, 1, 'Should have 1 child nodes.');
t.equals(statsWidget._innerContainer.childNodes.length, 2, 'Should have 2 child nodes.');
t.equals(statsWidget._statsContainer.childNodes.length, 3, 'Should have 3 child nodes.');

// @ts-ignore
t.equals(statsWidget._items.Count.innerHTML, 'Count: 1', 'Should correctly update count stats.');
Expand Down Expand Up @@ -134,7 +167,11 @@ test('StatsWidget#formatters', t => {

statsWidget.setStats(new Stats({id: 'test-stats-2'}));
// @ts-ignore
t.equals(statsWidget._header.innerText, 'test-stats-2', "Should use the new stats' header.");
t.equals(
statsWidget._header.innerText,
'\u2b07 test-stats-2',
"Should use the new stats' header."
);

t.end();
});
Expand All @@ -160,3 +197,14 @@ test('StatsWidget#resetOnUpdate', t => {

t.end();
});

test('StatsWidget#remove', t => {
const container = _global.document.createElement('div');
container.id = 'test-stats-widget-container';
const statsWidget = new StatsWidget(null, {container});
t.ok(statsWidget._container === container);
t.equals(statsWidget._container.childNodes.length, 1, 'Should have 1 child node.');
statsWidget.remove();
t.equals(statsWidget._container.childNodes.length, 0, 'Should have 0 child nodes.');
t.end();
});
6 changes: 3 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3828,9 +3828,9 @@ camelcase@^5.0.0, camelcase@^5.2.0, camelcase@^5.3.1:
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==

caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001261:
version "1.0.30001264"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001264.tgz"
integrity sha512-Ftfqqfcs/ePiUmyaySsQ4PUsdcYyXG2rfoBVsk3iY1ahHaJEw65vfb7Suzqm+cEkwwPIv/XWkg27iCpRavH4zA==
version "1.0.30001265"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz"
integrity sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==

caseless@~0.11.0:
version "0.11.0"
Expand Down