Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Waitp 1231 base matrix part 2 #4714

Merged
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
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
1,625 changes: 1,168 additions & 457 deletions packages/types/src/schemas/schemas.ts

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions packages/types/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export {
PresentationOptions,
ConditionValue,
ConditionType,
RangePresentationOptions,
ConditionalPresentationOptions,
} from './models-extra';
export * from './requests';
export * from './css';
4 changes: 4 additions & 0 deletions packages/types/src/types/models-extra/dashboard-item/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import type {
PresentationOptions,
ConditionValue,
ConditionType,
RangePresentationOptions,
ConditionalPresentationOptions,
} from './matricies';
import type { ComponentConfig } from './components';
import type {
Expand Down Expand Up @@ -70,4 +72,6 @@ export type {
PresentationOptions,
ConditionValue,
ConditionType,
RangePresentationOptions,
ConditionalPresentationOptions,
};
33 changes: 28 additions & 5 deletions packages/types/src/types/models-extra/dashboard-item/matricies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ export type MatrixConfig = BaseConfig & {
/**
* @description Category header rows can have values just like real rows, this is how you style them
*/
categoryPresentationOptions?: any;
categoryPresentationOptions?: PresentationOptions;
};

export type PresentationOptions = {
export type ConditionalPresentationOptions = {
type?: 'condition'; // optional key it seems like
conditions?: PresentationOptionCondition[];
/**
Expand All @@ -52,18 +52,41 @@ export type PresentationOptions = {
};
};

export type PresentationOptionCondition = {
key: string;
export type RangePresentationOptions = Record<CssColor, PresentationOptionRange> & {
type: 'range';
showRawValue?: boolean;
};

export type PresentationOptions = ConditionalPresentationOptions | RangePresentationOptions;

type BasePresentationOption = {
/**
* @description Specify the color of the display item
*/
color?: CssColor;
label?: string;
/**
* @description Specify the text for the legend item. Also used in the enlarged cell view
*/
description?: string;
/**
* @description Specify if you want a label to appear above the enlarged
*/
label?: string;
};
export type PresentationOptionCondition = BasePresentationOption & {
key: string;
/**
* @description the value to match against exactly, or an object with match criteria e.g. { '>=': 5.5 }
*/
condition: ConditionValue | Record<ConditionType, ConditionValue>;
legendLabel?: string;
};

export type PresentationOptionRange = BasePresentationOption & {
min?: number;
max?: number;
};

export type ConditionValue = string | number;

export enum ConditionType {
Expand Down
2 changes: 2 additions & 0 deletions packages/types/src/types/models-extra/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ export type {
PresentationOptions,
ConditionValue,
ConditionType,
RangePresentationOptions,
ConditionalPresentationOptions,
} from './dashboard-item';
4 changes: 3 additions & 1 deletion packages/ui-components/src/components/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,18 @@ interface DialogHeaderProps {
onClose: () => void;
color?: TypographyProps['color'];
children?: ReactNode;
titleVariant?: TypographyProps['variant'];
}

export const DialogHeader = ({
title,
onClose,
color = 'textPrimary',
children,
titleVariant = 'h3',
}: DialogHeaderProps) => (
<Header>
<DialogTitle color={color} variant="h3">
<DialogTitle color={color} variant={titleVariant}>
{title}
</DialogTitle>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/
import React, { useContext } from 'react';
import { DialogContent } from '@material-ui/core';
import { DialogContent, Typography } from '@material-ui/core';
import Markdown from 'markdown-to-jsx';
import { ACTION_TYPES, MatrixContext, MatrixDispatchContext } from './MatrixContext';
import styled from 'styled-components';
Expand All @@ -20,24 +20,40 @@ const DisplayWrapper = styled.div`
margin-bottom: 1rem;
`;

const SecondaryHeader = styled(Typography).attrs({
variant: 'h3',
})`
font-size: 1.2rem;
font-weight: ${({ theme }) => theme.typography.fontWeightMedium};
margin-bottom: 1rem;
`;
/**
* This is the modal that appears when a user clicks on a cell in the matrix
*/
export const EnlargedMatrixCell = () => {
const { enlargedCell } = useContext(MatrixContext);
const { enlargedCell, presentationOptions = {}, categoryPresentationOptions = {} } = useContext(
MatrixContext,
);
const dispatch = useContext(MatrixDispatchContext)!;
// If there is no enlarged cell set, don't render anything
if (!enlargedCell) return null;
const { rowTitle, value, displayValue, presentation = {} } = enlargedCell;
const { description = '', showRawValue } = presentation;
const { rowTitle, value, displayValue, presentation = {}, isCategory } = enlargedCell;
// If it is a category header cell, use the category presentation options, otherwise use the normal presentation options
const presentationOptionsToUse = isCategory ? categoryPresentationOptions : presentationOptions;

const { showRawValue } = presentationOptionsToUse;
const { description = '', label } = presentation;
const closeModal = () => {
dispatch({ type: ACTION_TYPES.SET_ENLARGED_CELL, payload: null });
};

// Render the description, and also value if showRawValue is true. Also handle newlines in markdown
const bodyText = `${description}${showRawValue ? ` ${value}` : ''}`.replace(/\\n/g, '\n\n');
return (
<Dialog open onClose={closeModal}>
<DialogHeader title={rowTitle} onClose={closeModal} />
<DialogHeader title={rowTitle} onClose={closeModal} titleVariant="h2" />
<Content>
{label && <SecondaryHeader>{label}</SecondaryHeader>}
<DisplayWrapper>{displayValue}</DisplayWrapper>
<Markdown>{bodyText.replace(/\\n/g, '\n\n')}</Markdown>
</Content>
Expand Down
33 changes: 17 additions & 16 deletions packages/ui-components/src/components/Matrix/Matrix.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
/*
* Tupaia
* Copyright (c) 2017 - 2022 Beyond Essential Systems Pty Ltd
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/

import React, { useEffect, useReducer, useRef } from 'react';
import styled from 'styled-components';
import { Table, TableBody } from '@material-ui/core';
import { MatrixHeaderRow } from './MatrixHeaderRow';
import { MatrixConfig, PresentationOptions } from '@tupaia/types';
import { MatrixColumnType, MatrixRowType } from '../../types';
import { hexToRgba } from './utils';
import { getFlattenedColumns } from './utils';
import { MatrixHeader } from './MatrixHeader';
import { ACTION_TYPES, MatrixContext, MatrixDispatchContext, matrixReducer } from './MatrixContext';
import { MatrixNavButtons } from './MatrixNavButtons';
import { MatrixRow } from './MatrixRow';
import { EnlargedMatrixCell } from './EnlargedMatrixCell';
import { MatrixLegend } from './MatrixLegend';

const MatrixTable = styled.table`
border-collapse: collapse;
border: 1px solid ${({ theme }) => hexToRgba(theme.palette.text.primary, 0.2)};
color: ${({ theme }) => theme.palette.text.primary};
height: 1px; // this is to make the cell content (eg. buttons) take full height of the cell, and does not actually get applied
td,
th {
border: 1px solid ${({ theme }) => hexToRgba(theme.palette.text.primary, 0.2)};
}
table-layout: fixed;
`;

// this is a scrollable container
Expand All @@ -31,13 +29,13 @@ const Wrapper = styled.div`
width: 100%;
overflow: auto;
`;
interface MatrixProps {

interface MatrixProps extends Omit<MatrixConfig, 'type' | 'name'> {
columns: MatrixColumnType[];
rows: MatrixRowType[];
presentationOptions: any;
}

export const Matrix = ({ columns = [], rows = [], presentationOptions }: MatrixProps) => {
export const Matrix = ({ columns = [], rows = [], ...config }: MatrixProps) => {
const [{ startColumn, expandedRows, maxColumns, enlargedCell }, dispatch] = useReducer(
matrixReducer,
{
Expand All @@ -55,21 +53,23 @@ export const Matrix = ({ columns = [], rows = [], presentationOptions }: MatrixP
if (!tableEl || !tableEl?.current || !tableEl?.current?.offsetWidth) return;
const { offsetWidth } = tableEl?.current;
// 200px is the max width of a column that we want to show
const maxColumns = Math.floor(offsetWidth / 200);
const usableWidth = offsetWidth - 200; // the max size of the first column (row title)
const maxColumns = Math.floor(usableWidth / 200);

const flattenedColumns = getFlattenedColumns(columns);
dispatch({
type: ACTION_TYPES.SET_MAX_COLUMNS,
payload: Math.min(maxColumns, columns.length),
payload: Math.min(maxColumns, flattenedColumns.length),
});
};

updateMaxColumns();
}, [tableEl?.current?.offsetWidth, columns]);

return (
<Wrapper>
<MatrixContext.Provider
value={{
presentationOptions,
...config,
columns,
rows,
startColumn,
Expand All @@ -82,13 +82,14 @@ export const Matrix = ({ columns = [], rows = [], presentationOptions }: MatrixP
<MatrixNavButtons />
<EnlargedMatrixCell />
<Table component={MatrixTable} ref={tableEl} stickyHeader>
<MatrixHeaderRow />
<MatrixHeader />
<TableBody>
{rows.map(row => (
<MatrixRow row={row} key={row.title} parents={[]} />
))}
</TableBody>
</Table>
<MatrixLegend />
</MatrixDispatchContext.Provider>
</MatrixContext.Provider>
</Wrapper>
Expand Down
54 changes: 37 additions & 17 deletions packages/ui-components/src/components/Matrix/MatrixCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@
import React, { useContext } from 'react';
import { TableCell, Button } from '@material-ui/core';
import styled from 'styled-components';
import { getIsUsingDots, getPresentationOption, hexToRgba } from './utils';
import { ConditionalPresentationOptions } from '@tupaia/types';
import {
checkIfApplyDotStyle,
getFlattenedColumns,
getIsUsingDots,
getPresentationOption,
hexToRgba,
} from './utils';
import { ACTION_TYPES, MatrixContext, MatrixDispatchContext } from './MatrixContext';
import { MatrixRowType } from '../../types';
import { MatrixColumnType, MatrixRowType } from '../../types';

export const Dot = styled.div<{ $color?: string }>`
width: 2rem;
Expand All @@ -25,11 +32,11 @@ export const Dot = styled.div<{ $color?: string }>`

const DataCell = styled(TableCell)`
vertical-align: middle;
text-align: center;
position: relative;
z-index: 1;
padding: 0;
height: 100%;
border: 1px solid ${({ theme }) => hexToRgba(theme.palette.text.primary, 0.2)};
`;

const DataCellContent = styled.div`
Expand All @@ -38,13 +45,12 @@ const DataCellContent = styled.div`
width: 100%;
display: flex;
align-items: center;
text-align: center;
justify-content: center;
`;

const ExpandButton = styled(Button)`
&:hover {
background-color: rgba(
255,
255,
255,
0.08
); // replicate the hover effect for all cell content, not just buttons
${Dot} {
transform: scale(1.2);
}
Expand All @@ -54,13 +60,28 @@ const DataCellContent = styled.div`
interface MatrixRowProps {
value: any;
rowTitle: MatrixRowType['title'];
isCategory?: boolean;
colKey: MatrixColumnType['key'];
}

export const MatrixCell = ({ value, rowTitle }: MatrixRowProps) => {
const { presentationOptions = {} } = useContext(MatrixContext);
/**
* This renders a cell in the matrix table. It can either be a category header cell or a data cell. If it has presentation options, it will be a button that can be clicked to expand the data. Otherwise, it will just display the data as normal
*/
export const MatrixCell = ({ value, rowTitle, isCategory, colKey }: MatrixRowProps) => {
const { presentationOptions = {}, categoryPresentationOptions = {}, columns } = useContext(
MatrixContext,
);
const dispatch = useContext(MatrixDispatchContext)!;
const isDots = getIsUsingDots(presentationOptions);
const presentation = getPresentationOption(presentationOptions, value);
// If the cell is a category, it means it is a category header cell and should use the category presentation options. Otherwise, it should use the normal presentation options

const allColumns = getFlattenedColumns(columns);
const colIndex = allColumns.findIndex(({ key }) => key === colKey);

const presentationOptionsForCell = isCategory ? categoryPresentationOptions : presentationOptions;
const isDots =
getIsUsingDots(presentationOptionsForCell) &&
checkIfApplyDotStyle(presentationOptionsForCell as ConditionalPresentationOptions, colIndex);
const presentation = getPresentationOption(presentationOptionsForCell, value);
const displayValue = isDots ? (
<Dot
$color={presentation?.color}
Expand All @@ -71,8 +92,6 @@ export const MatrixCell = ({ value, rowTitle }: MatrixRowProps) => {
) : (
value
);
// If the cell has presentation options, it should be a button so that the data can be expanded. Otherwise, it can just display the data as normal
const isButton = !!presentation;
const onClickCellButton = () => {
dispatch({
type: ACTION_TYPES.SET_ENLARGED_CELL,
Expand All @@ -81,14 +100,15 @@ export const MatrixCell = ({ value, rowTitle }: MatrixRowProps) => {
value,
displayValue,
presentation,
isCategory,
},
});
};
return (
<DataCell>
<DataCellContent
as={isButton ? Button : 'div'}
onClick={isButton ? onClickCellButton : undefined}
as={isDots ? ExpandButton : 'div'}
onClick={isDots ? onClickCellButton : undefined}
>
{displayValue}
</DataCellContent>
Expand Down
11 changes: 7 additions & 4 deletions packages/ui-components/src/components/Matrix/MatrixContext.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
/*
* Tupaia
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/

import { Dispatch, createContext } from 'react';
import { MatrixConfig, PresentationOptions } from '@tupaia/types';
import { MatrixColumnType, MatrixRowType } from '../../types';
import { PresentationOptions } from '@tupaia/types';

type RowTitle = MatrixRowType['title'];

const defaultContextValue = {
rows: [],
columns: [],
presentationOptions: {},
startColumn: 0,
maxColumns: 0,
expandedRows: [],
enlargedCell: null,
} as {
} as Omit<MatrixConfig, 'type' | 'name'> & {
rows: MatrixRowType[];
columns: MatrixColumnType[];
presentationOptions: PresentationOptions;
startColumn: number;
maxColumns: number;
expandedRows: RowTitle[];
Expand Down
Loading