rodriggj / saleforce_dev

0 stars 0 forks source link

8. Filter Functionality Design #17

Closed rodriggj closed 2 years ago

rodriggj commented 2 years ago

Here we will implement some "functionality" for our carFilter component.

Here we will support 4 business requirements for our Filter Functionality.

You're Product or UI/UX department provide you with a Mock Up that looks something like this.

Plan of Attack

rodriggj commented 2 years ago

So this process will occur in 2 parts:

  1. STEP 1: We will create the HTML elements that implement the UI components in the Mockup
  2. STEP 2: We will create the back-end functionality that makes the UI components 1. Interact with the carTileList component and display the data that is filtered.

To execute these 2 Steps we will utilize 2 files within the carFilter component.

While executing Step 1, we are concentrating on adding HTML Tags, and Tag attributes that will later support the Data Binding, or Javascript Functionality.

We will keep the 2 steps separate as much as possible so if there is a JS portion of the HTML file, we will jump over to the carFilter.js file to add a "placeholder" and we will circle back to the HTML.

rodriggj commented 2 years ago

Step 1: Implement the UI Elements

Keyword Search Functionality

  1. Start by implementing an lightning-card element which will act as the container. Within the container add a lightning-input element that will received the users Search Word. Use attributes in the lighting-input element to provide a label name, specify the type of input (Search), and a javascript placeholder that will manage the "onChange" behavior that occurs when a user inputs a Search Word.

carFilter.html

<template>
    <lightning-card title="Filters" icon-name="standard:calibration">
        <div class="slds-m-horizontal_medium">
            <!-- Search Key Input -->
            <lightning-input 
                label="Search Key" 
                type="search" 
                value={}
                onchange={handleSearchKeyChange}>
            </lightning-input>
        </div>
    </lightning-card>
</template>

carFilter.js

import { LightningElement } from 'lwc';

export default class CarFilter extends LightningElement {

    handleSearchKeyChange(){
        // Logic for search function to be written here
    }
}

rodriggj commented 2 years ago

Slider Bar Functionality

  1. Start by implementing an lightning-card element which will act as the container. Within the container add a lightning-input element that will received the users Search Word. Use attributes in the lighting-input element to provide a label name, specify the type of input (Search), and a javascript placeholder that will manage the "onChange" behavior that occurs when a user inputs a Search Word.

carFilter.html

<template>
    <lightning-card title="Filters" icon-name="standard:calibration">
        <div class="slds-m-horizontal_medium">
            <!-- Search Key Input -->
            <lightning-input 
                label="Search Key" 
                type="search" 
                value={}
                onchange={handleSearchKeyChange}>
            </lightning-input>
        </div>

        <!-- Slider Section -->
        <section>
            <lightning-slider
                label="Max Price"
                step="100"
                min="0"
                max="99999"
                value={}
                onchange={onMaxPriceChange}>

            </lightning-slider>
        </section>
    </lightning-card>
</template>

carFilter.js

import { LightningElement } from 'lwc';

export default class CarFilter extends LightningElement {

    handleSearchKeyChange(){
        // Logic for search function to be written here
    }

    onMaxPriceChange(){

    }
}

  1. Now we need to implement Data Binding between the HTML and the JS.

    • [ ] To do this we will create attributes to our carFilter class. Here we create an Object called filters which will contain the attributes we want to maintain State in our application.
    • [ ] And we will utilize our HTML Template utilizing the {} (Interpolation Syntax) to receive the State of our attributes and pass it to the front-end HTML. Javascript will maintain this state with the class properties and update as needed.
  2. The slider bar needs to maintain 2 pieces of data as the user interacts with this filter. These are the SearchKey, and the MaxPrice. Because these will change with the user interaction and because they are used by Javascript to query the right tiles we need to retain this input. This is referred to as State. The state of these 2 variables will change, and be used by Javascript to execute its behavior. To ensure that the HTML input can be shared with Javascript file, we need to create a place for the state to be stored. To do this we create a class property, called filters. This property is an Object, which has 2 attributes, searchKey and maxPrice. We will initiate searchKey with a null value and maxPrice with the Max Price in filter range. We will finally call the filter Object using dot notation, through a process called data binding between the HTML and JS files. The syntax for data binding is the curly braces { }.

carFilter.html

<template>
    <lightning-card title="Filters" icon-name="standard:calibration">
        <div class="slds-m-horizontal_medium">
            <!-- Search Key Input -->
            <lightning-input 
                label="Search Key" 
                type="search" 
                value={filters.searchKey}
                onchange={handleSearchKeyChange}>
            </lightning-input>

                    <!-- Slider Section -->
              <section>
                  <lightning-slider
                      label="Max Price"
                      step="100"
                      min="0"
                      max="99999"
                      value={filters.maxPrice}
                      onchange={onMaxPriceChange}>

                  </lightning-slider>
              </section>
        </div>
    </lightning-card>
</template>

carFilter.js

import { LightningElement } from 'lwc';

export default class CarFilter extends LightningElement {
    filters={
        searchKey: '',
        maxPrice: 99999
    }

    handleSearchKeyChange(){
        // Logic for search function to be written here
    }

    onMaxPriceChange(){

    }
}

  1. Right-click on the carFilter parent folder and Deploy to Source Org

rodriggj commented 2 years ago

Picklist Functionality

We have 2 Picklist selections we are placing in the Filter Component - these are Category & Make Type. Both have effectively the same coding construct, so we will simply explain the Category Picklist and then show the details for the Make Type.

  1. Because the user will be making a selection of checkboxes for this Category filter, you will need to collect thier input - so we start by creating a new <section> and adding a lightning Input component with identifying attributes. We also add our onchange attribute so we can assign behavior in our javascript file for when a box is checked.

carFilter.html

            <!-- Category Checkboxes -->
            <section>
                <h2>Categories</h2>
                <lightning-input 
                    type="checkbox"
                    name="category"
                    checked
                    onchange={handleCheckbox}/>
                </lightning-input>
            </section>

carFilter.js

    handleCheckbox(){

    }

  1. Now we need to get the picklist values that we assigned to the Category__c field when we created our Cars__c Object. Recall that we're trying to bring back a picklist for the Category__c field that contains 4 selection items.

To do this we need to import some functionality from Salesforce, specifically the getObjectInfo and getPicklistValues API from the Lightning Data Service (LDS), which is packaged in a module called lightning/uiObjectInfoApi. We will also be using the @wire() service to fetch this data from the LDS. We will use the @wire service to first fetch the Car__c Object Info, then we will again use the @wire() service on the retrieved Car__c Object to retrieve the Category__c info.

NOTE: the Lightning Data Service and the methods used to retrieve data from the LDS is another major difference between React, Vue, Angular, etc. Development that requires specialized tooling to utilize. (Elaborate more later)

carFilter.js

import { LightningElement, wire } from 'lwc';
import { getObjectInfo, getPicklistValues } from 'lightning/uiObjectInfoApi';

// Importing Car__c and associated fields via @salesforce/schema API
import CAR_OBJECT from '@salesforce/schema/Car__c'
import CATEGORY_FIELD from '@saleforce/schema/Car__c.Category__c'

export default class CarFilter extends LightningElement {
    filters={
        searchKey: '',
        maxPrice: 99999
    }

    // fetching Car__c Object Info
    @wire(getObjectInfo, {objectApiName: CAR_OBJECT})
    carObjectInfo

    // fetching Category__c referencing the property carObjectInfo (Car__c)
    @wire(getPicklistValues, { 
        recordTypeId: '$carObjectInfo.data.defaultRecordTypeId',
        fieldApiName: CATEGORY_FIELD
    }) categories

    handleSearchKeyChange(){
        // Logic for search function to be written here
    }

    onMaxPriceChange(){

    }

    handleCheckbox(){

    }
}

  1. Now that we should have the Object data in the categories property that we assigned using the wire Service. We can now go back to the carFilter.html file and use the picklist values. We will have to use the <template> tag to execute some iteration to list all 4 values in the pick list.
            <!-- Category Checkboxes -->
            <section>
                <h2>Categories</h2>
                <template if:true={categories.data}>
                    <template for:each={categories.data.values} for:item="category">
                        <lightning-input 
                            key={category.value}
                            label={category.label}
                            type="checkbox"
                            name="category"
                            checked
                            onchange={handleCheckbox}/>
                        </lightning-input>
                    </template>
                </template>
                <template if:true={categories.error}>
                    <div>Error loading categories</div>
                </template>
            </section>

  1. Save the files, right-click on the carFilter folder and Deploy Source to Org. Refresh or Open your DevOrg and you should see something like this

  1. Repeat the same process and make relevant changes to introduce the 2nd Picklist Filter Make Type. Your code should look like this.

carFilter.html

            <!-- Make Type Checkboxes -->
            <section>
                <h2>Make Type</h2>
                <template if:true={makeTypes.data}>
                    <template for:each={makeTypes.data.values} for:item="make">
                        <lightning-input 
                            key={make.value}
                            label={make.label}
                            type="checkbox"
                            name="makeType"
                            checked
                            onchange={handleCheckbox}/>
                        </lightning-input>
                    </template>
                </template>
                <template if:true={makeTypes.error}>
                    <div>Error loading categories</div>
                </template>
            </section>

carFilter.js

// ...
import MAKE_FIELD from '@salesforce/schema/Car__c.Make__c'
// ...
    // fetching Make__c referencing the property carObjectInfo (Make__c)
    @wire(getPicklistValues, { 
        recordTypeId: '$carObjectInfo.data.defaultRecordTypeId',
        fieldApiName: MAKE_FIELD
    })makeTypes

  1. Right-click on carFilter folder and Deploy Source to Org.

rodriggj commented 2 years ago

We may want to test to see if the filters we put in place do respond to front-end interaction. To do this we can print something to the console when any of the filters are engaged. This will invole writing a console.log() statement in our Javascript - handler functions that we've left empty so far. This isn't the completion of STEP 2 ; that logic will be much more specific. This is simply a test to see that the interaction between the front end components we built will receive input and react to it.

Search Key Filter

carFilter.js file

// Handle Search Key
    handleSearchKeyChange(event){
        console.log(event.target.value)
        this.filters = {...this.filters, "searchKey": event.target.value}
    }

Slider Filter

// Handle Slider Price Change
    onMaxPriceChange(event){
        console.log(event.target.value)
        this.filters = {...this.filters, "maxPrice": event.target.value}
    }

Checkbox Filters

 <!-- Category Checkboxes -->
            <section>
                <h2>Categories</h2>
                <template if:true={categories.data}>
                    <template for:each={categories.data.values} for:item="category">
                        <lightning-input 
                            key={category.value}
                            label={category.label}
                            type="checkbox"
                            data-name="category"
                            data-value={category.value}
                            checked
                            onchange={handleCheckbox}/>
                        </lightning-input>
                    </template>
...
            </section>

            <!-- Make Type Checkboxes -->
            <section>
                <h2>Make Type</h2>
                <template if:true={makeTypes.data}>
                    <template for:each={makeTypes.data.values} for:item="make">
                        <lightning-input 
                            key={make.value}
                            label={make.label}
                            type="checkbox"
                            data-name="makeType"
                            data-value={make.value}
                            checked
                            onchange={handleCheckbox}/>
                        </lightning-input>
...
            </section>
    handleCheckbox(event){
        const {name, value} = event.target.dataset
        console.log("name", name)
        console.log("value", value)
    }

rodriggj commented 2 years ago

Relevance to Test Automation

  1. We saw the interaction between our Javascript (Behavior/Logic) and the Front-End UI (Templates or HTML)

    • We saw this interaction utilizes <template> tags which are unique to Salesforce
    • We saw that <template> tags have an unique syntax for handling logic like Conditionals, Iteration, and Data Binding and require testing at Unit and System level
    • We saw that there are multiple additional Element identifiers added to our HTML that we did not write once deployed to Salesforce. These Elements need to be identified and handled as they are the basis for Test Automation Locators in the DOM.
  2. We saw the interaction in our Javascript to the Lighting Data Service (LDS)

    • We saw the Salesforce specific APIs and the @wire() adapter service and how these are unique to Salesforce
    • We saw that there is a process of identifying Objects and Object Attributes (fields) that require import statements, property identification and a Salesforce unique syntax to utilize.