-
Notifications
You must be signed in to change notification settings - Fork 4
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
feat: basic implementation for navigation among palette layers (resolves #5) #6
Changes from 29 commits
7105026
6f25a5f
0adab7b
f7530a8
f01e7e0
094f434
e43465c
5e3743f
4752e51
4305b13
d5df467
10a95a8
30b570a
4c91168
10e33c5
a73e48a
7201c22
9555c79
0357a3c
15b899f
4594254
a7c6d83
8776a1b
82d7cd6
08d262c
1ea48b5
de1028a
84ca4f6
946ee4e
44c960c
b30c693
bdf503a
3b832fd
7380960
4f7cc54
49a10b0
4e3a17f
13b87f7
2e4c520
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
*/ | ||
|
||
.actionBranchToPaletteCell { | ||
border: 2px solid blue; | ||
background-color: chartreuse; | ||
padding-inline: 0.5rem; | ||
text-align: center; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* | ||
* 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 { ActionBranchToPaletteCell } from "./ActionBranchToPaletteCell"; | ||
|
||
describe("ActionBranchToPaletteCell render tests", () => { | ||
|
||
const TEST_CELL_ID = "uuid-of-some-kind"; | ||
const goToPaletteCell = { | ||
options: { | ||
"label": "Animals", | ||
"branchTo": "Animals", | ||
"rowStart": "100", | ||
"rowSpan": "12", | ||
"columnStart": "33", | ||
"columnSpan": "11", | ||
"bciAvId": [ 16161, "/", 9011 ] | ||
} | ||
}; | ||
|
||
beforeAll(async () => { | ||
await initAdaptivePaletteGlobals(); | ||
}); | ||
|
||
test("ActionBranchToPaletteCell rendering", async () => { | ||
|
||
render(html` | ||
<${ActionBranchToPaletteCell} | ||
id="${TEST_CELL_ID}" | ||
options=${goToPaletteCell.options} | ||
/>` | ||
); | ||
|
||
// Check the rendered cell | ||
const button = await screen.findByRole("button", {name: goToPaletteCell.options.label}); | ||
|
||
// Check that the ActionBmwCodeCell/button is rendered and has the correct | ||
// attributes and text. | ||
expect(button).toBeVisible(); | ||
expect(button).toBeValid(); | ||
expect(button.id).toBe(TEST_CELL_ID); | ||
expect(button.getAttribute("class")).toBe("actionBranchToPaletteCell"); | ||
expect(button.textContent).toBe(goToPaletteCell.options.label); | ||
|
||
// Check the grid cell styles. | ||
expect(button.style["grid-column"]).toBe(`${goToPaletteCell.options.columnStart} / span ${goToPaletteCell.options.columnSpan}`); | ||
expect(button.style["grid-row"]).toBe(`${goToPaletteCell.options.rowStart} / span ${goToPaletteCell.options.rowSpan}`); | ||
|
||
// Check disabled state (should be enabled) | ||
expect(button.getAttribute("disabled")).toBe(null); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/* | ||
* Copyright 2023 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 } from "preact"; | ||
import { html } from "htm/preact"; | ||
import { BlissSymbolCellType } from "./index.d"; | ||
import { adaptivePaletteGlobals, importPaletteFromJsonFile } from "./GlobalData"; | ||
import { Palette } from "./Palette"; | ||
import { BlissSymbol } from "./BlissSymbol"; | ||
import { speak } from "./GlobalUtils"; | ||
import "./ActionBranchToPaletteCell.scss"; | ||
|
||
// TODO: this is identical to `ActionBmwCodeCellPropsType`. Should it be? | ||
type ActionBranchToPalettePropsType = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm thinking if we should expand the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, and will start work on it next. I also noticed that we don't define the return type of many of our functions. Should we? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a great idea to define the return type for functions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've done them all. The one that was problematic was the return type for the I did some more debugging and it looks like, in the case of Preact, the As for the expanding the options, I'm still working on that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For "expanding the options section", I took the full Let me know what you think. |
||
id: string, | ||
options: BlissSymbolCellType | ||
}; | ||
|
||
/* | ||
* Event handler for an ActionBranchToPaletteCell button/cell that, when clicked, | ||
* finds and renders the palette referenced by this cell. | ||
*/ | ||
const navigateToPalette = async (event) => { | ||
const { paletteStore, navigationStack } = adaptivePaletteGlobals; | ||
const button = event.currentTarget; | ||
speak(button.innerText); | ||
|
||
const branchToPaletteName = button.getAttribute("data-branchto"); | ||
const paletteDefinition = await paletteStore.getNamedPalette(branchToPaletteName, importPaletteFromJsonFile); | ||
if (paletteDefinition) { | ||
const mainPaletteDisplayArea = document.getElementById("mainPaletteDisplayArea"); | ||
navigationStack.push(navigationStack.currentPalette); | ||
render (html`<${Palette} json=${paletteDefinition}/>`, mainPaletteDisplayArea); | ||
navigationStack.currentPalette = paletteDefinition; | ||
} | ||
else { | ||
console.error(`navigateToPalette(): Unable to locate the palette definition for ${branchToPaletteName}`); | ||
} | ||
}; | ||
|
||
export function ActionBranchToPaletteCell (props: ActionBranchToPalettePropsType) { | ||
const { | ||
columnStart, columnSpan, rowStart, rowSpan, branchTo, bciAvId, label | ||
} = props.options; | ||
|
||
const gridStyles = ` | ||
grid-column: ${columnStart} / span ${columnSpan}; | ||
grid-row: ${rowStart} / span ${rowSpan}; | ||
`; | ||
|
||
return html` | ||
<button | ||
id="${props.id}" class="actionBranchToPaletteCell" style="${gridStyles}" | ||
data-branchto="${branchTo}" onClick=${navigateToPalette}> | ||
<${BlissSymbol} bciAvId=${bciAvId} label=${label} /> | ||
</button> | ||
`; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/* | ||
* 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 { CommandGoBackCell } from "./CommandGoBackCell"; | ||
|
||
describe("ActionBmwCodeDell render tests", () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be nice to test the click event of the go back button. Since this test involves The same idea applies in the comment above about testing the click event for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As above with the |
||
|
||
const TEST_CELL_ID = "uuid-of-some-kind"; | ||
const goBackCell = { | ||
options: { | ||
"label": "Back Up", | ||
"rowStart": "3", | ||
"rowSpan": "2", | ||
"columnStart": "2", | ||
"columnSpan": "1", | ||
"bciAvId": 12612 | ||
} | ||
}; | ||
|
||
beforeAll(async () => { | ||
await initAdaptivePaletteGlobals(); | ||
}); | ||
|
||
test("CommandGoBackCell rendering", async () => { | ||
|
||
render(html` | ||
<${CommandGoBackCell} | ||
id="${TEST_CELL_ID}" | ||
options=${goBackCell.options} | ||
/>` | ||
); | ||
|
||
// Check the rendered cell | ||
const button = await screen.findByRole("button", {name: goBackCell.options.label}); | ||
|
||
// Check that the ActionBmwCodeCell/button is rendered and has the correct | ||
// attributes and text. | ||
expect(button).toBeVisible(); | ||
expect(button).toBeValid(); | ||
expect(button.id).toBe(TEST_CELL_ID); | ||
expect(button.getAttribute("class")).toBe("btn-command"); | ||
expect(button.textContent).toBe(goBackCell.options.label); | ||
|
||
// Check the grid cell styles. | ||
expect(button.style["grid-column"]).toBe("2 / span 1"); | ||
expect(button.style["grid-row"]).toBe("3 / span 2"); | ||
|
||
// Check disabled state (should be enabled) | ||
expect(button.getAttribute("disabled")).toBe(null); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* | ||
* Copyright 2023 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 } from "preact"; | ||
import { html } from "htm/preact"; | ||
import { BlissSymbolCellType } from "./index.d"; | ||
import { adaptivePaletteGlobals, importPaletteFromJsonFile } from "./GlobalData"; | ||
import { Palette } from "./Palette"; | ||
import { BlissSymbol } from "./BlissSymbol"; | ||
import { speak } from "./GlobalUtils"; | ||
import "./ActionBmwCodeCell.scss"; | ||
|
||
// TODO: this is identical to `ActionBmwCodeCellPropsType`. Should it be? | ||
type CommandGoBackCellPropsType = { | ||
id: string, | ||
options: BlissSymbolCellType | ||
}; | ||
|
||
/* | ||
* Event handler for an CommandGoBackCellPropsType button/cell that, when clicked, | ||
* goes back one palette. | ||
*/ | ||
const goBackToPalette = async (event) => { | ||
const { paletteStore, navigationStack } = adaptivePaletteGlobals; | ||
const button = event.currentTarget; | ||
speak(button.innerText); | ||
|
||
const paletteToGoBackTo = navigationStack.peek(); | ||
const paletteDefinition = await paletteStore.getNamedPalette(paletteToGoBackTo.name, importPaletteFromJsonFile); | ||
if (paletteDefinition) { | ||
navigationStack.popAndSetCurrent(paletteDefinition); | ||
render ( | ||
html`<${Palette} json=${paletteDefinition}/>`, | ||
document.getElementById("mainPaletteDisplayArea") | ||
); | ||
} | ||
else { | ||
console.error(`goBackToPalette(): Unable to locate the palette definition for ${paletteToGoBackTo}`); | ||
} | ||
}; | ||
|
||
export function CommandGoBackCell (props: CommandGoBackCellPropsType) { | ||
|
||
const { | ||
columnStart, columnSpan, rowStart, rowSpan, bciAvId, label | ||
} = props.options; | ||
|
||
const gridStyles = ` | ||
grid-column: ${columnStart} / span ${columnSpan}; | ||
grid-row: ${rowStart} / span ${rowSpan}; | ||
`; | ||
|
||
return html` | ||
<button | ||
id="${props.id}" class="btn-command" style="${gridStyles}" | ||
onClick=${goBackToPalette}> | ||
<${BlissSymbol} bciAvId=${bciAvId} label=${label} /> | ||
</button> | ||
`; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,25 +22,34 @@ import { EncodingType } from "./index.d"; | |
* The map between cell types (string) and actual components that render corresponding cells | ||
*/ | ||
import { ActionBmwCodeCell } from "./ActionBmwCodeCell"; | ||
import { ActionBranchToPaletteCell } from "./ActionBranchToPaletteCell"; | ||
import { CommandGoBackCell } from "./CommandGoBackCell"; | ||
import { ContentBmwEncoding } from "./ContentBmwEncoding"; | ||
import { CommandClearEncoding } from "./CommandClearEncoding"; | ||
import { CommandDelLastEncoding } from "./CommandDelLastEncoding"; | ||
import { PaletteStore } from "./PaletteStore"; | ||
import { NavigationStack } from "./NavigationStack"; | ||
|
||
export const cellTypeRegistry = { | ||
"ActionBmwCodeCell": ActionBmwCodeCell, | ||
"ActionBranchToPaletteCell": ActionBranchToPaletteCell, | ||
"CommandGoBackCell": CommandGoBackCell, | ||
"ContentBmwEncoding": ContentBmwEncoding, | ||
"CommandClearEncoding": CommandClearEncoding, | ||
"CommandDelLastEncoding": CommandDelLastEncoding | ||
}; | ||
|
||
/** | ||
* Load the map between the BCI-AV IDs and the code consumed by the Bliss SVG builder | ||
* Load the map between the BCI-AV IDs and the code consumed by the Bliss SVG | ||
* and create the PaletterStore and NavigationStack objects. | ||
*/ | ||
export const adaptivePaletteGlobals = { | ||
// The map between the BCI-AV IDs and the code consumed by the Bliss SVG | ||
// builder. The map itself is set asynchronously. | ||
blissaryIdMapUrl: "https://raw.githubusercontent.com/hlridge/Bliss-Blissary-BCI-ID-Map/main/blissary_to_bci_mapping.json", | ||
blissaryIdMap: null | ||
blissaryIdMap: null, | ||
paletteStore: new PaletteStore(), | ||
navigationStack: new NavigationStack() | ||
}; | ||
|
||
export async function loadBlissaryIdMap () { | ||
|
@@ -52,6 +61,28 @@ export async function initAdaptivePaletteGlobals () { | |
adaptivePaletteGlobals.blissaryIdMap = await loadBlissaryIdMap(); | ||
} | ||
|
||
/** | ||
* Import a palette from the given json file using dynamic `import()`. | ||
* | ||
* Note: There are restrictions regarding the arguments to `import()`: | ||
* - the path must start with "./" or "../" and not be part of the argument, | ||
* - the path must end with "/" and not be part of the argument, | ||
* - the file name extension must be added here (not part of the argument) | ||
* See the following for more information: | ||
* https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars#limitations | ||
* | ||
* @param {String} jsonFile - Name of the JSON file to load, without the | ||
* ".json" extension (added herein). | ||
* @param {String} jsonFile - Path to the file to without any leading nor | ||
* trailing "/". | ||
* @return {JsonPaletteType} - The palette itself, or `null` if it could not be | ||
* loaded. | ||
*/ | ||
export async function importPaletteFromJsonFile (jsonFile: string, path: string) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would this function better belong to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that makes sense. I moved it there. |
||
const paletteJson = await import(`./${path}/${jsonFile}.json`); | ||
return paletteJson; | ||
} | ||
|
||
/** | ||
* Palette shared states | ||
*/ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to also test the click event of the
ActionBranchToPaletteCell
that leads to the rendering of the palette at the next layer.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, and I decided that the best place to do that is in
Palette.integration.test
since the go-to button affects other areas of the screen and involves other objects.