Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/NativeSafeAreaProvider.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export function NativeSafeAreaProvider({
children,
style,
onInsetsChange,
unstable_disableViewOnWeb,
}: NativeSafeAreaProviderProps) {
const viewRef = React.useRef<View>(null);

Expand Down Expand Up @@ -107,6 +108,10 @@ export function NativeSafeAreaProvider({
};
}, [onInsetsChange]);

if (unstable_disableViewOnWeb) {
return <>{children}</>;
}

return (
<View ref={viewRef} style={style}>
{children}
Expand Down
6 changes: 6 additions & 0 deletions src/SafeArea.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ export type InsetChangeNativeCallback = (event: InsetChangedEvent) => void;
export interface NativeSafeAreaProviderProps extends ViewProps {
children?: React.ReactNode;
onInsetsChange: InsetChangeNativeCallback;
/**
* Web only, undocumented and unstable. Render children without the wrapping
* view. Since there is no view to measure, insets and frame fall back to
* window values. Has no effect on other platforms.
*/
unstable_disableViewOnWeb?: boolean;
}

export interface NativeSafeAreaViewProps extends ViewProps {
Expand Down
6 changes: 6 additions & 0 deletions src/SafeAreaContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ export interface SafeAreaProviderProps extends ViewProps {
* @deprecated
*/
initialSafeAreaInsets?: EdgeInsets | null;
/**
* Web only, undocumented and unstable. Render children without the wrapping
* view. Since there is no view to measure, insets and frame fall back to
* window values. Has no effect on other platforms.
*/
unstable_disableViewOnWeb?: boolean;
}

export function SafeAreaProvider({
Expand Down
53 changes: 51 additions & 2 deletions src/__tests__/NativeSafeAreaProvider.web-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type {
EdgeInsets,
InsetChangeNativeCallback,
Metrics,
NativeSafeAreaProviderProps,
} from '../SafeArea.types';
import { NativeSafeAreaProvider } from '../NativeSafeAreaProvider.web';

Expand Down Expand Up @@ -123,12 +124,17 @@ let root: Root | null = null;
let host: HTMLElement | null = null;
let rectMock: ReturnType<typeof spyOnBoundingClientRect>;

function mountProvider(onInsetsChange: InsetChangeNativeCallback) {
function mountProvider(
onInsetsChange: InsetChangeNativeCallback,
props?: Partial<NativeSafeAreaProviderProps>,
) {
const newHost = document.createElement('div');
document.body.appendChild(newHost);
const newRoot = createRoot(newHost);
act(() => {
newRoot.render(<NativeSafeAreaProvider onInsetsChange={onInsetsChange} />);
newRoot.render(
<NativeSafeAreaProvider onInsetsChange={onInsetsChange} {...props} />,
);
});
root = newRoot;
host = newHost;
Expand Down Expand Up @@ -281,4 +287,47 @@ describe('NativeSafeAreaProvider.web', () => {
frame: { x: 0, y: 0, width: 800, height: 600 },
});
});

describe('unstable_disableViewOnWeb', () => {
it('wraps children in a view by default', () => {
const onInsetsChange = jest.fn<InsetChangeNativeCallback>();
mountProvider(onInsetsChange, { children: <span id="child" /> });
expect(host?.innerHTML).toBe('<div><span id="child"></span></div>');
});

it('renders children without a wrapping element when enabled', () => {
const onInsetsChange = jest.fn<InsetChangeNativeCallback>();
mountProvider(onInsetsChange, {
unstable_disableViewOnWeb: true,
children: <span id="child" />,
});
expect(host?.innerHTML).toBe('<span id="child"></span>');
});

it('reports window insets and frame when enabled', () => {
// Element rects are still measurable, but there is no provider view to
// measure, so metrics come from the window rather than from this rect.
rectMock.mockReturnValue(makeRect(20, 30, 200, 300));
const onInsetsChange = jest.fn<InsetChangeNativeCallback>();
mountProvider(onInsetsChange, { unstable_disableViewOnWeb: true });
expect(lastMetrics(onInsetsChange)).toEqual({
insets: WINDOW_INSETS,
frame: { x: 0, y: 0, width: WINDOW_WIDTH, height: WINDOW_HEIGHT },
});
});

it('keeps reporting window metrics on resize when enabled', () => {
const onInsetsChange = jest.fn<InsetChangeNativeCallback>();
mountProvider(onInsetsChange, { unstable_disableViewOnWeb: true });
setWindowDimensions(800, 600);
mockWindowInsets({ top: 20, bottom: 10, left: 0, right: 0 });
act(() => {
window.dispatchEvent(new Event('resize'));
});
expect(lastMetrics(onInsetsChange)).toEqual({
insets: { top: 20, bottom: 10, left: 0, right: 0 },
frame: { x: 0, y: 0, width: 800, height: 600 },
});
});
});
});