feat: extending delay time for error toasts with retry fn (#67)

This commit is contained in:
Jacobo Dominguez
2026-02-18 15:37:28 -06:00
committed by GitHub
parent 98e6d808dc
commit 8c4eeb2c09
4 changed files with 55 additions and 4 deletions

View File

@@ -83,9 +83,11 @@ export const useAssignTeamMembersRole = () => {
mutationFn: async ({ data }: {
data: AssignTeamMembersRoleRequest
}) => assignTeamMembersRole(data),
onSettled: (_data, _error, { data: { scope } }) => {
queryClient.invalidateQueries({ queryKey: authzQueryKeys.teamMembersAll(scope) });
queryClient.invalidateQueries({ queryKey: authzQueryKeys.permissionsByRole(scope) });
onSettled: (_data, error, { data: { scope } }) => {
if (!error) {
queryClient.invalidateQueries({ queryKey: authzQueryKeys.teamMembersAll(scope) });
queryClient.invalidateQueries({ queryKey: authzQueryKeys.permissionsByRole(scope) });
}
},
});
};

View File

@@ -213,4 +213,44 @@ describe('ToastManagerContext', () => {
expect(screen.queryByText('Default delay toast')).not.toBeInTheDocument();
}, { timeout: 5050 });
}, 5100);
it('uses longer delay for error toasts with retry functionality', async () => {
const user = userEvent.setup();
const retryFn = jest.fn();
const RetryErrorDelayTestComponent = () => {
const { showErrorToast } = useToastManager();
const handleShowRetryErrorToast = () => showErrorToast(
{ customAttributes: { httpErrorStatus: 500 } },
retryFn,
);
return (
<button type="button" onClick={handleShowRetryErrorToast}>
Show Retry Error Toast
</button>
);
};
renderWrapper(
<ToastManagerProvider>
<RetryErrorDelayTestComponent />
</ToastManagerProvider>,
);
const showButton = screen.getByText('Show Retry Error Toast');
await user.click(showButton);
await waitFor(() => {
expect(screen.getByRole('alert')).toBeInTheDocument();
expect(screen.getByText('Retry')).toBeInTheDocument();
});
await waitFor(() => {
expect(screen.getByRole('alert')).toBeInTheDocument();
}, { timeout: 5050 });
expect(logError).toHaveBeenCalled();
});
});

View File

@@ -5,7 +5,7 @@ import { logError } from '@edx/frontend-platform/logging';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Toast } from '@openedx/paragon';
import messages from './messages';
import { DEFAULT_TOAST_DELAY } from './constants';
import { DEFAULT_TOAST_DELAY, RETRY_TOAST_DELAY } from './constants';
type ToastType = 'success' | 'error' | 'error-retry';
@@ -68,11 +68,19 @@ export const ToastManagerProvider = ({ children }: ToastManagerProviderProps) =>
const errorStatus = error?.customAttributes?.httpErrorStatus;
const toastConfig = ERROR_TOAST_MAP[errorStatus] || ERROR_TOAST_MAP.DEFAULT;
const message = intl.formatMessage(messages[toastConfig.messageId], { Bold, Br });
/**
* For retryable errors, we set a longer delay to give users more time to read the message
* and decide to retry, while for non-retryable errors we use the default delay.
* Since current toast implementation does not allow disabling the autohide prop,
* we use a longer delay for retryable errors to give users more time to read the message.
*/
const delay = toastConfig.type === 'error-retry' && retryFn ? RETRY_TOAST_DELAY : DEFAULT_TOAST_DELAY;
showToast({
message,
type: toastConfig.type,
onRetry: toastConfig.type === 'error-retry' && retryFn ? retryFn : undefined,
delay,
});
};

View File

@@ -51,6 +51,7 @@ export const libraryPermissions: PermissionMetadata[] = [
];
export const DEFAULT_TOAST_DELAY = 5000;
export const RETRY_TOAST_DELAY = 120_000; // 2 minutes
export const SKELETON_ROWS = Array.from({ length: 10 }).map(() => ({
username: 'skeleton',
name: '',