division-ds 0.3.0 → 0.3.1

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.
Files changed (78) hide show
  1. package/AGENTS.md +1 -1
  2. package/package.json +1 -1
  3. package/public/docs/components/accordion.md +1 -1
  4. package/public/docs/components/ai-label.md +1 -1
  5. package/public/docs/components/alert-dialog.md +1 -1
  6. package/public/docs/components/alert.md +1 -1
  7. package/public/docs/components/approval-request.md +1 -1
  8. package/public/docs/components/auth-form.md +1 -1
  9. package/public/docs/components/avatar-group.md +1 -1
  10. package/public/docs/components/avatar.md +1 -1
  11. package/public/docs/components/badge.md +1 -1
  12. package/public/docs/components/breadcrumbs.md +1 -1
  13. package/public/docs/components/button.md +1 -1
  14. package/public/docs/components/card.md +1 -1
  15. package/public/docs/components/carousel.md +1 -1
  16. package/public/docs/components/cart.md +1 -1
  17. package/public/docs/components/chart-panel.md +1 -1
  18. package/public/docs/components/chat-message.md +1 -1
  19. package/public/docs/components/chat-thread.md +1 -1
  20. package/public/docs/components/checkbox.md +1 -1
  21. package/public/docs/components/checkout-form.md +1 -1
  22. package/public/docs/components/command-palette.md +1 -1
  23. package/public/docs/components/data-table.md +1 -1
  24. package/public/docs/components/date-picker.md +3 -3
  25. package/public/docs/components/date-presets.md +1 -1
  26. package/public/docs/components/date-range-picker.md +3 -3
  27. package/public/docs/components/description-list.md +1 -1
  28. package/public/docs/components/dialog.md +1 -1
  29. package/public/docs/components/divider.md +1 -1
  30. package/public/docs/components/dropdown-menu.md +1 -1
  31. package/public/docs/components/empty-state.md +1 -1
  32. package/public/docs/components/footer.md +1 -1
  33. package/public/docs/components/form-field.md +2 -2
  34. package/public/docs/components/icon-button.md +1 -1
  35. package/public/docs/components/input.md +1 -1
  36. package/public/docs/components/kanban-board.md +1 -1
  37. package/public/docs/components/kbd.md +1 -1
  38. package/public/docs/components/link.md +1 -1
  39. package/public/docs/components/list-item.md +1 -1
  40. package/public/docs/components/loading-animation.md +1 -1
  41. package/public/docs/components/multi-select.md +5 -5
  42. package/public/docs/components/navbar.md +1 -1
  43. package/public/docs/components/newsletter-form.md +1 -1
  44. package/public/docs/components/page-header.md +1 -1
  45. package/public/docs/components/pagination.md +1 -1
  46. package/public/docs/components/product-card.md +1 -1
  47. package/public/docs/components/progress.md +1 -1
  48. package/public/docs/components/prompt-input.md +1 -1
  49. package/public/docs/components/quantity-stepper.md +2 -2
  50. package/public/docs/components/radio.md +1 -1
  51. package/public/docs/components/rating.md +1 -1
  52. package/public/docs/components/score-ring.md +1 -1
  53. package/public/docs/components/search.md +1 -1
  54. package/public/docs/components/section-heading.md +1 -1
  55. package/public/docs/components/segmented-control.md +1 -1
  56. package/public/docs/components/select.md +2 -2
  57. package/public/docs/components/sheet.md +1 -1
  58. package/public/docs/components/sidebar.md +1 -1
  59. package/public/docs/components/skeleton.md +1 -1
  60. package/public/docs/components/slider.md +1 -1
  61. package/public/docs/components/sparkline.md +1 -1
  62. package/public/docs/components/spinner.md +1 -1
  63. package/public/docs/components/stat.md +1 -1
  64. package/public/docs/components/status-tracker.md +1 -1
  65. package/public/docs/components/stepper.md +1 -1
  66. package/public/docs/components/switch.md +1 -1
  67. package/public/docs/components/tabs.md +1 -1
  68. package/public/docs/components/tag.md +1 -1
  69. package/public/docs/components/textarea.md +1 -1
  70. package/public/docs/components/time-field.md +5 -5
  71. package/public/docs/components/timeline.md +1 -1
  72. package/public/docs/components/toast.md +1 -1
  73. package/public/docs/components/tool-call.md +1 -1
  74. package/public/docs/components/tooltip.md +1 -1
  75. package/public/manifest/components.json +171 -80
  76. package/scripts/lib/agent-docs.mjs +1 -1
  77. package/scripts/lib/props-validator.mjs +36 -2
  78. package/division-ds-0.3.0.tgz +0 -0
@@ -53,7 +53,7 @@ export function searchIndex(entries, tokens) {
53
53
  }
54
54
 
55
55
  /** Said under every Import block: the paths are the repository's, not an installed project's. */
56
- export const INSTALLED_PATHS = 'Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file.'
56
+ export const INSTALLED_PATHS = 'Paths are from the Division DS repository root. Installed with shadcn they start `src/division-ds/` instead of `src/` (`src/division-ds/components/...`); import them relative to your file. An install has no index barrels: import each component from its own file, never from `components/molecules`.'
57
57
 
58
58
  /** One component as a markdown page. `entry` is a manifest entry (see `manifestEntry`). */
59
59
  export function componentMarkdown(entry) {
@@ -156,7 +156,14 @@ export function buildIndex(manifest) {
156
156
  const single = exportNames.length === 1
157
157
  for (const name of exportNames) {
158
158
  const spec = specs[name]
159
- spec.checkUnknown = single && !unresolved && spec.rest?.open !== 'any'
159
+ // On a page that documents several components, the rows with no part tag are the main
160
+ // component's own. The main one is checked too when nothing passes through undocumented:
161
+ // its type is closed (manifest `closed`), or its only pass-through is native attributes,
162
+ // which the check already allows. Its parts stay unchecked: their rows list only what
163
+ // differs from the main component.
164
+ const main = name === entry.name && (entry.closed === true || spec.rest?.open === 'native')
165
+ spec.checkUnknown = (single || main) && !unresolved && spec.rest?.open !== 'any'
166
+ spec.namedByFormField = (entry.namedByFormField ?? []).includes(name)
160
167
  index.set(name, spec)
161
168
  }
162
169
  }
@@ -228,7 +235,7 @@ export function scriptKind(path) {
228
235
 
229
236
  /**
230
237
  * Every props violation in one file: `{ path, line, column, rule, component, prop, found, fix }`.
231
- * `rule` is 'unknown-prop' or 'invalid-value'. `index` comes from `buildIndex`.
238
+ * `rule` is 'unknown-prop', 'invalid-value' or 'missing-label' (a control FormField names, with no label and no FormField around it). `index` comes from `buildIndex`.
232
239
  */
233
240
  export function findPropViolations(text, path, index) {
234
241
  const source = ts.createSourceFile(path, text, ts.ScriptTarget.Latest, true, scriptKind(path))
@@ -254,11 +261,38 @@ export function findPropViolations(text, path, index) {
254
261
  return undefined
255
262
  }
256
263
 
264
+ // Inside a Division FormField, which names the control it holds.
265
+ const insideFormField = element => {
266
+ for (let node = element.parent; node; node = node.parent) {
267
+ const opening = ts.isJsxElement(node) ? node.openingElement : ts.isJsxSelfClosingElement(node) ? node : null
268
+ if (opening && opening !== element && resolve(opening.tagName) === 'FormField') return true
269
+ }
270
+ return false
271
+ }
272
+
257
273
  const check = element => {
258
274
  const exportName = resolve(element.tagName)
259
275
  const spec = exportName && index.get(exportName)
260
276
  if (!spec) return
261
277
  const tag = element.tagName.getText(source)
278
+ const attributes = element.attributes.properties
279
+ if (
280
+ spec.namedByFormField &&
281
+ !attributes.some(a => ts.isJsxSpreadAttribute(a) || a.name.getText(source) === 'label') &&
282
+ !insideFormField(element)
283
+ ) {
284
+ const at = source.getLineAndCharacterOfPosition(element.getStart(source))
285
+ violations.push({
286
+ path,
287
+ line: at.line + 1,
288
+ column: at.character + 1,
289
+ rule: 'missing-label',
290
+ component: tag,
291
+ prop: 'label',
292
+ found: `<${tag}>`,
293
+ fix: `${tag} has no name: give it a label, or put it in a FormField, which names it. See ${spec.docs}.`,
294
+ })
295
+ }
262
296
  const known = [...spec.props.keys()]
263
297
  for (const attribute of element.attributes.properties) {
264
298
  if (!ts.isJsxAttribute(attribute)) continue
Binary file