mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-10-05 14:46:55 +01:00
Clarify page field caption and tooltip inheritance
Prevent redundant page-level properties by documenting inherited captions and BC24/runtime 13.0 table-field tooltips. Correct companion examples and version-scoped tooltip guidance. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
1a5afdc0eb
commit
db767a0336
7 changed files with 139 additions and 32 deletions
|
|
@ -9,16 +9,22 @@ page 50253 "Sample Caption Bad"
|
||||||
{
|
{
|
||||||
group(General)
|
group(General)
|
||||||
{
|
{
|
||||||
field("Customer No."; Rec."No.")
|
Caption = 'General';
|
||||||
|
field(CustomerNoValue; CustomerNoValue)
|
||||||
{
|
{
|
||||||
ApplicationArea = All;
|
ApplicationArea = All;
|
||||||
|
ToolTip = 'Specifies the customer number to look up.';
|
||||||
}
|
}
|
||||||
field("Customer Name"; Rec.Name)
|
field("Customer Name"; Rec.Name)
|
||||||
{
|
{
|
||||||
ApplicationArea = All;
|
ApplicationArea = All;
|
||||||
Caption = '';
|
Caption = '';
|
||||||
|
ToolTip = 'Specifies the customer name.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var
|
||||||
|
CustomerNoValue: Code[20];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,36 @@
|
||||||
|
// BC24 / runtime 13.0 or later for table-field tooltips.
|
||||||
|
table 50252 "Sample Caption Source"
|
||||||
|
{
|
||||||
|
Caption = 'Caption Source';
|
||||||
|
DataClassification = CustomerContent;
|
||||||
|
|
||||||
|
fields
|
||||||
|
{
|
||||||
|
field(1; "No."; Code[20])
|
||||||
|
{
|
||||||
|
Caption = 'No.';
|
||||||
|
ToolTip = 'Specifies the customer number.';
|
||||||
|
}
|
||||||
|
field(2; Name; Text[100])
|
||||||
|
{
|
||||||
|
Caption = 'Name';
|
||||||
|
ToolTip = 'Specifies the customer name.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keys
|
||||||
|
{
|
||||||
|
key(PK; "No.")
|
||||||
|
{
|
||||||
|
Clustered = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
page 50252 "Sample Caption Good"
|
page 50252 "Sample Caption Good"
|
||||||
{
|
{
|
||||||
PageType = Card;
|
PageType = Card;
|
||||||
SourceTable = Customer;
|
SourceTable = "Sample Caption Source";
|
||||||
|
|
||||||
layout
|
layout
|
||||||
{
|
{
|
||||||
|
|
@ -10,19 +39,25 @@ page 50252 "Sample Caption Good"
|
||||||
group(General)
|
group(General)
|
||||||
{
|
{
|
||||||
Caption = 'General';
|
Caption = 'General';
|
||||||
field("Customer No."; Rec."No.")
|
field("No."; Rec."No.")
|
||||||
{
|
{
|
||||||
ApplicationArea = All;
|
ApplicationArea = All;
|
||||||
Caption = 'Customer No.';
|
|
||||||
ToolTip = 'Specifies the customer number.';
|
|
||||||
}
|
}
|
||||||
field("Customer Name"; Rec.Name)
|
field("Customer Name"; Rec.Name)
|
||||||
{
|
{
|
||||||
ApplicationArea = All;
|
ApplicationArea = All;
|
||||||
Caption = 'Customer Name';
|
Caption = 'Customer Name';
|
||||||
ToolTip = 'Specifies the customer name.';
|
}
|
||||||
|
field(DisplayValue; DisplayValue)
|
||||||
|
{
|
||||||
|
ApplicationArea = All;
|
||||||
|
Caption = 'Display Value';
|
||||||
|
ToolTip = 'Specifies the value to display.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var
|
||||||
|
DisplayValue: Text[100];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,36 @@
|
||||||
---
|
---
|
||||||
bc-version: [all]
|
bc-version: [all]
|
||||||
domain: style
|
domain: style
|
||||||
keywords: [caption, page-field, aa0225, aa0226, codecop, captionclass]
|
keywords: [caption, page-field, source-field, inheritance, aa0225, aa0226, codecop, captionclass, false-positive]
|
||||||
technologies: [al]
|
technologies: [al]
|
||||||
countries: [w1]
|
countries: [w1]
|
||||||
application-area: [all]
|
application-area: [all]
|
||||||
---
|
---
|
||||||
|
|
||||||
# Every page field needs a `Caption` (CodeCop AA0225/AA0226)
|
# Page fields can inherit their source table field's `Caption`
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
CodeCop AA0225 and AA0226 require every field control to expose a `Caption` property, separately from the field's source name. The caption is what the user sees as the column header or label; the source name is what the code uses to reference the field. Without an explicit `Caption`, AL falls back to the source field's caption — which may be wrong for the page's context — or to the field name itself in code casing, which surfaces internal naming to users and to translators.
|
A page field bound to a table field inherits the source field's `Caption` unless the page overrides it. An inherited caption is valid, user-facing, and translatable; omitting a page-level `Caption` does not mean the control displays an internal identifier or loses translations. CodeCop AA0225/AA0226 concern missing or empty captions, not a requirement to duplicate a caption already supplied by the source table field.
|
||||||
|
|
||||||
Acceptable exceptions: a field whose caption is inherited via `CaptionClass = '3,5,' + CurrencyCode` (or another CaptionClass formula) does not need a literal `Caption`; the formula provides it. API pages and test pages may omit captions because their consumers are not human users. Boolean fields whose name already reads as a sentence — `Enabled`, `Posted`, `Released` — do not need a redundant Caption that repeats the name.
|
Controls bound to variables or expressions cannot rely on table-field caption inheritance. For user-facing fields that need a label, supply a `Caption` or a `CaptionClass` that resolves to the intended caption. API pages are not human-facing UI; do not apply this UI-label guidance to their API contract names.
|
||||||
|
|
||||||
## Best Practice
|
## Best Practice
|
||||||
|
|
||||||
`Caption = 'Customer No.';` paired with `ToolTip = 'Specifies …';`. Captions are short, noun-phrase, title-case for primary labels; sentence-case is allowed for descriptive labels that read as a sentence fragment.
|
Define the shared caption on the table field and let bound page fields inherit it. Add a page-level `Caption` only when there is no suitable inherited caption or the page genuinely needs different wording. Keep a valid `CaptionClass` rather than adding a redundant literal caption.
|
||||||
|
|
||||||
See sample: `caption-required-on-page-fields.good.al`.
|
Before reporting a missing caption, inspect the binding and source field, including dependency symbols when needed. If the source definition is unavailable, do not treat an omitted page property as proof that the caption is missing. Caption and tooltip requirements are separate: do not add a `ToolTip` just because a caption is being reviewed; see [tooltip inheritance guidance](tooltip-required-on-page-fields.md).
|
||||||
|
|
||||||
|
See sample: `caption-required-on-page-fields.good.al`. Caption inheritance applies across BC versions; the sample uses BC24/runtime 13.0 or later to also define tooltips on its table fields.
|
||||||
|
|
||||||
## Anti Pattern
|
## Anti Pattern
|
||||||
|
|
||||||
A field control with no `Caption` and no `CaptionClass`, or `Caption = '';`. The user sees the internal identifier as the column header and the translation pipeline has nothing to translate.
|
A user-facing field that needs a label but has no non-empty explicit or inherited caption and no resolving `CaptionClass` has a genuine labeling gap. This includes `Caption = '';` when no `CaptionClass` supplies the label. A variable name alone is not a translatable caption.
|
||||||
|
|
||||||
|
The opposite review defect is flagging a bound field solely because it omits a page-level `Caption`, or inserting a copy of the table field's caption to satisfy AA0225/AA0226. That adds redundant text and prevents subsequent table-caption changes from flowing through to the page.
|
||||||
|
|
||||||
See sample: `caption-required-on-page-fields.bad.al`.
|
See sample: `caption-required-on-page-fields.bad.al`.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
[Caption property](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/properties/devenv-caption-property) and [ToolTip property remarks documenting inheritance of both properties](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/properties/devenv-tooltip-property).
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,29 @@
|
||||||
page 50251 "Sample Tooltip Bad"
|
page 50251 "Sample Tooltip Bad"
|
||||||
{
|
{
|
||||||
PageType = Card;
|
PageType = Card;
|
||||||
SourceTable = Customer;
|
|
||||||
layout
|
layout
|
||||||
{
|
{
|
||||||
area(Content)
|
area(Content)
|
||||||
{
|
{
|
||||||
group(General)
|
group(General)
|
||||||
{
|
{
|
||||||
field("No."; Rec."No.")
|
Caption = 'General';
|
||||||
|
field(CustomerNoValue; CustomerNoValue)
|
||||||
{
|
{
|
||||||
ApplicationArea = All;
|
ApplicationArea = All;
|
||||||
|
Caption = 'Customer No.';
|
||||||
}
|
}
|
||||||
field(Amount; Rec."Balance (LCY)")
|
field(PreviewAmount; PreviewAmount)
|
||||||
{
|
{
|
||||||
ApplicationArea = All;
|
ApplicationArea = All;
|
||||||
|
Caption = 'Preview Amount';
|
||||||
ToolTip = '';
|
ToolTip = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var
|
||||||
|
CustomerNoValue: Code[20];
|
||||||
|
PreviewAmount: Decimal;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,62 @@
|
||||||
|
// BC24 / runtime 13.0 or later.
|
||||||
|
table 50250 "Sample Tooltip Source"
|
||||||
|
{
|
||||||
|
Caption = 'Tooltip Source';
|
||||||
|
DataClassification = CustomerContent;
|
||||||
|
|
||||||
|
fields
|
||||||
|
{
|
||||||
|
field(1; "No."; Code[20])
|
||||||
|
{
|
||||||
|
Caption = 'No.';
|
||||||
|
ToolTip = 'Specifies the number that identifies the entry.';
|
||||||
|
}
|
||||||
|
field(2; Amount; Decimal)
|
||||||
|
{
|
||||||
|
Caption = 'Amount';
|
||||||
|
ToolTip = 'Specifies the entry amount.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keys
|
||||||
|
{
|
||||||
|
key(PK; "No.")
|
||||||
|
{
|
||||||
|
Clustered = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
page 50250 "Sample Tooltip Good"
|
page 50250 "Sample Tooltip Good"
|
||||||
{
|
{
|
||||||
PageType = Card;
|
PageType = Card;
|
||||||
SourceTable = Customer;
|
SourceTable = "Sample Tooltip Source";
|
||||||
layout
|
layout
|
||||||
{
|
{
|
||||||
area(Content)
|
area(Content)
|
||||||
{
|
{
|
||||||
group(General)
|
group(General)
|
||||||
{
|
{
|
||||||
|
Caption = 'General';
|
||||||
field("No."; Rec."No.")
|
field("No."; Rec."No.")
|
||||||
{
|
{
|
||||||
ApplicationArea = All;
|
ApplicationArea = All;
|
||||||
ToolTip = 'Specifies the number that identifies the customer.';
|
|
||||||
}
|
}
|
||||||
field(Amount; Rec."Balance (LCY)")
|
field(Amount; Rec.Amount)
|
||||||
{
|
{
|
||||||
ApplicationArea = All;
|
ApplicationArea = All;
|
||||||
ToolTip = 'Shows the total balance in local currency.';
|
ToolTip = 'Specifies the entry amount to use in the preview.';
|
||||||
|
}
|
||||||
|
field(PreviewAmount; PreviewAmount)
|
||||||
|
{
|
||||||
|
ApplicationArea = All;
|
||||||
|
Caption = 'Preview Amount';
|
||||||
|
ToolTip = 'Specifies the amount to preview before saving.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var
|
||||||
|
PreviewAmount: Decimal;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,38 @@
|
||||||
---
|
---
|
||||||
bc-version: [all]
|
bc-version: [all]
|
||||||
domain: style
|
domain: style
|
||||||
keywords: [tooltip, page-field, aa0218, codecop, accessibility, specifies]
|
keywords: [tooltip, page-field, source-field, inheritance, aa0218, codecop, accessibility, specifies]
|
||||||
technologies: [al]
|
technologies: [al]
|
||||||
countries: [w1]
|
countries: [w1]
|
||||||
application-area: [all]
|
application-area: [all]
|
||||||
---
|
---
|
||||||
|
|
||||||
# Every page field needs a `ToolTip` (CodeCop AA0218)
|
# Page fields need an explicit or inherited `ToolTip` (CodeCop AA0218)
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
CodeCop AA0218 requires a non-empty `ToolTip` property on every field control on a page. The tooltip is what users see on hover and is what screen readers announce; an empty or missing tooltip removes a piece of UI affordance that is part of BC's accessibility baseline. AppSource technical validation rejects pages with missing tooltips. The companion rules AA0219 and AA0220 push the wording further — tooltips should describe what the field shows, conventionally starting with `'Specifies …'`, though `'Shows …'` and similar variants are acceptable when they clearly describe the field's purpose.
|
User-facing page fields need tooltip text, but it does not have to be declared on each page control. Starting with BC24 (2024 release wave 1), runtime 13.0 supports `ToolTip` on table fields, and bound page fields inherit it unless they override it. A non-empty inherited tooltip satisfies the requirement; do not interpret CodeCop AA0218 as a requirement to repeat it on the page.
|
||||||
|
|
||||||
Acceptable exceptions: table fields inside `Upgrade`, `Migration`, `HybridBC14`, `HybridSL`, and `HybridGP` codeunits and tables are allowed to omit the tooltip — those types are not surfaced to users.
|
For targets before runtime 13.0, table-field tooltip inheritance is not available, so user-facing page fields need page-level tooltips. Controls bound to variables or expressions also need page-level tooltips because they have no table field to inherit from. This is UI guidance, not a blanket requirement to add tooltips to every table field, including fields never exposed to users.
|
||||||
|
|
||||||
AA0218 is a compiler analyzer, but its severity is configured per app in the ruleset and is frequently downgraded to `info`/`None` or disabled entirely. PR review therefore cannot assume the compiler will surface the gap: it is the last line of defence for a missing tooltip and should flag it independently. The one case review must *not* flag is a bound field that inherits a `ToolTip` from its source table field — see `bound-page-field-inherits-source-field-tooltip`.
|
AA0218's severity is configured per app and may be downgraded or disabled. Review should still report a genuinely missing tooltip, but absence of a page-level declaration alone is not evidence of a gap. See [bound page-field tooltip inheritance](../ui/bound-page-field-inherits-source-field-tooltip.md).
|
||||||
|
|
||||||
## Best Practice
|
## Best Practice
|
||||||
|
|
||||||
Every field control on a regular page carries `ToolTip = 'Specifies …';` (or a clear alternative phrasing). Compose the text in the form "what this value shows" rather than "what the user does with it". In review, raise a `medium`-severity finding for a field that has neither an inline nor an inherited tooltip, independently of whether AA0218 is active in the app's ruleset.
|
On runtime 13.0 or later, define shared tooltip text on the table field and omit duplicate page-level properties. Add a page-level `ToolTip` when no tooltip can be inherited or when the page needs different, context-specific help. Describe what the value shows, conventionally starting with "Specifies" or another clear phrasing.
|
||||||
|
|
||||||
See sample: `tooltip-required-on-page-fields.good.al`.
|
Before raising a `medium`-severity finding, check the target runtime, the control's binding, and the source field's tooltip, including dependency symbols when needed. Report a field with neither an explicit nor an inherited tooltip independently of whether AA0218 is active. If the source definition or target runtime is unavailable, do not assume a missing page property means missing tooltip text.
|
||||||
|
|
||||||
|
See sample: `tooltip-required-on-page-fields.good.al` (BC24/runtime 13.0 or later).
|
||||||
|
|
||||||
## Anti Pattern
|
## Anti Pattern
|
||||||
|
|
||||||
A field control with no `ToolTip` property at all, or `ToolTip = '';`. AA0218 flags both; the hover state is blank and the screen reader has nothing to announce.
|
A user-facing control with no page-level `ToolTip` and no non-empty source tooltip it can inherit, or a page-level `ToolTip = '';` that leaves the effective tooltip empty.
|
||||||
|
|
||||||
|
Flagging a bound field that already inherits its tooltip, or adding the same tooltip to every page, is also incorrect: duplicate overrides add maintenance and translation work and prevent source-field tooltip changes from reaching those pages.
|
||||||
|
|
||||||
See sample: `tooltip-required-on-page-fields.bad.al`.
|
See sample: `tooltip-required-on-page-fields.bad.al`.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
[ToolTip property](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/properties/devenv-tooltip-property).
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
bc-version: [all]
|
bc-version: [24..]
|
||||||
domain: ui
|
domain: ui
|
||||||
keywords: [tooltip, page-field, source-field, inheritance, aa0218, false-positive]
|
keywords: [tooltip, page-field, source-field, inheritance, aa0218, false-positive]
|
||||||
technologies: [al]
|
technologies: [al]
|
||||||
|
|
@ -11,14 +11,20 @@ application-area: [all]
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
A page field bound to a table field inherits the source field's `ToolTip` at runtime: the control shows the table field's `ToolTip` even when the page control declares none of its own. A page field without an inline `ToolTip` is therefore not, by itself, a missing-tooltip defect — the text may be supplied by the bound source field.
|
Starting with BC24 (2024 release wave 1), runtime 13.0 supports `ToolTip` on table fields. A page field bound to a table field inherits the source field's `ToolTip` when the page control declares none of its own. A page field without an inline `ToolTip` is therefore not, by itself, a missing-tooltip defect. This inheritance is not available when targeting earlier runtimes.
|
||||||
|
|
||||||
The genuinely-missing case is different: a bound field whose source table field *also* carries no `ToolTip`, or an unbound control, has no text to inherit and is a real accessibility gap. The compiler analyzer AA0218 detects this mechanically, but its severity is set by each app's ruleset and is routinely downgraded or disabled — so it cannot be relied on as the only net. PR review is the last line of defence and should raise this case independently.
|
The genuinely-missing case is different: a control with no inline `ToolTip` also has no text to inherit when it is unbound or its source table field carries no non-empty `ToolTip`. This leaves a real user-assistance gap. The compiler analyzer AA0218 detects this mechanically, but its severity is set by each app's ruleset and may be downgraded or disabled, so review should raise the genuine gap independently.
|
||||||
|
|
||||||
## Best Practice
|
## Best Practice
|
||||||
|
|
||||||
Do not raise a missing-`ToolTip` finding for a bound page field whose source table field supplies a `ToolTip`; assume the control inherits it. Do raise a `medium`-severity finding when the field has no inline `ToolTip` **and** no inherited one — that is, a bound field whose source field is also tooltip-less, or an unbound control — rather than assuming AA0218 will catch it downstream.
|
Check the target runtime and inspect the source field, including dependency symbols when needed. On runtime 13.0 or later, do not raise a missing-`ToolTip` finding for a bound page field whose source table field supplies a non-empty `ToolTip`, and do not add a duplicate page-level property. A page-level override is appropriate only when the page needs different help text or no tooltip can be inherited.
|
||||||
|
|
||||||
|
Do raise a `medium`-severity finding when the field has no inline `ToolTip` **and** no inherited one, rather than assuming AA0218 will catch it downstream. If the source definition is unavailable, do not infer that its tooltip is missing. See [tooltip requirements across target versions](../style/tooltip-required-on-page-fields.md).
|
||||||
|
|
||||||
## Anti Pattern
|
## Anti Pattern
|
||||||
|
|
||||||
Two opposite failures: (1) flagging every page field that has no inline `ToolTip` as a violation, ignoring that a bound field inherits its source field's tooltip; and (2) staying silent on a field that has neither an inline nor an inherited tooltip on the assumption that the compiler's AA0218 will report it — a ruleset that downgrades or disables AA0218 then lets a genuine gap ship unflagged.
|
Two opposite failures: (1) flagging every page field that has no inline `ToolTip` as a violation, ignoring that a bound field inherits its source field's tooltip; and (2) staying silent on a field that has neither an inline nor an inherited tooltip on the assumption that the compiler's AA0218 will report it — a ruleset that downgrades or disables AA0218 then lets a genuine gap ship unflagged.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
[ToolTip property](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/properties/devenv-tooltip-property).
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue