Skip to content

Commit

Permalink
ui: incorporate new changes from Prometheus React UI
Browse files Browse the repository at this point in the history
Signed-off-by: Prem Kumar <[email protected]>
  • Loading branch information
onprem committed Apr 15, 2020
1 parent 9fb4d23 commit 8d23c2b
Show file tree
Hide file tree
Showing 26 changed files with 227 additions and 103 deletions.
2 changes: 1 addition & 1 deletion pkg/ui/react-app/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
and set to Prometheus's external URL path. It gets prepended to all links back
to Prometheus, both for asset loading as well as API accesses.
-->
<script>const PATH_PREFIX='';</script>
<script>const GLOBAL_PATH_PREFIX='';</script>
<script>const THANOS_COMPONENT='query';</script>

<!--
Expand Down
11 changes: 7 additions & 4 deletions pkg/ui/react-app/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -153,17 +153,20 @@ button.execute-btn {
.legend-item {
cursor: pointer;
display: flex;
align-items: center;
padding: 0 5px;
border-radius: 3px;
line-height: 1.7;
}
.legend-item div {
flex-wrap: wrap;
}

.legend-swatch {
width: 7px;
min-width: 7px;
height: 7px;
outline-offset: 1px;
outline: 1.5px solid #ccc;
margin: 2px 8px 2px 0;
margin: 6px 8px 2px 0;
display: inline-block;
}

Expand Down Expand Up @@ -244,7 +247,7 @@ button.execute-btn {

.rule_cell {
white-space: pre-wrap;
background-color: #F5F5F5;
background-color: #f5f5f5;
display: block;
font-family: monospace;
}
2 changes: 1 addition & 1 deletion pkg/ui/react-app/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Router } from '@reach/router';
import { Alerts, Config, Flags, Rules, ServiceDiscovery, Status, Targets, TSDBStatus, PanelList } from './pages';

describe('App', () => {
const app = shallow(<App pathPrefix="/path/prefix" />);
const app = shallow(<App pathPrefix="/path/prefix" thanosComponent="query" />);

it('navigates', () => {
expect(app.find(Navigation)).toHaveLength(1);
Expand Down
30 changes: 30 additions & 0 deletions pkg/ui/react-app/src/Navbar.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import Navigation from './Navbar';
import { NavItem, NavLink } from 'reactstrap';

describe('Navbar should contain console Link', () => {
it('with non-empty consoleslink', () => {
const app = shallow(<Navigation pathPrefix="/path/prefix" consolesLink="/path/consoles" />);
expect(
app.contains(
<NavItem>
<NavLink href="/path/consoles">Consoles</NavLink>
</NavItem>
)
).toBeTruthy();
});
});

describe('Navbar should not contain consoles link', () => {
it('with empty string in consolesLink', () => {
const app = shallow(<Navigation pathPrefix="/path/prefix" consolesLink={null} />);
expect(
app.contains(
<NavItem>
<NavLink>Consoles</NavLink>
</NavItem>
)
).toBeFalsy();
});
});
11 changes: 10 additions & 1 deletion pkg/ui/react-app/src/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ import {
} from 'reactstrap';
import PathPrefixProps from './types/PathPrefixProps';

const Navigation: FC<PathPrefixProps> = ({ pathPrefix }) => {
interface NavbarProps {
consolesLink: string | null;
}

const Navigation: FC<PathPrefixProps & NavbarProps> = ({ pathPrefix, consolesLink }) => {
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(!isOpen);
return (
Expand All @@ -25,6 +29,11 @@ const Navigation: FC<PathPrefixProps> = ({ pathPrefix }) => {
</Link>
<Collapse isOpen={isOpen} navbar style={{ justifyContent: 'space-between' }}>
<Nav className="ml-0" navbar>
{consolesLink !== null && (
<NavItem>
<NavLink href={consolesLink}>Consoles</NavLink>
</NavItem>
)}
<NavItem>
<NavLink tag={Link} to={`${pathPrefix}/new/alerts`}>
Alerts
Expand Down
7 changes: 4 additions & 3 deletions pkg/ui/react-app/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import 'bootstrap/dist/css/bootstrap.min.css';
import { isPresent } from './utils';

// Declared/defined in public/index.html, value replaced by Prometheus when serving bundle.
declare const PATH_PREFIX: string;
declare const GLOBAL_PATH_PREFIX: string;
declare const THANOS_COMPONENT: string;

let prefix = PATH_PREFIX;
if (PATH_PREFIX === 'PATH_PREFIX_PLACEHOLDER' || PATH_PREFIX === '/') {
let prefix = GLOBAL_PATH_PREFIX;
if (GLOBAL_PATH_PREFIX === 'PATH_PREFIX_PLACEHOLDER' || GLOBAL_PATH_PREFIX === '/' || !isPresent(GLOBAL_PATH_PREFIX)) {
// Either we are running the app outside of Prometheus, so the placeholder value in
// the index.html didn't get replaced, or we have a '/' prefix, which we also need to
// normalize to '' to make concatenations work (prefixes like '/foo/bar/' already get
Expand Down
78 changes: 56 additions & 22 deletions pkg/ui/react-app/src/pages/graph/DataTable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,42 @@ describe('DataTable', () => {
});

it('renders a warning', () => {
const alert = dataTable.find(Alert);
expect(alert.render().text()).toEqual('Warning: Fetched 10001 metrics, only displaying first 10000.');
const alerts = dataTable.find(Alert);
expect(
alerts
.first()
.render()
.text()
).toEqual('Warning: Fetched 10001 metrics, only displaying first 10000.');
});
});

describe('when resultType is vector and size is more than maximum limit of formatting', () => {
const dataTableProps: QueryResult = {
data: {
resultType: 'vector',
result: Array.from(Array(1001).keys()).map(i => {
return {
metric: {
__name__: `metric_name_${i}`,
label1: 'value_1',
labeln: 'value_n',
},
value: [1572098246.599, `${i}`],
};
}),
},
};
const dataTable = shallow(<DataTable {...dataTableProps} />);

it('renders a warning', () => {
const alerts = dataTable.find(Alert);
expect(
alerts
.first()
.render()
.text()
).toEqual('Notice: Showing more than 1000 series, turning off label formatting for performance reasons.');
});
});

Expand Down Expand Up @@ -212,26 +246,26 @@ describe('DataTable', () => {
const rows = table.find('tr');
expect(table.find('tr')).toHaveLength(3);
const row = rows.at(0);
expect(row.text()).toEqual(`<SeriesName />1 @1572097950.93
1 @1572097965.931
1 @1572097980.929
1 @1572097995.931
1 @1572098010.932
1 @1572098025.933
1 @1572098040.93
1 @1572098055.93
1 @1572098070.93
1 @1572098085.936
1 @1572098100.936
1 @1572098115.933
1 @1572098130.932
1 @1572098145.932
1 @1572098160.933
1 @1572098175.934
1 @1572098190.937
1 @1572098205.934
1 @1572098220.933
1 @1572098235.934`);
expect(row.text()).toEqual(`<SeriesName />9 @1572097950.93
10 @1572097965.931
11 @1572097980.929
12 @1572097995.931
13 @1572098010.932
14 @1572098025.933
15 @1572098040.93
16 @1572098055.93
17 @1572098070.93
18 @1572098085.936
19 @1572098100.936
20 @1572098115.933
21 @1572098130.932
22 @1572098145.932
23 @1572098160.933
24 @1572098175.934
25 @1572098190.937
26 @1572098205.934
27 @1572098220.933
28 @1572098235.934`);
});
});

Expand Down
14 changes: 11 additions & 3 deletions pkg/ui/react-app/src/pages/graph/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,18 @@ const DataTable: FC<QueryResult> = ({ data }) => {
return <Alert color="secondary">Empty query result</Alert>;
}

const maxFormattableSize = 1000;
let rows: ReactNode[] = [];
let limited = false;
const doFormat = data.result.length <= maxFormattableSize;
switch (data.resultType) {
case 'vector':
rows = (limitSeries(data.result) as InstantSample[]).map(
(s: InstantSample, index: number): ReactNode => {
return (
<tr key={index}>
<td>
<SeriesName labels={s.metric} format={false} />
<SeriesName labels={s.metric} format={doFormat} />
</td>
<td>{s.value[1]}</td>
</tr>
Expand All @@ -78,13 +80,13 @@ const DataTable: FC<QueryResult> = ({ data }) => {
rows = (limitSeries(data.result) as RangeSamples[]).map((s, index) => {
const valueText = s.values
.map(v => {
return [1] + ' @' + v[0];
return v[1] + ' @' + v[0];
})
.join('\n');
return (
<tr style={{ whiteSpace: 'pre' }} key={index}>
<td>
<SeriesName labels={s.metric} format={false} />
<SeriesName labels={s.metric} format={doFormat} />
</td>
<td>{valueText}</td>
</tr>
Expand Down Expand Up @@ -119,6 +121,12 @@ const DataTable: FC<QueryResult> = ({ data }) => {
<strong>Warning:</strong> Fetched {data.result.length} metrics, only displaying first {rows.length}.
</Alert>
)}
{!doFormat && (
<Alert color="secondary">
<strong>Notice:</strong> Showing more than {maxFormattableSize} series, turning off label formatting for
performance reasons.
</Alert>
)}
<Table hover size="sm" className="data-table">
<tbody>{rows}</tbody>
</Table>
Expand Down
12 changes: 9 additions & 3 deletions pkg/ui/react-app/src/pages/graph/ExpressionInput.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ const getKeyEvent = (key: string): React.KeyboardEvent<HTMLInputElement> =>
({
key,
nativeEvent: {},
preventDefault: () => {},
preventDefault: () => {
// Do nothing.
},
} as React.KeyboardEvent<HTMLInputElement>);

describe('ExpressionInput', () => {
Expand All @@ -21,8 +23,12 @@ describe('ExpressionInput', () => {
'Query History': [],
'Metric Names': metricNames,
},
executeQuery: (): void => {},
onExpressionChange: (): void => {},
executeQuery: (): void => {
// Do nothing.
},
onExpressionChange: (): void => {
// Do nothing.
},
loading: false,
};

Expand Down
63 changes: 30 additions & 33 deletions pkg/ui/react-app/src/pages/graph/ExpressionInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,39 +77,36 @@ class ExpressionInput extends Component<ExpressionInputProps, ExpressionInputSta
const { autocompleteSections } = this.props;
let index = 0;
const sections = inputValue!.length
? Object.entries(autocompleteSections).reduce(
(acc, [title, items]) => {
const matches = this.getSearchMatches(inputValue!, items);
return !matches.length
? acc
: [
...acc,
<ul className="autosuggest-dropdown-list" key={title}>
<li className="autosuggest-dropdown-header">{title}</li>
{matches
.slice(0, 100) // Limit DOM rendering to 100 results, as DOM rendering is sloooow.
.map(({ original, string: text }) => {
const itemProps = downshift.getItemProps({
key: original,
index,
item: original,
style: {
backgroundColor: highlightedIndex === index++ ? 'lightgray' : 'white',
},
});
return (
<li
key={title}
{...itemProps}
dangerouslySetInnerHTML={{ __html: sanitizeHTML(text, { allowedTags: ['strong'] }) }}
/>
);
})}
</ul>,
];
},
[] as JSX.Element[]
)
? Object.entries(autocompleteSections).reduce((acc, [title, items]) => {
const matches = this.getSearchMatches(inputValue!, items);
return !matches.length
? acc
: [
...acc,
<ul className="autosuggest-dropdown-list" key={title}>
<li className="autosuggest-dropdown-header">{title}</li>
{matches
.slice(0, 100) // Limit DOM rendering to 100 results, as DOM rendering is sloooow.
.map(({ original, string: text }) => {
const itemProps = downshift.getItemProps({
key: original,
index,
item: original,
style: {
backgroundColor: highlightedIndex === index++ ? 'lightgray' : 'white',
},
});
return (
<li
key={title}
{...itemProps}
dangerouslySetInnerHTML={{ __html: sanitizeHTML(text, { allowedTags: ['strong'] }) }}
/>
);
})}
</ul>,
];
}, [] as JSX.Element[])
: [];

if (!sections.length) {
Expand Down
14 changes: 12 additions & 2 deletions pkg/ui/react-app/src/pages/graph/Graph.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,12 @@ describe('Graph', () => {
endTime: 1572128598,
resolution: 28,
},
data: { result: [{ values: [], metric: {} }, { values: [], metric: {} }] },
data: {
result: [
{ values: [], metric: {} },
{ values: [], metric: {} },
],
},
} as any)}
/>
);
Expand All @@ -251,7 +256,12 @@ describe('Graph', () => {
endTime: 1572128598,
resolution: 28,
},
data: { result: [{ values: [], metric: {} }, { values: [], metric: {} }] },
data: {
result: [
{ values: [], metric: {} },
{ values: [], metric: {} },
],
},
} as any)}
/>
);
Expand Down
Loading

0 comments on commit 8d23c2b

Please sign in to comment.