@rachelallyson/hero-hook-form 2.1.1 → 2.1.3

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/CHANGELOG.md CHANGED
@@ -2,6 +2,38 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [2.1.3] - 2025-01-28
6
+
7
+ ### Fixed
8
+
9
+ - **Cypress Form Submission Helpers**: Improved reliability of `cy.submitForm()` and related submission helpers
10
+ - Added form existence verification before submission
11
+ - Added submit button visibility and enabled state checks
12
+ - Implemented retry logic for better handling of timing issues
13
+ - Fixed `submitAndExpectSuccess()`, `submitAndExpectErrors()`, and `interceptFormSubmission()` helpers
14
+ - Made `expectFieldError()` more flexible by making `errorMessage` parameter optional
15
+ - Fixed TypeScript type compatibility issues in `getFormData()`
16
+ - All helpers now properly wait for forms to be ready before interacting
17
+
18
+ ### Added
19
+
20
+ - **Test Coverage**: Added comprehensive tests for `cy.submitForm()` helper in ZodForm component tests
21
+ - Tests successful form submission
22
+ - Tests validation error handling with submission helpers
23
+
24
+ ## [2.1.2] - 2025-01-28
25
+
26
+ ### Added
27
+
28
+ - **Date field helper**: Added `FormFieldHelpers.date()` method for creating date fields in forms
29
+ - Supports optional `dateProps` parameter for customizing the date input component
30
+ - Comprehensive test coverage for date field functionality
31
+
32
+ ### Fixed
33
+
34
+ - **ZodForm FormProvider**: Wrapped ZodForm component with FormProvider to ensure form context is properly available
35
+ - Fixes issues with form context not being accessible in nested components
36
+
5
37
  ## [2.1.1] - 2025-01-28
6
38
 
7
39
  ### Fixed
@@ -293,48 +293,72 @@ function expectNoValidationErrors() {
293
293
  return cy.get("body").should("not.contain", "error").and("not.contain", "invalid");
294
294
  }
295
295
  function expectFieldError(fieldLabel, errorMessage) {
296
- return cy.contains("label", fieldLabel).closest("div").should("contain", errorMessage);
296
+ const fieldContainer = cy.contains("label", fieldLabel).closest("div");
297
+ if (errorMessage) {
298
+ return fieldContainer.should("contain", errorMessage);
299
+ }
300
+ return fieldContainer.should(($div) => {
301
+ const text = $div.text();
302
+ const hasError = text.includes("error") || text.includes("invalid") || text.includes("required") || $div.find('[class*="error"], [class*="invalid"], [class*="danger"]').length > 0;
303
+ expect(hasError, "Field should have an error").to.be.true;
304
+ });
297
305
  }
298
306
  function expectFieldValid(fieldLabel) {
299
307
  return cy.contains("label", fieldLabel).closest("div").should("not.contain", "error").and("not.contain", "invalid");
300
308
  }
301
309
  function triggerValidation(submitButton = false) {
302
310
  if (submitButton) {
303
- return cy.get(HERO_UI_SELECTORS.button.submit).first().click();
311
+ return withRetry(() => {
312
+ cy.get("form").should("exist");
313
+ cy.get(HERO_UI_SELECTORS.button.submit).first().should("be.visible").should("not.be.disabled").click();
314
+ return cy.get("form");
315
+ }, DEFAULT_CONFIG);
304
316
  }
305
317
  return cy.get("input").first().blur();
306
318
  }
307
319
  function submitForm() {
308
- return cy.get(HERO_UI_SELECTORS.button.submit).first().click();
320
+ return withRetry(() => {
321
+ cy.get("form").should("exist");
322
+ cy.get(HERO_UI_SELECTORS.button.submit).first().should("be.visible").should("not.be.disabled").click();
323
+ return cy.get("form");
324
+ }, DEFAULT_CONFIG);
309
325
  }
310
326
  function submitAndExpectSuccess(successIndicator) {
311
- cy.get(HERO_UI_SELECTORS.button.submit).first().click();
312
- if (successIndicator) {
313
- return cy.contains(successIndicator).should("be.visible");
314
- }
315
- return cy.get("body").should("contain.one.of", ["success", "submitted", "complete", "thank you"]);
327
+ return withRetry(() => {
328
+ cy.get("form").should("exist");
329
+ cy.get(HERO_UI_SELECTORS.button.submit).first().should("be.visible").should("not.be.disabled").click();
330
+ if (successIndicator) {
331
+ cy.contains(successIndicator).should("be.visible");
332
+ } else {
333
+ cy.get("body").should("contain.one.of", ["success", "submitted", "complete", "thank you"]);
334
+ }
335
+ return cy.get("form");
336
+ }, DEFAULT_CONFIG);
316
337
  }
317
338
  function submitAndExpectErrors(errorMessage, formIndex = 0) {
318
- cy.get(HERO_UI_SELECTORS.button.submit).eq(formIndex).click();
319
- cy.get("form").should("exist");
320
- cy.wait(500);
321
- if (errorMessage) {
322
- cy.get("body").then(($body) => {
323
- if ($body.text().includes(errorMessage)) {
324
- cy.contains(errorMessage).should("be.visible");
325
- } else {
326
- cy.log("Expected error message not found:", errorMessage);
327
- cy.get('[class*="text-danger"], [class*="text-red"], [class*="error"]').then(($errors) => {
328
- cy.log("Found validation errors:", $errors.length);
329
- $errors.each((index, error) => {
330
- cy.log(`Error ${index}:`, error.textContent);
339
+ return withRetry(() => {
340
+ cy.get("form").should("exist");
341
+ cy.get(HERO_UI_SELECTORS.button.submit).eq(formIndex).should("be.visible").should("not.be.disabled").click();
342
+ cy.get("form").should("exist");
343
+ cy.wait(500);
344
+ if (errorMessage) {
345
+ cy.get("body").then(($body) => {
346
+ if ($body.text().includes(errorMessage)) {
347
+ cy.contains(errorMessage).should("be.visible");
348
+ } else {
349
+ cy.log("Expected error message not found:", errorMessage);
350
+ cy.get('[class*="text-danger"], [class*="text-red"], [class*="error"]').then(($errors) => {
351
+ cy.log("Found validation errors:", $errors.length);
352
+ $errors.each((index, error) => {
353
+ cy.log(`Error ${index}:`, error.textContent);
354
+ });
331
355
  });
332
- });
333
- cy.contains(errorMessage).should("be.visible");
334
- }
335
- });
336
- }
337
- return cy.get("form");
356
+ cy.contains(errorMessage).should("be.visible");
357
+ }
358
+ });
359
+ }
360
+ return cy.get("form");
361
+ }, DEFAULT_CONFIG);
338
362
  }
339
363
  function resetForm() {
340
364
  return cy.get("body").then(($body) => {
@@ -348,7 +372,11 @@ function resetForm() {
348
372
  }
349
373
  function interceptFormSubmission(method, url, alias) {
350
374
  cy.intercept(method, url).as(alias);
351
- return cy.get(HERO_UI_SELECTORS.button.submit).first().click();
375
+ return withRetry(() => {
376
+ cy.get("form").should("exist");
377
+ cy.get(HERO_UI_SELECTORS.button.submit).first().should("be.visible").should("not.be.disabled").click();
378
+ return cy.get("form");
379
+ }, DEFAULT_CONFIG);
352
380
  }
353
381
  function verifyFormExists() {
354
382
  return cy.get("form").should("exist");
@@ -365,7 +393,7 @@ function verifyFieldCount(selector, count) {
365
393
  return cy.get(selector).filter(":visible").not('[type="hidden"]').should("have.length", count);
366
394
  }
367
395
  function getFormData() {
368
- return extractFormData();
396
+ return extractFormData().then((data) => data);
369
397
  }
370
398
  function fillCompleteForm(formData) {
371
399
  return cy.then(() => {
package/dist/index.d.ts CHANGED
@@ -724,7 +724,7 @@ interface ZodFormProps<T extends FieldValues> {
724
724
  values: T;
725
725
  }) => React$1.ReactNode;
726
726
  }
727
- declare function ZodForm<T extends FieldValues>({ className, columns, config, layout, onError, onSubmit, onSuccess, render, resetButtonText, showResetButton, spacing, submitButtonProps, submitButtonText, subtitle, title, }: ZodFormProps<T>): string | number | bigint | boolean | Iterable<React$1.ReactNode> | Promise<string | number | bigint | boolean | React$1.ReactPortal | React$1.ReactElement<unknown, string | React$1.JSXElementConstructor<any>> | Iterable<React$1.ReactNode> | null | undefined> | React$1.JSX.Element | null | undefined;
727
+ declare function ZodForm<T extends FieldValues>({ className, columns, config, layout, onError, onSubmit, onSuccess, render, resetButtonText, showResetButton, spacing, submitButtonProps, submitButtonText, subtitle, title, }: ZodFormProps<T>): React$1.JSX.Element;
728
728
 
729
729
  /**
730
730
  * Hook for using Zod validation with React Hook Form
@@ -781,6 +781,10 @@ declare const FormFieldHelpers: {
781
781
  * Create a checkbox field
782
782
  */
783
783
  checkbox: <T extends FieldValues>(name: Path<T>, label: string) => ZodFormFieldConfig<T>;
784
+ /**
785
+ * Create a date field
786
+ */
787
+ date: <T extends FieldValues>(name: Path<T>, label: string, dateProps?: Record<string, string | number | boolean>) => ZodFormFieldConfig<T>;
784
788
  /**
785
789
  * Create an input field
786
790
  */
package/dist/index.js CHANGED
@@ -1855,6 +1855,9 @@ import { useFormContext as useFormContext5 } from "react-hook-form";
1855
1855
  // src/components/ZodForm.tsx
1856
1856
  import React21 from "react";
1857
1857
  import { Button as Button5 } from "@heroui/react";
1858
+ import {
1859
+ FormProvider as FormProvider2
1860
+ } from "react-hook-form";
1858
1861
 
1859
1862
  // src/zod-integration.ts
1860
1863
  import { useForm as useForm2 } from "react-hook-form";
@@ -2182,16 +2185,16 @@ function ZodForm({
2182
2185
  }
2183
2186
  }, [form.formState.errors, config.onError]);
2184
2187
  if (render) {
2185
- return render({
2188
+ return /* @__PURE__ */ React21.createElement(FormProvider2, { ...form }, render({
2186
2189
  errors: form.formState.errors,
2187
2190
  form,
2188
2191
  isSubmitted: enhancedState.status !== "idle",
2189
2192
  isSubmitting: enhancedState.isSubmitting,
2190
2193
  isSuccess: enhancedState.isSuccess,
2191
2194
  values: form.getValues()
2192
- });
2195
+ }));
2193
2196
  }
2194
- return /* @__PURE__ */ React21.createElement("form", { className, role: "form", onSubmit: handleFormSubmit }, title && /* @__PURE__ */ React21.createElement("div", { className: "mb-6" }, /* @__PURE__ */ React21.createElement("h2", { className: "text-xl font-semibold text-foreground mb-2" }, title), subtitle && /* @__PURE__ */ React21.createElement("p", { className: "text-sm text-muted-foreground" }, subtitle)), /* @__PURE__ */ React21.createElement(
2197
+ return /* @__PURE__ */ React21.createElement(FormProvider2, { ...form }, /* @__PURE__ */ React21.createElement("form", { className, role: "form", onSubmit: handleFormSubmit }, title && /* @__PURE__ */ React21.createElement("div", { className: "mb-6" }, /* @__PURE__ */ React21.createElement("h2", { className: "text-xl font-semibold text-foreground mb-2" }, title), subtitle && /* @__PURE__ */ React21.createElement("p", { className: "text-sm text-muted-foreground" }, subtitle)), /* @__PURE__ */ React21.createElement(
2195
2198
  FormStatus,
2196
2199
  {
2197
2200
  state: enhancedState,
@@ -2217,7 +2220,7 @@ function ZodForm({
2217
2220
  onPress: resetForm
2218
2221
  },
2219
2222
  resetButtonText
2220
- )));
2223
+ ))));
2221
2224
  }
2222
2225
 
2223
2226
  // src/builders/BasicFormBuilder.ts
@@ -2302,6 +2305,15 @@ var FormFieldHelpers = {
2302
2305
  name,
2303
2306
  type: "checkbox"
2304
2307
  }),
2308
+ /**
2309
+ * Create a date field
2310
+ */
2311
+ date: (name, label, dateProps) => ({
2312
+ dateProps,
2313
+ label,
2314
+ name,
2315
+ type: "date"
2316
+ }),
2305
2317
  /**
2306
2318
  * Create an input field
2307
2319
  */
@@ -716,7 +716,7 @@ interface ZodFormProps<T extends FieldValues> {
716
716
  values: T;
717
717
  }) => React$1.ReactNode;
718
718
  }
719
- declare function ZodForm<T extends FieldValues>({ className, columns, config, layout, onError, onSubmit, onSuccess, render, resetButtonText, showResetButton, spacing, submitButtonProps, submitButtonText, subtitle, title, }: ZodFormProps<T>): string | number | bigint | boolean | Iterable<React$1.ReactNode> | Promise<string | number | bigint | boolean | React$1.ReactPortal | React$1.ReactElement<unknown, string | React$1.JSXElementConstructor<any>> | Iterable<React$1.ReactNode> | null | undefined> | React$1.JSX.Element | null | undefined;
719
+ declare function ZodForm<T extends FieldValues>({ className, columns, config, layout, onError, onSubmit, onSuccess, render, resetButtonText, showResetButton, spacing, submitButtonProps, submitButtonText, subtitle, title, }: ZodFormProps<T>): React$1.JSX.Element;
720
720
 
721
721
  /**
722
722
  * Hook for using Zod validation with React Hook Form
@@ -773,6 +773,10 @@ declare const FormFieldHelpers: {
773
773
  * Create a checkbox field
774
774
  */
775
775
  checkbox: <T extends FieldValues>(name: Path<T>, label: string) => ZodFormFieldConfig<T>;
776
+ /**
777
+ * Create a date field
778
+ */
779
+ date: <T extends FieldValues>(name: Path<T>, label: string, dateProps?: Record<string, string | number | boolean>) => ZodFormFieldConfig<T>;
776
780
  /**
777
781
  * Create an input field
778
782
  */
@@ -1860,6 +1860,9 @@ import { useFormContext as useFormContext5 } from "react-hook-form";
1860
1860
  // src/components/ZodForm.tsx
1861
1861
  import React21 from "react";
1862
1862
  import { Button as Button5 } from "@heroui/react";
1863
+ import {
1864
+ FormProvider as FormProvider2
1865
+ } from "react-hook-form";
1863
1866
 
1864
1867
  // src/zod-integration.ts
1865
1868
  import { useForm as useForm2 } from "react-hook-form";
@@ -2187,16 +2190,16 @@ function ZodForm({
2187
2190
  }
2188
2191
  }, [form.formState.errors, config.onError]);
2189
2192
  if (render) {
2190
- return render({
2193
+ return /* @__PURE__ */ React21.createElement(FormProvider2, { ...form }, render({
2191
2194
  errors: form.formState.errors,
2192
2195
  form,
2193
2196
  isSubmitted: enhancedState.status !== "idle",
2194
2197
  isSubmitting: enhancedState.isSubmitting,
2195
2198
  isSuccess: enhancedState.isSuccess,
2196
2199
  values: form.getValues()
2197
- });
2200
+ }));
2198
2201
  }
2199
- return /* @__PURE__ */ React21.createElement("form", { className, role: "form", onSubmit: handleFormSubmit }, title && /* @__PURE__ */ React21.createElement("div", { className: "mb-6" }, /* @__PURE__ */ React21.createElement("h2", { className: "text-xl font-semibold text-foreground mb-2" }, title), subtitle && /* @__PURE__ */ React21.createElement("p", { className: "text-sm text-muted-foreground" }, subtitle)), /* @__PURE__ */ React21.createElement(
2202
+ return /* @__PURE__ */ React21.createElement(FormProvider2, { ...form }, /* @__PURE__ */ React21.createElement("form", { className, role: "form", onSubmit: handleFormSubmit }, title && /* @__PURE__ */ React21.createElement("div", { className: "mb-6" }, /* @__PURE__ */ React21.createElement("h2", { className: "text-xl font-semibold text-foreground mb-2" }, title), subtitle && /* @__PURE__ */ React21.createElement("p", { className: "text-sm text-muted-foreground" }, subtitle)), /* @__PURE__ */ React21.createElement(
2200
2203
  FormStatus,
2201
2204
  {
2202
2205
  state: enhancedState,
@@ -2222,7 +2225,7 @@ function ZodForm({
2222
2225
  onPress: resetForm
2223
2226
  },
2224
2227
  resetButtonText
2225
- )));
2228
+ ))));
2226
2229
  }
2227
2230
 
2228
2231
  // src/builders/BasicFormBuilder.ts
@@ -2307,6 +2310,15 @@ var FormFieldHelpers = {
2307
2310
  name,
2308
2311
  type: "checkbox"
2309
2312
  }),
2313
+ /**
2314
+ * Create a date field
2315
+ */
2316
+ date: (name, label, dateProps) => ({
2317
+ dateProps,
2318
+ label,
2319
+ name,
2320
+ type: "date"
2321
+ }),
2310
2322
  /**
2311
2323
  * Create an input field
2312
2324
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rachelallyson/hero-hook-form",
3
- "version": "2.1.1",
3
+ "version": "2.1.3",
4
4
  "description": "Typed form helpers that combine React Hook Form and HeroUI components.",
5
5
  "author": "Rachel Higley",
6
6
  "homepage": "https://rachelallyson.github.io/hero-hook-form/",