diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/options/TopBarOptions.java b/lib/android/app/src/main/java/com/reactnativenavigation/options/TopBarOptions.java index d40cd9cbf94..1dc61630d24 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/options/TopBarOptions.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/options/TopBarOptions.java @@ -43,6 +43,8 @@ public static TopBarOptions parse(Context context, TypefaceLoader typefaceLoader options.borderHeight = FractionParser.parse(json, "borderHeight"); options.elevation = FractionParser.parse(json, "elevation"); options.topMargin = NumberParser.parse(json, "topMargin"); + options.animateLeftButtons = BoolParser.parse(json, "animateLeftButtons"); + options.animateRightButtons = BoolParser.parse(json, "animateRightButtons"); options.buttons = TopBarButtons.parse(context, json); options.rightButtonColor = ColorParser.parse(context, json, "rightButtonColor"); @@ -68,7 +70,8 @@ public static TopBarOptions parse(Context context, TypefaceLoader typefaceLoader public Number topMargin = new NullNumber(); public Fraction borderHeight = new NullFraction(); public Colour borderColor = new NullColor(); - + public Bool animateLeftButtons = new NullBool(); + public Bool animateRightButtons = new NullBool(); // Deprecated public Colour rightButtonColor = new NullColor(); public Colour leftButtonColor = new NullColor(); @@ -96,6 +99,8 @@ void mergeWith(final TopBarOptions other) { if (other.borderColor.hasValue()) borderColor = other.borderColor; if (other.elevation.hasValue()) elevation = other.elevation; if (other.topMargin.hasValue()) topMargin = other.topMargin; + if (other.animateLeftButtons.hasValue()) animateLeftButtons = other.animateLeftButtons; + if (other.animateRightButtons.hasValue()) animateRightButtons = other.animateRightButtons; if (other.rightButtonColor.hasValue()) rightButtonColor = other.rightButtonColor; if (other.leftButtonColor.hasValue()) leftButtonColor = other.leftButtonColor; @@ -120,6 +125,8 @@ public TopBarOptions mergeWithDefault(TopBarOptions defaultOptions) { if (!borderColor.hasValue()) borderColor = defaultOptions.borderColor; if (!elevation.hasValue()) elevation = defaultOptions.elevation; if (!topMargin.hasValue()) topMargin = defaultOptions.topMargin; + if (!animateLeftButtons.hasValue()) animateLeftButtons = defaultOptions.animateLeftButtons; + if (!animateRightButtons.hasValue()) animateRightButtons = defaultOptions.animateRightButtons; if (!rightButtonColor.hasValue()) rightButtonColor = defaultOptions.rightButtonColor; if (!leftButtonColor.hasValue()) leftButtonColor = defaultOptions.leftButtonColor; diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenter.java b/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenter.java index 88efe4549de..5b97479addc 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenter.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenter.java @@ -282,7 +282,10 @@ private void applyButtons(TopBarOptions options, ViewController child) { if (options.buttons.back.visible.isTrue() && !options.buttons.hasLeftButtons()) { topBar.setBackButton(createButtonController(options.buttons.back)); } - + if(options.animateRightButtons.hasValue()) + topBar.animateRightButtons(options.animateRightButtons.isTrue()); + if(options.animateLeftButtons.hasValue()) + topBar.animateLeftButtons(options.animateLeftButtons.isTrue()); topBar.setOverflowButtonColor(options.rightButtonColor.get(Color.BLACK)); } @@ -460,7 +463,8 @@ private void mergeTopBarOptions(TopBarOptions resolveOptions, Options options, S if (topBarOptions.title.height.hasValue()) topBar.setTitleHeight(topBarOptions.title.height.get()); if (topBarOptions.title.topMargin.hasValue()) topBar.setTitleTopMargin(topBarOptions.title.topMargin.get()); - + if (topBarOptions.animateLeftButtons.hasValue()) topBar.animateLeftButtons(topBarOptions.animateLeftButtons.isTrue()); + if (topBarOptions.animateRightButtons.hasValue()) topBar.animateRightButtons(topBarOptions.animateRightButtons.isTrue()); if (topBarOptions.title.component.hasValue()) { TitleBarReactViewController controller = findTitleComponent(topBarOptions.title.component); if (controller == null) { diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/views/stack/topbar/TopBar.java b/lib/android/app/src/main/java/com/reactnativenavigation/views/stack/topbar/TopBar.java index 3b1a974a44b..4ba90e3c9f1 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/views/stack/topbar/TopBar.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/views/stack/topbar/TopBar.java @@ -161,6 +161,13 @@ public void setSubtitleFontSize(double size) { titleAndButtonsContainer.setSubtitleFontSize((float) size); } + public void animateRightButtons(boolean animate){ + titleAndButtonsContainer.animateRightButtons(animate); + } + + public void animateLeftButtons(boolean animate){ + titleAndButtonsContainer.animateLeftButtons(animate); + } public void setSubtitleAlignment(Alignment alignment) { titleAndButtonsContainer.setSubTitleTextAlignment(alignment); } diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/ButtonBar.kt b/lib/android/app/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/ButtonBar.kt index 618b6577337..c17afc67757 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/ButtonBar.kt +++ b/lib/android/app/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/ButtonBar.kt @@ -4,6 +4,8 @@ import android.content.Context import android.graphics.PorterDuff import android.graphics.PorterDuffColorFilter import android.text.SpannableString +import android.transition.AutoTransition +import android.transition.TransitionManager import android.view.MenuItem import android.view.View import android.view.ViewGroup @@ -14,6 +16,8 @@ import com.reactnativenavigation.utils.ViewUtils import com.reactnativenavigation.viewcontrollers.stack.topbar.button.ButtonController open class ButtonBar internal constructor(context: Context) : Toolbar(context) { + var shouldAnimate: Boolean=false + init { super.setContentInsetsAbsolute(0, 0) this.contentInsetStartWithNavigation = 0 @@ -35,17 +39,27 @@ open class ButtonBar internal constructor(context: Context) : Toolbar(context) { super.setLayoutDirection(layoutDirection) } + val buttonCount: Int + get() = menu.size() + fun addButton(menuItem: Int, intId: Int, order: Int, styledText: SpannableString): MenuItem? { + if(shouldAnimate) + TransitionManager.beginDelayedTransition(this,AutoTransition()) return this.menu?.add(menuItem, intId, order, styledText) } - val buttonCount: Int - get() = menu.size() + fun removeButton(buttonId: Int) { + if(shouldAnimate) + TransitionManager.beginDelayedTransition(this,AutoTransition()) + menu.removeItem(buttonId) + } open fun clearButtons() { + if(shouldAnimate) + TransitionManager.beginDelayedTransition(this,AutoTransition()) clearBackButton() if (menu.size() > 0) menu.clear() } @@ -58,10 +72,6 @@ open class ButtonBar internal constructor(context: Context) : Toolbar(context) { return menuItem != null && menu.findItem(menuItem.itemId) != null && menuItem.order == order } - fun removeButton(buttonId: Int) { - menu.removeItem(buttonId) - } - fun setBackButton(button: ButtonController) { button.applyNavigationIcon(this) } diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/TitleAndButtonsContainer.kt b/lib/android/app/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/TitleAndButtonsContainer.kt index 0223f58b711..75dcfadd8d4 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/TitleAndButtonsContainer.kt +++ b/lib/android/app/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/TitleAndButtonsContainer.kt @@ -23,6 +23,13 @@ class TitleAndButtonsContainer(context: Context) : ViewGroup(context) { } } + fun animateLeftButtons(animate:Boolean) { + leftButtonBar.shouldAnimate = animate + } + fun animateRightButtons(animate:Boolean) { + rightButtonBar.shouldAnimate = animate + } + private var titleSubTitleBar = TitleSubTitleLayout(context) var leftButtonBar = ButtonBar(context) private set @@ -64,7 +71,7 @@ class TitleAndButtonsContainer(context: Context) : ViewGroup(context) { component?.layoutDirection = layoutDirection titleSubTitleBar.layoutDirection = layoutDirection rightButtonBar.layoutDirection = layoutDirection - leftButtonBar.layoutDirection = layoutDirection + leftButtonBar.layoutDirection = if(isRTL()) View.LAYOUT_DIRECTION_LTR else View.LAYOUT_DIRECTION_RTL } fun setSubTitleTextAlignment(alignment: Alignment) = titleSubTitleBar.setSubTitleAlignment(alignment) diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenterTest.kt b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenterTest.kt index 50c45bee080..df78dab5507 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenterTest.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenterTest.kt @@ -344,6 +344,30 @@ class StackPresenterTest : BaseTest() { verify(topBar, never()).clearLeftButtons() } + @Test + fun mergeChildOptions_mergeAnimateLeftRightButtons(){ + val options = Options().apply { + topBar.animateLeftButtons = Bool(false) + } + uut.mergeChildOptions(options, EMPTY_OPTIONS, parent, child) + verify(topBar).animateLeftButtons(false) + verify(topBar, never()).animateRightButtons(any()) + + options.apply { + topBar.animateRightButtons = Bool(true) + } + uut.mergeChildOptions(options, EMPTY_OPTIONS, parent, child) + verify(topBar).animateRightButtons(true) + + + options.apply { + topBar.animateRightButtons = Bool(false) + topBar.animateLeftButtons = Bool(true) + } + uut.mergeChildOptions(options, EMPTY_OPTIONS, parent, child) + verify(topBar).animateRightButtons(false) + verify(topBar).animateLeftButtons(true) + } @Test fun mergeTopBarOptions() { val options = Options() @@ -796,6 +820,32 @@ class StackPresenterTest : BaseTest() { buttons.forEach { assertThat(it.isDestroyed).isFalse() } } + @Test + fun applyChildOptions_shouldNotPassAnimateLeftRightButtonBarWhenNoValue() { + val options = Options().apply { + topBar.buttons.right = ArrayList(listOf(componentBtn1)) + topBar.buttons.left = ArrayList(listOf(componentBtn2)) + + } + uut.applyChildOptions(options, parent, child) + verify(topBar, never()).animateLeftButtons(any()) + verify(topBar, never()).animateLeftButtons(any()) + } + + @Test + fun applyChildOptions_shouldPassAnimateLeftRightButtonBar() { + val options = Options().apply { + topBar.buttons.right = ArrayList(listOf(componentBtn1)) + topBar.buttons.left = ArrayList(listOf(componentBtn2)) + topBar.animateLeftButtons= Bool(false); + topBar.animateRightButtons= Bool(true); + } + + uut.applyChildOptions(options, parent, child) + verify(topBar).animateRightButtons(true) + verify(topBar).animateLeftButtons(false) + } + @Test fun onChildDestroyed_destroyedButtons() { val options = Options() diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/views/TitleAndButtonsContainerTest.kt b/lib/android/app/src/test/java/com/reactnativenavigation/views/TitleAndButtonsContainerTest.kt index d8b1db91b59..7ddb563a107 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/views/TitleAndButtonsContainerTest.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/views/TitleAndButtonsContainerTest.kt @@ -23,6 +23,7 @@ import org.junit.Test import org.mockito.Mockito import org.mockito.Mockito.times import kotlin.math.roundToInt +import kotlin.test.assertFalse private const val UUT_WIDTH = 1000 private const val UUT_HEIGHT = 100 @@ -30,9 +31,9 @@ private const val UUT_HEIGHT = 100 class TitleAndButtonsContainerTest : BaseTest() { lateinit var uut: TitleAndButtonsContainer private lateinit var activity: Activity - private lateinit var mockLeftBar: ButtonBar; - private lateinit var mockRightBar: ButtonBar; - private lateinit var mockComponent: View; + private lateinit var mockLeftBar: ButtonBar + private lateinit var mockRightBar: ButtonBar + private lateinit var mockComponent: View override fun beforeEach() { super.beforeEach() setup() @@ -79,6 +80,28 @@ class TitleAndButtonsContainerTest : BaseTest() { idleMainLooper() } + @Test + fun `animateLeftRightButtons - should be false as default`(){ + assertFalse(mockLeftBar.shouldAnimate) + assertFalse(mockRightBar.shouldAnimate) + } + @Test + fun `animateLeftRightButtons - should change corresponding button bar`(){ + setup(rightBarWidth = 10,leftBarWidth = 20) + + uut.animateLeftButtons(true) + verify(mockLeftBar).shouldAnimate=true + + uut.animateLeftButtons(false) + verify(mockLeftBar).shouldAnimate=false + + uut.animateRightButtons(true) + verify(mockRightBar).shouldAnimate=true + + uut.animateRightButtons(false) + verify(mockRightBar).shouldAnimate=false + } + @Test fun `setComponent - should not change component id`() { val component = View(activity).apply { id = 19 } diff --git a/lib/ios/RNNButtonsPresenter.h b/lib/ios/RNNButtonsPresenter.h index 927a0ca3395..5c07d798fe9 100644 --- a/lib/ios/RNNButtonsPresenter.h +++ b/lib/ios/RNNButtonsPresenter.h @@ -12,11 +12,13 @@ - (void)applyLeftButtons:(NSArray *)leftButtons defaultColor:(Color *)defaultColor - defaultDisabledColor:(Color *)defaultDisabledColor; + defaultDisabledColor:(Color *)defaultDisabledColor + animated:(BOOL)animated; - (void)applyRightButtons:(NSArray *)rightButtons defaultColor:(Color *)defaultColor - defaultDisabledColor:(Color *)defaultDisabledColor; + defaultDisabledColor:(Color *)defaultDisabledColor + animated:(BOOL)animated; - (void)applyLeftButtonsColor:(UIColor *)color; diff --git a/lib/ios/RNNButtonsPresenter.m b/lib/ios/RNNButtonsPresenter.m index da597101193..c426fea8e85 100644 --- a/lib/ios/RNNButtonsPresenter.m +++ b/lib/ios/RNNButtonsPresenter.m @@ -27,20 +27,22 @@ - (void)bindViewController:(UIViewController *)viewController - (void)applyLeftButtons:(NSArray *)leftButtons defaultColor:(Color *)defaultColor - defaultDisabledColor:(Color *)defaultDisabledColor { + defaultDisabledColor:(Color *)defaultDisabledColor + animated:(BOOL)animated { [self setButtons:leftButtons side:@"left" - animated:NO + animated:animated defaultColor:defaultColor defaultDisabledColor:defaultDisabledColor]; } - (void)applyRightButtons:(NSArray *)rightButtons defaultColor:(Color *)defaultColor - defaultDisabledColor:(Color *)defaultDisabledColor { + defaultDisabledColor:(Color *)defaultDisabledColor + animated:(BOOL)animated { [self setButtons:rightButtons side:@"right" - animated:NO + animated:animated defaultColor:defaultColor defaultDisabledColor:defaultDisabledColor]; } diff --git a/lib/ios/RNNComponentPresenter.m b/lib/ios/RNNComponentPresenter.m index fba7a83d06e..a336e847fb7 100644 --- a/lib/ios/RNNComponentPresenter.m +++ b/lib/ios/RNNComponentPresenter.m @@ -91,13 +91,15 @@ - (void)applyOptions:(RNNNavigationOptions *)options { if (withDefault.topBar.leftButtons) { [_buttonsPresenter applyLeftButtons:withDefault.topBar.leftButtons defaultColor:withDefault.topBar.leftButtonColor - defaultDisabledColor:withDefault.topBar.leftButtonDisabledColor]; + defaultDisabledColor:withDefault.topBar.leftButtonDisabledColor + animated:withDefault.topBar.animateLeftButtons]; } if (withDefault.topBar.rightButtons) { [_buttonsPresenter applyRightButtons:withDefault.topBar.rightButtons defaultColor:withDefault.topBar.rightButtonColor - defaultDisabledColor:withDefault.topBar.rightButtonDisabledColor]; + defaultDisabledColor:withDefault.topBar.rightButtonDisabledColor + animated:withDefault.topBar.animateRightButtons]; } } @@ -193,13 +195,15 @@ - (void)mergeOptions:(RNNNavigationOptions *)mergeOptions if (mergeOptions.topBar.leftButtons) { [_buttonsPresenter applyLeftButtons:mergeOptions.topBar.leftButtons defaultColor:withDefault.topBar.leftButtonColor - defaultDisabledColor:withDefault.topBar.leftButtonDisabledColor]; + defaultDisabledColor:withDefault.topBar.leftButtonDisabledColor + animated:withDefault.topBar.animateLeftButtons]; } if (mergeOptions.topBar.rightButtons) { [_buttonsPresenter applyRightButtons:mergeOptions.topBar.rightButtons defaultColor:withDefault.topBar.rightButtonColor - defaultDisabledColor:withDefault.topBar.rightButtonDisabledColor]; + defaultDisabledColor:withDefault.topBar.rightButtonDisabledColor + animated:withDefault.topBar.animateRightButtons]; } if (mergeOptions.topBar.leftButtonColor.hasValue) { diff --git a/lib/ios/RNNTopBarOptions.h b/lib/ios/RNNTopBarOptions.h index a111ac1f0b2..bc66ff8fc16 100644 --- a/lib/ios/RNNTopBarOptions.h +++ b/lib/ios/RNNTopBarOptions.h @@ -24,6 +24,8 @@ @property(nonatomic, strong) Bool *noBorder; @property(nonatomic, strong) Color *borderColor; @property(nonatomic, strong) Bool *animate; +@property(nonatomic, strong) Bool *animateLeftButtons; +@property(nonatomic, strong) Bool *animateRightButtons; @property(nonatomic, strong) RNNSearchBarOptions *searchBar; @property(nonatomic, strong) Bool *searchBarHiddenWhenScrolling; @property(nonatomic, strong) Bool *hideNavBarOnFocusSearchBar; diff --git a/lib/ios/RNNTopBarOptions.m b/lib/ios/RNNTopBarOptions.m index 59e0a6ec21d..432606d6736 100644 --- a/lib/ios/RNNTopBarOptions.m +++ b/lib/ios/RNNTopBarOptions.m @@ -18,6 +18,8 @@ - (instancetype)initWithDict:(NSDictionary *)dict { self.noBorder = [BoolParser parse:dict key:@"noBorder"]; self.borderColor = [ColorParser parse:dict key:@"borderColor"]; self.animate = [BoolParser parse:dict key:@"animate"]; + self.animateLeftButtons = [BoolParser parse:dict key:@"animateLeftButtons"]; + self.animateRightButtons = [BoolParser parse:dict key:@"animateRightButtons"]; self.searchBarHiddenWhenScrolling = [BoolParser parse:dict key:@"searchBarHiddenWhenScrolling"]; self.hideNavBarOnFocusSearchBar = [BoolParser parse:dict key:@"hideNavBarOnFocusSearchBar"]; self.testID = [TextParser parse:dict key:@"testID"]; @@ -69,6 +71,10 @@ - (void)mergeOptions:(RNNTopBarOptions *)options { self.borderColor = options.borderColor; if (options.animate.hasValue) self.animate = options.animate; + if (options.animateLeftButtons.hasValue) + self.animateLeftButtons = options.animateLeftButtons; + if (options.animateRightButtons.hasValue) + self.animateRightButtons = options.animateRightButtons; if (options.searchBarHiddenWhenScrolling.hasValue) self.searchBarHiddenWhenScrolling = options.searchBarHiddenWhenScrolling; if (options.hideNavBarOnFocusSearchBar.hasValue) diff --git a/lib/src/interfaces/Options.ts b/lib/src/interfaces/Options.ts index 944c0d1db10..703f6a2cfc1 100644 --- a/lib/src/interfaces/Options.ts +++ b/lib/src/interfaces/Options.ts @@ -712,6 +712,16 @@ export interface OptionsTopBar { * #### (Android specific) */ topMargin?: number; + + /** + * Toggles animation on left buttons bar upon changes + */ + animateLeftButtons?: boolean; + + /** + * Toggles animation on right buttons bar upon changes + */ + animateRightButtons?: boolean; } export interface SharedElementTransition { diff --git a/playground/ios/NavigationTests/RNNButtonsPresenterTest.m b/playground/ios/NavigationTests/RNNButtonsPresenterTest.m index 9222a473974..54b981561f7 100644 --- a/playground/ios/NavigationTests/RNNButtonsPresenterTest.m +++ b/playground/ios/NavigationTests/RNNButtonsPresenterTest.m @@ -22,24 +22,28 @@ - (void)setUp { - (void)testApplyButtons_shouldNotAddEmptyButton { [_uut applyLeftButtons:@[ [self buttonWithDict:@{@"id" : @"buttonId"}] ] defaultColor:nil - defaultDisabledColor:nil]; + defaultDisabledColor:nil + animated:NO]; XCTAssertTrue(_viewController.navigationItem.leftBarButtonItems.count == 0); [_uut applyRightButtons:@[ [self buttonWithDict:@{@"id" : @"buttonId"}] ] defaultColor:nil - defaultDisabledColor:nil]; + defaultDisabledColor:nil + animated:NO]; XCTAssertTrue(_viewController.navigationItem.rightBarButtonItems.count == 0); } - (void)testApplyButtons_shouldAddButtonWithTitle { [_uut applyLeftButtons:@[ [self buttonWithDict:@{@"id" : @"buttonId", @"text" : @"title"}] ] defaultColor:nil - defaultDisabledColor:nil]; + defaultDisabledColor:nil + animated:NO]; XCTAssertTrue(_viewController.navigationItem.leftBarButtonItems.count == 1); [_uut applyRightButtons:@[ [self buttonWithDict:@{@"id" : @"buttonId", @"text" : @"title"}] ] defaultColor:nil - defaultDisabledColor:nil]; + defaultDisabledColor:nil + animated:NO]; XCTAssertTrue(_viewController.navigationItem.rightBarButtonItems.count == 1); } @@ -47,14 +51,14 @@ - (void)testApplyButtons_shouldCreateCustomButtonView { RNNButtonOptions *button = [self buttonWithDict:@{@"id" : @"buttonId"}]; button.icon = [Image withValue:UIImage.new]; button.iconBackground.color = [Color withValue:UIColor.blackColor]; - [_uut applyLeftButtons:@[ button ] defaultColor:nil defaultDisabledColor:nil]; + [_uut applyLeftButtons:@[ button ] defaultColor:nil defaultDisabledColor:nil animated:NO]; XCTAssertNotNil([_viewController.navigationItem.leftBarButtonItems.lastObject customView]); } - (void)testApplyLeftButtonColor_shouldApplyTintColor { RNNButtonOptions *button = [self buttonWithDict:@{@"id" : @"buttonId"}]; button.icon = [Image withValue:UIImage.new]; - [_uut applyLeftButtons:@[ button ] defaultColor:nil defaultDisabledColor:nil]; + [_uut applyLeftButtons:@[ button ] defaultColor:nil defaultDisabledColor:nil animated:NO]; [_uut applyLeftButtonsColor:UIColor.redColor]; XCTAssertEqual(_viewController.navigationItem.leftBarButtonItems.firstObject.tintColor, UIColor.redColor); @@ -62,7 +66,7 @@ - (void)testApplyLeftButtonColor_shouldApplyTintColor { - (void)testApplyLeftButtonColor_shouldApplyTextAttributesColor { RNNButtonOptions *button = [self buttonWithDict:@{@"id" : @"buttonId", @"text" : @"title"}]; - [_uut applyLeftButtons:@[ button ] defaultColor:nil defaultDisabledColor:nil]; + [_uut applyLeftButtons:@[ button ] defaultColor:nil defaultDisabledColor:nil animated:NO]; [_uut applyLeftButtonsColor:UIColor.redColor]; XCTAssertEqual([[_viewController.navigationItem.leftBarButtonItems.firstObject titleTextAttributesForState:UIControlStateNormal] @@ -77,7 +81,7 @@ - (void)testApplyLeftButtonColor_shouldApplyTextAttributesColor { - (void)testApplyRightButtonColor_shouldApplyTintColor { RNNButtonOptions *button = [self buttonWithDict:@{@"id" : @"buttonId"}]; button.icon = [Image withValue:UIImage.new]; - [_uut applyRightButtons:@[ button ] defaultColor:nil defaultDisabledColor:nil]; + [_uut applyRightButtons:@[ button ] defaultColor:nil defaultDisabledColor:nil animated:NO]; [_uut applyRightButtonsColor:UIColor.redColor]; XCTAssertEqual(_viewController.navigationItem.rightBarButtonItems.firstObject.tintColor, UIColor.redColor); @@ -85,7 +89,7 @@ - (void)testApplyRightButtonColor_shouldApplyTintColor { - (void)testApplyRightButtonColor_shouldApplyTextAttributesColor { RNNButtonOptions *button = [self buttonWithDict:@{@"id" : @"buttonId", @"text" : @"title"}]; - [_uut applyRightButtons:@[ button ] defaultColor:nil defaultDisabledColor:nil]; + [_uut applyRightButtons:@[ button ] defaultColor:nil defaultDisabledColor:nil animated:NO]; [_uut applyRightButtonsColor:UIColor.redColor]; XCTAssertEqual([[_viewController.navigationItem.rightBarButtonItems.firstObject titleTextAttributesForState:UIControlStateNormal] diff --git a/playground/ios/NavigationTests/RNNComponentPresenterTest.m b/playground/ios/NavigationTests/RNNComponentPresenterTest.m index 443db12daf4..722f0058d6b 100644 --- a/playground/ios/NavigationTests/RNNComponentPresenterTest.m +++ b/playground/ios/NavigationTests/RNNComponentPresenterTest.m @@ -13,6 +13,7 @@ @interface RNNComponentPresenterTest : XCTestCase @property(nonatomic, strong) RNNNavigationOptions *options; @property(nonatomic, strong) UIViewController *boundViewController; @property(nonatomic, strong) RNNReactComponentRegistry *componentRegistry; +@property(nonatomic, strong) id buttonsPresenter; @end @@ -20,11 +21,12 @@ @implementation RNNComponentPresenterTest - (void)setUp { [super setUp]; + self.buttonsPresenter = [OCMockObject niceMockForClass:[RNNButtonsPresenter class]]; self.componentRegistry = [OCMockObject partialMockForObject:[RNNReactComponentRegistry new]]; self.uut = [[RNNComponentPresenter alloc] initWithComponentRegistry:self.componentRegistry defaultOptions:[RNNNavigationOptions emptyOptions] - buttonsPresenter:nil]; + buttonsPresenter:self.buttonsPresenter]; self.boundViewController = [OCMockObject partialMockForObject:[RNNComponentViewController new]]; [self.uut bindViewController:self.boundViewController]; self.options = [RNNNavigationOptions emptyOptions]; @@ -62,6 +64,64 @@ - (void)testApplyOptions_backButtonVisibleDefaultTrue { XCTAssertFalse(self.boundViewController.navigationItem.hidesBackButton); } +- (void)testApplyOptions_animateLeftButtons { + self.options.topBar.animateLeftButtons = [Bool withValue:NO]; + RNNButtonOptions *button = [RNNButtonOptions new]; + self.options.topBar.leftButtons = @[ button ]; + [[self.buttonsPresenter expect] applyLeftButtons:self.options.topBar.leftButtons + defaultColor:OCMArg.any + defaultDisabledColor:OCMArg.any + animated:YES]; + [self.uut applyOptions:self.options]; + [self.buttonsPresenter verify]; +} + +- (void)testApplyOptions_animateRightButtons { + self.options.topBar.animateRightButtons = [Bool withValue:YES]; + RNNButtonOptions *button = [RNNButtonOptions new]; + self.options.topBar.rightButtons = @[ button ]; + [[self.buttonsPresenter expect] applyRightButtons:self.options.topBar.rightButtons + defaultColor:OCMArg.any + defaultDisabledColor:OCMArg.any + animated:YES]; + [self.uut applyOptions:self.options]; + [self.buttonsPresenter verify]; +} + +- (void)testMergeOptions_animateLeftButtons { + RNNNavigationOptions *mergeOptions = RNNNavigationOptions.emptyOptions; + mergeOptions.topBar.animateLeftButtons = [Bool withValue:YES]; + RNNButtonOptions *button = [RNNButtonOptions new]; + mergeOptions.topBar.leftButtons = @[ button ]; + + [[self.buttonsPresenter expect] + applyLeftButtons:[OCMArg checkWithBlock:^BOOL(NSArray *buttons) { + return buttons.firstObject == button; + }] + defaultColor:OCMArg.any + defaultDisabledColor:OCMArg.any + animated:YES]; + [self.uut mergeOptions:mergeOptions resolvedOptions:RNNNavigationOptions.emptyOptions]; + [self.buttonsPresenter verify]; +} + +- (void)testMergeOptions_animateRightButtons { + RNNNavigationOptions *mergeOptions = RNNNavigationOptions.emptyOptions; + mergeOptions.topBar.animateRightButtons = [Bool withValue:YES]; + RNNButtonOptions *button = [RNNButtonOptions new]; + mergeOptions.topBar.rightButtons = @[ button ]; + + [[self.buttonsPresenter expect] + applyRightButtons:[OCMArg checkWithBlock:^BOOL(NSArray *buttons) { + return buttons.firstObject == button; + }] + defaultColor:OCMArg.any + defaultDisabledColor:OCMArg.any + animated:YES]; + [self.uut mergeOptions:mergeOptions resolvedOptions:RNNNavigationOptions.emptyOptions]; + [self.buttonsPresenter verify]; +} + - (void)testApplyOptions_drawBehindTabBarTrueWhenVisibleFalse { self.options.bottomTabs.visible = [[Bool alloc] initWithValue:@(0)]; [[(id)self.boundViewController expect] setDrawBehindBottomTabs:YES]; diff --git a/website/docs/api/options-stack.mdx b/website/docs/api/options-stack.mdx index 5ea46a521ae..dc2c9342b97 100644 --- a/website/docs/api/options-stack.mdx +++ b/website/docs/api/options-stack.mdx @@ -13,8 +13,8 @@ const options = { title: {}, subtitle: {}, backButton: {}, - background: {} - } + background: {}, + }, }; ``` @@ -34,45 +34,61 @@ Determines if changing the TopBar visibility will be animated or not. | ------- | -------- | -------- | | boolean | No | Both | +### `animateLeftButtons` + +Determines if changing the left buttons will be animated. + +| Type | Required | Platform | Default | +| ------- | -------- | -------- | ------- | +| boolean | No | Both | False | + +### `animateRightButtons` + +Determines if changing the right buttons will be animated. + +| Type | Required | Platform | Default | +| ------- | -------- | -------- | ------- | +| boolean | No | Both | False | + ### `title` Controls the top bar title. -| Type | Required | Platform | -| ---------------------- | -------- | -------- | +| Type | Required | Platform | +| -------------------------- | -------- | -------- | | [Title](options-title.mdx) | No | Both | ### `subtitle` Controls the top bar subtitle. -| Type | Required | Platform | -| ---------------------------- | -------- | -------- | +| Type | Required | Platform | +| ------------------------------- | -------- | -------- | | [Subitle](options-subtitle.mdx) | No | Both | ### `backButton` Controls the top bar back button. -| Type | Required | Platform | -| --------------------------------- | -------- | -------- | +| Type | Required | Platform | +| ------------------------------------ | -------- | -------- | | [BackButton](options-backButton.mdx) | No | Both | ### `background` Controls the top bar background. -| Type | Required | Platform | -| --------------------------------- | -------- | -------- | +| Type | Required | Platform | +| ------------------------------------ | -------- | -------- | | [Background](options-background.mdx) | No | Both | ### `scrollEdgeAppearance` Controls the appearance settings when the scrollable content reaches the matching edge of the navigation bar. -| Type | Required | Platform | -| --------------------------------- | -------- | -------- | -| [Scroll Edge Background](options-scrollEdgeAppearance.mdx) | No | iOS 13+ | +| Type | Required | Platform | +| ---------------------------------------------------------- | -------- | -------- | +| [Scroll Edge Background](options-scrollEdgeAppearance.mdx) | No | iOS 13+ | ### `barStyle` @@ -150,7 +166,6 @@ An array of buttons to be displayed at the right-side of the TopBar. Button layo Android currently only supports a single left button and does not support custom left Buttons. ::: - | Type | Required | Platform | | ------------------------------ | -------- | -------- | | [[Button]](options-button.mdx) | No | Both | @@ -175,8 +190,8 @@ Disables border at the bottom of the TopBar. An array of buttons to be displayed at the right side of the TopBar. Button layout is from right to left. See the [Buttons](options-button) section for more details. -| Type | Required | Platform | -| -------------------------- | -------- | -------- | +| Type | Required | Platform | +| ------------------------------ | -------- | -------- | | [[Button]](options-button.mdx) | No | Both | ### `rightButtonColor` @@ -191,11 +206,10 @@ Default color for the right button. Controls the SearchBar. -| Type | Required | Platform | -| ------- | -------- | -------- | +| Type | Required | Platform | +| ---------------------------------------- | -------- | -------- | | [OptionsSearchBar](topBar-searchBar.mdx) | No | iOS 11+ | - :::warning Deprecation warning This option is currently deprecated and will be removed in a future release. Please use the [OptionsSearchBar](topBar-searchBar.mdx) option instead. ::: @@ -228,9 +242,9 @@ This option is currently deprecated and will be removed in a future release. Ple The background color of the UISearchBar's TextField. -| Type | Required | Platform | -| ------ | -------- | -------- | -| Color | No | iOS 13+ | +| Type | Required | Platform | +| ----- | -------- | -------- | +| Color | No | iOS 13+ | :::warning Deprecation warning This option is currently deprecated and will be removed in a future release. Please use the [OptionsSearchBar](topBar-searchBar.mdx) option instead. @@ -240,6 +254,6 @@ This option is currently deprecated and will be removed in a future release. Ple The tint color of the UISearchBar. Affects text selection color, as well as "Cancel" button color. -| Type | Required | Platform | -| ------ | -------- | -------- | -| Color | No | iOS 13+ | +| Type | Required | Platform | +| ----- | -------- | -------- | +| Color | No | iOS 13+ |