playbook_ui 16.12.0.pre.alpha.ATtest18202 → 16.12.0.pre.alpha.PLAY3072railstextinputrequiredaddonmaskingbugs18157

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 (57) 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/advanced_table.test.jsx +1 -2
  4. data/app/pb_kits/playbook/pb_advanced_table/docs/_playground.json +0 -8
  5. data/app/pb_kits/playbook/pb_advanced_table/docs/_playground.overrides.json +0 -8
  6. data/app/pb_kits/playbook/pb_advanced_table/kit.schema.json +1 -1
  7. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +3 -18
  8. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +0 -2
  9. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +0 -1
  10. data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +0 -35
  11. data/app/pb_kits/playbook/pb_dropdown/hooks/useHandleOnKeydown.tsx +1 -18
  12. data/app/pb_kits/playbook/pb_dropdown/index.js +0 -34
  13. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +1 -8
  14. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +6 -7
  15. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate_addon_currency.html.erb +123 -0
  16. data/app/pb_kits/playbook/pb_form/docs/example.yml +1 -0
  17. data/app/pb_kits/playbook/pb_icon_button/_icon_button.scss +78 -0
  18. data/app/pb_kits/playbook/pb_icon_button/_icon_button.tsx +75 -0
  19. data/app/pb_kits/playbook/pb_icon_button/docs/_icon_button_click.jsx +13 -0
  20. data/app/pb_kits/playbook/pb_icon_button/docs/_icon_button_default.html.erb +2 -0
  21. data/app/pb_kits/playbook/pb_icon_button/docs/_icon_button_default.jsx +17 -0
  22. data/app/pb_kits/playbook/pb_icon_button/docs/_icon_button_sizes.html.erb +27 -0
  23. data/app/pb_kits/playbook/pb_icon_button/docs/_icon_button_sizes.jsx +63 -0
  24. data/app/pb_kits/playbook/pb_icon_button/docs/_playground.json +135 -0
  25. data/app/pb_kits/playbook/pb_icon_button/docs/_playground.overrides.json +131 -0
  26. data/app/pb_kits/playbook/pb_icon_button/docs/example.yml +10 -0
  27. data/app/pb_kits/playbook/pb_icon_button/docs/index.js +3 -0
  28. data/app/pb_kits/playbook/pb_icon_button/icon_button.html.erb +17 -0
  29. data/app/pb_kits/playbook/pb_icon_button/icon_button.rb +25 -0
  30. data/app/pb_kits/playbook/pb_icon_button/icon_button.test.jsx +39 -0
  31. data/app/pb_kits/playbook/pb_icon_button/kit.schema.json +116 -0
  32. data/app/pb_kits/playbook/pb_timestamp/_timestamp.tsx +2 -2
  33. data/app/pb_kits/playbook/pb_timestamp/docs/_timestamp_updated.html.erb +0 -19
  34. data/app/pb_kits/playbook/pb_timestamp/docs/_timestamp_updated.jsx +0 -21
  35. data/app/pb_kits/playbook/pb_timestamp/docs/_timestamp_updated.md +1 -3
  36. data/app/pb_kits/playbook/pb_timestamp/timestamp.test.js +0 -42
  37. data/dist/chunks/{_pb_line_graph-CVbptQfB.js → _pb_line_graph-BL9pXDkX.js} +1 -1
  38. data/dist/chunks/_typeahead-BrQbahtd.js +5 -0
  39. data/dist/chunks/{globalProps-CTGiwXyh.js → globalProps-BQqb1ZGy.js} +1 -1
  40. data/dist/chunks/lib-CYrXqTdQ.js +29 -0
  41. data/dist/chunks/time_picker_helper-CVMV9ync.js +1 -0
  42. data/dist/chunks/vendor.js +23 -3
  43. data/dist/menu.yml +6 -0
  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/version.rb +1 -1
  48. metadata +23 -13
  49. data/app/pb_kits/playbook/pb_advanced_table/docs/_description_react.md +0 -6
  50. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default_value_with_autocomplete.html.erb +0 -10
  51. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default_value_with_autocomplete.jsx +0 -35
  52. data/dist/chunks/StickyLayoutHelper-DCzCcWUN.js +0 -1
  53. data/dist/chunks/_detail-ChXSAxU0.js +0 -5
  54. data/dist/chunks/_table-CUOE7A8S.js +0 -1
  55. data/dist/chunks/_typeahead-D1_wsX6x.js +0 -1
  56. data/dist/chunks/lib-qubCkjWq.js +0 -29
  57. data/dist/chunks/time_picker_helper-Bx8nzyM8.js +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e84fbc3f83d86ebdfea1f090f7fa2e4e9d0327be6d9f5299f9e1ae76713abcd9
4
- data.tar.gz: 3aa90c06a1cde5a01a74e6adff6afb04ed391074600617cbc763c609afce4cbe
3
+ metadata.gz: 808c3d5ab3e82537f24aeac0f1cb2e402a30d4afae94e25825bfcc0cbe8feda0
4
+ data.tar.gz: '084bedeca9f2498f50b6cac05965995b7cc91021f4eebd6e5e149caf79583a25'
5
5
  SHA512:
6
- metadata.gz: 0aa917e55ff4285b13c44ccf8b50acd70b6aea6ec31e3316ab51880f90b86b1f16a5c62135d59cdd3545f3084806f5f3ffbb7dccbca6317e2f44d56a4c8d71d2
7
- data.tar.gz: 3387ad8a04404ad1f0c3b66dc47b8ea45eccff7f2514de0ea912af94ce486210806a0e297b32eeb96ebe0a3857d66638c2eeaf1921692e8a26c0521e69c9a278
6
+ metadata.gz: bca00447e61c8e7ed070c1ea0fe1fa3ade2c78d3ac3a2ecc2a294d9fb25464575f299aa231f8c4808344197e741daed75e5cd7d6b408c10fe16a39327953af13
7
+ data.tar.gz: 52fced873ada1be081e91b4b01dbf166ed0f41e17545e86d467a1168c0d0526660e0712c04921718db3201c540c19341403873f20fe816f0205f90ae445c1cbc
@@ -41,6 +41,7 @@
41
41
  @import 'pb_highlight/highlight';
42
42
  @import 'pb_home_address_street/home_address_street';
43
43
  @import 'pb_icon/icon';
44
+ @import 'pb_icon_button/icon_button';
44
45
  @import 'pb_icon_circle/icon_circle';
45
46
  @import 'pb_icon_stat_value/icon_stat_value';
46
47
  @import 'pb_icon_value/icon_value';
@@ -1,8 +1,7 @@
1
1
  import React, {useState} from "react"
2
2
  import { render, screen, waitFor } from "../utilities/test-utils"
3
3
 
4
- import { AdvancedTable } from "playbook-ui/advanced-table"
5
- import { Pill, colors } from "playbook-ui"
4
+ import { AdvancedTable, Pill, colors } from "playbook-ui"
6
5
 
7
6
  global.ResizeObserver = class {
8
7
  observe() {}
@@ -259,11 +259,6 @@
259
259
  }
260
260
  },
261
261
  "hints": {
262
- "advanced_table_import": {
263
- "when": {},
264
- "message": "AdvancedTable is imported from playbook-ui/advanced-table so apps can keep TanStack out of the main playbook-ui bundle until this kit is used.",
265
- "type": "info"
266
- },
267
262
  "sorting_info": {
268
263
  "when": {
269
264
  "enableSortingRemoval": true
@@ -321,9 +316,6 @@
321
316
  "type": "info"
322
317
  }
323
318
  },
324
- "externalImports": [
325
- "import { AdvancedTable } from 'playbook-ui/advanced-table'"
326
- ],
327
319
  "structureModes": {
328
320
  "default": "implicit",
329
321
  "modes": {
@@ -1,7 +1,4 @@
1
1
  {
2
- "externalImports": [
3
- "import { AdvancedTable } from 'playbook-ui/advanced-table'"
4
- ],
5
2
  "structureModes": {
6
3
  "default": "implicit",
7
4
  "modes": {
@@ -314,11 +311,6 @@
314
311
  }
315
312
  },
316
313
  "hints": {
317
- "advanced_table_import": {
318
- "when": {},
319
- "message": "AdvancedTable is imported from playbook-ui/advanced-table so apps can keep TanStack out of the main playbook-ui bundle until this kit is used.",
320
- "type": "info"
321
- },
322
314
  "sorting_info": {
323
315
  "when": {
324
316
  "enableSortingRemoval": true
@@ -312,7 +312,7 @@
312
312
  "globalProps": true,
313
313
  "usage": {
314
314
  "react": {
315
- "import": "import { AdvancedTable } from 'playbook-ui/advanced-table'",
315
+ "import": "import { AdvancedTable } from 'playbook-ui'",
316
316
  "example": "<AdvancedTable columnGroupBorderColor=\"text_lt_default\" enableToggleExpansion=\"all\"></AdvancedTable>"
317
317
  },
318
318
  "rails": {
@@ -203,6 +203,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
203
203
  : undefined;
204
204
  const errorId = error ? `${selectId}-error` : undefined;
205
205
 
206
+ const [filterItem, setFilterItem] = useState("");
206
207
  const initialSelected = useMemo(() => {
207
208
  // Handle quickpick variant with string defaultValue (e.g., "This Month")
208
209
  if (variant === "quickpick" && typeof defaultValue === "string" && defaultValue) {
@@ -225,13 +226,6 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
225
226
  initialSelected
226
227
  );
227
228
 
228
- // Autocomplete displays the selection in the input; seed from defaultValue
229
- const [filterItem, setFilterItem] = useState(() => {
230
- if (!autocomplete || multiSelect) return "";
231
- if (Array.isArray(initialSelected)) return "";
232
- return (initialSelected as GenericObject)?.label || "";
233
- });
234
-
235
229
  const filterResetDefaultSerialized = useMemo(
236
230
  () => serializeDropdownFilterResetDefault(variant, multiSelect, defaultValue, dropdownOptions),
237
231
  [variant, multiSelect, defaultValue, dropdownOptions]
@@ -326,17 +320,10 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
326
320
  }, [optionsWithBlankSelection, selectedArray, multiSelect]);
327
321
 
328
322
  const filteredOptions = useMemo(() => {
329
- // When the input shows the selected label, do not filter the list down to that one option
330
- const selectedLabel =
331
- !multiSelect &&
332
- !Array.isArray(selected) &&
333
- (selected as GenericObject)?.label;
334
- const filterText =
335
- selectedLabel && filterItem === selectedLabel ? "" : filterItem;
336
323
  return availableOptions.filter((opt: GenericObject) =>
337
- String(opt.label).toLowerCase().includes(filterText.toLowerCase())
324
+ String(opt.label).toLowerCase().includes(filterItem.toLowerCase())
338
325
  );
339
- }, [availableOptions, filterItem, multiSelect, selected]);
326
+ }, [availableOptions, filterItem]);
340
327
 
341
328
  // For keyboard accessibility: Set focus within dropdown to selected item if it exists
342
329
  useEffect(() => {
@@ -473,7 +460,6 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
473
460
  setSelected({});
474
461
  onSelect && onSelect(null);
475
462
  setFocusedOptionIndex(-1);
476
- setFilterItem("");
477
463
 
478
464
  // Clear linked DatePickers as well if this is a quickpick variant with controls
479
465
  if (variant === "quickpick") {
@@ -607,7 +593,6 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
607
593
  onSelect,
608
594
  optionsWithBlankSelection,
609
595
  selected,
610
- setFilterItem,
611
596
  setFocusedOptionIndex,
612
597
  setIsDropDownClosed,
613
598
  setIsInputFocused,
@@ -22,7 +22,6 @@ examples:
22
22
  - dropdown_error: Dropdown with Error
23
23
  - dropdown_default_value: Default Value
24
24
  - dropdown_multi_select_with_default: Multi Select Default Value
25
- - dropdown_default_value_with_autocomplete: Default Value with Autocomplete
26
25
  - dropdown_blank_selection: Blank Selection
27
26
  - dropdown_with_placeholder: Placeholder
28
27
  - dropdown_separators_hidden: Separators Hidden
@@ -64,7 +63,6 @@ examples:
64
63
  - dropdown_error: Dropdown with Error
65
64
  - dropdown_default_value: Default Value
66
65
  - dropdown_multi_select_with_default: Multi Select Default Value
67
- - dropdown_default_value_with_autocomplete: Default Value with Autocomplete
68
66
  - dropdown_blank_selection: Blank Selection
69
67
  - dropdown_with_placeholder: Placeholder
70
68
  - dropdown_clear_selection: Clear Selection
@@ -37,4 +37,3 @@ export { default as DropdownDisabledOption } from "./_dropdown_disabled_option.j
37
37
  export { default as DropdownCustomDisplayDisabledOption } from "./_dropdown_custom_display_disabled_option.jsx";
38
38
  export { default as DropdownDisabled } from "./_dropdown_disabled.jsx";
39
39
  export { default as DropdownGroupedOptions } from "./_dropdown_grouped_options.jsx";
40
- export { default as DropdownDefaultValueWithAutocomplete } from './_dropdown_default_value_with_autocomplete.jsx'
@@ -350,41 +350,6 @@ test("autocomplete prop to render input", () => {
350
350
  expect(input).toBeInTheDocument()
351
351
  })
352
352
 
353
- test("defaultValue renders in autocomplete input", () => {
354
- render(
355
- <Dropdown
356
- autocomplete
357
- data={{ testid: testId }}
358
- defaultValue={options[2]}
359
- options={options}
360
- />
361
- )
362
-
363
- const kit = screen.getByTestId(testId)
364
- const input = kit.querySelector('.dropdown_input')
365
- expect(input).toBeInTheDocument()
366
- expect(input).toHaveValue(options[2].label)
367
- expect(input).toHaveAttribute("placeholder", "")
368
- })
369
-
370
- test("autocomplete defaultValue still shows all options when opened", () => {
371
- render(
372
- <Dropdown
373
- autocomplete
374
- data={{ testid: testId }}
375
- defaultValue={options[2]}
376
- options={options}
377
- />
378
- )
379
-
380
- const kit = screen.getByTestId(testId)
381
- const optionEls = kit.querySelectorAll('.pb_dropdown_option_list, .pb_dropdown_option_selected')
382
- expect(optionEls).toHaveLength(options.length)
383
- expect(kit).toHaveTextContent(options[0].label)
384
- expect(kit).toHaveTextContent(options[1].label)
385
- expect(kit).toHaveTextContent(options[2].label)
386
- })
387
-
388
353
  test("searchbar prop to render TextInput in container", () => {
389
354
  render (
390
355
  <Dropdown
@@ -6,13 +6,11 @@ export const useHandleOnKeyDown = () => {
6
6
 
7
7
  const {
8
8
  autocomplete,
9
- filterItem,
10
9
  filteredOptions,
11
10
  focusedOptionIndex,
12
11
  handleBackspace,
13
12
  handleOptionClick,
14
13
  selected,
15
- setFilterItem,
16
14
  setFocusedOptionIndex,
17
15
  setIsDropDownClosed,
18
16
  }= useContext(DropdownContext)
@@ -42,22 +40,7 @@ const {
42
40
  return (e: React.KeyboardEvent) => {
43
41
 
44
42
  if (e.key !== "Tab" && autocomplete && selected && selected.label) {
45
- // Only when the input is showing the selected label (e.g. seeded defaultValue)
46
- const replacingSelectedLabel = filterItem === selected.label;
47
43
  handleBackspace();
48
-
49
- if (replacingSelectedLabel && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
50
- e.preventDefault();
51
- setFilterItem?.(e.key);
52
- setIsDropDownClosed(false);
53
- return;
54
- }
55
-
56
- if (replacingSelectedLabel && (e.key === "Backspace" || e.key === "Delete")) {
57
- e.preventDefault();
58
- setFilterItem?.("");
59
- return;
60
- }
61
44
  }
62
45
 
63
46
  switch (e.key) {
@@ -89,4 +72,4 @@ const {
89
72
  break;
90
73
  }
91
74
  }
92
- };
75
+ };
@@ -541,30 +541,6 @@ export default class PbDropdown extends PbEnhancedElement {
541
541
  }
542
542
  }
543
543
 
544
- // Keep the selected label in the autocomplete input, but do not treat it as a search filter
545
- resetOptionFilterForSelectedLabel() {
546
- if (this.isMultiSelect || !this.searchInput) return;
547
-
548
- const selectedOption = Array.from(this.queryAllOptions()).find((opt) =>
549
- opt.classList.contains("pb_dropdown_option_selected"),
550
- );
551
- if (!selectedOption) return;
552
-
553
- try {
554
- const selectedLabel = JSON.parse(
555
- selectedOption.dataset.dropdownOptionLabel,
556
- ).label;
557
- if (this.searchInput.value !== selectedLabel) return;
558
-
559
- this.queryAllOptions().forEach((opt) => {
560
- opt.style.display = "";
561
- });
562
- this.removeNoOptionsMessage();
563
- } catch {
564
- // ignore invalid option payloads
565
- }
566
- }
567
-
568
544
  showNoOptionsMessage() {
569
545
  if (this.target?.querySelector(".dropdown_no_options")) return;
570
546
 
@@ -947,9 +923,6 @@ export default class PbDropdown extends PbEnhancedElement {
947
923
 
948
924
  showElement(elem) {
949
925
  if (this.isDisabled) return;
950
-
951
- // When autocomplete is showing the selected label (not an active search), show all options
952
- this.resetOptionFilterForSelectedLabel();
953
926
 
954
927
  this.ensureFloatingPortalConfig();
955
928
  if (this.useMenuPortal) {
@@ -1123,13 +1096,6 @@ export default class PbDropdown extends PbEnhancedElement {
1123
1096
  const optionData = JSON.parse(selectedOption.dataset.dropdownOptionLabel);
1124
1097
  this.setTriggerElementText(optionData.label);
1125
1098
 
1126
- // Autocomplete trigger has no [data-dropdown-trigger-display]; set the input value instead
1127
- const autocompleteInput =
1128
- this.searchInput || this.element.querySelector(SEARCH_INPUT_SELECTOR);
1129
- if (autocompleteInput) {
1130
- autocompleteInput.value = optionData.label;
1131
- }
1132
-
1133
1099
  // Handle quickpick variant: populate start/end date hidden inputs and sync DatePickers
1134
1100
  if (optionData.formatted_start_date && optionData.formatted_end_date) {
1135
1101
  const startDateId = this.element.dataset.startDateId;
@@ -52,15 +52,8 @@ const DropdownOption = (props: DropdownOptionProps) => {
52
52
  } = useContext(DropdownContext);
53
53
 
54
54
  const isItemMatchingFilter = (option: GenericObject | undefined) => {
55
- // When the input is only showing the selected label (e.g. seeded defaultValue), do not filter
56
- const selectedLabel =
57
- !multiSelect &&
58
- !Array.isArray(selected) &&
59
- (selected as GenericObject)?.label;
60
- const filterText =
61
- selectedLabel && filterItem === selectedLabel ? "" : filterItem;
62
55
  const label = typeof option?.label === 'string' ? option.label.toLowerCase() : option?.label;
63
- return String(label).toLowerCase().includes(filterText.toLowerCase());
56
+ return String(label).toLowerCase().includes(filterItem.toLowerCase());
64
57
  }
65
58
 
66
59
  // When multiSelect, then if an option is selected, remove from dropdown
@@ -103,9 +103,8 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
103
103
  "Select..."
104
104
  );
105
105
 
106
- const hideJoinedLabelInBody = Boolean(autocomplete && filterItem === joinedLabels);
107
106
  const defaultDisplayPlaceholder = joinedLabels
108
- ? (hideJoinedLabelInBody ? "" : joinedLabels)
107
+ ? joinedLabels
109
108
  : autocomplete
110
109
  ? ""
111
110
  : placeholder
@@ -253,9 +252,9 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
253
252
  }}
254
253
  key={`${isDropDownClosed ? "chevron-down" : "chevron-up"}`}
255
254
  >
256
- {(!blankSelection || selected?.value !== optionsWithBlankSelection?.[0]?.value) &&
257
- clearable !== false &&
258
- selectedArray.length > 0 ? (
255
+ {(!blankSelection || selected?.value !== optionsWithBlankSelection?.[0]?.value) && (
256
+ <>
257
+ {clearable !== false && selectedArray.length > 0 && (
259
258
  <div onClick={(e)=>{e.stopPropagation(); !disabled && handleBackspace()}}>
260
259
  <Icon
261
260
  cursor={disabled ? "default" : "pointer"}
@@ -265,8 +264,8 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
265
264
  size="sm"
266
265
  />
267
266
  </div>
268
- ) : (
269
- <></>
267
+ )}
268
+ </>
270
269
  )}
271
270
  <Icon
272
271
  cursor={disabled ? "default" : "pointer"}
@@ -0,0 +1,123 @@
1
+ <%= pb_rails("title", props: { text: "Demo 1: Mask + Add On Hidden Input/Submission"})%>
2
+ <%= pb_form_with(scope: :transaction, method: :get, url: "", validate: true, data: { addon_currency_example: true }) do |form| %>
3
+ <%= pb_rails("text_input", props: {
4
+ label: "pb_rails Text Input Currency Mask + Add On",
5
+ name: "transaction[amount]",
6
+ id: "transaction_amount",
7
+ mask: "currency",
8
+ add_on: { icon: "dollar-sign", alignment: "left", border: false },
9
+ placeholder: "$0.00",
10
+ required: true,
11
+ }) %>
12
+ <%= form.text_field :fee, props: { label: "Form Builder Text Input Currency Mask + Add On", placeholder: "$0.00", mask: "currency", add_on: { icon: "percent", alignment: "right", border: true }, required: true } %>
13
+
14
+ <%= pb_rails("text_input", props: {
15
+ label: "pb_rails Text Input ZIP Code Mask + Add On",
16
+ name: "transaction[zip_code]",
17
+ id: "transaction_zip_code",
18
+ mask: "zip_code",
19
+ add_on: { icon: "map-marker-alt", alignment: "left", border: false },
20
+ placeholder: "12345",
21
+ required: true,
22
+ }) %>
23
+ <%= form.text_field :zip_code_builder, props: { label: "Form Builder Text Input ZIP Code Mask + Add On", mask: "zip_code", add_on: { icon: "map-marker-alt", alignment: "right", border: true }, placeholder: "12345", required: true } %>
24
+
25
+ <%= pb_rails("text_input", props: {
26
+ label: "pb_rails Text Input Postal Code Mask + Add On",
27
+ name: "transaction[postal_code]",
28
+ id: "transaction_postal_code",
29
+ mask: "postal_code",
30
+ add_on: { icon: "mail-bulk", alignment: "left", border: false },
31
+ placeholder: "12345-6789",
32
+ required: true,
33
+ }) %>
34
+ <%= form.text_field :postal_code_builder, props: { label: "Form Builder Text Input Postal Code Mask + Add On", mask: "postal_code", add_on: { icon: "mail-bulk", alignment: "right", border: true }, placeholder: "12345-6789", required: true } %>
35
+
36
+ <%= pb_rails("text_input", props: {
37
+ label: "pb_rails Text Input SSN Mask + Add On",
38
+ name: "transaction[ssn]",
39
+ id: "transaction_ssn",
40
+ mask: "ssn",
41
+ add_on: { icon: "id-badge", alignment: "left", border: false },
42
+ placeholder: "123-45-6789",
43
+ required: true,
44
+ }) %>
45
+ <%= form.text_field :ssn_builder, props: { label: "Form Builder Text Input SSN Mask + Add On", mask: "ssn", add_on: { icon: "id-badge", alignment: "right", border: true }, placeholder: "123-45-6789", required: true } %>
46
+
47
+ <%= pb_rails("text_input", props: {
48
+ label: "pb_rails Text Input Credit Card Mask + Add On",
49
+ name: "transaction[credit_card]",
50
+ id: "transaction_credit_card",
51
+ mask: "credit_card",
52
+ add_on: { icon: "credit-card", alignment: "left", border: false },
53
+ placeholder: "1234 5678 9012 3456",
54
+ required: true,
55
+ }) %>
56
+ <%= form.text_field :credit_card_builder, props: { label: "Form Builder Text Input Credit Card Mask + Add On", mask: "credit_card", add_on: { icon: "credit-card", alignment: "right", border: true }, placeholder: "1234 5678 9012 3456", required: true } %>
57
+
58
+ <%= pb_rails("text_input", props: {
59
+ label: "pb_rails Text Input CVV Mask + Add On",
60
+ name: "transaction[cvv]",
61
+ id: "transaction_cvv",
62
+ mask: "cvv",
63
+ add_on: { icon: "lock", alignment: "left", border: false },
64
+ placeholder: "123",
65
+ required: true,
66
+ }) %>
67
+ <%= form.text_field :cvv_builder, props: { label: "Form Builder Text Input CVV Mask + Add On", mask: "cvv", add_on: { icon: "lock", alignment: "right", border: true }, placeholder: "123", required: true } %>
68
+
69
+ <%= form.text_field :reference, props: { label: "Form Builder Text Input Add On NO MASK", add_on: { icon: "frog", alignment: "left", border: true }, required: true } %>
70
+
71
+ <%= form.actions do |action| %>
72
+ <%= action.submit %>
73
+ <%= action.button props: { type: "reset", text: "Cancel", variant: "secondary" } %>
74
+ <% end %>
75
+ <% end %>
76
+ <%= pb_rails("card", props: { margin_top: "sm" }) do %>
77
+ <%= pb_rails("caption", props: { text: "Submitted form data", margin_bottom: "xs" }) %>
78
+ <%= pb_rails("body", props: { text: "Submit the form to list every param the browser sends below. The same data is logged to the console. All fields are required." }) %>
79
+ <pre data-addon-currency-example-output></pre>
80
+ <% end %>
81
+
82
+ <%= javascript_tag defer: "defer" do %>
83
+ document.addEventListener("submit", function(event) {
84
+ const form = event.target
85
+ if (!form.dataset || !form.dataset.addonCurrencyExample) return
86
+
87
+ // Keep the example on the page so the params stay visible.
88
+ event.preventDefault()
89
+
90
+ const submitted = Array.from(new FormData(form).entries())
91
+ .filter(([name]) => name && !["utf8", "_method", "authenticity_token"].includes(name))
92
+
93
+ const output = document.querySelector("[data-addon-currency-example-output]")
94
+ output.textContent = submitted.length ?
95
+ submitted.map(([name, value]) => `${name}: ${JSON.stringify(value)}`).join("\n") :
96
+ "Nothing was submitted."
97
+
98
+ console.log("Submitted form data:", Object.fromEntries(submitted))
99
+ })
100
+ <% end %>
101
+
102
+ <%= pb_rails("title", props: { text: "Demo 2: Location of Validation Message"})%>
103
+ <%= pb_form_with(scope: :example, method: :get, url: "", validate: true) do |form| %>
104
+ <%= form.text_field :example_text_field_validation, props: { label: "Form Builder Text Input Add On", add_on: { icon: "frog", alignment: 'right', border: true }, required: true } %>
105
+ <%= form.text_field :example_text_field_validation_2, props: { label: "Form Builder Text Input Currency Mask + Add On", mask: "currency", placeholder:"$0.00", add_on: { icon: "frog", alignment: 'left', border: false }, required: true } %>
106
+ <%= pb_rails("text_input", props: {
107
+ label: "pb_rails Text Input Add On",
108
+ add_on: { icon: "frog", alignment: 'left', border: false },
109
+ required: true,
110
+ }) %>
111
+ <%= pb_rails("text_input", props: {
112
+ label: "pb_rails Text Input Currency + Add On",
113
+ mask: "currency",
114
+ add_on: { icon: "frog", alignment: 'right', border: true },
115
+ placeholder:"$0.00",
116
+ required: true,
117
+ }) %>
118
+
119
+ <%= form.actions do |action| %>
120
+ <%= action.submit %>
121
+ <%= action.button props: { type: "reset", text: "Cancel", variant: "secondary" } %>
122
+ <% end %>
123
+ <% end %>
@@ -1,6 +1,7 @@
1
1
  examples:
2
2
 
3
3
  rails:
4
+ - form_form_with_validate_addon_currency: Validation + Add On + Currency Mask
4
5
  - form_form_with: Default
5
6
  - form_form_with_validate: Default + Validation
6
7
  - form_form_with_validation_msg: Validation + Custom Validation Message
@@ -0,0 +1,78 @@
1
+ $icon_colors: (
2
+ default: $text_lt_default,
3
+ link: $primary_action
4
+ );
5
+
6
+ @mixin icon_button_variant($variant) {
7
+ .icon_button_icon {
8
+ color: map-get($icon_colors, $variant);
9
+ }
10
+ }
11
+
12
+ .pb_icon_button_kit_default,
13
+ .pb_icon_button_kit_link {
14
+ width: fit-content;
15
+ height: fit-content;
16
+
17
+ &:hover {
18
+ .pb_button_kit {
19
+ background-color: $bg_light;
20
+ }
21
+ .icon_button_icon {
22
+ color: $primary_action;
23
+ }
24
+ }
25
+
26
+ &:active {
27
+ .pb_button_kit {
28
+ background-color: $bg_light;
29
+ }
30
+ .icon_button_icon {
31
+ color: $primary_action;
32
+ }
33
+ }
34
+
35
+ &:hover:active {
36
+ .pb_button_kit {
37
+ background-color: $bg_light;
38
+ }
39
+ .icon_button_icon {
40
+ color: $text_lt_default;
41
+ }
42
+ }
43
+
44
+ &:focus {
45
+ outline: 2px solid $primary_action;
46
+ border-radius: 8px;
47
+ }
48
+
49
+ .pb_button_kit {
50
+ min-height: 0;
51
+ background: none;
52
+ position: relative;
53
+ display: flex;
54
+ justify-content: center;
55
+ align-items: center;
56
+ flex-shrink: 0;
57
+ flex-grow: 0;
58
+ width: fit-content;
59
+ height: fit-content;
60
+ line-height: normal;
61
+ flex-basis: auto;
62
+ min-width: 0;
63
+ padding: 0;
64
+
65
+ .icon_button_icon {
66
+ display: block;
67
+ vertical-align: middle;
68
+ }
69
+ }
70
+ }
71
+
72
+ .pb_icon_button_kit_default {
73
+ @include icon_button_variant(default);
74
+ }
75
+
76
+ .pb_icon_button_kit_link {
77
+ @include icon_button_variant(link);
78
+ }
@@ -0,0 +1,75 @@
1
+
2
+ import React from 'react'
3
+ import classnames from 'classnames'
4
+ import { buildAriaProps, buildCss, buildDataProps, noop } from '../utilities/props'
5
+ import { globalProps } from '../utilities/globalProps'
6
+
7
+ import Button from '../pb_button/_button'
8
+ import Icon from '../pb_icon/_icon'
9
+ import { IconSizes } from "../pb_icon/_icon"
10
+
11
+ type IconButtonProps = {
12
+ aria?: { [key: string]: string },
13
+ className?: string,
14
+ data?: { [key: string]: string },
15
+ htmlType?: 'submit' | 'reset' | 'button' | undefined,
16
+ icon?: string,
17
+ id?: string,
18
+ link?: string,
19
+ newWindow?: boolean,
20
+ onClick?: React.MouseEventHandler<HTMLElement>,
21
+ size?: IconSizes,
22
+ target?: string,
23
+ variant?: 'default' | 'link',
24
+ }
25
+
26
+ const IconButton = (props: IconButtonProps) => {
27
+ const {
28
+ aria = {},
29
+ className,
30
+ data = {},
31
+ htmlType = 'button',
32
+ icon = 'bars',
33
+ id,
34
+ link,
35
+ newWindow = false,
36
+ onClick = noop,
37
+ size = "2x",
38
+ target,
39
+ variant = "default",
40
+ } = props
41
+
42
+ const ariaProps = buildAriaProps(aria)
43
+ const dataProps = buildDataProps(data)
44
+ const classes = classnames(buildCss('pb_icon_button_kit', variant), globalProps(props), className)
45
+
46
+ return (
47
+ <div
48
+ {...ariaProps}
49
+ {...dataProps}
50
+ className={classes}
51
+ id={id}
52
+ >
53
+ <Button
54
+ aria={aria}
55
+ borderRadius="xs"
56
+ htmlType={htmlType}
57
+ link={link}
58
+ newWindow={newWindow}
59
+ onClick={onClick}
60
+ target={target}
61
+ >
62
+ <Icon
63
+ className="icon_button_icon"
64
+ fixedWidth
65
+ icon={icon}
66
+ paddingX="xxs"
67
+ paddingY="xs"
68
+ size={size}
69
+ />
70
+ </Button>
71
+ </div>
72
+ )
73
+ }
74
+
75
+ export default IconButton
@@ -0,0 +1,13 @@
1
+ import React from 'react'
2
+ import IconButton from '../../pb_icon_button/_icon_button'
3
+
4
+ const IconButtonClick = (props) => (
5
+ <div>
6
+ <IconButton
7
+ {...props}
8
+ onClick={() => alert('Click!')}
9
+ />
10
+ </div>
11
+ )
12
+
13
+ export default IconButtonClick
@@ -0,0 +1,2 @@
1
+ <%= pb_rails("icon_button") %>
2
+ <%= pb_rails("icon_button", props: {variant: "link", margin_top: "md" }) %>