fix(map): canonicalize facet visibility and compact header
This commit is contained in:
@@ -61,6 +61,8 @@ test("Map settings use a canonical trailing push panel and ApplicationPanel head
|
||||
assert.doesNotMatch(preview, /settingsPanelPinned|onPointerDownCapture/);
|
||||
assert.match(catalog, /endPanelOpen=\{mapSettingsPanelOpen\}/);
|
||||
assert.match(catalog, /headerTools=\{activePageTemplate\.id === "map"[\s\S]*?setMapHeaderActionsHost/);
|
||||
assert.match(catalog, /className="catalog-application-header-tools"[\s\S]*?label="Режим модуля"[\s\S]*?setMapHeaderActionsHost/);
|
||||
assert.doesNotMatch(catalog, /className="catalog-module-mode"/);
|
||||
assert.match(styles, /data-content-expanded="true"\]\[data-end-panel-open="true"\][\s\S]*?right: calc\(/);
|
||||
assert.match(styles, /@keyframes nodedc-application-side-panel-in[\s\S]*?translateX\(1\.6rem\)/);
|
||||
assert.match(catalogStyles, /\.catalog-map-header-action\.nodedc-icon-button[\s\S]*?--nodedc-panel-action-bg/);
|
||||
|
||||
@@ -29,6 +29,17 @@ test("hidden and visible layers have an explicit visual state", async () => {
|
||||
assert.match(styles, /\.catalog-map-fixture__objects-menu-item:not\(\[data-visible\]\)/);
|
||||
});
|
||||
|
||||
test("facet controls and renderer consume the same canonical enabled-value state", async () => {
|
||||
const preview = await readFile(new URL("../apps/catalog/src/MapFixturePreview.tsx", import.meta.url), "utf8");
|
||||
|
||||
assert.match(preview, /initialSubjectState\(initialLayout\?\.dataProductBindings[\s\S]*?presentationProfiles\)/);
|
||||
assert.match(preview, /facets: profile \? normalizeMapPresentationFacetSelections\(state\.filters, profile\) : state\.filters/);
|
||||
assert.match(preview, /filters: normalizeMapPresentationFacetSelections\(state\.filters, summary\.profile\)/);
|
||||
assert.match(preview, /const active = mapPresentationFacetValueIsEnabled\(state\.filters, facet\.field, item\.value\)/);
|
||||
assert.match(preview, /toggleMapPresentationFacetSelection\(filters, field, value, availableValues\)/);
|
||||
assert.match(preview, /facet\.values\.map\(\(value\) => value\.value\)/);
|
||||
});
|
||||
|
||||
test("joined detail aspects do not become independent map layers", async () => {
|
||||
const preview = await readFile(new URL("../apps/catalog/src/MapFixturePreview.tsx", import.meta.url), "utf8");
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ const {
|
||||
mapFactMatchesFilters,
|
||||
mapPresentationBindingIsAll,
|
||||
mapPresentationFacetCounts,
|
||||
mapPresentationFacetValueIsEnabled,
|
||||
normalizeMapPresentationFacetSelections,
|
||||
toggleMapPresentationFacetSelection,
|
||||
} = await import(moduleUrl);
|
||||
|
||||
@@ -35,6 +37,8 @@ const onlineMoving = {
|
||||
test("missing subject state is explicit all for that binding", () => {
|
||||
assert.equal(mapPresentationBindingIsAll("fleet", {}), true);
|
||||
assert.equal(mapFactMatchesFilters(onlineMoving, profile, {}, "fleet"), true);
|
||||
assert.equal(mapPresentationFacetValueIsEnabled({}, "signal_state", "active"), true);
|
||||
assert.equal(mapPresentationFacetValueIsEnabled({}, "signal_state", "inactive"), true);
|
||||
});
|
||||
|
||||
test("a selected chip restricts only its own facet", () => {
|
||||
@@ -49,62 +53,120 @@ test("deselecting the last chip remains empty and never normalizes to all", () =
|
||||
assert.equal(mapFactMatchesFilters(onlineMoving, profile, filters, "fleet"), false);
|
||||
});
|
||||
|
||||
test("interactive deselect of the last chip preserves the zero-match state", () => {
|
||||
const selected = toggleMapPresentationFacetSelection({}, "signal_state", "active");
|
||||
assert.deepEqual(selected, { signal_state: ["active"] });
|
||||
test("toggling from implicit all disables the clicked value instead of selecting only it", () => {
|
||||
const facets = toggleMapPresentationFacetSelection(
|
||||
{},
|
||||
"signal_state",
|
||||
"active",
|
||||
["active", "inactive"],
|
||||
);
|
||||
|
||||
const empty = toggleMapPresentationFacetSelection(selected, "signal_state", "active");
|
||||
const filters = { fleet: { visible: true, facets: empty } };
|
||||
assert.deepEqual(empty, { signal_state: [] });
|
||||
assert.equal(mapPresentationBindingIsAll("fleet", filters), false);
|
||||
assert.equal(mapFactMatchesFilters(onlineMoving, profile, filters, "fleet"), false);
|
||||
});
|
||||
|
||||
test("the first chip selected after a saved empty view removes every empty sentinel", () => {
|
||||
const selected = toggleMapPresentationFacetSelection({
|
||||
signal_state: [],
|
||||
movement_state: [],
|
||||
}, "signal_state", "active");
|
||||
|
||||
assert.deepEqual(selected, { signal_state: ["active"] });
|
||||
assert.deepEqual(facets, { signal_state: ["inactive"] });
|
||||
assert.equal(mapPresentationFacetValueIsEnabled(facets, "signal_state", "active"), false);
|
||||
assert.equal(mapPresentationFacetValueIsEnabled(facets, "signal_state", "inactive"), true);
|
||||
assert.equal(mapFactMatchesFilters(onlineMoving, profile, {
|
||||
fleet: { visible: true, facets: selected },
|
||||
}, "fleet"), true);
|
||||
fleet: { visible: true, facets },
|
||||
}, "fleet"), false);
|
||||
});
|
||||
|
||||
test("a complete interactive toggle cycle returns to zero matches instead of all", () => {
|
||||
let facets = { signal_state: [], movement_state: [] };
|
||||
const toggle = (field, value) => {
|
||||
facets = toggleMapPresentationFacetSelection(facets, field, value);
|
||||
};
|
||||
test("turning off the last enabled value is explicit zero and can be re-enabled locally", () => {
|
||||
const none = toggleMapPresentationFacetSelection(
|
||||
{ signal_state: ["inactive"] },
|
||||
"signal_state",
|
||||
"inactive",
|
||||
["active", "inactive"],
|
||||
);
|
||||
assert.deepEqual(none, { signal_state: [] });
|
||||
assert.equal(mapFactMatchesFilters(onlineMoving, profile, {
|
||||
fleet: { visible: true, facets: none },
|
||||
}, "fleet"), false);
|
||||
|
||||
toggle("signal_state", "active");
|
||||
toggle("signal_state", "inactive");
|
||||
toggle("movement_state", "moving");
|
||||
toggle("movement_state", "stopped");
|
||||
assert.deepEqual(facets, {
|
||||
const active = toggleMapPresentationFacetSelection(
|
||||
{ ...none, movement_state: [] },
|
||||
"signal_state",
|
||||
"active",
|
||||
["active", "inactive"],
|
||||
);
|
||||
assert.deepEqual(active, { signal_state: ["active"], movement_state: [] });
|
||||
assert.equal(mapPresentationFacetValueIsEnabled(active, "signal_state", "active"), true);
|
||||
assert.equal(mapPresentationFacetValueIsEnabled(active, "movement_state", "moving"), false);
|
||||
});
|
||||
|
||||
test("enabling the final disabled value canonicalizes that facet back to implicit all", () => {
|
||||
const facets = toggleMapPresentationFacetSelection(
|
||||
{ signal_state: ["active"] },
|
||||
"signal_state",
|
||||
"inactive",
|
||||
["active", "inactive"],
|
||||
);
|
||||
|
||||
assert.deepEqual(facets, {});
|
||||
assert.equal(mapPresentationBindingIsAll("fleet", { fleet: { visible: true, facets } }), true);
|
||||
assert.equal(mapPresentationFacetValueIsEnabled(facets, "signal_state", "active"), true);
|
||||
assert.equal(mapPresentationFacetValueIsEnabled(facets, "signal_state", "inactive"), true);
|
||||
});
|
||||
|
||||
test("legacy visually-all facets cannot keep suppressing offline subjects", () => {
|
||||
const legacyFacets = {
|
||||
signal_state: ["active", "inactive"],
|
||||
movement_state: ["moving", "stopped"],
|
||||
});
|
||||
};
|
||||
const offlineStopped = {
|
||||
attributes: { signal_state: "inactive", movement_state: "stopped" },
|
||||
};
|
||||
|
||||
toggle("signal_state", "active");
|
||||
toggle("signal_state", "inactive");
|
||||
toggle("movement_state", "moving");
|
||||
toggle("movement_state", "stopped");
|
||||
assert.equal(mapPresentationFacetValueIsEnabled(legacyFacets, "signal_state", "inactive"), true);
|
||||
assert.equal(mapPresentationFacetValueIsEnabled(legacyFacets, "movement_state", "stopped"), true);
|
||||
assert.equal(mapFactMatchesFilters(
|
||||
offlineStopped,
|
||||
profile,
|
||||
{ fleet: { visible: true, facets: legacyFacets } },
|
||||
"fleet",
|
||||
), false);
|
||||
|
||||
const filters = { fleet: { visible: true, facets } };
|
||||
assert.deepEqual(facets, { movement_state: [] });
|
||||
assert.equal(mapPresentationBindingIsAll("fleet", filters), false);
|
||||
assert.equal(mapFactMatchesFilters(onlineMoving, profile, filters, "fleet"), false);
|
||||
const facets = normalizeMapPresentationFacetSelections(legacyFacets, profile);
|
||||
|
||||
assert.deepEqual(facets, {});
|
||||
assert.equal(mapFactMatchesFilters(
|
||||
offlineStopped,
|
||||
profile,
|
||||
{ fleet: { visible: true, facets } },
|
||||
"fleet",
|
||||
), true);
|
||||
});
|
||||
|
||||
test("interactive deselect preserves other values and facet constraints", () => {
|
||||
test("a full toggle cycle has distinct and recoverable all and none states", () => {
|
||||
let facets = {};
|
||||
const toggle = (value) => {
|
||||
facets = toggleMapPresentationFacetSelection(
|
||||
facets,
|
||||
"signal_state",
|
||||
value,
|
||||
["active", "inactive"],
|
||||
);
|
||||
};
|
||||
|
||||
toggle("active");
|
||||
assert.deepEqual(facets, { signal_state: ["inactive"] });
|
||||
toggle("inactive");
|
||||
assert.deepEqual(facets, { signal_state: [] });
|
||||
toggle("active");
|
||||
assert.deepEqual(facets, { signal_state: ["active"] });
|
||||
toggle("inactive");
|
||||
assert.deepEqual(facets, {});
|
||||
});
|
||||
|
||||
test("interactive toggle preserves independent facet constraints", () => {
|
||||
const facets = {
|
||||
signal_state: ["active", "inactive"],
|
||||
signal_state: ["active"],
|
||||
movement_state: ["moving"],
|
||||
};
|
||||
assert.deepEqual(toggleMapPresentationFacetSelection(facets, "signal_state", "active"), {
|
||||
signal_state: ["inactive"],
|
||||
assert.deepEqual(toggleMapPresentationFacetSelection(
|
||||
facets,
|
||||
"signal_state",
|
||||
"inactive",
|
||||
["active", "inactive"],
|
||||
), {
|
||||
movement_state: ["moving"],
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user