From 3049c759201ed0d854cb57195f7c966b7f73abec Mon Sep 17 00:00:00 2001 From: Sakari Malkki Date: Tue, 28 Oct 2025 12:04:39 +0200 Subject: [PATCH 01/21] Optional toggle for showing last updated info based on content dimension value lastupdated property --- .storybook/preview.ts | 34 +- package.json | 2 +- .../chartOptions/Utility/formatters.test.ts | 29 +- src/core/chartOptions/Utility/formatters.ts | 22 + src/core/chartOptions/chartOptions.ts | 31 +- src/core/chartOptions/pieChartOptions.ts | 2 +- .../fixtures/percentHorizontalBarChart.ts | 2 + .../fixtures/percentVerticalBarChart.ts | 3 + .../translations/defaultTranslations.ts | 5 + .../translations/translationManager.test.ts | 3 + .../translations/translationTypes.ts | 1 + src/core/conversion/viewUtils.test.ts | 10 +- src/core/conversion/viewUtils.ts | 41 +- src/core/highcharts/drawChart.ts | 2 +- src/core/tables/htmlTable.ts | 22 +- src/core/types/chartOptions.ts | 1 + src/core/types/view.ts | 1 + .../chart/__snapshots__/chart.test.tsx.snap | 823 +++++++++++++++++- src/react/components/chart/chart.test.tsx | 20 + src/react/components/chart/chart.tsx | 11 +- src/react/components/chart/tableView.tsx | 7 +- .../grouphorizontalbar.stories.tsx | 2 +- .../chartstories/groupverticalbar.stories.tsx | 2 +- .../chartstories/horizontal.stories.tsx | 2 +- .../chartstories/linechart.stories.tsx | 2 +- .../percenthorizontalbar.stories.tsx | 2 +- .../percentverticalbar.stories.tsx | 2 +- src/stories/chartstories/pie.stories.tsx | 2 +- src/stories/chartstories/pyramid.stories.tsx | 2 +- .../chartstories/scatterplot.stories.tsx | 2 +- .../stackedhorizontalbar.stories.tsx | 2 +- .../stackedverticalbar.stories.tsx | 2 +- .../chartstories/verticalbar.stories.tsx | 2 +- .../fixtures/groupHorizontalBarChart.ts | 7 +- src/stories/fixtures/groupVerticalBarChart.ts | 5 +- src/stories/fixtures/horizontalBarChart.ts | 5 +- src/stories/fixtures/lineChart.ts | 5 +- .../fixtures/percentHorizontalBarChart.ts | 5 +- .../fixtures/percentVerticalBarChart.ts | 5 +- src/stories/fixtures/pieChart.ts | 5 +- src/stories/fixtures/pyramidChart.ts | 5 +- src/stories/fixtures/scatterPlot.ts | 5 +- .../fixtures/stackedHorizontalBarChart.ts | 5 +- .../fixtures/stackedVerticalBarChart.ts | 5 +- src/stories/fixtures/table.ts | 5 +- src/stories/fixtures/verticalBarChart.ts | 5 +- src/stories/tablestories/table.stories.tsx | 2 +- 47 files changed, 1096 insertions(+), 69 deletions(-) diff --git a/.storybook/preview.ts b/.storybook/preview.ts index e4f4d1e..969ada4 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -19,7 +19,39 @@ const preview: Preview = { }, type: 'desktop', }, - }, + small: { + name: 'Small screen', + styles: { + width: '383px', + height: '300px', + }, + type: 'desktop', + }, + large: { + name: 'Large screen', + styles: { + width: '1150px', + height: '900px' + }, + type: 'desktop', + }, + mobile: { + name: 'Mobile', + styles: { + width: '540px', + height: '960px', + }, + type: 'mobile', + }, + tablet: { + name: 'Tablet', + styles: { + width: '900px', + height: '600px', + }, + type: 'tablet', + }, + } }, }, tags: ['autodocs'] diff --git a/package.json b/package.json index e1e7903..74cc6f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@statisticsfinland/pxvisualizer", - "version": "1.3.2", + "version": "1.4.2", "description": "Component library for visualizing PxGraf data", "main": "./dist/pxv.cjs", "jestSonar": { diff --git a/src/core/chartOptions/Utility/formatters.test.ts b/src/core/chartOptions/Utility/formatters.test.ts index faa118a..5d889be 100644 --- a/src/core/chartOptions/Utility/formatters.test.ts +++ b/src/core/chartOptions/Utility/formatters.test.ts @@ -1,5 +1,5 @@ import { DataLabelsOptions, Point, Tooltip } from "highcharts"; -import { formatLocale, getDataFormattedForChartType, getDataLabelFormatterFunction, getLineChartToolTipFormatterFunction, getToolTipFormatterFunction, parseScreenReaderFriendlyTimePeriods, shortenStringValue } from "./formatters"; +import { formatLocale, getDataFormattedForChartType, getDataLabelFormatterFunction, getLineChartToolTipFormatterFunction, getToolTipFormatterFunction, parseScreenReaderFriendlyTimePeriods, shortenStringValue, getFormattedLastUpdatedText } from "./formatters"; import { combinationValuesLinechartViewFixture, multiselectableLineChartViewFixture, simpleQuarterLinechartViewFixture } from "./fixtures/linechartViews"; import { simpleHorizontalBarchartViewFixture } from "./fixtures/horizontalbarchartViews"; import { simpleGroupHorizontalBarchartViewFixture } from "./fixtures/grouphorizontalbarchartViews"; @@ -421,4 +421,31 @@ describe('getDataFormattedForChartType tests', () => { const result: string = getDataFormattedForChartType(view, point, 'en', 2); expect(result).toBe('12.0% (123.46 kg)'); }); +}); + +describe('getFormattedLastUpdatedText tests', () => { + it('should format last updated text correctly for Finnish locale', () => { + const result = getFormattedLastUpdatedText('2025-09-23', 'fi'); + expect(result).toBe('Päivitetty: 23.9.2025'); + }); + + it('should format last updated text correctly for Swedish locale', () => { + const result = getFormattedLastUpdatedText('2025-09-23', 'sv'); + expect(result).toBe('Uppdaterad: 2025-09-23'); + }); + + it('should format last updated text correctly for English locale', () => { + const result = getFormattedLastUpdatedText('2025-09-23', 'en'); + expect(result).toBe('Updated: 9/23/2025'); + }); + + it('should return undefined for undefined input', () => { + const result = getFormattedLastUpdatedText(undefined, 'fi'); + expect(result).toBeUndefined(); + }); + + it('should return undefined for invalid date', () => { + const result = getFormattedLastUpdatedText('invalid-date', 'fi'); + expect(result).toBeUndefined(); + }); }); \ No newline at end of file diff --git a/src/core/chartOptions/Utility/formatters.ts b/src/core/chartOptions/Utility/formatters.ts index 53828bb..e75706b 100644 --- a/src/core/chartOptions/Utility/formatters.ts +++ b/src/core/chartOptions/Utility/formatters.ts @@ -256,4 +256,26 @@ function parsePlacementSuffix(value: string, locale: string) { } else { return `${value}.`; } +} + +/** + * Formats the last updated text based on the provided date string and locale. + * @param lastUpdated Date string representing the last updated date. + * @param locale Locale string for formatting the date. + * @returns Formatted last updated text or undefined if input is invalid. + */ +export function getFormattedLastUpdatedText(lastUpdated: string | undefined, locale: string): string | undefined { + if (!lastUpdated) return undefined; + + try { + const date = new Date(lastUpdated); + if (Number.isNaN(date.getTime())) return undefined; + + const formattedDate: string = Intl.DateTimeFormat(locale).format(date); + + return `${Translations.lastUpdated[locale]}: ${formattedDate}`; + } catch (error) { + console.error('Error formatting date:', error); + return undefined; + } } \ No newline at end of file diff --git a/src/core/chartOptions/chartOptions.ts b/src/core/chartOptions/chartOptions.ts index 67a5e2e..d0fcc34 100644 --- a/src/core/chartOptions/chartOptions.ts +++ b/src/core/chartOptions/chartOptions.ts @@ -1,14 +1,39 @@ import { LegendOptions, Options, PlotSeriesDataLabelsOptions, YAxisOptions } from 'highcharts'; import { View } from "../types/view"; -import { getAxisLabelShorteningFunction, getFormattedUnits, getToolTipFormatterFunction, getScreenReaderFormatterCallbackFunction, getDataLabelFormatterFunction } from './Utility/formatters'; -import { Translations } from '../conversion/translations'; +import { getAxisLabelShorteningFunction, getFormattedUnits, getToolTipFormatterFunction, getScreenReaderFormatterCallbackFunction, getDataLabelFormatterFunction, getFormattedLastUpdatedText } from './Utility/formatters'; import { getXAxisOptions } from './Utility/timeIntervals'; import { getLinearAxisTickPositionerFunction } from './Utility/tickPositioners'; import { IChartOptions } from '../types/chartOptions'; import { buildBarChartSeries, buildColumnChartSeries } from './Utility/seriesDataBuilder'; +import { Translations } from "../conversion/translations"; export const commonChartOptions = (view: View, locale: string, options?: IChartOptions): Options => { const showTitle: boolean = options?.showTitle ?? true; + + const sourceText = Translations.source[locale]; + let creditsText = `${sourceText}: ${view.sources.map(s => s[locale]).join(', ')}`; + let creditsConfig: any = { + enabled: true, + text: creditsText + }; + + if (options?.showLastUpdated && view.lastUpdated) { + const lastUpdatedText = getFormattedLastUpdatedText(view.lastUpdated, locale); + if (lastUpdatedText) { + creditsText = `${lastUpdatedText}
${sourceText}: ${view.sources.map(s => s[locale]).join(', ')}`; + creditsConfig = { + enabled: true, + text: creditsText, + useHTML: true, + position: { + align: 'left', + verticalAlign: 'bottom', + y: -20 + } + }; + } + } + return { accessibility: { point: { @@ -17,7 +42,7 @@ export const commonChartOptions = (view: View, locale: string, options?: IChartO }, 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(', ')}` }, + credits: creditsConfig, tooltip: { formatter: getToolTipFormatterFunction(view, locale) }, diff --git a/src/core/chartOptions/pieChartOptions.ts b/src/core/chartOptions/pieChartOptions.ts index 090384c..2bf607d 100644 --- a/src/core/chartOptions/pieChartOptions.ts +++ b/src/core/chartOptions/pieChartOptions.ts @@ -7,7 +7,7 @@ import { buildPatternObject } from './Utility/patternFill'; export const pieChartOptions = (view: View, locale: string, options?: IChartOptions): Options => { return { - ...commonChartOptions(view, locale), + ...commonChartOptions(view, locale, options), chart: { type: 'pie' }, plotOptions: { pie: { diff --git a/src/core/conversion/fixtures/percentHorizontalBarChart.ts b/src/core/conversion/fixtures/percentHorizontalBarChart.ts index 4916be9..5d9412e 100644 --- a/src/core/conversion/fixtures/percentHorizontalBarChart.ts +++ b/src/core/conversion/fixtures/percentHorizontalBarChart.ts @@ -222,6 +222,7 @@ export const PERCENT_HORIZONTAL_BAR_CHART_VIEW: View = { "sv": "Antal, Enrumslägenhet 2022Q4 efter Område, Finansieringssätt", "en": "Number, One-room flat 2022Q4 by Region, Type of funding" }, + lastUpdated: "2023-01-19T06:00:00Z", subheaderValues: [], units: [ { @@ -595,6 +596,7 @@ export const PERCENT_HORIZONTAL_BAR_CHART_WITH_SELECTABLES_VIEW: View = { "sv": "Antal 2022Q4 efter Område, Antal rum, Finansieringssätt", "en": "Number 2022Q4 by Region, Number of rooms, Type of funding" }, + "lastUpdated": "2023-01-19T06:00:00Z", subheaderValues: [ { "fi": "Kaksiot", diff --git a/src/core/conversion/fixtures/percentVerticalBarChart.ts b/src/core/conversion/fixtures/percentVerticalBarChart.ts index 186533d..7db1def 100644 --- a/src/core/conversion/fixtures/percentVerticalBarChart.ts +++ b/src/core/conversion/fixtures/percentVerticalBarChart.ts @@ -280,6 +280,7 @@ export const PERCENT_VERTICAL_BAR_CHART_VIEW: View = { "sv": "Antal, Helsingfors, Fri finansierad 2021Q2-2022Q4 efter Antal rum", "en": "Number, Helsinki, Non subsidised 2021Q2-2022Q4 by Number of rooms" }, + lastUpdated: "2023-01-19T06:00:00Z", subheaderValues: [], units: [{ name: { @@ -843,6 +844,7 @@ export const PERCENT_VERTICAL_BAR_CHART_WITH_SELECTABLES_VIEW: View = { "sv": "Antal 2021Q4 efter Område, Antal rum, Finansieringssätt", "en": "Number 2021Q4 by Region, Number of rooms, Type of funding" }, + lastUpdated: "2023-01-19T06:00:00Z", subheaderValues: [ { "fi": "2022Q1", @@ -1241,6 +1243,7 @@ export const PERCENT_VERTICAL_BAR_CHART_PIVOTED_WITH_SELECTABLES_VIEW: View = { "sv": "Antal 2022Q4 efter Område, Antal rum, Finansieringssätt", "en": "Number 2022Q4 by Region, Number of rooms, Type of funding" }, + lastUpdated: "2023-01-19T06:00:00Z", subheaderValues: [ { "fi": "Vapaarahoitteinen", diff --git a/src/core/conversion/translations/defaultTranslations.ts b/src/core/conversion/translations/defaultTranslations.ts index f8ba659..3d8b0a6 100644 --- a/src/core/conversion/translations/defaultTranslations.ts +++ b/src/core/conversion/translations/defaultTranslations.ts @@ -330,6 +330,11 @@ export const DefaultTranslations: TTranslations = { 'fi': 'Poista kuviosta symbolit', 'sv': 'Ta bort symbolerna från diagrammet', 'en': 'Remove symbols from the figure' + }, + lastUpdated: { + 'fi': 'Päivitetty', + 'sv': 'Uppdaterad', + 'en': 'Updated' } } diff --git a/src/core/conversion/translations/translationManager.test.ts b/src/core/conversion/translations/translationManager.test.ts index 2e138ee..2832461 100644 --- a/src/core/conversion/translations/translationManager.test.ts +++ b/src/core/conversion/translations/translationManager.test.ts @@ -199,6 +199,9 @@ const mockTranslationPackage: TTranslationPackage = { }, toggleAccessibilityModeOff: { 'foo': 'Piilota kuviosta symbolit', + }, + lastUpdated: { + 'foo': 'Päivitetty: {date}', } }, ArrayTranslations: { diff --git a/src/core/conversion/translations/translationTypes.ts b/src/core/conversion/translations/translationTypes.ts index aa8e56a..55186e4 100644 --- a/src/core/conversion/translations/translationTypes.ts +++ b/src/core/conversion/translations/translationTypes.ts @@ -67,6 +67,7 @@ export type TTranslations = { dataMissing: TMultiLanguageString; toggleAccessibilityModeOn: TMultiLanguageString; toggleAccessibilityModeOff: TMultiLanguageString; + lastUpdated: TMultiLanguageString; } export type TArrayTranslations = { diff --git a/src/core/conversion/viewUtils.test.ts b/src/core/conversion/viewUtils.test.ts index b655b7c..5c71407 100644 --- a/src/core/conversion/viewUtils.test.ts +++ b/src/core/conversion/viewUtils.test.ts @@ -912,6 +912,7 @@ describe('horizontal bar chart view conversion', () => { sv: 'PxVisualizer-sv' } ], + "lastUpdated": "2023-01-19T06:00:00Z", subheaderValues: [], units: [ { @@ -996,6 +997,7 @@ describe('horizontal bar chart view conversion', () => { fi: 'Tiedot 2022Q4 muuttujina Tiedot, Alue, Huoneluku', sv: 'Uppgifter 2022Q4 efter Uppgifter, Område, Antal rum' }, + "lastUpdated": "2023-01-19T06:00:00Z", rowVarNames: [], series: [ { @@ -1147,7 +1149,8 @@ describe('horizontal bar chart view conversion', () => { "fi": "PxVisualizer-fi", "sv": "PxVisualizer-sv", }, - ], + ], + lastUpdated: "2022-03-17T06:00:00Z", subheaderValues: [], units: [ { @@ -1214,6 +1217,7 @@ describe('horizontal bar chart view conversion', () => { "fi": "Päästö, tuhatta tonnia CO2-ekv. 2020 muuttujina Päästöluokka, Kasvihuonekaasu", "sv": "Utsläpp, tusen ton CO2-ekv. 2020 efter Utsläppsklass, Växthusgas", }, + lastUpdated: "2022-03-17T06:00:00Z", rowVarNames: [ { "en": "Emission category", @@ -1302,7 +1306,6 @@ describe('line chart view conversion', () => { it('returns multiline linechart view', () => { const resultView: View = convertPxGrafResponseToView(LINE_CHART_WITH_QUARTER_SERIES.pxGraphData, {}); const expectedView: View = { - tableReferenceName: "table.px", seriesType: ESeriesType.Time, visualizationSettings: { @@ -1560,6 +1563,7 @@ describe('line chart view conversion', () => { "fi": "Neliövuokra (eur/m2), Yksiöt, Vapaarahoitteinen 2015Q1-2022Q4 muuttujana Alue", "sv": "Kvadratmeterspris (eur/m2), Enrumslägenhet, Fri finansierad 2015Q1-2022Q4 efter Område", }, + lastUpdated: "2023-01-19T06:00:00Z", "series": [ { "rowNameGroup": [ @@ -1983,6 +1987,7 @@ describe('line chart view conversion', () => { "fi": "Neliövuokra (eur/m2), Vapaarahoitteinen 2015Q1-2022Q4 muuttujina Alue, Huoneluku", "sv": "Kvadratmeterspris (eur/m2), Fri finansierad 2015Q1-2022Q4 efter Område, Antal rum", }, + lastUpdated: "2023-01-19T06:00:00Z", series: [ { rowNameGroup: [ @@ -2707,6 +2712,7 @@ describe('line chart view conversion', () => { "fi": "Neliövuokra (eur/m2), Vapaarahoitteinen 2015Q1-2022Q4 muuttujina Alue, Huoneluku", "sv": "Kvadratmeterspris (eur/m2), Fri finansierad 2015Q1-2022Q4 efter Område, Antal rum", }, + lastUpdated: "2023-01-19T06:00:00Z", series: [ { rowNameGroup: [ diff --git a/src/core/conversion/viewUtils.ts b/src/core/conversion/viewUtils.ts index e33eea2..3adaf4e 100644 --- a/src/core/conversion/viewUtils.ts +++ b/src/core/conversion/viewUtils.ts @@ -72,12 +72,15 @@ function convert(responseObj: IQueryVisualizationResponse, selectedValueCodes: T const rowVarNames = getVariableNames(directionlessMultiselectVarNames, metaData) .concat(getVariableNames(responseObj.rowVariableCodes, responseObj.metaData)); + const lastUpdated = getLastUpdated(contentVar, selectedValueCodes); + return { header: responseObj.header, tableReferenceName: responseObj.tableReference.name, subheaderValues: getSubheaderValues(selectableVariables, selectedValueCodes, selectedValueAmounts), units: getUnitInformation(contentVar, selectedValueCodes), sources: getContentProperty(contentVar, selectedValueCodes, (cc) => cc?.source ?? Translations.empty), + lastUpdated: lastUpdated, columnNameGroups: unsortedSeries.columnNameGroups, series: unsortedSeries.series, rowVarNames: rowVarNames, @@ -167,7 +170,43 @@ function getContentProperty( } } -function getSeriesType (varCodes: string[], meta: IVariableMeta[]) { +function getLastUpdated( + contentVar: IVariableMeta, + selectedValueCodes: TVariableSelections +): string { + let dates: (string | undefined)[]; + + if (contentVar.code in selectedValueCodes) { + dates = contentVar.values + .filter(v => selectedValueCodes[contentVar.code].includes(v.code)) + .map(cvv => cvv.contentComponent?.lastUpdated) + .filter(onlyUnique); + } else { + dates = contentVar.values + .map(cvv => cvv.contentComponent?.lastUpdated) + .filter(onlyUnique); + } + + // Filter out undefined values and get the most recent date + const validDates = dates.filter((date): date is string => date !== undefined); + + if (validDates.length === 0) { + return ''; + } + + if (validDates.length === 1) { + return validDates[0]; + } + + // Find the most recent date + return validDates.reduce((latest, current) => { + const latestDate = new Date(latest); + const currentDate = new Date(current); + return currentDate > latestDate ? current : latest; + }, ''); +} + +function getSeriesType(varCodes: string[], meta: IVariableMeta[]) { if (varCodes.length > 1 || varCodes.length === 0) { return ESeriesType.Nominal; } diff --git a/src/core/highcharts/drawChart.ts b/src/core/highcharts/drawChart.ts index 24dcbd9..dcd71a1 100644 --- a/src/core/highcharts/drawChart.ts +++ b/src/core/highcharts/drawChart.ts @@ -34,7 +34,7 @@ export const drawChart = ( Highcharts.setOptions(defaultTheme(validLocale, options?.fontFamily)); const variableSelections = extractSelectableVariableValues(pxGraphData.selectableVariableCodes, pxGraphData.metaData, pxGraphData.visualizationSettings.defaultSelectableVariableCodes, selectedVariableCodes); const view = convertPxGrafResponseToView(pxGraphData, variableSelections); + const highChartOptions = convertPxGraphDataToChartOptions(validLocale, view, options); - return Highcharts.chart(container, highChartOptions); } \ No newline at end of file diff --git a/src/core/tables/htmlTable.ts b/src/core/tables/htmlTable.ts index d8c4b2c..b2aad2c 100644 --- a/src/core/tables/htmlTable.ts +++ b/src/core/tables/htmlTable.ts @@ -1,10 +1,10 @@ -import { getFormattedUnits } from "../chartOptions/Utility/formatters"; +import { getFormattedUnits, getFormattedLastUpdatedText } from "../chartOptions/Utility/formatters"; import { Translations } from "../conversion/translations"; import { TMultiLanguageString } from "../types/queryVisualizationResponse"; import { IDataSeries, View } from "../types/view"; import { formatMissingData, formatNumericValue } from "./tableUtils"; -export function renderHtmlTable(view: View, locale: string, showTitles: boolean, showUnits: boolean, showSources: boolean, containerId: string, footnote?: string): void { +export function renderHtmlTable(view: View, locale: string, showTitles: boolean, showUnits: boolean, showSources: boolean, containerId: string, footnote?: string, showLastUpdated?: boolean): void { const container = document.getElementById(containerId); if (!container) throw new Error("No container with matching id found in the DOM tree"); @@ -43,6 +43,16 @@ export function renderHtmlTable(view: View, locale: string, showTitles: boolean, container.append(pFootnote); } + // Last Updated + if (showLastUpdated && view.lastUpdated) { + const pLastUpdated = document.createElement('p'); + const lastUpdatedText = getFormattedLastUpdatedText(view.lastUpdated, locale); + if (lastUpdatedText) { + pLastUpdated.append(lastUpdatedText); + container.append(pLastUpdated); + } + } + // Sources if (showSources) { const pSources = document.createElement('p'); @@ -160,10 +170,10 @@ const calculateColSpans = (columnNameGroups: TMultiLanguageString[][]): number[] const colSpans: number[] = Array(columnNameGroups[0].length).fill(1); for (let row = 0; row < columnNameGroups[0].length; row++) { - for (let col = 0; col < columnNameGroups.length - 1; col++) { - if (compare(columnNameGroups[col][row], columnNameGroups[col + 1][row])) colSpans[row]++; - else break; - } + for (let col = 0; col < columnNameGroups.length - 1; col++) { + if (compare(columnNameGroups[col][row], columnNameGroups[col + 1][row])) colSpans[row]++; + else break; + } } return colSpans; } \ No newline at end of file diff --git a/src/core/types/chartOptions.ts b/src/core/types/chartOptions.ts index 7286763..0609b83 100644 --- a/src/core/types/chartOptions.ts +++ b/src/core/types/chartOptions.ts @@ -5,4 +5,5 @@ export interface IChartOptions { accessibilityMode?: boolean; showTitle?: boolean; fontFamily?: string; + showLastUpdated?: boolean; } \ No newline at end of file diff --git a/src/core/types/view.ts b/src/core/types/view.ts index e6bbfc3..0c6f331 100644 --- a/src/core/types/view.ts +++ b/src/core/types/view.ts @@ -17,6 +17,7 @@ export interface View { subheaderValues: TMultiLanguageString[], units: IUnitInfo[], sources: TMultiLanguageString[], + lastUpdated?: string, columnNameGroups: TMultiLanguageString[][], series: IDataSeries[], colVarNames: TMultiLanguageString[], diff --git a/src/react/components/chart/__snapshots__/chart.test.tsx.snap b/src/react/components/chart/__snapshots__/chart.test.tsx.snap index a392720..dcc79a2 100644 --- a/src/react/components/chart/__snapshots__/chart.test.tsx.snap +++ b/src/react/components/chart/__snapshots__/chart.test.tsx.snap @@ -243,6 +243,133 @@ exports[`Rendering test renders chart data correctly with hidden title 1`] = ` `; +exports[`Rendering test renders chart data correctly with last updated date 1`] = ` + +
+
+
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + +
+ Lukumäärä, Pääkaupunkiseutu (PKS), Yksiöt 2015Q1-2015Q2 muuttujana Rahoitusmuoto +
+ + 2015Q1 + + 2015Q2 +
+ Vapaarahoitteinen + + 11 096 + + 11 625 +
+ ARA + + 4 845 + + 5 174 +
+

+ Päivitetty: 19.1.2023 +

+

+ Lähde: PxVisualizer-fi +

+
+
+
+ +`; + exports[`Rendering test renders error component on broken data 1`] = `
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Tiedot 2022Q1-2022Q4 muuttujina Tiedot, Alue, Huoneluku, Rahoitusmuoto +
+ + Yksiöt + + Kaksiot +
+ Lukumäärä + + Neliövuokra (eur/m2) + + Lukumäärä + + Neliövuokra (eur/m2) +
+ Helsinki + + 2022Q1 + + Vapaarahoitteinen + + 13 021 + + 26,64 + + 10 080 + + 20,22 +
+ ARA + + 3 810 + + 15,90 + + 6 176 + + 13,74 +
+ 2022Q2 + + Vapaarahoitteinen + + 11 231 + + 26,82 + + 9 326 + + 20,45 +
+ ARA + + 3 552 + + 15,97 + + 5 749 + + 13,78 +
+ 2022Q3 + + Vapaarahoitteinen + + 10 269 + + 26,93 + + 8 907 + + 20,59 +
+ ARA + + 3 249 + + 15,99 + + 5 280 + + 13,79 +
+ 2022Q4 + + Vapaarahoitteinen + + 10 272 + + 26,93 + + 8 805 + + 20,66 +
+ ARA + + 3 323 + + 16,08 + + 5 265 + + 13,80 +
+ Vantaa + + 2022Q1 + + Vapaarahoitteinen + + 3 043 + + 23,48 + + 5 261 + + 17,62 +
+ ARA + + 941 + + 16,13 + + 2 083 + + 13,96 +
+ 2022Q2 + + Vapaarahoitteinen + + 2 679 + + 23,64 + + 5 017 + + 17,79 +
+ ARA + + 846 + + 16,05 + + 1 902 + + 13,91 +
+ 2022Q3 + + Vapaarahoitteinen + + 2 533 + + 23,68 + + 4 925 + + 17,85 +
+ ARA + + 763 + + 15,97 + + 1 778 + + 13,90 +
+ 2022Q4 + + Vapaarahoitteinen + + 2 548 + + 23,77 + + 4 882 + + 17,99 +
+ ARA + + 767 + + 15,98 + + 1 744 + + 13,97 +
+ Turku + + 2022Q1 + + Vapaarahoitteinen + + 5 534 + + 18,16 + + 4 267 + + 14,07 +
+ ARA + + 1 292 + + 13,91 + + 1 782 + + 11,81 +
+ 2022Q2 + + Vapaarahoitteinen + + 4 437 + + 18,32 + + 3 666 + + 14,27 +
+ ARA + + 1 149 + + 14,02 + + 1 642 + + 11,87 +
+ 2022Q3 + + Vapaarahoitteinen + + 4 014 + + 18,44 + + 3 329 + + 14,37 +
+ ARA + + 1 107 + + 14,06 + + 1 554 + + 11,86 +
+ 2022Q4 + + Vapaarahoitteinen + + 4 195 + + 18,53 + + 3 414 + + 14,48 +
+ ARA + + 1 161 + + 14,11 + + 1 557 + + 11,96 +
+

+ Päivitetty: 19.1.2023 +

+

+ Lähde: PxVisualizer-fi +

+
+
+ +`; diff --git a/src/react/components/chart/chart.test.tsx b/src/react/components/chart/chart.test.tsx index 855b07b..87277f1 100644 --- a/src/react/components/chart/chart.test.tsx +++ b/src/react/components/chart/chart.test.tsx @@ -42,6 +42,16 @@ describe('Rendering test', () => { expect(asFragment()).toMatchSnapshot(); }); + it('renders chart data correctly with last updated date', () => { + const { asFragment } = render( + ); + expect(asFragment()).toMatchSnapshot(); + }); + it('renders table data correctly', () => { const { asFragment } = render( { expect(asFragment()).toMatchSnapshot(); }); + it('renders table data correctly with last updated date', () => { + const { asFragment } = render( + ); + expect(asFragment()).toMatchSnapshot(); + }); + it('renders error component on broken data', () => { const spy = jest.spyOn(console, "error"); spy.mockImplementation(() => { }); diff --git a/src/react/components/chart/chart.tsx b/src/react/components/chart/chart.tsx index 6ff8f46..1ea327a 100644 --- a/src/react/components/chart/chart.tsx +++ b/src/react/components/chart/chart.tsx @@ -73,6 +73,7 @@ export interface IChartProps { showTableSources?: boolean; footnote?: string; fontFamily?: string; + showLastUpdated?: boolean; } const ReactChart: React.FC = ({ @@ -86,7 +87,8 @@ const ReactChart: React.FC = ({ showTitles, showTableUnits, showTableSources, - fontFamily}) => { + fontFamily, + showLastUpdated = false}) => { const validLocale = formatLocale(locale); initializeHighcharts(validLocale, fontFamily); @@ -154,7 +156,8 @@ const ReactChart: React.FC = ({ const highChartOptions = convertPxGraphDataToChartOptions(validLocale, view, { accessibilityMode: accessibilityMode, showTitle: showTitles ?? true, - fontFamily: fontFamily + fontFamily: fontFamily, + showLastUpdated: showLastUpdated }); return ( @@ -173,7 +176,7 @@ const ReactChart: React.FC = ({ /> - + ); @@ -189,7 +192,7 @@ const ReactChart: React.FC = ({ } - + ); } diff --git a/src/react/components/chart/tableView.tsx b/src/react/components/chart/tableView.tsx index 6cf8157..5ffd44d 100644 --- a/src/react/components/chart/tableView.tsx +++ b/src/react/components/chart/tableView.tsx @@ -10,15 +10,16 @@ export interface ITableViewProps { showUnits?: boolean; showSources?: boolean; footnote?: string; + showLastUpdated?: boolean; } -export const TableView: React.FC = ({ view, locale, footnote, showTitles = true, showUnits = true, showSources = true }) => { +export const TableView: React.FC = ({ view, locale, footnote, showTitles = true, showUnits = true, showSources = true, showLastUpdated = false }) => { const uuid = useMemo(() => uuidv4(), [view, locale]); React.useEffect(() => { document.getElementById(uuid)?.replaceChildren(); - renderHtmlTable(view, locale, showTitles, showUnits, showSources, uuid, footnote); - }, [view, locale, showTitles, showUnits, showSources, footnote]); + renderHtmlTable(view, locale, showTitles, showUnits, showSources, uuid, footnote, showLastUpdated); + }, [view, locale, showTitles, showUnits, showSources, footnote, showLastUpdated]); return
; } \ No newline at end of file diff --git a/src/stories/chartstories/grouphorizontalbar.stories.tsx b/src/stories/chartstories/grouphorizontalbar.stories.tsx index 8effb9f..9752ef4 100644 --- a/src/stories/chartstories/grouphorizontalbar.stories.tsx +++ b/src/stories/chartstories/grouphorizontalbar.stories.tsx @@ -15,7 +15,7 @@ export default { } satisfies Meta; export const Simple = { - name: 'Simple', + name: 'Simple (with last updated)', args: GROUP_HORIZONTAL_BAR_CHART, } satisfies StoryObj; diff --git a/src/stories/chartstories/groupverticalbar.stories.tsx b/src/stories/chartstories/groupverticalbar.stories.tsx index 592d00f..366157d 100644 --- a/src/stories/chartstories/groupverticalbar.stories.tsx +++ b/src/stories/chartstories/groupverticalbar.stories.tsx @@ -14,7 +14,7 @@ export default { } satisfies Meta; export const Simple = { - name: 'Simple', + name: 'Simple (with last updated)', args: GROUP_VERTICAL_BAR_CHART, } satisfies StoryObj; diff --git a/src/stories/chartstories/horizontal.stories.tsx b/src/stories/chartstories/horizontal.stories.tsx index 66ebb8e..ffcba22 100644 --- a/src/stories/chartstories/horizontal.stories.tsx +++ b/src/stories/chartstories/horizontal.stories.tsx @@ -11,7 +11,7 @@ export default { } satisfies Meta; export const Ascending = { - name: 'Ascending', + name: 'Ascending (with last updated)', args: HORIZONTAL_BAR_CHART_ASCENDING, } satisfies StoryObj; diff --git a/src/stories/chartstories/linechart.stories.tsx b/src/stories/chartstories/linechart.stories.tsx index 3cca429..428c19e 100644 --- a/src/stories/chartstories/linechart.stories.tsx +++ b/src/stories/chartstories/linechart.stories.tsx @@ -20,7 +20,7 @@ export default { } satisfies Meta; export const YearSeries = { - name: 'Year Series', + name: 'Year Series (with last updated)', args: LINE_CHART_WITH_YEAR_SERIES, } satisfies StoryObj; diff --git a/src/stories/chartstories/percenthorizontalbar.stories.tsx b/src/stories/chartstories/percenthorizontalbar.stories.tsx index 67c9bc9..30483dc 100644 --- a/src/stories/chartstories/percenthorizontalbar.stories.tsx +++ b/src/stories/chartstories/percenthorizontalbar.stories.tsx @@ -13,7 +13,7 @@ export default { } satisfies Meta; export const Simple = { - name: 'Simple', + name: 'Simple (with last updated)', args: PERCENT_HORIZONTAL_BAR_CHART, } satisfies StoryObj; diff --git a/src/stories/chartstories/percentverticalbar.stories.tsx b/src/stories/chartstories/percentverticalbar.stories.tsx index 19ffd36..7cf25ac 100644 --- a/src/stories/chartstories/percentverticalbar.stories.tsx +++ b/src/stories/chartstories/percentverticalbar.stories.tsx @@ -12,7 +12,7 @@ export default { } satisfies Meta; export const Simple = { - name: 'Simple', + name: 'Simple (with last updated)', args: PERCENT_VERTICAL_BAR_CHART, } satisfies StoryObj; diff --git a/src/stories/chartstories/pie.stories.tsx b/src/stories/chartstories/pie.stories.tsx index 3e5792d..3f570cf 100644 --- a/src/stories/chartstories/pie.stories.tsx +++ b/src/stories/chartstories/pie.stories.tsx @@ -9,6 +9,6 @@ export default { } satisfies Meta; export const SelectableTime = { - name: 'Selectable time', + name: 'Selectable time (with last updated)', args: PIE_CHART_SELECTABLE_TIME, } satisfies StoryObj; diff --git a/src/stories/chartstories/pyramid.stories.tsx b/src/stories/chartstories/pyramid.stories.tsx index f8f96a6..6515aaa 100644 --- a/src/stories/chartstories/pyramid.stories.tsx +++ b/src/stories/chartstories/pyramid.stories.tsx @@ -13,7 +13,7 @@ export default { } satisfies Meta; export const Simple = { - name: 'Simple', + name: 'Simple (with last updated)', args: PYRAMID_CHART, } satisfies StoryObj; diff --git a/src/stories/chartstories/scatterplot.stories.tsx b/src/stories/chartstories/scatterplot.stories.tsx index 248456b..899203b 100644 --- a/src/stories/chartstories/scatterplot.stories.tsx +++ b/src/stories/chartstories/scatterplot.stories.tsx @@ -12,7 +12,7 @@ export default { } satisfies Meta; export const Simple = { - name: 'Simple', + name: 'Simple (with last updated)', args: SCATTER_PLOT, } satisfies StoryObj; diff --git a/src/stories/chartstories/stackedhorizontalbar.stories.tsx b/src/stories/chartstories/stackedhorizontalbar.stories.tsx index ed062cb..181000f 100644 --- a/src/stories/chartstories/stackedhorizontalbar.stories.tsx +++ b/src/stories/chartstories/stackedhorizontalbar.stories.tsx @@ -13,7 +13,7 @@ export default { } satisfies Meta; export const Simple = { - name: 'Simple', + name: 'Simple (with last updated)', args: STACKED_HORIZONTAL_BAR_CHART, } satisfies StoryObj; diff --git a/src/stories/chartstories/stackedverticalbar.stories.tsx b/src/stories/chartstories/stackedverticalbar.stories.tsx index 75dc0de..c64b845 100644 --- a/src/stories/chartstories/stackedverticalbar.stories.tsx +++ b/src/stories/chartstories/stackedverticalbar.stories.tsx @@ -14,7 +14,7 @@ export default { } satisfies Meta; export const Simple = { - name: 'Simple', + name: 'Simple (with last updated)', args: STACKED_VERTICAL_BAR_CHART, } satisfies StoryObj; diff --git a/src/stories/chartstories/verticalbar.stories.tsx b/src/stories/chartstories/verticalbar.stories.tsx index 2b7bf0d..4e6e26f 100644 --- a/src/stories/chartstories/verticalbar.stories.tsx +++ b/src/stories/chartstories/verticalbar.stories.tsx @@ -17,7 +17,7 @@ export default { } satisfies Meta; export const Simple = { - name: 'Simple', + name: 'Simple (with last updated)', args: VERTICAL_BAR_CHART, } satisfies StoryObj; diff --git a/src/stories/fixtures/groupHorizontalBarChart.ts b/src/stories/fixtures/groupHorizontalBarChart.ts index 8fc2aec..7606141 100644 --- a/src/stories/fixtures/groupHorizontalBarChart.ts +++ b/src/stories/fixtures/groupHorizontalBarChart.ts @@ -261,10 +261,10 @@ export const GROUP_HORIZONTAL_BAR_CHART_WITH_SELECTABLES: { pxGraphData: IQueryV defaultSelectableVariableCodes: null }, }, - selectedVariableCodes: { 'Alue': ['pks'], 'Huoneluku': ['01'], 'Tiedot': ['lkm'] } + selectedVariableCodes: { 'Alue': ['pks'], 'Huoneluku': ['01'], 'Tiedot': ['lkm'] }, }; -export const GROUP_HORIZONTAL_BAR_CHART: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: { [key: string]: string[] } | undefined } = { +export const GROUP_HORIZONTAL_BAR_CHART: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: { [key: string]: string[] } | undefined, showLastUpdated: boolean } = { pxGraphData: { tableReference: { name: "table.px", hierarchy: ["foo", "bar"] }, data: [ @@ -446,7 +446,8 @@ export const GROUP_HORIZONTAL_BAR_CHART: { pxGraphData: IQueryVisualizationRespo timeVariableIntervals: ETimeVariableInterval.Quarter, } }, - selectedVariableCodes: undefined + selectedVariableCodes: undefined, + showLastUpdated: true } export const GROUP_HORIZONTAL_BAR_CHART_WITH_SUM_SORTING: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: { [key: string]: string[] } | undefined } = diff --git a/src/stories/fixtures/groupVerticalBarChart.ts b/src/stories/fixtures/groupVerticalBarChart.ts index 44175ad..264da07 100644 --- a/src/stories/fixtures/groupVerticalBarChart.ts +++ b/src/stories/fixtures/groupVerticalBarChart.ts @@ -2,7 +2,7 @@ import { EVariableType, EVisualizationType, IQueryVisualizationResponse } from " import { ETimeVariableInterval } from "../../core/types/queryVisualizationResponse"; import { TVariableSelections } from "../../core/types/variableSelections"; -export const GROUP_VERTICAL_BAR_CHART: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { +export const GROUP_VERTICAL_BAR_CHART: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined, showLastUpdated: boolean } = { pxGraphData: { tableReference: { name: "table.px", hierarchy: ["foo", "bar"] }, data: [ @@ -185,7 +185,8 @@ export const GROUP_VERTICAL_BAR_CHART: { pxGraphData: IQueryVisualizationRespons defaultSelectableVariableCodes: null } }, - selectedVariableCodes: undefined + selectedVariableCodes: undefined, + showLastUpdated: true }; export const GROUP_VERTICAL_BAR_CHART_WITH_SELECTABLES: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: { [key: string]: string[] } | undefined } = { diff --git a/src/stories/fixtures/horizontalBarChart.ts b/src/stories/fixtures/horizontalBarChart.ts index c35e176..f05f971 100644 --- a/src/stories/fixtures/horizontalBarChart.ts +++ b/src/stories/fixtures/horizontalBarChart.ts @@ -2,7 +2,7 @@ import { EVariableType, EVisualizationType, IQueryVisualizationResponse } from " import { ETimeVariableInterval } from "../../core/types/queryVisualizationResponse"; import { TVariableSelections } from "../../core/types/variableSelections"; -export const HORIZONTAL_BAR_CHART_ASCENDING: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { +export const HORIZONTAL_BAR_CHART_ASCENDING: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined, showLastUpdated: boolean } = { pxGraphData: { tableReference: { name: "table.px", hierarchy: ["foo", "bar"] }, data: [ @@ -205,7 +205,8 @@ export const HORIZONTAL_BAR_CHART_ASCENDING: { pxGraphData: IQueryVisualizationR defaultSelectableVariableCodes: null } }, - selectedVariableCodes: undefined + selectedVariableCodes: undefined, + showLastUpdated: true } export const SELECTABLE_HORIZONTAL_BAR_CHART: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { diff --git a/src/stories/fixtures/lineChart.ts b/src/stories/fixtures/lineChart.ts index d36c1d8..e182cbc 100644 --- a/src/stories/fixtures/lineChart.ts +++ b/src/stories/fixtures/lineChart.ts @@ -2,7 +2,7 @@ import { EVariableType, EVisualizationType, IQueryVisualizationResponse } from " import { ETimeVariableInterval } from "../../core/types/queryVisualizationResponse"; import { TVariableSelections } from "../../core/types/variableSelections"; -export const LINE_CHART_WITH_YEAR_SERIES: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { +export const LINE_CHART_WITH_YEAR_SERIES: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined, showLastUpdated: boolean } = { pxGraphData: { tableReference: { name: "table.px", hierarchy: ["foo", "bar"] }, data: [ @@ -2189,7 +2189,8 @@ export const LINE_CHART_WITH_YEAR_SERIES: { pxGraphData: IQueryVisualizationResp cutValueAxis: false } }, - selectedVariableCodes: undefined + selectedVariableCodes: undefined, + showLastUpdated: true }; export const LINE_CHART_WITH_QUARTER_SERIES: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { diff --git a/src/stories/fixtures/percentHorizontalBarChart.ts b/src/stories/fixtures/percentHorizontalBarChart.ts index 5f9d3a2..1c413f3 100644 --- a/src/stories/fixtures/percentHorizontalBarChart.ts +++ b/src/stories/fixtures/percentHorizontalBarChart.ts @@ -2,7 +2,7 @@ import { EVariableType, EVisualizationType, IQueryVisualizationResponse } from " import { ETimeVariableInterval } from "../../core/types/queryVisualizationResponse" import { TVariableSelections } from "../../core/types/variableSelections" -export const PERCENT_HORIZONTAL_BAR_CHART: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { +export const PERCENT_HORIZONTAL_BAR_CHART: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined, showLastUpdated: boolean } = { pxGraphData: { tableReference: { name: "table.px", hierarchy: ["foo", "bar"] }, data: [ @@ -198,7 +198,8 @@ export const PERCENT_HORIZONTAL_BAR_CHART: { pxGraphData: IQueryVisualizationRes "sorting": "1" } }, - selectedVariableCodes: undefined + selectedVariableCodes: undefined, + showLastUpdated: true } export const PERCENT_HORIZONTAL_BAR_CHART_WITH_SELECTABLES: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { diff --git a/src/stories/fixtures/percentVerticalBarChart.ts b/src/stories/fixtures/percentVerticalBarChart.ts index a748440..8b83806 100644 --- a/src/stories/fixtures/percentVerticalBarChart.ts +++ b/src/stories/fixtures/percentVerticalBarChart.ts @@ -2,7 +2,7 @@ import { EVariableType, EVisualizationType, IQueryVisualizationResponse } from " import { ETimeVariableInterval } from "../../core/types/queryVisualizationResponse" import { TVariableSelections } from "../../core/types/variableSelections" -export const PERCENT_VERTICAL_BAR_CHART: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { +export const PERCENT_VERTICAL_BAR_CHART: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined, showLastUpdated: boolean } = { pxGraphData: { tableReference: { name: "table.px", hierarchy: ["foo", "bar"] }, data: [ @@ -240,7 +240,8 @@ export const PERCENT_VERTICAL_BAR_CHART: { pxGraphData: IQueryVisualizationRespo "showLastLabel": false } }, - selectedVariableCodes: undefined + selectedVariableCodes: undefined, + showLastUpdated: true } export const PERCENT_VERTICAL_BAR_CHART_WITH_SELECTABLES: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { diff --git a/src/stories/fixtures/pieChart.ts b/src/stories/fixtures/pieChart.ts index eade5fc..fa02852 100644 --- a/src/stories/fixtures/pieChart.ts +++ b/src/stories/fixtures/pieChart.ts @@ -2,7 +2,7 @@ import { EVariableType, EVisualizationType, IQueryVisualizationResponse } from " import { ETimeVariableInterval } from "../../core/types/queryVisualizationResponse"; import { TVariableSelections } from "../../core/types/variableSelections"; -export const PIE_CHART_SELECTABLE_TIME: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { +export const PIE_CHART_SELECTABLE_TIME: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined, showLastUpdated: boolean } = { pxGraphData: { tableReference: { name: "table.px", hierarchy: ["foo", "bar"] }, data: [ @@ -486,5 +486,6 @@ export const PIE_CHART_SELECTABLE_TIME: { pxGraphData: IQueryVisualizationRespon defaultSelectableVariableCodes: null } }, - selectedVariableCodes: { Vuosi: ['2007'] } + selectedVariableCodes: { Vuosi: ['2007'] }, + showLastUpdated: true }; \ No newline at end of file diff --git a/src/stories/fixtures/pyramidChart.ts b/src/stories/fixtures/pyramidChart.ts index 8f08498..6992b63 100644 --- a/src/stories/fixtures/pyramidChart.ts +++ b/src/stories/fixtures/pyramidChart.ts @@ -2,7 +2,7 @@ import { EVariableType, EVisualizationType, IQueryVisualizationResponse } from " import { ETimeVariableInterval } from "../../core/types/queryVisualizationResponse" import { TVariableSelections } from "../../core/types/variableSelections" -export const PYRAMID_CHART: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { +export const PYRAMID_CHART: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined, showLastUpdated: boolean } = { pxGraphData: { tableReference: { name: "table.px", hierarchy: ["foo", "bar"] }, "data": [ @@ -429,7 +429,8 @@ export const PYRAMID_CHART: { pxGraphData: IQueryVisualizationResponse, selected defaultSelectableVariableCodes: null } }, - selectedVariableCodes: undefined + selectedVariableCodes: undefined, + showLastUpdated: true } export const PYRAMID_CHART_LARGE_DATASET: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { diff --git a/src/stories/fixtures/scatterPlot.ts b/src/stories/fixtures/scatterPlot.ts index 7050e36..ad58b43 100644 --- a/src/stories/fixtures/scatterPlot.ts +++ b/src/stories/fixtures/scatterPlot.ts @@ -2,7 +2,7 @@ import { EVariableType, EVisualizationType, IQueryVisualizationResponse } from " import { ETimeVariableInterval } from "../../core/types/queryVisualizationResponse"; import { TVariableSelections } from "../../core/types/variableSelections"; -export const SCATTER_PLOT: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { +export const SCATTER_PLOT: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined, showLastUpdated: boolean } = { pxGraphData: { tableReference: { name: "table.px", hierarchy: ["foo", "bar"] }, data: [ @@ -588,7 +588,8 @@ export const SCATTER_PLOT: { pxGraphData: IQueryVisualizationResponse, selectedV defaultSelectableVariableCodes: null } }, - selectedVariableCodes: undefined + selectedVariableCodes: undefined, + showLastUpdated: true }; export const SCATTER_PLOT_WITH_SELECTABLES : { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { diff --git a/src/stories/fixtures/stackedHorizontalBarChart.ts b/src/stories/fixtures/stackedHorizontalBarChart.ts index 70ddc12..1228dd8 100644 --- a/src/stories/fixtures/stackedHorizontalBarChart.ts +++ b/src/stories/fixtures/stackedHorizontalBarChart.ts @@ -2,7 +2,7 @@ import { EVariableType, EVisualizationType } from "../../core" import { ETimeVariableInterval, IQueryVisualizationResponse } from "../../core/types/queryVisualizationResponse" import { TVariableSelections } from "../../core/types/variableSelections" -export const STACKED_HORIZONTAL_BAR_CHART: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { +export const STACKED_HORIZONTAL_BAR_CHART: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined, showLastUpdated: boolean } = { pxGraphData: { tableReference: { name: "table.px", hierarchy: ["foo", "bar"] }, data: [ @@ -203,7 +203,8 @@ export const STACKED_HORIZONTAL_BAR_CHART: { pxGraphData: IQueryVisualizationRes defaultSelectableVariableCodes: null } }, - selectedVariableCodes: undefined + selectedVariableCodes: undefined, + showLastUpdated: true } export const STACKED_HORIZONTAL_BAR_CHART_WITH_SELECTABLES: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections } = { diff --git a/src/stories/fixtures/stackedVerticalBarChart.ts b/src/stories/fixtures/stackedVerticalBarChart.ts index 90c4cd1..292e791 100644 --- a/src/stories/fixtures/stackedVerticalBarChart.ts +++ b/src/stories/fixtures/stackedVerticalBarChart.ts @@ -2,7 +2,7 @@ import { EVariableType, EVisualizationType, IQueryVisualizationResponse } from " import { ETimeVariableInterval } from "../../core/types/queryVisualizationResponse" import { TVariableSelections } from "../../core/types/variableSelections" -export const STACKED_VERTICAL_BAR_CHART: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { +export const STACKED_VERTICAL_BAR_CHART: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined, showLastUpdated: boolean } = { pxGraphData: { tableReference: { name: "table.px", hierarchy: ["foo", "bar"] }, "data": [ @@ -185,7 +185,8 @@ export const STACKED_VERTICAL_BAR_CHART: { pxGraphData: IQueryVisualizationRespo defaultSelectableVariableCodes: null } }, - selectedVariableCodes: undefined + selectedVariableCodes: undefined, + showLastUpdated: true } export const STACKED_VERTICAL_BAR_CHART_WITH_SELECTABLES: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { diff --git a/src/stories/fixtures/table.ts b/src/stories/fixtures/table.ts index 3f0ec74..0db43a1 100644 --- a/src/stories/fixtures/table.ts +++ b/src/stories/fixtures/table.ts @@ -679,7 +679,7 @@ export const TABLE_WITH_ONLY_ROW_VARIABLES: { pxGraphData: IQueryVisualizationRe selectedVariableCodes: { 'Vuosineljännes': ['2015Q1'], 'Alue': ['pks'], 'Rahoitusmuoto': ['1'] } }; -export const TABLE_WITH_ROW_AND_COLUMN_VARIABLES: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: {[key: string]: string[]}} = { +export const TABLE_WITH_ROW_AND_COLUMN_VARIABLES: { pxGraphData: IQueryVisualizationResponse, showLastUpdated: boolean, selectedVariableCodes: {[key: string]: string[]}} = { pxGraphData: { tableReference: { name: "table.px", hierarchy: ["foo", "bar"] }, data: [ @@ -1035,5 +1035,6 @@ export const TABLE_WITH_ROW_AND_COLUMN_VARIABLES: { pxGraphData: IQueryVisualiza "cutValueAxis": false } }, - selectedVariableCodes: { 'Vuosineljännes': ['2015Q1'], 'Alue': ['pks'], 'Rahoitusmuoto': ['1'] } + selectedVariableCodes: { 'Vuosineljännes': ['2015Q1'], 'Alue': ['pks'], 'Rahoitusmuoto': ['1'] }, + showLastUpdated: true }; \ No newline at end of file diff --git a/src/stories/fixtures/verticalBarChart.ts b/src/stories/fixtures/verticalBarChart.ts index 3b29524..d491999 100644 --- a/src/stories/fixtures/verticalBarChart.ts +++ b/src/stories/fixtures/verticalBarChart.ts @@ -383,7 +383,7 @@ export const VERTICAL_BAR_CHART_WITH_CUSTOM_MENU_ITEMS: { ] }; -export const VERTICAL_BAR_CHART: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { +export const VERTICAL_BAR_CHART: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined, showLastUpdated: boolean } = { pxGraphData: { tableReference: { name: "table.px", hierarchy: ["foo", "bar"] }, columnVariableCodes: ['Vuosi'], @@ -488,7 +488,8 @@ export const VERTICAL_BAR_CHART: { pxGraphData: IQueryVisualizationResponse, sel timeSeriesStartingPoint: '1970-01-01T00:00:00' } }, - selectedVariableCodes: undefined + selectedVariableCodes: undefined, + showLastUpdated: true }; export const VERTICAL_BAR_CHART_WITH_EXCESSIVE_PRECISION: { pxGraphData: IQueryVisualizationResponse, selectedVariableCodes: TVariableSelections | undefined } = { diff --git a/src/stories/tablestories/table.stories.tsx b/src/stories/tablestories/table.stories.tsx index 1838913..f7189a9 100644 --- a/src/stories/tablestories/table.stories.tsx +++ b/src/stories/tablestories/table.stories.tsx @@ -14,7 +14,7 @@ export default { } satisfies Meta; export const BAsic = { - name: 'Basic', + name: 'Basic (with last updated)', args: TABLE_WITH_ROW_AND_COLUMN_VARIABLES, } satisfies StoryObj; From e6f09b8b7263bc16617a642ed88ae7cf3c483488 Mon Sep 17 00:00:00 2001 From: Sakari Malkki Date: Tue, 28 Oct 2025 12:47:34 +0200 Subject: [PATCH 02/21] Add test coverage, fix sonar complaint about too many parameters --- src/core/conversion/viewUtils.test.ts | 63 ++++++++++++- src/core/conversion/viewUtils.ts | 78 ++++++++-------- src/core/tables/htmlTable.test.ts | 91 +++++++++++++++++-- src/core/tables/htmlTable.ts | 11 ++- src/core/types/index.ts | 1 + src/core/types/tableOptions.ts | 6 ++ src/react/components/chart/chart.tsx | 52 ++++++----- src/react/components/chart/tableView.test.tsx | 9 +- src/react/components/chart/tableView.tsx | 12 +-- 9 files changed, 241 insertions(+), 82 deletions(-) create mode 100644 src/core/types/tableOptions.ts diff --git a/src/core/conversion/viewUtils.test.ts b/src/core/conversion/viewUtils.test.ts index 5c71407..eb7fc66 100644 --- a/src/core/conversion/viewUtils.test.ts +++ b/src/core/conversion/viewUtils.test.ts @@ -15,7 +15,7 @@ import { } from './fixtures/percentVerticalBarChart'; import { SELECTABLE_TABLE_WITH_MISSING_DATA } from './fixtures/tableChart'; import { ASCENDING, DESCENDING, SUM, REVERSED, NO_SORTING } from './viewSorting'; -import { buildSeries, convertPxGrafResponseToView, convertToRelative } from './viewUtils'; +import { buildSeries, convertPxGrafResponseToView, convertToRelative, getLastUpdated } from './viewUtils'; const createContentComponent = (overrides?: Partial) => { const id = Math.random().toString(36).substring(2, 15); @@ -3605,3 +3605,64 @@ describe('table conversion', () => { })).toThrow("Provided selected value code can not be found from the metadata"); }); }); + +describe('getLastUpdated', () => { + it('returns the most recent last updated date from multiple content dimension values', () => { + const expectedLastUpdated: string = "2024-05-10T12:00:00"; + const contentVar: IVariableMeta = { + name: { + "fi": "Tiedot", + "en": "Information", + }, + type: EVariableType.Content, + note: null, + code: "Tiedot", + values: [ + { + code: "tiedot-1", + name: { + "fi": "Tiedot 1", + "en": "Tiedot 1 en" + }, + isSum: false, + note: null, + contentComponent: { + source: { + "fi": "Source 1", + "en": "Source 1 en" + }, + unit: { + "fi": "unit 1", + "en": "unit 1 en", + }, + numberOfDecimals: 2, + lastUpdated: "2023-05-10T12:00:00" // Older value + } + }, + { + code: "tiedot-2", + name: { + "fi": "Tiedot 2", + "en": "Tiedot 2 en" + }, + isSum: false, + note: null, + contentComponent: { + source: { + "fi": "Source 2", + "en": "Source 12 en" + }, + unit: { + "fi": "unit 2", + "en": "unit 2 en", + }, + numberOfDecimals: 1, + lastUpdated: expectedLastUpdated // Newer, expected value + } + } + ] + }; + const lastUpdated = getLastUpdated(contentVar, {}); + expect(lastUpdated).toBe(expectedLastUpdated); + }); +}); diff --git a/src/core/conversion/viewUtils.ts b/src/core/conversion/viewUtils.ts index 3adaf4e..39c2dd0 100644 --- a/src/core/conversion/viewUtils.ts +++ b/src/core/conversion/viewUtils.ts @@ -105,6 +105,48 @@ export function buildSeries(responseObj: IQueryVisualizationResponse, selectedVa }; } +/** + * Get the last updated date from the content variable values. + * @param contentVar Content variable metadata + * @param selectedValueCodes Selected selectable value codes if any + * @returns Last updated date as a string + */ +export function getLastUpdated( + contentVar: IVariableMeta, + selectedValueCodes: TVariableSelections +): string { + let dates: (string | undefined)[]; + + if (contentVar.code in selectedValueCodes) { + dates = contentVar.values + .filter(v => selectedValueCodes[contentVar.code].includes(v.code)) + .map(cvv => cvv.contentComponent?.lastUpdated) + .filter(onlyUnique); + } else { + dates = contentVar.values + .map(cvv => cvv.contentComponent?.lastUpdated) + .filter(onlyUnique); + } + + // Filter out undefined values and get the most recent date + const validDates = dates.filter((date): date is string => date !== undefined); + + if (validDates.length === 0) { + return ''; + } + + if (validDates.length === 1) { + return validDates[0]; + } + + // Find the most recent date + return validDates.reduce((latest, current) => { + const latestDate = new Date(latest); + const currentDate = new Date(current); + return currentDate > latestDate ? current : latest; + }, validDates[0]); +} + function getVariableNames(varCodes: string[], meta: IVariableMeta[]): TMultiLanguageString[] { return varCodes.reduce((acc: TMultiLanguageString[], code: string) => { const name = meta.find(v => v.code === code)?.name; @@ -170,42 +212,6 @@ function getContentProperty( } } -function getLastUpdated( - contentVar: IVariableMeta, - selectedValueCodes: TVariableSelections -): string { - let dates: (string | undefined)[]; - - if (contentVar.code in selectedValueCodes) { - dates = contentVar.values - .filter(v => selectedValueCodes[contentVar.code].includes(v.code)) - .map(cvv => cvv.contentComponent?.lastUpdated) - .filter(onlyUnique); - } else { - dates = contentVar.values - .map(cvv => cvv.contentComponent?.lastUpdated) - .filter(onlyUnique); - } - - // Filter out undefined values and get the most recent date - const validDates = dates.filter((date): date is string => date !== undefined); - - if (validDates.length === 0) { - return ''; - } - - if (validDates.length === 1) { - return validDates[0]; - } - - // Find the most recent date - return validDates.reduce((latest, current) => { - const latestDate = new Date(latest); - const currentDate = new Date(current); - return currentDate > latestDate ? current : latest; - }, ''); -} - function getSeriesType(varCodes: string[], meta: IVariableMeta[]) { if (varCodes.length > 1 || varCodes.length === 0) { return ESeriesType.Nominal; diff --git a/src/core/tables/htmlTable.test.ts b/src/core/tables/htmlTable.test.ts index 68ab874..0a65581 100644 --- a/src/core/tables/htmlTable.test.ts +++ b/src/core/tables/htmlTable.test.ts @@ -5,6 +5,7 @@ import { extractSelectableVariableValues } from "../conversion/helpers"; import { convertPxGrafResponseToView } from "../conversion/viewUtils"; import { renderHtmlTable } from "./htmlTable"; import { SELECTABLE_TABLE_WITH_INVALID_MISSING_DATA } from "./fixtures/pxGrafResponses"; +import { ITableOptions } from "../types/tableOptions"; describe('Html table render tests', () => { it('should match snapshot: Table with column variables only', () => { @@ -22,7 +23,14 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - renderHtmlTable(mockView, locale, true, true, true, testId); + const settings: ITableOptions = { + showTitles: true, + showUnits: true, + showSources: true, + showLastUpdated: false + }; + + renderHtmlTable(mockView, locale, settings, testId); const renderedOutput = prettyDOM(div); expect(renderedOutput).toMatchSnapshot(); @@ -45,7 +53,14 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - renderHtmlTable(mockView, locale, true, true, true, testId); + const settings: ITableOptions = { + showTitles: true, + showUnits: true, + showSources: true, + showLastUpdated: false + }; + + renderHtmlTable(mockView, locale, settings, testId); const renderedOutput = prettyDOM(div); expect(renderedOutput).toMatchSnapshot(); @@ -68,7 +83,14 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - renderHtmlTable(mockView, locale, true, true, true, testId); + const settings: ITableOptions = { + showTitles: true, + showUnits: true, + showSources: true, + showLastUpdated: false + }; + + renderHtmlTable(mockView, locale, settings, testId); const renderedOutput = prettyDOM(div); expect(renderedOutput).toMatchSnapshot(); @@ -91,7 +113,14 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - renderHtmlTable(mockView, locale, false, true, true, testId); + const settings: ITableOptions = { + showTitles: false, + showUnits: true, + showSources: true, + showLastUpdated: false + }; + + renderHtmlTable(mockView, locale, settings, testId); const renderedOutput = prettyDOM(div); expect(renderedOutput).toMatchSnapshot(); @@ -114,7 +143,14 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - renderHtmlTable(mockView, locale, true, false, false, testId); + const settings: ITableOptions = { + showTitles: true, + showUnits: false, + showSources: false, + showLastUpdated: false + }; + + renderHtmlTable(mockView, locale, settings, testId); const renderedOutput = prettyDOM(div); expect(renderedOutput).toMatchSnapshot(); @@ -137,7 +173,14 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - renderHtmlTable(mockView, locale, true, false, false, testId, 'Test footnote'); + const settings: ITableOptions = { + showTitles: true, + showUnits: false, + showSources: false, + showLastUpdated: false + }; + + renderHtmlTable(mockView, locale, settings, testId, 'Test footnote'); const renderedOutput = prettyDOM(div); expect(renderedOutput).toMatchSnapshot(); @@ -160,7 +203,14 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - renderHtmlTable(mockView, locale, true, true, true, testId); + const settings: ITableOptions = { + showTitles: true, + showUnits: true, + showSources: true, + showLastUpdated: false + }; + + renderHtmlTable(mockView, locale, settings, testId); const renderedOutput = prettyDOM(div); expect(renderedOutput).toMatchSnapshot(); @@ -183,7 +233,14 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - renderHtmlTable(mockView, locale, true, true, true, testId, 'Test footnote'); + const settings: ITableOptions = { + showTitles: true, + showUnits: true, + showSources: true, + showLastUpdated: false + }; + + renderHtmlTable(mockView, locale, settings, testId, 'Test footnote'); const renderedOutput = prettyDOM(div); expect(renderedOutput).toMatchSnapshot(); @@ -209,7 +266,14 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - renderHtmlTable(mockView, locale, true, true, true, testId); + const settings: ITableOptions = { + showTitles: true, + showUnits: true, + showSources: true, + showLastUpdated: false + }; + + renderHtmlTable(mockView, locale, settings, testId); const renderedOutput = prettyDOM(div); expect(renderedOutput).toMatchSnapshot(); @@ -237,7 +301,14 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - renderHtmlTable(mockView, locale, true, true, true, testId); + const settings: ITableOptions = { + showTitles: true, + showUnits: true, + showSources: true, + showLastUpdated: false + }; + + renderHtmlTable(mockView, locale, settings, testId); const renderedOutput = prettyDOM(div); expect(renderedOutput).toMatchSnapshot(); diff --git a/src/core/tables/htmlTable.ts b/src/core/tables/htmlTable.ts index b2aad2c..ea2dbbf 100644 --- a/src/core/tables/htmlTable.ts +++ b/src/core/tables/htmlTable.ts @@ -2,9 +2,10 @@ import { getFormattedUnits, getFormattedLastUpdatedText } from "../chartOptions/ import { Translations } from "../conversion/translations"; import { TMultiLanguageString } from "../types/queryVisualizationResponse"; import { IDataSeries, View } from "../types/view"; +import { ITableOptions } from "../types/tableOptions"; import { formatMissingData, formatNumericValue } from "./tableUtils"; -export function renderHtmlTable(view: View, locale: string, showTitles: boolean, showUnits: boolean, showSources: boolean, containerId: string, footnote?: string, showLastUpdated?: boolean): void { +export function renderHtmlTable(view: View, locale: string, options: ITableOptions, containerId: string, footnote?: string): void { const container = document.getElementById(containerId); if (!container) throw new Error("No container with matching id found in the DOM tree"); @@ -13,7 +14,7 @@ export function renderHtmlTable(view: View, locale: string, showTitles: boolean, // Table content const table = generateTable(view, locale); - if (showTitles) { + if (options.showTitles) { const caption = document.createElement('caption'); caption.textContent = view.header[locale]; @@ -28,7 +29,7 @@ export function renderHtmlTable(view: View, locale: string, showTitles: boolean, container.append(table); // Units - if (showUnits) { + if (options.showUnits) { const pUnits = document.createElement('p'); const unitName = getFormattedUnits(view.units, locale); const units: string = `${Translations.unit[locale]}: ${unitName}`; @@ -44,7 +45,7 @@ export function renderHtmlTable(view: View, locale: string, showTitles: boolean, } // Last Updated - if (showLastUpdated && view.lastUpdated) { + if (options.showLastUpdated && view.lastUpdated) { const pLastUpdated = document.createElement('p'); const lastUpdatedText = getFormattedLastUpdatedText(view.lastUpdated, locale); if (lastUpdatedText) { @@ -54,7 +55,7 @@ export function renderHtmlTable(view: View, locale: string, showTitles: boolean, } // Sources - if (showSources) { + if (options.showSources) { const pSources = document.createElement('p'); const sources: string = `${Translations.source[locale]}: ${view.sources.map(source => source[locale]).join(', ')}`; pSources.append(sources); diff --git a/src/core/types/index.ts b/src/core/types/index.ts index 4125346..4cc3757 100644 --- a/src/core/types/index.ts +++ b/src/core/types/index.ts @@ -8,3 +8,4 @@ export { TVariableType } from './queryVisualizationResponse'; export { IChartOptions } from './chartOptions'; +export { ITableOptions } from './tableOptions'; diff --git a/src/core/types/tableOptions.ts b/src/core/types/tableOptions.ts new file mode 100644 index 0000000..ad2b429 --- /dev/null +++ b/src/core/types/tableOptions.ts @@ -0,0 +1,6 @@ +export interface ITableOptions { + showTitles: boolean; + showUnits: boolean; + showSources: boolean; + showLastUpdated: boolean; +} \ No newline at end of file diff --git a/src/react/components/chart/chart.tsx b/src/react/components/chart/chart.tsx index 1ea327a..a977a46 100644 --- a/src/react/components/chart/chart.tsx +++ b/src/react/components/chart/chart.tsx @@ -17,6 +17,7 @@ import { formatLocale } from "../../../core/chartOptions/Utility/formatters"; import { TableView } from "./tableView"; import { GlobalStyle } from "../globalStyle"; import { View } from "../../../core/types/view"; +import { ITableOptions } from "../../../core/types/tableOptions"; import { ErrorInfo } from "./ErrorInfo"; import { ErrorBoundary } from "../ErrorBoundary/ErrorBoundary"; @@ -92,33 +93,40 @@ const ReactChart: React.FC = ({ const validLocale = formatLocale(locale); initializeHighcharts(validLocale, fontFamily); - const chartRef: React.MutableRefObject = React.useRef(null); + const chartRef: React.MutableRefObject = React.useRef(null); - const [currentChartRef, setCurrentChartRef] = React.useState(chartRef.current); + const [currentChartRef, setCurrentChartRef] = React.useState(chartRef.current); const [tableMode, setTableMode] = React.useState(false); const [accessibilityMode, setAccessibilityMode] = React.useState(false); const [width, setWidth] = React.useState(0); const variableSelections = useMemo(() => { try { - return extractSelectableVariableValues(pxGraphData.selectableVariableCodes, pxGraphData.metaData, pxGraphData.visualizationSettings.defaultSelectableVariableCodes, selectedVariableCodes); - } + return extractSelectableVariableValues(pxGraphData.selectableVariableCodes, pxGraphData.metaData, pxGraphData.visualizationSettings.defaultSelectableVariableCodes, selectedVariableCodes); + } catch (error: any) { - console.error(error); - return {}; - } + console.error(error); + return {}; + } }, [selectedVariableCodes, pxGraphData]); const view: View | null = useMemo(() => { - try { - return convertPxGrafResponseToView(pxGraphData, variableSelections); - } - catch (error: any) { + try { + return convertPxGrafResponseToView(pxGraphData, variableSelections); + } + catch (error: any) { console.error(error); - return null; + return null; } }, [variableSelections, pxGraphData]); + const tableOptions: ITableOptions = useMemo(() => ({ + showTitles: showTitles ?? true, + showUnits: !!showTableUnits, + showSources: showTableSources ?? true, + showLastUpdated: showLastUpdated + }), [showTitles, showTableUnits, showTableSources, showLastUpdated]); + // Force rerender on window resize events, so that scaling and scrollboxes work correctly. Feel free to refactor to a more performant or better solution let renderTimeOut: NodeJS.Timeout; function rerender() { @@ -141,20 +149,20 @@ const ReactChart: React.FC = ({ } const toggleAccessibilityMode = () => { - setAccessibilityMode(!accessibilityMode); + setAccessibilityMode(!accessibilityMode); } React.useEffect(() => { - if (chartRef.current) { - setCurrentChartRef(chartRef.current); - } - }, [chartRef.current]); + if (chartRef.current) { +setCurrentChartRef(chartRef.current); + } +}, [chartRef.current]); try { // Chart if (view && pxGraphData.visualizationSettings.visualizationType !== EVisualizationType.Table) { - const highChartOptions = convertPxGraphDataToChartOptions(validLocale, view, { - accessibilityMode: accessibilityMode, + const highChartOptions = convertPxGraphDataToChartOptions(validLocale, view, { + accessibilityMode: accessibilityMode, showTitle: showTitles ?? true, fontFamily: fontFamily, showLastUpdated: showLastUpdated @@ -176,7 +184,7 @@ const ReactChart: React.FC = ({ /> - + ); @@ -189,10 +197,10 @@ const ReactChart: React.FC = ({ { showContextMenu && - + } - + ); } diff --git a/src/react/components/chart/tableView.test.tsx b/src/react/components/chart/tableView.test.tsx index bbd9fde..8c42213 100644 --- a/src/react/components/chart/tableView.test.tsx +++ b/src/react/components/chart/tableView.test.tsx @@ -3,6 +3,7 @@ import { TableView } from "./tableView"; import { convertPxGrafResponseToView } from "../../../core/conversion/viewUtils"; import React from "react"; import { TABLE_WITH_ROW_AND_COLUMN_VARIABLES } from "../../../core/conversion/fixtures/tableChart"; +import { ITableOptions } from "../../../core/types/tableOptions"; jest.mock('uuid', () => ({ v4: () => 'foobar' @@ -10,7 +11,13 @@ jest.mock('uuid', () => ({ describe('TableView render tests', () => { it('Should render correctly', () => { - const { asFragment } = render(); + const options: ITableOptions = { + showTitles: true, + showUnits: true, + showSources: true, + showLastUpdated: false + }; + const { asFragment } = render(); expect(asFragment()).toMatchSnapshot(); }); }); \ No newline at end of file diff --git a/src/react/components/chart/tableView.tsx b/src/react/components/chart/tableView.tsx index 5ffd44d..8f8ac1e 100644 --- a/src/react/components/chart/tableView.tsx +++ b/src/react/components/chart/tableView.tsx @@ -1,25 +1,23 @@ import React, { useMemo } from "react"; import { View } from "../../../core/types/view"; +import { ITableOptions } from "../../../core/types/tableOptions"; import { v4 as uuidv4 } from 'uuid'; import { renderHtmlTable } from "../../../core/tables/htmlTable"; export interface ITableViewProps { view: View; locale: string; - showTitles?: boolean; - showUnits?: boolean; - showSources?: boolean; + options: ITableOptions; footnote?: string; - showLastUpdated?: boolean; } -export const TableView: React.FC = ({ view, locale, footnote, showTitles = true, showUnits = true, showSources = true, showLastUpdated = false }) => { +export const TableView: React.FC = ({ view, locale, options, footnote }) => { const uuid = useMemo(() => uuidv4(), [view, locale]); React.useEffect(() => { document.getElementById(uuid)?.replaceChildren(); - renderHtmlTable(view, locale, showTitles, showUnits, showSources, uuid, footnote, showLastUpdated); - }, [view, locale, showTitles, showUnits, showSources, footnote, showLastUpdated]); + renderHtmlTable(view, locale, options, uuid, footnote); + }, [view, locale, options, footnote]); return
; } \ No newline at end of file From c34e91c31665db45ee2406013c5dd591b9fa664e Mon Sep 17 00:00:00 2001 From: Sakari Malkki Date: Tue, 28 Oct 2025 13:19:48 +0200 Subject: [PATCH 03/21] Assume GB English locale for date formatting if not explicitly stated otherwise --- src/core/chartOptions/Utility/formatters.test.ts | 6 +++--- src/core/chartOptions/Utility/formatters.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/core/chartOptions/Utility/formatters.test.ts b/src/core/chartOptions/Utility/formatters.test.ts index 5d889be..d09f5ea 100644 --- a/src/core/chartOptions/Utility/formatters.test.ts +++ b/src/core/chartOptions/Utility/formatters.test.ts @@ -434,9 +434,9 @@ describe('getFormattedLastUpdatedText tests', () => { expect(result).toBe('Uppdaterad: 2025-09-23'); }); - it('should format last updated text correctly for English locale', () => { - const result = getFormattedLastUpdatedText('2025-09-23', 'en'); - expect(result).toBe('Updated: 9/23/2025'); + it('should format last updated text correctly for English locale to British English', () => { + const result = getFormattedLastUpdatedText('2025-09-23', 'en'); + expect(result).toBe('Updated: 23/09/2025'); }); it('should return undefined for undefined input', () => { diff --git a/src/core/chartOptions/Utility/formatters.ts b/src/core/chartOptions/Utility/formatters.ts index e75706b..509da58 100644 --- a/src/core/chartOptions/Utility/formatters.ts +++ b/src/core/chartOptions/Utility/formatters.ts @@ -271,7 +271,8 @@ export function getFormattedLastUpdatedText(lastUpdated: string | undefined, loc const date = new Date(lastUpdated); if (Number.isNaN(date.getTime())) return undefined; - const formattedDate: string = Intl.DateTimeFormat(locale).format(date); + const dateLocale = locale === 'en' ? 'en-GB' : locale; // Use en-GB for English to get DD/MM/YYYY format if not specified + const formattedDate: string = Intl.DateTimeFormat(dateLocale).format(date); return `${Translations.lastUpdated[locale]}: ${formattedDate}`; } catch (error) { From 7aab6fcc3413de2f8b8b8ccc4af347b1632916c7 Mon Sep 17 00:00:00 2001 From: Sakari Malkki Date: Tue, 28 Oct 2025 15:14:29 +0200 Subject: [PATCH 04/21] Correct minor version raise, handle chart spacing and credits text position dynamically --- .storybook/preview.ts | 2 +- package.json | 2 +- src/core/chartOptions/chartOptions.ts | 24 ++++++++++++++++++++---- src/core/highcharts/themes.ts | 1 - src/react/components/chart/chart.tsx | 2 ++ 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 969ada4..41ac159 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -15,7 +15,7 @@ const preview: Preview = { name: 'Website', styles: { width: '766px', - height: '600px', + height: '635px', }, type: 'desktop', }, diff --git a/package.json b/package.json index 74cc6f4..03875be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@statisticsfinland/pxvisualizer", - "version": "1.4.2", + "version": "1.4.0", "description": "Component library for visualizing PxGraf data", "main": "./dist/pxv.cjs", "jestSonar": { diff --git a/src/core/chartOptions/chartOptions.ts b/src/core/chartOptions/chartOptions.ts index d0fcc34..da7d845 100644 --- a/src/core/chartOptions/chartOptions.ts +++ b/src/core/chartOptions/chartOptions.ts @@ -17,7 +17,9 @@ export const commonChartOptions = (view: View, locale: string, options?: IChartO text: creditsText }; - if (options?.showLastUpdated && view.lastUpdated) { + const hasLastUpdated = options?.showLastUpdated && view.lastUpdated; + + if (hasLastUpdated) { const lastUpdatedText = getFormattedLastUpdatedText(view.lastUpdated, locale); if (lastUpdatedText) { creditsText = `${lastUpdatedText}
${sourceText}: ${view.sources.map(s => s[locale]).join(', ')}`; @@ -26,15 +28,29 @@ export const commonChartOptions = (view: View, locale: string, options?: IChartO text: creditsText, useHTML: true, position: { - align: 'left', - verticalAlign: 'bottom', - y: -20 + x: 5, + y: -30 // More space needed for two-line credits } }; } + } else { + // Single line credits, less spacing needed + creditsConfig = { + enabled: true, + text: creditsText, + position: { + align: 'left', + verticalAlign: 'bottom', + x: 5, + y: -10 // Less space needed for single line + } + }; } return { + chart: { + spacingBottom: hasLastUpdated ? 50 : 30 // Conditional spacing based on lastUpdated presence + }, accessibility: { point: { descriptionFormatter: getScreenReaderFormatterCallbackFunction(view, locale) diff --git a/src/core/highcharts/themes.ts b/src/core/highcharts/themes.ts index 9025cdb..56aa077 100644 --- a/src/core/highcharts/themes.ts +++ b/src/core/highcharts/themes.ts @@ -117,7 +117,6 @@ export const defaultTheme: (locale: string, fontFamily?: string) => Highcharts.O style: { fontFamily: fontFamily ?? '"Barlow Semi Condensed", Verdana, sans-serif' }, - spacingBottom: 30, spacingLeft: 20, // for tilted labels to fit in the x axis with ellipsis overflow height: (9 / 16 * 100) + '%' }, diff --git a/src/react/components/chart/chart.tsx b/src/react/components/chart/chart.tsx index a977a46..dfd7dfd 100644 --- a/src/react/components/chart/chart.tsx +++ b/src/react/components/chart/chart.tsx @@ -56,6 +56,8 @@ interface ITableContainerProps { const ChartContainer = styled.div` display: ${p => p.$tableMode ? 'none' : 'block'}; + min-height: 635px; + height: 100%; `; const TableContainer = styled.div` From 9318e519d5240d28cc3707a6755e44e638631185 Mon Sep 17 00:00:00 2001 From: Sakari Malkki Date: Tue, 28 Oct 2025 15:15:15 +0200 Subject: [PATCH 05/21] Update snapshots --- .../components/chart/__snapshots__/chart.test.tsx.snap | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/react/components/chart/__snapshots__/chart.test.tsx.snap b/src/react/components/chart/__snapshots__/chart.test.tsx.snap index dcc79a2..b64f920 100644 --- a/src/react/components/chart/__snapshots__/chart.test.tsx.snap +++ b/src/react/components/chart/__snapshots__/chart.test.tsx.snap @@ -45,7 +45,7 @@ exports[`Rendering test renders chart data correctly 1`] = `
@@ -169,7 +169,7 @@ exports[`Rendering test renders chart data correctly with hidden title 1`] = `
@@ -288,7 +288,7 @@ exports[`Rendering test renders chart data correctly with last updated date 1`]
From 0d19c4165515e4c1d3e39bbbd13fdcd077ee02e4 Mon Sep 17 00:00:00 2001 From: sakari-malkki <47064229+sakari-malkki@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:37:34 +0200 Subject: [PATCH 06/21] Update src/core/conversion/viewUtils.ts Complete description of last update utility function return string Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/core/conversion/viewUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/conversion/viewUtils.ts b/src/core/conversion/viewUtils.ts index 39c2dd0..7ca39e9 100644 --- a/src/core/conversion/viewUtils.ts +++ b/src/core/conversion/viewUtils.ts @@ -109,7 +109,7 @@ export function buildSeries(responseObj: IQueryVisualizationResponse, selectedVa * Get the last updated date from the content variable values. * @param contentVar Content variable metadata * @param selectedValueCodes Selected selectable value codes if any - * @returns Last updated date as a string + * @returns Last updated date as a string, or empty string if no valid dates are found */ export function getLastUpdated( contentVar: IVariableMeta, From 20e4a5fb53597b15b2ccd692effac14ed072812e Mon Sep 17 00:00:00 2001 From: sakari-malkki <47064229+sakari-malkki@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:38:38 +0200 Subject: [PATCH 07/21] Update src/core/highcharts/drawChart.ts Remove extra whitespace Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/core/highcharts/drawChart.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/highcharts/drawChart.ts b/src/core/highcharts/drawChart.ts index dcd71a1..7c1114d 100644 --- a/src/core/highcharts/drawChart.ts +++ b/src/core/highcharts/drawChart.ts @@ -34,7 +34,6 @@ export const drawChart = ( Highcharts.setOptions(defaultTheme(validLocale, options?.fontFamily)); const variableSelections = extractSelectableVariableValues(pxGraphData.selectableVariableCodes, pxGraphData.metaData, pxGraphData.visualizationSettings.defaultSelectableVariableCodes, selectedVariableCodes); const view = convertPxGrafResponseToView(pxGraphData, variableSelections); - const highChartOptions = convertPxGraphDataToChartOptions(validLocale, view, options); return Highcharts.chart(container, highChartOptions); } \ No newline at end of file From 3bb085cda6784802c22802362e8a55cef5e3773f Mon Sep 17 00:00:00 2001 From: sakari-malkki <47064229+sakari-malkki@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:39:02 +0200 Subject: [PATCH 08/21] Update src/react/components/chart/chart.test.tsx Remove extra trailing whitespace Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/react/components/chart/chart.test.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/react/components/chart/chart.test.tsx b/src/react/components/chart/chart.test.tsx index 87277f1..2aa4bc8 100644 --- a/src/react/components/chart/chart.test.tsx +++ b/src/react/components/chart/chart.test.tsx @@ -51,7 +51,6 @@ describe('Rendering test', () => { />); expect(asFragment()).toMatchSnapshot(); }); - it('renders table data correctly', () => { const { asFragment } = render( Date: Tue, 28 Oct 2025 15:41:51 +0200 Subject: [PATCH 09/21] Fix inconsistent indentation --- src/core/chartOptions/Utility/formatters.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/chartOptions/Utility/formatters.test.ts b/src/core/chartOptions/Utility/formatters.test.ts index d09f5ea..405c1f2 100644 --- a/src/core/chartOptions/Utility/formatters.test.ts +++ b/src/core/chartOptions/Utility/formatters.test.ts @@ -425,13 +425,13 @@ describe('getDataFormattedForChartType tests', () => { describe('getFormattedLastUpdatedText tests', () => { it('should format last updated text correctly for Finnish locale', () => { - const result = getFormattedLastUpdatedText('2025-09-23', 'fi'); - expect(result).toBe('Päivitetty: 23.9.2025'); + const result = getFormattedLastUpdatedText('2025-09-23', 'fi'); + expect(result).toBe('Päivitetty: 23.9.2025'); }); it('should format last updated text correctly for Swedish locale', () => { const result = getFormattedLastUpdatedText('2025-09-23', 'sv'); - expect(result).toBe('Uppdaterad: 2025-09-23'); + expect(result).toBe('Uppdaterad: 2025-09-23'); }); it('should format last updated text correctly for English locale to British English', () => { @@ -446,6 +446,6 @@ describe('getFormattedLastUpdatedText tests', () => { it('should return undefined for invalid date', () => { const result = getFormattedLastUpdatedText('invalid-date', 'fi'); - expect(result).toBeUndefined(); + expect(result).toBeUndefined(); }); }); \ No newline at end of file From 99d7caf75a73f773b8f23f59b76cbd345de5b5a1 Mon Sep 17 00:00:00 2001 From: Sakari Malkki Date: Tue, 28 Oct 2025 15:43:20 +0200 Subject: [PATCH 10/21] Copilot nitpick fix --- src/react/components/chart/chart.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/react/components/chart/chart.test.tsx b/src/react/components/chart/chart.test.tsx index 2aa4bc8..943cf5e 100644 --- a/src/react/components/chart/chart.test.tsx +++ b/src/react/components/chart/chart.test.tsx @@ -51,6 +51,7 @@ describe('Rendering test', () => { />); expect(asFragment()).toMatchSnapshot(); }); + it('renders table data correctly', () => { const { asFragment } = render( Date: Tue, 28 Oct 2025 15:48:54 +0200 Subject: [PATCH 11/21] Fix undefined return type for getLastUpdate function --- src/core/conversion/viewUtils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/conversion/viewUtils.ts b/src/core/conversion/viewUtils.ts index 7ca39e9..6e8989f 100644 --- a/src/core/conversion/viewUtils.ts +++ b/src/core/conversion/viewUtils.ts @@ -109,12 +109,12 @@ export function buildSeries(responseObj: IQueryVisualizationResponse, selectedVa * Get the last updated date from the content variable values. * @param contentVar Content variable metadata * @param selectedValueCodes Selected selectable value codes if any - * @returns Last updated date as a string, or empty string if no valid dates are found + * @returns Last updated date as a string, or undefined if no valid dates are found */ export function getLastUpdated( contentVar: IVariableMeta, selectedValueCodes: TVariableSelections -): string { +): string | undefined { let dates: (string | undefined)[]; if (contentVar.code in selectedValueCodes) { @@ -132,7 +132,7 @@ export function getLastUpdated( const validDates = dates.filter((date): date is string => date !== undefined); if (validDates.length === 0) { - return ''; + return undefined; } if (validDates.length === 1) { From 833efbc2ef54440a2a1478afc3754b4e423cdae2 Mon Sep 17 00:00:00 2001 From: Sakari Malkki Date: Wed, 29 Oct 2025 10:50:06 +0200 Subject: [PATCH 12/21] Combine tableOptions and chartOptions. Fix some indentations --- src/core/highcharts/drawChart.test.tsx | 2 +- src/core/tables/htmlTable.test.ts | 42 ++++----- src/core/tables/htmlTable.ts | 6 +- src/core/types/chartOptions.ts | 2 + src/core/types/index.ts | 1 - src/core/types/tableOptions.ts | 6 -- .../chart/__snapshots__/chart.test.tsx.snap | 48 +++++----- src/react/components/chart/chart.test.tsx | 4 +- src/react/components/chart/chart.tsx | 89 +++++++++---------- src/react/components/chart/tableView.test.tsx | 6 +- src/react/components/chart/tableView.tsx | 4 +- 11 files changed, 101 insertions(+), 109 deletions(-) delete mode 100644 src/core/types/tableOptions.ts diff --git a/src/core/highcharts/drawChart.test.tsx b/src/core/highcharts/drawChart.test.tsx index af07288..18594b2 100644 --- a/src/core/highcharts/drawChart.test.tsx +++ b/src/core/highcharts/drawChart.test.tsx @@ -20,7 +20,7 @@ describe('drawChart tests', () => { it('calls Highcharts.chart with correct parameters', () => { // Arrange - const customOptions = { accessibilityMode: true }; + const customOptions = { accessibilityMode: true, showUnits: false, showSources: false }; const selectedVariableCodes = { 'variableCode1': ['value1', 'value2'] }; // Act diff --git a/src/core/tables/htmlTable.test.ts b/src/core/tables/htmlTable.test.ts index 0a65581..970beee 100644 --- a/src/core/tables/htmlTable.test.ts +++ b/src/core/tables/htmlTable.test.ts @@ -5,7 +5,7 @@ import { extractSelectableVariableValues } from "../conversion/helpers"; import { convertPxGrafResponseToView } from "../conversion/viewUtils"; import { renderHtmlTable } from "./htmlTable"; import { SELECTABLE_TABLE_WITH_INVALID_MISSING_DATA } from "./fixtures/pxGrafResponses"; -import { ITableOptions } from "../types/tableOptions"; +import { IChartOptions } from "../types/chartOptions"; describe('Html table render tests', () => { it('should match snapshot: Table with column variables only', () => { @@ -23,8 +23,8 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - const settings: ITableOptions = { - showTitles: true, + const settings: IChartOptions = { + showTitle: true, showUnits: true, showSources: true, showLastUpdated: false @@ -53,8 +53,8 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - const settings: ITableOptions = { - showTitles: true, + const settings: IChartOptions = { + showTitle: true, showUnits: true, showSources: true, showLastUpdated: false @@ -83,8 +83,8 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - const settings: ITableOptions = { - showTitles: true, + const settings: IChartOptions = { + showTitle: true, showUnits: true, showSources: true, showLastUpdated: false @@ -113,8 +113,8 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - const settings: ITableOptions = { - showTitles: false, + const settings: IChartOptions = { + showTitle: false, showUnits: true, showSources: true, showLastUpdated: false @@ -143,8 +143,8 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - const settings: ITableOptions = { - showTitles: true, + const settings: IChartOptions = { + showTitle: true, showUnits: false, showSources: false, showLastUpdated: false @@ -173,8 +173,8 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - const settings: ITableOptions = { - showTitles: true, + const settings: IChartOptions = { + showTitle: true, showUnits: false, showSources: false, showLastUpdated: false @@ -203,8 +203,8 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - const settings: ITableOptions = { - showTitles: true, + const settings: IChartOptions = { + showTitle: true, showUnits: true, showSources: true, showLastUpdated: false @@ -233,8 +233,8 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - const settings: ITableOptions = { - showTitles: true, + const settings: IChartOptions = { + showTitle: true, showUnits: true, showSources: true, showLastUpdated: false @@ -266,8 +266,8 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - const settings: ITableOptions = { - showTitles: true, + const settings: IChartOptions = { + showTitle: true, showUnits: true, showSources: true, showLastUpdated: false @@ -301,8 +301,8 @@ describe('Html table render tests', () => { div.id = testId; document.body.appendChild(div); - const settings: ITableOptions = { - showTitles: true, + const settings: IChartOptions = { + showTitle: true, showUnits: true, showSources: true, showLastUpdated: false diff --git a/src/core/tables/htmlTable.ts b/src/core/tables/htmlTable.ts index ea2dbbf..cccacd7 100644 --- a/src/core/tables/htmlTable.ts +++ b/src/core/tables/htmlTable.ts @@ -2,10 +2,10 @@ import { getFormattedUnits, getFormattedLastUpdatedText } from "../chartOptions/ import { Translations } from "../conversion/translations"; import { TMultiLanguageString } from "../types/queryVisualizationResponse"; import { IDataSeries, View } from "../types/view"; -import { ITableOptions } from "../types/tableOptions"; +import { IChartOptions } from "../types/chartOptions"; import { formatMissingData, formatNumericValue } from "./tableUtils"; -export function renderHtmlTable(view: View, locale: string, options: ITableOptions, containerId: string, footnote?: string): void { +export function renderHtmlTable(view: View, locale: string, options: IChartOptions, containerId: string, footnote?: string): void { const container = document.getElementById(containerId); if (!container) throw new Error("No container with matching id found in the DOM tree"); @@ -14,7 +14,7 @@ export function renderHtmlTable(view: View, locale: string, options: ITableOptio // Table content const table = generateTable(view, locale); - if (options.showTitles) { + if (options.showTitle) { const caption = document.createElement('caption'); caption.textContent = view.header[locale]; diff --git a/src/core/types/chartOptions.ts b/src/core/types/chartOptions.ts index 0609b83..0a6097a 100644 --- a/src/core/types/chartOptions.ts +++ b/src/core/types/chartOptions.ts @@ -6,4 +6,6 @@ export interface IChartOptions { showTitle?: boolean; fontFamily?: string; showLastUpdated?: boolean; + showUnits: boolean; + showSources: boolean; } \ No newline at end of file diff --git a/src/core/types/index.ts b/src/core/types/index.ts index 4cc3757..4125346 100644 --- a/src/core/types/index.ts +++ b/src/core/types/index.ts @@ -8,4 +8,3 @@ export { TVariableType } from './queryVisualizationResponse'; export { IChartOptions } from './chartOptions'; -export { ITableOptions } from './tableOptions'; diff --git a/src/core/types/tableOptions.ts b/src/core/types/tableOptions.ts deleted file mode 100644 index ad2b429..0000000 --- a/src/core/types/tableOptions.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface ITableOptions { - showTitles: boolean; - showUnits: boolean; - showSources: boolean; - showLastUpdated: boolean; -} \ No newline at end of file diff --git a/src/react/components/chart/__snapshots__/chart.test.tsx.snap b/src/react/components/chart/__snapshots__/chart.test.tsx.snap index b64f920..d7b8436 100644 --- a/src/react/components/chart/__snapshots__/chart.test.tsx.snap +++ b/src/react/components/chart/__snapshots__/chart.test.tsx.snap @@ -3,10 +3,10 @@ exports[`Rendering test renders chart data correctly 1`] = `