Skip to content

Commit df4c5ec

Browse files
committed
Address comments and allow customization using resource customMetaData
1 parent 383bf02 commit df4c5ec

File tree

3 files changed

+163
-24
lines changed

3 files changed

+163
-24
lines changed

src/pages/studyView/StudyViewPage.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ import ResourceTab from '../../shared/components/resources/ResourceTab';
6262
import StudyViewURLWrapper from './StudyViewURLWrapper';
6363
import ResourcesTab, { RESOURCES_TAB_NAME } from './resources/ResourcesTab';
6464
import { ResourceData } from 'cbioportal-ts-api-client';
65+
import { getResourceConfig } from 'shared/lib/ResourceUtils';
6566
import $ from 'jquery';
6667
import { StudyViewComparisonGroup } from 'pages/groupComparison/GroupComparisonUtils';
6768
import { parse } from 'query-string';
@@ -546,21 +547,25 @@ export default class StudyViewPage extends React.Component<
546547
const tabs: JSX.Element[] = sorted.reduce((list, def) => {
547548
const data = resourceDataById[def.resourceId];
548549
if (data && data.length > 0) {
549-
const displayName =
550+
const config = getResourceConfig(def);
551+
const originalDisplayName =
550552
data.length > 1
551553
? pluralize(def.displayName, data.length)
552554
: def.displayName;
555+
const customDisplayName =
556+
config.customizedDisplayName || originalDisplayName;
557+
553558
list.push(
554559
<MSKTab
555560
key={getStudyViewResourceTabId(def.resourceId)}
556561
id={getStudyViewResourceTabId(def.resourceId)}
557-
linkText={displayName}
562+
linkText={originalDisplayName}
558563
onClickClose={this.closeResourceTab}
559564
>
560565
<ResourceTab
561566
resourceData={resourceDataById[def.resourceId]}
562567
urlWrapper={this.urlWrapper}
563-
resourceDisplayName={displayName}
568+
resourceDisplayName={customDisplayName}
564569
/>
565570
</MSKTab>
566571
);

src/pages/studyView/resources/FilesAndLinks.tsx

Lines changed: 95 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
ClinicalData,
2323
StudyViewFilter,
2424
} from 'cbioportal-ts-api-client';
25+
import { getResourceConfig } from 'shared/lib/ResourceUtils';
2526

2627
export interface IFilesLinksTable {
2728
resourceDisplayName: string;
@@ -250,13 +251,33 @@ export class FilesAndLinks extends React.Component<IFilesLinksTable, {}> {
250251
)
251252
)
252253
: [];
253-
const resourcesPerPatientColumnName =
254-
uniqueResourceTypes.length === 1 && uniqueResourceTypes[0]
255-
? `${pluralize(
256-
uniqueResourceTypes[0] as string,
257-
2
258-
)} per Patient`
259-
: 'Resources per Patient';
254+
let resourcesPerPatientColumnName = 'Resources per Patient';
255+
let shouldHideResourcesPerPatientColumn = false;
256+
257+
if (uniqueResourceTypes.length === 1 && uniqueResourceTypes[0]) {
258+
const def = this.props.store.resourceDefinitions.result?.find(
259+
d => d.displayName === uniqueResourceTypes[0]
260+
);
261+
if (def) {
262+
const config = getResourceConfig(def);
263+
if (config.columnHeader) {
264+
resourcesPerPatientColumnName = config.columnHeader;
265+
} else {
266+
resourcesPerPatientColumnName = `${pluralize(
267+
uniqueResourceTypes[0] as string,
268+
2
269+
)} per Patient`;
270+
}
271+
if (config.hideResourcesPerPatientColumn) {
272+
shouldHideResourcesPerPatientColumn = true;
273+
}
274+
} else {
275+
resourcesPerPatientColumnName = `${pluralize(
276+
uniqueResourceTypes[0] as string,
277+
2
278+
)} per Patient`;
279+
}
280+
}
260281

261282
let defaultColumns: Column<{ [id: string]: any }>[] = [
262283
{
@@ -357,8 +378,11 @@ export class FilesAndLinks extends React.Component<IFilesLinksTable, {}> {
357378
return <div>{data.description}</div>;
358379
},
359380
},
381+
];
360382

361-
{
383+
// Conditionally add the last column if not hidden
384+
if (!shouldHideResourcesPerPatientColumn) {
385+
defaultColumns.push({
362386
...this.getDefaultColumnConfig(
363387
'resourcesPerPatient',
364388
resourcesPerPatientColumnName,
@@ -367,8 +391,8 @@ export class FilesAndLinks extends React.Component<IFilesLinksTable, {}> {
367391
render: (data: { [id: string]: number }) => {
368392
return <div>{data.resourcesPerPatient}</div>;
369393
},
370-
},
371-
];
394+
});
395+
}
372396

373397
return defaultColumns;
374398
}
@@ -388,17 +412,67 @@ export class FilesAndLinks extends React.Component<IFilesLinksTable, {}> {
388412
<Else>
389413
<FilesLinksTableComponent
390414
initialItemsPerPage={20}
391-
headerComponent={
392-
<div className={'positionAbsolute'}>
393-
<strong>
394-
{
395-
this.resourceData.result
396-
?.totalItems
397-
}{' '}
398-
{this.props.resourceDisplayName}
399-
</strong>
400-
</div>
401-
}
415+
headerComponent={(() => {
416+
// Determine if there's only one unique resource type
417+
const uniqueResourceTypes =
418+
this.resourceData.result &&
419+
this.resourceData.result.data
420+
? _.uniq(
421+
this.resourceData.result.data.map(
422+
item =>
423+
item.typeOfResource as string
424+
)
425+
)
426+
: [];
427+
let def;
428+
if (
429+
uniqueResourceTypes.length === 1 &&
430+
uniqueResourceTypes[0]
431+
) {
432+
def = this.props.store.resourceDefinitions.result?.find(
433+
d =>
434+
d.displayName ===
435+
uniqueResourceTypes[0]
436+
);
437+
}
438+
let customName = '';
439+
if (def) {
440+
const config = getResourceConfig(def);
441+
if (config.customizedDisplayName) {
442+
customName =
443+
config.customizedDisplayName;
444+
}
445+
}
446+
447+
return (
448+
<div className={'positionAbsolute'}>
449+
<strong>
450+
{
451+
this.resourceData.result
452+
?.totalItems
453+
}{' '}
454+
{customName ? (
455+
customName
456+
) : (
457+
<>
458+
{pluralize(
459+
'sample',
460+
this.resourceData
461+
.result
462+
?.totalItems ||
463+
0
464+
)}{' '}
465+
with{' '}
466+
{
467+
this.props
468+
.resourceDisplayName
469+
}
470+
</>
471+
)}
472+
</strong>
473+
</div>
474+
);
475+
})()}
402476
data={this.resourceData.result?.data || []}
403477
columns={this.columns}
404478
showColumnVisibility={false}

src/shared/lib/ResourceUtils.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { ResourceDefinition } from 'cbioportal-ts-api-client';
2+
3+
export interface ResourceCustomConfig {
4+
customizedDisplayName?: string;
5+
columnHeader?: string;
6+
hideResourcesPerPatientColumn?: boolean;
7+
}
8+
9+
export const RESOURCE_CUSTOM_CONFIGS: Record<string, ResourceCustomConfig> = {
10+
HE: {
11+
customizedDisplayName: 'H&E Samples with Slides',
12+
columnHeader: 'H&E Slides per Sample',
13+
hideResourcesPerPatientColumn: true,
14+
},
15+
};
16+
17+
// Extend ResourceDefinition to include customMetaData if it's missing in the types
18+
interface ExtendedResourceDefinition extends ResourceDefinition {
19+
customMetaData?: string;
20+
}
21+
22+
export function getResourceConfig(
23+
def: ResourceDefinition
24+
): ResourceCustomConfig {
25+
let config: ResourceCustomConfig = {};
26+
const extendedDef = def as ExtendedResourceDefinition;
27+
28+
// 1. Load from local dictionary
29+
if (def.resourceId && RESOURCE_CUSTOM_CONFIGS[def.resourceId]) {
30+
config = { ...RESOURCE_CUSTOM_CONFIGS[def.resourceId] };
31+
}
32+
33+
// 2. Override with customMetaData
34+
if (extendedDef.customMetaData) {
35+
try {
36+
const customConfig = JSON.parse(extendedDef.customMetaData);
37+
if (customConfig.customizedDisplayName) {
38+
config.customizedDisplayName =
39+
customConfig.customizedDisplayName;
40+
} else if (customConfig.tabLabel) {
41+
// Fallback for backward compatibility
42+
config.customizedDisplayName = customConfig.tabLabel;
43+
}
44+
if (customConfig.columnHeader) {
45+
config.columnHeader = customConfig.columnHeader;
46+
}
47+
if (customConfig.hideResourcesPerPatientColumn !== undefined) {
48+
config.hideResourcesPerPatientColumn =
49+
customConfig.hideResourcesPerPatientColumn;
50+
}
51+
} catch (e) {
52+
console.warn(
53+
`Failed to parse customMetaData for resource ${def.resourceId}`,
54+
e
55+
);
56+
}
57+
}
58+
59+
return config;
60+
}

0 commit comments

Comments
 (0)