mirror of
https://github.com/forcedotcom/afv-library.git
synced 2026-08-08 16:25:58 +08:00
Merge d832b270af into fbf1c040c9
This commit is contained in:
commit
e57cbefbfe
@ -186,7 +186,8 @@ Local Dev commands install just-in-time on first run. They are long-running proc
|
||||
- [assets/jest-test/componentName.test.js.example](assets/jest-test/componentName.test.js.example) — Jest test template (copy and rename, remove `.example` suffix)
|
||||
- [assets/message-channel/lmsPublisher.js](assets/message-channel/lmsPublisher.js) — LMS publisher pattern
|
||||
- [assets/message-channel/lmsSubscriber.js](assets/message-channel/lmsSubscriber.js) — LMS subscriber pattern
|
||||
- [assets/modal-component/modalComponent.js](assets/modal-component/modalComponent.js) — modal with focus trap and ESC handling
|
||||
- [assets/native-modal-component/nativeModalComponent.js](assets/native-modal-component/nativeModalComponent.js) — default modal pattern using the native `lightning/modal` service
|
||||
- [assets/modal-component/modalComponent.js](assets/modal-component/modalComponent.js) — custom composable modal (focus trap, ESC handling); only for cases `lightning/modal` can't cover
|
||||
- [assets/record-picker/recordPicker.js](assets/record-picker/recordPicker.js) — record picker with search
|
||||
- [assets/state-store/store.js](assets/state-store/store.js) — reactive state store for cross-component state
|
||||
- [assets/typescript-component/typescriptComponent.ts](assets/typescript-component/typescriptComponent.ts) — TypeScript-enabled component (Spring '26)
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<lightning-modal-header label={label}></lightning-modal-header>
|
||||
|
||||
<lightning-modal-body>
|
||||
<p>{content}</p>
|
||||
<slot></slot>
|
||||
</lightning-modal-body>
|
||||
|
||||
<lightning-modal-footer>
|
||||
<lightning-button
|
||||
label="Cancel"
|
||||
onclick={handleCancel}
|
||||
class="slds-m-right_x-small">
|
||||
</lightning-button>
|
||||
<lightning-button
|
||||
variant="brand"
|
||||
label="Save"
|
||||
onclick={handleSave}>
|
||||
</lightning-button>
|
||||
</lightning-modal-footer>
|
||||
</template>
|
||||
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* NATIVE MODAL TEMPLATE (lightning/modal)
|
||||
*
|
||||
* Uses the platform's built-in modal service instead of a hand-rolled
|
||||
* backdrop/focus-trap implementation. LightningModal already provides:
|
||||
* - focus trap and initial focus
|
||||
* - ESC-to-close
|
||||
* - ARIA roles/labels
|
||||
* - size variants (small / medium / large / full)
|
||||
* - a promise-based result returned from open()
|
||||
*
|
||||
* Supported in Lightning Experience, the Salesforce app, and Experience
|
||||
* Builder sites (confirm current support for your specific site template
|
||||
* before relying on it as the only implementation).
|
||||
*
|
||||
* Replace: nativeModalComponent → yourModalName
|
||||
* Replace: NativeModalComponent → YourModalName
|
||||
*
|
||||
* ── How a parent opens this modal ──────────────────────────────────────
|
||||
* import YourModalName from 'c/yourModalName';
|
||||
*
|
||||
* async handleOpenModal() {
|
||||
* const result = await YourModalName.open({
|
||||
* size: 'small', // small | medium | large | full
|
||||
* description: 'Accessible description of the modal purpose',
|
||||
* label: 'Confirm Action', // passed through as @api label
|
||||
* content: 'Are you sure?' // any custom @api input props
|
||||
* });
|
||||
*
|
||||
* if (result === 'save') {
|
||||
* // user confirmed
|
||||
* }
|
||||
* }
|
||||
* ────────────────────────────────────────────────────────────────────────
|
||||
*/
|
||||
import { api } from 'lwc';
|
||||
import LightningModal from 'lightning/modal';
|
||||
|
||||
export default class NativeModalComponent extends LightningModal {
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// PUBLIC API (@api) - Passed in via YourModalName.open({ ... })
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
@api label;
|
||||
@api content;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// EVENT HANDLERS
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
handleCancel() {
|
||||
// Resolve the promise returned by open() with a falsy/known value
|
||||
this.close('cancel');
|
||||
}
|
||||
|
||||
handleSave() {
|
||||
// Resolve the promise returned by open() with the result the
|
||||
// caller needs. Close is explicit, so validation can prevent it:
|
||||
// guard this call behind your own validity check before closing.
|
||||
this.close('save');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
NATIVE MODAL TEMPLATE - METADATA
|
||||
|
||||
Modals that extend lightning/modal are opened imperatively via
|
||||
YourModalName.open({ ... }) from another component's JS — they are
|
||||
not placed declaratively on a page, so isExposed stays false and no
|
||||
targets are declared.
|
||||
-->
|
||||
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||
<apiVersion>66.0</apiVersion>
|
||||
<isExposed>false</isExposed>
|
||||
</LightningComponentBundle>
|
||||
@ -612,7 +612,9 @@ const BATCH_CREATE = gql`
|
||||
|
||||
## Modal Component Pattern
|
||||
|
||||
Based on [James Simone's composable modal pattern](https://www.jamessimone.net/blog/joys-of-apex/lwc-composable-modal/).
|
||||
**Default to the native `lightning/modal` service** — see [assets/native-modal-component](../assets/native-modal-component/nativeModalComponent.js). It ships focus trap, ESC-to-close, ARIA, and size variants out of the box, so there's nothing to reinvent. Confirm it's supported on the target surface (some Experience Builder site templates historically lagged Lightning Experience support) before falling back to a custom implementation.
|
||||
|
||||
Only reach for a custom composable modal below when `lightning/modal` genuinely can't cover the case (e.g. a confirmed gap on your site template, or composition needs beyond what `LightningModal` slots support). It's based on [James Simone's composable modal pattern](https://www.jamessimone.net/blog/joys-of-apex/lwc-composable-modal/); see [assets/modal-component](../assets/modal-component/modalComponent.js) for the fuller version with cleanup and multi-slot composition. Prefer a `save`/`cancel` `CustomEvent` over a function-reference `@api` prop for the save handler — function references can't be passed from Aura markup, which breaks on Aura-based Experience Builder pages.
|
||||
|
||||
```javascript
|
||||
// composableModal.js
|
||||
|
||||
Loading…
Reference in New Issue
Block a user