Port the remaining components of the ERD Tool to React. Fixes #7343

1. Make use of MUI styles and remove SCSS.
2. Use the new common components for buttons and tooltips, so that they are consistent.
3. UI design should be aligned with the query tool.
4. Remove tippyjs and Alertify dependencies.
This commit is contained in:
Aditya Toshniwal
2022-09-06 18:09:13 +05:30
committed by Akshay Joshi
parent 1d0ac0f7dc
commit 0f46f070ed
64 changed files with 1451 additions and 1757 deletions

View File

@@ -6,7 +6,7 @@
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import {KeyboardShortcutAction} from 'pgadmin.tools.erd/erd_tool/ui_components/BodyWidget';
import {KeyboardShortcutAction} from 'pgadmin.tools.erd/erd_tool/components/ERDTool';
describe('KeyboardShortcutAction', ()=>{
let keyAction = null;

View File

@@ -5,7 +5,7 @@ import '../helper/enzyme.helper';
import { DefaultNodeModel } from '@projectstorm/react-diagrams';
import {TableNodeModel, TableNodeWidget} from 'pgadmin.tools.erd/erd_tool/nodes/TableNode';
import { IconButton, DetailsToggleButton } from 'pgadmin.tools.erd/erd_tool/ui_components/ToolBar';
import Theme from '../../../pgadmin/static/js/Theme';
describe('ERD TableNodeModel', ()=>{
@@ -211,63 +211,45 @@ describe('ERD TableNodeWidget', ()=>{
});
it('render', ()=>{
let nodeWidget = mount(<TableNodeWidget node={node}/>);
expect(nodeWidget.getDOMNode().className).toBe('table-node ');
expect(nodeWidget.find('.table-node .table-toolbar').length).toBe(1);
expect(nodeWidget.find('.table-node .table-schema').text()).toBe('erd');
expect(nodeWidget.find('.table-node .table-name').text()).toBe('table1');
expect(nodeWidget.find('.table-node .table-cols').length).toBe(1);
expect(nodeWidget.find(DetailsToggleButton).length).toBe(1);
expect(nodeWidget.find(IconButton).findWhere(n => n.prop('title')=='Check note').length).toBe(1);
});
it('node selected', ()=>{
spyOn(node, 'isSelected').and.returnValue(true);
let nodeWidget = mount(<TableNodeWidget node={node}/>);
expect(nodeWidget.getDOMNode().className).toBe('table-node selected');
let nodeWidget = mount(<Theme><TableNodeWidget node={node}/></Theme>);
expect(nodeWidget.find('DefaultButton[aria-label="Show Details"]').length).toBe(1);
expect(nodeWidget.find('DefaultButton[aria-label="Check Note"]').length).toBe(1);
expect(nodeWidget.find('div[data-test="schema-name"]').length).toBe(1);
expect(nodeWidget.find('div[data-test="table-name"]').length).toBe(1);
expect(nodeWidget.find('div[data-test="column-row"]').length).toBe(3);
});
it('remove note', ()=>{
node.setNote('');
let nodeWidget = mount(<TableNodeWidget node={node}/>);
expect(nodeWidget.find(IconButton).findWhere(n => n.prop('title')=='Check note').length).toBe(0);
let nodeWidget = mount(<Theme><TableNodeWidget node={node}/></Theme>);
expect(nodeWidget.find('PgIconButton[aria-label="Check Note"]').length).toBe(0);
});
describe('generateColumn', ()=>{
let nodeWidget = null;
beforeEach(()=>{
nodeWidget = mount(<TableNodeWidget node={node}/>);
nodeWidget = mount(<Theme><TableNodeWidget node={node}/></Theme>);
});
it('count', ()=>{
expect(nodeWidget.find('.table-node .table-cols .col-row').length).toBe(3);
expect(nodeWidget.find('div[data-test="column-row"]').length).toBe(3);
});
it('column names', ()=>{
let cols = nodeWidget.find('.table-node .table-cols .col-row-data');
expect(cols.at(0).find('.col-name').text()).toBe('id');
expect(cols.at(1).find('.col-name').text()).toBe('amount');
expect(cols.at(2).find('.col-name').text()).toBe('desc');
let cols = nodeWidget.find('div[data-test="column-row"]');
expect(cols.at(0).find('span[data-test="column-name"]').text()).toBe('id');
expect(cols.at(1).find('span[data-test="column-name"]').text()).toBe('amount');
expect(cols.at(2).find('span[data-test="column-name"]').text()).toBe('desc');
});
it('data types', ()=>{
let cols = nodeWidget.find('.table-node .table-cols .col-row-data');
expect(cols.at(0).find('.col-datatype').text()).toBe('integer');
expect(cols.at(1).find('.col-datatype').text()).toBe('number(10,5)');
expect(cols.at(2).find('.col-datatype').text()).toBe('character varrying(50)');
});
let cols = nodeWidget.find('div[data-test="column-row"]');
it('show_details', (done)=>{
nodeWidget.setState({show_details: false});
expect(nodeWidget.find('.table-node .table-cols .col-row-data .col-datatype').length).toBe(0);
nodeWidget.instance().toggleShowDetails(jasmine.createSpyObj('event', ['preventDefault']));
/* Dummy set state to wait for toggleShowDetails -> setState to complete */
nodeWidget.setState({}, ()=>{
expect(nodeWidget.find('.table-node .table-cols .col-row-data .col-datatype').length).toBe(3);
done();
});
expect(cols.at(0).find('span[data-test="column-type"]').text()).toBe('integer');
expect(cols.at(1).find('span[data-test="column-type"]').text()).toBe('number(10,5)');
expect(cols.at(2).find('span[data-test="column-type"]').text()).toBe('character varrying(50)');
});
});
});

View File

@@ -0,0 +1,32 @@
import jasmineEnzyme from 'jasmine-enzyme';
import React from 'react';
import {mount} from 'enzyme';
import '../../helper/enzyme.helper';
import ConnectionBar, {STATUS} from 'pgadmin.tools.erd/erd_tool/components/ConnectionBar';
import Theme from '../../../../pgadmin/static/js/Theme';
describe('ERD ConnectionBar', ()=>{
beforeEach(()=>{
jasmineEnzyme();
});
it('<ConnectionBar /> comp', ()=>{
const connBar = mount(<Theme><ConnectionBar status={STATUS.DISCONNECTED} title="test title"/></Theme>);
expect(connBar.find('DefaultButton[data-test="btn-conn-title"]').text()).toBe('test title');
connBar.setProps({
children: <ConnectionBar status={STATUS.CONNECTING} title="test title"/>
});
expect(connBar.find('DefaultButton[data-test="btn-conn-title"]').text()).toBe('(Obtaining connection...) test title');
connBar.setProps({
children: <ConnectionBar status={STATUS.CONNECTING} title="test title" bgcolor='#000' fgcolor='#fff'/>
});
const titleEle = connBar.find('DefaultButton[data-test="btn-conn-title"]');
expect(titleEle.prop('style').backgroundColor).toBe('#000');
expect(titleEle.prop('style').color).toBe('#fff');
});
});

View File

@@ -1,4 +1,3 @@
import jasmineEnzyme from 'jasmine-enzyme';
import React from 'react';
import {mount} from 'enzyme';
import '../../helper/enzyme.helper';
@@ -6,12 +5,14 @@ import MockAdapter from 'axios-mock-adapter';
import axios from 'axios/index';
import ERDCore from 'pgadmin.tools.erd/erd_tool/ERDCore';
import * as erdModule from 'pgadmin.tools.erd/erd_module';
import * as erdModule from 'pgadmin.tools.erd/ERDModule';
import erdPref from './erd_preferences';
import BodyWidget from 'pgadmin.tools.erd/erd_tool/ui_components/BodyWidget';
import ERDTool from 'pgadmin.tools.erd/erd_tool/components/ERDTool';
import * as ERDSqlTool from 'tools/sqleditor/static/js/show_query_tool';
import { FakeLink, FakeNode, FakePort } from '../fake_item';
import Notify from '../../../../pgadmin/static/js/helpers/Notifier';
import Theme from '../../../../pgadmin/static/js/Theme';
import ModalProvider from '../../../../pgadmin/static/js/helpers/ModalProvider';
let pgAdmin = {
@@ -50,18 +51,9 @@ let pgWindow = {
pgAdmin: pgAdmin,
};
let alertify = jasmine.createSpyObj('alertify', {
'success': null,
'error': null,
'confirm': null,
'alert': {
'set': ()=>{/*This is intentional (SonarQube)*/},
},
});
let tableDialog = jasmine.createSpyObj('TableDialog', ['show']);
let otmDialog = jasmine.createSpyObj('otmDialog', ['show']);
let mtmDialog = jasmine.createSpyObj('mtmDialog', ['show']);
let tableDialog = jasmine.createSpy('TableDialog');
let otmDialog = jasmine.createSpy('otmDialog');
let mtmDialog = jasmine.createSpy('mtmDialog');
let getDialog = (dialogName)=>{
switch(dialogName) {
@@ -71,7 +63,8 @@ let getDialog = (dialogName)=>{
}
};
describe('ERD BodyWidget', ()=>{
describe('ERDTool', ()=>{
let erd = null;
let body = null;
let bodyInstance = null;
let networkMock = null;
@@ -129,36 +122,34 @@ describe('ERD BodyWidget', ()=>{
});
beforeEach(()=>{
jasmineEnzyme();
body = mount(<BodyWidget params={params} pgAdmin={pgAdmin} pgWindow={pgWindow} getDialog={getDialog} alertify={alertify}/>);
erd = mount(
<Theme>
<ModalProvider>
<ERDTool params={params} pgAdmin={pgAdmin} pgWindow={pgWindow} />
</ModalProvider>
</Theme>
);
body = erd.find('ERDTool');
bodyInstance = body.instance();
spyOn(bodyInstance, 'getDialog').and.callFake(getDialog);
});
afterAll(() => {
networkMock.restore();
if(body) {
body.unmount();
if(erd) {
erd.unmount();
}
});
it('constructor', (done)=>{
expect(body.find('ToolBar').length).toBe(1);
expect(body.find('ConnectionBar').length).toBe(1);
expect(body.find('FloatingNote').length).toBe(1);
expect(body.find('.diagram-container Loader').length).toBe(1);
expect(body.find('.diagram-container CanvasWidget').length).toBe(1);
body.instance().setState({}, ()=>{
let instance = body.instance();
bodyInstance.setState({}, ()=>{
setTimeout(()=>{
expect(body.state()).toEqual(jasmine.objectContaining({
server_version: serverVersion,
preferences: erdPref,
}));
expect(instance.diagram.getCache('colTypes')).toEqual(colTypes);
expect(instance.diagram.getCache('schemas')).toEqual(schemas);
expect(bodyInstance.diagram.getCache('colTypes')).toEqual(colTypes);
expect(bodyInstance.diagram.getCache('schemas')).toEqual(schemas);
done();
});
});
@@ -237,17 +228,6 @@ describe('ERD BodyWidget', ()=>{
});
});
it('getDialog', ()=>{
bodyInstance.getDialog('table_dialog')();
expect(tableDialog.show).toHaveBeenCalled();
bodyInstance.getDialog('onetomany_dialog')();
expect(otmDialog.show).toHaveBeenCalled();
bodyInstance.getDialog('manytomany_dialog')();
expect(mtmDialog.show).toHaveBeenCalled();
});
it('addEditTable', ()=>{
let node1 = new FakeNode({'name': 'table1', schema: 'erd1', columns: [{name: 'col1', type: 'type1', attnum: 1}]}, 'id1');
let node2 = new FakeNode({'name': 'table2', schema: 'erd2', columns: [{name: 'col2', type: 'type2', attnum: 2}]}, 'id2');
@@ -261,23 +241,23 @@ describe('ERD BodyWidget', ()=>{
spyOn(bodyInstance.diagram, 'addLink');
spyOn(bodyInstance.diagram, 'syncTableLinks');
/* New */
tableDialog.show.calls.reset();
tableDialog.calls.reset();
bodyInstance.addEditTable();
expect(tableDialog.show).toHaveBeenCalled();
expect(tableDialog).toHaveBeenCalled();
let saveCallback = tableDialog.show.calls.mostRecent().args[7];
let saveCallback = tableDialog.calls.mostRecent().args[3];
let newData = {key: 'value'};
saveCallback(newData);
expect(bodyInstance.diagram.addNode).toHaveBeenCalledWith(newData);
/* Existing */
tableDialog.show.calls.reset();
tableDialog.calls.reset();
let node = new FakeNode({name: 'table1', schema: 'erd1'});
spyOn(node, 'setData');
bodyInstance.addEditTable(node);
expect(tableDialog.show).toHaveBeenCalled();
expect(tableDialog).toHaveBeenCalled();
saveCallback = tableDialog.show.calls.mostRecent().args[7];
saveCallback = tableDialog.calls.mostRecent().args[3];
newData = {key: 'value'};
saveCallback(newData);
expect(node.setData).toHaveBeenCalledWith(newData);
@@ -435,11 +415,8 @@ describe('ERD BodyWidget', ()=>{
spyOn(bodyInstance.diagram, 'addLink');
spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
otmDialog.show.calls.reset();
bodyInstance.onOneToManyClick();
expect(otmDialog.show).toHaveBeenCalled();
let saveCallback = otmDialog.show.calls.mostRecent().args[4];
let saveCallback = otmDialog.calls.mostRecent().args[2];
let newData = {
local_table_uid: 'id1',
local_column_attnum: 1,
@@ -452,13 +429,6 @@ describe('ERD BodyWidget', ()=>{
it('onManyToManyClick', ()=>{
let node = new FakeNode({}, 'id1');
spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
mtmDialog.show.calls.reset();
bodyInstance.onManyToManyClick();
expect(mtmDialog.show).toHaveBeenCalled();
/* onSave */
let node1 = new FakeNode({'name': 'table1', schema: 'erd1', columns: [{name: 'col1', type: 'type1', attnum: 1}]}, 'id1');
let node2 = new FakeNode({'name': 'table2', schema: 'erd2', columns: [{name: 'col2', type: 'type2', attnum: 2}]}, 'id2');
let nodesDict = {
@@ -469,8 +439,13 @@ describe('ERD BodyWidget', ()=>{
spyOn(bodyInstance.diagram, 'getModel').and.returnValue({
'getNodesDict': ()=>nodesDict,
});
spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
bodyInstance.onManyToManyClick();
/* onSave */
spyOn(bodyInstance.diagram, 'addLink');
let saveCallback = mtmDialog.show.calls.mostRecent().args[4];
let saveCallback = mtmDialog.calls.mostRecent().args[2];
let newData = {
left_table_uid: 'id1',
left_table_column_attnum: 1,

View File

@@ -3,7 +3,8 @@ import React from 'react';
import {mount} from 'enzyme';
import '../../helper/enzyme.helper';
import FloatingNote from 'pgadmin.tools.erd/erd_tool/ui_components/FloatingNote';
import FloatingNote from 'pgadmin.tools.erd/erd_tool/components/FloatingNote';
import Theme from '../../../../pgadmin/static/js/Theme';
describe('ERD FloatingNote', ()=>{
@@ -24,15 +25,20 @@ describe('ERD FloatingNote', ()=>{
},
};
floatNote = mount(<FloatingNote open={false} onClose={onClose}
reference={null} noteNode={noteNode} appendTo={document.body} rows={8}/>);
floatNote = mount(
<Theme>
<FloatingNote
open={true} onClose={onClose} anchorEl={document.body} rows={8} noteNode={noteNode}
/>
</Theme>);
floatNote.find('textarea').simulate('change', {
target: {
value: 'the new note',
},
});
floatNote.find('button[data-label="OK"]').simulate('click');
floatNote.find('DefaultButton').simulate('click');
expect(noteNode.setNote).toHaveBeenCalledWith('the new note');
expect(onClose).toHaveBeenCalled();
});

View File

@@ -1,25 +0,0 @@
import jasmineEnzyme from 'jasmine-enzyme';
import React from 'react';
import {mount} from 'enzyme';
import '../../helper/enzyme.helper';
import ConnectionBar, {STATUS} from 'pgadmin.tools.erd/erd_tool/ui_components/ConnectionBar';
describe('ERD ConnectionBar', ()=>{
beforeEach(()=>{
jasmineEnzyme();
});
it('<ConnectionBar /> comp', ()=>{
let connBar = mount(<ConnectionBar statusId="conn-bar" status={STATUS.DISCONNECTED} title="test title"/>);
expect(connBar.find('.editor-title').text()).toBe('test title');
connBar.setProps({status: STATUS.CONNECTING});
expect(connBar.find('.editor-title').text()).toBe('(Obtaining connection...) test title');
connBar.setProps({bgcolor: '#000', fgcolor: '#fff'});
expect(connBar.find('.editor-title').prop('style').backgroundColor).toBe('#000');
expect(connBar.find('.editor-title').prop('style').color).toBe('#fff');
});
});

View File

@@ -1,23 +0,0 @@
import jasmineEnzyme from 'jasmine-enzyme';
import React from 'react';
import {shallow} from 'enzyme';
import '../../helper/enzyme.helper';
import Loader from 'pgadmin.tools.erd/erd_tool/ui_components/Loader';
describe('ERD Loader', ()=>{
beforeEach(()=>{
jasmineEnzyme();
});
it('<Loader /> comp', ()=>{
let loaderComp = shallow(<Loader />);
expect(loaderComp.isEmptyRender()).toBeTruthy();
loaderComp.setProps({message: 'test message'});
expect(loaderComp.find('.pg-sp-text').text()).toBe('test message');
loaderComp.setProps({autoEllipsis: true});
expect(loaderComp.find('.pg-sp-text').text()).toBe('test message...');
});
});

View File

@@ -1,76 +0,0 @@
import jasmineEnzyme from 'jasmine-enzyme';
import React from 'react';
import Tippy from '@tippyjs/react';
import {mount, shallow} from 'enzyme';
import '../../helper/enzyme.helper';
import ToolBar, {ButtonGroup, DetailsToggleButton, IconButton, Shortcut} from 'pgadmin.tools.erd/erd_tool/ui_components/ToolBar';
describe('ERD Toolbar', ()=>{
beforeEach(()=>{
jasmineEnzyme();
});
it('<Toolbar /> comp', ()=>{
let toolBar = mount(<ToolBar id="id1"><div className="test"></div></ToolBar>);
expect(toolBar.getDOMNode().id).toBe('id1');
expect(toolBar.find('.test').length).toBe(1);
});
it('<ButtonGroup /> comp', ()=>{
let btnGrp = mount(<ButtonGroup><div className="test"></div></ButtonGroup>);
expect(btnGrp.getDOMNode().className).toBe('btn-group mr-1 ');
expect(btnGrp.find('.test').length).toBe(1);
btnGrp.unmount();
btnGrp = mount(<ButtonGroup className="someclass"></ButtonGroup>);
expect(btnGrp.getDOMNode().className).toBe('btn-group mr-1 someclass');
});
it('<DetailsToggleButton /> comp', ()=>{
let toggle = shallow(<DetailsToggleButton showDetails={true} />);
let btn = toggle.find(IconButton);
expect(btn.prop('icon')).toBe('far fa-eye');
expect(btn.prop('title')).toBe('Show fewer details');
toggle.setProps({showDetails: false});
btn = toggle.find(IconButton);
expect(btn.prop('icon')).toBe('fas fa-low-vision');
expect(btn.prop('title')).toBe('Show more details');
});
it('<IconButton /> comp', ()=>{
let btn = mount(<IconButton />);
let tippy = btn.find(Tippy);
expect(tippy.length).toBe(0);
btn.setProps({title: 'test title'});
tippy = btn.find(Tippy);
expect(tippy.length).toBe(1);
expect(btn.find('button').getDOMNode().className).toBe('btn btn-sm btn-primary-icon ');
btn.setProps({icon: 'fa fa-icon'});
expect(btn.find('button .sql-icon-lg').getDOMNode().className).toBe('fa fa-icon sql-icon-lg');
});
it('<Shortcut /> comp', ()=>{
let key = {
alt: true,
control: true,
shift: false,
key: {
key_code: 65,
char: 'a',
},
};
let shortcutComp = mount(<Shortcut shortcut={key}/>);
expect(shortcutComp.find('.shortcut-key').length).toBe(3);
key.alt = false;
shortcutComp.setProps({shortcut: key});
expect(shortcutComp.find('.shortcut-key').length).toBe(2);
});
});