Foreign Key fixes:

1. Auto FK related fixes.
2. Properties tab not showing columns.
3. Toggle button is editable even if set to read-only.
4. Dropdown placeholder should be blank for read-only/disabled.
5. Input control help text color on a dark theme.

Fixes #6770
This commit is contained in:
Aditya Toshniwal 2021-09-22 18:18:09 +05:30 committed by Akshay Joshi
parent fc86faf51e
commit a8c8ea69e3
13 changed files with 29 additions and 195 deletions

View File

@ -22,7 +22,7 @@ export default class CheckConstraintSchema extends BaseUISchema {
if(_.isUndefined(this.nodeInfo)) {
return true;
}
return !_.isUndefined(this.nodeInfo['table']);
return _.isUndefined(this.nodeInfo['table']);
}
isReadonly(state) {

View File

@ -214,7 +214,7 @@ export default class ExclusionConstraintSchema extends BaseUISchema {
if(_.isUndefined(this.nodeInfo)) {
return true;
}
return !_.isUndefined(this.nodeInfo['table']);
return _.isUndefined(this.nodeInfo['table']);
}
initialise(data) {

View File

@ -124,7 +124,7 @@ export default class ForeignKeySchema extends BaseUISchema {
columns: undefined,
confupdtype: 'a',
confdeltype: 'a',
autoindex: true,
autoindex: ForeignKeySchema.checkInTable(nodeInfo) ? false : true,
coveringindex: undefined,
hasindex:undefined,
});
@ -142,10 +142,14 @@ export default class ForeignKeySchema extends BaseUISchema {
}
get inTable() {
if(_.isUndefined(this.nodeInfo)) {
return ForeignKeySchema.checkInTable(this.nodeInfo);
}
static checkInTable(nodeInfo) {
if(_.isUndefined(nodeInfo)) {
return true;
}
return !_.isUndefined(this.nodeInfo['table']);
return _.isUndefined(nodeInfo['table']);
}
changeColumnOptions(columns) {
@ -231,23 +235,14 @@ export default class ForeignKeySchema extends BaseUISchema {
id: 'autoindex', label: gettext('Auto FK index?'),
type: 'switch', group: gettext('Definition'),
deps: ['name', 'hasindex'],
disabled: (state)=>{
readonly: (state)=>{
if(!obj.isNew(state)) {
return true;
}
// If we are in table edit mode then
if(obj.inTable) {
// user is trying to add new constraint which should allowed for Unique
if(obj.isNew(state)) {
return true;
}
} else {
if(!obj.isNew(state) && state.autoindex && !isEmptyString(state.coveringindex)
&& state.hasindex) {
return true;
}
}
if(state.hasindex) {
return true;
} else if(state.hasindex) {
return true;
}
return false;
@ -256,10 +251,9 @@ export default class ForeignKeySchema extends BaseUISchema {
if(!obj.isNew(state)) {
return {};
}
// If we are in table edit mode then
// If we are in table edit mode
if(obj.inTable) {
// user is trying to add new constraint which should allowed for Unique
if(obj.isNew(state)) {
if(obj.isNew(state) && obj.top.isNew()) {
return {autoindex: false, coveringindex: ''};
}
}
@ -279,12 +273,7 @@ export default class ForeignKeySchema extends BaseUISchema {
mode: ['properties', 'create', 'edit'], group: gettext('Definition'),
deps:['autoindex', 'hasindex'],
disabled: (state)=>{
if(!obj.isNew(state) && state.autoindex && !isEmptyString(state.coveringindex)) {
return true;
}
if(state.hasindex) {
return true;
} else if(!state.autoindex) {
if(!state.autoindex && !state.hasindex) {
return true;
} else {
return false;
@ -304,7 +293,7 @@ export default class ForeignKeySchema extends BaseUISchema {
},{
id: 'columns', label: gettext('Columns'),
group: gettext('Columns'), type: 'collection',
mode: ['create', 'edit'],
mode: ['create', 'edit', 'properties'],
editable: false, schema: this.fkColumnSchema,
headerSchema: this.fkHeaderSchema, headerVisible: (state)=>obj.isNew(state),
CustomControl: DataGridViewWithHeaderForm,

View File

@ -81,7 +81,6 @@ const useStyles = makeStyles((theme)=>({
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
textAlign: 'center',
},
tableCellHeader: {
fontWeight: theme.typography.fontWeightBold,

View File

@ -59,6 +59,7 @@ export default function(basicSettings) {
},
text: {
primary: '#d4d4d4',
muted: '#8A8A8A',
},
background: {
paper: '#212121',

View File

@ -57,6 +57,7 @@ export default function(basicSettings) {
},
text: {
primary: '#fff',
muted: '#8b9cad',
},
background: {
paper: '#010B15',

View File

@ -325,6 +325,11 @@ function getFinalTheme(baseTheme) {
}
}
},
MuiFormHelperText: {
root: {
color: baseTheme.palette.text.muted,
},
},
}
}, baseTheme);
}

View File

@ -58,6 +58,7 @@ export default function(basicSettings) {
},
text: {
primary: '#222',
muted: '#646B82',
},
background: {
paper: '#fff',

View File

@ -511,7 +511,7 @@ export function InputToggle({cid, value, onChange, options, disabled, readonly,
{
(options||[]).map((option)=>{
const isSelected = option.value === value;
const isDisabled = disabled || (readonly && isSelected);
const isDisabled = disabled || (readonly && !isSelected);
return (
<ToggleButton key={option.label} value={option.value} component={isSelected ? PrimaryButton : DefaultButton}
disabled={isDisabled} aria-label={option.label}>
@ -766,7 +766,7 @@ export function InputSelect({
menuPortalTarget: document.body,
styles: styles,
inputId: cid,
placeholder: controlProps.placeholder || gettext('Select...'),
placeholder: (readonly || disabled) ? '' : controlProps.placeholder || gettext('Select...'),
...otherProps,
...props
};

View File

@ -1,162 +0,0 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import {ModelValidation} from 'sources/browser/server_groups/servers/model_validation';
describe('Server#ModelValidation', () => {
describe('When validating a server parameters', () => {
let model;
let modelValidation;
beforeEach(() => {
model = {
errorModel: jasmine.createSpyObj('errorModel', ['set', 'unset']),
allValues: {},
get: function (key) {
return this.allValues[key];
},
set: function (key, value) {
this.key = value;
},
sessAttrs: {},
};
model.isNew = jasmine.createSpy('isNew');
modelValidation = new ModelValidation(model);
});
describe('When all parameters are valid', () => {
beforeEach(() => {
model.isNew.and.returnValue(true);
model.allValues['name'] = 'some name';
model.allValues['username'] = 'some username';
model.allValues['port'] = 12345;
});
describe('No service id', () => {
it('does not set any error in the model', () => {
model.allValues['host'] = 'some host';
model.allValues['db'] = 'some db';
model.allValues['hostaddr'] = '1.1.1.1';
expect(modelValidation.validate()).toBeNull();
expect(model.errorModel.set).toHaveBeenCalledWith({});
});
});
describe('Service id and Maintenance database present', () => {
it('does not set any error in the model', () => {
model.allValues['service'] = 'asdfg';
model.allValues['db'] = 'postgres';
expect(modelValidation.validate()).toBeNull();
expect(model.errorModel.set).toHaveBeenCalledWith({});
});
});
describe('Service id present', () => {
it('sets empty service name which should throw an error', () => {
model.allValues['service'] = '';
expect(modelValidation.validate()).toEqual('Either Host name, Address or Service must be specified.');
expect(model.errorModel.set).toHaveBeenCalledWith({
host: 'Either Host name, Address or Service must be specified.',
hostaddr: 'Either Host name, Address or Service must be specified.',
db: 'Maintenance database must be specified.',
});
});
});
describe('SSH Tunnel parameters', () => {
beforeEach(() => {
model.isNew.and.returnValue(true);
model.allValues['name'] = 'some name';
model.allValues['username'] = 'some username';
model.allValues['port'] = 12345;
model.allValues['host'] = 'some host';
model.allValues['db'] = 'some db';
model.allValues['hostaddr'] = '1.1.1.1';
model.allValues['use_ssh_tunnel'] = 1;
});
it('sets the "SSH Tunnel host must be specified." error', () => {
model.allValues['tunnel_port'] = 22;
model.allValues['tunnel_username'] = 'user1';
expect(modelValidation.validate()).toEqual('SSH Tunnel host must be specified.');
expect(model.errorModel.set).toHaveBeenCalledWith({
tunnel_host:'SSH Tunnel host must be specified.',
});
});
it('sets the "SSH Tunnel port must be specified." error', () => {
model.allValues['tunnel_host'] = 'host';
model.allValues['tunnel_username'] = 'user1';
expect(modelValidation.validate()).toEqual('SSH Tunnel port must be specified.');
expect(model.errorModel.set).toHaveBeenCalledWith({
tunnel_port:'SSH Tunnel port must be specified.',
});
});
it('sets the "SSH Tunnel username be specified." error', () => {
model.allValues['tunnel_host'] = 'host';
model.allValues['tunnel_port'] = 22;
expect(modelValidation.validate()).toEqual('SSH Tunnel username must be specified.');
expect(model.errorModel.set).toHaveBeenCalledWith({
tunnel_username:'SSH Tunnel username must be specified.',
});
});
it('sets the "SSH Tunnel identity file be specified." error', () => {
model.allValues['tunnel_host'] = 'host';
model.allValues['tunnel_port'] = 22;
model.allValues['tunnel_username'] = 'user1';
model.allValues['tunnel_authentication'] = 1;
expect(modelValidation.validate()).toEqual('SSH Tunnel identity file must be specified.');
expect(model.errorModel.set).toHaveBeenCalledWith({
tunnel_identity_file:'SSH Tunnel identity file must be specified.',
});
});
});
});
describe('When no parameters are valid', () => {
describe('Service id not present', () => {
it('does not set any error in the model', () => {
expect(modelValidation.validate()).toEqual('Name must be specified.');
expect(model.errorModel.set).toHaveBeenCalledTimes(1);
expect(model.errorModel.set).toHaveBeenCalledWith({
name: 'Name must be specified.',
host: 'Either Host name, Address or Service must be specified.',
hostaddr: 'Either Host name, Address or Service must be specified.',
db: 'Maintenance database must be specified.',
username: 'Username must be specified.',
port: 'Port must be specified.',
});
});
});
describe('Host address is not valid', () => {
it('sets the "Host address must be a valid IPv4 or IPv6 address" error', () => {
model.allValues['hostaddr'] = 'something that is not an ip address';
expect(modelValidation.validate()).toEqual('Host address must be valid IPv4 or IPv6 address.');
expect(model.errorModel.set).toHaveBeenCalledTimes(1);
expect(model.errorModel.set).toHaveBeenCalledWith({
name: 'Name must be specified.',
hostaddr: 'Host address must be valid IPv4 or IPv6 address.',
db: 'Maintenance database must be specified.',
username: 'Username must be specified.',
port: 'Port must be specified.',
});
});
});
describe('Service id present', () => {
it('set the "Maintenance database must be specified." error', () => {
model.allValues['name'] = 'some name';
model.allValues['service'] = 'asdfg';
expect(modelValidation.validate()).toEqual('Maintenance database must be specified.');
expect(model.errorModel.set).toHaveBeenCalledWith({
db: 'Maintenance database must be specified.',
});
});
});
});
});
});

View File

@ -142,7 +142,6 @@ describe('CheckConstraintSchema', ()=>{
});
/* If partitioned table */
schemaObj.nodeInfo = {table: {}};
schemaObj.top = {
sessData: {
is_partitioned: true,

View File

@ -196,7 +196,6 @@ describe('ExclusionConstraintSchema', ()=>{
it('depChange', ()=>{
let state = {columns: [{local_column: 'id'}]};
schemaObj.nodeInfo = {table: {}};
expect(getFieldDepChange(schemaObj, 'columns')(state, ['columns', 0], null, {
type: SCHEMA_STATE_ACTIONS.DELETE_ROW,
oldState: {

View File

@ -171,6 +171,7 @@ describe('ForeignKeySchema', ()=>{
let state = {columns: [{local_column: 'id'}]};
let actionObj = {oldState:{name: 'fkname'}};
schemaObj.nodeInfo = {table: {}};
state.autoindex = true;
state.name = 'fkname';
expect(getFieldDepChange(schemaObj, 'autoindex')(state, null, null, actionObj)).toEqual({
@ -196,7 +197,8 @@ describe('ForeignKeySchema', ()=>{
expect(getFieldDepChange(schemaObj, 'autoindex')(state, null, null, actionObj)).toEqual({});
state.oid = null;
schemaObj.nodeInfo = {table: {}};
schemaObj.nodeInfo = {};
schemaObj.top = schemaObj;
expect(getFieldDepChange(schemaObj, 'autoindex')(state, null, null, actionObj)).toEqual({
autoindex: false,
coveringindex: '',