@shipfox/client-triggers 17.0.0 → 22.0.0

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 (32) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +41 -0
  3. package/dist/components/events-filter-bar.d.ts.map +1 -1
  4. package/dist/components/events-filter-bar.js +98 -94
  5. package/dist/components/events-filter-bar.js.map +1 -1
  6. package/dist/components/events-list-states.d.ts.map +1 -1
  7. package/dist/components/events-list-states.js +31 -35
  8. package/dist/components/events-list-states.js.map +1 -1
  9. package/dist/components/events-list.d.ts.map +1 -1
  10. package/dist/components/events-list.js +5 -3
  11. package/dist/components/events-list.js.map +1 -1
  12. package/dist/components/trigger-event-detail.d.ts.map +1 -1
  13. package/dist/components/trigger-event-detail.js +173 -157
  14. package/dist/components/trigger-event-detail.js.map +1 -1
  15. package/dist/pages/events-page.d.ts.map +1 -1
  16. package/dist/pages/events-page.js +29 -47
  17. package/dist/pages/events-page.js.map +1 -1
  18. package/dist/routes/events-settings.d.ts +3 -0
  19. package/dist/routes/events-settings.js +3 -0
  20. package/dist/routes/events-settings.js.map +1 -1
  21. package/dist/tsconfig.test.tsbuildinfo +1 -1
  22. package/package.json +6 -6
  23. package/src/components/events-filter-bar.tsx +80 -77
  24. package/src/components/events-list-states.tsx +32 -30
  25. package/src/components/events-list.test.tsx +9 -0
  26. package/src/components/events-list.tsx +6 -3
  27. package/src/components/trigger-event-detail.test.tsx +3 -0
  28. package/src/components/trigger-event-detail.tsx +129 -126
  29. package/src/pages/events-page.test.tsx +7 -0
  30. package/src/pages/events-page.tsx +1 -15
  31. package/src/routes/events-settings.tsx +1 -0
  32. package/tsconfig.build.tsbuildinfo +1 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shipfox/client-triggers",
3
3
  "license": "MIT",
4
- "version": "17.0.0",
4
+ "version": "22.0.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
@@ -28,11 +28,11 @@
28
28
  "@swc/helpers": "^0.5.17",
29
29
  "@shipfox/api-triggers-dto": "9.0.2",
30
30
  "@shipfox/client-api": "6.0.1",
31
- "@shipfox/client-projects": "17.0.0",
32
- "@shipfox/client-shell": "17.0.0",
33
- "@shipfox/client-ui": "17.0.0",
34
- "@shipfox/integration-icons": "0.3.3",
35
- "@shipfox/react-ui": "1.2.0"
31
+ "@shipfox/client-projects": "22.0.0",
32
+ "@shipfox/client-shell": "22.0.0",
33
+ "@shipfox/client-ui": "22.0.0",
34
+ "@shipfox/integration-icons": "0.3.5",
35
+ "@shipfox/react-ui": "2.1.0"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@tanstack/react-query": "^5.101.0",
@@ -2,6 +2,7 @@ import {Button} from '@shipfox/react-ui/button';
2
2
  import {Collapsible, CollapsibleContent, CollapsibleTrigger} from '@shipfox/react-ui/collapsible';
3
3
  import {Combobox, type ComboboxOption} from '@shipfox/react-ui/combobox';
4
4
  import {type DateRange, DateRangePicker} from '@shipfox/react-ui/date-range-picker';
5
+ import {PanelHeader} from '@shipfox/react-ui/panel';
5
6
  import {Text} from '@shipfox/react-ui/typography';
6
7
  import {cn} from '@shipfox/react-ui/utils';
7
8
  import {type ReactNode, useState} from 'react';
@@ -95,84 +96,86 @@ export function EventsFilterBar({
95
96
  }
96
97
 
97
98
  return (
98
- <Collapsible open={open} onOpenChange={setOpen}>
99
- <div className="flex items-center justify-between gap-inline border-b border-border-neutral-base px-row py-row">
100
- <CollapsibleTrigger asChild>
101
- <Button
102
- type="button"
103
- variant="secondary"
104
- size="sm"
105
- iconLeft="filter3Line"
106
- iconRight={open ? 'arrowUpSLine' : 'arrowDownSLine'}
107
- >
108
- {activeCount > 0 ? `Filters (${activeCount})` : 'Filters'}
109
- </Button>
110
- </CollapsibleTrigger>
111
- {hasActiveFilters ? (
112
- <Button type="button" size="2xs" variant="transparentMuted" onClick={onClear}>
113
- Clear filters
114
- </Button>
115
- ) : null}
116
- </div>
117
- <CollapsibleContent>
118
- <div className="grid grid-cols-1 gap-x-cluster gap-y-inline border-b border-border-neutral-base px-row py-row min-[520px]:grid-cols-2">
119
- <FilterField label="Date" className="col-span-full">
120
- <DateRangePicker
121
- size="small"
122
- className="w-full"
123
- {...(dateRange ? {dateRange} : {})}
124
- onDateRangeSelect={handleDateRange}
125
- onClear={() => onFiltersChange({from: undefined, to: undefined})}
126
- placeholder="Any date"
127
- />
128
- </FilterField>
129
- <FilterField label="Result">
130
- <Combobox
131
- multiple
132
- size="small"
133
- aria-label="Filter by result"
134
- options={RESULT_OPTIONS}
135
- value={resultValue}
136
- onValueChange={handleResultValue}
137
- placeholder="All results"
138
- emptyState="No results"
139
- className="w-full"
140
- maxVisibleChips={2}
141
- />
142
- </FilterField>
143
- <FilterField label="Source">
144
- <Combobox
145
- multiple
146
- size="small"
147
- options={toOptions(sources)}
148
- value={filters.source ?? []}
149
- onValueChange={(value) =>
150
- onFiltersChange({source: value.length > 0 ? value : undefined})
151
- }
152
- placeholder="All sources"
153
- emptyState="No sources yet"
154
- className="w-full"
155
- maxVisibleChips={2}
156
- />
157
- </FilterField>
158
- <FilterField label="Event">
159
- <Combobox
160
- multiple
161
- size="small"
162
- options={toOptions(events)}
163
- value={filters.event ?? []}
164
- onValueChange={(value) =>
165
- onFiltersChange({event: value.length > 0 ? value : undefined})
166
- }
167
- placeholder="All events"
168
- emptyState="No events yet"
169
- className="w-full"
170
- maxVisibleChips={2}
171
- />
172
- </FilterField>
99
+ <PanelHeader className="min-h-48 flex-col items-stretch gap-0 p-0">
100
+ <Collapsible open={open} onOpenChange={setOpen}>
101
+ <div className="flex items-center justify-between gap-inline px-row py-row">
102
+ <CollapsibleTrigger asChild>
103
+ <Button
104
+ type="button"
105
+ variant="secondary"
106
+ size="sm"
107
+ iconLeft="filter3Line"
108
+ iconRight={open ? 'arrowUpSLine' : 'arrowDownSLine'}
109
+ >
110
+ {activeCount > 0 ? `Filters (${activeCount})` : 'Filters'}
111
+ </Button>
112
+ </CollapsibleTrigger>
113
+ {hasActiveFilters ? (
114
+ <Button type="button" size="2xs" variant="transparentMuted" onClick={onClear}>
115
+ Clear filters
116
+ </Button>
117
+ ) : null}
173
118
  </div>
174
- </CollapsibleContent>
175
- </Collapsible>
119
+ <CollapsibleContent>
120
+ <div className="grid grid-cols-1 gap-x-cluster gap-y-inline px-row py-row min-[520px]:grid-cols-2">
121
+ <FilterField label="Date" className="col-span-full">
122
+ <DateRangePicker
123
+ size="small"
124
+ className="w-full"
125
+ {...(dateRange ? {dateRange} : {})}
126
+ onDateRangeSelect={handleDateRange}
127
+ onClear={() => onFiltersChange({from: undefined, to: undefined})}
128
+ placeholder="Any date"
129
+ />
130
+ </FilterField>
131
+ <FilterField label="Result">
132
+ <Combobox
133
+ multiple
134
+ size="small"
135
+ aria-label="Filter by result"
136
+ options={RESULT_OPTIONS}
137
+ value={resultValue}
138
+ onValueChange={handleResultValue}
139
+ placeholder="All results"
140
+ emptyState="No results"
141
+ className="w-full"
142
+ maxVisibleChips={2}
143
+ />
144
+ </FilterField>
145
+ <FilterField label="Source">
146
+ <Combobox
147
+ multiple
148
+ size="small"
149
+ options={toOptions(sources)}
150
+ value={filters.source ?? []}
151
+ onValueChange={(value) =>
152
+ onFiltersChange({source: value.length > 0 ? value : undefined})
153
+ }
154
+ placeholder="All sources"
155
+ emptyState="No sources yet"
156
+ className="w-full"
157
+ maxVisibleChips={2}
158
+ />
159
+ </FilterField>
160
+ <FilterField label="Event">
161
+ <Combobox
162
+ multiple
163
+ size="small"
164
+ options={toOptions(events)}
165
+ value={filters.event ?? []}
166
+ onValueChange={(value) =>
167
+ onFiltersChange({event: value.length > 0 ? value : undefined})
168
+ }
169
+ placeholder="All events"
170
+ emptyState="No events yet"
171
+ className="w-full"
172
+ maxVisibleChips={2}
173
+ />
174
+ </FilterField>
175
+ </div>
176
+ </CollapsibleContent>
177
+ </Collapsible>
178
+ </PanelHeader>
176
179
  );
177
180
  }
178
181
 
@@ -8,12 +8,16 @@ import {Link} from '@tanstack/react-router';
8
8
 
9
9
  export function EventsListSkeleton() {
10
10
  return (
11
- <div className="flex flex-col gap-inline p-tight" role="status" aria-label="Loading events">
11
+ <div
12
+ className="flex flex-col divide-y divide-border-neutral-base"
13
+ role="status"
14
+ aria-label="Loading events"
15
+ >
12
16
  {Array.from({length: 8}).map((_, index) => (
13
17
  <div
14
18
  // biome-ignore lint/suspicious/noArrayIndexKey: skeleton row, stable position
15
19
  key={index}
16
- className="flex h-20 items-center gap-cluster"
20
+ className="flex min-h-44 items-center gap-cluster px-row py-row"
17
21
  >
18
22
  <Skeleton className="size-6 shrink-0 rounded-full" />
19
23
  <Skeleton className="h-12 w-1/4" />
@@ -27,39 +31,37 @@ export function EventsListSkeleton() {
27
31
 
28
32
  export function EventsListEmpty({workspaceSlug}: {workspaceSlug?: string | undefined}) {
29
33
  return (
30
- <div className="p-panel-compact">
31
- <EmptyState
32
- icon="pulseLine"
33
- title="No events yet"
34
- description="Events appear here once a connected integration delivers one or you fire a trigger."
35
- action={
36
- workspaceSlug ? (
37
- <Button asChild size="sm" variant="secondary">
38
- <Link to="/w/$workspaceSlug/settings/integrations" params={{workspaceSlug}}>
39
- Configure integrations
40
- </Link>
41
- </Button>
42
- ) : undefined
43
- }
44
- />
45
- </div>
34
+ <EmptyState
35
+ icon="pulseLine"
36
+ title="No events yet"
37
+ description="Events appear here once a connected integration delivers one or you fire a trigger."
38
+ action={
39
+ workspaceSlug ? (
40
+ <Button asChild size="sm" variant="secondary">
41
+ <Link to="/w/$workspaceSlug/settings/integrations" params={{workspaceSlug}}>
42
+ Configure integrations
43
+ </Link>
44
+ </Button>
45
+ ) : undefined
46
+ }
47
+ variant="panel"
48
+ />
46
49
  );
47
50
  }
48
51
 
49
52
  export function EventsListNoMatches({onClear}: {onClear: () => void}) {
50
53
  return (
51
- <div className="p-panel-compact">
52
- <EmptyState
53
- icon="filterOffLine"
54
- title="No matching events"
55
- description="No events match your current filters."
56
- action={
57
- <Button type="button" size="sm" variant="secondary" onClick={onClear}>
58
- Clear filters
59
- </Button>
60
- }
61
- />
62
- </div>
54
+ <EmptyState
55
+ icon="filterOffLine"
56
+ title="No matching events"
57
+ description="No events match your current filters."
58
+ action={
59
+ <Button type="button" size="sm" variant="secondary" onClick={onClear}>
60
+ Clear filters
61
+ </Button>
62
+ }
63
+ variant="panel"
64
+ />
63
65
  );
64
66
  }
65
67
 
@@ -72,6 +72,15 @@ async function selectResult(label: string) {
72
72
  }
73
73
 
74
74
  describe('EventsList', () => {
75
+ test('renders the event list as a panel with a header strip', () => {
76
+ renderList(makeProps());
77
+
78
+ const panel = document.querySelector('[data-slot="panel"]');
79
+
80
+ expect(panel).toBeInTheDocument();
81
+ expect(panel?.querySelector('[data-slot="panel-header"]')).toBeInTheDocument();
82
+ });
83
+
75
84
  test('renders a row per event with its match summary', () => {
76
85
  renderList(makeProps());
77
86
 
@@ -1,5 +1,6 @@
1
1
  import {QueryLoadError} from '@shipfox/client-ui';
2
2
  import {Button} from '@shipfox/react-ui/button';
3
+ import {Panel} from '@shipfox/react-ui/panel';
3
4
  import {Table, TableBody, TableHead, TableHeader, TableRow} from '@shipfox/react-ui/table';
4
5
  import {hasTriggerEventFilters} from '#core/trigger-event.js';
5
6
  import {EventsFilterBar} from './events-filter-bar.js';
@@ -40,7 +41,7 @@ export function EventsList({
40
41
  }
41
42
 
42
43
  return (
43
- <div className="flex min-h-0 flex-col overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base">
44
+ <Panel className="min-h-0 overflow-hidden p-0">
44
45
  <EventsFilterBar
45
46
  filters={filters}
46
47
  onFiltersChange={onFiltersChange}
@@ -51,7 +52,9 @@ export function EventsList({
51
52
  />
52
53
 
53
54
  {query.isPending ? <EventsListSkeleton /> : null}
54
- {!query.isPending ? <QueryLoadError query={query} subject="events" icon="pulseLine" /> : null}
55
+ {!query.isPending ? (
56
+ <QueryLoadError query={query} subject="events" icon="pulseLine" variant="panel" />
57
+ ) : null}
55
58
  {!query.isPending && refreshFailed ? <EventsListStaleError query={query} /> : null}
56
59
  {showEmptyState && !activeFilters ? <EventsListEmpty workspaceSlug={workspaceSlug} /> : null}
57
60
  {showEmptyState && activeFilters ? <EventsListNoMatches onClear={clearFilters} /> : null}
@@ -92,6 +95,6 @@ export function EventsList({
92
95
  ) : null}
93
96
  </>
94
97
  ) : null}
95
- </div>
98
+ </Panel>
96
99
  );
97
100
  }
@@ -103,6 +103,9 @@ describe('TriggerEventDetailView', () => {
103
103
  renderDetailView(makeEvent());
104
104
 
105
105
  expect(await screen.findByText('Triggered 1 workflow')).toBeInTheDocument();
106
+ expect(screen.getByRole('heading', {name: 'Matched workflows'})).toBeInTheDocument();
107
+ expect(screen.getByRole('heading', {name: 'Payload'})).toBeInTheDocument();
108
+ expect(document.querySelectorAll('[data-slot="panel"]')).toHaveLength(2);
106
109
  expect(screen.getByText('Deploy production')).toBeInTheDocument();
107
110
  expect(screen.getByRole('link', {name: DEPLOY_RUN_LINK_NAME})).toHaveAttribute(
108
111
  'href',