0

Trying to figure out a way of getting the expandable section below the main row - expand to be full width of the table. I am having issues with it just fitting within the first column.

Are there alternatives? First I wanted to make a slide in Panel once you click a link … that didn't work. Then I tried a panel with a toggle... that didn't work.

Any and all help will be appreciated.

LWC main component

<template>  
    <div class="slds-tabs_default__content slds-show slds-clearfix" style="background-color:#f3f3f3;border: 1px solid; border-color: #C9C9C9; border-radius: var(--br-md);padding: var(--su12);">    
            <!-- <div class="slds-button-group slds-clearfix" role="group">
                <lightning-button  style="margin:7px;" variant="brand" label="Service GHP"  title="Submit Chnages"  onclick={onSGHP} class="slds-m-around_x-large"></lightning-button>
                <lightning-button style="margin:7px;" variant="brand" label="Sales Motion" title="Submit Chnages"  onclick={onSM}   class="slds-m-around_x-large"></lightning-button>
                <lightning-button style="margin:7px;" variant="brand" label="Implementation GHP" title="Submit Chnages"  onclick={onIGHP}   class="slds-m-around_x-large"></lightning-button>
            </div>             -->
            <div class="slds-card slds-page-header" style="border: 0;
            box-shadow: none;
            border-radius: var(--lwc-borderRadiusMedium,0.25rem)!important;">
                <div class="" style="background-color: #f3f3f3; display: flex;
                font-size: var(--slds-c-card-heading-font-size, var(--sds-c-card-heading-font-size, var(--lwc-varFontSize5,1rem)));
                font-weight: var(--slds-c-card-heading-font-weight, var(--sds-c-card-heading-font-weight, var(--lwc-cardFontWeight,700)));
                line-height: var(--lwc-lineHeightHeading,1.25); float: left!important;
                margin-bottom: 10px;
                padding-top: 17px;
                padding-left: 20px; ">
                    Risk Event Tasks
                    </div>
                <div style="float:right!important; margin-bottom: 10px; padding-top:10px; padding-right:10px;">
                    <lightning-button variant="brand-outline" onclick={addTask} label="New Task" aria-haspopup="modal">
                    </lightning-button>
                </div>  
            </div> 
            
        <table style="border: 1px solid; border-color: #C9C9C9; border-radius: var(--br-md);padding: var(--su12);"
      class="table-dragable slds-table slds-table_cell-buffer slds-table slds-table_cell-buffer slds-table_bordered slds-table_col-bordered slds-max-medium-table_stacked-horizontal" aria-describedby="User Details">
            <thead>
                <tr class="slds-line-height_reset">       
                    <th scope="col" style="max-width:175px;">Name</th>
                    <th scope="col" style="max-width:50px;">Task #</th>
                    <th scope="col" style="width:20px;display: none;">ID</th>
                    <th scope="col" style="max-width:175px;">Task</th>
                    <th scope="col" style="max-width:125px;">Phase</th>
                    <th scope="col" style="max-width:100px;">Status</th>
                    <th scope="col" >Started Date</th>
                    <th scope="col" >Last Touched</th>
                    <th scope="col" >Closed Date</th>
                </tr>
            </thead>        
            <tbody>    
                <template if:true={tasklist}>
                    <template for:each={tasklist} for:item="task" for:index="index"> 
                        <tr class="grabable"
                           key={task.Id}
                           id={task.Id}
                           draggable="true"
                           data-drag-id={task.Task__c}
                            ondragstart={onDragStart} ondragover={onDragOver} ondrop={onDrop}>

<!-- THIS IS WHERE THE CODE I NEED FIXED STARTS-->
                            <th scope="col" style="max-width:175px;">
                                {task.Name}
                                <template for:each={sections} for:item="section">
                      <c-expandable-section key={section.id} id={section.id} label="more details">
                                    <c-client-health-task-add-new></c-client-health-task-add-new>                           
                      </c-expandable-section>                                    
                                </template>
                            </th>
<!-- THIS IS WHERE THE CODE I NEED FIXED ENDS-->
                            <th scope="col" class="myIndex">{task.Task__c}</th>
                            <th scope="col" class="myId" style="display: none;">{task.Id}</th>
                            <th scope="col" class="slds-cell-wrap" style="max-width:175px;">{task.Task_Name__c}</th> 
                            <th scope="col" class="myPhase slds-cell-wrap">{task.Phase__c}</th>
                            <th scope="col">{task.Task_Status__c}</th>
                            <th scope="col"><lightning-formatted-date-time
                                value={task.Started_Date__c}
                                year="numeric"
                                month="numeric"
                                day="numeric"
                                time-zone="UTC"
                            ></lightning-formatted-date-time></th>
                            <th scope="col"><lightning-formatted-date-time
                                value={task.Last_Touched__c}
                                year="numeric"
                                month="numeric"
                                day="numeric"
                                time-zone="UTC"
                            ></lightning-formatted-date-time></th>
                            <th scope="col"><lightning-formatted-date-time
                                value={task.Closed_Date__c}
                                year="numeric"
                                month="numeric"
                                day="numeric"
                                time-zone="UTC"
                            ></lightning-formatted-date-time></th>
                            <template for:each={sections} for:item="section">
                                <c-expandable-section key={section.id} id={section.id} label="more details">           
                                    <c-client-health-task-add-new></c-client-health-task-add-new>                           
                                </c-expandable-section>                                    
                            </template>
                        </tr> 
                   

                    </template>    
                </template>    
                <template if:true={TaskData.error}>No rows found.</template>    
            </tbody>    
        </table>   
    </div>

    <br/>
    <br/>
    
    
      
    </template>

LWC expandable component HTML (the LWC that is inside the main LWC)

<template>
    <div class="slds-section slds-is-closed" data-id={id}>
        <!--section header-->
        <h3 class="slds-section__title" style="bg-color: #FFF;">
            <button type="button" aria-controls={id} class="slds-button slds-section__title-action" data-buttonid={id}
                onclick={toggleSection}>
                <lightning-icon icon-name="utility:switch" alternative-text="button icon" size="x-small"
                    class="slds-section__title-action-icon slds-button__icon_left"></lightning-icon>
                <span class="slds-truncate" title={label}>{label}</span>
            </button>
        </h3>
        <!--section body-->
        <div class="slds-section__content">
            <slot></slot>
        </div>
    </div>
</template>

LWC expandable component JS

import { LightningElement, api } from 'lwc';
export default class ExpandableSection extends LightningElement {
    @api id;
    @api label;

    toggleSection(event) {
        let buttonid = event.currentTarget.dataset.buttonid;
        let currentsection = this.template.querySelector('[data-id="' + buttonid + '"]');
        if (currentsection.className.search('slds-is-open') == -1) {
            currentsection.className = 'slds-section slds-is-open';
        } else {
            currentsection.className = 'slds-section slds-is-close';
        }
    }
}

** UPDATE ** Here's the latest code change according to the solution provided.

<tbody>    
            <template if:true={tasklist}>
                <template for:each={tasklist} for:item="task" for:index="index"> 
                    <tr class="grabable slds-hint-parent"
                       key={task.Id}
                       id={task.Id}
                       draggable="true"
                       data-drag-id={task.Task__c}
                        ondragstart={onDragStart} ondragover={onDragOver} ondrop={onDrop}>
                        <!-- <th scope="col" style="max-width:175px;">
                           {task.Name}
                            <template for:each={sections} for:item="section">
                                <c-expandable-section key={section.id} id={section.id} label="more details">
                                    <c-client-health-task-add-new></c-client-health-task-add-new>                           
                                </c-expandable-section>                                    
                            </template>
                        </th> -->
                        <th
                                scope="row"
                                class="expand-column"
                            >
                            {task.Name}
                                <lightning-button-icon
                                    class="slds-var-p-horizontal_x-small"
                                    variant="bare"
                                    size="large"
                                    icon-name={task.iconName}
                                    alternative-text="Expand"
                                    data-id={task.Id}
                                    onclick={handleExpand}
                                    title="Expand"
                                >
                                    <label>Expand</label>
                                </lightning-button-icon>
                        </th>
                        <th scope="col" class="myIndex">{task.Task__c}</th>
                        <th scope="col" class="myId" style="display: none;">{task.Id}</th>
                        <th scope="col" class="slds-cell-wrap" style="max-width:175px;">{task.Task_Name__c}</th> 
                        <th scope="col" class="myPhase slds-cell-wrap">{task.Phase__c}</th>
                        <th scope="col">{task.Task_Status__c}</th>
                        <th scope="col"><lightning-formatted-date-time
                            value={task.Started_Date__c}
                            year="numeric"
                            month="numeric"
                            day="numeric"
                            time-zone="UTC"
                        ></lightning-formatted-date-time></th>
                        <th scope="col"><lightning-formatted-date-time
                            value={task.Last_Touched__c}
                            year="numeric"
                            month="numeric"
                            day="numeric"
                            time-zone="UTC"
                        ></lightning-formatted-date-time></th>
                        <th scope="col"><lightning-formatted-date-time
                            value={task.Closed_Date__c}
                            year="numeric"
                            month="numeric"
                            day="numeric"
                            time-zone="UTC"
                        ></lightning-formatted-date-time></th>
                        <!-- <template for:each={sections} for:item="section">
                            <c-expandable-section key={section.id} id={section.id} label="more details">           
                                <c-client-health-task-add-new></c-client-health-task-add-new>                           
                            </c-expandable-section>                                    
                        </template> -->
                    </tr> 
                    <tr key={task.Id} if:true={task.isExpanded} class="slds-hint-parent">
                    <th colspan="4">
                        <template for:each={sections} for:item="section">
                            <c-expandable-section key={section.id} id={section.id} label="more details">           
                                <c-client-health-task-add-new></c-client-health-task-add-new>                           
                            </c-expandable-section>                                    
                        </template>
                    </th>
                    </tr>

                </template>    
            </template>    
        
            <template if:true={TaskData.error}>No rows found.</template>    
        </tbody>  

This is the entire JS file:

    import { LightningElement, wire, track, api } from "lwc";
import MyModalAddNew from 'c/clientHealthTaskAddNew';
import MyModalEdit from 'c/clientHealthTaskEdit';
import getList from '@salesforce/apex/ASI_RiskEventTaskController.getRiskEventTask';
import saveList from '@salesforce/apex/ASI_RiskEventTaskController.saveRiskEventTask';

import { updateRecord } from 'lightning/uiRecordApi';

import { refreshApex } from "@salesforce/apex";

export default class DataTableDragable extends LightningElement {

  @api recordId;
  @api userMap;
  @api dragMap;
  users;
  tasklist;
  error;
  testId = 'a3Z8K000001fO7BUAU';





handleExpand(event) {
  let taskId = event.target.dataset.id;
  let taskFound = this.tasklist.find((task) => task.Id === taskId);

  // toggle the value
  taskFound.isExpanded = !taskFound.isExpanded;
  
  // set the icon name
  taskFound.iconName = taskFound.isExpanded
      ? "utility:chevrondown"
      : "utility:chevronright";
}



  @track TaskData = JSON.parse('{"getTaskData": [{"Id": "111", "Phase": "Notification", "Task": "Client contacted & Meeting confirmed","Checklist": "Lorem ipsum dolor sit amet. Sit illo voluptas et fugit quibusdam qui omnis","index": 1},{"Id": "222","Phase": "Discovery", "Task": "Gather and document internal facts/history","Checklist": "Lorem ipsum dolor sit amet. Sit illo voluptas et fugit quibusdam qui omnis","index": 2},{"Id": "333","Phase": "Creation", "Task": "M and A Paperwork/Proposal created/sent to customer","Checklist": "Qui doloremque dolorem ut aperiam impedit et quidem praesentium","index": 3},{"Id": "444","Phase": "Present/Accept", "Task": "Proposal Presented","Checklist": "Ab internos consectetur aut soluta velit ut rerum quia","index": 4},{"Id": "555","Phase": "Delivery", "Task": "N/A","Checklist": "A consequuntur quaerat ea dolor necessitatibus rem voluptas modi quo expedita deleniti ea sunt facilis non modi","index": 5},{"Id": "666","Phase": "Post Plan Governance", "Task": "Update Client Health/Close Risk","Checklist": "Qui doloremque dolorem ut aperiam impedit et quidem praesentium","index": 6}]}');
  
  @wire(getList, { retId: '$testId'})  //a3Z8K000001bxakUAA
  contactRec(result)
  {
    this.taskResult = result;
    if(result.error){
        console.log('error is#'+result.error);
        this.error = result.error;
    }
    else if(result.data){
      this.tasklist = result.data;
    }
  }


  async addTask() {
    const result = await MyModalAddNew.open({
        size: 'small',
        description: 'Accessible description of modal\'s purpose',
        content: 'Risk Event Task',
    });
    console.log(result);
    refreshApex(this.taskResult)
      .then(result => this.processRowNumbers())
      .catch(error => console.log('adTask: ' + error));
}


  async editTask() {
    const result = await MyModalEdit.open({
        size: 'small',
        description: 'Accessible description of modal\'s purpose',
        content: 'Edit Risk Event Task',
    });
    console.log(result);
    refreshApex(this.taskResult)
      .then(result => this.processRowNumbers())
      .catch(error => console.log('adTask: ' + error));
  }
updateRecordView(recordId) {
  updateRecord({fields: { Id: recordId }});
  this.updateRecordView(this.recordId);
}



    // //Boolean tracked variable to indicate if modal is open or not default value is false as modal is closed when page is loaded 
    // @track isModalOpen = false;
    // openModal() {
    //     // to open modal set isModalOpen tarck value as true
    //     this.isModalOpen = true;
    // }
    // closeModal() {
    //     // to close modal set isModalOpen tarck value as false
    //     this.isModalOpen = false;
    // }
    // submitDetails() {
    //     // to close modal set isModalOpen tarck value as false
    //     //Add your code to call apex method or do some processing
    //     this.isModalOpen = false;
    // }


  renderedCallback() {

    if (!!this.tasklist) {
      this.userMap = new Map();
      let tempArray = JSON.parse(JSON.stringify(this.tasklist));
      tempArray.forEach((arrayElement, index) => {
        //arrayElement.index = index;
        this.userMap.set(arrayElement.Id, arrayElement);
      });
      console.log(
        ": ------------------------------------------------------------"
      );
      console.log(
        "DataTableDragable -> renderedCallback -> tempArray",
        JSON.stringify(tempArray)
      );
      console.log(
        ": ------------------------------------------------------------"
      );
      this.users = JSON.parse(JSON.stringify(tempArray));
    }
  }


  // handleSubmit() {
  //   console.log("in submit method");
  //   let data = this.users.data;
  //   console.log(": ----------------------------------------------");
  //   console.log(
  //     "DataTableDragable -> handleSubmit -> data",
  //     JSON.stringify(data)
  //   );
  //   console.log(": ----------------------------------------------");
  // }

  processRowNumbers() {
    console.log('PRN');
    const trs = this.template.querySelectorAll(".myIndex");
    const ids = this.template.querySelectorAll(".myId");      
    
    for (let i = 0; i < trs.length; i++) {
     let currentRowId = ids[i].innerText;
     let currentRowRef = this.userMap.get(currentRowId);
     currentRowRef.Task__c = i + 1;
     this.userMap.set(currentRowId, currentRowRef);
     trs[i].innerText = i + 1;
   }

    this.users = Array.from(this.userMap.values());
    this.tasklist = Array.from(this.userMap.values());
    let data = this.users;
    console.log(": ----------------------------------------------");
    console.log(
      "DataTableDragable -> handleSubmit -> data",
      JSON.stringify(data)
    );
    console.log(": ----------------------------------------------");

    saveList({ retl: this.tasklist })
    .then((result) => {
      console.log(result);
      this.error = undefined;
    })
    .catch((error) => {
      console.log(error);
      this.error = error;
    }); 


  }

  onDragStart(evt) {
    const inputs = this.template.querySelectorAll(".mychkbox");
    this.dragMap = new Map();

    if (inputs) {
      for (let i = 0; i < inputs.length; i++) {
        if (inputs[i].checked) {
          let currentRow = inputs[i].parentNode.parentNode;
          let currentDragId = currentRow.dataset.dragId;
          this.dragMap.set(currentDragId, currentRow);
          //currentRow.classList.add("grabbed");
        }
      }
    }

    let eventRowDataId = evt.currentTarget.dataset.dragId;
    evt.dataTransfer.setData("dragId", eventRowDataId);
    evt.dataTransfer.setData("sy", evt.pageY);
    evt.dataTransfer.effectAllowed = "move";
    evt.currentTarget.classList.add("grabbed");

    if (this.dragMap.has(eventRowDataId)) {
      this.dragMap.forEach((value) => value.classList.add("grabbed"));
    }
  }

  onDragOver(evt) {
    evt.preventDefault();
    evt.dataTransfer.dropEffect = "move";
  }

  onDrop(evt) {
    evt.preventDefault();
    let sourceId = evt.dataTransfer.getData("dragId");

    const sy = evt.dataTransfer.getData("sy");
    const cy = evt.pageY;

    if (sy > cy) {
      if (this.dragMap.has(sourceId)) {

        Array.from(this.dragMap).reverse().forEach( element => {
          let key = element[0];
          const elm = this.template.querySelector(`[data-drag-id="${key}"]`);
          if (!!elm) {
            elm.classList.remove("grabbed");
          }
          evt.currentTarget.parentElement.insertBefore(elm, evt.currentTarget);
        });
      } else {
        const elm = this.template.querySelector(`[data-drag-id="${sourceId}"]`);
        if (!!elm) {
          elm.classList.remove("grabbed");
        }
        evt.currentTarget.parentElement.insertBefore(elm, evt.currentTarget);
      }
    } else {
      if (this.dragMap.has(sourceId)) {
        this.dragMap.forEach((value, key, map) => {
          const elm = this.template.querySelector(`[data-drag-id="${key}"]`);
          if (!!elm) {
            elm.classList.remove("grabbed");
          }
          evt.currentTarget.parentElement.insertBefore(
            elm,
            evt.currentTarget.nextElementSibling
          );
        });
      } else {
        const elm = this.template.querySelector(`[data-drag-id="${sourceId}"]`);
        if (!!elm) {
          elm.classList.remove("grabbed");
        }
        evt.currentTarget.parentElement.insertBefore(
          elm,
          evt.currentTarget.nextElementSibling
        );
      }
    }
    this.processRowNumbers();
  }
  onRefresh() {
    this.dispatchEvent(new RefreshEvent());
    console.log('Refresh Event');
  }

  onSGHP() {
    this.testId = 'a3Z8K000001fO7BUAU';
  }

  onSM() {
    this.testId = 'a3Z8K000001fO76UAE';
  }

  onIGHP() {
    this.testId = 'a3Z8K000001fO7BUAU';
  }


}

Screenshot after code change

1 Answer 1

0

I am sure there are other ways to do this but the simplest way is to add two table rows inside an iterator.

Before that, you will need to add two additional attributes isExpanded to the list that you are iterating. and icon name to store the iconName.

And a button on click of which you will toggle the value of isExpanded. After that just handle the button click and toggle the value.

Here is the code.

HTML


<tbody>
    <template
        if:true={tasklist}
        for:each={tasklist}
        for:item="task"
    >
    
        <!--- first row with table column data -->
        <tr
            key={task.Id}
            class="slds-hint-parent"
        >
            <!-- button to expand the section -->
            <th
                scope="row"
                class="expand-column"
            >
                <lightning-button-icon
                    class="slds-var-p-horizontal_x-small"
                    variant="bare"
                    size="large"
                    icon-name={task.iconName}
                    alternative-text="Expand"
                    data-id={task.Id}
                    onclick={handleExpand}
                    title="Expand"
                >
                    <label>Expand</label>
                </lightning-button-icon>
            </th>
            <td>add your tds</td>
            <td></td>
            <td></td>
        </tr>
        
        <!--- another row for the expandable section -->
        <tr
            key={task.Id}
            if:true={task.isExpanded}
            class="slds-hint-parent"
        >
            <td colspan="4">
        </tr>
    </template>
</tbody>

JS

handleExpand(event) {
    let taskId = event.target.dataset.id;
    let taskFound = this.tasklist.find((task) => task.Id === taskId);

    // toggle the value
    taskFound.isExpanded = !taskFound.isExpanded;
    
    // set the icon name
    taskFound.iconName = taskFound.isExpanded
        ? "utility:chevrondown"
        : "utility:chevronright";
}

Also don't forget to modify your data when it is initially loaded to make it mutable and add the default iconName value. This can be done in both wired or getter-setter methods.

this.tasklist = data.map((task) => ({
    ...task,
    iconName: "utility:chevronright"
}));

Also don't forget to clean up the additional attributes before sending this data to apex code.

6
  • Thank you I will try this today. One thing to note is that the table rows, besides the header, are draggable. This way each row can be reordered. How would you make the 2 rows both drag at the same time?
    – Darrell W
    Commented Sep 19, 2023 at 11:27
  • @DarrellW use the same id or class to rows so that dragging one moves its related row as well or drag the other programmatically Commented Sep 19, 2023 at 11:36
  • Here's the latest code change (at the bottom above) - plus a screenshot. No change, no extra row displaying.
    – Darrell W
    Commented Sep 19, 2023 at 12:57
  • @DarrellW there are several things missing from your code, you are not setting properties isExpanded and iconName also I don't see the handleExpand method in your code. Also make sure to add @track to taskList attribute and set the correct colspan to the second <tr>. Commented Sep 19, 2023 at 14:50
  • Yes, it's in my JS file. I pasted above the contents. For some reason it's not formatting correctly. Is there a way to send you the files? How / where do I set the two properties of isExpanded and iconName and the @track ?
    – Darrell W
    Commented Sep 19, 2023 at 15:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .