diff --git a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx index 8b73f386cb..2176bf2aa4 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx @@ -133,15 +133,18 @@ export default class TraceSpanView extends Component { title: 'Service Name', dataIndex: 'process.serviceName', width: '25%', + sorter: (a, b) => a.process.serviceName.localeCompare(b.process.serviceName), }, { title: 'Operation', dataIndex: 'operationName', width: '25%', + sorter: (a, b) => a.operationName.localeCompare(b.operationName), }, { title: 'ID', dataIndex: 'spanID', + sorter: (a, b) => a.spanID.localeCompare(b.spanID), render: (text: any, record: Span) => { return ( ', () => { - let wrapper; - let props; - - beforeEach(() => { - props = { - color: '', - colorToPercent: 'rgb(248,248,248)', - columnsArray: [ - { title: 'Name', attribute: 'name', suffix: '', isDecimal: false }, - { title: 'Count', attribute: 'count', suffix: '', isDecimal: false }, - { title: 'Total', attribute: 'total', suffix: 'ms', isDecimal: true }, - { title: 'Avg', attribute: 'avg', suffix: 'ms', isDecimal: true }, - { title: 'Min', attribute: 'min', suffix: 'ms', isDecimal: true }, - { title: 'Max', attribute: 'max', suffix: 'ms', isDecimal: true }, - { title: 'ST Total', attribute: 'selfTotal', suffix: 'ms', isDecimal: true }, - { title: 'ST Avg', attribute: 'selfAvg', suffix: 'ms', isDecimal: true }, - { title: 'ST Min', attribute: 'selfMin', suffix: 'ms', isDecimal: true }, - { title: 'ST Max', attribute: 'selfMax', suffix: 'ms', isDecimal: true }, - { title: 'ST in Duration', attribute: 'percent', suffix: '%', isDecimal: true }, - ], - name: 'GET /owners/5', - searchColor: 'rgb(248,248,248)', - valueNameSelector2: 'Operation Name', - togglePopup: () => {}, - values: [3, 27.27, 9.09, 2.56, 13.73, 8.69, 2.9, 0.88, 5.06, 31.88], - }; - wrapper = shallow(); - }); - - it('does not explode', () => { - expect(wrapper).toBeDefined(); - expect(wrapper.find('.DetailTableData--tr').length).toBe(1); - expect(wrapper.find('.DetailTableData--td').length).toBe(11); - }); - - it('renders TableOverviewHeadTag', () => { - const firstRowColumns = wrapper.find('.DetailTableData--td').map(column => column.text()); - expect(firstRowColumns.length).toBe(11); - - expect(firstRowColumns[0]).toBe('GET /owners/5'); - expect(firstRowColumns[1]).toBe('3'); - expect(firstRowColumns[2]).toBe('27.27ms'); - expect(firstRowColumns[3]).toBe('9.09ms'); - expect(firstRowColumns[4]).toBe('2.56ms'); - expect(firstRowColumns[5]).toBe('13.73ms'); - expect(firstRowColumns[6]).toBe('8.69ms'); - expect(firstRowColumns[7]).toBe('2.90ms'); - expect(firstRowColumns[8]).toBe('0.88ms'); - expect(firstRowColumns[9]).toBe('5.06ms'); - expect(firstRowColumns[10]).toBe('31.88%'); - expect(wrapper.find('.DetailTableData--tr').prop('style')).toEqual({ - background: 'rgb(248,248,248)', - borderColor: 'rgb(248,248,248)', - }); - }); -}); diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/DetailTableData.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/DetailTableData.tsx deleted file mode 100644 index 8b4096dd03..0000000000 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/DetailTableData.tsx +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) 2020 The Jaeger Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import * as _ from 'lodash'; -import React, { Component } from 'react'; -import './DetailTableData.css'; -import { ITableValues, IColumnValue } from './types'; - -type Props = { - type: string; - name: string; - searchColor: string; - values: ITableValues[]; - columnsArray: IColumnValue[]; - color: string; - togglePopup: (name: string) => void; - valueNameSelector2: string | null; - colorToPercent: string; -}; - -type State = { - element: any; -}; - -/** - * Used to render the detail column. - */ -export default class DetailTableData extends Component { - constructor(props: Readonly) { - super(props); - const element = this.props.values.map(item => { - return { uid: _.uniqueId('id'), value: item }; - }); - this.state = { element }; - } - - render() { - const styleOption1 = { - background: this.props.colorToPercent, - borderColor: this.props.colorToPercent, - color: 'rgb(153,153,153)', - fontStyle: 'italic', - }; - - const styleOption2 = { - background: this.props.colorToPercent, - borderColor: this.props.colorToPercent, - }; - - const styleOption3 = { - background: this.props.searchColor, - borderColor: this.props.searchColor, - }; - const others = 'undefined'; - let styleCondition; - if (this.props.type === others) { - styleCondition = styleOption1; - } else if (this.props.searchColor === 'rgb(248,248,248)') { - styleCondition = styleOption2; - } else { - styleCondition = styleOption3; - } - const labelStyle1 = { borderColor: this.props.color }; - const labelStyle2 = { borderColor: this.props.color, marginLeft: '12px' }; - let labelCondition; - if (this.props.valueNameSelector2 === 'Service Name') { - labelCondition = labelStyle2; - } else { - labelCondition = labelStyle1; - } - const onClickOption = - this.props.valueNameSelector2 === 'sql' && this.props.type !== others - ? () => this.props.togglePopup(this.props.name) - : undefined; - return ( - - - - - - - {this.state.element.map((element: any, index: number) => ( - - {this.props.columnsArray[index + 1].isDecimal ? Number(element.value).toFixed(2) : element.value} - {this.props.columnsArray[index + 1].suffix} - - ))} - - ); - } -} diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/HeaderTable.css b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/HeaderTable.css deleted file mode 100644 index 9024fc0dd6..0000000000 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/HeaderTable.css +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright (c) 2020 The Jaeger Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at -http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -.HeaderTable--sortButton { - border: none; - background: transparent; -} - -.HeaderTable--th { - background: rgb(236, 236, 236); - border: 1px solid rgb(204, 204, 204); - color: rgb(35, 35, 35); - font-weight: 15; - line-height: 2em; - padding-left: 1em; -} - -.HeaderTable--sortButton { - display: flexbox; - flex-direction: row; - justify-content: space-around; -} diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/HeaderTable.test.js b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/HeaderTable.test.js deleted file mode 100644 index 82c3fdc0d9..0000000000 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/HeaderTable.test.js +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2020 The Jaeger Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import React from 'react'; -import { shallow } from 'enzyme'; -import HeaderTable from './HeaderTable'; - -describe('', () => { - let wrapper; - let props; - - beforeEach(() => { - props = { - element: { title: 'Name' }, - sortIndex: 1, - index: 1, - sortClick: () => {}, - sortAsc: false, - }; - wrapper = shallow(); - }); - - it('does not explode', () => { - expect(wrapper).toBeDefined(); - expect(wrapper.find('.HeaderTable--th').length).toBe(1); - expect(wrapper.find('.HeaderTable--sortButton').length).toBe(1); - }); -}); diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/HeaderTable.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/HeaderTable.tsx deleted file mode 100644 index 71bc963d0a..0000000000 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/HeaderTable.tsx +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2020 The Jaeger Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import React from 'react'; -import { Icon } from 'antd'; -import './HeaderTable.css'; - -type Props = { - element: any; - key: string; - sortIndex: number; - index: number; - sortClick: (index: number) => void; - sortAsc: boolean; -}; - -export default function HeaderTable(props: Props) { - // const thStyle = { width: Math.round(window.innerWidth * 0.2) }; - const iconStyle = { opacity: props.sortIndex === props.index ? 1.0 : 0.2 }; - const iconType = props.sortAsc && props.sortIndex === props.index ? 'up' : 'down'; - return ( - - {props.element.title} - - - ); -} diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/MainTableData.css b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/MainTableData.css deleted file mode 100644 index 411fbd797f..0000000000 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/MainTableData.css +++ /dev/null @@ -1,33 +0,0 @@ -/* -Copyright (c) 2020 The Jaeger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -.MainTableData--td { - border-left: 1px solid rgb(204, 204, 204); - border-right: 1px solid rgb(204, 204, 204); - padding-left: 1em; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 25em; -} - -.MainTableData--tr { - border-top: 1px solid rgb(255, 255, 255); -} - -.MainTableData--tr:hover { - background: rgb(250, 250, 250); - color: rgb(0, 0, 0); -} diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/MainTableData.test.js b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/MainTableData.test.js deleted file mode 100644 index c3a5110994..0000000000 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/MainTableData.test.js +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 2020 The Jaeger Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import React from 'react'; -import { shallow } from 'enzyme'; - -import MainTableData from './MainTableData'; - -describe('', () => { - let wrapper; - let props; - - beforeEach(() => { - props = { - color: '#17B8BE', - columnsArray: [ - { title: 'Name', attribute: 'name', suffix: '', isDecimal: false }, - { title: 'Count', attribute: 'count', suffix: '', isDecimal: false }, - { title: 'Total', attribute: 'total', suffix: 'ms', isDecimal: true }, - { title: 'Avg', attribute: 'avg', suffix: 'ms', isDecimal: true }, - { title: 'Min', attribute: 'min', suffix: 'ms', isDecimal: true }, - { title: 'Max', attribute: 'max', suffix: 'ms', isDecimal: true }, - { title: 'ST Total', attribute: 'selfTotal', suffix: 'ms', isDecimal: true }, - { title: 'ST Avg', attribute: 'selfAvg', suffix: 'ms', isDecimal: true }, - { title: 'ST Min', attribute: 'selfMin', suffix: 'ms', isDecimal: true }, - { title: 'ST Max', attribute: 'selfMax', suffix: 'ms', isDecimal: true }, - { title: 'ST in Duration', attribute: 'percent', suffix: '%', isDecimal: true }, - ], - valueNameSelector1: 'Service Name', - valueNameSelector2: null, - name: 'api-gateway', - searchColor: 'transparent', - togglePopup: '', - values: [5, 46.06, 9.21, 2.56, 15.43, 11.21, 2.24, 0.82, 5.06, 24.35], - }; - wrapper = shallow(); - }); - - it('does not explode', () => { - expect(wrapper).toBeDefined(); - expect(wrapper.find('.MainTableData--tr').length).toBe(1); - expect(wrapper.find('.MainTableData--td').length).toBe(11); - }); - - it('renders TableOverviewHeadTag', () => { - expect(wrapper.find('.MainTableData--label').text()).toBe('api-gateway'); - - const firstRowColumns = wrapper.find('.MainTableData--td').map(column => column.text()); - expect(firstRowColumns.length).toBe(11); - - expect(firstRowColumns[0]).toBe('api-gateway'); - expect(firstRowColumns[1]).toBe(' 5'); - expect(firstRowColumns[2]).toBe(' 46.06ms'); - expect(firstRowColumns[3]).toBe(' 9.21ms'); - expect(firstRowColumns[4]).toBe(' 2.56ms'); - expect(firstRowColumns[5]).toBe(' 15.43ms'); - expect(firstRowColumns[6]).toBe(' 11.21ms'); - expect(firstRowColumns[7]).toBe(' 2.24ms'); - expect(firstRowColumns[8]).toBe(' 0.82ms'); - expect(firstRowColumns[9]).toBe(' 5.06ms'); - expect(firstRowColumns[10]).toBe(' 24.35%'); - }); -}); diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/MainTableData.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/MainTableData.tsx deleted file mode 100644 index 460377e67d..0000000000 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/MainTableData.tsx +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) 2020 The Jaeger Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import * as _ from 'lodash'; -import React, { Component } from 'react'; -import './MainTableData.css'; -import { ITableValues, IColumnValue } from './types'; - -type Props = { - type: string; - name: string; - searchColor: string; - values: ITableValues[]; - columnsArray: IColumnValue[]; - togglePopup: any; - valueNameSelector1: string; - valueNameSelector2: string | null; - color: string; - clickColumn: (name: string) => void; - colorToPercent: string; -}; - -type State = { - element: any; -}; - -/** - * Used to render the main column. - */ -export default class MainTableData extends Component { - constructor(props: Readonly) { - super(props); - const element = this.props.values.map(item => { - return { uid: _.uniqueId('id'), value: item }; - }); - - this.state = { element }; - } - - render() { - const styleOption1 = { - background: this.props.colorToPercent, - borderColor: this.props.colorToPercent, - cursor: 'default', - }; - - const styleOption2 = { - background: this.props.searchColor, - borderColor: this.props.searchColor, - cursor: 'default', - }; - - const labelOption1 = { - color: 'rgb(153,153,153)', - fontStyle: 'italic', - }; - - const labelOption2 = { - borderLeft: '4px solid transparent', - paddingLeft: '0.6em', - borderColor: this.props.color, - }; - - const others = 'undefined'; - - let styleCondition; - if (this.props.type === others) { - if (this.props.valueNameSelector2 !== null && this.props.type !== 'undefined') { - styleOption1.cursor = 'pointer'; - } - styleCondition = styleOption1; - } else if (this.props.searchColor === 'transparent') { - if (this.props.valueNameSelector2 !== null) { - styleOption1.cursor = 'pointer'; - } - styleCondition = styleOption1; - } else { - if (this.props.valueNameSelector2 !== null) { - styleOption1.cursor = 'pointer'; - } - styleCondition = styleOption2; - } - - let labelCondition; - if (this.props.color !== '') { - labelCondition = labelOption2; - } else if (this.props.type === 'undefined') { - labelCondition = labelOption1; - } else { - labelCondition = undefined; - } - - const onClickOption = - this.props.valueNameSelector1 === 'sql' && this.props.type !== others - ? () => this.props.togglePopup(this.props.name) - : undefined; - return ( - this.props.clickColumn(this.props.name)} - style={styleCondition} - > - - - - - - - {this.state.element.map((element: any, index: number) => ( - - {' '} - {this.props.columnsArray[index + 1].isDecimal ? element.value.toFixed(2) : element.value} - {this.props.columnsArray[index + 1].suffix} - - ))} - - ); - } -} diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/generateDropdownValue.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/generateDropdownValue.tsx index dd2c4db566..21e8158925 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/generateDropdownValue.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/generateDropdownValue.tsx @@ -29,7 +29,7 @@ function getValueTagIsPicked(tableValue: ITableSpan[], trace: Trace, nameSelecto // add all Spans with this tag key for (let i = 0; i < tableValue.length; i++) { - if (tableValue[i].type !== 'undefined') { + if (tableValue[i].hasSubgroupValue) { for (let j = 0; j < allSpans.length; j++) { for (let l = 0; l < allSpans[j].tags.length; l++) { if (nameSelectorTitle === allSpans[j].tags[l].key) { diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/index.css b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/index.css index 4f7942f67e..b9fd8af748 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/index.css +++ b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/index.css @@ -22,10 +22,17 @@ limitations under the License. margin-bottom: 0%; } -.DetailTraceTableTbody--TraceStatistics { - border-bottom: 1px solid rgb(204, 204, 204); +.undefClass--TraceStatistics { + color: rgb(153, 153, 153); + font-style: italic; + cursor: default; } -.test1893 { - width: 99.999%; +.MainTableData--TraceStatistics { + border-top: 1px solid rgb(255, 255, 255); +} + +.MainTableData--TraceStatistics:hover { + background: rgb(250, 250, 250); + color: rgb(0, 0, 0); } diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/index.test.js b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/index.test.js index 56582c07dc..cd49d18a8a 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/index.test.js +++ b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/index.test.js @@ -15,19 +15,14 @@ import React from 'react'; import { mount } from 'enzyme'; import TraceStatistics from './index'; -import MainTableData from './MainTableData'; -import DetailTableData from './DetailTableData'; -import HeaderTable from './HeaderTable'; import TraceStatisticsHeader from './TraceStatisticsHeader'; import PopupSql from './PopupSql'; import transformTraceData from '../../../model/transform-trace-data'; import { getColumnValues, getColumnValuesSecondDropdown } from './tableValues'; const testTrace = require('./tableValuesTestTrace/testTrace.json'); -const secondTestTrace = require('./tableValuesTestTrace/spanLongerAsParent.json'); const transformedTrace = transformTraceData(testTrace); -const secondTransformedTrace = transformTraceData(secondTestTrace); describe('', () => { let wrapper; @@ -48,12 +43,9 @@ describe('', () => { }); it('renders Trace Tag Overview', () => { - expect(wrapper.find(HeaderTable).length).toBe(11); expect(wrapper.find(TraceStatisticsHeader).length).toBe(1); expect(wrapper.state('valueNameSelector1')).toBe('Service Name'); expect(wrapper.state('valueNameSelector2')).toBe(null); - expect(wrapper.find(MainTableData).length).toBe(2); - expect(wrapper.find(DetailTableData).length).toBe(0); expect(wrapper.find(PopupSql).length).toBe(0); }); @@ -86,29 +78,6 @@ describe('', () => { expect(tableValue[6].parentElement).toBe('service1'); }); - it('check sortTableWithOthers', () => { - const tableValue = getColumnValues('database', secondTransformedTrace); - const instance = wrapper.instance(); - let testArray = instance.sortTableWithOthers(tableValue, 1, false); - - expect(tableValue[0].count).toBe(2); - expect(tableValue[0].type).toBe('defined'); - expect(testArray[0].count).toBe(2); - expect(testArray[0].type).toBe('defined'); - - expect(tableValue[1].count).toBe(6); - expect(tableValue[1].type).toBe('undefined'); - expect(testArray[1].count).toBe(6); - expect(testArray[1].type).toBe('undefined'); - - testArray = instance.sortTableWithOthers(tableValue, 1, true); - - expect(testArray[0].count).toBe(2); - expect(testArray[0].type).toBe('defined'); - expect(testArray[1].count).toBe(6); - expect(testArray[1].type).toBe('undefined'); - }); - it('check togglePopup', () => { const instance = wrapper.instance(); instance.togglePopup('select *'); @@ -120,39 +89,4 @@ describe('', () => { expect(instance.state.popupContent).toBe('select *'); expect(instance.state.showPopup).toBe(false); }); - - it('check sortClick', () => { - const instance = wrapper.instance(); - expect(instance.state.tableValue[0].count).toBe(8); - instance.sortClick(1); - expect(instance.state.tableValue[0].count).toBe(3); - - expect(instance.state.tableValue[0].total).toBe(238); - instance.sortClick(2); - expect(instance.state.tableValue[0].total).toBe(573); - }); - - it('check clickColumn', () => { - let tableValue = getColumnValues('Service Name', transformedTrace); - tableValue = getColumnValuesSecondDropdown( - tableValue, - 'Service Name', - 'Operation Name', - transformedTrace - ); - const instance = wrapper.instance(); - instance.handler(tableValue, tableValue, 'Service Name', 'Operation Name'); - - expect(instance.state.tableValue.length).toBe(11); - expect(instance.state.wholeTable.length).toBe(11); - instance.clickColumn('service1'); - expect(instance.state.tableValue.length).toBe(5); - expect(instance.state.wholeTable.length).toBe(11); - instance.clickColumn('op1'); - expect(instance.state.tableValue.length).toBe(5); - expect(instance.state.wholeTable.length).toBe(11); - instance.clickColumn('service1'); - expect(instance.state.tableValue.length).toBe(11); - expect(instance.state.wholeTable.length).toBe(11); - }); }); diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/index.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/index.tsx index c2fda7093c..d055b5548b 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/index.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/index.tsx @@ -12,16 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import * as _ from 'lodash'; import React, { Component } from 'react'; import './index.css'; +import { Table } from 'antd'; +import { ColumnProps, CompareFn } from 'antd/es/table'; import { Trace } from '../../../types/trace'; -import HeaderTable from './HeaderTable'; -import MainTableData from './MainTableData'; -import DetailTableData from './DetailTableData'; import TraceStatisticsHeader from './TraceStatisticsHeader'; import { ITableSpan } from './types'; -import sortTable from './sortTable'; import { TNil } from '../../../types'; import PopupSQL from './PopupSql'; @@ -47,67 +44,56 @@ const columnsArray: any[] = [ title: 'Name', attribute: 'name', suffix: '', - isDecimal: false, }, { title: 'Count', attribute: 'count', suffix: '', - isDecimal: false, }, { title: 'Total', attribute: 'total', suffix: 'ms', - isDecimal: true, }, { title: 'Avg', attribute: 'avg', suffix: 'ms', - isDecimal: true, }, { title: 'Min', attribute: 'min', suffix: 'ms', - isDecimal: true, }, { title: 'Max', attribute: 'max', suffix: 'ms', - isDecimal: true, }, { title: 'ST Total', attribute: 'selfTotal', suffix: 'ms', - isDecimal: true, }, { title: 'ST Avg', attribute: 'selfAvg', suffix: 'ms', - isDecimal: true, }, { title: 'ST Min', attribute: 'selfMin', suffix: 'ms', - isDecimal: true, }, { title: 'ST Max', attribute: 'selfMax', suffix: 'ms', - isDecimal: true, }, { title: 'ST in Duration', attribute: 'percent', suffix: '%', - isDecimal: true, }, ]; @@ -130,9 +116,7 @@ export default class TraceStatistics extends Component { }; this.handler = this.handler.bind(this); - this.sortClick = this.sortClick.bind(this); this.togglePopup = this.togglePopup.bind(this); - this.clickColumn = this.clickColumn.bind(this); this.searchInTable(this.props.uiFindVertexKeys!, this.state.tableValue, this.props.uiFind); } @@ -170,11 +154,7 @@ export default class TraceStatistics extends Component { this.setState(prevState => { return { ...prevState, - tableValue: this.searchInTable( - this.props.uiFindVertexKeys!, - this.sortTableWithOthers(tableValue, 1, false), - this.props.uiFind - ), + tableValue: this.searchInTable(this.props.uiFindVertexKeys!, tableValue, this.props.uiFind), sortIndex: 1, sortAsc: false, valueNameSelector1, @@ -185,68 +165,7 @@ export default class TraceStatistics extends Component { } /** - * Searches for the others of the share and sorts afterwards. - */ - sortTableWithOthers = (tableValue: ITableSpan[], sortIndex: number, sortAsc: boolean) => { - let rememberIndexNoDetail = -1; - let rememberIndex = -1; - let othersInDetail = false; - let sortArray = []; - const sortArray2 = []; - let i; - - for (i = 0; i < tableValue.length; i++) { - if (tableValue[i].type !== 'undefined') { - sortArray.push(tableValue[i]); - } else if (!tableValue[i].isDetail) { - rememberIndexNoDetail = i; - } else { - othersInDetail = true; - } - } - sortArray = sortTable(sortArray, columnsArray[sortIndex].attribute, sortAsc); - if (rememberIndexNoDetail !== -1) { - sortArray.push(tableValue[rememberIndexNoDetail]); - } - - if (!othersInDetail) { - return sortArray; - } - - let parentElements = []; - for (i = 0; i < tableValue.length; i++) { - if (!tableValue[i].isDetail) { - parentElements.push(tableValue[i]); - } - } - parentElements = sortTable(parentElements, columnsArray[sortIndex].attribute, sortAsc); - for (i = 0; i < parentElements.length; i++) { - sortArray2.push(parentElements[i]); - let tempArray = []; - for (let j = 0; j < tableValue.length; j++) { - if (parentElements[i].name === tableValue[j].parentElement && tableValue[j].type !== 'undefined') { - tempArray.push(tableValue[j]); - } else if ( - parentElements[i].name === tableValue[j].parentElement && - tableValue[j].type === 'undefined' - ) { - rememberIndex = j; - } - } - tempArray = sortTable(tempArray, columnsArray[sortIndex].attribute, sortAsc); - if (rememberIndex !== -1) { - tempArray.push(tableValue[rememberIndex]); - rememberIndex = -1; - } - for (let j = 0; j < tempArray.length; j++) { - sortArray2.push(tempArray[j]); - } - } - return sortArray2; - }; - - /** - * Opern the popup button. + * Open the popup button. * @param popupContent */ togglePopup(popupContent: string) { @@ -260,71 +179,6 @@ export default class TraceStatistics extends Component { }); } - /** - * Change the sortButton an calls the sort function. - * @param index the index of the clicked column - */ - sortClick(index: number) { - const { tableValue, sortIndex, sortAsc } = this.state; - if (sortIndex !== index) { - this.setState(prevState => { - return { - ...prevState, - sortIndex: index, - sortAsc: false, - tableValue: this.sortTableWithOthers(tableValue, index, false), - }; - }); - } else { - this.setState(prevState => { - return { - ...prevState, - sortAsc: !sortAsc, - tableValue: this.sortTableWithOthers(tableValue, index, !sortAsc), - }; - }); - } - } - - /** - * Hides the child at the first click. - */ - clickColumn(selectedSpan: string) { - if (this.state.valueNameSelector2 !== null) { - let add = true; - const actualTable = this.state.tableValue; - let newTable = []; - for (let i = 0; i < actualTable.length; i++) { - if (actualTable[i].parentElement === selectedSpan) { - add = false; - } - if (actualTable[i].parentElement !== selectedSpan) { - newTable.push(actualTable[i]); - } - } - if (add) { - newTable = []; - for (let i = 0; i < actualTable.length; i++) { - if (actualTable[i].name !== selectedSpan) { - newTable.push(actualTable[i]); - } else { - newTable.push(actualTable[i]); - for (let j = 0; j < this.state.wholeTable.length; j++) { - if (this.state.wholeTable[j].parentElement === selectedSpan) { - newTable.push(this.state.wholeTable[j]); - } - } - } - } - newTable = this.searchInTable(this.props.uiFindVertexKeys!, newTable, this.props.uiFind); - newTable = this.sortTableWithOthers(newTable, this.state.sortIndex, this.state.sortAsc); - } - this.setState({ - tableValue: newTable, - }); - } - } - /** * Colors found entries in the table. * @param uiFindVertexKeys Set of found spans @@ -339,9 +193,9 @@ export default class TraceStatistics extends Component { const yellowSearchCollor = 'rgb(255,243,215)'; const defaultGrayCollor = 'rgb(248,248,248)'; for (let i = 0; i < allTableSpansChange.length; i++) { - if (!allTableSpansChange[i].isDetail && allTableSpansChange[i].type !== 'undefined') { + if (!allTableSpansChange[i].isDetail && allTableSpansChange[i].hasSubgroupValue) { allTableSpansChange[i].searchColor = 'transparent'; - } else if (allTableSpansChange[i].type !== 'undefined') { + } else if (allTableSpansChange[i].hasSubgroupValue) { allTableSpansChange[i].searchColor = defaultGrayCollor; } else { allTableSpansChange[i].searchColor = defaultGrayCollor; @@ -392,79 +246,87 @@ export default class TraceStatistics extends Component { return allTableSpansChange; }; - renderTableData() { - return this.state.tableValue.map(oneSpan => { - const { - count, - total, - avg, - min, - max, - selfTotal, - selfAvg, - selfMin, - selfMax, - percent, - color, - searchColor, - colorToPercent, - } = oneSpan; - const values: any[] = [count, total, avg, min, max, selfTotal, selfAvg, selfMin, selfMax, percent]; - const uid = _.uniqueId('id'); - if (!oneSpan.isDetail) { - return ( - - ); - } - return ( - - ); - }); - } + render() { + const onClickOption = (hasSubgroupValue: boolean, name: string) => { + if (this.state.valueNameSelector1 === 'sql.query' && hasSubgroupValue) this.togglePopup(name); + }; - renderTableHead() { - const { sortAsc, sortIndex } = this.state; - return ( - - {columnsArray.map((element: any, index: number) => ( - - ))} - - ); - } + const sorterFunction = (field: T): CompareFn => { + const sort = (a: ITableSpan, b: ITableSpan) => { + if (!a.hasSubgroupValue) { + return 0; + } + if (!b.hasSubgroupValue) { + return -1; + } + if (field === 'name') { + return (a[field] as string).localeCompare(b[field] as string); + } + return (a[field] as number) - (b[field] as number); + }; + return sort; + }; - render() { + const onCellFunction = (record: ITableSpan) => { + const backgroundColor = + this.props.uiFind && record.searchColor !== 'transparent' + ? record.searchColor + : record.colorToPercent; + return { + style: { background: backgroundColor, borderColor: backgroundColor }, + }; + }; + + const columns: ColumnProps[] = columnsArray.map(val => { + const renderFunction = (cell: string, row: ITableSpan) => { + if (val.attribute === 'name') + return ( + onClickOption(row.hasSubgroupValue, row.name)} + style={{ + borderLeft: `4px solid ${row.color || `transparent`}`, + padding: '7px 0px 7px 10px', + cursor: 'default', + }} + > + {cell} + + ); + return `${cell}${val.suffix}`; + }; + const ele = { + title: val.title, + dataIndex: val.attribute, + sorter: sorterFunction(val.attribute), + render: renderFunction, + onCell: onCellFunction, + }; + return val.attribute === 'count' ? { ...ele, defaultSortOrder: 'ascend' } : ele; + }); + /** + * Pre-process the table data into groups and sub-groups + */ + const groupAndSubgroupSpanData = (tableValue: ITableSpan[]): ITableSpan[] => { + const withDetail: ITableSpan[] = tableValue.filter((val: ITableSpan) => val.isDetail); + const withoutDetail: ITableSpan[] = tableValue.filter((val: ITableSpan) => !val.isDetail); + for (let i = 0; i < withoutDetail.length; i++) { + let newArr = withDetail.filter(value => value.parentElement === withoutDetail[i].name); + newArr = newArr.map((value, index) => { + const _key = { + key: `${i}-${index}`, + }; + const value2 = { ...value, ..._key }; + return value2; + }); + const child = { + key: i.toString(), + children: newArr, + }; + withoutDetail[i] = { ...withoutDetail[i], ...child }; + } + return withoutDetail; + }; + const groupedAndSubgroupedSpanData: ITableSpan[] = groupAndSubgroupSpanData(this.state.tableValue); return (

Trace Statistics

@@ -479,12 +341,23 @@ export default class TraceStatistics extends Component { {this.state.showPopup ? ( ) : null} - - - {this.renderTableHead()} - {this.renderTableData()} - -
+ + !row.hasSubgroupValue ? 'undefClass--TraceStatistics' : 'MainTableData--TraceStatistics' + } + key={groupedAndSubgroupedSpanData.length} + defaultExpandAllRows + sortDirections={['ascend', 'descend', 'ascend']} + /> ); } diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/sortTable.test.js b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/sortTable.test.js deleted file mode 100644 index d450b25eeb..0000000000 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/sortTable.test.js +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2020 The Jaeger Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import sortTable from './sortTable'; - -import transformTraceData from '../../../model/transform-trace-data'; -import { getColumnValues, getColumnValuesSecondDropdown } from './tableValues'; - -const testTrace = require('./tableValuesTestTrace/testTrace.json'); - -const transformedTrace = transformTraceData(testTrace); - -describe('sortTable', () => { - it('check sortTable with sortAsc false', () => { - let sortArray = getColumnValues('Service Name', transformedTrace); - - sortArray = sortTable(sortArray, 'count', false); - - expect(sortArray[0].count).toBe(8); - expect(sortArray[1].count).toBe(3); - }); - - it('check sortTable with sortAsc true', () => { - let sortArray = getColumnValues('Service Name', transformedTrace); - - sortArray = sortTable(sortArray, 'count', true); - - expect(sortArray[0].count).toBe(3); - expect(sortArray[1].count).toBe(8); - }); - - it('check sortTable with detail and sortAsc false', () => { - let sortArray = getColumnValuesSecondDropdown( - getColumnValues('Service Name', transformedTrace), - 'Service Name', - 'Operation Name', - transformedTrace - ); - - sortArray = sortTable(sortArray, 'count', false); - - expect(sortArray[0].isDetail).toBe(false); - expect(sortArray[0].count).toBe(8); - expect(sortArray[1].isDetail).toBe(true); - expect(sortArray[1].count).toBe(2); - expect(sortArray[2].isDetail).toBe(true); - expect(sortArray[2].count).toBe(2); - }); - - it('check sortTable with detail and sortAsc true', () => { - let sortArray = getColumnValuesSecondDropdown( - getColumnValues('Service Name', transformedTrace), - 'Service Name', - 'Operation Name', - transformedTrace - ); - - sortArray = sortTable(sortArray, 'count', true); - - expect(sortArray[0].isDetail).toBe(false); - expect(sortArray[0].count).toBe(3); - expect(sortArray[1].isDetail).toBe(true); - expect(sortArray[1].count).toBe(1); - expect(sortArray[2].isDetail).toBe(true); - expect(sortArray[2].count).toBe(1); - }); -}); diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/sortTable.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/sortTable.tsx deleted file mode 100644 index e24d286115..0000000000 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/sortTable.tsx +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) 2020 The Jaeger Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import * as _ from 'lodash'; -import { ITableSpan } from './types'; - -/** - * Sort - * @param array input whitch is sorted - * @param key attribut which is used for sorting - * @param sortAsc Specifies the direction in which the sort is to take place. - */ -function sortByKey(array: ITableSpan[], key: string, sortAsc: boolean) { - return array.sort(function calc(a, b) { - const x = (a as any)[key]; - const y = (b as any)[key]; - if (sortAsc) { - if (x < y) { - return -1; - } - if (x > y) { - return 1; - } - return 0; - } - if (x < y) { - return 1; - } - if (x > y) { - return -1; - } - return 0; - }); -} - -/** - * Sorts the table according to the key that is passed. - * @param array Input which is sorted - * @param key Which attribute is used for sorting - * @param upDown How should the data be sorted? Up or down - */ -export default function sortTable(array: any[], key: string, sortAsc: boolean) { - const isDetailArray = []; - const isNoDetail = []; - for (let i = 0; i < array.length; i++) { - if (array[i].isDetail) { - isDetailArray.push(array[i]); - } else { - isNoDetail.push(array[i]); - } - } - sortByKey(isNoDetail, key, sortAsc); - const diffParentNames = [] as any; - for (let i = 0; i < isDetailArray.length; i++) { - if (diffParentNames.length === 0) { - diffParentNames.push(isDetailArray[i]); - } else { - const lookup = { parentElement: isDetailArray[i].parentElement }; - const hasSameName = _.some(diffParentNames, lookup); - if (!hasSameName) { - diffParentNames.push(isDetailArray[i]); - } - } - } - for (let j = 0; j < diffParentNames.length; j++) { - const tempArray = _.chain(isDetailArray) - .filter(filterBy => filterBy.parentElement === diffParentNames[j].parentElement) - .groupBy(x => x.parentElement) - .map(value => ({ parentElement: key, groupedArry: value })) - .value()[0].groupedArry; - - sortByKey(tempArray, key, sortAsc); - if (tempArray.length > 0) { - // build whole array - let rememberIndex = 0; - for (let i = 0; i < isNoDetail.length; i++) { - if (isNoDetail[i].name === tempArray[0].parentElement) { - rememberIndex = i; - } - } - for (let i = 0; i < tempArray.length; i++) { - isNoDetail.splice(rememberIndex + 1, 0, tempArray[i]); - rememberIndex += 1; - } - } - } - return isNoDetail; -} diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx index 83ae9372e5..bd94c44cd5 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx @@ -334,7 +334,7 @@ function valueFirstDropdown(selectedTagKey: string, trace: Trace) { resultValue.selfAvg = resultValue.selfTotal / resultValue.count; resultValue.avg = resultValue.total / resultValue.count; let tableSpan = { - type: 'defined', + hasSubgroupValue: true, name: allDiffColumnValues[i], count: resultValue.count, total: resultValue.total, @@ -392,7 +392,7 @@ function valueFirstDropdown(selectedTagKey: string, trace: Trace) { resultValue.selfAvg = resultValue.selfTotal / resultValue.count; resultValue.avg = resultValue.total / resultValue.count; let tableSpanOTHERS = { - type: 'undefined', + hasSubgroupValue: false, name: `Without Tag: ${selectedTagKey}`, count: resultValue.count, total: resultValue.total, @@ -464,7 +464,7 @@ function buildDetail( resultValue.selfAvg = resultValue.selfTotal / resultValue.count; resultValue.avg = resultValue.total / resultValue.count; let buildOneColumnValue = { - type: 'defined', + hasSubgroupValue: true, name: diffNamesA[j], count: resultValue.count, total: resultValue.total, @@ -531,7 +531,7 @@ function generateDetailRest(allColumnValues: ITableSpan[], selectedTagKeySecond: resultValue.selfAvg = resultValue.selfTotal / resultValue.count; if (resultValue.count !== 0) { let buildOneColumnValue = { - type: 'undefined', + hasSubgroupValue: false, name: `Without Tag: ${selectedTagKeySecond}`, count: resultValue.count, total: resultValue.total, diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/types.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/types.tsx index 1c59c6e6ee..d7e7f58ffb 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/types.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/types.tsx @@ -13,7 +13,7 @@ // limitations under the License. export interface ITableSpan { - type: string; + hasSubgroupValue: boolean; // True when the entry has the subgroup tag in it. name: string; count: number; total: number; @@ -25,7 +25,7 @@ export interface ITableSpan { selfMin: number; selfMax: number; percent: number; - isDetail: boolean; + isDetail: boolean; // True when the entry represents a subgroup aggregation. parentElement: string; color: string; // If it is a service name, the color will be set. searchColor: string;