Improve the history UI.

This commit is contained in:
Matthew Kleiman 2017-07-20 20:50:37 +01:00 committed by Dave Page
parent 21bfcd83f4
commit e29cd8d83d
5 changed files with 93 additions and 17 deletions

View File

@ -78,6 +78,10 @@ class QueryToolJourneyTest(BaseFeatureTest):
self.page.click_tab("History") self.page.click_tab("History")
selected_history_entry = self.page.find_by_css_selector("#query_list .selected") selected_history_entry = self.page.find_by_css_selector("#query_list .selected")
self.assertIn("SELECT * FROM shoes", selected_history_entry.text) self.assertIn("SELECT * FROM shoes", selected_history_entry.text)
failed_history_detail_pane = self.page.find_by_id("query_detail")
self.assertIn("Error Message relation \"shoes\" does not exist", failed_history_detail_pane.text)
ActionChains(self.page.driver) \ ActionChains(self.page.driver) \
.send_keys(Keys.ARROW_DOWN) \ .send_keys(Keys.ARROW_DOWN) \
.perform() .perform()

View File

@ -0,0 +1,30 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2017, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////////////////
import React from 'react';
import Shapes from '../../react_shapes';
export default class HistoryErrorMessage extends React.Component {
parseErrorMessage(message) {
return message.match(/ERROR:\s*([^\n\r]*)/i)[1];
}
render() {
return (
<div className='history-error-text'>
<span>Error Message</span> {this.parseErrorMessage(this.props.historyEntry.message)}
</div>);
}
}
HistoryErrorMessage.propTypes = {
historyEntry: Shapes.historyDetail,
};

View File

@ -11,14 +11,23 @@ import React from 'react';
import HistoryDetailMetadata from './detail/history_detail_metadata'; import HistoryDetailMetadata from './detail/history_detail_metadata';
import HistoryDetailQuery from './detail/history_detail_query'; import HistoryDetailQuery from './detail/history_detail_query';
import HistoryDetailMessage from './detail/history_detail_message'; import HistoryDetailMessage from './detail/history_detail_message';
import HistoryErrorMessage from './detail/history_error_message';
import Shapes from '../react_shapes'; import Shapes from '../react_shapes';
export default class QueryHistoryDetail extends React.Component { export default class QueryHistoryDetail extends React.Component {
render() { render() {
if (!_.isUndefined(this.props.historyEntry)) { if (!_.isUndefined(this.props.historyEntry)) {
let historyErrorMessage = null;
if (!this.props.historyEntry.status) {
historyErrorMessage = <div className='error-message-block'>
<HistoryErrorMessage {...this.props} />
</div>;
}
return ( return (
<div id='query_detail' className='query-detail'> <div id='query_detail' className='query-detail'>
{historyErrorMessage}
<div className='metadata-block'> <div className='metadata-block'>
<HistoryDetailMetadata {...this.props} /> <HistoryDetailMetadata {...this.props} />
</div> </div>

View File

@ -11,7 +11,7 @@
border: 2px solid transparent; border: 2px solid transparent;
margin-left: 1px; margin-left: 1px;
.other-info{ .other-info {
@extend .text-13; @extend .text-13;
@extend .font-gray-4; @extend .font-gray-4;
font-family: monospace; font-family: monospace;
@ -47,7 +47,7 @@
font-weight: bold; font-weight: bold;
border: 2px solid; border: 2px solid;
.other-info{ .other-info {
@extend .font-primary-blue; @extend .font-primary-blue;
font-weight: bold; font-weight: bold;
} }
@ -62,19 +62,37 @@
.query-detail { .query-detail {
width: 100%; width: 100%;
padding-top: 10px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.error-message-block {
@extend .bg-red-1;
flex: 0.3;
padding-left: 20px;
.history-error-text {
@extend .text-12;
padding: 7px 0;
span {
@extend .font-red-3;
font-weight: 500;
margin-right: 8px;
}
}
}
.metadata-block { .metadata-block {
flex: 1; flex: 0.4;
padding: 0 10px; padding: 10px 20px;
.metadata { .metadata {
display: flex; display: flex;
flex-wrap: wrap;
.item { .item {
flex: 1; flex: 1;
min-width: 130px;
.value { .value {
@extend .text-14; @extend .text-14;
@ -106,7 +124,7 @@
.message-block { .message-block {
flex: 2; flex: 2;
display: flex; display: flex;
padding-left: 10px; padding: 0 20px;
.message { .message {
flex: 2 2 0%; flex: 2 2 0%;

View File

@ -49,6 +49,7 @@ describe('QueryHistory', () => {
expect(foundChildren.length).toBe(0); expect(foundChildren.length).toBe(0);
done(); done();
}); });
it('nothing is displayed on right panel', (done) => { it('nothing is displayed on right panel', (done) => {
let foundChildren = historyWrapper.find(QueryHistoryDetail); let foundChildren = historyWrapper.find(QueryHistoryDetail);
expect(foundChildren.length).toBe(1); expect(foundChildren.length).toBe(1);
@ -68,7 +69,7 @@ describe('QueryHistory', () => {
status: true, status: true,
row_affected: 12345, row_affected: 12345,
total_time: '14 msec', total_time: '14 msec',
message: 'message from first sql query', message: 'something important ERROR: message from first sql query',
}, { }, {
query: 'second sql statement', query: 'second sql statement',
start_time: new Date(2016, 11, 11, 1, 33, 5, 99), start_time: new Date(2016, 11, 11, 1, 33, 5, 99),
@ -191,27 +192,41 @@ describe('QueryHistory', () => {
done(); done();
}, 1000); }, 1000);
}); });
describe('when the query failed', () => {
let failedEntry;
beforeEach(() => {
failedEntry = foundChildren.at(1);
failedEntry.simulate('click');
});
it('displays the error message on top of the details pane', () => {
expect(queryDetail.at(0).text()).toContain('Error Message message from second sql query');
});
});
}); });
describe('when the older query is clicked on', () => { describe('when the older query is clicked on', () => {
let firstEntry, secondEntry;
beforeEach(() => { beforeEach(() => {
foundChildren.at(1).simulate('click'); firstEntry = foundChildren.at(0);
secondEntry = foundChildren.at(1);
secondEntry.simulate('click');
}); });
it('displays the query in the right pane', () => { it('displays the query in the right pane', () => {
expect(queryDetail.at(0).text()).toContain('second sql statement'); expect(queryDetail.at(0).text()).toContain('second sql statement');
}); });
it('renders the most recent query as selected in the left pane', () => { it('deselects the first history entry', () => {
expect(foundChildren.at(0).nodes.length).toBe(1); expect(firstEntry.nodes.length).toBe(1);
expect(foundChildren.at(0).hasClass('selected')).toBeFalsy(); expect(firstEntry.hasClass('selected')).toBeFalsy();
expect(foundChildren.at(0).hasClass('error')).toBeFalsy();
}); });
it('renders the older query as selected in the left pane', () => { it('selects the second history entry', () => {
expect(foundChildren.at(1).nodes.length).toBe(1); expect(secondEntry.nodes.length).toBe(1);
expect(foundChildren.at(1).hasClass('selected')).toBeTruthy(); expect(secondEntry.hasClass('selected')).toBeTruthy();
expect(foundChildren.at(1).hasClass('error')).toBeTruthy();
}); });
}); });
@ -223,7 +238,7 @@ describe('QueryHistory', () => {
status: false, status: false,
row_affected: 5, row_affected: 5,
total_time: '26 msec', total_time: '26 msec',
message: 'third sql message', message: 'pretext ERROR: third sql message',
}); });
foundChildren = historyWrapper.find(QueryHistoryEntry); foundChildren = historyWrapper.find(QueryHistoryEntry);