diff --git a/Changelog.md b/Changelog.md
new file mode 100644
index 0000000..59d79f3
--- /dev/null
+++ b/Changelog.md
@@ -0,0 +1,2 @@
+v1.3.0 (24 September 2025):
+- component optional property 'showTableTitles' renamed to 'showTitles' as it now applies to both chart and table titles. Default value is true.
\ No newline at end of file
diff --git a/README.md b/README.md
index f3eda23..e659707 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,7 @@ drawChart(container, pxGraphData, locale);>
- showContextMenu: boolean - Flag to show or hide the context menu in the visualization.
- menuItemDefinitions: object[] - Definitions for custom context menu items. These can be either links or run custom functions. The provided object must implement either the IFunctionalMenuItem or ILinkMenuItem interface.
- menuIconInheritColor: boolean - Flag to inherit the color of the context menu icons from the parent element.
-- showTableTitles: boolean - Flag to show or hide the titles in the table view.
+- showTitles: boolean - Flag to show or hide the titles in the tables and charts.
- showTableUnits: boolean - Flag to show or hide the units in the table view.
- showTableSources: boolean - Flag to show or hide the sources in the table view.
- footnote: string - Custom footnote to be displayed in the visualization.
diff --git a/package-lock.json b/package-lock.json
index aa5de19..b2305a7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,18 +1,16 @@
{
"name": "@statisticsfinland/pxvisualizer",
- "version": "1.2.2",
+ "version": "1.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@statisticsfinland/pxvisualizer",
- "version": "1.2.2",
+ "version": "1.3.0",
"license": "Apache-2.0",
"dependencies": {
"decimal.js": "^10.5.0",
- "fflate": "^0.8.2",
- "highcharts": "^12.1.2",
- "highcharts-react-official": "^3.2.1"
+ "fflate": "^0.8.2"
},
"devDependencies": {
"@babel/core": "^7.26.7",
@@ -37,8 +35,8 @@
"babel-loader": "^10.0.0",
"clean-publish": "^5.1.0",
"decimal.js": "^10.5.0",
- "highcharts": "^12.1.2",
- "highcharts-react-official": "^3.2.1",
+ "highcharts": "^12.4.0",
+ "highcharts-react-official": "^3.2.2",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-junit": "^16.0.0",
@@ -8021,16 +8019,16 @@
}
},
"node_modules/highcharts": {
- "version": "12.2.0",
- "resolved": "https://registry.npmjs.org/highcharts/-/highcharts-12.2.0.tgz",
- "integrity": "sha512-UUN+osTP3aeGc4KmoMuWAjzpKif8GYHFozzYI4O8h1ILGof25M/ZGBpXLvgqf1z0LVh7N9eG7i0HnzMfjcR4nA==",
+ "version": "12.4.0",
+ "resolved": "https://registry.npmjs.org/highcharts/-/highcharts-12.4.0.tgz",
+ "integrity": "sha512-o6UxxfChSUrvrZUbWrAuqL1HO/+exhAUPcZY6nnqLsadZQlnP16d082sg7DnXKZCk1gtfkyfkp6g3qkIZ9miZg==",
"dev": true,
"license": "https://www.highcharts.com/license"
},
"node_modules/highcharts-react-official": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/highcharts-react-official/-/highcharts-react-official-3.2.1.tgz",
- "integrity": "sha512-hyQTX7ezCxl7JqumaWiGsroGWalzh24GedQIgO3vJbkGOZ6ySRAltIYjfxhrq4HszJOySZegotEF7v+haQ75UA==",
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/highcharts-react-official/-/highcharts-react-official-3.2.2.tgz",
+ "integrity": "sha512-2kkWOB6RpdR26fmAJkrtJFG9xWFUDGKWyat88tW3fa/3l/Jc7D5ZfwTng2MZsdiKIH32AFy0Pr75udUe7uN6LA==",
"dev": true,
"license": "MIT",
"peerDependencies": {
diff --git a/package.json b/package.json
index 31d3478..5a6a102 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@statisticsfinland/pxvisualizer",
- "version": "1.2.4",
+ "version": "1.3.1",
"description": "Component library for visualizing PxGraf data",
"main": "./dist/pxv.cjs",
"jestSonar": {
@@ -68,8 +68,6 @@
"babel-loader": "^10.0.0",
"clean-publish": "^5.1.0",
"decimal.js": "^10.5.0",
- "highcharts": "^12.1.2",
- "highcharts-react-official": "^3.2.1",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-junit": "^16.0.0",
@@ -90,8 +88,8 @@
"dependencies": {
"decimal.js": "^10.5.0",
"fflate": "^0.8.2",
- "highcharts": "^12.1.2",
- "highcharts-react-official": "^3.2.1"
+ "highcharts": "^12.4.0",
+ "highcharts-react-official": "^3.2.2"
},
"peerDependencies": {
"react": "^18.2.0 || ^19.1.0",
diff --git a/src/core/chartOptions/basicHorizontalBarchartOptions.ts b/src/core/chartOptions/basicHorizontalBarchartOptions.ts
index 6aa231b..8a29168 100644
--- a/src/core/chartOptions/basicHorizontalBarchartOptions.ts
+++ b/src/core/chartOptions/basicHorizontalBarchartOptions.ts
@@ -6,7 +6,7 @@ import { IChartOptions } from '../types/chartOptions';
export const basicHorizontalBarChartOptions = (view: View, locale: string, options?: IChartOptions): Options => {
return {
- ...commonHorizontalBarChartOptions(view, locale),
+ ...commonHorizontalBarChartOptions(view, locale, options),
series: buildBarChartSeries(view, locale, false, options?.accessibilityMode),
chart: { type: 'bar', spacingBottom: 60 },
yAxis: {
diff --git a/src/core/chartOptions/chartOptions.ts b/src/core/chartOptions/chartOptions.ts
index 3f3c434..67a5e2e 100644
--- a/src/core/chartOptions/chartOptions.ts
+++ b/src/core/chartOptions/chartOptions.ts
@@ -7,14 +7,15 @@ import { getLinearAxisTickPositionerFunction } from './Utility/tickPositioners';
import { IChartOptions } from '../types/chartOptions';
import { buildBarChartSeries, buildColumnChartSeries } from './Utility/seriesDataBuilder';
-export const commonChartOptions = (view: View, locale: string): Options => {
+export const commonChartOptions = (view: View, locale: string, options?: IChartOptions): Options => {
+ const showTitle: boolean = options?.showTitle ?? true;
return {
accessibility: {
point: {
descriptionFormatter: getScreenReaderFormatterCallbackFunction(view, locale)
}
},
- title: { text: view.header[locale] },
+ title: { text: showTitle ? view.header[locale] : undefined },
subtitle: { text: view.subheaderValues.map(sv => sv[locale]).join(' | ') },
credits: { text: `${Translations.source[locale]}: ${view.sources.map(s => s[locale]).join(', ')}` },
tooltip: {
@@ -65,9 +66,9 @@ export const commonBasicHorizontalBarChartYAxisOptions = (view: View, locale: st
return yAxisOptions;
}
-export const commonHorizontalBarChartOptions = (view: View, locale: string): Options => {
+export const commonHorizontalBarChartOptions = (view: View, locale: string, options?: IChartOptions): Options => {
return {
- ...commonChartOptions(view, locale),
+ ...commonChartOptions(view, locale, options),
chart: { type: 'bar' },
xAxis: {
categories: view.columnNameGroups.map(cng => cng.map(n => n[locale]).join(', ')),
@@ -83,7 +84,7 @@ export const commonHorizontalBarChartOptions = (view: View, locale: string): Opt
export const commonStackedHorizontalBarChartOptions = (view: View, locale: string, options?: IChartOptions): Options => {
return {
- ...commonHorizontalBarChartOptions(view, locale),
+ ...commonHorizontalBarChartOptions(view, locale, options),
series: buildBarChartSeries(view, locale, true, options?.accessibilityMode),
legend: {
...commonLegendStyleOptions,
@@ -95,9 +96,9 @@ export const commonStackedHorizontalBarChartOptions = (view: View, locale: strin
};
}
-export const commonVerticalBarChartOptions = (view: View, locale: string): Options => {
+export const commonVerticalBarChartOptions = (view: View, locale: string, options?: IChartOptions): Options => {
const result = {
- ...commonChartOptions(view, locale),
+ ...commonChartOptions(view, locale, options),
chart: { type: 'column' },
xAxis: getXAxisOptions(view, locale),
};
@@ -106,7 +107,7 @@ export const commonVerticalBarChartOptions = (view: View, locale: string): Optio
export const commonBasicVerticalBarChartOptions = (view: View, locale: string, options?: IChartOptions): Options => {
return {
- ...commonVerticalBarChartOptions(view, locale),
+ ...commonVerticalBarChartOptions(view, locale, options),
series: buildColumnChartSeries(view, locale, false, options?.accessibilityMode),
yAxis: {
softMin: 0,
@@ -120,7 +121,7 @@ export const commonBasicVerticalBarChartOptions = (view: View, locale: string, o
export const commonStackedVerticalBarChartOptions = (view: View, locale: string, options?: IChartOptions): Options => {
return {
- ...commonVerticalBarChartOptions(view, locale),
+ ...commonVerticalBarChartOptions(view, locale, options),
series: buildColumnChartSeries(view, locale, true, options?.accessibilityMode),
legend: {
...commonLegendStyleOptions,
diff --git a/src/core/chartOptions/groupHorizontalBarChartOptions.ts b/src/core/chartOptions/groupHorizontalBarChartOptions.ts
index 5201bab..66720e5 100644
--- a/src/core/chartOptions/groupHorizontalBarChartOptions.ts
+++ b/src/core/chartOptions/groupHorizontalBarChartOptions.ts
@@ -6,7 +6,7 @@ import { IChartOptions } from '../types/chartOptions';
export const groupHorizontalBarChartOptions = (view: View, locale: string, options?: IChartOptions): Options => {
return {
- ...commonHorizontalBarChartOptions(view, locale),
+ ...commonHorizontalBarChartOptions(view, locale, options),
series: buildBarChartSeries(view, locale, false, options?.accessibilityMode),
yAxis: {
...commonBasicHorizontalBarChartYAxisOptions(view, locale),
diff --git a/src/core/chartOptions/lineChartOptions.ts b/src/core/chartOptions/lineChartOptions.ts
index 8ff19cd..89a886f 100644
--- a/src/core/chartOptions/lineChartOptions.ts
+++ b/src/core/chartOptions/lineChartOptions.ts
@@ -10,7 +10,7 @@ export const lineChartOptions = (view: View, locale: string, options?: IChartOpt
const cutValueAxis = !view.visualizationSettings?.cutValueAxis ? 0 : undefined;
const markerSettings = options?.accessibilityMode ? { enabledThreshold: 3 } : { enabled: false };
return {
- ...commonChartOptions(view, locale),
+ ...commonChartOptions(view, locale, options),
chart: { type: 'line' },
tooltip: {
formatter: getLineChartToolTipFormatterFunction(view, locale)
diff --git a/src/core/chartOptions/pyramidChartOptions.ts b/src/core/chartOptions/pyramidChartOptions.ts
index c6bb484..26990a9 100644
--- a/src/core/chartOptions/pyramidChartOptions.ts
+++ b/src/core/chartOptions/pyramidChartOptions.ts
@@ -9,7 +9,7 @@ export const pyramidChartOptions = (view: View, locale: string, options?: IChart
const categories = view.columnNameGroups.map(cng => cng.map(n => n[locale]).join(', '));
const maxValue = Math.max(...view.series.map(s => Math.max(...s.series.map(dataCell => dataCell.value ?? 0))));
return {
- ...commonChartOptions(view, locale),
+ ...commonChartOptions(view, locale, options),
chart: { type: 'bar' },
xAxis: {
categories: categories,
diff --git a/src/core/chartOptions/scatterPlotOptions.ts b/src/core/chartOptions/scatterPlotOptions.ts
index 7e4a692..72f214c 100644
--- a/src/core/chartOptions/scatterPlotOptions.ts
+++ b/src/core/chartOptions/scatterPlotOptions.ts
@@ -2,12 +2,13 @@ import { Options } from 'highcharts';
import { IDataSeries, View } from "../types/view";
import { getScatterPlotTooltipFormatterFunction } from './Utility/formatters';
import { commonChartOptions, commonYAxisOptions } from './chartOptions';
+import { IChartOptions } from '../types/chartOptions';
-export const scatterPlotOptions = (view: View, locale: string): Options => {
+export const scatterPlotOptions = (view: View, locale: string, options?: IChartOptions): Options => {
const X_INDEX = 1; const Y_INDEX = 0;
const cutValueAxis = !view.visualizationSettings?.cutValueAxis ? 0 : undefined;
return {
- ...commonChartOptions(view, locale),
+ ...commonChartOptions(view, locale, options),
chart: { type: 'scatter' },
xAxis: {
softMin: 0,
diff --git a/src/core/conversion/pxGrafDataConverter.ts b/src/core/conversion/pxGrafDataConverter.ts
index 37d87ac..5543306 100644
--- a/src/core/conversion/pxGrafDataConverter.ts
+++ b/src/core/conversion/pxGrafDataConverter.ts
@@ -41,7 +41,7 @@ export const convertPxGraphDataToChartOptions = (locale: string, view: View, opt
case EVisualizationType.PyramidChart:
return pyramidChartOptions(view, locale, options);
case EVisualizationType.ScatterPlot:
- return scatterPlotOptions(view, locale);
+ return scatterPlotOptions(view, locale, options);
default:
throw new Error('Unsupported chart type');
}
diff --git a/src/core/tables/__snapshots__/htmlTable.test.ts.snap b/src/core/tables/__snapshots__/htmlTable.test.ts.snap
index 1d60f94..9562609 100644
--- a/src/core/tables/__snapshots__/htmlTable.test.ts.snap
+++ b/src/core/tables/__snapshots__/htmlTable.test.ts.snap
@@ -3542,3 +3542,35 @@ exports[`Html table render tests should match snapshot: Table with row variables
[36m
[39m
[36m[39m"
`;
+
+exports[`Html table render tests should match snapshot: Table with source and footnote 1`] = `
+"[36m[39m
+ [36m
[39m
+ [36m[39m
+ [0mLukumäärä, Vantaa, Yksiöt, Vapaarahoitteinen 2022Q4[0m
+ [36m[39m
+ [36m[39m
+ [36m[39m
+ [36m| [39m
+ [0m2 548[0m
+ [36m | [39m
+ [36m
[39m
+ [36m[39m
+ [36m
[39m
+ [36m
[39m
+ [0mYksikkö: lukumäärä[0m
+ [36m
[39m
+ [36m
[39m
+ [0mTest footnote[0m
+ [36m
[39m
+ [36m
[39m
+ [0mLähde: PxVisualizer-fi[0m
+ [36m
[39m
+[36m
[39m"
+`;
diff --git a/src/core/tables/htmlTable.test.ts b/src/core/tables/htmlTable.test.ts
index 3d4b4ee..68ab874 100644
--- a/src/core/tables/htmlTable.test.ts
+++ b/src/core/tables/htmlTable.test.ts
@@ -168,6 +168,29 @@ describe('Html table render tests', () => {
document.body.removeChild(div);
});
+ it('should match snapshot: Table with source and footnote', () => {
+ const mockVarSelections = extractSelectableVariableValues(
+ TABLE_WITH_ONE_CELL.pxGraphData.selectableVariableCodes,
+ TABLE_WITH_ONE_CELL.pxGraphData.metaData,
+ TABLE_WITH_ONE_CELL.pxGraphData.visualizationSettings.defaultSelectableVariableCodes,
+ TABLE_WITH_ONE_CELL.selectedVariableCodes);
+ const mockView = convertPxGrafResponseToView(TABLE_WITH_ONE_CELL.pxGraphData, mockVarSelections);
+ const locale = 'fi';
+
+ const testId = 'test-6895638450983059889';
+
+ const div = document.createElement('div');
+ div.id = testId;
+ document.body.appendChild(div);
+
+ renderHtmlTable(mockView, locale, true, true, true, testId, 'Test footnote');
+
+ const renderedOutput = prettyDOM(div);
+ expect(renderedOutput).toMatchSnapshot();
+
+ document.body.removeChild(div);
+ });
+
it('should match snapshot: Table with missing data and selectable values', () => {
const mockVarSelections = extractSelectableVariableValues(
SELECTABLE_TABLE_WITH_MISSING_DATA.selectableVariableCodes,
diff --git a/src/core/tables/htmlTable.ts b/src/core/tables/htmlTable.ts
index 30f0d5d..d8c4b2c 100644
--- a/src/core/tables/htmlTable.ts
+++ b/src/core/tables/htmlTable.ts
@@ -36,6 +36,13 @@ export function renderHtmlTable(view: View, locale: string, showTitles: boolean,
container.append(pUnits);
}
+ // Footnote
+ if (footnote) {
+ const pFootnote = document.createElement('p');
+ pFootnote.append(footnote);
+ container.append(pFootnote);
+ }
+
// Sources
if (showSources) {
const pSources = document.createElement('p');
@@ -44,13 +51,6 @@ export function renderHtmlTable(view: View, locale: string, showTitles: boolean,
container.append(pSources);
}
- // Footnote
- if (footnote) {
- const pFootnote = document.createElement('p');
- pFootnote.append(footnote);
- container.append(pFootnote);
- }
-
} catch (error) {
console.error(error);
container.replaceChildren();
diff --git a/src/core/types/chartOptions.ts b/src/core/types/chartOptions.ts
index c9881e2..178f38a 100644
--- a/src/core/types/chartOptions.ts
+++ b/src/core/types/chartOptions.ts
@@ -3,4 +3,5 @@
*/
export interface IChartOptions {
accessibilityMode?: boolean;
+ showTitle?: boolean;
}
\ No newline at end of file
diff --git a/src/react/components/burgerMenu/burgerMenu.test.tsx b/src/react/components/burgerMenu/burgerMenu.test.tsx
index 5f867f8..7460f70 100644
--- a/src/react/components/burgerMenu/burgerMenu.test.tsx
+++ b/src/react/components/burgerMenu/burgerMenu.test.tsx
@@ -18,9 +18,9 @@ describe('burgerMenu, rendering tests', () => {
});
const viewData = { tableReference: "testTable" } as unknown as View;
-const mockExportChartLocal = jest.fn();
+const mockExportChart = jest.fn();
beforeEach(() => {
- mockExportChartLocal.mockClear();
+ mockExportChart.mockClear();
});
const mockTableToggle = {
@@ -221,10 +221,12 @@ describe('burgerMenu, functional tests', () => {
expect(finalHeight).toEqual(3000);
});
- it('should call exportChartLocal with correct parameters for SVG export', () => {
+ it('should call export chart with correct parameters for SVG export', () => {
const mockRef = {
chart: {
- exportChartLocal: mockExportChartLocal,
+ exporting: {
+ exportChart: mockExportChart,
+ },
chartWidth: 800,
chartHeight: 400
}
@@ -235,7 +237,7 @@ describe('burgerMenu, functional tests', () => {
fireEvent.click(screen.getByText(Translations.downloadSVG["fi"]));
const { finalWidth, finalHeight } = calculateExportDimensions(mockRef);
- expect(mockExportChartLocal).toHaveBeenCalledWith({
+ expect(mockExportChart).toHaveBeenCalledWith({
filename: generateFilename(viewData.tableReferenceName),
type: "image/svg+xml",
sourceWidth: finalWidth,
@@ -244,10 +246,12 @@ describe('burgerMenu, functional tests', () => {
}, {});
});
- it('should call exportChartLocal with correct parameters for PNG export', () => {
+ it('should call export chart with correct parameters for PNG export', () => {
const mockRef = {
chart: {
- exportChartLocal: mockExportChartLocal,
+ exporting: {
+ exportChart: mockExportChart,
+ },
chartWidth: 800,
chartHeight: 400
}
@@ -258,7 +262,7 @@ describe('burgerMenu, functional tests', () => {
fireEvent.click(screen.getByText(Translations.downloadPNG["fi"]));
const { finalWidth, finalHeight } = calculateExportDimensions(mockRef);
- expect(mockExportChartLocal).toHaveBeenCalledWith({
+ expect(mockExportChart).toHaveBeenCalledWith({
filename: generateFilename(viewData.tableReferenceName),
sourceWidth: finalWidth,
sourceHeight: finalHeight,
diff --git a/src/react/components/burgerMenu/burgerMenu.tsx b/src/react/components/burgerMenu/burgerMenu.tsx
index 044b36b..40fa4e2 100644
--- a/src/react/components/burgerMenu/burgerMenu.tsx
+++ b/src/react/components/burgerMenu/burgerMenu.tsx
@@ -229,7 +229,7 @@ export const BurgerMenu: React.FC = ({ viewData, currentChartR
prefixIcon={'Download'}
text={Translations.downloadSVG[locale]}
onClick={() => handleMenuItemClick(() =>
- currentChartRef.chart.exportChartLocal({
+ currentChartRef.chart.exporting.exportChart({
filename: `${generateFilename(viewData.tableReferenceName)}`,
type: "image/svg+xml",
sourceWidth: calculateExportDimensions(currentChartRef).finalWidth,
@@ -252,7 +252,7 @@ export const BurgerMenu: React.FC = ({ viewData, currentChartR
prefixIcon={'Download'}
text={Translations.downloadPNG[locale]}
onClick={() => handleMenuItemClick(() =>
- currentChartRef.chart.exportChartLocal({
+ currentChartRef.chart.exporting.exportChart({
filename: `${generateFilename(viewData.tableReferenceName)}`,
sourceWidth: calculateExportDimensions(currentChartRef).finalWidth,
sourceHeight: calculateExportDimensions(currentChartRef).finalHeight,
diff --git a/src/react/components/chart/__snapshots__/chart.test.tsx.snap b/src/react/components/chart/__snapshots__/chart.test.tsx.snap
index 0fe7d6b..29a6905 100644
--- a/src/react/components/chart/__snapshots__/chart.test.tsx.snap
+++ b/src/react/components/chart/__snapshots__/chart.test.tsx.snap
@@ -115,6 +115,128 @@ exports[`Rendering test renders chart data correctly 1`] = `
+
+ Lähde: PxVisualizer-fi
+
+
+
+
+
+`;
+
+exports[`Rendering test renders chart data correctly with hidden title 1`] = `
+
+
+
+
+
+
+
+
+
+ |
+
+ 2015Q1
+ |
+
+ 2015Q2
+ |
+
+
+
+
+ |
+ Vapaarahoitteinen
+ |
+
+ 11 096
+ |
+
+ 11 625
+ |
+
+
+ |
+ ARA
+ |
+
+ 4 845
+ |
+
+ 5 174
+ |
+
+
+
+
+ Lähde: PxVisualizer-fi
+
@@ -145,7 +267,7 @@ exports[`Rendering test renders table data correctly 1`] = `
class="sc-kpOvIu imTYeK"
>