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
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,108 @@ import type {ViewProps} from '../View/ViewPropTypes';

import Platform from '../../Utilities/Platform';
import View from '../View/View';
import RCTSafeAreaViewNativeComponent from './RCTSafeAreaViewNativeComponent';
import * as React from 'react';

export type SafeAreaViewInstance = HostInstance;

component SafeAreaViewIOS(
ref?: React.RefSetter<SafeAreaViewInstance>,
...props: ViewProps
) {
const {
accessibilityState,
accessibilityValue,
'aria-busy': ariaBusy,
'aria-checked': ariaChecked,
'aria-disabled': ariaDisabled,
'aria-expanded': ariaExpanded,
'aria-hidden': ariaHidden,
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledBy,
'aria-live': ariaLive,
'aria-modal': ariaModal,
'aria-selected': ariaSelected,
'aria-valuemax': ariaValueMax,
'aria-valuemin': ariaValueMin,
'aria-valuenow': ariaValueNow,
'aria-valuetext': ariaValueText,
id,
tabIndex,
...otherProps
} = props;

const resolvedProps = otherProps as {...ViewProps};

const parsedAriaLabelledBy = ariaLabelledBy?.split(/\s*,\s*/g);
if (parsedAriaLabelledBy !== undefined) {
resolvedProps.accessibilityLabelledBy = parsedAriaLabelledBy;
}

if (ariaLabel !== undefined) {
resolvedProps.accessibilityLabel = ariaLabel;
}

if (ariaLive !== undefined) {
resolvedProps.accessibilityLiveRegion =
ariaLive === 'off' ? 'none' : ariaLive;
}

if (ariaModal !== undefined) {
resolvedProps.accessibilityViewIsModal = ariaModal;
}

if (ariaHidden !== undefined) {
resolvedProps.accessibilityElementsHidden = ariaHidden;
}

if (id !== undefined) {
resolvedProps.nativeID = id;
}

if (tabIndex !== undefined) {
resolvedProps.focusable = !tabIndex;
}

if (
accessibilityState != null ||
ariaBusy != null ||
ariaChecked != null ||
ariaDisabled != null ||
ariaExpanded != null ||
ariaSelected != null
) {
resolvedProps.accessibilityState = {
busy: ariaBusy ?? accessibilityState?.busy,
checked: ariaChecked ?? accessibilityState?.checked,
disabled: ariaDisabled ?? accessibilityState?.disabled,
expanded: ariaExpanded ?? accessibilityState?.expanded,
selected: ariaSelected ?? accessibilityState?.selected,
};
}

if (
accessibilityValue != null ||
ariaValueMax != null ||
ariaValueMin != null ||
ariaValueNow != null ||
ariaValueText != null
) {
resolvedProps.accessibilityValue = {
max: ariaValueMax ?? accessibilityValue?.max,
min: ariaValueMin ?? accessibilityValue?.min,
now: ariaValueNow ?? accessibilityValue?.now,
text: ariaValueText ?? accessibilityValue?.text,
};
}

return ref == null ? (
<RCTSafeAreaViewNativeComponent {...resolvedProps} />
) : (
<RCTSafeAreaViewNativeComponent {...resolvedProps} ref={ref} />
);
}

/**
* Renders content within the safe area boundaries of a device. Currently only applicable to iOS devices with iOS version 11 or later. Automatically applies padding to reflect the portion of the view not covered by navigation bars, tab bars, toolbars, and other ancestor views.
*
Expand All @@ -28,7 +126,7 @@ const SafeAreaView: component(
ref?: React.RefSetter<SafeAreaViewInstance>,
...props: ViewProps
) = Platform.select({
ios: require('./RCTSafeAreaViewNativeComponent').default,
ios: SafeAreaViewIOS,
default: View,
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/

'use strict';

import {create} from '@react-native/jest-preset/jest/renderer';
import * as React from 'react';

describe('SafeAreaView on iOS', () => {
beforeEach(() => {
jest.resetModules();
jest.doMock('../../../Utilities/Platform', () => ({
__esModule: true,
default: {
OS: 'ios',
select: (spec: {[string]: unknown}) => spec.ios ?? spec.default,
},
}));
});

it('maps aria props to their accessibility counterparts', async () => {
const SafeAreaView = require('../SafeAreaView').default;
const component = await create(
<SafeAreaView
aria-hidden={true}
aria-label="Label"
aria-busy={true}
aria-valuenow={3}
id="safe"
/>,
);
const props = component.toJSON()?.props;

expect(props?.accessibilityElementsHidden).toBe(true);
expect(props?.accessibilityLabel).toBe('Label');
expect(props?.accessibilityState).toEqual(
expect.objectContaining({busy: true}),
);
expect(props?.accessibilityValue).toEqual(
expect.objectContaining({now: 3}),
);
expect(props?.nativeID).toBe('safe');
expect(props?.['aria-hidden']).toBeUndefined();
expect(props?.['aria-label']).toBeUndefined();
});
});
Loading