diff --git a/Example/Tests/JJFloatingActionButtonSpec.swift b/Example/Tests/JJFloatingActionButtonSpec.swift index 8fe1f084..fe4b66f5 100644 --- a/Example/Tests/JJFloatingActionButtonSpec.swift +++ b/Example/Tests/JJFloatingActionButtonSpec.swift @@ -48,12 +48,12 @@ class JJFloatingActionButtonSpec: QuickSpec { it("does not open") { actionButton.open(animated: false) - expect(actionButton.buttonState).to(equal(JJFloatingActionButtonState.closed)) + expect(actionButton.buttonState) == .closed } it("does not open when tapped") { actionButton.sendActions(for: .touchUpInside) - expect(actionButton.buttonState).toNotEventually(equal(JJFloatingActionButtonState.open)) + expect(actionButton.buttonState) == .closed } it("looks correct by default") { @@ -206,14 +206,15 @@ class JJFloatingActionButtonSpec: QuickSpec { it("opens when tapped") { actionButton.sendActions(for: .touchUpInside) - expect(actionButton.buttonState).toEventually(equal(JJFloatingActionButtonState.open)) + expect(actionButton.buttonState) == .opening + expect(actionButton.buttonState).toEventually(equal(.open)) } it("opens when tapped twice") { actionButton.sendActions(for: .touchUpInside) actionButton.sendActions(for: .touchUpInside) - expect(actionButton.buttonState).toEventually(equal(JJFloatingActionButtonState.opening)) - expect(actionButton.buttonState).toEventually(equal(JJFloatingActionButtonState.open)) + expect(actionButton.buttonState) == .opening + expect(actionButton.buttonState).toEventually(equal(.open)) } context("and is opened") { @@ -222,7 +223,7 @@ class JJFloatingActionButtonSpec: QuickSpec { } it("has state open") { - expect(actionButton.buttonState) == JJFloatingActionButtonState.open + expect(actionButton.buttonState) == .open } it("items look correct") { @@ -246,7 +247,7 @@ class JJFloatingActionButtonSpec: QuickSpec { it("can't be opened again") { actionButton.open(animated: true) - expect(actionButton.buttonState) != JJFloatingActionButtonState.opening + expect(actionButton.buttonState) != .opening } context("and overlay is tapped") { @@ -255,7 +256,8 @@ class JJFloatingActionButtonSpec: QuickSpec { } it("closes") { - expect(actionButton.buttonState).toEventually(equal(JJFloatingActionButtonState.closed)) + expect(actionButton.buttonState) == .closing + expect(actionButton.buttonState).toEventually(equal(.closed)) } } @@ -265,11 +267,13 @@ class JJFloatingActionButtonSpec: QuickSpec { } it("closes") { - expect(actionButton.buttonState).toEventually(equal(JJFloatingActionButtonState.closed)) + expect(actionButton.buttonState) == .closing + expect(actionButton.buttonState).toEventually(equal(.closed)) } it("does not perform action") { - expect(action).toNotEventually(equal("done!")) + waitUntil(timeout: 1.5) + expect(action) != "done!" } } @@ -280,13 +284,57 @@ class JJFloatingActionButtonSpec: QuickSpec { } it("closes") { - expect(actionButton.buttonState).toEventually(equal(JJFloatingActionButtonState.closed)) + expect(actionButton.buttonState) == .closing + expect(actionButton.buttonState).toEventually(equal(.closed)) } it("performs action") { expect(action).toEventually(equal("done!")) } } + + context("with closeAutomatically disabled") { + beforeEach { + actionButton.closeAutomatically = false + } + + context("and item is tapped") { + beforeEach { + let item = actionButton.items[0] + item.sendActions(for: .touchUpInside) + } + + it("stays open") { + expect(actionButton.buttonState) == .open + } + + it("performs action") { + expect(action).toEventually(equal("done!")) + } + } + } + + context("with closeAutomatically enabled") { + beforeEach { + actionButton.closeAutomatically = true + } + + context("and item is tapped") { + beforeEach { + let item = actionButton.items[0] + item.sendActions(for: .touchUpInside) + } + + it("closes") { + expect(actionButton.buttonState) == .closing + expect(actionButton.buttonState).toEventually(equal(.closed)) + } + + it("performs action") { + expect(action).toEventually(equal("done!")) + } + } + } } context("and is opened and closed") { @@ -301,7 +349,7 @@ class JJFloatingActionButtonSpec: QuickSpec { it("can't be closed again") { actionButton.close(animated: true) - expect(actionButton.buttonState) != JJFloatingActionButtonState.closing + expect(actionButton.buttonState) != .closing } } @@ -311,11 +359,11 @@ class JJFloatingActionButtonSpec: QuickSpec { } it("has state opening") { - expect(actionButton.buttonState) == JJFloatingActionButtonState.opening + expect(actionButton.buttonState) == .opening } it("has eventually state open") { - expect(actionButton.buttonState).toEventually(equal(JJFloatingActionButtonState.open)) + expect(actionButton.buttonState).toEventually(equal(.open)) } } @@ -326,11 +374,11 @@ class JJFloatingActionButtonSpec: QuickSpec { } it("has state closing") { - expect(actionButton.buttonState) == JJFloatingActionButtonState.closing + expect(actionButton.buttonState) == .closing } it("has eventually state closed") { - expect(actionButton.buttonState).toEventually(equal(JJFloatingActionButtonState.closed)) + expect(actionButton.buttonState).toEventually(equal(.closed)) } } @@ -392,8 +440,8 @@ class JJFloatingActionButtonSpec: QuickSpec { actionButton.sendActions(for: .touchUpInside) } - it("does not open") { - expect(actionButton.buttonState).toNotEventually(equal(JJFloatingActionButtonState.open)) + it("stays closed") { + expect(actionButton.buttonState) == .closed } it("performs action") { @@ -407,7 +455,7 @@ class JJFloatingActionButtonSpec: QuickSpec { } it("has state closed") { - expect(actionButton.buttonState) == JJFloatingActionButtonState.closed + expect(actionButton.buttonState) == .closed } it("looks correct") { @@ -422,7 +470,7 @@ class JJFloatingActionButtonSpec: QuickSpec { } it("opens") { - expect(actionButton.buttonState) == JJFloatingActionButtonState.open + expect(actionButton.buttonState) == .open } it("looks correct") { @@ -446,9 +494,9 @@ class JJFloatingActionButtonSpec: QuickSpec { actionButton.addItem(title: "item 2", image: #imageLiteral(resourceName: "Baloon").withRenderingMode(.alwaysTemplate)) } - it("does not open when tapped") { + it("stays closed") { actionButton.sendActions(for: .touchUpInside) - expect(actionButton.buttonState).toNotEventually(equal(JJFloatingActionButtonState.open)) + expect(actionButton.buttonState) == .closed } } } @@ -519,3 +567,12 @@ class JJFloatingActionButtonSpec: QuickSpec { } } } + +func waitUntil(timeout: TimeInterval = AsyncDefaults.Timeout) { + waitUntil(timeout: timeout + 1, action: { done in + DispatchQueue.global().async { + Thread.sleep(forTimeInterval: timeout) + done() + } + }) +} diff --git a/Sources/JJFloatingActionButton.swift b/Sources/JJFloatingActionButton.swift index f9af69e2..315d1583 100644 --- a/Sources/JJFloatingActionButton.swift +++ b/Sources/JJFloatingActionButton.swift @@ -162,7 +162,7 @@ import UIKit @objc public var itemAnimationConfiguration: JJItemAnimationConfiguration = .popUp() /// When enabled and only one action item is added, the floating action button will not open, - /// but the action from the action item will be executed direclty when the button is tapped. + /// but the action from the action item will be executed directly when the button is tapped. /// Also the image of the floating action button will be replaced with the one from the action item. /// /// Default is `true`. @@ -173,6 +173,15 @@ import UIKit } } + /// When enabled, the floating action button will close after an action item was tapped, + /// otherwise the action button will stay open and has to be closed explicitly. + /// + /// Default is `true`. + /// + /// - SeeAlso: `close` + /// + @objc @IBInspectable public var closeAutomatically: Bool = true + /// The current state of the floating action button. /// Possible values are /// - `.opening` @@ -526,7 +535,9 @@ fileprivate extension JJFloatingActionButton { } @objc func itemWasTapped(sender: JJActionItem) { - close() + if closeAutomatically { + close() + } sender.callAction() }