playbook_ui 9.10.0.pre.alpha2 → 9.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) 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 +1 -0
  5. data/app/pb_kits/playbook/pb_bread_crumbs/_bread_crumb_item.jsx +51 -0
  6. data/app/pb_kits/playbook/pb_bread_crumbs/_bread_crumbs.jsx +49 -0
  7. data/app/pb_kits/playbook/pb_bread_crumbs/_bread_crumbs.scss +55 -0
  8. data/app/pb_kits/playbook/pb_bread_crumbs/bread_crumb_item.html.erb +9 -0
  9. data/app/pb_kits/playbook/pb_bread_crumbs/bread_crumb_item.rb +12 -0
  10. data/app/pb_kits/playbook/pb_bread_crumbs/bread_crumbs.html.erb +7 -0
  11. data/app/pb_kits/playbook/pb_bread_crumbs/bread_crumbs.rb +11 -0
  12. data/app/pb_kits/playbook/pb_bread_crumbs/bread_crumbs.test.js +35 -0
  13. data/app/pb_kits/playbook/pb_bread_crumbs/docs/_bread_crumbs_default.html.erb +17 -0
  14. data/app/pb_kits/playbook/pb_bread_crumbs/docs/_bread_crumbs_default.jsx +56 -0
  15. data/app/pb_kits/playbook/pb_bread_crumbs/docs/_description.md +1 -0
  16. data/app/pb_kits/playbook/pb_bread_crumbs/docs/example.yml +7 -0
  17. data/app/pb_kits/playbook/pb_bread_crumbs/docs/index.js +1 -0
  18. data/app/pb_kits/playbook/pb_button/_button.jsx +8 -19
  19. data/app/pb_kits/playbook/pb_button/button.rb +5 -3
  20. data/app/pb_kits/playbook/pb_button/docs/_button_accessibility.html.erb +1 -1
  21. data/app/pb_kits/playbook/pb_button/docs/_button_accessibility.jsx +1 -1
  22. data/app/pb_kits/playbook/pb_button/docs/_button_link.html.erb +3 -3
  23. data/app/pb_kits/playbook/pb_button/docs/_button_link.jsx +3 -0
  24. data/app/pb_kits/playbook/pb_button/docs/_button_loading.html.erb +3 -3
  25. data/app/pb_kits/playbook/pb_button/docs/_button_loading.jsx +3 -0
  26. data/app/pb_kits/playbook/pb_date_stacked/_date_stacked.jsx +45 -11
  27. data/app/pb_kits/playbook/pb_date_stacked/date_stacked.html.erb +17 -6
  28. data/app/pb_kits/playbook/pb_date_stacked/date_stacked.rb +12 -5
  29. data/app/pb_kits/playbook/pb_date_stacked/docs/_date_stacked_bold.html.erb +5 -0
  30. data/app/pb_kits/playbook/pb_date_stacked/docs/_date_stacked_bold.jsx +38 -0
  31. data/app/pb_kits/playbook/pb_date_stacked/docs/example.yml +3 -1
  32. data/app/pb_kits/playbook/pb_date_stacked/docs/index.js +1 -0
  33. data/app/pb_kits/playbook/pb_date_time_stacked/_date_time_stacked.jsx +32 -16
  34. data/app/pb_kits/playbook/pb_date_time_stacked/_date_time_stacked.scss +23 -4
  35. data/app/pb_kits/playbook/pb_date_time_stacked/date_time_stacked.html.erb +12 -8
  36. data/app/pb_kits/playbook/pb_date_time_stacked/date_time_stacked.rb +9 -2
  37. data/app/pb_kits/playbook/pb_date_time_stacked/date_time_stacked.test.js +35 -0
  38. data/app/pb_kits/playbook/pb_date_time_stacked/docs/_date_time_stacked_default.html.erb +8 -1
  39. data/app/pb_kits/playbook/pb_date_time_stacked/docs/_date_time_stacked_default.jsx +19 -1
  40. data/app/pb_kits/playbook/pb_dialog/dialog.test.jsx +1 -1
  41. data/app/pb_kits/playbook/pb_flex/_flex.jsx +6 -1
  42. data/app/pb_kits/playbook/pb_passphrase/_passphrase.jsx +13 -11
  43. data/app/pb_kits/playbook/pb_passphrase/_passphrase.scss +18 -13
  44. data/app/pb_kits/playbook/pb_passphrase/useZxcvbn.js +3 -3
  45. data/app/pb_kits/playbook/pb_section_separator/_section_separator.jsx +7 -3
  46. data/app/pb_kits/playbook/pb_text_input/_text_input.jsx +80 -31
  47. data/app/pb_kits/playbook/pb_text_input/_text_input.scss +98 -0
  48. data/app/pb_kits/playbook/pb_text_input/add_on.html.erb +13 -0
  49. data/app/pb_kits/playbook/pb_text_input/add_on.rb +30 -0
  50. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_add_on.html.erb +24 -0
  51. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_add_on.jsx +82 -0
  52. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_custom.html.erb +1 -1
  53. data/app/pb_kits/playbook/pb_text_input/docs/example.yml +2 -0
  54. data/app/pb_kits/playbook/pb_text_input/docs/index.js +1 -0
  55. data/app/pb_kits/playbook/pb_text_input/text_input.html.erb +7 -16
  56. data/app/pb_kits/playbook/pb_text_input/text_input.rb +36 -2
  57. data/app/pb_kits/playbook/pb_time_stacked/time_stacked.html.erb +2 -2
  58. data/lib/playbook/props.rb +1 -0
  59. data/lib/playbook/props/nested_props.rb +23 -0
  60. data/lib/playbook/version.rb +1 -1
  61. metadata +36 -14
@@ -3,10 +3,17 @@
3
3
  module Playbook
4
4
  module PbDateTimeStacked
5
5
  class DateTimeStacked < Playbook::KitBase
6
- prop :date, type: Playbook::Props::Date,
7
- default: ::DateTime.current
6
+ prop :date, deprecated: true
7
+ prop :date_time, type: Playbook::Props::Date,
8
+ default: ::DateTime.current
8
9
  prop :dark, type: Playbook::Props::Boolean,
9
10
  default: false
11
+ prop :timezone, type: Playbook::Props::String,
12
+ default: "America/New_York"
13
+
14
+ def date_time_value
15
+ date || date_time
16
+ end
10
17
  end
11
18
  end
12
19
  end
@@ -0,0 +1,35 @@
1
+ import { ensureAccessible, renderKit } from '../utilities/test-utils'
2
+ import { DateTimeStacked } from '../'
3
+
4
+ /* eslint-disable jsx-control-statements/jsx-jcs-no-undef */
5
+
6
+ const props = {
7
+ data: { testid: 'datetimestacked' },
8
+ datetime: new Date('Wed Mar 31 2021 12:00:00 GMT-0500'),
9
+ }
10
+
11
+ test('Kit renders date time', () => {
12
+ const kit = renderKit(DateTimeStacked, props)
13
+ expect(kit).toBeInTheDocument()
14
+ })
15
+
16
+ it('Should be accessible', async () => {
17
+ ensureAccessible(DateTimeStacked, props)
18
+ })
19
+
20
+ test('renders time in default timezone', () => {
21
+ const kit = renderKit(DateTimeStacked, props)
22
+ expect(kit).toHaveTextContent(/Mar311:00pEDT/i)
23
+ })
24
+
25
+ test('renders time in timezone', () => {
26
+ props.timeZone = 'Asia/Tokyo'
27
+ const kit = renderKit(DateTimeStacked, props)
28
+ expect(kit).toHaveTextContent(/Mar312:00aJST/i)
29
+ })
30
+
31
+ test('renders time in timezone', () => {
32
+ props.timeZone = 'America/Denver'
33
+ const kit = renderKit(DateTimeStacked, props)
34
+ expect(kit).toHaveTextContent(/Mar3111:00aMDT/i)
35
+ })
@@ -1 +1,8 @@
1
- <%= pb_rails("date_time_stacked") %>
1
+ <%= pb_rails("date_time_stacked", props: { date_time: DateTime.now }) %>
2
+ <br>
3
+ <%= pb_rails("date_time_stacked", props: { date_time: Date.new(2018, 03, 20) }) %>
4
+ <br>
5
+ <%= pb_rails("date_time_stacked", props: { date_time: DateTime.now, timezone: "Asia/Tokyo" }) %>
6
+ <br>
7
+ <%= pb_rails("date_time_stacked", props: { date_time: DateTime.now, timezone: "America/Denver" }) %>
8
+
@@ -4,10 +4,28 @@ import { DateTimeStacked } from '../../'
4
4
  const DateTimeStackedDefault = (props) => (
5
5
  <div>
6
6
  <DateTimeStacked
7
- date={new Date()}
7
+ datetime={new Date()}
8
8
  {...props}
9
9
  />
10
+ <br />
11
+ <DateTimeStacked
12
+ datetime={new Date()}
13
+ timeZone="Asia/Tokyo"
14
+
15
+ {...props}
16
+ />
17
+ <br />
18
+ <DateTimeStacked
19
+ datetime={new Date()}
20
+ timeZone="America/Denver"
21
+
22
+ {...props}
23
+ />
24
+
10
25
  </div>
11
26
  )
12
27
 
13
28
  export default DateTimeStackedDefault
29
+
30
+ // *Development Note* - We are reviewing this kit for a potential name change due to naming collisions when `new Date()` is used.
31
+ // To avoid this bug, please use name spacing as shown in the code examples. ie `import { Date as AliasedComponentName } from '../../'
@@ -9,7 +9,7 @@ import { Dialog } from '../'
9
9
  - https://jestjs.io/docs/en/using-matchers
10
10
  */
11
11
 
12
- test('generated scaffold test - update me', () => {
12
+ test('Kit renders Dialog', () => {
13
13
  const testId = 'test1'
14
14
 
15
15
  render(
@@ -2,11 +2,12 @@
2
2
 
3
3
  import React from 'react'
4
4
  import classnames from 'classnames'
5
- import { buildCss } from '../utilities/props'
5
+ import { buildCss, buildDataProps } from '../utilities/props'
6
6
  import { globalProps } from '../utilities/globalProps.js'
7
7
  type FlexProps = {
8
8
  children: array<React.ReactNode> | React.ReactNode,
9
9
  className?: string,
10
+ data?: object,
10
11
  horizontal?: "left" | "center" | "right" | "stretch" | "none",
11
12
  justify?: "start" | "center" | "end" | "around" | "between" | "evenly" | "none",
12
13
  id?: string,
@@ -27,6 +28,7 @@ const Flex = (props: FlexProps) => {
27
28
  align = 'none',
28
29
  children,
29
30
  className,
31
+ data = {},
30
32
  inline = false,
31
33
  horizontal = 'left',
32
34
  justify = 'none',
@@ -51,6 +53,8 @@ const Flex = (props: FlexProps) => {
51
53
  const columnGapClass = columnGap !== 'none' ? `columnGap_${columnGap}` : ''
52
54
  const wrapClass = wrap === true ? 'wrap' : ''
53
55
  const reverseClass = reverse === true ? 'reverse' : ''
56
+ const dataProps = buildDataProps(data)
57
+
54
58
  return (
55
59
  <div
56
60
  className={classnames(
@@ -70,6 +74,7 @@ const Flex = (props: FlexProps) => {
70
74
  globalProps(props),
71
75
  className
72
76
  )}
77
+ {...dataProps}
73
78
  >
74
79
  {children}
75
80
  </div>
@@ -7,7 +7,7 @@ import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
7
7
  import { globalProps } from '../utilities/globalProps.js'
8
8
  import useZxcvbn from './useZxcvbn'
9
9
  import useHaveIBeenPwned from './useHaveIBeenPwned'
10
- import { Body, Caption, Flex, Icon, PbReactPopover, ProgressSimple, TextInput } from '../'
10
+ import { Body, Caption, CircleIconButton, Flex, Icon, PbReactPopover, ProgressSimple, TextInput } from '../'
11
11
 
12
12
  type PassphraseProps = {
13
13
  aria?: object,
@@ -69,6 +69,10 @@ const Passphrase = (props: PassphraseProps) => {
69
69
 
70
70
  const [showPopover, setShowPopover] = useState(false)
71
71
  const toggleShowPopover = () => setShowPopover(!showPopover)
72
+ const handleShouldClosePopover = (shouldClosePopover) => {
73
+ setShowPopover(!shouldClosePopover)
74
+ }
75
+
72
76
  const [showPassphrase, setShowPassphrase] = useState(false)
73
77
  const toggleShowPassphrase = () => setShowPassphrase(!showPassphrase)
74
78
 
@@ -85,7 +89,7 @@ const Passphrase = (props: PassphraseProps) => {
85
89
  }, [strength])
86
90
 
87
91
  const tipClass = classnames(
88
- (dark ? 'dark' : null),
92
+ 'passphrase-popover',
89
93
  (showTipsBelow === 'always' ? null : `show-below-${showTipsBelow}`),
90
94
  )
91
95
  const dataProps = useMemo(
@@ -94,17 +98,13 @@ const Passphrase = (props: PassphraseProps) => {
94
98
  )
95
99
 
96
100
  const popoverReference = (
97
- <a
101
+ <CircleIconButton
98
102
  className={tipClass}
103
+ dark={dark}
104
+ icon="info-circle"
99
105
  onClick={toggleShowPopover}
100
- >
101
- <Icon
102
- dark={dark}
103
- icon="info-circle"
104
- size="xs"
105
- variant="link"
106
- />
107
- </a>
106
+ variant="link"
107
+ />
108
108
  )
109
109
 
110
110
  return (
@@ -122,8 +122,10 @@ const Passphrase = (props: PassphraseProps) => {
122
122
  />
123
123
  <If condition={tips.length > 0 && !confirmation}>
124
124
  <PbReactPopover
125
+ closeOnClick="outside"
125
126
  placement="right"
126
127
  reference={popoverReference}
128
+ shouldClosePopover={handleShouldClosePopover}
127
129
  show={showPopover}
128
130
  >
129
131
  <Flex
@@ -9,20 +9,13 @@
9
9
  display: inline;
10
10
  }
11
11
 
12
- a.dark {
13
- color: $white;
12
+ [class*=show-below-] {
13
+ display: none;
14
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
- }
15
+ @each $breakpoint_name, $breakpoint in $breakpoints {
16
+ .show-below-#{$breakpoint_name} {
17
+ @include break_at($breakpoint) {
18
+ display: inline;
26
19
  }
27
20
  }
28
21
  }
@@ -70,4 +63,16 @@
70
63
  visibility: hidden;
71
64
  }
72
65
  }
66
+ .passphrase-popover {
67
+ position: relative;
68
+ left: -($space_xs * 1.25);
69
+ }
70
+ @-moz-document url-prefix() {
71
+ .passphrase-popover {
72
+ position: relative;
73
+ top: $space_sm;
74
+ left: -($space_xs * 1.5);
75
+ }
76
+ }
73
77
  }
78
+
@@ -15,9 +15,9 @@ export default function useZxcvbn(options) {
15
15
 
16
16
  useEffect(() => {
17
17
  if (confirmation) return
18
-
19
- setResult(calculator(passphrase))
20
- const str = result.score
18
+ const newResult = calculator(passphrase)
19
+ setResult(newResult)
20
+ const str = newResult.score
21
21
 
22
22
  const noPassphrase = passphrase.length <= 0
23
23
  const commonPassphrase = common || isPwned
@@ -38,9 +38,13 @@ const SectionSeparator = (props: SectionSeparatorProps) => {
38
38
  className={classes}
39
39
  id={id}
40
40
  >
41
- <span>
42
- <Caption text={text} />
43
- </span>
41
+
42
+ <If condition={text}>
43
+ <span>
44
+ <Caption text={text} />
45
+ </span>
46
+ </If>
47
+
44
48
  </div>
45
49
  )
46
50
  }
@@ -1,13 +1,10 @@
1
1
  /* @flow */
2
2
  import React, { forwardRef } from 'react'
3
3
  import classnames from 'classnames'
4
- import { Body, Caption } from '../'
4
+ import { Body, Caption, Card, Flex, Icon } from '../'
5
5
  import { globalProps } from '../utilities/globalProps.js'
6
6
 
7
- import {
8
- buildAriaProps,
9
- buildDataProps,
10
- } from '../utilities/props'
7
+ import { buildAriaProps, buildDataProps } from '../utilities/props'
11
8
 
12
9
  type TextInputProps = {
13
10
  aria?: object,
@@ -25,12 +22,14 @@ type TextInputProps = {
25
22
  type: string,
26
23
  value: string | number,
27
24
  children: Node,
25
+ addOn?: {
26
+ icon?: string,
27
+ alignment?: "right" | "left",
28
+ border?: boolean,
29
+ },
28
30
  }
29
31
 
30
- const TextInput = (
31
- props: TextInputProps,
32
- ref: React.ElementRef<"input">
33
- ) => {
32
+ const TextInput = (props: TextInputProps, ref: React.ElementRef<"input">) => {
34
33
  const {
35
34
  aria = {},
36
35
  className,
@@ -47,16 +46,78 @@ const TextInput = (
47
46
  type = 'text',
48
47
  value = '',
49
48
  children = null,
49
+ addOn = { icon: null, alignment: 'right', border: true },
50
50
  } = props
51
51
 
52
52
  const ariaProps = buildAriaProps(aria)
53
53
  const dataProps = buildDataProps(data)
54
+
55
+ const { alignment, border, icon } = addOn
56
+ const addOnAlignment = alignment === 'left' ? 'left' : 'right'
57
+ const borderToChange = addOnAlignment === 'left' ? 'right' : 'left'
58
+ const borderToggle = border === false ? 'off' : 'on'
59
+ const borderCss = `border_${borderToChange}_${borderToggle}`
60
+
61
+ const shouldShowAddOn = icon !== null
62
+ const addOnCss = shouldShowAddOn ? 'text_input_wrapper_add_on' : null
63
+ const addOnDarkModeCardCss = (shouldShowAddOn && dark) ? 'add-on-card-dark' : null
54
64
  const css = classnames([
55
65
  'pb_text_input_kit',
56
66
  error ? 'error' : null,
57
67
  globalProps(props),
58
68
  className,
59
69
  ])
70
+ const addOnIcon = (
71
+ <Icon
72
+ className="add-on-icon"
73
+ dark={dark}
74
+ fixedWidth={false}
75
+ icon={icon}
76
+ />
77
+ )
78
+ const textInput = (
79
+ <input
80
+ {...props}
81
+ className="text_input"
82
+ disabled={disabled}
83
+ id={id}
84
+ name={name}
85
+ onChange={onChange}
86
+ placeholder={placeholder}
87
+ ref={ref}
88
+ required={required}
89
+ type={type}
90
+ value={value}
91
+ />
92
+ )
93
+
94
+ const addOnInput = (
95
+ <React.Fragment>
96
+ <Flex
97
+ className={`add-on-${addOnAlignment} ${borderCss}`}
98
+ inline="flex-container"
99
+ vertical="center"
100
+ >
101
+ <If condition={addOnAlignment == 'left'}>
102
+ <Card
103
+ className={`${addOnDarkModeCardCss} add-on-card card-left-aligned`}
104
+ dark={dark}
105
+ >
106
+ {addOnIcon}
107
+ </Card>
108
+ {textInput}
109
+ <Else />
110
+ {textInput}
111
+ <Card
112
+ className={`${addOnDarkModeCardCss} add-on-card card-right-aligned`}
113
+ dark={dark}
114
+ >
115
+ {addOnIcon}
116
+ </Card>
117
+ </If>
118
+ </Flex>
119
+ </React.Fragment>
120
+ )
60
121
 
61
122
  return (
62
123
  <div
@@ -69,29 +130,17 @@ const TextInput = (
69
130
  dark={dark}
70
131
  text={label}
71
132
  />
72
- <div className="text_input_wrapper">
73
- <If condition={children}>
74
- {children}
75
- <Else />
76
- <input
77
- {...props}
78
- className="text_input"
79
- disabled={disabled}
80
- id={id}
81
- name={name}
82
- onChange={onChange}
83
- placeholder={placeholder}
84
- ref={ref}
85
- required={required}
86
- type={type}
87
- value={value}
133
+ <div className={`${addOnCss} text_input_wrapper`}>
134
+ <Choose>
135
+ <When condition={children}>{children}</When>
136
+ <When condition={shouldShowAddOn}>{addOnInput}</When>
137
+ <Otherwise>{textInput}</Otherwise>
138
+ </Choose>
139
+ <If condition={error}>
140
+ <Body
141
+ status="negative"
142
+ text={error}
88
143
  />
89
- <If condition={error}>
90
- <Body
91
- status="negative"
92
- text={error}
93
- />
94
- </If>
95
144
  </If>
96
145
  </div>
97
146
  </div>
@@ -36,6 +36,16 @@
36
36
  }
37
37
  }
38
38
  &.dark {
39
+ .add_on_left {
40
+ .text_input {
41
+ border-left: 0;
42
+ }
43
+ }
44
+ .add_on_right {
45
+ .text_input {
46
+ border-right: 0;
47
+ }
48
+ }
39
49
  .text_input_wrapper {
40
50
  margin-bottom: 1rem;
41
51
  input::placeholder,
@@ -80,3 +90,91 @@
80
90
  }
81
91
  }
82
92
  }
93
+
94
+ .text_input_wrapper_add_on {
95
+ & > [class^=pb_text_input_kit]:not(:last-child) {
96
+ .text_input_wrapper_add_on input, [class^=pb_text_input_kit] .text_input_wrapper_add_on .text_input {
97
+ border-bottom-right-radius: 0;
98
+ border-top-right-radius: 0;
99
+ border-right-width: 0;
100
+ }
101
+ }
102
+ .section-separator span {
103
+ padding: 0;
104
+ }
105
+
106
+ .add-on {
107
+ &-card {
108
+ height: 45px;
109
+ margin: 0;
110
+ padding: 0 !important;
111
+ align-items: center;
112
+ justify-content: center;
113
+ width: 45px;
114
+ }
115
+ &-icon {
116
+ color: $text_lt_lighter;
117
+ }
118
+ &-card-dark {
119
+ background-color: $bg-dark;
120
+ }
121
+ &-left {
122
+ .text_input {
123
+ border-top-left-radius: 0 !important;
124
+ border-bottom-left-radius: 0 !important;
125
+ }
126
+ }
127
+
128
+ &-right {
129
+ .text_input{
130
+ border-top-right-radius: 0 !important;
131
+ border-bottom-right-radius: 0 !important;
132
+ }
133
+ }
134
+ }
135
+ .text_input:focus ~ .add-on-card-dark {
136
+ background-color: $focus_input_dark;
137
+ border-width: 0px;
138
+ }
139
+
140
+ .card-left-aligned {
141
+ border-top-right-radius: 0 !important;
142
+ border-bottom-right-radius: 0 !important;
143
+ }
144
+
145
+ .card-right-aligned {
146
+ border-top-left-radius: 0 !important;
147
+ border-bottom-left-radius: 0 !important;
148
+ }
149
+
150
+ .border {
151
+ &_right_off {
152
+ .card-left-aligned {
153
+ border-right: 0;
154
+ }
155
+ .text_input {
156
+ border-left: 0 !important;
157
+ padding-left: 0 !important;
158
+ }
159
+ }
160
+ &_left_on {
161
+ .card-right-aligned {
162
+ border-left: 0;
163
+ }
164
+ }
165
+ &_right_on {
166
+ .card-left-aligned {
167
+ border-right: 0;
168
+ }
169
+ }
170
+ &_left_off {
171
+ .card-right-aligned {
172
+ border-left: 0;
173
+ }
174
+ .text_input {
175
+ border-right: 0 !important;
176
+ padding-right: 0 !important;
177
+ }
178
+ }
179
+ }
180
+ }