playbook_ui 15.6.0.pre.alpha.play2552collapsibleiconrefactor12903 → 15.6.0.pre.alpha.play261013050
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 +4 -4
- data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +3 -2
- data/app/pb_kits/playbook/pb_advanced_table/Components/TableHeaderCell.tsx +4 -0
- data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +95 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_background_colors_rails.html.erb +43 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_background_colors_rails.md +1 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_background_control_rails.html.erb +11 -5
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_background_control_rails.md +7 -1
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background.jsx +54 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background.md +9 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background_multi.jsx +80 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background_multi.md +3 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +4 -1
- data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +3 -1
- data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +2 -2
- data/app/pb_kits/playbook/pb_advanced_table/table_header.rb +57 -0
- data/app/pb_kits/playbook/pb_card/docs/_card_header.md +1 -1
- data/app/pb_kits/playbook/pb_card/docs/_card_highlight.md +1 -1
- data/app/pb_kits/playbook/pb_date_picker/date_picker.test.js +24 -0
- data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +181 -3
- data/app/pb_kits/playbook/pb_distribution_bar/docs/_distribution_bar_custom_colors.md +1 -1
- data/app/pb_kits/playbook/pb_draggable/context/index.tsx +316 -15
- data/app/pb_kits/playbook/pb_draggable/context/types.ts +1 -1
- data/app/pb_kits/playbook/pb_filter/Filter/FilterBackground.tsx +3 -3
- data/app/pb_kits/playbook/pb_radio/docs/_radio_error.md +1 -1
- data/app/pb_kits/playbook/pb_select/_select.tsx +8 -3
- data/app/pb_kits/playbook/pb_select/docs/_select_error.md +1 -1
- data/app/pb_kits/playbook/pb_select/docs/_select_input_options.html.erb +16 -0
- data/app/pb_kits/playbook/pb_select/docs/_select_input_options.jsx +30 -0
- data/app/pb_kits/playbook/pb_select/docs/_select_input_options.md +1 -0
- data/app/pb_kits/playbook/pb_select/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_select/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_select/select.html.erb +2 -2
- data/app/pb_kits/playbook/pb_select/select.rb +3 -1
- data/app/pb_kits/playbook/pb_select/select.test.js +23 -0
- data/app/pb_kits/playbook/pb_table/_table.tsx +187 -33
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant.jsx +134 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant.md +34 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_rails.html.erb +101 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_rails.md +33 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_with_pagination.jsx +180 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_with_pagination.md +3 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_with_pagination_rails.html.erb +122 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_with_pagination_rails.md +3 -0
- data/app/pb_kits/playbook/pb_table/docs/example.yml +4 -0
- data/app/pb_kits/playbook/pb_table/docs/index.js +2 -0
- data/app/pb_kits/playbook/pb_table/table.html.erb +68 -12
- data/app/pb_kits/playbook/pb_table/table.rb +22 -3
- data/app/pb_kits/playbook/pb_table/table.test.js +143 -0
- data/app/pb_kits/playbook/pb_text_input/docs/_text_input_error.md +1 -1
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_error.md +1 -1
- data/app/pb_kits/playbook/pb_timeline/_item.tsx +3 -0
- data/app/pb_kits/playbook/pb_timeline/docs/_timeline_show_current_year.html.erb +60 -0
- data/app/pb_kits/playbook/pb_timeline/docs/_timeline_show_current_year.jsx +118 -0
- data/app/pb_kits/playbook/pb_timeline/docs/_timeline_show_current_year.md +1 -0
- data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_date.md +1 -1
- data/app/pb_kits/playbook/pb_timeline/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_timeline/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_timeline/item.html.erb +1 -1
- data/app/pb_kits/playbook/pb_timeline/item.rb +2 -0
- data/app/pb_kits/playbook/pb_timeline/label.html.erb +2 -1
- data/app/pb_kits/playbook/pb_timeline/label.rb +2 -0
- data/app/pb_kits/playbook/pb_timeline/subcomponents/Label.tsx +3 -0
- data/app/pb_kits/playbook/pb_timeline/timeline.test.js +51 -0
- data/app/pb_kits/playbook/utilities/deprecated.ts +73 -0
- data/dist/chunks/_typeahead-CHwm9MTE.js +6 -0
- data/dist/chunks/{lib-BxonsVJa.js → lib-Cugvy62C.js} +3 -3
- data/dist/chunks/vendor.js +3 -3
- data/dist/menu.yml +1 -1
- data/dist/playbook-rails-react-bindings.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/lib/playbook/forms/builder/collection_select_field.rb +9 -1
- data/lib/playbook/forms/builder/select_field.rb +9 -1
- data/lib/playbook/forms/builder/time_zone_select_field.rb +9 -1
- data/lib/playbook/pb_kit_helper.rb +35 -0
- data/lib/playbook/version.rb +1 -1
- metadata +25 -4
- data/dist/chunks/_typeahead-DHGyRtix.js +0 -6
|
@@ -29,6 +29,7 @@ type SelectProps = {
|
|
|
29
29
|
id?: string,
|
|
30
30
|
includeBlank?: string,
|
|
31
31
|
inline?: boolean,
|
|
32
|
+
inputOptions?: {[key: string]: string | number | boolean | (() => void)},
|
|
32
33
|
label?: string,
|
|
33
34
|
margin: string,
|
|
34
35
|
marginBottom: string,
|
|
@@ -63,6 +64,7 @@ const Select = ({
|
|
|
63
64
|
label,
|
|
64
65
|
htmlOptions = {},
|
|
65
66
|
inline = false,
|
|
67
|
+
inputOptions = {},
|
|
66
68
|
multiple = false,
|
|
67
69
|
name,
|
|
68
70
|
onChange = () => undefined,
|
|
@@ -94,14 +96,17 @@ const Select = ({
|
|
|
94
96
|
const angleDown = getAllIcons()["angleDown"].icon as unknown as { [key: string]: SVGElement }
|
|
95
97
|
|
|
96
98
|
const selectWrapperClass = classnames(buildCss('pb_select_kit_wrapper'), { error }, className)
|
|
99
|
+
const selectId = (inputOptions?.id as string) || name
|
|
100
|
+
|
|
97
101
|
const selectBody =(() =>{
|
|
98
102
|
if (children) return children
|
|
99
103
|
return (
|
|
100
104
|
<select
|
|
101
105
|
{...htmlOptions}
|
|
102
106
|
{...domSafeProps(props)}
|
|
107
|
+
{...inputOptions}
|
|
103
108
|
disabled={disabled}
|
|
104
|
-
id={
|
|
109
|
+
id={selectId}
|
|
105
110
|
multiple={multiple}
|
|
106
111
|
name={name}
|
|
107
112
|
onChange={onChange}
|
|
@@ -125,7 +130,7 @@ const Select = ({
|
|
|
125
130
|
{label &&
|
|
126
131
|
<label
|
|
127
132
|
className="pb_select_kit_label"
|
|
128
|
-
htmlFor={
|
|
133
|
+
htmlFor={selectId}
|
|
129
134
|
>
|
|
130
135
|
<Caption
|
|
131
136
|
dark={props.dark}
|
|
@@ -135,7 +140,7 @@ const Select = ({
|
|
|
135
140
|
}
|
|
136
141
|
<label
|
|
137
142
|
className={selectWrapperClass}
|
|
138
|
-
htmlFor={
|
|
143
|
+
htmlFor={selectId}
|
|
139
144
|
>
|
|
140
145
|
{selectBody}
|
|
141
146
|
{ multiple !== true ?
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Select w/ Error shows that
|
|
1
|
+
Select w/ Error shows that an option must be selected or is invalid (i.e. when used in a form it signals a user to fix an error).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<%= pb_rails("select", props: {
|
|
2
|
+
label: "Favorite Food",
|
|
3
|
+
name: "favorite_food",
|
|
4
|
+
options: [
|
|
5
|
+
{ value: "pizza", value_text: "Pizza" },
|
|
6
|
+
{ value: "tacos", value_text: "Tacos" },
|
|
7
|
+
{ value: "sushi", value_text: "Sushi" }
|
|
8
|
+
],
|
|
9
|
+
input_options: {
|
|
10
|
+
'aria-label': "Select your favorite food",
|
|
11
|
+
class: "custom-select-class",
|
|
12
|
+
data: { controller: "search", action: "change->search#filter" },
|
|
13
|
+
id: "favorite-food-select"
|
|
14
|
+
}
|
|
15
|
+
}) %>
|
|
16
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import Select from '../_select'
|
|
4
|
+
|
|
5
|
+
const SelectInputOptions = (props) => {
|
|
6
|
+
const options = [
|
|
7
|
+
{ value: 'pizza', text: 'Pizza' },
|
|
8
|
+
{ value: 'tacos', text: 'Tacos' },
|
|
9
|
+
{ value: 'sushi', text: 'Sushi' },
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<>
|
|
14
|
+
<Select
|
|
15
|
+
inputOptions={{
|
|
16
|
+
'aria-label': 'Select your favorite food',
|
|
17
|
+
className: 'custom-select-class',
|
|
18
|
+
id: 'favorite-food-select',
|
|
19
|
+
}}
|
|
20
|
+
label="Favorite Food"
|
|
21
|
+
name="favorite_food"
|
|
22
|
+
options={options}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
</>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default SelectInputOptions
|
|
30
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Use the `input_options` / `inputOptions` prop to pass additional attributes directly to the underlying `<select>` element instead of the outer wrapper. This is useful for applying data attributes, custom IDs, or other HTML attributes that need to be on the select element itself.
|
|
@@ -15,6 +15,7 @@ examples:
|
|
|
15
15
|
- select_inline_compact: Select Inline Compact
|
|
16
16
|
- select_attributes: Select W/ Attributes
|
|
17
17
|
- select_multiple: Select Multiple
|
|
18
|
+
- select_input_options: Input Options
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
|
|
@@ -33,6 +34,7 @@ examples:
|
|
|
33
34
|
- select_inline_compact: Select Inline Compact
|
|
34
35
|
- select_multiple: Select Multiple
|
|
35
36
|
- select_react_hook: React Hook
|
|
37
|
+
- select_input_options: Input Options
|
|
36
38
|
|
|
37
39
|
swift:
|
|
38
40
|
- select_default_swift: Default
|
|
@@ -12,3 +12,4 @@ export { default as SelectInlineCompact } from './_select_inline_compact.jsx'
|
|
|
12
12
|
export { default as SelectMultiple } from './_select_multiple.jsx'
|
|
13
13
|
export { default as SelectReactHook } from './_select_react_hook.jsx'
|
|
14
14
|
export { default as SelectCustomSelectSubheaders } from './_select_custom_select_subheaders.jsx'
|
|
15
|
+
export { default as SelectInputOptions } from './_select_input_options.jsx'
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
id: nil,
|
|
3
3
|
class: object.classnames ) do %>
|
|
4
4
|
<% if object.label %>
|
|
5
|
-
<label class="pb_select_kit_label" for="<%= object.name %>">
|
|
5
|
+
<label class="pb_select_kit_label" for="<%= object.input_options[:id] || object.name %>">
|
|
6
6
|
<%= pb_rails("caption", props: { text: object.label, dark: object.dark }) %>
|
|
7
7
|
</label>
|
|
8
8
|
<% end %>
|
|
9
|
-
<label class="<%= object.select_wrapper_class %>" for="<%= object.name %>">
|
|
9
|
+
<label class="<%= object.select_wrapper_class %>" for="<%= object.input_options[:id] || object.name %>">
|
|
10
10
|
<% if content.present? %>
|
|
11
11
|
<%= content %>
|
|
12
12
|
<%= pb_rails("body", props: { status: "negative", text: object.error }) %>
|
|
@@ -14,6 +14,8 @@ module Playbook
|
|
|
14
14
|
prop :error
|
|
15
15
|
prop :include_blank
|
|
16
16
|
prop :inline, type: Playbook::Props::Boolean, default: false
|
|
17
|
+
prop :input_options, type: Playbook::Props::HashProp,
|
|
18
|
+
default: {}
|
|
17
19
|
prop :label
|
|
18
20
|
prop :multiple, type: Playbook::Props::Boolean, default: false
|
|
19
21
|
prop :name
|
|
@@ -38,7 +40,7 @@ module Playbook
|
|
|
38
40
|
multiple: multiple,
|
|
39
41
|
onchange: onchange,
|
|
40
42
|
include_blank: include_blank,
|
|
41
|
-
}.merge(attributes)
|
|
43
|
+
}.merge(attributes).merge(input_options)
|
|
42
44
|
end
|
|
43
45
|
|
|
44
46
|
def classname
|
|
@@ -65,4 +65,27 @@ test('returns multiple variant', () => {
|
|
|
65
65
|
const selectElement = kit.querySelector('select');
|
|
66
66
|
|
|
67
67
|
expect(selectElement).toHaveAttribute('multiple', '');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('inputOptions are passed to select element', () => {
|
|
71
|
+
render(
|
|
72
|
+
<Select
|
|
73
|
+
data={{ testid: testId }}
|
|
74
|
+
inputOptions={{
|
|
75
|
+
id: 'custom-select-id',
|
|
76
|
+
className: 'custom-select-class',
|
|
77
|
+
'aria-label': 'Custom aria label',
|
|
78
|
+
}}
|
|
79
|
+
label="Favorite Food"
|
|
80
|
+
name="food"
|
|
81
|
+
options={options}
|
|
82
|
+
/>
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
const kit = screen.getByTestId(testId)
|
|
86
|
+
const selectElement = kit.querySelector('select')
|
|
87
|
+
|
|
88
|
+
expect(selectElement).toHaveAttribute('id', 'custom-select-id')
|
|
89
|
+
expect(selectElement).toHaveClass('custom-select-class')
|
|
90
|
+
expect(selectElement).toHaveAttribute('aria-label', 'Custom aria label')
|
|
68
91
|
});
|
|
@@ -10,6 +10,11 @@ import {
|
|
|
10
10
|
TableCell,
|
|
11
11
|
} from "./subcomponents";
|
|
12
12
|
import { addDataTitle } from './utilities/addDataTitle'
|
|
13
|
+
import Card from '../pb_card/_card'
|
|
14
|
+
import Flex from '../pb_flex/_flex'
|
|
15
|
+
import Title from '../pb_title/_title'
|
|
16
|
+
import SectionSeparator from '../pb_section_separator/_section_separator'
|
|
17
|
+
import Filter from '../pb_filter/_filter'
|
|
13
18
|
|
|
14
19
|
type TableProps = {
|
|
15
20
|
aria?: { [key: string]: string },
|
|
@@ -21,10 +26,13 @@ type TableProps = {
|
|
|
21
26
|
data?: { [key: string]: string },
|
|
22
27
|
dataTable: boolean,
|
|
23
28
|
disableHover?: boolean,
|
|
29
|
+
filterProps?: { [key: string]: any },
|
|
30
|
+
filterContent?: any,
|
|
24
31
|
headerStyle?: "default" | "borderless" | "floating"
|
|
25
32
|
htmlOptions?: { [key: string]: string | number | boolean | (() => void) },
|
|
26
33
|
id?: string,
|
|
27
34
|
outerPadding?: "none" | "xxs" | "xs" | "sm" | "md" | "lg" | "xl",
|
|
35
|
+
pagination?: React.ReactElement,
|
|
28
36
|
responsive?: "collapse" | "scroll" | "none",
|
|
29
37
|
singleLine?: boolean,
|
|
30
38
|
size?: "sm" | "md" | "lg",
|
|
@@ -33,24 +41,32 @@ type TableProps = {
|
|
|
33
41
|
stickyRightColumn?: string[],
|
|
34
42
|
striped?: boolean,
|
|
35
43
|
tag?: "table" | "div",
|
|
44
|
+
title?: string,
|
|
45
|
+
variant?: "default" | "withFilter",
|
|
36
46
|
verticalBorder?: boolean,
|
|
37
47
|
} & GlobalProps
|
|
38
48
|
|
|
49
|
+
type AllSizes = "none" | "xxs" | "xs" | "sm" | "md" | "lg" | "xl" | "xxl" | "auto" | "initial" | "inherit"
|
|
50
|
+
|
|
39
51
|
const Table = (props: TableProps): React.ReactElement => {
|
|
40
52
|
const {
|
|
41
53
|
aria = {},
|
|
54
|
+
variant = 'default',
|
|
42
55
|
children,
|
|
43
56
|
className,
|
|
44
|
-
collapse = 'sm',
|
|
57
|
+
collapse = variant === 'withFilter' ? 'md' : 'sm',
|
|
45
58
|
container = true,
|
|
46
59
|
dark,
|
|
47
60
|
data = {},
|
|
48
61
|
dataTable = false,
|
|
49
62
|
disableHover = false,
|
|
63
|
+
filterProps = {},
|
|
64
|
+
filterContent,
|
|
50
65
|
headerStyle = "default",
|
|
51
66
|
htmlOptions = {},
|
|
52
67
|
id,
|
|
53
68
|
outerPadding = '',
|
|
69
|
+
pagination,
|
|
54
70
|
responsive = 'collapse',
|
|
55
71
|
singleLine = false,
|
|
56
72
|
size = 'sm',
|
|
@@ -59,6 +75,7 @@ const Table = (props: TableProps): React.ReactElement => {
|
|
|
59
75
|
stickyRightColumn= [],
|
|
60
76
|
striped = false,
|
|
61
77
|
tag = 'table',
|
|
78
|
+
title,
|
|
62
79
|
verticalBorder = false,
|
|
63
80
|
} = props
|
|
64
81
|
|
|
@@ -73,12 +90,15 @@ const Table = (props: TableProps): React.ReactElement => {
|
|
|
73
90
|
const dynamicInlineProps = globalInlineProps(props)
|
|
74
91
|
const stickyRightColumnReversed = stickyRightColumn.reverse()
|
|
75
92
|
|
|
93
|
+
const isFilterVariant = variant === 'withFilter'
|
|
94
|
+
const effectiveContainer = isFilterVariant ? false : container
|
|
95
|
+
|
|
76
96
|
const classNames = classnames(
|
|
77
97
|
'pb_table',
|
|
78
98
|
`table-${size}`,
|
|
79
99
|
`table-responsive-${responsive}`,
|
|
80
100
|
{
|
|
81
|
-
'table-card':
|
|
101
|
+
'table-card': effectiveContainer,
|
|
82
102
|
'table-dark': dark,
|
|
83
103
|
'data_table': dataTable,
|
|
84
104
|
'single-line': singleLine,
|
|
@@ -205,36 +225,70 @@ const Table = (props: TableProps): React.ReactElement => {
|
|
|
205
225
|
addDataTitle()
|
|
206
226
|
}, [])
|
|
207
227
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
228
|
+
// ------------ Default Table (non-filter variant rendering) ------------
|
|
229
|
+
const renderTable = () => {
|
|
230
|
+
const tableElement = responsive === 'scroll' ? (
|
|
231
|
+
<div className='table-responsive-scroll'>
|
|
232
|
+
{isTableTag ? (
|
|
233
|
+
<table
|
|
234
|
+
{...ariaProps}
|
|
235
|
+
{...dataProps}
|
|
236
|
+
{...htmlProps}
|
|
237
|
+
className={classNames}
|
|
238
|
+
id={id}
|
|
239
|
+
style={dynamicInlineProps}
|
|
240
|
+
>
|
|
241
|
+
{children}
|
|
242
|
+
</table>
|
|
243
|
+
) : (
|
|
244
|
+
<div
|
|
245
|
+
{...ariaProps}
|
|
246
|
+
{...dataProps}
|
|
247
|
+
{...htmlProps}
|
|
248
|
+
className={classNames}
|
|
249
|
+
id={id}
|
|
250
|
+
style={dynamicInlineProps}
|
|
251
|
+
>
|
|
252
|
+
{children}
|
|
253
|
+
</div>
|
|
254
|
+
)}
|
|
255
|
+
</div>
|
|
256
|
+
) : (
|
|
257
|
+
isTableTag ? (
|
|
258
|
+
<table
|
|
259
|
+
{...ariaProps}
|
|
260
|
+
{...dataProps}
|
|
261
|
+
{...htmlProps}
|
|
262
|
+
className={classNames}
|
|
263
|
+
id={id}
|
|
264
|
+
style={dynamicInlineProps}
|
|
265
|
+
>
|
|
266
|
+
{children}
|
|
267
|
+
</table>
|
|
236
268
|
) : (
|
|
237
|
-
|
|
269
|
+
<div
|
|
270
|
+
{...ariaProps}
|
|
271
|
+
{...dataProps}
|
|
272
|
+
{...htmlProps}
|
|
273
|
+
className={classNames}
|
|
274
|
+
id={id}
|
|
275
|
+
style={dynamicInlineProps}
|
|
276
|
+
>
|
|
277
|
+
{children}
|
|
278
|
+
</div>
|
|
279
|
+
)
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
return tableElement
|
|
283
|
+
}
|
|
284
|
+
// ------------ End Default Table (non-filter variant rendering) ------------
|
|
285
|
+
|
|
286
|
+
// ------------ variant = 'withFilter' rendering ------------
|
|
287
|
+
const renderCardVariant = () => {
|
|
288
|
+
// Render table element
|
|
289
|
+
const tableElement = responsive === 'scroll' ? (
|
|
290
|
+
<div className='table-responsive-scroll'>
|
|
291
|
+
{isTableTag ? (
|
|
238
292
|
<table
|
|
239
293
|
{...ariaProps}
|
|
240
294
|
{...dataProps}
|
|
@@ -256,8 +310,108 @@ const Table = (props: TableProps): React.ReactElement => {
|
|
|
256
310
|
>
|
|
257
311
|
{children}
|
|
258
312
|
</div>
|
|
259
|
-
)
|
|
260
|
-
|
|
313
|
+
)}
|
|
314
|
+
</div>
|
|
315
|
+
) : (
|
|
316
|
+
isTableTag ? (
|
|
317
|
+
<table
|
|
318
|
+
{...ariaProps}
|
|
319
|
+
{...dataProps}
|
|
320
|
+
{...htmlProps}
|
|
321
|
+
className={classNames}
|
|
322
|
+
id={id}
|
|
323
|
+
style={dynamicInlineProps}
|
|
324
|
+
>
|
|
325
|
+
{children}
|
|
326
|
+
</table>
|
|
327
|
+
) : (
|
|
328
|
+
<div
|
|
329
|
+
{...ariaProps}
|
|
330
|
+
{...dataProps}
|
|
331
|
+
{...htmlProps}
|
|
332
|
+
className={classNames}
|
|
333
|
+
id={id}
|
|
334
|
+
style={dynamicInlineProps}
|
|
335
|
+
>
|
|
336
|
+
{children}
|
|
337
|
+
</div>
|
|
338
|
+
)
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
// Default filter props that CAN be overridden (All props from Filter kit CAN be used, but these are the ones we set as defaults)
|
|
342
|
+
const defaultFilterProps = {
|
|
343
|
+
background: false,
|
|
344
|
+
maxHeight: "50vh",
|
|
345
|
+
minWidth: "xs",
|
|
346
|
+
popoverProps: { width: "350px" },
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Merge default props with user-provided props (user props override defaults)
|
|
350
|
+
const mergedFilterProps = { ...defaultFilterProps, ...filterProps }
|
|
351
|
+
|
|
352
|
+
return (
|
|
353
|
+
<>
|
|
354
|
+
{title && (
|
|
355
|
+
<Title
|
|
356
|
+
paddingLeft={{
|
|
357
|
+
xs: "sm",
|
|
358
|
+
sm: "sm",
|
|
359
|
+
md: "xl",
|
|
360
|
+
lg: "xl",
|
|
361
|
+
xl: "xl",
|
|
362
|
+
default: "xl",
|
|
363
|
+
} as any}
|
|
364
|
+
paddingY="md"
|
|
365
|
+
size={3}
|
|
366
|
+
text={title}
|
|
367
|
+
/>
|
|
368
|
+
)}
|
|
369
|
+
<Card
|
|
370
|
+
marginX={{
|
|
371
|
+
xs: "sm",
|
|
372
|
+
sm: "sm",
|
|
373
|
+
md: "xl",
|
|
374
|
+
lg: "xl",
|
|
375
|
+
xl: "xl",
|
|
376
|
+
default: "xl",
|
|
377
|
+
} as any}
|
|
378
|
+
padding="none"
|
|
379
|
+
>
|
|
380
|
+
<Flex
|
|
381
|
+
align="stretch"
|
|
382
|
+
flexDirection="column"
|
|
383
|
+
gap="none"
|
|
384
|
+
>
|
|
385
|
+
{filterContent && (
|
|
386
|
+
<Filter {...mergedFilterProps}>
|
|
387
|
+
{filterContent}
|
|
388
|
+
</Filter>
|
|
389
|
+
)}
|
|
390
|
+
{filterContent && <SectionSeparator />}
|
|
391
|
+
{pagination && (
|
|
392
|
+
<>
|
|
393
|
+
{pagination}
|
|
394
|
+
<SectionSeparator />
|
|
395
|
+
</>
|
|
396
|
+
)}
|
|
397
|
+
{tableElement}
|
|
398
|
+
{pagination && (
|
|
399
|
+
<>
|
|
400
|
+
{pagination}
|
|
401
|
+
</>
|
|
402
|
+
)}
|
|
403
|
+
</Flex>
|
|
404
|
+
</Card>
|
|
405
|
+
</>
|
|
406
|
+
)
|
|
407
|
+
}
|
|
408
|
+
// ------------ End variant = 'withFilter' rendering ------------
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
return (
|
|
413
|
+
<>
|
|
414
|
+
{isFilterVariant ? renderCardVariant() : renderTable()}
|
|
261
415
|
</>
|
|
262
416
|
)
|
|
263
417
|
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import React, { useState } from "react"
|
|
2
|
+
import { Button, Date as DateKit, DatePicker, Dropdown, Select, Table, TextInput, Typeahead, Flex } from "playbook-ui"
|
|
3
|
+
|
|
4
|
+
// Mock Data for Table
|
|
5
|
+
const users = [
|
|
6
|
+
{ id: 1, name: "Jennifer", title: "Associate Scrum Master", department: "Business Technology", branch: "Philadelphia", startDate: "2025-01-01" },
|
|
7
|
+
{ id: 2, name: "Nick", title: "UX Engineer II", department: "Business Technology", branch: "Philadelphia", startDate: "2025-01-02" },
|
|
8
|
+
{ id: 3, name: "Nida", title: "Senior UX Engineer", department: "Business Technology", branch: "Philadelphia", startDate: "2025-01-03" },
|
|
9
|
+
{ id: 4, name: "Justin", title: "Director of User Experience Engineering", department: "Business Technology", branch: "Philadelphia", startDate: "2025-01-04" },
|
|
10
|
+
{ id: 5, name: "Edward", title: "UX Designer II", department: "Business Technology", branch: "Philadelphia", startDate: "2025-01-05" },
|
|
11
|
+
{ id: 6, name: "Elisa", title: "UX Engineer", department: "Business Technology", branch: "Philadelphia", startDate: "2025-01-06" },
|
|
12
|
+
{ id: 7, name: "Gary", title: "UX Engineer", department: "Business Technology", branch: "Philadelphia", startDate: "2025-01-07" },
|
|
13
|
+
{ id: 8, name: "Barkley", title: "Nitro Quality Ninja", department: "Business Technology", branch: "Philadelphia", startDate: "2025-01-08" },
|
|
14
|
+
{ id: 9, name: "Aaron", title: "Associate Nitro Quality Ninja", department: "Business Technology", branch: "Philadelphia", startDate: "2025-01-09" },
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
const TableWithFilterVariant = () => {
|
|
18
|
+
const [territory, setTerritory] = useState("")
|
|
19
|
+
|
|
20
|
+
// --------Filter content example ------
|
|
21
|
+
const filterContent = ({ closePopover }) => (
|
|
22
|
+
<>
|
|
23
|
+
<TextInput
|
|
24
|
+
label="Territory ID"
|
|
25
|
+
onChange={event => setTerritory(event.target.value)}
|
|
26
|
+
value={territory}
|
|
27
|
+
/>
|
|
28
|
+
|
|
29
|
+
<Typeahead
|
|
30
|
+
label="Title"
|
|
31
|
+
options={[
|
|
32
|
+
{ key: "senior-ux-engineer", label: "Senior UX Engineer", value: "senior-ux-engineer" },
|
|
33
|
+
{ key: "ux-engineer", label: "UX Engineer", value: "ux-engineer" },
|
|
34
|
+
{ key: "ux-designer", label: "UX Designer", value: "ux-designer" }
|
|
35
|
+
]}
|
|
36
|
+
/>
|
|
37
|
+
|
|
38
|
+
<Select
|
|
39
|
+
blankSelection="All Departments"
|
|
40
|
+
label="Department"
|
|
41
|
+
options={[
|
|
42
|
+
{ value: "Business Technology", label: "Business Technology", key: "business-technology" },
|
|
43
|
+
{ value: "Customer Development", label: "Customer Development", key: "customer-development" },
|
|
44
|
+
{ value: "Talent Acquisition", label: "Talent Acquisition", key: "talent-acquisition" }
|
|
45
|
+
]}
|
|
46
|
+
/>
|
|
47
|
+
|
|
48
|
+
<Dropdown
|
|
49
|
+
label="Branch"
|
|
50
|
+
options={[
|
|
51
|
+
{ key: "Philadelphia", label: "Philadelphia", value: "philadelphia" },
|
|
52
|
+
{ key: "New York", label: "New York", value: "new-york" },
|
|
53
|
+
{ key: "Austin", label: "Austin", value: "austin" }
|
|
54
|
+
]}
|
|
55
|
+
/>
|
|
56
|
+
|
|
57
|
+
<DatePicker
|
|
58
|
+
label="Start Date"
|
|
59
|
+
paddingY="sm"
|
|
60
|
+
pickerId="startedOn"
|
|
61
|
+
/>
|
|
62
|
+
<Flex spacing="between">
|
|
63
|
+
<Button
|
|
64
|
+
onClick={() => {
|
|
65
|
+
alert("No filtering functionality - just a pattern demo!")
|
|
66
|
+
closePopover()
|
|
67
|
+
}}
|
|
68
|
+
text="Filter"
|
|
69
|
+
/>
|
|
70
|
+
<Button
|
|
71
|
+
text="Defaults"
|
|
72
|
+
variant="secondary"
|
|
73
|
+
/>
|
|
74
|
+
</Flex>
|
|
75
|
+
</>
|
|
76
|
+
)
|
|
77
|
+
// -------End Filter content example ------
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<Table
|
|
81
|
+
filterContent={filterContent}
|
|
82
|
+
filterProps={{
|
|
83
|
+
results: 50,
|
|
84
|
+
sortOptions: {
|
|
85
|
+
territory_id: "Territory ID",
|
|
86
|
+
first_name: "Name",
|
|
87
|
+
started_on: "Start Date",
|
|
88
|
+
department_name: "Department",
|
|
89
|
+
title_name: "Title",
|
|
90
|
+
branch_branch_name: "Branch",
|
|
91
|
+
},
|
|
92
|
+
sortValue: [{ name: 'started_on', dir: 'asc' }],
|
|
93
|
+
}}
|
|
94
|
+
title="Table Title Here"
|
|
95
|
+
variant="withFilter"
|
|
96
|
+
>
|
|
97
|
+
<Table.Head>
|
|
98
|
+
<Table.Row>
|
|
99
|
+
<Table.Header>{'Territory ID'}</Table.Header>
|
|
100
|
+
<Table.Header>{'Name'}</Table.Header>
|
|
101
|
+
<Table.Header>{'Title'}</Table.Header>
|
|
102
|
+
<Table.Header>{'Department'}</Table.Header>
|
|
103
|
+
<Table.Header>{'Branch'}</Table.Header>
|
|
104
|
+
<Table.Header textAlign="right">{'Start Date'}</Table.Header>
|
|
105
|
+
</Table.Row>
|
|
106
|
+
</Table.Head>
|
|
107
|
+
<Table.Body>
|
|
108
|
+
{users.map((user) => (
|
|
109
|
+
<Table.Row key={user.id}>
|
|
110
|
+
<Table.Cell
|
|
111
|
+
marginX={{ xs: "sm" }}
|
|
112
|
+
numberSpacing="tabular"
|
|
113
|
+
>
|
|
114
|
+
{user.id}
|
|
115
|
+
</Table.Cell>
|
|
116
|
+
<Table.Cell marginX={{ xs: "sm" }}>{user.name}</Table.Cell>
|
|
117
|
+
<Table.Cell marginX={{ xs: "sm" }}>{user.title}</Table.Cell>
|
|
118
|
+
<Table.Cell marginX={{ xs: "sm" }}>{user.department}</Table.Cell>
|
|
119
|
+
<Table.Cell marginX={{ xs: "sm" }}>{user.branch}</Table.Cell>
|
|
120
|
+
<Table.Cell marginX={{ xs: "sm" }}>
|
|
121
|
+
<DateKit
|
|
122
|
+
alignment="right"
|
|
123
|
+
showCurrentYear
|
|
124
|
+
value={user.startDate}
|
|
125
|
+
/>
|
|
126
|
+
</Table.Cell>
|
|
127
|
+
</Table.Row>
|
|
128
|
+
))}
|
|
129
|
+
</Table.Body>
|
|
130
|
+
</Table>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export default TableWithFilterVariant
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Set the `variant` prop to `withFilter` to render a Table with a filter. The variant automatically handles:
|
|
2
|
+
|
|
3
|
+
- Card wrapper with standard responsive margins
|
|
4
|
+
- Optional `title` prop to render title above the card
|
|
5
|
+
- Filter component rendering with Design defaults
|
|
6
|
+
- SectionSeparator between filter and table
|
|
7
|
+
- Flex layout for proper alignment
|
|
8
|
+
|
|
9
|
+
#### Required Props
|
|
10
|
+
|
|
11
|
+
- `variant="withFilter"`: Enables the filter variant
|
|
12
|
+
- `filterContent`: A function that receives `{ closePopover }` and returns the filter's body content (inputs, buttons, etc.). Use this to pass in all input kits, etc needed inside the Filter itself.
|
|
13
|
+
- `filterProps`: An object containing Filter-specific props like `results`, `sortOptions`, `sortValue`, etc.
|
|
14
|
+
|
|
15
|
+
#### Optional Props
|
|
16
|
+
|
|
17
|
+
- `title`: Displays a title above the card
|
|
18
|
+
- All standard Table props (`size`, `collapse`, etc.) can be used, but defaults are already set to match Design guidelines
|
|
19
|
+
- All standard Filter props can be used, but defaults are already set to match Design guidelines.
|
|
20
|
+
|
|
21
|
+
#### Default Filter Props
|
|
22
|
+
|
|
23
|
+
The Table kit automatically sets these Filter defaults (which you can override via `filterProps`):
|
|
24
|
+
|
|
25
|
+
- `background={false}`
|
|
26
|
+
- `maxHeight="50vh"`
|
|
27
|
+
- `minWidth="xs"`
|
|
28
|
+
- `popoverProps={{ width: "350px" }}`
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
**IMPORTANT NOTE**:
|
|
32
|
+
The purpose of this variant is to provide an easy way to set up a Table with a Filter with Design standards applied by default.
|
|
33
|
+
|
|
34
|
+
If you are looking for more customization than this embedded variant provides, you may be better served by using the individual kits as demonstrated in our Table Filter Card Building Block [here](https://playbook.powerapp.cloud/building_blocks/table_filter_card/react).
|