playbook_ui 9.6.1 → 9.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/pb_kits/playbook/pb_date/_date.jsx +3 -3
- data/app/pb_kits/playbook/pb_date/date.html.erb +2 -3
- data/app/pb_kits/playbook/pb_date/docs/_date_variants.html.erb +8 -0
- data/app/pb_kits/playbook/pb_date/docs/_date_variants.jsx +10 -0
- data/app/pb_kits/playbook/pb_dialog/dialog_header.rb +23 -24
- data/app/pb_kits/playbook/pb_fixed_confirmation_toast/_fixed_confirmation_toast.jsx +4 -2
- data/app/pb_kits/playbook/pb_fixed_confirmation_toast/_fixed_confirmation_toast.scss +7 -0
- data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_multi_line.html.erb +2 -1
- data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_multi_line.jsx +2 -1
- data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_multi_line.md +1 -1
- data/app/pb_kits/playbook/pb_fixed_confirmation_toast/fixed_confirmation_toast.rb +7 -1
- data/app/pb_kits/playbook/pb_flex/_flex_item.jsx +1 -1
- data/app/pb_kits/playbook/pb_form_group/_form_group.jsx +3 -1
- data/app/pb_kits/playbook/pb_form_group/_form_group.scss +8 -0
- data/app/pb_kits/playbook/pb_form_group/docs/_form_group_full_width.html.erb +13 -0
- data/app/pb_kits/playbook/pb_form_group/docs/_form_group_full_width.jsx +43 -0
- data/app/pb_kits/playbook/pb_form_group/docs/_form_group_full_width.md +1 -0
- data/app/pb_kits/playbook/pb_form_group/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_form_group/docs/index.js +1 -1
- data/app/pb_kits/playbook/pb_form_group/form_group.rb +10 -1
- data/app/pb_kits/playbook/pb_nav/_vertical_nav.scss +1 -1
- data/app/pb_kits/playbook/pb_nav/docs/_block_nav.html.erb +41 -5
- data/app/pb_kits/playbook/pb_nav/docs/_block_nav.jsx +44 -6
- data/app/pb_kits/playbook/pb_passphrase/_passphrase.jsx +12 -9
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_breached.html.erb +1 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_breached.jsx +24 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_breached.md +3 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_default.jsx +1 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_passphrase/passphrase.rb +2 -0
- data/app/pb_kits/playbook/pb_passphrase/passphrase.test.jsx +12 -0
- data/app/pb_kits/playbook/pb_passphrase/useHaveIBeenPwned.js +52 -0
- data/app/pb_kits/playbook/pb_passphrase/useZxcvbn.js +58 -0
- data/app/pb_kits/playbook/pb_select/_select.jsx +10 -1
- data/app/pb_kits/playbook/pb_select/_select.scss +27 -30
- data/app/pb_kits/playbook/pb_select/select.rb +5 -1
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_default.html.erb +1 -1
- data/app/pb_kits/playbook/pb_typeahead/typeahead.html.erb +5 -1
- data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +5 -13
- data/lib/playbook/version.rb +1 -1
- metadata +20 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc6cf85248c6d0e0b9c74d63c9d1dffa2a99d8e5b8d1010a8b4622829fa77d66
|
4
|
+
data.tar.gz: dae909122997e3eea81a47c6b5b9260bb84d87316c82cc70b351bec7a75d4ad7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 596e69c28e26d8ddcafc2d1006e8f72f56458a9a8eacc4811a918e0124ddffed05c75e61a8834630d041d742e1dd66d8cd97870b5b1d4ef74e7ce2b9b8f30d09
|
7
|
+
data.tar.gz: 9a71dd4b52c1da10cd03a288accf3c76e18dd8bc71aebe3e2818c7f27763cd65e0b069d9103c95c5d8cad56ec96411340419a3f729b139758c172d8628e6c8a9
|
@@ -95,16 +95,16 @@ const PbDate = (props: PbDateProps) => {
|
|
95
95
|
<Else />
|
96
96
|
<>
|
97
97
|
<If condition={showIcon}>
|
98
|
-
<
|
98
|
+
<Caption
|
99
99
|
className="pb_icon_kit_container"
|
100
|
-
color="light"
|
101
100
|
tag="span"
|
102
101
|
>
|
103
102
|
<Icon
|
104
103
|
fixedWidth
|
105
104
|
icon="calendar-alt"
|
105
|
+
size="xs"
|
106
106
|
/>
|
107
|
-
</
|
107
|
+
</Caption>
|
108
108
|
</If>
|
109
109
|
<If condition={showDayOfWeek}>
|
110
110
|
<Caption tag="div">
|
@@ -39,11 +39,10 @@
|
|
39
39
|
|
40
40
|
<!-- icon -->
|
41
41
|
<% if object.show_icon %>
|
42
|
-
<%= pb_rails("
|
43
|
-
color: "light",
|
42
|
+
<%= pb_rails("caption", props: {
|
44
43
|
tag: "div",
|
45
44
|
}) do %>
|
46
|
-
<%= pb_rails("icon", props: { icon: "calendar-alt", fixed_width: true }) %>
|
45
|
+
<%= pb_rails("icon", props: { icon: "calendar-alt", fixed_width: true, size: 'xs' }) %>
|
47
46
|
<% end %>
|
48
47
|
<% end %>
|
49
48
|
|
@@ -4,6 +4,16 @@ import { Date as FormattedDate } from '../..'
|
|
4
4
|
const DateVariants = (props) => {
|
5
5
|
return (
|
6
6
|
<div>
|
7
|
+
<FormattedDate
|
8
|
+
showIcon
|
9
|
+
size="sm"
|
10
|
+
value="1995-12-25"
|
11
|
+
{...props}
|
12
|
+
/>
|
13
|
+
|
14
|
+
<br />
|
15
|
+
<br />
|
16
|
+
|
7
17
|
<FormattedDate
|
8
18
|
value="1995-12-25"
|
9
19
|
{...props}
|
@@ -1,31 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Playbook
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
partial "pb_dialog/child_kits/dialog_header"
|
4
|
+
module PbDialog
|
5
|
+
class DialogHeader
|
6
|
+
include Playbook::Props
|
9
7
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
8
|
+
partial "pb_dialog/child_kits/dialog_header"
|
9
|
+
|
10
|
+
prop :closeable, type: Playbook::Props::Boolean, default: true
|
11
|
+
prop :padding
|
12
|
+
prop :separator, type: Playbook::Props::Boolean, default: true
|
13
|
+
prop :spacing
|
14
|
+
prop :text
|
15
|
+
prop :title
|
16
|
+
|
17
|
+
def dialog_header_options
|
18
|
+
{
|
19
|
+
id: id,
|
20
|
+
closeable: closeable,
|
21
|
+
padding: padding,
|
22
|
+
separator: separator,
|
23
|
+
spacing: spacing,
|
24
|
+
text: text,
|
25
|
+
title: title,
|
26
|
+
}
|
28
27
|
end
|
29
28
|
end
|
30
29
|
end
|
31
|
-
|
30
|
+
end
|
@@ -17,15 +17,17 @@ type FixedConfirmationToastProps = {
|
|
17
17
|
closeable?: boolean,
|
18
18
|
data?: string,
|
19
19
|
id?: string,
|
20
|
-
|
20
|
+
multiLine?: boolean,
|
21
|
+
status?: 'success' | 'error' | 'neutral' | 'tip',
|
21
22
|
text: string,
|
22
23
|
}
|
23
24
|
|
24
25
|
const FixedConfirmationToast = (props: FixedConfirmationToastProps) => {
|
25
26
|
const [showToast, toggleToast] = useState(true)
|
26
|
-
const { className, closeable = false, status = 'neutral', text } = props
|
27
|
+
const { className, closeable = false, multiLine = false, status = 'neutral', text } = props
|
27
28
|
const css = classnames(
|
28
29
|
`pb_fixed_confirmation_toast_kit_${status}`,
|
30
|
+
{ '_multi_line': multiLine },
|
29
31
|
globalProps(props),
|
30
32
|
className
|
31
33
|
)
|
@@ -47,6 +47,13 @@ $confirmation_toast_colors: (
|
|
47
47
|
.pb_icon {
|
48
48
|
color: $white;
|
49
49
|
}
|
50
|
+
|
51
|
+
&[class*=_multi_line] .pb_fixed_confirmation_toast_text {
|
52
|
+
color: $white;
|
53
|
+
margin: 0 $space_md 0 $space_md;
|
54
|
+
max-width: 100%;
|
55
|
+
white-space: break-spaces;
|
56
|
+
}
|
50
57
|
}
|
51
58
|
}
|
52
59
|
}
|
data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_multi_line.jsx
CHANGED
@@ -5,8 +5,9 @@ const FixedConfirmationToastMultiLine = (props) => {
|
|
5
5
|
return (
|
6
6
|
<div>
|
7
7
|
<FixedConfirmationToast
|
8
|
+
multiLine
|
8
9
|
status="tip"
|
9
|
-
text=
|
10
|
+
text="Scan to Assign Selected Items. Click here to generate report"
|
10
11
|
{...props}
|
11
12
|
/>
|
12
13
|
</div>
|
data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_multi_line.md
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
|
2
|
-
Multi-line is used when the given text will not fit on one line. Using Multi Line allows the height of the confirmation toast to grow.
|
2
|
+
Multi-line is used when the given text will not fit on one line. Using Multi Line allows the height of the confirmation toast to grow. Simply resize the screen to see the fixed confirmation toast wrap the text.
|
@@ -7,6 +7,8 @@ module Playbook
|
|
7
7
|
values: %w[success error neutral tip],
|
8
8
|
default: "neutral"
|
9
9
|
prop :text, type: Playbook::Props::String
|
10
|
+
prop :multi_line, type: Playbook::Props::Boolean,
|
11
|
+
default: false
|
10
12
|
prop :closeable, type: Playbook::Props::Boolean,
|
11
13
|
default: false
|
12
14
|
|
@@ -18,6 +20,10 @@ module Playbook
|
|
18
20
|
closeable.present? ? " remove_toast" : ""
|
19
21
|
end
|
20
22
|
|
23
|
+
def multi_line_class
|
24
|
+
multi_line.present? ? "multi_line" : nil
|
25
|
+
end
|
26
|
+
|
21
27
|
def icon_value
|
22
28
|
case status
|
23
29
|
when "success"
|
@@ -32,7 +38,7 @@ module Playbook
|
|
32
38
|
end
|
33
39
|
|
34
40
|
def classname
|
35
|
-
generate_classname("pb_fixed_confirmation_toast_kit", status) + close_class
|
41
|
+
generate_classname("pb_fixed_confirmation_toast_kit", status, multi_line_class) + close_class
|
36
42
|
end
|
37
43
|
end
|
38
44
|
end
|
@@ -14,7 +14,7 @@ type FlexItemPropTypes = {
|
|
14
14
|
}
|
15
15
|
|
16
16
|
const FlexItem = (props: FlexItemPropTypes) => {
|
17
|
-
const { children, className, fixedSize, grow, overflow = null, shrink, flex } = props
|
17
|
+
const { children, className, fixedSize, grow, overflow = null, shrink, flex = 'none' } = props
|
18
18
|
const growClass = grow === true ? 'grow' : ''
|
19
19
|
const flexClass = flex !== 'none' ? `flex_${flex}` : ''
|
20
20
|
const overflowClass = overflow ? `overflow_${overflow}` : ''
|
@@ -10,6 +10,7 @@ type FormGroupProps = {
|
|
10
10
|
children?: Node,
|
11
11
|
className?: string,
|
12
12
|
data?: object,
|
13
|
+
fullWidth?: boolean,
|
13
14
|
id?: string,
|
14
15
|
}
|
15
16
|
|
@@ -18,13 +19,14 @@ const FormGroup = (props: FormGroupProps) => {
|
|
18
19
|
aria = {},
|
19
20
|
className,
|
20
21
|
data = {},
|
22
|
+
fullWidth = false,
|
21
23
|
id,
|
22
24
|
children,
|
23
25
|
} = props
|
24
26
|
|
25
27
|
const ariaProps = buildAriaProps(aria)
|
26
28
|
const dataProps = buildDataProps(data)
|
27
|
-
const classes = classnames(buildCss('pb_form_group_kit'), globalProps(props), className)
|
29
|
+
const classes = classnames(buildCss('pb_form_group_kit', { full: fullWidth }), globalProps(props), className)
|
28
30
|
|
29
31
|
return (
|
30
32
|
<div
|
@@ -4,6 +4,14 @@
|
|
4
4
|
align-items: flex-end;
|
5
5
|
justify-content: flex-start;
|
6
6
|
|
7
|
+
&[class*=_full] {
|
8
|
+
display: flex;
|
9
|
+
justify-content: space-between;
|
10
|
+
& > div {
|
11
|
+
width: 100%;
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
7
15
|
& [class^=pb_text_input_kit] .text_input_wrapper,
|
8
16
|
& [class^=pb_date_picker_kit] .input_wrapper,
|
9
17
|
& [class^=pb_select] {
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<div>
|
2
|
+
<%= pb_rails("form_group", props: { full_width: true }) do %>
|
3
|
+
<%= pb_rails("text_input", props: { label: "First Name", placeholder: "Enter First Name" }) %>
|
4
|
+
<%= pb_rails("text_input", props: { label: "Middle Intial", placeholder: "Enter Middle Initial" }) %>
|
5
|
+
<%= pb_rails("text_input", props: { label: "Last Name", placeholder: "Enter Last Name" }) %>
|
6
|
+
<% end %>
|
7
|
+
<br/>
|
8
|
+
<br/>
|
9
|
+
<%= pb_rails("form_group", props: { full_width: true }) do %>
|
10
|
+
<%= pb_rails("text_input", props: { placeholder: "Search" }) %>
|
11
|
+
<%= pb_rails("button", props: { text: "Submit", variant: "secondary" }) %>
|
12
|
+
<% end %>
|
13
|
+
</div>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import React from 'react'
|
2
|
+
import { Button, FormGroup, TextInput } from '../../'
|
3
|
+
|
4
|
+
const FormGroupFullWidth = (props) => (
|
5
|
+
<div>
|
6
|
+
<div>
|
7
|
+
<FormGroup fullWidth>
|
8
|
+
<TextInput
|
9
|
+
label="First Name"
|
10
|
+
placeholder="Enter First Name"
|
11
|
+
{...props}
|
12
|
+
/>
|
13
|
+
<TextInput
|
14
|
+
label="Middle Intial"
|
15
|
+
placeholder="Enter Middle Initial"
|
16
|
+
{...props}
|
17
|
+
/>
|
18
|
+
<TextInput
|
19
|
+
label="Last Name"
|
20
|
+
placeholder="Enter Last Name"
|
21
|
+
{...props}
|
22
|
+
/>
|
23
|
+
</FormGroup>
|
24
|
+
</div>
|
25
|
+
<br />
|
26
|
+
<div>
|
27
|
+
<FormGroup fullWidth>
|
28
|
+
<TextInput
|
29
|
+
placeholder="Search"
|
30
|
+
{...props}
|
31
|
+
/>
|
32
|
+
<Button
|
33
|
+
onClick={() => alert('Button Clicked!')}
|
34
|
+
text="Submit"
|
35
|
+
variant="secondary"
|
36
|
+
{...props}
|
37
|
+
/>
|
38
|
+
</FormGroup>
|
39
|
+
</div>
|
40
|
+
</div>
|
41
|
+
)
|
42
|
+
|
43
|
+
export default FormGroupFullWidth
|
@@ -0,0 +1 @@
|
|
1
|
+
Full Width is a prop that can be added to any of the Form Group options. This prop allows the Form Group to stretch the full width of the div.
|
@@ -3,6 +3,7 @@ examples:
|
|
3
3
|
rails:
|
4
4
|
- form_group_default: Default
|
5
5
|
- form_group_button: Button
|
6
|
+
- form_group_full_width: Full Width
|
6
7
|
- form_group_date_picker: Date Picker
|
7
8
|
# - form_group_typeahead: Typeahead
|
8
9
|
- form_group_select: Select
|
@@ -13,6 +14,7 @@ examples:
|
|
13
14
|
react:
|
14
15
|
- form_group_default: Default
|
15
16
|
- form_group_button: Button
|
17
|
+
- form_group_full_width: Full Width
|
16
18
|
- form_group_date_picker: Date Picker
|
17
19
|
# - form_group_typeahead: Typeahead
|
18
20
|
- form_group_select: Select
|
@@ -1,7 +1,7 @@
|
|
1
1
|
export { default as FormGroupDefault } from './_form_group_default.jsx'
|
2
2
|
export { default as FormGroupButton } from './_form_group_button.jsx'
|
3
|
+
export { default as FormGroupFullWidth } from './_form_group_full_width.jsx'
|
3
4
|
export { default as FormGroupDatePicker } from './_form_group_date_picker.jsx'
|
4
|
-
// export { default as FormGroupTypeahead } from './_form_group_typeahead.jsx'
|
5
5
|
export { default as FormGroupSelect } from './_form_group_select.jsx'
|
6
6
|
export { default as FormGroupSelectableCard } from './_form_group_selectable_card.jsx'
|
7
7
|
export { default as FormGroupSelectableCardIcon } from './_form_group_selectable_card_icon.jsx'
|
@@ -3,8 +3,17 @@
|
|
3
3
|
module Playbook
|
4
4
|
module PbFormGroup
|
5
5
|
class FormGroup < Playbook::KitBase
|
6
|
+
prop :full_width, type: Playbook::Props::Boolean,
|
7
|
+
default: false
|
8
|
+
|
6
9
|
def classname
|
7
|
-
generate_classname("pb_form_group_kit")
|
10
|
+
generate_classname("pb_form_group_kit", full_width_class)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def full_width_class
|
16
|
+
full_width ? "full" : nil
|
8
17
|
end
|
9
18
|
end
|
10
19
|
end
|
@@ -1,6 +1,42 @@
|
|
1
|
-
<%= pb_rails("nav", props: {title: "
|
2
|
-
<%= pb_rails("nav/item", props: { link: "#", active: true }) do%>
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
<%= pb_rails("nav", props: {title: "Users", link: "#"}) do %>
|
2
|
+
<%= pb_rails("nav/item", props: { link: "#", active: true }) do%>
|
3
|
+
<%= pb_rails("user", props: {
|
4
|
+
name: "Anna Black",
|
5
|
+
territory: "PHL",
|
6
|
+
title: "Remodeling Consultant",
|
7
|
+
orientation: "horizontal",
|
8
|
+
align: "left",
|
9
|
+
avatar_url: "https://randomuser.me/api/portraits/women/44.jpg"
|
10
|
+
}) %>
|
11
|
+
<% end %>
|
12
|
+
<%= pb_rails("nav/item", props: { link: "#" }) do%>
|
13
|
+
<%= pb_rails("user", props: {
|
14
|
+
name: "Julie Hamilton",
|
15
|
+
territory: "PHL",
|
16
|
+
title: "Inside Sales Agent",
|
17
|
+
orientation: "horizontal",
|
18
|
+
align: "left",
|
19
|
+
avatar_url: "https://randomuser.me/api/portraits/women/45.jpg"
|
20
|
+
}) %>
|
21
|
+
<% end %>
|
22
|
+
<%= pb_rails("nav/item", props: { link: "#" }) do%>
|
23
|
+
<%= pb_rails("user", props: {
|
24
|
+
name: "Dennis Wilks",
|
25
|
+
territory: "PHL",
|
26
|
+
title: "Senior Remodeling Consultant",
|
27
|
+
orientation: "horizontal",
|
28
|
+
align: "left",
|
29
|
+
avatar_url: "https://randomuser.me/api/portraits/men/44.jpg"
|
30
|
+
}) %>
|
31
|
+
<% end %>
|
32
|
+
<%= pb_rails("nav/item", props: { link: "#" }) do%>
|
33
|
+
<%= pb_rails("user", props: {
|
34
|
+
name: "Ronnie Martin",
|
35
|
+
territory: "PHL",
|
36
|
+
title: "Customer Development Representative",
|
37
|
+
orientation: "horizontal",
|
38
|
+
align: "left",
|
39
|
+
avatar_url: "https://randomuser.me/api/portraits/men/46.jpg"
|
40
|
+
}) %>
|
41
|
+
<% end %>
|
6
42
|
<% end %>
|
@@ -1,12 +1,12 @@
|
|
1
1
|
import React from 'react'
|
2
|
-
import { Nav } from '../../'
|
2
|
+
import { Nav, User } from '../../'
|
3
3
|
import NavItem from '../_item.jsx'
|
4
4
|
|
5
5
|
const BlockNav = (props) => {
|
6
6
|
return (
|
7
7
|
<Nav
|
8
8
|
link="#"
|
9
|
-
title="
|
9
|
+
title="Users"
|
10
10
|
{...props}
|
11
11
|
>
|
12
12
|
<NavItem
|
@@ -14,11 +14,49 @@ const BlockNav = (props) => {
|
|
14
14
|
link="#"
|
15
15
|
{...props}
|
16
16
|
>
|
17
|
-
|
17
|
+
<User
|
18
|
+
align="left"
|
19
|
+
avatarUrl="https://randomuser.me/api/portraits/women/44.jpg"
|
20
|
+
name="Anna Black"
|
21
|
+
orientation="horizontal"
|
22
|
+
territory="PHL"
|
23
|
+
title="Remodeling Consultant"
|
24
|
+
{...props}
|
25
|
+
/>
|
26
|
+
</NavItem>
|
27
|
+
<NavItem link="#">
|
28
|
+
<User
|
29
|
+
align="left"
|
30
|
+
avatarUrl="https://randomuser.me/api/portraits/women/45.jpg"
|
31
|
+
name="Julie Hamilton"
|
32
|
+
orientation="horizontal"
|
33
|
+
territory="PHL"
|
34
|
+
title="Inside Sales Agent"
|
35
|
+
{...props}
|
36
|
+
/>
|
37
|
+
</NavItem>
|
38
|
+
<NavItem link="#">
|
39
|
+
<User
|
40
|
+
align="left"
|
41
|
+
avatarUrl="https://randomuser.me/api/portraits/men/44.jpg"
|
42
|
+
name="Dennis Wilks"
|
43
|
+
orientation="horizontal"
|
44
|
+
territory="PHL"
|
45
|
+
title="Senior Remodeling Consultant"
|
46
|
+
{...props}
|
47
|
+
/>
|
48
|
+
</NavItem>
|
49
|
+
<NavItem link="#">
|
50
|
+
<User
|
51
|
+
align="left"
|
52
|
+
avatarUrl="https://randomuser.me/api/portraits/men/46.jpg"
|
53
|
+
name="Ronnie Martin"
|
54
|
+
orientation="horizontal"
|
55
|
+
territory="PHL"
|
56
|
+
title="Customer Development Representative"
|
57
|
+
{...props}
|
58
|
+
/>
|
18
59
|
</NavItem>
|
19
|
-
<NavItem link="#">{'Music'}</NavItem>
|
20
|
-
<NavItem link="#">{'Video'}</NavItem>
|
21
|
-
<NavItem link="#">{'Files'}</NavItem>
|
22
60
|
</Nav>
|
23
61
|
)
|
24
62
|
}
|
@@ -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
|
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
|
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 } =
|
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.
|
@@ -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,
|
@@ -121,3 +121,15 @@ test('popover target does not show when tips are not given', () => {
|
|
121
121
|
const kit = screen.getByTestId(testId)
|
122
122
|
expect(kit.querySelector('[class^=pb_popover_reference_wrapper]')).toBeNull()
|
123
123
|
})
|
124
|
+
|
125
|
+
test('data-strength attribute exposes strength of password', () => {
|
126
|
+
render(
|
127
|
+
<Passphrase
|
128
|
+
data={{ testid: testId }}
|
129
|
+
value="correct horse battery staple"
|
130
|
+
/>
|
131
|
+
)
|
132
|
+
|
133
|
+
const kit = screen.getByTestId(testId)
|
134
|
+
expect(parseInt(kit.getAttribute('data-strength'))).toBeGreaterThan(0)
|
135
|
+
})
|
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
import { useEffect, useState } from 'react'
|
3
|
+
|
4
|
+
const checkHaveIBeenPwned = async function (passphrase) {
|
5
|
+
const buffer = new TextEncoder('utf-8').encode(passphrase)
|
6
|
+
const digest = await crypto.subtle.digest('SHA-1', buffer)
|
7
|
+
const hashArray = Array.from(new Uint8Array(digest))
|
8
|
+
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
|
9
|
+
|
10
|
+
const firstFive = hashHex.slice(0, 5)
|
11
|
+
const endOfHash = hashHex.slice(5)
|
12
|
+
|
13
|
+
const resp = await fetch(`https://api.pwnedpasswords.com/range/${firstFive}`)
|
14
|
+
const text = await resp.text()
|
15
|
+
|
16
|
+
const match = text.split('\n').some((line) => {
|
17
|
+
//Each line is <sha-1-hash-suffix>:<count of incidents>
|
18
|
+
return line.split(':')[0] === endOfHash.toUpperCase()
|
19
|
+
})
|
20
|
+
return match
|
21
|
+
}
|
22
|
+
|
23
|
+
/**
|
24
|
+
* If the input hasn't changed in <delay> ms,
|
25
|
+
* hit the haveibeenpwned api and check if the given passphrase is compromised
|
26
|
+
*/
|
27
|
+
export default function useHaveIBeenPwned(passphrase, minLength, delay = 400) {
|
28
|
+
const [isPwned, setIsPwned] = useState(false)
|
29
|
+
|
30
|
+
useEffect(
|
31
|
+
() => {
|
32
|
+
// only check the API for passphrases above the minimum size
|
33
|
+
if (passphrase.length < minLength) {
|
34
|
+
setIsPwned(false)
|
35
|
+
return
|
36
|
+
}
|
37
|
+
|
38
|
+
const handler = setTimeout(() => {
|
39
|
+
checkHaveIBeenPwned(passphrase)
|
40
|
+
.then((pwned) => setIsPwned(pwned))
|
41
|
+
.catch(() => setIsPwned(false))
|
42
|
+
}, delay)
|
43
|
+
|
44
|
+
return () => {
|
45
|
+
clearTimeout(handler)
|
46
|
+
}
|
47
|
+
},
|
48
|
+
[passphrase, minLength, delay]
|
49
|
+
)
|
50
|
+
|
51
|
+
return isPwned
|
52
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import { useEffect, useMemo, useState } from 'react'
|
2
|
+
import zxcvbn from 'zxcvbn'
|
3
|
+
|
4
|
+
export default function useZxcvbn(options) {
|
5
|
+
const { passphrase = '', common, isPwned, confirmation, averageThreshold, minLength, strongThreshold } = options
|
6
|
+
const calculator = useMemo(
|
7
|
+
() => confirmation ? () => ({ score: 0 }) : zxcvbn,
|
8
|
+
[confirmation]
|
9
|
+
)
|
10
|
+
|
11
|
+
const [percent, setPercent] = useState('0')
|
12
|
+
const [variant, setVariant] = useState('negative')
|
13
|
+
const [text, setText] = useState('\u00A0') //nbsp to keep height constant
|
14
|
+
const [result, setResult] = useState({})
|
15
|
+
|
16
|
+
useEffect(() => {
|
17
|
+
if (confirmation) return
|
18
|
+
|
19
|
+
setResult(calculator(passphrase))
|
20
|
+
const str = result.score
|
21
|
+
|
22
|
+
const noPassphrase = passphrase.length <= 0
|
23
|
+
const commonPassphrase = common || isPwned
|
24
|
+
const weakPassphrase = passphrase.length < minLength || str < averageThreshold
|
25
|
+
const averagePassphrase = str < strongThreshold
|
26
|
+
const strongPassphrase = str >= strongThreshold
|
27
|
+
|
28
|
+
if (noPassphrase) {
|
29
|
+
setPercent('0')
|
30
|
+
setVariant('negative')
|
31
|
+
setText('\u00A0') //nbsp to keep height constant
|
32
|
+
} else if (commonPassphrase) {
|
33
|
+
setPercent('25')
|
34
|
+
setVariant('negative')
|
35
|
+
setText('This passphrase is too common')
|
36
|
+
} else if (weakPassphrase) {
|
37
|
+
setPercent('25')
|
38
|
+
setVariant('negative')
|
39
|
+
setText('Too weak')
|
40
|
+
} else if (averagePassphrase){
|
41
|
+
setPercent('50')
|
42
|
+
setVariant('warning')
|
43
|
+
setText('Almost there, keep going!')
|
44
|
+
} else if (strongPassphrase) {
|
45
|
+
setPercent('100')
|
46
|
+
setVariant('positive')
|
47
|
+
setText('Success! Strong passphrase')
|
48
|
+
}
|
49
|
+
}, [passphrase, common, isPwned, averageThreshold, minLength, strongThreshold]
|
50
|
+
)
|
51
|
+
|
52
|
+
return {
|
53
|
+
strength: common || isPwned ? 0 : result.score,
|
54
|
+
percent,
|
55
|
+
variant,
|
56
|
+
text,
|
57
|
+
}
|
58
|
+
}
|
@@ -37,6 +37,8 @@ type SelectProps = {
|
|
37
37
|
id?: string,
|
38
38
|
includeBlank?: string,
|
39
39
|
label?: string,
|
40
|
+
margin: string,
|
41
|
+
marginBottom: string,
|
40
42
|
multiple?: boolean,
|
41
43
|
name?: string,
|
42
44
|
required?: boolean,
|
@@ -74,7 +76,14 @@ const Select = ({
|
|
74
76
|
const dataProps = buildDataProps(data)
|
75
77
|
const optionsList = createOptions(options)
|
76
78
|
|
77
|
-
const classes = classnames(
|
79
|
+
const classes = classnames(
|
80
|
+
buildCss('pb_select'),
|
81
|
+
globalProps({
|
82
|
+
...props,
|
83
|
+
marginBottom: props.marginBottom || props.margin || 'sm',
|
84
|
+
}),
|
85
|
+
className)
|
86
|
+
|
78
87
|
const selectWrapperClass = classnames(buildCss('pb_select_kit_wrapper'), { error }, className)
|
79
88
|
|
80
89
|
return (
|
@@ -4,7 +4,6 @@
|
|
4
4
|
@import "../tokens/colors";
|
5
5
|
|
6
6
|
[class^=pb_select] {
|
7
|
-
margin-bottom: $space_sm;
|
8
7
|
select {
|
9
8
|
@include pb_textarea_light;
|
10
9
|
@include pb_body_light;
|
@@ -65,38 +64,36 @@
|
|
65
64
|
transform: translateY(-50%);
|
66
65
|
pointer-events: none;
|
67
66
|
}
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
&:focus{
|
86
|
-
border-color: $active_dark;
|
67
|
+
}
|
68
|
+
|
69
|
+
[class^=pb_select].dark {
|
70
|
+
select {
|
71
|
+
@include pb_textarea_dark;
|
72
|
+
@include pb_body_light_dark;
|
73
|
+
background: none;
|
74
|
+
background-color: rgba($white,.10);
|
75
|
+
box-shadow: inset 0 -11px 20px rgba($white, 0.05);
|
76
|
+
text-shadow: 0 0 0 $text_dk_default;
|
77
|
+
padding-right: $space_xl;
|
78
|
+
white-space: nowrap;
|
79
|
+
overflow: hidden;
|
80
|
+
text-overflow: ellipsis;
|
81
|
+
@media (hover:hover) {
|
82
|
+
&:hover, &:active, &:focus {
|
83
|
+
background-color: rgba($white,.05);
|
87
84
|
}
|
88
85
|
}
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
86
|
+
}
|
87
|
+
.pb_select_kit_caret {
|
88
|
+
color: $white;
|
89
|
+
}
|
90
|
+
.pb_select_kit_wrapper {
|
91
|
+
&.error {
|
92
|
+
.pb_select_kit_wrapper {
|
93
|
+
> select:first-child {
|
94
|
+
border-color: $error_dark;
|
98
95
|
}
|
99
96
|
}
|
100
97
|
}
|
101
98
|
}
|
102
|
-
}
|
99
|
+
}
|
@@ -17,13 +17,17 @@ module Playbook
|
|
17
17
|
prop :required, type: Playbook::Props::Boolean, default: false
|
18
18
|
|
19
19
|
def classname
|
20
|
-
generate_classname("pb_select")
|
20
|
+
generate_classname("pb_select", select_margin_bottom, separator: " ")
|
21
21
|
end
|
22
22
|
|
23
23
|
def select_wrapper_class
|
24
24
|
"pb_select_kit_wrapper" + error_class
|
25
25
|
end
|
26
26
|
|
27
|
+
def select_margin_bottom
|
28
|
+
margin.present? || margin_bottom.present? ? nil : "mb_sm"
|
29
|
+
end
|
30
|
+
|
27
31
|
def options_to_array
|
28
32
|
options.map { |option| [option[:value_text] || option[:value], option[:value]] }
|
29
33
|
end
|
@@ -7,7 +7,11 @@
|
|
7
7
|
class: object.classname) do %>
|
8
8
|
<div class="pb_typeahead_wrapper">
|
9
9
|
<div class="pb_typeahead_loading_indicator" data-pb-typeahead-kit-loading-indicator>
|
10
|
-
|
10
|
+
<%= pb_rails("icon", props: {
|
11
|
+
icon: "spinner",
|
12
|
+
pulse: true,
|
13
|
+
fixed_width: true,
|
14
|
+
}) %>
|
11
15
|
</div>
|
12
16
|
<%= pb_rails("text_input", props: {
|
13
17
|
type: "search",
|
@@ -3,8 +3,7 @@
|
|
3
3
|
module Playbook
|
4
4
|
module PbTypeahead
|
5
5
|
class Typeahead < Playbook::KitBase
|
6
|
-
prop :async, type: Playbook::Props::Boolean,
|
7
|
-
default: false
|
6
|
+
prop :async, type: Playbook::Props::Boolean, default: false
|
8
7
|
prop :default_options, type: Playbook::Props::HashArray, default: []
|
9
8
|
prop :get_option_label
|
10
9
|
prop :get_option_value
|
@@ -13,9 +12,7 @@ module Playbook
|
|
13
12
|
prop :load_options
|
14
13
|
prop :name
|
15
14
|
prop :options, type: Playbook::Props::HashArray, default: []
|
16
|
-
prop :pills, type: Playbook::Props::Boolean,
|
17
|
-
default: false
|
18
|
-
|
15
|
+
prop :pills, type: Playbook::Props::Boolean, default: false
|
19
16
|
prop :placeholder
|
20
17
|
prop :search_term_minimum_length, default: 3
|
21
18
|
prop :search_debounce_timeout, default: 250
|
@@ -45,14 +42,9 @@ module Playbook
|
|
45
42
|
placeholder: placeholder,
|
46
43
|
}
|
47
44
|
|
48
|
-
base_options.merge!({getOptionLabel: get_option_label}) if get_option_label.present?
|
49
|
-
base_options.merge!({getOptionValue: get_option_value}) if get_option_value.present?
|
50
|
-
|
51
|
-
base_options.merge!({
|
52
|
-
async: true,
|
53
|
-
loadOptions: load_options,
|
54
|
-
}) if async
|
55
|
-
|
45
|
+
base_options.merge!({ getOptionLabel: get_option_label }) if get_option_label.present?
|
46
|
+
base_options.merge!({ getOptionValue: get_option_value }) if get_option_value.present?
|
47
|
+
base_options.merge!({ async: true, loadOptions: load_options }) if async
|
56
48
|
base_options
|
57
49
|
end
|
58
50
|
end
|
data/lib/playbook/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: playbook_ui
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 9.
|
4
|
+
version: 9.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Power UX
|
8
8
|
- Power Devs
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2021-04-
|
12
|
+
date: 2021-04-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: actionpack
|
@@ -285,22 +285,22 @@ dependencies:
|
|
285
285
|
name: rspec-rails
|
286
286
|
requirement: !ruby/object:Gem::Requirement
|
287
287
|
requirements:
|
288
|
-
- - ">="
|
289
|
-
- !ruby/object:Gem::Version
|
290
|
-
version: 3.8.0
|
291
288
|
- - "~>"
|
292
289
|
- !ruby/object:Gem::Version
|
293
290
|
version: '3.8'
|
291
|
+
- - ">="
|
292
|
+
- !ruby/object:Gem::Version
|
293
|
+
version: 3.8.0
|
294
294
|
type: :development
|
295
295
|
prerelease: false
|
296
296
|
version_requirements: !ruby/object:Gem::Requirement
|
297
297
|
requirements:
|
298
|
-
- - ">="
|
299
|
-
- !ruby/object:Gem::Version
|
300
|
-
version: 3.8.0
|
301
298
|
- - "~>"
|
302
299
|
- !ruby/object:Gem::Version
|
303
300
|
version: '3.8'
|
301
|
+
- - ">="
|
302
|
+
- !ruby/object:Gem::Version
|
303
|
+
version: 3.8.0
|
304
304
|
- !ruby/object:Gem::Dependency
|
305
305
|
name: rspec-html-matchers
|
306
306
|
requirement: !ruby/object:Gem::Requirement
|
@@ -1004,6 +1004,9 @@ files:
|
|
1004
1004
|
- app/pb_kits/playbook/pb_form_group/docs/_form_group_date_picker.jsx
|
1005
1005
|
- app/pb_kits/playbook/pb_form_group/docs/_form_group_default.html.erb
|
1006
1006
|
- app/pb_kits/playbook/pb_form_group/docs/_form_group_default.jsx
|
1007
|
+
- app/pb_kits/playbook/pb_form_group/docs/_form_group_full_width.html.erb
|
1008
|
+
- app/pb_kits/playbook/pb_form_group/docs/_form_group_full_width.jsx
|
1009
|
+
- app/pb_kits/playbook/pb_form_group/docs/_form_group_full_width.md
|
1007
1010
|
- app/pb_kits/playbook/pb_form_group/docs/_form_group_select.html.erb
|
1008
1011
|
- app/pb_kits/playbook/pb_form_group/docs/_form_group_select.jsx
|
1009
1012
|
- app/pb_kits/playbook/pb_form_group/docs/_form_group_selectable_card.html.erb
|
@@ -1380,6 +1383,9 @@ files:
|
|
1380
1383
|
- app/pb_kits/playbook/pb_online_status/online_status.rb
|
1381
1384
|
- app/pb_kits/playbook/pb_passphrase/_passphrase.jsx
|
1382
1385
|
- app/pb_kits/playbook/pb_passphrase/_passphrase.scss
|
1386
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_breached.html.erb
|
1387
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_breached.jsx
|
1388
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_breached.md
|
1383
1389
|
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_common.jsx
|
1384
1390
|
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_default.html.erb
|
1385
1391
|
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_default.jsx
|
@@ -1401,6 +1407,8 @@ files:
|
|
1401
1407
|
- app/pb_kits/playbook/pb_passphrase/passphrase.rb
|
1402
1408
|
- app/pb_kits/playbook/pb_passphrase/passphrase.test.jsx
|
1403
1409
|
- app/pb_kits/playbook/pb_passphrase/passwordStrength.js
|
1410
|
+
- app/pb_kits/playbook/pb_passphrase/useHaveIBeenPwned.js
|
1411
|
+
- app/pb_kits/playbook/pb_passphrase/useZxcvbn.js
|
1404
1412
|
- app/pb_kits/playbook/pb_person/_person.jsx
|
1405
1413
|
- app/pb_kits/playbook/pb_person/_person.scss
|
1406
1414
|
- app/pb_kits/playbook/pb_person/docs/_description.md
|
@@ -2115,7 +2123,7 @@ homepage: http://playbook.powerapp.cloud
|
|
2115
2123
|
licenses:
|
2116
2124
|
- MIT
|
2117
2125
|
metadata: {}
|
2118
|
-
post_install_message:
|
2126
|
+
post_install_message:
|
2119
2127
|
rdoc_options: []
|
2120
2128
|
require_paths:
|
2121
2129
|
- lib
|
@@ -2130,8 +2138,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
2130
2138
|
- !ruby/object:Gem::Version
|
2131
2139
|
version: '0'
|
2132
2140
|
requirements: []
|
2133
|
-
rubygems_version: 3.
|
2134
|
-
signing_key:
|
2141
|
+
rubygems_version: 3.1.4
|
2142
|
+
signing_key:
|
2135
2143
|
specification_version: 4
|
2136
2144
|
summary: Playbook Design System
|
2137
2145
|
test_files: []
|