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

$errors first string, then array #164

Open
letmejustputthishere opened this issue May 5, 2022 · 1 comment
Open

$errors first string, then array #164

letmejustputthishere opened this issue May 5, 2022 · 1 comment
Labels
bug Something isn't working

Comments

@letmejustputthishere
Copy link

letmejustputthishere commented May 5, 2022

Summary

When checking the $errors store that come with calling the createForm method, at first it's a string and then after more error are being added it becomes an array of strings. it should be an array of strings no matter how many errors are present for a key.

Steps to reproduce

Example Project

<script lang="ts">
  import { store } from "../store";
  import { ConfettiExplosion } from "svelte-confetti-explosion";
  import spinner from "../assets/loading.gif";
  import SvelteMarkdown from "svelte-markdown";
  import Button from "../components/Button.svelte";
  import Card from "./Card.svelte";

  import {
    createForm,
    Form,
    Field,
    ErrorMessage,
    Textarea,
  } from "svelte-forms-lib";
  import { object, string, array } from "yup";

  let loading = false;
  let confetti = false;
  export let preview;

  // validation schema
  const schema = object({
    title: string()
      .required("Title is required")
      .min(10, "Title must be at least 10 characters")
      .max(100, "Title must be less than 100 characters"),
    description: string()
      .required("Description is required")
      .min(200, "Description must be at least 200 characters")
      .max(50000, "Description must be less than 50000 characters"),
    options: array()
      .of(
        string()
          .min(1, "Option must be at least 1 character")
          .max(50, "Option must be less than 50 characters"),
      )
      .min(1, "You must provide at least 1 option")
      .max(10, "You can provide up to 10 options"),
  });

  const formContext = createForm({
    initialValues: {
      title: "",
      description: "",
      options: [""],
    },
    validationSchema: schema,
    onSubmit: submitProposal,
  });

  // this is needed so we can access the appropiate stores
  const { form, handleChange, errors } = formContext;
  $: console.log($errors);

  const addOption = () => {
    $form.options = [...$form.options, ""];
    $errors.options = $errors.options.concat("");
  };

  function removeOption(index: number) {
    $form.options = $form.options.filter((_, i) => i !== index);
    // this throws an error, as it is not initally of type string[]
    $errors.options = $errors.options.filter((_, i) => i !== index);
  }

  const clearProposal = () => {
    $form = {
      description: "",
      title: "",
      options: [""],
    };
  };

  async function submitProposal(proposal) {
    loading = true;
    await store.submitProposal(proposal);
    confetti = false;
    confetti = true;
    loading = false;
    clearProposal();
    await store.fetchProposals();
    store.filterProposals();
  }
</script>

{#if confetti}
  <div class="fixed bottom-0">
    <ConfettiExplosion
      particlesShape="circles"
      colors={["#BB64D2", "#24A0F5", "#FED030", "#FC514B"]}
      force={1}
    />
  </div>
{/if}

<!-- form -->
<Form context={formContext}>
  <Card style="lg:mx-2">
    <div class="p-2 lg:p-4 flex flex-col 2xl:text-xl">
      <h1 class="font-everett-medium text-3xl 2xl:text-4xl">
        create new proposal:
      </h1>
      <!-- title -->
      <div class="mt-10 flex flex-col mx-2 font-mono">
        <p class="italic">title:</p>
        <Field
          type="text"
          class="p-2 bg-white dark:bg-black border-black dark:border-white dark:text-white border-2 rounded-xl my-2"
          name="title"
          placeholder="enter your proposals title"
        />
        <ErrorMessage name="title" />
      </div>
      <!-- description -->
      <div class="mt-10 flex flex-col mx-2 font-mono">
        <p class="italic">description:</p>
        {#if preview}
          <article
            class="prose prose-black 2xl:prose-xl dark:prose-invert font-sans max-w-none"
          >
            <SvelteMarkdown source={$form.description} />
          </article>
        {:else}
          <Textarea
            rows={5}
            class="p-2 bg-white dark:bg-black border-black dark:border-white dark:text-white border-2 rounded-xl my-2"
            placeholder="you can write markdown here! to see the outcome click the 'preview' button at the top :)"
            name="description"
          />
          <ErrorMessage name="description" />
        {/if}
      </div>
      <!-- options -->
      <div class="mt-10 flex flex-col mx-2 font-mono">
        <p class="italic">options:</p>
        {#each $form.options as option, index}
          <div
            class="flex justify-between p-2 bg-white dark:bg-black border-black dark:border-white dark:text-white border-2 rounded-xl my-2"
          >
            <input
              type="text"
              class="flex-1 bg-white dark:bg-black"
              placeholder="an option people can vote on"
              name={`options[${index}]`}
              on:change={handleChange}
              on:blur={handleChange}
              bind:value={$form.options[index]}
            />
            <button
              type="button"
              tabindex="-1"
              on:click|preventDefault={() => removeOption(index)}
              class="px-2">x</button
            >
          </div>
          {#if $errors.options[index]}
            <small>{$errors.options[index]}</small>
          {/if}
        {/each}
        <button
          tabindex="-1"
          class="p-2 hover:shadow shadow-black dark:shadow-white bg-white dark:bg-black border-black dark:border-white dark:text-white border-2 rounded-xl my-2"
          on:click|preventDefault={addOption}
        >
          add option
        </button>
        {#if !($form.options.length > 0)}
          <ErrorMessage name="options" />
        {/if}
        <Button disabled={loading} style={"mt-10"}>
          {#if loading}
            <img class="h-6 block" src={spinner} alt="loading animation" />
          {:else}
            submit proposal →
          {/if}
        </Button>
        <p>{$store.error}</p>
      </div>
    </div>
  </Card>
</Form>

What is the current bug behavior?

What is the expected correct behavior?

Relevant logs and/or screenshots

Possible fixes

@letmejustputthishere letmejustputthishere added the bug Something isn't working label May 5, 2022
@workshur
Copy link

workshur commented May 6, 2022

And type of such error field is string even if the schema field is array of objects

const serversSchema = object({
  host: string().label('host').required('required'),
  port: number().max(65535, '65535 is the max').positive('positive only').required('required'),
});

const schema = object({
  servers: array().of(serversSchema),
});

Type of $errors.servers is string.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants