@assistant-ui/react-generative-ui 0.0.21 → 0.0.23

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 (212) hide show
  1. package/LICENSE +1 -1
  2. package/dist/JSONGenerativeUI.client.d.ts.map +1 -1
  3. package/dist/JSONGenerativeUI.client.js +30 -13
  4. package/dist/JSONGenerativeUI.client.js.map +1 -1
  5. package/dist/RadioGroupScope.d.ts +6 -0
  6. package/dist/RadioGroupScope.d.ts.map +1 -0
  7. package/dist/RadioGroupScope.js +25 -0
  8. package/dist/RadioGroupScope.js.map +1 -0
  9. package/dist/a2ui/BindingContext.d.ts +11 -0
  10. package/dist/a2ui/BindingContext.d.ts.map +1 -0
  11. package/dist/a2ui/BindingContext.js +12 -0
  12. package/dist/a2ui/BindingContext.js.map +1 -0
  13. package/dist/a2ui/PresentRenderer.d.ts +11 -0
  14. package/dist/a2ui/PresentRenderer.d.ts.map +1 -0
  15. package/dist/a2ui/PresentRenderer.js +39 -0
  16. package/dist/a2ui/PresentRenderer.js.map +1 -0
  17. package/dist/a2ui/convert.d.ts +6 -0
  18. package/dist/a2ui/convert.d.ts.map +1 -1
  19. package/dist/a2ui/convert.js +363 -85
  20. package/dist/a2ui/convert.js.map +1 -1
  21. package/dist/a2ui/dataModel.d.ts +9 -0
  22. package/dist/a2ui/dataModel.d.ts.map +1 -0
  23. package/dist/a2ui/dataModel.js +44 -0
  24. package/dist/a2ui/dataModel.js.map +1 -0
  25. package/dist/a2ui/reducer.d.ts +5 -0
  26. package/dist/a2ui/reducer.d.ts.map +1 -1
  27. package/dist/a2ui/reducer.js +3 -3
  28. package/dist/a2ui/reducer.js.map +1 -1
  29. package/dist/a2ui/types.d.ts +8 -2
  30. package/dist/a2ui/types.d.ts.map +1 -1
  31. package/dist/a2ui/types.js.map +1 -1
  32. package/dist/a2ui/valueFunctions.d.ts +16 -0
  33. package/dist/a2ui/valueFunctions.d.ts.map +1 -0
  34. package/dist/a2ui/valueFunctions.js +332 -0
  35. package/dist/a2ui/valueFunctions.js.map +1 -0
  36. package/dist/actionRegistry.d.ts +1 -1
  37. package/dist/actionRegistry.js.map +1 -1
  38. package/dist/answeredValues.d.ts +16 -0
  39. package/dist/answeredValues.d.ts.map +1 -0
  40. package/dist/answeredValues.js +37 -0
  41. package/dist/answeredValues.js.map +1 -0
  42. package/dist/constants.d.ts +3 -0
  43. package/dist/constants.d.ts.map +1 -1
  44. package/dist/constants.js +4 -1
  45. package/dist/constants.js.map +1 -1
  46. package/dist/fieldReferences.d.ts +9 -0
  47. package/dist/fieldReferences.d.ts.map +1 -0
  48. package/dist/fieldReferences.js +53 -0
  49. package/dist/fieldReferences.js.map +1 -0
  50. package/dist/generativeUIToJSX.js +3 -3
  51. package/dist/generativeUIToJSX.js.map +1 -1
  52. package/dist/index.js +2 -2
  53. package/dist/index.server.js +2 -2
  54. package/dist/index.shared.js +2 -2
  55. package/dist/ir.d.ts +1 -1
  56. package/dist/ir.js.map +1 -1
  57. package/dist/renderGenerativeUI.d.ts.map +1 -1
  58. package/dist/renderGenerativeUI.js +12 -1
  59. package/dist/renderGenerativeUI.js.map +1 -1
  60. package/dist/slack/constants.d.ts +2 -0
  61. package/dist/slack/constants.d.ts.map +1 -1
  62. package/dist/slack/constants.js +3 -1
  63. package/dist/slack/constants.js.map +1 -1
  64. package/dist/slack/decodeBlockAction.d.ts +3 -1
  65. package/dist/slack/decodeBlockAction.d.ts.map +1 -1
  66. package/dist/slack/decodeBlockAction.js +96 -7
  67. package/dist/slack/decodeBlockAction.js.map +1 -1
  68. package/dist/slack/fromSlackBlocks.d.ts.map +1 -1
  69. package/dist/slack/fromSlackBlocks.js +43 -3
  70. package/dist/slack/fromSlackBlocks.js.map +1 -1
  71. package/dist/slack/toSlackBlocks.d.ts.map +1 -1
  72. package/dist/slack/toSlackBlocks.js +244 -20
  73. package/dist/slack/toSlackBlocks.js.map +1 -1
  74. package/dist/slack/types.d.ts +24 -1
  75. package/dist/slack/types.d.ts.map +1 -1
  76. package/dist/slack.d.ts +1 -1
  77. package/dist/slack.d.ts.map +1 -1
  78. package/dist/teams/constants.d.ts +1 -1
  79. package/dist/teams/constants.d.ts.map +1 -1
  80. package/dist/teams/constants.js +1 -1
  81. package/dist/teams/constants.js.map +1 -1
  82. package/dist/teams/decodeSubmitData.d.ts +4 -0
  83. package/dist/teams/decodeSubmitData.d.ts.map +1 -1
  84. package/dist/teams/decodeSubmitData.js +29 -3
  85. package/dist/teams/decodeSubmitData.js.map +1 -1
  86. package/dist/teams/temporalId.d.ts +5 -0
  87. package/dist/teams/temporalId.d.ts.map +1 -0
  88. package/dist/teams/temporalId.js +28 -0
  89. package/dist/teams/temporalId.js.map +1 -0
  90. package/dist/teams/toAdaptiveCard.d.ts +5 -4
  91. package/dist/teams/toAdaptiveCard.d.ts.map +1 -1
  92. package/dist/teams/toAdaptiveCard.js +136 -18
  93. package/dist/teams/toAdaptiveCard.js.map +1 -1
  94. package/dist/teams/toTeamsAttachments.d.ts.map +1 -1
  95. package/dist/teams/toTeamsAttachments.js.map +1 -1
  96. package/dist/teams/types.d.ts +23 -2
  97. package/dist/teams/types.d.ts.map +1 -1
  98. package/dist/teams.d.ts +1 -1
  99. package/dist/teams.d.ts.map +1 -1
  100. package/dist/temporal.d.ts +33 -0
  101. package/dist/temporal.d.ts.map +1 -0
  102. package/dist/temporal.js +152 -0
  103. package/dist/temporal.js.map +1 -0
  104. package/dist/vocabulary/alert.d.ts +9 -2
  105. package/dist/vocabulary/alert.d.ts.map +1 -1
  106. package/dist/vocabulary/alert.js +116 -19
  107. package/dist/vocabulary/alert.js.map +1 -1
  108. package/dist/vocabulary/collectFormValues.d.ts +2 -1
  109. package/dist/vocabulary/collectFormValues.d.ts.map +1 -1
  110. package/dist/vocabulary/collectFormValues.js +15 -4
  111. package/dist/vocabulary/collectFormValues.js.map +1 -1
  112. package/dist/vocabulary/data.d.ts +22 -5
  113. package/dist/vocabulary/data.d.ts.map +1 -1
  114. package/dist/vocabulary/data.js +278 -83
  115. package/dist/vocabulary/data.js.map +1 -1
  116. package/dist/vocabulary/dispatch.d.ts +3 -2
  117. package/dist/vocabulary/dispatch.d.ts.map +1 -1
  118. package/dist/vocabulary/dispatch.js +33 -4
  119. package/dist/vocabulary/dispatch.js.map +1 -1
  120. package/dist/vocabulary/fact.d.ts +8 -1
  121. package/dist/vocabulary/fact.d.ts.map +1 -1
  122. package/dist/vocabulary/fact.js +37 -11
  123. package/dist/vocabulary/fact.js.map +1 -1
  124. package/dist/vocabulary/form.d.ts.map +1 -1
  125. package/dist/vocabulary/form.js +3 -3
  126. package/dist/vocabulary/form.js.map +1 -1
  127. package/dist/vocabulary/formatValue.d.ts +7 -0
  128. package/dist/vocabulary/formatValue.d.ts.map +1 -0
  129. package/dist/vocabulary/formatValue.js +76 -0
  130. package/dist/vocabulary/formatValue.js.map +1 -0
  131. package/dist/vocabulary/icon.d.ts +1 -1
  132. package/dist/vocabulary/interactive.d.ts +143 -8
  133. package/dist/vocabulary/interactive.d.ts.map +1 -1
  134. package/dist/vocabulary/interactive.js +458 -107
  135. package/dist/vocabulary/interactive.js.map +1 -1
  136. package/dist/vocabulary/layout.d.ts.map +1 -1
  137. package/dist/vocabulary/layout.js +4 -4
  138. package/dist/vocabulary/layout.js.map +1 -1
  139. package/dist/vocabulary/list.js +2 -2
  140. package/dist/vocabulary/list.js.map +1 -1
  141. package/dist/vocabulary/text.d.ts +1 -1
  142. package/package.json +8 -5
  143. package/src/JSONGenerativeUI.client.tsx +84 -40
  144. package/src/JSONGenerativeUI.test.tsx +964 -68
  145. package/src/RadioGroupScope.server.test.tsx +24 -0
  146. package/src/RadioGroupScope.tsx +34 -0
  147. package/src/a2ui/BindingContext.ts +26 -0
  148. package/src/a2ui/PresentRenderer.tsx +83 -0
  149. package/src/a2ui/convert.test.ts +2054 -229
  150. package/src/a2ui/convert.ts +665 -134
  151. package/src/a2ui/dataModel.ts +69 -0
  152. package/src/a2ui/reducer.test.ts +35 -0
  153. package/src/a2ui/reducer.ts +5 -4
  154. package/src/a2ui/types.ts +9 -2
  155. package/src/a2ui/valueFunctions.test.ts +271 -0
  156. package/src/a2ui/valueFunctions.ts +523 -0
  157. package/src/actionRegistry.ts +1 -1
  158. package/src/answeredValues.test.ts +33 -0
  159. package/src/answeredValues.tsx +70 -0
  160. package/src/constants.ts +5 -0
  161. package/src/fieldReferences.test.ts +66 -0
  162. package/src/fieldReferences.ts +83 -0
  163. package/src/generativeUIToJSX.test.ts +18 -0
  164. package/src/generativeUIToJSX.ts +4 -6
  165. package/src/index.server.test.ts +106 -0
  166. package/src/ir.ts +1 -1
  167. package/src/renderGenerativeUI.test.tsx +14 -5
  168. package/src/renderGenerativeUI.tsx +30 -1
  169. package/src/slack/a2uiSlack.test.ts +198 -0
  170. package/src/slack/constants.ts +3 -0
  171. package/src/slack/datePicker.test.ts +612 -0
  172. package/src/slack/decodeBlockAction.test.ts +98 -0
  173. package/src/slack/decodeBlockAction.ts +182 -16
  174. package/src/slack/fromSlackBlocks.test.ts +222 -3
  175. package/src/slack/fromSlackBlocks.ts +82 -2
  176. package/src/slack/toSlackBlocks.test.ts +682 -3
  177. package/src/slack/toSlackBlocks.ts +463 -25
  178. package/src/slack/types.ts +28 -0
  179. package/src/slack.ts +2 -0
  180. package/src/teams/constants.ts +1 -1
  181. package/src/teams/decodeSubmitData.test.ts +249 -0
  182. package/src/teams/decodeSubmitData.ts +57 -4
  183. package/src/teams/temporal.test.ts +766 -0
  184. package/src/teams/temporalId.ts +56 -0
  185. package/src/teams/toAdaptiveCard.test.ts +219 -0
  186. package/src/teams/toAdaptiveCard.ts +259 -21
  187. package/src/teams/toTeamsAttachments.ts +4 -1
  188. package/src/teams/types.ts +21 -1
  189. package/src/teams.ts +1 -0
  190. package/src/temporal.test.ts +319 -0
  191. package/src/temporal.ts +259 -0
  192. package/src/vocabulary/alert.test.tsx +288 -7
  193. package/src/vocabulary/alert.tsx +168 -26
  194. package/src/vocabulary/collectFormValues.test.ts +225 -1
  195. package/src/vocabulary/collectFormValues.ts +54 -5
  196. package/src/vocabulary/data.test.tsx +248 -6
  197. package/src/vocabulary/data.tsx +398 -100
  198. package/src/vocabulary/datepicker.dom.test.tsx +1148 -0
  199. package/src/vocabulary/dispatch.ts +63 -2
  200. package/src/vocabulary/fact.test.tsx +67 -0
  201. package/src/vocabulary/fact.tsx +40 -9
  202. package/src/vocabulary/form.tsx +7 -2
  203. package/src/vocabulary/formatValue.test.ts +72 -0
  204. package/src/vocabulary/formatValue.ts +127 -0
  205. package/src/vocabulary/index.test.ts +2 -0
  206. package/src/vocabulary/interactive.dispatch.test.tsx +167 -79
  207. package/src/vocabulary/interactive.dom.test.tsx +1747 -0
  208. package/src/vocabulary/interactive.test.tsx +60 -4
  209. package/src/vocabulary/interactive.tsx +910 -155
  210. package/src/vocabulary/layout.test.tsx +6 -4
  211. package/src/vocabulary/layout.tsx +13 -2
  212. package/src/vocabulary/list.tsx +2 -2
@@ -0,0 +1,56 @@
1
+ import { classifyTemporal } from "../temporal";
2
+ import type { TeamsTemporalField } from "./types";
3
+
4
+ export const TEMPORAL_INPUT_PREFIX = "aui:datetime:";
5
+ const encodeFieldId = (value: string): string =>
6
+ Array.from(value, (char) =>
7
+ /^[\uD800-\uDFFF]$/.test(char)
8
+ ? `%u${char.charCodeAt(0).toString(16).toUpperCase()}`
9
+ : encodeURIComponent(char),
10
+ ).join("");
11
+
12
+ const decodeFieldId = (value: string): string =>
13
+ value
14
+ .split(/(%uD[89A-F][0-9A-F]{2})/)
15
+ .map((part, index) =>
16
+ index % 2 === 0
17
+ ? decodeURIComponent(part)
18
+ : String.fromCharCode(Number.parseInt(part.slice(2), 16)),
19
+ )
20
+ .join("");
21
+
22
+ export const encodeTemporalInputId = (field: TeamsTemporalField): string =>
23
+ `${TEMPORAL_INPUT_PREFIX}${field.role}:${encodeFieldId(field.fieldId)}${field.role === "time" ? `:${encodeURIComponent(field.previousValue ?? "")}` : ""}`;
24
+
25
+ export const decodeTemporalInputId = (
26
+ id: string,
27
+ ): TeamsTemporalField | undefined => {
28
+ try {
29
+ const parts = id.slice(TEMPORAL_INPUT_PREFIX.length).split(":");
30
+ const role = parts[0];
31
+ if (
32
+ !id.startsWith(TEMPORAL_INPUT_PREFIX) ||
33
+ (role !== "date" && role !== "time") ||
34
+ parts.length !== (role === "date" ? 2 : 3)
35
+ )
36
+ return undefined;
37
+ const fieldId = decodeFieldId(parts[1]!);
38
+ const previousValue = role === "time" ? decodeURIComponent(parts[2]!) : "";
39
+ if (
40
+ !fieldId ||
41
+ fieldId === "aui" ||
42
+ fieldId.startsWith(TEMPORAL_INPUT_PREFIX) ||
43
+ (previousValue !== "" &&
44
+ classifyTemporal(previousValue).kind !== "instant")
45
+ )
46
+ return undefined;
47
+ const metadata: TeamsTemporalField = {
48
+ fieldId,
49
+ role,
50
+ ...(previousValue ? { previousValue } : {}),
51
+ };
52
+ return encodeTemporalInputId(metadata) === id ? metadata : undefined;
53
+ } catch {
54
+ return undefined;
55
+ }
56
+ };
@@ -202,6 +202,22 @@ describe("toAdaptiveCard", () => {
202
202
  expect((card.body[0] as TeamsFactSet).facts).toHaveLength(5);
203
203
  });
204
204
 
205
+ it("appends a directional fact delta to the value text", () => {
206
+ const { card } = toAdaptiveCard({
207
+ $type: "Fact",
208
+ label: "Revenue",
209
+ value: "$12.4k",
210
+ delta: "8%",
211
+ trend: "down",
212
+ });
213
+ expect(card.body).toEqual([
214
+ {
215
+ type: "FactSet",
216
+ facts: [{ title: "Revenue", value: "$12.4k (↓ 8%)" }],
217
+ },
218
+ ]);
219
+ });
220
+
205
221
  it("breaks Fact merging on a non-Fact sibling", () => {
206
222
  const { card } = toAdaptiveCard([
207
223
  { $type: "Fact", label: "A", value: "1" },
@@ -481,6 +497,24 @@ describe("toAdaptiveCard", () => {
481
497
  expect((card.body[0] as TeamsInputChoiceSet).id).toBe("select");
482
498
  });
483
499
 
500
+ it("sets value from a non-empty defaultValue", () => {
501
+ const { card } = toAdaptiveCard({
502
+ $type: "Select",
503
+ defaultValue: "blue",
504
+ options: [{ label: "Blue", value: "blue" }],
505
+ });
506
+ expect((card.body[0] as TeamsInputChoiceSet).value).toBe("blue");
507
+ });
508
+
509
+ it("omits value when defaultValue is empty", () => {
510
+ const { card } = toAdaptiveCard({
511
+ $type: "Select",
512
+ defaultValue: "",
513
+ options: [{ label: "Blue", value: "blue" }],
514
+ });
515
+ expect((card.body[0] as TeamsInputChoiceSet).value).toBeUndefined();
516
+ });
517
+
484
518
  it("does not dispatch a prop array through Symbol.species", () => {
485
519
  let dispatches = 0;
486
520
  const injected = Array.from(
@@ -556,6 +590,86 @@ describe("toAdaptiveCard", () => {
556
590
  },
557
591
  ]);
558
592
  });
593
+
594
+ it("appends option descriptions to choice titles", () => {
595
+ const { card } = toAdaptiveCard({
596
+ $type: "RadioGroup",
597
+ options: [
598
+ {
599
+ label: "Free",
600
+ description: "For personal projects",
601
+ value: "free",
602
+ },
603
+ ],
604
+ });
605
+ expect((card.body[0] as TeamsInputChoiceSet).choices).toEqual([
606
+ { title: "Free: For personal projects", value: "free" },
607
+ ]);
608
+ });
609
+ });
610
+
611
+ describe("CheckboxGroup", () => {
612
+ it("renders an expanded multi-select Input.ChoiceSet with the default values joined by commas", () => {
613
+ const { card } = toAdaptiveCard({
614
+ $type: "CheckboxGroup",
615
+ name: "toppings",
616
+ label: "Toppings",
617
+ defaultValue: ["basil", 7, "onion"],
618
+ options: [
619
+ { label: "Basil", value: "basil" },
620
+ { label: "Olives", value: "olives" },
621
+ { label: "Onion", value: "onion" },
622
+ ],
623
+ });
624
+ expect(card.body).toEqual([
625
+ {
626
+ type: "Input.ChoiceSet",
627
+ id: "toppings",
628
+ style: "expanded",
629
+ isMultiSelect: true,
630
+ choices: [
631
+ { title: "Basil", value: "basil" },
632
+ { title: "Olives", value: "olives" },
633
+ { title: "Onion", value: "onion" },
634
+ ],
635
+ value: "basil,onion",
636
+ label: "Toppings",
637
+ },
638
+ ]);
639
+ });
640
+
641
+ it("falls back to the checkboxgroup id and appends a companion submit for an action", () => {
642
+ const { card, warnings } = toAdaptiveCard({
643
+ $type: "CheckboxGroup",
644
+ options: [{ label: "Basil", value: "basil" }],
645
+ $action: { type: "pick" },
646
+ });
647
+ expect(card.body).toEqual([
648
+ {
649
+ type: "Input.ChoiceSet",
650
+ id: "checkboxgroup",
651
+ style: "expanded",
652
+ isMultiSelect: true,
653
+ choices: [{ title: "Basil", value: "basil" }],
654
+ },
655
+ {
656
+ type: "ActionSet",
657
+ actions: [
658
+ {
659
+ type: "Action.Submit",
660
+ title: "Submit",
661
+ data: { aui: { type: "pick" } },
662
+ },
663
+ ],
664
+ },
665
+ ]);
666
+ expect(warnings).toContainEqual({
667
+ code: "fallback",
668
+ component: "CheckboxGroup",
669
+ detail:
670
+ "Teams inputs cannot dispatch on change; a companion submit action was appended.",
671
+ });
672
+ });
559
673
  });
560
674
 
561
675
  describe("Checkbox", () => {
@@ -583,9 +697,58 @@ describe("toAdaptiveCard", () => {
583
697
  expect((card.body[0] as TeamsInputToggle).id).toBe("Agree");
584
698
  expect((card.body[0] as TeamsInputToggle).value).toBe("false");
585
699
  });
700
+
701
+ it("maps a switch like a checkbox", () => {
702
+ const { card } = toAdaptiveCard({
703
+ $type: "Checkbox",
704
+ label: "Subscribe",
705
+ variant: "switch",
706
+ });
707
+ expect(card.body[0]).toMatchObject({
708
+ type: "Input.Toggle",
709
+ title: "Subscribe",
710
+ });
711
+ });
712
+ });
713
+
714
+ describe("Slider", () => {
715
+ it("renders an Input.Number with its bounds, value, and label", () => {
716
+ const { card } = toAdaptiveCard({
717
+ $type: "Slider",
718
+ name: "quantity",
719
+ label: "Quantity",
720
+ min: 1,
721
+ max: 12,
722
+ defaultValue: 3,
723
+ });
724
+ expect(card.body[0]).toEqual({
725
+ type: "Input.Number",
726
+ id: "quantity",
727
+ label: "Quantity",
728
+ min: 1,
729
+ max: 12,
730
+ value: 3,
731
+ });
732
+ });
586
733
  });
587
734
 
588
735
  describe("Input", () => {
736
+ it("maps password and number inputs while preserving string values", () => {
737
+ const { card } = toAdaptiveCard([
738
+ {
739
+ $type: "Input",
740
+ name: "quantity",
741
+ inputType: "number",
742
+ defaultValue: "2.5",
743
+ },
744
+ { $type: "Input", name: "password", inputType: "password" },
745
+ ]);
746
+ expect(card.body).toEqual([
747
+ { type: "Input.Text", id: "quantity", value: "2.5" },
748
+ { type: "Input.Text", id: "password", style: "password" },
749
+ ]);
750
+ });
751
+
589
752
  it("renders an Input.Text", () => {
590
753
  const { card } = toAdaptiveCard({
591
754
  $type: "Input",
@@ -612,6 +775,38 @@ describe("toAdaptiveCard", () => {
612
775
  expect((card.body[0] as TeamsInputText).isMultiline).toBe(true);
613
776
  });
614
777
 
778
+ it("keeps a multiline password input single-line", () => {
779
+ const { card } = toAdaptiveCard({
780
+ $type: "Input",
781
+ name: "password",
782
+ inputType: "password",
783
+ multiline: true,
784
+ });
785
+ expect(card.body[0]).toEqual({
786
+ type: "Input.Text",
787
+ id: "password",
788
+ style: "password",
789
+ });
790
+ });
791
+
792
+ it("sets value from a non-empty defaultValue", () => {
793
+ const { card } = toAdaptiveCard({
794
+ $type: "Input",
795
+ name: "notes",
796
+ defaultValue: "Draft reply",
797
+ });
798
+ expect((card.body[0] as TeamsInputText).value).toBe("Draft reply");
799
+ });
800
+
801
+ it("omits value when defaultValue is empty", () => {
802
+ const { card } = toAdaptiveCard({
803
+ $type: "Input",
804
+ name: "notes",
805
+ defaultValue: "",
806
+ });
807
+ expect((card.body[0] as TeamsInputText).value).toBeUndefined();
808
+ });
809
+
615
810
  it("emits no ActionSet when $action is absent", () => {
616
811
  const { card } = toAdaptiveCard({ $type: "Input", name: "email" });
617
812
  expect(card.body).toHaveLength(1);
@@ -663,7 +858,9 @@ describe("toAdaptiveCard", () => {
663
858
  ["Input", { $type: "Input", name: "aui" }],
664
859
  ["Select", { $type: "Select", name: "aui", options: [] }],
665
860
  ["RadioGroup", { $type: "RadioGroup", name: "aui", options: [] }],
861
+ ["CheckboxGroup", { $type: "CheckboxGroup", name: "aui", options: [] }],
666
862
  ["Checkbox", { $type: "Checkbox", name: "aui", label: "Agree" }],
863
+ ["Slider", { $type: "Slider", name: "aui", min: 0, max: 10 }],
667
864
  ["DatePicker", { $type: "DatePicker", name: "aui" }],
668
865
  ] as const)(
669
866
  "renames a %s named aui to aui_ with a warning",
@@ -1142,6 +1339,28 @@ describe("toAdaptiveCard", () => {
1142
1339
  ]);
1143
1340
  });
1144
1341
 
1342
+ it("formats table cells from their columns", () => {
1343
+ const { card } = toAdaptiveCard({
1344
+ $type: "Table",
1345
+ columns: [
1346
+ { label: "Number", format: { kind: "number", decimals: 1 } },
1347
+ {
1348
+ label: "Revenue",
1349
+ format: { kind: "currency", currency: "USD", decimals: 2 },
1350
+ },
1351
+ { label: "Share", format: { kind: "percent", decimals: 0 } },
1352
+ { label: "Date", format: { kind: "date" } },
1353
+ ],
1354
+ rows: [[1234.5, 12.4, 0.08, "2024-01-02"]],
1355
+ });
1356
+ const table = card.body[0] as TeamsTable;
1357
+ expect(
1358
+ table.rows[1]?.cells.map(
1359
+ (cell) => (cell.items[0] as TeamsTextBlock).text,
1360
+ ),
1361
+ ).toEqual(["1,234.5", "$12.40", "8%", "Jan 2, 2024"]);
1362
+ });
1363
+
1145
1364
  it("sets firstRowAsHeaders false and emits no header row when there are no columns", () => {
1146
1365
  const { card } = toAdaptiveCard({
1147
1366
  $type: "Table",