@igstack/app-catalog-frontend-core 6.9.0 → 6.10.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 (80) hide show
  1. package/dist/esm/__tests__/modules/appCatalog/ui/detail/CommunitySection.feed.test.d.ts +0 -0
  2. package/dist/esm/__tests__/modules/appCatalog/ui/detail/CommunitySection.help.test.d.ts +0 -0
  3. package/dist/esm/__tests__/modules/appCatalog/ui/detail/CommunitySection.input.test.d.ts +0 -0
  4. package/dist/esm/__tests__/modules/appCatalog/ui/detail/composeComments.test.d.ts +1 -0
  5. package/dist/esm/__tests__/ui/detail/CommunityInput.test.d.ts +1 -0
  6. package/dist/esm/modules/appCatalog/hooks/useUpdateApp.d.ts +8 -0
  7. package/dist/esm/modules/appCatalog/ui/catalog/AppDetailPanel.js +1 -0
  8. package/dist/esm/modules/appCatalog/ui/catalog/AppDetailPanel.js.map +1 -1
  9. package/dist/esm/modules/appCatalog/ui/detail/AppDetails.js +237 -218
  10. package/dist/esm/modules/appCatalog/ui/detail/AppDetails.js.map +1 -1
  11. package/dist/esm/modules/appCatalog/ui/detail/CommentsFeed.d.ts +17 -0
  12. package/dist/esm/modules/appCatalog/ui/detail/CommentsFeed.js +187 -0
  13. package/dist/esm/modules/appCatalog/ui/detail/CommentsFeed.js.map +1 -0
  14. package/dist/esm/modules/appCatalog/ui/detail/CommentsHelp.d.ts +9 -0
  15. package/dist/esm/modules/appCatalog/ui/detail/CommentsHelp.js +91 -0
  16. package/dist/esm/modules/appCatalog/ui/detail/CommentsHelp.js.map +1 -0
  17. package/dist/esm/modules/appCatalog/ui/detail/CommunityInput.d.ts +17 -0
  18. package/dist/esm/modules/appCatalog/ui/detail/CommunityInput.js +328 -0
  19. package/dist/esm/modules/appCatalog/ui/detail/CommunityInput.js.map +1 -0
  20. package/dist/esm/modules/appCatalog/ui/detail/CommunitySection.d.ts +15 -0
  21. package/dist/esm/modules/appCatalog/ui/detail/CommunitySection.js +26 -0
  22. package/dist/esm/modules/appCatalog/ui/detail/CommunitySection.js.map +1 -0
  23. package/dist/esm/modules/appCatalog/ui/detail/DetailTabs.d.ts +8 -19
  24. package/dist/esm/modules/appCatalog/ui/detail/DetailTabs.js +49 -37
  25. package/dist/esm/modules/appCatalog/ui/detail/DetailTabs.js.map +1 -1
  26. package/dist/esm/modules/appCatalog/ui/detail/communityDraft.d.ts +30 -0
  27. package/dist/esm/modules/appCatalog/ui/detail/communityDraft.js +50 -0
  28. package/dist/esm/modules/appCatalog/ui/detail/communityDraft.js.map +1 -0
  29. package/dist/esm/modules/appCatalog/ui/detail/communityError.d.ts +6 -0
  30. package/dist/esm/modules/appCatalog/ui/detail/communityError.js +8 -0
  31. package/dist/esm/modules/appCatalog/ui/detail/communityError.js.map +1 -0
  32. package/dist/esm/modules/appCatalog/ui/detail/composeComments.d.ts +27 -0
  33. package/dist/esm/modules/appCatalog/ui/detail/composeComments.js +66 -0
  34. package/dist/esm/modules/appCatalog/ui/detail/composeComments.js.map +1 -0
  35. package/dist/esm/modules/appCatalog/ui/detail/usePendingCount.d.ts +11 -0
  36. package/dist/esm/modules/appCatalog/ui/detail/usePendingCount.js +15 -0
  37. package/dist/esm/modules/appCatalog/ui/detail/usePendingCount.js.map +1 -0
  38. package/dist/esm/modules/extensions/features/resourceDetail.d.ts +38 -0
  39. package/dist/esm/modules/extensions/features/resourceDetail.js +4 -0
  40. package/dist/esm/modules/extensions/features/resourceDetail.js.map +1 -1
  41. package/dist/esm/modules/extensions/index.d.ts +1 -1
  42. package/dist/esm/modules/extensions/index.js.map +1 -1
  43. package/dist/index.css +501 -49
  44. package/package.json +4 -4
  45. package/src/__tests__/modules/appCatalog/ui/catalog/AppDetailPanel.appSwitch.test.tsx +5 -2
  46. package/src/__tests__/modules/appCatalog/ui/catalog/AppDetailPanel.enter.test.tsx +5 -2
  47. package/src/__tests__/modules/appCatalog/ui/detail/AppDetails.background.test.tsx +9 -10
  48. package/src/__tests__/modules/appCatalog/ui/detail/AppDetails.behindThisCard.test.tsx +402 -0
  49. package/src/__tests__/modules/appCatalog/ui/detail/AppDetails.doorAndTabs.test.tsx +8 -4
  50. package/src/__tests__/modules/appCatalog/ui/detail/AppDetails.escape.test.tsx +5 -2
  51. package/src/__tests__/modules/appCatalog/ui/detail/AppDetails.knowMore.test.tsx +175 -0
  52. package/src/__tests__/modules/appCatalog/ui/detail/AppDetails.sectionOrder.test.tsx +5 -2
  53. package/src/__tests__/modules/appCatalog/ui/detail/AppDetails.subResourceTab.test.tsx +5 -2
  54. package/src/__tests__/modules/appCatalog/ui/detail/AppDetails.tags.test.tsx +5 -2
  55. package/src/__tests__/modules/appCatalog/ui/detail/CommunitySection.feed.test.tsx +281 -0
  56. package/src/__tests__/modules/appCatalog/ui/detail/CommunitySection.help.test.tsx +179 -0
  57. package/src/__tests__/modules/appCatalog/ui/detail/CommunitySection.input.test.tsx +423 -0
  58. package/src/__tests__/modules/appCatalog/ui/detail/composeComments.test.ts +170 -0
  59. package/src/__tests__/ui/detail/CommunityInput.test.tsx +39 -0
  60. package/src/index.css +501 -49
  61. package/src/modules/appCatalog/ui/detail/AppDetails.tsx +292 -289
  62. package/src/modules/appCatalog/ui/detail/CommentsFeed.tsx +254 -0
  63. package/src/modules/appCatalog/ui/detail/CommentsHelp.tsx +102 -0
  64. package/src/modules/appCatalog/ui/detail/CommunityInput.tsx +419 -0
  65. package/src/modules/appCatalog/ui/detail/CommunitySection.tsx +41 -0
  66. package/src/modules/appCatalog/ui/detail/DetailTabs.tsx +77 -71
  67. package/src/modules/appCatalog/ui/detail/communityDraft.ts +80 -0
  68. package/src/modules/appCatalog/ui/detail/communityError.ts +10 -0
  69. package/src/modules/appCatalog/ui/detail/composeComments.ts +103 -0
  70. package/src/modules/appCatalog/ui/detail/usePendingCount.ts +26 -0
  71. package/src/modules/extensions/features/resourceDetail.ts +28 -0
  72. package/src/modules/extensions/index.ts +2 -0
  73. package/dist/esm/modules/appCatalog/ui/detail/FeedbackSection.d.ts +0 -13
  74. package/dist/esm/modules/appCatalog/ui/detail/FeedbackSection.js +0 -433
  75. package/dist/esm/modules/appCatalog/ui/detail/FeedbackSection.js.map +0 -1
  76. package/src/__tests__/modules/appCatalog/ui/detail/FeedbackSection.emptyState.test.tsx +0 -181
  77. package/src/__tests__/modules/appCatalog/ui/detail/FeedbackSection.reviewStatus.test.tsx +0 -131
  78. package/src/modules/appCatalog/ui/detail/FeedbackSection.tsx +0 -605
  79. /package/dist/esm/__tests__/modules/appCatalog/ui/detail/{FeedbackSection.emptyState.test.d.ts → AppDetails.behindThisCard.test.d.ts} +0 -0
  80. /package/dist/esm/__tests__/modules/appCatalog/ui/detail/{FeedbackSection.reviewStatus.test.d.ts → AppDetails.knowMore.test.d.ts} +0 -0
@@ -1,181 +0,0 @@
1
- import { render, screen } from '@testing-library/react'
2
- import userEvent from '@testing-library/user-event'
3
- import { describe, expect, it, vi } from 'vitest'
4
- import '@testing-library/jest-dom/vitest'
5
-
6
- // The section only needs the list query here; the whole tRPC/react-query stack is
7
- // stubbed down to that so the composer's own behaviour is the only thing under test.
8
- const state = vi.hoisted(() => ({
9
- items: [] as unknown[],
10
- sent: [] as unknown[],
11
- }))
12
-
13
- vi.mock('@tanstack/react-query', () => ({
14
- useQuery: vi.fn(() => ({
15
- data: { items: state.items, openCount: 0 },
16
- isLoading: false,
17
- })),
18
- // `mutate` runs the success callback, so the stub can represent a completed send
19
- // and not just an attempted one. Without that, anything the component does on
20
- // success — clearing the stored draft, closing the composer — is unreachable from
21
- // a test, and a fixture that cannot express success quietly stops guarding it.
22
- useMutation: vi.fn((options?: { onSuccess?: () => void }) => ({
23
- mutate: (vars: unknown) => {
24
- state.sent.push(vars)
25
- options?.onSuccess?.()
26
- },
27
- isPending: false,
28
- })),
29
- useQueryClient: vi.fn(() => ({ invalidateQueries: vi.fn() })),
30
- }))
31
- vi.mock('~/api/infra/trpc', () => ({
32
- useTRPC: vi.fn(() => ({
33
- feedback: {
34
- list: { queryOptions: () => ({ queryKey: ['feedback'] }) },
35
- add: { mutationOptions: (o: unknown) => o },
36
- edit: { mutationOptions: (o: unknown) => o },
37
- dismiss: { mutationOptions: (o: unknown) => o },
38
- },
39
- })),
40
- }))
41
-
42
- const { FeedbackSection } =
43
- await import('~/modules/appCatalog/ui/detail/FeedbackSection')
44
-
45
- describe('FeedbackSection — composer', () => {
46
- it('stays closed until something asks for it', () => {
47
- state.items = []
48
- render(<FeedbackSection appSlug="example-app" />)
49
-
50
- expect(screen.getByText(/Nothing here yet/)).toBeInTheDocument()
51
- expect(screen.queryByPlaceholderText('What should change?')).toBeNull()
52
- // The affordance lives in the card's band now, not in this panel — down here
53
- // it was below the fold of a tab, which is why it moved.
54
- expect(
55
- screen.queryByRole('button', { name: /Suggest a change/ }),
56
- ).toBeNull()
57
- })
58
-
59
- it('opens and takes the caret when the band asks', () => {
60
- state.items = []
61
- render(<FeedbackSection appSlug="example-app" openComposer={1} />)
62
-
63
- const field = screen.getByPlaceholderText('What should change?')
64
- expect(field).toBeInTheDocument()
65
- expect(field).toHaveFocus()
66
- })
67
-
68
- it('does not auto-open just because feedback exists', () => {
69
- state.items = [
70
- {
71
- id: 'c1',
72
- authorName: 'Curious Ferret',
73
- body: 'Useful.',
74
- subject: null,
75
- resourceSlug: 'example-app',
76
- resourceName: 'Example App',
77
- attachmentIds: [],
78
- createdAt: new Date().toISOString(),
79
- reviewedAt: null,
80
- editedAt: null,
81
- isMine: false,
82
- canEditUntil: null,
83
- status: null,
84
- reviewerReply: null,
85
- },
86
- ]
87
- render(<FeedbackSection appSlug="example-app" />)
88
-
89
- expect(screen.queryByPlaceholderText('What should change?')).toBeNull()
90
- })
91
-
92
- it('sends with nothing typed — a bare flag is a valid signal', async () => {
93
- state.items = []
94
- state.sent = []
95
- render(<FeedbackSection appSlug="example-app" openComposer={1} />)
96
-
97
- const send = screen.getByRole('button', { name: 'Send' })
98
- // The whole point: no words, no image, still submittable.
99
- expect(send).toBeEnabled()
100
-
101
- await userEvent.click(send)
102
- expect(state.sent).toEqual([{ resourceSlug: 'example-app', body: '' }])
103
- })
104
-
105
- /**
106
- * The detail card mounts this inside a tab panel, and a panel is unmounted while
107
- * its tab is inactive — so an unmount is a routine event here, not an edge case.
108
- * Component state alone would drop whatever someone had typed.
109
- */
110
- it('keeps a half-typed draft across an unmount, and reopens the composer for it', async () => {
111
- sessionStorage.clear()
112
- state.items = []
113
- const first = render(
114
- <FeedbackSection appSlug="example-app" openComposer={1} />,
115
- )
116
-
117
- await userEvent.type(
118
- screen.getByPlaceholderText('What should change?'),
119
- 'half a thought',
120
- )
121
-
122
- // Leaving the tab unmounts the panel.
123
- first.unmount()
124
- render(<FeedbackSection appSlug="example-app" />)
125
-
126
- const recovered = screen.getByPlaceholderText('What should change?')
127
- expect(recovered).toHaveValue('half a thought')
128
- })
129
-
130
- /**
131
- * The composer closes itself on send, so the band has to be able to say "again".
132
- * It used to pass a boolean, which cannot: the second click left the prop at
133
- * `true`, the effect did not re-run, and the button was dead for the rest of the
134
- * card's life — you could file one correction per entry and no more. Caught by
135
- * sending twice in a real browser, which is the only place it showed.
136
- */
137
- it('reopens for a second request after the first one was sent', async () => {
138
- sessionStorage.clear()
139
- state.items = []
140
- state.sent = []
141
- const view = render(
142
- <FeedbackSection appSlug="example-app" openComposer={1} />,
143
- )
144
-
145
- await userEvent.click(screen.getByRole('button', { name: 'Send' }))
146
- expect(screen.queryByPlaceholderText('What should change?')).toBeNull()
147
-
148
- // The band asking a second time — a new count, same requested tab.
149
- view.rerender(<FeedbackSection appSlug="example-app" openComposer={2} />)
150
- expect(
151
- screen.getByPlaceholderText('What should change?'),
152
- ).toBeInTheDocument()
153
- })
154
-
155
- it('does not leak a draft between entries, and clears it once sent', async () => {
156
- sessionStorage.clear()
157
- state.items = []
158
- state.sent = []
159
- const first = render(
160
- <FeedbackSection appSlug="example-app" openComposer={1} />,
161
- )
162
- await userEvent.type(
163
- screen.getByPlaceholderText('What should change?'),
164
- 'about this one',
165
- )
166
- first.unmount()
167
-
168
- // A different entry starts clean rather than inheriting the neighbour's draft.
169
- const other = render(<FeedbackSection appSlug="other-app" />)
170
- expect(screen.queryByPlaceholderText('What should change?')).toBeNull()
171
- other.unmount()
172
-
173
- // Back on the original, send it, and the stored draft goes with it.
174
- render(<FeedbackSection appSlug="example-app" />)
175
- await userEvent.click(screen.getByRole('button', { name: 'Send' }))
176
- expect(state.sent).toEqual([
177
- { resourceSlug: 'example-app', body: 'about this one' },
178
- ])
179
- expect(sessionStorage.getItem('ac.feedback.draft.example-app')).toBeNull()
180
- })
181
- })
@@ -1,131 +0,0 @@
1
- import { render, screen } from '@testing-library/react'
2
- import { describe, expect, it, vi } from 'vitest'
3
- import '@testing-library/jest-dom/vitest'
4
-
5
- // Same stub shape as the empty-state test: only the list query matters here.
6
- const state = vi.hoisted(() => ({
7
- items: [] as unknown[],
8
- openCount: 0,
9
- }))
10
-
11
- vi.mock('@tanstack/react-query', () => ({
12
- useQuery: vi.fn(() => ({
13
- data: { items: state.items, openCount: state.openCount },
14
- isLoading: false,
15
- })),
16
- useMutation: vi.fn(() => ({ mutate: vi.fn(), isPending: false })),
17
- useQueryClient: vi.fn(() => ({ invalidateQueries: vi.fn() })),
18
- }))
19
- vi.mock('~/api/infra/trpc', () => ({
20
- useTRPC: vi.fn(() => ({
21
- feedback: {
22
- list: { queryOptions: () => ({ queryKey: ['feedback'] }) },
23
- add: { mutationOptions: (o: unknown) => o },
24
- edit: { mutationOptions: (o: unknown) => o },
25
- dismiss: { mutationOptions: (o: unknown) => o },
26
- },
27
- })),
28
- }))
29
-
30
- const { FeedbackSection } =
31
- await import('~/modules/appCatalog/ui/detail/FeedbackSection')
32
-
33
- const base = {
34
- id: 'c1',
35
- authorName: 'Merry Mole',
36
- body: 'This portal only covers one business unit.',
37
- subject: null,
38
- resourceSlug: 'example-app',
39
- resourceName: 'Example App',
40
- attachmentIds: [] as string[],
41
- createdAt: new Date().toISOString(),
42
- reviewedAt: null as string | null,
43
- editedAt: null,
44
- isMine: false,
45
- canEditUntil: null,
46
- status: null as string | null,
47
- reviewerReply: null as string | null,
48
- }
49
-
50
- describe('FeedbackSection — review status', () => {
51
- it('credits applied work with a short label, not a sentence', () => {
52
- state.items = [{ ...base, status: 'applied' }]
53
- state.openCount = 0
54
- render(<FeedbackSection appSlug="example-app" />)
55
-
56
- // Exactly one: the chip on the row. There is no legend restating it, and the
57
- // gratitude lives in the reply prose rather than inside the pill.
58
- expect(screen.getAllByText('Applied')).toHaveLength(1)
59
- expect(screen.queryByText(/thank you!/)).toBeNull()
60
- expect(screen.queryByText(/means the catalog now carries/)).toBeNull()
61
- })
62
-
63
- it('shows the quieter label for feedback that changed nothing', () => {
64
- state.items = [{ ...base, status: 'acknowledged' }]
65
- render(<FeedbackSection appSlug="example-app" />)
66
-
67
- expect(screen.getAllByText('Seen')).toHaveLength(1)
68
- })
69
-
70
- it('renders the maintainer reply under the row', () => {
71
- state.items = [
72
- {
73
- ...base,
74
- status: 'applied',
75
- reviewerReply: 'Folded into Background — thank you.',
76
- },
77
- ]
78
- render(<FeedbackSection appSlug="example-app" />)
79
-
80
- expect(screen.getByText('Catalog team')).toBeInTheDocument()
81
- expect(
82
- screen.getByText('Folded into Background — thank you.'),
83
- ).toBeInTheDocument()
84
- })
85
-
86
- it('gives an unreviewed row no chip at all — absence is the signal', () => {
87
- state.items = [base]
88
- state.openCount = 1
89
- render(<FeedbackSection appSlug="example-app" />)
90
-
91
- expect(screen.queryByText('Applied')).toBeNull()
92
- expect(screen.queryByText('Seen')).toBeNull()
93
- expect(screen.queryByText('Open')).toBeNull()
94
- expect(screen.queryByText('Catalog team')).toBeNull()
95
- })
96
-
97
- it('counts unreviewed items on the heading, and hides the count at zero', () => {
98
- state.items = [base]
99
- state.openCount = 2
100
- const { unmount } = render(<FeedbackSection appSlug="example-app" />)
101
- expect(screen.getByLabelText('2 awaiting review')).toHaveTextContent(
102
- '2 open',
103
- )
104
- unmount()
105
-
106
- state.openCount = 0
107
- render(<FeedbackSection appSlug="example-app" />)
108
- expect(screen.queryByText(/open$/)).toBeNull()
109
- })
110
-
111
- it('offers Dismiss on your own unreviewed row, and not once it is answered', () => {
112
- state.items = [{ ...base, isMine: true }]
113
- state.openCount = 1
114
- const { unmount } = render(<FeedbackSection appSlug="example-app" />)
115
- // No edit window left, but withdrawing is still allowed while nobody has acted.
116
- expect(screen.getByRole('button', { name: 'Dismiss' })).toBeInTheDocument()
117
- expect(screen.queryByRole('button', { name: 'Edit' })).toBeNull()
118
- unmount()
119
-
120
- state.items = [{ ...base, isMine: true, status: 'applied' }]
121
- render(<FeedbackSection appSlug="example-app" />)
122
- expect(screen.queryByRole('button', { name: 'Dismiss' })).toBeNull()
123
- })
124
-
125
- it('says so when someone sent a bare flag with no words', () => {
126
- state.items = [{ ...base, body: null }]
127
- render(<FeedbackSection appSlug="example-app" />)
128
-
129
- expect(screen.getByText(/no detail given/)).toBeInTheDocument()
130
- })
131
- })