Skip to content

[Image] | (UX) | Show image in explore modal even when size is undefined#3367

Merged
nishasy merged 9 commits intomainfrom
image-no-size-explore-modal
Mar 18, 2026
Merged

[Image] | (UX) | Show image in explore modal even when size is undefined#3367
nishasy merged 9 commits intomainfrom
image-no-size-explore-modal

Conversation

@nishasy
Copy link
Copy Markdown
Contributor

@nishasy nishasy commented Mar 16, 2026

Summary:

We have a number of images in our content that are saved without a size.
If the image has a long description, it makes the "explore image" button available.
If the "explore image" button is clicked and then image has no size, the modal
shows up as completely empty.

The image should show up in the modal even if there is no size. Updating that here.

Issue: https://khanacademy.atlassian.net/browse/LEMS-3990

Test plan:

pnpm jest packages/perseus/src/widgets/image/components/explore-image-modal.test.tsx

Storybook

  • /?path=/story/widgets-image-visual-regression-tests-interactions--long-description-clicked-state-with-no-size
  • /?path=/story/widgets-image-visual-regression-tests-interactions--long-description-clicked-state-with-no-size-large-image
  • /?path=/story/widgets-image-widget-demo--large-image-with-no-size-saved
  • /?path=/story/widgets-image-widget-demo--large-image-with-no-size-saved-scale-flag
Before After
Screenshot 2026-03-16 at 4 27 55 PM Screenshot 2026-03-16 at 4 28 12 PM
Screenshot 2026-03-16 at 4 28 00 PM Screenshot 2026-03-16 at 4 28 18 PM
Before - scale feature flag after - scale feature flag
Screenshot 2026-03-16 at 4 29 58 PM Screenshot 2026-03-16 at 4 30 12 PM

@nishasy nishasy self-assigned this Mar 16, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 16, 2026

🗄️ Schema Change: No Changes ✅

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 16, 2026

Size Change: +11 B (0%)

Total Size: 486 kB

Filename Size Change
packages/perseus/dist/es/index.js 188 kB +11 B (+0.01%)
ℹ️ View Unchanged
Filename Size
packages/kas/dist/es/index.js 20.5 kB
packages/keypad-context/dist/es/index.js 1 kB
packages/kmath/dist/es/index.js 5.96 kB
packages/math-input/dist/es/index.js 98.5 kB
packages/math-input/dist/es/strings.js 1.61 kB
packages/perseus-core/dist/es/index.item-splitting.js 11.8 kB
packages/perseus-core/dist/es/index.js 24.9 kB
packages/perseus-editor/dist/es/index.js 100 kB
packages/perseus-linter/dist/es/index.js 8.82 kB
packages/perseus-score/dist/es/index.js 9.4 kB
packages/perseus-utils/dist/es/index.js 403 B
packages/perseus/dist/es/strings.js 7.47 kB
packages/pure-markdown/dist/es/index.js 1.39 kB
packages/simple-markdown/dist/es/index.js 6.71 kB

compressed-size-action

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 16, 2026

🛠️ Item Splitting: No Changes ✅

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 16, 2026

npm Snapshot: Published

Good news!! We've packaged up the latest commit from this PR (dfbab76) and published it to npm. You
can install it using the tag PR3367.

Example:

pnpm add @khanacademy/perseus@PR3367

If you are working in Khan Academy's frontend, you can run the below command.

./dev/tools/bump_perseus_version.ts -t PR3367

If you are working in Khan Academy's webapp, you can run the below command.

./dev/tools/bump_perseus_version.js -t PR3367

// bgWidth / bgHeight = X / modalImageHeight
// => X = (bgWidth / bgHeight) * modalImageHeight
width =
(backgroundImage.width / backgroundImage.height) * modalImageHeight;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered removing this block entirely, since the sizing just works with undefined now. However, I opted to keep this block here since it will be useful for the work in my other branch on scaled graphie labels.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file has a lot of changes because of the boilerplate code that had to be updated to allow the image to actually load for testing.

@nishasy nishasy requested review from a team and mark-fitzgerald March 16, 2026 23:31
!backgroundImage.height ||
!backgroundImage.width ||
!backgroundImage.url
) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The culprit

background-color: var(--wb-semanticColor-core-background-base-default);
/* Stop large images from overflowing out of the image area of the modal. */
max-width: 100%;
max-height: var(--modal-panel-height);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Large images would overflow and cover the long description without this CSS.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This CSS looks appropriate. However, I would prefer some changes to how the CSS variables are handled. I realize that this may be beyond the intent of this PR, but these changes will help to avoid potential conflicts.

  1. Namespace the CSS variables
    • Instead of --modal-panel-height, I recommend using --perseus-image-modal-panel-height
    • This will prevent naming conflicts with other parts of the application that have a modal panel height variable of the same name.
  2. Attach the CSS variables to .modal-container instead of :root
    • Even if variables are namespaced, adding them to the :root element overloads that element with variables that don't apply anywhere else other than the image widget.
    • If the variables are NOT namespaced, then this runs the risk of overriding the variable if it exists anywhere else in frontend, or of being overridden by a setting elsewhere in frontend.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@nishasy nishasy marked this pull request as ready for review March 16, 2026 23:32
@claude
Copy link
Copy Markdown

claude bot commented Mar 16, 2026

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review.

@nishasy
Copy link
Copy Markdown
Contributor Author

nishasy commented Mar 16, 2026

Tested it in my local server with a PR tag using the exercise that I first noticed this issue in:

Before After
Screenshot 2026-03-16 at 4 40 42 PM Screenshot 2026-03-16 at 4 40 36 PM

display: flex;
width: 100%;
justify-content: center;
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mark-fitzgerald Is it okay for .modal-container to be defined in two spots like this to separate out where the variables are defined?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically it is OK, but I find it confusing. Is there a reason that it is split up?

Copy link
Copy Markdown
Contributor Author

@nishasy nishasy Mar 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just split up so the variables can be at the top, and the styles can be further down with the other modal styles. Maybe I should move the modal styles to their own file so that the modal container can be at the top with the variables AND the style?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no requirement for the variables to be at the top of the file. It's not like JavaScript.

@nishasy
Copy link
Copy Markdown
Contributor Author

nishasy commented Mar 17, 2026

@claude review

Comment on lines +52 to +58
let modalImageHeight: number | undefined = Math.min(
MODAL_HEIGHT,
zoomHeight,
);
// bgWidth / bgHeight = X / modalImageHeight
// => X = (bgWidth / bgHeight) * modalImageHeight
let width = (zoomWidth / zoomHeight) * modalImageHeight;
let width: number | undefined = (zoomWidth / zoomHeight) * modalImageHeight;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 When the scale feature flag is OFF (the default path) and the image has no saved dimensions, zoomSize is [0, 0], producing modalImageHeight = 0 and width = NaN (from 0/0 * 0) at lines 52-58. These values make the image invisible in the modal. The scaleFF path handles this correctly (lines 72-77 fall back to undefined), but the non-scaleFF path needs the same guard -- e.g. set width and modalImageHeight to undefined when zoomHeight is 0.

Extended reasoning...

What the bug is

In explore-image-modal-content.tsx, lines 52-58 compute the modal image dimensions for the non-scaleFF (legacy/default) path:

let modalImageHeight = Math.min(MODAL_HEIGHT, zoomHeight);  // Math.min(568, 0) = 0
let width = (zoomWidth / zoomHeight) * modalImageHeight;     // (0 / 0) * 0 = NaN

When the image has no saved width/height, zoomSize is initialized to [0, 0] in image.tsx:42-44 (backgroundImage.width || 0, backgroundImage.height || 0). This means zoomHeight = 0 and zoomWidth = 0, yielding modalImageHeight = 0 and width = NaN.

Why this PR introduced it

Before this PR, there was an early return guard that checked !backgroundImage.height || !backgroundImage.width || !backgroundImage.url. If the image had no saved dimensions, the component returned null and never reached the dimension calculation. This PR correctly removed the height/width checks from the guard (to allow images without saved dimensions to display), and the scaleFF path was updated with a proper fallback (lines 72-77 set width = undefined and modalImageHeight = undefined when dimensions are missing). However, the non-scaleFF path at lines 52-58 was not given the same fallback treatment.

Step-by-step proof

  1. Content has an image with no saved width/height but has a url and longDescription.
  2. image.tsx:42-44 initializes zoomSize to [0, 0].
  3. useEffect at line 68 fires getImageSizeModern() asynchronously.
  4. Before the async resolution completes, or if getImageSizeModern rejects (note: there is no .catch() handler at line 75), zoomSize remains [0, 0].
  5. If the user opens the Explore Image modal during this window, ExploreImageModalContent receives zoomSize = [0, 0].
  6. Line 52: modalImageHeight = Math.min(568, 0) = 0.
  7. Line 58: width = (0 / 0) * 0 = NaN.
  8. These are passed to SvgImage as height={0} and width={NaN}, resulting in an invisible image.

Practical impact and addressing the refutation

One verifier refuted this bug, arguing the scenario cannot occur because getImageSizeModern resolves before the user can click. This is largely correct for the happy path: the image is typically cached from the initial display, so async resolution is near-instant. However, there are two edge cases: (1) if the image load is slow or fails, there is no .catch() on the promise at image.tsx:75, so zoomSize stays [0, 0] permanently; (2) it is a transient state that exists between initial render and async resolution. Additionally, the non-scaleFF path is legacy code marked for removal (TODO LEMS-3912), further reducing the practical impact.

How to fix

Add the same fallback used in the scaleFF path. Before the if (scaleFF) block, add a guard:

if (!zoomHeight || !zoomWidth) {
    width = undefined;
    modalImageHeight = undefined;
}

This lets the CSS max-width/max-height rules (newly added in this PR) handle the sizing naturally, consistent with the scaleFF approach.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In image.tsx, the zoomSize is determined in a useEffect on load and then passed down into ImageInfoArea => ExploreImageModal => ExploreImageModalContent. So it would never actually be 0 in practice.

Copy link
Copy Markdown
Contributor Author

@nishasy nishasy Mar 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plus I'd prefer to avoid changing the non-flag code for now. We haven't had issues in production code, so there's no need to update this yet.

expect(pauseButton).not.toBeInTheDocument();
});

it("should have null content if there is no background image URL with scale flag enabled", () => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar with this state. Could you explain a little more on the case where we would expect this? This seems like a state we should ward against.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's actually possible to ever reach this case.

Copy link
Copy Markdown
Member

@catandthemachines catandthemachines left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved, thank you!

@nishasy nishasy merged commit 54eee35 into main Mar 18, 2026
12 checks passed
@nishasy nishasy deleted the image-no-size-explore-modal branch March 18, 2026 19:56
ivyolamit added a commit that referenced this pull request Mar 24, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @khanacademy/perseus-editor@30.0.0

### Major Changes

- [#3332](#3332)
[`604b3a6c25`](604b3a6)
Thanks [@benchristel](https://github.com/benchristel)! - The `options`
parameter of the `serialize` method of `EditorPage` and `Editor` has
been removed.


- [#3386](#3386)
[`7e76fbbc2f`](7e76fbb)
Thanks [@benchristel](https://github.com/benchristel)! - The `serialize`
methods of classes in `@khanacademy/perseus-editor` no longer use arrow
function syntax. Callers should not unbind them from the class instance.

Additionally, the `Editor` component no longer accepts a `replace` prop
(used for hints), and its serialize method no longer returns `replace`.
The `replace` prop was only used in `serialize`. Users of the `Editor`
component should manage hints' `replace` setting themselves.

### Minor Changes

- [#3395](#3395)
[`97223334ea`](9722333)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Implementation of Editor support for Exponential Graph


- [#3352](#3352)
[`b681e00a4f`](b681e00)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add editor support
for AbsoluteValue


- [#3348](#3348)
[`b1557c2a73`](b1557c2)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add schema for
AbsoluteValue graph


- [#3345](#3345)
[`dde985f3b5`](dde985f)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add tangent type
definitions, this is the initial implementation for supporting Tangent
graph in Interactive Graph


- [#3358](#3358)
[`8c503171b1`](8c50317)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add tangent graph
option in the Interactive Graph Editor


- [#3376](#3376)
[`8aa0a77886`](8aa0a77)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Creation of new Types, Schema, and Kmath utilities for Exponential Graph

### Patch Changes

- [#3396](#3396)
[`35fa9133db`](35fa913)
Thanks [@nishasy](https://github.com/nishasy)! - [Image] | (CX) | Add a
linter warning for images with no size


- [#3390](#3390)
[`d22c50dc2a`](d22c50d)
Thanks [@nishasy](https://github.com/nishasy)! - [Image] | (CX) | Make
the 125 character alt text warning less aggressive


- [#3372](#3372)
[`3cdb09813d`](3cdb098)
Thanks [@nishasy](https://github.com/nishasy)! - [Image] | (UX) |
Upscale Graphies within Explore Image Modal


- [#3391](#3391)
[`2f285ee161`](2f285ee)
Thanks [@nishasy](https://github.com/nishasy)! - [Image] | (CX) | Add
character counter to alt text field


- [#3374](#3374)
[`cd73c99ba3`](cd73c99)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Remove incorrect
usage of the feature flag setting in one of the test

- Updated dependencies
\[[`f18c0d9b6f`](f18c0d9),
[`a022e751d6`](a022e75),
[`35fa9133db`](35fa913),
[`54db3fd4bd`](54db3fd),
[`97223334ea`](9722333),
[`027a5edbda`](027a5ed),
[`ae0538d0a7`](ae0538d),
[`005e13d784`](005e13d),
[`3cdb09813d`](3cdb098),
[`afcff9f96f`](afcff9f),
[`75f184e5a7`](75f184e),
[`4b2a7c85db`](4b2a7c8),
[`5e1acd01f8`](5e1acd0),
[`b681e00a4f`](b681e00),
[`d99f1c0259`](d99f1c0),
[`54eee35d65`](54eee35),
[`b1557c2a73`](b1557c2),
[`dde985f3b5`](dde985f),
[`56e7dbe9a2`](56e7dbe),
[`85f9cd46fc`](85f9cd4),
[`8c503171b1`](8c50317),
[`3aca3dcdf4`](3aca3dc),
[`9f29bc7161`](9f29bc7),
[`7034844845`](7034844),
[`8aa0a77886`](8aa0a77),
[`003aca7612`](003aca7)]:
    -   @khanacademy/perseus-linter@4.9.0
    -   @khanacademy/perseus-score@8.4.0
    -   @khanacademy/perseus-core@23.7.0
    -   @khanacademy/perseus@76.1.0
    -   @khanacademy/kmath@2.3.0
    -   @khanacademy/keypad-context@3.2.40
    -   @khanacademy/math-input@26.4.10

## @khanacademy/kmath@2.3.0

### Minor Changes

- [#3351](#3351)
[`005e13d784`](005e13d)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add scoring for
AbsoluteValue


- [#3347](#3347)
[`d99f1c0259`](d99f1c0)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add the tangent
math utilities to kmath for supporting Tangent graph in Interactive
Graph


- [#3376](#3376)
[`8aa0a77886`](8aa0a77)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Creation of new Types, Schema, and Kmath utilities for Exponential Graph

### Patch Changes

- Updated dependencies
\[[`54db3fd4bd`](54db3fd),
[`ae0538d0a7`](ae0538d),
[`005e13d784`](005e13d),
[`b1557c2a73`](b1557c2),
[`dde985f3b5`](dde985f),
[`8aa0a77886`](8aa0a77)]:
    -   @khanacademy/perseus-core@23.7.0

## @khanacademy/perseus@76.1.0

### Minor Changes

- [#3350](#3350)
[`75f184e5a7`](75f184e)
Thanks [@handeyeco](https://github.com/handeyeco)! - Implement
AbsoluteValue rendering


- [#3354](#3354)
[`4b2a7c85db`](4b2a7c8)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Created the tangent
graph visual component, add Storybook coverage, SR strings, and equation
string for supporting Tangent graph in Interactive Graph


- [#3353](#3353)
[`5e1acd01f8`](5e1acd0)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add tangent graph
state management and reducer for supporting Tangent graph in Interactive
Graph


- [#3352](#3352)
[`b681e00a4f`](b681e00)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add editor support
for AbsoluteValue


- [#3348](#3348)
[`b1557c2a73`](b1557c2)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add schema for
AbsoluteValue graph


- [#3345](#3345)
[`dde985f3b5`](dde985f)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add tangent type
definitions, this is the initial implementation for supporting Tangent
graph in Interactive Graph


- [#3349](#3349)
[`56e7dbe9a2`](56e7dbe)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add state
management for AbsoluteValue


- [#3377](#3377)
[`85f9cd46fc`](85f9cd4)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Implementation of state management logic for new Exponential graph


- [#3358](#3358)
[`8c503171b1`](8c50317)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add tangent graph
option in the Interactive Graph Editor


- [#3393](#3393)
[`9f29bc7161`](9f29bc7)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Rendering logic for new Exponential Graph


- [#3376](#3376)
[`8aa0a77886`](8aa0a77)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Creation of new Types, Schema, and Kmath utilities for Exponential Graph

### Patch Changes

- [#3329](#3329)
[`027a5edbda`](027a5ed)
Thanks [@Myranae](https://github.com/Myranae)! - Fix image bug by
batching setState calls in setupGraphie


- [#3372](#3372)
[`3cdb09813d`](3cdb098)
Thanks [@nishasy](https://github.com/nishasy)! - [Image] | (UX) |
Upscale Graphies within Explore Image Modal


- [#3365](#3365)
[`afcff9f96f`](afcff9f)
Thanks [@jeremywiebe](https://github.com/jeremywiebe)! - Improve
ordering of Props type for `Renderer` component


- [#3367](#3367)
[`54eee35d65`](54eee35)
Thanks [@nishasy](https://github.com/nishasy)! - [Image] | (UX) | Show
image in explore modal even when size is undefined


- [#3407](#3407)
[`3aca3dcdf4`](3aca3dc)
Thanks [@Myranae](https://github.com/Myranae)! - Improve a11y with
graded group set


- [#3385](#3385)
[`003aca7612`](003aca7)
Thanks [@Myranae](https://github.com/Myranae)! - Small fix to prevent
pip duplication in Graded Group Sets

- Updated dependencies
\[[`f18c0d9b6f`](f18c0d9),
[`a022e751d6`](a022e75),
[`35fa9133db`](35fa913),
[`54db3fd4bd`](54db3fd),
[`97223334ea`](9722333),
[`ae0538d0a7`](ae0538d),
[`005e13d784`](005e13d),
[`d99f1c0259`](d99f1c0),
[`b1557c2a73`](b1557c2),
[`dde985f3b5`](dde985f),
[`7034844845`](7034844),
[`8aa0a77886`](8aa0a77)]:
    -   @khanacademy/perseus-linter@4.9.0
    -   @khanacademy/perseus-score@8.4.0
    -   @khanacademy/perseus-core@23.7.0
    -   @khanacademy/kmath@2.3.0
    -   @khanacademy/keypad-context@3.2.40
    -   @khanacademy/math-input@26.4.10

## @khanacademy/perseus-core@23.7.0

### Minor Changes

- [#3405](#3405)
[`54db3fd4bd`](54db3fd)
Thanks [@benchristel](https://github.com/benchristel)! -
`@khanacademy/perseus-core` now exports a
`removeOrphanedWidgetsFromPerseusItem` function, which removes
unreferenced widgets from a `PerseusItem`'s question and hints.


- [#3351](#3351)
[`005e13d784`](005e13d)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add scoring for
AbsoluteValue


- [#3348](#3348)
[`b1557c2a73`](b1557c2)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add schema for
AbsoluteValue graph


- [#3345](#3345)
[`dde985f3b5`](dde985f)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add tangent type
definitions, this is the initial implementation for supporting Tangent
graph in Interactive Graph


- [#3376](#3376)
[`8aa0a77886`](8aa0a77)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Creation of new Types, Schema, and Kmath utilities for Exponential Graph

### Patch Changes

- [#3357](#3357)
[`ae0538d0a7`](ae0538d)
Thanks [@jeremywiebe](https://github.com/jeremywiebe)! - Improve code
documentation for all data-schema and user-input types

## @khanacademy/perseus-linter@4.9.0

### Minor Changes

- [#3381](#3381)
[`f18c0d9b6f`](f18c0d9)
Thanks [@anakaren-rojas](https://github.com/anakaren-rojas)! - Adds new
linters for parsed objects


- [#3395](#3395)
[`97223334ea`](9722333)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Implementation of Editor support for Exponential Graph

### Patch Changes

- [#3396](#3396)
[`35fa9133db`](35fa913)
Thanks [@nishasy](https://github.com/nishasy)! - [Image] | (CX) | Add a
linter warning for images with no size

- Updated dependencies
\[[`54db3fd4bd`](54db3fd),
[`ae0538d0a7`](ae0538d),
[`005e13d784`](005e13d),
[`d99f1c0259`](d99f1c0),
[`b1557c2a73`](b1557c2),
[`dde985f3b5`](dde985f),
[`8aa0a77886`](8aa0a77)]:
    -   @khanacademy/perseus-core@23.7.0
    -   @khanacademy/kmath@2.3.0

## @khanacademy/perseus-score@8.4.0

### Minor Changes

- [#3356](#3356)
[`a022e751d6`](a022e75)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add tangent graph
scoring to support the Tangent graph in Interactive Graph


- [#3351](#3351)
[`005e13d784`](005e13d)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add scoring for
AbsoluteValue


- [#3394](#3394)
[`7034844845`](7034844)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Implementation of new scoring logic for Exponential Graph

### Patch Changes

- Updated dependencies
\[[`54db3fd4bd`](54db3fd),
[`ae0538d0a7`](ae0538d),
[`005e13d784`](005e13d),
[`d99f1c0259`](d99f1c0),
[`b1557c2a73`](b1557c2),
[`dde985f3b5`](dde985f),
[`8aa0a77886`](8aa0a77)]:
    -   @khanacademy/perseus-core@23.7.0
    -   @khanacademy/kmath@2.3.0

## @khanacademy/keypad-context@3.2.40

### Patch Changes

- Updated dependencies
\[[`54db3fd4bd`](54db3fd),
[`ae0538d0a7`](ae0538d),
[`005e13d784`](005e13d),
[`b1557c2a73`](b1557c2),
[`dde985f3b5`](dde985f),
[`8aa0a77886`](8aa0a77)]:
    -   @khanacademy/perseus-core@23.7.0

## @khanacademy/math-input@26.4.10

### Patch Changes

- Updated dependencies
\[[`54db3fd4bd`](54db3fd),
[`ae0538d0a7`](ae0538d),
[`005e13d784`](005e13d),
[`b1557c2a73`](b1557c2),
[`dde985f3b5`](dde985f),
[`8aa0a77886`](8aa0a77)]:
    -   @khanacademy/perseus-core@23.7.0
    -   @khanacademy/keypad-context@3.2.40
catandthemachines pushed a commit that referenced this pull request Apr 1, 2026
…ned (#3367)

## Summary:
We have a number of images in our content that are saved without a size.
If the image has a long description, it makes the "explore image" button available.
If the "explore image" button is clicked and then image has no size, the modal
shows up as completely empty.

The image should show up in the modal even if there is no size. Updating that here.

Issue: https://khanacademy.atlassian.net/browse/LEMS-3990

## Test plan:
`pnpm jest packages/perseus/src/widgets/image/components/explore-image-modal.test.tsx`

Storybook
- `/?path=/story/widgets-image-visual-regression-tests-interactions--long-description-clicked-state-with-no-size`
- `/?path=/story/widgets-image-visual-regression-tests-interactions--long-description-clicked-state-with-no-size-large-image`
- `/?path=/story/widgets-image-widget-demo--large-image-with-no-size-saved`
- `/?path=/story/widgets-image-widget-demo--large-image-with-no-size-saved-scale-flag`

| Before | After |
| --- | --- |
| <img width="1423" height="473" alt="Screenshot 2026-03-16 at 4 27 55 PM" src="https://github.com/user-attachments/assets/4044b4e4-6491-4fdc-b611-bd593b006051" /> | <img width="1427" height="761" alt="Screenshot 2026-03-16 at 4 28 12 PM" src="https://github.com/user-attachments/assets/1d5fd926-6818-4e90-ad9e-b1fb6e00b134" /> |
| <img width="1427" height="391" alt="Screenshot 2026-03-16 at 4 28 00 PM" src="https://github.com/user-attachments/assets/1bfb5f33-e05a-4c0b-9446-67a3657a3c40" /> | <img width="1430" height="762" alt="Screenshot 2026-03-16 at 4 28 18 PM" src="https://github.com/user-attachments/assets/1b4fb9f3-f1a2-4827-aefa-6a5221cef1f5" /> |
| Before - scale feature flag | after - scale feature flag |
| <img width="1278" height="248" alt="Screenshot 2026-03-16 at 4 29 58 PM" src="https://github.com/user-attachments/assets/e4f5e50c-56d5-46fc-a2e8-f21f8c8a1432" /> |  <img width="1281" height="755" alt="Screenshot 2026-03-16 at 4 30 12 PM" src="https://github.com/user-attachments/assets/58331eb2-7eeb-47d7-89f0-6e37ce31528d" /> |

Author: nishasy

Reviewers: nishasy, mark-fitzgerald, claude[bot], catandthemachines, ivyolamit

Required Reviewers:

Approved By: catandthemachines

Checks: ⚪️ 1 check is neutral, ⏭️  1 check has been skipped, ✅ 10 checks were successful

Pull Request URL: #3367
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants