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

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.
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}