Skip to content

Commit 773572b

Browse files
authored
feat: Add @formidable.Label("foo") annotation for custom labels (#67)
* feat: add annotation to set a custom label for fields * feat: also allow Label() to apply to the variants of sealed traits * format * adapt cypress tests for new Label-annotations
1 parent 6613868 commit 773572b

File tree

6 files changed

+24
-10
lines changed

6 files changed

+24
-10
lines changed

cypress/e2e/spec.cy.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ describe('Form interactions', () => {
146146
cy.get('.value').should('have.text', 'Cat(Tiger,4)') // test default value
147147
cy.get('select').select('Dog')
148148
cy.get('.value').should('have.text', 'Dog(,true)') // test default value
149+
cy.get('table > tr:nth-child(2)').should('have.text', 'Hungry?') // test Label("Hungry?")
149150
})
150151
})
151152

@@ -164,10 +165,10 @@ describe('Form interactions', () => {
164165

165166
it('BinaryTree (recursive sealed trait)', () => {
166167
cy.get('.BinaryTree').within(($form) => {
167-
cy.get('select').select('Branch')
168+
cy.get('select').select('Branch Node')
168169
cy.get('.value').should('have.text', 'Branch(Leaf(0),Leaf(0))')
169-
cy.contains('tr', 'right:').contains('select', 'Leaf').select('Branch')
170-
cy.contains('tr', 'left:').contains('select', 'Leaf').select('Branch')
170+
cy.contains('tr', 'right:').contains('select', 'Leaf Node').select('Branch Node')
171+
cy.contains('tr', 'left:').contains('select', 'Leaf Node').select('Branch Node')
171172
cy.get('.value').should('have.text', 'Branch(Branch(Leaf(0),Leaf(0)),Branch(Leaf(0),Leaf(0)))')
172173
cy.get('input[type="text"]').each((elem,index) => cy.wrap(elem).clear().type(index))
173174
cy.get('.value').should('have.text', 'Branch(Branch(Leaf(0),Leaf(1)),Branch(Leaf(2),Leaf(3)))')

demo/src/main/scala/Main.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ case class Person(name: String, age: Int = 5)
1111

1212
sealed trait Pet
1313
object Pet {
14-
case class Dog(name: String, hungry: Boolean = true) extends Pet
14+
case class Dog(name: String, @formidable.Label("Hungry?") hungry: Boolean = true) extends Pet
1515
@formidable.Default
1616
case class Cat(name: String, legs: Int = 4) extends Pet
1717
}
@@ -21,7 +21,9 @@ case class Tree(value: Int = 2, children: Seq[Tree])
2121
sealed trait BinaryTree
2222
object BinaryTree {
2323
@formidable.Default
24-
case class Leaf(value: Int) extends BinaryTree
24+
@formidable.Label("Leaf Node")
25+
case class Leaf(value: Int) extends BinaryTree
26+
@formidable.Label("Branch Node")
2527
case class Branch(left: BinaryTree, right: BinaryTree) extends BinaryTree
2628
}
2729

formidable/src/main/scala-2/Form-Scala2.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ trait FormDerivation {
2525
.map { case (param, subState) =>
2626
val subForm = ((s: Var[param.PType], c) => param.typeclass.render(s, c))
2727
.asInstanceOf[(Var[Any], FormConfig) => VModifier]
28-
param.label -> subForm(subState, config)
28+
val label = param.annotations.collectFirst { case Label(l) => l }.getOrElse(param.label + ":")
29+
label -> subForm(subState, config)
2930
}
3031
)
3132
}: VModifier
@@ -41,11 +42,14 @@ trait FormDerivation {
4142
val selectedSubtype: Var[Subtype[Form, T]] =
4243
selectedValue.imap[Subtype[Form, T]](subType => subType.typeclass.default)(value => ctx.split(value)(identity))
4344

45+
def labelForSubtype(subtype: Subtype[Form, _]): String =
46+
subtype.annotations.collectFirst { case Label(l) => l }.getOrElse(subtype.typeName.short)
47+
4448
config.unionSubform(
4549
config.selectInput[Subtype[Form, T]](
4650
options = ctx.subtypes,
4751
selectedValue = selectedSubtype,
48-
show = subtype => subtype.typeName.short,
52+
show = labelForSubtype,
4953
),
5054
subForm = selectedValue.map { value =>
5155
ctx.split(value) { sub =>

formidable/src/main/scala-3/Form-Scala3.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ trait FormDerivation extends AutoDerivation[Form] {
2222
.zip(subStates)
2323
.map { case (param, subState) =>
2424
val subForm = (param.typeclass.render _).asInstanceOf[(Var[Any], FormConfig) => VModifier]
25-
param.label -> subForm(subState, config)
25+
val label = param.annotations.collectFirst { case Label(l) => l }.getOrElse(param.label + ":")
26+
label -> subForm(subState, config)
2627
}
2728
)
2829
}: VModifier
@@ -40,11 +41,14 @@ trait FormDerivation extends AutoDerivation[Form] {
4041
ctx.choose(value)(_.subtype)
4142
)
4243

44+
def labelForSubtype[Type, SType](subtype: SealedTrait.Subtype[Form, Type, SType]): String =
45+
subtype.annotations.collectFirst { case Label(l) => l }.getOrElse(subtype.typeInfo.short)
46+
4347
config.unionSubform(
4448
config.selectInput[SealedTrait.Subtype[Form, T, _]](
4549
options = ctx.subtypes,
4650
selectedValue = selectedSubtype,
47-
show = subtype => subtype.typeInfo.short,
51+
show = labelForSubtype,
4852
),
4953
selectedValue.map { value =>
5054
ctx.choose(value) { sub =>

formidable/src/main/scala/Form.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import outwatch._
66
// used to mark default cases of sealed traits
77
case class Default() extends scala.annotation.StaticAnnotation
88

9+
// used to customize labels for fields and types
10+
case class Label(label: String) extends scala.annotation.StaticAnnotation
11+
912
trait Form[T] {
1013
def default: T
1114
def render(

formidable/src/main/scala/FormConfig.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ trait FormConfig {
1515
div(display.flex, VModifier.style("gap") := "0.5rem", subForms)
1616
def labeledFormGroup(subForms: Seq[(String, VModifier)]): VModifier =
1717
table(
18-
subForms.map { case (label, subForm) => tr(td(b(label, ": "), verticalAlign := "top"), td(subForm)) }
18+
subForms.map { case (label, subForm) => tr(td(b(label), verticalAlign := "top"), td(subForm)) }
1919
)
2020

2121
def formSequence(subForms: Seq[VModifier], addButton: VModifier): VModifier =

0 commit comments

Comments
 (0)