1use crate::groups::ComponentGroup;
11
12pub struct PropDef {
17 pub name: &'static str,
19 pub type_annotation: &'static str,
22 pub optional: bool,
24}
25
26pub struct ComponentDef {
31 pub name: &'static str,
33 pub props: &'static [PropDef],
35 pub description: &'static str,
37}
38
39pub struct Library {
43 pub root: &'static str,
45 pub components: Vec<ComponentDef>,
47 pub groups: Vec<ComponentGroup>,
49}
50
51#[allow(clippy::vec_init_then_push)]
55pub fn all_components() -> Vec<ComponentDef> {
56 let mut components = Vec::new();
57
58 components.push(ComponentDef {
65 name: "Stack",
66 props: &[
67 PropDef { name: "children", type_annotation: "any[]", optional: false },
68 PropDef { name: "direction", type_annotation: "\"row\" | \"column\"", optional: true },
69 PropDef { name: "gap", type_annotation: "\"none\" | \"xs\" | \"s\" | \"m\" | \"l\" | \"xl\" | \"2xl\"", optional: true },
70 PropDef { name: "align", type_annotation: "\"start\" | \"center\" | \"end\" | \"stretch\" | \"baseline\"", optional: true },
71 PropDef { name: "justify", type_annotation: "\"start\" | \"center\" | \"end\" | \"between\" | \"around\" | \"evenly\"", optional: true },
72 PropDef { name: "wrap", type_annotation: "boolean", optional: true },
73 ],
74 description: "Flex container. direction: \"row\"|\"column\" (default \"column\"). gap: \"none\"|\"xs\"|\"s\"|\"m\"|\"l\"|\"xl\"|\"2xl\" (default \"m\"). align: \"start\"|\"center\"|\"end\"|\"stretch\"|\"baseline\". justify: \"start\"|\"center\"|\"end\"|\"between\"|\"around\"|\"evenly\".",
75 });
76
77 components.push(ComponentDef {
79 name: "Tabs",
80 props: &[PropDef {
81 name: "tabs",
82 type_annotation: "TabItem[]",
83 optional: false,
84 }],
85 description: "Tabbed container with switchable panels",
86 });
87
88 components.push(ComponentDef {
90 name: "TabItem",
91 props: &[
92 PropDef {
93 name: "label",
94 type_annotation: "string",
95 optional: false,
96 },
97 PropDef {
98 name: "children",
99 type_annotation: "any[]",
100 optional: false,
101 },
102 ],
103 description: "One tab with label and content",
104 });
105
106 components.push(ComponentDef {
108 name: "Accordion",
109 props: &[PropDef {
110 name: "items",
111 type_annotation: "AccordionItem[]",
112 optional: false,
113 }],
114 description: "Collapsible sections",
115 });
116
117 components.push(ComponentDef {
118 name: "AccordionItem",
119 props: &[
120 PropDef {
121 name: "title",
122 type_annotation: "string",
123 optional: false,
124 },
125 PropDef {
126 name: "children",
127 type_annotation: "any[]",
128 optional: false,
129 },
130 ],
131 description: "One accordion section with title and content",
132 });
133
134 components.push(ComponentDef {
136 name: "Steps",
137 props: &[PropDef {
138 name: "steps",
139 type_annotation: "StepsItem[]",
140 optional: false,
141 }],
142 description: "Step-by-step progress indicator with content",
143 });
144
145 components.push(ComponentDef {
146 name: "StepsItem",
147 props: &[
148 PropDef {
149 name: "title",
150 type_annotation: "string",
151 optional: false,
152 },
153 PropDef {
154 name: "children",
155 type_annotation: "any[]",
156 optional: false,
157 },
158 ],
159 description: "One step with title and content",
160 });
161
162 components.push(ComponentDef {
164 name: "Carousel",
165 props: &[PropDef {
166 name: "items",
167 type_annotation: "Card[]",
168 optional: false,
169 }],
170 description: "Horizontally scrollable card carousel",
171 });
172
173 components.push(ComponentDef {
175 name: "Separator",
176 props: &[],
177 description: "Visual divider line",
178 });
179
180 components.push(ComponentDef {
187 name: "Card",
188 props: &[
189 PropDef { name: "children", type_annotation: "any[]", optional: false },
190 PropDef { name: "variant", type_annotation: "\"card\" | \"sunk\" | \"clear\"", optional: true },
191 PropDef { name: "direction", type_annotation: "\"row\" | \"column\"", optional: true },
192 PropDef { name: "gap", type_annotation: "\"none\" | \"xs\" | \"s\" | \"m\" | \"l\" | \"xl\" | \"2xl\"", optional: true },
193 PropDef { name: "align", type_annotation: "\"start\" | \"center\" | \"end\" | \"stretch\" | \"baseline\"", optional: true },
194 PropDef { name: "justify", type_annotation: "\"start\" | \"center\" | \"end\" | \"between\" | \"around\" | \"evenly\"", optional: true },
195 PropDef { name: "wrap", type_annotation: "boolean", optional: true },
196 ],
197 description: "Styled container. variant: card (elevated) | sunk (recessed) | clear (transparent). Always full width. Accepts all Stack flex params (default: direction column). Cards flex to share space in row/wrap layouts.",
198 });
199
200 components.push(ComponentDef {
202 name: "CardHeader",
203 props: &[
204 PropDef {
205 name: "title",
206 type_annotation: "string",
207 optional: false,
208 },
209 PropDef {
210 name: "subtitle",
211 type_annotation: "string",
212 optional: true,
213 },
214 ],
215 description: "Card title and optional subtitle",
216 });
217
218 components.push(ComponentDef {
220 name: "TextContent",
221 props: &[
222 PropDef { name: "text", type_annotation: "string", optional: false },
223 PropDef { name: "size", type_annotation: "\"small\" | \"default\" | \"large\" | \"small-heavy\" | \"large-heavy\"", optional: true },
224 ],
225 description: "Text block. Supports markdown. Optional size: \"small\" | \"default\" | \"large\" | \"small-heavy\" | \"large-heavy\".",
226 });
227
228 components.push(ComponentDef {
230 name: "MarkDownRenderer",
231 props: &[PropDef {
232 name: "text",
233 type_annotation: "string",
234 optional: false,
235 }],
236 description: "Renders markdown text",
237 });
238
239 components.push(ComponentDef {
241 name: "Callout",
242 props: &[
243 PropDef {
244 name: "title",
245 type_annotation: "string",
246 optional: false,
247 },
248 PropDef {
249 name: "children",
250 type_annotation: "any[]",
251 optional: false,
252 },
253 PropDef {
254 name: "variant",
255 type_annotation: "\"info\" | \"warning\" | \"error\" | \"success\"",
256 optional: true,
257 },
258 ],
259 description: "Highlighted block with title, content, and variant",
260 });
261
262 components.push(ComponentDef {
264 name: "TextCallout",
265 props: &[
266 PropDef {
267 name: "text",
268 type_annotation: "string",
269 optional: false,
270 },
271 PropDef {
272 name: "variant",
273 type_annotation: "\"info\" | \"warning\" | \"error\" | \"success\"",
274 optional: true,
275 },
276 ],
277 description: "Simple text callout with variant",
278 });
279
280 components.push(ComponentDef {
282 name: "CodeBlock",
283 props: &[
284 PropDef {
285 name: "code",
286 type_annotation: "string",
287 optional: false,
288 },
289 PropDef {
290 name: "language",
291 type_annotation: "string",
292 optional: true,
293 },
294 ],
295 description: "Syntax-highlighted code block",
296 });
297
298 components.push(ComponentDef {
300 name: "Image",
301 props: &[
302 PropDef {
303 name: "src",
304 type_annotation: "string",
305 optional: false,
306 },
307 PropDef {
308 name: "alt",
309 type_annotation: "string",
310 optional: true,
311 },
312 ],
313 description: "Single image",
314 });
315
316 components.push(ComponentDef {
318 name: "ImageBlock",
319 props: &[
320 PropDef {
321 name: "src",
322 type_annotation: "string",
323 optional: false,
324 },
325 PropDef {
326 name: "alt",
327 type_annotation: "string",
328 optional: true,
329 },
330 PropDef {
331 name: "caption",
332 type_annotation: "string",
333 optional: true,
334 },
335 ],
336 description: "Image with optional caption",
337 });
338
339 components.push(ComponentDef {
341 name: "ImageGallery",
342 props: &[PropDef {
343 name: "images",
344 type_annotation: "Image[]",
345 optional: false,
346 }],
347 description: "Grid of images",
348 });
349
350 components.push(ComponentDef {
357 name: "Table",
358 props: &[
359 PropDef {
360 name: "columns",
361 type_annotation: "Col[]",
362 optional: false,
363 },
364 PropDef {
365 name: "rows",
366 type_annotation: "(string | number | boolean)[][]",
367 optional: false,
368 },
369 ],
370 description: "Data table",
371 });
372
373 components.push(ComponentDef {
375 name: "Col",
376 props: &[
377 PropDef {
378 name: "label",
379 type_annotation: "string",
380 optional: false,
381 },
382 PropDef {
383 name: "type",
384 type_annotation: "\"string\" | \"number\" | \"action\"",
385 optional: true,
386 },
387 ],
388 description: "Table column definition",
389 });
390
391 components.push(ComponentDef {
398 name: "BarChart",
399 props: &[
400 PropDef { name: "labels", type_annotation: "string[]", optional: false },
401 PropDef { name: "series", type_annotation: "Series[]", optional: false },
402 PropDef { name: "variant", type_annotation: "\"grouped\" | \"stacked\"", optional: true },
403 PropDef { name: "xLabel", type_annotation: "string", optional: true },
404 PropDef { name: "yLabel", type_annotation: "string", optional: true },
405 ],
406 description: "Vertical bars; use for comparing values across categories with one or more series",
407 });
408
409 components.push(ComponentDef {
411 name: "LineChart",
412 props: &[
413 PropDef {
414 name: "labels",
415 type_annotation: "string[]",
416 optional: false,
417 },
418 PropDef {
419 name: "series",
420 type_annotation: "Series[]",
421 optional: false,
422 },
423 PropDef {
424 name: "xLabel",
425 type_annotation: "string",
426 optional: true,
427 },
428 PropDef {
429 name: "yLabel",
430 type_annotation: "string",
431 optional: true,
432 },
433 ],
434 description: "Line chart for trends over time or ordered categories",
435 });
436
437 components.push(ComponentDef {
439 name: "AreaChart",
440 props: &[
441 PropDef {
442 name: "labels",
443 type_annotation: "string[]",
444 optional: false,
445 },
446 PropDef {
447 name: "series",
448 type_annotation: "Series[]",
449 optional: false,
450 },
451 PropDef {
452 name: "variant",
453 type_annotation: "\"default\" | \"stacked\"",
454 optional: true,
455 },
456 PropDef {
457 name: "xLabel",
458 type_annotation: "string",
459 optional: true,
460 },
461 PropDef {
462 name: "yLabel",
463 type_annotation: "string",
464 optional: true,
465 },
466 ],
467 description: "Filled area chart; stacked variant shows cumulative totals",
468 });
469
470 components.push(ComponentDef {
472 name: "RadarChart",
473 props: &[
474 PropDef {
475 name: "labels",
476 type_annotation: "string[]",
477 optional: false,
478 },
479 PropDef {
480 name: "series",
481 type_annotation: "Series[]",
482 optional: false,
483 },
484 ],
485 description: "Spider/radar chart for multivariate comparison",
486 });
487
488 components.push(ComponentDef {
490 name: "HorizontalBarChart",
491 props: &[
492 PropDef {
493 name: "labels",
494 type_annotation: "string[]",
495 optional: false,
496 },
497 PropDef {
498 name: "series",
499 type_annotation: "Series[]",
500 optional: false,
501 },
502 PropDef {
503 name: "variant",
504 type_annotation: "\"grouped\" | \"stacked\"",
505 optional: true,
506 },
507 PropDef {
508 name: "xLabel",
509 type_annotation: "string",
510 optional: true,
511 },
512 PropDef {
513 name: "yLabel",
514 type_annotation: "string",
515 optional: true,
516 },
517 ],
518 description: "Horizontal bars; good for long category labels",
519 });
520
521 components.push(ComponentDef {
523 name: "Series",
524 props: &[
525 PropDef {
526 name: "category",
527 type_annotation: "string",
528 optional: false,
529 },
530 PropDef {
531 name: "values",
532 type_annotation: "number[]",
533 optional: false,
534 },
535 ],
536 description: "One data series",
537 });
538
539 components.push(ComponentDef {
545 name: "PieChart",
546 props: &[
547 PropDef { name: "slices", type_annotation: "Slice[]", optional: false },
548 PropDef { name: "variant", type_annotation: "\"pie\" | \"donut\"", optional: true },
549 ],
550 description: "Circular slices showing part-to-whole proportions; supports pie and donut variants",
551 });
552
553 components.push(ComponentDef {
555 name: "RadialChart",
556 props: &[PropDef {
557 name: "slices",
558 type_annotation: "Slice[]",
559 optional: false,
560 }],
561 description: "Radial bar chart for comparing a small number of values",
562 });
563
564 components.push(ComponentDef {
566 name: "SingleStackedBarChart",
567 props: &[PropDef {
568 name: "slices",
569 type_annotation: "Slice[]",
570 optional: false,
571 }],
572 description: "Single horizontal stacked bar for quick part-to-whole comparison",
573 });
574
575 components.push(ComponentDef {
577 name: "Slice",
578 props: &[
579 PropDef {
580 name: "category",
581 type_annotation: "string",
582 optional: false,
583 },
584 PropDef {
585 name: "value",
586 type_annotation: "number",
587 optional: false,
588 },
589 ],
590 description: "One slice with label and numeric value",
591 });
592
593 components.push(ComponentDef {
599 name: "ScatterChart",
600 props: &[
601 PropDef {
602 name: "series",
603 type_annotation: "ScatterSeries[]",
604 optional: false,
605 },
606 PropDef {
607 name: "xLabel",
608 type_annotation: "string",
609 optional: true,
610 },
611 PropDef {
612 name: "yLabel",
613 type_annotation: "string",
614 optional: true,
615 },
616 ],
617 description: "Scatter plot for showing correlation between two variables",
618 });
619
620 components.push(ComponentDef {
622 name: "ScatterSeries",
623 props: &[
624 PropDef {
625 name: "name",
626 type_annotation: "string",
627 optional: false,
628 },
629 PropDef {
630 name: "points",
631 type_annotation: "Point[]",
632 optional: false,
633 },
634 ],
635 description: "One scatter data series",
636 });
637
638 components.push(ComponentDef {
640 name: "Point",
641 props: &[
642 PropDef {
643 name: "x",
644 type_annotation: "number",
645 optional: false,
646 },
647 PropDef {
648 name: "y",
649 type_annotation: "number",
650 optional: false,
651 },
652 PropDef {
653 name: "z",
654 type_annotation: "number",
655 optional: true,
656 },
657 ],
658 description: "One data point with x, y coordinates and optional z",
659 });
660
661 components.push(ComponentDef {
668 name: "Form",
669 props: &[
670 PropDef {
671 name: "name",
672 type_annotation: "string",
673 optional: false,
674 },
675 PropDef {
676 name: "fields",
677 type_annotation: "FormControl[]",
678 optional: false,
679 },
680 PropDef {
681 name: "submit",
682 type_annotation: "Button",
683 optional: true,
684 },
685 ],
686 description: "Form container with named fields and optional submit button",
687 });
688
689 components.push(ComponentDef {
691 name: "FormControl",
692 props: &[
693 PropDef { name: "name", type_annotation: "string", optional: false },
694 PropDef { name: "label", type_annotation: "Label", optional: true },
695 PropDef { name: "input", type_annotation: "Input | TextArea | Select | DatePicker | Slider | CheckBoxGroup | RadioGroup | SwitchGroup", optional: false },
696 PropDef { name: "rules", type_annotation: "{required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}", optional: true },
697 ],
698 description: "Form field wrapper binding a name, label, input, and validation rules",
699 });
700
701 components.push(ComponentDef {
703 name: "Label",
704 props: &[
705 PropDef {
706 name: "text",
707 type_annotation: "string",
708 optional: false,
709 },
710 PropDef {
711 name: "description",
712 type_annotation: "string",
713 optional: true,
714 },
715 ],
716 description: "Field label with optional description",
717 });
718
719 components.push(ComponentDef {
721 name: "Input",
722 props: &[
723 PropDef {
724 name: "placeholder",
725 type_annotation: "string",
726 optional: true,
727 },
728 PropDef {
729 name: "defaultValue",
730 type_annotation: "string",
731 optional: true,
732 },
733 ],
734 description: "Single-line text input",
735 });
736
737 components.push(ComponentDef {
739 name: "TextArea",
740 props: &[
741 PropDef {
742 name: "placeholder",
743 type_annotation: "string",
744 optional: true,
745 },
746 PropDef {
747 name: "defaultValue",
748 type_annotation: "string",
749 optional: true,
750 },
751 PropDef {
752 name: "rows",
753 type_annotation: "number",
754 optional: true,
755 },
756 ],
757 description: "Multi-line text area",
758 });
759
760 components.push(ComponentDef {
762 name: "Select",
763 props: &[
764 PropDef {
765 name: "items",
766 type_annotation: "SelectItem[]",
767 optional: false,
768 },
769 PropDef {
770 name: "placeholder",
771 type_annotation: "string",
772 optional: true,
773 },
774 PropDef {
775 name: "defaultValue",
776 type_annotation: "string",
777 optional: true,
778 },
779 ],
780 description: "Dropdown select",
781 });
782
783 components.push(ComponentDef {
784 name: "SelectItem",
785 props: &[
786 PropDef {
787 name: "label",
788 type_annotation: "string",
789 optional: false,
790 },
791 PropDef {
792 name: "value",
793 type_annotation: "string",
794 optional: false,
795 },
796 ],
797 description: "One select option",
798 });
799
800 components.push(ComponentDef {
802 name: "DatePicker",
803 props: &[
804 PropDef {
805 name: "placeholder",
806 type_annotation: "string",
807 optional: true,
808 },
809 PropDef {
810 name: "defaultValue",
811 type_annotation: "string",
812 optional: true,
813 },
814 ],
815 description: "Date picker input",
816 });
817
818 components.push(ComponentDef {
820 name: "Slider",
821 props: &[
822 PropDef {
823 name: "min",
824 type_annotation: "number",
825 optional: true,
826 },
827 PropDef {
828 name: "max",
829 type_annotation: "number",
830 optional: true,
831 },
832 PropDef {
833 name: "step",
834 type_annotation: "number",
835 optional: true,
836 },
837 PropDef {
838 name: "defaultValue",
839 type_annotation: "number",
840 optional: true,
841 },
842 ],
843 description: "Numeric slider",
844 });
845
846 components.push(ComponentDef {
848 name: "CheckBoxGroup",
849 props: &[PropDef {
850 name: "items",
851 type_annotation: "CheckBoxItem[]",
852 optional: false,
853 }],
854 description: "Group of checkboxes for multi-select",
855 });
856
857 components.push(ComponentDef {
858 name: "CheckBoxItem",
859 props: &[
860 PropDef {
861 name: "label",
862 type_annotation: "string",
863 optional: false,
864 },
865 PropDef {
866 name: "value",
867 type_annotation: "string",
868 optional: false,
869 },
870 PropDef {
871 name: "defaultChecked",
872 type_annotation: "boolean",
873 optional: true,
874 },
875 ],
876 description: "One checkbox option",
877 });
878
879 components.push(ComponentDef {
881 name: "RadioGroup",
882 props: &[
883 PropDef {
884 name: "items",
885 type_annotation: "RadioItem[]",
886 optional: false,
887 },
888 PropDef {
889 name: "defaultValue",
890 type_annotation: "string",
891 optional: true,
892 },
893 ],
894 description: "Group of radio buttons for single-select",
895 });
896
897 components.push(ComponentDef {
898 name: "RadioItem",
899 props: &[
900 PropDef {
901 name: "label",
902 type_annotation: "string",
903 optional: false,
904 },
905 PropDef {
906 name: "value",
907 type_annotation: "string",
908 optional: false,
909 },
910 ],
911 description: "One radio option",
912 });
913
914 components.push(ComponentDef {
916 name: "SwitchGroup",
917 props: &[PropDef {
918 name: "items",
919 type_annotation: "SwitchItem[]",
920 optional: false,
921 }],
922 description: "Group of toggle switches",
923 });
924
925 components.push(ComponentDef {
926 name: "SwitchItem",
927 props: &[
928 PropDef {
929 name: "label",
930 type_annotation: "string",
931 optional: false,
932 },
933 PropDef {
934 name: "value",
935 type_annotation: "string",
936 optional: false,
937 },
938 PropDef {
939 name: "defaultChecked",
940 type_annotation: "boolean",
941 optional: true,
942 },
943 ],
944 description: "One toggle switch",
945 });
946
947 components.push(ComponentDef {
954 name: "Button",
955 props: &[
956 PropDef {
957 name: "label",
958 type_annotation: "string",
959 optional: false,
960 },
961 PropDef {
962 name: "action",
963 type_annotation: "ContinueConversation | OpenUrl | Custom",
964 optional: true,
965 },
966 PropDef {
967 name: "variant",
968 type_annotation: "\"primary\" | \"secondary\" | \"tertiary\"",
969 optional: true,
970 },
971 PropDef {
972 name: "type",
973 type_annotation: "\"normal\" | \"destructive\"",
974 optional: true,
975 },
976 PropDef {
977 name: "size",
978 type_annotation: "\"extra-small\" | \"small\" | \"medium\" | \"large\"",
979 optional: true,
980 },
981 ],
982 description: "Clickable button with label and optional action",
983 });
984
985 components.push(ComponentDef {
987 name: "Buttons",
988 props: &[
989 PropDef {
990 name: "buttons",
991 type_annotation: "Button[]",
992 optional: false,
993 },
994 PropDef {
995 name: "align",
996 type_annotation: "\"start\" | \"center\" | \"end\"",
997 optional: true,
998 },
999 ],
1000 description: "Row of buttons with alignment",
1001 });
1002
1003 components.push(ComponentDef {
1009 name: "TagBlock",
1010 props: &[PropDef {
1011 name: "tags",
1012 type_annotation: "Tag[]",
1013 optional: false,
1014 }],
1015 description: "Group of tags",
1016 });
1017
1018 components.push(ComponentDef {
1019 name: "Tag",
1020 props: &[
1021 PropDef {
1022 name: "label",
1023 type_annotation: "string",
1024 optional: false,
1025 },
1026 PropDef {
1027 name: "variant",
1028 type_annotation: "\"default\" | \"success\" | \"warning\" | \"error\" | \"info\"",
1029 optional: true,
1030 },
1031 ],
1032 description: "Single tag/badge",
1033 });
1034
1035 components.push(ComponentDef {
1042 name: "ListBlock",
1043 props: &[
1044 PropDef {
1045 name: "items",
1046 type_annotation: "ListItem[]",
1047 optional: false,
1048 },
1049 PropDef {
1050 name: "variant",
1051 type_annotation: "\"ordered\" | \"unordered\"",
1052 optional: true,
1053 },
1054 ],
1055 description: "Ordered or unordered list",
1056 });
1057
1058 components.push(ComponentDef {
1059 name: "ListItem",
1060 props: &[
1061 PropDef {
1062 name: "text",
1063 type_annotation: "string",
1064 optional: false,
1065 },
1066 PropDef {
1067 name: "children",
1068 type_annotation: "any[]",
1069 optional: true,
1070 },
1071 ],
1072 description: "One list item with text and optional nested content",
1073 });
1074
1075 components.push(ComponentDef {
1077 name: "FollowUpBlock",
1078 props: &[PropDef {
1079 name: "items",
1080 type_annotation: "FollowUpItem[]",
1081 optional: false,
1082 }],
1083 description: "Suggested follow-up questions",
1084 });
1085
1086 components.push(ComponentDef {
1087 name: "FollowUpItem",
1088 props: &[
1089 PropDef {
1090 name: "text",
1091 type_annotation: "string",
1092 optional: false,
1093 },
1094 PropDef {
1095 name: "action",
1096 type_annotation: "ContinueConversation | OpenUrl | Custom",
1097 optional: true,
1098 },
1099 ],
1100 description: "One follow-up suggestion with action",
1101 });
1102
1103 components.push(ComponentDef {
1105 name: "SectionBlock",
1106 props: &[PropDef {
1107 name: "items",
1108 type_annotation: "SectionItem[]",
1109 optional: false,
1110 }],
1111 description: "Block of titled sections",
1112 });
1113
1114 components.push(ComponentDef {
1115 name: "SectionItem",
1116 props: &[
1117 PropDef {
1118 name: "title",
1119 type_annotation: "string",
1120 optional: false,
1121 },
1122 PropDef {
1123 name: "children",
1124 type_annotation: "any[]",
1125 optional: false,
1126 },
1127 ],
1128 description: "One section with title and content",
1129 });
1130
1131 components
1132}
1133
1134#[cfg(test)]
1135mod tests {
1136 use super::*;
1137
1138 #[test]
1139 fn test_all_components_have_names() {
1140 let components = all_components();
1141 for comp in &components {
1142 assert!(!comp.name.is_empty());
1143 assert!(
1145 comp.name.chars().next().unwrap().is_uppercase(),
1146 "Component name '{}' should start with uppercase",
1147 comp.name
1148 );
1149 }
1150 }
1151
1152 #[test]
1153 fn test_no_duplicate_component_names() {
1154 let components = all_components();
1155 let mut seen = std::collections::HashSet::new();
1156 for comp in &components {
1157 assert!(
1158 seen.insert(comp.name),
1159 "Duplicate component name: {}",
1160 comp.name
1161 );
1162 }
1163 }
1164
1165 #[test]
1166 fn test_all_components_have_descriptions() {
1167 let components = all_components();
1168 for comp in &components {
1169 assert!(
1170 !comp.description.is_empty(),
1171 "Component '{}' has empty description",
1172 comp.name
1173 );
1174 }
1175 }
1176
1177 #[test]
1178 fn test_prop_types_not_empty() {
1179 let components = all_components();
1180 for comp in &components {
1181 for prop in comp.props {
1182 assert!(
1183 !prop.type_annotation.is_empty(),
1184 "Component '{}' prop '{}' has empty type annotation",
1185 comp.name,
1186 prop.name
1187 );
1188 }
1189 }
1190 }
1191
1192 #[test]
1193 fn test_stack_component_props() {
1194 let components = all_components();
1195 let stack = components.iter().find(|c| c.name == "Stack").unwrap();
1196 assert_eq!(stack.props[0].name, "children");
1197 assert!(!stack.props[0].optional);
1198 assert_eq!(stack.props[1].name, "direction");
1199 assert!(stack.props[1].optional);
1200 }
1201
1202 #[test]
1203 fn test_table_component_props() {
1204 let components = all_components();
1205 let table = components.iter().find(|c| c.name == "Table").unwrap();
1206 assert_eq!(table.props[0].name, "columns");
1207 assert_eq!(table.props[1].name, "rows");
1208 assert!(!table.props[0].optional);
1209 }
1210}