Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistent behavior in Salesforce LWC Hierarchy Tree Grid selection and state #591

Open
rafasegat opened this issue Jul 22, 2024 · 0 comments

Comments

@rafasegat
Copy link

rafasegat commented Jul 22, 2024

Full post here: https://salesforce.stackexchange.com/questions/424214/inconsistent-behavior-in-salesforce-lwc-hierarchy-tree-grid-selection-and-state

I'm working on a LWC Dashboard Analytics component - https://developer.salesforce.com/docs/atlas.en-us.bi_dev_guide_lwc_in_db.meta/bi_dev_guide_lwc_in_db/bi_lwc_in_db_reference.htm

image

I'm encountering an unexpected behavior in a Salesforce LWC component, and I'm not sure if it's a bug or an issue with my code. Here's the scenario I'm observing:

Scenario:

1 - Page loads user is on "Tab 1"

2 - User clicks "Current Tab"

Console.log:

this.selection = []

this.getStateStepFilter = ['item1', 'item2', 'item3', 'item4'] // Unexpected, should be []

3 - User selects Item 1 and click "Update": works nice

this.selection = ['item1']

this.getStateStepFilter = ['item1']

4 - User clicks on "Tab 2"

5 - User clicks back on "Current Tab"

this.selection = ['item1', 'item2', 'item3', 'item4'] // Unexpected, should be ['item1']

this.getStateStepFilter = ['item1', 'item2', 'item3', 'item4'] // Unexpected, should be ['item1']

-- End

Additionally, when refreshing the page, the values seem to be set inconsistently.

Questions:

Is this a known Salesforce bug? Could this be an issue with my code?
Has anyone else experienced similar behavior with Salesforce tab components?

import { LightningElement, api, track, wire } from 'lwc'; 
import saveUserSelection from '@salesforce/apex/HierarchyTreeGridUserSelection.saveUserSelection';
import apex_getUserSelection from '@salesforce/apex/HierarchyTreeGridUserSelection.getUserSelection';
import USER_ID from '@salesforce/user/Id';
import { refreshApex } from '@salesforce/apex';

export default class HierarchyTreeGrid_DEV extends LightningElement {
  @api selection;
  @api setSelection;
  @api refresh;
  @api getState;
  @api setState;
  @api metadata;
  @track selectedRows = [];
  @track selectValuesText = "All";

  isInitialized = false;

  @api stateChangedCallback(_, newState) {
    const stateStepFilter = this.getStateStepFilter(newState);
    const selectedRows = stateStepFilter || this.selection.map((e) => e.Id);
    if(selectedRows?.length) {
      console.log("stateChangedCallback stateStepFilter", JSON.parse(JSON.stringify(stateStepFilter)));
      console.log("stateChangedCallback selection", JSON.parse(JSON.stringify(this.selection)));
      console.log("stateChangedCallback selectedRows", JSON.parse(JSON.stringify(selectedRows || '{}')));
    } 
  }

  connectedCallback()  {
    const stateStepFilter = this.getStateStepFilter(newState);
    const selectedRows = stateStepFilter || this.selection.map((e) => e.Id);
    if(selectedRows?.length) {
      console.log("connectedCallback stateStepFilter", JSON.parse(JSON.stringify(stateStepFilter)));
      console.log("connectedCallbackselection", JSON.parse(JSON.stringify(this.selection)));
      console.log("connectedCallbackselectedRows", JSON.parse(JSON.stringify(selectedRows || '{}')));
  }

  getStateStepFilter(state) {
    const currentStep = Object.values(state?.state?.steps).find((value) => {
      const array1 = value?.metadata?.groups;
      const array2 = this.metadata?.groups;
      return (array1?.length === array2?.length) && array1.every((el, i) => {
        return el === array2[i]; 
      });
    });
    return currentStep?.values.map((item) => item[1]) || null;
  }

  setSelectedRows(selectedRows) {
    this.selectedRows = selectedRows;
    this.selectedRows?.length == 0 ? this.selectValuesText = "All" : this.selectValuesText = "Selected " + "(" + this.selectedRows?.length + ")";
  }

  wiredUserSelectionResult;
  @wire(apex_getUserSelection, { componentId: '$titleLabel', userId: USER_ID })
  wiredUserSelection(result) {
    this.wiredUserSelectionResult = result;
    if (result?.data?.length ) {
      try {
        const selectedRows = this.getStateStepFilter(this.getState());
        console.log("loaded from cache selectedRows(state) ", selectedRows)
        console.log("loaded from cache selection ", JSON.parse(JSON.stringify(this.selection || {})))
        const jsonData = JSON.parse(result?.data);
        console.log("loaded from cache jsonData", jsonData);
      } catch (e) {
        console.error('Error parsing selectedRows:', e);
      }
    } else if (result.error) {
      console.error('Error retrieving user selection:', result.error);
    }
  }

XML

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>59.0</apiVersion>
    <isExposed>true</isExposed>
    <masterLabel>Hierarchy Tree Grid DEV</masterLabel>
    <targets>
        <target>analytics__Dashboard</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="analytics__Dashboard">
            <hasStep>true</hasStep>
            <property name="idColumn" type="Dimension" label="ID Column" description="Primary key." required="true" />
            <property name="parentIdColumn" type="Dimension" label="Parent ID Column" description="Self-reference to parent record." required="true" />
            <property name="labelColumn" type="Dimension" label="Label Column" description="Record label." required="true" />
            <property name="root" type="String" label="Root Node" />
            <property name="titleLabel" type="String" label="Title Label" required="true"/>
            <property name="filterDivCSS" type="String" label="Stylesheet for the filter Div" />
            <property name="titleLabelCSS" type="String" label="Title Label Stylesheet" description="Stylesheet for the title label" />
            <property name="selectedValueCSS" type="String" label="Selected Value Stylesheet" description="Stylesheet for the Selected Value" />
            <property name="treeGridCSS" type="String" label="Tree Grid Stylesheet" description="Stylesheet for the tree grid" />
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

<template>
    <div class="collapsible-filter-widget" tabindex="0" role="button" aria-haspopup="dialog" onclick={openPopover} onkeydown={onKeyDownHandler}>
        <div class="collapsible-filter-body" style={filterDivCSS} >
            <div class="dropdown-trigger">
                <div class="title" style={titleLabelCSS}>{titleLabel}</div>
                <div class="selected-values" style={selectedValueCSS}>{selectValuesText}</div>
                <div style="position:absolute;right:8px;top:30%;">
                    <lightning-icon icon-name="utility:chevrondown" alternative-text="Down Arrow" title="Down Arrow" size="xx-small" ></lightning-icon>
                </div>
            </div>
        </div>
    </div>
<!--- PopOver Component with a tree grid table -->
    <template if:true={isPopOverOpen}>
        <div class="overlay-popup" onclick={closePopover}></div>
        <section aria-describedby="dialog-body-id-110" aria-labelledby="dialog-heading-id-5" class="slds-popover slds-popover_prompt dropdown-menu" 
        role="dialog" onmouseleave={mouseOutHandler} onmouseenter={mouseEnterHandler} 
        style="outline: 0px; position: absolute; width: 100%; min-width: 250px; margin-top: 5px; margin-bottom: 12px;">
            <button class="slds-button slds-button_icon slds-button_icon-small slds-popover__close" title="Close dialog" onclick={closePopover}>
                <lightning-icon icon-name="utility:close" alternative-text="Close" title="Close" size="x-small" ></lightning-icon>
                <span class="slds-assistive-text">Close dialog</span>
            </button>
            <div class="slds-popover__body" id="dialog-body-id-110">
                <div class="slds-media">
                    <div class="slds-media__body">
                        <div onchange={handleSearch}>
                            <lightning-input
                                name="searchBox"
                                label="Search"
                                type="search"
                            ></lightning-input>
                        </div>
                        <div class="slds-scrollable" style={treeGridCSS} >
                            <lightning-tree-grid
                            columns={gridColumns}
                            data={dataJson}
                            key-field="recordId"
                            aria-label="name"
                            onrowselection={updateSelectedRows}
                            selected-rows={selectedRows}
                            ontoggle={handleRowToggle}
                            min-column-width="200"
                            wrap-text="true"
                            ></lightning-tree-grid>
                        </div>
                    </div>
                </div>
            </div>
                <footer class="slds-popover__footer">
                    <div class="slds-grid slds-grid_vertical-align-center">
                        <button class="slds-button slds-button_neutral slds-col_bump-right" onclick={selectAllHierarchyTree}>Select all</button>
                        <button class="slds-button slds-button_neutral slds-col_bump-right" onclick={deselectAllHierarchyTree}>Deselect all</button>
                        <lightning-button variant="brand" label="Update" title="Update" onclick={updateFacets} ></lightning-button>
                    </div>
                </footer>

          </section>
    </template>
</template>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant