Skip to content

Commit

Permalink
Update UI dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
thatmattlove committed May 30, 2021
1 parent 4c12f13 commit 69f21a4
Show file tree
Hide file tree
Showing 14 changed files with 756 additions and 673 deletions.
4 changes: 3 additions & 1 deletion hyperglass/ui/components/form/field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ export const FormField: React.FC<TField> = (props: TField) => {
const errorColor = useColorValue('red.500', 'red.300');
const opacity = useBooleanValue(hiddenLabels, 0, undefined);

const { errors } = useFormContext();
const {
formState: { errors },
} = useFormContext();

const error = name in errors && (errors[name] as FieldError);

Expand Down
4 changes: 3 additions & 1 deletion hyperglass/ui/components/form/queryLocation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ export const QueryLocation: React.FC<TQuerySelectField> = (props: TQuerySelectFi
const { onChange, label } = props;

const { networks } = useConfig();
const { errors } = useFormContext();
const {
formState: { errors },
} = useFormContext();
const { selections } = useLGState();
const { exportState } = useLGMethods();

Expand Down
2 changes: 1 addition & 1 deletion hyperglass/ui/components/form/queryTarget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const QueryTarget: React.FC<TQueryTarget> = (props: TQueryTarget) => {

return (
<>
<input hidden readOnly name={name} ref={register} value={queryTarget.value} />
<input {...register} hidden readOnly value={queryTarget.value} />
<If c={queryType.value === 'bgp_community' && queries.bgp_community.mode === 'select'}>
<Select
size="lg"
Expand Down
4 changes: 3 additions & 1 deletion hyperglass/ui/components/form/queryType.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ function buildOptions(queryTypes: TQuery[]): TSelectOption[] {
export const QueryType: React.FC<TQuerySelectField> = (props: TQuerySelectField) => {
const { onChange, label } = props;
const { queries } = useConfig();
const { errors } = useFormContext();
const {
formState: { errors },
} = useFormContext();
const { selections } = useLGState();
const { exportState } = useLGMethods();

Expand Down
8 changes: 4 additions & 4 deletions hyperglass/ui/components/form/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { FormControlProps } from '@chakra-ui/react';
import type { Control } from 'react-hook-form';
import type { TDeviceVrf, TBGPCommunity, OnChangeArgs } from '~/types';
import type { UseFormRegister } from 'react-hook-form';
import type { TDeviceVrf, TBGPCommunity, OnChangeArgs, TFormData } from '~/types';

export interface TField extends FormControlProps {
name: string;
Expand All @@ -25,13 +25,13 @@ export interface TCommunitySelect {
name: string;
onChange: OnChange;
communities: TBGPCommunity[];
register: Control['register'];
register: UseFormRegister<TFormData>;
}

export interface TQueryTarget {
name: string;
placeholder: string;
register: Control['register'];
register: UseFormRegister<TFormData>;
onChange(e: OnChangeArgs): void;
}

Expand Down
4 changes: 2 additions & 2 deletions hyperglass/ui/components/layout/frame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ export const Frame: React.FC<TFrame> = (props: TFrame) => {
>
<Header resetForm={handleReset} />
<Flex
px={2}
px={4}
py={0}
w="100%"
as="main"
align="center"
flex="1 1 auto"
justify="start"
flexDir="column"
textAlign="center"
w={{ base: '100%', lg: 'calc(100% - 1rem)' }}
{...props}
/>
<Footer />
Expand Down
16 changes: 10 additions & 6 deletions hyperglass/ui/components/lookingGlass.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from '~/components';
import { useConfig } from '~/context';
import { useStrf, useGreeting, useDevice, useLGState, useLGMethods } from '~/hooks';
import { isQueryType, isQueryContent, isString } from '~/types';
import { isQueryType, isQueryContent, isString, isQueryField } from '~/types';

import type { TFormData, TDeviceVrf, OnChangeArgs } from '~/types';

Expand Down Expand Up @@ -206,7 +206,11 @@ export const LookingGlass: React.FC = () => {

function handleChange(e: OnChangeArgs): void {
// Signal the field & value to react-hook-form.
setValue(e.field, e.value);
if (isQueryField(e.field)) {
setValue(e.field, e.value);
} else {
throw new Error(`Field '${e.field}' is not a valid form field.`);
}

if (e.field === 'query_location' && Array.isArray(e.value)) {
handleLocChange(e.value);
Expand Down Expand Up @@ -244,10 +248,10 @@ export const LookingGlass: React.FC = () => {
}, [queryVrf.value, queryLocation.value, queryType.value]);

useEffect(() => {
register({ name: 'query_location', required: true });
register({ name: 'query_target', required: true });
register({ name: 'query_type', required: true });
register({ name: 'query_vrf' });
register('query_location', { required: true });
register('query_target', { required: true });
register('query_type', { required: true });
register('query_vrf');
}, [register]);

return (
Expand Down
24 changes: 3 additions & 21 deletions hyperglass/ui/hooks/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { State } from '@hookstate/core';
import type { QueryFunctionContext } from 'react-query';
import type * as ReactGA from 'react-ga';
import type {
TDevice,
Expand All @@ -10,6 +9,9 @@ import type {
TSelectOption,
} from '~/types';

export type LGQueryKey = [string, TFormQuery];
export type DNSQueryKey = [string, { target: string | null; family: 4 | 6 }];

export interface TOpposingOptions {
light?: string;
dark?: string;
Expand All @@ -23,26 +25,6 @@ export type TUseGreetingReturn = {
greetingReady(): boolean;
};

export interface TUseLGQueryFn {
pageParam?: QueryFunctionContext['pageParam'];
queryKey: [string, TFormQuery];
}

export interface TUseASNDetailFn {
pageParam?: QueryFunctionContext['pageParam'];
queryKey: string;
}

interface TUseDNSQueryParams {
target: string;
family: 4 | 6;
}

export interface TUseDNSQueryFn {
pageParam?: QueryFunctionContext['pageParam'];
queryKey: [string | null, TUseDNSQueryParams];
}

export type TUseDevice = (
/**
* Device's ID, e.g. the device.name field.
Expand Down
13 changes: 7 additions & 6 deletions hyperglass/ui/hooks/useASNDetail.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { useQuery } from 'react-query';

import type { QueryObserverResult } from 'react-query';
import type { QueryFunctionContext, QueryObserverResult, QueryFunction } from 'react-query';
import type { TASNQuery } from '~/types';
import type { TUseASNDetailFn } from './types';

async function query(ctx: TUseASNDetailFn): Promise<TASNQuery> {
const [asn] = ctx.queryKey;
const query: QueryFunction<TASNQuery, string> = async (ctx: QueryFunctionContext) => {
const asn = ctx.queryKey;
const res = await fetch('https://api.asrank.caida.org/v2/graphql', {
mode: 'cors',
method: 'POST',
Expand All @@ -14,14 +13,16 @@ async function query(ctx: TUseASNDetailFn): Promise<TASNQuery> {
body: JSON.stringify({ query: `{ asn(asn:\"${asn}\"){ organization { orgName } } }` }),
});
return await res.json();
}
};

/**
* Query the Caida AS Rank API to get an ASN's organization name for the AS Path component.
* @see https://api.asrank.caida.org/v2/docs
*/
export function useASNDetail(asn: string): QueryObserverResult<TASNQuery> {
return useQuery(asn, query, {
return useQuery<TASNQuery, unknown, TASNQuery, string>({
queryKey: asn,
queryFn: query,
refetchOnWindowFocus: false,
refetchInterval: false,
refetchOnMount: false,
Expand Down
14 changes: 9 additions & 5 deletions hyperglass/ui/hooks/useDNSQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import { useConfig } from '~/context';
import { fetchWithTimeout } from '~/util';
import { useGoogleAnalytics } from './useGoogleAnalytics';

import type { QueryObserverResult } from 'react-query';
import type { QueryFunction, QueryFunctionContext, QueryObserverResult } from 'react-query';
import type { DnsOverHttps } from '~/types';
import type { TUseDNSQueryFn } from './types';
import type { DNSQueryKey } from './types';

/**
* Perform a DNS over HTTPS query using the application/dns-json MIME type.
*/
async function dnsQuery(ctx: TUseDNSQueryFn): Promise<DnsOverHttps.Response | undefined> {
const query: QueryFunction<DnsOverHttps.Response, DNSQueryKey> = async (
ctx: QueryFunctionContext<DNSQueryKey>,
) => {
const [url, { target, family }] = ctx.queryKey;

const controller = new AbortController();
Expand All @@ -33,7 +35,7 @@ async function dnsQuery(ctx: TUseDNSQueryFn): Promise<DnsOverHttps.Response | un
}

return json;
}
};

/**
* Query the configured DNS over HTTPS provider for the provided target. If `family` is `4`, only
Expand All @@ -56,7 +58,9 @@ export function useDNSQuery(
trackEvent({ category: 'DNS', action: 'Query', label: target, dimension1: `IPv${family}` });
}

return useQuery([web.dns_provider.url, { target, family }], dnsQuery, {
return useQuery<DnsOverHttps.Response, unknown, DnsOverHttps.Response, DNSQueryKey>({
queryKey: [web.dns_provider.url, { target, family }],
queryFn: query,
cacheTime: cache.timeout * 1000,
});
}
36 changes: 18 additions & 18 deletions hyperglass/ui/hooks/useLGQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { useConfig } from '~/context';
import { useGoogleAnalytics } from './useGoogleAnalytics';
import { fetchWithTimeout } from '~/util';

import type { QueryObserverResult } from 'react-query';
import type { QueryFunction, QueryFunctionContext, QueryObserverResult } from 'react-query';
import type { TFormQuery } from '~/types';
import type { TUseLGQueryFn } from './types';
import type { LGQueryKey } from './types';

/**
* Custom hook handle submission of a query to the hyperglass backend.
Expand All @@ -26,7 +26,9 @@ export function useLGQuery(query: TFormQuery): QueryObserverResult<TQueryRespons
dimension4: query.queryVrf,
});

async function runQuery(ctx: TUseLGQueryFn): Promise<TQueryResponse> {
const runQuery: QueryFunction<TQueryResponse, LGQueryKey> = async (
ctx: QueryFunctionContext<LGQueryKey>,
): Promise<TQueryResponse> => {
const [url, data] = ctx.queryKey;
const { queryLocation, queryTarget, queryType, queryVrf } = data;
const res = await fetchWithTimeout(
Expand All @@ -50,7 +52,7 @@ export function useLGQuery(query: TFormQuery): QueryObserverResult<TQueryRespons
} catch (err) {
throw new Error(res.statusText);
}
}
};

// Cancel any still-running queries on unmount.
useEffect(
Expand All @@ -60,18 +62,16 @@ export function useLGQuery(query: TFormQuery): QueryObserverResult<TQueryRespons
[],
);

return useQuery<TQueryResponse, Response | TQueryResponse | Error>(
['/api/query/', query],
runQuery,
{
// Invalidate react-query's cache just shy of the configured cache timeout.
cacheTime: cache.timeout * 1000 * 0.95,
// Don't refetch when window refocuses.
refetchOnWindowFocus: false,
// Don't automatically refetch query data (queries should be on-off).
refetchInterval: false,
// Don't refetch on component remount.
refetchOnMount: false,
},
);
return useQuery<TQueryResponse, Response | TQueryResponse | Error, TQueryResponse, LGQueryKey>({
queryKey: ['/api/query/', query],
queryFn: runQuery,
// Invalidate react-query's cache just shy of the configured cache timeout.
cacheTime: cache.timeout * 1000 * 0.95,
// Don't refetch when window refocuses.
refetchOnWindowFocus: false,
// Don't automatically refetch query data (queries should be on-off).
refetchInterval: false,
// Don't refetch on component remount.
refetchOnMount: false,
});
}
31 changes: 16 additions & 15 deletions hyperglass/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,42 +18,43 @@
},
"browserslist": "> 0.25%, not dead",
"dependencies": {
"@chakra-ui/react": "^1.5.2",
"@emotion/react": "^11.1.5",
"@chakra-ui/react": "^1.6.3",
"@emotion/react": "^11.4.0",
"@emotion/styled": "^11.3.0",
"@hookform/resolvers": "1.3.0",
"@hookform/devtools": "^3.1.0",
"@hookform/resolvers": "^2.5.1",
"@hookstate/core": "^3.0.7",
"@hookstate/persistence": "^3.0.0",
"@meronex/icons": "^4.0.0",
"dagre": "^0.8.5",
"dayjs": "^1.10.4",
"framer-motion": "^4.1.8",
"lodash": "^4.17.15",
"next": "^10.1.3",
"framer-motion": "^4.1.17",
"lodash": "^4.17.21",
"next": "^10.2.3",
"palette-by-numbers": "^0.1.5",
"react": "^17.0.2",
"react-countdown": "^2.2.1",
"react-device-detect": "^1.15.0",
"react-dom": "^17.0.2",
"react-fast-compare": "^3.2.0",
"react-flow-renderer": "^9.5.2",
"react-flow-renderer": "^9.6.0",
"react-ga": "^3.3.0",
"react-hook-form": "^6.15.4",
"react-hook-form": "^7.7.0",
"react-markdown": "^5.0.3",
"react-query": "^3.6.0",
"react-select": "^4.1.0",
"react-table": "^7.6.2",
"react-query": "^3.16.0",
"react-select": "^4.3.1",
"react-table": "^7.7.0",
"remark-gfm": "^1.0.0",
"string-format": "^2.0.0",
"vest": "^3.1.2"
"vest": "^3.2.3"
},
"devDependencies": {
"@hookstate/devtools": "^3.0.0",
"@types/dagre": "^0.7.44",
"@types/node": "^14.14.41",
"@types/react": "^17.0.3",
"@types/react-select": "^4.0.13",
"@types/react-table": "^7.0.25",
"@types/react-select": "^4.0.15",
"@types/react-table": "^7.7.1",
"@types/string-format": "^2.0.0",
"@typescript-eslint/eslint-plugin": "^4.11.1",
"@typescript-eslint/parser": "^4.11.1",
Expand All @@ -76,6 +77,6 @@
"onchange": "^7.1.0",
"prettier": "^2.2.1",
"prettier-eslint": "^12.0.0",
"typescript": "^4.2.4"
"typescript": "^4.3.2"
}
}
9 changes: 8 additions & 1 deletion hyperglass/ui/types/guards.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint @typescript-eslint/explicit-module-boundary-types: off */
/* eslint @typescript-eslint/no-explicit-any: off */
import type { State } from '@hookstate/core';
import type { TValidQueryTypes, TStringTableData, TQueryResponseString } from './data';
import type { TFormData, TValidQueryTypes, TStringTableData, TQueryResponseString } from './data';
import type { TSelectOption } from './common';
import type { TQueryContent } from './config';

Expand Down Expand Up @@ -57,3 +57,10 @@ export function isState<S>(a: any): a is State<NonNullable<S>> {
}
return result;
}

/**
* Determine if a form field name is a valid form key name.
*/
export function isQueryField(field: string): field is keyof TFormData {
return ['query_location', 'query_type', 'query_vrf', 'query_target'].includes(field);
}
Loading

0 comments on commit 69f21a4

Please sign in to comment.