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 @@ -67,7 +67,7 @@ static BOOL RCTViewIsInteractiveAccessibilityElement(UIView *view, const ViewPro

// `role` wins over the legacy `accessibilityRole` when both are set, matching
// how the traits themselves are resolved.
if (props.role != Role::None) {
if (props.role.value_or(Role::None) != Role::None) {
static const std::unordered_set<Role> interactiveRoles{
Role::Button,
Role::Checkbox,
Expand All @@ -82,7 +82,7 @@ static BOOL RCTViewIsInteractiveAccessibilityElement(UIView *view, const ViewPro
Role::Switch,
Role::Tab,
Role::Treeitem};
return interactiveRoles.contains(props.role);
return interactiveRoles.contains(*props.role);
}

static const std::unordered_set<std::string_view> interactiveAccessibilityRoles{
Expand Down Expand Up @@ -1690,8 +1690,9 @@ - (NSString *)accessibilityValue
}

NSMutableArray *valueComponents = [NSMutableArray new];
NSString *roleString = (props.role != Role::None) ? [NSString stringWithUTF8String:toString(props.role).c_str()]
: [NSString stringWithUTF8String:props.accessibilityRole.c_str()];
NSString *roleString = (props.role.value_or(Role::None) != Role::None)
? [NSString stringWithUTF8String:toString(*props.role).c_str()]
: [NSString stringWithUTF8String:props.accessibilityRole.c_str()];

// In iOS, checkbox and radio buttons aren't recognized as traits. However,
// because our apps use checkbox and radio buttons often, we should announce
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ namespace facebook::react {
// members. This makes accessibilityTraits the single source of truth.
// See: github.com/facebook/react-native/issues/57515
static AccessibilityTraits deriveAccessibilityTraits(
Role role,
std::optional<Role> role,
const std::string& accessibilityRole,
const std::optional<AccessibilityState>& accessibilityState) {
AccessibilityTraits traits = AccessibilityTraits::None;
if (role != Role::None) {
fromString(toString(role), traits);
if (role.value_or(Role::None) != Role::None) {
fromString(toString(*role), traits);
} else if (!accessibilityRole.empty()) {
fromString(accessibilityRole, traits);
}
Expand Down Expand Up @@ -179,16 +179,15 @@ AccessibilityProps::AccessibilityProps(
auto* accessibilityRoleValue = rawProps.at("accessibilityRole");
auto* roleValue = rawProps.at("role");

if (accessibilityRoleValue == nullptr ||
!accessibilityRoleValue->hasValue()) {
if (accessibilityRoleValue == nullptr) {
accessibilityRole = sourceProps.accessibilityRole;
} else {
} else if (accessibilityRoleValue->hasValue()) {
fromRawValue(context, *accessibilityRoleValue, accessibilityRole);
}

if (roleValue == nullptr || !roleValue->hasValue()) {
if (roleValue == nullptr) {
role = sourceProps.role;
} else {
} else if (roleValue->hasValue()) {
fromRawValue(context, *roleValue, role);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class AccessibilityProps {
bool onAccessibilityEscape{};
bool onAccessibilityAction{};
ImportantForAccessibility importantForAccessibility{ImportantForAccessibility::Auto};
Role role{Role::None};
std::optional<Role> role{};
std::string testId;

#pragma mark - DebugStringConvertible
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -395,58 +395,39 @@ inline static void updateNativeDrawableProp(
result[propName] = nativeDrawableResult;
}

inline static void updateAccessibilityStateProp(
folly::dynamic& result,
const std::optional<AccessibilityState>& newState,
const std::optional<AccessibilityState>& oldState) {
folly::dynamic resultState = folly::dynamic::object();

if (!newState.has_value() && oldState.has_value()) {
result["accessibilityState"] = resultState;
return;
}
static folly::dynamic toDynamic(const AccessibilityState& state) {
folly::dynamic result = folly::dynamic::object();
result["disabled"] = state.disabled;

if (!oldState.has_value() || newState->disabled != oldState->disabled) {
resultState["disabled"] = newState->disabled;
// Omitting the key when `selected` is unset is what tells the platform the
// component is not selectable. BaseViewManager#setViewState falls back to
// `setSelected(false)` for an absent key, so the rendered result is
// unchanged from when this was a plain `bool`.
if (state.selected.has_value()) {
result["selected"] = state.selected.value();
}

if (!oldState.has_value() || newState->selected != oldState->selected) {
// Omitting the key when `selected` is unset is what tells the platform the
// component is not selectable. BaseViewManager#setViewState falls back to
// `setSelected(false)` for an absent key, so the rendered result is
// unchanged from when this was a plain `bool`.
if (newState->selected.has_value()) {
resultState["selected"] = newState->selected.value();
}
}
result["busy"] = state.busy;

if (!oldState.has_value() || newState->busy != oldState->busy) {
resultState["busy"] = newState->busy;
if (state.expanded.has_value()) {
result["expanded"] = state.expanded.value();
}

if (!oldState.has_value() || newState->expanded != oldState->expanded) {
if (newState->expanded.has_value()) {
resultState["expanded"] = newState->expanded.value();
}
switch (state.checked) {
case AccessibilityState::Unchecked:
result["checked"] = false;
break;
case AccessibilityState::Checked:
result["checked"] = true;
break;
case AccessibilityState::Mixed:
result["checked"] = "mixed";
break;
case AccessibilityState::None:
result["checked"] = "none";
break;
}

if (!oldState.has_value() || newState->checked != oldState->checked) {
switch (newState->checked) {
case AccessibilityState::Unchecked:
resultState["checked"] = false;
break;
case AccessibilityState::Checked:
resultState["checked"] = true;
break;
case AccessibilityState::Mixed:
resultState["checked"] = "mixed";
break;
case AccessibilityState::None:
resultState["checked"] = "none";
break;
}
}
result["accessibilityState"] = resultState;
return result;
}

static folly::dynamic toDynamic(
Expand Down Expand Up @@ -540,7 +521,8 @@ folly::dynamic HostPlatformViewProps::getDiffProps(
}

if (role != oldProps->role) {
result["role"] = toString(role);
result["role"] =
role.has_value() ? toString(*role) : folly::dynamic(nullptr);
}

if (opacity != oldProps->opacity) {
Expand Down Expand Up @@ -907,8 +889,9 @@ folly::dynamic HostPlatformViewProps::getDiffProps(
// Accessibility

if (accessibilityState != oldProps->accessibilityState) {
updateAccessibilityStateProp(
result, accessibilityState, oldProps->accessibilityState);
result["accessibilityState"] = accessibilityState.has_value()
? toDynamic(*accessibilityState)
: folly::dynamic::object();
}

if (accessibilityLabel != oldProps->accessibilityLabel) {
Expand Down Expand Up @@ -941,7 +924,9 @@ folly::dynamic HostPlatformViewProps::getDiffProps(
}

if (accessibilityRole != oldProps->accessibilityRole) {
result["accessibilityRole"] = accessibilityRole;
result["accessibilityRole"] = accessibilityRole.empty()
? folly::dynamic(nullptr)
: folly::dynamic(accessibilityRole);
}

if (accessibilityLanguage != oldProps->accessibilityLanguage) {
Expand All @@ -962,7 +947,9 @@ folly::dynamic HostPlatformViewProps::getDiffProps(
if (accessibilityValue.text.has_value()) {
accessibilityValueObject["text"] = accessibilityValue.text.value();
}
result["accessibilityValue"] = accessibilityValueObject;
result["accessibilityValue"] = accessibilityValueObject.empty()
? folly::dynamic(nullptr)
: accessibilityValueObject;
}

if (accessibilityActions != oldProps->accessibilityActions) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,55 @@ TEST(ViewPropsDiffTest, gradientColorHintEmitsNullColor) {
"position", nullptr)))))}});
}

// BaseViewManager.setViewState replaces the whole state: an absent `selected`
// deselects the view and an absent `disabled` re-enables it. Props 2.0 sends an
// unset `checked` as "none", which Java ignores.
TEST(ViewPropsDiffTest, emitsTheWholeAccessibilityStateWhenOneMemberChanges) {
auto state = [](bool busy) {
return dynamic::object("selected", true)("disabled", true)("busy", busy);
};
auto sentState = [&](bool busy) {
return dynamic::object(
"accessibilityState", state(busy)("checked", "none"));
};
expectPayloads({
{dynamic::object("accessibilityState", state(false)), sentState(false)},
{dynamic::object("accessibilityState", state(true)), sentState(true)},
});
}

// React sends a removed prop as null on top of the previous props.
// BaseViewManager clears the role only for null, and
// AccessibilityRole.fromValue("") throws.
TEST(ViewPropsDiffTest, removingAccessibilityRoleEmitsNull) {
expectPayloads({
{dynamic::object("accessibilityRole", "button")},
{dynamic::object("accessibilityRole", nullptr)},
});
}

// BaseViewManager falls back to accessibilityRole only for a null role.
TEST(ViewPropsDiffTest, removingRoleEmitsNull) {
expectPayloads({
{dynamic::object("role", "button")},
{dynamic::object("role", nullptr)},
});
}

// An explicit role="none" overrides accessibilityRole.
TEST(ViewPropsDiffTest, emitsAnExplicitRoleNone) {
expectPayloads(
{{dynamic::object("role", "none")("accessibilityRole", "button")}});
}

// BaseViewManager clears the value and the content description only for null.
TEST(ViewPropsDiffTest, removingAccessibilityValueEmitsNull) {
expectPayloads({
{dynamic::object("accessibilityValue", dynamic::object("text", "50%"))},
{dynamic::object("accessibilityValue", nullptr)},
});
}

} // namespace facebook::react

#endif
2 changes: 1 addition & 1 deletion scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -700,8 +700,8 @@ class facebook::react::AccessibilityProps {
public facebook::react::AccessibilityTraits accessibilityTraits;
public facebook::react::AccessibilityValue accessibilityValue;
public facebook::react::ImportantForAccessibility importantForAccessibility;
public facebook::react::Role role;
public std::optional<facebook::react::AccessibilityState> accessibilityState;
public std::optional<facebook::react::Role> role;
public std::string accessibilityHint;
public std::string accessibilityLabel;
public std::string accessibilityLanguage;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1016,8 +1016,8 @@ class facebook::react::AccessibilityProps {
public facebook::react::AccessibilityTraits accessibilityTraits;
public facebook::react::AccessibilityValue accessibilityValue;
public facebook::react::ImportantForAccessibility importantForAccessibility;
public facebook::react::Role role;
public std::optional<facebook::react::AccessibilityState> accessibilityState;
public std::optional<facebook::react::Role> role;
public std::string accessibilityHint;
public std::string accessibilityLabel;
public std::string accessibilityLanguage;
Expand Down
2 changes: 1 addition & 1 deletion scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -700,8 +700,8 @@ class facebook::react::AccessibilityProps {
public facebook::react::AccessibilityTraits accessibilityTraits;
public facebook::react::AccessibilityValue accessibilityValue;
public facebook::react::ImportantForAccessibility importantForAccessibility;
public facebook::react::Role role;
public std::optional<facebook::react::AccessibilityState> accessibilityState;
public std::optional<facebook::react::Role> role;
public std::string accessibilityHint;
public std::string accessibilityLabel;
public std::string accessibilityLanguage;
Expand Down
2 changes: 1 addition & 1 deletion scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -700,8 +700,8 @@ class facebook::react::AccessibilityProps {
public facebook::react::AccessibilityTraits accessibilityTraits;
public facebook::react::AccessibilityValue accessibilityValue;
public facebook::react::ImportantForAccessibility importantForAccessibility;
public facebook::react::Role role;
public std::optional<facebook::react::AccessibilityState> accessibilityState;
public std::optional<facebook::react::Role> role;
public std::string accessibilityHint;
public std::string accessibilityLabel;
public std::string accessibilityLanguage;
Expand Down
2 changes: 1 addition & 1 deletion scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -3435,8 +3435,8 @@ class facebook::react::AccessibilityProps {
public facebook::react::AccessibilityTraits accessibilityTraits;
public facebook::react::AccessibilityValue accessibilityValue;
public facebook::react::ImportantForAccessibility importantForAccessibility;
public facebook::react::Role role;
public std::optional<facebook::react::AccessibilityState> accessibilityState;
public std::optional<facebook::react::Role> role;
public std::string accessibilityHint;
public std::string accessibilityLabel;
public std::string accessibilityLanguage;
Expand Down
2 changes: 1 addition & 1 deletion scripts/cxx-api/api-snapshots/ReactAppleFrameworksCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -3794,8 +3794,8 @@ class facebook::react::AccessibilityProps {
public facebook::react::AccessibilityTraits accessibilityTraits;
public facebook::react::AccessibilityValue accessibilityValue;
public facebook::react::ImportantForAccessibility importantForAccessibility;
public facebook::react::Role role;
public std::optional<facebook::react::AccessibilityState> accessibilityState;
public std::optional<facebook::react::Role> role;
public std::string accessibilityHint;
public std::string accessibilityLabel;
public std::string accessibilityLanguage;
Expand Down
2 changes: 1 addition & 1 deletion scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -3427,8 +3427,8 @@ class facebook::react::AccessibilityProps {
public facebook::react::AccessibilityTraits accessibilityTraits;
public facebook::react::AccessibilityValue accessibilityValue;
public facebook::react::ImportantForAccessibility importantForAccessibility;
public facebook::react::Role role;
public std::optional<facebook::react::AccessibilityState> accessibilityState;
public std::optional<facebook::react::Role> role;
public std::string accessibilityHint;
public std::string accessibilityLabel;
public std::string accessibilityLanguage;
Expand Down
2 changes: 1 addition & 1 deletion scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -3435,8 +3435,8 @@ class facebook::react::AccessibilityProps {
public facebook::react::AccessibilityTraits accessibilityTraits;
public facebook::react::AccessibilityValue accessibilityValue;
public facebook::react::ImportantForAccessibility importantForAccessibility;
public facebook::react::Role role;
public std::optional<facebook::react::AccessibilityState> accessibilityState;
public std::optional<facebook::react::Role> role;
public std::string accessibilityHint;
public std::string accessibilityLabel;
public std::string accessibilityLanguage;
Expand Down
2 changes: 1 addition & 1 deletion scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,8 @@ class facebook::react::AccessibilityProps {
public facebook::react::AccessibilityTraits accessibilityTraits;
public facebook::react::AccessibilityValue accessibilityValue;
public facebook::react::ImportantForAccessibility importantForAccessibility;
public facebook::react::Role role;
public std::optional<facebook::react::AccessibilityState> accessibilityState;
public std::optional<facebook::react::Role> role;
public std::string accessibilityHint;
public std::string accessibilityLabel;
public std::string accessibilityLanguage;
Expand Down
2 changes: 1 addition & 1 deletion scripts/cxx-api/api-snapshots/ReactCommonFrameworksCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -570,8 +570,8 @@ class facebook::react::AccessibilityProps {
public facebook::react::AccessibilityTraits accessibilityTraits;
public facebook::react::AccessibilityValue accessibilityValue;
public facebook::react::ImportantForAccessibility importantForAccessibility;
public facebook::react::Role role;
public std::optional<facebook::react::AccessibilityState> accessibilityState;
public std::optional<facebook::react::Role> role;
public std::string accessibilityHint;
public std::string accessibilityLabel;
public std::string accessibilityLanguage;
Expand Down
2 changes: 1 addition & 1 deletion scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,8 @@ class facebook::react::AccessibilityProps {
public facebook::react::AccessibilityTraits accessibilityTraits;
public facebook::react::AccessibilityValue accessibilityValue;
public facebook::react::ImportantForAccessibility importantForAccessibility;
public facebook::react::Role role;
public std::optional<facebook::react::AccessibilityState> accessibilityState;
public std::optional<facebook::react::Role> role;
public std::string accessibilityHint;
public std::string accessibilityLabel;
public std::string accessibilityLanguage;
Expand Down
2 changes: 1 addition & 1 deletion scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,8 @@ class facebook::react::AccessibilityProps {
public facebook::react::AccessibilityTraits accessibilityTraits;
public facebook::react::AccessibilityValue accessibilityValue;
public facebook::react::ImportantForAccessibility importantForAccessibility;
public facebook::react::Role role;
public std::optional<facebook::react::AccessibilityState> accessibilityState;
public std::optional<facebook::react::Role> role;
public std::string accessibilityHint;
public std::string accessibilityLabel;
public std::string accessibilityLanguage;
Expand Down
Loading