I thought I'd provide an additional answer to this thread, since the accepted solution cannot accommodate de-selecting child rows when the parent is de-selected. This solution allows for de-selecting parents, de-selecting children separately from the parent, auto-selecting parents when all children are selected, and auto-deselecting parents when at least one child is deselected.
HTML:
<lightning-tree-grid
columns={columnList}
data={projectList}
key-field="Id"
onrowselection={updateSelectedRows}
selected-rows={selectedRows}>
</lightning-tree-grid>
JS:
import { LightningElement, api, track, wire } from 'lwc';
import getProjectRecords from '@salesforce/apex/cc_CreateInvoiceController.getProjectRecords';
export default class Test12 extends LightningElement {
@api recordId;
@track columnList; // set by apex wire service using field set
@track projectListObj; // the data as returned by the apex wire service
@track projectList; // wire service data required modification for correct display
@track selectedRows = [];
@track currentSelectedRows; // used to track changes
@wire(getProjectRecords, { projectId: '$recordId' })
wiredProjects({ error, data }) {
var dataObj;
if(data) {
dataObj = JSON.parse(data);
this.projectListObj = dataObj;
this.projectList = JSON.parse(JSON.stringify(dataObj).split('items').join('_children'));
}
}
}
updateSelectedRows() {
var tempList = [];
var selectRows = this.template.querySelector('lightning-tree-grid').getSelectedRows();
if(selectRows.length > 0){
selectRows.forEach(record => {
tempList.push(record.Id);
})
// select and deselect child rows based on header row
this.projectListObj.forEach(record => {
// if header was checked and remains checked, do not add sub-rows
// if header was not checked but is now checked, add sub-rows
if(!this.currentSelectedRows.includes(record.Id) && tempList.includes(record.Id)) {
record.items.forEach(item => {
if(!tempList.includes(item.Id)) {
tempList.push(item.Id);
}
})
}
// if header was checked and is no longer checked, remove header and sub-rows
if(this.currentSelectedRows.includes(record.Id) && !tempList.includes(record.Id)) {
record.items.forEach(item => {
const index = tempList.indexOf(item.Id);
if(index > -1) {
tempList.splice(index, 1);
}
})
}
// if all child rows for the header row are checked, add the header
// else remove the header
var allSelected = true;
record.items.forEach(item => {
if(!tempList.includes(item.Id)) {
allSelected = false;
}
})
if(allSelected && !tempList.includes(record.Id)) {
tempList.push(record.Id);
} else if(!allSelected && tempList.includes(record.Id)) {
const index = tempList.indexOf(record.Id);
if(index > -1) {
tempList.splice(index, 1);
}
}
})
this.selectedRows = tempList;
this.currentSelectedRows = tempList;
}
}
When I want to rebuild/extend some of the functionality for LWC I usually get styling from SLDS. You can check markup here for tree-grid.
Salesforce flattens data and uses aria-expanded, aria-level for styling. I would suggest the same. Here is the script to flatten data. I also updated playground with simple styling for Id
UPDATE:
Created separate playground to not override changes.
UPDATE
Added expand/hide.
Here is sample row markup with some code.
// rendered.js
let data = [
{ Name:'st1', Id:1, age:'2', num:'342222222', Email:'v@gmail.com' },
{ Name:'st1', age:'2', Id:2, num:'342222222', Email:'v@gmail.com', children:[
{ Name:'st1child', Id:6, age:'2', num:'342222222', Email:'v@gmail.com' },
{ Name:'st1child', Id:7, age:'2', num:'342222222', Email:'v@gmail.com' }, ]
},
{ Name:'st1', age:'2', Id:3, num:'342222222', Email:'v@gmail.com' },
{ Name:'st1', age:'2', Id:4, num:'342222222', Email:'v@gmail.com' },
{ Name:'st1', age:'2', Id:5, num:'342222222', Email:'v@gmail.com' },
];
const isExpandedDefault = false;
const flatIt = (data, level) => data.reduce((acc, row) => [...acc, Object.assign(row, {
level,
expanded: isExpandedDefault,
}), ...flatIt(row.children || [], level + 1)], []);
data = flatIt(data, 1);
<!-- parent.html -->
<template>
<table class="slds-tree">
<thead>
<th>Name</th>
<th>Age</th>
<th>Num</th>
<th>Email</th>
</thead>
<tbody>
<template for:each={datas} for:item="d">
<c-child key={d.Id} data={d}></c-child>
</template>
</tbody>
</table>
</template>
// Child.js
export default class Child extends LightningElement {
@track _data;
renderedCallback() {
}
@api
set data(value) {
this._data = value;
this.ariaExpanded = false;
this.ariaLevel = value.level;
}
get data() {
return this._data;
}
}
Best Answer
Component's DOM elements & CSS are isolated to its own, meaning you can't modify it from outside. So what you're trying to do is security feature & designed this way.
Recommended
So the straightforward & legit answer to your question is you can't do this & should roll your own tree-grid component & you can styling it the way you want. You can utilize the template provided here & add functionality to it.
SUBJECT TO CHANGE
Salesforce doesn't recommend doing it this way & is subject to change. This may fail in future SF release if they modify the internal which you targetted from the static resource. So use it on YOUR OWN RISK. The other way to style these component would be through a static resource which applies to the document level. You can write the same style in .css file upload it as static resource & load it in your lwc.
customTreeGrid.css
customLwc.html
customLwc.js