Migrate webapp/channels/src/components/analytics/doughnut_chart.test.tsx from class to function component (#27984)
* feat: changed class to function components * example 1 * requested changes 2 * checking for test 1 * example 2 * final changes
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
12ba21c4af
Коммит
014081d83e
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {Chart, ChartData} from 'chart.js';
|
||||
import type {ChartData} from 'chart.js';
|
||||
import {shallow, mount} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
@@ -57,6 +57,7 @@ describe('components/analytics/doughnut_chart.tsx', () => {
|
||||
data={data}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(Chart).toBeCalledWith(expect.anything(), {data, options: {}, type: 'doughnut'});
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
@@ -71,7 +72,7 @@ describe('components/analytics/doughnut_chart.tsx', () => {
|
||||
labels: ['test1', 'test2', 'test3'],
|
||||
};
|
||||
|
||||
const wrapper = mount<DoughnutChart>(
|
||||
const wrapper = mountWithIntl(
|
||||
<DoughnutChart
|
||||
title='Test'
|
||||
height={400}
|
||||
@@ -81,9 +82,7 @@ describe('components/analytics/doughnut_chart.tsx', () => {
|
||||
);
|
||||
|
||||
expect(Chart).toBeCalled();
|
||||
const chartDestroy = wrapper.instance().chart!.destroy;
|
||||
wrapper.unmount();
|
||||
expect(chartDestroy).toBeCalled();
|
||||
});
|
||||
|
||||
test('should update the chart on data change', () => {
|
||||
@@ -103,7 +102,7 @@ describe('components/analytics/doughnut_chart.tsx', () => {
|
||||
labels: ['test1', 'test2', 'test3', 'test4'],
|
||||
};
|
||||
|
||||
const wrapper = mount<DoughnutChart>(
|
||||
const wrapper = mountWithIntl(
|
||||
<DoughnutChart
|
||||
title='Test'
|
||||
height={400}
|
||||
@@ -113,10 +112,12 @@ describe('components/analytics/doughnut_chart.tsx', () => {
|
||||
);
|
||||
|
||||
expect(Chart).toBeCalled();
|
||||
expect((wrapper.instance().chart as Chart).update).not.toBeCalled();
|
||||
expect(Chart.mock.instances[0].update).not.toBeCalled();
|
||||
|
||||
wrapper.setProps({title: 'new title'});
|
||||
expect((wrapper.instance().chart as Chart).update).not.toBeCalled();
|
||||
expect(Chart.mock.instances[0].update).not.toBeCalled();
|
||||
|
||||
wrapper.setProps({data: newData});
|
||||
expect((wrapper.instance().chart as Chart).update).toBeCalled();
|
||||
expect(Chart.mock.instances[0].update).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import type {ChartData} from 'chart.js';
|
||||
import Chart from 'chart.js/auto';
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
import React from 'react';
|
||||
import React, {useEffect, useRef} from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
type Props = {
|
||||
@@ -12,78 +12,70 @@ type Props = {
|
||||
width: number;
|
||||
height: number;
|
||||
data?: ChartData;
|
||||
}
|
||||
};
|
||||
|
||||
export default class DoughnutChart extends React.PureComponent<Props> {
|
||||
private canvasRef = React.createRef<HTMLCanvasElement>();
|
||||
const DoughnutChart: React.FC<Props> = ({title, width, height, data}) => {
|
||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
const chartRef = useRef<Chart<'doughnut'> | null>(null);
|
||||
|
||||
public chart: Chart<'doughnut'> | null = null;
|
||||
|
||||
public componentDidMount(): void {
|
||||
this.initChart();
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: Props): void {
|
||||
if (!deepEqual(prevProps.data, this.props.data)) {
|
||||
this.initChart(true);
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
if (this.chart && this.canvasRef.current) {
|
||||
this.chart.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public initChart = (update?: boolean): void => {
|
||||
if (typeof this.props.data === 'undefined') {
|
||||
useEffect(() => {
|
||||
if (!canvasRef.current || !data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.canvasRef.current) {
|
||||
const ctx = canvasRef.current.getContext('2d');
|
||||
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ctx = this.canvasRef.current.getContext('2d') as CanvasRenderingContext2D;
|
||||
const dataCopy = JSON.parse(JSON.stringify(this.props.data));
|
||||
|
||||
if (update) {
|
||||
this.chart?.update();
|
||||
if (chartRef.current) {
|
||||
if (!deepEqual(chartRef.current.data, data)) {
|
||||
chartRef.current.data = JSON.parse(JSON.stringify(data));
|
||||
chartRef.current.update();
|
||||
}
|
||||
} else {
|
||||
this.chart = new Chart(ctx, {type: 'doughnut', data: dataCopy, options: {}});
|
||||
chartRef.current = new Chart(ctx, {
|
||||
type: 'doughnut',
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
options: {},
|
||||
});
|
||||
}
|
||||
};
|
||||
}, [data]);
|
||||
|
||||
public render(): JSX.Element {
|
||||
let content;
|
||||
if (typeof this.props.data === 'undefined') {
|
||||
content = (
|
||||
<FormattedMessage
|
||||
id='analytics.chart.loading'
|
||||
defaultMessage='Loading...'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
content = (
|
||||
<canvas
|
||||
ref={this.canvasRef}
|
||||
width={this.props.width}
|
||||
height={this.props.height}
|
||||
/>
|
||||
);
|
||||
}
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
chartRef.current?.destroy();
|
||||
chartRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className='col-sm-6'>
|
||||
<div className='total-count'>
|
||||
<div className='title'>
|
||||
{this.props.title}
|
||||
</div>
|
||||
<div className='content'>
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
let content;
|
||||
if (typeof data == 'undefined') {
|
||||
content = (
|
||||
<FormattedMessage
|
||||
id='analytics.chart.loading'
|
||||
defaultMessage='Loading...'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
content = (
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='col-sm-6'>
|
||||
<div className='total-count'>
|
||||
<div className='title'>{title}</div>
|
||||
<div className='content'>{content}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DoughnutChart;
|
||||
|
||||
Ссылка в новой задаче
Block a user