Skip to content

Commit a325837

Browse files
Merge pull request #512 from bcgsc/release/v6.30.0
Release/v6.30.0
2 parents 6b905db + 03dedfe commit a325837

File tree

135 files changed

+4627
-2037
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

135 files changed

+4627
-2037
lines changed

app/common.d.ts

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ type GroupType = {
5757
name: string;
5858
users: UserGroupMemberType[];
5959
owner: UserType;
60+
description: string;
6061
};
6162

6263
type UserProjectsType = {
@@ -82,6 +83,44 @@ type UserType = {
8283
projects?: UserProjectsType[];
8384
type: string;
8485
username: string;
86+
allowNotifications: boolean;
87+
} & RecordDefaults;
88+
89+
type ShortReportType = {
90+
alternateIdentifier: string | null;
91+
patientId: string;
92+
} & RecordDefaults;
93+
94+
type ProjectType = {
95+
name: string;
96+
description?: string;
97+
reportProject?: {
98+
additionalProject: boolean;
99+
};
100+
users?: UserType[];
101+
} & RecordDefaults;
102+
103+
type AppendixType = {
104+
template: TemplateType;
105+
project: {
106+
name: string | null;
107+
description: string | null;
108+
} & RecordDefaults;
109+
text: string;
110+
} & RecordDefaults;
111+
112+
type VariantTextType = {
113+
cancerType: string[];
114+
project: {
115+
ident: string | null;
116+
name: string | null;
117+
};
118+
template: {
119+
ident: string | null;
120+
name: string | null;
121+
};
122+
text: string;
123+
variantName: string;
85124
} & RecordDefaults;
86125

87126
type ImageType = {
@@ -329,15 +368,14 @@ type MicrobialType = {
329368
species: string | null;
330369
} & RecordDefaults;
331370

332-
type AppendixType = RecordDefaults & {
333-
text: string;
334-
};
335-
336371
export {
337372
RecordDefaults,
373+
ShortReportType,
374+
ProjectType,
375+
AppendixType,
376+
VariantTextType,
338377
UserType,
339378
TemplateType,
340-
AppendixType,
341379
AnyVariantType,
342380
GroupType,
343381
UserProjectsType,

app/commonComponents.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
type SummaryProps = {
22
templateName: string;
33
isPrint: boolean;
4-
printVersion?: 'stable' | 'beta' | null;
4+
printVersion?: 'standardLayout' | 'condensedLayout' | null;
55
loadedDispatch?: (type: Record<'type', string>) => void;
6+
visibleSections: string[] | null;
67
[x: string]: unknown;
78
};
89

app/components/AsyncButton/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import './index.scss';
1111

1212
type AsyncButtonProps = {
1313
className?: string;
14-
children;
14+
children?;
1515
isLoading: boolean;
1616
onClick?: () => void;
17-
} & ButtonProps<'label', { component: 'label' }>;
17+
} & ButtonProps<'label', { component?: 'label' }>;
1818

1919
const AsyncButton = ({
2020
className,
@@ -46,6 +46,7 @@ const AsyncButton = ({
4646
classes={{ label: `${loadingStarted ? 'async-button__label' : ''}` }}
4747
className="async-button"
4848
onClick={handleClick}
49+
disabled={isLoading}
4950
{...buttonProps}
5051
>
5152
{children}

app/components/AuthenticatedRoute/index.jsx

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ import { isAuthorized } from '@/services/management/auth';
1414
* @returns {Route} a route component which checks authorization on render or redirects to login
1515
*/
1616
const AuthenticatedRoute = ({
17-
component: Component, adminRequired, showNav, onToggleNav, ...rest
17+
component: Component, managerRequired, templateEditorRequired, appendixEditorRequired, germlineRequired, showNav, onToggleNav, ...rest
1818
}) => {
1919
const { authorizationToken } = useSecurity();
20-
const { adminAccess } = useResource();
20+
const { managerAccess, adminAccess, templateEditAccess, appendixEditAccess, germlineAccess } = useResource();
2121
const authOk = isAuthorized(authorizationToken);
2222

2323
const ChildComponent = useMemo(() => {
@@ -34,13 +34,28 @@ const AuthenticatedRoute = ({
3434
};
3535
}
3636

37-
if (!adminAccess && adminRequired) {
37+
if (!managerAccess && managerRequired) {
38+
return () => (
39+
<Redirect to="/" />
40+
);
41+
}
42+
if (!templateEditAccess && templateEditorRequired) {
43+
return () => (
44+
<Redirect to="/" />
45+
);
46+
}
47+
if (!appendixEditAccess && appendixEditorRequired) {
48+
return () => (
49+
<Redirect to="/" />
50+
);
51+
}
52+
if (!germlineAccess && germlineRequired) {
3853
return () => (
3954
<Redirect to="/" />
4055
);
4156
}
4257
return Component;
43-
}, [Component, adminAccess, adminRequired, authOk]);
58+
}, [Component, adminAccess, managerAccess, templateEditAccess, germlineAccess, appendixEditAccess, managerRequired, templateEditorRequired, germlineRequired, appendixEditorRequired, authOk]);
4459

4560
if (showNav) {
4661
onToggleNav(true);
@@ -57,7 +72,10 @@ const AuthenticatedRoute = ({
5772
};
5873

5974
AuthenticatedRoute.propTypes = {
60-
adminRequired: PropTypes.bool,
75+
managerRequired: PropTypes.bool,
76+
templateEditorRequired: PropTypes.bool,
77+
appendixEditorRequired: PropTypes.bool,
78+
germlineRequired: PropTypes.bool,
6179
// eslint-disable-next-line react/forbid-prop-types
6280
component: PropTypes.object.isRequired,
6381
// eslint-disable-next-line react/forbid-prop-types
@@ -67,9 +85,12 @@ AuthenticatedRoute.propTypes = {
6785
};
6886

6987
AuthenticatedRoute.defaultProps = {
70-
adminRequired: false,
88+
managerRequired: false,
89+
templateEditorRequired: false,
90+
appendixEditorRequired: false,
91+
germlineRequired: false,
7192
location: null,
72-
onToggleNav: () => {},
93+
onToggleNav: () => { },
7394
showNav: false,
7495
};
7596

app/components/DataTable/components/DetailDialog/index.tsx

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,16 @@ const PrimitiveAttribute = ({ keyString, value }: PrimitiveAttributeProps) => (
4848

4949
type ObjectAttributesProps = {
5050
obj: object;
51-
columnMapping: Record<string, string>
51+
objKey?: string;
52+
columnMapping: Record<string, string>;
53+
arrayKeyGetter?: Record<string, (val) => string>;
5254
};
5355

5456
const ObjectAttributes = ({
5557
obj,
58+
objKey,
5659
columnMapping,
60+
arrayKeyGetter,
5761
}: ObjectAttributesProps) => {
5862
if (obj === null || obj === undefined) {
5963
return null;
@@ -63,7 +67,11 @@ const ObjectAttributes = ({
6367
if (Array.isArray(obj)) {
6468
const inner = obj.map((mappedVal, idx) => {
6569
const mappedValIsArray = Array.isArray(mappedVal);
66-
const rowKey = `${mappedVal?.toString()}-${idx}`;
70+
const rowKey = `${JSON.stringify(mappedVal)}-${idx}`;
71+
let arrayKey = String(idx);
72+
if (arrayKeyGetter && arrayKeyGetter[objKey]) {
73+
arrayKey = arrayKeyGetter[objKey](mappedVal);
74+
}
6775

6876
// Value is primitive or null
6977
if (!mappedValIsArray && (typeof mappedVal !== 'object' || mappedVal === null)) {
@@ -89,15 +97,15 @@ const ObjectAttributes = ({
8997
expandIcon={<ExpandMoreIcon />}
9098
>
9199
<Typography variant="subtitle2">
92-
{`${idx}: ${mappedValIsArray ? '[' : '{'}`}
100+
{`${arrayKey}: ${mappedValIsArray ? '[' : '{'}`}
93101
</Typography>
94102
</AccordionSummary>
95103
<AccordionDetails
96104
style={{
97105
padding: 0,
98106
}}
99107
>
100-
<ObjectAttributes obj={mappedVal} columnMapping={columnMapping} />
108+
<ObjectAttributes arrayKeyGetter={arrayKeyGetter} objKey={objKey} obj={mappedVal} columnMapping={columnMapping} />
101109
<div className="detail-dialog__row">
102110
<Typography variant="subtitle2">
103111
{`${mappedValIsArray ? ']' : '}'}`}
@@ -155,7 +163,7 @@ const ObjectAttributes = ({
155163
{`${keyString}: ${mappedValIsArray ? '[' : '{'}`}
156164
</Typography>
157165
</div>
158-
<ObjectAttributes obj={mappedVal} columnMapping={columnMapping} />
166+
<ObjectAttributes arrayKeyGetter={arrayKeyGetter} obj={mappedVal} objKey={key} columnMapping={columnMapping} />
159167
<div className="detail-dialog__row">
160168
<Typography variant="subtitle2">
161169
{`${mappedValIsArray ? ']' : '}'}`}
@@ -206,6 +214,11 @@ const DetailDialog = ({
206214
<ObjectAttributes
207215
obj={selectedRow}
208216
columnMapping={columnMapping}
217+
arrayKeyGetter={{
218+
users: ({ username, firstName, lastName }) => `${firstName} ${lastName}` ?? username,
219+
projects: ({ name }) => name,
220+
groups: ({ name }) => name,
221+
}}
209222
/>
210223
</DialogContent>
211224
</Dialog>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.HTMLCellRenderer__container .ag-cell-wrapper {
2+
align-items: start;
3+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { ICellRendererParams } from '@ag-grid-community/core';
2+
import React, {
3+
useCallback, useEffect, useRef, useState,
4+
} from 'react';
5+
6+
import './index.scss';
7+
8+
enum DisplayMode {
9+
normal, compact,
10+
}
11+
12+
type HTMLCellRendererProps = ICellRendererParams & {
13+
mode: DisplayMode;
14+
};
15+
16+
const APP_TEXT_CELL_MIN_HEIGHT = 250;
17+
18+
const HTMLCellRenderer = (props: HTMLCellRendererProps) => {
19+
const {
20+
data: { text }, node, api, mode = DisplayMode.normal,
21+
} = props;
22+
const [dispMode, setDispMode] = useState(mode);
23+
const cellRef = useRef<HTMLDivElement | null>(null);
24+
25+
useEffect(() => {
26+
const resizeObserver = new ResizeObserver((entries) => {
27+
// onRowHeightChanged would cause ResizeObserver's loop to prematurely terminate
28+
// https://github.com/juggle/resize-observer/issues/103#issuecomment-1711148285
29+
setTimeout(() => {
30+
if (cellRef.current) {
31+
for (const entry of entries) {
32+
const { height } = cellRef.current.getBoundingClientRect();
33+
if (
34+
Number((entry.target as HTMLElement).style.height)
35+
!== height
36+
) {
37+
const htmlContainer = cellRef.current.closest('.HTMLCellRenderer__content');
38+
if (dispMode === DisplayMode.normal) {
39+
node.setRowHeight(entry.contentRect.height);
40+
api.onRowHeightChanged();
41+
} else {
42+
if (htmlContainer.clientHeight < APP_TEXT_CELL_MIN_HEIGHT) {
43+
(htmlContainer.closest('[role="gridcell"]') as HTMLElement).style.overflow = 'hidden';
44+
}
45+
node.setRowHeight(height < APP_TEXT_CELL_MIN_HEIGHT ? height : APP_TEXT_CELL_MIN_HEIGHT);
46+
api.onRowHeightChanged();
47+
}
48+
}
49+
}
50+
}
51+
}, 0);
52+
});
53+
54+
if (cellRef.current) {
55+
resizeObserver.observe(cellRef.current);
56+
}
57+
58+
return () => {
59+
resizeObserver.disconnect();
60+
};
61+
}, [api, node, dispMode]);
62+
63+
const handleOnClick = useCallback((evt) => {
64+
const gridCellElem = evt.target.closest('[role="gridcell"]');
65+
66+
setDispMode((prevMode) => {
67+
if (prevMode === DisplayMode.normal) {
68+
if (gridCellElem.clientHeight > APP_TEXT_CELL_MIN_HEIGHT) {
69+
gridCellElem.style.overflow = 'auto';
70+
}
71+
return DisplayMode.compact;
72+
}
73+
gridCellElem.style.overflow = 'hidden';
74+
return DisplayMode.normal;
75+
});
76+
}, []);
77+
78+
return (
79+
// eslint-disable-next-line jsx-a11y/click-events-have-key-events
80+
<div
81+
className="HTMLCellRenderer__content"
82+
onClick={handleOnClick}
83+
ref={cellRef}
84+
role="button"
85+
tabIndex={0}
86+
>
87+
<div className="inner-html" dangerouslySetInnerHTML={{ __html: text }} />
88+
</div>
89+
);
90+
};
91+
92+
export {
93+
DisplayMode,
94+
HTMLCellRenderer,
95+
};
96+
export default HTMLCellRenderer;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { ICellRendererParams } from '@ag-grid-community/core';
2+
import React from 'react';
3+
4+
const HyperlinkCellRenderer = (pogId: ICellRendererParams): JSX.Element => {
5+
return (
6+
<a href={"/reports/patients/" + pogId.value}>{pogId.value}</a>
7+
);
8+
}
9+
10+
export default HyperlinkCellRenderer;

0 commit comments

Comments
 (0)