Code refactoring at multiple places to improve re-usability

pull/8125/head
Aditya Toshniwal 2024-11-14 14:16:38 +05:30
parent 00d3aaa1fd
commit 296befc881
22 changed files with 283 additions and 212 deletions

View File

@ -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({

View File

@ -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??{})
}
];
}
}

View File

@ -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'}]);

View File

@ -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'}}>

View File

@ -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>

View File

@ -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,
};

View File

@ -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) {

View File

@ -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'
}
}
},

View File

@ -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,

View File

@ -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)=>{

View File

@ -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
};

View File

@ -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': {

View File

@ -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
};

View File

@ -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>
);
}

View File

@ -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;
});

View File

@ -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);
};
}

View File

@ -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()

View File

@ -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 });
});
});
});

View File

@ -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({});
});
});
});

View File

@ -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>
);
});

View File

@ -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;

View File

@ -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>;
};
}