playbook_ui 13.30.0 → 13.31.0.pre.alpha.PBNTR342navtabbing3230

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta_sort.html.erb +59 -0
  3. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta_sort.md +6 -0
  4. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +3 -2
  5. data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +4 -4
  6. data/app/pb_kits/playbook/pb_button/_button.scss +3 -3
  7. data/app/pb_kits/playbook/pb_button/_button_mixins.scss +3 -2
  8. data/app/pb_kits/playbook/pb_caption/_caption_mixin.scss +1 -1
  9. data/app/pb_kits/playbook/pb_card/_card.tsx +4 -3
  10. data/app/pb_kits/playbook/pb_collapsible/index.js +6 -1
  11. data/app/pb_kits/playbook/pb_dashboard/commonSettings.js +1 -1
  12. data/app/pb_kits/playbook/pb_dashboard/pbChartsDarkTheme.ts +1 -1
  13. data/app/pb_kits/playbook/pb_dashboard/pbChartsLightTheme.ts +1 -1
  14. data/app/pb_kits/playbook/pb_date_time_stacked/docs/_date_time_stacked_default_swift.md +33 -0
  15. data/app/pb_kits/playbook/pb_date_time_stacked/docs/_date_time_stacked_props_swift.md +18 -0
  16. data/app/pb_kits/playbook/pb_date_time_stacked/docs/example.yml +6 -1
  17. data/app/pb_kits/playbook/pb_date_year_stacked/docs/_date_year_stacked_default.jsx +4 -1
  18. data/app/pb_kits/playbook/pb_draggable/_draggable.scss +2 -4
  19. data/app/pb_kits/playbook/pb_draggable/context/index.tsx +84 -50
  20. data/app/pb_kits/playbook/pb_draggable/context/types.ts +31 -0
  21. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_default.jsx +15 -19
  22. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_default.md +5 -3
  23. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_multiple_containers.jsx +5 -1
  24. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_cards.jsx +11 -4
  25. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_cards.md +8 -2
  26. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_list.jsx +2 -2
  27. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_list.md +5 -1
  28. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_selectable_list.jsx +6 -4
  29. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_selectable_list.md +5 -0
  30. data/app/pb_kits/playbook/pb_draggable/draggable.test.jsx +142 -15
  31. data/app/pb_kits/playbook/pb_draggable/subcomponents/DraggableItem.tsx +6 -5
  32. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +18 -0
  33. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +228 -218
  34. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_error.html.erb +9 -0
  35. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_error.jsx +34 -0
  36. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +2 -0
  37. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +2 -1
  38. data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +16 -12
  39. data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +9 -0
  40. data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +17 -2
  41. data/app/pb_kits/playbook/pb_dropdown/index.js +33 -4
  42. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/_fixed_confirmation_toast.tsx +9 -3
  43. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_custom_icon.html.erb +32 -0
  44. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_custom_icon.jsx +48 -0
  45. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/example.yml +2 -0
  46. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/index.js +1 -0
  47. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/fixed_confirmation_toast.rb +16 -11
  48. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/fixed_confirmation_toast.test.js +72 -0
  49. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +9 -0
  50. data/app/pb_kits/playbook/pb_form_pill/_form_pill.scss +1 -1
  51. data/app/pb_kits/playbook/pb_icon/_icon.tsx +37 -6
  52. data/app/pb_kits/playbook/pb_icon/docs/_icon_default.html.erb +1 -1
  53. data/app/pb_kits/playbook/pb_icon/docs/_icon_default.jsx +2 -3
  54. data/app/pb_kits/playbook/pb_icon/icon.rb +21 -1
  55. data/app/pb_kits/playbook/pb_icon/icon_aliases.json +39 -0
  56. data/app/pb_kits/playbook/pb_list/_list.tsx +4 -4
  57. data/app/pb_kits/playbook/pb_list/_list_item.tsx +7 -3
  58. data/app/pb_kits/playbook/pb_nav/docs/_collapsible_nav.html.erb +1 -1
  59. data/app/pb_kits/playbook/pb_nav/docs/_collapsible_nav.jsx +1 -0
  60. data/app/pb_kits/playbook/pb_nav/docs/_collapsible_nav.md +3 -1
  61. data/app/pb_kits/playbook/pb_nav/docs/_tab_nav.html.erb +12 -0
  62. data/app/pb_kits/playbook/pb_nav/docs/_tab_nav.html.md +0 -0
  63. data/app/pb_kits/playbook/pb_nav/docs/example.yml +1 -0
  64. data/app/pb_kits/playbook/pb_nav/index.js +43 -0
  65. data/app/pb_kits/playbook/pb_nav/item.html.erb +1 -1
  66. data/app/pb_kits/playbook/pb_nav/item.rb +1 -0
  67. data/app/pb_kits/playbook/pb_nav/nav.rb +9 -0
  68. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_default.html.erb +24 -0
  69. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_default.md +4 -4
  70. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_multi_directional.html.erb +11 -0
  71. data/app/pb_kits/playbook/pb_overlay/docs/example.yml +4 -0
  72. data/app/pb_kits/playbook/pb_overlay/overlay.html.erb +27 -0
  73. data/app/pb_kits/playbook/pb_overlay/overlay.rb +110 -0
  74. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +8 -23
  75. data/app/pb_kits/playbook/pb_selectable_list/_item.tsx +7 -3
  76. data/app/pb_kits/playbook/pb_selectable_list/_selectable_list.tsx +3 -3
  77. data/app/pb_kits/playbook/pb_table/table_header.html.erb +16 -2
  78. data/app/pb_kits/playbook/pb_timeline/_item.tsx +11 -10
  79. data/app/pb_kits/playbook/pb_timeline/_timeline.tsx +8 -6
  80. data/app/pb_kits/playbook/playbook-rails.js +3 -0
  81. data/app/pb_kits/playbook/tokens/_titles.scss +4 -4
  82. data/app/pb_kits/playbook/tokens/_typography.scss +10 -10
  83. data/app/pb_kits/playbook/utilities/globalProps.ts +1 -0
  84. data/dist/menu.yml +566 -472
  85. data/dist/playbook-rails.js +7 -7
  86. data/dist/reset.css +1 -1
  87. data/lib/playbook/version.rb +2 -2
  88. metadata +24 -5
@@ -8,16 +8,22 @@ const DOWN_ARROW_SELECTOR = "#dropdown_open_icon";
8
8
  const UP_ARROW_SELECTOR = "#dropdown_close_icon";
9
9
  const OPTION_SELECTOR = "[data-dropdown-option-label]";
10
10
  const CUSTOM_DISPLAY_SELECTOR = "[data-dropdown-custom-trigger]";
11
+ const INPUT_FORM_VALIDATION = "#dropdown-form-validation";
11
12
 
12
13
  export default class PbDropdown extends PbEnhancedElement {
13
14
  static get selector() {
14
15
  return DROPDOWN_SELECTOR;
15
16
  }
16
17
 
18
+ get target() {
19
+ return this.element.parentNode.querySelector(CONTAINER_SELECTOR);
20
+ }
21
+
17
22
  connect() {
18
23
  this.keyboardHandler = new PbDropdownKeyboard(this);
19
24
  this.bindEventListeners();
20
25
  this.updateArrowDisplay(false);
26
+ this.handleFormValidation();
21
27
  }
22
28
 
23
29
  bindEventListeners() {
@@ -38,9 +44,13 @@ export default class PbDropdown extends PbEnhancedElement {
38
44
  handleOptionClick(event) {
39
45
  const option = event.target.closest(OPTION_SELECTOR);
40
46
  const hiddenInput = this.element.querySelector("#dropdown-selected-option");
47
+ const inputFormValidation = this.element.querySelector(INPUT_FORM_VALIDATION);
48
+
41
49
  if (option) {
42
50
  const value = option.dataset.dropdownOptionLabel;
43
51
  hiddenInput.value = JSON.parse(value).id;
52
+ inputFormValidation.value = JSON.parse(value).id;
53
+ this.clearFormValidation(inputFormValidation);
44
54
  this.onOptionSelected(value, option);
45
55
  }
46
56
  }
@@ -103,10 +113,6 @@ export default class PbDropdown extends PbEnhancedElement {
103
113
  selectedOption.classList.add("pb_dropdown_option_selected");
104
114
  }
105
115
 
106
- get target() {
107
- return this.element.parentNode.querySelector(CONTAINER_SELECTOR);
108
- }
109
-
110
116
  showElement(elem) {
111
117
  elem.classList.remove("close");
112
118
  elem.classList.add("open");
@@ -150,4 +156,27 @@ export default class PbDropdown extends PbEnhancedElement {
150
156
  upArrow.style.display = isOpen ? "inline-block" : "none";
151
157
  }
152
158
  }
159
+
160
+ handleFormValidation() {
161
+ const inputFormValidation = this.element.querySelector(INPUT_FORM_VALIDATION);
162
+
163
+ inputFormValidation.addEventListener("invalid", function (event) {
164
+ if (inputFormValidation.hasAttribute("required") && inputFormValidation.value === "") {
165
+ event.preventDefault();
166
+ inputFormValidation.closest(".dropdown_wrapper").classList.add("error");
167
+ }
168
+ }, true);
169
+ }
170
+
171
+ clearFormValidation(input) {
172
+ if (input.checkValidity()) {
173
+ const dropdownWrapperElement = input.closest(".dropdown_wrapper");
174
+ dropdownWrapperElement.classList.remove("error");
175
+
176
+ const errorLabelElement = dropdownWrapperElement.querySelector(".pb_body_kit_negative");
177
+ if (errorLabelElement) {
178
+ errorLabelElement.remove();
179
+ }
180
+ }
181
+ }
153
182
  }
@@ -23,6 +23,7 @@ type FixedConfirmationToastProps = {
23
23
  data?: string;
24
24
  horizontal?: "right" | "left" | "center";
25
25
  htmlOptions?: { [key: string]: string | number | boolean | (VoidCallback) };
26
+ icon?: string,
26
27
  id?: string;
27
28
  multiLine?: boolean;
28
29
  onClose?: VoidCallback;
@@ -41,6 +42,7 @@ const FixedConfirmationToast = (props: FixedConfirmationToastProps): React.React
41
42
  closeable = false,
42
43
  horizontal,
43
44
  htmlOptions = {},
45
+ icon,
44
46
  multiLine = false,
45
47
  onClose = () => undefined,
46
48
  open = true,
@@ -48,14 +50,18 @@ const FixedConfirmationToast = (props: FixedConfirmationToastProps): React.React
48
50
  text,
49
51
  vertical,
50
52
  } = props;
53
+
54
+ const returnedIcon = icon || iconMap[status]
55
+ const iconClass = icon ? "custom_icon" : ""
56
+
51
57
  const css = classnames(
52
58
  `pb_fixed_confirmation_toast_kit_${status}`,
53
59
  { _multi_line: multiLine },
54
60
  { [`positioned_toast ${vertical} ${horizontal}`]: vertical && horizontal },
61
+ `${iconClass}`,
55
62
  globalProps(props),
56
63
  className
57
64
  );
58
- const icon = iconMap[status];
59
65
 
60
66
  const htmlProps = buildHtmlProps(htmlOptions);
61
67
 
@@ -86,11 +92,11 @@ const FixedConfirmationToast = (props: FixedConfirmationToastProps): React.React
86
92
  onClick={handleClick}
87
93
  {...htmlProps}
88
94
  >
89
- {icon && (
95
+ {returnedIcon && (
90
96
  <Icon
91
97
  className="pb_icon"
92
98
  fixedWidth
93
- icon={icon}
99
+ icon={returnedIcon}
94
100
  />
95
101
  )}
96
102
 
@@ -0,0 +1,32 @@
1
+ <div>
2
+ <%= pb_rails("fixed_confirmation_toast", props: {
3
+ icon: "wrench",
4
+ text: "Fix before proceeding",
5
+ status: "error",
6
+ closeable: true,
7
+ margin_bottom: "md"
8
+ })%>
9
+ </div>
10
+ <div>
11
+ <%= pb_rails("fixed_confirmation_toast", props: {
12
+ icon: "star",
13
+ text: "Thank you for completing the form!",
14
+ status: "success",
15
+ margin_bottom: "md"
16
+ })%>
17
+ </div>
18
+ <div>
19
+ <%= pb_rails("fixed_confirmation_toast", props: {
20
+ icon: "file-pdf",
21
+ text: "Saved as PDF",
22
+ status: "neutral",
23
+ margin_bottom: "md"
24
+ })%>
25
+ </div>
26
+ <div>
27
+ <%= pb_rails("fixed_confirmation_toast", props: {
28
+ icon: "arrow-down",
29
+ text: "New Messages",
30
+ status: "tip"
31
+ })%>
32
+ </div>
@@ -0,0 +1,48 @@
1
+ import React from 'react'
2
+
3
+ import FixedConfirmationToast from '../_fixed_confirmation_toast'
4
+
5
+ const FixedConfirmationToastCustomIcon = (props) => {
6
+ return (
7
+ <div>
8
+ <div>
9
+ <FixedConfirmationToast
10
+ closeable
11
+ icon="wrench"
12
+ marginBottom="md"
13
+ status="error"
14
+ text="Fix before proceeding"
15
+ {...props}
16
+ />
17
+ </div>
18
+ <div>
19
+ <FixedConfirmationToast
20
+ icon="star"
21
+ marginBottom="md"
22
+ status="success"
23
+ text="Thank you for completing the form!"
24
+ {...props}
25
+ />
26
+ </div>
27
+ <div>
28
+ <FixedConfirmationToast
29
+ icon="file-pdf"
30
+ marginBottom="md"
31
+ status="neutral"
32
+ text="Saved as PDF"
33
+ {...props}
34
+ />
35
+ </div>
36
+ <div>
37
+ <FixedConfirmationToast
38
+ icon="arrow-down"
39
+ status="tip"
40
+ text="New Messages"
41
+ {...props}
42
+ />
43
+ </div>
44
+ </div>
45
+ )
46
+ }
47
+
48
+ export default FixedConfirmationToastCustomIcon
@@ -6,6 +6,7 @@ examples:
6
6
  - fixed_confirmation_toast_close: Click to Close
7
7
  - fixed_confirmation_toast_positions: Click to Show Positions
8
8
  - fixed_confirmation_toast_children: Children
9
+ - fixed_confirmation_toast_custom_icon: Custom Icon
9
10
 
10
11
  react:
11
12
  - fixed_confirmation_toast_default: Default
@@ -14,6 +15,7 @@ examples:
14
15
  - fixed_confirmation_toast_positions: Click to Show Positions
15
16
  - fixed_confirmation_toast_auto_close: Click to Show Auto Close
16
17
  - fixed_confirmation_toast_children: Children
18
+ - fixed_confirmation_toast_custom_icon: Custom Icon
17
19
 
18
20
  swift:
19
21
  - fixed_confirmation_toast_default_swift: Default
@@ -4,3 +4,4 @@ export { default as FixedConfirmationToastClose } from './_fixed_confirmation_to
4
4
  export { default as FixedConfirmationToastPositions } from './_fixed_confirmation_toast_positions.jsx'
5
5
  export { default as FixedConfirmationToastAutoClose } from './_fixed_confirmation_toast_auto_close.jsx'
6
6
  export { default as FixedConfirmationToastChildren } from './_fixed_confirmation_toast_children.jsx'
7
+ export { default as FixedConfirmationToastCustomIcon } from './_fixed_confirmation_toast_custom_icon.jsx'
@@ -20,6 +20,7 @@ module Playbook
20
20
  values: [nil, "top", "bottom"],
21
21
  default: nil
22
22
  prop :auto_close, type: Playbook::Props::Number
23
+ prop :icon, type: Playbook::Props::String
23
24
 
24
25
  def show_text?
25
26
  text.present?
@@ -42,20 +43,24 @@ module Playbook
42
43
  end
43
44
 
44
45
  def icon_value
45
- case status
46
- when "success"
47
- "check"
48
- when "error"
49
- "exclamation-triangle"
50
- when "neutral"
51
- "info-circle"
52
- when "tip"
53
- "info-circle"
54
- end
46
+ icon || case status
47
+ when "success"
48
+ "check"
49
+ when "error"
50
+ "exclamation-triangle"
51
+ when "neutral"
52
+ "info-circle"
53
+ when "tip"
54
+ "info-circle"
55
+ end
56
+ end
57
+
58
+ def icon_class
59
+ icon.present? ? " custom_icon" : ""
55
60
  end
56
61
 
57
62
  def classname
58
- generate_classname("pb_fixed_confirmation_toast_kit", status, multi_line_class) + close_class + position_class + auto_close_class
63
+ generate_classname("pb_fixed_confirmation_toast_kit", status, multi_line_class) + close_class + position_class + auto_close_class + icon_class
59
64
  end
60
65
  end
61
66
  end
@@ -0,0 +1,72 @@
1
+ import React from 'react'
2
+ import { render, waitFor } from '../utilities/test-utils'
3
+
4
+ import { FixedConfirmationToast } from '../'
5
+
6
+ beforeEach(() => {
7
+ // Silences error logs within the test suite.
8
+ jest.spyOn(console, 'error')
9
+ // eslint-disable-next-line
10
+ console.error.mockImplementation(() => {})
11
+ })
12
+
13
+ afterEach(() => {
14
+ // eslint-disable-next-line
15
+ console.error.mockRestore()
16
+ })
17
+
18
+ test('renders with default props', () => {
19
+ const { container } = render(<FixedConfirmationToast />);
20
+ expect(container.firstChild).toBeInTheDocument();
21
+ });
22
+
23
+ test('renders with text', () => {
24
+ const { getByText } = render(<FixedConfirmationToast text="Message Text" />);
25
+ expect(getByText('Message Text')).toBeInTheDocument();
26
+ });
27
+
28
+ test('does not render if open prop is false', () => {
29
+ const { container } = render(<FixedConfirmationToast open={false} />);
30
+ expect(container.firstChild).toBeNull();
31
+ });
32
+
33
+ test('closes after autoClose duration', async () => {
34
+ jest.useFakeTimers();
35
+ const onCloseMock = jest.fn();
36
+ render(
37
+ <FixedConfirmationToast
38
+ autoClose={1000}
39
+ onClose={onCloseMock}
40
+ open
41
+ />
42
+ );
43
+
44
+ jest.advanceTimersByTime(1000);
45
+ await waitFor(() => expect(onCloseMock).toHaveBeenCalled());
46
+ });
47
+
48
+ test('renders success status with icon', () => {
49
+ const { container } = render(<FixedConfirmationToast status="success" />);
50
+ expect(container.querySelector('.pb_fixed_confirmation_toast_kit_success')).toBeInTheDocument();
51
+ expect(container.querySelector('.pb_icon')).toBeInTheDocument();
52
+ });
53
+
54
+ test('renders custom icon when provided', () => {
55
+ const { container } = render(<FixedConfirmationToast icon="wrench" />);
56
+ expect(container.querySelector('.custom_icon')).toBeInTheDocument();
57
+ });
58
+
59
+ test('renders correctly with multiLine prop', () => {
60
+ const { container } = render(<FixedConfirmationToast multiLine />);
61
+ expect(container.querySelector('._multi_line')).toBeInTheDocument();
62
+ });
63
+
64
+ test('renders position when provided', () => {
65
+ const { container } = render(
66
+ <FixedConfirmationToast
67
+ horizontal="right"
68
+ vertical="bottom"
69
+ />
70
+ );
71
+ expect(container.querySelector('.positioned_toast')).toBeInTheDocument();
72
+ });
@@ -13,6 +13,14 @@
13
13
  ]
14
14
  %>
15
15
 
16
+ <%
17
+ example_dropdown_options = [
18
+ { label: 'United States', value: 'United States', id: 'us' },
19
+ { label: 'Canada', value: 'Canada', id: 'ca' },
20
+ { label: 'Pakistan', value: 'Pakistan', id: 'pk' },
21
+ ]
22
+ %>
23
+
16
24
  <%= pb_form_with(scope: :example, method: :get, url: "", validate: true) do |form| %>
17
25
  <%= form.text_field :example_text_field, props: { label: true, required: true } %>
18
26
  <%= form.phone_number_field :example_phone_number_field, props: { label: "Example phone field" } %>
@@ -22,6 +30,7 @@
22
30
  <%= form.password_field :example_password_field, props: { label: true, required: true } %>
23
31
  <%= form.url_field :example_url_field, props: { label: true, required: true } %>
24
32
  <%= form.text_area :example_text_area, props: { label: true, required: true } %>
33
+ <%= form.dropdown_field :example_dropdown, props: { label: true, options: example_dropdown_options, required: true } %>
25
34
  <%= form.select :example_select, [ ["Yes", 1], ["No", 2] ], props: { label: true, blank_selection: "Select One...", required: true } %>
26
35
  <%= form.collection_select :example_collection_select, example_collection, :value, :name, props: { label: true, blank_selection: "Select One...", required: true } %>
27
36
  <%= form.check_box :example_checkbox, props: { text: "Example Checkbox", label: true, required: true } %>
@@ -55,7 +55,7 @@ $form_pill_colors: (
55
55
  height: fit-content;
56
56
  height: -moz-fit-content;
57
57
  .pb_form_pill_text, .pb_form_pill_close, .pb_form_pill_tag {
58
- font-size: 16px;
58
+ font-size: $font_base;
59
59
  font-weight: $regular;
60
60
  }
61
61
  .pb_form_pill_text, .pb_form_pill_tag {
@@ -3,6 +3,7 @@ import classnames from 'classnames'
3
3
  import { buildAriaProps, buildDataProps, buildHtmlProps } from '../utilities/props'
4
4
  import { GlobalProps, globalProps } from '../utilities/globalProps'
5
5
  import { isValidEmoji } from '../utilities/validEmojiChecker'
6
+ import aliasesJson from './icon_aliases.json'
6
7
 
7
8
  export type IconSizes = "lg"
8
9
  | "xs"
@@ -40,6 +41,19 @@ type IconProps = {
40
41
  spin?: boolean,
41
42
  } & GlobalProps
42
43
 
44
+ type AliasType = string | string[];
45
+
46
+ interface Aliases {
47
+ [key: string]: AliasType;
48
+ }
49
+
50
+ interface AliasesJson {
51
+ aliases: Aliases;
52
+ }
53
+
54
+ const aliases: AliasesJson = aliasesJson;
55
+
56
+
43
57
  const flipMap = {
44
58
  horizontal: 'fa-flip-horizontal',
45
59
  vertical: 'fa-flip-vertical',
@@ -52,6 +66,22 @@ declare global {
52
66
  var PB_ICONS: {[key: string]: React.FunctionComponent<any>}
53
67
  }
54
68
 
69
+ // Resolve alias function
70
+ const resolveAlias = (icon: string): string => {
71
+ const alias = aliases.aliases[icon];
72
+
73
+ if (alias) {
74
+ if (Array.isArray(alias)) {
75
+ return alias[0];
76
+ } else {
77
+ return alias;
78
+ }
79
+ }
80
+
81
+ return icon;
82
+ };
83
+
84
+
55
85
  const Icon = (props: IconProps) => {
56
86
  const {
57
87
  aria = {},
@@ -74,7 +104,8 @@ const Icon = (props: IconProps) => {
74
104
  spin = false,
75
105
  } = props
76
106
 
77
- let iconElement: ReactSVGElement | null = typeof(icon) === "object" ? icon : null
107
+ const resolvedIcon = resolveAlias(icon as string)
108
+ let iconElement: ReactSVGElement | null = typeof(resolvedIcon) === "object" ? resolvedIcon : null
78
109
 
79
110
  const faClasses = {
80
111
  'fa-border': border,
@@ -90,12 +121,12 @@ const Icon = (props: IconProps) => {
90
121
 
91
122
  if (!customIcon && !iconElement) {
92
123
  const PowerIcon: React.FunctionComponent<any> | undefined =
93
- window.PB_ICONS ? window.PB_ICONS[icon as string] : null
124
+ window.PB_ICONS ? window.PB_ICONS[resolvedIcon as string] : null
94
125
 
95
126
  if (PowerIcon) {
96
127
  iconElement = <PowerIcon /> as ReactSVGElement
97
128
  } else {
98
- faClasses[`fa-${icon}`] = icon as string
129
+ faClasses[`fa-${resolvedIcon}`] = resolvedIcon as string
99
130
  }
100
131
  }
101
132
 
@@ -115,7 +146,7 @@ const Icon = (props: IconProps) => {
115
146
  className
116
147
  )
117
148
 
118
- aria.label ? null : aria.label = `${icon} icon`
149
+ aria.label ? null : aria.label = `${resolvedIcon} icon`
119
150
  const ariaProps: {[key: string]: any} = buildAriaProps(aria)
120
151
  const dataProps: {[key: string]: any} = buildDataProps(data)
121
152
  const htmlProps = buildHtmlProps(htmlOptions)
@@ -137,7 +168,7 @@ const Icon = (props: IconProps) => {
137
168
  }
138
169
  </>
139
170
  )
140
- else if (isValidEmoji(icon as string))
171
+ else if (isValidEmoji(resolvedIcon as string))
141
172
  return (
142
173
  <>
143
174
  <span
@@ -146,7 +177,7 @@ const Icon = (props: IconProps) => {
146
177
  className={classesEmoji}
147
178
  id={id}
148
179
  >
149
- {icon}
180
+ {resolvedIcon}
150
181
  </span>
151
182
  </>
152
183
  )
@@ -1 +1 @@
1
- <%= pb_rails("icon", props: { icon: "user", fixed_width: true }) %>
1
+ <%= pb_rails("icon", props: { icon: "user", fixed_width: true }) %>
@@ -1,6 +1,5 @@
1
- import React from 'react'
2
-
3
- import Icon from '../_icon'
1
+ import React from "react"
2
+ import Icon from "../_icon"
4
3
 
5
4
  const IconDefault = (props) => {
6
5
  return (
@@ -3,6 +3,7 @@
3
3
  # rubocop:disable Style/HashLikeCase
4
4
 
5
5
  require "open-uri"
6
+ require "json"
6
7
 
7
8
  module Playbook
8
9
  module PbIcon
@@ -38,6 +39,8 @@ module Playbook
38
39
  prop :spin, type: Playbook::Props::Boolean,
39
40
  default: false
40
41
 
42
+ ALIASES = JSON.parse(File.read(Playbook::Engine.root.join("app/pb_kits/playbook/pb_icon/icon_aliases.json")))["aliases"].freeze
43
+
41
44
  def valid_emoji?
42
45
  emoji_regex = /\p{Emoji}/
43
46
  emoji_regex.match?(icon)
@@ -83,7 +86,8 @@ module Playbook
83
86
  return unless Rails.application.config.respond_to?(:icon_path)
84
87
 
85
88
  base_path = Rails.application.config.icon_path
86
- icon_path = Dir.glob(Rails.root.join(base_path, "**", "#{icon}.svg")).first
89
+ resolved_icon = resolve_alias(icon)
90
+ icon_path = Dir.glob(Rails.root.join(base_path, "**", "#{resolved_icon}.svg")).first
87
91
  icon_path if icon_path && File.exist?(icon_path)
88
92
  end
89
93
 
@@ -106,6 +110,22 @@ module Playbook
106
110
 
107
111
  private
108
112
 
113
+ def resolve_alias(icon)
114
+ aliases = ALIASES[icon]
115
+ return icon unless aliases
116
+
117
+ if aliases.is_a?(Array)
118
+ aliases.find { |alias_name| file_exists?(alias_name) } || icon
119
+ else
120
+ aliases
121
+ end
122
+ end
123
+
124
+ def file_exists?(alias_name)
125
+ base_path = Rails.application.config.icon_path
126
+ File.exist?(Dir.glob(Rails.root.join(base_path, "**", "#{alias_name}.svg")).first)
127
+ end
128
+
109
129
  def svg_size
110
130
  size.nil? ? "1x" : size
111
131
  end
@@ -0,0 +1,39 @@
1
+ {
2
+ "aliases": {
3
+ "arrow-alt-circle-right": "circle-right",
4
+ "angles-down": "angle-double-down",
5
+ "arrow-alt-down": "down",
6
+ "arrow-alt-up": "up",
7
+ "arrow-right-long": "long-arrow-right",
8
+ "arrow-to-bottom": "arrow-down-to-line",
9
+ "arrows-h": "arrows-left-right",
10
+ "calendar-days": "calendar-alt",
11
+ "circle-arrow-right": "arrow-circle-right",
12
+ "clock-rotate-left": "history",
13
+ "close": [
14
+ "times",
15
+ "xmark"
16
+ ],
17
+ "ellipsis-h": "ellipsis",
18
+ "exclamation-circle": "circle-exclamation",
19
+ "external-link": "arrow-up-right-from-square",
20
+ "file-lines": "file-alt",
21
+ "gear": "cog",
22
+ "home": "house",
23
+ "info-circle": "circle-info",
24
+ "map-o": "map",
25
+ "message": "comment-alt",
26
+ "minus-circle": "circle-minus",
27
+ "money": "money-bill",
28
+ "mouse-pointer": "arrow-pointer",
29
+ "nitro": "nitro-n",
30
+ "play-circle": "circle-play",
31
+ "plus-circle": "circle-plus",
32
+ "plus-square": "square-plus",
33
+ "powergon": "powergon-p",
34
+ "question-circle": "circle-question",
35
+ "roofing": "product-roofing",
36
+ "shelves": "inventory",
37
+ "th-list": "table-list"
38
+ }
39
+ }
@@ -10,7 +10,7 @@ type ListProps = {
10
10
  className?: string;
11
11
  children: React.ReactNode[] | React.ReactNode;
12
12
  dark?: boolean;
13
- draggable?: boolean;
13
+ enableDrag?: boolean;
14
14
  data?: Record<string, unknown>;
15
15
  htmlOptions?: {[key: string]: string | number | boolean | (() => void)},
16
16
  id?: string;
@@ -32,7 +32,7 @@ const List = (props: ListProps) => {
32
32
  className,
33
33
  dark = false,
34
34
  data = {},
35
- draggable = false,
35
+ enableDrag = false,
36
36
  htmlOptions = {},
37
37
  id,
38
38
  layout = "",
@@ -54,7 +54,7 @@ const List = (props: ListProps) => {
54
54
  const childrenWithProps = React.Children.map(
55
55
  children,
56
56
  (child: React.ReactElement) => {
57
- return React.cloneElement(child, { text, variant, draggable });
57
+ return React.cloneElement(child, { text, variant, enableDrag });
58
58
  }
59
59
  );
60
60
  const ariaProps = buildAriaProps(aria);
@@ -74,7 +74,7 @@ const List = (props: ListProps) => {
74
74
  return (
75
75
  <>
76
76
  {
77
- draggable ? (
77
+ enableDrag ? (
78
78
  <Draggable.Container>
79
79
  <div className={classes}>
80
80
  {ordered ? (
@@ -11,6 +11,7 @@ type ListItemProps = {
11
11
  children: React.ReactNode[] | React.ReactNode,
12
12
  className?: string,
13
13
  data?: Record<string, unknown>,
14
+ dragId?: string,
14
15
  dragHandle?: boolean,
15
16
  htmlOptions?: {[key: string]: string | number | boolean | (() => void)},
16
17
  id?: string,
@@ -23,7 +24,8 @@ const ListItem = (props: ListItemProps) => {
23
24
  children,
24
25
  className,
25
26
  data = {},
26
- draggable = false,
27
+ enableDrag,
28
+ dragId,
27
29
  dragHandle = true,
28
30
  htmlOptions = {},
29
31
  id,
@@ -42,8 +44,10 @@ const ListItem = (props: ListItemProps) => {
42
44
  return (
43
45
  <>
44
46
  {
45
- draggable ? (
46
- <Draggable.Item id={id}>
47
+ enableDrag ? (
48
+ <Draggable.Item
49
+ dragId={dragId}
50
+ >
47
51
  <li
48
52
  {...ariaProps}
49
53
  {...dataProps}
@@ -1,5 +1,5 @@
1
1
  <%= pb_rails("nav", props: { variant: "bold" }) do %>
2
- <%= pb_rails("nav/item", props: { text: "Overview", link: "#", collapsible: true, icon_left:"city" }) do %>
2
+ <%= pb_rails("nav/item", props: { text: "Overview", link: "#", collapsible: true, icon_left:"city", collapsed: false }) do %>
3
3
  <%= pb_rails("nav", props: { variant: "bold" }) do %>
4
4
  <%= pb_rails("nav/item", props: { text: "City", link: "#" }) %>
5
5
  <%= pb_rails("nav/item", props: { text: "People", link: "#" }) %>
@@ -9,6 +9,7 @@ const CollapsibleNav = (props) => {
9
9
  >
10
10
  <NavItem
11
11
  active
12
+ collapsed={false}
12
13
  collapsible
13
14
  iconLeft="city"
14
15
  link="#"
@@ -1 +1,3 @@
1
- The `collapsible` prop allows users to create a nested, collapsible nav. Pass `collapsible` to any NavItem and pass that navItem any number of NavItems as children to create a collapsible nav.
1
+ The `collapsible` prop allows users to create a nested, collapsible nav. Pass `collapsible` to any NavItem and pass that navItem any number of NavItems as children to create a collapsible nav.
2
+
3
+ The optional `collapsed` prop can also be used to set the default state for the collapsed nav on first render of the page. `collapsed` takes a boolean value that is set to true (meaning nav is collapsed) by default. Set it to false as shown here to have the nav open on first render.