@salesforce/afv-skills 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/activating-datacloud/CREDITS.md +5 -0
- package/skills/activating-datacloud/README.md +39 -0
- package/skills/activating-datacloud/SKILL.md +118 -0
- package/skills/analyzing-omnistudio-dependencies/CREDITS.md +5 -0
- package/skills/analyzing-omnistudio-dependencies/SKILL.md +477 -0
- package/skills/analyzing-omnistudio-dependencies/references/dependency-patterns.md +508 -0
- package/skills/analyzing-omnistudio-dependencies/references/namespace-guide.md +300 -0
- package/skills/building-omnistudio-callable-apex/CREDITS.md +9 -0
- package/skills/building-omnistudio-callable-apex/README.md +80 -0
- package/skills/building-omnistudio-callable-apex/SKILL.md +276 -0
- package/skills/building-omnistudio-callable-apex/assets/pattern_callable_openinterface.cls +40 -0
- package/skills/building-omnistudio-callable-apex/assets/pattern_callable_vanilla.cls +32 -0
- package/skills/building-omnistudio-callable-apex/assets/pattern_migration.cls +54 -0
- package/skills/building-omnistudio-callable-apex/assets/pattern_openinterface.cls +45 -0
- package/skills/building-omnistudio-callable-apex/assets/pattern_test_class.cls +65 -0
- package/skills/building-omnistudio-callable-apex/examples/Test_QuoteByProductCallable/IndustriesCallableException.cls +7 -0
- package/skills/building-omnistudio-callable-apex/examples/Test_QuoteByProductCallable/Industries_QuoteByProductCallable.cls +115 -0
- package/skills/building-omnistudio-callable-apex/examples/Test_QuoteByProductCallable/Industries_QuoteByProductCallableTest.cls +189 -0
- package/skills/building-omnistudio-callable-apex/examples/Test_QuoteByProductCallable/TRANSCRIPT.md +115 -0
- package/skills/building-omnistudio-callable-apex/examples/Test_VlocityOpenInterface2Conversion/IndustriesCallableException.cls +7 -0
- package/skills/building-omnistudio-callable-apex/examples/Test_VlocityOpenInterface2Conversion/MyCustomCallable.cls +74 -0
- package/skills/building-omnistudio-callable-apex/examples/Test_VlocityOpenInterface2Conversion/MyCustomCallableTest.cls +146 -0
- package/skills/building-omnistudio-callable-apex/examples/Test_VlocityOpenInterface2Conversion/MyCustomRemoteClass.cls +16 -0
- package/skills/building-omnistudio-callable-apex/examples/Test_VlocityOpenInterface2Conversion/TRANSCRIPT.md +120 -0
- package/skills/building-omnistudio-callable-apex/examples/Test_VlocityOpenInterfaceConversion/IndustriesCallableException.cls +7 -0
- package/skills/building-omnistudio-callable-apex/examples/Test_VlocityOpenInterfaceConversion/MyCustomCallable.cls +73 -0
- package/skills/building-omnistudio-callable-apex/examples/Test_VlocityOpenInterfaceConversion/MyCustomCallableTest.cls +128 -0
- package/skills/building-omnistudio-callable-apex/examples/Test_VlocityOpenInterfaceConversion/MyCustomVlocityOpenInterface2.cls +23 -0
- package/skills/building-omnistudio-callable-apex/examples/Test_VlocityOpenInterfaceConversion/TRANSCRIPT.md +75 -0
- package/skills/building-omnistudio-datamapper/CREDITS.md +5 -0
- package/skills/building-omnistudio-datamapper/SKILL.md +270 -0
- package/skills/building-omnistudio-datamapper/assets/completion-summary-template.md +28 -0
- package/skills/building-omnistudio-datamapper/assets/omni-data-transform-extract.json +6 -0
- package/skills/building-omnistudio-datamapper/assets/omni-data-transform-item.json +12 -0
- package/skills/building-omnistudio-datamapper/assets/omni-data-transform-load.json +6 -0
- package/skills/building-omnistudio-datamapper/assets/omni-data-transform-transform.json +6 -0
- package/skills/building-omnistudio-datamapper/references/best-practices.md +277 -0
- package/skills/building-omnistudio-datamapper/references/naming-conventions.md +145 -0
- package/skills/building-omnistudio-flexcard/CREDITS.md +5 -0
- package/skills/building-omnistudio-flexcard/SKILL.md +325 -0
- package/skills/building-omnistudio-flexcard/assets/omni-ui-card.json +10 -0
- package/skills/building-omnistudio-flexcard/references/best-practices.md +291 -0
- package/skills/building-omnistudio-flexcard/references/data-binding-guide.md +311 -0
- package/skills/building-omnistudio-flexcard/references/scoring-rubric.md +66 -0
- package/skills/building-omnistudio-flexcard/scripts/flexcard-commands.sh +24 -0
- package/skills/building-omnistudio-integration-procedure/CREDITS.md +5 -0
- package/skills/building-omnistudio-integration-procedure/SKILL.md +275 -0
- package/skills/building-omnistudio-integration-procedure/assets/omni-process-element-dr-extract.json +10 -0
- package/skills/building-omnistudio-integration-procedure/assets/omni-process-element-set-values.json +10 -0
- package/skills/building-omnistudio-integration-procedure/assets/omni-process-ip.json +12 -0
- package/skills/building-omnistudio-integration-procedure/assets/scoring-report-format.txt +14 -0
- package/skills/building-omnistudio-integration-procedure/references/best-practices.md +388 -0
- package/skills/building-omnistudio-integration-procedure/references/element-types.md +588 -0
- package/skills/building-omnistudio-integration-procedure/scripts/cli-commands.sh +18 -0
- package/skills/building-omnistudio-omniscript/CREDITS.md +5 -0
- package/skills/building-omnistudio-omniscript/SKILL.md +367 -0
- package/skills/building-omnistudio-omniscript/assets/omni-process-element-step.json +10 -0
- package/skills/building-omnistudio-omniscript/assets/omni-process-element-text-block.json +11 -0
- package/skills/building-omnistudio-omniscript/assets/omni-process-omniscript.json +12 -0
- package/skills/building-omnistudio-omniscript/references/best-practices.md +480 -0
- package/skills/building-omnistudio-omniscript/references/element-types.md +1172 -0
- package/skills/building-omnistudio-omniscript/scripts/check-duplicate-omniscript.sh +13 -0
- package/skills/building-omnistudio-omniscript/scripts/cli-reference.sh +21 -0
- package/skills/building-omnistudio-omniscript/scripts/deploy-omniscript.sh +29 -0
- package/skills/building-sf-integrations/CREDITS.md +5 -0
- package/skills/building-sf-integrations/README.md +95 -0
- package/skills/building-sf-integrations/SKILL.md +192 -0
- package/skills/building-sf-integrations/assets/callouts/callout-retry-handler.cls +167 -0
- package/skills/building-sf-integrations/assets/callouts/http-response-handler.cls +257 -0
- package/skills/building-sf-integrations/assets/callouts/rest-queueable-callout.cls +262 -0
- package/skills/building-sf-integrations/assets/callouts/rest-sync-callout.cls +211 -0
- package/skills/building-sf-integrations/assets/cdc/cdc-handler.cls +246 -0
- package/skills/building-sf-integrations/assets/cdc/cdc-subscriber-trigger.trigger +139 -0
- package/skills/building-sf-integrations/assets/endpoint-security/example.cspTrustedSite-meta.xml +58 -0
- package/skills/building-sf-integrations/assets/endpoint-security/example.remoteSite-meta.xml +39 -0
- package/skills/building-sf-integrations/assets/external-credentials/jwt-external-credential.externalCredential-meta.xml +90 -0
- package/skills/building-sf-integrations/assets/external-credentials/oauth-external-credential.externalCredential-meta.xml +87 -0
- package/skills/building-sf-integrations/assets/external-services/external-service-operations.md +221 -0
- package/skills/building-sf-integrations/assets/external-services/openapi-registration.externalServiceRegistration-meta.xml +193 -0
- package/skills/building-sf-integrations/assets/named-credentials/certificate-auth.namedCredential-meta.xml +62 -0
- package/skills/building-sf-integrations/assets/named-credentials/custom-auth.namedCredential-meta.xml +71 -0
- package/skills/building-sf-integrations/assets/named-credentials/oauth-client-credentials.namedCredential-meta.xml +51 -0
- package/skills/building-sf-integrations/assets/named-credentials/oauth-jwt-bearer.namedCredential-meta.xml +67 -0
- package/skills/building-sf-integrations/assets/platform-events/event-publisher.cls +191 -0
- package/skills/building-sf-integrations/assets/platform-events/event-subscriber-action.cls +295 -0
- package/skills/building-sf-integrations/assets/platform-events/event-subscriber-trigger.trigger +108 -0
- package/skills/building-sf-integrations/assets/platform-events/platform-event-definition.object-meta.xml +124 -0
- package/skills/building-sf-integrations/assets/soap/soap-callout-service.cls +186 -0
- package/skills/building-sf-integrations/assets/soap/wsdl2apex-guide.md +213 -0
- package/skills/building-sf-integrations/hooks/scripts/suggest_credential_setup.py +271 -0
- package/skills/building-sf-integrations/hooks/scripts/validate_integration.py +363 -0
- package/skills/building-sf-integrations/references/callout-patterns.md +719 -0
- package/skills/building-sf-integrations/references/cdc-guide.md +288 -0
- package/skills/building-sf-integrations/references/cli-reference.md +94 -0
- package/skills/building-sf-integrations/references/event-driven-architecture-guide.md +266 -0
- package/skills/building-sf-integrations/references/event-patterns.md +838 -0
- package/skills/building-sf-integrations/references/external-services-guide.md +303 -0
- package/skills/building-sf-integrations/references/messaging-api-v2.md +609 -0
- package/skills/building-sf-integrations/references/named-credentials-automation.md +201 -0
- package/skills/building-sf-integrations/references/named-credentials-guide.md +173 -0
- package/skills/building-sf-integrations/references/platform-events-guide.md +288 -0
- package/skills/building-sf-integrations/references/rest-callout-patterns.md +288 -0
- package/skills/building-sf-integrations/references/scoring-rubric.md +59 -0
- package/skills/building-sf-integrations/references/security-best-practices.md +248 -0
- package/skills/building-sf-integrations/scripts/README.md +100 -0
- package/skills/building-sf-integrations/scripts/configure-named-credential.sh +236 -0
- package/skills/building-sf-integrations/scripts/set-api-credential.sh +146 -0
- package/skills/building-sf-integrations/scripts/templates/setup-credentials-with-csp.sh +158 -0
- package/skills/configuring-connected-apps/CREDITS.md +3 -0
- package/skills/configuring-connected-apps/README.md +99 -0
- package/skills/configuring-connected-apps/SKILL.md +224 -0
- package/skills/configuring-connected-apps/assets/connected-app-basic.xml +29 -0
- package/skills/configuring-connected-apps/assets/connected-app-canvas.xml +62 -0
- package/skills/configuring-connected-apps/assets/connected-app-jwt.xml +49 -0
- package/skills/configuring-connected-apps/assets/connected-app-oauth.xml +65 -0
- package/skills/configuring-connected-apps/assets/eca-global-oauth.xml +36 -0
- package/skills/configuring-connected-apps/assets/eca-oauth-settings.xml +36 -0
- package/skills/configuring-connected-apps/assets/eca-policies.xml +36 -0
- package/skills/configuring-connected-apps/assets/external-client-app.xml +35 -0
- package/skills/configuring-connected-apps/references/example-usage.md +256 -0
- package/skills/configuring-connected-apps/references/migration-guide.md +328 -0
- package/skills/configuring-connected-apps/references/oauth-flows-reference.md +660 -0
- package/skills/configuring-connected-apps/references/security-checklist.md +209 -0
- package/skills/configuring-connected-apps/references/testing-validation-guide.md +275 -0
- package/skills/connecting-datacloud/CREDITS.md +5 -0
- package/skills/connecting-datacloud/README.md +59 -0
- package/skills/connecting-datacloud/SKILL.md +155 -0
- package/skills/connecting-datacloud/examples/connections/heroku-postgres.json +15 -0
- package/skills/connecting-datacloud/examples/connections/ingest-api-connection.json +5 -0
- package/skills/connecting-datacloud/examples/connections/ingest-api-schema.json +31 -0
- package/skills/connecting-datacloud/examples/connections/redshift.json +16 -0
- package/skills/connecting-datacloud/examples/connections/sharepoint-unstructured.json +20 -0
- package/skills/connecting-datacloud/examples/connections/snowflake-connection.json +42 -0
- package/skills/debugging-apex-logs/CREDITS.md +22 -0
- package/skills/debugging-apex-logs/README.md +74 -0
- package/skills/debugging-apex-logs/SKILL.md +172 -0
- package/skills/debugging-apex-logs/assets/benchmarking-template.cls +327 -0
- package/skills/debugging-apex-logs/assets/cpu-heap-optimization.cls +307 -0
- package/skills/debugging-apex-logs/assets/dml-in-loop-fix.cls +219 -0
- package/skills/debugging-apex-logs/assets/null-pointer-fix.cls +252 -0
- package/skills/debugging-apex-logs/assets/soql-in-loop-fix.cls +157 -0
- package/skills/debugging-apex-logs/references/analysis-playbook.md +53 -0
- package/skills/debugging-apex-logs/references/benchmarking-guide.md +287 -0
- package/skills/debugging-apex-logs/references/cli-commands.md +368 -0
- package/skills/debugging-apex-logs/references/common-issues.md +68 -0
- package/skills/debugging-apex-logs/references/debug-log-reference.md +328 -0
- package/skills/debugging-apex-logs/references/log-analysis-tools.md +248 -0
- package/skills/debugging-apex-logs/references/scoring-rubric.md +21 -0
- package/skills/deploying-metadata/CREDITS.md +25 -0
- package/skills/deploying-metadata/README.md +104 -0
- package/skills/deploying-metadata/SKILL.md +214 -0
- package/skills/deploying-metadata/assets/destructiveChanges.xml +143 -0
- package/skills/deploying-metadata/assets/package.xml +121 -0
- package/skills/deploying-metadata/references/agent-deployment-guide.md +628 -0
- package/skills/deploying-metadata/references/deploy.sh +73 -0
- package/skills/deploying-metadata/references/deployment-report-template.md +89 -0
- package/skills/deploying-metadata/references/deployment-workflows.md +395 -0
- package/skills/deploying-metadata/references/orchestration.md +183 -0
- package/skills/deploying-metadata/references/trigger-deployment-safety.md +376 -0
- package/skills/deploying-omnistudio-datapacks/CREDITS.md +5 -0
- package/skills/deploying-omnistudio-datapacks/README.md +88 -0
- package/skills/deploying-omnistudio-datapacks/SKILL.md +174 -0
- package/skills/deploying-omnistudio-datapacks/examples/business-internet-plus-bundle/TRANSCRIPT.md +124 -0
- package/skills/deploying-omnistudio-datapacks/examples/business-internet-plus-bundle/deploy-business-internet-plus-bundle.yaml +11 -0
- package/skills/deploying-omnistudio-datapacks/examples/business-internet-plus-bundle-deploy/TRANSCRIPT.md +142 -0
- package/skills/deploying-omnistudio-datapacks/examples/business-internet-plus-bundle-deploy/deploy-business-internet-plus-bundle.yaml +10 -0
- package/skills/deploying-omnistudio-datapacks/references/job-file-template.md +42 -0
- package/skills/deploying-omnistudio-datapacks/references/troubleshooting-matrix.md +24 -0
- package/skills/developing-agentforce/assets/metadata/http-callout-flow.flow-meta.xml +1 -1
- package/skills/developing-agentforce/references/actions-reference.md +8 -8
- package/skills/fetching-salesforce-docs/README.md +66 -0
- package/skills/fetching-salesforce-docs/SKILL.md +209 -0
- package/skills/fetching-salesforce-docs/requirements.txt +2 -0
- package/skills/fetching-salesforce-docs/scripts/extract_help_salesforce.py +497 -0
- package/skills/fetching-salesforce-docs/scripts/extract_salesforce_doc.py +357 -0
- package/skills/fetching-salesforce-docs/scripts/runtime_bootstrap.py +58 -0
- package/skills/generating-apex/CREDITS.md +1 -26
- package/skills/generating-apex-test/CREDITS.md +2 -27
- package/skills/generating-lwc-components/CREDITS.md +5 -0
- package/skills/generating-lwc-components/README.md +126 -0
- package/skills/generating-lwc-components/SKILL.md +191 -0
- package/skills/generating-lwc-components/assets/apex-controller/LwcController.cls +327 -0
- package/skills/generating-lwc-components/assets/basic-component/basicComponent.css +72 -0
- package/skills/generating-lwc-components/assets/basic-component/basicComponent.html +111 -0
- package/skills/generating-lwc-components/assets/basic-component/basicComponent.js +163 -0
- package/skills/generating-lwc-components/assets/basic-component/basicComponent.js-meta.xml +137 -0
- package/skills/generating-lwc-components/assets/datatable-component/datatableComponent.html +111 -0
- package/skills/generating-lwc-components/assets/datatable-component/datatableComponent.js +367 -0
- package/skills/generating-lwc-components/assets/flow-screen-component/flowScreenComponent.css +63 -0
- package/skills/generating-lwc-components/assets/flow-screen-component/flowScreenComponent.html +154 -0
- package/skills/generating-lwc-components/assets/flow-screen-component/flowScreenComponent.js +348 -0
- package/skills/generating-lwc-components/assets/flow-screen-component/flowScreenComponent.js-meta.xml +87 -0
- package/skills/generating-lwc-components/assets/form-component/formComponent.html +165 -0
- package/skills/generating-lwc-components/assets/form-component/formComponent.js +275 -0
- package/skills/generating-lwc-components/assets/graphql-component/graphqlComponent.html +100 -0
- package/skills/generating-lwc-components/assets/graphql-component/graphqlComponent.js +336 -0
- package/skills/generating-lwc-components/assets/jest-test/componentName.test.js.example +371 -0
- package/skills/generating-lwc-components/assets/message-channel/RecordSelected.messageChannel-meta.xml +71 -0
- package/skills/generating-lwc-components/assets/message-channel/lmsPublisher.js +103 -0
- package/skills/generating-lwc-components/assets/message-channel/lmsSubscriber.js +181 -0
- package/skills/generating-lwc-components/assets/modal-component/modalComponent.html +85 -0
- package/skills/generating-lwc-components/assets/modal-component/modalComponent.js +199 -0
- package/skills/generating-lwc-components/assets/record-picker/recordPicker.html +55 -0
- package/skills/generating-lwc-components/assets/record-picker/recordPicker.js +199 -0
- package/skills/generating-lwc-components/assets/state-store/store.js +282 -0
- package/skills/generating-lwc-components/assets/typescript-component/typescriptComponent.css +65 -0
- package/skills/generating-lwc-components/assets/typescript-component/typescriptComponent.html +95 -0
- package/skills/generating-lwc-components/assets/typescript-component/typescriptComponent.js-meta.xml +75 -0
- package/skills/generating-lwc-components/assets/typescript-component/typescriptComponent.test.ts.example +301 -0
- package/skills/generating-lwc-components/assets/typescript-component/typescriptComponent.ts +295 -0
- package/skills/generating-lwc-components/assets/workspace-api/workspaceComponent.html +71 -0
- package/skills/generating-lwc-components/assets/workspace-api/workspaceComponent.js +316 -0
- package/skills/generating-lwc-components/hooks/scripts/lwc-lsp-validate.py +295 -0
- package/skills/generating-lwc-components/hooks/scripts/post-tool-validate.py +347 -0
- package/skills/generating-lwc-components/hooks/scripts/slds_data/deprecated_patterns.json +74 -0
- package/skills/generating-lwc-components/hooks/scripts/slds_data/styling_hooks.json +111 -0
- package/skills/generating-lwc-components/hooks/scripts/slds_data/valid_slds_classes.json +127 -0
- package/skills/generating-lwc-components/hooks/scripts/slds_linter_wrapper.py +294 -0
- package/skills/generating-lwc-components/hooks/scripts/slds_rules/__init__.py +22 -0
- package/skills/generating-lwc-components/hooks/scripts/template_validator.py +332 -0
- package/skills/generating-lwc-components/hooks/scripts/validate_slds.py +595 -0
- package/skills/generating-lwc-components/references/accessibility-guide.md +843 -0
- package/skills/generating-lwc-components/references/advanced-features.md +108 -0
- package/skills/generating-lwc-components/references/async-notification-patterns.md +661 -0
- package/skills/generating-lwc-components/references/cli-commands.md +545 -0
- package/skills/generating-lwc-components/references/component-patterns.md +1476 -0
- package/skills/generating-lwc-components/references/flow-integration-guide.md +675 -0
- package/skills/generating-lwc-components/references/jest-testing.md +1011 -0
- package/skills/generating-lwc-components/references/lms-guide.md +860 -0
- package/skills/generating-lwc-components/references/lwc-best-practices.md +1310 -0
- package/skills/generating-lwc-components/references/performance-guide.md +861 -0
- package/skills/generating-lwc-components/references/scoring-and-testing.md +116 -0
- package/skills/generating-lwc-components/references/slds-blueprints.json +14389 -0
- package/skills/generating-lwc-components/references/slds-design-guide.md +166 -0
- package/skills/generating-lwc-components/references/state-management.md +642 -0
- package/skills/generating-lwc-components/references/template-anti-patterns.md +948 -0
- package/skills/generating-lwc-components/references/triangle-pattern.md +365 -0
- package/skills/generating-lwc-components/scripts/local-dev-preview.sh +34 -0
- package/skills/generating-mermaid-diagrams/CREDITS.md +46 -0
- package/skills/generating-mermaid-diagrams/README.md +114 -0
- package/skills/generating-mermaid-diagrams/SKILL.md +218 -0
- package/skills/generating-mermaid-diagrams/assets/agentforce/agent-flow.md +313 -0
- package/skills/generating-mermaid-diagrams/assets/architecture/system-landscape.md +351 -0
- package/skills/generating-mermaid-diagrams/assets/datamodel/b2b-commerce-erd.md +317 -0
- package/skills/generating-mermaid-diagrams/assets/datamodel/campaigns-erd.md +195 -0
- package/skills/generating-mermaid-diagrams/assets/datamodel/consent-erd.md +262 -0
- package/skills/generating-mermaid-diagrams/assets/datamodel/files-erd.md +266 -0
- package/skills/generating-mermaid-diagrams/assets/datamodel/forecasting-erd.md +261 -0
- package/skills/generating-mermaid-diagrams/assets/datamodel/fsl-erd.md +332 -0
- package/skills/generating-mermaid-diagrams/assets/datamodel/party-model-erd.md +237 -0
- package/skills/generating-mermaid-diagrams/assets/datamodel/quote-order-erd.md +277 -0
- package/skills/generating-mermaid-diagrams/assets/datamodel/revenue-cloud-erd.md +343 -0
- package/skills/generating-mermaid-diagrams/assets/datamodel/sales-cloud-erd.md +192 -0
- package/skills/generating-mermaid-diagrams/assets/datamodel/salesforce-erd.md +209 -0
- package/skills/generating-mermaid-diagrams/assets/datamodel/scheduler-erd.md +276 -0
- package/skills/generating-mermaid-diagrams/assets/datamodel/service-cloud-erd.md +217 -0
- package/skills/generating-mermaid-diagrams/assets/datamodel/territory-management-erd.md +241 -0
- package/skills/generating-mermaid-diagrams/assets/integration/api-sequence.md +387 -0
- package/skills/generating-mermaid-diagrams/assets/oauth/authorization-code-pkce.md +197 -0
- package/skills/generating-mermaid-diagrams/assets/oauth/authorization-code.md +152 -0
- package/skills/generating-mermaid-diagrams/assets/oauth/client-credentials.md +233 -0
- package/skills/generating-mermaid-diagrams/assets/oauth/device-authorization.md +295 -0
- package/skills/generating-mermaid-diagrams/assets/oauth/jwt-bearer.md +256 -0
- package/skills/generating-mermaid-diagrams/assets/oauth/refresh-token.md +281 -0
- package/skills/generating-mermaid-diagrams/assets/oauth/user-agent-social-sign-on.md +281 -0
- package/skills/generating-mermaid-diagrams/assets/role-hierarchy/user-hierarchy.md +322 -0
- package/skills/generating-mermaid-diagrams/references/color-palette.md +464 -0
- package/skills/generating-mermaid-diagrams/references/diagram-conventions.md +313 -0
- package/skills/generating-mermaid-diagrams/references/erd-conventions.md +320 -0
- package/skills/generating-mermaid-diagrams/references/mermaid-reference.md +434 -0
- package/skills/generating-mermaid-diagrams/references/mermaid-styling.md +81 -0
- package/skills/generating-mermaid-diagrams/references/preview-guide.md +49 -0
- package/skills/generating-mermaid-diagrams/references/usage-examples.md +340 -0
- package/skills/generating-mermaid-diagrams/scripts/README.md +160 -0
- package/skills/generating-mermaid-diagrams/scripts/mermaid_preview.py +654 -0
- package/skills/generating-mermaid-diagrams/scripts/query-org-metadata.py +293 -0
- package/skills/generating-visual-diagrams/CREDITS.md +80 -0
- package/skills/generating-visual-diagrams/README.md +83 -0
- package/skills/generating-visual-diagrams/SKILL.md +208 -0
- package/skills/generating-visual-diagrams/assets/architecture/integration-flow.md +55 -0
- package/skills/generating-visual-diagrams/assets/erd/core-objects.md +131 -0
- package/skills/generating-visual-diagrams/assets/erd/custom-objects.md +60 -0
- package/skills/generating-visual-diagrams/assets/lwc/dashboard-card.md +45 -0
- package/skills/generating-visual-diagrams/assets/lwc/data-table.md +57 -0
- package/skills/generating-visual-diagrams/assets/lwc/record-form.md +60 -0
- package/skills/generating-visual-diagrams/assets/review/apex-review.md +57 -0
- package/skills/generating-visual-diagrams/assets/review/lwc-review.md +48 -0
- package/skills/generating-visual-diagrams/references/architect-aesthetic-guide.md +257 -0
- package/skills/generating-visual-diagrams/references/examples-index.md +35 -0
- package/skills/generating-visual-diagrams/references/gemini-cli-setup.md +65 -0
- package/skills/generating-visual-diagrams/references/interview-questions.md +529 -0
- package/skills/generating-visual-diagrams/references/iteration-workflow.md +173 -0
- package/skills/generating-visual-diagrams/scripts/check-prerequisites.sh +101 -0
- package/skills/generating-visual-diagrams/scripts/generate_image.py +243 -0
- package/skills/handling-sf-data/CREDITS.md +5 -0
- package/skills/handling-sf-data/README.md +112 -0
- package/skills/handling-sf-data/SKILL.md +235 -0
- package/skills/handling-sf-data/assets/bulk/bulk-insert-10000.apex +293 -0
- package/skills/handling-sf-data/assets/bulk/bulk-insert-200.apex +208 -0
- package/skills/handling-sf-data/assets/bulk/bulk-insert-500.apex +219 -0
- package/skills/handling-sf-data/assets/bulk/bulk-upsert-external-id.apex +324 -0
- package/skills/handling-sf-data/assets/cleanup/delete-by-created-date.apex +319 -0
- package/skills/handling-sf-data/assets/cleanup/delete-by-name.apex +240 -0
- package/skills/handling-sf-data/assets/cleanup/delete-test-data.apex +311 -0
- package/skills/handling-sf-data/assets/cleanup/rollback-transaction.apex +266 -0
- package/skills/handling-sf-data/assets/csv/account-import.csv +11 -0
- package/skills/handling-sf-data/assets/csv/contact-import.csv +11 -0
- package/skills/handling-sf-data/assets/csv/custom-object-import.csv +11 -0
- package/skills/handling-sf-data/assets/csv/opportunity-import.csv +11 -0
- package/skills/handling-sf-data/assets/factories/account-factory.apex +165 -0
- package/skills/handling-sf-data/assets/factories/case-factory.apex +237 -0
- package/skills/handling-sf-data/assets/factories/contact-factory.apex +168 -0
- package/skills/handling-sf-data/assets/factories/custom-object-factory.apex +260 -0
- package/skills/handling-sf-data/assets/factories/event-factory.apex +275 -0
- package/skills/handling-sf-data/assets/factories/hierarchy-factory.apex +372 -0
- package/skills/handling-sf-data/assets/factories/lead-factory.apex +190 -0
- package/skills/handling-sf-data/assets/factories/opportunity-factory.apex +206 -0
- package/skills/handling-sf-data/assets/factories/task-factory.apex +246 -0
- package/skills/handling-sf-data/assets/factories/user-factory.apex +278 -0
- package/skills/handling-sf-data/assets/json/account-contact-tree.json +130 -0
- package/skills/handling-sf-data/assets/json/account-opportunity-tree.json +110 -0
- package/skills/handling-sf-data/assets/json/full-hierarchy-tree.json +188 -0
- package/skills/handling-sf-data/assets/soql/aggregate.soql +226 -0
- package/skills/handling-sf-data/assets/soql/child-to-parent.soql +162 -0
- package/skills/handling-sf-data/assets/soql/parent-to-child.soql +153 -0
- package/skills/handling-sf-data/assets/soql/polymorphic.soql +198 -0
- package/skills/handling-sf-data/assets/soql/subquery.soql +287 -0
- package/skills/handling-sf-data/references/anonymous-apex-guide.md +98 -0
- package/skills/handling-sf-data/references/bulk-operations-guide.md +94 -0
- package/skills/handling-sf-data/references/bulk-testing-example.md +194 -0
- package/skills/handling-sf-data/references/cleanup-rollback-example.md +322 -0
- package/skills/handling-sf-data/references/cleanup-rollback-guide.md +84 -0
- package/skills/handling-sf-data/references/crud-workflow-example.md +183 -0
- package/skills/handling-sf-data/references/governor-limits-reference.md +74 -0
- package/skills/handling-sf-data/references/orchestration.md +174 -0
- package/skills/handling-sf-data/references/relationship-query-examples.md +249 -0
- package/skills/handling-sf-data/references/sf-cli-data-commands.md +158 -0
- package/skills/handling-sf-data/references/soql-relationship-guide.md +84 -0
- package/skills/handling-sf-data/references/test-data-best-practices.md +104 -0
- package/skills/handling-sf-data/references/test-data-factory-usage.md +290 -0
- package/skills/handling-sf-data/references/test-data-patterns.md +98 -0
- package/skills/handling-sf-data/scripts/soql_validator.py +292 -0
- package/skills/handling-sf-data/scripts/validate_data_operation.py +379 -0
- package/skills/harmonizing-datacloud/CREDITS.md +3 -0
- package/skills/harmonizing-datacloud/README.md +31 -0
- package/skills/harmonizing-datacloud/SKILL.md +117 -0
- package/skills/modeling-omnistudio-epc-catalog/CREDITS.md +14 -0
- package/skills/modeling-omnistudio-epc-catalog/README.md +89 -0
- package/skills/modeling-omnistudio-epc-catalog/SKILL.md +395 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/attribute-assignment-template.json +402 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/compiled-attribute-overrides-template.json +43 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/completion-block-template.txt +8 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/decomposition-relationships-template.json +233 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-premium-fttc-simple-offer/Business-Internet-Premium-FTTC_AttributeAssignments.json +514 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-premium-fttc-simple-offer/Business-Internet-Premium-FTTC_CompiledAttributeOverrides.json +21 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-premium-fttc-simple-offer/Business-Internet-Premium-FTTC_DataPack.json +649 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-premium-fttc-simple-offer/Business-Internet-Premium-FTTC_DecompositionRelationships.json +200 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-premium-fttc-simple-offer/Business-Internet-Premium-FTTC_ObjectFieldAttributes.json +138 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-premium-fttc-simple-offer/Business-Internet-Premium-FTTC_OrchestrationScenarios.json +54 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-premium-fttc-simple-offer/Business-Internet-Premium-FTTC_OverrideDefinitions.json +266 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-premium-fttc-simple-offer/Business-Internet-Premium-FTTC_ParentKeys.json +23 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-premium-fttc-simple-offer/Business-Internet-Premium-FTTC_PriceListEntries.json +54 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-premium-fttc-simple-offer/Business-Internet-Premium-FTTC_PricebookEntries.json +35 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-premium-fttc-simple-offer/Business-Internet-Premium-FTTC_ProductChildItems.json +34 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-premium-fttc-simple-offer/Business-Internet-Premium-FTTC_RuleAssignments.json +21 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-pro-vpl-simple-offer/Business-Internet-Pro-VPL_AttributeAssignments.json +410 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-pro-vpl-simple-offer/Business-Internet-Pro-VPL_DataPack.json +535 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-pro-vpl-simple-offer/Business-Internet-Pro-VPL_DecompositionRelationships.json +35 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-pro-vpl-simple-offer/Business-Internet-Pro-VPL_ObjectFieldAttributes.json +138 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-pro-vpl-simple-offer/Business-Internet-Pro-VPL_OrchestrationScenarios.json +28 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-pro-vpl-simple-offer/Business-Internet-Pro-VPL_ParentKeys.json +23 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-pro-vpl-simple-offer/Business-Internet-Pro-VPL_PriceListEntries.json +220 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-pro-vpl-simple-offer/Business-Internet-Pro-VPL_PricebookEntries.json +35 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/business-internet-pro-vpl-simple-offer/Business-Internet-Pro-VPL_ProductChildItems.json +414 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/samsung-galaxy-s22-bundle/Samsung-Galaxy-S22-Bundle_AttributeAssignments.json +382 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/samsung-galaxy-s22-bundle/Samsung-Galaxy-S22-Bundle_DataPack.json +565 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/samsung-galaxy-s22-bundle/Samsung-Galaxy-S22-Bundle_DecompositionRelationships.json +35 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/samsung-galaxy-s22-bundle/Samsung-Galaxy-S22-Bundle_ObjectFieldAttributes.json +104 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/samsung-galaxy-s22-bundle/Samsung-Galaxy-S22-Bundle_OrchestrationScenarios.json +28 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/samsung-galaxy-s22-bundle/Samsung-Galaxy-S22-Bundle_ParentKeys.json +13 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/samsung-galaxy-s22-bundle/Samsung-Galaxy-S22-Bundle_PriceListEntries.json +106 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/samsung-galaxy-s22-bundle/Samsung-Galaxy-S22-Bundle_PricebookEntries.json +35 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/samsung-galaxy-s22-bundle/Samsung-Galaxy-S22-Bundle_ProductChildItems.json +72 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/static-ip-simple-offer/Static-IP_AttributeAssignments.json +142 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/static-ip-simple-offer/Static-IP_DataPack.json +377 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/static-ip-simple-offer/Static-IP_DecompositionRelationships.json +35 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/static-ip-simple-offer/Static-IP_ObjectFieldAttributes.json +36 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/static-ip-simple-offer/Static-IP_ParentKeys.json +8 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/static-ip-simple-offer/Static-IP_PriceListEntries.json +54 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/static-ip-simple-offer/Static-IP_PricebookEntries.json +35 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/examples/static-ip-simple-offer/Static-IP_ProductChildItems.json +34 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/object-field-attributes-template.json +138 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/orchestration-scenarios-template.json +54 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/override-definitions-template.json +134 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/parent-keys-template.json +29 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/price-list-entries-template.json +158 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/pricebook-entries-template.json +35 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/product-child-item-template.json +338 -0
- package/skills/modeling-omnistudio-epc-catalog/assets/product2-offer-template.json +527 -0
- package/skills/modeling-omnistudio-epc-catalog/examples/.gitkeep +1 -0
- package/skills/modeling-omnistudio-epc-catalog/examples/business-internet-plus-bundle/Business-Internet-Plus_AttributeAssignments.json +95 -0
- package/skills/modeling-omnistudio-epc-catalog/examples/business-internet-plus-bundle/Business-Internet-Plus_CompiledAttributeOverrides.json +1 -0
- package/skills/modeling-omnistudio-epc-catalog/examples/business-internet-plus-bundle/Business-Internet-Plus_DataPack.json +214 -0
- package/skills/modeling-omnistudio-epc-catalog/examples/business-internet-plus-bundle/Business-Internet-Plus_DecompositionRelationships.json +28 -0
- package/skills/modeling-omnistudio-epc-catalog/examples/business-internet-plus-bundle/Business-Internet-Plus_ObjectFieldAttributes.json +98 -0
- package/skills/modeling-omnistudio-epc-catalog/examples/business-internet-plus-bundle/Business-Internet-Plus_OrchestrationScenarios.json +22 -0
- package/skills/modeling-omnistudio-epc-catalog/examples/business-internet-plus-bundle/Business-Internet-Plus_OverrideDefinitions.json +1 -0
- package/skills/modeling-omnistudio-epc-catalog/examples/business-internet-plus-bundle/Business-Internet-Plus_ParentKeys.json +13 -0
- package/skills/modeling-omnistudio-epc-catalog/examples/business-internet-plus-bundle/Business-Internet-Plus_PriceListEntries.json +35 -0
- package/skills/modeling-omnistudio-epc-catalog/examples/business-internet-plus-bundle/Business-Internet-Plus_PricebookEntries.json +28 -0
- package/skills/modeling-omnistudio-epc-catalog/examples/business-internet-plus-bundle/Business-Internet-Plus_ProductChildItems.json +110 -0
- package/skills/modeling-omnistudio-epc-catalog/examples/business-internet-plus-bundle/TRANSCRIPT.md +58 -0
- package/skills/modeling-omnistudio-epc-catalog/references/epc-field-guide.md +90 -0
- package/skills/modeling-omnistudio-epc-catalog/references/naming-conventions.md +80 -0
- package/skills/modeling-omnistudio-epc-catalog/references/scoring-model.md +57 -0
- package/skills/modeling-omnistudio-epc-catalog/scripts/cli-validation-commands.sh +19 -0
- package/skills/modeling-omnistudio-epc-catalog/scripts/sample-invocations.sh +18 -0
- package/skills/orchestrating-datacloud/CREDITS.md +15 -0
- package/skills/orchestrating-datacloud/README.md +129 -0
- package/skills/orchestrating-datacloud/SKILL.md +236 -0
- package/skills/orchestrating-datacloud/UPSTREAM.md +45 -0
- package/skills/orchestrating-datacloud/assets/definitions/activation-target.template.json +5 -0
- package/skills/orchestrating-datacloud/assets/definitions/activation.template.json +7 -0
- package/skills/orchestrating-datacloud/assets/definitions/calculated-insight.template.json +7 -0
- package/skills/orchestrating-datacloud/assets/definitions/data-action-target.template.json +5 -0
- package/skills/orchestrating-datacloud/assets/definitions/data-action.template.json +5 -0
- package/skills/orchestrating-datacloud/assets/definitions/data-graph.template.json +21 -0
- package/skills/orchestrating-datacloud/assets/definitions/data-stream.template.json +55 -0
- package/skills/orchestrating-datacloud/assets/definitions/dmo.template.json +17 -0
- package/skills/orchestrating-datacloud/assets/definitions/identity-resolution.template.json +30 -0
- package/skills/orchestrating-datacloud/assets/definitions/mapping.template.json +14 -0
- package/skills/orchestrating-datacloud/assets/definitions/relationship.template.json +12 -0
- package/skills/orchestrating-datacloud/assets/definitions/search-index.template.json +9 -0
- package/skills/orchestrating-datacloud/assets/definitions/segment.template.json +16 -0
- package/skills/orchestrating-datacloud/references/feature-readiness.md +157 -0
- package/skills/orchestrating-datacloud/references/plugin-setup.md +140 -0
- package/skills/orchestrating-datacloud/scripts/bootstrap-plugin.sh +53 -0
- package/skills/orchestrating-datacloud/scripts/diagnose-org.mjs +511 -0
- package/skills/orchestrating-datacloud/scripts/generate-manifest.mjs +68 -0
- package/skills/orchestrating-datacloud/scripts/verify-plugin.sh +58 -0
- package/skills/preparing-datacloud/CREDITS.md +7 -0
- package/skills/preparing-datacloud/README.md +51 -0
- package/skills/preparing-datacloud/SKILL.md +191 -0
- package/skills/preparing-datacloud/examples/ingestion-api/.env.example +8 -0
- package/skills/preparing-datacloud/examples/ingestion-api/README.md +48 -0
- package/skills/preparing-datacloud/examples/ingestion-api/send-data.py +144 -0
- package/skills/querying-soql/CREDITS.md +21 -0
- package/skills/querying-soql/README.md +41 -0
- package/skills/querying-soql/SKILL.md +143 -0
- package/skills/querying-soql/assets/aggregate-queries.soql +242 -0
- package/skills/querying-soql/assets/basic-queries.soql +188 -0
- package/skills/querying-soql/assets/bulkified-query-pattern.cls +280 -0
- package/skills/querying-soql/assets/optimization-patterns.soql +259 -0
- package/skills/querying-soql/assets/relationship-queries.soql +203 -0
- package/skills/querying-soql/assets/selector-class.cls +219 -0
- package/skills/querying-soql/references/anti-patterns.md +348 -0
- package/skills/querying-soql/references/cli-commands.md +358 -0
- package/skills/querying-soql/references/field-coverage-rules.md +514 -0
- package/skills/querying-soql/references/query-optimization.md +142 -0
- package/skills/querying-soql/references/selector-patterns.md +479 -0
- package/skills/querying-soql/references/soql-reference.md +227 -0
- package/skills/querying-soql/references/soql-syntax-reference.md +208 -0
- package/skills/querying-soql/scripts/post-tool-validate.py +322 -0
- package/skills/retrieving-datacloud/CREDITS.md +7 -0
- package/skills/retrieving-datacloud/README.md +44 -0
- package/skills/retrieving-datacloud/SKILL.md +120 -0
- package/skills/retrieving-datacloud/examples/search-indexes/hybrid-structured.json +44 -0
- package/skills/retrieving-datacloud/examples/search-indexes/vector-knowledge.json +43 -0
- package/skills/running-apex-tests/CREDITS.md +22 -0
- package/skills/running-apex-tests/README.md +94 -0
- package/skills/running-apex-tests/SKILL.md +158 -0
- package/skills/running-apex-tests/assets/basic-test.cls +169 -0
- package/skills/running-apex-tests/assets/bulk-test.cls +255 -0
- package/skills/running-apex-tests/assets/dml-mock.cls +339 -0
- package/skills/running-apex-tests/assets/mock-callout-test.cls +353 -0
- package/skills/running-apex-tests/assets/stub-provider-example.cls +364 -0
- package/skills/running-apex-tests/assets/test-data-factory.cls +328 -0
- package/skills/running-apex-tests/hooks/scripts/parse-test-results.py +364 -0
- package/skills/running-apex-tests/references/cli-commands.md +289 -0
- package/skills/running-apex-tests/references/mocking-patterns.md +500 -0
- package/skills/running-apex-tests/references/performance-optimization.md +283 -0
- package/skills/running-apex-tests/references/test-fix-loop.md +49 -0
- package/skills/running-apex-tests/references/test-patterns.md +154 -0
- package/skills/running-apex-tests/references/testing-best-practices.md +509 -0
- package/skills/segmenting-datacloud/CREDITS.md +3 -0
- package/skills/segmenting-datacloud/README.md +36 -0
- package/skills/segmenting-datacloud/SKILL.md +115 -0
|
@@ -0,0 +1,1476 @@
|
|
|
1
|
+
<!-- Parent: generating-lwc-components/SKILL.md -->
|
|
2
|
+
# LWC Component Patterns
|
|
3
|
+
|
|
4
|
+
Comprehensive code examples for common Lightning Web Component patterns.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Table of Contents
|
|
9
|
+
|
|
10
|
+
1. [PICKLES Framework Details](#pickles-framework-details)
|
|
11
|
+
2. [Wire Service Patterns](#wire-service-patterns)
|
|
12
|
+
- [Wire vs Imperative Apex Calls](#wire-vs-imperative-apex-calls)
|
|
13
|
+
3. [GraphQL Patterns](#graphql-patterns)
|
|
14
|
+
4. [Modal Component Pattern](#modal-component-pattern)
|
|
15
|
+
5. [Record Picker Pattern](#record-picker-pattern)
|
|
16
|
+
6. [Workspace API Pattern](#workspace-api-pattern)
|
|
17
|
+
7. [Parent-Child Communication](#parent-child-communication)
|
|
18
|
+
8. [Sibling Communication (via Parent)](#sibling-communication-via-parent)
|
|
19
|
+
9. [Navigation Patterns](#navigation-patterns)
|
|
20
|
+
10. [TypeScript Patterns](#typescript-patterns)
|
|
21
|
+
11. [Apex Controller Patterns](#apex-controller-patterns)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## PICKLES Framework Details
|
|
26
|
+
|
|
27
|
+
### P - Prototype
|
|
28
|
+
|
|
29
|
+
**Purpose**: Validate ideas early before full implementation.
|
|
30
|
+
|
|
31
|
+
| Action | Description |
|
|
32
|
+
|--------|-------------|
|
|
33
|
+
| Wireframe | Create high-level component sketches |
|
|
34
|
+
| Mock Data | Use sample data to test functionality |
|
|
35
|
+
| Stakeholder Review | Gather feedback before development |
|
|
36
|
+
| Separation of Concerns | Break into smaller functional pieces |
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
// Mock data pattern for prototyping
|
|
40
|
+
const MOCK_ACCOUNTS = [
|
|
41
|
+
{ Id: '001MOCK001', Name: 'Acme Corp', Industry: 'Technology' },
|
|
42
|
+
{ Id: '001MOCK002', Name: 'Global Inc', Industry: 'Finance' }
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
export default class AccountPrototype extends LightningElement {
|
|
46
|
+
accounts = MOCK_ACCOUNTS; // Replace with wire/Apex later
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### I - Integrate
|
|
51
|
+
|
|
52
|
+
**Purpose**: Determine how components interact with data systems.
|
|
53
|
+
|
|
54
|
+
**Integration Checklist**:
|
|
55
|
+
- [ ] Implement error handling with clear user notifications
|
|
56
|
+
- [ ] Add loading spinners to prevent duplicate requests
|
|
57
|
+
- [ ] Use LDS for single-object operations (minimizes DML)
|
|
58
|
+
- [ ] Respect FLS and CRUD in Apex implementations
|
|
59
|
+
- [ ] Store `wiredResult` for `refreshApex()` support
|
|
60
|
+
|
|
61
|
+
### C - Composition
|
|
62
|
+
|
|
63
|
+
**Purpose**: Structure how LWCs nest and communicate.
|
|
64
|
+
|
|
65
|
+
**Best Practices**:
|
|
66
|
+
- Maintain shallow component hierarchies (max 3-4 levels)
|
|
67
|
+
- Single responsibility per component
|
|
68
|
+
- Clean up subscriptions in `disconnectedCallback()`
|
|
69
|
+
- Use custom events purposefully, not for every interaction
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
// Parent-managed composition pattern
|
|
73
|
+
// parent.js
|
|
74
|
+
handleChildEvent(event) {
|
|
75
|
+
this.selectedId = event.detail.id;
|
|
76
|
+
// Update child via @api
|
|
77
|
+
this.template.querySelector('c-child').selectedId = this.selectedId;
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### K - Kinetics
|
|
82
|
+
|
|
83
|
+
**Purpose**: Manage user interaction and event responsiveness.
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
// Debounce pattern for search
|
|
87
|
+
delayTimeout;
|
|
88
|
+
|
|
89
|
+
handleSearchChange(event) {
|
|
90
|
+
const searchTerm = event.target.value;
|
|
91
|
+
clearTimeout(this.delayTimeout);
|
|
92
|
+
this.delayTimeout = setTimeout(() => {
|
|
93
|
+
this.dispatchEvent(new CustomEvent('search', {
|
|
94
|
+
detail: { searchTerm }
|
|
95
|
+
}));
|
|
96
|
+
}, 300);
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### L - Libraries
|
|
101
|
+
|
|
102
|
+
**Purpose**: Leverage Salesforce-provided and platform tools.
|
|
103
|
+
|
|
104
|
+
**Recommended Platform Features**:
|
|
105
|
+
|
|
106
|
+
| API/Module | Use Case |
|
|
107
|
+
|------------|----------|
|
|
108
|
+
| `lightning/navigation` | Page/record navigation |
|
|
109
|
+
| `lightning/uiRecordApi` | LDS operations (getRecord, updateRecord) |
|
|
110
|
+
| `lightning/platformShowToastEvent` | User notifications |
|
|
111
|
+
| `lightning/modal` | Native modal dialogs |
|
|
112
|
+
| Base Components | Pre-built UI (button, input, datatable) |
|
|
113
|
+
| `lightning/refresh` | Dispatch refresh events |
|
|
114
|
+
|
|
115
|
+
**Avoid reinventing** what base components already provide!
|
|
116
|
+
|
|
117
|
+
### E - Execution
|
|
118
|
+
|
|
119
|
+
**Purpose**: Optimize performance and resource efficiency.
|
|
120
|
+
|
|
121
|
+
**Performance Checklist**:
|
|
122
|
+
- [ ] Lazy load with `if:true` / `lwc:if`
|
|
123
|
+
- [ ] Use `key` directive in iterations
|
|
124
|
+
- [ ] Cache computed values in getters
|
|
125
|
+
- [ ] Avoid property updates that trigger re-renders
|
|
126
|
+
- [ ] Use browser DevTools Performance tab
|
|
127
|
+
|
|
128
|
+
### S - Security
|
|
129
|
+
|
|
130
|
+
**Purpose**: Enforce access control and data protection.
|
|
131
|
+
|
|
132
|
+
```apex
|
|
133
|
+
// Secure Apex pattern
|
|
134
|
+
@AuraEnabled(cacheable=true)
|
|
135
|
+
public static List<Account> getAccounts(String searchTerm) {
|
|
136
|
+
String searchKey = '%' + String.escapeSingleQuotes(searchTerm) + '%';
|
|
137
|
+
return [
|
|
138
|
+
SELECT Id, Name, Industry
|
|
139
|
+
FROM Account
|
|
140
|
+
WHERE Name LIKE :searchKey
|
|
141
|
+
WITH SECURITY_ENFORCED
|
|
142
|
+
LIMIT 50
|
|
143
|
+
];
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Wire Service Patterns
|
|
150
|
+
|
|
151
|
+
### Wire vs Imperative Apex Calls
|
|
152
|
+
|
|
153
|
+
LWC can interact with Apex in two ways: **@wire** (reactive/declarative) and **imperative calls** (manual/programmatic). Understanding when to use each is critical for building performant, maintainable components.
|
|
154
|
+
|
|
155
|
+
#### Quick Comparison
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
|
159
|
+
│ WIRE vs IMPERATIVE APEX CALLS │
|
|
160
|
+
├──────────────────┬──────────────────────────────┬────────────────────────────────────┤
|
|
161
|
+
│ Aspect │ Wire (@wire) │ Imperative Calls │
|
|
162
|
+
├──────────────────┼──────────────────────────────┼────────────────────────────────────┤
|
|
163
|
+
│ Execution │ Automatic / Reactive │ Manual / Programmatic │
|
|
164
|
+
│ DML Operations │ ❌ Read-Only │ ✅ Insert / Update / Delete │
|
|
165
|
+
│ Data Updates │ ✅ Auto on Parameter Change │ ❌ Manual Refresh Required │
|
|
166
|
+
│ Control │ ⚠️ Low (framework decides) │ ✅ Full (you decide when/how) │
|
|
167
|
+
│ Error Handling │ ✅ Framework Managed │ ⚠️ Developer Managed │
|
|
168
|
+
│ Supported Objects│ ⚠️ UI API Only │ ✅ All Objects │
|
|
169
|
+
│ Caching │ ✅ Built-in (cacheable=true) │ ❌ No automatic caching │
|
|
170
|
+
└──────────────────┴──────────────────────────────┴────────────────────────────────────┘
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### Pros & Cons
|
|
174
|
+
|
|
175
|
+
| Wire (@wire) | Imperative Calls |
|
|
176
|
+
|--------------|------------------|
|
|
177
|
+
| ✅ Auto UI sync & caching | ✅ Supports DML & all objects |
|
|
178
|
+
| ✅ Less boilerplate code | ✅ Full control over timing |
|
|
179
|
+
| ✅ Reactive to parameter changes | ✅ Can handle complex logic |
|
|
180
|
+
| ❌ Read-only, limited objects | ❌ Manual handling, no auto refresh |
|
|
181
|
+
| ❌ Can't control execution timing | ❌ More error handling code needed |
|
|
182
|
+
|
|
183
|
+
#### When to Use Each
|
|
184
|
+
|
|
185
|
+
**Use Wire (@wire) when:**
|
|
186
|
+
- 📌 Read-only data display
|
|
187
|
+
- 📌 Auto-refresh UI when parameters change
|
|
188
|
+
- 📌 Stable parameters (recordId, filter values)
|
|
189
|
+
- 📌 Working with UI API supported objects
|
|
190
|
+
|
|
191
|
+
**Use Imperative Calls when:**
|
|
192
|
+
- 📌 User actions (clicks, form submissions)
|
|
193
|
+
- 📌 DML operations (Insert, Update, Delete)
|
|
194
|
+
- 📌 Dynamic parameters determined at runtime
|
|
195
|
+
- 📌 Custom objects or complex queries
|
|
196
|
+
- 📌 Need control over execution timing
|
|
197
|
+
|
|
198
|
+
#### Side-by-Side Code Examples
|
|
199
|
+
|
|
200
|
+
**Wire Example** - Data loads automatically when `selectedIndustry` changes:
|
|
201
|
+
|
|
202
|
+
```javascript
|
|
203
|
+
import { LightningElement, wire } from 'lwc';
|
|
204
|
+
import fetchAccounts from '@salesforce/apex/AccountController.fetchAccounts';
|
|
205
|
+
|
|
206
|
+
export default class WireExample extends LightningElement {
|
|
207
|
+
selectedIndustry = 'Technology';
|
|
208
|
+
accounts;
|
|
209
|
+
error;
|
|
210
|
+
|
|
211
|
+
// Automatically re-fetches when selectedIndustry changes
|
|
212
|
+
@wire(fetchAccounts, { industry: '$selectedIndustry' })
|
|
213
|
+
wiredAccounts({ data, error }) {
|
|
214
|
+
if (data) {
|
|
215
|
+
this.accounts = data;
|
|
216
|
+
this.error = undefined;
|
|
217
|
+
} else if (error) {
|
|
218
|
+
this.error = error;
|
|
219
|
+
this.accounts = undefined;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Imperative Example** - Data loads only when user triggers action:
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
import { LightningElement } from 'lwc';
|
|
229
|
+
import fetchAccounts from '@salesforce/apex/AccountController.fetchAccounts';
|
|
230
|
+
|
|
231
|
+
export default class ImperativeExample extends LightningElement {
|
|
232
|
+
selectedIndustry = 'Technology';
|
|
233
|
+
accounts;
|
|
234
|
+
error;
|
|
235
|
+
isLoading = false;
|
|
236
|
+
|
|
237
|
+
// Called explicitly when user clicks button or submits form
|
|
238
|
+
async fetchAccounts() {
|
|
239
|
+
this.isLoading = true;
|
|
240
|
+
try {
|
|
241
|
+
this.accounts = await fetchAccounts({
|
|
242
|
+
industry: this.selectedIndustry
|
|
243
|
+
});
|
|
244
|
+
this.error = undefined;
|
|
245
|
+
} catch (error) {
|
|
246
|
+
this.error = error;
|
|
247
|
+
this.accounts = undefined;
|
|
248
|
+
} finally {
|
|
249
|
+
this.isLoading = false;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
#### Decision Tree
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
┌─────────────────────────────┐
|
|
259
|
+
│ Need to modify data? │
|
|
260
|
+
│ (Insert/Update/Delete) │
|
|
261
|
+
└─────────────┬───────────────┘
|
|
262
|
+
│
|
|
263
|
+
┌─────────────┴───────────────┐
|
|
264
|
+
│ │
|
|
265
|
+
YES NO
|
|
266
|
+
│ │
|
|
267
|
+
▼ ▼
|
|
268
|
+
┌─────────────────┐ ┌─────────────────────────┐
|
|
269
|
+
│ IMPERATIVE │ │ Should data auto- │
|
|
270
|
+
│ (Use await) │ │ refresh on param │
|
|
271
|
+
└─────────────────┘ │ change? │
|
|
272
|
+
└───────────┬─────────────┘
|
|
273
|
+
│
|
|
274
|
+
┌───────────┴───────────┐
|
|
275
|
+
│ │
|
|
276
|
+
YES NO
|
|
277
|
+
│ │
|
|
278
|
+
▼ ▼
|
|
279
|
+
┌─────────────────┐ ┌─────────────────┐
|
|
280
|
+
│ @WIRE │ │ IMPERATIVE │
|
|
281
|
+
│ (Reactive) │ │ (On-demand) │
|
|
282
|
+
└─────────────────┘ └─────────────────┘
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
### 1. Basic Data Display (Wire Service)
|
|
288
|
+
|
|
289
|
+
```javascript
|
|
290
|
+
// accountCard.js
|
|
291
|
+
import { LightningElement, api, wire } from 'lwc';
|
|
292
|
+
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
|
|
293
|
+
import NAME_FIELD from '@salesforce/schema/Account.Name';
|
|
294
|
+
import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';
|
|
295
|
+
|
|
296
|
+
const FIELDS = [NAME_FIELD, INDUSTRY_FIELD];
|
|
297
|
+
|
|
298
|
+
export default class AccountCard extends LightningElement {
|
|
299
|
+
@api recordId;
|
|
300
|
+
|
|
301
|
+
@wire(getRecord, { recordId: '$recordId', fields: FIELDS })
|
|
302
|
+
account;
|
|
303
|
+
|
|
304
|
+
get name() {
|
|
305
|
+
return getFieldValue(this.account.data, NAME_FIELD);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
get industry() {
|
|
309
|
+
return getFieldValue(this.account.data, INDUSTRY_FIELD);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
get isLoading() {
|
|
313
|
+
return !this.account.data && !this.account.error;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
```html
|
|
319
|
+
<!-- accountCard.html -->
|
|
320
|
+
<template>
|
|
321
|
+
<template lwc:if={isLoading}>
|
|
322
|
+
<lightning-spinner alternative-text="Loading"></lightning-spinner>
|
|
323
|
+
</template>
|
|
324
|
+
<template lwc:if={account.data}>
|
|
325
|
+
<div class="slds-box slds-theme_default">
|
|
326
|
+
<h2 class="slds-text-heading_medium">{name}</h2>
|
|
327
|
+
<p class="slds-text-color_weak">{industry}</p>
|
|
328
|
+
</div>
|
|
329
|
+
</template>
|
|
330
|
+
<template lwc:if={account.error}>
|
|
331
|
+
<p class="slds-text-color_error">{account.error.body.message}</p>
|
|
332
|
+
</template>
|
|
333
|
+
</template>
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### 2. Wire Service with Apex
|
|
337
|
+
|
|
338
|
+
```javascript
|
|
339
|
+
// contactList.js
|
|
340
|
+
import { LightningElement, api, wire } from 'lwc';
|
|
341
|
+
import getContacts from '@salesforce/apex/ContactController.getContacts';
|
|
342
|
+
import { refreshApex } from '@salesforce/apex';
|
|
343
|
+
|
|
344
|
+
export default class ContactList extends LightningElement {
|
|
345
|
+
@api recordId;
|
|
346
|
+
contacts;
|
|
347
|
+
error;
|
|
348
|
+
wiredContactsResult;
|
|
349
|
+
|
|
350
|
+
@wire(getContacts, { accountId: '$recordId' })
|
|
351
|
+
wiredContacts(result) {
|
|
352
|
+
this.wiredContactsResult = result; // Store for refreshApex
|
|
353
|
+
const { error, data } = result;
|
|
354
|
+
if (data) {
|
|
355
|
+
this.contacts = data;
|
|
356
|
+
this.error = undefined;
|
|
357
|
+
} else if (error) {
|
|
358
|
+
this.error = error;
|
|
359
|
+
this.contacts = undefined;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async handleRefresh() {
|
|
364
|
+
await refreshApex(this.wiredContactsResult);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## GraphQL Patterns
|
|
372
|
+
|
|
373
|
+
> **Module Note**: `lightning/graphql` supersedes `lightning/uiGraphQLApi` and provides newer features like mutations, optional fields, and dynamic query construction.
|
|
374
|
+
|
|
375
|
+
### GraphQL Query (Wire Adapter)
|
|
376
|
+
|
|
377
|
+
```javascript
|
|
378
|
+
// graphqlContacts.js
|
|
379
|
+
import { LightningElement, wire } from 'lwc';
|
|
380
|
+
import { gql, graphql } from 'lightning/graphql';
|
|
381
|
+
|
|
382
|
+
const CONTACTS_QUERY = gql`
|
|
383
|
+
query ContactsQuery($first: Int, $after: String) {
|
|
384
|
+
uiapi {
|
|
385
|
+
query {
|
|
386
|
+
Contact(first: $first, after: $after) {
|
|
387
|
+
edges {
|
|
388
|
+
node {
|
|
389
|
+
Id
|
|
390
|
+
Name { value }
|
|
391
|
+
Email { value }
|
|
392
|
+
Account {
|
|
393
|
+
Name { value }
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
cursor
|
|
397
|
+
}
|
|
398
|
+
pageInfo {
|
|
399
|
+
hasNextPage
|
|
400
|
+
endCursor
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
`;
|
|
407
|
+
|
|
408
|
+
export default class GraphqlContacts extends LightningElement {
|
|
409
|
+
contacts;
|
|
410
|
+
pageInfo;
|
|
411
|
+
error;
|
|
412
|
+
_cursor;
|
|
413
|
+
|
|
414
|
+
@wire(graphql, {
|
|
415
|
+
query: CONTACTS_QUERY,
|
|
416
|
+
variables: '$queryVariables'
|
|
417
|
+
})
|
|
418
|
+
wiredContacts({ data, error }) {
|
|
419
|
+
if (data) {
|
|
420
|
+
const result = data.uiapi.query.Contact;
|
|
421
|
+
this.contacts = result.edges.map(edge => ({
|
|
422
|
+
id: edge.node.Id,
|
|
423
|
+
name: edge.node.Name.value,
|
|
424
|
+
email: edge.node.Email?.value,
|
|
425
|
+
accountName: edge.node.Account?.Name?.value
|
|
426
|
+
}));
|
|
427
|
+
this.pageInfo = result.pageInfo;
|
|
428
|
+
} else if (error) {
|
|
429
|
+
this.error = error;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
get queryVariables() {
|
|
434
|
+
return { first: 10, after: this._cursor };
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
loadMore() {
|
|
438
|
+
if (this.pageInfo?.hasNextPage) {
|
|
439
|
+
this._cursor = this.pageInfo.endCursor;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### GraphQL Mutations (Spring '26 - GA in API 66.0)
|
|
446
|
+
|
|
447
|
+
Mutations allow create, update, and delete operations via GraphQL. Use `executeMutation` for imperative operations.
|
|
448
|
+
|
|
449
|
+
```javascript
|
|
450
|
+
// graphqlAccountMutation.js
|
|
451
|
+
import { LightningElement, track } from 'lwc';
|
|
452
|
+
import { gql, executeMutation } from 'lightning/graphql';
|
|
453
|
+
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
|
|
454
|
+
|
|
455
|
+
// Create mutation
|
|
456
|
+
const CREATE_ACCOUNT = gql`
|
|
457
|
+
mutation CreateAccount($name: String!, $industry: String) {
|
|
458
|
+
uiapi {
|
|
459
|
+
AccountCreate(input: {
|
|
460
|
+
Account: {
|
|
461
|
+
Name: $name
|
|
462
|
+
Industry: $industry
|
|
463
|
+
}
|
|
464
|
+
}) {
|
|
465
|
+
Record {
|
|
466
|
+
Id
|
|
467
|
+
Name { value }
|
|
468
|
+
Industry { value }
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
`;
|
|
474
|
+
|
|
475
|
+
// Update mutation
|
|
476
|
+
const UPDATE_ACCOUNT = gql`
|
|
477
|
+
mutation UpdateAccount($id: ID!, $name: String!) {
|
|
478
|
+
uiapi {
|
|
479
|
+
AccountUpdate(input: {
|
|
480
|
+
Account: {
|
|
481
|
+
Id: $id
|
|
482
|
+
Name: $name
|
|
483
|
+
}
|
|
484
|
+
}) {
|
|
485
|
+
Record {
|
|
486
|
+
Id
|
|
487
|
+
Name { value }
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
`;
|
|
493
|
+
|
|
494
|
+
// Delete mutation
|
|
495
|
+
const DELETE_ACCOUNT = gql`
|
|
496
|
+
mutation DeleteAccount($id: ID!) {
|
|
497
|
+
uiapi {
|
|
498
|
+
AccountDelete(input: { Account: { Id: $id } }) {
|
|
499
|
+
Id
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
`;
|
|
504
|
+
|
|
505
|
+
export default class GraphqlAccountMutation extends LightningElement {
|
|
506
|
+
@track accountName = '';
|
|
507
|
+
@track industry = '';
|
|
508
|
+
isLoading = false;
|
|
509
|
+
|
|
510
|
+
handleNameChange(event) {
|
|
511
|
+
this.accountName = event.target.value;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
handleIndustryChange(event) {
|
|
515
|
+
this.industry = event.target.value;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
async handleCreate() {
|
|
519
|
+
if (!this.accountName) return;
|
|
520
|
+
|
|
521
|
+
this.isLoading = true;
|
|
522
|
+
try {
|
|
523
|
+
const result = await executeMutation(CREATE_ACCOUNT, {
|
|
524
|
+
variables: {
|
|
525
|
+
name: this.accountName,
|
|
526
|
+
industry: this.industry || null
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
const newRecord = result.data.uiapi.AccountCreate.Record;
|
|
531
|
+
this.showToast('Success', `Account "${newRecord.Name.value}" created`, 'success');
|
|
532
|
+
this.resetForm();
|
|
533
|
+
} catch (error) {
|
|
534
|
+
this.handleError(error);
|
|
535
|
+
} finally {
|
|
536
|
+
this.isLoading = false;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
async handleUpdate(accountId, newName) {
|
|
541
|
+
try {
|
|
542
|
+
const result = await executeMutation(UPDATE_ACCOUNT, {
|
|
543
|
+
variables: { id: accountId, name: newName }
|
|
544
|
+
});
|
|
545
|
+
this.showToast('Success', 'Account updated', 'success');
|
|
546
|
+
return result.data.uiapi.AccountUpdate.Record;
|
|
547
|
+
} catch (error) {
|
|
548
|
+
this.handleError(error);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
async handleDelete(accountId) {
|
|
553
|
+
try {
|
|
554
|
+
await executeMutation(DELETE_ACCOUNT, {
|
|
555
|
+
variables: { id: accountId }
|
|
556
|
+
});
|
|
557
|
+
this.showToast('Success', 'Account deleted', 'success');
|
|
558
|
+
} catch (error) {
|
|
559
|
+
this.handleError(error);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
handleError(error) {
|
|
564
|
+
const message = error.graphQLErrors
|
|
565
|
+
? error.graphQLErrors.map(e => e.message).join(', ')
|
|
566
|
+
: error.message || 'Unknown error';
|
|
567
|
+
this.showToast('Error', message, 'error');
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
showToast(title, message, variant) {
|
|
571
|
+
this.dispatchEvent(new ShowToastEvent({ title, message, variant }));
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
resetForm() {
|
|
575
|
+
this.accountName = '';
|
|
576
|
+
this.industry = '';
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### GraphQL Mutation Operations
|
|
582
|
+
|
|
583
|
+
| Operation | Mutation Type | Notes |
|
|
584
|
+
|-----------|---------------|-------|
|
|
585
|
+
| **Create** | `{Object}Create` | Can request fields from newly created record |
|
|
586
|
+
| **Update** | `{Object}Update` | Cannot query fields in same request |
|
|
587
|
+
| **Delete** | `{Object}Delete` | Cannot query fields in same request |
|
|
588
|
+
|
|
589
|
+
### allOrNone Parameter
|
|
590
|
+
|
|
591
|
+
Control transaction behavior with `allOrNone` (default: `true`):
|
|
592
|
+
|
|
593
|
+
```javascript
|
|
594
|
+
const BATCH_CREATE = gql`
|
|
595
|
+
mutation BatchCreate($allOrNone: Boolean = true) {
|
|
596
|
+
uiapi(allOrNone: $allOrNone) {
|
|
597
|
+
acc1: AccountCreate(input: { Account: { Name: "Account 1" } }) {
|
|
598
|
+
Record { Id }
|
|
599
|
+
}
|
|
600
|
+
acc2: AccountCreate(input: { Account: { Name: "Account 2" } }) {
|
|
601
|
+
Record { Id }
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
`;
|
|
606
|
+
|
|
607
|
+
// If allOrNone=true: All rollback if any fails
|
|
608
|
+
// If allOrNone=false: Only failed operations rollback
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
## Modal Component Pattern
|
|
614
|
+
|
|
615
|
+
Based on [James Simone's composable modal pattern](https://www.jamessimone.net/blog/joys-of-apex/lwc-composable-modal/).
|
|
616
|
+
|
|
617
|
+
```javascript
|
|
618
|
+
// composableModal.js
|
|
619
|
+
import { LightningElement, api } from 'lwc';
|
|
620
|
+
|
|
621
|
+
const OUTER_MODAL_CLASS = 'outerModalContent';
|
|
622
|
+
|
|
623
|
+
export default class ComposableModal extends LightningElement {
|
|
624
|
+
@api modalHeader;
|
|
625
|
+
@api modalTagline;
|
|
626
|
+
@api modalSaveHandler;
|
|
627
|
+
|
|
628
|
+
_isOpen = false;
|
|
629
|
+
_focusableElements = [];
|
|
630
|
+
|
|
631
|
+
@api
|
|
632
|
+
toggleModal() {
|
|
633
|
+
this._isOpen = !this._isOpen;
|
|
634
|
+
if (this._isOpen) {
|
|
635
|
+
this._focusableElements = [...this.querySelectorAll('.focusable')];
|
|
636
|
+
this._focusFirstElement();
|
|
637
|
+
window.addEventListener('keyup', this._handleKeyUp);
|
|
638
|
+
} else {
|
|
639
|
+
window.removeEventListener('keyup', this._handleKeyUp);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
get modalAriaHidden() {
|
|
644
|
+
return !this._isOpen;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
get modalClass() {
|
|
648
|
+
return this._isOpen
|
|
649
|
+
? 'slds-modal slds-visible slds-fade-in-open'
|
|
650
|
+
: 'slds-modal slds-hidden';
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
get backdropClass() {
|
|
654
|
+
return this._isOpen ? 'slds-backdrop slds-backdrop_open' : 'slds-backdrop';
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
_handleKeyUp = (event) => {
|
|
658
|
+
if (event.code === 'Escape') {
|
|
659
|
+
this.toggleModal();
|
|
660
|
+
} else if (event.code === 'Tab') {
|
|
661
|
+
this._handleTabNavigation(event);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
_handleTabNavigation(event) {
|
|
666
|
+
// Focus trap logic - keep focus within modal
|
|
667
|
+
const activeEl = this.template.activeElement;
|
|
668
|
+
const lastIndex = this._focusableElements.length - 1;
|
|
669
|
+
const currentIndex = this._focusableElements.indexOf(activeEl);
|
|
670
|
+
|
|
671
|
+
if (event.shiftKey && currentIndex === 0) {
|
|
672
|
+
this._focusableElements[lastIndex]?.focus();
|
|
673
|
+
} else if (!event.shiftKey && currentIndex === lastIndex) {
|
|
674
|
+
this._focusFirstElement();
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
_focusFirstElement() {
|
|
679
|
+
if (this._focusableElements.length > 0) {
|
|
680
|
+
this._focusableElements[0].focus();
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
handleBackdropClick(event) {
|
|
685
|
+
if (event.target.classList.contains(OUTER_MODAL_CLASS)) {
|
|
686
|
+
this.toggleModal();
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
handleSave() {
|
|
691
|
+
if (this.modalSaveHandler) {
|
|
692
|
+
this.modalSaveHandler();
|
|
693
|
+
}
|
|
694
|
+
this.toggleModal();
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
disconnectedCallback() {
|
|
698
|
+
window.removeEventListener('keyup', this._handleKeyUp);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
```html
|
|
704
|
+
<!-- composableModal.html -->
|
|
705
|
+
<template>
|
|
706
|
+
<!-- Backdrop -->
|
|
707
|
+
<div class={backdropClass}></div>
|
|
708
|
+
|
|
709
|
+
<!-- Modal -->
|
|
710
|
+
<div class={modalClass}
|
|
711
|
+
role="dialog"
|
|
712
|
+
aria-modal="true"
|
|
713
|
+
aria-hidden={modalAriaHidden}
|
|
714
|
+
aria-labelledby="modal-heading">
|
|
715
|
+
|
|
716
|
+
<div class="slds-modal__container outerModalContent"
|
|
717
|
+
onclick={handleBackdropClick}>
|
|
718
|
+
|
|
719
|
+
<div class="slds-modal__content slds-p-around_medium">
|
|
720
|
+
<!-- Header -->
|
|
721
|
+
<template lwc:if={modalHeader}>
|
|
722
|
+
<h2 id="modal-heading" class="slds-text-heading_medium">
|
|
723
|
+
{modalHeader}
|
|
724
|
+
</h2>
|
|
725
|
+
</template>
|
|
726
|
+
<template lwc:if={modalTagline}>
|
|
727
|
+
<p class="slds-m-top_x-small slds-text-color_weak">
|
|
728
|
+
{modalTagline}
|
|
729
|
+
</p>
|
|
730
|
+
</template>
|
|
731
|
+
|
|
732
|
+
<!-- Slotted Content -->
|
|
733
|
+
<div class="slds-m-top_medium">
|
|
734
|
+
<slot name="modalContent"></slot>
|
|
735
|
+
</div>
|
|
736
|
+
|
|
737
|
+
<!-- Footer -->
|
|
738
|
+
<div class="slds-m-top_medium slds-text-align_right">
|
|
739
|
+
<lightning-button
|
|
740
|
+
label="Cancel"
|
|
741
|
+
onclick={toggleModal}
|
|
742
|
+
class="slds-m-right_x-small focusable">
|
|
743
|
+
</lightning-button>
|
|
744
|
+
<lightning-button
|
|
745
|
+
variant="brand"
|
|
746
|
+
label="Save"
|
|
747
|
+
onclick={handleSave}
|
|
748
|
+
class="focusable">
|
|
749
|
+
</lightning-button>
|
|
750
|
+
</div>
|
|
751
|
+
</div>
|
|
752
|
+
</div>
|
|
753
|
+
</div>
|
|
754
|
+
|
|
755
|
+
<!-- Hidden background content -->
|
|
756
|
+
<div aria-hidden={_isOpen}>
|
|
757
|
+
<slot name="body"></slot>
|
|
758
|
+
</div>
|
|
759
|
+
</template>
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
---
|
|
763
|
+
|
|
764
|
+
## Record Picker Pattern
|
|
765
|
+
|
|
766
|
+
```javascript
|
|
767
|
+
// recordPicker.js
|
|
768
|
+
import { LightningElement, api } from 'lwc';
|
|
769
|
+
|
|
770
|
+
export default class RecordPicker extends LightningElement {
|
|
771
|
+
@api label = 'Select Record';
|
|
772
|
+
@api objectApiName = 'Account';
|
|
773
|
+
@api placeholder = 'Search...';
|
|
774
|
+
@api required = false;
|
|
775
|
+
@api multiSelect = false;
|
|
776
|
+
|
|
777
|
+
_selectedIds = [];
|
|
778
|
+
|
|
779
|
+
@api
|
|
780
|
+
get value() {
|
|
781
|
+
return this.multiSelect ? this._selectedIds : this._selectedIds[0];
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
set value(val) {
|
|
785
|
+
this._selectedIds = Array.isArray(val) ? val : val ? [val] : [];
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
handleChange(event) {
|
|
789
|
+
const recordId = event.detail.recordId;
|
|
790
|
+
if (this.multiSelect) {
|
|
791
|
+
if (!this._selectedIds.includes(recordId)) {
|
|
792
|
+
this._selectedIds = [...this._selectedIds, recordId];
|
|
793
|
+
}
|
|
794
|
+
} else {
|
|
795
|
+
this._selectedIds = recordId ? [recordId] : [];
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
this.dispatchEvent(new CustomEvent('select', {
|
|
799
|
+
detail: {
|
|
800
|
+
recordId: this.value,
|
|
801
|
+
recordIds: this._selectedIds
|
|
802
|
+
}
|
|
803
|
+
}));
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
handleRemove(event) {
|
|
807
|
+
const idToRemove = event.target.dataset.id;
|
|
808
|
+
this._selectedIds = this._selectedIds.filter(id => id !== idToRemove);
|
|
809
|
+
this.dispatchEvent(new CustomEvent('select', {
|
|
810
|
+
detail: { recordIds: this._selectedIds }
|
|
811
|
+
}));
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
```html
|
|
817
|
+
<!-- recordPicker.html -->
|
|
818
|
+
<template>
|
|
819
|
+
<lightning-record-picker
|
|
820
|
+
label={label}
|
|
821
|
+
placeholder={placeholder}
|
|
822
|
+
object-api-name={objectApiName}
|
|
823
|
+
onchange={handleChange}
|
|
824
|
+
required={required}>
|
|
825
|
+
</lightning-record-picker>
|
|
826
|
+
|
|
827
|
+
<template lwc:if={multiSelect}>
|
|
828
|
+
<div class="slds-m-top_x-small">
|
|
829
|
+
<template for:each={_selectedIds} for:item="id">
|
|
830
|
+
<lightning-pill
|
|
831
|
+
key={id}
|
|
832
|
+
label={id}
|
|
833
|
+
data-id={id}
|
|
834
|
+
onremove={handleRemove}>
|
|
835
|
+
</lightning-pill>
|
|
836
|
+
</template>
|
|
837
|
+
</div>
|
|
838
|
+
</template>
|
|
839
|
+
</template>
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
---
|
|
843
|
+
|
|
844
|
+
## Workspace API Pattern
|
|
845
|
+
|
|
846
|
+
```javascript
|
|
847
|
+
// workspaceTabManager.js
|
|
848
|
+
import { LightningElement, wire } from 'lwc';
|
|
849
|
+
import { IsConsoleNavigation, getFocusedTabInfo, openTab, closeTab,
|
|
850
|
+
setTabLabel, setTabIcon, refreshTab } from 'lightning/platformWorkspaceApi';
|
|
851
|
+
|
|
852
|
+
export default class WorkspaceTabManager extends LightningElement {
|
|
853
|
+
@wire(IsConsoleNavigation) isConsole;
|
|
854
|
+
|
|
855
|
+
async openRecordTab(recordId, objectApiName) {
|
|
856
|
+
if (!this.isConsole) return;
|
|
857
|
+
|
|
858
|
+
await openTab({
|
|
859
|
+
recordId,
|
|
860
|
+
focus: true,
|
|
861
|
+
icon: `standard:${objectApiName.toLowerCase()}`,
|
|
862
|
+
label: 'Loading...'
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
async openSubtab(parentTabId, recordId) {
|
|
867
|
+
if (!this.isConsole) return;
|
|
868
|
+
|
|
869
|
+
await openTab({
|
|
870
|
+
parentTabId,
|
|
871
|
+
recordId,
|
|
872
|
+
focus: true
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
async getCurrentTabInfo() {
|
|
877
|
+
if (!this.isConsole) return null;
|
|
878
|
+
return await getFocusedTabInfo();
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
async updateTabLabel(tabId, label) {
|
|
882
|
+
if (!this.isConsole) return;
|
|
883
|
+
await setTabLabel(tabId, label);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
async updateTabIcon(tabId, iconName) {
|
|
887
|
+
if (!this.isConsole) return;
|
|
888
|
+
await setTabIcon(tabId, iconName);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
async refreshCurrentTab() {
|
|
892
|
+
if (!this.isConsole) return;
|
|
893
|
+
const tabInfo = await getFocusedTabInfo();
|
|
894
|
+
await refreshTab(tabInfo.tabId);
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
async closeCurrentTab() {
|
|
898
|
+
if (!this.isConsole) return;
|
|
899
|
+
const tabInfo = await getFocusedTabInfo();
|
|
900
|
+
await closeTab(tabInfo.tabId);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
```
|
|
904
|
+
|
|
905
|
+
---
|
|
906
|
+
|
|
907
|
+
<a id="parent-child-communication"></a>
|
|
908
|
+
|
|
909
|
+
## Parent-Child Communication
|
|
910
|
+
|
|
911
|
+
```javascript
|
|
912
|
+
// parent.js
|
|
913
|
+
import { LightningElement } from 'lwc';
|
|
914
|
+
|
|
915
|
+
export default class Parent extends LightningElement {
|
|
916
|
+
selectedAccountId;
|
|
917
|
+
|
|
918
|
+
handleAccountSelected(event) {
|
|
919
|
+
this.selectedAccountId = event.detail.accountId;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
```html
|
|
925
|
+
<!-- parent.html -->
|
|
926
|
+
<template>
|
|
927
|
+
<c-account-list onaccountselected={handleAccountSelected}></c-account-list>
|
|
928
|
+
<template lwc:if={selectedAccountId}>
|
|
929
|
+
<c-account-detail account-id={selectedAccountId}></c-account-detail>
|
|
930
|
+
</template>
|
|
931
|
+
</template>
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
```javascript
|
|
935
|
+
// child.js (accountList)
|
|
936
|
+
import { LightningElement } from 'lwc';
|
|
937
|
+
|
|
938
|
+
export default class AccountList extends LightningElement {
|
|
939
|
+
handleRowAction(event) {
|
|
940
|
+
const accountId = event.detail.row.Id;
|
|
941
|
+
|
|
942
|
+
// Dispatch event to parent
|
|
943
|
+
this.dispatchEvent(new CustomEvent('accountselected', {
|
|
944
|
+
detail: { accountId },
|
|
945
|
+
bubbles: true,
|
|
946
|
+
composed: false // Don't cross shadow DOM boundaries
|
|
947
|
+
}));
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
---
|
|
953
|
+
|
|
954
|
+
## Sibling Communication (via Parent)
|
|
955
|
+
|
|
956
|
+
When two child components need to communicate but share the same parent, use the **parent as middleware**. This is the recommended pattern for master-detail UIs.
|
|
957
|
+
|
|
958
|
+
```
|
|
959
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
960
|
+
│ SIBLING COMMUNICATION FLOW │
|
|
961
|
+
├─────────────────────────────────────────────────────────────────────┤
|
|
962
|
+
│ │
|
|
963
|
+
│ ┌──────────┐ │
|
|
964
|
+
│ │ Parent │ ← Manages state │
|
|
965
|
+
│ └────┬─────┘ │
|
|
966
|
+
│ ┌─────────┴─────────┐ │
|
|
967
|
+
│ │ │ │
|
|
968
|
+
│ CustomEvent @api property │
|
|
969
|
+
│ (up) (down) │
|
|
970
|
+
│ │ │ │
|
|
971
|
+
│ ┌─────┴─────┐ ┌─────┴─────┐ │
|
|
972
|
+
│ │ Child A │ │ Child B │ │
|
|
973
|
+
│ │ (List) │ │ (Detail) │ │
|
|
974
|
+
│ └───────────┘ └───────────┘ │
|
|
975
|
+
│ │
|
|
976
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
**The flow**:
|
|
980
|
+
1. **Child A** dispatches a custom event (e.g., user selects an account)
|
|
981
|
+
2. **Parent** catches the event and updates its state
|
|
982
|
+
3. **Parent** passes data to **Child B** via `@api` property
|
|
983
|
+
|
|
984
|
+
### Complete Example: Account List → Account Detail
|
|
985
|
+
|
|
986
|
+
```javascript
|
|
987
|
+
// accountContainer.js - Parent orchestrates communication between siblings
|
|
988
|
+
import { LightningElement } from 'lwc';
|
|
989
|
+
|
|
990
|
+
export default class AccountContainer extends LightningElement {
|
|
991
|
+
// State managed at parent level
|
|
992
|
+
selectedAccountId;
|
|
993
|
+
selectedAccountName;
|
|
994
|
+
|
|
995
|
+
// Child A (accountList) fires this event
|
|
996
|
+
handleAccountSelect(event) {
|
|
997
|
+
this.selectedAccountId = event.detail.accountId;
|
|
998
|
+
this.selectedAccountName = event.detail.accountName;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
// Clear selection (triggered by Child B)
|
|
1002
|
+
handleClearSelection() {
|
|
1003
|
+
this.selectedAccountId = null;
|
|
1004
|
+
this.selectedAccountName = null;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
get hasSelection() {
|
|
1008
|
+
return !!this.selectedAccountId;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
```
|
|
1012
|
+
|
|
1013
|
+
```html
|
|
1014
|
+
<!-- accountContainer.html -->
|
|
1015
|
+
<template>
|
|
1016
|
+
<div class="slds-grid slds-gutters">
|
|
1017
|
+
<!-- Child A: Account List -->
|
|
1018
|
+
<div class="slds-col slds-size_1-of-2">
|
|
1019
|
+
<c-account-list
|
|
1020
|
+
onaccountselect={handleAccountSelect}
|
|
1021
|
+
selected-id={selectedAccountId}>
|
|
1022
|
+
</c-account-list>
|
|
1023
|
+
</div>
|
|
1024
|
+
|
|
1025
|
+
<!-- Child B: Account Detail (receives data via @api) -->
|
|
1026
|
+
<div class="slds-col slds-size_1-of-2">
|
|
1027
|
+
<template lwc:if={hasSelection}>
|
|
1028
|
+
<c-account-detail
|
|
1029
|
+
account-id={selectedAccountId}
|
|
1030
|
+
account-name={selectedAccountName}
|
|
1031
|
+
onclearselection={handleClearSelection}>
|
|
1032
|
+
</c-account-detail>
|
|
1033
|
+
</template>
|
|
1034
|
+
<template lwc:else>
|
|
1035
|
+
<div class="slds-box slds-theme_shade">
|
|
1036
|
+
Select an account to view details
|
|
1037
|
+
</div>
|
|
1038
|
+
</template>
|
|
1039
|
+
</div>
|
|
1040
|
+
</div>
|
|
1041
|
+
</template>
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
```javascript
|
|
1045
|
+
// accountList.js - Child A: Dispatches events UP to parent
|
|
1046
|
+
import { LightningElement, api, wire } from 'lwc';
|
|
1047
|
+
import getAccounts from '@salesforce/apex/AccountController.getAccounts';
|
|
1048
|
+
|
|
1049
|
+
export default class AccountList extends LightningElement {
|
|
1050
|
+
@api selectedId; // Highlight selected row (from parent)
|
|
1051
|
+
accounts;
|
|
1052
|
+
error;
|
|
1053
|
+
|
|
1054
|
+
@wire(getAccounts)
|
|
1055
|
+
wiredAccounts({ data, error }) {
|
|
1056
|
+
if (data) {
|
|
1057
|
+
this.accounts = data;
|
|
1058
|
+
this.error = undefined;
|
|
1059
|
+
} else if (error) {
|
|
1060
|
+
this.error = error;
|
|
1061
|
+
this.accounts = undefined;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
handleRowClick(event) {
|
|
1066
|
+
const accountId = event.currentTarget.dataset.id;
|
|
1067
|
+
const accountName = event.currentTarget.dataset.name;
|
|
1068
|
+
|
|
1069
|
+
// Dispatch event to parent (not bubbles - parent listens directly)
|
|
1070
|
+
this.dispatchEvent(new CustomEvent('accountselect', {
|
|
1071
|
+
detail: { accountId, accountName }
|
|
1072
|
+
}));
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
// Computed: Check if row should be highlighted
|
|
1076
|
+
getRowClass(accountId) {
|
|
1077
|
+
return accountId === this.selectedId
|
|
1078
|
+
? 'slds-item slds-is-selected'
|
|
1079
|
+
: 'slds-item';
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
```javascript
|
|
1085
|
+
// accountDetail.js - Child B: Receives data via @api from parent
|
|
1086
|
+
import { LightningElement, api, wire } from 'lwc';
|
|
1087
|
+
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
|
|
1088
|
+
import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';
|
|
1089
|
+
import REVENUE_FIELD from '@salesforce/schema/Account.AnnualRevenue';
|
|
1090
|
+
|
|
1091
|
+
const FIELDS = [INDUSTRY_FIELD, REVENUE_FIELD];
|
|
1092
|
+
|
|
1093
|
+
export default class AccountDetail extends LightningElement {
|
|
1094
|
+
@api accountId; // Received from parent
|
|
1095
|
+
@api accountName; // Received from parent
|
|
1096
|
+
|
|
1097
|
+
@wire(getRecord, { recordId: '$accountId', fields: FIELDS })
|
|
1098
|
+
account;
|
|
1099
|
+
|
|
1100
|
+
get industry() {
|
|
1101
|
+
return getFieldValue(this.account.data, INDUSTRY_FIELD);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
get revenue() {
|
|
1105
|
+
return getFieldValue(this.account.data, REVENUE_FIELD);
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
get isLoading() {
|
|
1109
|
+
return !this.account.data && !this.account.error;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
handleClose() {
|
|
1113
|
+
// Dispatch event back to parent to clear selection
|
|
1114
|
+
this.dispatchEvent(new CustomEvent('clearselection'));
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
```
|
|
1118
|
+
|
|
1119
|
+
### When to Use Sibling Pattern vs LMS
|
|
1120
|
+
|
|
1121
|
+
| Scenario | Sibling Pattern | LMS |
|
|
1122
|
+
|----------|-----------------|-----|
|
|
1123
|
+
| Components share same parent | ✅ Recommended | ❌ Overkill |
|
|
1124
|
+
| State is simple (1-2 values) | ✅ | ❌ |
|
|
1125
|
+
| Need bidirectional updates | ✅ | ✅ |
|
|
1126
|
+
| Components in different DOM trees | ❌ | ✅ Required |
|
|
1127
|
+
| Cross-framework (LWC ↔ Aura) | ❌ | ✅ Required |
|
|
1128
|
+
| Many consumers need same data | ❌ Consider LMS | ✅ |
|
|
1129
|
+
| Component hierarchy is deep (4+ levels) | ❌ Consider LMS | ✅ |
|
|
1130
|
+
|
|
1131
|
+
**Rule of thumb**: If components share a parent and data flow is simple, use sibling pattern. If components are "far apart" in the DOM or you need pub/sub semantics, use LMS.
|
|
1132
|
+
|
|
1133
|
+
---
|
|
1134
|
+
|
|
1135
|
+
## Navigation Patterns
|
|
1136
|
+
|
|
1137
|
+
```javascript
|
|
1138
|
+
// navigator.js
|
|
1139
|
+
import { LightningElement } from 'lwc';
|
|
1140
|
+
import { NavigationMixin } from 'lightning/navigation';
|
|
1141
|
+
|
|
1142
|
+
export default class Navigator extends NavigationMixin(LightningElement) {
|
|
1143
|
+
|
|
1144
|
+
navigateToRecord(recordId, objectApiName = 'Account') {
|
|
1145
|
+
this[NavigationMixin.Navigate]({
|
|
1146
|
+
type: 'standard__recordPage',
|
|
1147
|
+
attributes: {
|
|
1148
|
+
recordId,
|
|
1149
|
+
objectApiName,
|
|
1150
|
+
actionName: 'view'
|
|
1151
|
+
}
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
navigateToList(objectApiName, filterName = 'Recent') {
|
|
1156
|
+
this[NavigationMixin.Navigate]({
|
|
1157
|
+
type: 'standard__objectPage',
|
|
1158
|
+
attributes: {
|
|
1159
|
+
objectApiName,
|
|
1160
|
+
actionName: 'list'
|
|
1161
|
+
},
|
|
1162
|
+
state: { filterName }
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
navigateToNewRecord(objectApiName, defaultValues = {}) {
|
|
1167
|
+
this[NavigationMixin.Navigate]({
|
|
1168
|
+
type: 'standard__objectPage',
|
|
1169
|
+
attributes: {
|
|
1170
|
+
objectApiName,
|
|
1171
|
+
actionName: 'new'
|
|
1172
|
+
},
|
|
1173
|
+
state: {
|
|
1174
|
+
defaultFieldValues: Object.entries(defaultValues)
|
|
1175
|
+
.map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
|
|
1176
|
+
.join(',')
|
|
1177
|
+
}
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
navigateToRelatedList(recordId, relationshipApiName) {
|
|
1182
|
+
this[NavigationMixin.Navigate]({
|
|
1183
|
+
type: 'standard__recordRelationshipPage',
|
|
1184
|
+
attributes: {
|
|
1185
|
+
recordId,
|
|
1186
|
+
relationshipApiName,
|
|
1187
|
+
actionName: 'view'
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
navigateToNamedPage(pageName, params = {}) {
|
|
1193
|
+
this[NavigationMixin.Navigate]({
|
|
1194
|
+
type: 'standard__namedPage',
|
|
1195
|
+
attributes: {
|
|
1196
|
+
pageName
|
|
1197
|
+
},
|
|
1198
|
+
state: params
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
```
|
|
1203
|
+
|
|
1204
|
+
---
|
|
1205
|
+
|
|
1206
|
+
## TypeScript Patterns
|
|
1207
|
+
|
|
1208
|
+
### TypeScript Component Pattern
|
|
1209
|
+
|
|
1210
|
+
```typescript
|
|
1211
|
+
// accountList.ts
|
|
1212
|
+
import { LightningElement, api, wire, track } from 'lwc';
|
|
1213
|
+
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
|
|
1214
|
+
import getAccounts from '@salesforce/apex/AccountController.getAccounts';
|
|
1215
|
+
import ACCOUNT_NAME_FIELD from '@salesforce/schema/Account.Name';
|
|
1216
|
+
|
|
1217
|
+
// Define interfaces for type safety
|
|
1218
|
+
interface AccountRecord {
|
|
1219
|
+
Id: string;
|
|
1220
|
+
Name: string;
|
|
1221
|
+
Industry?: string;
|
|
1222
|
+
AnnualRevenue?: number;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
interface WireResult<T> {
|
|
1226
|
+
data?: T;
|
|
1227
|
+
error?: Error;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
export default class AccountList extends LightningElement {
|
|
1231
|
+
// Typed @api properties
|
|
1232
|
+
@api recordId: string | undefined;
|
|
1233
|
+
|
|
1234
|
+
@api
|
|
1235
|
+
get maxRecords(): number {
|
|
1236
|
+
return this._maxRecords;
|
|
1237
|
+
}
|
|
1238
|
+
set maxRecords(value: number) {
|
|
1239
|
+
this._maxRecords = value;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
// Typed @track properties
|
|
1243
|
+
@track private _accounts: AccountRecord[] = [];
|
|
1244
|
+
@track private _error: string | null = null;
|
|
1245
|
+
|
|
1246
|
+
private _maxRecords: number = 10;
|
|
1247
|
+
private _wiredResult: WireResult<AccountRecord[]> | undefined;
|
|
1248
|
+
|
|
1249
|
+
// Typed wire service
|
|
1250
|
+
@wire(getAccounts, { maxRecords: '$maxRecords' })
|
|
1251
|
+
wiredAccounts(result: WireResult<AccountRecord[]>): void {
|
|
1252
|
+
this._wiredResult = result;
|
|
1253
|
+
const { data, error } = result;
|
|
1254
|
+
|
|
1255
|
+
if (data) {
|
|
1256
|
+
this._accounts = data;
|
|
1257
|
+
this._error = null;
|
|
1258
|
+
} else if (error) {
|
|
1259
|
+
this._error = this.reduceErrors(error);
|
|
1260
|
+
this._accounts = [];
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
// Typed getters
|
|
1265
|
+
get accounts(): AccountRecord[] {
|
|
1266
|
+
return this._accounts;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
get hasAccounts(): boolean {
|
|
1270
|
+
return this._accounts.length > 0;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
// Typed event handlers
|
|
1274
|
+
handleSelect(event: CustomEvent<{ accountId: string }>): void {
|
|
1275
|
+
const { accountId } = event.detail;
|
|
1276
|
+
this.dispatchEvent(new CustomEvent('accountselected', {
|
|
1277
|
+
detail: { accountId },
|
|
1278
|
+
bubbles: true,
|
|
1279
|
+
composed: true
|
|
1280
|
+
}));
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
// Typed utility methods
|
|
1284
|
+
private reduceErrors(error: Error | Error[]): string {
|
|
1285
|
+
const errors = Array.isArray(error) ? error : [error];
|
|
1286
|
+
return errors
|
|
1287
|
+
.filter((e): e is Error => e !== null)
|
|
1288
|
+
.map(e => e.message || 'Unknown error')
|
|
1289
|
+
.join('; ');
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
```
|
|
1293
|
+
|
|
1294
|
+
### TypeScript Jest Test Pattern
|
|
1295
|
+
|
|
1296
|
+
```typescript
|
|
1297
|
+
// accountList.test.ts
|
|
1298
|
+
import { createElement, LightningElement } from 'lwc';
|
|
1299
|
+
import AccountList from 'c/accountList';
|
|
1300
|
+
import getAccounts from '@salesforce/apex/AccountController.getAccounts';
|
|
1301
|
+
|
|
1302
|
+
// Type definitions for tests
|
|
1303
|
+
interface AccountRecord {
|
|
1304
|
+
Id: string;
|
|
1305
|
+
Name: string;
|
|
1306
|
+
Industry?: string;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
// Mock Apex
|
|
1310
|
+
jest.mock(
|
|
1311
|
+
'@salesforce/apex/AccountController.getAccounts',
|
|
1312
|
+
() => ({ default: jest.fn() }),
|
|
1313
|
+
{ virtual: true }
|
|
1314
|
+
);
|
|
1315
|
+
|
|
1316
|
+
const MOCK_ACCOUNTS: AccountRecord[] = [
|
|
1317
|
+
{ Id: '001xx000003DGQ', Name: 'Acme Corp', Industry: 'Technology' }
|
|
1318
|
+
];
|
|
1319
|
+
|
|
1320
|
+
describe('c-account-list', () => {
|
|
1321
|
+
let element: LightningElement & { maxRecords?: number };
|
|
1322
|
+
|
|
1323
|
+
afterEach(() => {
|
|
1324
|
+
while (document.body.firstChild) {
|
|
1325
|
+
document.body.removeChild(document.body.firstChild);
|
|
1326
|
+
}
|
|
1327
|
+
jest.clearAllMocks();
|
|
1328
|
+
});
|
|
1329
|
+
|
|
1330
|
+
it('displays accounts after data loads', async () => {
|
|
1331
|
+
(getAccounts as jest.Mock).mockResolvedValue(MOCK_ACCOUNTS);
|
|
1332
|
+
|
|
1333
|
+
element = createElement('c-account-list', { is: AccountList });
|
|
1334
|
+
document.body.appendChild(element);
|
|
1335
|
+
|
|
1336
|
+
await Promise.resolve();
|
|
1337
|
+
|
|
1338
|
+
const items = element.shadowRoot?.querySelectorAll('.slds-item');
|
|
1339
|
+
expect(items?.length).toBe(MOCK_ACCOUNTS.length);
|
|
1340
|
+
});
|
|
1341
|
+
});
|
|
1342
|
+
```
|
|
1343
|
+
|
|
1344
|
+
### TypeScript Features for LWC
|
|
1345
|
+
|
|
1346
|
+
| Feature | LWC Support | Notes |
|
|
1347
|
+
|---------|-------------|-------|
|
|
1348
|
+
| **Interface definitions** | ✅ | Define shapes for records, events, props |
|
|
1349
|
+
| **Typed @api properties** | ✅ | Getter/setter patterns with types |
|
|
1350
|
+
| **Typed @wire results** | ✅ | Generic `WireResult<T>` pattern |
|
|
1351
|
+
| **Typed event handlers** | ✅ | `CustomEvent<T>` for event detail typing |
|
|
1352
|
+
| **Private class fields** | ✅ | Use `private` keyword |
|
|
1353
|
+
| **Strict null checking** | ✅ | Optional chaining `?.` and nullish coalescing `??` |
|
|
1354
|
+
|
|
1355
|
+
---
|
|
1356
|
+
|
|
1357
|
+
## Apex Controller Patterns
|
|
1358
|
+
|
|
1359
|
+
### Cacheable Methods (for @wire)
|
|
1360
|
+
|
|
1361
|
+
```apex
|
|
1362
|
+
public with sharing class LwcController {
|
|
1363
|
+
|
|
1364
|
+
@AuraEnabled(cacheable=true)
|
|
1365
|
+
public static List<Account> getAccounts(String searchTerm) {
|
|
1366
|
+
String searchKey = '%' + String.escapeSingleQuotes(searchTerm) + '%';
|
|
1367
|
+
return [
|
|
1368
|
+
SELECT Id, Name, Industry, AnnualRevenue
|
|
1369
|
+
FROM Account
|
|
1370
|
+
WHERE Name LIKE :searchKey
|
|
1371
|
+
WITH SECURITY_ENFORCED
|
|
1372
|
+
ORDER BY Name
|
|
1373
|
+
LIMIT 50
|
|
1374
|
+
];
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
@AuraEnabled(cacheable=true)
|
|
1378
|
+
public static List<PicklistOption> getIndustryOptions() {
|
|
1379
|
+
List<PicklistOption> options = new List<PicklistOption>();
|
|
1380
|
+
Schema.DescribeFieldResult fieldResult =
|
|
1381
|
+
Account.Industry.getDescribe();
|
|
1382
|
+
for (Schema.PicklistEntry entry : fieldResult.getPicklistValues()) {
|
|
1383
|
+
if (entry.isActive()) {
|
|
1384
|
+
options.add(new PicklistOption(entry.getLabel(), entry.getValue()));
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
return options;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
public class PicklistOption {
|
|
1391
|
+
@AuraEnabled public String label;
|
|
1392
|
+
@AuraEnabled public String value;
|
|
1393
|
+
|
|
1394
|
+
public PicklistOption(String label, String value) {
|
|
1395
|
+
this.label = label;
|
|
1396
|
+
this.value = value;
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
```
|
|
1401
|
+
|
|
1402
|
+
### Non-Cacheable Methods (for DML)
|
|
1403
|
+
|
|
1404
|
+
```apex
|
|
1405
|
+
@AuraEnabled
|
|
1406
|
+
public static Account createAccount(String accountJson) {
|
|
1407
|
+
Account acc = (Account) JSON.deserialize(accountJson, Account.class);
|
|
1408
|
+
|
|
1409
|
+
// FLS check
|
|
1410
|
+
SObjectAccessDecision decision = Security.stripInaccessible(
|
|
1411
|
+
AccessType.CREATABLE,
|
|
1412
|
+
new List<Account>{ acc }
|
|
1413
|
+
);
|
|
1414
|
+
|
|
1415
|
+
insert decision.getRecords();
|
|
1416
|
+
return (Account) decision.getRecords()[0];
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
@AuraEnabled
|
|
1420
|
+
public static void deleteAccounts(List<Id> accountIds) {
|
|
1421
|
+
if (accountIds == null || accountIds.isEmpty()) {
|
|
1422
|
+
throw new AuraHandledException('No accounts to delete');
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
List<Account> toDelete = [
|
|
1426
|
+
SELECT Id FROM Account
|
|
1427
|
+
WHERE Id IN :accountIds
|
|
1428
|
+
WITH SECURITY_ENFORCED
|
|
1429
|
+
];
|
|
1430
|
+
|
|
1431
|
+
delete toDelete;
|
|
1432
|
+
}
|
|
1433
|
+
```
|
|
1434
|
+
|
|
1435
|
+
### Error Handling Pattern
|
|
1436
|
+
|
|
1437
|
+
```apex
|
|
1438
|
+
@AuraEnabled
|
|
1439
|
+
public static List<Contact> getContactsWithErrorHandling(Id accountId) {
|
|
1440
|
+
try {
|
|
1441
|
+
if (accountId == null) {
|
|
1442
|
+
throw new AuraHandledException('Account ID is required');
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
List<Contact> contacts = [
|
|
1446
|
+
SELECT Id, Name, Email, Phone
|
|
1447
|
+
FROM Contact
|
|
1448
|
+
WHERE AccountId = :accountId
|
|
1449
|
+
WITH SECURITY_ENFORCED
|
|
1450
|
+
ORDER BY Name
|
|
1451
|
+
LIMIT 100
|
|
1452
|
+
];
|
|
1453
|
+
|
|
1454
|
+
return contacts;
|
|
1455
|
+
} catch (Exception e) {
|
|
1456
|
+
throw new AuraHandledException('Error fetching contacts: ' + e.getMessage());
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
```
|
|
1460
|
+
|
|
1461
|
+
---
|
|
1462
|
+
|
|
1463
|
+
## Related Resources
|
|
1464
|
+
|
|
1465
|
+
- [lms-guide.md](lms-guide.md) - Lightning Message Service deep dive
|
|
1466
|
+
- [jest-testing.md](jest-testing.md) - Advanced testing patterns
|
|
1467
|
+
- [accessibility-guide.md](accessibility-guide.md) - WCAG compliance
|
|
1468
|
+
- [performance-guide.md](performance-guide.md) - Optimization techniques
|
|
1469
|
+
|
|
1470
|
+
---
|
|
1471
|
+
|
|
1472
|
+
## External References
|
|
1473
|
+
|
|
1474
|
+
- [PICKLES Framework](https://www.salesforceben.com/the-ideal-framework-for-architecting-salesforce-lightning-web-components/) — David Picksley, Third Eye Consulting
|
|
1475
|
+
- [LWC Recipes (GitHub)](https://github.com/trailheadapps/lwc-recipes)
|
|
1476
|
+
- [James Simone - Composable Modal](https://www.jamessimone.net/blog/joys-of-apex/lwc-composable-modal/)
|