Skip to content

Commit b03ea8e

Browse files
authored
Merge pull request #5 from scrive/custom-format-validation
Custom format validation
2 parents c6d9b5a + 84bf5ae commit b03ea8e

File tree

9 files changed

+72
-41
lines changed

9 files changed

+72
-41
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ Generate validating forms from JSON schemas.
3232

3333
# Changelog
3434

35+
## 4.0.0
36+
37+
* Add settings parameter for custom formats validation.
38+
3539
## 3.0.0
3640

3741
* Validate fields only when `onblur` is triggered, and the field is non-empty

elm.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"name": "scrive/elm-json-forms",
44
"summary": "JSON Forms Implementation in Elm.",
55
"license": "MIT",
6-
"version": "3.0.0",
6+
"version": "4.0.0",
77
"exposed-modules": [
88
"Form",
99
"Form.Widget",

src/Form.elm

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Documentation for the original TypeScript library can be found here: <https://js
99
-}
1010

1111
import Form.Error as Error
12-
import Form.State
12+
import Form.State exposing (Settings)
1313
import Form.Validation exposing (validate)
1414
import Form.Widget
1515
import Form.Widget.Generate
@@ -52,13 +52,14 @@ causes the resulting form to differ from json-forms.io specification. These diff
5252
should be documented.
5353
5454
-}
55-
init : UI.DefOptions -> String -> Schema -> Maybe UiSchema -> Form
56-
init options id schema uiSchema =
55+
init : Settings -> UI.DefOptions -> String -> Schema -> Maybe UiSchema -> Form
56+
init settings options id schema uiSchema =
5757
{ schema = schema
5858
, uiSchema = Maybe.withDefaultLazy (\() -> generateUiSchema schema) uiSchema
5959
, uiSchemaIsGenerated = uiSchema == Nothing
60-
, state = Form.State.initState id (defaultValue schema) (validate schema)
60+
, state = Form.State.initState id (defaultValue schema) (validate settings schema)
6161
, defaultOptions = options
62+
, settings = settings
6263
}
6364

6465

@@ -121,7 +122,7 @@ setSchema schema form =
121122

122123
else
123124
form.uiSchema
124-
, state = Form.State.initState form.state.formId (defaultValue schema) (validate schema)
125+
, state = Form.State.initState form.state.formId (defaultValue schema) (validate form.settings schema)
125126
}
126127

127128

@@ -145,7 +146,7 @@ update msg form =
145146
{ form
146147
| state =
147148
Form.State.updateState
148-
(validate form.schema)
149+
(validate form.settings form.schema)
149150
msg
150151
form.state
151152
}
@@ -170,7 +171,7 @@ The value is present only if form validation passes with no errors.
170171
-}
171172
getSubmitValue : Form -> Maybe Value
172173
getSubmitValue form =
173-
validate form.schema form.state.value
174+
validate form.settings form.schema form.state.value
174175
|> Result.toMaybe
175176

176177

src/Form/Error.elm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type ErrorValue
2222
= Empty
2323
| InvalidString
2424
| InvalidFormat TextFormat
25+
| InvalidCustomFormat String
2526
| InvalidInt
2627
| InvalidFloat
2728
| InvalidBool

src/Form/State.elm

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module Form.State exposing
22
( Form
33
, FormState
44
, Msg(..)
5+
, Settings
56
, ValidateWidgets(..)
67
, getErrorAt
78
, initState
@@ -27,6 +28,7 @@ type alias Form =
2728
, uiSchemaIsGenerated : Bool
2829
, state : FormState
2930
, defaultOptions : UI.DefOptions
31+
, settings : Settings
3032
}
3133

3234

@@ -40,6 +42,17 @@ type alias FormState =
4042
}
4143

4244

45+
{-| Settings for forms initialization:
46+
47+
- `customFormats` where keys are the accepted custom formats (e.g., personal-number-se, personal-number-no, personal-number-dk,
48+
company-number-dk, personal-number-fi) and values are validation functions for the formats.
49+
50+
-}
51+
type alias Settings =
52+
{ customFormats : Dict String (String -> Result () String)
53+
}
54+
55+
4356
{-| Controls which widgets should show validation errors.
4457
4558
All - all widgets should show validation errors. Used after a form submission attempt.

src/Form/Validation.elm

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
module Form.Validation exposing (validate)
22

3-
import Form.Error as Error exposing (ErrorValue(..))
3+
import Dict
4+
import Form.Error as Error exposing (ErrorValue(..), TextFormat(..))
45
import Form.Normalization exposing (normalizeValue)
56
import Form.Regex
7+
import Form.State exposing (Settings)
68
import Json.Decode as Decode exposing (Value)
79
import Json.Encode as Encode
810
import Json.Schema.Definitions
@@ -19,17 +21,17 @@ import Set
1921
import Validation exposing (Validation, error)
2022

2123

22-
validate : Schema -> Value -> Validation Value
23-
validate schema rawValue =
24+
validate : Settings -> Schema -> Value -> Validation Value
25+
validate settings schema rawValue =
2426
let
2527
value =
2628
normalizeValue rawValue
2729
in
28-
Validation.voidRight value <| validateSchema schema value
30+
Validation.voidRight value <| validateSchema settings schema value
2931

3032

31-
validateSchema : Schema -> Value -> Validation Value
32-
validateSchema schema rawValue =
33+
validateSchema : Settings -> Schema -> Value -> Validation Value
34+
validateSchema settings schema rawValue =
3335
let
3436
value =
3537
normalizeValue rawValue
@@ -44,29 +46,29 @@ validateSchema schema rawValue =
4446
Validation.fail (error <| Unimplemented "Boolean schemas are not implemented.")
4547

4648
ObjectSchema objectSchema ->
47-
validateSubSchema objectSchema value
49+
validateSubSchema settings objectSchema value
4850

4951

50-
validateSubSchema : SubSchema -> Value -> Validation Value
51-
validateSubSchema schema =
52+
validateSubSchema : Settings -> SubSchema -> Value -> Validation Value
53+
validateSubSchema settings schema =
5254
let
5355
typeValidations : Value -> Validation Value
5456
typeValidations =
5557
case schema.type_ of
5658
SingleType type_ ->
57-
validateSingleType schema type_
59+
validateSingleType settings schema type_
5860

5961
AnyType ->
6062
Validation.succeed
6163

6264
NullableType type_ ->
6365
Validation.oneOf
6466
[ \v -> Result.map (always Encode.null) <| validateNull v
65-
, validateSingleType schema type_
67+
, validateSingleType settings schema type_
6668
]
6769

6870
UnionType types ->
69-
Validation.oneOf <| List.map (\type_ -> validateSingleType schema type_) types
71+
Validation.oneOf <| List.map (\type_ -> validateSingleType settings schema type_) types
7072
in
7173
Validation.validateAll
7274
[ Validation.whenJust schema.const validateConst
@@ -75,8 +77,8 @@ validateSubSchema schema =
7577
]
7678

7779

78-
validateSingleType : SubSchema -> SingleType -> Value -> Validation Value
79-
validateSingleType schema type_ value =
80+
validateSingleType : Settings -> SubSchema -> SingleType -> Value -> Validation Value
81+
validateSingleType settings schema type_ value =
8082
case type_ of
8183
ObjectType ->
8284
let
@@ -99,7 +101,7 @@ validateSingleType schema type_ value =
99101
Ok Encode.null
100102

101103
( Just val, _ ) ->
102-
validateSchema propSchema val
104+
validateSchema settings propSchema val
103105
in
104106
Validation.validateAll (List.map (\( key, propSchema ) _ -> validateKey key propSchema) propList) value
105107

@@ -113,7 +115,7 @@ validateSingleType schema type_ value =
113115
Result.map Encode.bool <| validateBool value
114116

115117
StringType ->
116-
Result.map Encode.string <| validateString schema value
118+
Result.map Encode.string <| validateString settings schema value
117119

118120
NullType ->
119121
Result.map (always Encode.null) <| validateNull value
@@ -122,8 +124,8 @@ validateSingleType schema type_ value =
122124
Err <| error (Error.Unimplemented "array")
123125

124126

125-
validateString : SubSchema -> Value -> Validation String
126-
validateString schema v =
127+
validateString : Settings -> SubSchema -> Value -> Validation String
128+
validateString settings schema v =
127129
case Decode.decodeValue Decode.string v of
128130
Err _ ->
129131
Err <| error Error.InvalidString
@@ -133,31 +135,37 @@ validateString schema v =
133135
[ Validation.whenJust schema.minLength validateMinLength
134136
, Validation.whenJust schema.maxLength validateMaxLength
135137
, Validation.whenJust schema.pattern validatePattern -- TODO: check specs if this is correct
136-
, Validation.whenJust schema.format validateFormat -- TODO: check specs if this is correct
138+
, Validation.whenJust schema.format (validateFormat settings) -- TODO: check specs if this is correct
137139
]
138140
s
139141

140142

141-
validateFormat : String -> String -> Validation String
142-
validateFormat format v =
143+
validateFormat : Settings -> String -> String -> Validation String
144+
validateFormat settings format value =
143145
case format of
144146
"date-time" ->
145-
validateRegex Form.Regex.dateTime Error.DateTime v
147+
validateRegex Form.Regex.dateTime Error.DateTime value
146148

147149
"date" ->
148-
validateRegex Form.Regex.date Error.Date v
150+
validateRegex Form.Regex.date Error.Date value
149151

150152
"time" ->
151-
validateRegex Form.Regex.time Error.Time v
153+
validateRegex Form.Regex.time Error.Time value
152154

153155
"email" ->
154-
validateRegex Form.Regex.email Error.Email v
156+
validateRegex Form.Regex.email Error.Email value
155157

156158
"phone" ->
157-
validateRegex Form.Regex.phone Error.Phone v
159+
validateRegex Form.Regex.phone Error.Phone value
158160

159-
_ ->
160-
Validation.succeed v
161+
customFormat ->
162+
let
163+
customValidation =
164+
Dict.get customFormat settings.customFormats
165+
|> Maybe.map (\validation -> validation value)
166+
|> Maybe.withDefault (Result.Ok value)
167+
in
168+
Result.mapError (\_ -> error (Error.InvalidCustomFormat customFormat)) customValidation
161169

162170

163171
validatePattern : String -> String -> Validation String

src/Form/Widget/Generate.elm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ goWidget form uiState =
3838
let
3939
ruleEffect : Maybe Rule.AppliedEffect
4040
ruleEffect =
41-
Rule.computeRule form.state.value (UI.getRule uiState.uiSchema)
41+
Rule.computeRule form.settings form.state.value (UI.getRule uiState.uiSchema)
4242

4343
newUiState =
4444
{ uiState | disabled = ruleEffect == Just Rule.Disabled }
@@ -116,7 +116,7 @@ categorizationWidget form uiState categorization =
116116
Maybe.withDefault 0 <| Dict.get uiState.uiPath form.state.categoryFocus
117117

118118
categoryButton ix cat =
119-
if Rule.computeRule form.state.value cat.rule == Just Rule.Hidden then
119+
if Rule.computeRule form.settings form.state.value cat.rule == Just Rule.Hidden then
120120
Nothing
121121

122122
else

src/Form/Widget/View.elm

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,9 @@ errorString error =
332332
InvalidFormat _ ->
333333
"not the correct format"
334334

335+
InvalidCustomFormat _ ->
336+
"not the correct format"
337+
335338
InvalidInt ->
336339
"not a valid integer"
337340

src/UiSchema/Rule.elm

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module UiSchema.Rule exposing (AppliedEffect(..), computeRule)
22

3+
import Form.State exposing (Settings)
34
import Form.Validation exposing (validate)
45
import Json.Decode exposing (Value)
56
import Json.Pointer as Pointer
@@ -12,16 +13,16 @@ type AppliedEffect
1213
| Disabled
1314

1415

15-
computeRule : Value -> Maybe UI.Rule -> Maybe AppliedEffect
16-
computeRule formValue mRule =
16+
computeRule : Settings -> Value -> Maybe UI.Rule -> Maybe AppliedEffect
17+
computeRule settings formValue mRule =
1718
let
1819
condition rule =
1920
case Pointer.pointedValue rule.condition.scope formValue of
2021
Nothing ->
2122
False
2223

2324
Just v ->
24-
Validation.isOk <| validate rule.condition.schema v
25+
Validation.isOk <| validate settings rule.condition.schema v
2526

2627
go rule =
2728
case ( rule.effect, condition rule ) of

0 commit comments

Comments
 (0)