playbook_ui 12.26.1.pre.alpha.railsmultilevelimprovements842 → 12.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_flatpickr_styles.scss +11 -9
  3. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_month_and_year_styles.scss +1 -1
  4. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_week_styles.scss +1 -1
  5. data/app/pb_kits/playbook/pb_form_pill/_form_pill.scss +1 -1
  6. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb +0 -3
  7. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +109 -44
  8. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.html.erb +14 -0
  9. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.jsx +60 -0
  10. data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +3 -1
  11. data/app/pb_kits/playbook/pb_phone_number_input/docs/index.js +1 -0
  12. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +6 -0
  13. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +110 -62
  14. data/app/pb_kits/playbook/pb_rich_text_editor/_trix_styles.scss +2 -2
  15. data/app/pb_kits/playbook/pb_title/_title.scss +1 -1
  16. data/app/pb_kits/playbook/pb_title/_title.tsx +2 -3
  17. data/app/pb_kits/playbook/pb_title/docs/_title_light_weight.html.erb +1 -0
  18. data/app/pb_kits/playbook/pb_title/docs/_title_light_weight.jsx +6 -0
  19. data/app/pb_kits/playbook/pb_title/docs/_title_light_weight.md +1 -2
  20. data/app/pb_kits/playbook/pb_title/title.rb +3 -10
  21. data/app/pb_kits/playbook/pb_title/title.test.js +3 -3
  22. data/app/pb_kits/playbook/utilities/object.ts +3 -0
  23. data/dist/playbook-rails.js +3 -3
  24. data/lib/playbook/forms/builder/intl_telephone_field.rb +12 -0
  25. data/lib/playbook/forms/builder.rb +1 -0
  26. data/lib/playbook/version.rb +1 -1
  27. metadata +10 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 326f8eff50b791dc21ab3ec0bdb295adef8d14d3957277f4f92d8abdfe445686
4
- data.tar.gz: 0a9d5b6672174df0cb045cc3aea6b017d0e30f1a978df27e0529b7f1d3cb3472
3
+ metadata.gz: c8a90666ee111265053efa8a6306dcf5312bd5db5b1f9ed04f9f308efc4b800c
4
+ data.tar.gz: 5ff43b2f9c0b59121483cb30cfd1fcd24ba8fa02c65381000fb86b8ef5097fa9
5
5
  SHA512:
6
- metadata.gz: 1b079256265e6bcdea4576a6930b527b944044f0064936376278147aad59cf1a5b311ba25632ca3878bb50e3fd62a33ccdf17cf805d7dd60eaece7d5d3e640fe
7
- data.tar.gz: fa05822858223b65f2a3a2755a1f7fcecea6946d7a1f31ea40a32ad10ebd5b3df128cc3736fdab16b6329ba283e50d201412a8d79c6027745a646d8c6c94ac05
6
+ metadata.gz: 23a90f57292ebf633ef4d2305b4ca7c038abd1bfa8a0a2e8729c5c279522c265b76ca3e8f616076d5483990467baeaec22999d70dec6cb015f9064a64cba7e6a
7
+ data.tar.gz: ca7a82f0e5d3c9de8df3cef5c4913f1dbbd8890b0b29ebfd563f6cb02d5125c664a0a221cb676eb56a68e1f4be7ce26bd1507c0bf7893530a916ccf8e720891d
@@ -1,4 +1,6 @@
1
1
  // Manual Import -- Flatpickr Styles
2
+ @import "./tokens/typography";
3
+
2
4
  .flatpickr-calendar {
3
5
  background: transparent;
4
6
  opacity: 0;
@@ -295,7 +297,7 @@
295
297
  .flatpickr-current-month {
296
298
  font-size: 135%;
297
299
  line-height: inherit;
298
- font-weight: 300;
300
+ font-weight: $light;
299
301
  color: inherit;
300
302
  position: absolute;
301
303
  width: 75%;
@@ -310,7 +312,7 @@
310
312
  }
311
313
  .flatpickr-current-month span.cur-month {
312
314
  font-family: inherit;
313
- font-weight: 700;
315
+ font-weight: $bolder;
314
316
  color: inherit;
315
317
  display: inline-block;
316
318
  margin-left: 0.5ch;
@@ -341,7 +343,7 @@
341
343
  display: inline-block;
342
344
  font-size: inherit;
343
345
  font-family: inherit;
344
- font-weight: 300;
346
+ font-weight: $light;
345
347
  line-height: inherit;
346
348
  height: auto;
347
349
  border: 0;
@@ -371,7 +373,7 @@
371
373
  cursor: pointer;
372
374
  font-size: inherit;
373
375
  font-family: inherit;
374
- font-weight: 300;
376
+ font-weight: $light;
375
377
  height: auto;
376
378
  line-height: inherit;
377
379
  margin: -1px 0 0 0;
@@ -492,7 +494,7 @@ span.flatpickr-weekday {
492
494
  box-sizing: border-box;
493
495
  color: #393939;
494
496
  cursor: pointer;
495
- font-weight: 400;
497
+ font-weight: $regular;
496
498
  width: 14.2857143%;
497
499
  -webkit-flex-basis: 14.2857143%;
498
500
  -ms-flex-preferred-size: 14.2857143%;
@@ -707,11 +709,11 @@ span.flatpickr-weekday {
707
709
  appearance: textfield;
708
710
  }
709
711
  .flatpickr-time input.flatpickr-hour {
710
- font-weight: bold;
712
+ font-weight: $bolder;
711
713
  }
712
714
  .flatpickr-time input.flatpickr-minute,
713
715
  .flatpickr-time input.flatpickr-second {
714
- font-weight: 400;
716
+ font-weight: $regular;
715
717
  }
716
718
  .flatpickr-time input:focus {
717
719
  outline: 0;
@@ -723,7 +725,7 @@ span.flatpickr-weekday {
723
725
  float: left;
724
726
  line-height: inherit;
725
727
  color: #393939;
726
- font-weight: bold;
728
+ font-weight: $bolder;
727
729
  width: 2%;
728
730
  -webkit-user-select: none;
729
731
  -moz-user-select: none;
@@ -738,7 +740,7 @@ span.flatpickr-weekday {
738
740
  width: 18%;
739
741
  cursor: pointer;
740
742
  text-align: center;
741
- font-weight: 400;
743
+ font-weight: $regular;
742
744
  }
743
745
  .flatpickr-time input:hover,
744
746
  .flatpickr-time .flatpickr-am-pm:hover,
@@ -20,7 +20,7 @@
20
20
  color: $text_lt_default;
21
21
  cursor: pointer;
22
22
  display: inline-block;
23
- font-weight: 400;
23
+ font-weight: $regular;
24
24
  margin: 0.5px;
25
25
  justify-content: center;
26
26
  padding: 10px;
@@ -20,7 +20,7 @@
20
20
  color: $text_lt_default;
21
21
  cursor: pointer;
22
22
  display: inline-block;
23
- font-weight: 400;
23
+ font-weight: $regular;
24
24
  margin: 0.5px;
25
25
  justify-content: center;
26
26
  padding: 10px;
@@ -56,7 +56,7 @@ $form_pill_colors: (
56
56
  height: -moz-fit-content;
57
57
  .pb_form_pill_text, .pb_form_pill_close, .pb_form_pill_tag {
58
58
  font-size: 16px;
59
- font-weight: 400;
59
+ font-weight: $regular;
60
60
  }
61
61
  .pb_form_pill_text, .pb_form_pill_tag {
62
62
  line-height: 1.7;
@@ -9,8 +9,6 @@ module Playbook
9
9
  default: []
10
10
  prop :return_all_selected, type: Playbook::Props::Boolean,
11
11
  default: false
12
- prop :return_complete_data, type: Playbook::Props::Boolean,
13
- default: false
14
12
 
15
13
  def classname
16
14
  generate_classname("pb_multi_level_select")
@@ -22,7 +20,6 @@ module Playbook
22
20
  name: name,
23
21
  treeData: tree_data,
24
22
  returnAllSelected: return_all_selected,
25
- returnCompleteData: return_complete_data,
26
23
  }
27
24
  end
28
25
  end
@@ -1,12 +1,17 @@
1
- import React, { useEffect, useRef, useState } from 'react'
1
+ import React, { forwardRef, useEffect, useRef, useState } from 'react'
2
2
  import classnames from 'classnames'
3
- import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
4
- import { globalProps } from '../utilities/globalProps'
3
+
5
4
  import intlTelInput from 'intl-tel-input'
6
5
  import 'intl-tel-input/build/css/intlTelInput.css'
7
- import TextInput from '../pb_text_input/_text_input'
8
6
  import 'intl-tel-input/build/js/utils.js'
9
7
 
8
+ import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
9
+ import { globalProps } from '../utilities/globalProps'
10
+
11
+ import TextInput from '../pb_text_input/_text_input'
12
+ import { Callback } from '../types'
13
+ import { isEmpty } from '../utilities/object'
14
+
10
15
  declare global {
11
16
  interface Window {
12
17
  intlTelInputGlobals: any
@@ -19,20 +24,25 @@ type PhoneNumberInputProps = {
19
24
  dark?: boolean,
20
25
  data?: { [key: string]: string },
21
26
  disabled?: boolean,
27
+ error?: string,
22
28
  id?: string,
23
29
  initialCountry?: string,
24
30
  isValid?: (valid: boolean) => void,
25
31
  label?: string,
26
32
  name?: string,
27
33
  onChange?: (e: React.FormEvent<HTMLInputElement>) => void,
34
+ onValidate?: Callback<boolean, void>,
28
35
  onlyCountries: string[],
29
36
  preferredCountries?: string[],
37
+ required?: boolean,
30
38
  value?: string,
31
39
  }
32
40
 
33
41
  enum ValidationError {
34
42
  TooShort = 2,
35
43
  TooLong = 3,
44
+ MissingAreaCode = 4,
45
+ SomethingWentWrong = -99
36
46
  }
37
47
 
38
48
  const formatToGlobalCountryName = (countryName: string) => {
@@ -40,10 +50,10 @@ const formatToGlobalCountryName = (countryName: string) => {
40
50
  }
41
51
 
42
52
  const formatAllCountries = () => {
43
- let countryData = window.intlTelInputGlobals.getCountryData()
53
+ const countryData = window.intlTelInputGlobals.getCountryData()
44
54
 
45
55
  for (let i = 0; i < countryData.length; i++) {
46
- let country = countryData[i]
56
+ const country = countryData[i]
47
57
  country.name = formatToGlobalCountryName(country.name)
48
58
  }
49
59
  }
@@ -71,7 +81,9 @@ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
71
81
  onChange = () => {
72
82
  void 0
73
83
  },
84
+ onValidate = () => null,
74
85
  onlyCountries = [],
86
+ required = false,
75
87
  preferredCountries = [],
76
88
  value = "",
77
89
  } = props
@@ -87,39 +99,83 @@ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
87
99
  const inputRef = useRef<HTMLInputElement>()
88
100
  const [inputValue, setInputValue] = useState(value)
89
101
  const [itiInit, setItiInit] = useState<any>()
90
- const [error, setError] = useState('')
102
+ const [error, setError] = useState(props.error)
91
103
  const [dropDownIsOpen, setDropDownIsOpen] = useState(false)
92
104
  const [selectedData, setSelectedData] = useState()
93
105
 
106
+ useEffect(() => {
107
+ if (error?.length > 0) {
108
+ onValidate(false)
109
+ } else {
110
+ onValidate(true)
111
+ }
112
+ }, [error, onValidate])
113
+
114
+ const showFormattedError = (reason = '') => {
115
+ const countryName = itiInit.getSelectedCountryData().name
116
+ const reasonText = reason.length > 0 ? ` (${reason})` : ''
117
+ setError(`Invalid ${countryName} phone number${reasonText}`)
118
+ return true
119
+ }
120
+
94
121
  const validateTooLongNumber = (itiInit: any) => {
95
- const error = itiInit.getValidationError()
122
+ if (!itiInit) return
123
+ if (itiInit.getValidationError() === ValidationError.TooLong) {
124
+ return showFormattedError('too long')
125
+ } else {
126
+ setError('')
127
+ }
128
+ }
96
129
 
97
- if (error === ValidationError.TooLong) {
98
- const countryName = itiInit.getSelectedCountryData().name
99
- setError(`Invalid ${countryName} phone number (too long)`)
130
+ const validateTooShortNumber = (itiInit: any) => {
131
+ if (!itiInit) return
132
+ if (itiInit.getValidationError() === ValidationError.TooShort) {
133
+ return showFormattedError('too short')
100
134
  } else {
101
- setError("")
135
+ if (inputValue.length === 1) {
136
+ return showFormattedError('too short')
137
+ } else {
138
+ setError('')
139
+ }
102
140
  }
103
141
  }
104
142
 
105
- const validateTooShortNumber = () => {
106
- const error = itiInit.getValidationError()
143
+ const validateOnlyNumbers = (itiInit: any) => {
144
+ if (!itiInit) return
145
+ if (inputValue && !containOnlyNumbers(inputValue)) {
146
+ return showFormattedError('enter numbers only')
147
+ }
148
+ }
107
149
 
108
- if (error === ValidationError.TooShort) {
109
- const countryName = itiInit.getSelectedCountryData().name
110
- setError(`Invalid ${countryName} phone number (too short)`)
150
+ const validateUnhandledError = (itiInit: any) => {
151
+ if (!required || !itiInit) return
152
+ if (itiInit.getValidationError() === ValidationError.SomethingWentWrong) {
153
+ if (inputValue.length === 1) {
154
+ return showFormattedError('too short')
155
+ } else if (inputValue.length === 0) {
156
+ setError('Missing phone number')
157
+ return true
158
+ } else {
159
+ return showFormattedError()
160
+ }
111
161
  }
112
162
  }
113
163
 
114
- const validateOnlyNumbers = () => {
115
- if (inputValue && !containOnlyNumbers(inputValue)) {
116
- setError("Invalid phone number. Enter numbers only.")
164
+ const validateMissingAreaCode = (itiInit: any) => {
165
+ if (!required || !itiInit) return
166
+ if (itiInit.getValidationError() === ValidationError.MissingAreaCode) {
167
+ showFormattedError('missing area code')
168
+ return true
117
169
  }
118
170
  }
119
171
 
120
172
  const validateErrors = () => {
121
- validateTooShortNumber()
122
- validateOnlyNumbers()
173
+ if (itiInit) isValid(itiInit.isValidNumber())
174
+ if (validateOnlyNumbers(itiInit)) return
175
+ if (validateTooLongNumber(itiInit)) return
176
+ if (validateTooShortNumber(itiInit)) return
177
+ if (validateUnhandledError(itiInit)) return
178
+ if (validateMissingAreaCode(itiInit)) return
123
179
  }
124
180
 
125
181
  const getCurrentSelectedData = (itiInit: any, inputValue: string) => {
@@ -128,7 +184,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
128
184
 
129
185
  const handleOnChange = (evt: React.ChangeEvent<HTMLInputElement>) => {
130
186
  setInputValue(evt.target.value)
131
- validateTooLongNumber(itiInit)
132
187
  const phoneNumberData = getCurrentSelectedData(itiInit, evt.target.value)
133
188
  setSelectedData(phoneNumberData)
134
189
  onChange(phoneNumberData)
@@ -137,25 +192,24 @@ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
137
192
 
138
193
  // Separating Concerns as React Docs Recommend
139
194
  // This also Fixes things for our react_component rendering on the Rails Side
140
- useEffect(() => {
141
- formatAllCountries()
142
- }, [])
195
+ useEffect(formatAllCountries, [])
143
196
 
144
197
  useEffect(() => {
145
- const telInputInit = new intlTelInput(inputRef.current, {
198
+ const telInputInit = intlTelInput(inputRef.current, {
146
199
  separateDialCode: true,
147
200
  preferredCountries,
148
201
  allowDropdown: !disabled,
202
+ autoInsertDialCode: false,
149
203
  initialCountry,
150
204
  onlyCountries,
151
- }
152
- )
205
+ utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/18.1.6/js/utils.min.js"
206
+ })
153
207
 
154
208
  inputRef.current.addEventListener("countrychange", (evt: Event) => {
155
- validateTooLongNumber(telInputInit)
156
209
  const phoneNumberData = getCurrentSelectedData(telInputInit, (evt.target as HTMLInputElement).value)
157
210
  setSelectedData(phoneNumberData)
158
211
  onChange(phoneNumberData)
212
+ validateErrors()
159
213
  })
160
214
 
161
215
  inputRef.current.addEventListener("open:countrydropdown", () => setDropDownIsOpen(true))
@@ -164,24 +218,35 @@ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
164
218
  setItiInit(telInputInit)
165
219
  }, [])
166
220
 
221
+ let textInputProps: {[key: string]: any} = {
222
+ className: dropDownIsOpen ? 'dropdown_open' : '',
223
+ dark,
224
+ "data-phone-number": JSON.stringify(selectedData),
225
+ disabled,
226
+ error,
227
+ type: 'tel',
228
+ id,
229
+ label,
230
+ name,
231
+ onBlur: validateErrors,
232
+ onChange: handleOnChange,
233
+ value: inputValue
234
+ }
235
+
236
+ let wrapperProps: Record<string, unknown> = { className: classes }
237
+
238
+ if (!isEmpty(aria)) textInputProps = {...textInputProps, ...ariaProps}
239
+ if (!isEmpty(data)) wrapperProps = {...wrapperProps, ...dataProps}
240
+ if (required) textInputProps.required = true
241
+
167
242
  return (
168
- <div {...ariaProps} {...dataProps} className={classes}>
243
+ <div {...wrapperProps}>
169
244
  <TextInput
170
- className={dropDownIsOpen ? 'dropdown_open' : ''}
171
- dark={dark}
172
- data-phone-number={JSON.stringify(selectedData)}
173
- disabled={disabled}
174
- error={error}
175
- id={id}
176
- label={label}
177
- name={name}
178
- onBlur={() => validateErrors()}
179
- onChange={handleOnChange}
180
- ref={inputRef}
181
- value={inputValue}
245
+ ref={inputRef}
246
+ {...textInputProps}
182
247
  />
183
248
  </div>
184
249
  )
185
250
  }
186
251
 
187
- export default PhoneNumberInput
252
+ export default forwardRef(PhoneNumberInput)
@@ -0,0 +1,14 @@
1
+ <form id="example-form-validation" action="" method="get">
2
+ <%= pb_rails("phone_number_input", props: { error: "Missing phone number.", id: "validation", initial_country: "af", value: "", required: true }) %>
3
+ <%= pb_rails("button", props: {html_type: "submit", text: "Save Phone Number"}) %>
4
+ </form>
5
+
6
+ <% content_for(:pb_js) do %>
7
+ <%= javascript_tag do %>
8
+ document.addEventListener('DOMContentLoaded', function () {
9
+ document.querySelector('#example-form-validation').addEventListener('submit', function (e) {
10
+ if (e.target.querySelectorAll('[error]:not([error=""])').length > 0) e.preventDefault();
11
+ })
12
+ })
13
+ <% end %>
14
+ <% end %>
@@ -0,0 +1,60 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Button, FixedConfirmationToast, PhoneNumberInput } from "../../";
3
+
4
+ const PhoneNumberInputValidation = (props) => {
5
+ const [formErrors, setFormErrors] = useState("");
6
+ const [showFormErrors, setShowFormErrors] = useState(false);
7
+ const [phoneNumber, setPhoneNumber] = useState("");
8
+ const [countryCode, setCountryCode] = useState("af");
9
+
10
+ const handleOnValidate = (valid) => {
11
+ setFormErrors(
12
+ valid ? "" : "Please correct the fields below and try again."
13
+ );
14
+ };
15
+
16
+ const handleOnChange = ({ iso2, number }) => {
17
+ setCountryCode(iso2);
18
+ setPhoneNumber(number);
19
+ };
20
+
21
+ const handleOnSubmit = (e) => {
22
+ if (showFormErrors) e.preventDefault()
23
+ }
24
+
25
+ useEffect(() => {
26
+ setShowFormErrors(formErrors.length > 0);
27
+ }, [formErrors]);
28
+
29
+ return (
30
+ <form
31
+ action=""
32
+ method="get"
33
+ onSubmit={handleOnSubmit}
34
+ >
35
+ {showFormErrors && (
36
+ <FixedConfirmationToast
37
+ marginBottom="md"
38
+ status="error"
39
+ text={formErrors}
40
+ />
41
+ )}
42
+ <PhoneNumberInput
43
+ error="Missing phone number."
44
+ id="validation"
45
+ initialCountry={countryCode}
46
+ onChange={handleOnChange}
47
+ onValidate={handleOnValidate}
48
+ required
49
+ value={phoneNumber}
50
+ {...props}
51
+ />
52
+ <Button
53
+ htmlType="submit"
54
+ text="Save Phone Number"
55
+ />
56
+ </form>
57
+ );
58
+ };
59
+
60
+ export default PhoneNumberInputValidation;
@@ -5,9 +5,11 @@ examples:
5
5
  - phone_number_input_preferred_countries: Preferred Countries
6
6
  - phone_number_input_initial_country: Initial Country
7
7
  - phone_number_input_only_countries: Limited Countries
8
+ - phone_number_input_validation: Form Validation
8
9
 
9
10
  rails:
10
11
  - phone_number_input_default: Default
11
12
  - phone_number_input_preferred_countries: Preferred Countries
12
13
  - phone_number_input_initial_country: Initial Country
13
- - phone_number_input_only_countries: Limited Countries
14
+ - phone_number_input_only_countries: Limited Countries
15
+ - phone_number_input_validation: Form Validation
@@ -2,3 +2,4 @@ export { default as PhoneNumberInputDefault } from './_phone_number_input_defaul
2
2
  export { default as PhoneNumberInputPreferredCountries } from './_phone_number_input_preferred_countries'
3
3
  export { default as PhoneNumberInputInitialCountry } from './_phone_number_input_initial_country'
4
4
  export { default as PhoneNumberInputOnlyCountries } from './_phone_number_input_only_countries'
5
+ export { default as PhoneNumberInputValidation } from './_phone_number_input_validation'
@@ -5,6 +5,8 @@ module Playbook
5
5
  class PhoneNumberInput < Playbook::KitBase
6
6
  prop :disabled, type: Playbook::Props::Boolean,
7
7
  default: false
8
+ prop :required, type: Playbook::Props::Boolean,
9
+ default: false
8
10
  prop :initial_country, type: Playbook::Props::String,
9
11
  default: ""
10
12
  prop :label, type: Playbook::Props::String,
@@ -15,6 +17,8 @@ module Playbook
15
17
  default: []
16
18
  prop :preferred_countries, type: Playbook::Props::Array,
17
19
  default: []
20
+ prop :error, type: Playbook::Props::String,
21
+ default: ""
18
22
  prop :value, type: Playbook::Props::String,
19
23
  default: ""
20
24
 
@@ -27,11 +31,13 @@ module Playbook
27
31
  id: id,
28
32
  dark: dark,
29
33
  disabled: disabled,
34
+ error: error,
30
35
  initialCountry: initial_country,
31
36
  label: label,
32
37
  name: name,
33
38
  onlyCountries: only_countries,
34
39
  preferredCountries: preferred_countries,
40
+ required: required,
35
41
  value: value,
36
42
  }
37
43
  end