Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
dd11661
feat: add chart pivot functionality and enable chart feature in config
MikaelNordberg Jun 23, 2026
01f2692
refactor: enhance mock configuration for DrawerView tests
MikaelNordberg Jun 23, 2026
24c43e6
Merge branch 'main' into feature/chart-autopivot
MikaelNordberg Jun 23, 2026
4202f79
feat: add logging for pivot operations in TableDataProvider and Prese…
MikaelNordberg Jun 23, 2026
4d5c86f
Prettier code
MikaelNordberg Jun 23, 2026
9258a01
Merge branch 'main' into feature/chart-autopivot
MikaelNordberg Jun 23, 2026
91779fa
Commented out changes in TableDataProvider
MikaelNordberg Jun 24, 2026
5cfba2f
refactor: Keep track of stub and heading for chart in TableDataProvid…
MikaelNordberg Jun 24, 2026
855d122
Remember how table was pivoted when returning to table view from char…
MikaelNordberg Jun 25, 2026
aa702f5
Prettier code
MikaelNordberg Jun 25, 2026
8af218d
refactor: Replace render with renderWithProviders in DrawerView tests
MikaelNordberg Jun 25, 2026
d14919f
refactor: Update pivot method to pivotToChart in Presentation component
MikaelNordberg Jun 25, 2026
87b0115
Use of DataViewMode in TableDataProvider instead of isMobile
MikaelNordberg Jun 25, 2026
91de85a
Prettier code
MikaelNordberg Jun 25, 2026
5b64ff9
fix: Set default values for stubChart and headingChart in filterStubA…
MikaelNordberg Jun 25, 2026
ac7dd4c
Handle the case when the number of variables are changed for the chart
MikaelNordberg Jun 26, 2026
8f3e561
For chart new variables shall be added to heading. Not stub.
MikaelNordberg Jun 26, 2026
00808ec
Replaced state isMobileMode with state dataViewMode
MikaelNordberg Jun 26, 2026
8ab7530
refactor: remove isMobile parameter from fetchTableData and fetchSave…
MikaelNordberg Jun 26, 2026
48796bd
feat: add initialization functions for stub and heading orders in des…
MikaelNordberg Jun 29, 2026
526193f
feat: enhance DrawerView tests with pivot actions and toggle state ch…
MikaelNordberg Jun 29, 2026
92aa665
feat: implement initialization functions for stub and heading orders …
MikaelNordberg Jun 29, 2026
0231e24
feat: add getDataViewMode function and corresponding tests for view m…
MikaelNordberg Jun 29, 2026
0d985d2
feat: enhance TableDataProvider tests with additional data view modes…
MikaelNordberg Jun 29, 2026
701ba2d
Fixes after code review
MikaelNordberg Jun 30, 2026
c964401
feat: extend filterStubAndHeadingArrays to support chart view with ad…
MikaelNordberg Jun 30, 2026
16a480a
feat: refactor pivot functions to consolidate mobile and desktop logi…
MikaelNordberg Jun 30, 2026
7037ffd
feat: simplify metadata initialization in TableDataProviderUtils tests
MikaelNordberg Jul 1, 2026
50b9716
Merge branch 'main' into feature/chart-autopivot
MikaelNordberg Jul 1, 2026
6fa7225
If time variable has only 1 value use the variable with the most valu…
MikaelNordberg Jul 2, 2026
a2b3b1d
feat: update unit tests for initStubAndHeadingChart to prioritize var…
MikaelNordberg Jul 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/pxweb2/public/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,7 @@ globalThis.PxWeb2Config = {
sv: '', // Set to your Swedish homepage URL
en: '', // Set to your English homepage URL
},
features: {
chartEnabled: true,
},
};
Original file line number Diff line number Diff line change
@@ -1,49 +1,114 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen } from '@testing-library/react';
import { screen } from '@testing-library/react';
import '@testing-library/jest-dom/vitest';
import { MemoryRouter } from 'react-router';
import userEvent from '@testing-library/user-event';

import { DrawerView } from './DrawerView';
import { renderWithProviders } from '../../../util/testing-utils';

const mockGetConfig = vi.fn();
const {
defaultMockConfig,
mockGetConfig,
mockPivotToTable,
mockPivotToChart,
mockIsMobile,
} = vi.hoisted(() => {
const defaultMockConfig = {
baseApplicationPath: '/',
features: {
chartEnabled: true,
},
language: {
defaultLanguage: 'en',
fallbackLanguage: 'en',
supportedLanguages: [{ shorthand: 'en' }],
positionInPath: 'after',
},
};

return {
defaultMockConfig,
mockGetConfig: vi.fn(() => defaultMockConfig),
mockPivotToTable: vi.fn(),
mockPivotToChart: vi.fn(),
mockIsMobile: vi.fn(() => false),
};
});

vi.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => key,
}),
initReactI18next: {
type: '3rdParty',
init: () => undefined,
},
}));

vi.mock('../../../util/config/getConfig', () => ({
getConfig: () => mockGetConfig(),
getConfig: mockGetConfig,
}));

vi.mock('../../../context/useTableData', () => ({
default: () => ({
pivotToTable: mockPivotToTable,
pivotToChart: mockPivotToChart,
}),
}));

vi.mock('../../../context/useApp', () => ({
default: () => ({
isMobile: mockIsMobile(),
}),
}));

interface MockActionItemProps {
label?: string;
ariaLabel?: string;
onClick?: () => void;
toggleState?: boolean;
}

vi.mock('@pxweb2/pxweb2-ui', () => ({
ContentBox: ({ children }: { children: React.ReactNode }) => (
<div data-testid="content-box">{children}</div>
),
ActionItem: ({ label }: MockActionItemProps) => (
<button data-testid="action-item">{label}</button>
),
LocalAlert: ({ children }: { children: React.ReactNode }) => (
<div data-testid="local-alert">{children}</div>
),
}));
vi.mock('@pxweb2/pxweb2-ui', async (importOriginal) => {
const actual = await importOriginal<typeof import('@pxweb2/pxweb2-ui')>();

return {
...actual,
ContentBox: ({ children }: { children: React.ReactNode }) => (
<div data-testid="content-box">{children}</div>
),
ActionItem: ({
label,
ariaLabel,
onClick,
toggleState,
}: MockActionItemProps) => (
<button
data-testid="action-item"
aria-label={ariaLabel}
data-toggle-state={toggleState ? 'on' : 'off'}
onClick={onClick}
>
{label}
</button>
),
LocalAlert: ({ children }: { children: React.ReactNode }) => (
<div data-testid="local-alert">{children}</div>
),
};
});

beforeEach(() => {
mockGetConfig.mockReturnValue({
features: {
chartEnabled: true,
},
});
vi.clearAllMocks();
mockGetConfig.mockReset();
mockGetConfig.mockReturnValue(defaultMockConfig);
mockIsMobile.mockReturnValue(false);
});

describe('DrawerView', () => {
it('renders successfully', () => {
render(
renderWithProviders(
<MemoryRouter>
<DrawerView />
</MemoryRouter>,
Expand All @@ -56,12 +121,13 @@ describe('DrawerView', () => {

it('renders info alert when chart view is disabled', () => {
mockGetConfig.mockReturnValue({
...defaultMockConfig,
features: {
chartEnabled: false,
},
});

render(
renderWithProviders(
<MemoryRouter>
<DrawerView />
</MemoryRouter>,
Expand All @@ -76,4 +142,78 @@ describe('DrawerView', () => {
it('has correct display name', () => {
expect(DrawerView.displayName).toBe('DrawerView');
});

it('clicking table action pivots to desktop table when not mobile', async () => {
renderWithProviders(
<MemoryRouter initialEntries={['/?view=linechart']}>
<DrawerView />
</MemoryRouter>,
);

const user = userEvent.setup();
await user.click(
screen.getByRole('button', {
name: 'presentation_page.side_menu.view.table.title',
}),
);

expect(mockPivotToTable).toHaveBeenCalledTimes(1);
expect(mockPivotToTable).toHaveBeenCalledWith(false);
});

it('clicking table action pivots to mobile table when mobile', async () => {
mockIsMobile.mockReturnValue(true);

renderWithProviders(
<MemoryRouter initialEntries={['/?view=linechart']}>
<DrawerView />
</MemoryRouter>,
);

const user = userEvent.setup();
await user.click(
screen.getByRole('button', {
name: 'presentation_page.side_menu.view.table.title',
}),
);

expect(mockPivotToTable).toHaveBeenCalledTimes(1);
expect(mockPivotToTable).toHaveBeenCalledWith(true);
});

it('clicking linechart action pivots to chart', async () => {
renderWithProviders(
<MemoryRouter>
<DrawerView />
</MemoryRouter>,
);

const user = userEvent.setup();
await user.click(
screen.getByRole('button', {
name: 'presentation_page.side_menu.view.linechart.title',
}),
);

expect(mockPivotToChart).toHaveBeenCalledTimes(1);
expect(mockPivotToTable).not.toHaveBeenCalled();
});

it('sets correct toggleState from search param view=linechart', () => {
renderWithProviders(
<MemoryRouter initialEntries={['/?view=linechart']}>
<DrawerView />
</MemoryRouter>,
);

const tableButton = screen.getByRole('button', {
name: 'presentation_page.side_menu.view.table.title',
});
const chartButton = screen.getByRole('button', {
name: 'presentation_page.side_menu.view.linechart.title',
});

expect(tableButton).toHaveAttribute('data-toggle-state', 'off');
expect(chartButton).toHaveAttribute('data-toggle-state', 'on');
});
});
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
import { useTranslation } from 'react-i18next';
import { useSearchParams } from 'react-router';

import { ActionItem, ContentBox, LocalAlert } from '@pxweb2/pxweb2-ui';
import useTableData from '../../../context/useTableData';
import useApp from '../../../context/useApp';
import { getConfig } from '../../../util/config/getConfig';
import {
ViewMode,
getSearchParamsWithViewMode,
getViewMode,
} from '../../../pages/TableViewer/Utils/tableViewerHelper';
import { ActionItem, ContentBox, LocalAlert } from '@pxweb2/pxweb2-ui';
import classes from './DrawerView.module.scss';

export function DrawerView() {
const { t } = useTranslation();
const [searchParams, setSearchParams] = useSearchParams();
const { pivotToChart, pivotToTable } = useTableData();
const { isMobile } = useApp();
const config = getConfig();
const chartEnabled = config.features?.chartEnabled === true;
const selectedViewMode = getViewMode(searchParams, chartEnabled);

function setViewMode(viewMode: ViewMode) {
setSearchParams(getSearchParamsWithViewMode(searchParams, viewMode));
}
Expand All @@ -29,7 +35,10 @@ export function DrawerView() {
size="large"
label={t('presentation_page.side_menu.view.table.title')}
ariaLabel={t('presentation_page.side_menu.view.table.title')}
onClick={() => setViewMode('table')}
onClick={() => {
pivotToTable(isMobile);
setViewMode('table');
}}
toggleState={selectedViewMode === 'table'}
/>
</li>
Expand All @@ -39,7 +48,10 @@ export function DrawerView() {
size="large"
label={t('presentation_page.side_menu.view.linechart.title')}
ariaLabel={t('presentation_page.side_menu.view.linechart.title')}
onClick={() => setViewMode('linechart')}
onClick={() => {
pivotToChart();
setViewMode('linechart');
}}
toggleState={selectedViewMode === 'linechart'}
/>
</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ import useTableData from '../../context/useTableData';
import useVariables from '../../context/useVariables';
import { useDebounce } from '@uidotdev/usehooks';
import { getConfig } from '../../util/config/getConfig';
import { getViewMode } from '../../pages/TableViewer/Utils/tableViewerHelper';
import {
getViewMode,
getDataViewMode,
} from '../../pages/TableViewer/Utils/tableViewerHelper';

type propsType = {
readonly selectedTabId: string;
Expand Down Expand Up @@ -139,11 +142,17 @@ export function Presentation({
}
};
});

// Get the right view mode when switching between mobile/desktop screen sizes.
useEffect(() => {
if (isMobile) {
tableData.pivotToMobile();
} else {
tableData.pivotToDesktop();
if (viewMode === 'table') {
if (isMobile) {
tableData.pivotToMobile();
} else {
tableData.pivotToDesktop();
}
} else if (viewMode === 'linechart') {
tableData.pivotToChart();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isMobile]);
Expand All @@ -162,7 +171,8 @@ export function Presentation({

if (initialRun && !hasSelectedValues) {
if (getSavedQueryId()?.length > 0) {
tableData.fetchSavedQuery(getSavedQueryId(), i18n, isMobile);
const dataViewMode = getDataViewMode(viewMode, isMobile);
tableData.fetchSavedQuery(getSavedQueryId(), i18n, dataViewMode);
} else {
fetchTableDataIfAllowed();
}
Expand Down Expand Up @@ -203,7 +213,8 @@ export function Presentation({

function fetchTableDataIfAllowed() {
if (variables.isMatrixSizeAllowed) {
tableData.fetchTableData(tableId, i18n, isMobile);
const dataViewMode = getDataViewMode(viewMode, isMobile);
tableData.fetchTableData(tableId, i18n, dataViewMode);
} else {
// fade table and give warning
setIsFadingTable(true);
Expand Down
5 changes: 5 additions & 0 deletions packages/pxweb2/src/app/context/DataViewModeType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum DataViewModeType {
MobileTable = 'MobileTable',
DesktopTable = 'DesktopTable',
Chart = 'Chart',
}
Loading
Loading