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.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// 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 {shallow, mount} from 'enzyme';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
@@ -57,6 +57,7 @@ describe('components/analytics/doughnut_chart.tsx', () => {
|
|||||||
data={data}
|
data={data}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(Chart).toBeCalledWith(expect.anything(), {data, options: {}, type: 'doughnut'});
|
expect(Chart).toBeCalledWith(expect.anything(), {data, options: {}, type: 'doughnut'});
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
@@ -71,7 +72,7 @@ describe('components/analytics/doughnut_chart.tsx', () => {
|
|||||||
labels: ['test1', 'test2', 'test3'],
|
labels: ['test1', 'test2', 'test3'],
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = mount<DoughnutChart>(
|
const wrapper = mountWithIntl(
|
||||||
<DoughnutChart
|
<DoughnutChart
|
||||||
title='Test'
|
title='Test'
|
||||||
height={400}
|
height={400}
|
||||||
@@ -81,9 +82,7 @@ describe('components/analytics/doughnut_chart.tsx', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(Chart).toBeCalled();
|
expect(Chart).toBeCalled();
|
||||||
const chartDestroy = wrapper.instance().chart!.destroy;
|
|
||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
expect(chartDestroy).toBeCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should update the chart on data change', () => {
|
test('should update the chart on data change', () => {
|
||||||
@@ -103,7 +102,7 @@ describe('components/analytics/doughnut_chart.tsx', () => {
|
|||||||
labels: ['test1', 'test2', 'test3', 'test4'],
|
labels: ['test1', 'test2', 'test3', 'test4'],
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = mount<DoughnutChart>(
|
const wrapper = mountWithIntl(
|
||||||
<DoughnutChart
|
<DoughnutChart
|
||||||
title='Test'
|
title='Test'
|
||||||
height={400}
|
height={400}
|
||||||
@@ -113,10 +112,12 @@ describe('components/analytics/doughnut_chart.tsx', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(Chart).toBeCalled();
|
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'});
|
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});
|
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 type {ChartData} from 'chart.js';
|
||||||
import Chart from 'chart.js/auto';
|
import Chart from 'chart.js/auto';
|
||||||
import deepEqual from 'fast-deep-equal';
|
import deepEqual from 'fast-deep-equal';
|
||||||
import React from 'react';
|
import React, {useEffect, useRef} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -12,78 +12,70 @@ type Props = {
|
|||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
data?: ChartData;
|
data?: ChartData;
|
||||||
}
|
};
|
||||||
|
|
||||||
export default class DoughnutChart extends React.PureComponent<Props> {
|
const DoughnutChart: React.FC<Props> = ({title, width, height, data}) => {
|
||||||
private canvasRef = React.createRef<HTMLCanvasElement>();
|
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||||
|
const chartRef = useRef<Chart<'doughnut'> | null>(null);
|
||||||
|
|
||||||
public chart: Chart<'doughnut'> | null = null;
|
useEffect(() => {
|
||||||
|
if (!canvasRef.current || !data) {
|
||||||
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') {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.canvasRef.current) {
|
const ctx = canvasRef.current.getContext('2d');
|
||||||
|
|
||||||
|
if (!ctx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ctx = this.canvasRef.current.getContext('2d') as CanvasRenderingContext2D;
|
if (chartRef.current) {
|
||||||
const dataCopy = JSON.parse(JSON.stringify(this.props.data));
|
if (!deepEqual(chartRef.current.data, data)) {
|
||||||
|
chartRef.current.data = JSON.parse(JSON.stringify(data));
|
||||||
if (update) {
|
chartRef.current.update();
|
||||||
this.chart?.update();
|
}
|
||||||
} else {
|
} 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 {
|
useEffect(() => {
|
||||||
let content;
|
return () => {
|
||||||
if (typeof this.props.data === 'undefined') {
|
chartRef.current?.destroy();
|
||||||
content = (
|
chartRef.current = null;
|
||||||
<FormattedMessage
|
};
|
||||||
id='analytics.chart.loading'
|
}, []);
|
||||||
defaultMessage='Loading...'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
content = (
|
|
||||||
<canvas
|
|
||||||
ref={this.canvasRef}
|
|
||||||
width={this.props.width}
|
|
||||||
height={this.props.height}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
let content;
|
||||||
<div className='col-sm-6'>
|
if (typeof data == 'undefined') {
|
||||||
<div className='total-count'>
|
content = (
|
||||||
<div className='title'>
|
<FormattedMessage
|
||||||
{this.props.title}
|
id='analytics.chart.loading'
|
||||||
</div>
|
defaultMessage='Loading...'
|
||||||
<div className='content'>
|
/>
|
||||||
{content}
|
);
|
||||||
</div>
|
} else {
|
||||||
</div>
|
content = (
|
||||||
</div>
|
<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