Define and set RefTarget in TokamakUIKit (#54)
Also add `UIView.animate` animation effect to example code. Also removes redundant `Updatable` protocol. Resolves #49 * Define and set RefTarget in TokamakUIKit * Add animation example * Rename backgroundColor to currentColor * Add comments to Animation example
This commit is contained in:
parent
5fabe139dd
commit
923ffd02fd
|
@ -15,6 +15,7 @@
|
|||
A6D5AF87221B131400DBF186 /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6D5AF86221B131400DBF186 /* Image.swift */; };
|
||||
C449B806DFEE55B6CEE6478C /* libPods-TokamakDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B96B435A9D67621D318616E /* libPods-TokamakDemo.a */; };
|
||||
D11DB6432219C03000013FC3 /* Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D11DB6422219C03000013FC3 /* Timer.swift */; };
|
||||
D1BB3D302223F6B400C30062 /* Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1BB3D2F2223F6B400C30062 /* Animation.swift */; };
|
||||
D1BFAF772215795900845EA0 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1BFAF762215795900845EA0 /* Router.swift */; };
|
||||
D1BFAF792215800A00845EA0 /* Counter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1BFAF782215800A00845EA0 /* Counter.swift */; };
|
||||
D1BFAF7B22158B4000845EA0 /* List.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1BFAF7A22158B4000845EA0 /* List.swift */; };
|
||||
|
@ -43,6 +44,7 @@
|
|||
A9EEF813955DAEEFE1D52ED4 /* Pods-TokamakDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TokamakDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TokamakDemo/Pods-TokamakDemo.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
C6DA99382B6892EAB361742F /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
|
||||
D11DB6422219C03000013FC3 /* Timer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Timer.swift; sourceTree = "<group>"; };
|
||||
D1BB3D2F2223F6B400C30062 /* Animation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Animation.swift; sourceTree = "<group>"; };
|
||||
D1BFAF762215795900845EA0 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = "<group>"; };
|
||||
D1BFAF782215800A00845EA0 /* Counter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Counter.swift; sourceTree = "<group>"; };
|
||||
D1BFAF7A22158B4000845EA0 /* List.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = List.swift; sourceTree = "<group>"; };
|
||||
|
@ -162,6 +164,7 @@
|
|||
D1F2C3262214407B008358DC /* TableModal.swift */,
|
||||
D11DB6422219C03000013FC3 /* Timer.swift */,
|
||||
A6D5AF86221B131400DBF186 /* Image.swift */,
|
||||
D1BB3D2F2223F6B400C30062 /* Animation.swift */,
|
||||
);
|
||||
path = Components;
|
||||
sourceTree = "<group>";
|
||||
|
@ -283,6 +286,7 @@
|
|||
D1F7185D2215A4A1004E5951 /* LayerProps.swift in Sources */,
|
||||
D1BFAF792215800A00845EA0 /* Counter.swift in Sources */,
|
||||
D1F718612215A617004E5951 /* Modals.swift in Sources */,
|
||||
D1BB3D302223F6B400C30062 /* Animation.swift in Sources */,
|
||||
D1F7185522159EAD004E5951 /* DatePickers.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
//
|
||||
// Animation.swift
|
||||
// TokamakDemo
|
||||
//
|
||||
// Created by Max Desiatov on 25/02/2019.
|
||||
// Copyright © 2019 Tokamak. All rights reserved.
|
||||
//
|
||||
|
||||
import Tokamak
|
||||
|
||||
struct Animation: CompositeComponent {
|
||||
private static let colors: [(Color, String)] = [
|
||||
(.white, "white"),
|
||||
(.red, "red"),
|
||||
(.green, "green"),
|
||||
(.blue, "blue"),
|
||||
]
|
||||
|
||||
typealias Props = Null
|
||||
typealias Children = [AnyNode]
|
||||
|
||||
static func render(props: Null, children: Children, hooks: Hooks) -> AnyNode {
|
||||
let previousColor = hooks.state(0)
|
||||
let currentColor = hooks.state(0)
|
||||
let ref = hooks.ref(type: UIView.self)
|
||||
|
||||
hooks.effect(currentColor.value) {
|
||||
guard let view = ref.value else { return }
|
||||
|
||||
guard currentColor.value != previousColor.value else {
|
||||
view.backgroundColor = UIColor(colors[currentColor.value].0)
|
||||
return
|
||||
}
|
||||
|
||||
UIView.animate(withDuration: 0.5, animations: {
|
||||
view.backgroundColor = UIColor(colors[currentColor.value].0)
|
||||
}, completion: { _ in
|
||||
previousColor.set(currentColor.value)
|
||||
})
|
||||
}
|
||||
|
||||
return View.node(
|
||||
.init(Style(
|
||||
Edges.equal(to: .parent),
|
||||
backgroundColor: .white
|
||||
)),
|
||||
StackView.node(.init(
|
||||
Edges.equal(to: .safeArea),
|
||||
axis: .vertical,
|
||||
distribution: .fillEqually
|
||||
), [
|
||||
View.node(ref: ref, .init(), children),
|
||||
SegmentedControl.node(
|
||||
.init(
|
||||
value: currentColor.value,
|
||||
valueHandler: Handler {
|
||||
// sometimes UISegmentedControl allows deselecting all segments
|
||||
guard $0 >= 0 else { return }
|
||||
currentColor.set($0)
|
||||
}
|
||||
),
|
||||
colors.map { $0.1 }
|
||||
),
|
||||
])
|
||||
)
|
||||
}
|
||||
}
|
|
@ -29,42 +29,18 @@ struct NavigationModal: PureLeafComponent {
|
|||
}
|
||||
}
|
||||
|
||||
struct SimpleModal: LeafComponent {
|
||||
struct SimpleModal: PureLeafComponent {
|
||||
struct Props: Equatable {
|
||||
let isPresented: State<Bool>
|
||||
}
|
||||
|
||||
private static let colors: [(Color, String)] = [
|
||||
(.white, "white"),
|
||||
(.red, "red"),
|
||||
(.green, "green"),
|
||||
(.blue, "blue"),
|
||||
]
|
||||
|
||||
static func render(props: Props, hooks: Hooks) -> AnyNode {
|
||||
let backgroundColor = hooks.state(0)
|
||||
|
||||
static func render(props: Props) -> AnyNode {
|
||||
return props.isPresented.value ? ModalPresenter.node(
|
||||
View.node(
|
||||
.init(Style(
|
||||
Edges.equal(to: .parent),
|
||||
backgroundColor: colors[backgroundColor.value].0
|
||||
)),
|
||||
StackView.node(.init(
|
||||
Edges.equal(to: .parent),
|
||||
axis: .vertical,
|
||||
distribution: .fillEqually
|
||||
), [
|
||||
Button.node(.init(
|
||||
onPress: Handler { props.isPresented.set(false) }
|
||||
), "Close Modal"),
|
||||
SegmentedControl.node(
|
||||
.init(value: backgroundColor.value,
|
||||
valueHandler: Handler(backgroundColor.set)),
|
||||
colors.map { $0.1 }
|
||||
),
|
||||
])
|
||||
)
|
||||
Animation.node(Null(),
|
||||
Button.node(.init(
|
||||
Style(Center.equal(to: .parent)),
|
||||
onPress: Handler { props.isPresented.set(false) }
|
||||
), "Close Modal"))
|
||||
) : Null.node()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ struct TimerCounter: LeafComponent {
|
|||
let timer = hooks.ref(type: Timer.self)
|
||||
let interval = hooks.state(1.0)
|
||||
|
||||
hooks.effect(interval.value) { () -> () -> () in
|
||||
hooks.finalizedEffect(interval.value) {
|
||||
timer.value = Timer.scheduledTimer(
|
||||
withTimeInterval: interval.value,
|
||||
repeats: true
|
||||
|
|
|
@ -18,6 +18,7 @@ enum AppRoute: String, CaseIterable {
|
|||
case layerProps = "Layer Props"
|
||||
case timer
|
||||
case image
|
||||
case animation
|
||||
}
|
||||
|
||||
extension AppRoute: CustomStringConvertible {
|
||||
|
@ -59,6 +60,8 @@ struct Router: NavigationRouter {
|
|||
result = TimerCounter.node()
|
||||
case .image:
|
||||
result = ImageExample.node()
|
||||
case .animation:
|
||||
result = Animation.node()
|
||||
}
|
||||
|
||||
return NavigationItem.node(
|
||||
|
|
|
@ -77,6 +77,12 @@ extension Component where Children == [AnyNode] {
|
|||
}
|
||||
}
|
||||
|
||||
extension Component where Props == Null, Children == [AnyNode] {
|
||||
public static func node() -> AnyNode {
|
||||
return node(Null(), [])
|
||||
}
|
||||
}
|
||||
|
||||
extension Component where Props: Default, Props.DefaultValue == Props,
|
||||
Children == [AnyNode] {
|
||||
public static func node(_ child: AnyNode) -> AnyNode {
|
||||
|
@ -127,3 +133,23 @@ extension RefComponent {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension RefComponent where Children == [AnyNode] {
|
||||
public static func node(
|
||||
ref: Ref<RefTarget?>,
|
||||
_ props: Props,
|
||||
_ child: AnyNode
|
||||
) -> AnyNode {
|
||||
return node(ref: ref, props, [child])
|
||||
}
|
||||
|
||||
public static func node(ref: Ref<RefTarget?>, _ props: Props) -> AnyNode {
|
||||
return node(ref: ref, props, [])
|
||||
}
|
||||
}
|
||||
|
||||
extension RefComponent where Children == Null {
|
||||
public static func node(ref: Ref<RefTarget?>, _ props: Props) -> AnyNode {
|
||||
return node(ref: ref, props, Null())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ extension Hooks {
|
|||
closure should return a cleanup closure to be executed before the next
|
||||
call to `render` or when a component is unmounted.
|
||||
*/
|
||||
public func effect(closure: @escaping () -> () -> ()) {
|
||||
public func finalizedEffect(closure: @escaping () -> () -> ()) {
|
||||
scheduleEffect(nil, closure)
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,10 @@ extension Hooks {
|
|||
trigger effect execution (unsubscribing from updates on old user ID and
|
||||
subscribing for new user ID) when the ID has changed.
|
||||
*/
|
||||
public func effect<T>(_ observed: T, closure: @escaping () -> () -> ())
|
||||
public func finalizedEffect<T>(
|
||||
_ observed: T,
|
||||
closure: @escaping () -> () -> ()
|
||||
)
|
||||
where T: Equatable {
|
||||
scheduleEffect(AnyEquatable(observed), closure)
|
||||
}
|
||||
|
|
|
@ -5,16 +5,6 @@
|
|||
// Created by Max Desiatov on 09/02/2019.
|
||||
//
|
||||
|
||||
/// Formalizes an update to a value with a given action. Note that `update`
|
||||
/// function is mutating here to allow efficient in-place updates. As long as
|
||||
/// `Updatable` is implemented on a value type, this still allows freezing it
|
||||
/// into an immutable value when needed.
|
||||
public protocol Updatable {
|
||||
associatedtype Action
|
||||
|
||||
mutating func update(_ action: Action)
|
||||
}
|
||||
|
||||
typealias Updater<T> = (inout T) -> ()
|
||||
|
||||
/** Note that `set` functions are not `mutating`, they never update the
|
||||
|
@ -54,14 +44,6 @@ public struct State<T> {
|
|||
|
||||
extension State: Equatable where T: Equatable {}
|
||||
|
||||
extension State where T: Updatable {
|
||||
/// For any `Reduceable` state you can dispatch an `Action` to reduce that
|
||||
/// state to a different value.
|
||||
public func set(_ action: T.Action) {
|
||||
updateHandler.value { $0.update(action) }
|
||||
}
|
||||
}
|
||||
|
||||
extension Hooks {
|
||||
/** Allows a component to have its own state and to be updated when the state
|
||||
changes. Returns a very simple state container, which on initial call of
|
||||
|
|
|
@ -42,6 +42,8 @@ public final class MountedHostComponent<R: Renderer>: MountedComponent<R> {
|
|||
|
||||
self.target = target
|
||||
|
||||
reconciler.renderer?.update(target: target, with: self)
|
||||
|
||||
switch node.children.value {
|
||||
case let nodes as [AnyNode]:
|
||||
mountedChildren = nodes.map { $0.makeMountedComponent(target) }
|
||||
|
|
|
@ -28,8 +28,6 @@ public final class TestRenderer: Renderer {
|
|||
let result = TestView(component.node)
|
||||
parent.add(subview: result)
|
||||
|
||||
update(target: result, with: component)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
|
|
@ -19,4 +19,8 @@ class ViewBox<T: UIView>: ViewControllerBox<UIViewController> {
|
|||
|
||||
super.init(viewController, node)
|
||||
}
|
||||
|
||||
override var refTarget: Any {
|
||||
return view
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,4 +19,8 @@ class ViewControllerBox<T: UIViewController>: UITarget {
|
|||
override var viewController: UIViewController {
|
||||
return containerViewController
|
||||
}
|
||||
|
||||
override var refTarget: Any {
|
||||
return containerViewController
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ final class TokamakButton: UIButton, Default {
|
|||
|
||||
extension Button: UIControlComponent {
|
||||
typealias Target = TokamakButton
|
||||
public typealias RefTarget = UIButton
|
||||
|
||||
static func update(control box: ControlBox<TokamakButton>,
|
||||
_ props: Button.Props,
|
||||
|
|
|
@ -27,6 +27,7 @@ final class TokamakDatePicker: UIDatePicker, Default, ValueStorage {
|
|||
|
||||
extension DatePicker: UIValueComponent {
|
||||
typealias Target = TokamakDatePicker
|
||||
public typealias RefTarget = UIDatePicker
|
||||
|
||||
static func update(valueBox: ValueControlBox<TokamakDatePicker>,
|
||||
_ props: DatePicker.Props,
|
||||
|
|
|
@ -28,6 +28,8 @@ extension UIImage.RenderingMode {
|
|||
}
|
||||
|
||||
extension Image: UIViewComponent {
|
||||
public typealias RefTarget = UIImageView
|
||||
|
||||
static func update(
|
||||
view box: ViewBox<TokamakImage>,
|
||||
_ props: Image.Props,
|
||||
|
|
|
@ -32,6 +32,8 @@ extension NSTextAlignment {
|
|||
}
|
||||
|
||||
extension Label: UIViewComponent {
|
||||
public typealias RefTarget = UILabel
|
||||
|
||||
static func update(view box: ViewBox<TokamakLabel>,
|
||||
_ props: Label.Props,
|
||||
_ children: String) {
|
||||
|
|
|
@ -9,6 +9,8 @@ import Tokamak
|
|||
import UIKit
|
||||
|
||||
extension ListView: UIViewComponent {
|
||||
public typealias RefTarget = UITableView
|
||||
|
||||
static func box(
|
||||
for view: TokamakTableView,
|
||||
_ viewController: UIViewController,
|
||||
|
|
|
@ -41,7 +41,6 @@ extension NavigationItem: UIHostComponent {
|
|||
|
||||
let viewController = UIViewController()
|
||||
let result = ViewControllerBox(viewController, component.node)
|
||||
update(target: result, node: component.node)
|
||||
|
||||
parent.containerViewController.pushViewController(
|
||||
viewController,
|
||||
|
|
|
@ -25,6 +25,7 @@ final class TokamakSegmentedControl: UISegmentedControl, Default, ValueStorage {
|
|||
|
||||
extension SegmentedControl: UIValueComponent {
|
||||
typealias Target = TokamakSegmentedControl
|
||||
public typealias RefTarget = UISegmentedControl
|
||||
|
||||
static func update(valueBox: ValueControlBox<TokamakSegmentedControl>,
|
||||
_ props: SegmentedControl.Props,
|
||||
|
|
|
@ -16,6 +16,7 @@ final class TokamakSlider: UISlider, Default, ValueStorage {
|
|||
|
||||
extension Slider: UIValueComponent {
|
||||
typealias Target = TokamakSlider
|
||||
public typealias RefTarget = UISlider
|
||||
|
||||
static func update(valueBox: ValueControlBox<TokamakSlider>,
|
||||
_ props: Slider.Props,
|
||||
|
|
|
@ -60,6 +60,8 @@ extension UIStackView.Distribution {
|
|||
}
|
||||
|
||||
extension StackView: UIViewComponent {
|
||||
public typealias RefTarget = UIStackView
|
||||
|
||||
static func update(view box: ViewBox<TokamakStackView>,
|
||||
_ props: StackView.Props,
|
||||
_: [AnyNode]) {
|
||||
|
|
|
@ -16,6 +16,7 @@ final class TokamakStepper: UIStepper, Default, ValueStorage {
|
|||
|
||||
extension Stepper: UIValueComponent {
|
||||
typealias Target = TokamakStepper
|
||||
public typealias RefTarget = UIStepper
|
||||
|
||||
static func update(valueBox: ValueControlBox<TokamakStepper>,
|
||||
_ props: Stepper.Props,
|
||||
|
|
|
@ -27,6 +27,7 @@ final class TokamakSwitch: UISwitch, Default, ValueStorage {
|
|||
|
||||
extension Switch: UIValueComponent {
|
||||
typealias Target = TokamakSwitch
|
||||
public typealias RefTarget = UISwitch
|
||||
|
||||
static func update(valueBox: ValueControlBox<TokamakSwitch>,
|
||||
_ props: Switch.Props,
|
||||
|
|
|
@ -15,6 +15,8 @@ final class TokamakView: UIView, Default {
|
|||
}
|
||||
|
||||
extension View: UIViewComponent {
|
||||
public typealias RefTarget = UIView
|
||||
|
||||
static func update(view: ViewBox<TokamakView>,
|
||||
_ props: View.Props,
|
||||
_: [AnyNode]) {}
|
||||
|
|
|
@ -9,7 +9,7 @@ import Tokamak
|
|||
import UIKit
|
||||
|
||||
extension UIColor {
|
||||
convenience init(_ color: Color) {
|
||||
public convenience init(_ color: Color) {
|
||||
switch color.space {
|
||||
case .sRGB:
|
||||
self.init(red: CGFloat(color.red),
|
||||
|
|
|
@ -9,7 +9,7 @@ import Tokamak
|
|||
import UIKit
|
||||
|
||||
extension UIControl.Event {
|
||||
init(_ value: Event) {
|
||||
public init(_ value: Event) {
|
||||
switch value {
|
||||
case .touchDown:
|
||||
self = .touchDown
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import Tokamak
|
||||
import UIKit
|
||||
|
||||
protocol UIViewComponent: UIHostComponent, HostComponent {
|
||||
protocol UIViewComponent: UIHostComponent, RefComponent {
|
||||
associatedtype Target: UIView & Default
|
||||
|
||||
static func update(view box: ViewBox<Target>,
|
||||
|
@ -94,16 +94,6 @@ extension UIViewComponent where Target == Target.DefaultValue,
|
|||
component: UIKitRenderer.MountedHost,
|
||||
_ renderer: UIKitRenderer
|
||||
) -> UITarget? {
|
||||
guard let children = component.node.children.value as? Children else {
|
||||
childrenAssertionFailure()
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let props = component.node.props.value as? Props else {
|
||||
propsAssertionFailure()
|
||||
return nil
|
||||
}
|
||||
|
||||
let target = Target.defaultValue
|
||||
let result: ViewBox<Target>
|
||||
|
||||
|
@ -167,9 +157,6 @@ extension UIViewComponent where Target == Target.DefaultValue,
|
|||
parentAssertionFailure()
|
||||
}
|
||||
|
||||
applyStyle(result, props)
|
||||
update(view: result, props, children)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,11 @@ struct HackyProvider: SimpleCellProvider {
|
|||
|
||||
class UITarget: Target {
|
||||
var viewController: UIViewController {
|
||||
fatalError("viewController should be overriden in UITarget subclass")
|
||||
fatalError("\(#function) should be overriden in UITarget subclass")
|
||||
}
|
||||
|
||||
var refTarget: Any {
|
||||
fatalError("\(#function) should be overriden in UITarget subclass")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,6 +83,12 @@ final class UIKitRenderer: Renderer {
|
|||
|
||||
rendererComponent.update(target: target,
|
||||
node: component.node)
|
||||
|
||||
guard
|
||||
let componentType = component.type as? AnyRefComponent.Type,
|
||||
let anyRef = component.node.ref else { return }
|
||||
|
||||
componentType.update(ref: anyRef, with: target.refTarget)
|
||||
}
|
||||
|
||||
func unmount(
|
||||
|
|
|
@ -14,22 +14,6 @@ extension Button: RefComponent {
|
|||
public typealias RefTarget = TestView
|
||||
}
|
||||
|
||||
extension Int: Updatable {
|
||||
public enum Action {
|
||||
case increment
|
||||
case decrement
|
||||
}
|
||||
|
||||
public mutating func update(_ action: Int.Action) {
|
||||
switch action {
|
||||
case .decrement:
|
||||
self -= 1
|
||||
case .increment:
|
||||
self += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension Hooks {
|
||||
func custom() -> State<Int> {
|
||||
return state(42)
|
||||
|
@ -42,7 +26,6 @@ struct Test: LeafComponent {
|
|||
static func render(props: Null, hooks: Hooks) -> AnyNode {
|
||||
let state1 = hooks.custom()
|
||||
let state2 = hooks.custom()
|
||||
let state3 = hooks.custom()
|
||||
let ref = hooks.ref(type: TestView.self)
|
||||
|
||||
return StackView.node([
|
||||
|
@ -55,9 +38,6 @@ struct Test: LeafComponent {
|
|||
Button.node(.init(onPress: Handler { state2.set { $0 + 1 } }),
|
||||
"Increment"),
|
||||
Label.node("\(state2.value)"),
|
||||
Button.node(.init(onPress: Handler { state3.set(.increment) }),
|
||||
"Increment"),
|
||||
Label.node("\(state3.value)"),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
@ -74,8 +54,6 @@ final class HooksTests: XCTestCase {
|
|||
let button1Handler = button1Props.handlers[.touchUpInside]?.value,
|
||||
let button2Props = stack.subviews[2].props(Button.Props.self),
|
||||
let button2Handler = button2Props.handlers[.touchUpInside]?.value,
|
||||
let button3Props = stack.subviews[4].props(Button.Props.self),
|
||||
let button3Handler = button3Props.handlers[.touchUpInside]?.value,
|
||||
let button1Ref = stack.subviews[0].node.ref as? Ref<TestView?>
|
||||
else {
|
||||
XCTAssert(false, "components have no handlers")
|
||||
|
@ -89,16 +67,11 @@ final class HooksTests: XCTestCase {
|
|||
button2Handler(())
|
||||
button2Handler(())
|
||||
|
||||
button3Handler(())
|
||||
button3Handler(())
|
||||
button3Handler(())
|
||||
|
||||
let e = expectation(description: "rerender")
|
||||
|
||||
DispatchQueue.main.async {
|
||||
XCTAssertEqual(stack.subviews[1].node.children, AnyEquatable("43"))
|
||||
XCTAssertEqual(stack.subviews[3].node.children, AnyEquatable("44"))
|
||||
XCTAssertEqual(stack.subviews[5].node.children, AnyEquatable("45"))
|
||||
|
||||
e.fulfill()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue