diff --git a/packages/pxweb2/public/config/config.js b/packages/pxweb2/public/config/config.js index 87ec89910..fff68fc5a 100644 --- a/packages/pxweb2/public/config/config.js +++ b/packages/pxweb2/public/config/config.js @@ -12,6 +12,9 @@ globalThis.PxWeb2Config = { baseApplicationPath: '/', apiUrl: 'https://api.scb.se/OV0104/v2beta/api/v2', maxDataCells: 150000, + tableViewer: { + selectionOnLanguageChange: 'keep', // Options: 'keep', 'reset' + }, useDynamicContentInTitle: false, showBreadCrumbOnStartPage: false, specialCharacters: ['.', '..', ':', '-', '...', '*'], diff --git a/packages/pxweb2/src/app/components/Presentation/Presentation.tsx b/packages/pxweb2/src/app/components/Presentation/Presentation.tsx index 3f48d6014..035e336b3 100644 --- a/packages/pxweb2/src/app/components/Presentation/Presentation.tsx +++ b/packages/pxweb2/src/app/components/Presentation/Presentation.tsx @@ -147,7 +147,16 @@ export function Presentation({ setInitialRun(false); } } - }, [tableId, selectedVBValues]); + }, [ + tableId, + selectedVBValues, + pxTableMetadata, + hasLoadedInitialSelection, + isLoadingMetadata, + initialRun, + isMobile, + i18n.resolvedLanguage, + ]); useEffect(() => { memoizedDataFetch(); diff --git a/packages/pxweb2/src/app/components/Selection/Selection.spec.tsx b/packages/pxweb2/src/app/components/Selection/Selection.spec.tsx index e3e8b9eb8..fde81aef3 100644 --- a/packages/pxweb2/src/app/components/Selection/Selection.spec.tsx +++ b/packages/pxweb2/src/app/components/Selection/Selection.spec.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { act } from '@testing-library/react'; +import { act, waitFor, render } from '@testing-library/react'; import { vi } from 'vitest'; import { @@ -8,11 +8,45 @@ import { } from '../../util/testing-utils'; import Selection from './Selection'; import { AccessibilityProvider } from '../../context/AccessibilityProvider'; -import { TableDataContext } from '../../context/TableDataProvider'; +import { + TableDataContext, + TableDataProvider, +} from '../../context/TableDataProvider'; +import { AppProvider } from '../../context/AppProvider'; +import { VariablesProvider } from '../../context/VariablesProvider'; +import { TablesService } from '@pxweb2/pxweb2-api-client'; +import { mockedConfig } from '../../../../test/setupTests'; + +let mockResolvedLanguage = 'en'; + +vi.mock('react-i18next', () => ({ + useTranslation: () => ({ + t: (key: string) => key, + i18n: { + changeLanguage: () => Promise.resolve(), + language: mockResolvedLanguage, + resolvedLanguage: mockResolvedLanguage, + dir: () => 'ltr', + }, + }), + useRouteError: vi.fn(), + useLocation: vi.fn(), + useNavigate: vi.fn(() => vi.fn()), + initReactI18next: { + type: '3rdParty', + init: vi.fn(), + }, +})); describe('Selection', () => { mockTableService(); + beforeEach(() => { + mockResolvedLanguage = 'en'; + mockedConfig.tableViewer = { selectionOnLanguageChange: 'reset' }; + vi.clearAllMocks(); + }); + it('should throw an error when triggered', () => { const TestComponent = () => { const context = React.useContext(TableDataContext); @@ -54,4 +88,74 @@ describe('Selection', () => { throw new Error('Simulated error'); }).toThrow('Simulated error'); }); + + it('reloads default selection when language changes in reset mode', async () => { + const view = ( + + + + + + + + + + ); + + const { rerender } = render(view); + + await waitFor(() => { + expect(TablesService.getDefaultSelection).toHaveBeenCalledTimes(1); + }); + + mockResolvedLanguage = 'sv'; + + rerender(view); + + await waitFor(() => { + expect(TablesService.getDefaultSelection).toHaveBeenCalledTimes(2); + }); + }); + + it('keeps current selection when language changes in keep mode', async () => { + mockedConfig.tableViewer = { selectionOnLanguageChange: 'keep' }; + + const view = ( + + + + + + + + + + ); + + const { rerender } = render(view); + + await waitFor(() => { + expect(TablesService.getDefaultSelection).toHaveBeenCalledTimes(1); + }); + + mockResolvedLanguage = 'sv'; + + rerender(view); + + await waitFor(() => { + expect(TablesService.getMetadataById).toHaveBeenCalled(); + }); + + expect(TablesService.getDefaultSelection).toHaveBeenCalledTimes(1); + }); }); diff --git a/packages/pxweb2/src/app/components/Selection/Selection.tsx b/packages/pxweb2/src/app/components/Selection/Selection.tsx index 77a1b3183..31dd76be6 100644 --- a/packages/pxweb2/src/app/components/Selection/Selection.tsx +++ b/packages/pxweb2/src/app/components/Selection/Selection.tsx @@ -36,6 +36,7 @@ import useApp from '../../context/useApp'; import { NavigationItem } from '../../components/NavigationMenu/NavigationItem/NavigationItemType'; import useAccessibility from '../../context/useAccessibility'; import { problemMessage } from '../../util/problemMessage'; +import { getConfig } from '../../util/config/getConfig'; import { getSelectedCodelists, updateSelectedCodelistForVariable, @@ -233,6 +234,7 @@ export function Selection({ setSelectedNavigationView, hideMenuRef, }: SelectionProps) { + const config = getConfig(); const variables = useVariables(); const app = useApp(); const { isTablet } = useApp(); @@ -271,14 +273,22 @@ export function Selection({ return; } - // If the table has changed, or the language has changed, we need to reload the default selection + const isTableChanged = prevTableId === '' || prevTableId !== selectedTabId; + const languageChanged = prevLang !== i18n.resolvedLanguage; + const shouldResetSelectionOnLanguageChange = + config.tableViewer?.selectionOnLanguageChange !== 'keep'; + + // If table changes, selection should always be reset to initial selection. + // If language changes, resetting is controlled by config. if ( - prevTableId === '' || - prevTableId !== selectedTabId || - prevLang !== i18n.resolvedLanguage + isTableChanged || + (languageChanged && shouldResetSelectionOnLanguageChange) ) { variables.setHasLoadedInitialSelection(false); shouldGetInitialSelection = true; + } + + if (isTableChanged || languageChanged) { setPrevTableId(selectedTabId); setPrevLang(i18n.resolvedLanguage ?? ''); } diff --git a/packages/pxweb2/src/app/context/TableDataProvider.tsx b/packages/pxweb2/src/app/context/TableDataProvider.tsx index 16e7d4139..ca988582e 100644 --- a/packages/pxweb2/src/app/context/TableDataProvider.tsx +++ b/packages/pxweb2/src/app/context/TableDataProvider.tsx @@ -29,6 +29,7 @@ import { } from './TableDataProviderUtils'; import { problemMessage } from '../util/problemMessage'; import { PivotType } from './PivotType'; +import { getConfig } from '../util/config/getConfig'; // Define types for the context state and provider props export interface TableDataContextType { @@ -79,6 +80,7 @@ const TableDataContext = createContext({ }); const TableDataProvider: React.FC = ({ children }) => { + const config = getConfig(); const [isInitialized] = useState(true); // Data (metadata) that reflects variables and values selected by user right now. Used as data source for the table const [data, setData] = useState(undefined); @@ -337,7 +339,12 @@ const TableDataProvider: React.FC = ({ children }) => { accumulatedData?.metadata.language.toLowerCase() !== i18n.language.toLowerCase() ) { - variablesSelection = { selection: [] }; // If language is changed we shall fetch data with the default selection. + const shouldResetSelectionOnLanguageChange = + config.tableViewer?.selectionOnLanguageChange !== 'keep'; + + if (shouldResetSelectionOnLanguageChange) { + variablesSelection = { selection: [] }; // If language is changed we shall fetch data with the default selection. + } } const pxTable: PxTable = await fetchFromApi( @@ -353,7 +360,11 @@ const TableDataProvider: React.FC = ({ children }) => { setAccumulatedData(structuredClone(pxTable)); // } }, - [accumulatedData?.metadata.language, initializeStubAndHeading], + [ + accumulatedData?.metadata.language, + config.tableViewer?.selectionOnLanguageChange, + initializeStubAndHeading, + ], ); /** diff --git a/packages/pxweb2/src/app/util/config/configType.ts b/packages/pxweb2/src/app/util/config/configType.ts index 783d29b57..0aa09e12e 100644 --- a/packages/pxweb2/src/app/util/config/configType.ts +++ b/packages/pxweb2/src/app/util/config/configType.ts @@ -9,6 +9,9 @@ export type Config = { baseApplicationPath: string; apiUrl: string; maxDataCells: number; + tableViewer?: { + selectionOnLanguageChange?: 'reset' | 'keep'; + }; useDynamicContentInTitle: boolean; showBreadCrumbOnStartPage: boolean; specialCharacters: string[]; diff --git a/packages/pxweb2/test/setupTests.ts b/packages/pxweb2/test/setupTests.ts index e6a1c354f..0070d27a3 100644 --- a/packages/pxweb2/test/setupTests.ts +++ b/packages/pxweb2/test/setupTests.ts @@ -61,6 +61,10 @@ export const mockedConfig: Config = { baseApplicationPath: '/', apiUrl: '', maxDataCells: 100000, + tableViewer: { + selectionOnLanguageChange: 'reset', + }, + useDynamicContentInTitle: false, showBreadCrumbOnStartPage: true, specialCharacters: ['.', '..', ':', '-', '...', '*'], variableFilterExclusionList: {