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

add multi-email input component and refactor compose email form #386

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

Conversation

yashsharma999
Copy link

@yashsharma999 yashsharma999 commented Apr 1, 2025

closes #368

https://www.loom.com/share/641f7db9d9294edabfecbcc1c8180810?sid=20501053-759b-4b2f-93a8-09bf24e9bf4e

Summary by CodeRabbit

  • New Features
    • Enhanced the email composition interface with improved recipient fields.
    • Introduced a dynamic mailbox component that provides toggling options for additional fields like Cc and Bcc.
    • Added support for managing multiple email addresses with robust validation and formatting improvements.
    • Updated form behavior to consistently handle recipient inputs, ensuring a smoother email composition experience.

Copy link

vercel bot commented Apr 1, 2025

@yashsharma999 is attempting to deploy a commit to the Inbox Zero Team on Vercel.

A member of the Team first needs to authorize it.

@CLAassistant
Copy link

CLAassistant commented Apr 1, 2025

CLA assistant check
All committers have signed the CLA.

Copy link
Contributor

coderabbitai bot commented Apr 1, 2025

Walkthrough

This PR updates the email composition functionality. In the ComposeEmailForm, default values for cc and bcc are set to empty strings, and the form’s onSubmit logic now converts email fields from arrays to comma-separated strings. The old email input is replaced by a new ComposeMailBox component, which manages the display of additional fields via toggle buttons. Additionally, a new MultiEmailInput component is introduced to handle multiple email entries with regex validation and state management for form registration.

Changes

File(s) Change Summary
…/compose/ComposeEmailForm.tsx Updated default values for cc and bcc; revised onSubmit logic to convert email arrays to comma-separated strings; reintroduced SendEmailBody type; replaced the traditional <Input> with the ComposeMailBox component.
…/compose/ComposeMailBox.tsx, …/compose/MultiEmailInput.tsx Added new components: ComposeMailBox (along with ToggleButtonsWrapper and ToggleButton) enables dynamic Cc/Bcc handling. MultiEmailInput provides multi-email entry, regex validation, state management, and integration with form registration.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant Form as ComposeEmailForm
    participant MailBox as ComposeMailBox
    participant Server as Server

    User->>Form: Enter email details (to, cc, bcc)
    Form->>MailBox: Render email input fields with toggles for Cc/Bcc
    User->>MailBox: Toggle Cc/Bcc fields as needed
    MailBox->>MailBox: Update internal display state
    User->>Form: Submit form
    Form->>Form: Convert email arrays to comma-separated strings
    Form->>Server: Send processed email data
Loading
sequenceDiagram
    participant User as User
    participant MEI as MultiEmailInput
    participant Reg as Form Registration

    User->>MEI: Type email address(es)
    MEI->>MEI: Validate emails using regex and update state
    MEI->>Reg: Pass comma-separated email values for form submission
Loading

Assessment against linked issues

Objective Addressed Explanation
Update multi input on compose form (#368)

Possibly related PRs

Poem

I'm a rabbit hopping in the code glade,
Where email forms bloom in a cascade.
With toggles and inputs, I bound along,
Crafting code in a harmonious song.
Hoppy changes make my heart skip a beat!
🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/web/app/(app)/compose/ComposeEmailForm.tsx

Oops! Something went wrong! :(

ESLint: 9.23.0

ESLint couldn't find an eslint.config.(js|mjs|cjs) file.

From ESLint v9.0.0, the default configuration file is now eslint.config.js.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

apps/web/app/(app)/compose/MultiEmailInput.tsx

Oops! Something went wrong! :(

ESLint: 9.23.0

ESLint couldn't find an eslint.config.(js|mjs|cjs) file.

From ESLint v9.0.0, the default configuration file is now eslint.config.js.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

apps/web/app/(app)/compose/ComposeMailBox.tsx

Oops! Something went wrong! :(

ESLint: 9.23.0

ESLint couldn't find an eslint.config.(js|mjs|cjs) file.

From ESLint v9.0.0, the default configuration file is now eslint.config.js.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@yashsharma999 yashsharma999 marked this pull request as ready for review April 5, 2025 11:15
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (10)
apps/web/app/(app)/compose/MultiEmailInput.tsx (4)

69-70: Remove console.log statement.

There's a debugging console.log statement that should be removed before merging to production.

const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
  if ((e.key === " " || e.key === "Tab") && inputValue) {
-    console.log("came here,", e.key);
    e.preventDefault();
    addEmail(inputValue);

19-21: Consider using more specific types instead of any.

The register and error props are typed as any, which reduces type safety. Consider using more specific types based on react-hook-form's types.

interface MultiEmailInputProps {
  name: string;
  label: string;
  placeholder?: string;
  className?: string;
-  register?: any;
-  error?: any;
+  register?: UseFormRegister<any>;
+  error?: FieldError;
}

You'll need to import these types from react-hook-form:

import { UseFormRegister, FieldError } from "react-hook-form";

36-38: Consider using a more comprehensive email validation regex.

The current email validation is basic and might not catch all edge cases. Consider using a more comprehensive regex for validating emails.

const isValidEmail = (email: string) => {
-  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
+  return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(email);
};

104-119: Use email as key instead of index.

Using array indices as React keys can lead to unexpected behavior when items are removed from the middle of the list. Since email addresses are unique in this context, use them as keys instead.

{emails.map((email, index) => (
  <div
-    key={index}
+    key={email}
    className="flex w-fit items-center gap-1 rounded-md bg-primary/10 px-2 py-1 text-sm text-primary"
  >
    <span>{email}</span>
    <button
      type="button"
      onClick={() => removeEmail(index)}
      className="flex h-4 w-4 items-center justify-center rounded-full hover:bg-primary/20"
      aria-label={`Remove ${email}`}
    >
      <X className="h-3 w-3" />
    </button>
  </div>
))}
apps/web/app/(app)/compose/ComposeEmailForm.tsx (1)

29-30: Use import type for type-only imports.

Since SendEmailBody is only used as a type, it should be imported using import type to ensure it's removed during compilation.

import ComposeMailBox from "@/app/(app)/compose/ComposeMailBox";
-import { SendEmailBody } from "@/utils/gmail/mail";
+import type { SendEmailBody } from "@/utils/gmail/mail";
🧰 Tools
🪛 Biome (1.9.4)

[error] 30-30: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)

apps/web/app/(app)/compose/ComposeMailBox.tsx (5)

2-2: Remove unused import.

The useEffect import is not used in this component and should be removed.

-import React, { useEffect, useState } from "react";
+import React, { useState } from "react";

15-16: Consider using more specific types instead of any.

The register and errors props are typed as any, which reduces type safety. Consider using more specific types based on react-hook-form's types.

type ComposeMailBoxProps = {
  to: string;
  cc?: string;
  bcc?: string;
-  register: any;
-  errors?: any;
+  register: UseFormRegister<any>;
+  errors?: {
+    to?: FieldError;
+    cc?: FieldError;
+    bcc?: FieldError;
+  };
};

You'll need to import these types from react-hook-form:

import { UseFormRegister, FieldError } from "react-hook-form";

41-41: Use string literal instead of template literal.

No template interpolation is needed here, so a simple string literal would be more appropriate.

-<div className={`relative flex flex-col gap-2 rounded-md border p-2`}>
+<div className="relative flex flex-col gap-2 rounded-md border p-2">
🧰 Tools
🪛 Biome (1.9.4)

[error] 41-41: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)


101-116: Simplify array filtering logic.

The current implementation creates an array with potentially falsy elements, filters them, and then checks again whether each button exists. This can be simplified.

-{[
-  !showCC && { type: CarbonCopyType.CC, width: "w-8" },
-  !showBCC && { type: CarbonCopyType.BCC, width: "w-10" },
-]
-  .filter(Boolean)
-  .map(
-    (button) =>
-      button && (
-        <ToggleButton
-          key={button.type}
-          label={button.type}
-          className={button.width}
-          onClick={() => toggleCarbonCopy(button.type)}
-        />
-      ),
-  )}
+{[
+  { type: CarbonCopyType.CC, width: "w-8", show: !showCC },
+  { type: CarbonCopyType.BCC, width: "w-10", show: !showBCC },
+]
+  .filter(button => button.show)
+  .map(button => (
+    <ToggleButton
+      key={button.type}
+      label={button.type}
+      className={button.width}
+      onClick={() => toggleCarbonCopy(button.type)}
+    />
+  ))}

135-135: Use string literal instead of template literal.

No template interpolation is needed here, so a simple string literal would be more appropriate.

-className={cn(`h-6 w-8 text-[10px]`, className)}
+className={cn("h-6 w-8 text-[10px]", className)}
🧰 Tools
🪛 Biome (1.9.4)

[error] 135-135: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d702467 and e80f327.

📒 Files selected for processing (3)
  • apps/web/app/(app)/compose/ComposeEmailForm.tsx (5 hunks)
  • apps/web/app/(app)/compose/ComposeMailBox.tsx (1 hunks)
  • apps/web/app/(app)/compose/MultiEmailInput.tsx (1 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
apps/web/app/(app)/compose/ComposeEmailForm.tsx (1)
apps/web/app/(app)/compose/ComposeMailBox.tsx (1)
  • ComposeMailBox (19-87)
apps/web/app/(app)/compose/ComposeMailBox.tsx (1)
apps/web/app/(app)/compose/MultiEmailInput.tsx (1)
  • MultiEmailInput (23-136)
🪛 Biome (1.9.4)
apps/web/app/(app)/compose/ComposeEmailForm.tsx

[error] 30-30: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)

apps/web/app/(app)/compose/ComposeMailBox.tsx

[error] 41-41: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)


[error] 135-135: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

🔇 Additional comments (5)
apps/web/app/(app)/compose/ComposeEmailForm.tsx (4)

70-71: Verify intentional removal of reply context.

The default values for cc and bcc are now hardcoded to empty strings, ignoring potential values from replyingToEmail. Ensure this is intentional and not an oversight, as it might lead to loss of context when replying to emails.

defaultValues: {
  replyToEmail: replyingToEmail,
  subject: replyingToEmail?.subject,
  to: replyingToEmail?.to,
-  cc: "",
-  bcc: "",
+  cc: replyingToEmail?.cc || "",
+  bcc: replyingToEmail?.bcc || "",
  messageHtml: replyingToEmail?.draftHtml,
},

80-82: Good addition to handle array email fields.

The modifications to handle arrays for email fields is a good improvement for compatibility with the new multi-email input component.


135-137: Good adaptation for the new email format.

The adjustment to the selectedEmailAddressses logic to handle both string and array formats is appropriate and ensures backward compatibility.


315-321: Well integrated new email component.

The new ComposeMailBox component is well integrated into the form, passing all the necessary props for proper functioning.

apps/web/app/(app)/compose/ComposeMailBox.tsx (1)

19-87: Good implementation of the email composition box.

The component effectively manages the visibility of CC and BCC fields with clean toggle functionality. The dynamic positioning of toggle buttons based on content is a nice UX touch.

🧰 Tools
🪛 Biome (1.9.4)

[error] 41-41: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (8)
apps/web/app/(app)/compose/ComposeMailBox.tsx (5)

11-17: Improve type definitions for better type safety.

The register and errors props are currently typed as any, which reduces type safety and IDE assistance. Consider using more specific types for these props.

- type ComposeMailBoxProps = {
-   to: string;
-   cc?: string;
-   bcc?: string;
-   register: any;
-   errors?: any;
- };
+ type ComposeMailBoxProps = {
+   to: string;
+   cc?: string;
+   bcc?: string;
+   register: UseFormRegister<any>;
+   errors?: FieldErrors<any>;
+ };

You would need to import these types from react-hook-form:

import { UseFormRegister, FieldErrors } from "react-hook-form";

19-35: Props destructuring is incomplete.

You're destructuring some props in line 20 but still using props directly in other parts of the component. Consider destructuring all the props you need for better readability.

- export default function ComposeMailBox(props: ComposeMailBoxProps) {
-   const { register, to, errors } = props;
+ export default function ComposeMailBox({
+   register,
+   to,
+   cc,
+   bcc,
+   errors
+ }: ComposeMailBoxProps) {

37-38: Consider renaming state variable for clarity.

The variable moveToggleButtonsToNewLine doesn't actually move buttons but determines whether to render them on a new line. A more accurate name would improve readability.

- const moveToggleButtonsToNewLine =
-   carbonCopy.cc || carbonCopy.bcc || (to && to.length > 0);
+ const shouldRenderButtonsBelow =
+   carbonCopy.cc || carbonCopy.bcc || (to && to.length > 0);

Update the references to this variable in the JSX as well.


40-41: Fix template literal usage.

Template literals are used without any interpolation or special characters, which is flagged by static analysis.

- <div className={`relative flex flex-col gap-2 rounded-md border p-2`}>
+ <div className="relative flex flex-col gap-2 rounded-md border p-2">
🧰 Tools
🪛 Biome (1.9.4)

[error] 41-41: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)


119-138: Fix template literal usage in ToggleButton.

Template literals are used without any interpolation or special characters, which is flagged by static analysis.

- className={cn(`h-6 w-8 text-[10px]`, className)}
+ className={cn("h-6 w-8 text-[10px]", className)}
🧰 Tools
🪛 Biome (1.9.4)

[error] 132-132: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

apps/web/app/(app)/compose/MultiEmailInput.tsx (3)

8-15: Improve type definitions for better type safety.

The register and error props are typed as any, which reduces type safety. Consider using more specific types for form handling.

interface MultiEmailInputProps {
  name: string;
  label: string;
  placeholder?: string;
  className?: string;
- register?: any;
- error?: any;
+ register?: UseFormRegister<any>;
+ error?: FieldError;
}

You would need to import these types from react-hook-form:

import { UseFormRegister, FieldError } from "react-hook-form";

29-34: Consider extracting the email validation regex to a utility function.

The email validation regex is complex and would be better maintained as a utility function that can be reused across the application.

Create a utility file for validation functions (e.g., utils/validation.ts):

export const isValidEmail = (email: string): boolean => {
  return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(
    email,
  );
};

Then import and use it in your component:

- // Email validation regex
- const isValidEmail = (email: string) => {
-   return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(
-     email,
-   );
- };
+ import { isValidEmail } from "@/utils/validation";

125-125: Consider handling initial values more explicitly.

The component doesn't seem to handle initial values that might be passed from parent components. This could be important when editing an existing email.

Add an effect to handle initial values:

+ useEffect(() => {
+   // If an initial value is provided as a comma-separated string
+   if (register && register(name).value && typeof register(name).value === 'string') {
+     const initialEmails = register(name).value.split(',').filter(email => email.trim() && isValidEmail(email.trim()));
+     if (initialEmails.length > 0) {
+       setEmails(initialEmails);
+     }
+   }
+ }, [name, register]);

Don't forget to import useEffect if it's not already imported.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e80f327 and 78e5305.

📒 Files selected for processing (3)
  • apps/web/app/(app)/compose/ComposeEmailForm.tsx (5 hunks)
  • apps/web/app/(app)/compose/ComposeMailBox.tsx (1 hunks)
  • apps/web/app/(app)/compose/MultiEmailInput.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/(app)/compose/ComposeEmailForm.tsx
🧰 Additional context used
🧬 Code Definitions (1)
apps/web/app/(app)/compose/ComposeMailBox.tsx (1)
apps/web/app/(app)/compose/MultiEmailInput.tsx (1)
  • MultiEmailInput (17-131)
🪛 Biome (1.9.4)
apps/web/app/(app)/compose/ComposeMailBox.tsx

[error] 41-41: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)


[error] 132-132: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

🔇 Additional comments (5)
apps/web/app/(app)/compose/ComposeMailBox.tsx (2)

40-86: The component structure looks well-organized.

The component has a clean structure with conditional rendering of email input fields based on the visibility state. The layout adapts based on the presence of emails in the "To" field, which provides a good user experience.

🧰 Tools
🪛 Biome (1.9.4)

[error] 41-41: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)


89-117: The ToggleButtonsWrapper logic looks good.

The helper component effectively handles the visibility of toggle buttons based on the current state, filtering out buttons that shouldn't be shown. The use of a map function with filtering makes this code concise and readable.

apps/web/app/(app)/compose/MultiEmailInput.tsx (3)

36-62: Well-implemented email management functions.

The functions to add and remove emails are well implemented with proper validation and state management. The code ensures that:

  1. Only valid emails are added
  2. Duplicate emails are prevented
  3. The input field is cleared after adding
  4. Form state is updated appropriately

64-81: Good keyboard interaction handling.

The keyboard handlers implement a natural user experience:

  • Adding emails with space or tab
  • Deleting the last email with backspace when the input is empty
  • Adding the current input when the field loses focus

This makes the component intuitive and efficient to use.


83-129: Well-structured UI with good accessibility.

The UI implementation is clean and accessible with:

  • Proper labeling
  • Visual indication of errors
  • Clear representation of added emails
  • Keyboard navigation support
  • Aria labels for remove buttons

The component layout adjusts appropriately based on the state of the emails list.

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.

Update multi input on compose form
2 participants