Code refactoring at multiple places to improve re-usability
parent
00d3aaa1fd
commit
296befc881
|
@ -11,9 +11,8 @@ import gettext from 'sources/gettext';
|
|||
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
|
||||
import { isEmptyString } from 'sources/validators';
|
||||
import moment from 'moment';
|
||||
import { WEEKDAYS, MONTHDAYS, MONTHS, HOURS, MINUTES } from '../../../../../../static/js/constants';
|
||||
|
||||
const PGAGENT_MONTHDAYS = [...MONTHDAYS].concat([{label: gettext('Last day'), value: 'Last Day'}]);
|
||||
import { WEEKDAYS, MONTHS, HOURS, MINUTES, PGAGENT_MONTHDAYS } from '../../../../../../static/js/constants';
|
||||
import { DaysSchema, TimesSchema } from './repeat.ui';
|
||||
|
||||
export class ExceptionsSchema extends BaseUISchema {
|
||||
constructor(fieldOptions={}, initValues={}) {
|
||||
|
@ -63,119 +62,6 @@ export class ExceptionsSchema extends BaseUISchema {
|
|||
}
|
||||
}
|
||||
|
||||
const BooleanArrayFormatter = {
|
||||
fromRaw: (originalValue, options) => {
|
||||
if (!_.isNull(originalValue) && !_.isUndefined(originalValue) && Array.isArray(originalValue)) {
|
||||
let retValue = [],
|
||||
index = 0;
|
||||
originalValue.forEach( function (value) {
|
||||
if (value) {
|
||||
retValue.push(options[index]);
|
||||
}
|
||||
index = index + 1;
|
||||
});
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
return originalValue;
|
||||
},
|
||||
|
||||
toRaw: (selectedVal, options)=> {
|
||||
if (!_.isNull(options) && !_.isUndefined(options) && Array.isArray(options)) {
|
||||
let retValue = [];
|
||||
options.forEach( function (option) {
|
||||
let elementFound = _.find(selectedVal, (selVal)=>_.isEqual(selVal.label, option.label));
|
||||
if(_.isUndefined(elementFound)) {
|
||||
retValue.push(false);
|
||||
} else {
|
||||
retValue.push(true);
|
||||
}
|
||||
});
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
return selectedVal;
|
||||
}
|
||||
};
|
||||
|
||||
export class DaysSchema extends BaseUISchema {
|
||||
constructor(fieldOptions={}, initValues={}) {
|
||||
super({
|
||||
...initValues,
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'jscweekdays', label: gettext('Week Days'), type: 'select',
|
||||
group: gettext('Days'),
|
||||
controlProps: { allowClear: true, multiple: true, allowSelectAll: true,
|
||||
placeholder: gettext('Select the weekdays...'),
|
||||
formatter: BooleanArrayFormatter,
|
||||
},
|
||||
options: WEEKDAYS,
|
||||
}, {
|
||||
id: 'jscmonthdays', label: gettext('Month Days'), type: 'select',
|
||||
group: gettext('Days'),
|
||||
controlProps: { allowClear: true, multiple: true, allowSelectAll: true,
|
||||
placeholder: gettext('Select the month days...'),
|
||||
formatter: BooleanArrayFormatter,
|
||||
},
|
||||
options: PGAGENT_MONTHDAYS,
|
||||
}, {
|
||||
id: 'jscmonths', label: gettext('Months'), type: 'select',
|
||||
group: gettext('Days'),
|
||||
controlProps: { allowClear: true, multiple: true, allowSelectAll: true,
|
||||
placeholder: gettext('Select the months...'),
|
||||
formatter: BooleanArrayFormatter,
|
||||
},
|
||||
options: MONTHS,
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export class TimesSchema extends BaseUISchema {
|
||||
constructor(fieldOptions={}, initValues={}) {
|
||||
super({
|
||||
...initValues,
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'jschours', label: gettext('Hours'), type: 'select',
|
||||
group: gettext('Times'),
|
||||
controlProps: { allowClear: true, multiple: true, allowSelectAll: true,
|
||||
placeholder: gettext('Select the hours...'),
|
||||
formatter: BooleanArrayFormatter,
|
||||
},
|
||||
options: HOURS,
|
||||
}, {
|
||||
id: 'jscminutes', label: gettext('Minutes'), type: 'select',
|
||||
group: gettext('Times'),
|
||||
controlProps: { allowClear: true, multiple: true, allowSelectAll: true,
|
||||
placeholder: gettext('Select the minutes...'),
|
||||
formatter: BooleanArrayFormatter,
|
||||
},
|
||||
options: MINUTES,
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export default class PgaJobScheduleSchema extends BaseUISchema {
|
||||
constructor(fieldOptions={}, initValues={}) {
|
||||
super({
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
import gettext from 'sources/gettext';
|
||||
import { BaseUISchema } from '../../../../../../../static/js/SchemaView';
|
||||
import { WEEKDAYS, PGAGENT_MONTHDAYS, MONTHS, HOURS, MINUTES } from '../../../../../../static/js/constants';
|
||||
|
||||
|
||||
const BooleanArrayFormatter = {
|
||||
fromRaw: (originalValue, options) => {
|
||||
if (!_.isNull(originalValue) && !_.isUndefined(originalValue) && Array.isArray(originalValue)) {
|
||||
let retValue = [],
|
||||
index = 0;
|
||||
originalValue.forEach( function (value) {
|
||||
if (value) {
|
||||
retValue.push(options[index]);
|
||||
}
|
||||
index = index + 1;
|
||||
});
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
return originalValue;
|
||||
},
|
||||
|
||||
toRaw: (selectedVal, options)=> {
|
||||
if (!_.isNull(options) && !_.isUndefined(options) && Array.isArray(options)) {
|
||||
let retValue = [];
|
||||
options.forEach( function (option) {
|
||||
let elementFound = _.find(selectedVal, (selVal)=>_.isEqual(selVal.label, option.label));
|
||||
if(_.isUndefined(elementFound)) {
|
||||
retValue.push(false);
|
||||
} else {
|
||||
retValue.push(true);
|
||||
}
|
||||
});
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
return selectedVal;
|
||||
}
|
||||
};
|
||||
|
||||
export class TimesSchema extends BaseUISchema {
|
||||
constructor(initValues={}, schemaConfig={
|
||||
hours: {},
|
||||
minutes: {}
|
||||
}) {
|
||||
super({
|
||||
...initValues,
|
||||
});
|
||||
this.schemaConfig = schemaConfig;
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'jschours', label: gettext('Hours'), type: 'select',
|
||||
group: gettext('Times'),
|
||||
controlProps: { allowClear: true, multiple: true, allowSelectAll: true,
|
||||
placeholder: gettext('Select the hours...'),
|
||||
formatter: BooleanArrayFormatter,
|
||||
},
|
||||
options: HOURS, ...(this.schemaConfig.hours??{})
|
||||
}, {
|
||||
id: 'jscminutes', label: gettext('Minutes'), type: 'select',
|
||||
group: gettext('Times'),
|
||||
controlProps: { allowClear: true, multiple: true, allowSelectAll: true,
|
||||
placeholder: gettext('Select the minutes...'),
|
||||
formatter: BooleanArrayFormatter,
|
||||
},
|
||||
options: MINUTES, ...(this.schemaConfig.hours??{})
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export class DaysSchema extends BaseUISchema {
|
||||
constructor(initValues={}, schemaConfig={
|
||||
weekdays: {}, monthdays: {}, months: {}
|
||||
}) {
|
||||
super({
|
||||
...initValues,
|
||||
});
|
||||
|
||||
this.schemaConfig = schemaConfig;
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'jscweekdays', label: gettext('Week Days'), type: 'select',
|
||||
group: gettext('Days'),
|
||||
controlProps: { allowClear: true, multiple: true, allowSelectAll: true,
|
||||
placeholder: gettext('Select the weekdays...'),
|
||||
formatter: BooleanArrayFormatter,
|
||||
},
|
||||
options: WEEKDAYS, ...(this.schemaConfig.weekdays??{})
|
||||
}, {
|
||||
id: 'jscmonthdays', label: gettext('Month Days'), type: 'select',
|
||||
group: gettext('Days'),
|
||||
controlProps: { allowClear: true, multiple: true, allowSelectAll: true,
|
||||
placeholder: gettext('Select the month days...'),
|
||||
formatter: BooleanArrayFormatter,
|
||||
},
|
||||
options: PGAGENT_MONTHDAYS, ...(this.schemaConfig.monthdays??{})
|
||||
}, {
|
||||
id: 'jscmonths', label: gettext('Months'), type: 'select',
|
||||
group: gettext('Days'),
|
||||
controlProps: { allowClear: true, multiple: true, allowSelectAll: true,
|
||||
placeholder: gettext('Select the months...'),
|
||||
formatter: BooleanArrayFormatter,
|
||||
},
|
||||
options: MONTHS, ...(this.schemaConfig.months??{})
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
|
@ -104,3 +104,5 @@ export const WEEKDAYS = [
|
|||
{label: gettext('52'), value: '52'}, {label: gettext('53'), value: '53'}, {label: gettext('54'), value: '54'}, {label: gettext('55'), value: '55'},
|
||||
{label: gettext('56'), value: '56'}, {label: gettext('57'), value: '57'}, {label: gettext('58'), value: '58'}, {label: gettext('59'), value: '59'},
|
||||
];
|
||||
|
||||
export const PGAGENT_MONTHDAYS = [...MONTHDAYS].concat([{label: gettext('Last day'), value: 'Last Day'}]);
|
||||
|
|
|
@ -35,7 +35,7 @@ const StyledBox = styled(Box)(({theme}) => ({
|
|||
|
||||
export default function SectionContainer({title, titleExtras, children, style}) {
|
||||
return (
|
||||
<StyledBox style={style}>
|
||||
<StyledBox className='SectionContainer-root' style={style}>
|
||||
<Box className='SectionContainer-cardHeader' title={title}>
|
||||
<div className='SectionContainer-cardTitle'>{title}</div>
|
||||
<div style={{marginLeft: 'auto'}}>
|
||||
|
|
|
@ -10,10 +10,8 @@ import Statistics from '../../misc/statistics/static/js/Statistics';
|
|||
import { BROWSER_PANELS } from '../../browser/static/js/constants';
|
||||
import Dependencies from '../../misc/dependencies/static/js/Dependencies';
|
||||
import Dependents from '../../misc/dependents/static/js/Dependents';
|
||||
import UtilityView from './UtilityView';
|
||||
import ModalProvider from './helpers/ModalProvider';
|
||||
import { NotifierProvider } from './helpers/Notifier';
|
||||
import ToolView from './ToolView';
|
||||
import ObjectExplorerToolbar from './helpers/ObjectExplorerToolbar';
|
||||
import MainMoreToolbar from './helpers/MainMoreToolbar';
|
||||
import Dashboard from '../../dashboard/static/js/Dashboard';
|
||||
|
@ -144,8 +142,6 @@ export default function BrowserComponent({pgAdmin}) {
|
|||
resetToTabPanel={BROWSER_PANELS.MAIN}
|
||||
/>
|
||||
</div>
|
||||
<UtilityView />
|
||||
<ToolView />
|
||||
</ModalProvider>
|
||||
<ObjectBreadcrumbs pgAdmin={pgAdmin} />
|
||||
</PgAdminContext.Provider>
|
||||
|
|
|
@ -465,7 +465,9 @@ function parsePlanData(data, ctx) {
|
|||
return retPlan;
|
||||
}
|
||||
|
||||
export default function Explain({plans=[]}) {
|
||||
export default function Explain({plans=[],
|
||||
emptyMessage=gettext('Use Explain/Explain analyze button to generate the plan for a query. Alternatively, you can also execute "EXPLAIN (FORMAT JSON) [QUERY]".')
|
||||
}) {
|
||||
|
||||
const [tabValue, setTabValue] = React.useState(0);
|
||||
|
||||
|
@ -490,7 +492,7 @@ export default function Explain({plans=[]}) {
|
|||
if(_.isEmpty(plans)) {
|
||||
return (
|
||||
<StyledBox height="100%" display="flex" flexDirection="column">
|
||||
<EmptyPanelMessage text={gettext('Use Explain/Explain analyze button to generate the plan for a query. Alternatively, you can also execute "EXPLAIN (FORMAT JSON) [QUERY]".')} />
|
||||
{emptyMessage && <EmptyPanelMessage text={emptyMessage} />}
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
|
@ -526,5 +528,6 @@ export default function Explain({plans=[]}) {
|
|||
}
|
||||
|
||||
Explain.propTypes = {
|
||||
plans: PropTypes.array,
|
||||
plans: PropTypes.array.isRequired,
|
||||
emptyMessage: PropTypes.string,
|
||||
};
|
||||
|
|
|
@ -223,7 +223,7 @@ export class SchemaState extends DepListener {
|
|||
state.data = sessData;
|
||||
state._changes = state.changes();
|
||||
state.updateOptions();
|
||||
state.onDataChange && state.onDataChange(state.isDirty, state._changes);
|
||||
state.onDataChange && state.onDataChange(state.isDirty, state._changes, state.errors);
|
||||
}
|
||||
|
||||
changes(includeSkipChange=false) {
|
||||
|
|
|
@ -173,7 +173,7 @@ basicSettings = createTheme(basicSettings, {
|
|||
MuiTabs: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
minHeight: 0,
|
||||
minHeight: '30px',
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -242,6 +242,7 @@ basicSettings = createTheme(basicSettings, {
|
|||
marginBottom: 0,
|
||||
marginLeft: 0,
|
||||
marginRight: 0,
|
||||
gap: '4px'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import getApiInstance from 'sources/api_instance';
|
||||
import {getHelpUrl, getEPASHelpUrl} from 'pgadmin.help';
|
||||
import SchemaView from 'sources/SchemaView';
|
||||
|
@ -20,23 +20,32 @@ import usePreferences from '../../preferences/static/js/store';
|
|||
import gettext from 'sources/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default function UtilityView() {
|
||||
export default function UtilityView({dockerObj}) {
|
||||
const pgAdmin = usePgAdmin();
|
||||
const docker = useRef(dockerObj ?? pgAdmin.Browser.docker);
|
||||
|
||||
useEffect(()=>{
|
||||
docker.current = dockerObj ?? pgAdmin.Browser.docker;
|
||||
}, [dockerObj]);
|
||||
|
||||
useEffect(()=>{
|
||||
pgAdmin.Browser.Events.on('pgadmin:utility:show', (item, panelTitle, dialogProps, width=pgAdmin.Browser.stdW.default, height=pgAdmin.Browser.stdH.md)=>{
|
||||
const treeNodeInfo = pgAdmin.Browser.tree.getTreeNodeHierarchy(item);
|
||||
const treeNodeInfo = pgAdmin.Browser.tree?.getTreeNodeHierarchy(item);
|
||||
const panelId = _.uniqueId(BROWSER_PANELS.UTILITY_DIALOG);
|
||||
pgAdmin.Browser.docker.openDialog({
|
||||
const onClose = ()=>docker.current.close(panelId);
|
||||
docker.current.openDialog({
|
||||
id: panelId,
|
||||
title: panelTitle,
|
||||
content: (
|
||||
<ErrorBoundary>
|
||||
<UtilityViewContent
|
||||
docker={docker.current}
|
||||
panelId={panelId}
|
||||
schema={dialogProps.schema}
|
||||
treeNodeInfo={treeNodeInfo}
|
||||
actionType={dialogProps.actionType??'create'}
|
||||
formType='dialog'
|
||||
onClose={onClose}
|
||||
onSave={dialogProps.onSave ?? ((data)=>{
|
||||
if(data.errormsg) {
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
|
@ -48,7 +57,7 @@ export default function UtilityView() {
|
|||
} else if(data.info) {
|
||||
pgAdmin.Browser.notifier.success(data.info);
|
||||
}
|
||||
pgAdmin.Browser.docker.close(panelId);
|
||||
onClose();
|
||||
})}
|
||||
extraData={dialogProps.extraData??{}}
|
||||
saveBtnName={dialogProps.saveBtnName}
|
||||
|
@ -64,8 +73,12 @@ export default function UtilityView() {
|
|||
return <></>;
|
||||
}
|
||||
|
||||
UtilityView.propTypes = {
|
||||
dockerObj: PropTypes.object,
|
||||
};
|
||||
|
||||
/* The entry point for rendering React based view in properties, called in node.js */
|
||||
function UtilityViewContent({panelId, schema, treeNodeInfo, actionType, formType,
|
||||
function UtilityViewContent({schema, treeNodeInfo, actionType, formType, onClose,
|
||||
onSave, extraData, saveBtnName, urlBase, sqlHelpUrl, helpUrl, isTabView=true}) {
|
||||
|
||||
const pgAdmin = usePgAdmin();
|
||||
|
@ -83,8 +96,6 @@ function UtilityViewContent({panelId, schema, treeNodeInfo, actionType, formType
|
|||
let nodeObj = extraData.nodeType? pgAdmin.Browser.Nodes[extraData.nodeType]: undefined;
|
||||
let itemNodeData = extraData?.itemNodeData ? itemNodeData: undefined;
|
||||
|
||||
const onClose = ()=>pgAdmin.Browser.docker.close(panelId);
|
||||
|
||||
/* on save button callback, promise required */
|
||||
const onSaveClick = (isNew, data)=>new Promise((resolve, reject)=>{
|
||||
return api({
|
||||
|
@ -94,7 +105,7 @@ function UtilityViewContent({panelId, schema, treeNodeInfo, actionType, formType
|
|||
}).then((res)=>{
|
||||
/* Don't warn the user before closing dialog */
|
||||
resolve(res.data);
|
||||
onSave?.(res.data);
|
||||
onSave?.(res.data, data);
|
||||
onClose();
|
||||
}).catch((err)=>{
|
||||
reject(err instanceof Error ? err : Error(gettext('Something went wrong')));
|
||||
|
@ -212,6 +223,7 @@ UtilityViewContent.propTypes = {
|
|||
actionType: PropTypes.string,
|
||||
formType: PropTypes.string,
|
||||
onSave: PropTypes.func,
|
||||
onClose: PropTypes.func,
|
||||
extraData: PropTypes.object,
|
||||
saveBtnName: PropTypes.string,
|
||||
urlBase: PropTypes.string,
|
||||
|
|
|
@ -165,7 +165,7 @@ DefaultButton.propTypes = {
|
|||
|
||||
|
||||
/* pgAdmin Icon button, takes Icon component as input */
|
||||
export const PgIconButton = forwardRef(({icon, title, shortcut, className, splitButton, style, color, accesskey, ...props}, ref)=>{
|
||||
export const PgIconButton = forwardRef(({icon, title, shortcut, className, splitButton, style, color, accesskey, isDropdown, ...props}, ref)=>{
|
||||
let shortcutTitle = null;
|
||||
if(accesskey || shortcut) {
|
||||
shortcutTitle = <ShortcutTitle title={title} accesskey={accesskey} shortcut={shortcut}/>;
|
||||
|
@ -192,7 +192,7 @@ export const PgIconButton = forwardRef(({icon, title, shortcut, className, split
|
|||
}
|
||||
} else if(color == 'primary') {
|
||||
return (
|
||||
<Tooltip title={shortcutTitle || title || ''} aria-label={title || ''}>
|
||||
<Tooltip title={shortcutTitle || title || ''} aria-label={title || ''} enterDelay={isDropdown ? 1500 : undefined}>
|
||||
<PrimaryButton ref={ref} style={style}
|
||||
className={['Buttons-iconButton', (splitButton ? 'Buttons-splitButton' : ''), className].join(' ')}
|
||||
accessKey={accesskey} data-label={title || ''} {...props}>
|
||||
|
@ -203,7 +203,7 @@ export const PgIconButton = forwardRef(({icon, title, shortcut, className, split
|
|||
);
|
||||
} else {
|
||||
return (
|
||||
<Tooltip title={shortcutTitle || title || ''} aria-label={title || ''}>
|
||||
<Tooltip title={shortcutTitle || title || ''} aria-label={title || ''} enterDelay={isDropdown ? 1500 : undefined}>
|
||||
<DefaultButton ref={ref} style={style}
|
||||
className={['Buttons-iconButton', 'Buttons-iconButtonDefault',(splitButton ? 'Buttons-splitButton' : ''), className].join(' ')}
|
||||
accessKey={accesskey} data-label={title || ''} {...props}>
|
||||
|
@ -224,6 +224,7 @@ PgIconButton.propTypes = {
|
|||
color: PropTypes.oneOf(['primary', 'default', undefined]),
|
||||
disabled: PropTypes.bool,
|
||||
splitButton: PropTypes.bool,
|
||||
isDropdown: PropTypes.bool,
|
||||
};
|
||||
|
||||
export const PgButtonGroup = forwardRef(({children, ...props}, ref)=>{
|
||||
|
|
|
@ -261,15 +261,17 @@ export function InputDateTimePicker({ value, onChange, readonly, controlProps, .
|
|||
let placeholder = '';
|
||||
let regExp = /[a-zA-Z]/;
|
||||
let timeZoneString = '';
|
||||
|
||||
controlProps = controlProps ?? {};
|
||||
if (controlProps?.pickerType === 'Date') {
|
||||
format = controlProps.format || DATE_TIME_FORMAT.DATE;
|
||||
format = controlProps?.format || DATE_TIME_FORMAT.DATE;
|
||||
placeholder = controlProps.placeholder || 'YYYY-MM-DD';
|
||||
} else if (controlProps?.pickerType === 'Time') {
|
||||
format = controlProps.format || (controlProps.ampm ? DATE_TIME_FORMAT.TIME_12 : DATE_TIME_FORMAT.TIME_24);
|
||||
placeholder = controlProps.placeholder || 'HH:mm:ss';
|
||||
format = controlProps?.format || (controlProps?.ampm ? DATE_TIME_FORMAT.TIME_12 : DATE_TIME_FORMAT.TIME_24);
|
||||
placeholder = controlProps?.placeholder || 'HH:mm:ss';
|
||||
} else {
|
||||
format = controlProps.format || (controlProps.ampm ? DATE_TIME_FORMAT.DATE_TIME_12 : DATE_TIME_FORMAT.DATE_TIME_24);
|
||||
placeholder = controlProps.placeholder || 'YYYY-MM-DD HH:mm:ss Z';
|
||||
format = controlProps?.format || (controlProps?.ampm ? DATE_TIME_FORMAT.DATE_TIME_12 : DATE_TIME_FORMAT.DATE_TIME_24);
|
||||
placeholder = controlProps?.placeholder || 'YYYY-MM-DD HH:mm:ss Z';
|
||||
}
|
||||
|
||||
const handleChange = (dateVal) => {
|
||||
|
@ -572,7 +574,7 @@ FormInputSwitch.propTypes = {
|
|||
labelTooltip: PropTypes.string
|
||||
};
|
||||
|
||||
export function InputCheckbox({ cid, helpid, value, onChange, controlProps, readonly, labelPlacement, ...props }) {
|
||||
export function InputCheckbox({ cid, helpid, value, onChange, controlProps, readonly, disabled, labelPlacement, ...props }) {
|
||||
controlProps = controlProps || {};
|
||||
return (
|
||||
<FormControlLabel
|
||||
|
@ -585,6 +587,7 @@ export function InputCheckbox({ cid, helpid, value, onChange, controlProps, read
|
|||
inputProps={{ 'aria-describedby': helpid, 'title': controlProps.label}}
|
||||
{...props} />
|
||||
}
|
||||
disabled={disabled}
|
||||
label={controlProps.label}
|
||||
labelPlacement={labelPlacement}
|
||||
/>
|
||||
|
@ -597,6 +600,7 @@ InputCheckbox.propTypes = {
|
|||
controlProps: PropTypes.object,
|
||||
onChange: PropTypes.func,
|
||||
readonly: PropTypes.bool,
|
||||
disabled: PropTypes.bool,
|
||||
labelPlacement: PropTypes.string
|
||||
};
|
||||
|
||||
|
|
|
@ -39,6 +39,9 @@ const StyledReactDataGrid = styled(ReactDataGrid)(({theme})=>({
|
|||
},
|
||||
'& .rdg-cell-value': {
|
||||
height: '100%',
|
||||
},
|
||||
'&.rdg-cell-copied[aria-selected=false][role="gridcell"]': {
|
||||
backgroundColor: 'unset',
|
||||
}
|
||||
},
|
||||
'& .rdg-header-row .rdg-cell': {
|
||||
|
|
|
@ -317,13 +317,13 @@ const StyledPgTableRoot = styled('div')(({theme})=>({
|
|||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
export default function PgTable({ caveTable = true, tableNoBorder = true, ...props }) {
|
||||
export default function PgTable({ caveTable = true, tableNoBorder = true, tableNoHeader=false, ...props }) {
|
||||
const [searchVal, setSearchVal] = React.useState('');
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<StyledPgTableRoot className={[tableNoBorder ? '' : 'pgtable-pgrt-border', caveTable ? 'pgtable-pgrt-cave' : ''].join(' ')} data-test={props['data-test']}>
|
||||
<Box className='pgtable-header'>
|
||||
{!tableNoHeader && <Box className='pgtable-header'>
|
||||
{props.customHeader && (<Box className={['pgtable-custom-header-section', props['className']].join(' ')}> {props.customHeader }</Box>)}
|
||||
<Box marginLeft="auto">
|
||||
<InputText
|
||||
|
@ -336,7 +336,7 @@ export default function PgTable({ caveTable = true, tableNoBorder = true, ...pro
|
|||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>}
|
||||
<div className={'pgtable-body'} >
|
||||
<Table {...props} searchVal={searchVal}/>
|
||||
</div>
|
||||
|
@ -349,6 +349,7 @@ PgTable.propTypes = {
|
|||
customHeader: PropTypes.element,
|
||||
caveTable: PropTypes.bool,
|
||||
tableNoBorder: PropTypes.bool,
|
||||
tableNoHeader: PropTypes.bool,
|
||||
'data-test': PropTypes.string,
|
||||
'className': PropTypes.string
|
||||
};
|
||||
|
|
|
@ -15,6 +15,8 @@ import ContextMenu from '../../components/ContextMenu';
|
|||
import { showRenameTab } from '../../Dialogs';
|
||||
import usePreferences from '../../../../preferences/static/js/store';
|
||||
import _ from 'lodash';
|
||||
import UtilityView from '../../UtilityView';
|
||||
import ToolView from '../../ToolView';
|
||||
|
||||
function TabTitle({id, closable, defaultInternal}) {
|
||||
const layoutDocker = React.useContext(LayoutDockerContext);
|
||||
|
@ -482,6 +484,8 @@ export default function Layout({groups, noContextGroups, getLayoutInstance, layo
|
|||
<div id="layout-portal"></div>
|
||||
<ContextMenu menuItems={contextMenuItems} position={contextPos} onClose={()=>setContextPos([null, null, null])}
|
||||
label="Layout Context Menu" />
|
||||
<UtilityView dockerObj={layoutDockerObj} />
|
||||
<ToolView dockerObj={layoutDockerObj} />
|
||||
</LayoutDockerContext.Provider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -72,51 +72,5 @@ define([], function() {
|
|||
return 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decimal adjustment of a number.
|
||||
*
|
||||
* @param {String} type The type of adjustment.
|
||||
* @param {Number} value The number.
|
||||
* @param {Integer} exp The exponent (the 10 logarithm of the adjustment base).
|
||||
* @returns {Number} The adjusted value.
|
||||
*/
|
||||
function decimalAdjust(type, value, exp) {
|
||||
// If the exp is undefined or zero...
|
||||
if (typeof exp === 'undefined' || +exp === 0) {
|
||||
return Math[type](value);
|
||||
}
|
||||
value = +value;
|
||||
exp = +exp;
|
||||
// If the value is not a number or the exp is not an integer...
|
||||
if (isNaN(value) || exp % 1 !== 0) {
|
||||
return NaN;
|
||||
}
|
||||
// Shift
|
||||
value = value.toString().split('e');
|
||||
value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
|
||||
// Shift back
|
||||
value = value.toString().split('e');
|
||||
return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
|
||||
}
|
||||
|
||||
// Decimal round
|
||||
if (!Math.round10) {
|
||||
Math.round10 = function(value, exp) {
|
||||
return decimalAdjust('round', value, exp);
|
||||
};
|
||||
}
|
||||
// Decimal floor
|
||||
if (!Math.floor10) {
|
||||
Math.floor10 = function(value, exp) {
|
||||
return decimalAdjust('floor', value, exp);
|
||||
};
|
||||
}
|
||||
// Decimal ceil
|
||||
if (!Math.ceil10) {
|
||||
Math.ceil10 = function(value, exp) {
|
||||
return decimalAdjust('ceil', value, exp);
|
||||
};
|
||||
}
|
||||
|
||||
return pgAdmin;
|
||||
});
|
||||
|
|
|
@ -415,21 +415,27 @@ export function checkTrojanSource(content, isPasteEvent) {
|
|||
}
|
||||
|
||||
export function downloadBlob(blob, fileName) {
|
||||
let urlCreator = window.URL || window.webkitURL,
|
||||
downloadUrl = urlCreator.createObjectURL(blob),
|
||||
link = document.createElement('a');
|
||||
|
||||
document.body.appendChild(link);
|
||||
|
||||
if (getBrowser() == 'IE' && window.navigator.msSaveBlob) {
|
||||
// IE10+ : (has Blob, but not a[download] or URL)
|
||||
// IE10+ : (has Blob, but not a[download] or URL)
|
||||
window.navigator.msSaveBlob(blob, fileName);
|
||||
} else {
|
||||
const urlCreator = window.URL || window.webkitURL;
|
||||
const downloadUrl = urlCreator.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.setAttribute('href', downloadUrl);
|
||||
link.setAttribute('download', fileName);
|
||||
link.style.setProperty('visibility ', 'hidden');
|
||||
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
export function downloadFile(textData, fileName, fileType) {
|
||||
const respBlob = new Blob([textData], {type : fileType});
|
||||
downloadBlob(respBlob, fileName);
|
||||
}
|
||||
|
||||
export function toPrettySize(rawSize, from='B') {
|
||||
|
@ -748,4 +754,50 @@ export function getPlatform() {
|
|||
} else {
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decimal adjustment of a number.
|
||||
*
|
||||
* @param {String} type The type of adjustment.
|
||||
* @param {Number} value The number.
|
||||
* @param {Integer} exp The exponent (the 10 logarithm of the adjustment base).
|
||||
* @returns {Number} The adjusted value.
|
||||
*/
|
||||
function decimalAdjust(type, value, exp) {
|
||||
// If the exp is undefined or zero...
|
||||
if (typeof exp === 'undefined' || +exp === 0) {
|
||||
return Math[type](value);
|
||||
}
|
||||
value = +value;
|
||||
exp = +exp;
|
||||
// If the value is not a number or the exp is not an integer...
|
||||
if (isNaN(value) || exp % 1 !== 0) {
|
||||
return NaN;
|
||||
}
|
||||
// Shift
|
||||
value = value.toString().split('e');
|
||||
value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
|
||||
// Shift back
|
||||
value = value.toString().split('e');
|
||||
return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
|
||||
}
|
||||
|
||||
// Decimal round
|
||||
if (!Math.round10) {
|
||||
Math.round10 = function(value, exp) {
|
||||
return decimalAdjust('round', value, exp);
|
||||
};
|
||||
}
|
||||
// Decimal floor
|
||||
if (!Math.floor10) {
|
||||
Math.floor10 = function(value, exp) {
|
||||
return decimalAdjust('floor', value, exp);
|
||||
};
|
||||
}
|
||||
// Decimal ceil
|
||||
if (!Math.ceil10) {
|
||||
Math.ceil10 = function(value, exp) {
|
||||
return decimalAdjust('ceil', value, exp);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1302,6 +1302,11 @@ WHERE db.datname = current_database()""")
|
|||
rows = []
|
||||
self.row_count = cur.rowcount
|
||||
|
||||
# If multiple queries are run, make sure to reach
|
||||
# the last query result
|
||||
while cur.nextset():
|
||||
pass # This loop is empty
|
||||
|
||||
if cur.get_rowcount() > 0:
|
||||
rows = cur.fetchall()
|
||||
|
||||
|
|
|
@ -225,7 +225,9 @@ describe('SchemaView', ()=>{
|
|||
let onResetAction = (data)=> {
|
||||
expect(ctrl.container.querySelector('[data-test="Reset"]').hasAttribute('disabled')).toBe(true);
|
||||
expect(ctrl.container.querySelector('[data-test="Save"]').hasAttribute('disabled')).toBe(true);
|
||||
expect(onDataChange).toHaveBeenCalledWith(false, data);
|
||||
const callArgs = onDataChange.mock.calls[onDataChange.mock.calls.length - 1];
|
||||
expect(callArgs[0]).toEqual(false);
|
||||
expect(callArgs[1]).toEqual(data);
|
||||
};
|
||||
|
||||
beforeEach(async ()=>{
|
||||
|
@ -274,7 +276,9 @@ describe('SchemaView', ()=>{
|
|||
expect(ctrl.container.querySelector('[data-test="Reset"]').hasAttribute('disabled')).toBe(true);
|
||||
expect(ctrl.container.querySelector('[data-test="Save"]').hasAttribute('disabled')).toBe(true);
|
||||
// on reset, orig data will be considered
|
||||
expect(onDataChange).toHaveBeenCalledWith(false, { id: undefined, field1: null, field2: null, fieldcoll: null });
|
||||
const callArgs = onDataChange.mock.calls[onDataChange.mock.calls.length - 1];
|
||||
expect(callArgs[0]).toEqual(false);
|
||||
expect(callArgs[1]).toEqual({ id: undefined, field1: null, field2: null, fieldcoll: null });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -120,7 +120,9 @@ describe('SchemaView', ()=>{
|
|||
});
|
||||
expect(ctrl.container.querySelector('[data-test="Reset"]').hasAttribute('disabled')).toBe(true);
|
||||
expect(ctrl.container.querySelector('[data-test="Save"]').hasAttribute('disabled')).toBe(true);
|
||||
expect(onDataChange).toHaveBeenCalledWith(false, {});
|
||||
const callArgs = onDataChange.mock.calls[onDataChange.mock.calls.length - 1];
|
||||
expect(callArgs[0]).toEqual(false);
|
||||
expect(callArgs[1]).toEqual({});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -22,6 +22,7 @@ import DebuggerComponent from '../../../pgadmin/tools/debugger/static/js/compone
|
|||
import FunctionArguments from '../../../pgadmin/tools/debugger/static/js/debugger_ui';
|
||||
import Debugger from '../../../pgadmin/tools/debugger/static/js/DebuggerModule';
|
||||
import Theme from '../../../pgadmin/static/js/Theme';
|
||||
import { PgAdminContext } from '../../../pgadmin/static/js/BrowserComponent';
|
||||
|
||||
|
||||
describe('Debugger Component', () => {
|
||||
|
@ -58,13 +59,15 @@ describe('Debugger Component', () => {
|
|||
await act(async () => {
|
||||
render(
|
||||
<Theme>
|
||||
<DebuggerComponent
|
||||
pgAdmin={pgAdmin}
|
||||
panel={document.getElementById('debugger-main-container')}
|
||||
selectedNodeInfo={nodeInfo}
|
||||
layout={''}
|
||||
params={params}
|
||||
/>
|
||||
<PgAdminContext.Provider value={pgAdmin}>
|
||||
<DebuggerComponent
|
||||
pgAdmin={pgAdmin}
|
||||
panel={document.getElementById('debugger-main-container')}
|
||||
selectedNodeInfo={nodeInfo}
|
||||
layout={''}
|
||||
params={params}
|
||||
/>
|
||||
</PgAdminContext.Provider>
|
||||
</Theme>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -94,6 +94,15 @@ Element.prototype.getBoundingClientRect = jest.fn(function () {
|
|||
};
|
||||
});
|
||||
|
||||
Object.defineProperty(global.SVGElement.prototype, 'getBBox', {
|
||||
writable: true,
|
||||
value: jest.fn().mockReturnValue({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
}),
|
||||
});
|
||||
|
||||
global.TextEncoder = TextEncoder;
|
||||
global.TextDecoder = TextDecoder;
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import React from 'react';
|
||||
import { PgAdminContext } from '../../pgadmin/static/js/BrowserComponent';
|
||||
import fakePgAdmin from './fake_pgadmin';
|
||||
|
||||
export default function withPgadmin(WrappedComp) {
|
||||
/* eslint-disable react/display-name */
|
||||
return (props)=>{
|
||||
return <PgAdminContext.Provider value={fakePgAdmin}>
|
||||
<WrappedComp {...props}/>
|
||||
</PgAdminContext.Provider>;
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue