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:
- Lists Accounts in a responsive grid (Lesson 4) — one column on mobile, three on desktop.
- Lets the user click an Account to navigate to its record page (Lessons 1-2).
- Offers a "Delete" action per Account, confirmed via a modal (Lesson 3) before proceeding.
- 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.
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.
Project: A Mobile-Responsive Salesforce Workspace Quiz
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.