diff --git a/hyperglass/ui/components/form/field.tsx b/hyperglass/ui/components/form/field.tsx
index 80f4692..d0d1a47 100644
--- a/hyperglass/ui/components/form/field.tsx
+++ b/hyperglass/ui/components/form/field.tsx
@@ -37,9 +37,9 @@ export const FormField = (props: TField) => {
htmlFor={name}
display="flex"
opacity={opacity}
- color={error !== false ? errorColor : labelColor}
alignItems="center"
- justifyContent="space-between">
+ justifyContent="space-between"
+ color={error !== false ? errorColor : labelColor}>
{label}
{labelAddOn}
diff --git a/hyperglass/ui/components/index.ts b/hyperglass/ui/components/index.ts
index 2221434..d4146af 100644
--- a/hyperglass/ui/components/index.ts
+++ b/hyperglass/ui/components/index.ts
@@ -19,5 +19,4 @@ export * from './path';
export * from './results';
export * from './select';
export * from './table';
-export * from './title';
export * from './util';
diff --git a/hyperglass/ui/components/lookingGlass.tsx b/hyperglass/ui/components/lookingGlass.tsx
index 4f36e5d..9b587a4 100644
--- a/hyperglass/ui/components/lookingGlass.tsx
+++ b/hyperglass/ui/components/lookingGlass.tsx
@@ -18,7 +18,7 @@ import {
} from '~/components';
import { useConfig } from '~/context';
import { useStrf, useGreeting, useDevice, useLGState } from '~/hooks';
-import { isQueryType, isString } from '~/types';
+import { isQueryType, isQueryContent, isString } from '~/types';
import type { TFormData, TDeviceVrf, OnChangeArgs } from '~/types';
@@ -63,8 +63,6 @@ export const HyperglassForm = () => {
} = useLGState();
function submitHandler(values: TFormData) {
- console.dir(values);
- console.dir(formData.value);
if (!greetingAck && web.greeting.required) {
window.location.reload(false);
setGreetingAck(false);
@@ -143,15 +141,15 @@ export const HyperglassForm = () => {
} else if (e.field === 'query_target' && isString(e.value)) {
queryTarget.set(e.value);
}
- console.log(e.field, e.value);
- console.dir(getValues());
}
const vrfContent = useMemo(() => {
if (Object.keys(content.vrf).includes(queryVrf.value) && queryType.value !== '') {
return content.vrf[queryVrf.value][queryType.value];
+ } else {
+ return null;
}
- }, [queryVrf]);
+ }, [queryVrf.value, queryLocation.value, queryType.value]);
const isFqdnQuery = useMemo(() => {
return ['bgp_route', 'ping', 'traceroute'].includes(queryType.value);
@@ -185,7 +183,9 @@ export const HyperglassForm = () => {
}>
+ labelAddOn={
+
+ }>
diff --git a/hyperglass/ui/components/path/chart.tsx b/hyperglass/ui/components/path/chart.tsx
index 1b13d47..4b84ce8 100644
--- a/hyperglass/ui/components/path/chart.tsx
+++ b/hyperglass/ui/components/path/chart.tsx
@@ -19,7 +19,7 @@ export const Chart = (props: TChart) => {
const flowProps = useBreakpointValue>({
base: { defaultPosition: [0, 300], defaultZoom: 0 },
- lg: { defaultPosition: [500, 450] },
+ lg: { defaultPosition: [100, 300], defaultZoom: 0.7 },
});
const elements = useMemo(() => [...buildElements({ asn: primary_asn, name: org_name }, data)], [
@@ -29,7 +29,7 @@ export const Chart = (props: TChart) => {
return (
-
+
@@ -38,7 +38,7 @@ export const Chart = (props: TChart) => {
);
};
-const TestNode = (props: TNode) => {
+const ASNode = (props: TNode) => {
const { data } = props;
const { asn, name, hasChildren, hasParents } = data;
diff --git a/hyperglass/ui/components/path/path.tsx b/hyperglass/ui/components/path/path.tsx
index 8cf53d0..7e4596c 100644
--- a/hyperglass/ui/components/path/path.tsx
+++ b/hyperglass/ui/components/path/path.tsx
@@ -23,6 +23,7 @@ export const Path = (props: TPath) => {
const response = getResponse(device);
const output = response?.output as TStructuredResponse;
const bg = useColorValue('whiteFaded.50', 'blackFaded.900');
+
return (
<>
diff --git a/hyperglass/ui/components/path/util.ts b/hyperglass/ui/components/path/util.ts
index 34155f8..0f17b4b 100644
--- a/hyperglass/ui/components/path/util.ts
+++ b/hyperglass/ui/components/path/util.ts
@@ -10,7 +10,7 @@ function treeToElement(part: PathPart, len: number, index: number): FlowElement[
let elements = [
{
id: String(part.base),
- type: 'TestNode',
+ type: 'ASNode',
position: { x, y },
data: {
asn: part.base,
@@ -50,7 +50,7 @@ export function* buildElements(base: BasePath, data: TStructuredResponse): Gener
// Add the first hop at the base.
yield {
id: base.asn,
- type: 'TestNode',
+ type: 'ASNode',
position: { x: 150, y: numHops * 10 },
data: { asn: base.asn, name: base.name, hasChildren: true, hasParents: false },
};
diff --git a/hyperglass/ui/components/results/group.tsx b/hyperglass/ui/components/results/group.tsx
index 791d984..c6439fb 100644
--- a/hyperglass/ui/components/results/group.tsx
+++ b/hyperglass/ui/components/results/group.tsx
@@ -1,4 +1,4 @@
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
import { Accordion, Box, Stack, useToken } from '@chakra-ui/react';
import { motion, AnimatePresence } from 'framer-motion';
import { AnimatedDiv, Label } from '~/components';
@@ -63,7 +63,7 @@ export const Results = () => {
xl: { opacity: 0, x: 100 },
});
- const [resultsComplete, setComplete] = useState(null);
+ const [resultsComplete, setComplete] = useState([]);
const matchedVrf =
vrfs.filter(v => v.id === queryVrf.value)[0] ?? vrfs.filter(v => v.id === 'default')[0];
@@ -73,6 +73,13 @@ export const Results = () => {
queryTypeLabel = queries[queryType.value].display_name;
}
+ // Scroll to the top of the page when results load - primarily for mobile.
+ useEffect(() => {
+ if (typeof window !== 'undefined') {
+ window.scrollTo(0, 0);
+ }
+ }, []);
+
return (
<>
{
transition={{ duration: 0.3 }}
animate={{ opacity: 1, y: 0 }}
maxW={{ base: '100%', md: '75%' }}>
-
+
{queryLocation &&
queryLocation.map((loc, i) => {
diff --git a/hyperglass/ui/components/results/individual.tsx b/hyperglass/ui/components/results/individual.tsx
index df5843e..a001b0b 100644
--- a/hyperglass/ui/components/results/individual.tsx
+++ b/hyperglass/ui/components/results/individual.tsx
@@ -16,9 +16,9 @@ import { BGPTable, Countdown, CopyButton, RequeryButton, TextOutput, If, Path }
import { useColorValue, useConfig, useMobile } from '~/context';
import { useStrf, useLGQuery, useLGState, useTableToString } from '~/hooks';
import { isStructuredOutput, isStringOutput } from '~/types';
+import { isStackError, isFetchError, isLGError } from './guards';
import { FormattedError } from './error';
import { ResultHeader } from './header';
-import { isStackError, isFetchError, isLGError } from './guards';
import type { TAccordionHeaderWrapper, TResult, TErrorLevels } from './types';
@@ -71,12 +71,15 @@ export const Result = forwardRef((props, ref) => {
const cacheLabel = useStrf(web.text.cache_icon, { time: data?.timestamp }, [data?.timestamp]);
- const [isOpen, setOpen] = useState(false);
- const [hasOverride, setOverride] = useState(false);
-
const handleToggle = () => {
- setOpen(!isOpen);
- setOverride(true);
+ // Close if open.
+ if (resultsComplete.includes(index)) {
+ setComplete(p => p.filter(i => i !== index));
+ }
+ // Open if closed.
+ else if (!resultsComplete.includes(index)) {
+ setComplete(p => [...p, index]);
+ }
};
const errorKeywords = useMemo(() => {
@@ -140,17 +143,12 @@ export const Result = forwardRef((props, ref) => {
copyValue = errorMsg;
}
+ // If this is the first completed result, open it.
useEffect(() => {
- if (isLoading && resultsComplete === null) {
- setComplete(index);
+ if (!isLoading && !isError && resultsComplete.length === 0) {
+ setComplete([index]);
}
- }, [isLoading, resultsComplete]);
-
- useEffect(() => {
- if (resultsComplete === index && !hasOverride) {
- setOpen(true);
- }
- }, [resultsComplete, index]);
+ }, [isLoading, isError]);
return (
>;
}
export type TErrorLevels = 'success' | 'warning' | 'error';
diff --git a/hyperglass/ui/components/select/styles.tsx b/hyperglass/ui/components/select/styles.tsx
index 2ce0c59..1dc5515 100644
--- a/hyperglass/ui/components/select/styles.tsx
+++ b/hyperglass/ui/components/select/styles.tsx
@@ -27,7 +27,10 @@ export const useControlStyle = (base: TStyles, state: TControl): TStyles => {
);
const focusBorder = useColorValue(useToken('colors', 'blue.500'), useToken('colors', 'blue.300'));
const invalidBorder = useColorValue(useToken('colors', 'red.500'), useToken('colors', 'red.300'));
- const borderColor = useColorValue('inherit', useToken('colors', 'whiteAlpha.50'));
+ const borderColor = useColorValue(
+ useToken('colors', 'gray.100'),
+ useToken('colors', 'whiteAlpha.50'),
+ );
const borderRadius = useToken('radii', 'md');
const minHeight = useToken('space', 12);
const color = useColorValue(useToken('colors', 'black'), useToken('colors', 'whiteAlpha.800'));
@@ -132,11 +135,11 @@ export const useOptionStyle = (base: TStyles, state: TOption): TStyles => {
const color = useOpposingColor(backgroundColor);
const styles = {
- backgroundColor,
- color,
- fontSize,
- '&:focus': { backgroundColor: active, color: activeColor },
+ color: backgroundColor === 'transparent' ? 'currentColor' : color,
'&:active': { backgroundColor: active, color: activeColor },
+ '&:focus': { backgroundColor: active, color: activeColor },
+ backgroundColor,
+ fontSize,
};
return useMemo(() => mergeWith({}, base, styles), [