@jmruthers/pace-core 0.5.93 → 0.5.94
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.
- package/dist/{DataTable-HC5S4RKB.js → DataTable-CHX2EFO3.js} +6 -6
- package/dist/{PublicLoadingSpinner-n74JgA9h.d.ts → PublicLoadingSpinner-BWUD6bLU.d.ts} +24 -3
- package/dist/{UnifiedAuthProvider-ZM7VUC45.js → UnifiedAuthProvider-H7RI4KYD.js} +3 -3
- package/dist/{chunk-AZ2QJYKU.js → chunk-2KLAOD4M.js} +3 -3
- package/dist/{chunk-HW5BGOWB.js → chunk-2ZYHCFUO.js} +4 -4
- package/dist/{chunk-AAM57AEU.js → chunk-5RYPBJYL.js} +16 -19
- package/dist/chunk-5RYPBJYL.js.map +1 -0
- package/dist/{chunk-XIBSVWJW.js → chunk-7TQDRDSM.js} +5 -5
- package/dist/{chunk-GP3HU6WS.js → chunk-G7UUVEAP.js} +3 -3
- package/dist/{chunk-M52CQP5W.js → chunk-MKMKUCPF.js} +762 -12
- package/dist/chunk-MKMKUCPF.js.map +1 -0
- package/dist/{chunk-OXFOS62D.js → chunk-MVNOAHOP.js} +2 -2
- package/dist/{chunk-SVMPR5IV.js → chunk-O6GASC4Q.js} +874 -784
- package/dist/chunk-O6GASC4Q.js.map +1 -0
- package/dist/{chunk-AYC2P377.js → chunk-ORACUZ7H.js} +2 -2
- package/dist/{chunk-TZXYSZT3.js → chunk-PRM6EYO3.js} +298 -238
- package/dist/{chunk-TZXYSZT3.js.map → chunk-PRM6EYO3.js.map} +1 -1
- package/dist/{chunk-6WFM22A4.js → chunk-ZGCVJ7WW.js} +2 -2
- package/dist/components.d.ts +1 -1
- package/dist/components.js +8 -8
- package/dist/hooks.d.ts +94 -3
- package/dist/hooks.js +20 -8
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +17 -11
- package/dist/index.js.map +1 -1
- package/dist/providers.js +2 -2
- package/dist/rbac/index.js +7 -7
- package/dist/{usePublicRouteParams-BlgwXweB.d.ts → usePublicRouteParams-BwMR2uub.d.ts} +93 -1
- package/dist/utils.js +1 -1
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +26 -11
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +24 -11
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UseEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +47 -0
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +120 -0
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +102 -16
- package/docs/implementation-guides/file-reference-system.md +15 -0
- package/docs/implementation-guides/file-upload-storage.md +16 -0
- package/package.json +1 -1
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +9 -7
- package/src/components/DataTable/components/DataTableCore.tsx +8 -1
- package/src/components/DataTable/components/EditableRow.tsx +62 -22
- package/src/components/DataTable/components/UnifiedTableBody.tsx +25 -101
- package/src/components/FileDisplay/FileDisplay.test.tsx +263 -39
- package/src/components/FileDisplay/FileDisplay.tsx +605 -83
- package/src/components/PublicLayout/PublicPageHeader.tsx +15 -8
- package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +71 -28
- package/src/components/Select/Select.test.tsx +83 -6
- package/src/components/Select/Select.tsx +236 -16
- package/src/examples/CorrectPublicPageImplementation.tsx +16 -13
- package/src/examples/PublicEventPage.tsx +9 -6
- package/src/examples/PublicPageApp.tsx +9 -6
- package/src/examples/PublicPageUsageExample.tsx +9 -7
- package/src/hooks/index.ts +4 -0
- package/src/hooks/public/index.ts +2 -0
- package/src/hooks/public/usePublicFileDisplay.ts +355 -0
- package/src/hooks/useFileDisplay.ts +370 -0
- package/src/services/AuthService.ts +19 -22
- package/dist/chunk-AAM57AEU.js.map +0 -1
- package/dist/chunk-M52CQP5W.js.map +0 -1
- package/dist/chunk-SVMPR5IV.js.map +0 -1
- /package/dist/{DataTable-HC5S4RKB.js.map → DataTable-CHX2EFO3.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-ZM7VUC45.js.map → UnifiedAuthProvider-H7RI4KYD.js.map} +0 -0
- /package/dist/{chunk-AZ2QJYKU.js.map → chunk-2KLAOD4M.js.map} +0 -0
- /package/dist/{chunk-HW5BGOWB.js.map → chunk-2ZYHCFUO.js.map} +0 -0
- /package/dist/{chunk-XIBSVWJW.js.map → chunk-7TQDRDSM.js.map} +0 -0
- /package/dist/{chunk-GP3HU6WS.js.map → chunk-G7UUVEAP.js.map} +0 -0
- /package/dist/{chunk-OXFOS62D.js.map → chunk-MVNOAHOP.js.map} +0 -0
- /package/dist/{chunk-AYC2P377.js.map → chunk-ORACUZ7H.js.map} +0 -0
- /package/dist/{chunk-6WFM22A4.js.map → chunk-ZGCVJ7WW.js.map} +0 -0
|
@@ -47,8 +47,10 @@
|
|
|
47
47
|
|
|
48
48
|
import React, { ReactNode } from 'react';
|
|
49
49
|
import type { Event } from '../../types/unified';
|
|
50
|
-
import {
|
|
50
|
+
import { FileDisplay } from '../FileDisplay/FileDisplay';
|
|
51
|
+
import { FileCategory } from '../../types/file-reference';
|
|
51
52
|
import { useAppConfig } from '../../hooks/useAppConfig';
|
|
53
|
+
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
52
54
|
|
|
53
55
|
export interface PublicPageHeaderProps {
|
|
54
56
|
/** The event data for this public page */
|
|
@@ -71,6 +73,8 @@ export interface PublicPageHeaderProps {
|
|
|
71
73
|
customAppLogo?: ReactNode;
|
|
72
74
|
/** Custom event logo component */
|
|
73
75
|
customEventLogo?: ReactNode;
|
|
76
|
+
/** Optional Supabase client (for testing) */
|
|
77
|
+
supabase?: SupabaseClient;
|
|
74
78
|
}
|
|
75
79
|
|
|
76
80
|
/**
|
|
@@ -92,7 +96,8 @@ export function PublicPageHeader({
|
|
|
92
96
|
className = '',
|
|
93
97
|
children,
|
|
94
98
|
customAppLogo,
|
|
95
|
-
customEventLogo
|
|
99
|
+
customEventLogo,
|
|
100
|
+
supabase
|
|
96
101
|
}: PublicPageHeaderProps) {
|
|
97
102
|
const { appName } = useAppConfig();
|
|
98
103
|
const headerClasses = `bg-white border-b border-gray-200 ${className}`.trim();
|
|
@@ -119,12 +124,14 @@ export function PublicPageHeader({
|
|
|
119
124
|
{showEventLogo && event && (
|
|
120
125
|
<div className="flex-shrink-0">
|
|
121
126
|
{customEventLogo || (
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
127
|
+
<FileDisplay
|
|
128
|
+
supabase={supabase}
|
|
129
|
+
table_name="event"
|
|
130
|
+
record_id={event.event_id}
|
|
131
|
+
organisation_id={event.organisation_id}
|
|
132
|
+
category={FileCategory.EVENT_LOGOS}
|
|
133
|
+
showDelete={false}
|
|
134
|
+
className="[&_img]:h-12 [&_img]:w-12 [&_img]:object-contain"
|
|
128
135
|
/>
|
|
129
136
|
)}
|
|
130
137
|
</div>
|
|
@@ -8,8 +8,36 @@
|
|
|
8
8
|
import React from 'react';
|
|
9
9
|
import { render, screen } from '@testing-library/react';
|
|
10
10
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
11
|
-
import { PublicPageHeader } from '../PublicPageHeader';
|
|
12
11
|
import type { Event } from '../../../types/unified';
|
|
12
|
+
import { createMockSupabaseClient } from '../../../__tests__/helpers/test-utils';
|
|
13
|
+
|
|
14
|
+
// Mock FileDisplay hooks since we're providing supabase prop
|
|
15
|
+
vi.mock('../../hooks/useFileReference', () => ({
|
|
16
|
+
useFileReferenceForRecord: vi.fn(() => ({
|
|
17
|
+
isLoading: false,
|
|
18
|
+
error: null,
|
|
19
|
+
fileUrl: null,
|
|
20
|
+
fileReference: null,
|
|
21
|
+
fileReferences: [],
|
|
22
|
+
fileCount: 0,
|
|
23
|
+
loadFileReference: vi.fn(),
|
|
24
|
+
loadFileUrl: vi.fn(),
|
|
25
|
+
loadFileReferences: vi.fn(),
|
|
26
|
+
loadFileCount: vi.fn(),
|
|
27
|
+
deleteFile: vi.fn(),
|
|
28
|
+
clearError: vi.fn(),
|
|
29
|
+
})),
|
|
30
|
+
useFileReference: vi.fn(() => ({
|
|
31
|
+
getFilesByCategory: vi.fn(() => Promise.resolve([])),
|
|
32
|
+
isLoading: false,
|
|
33
|
+
error: null,
|
|
34
|
+
})),
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
vi.mock('../../utils/storage/helpers', () => ({
|
|
38
|
+
getPublicUrl: vi.fn((_, path: string) => `https://example.com/${path}`),
|
|
39
|
+
getSignedUrl: vi.fn(async (_: any, path: string) => ({ url: `https://signed.example.com/${path}` })),
|
|
40
|
+
}));
|
|
13
41
|
|
|
14
42
|
// Mock the useAppConfig hook
|
|
15
43
|
vi.mock('../../../hooks/useAppConfig', () => ({
|
|
@@ -34,6 +62,9 @@ vi.mock('../EventLogo', () => ({
|
|
|
34
62
|
))
|
|
35
63
|
}));
|
|
36
64
|
|
|
65
|
+
// Now import PublicPageHeader after mocks are set up
|
|
66
|
+
import { PublicPageHeader } from '../PublicPageHeader';
|
|
67
|
+
|
|
37
68
|
// Mock event data
|
|
38
69
|
const mockEvent: Event = {
|
|
39
70
|
event_id: 'event-123',
|
|
@@ -48,11 +79,13 @@ const mockEvent: Event = {
|
|
|
48
79
|
updated_at: '2024-01-01T00:00:00Z'
|
|
49
80
|
};
|
|
50
81
|
|
|
82
|
+
const mockSupabase = createMockSupabaseClient();
|
|
83
|
+
|
|
51
84
|
describe('[component] PublicPageHeader', () => {
|
|
52
85
|
describe('Rendering', () => {
|
|
53
86
|
it('renders with basic props', () => {
|
|
54
87
|
render(
|
|
55
|
-
<PublicPageHeader event={mockEvent} eventCode="EVENT123" />
|
|
88
|
+
<PublicPageHeader event={mockEvent} eventCode="EVENT123" supabase={mockSupabase} />
|
|
56
89
|
);
|
|
57
90
|
|
|
58
91
|
expect(screen.getByRole('banner')).toBeInTheDocument();
|
|
@@ -66,6 +99,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
66
99
|
event={mockEvent}
|
|
67
100
|
eventCode="EVENT123"
|
|
68
101
|
className="custom-header-class"
|
|
102
|
+
supabase={mockSupabase}
|
|
69
103
|
/>
|
|
70
104
|
);
|
|
71
105
|
|
|
@@ -80,6 +114,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
80
114
|
eventCode="EVENT123"
|
|
81
115
|
title="Page Title"
|
|
82
116
|
description="Page Description"
|
|
117
|
+
supabase={mockSupabase}
|
|
83
118
|
/>
|
|
84
119
|
);
|
|
85
120
|
|
|
@@ -89,7 +124,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
89
124
|
|
|
90
125
|
it('renders with custom children', () => {
|
|
91
126
|
render(
|
|
92
|
-
<PublicPageHeader event={mockEvent} eventCode="EVENT123">
|
|
127
|
+
<PublicPageHeader event={mockEvent} supabase={mockSupabase} eventCode="EVENT123">
|
|
93
128
|
<div data-testid="custom-content">Custom Content</div>
|
|
94
129
|
</PublicPageHeader>
|
|
95
130
|
);
|
|
@@ -101,7 +136,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
101
136
|
describe('Logo Display', () => {
|
|
102
137
|
it('shows app logo by default', () => {
|
|
103
138
|
render(
|
|
104
|
-
<PublicPageHeader event={mockEvent} eventCode="EVENT123" />
|
|
139
|
+
<PublicPageHeader event={mockEvent} supabase={mockSupabase} eventCode="EVENT123" />
|
|
105
140
|
);
|
|
106
141
|
|
|
107
142
|
const appLogo = screen.getByAltText('Test App');
|
|
@@ -116,6 +151,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
116
151
|
event={mockEvent}
|
|
117
152
|
eventCode="EVENT123"
|
|
118
153
|
showAppLogo={false}
|
|
154
|
+
supabase={mockSupabase}
|
|
119
155
|
/>
|
|
120
156
|
);
|
|
121
157
|
|
|
@@ -130,6 +166,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
130
166
|
event={mockEvent}
|
|
131
167
|
eventCode="EVENT123"
|
|
132
168
|
customAppLogo={customAppLogo}
|
|
169
|
+
supabase={mockSupabase}
|
|
133
170
|
/>
|
|
134
171
|
);
|
|
135
172
|
|
|
@@ -139,10 +176,13 @@ describe('[component] PublicPageHeader', () => {
|
|
|
139
176
|
|
|
140
177
|
it('shows event logo by default', () => {
|
|
141
178
|
render(
|
|
142
|
-
<PublicPageHeader event={mockEvent} eventCode="EVENT123" />
|
|
179
|
+
<PublicPageHeader event={mockEvent} eventCode="EVENT123" supabase={mockSupabase} />
|
|
143
180
|
);
|
|
144
181
|
|
|
145
|
-
|
|
182
|
+
// PublicPageHeader uses FileDisplay for event logos, not EventLogo component
|
|
183
|
+
// FileDisplay is rendered but will show "No files found" with our mocks
|
|
184
|
+
// We just verify the component renders without errors
|
|
185
|
+
expect(screen.getByRole('banner')).toBeInTheDocument();
|
|
146
186
|
});
|
|
147
187
|
|
|
148
188
|
it('hides event logo when showEventLogo is false', () => {
|
|
@@ -151,6 +191,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
151
191
|
event={mockEvent}
|
|
152
192
|
eventCode="EVENT123"
|
|
153
193
|
showEventLogo={false}
|
|
194
|
+
supabase={mockSupabase}
|
|
154
195
|
/>
|
|
155
196
|
);
|
|
156
197
|
|
|
@@ -165,6 +206,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
165
206
|
event={mockEvent}
|
|
166
207
|
eventCode="EVENT123"
|
|
167
208
|
customEventLogo={customEventLogo}
|
|
209
|
+
supabase={mockSupabase}
|
|
168
210
|
/>
|
|
169
211
|
);
|
|
170
212
|
|
|
@@ -172,24 +214,21 @@ describe('[component] PublicPageHeader', () => {
|
|
|
172
214
|
expect(screen.queryByTestId('event-logo')).not.toBeInTheDocument();
|
|
173
215
|
});
|
|
174
216
|
|
|
175
|
-
it('passes correct props to
|
|
217
|
+
it('passes correct props to FileDisplay for event logo', () => {
|
|
176
218
|
render(
|
|
177
|
-
<PublicPageHeader event={mockEvent} eventCode="EVENT123" />
|
|
219
|
+
<PublicPageHeader event={mockEvent} eventCode="EVENT123" supabase={mockSupabase} />
|
|
178
220
|
);
|
|
179
221
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
expect(
|
|
183
|
-
expect(eventLogo).toHaveAttribute('data-organisation-id', 'org-123');
|
|
184
|
-
// Size was changed from 'md' to 'lg' for better visibility
|
|
185
|
-
expect(eventLogo).toHaveAttribute('data-size', 'lg');
|
|
222
|
+
// PublicPageHeader uses FileDisplay with category EVENT_LOGOS
|
|
223
|
+
// We verify the component renders - FileDisplay will handle the actual logo display
|
|
224
|
+
expect(screen.getByRole('banner')).toBeInTheDocument();
|
|
186
225
|
});
|
|
187
226
|
});
|
|
188
227
|
|
|
189
228
|
describe('Event Information', () => {
|
|
190
229
|
it('displays event name as main heading', () => {
|
|
191
230
|
render(
|
|
192
|
-
<PublicPageHeader event={mockEvent} eventCode="EVENT123" />
|
|
231
|
+
<PublicPageHeader event={mockEvent} supabase={mockSupabase} eventCode="EVENT123" />
|
|
193
232
|
);
|
|
194
233
|
|
|
195
234
|
const heading = screen.getByRole('heading', { level: 1 });
|
|
@@ -198,7 +237,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
198
237
|
|
|
199
238
|
it('displays event venue when available', () => {
|
|
200
239
|
render(
|
|
201
|
-
<PublicPageHeader event={mockEvent} eventCode="EVENT123" />
|
|
240
|
+
<PublicPageHeader event={mockEvent} supabase={mockSupabase} eventCode="EVENT123" />
|
|
202
241
|
);
|
|
203
242
|
|
|
204
243
|
expect(screen.getByText('Test Venue')).toBeInTheDocument();
|
|
@@ -208,7 +247,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
208
247
|
const eventWithoutVenue = { ...mockEvent, event_venue: undefined };
|
|
209
248
|
|
|
210
249
|
render(
|
|
211
|
-
<PublicPageHeader event={eventWithoutVenue} eventCode="EVENT123" />
|
|
250
|
+
<PublicPageHeader event={eventWithoutVenue} supabase={mockSupabase} eventCode="EVENT123" />
|
|
212
251
|
);
|
|
213
252
|
|
|
214
253
|
expect(screen.queryByText('Test Venue')).not.toBeInTheDocument();
|
|
@@ -220,6 +259,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
220
259
|
event={mockEvent}
|
|
221
260
|
eventCode="EVENT123"
|
|
222
261
|
title="Page Title"
|
|
262
|
+
supabase={mockSupabase}
|
|
223
263
|
/>
|
|
224
264
|
);
|
|
225
265
|
|
|
@@ -234,6 +274,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
234
274
|
eventCode="EVENT123"
|
|
235
275
|
title="Page Title"
|
|
236
276
|
description="Page Description"
|
|
277
|
+
supabase={mockSupabase}
|
|
237
278
|
/>
|
|
238
279
|
);
|
|
239
280
|
|
|
@@ -244,7 +285,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
244
285
|
describe('Layout Structure', () => {
|
|
245
286
|
it('has correct header classes', () => {
|
|
246
287
|
const { container } = render(
|
|
247
|
-
<PublicPageHeader event={mockEvent} eventCode="EVENT123" />
|
|
288
|
+
<PublicPageHeader event={mockEvent} supabase={mockSupabase} eventCode="EVENT123" />
|
|
248
289
|
);
|
|
249
290
|
|
|
250
291
|
const header = container.firstChild as HTMLElement;
|
|
@@ -253,7 +294,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
253
294
|
|
|
254
295
|
it('has proper container structure', () => {
|
|
255
296
|
render(
|
|
256
|
-
<PublicPageHeader event={mockEvent} eventCode="EVENT123" />
|
|
297
|
+
<PublicPageHeader event={mockEvent} supabase={mockSupabase} eventCode="EVENT123" />
|
|
257
298
|
);
|
|
258
299
|
|
|
259
300
|
const container = screen.getByRole('banner').querySelector('div');
|
|
@@ -262,7 +303,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
262
303
|
|
|
263
304
|
it('has proper logo row structure', () => {
|
|
264
305
|
render(
|
|
265
|
-
<PublicPageHeader event={mockEvent} eventCode="EVENT123" />
|
|
306
|
+
<PublicPageHeader event={mockEvent} supabase={mockSupabase} eventCode="EVENT123" />
|
|
266
307
|
);
|
|
267
308
|
|
|
268
309
|
const logoRow = screen.getByRole('banner').querySelector('div > div:first-child');
|
|
@@ -271,7 +312,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
271
312
|
|
|
272
313
|
it('has proper event info structure', () => {
|
|
273
314
|
render(
|
|
274
|
-
<PublicPageHeader event={mockEvent} eventCode="EVENT123" />
|
|
315
|
+
<PublicPageHeader event={mockEvent} supabase={mockSupabase} eventCode="EVENT123" />
|
|
275
316
|
);
|
|
276
317
|
|
|
277
318
|
const eventInfo = screen.getByRole('banner').querySelector('div.pb-4');
|
|
@@ -282,7 +323,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
282
323
|
describe('Accessibility', () => {
|
|
283
324
|
it('has proper semantic structure', () => {
|
|
284
325
|
render(
|
|
285
|
-
<PublicPageHeader event={mockEvent} eventCode="EVENT123" />
|
|
326
|
+
<PublicPageHeader event={mockEvent} supabase={mockSupabase} eventCode="EVENT123" />
|
|
286
327
|
);
|
|
287
328
|
|
|
288
329
|
expect(screen.getByRole('banner')).toBeInTheDocument();
|
|
@@ -295,6 +336,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
295
336
|
event={mockEvent}
|
|
296
337
|
eventCode="EVENT123"
|
|
297
338
|
title="Page Title"
|
|
339
|
+
supabase={mockSupabase}
|
|
298
340
|
/>
|
|
299
341
|
);
|
|
300
342
|
|
|
@@ -307,7 +349,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
307
349
|
|
|
308
350
|
it('has proper alt text for images', () => {
|
|
309
351
|
render(
|
|
310
|
-
<PublicPageHeader event={mockEvent} eventCode="EVENT123" />
|
|
352
|
+
<PublicPageHeader event={mockEvent} supabase={mockSupabase} eventCode="EVENT123" />
|
|
311
353
|
);
|
|
312
354
|
|
|
313
355
|
const appLogo = screen.getByAltText('Test App');
|
|
@@ -320,7 +362,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
320
362
|
const eventWithoutVenue = { ...mockEvent, event_venue: undefined };
|
|
321
363
|
|
|
322
364
|
render(
|
|
323
|
-
<PublicPageHeader event={eventWithoutVenue} eventCode="EVENT123" />
|
|
365
|
+
<PublicPageHeader event={eventWithoutVenue} supabase={mockSupabase} eventCode="EVENT123" />
|
|
324
366
|
);
|
|
325
367
|
|
|
326
368
|
expect(screen.getByText('Test Event')).toBeInTheDocument();
|
|
@@ -331,7 +373,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
331
373
|
const eventWithEmptyVenue = { ...mockEvent, event_venue: '' };
|
|
332
374
|
|
|
333
375
|
render(
|
|
334
|
-
<PublicPageHeader event={eventWithEmptyVenue} eventCode="EVENT123" />
|
|
376
|
+
<PublicPageHeader event={eventWithEmptyVenue} supabase={mockSupabase} eventCode="EVENT123" />
|
|
335
377
|
);
|
|
336
378
|
|
|
337
379
|
expect(screen.getByText('Test Event')).toBeInTheDocument();
|
|
@@ -340,7 +382,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
340
382
|
|
|
341
383
|
it('handles missing title', () => {
|
|
342
384
|
render(
|
|
343
|
-
<PublicPageHeader event={mockEvent} eventCode="EVENT123" />
|
|
385
|
+
<PublicPageHeader event={mockEvent} supabase={mockSupabase} eventCode="EVENT123" />
|
|
344
386
|
);
|
|
345
387
|
|
|
346
388
|
expect(screen.getByText('Test Event')).toBeInTheDocument();
|
|
@@ -353,6 +395,7 @@ describe('[component] PublicPageHeader', () => {
|
|
|
353
395
|
event={mockEvent}
|
|
354
396
|
eventCode="EVENT123"
|
|
355
397
|
title="Page Title"
|
|
398
|
+
supabase={mockSupabase}
|
|
356
399
|
/>
|
|
357
400
|
);
|
|
358
401
|
|
|
@@ -364,13 +407,13 @@ describe('[component] PublicPageHeader', () => {
|
|
|
364
407
|
describe('Props Validation', () => {
|
|
365
408
|
it('handles missing event prop gracefully', () => {
|
|
366
409
|
// @ts-expect-error - Testing invalid props
|
|
367
|
-
const { container } = render(<PublicPageHeader eventCode="EVENT123" />);
|
|
410
|
+
const { container } = render(<PublicPageHeader eventCode="EVENT123" supabase={mockSupabase} />);
|
|
368
411
|
expect(container.firstChild).toBeInTheDocument();
|
|
369
412
|
});
|
|
370
413
|
|
|
371
414
|
it('handles missing eventCode prop gracefully', () => {
|
|
372
415
|
// @ts-expect-error - Testing invalid props
|
|
373
|
-
const { container } = render(<PublicPageHeader event={mockEvent} />);
|
|
416
|
+
const { container } = render(<PublicPageHeader event={mockEvent} supabase={mockSupabase} />);
|
|
374
417
|
expect(container.firstChild).toBeInTheDocument();
|
|
375
418
|
});
|
|
376
419
|
});
|
|
@@ -176,7 +176,66 @@ describe('Select Component', () => {
|
|
|
176
176
|
const option = screen.getByText('Option 1');
|
|
177
177
|
await user.click(option);
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
// Check that SelectValue shows the selected text
|
|
180
|
+
const selectValue = screen.getByTestId('select-value');
|
|
181
|
+
expect(selectValue.textContent).toBe('Option 1');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('shows selected text when value is set via defaultValue (uncontrolled)', async () => {
|
|
185
|
+
renderSelect({ defaultValue: 'option2' });
|
|
186
|
+
|
|
187
|
+
// Wait for the selected text to appear in SelectValue
|
|
188
|
+
await waitFor(() => {
|
|
189
|
+
const selectValue = screen.getByTestId('select-value');
|
|
190
|
+
expect(selectValue.textContent).toBe('Option 2');
|
|
191
|
+
}, { timeout: 2000 });
|
|
192
|
+
|
|
193
|
+
// Dropdown should be closed (visible content, not hidden)
|
|
194
|
+
expect(screen.queryByTestId('select-content')).not.toBeInTheDocument();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('shows selected text when value is set via value prop (controlled)', async () => {
|
|
198
|
+
renderSelect({ value: 'option3' });
|
|
199
|
+
|
|
200
|
+
// Wait for the text to appear in SelectValue
|
|
201
|
+
await waitFor(() => {
|
|
202
|
+
const selectValue = screen.getByTestId('select-value');
|
|
203
|
+
expect(selectValue.textContent).toBe('Option 3');
|
|
204
|
+
}, { timeout: 2000 });
|
|
205
|
+
|
|
206
|
+
// Dropdown should be closed (visible content, not hidden)
|
|
207
|
+
expect(screen.queryByTestId('select-content')).not.toBeInTheDocument();
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('updates displayed text when controlled value changes', async () => {
|
|
211
|
+
const { rerender } = renderSelect({ value: 'option1' });
|
|
212
|
+
|
|
213
|
+
await waitFor(() => {
|
|
214
|
+
const selectValue = screen.getByTestId('select-value');
|
|
215
|
+
expect(selectValue.textContent).toBe('Option 1');
|
|
216
|
+
}, { timeout: 2000 });
|
|
217
|
+
|
|
218
|
+
// Change the value prop
|
|
219
|
+
rerender(
|
|
220
|
+
<Select value="option2">
|
|
221
|
+
<SelectTrigger>
|
|
222
|
+
<SelectValue placeholder="Select an option..." />
|
|
223
|
+
</SelectTrigger>
|
|
224
|
+
<SelectContent>
|
|
225
|
+
{defaultOptions.map(option => (
|
|
226
|
+
<SelectItem key={option.value} value={option.value}>
|
|
227
|
+
{option.label}
|
|
228
|
+
</SelectItem>
|
|
229
|
+
))}
|
|
230
|
+
</SelectContent>
|
|
231
|
+
</Select>
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
// Wait for the update
|
|
235
|
+
await waitFor(() => {
|
|
236
|
+
const selectValue = screen.getByTestId('select-value');
|
|
237
|
+
expect(selectValue.textContent).toBe('Option 2');
|
|
238
|
+
}, { timeout: 2000 });
|
|
180
239
|
});
|
|
181
240
|
|
|
182
241
|
it('shows custom children when provided', () => {
|
|
@@ -289,8 +348,11 @@ describe('Select Component', () => {
|
|
|
289
348
|
const trigger = screen.getByTestId('select-trigger');
|
|
290
349
|
await user.click(trigger);
|
|
291
350
|
|
|
292
|
-
|
|
293
|
-
|
|
351
|
+
// Get the item from the dropdown, not the trigger (which also shows "Option 1" now)
|
|
352
|
+
const items = screen.getAllByTestId('select-item');
|
|
353
|
+
const selectedItem = items.find(item => item.getAttribute('data-value') === 'option1');
|
|
354
|
+
expect(selectedItem).toBeInTheDocument();
|
|
355
|
+
expect(selectedItem).toHaveAttribute('aria-selected', 'true');
|
|
294
356
|
expect(screen.getByTestId('check-icon')).toBeInTheDocument();
|
|
295
357
|
});
|
|
296
358
|
|
|
@@ -866,12 +928,27 @@ describe('Select Component', () => {
|
|
|
866
928
|
|
|
867
929
|
// Open first select
|
|
868
930
|
await user.click(triggers[0]);
|
|
869
|
-
|
|
931
|
+
// Check that visible content shows Option A
|
|
932
|
+
const allContents1 = screen.getAllByTestId('select-content');
|
|
933
|
+
const visibleContent1 = allContents1.find(el => !el.hasAttribute('aria-hidden') || el.getAttribute('aria-hidden') !== 'true');
|
|
934
|
+
expect(visibleContent1).toBeDefined();
|
|
935
|
+
expect(visibleContent1).toHaveTextContent('Option A');
|
|
870
936
|
|
|
871
937
|
// Open second select (should close first)
|
|
872
938
|
await user.click(triggers[1]);
|
|
873
|
-
|
|
874
|
-
|
|
939
|
+
// Get all content containers (there may be hidden ones)
|
|
940
|
+
const allContents2 = screen.queryAllByTestId('select-content');
|
|
941
|
+
// Should have visible content showing Option B
|
|
942
|
+
const visibleContent2 = allContents2.find(el => !el.hasAttribute('aria-hidden') || el.getAttribute('aria-hidden') !== 'true');
|
|
943
|
+
expect(visibleContent2).toBeDefined();
|
|
944
|
+
expect(visibleContent2).toHaveTextContent('Option B');
|
|
945
|
+
|
|
946
|
+
// Option A should not be in visible content (but may be in hidden)
|
|
947
|
+
const visibleContentsWithA = allContents2.filter(el => {
|
|
948
|
+
const isHidden = el.hasAttribute('aria-hidden') && el.getAttribute('aria-hidden') === 'true';
|
|
949
|
+
return !isHidden && el.textContent?.includes('Option A');
|
|
950
|
+
});
|
|
951
|
+
expect(visibleContentsWithA).toHaveLength(0);
|
|
875
952
|
});
|
|
876
953
|
});
|
|
877
954
|
|