playbook_ui 11.4.0 → 11.5.0.pre.alpha.datepicker1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/data/menu.yml +0 -13
  3. data/app/pb_kits/playbook/pb_badge/badge.test.js +80 -0
  4. data/app/pb_kits/playbook/pb_button/_button.tsx +7 -2
  5. data/app/pb_kits/playbook/pb_button/_button_mixins.scss +7 -0
  6. data/app/pb_kits/playbook/pb_button/button.html.erb +11 -0
  7. data/app/pb_kits/playbook/pb_button/button.rb +3 -0
  8. data/app/pb_kits/playbook/pb_button/button.test.js +6 -8
  9. data/app/pb_kits/playbook/pb_button/docs/_button_block_content.html.erb +1 -1
  10. data/app/pb_kits/playbook/pb_button/docs/_button_block_content.jsx +12 -9
  11. data/app/pb_kits/playbook/pb_button/docs/_button_block_content.md +1 -0
  12. data/app/pb_kits/playbook/pb_button/docs/_button_icon_options.html.erb +2 -0
  13. data/app/pb_kits/playbook/pb_button/docs/_button_icon_options.jsx +23 -0
  14. data/app/pb_kits/playbook/pb_button/docs/_button_icon_options.md +1 -0
  15. data/app/pb_kits/playbook/pb_button/docs/example.yml +2 -0
  16. data/app/pb_kits/playbook/pb_button/docs/index.js +1 -0
  17. data/app/pb_kits/playbook/pb_date/_date.tsx +1 -1
  18. data/app/pb_kits/playbook/pb_date_picker/_date_picker.jsx +13 -1
  19. data/app/pb_kits/playbook/pb_date_picker/_date_picker.scss +24 -19
  20. data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +1 -1
  21. data/app/pb_kits/playbook/pb_date_picker/date_picker.rb +9 -0
  22. data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.js +25 -3
  23. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_positions.html.erb +44 -0
  24. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_positions.jsx +60 -0
  25. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_positions.md +9 -0
  26. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_positions_element.html.erb +33 -0
  27. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_positions_element.jsx +67 -0
  28. data/app/pb_kits/playbook/pb_date_picker/docs/example.yml +4 -0
  29. data/app/pb_kits/playbook/pb_date_picker/docs/index.js +2 -0
  30. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_flatpickr_styles.scss +0 -3
  31. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_overrides.scss +4 -4
  32. data/app/pb_kits/playbook/pb_date_range_inline/_date_range_inline.tsx +142 -0
  33. data/app/pb_kits/playbook/pb_date_range_inline/date_range_inline.test.js +116 -0
  34. data/app/pb_kits/playbook/pb_date_range_inline/docs/_date_range_inline_default.jsx +1 -1
  35. data/app/pb_kits/playbook/pb_date_time/{_date_time.jsx → _date_time.tsx} +2 -5
  36. data/app/pb_kits/playbook/pb_date_time/dateTime.test.js +110 -0
  37. data/app/pb_kits/playbook/pb_date_time/docs/_date_time_align.jsx +1 -1
  38. data/app/pb_kits/playbook/pb_date_time/docs/_date_time_default.jsx +1 -1
  39. data/app/pb_kits/playbook/pb_date_time/docs/_date_time_size.jsx +1 -1
  40. data/app/pb_kits/playbook/pb_date_year_stacked/{_date_year_stacked.jsx → _date_year_stacked.tsx} +6 -6
  41. data/app/pb_kits/playbook/pb_date_year_stacked/date_year_stacked.test.js +67 -0
  42. data/app/pb_kits/playbook/pb_dialog/_dialog.jsx +32 -14
  43. data/app/pb_kits/playbook/pb_dialog/_dialog.scss +5 -0
  44. data/app/pb_kits/playbook/pb_dialog/child_kits/_dialog_body.jsx +2 -2
  45. data/app/pb_kits/playbook/pb_dialog/child_kits/_dialog_footer.jsx +22 -4
  46. data/app/pb_kits/playbook/pb_dialog/child_kits/_dialog_header.jsx +3 -3
  47. data/app/pb_kits/playbook/pb_dialog/dialog.test.jsx +79 -17
  48. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_stacked_alert.jsx +55 -93
  49. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_status.jsx +79 -42
  50. data/app/pb_kits/playbook/pb_file_upload/{_file_upload.jsx → _file_upload.tsx} +6 -10
  51. data/app/pb_kits/playbook/pb_filter/Filter/FilterDouble.jsx +2 -0
  52. data/app/pb_kits/playbook/pb_filter/Filter/FilterSingle.jsx +2 -0
  53. data/app/pb_kits/playbook/pb_filter/Filter/FiltersPopover.jsx +2 -2
  54. data/app/pb_kits/playbook/pb_filter/docs/_filter_min_width.html.erb +1 -0
  55. data/app/pb_kits/playbook/pb_filter/docs/_filter_placement.html.erb +34 -0
  56. data/app/pb_kits/playbook/pb_filter/docs/_filter_placement.jsx +66 -0
  57. data/app/pb_kits/playbook/pb_filter/docs/_filter_placement.md +4 -0
  58. data/app/pb_kits/playbook/pb_filter/docs/example.yml +2 -0
  59. data/app/pb_kits/playbook/pb_filter/docs/index.js +1 -0
  60. data/app/pb_kits/playbook/pb_filter/filter.html.erb +2 -2
  61. data/app/pb_kits/playbook/pb_filter/filter.rb +3 -0
  62. data/app/pb_kits/playbook/pb_filter/filter.test.js +76 -0
  63. data/app/pb_kits/playbook/pb_form_group/{_form_group.jsx → _form_group.tsx} +1 -4
  64. data/app/pb_kits/playbook/pb_form_group/form_group.test.js +17 -0
  65. data/app/pb_kits/playbook/pb_hashtag/_hashtag.tsx +10 -1
  66. data/app/pb_kits/playbook/pb_hashtag/docs/_hashtag_link.html.erb +5 -0
  67. data/app/pb_kits/playbook/pb_hashtag/docs/_hashtag_link.jsx +26 -0
  68. data/app/pb_kits/playbook/pb_hashtag/docs/_hashtag_link.md +1 -0
  69. data/app/pb_kits/playbook/pb_hashtag/docs/example.yml +2 -0
  70. data/app/pb_kits/playbook/pb_hashtag/docs/index.js +1 -0
  71. data/app/pb_kits/playbook/pb_hashtag/hashtag.html.erb +1 -1
  72. data/app/pb_kits/playbook/pb_hashtag/hashtag.rb +6 -0
  73. data/app/pb_kits/playbook/pb_hashtag/hashtag.test.js +54 -0
  74. data/app/pb_kits/playbook/pb_home_address_street/_home_address_street.tsx +129 -0
  75. data/app/pb_kits/playbook/pb_home_address_street/city_emphasis.html.erb +2 -1
  76. data/app/pb_kits/playbook/pb_home_address_street/city_emphasis.rb +2 -0
  77. data/app/pb_kits/playbook/pb_home_address_street/docs/_home_address_street_default.jsx +1 -1
  78. data/app/pb_kits/playbook/pb_home_address_street/docs/_home_address_street_emphasis.jsx +2 -2
  79. data/app/pb_kits/playbook/pb_home_address_street/docs/_home_address_street_link.html.erb +12 -0
  80. data/app/pb_kits/playbook/pb_home_address_street/docs/_home_address_street_link.jsx +23 -0
  81. data/app/pb_kits/playbook/pb_home_address_street/docs/_home_address_street_link.md +1 -0
  82. data/app/pb_kits/playbook/pb_home_address_street/docs/example.yml +3 -0
  83. data/app/pb_kits/playbook/pb_home_address_street/docs/index.js +1 -0
  84. data/app/pb_kits/playbook/pb_home_address_street/home_address_street.rb +4 -0
  85. data/app/pb_kits/playbook/pb_home_address_street/home_adress_street.test.js +60 -0
  86. data/app/pb_kits/playbook/pb_home_address_street/street_emphasis.html.erb +2 -1
  87. data/app/pb_kits/playbook/pb_home_address_street/street_emphasis.rb +4 -1
  88. data/app/pb_kits/playbook/pb_icon_stat_value/{_icon_stat_value.jsx → _icon_stat_value.tsx} +2 -4
  89. data/app/pb_kits/playbook/pb_icon_stat_value/icon_stat_value.test.js +154 -0
  90. data/app/pb_kits/playbook/pb_icon_value/{_icon_value.jsx → _icon_value.tsx} +2 -4
  91. data/app/pb_kits/playbook/pb_icon_value/icon_value.test.js +77 -0
  92. data/app/pb_kits/playbook/pb_label_value/_label_value.tsx +123 -0
  93. data/app/pb_kits/playbook/pb_label_value/label_value.test.js +109 -0
  94. data/app/pb_kits/playbook/pb_layout/{_layout.jsx → _layout.tsx} +13 -19
  95. data/app/pb_kits/playbook/pb_layout/layout.test.js +97 -0
  96. data/app/pb_kits/playbook/pb_lightbox/lightbox.test.jsx +23 -15
  97. data/app/pb_kits/playbook/pb_popover/popover.rb +1 -1
  98. data/app/pb_kits/playbook/pb_text_input/_text_input.tsx +0 -1
  99. data/app/pb_kits/playbook/pb_time/_time.tsx +2 -2
  100. data/app/pb_kits/playbook/pb_timestamp/_timestamp.jsx +5 -2
  101. data/app/pb_kits/playbook/pb_timestamp/docs/_timestamp_elapsed.html.erb +9 -0
  102. data/app/pb_kits/playbook/pb_timestamp/docs/_timestamp_elapsed.jsx +10 -0
  103. data/app/pb_kits/playbook/pb_timestamp/timestamp.rb +4 -1
  104. data/app/pb_kits/playbook/pb_timestamp/timestamp.test.js +164 -0
  105. data/app/pb_kits/playbook/pb_tooltip/_tooltip.tsx +0 -4
  106. data/app/pb_kits/playbook/pb_tooltip/tooltip.test.jsx +11 -8
  107. data/app/pb_kits/playbook/tokens/_colors.scss +74 -74
  108. data/app/pb_kits/playbook/tokens/_typography.scss +8 -8
  109. data/app/pb_kits/playbook/utilities/_flexbox.scss +11 -11
  110. data/app/pb_kits/playbook/utilities/{_align_content.scss → flexbox_global_props/_align_content.scss} +0 -0
  111. data/app/pb_kits/playbook/utilities/{_align_items.scss → flexbox_global_props/_align_items.scss} +0 -0
  112. data/app/pb_kits/playbook/utilities/{_align_self.scss → flexbox_global_props/_align_self.scss} +0 -0
  113. data/app/pb_kits/playbook/utilities/{_flex.scss → flexbox_global_props/_flex.scss} +0 -0
  114. data/app/pb_kits/playbook/utilities/{_flex_direction.scss → flexbox_global_props/_flex_direction.scss} +0 -0
  115. data/app/pb_kits/playbook/utilities/{_flex_grow.scss → flexbox_global_props/_flex_grow.scss} +0 -0
  116. data/app/pb_kits/playbook/utilities/{_flex_shrink.scss → flexbox_global_props/_flex_shrink.scss} +0 -0
  117. data/app/pb_kits/playbook/utilities/{_flex_wrap.scss → flexbox_global_props/_flex_wrap.scss} +0 -0
  118. data/app/pb_kits/playbook/utilities/{_justify_content.scss → flexbox_global_props/_justify_content.scss} +0 -0
  119. data/app/pb_kits/playbook/utilities/{_justify_self.scss → flexbox_global_props/_justify_self.scss} +0 -0
  120. data/app/pb_kits/playbook/utilities/{_order.scss → flexbox_global_props/_order.scss} +0 -0
  121. data/app/pb_kits/playbook/utilities/props.ts +1 -1
  122. data/app/pb_kits/playbook/utilities/test-utils.js +2 -3
  123. data/app/pb_kits/playbook/utilities/text.ts +1 -1
  124. data/lib/playbook/version.rb +2 -2
  125. metadata +56 -25
  126. data/app/pb_kits/playbook/pb_date_range_inline/_date_range_inline.jsx +0 -155
  127. data/app/pb_kits/playbook/pb_home_address_street/_home_address_street.jsx +0 -124
  128. data/app/pb_kits/playbook/pb_label_value/_label_value.jsx +0 -155
@@ -0,0 +1,77 @@
1
+ import React from 'react'
2
+ import { render, screen } from '../utilities/test-utils'
3
+
4
+ import IconValue from './_icon_value'
5
+
6
+ const testId = "iconvalue-kit";
7
+
8
+ describe("IconValue Kit", () => {
9
+ test("renders IconValue classname", () => {
10
+ render(
11
+ <IconValue
12
+ data={{ testid: testId }}
13
+ icon="clipboard"
14
+ text="33-123456"
15
+ />
16
+ )
17
+
18
+ const kit = screen.getByTestId(testId)
19
+ expect(kit).toHaveClass("pb_icon_value_kit_left")
20
+ })
21
+
22
+ test("renders icon", () => {
23
+ render(
24
+ <IconValue
25
+ data={{ testid: testId }}
26
+ icon="clipboard"
27
+ text="33-123456"
28
+ />
29
+ )
30
+
31
+ const kit = screen.getByTestId(testId)
32
+ const icon = kit.querySelector(".fa-clipboard.pb_icon_kit.fa-fw")
33
+ expect(icon).toBeInTheDocument()
34
+ })
35
+
36
+ test("renders value", () => {
37
+ render(
38
+ <IconValue
39
+ data={{ testid: testId }}
40
+ icon="clipboard"
41
+ text="33-123456"
42
+ />
43
+ )
44
+
45
+ const kit = screen.getByTestId(testId)
46
+ const value = kit.querySelector(".pb_body_kit_light")
47
+ expect(value.textContent).toEqual("33-123456")
48
+ })
49
+
50
+ test("aligns content center", () => {
51
+ render(
52
+ <IconValue
53
+ align="center"
54
+ data={{ testid: testId }}
55
+ icon="clipboard"
56
+ text="33-123456"
57
+ />
58
+ )
59
+
60
+ const kit = screen.getByTestId(testId)
61
+ expect(kit).toHaveClass("pb_icon_value_kit_center")
62
+ })
63
+
64
+ test("aligns content right", () => {
65
+ render(
66
+ <IconValue
67
+ align="right"
68
+ data={{ testid: testId }}
69
+ icon="clipboard"
70
+ text="33-123456"
71
+ />
72
+ )
73
+
74
+ const kit = screen.getByTestId(testId)
75
+ expect(kit).toHaveClass("pb_icon_value_kit_right")
76
+ })
77
+ })
@@ -0,0 +1,123 @@
1
+ import React from "react";
2
+ import classnames from "classnames";
3
+ import DateTime from "../pb_kit/dateTime";
4
+ import { buildAriaProps, buildCss, buildDataProps } from "../utilities/props";
5
+ import { globalProps } from "../utilities/globalProps";
6
+
7
+ import Body from "../pb_body/_body";
8
+ import Caption from "../pb_caption/_caption";
9
+ import Flex from "../pb_flex/_flex";
10
+ import Icon from "../pb_icon/_icon";
11
+ import Title from "../pb_title/_title";
12
+
13
+ type LabelValueProps = {
14
+ active?: boolean;
15
+ aria?: { [key: string]: string };
16
+ className?: string;
17
+ dark?: boolean;
18
+ data?: object;
19
+ date?: Date;
20
+ id?: string;
21
+ label: string;
22
+ value?: string;
23
+ variant?: "default" | "details";
24
+ icon?: string;
25
+ description?: string;
26
+ title?: string;
27
+ };
28
+
29
+ const dateString = (value: DateTime) => {
30
+ const month = value.toMonthNum();
31
+ const day = value.toDay();
32
+
33
+ return ` · ${month}/${day}`;
34
+ };
35
+
36
+ const LabelValue = (props: LabelValueProps) => {
37
+ const {
38
+ active = false,
39
+ aria = {},
40
+ className,
41
+ dark = false,
42
+ data = {},
43
+ date,
44
+ description,
45
+ icon,
46
+ id,
47
+ label,
48
+ title,
49
+ value,
50
+ variant = "default",
51
+ } = props;
52
+
53
+ const ariaProps = buildAriaProps(aria);
54
+ const dataProps = buildDataProps(data);
55
+ const formattedDate = new DateTime({ value: date });
56
+ const variantClass = variant === "details" ? "details" : "";
57
+ const classes = classnames(
58
+ buildCss("pb_label_value_kit", variantClass),
59
+ globalProps(props),
60
+ className
61
+ );
62
+
63
+ return (
64
+ <div
65
+ {...ariaProps}
66
+ {...dataProps}
67
+ className={classes}
68
+ id={id}
69
+ title={title}
70
+ >
71
+ <Caption dark={dark} text={label} />
72
+ {variant === "details" ? (
73
+ <Flex inline vertical="center">
74
+ {icon && (
75
+ <Body color="light" dark={dark} marginRight="xs">
76
+ <Icon dark={dark} fixedWidth icon={icon} />
77
+ </Body>
78
+ )}
79
+ {description && (
80
+ <Body
81
+ color="light"
82
+ dark={dark}
83
+ marginRight="xs"
84
+ text={description}
85
+ />
86
+ )}
87
+ {active === true ? (
88
+ <Flex inline vertical="center">
89
+ {title && (
90
+ <Title dark={dark} size={4} text={title} variant="link" />
91
+ )}
92
+ {date && (
93
+ <Title
94
+ dark={dark}
95
+ marginLeft="xs"
96
+ size={4}
97
+ text={" " + dateString(formattedDate)}
98
+ variant="link"
99
+ />
100
+ )}
101
+ </Flex>
102
+ ) : (
103
+ <>
104
+ {title && <Title dark={dark} size={4} text={title} />}
105
+ {date && (
106
+ <Title
107
+ dark={dark}
108
+ marginLeft="xs"
109
+ size={4}
110
+ text={" " + dateString(formattedDate)}
111
+ />
112
+ )}
113
+ </>
114
+ )}
115
+ </Flex>
116
+ ) : (
117
+ <Body dark={dark} text={value} />
118
+ )}
119
+ </div>
120
+ );
121
+ };
122
+
123
+ export default LabelValue;
@@ -0,0 +1,109 @@
1
+ import React from 'react'
2
+ import { render, screen } from '../utilities/test-utils'
3
+
4
+ import LabelValue from './_label_value'
5
+
6
+ const testId = "labelvalue-kit";
7
+ jest.useFakeTimers()
8
+
9
+ describe("LabelValue Kit", () => {
10
+ test("renders LabelValue classname", () => {
11
+ render(
12
+ <LabelValue
13
+ data={{ testid: testId }}
14
+ label="Role"
15
+ value="Administrator, Moderator"
16
+ />
17
+ )
18
+ const kit = screen.getByTestId(testId)
19
+ expect(kit).toHaveClass("pb_label_value_kit")
20
+ })
21
+
22
+ test("renders label correctly", () => {
23
+ render(
24
+ <LabelValue
25
+ data={{ testid: testId }}
26
+ label="Role"
27
+ value="Administrator, Moderator"
28
+ />
29
+ )
30
+ const kit = screen.getByTestId(testId)
31
+ const text = kit.querySelector(".pb_caption_kit_md")
32
+ expect(text.textContent).toEqual("Role")
33
+ })
34
+
35
+ test("renders value correctly", () => {
36
+ render(
37
+ <LabelValue
38
+ data={{ testid: testId }}
39
+ label="Role"
40
+ value="Administrator, Moderator"
41
+ />
42
+ )
43
+ const kit = screen.getByTestId(testId)
44
+ const text = kit.querySelector(".pb_body_kit")
45
+ expect(text.textContent).toEqual("Administrator, Moderator")
46
+ })
47
+
48
+ test("renders icon if included with details variant", () => {
49
+ render(
50
+ <LabelValue
51
+ data={{ testid: testId }}
52
+ icon="truck"
53
+ label="Installer"
54
+ title="JD Installations LLC"
55
+ variant="details"
56
+ />
57
+ )
58
+ const kit = screen.getByTestId(testId)
59
+ const icon = kit.querySelector(".fa-truck.pb_icon_kit.fa-fw")
60
+ expect(icon).toBeInTheDocument()
61
+ })
62
+
63
+ test("renders title if included with details variant", () => {
64
+ render(
65
+ <LabelValue
66
+ data={{ testid: testId }}
67
+ icon="truck"
68
+ label="Installer"
69
+ title="JD Installations LLC"
70
+ variant="details"
71
+ />
72
+ )
73
+ const kit = screen.getByTestId(testId)
74
+ const text = kit.querySelector(".pb_title_kit_size_4")
75
+ expect(text.textContent).toEqual("JD Installations LLC")
76
+ })
77
+
78
+ test("renders date if included with details variant", () => {
79
+ render(
80
+ <LabelValue
81
+ data={{ testid: testId }}
82
+ date={new Date('18 Nov 2019')}
83
+ description="33-12345"
84
+ icon="home"
85
+ label="Project"
86
+ title="Jefferson-Smith"
87
+ variant="details"
88
+ />
89
+ )
90
+ const text = screen.getByText("11/18", {exact: false})
91
+ expect(text).toBeInTheDocument()
92
+ })
93
+
94
+ test("renders title if included with details variant", () => {
95
+ render(
96
+ <LabelValue
97
+ active
98
+ data={{ testid: testId }}
99
+ icon="truck"
100
+ label="Installer"
101
+ title="JD Installations LLC"
102
+ variant="details"
103
+ />
104
+ )
105
+ const kit = screen.getByTestId(testId)
106
+ const activeProp = kit.querySelector(".pb_title_kit_size_4_link")
107
+ expect(activeProp).toBeInTheDocument()
108
+ })
109
+ })
@@ -1,5 +1,3 @@
1
- /* @flow */
2
-
3
1
  import React from 'react'
4
2
  import classnames from 'classnames'
5
3
  import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
@@ -7,8 +5,8 @@ import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
7
5
  import { globalProps } from '../utilities/globalProps'
8
6
 
9
7
  type LayoutPropTypes = {
10
- aria?: object,
11
- children?: array<React.ReactNode> | React.ReactNode,
8
+ aria?: {[key: string]: string},
9
+ children?: React.ReactChild[] | React.ReactChild,
12
10
  className?: string,
13
11
  collapse?: "xs" | "sm" | "md" | "lg" | "xl",
14
12
  dark?: boolean,
@@ -23,28 +21,28 @@ type LayoutPropTypes = {
23
21
  }
24
22
 
25
23
  type LayoutSideProps = {
26
- children: array<React.ReactNode> | React.ReactNode,
24
+ children: React.ReactNode[] | React.ReactNode,
27
25
  className?: string,
28
26
  }
29
27
 
30
28
  type LayoutBodyProps = {
31
- children: array<React.ReactNode> | React.ReactNode,
29
+ children: React.ReactNode[] | React.ReactNode,
32
30
  className?: string,
33
31
  }
34
32
 
35
33
  type LayoutItemProps = {
36
- children: array<React.ReactNode> | React.ReactNode,
34
+ children: React.ReactNode[] | React.ReactNode,
37
35
  className?: string,
38
36
  size?: "sm" | "md" | "lg"
39
37
  }
40
38
 
41
39
  type LayoutHeaderProps = {
42
- children: array<React.ReactNode> | React.ReactNode,
40
+ children: React.ReactNode[] | React.ReactNode,
43
41
  className?: string,
44
42
  }
45
43
 
46
44
  type LayoutFooterProps = {
47
- children: array<React.ReactNode> | React.ReactNode,
45
+ children: React.ReactNode[] | React.ReactNode,
48
46
  className?: string,
49
47
  }
50
48
 
@@ -99,8 +97,6 @@ const Footer = (props: LayoutFooterProps) => {
99
97
  )
100
98
  }
101
99
 
102
- // Main componenet
103
-
104
100
  const Layout = (props: LayoutPropTypes) => {
105
101
  const {
106
102
  aria = {},
@@ -138,25 +134,23 @@ const Layout = (props: LayoutPropTypes) => {
138
134
  ? ''
139
135
  : buildCss('layout', position, 'collapse', collapse)
140
136
 
141
- const layoutChildren =
142
- typeof children === 'object' && children.length ? children : [children]
137
+ const layoutChildren = React.Children.toArray(children)
143
138
 
144
- const subComponentTags = (tagName) => {
139
+ const subComponentTags = (tagName: string) => {
145
140
  return layoutChildren
146
- .filter((c) => {
147
- return c.type && c.type.displayName === tagName
141
+ .filter((c: React.ReactElement & {type: {displayName: string}}) => {
142
+ return c.type?.displayName === tagName
148
143
  })
149
144
  .map((child, i) => {
150
- return React.cloneElement(child, {
145
+ return React.cloneElement(child as React.ReactElement, {
151
146
  key: `${tagName.toLowerCase()}-${i}`,
152
147
  })
153
148
  })
154
149
  }
155
150
 
156
151
  const nonSideChildren = layoutChildren.filter(
157
- (child) => !child.type || child.type.displayName !== 'Side'
152
+ (child: React.ReactElement & {type: {displayName: string}}) => child.type?.displayName !== 'Side'
158
153
  )
159
-
160
154
  return (
161
155
  <div
162
156
  {...ariaProps}
@@ -0,0 +1,97 @@
1
+ import React from "react"
2
+ import { render, cleanup } from "../utilities/test-utils"
3
+ import { Layout, Card } from ".."
4
+
5
+ function LayoutTest(props) {
6
+ return (
7
+ <Layout {...props}>
8
+ <Layout.Side>{"Light"}</Layout.Side>
9
+ <Layout.Body>{"Body"}</Layout.Body>
10
+ </Layout>
11
+ )
12
+ }
13
+
14
+ test("render all color variants", () => {
15
+ const testValues = [undefined, "light", "dark", "gradient"]
16
+ testValues.forEach((variant) => {
17
+ const { getByTestId } = render(
18
+ <LayoutTest data={{ testid: `test-${variant}` }}
19
+ variant={variant} />
20
+ )
21
+ expect(getByTestId(`test-${variant}`)).toHaveClass(
22
+ `pb_layout_kit_sidebar_size_md_left_${
23
+ variant == undefined ? "light" : variant
24
+ }`
25
+ )
26
+
27
+ cleanup()
28
+ })
29
+ })
30
+
31
+ test("render transparent class", () => {
32
+ const id = "transparent"
33
+
34
+ const { getByTestId } = render(
35
+ <LayoutTest data={{ testid: `test-${id}` }}
36
+ variant={id} />
37
+ )
38
+ expect(getByTestId(`test-${id}`)).toHaveClass(
39
+ `pb_layout_kit_sidebar_size_md_left_${id}`
40
+ )
41
+
42
+ cleanup()
43
+ })
44
+
45
+ test("render all sizes variants", () => {
46
+ const testValues = ["xs", "sm", "md", "lg", "xl"]
47
+ testValues.forEach((size) => {
48
+ const { getByTestId } = render(
49
+ <LayoutTest data={{ testid: `test-${size}` }}
50
+ size={size} />
51
+ )
52
+ expect(getByTestId(`test-${size}`)).toHaveClass(
53
+ `pb_layout_kit_sidebar_size_${size}_left_light`
54
+ )
55
+
56
+ cleanup()
57
+ })
58
+ })
59
+
60
+ test("render all layout variants", () => {
61
+ const testValues = [
62
+ {
63
+ layout: "collection",
64
+ expected: "pb_layout_kit_collection",
65
+ },
66
+ {
67
+ layout: "collection_detail",
68
+ expected: "pb_layout_kit_collection_detail_size_md_left_light",
69
+ },
70
+ {
71
+ layout: "content",
72
+ expected: "pb_layout_kit_content_size_md_left_light",
73
+ },
74
+ {
75
+ layout: "kanban",
76
+ expected: "pb_layout_kit_kanban",
77
+ },
78
+ {
79
+ layout: "masonry",
80
+ expected: "pb_layout_kit_masonry_size_md_left_light",
81
+ },
82
+ ]
83
+
84
+ testValues.forEach(({ layout, expected }) => {
85
+ const { getByTestId } = render(
86
+ <Layout data={{ testid: `test-${layout}` }}
87
+ layout={layout}>
88
+ <Layout.Body>
89
+ <Card>{"Card content"}</Card>
90
+ </Layout.Body>
91
+ </Layout>
92
+ )
93
+
94
+ expect(getByTestId(`test-${layout}`)).toHaveClass(expected)
95
+ cleanup()
96
+ })
97
+ })
@@ -1,5 +1,5 @@
1
1
  import React from 'react'
2
- import { fireEvent, render, screen, waitForElementToBeRemoved } from '../utilities/test-utils'
2
+ import { cleanup, fireEvent, render, screen } from '../utilities/test-utils'
3
3
 
4
4
  import { Lightbox } from '../'
5
5
 
@@ -27,6 +27,7 @@ test('Kit renders', () => {
27
27
  const kit = screen.getByTestId(testId)
28
28
  expect(kit).toHaveClass(`${kitClass} customClass`)
29
29
  expect(kit).toBeInTheDocument()
30
+ cleanup()
30
31
  })
31
32
 
32
33
  test('Shows selected images', () => {
@@ -67,9 +68,12 @@ test('Shows selected images', () => {
67
68
  )
68
69
 
69
70
  expect(image).toHaveAttribute('src', TEST_PHOTOS[0])
71
+ cleanup()
70
72
  })
71
73
 
72
74
  test('Closes on escape key', async () => {
75
+ const mockClose = jest.fn()
76
+
73
77
  render(
74
78
  <Lightbox
75
79
  data={{ testid: testId }}
@@ -77,25 +81,29 @@ test('Closes on escape key', async () => {
77
81
  iconSize="3x"
78
82
  id="test1"
79
83
  initialPhoto={0}
80
- onClose={() => {}}
84
+ onClose={mockClose}
81
85
  photos={TEST_PHOTOS}
82
86
  />
83
87
  )
84
88
 
85
- const kit = screen.getByTestId(testId)
86
-
87
- fireEvent(
88
- document.body,
89
- new KeyboardEvent('keydown', {
90
- keyCode: 27, //escape
91
- })
89
+ fireEvent.keyDown(
90
+ global.window,
91
+ {
92
+ bubbles: true,
93
+ key: "Escape",
94
+ code: "Escape",
95
+ keyCode: 27,
96
+ charCode: 27
97
+ }
92
98
  )
93
99
 
94
- waitForElementToBeRemoved(kit)
95
- .then(() => expect(kit).not.toBeInTheDocument())
100
+ expect(mockClose).toHaveBeenCalled()
101
+ cleanup()
96
102
  })
97
103
 
98
- test('Closes on close button', () => {
104
+ test('Closes on close button', async () => {
105
+ const mockClose = jest.fn()
106
+
99
107
  render(
100
108
  <Lightbox
101
109
  data={{ testid: testId }}
@@ -103,7 +111,7 @@ test('Closes on close button', () => {
103
111
  iconSize="3x"
104
112
  id="test1"
105
113
  initialPhoto={0}
106
- onClose={() => {}}
114
+ onClose={mockClose}
107
115
  photos={TEST_PHOTOS}
108
116
  />
109
117
  )
@@ -119,6 +127,6 @@ test('Closes on close button', () => {
119
127
  }),
120
128
  )
121
129
 
122
- waitForElementToBeRemoved(kit)
123
- .then(() => expect(kit).not.toBeInTheDocument())
130
+ expect(mockClose).toHaveBeenCalled()
131
+ cleanup()
124
132
  })
@@ -4,7 +4,7 @@ module Playbook
4
4
  module PbPopover
5
5
  class Popover < Playbook::KitBase
6
6
  prop :position, type: Playbook::Props::Enum,
7
- values: %w[top bottom left right],
7
+ values: %w[top bottom left right top-start top-end bottom-start bottom-end right-start right-end left-start left-end],
8
8
  default: "left"
9
9
  prop :trigger_element_id
10
10
  prop :tooltip_id
@@ -103,7 +103,6 @@ const TextInput = (props: TextInputProps, ref: React.LegacyRef<HTMLInputElement>
103
103
  <React.Fragment>
104
104
  <Flex
105
105
  className={`add-on-${addOnAlignment} ${borderCss}`}
106
- inline
107
106
  vertical="center"
108
107
  >
109
108
  {addOnAlignment == 'left' && <>
@@ -3,7 +3,7 @@ import classnames from "classnames";
3
3
 
4
4
  import DateTime from "../pb_kit/dateTime";
5
5
  import { buildCss } from "../utilities/props";
6
- import { globalProps } from "../utilities/globalProps";
6
+ import { globalProps, GlobalProps } from "../utilities/globalProps";
7
7
 
8
8
  import Body from "../pb_body/_body";
9
9
  import Caption from "../pb_caption/_caption";
@@ -20,7 +20,7 @@ type TimeProps = {
20
20
  size?: "md" | "sm";
21
21
  showTimezone?: boolean;
22
22
  timeZone?: string;
23
- };
23
+ } & GlobalProps
24
24
 
25
25
  const Time = (props: TimeProps) => {
26
26
  const {
@@ -21,6 +21,7 @@ type TimestampProps = {
21
21
  id?: string,
22
22
  showDate?: boolean,
23
23
  showUser?: boolean,
24
+ hideUpdated?: boolean,
24
25
  showTimezone?: boolean,
25
26
  variant?: "default" | "elapsed" | "updated"
26
27
  }
@@ -37,6 +38,7 @@ const Timestamp = (props: TimestampProps) => {
37
38
  timezone,
38
39
  showDate = true,
39
40
  showUser = false,
41
+ hideUpdated = false,
40
42
  showTimezone = false,
41
43
  variant = 'default',
42
44
  } = props
@@ -57,6 +59,7 @@ const Timestamp = (props: TimestampProps) => {
57
59
  const dateDisplay = `${dateTimestamp.toMonth()} ${dateTimestamp.toDay()}`
58
60
  const shouldShowUser = showUser == true && text.length > 0
59
61
  const shouldShowTimezone = showTimezone == true && timezone.length > 0
62
+ const updatedText = hideUpdated ? "" : "Last updated"
60
63
  const userDisplay = shouldShowUser ? ` by ${text}` : ''
61
64
 
62
65
  let timeDisplay = `${dateTimestamp.toHour()}:${dateTimestamp.toMinute()}${dateTimestamp.toMeridian()}`
@@ -81,7 +84,7 @@ const Timestamp = (props: TimestampProps) => {
81
84
  }
82
85
 
83
86
  const formatElapsedString = () => {
84
- return `Last updated ${userDisplay} ${dateTimestamp.value.fromNow()}`
87
+ return `${updatedText} ${userDisplay} ${dateTimestamp.value.fromNow()}`
85
88
  }
86
89
 
87
90
  const captionText = () => {
@@ -89,7 +92,7 @@ const Timestamp = (props: TimestampProps) => {
89
92
  case 'updated':
90
93
  return formatUpdatedString(userDisplay, dateTimestamp)
91
94
  case 'elapsed':
92
- return formatElapsedString(userDisplay, timeDisplay)
95
+ return formatElapsedString(userDisplay, timeDisplay, updatedText)
93
96
  default:
94
97
  return showDate ? timestamp ? fullDateDisplay() : text : fullTimeDisplay()
95
98
  }
@@ -12,3 +12,12 @@
12
12
  variant: "elapsed",
13
13
  show_user: false
14
14
  }) %>
15
+
16
+ <br>
17
+
18
+ <%= pb_rails("timestamp", props: {
19
+ timestamp: DateTime.now,
20
+ variant: "elapsed",
21
+ show_user: false,
22
+ hide_updated: true
23
+ }) %>
@@ -28,6 +28,16 @@ const TimestampElapsed = (props) => {
28
28
  variant="elapsed"
29
29
  {...props}
30
30
  />
31
+
32
+ <br />
33
+
34
+ <Timestamp
35
+ hideUpdated
36
+ showUser={false}
37
+ timestamp={customDate}
38
+ variant="elapsed"
39
+ {...props}
40
+ />
31
41
  </div>
32
42
  )
33
43
  }