diff --git a/.changeset/fluffy-wombats-teach.md b/.changeset/fluffy-wombats-teach.md new file mode 100644 index 000000000..e08d25e27 --- /dev/null +++ b/.changeset/fluffy-wombats-teach.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/widgets': patch +--- + +Prevent propagation of form submissions from ChainSearchMenu diff --git a/typescript/widgets/package.json b/typescript/widgets/package.json index f21f536b2..f4a4d2d9d 100644 --- a/typescript/widgets/package.json +++ b/typescript/widgets/package.json @@ -86,7 +86,7 @@ "build:ts": "tsc", "build:css": "tailwindcss -c ./tailwind.config.cjs -i ./src/styles.css -o ./dist/styles.css --minify", "clean": "rm -rf ./dist ./cache ./storybook-static", - "lint": "eslint ./src --ext .ts", + "lint": "eslint ./src --ext '.ts,.tsx'", "prettier": "prettier --write ./src", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build" diff --git a/typescript/widgets/src/chains/ChainAddMenu.tsx b/typescript/widgets/src/chains/ChainAddMenu.tsx index 102b1bf5e..9b3c4065c 100644 --- a/typescript/widgets/src/chains/ChainAddMenu.tsx +++ b/typescript/widgets/src/chains/ChainAddMenu.tsx @@ -21,6 +21,7 @@ import { CopyButton } from '../components/CopyButton.js'; import { LinkButton } from '../components/LinkButton.js'; import { ChevronIcon } from '../icons/Chevron.js'; import { PlusIcon } from '../icons/Plus.js'; +import { widgetLogger } from '../logger.js'; export interface ChainAddMenuProps { chainMetadata: ChainMap; @@ -143,7 +144,7 @@ function tryParseMetadataInput( const result = ChainMetadataSchema.safeParse(parsed.data); if (!result.success) { - console.error('Error validating chain config', result.error); + widgetLogger.error('Error validating chain config', result.error); const firstIssue = result.error.issues[0]; return failure(`${firstIssue.path} => ${firstIssue.message}`); } diff --git a/typescript/widgets/src/chains/ChainLogo.tsx b/typescript/widgets/src/chains/ChainLogo.tsx index 082c8eaf3..0f58c66f1 100644 --- a/typescript/widgets/src/chains/ChainLogo.tsx +++ b/typescript/widgets/src/chains/ChainLogo.tsx @@ -4,6 +4,7 @@ import type { IRegistry } from '@hyperlane-xyz/registry'; import { Circle } from '../icons/Circle.js'; import { QuestionMarkIcon } from '../icons/QuestionMark.js'; +import { widgetLogger } from '../logger.js'; type SvgIcon = (props: { width: number; @@ -41,8 +42,8 @@ export function ChainLogo({ registry .getChainLogoUri(chainName) .then((uri) => uri && setSvgLogos({ ...svgLogos, [chainName]: uri })) - .catch((err) => console.error(err)); - }, [chainName, registry, svgLogos, Icon]); + .catch((err) => widgetLogger.error('Error fetching log uri', err)); + }, [chainName, logoUri, registry, svgLogos, Icon]); if (!uri && !Icon) { return ( diff --git a/typescript/widgets/src/chains/ChainSearchMenu.tsx b/typescript/widgets/src/chains/ChainSearchMenu.tsx index 3d88152f5..59532e9f6 100644 --- a/typescript/widgets/src/chains/ChainSearchMenu.tsx +++ b/typescript/widgets/src/chains/ChainSearchMenu.tsx @@ -89,7 +89,7 @@ export function ChainSearchMenu({ overrideChainMetadata, ); return { mergedMetadata, listData: Object.values(mergedMetadata) }; - }, [chainMetadata]); + }, [chainMetadata, overrideChainMetadata]); const { ListComponent, searchFn, sortOptions, defaultSortState } = useCustomizedListItems(customListItemField, defaultSortField); @@ -297,7 +297,7 @@ function useCustomizedListItems( ({ data }: { data: ChainMetadata<{ disabled?: boolean }> }) => ( ), - [ChainListItem, customListItemField], + [customListItemField], ); // Bind the custom field to the search function diff --git a/typescript/widgets/src/components/ErrorBoundary.tsx b/typescript/widgets/src/components/ErrorBoundary.tsx index a21611ec0..79a7ce15e 100644 --- a/typescript/widgets/src/components/ErrorBoundary.tsx +++ b/typescript/widgets/src/components/ErrorBoundary.tsx @@ -3,6 +3,7 @@ import React, { Component, PropsWithChildren, ReactNode } from 'react'; import { errorToString } from '@hyperlane-xyz/utils'; import { ErrorIcon } from '../icons/Error.js'; +import { widgetLogger } from '../logger.js'; type Props = PropsWithChildren<{ supportLink?: ReactNode; @@ -24,7 +25,7 @@ export class ErrorBoundary extends Component { error, errorInfo, }); - console.error('Error caught by error boundary', error, errorInfo); + widgetLogger.error('Error caught by error boundary', error, errorInfo); } render() { diff --git a/typescript/widgets/src/components/SearchMenu.tsx b/typescript/widgets/src/components/SearchMenu.tsx index 831b46e74..44c1c2c79 100644 --- a/typescript/widgets/src/components/SearchMenu.tsx +++ b/typescript/widgets/src/components/SearchMenu.tsx @@ -102,12 +102,13 @@ export function SearchMenu< const handleSubmit = useCallback( (e: React.FormEvent) => { e.preventDefault(); + e.stopPropagation(); if (results.length === 1) { const item = results[0]; isEditMode ? onClickEditItem(item) : onClickItem(item); } }, - [results, isEditMode], + [results, isEditMode, onClickEditItem, onClickItem], ); useEffect(() => { @@ -254,6 +255,7 @@ function SortDropdown({ buttonClassname="htw-flex htw-items-stretch hover:htw-bg-gray-100 active:htw-scale-95" menuClassname="htw-py-1.5 htw-px-2 htw-flex htw-flex-col htw-gap-2 htw-text-sm htw-border htw-border-gray-100" menuItems={options.map((o) => ( + // eslint-disable-next-line react/jsx-key
onSetSortBy(o)} @@ -298,7 +300,7 @@ function FilterDropdown({ (k) => !deepEquals(value[k], defaultValue[k]), ); return modifiedKeys.map((k) => value[k]); - }, [value]); + }, [value, defaultValue]); const hasFilters = filterValues.length > 0; const onClear = () => { diff --git a/typescript/widgets/src/icons/Web.tsx b/typescript/widgets/src/icons/Web.tsx index 8915ed026..228087f5c 100644 --- a/typescript/widgets/src/icons/Web.tsx +++ b/typescript/widgets/src/icons/Web.tsx @@ -57,9 +57,9 @@ function _Web({ color = ColorPalette.Black, ...rest }: DefaultIconProps) { /> );