Skip to content

GonchuB/emails-input

Repository files navigation

emails input

A 4KB emails input component.

emails-input

Live demos

Using Netlify

API

EmailsInput function

The EmailsInput function accepts 2 parameters:

  • node (required): entry point of the emails input. The component will attach itself to this DOM node.
  • options (required): options object to customise the component
  • options.initialEmails (optional): an array of strings to be used as initial data. Defaults to []
  • options.validator (optional): a validator function that, given a string, returns true if the string is a valid email, false otherwise. Defaults to testing the regular expression /^.+@.+\..+$/.
  • options.placeholder (optional): the placeholder that will be used for the input element. Defaults to 'add more people...'

EmailsInput instance methods

The EmailsInput component exposes 3 methods in its public API:

  • getEmails(): string[]: A method to get all entered emails. Both valid and invalid.
  • setEmails(emails: string[]): void: A method to replace all entered emails with new ones.
  • subscribe(callback: (emails: string[]) => void): void: Ability to subscribe for emails list changes.

Example usage

<!-- Your markup with buttons, headers, etc -->
<script src="./emails-input.js"></script>
<script>
    function isEmailValid(email) {
        return /^.+@.+\..+$/.test(email);
    }
    
    const inputContainerNode = document.querySelector("#emails-input"); // <- your entry point for the component
    const emailsInput = EmailsInput(inputContainerNode, { initialEmails: ['[email protected]'], validator: isEmailValid });
    
    document.querySelector("#button-add").addEventListener('click', function onButtonAddClick() {
        const email =
            (Math.random().toString(36) + "00000000000000000").slice(2, 12) +
            "@" +
            (Math.random() > 0.2 ? "domain" : "") +
            ".com";
    
        const allEmails = emailsInput.getEmails();
        emailsInput.setEmails(allEmails.concat([email]));
    });
    
    document.querySelector("#button-get").addEventListener('click', function onButtonGetClick() {
        const validEmailsCount = emailsInput.getEmails().filter(function(email) {
            return isEmailValid(email);
        }).length;

        alert(validEmailsCount);
    });
</script>

Solution design

Disclaimer: this is not using strict UML, rather using some of its diagrams to give an idea of the design.

UML-diagram

Performance

Bundle size

bundle-size

Pasting 50 csv emails

paste-50

Pasting 1,000 csv emails

paste-1000

After removing the 1,000 emails

remove-1000

Improvements

Things that can be changed to improve the current codebase and/or performance:

  • Minify css class names. With a minimal css-in-js solution, or using the same hash function in runtine (call function with base class name instead of just using the class name) as in build time (pre-process css class names).
  • When adding or removing elements using the ui, use an ad-hoc reaction that won't update the full list of emails (trying to find the "deleted emails" currently iterates over the list of rendered components to check if they were delete).
  • Use the Component abstraction to take care of manipulating the dom, adding and removing event listeners, etc. Not necessarily related to performance, but having DOM manipulation done in one place only helps reduce memory leaks. By abstracting the DOM under a Component interface, it can be changed in isolation in the future, optimised, etc.
  • Avoid prop drilling. Although the current tree is quite shallow, prop drilling usually turns into a mess. If more options or properties are needed, consider having something shared between components to handle different configurations.

Building the repo

npm run build

Building to public

Builds the js and moves it to the ./public folder.

npm run build:public

Running tests

npm run test

Running the demo locally

First, build the emails-input.js bundle and move it to the public folder:

npm run build:public

Use a static file server to serve the contents of public:

# Using serve
npx serve public
# or using python
cd public && python -m SimpleHTTPServer 5000