From 5f1e5dc80073b9a37f16bf2e36eefa15aeafcef5 Mon Sep 17 00:00:00 2001 From: Srimon Date: Fri, 15 May 2026 06:33:58 +0530 Subject: [PATCH 1/2] Fix truncated error messages and expand image URL detection (#283, #253) --- src/components/Points/PointImage.jsx | 8 +++++++- .../ToastNotifications/ErrorNotifier.jsx | 17 ++++++++++++++--- src/lib/get-error-message.js | 7 ++++++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/components/Points/PointImage.jsx b/src/components/Points/PointImage.jsx index 9a694da01..12bddb1a9 100644 --- a/src/components/Points/PointImage.jsx +++ b/src/components/Points/PointImage.jsx @@ -10,6 +10,11 @@ function PointImage({ data, sx }) { const images = []; function isImgUrl(string) { + // Handle data URIs + if (/^data:image\//.test(string)) { + return true; + } + let url; try { url = new URL(string); @@ -17,7 +22,8 @@ function PointImage({ data, sx }) { return false; } if (url) { - return /\.(jpg|jpeg|png|webp|gif|svg)$/.test(url.pathname); + const pathname = url.pathname.split('?')[0].split('#')[0]; + return /\.(jpg|jpeg|png|webp|gif|svg|bmp|ico|tiff|tif|avif|heic|heif)$/i.test(pathname); } return false; } diff --git a/src/components/ToastNotifications/ErrorNotifier.jsx b/src/components/ToastNotifications/ErrorNotifier.jsx index 75bb88703..ab07ee622 100644 --- a/src/components/ToastNotifications/ErrorNotifier.jsx +++ b/src/components/ToastNotifications/ErrorNotifier.jsx @@ -1,16 +1,27 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useMemo } from 'react'; import PropTypes from 'prop-types'; import { useSnackbar } from 'notistack'; import { getSnackbarOptions } from '../Common/utils/snackbarOptions'; +const MAX_MESSAGE_LENGTH = 250; + +const truncateMessage = (msg) => { + if (msg && msg.length > MAX_MESSAGE_LENGTH) { + return msg.substring(0, MAX_MESSAGE_LENGTH - 3) + '...'; + } + return msg; +}; + export const ErrorNotifier = ({ message = 'Something went wrong', callback }) => { const { enqueueSnackbar, closeSnackbar } = useSnackbar(); const errorSnackbarOptions = getSnackbarOptions('error', closeSnackbar, 6000); + const truncatedMessage = useMemo(() => truncateMessage(message), [message]); + useEffect(() => { - enqueueSnackbar(message, errorSnackbarOptions); + enqueueSnackbar(truncatedMessage, errorSnackbarOptions); typeof callback === 'function' && callback(); - }, [enqueueSnackbar, errorSnackbarOptions, message]); + }, [enqueueSnackbar, errorSnackbarOptions, truncatedMessage]); return null; }; diff --git a/src/lib/get-error-message.js b/src/lib/get-error-message.js index f6d755ae8..ab9fc145e 100644 --- a/src/lib/get-error-message.js +++ b/src/lib/get-error-message.js @@ -27,5 +27,10 @@ export const getErrorMessage = (e, options = {}) => { // error is not instance of ApiError message = e?.message || fallbackMessage; } - return message; + + if (!message || !message.trim()) { + message = fallbackMessage; + } + + return message.trim(); }; From 826b67d2aeb7a88ffe8dbbf3d37f1b278262380e Mon Sep 17 00:00:00 2001 From: Srimon Date: Fri, 15 May 2026 06:54:18 +0530 Subject: [PATCH 2/2] Preserve full error toast messages --- .../ToastNotifications/ErrorNotifier.jsx | 17 ++------- .../ToastNotifications/ErrorNotifier.test.jsx | 25 +++++++++++++ src/lib/get-error-message.test.js | 36 +++++++++++++++++++ 3 files changed, 64 insertions(+), 14 deletions(-) create mode 100644 src/components/ToastNotifications/ErrorNotifier.test.jsx create mode 100644 src/lib/get-error-message.test.js diff --git a/src/components/ToastNotifications/ErrorNotifier.jsx b/src/components/ToastNotifications/ErrorNotifier.jsx index ab07ee622..cf9245f35 100644 --- a/src/components/ToastNotifications/ErrorNotifier.jsx +++ b/src/components/ToastNotifications/ErrorNotifier.jsx @@ -1,27 +1,16 @@ -import React, { useEffect, useMemo } from 'react'; +import React, { useEffect } from 'react'; import PropTypes from 'prop-types'; import { useSnackbar } from 'notistack'; import { getSnackbarOptions } from '../Common/utils/snackbarOptions'; -const MAX_MESSAGE_LENGTH = 250; - -const truncateMessage = (msg) => { - if (msg && msg.length > MAX_MESSAGE_LENGTH) { - return msg.substring(0, MAX_MESSAGE_LENGTH - 3) + '...'; - } - return msg; -}; - export const ErrorNotifier = ({ message = 'Something went wrong', callback }) => { const { enqueueSnackbar, closeSnackbar } = useSnackbar(); const errorSnackbarOptions = getSnackbarOptions('error', closeSnackbar, 6000); - const truncatedMessage = useMemo(() => truncateMessage(message), [message]); - useEffect(() => { - enqueueSnackbar(truncatedMessage, errorSnackbarOptions); + enqueueSnackbar(message, errorSnackbarOptions); typeof callback === 'function' && callback(); - }, [enqueueSnackbar, errorSnackbarOptions, truncatedMessage]); + }, [enqueueSnackbar, errorSnackbarOptions, message, callback]); return null; }; diff --git a/src/components/ToastNotifications/ErrorNotifier.test.jsx b/src/components/ToastNotifications/ErrorNotifier.test.jsx new file mode 100644 index 000000000..02b7d7f81 --- /dev/null +++ b/src/components/ToastNotifications/ErrorNotifier.test.jsx @@ -0,0 +1,25 @@ +import { render, waitFor } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; +import { ErrorNotifier } from './ErrorNotifier'; + +const enqueueSnackbar = vi.fn(); +const closeSnackbar = vi.fn(); + +vi.mock('notistack', () => ({ + useSnackbar: () => ({ + enqueueSnackbar, + closeSnackbar, + }), +})); + +describe('ErrorNotifier', () => { + it('passes the full message to the snackbar', async () => { + const message = 'x'.repeat(300); + + render(); + + await waitFor(() => { + expect(enqueueSnackbar).toHaveBeenCalledWith(message, expect.any(Object)); + }); + }); +}); diff --git a/src/lib/get-error-message.test.js b/src/lib/get-error-message.test.js new file mode 100644 index 000000000..98f2bffb4 --- /dev/null +++ b/src/lib/get-error-message.test.js @@ -0,0 +1,36 @@ +import { describe, expect, it } from 'vitest'; +import { getErrorMessage } from './get-error-message'; + +describe('getErrorMessage', () => { + it('returns the fallback when the extracted API error message is empty', () => { + const error = { + message: 'Request error:', + getActualType: () => ({ + status: 400, + data: { + status: { + error: ' ', + }, + }, + }), + }; + + expect(getErrorMessage(error, { fallbackMessage: 'Something went wrong.' })).toBe('Something went wrong.'); + }); + + it('trims a non-empty API error message', () => { + const error = { + message: 'Request error:', + getActualType: () => ({ + status: 400, + data: { + status: { + error: ' Detailed backend message ', + }, + }, + }), + }; + + expect(getErrorMessage(error)).toBe('Detailed backend message'); + }); +});