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: 4 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { StyleProp, ViewStyle } from "react-native";
import { StyleProp, ViewStyle, AccessibilityRole } from "react-native";
import { Transform, IconProp } from "@fortawesome/fontawesome-svg-core";

export type FontAwesomeIconStyle = StyleProp<ViewStyle> & {
Expand All @@ -24,6 +24,9 @@ export interface Props {
transform?: string | Transform;
style?: FontAwesomeIconStyle;
testID?: string;
accessible?: boolean;
accessibilityRole?: AccessibilityRole;
accessibilityLabel?: string;
}

export function FontAwesomeIcon(props: Props): JSX.Element;
23 changes: 22 additions & 1 deletion src/components/FontAwesomeIcon.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,21 @@ export default function FontAwesomeIcon (props) {
rootAttributes.width = resolvedWidth
rootAttributes.style = modifiedStyle

// Add accessibility properties to the root SVG element
// atleast accessibilityLabel prop should be passed to make the icon accessible
if (_props.accessibilityLabel && _props.accessible !== false) {
rootAttributes.accessible = true
rootAttributes.accessibilityLabel = _props.accessibilityLabel

if (_props.accessibilityRole) {
rootAttributes.accessibilityRole = _props.accessibilityRole
} else {
// If accessibilityLabel is set, we default to 'image' role if not already set
// This is to ensure that screen readers can announce the icon correctly
rootAttributes.accessibilityRole = 'image'
}
}

replaceCurrentColor(abstract[0], color, secondaryColor, secondaryOpacity)

return convertCurry(abstract[0])
Expand Down Expand Up @@ -147,7 +162,13 @@ FontAwesomeIcon.propTypes = {

maskId: PropTypes.string,

transform: PropTypes.oneOfType([PropTypes.string, PropTypes.object])
transform: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),

accessible: PropTypes.bool,

accessibilityRole: PropTypes.string,

accessibilityLabel: PropTypes.string
}

const convertCurry = convert.bind(null, React.createElement)
Expand Down
58 changes: 58 additions & 0 deletions src/components/__tests__/FontAwesomeIcon.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,64 @@ describe('when extra props are given', () => {
})
})

describe('when accessibility props are given', () => {
test('accessibility is automatically enabled when accessibilityLabel is provided', () => {
const tree = renderer.create(<FontAwesomeIcon icon={ faCoffee } accessibilityLabel="Coffee icon" />).toJSON()

expect(tree.props).toHaveProperty('accessible', true)
expect(tree.props).toHaveProperty('accessibilityLabel', 'Coffee icon')
expect(tree.props).toHaveProperty('accessibilityRole', 'image') // defaults to 'image'
})

test('custom accessibilityRole is applied when provided with accessibilityLabel', () => {
const tree = renderer.create(<FontAwesomeIcon icon={ faCoffee } accessibilityLabel="Coffee icon" accessibilityRole="button" />).toJSON()

expect(tree.props).toHaveProperty('accessible', true)
expect(tree.props).toHaveProperty('accessibilityLabel', 'Coffee icon')
expect(tree.props).toHaveProperty('accessibilityRole', 'button')
})

test('no accessibility props when accessibilityLabel is not provided', () => {
const tree = renderer.create(<FontAwesomeIcon icon={ faCoffee } />).toJSON()

expect(tree.props).not.toHaveProperty('accessible')
expect(tree.props).not.toHaveProperty('accessibilityLabel')
expect(tree.props).not.toHaveProperty('accessibilityRole')
})

test('accessibility is disabled when accessible is explicitly set to false', () => {
const tree = renderer.create(<FontAwesomeIcon icon={ faCoffee } accessible={false} accessibilityLabel="Coffee icon" accessibilityRole="image" />).toJSON()

expect(tree.props).not.toHaveProperty('accessible')
expect(tree.props).not.toHaveProperty('accessibilityLabel')
expect(tree.props).not.toHaveProperty('accessibilityRole')
})

test('accessibility works when explicitly enabled with accessible=true', () => {
const tree = renderer.create(<FontAwesomeIcon icon={ faCoffee } accessible={true} accessibilityLabel="Coffee icon" accessibilityRole="image" />).toJSON()

expect(tree.props).toHaveProperty('accessible', true)
expect(tree.props).toHaveProperty('accessibilityLabel', 'Coffee icon')
expect(tree.props).toHaveProperty('accessibilityRole', 'image')
})

test('accessibilityRole only applied when accessibilityLabel is provided', () => {
const tree = renderer.create(<FontAwesomeIcon icon={ faCoffee } accessibilityRole="button" />).toJSON()

expect(tree.props).not.toHaveProperty('accessible')
expect(tree.props).not.toHaveProperty('accessibilityLabel')
expect(tree.props).not.toHaveProperty('accessibilityRole')
})

test('accessibility defaults to image role when only accessibilityLabel is provided', () => {
const tree = renderer.create(<FontAwesomeIcon icon={ faCoffee } accessibilityLabel="Coffee icon" />).toJSON()

expect(tree.props).toHaveProperty('accessible', true)
expect(tree.props).toHaveProperty('accessibilityLabel', 'Coffee icon')
expect(tree.props).toHaveProperty('accessibilityRole', 'image')
})
})

describe('focusable attribute', () => {
test('is never used to render elements', () => {
renderer.create(<FontAwesomeIcon icon={faCoffee} />).toJSON()
Expand Down
7 changes: 6 additions & 1 deletion src/converter.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,18 @@ function convert (createElement, element) {
const val = element.attributes[key]
switch (key) {
case 'class':
case 'role':
case 'xmlns':
delete element.attributes[key]
break
case 'focusable':
acc.attrs[key] = val === 'true'
break
case 'role':
case 'accessible':
case 'accessibilityRole':
case 'accessibilityLabel':
acc.attrs[key] = val
break
default:
if (key.indexOf('aria-') === 0 || key.indexOf('data-') === 0 || (key === 'fill' && val === 'currentColor')) {
delete element.attributes[key]
Expand Down