Skip to content

Commit

Permalink
Merge pull request #1019 from TimLariviere/bypass-view-map-if-same-type
Browse files Browse the repository at this point in the history
Bypass View.map conversion if msg is already of type 'newMsg
  • Loading branch information
TimLariviere authored Oct 20, 2022
2 parents 2fa746e + a9e63d4 commit b051bb8
Show file tree
Hide file tree
Showing 5 changed files with 381 additions and 13 deletions.
205 changes: 205 additions & 0 deletions src/Fabulous.Tests/APISketchTests/APISketchTests.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace Fabulous.Tests.APISketchTests

open Fabulous.StackAllocatedCollections
open Fabulous.Tests.APISketchTests.Platform
open NUnit.Framework

open Platform
Expand Down Expand Up @@ -847,3 +848,207 @@ module Attributes =
instance.ProcessMessage(())

Assert.AreEqual(label.TextColor, "red")

module ViewHelpers =
type ChildMsg = | ChildClick

type ParentMsg =
| ParentClick
| ParentTap
| ChildMessage of ChildMsg

type GrandParentMsg =
| GrandParentClick
| GrandParentTap
| ParentMessage of ParentMsg

[<Test>]
let ``Widget converted with View.map dispatches the right message type`` () =
let mutable expectedMsg = Unchecked.defaultof<ParentMsg>

let childView =
Button("Child button", ChildClick)
.automationId("childButton")

let parentView model =
Stack() {
Button("Parent button", ParentClick)
.automationId("parentButton")

(View.map ChildMessage childView).tap(ParentTap)
}

let init () = true

let update msg model =
Assert.AreEqual(expectedMsg, msg)
not model

let program =
StatefulWidget.mkSimpleView init update parentView

let instance = Run.Instance program
let tree = instance.Start()

let childButton = find<TestButton> tree "childButton"
let parentButton = find<TestButton> tree "parentButton"

expectedMsg <- ParentClick
parentButton.Press()

expectedMsg <- ChildMessage ChildClick
childButton.Press()

[<Test>]
let ``Adding property modifiers to widget converted with View.map is valid`` () =
let childView =
Button("Child button", ChildClick)
.automationId("childButton")

let parentView model =
Stack() {
(View.map ChildMessage childView)
.textColor("blue")
}

let init () = true
let update _msg model = not model

let program =
StatefulWidget.mkSimpleView init update parentView

let instance = Run.Instance program
let tree = instance.Start()

let childButton =
find<TestButton> tree "childButton" :> IText

Assert.AreEqual(childButton.TextColor, "blue")

[<Test>]
let ``Adding event modifiers to widget converted with View.map still dispatches the right message type`` () =
let mutable expectedMsg = Unchecked.defaultof<ParentMsg>

let childView =
Button("Child button", ChildClick)
.automationId("childButton")

let parentView model =
Stack() { (View.map ChildMessage childView).tap(ParentTap) }

let init () = true

let update msg model =
Assert.AreEqual(expectedMsg, msg)
not model

let program =
StatefulWidget.mkSimpleView init update parentView

let instance = Run.Instance program
let tree = instance.Start()

let childButton = find<TestButton> tree "childButton"

expectedMsg <- ParentTap
childButton.Tap()

[<Test>]
let ``Several layers of View.map produce the right result`` () =
let mutable expectedMsg = Unchecked.defaultof<GrandParentMsg>

let childView =
Button("Child button", ChildClick)
.automationId("childButton")

let parentView =
Stack() {
Button("Parent button", ParentClick)
.automationId("parentButton")

(View.map ChildMessage childView).tap(ParentTap)
}

let grandParentView model =
Stack() {
Button("Grand Parent button", GrandParentClick)
.automationId("grandParentButton")

(View.map ParentMessage parentView)
.automationId("parentStack")
.tapContainer(GrandParentTap)
}


let init () = true

let update msg model =
Assert.AreEqual(expectedMsg, msg)
not model

let program =
StatefulWidget.mkSimpleView init update grandParentView

let instance = Run.Instance program
let tree = instance.Start()

let childButton = find<TestButton> tree "childButton"
let parentButton = find<TestButton> tree "parentButton"

let grandParentButton =
find<TestButton> tree "grandParentButton"

let parentStack = find<TestStack> tree "parentStack"

expectedMsg <- ParentMessage(ChildMessage ChildClick)
childButton.Press()

expectedMsg <- ParentMessage ParentTap
childButton.Tap()

expectedMsg <- ParentMessage ParentClick
parentButton.Press()

expectedMsg <- GrandParentClick
grandParentButton.Press()

expectedMsg <- GrandParentTap
parentStack.Tap()

[<Test>]
let ``Cascading View.map produce the right result`` () =
let mutable expectedMsg = Unchecked.defaultof<GrandParentMsg>

let childView =
Button("Child button", ChildClick)
.automationId("childButton")

let parentView =
(View.map ChildMessage childView).tap(ParentTap)

let grandParentView model =
(View.map ParentMessage parentView)
.tap2(GrandParentTap)

let init () = true

let update msg model =
Assert.AreEqual(expectedMsg, msg)
not model

let program =
StatefulWidget.mkSimpleView init update grandParentView

let instance = Run.Instance program
let tree = instance.Start()

let childButton = find<TestButton> tree "childButton"

expectedMsg <- ParentMessage(ChildMessage ChildClick)
childButton.Press()

expectedMsg <- ParentMessage ParentTap
childButton.Tap()

expectedMsg <- GrandParentTap
childButton.Tap2()
82 changes: 82 additions & 0 deletions src/Fabulous.Tests/APISketchTests/TestUI.Attributes.fs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,84 @@ module TestUI_Attributes =

{ Key = key; Name = name }

let defineTappable name : ScalarAttributeDefinition<obj, obj> =
let key =
ScalarAttributeDefinition.CreateAttributeData<obj, obj>(
(fun x -> x),
ScalarAttributeComparers.noCompare,
(fun _ newValueOpt node ->

let btn = node.Target :?> IButton

match node.TryGetHandler<int>(name) with
| ValueNone -> ()
| ValueSome handlerId -> btn.RemoveTapListener handlerId

match newValueOpt with
| ValueNone -> node.SetHandler(name, ValueNone)

| ValueSome msg ->
let handler () = Dispatcher.dispatch node msg

let handlerId = btn.AddTapListener handler
node.SetHandler<int>(name, ValueSome handlerId))
)
|> AttributeDefinitionStore.registerScalar

{ Key = key; Name = name }

let defineTappable2 name : ScalarAttributeDefinition<obj, obj> =
let key =
ScalarAttributeDefinition.CreateAttributeData<obj, obj>(
(fun x -> x),
ScalarAttributeComparers.noCompare,
(fun _ newValueOpt node ->

let btn = node.Target :?> IButton

match node.TryGetHandler<int>(name) with
| ValueNone -> ()
| ValueSome handlerId -> btn.RemoveTap2Listener handlerId

match newValueOpt with
| ValueNone -> node.SetHandler(name, ValueNone)

| ValueSome msg ->
let handler () = Dispatcher.dispatch node msg

let handlerId = btn.AddTap2Listener handler
node.SetHandler<int>(name, ValueSome handlerId))
)
|> AttributeDefinitionStore.registerScalar

{ Key = key; Name = name }

let defineContainerTappable name : ScalarAttributeDefinition<obj, obj> =
let key =
ScalarAttributeDefinition.CreateAttributeData<obj, obj>(
(fun x -> x),
ScalarAttributeComparers.noCompare,
(fun _ newValueOpt node ->

let btn = node.Target :?> IContainer

match node.TryGetHandler<int>(name) with
| ValueNone -> ()
| ValueSome handlerId -> btn.RemoveTapListener handlerId

match newValueOpt with
| ValueNone -> node.SetHandler(name, ValueNone)

| ValueSome msg ->
let handler () = Dispatcher.dispatch node msg

let handlerId = btn.AddTapListener handler
node.SetHandler<int>(name, ValueSome handlerId))
)
|> AttributeDefinitionStore.registerScalar

{ Key = key; Name = name }




Expand All @@ -59,8 +137,12 @@ module TestUI_Attributes =
"Container_Children"
(fun target -> (target :?> IContainer).Children :> System.Collections.Generic.IList<_>)

let Tap = defineContainerTappable "Container_Tap"

module Button =
let Pressed = definePressable "Button_Pressed"
let Tap = defineTappable "Button_Tap"
let Tap2 = defineTappable2 "Button_Tap2"


module Automation =
Expand Down
Loading

0 comments on commit b051bb8

Please sign in to comment.