playbook_ui 8.2.0.pre.alpha2 → 8.2.1.pre.alpha2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -18
  3. data/app/pb_kits/playbook/_playbook.scss +1 -0
  4. data/app/pb_kits/playbook/data/menu.yml +2 -1
  5. data/app/pb_kits/playbook/index.js +1 -0
  6. data/app/pb_kits/playbook/pb_avatar/_avatar.jsx +14 -2
  7. data/app/pb_kits/playbook/pb_avatar/avatar.html.erb +1 -1
  8. data/app/pb_kits/playbook/pb_avatar/avatar.rb +5 -2
  9. data/app/pb_kits/playbook/pb_avatar/avatar.test.js +5 -2
  10. data/app/pb_kits/playbook/pb_avatar/docs/_avatar_default.html.erb +5 -0
  11. data/app/pb_kits/playbook/pb_avatar/docs/_avatar_default.jsx +5 -0
  12. data/app/pb_kits/playbook/pb_avatar/docs/_avatar_status.html.erb +4 -0
  13. data/app/pb_kits/playbook/pb_avatar/docs/_avatar_status.jsx +4 -0
  14. data/app/pb_kits/playbook/pb_avatar_action_button/_avatar_action_button.jsx +6 -0
  15. data/app/pb_kits/playbook/pb_avatar_action_button/avatar_action_button.html.erb +2 -1
  16. data/app/pb_kits/playbook/pb_avatar_action_button/avatar_action_button.rb +2 -0
  17. data/app/pb_kits/playbook/pb_avatar_action_button/docs/_avatar_action_button_actions.html.erb +4 -0
  18. data/app/pb_kits/playbook/pb_avatar_action_button/docs/_avatar_action_button_actions.jsx +4 -0
  19. data/app/pb_kits/playbook/pb_avatar_action_button/docs/_avatar_action_button_default.html.erb +2 -0
  20. data/app/pb_kits/playbook/pb_avatar_action_button/docs/_avatar_action_button_default.jsx +2 -0
  21. data/app/pb_kits/playbook/pb_avatar_action_button/docs/_avatar_action_button_on_click.jsx +2 -0
  22. data/app/pb_kits/playbook/pb_avatar_action_button/docs/_avatar_action_button_onclick.html.erb +2 -0
  23. data/app/pb_kits/playbook/pb_avatar_action_button/docs/_avatar_action_button_placement.html.erb +8 -0
  24. data/app/pb_kits/playbook/pb_avatar_action_button/docs/_avatar_action_button_placement.jsx +8 -0
  25. data/app/pb_kits/playbook/pb_avatar_action_button/docs/_avatar_action_button_tooltip.html.erb +2 -0
  26. data/app/pb_kits/playbook/pb_avatar_action_button/pb_avatar_action_button.test.js +31 -0
  27. data/app/pb_kits/playbook/pb_badge/_badge.jsx +26 -1
  28. data/app/pb_kits/playbook/pb_caption/_caption.jsx +3 -2
  29. data/app/pb_kits/playbook/pb_caption/caption.rb +1 -1
  30. data/app/pb_kits/playbook/pb_card/_card.jsx +18 -3
  31. data/app/pb_kits/playbook/pb_card/card.html.erb +1 -1
  32. data/app/pb_kits/playbook/pb_card/card.rb +3 -0
  33. data/app/pb_kits/playbook/pb_card/docs/_card_tag.html.erb +25 -0
  34. data/app/pb_kits/playbook/pb_card/docs/_card_tag.jsx +59 -0
  35. data/app/pb_kits/playbook/pb_card/docs/example.yml +2 -0
  36. data/app/pb_kits/playbook/pb_card/docs/index.js +1 -0
  37. data/app/pb_kits/playbook/pb_checkbox/_checkbox.jsx +31 -9
  38. data/app/pb_kits/playbook/pb_checkbox/_checkbox.scss +28 -19
  39. data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +11 -3
  40. data/app/pb_kits/playbook/pb_checkbox/checkbox.rb +6 -1
  41. data/app/pb_kits/playbook/pb_checkbox/checkbox.test.js +94 -0
  42. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_custom.jsx +0 -1
  43. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_indeterminate.html.erb +7 -0
  44. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_indeterminate.jsx +16 -0
  45. data/app/pb_kits/playbook/pb_checkbox/docs/example.yml +2 -0
  46. data/app/pb_kits/playbook/pb_checkbox/docs/index.js +1 -0
  47. data/app/pb_kits/playbook/pb_date_picker/_date_picker.jsx +6 -1
  48. data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.js +3 -0
  49. data/app/pb_kits/playbook/pb_dialog/_close_icon.jsx +23 -0
  50. data/app/pb_kits/playbook/pb_dialog/_dialog.html.erb +10 -0
  51. data/app/pb_kits/playbook/pb_dialog/_dialog.jsx +142 -0
  52. data/app/pb_kits/playbook/pb_dialog/_dialog.scss +133 -0
  53. data/app/pb_kits/playbook/pb_dialog/_dialog_context.jsx +3 -0
  54. data/app/pb_kits/playbook/pb_dialog/child_kits/_dialog_body.jsx +21 -0
  55. data/app/pb_kits/playbook/pb_dialog/child_kits/_dialog_footer.jsx +36 -0
  56. data/app/pb_kits/playbook/pb_dialog/child_kits/_dialog_header.jsx +68 -0
  57. data/app/pb_kits/playbook/pb_dialog/dialog.rb +47 -0
  58. data/app/pb_kits/playbook/pb_dialog/dialog.test.jsx +23 -0
  59. data/app/pb_kits/playbook/pb_dialog/dialog_header.rb +31 -0
  60. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_compound_components.jsx +53 -0
  61. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_compound_components.md +2 -0
  62. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_default.jsx +27 -0
  63. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_scrollable.jsx +27 -0
  64. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_scrollable.md +2 -0
  65. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_separators.jsx +119 -0
  66. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_separators.md +2 -0
  67. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_should_close_on_overlay.jsx +28 -0
  68. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_should_close_on_overlay.md +2 -0
  69. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_sizes.jsx +93 -0
  70. data/app/pb_kits/playbook/pb_dialog/docs/example.yml +10 -0
  71. data/app/pb_kits/playbook/pb_dialog/docs/index.js +6 -0
  72. data/app/pb_kits/playbook/pb_flex/_flex.jsx +6 -3
  73. data/app/pb_kits/playbook/pb_form/form_builder.rb +4 -2
  74. data/app/pb_kits/playbook/pb_form/form_builder/action_area.rb +14 -7
  75. data/app/pb_kits/playbook/pb_form/simple_form.html.erb +2 -4
  76. data/app/pb_kits/playbook/pb_form/simple_form.rb +4 -0
  77. data/app/pb_kits/playbook/pb_online_status/_online_status.jsx +2 -0
  78. data/app/pb_kits/playbook/pb_online_status/online_status.html.erb +1 -1
  79. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.jsx +4 -3
  80. data/app/pb_kits/playbook/pb_text_input/_text_input.jsx +3 -0
  81. data/app/pb_kits/playbook/pb_textarea/_textarea.jsx +3 -0
  82. data/app/pb_kits/playbook/pb_typeahead/_typeahead.jsx +9 -1
  83. data/app/pb_kits/playbook/pb_typeahead/_typeahead.scss +9 -0
  84. data/app/pb_kits/playbook/pb_typeahead/components/Input.jsx +43 -0
  85. data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.jsx +21 -11
  86. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_default.jsx +1 -0
  87. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills.jsx +8 -3
  88. data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +4 -4
  89. data/app/pb_kits/playbook/pb_user_badge/_user_badge.jsx +1 -1
  90. data/app/pb_kits/playbook/react_rails_kits.js +4 -0
  91. data/lib/playbook/version.rb +2 -1
  92. metadata +55 -27
  93. data/app/pb_kits/playbook/pb_form/form_builder/action_area.html.erb +0 -3
@@ -1,9 +1,11 @@
1
1
  <div class="pb--doc-demo-row">
2
2
 
3
3
  <%= pb_rails("avatar_action_button", props: {
4
+ link_aria_label: "Alert Sophia Carden",
4
5
  name: "Sophia Carden",
5
6
  id: "clickable",
6
7
  link_url: "http://www.google.com",
8
+ image_alt: "Sophia Carden",
7
9
  image_url: "https://randomuser.me/api/portraits/women/8.jpg",
8
10
  }) %>
9
11
 
@@ -1,25 +1,33 @@
1
1
  <div class="pb--doc-demo-row">
2
2
 
3
3
  <%= pb_rails("avatar_action_button", props: {
4
+ link_aria_label: "Sophia Carden",
4
5
  name: "Sophia Carden",
6
+ image_alt: "Sophia Carden",
5
7
  image_url: "https://randomuser.me/api/portraits/women/8.jpg",
6
8
  placement: "bottom_left"
7
9
  }) %>
8
10
 
9
11
  <%= pb_rails("avatar_action_button", props: {
12
+ link_aria_label: "Sophia Carden",
10
13
  name: "Sophia Carden",
14
+ image_alt: "Sophia Carden",
11
15
  image_url: "https://randomuser.me/api/portraits/women/8.jpg",
12
16
  placement: "bottom_right"
13
17
  }) %>
14
18
 
15
19
  <%= pb_rails("avatar_action_button", props: {
20
+ link_aria_label: "Sophia Carden",
16
21
  name: "Sophia Carden",
22
+ image_alt: "Sophia Carden",
17
23
  image_url: "https://randomuser.me/api/portraits/women/8.jpg",
18
24
  placement: "top_left"
19
25
  }) %>
20
26
 
21
27
  <%= pb_rails("avatar_action_button", props: {
28
+ link_aria_label: "Sophia Carden",
22
29
  name: "Sophia Carden",
30
+ image_alt: "Sophia Carden",
23
31
  image_url: "https://randomuser.me/api/portraits/women/8.jpg",
24
32
  placement: "top_right"
25
33
  }) %>
@@ -4,25 +4,33 @@ import { AvatarActionButton } from '../../'
4
4
  const AvatarActionButtonPlacement = (props) => (
5
5
  <div className="pb--doc-demo-row">
6
6
  <AvatarActionButton
7
+ imageAlt="Sophia Carden"
7
8
  imageUrl="https://randomuser.me/api/portraits/women/8.jpg"
9
+ linkAriaLabel="Sophia Carden"
8
10
  name="Sophia Carden"
9
11
  placement="bottom_left"
10
12
  {...props}
11
13
  />
12
14
  <AvatarActionButton
15
+ imageAlt="Sophia Carden"
13
16
  imageUrl="https://randomuser.me/api/portraits/women/8.jpg"
17
+ linkAriaLabel="Sophia Carden"
14
18
  name="Sophia Carden"
15
19
  placement="bottom_right"
16
20
  {...props}
17
21
  />
18
22
  <AvatarActionButton
23
+ imageAlt="Sophia Carden"
19
24
  imageUrl="https://randomuser.me/api/portraits/women/8.jpg"
25
+ linkAriaLabel="Sophia Carden"
20
26
  name="Sophia Carden"
21
27
  placement="top_left"
22
28
  {...props}
23
29
  />
24
30
  <AvatarActionButton
31
+ imageAlt="Sophia Carden"
25
32
  imageUrl="https://randomuser.me/api/portraits/women/8.jpg"
33
+ linkAriaLabel="Sophia Carden"
26
34
  name="Sophia Carden"
27
35
  placement="top_right"
28
36
  {...props}
@@ -1,8 +1,10 @@
1
1
  <div class="pb--doc-demo-row">
2
2
 
3
3
  <%= pb_rails("avatar_action_button", props: {
4
+ link_aria_label: "Sophia Carden",
4
5
  name: "Sophia Carden",
5
6
  link_url: "http://www.google.com",
7
+ image_alt: "Sophia Carden",
6
8
  image_url: "https://randomuser.me/api/portraits/women/8.jpg",
7
9
  tooltip_text: "Tooltip Text",
8
10
  tooltip_id: "avatar_1",
@@ -0,0 +1,31 @@
1
+ import React from 'react'
2
+ import { render, screen } from '../utilities/test-utils'
3
+
4
+ import AvatarActionButton from './_avatar_action_button'
5
+
6
+ const imageUrl = 'https://randomuser.me/api/portraits/women/8.jpg',
7
+ testId = 'scarden',
8
+ name = 'Sophia Carden',
9
+ imageAlt = 'Sophia Carden Profile'
10
+
11
+ test('loads the given image url and name', () => {
12
+ render(
13
+ <AvatarActionButton
14
+ data={{ testid: testId }}
15
+ imageAlt={imageAlt}
16
+ imageUrl={imageUrl}
17
+ linkAriaLabel={name}
18
+ name={name}
19
+ />
20
+ )
21
+
22
+ const kit = screen.getByTestId(testId)
23
+ const image = screen.getByAltText(imageAlt)
24
+ const link = kit.children[0]
25
+
26
+ expect(kit).toHaveClass('pb_avatar_action_button_kit_add_bottom_left_md')
27
+ expect(image).toHaveAttribute('data-src', imageUrl)
28
+ expect(image).toHaveAttribute('src', imageUrl)
29
+ expect(image).toHaveAttribute('alt', imageAlt)
30
+ expect(link).toHaveAttribute('aria-label', name)
31
+ })
@@ -3,6 +3,7 @@
3
3
  import React from 'react'
4
4
  import classnames from 'classnames'
5
5
  import { globalProps } from '../utilities/globalProps.js'
6
+ import { Icon } from '../'
6
7
 
7
8
  import {
8
9
  buildAriaProps,
@@ -13,8 +14,15 @@ import {
13
14
  type BadgeProps = {
14
15
  aria?: object,
15
16
  className?: string,
17
+ closeProps?: {
18
+ onClick?: EventHandler,
19
+ onMouseDown?: EventHandler,
20
+ onTouchEnd?: EventHandler,
21
+ },
16
22
  data?: object,
17
23
  id?: string,
24
+ removeIcon?: Boolean,
25
+ removeOnClick?: EventHandler,
18
26
  rounded?: boolean,
19
27
  text?: string,
20
28
  variant?: "error" | "info" | "neutral" | "primary" | "success" | "warning",
@@ -23,8 +31,11 @@ const Badge = (props: BadgeProps) => {
23
31
  const {
24
32
  aria = {},
25
33
  className,
34
+ closeProps = {},
26
35
  data = {},
27
36
  id,
37
+ removeIcon = false,
38
+ removeOnClick = () => {},
28
39
  rounded = false,
29
40
  text,
30
41
  variant = 'neutral',
@@ -44,7 +55,21 @@ const Badge = (props: BadgeProps) => {
44
55
  className={css}
45
56
  id={id}
46
57
  >
47
- <span>{text}</span>
58
+ <span>
59
+ {text}
60
+ <If condition={removeIcon}>
61
+ <span
62
+ onClick={removeOnClick}
63
+ style={{ cursor: 'pointer' }}
64
+ {...closeProps}
65
+ >
66
+ <Icon
67
+ fixedWidth
68
+ icon="times"
69
+ />
70
+ </span>
71
+ </If>
72
+ </span>
48
73
  </div>
49
74
  )
50
75
  }
@@ -12,7 +12,7 @@ type CaptionProps = {
12
12
  data?: object,
13
13
  id?: string,
14
14
  size?: "xs" | "sm" | "md" | "lg" | "xl",
15
- tag?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div",
15
+ tag?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div" | "caption",
16
16
  text?: string,
17
17
  variant?: null | "link",
18
18
  }
@@ -29,7 +29,8 @@ const Caption = (props: CaptionProps) => {
29
29
  text,
30
30
  variant = null,
31
31
  } = props
32
- const Tag = `${tag}`
32
+ const tagOptions = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'span', 'div', 'caption']
33
+ const Tag = tagOptions.includes(tag) ? tag : 'div'
33
34
 
34
35
  const ariaProps = buildAriaProps(aria)
35
36
  const dataProps = buildDataProps(data)
@@ -7,7 +7,7 @@ module Playbook
7
7
  values: %w[xs sm md base lg xl],
8
8
  default: "md"
9
9
  prop :tag, type: Playbook::Props::Enum,
10
- values: %w[h1 h2 h3 h4 h5 h6 p span div],
10
+ values: %w[h1 h2 h3 h4 h5 h6 p span div caption],
11
11
  default: "div"
12
12
  prop :text
13
13
  prop :variant, type: Playbook::Props::Enum,
@@ -3,14 +3,16 @@
3
3
  import React from 'react'
4
4
  import { get } from 'lodash'
5
5
  import classnames from 'classnames'
6
- import { buildCss } from '../utilities/props'
6
+ import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
7
7
  import { globalProps } from '../utilities/globalProps.js'
8
8
 
9
9
  type CardPropTypes = {
10
+ aria?: object,
10
11
  borderNone?: boolean,
11
12
  borderRadius?: "xs" | "sm" | "md" | "lg" | "xl" | "none" | "rounded",
12
13
  children: array<React.ReactNode> | React.ReactNode,
13
14
  className?: string,
15
+ data?: object,
14
16
  highlight?: {
15
17
  position?: "side" | "top",
16
18
  color?: string,
@@ -18,6 +20,7 @@ type CardPropTypes = {
18
20
  padding?: string,
19
21
  selected?: boolean,
20
22
  shadow?: "none" | "deep" | "deeper" | "deepest",
23
+ tag?: "div" | "section" | "footer" | "header" | "article" | "aside" | "main" | "nav",
21
24
  }
22
25
 
23
26
  type CardHeaderProps = {
@@ -62,13 +65,16 @@ const Body = (props: CardBodyProps) => {
62
65
 
63
66
  const Card = (props: CardPropTypes) => {
64
67
  const {
68
+ aria = {},
65
69
  borderNone = false,
66
70
  borderRadius = 'md',
67
71
  children,
68
72
  className,
73
+ data = {},
69
74
  highlight = {},
70
75
  selected = false,
71
76
  shadow = 'none',
77
+ tag = 'div',
72
78
  padding = 'md',
73
79
  } = props
74
80
  const borderCSS = borderNone == true ? 'border_none' : ''
@@ -78,6 +84,8 @@ const Card = (props: CardPropTypes) => {
78
84
  [`highlight_${highlight.position}`]: highlight.position,
79
85
  [`highlight_${highlight.color}`]: highlight.color,
80
86
  })
87
+ const ariaProps = buildAriaProps(aria)
88
+ const dataProps = buildDataProps(data)
81
89
 
82
90
  // coerce to array
83
91
  const cardChildren =
@@ -93,11 +101,18 @@ const Card = (props: CardPropTypes) => {
93
101
 
94
102
  const nonHeaderChildren = cardChildren.filter((child) => (get(child, 'type.displayName') !== 'Header'))
95
103
 
104
+ const tagOptions = ['div', 'section', 'footer', 'header', 'article', 'aside', 'main', 'nav']
105
+ const Tag = tagOptions.includes(tag) ? tag : 'div'
106
+
96
107
  return (
97
- <div className={classnames(cardCss, globalProps(props, { padding }), className)}>
108
+ <Tag
109
+ {...ariaProps}
110
+ {...dataProps}
111
+ className={classnames(cardCss, globalProps(props, { padding }), className)}
112
+ >
98
113
  {subComponentTags('Header')}
99
114
  {nonHeaderChildren}
100
- </div>
115
+ </Tag>
101
116
  )
102
117
  }
103
118
 
@@ -1,4 +1,4 @@
1
- <%= content_tag(:div,
1
+ <%= content_tag(object.tag,
2
2
  id: object.id,
3
3
  data: object.data,
4
4
  class: object.classname,
@@ -9,6 +9,9 @@ module Playbook
9
9
  default: "none"
10
10
  prop :highlight, type: Playbook::Props::Hash,
11
11
  default: {}
12
+ prop :tag, type: Playbook::Props::Enum,
13
+ values: %w[div section footer header article aside main nav],
14
+ default: "div"
12
15
  prop :border_none, type: Playbook::Props::Boolean,
13
16
  default: false
14
17
  prop :border_radius, type: Playbook::Props::Enum,
@@ -0,0 +1,25 @@
1
+ <%= pb_rails("card", props: { tag: "section" }) do %> section <% end %>
2
+
3
+ <br />
4
+
5
+ <%= pb_rails("card", props: { tag: "footer" }) do %> footer <% end %>
6
+
7
+ <br />
8
+
9
+ <%= pb_rails("card", props: { tag: "header" }) do %> header <% end %>
10
+
11
+ <br />
12
+
13
+ <%= pb_rails("card", props: { tag: "article" }) do %> article <% end %>
14
+
15
+ <br />
16
+
17
+ <%= pb_rails("card", props: { tag: "aside" }) do %> aside <% end %>
18
+
19
+ <br />
20
+
21
+ <%= pb_rails("card", props: { tag: "main" }) do %> main <% end %>
22
+
23
+ <br />
24
+
25
+ <%= pb_rails("card", props: { tag: "nav" }) do %> nav <% end %>
@@ -0,0 +1,59 @@
1
+ import React from 'react'
2
+ import { Card } from '../../'
3
+
4
+ const CardTag = (props) => {
5
+ return (
6
+ <div>
7
+ <Card
8
+ tag="section"
9
+ {...props}
10
+ >
11
+ {'section'}
12
+ </Card>
13
+ <br />
14
+ <Card
15
+ tag="footer"
16
+ {...props}
17
+ >
18
+ {'footer'}
19
+ </Card>
20
+ <br />
21
+ <Card
22
+ tag="header"
23
+ {...props}
24
+ >
25
+ {'header'}
26
+ </Card>
27
+ <br />
28
+ <Card
29
+ tag="article"
30
+ {...props}
31
+ >
32
+ {'article'}
33
+ </Card>
34
+ <br />
35
+ <Card
36
+ tag="aside"
37
+ {...props}
38
+ >
39
+ {'aside'}
40
+ </Card>
41
+ <br />
42
+ <Card
43
+ tag="main"
44
+ {...props}
45
+ >
46
+ {'main'}
47
+ </Card>
48
+ <br />
49
+ <Card
50
+ tag="nav"
51
+ {...props}
52
+ >
53
+ {'nav'}
54
+ </Card>
55
+ </div>
56
+ )
57
+ }
58
+
59
+ export default CardTag
@@ -4,6 +4,7 @@ examples:
4
4
  - card_highlight: Highlight Cards
5
5
  - card_header: Header Cards
6
6
  - card_selected: Selected
7
+ - card_tag: HTML Tag
7
8
  - card_padding: Padding Size
8
9
  - card_shadow: Shadow Size
9
10
  - card_content: Content Size
@@ -15,6 +16,7 @@ examples:
15
16
  - card_highlight: Highlight Cards
16
17
  - card_header: Header Cards
17
18
  - card_selected: Selected
19
+ - card_tag: HTML Tag
18
20
  - card_padding: Padding Size
19
21
  - card_shadow: Shadow Size
20
22
  - card_content: Content Size
@@ -2,6 +2,7 @@ export { default as CardLight } from './_card_light.jsx'
2
2
  export { default as CardHighlight } from './_card_highlight.jsx'
3
3
  export { default as CardHeader } from './_card_header.jsx'
4
4
  export { default as CardSelected } from './_card_selected.jsx'
5
+ export { default as CardTag } from './_card_tag.jsx'
5
6
  export { default as CardPadding } from './_card_padding.jsx'
6
7
  export { default as CardShadow } from './_card_shadow.jsx'
7
8
  export { default as CardContent } from './_card_content.jsx'
@@ -1,6 +1,6 @@
1
1
  /* @flow */
2
2
 
3
- import React from 'react'
3
+ import React, { useEffect, useRef } from 'react'
4
4
  import Body from '../pb_body/_body.jsx'
5
5
  import Icon from '../pb_icon/_icon.jsx'
6
6
  import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
@@ -16,6 +16,7 @@ type CheckboxProps = {
16
16
  data?: object,
17
17
  error?: boolean,
18
18
  id?: string,
19
+ indeterminate?: boolean,
19
20
  name: string,
20
21
  onChange: (boolean) => void,
21
22
  tabIndex: number,
@@ -33,6 +34,7 @@ const Checkbox = (props: CheckboxProps) => {
33
34
  data = {},
34
35
  error = false,
35
36
  id,
37
+ indeterminate = false,
36
38
  name = '',
37
39
  onChange = () => {},
38
40
  tabIndex,
@@ -40,14 +42,21 @@ const Checkbox = (props: CheckboxProps) => {
40
42
  value = '',
41
43
  } = props
42
44
 
45
+ const checkRef = useRef()
43
46
  const dataProps = buildDataProps(data)
44
47
  const ariaProps = buildAriaProps(aria)
45
48
  const classes = classnames(
46
- buildCss('pb_checkbox_kit', { checked, error }),
49
+ buildCss('pb_checkbox_kit', { checked, error, indeterminate }),
47
50
  globalProps(props),
48
51
  className
49
52
  )
50
53
 
54
+ useEffect(() => {
55
+ if (checkRef.current) {
56
+ checkRef.current.indeterminate = indeterminate
57
+ }
58
+ }, [indeterminate])
59
+
51
60
  return (
52
61
  <label
53
62
  {...ariaProps}
@@ -62,19 +71,32 @@ const Checkbox = (props: CheckboxProps) => {
62
71
  defaultChecked={checked}
63
72
  name={name}
64
73
  onChange={onChange}
74
+ ref={checkRef}
65
75
  tabIndex={tabIndex}
66
76
  type="checkbox"
67
77
  value={value}
68
78
  />
69
79
  </If>
80
+ <If condition={!indeterminate}>
81
+ <span className="pb_checkbox_checkmark">
82
+ <Icon
83
+ className="check_icon"
84
+ fixedWidth
85
+ icon="check"
86
+ />
87
+ </span>
88
+ </If>
89
+
90
+ <If condition={indeterminate}>
91
+ <span className="pb_checkbox_indeterminate">
92
+ <Icon
93
+ className="indeterminate_icon"
94
+ fixedWidth
95
+ icon="minus"
96
+ />
97
+ </span>
98
+ </If>
70
99
 
71
- <span className="pb_checkbox_checkmark">
72
- <Icon
73
- className="check_icon"
74
- fixedWidth
75
- icon="check"
76
- />
77
- </span>
78
100
  <Body
79
101
  className="pb_checkbox_label"
80
102
  dark={dark}