Ctrl K
ComunidadGuías…

packages/app/component-tests/input-surfaces.spec.ts

Rama dev · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { expect, story } from "../../stories/playwright/story"

for (const control of ["text-input", "textarea"]) {
  story(`${control} supports its surfaces with inset feedback and stable geometry`, async ({ mount, page }) => {
    const root = await mount(`ui-${control}--surfaces`)

    const fields = root.locator(`[data-component="${control}-v2"]`)

    await page.addStyleTag({
      content: `[data-component="${control}-v2"] {
        --v2-background-bg-base: rgb(20, 30, 40);
        --v2-background-bg-layer-01: rgb(50, 60, 70);
        --v2-overlay-simple-overlay-hover: rgba(10, 20, 30, 0.1);
        --v2-elevation-input: 0px 0px 0px 3px rgb(1, 2, 3);
        --v2-elevation-button-neutral: 0px 0px 0px 7px rgb(4, 5, 6);
      }`,
    })

    await expect(fields.nth(0)).toHaveCSS("background-color", "rgb(20, 30, 40)")
    await expect(fields.nth(1)).toHaveCSS("background-color", "rgb(50, 60, 70)")
    await expect(fields.nth(0)).toHaveCSS("box-shadow", "rgb(1, 2, 3) 0px 0px 0px 3px")
    await expect(fields.nth(1)).toHaveCSS("box-shadow", "rgb(1, 2, 3) 0px 0px 0px 3px")

    for (const field of await fields.all()) {
      await expect(field).toHaveCSS("background-image", "none")
    }

    const base = fields.nth(0)

    const geometry = await base.getByRole("textbox").boundingBox()

    await base.hover()
    await expect
      .poll(() => base.evaluate((element) => getComputedStyle(element).boxShadow))
      .toContain("rgb(1, 2, 3) 0px 0px 0px 3px")
    await base.getByRole("textbox").fill("Still editable")
    await expect(base.getByRole("textbox")).toHaveValue("Still editable")
    await expect(base).toHaveCSS("outline-width", "1px")
    await expect(base).toHaveCSS("outline-offset", "-1px")
    await expect(base).not.toHaveCSS("outline-color", "rgba(0, 0, 0, 0)")
    expect(await base.getByRole("textbox").boundingBox()).toEqual(geometry)

    const invalid = root.locator("[data-invalid]")

    const disabled = root.locator("[data-disabled]")

    await expect(invalid).toHaveCSS("outline-width", "1px")
    await expect(invalid).toHaveCSS("outline-offset", "-1px")
    await expect(invalid).not.toHaveCSS("outline-color", "rgba(0, 0, 0, 0)")
    await expect(invalid.getByRole("textbox")).toHaveAttribute("aria-invalid", "true")
    await expect(disabled.getByRole("textbox")).toBeDisabled()

    if (control === "textarea") return

    const invisible = root.locator('[data-surface="invisible"]').first()

    await base.getByRole("textbox").blur()
    await expect(invisible).toHaveCSS("background-color", "rgba(0, 0, 0, 0)")
    await invisible.hover()
    await expect(invisible).toHaveCSS("background-color", "rgba(10, 20, 30, 0.1)")
    await expect(invisible).toHaveCSS("box-shadow", "none")
    await invisible.getByRole("textbox").focus()
    await expect(invisible).toHaveCSS("background-color", "rgba(0, 0, 0, 0)")
    await expect(invisible).toHaveCSS("outline-offset", "-1px")
    await expect(invisible).not.toHaveCSS("outline-color", "rgba(0, 0, 0, 0)")
    await expect(invalid).toHaveCSS("background-color", "rgba(0, 0, 0, 0)")
    await expect(disabled).toHaveCSS("background-color", "rgba(0, 0, 0, 0)")
    await expect(disabled).toHaveCSS("box-shadow", "none")
  })
}

story("select fields match the text input surfaces with inset feedback", async ({ mount, page }) => {
  const root = await mount("ui-select--surfaces")

  const fields = root.locator('[data-component="select-v2"]')

  await page.addStyleTag({
    content: `[data-component="select-v2"] {
      --v2-background-bg-base: rgb(20, 30, 40);
      --v2-background-bg-layer-01: rgb(50, 60, 70);
      --v2-overlay-simple-overlay-hover: rgba(10, 20, 30, 0.1);
      --v2-elevation-input: 0px 0px 0px 3px rgb(1, 2, 3);
      --v2-elevation-button-neutral: 0px 0px 0px 7px rgb(4, 5, 6);
    }`,
  })

  const base = fields.nth(0)

  const invisible = fields.nth(2)

  await expect(base).toHaveCSS("background-color", "rgb(20, 30, 40)")
  await expect(fields.nth(1)).toHaveCSS("background-color", "rgb(50, 60, 70)")
  await expect(base).toHaveCSS("box-shadow", "rgb(1, 2, 3) 0px 0px 0px 3px")
  await expect(base).toHaveCSS("background-image", "none")
  await expect(invisible).toHaveCSS("background-color", "rgba(0, 0, 0, 0)")
  await expect(invisible).toHaveCSS("box-shadow", "none")

  await base.hover()
  await expect
    .poll(() => base.evaluate((element) => getComputedStyle(element).boxShadow))
    .toContain("rgb(1, 2, 3) 0px 0px 0px 3px")
  await expect(base).toHaveCSS("background-color", "rgb(20, 30, 40)")

  await invisible.hover()
  await expect(invisible).toHaveCSS("background-color", "rgba(10, 20, 30, 0.1)")
  await expect(invisible).toHaveCSS("box-shadow", "none")

  const invalid = root.locator("[data-invalid]")

  await expect(invalid).toHaveCSS("outline-width", "1px")
  await expect(invalid).toHaveCSS("outline-offset", "-1px")
  await expect(invalid).not.toHaveCSS("outline-color", "rgba(0, 0, 0, 0)")
})

story("select fields come in the text input sizes and default to the inline trigger", async ({ mount }) => {
  const root = await mount("ui-select--appearances")

  const fields = root.locator('[data-component="select-v2"]')

  for (const [index, height] of [24, 28, 32].entries()) {
    await expect(fields.nth(index)).toHaveCSS("height", `${height}px`)
  }

  await expect(fields.nth(3)).toHaveAttribute("data-appearance", "inline")
  await expect(fields.nth(3)).toHaveCSS("box-shadow", "none")
})

story(
  "a select previews the highlighted option, reverts it on Escape, and keeps the chosen one",
  async ({ mount, page }) => {
    const root = await mount("ui-select--highlight-preview")
    const trigger = root.locator('[data-component="select-v2"]')
    const preview = root.locator('[data-slot="select-preview"]')

    await trigger.click()
    await page.getByRole("option", { name: "Cherry", exact: true }).hover()
    await expect(preview).toHaveText("Showing Cherry")
    await page.keyboard.press("Escape")
    await expect(page.getByRole("listbox")).toHaveCount(0)
    await expect(preview).toHaveText("Showing Apple")

    await trigger.click()
    const banana = page.getByRole("option", { name: "Banana", exact: true })
    await banana.hover()
    await expect(preview).toHaveText("Showing Banana")
    await banana.click()
    await expect(page.getByRole("listbox")).toHaveCount(0)
    await expect(trigger).toContainText("Banana")
    // Choosing the previewed option must not run its undo afterwards.
    await expect(preview).toHaveText("Showing Banana")
  },
)

story("compact checkboxes use the design system control states and focus ring", async ({ mount, page }) => {
  const root = await mount("ui-checkbox--compact")

  await page.addStyleTag({
    content: `[data-slot="checkbox-v2"] {
      --v2-background-bg-base: rgb(20, 30, 40);
      --v2-background-bg-layer-01: rgb(50, 60, 70);
      --v2-background-bg-accent: rgb(1, 2, 3);
      --v2-state-fg-danger: rgb(7, 8, 9);
    }`,
  })

  const controls = root.locator('[data-slot="checkbox-v2-control"]')

  await expect(controls.nth(0)).toHaveCSS("background-color", "rgb(20, 30, 40)")
  await expect(controls.nth(0)).toHaveCSS("background-image", "none")
  await expect(controls.nth(1)).toHaveCSS("background-color", "rgb(1, 2, 3)")
  await expect(controls.nth(1)).not.toHaveCSS("background-image", "none")
  await expect(controls.nth(2)).toHaveCSS("background-color", "rgb(1, 2, 3)")
  await expect(controls.nth(3)).toHaveCSS("box-shadow", "rgb(7, 8, 9) 0px 0px 0px 0.5px inset")
  await expect(controls.nth(6)).toHaveCSS("background-color", "rgb(50, 60, 70)")

  await page.keyboard.press("Tab")
  await expect(root.getByRole("checkbox", { name: "Default" })).toBeFocused()
  await expect(controls.nth(0)).toHaveCSS("outline-width", "2px")
  await expect(controls.nth(0)).toHaveCSS("outline-offset", "2px")
})

story(
  "text inputs keep clear and copy actions equally sized and inset across sizes and directions",
  async ({ mount, page }) => {
    const root = await mount("ui-text-input--appearances")

    const fields = root.locator('[data-component="text-input-v2"]')

    for (const [index, height] of [24, 28, 32, 32, 24, 28, 32].entries()) {
      await expect(fields.nth(index)).toHaveCSS("height", `${height}px`)
    }

    for (const direction of ["ltr", "rtl"]) {
      await page.evaluate((dir) => (document.documentElement.dir = dir), direction)

      for (const field of await fields.filter({ has: page.getByRole("button") }).all()) {
        const button = field.getByRole("button")

        await expect(button).toHaveCSS("width", "20px")
        await expect(button).toHaveCSS("height", "20px")
        await expect(button).toHaveCSS("padding", "2px")
        await expect(button).toHaveCSS("border-radius", "4px")

        expect(
          await field.evaluate((element) => {
            const bounds = element.getBoundingClientRect()

            const action = element.querySelector("button")!.getBoundingClientRect()

            return {
              inset:
                getComputedStyle(element).direction === "rtl" ? action.left - bounds.left : bounds.right - action.right,
              center: action.y + action.height / 2 - bounds.y - bounds.height / 2,
            }
          }),
        ).toEqual({ inset: 8, center: 0 })
      }
    }
  },
)