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
2 changes: 2 additions & 0 deletions DebugView.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './lib/typescript/src/DebugView';
export { default } from './lib/typescript/src/DebugView';
1 change: 1 addition & 0 deletions DebugView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./lib/commonjs/DebugView');
42 changes: 42 additions & 0 deletions docs/docs/api/debug-view.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
sidebar_position: 11
title: DebugView
sidebar_label: DebugView
---

`DebugView` is a development helper that draws the safe area insets of the nearest [`SafeAreaProvider`](./safe-area-provider.mdx) on top of your UI, so you can check visually how your views interact with the safe area.

It is absolutely positioned, fills its parent and never intercepts touches. Each inset is drawn as a translucent band labeled with its value.

Since React Native doesn't tree shake, `DebugView` is not exported from the main entry point. Import it from the `react-native-safe-area-context/DebugView` subpath so it is only bundled when you use it.

### Example

```tsx
import DebugView from 'react-native-safe-area-context/DebugView';

function App() {
return (
<SafeAreaProvider>
<Content />
{__DEV__ ? <DebugView /> : null}
</SafeAreaProvider>
);
}
```

### Props

Accepts all [View](https://reactnative.dev/docs/view) props.

#### `color`

Optional, defaults to `rgba(255, 0, 0, 0.3)`.

Color of the bands drawn over the insets.

#### `showLabels`

Optional, defaults to `true`.

Whether to print the value of each inset (for example `top: 47`) in its band.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
"ios",
"common",
"jest",
"DebugView.js",
"DebugView.d.ts",
"*.podspec",
"Package.swift",
"react-native.config.js",
Expand Down
89 changes: 89 additions & 0 deletions src/DebugView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import * as React from 'react';
import {
StyleSheet,
Text,
View,
type ColorValue,
type ViewProps,
} from 'react-native';
import { useSafeAreaInsets } from './SafeAreaContext';
import type { Edge } from './SafeArea.types';

export interface DebugViewProps extends ViewProps {
/**
* Color of the overlay drawn over the insets. Defaults to a translucent red.
*/
color?: ColorValue;
/**
* Whether to print the inset values. Defaults to `true`.
*/
showLabels?: boolean;
}

const EDGES: readonly Edge[] = ['top', 'right', 'bottom', 'left'];

/**
* Development helper that draws the safe area insets of the nearest
* `SafeAreaProvider` on top of its parent. It is absolutely positioned,
* fills the parent and never intercepts touches.
*
* Not exported from the package entry point so that apps that don't use it
* don't bundle it. Import it from `react-native-safe-area-context/DebugView`.
*/
export function DebugView({
color = 'rgba(255, 0, 0, 0.3)',
showLabels = true,
style,
...others
}: DebugViewProps) {
const insets = useSafeAreaInsets();

return (
<View
pointerEvents="none"
{...others}
style={[StyleSheet.absoluteFill, style]}
>
{EDGES.map((edge) => {
const inset = insets[edge];
const horizontal = edge === 'top' || edge === 'bottom';
return (
<View
key={edge}
testID={`safe-area-debug-${edge}`}
style={[
styles.edge,
{ backgroundColor: color },
horizontal
? { left: 0, right: 0, height: inset }
: { top: 0, bottom: 0, width: inset },
{ [edge]: 0 },
]}
>
{showLabels ? (
<Text style={styles.label} numberOfLines={1}>
{`${edge}: ${inset}`}
</Text>
) : null}
</View>
);
})}
</View>
);
}

const styles = StyleSheet.create({
edge: {
position: 'absolute',
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
},
label: {
fontSize: 10,
color: 'white',
fontWeight: 'bold',
},
});

export default DebugView;
66 changes: 66 additions & 0 deletions src/__tests__/DebugView-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { describe, expect, it } from '@jest/globals';
import { render, screen } from '@testing-library/react-native';
import * as React from 'react';
import { StyleSheet } from 'react-native';
import { DebugView } from '../DebugView';
import type { Metrics } from '../SafeArea.types';
import { SafeAreaProvider } from '../SafeAreaContext';

const INITIAL_METRICS: Metrics = {
insets: { top: 1, left: 2, right: 3, bottom: 4 },
frame: { x: 0, y: 0, height: 100, width: 100 },
};

describe('DebugView', () => {
it('draws every inset with its size and label', () => {
render(
<SafeAreaProvider initialMetrics={INITIAL_METRICS}>
<DebugView />
</SafeAreaProvider>,
);

const top = StyleSheet.flatten(
screen.getByTestId('safe-area-debug-top').props.style,
);
const bottom = StyleSheet.flatten(
screen.getByTestId('safe-area-debug-bottom').props.style,
);
const left = StyleSheet.flatten(
screen.getByTestId('safe-area-debug-left').props.style,
);
const right = StyleSheet.flatten(
screen.getByTestId('safe-area-debug-right').props.style,
);

expect(top).toMatchObject({ top: 0, left: 0, right: 0, height: 1 });
expect(bottom).toMatchObject({ bottom: 0, left: 0, right: 0, height: 4 });
expect(left).toMatchObject({ left: 0, top: 0, bottom: 0, width: 2 });
expect(right).toMatchObject({ right: 0, top: 0, bottom: 0, width: 3 });
expect(screen.getByText('top: 1')).toBeTruthy();
expect(screen.getByText('right: 3')).toBeTruthy();
expect(screen.getByText('bottom: 4')).toBeTruthy();
expect(screen.getByText('left: 2')).toBeTruthy();
});

it('does not intercept touches', () => {
render(
<SafeAreaProvider initialMetrics={INITIAL_METRICS}>
<DebugView testID="debug" />
</SafeAreaProvider>,
);
expect(screen.getByTestId('debug').props.pointerEvents).toBe('none');
});

it('supports a custom color and hiding labels', () => {
render(
<SafeAreaProvider initialMetrics={INITIAL_METRICS}>
<DebugView color="blue" showLabels={false} />
</SafeAreaProvider>,
);
const top = StyleSheet.flatten(
screen.getByTestId('safe-area-debug-top').props.style,
);
expect(top.backgroundColor).toBe('blue');
expect(screen.queryByText('top: 1')).toBeNull();
});
});