mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2024-11-22 00:37:36 -06:00
Fixed issues found in testing of react-table upgrade changes. #7419
UI fixes and improvement in System Stats Dashboard.
This commit is contained in:
parent
9647482791
commit
d6a9f8a06c
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 */
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user