Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework design form functionality #395

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

SalihuDickson
Copy link
Contributor

@SalihuDickson SalihuDickson commented Jan 27, 2025

Description

This PR aims to rework the functionality of the utils design form component

Fixes #(issue)

Checklist

  • My code follows the contributing guidelines of this project.
  • I am aware that all my commits will be squashed into a single commit, using the PR title as the commit message.
  • I have performed a self-review of my own code.
  • I have commented my code in hard-to-understand areas.
  • I have updated the user-facing documentation to describe any new or changed behavior.
  • My changes generate no new warnings.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have not reduced the existing code coverage.

Comments

Summary by Sourcery

Refactor the design form component to simplify its structure and improve maintainability. Update the demo form to use the new component API.

New Features:

  • Redesigned the form component API to be more declarative and user-friendly.

Tests:

  • Updated the demo form to showcase the new component API.

Copy link

changeset-bot bot commented Jan 27, 2025

⚠️ No Changeset found

Latest commit: b7a43a1

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

sourcery-ai bot commented Jan 27, 2025

Reviewer's Guide by Sourcery

This pull request refactors the form component to use a more modular approach. It removes the fields property and instead uses the children of the form component to render the form fields. It also adds support for nested forms and arrays.

Sequence diagram for form submission flow

sequenceDiagram
    participant User
    participant Form as EccUtilsDesignForm
    participant Input as EccUtilsDesignFormInput
    participant Group as EccUtilsDesignFormGroup

    User->>Form: Submit form
    Form->>Form: handleSubmit()
    Form->>Form: Check validity
    alt form is valid
        Form->>Form: Set loading state
        Form-->>User: Show loading state
        Form->>Form: Emit submit event
        Form-->>User: Show success message
    else form is invalid
        Form-->>User: Show validation errors
    end
Loading

Class diagram showing the new form component structure

classDiagram
    class EccUtilsDesignForm {
        -form: object
        -formState: string
        -canSubmit: boolean
        -submitDisabledByUser: boolean
        -errorMessage: string
        -successMessage: string
        -items: Array~Element~
        +firstUpdated()
        +handleSubmit()
        +render()
    }

    class EccUtilsDesignFormInput {
        +label: string
        +key: string
        +type: FormItemType
        +disabled: boolean
        +tooltip: string
        +required: boolean
        +value: any
        -path: string
        +handleValueUpdate()
        +handleFileUpload()
        +render()
    }

    class EccUtilsDesignFormGroup {
        +label: string
        +key: string
        +type: string
        +required: boolean
        +tooltip: string
        +instances: number
        -arrayInstances: Array
        -items: Array~Element~
        +render()
    }

    EccUtilsDesignForm *-- EccUtilsDesignFormInput
    EccUtilsDesignForm *-- EccUtilsDesignFormGroup
Loading

State diagram for form component states

stateDiagram-v2
    [*] --> Idle
    Idle --> Loading: Submit form
    Loading --> Success: Submit successful
    Loading --> Error: Submit failed
    Success --> Idle: Reset
    Error --> Idle: Reset

    state Idle {
        [*] --> Valid
        Valid --> Invalid: Validation failed
        Invalid --> Valid: Validation passed
    }
Loading

File-Level Changes

Change Details Files
Removed the fields property from the form component.
  • Removed the fields property.
  • Removed the logic to render the form based on the fields property.
packages/ecc-utils-design/src/components/form/form.ts
Added support for nested forms and arrays.
  • Added ecc-d-form-input component to render form fields.
  • Added ecc-d-form-group component to render nested forms and arrays.
  • Added logic to handle nested form and array changes.
  • Added logic to handle nested form and array submissions.
packages/ecc-utils-design/src/components/form/form.ts
packages/ecc-utils-design/src/components/form/formInput.ts
packages/ecc-utils-design/src/components/form/formGroup.ts
Refactored the form component to use a more modular approach.
  • Moved the logic to render form fields to the ecc-d-form-input component.
  • Moved the logic to render nested forms and arrays to the ecc-d-form-group component.
  • Refactored the form component to use the children to render the form fields.
packages/ecc-utils-design/src/components/form/form.ts
packages/ecc-utils-design/src/components/form/formInput.ts
packages/ecc-utils-design/src/components/form/formGroup.ts
Added support for TUS file uploads.
  • Added support for TUS file uploads using the @anurag_gupta/tus-js-client library.
  • Added logic to handle TUS file upload progress and errors.
packages/ecc-utils-design/src/components/form/formInput.ts
Added support for form events.
  • Added support for ecc-utils-change event to notify when a form field changes.
  • Added support for ecc-utils-submit event to notify when a form is submitted.
  • Added support for ecc-utils-array-add event to notify when a new array item is added.
  • Added support for ecc-utils-array-delete event to notify when an array item is deleted.
packages/ecc-utils-design/src/components/form/form.ts
packages/ecc-utils-design/src/events/index.ts
packages/ecc-utils-design/src/events/ecc-utils-array-add.ts
packages/ecc-utils-design/src/events/ecc-utils-array-delete.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!
  • Generate a plan of action for an issue: Comment @sourcery-ai plan on
    an issue to generate a plan of action for it.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

vercel bot commented Jan 27, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
elixir-cloud-components ❌ Failed (Inspect) Jan 28, 2025 11:51pm

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @SalihuDickson - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Consider consolidating duplicate utility functions (e.g. renderInTooltip, formatLabel) into a shared utilities module to improve maintainability
  • Error handling approach varies between components - recommend standardizing error handling and validation patterns across the codebase
Here's what I looked at during the review
  • 🟡 General issues: 3 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟢 Complexity: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@@ -83,499 +84,32 @@ export default class EccUtilsDesignForm extends LitElement {
formStyles,
];

@property({ type: Array, reflect: true }) fields: Array<Field> = [];
@state() private form: object = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Consider initializing canSubmit to false by default and enabling it only after validating form state

The current implementation may allow submission of invalid forms. Consider restoring form validation logic or documenting why immediate submission should be allowed.

Suggested implementation:

  @state() private canSubmit = false;
  @state() private form: object = {};

  private validateForm(): void {
    // Check if all required fields are filled
    const hasEmptyRequired = this.requiredButEmpty.length > 0;

    // Update canSubmit based on validation
    this.canSubmit = !hasEmptyRequired && Object.keys(this.form).length > 0;
  }

  protected updated(changedProperties: Map<string, unknown>): void {
    if (changedProperties.has('form') || changedProperties.has('requiredButEmpty')) {
      this.validateForm();
    }
  }

Note: You may need to:

  1. Adjust the validation logic in validateForm() if there are additional validation requirements specific to your forms
  2. Ensure the updated lifecycle method properly merges with any existing implementation if it already exists

@@ -644,6 +178,7 @@ export default class EccUtilsDesignForm extends LitElement {

private handleSubmit(e: Event) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: The error handling in handleSubmit could be improved to provide better feedback

Consider adding more specific error handling and user feedback when form validation fails.

Suggested implementation:

  private handleSubmit(e: Event) {
    e.preventDefault();

    try {
      // Check for required fields
      const emptyRequired = this.items
        .filter((item: any) => item.hasAttribute('required') && !item.value)
        .map((item: any) => item.name || 'Unknown field');

      if (emptyRequired.length > 0) {
        this.formState = "error";
        this.errorMessage = `Please fill in required fields: ${emptyRequired.join(', ')}`;
        this.requiredButEmpty = emptyRequired;
        return;
      }

      // Check for invalid fields
      const invalidFields = this.items
        .filter((item: any) => !item.checkValidity())
        .map((item: any) => item.name || 'Unknown field');

      if (invalidFields.length > 0) {
        this.formState = "error";
        this.errorMessage = `Please correct invalid fields: ${invalidFields.join(', ')}`;
        return;
      }

To fully implement this change, you'll also need to:

  1. Add error message display in the template, likely using sl-alert for errors
  2. Add CSS styles for highlighting invalid fields
  3. Ensure the rest of the handleSubmit method (which I can't see) properly sets formState back to "idle" after successful submission
  4. Consider adding field-level error messages using the sl-input or equivalent component's error states

@@ -1,13 +1,12 @@
# ecc-utils-design

The `@elixir-cloud/design` package is a foundational utility library that powers the ELIXIR Cloud Component's (ECC) ecosystem.
The `@elixir-cloud/design` package is a foundational utility library that powers the ELIXIR Cloud Component's (ECC) ecosystem.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (typo): Typo: "Component's" should be "Components"

The possessive should be plural: "ELIXIR Cloud Components".

Suggested change
The `@elixir-cloud/design` package is a foundational utility library that powers the ELIXIR Cloud Component's (ECC) ecosystem.
The `@elixir-cloud/design` package is a foundational utility library that powers the ELIXIR Cloud Components (ECC) ecosystem.

import "@shoelace-style/shoelace/dist/components/tooltip/tooltip.js";

export const getListData = (input: string) => {
if (typeof input !== "string") return input;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces)

Suggested change
if (typeof input !== "string") return input;
if (typeof input !== "string") {


ExplanationIt is recommended to always use braces and create explicit statement blocks.

Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).

}

private findNearestFormGroup(element: HTMLElement | null = this): void {
if (!element) return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces)

Suggested change
if (!element) return;
if (!element) {


ExplanationIt is recommended to always use braces and create explicit statement blocks.

Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).

}

private findNearestFormGroup(element: HTMLElement | null = this): void {
if (!element) return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces)

Suggested change
if (!element) return;
if (!element) {


ExplanationIt is recommended to always use braces and create explicit statement blocks.

Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).

}

const { parentElement } = element;
if (!parentElement) return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces)

Suggested change
if (!parentElement) return;
if (!parentElement) {


ExplanationIt is recommended to always use braces and create explicit statement blocks.

Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).


private renderTemplate(): TemplateResult {
const { type } = this;
if (type === "switch") return this.renderSwitchTemplate();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces)

Suggested change
if (type === "switch") return this.renderSwitchTemplate();
if (type === "switch") {


ExplanationIt is recommended to always use braces and create explicit statement blocks.

Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).

private renderTemplate(): TemplateResult {
const { type } = this;
if (type === "switch") return this.renderSwitchTemplate();
if (type === "file") return this.renderFileTemplate();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces)

Suggested change
if (type === "file") return this.renderFileTemplate();
if (type === "file") {


ExplanationIt is recommended to always use braces and create explicit statement blocks.

Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).

const { type } = this;
if (type === "switch") return this.renderSwitchTemplate();
if (type === "file") return this.renderFileTemplate();
if (type === "select") return this.renderSelectTemplate();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces)

Suggested change
if (type === "select") return this.renderSelectTemplate();
if (type === "select") {


ExplanationIt is recommended to always use braces and create explicit statement blocks.

Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).

@SalihuDickson
Copy link
Contributor Author

@anuragxxd, can you please take a quick look at the demo of the form component in this PR and tell me what you think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant