Skip to content

Commit

Permalink
Merge pull request #512 from bcgsc/release/v6.30.0
Browse files Browse the repository at this point in the history
Release/v6.30.0
  • Loading branch information
bnguyen-bcgsc authored Jul 30, 2024
2 parents 6b905db + 03dedfe commit a325837
Show file tree
Hide file tree
Showing 135 changed files with 4,627 additions and 2,037 deletions.
48 changes: 43 additions & 5 deletions app/common.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ type GroupType = {
name: string;
users: UserGroupMemberType[];
owner: UserType;
description: string;
};

type UserProjectsType = {
Expand All @@ -82,6 +83,44 @@ type UserType = {
projects?: UserProjectsType[];
type: string;
username: string;
allowNotifications: boolean;
} & RecordDefaults;

type ShortReportType = {
alternateIdentifier: string | null;
patientId: string;
} & RecordDefaults;

type ProjectType = {
name: string;
description?: string;
reportProject?: {
additionalProject: boolean;
};
users?: UserType[];
} & RecordDefaults;

type AppendixType = {
template: TemplateType;
project: {
name: string | null;
description: string | null;
} & RecordDefaults;
text: string;
} & RecordDefaults;

type VariantTextType = {
cancerType: string[];
project: {
ident: string | null;
name: string | null;
};
template: {
ident: string | null;
name: string | null;
};
text: string;
variantName: string;
} & RecordDefaults;

type ImageType = {
Expand Down Expand Up @@ -329,15 +368,14 @@ type MicrobialType = {
species: string | null;
} & RecordDefaults;

type AppendixType = RecordDefaults & {
text: string;
};

export {
RecordDefaults,
ShortReportType,
ProjectType,
AppendixType,
VariantTextType,
UserType,
TemplateType,
AppendixType,
AnyVariantType,
GroupType,
UserProjectsType,
Expand Down
3 changes: 2 additions & 1 deletion app/commonComponents.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
type SummaryProps = {
templateName: string;
isPrint: boolean;
printVersion?: 'stable' | 'beta' | null;
printVersion?: 'standardLayout' | 'condensedLayout' | null;
loadedDispatch?: (type: Record<'type', string>) => void;
visibleSections: string[] | null;
[x: string]: unknown;
};

Expand Down
5 changes: 3 additions & 2 deletions app/components/AsyncButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import './index.scss';

type AsyncButtonProps = {
className?: string;
children;
children?;
isLoading: boolean;
onClick?: () => void;
} & ButtonProps<'label', { component: 'label' }>;
} & ButtonProps<'label', { component?: 'label' }>;

const AsyncButton = ({
className,
Expand Down Expand Up @@ -46,6 +46,7 @@ const AsyncButton = ({
classes={{ label: `${loadingStarted ? 'async-button__label' : ''}` }}
className="async-button"
onClick={handleClick}
disabled={isLoading}
{...buttonProps}
>
{children}
Expand Down
35 changes: 28 additions & 7 deletions app/components/AuthenticatedRoute/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import { isAuthorized } from '@/services/management/auth';
* @returns {Route} a route component which checks authorization on render or redirects to login
*/
const AuthenticatedRoute = ({
component: Component, adminRequired, showNav, onToggleNav, ...rest
component: Component, managerRequired, templateEditorRequired, appendixEditorRequired, germlineRequired, showNav, onToggleNav, ...rest
}) => {
const { authorizationToken } = useSecurity();
const { adminAccess } = useResource();
const { managerAccess, adminAccess, templateEditAccess, appendixEditAccess, germlineAccess } = useResource();
const authOk = isAuthorized(authorizationToken);

const ChildComponent = useMemo(() => {
Expand All @@ -34,13 +34,28 @@ const AuthenticatedRoute = ({
};
}

if (!adminAccess && adminRequired) {
if (!managerAccess && managerRequired) {
return () => (
<Redirect to="/" />
);
}
if (!templateEditAccess && templateEditorRequired) {
return () => (
<Redirect to="/" />
);
}
if (!appendixEditAccess && appendixEditorRequired) {
return () => (
<Redirect to="/" />
);
}
if (!germlineAccess && germlineRequired) {
return () => (
<Redirect to="/" />
);
}
return Component;
}, [Component, adminAccess, adminRequired, authOk]);
}, [Component, adminAccess, managerAccess, templateEditAccess, germlineAccess, appendixEditAccess, managerRequired, templateEditorRequired, germlineRequired, appendixEditorRequired, authOk]);

if (showNav) {
onToggleNav(true);
Expand All @@ -57,7 +72,10 @@ const AuthenticatedRoute = ({
};

AuthenticatedRoute.propTypes = {
adminRequired: PropTypes.bool,
managerRequired: PropTypes.bool,
templateEditorRequired: PropTypes.bool,
appendixEditorRequired: PropTypes.bool,
germlineRequired: PropTypes.bool,
// eslint-disable-next-line react/forbid-prop-types
component: PropTypes.object.isRequired,
// eslint-disable-next-line react/forbid-prop-types
Expand All @@ -67,9 +85,12 @@ AuthenticatedRoute.propTypes = {
};

AuthenticatedRoute.defaultProps = {
adminRequired: false,
managerRequired: false,
templateEditorRequired: false,
appendixEditorRequired: false,
germlineRequired: false,
location: null,
onToggleNav: () => {},
onToggleNav: () => { },
showNav: false,
};

Expand Down
23 changes: 18 additions & 5 deletions app/components/DataTable/components/DetailDialog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,16 @@ const PrimitiveAttribute = ({ keyString, value }: PrimitiveAttributeProps) => (

type ObjectAttributesProps = {
obj: object;
columnMapping: Record<string, string>
objKey?: string;
columnMapping: Record<string, string>;
arrayKeyGetter?: Record<string, (val) => string>;
};

const ObjectAttributes = ({
obj,
objKey,
columnMapping,
arrayKeyGetter,
}: ObjectAttributesProps) => {
if (obj === null || obj === undefined) {
return null;
Expand All @@ -63,7 +67,11 @@ const ObjectAttributes = ({
if (Array.isArray(obj)) {
const inner = obj.map((mappedVal, idx) => {
const mappedValIsArray = Array.isArray(mappedVal);
const rowKey = `${mappedVal?.toString()}-${idx}`;
const rowKey = `${JSON.stringify(mappedVal)}-${idx}`;
let arrayKey = String(idx);
if (arrayKeyGetter && arrayKeyGetter[objKey]) {
arrayKey = arrayKeyGetter[objKey](mappedVal);
}

// Value is primitive or null
if (!mappedValIsArray && (typeof mappedVal !== 'object' || mappedVal === null)) {
Expand All @@ -89,15 +97,15 @@ const ObjectAttributes = ({
expandIcon={<ExpandMoreIcon />}
>
<Typography variant="subtitle2">
{`${idx}: ${mappedValIsArray ? '[' : '{'}`}
{`${arrayKey}: ${mappedValIsArray ? '[' : '{'}`}
</Typography>
</AccordionSummary>
<AccordionDetails
style={{
padding: 0,
}}
>
<ObjectAttributes obj={mappedVal} columnMapping={columnMapping} />
<ObjectAttributes arrayKeyGetter={arrayKeyGetter} objKey={objKey} obj={mappedVal} columnMapping={columnMapping} />
<div className="detail-dialog__row">
<Typography variant="subtitle2">
{`${mappedValIsArray ? ']' : '}'}`}
Expand Down Expand Up @@ -155,7 +163,7 @@ const ObjectAttributes = ({
{`${keyString}: ${mappedValIsArray ? '[' : '{'}`}
</Typography>
</div>
<ObjectAttributes obj={mappedVal} columnMapping={columnMapping} />
<ObjectAttributes arrayKeyGetter={arrayKeyGetter} obj={mappedVal} objKey={key} columnMapping={columnMapping} />
<div className="detail-dialog__row">
<Typography variant="subtitle2">
{`${mappedValIsArray ? ']' : '}'}`}
Expand Down Expand Up @@ -206,6 +214,11 @@ const DetailDialog = ({
<ObjectAttributes
obj={selectedRow}
columnMapping={columnMapping}
arrayKeyGetter={{
users: ({ username, firstName, lastName }) => `${firstName} ${lastName}` ?? username,
projects: ({ name }) => name,
groups: ({ name }) => name,
}}
/>
</DialogContent>
</Dialog>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.HTMLCellRenderer__container .ag-cell-wrapper {
align-items: start;
}
96 changes: 96 additions & 0 deletions app/components/DataTable/components/HTMLCellRenderer/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { ICellRendererParams } from '@ag-grid-community/core';
import React, {
useCallback, useEffect, useRef, useState,
} from 'react';

import './index.scss';

enum DisplayMode {
normal, compact,
}

type HTMLCellRendererProps = ICellRendererParams & {
mode: DisplayMode;
};

const APP_TEXT_CELL_MIN_HEIGHT = 250;

const HTMLCellRenderer = (props: HTMLCellRendererProps) => {
const {
data: { text }, node, api, mode = DisplayMode.normal,
} = props;
const [dispMode, setDispMode] = useState(mode);
const cellRef = useRef<HTMLDivElement | null>(null);

useEffect(() => {
const resizeObserver = new ResizeObserver((entries) => {
// onRowHeightChanged would cause ResizeObserver's loop to prematurely terminate
// https://github.com/juggle/resize-observer/issues/103#issuecomment-1711148285
setTimeout(() => {
if (cellRef.current) {
for (const entry of entries) {
const { height } = cellRef.current.getBoundingClientRect();
if (
Number((entry.target as HTMLElement).style.height)
!== height
) {
const htmlContainer = cellRef.current.closest('.HTMLCellRenderer__content');
if (dispMode === DisplayMode.normal) {
node.setRowHeight(entry.contentRect.height);
api.onRowHeightChanged();
} else {
if (htmlContainer.clientHeight < APP_TEXT_CELL_MIN_HEIGHT) {
(htmlContainer.closest('[role="gridcell"]') as HTMLElement).style.overflow = 'hidden';
}
node.setRowHeight(height < APP_TEXT_CELL_MIN_HEIGHT ? height : APP_TEXT_CELL_MIN_HEIGHT);
api.onRowHeightChanged();
}
}
}
}
}, 0);
});

if (cellRef.current) {
resizeObserver.observe(cellRef.current);
}

return () => {
resizeObserver.disconnect();
};
}, [api, node, dispMode]);

const handleOnClick = useCallback((evt) => {
const gridCellElem = evt.target.closest('[role="gridcell"]');

setDispMode((prevMode) => {
if (prevMode === DisplayMode.normal) {
if (gridCellElem.clientHeight > APP_TEXT_CELL_MIN_HEIGHT) {
gridCellElem.style.overflow = 'auto';
}
return DisplayMode.compact;
}
gridCellElem.style.overflow = 'hidden';
return DisplayMode.normal;
});
}, []);

return (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events
<div
className="HTMLCellRenderer__content"
onClick={handleOnClick}
ref={cellRef}
role="button"
tabIndex={0}
>
<div className="inner-html" dangerouslySetInnerHTML={{ __html: text }} />
</div>
);
};

export {
DisplayMode,
HTMLCellRenderer,
};
export default HTMLCellRenderer;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ICellRendererParams } from '@ag-grid-community/core';
import React from 'react';

const HyperlinkCellRenderer = (pogId: ICellRendererParams): JSX.Element => {
return (
<a href={"/reports/patients/" + pogId.value}>{pogId.value}</a>
);
}

export default HyperlinkCellRenderer;
Loading

0 comments on commit a325837

Please sign in to comment.