From ae3ed808425b1dd954de4a1f88603717bb9a0c20 Mon Sep 17 00:00:00 2001 From: Micael Dias Date: Fri, 31 Oct 2025 18:52:19 +0100 Subject: [PATCH 1/2] fix: web support --- src/components/Toast.tsx | 1 + src/components/Toasts.tsx | 2 +- src/utils/useScreenReader.ts | 8 ++++++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/Toast.tsx b/src/components/Toast.tsx index 1a6d02b..54a421a 100644 --- a/src/components/Toast.tsx +++ b/src/components/Toast.tsx @@ -374,6 +374,7 @@ export const Toast: FC = ({ zIndex: toast.visible ? 9999 : undefined, alignItems: 'center', justifyContent: 'center', + pointerEvents: 'auto', }, style, !toast.disableShadow && ConstructShadow('#181821', 0.15, false), diff --git a/src/components/Toasts.tsx b/src/components/Toasts.tsx index f906f2e..61f3b7e 100644 --- a/src/components/Toasts.tsx +++ b/src/components/Toasts.tsx @@ -92,7 +92,7 @@ export const Toasts: FunctionComponent = ({ left: insets.left + (extraInsets?.left ?? 0), right: insets.right + (extraInsets?.right ?? 0), bottom: insets.bottom + bugFixDelta + (extraInsets?.bottom ?? 0) + 16, - pointerEvents: 'box-none', + pointerEvents: 'none', }} > {toasts.map((t) => ( diff --git a/src/utils/useScreenReader.ts b/src/utils/useScreenReader.ts index 02506af..31217f5 100644 --- a/src/utils/useScreenReader.ts +++ b/src/utils/useScreenReader.ts @@ -1,11 +1,15 @@ import { useEffect, useState } from 'react'; -import { AccessibilityInfo } from 'react-native'; +import { AccessibilityInfo, Platform } from 'react-native'; export const useScreenReader = () => { const [isScreenReaderEnabled, setIsScreenReaderEnabled] = useState(false); useEffect(() => { AccessibilityInfo.isScreenReaderEnabled() - .then(setIsScreenReaderEnabled) + .then((isEnabled) => { + if (Platform.OS !== 'web') { + setIsScreenReaderEnabled(isEnabled); + } + }) .catch(() => { setIsScreenReaderEnabled(false); }); From 17cfd188d1d7fe2dfb986183697c3f48908e7a93 Mon Sep 17 00:00:00 2001 From: Nick DeBaise Date: Sun, 2 Nov 2025 17:52:09 -0500 Subject: [PATCH 2/2] fix: fix interactivity on mobile, add param for screen reader, update docs --- src/components/Toast.tsx | 5 ++++- src/components/Toasts.tsx | 9 +++++++-- src/utils/useScreenReader.ts | 24 ++++++++++++++++++++++-- website/docs/components/toasts.md | 18 ++++++++++++++++++ website/docs/features/accessibility.md | 23 +++++++++++++++++++++++ 5 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/components/Toast.tsx b/src/components/Toast.tsx index 54a421a..5d98dd3 100644 --- a/src/components/Toast.tsx +++ b/src/components/Toast.tsx @@ -374,7 +374,10 @@ export const Toast: FC = ({ zIndex: toast.visible ? 9999 : undefined, alignItems: 'center', justifyContent: 'center', - pointerEvents: 'auto', + pointerEvents: Platform.select({ + web: 'auto', + default: undefined, + }), }, style, !toast.disableShadow && ConstructShadow('#181821', 0.15, false), diff --git a/src/components/Toasts.tsx b/src/components/Toasts.tsx index 61f3b7e..5870a52 100644 --- a/src/components/Toasts.tsx +++ b/src/components/Toasts.tsx @@ -25,6 +25,7 @@ import { useKeyboard } from '../utils'; type Props = { overrideDarkMode?: boolean; + overrideScreenReaderEnabled?: boolean; extraInsets?: ExtraInsets; onToastShow?: (toast: T) => void; onToastHide?: (toast: T, reason?: DismissReason) => void; @@ -46,6 +47,7 @@ type Props = { export const Toasts: FunctionComponent = ({ overrideDarkMode, + overrideScreenReaderEnabled, extraInsets, onToastHide, onToastPress, @@ -69,7 +71,7 @@ export const Toasts: FunctionComponent = ({ const insets = useSafeAreaInsets(); const safeAreaFrame = useSafeAreaFrame(); const dimensions = useWindowDimensions(); - const isScreenReaderEnabled = useScreenReader(); + const isScreenReaderEnabled = useScreenReader(overrideScreenReaderEnabled); const { keyboardShown: keyboardVisible, keyboardHeight } = useKeyboard(); // Fix for Android bottom inset bug: https://github.com/facebook/react-native/issues/47080 @@ -92,7 +94,10 @@ export const Toasts: FunctionComponent = ({ left: insets.left + (extraInsets?.left ?? 0), right: insets.right + (extraInsets?.right ?? 0), bottom: insets.bottom + bugFixDelta + (extraInsets?.bottom ?? 0) + 16, - pointerEvents: 'none', + pointerEvents: Platform.select({ + web: 'none', + default: 'box-none', + }), }} > {toasts.map((t) => ( diff --git a/src/utils/useScreenReader.ts b/src/utils/useScreenReader.ts index 31217f5..6c68919 100644 --- a/src/utils/useScreenReader.ts +++ b/src/utils/useScreenReader.ts @@ -1,7 +1,27 @@ import { useEffect, useState } from 'react'; import { AccessibilityInfo, Platform } from 'react-native'; -export const useScreenReader = () => { +/** + * Hook to detect if a screen reader is enabled on the device. + * + * @param override - Optional boolean to override the detected screen reader state. + * When provided, this value will be returned instead of the detected state. + * Useful for testing or forcing a specific screen reader state. + * @returns {boolean} True if screen reader is enabled (or overridden to true), false otherwise. + * + * @example + * // Auto-detect screen reader + * const isScreenReaderEnabled = useScreenReader(); + * + * @example + * // Force screen reader to be enabled + * const isScreenReaderEnabled = useScreenReader(true); + * + * @example + * // Force screen reader to be disabled + * const isScreenReaderEnabled = useScreenReader(false); + */ +export const useScreenReader = (override?: boolean) => { const [isScreenReaderEnabled, setIsScreenReaderEnabled] = useState(false); useEffect(() => { AccessibilityInfo.isScreenReaderEnabled() @@ -14,7 +34,7 @@ export const useScreenReader = () => { setIsScreenReaderEnabled(false); }); }, []); - return isScreenReaderEnabled; + return override !== undefined ? override : isScreenReaderEnabled; }; export const announceForAccessibility = (message: string) => { diff --git a/website/docs/components/toasts.md b/website/docs/components/toasts.md index 0322b82..a98f6f1 100644 --- a/website/docs/components/toasts.md +++ b/website/docs/components/toasts.md @@ -73,6 +73,24 @@ toast("Quick message", { duration: 2000 }); Override the system dark mode. If a value is supplied (I.e. `true` or `false`), then the toast components will use that value for the dark mode. For example, if `overrideDarkMode = {false}`, dark mode will be disabled, regardless of the system's preferences. +### overrideScreenReaderEnabled +`boolean | undefined` + +Override the detected screen reader state. When set, this forces the component to behave as if the screen reader is enabled or disabled, regardless of the actual device state. + +- `true`: Component behaves as if screen reader is enabled (toasts will be hidden unless `preventScreenReaderFromHiding` is also `true`) +- `false`: Component behaves as if screen reader is disabled (toasts will always be shown) +- `undefined` (default): Auto-detect screen reader state from the device + +This is useful for testing or forcing specific behavior in your app. + +```js +// Force toasts to show even if screen reader is enabled + +``` + +See the [Accessibility](/features/accessibility#overriding-screen-reader-detection) section for more details. + ### extraInsets `object` diff --git a/website/docs/features/accessibility.md b/website/docs/features/accessibility.md index 2328f23..a00ae23 100644 --- a/website/docs/features/accessibility.md +++ b/website/docs/features/accessibility.md @@ -11,3 +11,26 @@ Bottom-positioned toasts will automatically move with the keyboard. This behavio ## Screen Reader Support If the device's screen reader is enabled, react-native-toast will not show the toast. It will announce the toast message to the user using the announceAccessibility method. + +### Overriding Screen Reader Detection + +You can override the detected screen reader state using the `overrideScreenReaderEnabled` prop on the `` component. This is useful for testing or when you want to force a specific behavior regardless of the actual screen reader state. + +```js +// Force the component to behave as if screen reader is disabled + +``` + +```js +// Force the component to behave as if screen reader is enabled + +``` + +When `overrideScreenReaderEnabled` is set: +- `true`: Toasts will be hidden (unless `preventScreenReaderFromHiding` is also `true`) +- `false`: Toasts will always be shown, even if a screen reader is actually enabled +- `undefined` (default): Auto-detect the screen reader state from the device + +This prop works independently from `preventScreenReaderFromHiding`: +- If you set `overrideScreenReaderEnabled={true}` and `preventScreenReaderFromHiding={true}`, toasts will still be shown +- If you set `overrideScreenReaderEnabled={false}`, toasts will always be shown regardless of `preventScreenReaderFromHiding`