@assistant-ui/react-native 0.1.27 → 0.1.28

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 (39) hide show
  1. package/dist/index.d.ts +3 -3
  2. package/dist/index.js +3 -3
  3. package/dist/primitives/composer/ComposerInput.d.ts.map +1 -1
  4. package/dist/primitives/composer/ComposerInput.js +8 -0
  5. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  6. package/dist/primitives/thread.d.ts +3 -3
  7. package/dist/primitives/thread.js +4 -3
  8. package/dist/primitives/thread.js.map +1 -1
  9. package/package.json +11 -7
  10. package/src/index.ts +29 -0
  11. package/src/primitives/attachment/AttachmentName.test.tsx +55 -0
  12. package/src/primitives/attachment/AttachmentRemove.test.tsx +81 -0
  13. package/src/primitives/attachment/AttachmentThumb.test.tsx +75 -0
  14. package/src/primitives/branchPicker/BranchPickerCount.test.tsx +55 -0
  15. package/src/primitives/branchPicker/BranchPickerNext.test.tsx +97 -0
  16. package/src/primitives/branchPicker/BranchPickerNumber.test.tsx +56 -0
  17. package/src/primitives/branchPicker/BranchPickerPrevious.test.tsx +100 -0
  18. package/src/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.test.tsx +85 -0
  19. package/src/primitives/composer/ComposerAddAttachment.test.tsx +136 -0
  20. package/src/primitives/composer/ComposerCancel.test.tsx +116 -0
  21. package/src/primitives/composer/ComposerInput.test.tsx +253 -0
  22. package/src/primitives/composer/ComposerInput.tsx +12 -0
  23. package/src/primitives/composer/ComposerSend.test.tsx +116 -0
  24. package/src/primitives/error/ErrorMessage.test.tsx +64 -0
  25. package/src/primitives/error/ErrorRoot.test.tsx +65 -0
  26. package/src/primitives/message/MessageContent.test.tsx +278 -0
  27. package/src/primitives/message/MessageIf.test.tsx +156 -0
  28. package/src/primitives/suggestion/SuggestionDescription.test.tsx +61 -0
  29. package/src/primitives/suggestion/SuggestionTitle.test.tsx +61 -0
  30. package/src/primitives/suggestion/SuggestionTrigger.test.tsx +144 -0
  31. package/src/primitives/thread/ThreadEmpty.test.tsx +56 -0
  32. package/src/primitives/thread/ThreadIf.test.tsx +121 -0
  33. package/src/primitives/thread/ThreadMessages.test.tsx +192 -0
  34. package/src/primitives/thread.ts +1 -0
  35. package/src/primitives/threadList/ThreadListNew.test.tsx +72 -0
  36. package/src/primitives/threadListItem/ThreadListItemArchive.test.tsx +72 -0
  37. package/src/primitives/threadListItem/ThreadListItemDelete.test.tsx +72 -0
  38. package/src/primitives/threadListItem/ThreadListItemTrigger.test.tsx +72 -0
  39. package/src/primitives/threadListItem/ThreadListItemUnarchive.test.tsx +72 -0
@@ -0,0 +1,56 @@
1
+ import { act } from "react";
2
+ import { createRoot, type Root } from "react-dom/client";
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
+ import { BranchPickerNumber } from "./BranchPickerNumber";
5
+
6
+ const h = vi.hoisted(() => ({
7
+ state: { message: { branchNumber: 0 } },
8
+ }));
9
+
10
+ vi.mock("@assistant-ui/store", () => ({
11
+ useAuiState: <T,>(
12
+ selector: (s: { message: { branchNumber: number } }) => T,
13
+ ) => selector(h.state),
14
+ }));
15
+
16
+ (globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
17
+
18
+ describe("BranchPickerNumber", () => {
19
+ let container: HTMLDivElement;
20
+ let root: Root;
21
+
22
+ beforeEach(() => {
23
+ h.state.message.branchNumber = 0;
24
+ container = document.createElement("div");
25
+ document.body.appendChild(container);
26
+ root = createRoot(container);
27
+ });
28
+
29
+ afterEach(async () => {
30
+ await act(async () => {
31
+ root.unmount();
32
+ });
33
+ container.remove();
34
+ });
35
+
36
+ const mount = async () => {
37
+ await act(async () => {
38
+ root.render(<BranchPickerNumber testID="number" />);
39
+ });
40
+ const el = container.querySelector('[data-testid="number"]');
41
+ expect(el).not.toBeNull();
42
+ return el as HTMLElement;
43
+ };
44
+
45
+ it("renders the branchNumber from store state", async () => {
46
+ h.state.message.branchNumber = 1;
47
+ const el = await mount();
48
+ expect(el.textContent).toBe("1");
49
+ });
50
+
51
+ it("renders a different branchNumber value", async () => {
52
+ h.state.message.branchNumber = 7;
53
+ const el = await mount();
54
+ expect(el.textContent).toBe("7");
55
+ });
56
+ });
@@ -0,0 +1,100 @@
1
+ import { act } from "react";
2
+ import { createRoot, type Root } from "react-dom/client";
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
+ import { BranchPickerPrevious } from "./BranchPickerPrevious";
5
+
6
+ const h = vi.hoisted(() => ({
7
+ previous: vi.fn<() => void>(),
8
+ state: { disabled: false },
9
+ }));
10
+
11
+ vi.mock("@assistant-ui/core/react", () => ({
12
+ useBranchPickerPrevious: () => ({
13
+ previous: h.previous,
14
+ disabled: h.state.disabled,
15
+ }),
16
+ }));
17
+
18
+ (globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
19
+
20
+ const click = (el: Element) =>
21
+ el.dispatchEvent(
22
+ new MouseEvent("click", { bubbles: true, cancelable: true }),
23
+ );
24
+
25
+ describe("BranchPickerPrevious", () => {
26
+ let container: HTMLDivElement;
27
+ let root: Root;
28
+
29
+ beforeEach(() => {
30
+ h.previous.mockReset();
31
+ h.state.disabled = false;
32
+
33
+ container = document.createElement("div");
34
+ document.body.appendChild(container);
35
+ root = createRoot(container);
36
+ });
37
+
38
+ afterEach(async () => {
39
+ await act(async () => {
40
+ root.unmount();
41
+ });
42
+ container.remove();
43
+ });
44
+
45
+ const mount = async (
46
+ props: Partial<Parameters<typeof BranchPickerPrevious>[0]> = {},
47
+ ) => {
48
+ await act(async () => {
49
+ root.render(
50
+ <BranchPickerPrevious testID="t" {...props}>
51
+ prev
52
+ </BranchPickerPrevious>,
53
+ );
54
+ });
55
+ return container.querySelector('[data-testid="t"]') as HTMLElement;
56
+ };
57
+
58
+ it("fires previous when pressed and the hook is enabled", async () => {
59
+ const el = await mount();
60
+
61
+ await act(async () => {
62
+ click(el);
63
+ });
64
+
65
+ expect(h.previous).toHaveBeenCalledTimes(1);
66
+ });
67
+
68
+ it("does not fire previous when the hook reports disabled", async () => {
69
+ h.state.disabled = true;
70
+ const el = await mount();
71
+
72
+ await act(async () => {
73
+ click(el);
74
+ });
75
+
76
+ expect(h.previous).not.toHaveBeenCalled();
77
+ });
78
+
79
+ it("lets an explicit disabled prop override an enabled hook", async () => {
80
+ h.state.disabled = false;
81
+ const el = await mount({ disabled: true });
82
+
83
+ await act(async () => {
84
+ click(el);
85
+ });
86
+
87
+ expect(h.previous).not.toHaveBeenCalled();
88
+ });
89
+
90
+ it("lets an explicit disabled={false} prop override a disabled hook", async () => {
91
+ h.state.disabled = true;
92
+ const el = await mount({ disabled: false });
93
+
94
+ await act(async () => {
95
+ click(el);
96
+ });
97
+
98
+ expect(h.previous).toHaveBeenCalledTimes(1);
99
+ });
100
+ });
@@ -0,0 +1,85 @@
1
+ import { act } from "react";
2
+ import { createRoot, type Root } from "react-dom/client";
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
+ import { Text } from "react-native";
5
+ import { ChainOfThoughtAccordionTrigger } from "./ChainOfThoughtAccordionTrigger";
6
+
7
+ const h = vi.hoisted(() => ({
8
+ setCollapsed: vi.fn<(collapsed: boolean) => void>(),
9
+ state: { chainOfThought: { collapsed: true } },
10
+ }));
11
+
12
+ vi.mock("@assistant-ui/store", () => {
13
+ const aui = {
14
+ chainOfThought: () => ({ setCollapsed: h.setCollapsed }),
15
+ };
16
+ return {
17
+ useAui: () => aui,
18
+ useAuiState: <T,>(selector: (s: typeof h.state) => T) => selector(h.state),
19
+ };
20
+ });
21
+
22
+ (globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
23
+
24
+ describe("ChainOfThoughtAccordionTrigger", () => {
25
+ let container: HTMLDivElement;
26
+ let root: Root;
27
+
28
+ beforeEach(() => {
29
+ h.setCollapsed.mockReset();
30
+ h.state.chainOfThought.collapsed = true;
31
+ container = document.createElement("div");
32
+ document.body.appendChild(container);
33
+ root = createRoot(container);
34
+ });
35
+
36
+ afterEach(async () => {
37
+ await act(async () => {
38
+ root.unmount();
39
+ });
40
+ container.remove();
41
+ });
42
+
43
+ const mount = async () => {
44
+ await act(async () => {
45
+ root.render(
46
+ <ChainOfThoughtAccordionTrigger testID="trigger">
47
+ <Text>toggle</Text>
48
+ </ChainOfThoughtAccordionTrigger>,
49
+ );
50
+ });
51
+ return container.querySelector(
52
+ '[data-testid="trigger"]',
53
+ ) as HTMLElement | null;
54
+ };
55
+
56
+ const press = async (el: HTMLElement) => {
57
+ await act(async () => {
58
+ el.dispatchEvent(
59
+ new MouseEvent("click", { bubbles: true, cancelable: true }),
60
+ );
61
+ });
62
+ };
63
+
64
+ it("renders the trigger with its children", async () => {
65
+ const el = await mount();
66
+ expect(el).not.toBeNull();
67
+ expect(el?.textContent).toBe("toggle");
68
+ });
69
+
70
+ it("expands (sets collapsed false) when currently collapsed", async () => {
71
+ h.state.chainOfThought.collapsed = true;
72
+ const el = await mount();
73
+ await press(el!);
74
+ expect(h.setCollapsed).toHaveBeenCalledTimes(1);
75
+ expect(h.setCollapsed).toHaveBeenCalledWith(false);
76
+ });
77
+
78
+ it("collapses (sets collapsed true) when currently expanded", async () => {
79
+ h.state.chainOfThought.collapsed = false;
80
+ const el = await mount();
81
+ await press(el!);
82
+ expect(h.setCollapsed).toHaveBeenCalledTimes(1);
83
+ expect(h.setCollapsed).toHaveBeenCalledWith(true);
84
+ });
85
+ });
@@ -0,0 +1,136 @@
1
+ import { act } from "react";
2
+ import { createRoot, type Root } from "react-dom/client";
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
+ import { ComposerAddAttachment } from "./ComposerAddAttachment";
5
+
6
+ const h = vi.hoisted(() => ({
7
+ disabled: false,
8
+ }));
9
+
10
+ vi.mock("@assistant-ui/core/react", () => ({
11
+ useComposerAddAttachment: () => ({ disabled: h.disabled }),
12
+ }));
13
+
14
+ (globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
15
+
16
+ const click = (el: Element) => {
17
+ el.dispatchEvent(
18
+ new MouseEvent("click", { bubbles: true, cancelable: true }),
19
+ );
20
+ };
21
+
22
+ describe("ComposerAddAttachment", () => {
23
+ let container: HTMLDivElement;
24
+ let root: Root;
25
+
26
+ beforeEach(() => {
27
+ h.disabled = false;
28
+
29
+ container = document.createElement("div");
30
+ document.body.appendChild(container);
31
+ root = createRoot(container);
32
+ });
33
+
34
+ afterEach(async () => {
35
+ await act(async () => {
36
+ root.unmount();
37
+ });
38
+ container.remove();
39
+ });
40
+
41
+ const mount = async (
42
+ props: Partial<Parameters<typeof ComposerAddAttachment>[0]> & {
43
+ onPress?: () => void;
44
+ } = {},
45
+ ) => {
46
+ await act(async () => {
47
+ root.render(
48
+ <ComposerAddAttachment testID="add" {...props}>
49
+ add
50
+ </ComposerAddAttachment>,
51
+ );
52
+ });
53
+ const el = container.querySelector('[data-testid="add"]');
54
+ expect(el).not.toBeNull();
55
+ return el as Element;
56
+ };
57
+
58
+ it("renders its children", async () => {
59
+ const el = await mount();
60
+ expect(el.textContent).toBe("add");
61
+ });
62
+
63
+ it("wires no onPress so pressing is a no-op", async () => {
64
+ const el = await mount();
65
+
66
+ let threw = false;
67
+ await act(async () => {
68
+ try {
69
+ click(el);
70
+ } catch {
71
+ threw = true;
72
+ }
73
+ });
74
+
75
+ expect(threw).toBe(false);
76
+ });
77
+
78
+ it("forwards an explicit onPress prop", async () => {
79
+ const onPress = vi.fn();
80
+ const el = await mount({ onPress });
81
+
82
+ await act(async () => {
83
+ click(el);
84
+ });
85
+
86
+ expect(onPress).toHaveBeenCalledTimes(1);
87
+ });
88
+
89
+ it("blocks a forwarded onPress when the hook reports disabled", async () => {
90
+ h.disabled = true;
91
+ const onPress = vi.fn();
92
+ const el = await mount({ onPress });
93
+
94
+ await act(async () => {
95
+ click(el);
96
+ });
97
+
98
+ expect(onPress).not.toHaveBeenCalled();
99
+ });
100
+
101
+ it("lets a false prop override the hook's disabled:true", async () => {
102
+ h.disabled = true;
103
+ const onPress = vi.fn();
104
+ const el = await mount({ disabled: false, onPress });
105
+
106
+ await act(async () => {
107
+ click(el);
108
+ });
109
+
110
+ expect(onPress).toHaveBeenCalledTimes(1);
111
+ });
112
+
113
+ it("lets a true prop override the hook's disabled:false", async () => {
114
+ h.disabled = false;
115
+ const onPress = vi.fn();
116
+ const el = await mount({ disabled: true, onPress });
117
+
118
+ await act(async () => {
119
+ click(el);
120
+ });
121
+
122
+ expect(onPress).not.toHaveBeenCalled();
123
+ });
124
+
125
+ it("defers to the hook when disabled is undefined", async () => {
126
+ h.disabled = false;
127
+ const onPress = vi.fn();
128
+ const el = await mount({ disabled: undefined, onPress });
129
+
130
+ await act(async () => {
131
+ click(el);
132
+ });
133
+
134
+ expect(onPress).toHaveBeenCalledTimes(1);
135
+ });
136
+ });
@@ -0,0 +1,116 @@
1
+ import { act } from "react";
2
+ import { createRoot, type Root } from "react-dom/client";
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
+ import { ComposerCancel } from "./ComposerCancel";
5
+
6
+ const h = vi.hoisted(() => ({
7
+ cancel: vi.fn<() => void>(),
8
+ disabled: false,
9
+ }));
10
+
11
+ vi.mock("@assistant-ui/core/react", () => ({
12
+ useComposerCancel: () => ({ cancel: h.cancel, disabled: h.disabled }),
13
+ }));
14
+
15
+ (globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
16
+
17
+ const click = (el: Element) => {
18
+ el.dispatchEvent(
19
+ new MouseEvent("click", { bubbles: true, cancelable: true }),
20
+ );
21
+ };
22
+
23
+ describe("ComposerCancel", () => {
24
+ let container: HTMLDivElement;
25
+ let root: Root;
26
+
27
+ beforeEach(() => {
28
+ h.cancel.mockReset();
29
+ h.disabled = false;
30
+
31
+ container = document.createElement("div");
32
+ document.body.appendChild(container);
33
+ root = createRoot(container);
34
+ });
35
+
36
+ afterEach(async () => {
37
+ await act(async () => {
38
+ root.unmount();
39
+ });
40
+ container.remove();
41
+ });
42
+
43
+ const mount = async (
44
+ props: Partial<Parameters<typeof ComposerCancel>[0]> = {},
45
+ ) => {
46
+ await act(async () => {
47
+ root.render(
48
+ <ComposerCancel testID="cancel" {...props}>
49
+ cancel
50
+ </ComposerCancel>,
51
+ );
52
+ });
53
+ const el = container.querySelector('[data-testid="cancel"]');
54
+ expect(el).not.toBeNull();
55
+ return el as Element;
56
+ };
57
+
58
+ it("renders its children", async () => {
59
+ const el = await mount();
60
+ expect(el.textContent).toBe("cancel");
61
+ });
62
+
63
+ it("calls cancel on press", async () => {
64
+ const el = await mount();
65
+
66
+ await act(async () => {
67
+ click(el);
68
+ });
69
+
70
+ expect(h.cancel).toHaveBeenCalledTimes(1);
71
+ });
72
+
73
+ it("does not press when the hook reports disabled", async () => {
74
+ h.disabled = true;
75
+ const el = await mount();
76
+
77
+ await act(async () => {
78
+ click(el);
79
+ });
80
+
81
+ expect(h.cancel).not.toHaveBeenCalled();
82
+ });
83
+
84
+ it("lets a false prop override the hook's disabled:true", async () => {
85
+ h.disabled = true;
86
+ const el = await mount({ disabled: false });
87
+
88
+ await act(async () => {
89
+ click(el);
90
+ });
91
+
92
+ expect(h.cancel).toHaveBeenCalledTimes(1);
93
+ });
94
+
95
+ it("lets a true prop override the hook's disabled:false", async () => {
96
+ h.disabled = false;
97
+ const el = await mount({ disabled: true });
98
+
99
+ await act(async () => {
100
+ click(el);
101
+ });
102
+
103
+ expect(h.cancel).not.toHaveBeenCalled();
104
+ });
105
+
106
+ it("defers to the hook when disabled is undefined", async () => {
107
+ h.disabled = false;
108
+ const el = await mount({ disabled: undefined });
109
+
110
+ await act(async () => {
111
+ click(el);
112
+ });
113
+
114
+ expect(h.cancel).toHaveBeenCalledTimes(1);
115
+ });
116
+ });