Fixed issues found in testing of react-table upgrade changes. #7419

UI fixes and improvement in System Stats Dashboard.
This commit is contained in:
Aditya Toshniwal 2024-05-20 10:41:55 +05:30
parent 9647482791
commit d6a9f8a06c
7 changed files with 137 additions and 158 deletions

View File

@ -21,6 +21,7 @@ import {useInterval, usePrevious} from 'sources/custom_hooks';
import axios from 'axios';
import { getStatsUrl, transformData, statsReducer, X_AXIS_LENGTH } from './utility.js';
import { toPrettySize } from '../../../../static/js/utils';
import SectionContainer from '../components/SectionContainer.jsx';
const useStyles = makeStyles((theme) => ({
autoResizer: {
@ -252,7 +253,7 @@ export function CPUWrapper(props) {
lineBorderWidth: props.lineBorderWidth,
}), [props.showTooltip, props.showDataPoints, props.lineBorderWidth]);
return (
<>
<Box display="flex" flexDirection="column" height="100%">
<Grid container spacing={0.5} className={classes.container}>
<Grid item md={6}>
<ChartContainer id='cu-graph' title={gettext('CPU usage')} datasets={props.cpuUsageInfo.datasets} errorMsg={props.errorMsg} isTest={props.isTest}>
@ -265,22 +266,20 @@ export function CPUWrapper(props) {
</ChartContainer>
</Grid>
</Grid>
<Grid container spacing={0.5} className={classes.fixedContainer}>
<div className={classes.tableContainer}>
<Box flexGrow={1} minHeight={0}>
<SectionContainer title={gettext('Process CPU usage')}>
<PgTable
className={classes.autoResizer}
CustomHeader={() => {
return <div className={classes.containerHeader}>{gettext('Process CPU usage')}</div>;
}}
columns={props.tableHeader}
data={props.processCpuUsageStats}
msg={props.errorMsg}
type={'panel'}
caveTable={false}
tableNoBorder={false}
></PgTable>
</div>
</Grid>
</>
</SectionContainer>
</Box>
</Box>
);
}

View File

@ -20,6 +20,7 @@ import {useInterval, usePrevious} from 'sources/custom_hooks';
import axios from 'axios';
import { getStatsUrl, transformData, statsReducer, X_AXIS_LENGTH } from './utility.js';
import { toPrettySize } from '../../../../static/js/utils';
import SectionContainer from '../components/SectionContainer.jsx';
const useStyles = makeStyles((theme) => ({
autoResizer: {
@ -255,7 +256,7 @@ export function MemoryWrapper(props) {
}), [props.showTooltip, props.showDataPoints, props.lineBorderWidth]);
return (
<>
<Box display="flex" flexDirection="column" height="100%">
<Grid container spacing={0.5} className={classes.container}>
<Grid item md={6}>
<ChartContainer id='m-graph' title={gettext('Memory')} datasets={props.memoryUsageInfo.datasets} errorMsg={props.errorMsg} isTest={props.isTest}>
@ -270,22 +271,20 @@ export function MemoryWrapper(props) {
</ChartContainer>
</Grid>
</Grid>
<Grid container spacing={0.5} className={classes.fixedContainer}>
<div className={classes.tableContainer}>
<Box flexGrow={1} minHeight={0}>
<SectionContainer title={gettext('Process memory usage')}>
<PgTable
className={classes.autoResizer}
CustomHeader={() => {
return <div className={classes.containerHeader}>{gettext('Process memory usage')}</div>;
}}
columns={props.tableHeader}
data={props.processMemoryUsageStats}
msg={props.errorMsg}
type={'panel'}
caveTable={false}
tableNoBorder={false}
></PgTable>
</div>
</Grid>
</>
</SectionContainer>
</Box>
</Box>
);
}

View File

@ -23,6 +23,7 @@ import { getStatsUrl, transformData, X_AXIS_LENGTH } from './utility.js';
import { toPrettySize } from '../../../../static/js/utils';
import clsx from 'clsx';
import { commonTableStyles } from '../../../../static/js/Theme';
import SectionContainer from '../components/SectionContainer.jsx';
const useStyles = makeStyles((theme) => ({
container: {
@ -33,20 +34,8 @@ const useStyles = makeStyles((theme) => ({
driveContainer: {
width: '100%',
},
diskInfoContainer: {
height: 'auto',
padding: '8px 8px 0px 8px',
marginBottom: '0px',
},
diskInfoSummary: {
height: 'auto',
padding: '0px 0px 4px 0px',
marginBottom: '0px',
},
diskInfoCharts: {
height: 'auto',
padding: '0px 0px 2px 0px',
marginBottom: '0px',
marginBottom: '4px',
},
containerHeaderText: {
fontWeight: 'bold',
@ -54,13 +43,12 @@ const useStyles = makeStyles((theme) => ({
},
tableContainer: {
background: theme.otherVars.tableBg,
padding: '0px',
border: '1px solid '+theme.otherVars.borderColor,
borderCollapse: 'collapse',
borderRadius: '4px',
overflow: 'auto',
width: '100%',
margin: '4px 4px 4px 4px',
marginBottom: '4px',
},
tableWhiteSpace: {
'& td, & th': {
@ -143,7 +131,7 @@ const DiskStatsTable = (props) => {
<thead>
<tr>
{tableHeader.map((item, index) => (
<th key={index}>{item.Header}</th>
<th key={index}>{item.header}</th>
))}
</tr>
</thead>
@ -151,7 +139,7 @@ const DiskStatsTable = (props) => {
{data.map((item, index) => (
<tr key={index}>
{tableHeader.map((header, id) => (
<td key={header.accessor+'-'+id}>{item[header.accessor]}</td>
<td key={header.accessorKey+'-'+id}>{item[header.accessorKey]}</td>
))}
</tr>
))}
@ -428,112 +416,101 @@ export function StorageWrapper(props) {
return (
<>
<Grid container spacing={1} className={classes.diskInfoContainer}>
<Grid container spacing={1} className={classes.diskInfoSummary}>
<div className={classes.tableContainer}>
<div className={classes.containerHeaderText}>{gettext('Disk information')}</div>
<DiskStatsTable tableHeader={props.tableHeader} data={props.diskStats} />
</div>
</Grid>
<Grid container spacing={1} className={classes.diskInfoCharts}>
<Grid item md={6} sm={12}>
<ChartContainer
id='t-space-graph'
title={''}
datasets={props.diskStats.map((item, index) => ({
borderColor: colors[(index + 2) % colors.length],
label: item.mount_point !== '' ? item.mount_point : item.drive_letter !== '' ? item.drive_letter : 'disk' + index,
}))}
errorMsg={props.errorMsg}
isTest={props.isTest}>
<PieChart data={{
labels: props.diskStats.map((item, index) => item.mount_point!=''?item.mount_point:item.drive_letter!=''?item.drive_letter:'disk'+index),
datasets: [
{
data: props.diskStats.map((item) => item.total_space_actual?item.total_space_actual:0),
backgroundColor: props.diskStats.map((item, index) => colors[(index + 2) % colors.length]),
},
],
}}
options={{
animation: false,
...chartJsExtraOptions,
}}
/>
</ChartContainer>
</Grid>
<Grid item md={6} sm={12}>
<ChartContainer id='ua-space-graph' title={''} datasets={[{borderColor: '#FF6384', label: 'Used space'}, {borderColor: '#36a2eb', label: 'Available space'}]} errorMsg={props.errorMsg} isTest={props.isTest}>
<BarChart data={{
labels: props.diskStats.map((item, index) => item.mount_point!=''?item.mount_point:item.drive_letter!=''?item.drive_letter:'disk'+index),
datasets: [
{
label: 'Used space',
data: props.diskStats.map((item) => item.used_space_actual?item.used_space_actual:0),
backgroundColor: '#FF6384',
borderColor: '#FF6384',
borderWidth: 1,
},
{
label: 'Available space',
data: props.diskStats.map((item) => item.free_space_actual?item.free_space_actual:0),
backgroundColor: '#36a2eb',
borderColor: '#36a2eb',
borderWidth: 1,
},
],
}}
options={
<div className={classes.tableContainer}>
<div className={classes.containerHeaderText}>{gettext('Disk information')}</div>
<DiskStatsTable tableHeader={props.tableHeader} data={props.diskStats} />
</div>
<Grid container spacing={0.5} sx={{marginBottom: '4px'}}>
<Grid item md={6} sm={12}>
<ChartContainer
id='t-space-graph'
title={''}
datasets={props.diskStats.map((item, index) => ({
borderColor: colors[(index + 2) % colors.length],
label: item.mount_point !== '' ? item.mount_point : item.drive_letter !== '' ? item.drive_letter : 'disk' + index,
}))}
errorMsg={props.errorMsg}
isTest={props.isTest}>
<PieChart data={{
labels: props.diskStats.map((item, index) => item.mount_point!=''?item.mount_point:item.drive_letter!=''?item.drive_letter:'disk'+index),
datasets: [
{
scales: {
x: {
data: props.diskStats.map((item) => item.total_space_actual?item.total_space_actual:0),
backgroundColor: props.diskStats.map((item, index) => colors[(index + 2) % colors.length]),
},
],
}}
options={{
animation: false,
...chartJsExtraOptions,
}}
/>
</ChartContainer>
</Grid>
<Grid item md={6} sm={12}>
<ChartContainer id='ua-space-graph' title={''} datasets={[{borderColor: '#FF6384', label: 'Used space'}, {borderColor: '#36a2eb', label: 'Available space'}]} errorMsg={props.errorMsg} isTest={props.isTest}>
<BarChart data={{
labels: props.diskStats.map((item, index) => item.mount_point!=''?item.mount_point:item.drive_letter!=''?item.drive_letter:'disk'+index),
datasets: [
{
label: 'Used space',
data: props.diskStats.map((item) => item.used_space_actual?item.used_space_actual:0),
backgroundColor: '#FF6384',
borderColor: '#FF6384',
borderWidth: 1,
},
{
label: 'Available space',
data: props.diskStats.map((item) => item.free_space_actual?item.free_space_actual:0),
backgroundColor: '#36a2eb',
borderColor: '#36a2eb',
borderWidth: 1,
},
],
}}
options={
{
scales: {
x: {
display: true,
stacked: true,
ticks: {
display: true,
stacked: true,
ticks: {
display: true,
},
},
y: {
beginAtZero: true,
stacked: true,
ticks: {
callback: function (value) {
return toPrettySize(value);
},
},
y: {
beginAtZero: true,
stacked: true,
ticks: {
callback: function (value) {
return toPrettySize(value);
},
},
},
...chartJsExtraOptions,
}
},
...chartJsExtraOptions,
}
/>
</ChartContainer>
</Grid>
}
/>
</ChartContainer>
</Grid>
</Grid>
<Grid container spacing={0.5} className={classes.container}>
{Object.keys(props.ioInfo).map((drive, index) => (
<Grid key={`disk-${index}`} container spacing={1} className={classes.container}>
<div className={classes.driveContainer}>
<Grid container spacing={1} className={classes.driveContainerHeader}>
<div className={classes.containerHeaderText}>{gettext(drive)}</div>
{Object.keys(props.ioInfo).map((drive) => (
<SectionContainer key={drive} title={drive} style={{minHeight: 'unset', height: 'auto', marginBottom: '0.5px'}}>
<Grid container spacing={0.5} p={0.5}>
{Object.keys(props.ioInfo[drive]).map((type, innerKeyIndex) => (
<Grid key={`${type}-${innerKeyIndex}`} item md={4} sm={6}>
<ChartContainer id={`io-graph-${type}`} title={type.endsWith('_bytes_rw') ? gettext('Data transfer'): type.endsWith('_total_rw') ? gettext('I/O operations count'): type.endsWith('_time_rw') ? gettext('Time spent in I/O operations'):''} datasets={transformData(props.ioInfo[drive][type], props.ioRefreshRate).datasets} errorMsg={props.errorMsg} isTest={props.isTest}>
<StreamingChart data={transformData(props.ioInfo[drive][type], props.ioRefreshRate)} dataPointSize={DATA_POINT_SIZE} xRange={X_AXIS_LENGTH} options={options}
valueFormatter={(v)=>{
return type.endsWith('_time_rw') ? toPrettySize(v, 'ms') : type.endsWith('_total_rw') ? toPrettySize(v, ''): toPrettySize(v);
}} />
</ChartContainer>
</Grid>
<Grid container spacing={0.5} className={classes.driveContainerBody}>
{Object.keys(props.ioInfo[drive]).map((type, innerKeyIndex) => (
<Grid key={`${type}-${innerKeyIndex}`} item md={4} sm={6}>
<ChartContainer id={`io-graph-${type}`} title={type.endsWith('_bytes_rw') ? gettext('Data transfer'): type.endsWith('_total_rw') ? gettext('I/O operations count'): type.endsWith('_time_rw') ? gettext('Time spent in I/O operations'):''} datasets={transformData(props.ioInfo[drive][type], props.ioRefreshRate).datasets} errorMsg={props.errorMsg} isTest={props.isTest}>
<StreamingChart data={transformData(props.ioInfo[drive][type], props.ioRefreshRate)} dataPointSize={DATA_POINT_SIZE} xRange={X_AXIS_LENGTH} options={options}
valueFormatter={(v)=>{
return type.endsWith('_time_rw') ? toPrettySize(v, 'ms') : type.endsWith('_total_rw') ? toPrettySize(v, ''): toPrettySize(v);
}} />
</ChartContainer>
</Grid>
))}
</Grid>
</div>
))}
</Grid>
))}
</Grid>
</SectionContainer>
))}
</>
);
}

View File

@ -317,7 +317,7 @@ DataGridHeader.propTypes = {
onSearchTextChange: PropTypes.func,
};
function DataGridView({
export default function DataGridView({
value, viewHelperProps, schema, accessPath, dataDispatch, containerClassName,
fixedRows, ...props}) {
const classes = useStyles();
@ -639,14 +639,6 @@ function DataGridView({
);
}
export default function DataGridViewMoized({memoDeps, ...props}) {
return useMemo(()=><DataGridView {...props} />, memoDeps??[]);
}
DataGridViewMoized.propTypes = {
memoDeps: PropTypes.array,
};
DataGridView.propTypes = {
label: PropTypes.string,
value: PropTypes.array,

View File

@ -305,12 +305,7 @@ export default function FormView({
if(CustomControl) {
tabs[group].push(<CustomControl {...ctrlProps}/>);
} else {
tabs[group].push(<DataGridView {...ctrlProps} memoDeps={[
JSON.stringify(ctrlProps.value),
ctrlProps.containerClassName,
ctrlProps.visible,
...(evalFunc(null, ctrlProps.deps) || []).map((dep)=>value[dep]),
]} />);
tabs[group].push(<DataGridView {...ctrlProps} />);
}
} else {
/* Its a form control */

View File

@ -7,7 +7,7 @@
//
//////////////////////////////////////////////////////////////
import React, { forwardRef } from 'react';
import React, { forwardRef, useEffect } from 'react';
import { flexRender } from '@tanstack/react-table';
import { styled } from '@mui/styles';
import PropTypes from 'prop-types';
@ -58,6 +58,12 @@ const StyledDiv = styled('div')(({theme})=>({
...theme.mixins.panelBorder.bottom,
...theme.mixins.panelBorder.right,
'& > div': {
overflow: 'hidden',
textOverflow: 'ellipsis',
textWrap: 'nowrap'
},
'& .pgrt-header-resizer': {
display: 'inline-block',
width: '5px',
@ -160,8 +166,8 @@ export const PgReactTableCell = forwardRef(({row, cell, children, className}, re
return (
<div ref={ref} key={cell.id} style={{
flex: `var(--col-${cell.column.id}-size) 0 auto`,
width: `calc(var(--col-${cell.column.id}-size)*1px)`,
flex: `var(--col-${cell.column.id.replace(/\W/g, '_')}-size) 0 auto`,
width: `calc(var(--col-${cell.column.id.replace(/\W/g, '_')}-size)*1px)`,
...(cell.column.columnDef.maxSize ? { maxWidth: `${cell.column.columnDef.maxSize}px` } : {})
}} role='cell'
className={clsx(...classNames)}
@ -231,14 +237,14 @@ export function PgReactTableHeader({table}) {
key={header.id}
className='pgrt-header-cell'
style={{
flex: `var(--header-${header?.id}-size) 0 auto`,
width: `calc(var(--header-${header?.id}-size)*1px)`,
flex: `var(--header-${header?.id.replace(/\W/g, '_')}-size) 0 auto`,
width: `calc(var(--header-${header?.id.replace(/\W/g, '_')}-size)*1px)`,
...(header.column.columnDef.maxSize ? { maxWidth: `${header.column.columnDef.maxSize}px` } : {}),
cursor: header.column.getCanSort() ? 'pointer' : 'initial',
}}
onClick={header.column.getCanSort() ? header.column.getToggleSortingHandler() : undefined}
>
<div>
<div title={flexRender(header.column.columnDef.header, header.getContext())}>
{flexRender(header.column.columnDef.header, header.getContext())}
{header.column.getCanSort() && header.column.getIsSorted() &&
<span>
@ -280,22 +286,32 @@ PgReactTableBody.propTypes = {
export const PgReactTable = forwardRef(({children, table, rootClassName, tableClassName, ...props}, ref)=>{
const columns = table.getAllColumns();
// Render the UI for your table
const maxExpandWidth = (ref.current?.getBoundingClientRect().width ?? 430) - 30; //margin,scrollbar,etc.
useEffect(()=>{
const setMaxExpandWidth = ()=>{
if(ref.current) {
ref.current.style['--expand-width'] = (ref.current.getBoundingClientRect().width ?? 430) - 30; //margin,scrollbar,etc.
}
};
const tableResizeObserver = new ResizeObserver(()=>{
setMaxExpandWidth();
});
tableResizeObserver.observe(ref.current);
}, []);
const columnSizeVars = React.useMemo(() => {
const headers = table.getFlatHeaders();
const colSizes = {};
for (let i = 0; i < headers.length; i++) {
const header = headers[i];
colSizes[`--header-${header.id}-size`] = header.getSize();
colSizes[`--col-${header.column.id}-size`] = header.column.getSize();
colSizes[`--header-${header.id.replace(/\W/g, '_')}-size`] = header.getSize();
colSizes[`--col-${header.column.id.replace(/\W/g, '_')}-size`] = header.column.getSize();
}
return colSizes;
}, [columns, table.getState().columnSizingInfo]);
return (
<StyledDiv className={clsx('pgrt', rootClassName)} style={{'--expand-width': maxExpandWidth }} ref={ref} >
<StyledDiv className={clsx('pgrt', rootClassName)} ref={ref} >
<div className={clsx('pgrt-table', tableClassName)} style={{ ...columnSizeVars }} {...props}>
{children}
</div>

View File

@ -22,13 +22,14 @@ global.matchMedia = (query)=>({
dispatchEvent: jest.fn(),
});
class IntersectionObserver {
class GeneralObserver {
observe() {return null;}
unobserve() {return null;}
disconnect() {return null;}
}
global.IntersectionObserver = IntersectionObserver;
global.IntersectionObserver = GeneralObserver;
global.ResizeObserver = GeneralObserver;
import lodash from 'lodash';
global._ = lodash;