From 0bfb6c302ceeb634feb4b1e97d3ea54c88772fe7 Mon Sep 17 00:00:00 2001 From: Nida Ghuman Date: Wed, 8 Jan 2025 09:24:39 -0500 Subject: [PATCH] [PBNTR 783] AdvancedTable Rails: Subrow Headers (#4088) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Runway Story](https://runway.powerhrg.com/backlog_items/PBNTR-783) This PR tackles the following: ✅ Subrow headers to show up with subrow_headers prop ✅ Subrow headers to toggle with parent row ✅ 'toggle all' button on subrow headers to toggle open first level children under the relevant header ![Screenshot 2025-01-07 at 2 40 36 PM](https://github.com/user-attachments/assets/b9c70fd3-d79c-4eed-9ee0-1ec6e2ab0932) --------- Co-authored-by: Jasper Furniss --- ...dvanced_table_beta_subrow_headers.html.erb | 2 +- .../_advanced_table_beta_subrow_headers.md | 2 +- .../pb_advanced_table/docs/example.yml | 2 +- .../playbook/pb_advanced_table/index.js | 61 ++++++++++++++++--- .../playbook/pb_advanced_table/table_body.rb | 41 +++++++------ .../playbook/pb_advanced_table/table_row.rb | 6 ++ .../table_subrow_header.html.erb | 4 +- .../pb_advanced_table/table_subrow_header.rb | 10 +-- 8 files changed, 93 insertions(+), 35 deletions(-) diff --git a/playbook/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta_subrow_headers.html.erb b/playbook/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta_subrow_headers.html.erb index 7faa232493..87efbb3a25 100644 --- a/playbook/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta_subrow_headers.html.erb +++ b/playbook/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta_subrow_headers.html.erb @@ -36,5 +36,5 @@ <%= pb_rails("advanced_table", props: { table_data: @table_data, column_definitions: column_definitions }) do %> <%= pb_rails("advanced_table/table_header", props: { column_definitions: column_definitions }) %> - <%= pb_rails("advanced_table/table_body", props: { id: "subrow_headers", table_data: @table_data, column_definitions: column_definitions, subrow_headers: subrow_headers, enable_toggle_expansion: "all" }) %> + <%= pb_rails("advanced_table/table_body", props: { id: "test_table", table_data: @table_data, column_definitions: column_definitions, subrow_headers: subrow_headers, enable_toggle_expansion: "all" }) %> <% end %> diff --git a/playbook/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta_subrow_headers.md b/playbook/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta_subrow_headers.md index 4aa2172cc1..cb5109395a 100644 --- a/playbook/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta_subrow_headers.md +++ b/playbook/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta_subrow_headers.md @@ -1,3 +1,3 @@ `subrow_headers` is an optional prop that if present will add header rows at each level of the nested data. The prop takes an array of strings, each string being the text for each header row. The array of strings must be in the order in which they need to be rendered in the UI according to depth. -`enable_toggle_expansion` is an additional optional prop that can be used in conjunction with the subRowHeaders prop. `enable_toggle_expansion` is a string that can be "all", "header" or "none". If set to "all", the toggle exapansion button will appear in the table header as well as in the subRow headers. If set to "header" button will only appear in header and NOT in subRow headers. This is set to "header" by default. \ No newline at end of file +`enable_toggle_expansion` is an additional optional prop that can be used in conjunction with the subRowHeaders prop. `enable_toggle_expansion` is a string that can be "all", "header" or "none". If set to "all", the toggle expansion button will appear in the table header as well as in the subRow headers. If set to "header", the button will only appear in header and NOT in subRow headers. This prop is set to "header" by default. \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_advanced_table/docs/example.yml b/playbook/app/pb_kits/playbook/pb_advanced_table/docs/example.yml index 2a4c76b696..db4a19d402 100644 --- a/playbook/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +++ b/playbook/app/pb_kits/playbook/pb_advanced_table/docs/example.yml @@ -1,7 +1,7 @@ examples: rails: - advanced_table_beta: Default (Required Props) - # - advanced_table_beta_subrow_headers: SubRow Headers + - advanced_table_beta_subrow_headers: SubRow Headers - advanced_table_collapsible_trail_rails: Collapsible Trail - advanced_table_beta_sort: Enable Sorting - advanced_table_custom_cell_rails: Custom Components for Cells diff --git a/playbook/app/pb_kits/playbook/pb_advanced_table/index.js b/playbook/app/pb_kits/playbook/pb_advanced_table/index.js index bfc2b1127b..e3c3e0f346 100644 --- a/playbook/app/pb_kits/playbook/pb_advanced_table/index.js +++ b/playbook/app/pb_kits/playbook/pb_advanced_table/index.js @@ -65,8 +65,7 @@ export default class PbAdvancedTable extends PbEnhancedElement { if (!dataContent) { return; } - - // Split the dataContent to get all ancestor IDs, check against simpleExpandedRows + // Split the dataContent to get all ancestor IDs, check against ExpandedRows const ancestorIds = dataContent.split("-").slice(0, -1); const prefixedAncestorIds = ancestorIds.map( @@ -76,7 +75,22 @@ export default class PbAdvancedTable extends PbEnhancedElement { PbAdvancedTable.expandedRows.has(id) ); - if (allAncestorsExpanded) { + const checkIfParentIsExpanded = () => { + if (dataContent.endsWith("sr")) { + const parentRowId = childRow.dataset.rowParent; + const isParentVisible = + childRow.previousElementSibling.classList.contains("is-visible"); + if (parentRowId) { + const isInSet = PbAdvancedTable.expandedRows.has(parentRowId); + if (isInSet && isParentVisible) { + return true; + } + } + } + return false; + }; + + if (allAncestorsExpanded || checkIfParentIsExpanded()) { childRow.style.display = "table-row"; childRow.classList.add("is-visible"); } else { @@ -143,7 +157,7 @@ export default class PbAdvancedTable extends PbEnhancedElement { static handleToggleAllHeaders(element) { const table = element.closest(".pb_table"); const firstLevelButtons = table.querySelectorAll( - ".pb_advanced_table_body > .pb_table_tr [data-advanced-table]" + ".pb_advanced_table_body > .pb_table_tr[data-row-depth='0'] [data-advanced-table]" ); const allExpanded = Array.from(firstLevelButtons).every( @@ -175,12 +189,43 @@ export default class PbAdvancedTable extends PbEnhancedElement { } } - // static handleToggleAllSubRows(element, rowDepth) {} + static handleToggleAllSubRows(element, rowDepth) { + const table = element.closest(".pb_table"); + const parentRow = element.closest("tr"); + if (!parentRow) { + return; + } + const rowParentId = parentRow.dataset.rowParent; + // Select all buttons that for subrows at that depth and with same rowParent + const subRowButtons = table.querySelectorAll( + `.pb_advanced_table_body > .pb_table_tr[data-row-depth='${rowDepth}'].pb_table_tr[data-row-parent='${rowParentId}'] [data-advanced-table]` + ); + + const allExpanded = Array.from(subRowButtons).every( + (button) => + button.querySelector(UP_ARROW_SELECTOR).style.display === "inline-block" + ); + + if (allExpanded) { + subRowButtons.forEach((button) => { + button.click(); + PbAdvancedTable.expandedRows.delete(button.id); + }); + } else { + subRowButtons.forEach((button) => { + if (!PbAdvancedTable.expandedRows.has(button.id)) { + button.click(); + PbAdvancedTable.expandedRows.add(button.id); + } + }); + } + } } window.expandAllRows = (element) => { PbAdvancedTable.handleToggleAllHeaders(element); }; -// window.expandAllSubRows = (element, rowDepth) => { -// PbAdvancedTable.handleToggleAllSubRows(element, rowDepth); -// }; + +window.expandAllSubRows = (element, rowDepth) => { + PbAdvancedTable.handleToggleAllSubRows(element, rowDepth); +}; diff --git a/playbook/app/pb_kits/playbook/pb_advanced_table/table_body.rb b/playbook/app/pb_kits/playbook/pb_advanced_table/table_body.rb index 509d5e3348..52b0fd8bdf 100644 --- a/playbook/app/pb_kits/playbook/pb_advanced_table/table_body.rb +++ b/playbook/app/pb_kits/playbook/pb_advanced_table/table_body.rb @@ -31,7 +31,7 @@ def flatten_columns(columns) end.compact end - def render_row_and_children(row, column_definitions, current_depth, first_parent_child, ancestor_ids = [], top_parent_id = nil) + def render_row_and_children(row, column_definitions, current_depth, first_parent_child, ancestor_ids = [], top_parent_id = nil, additional_classes: "", table_data_attributes: {}) top_parent_id ||= row.object_id new_ancestor_ids = ancestor_ids + [row.object_id] leaf_columns = flatten_columns(column_definitions) @@ -39,30 +39,35 @@ def render_row_and_children(row, column_definitions, current_depth, first_parent output = ActiveSupport::SafeBuffer.new is_first_child_of_subrow = current_depth.positive? && first_parent_child && subrow_headers[current_depth - 1].present? - output << pb_rails("advanced_table/table_subrow_header", props: { row: row, column_definitions: column_definitions, depth: current_depth, subrow_header: subrow_headers[current_depth - 1], collapsible_trail: collapsible_trail }) if is_first_child_of_subrow && enable_toggle_expansion == "all" + subrow_ancestor_ids = ancestor_ids + ["#{row.object_id}sr"] + subrow_data_attributes = { + advanced_table_content: subrow_ancestor_ids.join("-"), + row_depth: current_depth, + row_parent: "#{id}_#{ancestor_ids.last}", + } + # Subrow header if applicable + output << pb_rails("advanced_table/table_subrow_header", props: { row: row, column_definitions: leaf_columns, depth: current_depth, subrow_header: subrow_headers[current_depth - 1], collapsible_trail: collapsible_trail, classname: "toggle-content", subrow_data_attributes: subrow_data_attributes }) if is_first_child_of_subrow && enable_toggle_expansion == "all" - # Pass only leaf_columns to table_row to account for multiple nested columns - output << pb_rails("advanced_table/table_row", props: { - id: id, - row: row, - column_definitions: leaf_columns, - depth: current_depth, - collapsible_trail: collapsible_trail, - }) + current_data_attributes = current_depth.zero? ? { row_depth: 0 } : table_data_attributes + + # Additional class and data attributes needed for toggle logic + output << pb_rails("advanced_table/table_row", props: { id: id, row: row, column_definitions: leaf_columns, depth: current_depth, collapsible_trail: collapsible_trail, classname: additional_classes, table_data_attributes: current_data_attributes }) if row[:children].present? - output << row[:children].map do |child_row| + row[:children].each do |child_row| is_first_child = row[:children].first == child_row - - child_output = render_row_and_children(child_row, column_definitions, current_depth + 1, is_first_child, new_ancestor_ids, top_parent_id) - immediate_parent_id = row.object_id - top_parent = top_parent_id - # Combine ancestor_ids to build the content id data_content = new_ancestor_ids.join("-") + "-#{child_row.object_id}" - child_output.to_str.sub(" +<%= pb_content_tag(:tr) do %> <% object.column_definitions.each_with_index do |column, index| %> - <%= pb_rails("table/table_cell", props: { classname: object.td_classname}) do %> + <%= pb_rails("table/table_cell", props: { classname: "id-cell chrome-styles"}) do %> <%= pb_rails("flex", props:{ align: "center", justify: "start" }) do %> <% if collapsible_trail && index.zero? %> <% (1..depth).each do |i| %> diff --git a/playbook/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.rb b/playbook/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.rb index 2d708b9b2f..58cdd3b14c 100644 --- a/playbook/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.rb +++ b/playbook/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.rb @@ -14,13 +14,15 @@ class TableSubrowHeader < Playbook::KitBase default: "" prop :collapsible_trail, type: Playbook::Props::Boolean, default: true + prop :subrow_data_attributes, type: Playbook::Props::HashProp, + default: {} - def classname - generate_classname("pb_table_tr", "bg-white", subrow_depth_classname, separator: " ") + def data + Hash(prop(:data)).merge(subrow_data_attributes) end - def td_classname - generate_classname("id-cell", "chrome-styles", separator: " ") + def classname + generate_classname("pb_table_tr", "bg-silver", "pb_subrow_header", subrow_depth_classname, separator: " ") end private