@vielzeug/refine 1.5.1 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/async.cjs +9 -9
- package/dist/async.cjs.map +1 -1
- package/dist/async.js +30 -31
- package/dist/async.js.map +1 -1
- package/dist/button.cjs +6 -6
- package/dist/button.cjs.map +1 -1
- package/dist/button.js +29 -30
- package/dist/button.js.map +1 -1
- package/dist/calendar.cjs +44 -44
- package/dist/calendar.cjs.map +1 -1
- package/dist/calendar.js +115 -117
- package/dist/calendar.js.map +1 -1
- package/dist/checkbox-group.cjs +7 -7
- package/dist/checkbox-group.cjs.map +1 -1
- package/dist/checkbox-group.js +71 -73
- package/dist/checkbox-group.js.map +1 -1
- package/dist/checkbox.cjs +3 -3
- package/dist/checkbox.cjs.map +1 -1
- package/dist/checkbox.js +28 -29
- package/dist/checkbox.js.map +1 -1
- package/dist/code-window.cjs +7 -7
- package/dist/code-window.cjs.map +1 -1
- package/dist/code-window.js +17 -18
- package/dist/code-window.js.map +1 -1
- package/dist/combobox.cjs +1 -1
- package/dist/combobox.cjs.map +1 -1
- package/dist/combobox.js +11 -12
- package/dist/combobox.js.map +1 -1
- package/dist/custom-elements.json +5267 -5267
- package/dist/datagrid.cjs +66 -66
- package/dist/datagrid.cjs.map +1 -1
- package/dist/datagrid.js +267 -268
- package/dist/datagrid.js.map +1 -1
- package/dist/date-picker.cjs +11 -11
- package/dist/date-picker.cjs.map +1 -1
- package/dist/date-picker.js +8 -9
- package/dist/date-picker.js.map +1 -1
- package/dist/file-input.cjs +6 -6
- package/dist/file-input.cjs.map +1 -1
- package/dist/file-input.js +50 -51
- package/dist/file-input.js.map +1 -1
- package/dist/grid.cjs +1 -1
- package/dist/grid.cjs.map +1 -1
- package/dist/grid.js +47 -48
- package/dist/grid.js.map +1 -1
- package/dist/icon.cjs +1 -1
- package/dist/icon.cjs.map +1 -1
- package/dist/icon.js +26 -27
- package/dist/icon.js.map +1 -1
- package/dist/input.cjs +20 -20
- package/dist/input.cjs.map +1 -1
- package/dist/input.js +98 -100
- package/dist/input.js.map +1 -1
- package/dist/message-composer.cjs +18 -18
- package/dist/message-composer.cjs.map +1 -1
- package/dist/message-composer.js +113 -115
- package/dist/message-composer.js.map +1 -1
- package/dist/navbar.cjs +11 -11
- package/dist/navbar.cjs.map +1 -1
- package/dist/navbar.js +78 -79
- package/dist/navbar.js.map +1 -1
- package/dist/otp-input.cjs +4 -4
- package/dist/otp-input.cjs.map +1 -1
- package/dist/otp-input.js +31 -32
- package/dist/otp-input.js.map +1 -1
- package/dist/progress.cjs +13 -13
- package/dist/progress.cjs.map +1 -1
- package/dist/progress.js +45 -46
- package/dist/progress.js.map +1 -1
- package/dist/radio-group.cjs +5 -5
- package/dist/radio-group.cjs.map +1 -1
- package/dist/radio-group.js +83 -85
- package/dist/radio-group.js.map +1 -1
- package/dist/radio.cjs +3 -3
- package/dist/radio.cjs.map +1 -1
- package/dist/radio.js +27 -28
- package/dist/radio.js.map +1 -1
- package/dist/rating.cjs +4 -4
- package/dist/rating.cjs.map +1 -1
- package/dist/rating.js +30 -31
- package/dist/rating.js.map +1 -1
- package/dist/select.cjs +4 -4
- package/dist/select.cjs.map +1 -1
- package/dist/select.js +88 -89
- package/dist/select.js.map +1 -1
- package/dist/sidebar.cjs +11 -11
- package/dist/sidebar.cjs.map +1 -1
- package/dist/sidebar.js +97 -98
- package/dist/sidebar.js.map +1 -1
- package/dist/skeleton.cjs +3 -3
- package/dist/skeleton.cjs.map +1 -1
- package/dist/skeleton.js +23 -24
- package/dist/skeleton.js.map +1 -1
- package/dist/slider.cjs +3 -3
- package/dist/slider.cjs.map +1 -1
- package/dist/slider.js +65 -66
- package/dist/slider.js.map +1 -1
- package/dist/switch.cjs +3 -3
- package/dist/switch.cjs.map +1 -1
- package/dist/switch.js +23 -24
- package/dist/switch.js.map +1 -1
- package/dist/tab-panel.cjs +7 -7
- package/dist/tab-panel.cjs.map +1 -1
- package/dist/tab-panel.js +21 -23
- package/dist/tab-panel.js.map +1 -1
- package/dist/testing.cjs +1 -1
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js +4 -5
- package/dist/testing.js.map +1 -1
- package/dist/textarea.cjs +13 -13
- package/dist/textarea.cjs.map +1 -1
- package/dist/textarea.js +92 -94
- package/dist/textarea.js.map +1 -1
- package/dist/time-picker.cjs +9 -9
- package/dist/time-picker.cjs.map +1 -1
- package/dist/time-picker.js +47 -48
- package/dist/time-picker.js.map +1 -1
- package/package.json +1 -1
- package/dist/ore/dist/_dev.cjs +0 -2
- package/dist/ore/dist/_dev.cjs.map +0 -1
- package/dist/ore/dist/_dev.js +0 -12
- package/dist/ore/dist/_dev.js.map +0 -1
- package/dist/ore/dist/base-element.cjs +0 -2
- package/dist/ore/dist/base-element.cjs.map +0 -1
- package/dist/ore/dist/base-element.js +0 -158
- package/dist/ore/dist/base-element.js.map +0 -1
- package/dist/ore/dist/context.cjs +0 -2
- package/dist/ore/dist/context.cjs.map +0 -1
- package/dist/ore/dist/context.js +0 -11
- package/dist/ore/dist/context.js.map +0 -1
- package/dist/ore/dist/define.cjs +0 -2
- package/dist/ore/dist/define.cjs.map +0 -1
- package/dist/ore/dist/define.js +0 -27
- package/dist/ore/dist/define.js.map +0 -1
- package/dist/ore/dist/directives/classMap.cjs +0 -1
- package/dist/ore/dist/directives/classMap.js +0 -1
- package/dist/ore/dist/directives/each.cjs +0 -1
- package/dist/ore/dist/directives/each.js +0 -5
- package/dist/ore/dist/directives/live.cjs +0 -2
- package/dist/ore/dist/directives/live.cjs.map +0 -1
- package/dist/ore/dist/directives/live.js +0 -6
- package/dist/ore/dist/directives/live.js.map +0 -1
- package/dist/ore/dist/directives/model.cjs +0 -1
- package/dist/ore/dist/directives/model.js +0 -3
- package/dist/ore/dist/directives/raw.cjs +0 -2
- package/dist/ore/dist/directives/raw.cjs.map +0 -1
- package/dist/ore/dist/directives/raw.js +0 -38
- package/dist/ore/dist/directives/raw.js.map +0 -1
- package/dist/ore/dist/directives/styleMap.cjs +0 -2
- package/dist/ore/dist/directives/styleMap.cjs.map +0 -1
- package/dist/ore/dist/directives/styleMap.js +0 -20
- package/dist/ore/dist/directives/styleMap.js.map +0 -1
- package/dist/ore/dist/directives/when.cjs +0 -2
- package/dist/ore/dist/directives/when.cjs.map +0 -1
- package/dist/ore/dist/directives/when.js +0 -38
- package/dist/ore/dist/directives/when.js.map +0 -1
- package/dist/ore/dist/directives.cjs +0 -1
- package/dist/ore/dist/directives.js +0 -8
- package/dist/ore/dist/errors.cjs +0 -3
- package/dist/ore/dist/errors.cjs.map +0 -1
- package/dist/ore/dist/errors.js +0 -50
- package/dist/ore/dist/errors.js.map +0 -1
- package/dist/ore/dist/forms/context.cjs +0 -2
- package/dist/ore/dist/forms/context.cjs.map +0 -1
- package/dist/ore/dist/forms/context.js +0 -6
- package/dist/ore/dist/forms/context.js.map +0 -1
- package/dist/ore/dist/forms/field.cjs +0 -2
- package/dist/ore/dist/forms/field.cjs.map +0 -1
- package/dist/ore/dist/forms/field.js +0 -42
- package/dist/ore/dist/forms/field.js.map +0 -1
- package/dist/ore/dist/forms.cjs +0 -1
- package/dist/ore/dist/forms.js +0 -3
- package/dist/ore/dist/observers/intersection-observe.cjs +0 -2
- package/dist/ore/dist/observers/intersection-observe.cjs.map +0 -1
- package/dist/ore/dist/observers/intersection-observe.js +0 -12
- package/dist/ore/dist/observers/intersection-observe.js.map +0 -1
- package/dist/ore/dist/observers/media-observe.cjs +0 -1
- package/dist/ore/dist/observers/media-observe.js +0 -1
- package/dist/ore/dist/observers/mutation-observe.cjs +0 -1
- package/dist/ore/dist/observers/mutation-observe.js +0 -1
- package/dist/ore/dist/observers/resize-observe.cjs +0 -2
- package/dist/ore/dist/observers/resize-observe.cjs.map +0 -1
- package/dist/ore/dist/observers/resize-observe.js +0 -20
- package/dist/ore/dist/observers/resize-observe.js.map +0 -1
- package/dist/ore/dist/observers.cjs +0 -1
- package/dist/ore/dist/observers.js +0 -5
- package/dist/ore/dist/props.cjs +0 -2
- package/dist/ore/dist/props.cjs.map +0 -1
- package/dist/ore/dist/props.js +0 -56
- package/dist/ore/dist/props.js.map +0 -1
- package/dist/ore/dist/runtime.cjs +0 -2
- package/dist/ore/dist/runtime.cjs.map +0 -1
- package/dist/ore/dist/runtime.js +0 -25
- package/dist/ore/dist/runtime.js.map +0 -1
- package/dist/ore/dist/testing/flush.cjs +0 -2
- package/dist/ore/dist/testing/flush.cjs.map +0 -1
- package/dist/ore/dist/testing/flush.js +0 -11
- package/dist/ore/dist/testing/flush.js.map +0 -1
- package/dist/ore/dist/testing/mount.cjs +0 -2
- package/dist/ore/dist/testing/mount.cjs.map +0 -1
- package/dist/ore/dist/testing/mount.js +0 -94
- package/dist/ore/dist/testing/mount.js.map +0 -1
- package/dist/ore/dist/testing/query.cjs +0 -2
- package/dist/ore/dist/testing/query.cjs.map +0 -1
- package/dist/ore/dist/testing/query.js +0 -12
- package/dist/ore/dist/testing/query.js.map +0 -1
- package/dist/ore/dist/testing/render-hook.cjs +0 -1
- package/dist/ore/dist/testing/render-hook.js +0 -3
- package/dist/ore/dist/testing/testing.cjs +0 -1
- package/dist/ore/dist/testing/testing.js +0 -4
- package/dist/ore/dist/testing/wait.cjs +0 -1
- package/dist/ore/dist/testing/wait.js +0 -1
- package/dist/ore/dist/testing.cjs +0 -1
- package/dist/ore/dist/testing.js +0 -6
- package/dist/ore/dist/types/bindings.cjs +0 -2
- package/dist/ore/dist/types/bindings.cjs.map +0 -1
- package/dist/ore/dist/types/bindings.js +0 -15
- package/dist/ore/dist/types/bindings.js.map +0 -1
- package/dist/ore/dist/types.cjs +0 -2
- package/dist/ore/dist/types.cjs.map +0 -1
- package/dist/ore/dist/types.js +0 -15
- package/dist/ore/dist/types.js.map +0 -1
- package/dist/ore/dist/utils/css.cjs +0 -2
- package/dist/ore/dist/utils/css.cjs.map +0 -1
- package/dist/ore/dist/utils/css.js +0 -18
- package/dist/ore/dist/utils/css.js.map +0 -1
- package/dist/ore/dist/utils/dom.cjs +0 -2
- package/dist/ore/dist/utils/dom.cjs.map +0 -1
- package/dist/ore/dist/utils/dom.js +0 -43
- package/dist/ore/dist/utils/dom.js.map +0 -1
- package/dist/ore/dist/utils/id.cjs +0 -2
- package/dist/ore/dist/utils/id.cjs.map +0 -1
- package/dist/ore/dist/utils/id.js +0 -4
- package/dist/ore/dist/utils/id.js.map +0 -1
package/dist/testing.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.cjs","names":[],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * @vielzeug/refine/testing\n *\n * Testing utilities for refine components. Provides a11y helpers, ARIA query\n * utilities, and convenience wrappers for common patterns in component tests.\n *\n * @example\n * ```typescript\n * import { getAriaLabel, getAriaDescribedBy, isAriaInvalid } from '@vielzeug/refine/testing';\n * import { mount } from '@vielzeug/ore/testing';\n *\n * const fixture = mount('<ore-input label=\"Name\"></ore-input>');\n * const input = fixture.query('input')!;\n * expect(isAriaInvalid(input)).toBe(false);\n * ```\n */\n\n// ── ARIA attribute helpers ────────────────────────────────────────────────────\n\n/** Returns whether `aria-invalid=\"true\"` is set on the element. */\nexport const isAriaInvalid = (el: Element): boolean => el.getAttribute('aria-invalid') === 'true';\n\n/** Returns whether `aria-disabled=\"true\"` is set on the element. */\nexport const isAriaDisabled = (el: Element): boolean => el.getAttribute('aria-disabled') === 'true';\n\n/** Returns whether `aria-checked=\"true\"` is set on the element. */\nexport const isAriaChecked = (el: Element): boolean => el.getAttribute('aria-checked') === 'true';\n\n/** Returns whether `aria-checked=\"mixed\"` is set on the element. */\nexport const isAriaIndeterminate = (el: Element): boolean => el.getAttribute('aria-checked') === 'mixed';\n\n/** Returns whether `aria-expanded=\"true\"` is set on the element. */\nexport const isAriaExpanded = (el: Element): boolean => el.getAttribute('aria-expanded') === 'true';\n\n/** Returns whether `aria-pressed=\"true\"` is set on the element. */\nexport const isAriaPressed = (el: Element): boolean => el.getAttribute('aria-pressed') === 'true';\n\n/** Returns whether `aria-required=\"true\"` is set on the element. */\nexport const isAriaRequired = (el: Element): boolean => el.getAttribute('aria-required') === 'true';\n\n/** Returns whether `aria-hidden=\"true\"` is set on the element. */\nexport const isAriaHidden = (el: Element): boolean => el.getAttribute('aria-hidden') === 'true';\n\n/** Returns the `aria-label` string attribute, or null. */\nexport const getAriaLabel = (el: Element): null | string => el.getAttribute('aria-label');\n\n/** Returns the `aria-labelledby` attribute value (space-separated IDs), or null. */\nexport const getAriaLabelledBy = (el: Element): null | string => el.getAttribute('aria-labelledby');\n\n/** Returns the `aria-describedby` attribute value (space-separated IDs), or null. */\nexport const getAriaDescribedBy = (el: Element): null | string => el.getAttribute('aria-describedby');\n\n/** Returns the `aria-controls` attribute value, or null. */\nexport const getAriaControls = (el: Element): null | string => el.getAttribute('aria-controls');\n\n/** Returns the `role` attribute value, or null. */\nexport const getRole = (el: Element): null | string => el.getAttribute('role');\n\n// ── DOM query helpers ─────────────────────────────────────────────────────────\n\n/**\n * Queries the shadow root of a custom element for a matching CSS selector.\n * Returns null if the element has no shadow root or no match is found.\n */\nexport const queryInShadow = <T extends Element = Element>(host: Element, selector: string): null | T => {\n return host.shadowRoot?.querySelector<T>(selector) ?? null;\n};\n\n/**\n * Queries all matching elements inside the shadow root of a custom element.\n * Returns an empty array if the element has no shadow root.\n */\nexport const queryAllInShadow = <T extends Element = Element>(host: Element, selector: string): T[] => {\n return Array.from(host.shadowRoot?.querySelectorAll<T>(selector) ?? []);\n};\n\n/**\n * Queries a shadow DOM element by its CSS `part` attribute.\n * Shorthand for `queryInShadow(host, '[part=\"name\"]')`.\n *\n * @example\n * ```ts\n * const btn = queryPart(carousel, 'prev-btn');\n * expect(btn).not.toBeNull();\n * ```\n */\nexport const queryPart = <T extends Element = Element>(host: Element, part: string): null | T =>\n queryInShadow<T>(host, `[part=\"${part}\"]`);\n\n/**\n * Returns an array of light-DOM children assigned to a named slot,\n * or all slotted children if no name is given.\n *\n * @example\n * ```ts\n * const slides = getSlotted(carousel);\n * expect(slides).toHaveLength(3);\n * ```\n */\nexport const getSlotted = (host: Element, slotName?: string): Element[] => {\n const selector = slotName ? `[slot=\"${slotName}\"]` : ':not([slot])';\n\n return Array.from(host.querySelectorAll(`:scope > ${selector}`));\n};\n\n/**\n * Dispatches a `PointerEvent` on the given element.\n * Useful for testing hover, drag, and pointer interactions without\n * relying on the fire helper from @vielzeug/ore/testing.\n *\n * @example\n * ```ts\n * dispatchPointer(el, 'enter');\n * dispatchPointer(el, 'leave');\n * ```\n */\nexport const dispatchPointer = (\n el: Element,\n type: 'cancel' | 'down' | 'enter' | 'leave' | 'move' | 'up',\n init?: PointerEventInit,\n): void => {\n el.dispatchEvent(new PointerEvent(`pointer${type}`, { bubbles: true, composed: true, ...init }));\n};\n\n// ── Form-associated component helpers ─────────────────────────────────────────\n\n/**\n * Returns the current form value of a form-associated custom element.\n * Uses the ElementInternals-exposed `value` if available, then falls back\n * to the reflected DOM `value` property.\n */\nexport const getFormValue = (el: HTMLElement & { value?: string }): null | string => {\n return el.value ?? null;\n};\n\n/**\n * Returns whether a form-associated element is considered valid (no constraint\n * violations reported by the browser or via `setValidity`).\n */\nexport const isFormValid = (el: HTMLElement & { validity?: ValidityState }): boolean => {\n return el.validity?.valid ?? true;\n};\n\n// ── Event helpers ─────────────────────────────────────────────────────────────\n\n/**\n * Creates a synthetic `KeyboardEvent` with the given key.\n * Useful for testing keyboard navigation in headless controls.\n */\nexport const keyEvent = (key: string, init?: KeyboardEventInit): KeyboardEvent => {\n return new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key, ...init });\n};\n\n/**\n * Returns a Promise that resolves after a microtask tick (one `queueMicrotask`).\n * Use when you need to wait for reactivity (signal effects) to settle without\n * moving into the macrotask queue.\n */\nexport const nextTick = (): Promise<void> =>\n new Promise((resolve) => {\n queueMicrotask(resolve);\n });\n\n/**\n * Returns a Promise that resolves after `ms` milliseconds.\n * Use sparingly — prefer `nextTick()` for reactive updates.\n */\nexport const wait = (ms = 0): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\n// ── ID counter ────────────────────────────────────────────────────────────────\n\n/**\n * Resets the headless ID counter to 0. Use in test `beforeEach` hooks when you need\n * deterministic IDs across test runs.\n *\n * @example\n * ```ts\n * import { resetIdCounter } from '@vielzeug/refine/testing';\n * beforeEach(() => resetIdCounter());\n * ```\n */\nexport { resetIdCounter } from '@vielzeug/ore';\n\n// ── ARIA snapshot helper ───────────────────────────────────────────────────────\n\n/**\n * Returns a snapshot of the most commonly tested ARIA attributes on an element.\n * Useful for concise inline snapshot assertions in component tests.\n *\n * `ariaLabel` and `labelledby` are kept separate because `aria-label` is a\n * direct string while `aria-labelledby` is an ID reference — conflating them\n * masks bugs where the wrong labelling mechanism is used.\n *\n * @example\n * ```ts\n * expect(getAriaState(input)).toMatchObject({ invalid: 'true', required: 'true' });\n * ```\n */\nexport const getAriaState = (\n el: Element,\n): {\n ariaLabel: null | string;\n checked: null | string;\n disabled: null | string;\n expanded: null | string;\n invalid: null | string;\n labelledby: null | string;\n required: null | string;\n role: null | string;\n} => ({\n ariaLabel: el.getAttribute('aria-label'),\n checked: el.getAttribute('aria-checked'),\n disabled: el.getAttribute('aria-disabled'),\n expanded: el.getAttribute('aria-expanded'),\n invalid: el.getAttribute('aria-invalid'),\n labelledby: el.getAttribute('aria-labelledby'),\n required: el.getAttribute('aria-required'),\n role: el.getAttribute('role'),\n});\n\n// ── Typed component mount wrappers (F3) ───────────────────────────────────────\n\nimport type { Fixture } from '@vielzeug/ore/testing';\n\nimport { mount } from '@vielzeug/ore/testing';\n\nimport type { OreButtonGroupProps } from '../inputs/button-group/button-group';\nimport type { OreButtonProps } from '../inputs/button/button';\nimport type { OreCheckboxGroupProps } from '../inputs/checkbox-group/checkbox-group';\nimport type { OreCheckboxProps } from '../inputs/checkbox/checkbox';\nimport type { OreComboboxProps } from '../inputs/combobox/combobox.types';\nimport type { OreFileInputProps } from '../inputs/file-input/file-input';\nimport type { OreFormProps } from '../inputs/form/form';\nimport type { OreInputProps } from '../inputs/input/input';\nimport type { OreNumberInputProps } from '../inputs/number-input/number-input';\nimport type { OreOtpInputProps } from '../inputs/otp-input/otp-input';\nimport type { OreRadioGroupProps } from '../inputs/radio-group/radio-group';\nimport type { OreRadioProps } from '../inputs/radio/radio';\nimport type { OreRatingProps } from '../inputs/rating/rating';\nimport type { OreSelectProps } from '../inputs/select/select';\nimport type { OreSliderProps } from '../inputs/slider/slider';\nimport type { OreSwitchProps } from '../inputs/switch/switch';\nimport type { OreTextareaProps } from '../inputs/textarea/textarea';\n\n/**\n * Serializes an attribute map to an HTML attribute string fragment.\n * Values are HTML-escaped to prevent attribute injection.\n *\n * @example\n * ```ts\n * attrsToHtml({ disabled: '', label: 'Name' }); // → 'disabled label=\"Name\"'\n * ```\n */\nexport const attrsToHtml = (attrs: Record<string, string>): string =>\n Object.entries(attrs)\n .map(([key, value]) => (value === '' ? key : `${key}=\"${value.replace(/\"/g, '"')}\"`))\n .join(' ');\n\n/**\n * Converts a props object into an HTML attribute record.\n * - `true` → empty boolean attribute (e.g., `disabled`)\n * - `false` / `null` / `undefined` → attribute omitted\n * - Array of objects → **omitted** (cannot be serialized as an HTML attribute;\n * use `opts.innerHTML` with slotted elements instead)\n * - Array of primitives → comma-joined string\n * - everything else → stringified\n *\n * @example\n * ```ts\n * propsToAttrs({ disabled: true, label: 'Name', value: undefined });\n * // → { disabled: '', label: 'Name' }\n * ```\n */\nexport const propsToAttrs = (props: Record<string, unknown> = {}): Record<string, string> => {\n const attrs: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(props)) {\n if (value === false || value == null) continue;\n\n if (value === true) {\n attrs[key] = '';\n } else if (Array.isArray(value)) {\n // Object arrays cannot be represented as HTML attributes — skip silently.\n if (value.length > 0 && typeof value[0] === 'object') continue;\n\n attrs[key] = (value as string[]).join(',');\n } else {\n attrs[key] = String(value);\n }\n }\n\n return attrs;\n};\n\ntype MountOptions = { innerHTML?: string };\n\n/** Typed mount wrapper for `<ore-input>`. Catch prop name typos at compile time. */\nexport const mountOreInput = (props?: Partial<OreInputProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-input ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-input>`);\n\n/** Typed mount wrapper for `<ore-textarea>`. */\nexport const mountOreTextarea = (props?: Partial<OreTextareaProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-textarea ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-textarea>`);\n\n/** Typed mount wrapper for `<ore-select>`. */\nexport const mountOreSelect = (props?: Partial<OreSelectProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-select ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-select>`);\n\n/** Typed mount wrapper for `<ore-combobox>`. */\nexport const mountOreCombobox = (props?: Partial<OreComboboxProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-combobox ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-combobox>`);\n\n/** Typed mount wrapper for `<ore-checkbox>`. */\nexport const mountOreCheckbox = (props?: Partial<OreCheckboxProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-checkbox ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-checkbox>`);\n\n/** Typed mount wrapper for `<ore-checkbox-group>`. */\nexport const mountOreCheckboxGroup = (props?: Partial<OreCheckboxGroupProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-checkbox-group ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-checkbox-group>`);\n\n/** Typed mount wrapper for `<ore-radio>`. */\nexport const mountOreRadio = (props?: Partial<OreRadioProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-radio ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-radio>`);\n\n/** Typed mount wrapper for `<ore-radio-group>`. */\nexport const mountOreRadioGroup = (props?: Partial<OreRadioGroupProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-radio-group ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-radio-group>`);\n\n/** Typed mount wrapper for `<ore-switch>`. */\nexport const mountOreSwitch = (props?: Partial<OreSwitchProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-switch ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-switch>`);\n\n/** Typed mount wrapper for `<ore-button>`. */\nexport const mountOreButton = (props?: Partial<OreButtonProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-button ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-button>`);\n\n/** Typed mount wrapper for `<ore-button-group>`. */\nexport const mountOreButtonGroup = (props?: Partial<OreButtonGroupProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-button-group ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-button-group>`);\n\n/** Typed mount wrapper for `<ore-file-input>`. */\nexport const mountOreFileInput = (props?: Partial<OreFileInputProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-file-input ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-file-input>`);\n\n/** Typed mount wrapper for `<ore-form>`. */\nexport const mountOreForm = (props?: Partial<OreFormProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-form ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-form>`);\n\n/** Typed mount wrapper for `<ore-number-input>`. */\nexport const mountOreNumberInput = (props?: Partial<OreNumberInputProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-number-input ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-number-input>`);\n\n/** Typed mount wrapper for `<ore-otp-input>`. */\nexport const mountOreOtpInput = (props?: Partial<OreOtpInputProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-otp-input ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-otp-input>`);\n\n/** Typed mount wrapper for `<ore-rating>`. */\nexport const mountOreRating = (props?: Partial<OreRatingProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-rating ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-rating>`);\n\n/** Typed mount wrapper for `<ore-slider>`. */\nexport const mountOreSlider = (props?: Partial<OreSliderProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-slider ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-slider>`);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"testing.cjs","names":[],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * @vielzeug/refine/testing\n *\n * Testing utilities for refine components. Provides a11y helpers, ARIA query\n * utilities, and convenience wrappers for common patterns in component tests.\n *\n * @example\n * ```typescript\n * import { getAriaLabel, getAriaDescribedBy, isAriaInvalid } from '@vielzeug/refine/testing';\n * import { mount } from '@vielzeug/ore/testing';\n *\n * const fixture = mount('<ore-input label=\"Name\"></ore-input>');\n * const input = fixture.query('input')!;\n * expect(isAriaInvalid(input)).toBe(false);\n * ```\n */\n\n// ── ARIA attribute helpers ────────────────────────────────────────────────────\n\n/** Returns whether `aria-invalid=\"true\"` is set on the element. */\nexport const isAriaInvalid = (el: Element): boolean => el.getAttribute('aria-invalid') === 'true';\n\n/** Returns whether `aria-disabled=\"true\"` is set on the element. */\nexport const isAriaDisabled = (el: Element): boolean => el.getAttribute('aria-disabled') === 'true';\n\n/** Returns whether `aria-checked=\"true\"` is set on the element. */\nexport const isAriaChecked = (el: Element): boolean => el.getAttribute('aria-checked') === 'true';\n\n/** Returns whether `aria-checked=\"mixed\"` is set on the element. */\nexport const isAriaIndeterminate = (el: Element): boolean => el.getAttribute('aria-checked') === 'mixed';\n\n/** Returns whether `aria-expanded=\"true\"` is set on the element. */\nexport const isAriaExpanded = (el: Element): boolean => el.getAttribute('aria-expanded') === 'true';\n\n/** Returns whether `aria-pressed=\"true\"` is set on the element. */\nexport const isAriaPressed = (el: Element): boolean => el.getAttribute('aria-pressed') === 'true';\n\n/** Returns whether `aria-required=\"true\"` is set on the element. */\nexport const isAriaRequired = (el: Element): boolean => el.getAttribute('aria-required') === 'true';\n\n/** Returns whether `aria-hidden=\"true\"` is set on the element. */\nexport const isAriaHidden = (el: Element): boolean => el.getAttribute('aria-hidden') === 'true';\n\n/** Returns the `aria-label` string attribute, or null. */\nexport const getAriaLabel = (el: Element): null | string => el.getAttribute('aria-label');\n\n/** Returns the `aria-labelledby` attribute value (space-separated IDs), or null. */\nexport const getAriaLabelledBy = (el: Element): null | string => el.getAttribute('aria-labelledby');\n\n/** Returns the `aria-describedby` attribute value (space-separated IDs), or null. */\nexport const getAriaDescribedBy = (el: Element): null | string => el.getAttribute('aria-describedby');\n\n/** Returns the `aria-controls` attribute value, or null. */\nexport const getAriaControls = (el: Element): null | string => el.getAttribute('aria-controls');\n\n/** Returns the `role` attribute value, or null. */\nexport const getRole = (el: Element): null | string => el.getAttribute('role');\n\n// ── DOM query helpers ─────────────────────────────────────────────────────────\n\n/**\n * Queries the shadow root of a custom element for a matching CSS selector.\n * Returns null if the element has no shadow root or no match is found.\n */\nexport const queryInShadow = <T extends Element = Element>(host: Element, selector: string): null | T => {\n return host.shadowRoot?.querySelector<T>(selector) ?? null;\n};\n\n/**\n * Queries all matching elements inside the shadow root of a custom element.\n * Returns an empty array if the element has no shadow root.\n */\nexport const queryAllInShadow = <T extends Element = Element>(host: Element, selector: string): T[] => {\n return Array.from(host.shadowRoot?.querySelectorAll<T>(selector) ?? []);\n};\n\n/**\n * Queries a shadow DOM element by its CSS `part` attribute.\n * Shorthand for `queryInShadow(host, '[part=\"name\"]')`.\n *\n * @example\n * ```ts\n * const btn = queryPart(carousel, 'prev-btn');\n * expect(btn).not.toBeNull();\n * ```\n */\nexport const queryPart = <T extends Element = Element>(host: Element, part: string): null | T =>\n queryInShadow<T>(host, `[part=\"${part}\"]`);\n\n/**\n * Returns an array of light-DOM children assigned to a named slot,\n * or all slotted children if no name is given.\n *\n * @example\n * ```ts\n * const slides = getSlotted(carousel);\n * expect(slides).toHaveLength(3);\n * ```\n */\nexport const getSlotted = (host: Element, slotName?: string): Element[] => {\n const selector = slotName ? `[slot=\"${slotName}\"]` : ':not([slot])';\n\n return Array.from(host.querySelectorAll(`:scope > ${selector}`));\n};\n\n/**\n * Dispatches a `PointerEvent` on the given element.\n * Useful for testing hover, drag, and pointer interactions without\n * relying on the fire helper from @vielzeug/ore/testing.\n *\n * @example\n * ```ts\n * dispatchPointer(el, 'enter');\n * dispatchPointer(el, 'leave');\n * ```\n */\nexport const dispatchPointer = (\n el: Element,\n type: 'cancel' | 'down' | 'enter' | 'leave' | 'move' | 'up',\n init?: PointerEventInit,\n): void => {\n el.dispatchEvent(new PointerEvent(`pointer${type}`, { bubbles: true, composed: true, ...init }));\n};\n\n// ── Form-associated component helpers ─────────────────────────────────────────\n\n/**\n * Returns the current form value of a form-associated custom element.\n * Uses the ElementInternals-exposed `value` if available, then falls back\n * to the reflected DOM `value` property.\n */\nexport const getFormValue = (el: HTMLElement & { value?: string }): null | string => {\n return el.value ?? null;\n};\n\n/**\n * Returns whether a form-associated element is considered valid (no constraint\n * violations reported by the browser or via `setValidity`).\n */\nexport const isFormValid = (el: HTMLElement & { validity?: ValidityState }): boolean => {\n return el.validity?.valid ?? true;\n};\n\n// ── Event helpers ─────────────────────────────────────────────────────────────\n\n/**\n * Creates a synthetic `KeyboardEvent` with the given key.\n * Useful for testing keyboard navigation in headless controls.\n */\nexport const keyEvent = (key: string, init?: KeyboardEventInit): KeyboardEvent => {\n return new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key, ...init });\n};\n\n/**\n * Returns a Promise that resolves after a microtask tick (one `queueMicrotask`).\n * Use when you need to wait for reactivity (signal effects) to settle without\n * moving into the macrotask queue.\n */\nexport const nextTick = (): Promise<void> =>\n new Promise((resolve) => {\n queueMicrotask(resolve);\n });\n\n/**\n * Returns a Promise that resolves after `ms` milliseconds.\n * Use sparingly — prefer `nextTick()` for reactive updates.\n */\nexport const wait = (ms = 0): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\n// ── ID counter ────────────────────────────────────────────────────────────────\n\n/**\n * Resets the headless ID counter to 0. Use in test `beforeEach` hooks when you need\n * deterministic IDs across test runs.\n *\n * @example\n * ```ts\n * import { resetIdCounter } from '@vielzeug/refine/testing';\n * beforeEach(() => resetIdCounter());\n * ```\n */\nexport { resetIdCounter } from '@vielzeug/ore';\n\n// ── ARIA snapshot helper ───────────────────────────────────────────────────────\n\n/**\n * Returns a snapshot of the most commonly tested ARIA attributes on an element.\n * Useful for concise inline snapshot assertions in component tests.\n *\n * `ariaLabel` and `labelledby` are kept separate because `aria-label` is a\n * direct string while `aria-labelledby` is an ID reference — conflating them\n * masks bugs where the wrong labelling mechanism is used.\n *\n * @example\n * ```ts\n * expect(getAriaState(input)).toMatchObject({ invalid: 'true', required: 'true' });\n * ```\n */\nexport const getAriaState = (\n el: Element,\n): {\n ariaLabel: null | string;\n checked: null | string;\n disabled: null | string;\n expanded: null | string;\n invalid: null | string;\n labelledby: null | string;\n required: null | string;\n role: null | string;\n} => ({\n ariaLabel: el.getAttribute('aria-label'),\n checked: el.getAttribute('aria-checked'),\n disabled: el.getAttribute('aria-disabled'),\n expanded: el.getAttribute('aria-expanded'),\n invalid: el.getAttribute('aria-invalid'),\n labelledby: el.getAttribute('aria-labelledby'),\n required: el.getAttribute('aria-required'),\n role: el.getAttribute('role'),\n});\n\n// ── Typed component mount wrappers (F3) ───────────────────────────────────────\n\nimport type { Fixture } from '@vielzeug/ore/testing';\n\nimport { mount } from '@vielzeug/ore/testing';\n\nimport type { OreButtonGroupProps } from '../inputs/button-group/button-group';\nimport type { OreButtonProps } from '../inputs/button/button';\nimport type { OreCheckboxGroupProps } from '../inputs/checkbox-group/checkbox-group';\nimport type { OreCheckboxProps } from '../inputs/checkbox/checkbox';\nimport type { OreComboboxProps } from '../inputs/combobox/combobox.types';\nimport type { OreFileInputProps } from '../inputs/file-input/file-input';\nimport type { OreFormProps } from '../inputs/form/form';\nimport type { OreInputProps } from '../inputs/input/input';\nimport type { OreNumberInputProps } from '../inputs/number-input/number-input';\nimport type { OreOtpInputProps } from '../inputs/otp-input/otp-input';\nimport type { OreRadioGroupProps } from '../inputs/radio-group/radio-group';\nimport type { OreRadioProps } from '../inputs/radio/radio';\nimport type { OreRatingProps } from '../inputs/rating/rating';\nimport type { OreSelectProps } from '../inputs/select/select';\nimport type { OreSliderProps } from '../inputs/slider/slider';\nimport type { OreSwitchProps } from '../inputs/switch/switch';\nimport type { OreTextareaProps } from '../inputs/textarea/textarea';\n\n/**\n * Serializes an attribute map to an HTML attribute string fragment.\n * Values are HTML-escaped to prevent attribute injection.\n *\n * @example\n * ```ts\n * attrsToHtml({ disabled: '', label: 'Name' }); // → 'disabled label=\"Name\"'\n * ```\n */\nexport const attrsToHtml = (attrs: Record<string, string>): string =>\n Object.entries(attrs)\n .map(([key, value]) => (value === '' ? key : `${key}=\"${value.replace(/\"/g, '"')}\"`))\n .join(' ');\n\n/**\n * Converts a props object into an HTML attribute record.\n * - `true` → empty boolean attribute (e.g., `disabled`)\n * - `false` / `null` / `undefined` → attribute omitted\n * - Array of objects → **omitted** (cannot be serialized as an HTML attribute;\n * use `opts.innerHTML` with slotted elements instead)\n * - Array of primitives → comma-joined string\n * - everything else → stringified\n *\n * @example\n * ```ts\n * propsToAttrs({ disabled: true, label: 'Name', value: undefined });\n * // → { disabled: '', label: 'Name' }\n * ```\n */\nexport const propsToAttrs = (props: Record<string, unknown> = {}): Record<string, string> => {\n const attrs: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(props)) {\n if (value === false || value == null) continue;\n\n if (value === true) {\n attrs[key] = '';\n } else if (Array.isArray(value)) {\n // Object arrays cannot be represented as HTML attributes — skip silently.\n if (value.length > 0 && typeof value[0] === 'object') continue;\n\n attrs[key] = (value as string[]).join(',');\n } else {\n attrs[key] = String(value);\n }\n }\n\n return attrs;\n};\n\ntype MountOptions = { innerHTML?: string };\n\n/** Typed mount wrapper for `<ore-input>`. Catch prop name typos at compile time. */\nexport const mountOreInput = (props?: Partial<OreInputProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-input ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-input>`);\n\n/** Typed mount wrapper for `<ore-textarea>`. */\nexport const mountOreTextarea = (props?: Partial<OreTextareaProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-textarea ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-textarea>`);\n\n/** Typed mount wrapper for `<ore-select>`. */\nexport const mountOreSelect = (props?: Partial<OreSelectProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-select ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-select>`);\n\n/** Typed mount wrapper for `<ore-combobox>`. */\nexport const mountOreCombobox = (props?: Partial<OreComboboxProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-combobox ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-combobox>`);\n\n/** Typed mount wrapper for `<ore-checkbox>`. */\nexport const mountOreCheckbox = (props?: Partial<OreCheckboxProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-checkbox ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-checkbox>`);\n\n/** Typed mount wrapper for `<ore-checkbox-group>`. */\nexport const mountOreCheckboxGroup = (props?: Partial<OreCheckboxGroupProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-checkbox-group ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-checkbox-group>`);\n\n/** Typed mount wrapper for `<ore-radio>`. */\nexport const mountOreRadio = (props?: Partial<OreRadioProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-radio ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-radio>`);\n\n/** Typed mount wrapper for `<ore-radio-group>`. */\nexport const mountOreRadioGroup = (props?: Partial<OreRadioGroupProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-radio-group ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-radio-group>`);\n\n/** Typed mount wrapper for `<ore-switch>`. */\nexport const mountOreSwitch = (props?: Partial<OreSwitchProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-switch ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-switch>`);\n\n/** Typed mount wrapper for `<ore-button>`. */\nexport const mountOreButton = (props?: Partial<OreButtonProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-button ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-button>`);\n\n/** Typed mount wrapper for `<ore-button-group>`. */\nexport const mountOreButtonGroup = (props?: Partial<OreButtonGroupProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-button-group ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-button-group>`);\n\n/** Typed mount wrapper for `<ore-file-input>`. */\nexport const mountOreFileInput = (props?: Partial<OreFileInputProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-file-input ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-file-input>`);\n\n/** Typed mount wrapper for `<ore-form>`. */\nexport const mountOreForm = (props?: Partial<OreFormProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-form ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-form>`);\n\n/** Typed mount wrapper for `<ore-number-input>`. */\nexport const mountOreNumberInput = (props?: Partial<OreNumberInputProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-number-input ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-number-input>`);\n\n/** Typed mount wrapper for `<ore-otp-input>`. */\nexport const mountOreOtpInput = (props?: Partial<OreOtpInputProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-otp-input ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-otp-input>`);\n\n/** Typed mount wrapper for `<ore-rating>`. */\nexport const mountOreRating = (props?: Partial<OreRatingProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-rating ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-rating>`);\n\n/** Typed mount wrapper for `<ore-slider>`. */\nexport const mountOreSlider = (props?: Partial<OreSliderProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-slider ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-slider>`);\n"],"mappings":"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"}
|
package/dist/testing.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import "
|
|
3
|
-
import { resetIdCounter as t } from "@vielzeug/ore";
|
|
1
|
+
import { resetIdCounter as e } from "@vielzeug/ore";
|
|
2
|
+
import { mount as t } from "@vielzeug/ore/testing";
|
|
4
3
|
//#region src/testing/index.ts
|
|
5
4
|
var n = (e) => e.getAttribute("aria-invalid") === "true", r = (e) => e.getAttribute("aria-disabled") === "true", i = (e) => e.getAttribute("aria-checked") === "true", a = (e) => e.getAttribute("aria-checked") === "mixed", o = (e) => e.getAttribute("aria-expanded") === "true", s = (e) => e.getAttribute("aria-pressed") === "true", c = (e) => e.getAttribute("aria-required") === "true", l = (e) => e.getAttribute("aria-hidden") === "true", u = (e) => e.getAttribute("aria-label"), d = (e) => e.getAttribute("aria-labelledby"), f = (e) => e.getAttribute("aria-describedby"), p = (e) => e.getAttribute("aria-controls"), m = (e) => e.getAttribute("role"), h = (e, t) => e.shadowRoot?.querySelector(t) ?? null, g = (e, t) => Array.from(e.shadowRoot?.querySelectorAll(t) ?? []), _ = (e, t) => h(e, `[part="${t}"]`), v = (e, t) => {
|
|
6
5
|
let n = t ? `[slot="${t}"]` : ":not([slot])";
|
|
@@ -37,8 +36,8 @@ var n = (e) => e.getAttribute("aria-invalid") === "true", r = (e) => e.getAttrib
|
|
|
37
36
|
t[n] = r.join(",");
|
|
38
37
|
} else t[n] = String(r);
|
|
39
38
|
return t;
|
|
40
|
-
}, O = (
|
|
39
|
+
}, O = (e, n) => t(`<ore-input ${E(D(e))}>${n?.innerHTML ?? ""}</ore-input>`), k = (e, n) => t(`<ore-textarea ${E(D(e))}>${n?.innerHTML ?? ""}</ore-textarea>`), A = (e, n) => t(`<ore-select ${E(D(e))}>${n?.innerHTML ?? ""}</ore-select>`), j = (e, n) => t(`<ore-combobox ${E(D(e))}>${n?.innerHTML ?? ""}</ore-combobox>`), M = (e, n) => t(`<ore-checkbox ${E(D(e))}>${n?.innerHTML ?? ""}</ore-checkbox>`), N = (e, n) => t(`<ore-checkbox-group ${E(D(e))}>${n?.innerHTML ?? ""}</ore-checkbox-group>`), P = (e, n) => t(`<ore-radio ${E(D(e))}>${n?.innerHTML ?? ""}</ore-radio>`), F = (e, n) => t(`<ore-radio-group ${E(D(e))}>${n?.innerHTML ?? ""}</ore-radio-group>`), I = (e, n) => t(`<ore-switch ${E(D(e))}>${n?.innerHTML ?? ""}</ore-switch>`), L = (e, n) => t(`<ore-button ${E(D(e))}>${n?.innerHTML ?? ""}</ore-button>`), R = (e, n) => t(`<ore-button-group ${E(D(e))}>${n?.innerHTML ?? ""}</ore-button-group>`), z = (e, n) => t(`<ore-file-input ${E(D(e))}>${n?.innerHTML ?? ""}</ore-file-input>`), B = (e, n) => t(`<ore-form ${E(D(e))}>${n?.innerHTML ?? ""}</ore-form>`), V = (e, n) => t(`<ore-number-input ${E(D(e))}>${n?.innerHTML ?? ""}</ore-number-input>`), H = (e, n) => t(`<ore-otp-input ${E(D(e))}>${n?.innerHTML ?? ""}</ore-otp-input>`), U = (e, n) => t(`<ore-rating ${E(D(e))}>${n?.innerHTML ?? ""}</ore-rating>`), W = (e, n) => t(`<ore-slider ${E(D(e))}>${n?.innerHTML ?? ""}</ore-slider>`);
|
|
41
40
|
//#endregion
|
|
42
|
-
export { E as attrsToHtml, y as dispatchPointer, p as getAriaControls, f as getAriaDescribedBy, u as getAriaLabel, d as getAriaLabelledBy, T as getAriaState, b as getFormValue, m as getRole, v as getSlotted, i as isAriaChecked, r as isAriaDisabled, o as isAriaExpanded, l as isAriaHidden, a as isAriaIndeterminate, n as isAriaInvalid, s as isAriaPressed, c as isAriaRequired, x as isFormValid, S as keyEvent, L as mountOreButton, R as mountOreButtonGroup, M as mountOreCheckbox, N as mountOreCheckboxGroup, j as mountOreCombobox, z as mountOreFileInput, B as mountOreForm, O as mountOreInput, V as mountOreNumberInput, H as mountOreOtpInput, P as mountOreRadio, F as mountOreRadioGroup, U as mountOreRating, A as mountOreSelect, W as mountOreSlider, I as mountOreSwitch, k as mountOreTextarea, C as nextTick, D as propsToAttrs, g as queryAllInShadow, h as queryInShadow, _ as queryPart,
|
|
41
|
+
export { E as attrsToHtml, y as dispatchPointer, p as getAriaControls, f as getAriaDescribedBy, u as getAriaLabel, d as getAriaLabelledBy, T as getAriaState, b as getFormValue, m as getRole, v as getSlotted, i as isAriaChecked, r as isAriaDisabled, o as isAriaExpanded, l as isAriaHidden, a as isAriaIndeterminate, n as isAriaInvalid, s as isAriaPressed, c as isAriaRequired, x as isFormValid, S as keyEvent, L as mountOreButton, R as mountOreButtonGroup, M as mountOreCheckbox, N as mountOreCheckboxGroup, j as mountOreCombobox, z as mountOreFileInput, B as mountOreForm, O as mountOreInput, V as mountOreNumberInput, H as mountOreOtpInput, P as mountOreRadio, F as mountOreRadioGroup, U as mountOreRating, A as mountOreSelect, W as mountOreSlider, I as mountOreSwitch, k as mountOreTextarea, C as nextTick, D as propsToAttrs, g as queryAllInShadow, h as queryInShadow, _ as queryPart, e as resetIdCounter, w as wait };
|
|
43
42
|
|
|
44
43
|
//# sourceMappingURL=testing.js.map
|
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","names":[],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * @vielzeug/refine/testing\n *\n * Testing utilities for refine components. Provides a11y helpers, ARIA query\n * utilities, and convenience wrappers for common patterns in component tests.\n *\n * @example\n * ```typescript\n * import { getAriaLabel, getAriaDescribedBy, isAriaInvalid } from '@vielzeug/refine/testing';\n * import { mount } from '@vielzeug/ore/testing';\n *\n * const fixture = mount('<ore-input label=\"Name\"></ore-input>');\n * const input = fixture.query('input')!;\n * expect(isAriaInvalid(input)).toBe(false);\n * ```\n */\n\n// ── ARIA attribute helpers ────────────────────────────────────────────────────\n\n/** Returns whether `aria-invalid=\"true\"` is set on the element. */\nexport const isAriaInvalid = (el: Element): boolean => el.getAttribute('aria-invalid') === 'true';\n\n/** Returns whether `aria-disabled=\"true\"` is set on the element. */\nexport const isAriaDisabled = (el: Element): boolean => el.getAttribute('aria-disabled') === 'true';\n\n/** Returns whether `aria-checked=\"true\"` is set on the element. */\nexport const isAriaChecked = (el: Element): boolean => el.getAttribute('aria-checked') === 'true';\n\n/** Returns whether `aria-checked=\"mixed\"` is set on the element. */\nexport const isAriaIndeterminate = (el: Element): boolean => el.getAttribute('aria-checked') === 'mixed';\n\n/** Returns whether `aria-expanded=\"true\"` is set on the element. */\nexport const isAriaExpanded = (el: Element): boolean => el.getAttribute('aria-expanded') === 'true';\n\n/** Returns whether `aria-pressed=\"true\"` is set on the element. */\nexport const isAriaPressed = (el: Element): boolean => el.getAttribute('aria-pressed') === 'true';\n\n/** Returns whether `aria-required=\"true\"` is set on the element. */\nexport const isAriaRequired = (el: Element): boolean => el.getAttribute('aria-required') === 'true';\n\n/** Returns whether `aria-hidden=\"true\"` is set on the element. */\nexport const isAriaHidden = (el: Element): boolean => el.getAttribute('aria-hidden') === 'true';\n\n/** Returns the `aria-label` string attribute, or null. */\nexport const getAriaLabel = (el: Element): null | string => el.getAttribute('aria-label');\n\n/** Returns the `aria-labelledby` attribute value (space-separated IDs), or null. */\nexport const getAriaLabelledBy = (el: Element): null | string => el.getAttribute('aria-labelledby');\n\n/** Returns the `aria-describedby` attribute value (space-separated IDs), or null. */\nexport const getAriaDescribedBy = (el: Element): null | string => el.getAttribute('aria-describedby');\n\n/** Returns the `aria-controls` attribute value, or null. */\nexport const getAriaControls = (el: Element): null | string => el.getAttribute('aria-controls');\n\n/** Returns the `role` attribute value, or null. */\nexport const getRole = (el: Element): null | string => el.getAttribute('role');\n\n// ── DOM query helpers ─────────────────────────────────────────────────────────\n\n/**\n * Queries the shadow root of a custom element for a matching CSS selector.\n * Returns null if the element has no shadow root or no match is found.\n */\nexport const queryInShadow = <T extends Element = Element>(host: Element, selector: string): null | T => {\n return host.shadowRoot?.querySelector<T>(selector) ?? null;\n};\n\n/**\n * Queries all matching elements inside the shadow root of a custom element.\n * Returns an empty array if the element has no shadow root.\n */\nexport const queryAllInShadow = <T extends Element = Element>(host: Element, selector: string): T[] => {\n return Array.from(host.shadowRoot?.querySelectorAll<T>(selector) ?? []);\n};\n\n/**\n * Queries a shadow DOM element by its CSS `part` attribute.\n * Shorthand for `queryInShadow(host, '[part=\"name\"]')`.\n *\n * @example\n * ```ts\n * const btn = queryPart(carousel, 'prev-btn');\n * expect(btn).not.toBeNull();\n * ```\n */\nexport const queryPart = <T extends Element = Element>(host: Element, part: string): null | T =>\n queryInShadow<T>(host, `[part=\"${part}\"]`);\n\n/**\n * Returns an array of light-DOM children assigned to a named slot,\n * or all slotted children if no name is given.\n *\n * @example\n * ```ts\n * const slides = getSlotted(carousel);\n * expect(slides).toHaveLength(3);\n * ```\n */\nexport const getSlotted = (host: Element, slotName?: string): Element[] => {\n const selector = slotName ? `[slot=\"${slotName}\"]` : ':not([slot])';\n\n return Array.from(host.querySelectorAll(`:scope > ${selector}`));\n};\n\n/**\n * Dispatches a `PointerEvent` on the given element.\n * Useful for testing hover, drag, and pointer interactions without\n * relying on the fire helper from @vielzeug/ore/testing.\n *\n * @example\n * ```ts\n * dispatchPointer(el, 'enter');\n * dispatchPointer(el, 'leave');\n * ```\n */\nexport const dispatchPointer = (\n el: Element,\n type: 'cancel' | 'down' | 'enter' | 'leave' | 'move' | 'up',\n init?: PointerEventInit,\n): void => {\n el.dispatchEvent(new PointerEvent(`pointer${type}`, { bubbles: true, composed: true, ...init }));\n};\n\n// ── Form-associated component helpers ─────────────────────────────────────────\n\n/**\n * Returns the current form value of a form-associated custom element.\n * Uses the ElementInternals-exposed `value` if available, then falls back\n * to the reflected DOM `value` property.\n */\nexport const getFormValue = (el: HTMLElement & { value?: string }): null | string => {\n return el.value ?? null;\n};\n\n/**\n * Returns whether a form-associated element is considered valid (no constraint\n * violations reported by the browser or via `setValidity`).\n */\nexport const isFormValid = (el: HTMLElement & { validity?: ValidityState }): boolean => {\n return el.validity?.valid ?? true;\n};\n\n// ── Event helpers ─────────────────────────────────────────────────────────────\n\n/**\n * Creates a synthetic `KeyboardEvent` with the given key.\n * Useful for testing keyboard navigation in headless controls.\n */\nexport const keyEvent = (key: string, init?: KeyboardEventInit): KeyboardEvent => {\n return new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key, ...init });\n};\n\n/**\n * Returns a Promise that resolves after a microtask tick (one `queueMicrotask`).\n * Use when you need to wait for reactivity (signal effects) to settle without\n * moving into the macrotask queue.\n */\nexport const nextTick = (): Promise<void> =>\n new Promise((resolve) => {\n queueMicrotask(resolve);\n });\n\n/**\n * Returns a Promise that resolves after `ms` milliseconds.\n * Use sparingly — prefer `nextTick()` for reactive updates.\n */\nexport const wait = (ms = 0): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\n// ── ID counter ────────────────────────────────────────────────────────────────\n\n/**\n * Resets the headless ID counter to 0. Use in test `beforeEach` hooks when you need\n * deterministic IDs across test runs.\n *\n * @example\n * ```ts\n * import { resetIdCounter } from '@vielzeug/refine/testing';\n * beforeEach(() => resetIdCounter());\n * ```\n */\nexport { resetIdCounter } from '@vielzeug/ore';\n\n// ── ARIA snapshot helper ───────────────────────────────────────────────────────\n\n/**\n * Returns a snapshot of the most commonly tested ARIA attributes on an element.\n * Useful for concise inline snapshot assertions in component tests.\n *\n * `ariaLabel` and `labelledby` are kept separate because `aria-label` is a\n * direct string while `aria-labelledby` is an ID reference — conflating them\n * masks bugs where the wrong labelling mechanism is used.\n *\n * @example\n * ```ts\n * expect(getAriaState(input)).toMatchObject({ invalid: 'true', required: 'true' });\n * ```\n */\nexport const getAriaState = (\n el: Element,\n): {\n ariaLabel: null | string;\n checked: null | string;\n disabled: null | string;\n expanded: null | string;\n invalid: null | string;\n labelledby: null | string;\n required: null | string;\n role: null | string;\n} => ({\n ariaLabel: el.getAttribute('aria-label'),\n checked: el.getAttribute('aria-checked'),\n disabled: el.getAttribute('aria-disabled'),\n expanded: el.getAttribute('aria-expanded'),\n invalid: el.getAttribute('aria-invalid'),\n labelledby: el.getAttribute('aria-labelledby'),\n required: el.getAttribute('aria-required'),\n role: el.getAttribute('role'),\n});\n\n// ── Typed component mount wrappers (F3) ───────────────────────────────────────\n\nimport type { Fixture } from '@vielzeug/ore/testing';\n\nimport { mount } from '@vielzeug/ore/testing';\n\nimport type { OreButtonGroupProps } from '../inputs/button-group/button-group';\nimport type { OreButtonProps } from '../inputs/button/button';\nimport type { OreCheckboxGroupProps } from '../inputs/checkbox-group/checkbox-group';\nimport type { OreCheckboxProps } from '../inputs/checkbox/checkbox';\nimport type { OreComboboxProps } from '../inputs/combobox/combobox.types';\nimport type { OreFileInputProps } from '../inputs/file-input/file-input';\nimport type { OreFormProps } from '../inputs/form/form';\nimport type { OreInputProps } from '../inputs/input/input';\nimport type { OreNumberInputProps } from '../inputs/number-input/number-input';\nimport type { OreOtpInputProps } from '../inputs/otp-input/otp-input';\nimport type { OreRadioGroupProps } from '../inputs/radio-group/radio-group';\nimport type { OreRadioProps } from '../inputs/radio/radio';\nimport type { OreRatingProps } from '../inputs/rating/rating';\nimport type { OreSelectProps } from '../inputs/select/select';\nimport type { OreSliderProps } from '../inputs/slider/slider';\nimport type { OreSwitchProps } from '../inputs/switch/switch';\nimport type { OreTextareaProps } from '../inputs/textarea/textarea';\n\n/**\n * Serializes an attribute map to an HTML attribute string fragment.\n * Values are HTML-escaped to prevent attribute injection.\n *\n * @example\n * ```ts\n * attrsToHtml({ disabled: '', label: 'Name' }); // → 'disabled label=\"Name\"'\n * ```\n */\nexport const attrsToHtml = (attrs: Record<string, string>): string =>\n Object.entries(attrs)\n .map(([key, value]) => (value === '' ? key : `${key}=\"${value.replace(/\"/g, '"')}\"`))\n .join(' ');\n\n/**\n * Converts a props object into an HTML attribute record.\n * - `true` → empty boolean attribute (e.g., `disabled`)\n * - `false` / `null` / `undefined` → attribute omitted\n * - Array of objects → **omitted** (cannot be serialized as an HTML attribute;\n * use `opts.innerHTML` with slotted elements instead)\n * - Array of primitives → comma-joined string\n * - everything else → stringified\n *\n * @example\n * ```ts\n * propsToAttrs({ disabled: true, label: 'Name', value: undefined });\n * // → { disabled: '', label: 'Name' }\n * ```\n */\nexport const propsToAttrs = (props: Record<string, unknown> = {}): Record<string, string> => {\n const attrs: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(props)) {\n if (value === false || value == null) continue;\n\n if (value === true) {\n attrs[key] = '';\n } else if (Array.isArray(value)) {\n // Object arrays cannot be represented as HTML attributes — skip silently.\n if (value.length > 0 && typeof value[0] === 'object') continue;\n\n attrs[key] = (value as string[]).join(',');\n } else {\n attrs[key] = String(value);\n }\n }\n\n return attrs;\n};\n\ntype MountOptions = { innerHTML?: string };\n\n/** Typed mount wrapper for `<ore-input>`. Catch prop name typos at compile time. */\nexport const mountOreInput = (props?: Partial<OreInputProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-input ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-input>`);\n\n/** Typed mount wrapper for `<ore-textarea>`. */\nexport const mountOreTextarea = (props?: Partial<OreTextareaProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-textarea ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-textarea>`);\n\n/** Typed mount wrapper for `<ore-select>`. */\nexport const mountOreSelect = (props?: Partial<OreSelectProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-select ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-select>`);\n\n/** Typed mount wrapper for `<ore-combobox>`. */\nexport const mountOreCombobox = (props?: Partial<OreComboboxProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-combobox ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-combobox>`);\n\n/** Typed mount wrapper for `<ore-checkbox>`. */\nexport const mountOreCheckbox = (props?: Partial<OreCheckboxProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-checkbox ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-checkbox>`);\n\n/** Typed mount wrapper for `<ore-checkbox-group>`. */\nexport const mountOreCheckboxGroup = (props?: Partial<OreCheckboxGroupProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-checkbox-group ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-checkbox-group>`);\n\n/** Typed mount wrapper for `<ore-radio>`. */\nexport const mountOreRadio = (props?: Partial<OreRadioProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-radio ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-radio>`);\n\n/** Typed mount wrapper for `<ore-radio-group>`. */\nexport const mountOreRadioGroup = (props?: Partial<OreRadioGroupProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-radio-group ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-radio-group>`);\n\n/** Typed mount wrapper for `<ore-switch>`. */\nexport const mountOreSwitch = (props?: Partial<OreSwitchProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-switch ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-switch>`);\n\n/** Typed mount wrapper for `<ore-button>`. */\nexport const mountOreButton = (props?: Partial<OreButtonProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-button ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-button>`);\n\n/** Typed mount wrapper for `<ore-button-group>`. */\nexport const mountOreButtonGroup = (props?: Partial<OreButtonGroupProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-button-group ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-button-group>`);\n\n/** Typed mount wrapper for `<ore-file-input>`. */\nexport const mountOreFileInput = (props?: Partial<OreFileInputProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-file-input ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-file-input>`);\n\n/** Typed mount wrapper for `<ore-form>`. */\nexport const mountOreForm = (props?: Partial<OreFormProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-form ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-form>`);\n\n/** Typed mount wrapper for `<ore-number-input>`. */\nexport const mountOreNumberInput = (props?: Partial<OreNumberInputProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-number-input ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-number-input>`);\n\n/** Typed mount wrapper for `<ore-otp-input>`. */\nexport const mountOreOtpInput = (props?: Partial<OreOtpInputProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-otp-input ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-otp-input>`);\n\n/** Typed mount wrapper for `<ore-rating>`. */\nexport const mountOreRating = (props?: Partial<OreRatingProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-rating ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-rating>`);\n\n/** Typed mount wrapper for `<ore-slider>`. */\nexport const mountOreSlider = (props?: Partial<OreSliderProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-slider ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-slider>`);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"testing.js","names":[],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * @vielzeug/refine/testing\n *\n * Testing utilities for refine components. Provides a11y helpers, ARIA query\n * utilities, and convenience wrappers for common patterns in component tests.\n *\n * @example\n * ```typescript\n * import { getAriaLabel, getAriaDescribedBy, isAriaInvalid } from '@vielzeug/refine/testing';\n * import { mount } from '@vielzeug/ore/testing';\n *\n * const fixture = mount('<ore-input label=\"Name\"></ore-input>');\n * const input = fixture.query('input')!;\n * expect(isAriaInvalid(input)).toBe(false);\n * ```\n */\n\n// ── ARIA attribute helpers ────────────────────────────────────────────────────\n\n/** Returns whether `aria-invalid=\"true\"` is set on the element. */\nexport const isAriaInvalid = (el: Element): boolean => el.getAttribute('aria-invalid') === 'true';\n\n/** Returns whether `aria-disabled=\"true\"` is set on the element. */\nexport const isAriaDisabled = (el: Element): boolean => el.getAttribute('aria-disabled') === 'true';\n\n/** Returns whether `aria-checked=\"true\"` is set on the element. */\nexport const isAriaChecked = (el: Element): boolean => el.getAttribute('aria-checked') === 'true';\n\n/** Returns whether `aria-checked=\"mixed\"` is set on the element. */\nexport const isAriaIndeterminate = (el: Element): boolean => el.getAttribute('aria-checked') === 'mixed';\n\n/** Returns whether `aria-expanded=\"true\"` is set on the element. */\nexport const isAriaExpanded = (el: Element): boolean => el.getAttribute('aria-expanded') === 'true';\n\n/** Returns whether `aria-pressed=\"true\"` is set on the element. */\nexport const isAriaPressed = (el: Element): boolean => el.getAttribute('aria-pressed') === 'true';\n\n/** Returns whether `aria-required=\"true\"` is set on the element. */\nexport const isAriaRequired = (el: Element): boolean => el.getAttribute('aria-required') === 'true';\n\n/** Returns whether `aria-hidden=\"true\"` is set on the element. */\nexport const isAriaHidden = (el: Element): boolean => el.getAttribute('aria-hidden') === 'true';\n\n/** Returns the `aria-label` string attribute, or null. */\nexport const getAriaLabel = (el: Element): null | string => el.getAttribute('aria-label');\n\n/** Returns the `aria-labelledby` attribute value (space-separated IDs), or null. */\nexport const getAriaLabelledBy = (el: Element): null | string => el.getAttribute('aria-labelledby');\n\n/** Returns the `aria-describedby` attribute value (space-separated IDs), or null. */\nexport const getAriaDescribedBy = (el: Element): null | string => el.getAttribute('aria-describedby');\n\n/** Returns the `aria-controls` attribute value, or null. */\nexport const getAriaControls = (el: Element): null | string => el.getAttribute('aria-controls');\n\n/** Returns the `role` attribute value, or null. */\nexport const getRole = (el: Element): null | string => el.getAttribute('role');\n\n// ── DOM query helpers ─────────────────────────────────────────────────────────\n\n/**\n * Queries the shadow root of a custom element for a matching CSS selector.\n * Returns null if the element has no shadow root or no match is found.\n */\nexport const queryInShadow = <T extends Element = Element>(host: Element, selector: string): null | T => {\n return host.shadowRoot?.querySelector<T>(selector) ?? null;\n};\n\n/**\n * Queries all matching elements inside the shadow root of a custom element.\n * Returns an empty array if the element has no shadow root.\n */\nexport const queryAllInShadow = <T extends Element = Element>(host: Element, selector: string): T[] => {\n return Array.from(host.shadowRoot?.querySelectorAll<T>(selector) ?? []);\n};\n\n/**\n * Queries a shadow DOM element by its CSS `part` attribute.\n * Shorthand for `queryInShadow(host, '[part=\"name\"]')`.\n *\n * @example\n * ```ts\n * const btn = queryPart(carousel, 'prev-btn');\n * expect(btn).not.toBeNull();\n * ```\n */\nexport const queryPart = <T extends Element = Element>(host: Element, part: string): null | T =>\n queryInShadow<T>(host, `[part=\"${part}\"]`);\n\n/**\n * Returns an array of light-DOM children assigned to a named slot,\n * or all slotted children if no name is given.\n *\n * @example\n * ```ts\n * const slides = getSlotted(carousel);\n * expect(slides).toHaveLength(3);\n * ```\n */\nexport const getSlotted = (host: Element, slotName?: string): Element[] => {\n const selector = slotName ? `[slot=\"${slotName}\"]` : ':not([slot])';\n\n return Array.from(host.querySelectorAll(`:scope > ${selector}`));\n};\n\n/**\n * Dispatches a `PointerEvent` on the given element.\n * Useful for testing hover, drag, and pointer interactions without\n * relying on the fire helper from @vielzeug/ore/testing.\n *\n * @example\n * ```ts\n * dispatchPointer(el, 'enter');\n * dispatchPointer(el, 'leave');\n * ```\n */\nexport const dispatchPointer = (\n el: Element,\n type: 'cancel' | 'down' | 'enter' | 'leave' | 'move' | 'up',\n init?: PointerEventInit,\n): void => {\n el.dispatchEvent(new PointerEvent(`pointer${type}`, { bubbles: true, composed: true, ...init }));\n};\n\n// ── Form-associated component helpers ─────────────────────────────────────────\n\n/**\n * Returns the current form value of a form-associated custom element.\n * Uses the ElementInternals-exposed `value` if available, then falls back\n * to the reflected DOM `value` property.\n */\nexport const getFormValue = (el: HTMLElement & { value?: string }): null | string => {\n return el.value ?? null;\n};\n\n/**\n * Returns whether a form-associated element is considered valid (no constraint\n * violations reported by the browser or via `setValidity`).\n */\nexport const isFormValid = (el: HTMLElement & { validity?: ValidityState }): boolean => {\n return el.validity?.valid ?? true;\n};\n\n// ── Event helpers ─────────────────────────────────────────────────────────────\n\n/**\n * Creates a synthetic `KeyboardEvent` with the given key.\n * Useful for testing keyboard navigation in headless controls.\n */\nexport const keyEvent = (key: string, init?: KeyboardEventInit): KeyboardEvent => {\n return new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key, ...init });\n};\n\n/**\n * Returns a Promise that resolves after a microtask tick (one `queueMicrotask`).\n * Use when you need to wait for reactivity (signal effects) to settle without\n * moving into the macrotask queue.\n */\nexport const nextTick = (): Promise<void> =>\n new Promise((resolve) => {\n queueMicrotask(resolve);\n });\n\n/**\n * Returns a Promise that resolves after `ms` milliseconds.\n * Use sparingly — prefer `nextTick()` for reactive updates.\n */\nexport const wait = (ms = 0): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\n// ── ID counter ────────────────────────────────────────────────────────────────\n\n/**\n * Resets the headless ID counter to 0. Use in test `beforeEach` hooks when you need\n * deterministic IDs across test runs.\n *\n * @example\n * ```ts\n * import { resetIdCounter } from '@vielzeug/refine/testing';\n * beforeEach(() => resetIdCounter());\n * ```\n */\nexport { resetIdCounter } from '@vielzeug/ore';\n\n// ── ARIA snapshot helper ───────────────────────────────────────────────────────\n\n/**\n * Returns a snapshot of the most commonly tested ARIA attributes on an element.\n * Useful for concise inline snapshot assertions in component tests.\n *\n * `ariaLabel` and `labelledby` are kept separate because `aria-label` is a\n * direct string while `aria-labelledby` is an ID reference — conflating them\n * masks bugs where the wrong labelling mechanism is used.\n *\n * @example\n * ```ts\n * expect(getAriaState(input)).toMatchObject({ invalid: 'true', required: 'true' });\n * ```\n */\nexport const getAriaState = (\n el: Element,\n): {\n ariaLabel: null | string;\n checked: null | string;\n disabled: null | string;\n expanded: null | string;\n invalid: null | string;\n labelledby: null | string;\n required: null | string;\n role: null | string;\n} => ({\n ariaLabel: el.getAttribute('aria-label'),\n checked: el.getAttribute('aria-checked'),\n disabled: el.getAttribute('aria-disabled'),\n expanded: el.getAttribute('aria-expanded'),\n invalid: el.getAttribute('aria-invalid'),\n labelledby: el.getAttribute('aria-labelledby'),\n required: el.getAttribute('aria-required'),\n role: el.getAttribute('role'),\n});\n\n// ── Typed component mount wrappers (F3) ───────────────────────────────────────\n\nimport type { Fixture } from '@vielzeug/ore/testing';\n\nimport { mount } from '@vielzeug/ore/testing';\n\nimport type { OreButtonGroupProps } from '../inputs/button-group/button-group';\nimport type { OreButtonProps } from '../inputs/button/button';\nimport type { OreCheckboxGroupProps } from '../inputs/checkbox-group/checkbox-group';\nimport type { OreCheckboxProps } from '../inputs/checkbox/checkbox';\nimport type { OreComboboxProps } from '../inputs/combobox/combobox.types';\nimport type { OreFileInputProps } from '../inputs/file-input/file-input';\nimport type { OreFormProps } from '../inputs/form/form';\nimport type { OreInputProps } from '../inputs/input/input';\nimport type { OreNumberInputProps } from '../inputs/number-input/number-input';\nimport type { OreOtpInputProps } from '../inputs/otp-input/otp-input';\nimport type { OreRadioGroupProps } from '../inputs/radio-group/radio-group';\nimport type { OreRadioProps } from '../inputs/radio/radio';\nimport type { OreRatingProps } from '../inputs/rating/rating';\nimport type { OreSelectProps } from '../inputs/select/select';\nimport type { OreSliderProps } from '../inputs/slider/slider';\nimport type { OreSwitchProps } from '../inputs/switch/switch';\nimport type { OreTextareaProps } from '../inputs/textarea/textarea';\n\n/**\n * Serializes an attribute map to an HTML attribute string fragment.\n * Values are HTML-escaped to prevent attribute injection.\n *\n * @example\n * ```ts\n * attrsToHtml({ disabled: '', label: 'Name' }); // → 'disabled label=\"Name\"'\n * ```\n */\nexport const attrsToHtml = (attrs: Record<string, string>): string =>\n Object.entries(attrs)\n .map(([key, value]) => (value === '' ? key : `${key}=\"${value.replace(/\"/g, '"')}\"`))\n .join(' ');\n\n/**\n * Converts a props object into an HTML attribute record.\n * - `true` → empty boolean attribute (e.g., `disabled`)\n * - `false` / `null` / `undefined` → attribute omitted\n * - Array of objects → **omitted** (cannot be serialized as an HTML attribute;\n * use `opts.innerHTML` with slotted elements instead)\n * - Array of primitives → comma-joined string\n * - everything else → stringified\n *\n * @example\n * ```ts\n * propsToAttrs({ disabled: true, label: 'Name', value: undefined });\n * // → { disabled: '', label: 'Name' }\n * ```\n */\nexport const propsToAttrs = (props: Record<string, unknown> = {}): Record<string, string> => {\n const attrs: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(props)) {\n if (value === false || value == null) continue;\n\n if (value === true) {\n attrs[key] = '';\n } else if (Array.isArray(value)) {\n // Object arrays cannot be represented as HTML attributes — skip silently.\n if (value.length > 0 && typeof value[0] === 'object') continue;\n\n attrs[key] = (value as string[]).join(',');\n } else {\n attrs[key] = String(value);\n }\n }\n\n return attrs;\n};\n\ntype MountOptions = { innerHTML?: string };\n\n/** Typed mount wrapper for `<ore-input>`. Catch prop name typos at compile time. */\nexport const mountOreInput = (props?: Partial<OreInputProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-input ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-input>`);\n\n/** Typed mount wrapper for `<ore-textarea>`. */\nexport const mountOreTextarea = (props?: Partial<OreTextareaProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-textarea ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-textarea>`);\n\n/** Typed mount wrapper for `<ore-select>`. */\nexport const mountOreSelect = (props?: Partial<OreSelectProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-select ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-select>`);\n\n/** Typed mount wrapper for `<ore-combobox>`. */\nexport const mountOreCombobox = (props?: Partial<OreComboboxProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-combobox ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-combobox>`);\n\n/** Typed mount wrapper for `<ore-checkbox>`. */\nexport const mountOreCheckbox = (props?: Partial<OreCheckboxProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-checkbox ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-checkbox>`);\n\n/** Typed mount wrapper for `<ore-checkbox-group>`. */\nexport const mountOreCheckboxGroup = (props?: Partial<OreCheckboxGroupProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-checkbox-group ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-checkbox-group>`);\n\n/** Typed mount wrapper for `<ore-radio>`. */\nexport const mountOreRadio = (props?: Partial<OreRadioProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-radio ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-radio>`);\n\n/** Typed mount wrapper for `<ore-radio-group>`. */\nexport const mountOreRadioGroup = (props?: Partial<OreRadioGroupProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-radio-group ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-radio-group>`);\n\n/** Typed mount wrapper for `<ore-switch>`. */\nexport const mountOreSwitch = (props?: Partial<OreSwitchProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-switch ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-switch>`);\n\n/** Typed mount wrapper for `<ore-button>`. */\nexport const mountOreButton = (props?: Partial<OreButtonProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-button ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-button>`);\n\n/** Typed mount wrapper for `<ore-button-group>`. */\nexport const mountOreButtonGroup = (props?: Partial<OreButtonGroupProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-button-group ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-button-group>`);\n\n/** Typed mount wrapper for `<ore-file-input>`. */\nexport const mountOreFileInput = (props?: Partial<OreFileInputProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-file-input ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-file-input>`);\n\n/** Typed mount wrapper for `<ore-form>`. */\nexport const mountOreForm = (props?: Partial<OreFormProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-form ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-form>`);\n\n/** Typed mount wrapper for `<ore-number-input>`. */\nexport const mountOreNumberInput = (props?: Partial<OreNumberInputProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-number-input ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-number-input>`);\n\n/** Typed mount wrapper for `<ore-otp-input>`. */\nexport const mountOreOtpInput = (props?: Partial<OreOtpInputProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-otp-input ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-otp-input>`);\n\n/** Typed mount wrapper for `<ore-rating>`. */\nexport const mountOreRating = (props?: Partial<OreRatingProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-rating ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-rating>`);\n\n/** Typed mount wrapper for `<ore-slider>`. */\nexport const mountOreSlider = (props?: Partial<OreSliderProps>, opts?: MountOptions): Promise<Fixture> =>\n mount(`<ore-slider ${attrsToHtml(propsToAttrs(props))}>${opts?.innerHTML ?? ''}</ore-slider>`);\n"],"mappings":"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"}
|
package/dist/textarea.cjs
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/shape.css.cjs"),t=require("./styles/mixins/theme.css.cjs"),n=require("./styles/index.cjs"),r=require("./shared/prop-bundles.cjs"),i=require("./shared/size-presets.cjs"),a=require("./headless/ore.cjs"),o=require("./headless/auto-resize.cjs"),s=require("./headless/field-base.cjs"),c=require("./headless/text-field.cjs"),l=require("./inputs/shared/field-binding.cjs"),u=require("./inputs/shared/form-context.cjs"),d=require("./inputs/textarea/textarea.css.cjs");let f=require("@vielzeug/ore"),p=require("@vielzeug/ore/directives"),m=require("@vielzeug/ripple"),h=require("@vielzeug/ore/forms");var g=`ore-textarea`;(0,f.define)(g,{formAssociated:!0,props:{...r.themableBundle,...r.sizableBundle,...r.disablableBundle,...r.roundableBundle,"auto-resize":f.prop.bool(!1),error:f.prop.string(),fullwidth:f.prop.bool(!1),helper:f.prop.string(),label:f.prop.string(),"label-placement":f.prop.oneOf([`inset`,`outside`],`inset`),maxlength:f.prop.json(void 0),name:f.prop.string(),"no-resize":f.prop.bool(!1),placeholder:f.prop.string(),readonly:f.prop.bool(!1),ref:f.prop.json(void 0),required:f.prop.bool(!1),resize:f.prop.string(),rows:f.prop.json(void 0),value:f.prop.string(),variant:f.prop.string()},setup(e){let t=(0,f.useEmit)(),n=f.watchEffect,r=(0,f.inject)(u.FORM_CTX),i=u.useFormContext(e,r),d=(0,f.ref)(),g=o.createAutoResize({enabled:e[`auto-resize`]}),_=a.lifecycleSignal(f.onCleanup),v=c.createTextField({disabled:i.disabled,error:e.error,helper:e.helper,label:e.label,labelPlacement:e[`label-placement`],maxLength:e.maxlength,onChange:(e,n)=>{t(`change`,{originalEvent:e,value:n})},onInput:(e,n)=>{t(`input`,{originalEvent:e,value:n})},prefix:`textarea`,readonly:e.readonly,required:e.required,signal:_,validateOn:r?.validateOn,value:e.value});v.attachFormField((0,h.useField)({disabled:v.disabled,onReset:()=>{v.reset(),requestAnimationFrame(()=>g.recompute())},toFormValue:e=>e,validationMessage:v.validationMessage,validity:v.validity,value:v.value}));let{ariaDescribedBy:y,ariaErrorMessage:b,ariaInvalid:x,ariaLabelledBy:S,assistiveId:C,counter:w,errorId:T,errorText:E,fieldId:D,helperText:O,labelId:k,labelVisible:A}=v;return(0,f.onElement)(d,t=>{let r=v.wire(t),i=g.wire(t);e.ref.value?.(t);let a=(0,m.watch)(e.ref,e=>{e?.(t)}),o=n(()=>{t.style.resize=e[`auto-resize`].value||e[`no-resize`].value?`none`:e.resize.value||`vertical`,requestAnimationFrame(()=>g.recompute())});return()=>{a.dispose(),e.ref.value?.(null),r(),i(),o()}}),(0,f.bind)({attr:{error:l.errorAttr(E),size:i.size,variant:i.variant}}),f.html`
|
|
2
2
|
<div class="textarea-wrapper" part="wrapper">
|
|
3
3
|
<label class="label" part="label" for="${D}" id="${k}" ?hidden="${()=>!A.value}"
|
|
4
|
-
>${
|
|
4
|
+
>${e.label}</label
|
|
5
5
|
>
|
|
6
6
|
<div class="field" part="field">
|
|
7
7
|
<textarea
|
|
8
8
|
part="textarea"
|
|
9
|
-
ref="${
|
|
9
|
+
ref="${d}"
|
|
10
10
|
id="${D}"
|
|
11
|
-
:name="${
|
|
12
|
-
:placeholder="${
|
|
13
|
-
:rows="${
|
|
14
|
-
:maxlength="${
|
|
15
|
-
?disabled="${
|
|
16
|
-
?readonly="${
|
|
17
|
-
?required="${
|
|
18
|
-
:value="${
|
|
11
|
+
:name="${e.name}"
|
|
12
|
+
:placeholder="${e.placeholder}"
|
|
13
|
+
:rows="${e.rows}"
|
|
14
|
+
:maxlength="${e.maxlength}"
|
|
15
|
+
?disabled="${e.disabled}"
|
|
16
|
+
?readonly="${e.readonly}"
|
|
17
|
+
?required="${e.required}"
|
|
18
|
+
:value="${(0,p.live)(v.value)}"
|
|
19
19
|
:aria-describedby="${y}"
|
|
20
20
|
:aria-errormessage="${b}"
|
|
21
21
|
:aria-invalid="${x}"
|
|
22
22
|
:aria-labelledby="${S}"></textarea>
|
|
23
23
|
</div>
|
|
24
|
-
<span class="${()=>
|
|
24
|
+
<span class="${()=>s.counterClassName(w?.value)}" aria-live="polite" ?hidden="${()=>!w}">${()=>w?.value.counterText.replace(` / `,`/`)??``}</span>
|
|
25
25
|
<div id="${C}" class="helper-text" aria-live="polite" part="helper" ?hidden="${()=>!!E.value||!O.value}">
|
|
26
26
|
${()=>O.value}
|
|
27
27
|
</div>
|
|
@@ -29,5 +29,5 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
|
|
|
29
29
|
${()=>E.value}
|
|
30
30
|
</div>
|
|
31
31
|
</div>
|
|
32
|
-
`},shadow:{delegatesFocus:!0},styles:[...
|
|
32
|
+
`},shadow:{delegatesFocus:!0},styles:[...n.fieldMixins,e.sizeVariantMixin(i.TEXTAREA_SIZE_PRESET),t.forcedColorsFocusMixin(`textarea`),d.default]}),exports.TEXTAREA_TAG=g;
|
|
33
33
|
//# sourceMappingURL=textarea.cjs.map
|
package/dist/textarea.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textarea.cjs","names":[],"sources":["../src/inputs/textarea/textarea.ts"],"sourcesContent":["import { define, html, inject, prop, ref, bind, onCleanup, onElement, useEmit, watchEffect } from '@vielzeug/ore';\nimport { live } from '@vielzeug/ore/directives';\nimport { useField } from '@vielzeug/ore/forms';\nimport { watch as rippleWatch } from '@vielzeug/ripple';\n\nimport type { TextFieldProps } from '../../shared';\nimport type { VisualVariant } from '../../types';\n\nimport { counterClassName, createAutoResize, lifecycleSignal, createTextField } from '../../headless';\nimport { disablableBundle, roundableBundle, sizableBundle, TEXTAREA_SIZE_PRESET, themableBundle } from '../../shared';\nimport { fieldMixins, forcedColorsFocusMixin, sizeVariantMixin } from '../../styles';\nimport { errorAttr } from '../shared/field-binding';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport componentStyles from './textarea.css?inline';\n\n/** Textarea component properties */\n\nexport type OreTextareaEvents = {\n change: { originalEvent: Event; value: string };\n input: { originalEvent: Event; value: string };\n};\n\nexport type OreTextareaProps = TextFieldProps<Exclude<VisualVariant, 'frost' | 'text'>> & {\n /** Allow auto-grow with content */\n 'auto-resize'?: boolean;\n /** Maximum character count; shows a counter when set */\n maxlength?: number;\n /** Disable a manual resize handle */\n 'no-resize'?: boolean;\n /**\n * JS-only callback fired with the inner `<textarea>` element when it mounts,\n * and with `null` when it unmounts. Intended for composed components that\n * need imperative access to the raw element.\n * Set as a JS property: `bitTextarea.ref = (el) => { ... }`.\n */\n ref?: ((el: HTMLTextAreaElement | null) => void) | null;\n /** Resize direction override */\n resize?: 'none' | 'horizontal' | 'both' | 'vertical';\n /** Number of visible text rows */\n rows?: number;\n};\n\n/**\n * A multi-line text input with label, helper text, character counter, and auto-resize.\n *\n * @element ore-textarea\n *\n * @attr {string} label - Label text\n * @attr {string} label-placement - 'inset' | 'outside'\n * @attr {string} value - Current value\n * @attr {string} placeholder - Placeholder text\n * @attr {string} name - Form field name\n * @attr {number} rows - Visible row count\n * @attr {number} maxlength - Max character count (shows counter)\n * @attr {string} helper - Helper text below the textarea\n * @attr {string} error - Error message\n * @attr {boolean} disabled - Disable interaction\n * @attr {boolean} readonly - Read-only mode\n * @attr {boolean} required - Required field\n * @attr {boolean} no-resize - Disable manual resize\n * @attr {boolean} auto-resize - Grow with content\n * @attr {string} resize - Resize direction: 'none' | 'horizontal' | 'both' | 'vertical'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n *\n * @fires input - Fired on every keystroke with current value. detail: { value: string; originalEvent: Event }\n * @fires change - Fired on blur with changed value. detail: { value: string; originalEvent: Event }\n *\n * @slot helper - Complex helper content\n *\n * @cssprop --textarea-bg - Background color\n * @cssprop --textarea-border-color - Border color\n * @cssprop --textarea-placeholder-color - Placeholder text color\n * @cssprop --textarea-radius - Border radius\n * @cssprop --textarea-padding - Inner padding (block inline)\n * @cssprop --textarea-gap - Gap between label and field\n * @cssprop --textarea-font-size - Font size\n * @cssprop --textarea-min-height - Minimum field height\n * @cssprop --textarea-max-height - Maximum field height (none = unlimited)\n * @cssprop --textarea-resize - CSS resize direction ('vertical' | 'horizontal' | 'both' | 'none')\n * @cssprop --textarea-hover-bg - Field background on hover (flat/ghost variants)\n * @cssprop --textarea-hover-border-color - Field border on hover (flat/bordered variants)\n * @cssprop --textarea-focus-bg - Field background when focused (flat variant)\n * @cssprop --textarea-focus-border-color - Field border when focused (flat variant)\n *\n * @part wrapper - Outer wrapper element.\n * @part label - Label element.\n * @part field - Field container.\n * @part textarea - The native `<textarea>` element.\n * @part helper - The helper text element.\n * @part error - The error text element (`role=\"alert\"`).\n *\n * @example\n * ```html\n * <ore-textarea></ore-textarea>\n * ```\n */\nexport const TEXTAREA_TAG = 'ore-textarea' as const;\ndefine<OreTextareaProps>(TEXTAREA_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...roundableBundle,\n 'auto-resize': prop.bool(false),\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n 'label-placement': prop.oneOf(['inset', 'outside'] as const, 'inset'),\n maxlength: prop.json(undefined as number | undefined),\n name: prop.string(),\n 'no-resize': prop.bool(false),\n placeholder: prop.string(),\n readonly: prop.bool(false),\n ref: prop.json(undefined as ((el: HTMLTextAreaElement | null) => void) | null | undefined),\n required: prop.bool(false),\n resize: prop.string<'none' | 'both' | 'horizontal' | 'vertical'>(),\n rows: prop.json(undefined as number | undefined),\n value: prop.string(),\n variant: prop.string<'flat' | 'solid' | 'bordered' | 'outline' | 'ghost'>(),\n },\n setup(props) {\n const emit = useEmit<OreTextareaEvents>();\n const watch = watchEffect;\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n const textareaRef = ref<HTMLTextAreaElement>();\n const autoResize = createAutoResize({ enabled: props['auto-resize'] });\n\n const abortSignal = lifecycleSignal(onCleanup);\n const tf = createTextField({\n disabled: fCtxProps.disabled,\n error: props.error,\n helper: props.helper,\n label: props.label,\n labelPlacement: props['label-placement'],\n maxLength: props.maxlength,\n onChange: (event: Event, value: string) => {\n emit('change', { originalEvent: event, value });\n },\n onInput: (event: Event, value: string) => {\n emit('input', { originalEvent: event, value });\n },\n prefix: 'textarea',\n readonly: props.readonly,\n required: props.required,\n signal: abortSignal,\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n tf.attachFormField(\n useField<string>({\n disabled: tf.disabled,\n onReset: () => {\n tf.reset();\n requestAnimationFrame(() => autoResize.recompute());\n },\n toFormValue: (v) => v,\n validationMessage: tf.validationMessage,\n validity: tf.validity,\n value: tf.value,\n }),\n );\n\n const {\n ariaDescribedBy,\n ariaErrorMessage,\n ariaInvalid,\n ariaLabelledBy,\n assistiveId,\n counter,\n errorId,\n errorText,\n fieldId: textareaId,\n helperText,\n labelId,\n labelVisible,\n } = tf;\n\n onElement(textareaRef, (textareaEl) => {\n const unwireEl = tf.wire(textareaEl);\n const unwireAutoResize = autoResize.wire(textareaEl);\n\n props.ref.value?.(textareaEl);\n\n const sub = rippleWatch(props.ref, (cb) => {\n cb?.(textareaEl);\n });\n\n const stopLayoutEffect = watch(() => {\n textareaEl.style.resize =\n props['auto-resize'].value || props['no-resize'].value ? 'none' : props.resize.value || 'vertical';\n\n // Deferred a frame: on mount (or right after `auto-resize` flips true) the browser\n // hasn't necessarily finished laying out the textarea yet, so `scrollHeight` read\n // synchronously here could be stale.\n requestAnimationFrame(() => autoResize.recompute());\n });\n\n return () => {\n sub.dispose();\n props.ref.value?.(null);\n unwireEl();\n unwireAutoResize();\n stopLayoutEffect();\n };\n });\n\n bind({\n attr: {\n error: errorAttr(errorText),\n size: fCtxProps.size,\n variant: fCtxProps.variant,\n },\n });\n\n const counterClass = () => counterClassName(counter?.value);\n const counterHidden = () => !counter;\n const counterText = () => counter?.value.counterText.replace(' / ', '/') ?? '';\n const helperHidden = () => !!errorText.value || !helperText.value;\n const errorHidden = () => !errorText.value;\n\n return html`\n <div class=\"textarea-wrapper\" part=\"wrapper\">\n <label class=\"label\" part=\"label\" for=\"${textareaId}\" id=\"${labelId}\" ?hidden=\"${() => !labelVisible.value}\"\n >${props.label}</label\n >\n <div class=\"field\" part=\"field\">\n <textarea\n part=\"textarea\"\n ref=\"${textareaRef}\"\n id=\"${textareaId}\"\n :name=\"${props.name}\"\n :placeholder=\"${props.placeholder}\"\n :rows=\"${props.rows}\"\n :maxlength=\"${props.maxlength}\"\n ?disabled=\"${props.disabled}\"\n ?readonly=\"${props.readonly}\"\n ?required=\"${props.required}\"\n :value=\"${live(tf.value)}\"\n :aria-describedby=\"${ariaDescribedBy}\"\n :aria-errormessage=\"${ariaErrorMessage}\"\n :aria-invalid=\"${ariaInvalid}\"\n :aria-labelledby=\"${ariaLabelledBy}\"></textarea>\n </div>\n <span class=\"${counterClass}\" aria-live=\"polite\" ?hidden=\"${counterHidden}\">${counterText}</span>\n <div id=\"${assistiveId}\" class=\"helper-text\" aria-live=\"polite\" part=\"helper\" ?hidden=\"${helperHidden}\">\n ${() => helperText.value}\n </div>\n <div id=\"${errorId}\" class=\"helper-text\" role=\"alert\" part=\"error\" ?hidden=\"${errorHidden}\">\n ${() => errorText.value}\n </div>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [...fieldMixins, sizeVariantMixin(TEXTAREA_SIZE_PRESET), forcedColorsFocusMixin('textarea'), componentStyles],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"textarea.cjs","names":[],"sources":["../src/inputs/textarea/textarea.ts"],"sourcesContent":["import { define, html, inject, prop, ref, bind, onCleanup, onElement, useEmit, watchEffect } from '@vielzeug/ore';\nimport { live } from '@vielzeug/ore/directives';\nimport { useField } from '@vielzeug/ore/forms';\nimport { watch as rippleWatch } from '@vielzeug/ripple';\n\nimport type { TextFieldProps } from '../../shared';\nimport type { VisualVariant } from '../../types';\n\nimport { counterClassName, createAutoResize, lifecycleSignal, createTextField } from '../../headless';\nimport { disablableBundle, roundableBundle, sizableBundle, TEXTAREA_SIZE_PRESET, themableBundle } from '../../shared';\nimport { fieldMixins, forcedColorsFocusMixin, sizeVariantMixin } from '../../styles';\nimport { errorAttr } from '../shared/field-binding';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport componentStyles from './textarea.css?inline';\n\n/** Textarea component properties */\n\nexport type OreTextareaEvents = {\n change: { originalEvent: Event; value: string };\n input: { originalEvent: Event; value: string };\n};\n\nexport type OreTextareaProps = TextFieldProps<Exclude<VisualVariant, 'frost' | 'text'>> & {\n /** Allow auto-grow with content */\n 'auto-resize'?: boolean;\n /** Maximum character count; shows a counter when set */\n maxlength?: number;\n /** Disable a manual resize handle */\n 'no-resize'?: boolean;\n /**\n * JS-only callback fired with the inner `<textarea>` element when it mounts,\n * and with `null` when it unmounts. Intended for composed components that\n * need imperative access to the raw element.\n * Set as a JS property: `bitTextarea.ref = (el) => { ... }`.\n */\n ref?: ((el: HTMLTextAreaElement | null) => void) | null;\n /** Resize direction override */\n resize?: 'none' | 'horizontal' | 'both' | 'vertical';\n /** Number of visible text rows */\n rows?: number;\n};\n\n/**\n * A multi-line text input with label, helper text, character counter, and auto-resize.\n *\n * @element ore-textarea\n *\n * @attr {string} label - Label text\n * @attr {string} label-placement - 'inset' | 'outside'\n * @attr {string} value - Current value\n * @attr {string} placeholder - Placeholder text\n * @attr {string} name - Form field name\n * @attr {number} rows - Visible row count\n * @attr {number} maxlength - Max character count (shows counter)\n * @attr {string} helper - Helper text below the textarea\n * @attr {string} error - Error message\n * @attr {boolean} disabled - Disable interaction\n * @attr {boolean} readonly - Read-only mode\n * @attr {boolean} required - Required field\n * @attr {boolean} no-resize - Disable manual resize\n * @attr {boolean} auto-resize - Grow with content\n * @attr {string} resize - Resize direction: 'none' | 'horizontal' | 'both' | 'vertical'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n *\n * @fires input - Fired on every keystroke with current value. detail: { value: string; originalEvent: Event }\n * @fires change - Fired on blur with changed value. detail: { value: string; originalEvent: Event }\n *\n * @slot helper - Complex helper content\n *\n * @cssprop --textarea-bg - Background color\n * @cssprop --textarea-border-color - Border color\n * @cssprop --textarea-placeholder-color - Placeholder text color\n * @cssprop --textarea-radius - Border radius\n * @cssprop --textarea-padding - Inner padding (block inline)\n * @cssprop --textarea-gap - Gap between label and field\n * @cssprop --textarea-font-size - Font size\n * @cssprop --textarea-min-height - Minimum field height\n * @cssprop --textarea-max-height - Maximum field height (none = unlimited)\n * @cssprop --textarea-resize - CSS resize direction ('vertical' | 'horizontal' | 'both' | 'none')\n * @cssprop --textarea-hover-bg - Field background on hover (flat/ghost variants)\n * @cssprop --textarea-hover-border-color - Field border on hover (flat/bordered variants)\n * @cssprop --textarea-focus-bg - Field background when focused (flat variant)\n * @cssprop --textarea-focus-border-color - Field border when focused (flat variant)\n *\n * @part wrapper - Outer wrapper element.\n * @part label - Label element.\n * @part field - Field container.\n * @part textarea - The native `<textarea>` element.\n * @part helper - The helper text element.\n * @part error - The error text element (`role=\"alert\"`).\n *\n * @example\n * ```html\n * <ore-textarea></ore-textarea>\n * ```\n */\nexport const TEXTAREA_TAG = 'ore-textarea' as const;\ndefine<OreTextareaProps>(TEXTAREA_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...roundableBundle,\n 'auto-resize': prop.bool(false),\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n 'label-placement': prop.oneOf(['inset', 'outside'] as const, 'inset'),\n maxlength: prop.json(undefined as number | undefined),\n name: prop.string(),\n 'no-resize': prop.bool(false),\n placeholder: prop.string(),\n readonly: prop.bool(false),\n ref: prop.json(undefined as ((el: HTMLTextAreaElement | null) => void) | null | undefined),\n required: prop.bool(false),\n resize: prop.string<'none' | 'both' | 'horizontal' | 'vertical'>(),\n rows: prop.json(undefined as number | undefined),\n value: prop.string(),\n variant: prop.string<'flat' | 'solid' | 'bordered' | 'outline' | 'ghost'>(),\n },\n setup(props) {\n const emit = useEmit<OreTextareaEvents>();\n const watch = watchEffect;\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n const textareaRef = ref<HTMLTextAreaElement>();\n const autoResize = createAutoResize({ enabled: props['auto-resize'] });\n\n const abortSignal = lifecycleSignal(onCleanup);\n const tf = createTextField({\n disabled: fCtxProps.disabled,\n error: props.error,\n helper: props.helper,\n label: props.label,\n labelPlacement: props['label-placement'],\n maxLength: props.maxlength,\n onChange: (event: Event, value: string) => {\n emit('change', { originalEvent: event, value });\n },\n onInput: (event: Event, value: string) => {\n emit('input', { originalEvent: event, value });\n },\n prefix: 'textarea',\n readonly: props.readonly,\n required: props.required,\n signal: abortSignal,\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n tf.attachFormField(\n useField<string>({\n disabled: tf.disabled,\n onReset: () => {\n tf.reset();\n requestAnimationFrame(() => autoResize.recompute());\n },\n toFormValue: (v) => v,\n validationMessage: tf.validationMessage,\n validity: tf.validity,\n value: tf.value,\n }),\n );\n\n const {\n ariaDescribedBy,\n ariaErrorMessage,\n ariaInvalid,\n ariaLabelledBy,\n assistiveId,\n counter,\n errorId,\n errorText,\n fieldId: textareaId,\n helperText,\n labelId,\n labelVisible,\n } = tf;\n\n onElement(textareaRef, (textareaEl) => {\n const unwireEl = tf.wire(textareaEl);\n const unwireAutoResize = autoResize.wire(textareaEl);\n\n props.ref.value?.(textareaEl);\n\n const sub = rippleWatch(props.ref, (cb) => {\n cb?.(textareaEl);\n });\n\n const stopLayoutEffect = watch(() => {\n textareaEl.style.resize =\n props['auto-resize'].value || props['no-resize'].value ? 'none' : props.resize.value || 'vertical';\n\n // Deferred a frame: on mount (or right after `auto-resize` flips true) the browser\n // hasn't necessarily finished laying out the textarea yet, so `scrollHeight` read\n // synchronously here could be stale.\n requestAnimationFrame(() => autoResize.recompute());\n });\n\n return () => {\n sub.dispose();\n props.ref.value?.(null);\n unwireEl();\n unwireAutoResize();\n stopLayoutEffect();\n };\n });\n\n bind({\n attr: {\n error: errorAttr(errorText),\n size: fCtxProps.size,\n variant: fCtxProps.variant,\n },\n });\n\n const counterClass = () => counterClassName(counter?.value);\n const counterHidden = () => !counter;\n const counterText = () => counter?.value.counterText.replace(' / ', '/') ?? '';\n const helperHidden = () => !!errorText.value || !helperText.value;\n const errorHidden = () => !errorText.value;\n\n return html`\n <div class=\"textarea-wrapper\" part=\"wrapper\">\n <label class=\"label\" part=\"label\" for=\"${textareaId}\" id=\"${labelId}\" ?hidden=\"${() => !labelVisible.value}\"\n >${props.label}</label\n >\n <div class=\"field\" part=\"field\">\n <textarea\n part=\"textarea\"\n ref=\"${textareaRef}\"\n id=\"${textareaId}\"\n :name=\"${props.name}\"\n :placeholder=\"${props.placeholder}\"\n :rows=\"${props.rows}\"\n :maxlength=\"${props.maxlength}\"\n ?disabled=\"${props.disabled}\"\n ?readonly=\"${props.readonly}\"\n ?required=\"${props.required}\"\n :value=\"${live(tf.value)}\"\n :aria-describedby=\"${ariaDescribedBy}\"\n :aria-errormessage=\"${ariaErrorMessage}\"\n :aria-invalid=\"${ariaInvalid}\"\n :aria-labelledby=\"${ariaLabelledBy}\"></textarea>\n </div>\n <span class=\"${counterClass}\" aria-live=\"polite\" ?hidden=\"${counterHidden}\">${counterText}</span>\n <div id=\"${assistiveId}\" class=\"helper-text\" aria-live=\"polite\" part=\"helper\" ?hidden=\"${helperHidden}\">\n ${() => helperText.value}\n </div>\n <div id=\"${errorId}\" class=\"helper-text\" role=\"alert\" part=\"error\" ?hidden=\"${errorHidden}\">\n ${() => errorText.value}\n </div>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [...fieldMixins, sizeVariantMixin(TEXTAREA_SIZE_PRESET), forcedColorsFocusMixin('textarea'), componentStyles],\n});\n"],"mappings":"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"}
|