feat: add FLYOUT_MENU_MAP data map and _buildFlyoutMenuItems builder
This commit is contained in:
@@ -139,6 +139,82 @@ let _flyoutSessionKey = null;
|
|||||||
let _flyoutSessionName = null;
|
let _flyoutSessionName = null;
|
||||||
let _flyoutRemoteId = null;
|
let _flyoutRemoteId = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data map of menu item definitions keyed by view type.
|
||||||
|
* Each entry is an array of item config objects with:
|
||||||
|
* { label, action, className?, separator? }
|
||||||
|
* The 'user' view type gets the active view name injected at render time.
|
||||||
|
*/
|
||||||
|
var FLYOUT_MENU_MAP = {
|
||||||
|
'all': [
|
||||||
|
{ label: 'Add to View\u2026', action: 'add-to-view', className: 'flyout-menu__item--has-submenu' },
|
||||||
|
{ label: 'Hide', action: 'hide' },
|
||||||
|
{ separator: true },
|
||||||
|
{ label: 'Kill Session', action: 'kill', className: 'flyout-menu__item--danger' },
|
||||||
|
],
|
||||||
|
'user': [
|
||||||
|
{ label: 'Add to View\u2026', action: 'add-to-view', className: 'flyout-menu__item--has-submenu' },
|
||||||
|
{ label: 'Remove from {viewName}', action: 'remove-from-view' },
|
||||||
|
{ label: 'Hide', action: 'hide' },
|
||||||
|
{ separator: true },
|
||||||
|
{ label: 'Kill Session', action: 'kill', className: 'flyout-menu__item--danger' },
|
||||||
|
],
|
||||||
|
'hidden': [
|
||||||
|
{ label: 'Unhide', action: 'unhide' },
|
||||||
|
{ label: 'Unhide & Add to View\u2026', action: 'unhide-add-to-view', className: 'flyout-menu__item--has-submenu' },
|
||||||
|
{ separator: true },
|
||||||
|
{ label: 'Kill Session', action: 'kill', className: 'flyout-menu__item--danger' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the flyout menu HTML string based on the active view type.
|
||||||
|
* Uses FLYOUT_MENU_MAP to generate items — no if/else chains.
|
||||||
|
* @returns {string} HTML for the menu items
|
||||||
|
*/
|
||||||
|
function _buildFlyoutMenuItems() {
|
||||||
|
// Determine view type: 'all', 'hidden', or 'user'
|
||||||
|
var viewType = _activeView;
|
||||||
|
if (viewType !== 'all' && viewType !== 'hidden') {
|
||||||
|
viewType = 'user';
|
||||||
|
}
|
||||||
|
|
||||||
|
var items = FLYOUT_MENU_MAP[viewType] || FLYOUT_MENU_MAP['all'];
|
||||||
|
var html = '';
|
||||||
|
|
||||||
|
for (var i = 0; i < items.length; i++) {
|
||||||
|
var item = items[i];
|
||||||
|
if (item.separator) {
|
||||||
|
html += '<div class="flyout-menu__separator" role="separator"></div>';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var label = item.label;
|
||||||
|
// Inject view name for "Remove from {viewName}"
|
||||||
|
if (label.indexOf('{viewName}') !== -1) {
|
||||||
|
var displayName = _activeView;
|
||||||
|
if (displayName.length > 20) {
|
||||||
|
displayName = displayName.substring(0, 20) + '\u2026';
|
||||||
|
}
|
||||||
|
label = label.replace('{viewName}', escapeHtml(displayName));
|
||||||
|
}
|
||||||
|
|
||||||
|
var cls = 'flyout-menu__item';
|
||||||
|
if (item.className) cls += ' ' + item.className;
|
||||||
|
|
||||||
|
var titleAttr = '';
|
||||||
|
if (item.action === 'remove-from-view' && _activeView && _activeView.length > 20) {
|
||||||
|
titleAttr = ' title="Remove from ' + escapeHtml(_activeView) + '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '<button class="' + cls + '" role="menuitem" data-action="' + item.action + '"' + titleAttr + '>';
|
||||||
|
html += label;
|
||||||
|
html += '</button>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Settings state ───────────────────────────────────────────────────────────
|
// ─── Settings state ───────────────────────────────────────────────────────────
|
||||||
let _settingsOpen = false;
|
let _settingsOpen = false;
|
||||||
let _serverSettings = null;
|
let _serverSettings = null;
|
||||||
|
|||||||
@@ -3748,3 +3748,43 @@ def test_old_tile_delete_handler_removed() -> None:
|
|||||||
assert "tile-delete" not in bind_body, (
|
assert "tile-delete" not in bind_body, (
|
||||||
"The old .tile-delete delegated handler must be removed from bindStaticEventListeners"
|
"The old .tile-delete delegated handler must be removed from bindStaticEventListeners"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ─── Task 4: Context-dependent menu items — FLYOUT_MENU_MAP ──────────────────
|
||||||
|
|
||||||
|
|
||||||
|
def test_flyout_menu_map_exists() -> None:
|
||||||
|
"""app.js must define a FLYOUT_MENU_MAP data structure."""
|
||||||
|
assert "FLYOUT_MENU_MAP" in _JS, (
|
||||||
|
"app.js must contain a FLYOUT_MENU_MAP data structure"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_flyout_menu_map_has_three_view_types() -> None:
|
||||||
|
"""FLYOUT_MENU_MAP must have keys for 'all', 'user', and 'hidden'."""
|
||||||
|
# The map should reference all three view types
|
||||||
|
map_section = _JS.split("FLYOUT_MENU_MAP")[1].split("};")[0]
|
||||||
|
assert "'all'" in map_section or '"all"' in map_section, (
|
||||||
|
"FLYOUT_MENU_MAP must include an 'all' key"
|
||||||
|
)
|
||||||
|
assert "'user'" in map_section or '"user"' in map_section, (
|
||||||
|
"FLYOUT_MENU_MAP must include a 'user' key"
|
||||||
|
)
|
||||||
|
assert "'hidden'" in map_section or '"hidden"' in map_section, (
|
||||||
|
"FLYOUT_MENU_MAP must include a 'hidden' key"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_flyout_menu_items_function_exists() -> None:
|
||||||
|
"""app.js must define a _buildFlyoutMenuItems function."""
|
||||||
|
assert "function _buildFlyoutMenuItems" in _JS, (
|
||||||
|
"app.js must contain a _buildFlyoutMenuItems function"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_flyout_uses_menu_map() -> None:
|
||||||
|
"""_buildFlyoutMenuItems must reference FLYOUT_MENU_MAP."""
|
||||||
|
fn_body = _JS.split("function _buildFlyoutMenuItems")[1].split("\nfunction ")[0]
|
||||||
|
assert "FLYOUT_MENU_MAP" in fn_body, (
|
||||||
|
"_buildFlyoutMenuItems must reference FLYOUT_MENU_MAP (data-driven, not if/else)"
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user