Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,44 @@ describe('InputText', () => {
})
})

describe('clickable area', () => {
const pressOn = (element: Element) =>
element.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }))

it('focuses the input when the press lands on the wrapper chrome (padding band)', () => {
const { getByTestId } = render(InputText, { props: { size: 'large' } })
const input = getByTestId('input-text') as HTMLInputElement
const wrapper = input.closest('[data-size]') as HTMLElement
const notPrevented = pressOn(wrapper)
expect(document.activeElement).toBe(input)
expect(notPrevented).toBe(false)
})

it('focuses the input when the press lands on a decorative icon', () => {
const { getByTestId, getByText } = render(InputText, {
props: { size: 'large' },
slots: { iconLeft: '<i>L</i>' }
})
const input = getByTestId('input-text') as HTMLInputElement
pressOn(getByText('L'))
expect(document.activeElement).toBe(input)
})

it('leaves a press on the input itself untouched so the native caret still lands', () => {
const { getByTestId } = render(InputText, { props: { modelValue: 'azion' } })
const input = getByTestId('input-text') as HTMLInputElement
expect(pressOn(input)).toBe(true)
})

it('does not focus from the wrapper chrome when disabled', () => {
const { getByTestId } = render(InputText, { props: { size: 'large', disabled: true } })
const input = getByTestId('input-text') as HTMLInputElement
const wrapper = input.closest('[data-size]') as HTMLElement
expect(pressOn(wrapper)).toBe(true)
expect(document.activeElement).not.toBe(input)
})
})

describe('a11y (axe against styled DOM)', () => {
it('Default has no violations', async () => {
const { container } = render(InputText, {
Expand Down
19 changes: 15 additions & 4 deletions packages/webkit/src/components/inputs/input-text/input-text.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, useAttrs, useSlots } from 'vue'
import { computed, ref, useAttrs, useSlots } from 'vue'

export type InputTextSize = 'small' | 'medium' | 'large'
export type InputTextType = 'text' | 'email' | 'number'
Expand Down Expand Up @@ -30,7 +30,7 @@
invalid?: boolean
}

withDefaults(defineProps<Props>(), {
const props = withDefaults(defineProps<Props>(), {
modelValue: '',
placeholder: '',
size: 'medium',
Expand All @@ -54,6 +54,8 @@
const attrs = useAttrs()
const slots = useSlots()

const inputRef = ref<globalThis.HTMLInputElement | null>(null)

const testId = computed(() => (attrs['data-testid'] as string | undefined) ?? 'input-text')

const passthroughAttrs = computed(() => {
Expand All @@ -70,6 +72,13 @@
const target = event.target as globalThis.HTMLInputElement
emit('update:modelValue', target.value)
}

const handleChromeMouseDown = (event: globalThis.MouseEvent) => {
if (props.disabled) return
if (event.target === inputRef.value) return
event.preventDefault()
inputRef.value?.focus()
}
</script>

<template>
Expand All @@ -81,7 +90,7 @@
:data-has-icon-left="hasIconLeft || null"
:data-has-icon-right="hasIconRight || null"
:class="[
'relative inline-flex items-center w-full',
'relative inline-flex items-center w-full cursor-text',
'gap-[var(--spacing-xs)] px-[var(--spacing-sm)]',
'rounded-[var(--shape-elements)]',
'border border-[var(--border-default)] bg-[var(--bg-surface)] text-[var(--text-default)]',
Expand All @@ -94,6 +103,7 @@
'data-[disabled]:bg-[var(--bg-disabled)] data-[disabled]:text-[var(--text-disabled)] data-[disabled]:cursor-not-allowed data-[disabled]:hover:border-[var(--border-default)] data-[disabled]:focus-within:ring-0 data-[disabled]:focus-within:ring-offset-0',
attrs.class
]"
@mousedown="handleChromeMouseDown"
>
<span
v-if="hasIconLeft"
Expand All @@ -104,6 +114,7 @@
</span>

<input
ref="inputRef"
:type="type"
:value="modelValue"
:placeholder="placeholder"
Expand All @@ -114,7 +125,7 @@
:aria-invalid="invalid || undefined"
:aria-required="required || undefined"
:data-testid="testId"
class="relative z-[var(--z-input-field)] w-full min-w-0 border-0 bg-transparent p-0 outline-none text-label-sm text-[var(--text-default)] placeholder:text-[var(--text-muted)] disabled:cursor-not-allowed disabled:text-[var(--text-disabled)] read-only:cursor-default"
class="relative z-[var(--z-input-field)] h-full w-full min-w-0 self-stretch border-0 bg-transparent p-0 outline-none text-label-sm text-[var(--text-default)] placeholder:text-[var(--text-muted)] disabled:cursor-not-allowed disabled:text-[var(--text-disabled)] read-only:cursor-default"
v-bind="passthroughAttrs"
@input="handleInput"
/>
Expand Down
Loading