Skip to content

Commit

Permalink
Merge pull request #45 from Alex-NRCan/develop
Browse files Browse the repository at this point in the history
Overall progress, better support for pie/doughnut, Ajv validation update, coloring, bilingual, see description for all (#45)
  • Loading branch information
jolevesq authored Nov 14, 2023
2 parents 7c0545d + 22b8cbe commit 257d3ce
Show file tree
Hide file tree
Showing 11 changed files with 746 additions and 342 deletions.
40 changes: 24 additions & 16 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,27 @@
title: 'Line Chart with time on x and sliders',
geochart: {
borderWidth: 2,
useSteps: "after",
xAxis: {
type: 'time',
unit: 'month',
property: 'CollectionStart_DebutPrelevement',
usePalette: false
},
yAxis: {
property: 'Activity_Activite_mBqm3'
type: 'linear',
property: 'Activity_Activite_mBqm3',
},
xSlider: {
display: true,
},
ySlider: {
display: true,
step: 0.1
}
},
ui: {
stepsSwitcher: true
},
category: {
property: 'Radionuclide_Radionucleide',
usePalette: false
Expand Down Expand Up @@ -165,18 +170,19 @@
tension: 0.3,
xAxis: {
type: 'time',
unit: 'month',
property: 'CollectionStart_DebutPrelevement',
usePalette: false
},
yAxis: {
type: 'logarithmic',
property: 'Activity_Activite_mBqm3'
},
xSlider: {
display: true,
},
ySlider: {
display: true,
step: 0.1
}
},
category: {
Expand Down Expand Up @@ -761,6 +767,13 @@
<div style="text-align: left;">
GEOCHART INPUTS <span style="font-size: small;">(these parameters will be parsed and sent to the Chart.js props on the right and GeoChart created below)</span>
</div>
<div>
<span>Language: </span>
<select id="lang" onChange="languageChanged()">
<option selected value="en">English</option>
<option value="fr">Français</option>
</select>
</div>
<div>
<div>
<button onclick="importDataInputs(DATA_INPUT_LINE_1)">Import Data LINE w/ time w/sliders</button>
Expand Down Expand Up @@ -823,9 +836,9 @@
<div id="root2aca7b6b288c" style="position: relative; width: 800px;"></div>
<div>
<button onclick="redrawChart()" style="padding:5px;"><strong>Looks weird? Redraw!</strong></button>
<button onclick="isLoadingChart()" style="padding:5px;"><strong>Sim. loading whole chart</strong></button>
<button onclick="isLoadingDatasource()" style="padding:5px;"><strong>Sim. lazy loading of datasource</strong></button>
<button onclick="isLoadingClear()" style="padding:5px;"><strong>Clear loading sim.</strong></button>
<button onclick="isLoadingState(1)" style="padding:5px;"><strong>Sim. loading whole chart</strong></button>
<button onclick="isLoadingState(2)" style="padding:5px;"><strong>Sim. lazy loading of datasource</strong></button>
<button onclick="isLoadingState(0)" style="padding:5px;"><strong>Clear loading sim.</strong></button>
</div>

<script>
Expand Down Expand Up @@ -890,19 +903,14 @@
window.dispatchEvent(new CustomEvent("chart/redraw"));
}

function isLoadingChart() {
// Emit a Chart Redraw event so the chart updates
window.dispatchEvent(new CustomEvent("chart/isLoading", { detail: { state: 1 } }));
}

function isLoadingDatasource() {
// Emit a Chart Redraw event so the chart updates
window.dispatchEvent(new CustomEvent("chart/isLoading", { detail: { state: 2 } }));
function languageChanged() {
// Emit a Chart Language changed event so the chart updates
window.dispatchEvent(new CustomEvent("chart/language", { detail: { language: event.target.value } }));
}

function isLoadingClear() {
function isLoadingState(state) {
// Emit a Chart Redraw event so the chart updates
window.dispatchEvent(new CustomEvent("chart/isLoading", { detail: { state: 0 } }));
window.dispatchEvent(new CustomEvent("chart/isLoading", { detail: { state: state } }));
}

function handleChartLoaded(e) {
Expand Down
9 changes: 9 additions & 0 deletions locales/en/translation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"geochart": {
"feature": "Feature",
"steps": "Steps",
"category": "Category",
"parsingError": "There was an error parsing the Chart inputs.",
"viewConsoleDetails": "View console for details."
}
}
9 changes: 9 additions & 0 deletions locales/fr/translation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"geochart": {
"feature": "Enregistrement",
"steps": "Marches",
"category": "Catégorie",
"parsingError": "Une erreur est survenue lors de la lecture des paramètres.",
"viewConsoleDetails": "Voir détails dans la console."
}
}
7 changes: 3 additions & 4 deletions schema-chartjs-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@
"type": "string"
}
]

}
},
"required": ["x", "y"]
Expand Down Expand Up @@ -104,9 +103,9 @@
"type": "integer"
},
"stepped": {
"type": "string",
"enum": ["before", "after", "middle", "true", "false"],
"default": "false"
"type": ["string", "boolean"],
"enum": ["before", "after", "middle", false],
"default": false
}
},
"required": ["data"]
Expand Down
112 changes: 73 additions & 39 deletions src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { GeoChart } from './chart';
import { ChartType, ChartData, ChartOptions } from './chart-types';
import { GeoChartConfig, ChartType, ChartOptions, ChartData, GeoChartAction, DefaultDataPoint } from './chart-types';
import { SchemaValidator, ValidatorResult } from './chart-schema-validator';

/**
Expand All @@ -20,18 +20,31 @@ export function App(props: TypeAppProps): JSX.Element {
const w = window as any;
// Fetch the cgpv module
const { cgpv } = w;
const { react } = cgpv;
const { useEffect, useState } = react;
const { react, useTranslation } = cgpv;
const { useEffect, useState, useCallback } = react;
const { schemaValidator } = props;

// Wire handler
const [inputs, setInputs] = useState();
const [chart, setChart] = useState();
const [data, setData] = useState();
const [options, setOptions] = useState();
const [action, setAction] = useState();
const [isLoadingChart, setIsLoadingChart] = useState();
const [isLoadingDatasource, setIsLoadingDatasource] = useState();
// Translation
const { t, i18n } = useTranslation();

/** ****************************************** USE STATE SECTION START ************************************************ */

const [inputs, setInputs] = useState() as [
GeoChartConfig<ChartType> | undefined,
React.Dispatch<React.SetStateAction<GeoChartConfig<ChartType> | undefined>>
];
const [chart, setChart] = useState() as [ChartType, React.Dispatch<React.SetStateAction<ChartType>>];
const [data, setData] = useState() as [
ChartData<ChartType, DefaultDataPoint<ChartType>, string> | undefined,
React.Dispatch<React.SetStateAction<ChartData<ChartType, DefaultDataPoint<ChartType>, string> | undefined>>
];
const [options, setOptions] = useState() as [ChartOptions | undefined, React.Dispatch<React.SetStateAction<ChartOptions> | undefined>];
const [action, setAction] = useState() as [GeoChartAction, React.Dispatch<React.SetStateAction<GeoChartAction>>];
const [isLoadingChart, setIsLoadingChart] = useState() as [boolean, React.Dispatch<React.SetStateAction<boolean>>];
const [isLoadingDatasource, setIsLoadingDatasource] = useState() as [boolean, React.Dispatch<React.SetStateAction<boolean>>];

/** ****************************************** USE STATE SECTION END ************************************************** */
/** *************************************** EVENT HANDLERS SECTION START ********************************************** */

/**
* Handles when the Chart has to be loaded with data or options.
Expand All @@ -43,13 +56,13 @@ export function App(props: TypeAppProps): JSX.Element {
if (ev.detail.inputs) {
setInputs(ev.detail.inputs);
} else {
setInputs(null); // Clear
if (ev.detail.options) {
setOptions(ev.detail.options);
}
setInputs(undefined); // Clear
if (ev.detail.chart) {
setChart(ev.detail.chart);
}
if (ev.detail.options) {
setOptions(ev.detail.options);
}
if (ev.detail.data) {
setData(ev.detail.data);
}
Expand All @@ -65,13 +78,8 @@ export function App(props: TypeAppProps): JSX.Element {
};

/**
* Handles when the Chart has parsed inputs.
* Handles when the Chart has to show a loading state.
*/
const handleParsed = (theChart: ChartType, theOptions: ChartOptions, theData: ChartData): void => {
// Raise event higher
window.dispatchEvent(new CustomEvent('chart/parsed', { detail: { chart: theChart, options: theOptions, data: theData } }));
};

const handleChartLoading = (e: Event): void => {
const ev = e as CustomEvent;

Expand All @@ -81,42 +89,68 @@ export function App(props: TypeAppProps): JSX.Element {
if (ev.detail.state === 2) setIsLoadingDatasource(true);
};

/** **************************************** EVENT HANDLERS SECTION END *********************************************** */
/** ******************************************* HOOKS SECTION START *************************************************** */

/**
* Handles when the Chart language is changed.
*/
const handleChartLanguage = useCallback(
(e: Event): void => {
const ev = e as CustomEvent;
i18n.changeLanguage(ev.detail.language);
},
[i18n]
);

/**
* Handles when the Chart has parsed inputs.
* We use a 'useCallback' so that any child component with a useEffect dependency on the callback
* doesn't get triggered everytime this parent component re-renders and re-generates its stub.
*/
const handleParsed = useCallback((theChart: ChartType, theOptions: ChartOptions, theData: ChartData): void => {
// Raise event higher
window.dispatchEvent(new CustomEvent('chart/parsed', { detail: { chart: theChart, options: theOptions, data: theData } }));
}, []) as (theChart: ChartType, theOptions: ChartOptions, theData: ChartData) => void; // Crazy typing, because can't use the generic version of 'useCallback'

/**
* Handles an error that happened in the Chart component.
* We use a 'useCallback' so that any child component with a useEffect dependency on the callback
* doesn't get triggered everytime this parent component re-renders and re-generates its stub.
* @param dataErrors The data errors that happened (if any)
* @param optionsErrors The options errors that happened (if any)
*/
const handleError = (
inputErrors: ValidatorResult | undefined,
optionsErrors: ValidatorResult | undefined,
dataErrors: ValidatorResult | undefined
): void => {
// Gather all error messages
const msgs = [];
if (inputErrors) msgs.push(inputErrors);
if (optionsErrors) msgs.push(optionsErrors);
if (dataErrors) msgs.push(dataErrors);
const msgAll = SchemaValidator.parseValidatorResultsMessages(msgs);

// Show the error using an alert. We can't use the cgpv SnackBar as that component is attached to
// a map and we're not even running a cgpv.init() at all here.
// eslint-disable-next-line no-alert
alert(`There was an error parsing the Chart inputs.\n\n${msgAll}\n\nView console for details.`);
};
const handleError = useCallback(
(validators: (ValidatorResult | undefined)[]): void => {
// Gather all error messages
const msgAll = SchemaValidator.parseValidatorResultsMessages(validators);

// Show the error using an alert. We can't use the cgpv SnackBar as that component is attached to
// a map and we're not even running a cgpv.init() at all here.
// eslint-disable-next-line no-alert
alert(`${t('geochart.parsingError')}\n\n${msgAll}\n\n${t('geochart.viewConsoleDetails')}`);
},
[t]
) as (validators: (ValidatorResult | undefined)[]) => void; // Crazy typing, because can't use the generic version of 'useCallback'

// Effect hook to add and remove event listeners.
// Using window.addEventListener is unconventional here, but this is strictly for the 'app' logic with the index.html.
// It's not something to be used by the developers when using the Chart component in their projects.
useEffect(() => {
window.addEventListener('chart/load', handleChartLoad);
window.addEventListener('chart/redraw', handleChartRedraw);
window.addEventListener('chart/language', handleChartLanguage);
window.addEventListener('chart/isLoading', handleChartLoading);
return () => {
window.removeEventListener('chart/load', handleChartLoad);
window.removeEventListener('chart/redraw', handleChartRedraw);
window.removeEventListener('chart/language', handleChartLanguage);
window.removeEventListener('chart/isLoading', handleChartLoading);
};
}, [chart, inputs, data, options]);
}, [handleChartLanguage]);

/** ********************************************* HOOKS SECTION END *************************************************** */
/** ******************************************** RENDER SECTION START ************************************************* */

// Render the Chart
return (
Expand Down
Loading

0 comments on commit 257d3ce

Please sign in to comment.