Skip to content

Commit

Permalink
Merge pull request #6023 from beyondessential/dev
Browse files Browse the repository at this point in the history
merge: update branch with latest dev
  • Loading branch information
avaek authored Dec 8, 2024
2 parents 6507d69 + 5917f65 commit a6abebd
Show file tree
Hide file tree
Showing 24 changed files with 912 additions and 519 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,4 @@ In order to automatically format code in VS Code according to our style guide:
1. Install [Prettier for VS Code](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode).
2. Enable the **Editor: Format on Save** setting: `"editor.formatOnSave": true`.

Your files will now be formatted automatically when you save them.
Your files will now be formatted automatically when you save them
2 changes: 1 addition & 1 deletion packages/admin-panel/src/VizBuilderApp/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ export const DASHBOARD_ITEM_VIZ_TYPES = {
// Matrix
MATRIX: {
name: 'Matrix',
schema: MatrixVizBuilderConfigSchema,
// schema: MatrixVizBuilderConfigSchema,
vizMatchesType: viz => viz.type === 'matrix',
initialConfig: {
type: 'matrix',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,77 @@ describe('matrix', () => {
);
expect(result).toEqual(expectedData);
});

it('can include entity columns', async () => {
const expectedData = {
columns: [
{
entityCode: 'TO',
key: 'Tonga',
title: 'Tonga',
},
{
key: 'InfrastructureType',
title: 'InfrastructureType',
},
{
key: 'Laos',
title: 'Laos',
},
],
rows: [
{
dataElement: 'hospital',
InfrastructureType: 'medical center',
Tonga: 0,
Laos: 3,
},
{
InfrastructureType: 'medical center',
dataElement: 'clinic',
Tonga: 9,
Laos: 4,
},
{
InfrastructureType: 'others',
Tonga: 0,
dataElement: 'park',
},
{
InfrastructureType: 'others',
Tonga: 5,
dataElement: 'library',
},
],
};
// Test setting fixed columns
const output1 = buildOutput(
{
type: 'matrix',
rowField: 'FacilityType',
columns: [{ entityLabel: 'Tonga', entityCode: 'TO' }, 'InfrastructureType', 'Laos'],
},
reportServerAggregator,
);
const result1 = await output1(
TransformTable.fromRows(MULTIPLE_TRANSFORMED_DATA_FOR_SPECIFIED_COLUMNS),
);
expect(result1).toEqual(expectedData);

// Test setting fixed & dynamic columns
const output2 = buildOutput(
{
type: 'matrix',
rowField: 'FacilityType',
columns: [{ entityLabel: 'Tonga', entityCode: 'TO' }, '*'],
},
reportServerAggregator,
);
const result2 = await output2(
TransformTable.fromRows(MULTIPLE_TRANSFORMED_DATA_FOR_SPECIFIED_COLUMNS),
);
expect(result2).toEqual(expectedData);
});
});

describe('categoryField', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const paramsValidator = yup.object().shape(
{
columns: yup.lazy((value: unknown) =>
Array.isArray(value)
? yup.array().of(yup.string().required())
? yup.array()
: yup.mixed<'*'>().oneOf(['*'], "columns must be either '*' or an array"),
),
rowField: yup
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import pick from 'lodash.pick';
import { MatrixEntityCell } from '@tupaia/types';
import { TransformTable } from '../../../transform';

import { Row } from '../../../types';
import { MatrixParams, Matrix } from './types';

function isMatrixEntityCell(cell: unknown): cell is MatrixEntityCell {
return typeof cell === 'object' && cell !== null && 'entityLabel' in cell && 'entityCode' in cell;
}

/** TODO: currently we are using 'dataElement' as a key in rows to specify row field (name),
* eventually we want to change Tupaia front end logic to use 'rowField' instead of 'dataElement'
*/
Expand Down Expand Up @@ -36,19 +40,46 @@ export class MatrixBuilder {
* eventually we want to refactor Tupaia frontend logic to render columns with an array formatted as
* '[ ${columnName} ]'
*/
const assignColumnSetToMatrixData = (columns: string[]) => {
this.matrixData.columns = columns.map(c => ({ key: c, title: c }));
const assignColumnSetToMatrixData = (columns: (string | MatrixEntityCell)[]) => {
this.matrixData.columns = columns.map(c => {
if (isMatrixEntityCell(c)) {
return {
key: c.entityLabel,
title: c.entityLabel,
entityCode: c.entityCode,
};
}
return { key: c as string, title: c as string };
});
};

const getRemainingFieldsFromRows = (includeFields: string[], excludeFields: string[]) => {
return this.table
.getColumns()
.filter(
columnName => !excludeFields.includes(columnName) && !includeFields.includes(columnName),
const getRemainingFieldsFromRows = (
includeFields: (string | MatrixEntityCell)[],
excludeFields: string[],
) => {
const entityFields = includeFields.filter(field =>
isMatrixEntityCell(field),
) as MatrixEntityCell[];
const entityFieldNames = entityFields.map(field => field.entityLabel);

return this.table.getColumns().filter((columnName: string) => {
return (
!entityFieldNames.includes(columnName) &&
!excludeFields.includes(columnName) &&
!includeFields.includes(columnName)
);
});
};

const { includeFields, excludeFields } = this.params.columns;
const { includeFields: originalIncludeFields, excludeFields } = this.params.columns;

// If the include fields are a matrix entity cell not in the table columns, don't include them
const includeFields = originalIncludeFields.filter(field => {
if (isMatrixEntityCell(field)) {
return this.table.getColumns().includes(field.entityLabel);
}
return true;
});

const remainingFields = includeFields.includes('*')
? getRemainingFieldsFromRows(includeFields, excludeFields)
Expand All @@ -74,12 +105,16 @@ export class MatrixBuilder {
}
rows.push(newRows);
});

this.matrixData.rows = rows;
}

private adjustRowsToUseIncludedColumns() {
const { includeFields } = this.params.columns;
const includeFields = this.params.columns.includeFields.map(field => {
if (isMatrixEntityCell(field)) {
return field.entityLabel;
}
return field;
});
const newRows: Row[] = [];

if (includeFields.includes('*')) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { MatrixEntityCell } from '@tupaia/types';
import { Row } from '../../../types';

export type MatrixParams = {
columns: { includeFields: string[]; excludeFields: string[] };
columns: { includeFields: (string | MatrixEntityCell)[]; excludeFields: string[] };
rows: { rowField: string; categoryField: string | undefined };
};

export type Matrix = {
columns: {
key: string;
title: string;
entityCode?: string;
}[];
rows: Row[];
};
2 changes: 0 additions & 2 deletions packages/report-server/src/reportBuilder/reportBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,9 @@ export class ReportBuilder {
}

const data = this.testData || [];

const context = await buildContext(this.config.transform, this.reqContext);
const transform = buildTransform(this.config.transform, context);
const transformedData = await transform(TransformTable.fromRows(data));

const output = buildOutput(this.config.output, this.reqContext.aggregator);
const outputData = await output(transformedData);

Expand Down
1 change: 0 additions & 1 deletion packages/report-server/src/routes/FetchReportRoute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ export class FetchReportRoute extends Route<FetchReportRequest> {
};

const reportBuilder = new ReportBuilder(reqContext).setConfig(report.config);

const reportResponse = await reportBuilder.build();

const { results } = reportResponse;
Expand Down
42 changes: 21 additions & 21 deletions packages/tupaia-web/src/features/EntitySearch/EntityMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,38 +11,38 @@ import { Button, IconButton, List as MuiList, ListItemProps } from '@material-ui
import { useEntities } from '../../api/queries';

const FlexRow = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
display: flex;
align-items: center;
justify-content: space-between;
`;

const List = styled(MuiList)`
margin-left: 1rem;
margin-left: 1rem;
.MuiIconButton-root,
.MuiSvgIcon-root {
font-size: 1.5rem;
}
.MuiIconButton-root,
.MuiSvgIcon-root {
font-size: 1.5rem;
}
// Hide expand icon when there are no children but keep the element on the page for spacing
.MuiButtonBase-root.MuiIconButton-root.Mui-disabled .MuiSvgIcon-root {
color: transparent;
}
// Hide expand icon when there are no children but keep the element on the page for spacing
.MuiButtonBase-root.MuiIconButton-root.Mui-disabled .MuiSvgIcon-root {
color: transparent;
}
`;

const MenuLink = styled(Button).attrs({
component: Link,
})<ListItemProps>`
display: inline;
flex: 1;
justify-content: flex-start;
text-transform: none;
padding: 0.8rem;
font-size: 0.875rem;
display: inline;
flex: 1;
justify-content: flex-start;
text-transform: none;
padding: 0.8rem;
font-size: 0.875rem;
.MuiSvgIcon-root {
vertical-align: bottom;
}
.MuiSvgIcon-root {
vertical-align: bottom;
}
`;

interface EntityMenuProps {
Expand Down
18 changes: 10 additions & 8 deletions packages/tupaia-web/src/features/Visuals/Matrix/Matrix.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import React, { useContext, useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import { useSearchParams } from 'react-router-dom';
import { useSearchParams, useParams } from 'react-router-dom';
import { Typography } from '@material-ui/core';
import { Matrix as MatrixComponent, NoData, SearchFilter } from '@tupaia/ui-components';
import { DashboardItemType, isMatrixReport } from '@tupaia/types';
Expand Down Expand Up @@ -39,6 +39,7 @@ const NoResultsMessage = styled(Typography)`

const MatrixVisual = () => {
const context = useContext(DashboardItemContext);
const { projectCode } = useParams();
const [urlSearchParams, setUrlSearchParams] = useSearchParams();
const activeDrillDownId = urlSearchParams.get(URL_SEARCH_PARAMS.REPORT_DRILLDOWN_ID);
const reportPeriod = urlSearchParams.get(URL_SEARCH_PARAMS.REPORT_PERIOD);
Expand All @@ -50,30 +51,31 @@ const MatrixVisual = () => {
const { columns = [], rows = [] } = report;
const [searchFilters, setSearchFilters] = useState<SearchFilter[]>([]);

const { drillDown, valueType } = config;

// memoise the parsed rows and columns so that they don't get recalculated on every render, for performance reasons
const parsedRows = useMemo(
() =>
parseRows(
rows,
undefined,
searchFilters,
drillDown,
valueType,
config,
urlSearchParams,
setUrlSearchParams,
projectCode!,
),
[
JSON.stringify(rows),
JSON.stringify(searchFilters),
JSON.stringify(drillDown),
valueType,
JSON.stringify(config),
JSON.stringify(urlSearchParams),
setUrlSearchParams,
projectCode,
],
);
const parsedColumns = useMemo(() => parseColumns(columns), [JSON.stringify(columns)]);
const parsedColumns = useMemo(
() => parseColumns(columns, projectCode!),
[JSON.stringify(columns), projectCode],
);

const updateSearchFilter = ({ key, value }: SearchFilter) => {
const filtersWithoutKey = searchFilters.filter(filter => filter.key !== key);
Expand Down
Loading

0 comments on commit a6abebd

Please sign in to comment.