import * as React from 'react'; import { observer } from 'mobx-react'; import _ from 'lodash'; import FixedHeaderTable, { IFixedHeaderTableProps } from '../FixedHeaderTable'; import { action, computed, observable, makeObservable } from 'mobx'; import autobind from 'autobind-decorator'; import { Column, SortDirection, } from '../../../../shared/components/lazyMobXTable/LazyMobXTable'; import { SampleTreatmentRow } from 'cbioportal-ts-api-client'; import { correctColumnWidth } from 'pages/studyView/StudyViewUtils'; import LabeledCheckbox from 'shared/components/labeledCheckbox/LabeledCheckbox'; import styles from 'pages/studyView/table/tables.module.scss'; import MobxPromise from 'mobxpromise'; import { stringListToIndexSet } from 'cbioportal-frontend-commons'; import ifNotDefined from 'shared/lib/ifNotDefined'; import { treatmentUniqueKey, TreatmentTableType, TreatmentGenericColumnHeader, TreatmentColumnCell, filterTreatmentCell, toNumericValue, } from './treatmentsTableUtil'; import { TreatmentsTable } from './AbstractTreatmentsTable'; import { MultiSelectionTableRow } from 'pages/studyView/table/MultiSelectionTable'; export enum SampleTreatmentsTableColumnKey { TREATMENT = 'Treatment', TIME = 'Pre / Post', COUNT = '#', } export type SampleTreatmentsTableColumn = { columnKey: SampleTreatmentsTableColumnKey; columnWidthRatio?: number; }; export type SampleTreatmentsTableProps = { tableType: TreatmentTableType; promise: MobxPromise; width: number; height: number; filters: string[][]; onSubmitSelection: (value: string[][]) => void; onChangeSelectedRows: (rowsKeys: string[]) => void; extraButtons?: IFixedHeaderTableProps< MultiSelectionTableRow >['extraButtons']; selectedRowsKeys: string[]; selectedTreatments: string[]; defaultSortBy: SampleTreatmentsTableColumnKey; columns: SampleTreatmentsTableColumn[]; }; const DEFAULT_COLUMN_WIDTH_RATIO: { [key in SampleTreatmentsTableColumnKey]: number; } = { [SampleTreatmentsTableColumnKey.TREATMENT]: 0.6, [SampleTreatmentsTableColumnKey.TIME]: 0.3, [SampleTreatmentsTableColumnKey.COUNT]: 0.2, }; class MultiSelectionTableComponent extends FixedHeaderTable< SampleTreatmentRow > {} @observer export class SampleTreatmentsTable extends TreatmentsTable< SampleTreatmentsTableProps > { @observable protected sortBy: SampleTreatmentsTableColumnKey; public static defaultProps = { cancerGeneFilterEnabled: false, }; constructor(props: SampleTreatmentsTableProps) { super(props); makeObservable(this); this.sortBy = this.props.defaultSortBy; } createTemporalRelationColumnCell(row: SampleTreatmentRow): JSX.Element { return
{row.time}
; } createNubmerColumnCell( row: SampleTreatmentRow, cellMargin: number ): JSX.Element { return ( this.toggleSelectRow(treatmentUniqueKey(row))} labelProps={{ style: { display: 'flex', justifyContent: 'space-between', marginLeft: 0, marginRight: cellMargin, }, }} inputProps={{ className: styles.autoMarginCheckbox, }} > {row.count.toLocaleString()} ); } getDefaultColumnDefinition = ( columnKey: SampleTreatmentsTableColumnKey, columnWidth: number, cellMargin: number ) => { const defaults: { [key in SampleTreatmentsTableColumnKey]: Column; } = { [SampleTreatmentsTableColumnKey.TREATMENT]: { name: columnKey, headerRender: () => ( ), render: (data: SampleTreatmentRow) => ( ), sortBy: (data: SampleTreatmentRow) => data.treatment, defaultSortDirection: 'asc' as 'asc', filter: filterTreatmentCell, width: columnWidth, }, [SampleTreatmentsTableColumnKey.TIME]: { name: columnKey, headerRender: () => ( ), render: this.createTemporalRelationColumnCell, sortBy: (data: SampleTreatmentRow) => data.time, defaultSortDirection: 'asc' as 'asc', filter: filterTreatmentCell, width: columnWidth, }, [SampleTreatmentsTableColumnKey.COUNT]: { tooltip: ( Number of samples acquired before treatment or after/on treatment ), name: columnKey, headerRender: () => ( ), render: (data: SampleTreatmentRow) => this.createNubmerColumnCell(data, 28), sortBy: (data: SampleTreatmentRow) => this.calculateMaxValueForTreatment(data) + toNumericValue(data.time + data.treatment), defaultSortDirection: 'desc' as 'desc', filter: filterTreatmentCell, width: columnWidth, }, }; return defaults[columnKey]; }; calculateMaxValueForTreatment(treatment: SampleTreatmentRow): number { const matchingSortedRows = this.tableData .filter(row => row.treatment == treatment.treatment) .sort((a, b) => b.count - a.count); return matchingSortedRows.length == 0 ? 0 : matchingSortedRows[0].count; } @computed get columnsWidth() { return _.reduce( this.props.columns, (acc, column) => { acc[column.columnKey] = correctColumnWidth( (column.columnWidthRatio ? column.columnWidthRatio : DEFAULT_COLUMN_WIDTH_RATIO[column.columnKey]) * this.props.width ); return acc; }, {} as { [key in SampleTreatmentsTableColumnKey]: number } ); } @computed get cellMargin() { return _.reduce( this.props.columns, (acc, column) => { acc[column.columnKey] = 0; return acc; }, {} as { [key in SampleTreatmentsTableColumnKey]: number } ); } @computed get tableData(): SampleTreatmentRow[] { return this.props.promise.result || []; } @computed get selectableTableData() { if (this.flattenedFilters.length === 0) { return this.tableData; } return _.filter( this.tableData, data => !this.flattenedFilters.includes(treatmentUniqueKey(data)) ); } @computed get preSelectedRows() { if (this.flattenedFilters.length === 0) { return []; } const order = stringListToIndexSet(this.flattenedFilters); return _.chain(this.tableData) .filter(data => this.flattenedFilters.includes(treatmentUniqueKey(data)) ) .sortBy(data => ifNotDefined( order[treatmentUniqueKey(data)], Number.POSITIVE_INFINITY ) ) .value(); } @computed get preSelectedRowsKeys() { return this.preSelectedRows.map(row => treatmentUniqueKey(row)); } @computed get tableColumns() { return this.props.columns.map(column => this.getDefaultColumnDefinition( column.columnKey, this.columnsWidth[column.columnKey], this.cellMargin[column.columnKey] ) ); } @action.bound afterSorting( sortBy: SampleTreatmentsTableColumnKey, sortDirection: SortDirection ) { this.sortBy = sortBy; this.sortDirection = sortDirection; } public render() { const tableId = `${this.props.tableType}-table`; return (
{this.props.promise.isComplete && ( )}
); } }