Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## [22.0.2] - 2026-10-13
### Added
- Added typed Plotly event payloads for click, hover, selection, relayout, and
restyle outputs, including standalone-template type-check coverage.
- Added standalone Angular and lazy-loading guidance.
- Expanded the Angular compatibility reference and documented the Angular 19
Plotly.js bundle workaround.

### Fixed
- Resize autosized plots after layout updates and observe responsive plot
container size changes when `[useResizeHandler]` is enabled.

## [22.0.1] - 2026-08-20
### Fixed
- Added `[innerStyle]` as the preferred plot container styling input while
Expand Down
33 changes: 21 additions & 12 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,24 +49,33 @@ package compatible.
ng test --watch=false
```

### Compatibility reference
The [README compatibility table](README.md#angular-compatibility) is the
single reference for matching `angular-plotly.js` and Angular versions.

| `angular-plotly.js` | Angular | Notes |
| --- | --- | --- |
| `2.x` | 9 | Not compatible with Angular 13 |
| `3.x` | 10 | Angular 10 library-format release |
| `5.x` | 13 | Use this line for Angular 13 |
| `6.x` | 16 | Angular 16 release |
### Plotly.js bundles and Angular 19

The package version does not increase for every Angular major. Check the
package peer dependencies before upgrading across an Angular major, and use
the package line that declares support for the Angular version in the
application.
`angular-plotly.js` does not install a Plotly.js bundle for an application.
The bundle is selected by the application and passed to `PlotlyModule.forRoot`
or `PlotlyService.setPlotly`. The library's own tested baseline is currently
`plotly.js-dist` 2.35.3.

If an Angular 19 test build fails to resolve the `d3-*` modules from
`plotly.js-dist-min` 3.x, pin the bundle to the known-good 2.x line while
investigating the application build configuration:

```bash
npm install plotly.js-dist-min@^2.35.3
```

The wrapper only requires a Plotly object with `newPlot`/`react`; the missing
module errors occur while Angular bundles Plotly.js, before the wrapper runs.
Use a small production build and a test build to validate a move to Plotly.js
3.x before changing the pin.

### Angular 13 follow-up notes

Version 5.0.0 migrated the library to the Ivy engine and removed Protractor
and Codelyzer from the library development setup. Review application code and
CI configuration for those Angular ecosystem changes as part of the Angular
13 migration. The Plotly component API is otherwise unchanged in the release
notes for 5.0.0.
notes for 5.0.0.
57 changes: 46 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,33 @@

---

## Angular compatibility

Since version 20, the angular-plotly.js major follows the supported Angular
major. Install the matching package line:

| angular-plotly.js | Angular | Branch | Support |
| `angular-plotly.js` | Angular | Branch | Support / notes |
| --- | --- | --- | --- |
| 22.x | 22.x | `master` | Active |
| 21.x | 21.x | `v21` | LTS |
| 20.x | 20.x | `v20` | LTS through 2026-11-28 |
| `2.x` | 9 | — | Historical; not compatible with Angular 13 |
| `3.x` | 10 | — | Historical Angular 10 library-format release |
| `4.x` | 11–12 | — | Historical; declares Angular greater than 10.0.9 |
| `5.x` | 13–15 | — | Historical Angular 13 compatibility line |
| `6.x` | 16–19 | — | Historical; declares Angular 16 or newer; validate before a major Angular upgrade |
| `20.x` | 20 | `v20` | LTS through 2026-11-28 |
| `21.x` | 21 | `v21` | LTS; peer dependencies are capped below Angular 22 |
| `22.x` | 22 | `master` | Active; peer dependencies are capped below Angular 23 |

Maintenance and release details are documented in [MAINTENANCE.md](MAINTENANCE.md).

If you want to use with Angular 8.x, please use version [`angular-plotly.js@1.x`](https://github.com/plotly/angular-plotly.js/tree/1.x).

For upgrade instructions and historical Angular/package compatibility, see
the [migration guide](MIGRATION.md).
For upgrade instructions, see the [migration guide](MIGRATION.md).


## Content

* [Installation](#installation)
* [Angular compatibility](#angular-compatibility)
* [Migration guide](MIGRATION.md)
* [Quick start](#quick-start)
* [API](#api-reference)
Expand Down Expand Up @@ -97,6 +104,34 @@ You should see a plot like this:
<img src="example.png" alt="Example plot" width="320" height="240">
</p>

### Standalone applications

`PlotlyComponent` is standalone. Configure the Plotly bundle once, then import
the component wherever a chart is rendered:

```typescript
import { Component } from '@angular/core';
import * as PlotlyJS from 'plotly.js-dist-min';
import { PlotlyComponent, PlotlyService } from 'angular-plotly.js';

PlotlyService.setPlotly(PlotlyJS);

@Component({
standalone: true,
imports: [PlotlyComponent],
template: '<plotly-plot [data]="data" [layout]="layout" />',
})
export class SalesChartComponent {
data = [{ x: [1, 2, 3], y: [2, 6, 3], type: 'scatter' }];
layout = { autosize: true };
}
```

To keep Plotly out of the initial bundle, put this setup in a component loaded
through a lazy route. The `plotly.js-dist-min` import then belongs to that lazy
chunk. Do not configure `PlotlyModule.forRoot()` in application-wide providers
when the chart itself is intended to be lazy-loaded.


For a full description of Plotly chart types and attributes see the following resources:

Expand Down Expand Up @@ -161,19 +196,19 @@ Event handlers for specific [`plotly.js` events](https://plot.ly/javascript/plot
| `(autoSize)` | `Function` | `plotly_autosize` | |
| `(beforeExport)` | `Function` | `plotly_beforeexport` | |
| `(buttonClicked)` | `Function` | `plotly_buttonclicked` | |
| `(plotlyClick)` | `Function` | `plotly_click` | [why not (click)?](FAQ.md#why-using-plotlyclick-instead-of-click) |
| `(plotlyClick)` | `Plotly.PlotMouseEvent` | `plotly_click` | [why not (click)?](FAQ.md#why-using-plotlyclick-instead-of-click) |
| `(clickAnnotation)` | `Function` | `plotly_clickannotation` | |
| `(deselect)` | `Function` | `plotly_deselect` | |
| `(doubleClick)` | `Function` | `plotly_doubleclick` | |
| `(framework)` | `Function` | `plotly_framework` | |
| `(hover)` | `Function` | `plotly_hover` | |
| `(hover)` | `Plotly.PlotMouseEvent` | `plotly_hover` | |
| `(legendClick)` | `Function` | `plotly_legendclick` | |
| `(legendDoubleClick)` | `Function` | `plotly_legenddoubleclick` | |
| `(react)` | `Function` | `plotly_react` | |
| `(relayout)` | `Function` | `plotly_relayout` | |
| `(restyle)` | `Function` | `plotly_restyle` | |
| `(relayout)` | `Plotly.PlotRelayoutEvent` | `plotly_relayout` | |
| `(restyle)` | `Plotly.PlotRestyleEvent` | `plotly_restyle` | |
| `(redraw)` | `Function` | `plotly_redraw` | |
| `(selected)` | `Function` | `plotly_selected` | |
| `(selected)` | `Plotly.PlotSelectionEvent` | `plotly_selected` | |
| `(selecting)` | `Function` | `plotly_selecting` | |
| `(sliderChange)` | `Function` | `plotly_sliderchange` | |
| `(sliderEnd)` | `Function` | `plotly_sliderend` | |
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "angular-plotly.js",
"version": "22.0.1",
"version": "22.0.2",
"license": "MIT",
"scripts": {
"ng": "ng",
Expand Down
2 changes: 1 addition & 1 deletion projects/plotly/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "angular-plotly.js",
"version": "22.0.1",
"version": "22.0.2",
"license": "MIT",
"peerDependencies": {
"@angular/common": ">=22.0.0 <23.0.0",
Expand Down
50 changes: 48 additions & 2 deletions projects/plotly/src/lib/plotly.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import { SimpleChange, ComponentRef } from '@angular/core';
import { Component, SimpleChange, ComponentRef } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { PlotlyComponent } from './plotly.component';
import { Plotly } from './plotly.interface';
import { PlotlyService } from './plotly.service';

import PlotlyJS from 'plotly.js-dist';


PlotlyService.setPlotly(PlotlyJS);

@Component({
standalone: true,
imports: [PlotlyComponent],
template: '<plotly-plot (plotlyClick)="capture($event)" />',
})
class TypedEventHostComponent {
capture(event: Plotly.PlotMouseEvent): void {
void event.points;
}
}

describe('PlotlyComponent', () => {
let component: PlotlyComponent;
let fixture: ComponentFixture<PlotlyComponent>;
Expand All @@ -19,7 +31,7 @@ describe('PlotlyComponent', () => {
windowSpy = jasmine.createSpyObj('Window', ['addEventListener', 'removeEventListener']);

TestBed.configureTestingModule({
imports: [PlotlyComponent],
imports: [PlotlyComponent, TypedEventHostComponent],
providers: [
PlotlyService,
],
Expand Down Expand Up @@ -198,6 +210,40 @@ describe('PlotlyComponent', () => {
expect(deprecatedClick).toHaveBeenCalledWith(event);
});

it('should type-check plotlyClick handlers in standalone templates', () => {
expect(TestBed.createComponent(TypedEventHostComponent).componentInstance).toBeTruthy();
});

it('should resize an autosized plot after a layout update', async () => {
const instance = document.createElement('div') as any;
component.plotlyInstance = instance;
componentRef.setInput('layout', { autosize: true });
spyOn(component.plotly, 'update').and.resolveTo(instance);
spyOn(component.plotly, 'resize');
spyOn(component, 'getWindow').and.returnValue({
requestAnimationFrame: (callback: FrameRequestCallback) => {
callback(0);
return 1;
},
} as Window);

await component.updatePlot();

expect(component.plotly.resize).toHaveBeenCalledWith(instance);
});

it('should not resize after a layout update with explicit dimensions', async () => {
const instance = document.createElement('div') as any;
component.plotlyInstance = instance;
componentRef.setInput('layout', { autosize: true, height: 400 });
spyOn(component.plotly, 'update').and.resolveTo(instance);
spyOn(component.plotly, 'resize');

await component.updatePlot();

expect(component.plotly.resize).not.toHaveBeenCalled();
});

it('should skip Plotly initialization on the server', () => {
const serverComponent = TestBed.runInInjectionContext(() => new PlotlyComponent(
component.plotly,
Expand Down
Loading
Loading