-
Notifications
You must be signed in to change notification settings - Fork 2
Description
π§© Issue: Refactor GetImage to Dynamically Load Component Images Based on Naming Convention
π Goal
Refactor the GetImage module to eliminate the need for hardcoding each circuit component's image imports. Instead, the base ElementRenderer should dynamically resolve the appropriate image(s) for each subclass (e.g., ResistorRenderer, CapacitorRenderer) based on a consistent naming convention.
This change aims to:
- β Eliminate repetitive image imports.
- β Scale gracefully as new elements are added.
- β Centralize and standardize icon lookup logic.
- β
Let the renderer resolve variants (
hover,selected, etc.) without hardcoding.
π§ Context
Right now, we manually do:
import R from '../../../assets/R.png';
...
this.image.src = GetImage("R.png");We want to switch to:
this.image = this.loadImageVariant("default");Where loadImageVariant uses either the renderer class name (ResistorRenderer) or the element type ("resistor") to resolve the correct icon path.
This allows consistent handling of image variants and removes boilerplate from every renderer.
β Acceptance Criteria
- The GetImage utility dynamically resolves image paths using naming conventions.
- Renderers do not manually import or map image paths.
- ElementRenderer can initialize all icon variants: default, hover, selected, hover_selected.- - If an asset is missing, a descriptive error is thrown.
- The logic is fully tested using unit tests and follows a TDD approach.
π§ͺ Test-Driven Development Plan
Start by writing the following unit tests in tests/utils/getImagePath.test.js:
describe("getImagePath", () => {
it("resolves default image path from type name", () => {
expect(getImagePath("resistor")).toContain("R.png");
});
it("resolves hover image path correctly", () => {
expect(getImagePath("resistor", "hover")).toContain("R_hover.png");
});
it("throws if type is empty or unknown", () => {
expect(() => getImagePath("")).toThrow("Invalid or unknown type");
});
it("resolves all image variants for a valid type", () => {
const variants = ["default", "hover", "selected", "hover_selected"];
const paths = variants.map(v => getImagePath("junction", v));
expect(paths).toEqual(expect.arrayContaining([
expect.stringContaining("J.png"),
expect.stringContaining("J_hover.png"),
expect.stringContaining("J_selected.png"),
expect.stringContaining("J_hover_selected.png"),
]));
});
});βοΈ Suggested Refactor Strategy (pseudocode)
1. Create a utility to resolve base icon letter from type:
function getBaseImageNameFromType(type) {
return type.charAt(0).toUpperCase(); // "resistor" β "R"
}2. Create a getImagePath utility:
// utils/getImagePath.js
export function getImagePath(type, variant = "default") {
if (!type || typeof type !== "string") throw new Error("Invalid or unknown type");
const base = getBaseImageNameFromType(type);
const suffix = variant === "default" ? "" : `_${variant}`;
return new URL(`../../../assets/${base}${suffix}.png`, import.meta.url).href;
}3. In ElementRenderer:
initializeImageAssets(type) {
this.images = {};
const variants = ["default", "hover", "selected", "hover_selected"];
variants.forEach(variant => {
const img = new Image();
img.src = getImagePath(type, variant);
this.images[variant] = img;
img.onload = () => {
if (variant === "default") this.imageLoaded = true;
};
});
}4. In ResistorRenderer (and others):
constructor(context) {
super(context);
this.initializeImageAssets("resistor");
}π¬ Optional Phase 2 Tests
π¬ Optional Phase 2 Tests
- Ensure image objects are cached and reused if needed.
- Simulate image loading in the browser (e.g. with jest-canvas-mock).
- Test rendering fallback or missing icon behavior.
π§ Bonus Improvements
- Preload assets from a manifest or dynamically import via Vite/Webpack.
- Replace
Image()instances with a sharedImageCacheutility. - Use renderer class name inference (
this.constructor.name) to determine type automatically.
π§± Dependencies
None. This refactor introduces no new dependencies and is a modular upgrade to the current image system.
π§ Notes
This issue helps centralize and abstract rendering logic, aligning with our Hexagonal Architecture by improving adapter reusability and enforcing naming consistency.
Let me know if you'd like help scaffolding the initial test file or setting up an asset loader abstraction.