playbook_ui 9.5.0 → 9.6.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.
- checksums.yaml +4 -4
- data/app/pb_kits/playbook/_playbook.scss +1 -0
- data/app/pb_kits/playbook/data/menu.yml +1 -0
- data/app/pb_kits/playbook/index.js +4 -3
- data/app/pb_kits/playbook/pb_passphrase/_passphrase.jsx +205 -0
- data/app/pb_kits/playbook/pb_passphrase/_passphrase.scss +73 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_common.jsx +33 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_default.html.erb +3 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_default.jsx +31 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_default.md +1 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_input_props.html.erb +16 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_input_props.jsx +56 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_input_props.md +1 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.html.erb +10 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.jsx +68 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.md +9 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_strength_change.jsx +33 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_strength_change.md +3 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_tips.html.erb +26 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_tips.jsx +54 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_tips.md +1 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/example.yml +15 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/index.js +6 -0
- data/app/pb_kits/playbook/pb_passphrase/passphrase.html.erb +1 -0
- data/app/pb_kits/playbook/pb_passphrase/passphrase.rb +36 -0
- data/app/pb_kits/playbook/pb_passphrase/passphrase.test.jsx +123 -0
- data/app/pb_kits/playbook/pb_passphrase/passwordStrength.js +55 -0
- data/app/pb_kits/playbook/react_rails_kits.js +1 -0
- data/lib/playbook/version.rb +1 -1
- metadata +26 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d5c9c206b5e2196df28e9fdb9088f32c40c5e3c1813d6f540080c2d57274639c
|
|
4
|
+
data.tar.gz: af75f14e148548a6ac02dff2ccf0d3cd5a91606e77b9e2b0a99a2a99494b8bbe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 35e89c59f507a8b8b9066fd62ea2dbd957da3c13ff6ff28680eeed2cb6df74f131e08223be7e463373ee00d9383048e1158bf9a68dbd11d16f898cbc7597a20c
|
|
7
|
+
data.tar.gz: a930c3874d040650eed126e1ac89d75767bc2aa8a76a9b9235039b15eb2173e8b89c9f87966ad79859341010b699dfca023f9a62f1f6fd6a6b17430a349ee55e
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
@import 'pb_multiple_users_stacked/multiple_users_stacked';
|
|
56
56
|
@import 'pb_nav/nav';
|
|
57
57
|
@import 'pb_online_status/online_status';
|
|
58
|
+
@import 'pb_passphrase/passphrase';
|
|
58
59
|
@import 'pb_person/person';
|
|
59
60
|
@import 'pb_person_contact/person_contact';
|
|
60
61
|
@import 'pb_pill/pill';
|
|
@@ -61,6 +61,7 @@ export MultipleUsersStacked from './pb_multiple_users_stacked/_multiple_users_st
|
|
|
61
61
|
export Nav from './pb_nav/_nav.jsx'
|
|
62
62
|
export NavItem from './pb_nav/_item.jsx'
|
|
63
63
|
export OnlineStatus from './pb_online_status/_online_status.jsx'
|
|
64
|
+
export Passphrase from './pb_passphrase/_passphrase.jsx'
|
|
64
65
|
export PbReactPopover from './pb_popover/_popover.jsx'
|
|
65
66
|
export Person from './pb_person/_person.jsx'
|
|
66
67
|
export PersonContact from './pb_person_contact/_person_contact.jsx'
|
|
@@ -84,13 +85,13 @@ export StatChange from './pb_stat_change/_stat_change.jsx'
|
|
|
84
85
|
export StatValue from './pb_stat_value/_stat_value.jsx'
|
|
85
86
|
export Table from './pb_table/_table.jsx'
|
|
86
87
|
export TableRow from './pb_table/_table_row.jsx'
|
|
87
|
-
export Textarea from './pb_textarea/_textarea.jsx'
|
|
88
88
|
export TextInput from './pb_text_input/_text_input.jsx'
|
|
89
|
+
export Textarea from './pb_textarea/_textarea.jsx'
|
|
89
90
|
export Time from './pb_time/_time.jsx'
|
|
90
|
-
export
|
|
91
|
+
export TimeRangeInline from './pb_time_range_inline/_time_range_inline.jsx'
|
|
91
92
|
export TimeStacked from './pb_time_stacked/_time_stacked.jsx'
|
|
93
|
+
export Timeline from './pb_timeline/_timeline.jsx'
|
|
92
94
|
export Timestamp from './pb_timestamp/_timestamp.jsx'
|
|
93
|
-
export TimeRangeInline from './pb_time_range_inline/_time_range_inline.jsx'
|
|
94
95
|
export Title from './pb_title/_title.jsx'
|
|
95
96
|
export TitleCount from './pb_title_count/_title_count.jsx'
|
|
96
97
|
export TitleDetail from './pb_title_detail/_title_detail.jsx'
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
|
|
2
|
+
/* @flow */
|
|
3
|
+
|
|
4
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
|
5
|
+
import classnames from 'classnames'
|
|
6
|
+
import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
|
|
7
|
+
import { globalProps } from '../utilities/globalProps.js'
|
|
8
|
+
import { zxcvbnPasswordScore } from './passwordStrength.js'
|
|
9
|
+
import { Body, Caption, Flex, Icon, PbReactPopover, ProgressSimple, TextInput } from '../'
|
|
10
|
+
|
|
11
|
+
type PassphraseProps = {
|
|
12
|
+
aria?: object,
|
|
13
|
+
averageThreshold?: number,
|
|
14
|
+
common?: boolean,
|
|
15
|
+
confirmation?: boolean,
|
|
16
|
+
className?: string,
|
|
17
|
+
data?: object,
|
|
18
|
+
dark?: boolean,
|
|
19
|
+
id?: string,
|
|
20
|
+
inputProps?: {},
|
|
21
|
+
label?: string,
|
|
22
|
+
minLength?: number,
|
|
23
|
+
onChange: (String) => void,
|
|
24
|
+
showTipsBelow?: 'always' | 'xs' | 'sm' | 'md' | 'lg' | 'xl',
|
|
25
|
+
onStrengthChange?: (number) => void,
|
|
26
|
+
strongThreshold?: number,
|
|
27
|
+
tips?: Array<string>,
|
|
28
|
+
uncontrolled?: boolean,
|
|
29
|
+
value: string,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const Passphrase = (props: PassphraseProps) => {
|
|
33
|
+
const {
|
|
34
|
+
aria = {},
|
|
35
|
+
averageThreshold = 2,
|
|
36
|
+
className,
|
|
37
|
+
common = false,
|
|
38
|
+
confirmation = false,
|
|
39
|
+
dark = false,
|
|
40
|
+
data = {},
|
|
41
|
+
id,
|
|
42
|
+
inputProps = {},
|
|
43
|
+
label = confirmation ? 'Confirm Passphrase' : 'Passphrase',
|
|
44
|
+
minLength,
|
|
45
|
+
onChange = () => {},
|
|
46
|
+
showTipsBelow = 'always',
|
|
47
|
+
onStrengthChange,
|
|
48
|
+
strongThreshold = 3,
|
|
49
|
+
tips = [],
|
|
50
|
+
uncontrolled = false,
|
|
51
|
+
value = '',
|
|
52
|
+
} = props
|
|
53
|
+
|
|
54
|
+
const [uncontrolledValue, setUncontrolledValue] = useState('')
|
|
55
|
+
|
|
56
|
+
const handleChange = useCallback(
|
|
57
|
+
(e) => uncontrolled ? setUncontrolledValue(e.target.value) : onChange(e),
|
|
58
|
+
[uncontrolled, onChange]
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
const displayValue = useMemo(
|
|
62
|
+
() => (uncontrolled ? uncontrolledValue : value),
|
|
63
|
+
[value, uncontrolledValue, uncontrolled],
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
const [showPopover, setShowPopover] = useState(false)
|
|
67
|
+
const toggleShowPopover = () => setShowPopover(!showPopover)
|
|
68
|
+
const [showPassphrase, setShowPassphrase] = useState(false)
|
|
69
|
+
const toggleShowPassphrase = () => setShowPassphrase(!showPassphrase)
|
|
70
|
+
|
|
71
|
+
const ariaProps = buildAriaProps(aria)
|
|
72
|
+
const dataProps = buildDataProps(data)
|
|
73
|
+
const classes = classnames(buildCss('pb_passphrase'), globalProps(props), className)
|
|
74
|
+
|
|
75
|
+
const calculator = useMemo(
|
|
76
|
+
() => confirmation ? { test: () => ({}) } : zxcvbnPasswordScore({ averageThreshold, strongThreshold, minLength }),
|
|
77
|
+
[averageThreshold, confirmation, strongThreshold, minLength]
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
const { percent: progressPercent, variant: progressVariant, text: strengthLabel, strength } = calculator.test(displayValue, common)
|
|
81
|
+
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (typeof onStrengthChange === 'function') {
|
|
84
|
+
onStrengthChange(strength)
|
|
85
|
+
}
|
|
86
|
+
}, [strength])
|
|
87
|
+
|
|
88
|
+
const tipClass = classnames(
|
|
89
|
+
(dark ? 'dark' : null),
|
|
90
|
+
(showTipsBelow === 'always' ? null : `show-below-${showTipsBelow}`),
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
const popoverReference = (
|
|
94
|
+
<a
|
|
95
|
+
className={tipClass}
|
|
96
|
+
onClick={toggleShowPopover}
|
|
97
|
+
>
|
|
98
|
+
<Icon
|
|
99
|
+
dark={dark}
|
|
100
|
+
icon="info-circle"
|
|
101
|
+
size="xs"
|
|
102
|
+
variant="link"
|
|
103
|
+
/>
|
|
104
|
+
</a>
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<div
|
|
109
|
+
{...ariaProps}
|
|
110
|
+
{...dataProps}
|
|
111
|
+
className={classes}
|
|
112
|
+
id={id}
|
|
113
|
+
>
|
|
114
|
+
<label>
|
|
115
|
+
<Flex align="baseline">
|
|
116
|
+
<Caption
|
|
117
|
+
className="passphrase-label"
|
|
118
|
+
text={label}
|
|
119
|
+
/>
|
|
120
|
+
<If condition={tips.length > 0 && !confirmation}>
|
|
121
|
+
<PbReactPopover
|
|
122
|
+
placement="right"
|
|
123
|
+
reference={popoverReference}
|
|
124
|
+
show={showPopover}
|
|
125
|
+
>
|
|
126
|
+
<Flex
|
|
127
|
+
align="center"
|
|
128
|
+
orientation="column"
|
|
129
|
+
>
|
|
130
|
+
<Caption
|
|
131
|
+
marginBottom="xs"
|
|
132
|
+
text="Tips for a good passphrase"
|
|
133
|
+
/>
|
|
134
|
+
<div>
|
|
135
|
+
{
|
|
136
|
+
tips.map((tip, i) => (
|
|
137
|
+
<Caption
|
|
138
|
+
key={i}
|
|
139
|
+
marginBottom="xs"
|
|
140
|
+
size="xs"
|
|
141
|
+
>
|
|
142
|
+
<Icon
|
|
143
|
+
icon="shield-check"
|
|
144
|
+
marginRight="xs"
|
|
145
|
+
/>
|
|
146
|
+
{tip}
|
|
147
|
+
</Caption>
|
|
148
|
+
))
|
|
149
|
+
}
|
|
150
|
+
</div>
|
|
151
|
+
</Flex>
|
|
152
|
+
</PbReactPopover>
|
|
153
|
+
</If>
|
|
154
|
+
</Flex>
|
|
155
|
+
<div className="passphrase-text-input-wrapper">
|
|
156
|
+
<TextInput
|
|
157
|
+
className="passphrase-text-input"
|
|
158
|
+
dark={dark}
|
|
159
|
+
marginBottom="xs"
|
|
160
|
+
onChange={handleChange}
|
|
161
|
+
placeholder="Enter a passphrase..."
|
|
162
|
+
type={showPassphrase ? 'text' : 'password'}
|
|
163
|
+
value={displayValue}
|
|
164
|
+
{...inputProps}
|
|
165
|
+
/>
|
|
166
|
+
<span
|
|
167
|
+
className="show-passphrase-icon"
|
|
168
|
+
dark={dark}
|
|
169
|
+
onClick={toggleShowPassphrase}
|
|
170
|
+
>
|
|
171
|
+
<Body
|
|
172
|
+
className={showPassphrase ? 'hide-icon' : ''}
|
|
173
|
+
color="light"
|
|
174
|
+
dark={dark}
|
|
175
|
+
>
|
|
176
|
+
<Icon icon="eye-slash" />
|
|
177
|
+
</Body>
|
|
178
|
+
<Body
|
|
179
|
+
className={showPassphrase ? '' : 'hide-icon'}
|
|
180
|
+
color="light"
|
|
181
|
+
dark={dark}
|
|
182
|
+
>
|
|
183
|
+
<Icon icon="eye" />
|
|
184
|
+
</Body>
|
|
185
|
+
</span>
|
|
186
|
+
</div>
|
|
187
|
+
</label>
|
|
188
|
+
<If condition={!confirmation}>
|
|
189
|
+
<ProgressSimple
|
|
190
|
+
className={displayValue.length === 0 ? 'progress-empty-input' : null}
|
|
191
|
+
dark={dark}
|
|
192
|
+
percent={progressPercent}
|
|
193
|
+
variant={progressVariant}
|
|
194
|
+
/>
|
|
195
|
+
<Caption
|
|
196
|
+
dark={dark}
|
|
197
|
+
size="xs"
|
|
198
|
+
text={strengthLabel}
|
|
199
|
+
/>
|
|
200
|
+
</If>
|
|
201
|
+
</div>
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export default Passphrase
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
@import "../tokens/colors";
|
|
2
|
+
@import "../tokens/spacing";
|
|
3
|
+
@import "../tokens/screen_sizes";
|
|
4
|
+
|
|
5
|
+
.pb_passphrase {
|
|
6
|
+
margin-bottom: $space_sm;
|
|
7
|
+
|
|
8
|
+
label {
|
|
9
|
+
display: inline;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
a.dark {
|
|
13
|
+
color: $white;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
a {
|
|
17
|
+
|
|
18
|
+
&[class*=show-below-] {
|
|
19
|
+
display: none;
|
|
20
|
+
}
|
|
21
|
+
@each $breakpoint_name, $breakpoint in $breakpoints {
|
|
22
|
+
&.show-below-#{$breakpoint_name} {
|
|
23
|
+
@include break_at($breakpoint) {
|
|
24
|
+
display: inline;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.passphrase-label {
|
|
31
|
+
margin-right: ($space_xs/2);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.passphrase-text-input-wrapper {
|
|
35
|
+
position: relative;
|
|
36
|
+
|
|
37
|
+
.pb_text_input_kit_label {
|
|
38
|
+
margin-bottom: ($space_xs/2);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.passphrase-text-input input {
|
|
42
|
+
padding-right: 42px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.passphrase-text-input .text_input_wrapper {
|
|
46
|
+
margin-bottom: 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.show-passphrase-icon {
|
|
50
|
+
position: absolute;
|
|
51
|
+
right: 11px;
|
|
52
|
+
top: 11px;
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
@media (hover:hover) {
|
|
55
|
+
&:hover .pb_body_kit_light {
|
|
56
|
+
color: $primary;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.hide-icon {
|
|
61
|
+
display: none;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.pb_progress_simple_wrapper, .pb_progress_simple_wrapper_dark {
|
|
67
|
+
margin-bottom: ($space_xs/2);
|
|
68
|
+
|
|
69
|
+
&.progress-empty-input {
|
|
70
|
+
visibility: hidden;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { Body, Passphrase } from '../../'
|
|
3
|
+
|
|
4
|
+
const PassphraseCommon = (props) => {
|
|
5
|
+
const [input, setInput] = useState('')
|
|
6
|
+
|
|
7
|
+
const handleChange = (e) => setInput(e.target.value)
|
|
8
|
+
|
|
9
|
+
const COMMON_PASSPHRASES = ['passphrase', 'apple', 'password', 'p@55w0rd']
|
|
10
|
+
|
|
11
|
+
const commonCheck = (passphrase) => {
|
|
12
|
+
if (COMMON_PASSPHRASES.includes(passphrase))
|
|
13
|
+
return true
|
|
14
|
+
return false
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<div>
|
|
20
|
+
<Body text={`Try typing any of the following: ${COMMON_PASSPHRASES.join(' ')}`} />
|
|
21
|
+
<br />
|
|
22
|
+
<Passphrase
|
|
23
|
+
common={commonCheck(input)}
|
|
24
|
+
onChange={handleChange}
|
|
25
|
+
value={input}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
</div>
|
|
29
|
+
</>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default PassphraseCommon
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { Passphrase } from '../../'
|
|
3
|
+
|
|
4
|
+
const PassphraseDefault = (props) => {
|
|
5
|
+
const [input, setInput] = useState('')
|
|
6
|
+
const handleChange = (e) => setInput(e.target.value)
|
|
7
|
+
|
|
8
|
+
const [confoInput, setConfoInput] = useState('')
|
|
9
|
+
const handleConfoChange = (e) => setConfoInput(e.target.value)
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<>
|
|
13
|
+
<div>
|
|
14
|
+
<Passphrase
|
|
15
|
+
onChange={handleChange}
|
|
16
|
+
value={input}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
<Passphrase
|
|
20
|
+
confirmation
|
|
21
|
+
onChange={handleConfoChange}
|
|
22
|
+
value={confoInput}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
<span>{input === confoInput ? 'They match!' : 'They don\'t match!'}</span>
|
|
26
|
+
</div>
|
|
27
|
+
</>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default PassphraseDefault
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Use the `confirmation` prop to only include the label and show/hide icon.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<%= pb_rails("passphrase", props: {
|
|
2
|
+
input_props: {
|
|
3
|
+
disabled: true,
|
|
4
|
+
id: "my-disabled-passphrase",
|
|
5
|
+
name: "my-disabled-field",
|
|
6
|
+
},
|
|
7
|
+
label: "Input props passed directly to input kit"
|
|
8
|
+
}) %>
|
|
9
|
+
|
|
10
|
+
<%= pb_rails("passphrase", props: {
|
|
11
|
+
input_props: {
|
|
12
|
+
id: "my-custome-id",
|
|
13
|
+
name: "my-value-name",
|
|
14
|
+
},
|
|
15
|
+
label: "Set name, id, etc for use in forms"
|
|
16
|
+
}) %>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { Passphrase } from '../../'
|
|
3
|
+
|
|
4
|
+
const PassphraseInputProps = (props) => {
|
|
5
|
+
const [input, setInput] = useState('')
|
|
6
|
+
|
|
7
|
+
const handleChange = (e) => setInput(e.target.value)
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<>
|
|
11
|
+
<div>
|
|
12
|
+
<Passphrase
|
|
13
|
+
inputProps={{
|
|
14
|
+
name: 'my-disabled-field',
|
|
15
|
+
id: 'my-value-id',
|
|
16
|
+
disabled: true,
|
|
17
|
+
}}
|
|
18
|
+
label="Pass props directly to input kit"
|
|
19
|
+
onChange={handleChange}
|
|
20
|
+
value={input}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
<Passphrase
|
|
24
|
+
inputProps={{
|
|
25
|
+
children: (
|
|
26
|
+
<input
|
|
27
|
+
onChange={handleChange}
|
|
28
|
+
type="password"
|
|
29
|
+
value={input}
|
|
30
|
+
/>),
|
|
31
|
+
}}
|
|
32
|
+
label="Custom input"
|
|
33
|
+
onChange={handleChange}
|
|
34
|
+
value={input}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
<Passphrase
|
|
38
|
+
inputProps={{ name: 'my-value-name', id: 'my-value-id' }}
|
|
39
|
+
label="Set name and ID for use in form libraries"
|
|
40
|
+
onChange={handleChange}
|
|
41
|
+
value={input}
|
|
42
|
+
{...props}
|
|
43
|
+
/>
|
|
44
|
+
<Passphrase
|
|
45
|
+
confirmation
|
|
46
|
+
inputProps={{ name: 'my-value-confirmation-name', id: 'my-value-confirmation-id' }}
|
|
47
|
+
onChange={handleChange}
|
|
48
|
+
value={input}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
</>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default PassphraseInputProps
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
`inputProps` is passed directly to an underlying Text Input kit. See the specific docs <a href="/kits/text_input/react" target="_blank">here</a> for more details.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
|
|
2
|
+
<%= pb_rails("passphrase", props: { label: "Default Settings"}) %>
|
|
3
|
+
|
|
4
|
+
<%= pb_rails("passphrase", props: { label: "Min length = 5", min_length: 5}) %>
|
|
5
|
+
|
|
6
|
+
<%= pb_rails("passphrase", props: { label: "Min length = 30", min_length: 30}) %>
|
|
7
|
+
|
|
8
|
+
<%= pb_rails("passphrase", props: { average_threshold: 1, label: "Average Threshold = 1"}) %>
|
|
9
|
+
|
|
10
|
+
<%= pb_rails("passphrase", props: { label: "Strong Threshold = 4", strong_threshold: 4}) %>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { Body, Passphrase, TextInput } from '../../'
|
|
3
|
+
|
|
4
|
+
const PassphraseMeterSettings = (props) => {
|
|
5
|
+
const [input, setInput] = useState('')
|
|
6
|
+
|
|
7
|
+
const handleChange = (e) => setInput(e.target.value)
|
|
8
|
+
|
|
9
|
+
const [strength, setStrength] = useState(0)
|
|
10
|
+
const handleStrengthChange = (str) => setStrength(str)
|
|
11
|
+
return (
|
|
12
|
+
<>
|
|
13
|
+
<div>
|
|
14
|
+
<Body>
|
|
15
|
+
{'These examples will all share the same input value. Type in any of the inputs to see how the strength meter changes in response to different settings.'}
|
|
16
|
+
</Body>
|
|
17
|
+
<br />
|
|
18
|
+
<TextInput
|
|
19
|
+
disabled
|
|
20
|
+
label="Calculated Strength"
|
|
21
|
+
readOnly
|
|
22
|
+
value={strength}
|
|
23
|
+
/>
|
|
24
|
+
|
|
25
|
+
<Passphrase
|
|
26
|
+
label="Default settings"
|
|
27
|
+
onChange={handleChange}
|
|
28
|
+
onStrengthChange={handleStrengthChange}
|
|
29
|
+
value={input}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
|
|
33
|
+
<Passphrase
|
|
34
|
+
label="Min length = 5"
|
|
35
|
+
minLength={5}
|
|
36
|
+
onChange={handleChange}
|
|
37
|
+
value={input}
|
|
38
|
+
{...props}
|
|
39
|
+
/>
|
|
40
|
+
<Passphrase
|
|
41
|
+
label="Min length = 30"
|
|
42
|
+
minLength={30}
|
|
43
|
+
onChange={handleChange}
|
|
44
|
+
value={input}
|
|
45
|
+
{...props}
|
|
46
|
+
/>
|
|
47
|
+
|
|
48
|
+
<Passphrase
|
|
49
|
+
averageThreshold={1}
|
|
50
|
+
label="Average threshold = 1"
|
|
51
|
+
onChange={handleChange}
|
|
52
|
+
value={input}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
|
|
56
|
+
<Passphrase
|
|
57
|
+
label="Strong Threshold = 4"
|
|
58
|
+
onChange={handleChange}
|
|
59
|
+
strongThreshold={4}
|
|
60
|
+
value={input}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
</div>
|
|
64
|
+
</>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export default PassphraseMeterSettings
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
By default, the minimum length is 12 and the strength meter will show a strength of 1 if not met. Notice the bar won't change from red until the minimum is met
|
|
2
|
+
Use the `minLength` prop to adjust.
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
The meter also response to `averageThreshold` and `strongTreshold` props. `averageThresold` defaults to 2, and `strongThreshold` defaults to 3.
|
|
6
|
+
This means that the bar will turn yellow when the strength of the passphrase is calculated to be 2 on a 0-4 scale, and green when 3.
|
|
7
|
+
|
|
8
|
+
Adjust these props to tune the sensitivity of the bar.
|
|
9
|
+
Note: minimum length trumps strength and will set the bar to a red color, despite whatever strength is calculated.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { Passphrase, TextInput } from '../../'
|
|
3
|
+
|
|
4
|
+
const PassphraseStrengthChange = (props) => {
|
|
5
|
+
const [input, setInput] = useState('')
|
|
6
|
+
|
|
7
|
+
const handleChange = (e) => setInput(e.target.value)
|
|
8
|
+
|
|
9
|
+
const [strength, setStrength] = useState(0)
|
|
10
|
+
const handleStrengthChange = (str) => setStrength(str)
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<>
|
|
14
|
+
<div>
|
|
15
|
+
<Passphrase
|
|
16
|
+
label="Passphrase"
|
|
17
|
+
onChange={handleChange}
|
|
18
|
+
onStrengthChange={handleStrengthChange}
|
|
19
|
+
value={input}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
<TextInput
|
|
23
|
+
disabled
|
|
24
|
+
label="Passphrase Strength"
|
|
25
|
+
readOnly
|
|
26
|
+
value={strength}
|
|
27
|
+
/>
|
|
28
|
+
</div>
|
|
29
|
+
</>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default PassphraseStrengthChange
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
As the strength of the entered passphrase changes, the optional `onStrengthChange` callback is called with the new strength value. This exposes the calculated strength.
|
|
2
|
+
|
|
3
|
+
Strength is calculated on a 0-4 scale by the <a href="https://github.com/dropbox/zxcvbn" target="_blank"> Zxcvbn package</a>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<%= pb_rails("passphrase", props: {
|
|
2
|
+
label: "Pass an array of strings to the tips prop",
|
|
3
|
+
tips: ['And the info icon will appear.', 'Each string will be displayed as its own tip'],
|
|
4
|
+
}) %>
|
|
5
|
+
|
|
6
|
+
<%= pb_rails("passphrase", props: {
|
|
7
|
+
label: "Omit the prop to hide the icon"
|
|
8
|
+
}) %>
|
|
9
|
+
|
|
10
|
+
<%= pb_rails("passphrase", props: {
|
|
11
|
+
label: "Only show tips at small screen size",
|
|
12
|
+
show_tips_below: "sm",
|
|
13
|
+
tips: ['Make the password longer', 'Type more things', 'Use something else'],
|
|
14
|
+
}) %>
|
|
15
|
+
|
|
16
|
+
<%= pb_rails("passphrase", props: {
|
|
17
|
+
label: "Only show tips at medium screen size",
|
|
18
|
+
show_tips_below: "md",
|
|
19
|
+
tips: ['Make the password longer', 'Type more things', 'Use something else'],
|
|
20
|
+
}) %>
|
|
21
|
+
|
|
22
|
+
<%= pb_rails("passphrase", props: {
|
|
23
|
+
label: "Only show tips at large screen size",
|
|
24
|
+
show_tips_below: "lg",
|
|
25
|
+
tips: ['Make the password longer', 'Type more things', 'Use something else'],
|
|
26
|
+
}) %>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { Passphrase } from '../../'
|
|
3
|
+
|
|
4
|
+
const PassphraseTips = (props) => {
|
|
5
|
+
const [input, setInput] = useState('')
|
|
6
|
+
|
|
7
|
+
const handleChange = (e) => setInput(e.target.value)
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<>
|
|
11
|
+
<div>
|
|
12
|
+
<Passphrase
|
|
13
|
+
label="Pass an array of strings to the tips prop"
|
|
14
|
+
onChange={handleChange}
|
|
15
|
+
tips={['And the info icon will appear.', 'Each string will be displayed as its own tip']}
|
|
16
|
+
value={input}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
<Passphrase
|
|
20
|
+
label="Omit the prop to hide the icon"
|
|
21
|
+
onChange={handleChange}
|
|
22
|
+
value={input}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
<Passphrase
|
|
26
|
+
label="Only show tips at small screen size"
|
|
27
|
+
onChange={handleChange}
|
|
28
|
+
showTipsBelow="xs"
|
|
29
|
+
tips={['Make the password longer', 'Type more things', 'Use something else']}
|
|
30
|
+
value={input}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
<Passphrase
|
|
34
|
+
label="Only show tips at medium screen size"
|
|
35
|
+
onChange={handleChange}
|
|
36
|
+
showTipsBelow="md"
|
|
37
|
+
tips={['Make the password longer', 'Type more things', 'Use something else']}
|
|
38
|
+
value={input}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
<Passphrase
|
|
42
|
+
label="Only show tips at large screen size"
|
|
43
|
+
onChange={handleChange}
|
|
44
|
+
showTipsBelow="lg"
|
|
45
|
+
tips={['Make the password longer', 'Type more things', 'Use something else']}
|
|
46
|
+
value={input}
|
|
47
|
+
{...props}
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
</>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default PassphraseTips
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
`showTipsBelow`(react) / `show_tips_below`(rails) takes 'xs', 'sm', 'md', 'lg', 'xl' and only show the tips below the given screen size. Similar to the <a href="/kits/table/react" target="_blank">responsive table breakpoints.</a> Omit this prop to always show.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
examples:
|
|
2
|
+
|
|
3
|
+
rails:
|
|
4
|
+
- passphrase_default: Default
|
|
5
|
+
- passphrase_meter_settings: Meter Settings
|
|
6
|
+
- passphrase_input_props: Input Props
|
|
7
|
+
- passphrase_tips: Tips
|
|
8
|
+
|
|
9
|
+
react:
|
|
10
|
+
- passphrase_default: Default
|
|
11
|
+
- passphrase_meter_settings: Meter Settings
|
|
12
|
+
- passphrase_input_props: Input Props
|
|
13
|
+
- passphrase_tips: Tips
|
|
14
|
+
- passphrase_strength_change: Strength Change
|
|
15
|
+
- passphrase_common: Common Passphrases
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as PassphraseDefault } from './_passphrase_default.jsx'
|
|
2
|
+
export { default as PassphraseMeterSettings } from './_passphrase_meter_settings'
|
|
3
|
+
export { default as PassphraseInputProps } from './_passphrase_input_props'
|
|
4
|
+
export { default as PassphraseTips } from './_passphrase_tips'
|
|
5
|
+
export { default as PassphraseStrengthChange } from './_passphrase_strength_change'
|
|
6
|
+
export { default as PassphraseCommon } from './_passphrase_common'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= react_component('Passphrase', object.passphrase_options) %>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Playbook
|
|
4
|
+
module PbPassphrase
|
|
5
|
+
class Passphrase < Playbook::KitBase
|
|
6
|
+
prop :average_threshold
|
|
7
|
+
prop :confirmation, type: Playbook::Props::Boolean, default: false
|
|
8
|
+
prop :input_props, type: Playbook::Props::Hash, default: {}
|
|
9
|
+
prop :label
|
|
10
|
+
prop :min_length
|
|
11
|
+
prop :show_tips_below
|
|
12
|
+
prop :strong_threshold
|
|
13
|
+
prop :tips, type: Playbook::Props::Array, default: []
|
|
14
|
+
|
|
15
|
+
def classname
|
|
16
|
+
generate_classname("pb_passphrase")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def passphrase_options
|
|
20
|
+
{
|
|
21
|
+
dark: dark,
|
|
22
|
+
id: id,
|
|
23
|
+
averageThreshold: average_threshold,
|
|
24
|
+
confirmation: confirmation,
|
|
25
|
+
inputProps: input_props,
|
|
26
|
+
label: label,
|
|
27
|
+
minLength: min_length,
|
|
28
|
+
showTipsBelow: show_tips_below,
|
|
29
|
+
strongThreshold: strong_threshold,
|
|
30
|
+
tips: tips,
|
|
31
|
+
uncontrolled: true,
|
|
32
|
+
}.compact
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { render, screen } from '../utilities/test-utils'
|
|
3
|
+
import { Passphrase } from '../'
|
|
4
|
+
|
|
5
|
+
const testId = 'text-input1',
|
|
6
|
+
kitClass = 'pb_passphrase'
|
|
7
|
+
|
|
8
|
+
/* See these resources for more testing info:
|
|
9
|
+
- https://github.com/testing-library/jest-dom#usage for useage and examples
|
|
10
|
+
- https://jestjs.io/docs/en/using-matchers
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
test('returns namespaced class name', () => {
|
|
14
|
+
render(
|
|
15
|
+
<Passphrase
|
|
16
|
+
data={{ testid: testId }}
|
|
17
|
+
/>
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
const kit = screen.getByTestId(testId)
|
|
21
|
+
expect(kit).toHaveClass(kitClass)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('returns additional class name', () => {
|
|
25
|
+
render(
|
|
26
|
+
<Passphrase
|
|
27
|
+
className="additional_class"
|
|
28
|
+
data={{ testid: testId }}
|
|
29
|
+
/>
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
const kit = screen.getByTestId(testId)
|
|
33
|
+
expect(kit).toHaveClass(`${kitClass} additional_class`)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('returns dark class name', () => {
|
|
37
|
+
render(
|
|
38
|
+
<Passphrase
|
|
39
|
+
dark
|
|
40
|
+
data={{ testid: testId }}
|
|
41
|
+
/>
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
const kit = screen.getByTestId(testId)
|
|
45
|
+
expect(kit).toHaveClass(`${kitClass} dark`)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('passes input props to input element', () => {
|
|
49
|
+
render(
|
|
50
|
+
<Passphrase
|
|
51
|
+
data={{ testid: testId }}
|
|
52
|
+
inputProps={{
|
|
53
|
+
name: 'test-name',
|
|
54
|
+
id: 'test-input-id',
|
|
55
|
+
disabled: true,
|
|
56
|
+
}}
|
|
57
|
+
/>
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
const kit = screen.getByTestId(testId)
|
|
61
|
+
const input = kit.getElementsByTagName('input')[0]
|
|
62
|
+
expect(input).toHaveAttribute('name', 'test-name')
|
|
63
|
+
expect(input).toHaveAttribute('id', 'test-input-id')
|
|
64
|
+
expect(input).toBeDisabled()
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('progress bar is invisible when value is empty', () => {
|
|
68
|
+
render(
|
|
69
|
+
<Passphrase
|
|
70
|
+
data={{ testid: testId }}
|
|
71
|
+
/>
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
const kit = screen.getByTestId(testId)
|
|
75
|
+
expect(kit.querySelector('[class^=pb_progress_simple_wrapper]')).toHaveClass('progress-empty-input')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test('progress bar is visible when value is not empty', () => {
|
|
79
|
+
render(
|
|
80
|
+
<Passphrase
|
|
81
|
+
data={{ testid: testId }}
|
|
82
|
+
value="test_password_input"
|
|
83
|
+
/>
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
const kit = screen.getByTestId(testId)
|
|
87
|
+
expect(kit.querySelector('[class^=pb_progress_simple_wrapper]')).not.toHaveClass('progress-empty-input')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('no progress bar is show when confirmation is true', () => {
|
|
91
|
+
render(
|
|
92
|
+
<Passphrase
|
|
93
|
+
confirmation
|
|
94
|
+
data={{ testid: testId }}
|
|
95
|
+
/>
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
const kit = screen.getByTestId(testId)
|
|
99
|
+
expect(kit.querySelector('[class^=pb_progress_simple_wrapper]')).toBeNull()
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('popover target shows when tips are given', () => {
|
|
103
|
+
render(
|
|
104
|
+
<Passphrase
|
|
105
|
+
data={{ testid: testId }}
|
|
106
|
+
tips={['some helpful tips']}
|
|
107
|
+
/>
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
const kit = screen.getByTestId(testId)
|
|
111
|
+
expect(kit.querySelector('[class^=pb_popover_reference_wrapper]')).toBeDefined()
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test('popover target does not show when tips are not given', () => {
|
|
115
|
+
render(
|
|
116
|
+
<Passphrase
|
|
117
|
+
data={{ testid: testId }}
|
|
118
|
+
/>
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
const kit = screen.getByTestId(testId)
|
|
122
|
+
expect(kit.querySelector('[class^=pb_popover_reference_wrapper]')).toBeNull()
|
|
123
|
+
})
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import zxcvbn from 'zxcvbn'
|
|
2
|
+
|
|
3
|
+
export const zxcvbnPasswordScore = (options) => {
|
|
4
|
+
const {
|
|
5
|
+
calculate = zxcvbn,
|
|
6
|
+
averageThreshold = 2,
|
|
7
|
+
strongThreshold = 3,
|
|
8
|
+
minLength = 12,
|
|
9
|
+
} = options
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
minLength,
|
|
13
|
+
averageThreshold,
|
|
14
|
+
strongThreshold,
|
|
15
|
+
test: function (password = '', common = false) {
|
|
16
|
+
const feedbackValues = (str) => {
|
|
17
|
+
let percent, variant, text
|
|
18
|
+
|
|
19
|
+
if (password.length <= 0) {
|
|
20
|
+
percent = '0'
|
|
21
|
+
variant = 'negative'
|
|
22
|
+
text = '\u00A0' //nbsp to keep form from jumping when typing beings
|
|
23
|
+
} else if (common) {
|
|
24
|
+
percent = '25'
|
|
25
|
+
variant = 'negative'
|
|
26
|
+
text = 'This passphrase is too common'
|
|
27
|
+
} else if (password.length < this.minLength || str < this.averageThreshold) {
|
|
28
|
+
percent = '25'
|
|
29
|
+
variant = 'negative'
|
|
30
|
+
text = 'Too weak'
|
|
31
|
+
} else if (str < this.strongThreshold){
|
|
32
|
+
percent = '50'
|
|
33
|
+
variant = 'warning'
|
|
34
|
+
text = 'Almost there, keep going!'
|
|
35
|
+
} else if (str >= this.strongThreshold) {
|
|
36
|
+
percent = '100'
|
|
37
|
+
variant = 'positive'
|
|
38
|
+
text = 'Success! Strong passphrase'
|
|
39
|
+
}
|
|
40
|
+
return { percent, variant, text }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const result = calculate(password)
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
{
|
|
47
|
+
suggestions: result.feedback.suggestions,
|
|
48
|
+
warning: result.feedback.warning,
|
|
49
|
+
strength: result.score,
|
|
50
|
+
...feedbackValues(result.score),
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -3,6 +3,7 @@ export { default as BarGraph } from './pb_bar_graph/_bar_graph.jsx'
|
|
|
3
3
|
export { default as DistributionBar } from './pb_distribution_bar/_distribution_bar.jsx'
|
|
4
4
|
export { default as Legend } from './pb_legend/_legend.jsx'
|
|
5
5
|
export { default as LineGraph } from './pb_line_graph/_line_graph.jsx'
|
|
6
|
+
export { default as Passphrase } from './pb_passphrase/_passphrase.jsx'
|
|
6
7
|
export { default as Typeahead } from './pb_typeahead/_typeahead.jsx'
|
|
7
8
|
export { default as RichTextEditor } from './pb_rich_text_editor/_rich_text_editor.jsx'
|
|
8
9
|
export { default as Dialog } from './pb_dialog/_dialog.jsx'
|
data/lib/playbook/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: playbook_ui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 9.
|
|
4
|
+
version: 9.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Power UX
|
|
@@ -1378,6 +1378,29 @@ files:
|
|
|
1378
1378
|
- app/pb_kits/playbook/pb_online_status/docs/index.js
|
|
1379
1379
|
- app/pb_kits/playbook/pb_online_status/online_status.html.erb
|
|
1380
1380
|
- app/pb_kits/playbook/pb_online_status/online_status.rb
|
|
1381
|
+
- app/pb_kits/playbook/pb_passphrase/_passphrase.jsx
|
|
1382
|
+
- app/pb_kits/playbook/pb_passphrase/_passphrase.scss
|
|
1383
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_common.jsx
|
|
1384
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_default.html.erb
|
|
1385
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_default.jsx
|
|
1386
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_default.md
|
|
1387
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_input_props.html.erb
|
|
1388
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_input_props.jsx
|
|
1389
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_input_props.md
|
|
1390
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.html.erb
|
|
1391
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.jsx
|
|
1392
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.md
|
|
1393
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_strength_change.jsx
|
|
1394
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_strength_change.md
|
|
1395
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_tips.html.erb
|
|
1396
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_tips.jsx
|
|
1397
|
+
- app/pb_kits/playbook/pb_passphrase/docs/_passphrase_tips.md
|
|
1398
|
+
- app/pb_kits/playbook/pb_passphrase/docs/example.yml
|
|
1399
|
+
- app/pb_kits/playbook/pb_passphrase/docs/index.js
|
|
1400
|
+
- app/pb_kits/playbook/pb_passphrase/passphrase.html.erb
|
|
1401
|
+
- app/pb_kits/playbook/pb_passphrase/passphrase.rb
|
|
1402
|
+
- app/pb_kits/playbook/pb_passphrase/passphrase.test.jsx
|
|
1403
|
+
- app/pb_kits/playbook/pb_passphrase/passwordStrength.js
|
|
1381
1404
|
- app/pb_kits/playbook/pb_person/_person.jsx
|
|
1382
1405
|
- app/pb_kits/playbook/pb_person/_person.scss
|
|
1383
1406
|
- app/pb_kits/playbook/pb_person/docs/_description.md
|
|
@@ -2107,7 +2130,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
2107
2130
|
- !ruby/object:Gem::Version
|
|
2108
2131
|
version: '0'
|
|
2109
2132
|
requirements: []
|
|
2110
|
-
|
|
2133
|
+
rubyforge_project:
|
|
2134
|
+
rubygems_version: 2.7.3
|
|
2111
2135
|
signing_key:
|
|
2112
2136
|
specification_version: 4
|
|
2113
2137
|
summary: Playbook Design System
|