playbook_ui 12.18.0.pre.alpha.play786multilevelselectimprovements627 → 12.19.0.pre.alpha.PLAY603datepickerquickpickinputpresetdropdown638

Sign up to get free protection for your applications and to get access to all the features.
Files changed (25) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_date_picker/_date_picker.scss +26 -0
  3. data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +99 -95
  4. data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +3 -2
  5. data/app/pb_kits/playbook/pb_date_picker/date_picker.rb +1 -1
  6. data/app/pb_kits/playbook/pb_date_picker/date_picker.test.js +44 -1
  7. data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +34 -2
  8. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick.html.erb +8 -0
  9. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick.jsx +18 -0
  10. data/app/pb_kits/playbook/pb_date_picker/docs/example.yml +2 -0
  11. data/app/pb_kits/playbook/pb_date_picker/docs/index.js +2 -1
  12. data/app/pb_kits/playbook/pb_date_picker/plugins/quickPick.tsx +161 -0
  13. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_calendar_input_icon.scss +3 -2
  14. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_quick_pick_styles.scss +75 -0
  15. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +2 -1
  16. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +7 -22
  17. data/app/pb_kits/playbook/pb_multi_level_select/helper_functions.ts +5 -32
  18. data/app/pb_kits/playbook/pb_nav/_item.tsx +1 -1
  19. data/app/pb_kits/playbook/pb_nav/_subtle_mixin.scss +1 -1
  20. data/app/pb_kits/playbook/pb_table/index.ts +6 -2
  21. data/app/pb_kits/playbook/pb_tooltip/_tooltip.tsx +11 -10
  22. data/app/pb_kits/playbook/pb_tooltip/tooltip.test.jsx +29 -0
  23. data/app/pb_kits/playbook/tokens/_colors.scss +5 -3
  24. data/lib/playbook/version.rb +2 -2
  25. metadata +6 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71565d1e911bd318cffad56ff64e34bab539bc740f5138b68d1f1896c1a6808e
4
- data.tar.gz: '0278d976d8320ee5013b5cc1cb9ae03349ba1948317c315624291a4ef1c9c93a'
3
+ metadata.gz: 453164dea66b229cdd84038a46f6178bf1008bbf17b5838d2f319e94f1f8fd55
4
+ data.tar.gz: 8cec3bedef3f08dda63bae0c5faa319542743c9c0e9ebaeef085d868510925e1
5
5
  SHA512:
6
- metadata.gz: 01c0a50b86ab971669772bfffb39f010929ca496ded033027592aac11de73efb74f40a13b24705a379bba1e81df2b0e4a863415ab1c51b281a1b313a967cd216
7
- data.tar.gz: e0703c831017c9f2adb8b05f0eeb61a934142e52beca288c37fa3ee08d6268a6db7198e146e2efb743238a5a5245b7bd54b1a5e0f2c5c7518eb63a8fed94e114
6
+ metadata.gz: e3982d4582e7c91ec22107b1bd2288f943dd22fe8d054060d56d4193e067a5041a0947e02a940f9873457c7566db4a1ec020858d2bee8aa78985e2430e7c7a6b
7
+ data.tar.gz: 4af1dfb8a83b8e74411aac7bf618f0f5f946facb7636e560eb87918aec8210ebb91a644d8ede92fbab461d61ba20d125f660a8f5497cd9fd8622af8e39dc2b72
@@ -6,6 +6,7 @@
6
6
  @import "./sass_partials/month_and_year_styles";
7
7
  @import "./sass_partials/time_selection_styles";
8
8
  @import "./sass_partials/input_styles";
9
+ @import "./sass_partials/quick_pick_styles";
9
10
 
10
11
  [class^=pb_date_picker_kit] {
11
12
  .input_wrapper {
@@ -26,7 +27,32 @@
26
27
  @include transition_default;
27
28
  border-color: $primary;
28
29
  }
30
+ .add-on-card {
31
+ border-color: $primary;
32
+ }
33
+ }
34
+
35
+ @media (hover: hover) {
36
+ &:hover {
37
+ .add-on-card {
38
+ cursor: pointer;
39
+ background-color: rgba($focus_input_light,$opacity_5);
40
+ }
41
+ }
42
+ }
43
+ .text_input_wrapper_add_on .add-on-icon {
44
+ color: $text_lt_light
29
45
  }
46
+
47
+ .text_input_wrapper_add_on .add-on-right .text_input{
48
+ cursor: pointer;
49
+ }
50
+
51
+ }
52
+
53
+
54
+ .pb_popover_reference_wrapper {
55
+ display: unset !important
30
56
  }
31
57
 
32
58
  .flatpickr-calendar {
@@ -30,7 +30,7 @@ type DatePickerProps = {
30
30
  inputAria?: { [key: string]: string },
31
31
  inputData?: { [key: string]: string },
32
32
  inputOnChange?: (e: React.FormEvent<HTMLInputElement>) => void,
33
- inputValue?: any,
33
+ inputValue?: string,
34
34
  label?: string,
35
35
  maxDate: string,
36
36
  minDate: string,
@@ -39,7 +39,7 @@ type DatePickerProps = {
39
39
  placeholder?: string,
40
40
  positionElement?: HTMLElement | null,
41
41
  scrollContainer?: string,
42
- selectionType?: "month" | "week",
42
+ selectionType?: "month" | "week"| "quickpick",
43
43
  showTimezone?: boolean,
44
44
  staticPosition: boolean,
45
45
  timeFormat?: string,
@@ -96,46 +96,46 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
96
96
  const inputAriaProps = buildAriaProps(inputAria)
97
97
  const inputDataProps = buildDataProps(inputData)
98
98
 
99
+ useEffect(() => {
100
+ datePickerHelper({
101
+ allowInput,
102
+ defaultDate,
103
+ disableDate,
104
+ disableRange,
105
+ disableWeekdays,
106
+ enableTime,
107
+ format,
108
+ hideIcon,
109
+ inLine,
110
+ maxDate,
111
+ minDate,
112
+ mode,
113
+ onChange,
114
+ onClose,
115
+ pickerId,
116
+ plugins,
117
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
118
+ // @ts-ignore
119
+ position,
120
+ positionElement,
121
+ selectionType,
122
+ showTimezone,
123
+ staticPosition,
124
+ yearRange,
125
+ required: false,
126
+ }, scrollContainer)
127
+ })
99
128
  const filteredProps = {...props}
100
129
  delete filteredProps?.position
101
130
 
102
131
  const classes = classnames(
103
132
  buildCss('pb_date_picker_kit'),
133
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
104
134
  //@ts-ignore
105
135
  globalProps(filteredProps),
106
136
  error ? 'error' : null,
107
137
  className
108
138
  )
109
-
110
- useEffect(() => {
111
- datePickerHelper({
112
- allowInput,
113
- defaultDate,
114
- disableDate,
115
- disableRange,
116
- disableWeekdays,
117
- enableTime,
118
- format,
119
- hideIcon,
120
- inLine,
121
- maxDate,
122
- minDate,
123
- mode,
124
- onChange,
125
- onClose,
126
- pickerId,
127
- plugins,
128
- // @ts-ignore
129
- position,
130
- positionElement,
131
- selectionType,
132
- showTimezone,
133
- staticPosition,
134
- yearRange,
135
- required: false,
136
- }, scrollContainer)
137
- })
138
-
139
139
  const iconWrapperClass = () => {
140
140
  let base = 'cal_icon_wrapper'
141
141
  if (dark) {
@@ -150,80 +150,84 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
150
150
  return base
151
151
  }
152
152
 
153
+
153
154
  return (
154
155
  <div
155
- {...ariaProps}
156
- {...dataProps}
157
- className={classes}
158
- id={id}
156
+ {...ariaProps}
157
+ {...dataProps}
158
+ className={classes}
159
+ id={id}
159
160
  >
160
161
  <div
161
- {...inputAriaProps}
162
- {...inputDataProps}
163
- className="input_wrapper">
162
+ {...inputAriaProps}
163
+ {...inputDataProps}
164
+ className="input_wrapper"
165
+ >
164
166
 
165
167
  <Caption
166
- className="pb_date_picker_kit_label"
167
- text={hideLabel ? null : label}
168
+ className="pb_date_picker_kit_label"
169
+ text={hideLabel ? null : label}
168
170
  />
169
-
170
- <div className="date_picker_input_wrapper">
171
- <input
172
- autoComplete="off"
173
- className="date_picker_input"
174
- disabled={disableInput}
175
- id={pickerId}
176
- name={name}
177
- onChange={inputOnChange}
178
- placeholder={placeholder}
179
- value={inputValue}
180
- />
181
-
182
- {error && <Body
183
- status="negative"
184
- text={error}
185
- variant={null}
186
- />
187
- }
188
- </div>
189
-
190
- {!hideIcon &&
191
- <div
192
- className={iconWrapperClass()}
193
- id={`cal-icon-${pickerId}`}
194
- >
195
- <Icon
196
- className="cal_icon"
197
- icon="calendar-alt"
198
- />
199
- </div>
200
- }
201
-
202
- {hideIcon && inLine ?
203
- <div>
204
- <div
205
- className={iconWrapperClass()}
206
- id={`${pickerId}-icon-plus`}
207
- >
208
- <Icon
209
- className="date-picker-plus-icon"
210
- icon="plus"
211
- />
212
- </div>
213
- <div
214
- className={iconWrapperClass()}
215
- id={`${pickerId}-angle-down`}
216
- >
217
- <Icon
218
- className="angle_down_icon"
219
- icon="angle-down"
171
+ <>
172
+ <div className="date_picker_input_wrapper">
173
+ <input
174
+ autoComplete="off"
175
+ className="date_picker_input"
176
+ disabled={disableInput}
177
+ id={pickerId}
178
+ name={name}
179
+ onChange={inputOnChange}
180
+ placeholder={placeholder}
181
+ value={inputValue}
220
182
  />
183
+
184
+ {error &&
185
+ <Body
186
+ status="negative"
187
+ text={error}
188
+ variant={null}
189
+ />
190
+ }
221
191
  </div>
222
- </div>
223
- : null}
192
+
193
+ {!hideIcon &&
194
+ <div
195
+ className={iconWrapperClass()}
196
+ id={`cal-icon-${pickerId}`}
197
+ >
198
+ <Icon
199
+ className="cal_icon"
200
+ icon="calendar-alt"
201
+ />
202
+ </div>
203
+ }
204
+
205
+ {hideIcon && inLine ?
206
+ <div>
207
+ <div
208
+ className={iconWrapperClass()}
209
+ id={`${pickerId}-icon-plus`}
210
+ >
211
+ <Icon
212
+ className="date-picker-plus-icon"
213
+ icon="plus"
214
+ />
215
+ </div>
216
+ <div
217
+ className={iconWrapperClass()}
218
+ id={`${pickerId}-angle-down`}
219
+ >
220
+ <Icon
221
+ className="angle_down_icon"
222
+ icon="angle-down"
223
+ />
224
+ </div>
225
+ </div>
226
+ : null
227
+ }
228
+ </>
224
229
  </div>
225
230
  </div>
226
231
  )
227
232
  }
228
-
229
233
  export default DatePicker
@@ -61,7 +61,8 @@
61
61
 
62
62
  <%= javascript_tag do %>
63
63
  window.addEventListener("DOMContentLoaded", () => {
64
- datePickerHelper(<%= object.date_picker_config %>, "<%= object.scroll_container %>")
64
+ datePickerHelper(<%= object.date_picker_config %>, "<%= object.scroll_container %>")
65
65
  })
66
+
66
67
  <% end %>
67
- <% end %>
68
+ <% end %>
@@ -51,7 +51,7 @@ module Playbook
51
51
  prop :position_element, type: Playbook::Props::String
52
52
  prop :scroll_container, type: Playbook::Props::String
53
53
  prop :selection_type, type: Playbook::Props::Enum,
54
- values: %w[week month none],
54
+ values: %w[week month quickpick none],
55
55
  default: "none"
56
56
  prop :show_timezone, type: Playbook::Props::Boolean,
57
57
  default: false
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable no-console */
2
2
  import React from 'react'
3
+ import moment from 'moment'
3
4
  import { fireEvent, render, screen, waitFor, within } from '../utilities/test-utils'
4
5
 
5
6
  import DatePicker from './_date_picker'
@@ -10,7 +11,6 @@ import { getTimezoneText } from './plugins/timeSelect'
10
11
  jest.setSystemTime(new Date('01/01/2020'));
11
12
  const DEFAULT_DATE = new Date()
12
13
 
13
-
14
14
  describe('DatePicker Kit', () => {
15
15
  beforeEach(() => {
16
16
  jest.spyOn(console, 'error').mockImplementation(() => { });
@@ -158,4 +158,47 @@ describe('DatePicker Kit', () => {
158
158
  expect(input).toHaveValue('01/01/2020 at 12:00 PM')
159
159
  })
160
160
  })
161
+ test('shows DatePicker QuickPick dropdown and adds correct date to input', async () => {
162
+ const testId = 'datepicker-quick-pick'
163
+ render(
164
+ <DatePicker
165
+ allowInput
166
+ data={{ testid: testId }}
167
+ mode="range"
168
+ pickerId="date-picker-quick-pick"
169
+ placeholder="mm/dd/yyyy → mm/dd/yyyy"
170
+ selectionType="quickpick"
171
+ />
172
+ )
173
+
174
+ const kit = screen.getByTestId(testId)
175
+ const input = within(kit).getByPlaceholderText('mm/dd/yyyy → mm/dd/yyyy')
176
+
177
+ fireEvent(
178
+ input,
179
+ new MouseEvent('click', {
180
+ bubbles: true,
181
+ cancelable: true,
182
+ }),
183
+ )
184
+ const today = within(kit).getByText('Today')
185
+ const thisYear = within(kit).getByText('This year')
186
+ await waitFor(() => {
187
+ expect(today).toBeInTheDocument()
188
+ expect(thisYear).toBeInTheDocument()
189
+ })
190
+
191
+ fireEvent(
192
+ thisYear,
193
+ new MouseEvent('click', {
194
+ bubbles: true,
195
+ cancelable: true,
196
+ }),
197
+ )
198
+
199
+ await waitFor(() => {
200
+ expect(input).toHaveValue(moment().startOf('year').format('MM/DD/YYYY') + " → " + moment().format('MM/DD/YYYY'))
201
+ })
202
+
203
+ })
161
204
  })
@@ -3,6 +3,7 @@ import { BaseOptions } from 'flatpickr/dist/types/options'
3
3
  import monthSelectPlugin from 'flatpickr/dist/plugins/monthSelect'
4
4
  import weekSelect from "flatpickr/dist/plugins/weekSelect/weekSelect"
5
5
  import timeSelectPlugin from './plugins/timeSelect'
6
+ import quickPickPlugin from './plugins/quickPick'
6
7
 
7
8
  const getPositionElement = (element: string | Element) => {
8
9
  return (typeof element === 'string') ? document.querySelectorAll(element)[0] : element
@@ -19,8 +20,8 @@ type DatePickerConfig = {
19
20
  hideIcon?: boolean;
20
21
  inLine?: boolean,
21
22
  onChange: (dateStr: string, selectedDates: Date[]) => void,
23
+ selectionType?: "month" | "week" | "quickpick" | "",
22
24
  onClose: (dateStr: Date[] | string, selectedDates: Date[] | string) => void,
23
- selectionType?: "month" | "week" | "",
24
25
  showTimezone?: boolean,
25
26
  staticPosition: boolean,
26
27
  timeCaption?: string,
@@ -90,18 +91,23 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
90
91
  }
91
92
 
92
93
  const setPlugins = () => {
93
- let pluginList = []
94
+ const pluginList = []
94
95
 
95
96
  // month and week selection
96
97
  if (selectionType === "month" || plugins.length > 0) {
97
98
  pluginList.push(monthSelectPlugin({ shorthand: true, dateFormat: 'F Y', altFormat: 'F Y' }))
98
99
  } else if ( selectionType === "week") {
99
100
  pluginList.push(weekSelect())
101
+
102
+ } else if (selectionType === "quickpick") {
103
+ //------- QUICKPICK VARIANT PLUGIN -------------//
104
+ pluginList.push(quickPickPlugin())
100
105
  }
101
106
 
102
107
  // time selection
103
108
  if (enableTime) pluginList.push(timeSelectPlugin({ caption: timeCaption, showTimezone: showTimezone}))
104
109
 
110
+
105
111
  return pluginList
106
112
  }
107
113
 
@@ -144,6 +150,9 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
144
150
  },
145
151
  ] : disabledParser(),
146
152
  enableTime,
153
+ locale: {
154
+ rangeSeparator: ' → '
155
+ },
147
156
  maxDate,
148
157
  minDate,
149
158
  mode,
@@ -171,10 +180,33 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
171
180
  static: staticPosition,
172
181
  })
173
182
 
183
+
174
184
  // ===========================================================
175
185
  // Additional JS Functionality |
176
186
  // ===========================================================
177
187
 
188
+ // opens flatpickr instance when calander icon is clicked so we can have a hover state on icon
189
+ // window.addEventListener("DOMContentLoaded", (event) => {
190
+ // event.preventDefault();
191
+ // function attachIconClickHandler() {
192
+ // const variantArr = document.querySelectorAll(`#cal-icon-${pickerId}`)
193
+ // if (!variantArr) {
194
+ // setTimeout(attachIconClickHandler, 100);
195
+ // return;
196
+ // }
197
+
198
+ // const instance = document.querySelector<HTMLElement & { [x: string]: any }>(`#${pickerId}`)._flatpickr
199
+ // variantArr.forEach((icon) => {
200
+ // icon.addEventListener("click", function(event) {
201
+ // event.preventDefault();
202
+ // instance.open()
203
+ // });
204
+ // })
205
+ // }
206
+
207
+ // attachIconClickHandler();
208
+ // });
209
+
178
210
  // Assign dynamically sourced flatpickr instance to variable
179
211
  const picker = document.querySelector<HTMLElement & { [x: string]: any }>(`#${pickerId}`)._flatpickr
180
212
  picker.innerContainer.parentElement.id = `cal-${pickerId}`
@@ -0,0 +1,8 @@
1
+ <%= pb_rails("date_picker", props: {
2
+ allow_input: true,
3
+ mode: "range",
4
+ picker_id: "date-picker-quick-pick",
5
+ placeholder: "mm/dd/yyyy → mm/dd/yyyy",
6
+ selection_type: "quickpick"
7
+ }) %>
8
+
@@ -0,0 +1,18 @@
1
+ import React from 'react'
2
+
3
+ import DatePicker from '../_date_picker'
4
+
5
+ const DatePickerQuickPick = (props) => (
6
+ <div>
7
+ <DatePicker
8
+ allowInput
9
+ mode="range"
10
+ pickerId="date-picker-quick-pick"
11
+ placeholder="mm/dd/yyyy → mm/dd/yyyy"
12
+ selectionType="quickpick"
13
+ {...props}
14
+ />
15
+ </div>
16
+ )
17
+
18
+ export default DatePickerQuickPick
@@ -8,6 +8,7 @@ examples:
8
8
  - date_picker_input: Input Field
9
9
  - date_picker_label: Label
10
10
  - date_picker_range: Range
11
+ - date_picker_quick_pick: Range (Quick Pick)
11
12
  - date_picker_format: Format
12
13
  - date_picker_disabled: Disabled Dates
13
14
  - date_picker_min_max: Min Max
@@ -33,6 +34,7 @@ examples:
33
34
  - date_picker_on_change: onChange
34
35
  - date_picker_on_close: onClose
35
36
  - date_picker_range: Range
37
+ - date_picker_quick_pick: Range (Quick Pick)
36
38
  - date_picker_format: Format
37
39
  - date_picker_disabled: Disabled Dates
38
40
  - date_picker_min_max: Min Max
@@ -19,4 +19,5 @@ export { default as DatePickerWeek } from './_date_picker_week.jsx'
19
19
  export { default as DatePickerPositions } from './_date_picker_positions.jsx'
20
20
  export { default as DatePickerPositionsElement } from './_date_picker_positions_element.jsx'
21
21
  export { default as DatePickerAllowInput } from './_date_picker_allow_input'
22
- export { default as DatePickerOnClose } from './_date_picker_on_close.jsx'
22
+ export { default as DatePickerQuickPick } from './_date_picker_quick_pick'
23
+ export { default as DatePickerOnClose } from './_date_picker_on_close.jsx'
@@ -0,0 +1,161 @@
1
+ import moment from 'moment'
2
+
3
+ type FpTypes = {
4
+ setDate: (arg0: any, arg1: boolean) => void,
5
+ config: { [key: string]: string },
6
+ clear: (arg0: boolean, arg1: boolean) => void,
7
+ close: () => void,
8
+ calendarContainer?: {
9
+ classList: { add: (arg0: string) => void };
10
+ prepend: (arg0: HTMLDivElement) => void;
11
+ append: (arg0: HTMLDivElement) => void;
12
+ },
13
+ loadedPlugins: string[],
14
+ };
15
+
16
+ type pluginDataType = {
17
+ ranges: { [key: string]: Date[] },
18
+ rangesNav: HTMLUListElement,
19
+ rangesButtons: [] | any,
20
+ }
21
+
22
+ const quickPickPlugin = () => {
23
+ return function (fp: FpTypes & any): any {
24
+
25
+ let activeLabel = ""
26
+
27
+ // variable that holds the ranges available
28
+ const ranges = {
29
+ 'Today': [new Date(), new Date()],
30
+ 'Yesterday': [moment().subtract(1, 'days').toDate(), moment().subtract(1, 'days').toDate()],
31
+ 'This week': [moment().startOf('week').toDate(), moment().endOf('week').toDate()],
32
+ 'This month': [moment().startOf('month').toDate(), new Date()],
33
+ 'This quarter': [moment().startOf('quarter').toDate(), new Date()],
34
+ 'This year': [moment().startOf('year').toDate(), new Date()],
35
+ 'Last week': [
36
+ moment().subtract(1, 'week').startOf('week').toDate(),
37
+ moment().subtract(1, 'week').endOf('week').toDate()
38
+ ],
39
+ 'Last month': [
40
+ moment().subtract(1, 'month').startOf('month').toDate(),
41
+ moment().subtract(1, 'month').endOf('month').toDate()
42
+ ],
43
+ 'Last quarter': [
44
+ moment().subtract(1, 'quarter').startOf('quarter').toDate(),
45
+ moment().subtract(1, 'quarter').endOf('quarter').toDate()
46
+ ],
47
+ 'Last year': [
48
+ moment().subtract(1, 'year').startOf('year').toDate(),
49
+ moment().subtract(1, 'year').endOf('year').toDate()
50
+ ]
51
+ }
52
+ //creating the ul element for the nav dropdown and giving it classnames
53
+ const rangesNav = document.createElement('ul');
54
+
55
+ // creating the pluginData object that will hold the properties of this plugin
56
+ const pluginData: pluginDataType = {
57
+ ranges: ranges,
58
+ rangesNav: rangesNav,
59
+ rangesButtons: [],
60
+ };
61
+
62
+ /**
63
+ * @param {string} label
64
+ * @returns HTML Element
65
+ */
66
+
67
+ //funciton for creating the range buttons in the nav
68
+ const addRangeButton = (label: string) => {
69
+
70
+ // creating new elements to mimick selectable card component
71
+ const div2 = document.createElement('div');
72
+ div2.className = "nav-item-link"
73
+ div2.innerHTML = label;
74
+
75
+ pluginData.rangesButtons[label] = div2;
76
+
77
+ // create li elements inside the dropdown
78
+ const item = document.createElement('li');
79
+ item.className = "nav-item";
80
+
81
+ // append those nav items to the li items
82
+ item.appendChild(pluginData.rangesButtons[label]);
83
+
84
+ // append the li item to the ul rangeNav prop
85
+ pluginData.rangesNav.appendChild(item);
86
+
87
+ // return the ranges buton prop
88
+ return pluginData.rangesButtons[label];
89
+ };
90
+
91
+ const selectActiveRangeButton = (selectedDates: Array<string>) => {
92
+ const current = pluginData.rangesNav.querySelector('.active');
93
+
94
+ if (current) {
95
+ current.classList.remove('active');
96
+ }
97
+ /** conditional statment to extract start and end dates from selectedDates,
98
+ * then loop through ranges prop in pluginData
99
+ * and check if chosen dates equal to a date in the ranges prop
100
+ * if they are equal, add the active class
101
+ */
102
+ if (selectedDates.length > 0) {
103
+ pluginData.rangesButtons[activeLabel].classList.add('active');
104
+ }
105
+ }
106
+
107
+
108
+ return {
109
+ // onReady is a hook from flatpickr that runs when calender is in a ready state
110
+ onReady(selectedDates: Array<string>) {
111
+ // loop through the ranges and create an anchor tag for each range and add an event listiner to set the date when user clicks on a date range
112
+ for (const [label, range] of Object.entries(pluginData.ranges)) {
113
+ addRangeButton(label).addEventListener('click', function () {
114
+
115
+ const start = moment(range[0]).toDate();
116
+ const end = moment(range[1]).toDate();
117
+
118
+ if (!start) {
119
+ fp.clear();
120
+ }
121
+ else {
122
+ activeLabel = label
123
+ fp.setDate([start, end], true);
124
+ fp.close();
125
+ }
126
+
127
+ });
128
+ }
129
+
130
+ // conditional to check if there is a dropdown to add it to the calendar container and git it the classes it needs
131
+ if (pluginData.rangesNav.children.length > 0) {
132
+
133
+ fp.calendarContainer.prepend(pluginData.rangesNav);
134
+ pluginData.rangesNav.classList.add('quick-pick-ul')
135
+ fp.calendarContainer.classList.add('quick-pick-drop-down');
136
+
137
+ /**
138
+ *
139
+ * @param {Array} selectedDates
140
+ */
141
+
142
+ // function to give the active butto the active class
143
+ selectActiveRangeButton(selectedDates);
144
+ }
145
+
146
+ },
147
+ onValueUpdate(selectedDates: Array<string>) {
148
+ selectActiveRangeButton(selectedDates);
149
+ },
150
+
151
+ // onClose(selectedDates: Array<string>) {
152
+ // // set the input value to the selected dates when the dropdown is closed
153
+ // // if (!selectedDates[1]) {
154
+ // // fp.input.value = fp.formatDate(this.selectedDates[0], fp.config.dateFormat) + ' → ' + fp.formatDate(this.selectedDates[1], fp.config.dateFormat);
155
+ // // }
156
+ // }
157
+ };
158
+ };
159
+ }
160
+
161
+ export default quickPickPlugin;
@@ -1,3 +1,4 @@
1
+ @import "../../tokens/colors";
1
2
  // Calendar Icon Styles
2
3
  .cal_icon_wrapper {
3
4
  pointer-events: none;
@@ -13,8 +14,8 @@
13
14
  padding-left: $space_sm - 1;
14
15
  color: $text_lt_light;
15
16
  @media (hover: hover) {
16
- &:hover {
17
- cursor: pointer;
17
+ &:hover{
18
+ background-color: rgba($focus_input_light,$opacity_5);
18
19
  }
19
20
  }
20
21
  &.dark {
@@ -0,0 +1,75 @@
1
+ @import "../../tokens/animation-curves";
2
+ @import "../../tokens/colors";
3
+ @import "../../tokens/typography";
4
+ @import "../../tokens/titles";
5
+ @import "../../tokens/spacing";
6
+
7
+ $pb_card_border_width: 1px;
8
+ $pb_card_border_radius: $border_rad_heavier;
9
+
10
+ // used to display dropdown on the left of the calender
11
+ .quick-pick-drop-down {
12
+ width: auto;
13
+ display: grid;
14
+ }
15
+
16
+ .quick-pick-ul {
17
+ padding: $space_xs 0px;
18
+ margin: 0;
19
+ list-style: none;
20
+ }
21
+
22
+ .nav-item {
23
+ list-style: none;
24
+ border-radius: 6px;
25
+ border-bottom: 0;
26
+ margin: $space_xs $space_sm;
27
+ }
28
+
29
+ .nav-item-link {
30
+ text-decoration: none;
31
+ border-width: $pb_card_border_width;
32
+ border-style: solid;
33
+ border-color: $border_light;
34
+ border-radius: $pb_card_border_radius;
35
+ padding: $space_xs 14px;
36
+ transition-property: color, background-color;
37
+ transition-duration: 0.15s;
38
+ transition-timing-function: $bezier;
39
+ line-height: 1.4;
40
+ color: $charcoal;
41
+ font-size: $font_default;
42
+ font-weight: $regular;
43
+ &.active {
44
+ border-width: 2px;
45
+ border-color: $primary;
46
+ }
47
+ @media (hover:hover) {
48
+ &:hover {
49
+ cursor: pointer;
50
+ box-shadow: $shadow-deep;
51
+ border-color: $slate;
52
+ }
53
+ }
54
+ }
55
+
56
+ // Hide the calendar
57
+ .quick-pick-drop-down > .flatpickr-months, .quick-pick-drop-down > .flatpickr-innerContainer {
58
+ display: none;
59
+ }
60
+
61
+ @media only screen and (max-width: 767px) {
62
+ .quick-pick-ul {
63
+ padding: $space_xs $space_xs;
64
+ display: grid;
65
+ grid-template-columns: 1fr 1fr;
66
+ }
67
+
68
+ .nav-item {
69
+ margin: $space_xxs $space_xs;
70
+ }
71
+
72
+ .nav-item-link {
73
+ padding: $space_xs $space_xxs;
74
+ }
75
+ }
@@ -36,9 +36,10 @@
36
36
  vertical-align: top;
37
37
  width: 0.6em;
38
38
  top: 10px;
39
+ transform: rotate(135deg);
39
40
  font-size: 0.5em;
40
41
 
41
- transform: rotate(45deg);
42
+ transform: rotate(-45deg);
42
43
  top: 12px;
43
44
  }
44
45
 
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useMemo } from "react";
2
2
  import classnames from "classnames";
3
3
  import { buildAriaProps, buildCss, buildDataProps } from "../utilities/props";
4
4
  import { globalProps } from "../utilities/globalProps";
5
- import { findItemById, checkIt, unCheckIt, getParentAndAncestorsIds } from "./helper_functions";
5
+ import { findItemById, checkIt, unCheckIt } from "./helper_functions";
6
6
  import MultiSelectHelper from "./_multi_select_helper";
7
7
 
8
8
  type MultiLevelSelectProps = {
@@ -42,31 +42,17 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
42
42
  const updatedData = formattedData.map((item: any) => {
43
43
  if (item.id === currentNode._id) {
44
44
  if (currentNode.checked) {
45
- checkIt(item, selectedItems, setSelectedItems, false);
45
+ checkIt(item, selectedItems, setSelectedItems);
46
46
  } else {
47
- unCheckIt(item, selectedItems, setSelectedItems, false);
47
+ unCheckIt(item, selectedItems, setSelectedItems);
48
48
  }
49
49
  } else if (item.children) {
50
50
  const foundItem = findItemById(item.children, currentNode._id);
51
51
  if (foundItem) {
52
52
  if (currentNode.checked) {
53
- checkIt(foundItem, selectedItems, setSelectedItems, false);
54
- if (currentNode._parent) {
55
- const parents = getParentAndAncestorsIds(currentNode._parent, formattedData)
56
- parents.forEach((item:string) => {
57
- const ancestor = findItemById(formattedData,item)
58
- ancestor.expanded = true
59
- });
60
- }
53
+ checkIt(foundItem, selectedItems, setSelectedItems);
61
54
  } else {
62
- unCheckIt(foundItem, selectedItems, setSelectedItems, false);
63
- if (currentNode._parent) {
64
- const parents = getParentAndAncestorsIds(currentNode._parent, formattedData)
65
- parents.forEach((item:string) => {
66
- const ancestor = findItemById(formattedData,item)
67
- ancestor.expanded = true
68
- });
69
- }
55
+ unCheckIt(foundItem, selectedItems, setSelectedItems);
70
56
  }
71
57
  }
72
58
  }
@@ -106,18 +92,17 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
106
92
  treeData={formattedData}
107
93
  onChange={(
108
94
  // @ts-ignore
109
- selectedNodes: { [key: string]: any }[],
95
+ selectedNodes: { [key: string]: any }[],
110
96
  currentNode: { [key: string]: any }[]
111
97
  ) => {
112
98
  setCheckedData(currentNode);
113
99
  onSelect(currentNode);
114
-
115
100
  }}
116
101
  id={id}
117
102
  {...props}
118
103
  />
119
104
  );
120
- }, [formattedData])
105
+ }, [formattedData]);
121
106
 
122
107
  return (
123
108
  <div {...ariaProps} {...dataProps} className={classes} id={id}>
@@ -19,20 +19,19 @@ export const findItemById = (
19
19
  export const checkIt = (
20
20
  foundItem: { [key: string]: any },
21
21
  selectedItems: any[],
22
- setSelectedItems: Function,
23
- expand: boolean
22
+ setSelectedItems: Function
24
23
  ) => {
25
24
  if (!foundItem) {
26
25
  return;
27
26
  }
28
27
 
29
28
  foundItem.checked = true;
30
- foundItem.expanded = expand;
29
+ foundItem.expanded = true;
31
30
  selectedItems.push(foundItem);
32
31
 
33
32
  if (foundItem.children) {
34
33
  foundItem.children.map((x: any) => {
35
- checkIt(x, selectedItems, setSelectedItems, expand);
34
+ checkIt(x, selectedItems, setSelectedItems);
36
35
  });
37
36
  }
38
37
 
@@ -42,46 +41,20 @@ export const checkIt = (
42
41
  export const unCheckIt = (
43
42
  foundItem: { [key: string]: any },
44
43
  selectedItems: any,
45
- setSelectedItems: any,
46
- expand: boolean
44
+ setSelectedItems: any
47
45
  ) => {
48
46
  if (!foundItem) {
49
47
  return;
50
48
  }
51
49
 
52
50
  foundItem.checked = false;
53
- foundItem.expanded = false;
54
51
  const newSelectedItems = selectedItems.filter(
55
52
  (item: any) => item.id !== foundItem.id
56
53
  );
57
54
  if (foundItem.children) {
58
55
  foundItem.children.map((x: any) => {
59
- unCheckIt(x, selectedItems, setSelectedItems, expand);
56
+ unCheckIt(x, selectedItems, setSelectedItems);
60
57
  });
61
58
  }
62
59
  setSelectedItems([...newSelectedItems]);
63
60
  };
64
-
65
-
66
- export const getParentAndAncestorsIds = (itemId:string, items:{ [key: string]: string; }[], ancestors:string[] = []):any => {
67
- for (let i = 0; i < items.length; i++) {
68
- const item:any = items[i];
69
- if (item.id === itemId) {
70
- // item found in current level of items array
71
- return [...ancestors, item.id];
72
- }
73
- if (item.children && item.children.length > 0) {
74
- // recursively search through children
75
- const foundAncestors = getParentAndAncestorsIds(
76
- itemId,
77
- item.children,
78
- [...ancestors, item.id]
79
- );
80
- if (foundAncestors) {
81
- return foundAncestors;
82
- }
83
- }
84
- }
85
- // item not found in this level of items array or its children
86
- return null;
87
- }
@@ -87,7 +87,7 @@ const NavItem = (props: NavItemProps) => {
87
87
  <span className="pb_nav_list_item_text">
88
88
  {text || children}
89
89
  </span>
90
-
90
+
91
91
  {iconRight &&
92
92
  <div
93
93
  className="pb_nav_list_item_icon_section"
@@ -44,7 +44,7 @@
44
44
  &[class*=_active] [class*=_link] {
45
45
  @include pb_title_4;
46
46
  color: $primary;
47
- letter-spacing: normal;
47
+ letter-spacing: normal;
48
48
  }
49
49
  }
50
50
  }
@@ -11,8 +11,12 @@ export default class PbTable extends PbEnhancedElement {
11
11
  // Each Table
12
12
  [].forEach.call(tables, (table: HTMLTableElement) => {
13
13
  // Header Titles
14
- var headers = [].map.call(table.querySelectorAll('th'), (header: Element) => {
15
- return header.textContent.replace(/\r?\n|\r/, '')
14
+ let headers: string[] = [];
15
+ [].forEach.call(table.querySelectorAll('th'), (header: HTMLTableCellElement) => {
16
+ let colSpan = header.colSpan
17
+ for (let i = 0; i < colSpan; i++) {
18
+ headers.push(header.textContent.replace(/\r?\n|\r/, ''));
19
+ }
16
20
  });
17
21
 
18
22
  // for each row in tbody
@@ -6,7 +6,7 @@ import {
6
6
  offset,
7
7
  Placement,
8
8
  safePolygon,
9
- shift,
9
+ shift,
10
10
  useFloating,
11
11
  useHover,
12
12
  useInteractions,
@@ -17,9 +17,6 @@ import { GlobalProps, globalProps } from "../utilities/globalProps"
17
17
  import { buildAriaProps, buildDataProps } from "../utilities/props"
18
18
  import Flex from "../pb_flex/_flex"
19
19
 
20
-
21
-
22
-
23
20
  type TooltipProps = {
24
21
  aria?: { [key: string]: string },
25
22
  className?: string | string[],
@@ -29,8 +26,8 @@ type TooltipProps = {
29
26
  icon?: string,
30
27
  interaction?: boolean,
31
28
  placement?: Placement,
29
+ position?: "absolute" | "fixed";
32
30
  text: string,
33
- zIndex?: Pick<GlobalProps, "ZIndex">,
34
31
  } & GlobalProps
35
32
 
36
33
  const Tooltip = (props: TooltipProps): React.ReactElement => {
@@ -43,6 +40,7 @@ const Tooltip = (props: TooltipProps): React.ReactElement => {
43
40
  icon = null,
44
41
  interaction = false,
45
42
  placement: preferredPlacement = "top",
43
+ position = "absolute",
46
44
  text,
47
45
  zIndex,
48
46
  ...rest
@@ -50,24 +48,26 @@ const Tooltip = (props: TooltipProps): React.ReactElement => {
50
48
 
51
49
  const dataProps: { [key: string]: any } = buildDataProps(data)
52
50
  const ariaProps: { [key: string]: any } = buildAriaProps(aria)
53
-
51
+
54
52
  const css = classnames(
55
53
  globalProps({...rest}),
56
54
  className,
57
55
  )
58
56
  const [open, setOpen] = useState(false)
59
57
  const arrowRef = useRef(null)
60
- const {
61
58
 
59
+
60
+ const {
62
61
  context,
63
62
  floating,
64
- middlewareData: { arrow: { x: arrowX, y: arrowY } = {} },
63
+ middlewareData: { arrow: { x: arrowX, y: arrowY } = {}, },
65
64
  placement,
66
65
  reference,
67
66
  strategy,
68
67
  x,
69
68
  y,
70
69
  } = useFloating({
70
+ strategy: position,
71
71
  middleware: [
72
72
  arrow({
73
73
  element: arrowRef,
@@ -87,6 +87,7 @@ const Tooltip = (props: TooltipProps): React.ReactElement => {
87
87
  placement: preferredPlacement
88
88
  })
89
89
 
90
+
90
91
  const { getFloatingProps } = useInteractions([
91
92
  useHover(context, {
92
93
  delay,
@@ -142,7 +143,7 @@ const Tooltip = (props: TooltipProps): React.ReactElement => {
142
143
  className="arrow_bg"
143
144
  ref={arrowRef}
144
145
  style={{
145
- position: strategy,
146
+ position: "absolute",
146
147
  left: arrowX != null ? `${arrowX}px` : "",
147
148
  top: arrowY != null ? `${arrowY}px` : "",
148
149
  [staticSide]: "-5px",
@@ -154,4 +155,4 @@ const Tooltip = (props: TooltipProps): React.ReactElement => {
154
155
  )
155
156
  }
156
157
 
157
- export default Tooltip
158
+ export default Tooltip
@@ -65,3 +65,32 @@ test("closes on mouseleave", async () => {
65
65
 
66
66
  cleanup();
67
67
  });
68
+
69
+ test("has default position absolute", async () => {
70
+ render(<TooltipTest />);
71
+
72
+ fireEvent.mouseEnter(screen.getByRole("tooltip_trigger"));
73
+ await waitFor(() => {
74
+ expect(screen.queryByRole("tooltip")).toHaveStyle({"position": "absolute"});
75
+ cleanup();
76
+ })
77
+
78
+ cleanup();
79
+ });
80
+
81
+ test("has position fixed", async () => {
82
+ render(
83
+ <Tooltip
84
+ data={{ testid: "fixed-position-test" }}
85
+ position="fixed"
86
+ />
87
+ );
88
+
89
+ fireEvent.mouseEnter(screen.getByRole("tooltip_trigger"));
90
+ await waitFor(() => {
91
+ expect(screen.queryByRole("tooltip")).toHaveStyle({"position": "fixed"});
92
+ cleanup();
93
+ })
94
+
95
+ cleanup();
96
+ });
@@ -68,7 +68,7 @@ $background_colors: (
68
68
 
69
69
  /* Card colors ------------------*/
70
70
  $card_light: $white !default;
71
- $card_dark: rgba($white, $opacity_1) !default;
71
+ $card_dark: mix(white, $bg_dark, 10%) !default;
72
72
  $card_colors: (
73
73
  card_light: $card_light,
74
74
  card_dark: $card_dark
@@ -109,16 +109,18 @@ $focus_input_colors: (
109
109
 
110
110
  /* Border colors ----------------------*/
111
111
  $border_light: #E4E8F0 !default;
112
- $border_dark: rgba($white, $opacity_1) !default;
112
+ $border_dark: mix(white, $bg_dark, 20%) !default;
113
113
  $border_colors: (
114
114
  border_light: $border_light,
115
115
  border_dark: $border_dark
116
116
  );
117
117
 
118
118
  /* Shadow colors ----------------------*/
119
- $shadow: rgba(#3C6AAC, $opacity_2) !default;
119
+ $shadow: rgba(#3C6AAC, $opacity_2) !default;
120
+ $shadow_dark: $bg_dark !default;
120
121
  $shadow_colors: (
121
122
  shadow: $shadow,
123
+ shadow_dark: $shadow_dark,
122
124
  );
123
125
 
124
126
  /* Text colors ------------------------*/
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Playbook
4
- PREVIOUS_VERSION = "12.18.0"
5
- VERSION = "12.18.0.pre.alpha.play786multilevelselectimprovements627"
4
+ PREVIOUS_VERSION = "12.19.0"
5
+ VERSION = "12.19.0.pre.alpha.PLAY603datepickerquickpickinputpresetdropdown638"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: playbook_ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 12.18.0.pre.alpha.play786multilevelselectimprovements627
4
+ version: 12.19.0.pre.alpha.PLAY603datepickerquickpickinputpresetdropdown638
5
5
  platform: ruby
6
6
  authors:
7
7
  - Power UX
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-05-09 00:00:00.000000000 Z
12
+ date: 2023-05-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack
@@ -745,6 +745,8 @@ files:
745
745
  - app/pb_kits/playbook/pb_date_picker/docs/_date_picker_positions.md
746
746
  - app/pb_kits/playbook/pb_date_picker/docs/_date_picker_positions_element.html.erb
747
747
  - app/pb_kits/playbook/pb_date_picker/docs/_date_picker_positions_element.jsx
748
+ - app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick.html.erb
749
+ - app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick.jsx
748
750
  - app/pb_kits/playbook/pb_date_picker/docs/_date_picker_range.html.erb
749
751
  - app/pb_kits/playbook/pb_date_picker/docs/_date_picker_range.jsx
750
752
  - app/pb_kits/playbook/pb_date_picker/docs/_date_picker_time.html.erb
@@ -759,6 +761,7 @@ files:
759
761
  - app/pb_kits/playbook/pb_date_picker/docs/_description.md
760
762
  - app/pb_kits/playbook/pb_date_picker/docs/example.yml
761
763
  - app/pb_kits/playbook/pb_date_picker/docs/index.js
764
+ - app/pb_kits/playbook/pb_date_picker/plugins/quickPick.tsx
762
765
  - app/pb_kits/playbook/pb_date_picker/plugins/timeSelect.ts
763
766
  - app/pb_kits/playbook/pb_date_picker/sass_partials/_calendar_input_icon.scss
764
767
  - app/pb_kits/playbook/pb_date_picker/sass_partials/_day_styles.scss
@@ -768,6 +771,7 @@ files:
768
771
  - app/pb_kits/playbook/pb_date_picker/sass_partials/_input_styles.scss
769
772
  - app/pb_kits/playbook/pb_date_picker/sass_partials/_month_and_year_styles.scss
770
773
  - app/pb_kits/playbook/pb_date_picker/sass_partials/_overrides.scss
774
+ - app/pb_kits/playbook/pb_date_picker/sass_partials/_quick_pick_styles.scss
771
775
  - app/pb_kits/playbook/pb_date_picker/sass_partials/_time_selection_styles.scss
772
776
  - app/pb_kits/playbook/pb_date_picker/sass_partials/_week_styles.scss
773
777
  - app/pb_kits/playbook/pb_date_range_inline/_date_range_inline.scss