Skip to content

Commit

Permalink
[PBNTR-702] Create a "stickyLeftcolumn" prop for the Table kit - RAIL…
Browse files Browse the repository at this point in the history
…S only (#3970)

**What does this PR do?** A clear and concise description with your
runway ticket url.
As a Playbook user,
I want to create a "stickyLeftcolumn" prop for our [Rails Table
kit](https://playbook.powerapp.cloud/kits/table/rails),
so that I can begin implementing a Frozen column for our Table kits.

**Screenshots:** Screenshots to visualize your addition/change
![Screenshot 2024-12-03 at 8 15
05 AM](https://github.com/user-attachments/assets/78dc4640-c43f-4e81-b7e1-4a52d6a93e7c)

#### Checklist:
- [x] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new
kit`, `deprecated`, or `breaking`. See [Changelog &
Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels)
for details.
- [x] **DEPLOY** I have added the `milano` label to show I'm ready for a
review.
- [x] **TESTS** I have added test coverage to my code.
  • Loading branch information
skduncan authored Dec 10, 2024
1 parent d5e29a8 commit 96a8181
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<%= pb_rails("table", props: { size: "md", responsive: "scroll", sticky_left_column: ["1", "2", "3"] }) do %>
<thead>
<tr>
<th id="1">Column 1</th>
<th id="2">Column 2</th>
<th id="3">Column 3</th>
<th>Column 4</th>
<th>Column 5</th>
<th>Column 6</th>
<th>Column 7</th>
<th>Column 8</th>
<th>Column 9</th>
<th>Column 10</th>
<th>Column 11</th>
<th>Column 12</th>
<th>Column 13</th>
<th>Column 14</th>
<th>Column 15</th>
</tr>
</thead>
<tbody>
<tr>
<td id="1">Value 1</td>
<td id="2">Value 2</td>
<td id="3">Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Column 6</td>
<td>Column 7</td>
<td>Column 8</td>
<td>Column 9</td>
<td>Column 10</td>
<td>Column 11</td>
<td>Column 12</td>
<td>Column 13</td>
<td>Column 14</td>
<td>Column 15</td>

</tr>
<tr>
<td id="1">Value 1</td>
<td id="2">Value 2</td>
<td id="3">Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Column 6</td>
<td>Column 7</td>
<td>Column 8</td>
<td>Column 9</td>
<td>Column 10</td>
<td>Column 11</td>
<td>Column 12</td>
<td>Column 13</td>
<td>Column 14</td>
<td>Column 15</td>

</tr>
<tr>
<td id="1">Value 1</td>
<td id="2">Value 2</td>
<td id="3">Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Column 6</td>
<td>Column 7</td>
<td>Column 8</td>
<td>Column 9</td>
<td>Column 10</td>
<td>Column 11</td>
<td>Column 12</td>
<td>Column 13</td>
<td>Column 14</td>
<td>Column 15</td>

</tr>
<tr>
<td id="1">Value 1</td>
<td id="2">Value 2</td>
<td id="3">Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Column 6</td>
<td>Column 7</td>
<td>Column 8</td>
<td>Column 9</td>
<td>Column 10</td>
<td>Column 11</td>
<td>Column 12</td>
<td>Column 13</td>
<td>Column 14</td>
<td>Column 15</td>

</tr>
</tbody>
<% end %>
126 changes: 100 additions & 26 deletions playbook/app/pb_kits/playbook/pb_table/index.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,106 @@
import PbEnhancedElement from '../pb_enhanced_element'

export default class PbTable extends PbEnhancedElement {
static get selector(): string {
return '.table-responsive-collapse'
}

connect(): void {
const tables = document.querySelectorAll('.table-responsive-collapse');

// Each Table
[].forEach.call(tables, (table: HTMLTableElement) => {
// Header Titles
const headers: string[] = [];
[].forEach.call(table.querySelectorAll('th'), (header: HTMLTableCellElement) => {
const colSpan = header.colSpan
for (let i = 0; i < colSpan; i++) {
headers.push(header.textContent.replace(/\r?\n|\r/, ''));
private stickyLeftColumns: string[] = [];
private handleStickyColumnsRef: () => void;

static get selector(): string {
return '.table-responsive-collapse'
}

connect(): void {
const tables = document.querySelectorAll('.table-responsive-collapse');
// Each Table
[].forEach.call(tables, (table: HTMLTableElement) => {
// Header Titles
const headers: string[] = [];
[].forEach.call(table.querySelectorAll('th'), (header: HTMLTableCellElement) => {
const colSpan = header.colSpan
for (let i = 0; i < colSpan; i++) {
headers.push(header.textContent.replace(/\r?\n|\r/, ''));
}
});
// for each row in tbody
[].forEach.call(table.querySelectorAll('tbody tr'), (row: HTMLTableRowElement) => {
// for each cell
[].forEach.call(row.cells, (cell: HTMLTableCellElement, headerIndex: number) => {
// apply the attribute
cell.setAttribute('data-title', headers[headerIndex])
})
})
});

// New sticky columns logic
this.initStickyColumns();
}

private initStickyColumns(): void {
// Find tables with sticky-left-column class
const tables = document.querySelectorAll('.sticky-left-column');

tables.forEach((table) => {
// Extract sticky left column IDs by looking at the component's class
const classList = Array.from(table.classList);

// Look for classes in the format sticky-left-column-{ids}
const stickyColumnClass = classList.find(cls => cls.startsWith('sticky-columns-'));
if (stickyColumnClass) {
// Extract the IDs from the class name
this.stickyLeftColumns = stickyColumnClass
.replace('sticky-columns-', '')
.split('-');

if (this.stickyLeftColumns.length > 0) {
this.handleStickyColumnsRef = this.handleStickyColumns.bind(this);
this.handleStickyColumns();
window.addEventListener('resize', this.handleStickyColumnsRef);
}
}
});
}

// for each row in tbody
[].forEach.call(table.querySelectorAll('tbody tr'), (row: HTMLTableRowElement) => {
// for each cell
[].forEach.call(row.cells, (cell: HTMLTableCellElement, headerIndex: number) => {
// apply the attribute
cell.setAttribute('data-title', headers[headerIndex])
})
})
})
}
}
private handleStickyColumns(): void {
let accumulatedWidth = 0;

this.stickyLeftColumns.forEach((colId, index) => {
const isLastColumn = index === this.stickyLeftColumns.length - 1;
const header = document.querySelector(`th[id="${colId}"]`);
const cells = document.querySelectorAll(`td[id="${colId}"]`);

if (header) {
header.classList.add('sticky');
(header as HTMLElement).style.left = `${accumulatedWidth}px`;

if (!isLastColumn) {
header.classList.add('with-border');
header.classList.remove('sticky-shadow');
} else {
header.classList.remove('with-border');
header.classList.add('sticky-shadow');
}

accumulatedWidth += (header as HTMLElement).offsetWidth;
}

cells.forEach((cell) => {
cell.classList.add('sticky');
(cell as HTMLElement).style.left = `${accumulatedWidth - (header as HTMLElement).offsetWidth}px`;

if (!isLastColumn) {
cell.classList.add('with-border');
cell.classList.remove('sticky-shadow');
} else {
cell.classList.remove('with-border');
cell.classList.add('sticky-shadow');
}
});
});
}

// Cleanup method to remove event listener
disconnect(): void {
if (this.handleStickyColumnsRef) {
window.removeEventListener('resize', this.handleStickyColumnsRef);
}
}
}
2 changes: 1 addition & 1 deletion playbook/app/pb_kits/playbook/pb_table/table.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
<%= content.presence %>
<% end %>
<% end %>
<% end %>
<% end %>
19 changes: 17 additions & 2 deletions playbook/app/pb_kits/playbook/pb_table/table.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class Table < Playbook::KitBase
prop :text
prop :sticky, type: Playbook::Props::Boolean,
default: false
prop :sticky_left_column, type: Playbook::Props::Array,
default: []
prop :vertical_border, type: Playbook::Props::Boolean,
default: false
prop :striped, type: Playbook::Props::Boolean,
Expand All @@ -37,8 +39,8 @@ class Table < Playbook::KitBase
def classname
generate_classname(
"pb_table", "table-#{size}", single_line_class, dark_class,
disable_hover_class, container_class, data_table_class, sticky_class, collapse_class,
vertical_border_class, striped_class, outer_padding_class,
disable_hover_class, container_class, data_table_class, sticky_class, sticky_left_column_class,
collapse_class, vertical_border_class, striped_class, outer_padding_class,
"table-responsive-#{responsive}", separator: " "
)
end
Expand Down Expand Up @@ -73,6 +75,19 @@ def sticky_class
sticky ? "sticky-header" : nil
end

def sticky_left_column_class
if sticky_left_column.empty?
nil
else
sticky_col_classname = "sticky-left-column sticky-columns"
sticky_left_column.each do |id|
sticky_col_classname += "-#{id}"
end

sticky_col_classname
end
end

def striped_class
striped ? "striped" : nil
end
Expand Down
1 change: 1 addition & 0 deletions playbook/spec/pb_kits/playbook/kits/table_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
expect(subject.new(disable_hover: true, dark: true, size: "lg", single_line: true).classname).to eq "pb_table table-lg single-line table-dark no-hover table-card table-collapse-sm table-responsive-collapse dark"
expect(subject.new(sticky: true).classname).to eq "pb_table table-md table-card sticky-header table-collapse-sm table-responsive-collapse"
expect(subject.new(outer_padding: "sm").classname).to eq "pb_table table-md table-card table-collapse-sm outer_padding_space_sm table-responsive-collapse"
expect(subject.new(sticky_left_column: %w[1 2 3]).classname).to eq "pb_table table-md table-card sticky-left-column sticky-columns-1-2-3 table-collapse-sm table-responsive-collapse"
end
end
end

0 comments on commit 96a8181

Please sign in to comment.