SuccessTable.tsx 8.13 KB
Newer Older
Lijiao's avatar
Lijiao committed
1
import * as React from 'react';
Lijiaoa's avatar
Lijiaoa committed
2
import {
3
    Stack,
Lijiaoa's avatar
Lijiaoa committed
4
5
6
    DetailsList,
    IDetailsListProps,
    IColumn,
7
8
    Icon,
    DetailsRow,
Lijiaoa's avatar
Lijiaoa committed
9
10
11
12
13
14
15
    IRenderFunction,
    IDetailsHeaderProps,
    Sticky,
    StickyPositionType,
    ScrollablePane,
    ScrollbarVisibility
} from '@fluentui/react';
Lijiaoa's avatar
Lijiaoa committed
16
import DefaultMetric from './DefaultMetric';
17
import OpenRow from '@/components/common/ExpandableDetails/OpenRow';
Lijiaoa's avatar
Lijiaoa committed
18
19
20
21
22
23
24
25
import CopyButton from '@components/common/CopyButton';
import { convertDuration, copyAndSort } from '@static/function';
import { TRIALS } from '@static/datamodel';
import { SortInfo } from '@static/interface';
import { DETAILTABS } from '@components/nav/slideNav/NNItabs';
import '@style/experiment/overview/succTable.scss';
import '@style/common/trialStatus.css';
import '@style/openRow.scss';
Lijiao's avatar
Lijiao committed
26
27

interface SuccessTableProps {
28
    trialIds: string[];
29
    updateOverviewPage: () => void;
30
31
    expandRowIDs: Set<string>;
    changeExpandRowIDs: Function;
Lijiao's avatar
Lijiao committed
32
33
}

34
35
36
interface SuccessTableState {
    columns: IColumn[];
    source: Array<any>;
37
    sortInfo: SortInfo;
38
}
Lijiao's avatar
Lijiao committed
39

40
class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState> {
Lijiao's avatar
Lijiao committed
41
42
    constructor(props: SuccessTableProps) {
        super(props);
43
44
45
        this.state = {
            columns: this.columns,
            source: TRIALS.table(this.props.trialIds),
46
            sortInfo: { field: '', isDescend: false }
47
        };
Lijiao's avatar
Lijiao committed
48
49
    }

50
51
52
53
    componentDidUpdate(prevProps: SuccessTableProps): void {
        if (this.props.trialIds !== prevProps.trialIds) {
            const { trialIds } = this.props;
            this.setState(() => ({ source: TRIALS.table(trialIds) }));
54
        }
55
    }
56

57
58
59
60
    render(): React.ReactNode {
        const { columns, source, sortInfo } = this.state;
        const keepSortedSource = copyAndSort(source, sortInfo.field, sortInfo.isDescend);
        const isNoneData = source.length === 0 ? true : false;
61

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
        return (
            <div id='succTable'>
                <ScrollablePane className='scrollPanel' scrollbarVisibility={ScrollbarVisibility.auto}>
                    <DetailsList
                        columns={columns}
                        items={keepSortedSource}
                        setKey='set'
                        compact={true}
                        onRenderRow={this.onRenderRow}
                        onRenderDetailsHeader={this.onRenderDetailsHeader}
                        selectionMode={0} // close selector function
                        className='succTable'
                    />
                </ScrollablePane>
                {isNoneData && <div className='succTable-tooltip'>{this.tooltipStr}</div>}
            </div>
        );
    }

    private onColumnClick = (_ev: React.MouseEvent<HTMLElement>, getColumn: IColumn): void => {
82
83
84
85
86
87
88
89
90
91
        const { columns, source } = this.state;
        const newColumns: IColumn[] = columns.slice();
        const currColumn: IColumn = newColumns.filter(item => getColumn.key === item.key)[0];
        newColumns.forEach((newCol: IColumn) => {
            if (newCol === currColumn) {
                currColumn.isSortedDescending = !currColumn.isSortedDescending;
                currColumn.isSorted = true;
            } else {
                newCol.isSorted = false;
                newCol.isSortedDescending = true;
Lijiao's avatar
Lijiao committed
92
            }
93
94
        });
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
95
        const newItems = copyAndSort(source, currColumn.fieldName!, currColumn.isSortedDescending);
96
97
        this.setState({
            columns: newColumns,
98
99
100
            source: newItems,
            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
            sortInfo: { field: currColumn.fieldName!, isDescend: currColumn.isSortedDescending }
101
102
103
        });
    };

104
    private tooltipStr = (
105
106
107
108
        <React.Fragment>
            The experiment is running, please wait for the final metric patiently. You could also find status of trial
            job with <span>{DETAILTABS}</span> button.
        </React.Fragment>
109
    );
110

111
    private columns: IColumn[] = [
112
113
114
115
116
117
118
119
120
121
        {
            key: '_expand',
            name: '',
            onRender: (item: any): any => (
                <Icon
                    aria-hidden={true}
                    iconName='ChevronRight'
                    styles={{
                        root: {
                            transition: 'all 0.2s',
122
                            transform: `rotate(${this.props.expandRowIDs.has(item.id) ? 90 : 0}deg)`
123
124
                        }
                    }}
Lijiaoa's avatar
Lijiaoa committed
125
                    className='cursor bold positionTop'
126
127
128
129
130
131
132
133
                    onClick={this.expandTrialId.bind(this, Event, item.id)}
                />
            ),
            fieldName: 'expand',
            isResizable: false,
            minWidth: 20,
            maxWidth: 20
        },
134
135
136
137
        {
            name: 'Trial No.',
            key: 'sequenceId',
            fieldName: 'sequenceId', // required!
138
            minWidth: 60,
139
            maxWidth: 80,
140
            isResizable: true,
141
            data: 'number',
142
143
            onColumnClick: this.onColumnClick,
            onRender: (item: any): React.ReactNode => <div className='succeed-padding'>{item.sequenceId}</div>
144
145
        },
        {
146
147
148
            name: 'ID',
            key: 'id',
            fieldName: 'id',
149
150
            minWidth: 90,
            maxWidth: 100,
151
            isResizable: true,
152
153
            className: 'tableHead leftTitle',
            data: 'string',
154
            onColumnClick: this.onColumnClick,
155
156
157
158
159
160
            onRender: (item: any): React.ReactNode => (
                <Stack horizontal className='idCopy'>
                    <div className='succeed-padding'>{item.id}</div>
                    <CopyButton value={item.id} />
                </Stack>
            )
161
162
        },
        {
163
164
            name: 'Duration',
            key: 'duration',
165
            minWidth: 70,
166
            maxWidth: 120,
167
            isResizable: true,
168
169
170
            fieldName: 'duration',
            data: 'number',
            onColumnClick: this.onColumnClick,
171
            onRender: (item: any): React.ReactNode => (
172
                <div className='durationsty succeed-padding'>
173
174
175
176
177
                    <div>{convertDuration(item.duration)}</div>
                </div>
            )
        },
        {
178
179
            name: 'Status',
            key: 'status',
180
181
            minWidth: 88,
            maxWidth: 120,
182
            isResizable: true,
183
            fieldName: 'status',
184
185
186
            onRender: (item: any): React.ReactNode => (
                <div className={`${item.status} commonStyle succeed-padding`}>{item.status}</div>
            )
187
188
        },
        {
189
190
191
            name: 'Default metric',
            key: 'accuracy',
            fieldName: 'accuracy',
192
            minWidth: 100,
Lijiaoa's avatar
Lijiaoa committed
193
            maxWidth: 166,
194
            isResizable: true,
195
196
            data: 'number',
            onColumnClick: this.onColumnClick,
197
            onRender: (item: any): React.ReactNode => <DefaultMetric trialId={item.id} />
198
199
200
        }
    ];

201
    private onRenderDetailsHeader: IRenderFunction<IDetailsHeaderProps> = (props, defaultRender) => {
Lijiaoa's avatar
Lijiaoa committed
202
203
204
205
206
        if (!props) {
            return null;
        }
        return (
            <Sticky stickyPosition={StickyPositionType.Header} isScrollSynced>
207
208
209
210
211
212
                {
                    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                    defaultRender!({
                        ...props
                    })
                }
Lijiaoa's avatar
Lijiaoa committed
213
214
215
216
            </Sticky>
        );
    };

217
    private onRenderRow: IDetailsListProps['onRenderRow'] = props => {
218
        const { expandRowIDs } = this.props;
219
220
221
222
223
224
        if (props) {
            return (
                <div>
                    <div>
                        <DetailsRow {...props} />
                    </div>
225
                    {Array.from(expandRowIDs).map(
226
227
228
229
230
231
                        item => item === props.item.id && <OpenRow key={item} trialId={item} />
                    )}
                </div>
            );
        }
        return null;
232
233
    };

234
    private expandTrialId = (_event: any, id: string): void => {
235
236
        const { updateOverviewPage, changeExpandRowIDs } = this.props;
        changeExpandRowIDs(id);
237
238
        updateOverviewPage();
    };
Lijiao's avatar
Lijiao committed
239
}
240
241

export default SuccessTable;