@open-mercato/ui 0.6.1-develop.3246.1.dbef9d7392 → 0.6.1-develop.3260.1.dc719cac84

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 (164) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/__integration__/TC-AI-UI-003-aichat-registry.spec.tsx +13 -0
  3. package/dist/ai/AiAssistantLauncher.js +8 -0
  4. package/dist/ai/AiAssistantLauncher.js.map +2 -2
  5. package/dist/ai/AiChat.js +226 -58
  6. package/dist/ai/AiChat.js.map +2 -2
  7. package/dist/ai/AiDock.js +3 -2
  8. package/dist/ai/AiDock.js.map +2 -2
  9. package/dist/ai/ModelPicker.js +186 -0
  10. package/dist/ai/ModelPicker.js.map +7 -0
  11. package/dist/ai/index.js +3 -1
  12. package/dist/ai/index.js.map +2 -2
  13. package/dist/ai/useAiChat.js +12 -16
  14. package/dist/ai/useAiChat.js.map +2 -2
  15. package/dist/backend/CrudForm.js +46 -129
  16. package/dist/backend/CrudForm.js.map +3 -3
  17. package/dist/backend/DataTable.js +10 -3
  18. package/dist/backend/DataTable.js.map +2 -2
  19. package/dist/backend/EmptyState.js +1 -47
  20. package/dist/backend/EmptyState.js.map +2 -2
  21. package/dist/backend/FilterOverlay.js +31 -26
  22. package/dist/backend/FilterOverlay.js.map +2 -2
  23. package/dist/backend/FlashMessages.js +23 -25
  24. package/dist/backend/FlashMessages.js.map +2 -2
  25. package/dist/backend/JsonBuilder.js +4 -2
  26. package/dist/backend/JsonBuilder.js.map +2 -2
  27. package/dist/backend/LogList.js +49 -0
  28. package/dist/backend/LogList.js.map +7 -0
  29. package/dist/backend/crud/CollapsibleGroup.js +13 -3
  30. package/dist/backend/crud/CollapsibleGroup.js.map +2 -2
  31. package/dist/backend/detail/InlineEditors.js +80 -32
  32. package/dist/backend/detail/InlineEditors.js.map +2 -2
  33. package/dist/backend/inputs/ComboboxInput.js +60 -6
  34. package/dist/backend/inputs/ComboboxInput.js.map +2 -2
  35. package/dist/backend/inputs/DatePicker.js +6 -142
  36. package/dist/backend/inputs/DatePicker.js.map +2 -2
  37. package/dist/backend/inputs/DateTimePicker.js +6 -182
  38. package/dist/backend/inputs/DateTimePicker.js.map +2 -2
  39. package/dist/backend/inputs/TagsInput.js +16 -19
  40. package/dist/backend/inputs/TagsInput.js.map +2 -2
  41. package/dist/backend/inputs/TimeInput.js +1 -1
  42. package/dist/backend/inputs/TimeInput.js.map +2 -2
  43. package/dist/backend/inputs/TimePicker.js +83 -72
  44. package/dist/backend/inputs/TimePicker.js.map +2 -2
  45. package/dist/backend/utils/customFieldForms.js +2 -2
  46. package/dist/backend/utils/customFieldForms.js.map +1 -1
  47. package/dist/portal/components/PortalEmptyState.js +13 -9
  48. package/dist/portal/components/PortalEmptyState.js.map +2 -2
  49. package/dist/primitives/accordion.js +137 -0
  50. package/dist/primitives/accordion.js.map +7 -0
  51. package/dist/primitives/alert.js +180 -12
  52. package/dist/primitives/alert.js.map +2 -2
  53. package/dist/primitives/calendar.js +122 -32
  54. package/dist/primitives/calendar.js.map +2 -2
  55. package/dist/primitives/compact-select.js +47 -0
  56. package/dist/primitives/compact-select.js.map +7 -0
  57. package/dist/primitives/counter-input.js +174 -0
  58. package/dist/primitives/counter-input.js.map +7 -0
  59. package/dist/primitives/date-picker-helpers.js +79 -0
  60. package/dist/primitives/date-picker-helpers.js.map +7 -0
  61. package/dist/primitives/date-picker.js +216 -0
  62. package/dist/primitives/date-picker.js.map +7 -0
  63. package/dist/primitives/date-range-picker.js +239 -0
  64. package/dist/primitives/date-range-picker.js.map +7 -0
  65. package/dist/primitives/digit-input.js +157 -0
  66. package/dist/primitives/digit-input.js.map +7 -0
  67. package/dist/primitives/empty-state.js +93 -0
  68. package/dist/primitives/empty-state.js.map +7 -0
  69. package/dist/primitives/inline-input.js +29 -0
  70. package/dist/primitives/inline-input.js.map +7 -0
  71. package/dist/primitives/inline-select.js +41 -0
  72. package/dist/primitives/inline-select.js.map +7 -0
  73. package/dist/primitives/notification-stack.js +116 -0
  74. package/dist/primitives/notification-stack.js.map +7 -0
  75. package/dist/primitives/notification.js +62 -0
  76. package/dist/primitives/notification.js.map +7 -0
  77. package/dist/primitives/rich-editor.js +1230 -0
  78. package/dist/primitives/rich-editor.js.map +7 -0
  79. package/dist/primitives/select.js +1 -0
  80. package/dist/primitives/select.js.map +2 -2
  81. package/dist/primitives/skeleton.js +55 -0
  82. package/dist/primitives/skeleton.js.map +7 -0
  83. package/dist/primitives/tag-input.js +186 -0
  84. package/dist/primitives/tag-input.js.map +7 -0
  85. package/dist/primitives/tag.js +66 -13
  86. package/dist/primitives/tag.js.map +2 -2
  87. package/dist/primitives/time-picker.js +793 -0
  88. package/dist/primitives/time-picker.js.map +7 -0
  89. package/package.json +4 -3
  90. package/src/ai/AiAssistantLauncher.tsx +8 -0
  91. package/src/ai/AiChat.tsx +229 -11
  92. package/src/ai/AiDock.tsx +3 -2
  93. package/src/ai/ModelPicker.tsx +248 -0
  94. package/src/ai/__tests__/AiAssistantLauncher.test.tsx +58 -0
  95. package/src/ai/__tests__/AiChat.conversation.test.tsx +16 -0
  96. package/src/ai/__tests__/AiChat.debug.test.tsx +16 -0
  97. package/src/ai/__tests__/AiChat.registry.test.tsx +16 -0
  98. package/src/ai/__tests__/AiChat.test.tsx +180 -0
  99. package/src/ai/__tests__/AiChat.thinking-unmount.test.tsx +16 -0
  100. package/src/ai/__tests__/AiDock.test.tsx +12 -1
  101. package/src/ai/__tests__/ModelPicker.test.tsx +252 -0
  102. package/src/ai/index.ts +1 -0
  103. package/src/ai/useAiChat.ts +29 -16
  104. package/src/backend/CrudForm.tsx +61 -113
  105. package/src/backend/DataTable.tsx +17 -5
  106. package/src/backend/EmptyState.tsx +13 -63
  107. package/src/backend/FilterOverlay.tsx +38 -22
  108. package/src/backend/FlashMessages.tsx +23 -24
  109. package/src/backend/JsonBuilder.tsx +4 -2
  110. package/src/backend/LogList.tsx +109 -0
  111. package/src/backend/__tests__/CrudForm.datetime.test.tsx +12 -7
  112. package/src/backend/__tests__/FilterOverlay.dateRange.test.tsx +89 -0
  113. package/src/backend/__tests__/LogList.test.tsx +163 -0
  114. package/src/backend/crud/CollapsibleGroup.tsx +17 -3
  115. package/src/backend/detail/InlineEditors.tsx +78 -25
  116. package/src/backend/detail/__tests__/InlineEditors.test.tsx +81 -1
  117. package/src/backend/inputs/ComboboxInput.tsx +81 -9
  118. package/src/backend/inputs/DatePicker.tsx +28 -155
  119. package/src/backend/inputs/DateTimePicker.tsx +25 -193
  120. package/src/backend/inputs/TagsInput.tsx +10 -13
  121. package/src/backend/inputs/TimeInput.tsx +2 -1
  122. package/src/backend/inputs/TimePicker.tsx +99 -70
  123. package/src/backend/inputs/__tests__/ComboboxInput.test.tsx +133 -0
  124. package/src/backend/inputs/__tests__/TimePicker.test.tsx +2 -2
  125. package/src/backend/utils/__tests__/richTextSanitizer.test.ts +5 -2
  126. package/src/backend/utils/customFieldForms.ts +2 -2
  127. package/src/portal/components/PortalEmptyState.tsx +27 -22
  128. package/src/primitives/__tests__/accordion.test.tsx +197 -0
  129. package/src/primitives/__tests__/alert.test.tsx +204 -0
  130. package/src/primitives/__tests__/compact-select.test.tsx +98 -0
  131. package/src/primitives/__tests__/counter-input.test.tsx +165 -0
  132. package/src/primitives/__tests__/date-picker.test.tsx +221 -0
  133. package/src/primitives/__tests__/date-range-picker.test.tsx +173 -0
  134. package/src/primitives/__tests__/digit-input.test.tsx +168 -0
  135. package/src/primitives/__tests__/empty-state.test.tsx +154 -0
  136. package/src/primitives/__tests__/inline-input.test.tsx +100 -0
  137. package/src/primitives/__tests__/inline-select.test.tsx +103 -0
  138. package/src/primitives/__tests__/notification-stack.test.tsx +174 -0
  139. package/src/primitives/__tests__/notification.test.tsx +119 -0
  140. package/src/primitives/__tests__/rich-editor.test.tsx +411 -0
  141. package/src/primitives/__tests__/skeleton.test.tsx +76 -0
  142. package/src/primitives/__tests__/tag-input.test.tsx +198 -0
  143. package/src/primitives/__tests__/tag.test.tsx +123 -0
  144. package/src/primitives/__tests__/time-picker.test.tsx +392 -0
  145. package/src/primitives/accordion.tsx +225 -0
  146. package/src/primitives/alert.tsx +234 -24
  147. package/src/primitives/calendar.tsx +146 -38
  148. package/src/primitives/compact-select.tsx +59 -0
  149. package/src/primitives/counter-input.tsx +211 -0
  150. package/src/primitives/date-picker-helpers.ts +96 -0
  151. package/src/primitives/date-picker.tsx +290 -0
  152. package/src/primitives/date-range-picker.tsx +281 -0
  153. package/src/primitives/digit-input.tsx +208 -0
  154. package/src/primitives/empty-state.tsx +140 -0
  155. package/src/primitives/inline-input.tsx +55 -0
  156. package/src/primitives/inline-select.tsx +74 -0
  157. package/src/primitives/notification-stack.tsx +185 -0
  158. package/src/primitives/notification.tsx +136 -0
  159. package/src/primitives/rich-editor.tsx +1578 -0
  160. package/src/primitives/select.tsx +1 -0
  161. package/src/primitives/skeleton.tsx +70 -0
  162. package/src/primitives/tag-input.tsx +223 -0
  163. package/src/primitives/tag.tsx +69 -10
  164. package/src/primitives/time-picker.tsx +1059 -0
@@ -0,0 +1,174 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+
5
+ import * as React from 'react'
6
+ import { act, fireEvent, render, screen } from '@testing-library/react'
7
+ import {
8
+ NotificationProvider,
9
+ NotificationStack,
10
+ useNotification,
11
+ } from '../notification-stack'
12
+
13
+ function Harness({
14
+ placement,
15
+ maxVisible,
16
+ children,
17
+ }: {
18
+ placement?: React.ComponentProps<typeof NotificationStack>['placement']
19
+ maxVisible?: number
20
+ children?: React.ReactNode
21
+ }) {
22
+ return (
23
+ <NotificationProvider maxVisible={maxVisible}>
24
+ {children}
25
+ <NotificationStack placement={placement} />
26
+ </NotificationProvider>
27
+ )
28
+ }
29
+
30
+ function NotifyButton({ label = 'Notify', options }: { label?: string; options: any }) {
31
+ const { notify } = useNotification()
32
+ return (
33
+ <button type="button" onClick={() => notify(options)}>
34
+ {label}
35
+ </button>
36
+ )
37
+ }
38
+
39
+ describe('NotificationStack + useNotification', () => {
40
+ beforeEach(() => {
41
+ jest.useFakeTimers()
42
+ })
43
+ afterEach(() => {
44
+ jest.useRealTimers()
45
+ })
46
+
47
+ it('renders nothing when the queue is empty', () => {
48
+ const { container } = render(<Harness />)
49
+ expect(container.querySelector('[data-slot="notification-stack"]')).toBeNull()
50
+ })
51
+
52
+ it('renders the stack wrapper once a notification is queued', () => {
53
+ const { container } = render(
54
+ <Harness>
55
+ <NotifyButton options={{ title: 'Hello' }} />
56
+ </Harness>,
57
+ )
58
+ fireEvent.click(screen.getByText('Notify'))
59
+ expect(container.querySelector('[data-slot="notification-stack"]')).not.toBeNull()
60
+ expect(screen.getByText('Hello')).toBeInTheDocument()
61
+ })
62
+
63
+ it('respects the placement prop (data-placement attribute + Tailwind classes)', () => {
64
+ const { container } = render(
65
+ <Harness placement="bottom-left">
66
+ <NotifyButton options={{ title: 'A' }} />
67
+ </Harness>,
68
+ )
69
+ fireEvent.click(screen.getByText('Notify'))
70
+ const stack = container.querySelector('[data-slot="notification-stack"]')
71
+ expect(stack).toHaveAttribute('data-placement', 'bottom-left')
72
+ expect(stack).toHaveClass('bottom-4')
73
+ expect(stack).toHaveClass('left-4')
74
+ })
75
+
76
+ it('dismiss button on a notification removes it from the queue', () => {
77
+ render(
78
+ <Harness>
79
+ <NotifyButton options={{ title: 'Dismissable' }} />
80
+ </Harness>,
81
+ )
82
+ fireEvent.click(screen.getByText('Notify'))
83
+ expect(screen.getByText('Dismissable')).toBeInTheDocument()
84
+ fireEvent.click(screen.getByRole('button', { name: 'Dismiss' }))
85
+ expect(screen.queryByText('Dismissable')).toBeNull()
86
+ })
87
+
88
+ it('auto-dismisses after autoDismissMs and clears its timer', () => {
89
+ render(
90
+ <Harness>
91
+ <NotifyButton options={{ title: 'Auto', autoDismissMs: 2000 }} />
92
+ </Harness>,
93
+ )
94
+ fireEvent.click(screen.getByText('Notify'))
95
+ expect(screen.getByText('Auto')).toBeInTheDocument()
96
+ act(() => {
97
+ jest.advanceTimersByTime(1999)
98
+ })
99
+ expect(screen.getByText('Auto')).toBeInTheDocument()
100
+ act(() => {
101
+ jest.advanceTimersByTime(1)
102
+ })
103
+ expect(screen.queryByText('Auto')).toBeNull()
104
+ })
105
+
106
+ it('manual dismiss before autoDismissMs cancels the timer (no late re-fire)', () => {
107
+ render(
108
+ <Harness>
109
+ <NotifyButton options={{ title: 'Cancel', autoDismissMs: 3000 }} />
110
+ </Harness>,
111
+ )
112
+ fireEvent.click(screen.getByText('Notify'))
113
+ fireEvent.click(screen.getByRole('button', { name: 'Dismiss' }))
114
+ expect(screen.queryByText('Cancel')).toBeNull()
115
+ // Advance past the auto-dismiss window — no errors, no resurrected card.
116
+ act(() => {
117
+ jest.advanceTimersByTime(5000)
118
+ })
119
+ expect(screen.queryByText('Cancel')).toBeNull()
120
+ })
121
+
122
+ it('respects maxVisible cap (FIFO trim of oldest entries)', () => {
123
+ render(
124
+ <Harness maxVisible={2}>
125
+ <NotifyButton label="A" options={{ title: 'First' }} />
126
+ <NotifyButton label="B" options={{ title: 'Second' }} />
127
+ <NotifyButton label="C" options={{ title: 'Third' }} />
128
+ </Harness>,
129
+ )
130
+ fireEvent.click(screen.getByText('A'))
131
+ fireEvent.click(screen.getByText('B'))
132
+ fireEvent.click(screen.getByText('C'))
133
+ expect(screen.queryByText('First')).toBeNull()
134
+ expect(screen.getByText('Second')).toBeInTheDocument()
135
+ expect(screen.getByText('Third')).toBeInTheDocument()
136
+ })
137
+
138
+ it('useNotification throws outside the provider', () => {
139
+ function Bad() {
140
+ useNotification()
141
+ return null
142
+ }
143
+ // Suppress React error logging for the expected throw.
144
+ const spy = jest.spyOn(console, 'error').mockImplementation(() => {})
145
+ expect(() => render(<Bad />)).toThrow(
146
+ /useNotification must be used within a NotificationProvider/,
147
+ )
148
+ spy.mockRestore()
149
+ })
150
+
151
+ it('dismissAll clears the queue', () => {
152
+ function DismissAllButton() {
153
+ const { dismissAll } = useNotification()
154
+ return (
155
+ <button type="button" onClick={dismissAll}>
156
+ Clear
157
+ </button>
158
+ )
159
+ }
160
+ render(
161
+ <Harness>
162
+ <NotifyButton label="A" options={{ title: 'First' }} />
163
+ <NotifyButton label="B" options={{ title: 'Second' }} />
164
+ <DismissAllButton />
165
+ </Harness>,
166
+ )
167
+ fireEvent.click(screen.getByText('A'))
168
+ fireEvent.click(screen.getByText('B'))
169
+ expect(screen.getByText('First')).toBeInTheDocument()
170
+ fireEvent.click(screen.getByText('Clear'))
171
+ expect(screen.queryByText('First')).toBeNull()
172
+ expect(screen.queryByText('Second')).toBeNull()
173
+ })
174
+ })
@@ -0,0 +1,119 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+
5
+ import * as React from 'react'
6
+ import { fireEvent, render, screen } from '@testing-library/react'
7
+ import { Notification } from '../notification'
8
+
9
+ describe('Notification primitive', () => {
10
+ it('renders the underlying Alert at size="default" (rounded-xl)', () => {
11
+ const { container } = render(<Notification title="Test" />)
12
+ const alert = container.querySelector('[data-slot="notification"]')
13
+ expect(alert).toHaveClass('rounded-xl')
14
+ })
15
+
16
+ it('marks the root with data-slot="notification" (overrides Alert data-slot via spread)', () => {
17
+ const { container } = render(<Notification title="Test" />)
18
+ expect(container.querySelector('[data-slot="notification"]')).not.toBeNull()
19
+ })
20
+
21
+ it('forwards id as data-notification-id', () => {
22
+ const { container } = render(<Notification id="notif-123" title="Test" />)
23
+ expect(container.querySelector('[data-notification-id="notif-123"]')).not.toBeNull()
24
+ })
25
+
26
+ it('renders title as AlertTitle and description below', () => {
27
+ render(<Notification title="John commented" description="On the latest deal" />)
28
+ expect(screen.getByText('John commented').tagName).toBe('H5')
29
+ expect(screen.getByText('On the latest deal').tagName).toBe('P')
30
+ })
31
+
32
+ it('description gets opacity-72 per Figma Notification reference', () => {
33
+ const { container } = render(
34
+ <Notification title="t" description="body" />,
35
+ )
36
+ const desc = container.querySelector('[data-slot="notification-description"]')
37
+ expect(desc).toHaveClass('opacity-72')
38
+ })
39
+
40
+ it('renders the timestamp slot when provided', () => {
41
+ render(<Notification title="t" timestamp="2 min ago" />)
42
+ expect(screen.getByText('2 min ago')).toBeInTheDocument()
43
+ })
44
+
45
+ it('omits the title row entirely when neither title nor timestamp is provided', () => {
46
+ const { container } = render(<Notification description="just a body" />)
47
+ expect(container.querySelector('h5')).toBeNull()
48
+ expect(container.querySelector('[data-slot="notification-timestamp"]')).toBeNull()
49
+ })
50
+
51
+ it('renders an actions row when actions are provided', () => {
52
+ render(
53
+ <Notification
54
+ title="t"
55
+ actions={
56
+ <>
57
+ <button data-testid="view">View</button>
58
+ <button data-testid="dismiss">Dismiss</button>
59
+ </>
60
+ }
61
+ />,
62
+ )
63
+ expect(screen.getByTestId('view')).toBeInTheDocument()
64
+ expect(screen.getByTestId('dismiss')).toBeInTheDocument()
65
+ })
66
+
67
+ it('actions row carries data-slot="notification-actions"', () => {
68
+ const { container } = render(
69
+ <Notification title="t" actions={<button>x</button>} />,
70
+ )
71
+ expect(container.querySelector('[data-slot="notification-actions"]')).not.toBeNull()
72
+ })
73
+
74
+ it('uses the avatar prop as the Alert icon override (no default status icon when avatar set)', () => {
75
+ const Avatar = () => <span data-testid="user-avatar" />
76
+ const { container } = render(<Notification title="t" avatar={<Avatar />} />)
77
+ expect(screen.getByTestId('user-avatar')).toBeInTheDocument()
78
+ // No badge wrap because Alert sees `icon` prop and treats this as the leading visual
79
+ expect(container.querySelector('[data-slot="alert-icon-badge"]')).not.toBeNull()
80
+ })
81
+
82
+ it('dismissible defaults to true and fires onDismiss when X is clicked', () => {
83
+ const onDismiss = jest.fn()
84
+ render(<Notification title="t" onDismiss={onDismiss} />)
85
+ fireEvent.click(screen.getByRole('button', { name: 'Dismiss' }))
86
+ expect(onDismiss).toHaveBeenCalledTimes(1)
87
+ })
88
+
89
+ it('omits the dismiss button when dismissible is false', () => {
90
+ render(<Notification title="t" dismissible={false} />)
91
+ expect(screen.queryByRole('button', { name: 'Dismiss' })).toBeNull()
92
+ })
93
+
94
+ it('honors a custom dismissAriaLabel', () => {
95
+ render(<Notification title="t" dismissAriaLabel="Zamknij" />)
96
+ expect(screen.getByRole('button', { name: 'Zamknij' })).toBeInTheDocument()
97
+ })
98
+
99
+ it('forwards status to the underlying Alert', () => {
100
+ const { container } = render(<Notification status="success" title="Saved" />)
101
+ expect(container.querySelector('[data-slot="notification"]')).toHaveAttribute('data-status', 'success')
102
+ })
103
+
104
+ it('forwards style to the underlying Alert', () => {
105
+ const { container } = render(
106
+ <Notification status="warning" style="lighter" title="Heads up" />,
107
+ )
108
+ const alert = container.querySelector('[data-slot="notification"]')
109
+ expect(alert).toHaveAttribute('data-style', 'lighter')
110
+ expect(alert).toHaveClass('bg-status-warning-bg')
111
+ })
112
+
113
+ it('forwards refs to the underlying Alert wrapper div', () => {
114
+ const ref = React.createRef<HTMLDivElement>()
115
+ render(<Notification ref={ref} title="t" />)
116
+ expect(ref.current).not.toBeNull()
117
+ expect(ref.current?.getAttribute('data-slot')).toBe('notification')
118
+ })
119
+ })