Skip to content

Commit

Permalink
Web console: Improve number alignment in tables (#10389)
Browse files Browse the repository at this point in the history
* Improve tables

* removed unused state interfaces

* better copy

* one more functional component

* updated e2e tests

* extract braced text correctly
  • Loading branch information
vogievetsky committed Sep 15, 2020
1 parent 5751d0e commit e465f05
Show file tree
Hide file tree
Showing 22 changed files with 446 additions and 187 deletions.
2 changes: 1 addition & 1 deletion web-console/e2e-tests/component/datasources/datasource.ts
Expand Up @@ -28,7 +28,7 @@ export class Datasource {
interface DatasourceProps {
readonly name: string;
readonly availability: string;
readonly numRows: number;
readonly totalRows: number;
}

export interface Datasource extends DatasourceProps {}
12 changes: 7 additions & 5 deletions web-console/e2e-tests/component/datasources/overview.ts
Expand Up @@ -29,12 +29,14 @@ enum DatasourceColumn {
NAME = 0,
AVAILABILITY,
SEGMENT_LOAD_DROP,
RETENTION,
TOTAL_DATA_SIZE,
SEGMENT_SIZE,
TOTAL_ROWS,
AVG_ROW_SIZE,
REPLICATED_SIZE,
SIZE,
COMPACTION,
AVG_SEGMENT_SIZE,
NUM_ROWS,
RETENTION,
ACTIONS,
}

/**
Expand All @@ -60,7 +62,7 @@ export class DatasourcesOverview {
new Datasource({
name: row[DatasourceColumn.NAME],
availability: row[DatasourceColumn.AVAILABILITY],
numRows: DatasourcesOverview.parseNumber(row[DatasourceColumn.NUM_ROWS]),
totalRows: DatasourcesOverview.parseNumber(row[DatasourceColumn.TOTAL_ROWS]),
}),
);
}
Expand Down
2 changes: 1 addition & 1 deletion web-console/e2e-tests/tutorial-batch.spec.ts
Expand Up @@ -156,7 +156,7 @@ async function validateDatasourceStatus(page: playwright.Page, datasourceName: s
const datasource = datasources.find(t => t.name === datasourceName);
expect(datasource).toBeDefined();
expect(datasource!.availability).toMatch('Fully available (1 segment)');
expect(datasource!.numRows).toBe(39244);
expect(datasource!.totalRows).toBe(39244);
});
}

Expand Down
7 changes: 6 additions & 1 deletion web-console/e2e-tests/util/table.ts
Expand Up @@ -39,7 +39,12 @@ export async function extractTable(
for (let i = 0; i < rows.length; i++) {
const row = rows[i];
const columns = row.querySelectorAll(rowSelector);
const values = Array.from(columns).map(c => (c as HTMLElement).innerText);
const values = Array.from(columns).map(c => {
const realTexts = Array.from(c.querySelectorAll('.real-text'));
return realTexts.length
? (realTexts[0] as HTMLElement).innerText
: (c as HTMLElement).innerText;
});
if (!values.every(value => value === BLANK_VALUE)) {
data.push(values);
}
Expand Down
16 changes: 5 additions & 11 deletions web-console/src/bootstrap/react-table-defaults.tsx
Expand Up @@ -24,17 +24,11 @@ import { booleanCustomTableFilter, countBy, makeTextFilter } from '../utils';

import { ReactTableCustomPagination } from './react-table-custom-pagination';

/* tslint:disable:max-classes-per-file */

class NoData extends React.PureComponent {
render(): JSX.Element | null {
const { children } = this.props;
if (!children) return null;
return <div className="rt-noData">{children}</div>;
}
}

/* tslint:enable:max-classes-per-file */
export const NoData = React.memo(function NoData(props) {
const { children } = props;
if (!children) return null;
return <div className="rt-noData">{children}</div>;
});

Object.assign(ReactTableDefaults, {
className: '-striped -highlight',
Expand Down
@@ -0,0 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`BracedText matches snapshot 1`] = `
<span
className="braced-text"
>
<span
className="brace-text"
>
00,000.0
</span>
<span
className="real-text"
>
23.3
</span>
</span>
`;
38 changes: 38 additions & 0 deletions web-console/src/components/braced-text/braced-text.scss
@@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/

.braced-text {
position: relative;
text-align: right;
white-space: nowrap;

.brace-text {
position: relative;
top: -50000px; // Send it into the stratosphere (get it out of the parent container to prevent the browser from adding '...')
opacity: 0;
pointer-events: none;
}

.real-text {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
}
30 changes: 30 additions & 0 deletions web-console/src/components/braced-text/braced-text.spec.tsx
@@ -0,0 +1,30 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 { shallow } from 'enzyme';
import React from 'react';

import { BracedText } from './braced-text';

describe('BracedText', () => {
it('matches snapshot', () => {
const bracedText = shallow(<BracedText text="23.3" braces={['34', '23,423.4']} />);

expect(bracedText).toMatchSnapshot();
});
});
55 changes: 55 additions & 0 deletions web-console/src/components/braced-text/braced-text.tsx
@@ -0,0 +1,55 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 './braced-text.scss';

export interface BracedTextProps {
text: string;
braces: string[];
}

export function findMostNumbers(strings: string[]): string {
let longest = '';
let longestNumLengthPlusOne = 1;
for (const s of strings) {
const parts = s.split(/\d/g);
const numLengthPlusOne = parts.length;
if (longestNumLengthPlusOne < numLengthPlusOne) {
longest = parts.join('0');
longestNumLengthPlusOne = numLengthPlusOne;
} else if (longestNumLengthPlusOne === numLengthPlusOne && longest.length < s.length) {
// Tie break on general length
longest = parts.join('0');
longestNumLengthPlusOne = numLengthPlusOne;
}
}
return longest;
}

export const BracedText = React.memo(function BracedText(props: BracedTextProps) {
const { text, braces } = props;

return (
<span className="braced-text">
<span className="brace-text">{findMostNumbers(braces.concat(text))}</span>
<span className="real-text">{text}</span>
</span>
);
});
Expand Up @@ -21,7 +21,7 @@ import React from 'react';
import ReactTable from 'react-table';

import { useQueryManager } from '../../hooks';
import { ColumnMetadata, queryDruidSql, QueryState } from '../../utils';
import { ColumnMetadata, queryDruidSql } from '../../utils';
import { Loader } from '../loader/loader';

import './datasource-columns-table.scss';
Expand All @@ -36,10 +36,6 @@ export interface DatasourceColumnsTableProps {
downloadFilename?: string;
}

export interface DatasourceColumnsTableState {
columnsState: QueryState<DatasourceColumnsTableRow[]>;
}

export const DatasourceColumnsTable = React.memo(function DatasourceColumnsTable(
props: DatasourceColumnsTableProps,
) {
Expand Down
1 change: 1 addition & 0 deletions web-console/src/components/index.ts
Expand Up @@ -20,6 +20,7 @@ export * from './action-cell/action-cell';
export * from './action-icon/action-icon';
export * from './array-input/array-input';
export * from './auto-form/auto-form';
export * from './braced-text/braced-text';
export * from './center-message/center-message';
export * from './clearable-input/clearable-input';
export * from './external-link/external-link';
Expand Down
Expand Up @@ -56,25 +56,25 @@ exports[`Segment Timeline matches snapshot 1`] = `
checked=""
name="Blueprint3.RadioGroup-0"
type="radio"
value="countData"
value="sizeData"
/>
<span
class="bp3-control-indicator"
/>
Segment count
Total size
</label>
<label
class="bp3-control bp3-radio"
>
<input
name="Blueprint3.RadioGroup-0"
type="radio"
value="sizeData"
value="countData"
/>
<span
class="bp3-control-indicator"
/>
Total size
Segment count
</label>
</div>
</div>
Expand Down
Expand Up @@ -38,13 +38,15 @@ interface SegmentTimelineProps {
dataQueryManager?: QueryManager<{ capabilities: Capabilities; timeSpan: number }, any>;
}

type ActiveDataType = 'sizeData' | 'countData';

interface SegmentTimelineState {
data?: Record<string, any>;
datasources: string[];
stackedData?: Record<string, BarUnitData[]>;
singleDatasourceData?: Record<string, Record<string, BarUnitData[]>>;
activeDatasource: string | null;
activeDataType: string; // "countData" || "sizeData"
activeDataType: ActiveDataType;
dataToRender: BarUnitData[];
timeSpan: number; // by months
loading: boolean;
Expand Down Expand Up @@ -232,7 +234,7 @@ export class SegmentTimeline extends React.PureComponent<
singleDatasourceData: {},
dataToRender: [],
activeDatasource: null,
activeDataType: 'countData',
activeDataType: 'sizeData',
timeSpan: DEFAULT_TIME_SPAN_MONTHS,
loading: true,
xScale: null,
Expand Down Expand Up @@ -517,8 +519,8 @@ ORDER BY "start" DESC`;
onChange={(e: any) => this.setState({ activeDataType: e.target.value })}
selectedValue={activeDataType}
>
<Radio label={'Segment count'} value={'countData'} />
<Radio label={'Total size'} value={'sizeData'} />
<Radio label={'Segment count'} value={'countData'} />
</RadioGroup>
</FormGroup>

Expand Down
5 changes: 0 additions & 5 deletions web-console/src/components/show-history/show-history.tsx
Expand Up @@ -21,7 +21,6 @@ import axios from 'axios';
import React from 'react';

import { useQueryManager } from '../../hooks';
import { QueryState } from '../../utils';
import { Loader } from '../loader/loader';
import { ShowValue } from '../show-value/show-value';

Expand All @@ -37,10 +36,6 @@ export interface ShowHistoryProps {
downloadFilename?: string;
}

export interface ShowHistoryState {
historyState: QueryState<VersionSpec[]>;
}

export const ShowHistory = React.memo(function ShowHistory(props: ShowHistoryProps) {
const { downloadFilename, endpoint } = props;

Expand Down
6 changes: 1 addition & 5 deletions web-console/src/components/show-json/show-json.tsx
Expand Up @@ -24,7 +24,7 @@ import React from 'react';
import { useQueryManager } from '../../hooks';
import { AppToaster } from '../../singletons/toaster';
import { UrlBaser } from '../../singletons/url-baser';
import { downloadFile, QueryState } from '../../utils';
import { downloadFile } from '../../utils';
import { Loader } from '../loader/loader';

import './show-json.scss';
Expand All @@ -35,10 +35,6 @@ export interface ShowJsonProps {
downloadFilename?: string;
}

export interface ShowJsonState {
jsonState: QueryState<string>;
}

export const ShowJson = React.memo(function ShowJson(props: ShowJsonProps) {
const { endpoint, transform, downloadFilename } = props;

Expand Down
5 changes: 0 additions & 5 deletions web-console/src/dialogs/retention-dialog/retention-dialog.tsx
Expand Up @@ -40,11 +40,6 @@ export interface RetentionDialogProps {
onSave: (datasource: string, newRules: Rule[], comment: string) => void;
}

export interface RetentionDialogState {
currentRules: Rule[];
historyRecords: any[] | undefined;
}

export const RetentionDialog = React.memo(function RetentionDialog(props: RetentionDialogProps) {
const { datasource, onCancel, onEditDefaults, rules, defaultRules, tiers } = props;
const [currentRules, setCurrentRules] = useState(props.rules);
Expand Down

0 comments on commit e465f05

Please sign in to comment.