Advanced 30 min read

Project: A Mobile-Responsive Salesforce Workspace

By the end of this lesson, you'll be able to:

  • Combine NavigationMixin, modals, and toasts into one cohesive workspace component
  • Apply a responsive SLDS grid layout that adapts from mobile to desktop
  • Apply accessible labeling and focus management throughout

Prerequisites: "Responsive Components and Accessibility"

What We're Building

An accountWorkspace component that:

  1. Lists Accounts in a responsive grid (Lesson 4) — one column on mobile, three on desktop.
  2. Lets the user click an Account to navigate to its record page (Lessons 1-2).
  3. Offers a "Delete" action per Account, confirmed via a modal (Lesson 3) before proceeding.
  4. Shows a toast on success (Module 8) and manages focus sensibly after the modal closes (Lesson 4).

The Component

import { LightningElement, wire } from 'lwc';
import { NavigationMixin } from 'lightning/navigation';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { deleteRecord } from 'lightning/uiRecordApi';
import { refreshApex } from '@salesforce/apex';
import getAccounts from '@salesforce/apex/AccountSearchController.search';
import ConfirmDialog from 'c/confirmDialog';

export default class AccountWorkspace extends NavigationMixin(LightningElement) {
    wiredAccounts;

    @wire(getAccounts, { searchTerm: '' })
    wiredResult(result) {
        this.wiredAccounts = result;
    }

    get accounts() {
        return this.wiredAccounts?.data ?? [];
    }

    handleViewAccount(event) {
        const accountId = event.currentTarget.dataset.id;
        this[NavigationMixin.Navigate]({
            type: 'standard__recordPage',
            attributes: { recordId: accountId, objectApiName: 'Account', actionName: 'view' },
        });
    }

    async handleDeleteAccount(event) {
        const accountId = event.currentTarget.dataset.id;
        const result = await ConfirmDialog.open({
            size: 'small',
            message: 'Are you sure you want to delete this account?',
        });

        if (result !== 'confirm') {
            return; // focus naturally returns to the triggering button
        }

        try {
            await deleteRecord(accountId);
            await refreshApex(this.wiredAccounts);
            this.dispatchEvent(new ShowToastEvent({
                title: 'Account Deleted',
                variant: 'success',
            }));
        } catch (error) {
            this.dispatchEvent(new ShowToastEvent({
                title: 'Could Not Delete Account',
                message: error.body?.message ?? 'An unexpected error occurred.',
                variant: 'error',
            }));
        }
    }
}
<div class="slds-grid slds-wrap slds-gutters">
    <template for:each={accounts} for:item="account">
        <div key={account.accountId} class="slds-col slds-size_1-of-1 slds-medium-size_1-of-2 slds-large-size_1-of-3">
            <lightning-button
                label={account.accountName}
                data-id={account.accountId}
                onclick={handleViewAccount}>
            </lightning-button>
            <lightning-button
                label="Delete"
                variant="destructive"
                data-id={account.accountId}
                onclick={handleDeleteAccount}>
            </lightning-button>
        </div>
    </template>
</div>

Every technique from this module appears together: NavigationMixin (Lessons 1-2) for viewing a record, ConfirmDialog.open() (Lesson 3) before a destructive action, refreshApex (Module 6) plus a toast on success, and a responsive slds-grid (Lesson 4) that reflows from one column on mobile to three on desktop.

Exercise

Add data-id={account.accountId} handling awareness: explain, as a comment, why event.currentTarget.dataset.id (not event.target) is the reliable way to read which account's button was clicked.

Show hint

Think about what currentTarget vs. target represent when a button contains nested elements.

JAVASCRIPT

Exercise

Challenge: explain, as a comment, why handleDeleteAccount returns early (without deleting anything) if the modal result is not "confirm".

Show hint

Think about what other values close(result) could produce.

JAVASCRIPT

Project: A Mobile-Responsive Salesforce Workspace Quiz

1. What happens if the confirm dialog's result is not "confirm"?

2. How many columns does the account grid use on small mobile screens?

3. What refreshes the account list after a successful delete?

4. What does clicking an Account's name button do in this component?

5. Why is event.currentTarget.dataset.id used instead of event.target.dataset.id?

Log in to submit the quiz and save your score.

My Notes

Log in to keep private notes on this lesson.

Questions about this lesson

No questions yet — be the first to ask.

Log in to ask a question about this lesson.

Summary

A hands-on capstone assembling this module's navigation, feedback, and responsive/accessible layout techniques into a realistic, mobile-friendly Salesforce workspace.