playbook_ui 14.5.0.pre.alpha.PBNTR374multilevelselectPOC3946 → 14.5.0.pre.alpha.PBNTR568dropdowncleaning4041

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/index.js +60 -0
  3. data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +1 -9
  4. data/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.html.erb +1 -9
  5. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +20 -4
  6. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.jsx +45 -0
  7. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +1 -0
  8. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +1 -0
  9. data/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx +9 -1
  10. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.html.erb +19 -0
  11. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.jsx +27 -0
  12. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.md +1 -0
  13. data/app/pb_kits/playbook/pb_form_pill/docs/example.yml +2 -0
  14. data/app/pb_kits/playbook/pb_form_pill/docs/index.js +1 -0
  15. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +2 -2
  16. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.jsx +5 -7
  17. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.md +1 -0
  18. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.jsx +5 -7
  19. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.md +1 -0
  20. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +4 -1
  21. data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +3 -1
  22. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +3 -1
  23. data/dist/chunks/{_typeahead-DMIBeCYd.js → _typeahead-CT2ByCBK.js} +1 -1
  24. data/dist/chunks/{_weekday_stacked-CviGlw2m.js → _weekday_stacked-CwIBeloD.js} +2 -2
  25. data/dist/chunks/vendor.js +1 -1
  26. data/dist/playbook-doc.js +1 -1
  27. data/dist/playbook-rails-react-bindings.js +1 -1
  28. data/dist/playbook-rails.js +1 -1
  29. data/lib/playbook/version.rb +1 -1
  30. metadata +10 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 650696e840933991a498e8b6af55eaef10722d1f4e6c8f00255a6bd6454c38cd
4
- data.tar.gz: e3d5c195a361cd5685bf6c197ab1b22e3fd2113660b704bcc3fa640027612cae
3
+ metadata.gz: 7276cf8a18d6ba899d40091f3a8cee38bc28a154cecf9f7390da283daac0e44a
4
+ data.tar.gz: f99c6b2f4ad8580b33b64b91888f20dafdd7df7603cd44a26c6803f6f2bf505a
5
5
  SHA512:
6
- metadata.gz: 80c9d2340f45cb1bfcf86ca304b0d613d5e5c2387293b0a7fdebeec9f9da184bbb1c5e7ec1aa8caaf6b4d82d4fd9b4d0225b4f9aa02882e0d6bcd7d0774fadfa
7
- data.tar.gz: 3027bb6d7df594ef4cd455c135c31a91a8418d9e12b5cf0c515f2ce2152bf4e2a6753eb87efb606c79780b95b7d5ef625aabd7674696ffb0e2269a3b17762da1
6
+ metadata.gz: a5dcbd59b747abf89a5959410e6fe2cf8feab4032ef31cff0b19a46c3ceaf27bb4fbec97882ed421ddf4069bbcaeeb668a56b343b635372d70bf07376d4d473f
7
+ data.tar.gz: 44bf8232614c890c34d265cd1bee21eed7967aadaede86121c594ad3d7531dd29647c2c6c72c6bc67d743fb5c9b8f613c6f38185883c0c5fb3c8f6e0e1607928
@@ -13,9 +13,20 @@ export default class PbAdvancedTable extends PbEnhancedElement {
13
13
  get target() {
14
14
  return document.querySelector(CONTENT_SELECTOR.replace("id", this.element.id))
15
15
  }
16
+
17
+ static expandedRows = new Set()
18
+ static isCollapsing = false
16
19
 
17
20
  connect() {
18
21
  this.element.addEventListener('click', () => {
22
+ if (!PbAdvancedTable.isCollapsing) {
23
+ const isExpanded = this.element.querySelector(UP_ARROW_SELECTOR).style.display === 'inline-block'
24
+ if (!isExpanded) {
25
+ PbAdvancedTable.expandedRows.add(this.element.id)
26
+ } else {
27
+ PbAdvancedTable.expandedRows.delete(this.element.id)
28
+ }
29
+ }
19
30
  this.toggleElement(this.target)
20
31
  })
21
32
  }
@@ -75,4 +86,53 @@ export default class PbAdvancedTable extends PbEnhancedElement {
75
86
  this.element.querySelector(UP_ARROW_SELECTOR).style.display = 'inline-block'
76
87
  this.element.querySelector(DOWN_ARROW_SELECTOR).style.display = 'none'
77
88
  }
89
+
90
+ static handleToggleAllHeaders(element) {
91
+ const table = element.closest('.pb_table')
92
+ const firstLevelButtons = table.querySelectorAll('.pb_advanced_table_body > .pb_table_tr [data-advanced-table]')
93
+
94
+ const expandedRows = Array.from(firstLevelButtons).filter(button =>
95
+ button.querySelector(UP_ARROW_SELECTOR).style.display === 'inline-block'
96
+ )
97
+
98
+ if (expandedRows.length === firstLevelButtons.length) {
99
+ expandedRows.forEach(button => {
100
+ button.click()
101
+ })
102
+ this.expandedRows.clear()
103
+ } else {
104
+ firstLevelButtons.forEach(button => {
105
+ if (!this.expandedRows.has(button.id)) {
106
+ button.click()
107
+ }
108
+ })
109
+ }
110
+ }
111
+ static handleToggleAllSubRows(element, rowDepth) {
112
+ const parentElement = element.closest(".toggle-content")
113
+ const subrowButtons = parentElement.querySelectorAll('.depth-sub-row-' + rowDepth + ' [data-advanced-table]')
114
+
115
+ const expandedSubRows = Array.from(subrowButtons).filter(button =>
116
+ button.querySelector(UP_ARROW_SELECTOR).style.display === 'inline-block'
117
+ )
118
+
119
+ if (expandedSubRows.length === subrowButtons.length) {
120
+ expandedSubRows.forEach(button => {
121
+ button.click()
122
+ })
123
+ } else {
124
+ subrowButtons.forEach(button => {
125
+ if (!this.expandedRows.has(button.id)) {
126
+ button.click()
127
+ }
128
+ })
129
+ }
130
+ }
131
+ }
132
+
133
+ window.expandAllRows = (element) => {
134
+ PbAdvancedTable.handleToggleAllHeaders(element)
78
135
  }
136
+ window.expandAllSubRows = (element, rowDepth) => {
137
+ PbAdvancedTable.handleToggleAllSubRows(element, rowDepth)
138
+ }
@@ -13,12 +13,4 @@
13
13
  <% end %>
14
14
  <% end %>
15
15
  <% end %>
16
- <% end %>
17
-
18
- <script type="text/javascript">
19
- var expandAllRows = (element) => {
20
- element.closest('.pb_table').querySelectorAll('.pb_advanced_table_body > .pb_table_tr [data-advanced-table]').forEach((button) => {
21
- button.dispatchEvent(new Event('click'));
22
- });
23
- };
24
- </script>
16
+ <% end %>
@@ -23,12 +23,4 @@
23
23
  <% end %>
24
24
  <% end %>
25
25
  <% end %>
26
- <% end %>
27
-
28
- <script type="text/javascript">
29
- var expandAllSubRows = (element, rowDepth) => {
30
- element.closest(".toggle-content").querySelectorAll('.depth-sub-row-' + rowDepth + ' [data-advanced-table]').forEach((button) => {
31
- button.dispatchEvent(new Event('click'));
32
- });
33
- };
34
- </script>
26
+ <% end %>
@@ -1,4 +1,4 @@
1
- import React, { useState, useRef, useEffect } from "react";
1
+ import React, { useState, useRef, useEffect, forwardRef, useImperativeHandle } from "react";
2
2
  import classnames from "classnames";
3
3
  import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from "../utilities/props";
4
4
  import { globalProps } from "../utilities/globalProps";
@@ -38,7 +38,14 @@ type DropdownProps = {
38
38
  triggerRef?: any;
39
39
  };
40
40
 
41
- const Dropdown = (props: DropdownProps) => {
41
+ interface DropdownComponent
42
+ extends React.ForwardRefExoticComponent<DropdownProps & React.RefAttributes<unknown>> {
43
+ Option: typeof DropdownOption;
44
+ Trigger: typeof DropdownTrigger;
45
+ Container: typeof DropdownContainer;
46
+ }
47
+
48
+ const Dropdown = forwardRef((props: DropdownProps, ref: any) => {
42
49
  const {
43
50
  aria = {},
44
51
  autocomplete = false,
@@ -125,7 +132,7 @@ const Dropdown = (props: DropdownProps) => {
125
132
  const filteredOptions = optionsWithBlankSelection?.filter((option: GenericObject) => {
126
133
  const label = typeof option.label === 'string' ? option.label.toLowerCase() : option.label;
127
134
  return String(label).toLowerCase().includes(filterItem.toLowerCase());
128
- });
135
+ });
129
136
 
130
137
  // For keyboard accessibility: Set focus within dropdown to selected item if it exists
131
138
  useEffect(() => {
@@ -175,6 +182,14 @@ const Dropdown = (props: DropdownProps) => {
175
182
  dark
176
183
  });
177
184
 
185
+ useImperativeHandle(ref, () => ({
186
+ clearSelected: () => {
187
+ setSelected({});
188
+ setFilterItem("");
189
+ setIsDropDownClosed(true);
190
+ onSelect && onSelect(null);
191
+ },
192
+ }));
178
193
 
179
194
  return (
180
195
  <div {...ariaProps}
@@ -258,8 +273,9 @@ const Dropdown = (props: DropdownProps) => {
258
273
  </DropdownContext.Provider>
259
274
  </div>
260
275
  )
261
- };
276
+ }) as DropdownComponent
262
277
 
278
+ Dropdown.displayName = "Dropdown";
263
279
  Dropdown.Option = DropdownOption;
264
280
  Dropdown.Trigger = DropdownTrigger;
265
281
  Dropdown.Container = DropdownContainer;
@@ -0,0 +1,45 @@
1
+ import React, { useRef } from 'react'
2
+ import { Button, Dropdown } from 'playbook-ui'
3
+
4
+ const options = [
5
+ {
6
+ label: "United States",
7
+ value: "United States",
8
+ },
9
+ {
10
+ label: "Canada",
11
+ value: "Canada",
12
+ },
13
+ {
14
+ label: "Pakistan",
15
+ value: "Pakistan",
16
+ }
17
+ ]
18
+
19
+ const DropdownClearSelection = (props) => {
20
+ const dropdownRef = useRef(null)
21
+
22
+ const handleReset = () => {
23
+ if (dropdownRef.current) {
24
+ dropdownRef.current.clearSelected()
25
+ }
26
+ }
27
+
28
+ return (
29
+ <>
30
+ <Dropdown
31
+ defaultValue={options[2]}
32
+ options={options}
33
+ ref={dropdownRef}
34
+ {...props}
35
+ />
36
+ <Button
37
+ marginTop="md"
38
+ onClick={handleReset}
39
+ text="Reset"
40
+ />
41
+ </>
42
+ )
43
+ }
44
+
45
+ export default DropdownClearSelection
@@ -22,6 +22,7 @@ examples:
22
22
  - dropdown_error: Dropdown with Error
23
23
  - dropdown_default_value: Default Value
24
24
  - dropdown_blank_selection: Blank Selection
25
+ - dropdown_clear_selection: Clear Selection
25
26
  # - dropdown_with_autocomplete: Autocomplete
26
27
  # - dropdown_with_autocomplete_and_custom_display: Autocomplete with Custom Display
27
28
  # - dropdown_with_external_control: useDropdown Hook
@@ -12,3 +12,4 @@ export { default as DropdownSubcomponentStructure } from './_dropdown_subcompone
12
12
  export { default as DropdownError } from './_dropdown_error.jsx'
13
13
  export { default as DropdownDefaultValue } from './_dropdown_default_value.jsx'
14
14
  export { default as DropdownBlankSelection } from './_dropdown_blank_selection.jsx'
15
+ export { default as DropdownClearSelection } from './_dropdown_clear_selection.jsx'
@@ -47,9 +47,13 @@ const FormPill = (props: FormPillProps): React.ReactElement => {
47
47
 
48
48
  const iconClass = icon ? "_icon" : ""
49
49
  const closeIconSize = size === "small" ? "xs" : "sm"
50
+
51
+ const filteredProps: FormPillProps = {...props}
52
+ delete filteredProps.truncate
53
+
50
54
  const css = classnames(
51
55
  `pb_form_pill_kit_${color}${iconClass}`,
52
- globalProps(props),
56
+ globalProps(filteredProps),
53
57
  className,
54
58
  size === 'small' ? 'small' : null,
55
59
  textTransform,
@@ -77,6 +81,7 @@ const FormPill = (props: FormPillProps): React.ReactElement => {
77
81
  className="pb_form_pill_text"
78
82
  size={4}
79
83
  text={name}
84
+ truncate={props.truncate}
80
85
  />
81
86
  </>
82
87
  )}
@@ -92,6 +97,7 @@ const FormPill = (props: FormPillProps): React.ReactElement => {
92
97
  className="pb_form_pill_text"
93
98
  size={4}
94
99
  text={name}
100
+ truncate={props.truncate}
95
101
  />
96
102
  <Icon
97
103
  className="pb_form_pill_icon"
@@ -111,6 +117,7 @@ const FormPill = (props: FormPillProps): React.ReactElement => {
111
117
  className="pb_form_pill_tag"
112
118
  size={4}
113
119
  text={text}
120
+ truncate={props.truncate}
114
121
  />
115
122
  </>
116
123
  )}
@@ -119,6 +126,7 @@ const FormPill = (props: FormPillProps): React.ReactElement => {
119
126
  className="pb_form_pill_tag"
120
127
  size={4}
121
128
  text={text}
129
+ truncate={props.truncate}
122
130
  />
123
131
  )}
124
132
  <div
@@ -0,0 +1,19 @@
1
+ <%
2
+ names = [
3
+ { label: 'Alexander Nathaniel Montgomery', value: 'Alexander Nathaniel Montgomery' },
4
+ { label: 'Isabella Anastasia Wellington', value: 'Isabella Anastasia Wellington' },
5
+ { label: 'Christopher Maximilian Harrington', value: 'Christopher Maximilian Harrington' },
6
+ { label: 'Elizabeth Seraphina Kensington', value: 'Elizabeth Seraphina Kensington' },
7
+ { label: 'Theodore Jonathan Abernathy', value: 'Theodore Jonathan Abernathy' },
8
+ ]
9
+ %>
10
+
11
+ <%= pb_rails("typeahead", props: {
12
+ html_options: { style: { maxWidth: "240px" }},
13
+ id: "typeahead-form-pill",
14
+ is_multi: true,
15
+ options: names,
16
+ label: "Names",
17
+ pills: true,
18
+ truncate: 1,
19
+ }) %>
@@ -0,0 +1,27 @@
1
+ import React from 'react'
2
+ import Typeahead from '../../pb_typeahead/_typeahead'
3
+
4
+ const names = [
5
+ { label: 'Alexander Nathaniel Montgomery', value: 'Alexander Nathaniel Montgomery' },
6
+ { label: 'Isabella Anastasia Wellington', value: 'Isabella Anastasia Wellington' },
7
+ { label: 'Christopher Maximilian Harrington', value: 'Christopher Maximilian Harrington' },
8
+ { label: 'Elizabeth Seraphina Kensington', value: 'Elizabeth Seraphina Kensington' },
9
+ { label: 'Theodore Jonathan Abernathy', value: 'Theodore Jonathan Abernathy' },
10
+ ]
11
+
12
+ const FormPillTruncatedText = (props) => {
13
+ return (
14
+ <>
15
+ <Typeahead
16
+ htmlOptions={{ style: { maxWidth: "240px" }}}
17
+ isMulti
18
+ label="Names"
19
+ options={names}
20
+ truncate={1}
21
+ {...props}
22
+ />
23
+ </>
24
+ )
25
+ }
26
+
27
+ export default FormPillTruncatedText
@@ -0,0 +1 @@
1
+ For pills with longer text, the `truncate` global prop can be used to truncate the label within each Form Pill. See [here](https://playbook.powerapp.cloud/visual_guidelines/truncate) for more information on the truncate global prop.
@@ -3,6 +3,7 @@ examples:
3
3
  rails:
4
4
  - form_pill_user: Form Pill User
5
5
  - form_pill_size: Form Pill Size
6
+ - form_pill_truncated_text: Truncated Text
6
7
  - form_pill_tag: Form Pill Tag
7
8
  - form_pill_example: Example
8
9
  - form_pill_icon: Form Pill Icon
@@ -11,6 +12,7 @@ examples:
11
12
  react:
12
13
  - form_pill_user: Form Pill User
13
14
  - form_pill_size: Form Pill Size
15
+ - form_pill_truncated_text: Truncated Text
14
16
  - form_pill_tag: Form Pill Tag
15
17
  - form_pill_example: Example
16
18
  - form_pill_icon: Form Pill Icon
@@ -4,3 +4,4 @@ export { default as FormPillTag } from './_form_pill_tag.jsx'
4
4
  export { default as FormPillExample } from './_form_pill_example.jsx'
5
5
  export { default as FormPillIcon } from './_form_pill_icon.jsx'
6
6
  export { default as FormPillColors } from './_form_pill_colors.jsx'
7
+ export { default as FormPillTruncatedText } from './_form_pill_truncated_text.jsx'
@@ -367,7 +367,7 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
367
367
  // Rendering formattedData to UI based on typeahead
368
368
  const renderNestedOptions = (items: { [key: string]: string; }[] | any ) => {
369
369
  const hasOptionsChild = React.Children.toArray(props.children).some(
370
- child => child.type === MultiLevelSelect.Options
370
+ (child: any) => child.type === MultiLevelSelect.Options
371
371
  );
372
372
 
373
373
  if (hasOptionsChild) {
@@ -378,7 +378,7 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
378
378
  return null;
379
379
  });
380
380
  } else {
381
- // If nochildren, use the default rendering
381
+ // If no children, use the default rendering
382
382
  return (
383
383
  <MultiLevelSelectOptions items={items} />
384
384
  );
@@ -90,13 +90,11 @@ const MultiLevelSelectWithChildren = (props) => {
90
90
  >
91
91
  <MultiLevelSelect.Options>
92
92
  {(item) => (
93
- <div>
94
- <Badge
95
- marginLeft="sm"
96
- text={item.status}
97
- variant={item.status === "active" ? "success" : "warning"}
98
- />
99
- </div>
93
+ <Badge
94
+ marginLeft="sm"
95
+ text={item.status}
96
+ variant={item.status === "active" ? "success" : "warning"}
97
+ />
100
98
  )}
101
99
  </MultiLevelSelect.Options>
102
100
  </MultiLevelSelect>
@@ -0,0 +1 @@
1
+ The MultiLevelSelect also provides a subcomponent structure which can be used to render children to the right of the Checkboxes and their labels. As seen in the code snippet below, these children have access to the current item being iterated over which can be used for conditional rendering.
@@ -91,13 +91,11 @@ const MultiLevelSelectWithChildrenWithRadios = (props) => {
91
91
  >
92
92
  <MultiLevelSelect.Options>
93
93
  {(item) => (
94
- <div>
95
- <Badge
96
- marginLeft="sm"
97
- text={item.status}
98
- variant={item.status === "active" ? "success" : "warning"}
99
- />
100
- </div>
94
+ <Badge
95
+ marginLeft="sm"
96
+ text={item.status}
97
+ variant={item.status === "active" ? "success" : "warning"}
98
+ />
101
99
  )}
102
100
  </MultiLevelSelect.Options>
103
101
  </MultiLevelSelect>
@@ -0,0 +1 @@
1
+ The MultiLevelSelect subcomponent structure is also available in the 'Single Select' variant. In this variant, the children will be rendered to the right of the Radios and their labels.
@@ -136,12 +136,15 @@ const Typeahead = ({
136
136
  }
137
137
  }
138
138
 
139
+ const filteredProps: TypeaheadProps = {...props}
140
+ delete filteredProps.truncate
141
+
139
142
  const dataProps = buildDataProps(data)
140
143
  const htmlProps = buildHtmlProps(htmlOptions)
141
144
  const classes = classnames(
142
145
  'pb_typeahead_kit react-select',
143
146
  `mb_${marginBottom}`,
144
- globalProps(props),
147
+ globalProps(filteredProps),
145
148
  className
146
149
  )
147
150
 
@@ -16,7 +16,7 @@ type Props = {
16
16
  const MultiValue = (props: Props) => {
17
17
  const { removeProps } = props
18
18
  const { imageUrl, label } = props.data
19
- const { dark, multiKit, pillColor } = props.selectProps
19
+ const { dark, multiKit, pillColor, truncate } = props.selectProps
20
20
 
21
21
  const formPillProps = {
22
22
  marginRight: 'xs',
@@ -51,6 +51,7 @@ const MultiValue = (props: Props) => {
51
51
  name={label}
52
52
  size={multiKit === 'smallPill' ? 'small' : ''}
53
53
  text=''
54
+ truncate={truncate}
54
55
  {...props}
55
56
  />
56
57
  }
@@ -64,6 +65,7 @@ const MultiValue = (props: Props) => {
64
65
  name=''
65
66
  size={multiKit === 'smallPill' ? 'small' : ''}
66
67
  text={label}
68
+ truncate={truncate}
67
69
  {...props}
68
70
  />
69
71
  }
@@ -65,10 +65,10 @@ module Playbook
65
65
  def typeahead_react_options
66
66
  base_options = {
67
67
  className: classname,
68
- pillColor: pill_color,
69
68
  dark: dark,
70
69
  defaultValue: default_options,
71
70
  error: error,
71
+ htmlOptions: html_options,
72
72
  id: id,
73
73
  inline: inline,
74
74
  isMulti: is_multi,
@@ -77,8 +77,10 @@ module Playbook
77
77
  multiKit: multi_kit,
78
78
  name: name,
79
79
  options: options,
80
+ pillColor: pill_color,
80
81
  placeholder: placeholder,
81
82
  plusIcon: plus_icon,
83
+ truncate: truncate,
82
84
  }
83
85
 
84
86
  base_options[:getOptionLabel] = get_option_label if get_option_label.present?