Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide a convenient way to select the whole text with type() #1043

Open
glenjamin opened this issue Aug 22, 2022 · 7 comments
Open

Provide a convenient way to select the whole text with type() #1043

glenjamin opened this issue Aug 22, 2022 · 7 comments
Labels
enhancement New feature or request needs specification The desired behavior is not defined yet

Comments

@glenjamin
Copy link

glenjamin commented Aug 22, 2022

Problem description

When using .type() I've been finding a common pattern that emerges is variations on the following

        await userEvent.type(titleInput, 'Edited title', {
          initialSelectionStart: 0,
          initialSelectionEnd: titleInput.value.length,
        });

To represent selecting the whole content and then overwriting it when typing

There was some related discussion in #755 (comment)

Suggested solution

Adding a new property like selectAll would be convenient, but probably opens up some awkward interactions if initialSelectionStart or initialSelectionEnd are also supplied.

From poking around in the code it looks like if I simply set a large value for initialSelectionEnd then it'll get clamped to the length of the input - which is basically what I want (although it's still quite verbose in a test). Perhaps the answer would be document the pattern of using a large number or Infinity as the selection end?

Additional context

No response

@glenjamin glenjamin added enhancement New feature or request needs assessment This needs to be looked at by a team member labels Aug 22, 2022
@ph-fritsche
Copy link
Member

ph-fritsche commented Aug 22, 2022

I'd rather not add any options to .type(), but remove the existing options in the future.
Reasons from the linked issue regarding a clear option apply.

The magic happening when these options are used is not obvious to many users and isn't in line with the philosophy to simulate a user interaction.

The .tripleClick() API provides a convenient way to describe a workflow that can actually be performed by a real user:

await user.tripleClick(element) // selects a whole line - in <input/> this is everything
await user.keyboard('somethingNew')

@glenjamin
Copy link
Author

That makes sense, but presumably that can't be combined with type as it would reset the selection?

The description for type says

You can use type() if you just want to conveniently insert some text into an input field or textarea.

Is it no longer a goal to have a convenient way to suffinctly fill in a form?

@ph-fritsche
Copy link
Member

Without the options, .type(element, 'foo') is:

await user.click(element)
await user.keyboard('foo')

If that's what you want, you can use .type().

If you use skipClick option, just use .keyboard().
If you use initialSelection*, consider setting the selection like a user either per .pointer() or .keyboard(). (For shortcuts see https://testing-library.com/docs/user-event/convenience)

To me

await user.type(element, 'foo', {initialSelectionStart: 0, initialSelectionEnd: 1000})

doesn't seem more convenient than

await user.tripleClick(element)
await user.keyboard('foo')

But the latter is unambiguous regarding the events that should happen.

@glenjamin
Copy link
Author

I see what you're saying - I think my mental model was that the convenience helpers are a way of succinctly expressing what the user is doing at a higher level.

My app doesn't care that they use triple-click to select the text, the user interaction I care about is that the user replaces the content of the text box with new text.

I can write a wrapper for my own tests but that ends up being quite specific to my own code.

@ph-fritsche
Copy link
Member

Would a .selectAndType(element, 'text') API satisfy your use case?

@ph-fritsche ph-fritsche added needs specification The desired behavior is not defined yet and removed needs assessment This needs to be looked at by a team member labels Sep 1, 2022
@glenjamin
Copy link
Author

Yeah, something specifically intended to replace a whole textbox would be great! 👍

@phegman
Copy link

phegman commented Aug 30, 2023

I created my own custom clearAndType utility like this:

// custom-user-events.ts

import {
  UserEvent,
  UserEventApi,
} from "@testing-library/user-event/dist/types/setup/setup";

type CustomUserEvents = {
  clearAndType: (
    ...args: Parameters<UserEventApi["type"]>
  ) => ReturnType<UserEventApi["type"]>;
};

export async function clearAndType(
  user: UserEvent,
  ...args: Parameters<CustomUserEvents["clearAndType"]>
): Promise<void> {
  const [element] = args;

  await user.clear(element);
  await user.type(...args);
}

export default CustomUserEvents;
// utils.tsx

import * as customUserEvents from "./custom-user-events";
import type CustomUserEventTypes from "./custom-user-events";

function setup(jsx) {
  const baseUser = userEvent.setup();
  const customUser = Object.entries(customUserEvents).reduce(
    (accumulator, [key, customUserEvent]) => {
      return {
        ...accumulator,
        [key]: customUserEvent.bind(null, baseUser),
      };
    },
    {}
  ) as CustomUserEventTypes;

  const user = {
    ...baseUser,
    ...customUser,
  };

  return {
    user,
    ...render(jsx),
  };
}
// foo-spec.tsx

import { setup } from './utils'

test('edits name', async () => {
  const { user } = setup(<MyComponent />)

  user.type(screen.getByLabelText("Name"), "Dwight Schrute")
  user.clearAndType(screen.getByLabelText("Name"), "Michael Scott")
  expect(screen.getByLabelText("Name")).toHaveValue("Michael Scott")
})

Works for me and can't really think of any reason why this would be a bad idea. Feels kind of similar to custom queries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request needs specification The desired behavior is not defined yet
Projects
None yet
Development

No branches or pull requests

3 participants