• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

plotly / angular-plotly.js / 526fb337-2826-4cfc-a4c1-48148f2b109d

pending completion
526fb337-2826-4cfc-a4c1-48148f2b109d

push

circleci

André Farzat
Updating ng build command in circleci [2]

61 of 71 branches covered (85.92%)

Branch coverage included in aggregate %.

194 of 222 relevant lines covered (87.39%)

15.65 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.6
/projects/plotly/src/lib/plotly.component.ts
1
/* eslint-disable @angular-eslint/no-conflicting-lifecycle */
2

3
import {
4
    Component,
5
    ElementRef,
6
    EventEmitter,
7
    Input,
8
    OnDestroy,
9
    OnChanges,
10
    OnInit,
11
    Output,
12
    SimpleChange,
13
    SimpleChanges,
14
    ViewChild,
15
    DoCheck,
16
    IterableDiffer,
17
    IterableDiffers,
18
    KeyValueDiffer,
19
    KeyValueDiffers,
20
} from '@angular/core';
21

22
import { PlotlyService } from './plotly.service';
23
import { Plotly } from './plotly.interface';
24

25
// @dynamic
26
@Component({
27
    selector: 'plotly-plot',
28
    template: `<div #plot [attr.id]="divId" [ngClass]="getClassName()" [ngStyle]="style">
29
      <ng-content></ng-content>
30
    </div>`,
31
    providers: [PlotlyService],
32
})
33
export class PlotlyComponent implements OnInit, OnChanges, OnDestroy, DoCheck {
1✔
34
    protected defaultClassName = 'js-plotly-plot';
12✔
35

36
    public plotlyInstance?: Plotly.PlotlyHTMLElement;
37
    public resizeHandler?: (instance: Plotly.PlotlyHTMLElement) => void;
38
    public layoutDiffer?: KeyValueDiffer<string, any>;
39
    public dataDiffer?: IterableDiffer<Plotly.Data>;
40

41
    @ViewChild('plot', { static: true }) plotEl!: ElementRef;
42

43
    @Input() data?: Plotly.Data[];
44
    @Input() layout?: Partial<Plotly.Layout>;
45
    @Input() config?: Partial<Plotly.Config>;
46
    @Input() frames?: Partial<Plotly.Config>[];
47
    @Input() style?: { [key: string]: string };
48

49
    @Input() divId?: string;
50
    @Input() revision = 0;
12✔
51
    @Input() className?: string | string[];
52
    @Input() debug = false;
12✔
53
    @Input() useResizeHandler = false;
12✔
54

55
    @Input() updateOnLayoutChange = true;
12✔
56
    @Input() updateOnDataChange = true;
12✔
57
    @Input() updateOnlyWithRevision = false;
12✔
58

59
    @Output() initialized = new EventEmitter<Plotly.Figure>();
12✔
60
    @Output() update = new EventEmitter<Plotly.Figure>();
12✔
61
    @Output() purge = new EventEmitter<Plotly.Figure>();
12✔
62
    // eslint-disable-next-line @angular-eslint/no-output-native
63
    @Output() error = new EventEmitter<Error>();
12✔
64

65
    @Output() afterExport = new EventEmitter();
12✔
66
    @Output() afterPlot = new EventEmitter();
12✔
67
    @Output() animated = new EventEmitter();
12✔
68
    @Output() animatingFrame = new EventEmitter();
12✔
69
    @Output() animationInterrupted = new EventEmitter();
12✔
70
    @Output() autoSize = new EventEmitter();
12✔
71
    @Output() beforeExport = new EventEmitter();
12✔
72
    @Output() buttonClicked = new EventEmitter();
12✔
73
    /**
74
     * @deprecated DEPRECATED: Reconsider using `(plotlyClick)` instead of `(click)` to avoid event conflict. Please check https://github.com/plotly/angular-plotly.js#FAQ
75
     */
76
    // eslint-disable-next-line @angular-eslint/no-output-native
77
    @Output() click = new EventEmitter();
12✔
78
    @Output() plotlyClick = new EventEmitter();
12✔
79
    @Output() clickAnnotation = new EventEmitter();
12✔
80
    @Output() deselect = new EventEmitter();
12✔
81
    @Output() doubleClick = new EventEmitter();
12✔
82
    @Output() framework = new EventEmitter();
12✔
83
    @Output() hover = new EventEmitter();
12✔
84
    @Output() legendClick = new EventEmitter();
12✔
85
    @Output() legendDoubleClick = new EventEmitter();
12✔
86
    @Output() react = new EventEmitter();
12✔
87
    @Output() relayout = new EventEmitter();
12✔
88
    @Output() restyle = new EventEmitter();
12✔
89
    @Output() redraw = new EventEmitter();
12✔
90
    @Output() selected = new EventEmitter();
12✔
91
    @Output() selecting = new EventEmitter();
12✔
92
    @Output() sliderChange = new EventEmitter();
12✔
93
    @Output() sliderEnd = new EventEmitter();
12✔
94
    @Output() sliderStart = new EventEmitter();
12✔
95
    @Output() transitioning = new EventEmitter();
12✔
96
    @Output() transitionInterrupted = new EventEmitter();
12✔
97
    @Output() unhover = new EventEmitter();
12✔
98
    @Output() relayouting = new EventEmitter();
12✔
99
    @Output() treemapclick = new EventEmitter();
12✔
100
    @Output() sunburstclick = new EventEmitter();
12✔
101

102
    public eventNames = ['afterExport', 'afterPlot', 'animated', 'animatingFrame', 'animationInterrupted', 'autoSize',
12✔
103
        'beforeExport', 'buttonClicked', 'clickAnnotation', 'deselect', 'doubleClick', 'framework', 'hover',
104
        'legendClick', 'legendDoubleClick', 'react', 'relayout', 'restyle', 'redraw', 'selected', 'selecting', 'sliderChange',
105
        'sliderEnd', 'sliderStart', 'transitioning', 'transitionInterrupted', 'unhover', 'relayouting', 'treemapclick',
106
        'sunburstclick'];
107

108
    constructor(
109
        public plotly: PlotlyService,
12✔
110
        public iterableDiffers: IterableDiffers,
12✔
111
        public keyValueDiffers: KeyValueDiffers,
12✔
112
    ) { }
113

114
    ngOnInit(): void {
115
        this.createPlot().then(() => {
12✔
116
            const figure = this.createFigure();
12✔
117
            this.initialized.emit(figure);
12✔
118
        });
119

120
        if (this.click.observers.length > 0) {
12!
121
            const msg = 'DEPRECATED: Reconsider using `(plotlyClick)` instead of `(click)` to avoid event conflict. '
×
122
                + 'Please check https://github.com/plotly/angular-plotly.js#FAQ';
123
            console.error(msg);
×
124
        }
125
    }
126

127
    ngOnDestroy(): void {
128
        if (typeof this.resizeHandler === 'function') {
12✔
129
            this.getWindow().removeEventListener('resize', this.resizeHandler as any);
2✔
130
            this.resizeHandler = undefined;
2✔
131
        }
132

133
        const figure = this.createFigure();
12✔
134
        this.purge.emit(figure);
12✔
135
        PlotlyService.remove(this.plotlyInstance!);
12✔
136
    }
137

138
    ngOnChanges(changes: SimpleChanges): void {
139
        let shouldUpdate = false;
4✔
140

141
        const revision: SimpleChange = changes['revision'];
4✔
142
        if (revision && !revision.isFirstChange()) {
4✔
143
            shouldUpdate = true;
2✔
144
        }
145

146
        const debug: SimpleChange = changes['debug'];
4✔
147
        if (debug && !debug.isFirstChange()) {
4✔
148
            shouldUpdate = true;
1✔
149
        }
150

151
        if (shouldUpdate) {
4✔
152
            this.updatePlot();
3✔
153
        }
154

155
        this.updateWindowResizeHandler();
4✔
156
    }
157

158
    ngDoCheck(): boolean | void {
159
        if (this.updateOnlyWithRevision) {
29!
160
            return false;
×
161
        }
162

163
        let shouldUpdate = false;
29✔
164

165
        if (this.updateOnLayoutChange) {
29✔
166
            if (this.layoutDiffer) {
29✔
167
                const layoutHasDiff = this.layoutDiffer.diff(this.layout!);
3✔
168
                if (layoutHasDiff) {
3✔
169
                    shouldUpdate = true;
2✔
170
                }
171
            } else if (this.layout) {
26✔
172
                this.layoutDiffer = this.keyValueDiffers.find(this.layout).create();
1✔
173
            } else {
174
                this.layoutDiffer = undefined;
25✔
175
            }
176
        }
177

178
        if (this.updateOnDataChange) {
29✔
179
            if (this.dataDiffer) {
29✔
180
                const dataHasDiff = this.dataDiffer.diff(this.data);
3✔
181
                if (dataHasDiff) {
3✔
182
                    shouldUpdate = true;
2✔
183
                }
184
            } else if (Array.isArray(this.data)) {
26✔
185
                this.dataDiffer = this.iterableDiffers.find(this.data).create(this.dataDifferTrackBy);
1✔
186
            } else {
187
                this.dataDiffer = undefined;
25✔
188
            }
189
        }
190

191
        if (shouldUpdate && this.plotlyInstance) {
29✔
192
            this.updatePlot();
4✔
193
        }
194
    }
195

196
    getData(): Plotly.Data[] {
197
        return this.data ?? [];
17✔
198
    }
199

200
    getWindow(): any {
201
        return window;
19✔
202
    }
203

204
    getClassName(): string {
205
        let classes = [this.defaultClassName];
45✔
206

207
        if (Array.isArray(this.className)) {
45✔
208
            classes = classes.concat(this.className);
3✔
209
        } else if (this.className) {
42✔
210
            classes.push(this.className);
3✔
211
        }
212

213
        return classes.join(' ');
45✔
214
    }
215

216
    createPlot(): Promise<void> {
217
        return this.plotly.newPlot(this.plotEl.nativeElement, this.getData(), this.layout, this.config, this.frames).then(plotlyInstance => {
16✔
218
            this.plotlyInstance = plotlyInstance;
16✔
219
            this.getWindow().gd = this.debug ? plotlyInstance : undefined;
16✔
220

221
            this.eventNames.forEach(name => {
16✔
222
                const eventName = `plotly_${name.toLowerCase()}`;
480✔
223
                const event = (this as any)[name] as EventEmitter<void>;
480✔
224

225
                plotlyInstance.on(eventName, (data: any) => event.emit(data));
480✔
226
            });
227

228
            plotlyInstance.on('plotly_click', (data: any) => {
16✔
229
                this.plotlyClick.emit(data);
×
230
            });
231

232
            this.updateWindowResizeHandler();
16✔
233
        }, err => {
234
            console.error('Error while plotting:', err);
×
235
            this.error.emit(err);
×
236
        });
237
    }
238

239
    createFigure(): Plotly.Figure {
240
        const p: any = this.plotlyInstance;
25✔
241
        const figure: Plotly.Figure = {
25✔
242
            data: p.data,
243
            layout: p.layout,
244
            frames: p._transitionData ? p._transitionData._frames : null
25!
245
        };
246

247
        return figure;
25✔
248
    }
249

250
    updatePlot(): Promise<any> {
251
        if (!this.plotlyInstance) {
2✔
252
            const error = new Error(`Plotly component wasn't initialized`);
1✔
253
            this.error.emit(error);
1✔
254
            throw error;
1✔
255
        }
256

257
        const layout = { ...this.layout };
1✔
258

259
        return this.plotly.update(this.plotlyInstance, this.getData(), layout, this.config, this.frames).then(() => {
1✔
260
            const figure = this.createFigure();
1✔
261
            this.update.emit(figure);
1✔
262
        }, err => {
263
            console.error('Error while updating plot:', err);
×
264
            this.error.emit(err);
×
265
        });
266
    }
267

268
    updateWindowResizeHandler(): void {
269
        if (this.useResizeHandler) {
22✔
270
            if (this.resizeHandler === undefined) {
5✔
271
                this.resizeHandler = () => this.plotly.resize(this.plotlyInstance!);
3✔
272
                this.getWindow().addEventListener('resize', this.resizeHandler as any);
3✔
273
            }
274
        } else {
275
            if (typeof this.resizeHandler === 'function') {
17✔
276
                this.getWindow().removeEventListener('resize', this.resizeHandler as any);
1✔
277
                this.resizeHandler = undefined;
1✔
278
            }
279
        }
280
    }
281

282
    dataDifferTrackBy(_: number, item: any): unknown {
283
        const obj = Object.assign({}, item, { uid: '' });
3✔
284
        return JSON.stringify(obj);
3✔
285
    }
286
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc