arbor-dashboard 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ v18.18.0
package/.prettierrc ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "singleQuote": true,
3
+ "tabWidth": 4,
4
+ "trailingComma": "all",
5
+ "bracketSpacing": false
6
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
3
+ "editor.formatOnSave": true,
4
+ "[typescriptreact]": {
5
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
6
+ }
7
+ }
package/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # data.responsive_charting_dashboards
2
+ An npm package of responsive charting components for data visualisation
@@ -0,0 +1,18 @@
1
+ import globals from 'globals';
2
+ import pluginJs from '@eslint/js';
3
+ import tseslint from 'typescript-eslint';
4
+ import pluginReact from 'eslint-plugin-react';
5
+
6
+ export default [
7
+ {files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}']},
8
+ {languageOptions: {globals: globals.browser}},
9
+ pluginJs.configs.recommended,
10
+ ...tseslint.configs.recommended,
11
+ pluginReact.configs.flat.recommended,
12
+ {
13
+ rules: {
14
+ 'no-unused-vars': 'error',
15
+ 'no-undef': 'error',
16
+ },
17
+ },
18
+ ];
package/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {BarChart} from './src/Components/Charts/BarChart';
2
+ export {Dashboard} from './src/Components/Dashboard/Dashboard';
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "arbor-dashboard",
3
+ "version": "1.0.0",
4
+ "description": "A tool to create responsive dashboards with charts",
5
+ "main": "index.ts",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+ssh://git@github.com:arbor-education/data.responsive_charting_dashboards.git"
12
+ },
13
+ "keywords": [
14
+ "Dashboard",
15
+ "Reporting",
16
+ "Arbor",
17
+ "Charts"
18
+ ],
19
+ "author": "Ashley Boyd",
20
+ "license": "ISC",
21
+ "bugs": {
22
+ "url": "https://github.com/arbor-education/data.responsive_charting_dashboards/issues"
23
+ },
24
+ "homepage": "https://github.com/arbor-education/data.responsive_charting_dashboards#readme",
25
+ "dependencies": {
26
+ "@nivo/bar": "^0.87.0",
27
+ "@nivo/heatmap": "^0.88.0",
28
+ "@nivo/line": "^0.87.0",
29
+ "@tanstack/react-query": "^5.60.5",
30
+ "react": "^18.3.1",
31
+ "react-grid-layout": "^1.5.0",
32
+ "typescript": "^5.6.3"
33
+ },
34
+ "devDependencies": {
35
+ "@eslint/js": "^9.13.0",
36
+ "eslint": "^9.13.0",
37
+ "eslint-plugin-react": "^7.37.1",
38
+ "globals": "^15.11.0",
39
+ "prettier": "^3.3.3",
40
+ "typescript-eslint": "^8.11.0"
41
+ }
42
+ }
@@ -0,0 +1,133 @@
1
+ import {ResponsiveBar} from '@nivo/bar';
2
+ import React from 'react';
3
+ import {transformChartData} from './transformChartData';
4
+ import {useQuery} from '@tanstack/react-query';
5
+ import {httpGet} from '../../HttpRequest/HttpRequest';
6
+ type BarChartProps = {
7
+ dataUrl: string;
8
+ token: string;
9
+ testEnv: boolean;
10
+ };
11
+ export const BarChart = ({dataUrl, token, testEnv}: BarChartProps) => {
12
+ const {data} = useQuery({
13
+ queryKey: ['bar'],
14
+ queryFn: () => httpGet(dataUrl, token),
15
+ });
16
+
17
+ const {transformedData, query, annotation} = transformChartData(
18
+ data,
19
+ 'bar',
20
+ testEnv,
21
+ );
22
+ const keys = query.measures;
23
+ const indexBy = query.dimensions[0];
24
+ const xAxisLabel = annotation.dimensions[indexBy].title;
25
+ const yAxisLabel = annotation.measures[keys[0]].title;
26
+
27
+ return (
28
+ <ResponsiveBar
29
+ data={transformedData}
30
+ keys={keys}
31
+ indexBy={indexBy}
32
+ margin={{top: 50, right: 130, bottom: 50, left: 60}}
33
+ padding={0.3}
34
+ valueScale={{type: 'linear'}}
35
+ indexScale={{type: 'band', round: true}}
36
+ colors={{scheme: 'nivo'}}
37
+ defs={[
38
+ {
39
+ id: 'dots',
40
+ type: 'patternDots',
41
+ background: 'inherit',
42
+ color: '#38bcb2',
43
+ size: 4,
44
+ padding: 1,
45
+ stagger: true,
46
+ },
47
+ {
48
+ id: 'lines',
49
+ type: 'patternLines',
50
+ background: 'inherit',
51
+ color: '#eed312',
52
+ rotation: -45,
53
+ lineWidth: 6,
54
+ spacing: 10,
55
+ },
56
+ ]}
57
+ fill={[
58
+ {
59
+ match: {
60
+ id: 'fries',
61
+ },
62
+ id: 'dots',
63
+ },
64
+ {
65
+ match: {
66
+ id: 'sandwich',
67
+ },
68
+ id: 'lines',
69
+ },
70
+ ]}
71
+ borderColor={{
72
+ from: 'color',
73
+ modifiers: [['darker', 1.6]],
74
+ }}
75
+ axisTop={null}
76
+ axisRight={null}
77
+ axisBottom={{
78
+ tickSize: 5,
79
+ tickPadding: 5,
80
+ tickRotation: 0,
81
+ legend: xAxisLabel,
82
+ legendPosition: 'middle',
83
+ legendOffset: 32,
84
+ truncateTickAt: 0,
85
+ }}
86
+ axisLeft={{
87
+ tickSize: 5,
88
+ tickPadding: 5,
89
+ tickRotation: 0,
90
+ legend: yAxisLabel,
91
+ legendPosition: 'middle',
92
+ legendOffset: -40,
93
+ truncateTickAt: 0,
94
+ }}
95
+ labelSkipWidth={12}
96
+ labelSkipHeight={12}
97
+ labelTextColor={{
98
+ from: 'color',
99
+ modifiers: [['darker', 1.6]],
100
+ }}
101
+ legends={[
102
+ {
103
+ dataFrom: 'keys',
104
+ anchor: 'bottom-right',
105
+ direction: 'column',
106
+ justify: false,
107
+ translateX: 120,
108
+ translateY: 0,
109
+ itemsSpacing: 2,
110
+ itemWidth: 100,
111
+ itemHeight: 20,
112
+ itemDirection: 'left-to-right',
113
+ itemOpacity: 0.85,
114
+ symbolSize: 20,
115
+ effects: [
116
+ {
117
+ on: 'hover',
118
+ style: {
119
+ itemOpacity: 1,
120
+ },
121
+ },
122
+ ],
123
+ },
124
+ ]}
125
+ role="application"
126
+ ariaLabel="Nivo bar chart demo"
127
+ barAriaLabel={(e) =>
128
+ e.id + ': ' + e.formattedValue + ' in country: ' + e.indexValue
129
+ }
130
+ onClick={(e) => console.log(e)}
131
+ />
132
+ );
133
+ };
@@ -0,0 +1,100 @@
1
+ import {
2
+ DefaultHeatMapDatum,
3
+ HeatMapDatum,
4
+ HeatMapSerie,
5
+ ResponsiveHeatMap,
6
+ } from '@nivo/heatmap';
7
+ import React from 'react';
8
+ import {transformChartData} from './transformChartData';
9
+ import {useQuery} from '@tanstack/react-query';
10
+ import {httpGet} from '../../HttpRequest/HttpRequest';
11
+
12
+ type HeatMapProps = {
13
+ dataUrl: string;
14
+ token: string;
15
+ testEnv: boolean;
16
+ };
17
+
18
+ export const HeatMap = ({dataUrl, token, testEnv}: HeatMapProps) => {
19
+ const {data} = useQuery({
20
+ queryKey: ['heatMap'],
21
+ queryFn: () => httpGet(dataUrl, token),
22
+ });
23
+
24
+ const {transformedData, query, annotation} = transformChartData(
25
+ data,
26
+ 'heatMap',
27
+ testEnv,
28
+ );
29
+
30
+ const keys = query.measures;
31
+ const indexBy = query.dimensions[0];
32
+ const xAxisLabel = annotation.dimensions[indexBy].title;
33
+ const yAxisLabel = annotation.measures[keys[0]].title;
34
+
35
+ return (
36
+ <ResponsiveHeatMap
37
+ data={
38
+ transformedData as HeatMapSerie<
39
+ DefaultHeatMapDatum,
40
+ HeatMapDatum
41
+ >[]
42
+ }
43
+ margin={{top: 100, right: 180, bottom: 50, left: 100}}
44
+ valueFormat=">-.2s"
45
+ axisTop={{
46
+ tickSize: 5,
47
+ tickPadding: 5,
48
+ tickRotation: -90,
49
+ legend: xAxisLabel,
50
+ legendOffset: 46,
51
+ truncateTickAt: 0,
52
+ }}
53
+ axisRight={{
54
+ tickSize: 5,
55
+ tickPadding: 5,
56
+ tickRotation: 0,
57
+ legend: yAxisLabel,
58
+ legendPosition: 'middle',
59
+ legendOffset: 70,
60
+ truncateTickAt: 0,
61
+ }}
62
+ axisLeft={{
63
+ tickSize: 5,
64
+ tickPadding: 5,
65
+ tickRotation: 0,
66
+ legend: yAxisLabel,
67
+ legendPosition: 'middle',
68
+ legendOffset: -72,
69
+ truncateTickAt: 0,
70
+ }}
71
+ colors={{
72
+ type: 'diverging',
73
+ scheme: 'red_yellow_blue',
74
+ divergeAt: 0.5,
75
+ minValue: -100000,
76
+ maxValue: 100000,
77
+ }}
78
+ emptyColor="#555555"
79
+ legends={[
80
+ {
81
+ anchor: 'bottom',
82
+ translateX: 0,
83
+ translateY: 30,
84
+ length: 400,
85
+ thickness: 8,
86
+ direction: 'row',
87
+ tickPosition: 'after',
88
+ tickSize: 3,
89
+ tickSpacing: 4,
90
+ tickOverlap: false,
91
+ tickFormat: '>-.2s',
92
+ title: 'Value →',
93
+ titleAlign: 'start',
94
+ titleOffset: 4,
95
+ },
96
+ ]}
97
+ onClick={(e) => console.log(e)}
98
+ />
99
+ );
100
+ };
@@ -0,0 +1,98 @@
1
+ import {ResponsiveLine} from '@nivo/line';
2
+ import React from 'react';
3
+ import {transformChartData} from './transformChartData';
4
+ import {useQuery} from '@tanstack/react-query';
5
+ import {httpGet} from '../../HttpRequest/HttpRequest';
6
+
7
+ type LineChartProps = {
8
+ dataUrl: string;
9
+ token: string;
10
+ testEnv: boolean;
11
+ };
12
+ export const LineChart = ({dataUrl, token, testEnv}: LineChartProps) => {
13
+ const {data} = useQuery({
14
+ queryKey: ['line'],
15
+ queryFn: () => httpGet(dataUrl, token),
16
+ });
17
+
18
+ const {transformedData, query, annotation} = transformChartData(
19
+ data,
20
+ 'line',
21
+ testEnv,
22
+ );
23
+ // TODO: this logic is replicated - should extract it
24
+ const keys = query.measures;
25
+ const indexBy = query.dimensions[0];
26
+ const xAxisLabel = annotation.dimensions[indexBy].title;
27
+ const yAxisLabel = annotation.measures[keys[0]].title;
28
+ return (
29
+ <ResponsiveLine
30
+ data={transformedData}
31
+ margin={{top: 100, right: 180, bottom: 100, left: 100}}
32
+ xScale={{type: 'point'}}
33
+ yScale={{
34
+ type: 'linear',
35
+ min: 'auto',
36
+ max: 'auto',
37
+ stacked: true,
38
+ reverse: false,
39
+ }}
40
+ yFormat=" >-.2f"
41
+ axisTop={null}
42
+ axisRight={null}
43
+ axisBottom={{
44
+ tickSize: 5,
45
+ tickPadding: 5,
46
+ tickRotation: 0,
47
+ legend: xAxisLabel,
48
+ legendOffset: 36,
49
+ legendPosition: 'middle',
50
+ truncateTickAt: 0,
51
+ }}
52
+ axisLeft={{
53
+ tickSize: 5,
54
+ tickPadding: 5,
55
+ tickRotation: 0,
56
+ legend: yAxisLabel,
57
+ legendOffset: -60,
58
+ legendPosition: 'middle',
59
+ truncateTickAt: 0,
60
+ }}
61
+ pointSize={10}
62
+ pointColor={{theme: 'background'}}
63
+ pointBorderWidth={2}
64
+ pointBorderColor={{from: 'serieColor'}}
65
+ pointLabel="data.yFormatted"
66
+ pointLabelYOffset={-12}
67
+ enableTouchCrosshair={true}
68
+ useMesh={true}
69
+ legends={[
70
+ {
71
+ anchor: 'bottom-right',
72
+ direction: 'column',
73
+ justify: false,
74
+ translateX: 100,
75
+ translateY: 0,
76
+ itemsSpacing: 0,
77
+ itemDirection: 'left-to-right',
78
+ itemWidth: 80,
79
+ itemHeight: 20,
80
+ itemOpacity: 0.75,
81
+ symbolSize: 12,
82
+ symbolShape: 'circle',
83
+ symbolBorderColor: 'rgba(0, 0, 0, .5)',
84
+ effects: [
85
+ {
86
+ on: 'hover',
87
+ style: {
88
+ itemBackground: 'rgba(0, 0, 0, .03)',
89
+ itemOpacity: 1,
90
+ },
91
+ },
92
+ ],
93
+ },
94
+ ]}
95
+ onClick={(e) => console.log(e)}
96
+ />
97
+ );
98
+ };