mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
feat(panels): render each panel's state classes from the store
A panel's collapsed, dock-pinned, dock-pinned-top, layout-auto-collapsed
and accordion-collapsed classes are drawn by its root's v-panel-state from
the gev-panel-chrome store. PanelChrome, its restore, the layout
controller, both rails and the side accordion present each panel's state
there instead of writing its classes; the rails call onCollapse(panel,
{collapsed, auto}). The directive applies a change in a synchronous
watcher, since the rails measure a panel in the task that opens or closes
it.
Destroying the chrome inside Cockpit now puts back the panels Cockpit's
entry closed while it can still present them. Without a shell, the voice
tools' set_panel_open reports that there is no panel to open. QA scripts
present the states they set instead of writing the classes.
This commit is contained in:
1 parent
dd3a2c87f9
commit
95523e09cd
37 files changed
+404
-135
No files matched your search
@@ -4,9 +4,11 @@
|
||||
import '../testSupport/registerDom.mjs';
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { createApp, h, nextTick } from 'vue';
|
||||
import { createApp, h, nextTick, withDirectives } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
import { settleSideAccordion } from './panelAccordion.js';
|
||||
import { PanelChrome } from './panelChrome.js';
|
||||
import { followPanelState, vPanelState } from './rail/panelState.js';
|
||||
import { bindPanelDisclosure } from './panelDisclosure.js';
|
||||
import PanelCollapseButton from './rail/PanelCollapseButton.vue';
|
||||
import { usePanelChromeStore } from './stores/panelChrome.js';
|
||||
@@ -30,6 +32,8 @@ test('the store republishes a panel only when its state changes', () => {
|
||||
assert.deepEqual(store.panels['data-panel'], {
|
||||
collapsed: true,
|
||||
pinned: false,
|
||||
pinnedTop: false,
|
||||
auto: null,
|
||||
});
|
||||
store.publish(token, 'data-panel', { collapsed: true });
|
||||
assert.ok(store.panels === first, 'an unchanged state is not republished');
|
||||
@@ -175,6 +179,8 @@ test('PanelChrome takes clicks only once initialized, pins only dock trays, and
|
||||
<div id="control-panel" class="collapsed"></div>`;
|
||||
const store = usePanelChromeStore(createPinia());
|
||||
const chrome = panelChrome(store);
|
||||
for (const id of ['data-panel', 'cctv-panel', 'control-panel'])
|
||||
followPanelState(store, id, document.getElementById(id));
|
||||
const calls = [];
|
||||
const setPanelCollapsed = chrome.setPanelCollapsed.bind(chrome);
|
||||
chrome.setPanelCollapsed = (id, collapsed, options) => {
|
||||
@@ -196,6 +202,8 @@ test('PanelChrome takes clicks only once initialized, pins only dock trays, and
|
||||
assert.deepEqual(store.panels['control-panel'], {
|
||||
collapsed: false,
|
||||
pinned: true,
|
||||
pinnedTop: true,
|
||||
auto: null,
|
||||
});
|
||||
assert.equal(
|
||||
document.getElementById('control-panel').classList.contains('dock-pinned'),
|
||||
@@ -206,8 +214,12 @@ test('PanelChrome takes clicks only once initialized, pins only dock trays, and
|
||||
const before = calls.length;
|
||||
store.act('toggle', 'data-panel');
|
||||
assert.equal(calls.length, before, 'a destroyed owner gets no clicks');
|
||||
chrome._syncPanelCollapseButton(document.getElementById('cctv-panel'));
|
||||
chrome._presentPanel('cctv-panel', { collapsed: true });
|
||||
assert.equal(store.panels['cctv-panel'], undefined, 'nor publishes');
|
||||
assert.equal(
|
||||
document.getElementById('cctv-panel').classList.contains('collapsed'),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test('a remounted chrome starts from its own states, not a pin left behind', () => {
|
||||
@@ -221,10 +233,12 @@ test('a remounted chrome starts from its own states, not a pin left behind', ()
|
||||
document.body.innerHTML = '<div id="location-bar" class="collapsed"></div>';
|
||||
const second = panelChrome(store);
|
||||
assert.equal(store.panels['location-bar'], undefined);
|
||||
second._syncPanelCollapseButton(document.getElementById('location-bar'));
|
||||
second._presentPanel('location-bar');
|
||||
assert.deepEqual(store.panels['location-bar'], {
|
||||
collapsed: true,
|
||||
pinned: false,
|
||||
pinnedTop: false,
|
||||
auto: null,
|
||||
});
|
||||
second.destroy();
|
||||
});
|
||||
@@ -238,3 +252,80 @@ test('bindPanelDisclosure refuses the retired button wiring', () => {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('a panel root draws its state at once, keeping classes others own', async (t) => {
|
||||
const store = mount(t, {
|
||||
render: () =>
|
||||
withDirectives(
|
||||
h('div', { id: 'scene-panel', class: 'panel-collapsible collapsed' }),
|
||||
[[vPanelState, 'scene-panel']],
|
||||
),
|
||||
});
|
||||
const panel = document.getElementById('scene-panel');
|
||||
panel.classList.add('running');
|
||||
assert.equal(panel.classList.contains('collapsed'), true, 'markup stands');
|
||||
const token = store.connect({});
|
||||
store.publish(token, 'scene-panel', { collapsed: true, auto: 'accordion' });
|
||||
// No tick: a layout measures in the same task.
|
||||
assert.equal(panel.classList.contains('accordion-collapsed'), true);
|
||||
store.publish(token, 'scene-panel', { collapsed: false, auto: null });
|
||||
assert.equal(panel.classList.contains('collapsed'), false);
|
||||
assert.equal(panel.classList.contains('accordion-collapsed'), false);
|
||||
assert.equal(panel.classList.contains('running'), true);
|
||||
assert.equal(panel.classList.contains('panel-collapsible'), true);
|
||||
store.publish(token, 'scene-panel', { pinned: true, pinnedTop: true });
|
||||
assert.ok(panel.classList.contains('dock-pinned'));
|
||||
assert.ok(panel.classList.contains('dock-pinned-top'));
|
||||
});
|
||||
|
||||
test('destroying the chrome inside Cockpit puts back the panels its entry closed', () => {
|
||||
document.body.innerHTML = `
|
||||
<div id="data-panel"></div>
|
||||
<div id="cctv-panel" class="collapsed"></div>`;
|
||||
const store = usePanelChromeStore(createPinia());
|
||||
const chrome = panelChrome(store);
|
||||
const data = document.getElementById('data-panel');
|
||||
followPanelState(store, 'data-panel', data);
|
||||
chrome.enterCockpit();
|
||||
assert.equal(data.classList.contains('collapsed'), true);
|
||||
chrome.destroy();
|
||||
assert.equal(data.classList.contains('collapsed'), false);
|
||||
chrome.exitCockpit();
|
||||
assert.equal(data.classList.contains('collapsed'), false);
|
||||
});
|
||||
|
||||
test('the accordion closes peers through the real store, and opens them again', () => {
|
||||
document.body.innerHTML = `
|
||||
<div id="data-panel" data-panel-id="data-panel"></div>
|
||||
<div id="scene-panel" data-panel-id="scene-panel"></div>`;
|
||||
const store = usePanelChromeStore(createPinia());
|
||||
const chrome = panelChrome(store);
|
||||
const panels = ['data-panel', 'scene-panel'].map((id) => {
|
||||
const panel = document.getElementById(id);
|
||||
followPanelState(store, id, panel);
|
||||
return panel;
|
||||
});
|
||||
const onCollapse = (panel, patch) => chrome._presentPanel(panel, patch);
|
||||
settleSideAccordion({
|
||||
panels,
|
||||
enabled: true,
|
||||
preferredPanelId: 'scene-panel',
|
||||
documentRef: document,
|
||||
onCollapse,
|
||||
});
|
||||
assert.deepEqual(store.panels['data-panel'], {
|
||||
collapsed: true,
|
||||
pinned: false,
|
||||
pinnedTop: false,
|
||||
auto: 'accordion',
|
||||
});
|
||||
assert.equal(panels[0].className, 'collapsed accordion-collapsed');
|
||||
assert.equal(
|
||||
store.panels['scene-panel'],
|
||||
undefined,
|
||||
'the owner is untouched',
|
||||
);
|
||||
settleSideAccordion({ panels, enabled: false, onCollapse });
|
||||
assert.equal(panels[0].className, '');
|
||||
chrome.destroy();
|
||||
});
|
||||
Reference in new issue
Block a user