@flamingo-stack/openframe-frontend-core 0.0.675 → 0.0.676-2437.7317.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.
- package/dist/components/meeting-scheduler/booking-form.d.ts +17 -1
- package/dist/components/meeting-scheduler/booking-form.d.ts.map +1 -1
- package/dist/components/meeting-scheduler/index.cjs +38 -23
- package/dist/components/meeting-scheduler/index.cjs.map +1 -1
- package/dist/components/meeting-scheduler/index.d.ts +6 -5
- package/dist/components/meeting-scheduler/index.d.ts.map +1 -1
- package/dist/components/meeting-scheduler/index.js +23 -8
- package/dist/components/meeting-scheduler/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/meeting-scheduler-details-first.test.tsx +21 -0
- package/src/components/meeting-scheduler/booking-form.tsx +44 -12
- package/src/components/meeting-scheduler/index.tsx +6 -4
package/package.json
CHANGED
|
@@ -164,4 +164,25 @@ describe('HubSpotMeetingScheduler — which links fall back to HubSpot', () => {
|
|
|
164
164
|
expect(screen.queryByText(BOOKED_ON_HUBSPOT)).not.toBeInTheDocument();
|
|
165
165
|
expect(screen.queryByLabelText(/^Upload/)).not.toBeInTheDocument();
|
|
166
166
|
});
|
|
167
|
+
it('fieldCopy replaces a field label and placeholder, and derives the one it is not given', async () => {
|
|
168
|
+
availability.formFields = [];
|
|
169
|
+
render(
|
|
170
|
+
scheduler({
|
|
171
|
+
detailsFormProps: {
|
|
172
|
+
fieldCopy: {
|
|
173
|
+
email: { label: 'Business Email', placeholder: 'username@company.com' },
|
|
174
|
+
firstName: { label: 'Given Name' },
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
}),
|
|
178
|
+
);
|
|
179
|
+
const email = await screen.findByLabelText(/^Business Email/);
|
|
180
|
+
expect(email).toHaveAttribute('placeholder', 'username@company.com');
|
|
181
|
+
// A label alone still moves the placeholder the TYPE derives from it.
|
|
182
|
+
expect(screen.getByLabelText(/^Given Name/)).toHaveAttribute('placeholder', 'Enter Given Name');
|
|
183
|
+
// The overridden field keeps everything else: its own label is gone, and the
|
|
184
|
+
// untouched one is drawn exactly as the widget declares it.
|
|
185
|
+
expect(screen.queryByLabelText(/^Email/)).not.toBeInTheDocument();
|
|
186
|
+
expect(screen.getByLabelText(/^Last Name/)).toHaveAttribute('placeholder', 'Enter Last Name');
|
|
187
|
+
});
|
|
167
188
|
});
|
|
@@ -60,6 +60,21 @@ export type BookingFieldSpan = keyof typeof SPAN_CLASS;
|
|
|
60
60
|
|
|
61
61
|
export type BookingFieldRow = BookingFieldSlot[];
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Display copy a HOST overrides for ONE field, keyed by its name — a built-in
|
|
65
|
+
* (`email`) or a HubSpot-declared question. It replaces what is DRAWN and
|
|
66
|
+
* nothing else: the name the answer rides under, the validation and the
|
|
67
|
+
* schema's own messages are untouched, so a campaign page can ask for a
|
|
68
|
+
* "Business Email" without owning a second email rule.
|
|
69
|
+
*
|
|
70
|
+
* An overridden `label` also feeds the type's derived placeholder
|
|
71
|
+
* (`Enter <label>`), so overriding the label alone keeps the pair consistent.
|
|
72
|
+
*/
|
|
73
|
+
export interface BookingFieldCopy {
|
|
74
|
+
label?: string;
|
|
75
|
+
placeholder?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
63
78
|
/**
|
|
64
79
|
* A HOST-supplied consent row — the block the waitlist form draws for its SMS
|
|
65
80
|
* consent, here for "I agree to the Privacy Policy and to be contacted". It is
|
|
@@ -365,6 +380,8 @@ export interface BookingFormProps {
|
|
|
365
380
|
* appended full width. Both are deliberate — see `slotNode`/`unplacedFields`.
|
|
366
381
|
*/
|
|
367
382
|
fieldRows?: BookingFieldRow[];
|
|
383
|
+
/** Per-field display overrides, keyed by field name — see `BookingFieldCopy`. */
|
|
384
|
+
fieldCopy?: Record<string, BookingFieldCopy>;
|
|
368
385
|
/** Host-supplied consent row, rendered after the fields — see `BookingFormConsent`. */
|
|
369
386
|
consent?: BookingFormConsent;
|
|
370
387
|
isSubmitting: boolean;
|
|
@@ -404,6 +421,7 @@ export function BookingForm({
|
|
|
404
421
|
submitLabel,
|
|
405
422
|
footerNote,
|
|
406
423
|
fieldRows,
|
|
424
|
+
fieldCopy,
|
|
407
425
|
consent,
|
|
408
426
|
isSubmitting,
|
|
409
427
|
onSubmit,
|
|
@@ -546,18 +564,32 @@ export function BookingForm({
|
|
|
546
564
|
const renderField = (
|
|
547
565
|
field: ControlArgs['field'],
|
|
548
566
|
where: { id: string; registerName: string; error?: string },
|
|
549
|
-
): ReactNode =>
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
567
|
+
): ReactNode => {
|
|
568
|
+
// DISPLAY only: the host's copy replaces what this control draws, never the
|
|
569
|
+
// name the answer registers under, the validation, or the schema's messages.
|
|
570
|
+
const copy = fieldCopy?.[field.name];
|
|
571
|
+
const shown = copy
|
|
572
|
+
? { ...field, label: copy.label ?? field.label, placeholder: copy.placeholder ?? field.placeholder }
|
|
573
|
+
: field;
|
|
574
|
+
return (
|
|
575
|
+
<FieldWrapper
|
|
576
|
+
key={shown.name}
|
|
577
|
+
label={shown.label}
|
|
578
|
+
htmlFor={where.id}
|
|
579
|
+
required={shown.required}
|
|
580
|
+
error={where.error}
|
|
581
|
+
>
|
|
582
|
+
{FIELD_CONTROLS[shown.type]({
|
|
583
|
+
field: shown,
|
|
584
|
+
id: where.id,
|
|
585
|
+
registerName: where.registerName,
|
|
586
|
+
error: where.error,
|
|
587
|
+
register,
|
|
588
|
+
control,
|
|
589
|
+
})}
|
|
590
|
+
</FieldWrapper>
|
|
591
|
+
);
|
|
592
|
+
};
|
|
561
593
|
|
|
562
594
|
const builtInFields: Record<string, ReactNode> = Object.fromEntries(
|
|
563
595
|
BUILT_IN_BOOKING_FIELDS.map(field => [
|
|
@@ -147,11 +147,12 @@ export interface HubSpotMeetingSchedulerProps {
|
|
|
147
147
|
*/
|
|
148
148
|
detailsForm?: ComponentType<BookingFormProps>;
|
|
149
149
|
/**
|
|
150
|
-
* The DATA form of the same override: `fieldRows
|
|
151
|
-
* spread onto whichever form renders. Serialisable,
|
|
152
|
-
* can pass it across the RSC boundary where a component
|
|
150
|
+
* The DATA form of the same override: `fieldRows`, a host consent row and
|
|
151
|
+
* per-field display copy, spread onto whichever form renders. Serialisable,
|
|
152
|
+
* so a Server Component can pass it across the RSC boundary where a component
|
|
153
|
+
* cannot.
|
|
153
154
|
*/
|
|
154
|
-
detailsFormProps?: Pick<BookingFormProps, 'fieldRows' | 'consent'>;
|
|
155
|
+
detailsFormProps?: Pick<BookingFormProps, 'fieldRows' | 'consent' | 'fieldCopy'>;
|
|
155
156
|
/** Form rescue for the details form (`RESCUE_FORMS.meetingBooking`). OPT-IN:
|
|
156
157
|
* omitted or `null` saves nothing. */
|
|
157
158
|
rescue?: FormRescueDefinition | null;
|
|
@@ -986,6 +987,7 @@ export {
|
|
|
986
987
|
type BookingFormProps,
|
|
987
988
|
type BookingFieldRow,
|
|
988
989
|
type BookingFieldSlot,
|
|
990
|
+
type BookingFieldCopy,
|
|
989
991
|
type BookingFormConsent,
|
|
990
992
|
} from './booking-form';
|
|
991
993
|
|