playbook_ui 13.30.0 → 13.31.0.pre.alpha.PLAY882formpillprimaryandneutral3201

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) 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_background/background.html.erb +2 -11
  7. data/app/pb_kits/playbook/pb_body/body.html.erb +1 -6
  8. data/app/pb_kits/playbook/pb_bread_crumbs/bread_crumb_item.html.erb +1 -6
  9. data/app/pb_kits/playbook/pb_bread_crumbs/bread_crumbs.html.erb +1 -6
  10. data/app/pb_kits/playbook/pb_caption/caption.html.erb +1 -6
  11. data/app/pb_kits/playbook/pb_card/_card.tsx +4 -3
  12. data/app/pb_kits/playbook/pb_card/card.html.erb +1 -7
  13. data/app/pb_kits/playbook/pb_collapsible/index.js +6 -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_detail/detail.html.erb +1 -6
  19. data/app/pb_kits/playbook/pb_dialog/dialog.html.erb +1 -6
  20. data/app/pb_kits/playbook/pb_dialog/dialog_body.html.erb +1 -6
  21. data/app/pb_kits/playbook/pb_dialog/dialog_footer.html.erb +2 -5
  22. data/app/pb_kits/playbook/pb_dialog/dialog_header.html.erb +1 -6
  23. data/app/pb_kits/playbook/pb_draggable/_draggable.scss +2 -4
  24. data/app/pb_kits/playbook/pb_draggable/context/index.tsx +84 -50
  25. data/app/pb_kits/playbook/pb_draggable/context/types.ts +31 -0
  26. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_default.jsx +15 -19
  27. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_default.md +5 -3
  28. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_multiple_containers.jsx +5 -1
  29. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_cards.jsx +11 -4
  30. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_cards.md +8 -2
  31. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_list.jsx +2 -2
  32. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_list.md +5 -1
  33. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_selectable_list.jsx +6 -4
  34. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_selectable_list.md +5 -0
  35. data/app/pb_kits/playbook/pb_draggable/draggable.test.jsx +142 -15
  36. data/app/pb_kits/playbook/pb_draggable/subcomponents/DraggableItem.tsx +6 -5
  37. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +18 -0
  38. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +228 -218
  39. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_error.html.erb +9 -0
  40. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_error.jsx +34 -0
  41. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +2 -0
  42. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +2 -1
  43. data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +16 -12
  44. data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +9 -0
  45. data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +17 -2
  46. data/app/pb_kits/playbook/pb_dropdown/index.js +33 -4
  47. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/_fixed_confirmation_toast.tsx +9 -3
  48. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_custom_icon.html.erb +32 -0
  49. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_custom_icon.jsx +48 -0
  50. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/example.yml +2 -0
  51. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/index.js +1 -0
  52. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/fixed_confirmation_toast.rb +16 -11
  53. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/fixed_confirmation_toast.test.js +72 -0
  54. data/app/pb_kits/playbook/pb_flex/flex.html.erb +1 -5
  55. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +9 -0
  56. data/app/pb_kits/playbook/pb_form_pill/_form_pill.scss +108 -5
  57. data/app/pb_kits/playbook/pb_form_pill/_form_pill.test.jsx +53 -0
  58. data/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx +11 -2
  59. data/app/pb_kits/playbook/pb_form_pill/form_pill.html.erb +1 -1
  60. data/app/pb_kits/playbook/pb_form_pill/form_pill.rb +5 -1
  61. data/app/pb_kits/playbook/pb_hashtag/hashtag.html.erb +1 -6
  62. data/app/pb_kits/playbook/pb_highlight/highlight.html.erb +1 -5
  63. data/app/pb_kits/playbook/pb_home_address_street/home_address_street.html.erb +1 -5
  64. data/app/pb_kits/playbook/pb_icon/_icon.tsx +37 -6
  65. data/app/pb_kits/playbook/pb_icon/docs/_icon_default.html.erb +1 -1
  66. data/app/pb_kits/playbook/pb_icon/docs/_icon_default.jsx +2 -3
  67. data/app/pb_kits/playbook/pb_icon/icon.rb +21 -1
  68. data/app/pb_kits/playbook/pb_icon/icon_aliases.json +39 -0
  69. data/app/pb_kits/playbook/pb_list/_list.tsx +4 -4
  70. data/app/pb_kits/playbook/pb_list/_list_item.tsx +7 -3
  71. data/app/pb_kits/playbook/pb_nav/docs/_collapsible_nav.html.erb +1 -1
  72. data/app/pb_kits/playbook/pb_nav/docs/_collapsible_nav.jsx +1 -0
  73. data/app/pb_kits/playbook/pb_nav/docs/_collapsible_nav.md +3 -1
  74. data/app/pb_kits/playbook/pb_nav/item.html.erb +1 -1
  75. data/app/pb_kits/playbook/pb_nav/item.rb +1 -0
  76. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_default.html.erb +24 -0
  77. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_default.md +4 -4
  78. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_multi_directional.html.erb +11 -0
  79. data/app/pb_kits/playbook/pb_overlay/docs/example.yml +4 -0
  80. data/app/pb_kits/playbook/pb_overlay/overlay.html.erb +27 -0
  81. data/app/pb_kits/playbook/pb_overlay/overlay.rb +110 -0
  82. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +8 -23
  83. data/app/pb_kits/playbook/pb_selectable_list/_item.tsx +7 -3
  84. data/app/pb_kits/playbook/pb_selectable_list/_selectable_list.tsx +3 -3
  85. data/app/pb_kits/playbook/pb_table/table_header.html.erb +16 -2
  86. data/app/pb_kits/playbook/pb_timeline/_item.tsx +11 -10
  87. data/app/pb_kits/playbook/pb_timeline/_timeline.tsx +8 -6
  88. data/app/pb_kits/playbook/utilities/globalProps.ts +1 -0
  89. data/dist/menu.yml +566 -472
  90. data/dist/playbook-rails.js +7 -7
  91. data/lib/playbook/version.rb +2 -2
  92. metadata +22 -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
+ });
@@ -1,7 +1,3 @@
1
- <%= content_tag(:div,
2
- id: object.id,
3
- data: object.data,
4
- class: object.classname,
5
- **combined_html_options) do %>
1
+ <%= pb_content_tag do %>
6
2
  <%= content.presence %>
7
3
  <% end %>
@@ -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 } %>
@@ -8,6 +8,7 @@ $selector: ".pb_form_pill";
8
8
  $pb_form_pill_height: 37px;
9
9
  $form_pill_colors: (
10
10
  primary: map-get($status_color_text, "primary"),
11
+ neutral: map-get($status_color_text, "neutral"),
11
12
  );
12
13
 
13
14
 
@@ -23,34 +24,71 @@ $form_pill_colors: (
23
24
  cursor: pointer;
24
25
  @each $color_name, $color_value in $form_pill_colors {
25
26
  &[class*=_#{$color_name}] {
26
- background-color: rgba($color_value, $opacity-1);
27
+ background-color: mix($color_value, $card_light, 10%);
28
+ @if ($color_name == "neutral") {
29
+ background-color: $white;
30
+ border: 1px solid $border_light;
31
+ }
27
32
  transition: background-color 0.2s ease;
28
33
  box-shadow: none;
29
34
  @media (hover:hover) {
30
35
  &:hover {
31
- background-color: rgba($color_value, $opacity-2);
36
+ background-color: mix($color_value, $card_light, 20%);
37
+ @if ($color_name == "neutral") {
38
+ background-color: mix($neutral, $card_light, 20%);
39
+ border: 1px solid $border_light;
40
+ }
41
+ }
42
+ &:active {
43
+ background-color: mix($color_value, $card_light, 30%);
44
+ @if ($color_name == "neutral") {
45
+ background-color: mix($neutral, $card_light, 30%);
46
+ }
32
47
  }
33
48
  }
34
49
  #{$selector}_text {
35
50
  color: $color_value;
51
+ @if ($color_name == "neutral") {
52
+ color: $text_lt_default;
53
+ }
36
54
  padding-left: $space-sm;
37
- padding-right: $space-sm/4;
55
+ padding-right: $space-sm/2;
38
56
  }
39
57
  #{$selector}_close {
40
58
  color: $color_value;
41
- padding-left: $space-sm/2;
59
+ padding-left: $space-sm/4;
42
60
  padding-right: $space-sm/4;
43
61
  display: flex;
44
62
  align-items: center;
45
- height: 100%;
63
+ // I had to temporarily change height to 27px so new hover state darker background forms a circle not an oval
64
+ // before size change (ticket 2 of 4) - change back to 100% when $pb_form_pill_height changed to 27px from 37px
65
+ height: 27px;
66
+ border-radius: 70px;
46
67
  cursor: pointer;
68
+ &:hover {
69
+ background-color: mix($color_value, $card_light, 40%);
70
+ @if ($color_name == "neutral") {
71
+ background-color: mix($neutral, $card_light, 60%);
72
+ }
73
+ }
47
74
  }
48
75
  #{$selector}_tag {
49
76
  color: $color_value;
50
77
  padding-left: $space-sm;
78
+ @if ($color_name == "neutral") {
79
+ color: $text_lt_default;
80
+ }
51
81
  }
52
82
  }
53
83
  }
84
+ &:focus {
85
+ outline: $primary solid 2px;
86
+ outline-offset: -1px;
87
+ }
88
+ &:focus-visible {
89
+ outline: $primary solid 2px;
90
+ outline-offset: -1px;
91
+ }
54
92
  &.small {
55
93
  height: fit-content;
56
94
  height: -moz-fit-content;
@@ -70,6 +108,71 @@ $form_pill_colors: (
70
108
  &::before { line-height: 21px; }
71
109
  }
72
110
  }
111
+ &.dark {
112
+ @each $color_name, $color_value in $form_pill_colors {
113
+ // In dark mode, the base patterns below are needed for the next tickets for success, warning, error, info.
114
+ // Primary and Neutral are exceptions to the general rule in the handoff
115
+ &[class*=_#{$color_name}] {
116
+ // background-color: mix($color_value, $card_dark, 10%);
117
+ // .pb_form_pill_tag {
118
+ // color: $color_name;
119
+ // }
120
+ // .pb_form_pill_close {
121
+ // color: $color_name;
122
+ // &:hover {
123
+ // background-color: mix($color_value, $card_dark, 40%);
124
+ // }
125
+ // }
126
+ // &:hover {
127
+ // background-color: mix($color_value, $card_dark, 20%);
128
+ // }
129
+ // &:active {
130
+ // background-color: mix($color_value, $card_dark, 30%);
131
+ // }
132
+ @if ($color_name == "neutral") {
133
+ background-color: transparent;
134
+ border: 1px solid $border_dark;
135
+ .pb_form_pill_text, .pb_form_pill_tag {
136
+ color: $text_dk_default;
137
+ }
138
+ .pb_form_pill_close {
139
+ color: $text_dk_default;
140
+ &:hover {
141
+ background-color: mix($neutral, $card_dark, 40%);
142
+ }
143
+ }
144
+ &:hover {
145
+ background-color: mix($white, $card_dark, 10%);
146
+ }
147
+ &:active {
148
+ background-color: mix($white, $card_dark, 20%);
149
+ }
150
+ &:focus {
151
+ border: 1px solid $primary;
152
+ }
153
+ }
154
+ @if ($color_name == "primary") {
155
+ background-color: mix($active_dark, $card_dark, 10%);
156
+ .pb_form_pill_text, .pb_form_pill_tag {
157
+ color: $active_dark;
158
+ }
159
+ .pb_form_pill_close {
160
+ color: $active_dark;
161
+ &:hover {
162
+ background-color: mix($active_dark, $card_dark, 40%);
163
+ }
164
+ }
165
+ &:hover {
166
+ background-color: mix($active_dark, $card_dark, 20px);
167
+ }
168
+ &:active {
169
+ background-color: mix($active_dark, $card_dark, 30%);
170
+ }
171
+ }
172
+ }
173
+ }
174
+ }
175
+
73
176
  &[class*=lowercase] {
74
177
  text-transform: lowercase;
75
178
  }
@@ -0,0 +1,53 @@
1
+ import React from 'react';
2
+ import { render, screen } from '../utilities/test-utils';
3
+ import FormPill from './_form_pill';
4
+
5
+ const testId = 'formpill';
6
+
7
+ test('should render classname', () => {
8
+ render(
9
+ <FormPill
10
+ data={{ testid: testId }}
11
+ text="test"
12
+ />
13
+ )
14
+
15
+ const kit = screen.getByTestId(testId)
16
+ expect(kit).toHaveClass('pb_form_pill_kit_primary none')
17
+ });
18
+
19
+ test('displays text content', () => {
20
+ render(
21
+ <FormPill
22
+ data={{ testid: testId }}
23
+ text="test"
24
+ />
25
+ )
26
+
27
+ const text = screen.getByText("test")
28
+ expect(text).toBeInTheDocument()
29
+ });
30
+
31
+ test('displays color variant', () => {
32
+ render(
33
+ <FormPill
34
+ color={"neutral"}
35
+ data={{ testid: testId }}
36
+ text={"test"}
37
+ />
38
+ )
39
+ const kit = screen.getByTestId(testId)
40
+ expect(kit).toHaveClass(`pb_form_pill_kit_neutral none`)
41
+ });
42
+
43
+ test('displays size variant', () => {
44
+ render(
45
+ <FormPill
46
+ data={{ testid: testId }}
47
+ size={"small"}
48
+ text={"test"}
49
+ />
50
+ )
51
+ const kit = screen.getByTestId('formpill')
52
+ expect(kit).toHaveClass(`pb_form_pill_kit_primary small none`)
53
+ });
@@ -5,7 +5,7 @@ import Title from '../pb_title/_title'
5
5
  import Icon from '../pb_icon/_icon'
6
6
  import Avatar from '../pb_avatar/_avatar'
7
7
  import { globalProps, GlobalProps } from '../utilities/globalProps'
8
- import { buildHtmlProps } from '../utilities/props'
8
+ import { buildDataProps, buildHtmlProps } from '../utilities/props'
9
9
 
10
10
  type FormPillProps = {
11
11
  className?: string,
@@ -18,6 +18,9 @@ type FormPillProps = {
18
18
  avatarUrl?: string,
19
19
  size?: string,
20
20
  textTransform?: 'none' | 'lowercase',
21
+ color?: "primary" | "neutral",
22
+ data?: {[key: string]: string},
23
+ tabIndex?: number,
21
24
  closeProps?: {
22
25
  onClick?: React.MouseEventHandler<HTMLSpanElement>,
23
26
  onMouseDown?: React.MouseEventHandler<HTMLSpanElement>,
@@ -36,20 +39,26 @@ const FormPill = (props: FormPillProps): React.ReactElement => {
36
39
  closeProps = {},
37
40
  size = '',
38
41
  textTransform = 'none',
42
+ color = "primary",
43
+ data = {},
44
+ tabIndex,
39
45
  } = props
40
46
  const css = classnames(
41
- `pb_form_pill_kit_${'primary'}`,
47
+ `pb_form_pill_kit_${color}`,
42
48
  globalProps(props),
43
49
  className,
44
50
  size === 'small' ? 'small' : null,
45
51
  textTransform,
46
52
  )
47
53
 
54
+ const dataProps = buildDataProps(data)
48
55
  const htmlProps = buildHtmlProps(htmlOptions)
49
56
 
50
57
  return (
51
58
  <div className={css}
52
59
  id={id}
60
+ tabIndex={tabIndex}
61
+ {...dataProps}
53
62
  {...htmlProps}
54
63
  >
55
64
  {name &&
@@ -1,4 +1,4 @@
1
- <%= content_tag(:div, id: object.id, data: object.data, class: object.classname + object.size_class, **combined_html_options) do %>
1
+ <%= content_tag(:div, id: object.id, data: object.data, class: object.classname + object.size_class, tabindex: object.tabindex, **combined_html_options) do %>
2
2
  <% if object.name.present? %>
3
3
  <%= pb_rails("avatar", props: { name: object.name, image_url: object.avatar_url, size: "xs" }) %>
4
4
  <%= pb_rails("title", props: { text: object.name, size: 4, classname: "pb_form_pill_text" }) %>
@@ -11,9 +11,13 @@ module Playbook
11
11
  prop :text_transform, type: Playbook::Props::Enum,
12
12
  values: %w[none lowercase],
13
13
  default: "none"
14
+ prop :color, type: Playbook::Props::Enum,
15
+ values: %w[primary neutral],
16
+ default: "primary"
17
+ prop :tabindex
14
18
 
15
19
  def classname
16
- generate_classname("pb_form_pill_kit", "primary", name, text, text_transform)
20
+ generate_classname("pb_form_pill_kit", color, name, text, text_transform)
17
21
  end
18
22
 
19
23
  def display_text
@@ -1,9 +1,4 @@
1
- <%= content_tag(:span,
2
- aria: object.aria,
3
- class: object.classname,
4
- data: object.data,
5
- id: object.id,
6
- **combined_html_options) do %>
1
+ <%= pb_content_tag(:span) do %>
7
2
  <%= link_to object.url, target: object.link_option do %>
8
3
  <%= pb_rails("badge", props: { dark: object.dark, variant: "primary", text: object.hashtag_text }) %>
9
4
  <% end %>
@@ -1,8 +1,4 @@
1
- <%= content_tag(:span,
2
- id: object.id,
3
- data: object.data,
4
- class: object.classname,
5
- **combined_html_options) do %>
1
+ <%= pb_content_tag(:span) do %>
6
2
  <mark>
7
3
  <%= content.presence || object.text %>
8
4
  </mark>