` or the control group around it.
-test('ComboboxField resolves inputRef to the input element, not a wrapper', async () => {
- const ref = createRef
();
- render(
-
- {renderCountryItem}
- ,
- );
-
- const input = page.getByRole('combobox', { name: 'Country' });
- await expect.element(input).toBeVisible();
-
- expect(ref.current).toBeInstanceOf(HTMLInputElement);
- expect(ref.current).toBe(input.element());
-});
-
-test('ComboboxField resolves a callback inputRef to the input element', async () => {
- const resolved: Array = [];
- render(
- {
- resolved.push(node);
- }}
- label="Country"
- name="country"
- >
- {renderCountryItem}
- ,
- );
-
- const input = page.getByRole('combobox', { name: 'Country' });
- await expect.element(input).toBeVisible();
-
- expect(resolved.at(-1)).toBeInstanceOf(HTMLInputElement);
- expect(resolved.at(-1)).toBe(input.element());
-});
-
-// `name` lands on the hidden input React Aria renders for form submission, not on
-// the visible combobox — the visible one holds the filter text, which is not the
-// value. The submitted value is the selected key by default (`formValue`).
-test('ComboboxField forwards name so a native form submit collects the selected key', async () => {
- const { locator: scene } = render(
- ,
- );
-
- const input = page.getByRole('combobox', { name: 'Country' });
- await expect.element(input).toBeVisible();
- expect(input.element()).not.toHaveAttribute('name');
-
- const form = scene.element().querySelector('form');
- if (form == null) throw new Error('Expected the form element.');
- expect(new FormData(form).get('country')).toBe('');
-
- await userEvent.click(input);
-
- const option = page.getByRole('option', { name: 'Canada' });
- await expect.element(option).toBeInTheDocument();
-
- // Clicking an option scrolls it into view first, and React Aria closes the popover on a
- // document scroll. While the popover is still entering, that close lands before the click and
- // detaches the option.
- const popover = page.getByRole('listbox').element().parentElement;
- if (popover == null) throw new Error('Expected the popover element.');
- await waitForOverlayEnter(popover);
-
- await userEvent.click(option);
-
- expect(new FormData(form).get('country')).toBe('ca');
-});
-
-test('ComboboxField forwards onBlur to the input', async () => {
- const blurs: Array = [];
- render(
- <>
- {
- blurs.push('country');
- }}
- >
- {renderCountryItem}
-
-
- >,
- );
-
- const input = page.getByRole('combobox', { name: 'Country' });
- await expect.element(input).toBeVisible();
-
- await userEvent.click(input);
- await expect.element(input).toHaveFocus();
- expect(blurs).toEqual([]);
-
- // Clicking the input opens the listbox, whose popover would swallow the click on
- // the button behind it; Escape closes it without moving focus.
- await userEvent.keyboard('{Escape}');
- await expect.element(page.getByRole('listbox')).not.toBeInTheDocument();
-
- await userEvent.click(page.getByRole('button', { name: 'Next' }));
- expect(blurs).toEqual(['country']);
-});
-
/** The control group wrapping the combobox input labelled `name`. */
function getControl(name: string) {
const control = page
diff --git a/packages/@luke-ui/react/src/conformance/helpers.ts b/packages/@luke-ui/react/src/conformance/helpers.ts
index b8bc41cb..1add77e8 100644
--- a/packages/@luke-ui/react/src/conformance/helpers.ts
+++ b/packages/@luke-ui/react/src/conformance/helpers.ts
@@ -94,6 +94,22 @@ export function testFieldShapedConformance(options: FieldConformanceOptions) {
result.unmount();
form.remove();
});
+
+ // React Aria types `inputRef` as a ref object. Luke UI widens it to accept a
+ // callback so React Hook Form's `field.ref` works without an adapter.
+ test(`${name} resolves a callback inputRef to the control`, () => {
+ const resolved: Array = [];
+ const result = render({
+ inputRef: (node: HTMLElement | null) => {
+ resolved.push(node);
+ },
+ name: 'conformance-field',
+ });
+ const control = getControl(result);
+
+ expect(resolved.at(-1)).toBe(control);
+ result.unmount();
+ });
}
export function testIntegration(
diff --git a/packages/@luke-ui/react/src/text-field/text-field.browser.test.tsx b/packages/@luke-ui/react/src/text-field/text-field.browser.test.tsx
index 1f4b7468..9355235a 100644
--- a/packages/@luke-ui/react/src/text-field/text-field.browser.test.tsx
+++ b/packages/@luke-ui/react/src/text-field/text-field.browser.test.tsx
@@ -73,43 +73,6 @@ function indicatorFor(name: string): SVGSVGElement | null {
return groupFor(name).querySelector(`.${invalidIndicatorClass}`);
}
-test('the indicator icon adds no text to the accessible name', async () => {
- render();
-
- const input = page.getByRole('textbox', { name: 'Invalid' });
- // The field's label is an external `