@praxisui/table 8.0.0-beta.2 → 8.0.0-beta.21
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/README.md +150 -8
- package/docs/DSL-Extensions-Guide.md +23 -0
- package/docs/adr/2026-03-dynamic-filter-cross-lib-coupling.md +107 -0
- package/docs/adr/2026-03-filter-drawer-adapter-light-entrypoint.md +105 -0
- package/docs/adr/2026-03-table-editor-idfield-decision.md +85 -0
- package/docs/column-resize-reorder-implementation-plan.md +338 -0
- package/docs/column-resize-reorder-review-prompt.md +34 -0
- package/docs/dynamic-filter-architecture-overview.md +207 -0
- package/docs/dynamic-filter-backend-contract-cheatsheet.md +167 -0
- package/docs/dynamic-filter-editor-settings-guide.md +229 -0
- package/docs/dynamic-filter-host-integration-guide.md +217 -0
- package/docs/dynamic-filter-payload-contract.md +331 -0
- package/docs/dynamic-filter-range-filters-guide.md +289 -0
- package/docs/dynamic-filter-troubleshooting-guide.md +220 -0
- package/docs/dynamic-inline-filter-catalog.md +147 -0
- package/docs/e2e-column-drag-playwright.md +62 -0
- package/docs/expandable-rows-enterprise-big-leagues-plan.md +1080 -0
- package/docs/json-logic-operators-and-helpers.md +57 -0
- package/docs/local-data-mode-precedence.md +12 -0
- package/docs/local-data-pre-implementation-baseline.md +22 -0
- package/docs/local-data-preimplementation-go-no-go.md +39 -0
- package/docs/local-data-support-implementation-plan.md +524 -0
- package/docs/local-data-support-pr-package.md +66 -0
- package/docs/localization-persistence-merge.md +22 -0
- package/docs/performance-hardening-v2-implementation-plan.md +479 -0
- package/docs/playground-scenario-curation-plan.md +482 -0
- package/docs/playground-scenario-second-opinion-prompt.md +121 -0
- package/docs/playground-scenario-second-opinion-review.md +234 -0
- package/docs/release-notes-p1-hardening.md +76 -0
- package/docs/table-authoring-document-completeness-checklist.md +120 -0
- package/docs/table-editor-capability-review-prompt.md +349 -0
- package/docs/visual-rules-editor-transition.md +29 -0
- package/fesm2022/{praxisui-table-table-agentic-authoring-turn-flow-tu7jtTwV.mjs → praxisui-table-table-agentic-authoring-turn-flow-DRuE55Mi.mjs} +100 -0
- package/fesm2022/{praxisui-table-table-ai.adapter-DxjDaQqy.mjs → praxisui-table-table-ai.adapter-fS74fZ7o.mjs} +14 -5
- package/fesm2022/praxisui-table.mjs +5317 -736
- package/index.d.ts +325 -106
- package/package.json +15 -9
- package/src/lib/praxis-table.json-api.md +1325 -0
|
@@ -0,0 +1,1080 @@
|
|
|
1
|
+
# Praxis Table - Expandable Rows Enterprise Plan (V5 Execution-Aligned)
|
|
2
|
+
|
|
3
|
+
## 1. Objetivo
|
|
4
|
+
|
|
5
|
+
Consolidar um plano **enterprise-ready** para `expandable rows` na `praxis-table`, com:
|
|
6
|
+
|
|
7
|
+
1. paridade competitiva com AG Grid Enterprise, MUI DataGrid Premium, TanStack Table e Kendo UI;
|
|
8
|
+
2. diferencial `schema-first` (JSON + editor), com composição por metadados (`card/form/table/chart/richText/tabs/list`) e renderer registry;
|
|
9
|
+
3. governança de contrato/runtime/documentação/AI metadata para evitar drift;
|
|
10
|
+
4. segurança, privacidade, observabilidade e operação tratadas desde o início;
|
|
11
|
+
5. defaults de segurança aplicados no runtime (não apenas declarados em documentação).
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 2. Veredito Executivo do Plano
|
|
16
|
+
|
|
17
|
+
**Go com ressalvas moderadas para o plano completo.**
|
|
18
|
+
|
|
19
|
+
Recorte de execução recomendado para reduzir risco:
|
|
20
|
+
|
|
21
|
+
1. P0A: contrato + runtime não virtualizado + evento canônico + A11y base;
|
|
22
|
+
2. P0B: virtualizado fixo + collapse policies essenciais + observabilidade mínima;
|
|
23
|
+
3. P1: detail schema/lazy/deep-link seguro/editor completo;
|
|
24
|
+
4. P2: altura dinâmica em virtualização + perf avançada.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 3. Baseline Técnico (estado atual da Praxis)
|
|
29
|
+
|
|
30
|
+
1. não existe `behavior.expansion` no contrato atual;
|
|
31
|
+
2. não há detail row no runtime atual;
|
|
32
|
+
3. há dois caminhos de render (`mat-table` e `cdk-virtual-scroll`);
|
|
33
|
+
4. virtualização atual usa `itemSize` fixo;
|
|
34
|
+
5. já existem fundações reutilizáveis:
|
|
35
|
+
- persistência com ack;
|
|
36
|
+
- warnings deduplicados;
|
|
37
|
+
- avaliação DSL segura;
|
|
38
|
+
- i18n e `aria-live`;
|
|
39
|
+
- editor robusto para seções de comportamento.
|
|
40
|
+
|
|
41
|
+
## 3.1 Impacto cross-lib (obrigatório no plano)
|
|
42
|
+
|
|
43
|
+
`behavior.expansion` nasce no contrato de `@praxisui/core`, então rollout exige alinhamento conjunto:
|
|
44
|
+
|
|
45
|
+
1. model/types/defaults em `praxis-core`;
|
|
46
|
+
2. editor/runtime/docs/testes em `praxis-table`;
|
|
47
|
+
3. metadata de AI/context pack com os novos paths.
|
|
48
|
+
|
|
49
|
+
## 3.2 AS-IS vs TO-BE (Schema-Driven UI da tabela dinâmica)
|
|
50
|
+
|
|
51
|
+
| Camada | AS-IS (runtime atual) | TO-BE para expandable rows | Gap crítico |
|
|
52
|
+
| --- | --- | --- | --- |
|
|
53
|
+
| Contrato `TableConfig` | `TableBehaviorConfig` não possui `expansion` | Adicionar `behavior.expansion` com defaults e coerções fail-closed | Sem campo nativo, não há governança de estado de expansão |
|
|
54
|
+
| Resolução de modo de dados | `DataMode` já resolve `remote/local/empty`, com precedência `resourcePath > data > persistido` | Reutilizar resolução existente sem bypass na expansão | Evitar branch paralelo de estado que ignore `DataMode` |
|
|
55
|
+
| Pipeline local | `computeLocalViewPipeline` + `matchesAdvancedCriteria` já cobre filtro/sort/paginação | Integrar `collapseOn.*` e limites de expansão ao recálculo local | Risco de drift entre recálculo local e estado expandido |
|
|
56
|
+
| Filtro schema-driven | `praxis-filter` usa schema remoto ou `fieldMetadata` local derivado de colunas | Detail schema deve seguir mesma governança (validator compartilhado runtime/editor) | Duas engines de validação diferentes geram incoerência |
|
|
57
|
+
| Render surface | Dois caminhos ativos: `mat-table` e `cdk-virtual-scroll` | Expandir primeiro em `mat-table` (P0A) e depois virtualizado fixo (P0B) | Detail dinâmico no virtualizado quebra premissa de altura fixa |
|
|
58
|
+
| Segurança/privacidade | Já existe disciplina de schema/metadata e modo local com guard rails | Eventos/URL/persistência da expansão entram com redaction + canonicalização + anti-injection | Vazamento de IDs e state-injection se aplicar padrão antigo |
|
|
59
|
+
| Observabilidade | Adapter/sink e padrões de métrica já existem no ecossistema da tabela | Incluir namespace `praxis.table.expansion.*` no P0/P1 | Telemetria ad-hoc sem contrato aumenta risco operacional |
|
|
60
|
+
| Editor/IA | Editor de comportamento maduro + AI context pack para paths existentes | Nova seção `Expansion` + update de capabilities/context pack | IA continuará sugerindo contrato incompleto se não atualizar metadados |
|
|
61
|
+
| Documentação conceitual | JSON API da tabela é forte, mas link de conceito global (`schema-driven-ui.md`) não está resolvido localmente | Registrar conceito no workspace/documentação efetiva da lib | Onboarding e governança prejudicados por referência quebrada |
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 4. Matriz Estratégica (benchmark resumido)
|
|
66
|
+
|
|
67
|
+
| Recurso | AG Grid | MUI | TanStack | Kendo | Praxis alvo |
|
|
68
|
+
| --- | --- | --- | --- | --- | --- |
|
|
69
|
+
| Master/detail base | Forte | Forte | Flexível headless | Forte | Paridade |
|
|
70
|
+
| Estado controlado | Forte | Forte | Forte | Forte | Paridade |
|
|
71
|
+
| Virtualização + detail | Forte | Forte (com caveats) | Depende | Forte | Paridade com política explícita |
|
|
72
|
+
| Lazy detail | Forte | Forte | Depende | Forte | Paridade |
|
|
73
|
+
| A11y | Forte | Forte | Depende | Forte | Paridade |
|
|
74
|
+
| JSON no-code | Não foco | Não foco | Não foco | Não foco | Acima (diferencial) |
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 5. Decisões Mandatórias
|
|
79
|
+
|
|
80
|
+
1. contrato determinístico de estado/precedência;
|
|
81
|
+
2. política de virtualização formal (`fixed-height-only` no P0);
|
|
82
|
+
3. regra explícita de precedência de interação;
|
|
83
|
+
4. persistência robusta no P0 e deep-link seguro no P1;
|
|
84
|
+
5. round-trip obrigatório em model/editor/json-api/AI capabilities;
|
|
85
|
+
6. telemetria mínima operacional já no P0/P1 com adapter/sink explícito;
|
|
86
|
+
7. defaults de segurança ativos por padrão no runtime (fail-closed).
|
|
87
|
+
8. quando o backend expuser `_links.capabilities`, o detail row deve preferir o snapshot agregado canônico em vez de reintroduzir `resourcePath` ad hoc no host.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 6. Contrato JSON Proposto (V5)
|
|
92
|
+
|
|
93
|
+
## 6.1 JSON alvo
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"behavior": {
|
|
98
|
+
"expansion": {
|
|
99
|
+
"enabled": true,
|
|
100
|
+
"state": {
|
|
101
|
+
"mode": "controlled",
|
|
102
|
+
"expandedRowKeys": [],
|
|
103
|
+
"emitChanges": true,
|
|
104
|
+
"sourceOfTruth": "input",
|
|
105
|
+
"onSetExpandedKeys": "emitOnly"
|
|
106
|
+
},
|
|
107
|
+
"identity": {
|
|
108
|
+
"rowKeySource": "table.idField",
|
|
109
|
+
"requireStableIdField": true
|
|
110
|
+
},
|
|
111
|
+
"interaction": {
|
|
112
|
+
"trigger": "icon",
|
|
113
|
+
"toggleOnRowClick": false,
|
|
114
|
+
"keyboard": {
|
|
115
|
+
"profile": "disclosure",
|
|
116
|
+
"enterSpace": true,
|
|
117
|
+
"arrowLeftRight": false
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"limits": {
|
|
121
|
+
"allowMultiple": false,
|
|
122
|
+
"maxExpandedRows": 1,
|
|
123
|
+
"onOverflow": "collapseOldest"
|
|
124
|
+
},
|
|
125
|
+
"collapseOn": {
|
|
126
|
+
"sortChange": true,
|
|
127
|
+
"pageChange": true,
|
|
128
|
+
"filterChange": true,
|
|
129
|
+
"dataRefresh": true,
|
|
130
|
+
"groupChange": true,
|
|
131
|
+
"columnVisibilityChange": true,
|
|
132
|
+
"columnOrderChange": true,
|
|
133
|
+
"columnResizeChange": true,
|
|
134
|
+
"densityChange": true,
|
|
135
|
+
"rowSelectionChange": false,
|
|
136
|
+
"viewportDataWindowChange": false
|
|
137
|
+
},
|
|
138
|
+
"rowExpandableWhen": {
|
|
139
|
+
"expr": "row.status != 'ARCHIVED'",
|
|
140
|
+
"onError": "deny",
|
|
141
|
+
"context": ["row", "computed"]
|
|
142
|
+
},
|
|
143
|
+
"detail": {
|
|
144
|
+
"schemaContract": {
|
|
145
|
+
"kind": "praxis.detail.schema",
|
|
146
|
+
"version": "1.0.0",
|
|
147
|
+
"compat": "semver",
|
|
148
|
+
"allowedNodes": ["layout", "stack", "tabs", "tab", "card", "value", "action", "list", "formRef", "tableRef", "chartRef", "richText", "templateRef"],
|
|
149
|
+
"sanitization": "strict"
|
|
150
|
+
},
|
|
151
|
+
"source": {
|
|
152
|
+
"mode": "resource",
|
|
153
|
+
"resource": {
|
|
154
|
+
"kind": "ui-composition",
|
|
155
|
+
"id": "order-detail-v1",
|
|
156
|
+
"version": "1.0.0"
|
|
157
|
+
},
|
|
158
|
+
"contextMap": {
|
|
159
|
+
"orderId": "$row.id",
|
|
160
|
+
"tenantId": "$ctx.tenantId"
|
|
161
|
+
},
|
|
162
|
+
"fallbackMode": "inline",
|
|
163
|
+
"inlineSchema": {
|
|
164
|
+
"layout": "stack",
|
|
165
|
+
"items": [
|
|
166
|
+
{ "type": "card", "title": "Resumo", "fields": [{ "label": "Pedido", "value": "$data.orderNumber" }] },
|
|
167
|
+
{ "type": "formRef", "schemaId": "order-edit-form-v2" },
|
|
168
|
+
{ "type": "tableRef", "schemaId": "order-items-table-v1" }
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
"rendering": {
|
|
173
|
+
"strategy": "registry",
|
|
174
|
+
"registryId": "praxis.detail.default",
|
|
175
|
+
"hostLayout": "auto",
|
|
176
|
+
"fallbackNodePolicy": "failClosed"
|
|
177
|
+
},
|
|
178
|
+
"height": {
|
|
179
|
+
"mode": "fixed",
|
|
180
|
+
"px": 160
|
|
181
|
+
},
|
|
182
|
+
"lazyLoad": {
|
|
183
|
+
"enabled": true,
|
|
184
|
+
"actionId": "fetchOrderDetails",
|
|
185
|
+
"cache": { "enabled": true, "ttlMs": 300000 },
|
|
186
|
+
"retry": { "maxAttempts": 2 },
|
|
187
|
+
"cancelOnCollapse": true,
|
|
188
|
+
"dedupeByRowKey": true
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
"virtualization": {
|
|
192
|
+
"policy": "fixed-height-only"
|
|
193
|
+
},
|
|
194
|
+
"persistence": {
|
|
195
|
+
"enabled": true,
|
|
196
|
+
"storageKey": "expanded-rows",
|
|
197
|
+
"storageKeyStrategy": {
|
|
198
|
+
"namespace": "praxis.table.expansion",
|
|
199
|
+
"version": "v1",
|
|
200
|
+
"hashScope": true
|
|
201
|
+
},
|
|
202
|
+
"scope": ["tableId", "user", "tenant"],
|
|
203
|
+
"clearOn": ["resetPreferences", "logout", "tenantChange"]
|
|
204
|
+
},
|
|
205
|
+
"security": {
|
|
206
|
+
"eventExposureDefault": {
|
|
207
|
+
"rowId": "hashed",
|
|
208
|
+
"expandedKeys": "none"
|
|
209
|
+
},
|
|
210
|
+
"allowRawExposure": false
|
|
211
|
+
},
|
|
212
|
+
"deepLink": {
|
|
213
|
+
"enabled": false,
|
|
214
|
+
"queryParam": "expanded",
|
|
215
|
+
"encoding": "csv",
|
|
216
|
+
"keyFormat": "^[a-zA-Z0-9_-]{1,64}$",
|
|
217
|
+
"maxKeys": 5,
|
|
218
|
+
"maxLength": 256,
|
|
219
|
+
"parsing": {
|
|
220
|
+
"duplicateParams": "firstWins",
|
|
221
|
+
"decodePasses": 1,
|
|
222
|
+
"trimWhitespace": true,
|
|
223
|
+
"sortKeysBeforeApply": true
|
|
224
|
+
},
|
|
225
|
+
"integrity": {
|
|
226
|
+
"mode": "opaqueToken",
|
|
227
|
+
"exchange": {
|
|
228
|
+
"endpointId": "resolveExpandedKeysToken",
|
|
229
|
+
"ttlMs": 300000,
|
|
230
|
+
"request": {
|
|
231
|
+
"token": "string",
|
|
232
|
+
"tableId": "string"
|
|
233
|
+
},
|
|
234
|
+
"response": {
|
|
235
|
+
"expandedRowKeys": "string[]"
|
|
236
|
+
},
|
|
237
|
+
"errors": ["expired", "invalid", "rate_limited"]
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
"privacy": {
|
|
241
|
+
"mode": "denyByDefault",
|
|
242
|
+
"allowListTables": []
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Nota de escopo por fase:
|
|
251
|
+
|
|
252
|
+
1. P0 usa contexto DSL `row/computed` (alinhado ao runtime atual);
|
|
253
|
+
2. `user/env` entram apenas em P1 via contexto injetado pelo host com contrato explícito;
|
|
254
|
+
3. `interaction.keyboard.profile` inicia em `disclosure` no P0; perfil `grid` entra com suíte dedicada no P1.
|
|
255
|
+
|
|
256
|
+
## 6.2 Regras de validação (fail-closed)
|
|
257
|
+
|
|
258
|
+
1. `enabled=true` exige `identity.requireStableIdField=true` e `table.idField` estável -> sem isso, desabilitar expansão + warning + diagnóstico estruturado.
|
|
259
|
+
2. `mode=controlled` -> runtime não muta `expandedRowKeys`; somente emite evento.
|
|
260
|
+
3. `allowMultiple=false` com `maxExpandedRows>1` -> coerção para `1`.
|
|
261
|
+
4. `lazyLoad.enabled=true` sem `actionId` -> bloqueio do lazy + diagnóstico.
|
|
262
|
+
5. `detail.source.mode` deve ser `inline`, `resource` ou `resourcePath`.
|
|
263
|
+
6. `detail.source.mode=inline` sem `detail.source.inlineSchema` -> inválido.
|
|
264
|
+
7. `detail.source.mode=resource` sem `detail.source.resource.kind/id/version` -> inválido.
|
|
265
|
+
8. `detail.source.mode=resourcePath` sem `detail.source.resourcePath.path/method` -> inválido.
|
|
266
|
+
9. `detail.source.mode=resourcePath` sem `detail.source.resourceAllowList` em ambiente restrito -> inválido.
|
|
267
|
+
10. `detail.rendering.strategy=registry` sem `detail.rendering.registryId` -> inválido.
|
|
268
|
+
11. nó presente no schema sem renderer registrado -> fail-closed (`EXPANSION_DETAIL_RENDERER_MISSING`).
|
|
269
|
+
12. `schemaContract.compat=semver` com versão incompatível -> fail-closed + hint de migração.
|
|
270
|
+
13. `detail.height.mode=dynamic` com virtualização ativa e política fixa -> expansão bloqueada no modo virtualizado.
|
|
271
|
+
14. `interaction.keyboard.profile=disclosure` com `arrowLeftRight=true` -> coerção para `false`.
|
|
272
|
+
15. `interaction.keyboard.profile=grid` sem semântica de grid habilitada -> fallback para `disclosure` + diagnóstico.
|
|
273
|
+
16. deep-link deve canonicalizar (`decodePasses=1`, `duplicateParams=firstWins`, trim + ordenação) antes de validar formato.
|
|
274
|
+
17. deep-link acima de `maxLength`/`maxKeys` -> parsing negado + limpeza segura (somente quando `deepLink.enabled=true` em P1).
|
|
275
|
+
18. deep-link fora de `keyFormat` -> parsing negado + diagnóstico (somente quando `deepLink.enabled=true` em P1).
|
|
276
|
+
19. `security.allowRawExposure=false` -> runtime bloqueia emissão `raw` em eventos/logs.
|
|
277
|
+
20. `persistence.enabled=true` sem `storageKeyStrategy.namespace/version` -> invalidar persistência + diagnóstico.
|
|
278
|
+
21. token opaco inválido/expirado -> deep-link rejeitado com diagnóstico (P1).
|
|
279
|
+
|
|
280
|
+
## 6.3 Contrato semântico do Detail Schema (AST)
|
|
281
|
+
|
|
282
|
+
1. `schemaContract.kind` define namespace estável (`praxis.detail.schema`).
|
|
283
|
+
2. `schemaContract.compat=semver` define política de compatibilidade.
|
|
284
|
+
3. `schemaContract.allowedNodes` limita nós permitidos do AST.
|
|
285
|
+
4. `schemaContract.sanitization=strict` força sanitização forte no runtime.
|
|
286
|
+
5. toda validação de AST deve ser executada por `SchemaContractValidator` (runtime e editor).
|
|
287
|
+
|
|
288
|
+
Exemplo de estrutura AST mínima suportada:
|
|
289
|
+
|
|
290
|
+
```json
|
|
291
|
+
{
|
|
292
|
+
"layout": "tabs",
|
|
293
|
+
"items": [
|
|
294
|
+
{
|
|
295
|
+
"type": "tab",
|
|
296
|
+
"id": "summary",
|
|
297
|
+
"label": "Resumo",
|
|
298
|
+
"content": [{ "type": "card", "title": "Status", "fields": [{ "label": "Status", "value": "$data.status" }] }]
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"type": "tab",
|
|
302
|
+
"id": "items",
|
|
303
|
+
"label": "Itens",
|
|
304
|
+
"content": [{ "type": "tableRef", "schemaId": "order-items-table-v1" }]
|
|
305
|
+
}
|
|
306
|
+
]
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## 6.4 Funcionamento da Feature em Modelo Schema-Driven UI
|
|
311
|
+
|
|
312
|
+
Pipeline de execução (runtime):
|
|
313
|
+
|
|
314
|
+
1. carregar `behavior.expansion` do JSON;
|
|
315
|
+
2. validar shape com schema fechado (sem propriedades órfãs) + regras fail-closed;
|
|
316
|
+
3. resolver fonte do schema de detalhe (`detail.source.mode=inline|resource|resourcePath`);
|
|
317
|
+
4. transformar AST em render tree via registry de renderers (`layout`, `tabs`, `card`, `formRef`, `tableRef`, `chartRef`, `richText`, `value`, `action`, `list`);
|
|
318
|
+
5. renderizar nós por componente Angular dinâmico, com bindings estritos de contexto (`row`, `computed`);
|
|
319
|
+
6. aplicar políticas de layout/altura por modo de render (não virtualizado vs virtualizado fixo);
|
|
320
|
+
7. aplicar lazy load/caching/retry/cancel e anunciar estado assíncrono com `aria-busy` + `aria-live`;
|
|
321
|
+
8. emitir `rowExpansionChange` redigido por política de exposição e publicar métricas.
|
|
322
|
+
|
|
323
|
+
Diretrizes de arquitetura Schema-Driven UI:
|
|
324
|
+
|
|
325
|
+
1. runtime e editor devem compartilhar o mesmo `SchemaContractValidator` para evitar drift;
|
|
326
|
+
2. renderização deve usar whitelist de tipos e contratos (`schemaContract.allowedNodes`), nunca execução arbitrária;
|
|
327
|
+
3. registry de renderers precisa ser extensível por token/injeção (enterprise plugin model) com fallback seguro;
|
|
328
|
+
4. DSL e parsing URL usam canonicalização determinística antes de validação para reduzir bypass;
|
|
329
|
+
5. feature flags por fase (`P0/P1/P2`) devem bloquear capacidades não suportadas no caminho virtualizado.
|
|
330
|
+
|
|
331
|
+
## 6.4.1 Princípio arquitetural (Praxis)
|
|
332
|
+
|
|
333
|
+
1. `detail` é sempre schema-driven (não há bifurcação de tipo em `schema|template`).
|
|
334
|
+
2. o que varia é a **fonte do schema** (`source.mode`) e a **estratégia de renderização** (`rendering.*`).
|
|
335
|
+
3. nós de composição (`card`, `formRef`, `tableRef`, `chartRef`, `richText`, `tabs`, `list`) são capacidades de plataforma, não exceções por caso.
|
|
336
|
+
|
|
337
|
+
Exemplo A (`source.mode=resource`, recomendado enterprise):
|
|
338
|
+
|
|
339
|
+
```json
|
|
340
|
+
{
|
|
341
|
+
"detail": {
|
|
342
|
+
"schemaContract": { "kind": "praxis.detail.schema", "version": "1.0.0", "compat": "semver" },
|
|
343
|
+
"source": {
|
|
344
|
+
"mode": "resource",
|
|
345
|
+
"resource": { "kind": "ui-composition", "id": "customer-detail-v2", "version": "2.1.0" },
|
|
346
|
+
"contextMap": { "customerId": "$row.id", "tenantId": "$ctx.tenantId" }
|
|
347
|
+
},
|
|
348
|
+
"rendering": { "strategy": "registry", "registryId": "praxis.detail.default" }
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Exemplo B (`source.mode=resourcePath`, integrado ao pipeline CRUD):
|
|
354
|
+
|
|
355
|
+
```json
|
|
356
|
+
{
|
|
357
|
+
"detail": {
|
|
358
|
+
"schemaContract": { "kind": "praxis.detail.schema", "version": "1.0.0", "compat": "semver" },
|
|
359
|
+
"source": {
|
|
360
|
+
"mode": "resourcePath",
|
|
361
|
+
"resourcePath": {
|
|
362
|
+
"path": "orders/{orderId}/detail-schema",
|
|
363
|
+
"paramsMap": { "orderId": "$row.id" },
|
|
364
|
+
"method": "GET"
|
|
365
|
+
},
|
|
366
|
+
"resourceAllowList": ["orders/*/detail-schema"]
|
|
367
|
+
},
|
|
368
|
+
"rendering": { "strategy": "registry", "registryId": "praxis.detail.default" }
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Exemplo C (`source.mode=inline`, fallback/local):
|
|
374
|
+
|
|
375
|
+
```json
|
|
376
|
+
{
|
|
377
|
+
"detail": {
|
|
378
|
+
"schemaContract": {
|
|
379
|
+
"kind": "praxis.detail.schema",
|
|
380
|
+
"version": "1.0.0",
|
|
381
|
+
"compat": "semver",
|
|
382
|
+
"allowedNodes": ["tabs", "tab", "card", "formRef", "tableRef", "chartRef", "richText"]
|
|
383
|
+
},
|
|
384
|
+
"source": {
|
|
385
|
+
"mode": "inline",
|
|
386
|
+
"inlineSchema": {
|
|
387
|
+
"layout": "tabs",
|
|
388
|
+
"items": [
|
|
389
|
+
{ "type": "tab", "id": "overview", "label": "Resumo", "content": [{ "type": "card", "title": "Conta" }] },
|
|
390
|
+
{ "type": "tab", "id": "edit", "label": "Formulário", "content": [{ "type": "formRef", "schemaId": "account-edit-v1" }] },
|
|
391
|
+
{ "type": "tab", "id": "history", "label": "Histórico", "content": [{ "type": "tableRef", "schemaId": "account-history-v1" }] }
|
|
392
|
+
]
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
"rendering": { "strategy": "registry", "registryId": "praxis.detail.default", "hostLayout": "tabs" }
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## 6.5 Evolução de Contrato (Deep Dive)
|
|
401
|
+
|
|
402
|
+
## 6.5.1 Fronteiras de contrato que precisam evoluir
|
|
403
|
+
|
|
404
|
+
1. `TableBehaviorConfig` em `@praxisui/core` deve ganhar `expansion?: TableExpansionConfig` como capacidade nativa.
|
|
405
|
+
2. `createDefaultTableConfig()` deve incluir defaults explícitos de `behavior.expansion` com `enabled=false` (mudança aditiva, sem ativação implícita).
|
|
406
|
+
3. metadata pública do componente (`PRAXIS_TABLE_COMPONENT_METADATA`) deve declarar o novo evento `rowExpansionChange`.
|
|
407
|
+
4. JSON API da tabela deve publicar paths `behavior.expansion.*` com status (`Active/Partial/Schema-only`) por fase.
|
|
408
|
+
5. catálogo de capacidades AI/context pack deve incluir os novos paths para evitar recomendações inválidas.
|
|
409
|
+
6. painel/editor de comportamento deve refletir as mesmas coerções do runtime (single source of truth para validação).
|
|
410
|
+
|
|
411
|
+
## 6.5.2 Proposta de shape tipado em `@praxisui/core` (draft)
|
|
412
|
+
|
|
413
|
+
```ts
|
|
414
|
+
export interface TableBehaviorConfig {
|
|
415
|
+
pagination?: PaginationConfig;
|
|
416
|
+
sorting?: SortingConfig;
|
|
417
|
+
filtering?: FilteringConfig;
|
|
418
|
+
selection?: SelectionConfig;
|
|
419
|
+
interaction?: InteractionConfig;
|
|
420
|
+
loading?: LoadingConfig;
|
|
421
|
+
emptyState?: EmptyStateConfig;
|
|
422
|
+
virtualization?: VirtualizationConfig;
|
|
423
|
+
resizing?: ResizingConfig;
|
|
424
|
+
dragging?: DraggingConfig;
|
|
425
|
+
localDataMode?: TableLocalDataModeConfig;
|
|
426
|
+
expansion?: TableExpansionConfig; // NOVO
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export interface TableExpansionConfig {
|
|
430
|
+
enabled?: boolean;
|
|
431
|
+
contractVersion?: '1.0.0';
|
|
432
|
+
identity?: {
|
|
433
|
+
rowKeySource?: 'table.idField';
|
|
434
|
+
requireStableIdField?: boolean;
|
|
435
|
+
};
|
|
436
|
+
state?: {
|
|
437
|
+
mode?: 'controlled' | 'uncontrolled';
|
|
438
|
+
expandedRowKeys?: string[];
|
|
439
|
+
emitChanges?: boolean;
|
|
440
|
+
sourceOfTruth?: 'input' | 'runtime';
|
|
441
|
+
onSetExpandedKeys?: 'emitOnly' | 'mutateInternal';
|
|
442
|
+
};
|
|
443
|
+
interaction?: {
|
|
444
|
+
trigger?: 'icon' | 'row' | 'both';
|
|
445
|
+
toggleOnRowClick?: boolean;
|
|
446
|
+
keyboard?: {
|
|
447
|
+
profile?: 'disclosure' | 'grid';
|
|
448
|
+
enterSpace?: boolean;
|
|
449
|
+
arrowLeftRight?: boolean;
|
|
450
|
+
};
|
|
451
|
+
};
|
|
452
|
+
limits?: {
|
|
453
|
+
allowMultiple?: boolean;
|
|
454
|
+
maxExpandedRows?: number;
|
|
455
|
+
onOverflow?: 'collapseOldest' | 'denyNew' | 'collapseAll';
|
|
456
|
+
};
|
|
457
|
+
collapseOn?: {
|
|
458
|
+
sortChange?: boolean;
|
|
459
|
+
pageChange?: boolean;
|
|
460
|
+
filterChange?: boolean;
|
|
461
|
+
dataRefresh?: boolean;
|
|
462
|
+
groupChange?: boolean;
|
|
463
|
+
columnVisibilityChange?: boolean;
|
|
464
|
+
columnOrderChange?: boolean;
|
|
465
|
+
columnResizeChange?: boolean;
|
|
466
|
+
densityChange?: boolean;
|
|
467
|
+
rowSelectionChange?: boolean;
|
|
468
|
+
viewportDataWindowChange?: boolean;
|
|
469
|
+
};
|
|
470
|
+
rowExpandableWhen?: {
|
|
471
|
+
expr?: string;
|
|
472
|
+
onError?: 'deny' | 'allow';
|
|
473
|
+
context?: Array<'row' | 'computed' | 'user' | 'env'>;
|
|
474
|
+
};
|
|
475
|
+
detail?: {
|
|
476
|
+
schemaContract?: {
|
|
477
|
+
kind?: 'praxis.detail.schema';
|
|
478
|
+
version?: string;
|
|
479
|
+
compat?: 'semver';
|
|
480
|
+
allowedNodes?: Array<
|
|
481
|
+
| 'layout'
|
|
482
|
+
| 'stack'
|
|
483
|
+
| 'tabs'
|
|
484
|
+
| 'tab'
|
|
485
|
+
| 'card'
|
|
486
|
+
| 'value'
|
|
487
|
+
| 'action'
|
|
488
|
+
| 'list'
|
|
489
|
+
| 'formRef'
|
|
490
|
+
| 'tableRef'
|
|
491
|
+
| 'chartRef'
|
|
492
|
+
| 'richText'
|
|
493
|
+
| 'templateRef'
|
|
494
|
+
>;
|
|
495
|
+
sanitization?: 'strict';
|
|
496
|
+
};
|
|
497
|
+
source?: {
|
|
498
|
+
mode?: 'inline' | 'resource' | 'resourcePath';
|
|
499
|
+
inlineSchema?: Record<string, unknown>;
|
|
500
|
+
resource?: {
|
|
501
|
+
kind?: 'ui-composition' | 'form-schema' | 'table-schema' | 'dashboard-schema';
|
|
502
|
+
id?: string;
|
|
503
|
+
version?: string;
|
|
504
|
+
};
|
|
505
|
+
resourcePath?: {
|
|
506
|
+
path?: string;
|
|
507
|
+
paramsMap?: Record<string, string>;
|
|
508
|
+
method?: 'GET' | 'POST';
|
|
509
|
+
};
|
|
510
|
+
contextMap?: Record<string, string>;
|
|
511
|
+
resourceAllowList?: string[];
|
|
512
|
+
fallbackMode?: 'none' | 'inline' | 'resource';
|
|
513
|
+
};
|
|
514
|
+
rendering?: {
|
|
515
|
+
strategy?: 'registry';
|
|
516
|
+
registryId?: string;
|
|
517
|
+
hostLayout?: 'auto' | 'stack' | 'tabs';
|
|
518
|
+
fallbackNodePolicy?: 'failClosed' | 'renderPlaceholder';
|
|
519
|
+
};
|
|
520
|
+
height?: { mode?: 'fixed' | 'dynamic'; px?: number };
|
|
521
|
+
lazyLoad?: {
|
|
522
|
+
enabled?: boolean;
|
|
523
|
+
actionId?: string;
|
|
524
|
+
cache?: { enabled?: boolean; ttlMs?: number };
|
|
525
|
+
retry?: { maxAttempts?: number };
|
|
526
|
+
cancelOnCollapse?: boolean;
|
|
527
|
+
dedupeByRowKey?: boolean;
|
|
528
|
+
};
|
|
529
|
+
};
|
|
530
|
+
virtualization?: {
|
|
531
|
+
policy?: 'fixed-height-only' | 'allow-dynamic-under-flag';
|
|
532
|
+
};
|
|
533
|
+
persistence?: {
|
|
534
|
+
enabled?: boolean;
|
|
535
|
+
storageKey?: string;
|
|
536
|
+
storageKeyStrategy?: {
|
|
537
|
+
namespace?: string;
|
|
538
|
+
version?: string;
|
|
539
|
+
hashScope?: boolean;
|
|
540
|
+
};
|
|
541
|
+
scope?: Array<'tableId' | 'componentInstance' | 'user' | 'tenant'>;
|
|
542
|
+
clearOn?: Array<'resetPreferences' | 'logout' | 'tenantChange'>;
|
|
543
|
+
};
|
|
544
|
+
security?: {
|
|
545
|
+
eventExposureDefault?: {
|
|
546
|
+
rowId?: 'redacted' | 'hashed' | 'raw';
|
|
547
|
+
expandedKeys?: 'none' | 'hashed' | 'raw';
|
|
548
|
+
};
|
|
549
|
+
allowRawExposure?: boolean;
|
|
550
|
+
};
|
|
551
|
+
deepLink?: {
|
|
552
|
+
enabled?: boolean;
|
|
553
|
+
queryParam?: string;
|
|
554
|
+
encoding?: 'csv';
|
|
555
|
+
keyFormat?: string;
|
|
556
|
+
maxKeys?: number;
|
|
557
|
+
maxLength?: number;
|
|
558
|
+
parsing?: {
|
|
559
|
+
duplicateParams?: 'firstWins' | 'reject';
|
|
560
|
+
decodePasses?: 1;
|
|
561
|
+
trimWhitespace?: boolean;
|
|
562
|
+
sortKeysBeforeApply?: boolean;
|
|
563
|
+
};
|
|
564
|
+
integrity?: {
|
|
565
|
+
mode?: 'opaqueToken';
|
|
566
|
+
exchange?: {
|
|
567
|
+
endpointId?: string;
|
|
568
|
+
ttlMs?: number;
|
|
569
|
+
errors?: Array<'expired' | 'invalid' | 'rate_limited'>;
|
|
570
|
+
};
|
|
571
|
+
};
|
|
572
|
+
privacy?: {
|
|
573
|
+
mode?: 'denyByDefault' | 'allowByDefault';
|
|
574
|
+
allowListTables?: string[];
|
|
575
|
+
};
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
## 6.5.3 Semântica de versionamento (sem breaking change)
|
|
581
|
+
|
|
582
|
+
1. manter `meta.version='2.0.0'` no contrato raiz para preservar consumidores atuais.
|
|
583
|
+
2. versionar a capability nova em `behavior.expansion.contractVersion='1.0.0'`.
|
|
584
|
+
3. tratar ausência de `behavior.expansion` como feature desligada (`enabled=false`) e sem warnings de erro.
|
|
585
|
+
4. evoluções incompatíveis futuras da expansão devem subir `contractVersion` local (não o `meta.version` global).
|
|
586
|
+
|
|
587
|
+
## 6.5.4 Regras formais de precedência e compatibilidade
|
|
588
|
+
|
|
589
|
+
1. estado: `controlled input` > `deepLink válido` > `persistência` > vazio.
|
|
590
|
+
2. dados: expansão não altera precedência de `DataMode` já existente (`resourcePath > data > persistido`).
|
|
591
|
+
3. teclado: `behavior.interaction.keyboard` continua governando navegação global da tabela; `behavior.expansion.interaction.keyboard` governa toggle de expansão.
|
|
592
|
+
4. virtualização: `behavior.virtualization.strategy='dynamic'` não habilita detalhe dinâmico automaticamente; depende de `expansion.virtualization.policy` + fase/flag.
|
|
593
|
+
5. eventos: payload deve ser discriminado por exposição (`none/hashed/raw`) para impedir vazamento acidental em compile-time/runtime.
|
|
594
|
+
|
|
595
|
+
## 6.5.5 Validação e coerção obrigatórias (runtime + editor)
|
|
596
|
+
|
|
597
|
+
1. validação estrutural: rejeitar propriedades órfãs em `behavior.expansion` (schema fechado).
|
|
598
|
+
2. validação semântica: `enabled=true` exige `table.idField` estável e resolvível.
|
|
599
|
+
3. coerções determinísticas: conflitos (`allowMultiple=false` + `maxExpandedRows>1`) são normalizados.
|
|
600
|
+
4. canonicalização URL: parse/canonicalize/validate sempre nessa ordem.
|
|
601
|
+
5. política de segurança: `allowRawExposure=false` bloqueia emissão raw mesmo se consumidor tentar forçar.
|
|
602
|
+
|
|
603
|
+
## 6.5.6 Taxonomia de diagnósticos (padronizar códigos)
|
|
604
|
+
|
|
605
|
+
1. `EXPANSION_IDFIELD_REQUIRED`
|
|
606
|
+
2. `EXPANSION_KEYBOARD_PROFILE_FALLBACK`
|
|
607
|
+
3. `EXPANSION_VIEWPORT_POLICY_RENAMED`
|
|
608
|
+
4. `EXPANSION_DEEPLINK_DEFERRED_P1`
|
|
609
|
+
5. `EXPANSION_DEEPLINK_PARSE_REJECTED`
|
|
610
|
+
6. `EXPANSION_SCHEMA_CONTRACT_INVALID`
|
|
611
|
+
7. `EXPANSION_DETAIL_SOURCE_INVALID`
|
|
612
|
+
8. `EXPANSION_DETAIL_RESOURCE_INVALID`
|
|
613
|
+
9. `EXPANSION_DETAIL_RESOURCEPATH_INVALID`
|
|
614
|
+
10. `EXPANSION_DETAIL_RENDERER_MISSING`
|
|
615
|
+
11. `EXPANSION_DETAIL_LEGACY_TEMPLATE_MIGRATED`
|
|
616
|
+
12. `EXPANSION_PERSISTENCE_STRATEGY_INVALID`
|
|
617
|
+
13. `EXPANSION_VIRTUALIZATION_DYNAMIC_BLOCKED`
|
|
618
|
+
|
|
619
|
+
## 6.5.7 Matriz de migração de contrato (configs legadas)
|
|
620
|
+
|
|
621
|
+
| Condição legada | Transformação | Diagnóstico |
|
|
622
|
+
| --- | --- | --- |
|
|
623
|
+
| sem `behavior.expansion` | inserir defaults (`enabled=false`) | nenhum |
|
|
624
|
+
| `collapseOn.viewportChange` | renomear para `viewportDataWindowChange=false` | `EXPANSION_VIEWPORT_POLICY_RENAMED` |
|
|
625
|
+
| `keyboard.profile` ausente | default `disclosure` | nenhum |
|
|
626
|
+
| `profile=disclosure` + `arrowLeftRight=true` | coerção para `false` | `EXPANSION_KEYBOARD_PROFILE_FALLBACK` |
|
|
627
|
+
| `persistence.enabled=true` sem `storageKeyStrategy` | inserir default namespaced/versionado | `EXPANSION_PERSISTENCE_STRATEGY_INVALID` |
|
|
628
|
+
| legado `detail.type=template` + `templateId` | migrar para `source.mode=inline` + `inlineSchema.items=[{ type:'templateRef', id: templateId }]` | `EXPANSION_DETAIL_LEGACY_TEMPLATE_MIGRATED` |
|
|
629
|
+
| `detail.source.mode=resource` sem `resource.kind/id/version` | fail-closed | `EXPANSION_DETAIL_RESOURCE_INVALID` |
|
|
630
|
+
| `detail.source.mode=resourcePath` sem `resourcePath.path/method` | fail-closed | `EXPANSION_DETAIL_RESOURCEPATH_INVALID` |
|
|
631
|
+
| `detail.rendering.strategy=registry` sem `registryId` | fail-closed | `EXPANSION_DETAIL_SOURCE_INVALID` |
|
|
632
|
+
| sem `schemaContract.kind/version` | fail-closed | `EXPANSION_SCHEMA_CONTRACT_INVALID` |
|
|
633
|
+
|
|
634
|
+
## 6.5.8 Artefatos impactados (implementação obrigatória)
|
|
635
|
+
|
|
636
|
+
1. `projects/praxis-core/src/lib/models/table-config-v2.model.ts`
|
|
637
|
+
2. `projects/praxis-core/src/lib/models/table-config.model.ts`
|
|
638
|
+
3. `projects/praxis-table/src/lib/praxis-table.metadata.ts`
|
|
639
|
+
4. `projects/praxis-table/src/lib/praxis-table.json-api.md`
|
|
640
|
+
5. `projects/praxis-table/src/lib/ai/table-ai-capabilities.ts`
|
|
641
|
+
6. `projects/praxis-table/src/lib/ai/table-context-pack.ts`
|
|
642
|
+
7. `projects/praxis-table/src/lib/behavior-config-editor/behavior-config-editor.component.ts`
|
|
643
|
+
|
|
644
|
+
---
|
|
645
|
+
|
|
646
|
+
## 7. Precedência Oficial de Estado
|
|
647
|
+
|
|
648
|
+
## 7.1 Fonte da verdade (ordem)
|
|
649
|
+
|
|
650
|
+
1. `state.mode=controlled` + `expandedRowKeys` (input);
|
|
651
|
+
2. deep-link (se habilitado e válido, a partir do P1);
|
|
652
|
+
3. persistência local (se habilitada);
|
|
653
|
+
4. default vazio.
|
|
654
|
+
|
|
655
|
+
## 7.2 Mutabilidade por modo
|
|
656
|
+
|
|
657
|
+
| Modo | Runtime altera estado interno | Runtime altera input | Emite evento |
|
|
658
|
+
| --- | --- | --- | --- |
|
|
659
|
+
| `controlled` | Não | Não | Sim (quando `emitChanges=true`) |
|
|
660
|
+
| `uncontrolled` | Sim | N/A | Sim (quando `emitChanges=true`) |
|
|
661
|
+
|
|
662
|
+
---
|
|
663
|
+
|
|
664
|
+
## 8. Segurança e Privacidade
|
|
665
|
+
|
|
666
|
+
1. deep-link com IDs é opt-in e deny-by-default (feature entra no P1).
|
|
667
|
+
2. limitar quantidade e tamanho de payload em URL.
|
|
668
|
+
3. canonicalização obrigatória antes de validar (`decodePasses=1`, trim, dedupe por política e ordenação determinística).
|
|
669
|
+
4. parsing estrito e sanitização (sem aceitar chaves inválidas/orfãs).
|
|
670
|
+
5. rejeitar parâmetros duplicados fora da política (`duplicateParams`) com diagnóstico estruturado.
|
|
671
|
+
6. logs/eventos/telemetria não devem expor IDs brutos por default.
|
|
672
|
+
7. persistência deve respeitar política de limpeza por reset/logout/context switch.
|
|
673
|
+
8. persistência deve usar namespace/versionamento para evitar colisão cross-app/tenant/ambiente.
|
|
674
|
+
9. runtime deve aplicar redaction default (`raw` somente em modo explicitamente permitido).
|
|
675
|
+
10. bloquear state injection por URL com validação de formato e integridade (P1).
|
|
676
|
+
11. estratégia de integridade para URL em SPA: **token opaco server-side** (evitar HMAC no cliente, P1).
|
|
677
|
+
12. contrato de exchange deve mapear erros (`expired`, `invalid`, `rate_limited`) para diagnósticos e UX acessível (P1).
|
|
678
|
+
|
|
679
|
+
---
|
|
680
|
+
|
|
681
|
+
## 9. Arquitetura de Runtime
|
|
682
|
+
|
|
683
|
+
## 9.1 Estado interno mínimo
|
|
684
|
+
|
|
685
|
+
1. `expandedRowKeys: Set<string>`
|
|
686
|
+
2. `detailLoadingKeys: Set<string>`
|
|
687
|
+
3. `detailErrorByKey: Map<string, string>`
|
|
688
|
+
4. `detailDataByKey: Map<string, unknown>`
|
|
689
|
+
5. `expansionDiagnostics: Array<DiagnosticEntry>` (estruturado para editor/log).
|
|
690
|
+
6. `interactionPolicyResolver: InteractionPolicyResolver` (enforcement testável de precedência).
|
|
691
|
+
7. `schemaContractValidator: SchemaContractValidator` (shape no P0, AST completo no P1).
|
|
692
|
+
8. `securityDefaults: SecurityDefaults` (redaction/parsing/integrity).
|
|
693
|
+
|
|
694
|
+
## 9.2 Precedência de interação
|
|
695
|
+
|
|
696
|
+
1. trigger de expansão;
|
|
697
|
+
2. teclado conforme `interaction.keyboard.profile` (`disclosure` no P0, `grid` no P1);
|
|
698
|
+
3. row actions/menu;
|
|
699
|
+
4. seleção;
|
|
700
|
+
5. `rowClick`.
|
|
701
|
+
|
|
702
|
+
## 9.3 Pipeline de deep-link (obrigatório no P1)
|
|
703
|
+
|
|
704
|
+
Fluxo canônico:
|
|
705
|
+
|
|
706
|
+
1. parse bruto do query param;
|
|
707
|
+
2. canonicalização determinística (decode único, trim, dedupe por política, ordenação);
|
|
708
|
+
3. validação de formato/tamanho (`keyFormat`, `maxKeys`, `maxLength`);
|
|
709
|
+
4. exchange do token opaco (`endpointId`);
|
|
710
|
+
5. validação do payload retornado;
|
|
711
|
+
6. reconciliação com dataset atual;
|
|
712
|
+
7. aplicação do estado (ou diagnóstico fail-closed).
|
|
713
|
+
|
|
714
|
+
## 9.4 Custos de avaliação DSL
|
|
715
|
+
|
|
716
|
+
`rowExpandableWhen` deve usar cache por `rowKey` com invalidação quando:
|
|
717
|
+
|
|
718
|
+
1. row data muda;
|
|
719
|
+
2. dependências computadas mudam;
|
|
720
|
+
3. contexto de usuário/ambiente muda (quando habilitado pelo host no P1).
|
|
721
|
+
|
|
722
|
+
## 9.5 Evento canônico (auditoria + integração)
|
|
723
|
+
|
|
724
|
+
`rowExpansionChange` (obrigatório) com contrato discriminado por exposição:
|
|
725
|
+
|
|
726
|
+
```ts
|
|
727
|
+
type RowExpansionChangeBase = {
|
|
728
|
+
tableId: string;
|
|
729
|
+
trigger: 'icon' | 'row' | 'keyboard' | 'api' | 'restore';
|
|
730
|
+
reasonCode: 'user' | 'policy' | 'restore' | 'api';
|
|
731
|
+
expanded: boolean;
|
|
732
|
+
persisted: boolean;
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
type RowExpansionChangeEvent =
|
|
736
|
+
| (RowExpansionChangeBase & {
|
|
737
|
+
rowIdExposure: 'redacted';
|
|
738
|
+
rowIdRef: null;
|
|
739
|
+
expandedKeysExposure: 'none';
|
|
740
|
+
previousExpandedKeysRef: null;
|
|
741
|
+
currentExpandedKeysRef: null;
|
|
742
|
+
})
|
|
743
|
+
| (RowExpansionChangeBase & {
|
|
744
|
+
rowIdExposure: 'hashed';
|
|
745
|
+
rowIdHash: string;
|
|
746
|
+
expandedKeysExposure: 'hashed';
|
|
747
|
+
previousExpandedKeysHash: string[] | null;
|
|
748
|
+
currentExpandedKeysHash: string[] | null;
|
|
749
|
+
})
|
|
750
|
+
| (RowExpansionChangeBase & {
|
|
751
|
+
rowIdExposure: 'raw';
|
|
752
|
+
rowId: string;
|
|
753
|
+
expandedKeysExposure: 'raw';
|
|
754
|
+
previousExpandedKeys: string[] | null;
|
|
755
|
+
currentExpandedKeys: string[] | null;
|
|
756
|
+
});
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
---
|
|
760
|
+
|
|
761
|
+
## 10. Virtualização (governança por modo)
|
|
762
|
+
|
|
763
|
+
| Cenário | Status |
|
|
764
|
+
| --- | --- |
|
|
765
|
+
| Não virtualizado + `fixed` | Active |
|
|
766
|
+
| Não virtualizado + `dynamic` | Active |
|
|
767
|
+
| Virtualizado + `fixed` | Active |
|
|
768
|
+
| Virtualizado + `dynamic` | Partial/Blocked por política no P0 |
|
|
769
|
+
|
|
770
|
+
Regra de rollout: manter `dynamic` em virtualizado somente após P2 e sob feature flag.
|
|
771
|
+
|
|
772
|
+
Semântica obrigatória de viewport:
|
|
773
|
+
|
|
774
|
+
1. `collapseOn.viewportDataWindowChange` representa mudança estrutural da janela de dados (ex.: troca de dataset/página virtual), não eventos de scroll pixel-a-pixel;
|
|
775
|
+
2. default recomendado: `false` no preset enterprise para evitar colapso inesperado durante navegação.
|
|
776
|
+
|
|
777
|
+
Nota de complexidade (crítica):
|
|
778
|
+
|
|
779
|
+
1. existem dois caminhos de render independentes (`mat-table` e virtualizado);
|
|
780
|
+
2. P0A cobre somente não virtualizado;
|
|
781
|
+
3. P0B cobre virtualizado fixo, sem detail row de altura variável.
|
|
782
|
+
|
|
783
|
+
---
|
|
784
|
+
|
|
785
|
+
## 11. UX + A11y (WCAG 2.2 AA)
|
|
786
|
+
|
|
787
|
+
1. trigger com `aria-expanded`, `aria-controls`, `aria-label`;
|
|
788
|
+
2. teclado por perfil: `disclosure` (Enter/Space) e `grid` (setas + roving tabindex, P1);
|
|
789
|
+
3. foco retorna ao trigger após colapso;
|
|
790
|
+
4. mensagens `aria-live` para expand/collapse/bloqueio/erro;
|
|
791
|
+
5. lazy detail anuncia estado assíncrono com `aria-busy` e estado carregado/erro;
|
|
792
|
+
6. `prefers-reduced-motion` respeitado;
|
|
793
|
+
7. atender WCAG 2.2 AA para `Focus Not Obscured (Minimum)` e `Target Size (Minimum)` no trigger.
|
|
794
|
+
|
|
795
|
+
Chaves i18n mínimas:
|
|
796
|
+
|
|
797
|
+
1. `table.expansion.toggle.expand.ariaLabel`
|
|
798
|
+
2. `table.expansion.toggle.collapse.ariaLabel`
|
|
799
|
+
3. `table.expansion.status.expanded`
|
|
800
|
+
4. `table.expansion.status.collapsed`
|
|
801
|
+
5. `table.expansion.status.blockedPolicy`
|
|
802
|
+
6. `table.expansion.status.lazyLoadError`
|
|
803
|
+
7. `table.expansion.status.loading`
|
|
804
|
+
8. `table.expansion.status.loaded`
|
|
805
|
+
|
|
806
|
+
---
|
|
807
|
+
|
|
808
|
+
## 12. Editor + Governança JSON-Driven
|
|
809
|
+
|
|
810
|
+
## 12.1 Behavior editor
|
|
811
|
+
|
|
812
|
+
Adicionar seção `Expansion` cobrindo:
|
|
813
|
+
|
|
814
|
+
1. `enabled`, `state.mode`, `state.emitChanges`;
|
|
815
|
+
2. `identity.*` (dependência de `table.idField`);
|
|
816
|
+
3. `interaction.*` incluindo `keyboard.profile`;
|
|
817
|
+
4. `limits.*`;
|
|
818
|
+
5. `collapseOn.*` (com semântica explícita de `viewportDataWindowChange`);
|
|
819
|
+
6. `rowExpandableWhen.*`;
|
|
820
|
+
7. `detail.schemaContract/detail.source/detail.rendering/height/lazyLoad`;
|
|
821
|
+
8. `virtualization.policy`;
|
|
822
|
+
9. `persistence.*` incluindo `storageKeyStrategy.*`;
|
|
823
|
+
10. `deepLink.*` (incluindo privacy/limites/parsing).
|
|
824
|
+
|
|
825
|
+
## 12.2 JSON editor
|
|
826
|
+
|
|
827
|
+
1. P0: validação estrutural mínima + coerções documentadas;
|
|
828
|
+
2. P1: validação síncrona completa com `SchemaContractValidator` compartilhado (runtime/editor);
|
|
829
|
+
3. P1: diagnósticos estruturados (codes/severity/hints) e round-trip sem perda.
|
|
830
|
+
|
|
831
|
+
## 12.3 Presets corporativos recomendados
|
|
832
|
+
|
|
833
|
+
Preset `enterprise-safe` (recomendado):
|
|
834
|
+
|
|
835
|
+
1. `collapseOn.dataRefresh=true`;
|
|
836
|
+
2. `collapseOn.columnVisibilityChange=true`;
|
|
837
|
+
3. `collapseOn.columnOrderChange=true`;
|
|
838
|
+
4. `collapseOn.columnResizeChange=true`;
|
|
839
|
+
5. `collapseOn.densityChange=true`;
|
|
840
|
+
6. `collapseOn.viewportDataWindowChange=false`.
|
|
841
|
+
|
|
842
|
+
Pode ser relaxado por caso de uso, mas o default recomendado para produção é fail-safe.
|
|
843
|
+
|
|
844
|
+
## 12.4 AI metadata
|
|
845
|
+
|
|
846
|
+
Atualizar `table-ai-capabilities` e context pack com `behavior.expansion.*` para evitar recomendações inválidas.
|
|
847
|
+
Adicionar categorias de segurança/privacidade (`deepLink.keyFormat`, `deepLink.integrity`, `event exposure mode`).
|
|
848
|
+
Execução recomendada: entregar já em P0A (baixo risco técnico e alto ganho de governança).
|
|
849
|
+
|
|
850
|
+
---
|
|
851
|
+
|
|
852
|
+
## 13. Observabilidade Mínima (obrigatória já em P0/P1)
|
|
853
|
+
|
|
854
|
+
## P0
|
|
855
|
+
|
|
856
|
+
1. contadores: expand, collapse, blockedPolicy;
|
|
857
|
+
2. erros: lazyLoadError, parseError, validationError;
|
|
858
|
+
3. timing básico: tempo de lazy load.
|
|
859
|
+
4. contadores de segurança: `deepLinkRejected`, `urlInjectionBlocked` (quando deep-link habilitado no P1), `redactionApplied`.
|
|
860
|
+
5. adapter/sink explícito (evitar telemetria ad-hoc):
|
|
861
|
+
- `TableMetricsAdapter` (DI token);
|
|
862
|
+
- implementação default `NoopTableMetricsAdapter`;
|
|
863
|
+
- contrato mínimo: `count(name, tags)`, `timing(name, ms, tags)`, `error(name, tags)`.
|
|
864
|
+
6. padrão de nomes/tags:
|
|
865
|
+
- metric names: `praxis.table.expansion.*`
|
|
866
|
+
- tags permitidas: `tableId`, `reasonCode`, `result`
|
|
867
|
+
- proibição: `rowId` e `expandedKeys` em tags (evitar cardinalidade e vazamento).
|
|
868
|
+
|
|
869
|
+
## P1
|
|
870
|
+
|
|
871
|
+
1. reason codes agregados;
|
|
872
|
+
2. taxa de uso por `detail.source.mode` (`inline/resource/resourcePath`) e mix de nós renderizados;
|
|
873
|
+
3. taxa de restauração via storage/deep-link.
|
|
874
|
+
|
|
875
|
+
## P2
|
|
876
|
+
|
|
877
|
+
1. métricas avançadas de performance (scroll jitter/frame budget) e auto-height experimental.
|
|
878
|
+
|
|
879
|
+
---
|
|
880
|
+
|
|
881
|
+
## 14. Plano por Fases
|
|
882
|
+
|
|
883
|
+
## P0A (fundação executável)
|
|
884
|
+
|
|
885
|
+
1. contrato `behavior.expansion` em `@praxisui/core` (model + defaults + coercions base);
|
|
886
|
+
2. precondição de identidade (`identity.rowKeySource=table.idField`) com guard em editor/runtime;
|
|
887
|
+
3. runtime não virtualizado (expand/collapse);
|
|
888
|
+
4. evento canônico discriminado por política de exposição;
|
|
889
|
+
5. precedência de interação com `keyboard.profile=disclosure`;
|
|
890
|
+
6. A11y base (`aria-expanded`, `aria-live`, foco de retorno);
|
|
891
|
+
7. testes de estado/precedência;
|
|
892
|
+
8. atualização inicial de docs e AI metadata.
|
|
893
|
+
|
|
894
|
+
## P0B (paridade operacional)
|
|
895
|
+
|
|
896
|
+
1. runtime virtualizado em política `fixed-height-only` (sem detail dinâmico);
|
|
897
|
+
2. `collapseOn` essenciais para mutações estruturais (incluindo `viewportDataWindowChange`, sem colapso por scroll);
|
|
898
|
+
3. persistência + reset com `scope/clearOn` + `storageKeyStrategy`;
|
|
899
|
+
4. observabilidade mínima com `TableMetricsAdapter`;
|
|
900
|
+
5. testes de regressão em ambos os caminhos de render (`mat-table` e virtualizado).
|
|
901
|
+
|
|
902
|
+
## P1 (expansão avançada governada)
|
|
903
|
+
|
|
904
|
+
1. detail schema completo com subset seguro + `SchemaContractValidator` compartilhado (runtime/editor);
|
|
905
|
+
2. lazy load completo (retry/cache/cancel/dedupe/timeout);
|
|
906
|
+
3. deep-link seguro com privacy mode + anti-injection URL + canonicalização determinística;
|
|
907
|
+
4. perfil de teclado `grid` com semântica APG e suíte dedicada;
|
|
908
|
+
5. editor completo com diagnósticos estruturados;
|
|
909
|
+
6. contexto DSL opcional `user/env` via contrato host-provided;
|
|
910
|
+
7. testes de integração cruzada.
|
|
911
|
+
|
|
912
|
+
## P2
|
|
913
|
+
|
|
914
|
+
1. altura dinâmica em virtualização sob flag;
|
|
915
|
+
2. telemetria/performance avançada;
|
|
916
|
+
3. harness/e2e corporativo.
|
|
917
|
+
|
|
918
|
+
## 14.1 Migração de Configs Legadas (V4 -> V5)
|
|
919
|
+
|
|
920
|
+
Objetivo: evitar drift de comportamento no rollout e garantir coerção determinística.
|
|
921
|
+
|
|
922
|
+
1. `behavior.expansion` ausente -> inserir defaults V5 (`enabled=false`) sem ativar feature implicitamente.
|
|
923
|
+
2. `rowExpandableWhen.context` com `user/env` antes do P1 -> coerção para `["row","computed"]` + diagnóstico `EXPANSION_CONTEXT_DEFERRED_P1`.
|
|
924
|
+
3. `deepLink.enabled=true` antes do P1 -> feature desabilitada em runtime + diagnóstico `EXPANSION_DEEPLINK_DEFERRED_P1`.
|
|
925
|
+
4. `detail.schemaContract.kind/version` ausente ou inválido -> fail-closed + `enabled=false` para expansão de detalhe.
|
|
926
|
+
5. `collapseOn` parcial -> preencher flags ausentes com preset `enterprise-safe`.
|
|
927
|
+
6. `security.allowRawExposure` ausente -> default `false`.
|
|
928
|
+
7. `persistence.scope` ausente -> default mínimo `["tableId"]` e recomendação de upgrade para `["tableId","user","tenant"]`.
|
|
929
|
+
8. legado `collapseOn.viewportChange` -> migrar para `collapseOn.viewportDataWindowChange=false` + diagnóstico `EXPANSION_VIEWPORT_POLICY_RENAMED`.
|
|
930
|
+
9. `interaction.keyboard.profile` ausente -> default `disclosure`; `arrowLeftRight=true` legado vira `false` no P0.
|
|
931
|
+
10. `persistence.storageKeyStrategy` ausente -> default `{ namespace: "praxis.table.expansion", version: "v1", hashScope: true }`.
|
|
932
|
+
|
|
933
|
+
## 14.2 Owners e Dependências Externas (Host-Provided)
|
|
934
|
+
|
|
935
|
+
| Item | Fase | Owner primário | Dependência externa | Critério de pronto |
|
|
936
|
+
| --- | --- | --- | --- | --- |
|
|
937
|
+
| Contexto DSL `user/env` | P1 | App Host (frontend) | Provider de contexto assinado em contrato | Contexto injetado e testado em runtime/editor |
|
|
938
|
+
| Deep-link `opaqueToken.exchange` | P1 | Backend/API + App Host | Endpoint `resolveExpandedKeysToken` com TTL/erros | Contrato request/response/errors implementado e testado |
|
|
939
|
+
| `persistence.scope` completo (`user/tenant`) | P0B/P1 | App Host (auth/session) | Fonte confiável de `userId` e `tenantId` | Escopo aplicado sem vazamento cross-tenant |
|
|
940
|
+
| Sink de métricas (`TableMetricsAdapter`) | P0B | Plataforma/Observability | Adapter para stack corporativa (ex.: OTEL/DataDog) | Métricas mínimas coletadas sem PII |
|
|
941
|
+
|
|
942
|
+
## 14.3 Checklist de Implementação por Camada (AS-IS -> TO-BE)
|
|
943
|
+
|
|
944
|
+
1. `@praxisui/core`:
|
|
945
|
+
- introduzir `behavior.expansion` no modelo tipado (`TableBehaviorConfig`) e em `createDefaultTableConfig()`;
|
|
946
|
+
- garantir coerções e validações mínimas de P0 (`identity`, `keyboard.profile`, `virtualization.policy`).
|
|
947
|
+
2. `praxis-table` runtime:
|
|
948
|
+
- implementar expansão em `mat-table` primeiro, sem virtualização dinâmica;
|
|
949
|
+
- integrar estado expandido ao ciclo de `DataMode` e às transições `remote/local/empty`;
|
|
950
|
+
- aplicar evento canônico discriminado por exposição e métricas `praxis.table.expansion.*`.
|
|
951
|
+
3. `praxis-table` virtualização:
|
|
952
|
+
- habilitar apenas `fixed-height-only` em P0B;
|
|
953
|
+
- bloquear expansão quando `detail.height.mode=dynamic` com virtualização ativa;
|
|
954
|
+
- validar ausência de colapso em scroll normal (`viewportDataWindowChange=false`).
|
|
955
|
+
4. `praxis-table` editor:
|
|
956
|
+
- adicionar seção `Expansion` com guards de pré-condição (`table.idField`, policies, deep-link parsing);
|
|
957
|
+
- reusar `SchemaContractValidator` no editor para paridade runtime.
|
|
958
|
+
5. Docs e JSON API:
|
|
959
|
+
- atualizar documentação da tabela dinâmica com `behavior.expansion.*` e exemplos remotos/locais;
|
|
960
|
+
- resolver referência de conceito schema-driven no workspace (evitar link quebrado).
|
|
961
|
+
6. AI metadata/context pack:
|
|
962
|
+
- incluir novos paths e bloqueios de segurança no `table-ai-capabilities`/context pack;
|
|
963
|
+
- adicionar hints de recomendação para `viewportDataWindowChange` e exposição de evento.
|
|
964
|
+
|
|
965
|
+
---
|
|
966
|
+
|
|
967
|
+
## 15. Testes Corporativos
|
|
968
|
+
|
|
969
|
+
## 15.1 Contrato/estado
|
|
970
|
+
|
|
971
|
+
1. testes de `controlled` (runtime não muta input);
|
|
972
|
+
2. testes de `uncontrolled`;
|
|
973
|
+
3. precedência input > deep-link > storage (deep-link a partir do P1);
|
|
974
|
+
4. `enabled=true` sem `table.idField` estável -> fail-closed com diagnóstico.
|
|
975
|
+
|
|
976
|
+
## 15.2 Segurança
|
|
977
|
+
|
|
978
|
+
1. deep-link inválido/overflow -> fail-closed (P1);
|
|
979
|
+
2. chaves inexistentes/orfãs -> ignorar com diagnóstico;
|
|
980
|
+
3. privacidade deny-by-default -> sem espelhamento em URL (P1).
|
|
981
|
+
4. redaction default -> sem IDs `raw` em logs/eventos.
|
|
982
|
+
5. token opaco inválido/expirado -> estado URL rejeitado (P1).
|
|
983
|
+
6. parâmetros duplicados/múltiplos encodings -> canonicalização + rejeição conforme política (P1).
|
|
984
|
+
|
|
985
|
+
## 15.3 Contrato de schema
|
|
986
|
+
|
|
987
|
+
1. `schemaContract.kind` inválido -> fail-closed;
|
|
988
|
+
2. versão incompatível -> fail-closed + hint;
|
|
989
|
+
3. node não permitido em `allowedNodes` -> fail-closed;
|
|
990
|
+
4. sanitização estrita aplicada e testada.
|
|
991
|
+
|
|
992
|
+
## 15.4 Runtime e integração
|
|
993
|
+
|
|
994
|
+
1. expand/collapse e limites;
|
|
995
|
+
2. `collapseOn.*` completo;
|
|
996
|
+
3. não quebrar sorting/filter/pagination/selection/rowAction/DnD;
|
|
997
|
+
4. ambos caminhos de render válidos (`mat-table` e virtualizado fixo);
|
|
998
|
+
5. virtualização não colapsa em scroll normal quando `viewportDataWindowChange=false`.
|
|
999
|
+
|
|
1000
|
+
## 15.5 A11y e performance
|
|
1001
|
+
|
|
1002
|
+
1. teclado/foco/aria-live/aria-busy;
|
|
1003
|
+
2. WCAG 2.2 AA: `Focus Not Obscured (Minimum)` e `Target Size (Minimum)` no trigger;
|
|
1004
|
+
3. cenários de alto volume com virtualização;
|
|
1005
|
+
4. budget de scroll sem regressão perceptível.
|
|
1006
|
+
|
|
1007
|
+
---
|
|
1008
|
+
|
|
1009
|
+
## 16. Checklist de Go-Live
|
|
1010
|
+
|
|
1011
|
+
1. `behavior.expansion` alinhado em `@praxisui/core` + `praxis-table` (model/defaults/editor/json-api/AI metadata).
|
|
1012
|
+
2. estado controlado/uncontrolled formalmente testado.
|
|
1013
|
+
3. política de virtualização aplicada e documentada (`fixed-height-only` no P0).
|
|
1014
|
+
4. persistência com `scope/clearOn` ativa e testada.
|
|
1015
|
+
5. diagnósticos estruturados disponíveis para editor e logs.
|
|
1016
|
+
6. observabilidade mínima ativa com adapter/sink explícito (não postergada para P2).
|
|
1017
|
+
7. `InteractionPolicyResolver` com suíte de regressão.
|
|
1018
|
+
8. **Bloqueador P0**: redaction default ativo e testado (sem emissão `raw` por padrão).
|
|
1019
|
+
9. **Bloqueador P1**: anti URL state-injection ativo e testado quando `deepLink.enabled=true`.
|
|
1020
|
+
10. **Bloqueador P1**: `SchemaContractValidator` completo (fail-closed + migration hints) no runtime e editor para `detail.source` + AST de composição.
|
|
1021
|
+
11. **Bloqueador P0**: contrato discriminado de evento impede payload sensível fora de política.
|
|
1022
|
+
12. **Bloqueador P0B**: ausência de colapso por scroll normal no modo virtualizado validada por teste.
|
|
1023
|
+
13. suíte de testes unit/integration/a11y/perf verde.
|
|
1024
|
+
14. teste automatizado garantindo: **snippet canônico da doc == defaults reais do runtime**.
|
|
1025
|
+
15. migração V4 -> V5 executada com relatório de diagnósticos e taxa de coerção.
|
|
1026
|
+
16. owners/dependências externas de P1 formalmente atribuídos (host + backend).
|
|
1027
|
+
|
|
1028
|
+
---
|
|
1029
|
+
|
|
1030
|
+
## 17. Prompt Final para ChatGPT
|
|
1031
|
+
|
|
1032
|
+
```text
|
|
1033
|
+
Atue como Arquiteto Front-end Enterprise (Angular/React), revisor UX/UI senior (Nielsen), especialista WCAG 2.2 AA e reviewer de contratos JSON-driven.
|
|
1034
|
+
|
|
1035
|
+
Faça uma revisao CRITICA do plano de expandable rows abaixo.
|
|
1036
|
+
|
|
1037
|
+
Formato obrigatorio:
|
|
1038
|
+
1) Veredito executivo (Go / Go com ressalvas / No-Go)
|
|
1039
|
+
2) Matriz comparativa (Praxis vs AG Grid vs MUI vs TanStack vs Kendo)
|
|
1040
|
+
3) Findings priorizados (High/Medium/Low)
|
|
1041
|
+
4) Gaps de contrato JSON
|
|
1042
|
+
5) Viabilidade JSON-driven + editor
|
|
1043
|
+
6) Ajustes no contrato (antes/depois)
|
|
1044
|
+
7) Ajustes de arquitetura/runtime (P0/P1/P2)
|
|
1045
|
+
8) Plano de testes corporativo
|
|
1046
|
+
9) Melhorias de documentação/API
|
|
1047
|
+
10) Checklist final de aceite de produção
|
|
1048
|
+
|
|
1049
|
+
Critérios:
|
|
1050
|
+
- Não aceite suposições frágeis.
|
|
1051
|
+
- Aponte inconsistências entre planejado e executável.
|
|
1052
|
+
- Traga recomendações acionáveis com trade-offs.
|
|
1053
|
+
- Use referências oficiais e inclua links.
|
|
1054
|
+
|
|
1055
|
+
Plano para revisão:
|
|
1056
|
+
[COLE AQUI O CONTEÚDO DE projects/praxis-table/docs/expandable-rows-enterprise-big-leagues-plan.md]
|
|
1057
|
+
```
|
|
1058
|
+
|
|
1059
|
+
---
|
|
1060
|
+
|
|
1061
|
+
## 18. Referências Oficiais
|
|
1062
|
+
|
|
1063
|
+
1. AG Grid Master/Detail: https://www.ag-grid.com/angular-data-grid/master-detail/
|
|
1064
|
+
2. AG Grid Detail Grids: https://www.ag-grid.com/javascript-data-grid/master-detail-grids/
|
|
1065
|
+
3. AG Grid Row IDs: https://www.ag-grid.com/javascript-data-grid/row-ids/
|
|
1066
|
+
4. MUI Master-detail: https://mui.com/x/react-data-grid/master-detail/
|
|
1067
|
+
5. MUI DataGridPro API: https://mui.com/x/api/data-grid/data-grid-pro/
|
|
1068
|
+
6. TanStack Expanding Guide: https://tanstack.com/table/latest/docs/guide/expanding
|
|
1069
|
+
7. Kendo Angular Master-Detail: https://www.telerik.com/kendo-angular-ui/components/grid/master-detail
|
|
1070
|
+
8. Kendo React Detail Rows: https://www.telerik.com/kendo-react-ui/components/grid/rows/detail
|
|
1071
|
+
9. Angular Dynamic Forms (metadata-driven): https://angular.dev/guide/forms/dynamic-forms
|
|
1072
|
+
10. Angular `NgComponentOutlet` (dynamic component rendering): https://angular.dev/api/common/NgComponentOutlet
|
|
1073
|
+
11. Angular Security Best Practices (sanitization/trust boundaries): https://angular.dev/best-practices/security
|
|
1074
|
+
12. Angular Router - Read Route State (query params): https://angular.dev/guide/routing/read-route-state
|
|
1075
|
+
13. Angular CDK Scrolling (fixed-size strategy e limites de auto-size): https://raw.githubusercontent.com/angular/components/main/src/cdk/scrolling/scrolling.md
|
|
1076
|
+
14. JSON Schema Object Constraints (`additionalProperties`): https://json-schema.org/understanding-json-schema/reference/object?highlight=additionalproperties
|
|
1077
|
+
15. WAI-ARIA APG Disclosure Pattern: https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/
|
|
1078
|
+
16. WAI-ARIA APG Grid Pattern: https://www.w3.org/WAI/ARIA/apg/patterns/grid/
|
|
1079
|
+
17. WCAG 2.2 Quick Reference: https://www.w3.org/WAI/WCAG22/quickref/
|
|
1080
|
+
18. URLSearchParams duplicate handling (`getAll`): https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/getAll
|