Skip to content

Commit 98063c5

Browse files
committed
Change logic for single record type input #603 πŸ›πŸ—‘οΈ
1 parent 549a064 commit 98063c5

10 files changed

Lines changed: 124 additions & 21 deletions

File tree

β€Žplayground/src/App.fsβ€Ž

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,5 @@ open Shared
99
[<ReactComponent>]
1010
let App() =
1111
Html.div [
12-
Components.Test(null)
13-
Components.Test("Hello")
12+
Components.RecordTypeContainer()
1413
]

β€Žplayground/src/Components.fsβ€Ž

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,44 @@ module Components
33
open Feliz
44
open Fable.Core
55
open Fable.Core.JsInterop
6+
open Shared
7+
8+
[<AttachMembers>]
9+
type private RecordTypeInput =
10+
{
11+
Name: string;
12+
Job: string;
13+
}
14+
15+
member this.Greet() =
16+
$"Hello, my name is {this.Name} and I work as a {this.Job}."
17+
18+
let private RecordTypeCtx =
19+
let init = Set.empty<RecordTypeInput>
20+
let setter = fun (_: Set<RecordTypeInput>) -> ()
21+
React.createContext({|state = init; setState = setter|})
22+
23+
[<ReactComponent>]
24+
let private SingleRecordTypeInput(recordInput: RecordTypeInput) =
25+
let ctx = React.useContext RecordTypeCtx
26+
Html.div [
27+
Html.div [
28+
prop.testId "single-greet"
29+
prop.text (recordInput.Greet())
30+
]
31+
Html.div [
32+
prop.testId "single-exists"
33+
prop.text (ctx.state |> Set.contains recordInput |> string)
34+
]
35+
]
636

737
[<ReactComponent>]
8-
let Test(input: string | null) =
38+
let RecordTypeContainer() =
39+
let record = React.useMemo (fun () -> { Name = "Alice"; Job = "Engineer" })
40+
let records, setRecords = React.useState(Set [record])
941
Html.div [
10-
match input with
11-
| null -> prop.text "Input is null"
12-
| notNull ->
13-
prop.text (notNull + " from F#")
42+
RecordTypeCtx.Provider({|state = records; setState = setRecords|}, [
43+
for record in records do
44+
SingleRecordTypeInput(record)
45+
])
1446
]

β€Žplayground/src/Main.fsβ€Ž

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,8 @@ open Feliz
44
open Browser.Dom
55

66
let private root = ReactDOM.createRoot(document.getElementById "root")
7-
root.render(App.App())
7+
root.render(
8+
React.StrictMode [
9+
App.App()
10+
]
11+
)

β€Žplayground/src/Shared.fsβ€Ž

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
11
module Shared
22

3-
type AB = A | B
4-
type AbNull = AB | null
5-
6-
type RecordField = { X: string | null }
7-
type TupleField = string * string | null
8-
9-
type NestedGenerics = { Z : List<List<string | null> | null> | null }
10-
113
type TestRecord =
124
{
135
Name: string
14-
Age: string | null
6+
Age: int
157
}
168

179
member this.Greet() =
18-
sprintf "Hello, my name is %s and I am %s years old." this.Name (this.Age |> function | null -> "unknown" | notNull -> notNull)
10+
sprintf "Hello, my name is %s and I am %i years old." this.Name this.Age

β€Žsrc/Feliz.CompilerPlugins/AstUtils.fsβ€Ž

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ let isRecord (compiler: PluginHelper) (fableType: Fable.Type) =
8080
| Fable.Type.DeclaredType (entity, genericArgs) -> compiler.GetEntity(entity).IsFSharpRecord
8181
| _ -> false
8282

83+
let isAnonymRecord (compiler: PluginHelper) (fableType: Fable.Type) =
84+
match fableType with
85+
| Fable.Type.AnonymousRecordType _ -> true
86+
| _ -> false
87+
8388
let isPropertyList (compiler: PluginHelper) (fableType: Fable.Type) =
8489
match fableType with
8590
| Fable.Type.List(genericArg) ->

β€Žsrc/Feliz.CompilerPlugins/CHANGELOG.mdβ€Ž

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616

1717
## [Unreleased]
1818

19+
## 3.0.0-rc.4 - 2025-11-18
20+
21+
### πŸ—‘οΈ Deprecated
22+
23+
- Remove transformation of single input record types for ReactComponent #603 (by @Freymaurer)
24+
25+
### πŸ› Fixed
26+
27+
- Fix equality issue for single input record types for ReactComponent #603 (by @Freymaurer)
28+
1929
## 3.0.0-rc.3 - 2025-11-03
2030

2131
### πŸ› Fixed

β€Žsrc/Feliz.CompilerPlugins/ReactComponent.fsβ€Ž

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ type ReactComponentAttribute(?exportDefault: bool, ?import: string, ?from: strin
202202
if
203203
List.length membArgs = info.Args.Length
204204
&& info.Args.Length = 1
205-
&& AstUtils.isRecord compiler info.Args[0].Type
205+
&& AstUtils.isAnonymousRecord info.Args[0].Type
206206
then
207207
// F# Component { Value = 1 }
208208
// JSX <Component Value={1} />
@@ -308,7 +308,7 @@ type ReactComponentAttribute(?exportDefault: bool, ?import: string, ?from: strin
308308
| None -> decl
309309

310310
// do not rewrite components accepting records as input
311-
if decl.Args.Length = 1 && AstUtils.isRecord compiler decl.Args[0].Type then
311+
if decl.Args.Length = 1 && AstUtils.isAnonymousRecord decl.Args[0].Type then
312312
// check whether the record type is defined in this file
313313
// trigger warning if that is case
314314
let definedInThisFileAndIsUpperCase =

β€Žsrc/Feliz/CHANGELOG.mdβ€Ž

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616

1717
## [Unreleased]
1818

19+
## 3.0.0-rc.10 - 2025-11-18
20+
21+
### πŸ—‘οΈ Deprecated
22+
23+
- Remove transformation of single input record types for ReactComponent #603 (by @Freymaurer)
24+
25+
### πŸ› Fixed
26+
27+
- Fix equality issue for single input record types for ReactComponent #603 (by @Freymaurer)
28+
1929
## 3.0.0-rc.9 - 2025-11-03
2030

2131
### πŸ”„ Changed

β€Žtests/Feliz/Basic.fsβ€Ž

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ module Tests.Basic
33
open Fable.Core
44
open Feliz
55

6-
76
[<Erase; Mangle(false)>]
87
type Components =
98

@@ -172,3 +171,45 @@ type Components =
172171
prop.text c
173172
]
174173
]
174+
175+
module RecordTypeInputTesting =
176+
177+
[<AttachMembers>]
178+
type private RecordTypeInput =
179+
{
180+
Name: string;
181+
Job: string;
182+
}
183+
184+
member this.Greet() =
185+
$"Hello, my name is {this.Name} and I work as a {this.Job}."
186+
187+
let private RecordTypeCtx =
188+
let init = Set.empty<RecordTypeInput>
189+
let setter = fun (_: Set<RecordTypeInput>) -> ()
190+
React.createContext({|state = init; setState = setter|})
191+
192+
[<ReactComponent>]
193+
let private SingleRecordTypeInput(recordInput: RecordTypeInput) =
194+
let ctx = React.useContext RecordTypeCtx
195+
Html.div [
196+
Html.div [
197+
prop.testId "single-greet"
198+
prop.text (recordInput.Greet())
199+
]
200+
Html.div [
201+
prop.testId "single-exists"
202+
prop.text (ctx.state |> Set.contains recordInput |> string)
203+
]
204+
]
205+
206+
[<ReactComponent>]
207+
let RecordTypeContainer() =
208+
let record = React.useMemo (fun () -> { Name = "Alice"; Job = "Developer" })
209+
let records, setRecords = React.useState(Set [record])
210+
Html.div [
211+
RecordTypeCtx.Provider({|state = records; setState = setRecords|}, [
212+
for record in records do
213+
SingleRecordTypeInput(record)
214+
])
215+
]

β€Žtests/Feliz/Basic.test.fsβ€Ž

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,13 @@ describe "Tests for specific style elements" <| fun _ ->
234234

235235
expect(div).toBeInTheDocument()
236236
expect(div).toHaveStyle("font-size: small")
237+
238+
describe "Record Type Input Tests #606, #603" <| fun _ ->
239+
240+
test "Component with record type input passes args correctly" <| fun _ ->
241+
RTL.render(
242+
RecordTypeInputTesting.RecordTypeContainer()
243+
) |> ignore
244+
245+
expect(RTL.screen.getByTestId "single-greet").toHaveTextContent("Hello, my name is Alice and I work as a Developer.")
246+
expect(RTL.screen.getByTestId "single-exists").toHaveTextContent("true")

0 commit comments

Comments
Β (0)