playbook_ui 9.8.0 → 9.9.0.alpha.inline1

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/pb_badge/_badge.jsx +26 -1
  3. data/app/pb_kits/playbook/pb_date_picker/_date_picker.jsx +6 -1
  4. data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.js +3 -0
  5. data/app/pb_kits/playbook/pb_form_pill/_form_pill.jsx +12 -2
  6. data/app/pb_kits/playbook/pb_form_pill/_form_pill.scss +19 -0
  7. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_size.html.erb +13 -0
  8. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_size.jsx +25 -0
  9. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_user.html.erb +4 -5
  10. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_user.jsx +2 -6
  11. data/app/pb_kits/playbook/pb_form_pill/docs/example.yml +2 -0
  12. data/app/pb_kits/playbook/pb_form_pill/docs/index.js +1 -0
  13. data/app/pb_kits/playbook/pb_form_pill/form_pill.html.erb +1 -1
  14. data/app/pb_kits/playbook/pb_form_pill/form_pill.rb +5 -0
  15. data/app/pb_kits/playbook/pb_passphrase/_passphrase.jsx +12 -9
  16. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_breached.html.erb +1 -0
  17. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_breached.jsx +24 -0
  18. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_breached.md +3 -0
  19. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_default.jsx +1 -0
  20. data/app/pb_kits/playbook/pb_passphrase/docs/example.yml +2 -0
  21. data/app/pb_kits/playbook/pb_passphrase/docs/index.js +1 -0
  22. data/app/pb_kits/playbook/pb_passphrase/passphrase.rb +2 -0
  23. data/app/pb_kits/playbook/pb_passphrase/passphrase.test.jsx +12 -0
  24. data/app/pb_kits/playbook/pb_passphrase/useHaveIBeenPwned.js +52 -0
  25. data/app/pb_kits/playbook/pb_passphrase/useZxcvbn.js +58 -0
  26. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.jsx +10 -0
  27. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.scss +61 -0
  28. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_inline.html.erb +6 -0
  29. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_inline.jsx +16 -0
  30. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_toolbar_bottom.html.erb +4 -0
  31. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_toolbar_bottom.jsx +14 -0
  32. data/app/pb_kits/playbook/pb_rich_text_editor/docs/example.yml +4 -0
  33. data/app/pb_kits/playbook/pb_rich_text_editor/docs/index.js +2 -0
  34. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.rb +9 -2
  35. data/app/pb_kits/playbook/pb_text_input/_text_input.jsx +3 -0
  36. data/app/pb_kits/playbook/pb_text_input/_text_input.scss +8 -0
  37. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_inline.html.erb +5 -0
  38. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_inline.jsx +22 -0
  39. data/app/pb_kits/playbook/pb_text_input/docs/example.yml +2 -0
  40. data/app/pb_kits/playbook/pb_text_input/docs/index.js +1 -0
  41. data/app/pb_kits/playbook/pb_text_input/text_input.rb +7 -1
  42. data/app/pb_kits/playbook/pb_typeahead/_typeahead.jsx +11 -2
  43. data/app/pb_kits/playbook/pb_typeahead/_typeahead.scss +23 -0
  44. data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.jsx +23 -11
  45. data/app/pb_kits/playbook/pb_typeahead/components/Placeholder.jsx +17 -4
  46. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_inline.html.erb +36 -0
  47. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_inline.jsx +43 -0
  48. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_multi_kit.html.erb +35 -0
  49. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_multi_kit.jsx +44 -0
  50. data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +4 -0
  51. data/app/pb_kits/playbook/pb_typeahead/docs/index.js +7 -5
  52. data/app/pb_kits/playbook/pb_typeahead/typeahead.html.erb +1 -1
  53. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +18 -2
  54. data/lib/playbook/version.rb +1 -1
  55. metadata +22 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bfe0ad928a2d058675961581c2e7579a7b2b686b7444c461b907d9976e46983b
4
- data.tar.gz: 7dd7e0d985acdff72829e21de5b09b94f823006cd26b588831096334ea645690
3
+ metadata.gz: aad061e395898f02d246cad01fc3ca3c158c910c058c546fdff62258f99ceced
4
+ data.tar.gz: fb468e1242133f77abb769f74805a44c6a3caa88fb40dd10cf48791b98683466
5
5
  SHA512:
6
- metadata.gz: c8682707eb5b82740167457664a027d16b1635181692900c4c62f0aba012ee6803a13c4fdcfe55296682cf7979688cf34ea36d15a5b6fb2085faa40c2f7e58a8
7
- data.tar.gz: c879311f4ffaa99290028fdf45579a595920fd8c9d953823c451d2891e9bac9b1302bc95d9f31dfb9a389f48657f36502f00b8a85eb15b3e29ce4775bca325f3
6
+ metadata.gz: e23d91eb54be1797a2cfb44c35941b3c0302d7bb02cb880ee61a21710a540014a0c4a0bc9b82c3b09d47d38d0b030a702f33a2d3b54fdf0ceec805d674958545
7
+ data.tar.gz: e04139cbbbf7a5e9b93e47ec464583715fae88a10463047c08da99ffae60986832117ae533c880de82cf54989bfe2914191dc1d87cf37763f8426c2bca571662
@@ -3,6 +3,7 @@
3
3
  import React from 'react'
4
4
  import classnames from 'classnames'
5
5
  import { globalProps } from '../utilities/globalProps.js'
6
+ import { Icon } from '../'
6
7
 
7
8
  import {
8
9
  buildAriaProps,
@@ -13,8 +14,15 @@ import {
13
14
  type BadgeProps = {
14
15
  aria?: object,
15
16
  className?: string,
17
+ closeProps?: {
18
+ onClick?: EventHandler,
19
+ onMouseDown?: EventHandler,
20
+ onTouchEnd?: EventHandler,
21
+ },
16
22
  data?: object,
17
23
  id?: string,
24
+ removeIcon?: Boolean,
25
+ removeOnClick?: EventHandler,
18
26
  rounded?: boolean,
19
27
  text?: string,
20
28
  variant?: "error" | "info" | "neutral" | "primary" | "success" | "warning",
@@ -23,8 +31,11 @@ const Badge = (props: BadgeProps) => {
23
31
  const {
24
32
  aria = {},
25
33
  className,
34
+ closeProps = {},
26
35
  data = {},
27
36
  id,
37
+ removeIcon = false,
38
+ removeOnClick = () => {},
28
39
  rounded = false,
29
40
  text,
30
41
  variant = 'neutral',
@@ -44,7 +55,21 @@ const Badge = (props: BadgeProps) => {
44
55
  className={css}
45
56
  id={id}
46
57
  >
47
- <span>{text}</span>
58
+ <span>
59
+ {text}
60
+ <If condition={removeIcon}>
61
+ <span
62
+ onClick={removeOnClick}
63
+ style={{ cursor: 'pointer' }}
64
+ {...closeProps}
65
+ >
66
+ <Icon
67
+ fixedWidth
68
+ icon="times"
69
+ />
70
+ </span>
71
+ </If>
72
+ </span>
48
73
  </div>
49
74
  )
50
75
  }
@@ -25,6 +25,8 @@ type DatePickerProps = {
25
25
  id?: String,
26
26
  inputAria?: object,
27
27
  inputData?: object,
28
+ inputOnChange?: (String) => void,
29
+ inputValue?: any,
28
30
  label?: String,
29
31
  maxDate: String,
30
32
  minDate: String,
@@ -55,6 +57,8 @@ const DatePicker = (props: DatePickerProps) => {
55
57
  id,
56
58
  inputAria,
57
59
  inputData,
60
+ inputOnChange,
61
+ inputValue,
58
62
  label = 'Date Picker',
59
63
  maxDate,
60
64
  minDate,
@@ -114,7 +118,6 @@ const DatePicker = (props: DatePickerProps) => {
114
118
  className={classes}
115
119
  id={id}
116
120
  >
117
- {className}
118
121
  <div className="input_wrapper">
119
122
  <TextInput
120
123
  aria={inputAria}
@@ -126,7 +129,9 @@ const DatePicker = (props: DatePickerProps) => {
126
129
  id={pickerId}
127
130
  label={hideLabel ? null : label}
128
131
  name={name}
132
+ onChange={inputOnChange}
129
133
  placeholder={placeholder}
134
+ value={inputValue}
130
135
  />
131
136
  <If condition={!hideIcon}>
132
137
  <div
@@ -166,6 +166,9 @@ const datePickerHelper = (config) => {
166
166
  picker.input.style.caretColor = 'transparent'
167
167
  picker.input.style.cursor = 'pointer'
168
168
  }
169
+
170
+ // Fix event bubbling bug on wrapper
171
+ document.querySelector(`#${pickerId}`).parentElement.addEventListener('click', (e) => e.stopPropagation())
169
172
  }
170
173
 
171
174
  export default datePickerHelper
@@ -14,6 +14,7 @@ type FormPillProps = {
14
14
  onClick?: EventHandler,
15
15
  avatar?: boolean,
16
16
  avatarUrl?: string,
17
+ size?: string,
17
18
  closeProps?: {
18
19
  onClick?: EventHandler,
19
20
  onMouseDown?: EventHandler,
@@ -21,11 +22,20 @@ type FormPillProps = {
21
22
  },
22
23
  }
23
24
  const FormPill = (props: FormPillProps) => {
24
- const { className, text, name, onClick = () => {}, avatarUrl, closeProps = {} } = props
25
+ const {
26
+ className,
27
+ text,
28
+ name,
29
+ onClick = () => {},
30
+ avatarUrl,
31
+ closeProps = {},
32
+ size = '',
33
+ } = props
25
34
  const css = classnames(
26
35
  `pb_form_pill_kit_${'primary'}`,
27
36
  globalProps(props),
28
- className
37
+ className,
38
+ size === 'small' ? 'small' : null,
29
39
  )
30
40
  return (
31
41
  <div className={css}>
@@ -51,4 +51,23 @@ $form_pill_colors: (
51
51
  }
52
52
  }
53
53
  }
54
+ &.small {
55
+ height: fit-content;
56
+ height: -moz-fit-content;
57
+ .pb_form_pill_text, .pb_form_pill_close, .pb_form_pill_tag {
58
+ font-size: 16px;
59
+ font-weight: 400;
60
+ }
61
+ .pb_form_pill_text, .pb_form_pill_tag {
62
+ line-height: 1.7;
63
+ padding-left: $space_xs;
64
+ padding-right: 2px;
65
+ }
66
+ [class^=pb_avatar_kit], [class^=pb_avatar_kit] .avatar_wrapper {
67
+ width: 20px;
68
+ height: 20px;
69
+ flex-basis: 20px;
70
+ &::before { line-height: 21px; }
71
+ }
72
+ }
54
73
  }
@@ -0,0 +1,13 @@
1
+ <%= pb_rails("form_pill", props: {
2
+ name: "Anna Black",
3
+ avatar_url: "https://randomuser.me/api/portraits/women/44.jpg",
4
+ size: "small",
5
+ }) %>
6
+
7
+ <br />
8
+ <br />
9
+
10
+ <%= pb_rails("form_pill", props: {
11
+ name: "Anna Black",
12
+ size: "small",
13
+ }) %>
@@ -0,0 +1,25 @@
1
+ import React from 'react'
2
+ import FormPill from '../_form_pill.jsx'
3
+
4
+ const FormPillSize = (props) => {
5
+ return (
6
+
7
+ <div>
8
+ <FormPill
9
+ avatarUrl="https://randomuser.me/api/portraits/women/44.jpg"
10
+ name="Anna Black"
11
+ size="small"
12
+ {...props}
13
+ />
14
+ <br />
15
+ <br />
16
+ <FormPill
17
+ name="Anna Black"
18
+ size="small"
19
+ {...props}
20
+ />
21
+ </div>
22
+ )
23
+ }
24
+
25
+ export default FormPillSize
@@ -1,12 +1,11 @@
1
1
  <%= pb_rails("form_pill", props: {
2
2
  name: "Anna Black",
3
3
  avatar_url: "https://randomuser.me/api/portraits/women/44.jpg",
4
+ }) %>
4
5
 
5
- }) %>
6
-
7
- <br>
8
- <br>
6
+ <br />
7
+ <br />
9
8
 
10
9
  <%= pb_rails("form_pill", props: {
11
10
  name: "Anna Black",
12
- }) %>
11
+ }) %>
@@ -8,18 +8,14 @@ const FormPillDefault = (props) => {
8
8
  <FormPill
9
9
  avatarUrl="https://randomuser.me/api/portraits/women/44.jpg"
10
10
  name="Anna Black"
11
- onClick={() => {
12
- alert('Click!')
13
- }}
11
+ onClick={() => alert('Click!')}
14
12
  {...props}
15
13
  />
16
14
  <br />
17
15
  <br />
18
16
  <FormPill
19
17
  name="Anna Black"
20
- onClick={() => {
21
- alert('Click!')
22
- }}
18
+ onClick={() => alert('Click!')}
23
19
  {...props}
24
20
  />
25
21
  </div>
@@ -2,9 +2,11 @@ examples:
2
2
 
3
3
  rails:
4
4
  - form_pill_user: Form Pill User
5
+ - form_pill_size: Form Pill Size
5
6
  - form_pill_tag: Form Pill Tag
6
7
 
7
8
 
8
9
  react:
9
10
  - form_pill_user: Form Pill User
11
+ - form_pill_size: Form Pill Size
10
12
  - form_pill_tag: Form Pill Tag
@@ -1,2 +1,3 @@
1
1
  export { default as FormPillUser } from './_form_pill_user.jsx'
2
+ export { default as FormPillSize } from './_form_pill_size.jsx'
2
3
  export { default as FormPillTag } from './_form_pill_tag.jsx'
@@ -1,7 +1,7 @@
1
1
  <%= content_tag(:div,
2
2
  id: object.id,
3
3
  data: object.data,
4
- class: object.classname) do %>
4
+ class: object.classname + object.size_class) do %>
5
5
  <% if object.name.present? %>
6
6
  <%= pb_rails("avatar", props: {
7
7
  name: object.name,
@@ -6,6 +6,7 @@ module Playbook
6
6
  prop :avatar_url
7
7
  prop :name
8
8
  prop :text
9
+ prop :size
9
10
 
10
11
  def classname
11
12
  generate_classname("pb_form_pill_kit", "primary", name, text)
@@ -14,6 +15,10 @@ module Playbook
14
15
  def display_text
15
16
  name.downcase
16
17
  end
18
+
19
+ def size_class
20
+ size == "small" ? " small" : ""
21
+ end
17
22
  end
18
23
  end
19
24
  end
@@ -5,12 +5,14 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'
5
5
  import classnames from 'classnames'
6
6
  import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
7
7
  import { globalProps } from '../utilities/globalProps.js'
8
- import { zxcvbnPasswordScore } from './passwordStrength.js'
8
+ import useZxcvbn from './useZxcvbn'
9
+ import useHaveIBeenPwned from './useHaveIBeenPwned'
9
10
  import { Body, Caption, Flex, Icon, PbReactPopover, ProgressSimple, TextInput } from '../'
10
11
 
11
12
  type PassphraseProps = {
12
13
  aria?: object,
13
14
  averageThreshold?: number,
15
+ checkPwned?: boolean,
14
16
  common?: boolean,
15
17
  confirmation?: boolean,
16
18
  className?: string,
@@ -33,6 +35,7 @@ const Passphrase = (props: PassphraseProps) => {
33
35
  const {
34
36
  aria = {},
35
37
  averageThreshold = 2,
38
+ checkPwned = false,
36
39
  className,
37
40
  common = false,
38
41
  confirmation = false,
@@ -41,7 +44,7 @@ const Passphrase = (props: PassphraseProps) => {
41
44
  id,
42
45
  inputProps = {},
43
46
  label = confirmation ? 'Confirm Passphrase' : 'Passphrase',
44
- minLength,
47
+ minLength = 12,
45
48
  onChange = () => {},
46
49
  showTipsBelow = 'always',
47
50
  onStrengthChange,
@@ -50,6 +53,7 @@ const Passphrase = (props: PassphraseProps) => {
50
53
  uncontrolled = false,
51
54
  value = '',
52
55
  } = props
56
+ const ariaProps = buildAriaProps(aria)
53
57
 
54
58
  const [uncontrolledValue, setUncontrolledValue] = useState('')
55
59
 
@@ -68,16 +72,11 @@ const Passphrase = (props: PassphraseProps) => {
68
72
  const [showPassphrase, setShowPassphrase] = useState(false)
69
73
  const toggleShowPassphrase = () => setShowPassphrase(!showPassphrase)
70
74
 
71
- const ariaProps = buildAriaProps(aria)
72
- const dataProps = buildDataProps(data)
73
75
  const classes = classnames(buildCss('pb_passphrase'), globalProps(props), className)
74
76
 
75
- const calculator = useMemo(
76
- () => confirmation ? { test: () => ({}) } : zxcvbnPasswordScore({ averageThreshold, strongThreshold, minLength }),
77
- [averageThreshold, confirmation, strongThreshold, minLength]
78
- )
77
+ const isPwned = checkPwned ? useHaveIBeenPwned(displayValue, minLength) : false
79
78
 
80
- const { percent: progressPercent, variant: progressVariant, text: strengthLabel, strength } = calculator.test(displayValue, common)
79
+ const { percent: progressPercent, variant: progressVariant, text: strengthLabel, strength } = useZxcvbn({ passphrase: displayValue, common, isPwned, confirmation, averageThreshold, minLength, strongThreshold })
81
80
 
82
81
  useEffect(() => {
83
82
  if (typeof onStrengthChange === 'function') {
@@ -89,6 +88,10 @@ const Passphrase = (props: PassphraseProps) => {
89
88
  (dark ? 'dark' : null),
90
89
  (showTipsBelow === 'always' ? null : `show-below-${showTipsBelow}`),
91
90
  )
91
+ const dataProps = useMemo(
92
+ () => (buildDataProps(Object.assign({}, data, { strength }))),
93
+ [data, strength]
94
+ )
92
95
 
93
96
  const popoverReference = (
94
97
  <a
@@ -0,0 +1 @@
1
+ <%= pb_rails("passphrase", props: { check_pwned: true }) %>
@@ -0,0 +1,24 @@
1
+ import React, { useState } from 'react'
2
+ import { Passphrase } from '../../'
3
+
4
+ const PassphraseBreached = (props) => {
5
+ const [input, setInput] = useState('')
6
+
7
+ const handleChange = (e) => setInput(e.target.value)
8
+
9
+ return (
10
+ <>
11
+ <div>
12
+ <br />
13
+ <Passphrase
14
+ checkPwned
15
+ onChange={handleChange}
16
+ value={input}
17
+ {...props}
18
+ />
19
+ </div>
20
+ </>
21
+ )
22
+ }
23
+
24
+ export default PassphraseBreached
@@ -0,0 +1,3 @@
1
+ Use `checkPwned | checked_pwned` prop to enable checking against <a href='https://haveibeenpwned.com/Passwords'>HaveIBeenPwned's</a> API. As the passphrase is typed, it is checked against more than half a billion breached passwords, to help ensure its not compromised.
2
+ Should it fail, the feedback will express the passphrase is too common, prompting the user to change.
3
+ This uses their k-Anonymity model, so only the first 5 characters of a hashed copy of the passphrase are sent.
@@ -12,6 +12,7 @@ const PassphraseDefault = (props) => {
12
12
  <>
13
13
  <div>
14
14
  <Passphrase
15
+ id="my-passphrase"
15
16
  onChange={handleChange}
16
17
  value={input}
17
18
  {...props}
@@ -5,6 +5,7 @@ examples:
5
5
  - passphrase_meter_settings: Meter Settings
6
6
  - passphrase_input_props: Input Props
7
7
  - passphrase_tips: Tips
8
+ - passphrase_breached: Breached Passphrases
8
9
 
9
10
  react:
10
11
  - passphrase_default: Default
@@ -13,3 +14,4 @@ examples:
13
14
  - passphrase_tips: Tips
14
15
  - passphrase_strength_change: Strength Change
15
16
  - passphrase_common: Common Passphrases
17
+ - passphrase_breached: Breached Passphrases
@@ -4,3 +4,4 @@ export { default as PassphraseInputProps } from './_passphrase_input_props'
4
4
  export { default as PassphraseTips } from './_passphrase_tips'
5
5
  export { default as PassphraseStrengthChange } from './_passphrase_strength_change'
6
6
  export { default as PassphraseCommon } from './_passphrase_common'
7
+ export { default as PassphraseBreached } from './_passphrase_breached'
@@ -4,6 +4,7 @@ module Playbook
4
4
  module PbPassphrase
5
5
  class Passphrase < Playbook::KitBase
6
6
  prop :average_threshold
7
+ prop :check_pwned
7
8
  prop :confirmation, type: Playbook::Props::Boolean, default: false
8
9
  prop :input_props, type: Playbook::Props::Hash, default: {}
9
10
  prop :label
@@ -18,6 +19,7 @@ module Playbook
18
19
 
19
20
  def passphrase_options
20
21
  {
22
+ checkPwned: check_pwned,
21
23
  dark: dark,
22
24
  id: id,
23
25
  averageThreshold: average_threshold,