Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
<PROJECT_ROOT>/lib/.*
<PROJECT_ROOT>/docs/.*
<PROJECT_ROOT>/reports/.*

[options]
esproposal.optional_chaining=enable
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ ruleTester.run('has-valid-accessibility-ignores-invert-colors', rule, {
<Image accessibilityIgnoresInvertColors={invertColors} />
);`,
},
{
code: `<Image accessibilityIgnoresInvertColors={shouldInvert ? true : false} />`,
},
].map(parserOptionsMapper),
invalid: [
{
Expand Down
4 changes: 4 additions & 0 deletions __tests__/src/rules/has-valid-accessibility-states-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ ruleTester.run('has-valid-accessibility-states', rule, {
},
{ code: '<TouchableOpacity accessibilityStates={[""]} />;' },
{ code: '<TouchableOpacity accessibilityStates={[]} />;' },
{
code:
'<TouchableHighlight accessibilityStates={props.disabled ? ["disabled"]: []} />',
},
].map(parserOptionsMapper),
invalid: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ ruleTester.run('has-valid-important-for-accessibility', rule, {
{ code: '<View importantForAccessibility="yes"/>' },
{ code: '<View importantForAccessibility="no"/>' },
{ code: '<View importantForAccessibility="no-hide-descendants"/>' },
{
code:
'<View importantForAccessibility={isHidden ? "no-hide-descendants" : "yes"} />',
},
].map(parserOptionsMapper),
invalid: [
{
Expand Down
36 changes: 20 additions & 16 deletions src/factory/valid-prop.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { elementType, getLiteralPropValue } from 'jsx-ast-utils';
import { generateObjSchema } from '../util/schemas';
import type { ESLintContext } from '../../flow/eslint';
import isOneOf from '../util/isOneOf';
import isNodePropExpression from '../util/isNodePropExpression';

/**
* Produces an ESLint rule that validates a prop against an array of acceptable values
Expand All @@ -30,24 +31,27 @@ const createValidPropRule = (
JSXAttribute: (node: JSXAttribute) => {
const attrName = elementType(node);
if (attrName === propName) {
// ensure we are only checking literal prop values
const attrValue = getLiteralPropValue(node);
let invalid = false;
const isExpression = isNodePropExpression(node);
if (!isExpression) {
// ensure we are only checking literal prop values
const attrValue = getLiteralPropValue(node);
let invalid = false;

if (Array.isArray(attrValue)) {
const validate = attrValue.map((strValue) =>
isOneOf(strValue, validValues)
);
invalid = validate.indexOf(false) > -1;
} else {
invalid = !isOneOf(attrValue, validValues);
}
if (Array.isArray(attrValue)) {
const validate = attrValue.map((strValue) =>
isOneOf(strValue, validValues)
);
invalid = validate.indexOf(false) > -1;
} else {
invalid = !isOneOf(attrValue, validValues);
}

if (invalid) {
context.report({
node,
message: errorMessage,
});
if (invalid) {
context.report({
node,
message: errorMessage,
});
}
}
}
},
Expand Down
11 changes: 11 additions & 0 deletions src/util/isNodePropExpression.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// @flow
import type { JSXAttribute } from 'ast-types-flow';

const ALLOWED_TYPES = ['Identifier', 'ConditionalExpression'];

export default function isattrPropExpression(attr: JSXAttribute): boolean {
// $FlowFixMe
const expression = attr.value?.expression;
// $FlowFixMe
return expression && ALLOWED_TYPES.includes(expression.type);
}
39 changes: 13 additions & 26 deletions src/util/isNodePropValueBoolean.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @flow
import type { JSXAttribute } from 'ast-types-flow';
import isNodePropExpression from './isNodePropExpression';

export default function isattrPropValueBoolean(attr: JSXAttribute): boolean {
/**
Expand All @@ -9,34 +10,20 @@ export default function isattrPropValueBoolean(attr: JSXAttribute): boolean {
* of the boolean, so that we can correctly identify this error.
*/

if (typeof attr !== 'object' || !attr.hasOwnProperty('value')) {
// Loose check for correct data being passed in to this function
throw new Error('isattrPropValueBoolean expects a attr object as argument');
}

const { value } = attr;

if (value === null) {
const isExpression = isNodePropExpression(attr);
if (isExpression || attr.value === null) {
// we can't determine the resulting value type of JSXExpressions
// attr.value is null when it is declared as a prop but not equal to anything. This defaults to `true` in JSX
return true;
}

if (!value || !value.expression) {
return false;
}

const { expression } = value;

if (expression.type === 'Identifier') {
// we can't determine the associated value type of an Identifier expression
// treat these cases as though they are valid
return true;
} else {
// $FlowFixMe
const { expression } = attr.value;
if (expression?.type !== 'Literal') {
// If not a literal, it cannot be a boolean
return false;
}
// $FlowFixMe
return typeof expression?.value === 'boolean';
}

if (expression.type !== 'Literal') {
// If not a literal, it cannot be a boolean
return false;
}
// $FlowFixMe
return typeof expression.value === 'boolean';
}