diff --git a/index.html b/index.html index b9a5b84..259cc89 100644 --- a/index.html +++ b/index.html @@ -14,7 +14,12 @@

Palettes Based on JSON

-
+
 
+
+
+
+
 
+
diff --git a/public/palettes/bliss_strategies.json b/public/palettes/bliss_strategies.json new file mode 100644 index 0000000..c77da4d --- /dev/null +++ b/public/palettes/bliss_strategies.json @@ -0,0 +1,203 @@ +{ + "name": "Bliss Strategies", + "cells": { + "bliss-strategies-7c33c282-4103-4c1e-a872-e97ebd6468ea": { + "type": "LabelCell", + "options": { + "label": "Bliss Strategies", + "bciAvId": 23031, + "rowStart": 1, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 9 + } + }, + "abstract-37848ed0-cbff-450b-bb55-f91a0d1bebb0": { + "type": "ActionBmwCodeCell", + "options": { + "label": "abstract", + "bciAvId": 27001, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "answer-b7ed0181-bd57-4389-b2f1-b65baf09822d": { + "type": "ActionBmwCodeCell", + "options": { + "label": "answer", + "bciAvId": 12393, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "belongs-with-2cd58e48-1a85-4acf-bc70-1c509c346cdd": { + "type": "ActionBmwCodeCell", + "options": { + "label": "belongs with", + "bciAvId": [12663, ";", 8993], + "rowStart": 2, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "combine-indicator-4b96fbf1-37fb-41e5-a5b5-05097e158e33": { + "type": "ActionBmwCodeCell", + "options": { + "label": "combine (indicator)", + "bciAvId": 13382, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 4, + "columnSpan": 1 + } + }, + "command-025a1cd8-73d9-4fff-bb83-f4664a7e1b8c": { + "type": "ActionBmwCodeCell", + "options": { + "label": "command", + "bciAvId": 8483, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 5, + "columnSpan": 1 + } + }, + "generalization-af769fa5-09d7-4e63-bf2e-d6e08fdcd446": { + "type": "ActionBmwCodeCell", + "options": { + "label": "generalization", + "bciAvId": 14430, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 6, + "columnSpan": 1 + } + }, + "group-of-3097898d-fa15-41cc-8848-af26d001e92e": { + "type": "ActionBmwCodeCell", + "options": { + "label": "group of", + "bciAvId": 14647, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 7, + "columnSpan": 1 + } + }, + "intensity-25f26791-580b-425e-8a16-3fc5b4bbc7d1": { + "type": "ActionBmwCodeCell", + "options": { + "label": "intensity", + "bciAvId": 14947, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 8, + "columnSpan": 1 + } + }, + "metaphor-94eddd4b-9e86-4f87-a80e-2b3254cd0426": { + "type": "ActionBmwCodeCell", + "options": { + "label": "metaphor", + "bciAvId": 15460, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 9, + "columnSpan": 1 + } + }, + "not-94eddd4b-9e86-4f87-a80e-2b3254cd0426": { + "type": "ActionBmwCodeCell", + "options": { + "label": "not", + "bciAvId": 15733, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "opposite-a13f5d63-c4bb-4080-b256-add0a5171739": { + "type": "ActionBmwCodeCell", + "options": { + "label": "opposite", + "bciAvId": 15927, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "meaning-d0c38fc5-6f27-4aec-9e0e-46a3a5e2c12f": { + "type": "ActionBmwCodeCell", + "options": { + "label": "meaning", + "bciAvId": 15441, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "rhymes-with-4f8a3e5b-ddbd-42a3-ba37-b00aa8923df7": { + "type": "ActionBmwCodeCell", + "options": { + "label": "rhymes (with)", + "bciAvId": [16502, ";", 8993], + "rowStart": 3, + "rowSpan": 1, + "columnStart": 4, + "columnSpan": 1 + } + }, + "same-0dac5ebd-9fdb-4aa8-a80b-ed607e43a445": { + "type": "ActionBmwCodeCell", + "options": { + "label": "same", + "bciAvId": 25368, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 5, + "columnSpan": 1 + } + }, + "similar-sound-8481d07c-cbbf-4a9c-acdd-d7de99ee05b4": { + "type": "ActionBmwCodeCell", + "options": { + "label": "similar sound", + "bciAvId": 16986, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 6, + "columnSpan": 1 + } + }, + "similar-to-6005ae50-b518-4a2a-9106-0f48b8b3453c": { + "type": "ActionBmwCodeCell", + "options": { + "label": "similar (to)", + "bciAvId": 16984, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 7, + "columnSpan": 1 + } + }, + "type-31efeefe-5d7a-471b-bcc0-1bc311699328": { + "type": "ActionBmwCodeCell", + "options": { + "label": "type", + "bciAvId": 17961, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 8, + "columnSpan": 1 + } + } + } +} diff --git a/public/palettes/grammar_strategies.json b/public/palettes/grammar_strategies.json new file mode 100644 index 0000000..35d7c35 --- /dev/null +++ b/public/palettes/grammar_strategies.json @@ -0,0 +1,71 @@ +{ + "name": "Grammar Strategies", + "cells": { + "grammar-strategies-56f542cf-0b58-4713-84d1-4772170e43f2": { + "type": "LabelCell", + "options": { + "label": "Grammar Strategies", + "bciAvId": 27060, + "rowStart": 1, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 5 + } + }, + "adjective-ab6e667a-807f-42f6-80aa-10d69582389b": { + "type": "ActionBmwCodeCell", + "options": { + "label": "adjective", + "bciAvId": 27002, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "adverb-d67d8cdd-eb28-4a88-99fb-1da2da4b91db": { + "type": "ActionBmwCodeCell", + "options": { + "label": "adverb", + "bciAvId": 27003, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "noun-a8e94a5b-e87e-4129-8d36-2b1eb126b422": { + "type": "ActionBmwCodeCell", + "options": { + "label": "noun", + "bciAvId": 27096, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "plural-a843e08e-b92a-4e51-bbe9-24d8f9694fd0": { + "type": "ActionBmwCodeCell", + "options": { + "label": "plural", + "bciAvId": 9011, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 4, + "columnSpan": 1 + } + }, + "verb-8ae2dfb4-f95e-4a37-9f9c-f18dff198dbc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "verb", + "bciAvId": 27152, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 5, + "columnSpan": 1 + } + } + } +} diff --git a/public/palettes/palette_file_map.json b/public/palettes/palette_file_map.json index 24c54a1..03a8f4c 100644 --- a/public/palettes/palette_file_map.json +++ b/public/palettes/palette_file_map.json @@ -2,5 +2,9 @@ "My Family Palette": "/palettes/myfamily.json", "People": "/palettes/people.json", "BMW Palette": "/palettes/bmw_palette.json", - "Palettes": "/palettes/palettes.json" + "Palettes": "/palettes/palettes.json", + "Strategies": "/palettes/strategies.json", + "Grammar Strategies": "/palettes/grammar_strategies.json", + "Search Strategies": "/palettes/search_strategies.json", + "Bliss Strategies": "/palettes/bliss_strategies.json" } diff --git a/public/palettes/search_strategies.json b/public/palettes/search_strategies.json new file mode 100644 index 0000000..463885b --- /dev/null +++ b/public/palettes/search_strategies.json @@ -0,0 +1,51 @@ +{ + "name": "Search Strategies", + "cells": { + "search-strategies-4a922731-9781-4ab5-a645-4176d6d8edb9": { + "type": "LabelCell", + "options": { + "label": "Search Strategies", + "bciAvId": 16742, + "rowStart": 1, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 3 + } + }, + "word-that-means-4a922731-9781-4ab5-a645-4176d6d8edb9": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Word that means", + "bciAvId": [18272, "/", 16482 , "/", 15441, ";", 8999, ";", 9004 ], + "rowStart": 2, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "minspeak-personal-associations-622a33f2-b598-47ed-a6e2-6a5466859ccc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Minspeak personal associations", + "bciAvId": [16752, ";", 8998, "/", 12663, "/", 13380, ";", 9011 ], + "rowStart": 2, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "personal-conventions-792c9146-309c-45ef-a645-026c69d0aa50": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Personal conventions", + "bciAvId": [ + 16752, ";", 8998, "/", 12663, "/", 13382, "/", 12334, ";", 9011, "/", 12354, "/", 13382 + ], + "rowStart": 2, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + } + } +} diff --git a/public/palettes/strategies.json b/public/palettes/strategies.json new file mode 100644 index 0000000..1d4ac6c --- /dev/null +++ b/public/palettes/strategies.json @@ -0,0 +1,88 @@ +{ + "name": "Strategies", + "cells": { + "strategies-label-6057d056-db6a-4f73-8259-0d28566e7388": { + "type": "LabelCell", + "options": { + "label": "Strategy", + "bciAvId": 25814, + "rowStart": 1, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 6 + } + }, + "bliss-37863659-0289-4c4d-a88f-e33e6582f565": { + "type": "ActionBranchToPaletteCell", + "options": { + "label": "Bliss strategies", + "branchTo": "Bliss Strategies", + "bciAvId": 23031, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "grammar-a9c75796-b5e0-43ca-8822-04d05e8a1309": { + "type": "ActionBranchToPaletteCell", + "options": { + "label": "Grammar strategies", + "branchTo": "Grammar Strategies", + "bciAvId": 27060, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "search-529841cf-3370-4365-b04e-ac88b0f0d5f2": { + "type": "ActionBranchToPaletteCell", + "options": { + "label": "Search strategies", + "branchTo": "Search Strategies", + "bciAvId": 16742, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "augment-14de9078-6f8d-489e-ac6e-27cee440722d": { + "type": "ActionBranchToPaletteCell", + "options": { + "label": "Augment with pictures", + "branchTo": "Augmentative Strategies", + "bciAvId": [29003, "/", 18267, "/", 16172, ";", 9011], + "rowStart": 2, + "rowSpan": 1, + "columnStart": 4, + "columnSpan": 1 + } + }, + "personal-a9cbb381-ad44-4af0-a24c-515fc5587b9d": { + "type": "ActionBranchToPaletteCell", + "options": { + "label": "Personal conventions", + "branchTo": "Personal Strategies", + "bciAvId": 16445, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 5, + "columnSpan": 1 + } + }, + "personal2-a9cbb381-ad44-4af0-a24c-515fc5587b9d": { + "type": "ActionBranchToPaletteCell", + "options": { + "label": "Personal conventions 2", + "branchTo": "Personal Strategies 2", + "bciAvId": 16445, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 6, + "columnSpan": 1 + } + } + } +} diff --git a/src/client/ActionBranchToPaletteCell.ts b/src/client/ActionBranchToPaletteCell.ts index 4f1dd77..8b4d426 100644 --- a/src/client/ActionBranchToPaletteCell.ts +++ b/src/client/ActionBranchToPaletteCell.ts @@ -33,13 +33,15 @@ const navigateToPalette = async (event: Event): Promise => { const button = event.currentTarget as HTMLElement; speak(button.innerText); + const buttonsPaletteName = button.parentElement.getAttribute("data-palettename"); const branchToPaletteName = button.getAttribute("data-branchto"); const paletteDefinition = await paletteStore.getNamedPalette(branchToPaletteName, loadPaletteFromJsonFile); if (paletteDefinition) { const displayElement = button.parentElement.parentElement; - navigationStack.push(navigationStack.currentPalette); + const goBackPalette = await paletteStore.getNamedPalette(buttonsPaletteName); + navigationStack.push({ palette: goBackPalette, htmlElement: displayElement }); render (html`<${Palette} json=${paletteDefinition}/>`, displayElement); - navigationStack.currentPalette = paletteDefinition; + navigationStack.currentPalette = { palette: paletteDefinition, htmlElement: displayElement }; } else { console.error(`navigateToPalette(): Unable to locate the palette definition for ${branchToPaletteName}`); diff --git a/src/client/CommandGoBackCell.test.ts b/src/client/CommandGoBackCell.test.ts index 2e1f001..b089dd7 100644 --- a/src/client/CommandGoBackCell.test.ts +++ b/src/client/CommandGoBackCell.test.ts @@ -13,14 +13,12 @@ import { render, screen } from "@testing-library/preact"; import "@testing-library/jest-dom"; import { html } from "htm/preact"; -import { initAdaptivePaletteGlobals, adaptivePaletteGlobals } from "./GlobalData"; +import { initAdaptivePaletteGlobals } from "./GlobalData"; import { CommandGoBackCell } from "./CommandGoBackCell"; describe("CommandGoBackCell render tests", (): void => { const TEST_CELL1_ID = "uuid-of-some-kind"; - const TEST_CELL2_ID = "uuid-of-another-kind"; - const TEST_CONTROL_ID = "non-empty-id"; const goBackCell = { options: { "label": "Back Up", @@ -31,25 +29,12 @@ describe("CommandGoBackCell render tests", (): void => { "bciAvId": 12612 } }; - const goBackCell2 = { - options: { - "label": "Back Up non-empty aria-controls", - "rowStart": "3", - "rowSpan": "2", - "columnStart": "2", - "columnSpan": "1", - "bciAvId": 12612 - } - }; beforeAll(async () => { - // Note: no id provided for the element that palettes are rendered inside. - // The `aria-controls` attribute of the test CommandGoBackCell should be - // the empty string, at first. await initAdaptivePaletteGlobals(); }); - test("CommandGoBackCell rendering, empty aria-controls", async (): Promise => { + test("CommandGoBackCell rendering", async (): Promise => { render(html` <${CommandGoBackCell} @@ -68,7 +53,6 @@ describe("CommandGoBackCell render tests", (): void => { expect(button.id).toBe(TEST_CELL1_ID); expect(button.getAttribute("class")).toBe("btn-command"); expect(button.textContent).toBe(goBackCell.options.label); - expect(button.getAttribute("aria-controls")).toBe(""); // Check the grid cell styles. expect(button.style["grid-column"]).toBe("2 / span 1"); @@ -77,19 +61,4 @@ describe("CommandGoBackCell render tests", (): void => { // Check disabled state (should be enabled) expect(button.getAttribute("disabled")).toBe(null); }); - - test("CommandGoBackCell rendering with non-empty aria-controls", async(): Promise => { - // Give the main palette rendering area a non-empty id. - adaptivePaletteGlobals.mainPaletteContainerId = TEST_CONTROL_ID; - render(html` - <${CommandGoBackCell} - id="${TEST_CELL2_ID}" - options=${goBackCell2.options} - />` - ); - - const button = await screen.findByRole("button", {name: goBackCell2.options.label}); - expect(button.id).toBe(TEST_CELL2_ID); - expect(button.getAttribute("aria-controls")).toBe(TEST_CONTROL_ID); - }); }); diff --git a/src/client/CommandGoBackCell.ts b/src/client/CommandGoBackCell.ts index 1e96dad..a6a6e87 100644 --- a/src/client/CommandGoBackCell.ts +++ b/src/client/CommandGoBackCell.ts @@ -34,15 +34,15 @@ const goBackToPalette = async (event: Event): Promise => { speak(button.innerText); const paletteToGoBackTo = navigationStack.peek(); - if (paletteToGoBackTo) { - const paletteDefinition = await paletteStore.getNamedPalette(paletteToGoBackTo.name, loadPaletteFromJsonFile); + if (paletteToGoBackTo && paletteToGoBackTo.palette) { + const paletteDefinition = await paletteStore.getNamedPalette(paletteToGoBackTo.palette.name, loadPaletteFromJsonFile); if (paletteDefinition) { - const paletteContainer = document.getElementById(button.getAttribute("aria-controls")) || document.body; - navigationStack.popAndSetCurrent(paletteDefinition); + const paletteContainer = paletteToGoBackTo.htmlElement || document.body; + navigationStack.popAndSetCurrent(paletteToGoBackTo); render (html`<${Palette} json=${paletteDefinition}/>`, paletteContainer); } else { - console.error(`goBackToPalette(): Unable to locate the palette definition for ${paletteToGoBackTo}`); + console.error(`goBackToPalette(): Unable to locate the palette definition for ${paletteToGoBackTo.palette.name}`); } } }; @@ -52,7 +52,6 @@ export function CommandGoBackCell (props: CommandGoBackCellPropsType): VNode { const { columnStart, columnSpan, rowStart, rowSpan, bciAvId, label } = props.options; - const ariaControlsId = adaptivePaletteGlobals.mainPaletteContainerId; const gridStyles = ` grid-column: ${columnStart} / span ${columnSpan}; @@ -62,7 +61,7 @@ export function CommandGoBackCell (props: CommandGoBackCellPropsType): VNode { return html` `; diff --git a/src/client/GlobalData.ts b/src/client/GlobalData.ts index cdd2181..a2f75da 100644 --- a/src/client/GlobalData.ts +++ b/src/client/GlobalData.ts @@ -24,6 +24,7 @@ import { CommandGoBackCell } from "./CommandGoBackCell"; import { ContentBmwEncoding } from "./ContentBmwEncoding"; import { CommandClearEncoding } from "./CommandClearEncoding"; import { CommandDelLastEncoding } from "./CommandDelLastEncoding"; +import { LabelCell } from "./LabelCell"; import { PaletteStore } from "./PaletteStore"; import { NavigationStack } from "./NavigationStack"; @@ -33,7 +34,8 @@ export const cellTypeRegistry = { "CommandGoBackCell": CommandGoBackCell, "ContentBmwEncoding": ContentBmwEncoding, "CommandClearEncoding": CommandClearEncoding, - "CommandDelLastEncoding": CommandDelLastEncoding + "CommandDelLastEncoding": CommandDelLastEncoding, + "LabelCell": LabelCell }; /** diff --git a/src/client/LabelCell.scss b/src/client/LabelCell.scss new file mode 100644 index 0000000..0ac9a1e --- /dev/null +++ b/src/client/LabelCell.scss @@ -0,0 +1,17 @@ +/* + * Copyright 2024 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ + +.labelCell { + border: 2px solid blue; + background-color: lightgrey; + padding-inline: 0.5rem; + text-align: center; +} diff --git a/src/client/LabelCell.test.ts b/src/client/LabelCell.test.ts new file mode 100644 index 0000000..9e3eadd --- /dev/null +++ b/src/client/LabelCell.test.ts @@ -0,0 +1,60 @@ +/* + * Copyright 2024 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ + +import { render, screen } from "@testing-library/preact"; +import "@testing-library/jest-dom"; +import { html } from "htm/preact"; + +import { initAdaptivePaletteGlobals } from "./GlobalData"; +import { LabelCell } from "./LabelCell"; + +describe("LabelCell render tests", (): void => { + + const LABEL_CELL_ID = "uuid-of-label-element"; + const testLabelCell = { + options: { + "label": "Verbs", + "rowStart": "3", + "rowSpan": "2", + "columnStart": "2", + "columnSpan": "1", + "bciAvId": [ 12335, ";", 9011 ] // Verb with plural indicator + } + }; + + beforeAll(async (): Promise => { + await initAdaptivePaletteGlobals(); + }); + + test("Label cell rendering", async (): Promise => { + + render(html` + <${LabelCell} + id=${LABEL_CELL_ID} + options=${testLabelCell.options} + />` + ); + + // Check the rendered cell + const labelElement = await screen.findByText(testLabelCell.options.label); + + // Check that the LabelCell/labelElement is rendered and has the correct + // attributes and text. + expect(labelElement).toBeVisible(); + expect(labelElement).toBeValid(); + expect(labelElement.id).toBe(LABEL_CELL_ID); + expect(labelElement.getAttribute("class")).toBe("labelCell"); + + // Check the grid cell styles. + expect(labelElement.style["grid-column"]).toBe("2 / span 1"); + expect(labelElement.style["grid-row"]).toBe("3 / span 2"); + }); +}); diff --git a/src/client/LabelCell.ts b/src/client/LabelCell.ts new file mode 100644 index 0000000..c1be822 --- /dev/null +++ b/src/client/LabelCell.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2024 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ + +import { VNode } from "preact"; +import { html } from "htm/preact"; +import { BlissSymbolInfoType, LayoutInfoType } from "./index.d"; +import { generateGridStyle } from "./GlobalUtils"; +import { BlissSymbol } from "./BlissSymbol"; +import "./LabelCell.scss"; + +type LabelCellPropsType = { + id: string, + options: BlissSymbolInfoType & LayoutInfoType +}; + +export function LabelCell (props: LabelCellPropsType): VNode { + + const { + columnStart, columnSpan, rowStart, rowSpan, bciAvId, label + } = props.options; + + const gridStyles = generateGridStyle(columnStart, columnSpan, rowStart, rowSpan); + return html` + + `; +} diff --git a/src/client/NavigationStack.test.ts b/src/client/NavigationStack.test.ts index 79b5eee..8c0a897 100644 --- a/src/client/NavigationStack.test.ts +++ b/src/client/NavigationStack.test.ts @@ -9,8 +9,14 @@ * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE */ +import { render, screen } from "@testing-library/preact"; +import "@testing-library/jest-dom"; +import { html } from "htm/preact"; + import { NavigationStack } from "./NavigationStack"; +const RENDERING_TEST_ID = "renderingDiv"; + const testPalette1 = { "name": "testPalette1", "cells": { @@ -56,9 +62,16 @@ const testPalette2 = { } }; + describe("NavigationStack module - basics", (): void => { const navigation = new NavigationStack(); + let renderingElement; + + beforeAll(async (): Promise => { + render(html`
Rendering div
`); + renderingElement = await screen.findByTestId(RENDERING_TEST_ID); + }); test("Empty NavigationStack", (): void => { expect(navigation.isEmpty()).toBe(true); @@ -66,76 +79,90 @@ describe("NavigationStack module - basics", (): void => { }); test("Current palette accessors", (): void => { - navigation.currentPalette = testPalette1; - expect(navigation.currentPalette).toBe(testPalette1); + const testStackItem1 = { + palette: testPalette1, + htmlElement: renderingElement + }; + navigation.currentPalette = testStackItem1; + expect(navigation.currentPalette).toBe(testStackItem1); navigation.currentPalette = null; expect(navigation.currentPalette).toBe(null); }); test("Flush and reset the navigation stack", (): void => { - navigation.flushReset(testPalette2); + const testStackItem2 = { + palette: testPalette2, + htmlElement: renderingElement + }; + navigation.flushReset(testStackItem2); expect(navigation.isEmpty()).toBe(true); - expect(navigation.currentPalette).toBe(testPalette2); + expect(navigation.currentPalette).toBe(testStackItem2); }); }); describe("NavigationStack module - pushing and popping", (): void => { const navigation = new NavigationStack(); + let renderingElement, testStackItem1, testStackItem2; - beforeEach ((): void => { - navigation.flushReset(null); + beforeAll(async (): Promise => { + render(html`
Rendering div
`); + renderingElement = await screen.findByTestId(RENDERING_TEST_ID); + testStackItem1 = { palette: testPalette1, htmlElement: renderingElement }; + testStackItem2 = { palette: testPalette2, htmlElement: renderingElement }; }); test("Non-empty NavigationStack", (): void => { - navigation.push(testPalette1); + navigation.push(testStackItem1); expect(navigation.isEmpty()).toBe(false); - expect(navigation.peek()).toBe(testPalette1); + expect(navigation.peek()).toBe(testStackItem1); expect(navigation.currentPalette).toBe(null); }); test("Pop the top of the stack", (): void => { - navigation.push(testPalette1); + navigation.flushReset(null); + navigation.push(testStackItem1); const topPalette = navigation.pop(); - expect(topPalette).toBe(testPalette1); + expect(topPalette).toBe(testStackItem1); expect(navigation.isEmpty()).toBe(true); // The current palette should be unaffected by a pop operation. expect(navigation.currentPalette).toBe(null); }); test("Multiple layers and a current palette", (): void => { - navigation.push(testPalette1); - navigation.push(testPalette2); - navigation.currentPalette = testPalette1; + navigation.push(testStackItem1); + navigation.push(testStackItem2); + navigation.currentPalette = testStackItem1; expect(navigation.isEmpty()).toBe(false); - expect(navigation.peek()).toBe(testPalette2); - expect(navigation.peek(1)).toBe(testPalette1); - expect(navigation.currentPalette).toBe(testPalette1); + expect(navigation.peek()).toBe(testStackItem2); + expect(navigation.peek(1)).toBe(testStackItem1); + expect(navigation.currentPalette).toBe(testStackItem1); }); test("Check invalid peek()", (): void => { - navigation.push(testPalette1); - navigation.push(testPalette2); + navigation.push(testStackItem1); + navigation.push(testStackItem2); expect(navigation.isEmpty()).toBe(false); expect(navigation.peek(-1)).toBe(undefined); expect(navigation.peek(1024)).toBe(undefined); }); test("Check pop and set current utility function", (): void => { - navigation.currentPalette = testPalette1; - navigation.push(testPalette1); - navigation.push(testPalette2); - const poppedPalette = navigation.popAndSetCurrent(testPalette2); - expect(poppedPalette).toBe(testPalette2); - expect(navigation.peek()).toBe(testPalette1); - expect(navigation.currentPalette).toBe(testPalette2); + navigation.currentPalette = testStackItem1; + navigation.push(testStackItem1); + navigation.push(testStackItem2); + const poppedPalette = navigation.popAndSetCurrent(testStackItem2); + expect(poppedPalette).toBe(testStackItem2); + expect(navigation.peek()).toBe(testStackItem1); + expect(navigation.currentPalette).toBe(testStackItem2); }); test("Check peeking at the bottom of the stack", (): void => { + navigation.flushReset(null); expect(navigation.isEmpty()).toBe(true); expect(navigation.peekLast()).toBe(undefined); - navigation.push(testPalette1); - navigation.push(testPalette2); - expect(navigation.peekLast()).toBe(testPalette1); + navigation.push(testStackItem1); + navigation.push(testStackItem2); + expect(navigation.peekLast()).toBe(testStackItem1); }); }); diff --git a/src/client/NavigationStack.ts b/src/client/NavigationStack.ts index a75eda9..51c2386 100644 --- a/src/client/NavigationStack.ts +++ b/src/client/NavigationStack.ts @@ -9,17 +9,17 @@ * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE */ -import { JsonPaletteType } from "./index.d"; +import { NavStackItemType } from "./index.d"; export class NavigationStack { // The actual stack keeping track of where the user was. The top-most palette // is the most recent one the user was at before navigating to a new // layer/palette. - navigateBackStack: Array; + navigateBackStack: Array; // The current palette in the palette display area - currPalette: JsonPaletteType; + currPalette: NavStackItemType; /** * Initialize the navigation stack to have zero entries. @@ -38,12 +38,13 @@ export class NavigationStack { } /** - * Puah a palette onto the top of the navigation stack. - * @param: {JsonPaletteType} palette - The palette to push. If `null` or + * Puah a palette onto the top of the navigation stack and also remember where + * it was rendered. + * @param: {NavStackItemType} palette - The palette to push. If `null` or * `undefined`, the navigation stack is * left untouched. */ - push (palette: JsonPaletteType): void { + push (palette: NavStackItemType): void { if (!palette) { return; } @@ -53,10 +54,10 @@ export class NavigationStack { /** * Pop and return the most recently pushed palette from the top of the * navigation stack. - * @return {JsonPaletteType} - reference to the popped palette; null if the - * stack is empty. + * @return {NavStackItemType} - reference to the popped palette; null if the + * stack is empty. */ - pop (): JsonPaletteType | null { + pop (): NavStackItemType | null { if (this.isEmpty()) { return null; } else { @@ -65,18 +66,18 @@ export class NavigationStack { } /** - * Return the palette at the top of the stack without changing the stack + * Return the stack item at the top of the stack without changing the stack * itself. If an index is given, the palette at that index is returned. Note * that an index of zero denotes the top of the stack. * @param {integer} stackIndex - Optional: How far down the stack to peek, * where zero is the top of the stack (default). * If out of range, `undefined` is returned. - * @return {JsonPaletteType} - Reference to the palette at the top of the - * stack or at the given index; `undefined` if - * the given stack index is invalid -- negative or - * greater than the size of the stack. + * @return {NavStackItemType} - Reference to the palette at the top of the + * stack or at the given index; `undefined` if + * the given stack index is invalid -- negative + * or greater than the size of the stack. */ - peek (stackIndex:number = 0): JsonPaletteType | undefined { + peek (stackIndex:number = 0): NavStackItemType | undefined { // Flip the index value since Array.push() puts the item at the end // of the array. let palette = undefined; @@ -88,11 +89,11 @@ export class NavigationStack { } /** - * Return the palette at the bottom of the stack without changing the stack. - * @return {JsonPaletteType} - Reference to the palette at the bottom of the - * stack, or `undefined` if the stack is empty. + * Return the stack item at the bottom of the stack without changing the stack. + * @return {NavStackItemType} - Reference to the palette at the bottom of the + * stack, or `undefined` if the stack is empty. */ - peekLast (): JsonPaletteType | undefined { + peekLast (): NavStackItemType | undefined { if (this.isEmpty()) { return undefined; } else { @@ -103,37 +104,37 @@ export class NavigationStack { /** * Pop/return the most recently pushed palette and set the currently displayed * palette to the given one. - * @param {JsonPaletteType} - The palette that is currently displayed, or - * is about to be displayed. - * @return {JsonPaletteType} - The most recently visited palette. + * @param {NavStackItemType} - The palette that is currently displayed, or + * is about to be displayed. + * @return {NavStackItemType} - The most recently visited palette. */ - popAndSetCurrent (currentPalette: JsonPaletteType): JsonPaletteType | null { + popAndSetCurrent (currentPalette: NavStackItemType): NavStackItemType | null { this.currentPalette = currentPalette; return this.pop(); } /** * Empty the navigation stack and reset the current palette displayed. - * @param {JsonPaletteType} - The palette that is currently displayed. + * @param {NavStackItemType} - The palette that is currently displayed. */ - flushReset (currentPalette: JsonPaletteType): void { + flushReset (currentPalette: NavStackItemType): void { this.currentPalette = currentPalette; this.navigateBackStack.length = 0; } /** * Accessor for setting the currently displayed palette. - * @param: {JsonPaletteType} - the intended current palette. + * @param: {NavStackItemType} - the intended current palette. */ - set currentPalette (palette: JsonPaletteType) { + set currentPalette (palette: NavStackItemType) { this.currPalette = palette; } /** * Accessor for getting the currently displayed palette. - * @return: {JsonPaletteType} - The current palette. + * @return: {NavStackItemType} - The current palette. */ - get currentPalette(): JsonPaletteType { + get currentPalette(): NavStackItemType { return this.currPalette; } } diff --git a/src/client/Palette.integration.test.ts b/src/client/Palette.integration.test.ts index e322f83..247108c 100644 --- a/src/client/Palette.integration.test.ts +++ b/src/client/Palette.integration.test.ts @@ -158,7 +158,7 @@ describe("Palette integration test", () => { // insure that the entire palette is in the DOM. render(html`<${Palette} json=${testPalette}/>`); const navStack = adaptivePaletteGlobals.navigationStack; - navStack.currentPalette = testPalette; + navStack.currentPalette = { palette: testPalette, htmlElement: document.body }; const firstCell = await screen.findByText("First Cell"); expect(firstCell).toBeInTheDocument(); @@ -198,18 +198,22 @@ describe("Palette integration test", () => { fireEvent.click(clearButton); expect(contentArea.childNodes.length).toBe(0); - // Trigger forward navigation - const goForwardButton = await screen.findByText("Go To"); + // Trigger forward navigation. + // Note: the element whose text is "Go To" is actually a
within the + //