playbook_ui 16.7.0.pre.alpha.play2924tooltipmisalignment16242 → 16.7.0.pre.alpha.play2924tooltipmisalignment16289

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3992fdab5906608c6118a8ce2ba3c4fea08b25c565d0978c524905c4d6954f64
4
- data.tar.gz: cc07250a9142d0344314a823c2f0b01eba76f0277daa1a33b10066a844228c7d
3
+ metadata.gz: 9b032b2af16b04367de05d6b9acdfa51697b94da0010ba7189b001adcf0ba447
4
+ data.tar.gz: 322e6db4a0669d03521b9e676a71682b210c316ada7898d94a37eea4cba696fd
5
5
  SHA512:
6
- metadata.gz: 99ea73cdfd8b0b0b73a46d3cee50b20f24d2dc5b11a39d6730b7eb0dd147dce3d774c5c7270d3c9ce060a6ec4d06d752ad870033dfcc766a7d8bc795ebd0e883
7
- data.tar.gz: 4cd63b0368a8f3746e8555485cf4236066a3ac4828b60d49c888a6e0f5d6e7cc38909b34a5469ddf4fd906258909774fe17976bca4e8838e03eadc6af638e94c
6
+ metadata.gz: 0f8b662b1de6dcb08081c06101dee838ac36cc3cd616b64214893226942eaef61723bc52e215646b068a0756de9a90f037b2f4c6d04206570babc3f1c01a73a2
7
+ data.tar.gz: b4f795a65bb17995ae668450ea8535f16e2cee02b3e9a451aac53849fa0fc922a345630b0aac60974b8517fb7413de2d3347c5ea8705f551459bab6cbd0e5c47
@@ -12,6 +12,20 @@ import Caption from '../pb_caption/_caption'
12
12
  import Body from '../pb_body/_body'
13
13
  import colors from "../tokens/exports/_colors.module.scss"
14
14
 
15
+ function serializeDefaultDateForFilterReset(defaultDate: unknown): string | undefined {
16
+ if (defaultDate === '' || defaultDate == null) return undefined
17
+ if (Array.isArray(defaultDate)) {
18
+ const parts = defaultDate.map((d) => {
19
+ if (d == null || d === '') return ''
20
+ if (d instanceof Date) return d.toISOString()
21
+ return String(d)
22
+ }).filter(Boolean)
23
+ return parts.length ? parts.join(',') : undefined
24
+ }
25
+ if (defaultDate instanceof Date) return defaultDate.toISOString()
26
+ return String(defaultDate)
27
+ }
28
+
15
29
  type DatePickerProps = {
16
30
  allowInput?: boolean,
17
31
  aria?: { [key: string]: string },
@@ -113,7 +127,11 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
113
127
  } = props
114
128
 
115
129
  const ariaProps = buildAriaProps(aria)
116
- const dataProps = buildDataProps(data)
130
+ const filterResetDefaultSerialized = serializeDefaultDateForFilterReset(defaultDate)
131
+ const dataProps = buildDataProps({
132
+ ...data,
133
+ ...(filterResetDefaultSerialized ? { 'default-value': filterResetDefaultSerialized } : {}),
134
+ })
117
135
  const htmlProps = buildHtmlProps(htmlOptions)
118
136
  const inputAriaProps = buildAriaProps(inputAria)
119
137
  const inputDataProps = buildDataProps(inputData)
@@ -180,6 +198,7 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
180
198
  //@ts-ignore
181
199
  globalProps(filteredProps),
182
200
  error ? 'error' : null,
201
+ inLine && 'inline-date-picker',
183
202
  className
184
203
  )
185
204
 
@@ -262,7 +281,7 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
262
281
  }
263
282
  </div>
264
283
 
265
- {!hideIcon &&
284
+ {!hideIcon && !inLine &&
266
285
  <div
267
286
  className={iconWrapperClass()}
268
287
  id={`cal-icon-${pickerId}`}
@@ -274,7 +293,7 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
274
293
  </div>
275
294
  }
276
295
 
277
- {hideIcon && inLine ?
296
+ {inLine ?
278
297
  <div>
279
298
  <div
280
299
  className={`${iconWrapperClass()} date-picker-inline-icon-plus`}
@@ -1,8 +1,11 @@
1
- <%= pb_content_tag(:div,
2
- class: object.classname + object.error_class,
3
- 'data-pb-date-picker' => true,
4
- 'data-validation-message' => object.validation_message,
5
- ) do %>
1
+ <% date_picker_root_attrs = {
2
+ class: object.classname + object.error_class,
3
+ 'data-pb-date-picker' => true,
4
+ 'data-validation-message' => object.validation_message,
5
+ }
6
+ date_picker_root_attrs['data-default-value'] = object.serialized_default_date_for_dom if object.serialized_default_date_for_dom.present?
7
+ %>
8
+ <%= pb_content_tag(:div, date_picker_root_attrs) do %>
6
9
  <div class="input_wrapper">
7
10
  <% if !object.hide_label && object.label %>
8
11
  <label for="<%= object.picker_id %>">
@@ -39,7 +42,7 @@
39
42
  <input type="hidden" id="<%= "#{object.end_date_id}" %>" name="<%= "#{object.end_date_name}" %>">
40
43
  <% end %>
41
44
 
42
- <% if !object.hide_icon %>
45
+ <% if !object.hide_icon && !object.inline %>
43
46
  <div
44
47
  class="<%= object.icon_wrapper_class %>"
45
48
  id="cal-icon-<%= object.picker_id %>"
@@ -52,7 +55,7 @@
52
55
  <% end %>
53
56
 
54
57
  <!-- Inline -->
55
- <% if object.hide_icon && object.inline %>
58
+ <% if object.inline %>
56
59
  <!-- Plus Icon -->
57
60
  <div
58
61
  class="<%= object.icon_wrapper_class %> date-picker-inline-icon-plus"
@@ -92,7 +92,8 @@ module Playbook
92
92
 
93
93
  def classname
94
94
  default_margin_bottom = margin_bottom.present? ? "" : " mb_sm"
95
- generate_classname("pb_date_picker_kit") + default_margin_bottom
95
+ inline_class = inline ? " inline-date-picker" : ""
96
+ generate_classname("pb_date_picker_kit") + default_margin_bottom + inline_class
96
97
  end
97
98
 
98
99
  def date_picker_config
@@ -144,6 +145,18 @@ module Playbook
144
145
  def angle_down_path
145
146
  "app/pb_kits/playbook/utilities/icons/angle-down.svg"
146
147
  end
148
+
149
+ # Serialized business default for opt-in smart filter reset (Nitro / data-default-value).
150
+ def serialized_default_date_for_dom
151
+ case default_date
152
+ when nil, ""
153
+ nil
154
+ when Array
155
+ default_date.compact_blank.map(&:to_s).join(",")
156
+ else
157
+ default_date.to_s.presence
158
+ end
159
+ end
147
160
  end
148
161
  end
149
162
  end
@@ -40,6 +40,63 @@ describe('DatePicker Kit', () => {
40
40
  expect(kit).toHaveClass('pb_date_picker_kit mb_sm')
41
41
  })
42
42
 
43
+ test('exposes data-default-value on kit root when defaultDate is set', () => {
44
+ const testId = 'datepicker-def-attr'
45
+ render(
46
+ <DatePicker
47
+ data={{ testid: testId }}
48
+ defaultDate={DEFAULT_DATE}
49
+ pickerId="date-picker-def-attr"
50
+ />
51
+ )
52
+ const kit = screen.getByTestId(testId)
53
+ expect(kit).toHaveAttribute('data-default-value', DEFAULT_DATE.toISOString())
54
+ })
55
+
56
+ test('omits data-default-value when defaultDate is empty', () => {
57
+ const testId = 'datepicker-no-def-attr'
58
+ render(
59
+ <DatePicker
60
+ data={{ testid: testId }}
61
+ defaultDate=""
62
+ pickerId="date-picker-no-def-attr"
63
+ />
64
+ )
65
+ const kit = screen.getByTestId(testId)
66
+ expect(kit).not.toHaveAttribute('data-default-value')
67
+ })
68
+
69
+ test('inLine alone adds inline-date-picker class and inline control icons, not the calendar icon', () => {
70
+ const testId = 'datepicker-inline-only'
71
+ render(
72
+ <DatePicker
73
+ data={{ testid: testId }}
74
+ inLine
75
+ pickerId="date-picker-inline-only"
76
+ />
77
+ )
78
+
79
+ const kit = screen.getByTestId(testId)
80
+ expect(kit).toHaveClass('inline-date-picker')
81
+ expect(kit.querySelector('#cal-icon-date-picker-inline-only')).not.toBeInTheDocument()
82
+ expect(kit.querySelector('#date-picker-inline-only-icon-plus')).toBeInTheDocument()
83
+ expect(kit.querySelector('#date-picker-inline-only-angle-down')).toBeInTheDocument()
84
+ })
85
+
86
+ test('hideIcon without inLine does not render inline control icons', () => {
87
+ const testId = 'datepicker-hide-icon-only'
88
+ render(
89
+ <DatePicker
90
+ data={{ testid: testId }}
91
+ hideIcon
92
+ pickerId="date-picker-hide-icon-only"
93
+ />
94
+ )
95
+
96
+ const kit = screen.getByTestId(testId)
97
+ expect(kit.querySelector('#date-picker-hide-icon-only-icon-plus')).not.toBeInTheDocument()
98
+ })
99
+
43
100
  test('shows DatePicker date format m/d/Y', async () => {
44
101
  const testId = 'datepicker-date'
45
102
  render(
@@ -1,6 +1,4 @@
1
1
  <%= pb_rails("date_picker", props: {
2
- classname: "inline-date-picker",
3
- hide_icon: true,
4
2
  inline: true,
5
3
  picker_id: "date-picker-inline"
6
4
  }) %>
@@ -6,8 +6,6 @@ const DatePickerInline = (props) => {
6
6
  return (
7
7
  <div>
8
8
  <DatePicker
9
- className="inline-date-picker"
10
- hideIcon
11
9
  inLine
12
10
  pickerId="date-picker-inline"
13
11
  {...props}
@@ -113,9 +113,7 @@
113
113
  "name": "inline_date",
114
114
  "pickerId": "playground-date-inline",
115
115
  "label": "Inline",
116
- "className": "inline-date-picker",
117
- "inLine": true,
118
- "hideIcon": true
116
+ "inLine": true
119
117
  }
120
118
  },
121
119
  {
@@ -88,9 +88,7 @@
88
88
  "name": "inline_date",
89
89
  "pickerId": "playground-date-inline",
90
90
  "label": "Inline",
91
- "className": "inline-date-picker",
92
- "inLine": true,
93
- "hideIcon": true
91
+ "inLine": true
94
92
  }
95
93
  },
96
94
  {
@@ -21,6 +21,58 @@ import {
21
21
  handleClickOutside,
22
22
  } from "./utilities";
23
23
 
24
+ function serializeDropdownFilterResetDefault(
25
+ variant: "default" | "subtle" | "quickpick" | undefined,
26
+ multiSelect: boolean,
27
+ defaultValue: GenericObject | GenericObject[] | string | undefined,
28
+ dropdownOptions: GenericObject[] | GenericObject | undefined,
29
+ ): string | undefined {
30
+ const optionList: GenericObject[] = Array.isArray(dropdownOptions)
31
+ ? dropdownOptions
32
+ : [];
33
+ const optionDefaultId = (option: GenericObject | undefined): string | undefined => {
34
+ if (!option) return undefined;
35
+
36
+ const id = option.id;
37
+ if (id != null && id !== "") return String(id);
38
+
39
+ const matched = optionList.find((listOption: GenericObject) => (
40
+ (option.value != null && listOption.value === option.value) ||
41
+ (option.label != null && listOption.label === option.label)
42
+ ));
43
+
44
+ if (matched?.id != null && matched.id !== "") return String(matched.id);
45
+ return undefined;
46
+ };
47
+
48
+ if (variant === "quickpick") {
49
+ if (typeof defaultValue === "string" && defaultValue) {
50
+ const matched = optionList.find(
51
+ (opt: GenericObject) => opt.label?.toLowerCase() === defaultValue.toLowerCase()
52
+ );
53
+ if (matched?.id != null && matched.id !== "") return String(matched.id);
54
+ }
55
+ return undefined;
56
+ }
57
+ if (multiSelect) {
58
+ const arr = Array.isArray(defaultValue)
59
+ ? defaultValue
60
+ : defaultValue && typeof defaultValue === "object" && Object.keys(defaultValue).length
61
+ ? [defaultValue as GenericObject]
62
+ : [];
63
+ if (!arr.length) return undefined;
64
+ const ids = arr
65
+ .map((v) => optionDefaultId(v as GenericObject))
66
+ .filter((id) => id != null && id !== "");
67
+ return ids.length ? ids.join(",") : undefined;
68
+ }
69
+ if (defaultValue && typeof defaultValue === "object" && !Array.isArray(defaultValue)) {
70
+ const id = optionDefaultId(defaultValue as GenericObject);
71
+ if (id) return id;
72
+ }
73
+ return undefined;
74
+ }
75
+
24
76
  type CustomQuickPickDate = {
25
77
  label: string;
26
78
  value: string[] | { timePeriod: string; amount: number };
@@ -157,6 +209,11 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
157
209
  initialSelected
158
210
  );
159
211
 
212
+ const filterResetDefaultSerialized = useMemo(
213
+ () => serializeDropdownFilterResetDefault(variant, multiSelect, defaultValue, dropdownOptions),
214
+ [variant, multiSelect, defaultValue, dropdownOptions]
215
+ );
216
+
160
217
  const [isInputFocused, setIsInputFocused] = useState(false);
161
218
  const [hasTriggerSubcomponent, setHasTriggerSubcomponent] = useState(true);
162
219
  const [hasContainerSubcomponent, setHasContainerSubcomponent] =
@@ -432,6 +489,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
432
489
  <div {...ariaProps}
433
490
  {...dataProps}
434
491
  {...htmlProps}
492
+ {...(filterResetDefaultSerialized ? { "data-default-value": filterResetDefaultSerialized } : {})}
435
493
  className={classes}
436
494
  id={id}
437
495
  ref={outerDivRef}
@@ -12,7 +12,9 @@
12
12
  <% end %>
13
13
  <div class="dropdown_wrapper<%= error_class %>" style="position: relative">
14
14
  <input
15
+ <% if input_default_value.present? %>
15
16
  data-default-value="<%= input_default_value %>"
17
+ <% end %>
16
18
  data-dropdown-selected-option
17
19
  name="<%= object.name %><%= '[]' if object.multi_select %>"
18
20
  style="display: none"
@@ -51,6 +51,7 @@ test('generated default kit and classname', () => {
51
51
  const kit = screen.getByTestId(testId)
52
52
  expect(kit).toBeInTheDocument()
53
53
  expect(kit).toHaveClass('pb_dropdown_default')
54
+ expect(kit).not.toHaveAttribute('data-default-value')
54
55
  })
55
56
 
56
57
  test('generated default Trigger and Container when none passed in', () => {
@@ -433,12 +434,16 @@ test("defaultValue works with multiSelect", () => {
433
434
  render(
434
435
  <Dropdown
435
436
  data={{ testid: testId }}
436
- defaultValue={[options[0], options[2]]}
437
+ defaultValue={[
438
+ { label: options[0].label, value: options[0].value },
439
+ { label: options[2].label, value: options[2].value },
440
+ ]}
437
441
  multiSelect
438
442
  options={options}
439
443
  />
440
444
  )
441
445
  const kit = screen.getByTestId(testId)
446
+ expect(kit).toHaveAttribute("data-default-value", "United-states,pakistan")
442
447
  expect(kit.querySelectorAll(".pb_form_pill_kit.pb_form_pill_primary")).toHaveLength(2)
443
448
  const option2 = Array.from(kit.querySelectorAll(".pb_dropdown_option_list"));
444
449
  const firstOpt = options[0].label
@@ -499,6 +504,7 @@ test("quickpick variant accepts string defaultValue", () => {
499
504
  const trigger = kit.querySelector('.pb_dropdown_trigger')
500
505
 
501
506
  expect(trigger).toHaveTextContent("This Month")
507
+ expect(kit).toHaveAttribute("data-default-value", "quickpick-this-month")
502
508
  })
503
509
 
504
510
  test("quickpick attaches _dropdownRef to DOM element when id is provided", () => {
@@ -50,9 +50,18 @@ module Playbook
50
50
  def validation_data
51
51
  fields = input_options[:data] || {}
52
52
  fields[:message] = validation_message unless validation_message.blank?
53
+ dv = filter_reset_default_value
54
+ fields[:default_value] = dv if dv.present?
53
55
  fields
54
56
  end
55
57
 
58
+ def filter_reset_default_value
59
+ s = selected
60
+ return if s.blank?
61
+
62
+ s.join(",")
63
+ end
64
+
56
65
  # Same resolved id as the native +<select>+ (+all_attributes[:id]+) for label +for+.
57
66
  def select_input_id
58
67
  all_attributes[:id].presence
@@ -121,9 +121,18 @@ module Playbook
121
121
  fields[:message] = validation_message unless validation_message.blank?
122
122
  fields[:pb_input_mask] = true if mask
123
123
  fields[:pb_emoji_mask] = true if emoji_mask
124
+ dv = filter_reset_default_value
125
+ fields[:default_value] = dv if dv.present?
124
126
  fields
125
127
  end
126
128
 
129
+ def filter_reset_default_value
130
+ return if value.nil?
131
+ return if value.is_a?(String) && value.empty?
132
+
133
+ value.to_s
134
+ end
135
+
127
136
  def error_class
128
137
  error ? " error" : ""
129
138
  end
@@ -58,7 +58,7 @@ $arrow_vertical_offset: calc(50% - #{$space_xs * 1.2});
58
58
 
59
59
  &.visible,
60
60
  &.show {
61
- z-index: $z_9;
61
+ z-index: $z_max;
62
62
  padding: $space_xs $space_sm;
63
63
  box-shadow: $shadow_deeper;
64
64
  border-radius: $border_rad_light;
@@ -160,7 +160,7 @@ $arrow_vertical_offset: calc(50% - #{$space_xs * 1.2});
160
160
 
161
161
  &.visible,
162
162
  &.show {
163
- z-index: $z_9;
163
+ z-index: $z_max;
164
164
  padding: $space_xs $space_sm;
165
165
  box-shadow: $shadow_deeper;
166
166
  border-radius: $border_rad_light;