playbook_ui 12.25.0.pre.alpha.play822bolddefaultfortitle3764 → 12.25.0.pre.alpha.play824786

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/_playbook.scss +1 -0
  3. data/app/pb_kits/playbook/index.js +1 -0
  4. data/app/pb_kits/playbook/pb_avatar/docs/_avatar_swift.md +82 -1
  5. data/app/pb_kits/playbook/pb_detail/_detail.scss +44 -0
  6. data/app/pb_kits/playbook/pb_detail/_detail.tsx +55 -0
  7. data/app/pb_kits/playbook/pb_detail/_detail_mixins.scss +29 -0
  8. data/app/pb_kits/playbook/pb_detail/detail.html.erb +7 -0
  9. data/app/pb_kits/playbook/pb_detail/detail.rb +31 -0
  10. data/app/pb_kits/playbook/pb_detail/detail.test.jsx +46 -0
  11. data/app/pb_kits/playbook/pb_detail/docs/_description.md +1 -0
  12. data/app/pb_kits/playbook/pb_detail/docs/_detail_bold.html.erb +34 -0
  13. data/app/pb_kits/playbook/pb_detail/docs/_detail_bold.jsx +49 -0
  14. data/app/pb_kits/playbook/pb_detail/docs/_detail_bold.md +1 -0
  15. data/app/pb_kits/playbook/pb_detail/docs/_detail_colors.html.erb +24 -0
  16. data/app/pb_kits/playbook/pb_detail/docs/_detail_colors.jsx +38 -0
  17. data/app/pb_kits/playbook/pb_detail/docs/_detail_colors.md +6 -0
  18. data/app/pb_kits/playbook/pb_detail/docs/_detail_default.html.erb +3 -0
  19. data/app/pb_kits/playbook/pb_detail/docs/_detail_default.jsx +13 -0
  20. data/app/pb_kits/playbook/pb_detail/docs/_detail_styled.html.erb +22 -0
  21. data/app/pb_kits/playbook/pb_detail/docs/_detail_styled.jsx +32 -0
  22. data/app/pb_kits/playbook/pb_detail/docs/example.yml +11 -0
  23. data/app/pb_kits/playbook/pb_detail/docs/index.js +4 -0
  24. data/app/pb_kits/playbook/pb_docs/kit_example.html.erb +14 -13
  25. data/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx +3 -2
  26. data/app/pb_kits/playbook/pb_multi_level_select/_helper_functions.tsx +212 -0
  27. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +58 -98
  28. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +340 -86
  29. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.md +1 -1
  30. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_return_all_selected.html.erb +1 -0
  31. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.test.jsx +1 -1
  32. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +109 -44
  33. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.html.erb +14 -0
  34. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.jsx +60 -0
  35. data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +3 -1
  36. data/app/pb_kits/playbook/pb_phone_number_input/docs/index.js +1 -0
  37. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +6 -0
  38. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +110 -62
  39. data/app/pb_kits/playbook/pb_title/_title.scss +1 -1
  40. data/app/pb_kits/playbook/pb_title/_title.tsx +3 -2
  41. data/app/pb_kits/playbook/pb_title/docs/_title_light_weight.html.erb +0 -1
  42. data/app/pb_kits/playbook/pb_title/docs/_title_light_weight.jsx +0 -6
  43. data/app/pb_kits/playbook/pb_title/docs/_title_light_weight.md +2 -1
  44. data/app/pb_kits/playbook/pb_title/title.rb +10 -3
  45. data/app/pb_kits/playbook/pb_title/title.test.js +3 -3
  46. data/app/pb_kits/playbook/playbook-doc.js +2 -0
  47. data/app/pb_kits/playbook/utilities/object.ts +3 -0
  48. data/dist/menu.yml +1 -0
  49. data/dist/playbook-rails.js +7 -7
  50. data/lib/playbook/forms/builder/intl_telephone_field.rb +12 -0
  51. data/lib/playbook/forms/builder.rb +1 -0
  52. data/lib/playbook/version.rb +1 -1
  53. metadata +26 -4
  54. data/app/pb_kits/playbook/pb_multi_level_select/_multi_select_helper.tsx +0 -31
  55. data/app/pb_kits/playbook/pb_multi_level_select/helper_functions.ts +0 -87
@@ -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
@@ -1,74 +1,122 @@
1
- import React from 'react'
2
- import { render, screen } from '../utilities/test-utils'
3
- import PhoneNumberInput from './_phone_number_input'
1
+ import React from "react";
2
+ import { render, screen } from "../utilities/test-utils";
3
+ import PhoneNumberInput from "./_phone_number_input";
4
4
 
5
- const testId = "phoneNumberInput"
5
+ const testId = "phoneNumberInput";
6
6
 
7
- test('should be disabled', () => {
8
- const props = {
9
- disabled: true,
10
- id: testId,
11
- }
7
+ test("should be disabled", () => {
8
+ const props = {
9
+ disabled: true,
10
+ id: testId,
11
+ };
12
12
 
13
- render(<PhoneNumberInput {...props} />)
14
- const kit = screen.getByRole("textbox")
15
- expect(kit).toBeDisabled()
16
- })
13
+ render(<PhoneNumberInput {...props} />);
14
+ const kit = screen.getByRole("textbox");
15
+ expect(kit).toBeDisabled();
16
+ });
17
17
 
18
- test('should be enabled by default', () => {
19
- const props = {
20
- id: testId,
21
- }
18
+ test("should be enabled by default", () => {
19
+ const props = {
20
+ id: testId,
21
+ };
22
22
 
23
- render(<PhoneNumberInput {...props} />)
24
- const kit = screen.getByRole("textbox")
25
- expect(kit).not.toBeDisabled()
26
- })
23
+ render(<PhoneNumberInput {...props} />);
24
+ const kit = screen.getByRole("textbox");
25
+ expect(kit).not.toBeDisabled();
26
+ });
27
27
 
28
- test('should have label', () => {
29
- const label = 'Phone Number'
30
- const props = {
31
- id: testId,
32
- label,
33
- }
28
+ test("should have label", () => {
29
+ const label = "Phone Number";
30
+ const props = {
31
+ id: testId,
32
+ label,
33
+ };
34
34
 
35
- render(<PhoneNumberInput {...props} />)
36
- const kit = screen.getByText(label)
37
- expect(kit).toBeInTheDocument()
38
- })
35
+ render(<PhoneNumberInput {...props} />);
36
+ const kit = screen.getByText(label);
37
+ expect(kit).toBeInTheDocument();
38
+ });
39
39
 
40
- test('should pass data prop', () => {
41
- const props = {
42
- data: { testid: testId },
43
- id: testId,
44
- }
40
+ test("should pass data prop", () => {
41
+ const props = {
42
+ data: { testid: testId },
43
+ id: testId,
44
+ };
45
+
46
+ render(<PhoneNumberInput {...props} />);
47
+ const kit = screen.getByTestId(testId);
48
+ expect(kit).toBeInTheDocument();
49
+ });
50
+
51
+ test("should pass className prop", () => {
52
+ const className = "custom-class-name";
53
+ const props = {
54
+ className,
55
+ data: { testid: testId },
56
+ id: testId,
57
+ };
58
+
59
+ render(<PhoneNumberInput {...props} />);
60
+ const kit = screen.getByTestId(testId);
61
+ expect(kit).toHaveClass(className);
62
+ });
63
+
64
+ test("should pass value prop", () => {
65
+ const value = "1234567890";
66
+ const props = {
67
+ id: testId,
68
+ value,
69
+ };
45
70
 
46
- render(<PhoneNumberInput {...props} />)
47
- const kit = screen.getByTestId(testId)
48
- expect(kit).toBeInTheDocument()
49
- })
71
+ render(<PhoneNumberInput {...props} />);
72
+ const kit = screen.getByRole("textbox");
73
+ expect(kit).toHaveDisplayValue(value);
74
+ });
50
75
 
51
- test('should pass className prop', () => {
52
- const className = 'custom-class-name'
76
+ //TODO: test required field presence
77
+ test("should pass required prop", () => {
78
+ const props = {
79
+ id: testId,
80
+ };
81
+
82
+ render(
83
+ <PhoneNumberInput
84
+ required
85
+ {...props}
86
+ />
87
+ );
88
+ const kit = screen.getByRole("textbox");
89
+ expect(kit).toHaveAttribute("required");
90
+ });
91
+
92
+ test("should have error attribute when invalid", () => {
53
93
  const props = {
54
- className,
55
- data: { testid: testId },
56
- id: testId,
57
- }
58
-
59
- render(<PhoneNumberInput {...props} />)
60
- const kit = screen.getByTestId(testId)
61
- expect(kit).toHaveClass(className)
62
- })
63
-
64
- test('should pass value prop', () => {
65
- const value = '1234567890'
94
+ id: testId,
95
+ error: "Something isn't right here."
96
+ };
97
+
98
+ render(
99
+ <PhoneNumberInput
100
+ {...props}
101
+ />
102
+ );
103
+ const kit = screen.getByRole("textbox");
104
+ expect(kit).toHaveAttribute("error");
105
+ });
106
+
107
+ test("should trigger callback", () => {
108
+ const handleOnValidate = jest.fn((valid) => valid)
109
+
66
110
  const props = {
67
- id: testId,
68
- value,
69
- }
70
-
71
- render(<PhoneNumberInput {...props} />)
72
- const kit = screen.getByRole("textbox")
73
- expect(kit).toHaveDisplayValue(value)
74
- })
111
+ id: testId,
112
+ onValidate: handleOnValidate
113
+ };
114
+
115
+ render(
116
+ <PhoneNumberInput
117
+ {...props}
118
+ />
119
+ );
120
+
121
+ expect(handleOnValidate).toBeCalledWith(true)
122
+ });
@@ -30,7 +30,7 @@
30
30
  @include pb_title_dark;
31
31
  }
32
32
 
33
- &[class*=_thin] {
33
+ &[class*=thin] {
34
34
  @include pb_title_thin;
35
35
  }
36
36
  }
@@ -27,7 +27,7 @@ const Title = (props: TitleProps): React.ReactElement => {
27
27
  data = {},
28
28
  id,
29
29
  size = 3,
30
- bold = true,
30
+ bold = size === 3 ? false : true,
31
31
  tag = 'h3',
32
32
  text,
33
33
  variant = null,
@@ -35,9 +35,10 @@ const Title = (props: TitleProps): React.ReactElement => {
35
35
 
36
36
  const ariaProps: {[key: string]: string | number} = buildAriaProps(aria)
37
37
  const dataProps: {[key: string]: string | number} = buildDataProps(data)
38
+
38
39
  const getBold = bold ? '' : 'thin'
39
40
  const classes = classnames(
40
- buildCss('pb_title_kit', `size_${size}`, variant, color, getBold),
41
+ buildCss("pb_title_kit", `size_${size}`, variant, color) + ` ${getBold}`,
41
42
  globalProps(props),
42
43
  className
43
44
  )
@@ -1,3 +1,2 @@
1
1
  <%= pb_rails("title", props: { text: "Title 1", tag: "h1", size: 1, bold: false }) %>
2
2
  <%= pb_rails("title", props: { text: "Title 2", tag: "h2", size: 2, bold: false }) %>
3
- <%= pb_rails("title", props: { text: "Title 3", tag: "h3", size: 3, bold: false }) %>
@@ -17,12 +17,6 @@ const TitleLightWeight = (props) => {
17
17
  text='Title 2'
18
18
  {...props}
19
19
  />
20
- <Title bold={false}
21
- size={3}
22
- tag='h2'
23
- text='Title 3'
24
- {...props}
25
- />
26
20
  </div>
27
21
  )
28
22
  }
@@ -1,3 +1,4 @@
1
1
  ##### Prop
2
- Title `size 1`, `size 2`, & `size 3` will use `font-weight: 700` by default, if you want a lighter font weight, use the `bold` prop set to `false`.
2
+ Title `size 1` & `size 2` will use `font-weight: 700` by default, if you want a lighter font weight, use the `bold` prop set to `false`.
3
+ Title `size 3` uses a light font weight by default and will not accept a bold font weight.
3
4
  Title `size 4` uses a heavy font weight by default and will not accept a lighter font weight.
@@ -17,14 +17,21 @@ module Playbook
17
17
  values: [nil, "link"],
18
18
  default: nil,
19
19
  deprecated: true
20
- prop :bold, type: Playbook::Props::Boolean, default: true
20
+
21
+ def initialize(props)
22
+ props[:bold] = [1, 2, 4].include?(props[:size]) unless props.key?(:bold)
23
+ props[:bold] = false if props[:size].nil? && !props.key?(:bold)
24
+ super(props)
25
+ end
26
+
27
+ prop :bold, type: Playbook::Props::Boolean
21
28
 
22
29
  def classname
23
- generate_classname("pb_title_kit", size, variant, color, is_bold)
30
+ generate_classname("pb_title_kit", size, variant, color) + is_bold
24
31
  end
25
32
 
26
33
  def is_bold
27
- bold ? nil : "thin"
34
+ bold ? "" : " thin"
28
35
  end
29
36
  end
30
37
  end