playbook_ui 9.5.0.alpha.rs.downgrade → 9.7.0.pre.alpha.a11y.btn

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/_playbook.scss +1 -0
  3. data/app/pb_kits/playbook/data/menu.yml +1 -0
  4. data/app/pb_kits/playbook/index.js +4 -3
  5. data/app/pb_kits/playbook/pb_button/_button.jsx +8 -19
  6. data/app/pb_kits/playbook/pb_button/button.rb +6 -4
  7. data/app/pb_kits/playbook/pb_button/docs/_button_accessibility.html.erb +1 -1
  8. data/app/pb_kits/playbook/pb_button/docs/_button_accessibility.jsx +1 -1
  9. data/app/pb_kits/playbook/pb_button/docs/_button_link.html.erb +3 -3
  10. data/app/pb_kits/playbook/pb_button/docs/_button_link.jsx +3 -0
  11. data/app/pb_kits/playbook/pb_button/docs/_button_loading.html.erb +3 -3
  12. data/app/pb_kits/playbook/pb_button/docs/_button_loading.jsx +3 -0
  13. data/app/pb_kits/playbook/pb_date/_date.jsx +3 -3
  14. data/app/pb_kits/playbook/pb_date/date.html.erb +2 -3
  15. data/app/pb_kits/playbook/pb_date/docs/_date_variants.html.erb +8 -0
  16. data/app/pb_kits/playbook/pb_date/docs/_date_variants.jsx +10 -0
  17. data/app/pb_kits/playbook/pb_dialog/dialog_header.rb +23 -24
  18. data/app/pb_kits/playbook/pb_flex/_flex_item.jsx +1 -1
  19. data/app/pb_kits/playbook/pb_nav/_vertical_nav.scss +1 -1
  20. data/app/pb_kits/playbook/pb_nav/docs/_block_nav.html.erb +41 -5
  21. data/app/pb_kits/playbook/pb_nav/docs/_block_nav.jsx +44 -6
  22. data/app/pb_kits/playbook/pb_passphrase/_passphrase.jsx +205 -0
  23. data/app/pb_kits/playbook/pb_passphrase/_passphrase.scss +73 -0
  24. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_common.jsx +33 -0
  25. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_default.html.erb +3 -0
  26. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_default.jsx +31 -0
  27. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_default.md +1 -0
  28. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_input_props.html.erb +16 -0
  29. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_input_props.jsx +56 -0
  30. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_input_props.md +1 -0
  31. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.html.erb +10 -0
  32. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.jsx +68 -0
  33. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.md +9 -0
  34. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_strength_change.jsx +33 -0
  35. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_strength_change.md +3 -0
  36. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_tips.html.erb +26 -0
  37. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_tips.jsx +54 -0
  38. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_tips.md +1 -0
  39. data/app/pb_kits/playbook/pb_passphrase/docs/example.yml +15 -0
  40. data/app/pb_kits/playbook/pb_passphrase/docs/index.js +6 -0
  41. data/app/pb_kits/playbook/pb_passphrase/passphrase.html.erb +1 -0
  42. data/app/pb_kits/playbook/pb_passphrase/passphrase.rb +36 -0
  43. data/app/pb_kits/playbook/pb_passphrase/passphrase.test.jsx +123 -0
  44. data/app/pb_kits/playbook/pb_passphrase/passwordStrength.js +55 -0
  45. data/app/pb_kits/playbook/pb_select/_select.jsx +10 -1
  46. data/app/pb_kits/playbook/pb_select/_select.scss +27 -30
  47. data/app/pb_kits/playbook/pb_select/select.rb +5 -1
  48. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_default.html.erb +1 -1
  49. data/app/pb_kits/playbook/pb_typeahead/typeahead.html.erb +5 -1
  50. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +5 -13
  51. data/app/pb_kits/playbook/react_rails_kits.js +1 -0
  52. data/lib/playbook/version.rb +1 -1
  53. metadata +27 -3
@@ -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
+ }
@@ -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(buildCss('pb_select'), globalProps(props), className)
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
- &.dark {
69
- select {
70
- @include pb_textarea_dark;
71
- @include pb_body_light_dark;
72
- background: none;
73
- background-color: rgba($white,.10);
74
- box-shadow: inset 0 -11px 20px rgba($white, 0.05);
75
- text-shadow: 0 0 0 $text_dk_default;
76
- padding-right: $space_xl;
77
- white-space: nowrap;
78
- overflow: hidden;
79
- text-overflow: ellipsis;
80
- @media (hover:hover) {
81
- &:hover, &:active, &:focus {
82
- background-color: rgba($white,.05);
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
- .pb_select_kit_caret {
90
- color: $white;
91
- }
92
- .pb_select_kit_wrapper {
93
- &.error {
94
- .pb_select_kit_wrapper {
95
- > select:first-child {
96
- border-color: $error_dark;
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
+ }