playbook_ui 14.12.0.pre.alpha.PBNTR456fixedconftoastrailsautoclose5673 → 14.12.0.pre.alpha.PBNTR456fixedconftoastrailsautoclose5738

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/_playbook.scss +1 -0
  3. data/app/pb_kits/playbook/pb_advanced_table/Components/CustomCell.tsx +1 -1
  4. data/app/pb_kits/playbook/pb_advanced_table/Components/TableHeaderCell.tsx +2 -2
  5. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.html.erb +8 -13
  6. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.rb +2 -0
  7. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +1 -1
  8. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_loading.html.erb +33 -0
  9. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_loading_rails.md +1 -0
  10. data/app/pb_kits/playbook/pb_advanced_table/docs/{_advanced_table_loading.md → _advanced_table_loading_react.md} +2 -2
  11. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +1 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/table_body.rb +3 -1
  13. data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +9 -5
  14. data/app/pb_kits/playbook/pb_advanced_table/table_header.rb +2 -0
  15. data/app/pb_kits/playbook/pb_advanced_table/table_row.html.erb +1 -1
  16. data/app/pb_kits/playbook/pb_advanced_table/table_row.rb +2 -0
  17. data/app/pb_kits/playbook/pb_avatar/_avatar.scss +5 -0
  18. data/app/pb_kits/playbook/pb_copy_button/_copy_button.scss +3 -0
  19. data/app/pb_kits/playbook/pb_copy_button/_copy_button.tsx +92 -0
  20. data/app/pb_kits/playbook/pb_copy_button/copy_button.test.jsx +64 -0
  21. data/app/pb_kits/playbook/pb_copy_button/docs/_copy_button_default.jsx +21 -0
  22. data/app/pb_kits/playbook/pb_copy_button/docs/_copy_button_from.jsx +45 -0
  23. data/app/pb_kits/playbook/pb_copy_button/docs/_copy_button_from.md +1 -0
  24. data/app/pb_kits/playbook/pb_copy_button/docs/example.yml +8 -0
  25. data/app/pb_kits/playbook/pb_copy_button/docs/index.js +2 -0
  26. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_auto_close.html.erb +13 -54
  27. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_auto_close_rails.md +2 -2
  28. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/index.js +13 -47
  29. data/app/pb_kits/playbook/pb_table/docs/_table_with_collapsible_with_custom_content.jsx +12 -8
  30. data/app/pb_kits/playbook/pb_table/docs/_table_with_collapsible_with_custom_content_rails.html.erb +52 -0
  31. data/app/pb_kits/playbook/pb_table/docs/_table_with_collapsible_with_custom_content_rails.md +0 -0
  32. data/app/pb_kits/playbook/pb_table/docs/_table_with_collapsible_with_nested_rows_rails.html.erb +52 -0
  33. data/app/pb_kits/playbook/pb_table/docs/_table_with_collapsible_with_nested_rows_rails.md +3 -0
  34. data/app/pb_kits/playbook/pb_table/docs/_table_with_collapsible_with_nested_table_rails.html.erb +80 -0
  35. data/app/pb_kits/playbook/pb_table/docs/_table_with_collapsible_with_nested_table_rails.md +1 -0
  36. data/app/pb_kits/playbook/pb_table/docs/example.yml +3 -0
  37. data/app/pb_kits/playbook/pb_table/table_row.rb +1 -1
  38. data/app/pb_kits/playbook/pb_tooltip/_tooltip.tsx +3 -1
  39. data/dist/chunks/{_typeahead-BWwaAo_0.js → _typeahead-BIhRQo8Q.js} +3 -3
  40. data/dist/chunks/_weekday_stacked-VKMYuo6-.js +45 -0
  41. data/dist/chunks/vendor.js +1 -1
  42. data/dist/menu.yml +6 -0
  43. data/dist/playbook-doc.js +1 -1
  44. data/dist/playbook-rails-react-bindings.js +1 -1
  45. data/dist/playbook-rails.js +1 -1
  46. data/dist/playbook.css +1 -1
  47. data/lib/playbook/pb_forms_global_props_helper.rb +136 -0
  48. data/lib/playbook/pb_forms_helper.rb +13 -4
  49. data/lib/playbook/version.rb +1 -1
  50. metadata +22 -5
  51. data/dist/chunks/_weekday_stacked-zyBCd1s8.js +0 -45
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab973abc311a1d8a8b406fe1733365d241d40ff7c9d71ab47286685a39931831
4
- data.tar.gz: 1efceea37120fc1a5cc40b95ed64b05b0b4bb94a6ea4a48037c87b2dab971de7
3
+ metadata.gz: 3fc1642e15301ac5fecdafb78ad0d02de29c9dbbb26b3bc08760f2528ecd61c5
4
+ data.tar.gz: 9fc96e2bc464ba755a7fbcedb891829e390d557d04da9e37cd51e607737c95b9
5
5
  SHA512:
6
- metadata.gz: 624d99dd670eaeec337a32e50a5330138c5a15d77d7a21c2a4499b5a7d2389dacc4f076f37c42ae76c1bef1ee0c1ef3140ef5266f6ed50dea8b6713c07aaf6d2
7
- data.tar.gz: af70dcadf4147c80d19a6fc71c4d4d0cd6427a9b10a65a3d7ef1c79e67549df49c5b2efea002414e4c6a1f4204602a47d9999bd808afbcd61cbec9e8e9b56afc
6
+ metadata.gz: a1bc8cd947d1555876f16312bf9ca8c81b79bfb44f2d5f5bf5f8a968faf0081252cc8be904e77f8935fd175cdea7bc47d7dcfad2f4fc07915edff5f4f7287088
7
+ data.tar.gz: d7ed7ae200c1b5c09e5aac2f39a945060fc3f9a7f997bc083854bd94d6a062f12ed865abfd8f82c631118020f2c4ef1222a237663595fa0ce72aa8e3881c9acb
@@ -15,6 +15,7 @@
15
15
  @import 'pb_circle_chart/circle_chart';
16
16
  @import 'pb_circle_icon_button/circle_icon_button';
17
17
  @import 'pb_collapsible/collapsible';
18
+ @import 'pb_copy_button/copy_button';
18
19
  @import 'pb_contact/contact';
19
20
  @import 'pb_currency/currency';
20
21
  @import 'pb_dashboard_value/dashboard_value';
@@ -47,7 +47,7 @@ export const CustomCell = ({
47
47
  <Flex
48
48
  alignItems="center"
49
49
  columnGap="xs"
50
- justify={!hasAnySubRows ? "end" : "start"}
50
+ justify={!hasAnySubRows && !inlineRowLoading ? "end" : "start"}
51
51
  orientation="row"
52
52
  >
53
53
  {
@@ -39,7 +39,7 @@ export const TableHeaderCell = ({
39
39
  sortIcon,
40
40
  table
41
41
  }: TableHeaderCellProps) => {
42
- const { sortControl, responsive, selectableRows, hasAnySubRows, showActionsBar } =
42
+ const { sortControl, responsive, selectableRows, hasAnySubRows, showActionsBar, inlineRowLoading } =
43
43
  useContext(AdvancedTableContext);
44
44
 
45
45
  type justifyTypes = "none" | "center" | "start" | "end" | "between" | "around" | "evenly"
@@ -91,7 +91,7 @@ const isToggleExpansionEnabled =
91
91
 
92
92
  let justifyHeader:justifyTypes;
93
93
 
94
- if (header?.index === 0 && hasAnySubRows) {
94
+ if (header?.index === 0 && hasAnySubRows || (header?.index === 0 && inlineRowLoading)) {
95
95
  justifyHeader = enableSorting ? "between" : "start";
96
96
  } else {
97
97
  justifyHeader = isLeafColumn ? "end" : "center";
@@ -1,15 +1,10 @@
1
1
  <%= pb_content_tag do %>
2
- <%= pb_rails("table", props: { size: "sm", data_table: true, number_spacing:"tabular", responsive: "none", dark: dark }.merge(object.table_props)) do %>
3
- <% if content.present? %>
4
- <% content.presence %>
5
- <% else %>
6
- <%= pb_rails("advanced_table/table_header", props: { column_definitions: object.column_definitions,
7
- enable_toggle_expansion: object.enable_toggle_expansion,
8
- responsive: object.responsive }) %>
9
- <%= pb_rails("advanced_table/table_body", props: { id: object.id,
10
- table_data: object.table_data,
11
- column_definitions: object.column_definitions,
12
- responsive: object.responsive }) %>
13
- <% end %>
14
- <% end %>
2
+ <%= pb_rails("table", props: { size: "sm", data_table: true, number_spacing:"tabular", responsive:"none", dark: dark, classname: object.loading ? "content-loading" : "" }.merge(object.table_props)) do %>
3
+ <% if content.present? %>
4
+ <% content.presence %>
5
+ <% else %>
6
+ <%= pb_rails("advanced_table/table_header", props: { column_definitions: object.column_definitions, enable_toggle_expansion: object.enable_toggle_expansion, responsive: object.responsive, loading: object.loading }) %>
7
+ <%= pb_rails("advanced_table/table_body", props: { id: object.id, table_data: object.table_data, column_definitions: object.column_definitions, responsive: object.responsive, loading: object.loading }) %>
8
+ <% end %>
9
+ <% end %>
15
10
  <% end %>
@@ -10,6 +10,8 @@ module Playbook
10
10
  prop :enable_toggle_expansion, type: Playbook::Props::Enum,
11
11
  values: %w[all header none],
12
12
  default: "header"
13
+ prop :loading, type: Playbook::Props::Boolean,
14
+ default: false
13
15
  prop :responsive, type: Playbook::Props::Enum,
14
16
  values: %w[none scroll],
15
17
  default: "scroll"
@@ -245,7 +245,7 @@ test("toggleExpansionAll button exists and toggles subrows open and closed", asy
245
245
  })
246
246
 
247
247
 
248
- test("loading state + initialLoadingRowCount prop", () => {
248
+ test("loading state + initialLoadingRowsCount prop", () => {
249
249
  render(
250
250
  <AdvancedTable
251
251
  columnDefinitions={columnDefinitions}
@@ -0,0 +1,33 @@
1
+ <% column_definitions = [
2
+ {
3
+ accessor: "year",
4
+ label: "Year",
5
+ cellAccessors: ["quarter", "month", "day"],
6
+ },
7
+ {
8
+ accessor: "newEnrollments",
9
+ label: "New Enrollments",
10
+ },
11
+ {
12
+ accessor: "scheduledMeetings",
13
+ label: "Scheduled Meetings",
14
+ },
15
+ {
16
+ accessor: "attendanceRate",
17
+ label: "Attendance Rate",
18
+ },
19
+ {
20
+ accessor: "completedClasses",
21
+ label: "Completed Classes",
22
+ },
23
+ {
24
+ accessor: "classCompletionRate",
25
+ label: "Class Completion Rate",
26
+ },
27
+ {
28
+ accessor: "graduatedStudents",
29
+ label: "Graduated Students",
30
+ }
31
+ ] %>
32
+
33
+ <%= pb_rails("advanced_table", props: { id: "beta_table", table_data: @table_data, column_definitions: column_definitions, loading: true }) %>
@@ -0,0 +1 @@
1
+ The optional `loading` prop takes a boolean value that can be managed using state. If loading is true, the table will display the loading skeleton and once loading is false, the table will render with the data provided.
@@ -1,3 +1,3 @@
1
- the optional `loading` prop takes a boolean value that can be managed using state. If loading is true, the table will display the loading skeleton and once loading is false, the table will render with the data provided.
1
+ The optional `loading` prop takes a boolean value that can be managed using state. If loading is true, the table will display the loading skeleton and once loading is false, the table will render with the data provided.
2
2
 
3
- By default, the inital row count of the loading skeleton is set to 10. If you want more control over this initial row count, the optional `initialLoadingRowCount` prop can be used to pass in a number. __NOTE__: This is only for the first render of the table, subsequent loading skeleton row count logic is handled within the kit itself.
3
+ By default, the inital row count of the loading skeleton is set to 10. If you want more control over this initial row count, the optional `initialLoadingRowsCount` prop can be used to pass in a number. __NOTE__: This is only for the first render of the table, subsequent loading skeleton row count logic is handled within the kit itself.
@@ -1,6 +1,7 @@
1
1
  examples:
2
2
  rails:
3
3
  - advanced_table_beta: Default (Required Props)
4
+ - advanced_table_loading: Loading State
4
5
  - advanced_table_beta_subrow_headers: SubRow Headers
5
6
  - advanced_table_collapsible_trail_rails: Collapsible Trail
6
7
  - advanced_table_table_props: Table Props
@@ -16,6 +16,8 @@ module Playbook
16
16
  default: []
17
17
  prop :collapsible_trail, type: Playbook::Props::Boolean,
18
18
  default: true
19
+ prop :loading, type: Playbook::Props::Boolean,
20
+ default: false
19
21
  prop :responsive, type: Playbook::Props::Enum,
20
22
  values: %w[none scroll],
21
23
  default: "scroll"
@@ -54,7 +56,7 @@ module Playbook
54
56
  current_data_attributes = current_depth.zero? ? { row_depth: 0 } : table_data_attributes
55
57
 
56
58
  # Additional class and data attributes needed for toggle logic
57
- output << pb_rails("advanced_table/table_row", props: { id: id, row: row, column_definitions: leaf_columns, depth: current_depth, collapsible_trail: collapsible_trail, classname: additional_classes, table_data_attributes: current_data_attributes, responsive: responsive })
59
+ output << pb_rails("advanced_table/table_row", props: { id: id, row: row, column_definitions: leaf_columns, depth: current_depth, collapsible_trail: collapsible_trail, classname: additional_classes, table_data_attributes: current_data_attributes, responsive: responsive, loading: loading })
58
60
 
59
61
  if row[:children].present?
60
62
  row[:children].each do |child_row|
@@ -6,11 +6,15 @@
6
6
  <%= pb_rails("table/table_header", props: { id: header_id, colspan: cell[:colspan], classname: [object.th_classname(is_first_column: cell_index.zero?), ('last-header-cell' if cell[:is_last_in_group] && cell_index != 0)].compact.join(' '), sort_menu: cell[:accessor] ? cell[:sort_menu] : nil }) do %>
7
7
  <%= pb_rails("flex", props: { align: "center", justify: cell_index.zero? ? "start" : row_index === header_rows.size - 1 ? "end" : "center", text_align: "end" }) do %>
8
8
  <% if cell_index.zero? && (object.enable_toggle_expansion == "header" || object.enable_toggle_expansion == "all") && row_index === header_rows.size - 1 %>
9
- <button
10
- class="gray-icon toggle-all-icon"
11
- onclick="expandAllRows(this); event.preventDefault();">
12
- <%= pb_rails("icon", props: { icon: "arrows-from-line", cursor: "pointer", fixed_width: true, padding_right: "xs" }) %>
13
- </button>
9
+ <% if loading %>
10
+ <div class="<%= object.loading ? 'loading-toggle-icon' : '' %>"></div>
11
+ <% else %>
12
+ <button
13
+ class="gray-icon toggle-all-icon"
14
+ onclick="expandAllRows(this); event.preventDefault();">
15
+ <%= pb_rails("icon", props: { icon: "arrows-from-line", cursor: "pointer", fixed_width: true, padding_right: "xs" }) %>
16
+ </button>
17
+ <% end %>
14
18
  <% end %>
15
19
  <%= cell[:label] %>
16
20
  <% end %>
@@ -8,6 +8,8 @@ module Playbook
8
8
  prop :enable_toggle_expansion, type: Playbook::Props::Enum,
9
9
  values: %w[all header none],
10
10
  default: "header"
11
+ prop :loading, type: Playbook::Props::Boolean,
12
+ default: false
11
13
  prop :responsive, type: Playbook::Props::Enum,
12
14
  values: %w[none scroll],
13
15
  default: "scroll"
@@ -2,7 +2,7 @@
2
2
  <% object.column_definitions.each_with_index do |column, index| %>
3
3
  <% next unless column[:accessor].present? %>
4
4
  <%= pb_rails("table/table_cell", props: { classname:object.td_classname(column, index)}) do %>
5
- <%= pb_rails("flex", props:{ align: "center", justify: index.zero? ? "start" : "end" }) do %>
5
+ <%= pb_rails("flex", props:{ align: "center", justify: index.zero? ? "start" : "end", classname: object.loading ? "loading-cell" : "" }) do %>
6
6
  <% if collapsible_trail && index.zero? %>
7
7
  <% (1..depth).each do |i| %>
8
8
  <% additional_offset = i > 1 ? (i - 1) * 0.25 : 0 %>
@@ -13,6 +13,8 @@ module Playbook
13
13
  default: true
14
14
  prop :table_data_attributes, type: Playbook::Props::HashProp,
15
15
  default: {}
16
+ prop :loading, type: Playbook::Props::Boolean,
17
+ default: false
16
18
  prop :responsive, type: Playbook::Props::Enum,
17
19
  values: %w[none scroll],
18
20
  default: "scroll"
@@ -78,4 +78,9 @@ $avatar-sizes: (
78
78
  }
79
79
  }
80
80
  }
81
+ &.dark {
82
+ [class^=pb_card_kit] {
83
+ position: absolute;
84
+ }
85
+ }
81
86
  }
@@ -0,0 +1,3 @@
1
+ .pb_copy_button_kit {
2
+
3
+ }
@@ -0,0 +1,92 @@
1
+
2
+ import React, { useState } from 'react'
3
+ import classnames from 'classnames'
4
+ import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
5
+ import { globalProps } from '../utilities/globalProps'
6
+
7
+ import Button from '../pb_button/_button'
8
+ import Tooltip from '../pb_tooltip/_tooltip'
9
+
10
+ type CopyButtonProps = {
11
+ aria?: { [key: string]: string },
12
+ className?: string,
13
+ data?: { [key: string]: string },
14
+ id?: string,
15
+ from?: string,
16
+ text?: string,
17
+ tooltipPlacement?: "top" | "right" | "bottom" | "left",
18
+ tooltipText?: string,
19
+ value?: string,
20
+ }
21
+
22
+ const CopyButton = (props: CopyButtonProps) => {
23
+ const {
24
+ aria = {},
25
+ className,
26
+ data = {},
27
+ from = '',
28
+ id,
29
+ text= 'Copy',
30
+ tooltipPlacement= 'bottom',
31
+ tooltipText = 'Copied!',
32
+ value = '',
33
+ } = props
34
+
35
+ const [copied, setCopied] = useState(false)
36
+
37
+ const ariaProps = buildAriaProps(aria)
38
+ const dataProps = buildDataProps(data)
39
+ const classes = classnames(buildCss('pb_copy_button_kit'), globalProps(props), className)
40
+
41
+ const copy = () => {
42
+ if (!from && !value) {
43
+ return
44
+ }
45
+
46
+ if (value) {
47
+ navigator.clipboard.writeText(value)
48
+ } else if (from) {
49
+ const copyElement = document.getElementById(from);
50
+ let copyText = copyElement?.innerText
51
+
52
+ if (copyElement instanceof HTMLTextAreaElement || copyElement instanceof HTMLInputElement) {
53
+ copyText = copyElement.value;
54
+ }
55
+
56
+ if (copyText) {
57
+ navigator.clipboard.writeText(copyText)
58
+ }
59
+ }
60
+
61
+ setCopied(true)
62
+
63
+ setTimeout(() => {
64
+ setCopied(false)
65
+ }, 1000);
66
+ }
67
+
68
+ return (
69
+ <div
70
+ {...ariaProps}
71
+ {...dataProps}
72
+ className={classes}
73
+ id={id}
74
+ >
75
+ <Tooltip
76
+ forceOpenTooltip={copied}
77
+ placement={tooltipPlacement}
78
+ showTooltip={false}
79
+ text={tooltipText}
80
+ >
81
+ <Button
82
+ icon='copy'
83
+ onClick={copy}
84
+ >
85
+ { text }
86
+ </Button>
87
+ </Tooltip>
88
+ </div>
89
+ )
90
+ }
91
+
92
+ export default CopyButton
@@ -0,0 +1,64 @@
1
+ import React from 'react';
2
+ import { CopyButton } from 'playbook-ui'
3
+ import { ensureAccessible, renderKit, render, fireEvent, screen } from '../utilities/test-utils'
4
+
5
+ const props = {
6
+ data: { testid: 'default', value: 'copy' }
7
+ }
8
+
9
+ test('returns namespaced class name', () => {
10
+ const kit = renderKit(CopyButton, props)
11
+ expect(kit).toBeInTheDocument()
12
+ expect(kit).toHaveClass('pb_copy_button_kit')
13
+ })
14
+
15
+ it('should be accessible', async () => {
16
+ ensureAccessible(CopyButton, props)
17
+ })
18
+
19
+ // It's difficult to actually use navigator.clipboard.readText, so we mock
20
+ it('copies the value to clipboard and pastes it into an input', async () => {
21
+ Object.defineProperty(global, 'navigator', {
22
+ value: {
23
+ clipboard: {
24
+ writeText: jest.fn().mockResolvedValueOnce(undefined),
25
+ },
26
+ },
27
+ writable: true,
28
+ })
29
+
30
+ render(<CopyButton {...props} />)
31
+
32
+ const copyButton = screen.getByTestId('default')
33
+ fireEvent.click(copyButton)
34
+
35
+ await navigator.clipboard.writeText('copy')
36
+
37
+ expect(navigator.clipboard.writeText).toHaveBeenCalledWith("copy");
38
+ })
39
+
40
+ test('passes text and tooltip props to button', () => {
41
+ render(
42
+ <CopyButton
43
+ data={{ testid: 'text-test' }}
44
+ text={"text"}
45
+ tooltipPlacement="right"
46
+ tooltipText="Text copied!"
47
+ value="copy"
48
+ />
49
+ )
50
+
51
+ const content = screen.getByText("text")
52
+ expect(content).toHaveTextContent("text")
53
+
54
+ const kit = screen.getByTestId('text-test')
55
+ const button = kit.querySelector('.pb_button_kit_primary_inline_enabled')
56
+ expect(button).toBeInTheDocument()
57
+
58
+ fireEvent.click(button)
59
+ const tooltipContent = screen.getByText("Text copied!")
60
+ expect(tooltipContent).toHaveTextContent("Text copied!")
61
+
62
+ const tooltip = kit.querySelector('.pb_tooltip_kit')
63
+ expect(tooltip).toBeInTheDocument()
64
+ })
@@ -0,0 +1,21 @@
1
+ import React from 'react'
2
+ import { CopyButton, Textarea } from 'playbook-ui'
3
+
4
+ const CopyButtonDefault = (props) => (
5
+ <div>
6
+ <CopyButton
7
+ {...props}
8
+ text="Copy Text"
9
+ tooltipPlacement="right"
10
+ tooltipText="Text copied!"
11
+ value="Playbook makes it easy to support bleeding edge, or legacy systems. Use Playbook’s 200+ components and end-to-end design language to create simple, intuitive and beautiful experiences with ease."
12
+ />
13
+
14
+ <Textarea
15
+ {...props}
16
+ placeholder="Copy and paste here"
17
+ />
18
+ </div>
19
+ )
20
+
21
+ export default CopyButtonDefault
@@ -0,0 +1,45 @@
1
+ import React, { useState } from 'react'
2
+ import { CopyButton, Body, TextInput, Textarea } from 'playbook-ui'
3
+
4
+ const CopyButtonFrom = (props) => {
5
+ const [text, setText] = useState("Copy this text input text")
6
+
7
+ const handleChange = (event) => {
8
+ setText(event.target.value);
9
+ }
10
+
11
+ return (<div>
12
+ <Body id="body">Copy this body text!</Body>
13
+ <CopyButton
14
+ {...props}
15
+ from="body"
16
+ marginBottom="sm"
17
+ text="Copy Body text"
18
+ tooltipPlacement="right"
19
+ tooltipText="Body text copied!"
20
+ />
21
+
22
+ <TextInput
23
+ {...props}
24
+ id="textinput"
25
+ onChange={handleChange}
26
+ value={text}
27
+ />
28
+ <CopyButton
29
+ {...props}
30
+ from="textinput"
31
+ marginBottom="sm"
32
+ text="Copy Text Input"
33
+ tooltipPlacement="right"
34
+ tooltipText="Text input copied!"
35
+ />
36
+
37
+ <Textarea
38
+ {...props}
39
+ placeholder="Copy and paste here"
40
+ />
41
+ </div>
42
+ )
43
+ }
44
+
45
+ export default CopyButtonFrom
@@ -0,0 +1 @@
1
+ Provide an element's ID as the `from` parameter, and its text will be copied. If the element is an input, its `value` will be copied; otherwise, the `innerText` will be used. Additionally, if a `value` prop is provided, it will override the content from the `from` element and be copied instead.
@@ -0,0 +1,8 @@
1
+ examples:
2
+
3
+
4
+ react:
5
+ - copy_button_default: Default
6
+ - copy_button_from: Copy From
7
+
8
+
@@ -0,0 +1,2 @@
1
+ export { default as CopyButtonDefault } from './_copy_button_default.jsx'
2
+ export { default as CopyButtonFrom } from './_copy_button_from.jsx'
@@ -1,15 +1,5 @@
1
- <%= pb_rails("button", props: { text: "Show Auto Close Toast", variant: "secondary", data: { toast: "#toast-auto-close" } }) %>
2
1
  <%= pb_rails("button", props: { text: "Show Closeable Auto Close Toast", variant: "secondary", data: { toast: "#toast-auto-close-closeable" } }) %>
3
2
 
4
- <%= pb_rails("fixed_confirmation_toast", props: {
5
- auto_close: 3000,
6
- id: "toast-auto-close",
7
- text: "I will disappear in 3 seconds.",
8
- status: "tip",
9
- vertical: "top",
10
- horizontal: "center"
11
- }) %>
12
-
13
3
  <%= pb_rails("fixed_confirmation_toast", props: {
14
4
  auto_close: 10000,
15
5
  closeable: true,
@@ -22,53 +12,22 @@
22
12
 
23
13
  <script>
24
14
  document.addEventListener('DOMContentLoaded', () => {
25
- const toasts = {
26
- '#toast-auto-close': document.querySelector("#toast-auto-close"),
27
- '#toast-auto-close-closeable': document.querySelector("#toast-auto-close-closeable")
28
- }
29
-
30
- const buttons = {
31
- '#toast-auto-close': document.querySelector("button[data-toast='#toast-auto-close']"),
32
- '#toast-auto-close-closeable': document.querySelector("button[data-toast='#toast-auto-close-closeable']")
33
- }
15
+ const toast10SecCloseable = document.querySelector("#toast-auto-close-closeable")
16
+ const button10SecCloseable = document.querySelector("button[data-toast='#toast-auto-close-closeable']")
34
17
 
35
- const hideAllToastsUponPageLoad = () => {
36
- Object.values(toasts).forEach(toast => {
37
- if (toast) toast.style.display = "none"
38
- })
18
+ const originalToast = toast10SecCloseable.cloneNode(true)
19
+
20
+ if (toast10SecCloseable) {
21
+ toast10SecCloseable.remove()
39
22
  }
40
23
 
41
- const showAndAutoCloseToast = (toastId) => {
42
- hideAllToastsUponPageLoad()
43
- const toast = toasts[toastId]
44
-
45
- if (toast) {
46
- toast.style.display = "flex" // Show the selected toast
47
-
48
- const autoCloseClass = Array.from(toast.classList).find(cls => cls.startsWith('auto_close_'))
49
- if (autoCloseClass) {
50
- const duration = parseInt(autoCloseClass.split('_')[2]) || 0; // Use 0 as fallback if parsing fails
51
-
52
- if (toast.autoCloseTimeout) {
53
- clearTimeout(toast.autoCloseTimeout)
54
- }
55
-
56
- toast.autoCloseTimeout = setTimeout(() => {
57
- toast.style.display = "none"
58
- }, duration)
59
- }
24
+ if (button10SecCloseable) {
25
+ button10SecCloseable.onclick = () => {
26
+ const newToast = originalToast.cloneNode(true)
27
+ newToast.style.display = "flex"
28
+
29
+ document.body.appendChild(newToast)
60
30
  }
61
31
  }
62
-
63
- Object.keys(buttons).forEach((key) => {
64
- const button = buttons[key]
65
- if (button) {
66
- button.onclick = () => {
67
- showAndAutoCloseToast(key)
68
- }
69
- }
70
- })
71
-
72
- hideAllToastsUponPageLoad()
73
32
  })
74
- </script>
33
+ </script>
@@ -1,3 +1,3 @@
1
- Auto close is used when you want the confirmation toast to close automatically after a certain time. `auto_close` property will be a delay number in ms.
1
+ Auto close is used when you want the confirmation toast to close automatically after a certain time. `auto_close` property will be a delay number in ms. The `auto_close` property currently only works in rails when it is a closeable one (`closeable: true`).
2
2
 
3
- Script tag in code snippet is for demonstration purposes only. It provides the functionality needed to show and hide the toaster in response to user interaction, and hides them from appearing upon initial page load. In a typical production environment this functionality would be handled by a controller or a separate javascript file.
3
+ The script tag in this code snippet is for demonstration purposes only. It clones the toast in order to have it appear with a button click prompt and not upon initial page load. In a typical production environment the event triggering a fixed confirmation toast to appear would be handled by a controller or a separate javascript file.
@@ -1,69 +1,35 @@
1
1
  import PbEnhancedElement from '../pb_enhanced_element'
2
2
 
3
- const TOAST_WRAPPER_SELECTOR = '[data-pb-toast-wrapper]'
4
- const TOAST_AUTO_CLOSE_CLASS = 'auto_close'
5
- const TOAST_REMOVE_CLASS = 'remove_toast'
6
-
7
3
  export default class PbFixedConfirmationToast extends PbEnhancedElement {
8
4
  static get selector() {
9
- return `${TOAST_WRAPPER_SELECTOR}, .${TOAST_AUTO_CLOSE_CLASS}, .${TOAST_REMOVE_CLASS}`
5
+ return '.remove_toast'
10
6
  }
11
7
 
12
8
  connect() {
13
9
  this.self = this.element
10
+ this.autoCloseToast(this.self)
14
11
 
15
- if (this.self.classList.contains(TOAST_AUTO_CLOSE_CLASS) ||
16
- this.self.hasAttribute('data-pb-auto-close')) {
17
- this.autoCloseToast(this.self)
18
- }
19
-
20
- if (this.self.classList.contains(TOAST_REMOVE_CLASS) ||
21
- this.self.hasAttribute('data-pb-remove-toast')) {
22
- this.self.addEventListener('click', () => {
23
- this.removeToast(this.self)
24
- })
25
- }
12
+ this.self.addEventListener('click', () => {
13
+ this.removeToast(this.self)
14
+ })
26
15
  }
27
16
 
28
17
  removeToast(elem) {
29
- if (elem.autoCloseTimeout) {
30
- clearTimeout(elem.autoCloseTimeout)
31
- elem.autoCloseTimeout = null
32
- }
33
-
34
18
  elem.parentNode.removeChild(elem)
35
19
  }
36
20
 
37
21
  autoCloseToast(element) {
38
- let duration
22
+ const classListValues = element.classList.value
23
+ const hasAutoCloseClass = classListValues.includes('auto_close')
39
24
 
40
- if (element.classList.contains(TOAST_AUTO_CLOSE_CLASS)) {
41
- const autoCloseClass = Array.from(element.classList)
42
- .find(cls => cls.startsWith('auto_close_'))
43
-
44
- if (autoCloseClass) {
45
- duration = parseInt(autoCloseClass.split('_')[2])
46
- }
47
- }
25
+ if (hasAutoCloseClass) {
26
+ const classList = classListValues.split(' ')
27
+ const autoCloseValue = classList[classList.length - 1].split('_')[2]
28
+ const autoCloseIntValue = parseInt(autoCloseValue)
48
29
 
49
- if (!duration) {
50
- duration = parseInt(element.getAttribute('data-pb-auto-close'))
51
- }
52
-
53
- if (duration) {
54
- if (element.autoCloseTimeout) {
55
- clearTimeout(element.autoCloseTimeout)
56
- }
57
-
58
- element.autoCloseTimeout = setTimeout(() => {
30
+ setTimeout(() => {
59
31
  this.removeToast(element)
60
- }, duration)
61
- }
62
- }
63
-
64
- disconnect() {
65
- if (this.element.autoCloseTimeout) {
66
- clearTimeout(this.element.autoCloseTimeout)
32
+ }, autoCloseIntValue)
67
33
  }
68
34
  }
69
35
  }