|
| 1 | +# Canvas App YAML — QA Self-Check Guide |
| 2 | + |
| 3 | +This guide lists runtime layout issues that `compile_canvas` does NOT catch. The |
| 4 | +compiler validates syntax and property names. It cannot tell you that your |
| 5 | +scrollable container will never scroll, or that a transparent overlay button |
| 6 | +will collapse its siblings to zero height. |
| 7 | + |
| 8 | +Agents that write `.pa.yaml` files MUST run these checks against their own |
| 9 | +output before returning, and fix every issue inline. Report the total number of |
| 10 | +fixes applied in the result summary. |
| 11 | + |
| 12 | +--- |
| 13 | + |
| 14 | +## How to run the checks |
| 15 | + |
| 16 | +1. Read the `.pa.yaml` file you just wrote |
| 17 | +2. Apply each check below in order |
| 18 | +3. For every issue found: apply the fix directly using `Edit` |
| 19 | +4. Track the count and a one-line description of each fix |
| 20 | +5. Do NOT re-run `compile_canvas` here — the orchestrating skill does that |
| 21 | + |
| 22 | +All checks are safe: they tighten existing YAML, never delete semantic content. |
| 23 | + |
| 24 | +--- |
| 25 | + |
| 26 | +## Check 1 — LayoutMinWidth / LayoutMinHeight on every GroupContainer |
| 27 | + |
| 28 | +**Problem:** Power Apps defaults `LayoutMinWidth` to 250 and `LayoutMinHeight` to |
| 29 | +100 on `GroupContainer`. In a sidebar, header, or narrow cell, these defaults |
| 30 | +silently push the container wider/taller than intended and clip siblings. |
| 31 | + |
| 32 | +**Detect:** For every control with `Control: GroupContainer`, check whether |
| 33 | +`LayoutMinWidth: =0` and `LayoutMinHeight: =0` are present in `Properties:`. |
| 34 | + |
| 35 | +**Fix:** Add either property if missing: |
| 36 | + |
| 37 | +```yaml |
| 38 | +LayoutMinWidth: =0 |
| 39 | +LayoutMinHeight: =0 |
| 40 | +``` |
| 41 | +
|
| 42 | +**Exception:** None. Always set both on every GroupContainer. |
| 43 | +
|
| 44 | +--- |
| 45 | +
|
| 46 | +## Check 2 — AlignInContainer on every AutoLayout child |
| 47 | +
|
| 48 | +**Problem:** Children of an AutoLayout container (any container that sets |
| 49 | +`LayoutDirection`) have unpredictable cross-axis alignment when |
| 50 | +`AlignInContainer` is omitted. PA picks a default that depends on control type. |
| 51 | + |
| 52 | +**Detect:** For every control, check whether its parent has a `LayoutDirection` |
| 53 | +property. If yes, check whether the child has `AlignInContainer` set. If not, |
| 54 | +it's missing. |
| 55 | + |
| 56 | +**Fix:** Add `AlignInContainer: =AlignInContainer.Stretch` to the child. This |
| 57 | +is the correct default for labels, inputs, buttons, and generic content — the |
| 58 | +child fills the parent's cross-axis dimension. |
| 59 | + |
| 60 | +```yaml |
| 61 | +AlignInContainer: =AlignInContainer.Stretch |
| 62 | +``` |
| 63 | + |
| 64 | +**Exception:** If the child has an explicit smaller-than-parent cross-axis |
| 65 | +dimension (e.g., a 28px circular avatar inside a 44px horizontal row), use |
| 66 | +`AlignInContainer: =AlignInContainer.Center` instead, so the child keeps its |
| 67 | +natural size and is centered. |
| 68 | + |
| 69 | +--- |
| 70 | + |
| 71 | +## Check 3 — SCROLL-TRAP (`FillPortions: =1` inside scroll container) |
| 72 | + |
| 73 | +**Problem:** When a container has `LayoutOverflowY: =LayoutOverflow.Scroll` and |
| 74 | +its direct child has `FillPortions: =1`, the child is pinned to the viewport |
| 75 | +height. Content that exceeds the viewport is clipped, not scrolled — the whole |
| 76 | +point of the scroll container is defeated. |
| 77 | + |
| 78 | +**Detect:** For every container with `LayoutOverflowY: =LayoutOverflow.Scroll` |
| 79 | +inspect its direct children. Flag any direct child that has `FillPortions: =1`. |
| 80 | + |
| 81 | +**Fix:** Change the child's `FillPortions` to `=0`. |
| 82 | + |
| 83 | +```yaml |
| 84 | +# Before: |
| 85 | +FillPortions: =1 |
| 86 | +# After: |
| 87 | +FillPortions: =0 |
| 88 | +``` |
| 89 | + |
| 90 | +--- |
| 91 | + |
| 92 | +## Check 4 — WRAP-MISSING (single-line label without `Wrap: =false`) |
| 93 | + |
| 94 | +**Problem:** Power Apps defaults `Wrap` to `true` on `Label` controls. A narrow |
| 95 | +nav item, breadcrumb, badge, or KPI value will wrap its text onto two lines and |
| 96 | +break the intended layout. |
| 97 | + |
| 98 | +**Detect:** For every `Label` (including `ModernText`), check whether |
| 99 | +`Wrap: =false` is set. Flag any label that looks like a single-line UI element: |
| 100 | + |
| 101 | +- Nav/menu item labels (inside a navigation gallery or sidebar) |
| 102 | +- Tab labels |
| 103 | +- Logo text labels |
| 104 | +- Column headers in tables or galleries |
| 105 | +- Status badges / pill text |
| 106 | +- KPI metric values and card titles |
| 107 | +- Breadcrumb text |
| 108 | +- Button-adjacent short descriptors |
| 109 | + |
| 110 | +**Fix:** Add `Wrap: =false` to the label's Properties. |
| 111 | + |
| 112 | +```yaml |
| 113 | +Wrap: =false |
| 114 | +``` |
| 115 | + |
| 116 | +**Exception:** Do NOT add `Wrap: =false` to labels that intentionally display |
| 117 | +multi-line content — description paragraphs, body copy, notes fields, long |
| 118 | +comment text. These should keep the default wrapping behavior. |
| 119 | + |
| 120 | +--- |
| 121 | + |
| 122 | +## Check 5 — NO-HEIGHT-TRAP (`FillPortions: =0` without explicit `Height`) |
| 123 | + |
| 124 | +**Problem:** When an AutoLayout child has `FillPortions: =0` (or `FillPortions` |
| 125 | +is absent, which defaults to 0) and no explicit `Height`, Power Apps defaults |
| 126 | +its height to 200px. This pushes surrounding controls around and produces |
| 127 | +inexplicable gaps or clipping. |
| 128 | + |
| 129 | +**Detect:** For every `GroupContainer` whose parent has `LayoutDirection` |
| 130 | +(AutoLayout child), check: |
| 131 | +- Is `FillPortions` absent or `=0`? |
| 132 | +- Is `Height` absent? |
| 133 | +- If both → flag it. |
| 134 | + |
| 135 | +**Fix:** Add an explicit `Height` formula that sums child heights + gaps + |
| 136 | +padding: |
| 137 | + |
| 138 | +```yaml |
| 139 | +Height: =PaddingTop + child1.Height + LayoutGap + child2.Height + PaddingBottom |
| 140 | +``` |
| 141 | + |
| 142 | +If the children's heights are unknown at write time, use a safe static value |
| 143 | +(e.g., `Height: =44` for a single row, `=200` for a card panel) and note it in |
| 144 | +the fix log so the user can refine. |
| 145 | + |
| 146 | +**Exception:** The screen root container uses `Width: =Parent.Width` and |
| 147 | +`Height: =Parent.Height` — not an AutoLayout child. Do NOT flag it. |
| 148 | + |
| 149 | +Also do NOT flag controls where `FillPortions > 0` — PA computes the height |
| 150 | +proportionally and `Height` should be absent. |
| 151 | + |
| 152 | +--- |
| 153 | + |
| 154 | +## Check 6 — TEXT-PADDING (ModernText, Label padding defaults to 5) |
| 155 | + |
| 156 | +**Problem:** `ModernText` and `Label` controls default `PaddingTop`, `PaddingBottom`, |
| 157 | +`PaddingLeft`, and `PaddingRight` to `5`. In most UI contexts (labels in a |
| 158 | +table row, card header text, inline metadata, KPI values), the 5px default is |
| 159 | +unintended and breaks alignment with adjacent controls or adds stray visual |
| 160 | +space in tight layouts. |
| 161 | + |
| 162 | +**Detect:** For every control with `Control: ModernText` or `Control: Label`, check whether all |
| 163 | +four padding properties — `PaddingTop`, `PaddingBottom`, `PaddingLeft`, and |
| 164 | +`PaddingRight` — are explicitly set in `Properties:`. Flag any that are |
| 165 | +absent. |
| 166 | + |
| 167 | +**Fix:** For each of the four properties that is absent, add it with value |
| 168 | +`=0`: |
| 169 | + |
| 170 | +```yaml |
| 171 | +PaddingTop: =0 |
| 172 | +PaddingBottom: =0 |
| 173 | +PaddingLeft: =0 |
| 174 | +PaddingRight: =0 |
| 175 | +``` |
| 176 | + |
| 177 | +**Exception:** If the design explicitly requires internal padding on a |
| 178 | +`ModernText` (e.g., a status pill or badge where inset text is intended), set |
| 179 | +the intended non-zero value explicitly. The rule is **never leave any of the |
| 180 | +four padding properties absent on a `ModernText`** — always set all four so the |
| 181 | +PA default of 5 cannot creep in. |
| 182 | + |
| 183 | +--- |
| 184 | + |
| 185 | +## Check 7 — FILLPORTIONS-HEIGHT-CONFLICT (both set on the same control) |
| 186 | + |
| 187 | +**Problem:** Setting both `FillPortions: =N` (where `N > 0`) and an explicit |
| 188 | +`Height: =value` on the same control within a vertical AutoLayout container confuses the layout engine. |
| 189 | +The container renders one size at design time and another at runtime. |
| 190 | + |
| 191 | +**Detect:** For every control, check whether it has both: |
| 192 | +- `FillPortions` with a value greater than 0, AND |
| 193 | +- An explicit `Height` (any non-formula numeric or a formula that doesn't |
| 194 | + reference Parent) |
| 195 | + |
| 196 | +**Fix:** Remove the `Height` property. PA computes it from `FillPortions` |
| 197 | +against the parent's available space. |
| 198 | + |
| 199 | +--- |
| 200 | + |
| 201 | +## Check 8 — FILLPORTIONS-WIDTH-CONFLICT (both set on the same control) |
| 202 | + |
| 203 | +**Problem:** Setting both `FillPortions: =N` (where `N > 0`) and an explicit |
| 204 | +`Width: =value` on the same control within a horizontal AutoLayout container |
| 205 | +confuses the layout engine. The container renders one size at design time and |
| 206 | +another at runtime. |
| 207 | + |
| 208 | +**Detect:** For every control, check whether it has both: |
| 209 | +- `FillPortions` with a value greater than 0, AND |
| 210 | +- An explicit `Width` (any non-formula numeric or a formula that doesn't |
| 211 | + reference Parent) |
| 212 | + |
| 213 | +**Fix:** Remove the `Width` property. PA computes it from `FillPortions` |
| 214 | +against the parent's available space. |
| 215 | + |
| 216 | +--- |
0 commit comments