Skip to content

Commit e6b8748

Browse files
authored
JS performance improvements (#2475)
* Setup webpack-bundle-analyzer * Get rid of barrel imports for fortawesome * Lazy load charts * Remove translations.json from bundle * Enable full treeshaking * Upgrade chart package * Fix broken React tests * Fix Rspec tests
1 parent 6ea277c commit e6b8748

33 files changed

Lines changed: 336 additions & 70 deletions

app/helpers/application_helper.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,25 @@ def get_icon_class(icon)
125125
'fa fa-globe'
126126
end
127127

128+
def i18n_translations_json
129+
Rails.cache.fetch("i18n_translations/#{I18n.locale}") do
130+
flatten_translations(I18n.t('.', locale: I18n.locale)).to_json
131+
end
132+
end
133+
128134
private
129135

136+
def flatten_translations(hash, prefix = nil)
137+
hash.each_with_object({}) do |(key, value), result|
138+
full_key = prefix ? "#{prefix}.#{key}" : key.to_s
139+
if value.is_a?(Hash)
140+
result.merge!(flatten_translations(value, full_key))
141+
elsif !value.is_a?(Proc)
142+
result[full_key] = value.to_s
143+
end
144+
end
145+
end
146+
130147
def current_controller?(link_path, environment = {})
131148
link_controller = Rails.application.routes
132149
.recognize_path(link_path, environment)[:controller]

app/views/layouts/application.html.erb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
<% else %>
3131
<%= stylesheet_pack_tag 'webpack_bundle', media: 'all' %>
3232
<% end %>
33+
<script>window.__I18N__ = <%= raw i18n_translations_json %>;</script>
3334
<%= javascript_pack_tag 'webpack_bundle', 'data-turbolinks-track': true, defer: true %>
3435
<!-- Webpack Assets END -->
3536

client/.babelrc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[
44
"@babel/env", {
55
"useBuiltIns": "usage",
6-
"modules": "commonjs",
6+
"modules": "auto",
77
"corejs": "3"
88
}
99
],
@@ -13,7 +13,6 @@
1313
"plugins": [
1414
"@babel/plugin-transform-class-properties",
1515
"@babel/plugin-syntax-dynamic-import",
16-
"@babel/plugin-transform-modules-commonjs",
1716
"@babel/plugin-transform-private-methods",
1817
"@babel/plugin-transform-private-property-in-object"
1918
],

client/app/components/Accordion/index.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import React, { useState } from 'react';
33
import type { Node } from 'react';
44
import { Utils } from 'utils';
55
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
6-
import { faCaretDown, faCaretUp } from '@fortawesome/free-solid-svg-icons';
6+
import { faCaretDown } from '@fortawesome/free-solid-svg-icons/faCaretDown';
7+
import { faCaretUp } from '@fortawesome/free-solid-svg-icons/faCaretUp';
78
import globalCss from 'styles/_global.scss';
89
import css from 'components/Input/Input.scss';
910

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// @flow
2+
/* eslint react/jsx-props-no-spreading: 0 */
3+
import { Chart as ChartJS } from 'chart.js';
4+
import React from 'react';
5+
import type { Node } from 'react';
6+
import { AreaChart, LineChart } from 'react-chartkick';
7+
8+
ChartJS.defaults.global.defaultFontFamily = 'Lato';
9+
10+
type chartShape = {
11+
xtitle?: string,
12+
ytitle?: string,
13+
data?: Object | any[],
14+
chartType: 'Line' | 'Area',
15+
};
16+
17+
const colorSchemes = ['#6D0839', '#66118', '#7F503F', '#775577', '#CCAADD'];
18+
19+
export default function ChartRenderer({
20+
chartType,
21+
...props
22+
}: chartShape): Node {
23+
return chartType === 'Line' ? (
24+
<LineChart {...props} colors={colorSchemes} />
25+
) : (
26+
<AreaChart {...props} colors={colorSchemes} />
27+
);
28+
}

client/app/components/Chart/__tests__/Chart.spec.jsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// @flow
22
import React from 'react';
3-
import { render } from '@testing-library/react';
3+
import { render, waitFor } from '@testing-library/react';
44
import { Chart } from 'components/Chart/index';
55

66
const renderComponent = ({ chartType }) => render(
@@ -16,13 +16,17 @@ const renderComponent = ({ chartType }) => render(
1616
);
1717

1818
describe('Chart', () => {
19-
it('renders a Line chart', () => {
19+
it('renders a Line chart', async () => {
2020
const { container } = renderComponent({ chartType: 'Line' });
21-
expect(container.querySelector('canvas')).toBeInTheDocument();
21+
await waitFor(() => {
22+
expect(container.querySelector('canvas')).toBeInTheDocument();
23+
});
2224
});
2325

24-
it('renders an Area chart', () => {
26+
it('renders an Area chart', async () => {
2527
const { container } = renderComponent({ chartType: 'Area' });
26-
expect(container.querySelector('canvas')).toBeInTheDocument();
28+
await waitFor(() => {
29+
expect(container.querySelector('canvas')).toBeInTheDocument();
30+
});
2731
});
2832
});

client/app/components/Chart/__tests__/ChartControl.spec.jsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// @flow
22
import React from 'react';
3-
import { render, screen } from '@testing-library/react';
3+
import { render, screen, waitFor } from '@testing-library/react';
44
import userEvent from '@testing-library/user-event';
55
import { AreaChart } from 'react-chartkick';
66
import { ChartControl } from 'components/Chart/ChartControl';
@@ -21,7 +21,7 @@ describe('ChartControl', () => {
2121
AreaChart.mockClear();
2222
});
2323

24-
it('renders', () => {
24+
it('renders', async () => {
2525
const { container } = render(
2626
<ChartControl
2727
types={['Moments']}
@@ -40,7 +40,9 @@ describe('ChartControl', () => {
4040
const button = screen.getByRole('button', { name: 'Moments' });
4141
expect(button).toBeInTheDocument();
4242
// using querySelector as a last resort for canvas
43-
expect(container.querySelector('canvas')).toBeInTheDocument();
43+
await waitFor(() => {
44+
expect(container.querySelector('canvas')).toBeInTheDocument();
45+
});
4446
});
4547

4648
it('passes down the expected data for the selected type', async () => {
Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
// @flow
22
/* eslint react/jsx-props-no-spreading: 0 */
3-
import { Chart as ChartJS } from 'chart.js';
4-
import React from 'react';
3+
import React, { Suspense, lazy } from 'react';
54
import type { Node } from 'react';
6-
import { AreaChart, LineChart } from 'react-chartkick';
75

8-
ChartJS.defaults.global.defaultFontFamily = 'Lato';
6+
const ChartRenderer = lazy(() => import('./ChartRenderer'));
97

108
type chartShape = {
119
xtitle?: string,
@@ -14,12 +12,10 @@ type chartShape = {
1412
chartType: 'Line' | 'Area',
1513
};
1614

17-
const colorSchemes = ['#6D0839', '#66118', '#7F503F', '#775577', '#CCAADD'];
18-
19-
export function Chart({ chartType, ...props }: chartShape): Node {
20-
return chartType === 'Line' ? (
21-
<LineChart {...props} colors={colorSchemes} />
22-
) : (
23-
<AreaChart {...props} colors={colorSchemes} />
15+
export function Chart(props: chartShape): Node {
16+
return (
17+
<Suspense fallback={null}>
18+
<ChartRenderer {...props} />
19+
</Suspense>
2420
);
2521
}

client/app/components/Header/HeaderProfile.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import React from 'react';
33
import type { Node } from 'react';
44
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
5-
import { faBell } from '@fortawesome/free-solid-svg-icons';
5+
import { faBell } from '@fortawesome/free-solid-svg-icons/faBell';
66
import { Utils } from 'utils';
77
import globalCSS from 'styles/_global.scss';
88
import { Notifications } from 'widgets/Notifications';

client/app/components/Header/index.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import React, {
33
useState, useRef, useEffect, type Node,
44
} from 'react';
55
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
6-
import { faBars, faTimes } from '@fortawesome/free-solid-svg-icons';
6+
import { faBars } from '@fortawesome/free-solid-svg-icons/faBars';
7+
import { faTimes } from '@fortawesome/free-solid-svg-icons/faTimes';
78
import { Utils } from 'utils';
89
import { I18n } from 'libs/i18n';
910
import { Logo } from 'components/Logo';

0 commit comments

Comments
 (0)