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 @@ -412,7 +412,8 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &

auto *scrollView = (RCTEnhancedScrollView *)_scrollView;
if (oldScrollViewProps.snapToAlignment != newScrollViewProps.snapToAlignment) {
scrollView.snapToAlignment = RCTNSStringFromString(toString(newScrollViewProps.snapToAlignment));
scrollView.snapToAlignment =
RCTNSStringFromString(toString(newScrollViewProps.snapToAlignment.value_or(ScrollViewSnapToAlignment::Start)));
}

scrollView.snapToStart = newScrollViewProps.snapToStart;
Expand Down
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 @@ -80,7 +80,7 @@ BaseScrollViewProps::BaseScrollViewProps(
rawProps,
"decelerationRate",
sourceProps.decelerationRate,
(Float)0.998)),
kDefaultDecelerationRate)),
endDraggingSensitivityMultiplier(convertRawProp(
context,
rawProps,
Expand Down Expand Up @@ -427,10 +427,7 @@ SharedDebugStringConvertibleList BaseScrollViewProps::getDebugProps() const {
"snapToInterval",
snapToInterval,
defaultScrollViewProps.snapToInterval),
debugStringConvertibleItem(
"snapToAlignment",
snapToAlignment,
defaultScrollViewProps.snapToAlignment),
debugStringConvertibleItem("snapToAlignment", snapToAlignment),
debugStringConvertibleItem(
"disableIntervalMomentum",
disableIntervalMomentum,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ class BaseScrollViewProps : public ViewProps {
bool automaticallyAdjustContentInsets{};
bool automaticallyAdjustsScrollIndicatorInsets{true};
bool automaticallyAdjustKeyboardInsets{false};
Float decelerationRate{0.998f};
#ifdef ANDROID
// ReactScrollView's initial rate.
static constexpr Float kDefaultDecelerationRate = 0.985f;
#else
static constexpr Float kDefaultDecelerationRate = 0.998f;
#endif
Float decelerationRate{kDefaultDecelerationRate};
Float endDraggingSensitivityMultiplier{1};
bool directionalLockEnabled{};
ScrollViewIndicatorStyle indicatorStyle{};
Expand All @@ -63,7 +69,7 @@ class BaseScrollViewProps : public ViewProps {
Point contentOffset{};
EdgeInsets scrollIndicatorInsets{};
Float snapToInterval{};
ScrollViewSnapToAlignment snapToAlignment{};
std::optional<ScrollViewSnapToAlignment> snapToAlignment{};
bool disableIntervalMomentum{false};
std::vector<Float> snapToOffsets{};
bool snapToStart{true};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,13 @@ HostPlatformScrollViewProps::HostPlatformScrollViewProps(
rawProps,
"endFillColor",
sourceProps.endFillColor,
clearColor())) {}
clearColor())),
scrollsChildToFocus(convertRawProp(
context,
rawProps,
"scrollsChildToFocus",
sourceProps.scrollsChildToFocus,
true)) {}

void HostPlatformScrollViewProps::setProp(
const PropsParserContext& context,
Expand All @@ -69,6 +75,7 @@ void HostPlatformScrollViewProps::setProp(
RAW_SET_PROP_SWITCH_CASE_BASIC(fadingEdgeLength);
RAW_SET_PROP_SWITCH_CASE_BASIC(overScrollMode);
RAW_SET_PROP_SWITCH_CASE_BASIC(endFillColor);
RAW_SET_PROP_SWITCH_CASE_BASIC(scrollsChildToFocus);
}
}

Expand Down Expand Up @@ -96,7 +103,11 @@ SharedDebugStringConvertibleList HostPlatformScrollViewProps::getDebugProps()
debugStringConvertibleItem(
"endFillColor",
endFillColor,
defaultScrollViewProps.endFillColor)};
defaultScrollViewProps.endFillColor),
debugStringConvertibleItem(
"scrollsChildToFocus",
scrollsChildToFocus,
defaultScrollViewProps.scrollsChildToFocus)};
}
#endif

Expand Down Expand Up @@ -301,17 +312,9 @@ folly::dynamic HostPlatformScrollViewProps::getDiffProps(
}

if (snapToAlignment != oldProps->snapToAlignment) {
switch (snapToAlignment) {
case ScrollViewSnapToAlignment::Start:
result["snapToAlignment"] = "start";
break;
case ScrollViewSnapToAlignment::Center:
result["snapToAlignment"] = "center";
break;
case ScrollViewSnapToAlignment::End:
result["snapToAlignment"] = "end";
break;
}
result["snapToAlignment"] = snapToAlignment.has_value()
? toString(*snapToAlignment)
: folly::dynamic(nullptr);
}

if (disableIntervalMomentum != oldProps->disableIntervalMomentum) {
Expand Down Expand Up @@ -380,6 +383,10 @@ folly::dynamic HostPlatformScrollViewProps::getDiffProps(
result["endFillColor"] = *endFillColor;
}

if (scrollsChildToFocus != oldProps->scrollsChildToFocus) {
result["scrollsChildToFocus"] = scrollsChildToFocus;
}

return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class HostPlatformScrollViewProps : public BaseScrollViewProps {
folly::dynamic fadingEdgeLength{};
std::string overScrollMode{"auto"};
SharedColor endFillColor{clearColor()};
bool scrollsChildToFocus{true};

#pragma mark - DebugStringConvertible

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* 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.
*/

#include <gtest/gtest.h>

#include <react/renderer/components/scrollview/ScrollViewProps.h>
#include <react/test_utils/PropsPayloads.h>

#ifdef RN_SERIALIZABLE_STATE

namespace facebook::react {

namespace {

using folly::dynamic;

void expectPayloads(const std::vector<PropsCommit>& commits) {
expectPayloadsMatchRawProps<ScrollViewProps>(commits);
}

} // namespace

// ReactScrollViewManager has a setter for it; with
// enableExclusivePropsUpdateAndroid there is no rawProps fallback.
TEST(ScrollViewPropsDiffTest, emitsScrollsChildToFocus) {
expectPayloads({{dynamic::object("scrollsChildToFocus", false)}});
}

// Android ScrollViews start with snapping alignment disabled, and
// setSnapToAlignment disables it for null.
TEST(
ScrollViewPropsDiffTest,
keepsAnExplicitSnapToAlignmentStartApartFromUnset) {
expectPayloads({
{dynamic::object("snapToAlignment", "start")},
{dynamic::object("snapToAlignment", "center")},
{dynamic::object("snapToAlignment", nullptr)},
});
}

// Android ScrollViews start decelerating at 0.985 (ReactScrollViewHelper).
// A removed rate restores it; Props 1.5 sends null, which the setter reads as
// 0.
TEST(ScrollViewPropsDiffTest, emitsAnExplicitDecelerationRate) {
expectPayloads({
{dynamic::object("decelerationRate", 0.998)},
{dynamic::object("decelerationRate", nullptr),
dynamic::object("decelerationRate", 0.985)},
});
}

// The ScrollView managers turn both off for null.
TEST(ScrollViewPropsDiffTest, removingNestedScrollAndMomentumEventsEmitsFalse) {
expectPayloads({
{dynamic::object("nestedScrollEnabled", true)(
"sendMomentumEvents", true)},
{dynamic::object("nestedScrollEnabled", nullptr)(
"sendMomentumEvents", nullptr),
dynamic::object("nestedScrollEnabled", false)(
"sendMomentumEvents", false)},
});
}

} // namespace facebook::react

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include <react/renderer/debug/DebugStringConvertibleItem.h>
#include <react/utils/FloatComparison.h>

#include <cmath>

namespace facebook::react {

static TextAttributes convertRawProp(
Expand Down Expand Up @@ -436,7 +438,9 @@ void BaseTextProps::appendTextAttributesProps(
if (!floatEquality(
textAttributes.letterSpacing,
oldProps->textAttributes.letterSpacing)) {
result["letterSpacing"] = textAttributes.letterSpacing;
result["letterSpacing"] = std::isnan(textAttributes.letterSpacing)
? folly::dynamic(nullptr)
: textAttributes.letterSpacing;
}

if (textAttributes.textTransform != oldProps->textAttributes.textTransform) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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.
*/

#include <gtest/gtest.h>

#include <react/renderer/components/text/ParagraphProps.h>
#include <react/test_utils/PropsPayloads.h>

#ifdef RN_SERIALIZABLE_STATE

namespace facebook::react {

using folly::dynamic;

// ReactTextViewManager resets letterSpacing to 0 for null;
// ReactTextView.setLetterSpacing ignores NaN.
TEST(ParagraphPropsDiffTest, removingLetterSpacingEmitsNull) {
expectPayloadsMatchRawProps<ParagraphProps>({
{dynamic::object("letterSpacing", 4.0)},
{dynamic::object("letterSpacing", nullptr)},
});
}

} // namespace facebook::react

#endif
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
Loading
Loading