diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSDeclaredStyle.h b/packages/react-native/ReactCommon/react/renderer/css/CSSDeclaredStyle.h new file mode 100644 index 00000000000..c52bf22e679 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSDeclaredStyle.h @@ -0,0 +1,108 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace facebook::react { + +/** + * CSSDeclaredStyle represents the set of style declarations on an element set + * by the user. Users should generally not read from CSSDeclaredStyle directly, + * and should instead use the computed style calculated on ShadowTree commit. + */ +class CSSDeclaredStyle { + public: + template + void set(const CSSDeclaredValue& value) { + using DeclaredValueT = std::remove_cvref_t>; + static_assert(sizeof(value) <= sizeof(PropMapping::value)); + static_assert(std::is_trivially_destructible_v); + + if (specifiedProperties_.test(to_underlying(Prop))) { + auto it = std::lower_bound( + properties_.begin(), properties_.end(), PropMapping{Prop, {}}); + react_native_assert(it->prop == Prop); + std::construct_at( + reinterpret_cast(it->value.data()), value); + } else { + auto it = std::upper_bound( + properties_.begin(), properties_.end(), PropMapping{Prop, {}}); + it = properties_.insert(it, {Prop, {}}); + std::construct_at( + reinterpret_cast(it->value.data()), value); + specifiedProperties_.set(to_underlying(Prop)); + } + } + + template + void set(std::string_view value) { + set(parseCSSProp(value)); + } + + /** + * Returns the declared value, represented as the "unset" keyword if never + * specified. Additional shorthands can be provided in order + * of precedence if Prop is unset. + */ + template + CSSDeclaredValue get() const { + if (specifiedProperties_.test(to_underlying(Prop))) { + auto it = std::lower_bound( + properties_.begin(), properties_.end(), PropMapping{Prop, {}}); + react_native_assert(it->prop == Prop); + + CSSDeclaredValue value{*std::launder( + reinterpret_cast*>(it->value.data()))}; + + if (value) { + return value; + } + } + + if constexpr (sizeof...(ShorthandsT) == 0) { + return {}; + } else { + return get(); + } + } + + bool operator==(const CSSDeclaredStyle& rhs) const = default; + + private: + struct PropMapping { + CSSProp prop; + std::array< + std::byte, + sizeof(CSSValueVariant< + CSSWideKeyword, + CSSKeyword, + CSSLength, + CSSNumber, + CSSPercentage, + CSSRatio>)> + value; + + constexpr bool operator<(const PropMapping& rhs) const { + return to_underlying(prop) < to_underlying(rhs.prop); + } + }; + + std::vector properties_; + std::bitset specifiedProperties_; +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSParser.h b/packages/react-native/ReactCommon/react/renderer/css/CSSParser.h index f9ad61c4846..c2ea7d9bc94 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSParser.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSParser.h @@ -212,7 +212,7 @@ CSSValueVariant parseCSSComponentValue(std::string_view css) { template constexpr auto parseCSSProp(std::string_view css) { // For now we only allow parsing props composed of a single component value. - CSSSpecifiedValue value; + CSSDeclaredValue value; parseCSSComponentValue(css, value); return value; } diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSProperties.h b/packages/react-native/ReactCommon/react/renderer/css/CSSProperties.h index 067d6c2b3a1..582dbb4385c 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSProperties.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSProperties.h @@ -137,16 +137,22 @@ struct CSSPropDefinition {}; template using CSSAllowedKeywords = typename CSSPropDefinition

::Keyword; +template +using CSSDeclaredValue = typename CSSPropDefinition

::DeclaredValue; + template using CSSSpecifiedValue = typename CSSPropDefinition

::SpecifiedValue; template using CSSComputedValue = typename CSSPropDefinition

::ComputedValue; -struct CSSComputeContext { - bool isRTL{false}; - bool useWebDefaults{false}; - bool swapLeftAndRight{false}; +/** + * Whether to behave in accordance with W3C specs, or to incorporate React + * Native specific tweaks to defaults and computation. + */ +enum class CSSFlavor { + W3C, + ReactNative, }; /** @@ -168,16 +174,18 @@ struct CSSPropDefinition { End = to_underlying(CSSKeyword::End), }; - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext& ctx) { - return ctx.useWebDefaults ? SpecifiedValue::keyword(Keyword::Stretch) - : SpecifiedValue::keyword(Keyword::FlexStart); + constexpr static DeclaredValue initialValue(CSSFlavor flavor) { + return flavor == CSSFlavor::W3C + ? DeclaredValue::keyword(Keyword::Stretch) + : DeclaredValue::keyword(Keyword::FlexStart); } }; @@ -198,15 +206,16 @@ struct CSSPropDefinition { End = to_underlying(CSSKeyword::End), }; - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext&) { - return SpecifiedValue::keyword(Keyword::Stretch); + constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { + return DeclaredValue::keyword(Keyword::Stretch); } }; @@ -228,15 +237,16 @@ struct CSSPropDefinition { End = to_underlying(CSSKeyword::End), }; - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext&) { - return SpecifiedValue::keyword(Keyword::Auto); + constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { + return DeclaredValue::keyword(Keyword::Auto); } }; @@ -250,15 +260,16 @@ struct CSSPropDefinition { Auto = to_underlying(CSSKeyword::Auto), }; - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext&) { - return SpecifiedValue::keyword(Keyword::Auto); + constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { + return DeclaredValue::keyword(Keyword::Auto); } }; @@ -268,16 +279,17 @@ struct CSSPropDefinition { */ template <> struct CSSPropDefinition { - using SpecifiedValue = + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext&) { - return SpecifiedValue::length(0.0f, CSSLengthUnit::Px); + constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { + return DeclaredValue::length(0.0f, CSSLengthUnit::Px); } }; @@ -332,16 +344,17 @@ struct CSSPropDefinition { Outset = to_underlying(CSSKeyword::Outset), }; - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext& ctx) { - return ctx.useWebDefaults ? SpecifiedValue::keyword(Keyword::None) - : SpecifiedValue::keyword(Keyword::Solid); + constexpr static DeclaredValue initialValue(CSSFlavor flavor) { + return flavor == CSSFlavor::W3C ? DeclaredValue::keyword(Keyword::None) + : DeclaredValue::keyword(Keyword::Solid); } }; @@ -397,16 +410,18 @@ struct CSSPropDefinition { Thick = to_underlying(CSSKeyword::Thick), }; - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext& ctx) { - return ctx.useWebDefaults ? SpecifiedValue::keyword(Keyword::Medium) - : SpecifiedValue::length(0.0f, CSSLengthUnit::Px); + constexpr static DeclaredValue initialValue(CSSFlavor flavor) { + return flavor == CSSFlavor::W3C + ? DeclaredValue::keyword(Keyword::Medium) + : DeclaredValue::length(0.0f, CSSLengthUnit::Px); } }; @@ -477,15 +492,16 @@ struct CSSPropDefinition { Rtl = to_underlying(CSSKeyword::Rtl), }; - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return true; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext&) { - return SpecifiedValue::keyword(Keyword::Ltr); + constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { + return DeclaredValue::keyword(Keyword::Ltr); } }; @@ -507,16 +523,17 @@ struct CSSPropDefinition { InlineGrid = to_underlying(CSSKeyword::InlineGrid), }; - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext& ctx) { - return ctx.useWebDefaults ? SpecifiedValue::keyword(Keyword::Inline) - : SpecifiedValue::keyword(Keyword::Flex); + constexpr static DeclaredValue initialValue(CSSFlavor flavor) { + return flavor == CSSFlavor::W3C ? DeclaredValue::keyword(Keyword::Inline) + : DeclaredValue::keyword(Keyword::Flex); } }; @@ -534,15 +551,16 @@ struct CSSPropDefinition { None = to_underlying(CSSKeyword::None), }; - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext&) { - return SpecifiedValue::number(0.0f); + constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { + return DeclaredValue::number(0.0f); } }; @@ -557,16 +575,17 @@ struct CSSPropDefinition { Content = to_underlying(CSSKeyword::Content), }; - using SpecifiedValue = + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext&) { - return SpecifiedValue::keyword(Keyword::Auto); + constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { + return DeclaredValue::keyword(Keyword::Auto); } }; @@ -583,16 +602,17 @@ struct CSSPropDefinition { ColumnReverse = to_underlying(CSSKeyword::ColumnReverse), }; - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext& ctx) { - return ctx.useWebDefaults ? SpecifiedValue::keyword(Keyword::Row) - : SpecifiedValue::keyword(Keyword::Column); + constexpr static DeclaredValue initialValue(CSSFlavor flavor) { + return flavor == CSSFlavor::W3C ? DeclaredValue::keyword(Keyword::Row) + : DeclaredValue::keyword(Keyword::Column); } }; @@ -602,15 +622,16 @@ struct CSSPropDefinition { */ template <> struct CSSPropDefinition { - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext&) { - return SpecifiedValue::number(0.0f); + constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { + return DeclaredValue::number(0.0f); } }; @@ -620,16 +641,17 @@ struct CSSPropDefinition { */ template <> struct CSSPropDefinition { - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext& ctx) { - return ctx.useWebDefaults ? SpecifiedValue::number(1.0f) - : SpecifiedValue::number(0.0f); + constexpr static DeclaredValue initialValue(CSSFlavor flavor) { + return flavor == CSSFlavor::W3C ? DeclaredValue::number(1.0f) + : DeclaredValue::number(0.0f); } }; @@ -645,15 +667,16 @@ struct CSSPropDefinition { WrapReverse = to_underlying(CSSKeyword::WrapReverse), }; - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext&) { - return SpecifiedValue::keyword(Keyword::NoWrap); + constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { + return DeclaredValue::keyword(Keyword::NoWrap); } }; @@ -667,15 +690,16 @@ struct CSSPropDefinition { Normal = to_underlying(CSSKeyword::Normal), }; - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext&) { - return SpecifiedValue::keyword(Keyword::Normal); + constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { + return DeclaredValue::keyword(Keyword::Normal); } }; @@ -698,16 +722,17 @@ struct CSSPropDefinition { MinContent = to_underlying(CSSKeyword::MinContent), }; - using SpecifiedValue = + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext&) { - return SpecifiedValue::keyword(Keyword::Auto); + constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { + return DeclaredValue::keyword(Keyword::Auto); } }; @@ -741,16 +766,17 @@ struct CSSPropDefinition { Auto = to_underlying(CSSKeyword::Auto), }; - using SpecifiedValue = + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext&) { - return SpecifiedValue::keyword(Keyword::Auto); + constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { + return DeclaredValue::keyword(Keyword::Auto); } }; @@ -815,15 +841,16 @@ struct CSSPropDefinition { End = to_underlying(CSSKeyword::End), }; - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext&) { - return SpecifiedValue::keyword(Keyword::FlexStart); + constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { + return DeclaredValue::keyword(Keyword::FlexStart); } }; @@ -837,16 +864,17 @@ struct CSSPropDefinition { Auto = to_underlying(CSSKeyword::Auto), }; - using SpecifiedValue = + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext&) { - return SpecifiedValue::length(0.0f, CSSLengthUnit::Px); + constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { + return DeclaredValue::length(0.0f, CSSLengthUnit::Px); } }; @@ -912,15 +940,16 @@ struct CSSPropDefinition */ template <> struct CSSPropDefinition { - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext&) { - return SpecifiedValue::number(1.0f); + constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { + return DeclaredValue::number(1.0f); } }; @@ -938,15 +967,16 @@ struct CSSPropDefinition { Visible = to_underlying(CSSKeyword::Visible), }; - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext&) { - return SpecifiedValue::keyword(Keyword::Visible); + constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { + return DeclaredValue::keyword(Keyword::Visible); } }; @@ -956,16 +986,17 @@ struct CSSPropDefinition { */ template <> struct CSSPropDefinition { - using SpecifiedValue = + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext&) { - return SpecifiedValue::length(0.0f, CSSLengthUnit::Px); + constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { + return DeclaredValue::length(0.0f, CSSLengthUnit::Px); } }; @@ -1039,16 +1070,17 @@ struct CSSPropDefinition { Sticky = to_underlying(CSSKeyword::Sticky), }; - using SpecifiedValue = CSSValueVariant; + using DeclaredValue = CSSValueVariant; + using SpecifiedValue = CSSValueVariant; using ComputedValue = CSSValueVariant; constexpr static bool isInherited() { return false; } - constexpr static SpecifiedValue initialValue(const CSSComputeContext& ctx) { - return ctx.useWebDefaults ? SpecifiedValue::keyword(Keyword::Static) - : SpecifiedValue::keyword(Keyword::Relative); + constexpr static DeclaredValue initialValue(CSSFlavor flavor) { + return flavor == CSSFlavor::W3C ? DeclaredValue::keyword(Keyword::Static) + : DeclaredValue::keyword(Keyword::Relative); } }; diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSValue.h b/packages/react-native/ReactCommon/react/renderer/css/CSSValue.h index 5d7d9b1ee41..afe7d436c43 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSValue.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSValue.h @@ -36,9 +36,7 @@ enum class CSSValueType : uint8_t { */ template concept CSSDataType = std::is_trivially_destructible_v && - std::is_default_constructible_v && requires() { - sizeof(T); -}; + std::is_copy_constructible_v && std::is_default_constructible_v; #pragma pack(push, 1) /** diff --git a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSDeclaredStyleTest.cpp b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSDeclaredStyleTest.cpp new file mode 100644 index 00000000000..caee42e5653 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSDeclaredStyleTest.cpp @@ -0,0 +1,151 @@ +/* + * 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 +#include +#include + +namespace facebook::react { + +TEST(CSSDeclaredStyle, unset_keyword) { + CSSDeclaredStyle style; + auto value = style.get(); + EXPECT_EQ(value.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(value.getCSSWideKeyword(), CSSWideKeyword::Unset); +} + +TEST(CSSDeclaredStyle, unset_ratio) { + CSSDeclaredStyle style; + auto value = style.get(); + EXPECT_EQ(value.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(value.getCSSWideKeyword(), CSSWideKeyword::Unset); +} + +TEST(CSSDeclaredStyle, set_keyword) { + CSSDeclaredStyle style; + + style.set("row"); + + auto value = style.get(); + EXPECT_EQ(value.type(), CSSValueType::Keyword); + EXPECT_EQ( + value.getKeyword(), CSSAllowedKeywords::Row); +} + +TEST(CSSDeclaredStyle, set_ratio) { + CSSDeclaredStyle style; + + style.set("16 / 9"); + + auto value = style.get(); + EXPECT_EQ(value.type(), CSSValueType::Ratio); + EXPECT_EQ(value.getRatio().numerator, 16.0f); + EXPECT_EQ(value.getRatio().denominator, 9.0f); +} + +TEST(CSSDeclaredStyle, overwrite_ratio) { + CSSDeclaredStyle style; + + style.set("16 / 9"); + + auto value1 = style.get(); + EXPECT_EQ(value1.type(), CSSValueType::Ratio); + EXPECT_EQ(value1.getRatio().numerator, 16.0f); + EXPECT_EQ(value1.getRatio().denominator, 9.0f); + + style.set("4/3"); + + auto value2 = style.get(); + EXPECT_EQ(value2.type(), CSSValueType::Ratio); + EXPECT_EQ(value2.getRatio().numerator, 4.0f); + EXPECT_EQ(value2.getRatio().denominator, 3.0f); +} + +TEST(CSSDeclaredStyle, set_multiple) { + CSSDeclaredStyle style; + + style.set("row"); + style.set("16 / 9"); + + auto flexDirection = style.get(); + EXPECT_EQ(flexDirection.type(), CSSValueType::Keyword); + EXPECT_EQ( + flexDirection.getKeyword(), + CSSAllowedKeywords::Row); + + auto aspectRatio = style.get(); + EXPECT_EQ(aspectRatio.type(), CSSValueType::Ratio); + EXPECT_EQ(aspectRatio.getRatio().numerator, 16.0f); + EXPECT_EQ(aspectRatio.getRatio().denominator, 9.0f); +} + +TEST(CSSDeclaredStyle, set_multiple_overwrite) { + CSSDeclaredStyle style; + + style.set("row"); + style.set("16 / 9"); + style.set("column"); + + auto flexDirection = style.get(); + EXPECT_EQ(flexDirection.type(), CSSValueType::Keyword); + EXPECT_EQ( + flexDirection.getKeyword(), + CSSAllowedKeywords::Column); + + auto aspectRatio = style.get(); + EXPECT_EQ(aspectRatio.type(), CSSValueType::Ratio); + EXPECT_EQ(aspectRatio.getRatio().numerator, 16.0f); + EXPECT_EQ(aspectRatio.getRatio().denominator, 9.0f); +} + +TEST(CSSDeclaredStyle, set_multiple_reset) { + CSSDeclaredStyle style; + + style.set("row"); + style.set("16 / 9"); + style.set(""); + + auto flexDirection = style.get(); + EXPECT_EQ(flexDirection.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(flexDirection.getCSSWideKeyword(), CSSWideKeyword::Unset); + + auto aspectRatio = style.get(); + EXPECT_EQ(aspectRatio.type(), CSSValueType::Ratio); + EXPECT_EQ(aspectRatio.getRatio().numerator, 16.0f); + EXPECT_EQ(aspectRatio.getRatio().denominator, 9.0f); +} + +TEST(CSSDeclaredStyle, get_with_precedence) { + CSSDeclaredStyle style; + + style.set("1px"); + + auto margin1 = style.get< + CSSProp::MarginStart, + CSSProp::MarginLeft, + CSSProp::MarginInline, + CSSProp::MarginHorizontal, + CSSProp::Margin>(); + + EXPECT_EQ(margin1.type(), CSSValueType::Length); + EXPECT_EQ(margin1.getLength().value, 1.0f); + EXPECT_EQ(margin1.getLength().unit, CSSLengthUnit::Px); + + style.set("3px"); + auto margin2 = style.get< + CSSProp::MarginStart, + CSSProp::MarginLeft, + CSSProp::MarginInline, + CSSProp::MarginHorizontal, + CSSProp::Margin>(); + + EXPECT_EQ(margin2.type(), CSSValueType::Length); + EXPECT_EQ(margin2.getLength().value, 3.0f); + EXPECT_EQ(margin2.getLength().unit, CSSLengthUnit::Px); +} + +} // namespace facebook::react