playbook_ui 14.8.0.pre.rc.3 → 14.8.0.pre.rc.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/pb_kits/playbook/pb_form/docs/_form_form_with.html.erb +2 -2
- data/app/pb_kits/playbook/pb_form/docs/_form_form_with_loading.html.erb +1 -1
- data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +63 -12
- data/app/pb_kits/playbook/pb_timeline/_timeline.scss +43 -1
- data/app/pb_kits/playbook/pb_timeline/_timeline.tsx +6 -2
- data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_gap.html.erb +94 -0
- data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_gap.jsx +180 -0
- data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_gap.md +1 -0
- data/app/pb_kits/playbook/pb_timeline/docs/example.yml +5 -3
- data/app/pb_kits/playbook/pb_timeline/docs/index.js +2 -0
- data/app/pb_kits/playbook/pb_timeline/timeline.rb +11 -1
- data/app/pb_kits/playbook/pb_timeline/timeline.test.js +4 -4
- data/app/pb_kits/playbook/pb_typeahead/index.ts +36 -2
- data/app/pb_kits/playbook/pb_typeahead/typeahead.html.erb +5 -2
- data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +4 -0
- data/dist/chunks/{_typeahead-CFuWkfWK.js → _typeahead-DEskmS_o.js} +1 -1
- data/dist/chunks/{_weekday_stacked-OvN7ix-I.js → _weekday_stacked-f4faf8xD.js} +2 -2
- data/dist/chunks/{lib-D-mTv-kp.js → lib-DpxYMiKe.js} +1 -1
- data/dist/chunks/{pb_form_validation-BkWGwJsl.js → pb_form_validation-LqRlnmi6.js} +1 -1
- data/dist/chunks/vendor.js +1 -1
- data/dist/playbook-doc.js +1 -1
- data/dist/playbook-rails-react-bindings.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/dist/playbook.css +1 -1
- data/lib/playbook/forms/builder/typeahead_field.rb +13 -0
- data/lib/playbook/version.rb +1 -1
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8af7fb78b456740d828272224ea20658cd7b15a6bd564b38ed4b3d46081e7616
|
4
|
+
data.tar.gz: fb77b67764cb414eed88eea74b1af9a811526b9950d86d59fca06ab8e601bb14
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f686b855c98185a49feae8305793bb632c5abb7289b6d9b3f63263abe36469c1ce1f2f6a06e749750b42bc33f9361986730983ddcf0bcb675ccbc54c42260ffc
|
7
|
+
data.tar.gz: 28d6880206326fdb1b290ba613317d8c86797053a97694a32ca3d6c60708a0c6d1190d3b96c5051c0ee9aeab3ef2a8e1fe5f44fec39b50a3a6e7a46aef944c37
|
@@ -23,7 +23,7 @@
|
|
23
23
|
%>
|
24
24
|
|
25
25
|
<%= pb_form_with(scope: :example, url: "", method: :get) do |form| %>
|
26
|
-
<%= form.typeahead :
|
26
|
+
<%= form.typeahead :example_typeahead, props: { data: { typeahead_example1: true, user: {} }, label: true, placeholder: "Search for a user" } %>
|
27
27
|
<%= form.text_field :example_text_field, props: { label: true } %>
|
28
28
|
<%= form.phone_number_field :example_phone_number_field, props: { label: "Example phone field" } %>
|
29
29
|
<%= form.email_field :example_email_field, props: { label: true } %>
|
@@ -92,7 +92,7 @@
|
|
92
92
|
const selectedUserData = JSON.parse(selectedUserJSON)
|
93
93
|
|
94
94
|
// set the input field's value
|
95
|
-
event.target.querySelector('input[name=
|
95
|
+
event.target.querySelector('input[name=example_typeahead]').value = selectedUserData.login
|
96
96
|
|
97
97
|
// log the selected option's dataset
|
98
98
|
console.log('The selected user data:')
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<%= pb_form_with(scope: :example, url: "", method: :get, loading: true) do |form| %>
|
2
|
-
<%= form.text_field :
|
2
|
+
<%= form.text_field :example_text_field_loading, props: { label: true } %>
|
3
3
|
|
4
4
|
<%= form.actions do |action| %>
|
5
5
|
<%= action.submit %>
|
@@ -22,23 +22,74 @@
|
|
22
22
|
%>
|
23
23
|
|
24
24
|
<%= pb_form_with(scope: :example, method: :get, url: "", validate: true) do |form| %>
|
25
|
-
<%= form.
|
26
|
-
<%= form.
|
27
|
-
<%= form.
|
28
|
-
<%= form.
|
29
|
-
<%= form.
|
30
|
-
<%= form.
|
31
|
-
<%= form.
|
32
|
-
<%= form.
|
33
|
-
<%= form.
|
34
|
-
<%= form.
|
35
|
-
<%= form.
|
25
|
+
<%= form.typeahead :example_typeahead_validation, props: { data: { typeahead_example2: true, user: {} }, label: true, placeholder: "Search for a user", required: true, validation: { message: "Please select a user." } } %>
|
26
|
+
<%= form.text_field :example_text_field_validation, props: { label: true, required: true } %>
|
27
|
+
<%= form.phone_number_field :example_phone_number_field_validation, props: { label: "Example phone field" } %>
|
28
|
+
<%= form.email_field :example_email_field_validation, props: { label: true, required: true } %>
|
29
|
+
<%= form.number_field :example_number_field_validation, props: { label: true, required: true } %>
|
30
|
+
<%= form.search_field :example_project_number_validation, props: { label: true, required: true, validation: { pattern: "[0-9]{2}-[0-9]{5}", message: "Please enter a valid project number (example: 33-12345)." } } %>
|
31
|
+
<%= form.password_field :example_password_field_validation, props: { label: true, required: true } %>
|
32
|
+
<%= form.url_field :example_url_field_validation, props: { label: true, required: true } %>
|
33
|
+
<%= form.text_area :example_text_area_validation, props: { label: true, required: true } %>
|
34
|
+
<%= form.dropdown_field :example_dropdown_validation, props: { label: true, options: example_dropdown_options, required: true } %>
|
35
|
+
<%= form.select :example_select_validation, [ ["Yes", 1], ["No", 2] ], props: { label: true, blank_selection: "Select One...", required: true } %>
|
36
|
+
<%= form.collection_select :example_collection_select_validation, example_collection, :value, :name, props: { label: true, blank_selection: "Select One...", required: true } %>
|
36
37
|
<%= form.check_box :example_checkbox, props: { text: "Example Checkbox", label: true, required: true } %>
|
37
38
|
<%= form.date_picker :example_date_picker_2, props: { label: true, required: true } %>
|
38
|
-
<%= form.star_rating_field :
|
39
|
+
<%= form.star_rating_field :example_star_rating_validation, props: { variant: "interactive", label: true, required: true } %>
|
39
40
|
|
40
41
|
<%= form.actions do |action| %>
|
41
42
|
<%= action.submit %>
|
42
43
|
<%= action.button props: { type: "reset", text: "Cancel", variant: "secondary" } %>
|
43
44
|
<% end %>
|
44
45
|
<% end %>
|
46
|
+
|
47
|
+
<!-- form.typeahead user results example template -->
|
48
|
+
<template data-typeahead-example-result-option>
|
49
|
+
<%= pb_rails("user", props: {
|
50
|
+
name: tag(:slot, name: "name"),
|
51
|
+
orientation: "horizontal",
|
52
|
+
align: "left",
|
53
|
+
avatar_url: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGP6zwAAAgcBApocMXEAAAAASUVORK5CYII=",
|
54
|
+
avatar: true
|
55
|
+
}) %>
|
56
|
+
</template>
|
57
|
+
|
58
|
+
<!-- form.typeahead JS example implementation -->
|
59
|
+
<%= javascript_tag defer: "defer" do %>
|
60
|
+
document.addEventListener("pb-typeahead-kit-search", function(event) {
|
61
|
+
if (!event.target.dataset || !event.target.dataset.typeaheadExample2) return
|
62
|
+
|
63
|
+
fetch(`https://api.github.com/search/users?q=${encodeURIComponent(event.detail.searchingFor)}`)
|
64
|
+
.then(response => response.json())
|
65
|
+
.then((result) => {
|
66
|
+
const resultOptionTemplate = document.querySelector("[data-typeahead-example-result-option]")
|
67
|
+
|
68
|
+
event.detail.setResults((result.items || []).map((user) => {
|
69
|
+
const wrapper = resultOptionTemplate.content.cloneNode(true)
|
70
|
+
wrapper.children[0].dataset.user = JSON.stringify(user)
|
71
|
+
wrapper.querySelector('slot[name="name"]').replaceWith(user.login)
|
72
|
+
wrapper.querySelector('img').dataset.src = user.avatar_url
|
73
|
+
return wrapper
|
74
|
+
}))
|
75
|
+
})
|
76
|
+
})
|
77
|
+
|
78
|
+
|
79
|
+
document.addEventListener("pb-typeahead-kit-result-option-selected", function(event) {
|
80
|
+
if (!event.target.dataset.typeaheadExample2) return
|
81
|
+
|
82
|
+
const selectedUserJSON = event.detail.selected.firstElementChild.dataset.user
|
83
|
+
const selectedUserData = JSON.parse(selectedUserJSON)
|
84
|
+
|
85
|
+
// set the input field's value
|
86
|
+
event.target.querySelector('input[name=example_typeahead_validation]').value = selectedUserData.login
|
87
|
+
|
88
|
+
// log the selected option's dataset
|
89
|
+
console.log('The selected user data:')
|
90
|
+
console.dir(selectedUserData)
|
91
|
+
|
92
|
+
// do even more with the data later - TBD
|
93
|
+
event.target.dataset.user = selectedUserJSON
|
94
|
+
})
|
95
|
+
<% end %>
|
@@ -8,6 +8,12 @@ $icon_margin: $space_xs/2;
|
|
8
8
|
$icon_height: 28px;
|
9
9
|
$height_from_top: $icon_height/2 - $connector_width/2;
|
10
10
|
|
11
|
+
// Add gap variables
|
12
|
+
$gap_xs: $height_from_top + $space_xs;
|
13
|
+
$gap_sm: $height_from_top + $space_sm;
|
14
|
+
$gap_md: $height_from_top + $space_md;
|
15
|
+
$gap_lg: $height_from_top + $space_lg;
|
16
|
+
|
11
17
|
@mixin pb_timeline_line_solid($width, $height, $margin) {
|
12
18
|
width: $width;
|
13
19
|
height: $height;
|
@@ -156,7 +162,7 @@ $height_from_top: $icon_height/2 - $connector_width/2;
|
|
156
162
|
}
|
157
163
|
}
|
158
164
|
}
|
159
|
-
}
|
165
|
+
}
|
160
166
|
}
|
161
167
|
}
|
162
168
|
&[class*=_vertical] {
|
@@ -266,5 +272,41 @@ $height_from_top: $icon_height/2 - $connector_width/2;
|
|
266
272
|
}
|
267
273
|
}
|
268
274
|
}
|
275
|
+
&[class*=_gap_xs] {
|
276
|
+
[class*=pb_timeline_item_kit] {
|
277
|
+
[class=pb_timeline_item_step] {
|
278
|
+
[class=pb_timeline_item_connector] {
|
279
|
+
height: $gap_xs !important;
|
280
|
+
}
|
281
|
+
}
|
282
|
+
}
|
283
|
+
}
|
284
|
+
&[class*=_gap_sm] {
|
285
|
+
[class*=pb_timeline_item_kit] {
|
286
|
+
[class=pb_timeline_item_step] {
|
287
|
+
[class=pb_timeline_item_connector] {
|
288
|
+
height: $gap_sm !important;
|
289
|
+
}
|
290
|
+
}
|
291
|
+
}
|
292
|
+
}
|
293
|
+
&[class*=_gap_md] {
|
294
|
+
[class*=pb_timeline_item_kit] {
|
295
|
+
[class=pb_timeline_item_step] {
|
296
|
+
[class=pb_timeline_item_connector] {
|
297
|
+
height: $gap_md !important;
|
298
|
+
}
|
299
|
+
}
|
300
|
+
}
|
301
|
+
}
|
302
|
+
&[class*=_gap_lg] {
|
303
|
+
[class*=pb_timeline_item_kit] {
|
304
|
+
[class=pb_timeline_item_step] {
|
305
|
+
[class=pb_timeline_item_connector] {
|
306
|
+
height: $gap_lg !important;
|
307
|
+
}
|
308
|
+
}
|
309
|
+
}
|
310
|
+
}
|
269
311
|
}
|
270
312
|
}
|
@@ -20,6 +20,7 @@ type TimelineProps = {
|
|
20
20
|
id?: string,
|
21
21
|
orientation?: string,
|
22
22
|
showDate?: boolean,
|
23
|
+
gap?: 'xs' | 'sm' | 'md' | 'lg' | 'none',
|
23
24
|
} & GlobalProps
|
24
25
|
|
25
26
|
const Timeline = ({
|
@@ -31,13 +32,16 @@ const Timeline = ({
|
|
31
32
|
id,
|
32
33
|
orientation = 'horizontal',
|
33
34
|
showDate = false,
|
35
|
+
gap = 'none',
|
34
36
|
...props
|
35
37
|
}: TimelineProps): React.ReactElement => {
|
36
38
|
const ariaProps = buildAriaProps(aria)
|
37
39
|
const dataProps = buildDataProps(data)
|
38
40
|
const htmlProps = buildHtmlProps(htmlOptions)
|
39
41
|
const dateStyle = showDate === true ? '_with_date' : ''
|
40
|
-
const
|
42
|
+
const gapStyle = gap === 'none' ? '' : `gap_${gap}`
|
43
|
+
const timelineCss = buildCss('pb_timeline_kit', `${orientation}`, dateStyle, gapStyle)
|
44
|
+
|
41
45
|
return (
|
42
46
|
<div
|
43
47
|
{...ariaProps}
|
@@ -56,4 +60,4 @@ Timeline.Step = TimelineStep
|
|
56
60
|
Timeline.Label = TimelineLabel
|
57
61
|
Timeline.Detail = TimelineDetail
|
58
62
|
|
59
|
-
export default Timeline
|
63
|
+
export default Timeline
|
@@ -0,0 +1,94 @@
|
|
1
|
+
<%= pb_rails("flex", props: { justify: "evenly" }) do %>
|
2
|
+
<%= pb_rails("flex/flex_item") do %>
|
3
|
+
<%= pb_rails("timeline", props: {orientation: "vertical", gap: "xs"}) do %>
|
4
|
+
<%= pb_rails("timeline/item", props: {icon: "user", icon_color: "royal"}) do %>
|
5
|
+
<%= pb_rails("body", props: {
|
6
|
+
text: "Conversation started",
|
7
|
+
color: "light"
|
8
|
+
}) %>
|
9
|
+
<% end %>
|
10
|
+
<%= pb_rails("timeline/item", props: {icon: "check", icon_color: "teal", line_style: "dotted"}) do %>
|
11
|
+
<%= pb_rails("body", props: {
|
12
|
+
text: "Trip #12422",
|
13
|
+
color: "light"
|
14
|
+
}) %>
|
15
|
+
<% end %>
|
16
|
+
<%= pb_rails("timeline/item", props: {icon: "credit-card", icon_color: "red"}) do %>
|
17
|
+
<%= pb_rails("body", props: {
|
18
|
+
text: "Refund issue #12422",
|
19
|
+
color: "light"
|
20
|
+
}) %>
|
21
|
+
<% end %>
|
22
|
+
<% end %>
|
23
|
+
<% end %>
|
24
|
+
|
25
|
+
<%= pb_rails("flex/flex_item") do %>
|
26
|
+
<%= pb_rails("timeline", props: {orientation: "vertical", gap: "sm"}) do %>
|
27
|
+
<%= pb_rails("timeline/item", props: {icon: "user", icon_color: "royal"}) do %>
|
28
|
+
<%= pb_rails("body", props: {
|
29
|
+
text: "Conversation started",
|
30
|
+
color: "light"
|
31
|
+
}) %>
|
32
|
+
<% end %>
|
33
|
+
<%= pb_rails("timeline/item", props: {icon: "check", icon_color: "teal", line_style: "dotted"}) do %>
|
34
|
+
<%= pb_rails("body", props: {
|
35
|
+
text: "Trip #12422",
|
36
|
+
color: "light"
|
37
|
+
}) %>
|
38
|
+
<% end %>
|
39
|
+
<%= pb_rails("timeline/item", props: {icon: "credit-card", icon_color: "red"}) do %>
|
40
|
+
<%= pb_rails("body", props: {
|
41
|
+
text: "Refund issue #12422",
|
42
|
+
color: "light"
|
43
|
+
}) %>
|
44
|
+
<% end %>
|
45
|
+
<% end %>
|
46
|
+
<% end %>
|
47
|
+
|
48
|
+
<%= pb_rails("flex/flex_item") do %>
|
49
|
+
<%= pb_rails("timeline", props: {orientation: "vertical", gap: "md"}) do %>
|
50
|
+
<%= pb_rails("timeline/item", props: {icon: "user", icon_color: "royal"}) do %>
|
51
|
+
<%= pb_rails("body", props: {
|
52
|
+
text: "Conversation started",
|
53
|
+
color: "light"
|
54
|
+
}) %>
|
55
|
+
<% end %>
|
56
|
+
<%= pb_rails("timeline/item", props: {icon: "check", icon_color: "teal", line_style: "dotted"}) do %>
|
57
|
+
<%= pb_rails("body", props: {
|
58
|
+
text: "Trip #12422",
|
59
|
+
color: "light"
|
60
|
+
}) %>
|
61
|
+
<% end %>
|
62
|
+
<%= pb_rails("timeline/item", props: {icon: "credit-card", icon_color: "red"}) do %>
|
63
|
+
<%= pb_rails("body", props: {
|
64
|
+
text: "Refund issue #12422",
|
65
|
+
color: "light"
|
66
|
+
}) %>
|
67
|
+
<% end %>
|
68
|
+
<% end %>
|
69
|
+
<% end %>
|
70
|
+
|
71
|
+
<%= pb_rails("flex/flex_item") do %>
|
72
|
+
<%= pb_rails("timeline", props: {orientation: "vertical", gap: "lg"}) do %>
|
73
|
+
<%= pb_rails("timeline/item", props: {icon: "user", icon_color: "royal"}) do %>
|
74
|
+
<%= pb_rails("body", props: {
|
75
|
+
text: "Conversation started",
|
76
|
+
color: "light"
|
77
|
+
}) %>
|
78
|
+
<% end %>
|
79
|
+
<%= pb_rails("timeline/item", props: {icon: "check", icon_color: "teal", line_style: "dotted"}) do %>
|
80
|
+
<%= pb_rails("body", props: {
|
81
|
+
text: "Trip #12422",
|
82
|
+
color: "light"
|
83
|
+
}) %>
|
84
|
+
<% end %>
|
85
|
+
<%= pb_rails("timeline/item", props: {icon: "credit-card", icon_color: "red"}) do %>
|
86
|
+
<%= pb_rails("body", props: {
|
87
|
+
text: "Refund issue #12422",
|
88
|
+
color: "light"
|
89
|
+
}) %>
|
90
|
+
<% end %>
|
91
|
+
<% end %>
|
92
|
+
<% end %>
|
93
|
+
<% end %>
|
94
|
+
|
@@ -0,0 +1,180 @@
|
|
1
|
+
import React from 'react'
|
2
|
+
|
3
|
+
import Timeline from '../_timeline'
|
4
|
+
import Flex from "../../pb_flex/_flex"
|
5
|
+
import FlexItem from "../../pb_flex/_flex_item"
|
6
|
+
import Body from '../../pb_body/_body'
|
7
|
+
|
8
|
+
const TimelineWithGap = (props) => (
|
9
|
+
<div>
|
10
|
+
<Flex justify="evenly">
|
11
|
+
<FlexItem>
|
12
|
+
<Timeline
|
13
|
+
gap="xs"
|
14
|
+
orientation="vertical"
|
15
|
+
>
|
16
|
+
<Timeline.Item
|
17
|
+
icon="user"
|
18
|
+
iconColor="royal"
|
19
|
+
{...props}
|
20
|
+
>
|
21
|
+
<Body
|
22
|
+
color="light"
|
23
|
+
text="Conversation started"
|
24
|
+
{...props}
|
25
|
+
/>
|
26
|
+
</Timeline.Item>
|
27
|
+
<Timeline.Item
|
28
|
+
icon="check"
|
29
|
+
iconColor="teal"
|
30
|
+
lineStyle="dotted"
|
31
|
+
{...props}
|
32
|
+
>
|
33
|
+
<Body
|
34
|
+
color="light"
|
35
|
+
text="Trip #12422"
|
36
|
+
{...props}
|
37
|
+
/>
|
38
|
+
</Timeline.Item>
|
39
|
+
<Timeline.Item
|
40
|
+
icon="credit-card"
|
41
|
+
iconColor="red"
|
42
|
+
{...props}
|
43
|
+
>
|
44
|
+
<Body
|
45
|
+
color="light"
|
46
|
+
text="Refund issue #12422"
|
47
|
+
{...props}
|
48
|
+
/>
|
49
|
+
</Timeline.Item>
|
50
|
+
</Timeline>
|
51
|
+
</FlexItem>
|
52
|
+
<FlexItem>
|
53
|
+
<Timeline
|
54
|
+
gap="sm"
|
55
|
+
orientation="vertical"
|
56
|
+
>
|
57
|
+
<Timeline.Item
|
58
|
+
icon="user"
|
59
|
+
iconColor="royal"
|
60
|
+
{...props}
|
61
|
+
>
|
62
|
+
<Body
|
63
|
+
color="light"
|
64
|
+
text="Conversation started"
|
65
|
+
{...props}
|
66
|
+
/>
|
67
|
+
</Timeline.Item>
|
68
|
+
<Timeline.Item
|
69
|
+
icon="check"
|
70
|
+
iconColor="teal"
|
71
|
+
lineStyle="dotted"
|
72
|
+
{...props}
|
73
|
+
>
|
74
|
+
<Body
|
75
|
+
color="light"
|
76
|
+
text="Trip #12422"
|
77
|
+
{...props}
|
78
|
+
/>
|
79
|
+
</Timeline.Item>
|
80
|
+
<Timeline.Item
|
81
|
+
icon="credit-card"
|
82
|
+
iconColor="red"
|
83
|
+
{...props}
|
84
|
+
>
|
85
|
+
<Body
|
86
|
+
color="light"
|
87
|
+
text="Refund issue #12422"
|
88
|
+
{...props}
|
89
|
+
/>
|
90
|
+
</Timeline.Item>
|
91
|
+
</Timeline>
|
92
|
+
</FlexItem>
|
93
|
+
<FlexItem>
|
94
|
+
<Timeline
|
95
|
+
gap="md"
|
96
|
+
orientation="vertical"
|
97
|
+
>
|
98
|
+
<Timeline.Item
|
99
|
+
icon="user"
|
100
|
+
iconColor="royal"
|
101
|
+
{...props}
|
102
|
+
>
|
103
|
+
<Body
|
104
|
+
color="light"
|
105
|
+
text="Conversation started"
|
106
|
+
{...props}
|
107
|
+
/>
|
108
|
+
</Timeline.Item>
|
109
|
+
<Timeline.Item
|
110
|
+
icon="check"
|
111
|
+
iconColor="teal"
|
112
|
+
lineStyle="dotted"
|
113
|
+
{...props}
|
114
|
+
>
|
115
|
+
<Body
|
116
|
+
color="light"
|
117
|
+
text="Trip #12422"
|
118
|
+
{...props}
|
119
|
+
/>
|
120
|
+
</Timeline.Item>
|
121
|
+
<Timeline.Item
|
122
|
+
icon="credit-card"
|
123
|
+
iconColor="red"
|
124
|
+
lineStyle="dotted"
|
125
|
+
{...props}
|
126
|
+
>
|
127
|
+
<Body
|
128
|
+
color="light"
|
129
|
+
text="Refund issue #12422"
|
130
|
+
{...props}
|
131
|
+
/>
|
132
|
+
</Timeline.Item>
|
133
|
+
</Timeline>
|
134
|
+
</FlexItem>
|
135
|
+
<FlexItem>
|
136
|
+
<Timeline
|
137
|
+
gap="lg"
|
138
|
+
orientation="vertical"
|
139
|
+
>
|
140
|
+
<Timeline.Item
|
141
|
+
icon="user"
|
142
|
+
iconColor="royal"
|
143
|
+
{...props}
|
144
|
+
>
|
145
|
+
<Body
|
146
|
+
color="light"
|
147
|
+
text="Conversation started"
|
148
|
+
{...props}
|
149
|
+
/>
|
150
|
+
</Timeline.Item>
|
151
|
+
<Timeline.Item
|
152
|
+
icon="check"
|
153
|
+
iconColor="teal"
|
154
|
+
lineStyle="dotted"
|
155
|
+
{...props}
|
156
|
+
>
|
157
|
+
<Body
|
158
|
+
color="light"
|
159
|
+
text="Trip #12422"
|
160
|
+
{...props}
|
161
|
+
/>
|
162
|
+
</Timeline.Item>
|
163
|
+
<Timeline.Item
|
164
|
+
icon="credit-card"
|
165
|
+
iconColor="red"
|
166
|
+
{...props}
|
167
|
+
>
|
168
|
+
<Body
|
169
|
+
color="light"
|
170
|
+
text="Refund issue #12422"
|
171
|
+
{...props}
|
172
|
+
/>
|
173
|
+
</Timeline.Item>
|
174
|
+
</Timeline>
|
175
|
+
</FlexItem>
|
176
|
+
</Flex>
|
177
|
+
</div>
|
178
|
+
)
|
179
|
+
|
180
|
+
export default TimelineWithGap
|
@@ -0,0 +1 @@
|
|
1
|
+
Use the optional `gap` prop to render the timeline kit with adjusted spacing between nodes. The `gap` prop will only work when utilized with a vertical timeline. Horizontal timelines will show no difference in spacing.
|
@@ -1,14 +1,16 @@
|
|
1
1
|
examples:
|
2
|
-
|
2
|
+
|
3
3
|
rails:
|
4
4
|
- timeline_default: Default
|
5
5
|
- timeline_vertical: Vertical
|
6
6
|
- timeline_with_date: With Date
|
7
7
|
- timeline_with_children: With Children
|
8
|
-
|
9
|
-
|
8
|
+
- timeline_with_gap: With Gap
|
9
|
+
|
10
|
+
|
10
11
|
react:
|
11
12
|
- timeline_default: Default
|
12
13
|
- timeline_vertical: Vertical
|
13
14
|
- timeline_with_date: With Date
|
14
15
|
- timeline_with_children: With Children
|
16
|
+
- timeline_with_gap: With Gap
|
@@ -2,3 +2,5 @@ export { default as TimelineDefault } from './_timeline_default.jsx'
|
|
2
2
|
export { default as TimelineVertical } from './_timeline_vertical.jsx'
|
3
3
|
export { default as TimelineWithDate } from './_timeline_with_date.jsx'
|
4
4
|
export { default as TimelineWithChildren } from './_timeline_with_children.jsx'
|
5
|
+
export { default as TimelineWithGap } from './_timeline_with_gap.jsx'
|
6
|
+
|
@@ -8,9 +8,15 @@ module Playbook
|
|
8
8
|
default: "horizontal"
|
9
9
|
prop :show_date, type: Playbook::Props::Boolean,
|
10
10
|
default: false
|
11
|
+
prop :gap, type: Playbook::Props::Enum,
|
12
|
+
values: %w[xs sm md lg none],
|
13
|
+
default: "none"
|
11
14
|
|
12
15
|
def classname
|
13
|
-
generate_classname("pb_timeline_kit",
|
16
|
+
generate_classname("pb_timeline_kit",
|
17
|
+
orientation,
|
18
|
+
date_class,
|
19
|
+
gap_class)
|
14
20
|
end
|
15
21
|
|
16
22
|
private
|
@@ -18,6 +24,10 @@ module Playbook
|
|
18
24
|
def date_class
|
19
25
|
show_date ? "with_date" : nil
|
20
26
|
end
|
27
|
+
|
28
|
+
def gap_class
|
29
|
+
gap == "none" ? nil : "gap_#{gap}"
|
30
|
+
end
|
21
31
|
end
|
22
32
|
end
|
23
33
|
end
|
@@ -147,26 +147,26 @@ test('should pass id prop', () => {
|
|
147
147
|
test('should have horizontal orientation by default', () => {
|
148
148
|
render(<TimelineDefault />)
|
149
149
|
const kit = screen.getByTestId(testId)
|
150
|
-
expect(kit).toHaveClass('
|
150
|
+
expect(kit).toHaveClass('pb_timeline_kit_horizontal')
|
151
151
|
})
|
152
152
|
|
153
153
|
test('should pass vertical orientation', () => {
|
154
154
|
const props = { orientation: 'vertical' }
|
155
155
|
render(<TimelineDefault {...props} />)
|
156
156
|
const kit = screen.getByTestId(testId)
|
157
|
-
expect(kit).toHaveClass('
|
157
|
+
expect(kit).toHaveClass('pb_timeline_kit_vertical')
|
158
158
|
})
|
159
159
|
|
160
160
|
test('should pass showDate prop', () => {
|
161
161
|
const props = { showDate: true }
|
162
162
|
render(<TimelineDefault {...props} />)
|
163
163
|
const kit = screen.getByTestId(testId)
|
164
|
-
expect(kit).toHaveClass('
|
164
|
+
expect(kit).toHaveClass('pb_timeline_kit_horizontal__with_date')
|
165
165
|
})
|
166
166
|
|
167
167
|
test('should pass showDate prop with Children', () => {
|
168
168
|
const props = { showDate: true }
|
169
169
|
render(<TimelineWithChildren {...props} />)
|
170
170
|
const kit = screen.getByTestId(testId)
|
171
|
-
expect(kit).toHaveClass('
|
171
|
+
expect(kit).toHaveClass('pb_timeline_kit_horizontal__with_date')
|
172
172
|
})
|
@@ -4,11 +4,12 @@ import { debounce } from 'lodash'
|
|
4
4
|
export default class PbTypeahead extends PbEnhancedElement {
|
5
5
|
_searchInput: HTMLInputElement
|
6
6
|
_resultsElement: HTMLElement
|
7
|
-
_debouncedSearch:
|
7
|
+
_debouncedSearch: () => void
|
8
8
|
_resultsLoadingIndicator: HTMLElement
|
9
9
|
_resultOptionTemplate: HTMLElement
|
10
10
|
_resultsOptionCache: Map<string, Array<DocumentFragment>>
|
11
11
|
_searchContext: string
|
12
|
+
_validSelection: boolean
|
12
13
|
|
13
14
|
static get selector() {
|
14
15
|
return '[data-pb-typeahead-kit]'
|
@@ -19,6 +20,7 @@ export default class PbTypeahead extends PbEnhancedElement {
|
|
19
20
|
this.searchInput.addEventListener('focus', () => this.debouncedSearch())
|
20
21
|
this.searchInput.addEventListener('input', () => this.debouncedSearch())
|
21
22
|
this.resultsElement.addEventListener('click', (event: MouseEvent) => this.optionSelected(event))
|
23
|
+
this.element.closest('form')?.addEventListener('submit', (event) => this.handleFormSubmission(event))
|
22
24
|
}
|
23
25
|
|
24
26
|
handleKeydown(event: KeyboardEvent) {
|
@@ -86,6 +88,9 @@ export default class PbTypeahead extends PbEnhancedElement {
|
|
86
88
|
const resultOption = (event.target as Element).closest('[data-result-option-item]')
|
87
89
|
if (!resultOption) return
|
88
90
|
|
91
|
+
this._validSelection = true
|
92
|
+
this.removeValidationError()
|
93
|
+
|
89
94
|
this.resultsCacheClear()
|
90
95
|
this.searchInputClear()
|
91
96
|
this.clearResults()
|
@@ -93,6 +98,35 @@ export default class PbTypeahead extends PbEnhancedElement {
|
|
93
98
|
this.element.dispatchEvent(new CustomEvent('pb-typeahead-kit-result-option-selected', { bubbles: true, detail: { selected: resultOption, typeahead: this } }))
|
94
99
|
}
|
95
100
|
|
101
|
+
removeValidationError() {
|
102
|
+
const inputWrapper = this.searchInput.closest('.text_input_wrapper')
|
103
|
+
if (inputWrapper) {
|
104
|
+
const errorMessage = inputWrapper.querySelector('.pb_body_kit_negative') as HTMLElement
|
105
|
+
if (errorMessage) {
|
106
|
+
errorMessage.style.display = 'none'
|
107
|
+
}
|
108
|
+
this.searchInput.classList.remove('error')
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
showValidationError() {
|
113
|
+
const inputWrapper = this.searchInput.closest('.text_input_wrapper')
|
114
|
+
if (inputWrapper) {
|
115
|
+
const errorMessage = inputWrapper.querySelector('.pb_body_kit_negative') as HTMLElement
|
116
|
+
if (errorMessage) {
|
117
|
+
errorMessage.style.display = 'block'
|
118
|
+
}
|
119
|
+
this.searchInput.classList.add('error')
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
handleFormSubmission(event: Event) {
|
124
|
+
if (!this._validSelection) {
|
125
|
+
this.showValidationError()
|
126
|
+
event.preventDefault()
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
96
130
|
clearResults() {
|
97
131
|
this.resultsElement.innerHTML = ''
|
98
132
|
}
|
@@ -201,7 +235,7 @@ export default class PbTypeahead extends PbEnhancedElement {
|
|
201
235
|
}
|
202
236
|
|
203
237
|
toggleResultsLoadingIndicator(visible: boolean) {
|
204
|
-
|
238
|
+
let visibilityProperty = '0'
|
205
239
|
if (visible) visibilityProperty = '1'
|
206
240
|
this.resultsLoadingIndicator.style.opacity = visibilityProperty
|
207
241
|
}
|