diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c27cb8..5a9237e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Fixed a bug where the `Download JSON` button would not download the JSON currently inside the input box. - Made sure file input would reset when file input dialog was closed. - Fixed a bug where the styles in `DisplaySettings.roughjs_config` were not applied. +- Fixed a bug where passing an empty array as objects will crash the program. +- Fixed a bug where the text may go outside of the box when it has a text font set ### 📚 Documentation and demo website changes diff --git a/memory-viz/src/automate.ts b/memory-viz/src/automate.ts index 9c5ec2b..cd182c7 100644 --- a/memory-viz/src/automate.ts +++ b/memory-viz/src/automate.ts @@ -252,11 +252,19 @@ function drawAutomatedOtherItems( x_coord = hor_reach; } - const right_most_obj = objs.reduce((prev, curr) => - compareByRightness(prev, curr) <= 0 ? prev : curr + const defaultObject: DrawnEntity = { + x: 0, + y: 0, + width: 0, + height: 0, + }; + const right_most_obj = objs.reduce( + (prev, curr) => (compareByRightness(prev, curr) <= 0 ? prev : curr), + defaultObject ); - const down_most_obj = objs.reduce((prev, curr) => - compareByBottomness(prev, curr) <= 0 ? prev : curr + const down_most_obj = objs.reduce( + (prev, curr) => (compareByBottomness(prev, curr) <= 0 ? prev : curr), + defaultObject ); const canvas_width = diff --git a/memory-viz/src/memory_model.ts b/memory-viz/src/memory_model.ts index d5fba9c..a36d9e8 100644 --- a/memory-viz/src/memory_model.ts +++ b/memory-viz/src/memory_model.ts @@ -223,9 +223,12 @@ export class MemoryModel { value: Primitive, style: Style ): Rect { + const renderedText = + typeof value === "string" ? `"${value}"` : String(value); let box_width = Math.max( this.obj_min_width, - this.getTextLength(String(value)) + this.obj_x_padding + this.getTextLength(renderedText, style.text_value) + + this.obj_x_padding ); this.drawRect( x, @@ -303,12 +306,12 @@ export class MemoryModel { ) { let id_box = Math.max( this.prop_min_width, - this.getTextLength(`id${id}`) + 10 + this.getTextLength(`id${id}`, style.text_id) + 10 ); let type_box = Math.max( this.prop_min_width, - this.getTextLength(type) + 10 + this.getTextLength(type, style.text_type) + 10 ); this.drawRect(x, y, id_box, this.prop_min_height, style.box_id); @@ -380,7 +383,8 @@ export class MemoryModel { element_ids.forEach((v) => { box_width += Math.max( this.item_min_width, - this.getTextLength(v === null ? "" : `id${v}`) + 10 + this.getTextLength(v === null ? "" : `id${v}`, style.text_id) + + 10 ); }); @@ -419,7 +423,7 @@ export class MemoryModel { const idv = v === null ? "" : `id${v}`; const item_length = Math.max( this.item_min_width, - this.getTextLength(idv) + 10 + this.getTextLength(idv, style.text_id) + 10 ); this.drawRect(curr_x, item_y, item_length, this.item_min_height); this.drawText( @@ -482,7 +486,8 @@ export class MemoryModel { element_ids.forEach((v) => { box_width += Math.max( this.item_min_width, - this.getTextLength(v === null ? "" : `id${v}`) + 10 + this.getTextLength(v === null ? "" : `id${v}`, style.text_id) + + 10 ); }); box_width = Math.max(this.obj_min_width, box_width); @@ -518,7 +523,7 @@ export class MemoryModel { const idv = v === null ? "" : `id${v}`; const item_length = Math.max( this.item_min_width, - this.getTextLength(idv) + 10 + this.getTextLength(idv, style.text_id) + 10 ); this.drawRect(curr_x, item_y, item_length, this.item_min_height); this.drawText( @@ -587,11 +592,11 @@ export class MemoryModel { let key_box = Math.max( this.item_min_width, - this.getTextLength(idk + 5) + this.getTextLength(idk + 5, style.text_id) ); let value_box = Math.max( this.item_min_width, - this.getTextLength(idv + 5) + this.getTextLength(idv + 5, style.text_value) ); // Draw the rectangles representing the keys. @@ -632,7 +637,7 @@ export class MemoryModel { let value_box = Math.max( this.item_min_width, - this.getTextLength(idv + 5) + this.getTextLength(idv + 5, style.text_value) ); // Draw the rectangle for values. @@ -692,14 +697,17 @@ export class MemoryModel { let box_width = this.obj_min_width; let longest = 0; for (const attribute in attributes) { - longest = Math.max(longest, this.getTextLength(attribute)); + longest = Math.max( + longest, + this.getTextLength(attribute, style.text_value) + ); } if (longest > 0) { box_width = longest + this.item_min_width * 3; } box_width = Math.max( box_width, - this.prop_min_width + this.getTextLength(name) + 10 + this.prop_min_width + this.getTextLength(name, style.text_type) + 10 ); let box_height = 0; @@ -750,7 +758,7 @@ export class MemoryModel { } if (stack_frame) { - let text_length = this.getTextLength(name); + let text_length = this.getTextLength(name, style.text_type); this.drawRect( x, y, @@ -846,8 +854,13 @@ export class MemoryModel { /** * Return the length of this text. * @param s - The given text. + * @param textStyle - The style configuration for the text. */ - getTextLength(s: string): number { + getTextLength(s: string, textStyle?: CSS.PropertiesHyphen): number { + if (textStyle?.["font-size"]) { + // Note: this assumes font size is in px + return s.length * parseInt(textStyle["font-size"]) * 0.6; + } return s.length * 12; } diff --git a/memory-viz/src/tests/__snapshots__/cli.spec.tsx.snap b/memory-viz/src/tests/__snapshots__/cli.spec.tsx.snap index eee4b4e..5289bae 100644 --- a/memory-viz/src/tests/__snapshots__/cli.spec.tsx.snap +++ b/memory-viz/src/tests/__snapshots__/cli.spec.tsx.snap @@ -43,18 +43,18 @@ exports[`memory-viz CLI output path should overwrite existing svg when the outpu } - - + + - - "David is cool!" + + "David is cool!" - + - - id19str + + id19str `; @@ -101,18 +101,18 @@ exports[`memory-viz CLI output path should produce consistent svg when the outpu } - - + + - - "David is cool!" + + "David is cool!" - + - - id19str + + id19str `; @@ -159,18 +159,18 @@ exports[`memory-viz CLI output path should produce consistent svg when the outpu } - - + + - - "David is cool!" + + "David is cool!" - + - - id19str + + id19str `; @@ -217,21 +217,21 @@ exports[`memory-viz cli produces consistent svg when provided filepath and a var } - - + + - - - "David is cool!" + + + "David is cool!" - - + + - - - id19str + + + id19str `; @@ -278,18 +278,18 @@ exports[`memory-viz cli produces consistent svg when provided filepath and outpu } - - + + - - "David is cool!" + + "David is cool!" - + - - id19str + + id19str `; @@ -336,18 +336,18 @@ exports[`memory-viz cli produces consistent svg when provided filepath and stdou } - - + + - - "David is cool!" + + "David is cool!" - + - - id19str + + id19str `; @@ -394,18 +394,18 @@ exports[`memory-viz cli produces consistent svg when provided filepath, output, } - - + + - - "David is cool!" + + "David is cool!" - + - - id19str + + id19str `; @@ -452,18 +452,18 @@ exports[`memory-viz cli produces consistent svg when provided filepath, output, } - - + + - - "David is cool!" + + "David is cool!" - + - - id19str + + id19str `; @@ -510,18 +510,18 @@ exports[`memory-viz cli produces consistent svg when provided filepath, output, } - - + + - - "David is cool!" + + "David is cool!" - + - - id19str + + id19str `; @@ -568,18 +568,18 @@ exports[`memory-viz cli produces consistent svg when provided stdin and output 1 } - - + + - - "David is cool!" + + "David is cool!" - + - - id19str + + id19str `; @@ -626,17 +626,17 @@ exports[`memory-viz cli produces consistent svg when provided stdin and stdout 1 } - - + + - - "David is cool!" + + "David is cool!" - + - - id19str + + id19str `; diff --git a/memory-viz/src/tests/__snapshots__/draw.spec.tsx.snap b/memory-viz/src/tests/__snapshots__/draw.spec.tsx.snap index e8547af..840e8a5 100644 --- a/memory-viz/src/tests/__snapshots__/draw.spec.tsx.snap +++ b/memory-viz/src/tests/__snapshots__/draw.spec.tsx.snap @@ -521,18 +521,18 @@ exports[`draw function renders 'hide_container' style preset 1`] = ` __main__ - - + + - - "hide container!" + + "hide container!" - - id42str + + id42str `; @@ -646,29 +646,29 @@ exports[`draw function renders 'highlight' style preset 1`] = ` } - - + + - - itemid45 + + itemid45 - - - __main__ + + + __main__ - - + + - - "highlight!" + + "highlight!" - + - - id42str + + id42str `; @@ -724,18 +724,18 @@ exports[`draw function renders 'highlight_id' style preset 1`] = ` __main__ - + - - "highlight id!" + + "highlight id!" - - + + - - id42str + + id42str `; @@ -788,21 +788,21 @@ exports[`draw function renders 'highlight_type' style preset 1`] = ` itemid45 - - __main__ + + __main__ - + - - "highlight type!" + + "highlight type!" - - - id42str + + + id42str `; @@ -1032,7 +1032,7 @@ exports[`draw function renders a bool using manual layout 1`] = ` `; exports[`draw function renders a diagram with 'small' width value and a mix stack frame/non-stack frame objects 1`] = ` - + `; exports[`draw function renders a diagram with 'small' width value and no stack frames 1`] = ` - + - - + + - - "David is cool!" + + "David is cool!" - + - - id19str + + id19str `; exports[`draw function renders a diagram with large left margins 1`] = ` - + `; @@ -1907,6 +1907,121 @@ exports[`draw function renders a tuple without indexes showing 1`] = ` `; +exports[`draw function renders an appropriately sized box for a string with the highlight style 1`] = ` + + + + + + + + + "I am a very very very very very very very very very very very very very very very very very very very very very very very very very very very long string" + + + + + + id1str + +`; + +exports[`draw function renders an appropriately sized box for a very long string 1`] = ` + + + + + + + + "I am a very very very very very very very very very very very very very very very very very very very very very very very very very very very long string" + + + + + + id1str + +`; + exports[`draw function renders an empty dict 1`] = ` `; +exports[`draw function renders an empty svg given an empty array 1`] = ` + + + +`; + exports[`draw function renders an empty tuple 1`] = ` - - + + - - itemid45 + + itemid45 - - - __main__ + + + __main__ - + - - "combination!" + + "combination!" - - + + - - - id42str + + + id42str `; @@ -2422,19 +2582,19 @@ exports[`draw function renders custom style (without presets) 1`] = ` } - - + + - - "David is cool!" + + "David is cool!" - - id19str + + id19str `; @@ -2796,29 +2956,29 @@ exports[`draw function should produce consistent svg when provided seed 1`] = ` __main__ - - + + - - "David is cool!" + + "David is cool!" - + - - id19str + + id19str - + - - 7 + + 7 - + - - id13int + + id13int `; diff --git a/memory-viz/src/tests/draw.spec.tsx b/memory-viz/src/tests/draw.spec.tsx index c907e5b..fe15628 100644 --- a/memory-viz/src/tests/draw.spec.tsx +++ b/memory-viz/src/tests/draw.spec.tsx @@ -786,7 +786,7 @@ describe("draw function", () => { const message = new RegExp( "^WARNING: provided width \\(\\d+\\) is smaller than " + - "the required width \\(\\d+\\). The provided width has been overwritten " + + "the required width \\(\\d+(\\.\\d+)?\\). The provided width has been overwritten " + "in the generated diagram.$" ); expect(message.test(spy.mock.calls[0][0])).toBe(true); @@ -907,4 +907,57 @@ describe("draw function", () => { }) ).toThrow(errorMessage); }); + + it("renders an appropriately sized box for a very long string", () => { + const objects: DrawnEntity[] = [ + { + id: 1, + type: "str", + value: "I am a very very very very very very very very very very very very very very very very very very very very very very very very very very very long string", + }, + ]; + const output: InstanceType = draw(objects, true, { + width: 100, + roughjs_config: { + options: { + seed: 12345, + }, + }, + }); + const svg = output.serializeSVG(); + expect(svg).toMatchSnapshot(); + }); + it("renders an appropriately sized box for a string with the highlight style", () => { + const objects: DrawnEntity[] = [ + { + id: 1, + type: "str", + value: "I am a very very very very very very very very very very very very very very very very very very very very very very very very very very very long string", + style: ["highlight"], + }, + ]; + const output: InstanceType = draw(objects, true, { + width: 100, + roughjs_config: { + options: { + seed: 12345, + }, + }, + }); + const svg = output.serializeSVG(); + expect(svg).toMatchSnapshot(); + }); + it("renders an empty svg given an empty array", () => { + const objects = []; + const output: InstanceType = draw(objects, true, { + width: 100, + roughjs_config: { + options: { + seed: 12345, + }, + }, + }); + const svg = output.serializeSVG(); + expect(svg).toMatchSnapshot(); + }); });