agent-docs 1.2.0 → 1.3.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/PLAN.md +6 -6
- package/README.md +3 -0
- package/docs/LIGHTNINGBASECOMPONENTS.md +328 -0
- package/docs/LWCHTMLTEMPLATES.md +314 -0
- package/docs/PMD.md +265 -761
- package/docs/PRETTIER.md +1 -39
- package/package.json +1 -1
package/PLAN.md
CHANGED
|
@@ -607,7 +607,7 @@ Standard MIT license with copyright holder: starch-uk
|
|
|
607
607
|
|
|
608
608
|
- Name: `agent-docs`
|
|
609
609
|
- Type: module
|
|
610
|
-
- Version: `1.
|
|
610
|
+
- Version: `1.0.0`
|
|
611
611
|
- Scripts: `format`, `format:fix`, `format:check`, `postinstall`
|
|
612
612
|
- Dev dependencies: `prettier`
|
|
613
613
|
- Engines: Node.js >= 20.0.0
|
|
@@ -646,11 +646,11 @@ The plan should:
|
|
|
646
646
|
APEXDOC.md, CML.md, CODEANALYZER.md, CONTEXTDEFINITIONS.md, ESLINT.md,
|
|
647
647
|
ESLINTJSDOC.md, FIELDSERVICE.md, GRAPHBINARY.md, GRAPHENGINE.md, GRAPHML.md,
|
|
648
648
|
GRAPHSON.md, GREMLIN.md, GRYO.md, HUSKY.md, JEST.md, JORJE.md, JSDOC.md,
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
`1.0.0` (or appropriate version
|
|
652
|
-
versioning system is first implemented.
|
|
653
|
-
going forward using the same semver system.
|
|
649
|
+
LIGHTNINGBASECOMPONENTS.md, LWCHTMLTEMPLATES.md, PMD.md, PNPM.md, PRETTIER.md,
|
|
650
|
+
PRETTIERAPEX.md, REVENUETRANSACTIONMANAGEMENT.md, TINKERPOP.md, VITEST.md,
|
|
651
|
+
XPATH31.md) need to be initialized with version `1.0.0` (or appropriate version
|
|
652
|
+
based on their current state) when the versioning system is first implemented.
|
|
653
|
+
These existing docs will be tracked going forward using the same semver system.
|
|
654
654
|
- Describe how scripts can help with versioning by:
|
|
655
655
|
- Reading markdown files and detecting headers/sections
|
|
656
656
|
- Comparing current state with the latest commit in `main` branch
|
package/README.md
CHANGED
|
@@ -101,6 +101,9 @@ structured format optimized for AI agent consumption:
|
|
|
101
101
|
- **[JEST.md](docs/JEST.md)** - Jest testing framework reference
|
|
102
102
|
- **[JORJE.md](docs/JORJE.md)** - Jorje Apex parser reference
|
|
103
103
|
- **[JSDOC.md](docs/JSDOC.md)** - JSDoc documentation generator reference
|
|
104
|
+
- **[LIGHTNINGBASECOMPONENTS.md](docs/LIGHTNINGBASECOMPONENTS.md)** - Lightning Base
|
|
105
|
+
Components reference
|
|
106
|
+
- **[LWCHTMLTEMPLATES.md](docs/LWCHTMLTEMPLATES.md)** - LWC HTML Templates reference
|
|
104
107
|
- **[PMD.md](docs/PMD.md)** - PMD static analysis tool reference (includes Apex
|
|
105
108
|
AST reference and suppressing warnings)
|
|
106
109
|
- **[PNPM.md](docs/PNPM.md)** - pnpm package manager reference
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
# Lightning Base Components
|
|
2
|
+
|
|
3
|
+
> **Version**: 1.0.0
|
|
4
|
+
|
|
5
|
+
## Guidelines
|
|
6
|
+
- Prefer base components over custom HTML - built-in SLDS, a11y, less maintenance
|
|
7
|
+
- A11y: Always provide accessible names via `label`, `aria-label`, `alternative-text`
|
|
8
|
+
- Events: `onclick` (buttons), `onchange`/`onselect` (inputs/menus), `onstatuschange` (flows), `onuploadfinished` (files), `onscan`/`onerror` (barcode)
|
|
9
|
+
- Nav: Use `lightning/navigation` not `href`
|
|
10
|
+
- Style: SLDS utilities + `--slds-c-*` CSS vars. Avoid internal overrides
|
|
11
|
+
- Files: Guest uploads via org prefs. `file-field-name`/`value` for guests, `record-id` for auth users
|
|
12
|
+
- Limits: combobox no multi-select/autocomplete; barcode/click-to-dial no iFrames; carousel max 6 images; flow restrictions on LWR+custom components
|
|
13
|
+
|
|
14
|
+
## ValidityState & Validation
|
|
15
|
+
**ValidityState props** (all boolean): `valid`, `valueMissing`, `typeMismatch`, `patternMismatch`, `tooLong`, `tooShort`, `rangeOverflow`, `rangeUnderflow`, `stepMismatch`, `badInput`, `customError`
|
|
16
|
+
|
|
17
|
+
**Methods**: `setCustomValidity(msg)`, `reportValidity()`, `checkValidity()`
|
|
18
|
+
|
|
19
|
+
**Custom messages**: `message-when-value-missing`, `message-when-type-mismatch`, `message-when-pattern-mismatch`, `message-when-too-long`, `message-when-too-short`, `message-when-range-overflow`, `message-when-range-underflow`, `message-when-step-mismatch`, `message-when-bad-input`
|
|
20
|
+
|
|
21
|
+
Start w/ LDS components for built-in validation. Client+server validation together.
|
|
22
|
+
|
|
23
|
+
## Components
|
|
24
|
+
|
|
25
|
+
**Legend**: `*` = required, `|` = options, `→` = returns
|
|
26
|
+
|
|
27
|
+
### lightning-accordion-section
|
|
28
|
+
Nest in `lightning-accordion`
|
|
29
|
+
- `class`, `heading-level` (1-6), `label`, `name`*
|
|
30
|
+
- Slots: default, `actions`
|
|
31
|
+
- ARIA: `role="listitem"`, `aria-expanded`, `aria-controls`
|
|
32
|
+
- Event: `onactive`
|
|
33
|
+
|
|
34
|
+
### lightning-alert (lightning/alert)
|
|
35
|
+
`LightningAlert.open(config)` → Promise
|
|
36
|
+
- `label`, `message`*, `theme` (default|shade|inverse|alt-inverse|success|info|warning|error|offline), `variant` (header|headerless)
|
|
37
|
+
- ARIA: `role="alertdialog"`, focus trap
|
|
38
|
+
|
|
39
|
+
### lightning-avatar
|
|
40
|
+
- `alternative-text`* (if informational), `class`, `fallback-icon-name` (standard/custom only), `initials`, `src`, `variant` (circle|square)
|
|
41
|
+
- Initials+fallback: initials show w/ icon bg color
|
|
42
|
+
|
|
43
|
+
### lightning-badge
|
|
44
|
+
- `class`, `icon-name`, `icon-position` (end|start), `label`
|
|
45
|
+
- No links/nested elements. Use `slds-theme_*` for colors
|
|
46
|
+
|
|
47
|
+
### lightning-barcode-scanner
|
|
48
|
+
Mobile only, no iFrames
|
|
49
|
+
- `disabled`, `disabled-alternative-text`, `disabled-icon-src`, `enable-continuous-scan`, `enabled-alternative-text`, `enabled-icon-src`, `icon-size`
|
|
50
|
+
- Events: `onerror`, `onscan` → `event.detail.scannedBarcodes`
|
|
51
|
+
|
|
52
|
+
### lightning-breadcrumb
|
|
53
|
+
Nest in `lightning-breadcrumbs`
|
|
54
|
+
- `class`, `href` (defaults "#"), `label`*, `name`
|
|
55
|
+
- Event: `onclick` (use w/ lightning/navigation)
|
|
56
|
+
|
|
57
|
+
### lightning-breadcrumbs
|
|
58
|
+
- `class`
|
|
59
|
+
- Slots: `lightning-breadcrumb`
|
|
60
|
+
- ARIA: `role="navigation"`, last item `aria-current="page"`
|
|
61
|
+
|
|
62
|
+
### lightning-button
|
|
63
|
+
- `accesskey`, `aria-atomic`, `aria-controls`, `aria-describedby`, `aria-expanded`, `aria-haspopup`, `aria-label`, `aria-labelledby`, `aria-live`, `class`, `disabled`, `disable-animation`, `icon-name` (utility), `icon-position` (end|start), `label`*, `stretch`, `tabindex`, `title`, `type` (button|reset|submit), `variant` (base|brand|brand-outline|destructive|destructive-text|inverse|neutral|success)
|
|
64
|
+
- Event: `onclick`
|
|
65
|
+
- Use button-icon for icon-only. Min 44x44px mobile
|
|
66
|
+
|
|
67
|
+
### lightning-button-group
|
|
68
|
+
- `class`
|
|
69
|
+
- Slots: `lightning-button*`
|
|
70
|
+
|
|
71
|
+
### lightning-button-icon-stateful
|
|
72
|
+
- `accesskey`, `alternative-text`, `aria-atomic`, `aria-controls`, `aria-describedby`, `aria-expanded`, `aria-haspopup`, `aria-label`*, `aria-live`, `class`, `disabled`, `icon-name`* (utility), `selected`, `size`, `variant` (border|border-filled|border-inverse)
|
|
73
|
+
- ARIA: `aria-pressed`
|
|
74
|
+
- Event: `onclick`
|
|
75
|
+
|
|
76
|
+
### lightning-button-menu
|
|
77
|
+
- `alternative-text`, `class`, `disabled`, `icon-name`, `icon-size`, `is-draft`, `draft-alternative-text`, `is-loading`, `loading-state-alternative-text`, `label`, `menu-alignment`, `size`, `tooltip`, `title`, `variant` (bare|bare-inverse|border|border-filled|border-inverse|container)
|
|
78
|
+
- Slots: `lightning-menu-item`, `lightning-menu-divider`, `lightning-menu-subheader`
|
|
79
|
+
- ARIA: `aria-haspopup="true"`, `aria-expanded`
|
|
80
|
+
- Events: `onclose`, `onopen`, `onselect`
|
|
81
|
+
|
|
82
|
+
### lightning-button-stateful
|
|
83
|
+
- `accesskey`, `aria-*` (same as button), `class`, `disabled`, `icon-name` (utility), `label-when-hover`, `label-when-off`, `label-when-on`, `selected`, `tabindex`, `variant` (brand|destructive|inverse|neutral|success|text)
|
|
84
|
+
- ARIA: `aria-pressed`, `aria-live="polite"`
|
|
85
|
+
- Event: `onclick` (toggle selected)
|
|
86
|
+
|
|
87
|
+
### lightning-card
|
|
88
|
+
- `class`, `icon-name`, `title`
|
|
89
|
+
- Slots: `actions`, `footer`, `title`, default
|
|
90
|
+
|
|
91
|
+
### lightning-carousel
|
|
92
|
+
Max 6 images
|
|
93
|
+
- `class`, `disable-auto-refresh`, `disable-auto-scroll`, `scroll-duration`
|
|
94
|
+
- Slots: `lightning-carousel-image`
|
|
95
|
+
- ARIA: indicators `role="tablist"`
|
|
96
|
+
|
|
97
|
+
### lightning-carousel-image
|
|
98
|
+
- `alternative-text`, `description`, `header`, `href`, `src`*
|
|
99
|
+
|
|
100
|
+
### lightning-click-to-dial
|
|
101
|
+
Lightning Experience only, no iFrames. Requires Open CTI `enableClickToDial`
|
|
102
|
+
- `record-id`, `value` (phone)
|
|
103
|
+
- Event: `onclicktodial`
|
|
104
|
+
|
|
105
|
+
### lightning-combobox
|
|
106
|
+
No multi-select/autocomplete. Mobile issues - consider HTML `<select>`
|
|
107
|
+
- `autocomplete`, `class`, `label`*, `message-when-value-missing`, `options`* [{value,label,description?}], `required`, `validity`, `value`, `variant` (label-hidden)
|
|
108
|
+
- Events: `onchange` → `event.detail.selectedValue`, `onopen`
|
|
109
|
+
|
|
110
|
+
### lightning-dynamic-icon
|
|
111
|
+
- `alternative-text` (provide on small screens), `type`* (ellie|eq|score|strength|trend|waffle)
|
|
112
|
+
|
|
113
|
+
### lightning-file-upload
|
|
114
|
+
Max 25 files, 2GB each. Guest: create `*fileupload__c` field
|
|
115
|
+
- `accept`, `class`, `file-field-name`, `file-field-value`, `label`, `record-id`
|
|
116
|
+
- Event: `onuploadfinished` → `event.detail.files` [{name,documentId}]
|
|
117
|
+
|
|
118
|
+
### lightning-flow
|
|
119
|
+
Custom components unsupported on LWR
|
|
120
|
+
- `flow-api-name`*, `flow-input-variables`, `flow-finish-behavior` (NONE|RESTART)
|
|
121
|
+
- Event: `onstatuschange` → status, outputVariables
|
|
122
|
+
|
|
123
|
+
### lightning-formatted-address
|
|
124
|
+
Format by user locale. Links Google Maps
|
|
125
|
+
- `city`, `country`, `disabled`, `latitude`, `longitude`, `postal-code`, `province`, `show-static-map`, `street`, `variant` (plain)
|
|
126
|
+
|
|
127
|
+
### lightning-formatted-email
|
|
128
|
+
- `bcc`, `body`, `cc`, `disable-linkify`, `hide-icon`, `label`, `subject`, `value`
|
|
129
|
+
|
|
130
|
+
### lightning-formatted-location
|
|
131
|
+
- `latitude`*, `longitude`*
|
|
132
|
+
|
|
133
|
+
### lightning-formatted-number
|
|
134
|
+
User locale
|
|
135
|
+
- `currency-code`, `currency-display-as` (code|name|symbol), `format-style` (currency|decimal|percent|percent-fixed), `maximum-fraction-digits`, `maximum-significant-digits`, `minimum-fraction-digits`, `minimum-significant-digits`, `value`*
|
|
136
|
+
|
|
137
|
+
### lightning-formatted-rich-text
|
|
138
|
+
- `disable-linkify`, `value`
|
|
139
|
+
- Supported: a, div, p, h1-h6, strong, em, ul, ol, li, table, img, etc.
|
|
140
|
+
|
|
141
|
+
### lightning-formatted-time
|
|
142
|
+
- `value`* (ISO8601)
|
|
143
|
+
|
|
144
|
+
### lightning-input
|
|
145
|
+
- `accept`, `aria-describedby`, `aria-labelledby`, `autocomplete`, `checked`, `class`, `date-aria-describedby`, `date-aria-labelledby`, `disabled`, `field-level-help`, `files`, `label`*, `max`, `maxlength`, `message-when-*` (all validation msgs), `min`, `minlength`, `multiple`, `name`, `pattern`, `placeholder`, `readonly`, `required`, `step`, `time-aria-describedby`, `time-aria-labelledby`, `type` (checkbox|checkbox-button|color|date|datetime|datetime-local|email|file|number|password|search|tel|text|time|toggle|url), `validity`, `value`, `variant` (label-hidden|label-inline|label-stacked|standard)
|
|
146
|
+
- Events: `onblur`, `onchange`, `onfocus`, `oninput`
|
|
147
|
+
|
|
148
|
+
### lightning-input-field
|
|
149
|
+
- `field-name`*, `required`, `value`, `variant`
|
|
150
|
+
- Event: `onchange`
|
|
151
|
+
|
|
152
|
+
### lightning-input-location
|
|
153
|
+
Validates lat -90/90, lon -180/180
|
|
154
|
+
- `city`, `country`, `latitude`, `longitude`, `postal-code`, `province`, `street`
|
|
155
|
+
- Event: `onchange` → lat, lon
|
|
156
|
+
|
|
157
|
+
### lightning-layout-item
|
|
158
|
+
- `flexibility`, `large-device-size` (1-12), `medium-device-size`, `padding`, `size`, `small-device-size`
|
|
159
|
+
|
|
160
|
+
### lightning-map
|
|
161
|
+
- `center`, `list-view`, `map-markers`*, `markers-title`, `show-footer-address`, `zoom-level`
|
|
162
|
+
|
|
163
|
+
### lightning-menu-divider
|
|
164
|
+
- `variant` (compact|default)
|
|
165
|
+
|
|
166
|
+
### lightning-menu-item
|
|
167
|
+
- `accesskey`, `checked`, `class`, `draft-alternative-text`, `href`, `icon-name`, `icon-type` (color|standard), `is-draft`, `label`, `prefix-icon-name`, `tabindex`, `target`, `value`
|
|
168
|
+
- Selection via parent `onselect`
|
|
169
|
+
|
|
170
|
+
### lightning-menu-subheader
|
|
171
|
+
- `label`*
|
|
172
|
+
|
|
173
|
+
### lightning-modal (lightning/modal)
|
|
174
|
+
Extend `LightningModal`
|
|
175
|
+
- `description`, `disableClose`, `label`*, `size` (full|large|medium|small)
|
|
176
|
+
- ARIA: focus trap
|
|
177
|
+
- Events bubble to opener
|
|
178
|
+
|
|
179
|
+
### lightning-modal-body/footer
|
|
180
|
+
Slots: default
|
|
181
|
+
|
|
182
|
+
### lightning-modal-header
|
|
183
|
+
- `icon-assistive-text`, `icon-name`, `label`*
|
|
184
|
+
|
|
185
|
+
### lightning-omnistudio-flexcard
|
|
186
|
+
- `flexcard-name`*, `input`, `record-id`
|
|
187
|
+
|
|
188
|
+
### lightning-omnistudio-omniscript
|
|
189
|
+
- `input`, `record-id`, `script-name`*
|
|
190
|
+
|
|
191
|
+
### lightning-output-field
|
|
192
|
+
- `field-name`*, `record-id`, `variant` (label-hidden|standard)
|
|
193
|
+
|
|
194
|
+
### lightning-pill
|
|
195
|
+
3 clickable areas: icon/avatar, label, remove btn
|
|
196
|
+
- `class`, `has-error`, `href`, `label`, `name`, `variant` (link|plain)
|
|
197
|
+
- Slots: default (icon/avatar)
|
|
198
|
+
- Events: `onclick`, `onremove`
|
|
199
|
+
|
|
200
|
+
### lightning-pill-container
|
|
201
|
+
- `class`, `is-collapsible`, `is-expanded`, `items`*, `single-line`, `variant` (bare|standard)
|
|
202
|
+
- Event: `onitemremove` → item, index
|
|
203
|
+
|
|
204
|
+
### lightning-progress-bar
|
|
205
|
+
- `size` (large|medium|small|x-large), `value`* (0-100), `variant` (circular|circular-with-label|light)
|
|
206
|
+
|
|
207
|
+
### lightning-progress-indicator
|
|
208
|
+
- `class`, `current-step`, `type` (horizontal|vertical), `variant` (base|path)
|
|
209
|
+
- Slots: `lightning-progress-step`
|
|
210
|
+
|
|
211
|
+
### lightning-progress-ring
|
|
212
|
+
- `direction` (clockwise|counterclockwise), `value`* (0-100), `variant` (active-step|base|base-autocomplete|expired|warning)
|
|
213
|
+
|
|
214
|
+
### lightning-prompt (lightning/prompt)
|
|
215
|
+
`LightningPrompt.open()` → Promise
|
|
216
|
+
- `defaultValue`, `label`*, `message`, `options`, `variant` (header|headerless)
|
|
217
|
+
|
|
218
|
+
### lightning-quick-action-panel
|
|
219
|
+
- `object-api-name`*, `quick-action-api-name`, `record-id`*
|
|
220
|
+
- Event: `onquickactionmenu`
|
|
221
|
+
|
|
222
|
+
### lightning-record-edit-form
|
|
223
|
+
Uses LDS. Shows first validation error
|
|
224
|
+
- `class`, `layout-type` (Compact|Full), `mode` (edit|readonly), `object-api-name`, `record-id`
|
|
225
|
+
- Events: `onerror`, `onload`, `onsubmit`, `onsuccess`
|
|
226
|
+
|
|
227
|
+
### lightning-relative-date-time
|
|
228
|
+
- `class`, `options`, `value`*
|
|
229
|
+
|
|
230
|
+
### lightning-rich-text-toolbar-button
|
|
231
|
+
- `class`, `disabled`, `icon-name`, `pressed`, `value`
|
|
232
|
+
|
|
233
|
+
### lightning-rich-text-toolbar-button-group
|
|
234
|
+
Slots: `lightning-rich-text-toolbar-button`
|
|
235
|
+
|
|
236
|
+
### lightning-select
|
|
237
|
+
- `class`, `disabled`, `field-level-help`, `label`*, `message-when-value-missing`, `multiple`, `options` [{value,label,disabled?}], `required`, `size`, `validity`, `value`, `variant`
|
|
238
|
+
- Event: `onchange` → `event.detail.value`
|
|
239
|
+
|
|
240
|
+
### lightning-slider
|
|
241
|
+
Use onchange not onblur (Safari). Values clamped to min/max
|
|
242
|
+
- `class`, `disabled`, `label`, `max` (100), `min` (0), `step`, `type` (horizontal|vertical), `value`
|
|
243
|
+
- Event: `onchange`
|
|
244
|
+
|
|
245
|
+
### lightning-spinner
|
|
246
|
+
- `variant` (brand|inverse|default)
|
|
247
|
+
- Use w/ `if:true`
|
|
248
|
+
|
|
249
|
+
### lightning-tab
|
|
250
|
+
Lazy loaded. Only query active/prev-active content
|
|
251
|
+
- `class`, `end-icon-alternative-text`, `end-icon-name`, `icon-assistive-text`, `icon-name`, `label`*, `show-error-indicator`
|
|
252
|
+
- Event: `onactive`
|
|
253
|
+
|
|
254
|
+
### lightning-textarea
|
|
255
|
+
- `aria-describedby`, `aria-labelledby`, `autocomplete`, `class`, `disabled`, `label`*, `maxlength`, `message-when-*`, `minlength`, `placeholder`, `read-only`, `required`, `validity`, `value`, `variant`
|
|
256
|
+
- Events: `onblur`, `onchange`, `onfocus`
|
|
257
|
+
|
|
258
|
+
### lightning-tile
|
|
259
|
+
- `class`, `label`*
|
|
260
|
+
- Slots: `media`, default
|
|
261
|
+
- Event: `onactiontriggered`
|
|
262
|
+
|
|
263
|
+
### lightning-toast (lightning/toast)
|
|
264
|
+
`Toast.show()` → Promise
|
|
265
|
+
- `label`*, `labelLinks`, `message`, `messageLinks`, `mode` (dismissible|sticky), `variant` (error|info|success|warning)
|
|
266
|
+
|
|
267
|
+
### lightning-toast-container (lightning/toastContainer)
|
|
268
|
+
One per page. Ctrl+F6/Cmd+F6 nav
|
|
269
|
+
- `containerPosition` (absolute|fixed), `maxToasts` (3), `toastPosition` (bottom-center|bottom-left|bottom-right|top-center|top-left|top-right)
|
|
270
|
+
|
|
271
|
+
### lightning-tree
|
|
272
|
+
- `class`, `header`, `items`*
|
|
273
|
+
- Event: `onselect` → name
|
|
274
|
+
|
|
275
|
+
### lightning-tree-grid
|
|
276
|
+
Set `sortable:true` on columns for sorting
|
|
277
|
+
- `class`, `columns`*, `data`, `expanded-rows`, `hide-checkbox-column`, `key-field`*, `max-column-width`, `min-column-width`, `resize-column-disabled`, `selected-rows`, `show-row-number-column`, `variant`
|
|
278
|
+
- Events: `onrowaction`, `onrowselection`, `onsort`
|
|
279
|
+
|
|
280
|
+
### lightning-vertical-navigation
|
|
281
|
+
- `class`, `selected-item`
|
|
282
|
+
- Slots: `lightning-vertical-navigation-item*`
|
|
283
|
+
- Events: `onbeforeselect`, `onselect`
|
|
284
|
+
|
|
285
|
+
### lightning-vertical-navigation-item
|
|
286
|
+
- `badge`, `class`, `href`, `icon-name`, `label`*, `name`
|
|
287
|
+
- Event: `onselect`
|
|
288
|
+
|
|
289
|
+
### lightning-vertical-navigation-item-badge
|
|
290
|
+
- `class`, `label`, `variant` (default|inverse|lightest)
|
|
291
|
+
|
|
292
|
+
### lightning-vertical-navigation-item-icon
|
|
293
|
+
- `alternative-text`, `class`, `icon-name`, `position` (end|start)
|
|
294
|
+
|
|
295
|
+
## Patterns
|
|
296
|
+
|
|
297
|
+
### Validation
|
|
298
|
+
```js
|
|
299
|
+
// Check validity
|
|
300
|
+
if (!this.template.querySelector('lightning-input').checkValidity()) {
|
|
301
|
+
this.template.querySelector('lightning-input').reportValidity();
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Styling
|
|
306
|
+
```html
|
|
307
|
+
<lightning-button class="slds-m-left_small">
|
|
308
|
+
```
|
|
309
|
+
```css
|
|
310
|
+
--slds-c-button-color-background
|
|
311
|
+
--slds-c-button-text-color
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Navigation
|
|
315
|
+
```js
|
|
316
|
+
import { NavigationMixin } from 'lightning/navigation';
|
|
317
|
+
this[NavigationMixin.Navigate]({
|
|
318
|
+
type: 'standard__recordPage',
|
|
319
|
+
attributes: { recordId, actionName: 'view' }
|
|
320
|
+
});
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Events
|
|
324
|
+
```js
|
|
325
|
+
handleChange(event) {
|
|
326
|
+
const value = event.detail.value; // or .selectedValue
|
|
327
|
+
}
|
|
328
|
+
```
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# LWC HTML Templates
|
|
2
|
+
|
|
3
|
+
> **Version**: 1.0.0
|
|
4
|
+
|
|
5
|
+
## File Structure
|
|
6
|
+
|
|
7
|
+
- Location: `lwc/<ComponentName>/<ComponentName>.html`
|
|
8
|
+
- Additional HTML files allowed for multiple template rendering (advanced)
|
|
9
|
+
- Leverage virtual DOM; avoid manual DOM manipulation in JavaScript
|
|
10
|
+
|
|
11
|
+
## Template Structure
|
|
12
|
+
|
|
13
|
+
- **Root element:** `<template>` (renders as `<namespace-component-name>`)
|
|
14
|
+
- **Nested `<template>` tags:** required for directives; only allowed attributes:
|
|
15
|
+
- `for:each`, `iterator:iteratorname`, `lwc:if`, `lwc:else`, `lwc:elseif`, `if:true|false`
|
|
16
|
+
- No other directives or HTML attributes (e.g., no `class` on nested `<template>`)
|
|
17
|
+
- **No inline `<style>` or `<script>` tags** — use separate CSS/JS files
|
|
18
|
+
- Follow Lightning Design System patterns and accessibility guidelines
|
|
19
|
+
|
|
20
|
+
## Data Binding & Expressions
|
|
21
|
+
|
|
22
|
+
| Syntax | Usage |
|
|
23
|
+
|--------|-------|
|
|
24
|
+
| `{property}` | Bind data (no spaces around property) |
|
|
25
|
+
| `{data.name}` | Dot notation supported |
|
|
26
|
+
| `{!property}` | Two-way binding (form inputs) |
|
|
27
|
+
| `{handlerMethod}` | Event handler binding |
|
|
28
|
+
|
|
29
|
+
### Allowed Expression Syntax
|
|
30
|
+
|
|
31
|
+
The `{expression}` binding syntax **only** supports:
|
|
32
|
+
|
|
33
|
+
- **Simple property references:** `{myProperty}`
|
|
34
|
+
- **Dot notation paths:** `{contact.name}`, `{account.owner.email}`
|
|
35
|
+
- **Method references for events:** `onclick={handleClick}`
|
|
36
|
+
|
|
37
|
+
**That's it.** No operators, no function calls, no array indexing, no ternaries.
|
|
38
|
+
|
|
39
|
+
### ❌ What's NOT Allowed (Common AI Agent Mistakes)
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<!-- ❌ Logical operators -->
|
|
43
|
+
{isReadOnly || isLoading}
|
|
44
|
+
{isActive && isVisible}
|
|
45
|
+
|
|
46
|
+
<!-- ❌ Negation operator -->
|
|
47
|
+
{!isDisabled}
|
|
48
|
+
|
|
49
|
+
<!-- ❌ Ternary expressions -->
|
|
50
|
+
{isActive ? 'active' : 'inactive'}
|
|
51
|
+
|
|
52
|
+
<!-- ❌ Arithmetic -->
|
|
53
|
+
{count + 1}
|
|
54
|
+
{price * quantity}
|
|
55
|
+
|
|
56
|
+
<!-- ❌ Array indexing -->
|
|
57
|
+
{items[0].name}
|
|
58
|
+
{person[2].name['John']}
|
|
59
|
+
|
|
60
|
+
<!-- ❌ Function calls in templates -->
|
|
61
|
+
{formatDate(startDate)}
|
|
62
|
+
{calculateTotal()}
|
|
63
|
+
|
|
64
|
+
<!-- ❌ String concatenation -->
|
|
65
|
+
{'Hello ' + name}
|
|
66
|
+
{`Item: ${itemName}`}
|
|
67
|
+
|
|
68
|
+
<!-- ❌ Comparisons -->
|
|
69
|
+
{count > 0}
|
|
70
|
+
{status === 'active'}
|
|
71
|
+
|
|
72
|
+
<!-- ❌ Nullish coalescing / optional chaining in binding -->
|
|
73
|
+
{user?.name}
|
|
74
|
+
{value ?? 'default'}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### ✅ The Solution: Use JavaScript Getters
|
|
78
|
+
|
|
79
|
+
All computed logic **must** be moved to getters in the JavaScript class:
|
|
80
|
+
|
|
81
|
+
**JavaScript (myComponent.js):**
|
|
82
|
+
```javascript
|
|
83
|
+
import { LightningElement, api, track } from 'lwc';
|
|
84
|
+
|
|
85
|
+
export default class MyComponent extends LightningElement {
|
|
86
|
+
@api isReadOnly = false;
|
|
87
|
+
@api isLoading = false;
|
|
88
|
+
@track items = [];
|
|
89
|
+
|
|
90
|
+
// ✅ Logical operators → getter
|
|
91
|
+
get isDisabled() {
|
|
92
|
+
return this.isReadOnly || this.isLoading;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ✅ Negation → getter
|
|
96
|
+
get isEnabled() {
|
|
97
|
+
return !this.isDisabled;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ✅ Ternary → getter
|
|
101
|
+
get buttonLabel() {
|
|
102
|
+
return this.isLoading ? 'Loading...' : 'Submit';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ✅ Arithmetic → getter
|
|
106
|
+
get itemCountPlusOne() {
|
|
107
|
+
return this.count + 1;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ✅ Array indexing → getter
|
|
111
|
+
get firstItemName() {
|
|
112
|
+
return this.items[0]?.name || '';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ✅ String formatting → getter
|
|
116
|
+
get formattedCount() {
|
|
117
|
+
return `${this.count} items`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ✅ Comparisons for conditional rendering → getter
|
|
121
|
+
get hasItems() {
|
|
122
|
+
return this.items.length > 0;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ✅ Complex class computation → getter
|
|
126
|
+
get containerClass() {
|
|
127
|
+
return {
|
|
128
|
+
'slds-box': true,
|
|
129
|
+
'slds-theme_shade': this.isActive,
|
|
130
|
+
'slds-hide': !this.isVisible
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**HTML (myComponent.html):**
|
|
137
|
+
```html
|
|
138
|
+
<template>
|
|
139
|
+
<template lwc:if={hasItems}>
|
|
140
|
+
<div class={containerClass}>
|
|
141
|
+
<p>{formattedCount}</p>
|
|
142
|
+
<p>First item: {firstItemName}</p>
|
|
143
|
+
</div>
|
|
144
|
+
</template>
|
|
145
|
+
|
|
146
|
+
<lightning-button
|
|
147
|
+
label={buttonLabel}
|
|
148
|
+
disabled={isDisabled}
|
|
149
|
+
onclick={handleClick}>
|
|
150
|
+
</lightning-button>
|
|
151
|
+
</template>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Why This Design?
|
|
155
|
+
|
|
156
|
+
Salesforce made this intentional choice for:
|
|
157
|
+
|
|
158
|
+
| Reason | Explanation |
|
|
159
|
+
|--------|-------------|
|
|
160
|
+
| **Performance** | Simple property lookups are fast; no runtime expression parsing/evaluation |
|
|
161
|
+
| **Security** | No risk of template injection or expression-based exploits |
|
|
162
|
+
| **Predictability** | Reactivity is straightforward—change a property, component re-renders |
|
|
163
|
+
| **Testability** | All logic lives in unit-testable JavaScript, not scattered in templates |
|
|
164
|
+
| **Debugging** | Stack traces point to JS files, not opaque template expressions |
|
|
165
|
+
|
|
166
|
+
### AI Agent Rule Summary
|
|
167
|
+
|
|
168
|
+
> **LWC Template Expression Rule:** The `{...}` binding syntax accepts ONLY property names and dot-notation paths. Any logic requiring operators (`||`, `&&`, `?:`, `+`, `>`, `===`, etc.), function calls, array indexing, or string interpolation MUST be implemented as a getter in the JavaScript class.
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
Template binding: {propertyName} or {object.nested.property}
|
|
172
|
+
Computed logic: get computedProperty() { return /* any JS expression */; }
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Conditional Rendering
|
|
176
|
+
|
|
177
|
+
Use `lwc:if`, `lwc:elseif`, `lwc:else` directives (prefer over multiple templates for simple conditions):
|
|
178
|
+
|
|
179
|
+
```html
|
|
180
|
+
<template lwc:if={condition}>...</template>
|
|
181
|
+
<template lwc:elseif={otherCondition}>...</template>
|
|
182
|
+
<template lwc:else>...</template>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Note:** The condition must be a simple property reference or getter—not an expression:
|
|
186
|
+
|
|
187
|
+
```html
|
|
188
|
+
<!-- ❌ Invalid -->
|
|
189
|
+
<template lwc:if={items.length > 0}>
|
|
190
|
+
|
|
191
|
+
<!-- ✅ Valid — use a getter -->
|
|
192
|
+
<template lwc:if={hasItems}>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## List Rendering
|
|
196
|
+
|
|
197
|
+
### for:each
|
|
198
|
+
```html
|
|
199
|
+
<template for:each={array} for:item="item">
|
|
200
|
+
<div key={item.uniqueId}>{item.property}</div>
|
|
201
|
+
</template>
|
|
202
|
+
```
|
|
203
|
+
- `key` required: string/number, **not** index or object
|
|
204
|
+
- Access current item via `for:item="itemName"` → `{itemName.property}`
|
|
205
|
+
|
|
206
|
+
### iterator
|
|
207
|
+
```html
|
|
208
|
+
<template iterator:it={array}>
|
|
209
|
+
<div key={it.value.uniqueId}>{it.value.property}</div>
|
|
210
|
+
</template>
|
|
211
|
+
```
|
|
212
|
+
- Iterator name must be lowercase
|
|
213
|
+
- Properties: `{it.value.property}`, `{it.index}`, `{it.first}`, `{it.last}`
|
|
214
|
+
- Combine with `lwc:if` for first/last styling: `lwc:if={it.first}`
|
|
215
|
+
|
|
216
|
+
## Multiple Template Rendering (Advanced)
|
|
217
|
+
|
|
218
|
+
- Import templates: `import templateOne from "./templateOne.html"`
|
|
219
|
+
- Override `render()` to return template based on state
|
|
220
|
+
- CSS must match template filename: `templateTwo.html` → `templateTwo.css`
|
|
221
|
+
- Default: `componentName.html` when no `render()` override
|
|
222
|
+
- **Prefer `lwc:if` for simple variations; use multiple templates for significantly different layouts**
|
|
223
|
+
|
|
224
|
+
## Class Object Binding (LWC API v62.0+)
|
|
225
|
+
|
|
226
|
+
Bind dynamic classes using arrays or objects instead of string concatenation:
|
|
227
|
+
|
|
228
|
+
| Input | Output |
|
|
229
|
+
|-------|--------|
|
|
230
|
+
| `["highlight", "yellow"]` | `class="highlight yellow"` |
|
|
231
|
+
| `{highlight: true, hidden: false}` | `class="highlight"` |
|
|
232
|
+
|
|
233
|
+
- Booleans, numbers, functions render as empty string (use `String(value)` if needed)
|
|
234
|
+
- Use getters to compute complex class combinations
|
|
235
|
+
|
|
236
|
+
**Example with getter:**
|
|
237
|
+
```javascript
|
|
238
|
+
get cardClasses() {
|
|
239
|
+
return {
|
|
240
|
+
'slds-card': true,
|
|
241
|
+
'slds-card_boundary': this.hasBorder,
|
|
242
|
+
'custom-highlight': this.isHighlighted
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
```html
|
|
248
|
+
<div class={cardClasses}>...</div>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Inline Style Binding
|
|
252
|
+
|
|
253
|
+
- Syntax: `style={computedStyles}`
|
|
254
|
+
- Getter returns semi-colon separated properties: `width: 50%; font-size: 20px`
|
|
255
|
+
- Use kebab-case CSS properties (`font-size`, not `fontSize`)
|
|
256
|
+
- **Prefer CSS classes over inline styles**; use inline for dynamic/computed values only
|
|
257
|
+
|
|
258
|
+
**Example:**
|
|
259
|
+
```javascript
|
|
260
|
+
get progressBarStyle() {
|
|
261
|
+
return `width: ${this.progressPercent}%; background-color: ${this.barColor}`;
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
```html
|
|
266
|
+
<div class="progress-bar" style={progressBarStyle}></div>
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Event Handling
|
|
270
|
+
|
|
271
|
+
- Bind handlers via method reference: `onclick={handleClick}`
|
|
272
|
+
- No inline functions in templates
|
|
273
|
+
- Access values via `event.target.value`
|
|
274
|
+
- Custom events: extend `LightningElement`, use proper event naming
|
|
275
|
+
|
|
276
|
+
```html
|
|
277
|
+
<!-- ❌ Invalid — no inline functions -->
|
|
278
|
+
<button onclick={() => this.handleClick()}>
|
|
279
|
+
|
|
280
|
+
<!-- ✅ Valid — method reference -->
|
|
281
|
+
<button onclick={handleClick}>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Accessibility
|
|
285
|
+
|
|
286
|
+
- Include proper ARIA attributes
|
|
287
|
+
- Use semantic HTML elements
|
|
288
|
+
- Ensure keyboard navigation for interactive elements
|
|
289
|
+
|
|
290
|
+
## Performance
|
|
291
|
+
|
|
292
|
+
- Minimize DOM manipulation
|
|
293
|
+
- Use lazy loading / pagination for large lists
|
|
294
|
+
- Avoid unnecessary rerenders by optimizing data binding
|
|
295
|
+
- Getters are re-evaluated on every render—keep them lightweight or cache expensive computations
|
|
296
|
+
|
|
297
|
+
## Validation Checklist
|
|
298
|
+
|
|
299
|
+
- [ ] File path: `lwc/<ComponentName>/<ComponentName>.html`
|
|
300
|
+
- [ ] Root element is `<template>`
|
|
301
|
+
- [ ] Nested `<template>` tags include only allowed directives
|
|
302
|
+
- [ ] Nested `<template>` tags have no other attributes (e.g., no `class`)
|
|
303
|
+
- [ ] No inline `<style>` or `<script>` tags
|
|
304
|
+
- [ ] No manual DOM manipulation in JS
|
|
305
|
+
- [ ] Data binding: `{property}` syntax, no spaces
|
|
306
|
+
- [ ] **No computed expressions in templates** (no operators, function calls, array indexing)
|
|
307
|
+
- [ ] **All computed logic implemented as getters in JS class**
|
|
308
|
+
- [ ] `for:each`: has `for:item` and unique `key` (string/number)
|
|
309
|
+
- [ ] `iterator`: lowercase name, unique `key={it.value.uniqueId}`
|
|
310
|
+
- [ ] Multiple templates: proper imports, `render()` override, matching CSS filenames
|
|
311
|
+
- [ ] Class binding follows v62.0+ semantics (arrays/objects via getters)
|
|
312
|
+
- [ ] Inline styles: `style={getter}`, kebab-case properties
|
|
313
|
+
- [ ] Valid HTML syntax (use Salesforce Extensions Pack for validation)
|
|
314
|
+
- [ ] Follows accessibility and performance best practices
|