React UI: Move global /graph settings to navbar

This saves precious space at the top and makes the page look less
awkward.

Signed-off-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
Julius Volz 2020-01-25 00:54:55 +01:00
parent d996ba20ec
commit c36a781b1c
3 changed files with 48 additions and 16 deletions

View file

@ -1,4 +1,4 @@
import React, { FC } from 'react'; import React, { FC, useState } from 'react';
import Navigation from './Navbar'; import Navigation from './Navbar';
import { Container } from 'reactstrap'; import { Container } from 'reactstrap';
@ -8,9 +8,11 @@ import { Alerts, Config, Flags, Rules, Services, Status, Targets, TSDBStatus, Pa
import PathPrefixProps from './types/PathPrefixProps'; import PathPrefixProps from './types/PathPrefixProps';
const App: FC<PathPrefixProps> = ({ pathPrefix }) => { const App: FC<PathPrefixProps> = ({ pathPrefix }) => {
const [extraNavItem, setExtraNavItem] = useState<React.ReactNode>(null);
return ( return (
<> <>
<Navigation pathPrefix={pathPrefix} /> <Navigation pathPrefix={pathPrefix} extraNavItem={extraNavItem} />
<Container fluid style={{ paddingTop: 70 }}> <Container fluid style={{ paddingTop: 70 }}>
<Router basepath={`${pathPrefix}/new`}> <Router basepath={`${pathPrefix}/new`}>
<Redirect from="/" to={`${pathPrefix}/new/graph`} /> <Redirect from="/" to={`${pathPrefix}/new/graph`} />
@ -19,7 +21,7 @@ const App: FC<PathPrefixProps> = ({ pathPrefix }) => {
NOTE: Any route added here needs to also be added to the list of NOTE: Any route added here needs to also be added to the list of
React-handled router paths ("reactRouterPaths") in /web/web.go. React-handled router paths ("reactRouterPaths") in /web/web.go.
*/} */}
<PanelList path="/graph" pathPrefix={pathPrefix} /> <PanelList path="/graph" pathPrefix={pathPrefix} setExtraNavItem={setExtraNavItem} />
<Alerts path="/alerts" pathPrefix={pathPrefix} /> <Alerts path="/alerts" pathPrefix={pathPrefix} />
<Config path="/config" pathPrefix={pathPrefix} /> <Config path="/config" pathPrefix={pathPrefix} />
<Flags path="/flags" pathPrefix={pathPrefix} /> <Flags path="/flags" pathPrefix={pathPrefix} />

View file

@ -14,9 +14,14 @@ import {
} from 'reactstrap'; } from 'reactstrap';
import PathPrefixProps from './types/PathPrefixProps'; import PathPrefixProps from './types/PathPrefixProps';
const Navigation: FC<PathPrefixProps> = ({ pathPrefix }) => { interface NavigationProps {
extraNavItem: React.ReactNode;
}
const Navigation: FC<PathPrefixProps & NavigationProps> = ({ pathPrefix, extraNavItem }) => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(!isOpen); const toggle = () => setIsOpen(!isOpen);
return ( return (
<Navbar className="mb-3" dark color="dark" expand="md" fixed="top"> <Navbar className="mb-3" dark color="dark" expand="md" fixed="top">
<NavbarToggler onClick={toggle} /> <NavbarToggler onClick={toggle} />
@ -70,6 +75,7 @@ const Navigation: FC<PathPrefixProps> = ({ pathPrefix }) => {
<NavLink href={`${pathPrefix}/`}>Classic UI</NavLink> <NavLink href={`${pathPrefix}/`}>Classic UI</NavLink>
</NavItem> </NavItem>
</Nav> </Nav>
{extraNavItem}
</Collapse> </Collapse>
</Navbar> </Navbar>
); );

View file

@ -1,16 +1,22 @@
import React, { Component, ChangeEvent } from 'react'; import React, { Component, ChangeEvent, CSSProperties } from 'react';
import { RouteComponentProps } from '@reach/router'; import { RouteComponentProps } from '@reach/router';
import { Alert, Button, Col, Row } from 'reactstrap'; import { Alert, Button, Col, Row, DropdownToggle, DropdownMenu, UncontrolledDropdown } from 'reactstrap';
import Panel, { PanelOptions, PanelDefaultOptions } from './Panel'; import Panel, { PanelOptions, PanelDefaultOptions } from './Panel';
import Checkbox from '../../components/Checkbox'; import Checkbox from '../../components/Checkbox';
import PathPrefixProps from '../../types/PathPrefixProps'; import PathPrefixProps from '../../types/PathPrefixProps';
import { generateID, decodePanelOptionsFromQueryString, encodePanelOptionsToQueryString } from '../../utils'; import { generateID, decodePanelOptionsFromQueryString, encodePanelOptionsToQueryString } from '../../utils';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCog } from '@fortawesome/free-solid-svg-icons';
export type MetricGroup = { title: string; items: string[] }; export type MetricGroup = { title: string; items: string[] };
export type PanelMeta = { key: string; options: PanelOptions; id: string }; export type PanelMeta = { key: string; options: PanelOptions; id: string };
interface PanelListProps {
setExtraNavItem: (item: React.ReactNode) => void;
}
interface PanelListState { interface PanelListState {
panels: PanelMeta[]; panels: PanelMeta[];
pastQueries: string[]; pastQueries: string[];
@ -20,8 +26,8 @@ interface PanelListState {
useLocalTime: boolean; useLocalTime: boolean;
} }
class PanelList extends Component<RouteComponentProps & PathPrefixProps, PanelListState> { class PanelList extends Component<RouteComponentProps & PathPrefixProps & PanelListProps, PanelListState> {
constructor(props: RouteComponentProps & PathPrefixProps) { constructor(props: RouteComponentProps & PathPrefixProps & PanelListProps) {
super(props); super(props);
this.state = { this.state = {
@ -35,6 +41,8 @@ class PanelList extends Component<RouteComponentProps & PathPrefixProps, PanelLi
} }
componentDidMount() { componentDidMount() {
this.props.setExtraNavItem(this.panelSettingsDropdown());
!this.state.panels.length && this.addPanel(); !this.state.panels.length && this.addPanel();
fetch(`${this.props.pathPrefix}/api/v1/label/__name__/values`, { cache: 'no-store', credentials: 'same-origin' }) fetch(`${this.props.pathPrefix}/api/v1/label/__name__/values`, { cache: 'no-store', credentials: 'same-origin' })
.then(resp => { .then(resp => {
@ -82,6 +90,10 @@ class PanelList extends Component<RouteComponentProps & PathPrefixProps, PanelLi
this.updatePastQueries(); this.updatePastQueries();
} }
componentWillUnmount() {
this.props.setExtraNavItem(null);
}
isHistoryEnabled = () => JSON.parse(localStorage.getItem('enable-query-history') || 'false') as boolean; isHistoryEnabled = () => JSON.parse(localStorage.getItem('enable-query-history') || 'false') as boolean;
getHistoryItems = () => JSON.parse(localStorage.getItem('history') || '[]') as string[]; getHistoryItems = () => JSON.parse(localStorage.getItem('history') || '[]') as string[];
@ -154,15 +166,18 @@ class PanelList extends Component<RouteComponentProps & PathPrefixProps, PanelLi
); );
}; };
render() { panelSettingsDropdown() {
const { metricNames, pastQueries, timeDriftError, fetchMetricsError, panels } = this.state; const wrapperStyles: CSSProperties = { margin: '3px 15px', alignSelf: 'center', whiteSpace: 'nowrap' };
const { pathPrefix } = this.props;
return ( return (
<> <UncontrolledDropdown className="float-right">
<Row className="mb-2"> <DropdownToggle>
<FontAwesomeIcon icon={faCog} fixedWidth />
</DropdownToggle>
<DropdownMenu right>
<Checkbox <Checkbox
id="query-history-checkbox" id="query-history-checkbox"
wrapperStyles={{ margin: '0 0 0 15px', alignSelf: 'center' }} wrapperStyles={wrapperStyles}
onChange={this.toggleQueryHistory} onChange={this.toggleQueryHistory}
defaultChecked={this.isHistoryEnabled()} defaultChecked={this.isHistoryEnabled()}
> >
@ -170,13 +185,22 @@ class PanelList extends Component<RouteComponentProps & PathPrefixProps, PanelLi
</Checkbox> </Checkbox>
<Checkbox <Checkbox
id="use-local-time-checkbox" id="use-local-time-checkbox"
wrapperStyles={{ margin: '0 0 0 15px', alignSelf: 'center' }} wrapperStyles={wrapperStyles}
onChange={this.toggleUseLocalTime} onChange={this.toggleUseLocalTime}
defaultChecked={this.useLocalTime()} defaultChecked={this.useLocalTime()}
> >
Use local time Use local time
</Checkbox> </Checkbox>
</Row> </DropdownMenu>
</UncontrolledDropdown>
);
}
render() {
const { metricNames, pastQueries, timeDriftError, fetchMetricsError, panels } = this.state;
const { pathPrefix } = this.props;
return (
<>
<Row> <Row>
<Col> <Col>
{timeDriftError && ( {timeDriftError && (