mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
- Add feature to allow collection header form using CustomControl. - Framework stability and bug fixes.
This commit is contained in:
parent
08f2121544
commit
9bfef1f6e5
@ -78,6 +78,8 @@ const useStyles = makeStyles((theme)=>({
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
...theme.mixins.panelBorder.right,
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
},
|
||||
tableCellHeader: {
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
@ -159,6 +161,20 @@ function DataTableRow({row, totalRows, isResizing, schema, schemaRef, accessPath
|
||||
let retVal = [];
|
||||
/* Calculate the fields which depends on the current field
|
||||
deps has info on fields which the current field depends on. */
|
||||
schema.fields.forEach((field)=>{
|
||||
(evalFunc(null, field.deps) || []).forEach((dep)=>{
|
||||
let source = accessPath.concat(dep);
|
||||
if(_.isArray(dep)) {
|
||||
source = dep;
|
||||
/* If its an array, then dep is from the top schema and external */
|
||||
retVal.push(source);
|
||||
}
|
||||
});
|
||||
});
|
||||
return retVal;
|
||||
}, []);
|
||||
|
||||
useEffect(()=>{
|
||||
schema.fields.forEach((field)=>{
|
||||
/* Self change is also dep change */
|
||||
if(field.depChange) {
|
||||
@ -168,13 +184,14 @@ function DataTableRow({row, totalRows, isResizing, schema, schemaRef, accessPath
|
||||
let source = accessPath.concat(dep);
|
||||
if(_.isArray(dep)) {
|
||||
source = dep;
|
||||
/* If its an array, then dep is from the top schema */
|
||||
retVal.push(source);
|
||||
}
|
||||
depListener.addDepListener(source, accessPath.concat(field.id), field.depChange);
|
||||
});
|
||||
});
|
||||
return retVal;
|
||||
return ()=>{
|
||||
/* Cleanup the listeners when unmounting */
|
||||
depListener.removeDepListener(accessPath);
|
||||
};
|
||||
}, []);
|
||||
|
||||
/* External deps values are from top schema sess data */
|
||||
@ -201,12 +218,28 @@ function DataTableRow({row, totalRows, isResizing, schema, schemaRef, accessPath
|
||||
</div>, depsMap);
|
||||
}
|
||||
|
||||
export function DataGridHeader({label, canAdd, onAddClick}) {
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<Box className={classes.gridHeader}>
|
||||
<Box className={classes.gridHeaderText}>{label}</Box>
|
||||
<Box className={classes.gridControls}>
|
||||
{canAdd && <PgIconButton data-test="add-row" title={gettext('Add row')} onClick={onAddClick} icon={<AddIcon />} className={classes.gridControlsButton} />}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
DataGridHeader.propTypes = {
|
||||
label: PropTypes.string,
|
||||
canAdd: PropTypes.bool,
|
||||
onAddClick: PropTypes.func,
|
||||
};
|
||||
|
||||
export default function DataGridView({
|
||||
value, viewHelperProps, formErr, schema, accessPath, dataDispatch, containerClassName,
|
||||
fixedRows, ...props}) {
|
||||
const classes = useStyles();
|
||||
const stateUtils = useContext(StateUtilsContext);
|
||||
const depListener = useContext(DepListenerContext);
|
||||
|
||||
/* Using ref so that schema variable is not frozen in columns closure */
|
||||
const schemaRef = useRef(schema);
|
||||
@ -268,7 +301,6 @@ export default function DataGridView({
|
||||
value: row.index,
|
||||
});
|
||||
|
||||
depListener.removeDepListener(accessPath.concat(row.index));
|
||||
}, ()=>{}, props.customDeleteTitle, props.customDeleteMsg);
|
||||
}} className={classes.gridRowButton} disabled={!canDeleteRow} />
|
||||
);
|
||||
@ -349,22 +381,30 @@ export default function DataGridView({
|
||||
})
|
||||
);
|
||||
return cols;
|
||||
},[]
|
||||
},[props.canEdit, props.canDelete]
|
||||
);
|
||||
|
||||
const onAddClick = useCallback(()=>{
|
||||
if(props.canAddRow) {
|
||||
let state = schemaRef.current.top ? schemaRef.current.top.sessData : schemaRef.current.sessData;
|
||||
let canAddRow = evalFunc(schemaRef.current, props.canAddRow, state || {});
|
||||
if(!canAddRow) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let newRow = schemaRef.current.getNewData();
|
||||
dataDispatch({
|
||||
type: SCHEMA_STATE_ACTIONS.ADD_ROW,
|
||||
path: accessPath,
|
||||
value: newRow,
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
|
||||
const defaultColumn = useMemo(()=>({
|
||||
minWidth: 175,
|
||||
width: 0,
|
||||
}));
|
||||
}), []);
|
||||
|
||||
let tablePlugins = [
|
||||
useBlockLayout,
|
||||
@ -393,6 +433,8 @@ export default function DataGridView({
|
||||
|
||||
useEffect(()=>{
|
||||
let rowsPromise = fixedRows, umounted=false;
|
||||
|
||||
/* If fixedRows is defined, fetch the details */
|
||||
if(typeof rowsPromise === 'function') {
|
||||
rowsPromise = rowsPromise();
|
||||
}
|
||||
@ -417,12 +459,7 @@ export default function DataGridView({
|
||||
return (
|
||||
<Box className={containerClassName}>
|
||||
<Box className={classes.grid}>
|
||||
{(props.label || props.canAdd) && <Box className={classes.gridHeader}>
|
||||
<Box className={classes.gridHeaderText}>{props.label}</Box>
|
||||
<Box className={classes.gridControls}>
|
||||
{props.canAdd && <PgIconButton data-test="add-row" title={gettext('Add row')} onClick={onAddClick} icon={<AddIcon />} className={classes.gridControlsButton} />}
|
||||
</Box>
|
||||
</Box>}
|
||||
{(props.label || props.canAdd) && <DataGridHeader label={props.label} canAdd={props.canAdd} onAddClick={onAddClick} />}
|
||||
<div {...getTableProps()} className={classes.table}>
|
||||
<DataTableHeader headerGroups={headerGroups} />
|
||||
<div {...getTableBodyProps()}>
|
||||
@ -460,6 +497,9 @@ DataGridView.propTypes = {
|
||||
canAdd: PropTypes.bool,
|
||||
canDelete: PropTypes.bool,
|
||||
visible: PropTypes.bool,
|
||||
canAddRow: PropTypes.oneOfType([
|
||||
PropTypes.bool, PropTypes.func,
|
||||
]),
|
||||
canEditRow: PropTypes.oneOfType([
|
||||
PropTypes.bool, PropTypes.func,
|
||||
]),
|
||||
|
@ -25,7 +25,7 @@ export default function FieldSetView({
|
||||
|
||||
useEffect(()=>{
|
||||
/* Calculate the fields which depends on the current field */
|
||||
if(!isDataGridForm) {
|
||||
if(!isDataGridForm && depListener) {
|
||||
schema.fields.forEach((field)=>{
|
||||
/* Self change is also dep change */
|
||||
if(field.depChange || field.deferredDepChange) {
|
||||
|
@ -122,7 +122,7 @@ export function getFieldMetaData(field, schema, value, viewHelperProps) {
|
||||
/* The first component of schema view form */
|
||||
export default function FormView({
|
||||
value, formErr, schema={}, viewHelperProps, isNested=false, accessPath, dataDispatch, hasSQLTab,
|
||||
getSQLValue, onTabChange, firstEleRef, className, isDataGridForm=false, visible}) {
|
||||
getSQLValue, onTabChange, firstEleRef, className, isDataGridForm=false, isTabView=true, visible}) {
|
||||
let defaultTab = 'General';
|
||||
let tabs = {};
|
||||
let tabsClassname = {};
|
||||
@ -164,6 +164,10 @@ export default function FormView({
|
||||
}
|
||||
});
|
||||
});
|
||||
return ()=>{
|
||||
/* Cleanup the listeners when unmounting */
|
||||
depListener.removeDepListener(accessPath);
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
|
||||
@ -175,7 +179,7 @@ export default function FormView({
|
||||
getFieldMetaData(field, schema, value, viewHelperProps);
|
||||
|
||||
if(modeSupported) {
|
||||
let {group} = field;
|
||||
let {group, CustomControl} = field;
|
||||
group = groupLabels[group] || group || defaultTab;
|
||||
|
||||
if(!tabs[group]) tabs[group] = [];
|
||||
@ -190,7 +194,7 @@ export default function FormView({
|
||||
}
|
||||
tabs[group].push(
|
||||
<FormView key={`nested${tabs[group].length}`} value={value} viewHelperProps={viewHelperProps} formErr={formErr}
|
||||
schema={field.schema} accessPath={accessPath} dataDispatch={dataDispatch} isNested={true} isDataGridForm={isDataGridForm}
|
||||
schema={field.schema} accessPath={accessPath} dataDispatch={dataDispatch} isNested={true} isDataGridForm={isDataGridForm}
|
||||
{...field} visible={visible}/>
|
||||
);
|
||||
} else if(field.type === 'nested-fieldset') {
|
||||
@ -223,11 +227,18 @@ export default function FormView({
|
||||
canDelete = false;
|
||||
}
|
||||
|
||||
tabs[group].push(
|
||||
<DataGridView key={field.id} value={value[field.id]} viewHelperProps={viewHelperProps} formErr={formErr}
|
||||
schema={field.schema} accessPath={accessPath.concat(field.id)} dataDispatch={dataDispatch} containerClassName={classes.controlRow}
|
||||
{...field} canAdd={canAdd} canEdit={canEdit} canDelete={canDelete} visible={visible}/>
|
||||
);
|
||||
const props = {
|
||||
key: field.id, value: value[field.id], viewHelperProps: viewHelperProps, formErr: formErr,
|
||||
schema: field.schema, accessPath: accessPath.concat(field.id), dataDispatch: dataDispatch,
|
||||
containerClassName: classes.controlRow, ...field, canAdd: canAdd, canEdit: canEdit, canDelete: canDelete,
|
||||
visible: visible,
|
||||
};
|
||||
|
||||
if(CustomControl) {
|
||||
tabs[group].push(<CustomControl {...props}/>);
|
||||
} else {
|
||||
tabs[group].push(<DataGridView {...props}/>);
|
||||
}
|
||||
} else if(field.type === 'group') {
|
||||
groupLabels[field.id] = field.label;
|
||||
if(!visible) {
|
||||
@ -311,35 +322,48 @@ export default function FormView({
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box height="100%" display="flex" flexDirection="column" className={className} ref={formRef}>
|
||||
<Box>
|
||||
<Tabs
|
||||
value={tabValue}
|
||||
onChange={(event, selTabValue) => {
|
||||
setTabValue(selTabValue);
|
||||
}}
|
||||
// indicatorColor="primary"
|
||||
variant="scrollable"
|
||||
scrollButtons="auto"
|
||||
action={(ref)=>ref && ref.updateIndicator()}
|
||||
>
|
||||
{Object.keys(tabs).map((tabName)=>{
|
||||
return <Tab key={tabName} label={tabName} />;
|
||||
})}
|
||||
</Tabs>
|
||||
if(isTabView) {
|
||||
return (
|
||||
<>
|
||||
<Box height="100%" display="flex" flexDirection="column" className={className} ref={formRef}>
|
||||
<Box>
|
||||
<Tabs
|
||||
value={tabValue}
|
||||
onChange={(event, selTabValue) => {
|
||||
setTabValue(selTabValue);
|
||||
}}
|
||||
// indicatorColor="primary"
|
||||
variant="scrollable"
|
||||
scrollButtons="auto"
|
||||
action={(ref)=>ref && ref.updateIndicator()}
|
||||
>
|
||||
{Object.keys(tabs).map((tabName)=>{
|
||||
return <Tab key={tabName} label={tabName} />;
|
||||
})}
|
||||
</Tabs>
|
||||
</Box>
|
||||
{Object.keys(tabs).map((tabName, i)=>{
|
||||
return (
|
||||
<TabPanel key={tabName} value={tabValue} index={i} classNameRoot={clsx(tabsClassname[tabName], isNested ? classes.nestedTabPanel : null)}
|
||||
className={fullTabs.indexOf(tabName) == -1 ? classes.nestedControl : null}>
|
||||
{tabs[tabName]}
|
||||
</TabPanel>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
{Object.keys(tabs).map((tabName, i)=>{
|
||||
return (
|
||||
<TabPanel key={tabName} value={tabValue} index={i} classNameRoot={clsx(tabsClassname[tabName], isNested ? classes.nestedTabPanel : null)}
|
||||
className={fullTabs.indexOf(tabName) == -1 ? classes.nestedControl : null}>
|
||||
{tabs[tabName]}
|
||||
</TabPanel>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
</>);
|
||||
</>);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<Box height="100%" display="flex" flexDirection="column" className={className} ref={formRef}>
|
||||
{Object.keys(tabs).map((tabName)=>{
|
||||
return (
|
||||
<>{tabs[tabName]}</>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
</>);
|
||||
}
|
||||
}
|
||||
|
||||
FormView.propTypes = {
|
||||
|
@ -126,4 +126,9 @@ export default class BaseUISchema {
|
||||
});
|
||||
return newRow;
|
||||
}
|
||||
|
||||
/* Used in header schema */
|
||||
addDisabled() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ import { minMaxValidator, numberValidator, integerValidator, emptyValidator, che
|
||||
import { MappedFormControl } from './MappedControl';
|
||||
import gettext from 'sources/gettext';
|
||||
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
|
||||
import FormView from './FormView';
|
||||
import FormView, { getFieldMetaData } from './FormView';
|
||||
import { pgAlertify } from '../helpers/legacyConnector';
|
||||
import { evalFunc } from 'sources/utils';
|
||||
import PropTypes from 'prop-types';
|
||||
@ -113,9 +113,9 @@ const diffArrayOptions = {
|
||||
compareFunction: objectComparator,
|
||||
};
|
||||
|
||||
function getChangedData(topSchema, mode, sessData, stringify=false) {
|
||||
function getChangedData(topSchema, viewHelperProps, sessData, stringify=false) {
|
||||
let changedData = {};
|
||||
let isEdit = mode === 'edit';
|
||||
let isEdit = viewHelperProps.mode === 'edit';
|
||||
|
||||
/* The comparator and setter */
|
||||
const attrChanged = (currPath, change, force=false)=>{
|
||||
@ -136,6 +136,10 @@ function getChangedData(topSchema, mode, sessData, stringify=false) {
|
||||
/* Will be called recursively as data can be nested */
|
||||
const parseChanges = (schema, accessPath, changedData)=>{
|
||||
schema.fields.forEach((field)=>{
|
||||
let {modeSupported} = getFieldMetaData(field, schema, {}, viewHelperProps);
|
||||
if(!modeSupported) {
|
||||
return;
|
||||
}
|
||||
if(typeof(field.type) == 'string' && field.type.startsWith('nested-')) {
|
||||
/* its nested */
|
||||
parseChanges(field.schema, accessPath, changedData);
|
||||
@ -324,7 +328,6 @@ const sessDataReducer = (state, action)=>{
|
||||
/* If there is any dep listeners get the changes */
|
||||
data = getDepChange(action.path, data, state, action);
|
||||
deferredList = getDeferredDepChange(action.path, data, state, action);
|
||||
// let deferredInfo = getDeferredDepChange(action.path, data, state, action);
|
||||
data.__deferred__ = deferredList || [];
|
||||
break;
|
||||
case SCHEMA_STATE_ACTIONS.ADD_ROW:
|
||||
@ -387,7 +390,7 @@ function prepareData(val) {
|
||||
|
||||
/* If its the dialog */
|
||||
function SchemaDialogView({
|
||||
getInitData, viewHelperProps, schema={}, ...props}) {
|
||||
getInitData, viewHelperProps, schema={}, showFooter=true, isTabView=true, ...props}) {
|
||||
const classes = useDialogStyles();
|
||||
/* Some useful states */
|
||||
const [dirty, setDirty] = useState(false);
|
||||
@ -421,11 +424,12 @@ function SchemaDialogView({
|
||||
if(!isNotValid) setFormErr({});
|
||||
|
||||
/* check if anything changed */
|
||||
let dataChanged = Object.keys(getChangedData(schema, viewHelperProps.mode, sessData)).length > 0;
|
||||
setDirty(dataChanged);
|
||||
let changedData = getChangedData(schema, viewHelperProps, sessData);
|
||||
let isDataChanged = Object.keys(changedData).length > 0;
|
||||
setDirty(isDataChanged);
|
||||
|
||||
/* tell the callbacks the data has changed */
|
||||
props.onDataChange && props.onDataChange(dataChanged);
|
||||
props.onDataChange && props.onDataChange(isDataChanged, changedData);
|
||||
}, [sessData]);
|
||||
|
||||
useEffect(()=>{
|
||||
@ -518,7 +522,7 @@ function SchemaDialogView({
|
||||
setSaving(true);
|
||||
setLoaderText('Saving...');
|
||||
/* Get the changed data */
|
||||
let changeData = getChangedData(schema, viewHelperProps.mode, sessData);
|
||||
let changeData = getChangedData(schema, viewHelperProps, sessData);
|
||||
|
||||
/* Add the id when in edit mode */
|
||||
if(viewHelperProps.mode !== 'edit') {
|
||||
@ -577,7 +581,7 @@ function SchemaDialogView({
|
||||
/* Called when SQL tab is active */
|
||||
if(dirty) {
|
||||
if(!formErr.name) {
|
||||
let changeData = getChangedData(schema, viewHelperProps.mode, sessData);
|
||||
let changeData = getChangedData(schema, viewHelperProps, sessData);
|
||||
if(viewHelperProps.mode !== 'edit') {
|
||||
/* If new then merge the changed data with origData */
|
||||
changeData = _.assign({}, schema.origData, changeData);
|
||||
@ -626,11 +630,11 @@ function SchemaDialogView({
|
||||
<Loader message={loaderText}/>
|
||||
<FormView value={sessData} viewHelperProps={viewHelperProps} formErr={formErr}
|
||||
schema={schema} accessPath={[]} dataDispatch={sessDispatchWithListener}
|
||||
hasSQLTab={props.hasSQL} getSQLValue={getSQLValue} firstEleRef={firstEleRef} />
|
||||
hasSQLTab={props.hasSQL} getSQLValue={getSQLValue} firstEleRef={firstEleRef} isTabView={isTabView} />
|
||||
<FormFooterMessage type={MESSAGE_TYPE.ERROR} message={formErr.message}
|
||||
onClose={onErrClose} />
|
||||
</Box>
|
||||
<Box className={classes.footer}>
|
||||
{showFooter && <Box className={classes.footer}>
|
||||
{useMemo(()=><Box>
|
||||
<PgIconButton data-test="sql-help" onClick={()=>props.onHelp(true, isNew)} icon={<InfoIcon />}
|
||||
disabled={props.disableSqlHelp} className={classes.buttonMargin} title="SQL help for this object type."/>
|
||||
@ -648,7 +652,7 @@ function SchemaDialogView({
|
||||
{gettext('Save')}
|
||||
</PrimaryButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>}
|
||||
</Box>
|
||||
</DepListenerContext.Provider>
|
||||
</StateUtilsContext.Provider>
|
||||
@ -671,10 +675,12 @@ SchemaDialogView.propTypes = {
|
||||
onHelp: PropTypes.func,
|
||||
onDataChange: PropTypes.func,
|
||||
confirmOnCloseReset: PropTypes.bool,
|
||||
isTabView: PropTypes.bool,
|
||||
hasSQL: PropTypes.bool,
|
||||
getSQLValue: PropTypes.func,
|
||||
disableSqlHelp: PropTypes.bool,
|
||||
disableDialogHelp: PropTypes.bool,
|
||||
showFooter: PropTypes.bool,
|
||||
};
|
||||
|
||||
const usePropsStyles = makeStyles((theme)=>({
|
||||
|
92
web/pgadmin/static/js/helpers/DataGridViewWithHeaderForm.jsx
Normal file
92
web/pgadmin/static/js/helpers/DataGridViewWithHeaderForm.jsx
Normal file
@ -0,0 +1,92 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Box, makeStyles } from '@material-ui/core';
|
||||
import DataGridView, { DataGridHeader } from '../SchemaView/DataGridView';
|
||||
import SchemaView, { SCHEMA_STATE_ACTIONS } from '../SchemaView';
|
||||
import { DefaultButton } from '../components/Buttons';
|
||||
import { evalFunc } from '../utils';
|
||||
import PropTypes from 'prop-types';
|
||||
import CustomPropTypes from '../custom_prop_types';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
formBorder: {
|
||||
...theme.mixins.panelBorder,
|
||||
borderBottom: 0,
|
||||
},
|
||||
form: {
|
||||
padding: '0.25rem',
|
||||
},
|
||||
addBtn: {
|
||||
marginLeft: 'auto',
|
||||
}
|
||||
}));
|
||||
|
||||
export default function DataGridViewWithHeaderForm(props) {
|
||||
let {containerClassName, headerSchema, headerVisible, ...otherProps} = props;
|
||||
const classes = useStyles();
|
||||
const headerFormData = useRef({});
|
||||
const schemaRef = useRef(otherProps.schema);
|
||||
const [isAddDisabled, setAddDisabled] = useState(true);
|
||||
|
||||
const onAddClick = useCallback(()=>{
|
||||
if(otherProps.canAddRow) {
|
||||
let state = schemaRef.current.top ? schemaRef.current.top.sessData : schemaRef.current.sessData;
|
||||
let canAddRow = evalFunc(schemaRef.current, otherProps.canAddRow, state || {});
|
||||
if(!canAddRow) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let newRow = headerSchema.getNewData(headerFormData.current);
|
||||
otherProps.dataDispatch({
|
||||
type: SCHEMA_STATE_ACTIONS.ADD_ROW,
|
||||
path: otherProps.accessPath,
|
||||
value: newRow,
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(()=>{
|
||||
headerSchema.top = schemaRef.current.top;
|
||||
}, []);
|
||||
|
||||
let state = schemaRef.current.top ? schemaRef.current.top.origData : schemaRef.current.origData;
|
||||
headerVisible = headerVisible && evalFunc(null, headerVisible, state);
|
||||
return (
|
||||
<Box className={containerClassName}>
|
||||
<Box className={classes.formBorder}>
|
||||
<DataGridHeader label={props.label} />
|
||||
{headerVisible && <Box className={classes.form}>
|
||||
<SchemaView
|
||||
formType={'dialog'}
|
||||
getInitData={()=>Promise.resolve({})}
|
||||
schema={headerSchema}
|
||||
viewHelperProps={props.viewHelperProps}
|
||||
showFooter={false}
|
||||
onDataChange={(isDataChanged, dataChanged)=>{
|
||||
headerFormData.current = dataChanged;
|
||||
setAddDisabled(headerSchema.addDisabled(headerFormData.current));
|
||||
}}
|
||||
hasSQL={false}
|
||||
isTabView={false}
|
||||
/>
|
||||
<Box display="flex">
|
||||
<DefaultButton className={classes.addBtn} onClick={onAddClick} disabled={isAddDisabled}>Add</DefaultButton>
|
||||
</Box>
|
||||
</Box>}
|
||||
</Box>
|
||||
<DataGridView {...otherProps} canAdd={false}/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
DataGridViewWithHeaderForm.propTypes = {
|
||||
label: PropTypes.string,
|
||||
value: PropTypes.array,
|
||||
viewHelperProps: PropTypes.object,
|
||||
formErr: PropTypes.object,
|
||||
headerSchema: CustomPropTypes.schemaUI.isRequired,
|
||||
headerVisible: PropTypes.func,
|
||||
schema: CustomPropTypes.schemaUI,
|
||||
accessPath: PropTypes.array.isRequired,
|
||||
dataDispatch: PropTypes.func.isRequired,
|
||||
containerClassName: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
|
||||
};
|
Loading…
Reference in New Issue
Block a user