microsoft / fluentui

Fluent UI web represents a collection of utilities, React components, and web components for building web applications.
https://react.fluentui.dev
Other
18.48k stars 2.73k forks source link

Allow custom regex in the MaskedTextField #5872

Closed yanckst closed 5 years ago

yanckst commented 6 years ago

Hi,

I followed how to put a mask in the MaskedTextField. What I see is we can only allow specific patterns like the telephone number example: (999) 999-9999 or some mask with letters.

In my case, I would like to have a custom mask depending on what the user enters. For example, I would like to have a RegEx pattern like ^\d{1,2}\.\d{1,2}\.\d{1,2}\.\d{1,2}$. The user could enter 1.2.3.4 or 11.22.33.44. I want also my "mask character" to follow the RegEx pattern.

If the user enter 1 digit, I would like to have my mask like: 1._._._. Otherwise if the user enter 2 digits, I would like my mask like: 11.__.__.__

I don't know if it's possible or it wasn't design for that, but let me know.

Thanks!

natalieethell commented 6 years ago

Hey @yanckst! The MaskedTextField supports regex with some limitations--the use case you described is currently not supported. The supported cases are for single character regex matches, such as /[0-9]/ for digits or /[a-zA-Z]/ for letters. I just added this to our backlog, though, and tagged it as an "enhancement".

lambertwang-zz commented 6 years ago

Currently, the Masked text field assumes that any mask character will match at most one character of input. Allowing the 'mask character' to follow arbitrary regular expressions introduces a large amount of complexity to the component. There isn't a clear definition of how the component would look if I wanted to use the expression /*/ for my mask.

In your example described above, the component would need to compute the length of the largest word of the regular language that the Regex expresses to know how many mask characters to show. That may be infinite depending on the regex.

If you want the mask to support 1 or 2 digits, then you can use two consecutive masks for /[0-9]/. The mask would be M\ask: 99.99.99.99 and the mask text would like like this:

Though this behavior is not exactly what you're asking for. In the mean time, it may be better to customize a TextField component yourself to fit your needs.

natalieethell commented 6 years ago

Thanks @lambertwang!

yanckst commented 6 years ago

@natalieethell Thanks for your quick answer. I will wait until this feature comes. Thank you !

@lambertwang Thanks! By the way, your mask, in your example, was already what I did. This RegEx support 1 or 2 digits as well, but the mask character stays there. It will be : 1_.1_.1_.1_ So, if a basic user want to enter value in this Textbox, I think it will confuse him to see the "_" between digits. Furthermore, you need to navigate with the mouse or the arrow keys on the keyboard to move the cursor for adding another digit. That's why, I wanted my mask character to follow the RegEx I gave.

If we use a /*/ pattern, we'll see a mask like : Mask: /_/. We can put any letters or digits. We cannot use any special characters like ':^[]{}, etc. Maybe we should support them as well. If you are living in a country that has special characters like ç î ô é à in France or Canada, we are limiting the user to use only a-zA-Z characters, It would be nice to support them too. I don't know if we can add another feature request or it will be implemented in this feature. Please let me know.

lambertwang-zz commented 6 years ago

@yanckst Even if you used a regex that matched one or two digits, you would still need to navigate with the mouse or arrow keys to move between digits. If you wanted to input 1.1.1.1 and entered the keypresses 1111 the TextField would show 11.11._._.

Regarding the global pattern match, a user in practice would be far better off using a standard TextField component.

Also, special characters and accented characters are definitely supported right now:

public render() {
    return (
      <div>
      <MaskedTextField
        label="Special Characters"
        mask="ma\sk: ssssss"
        maskFormat={{'s': /[\!\@\#\$\%\^\&\*\(\)]/}}
        />
      <MaskedTextField
        label="Accent Characters"
        mask="ma\sk: ssssss"
        maskFormat={{'s': /[çîôéàa-zA-Z]/}}
        />
      </div>
    );
  }
}
yanckst commented 6 years ago

@lambertwang Thank you! I didn't know about the maskFormat. It is amazing.

I tried something different for my MaskedTextField. I tried to changed the mask on user input (keypress). My mask is a state that will be updated. So, when the user enter the first digit, I do nothing because I can't tell if the mask will be 1.1.1.1 or 11.11.11.11. When the user enters a "." or a digit, I do a setState for updating the mask. Changing the mask is working, but I loose the user input even if I put a defaultValue.

So, I see that the component needs a specific pattern and I cannot be updated. It isn't designed for that. For the moment, I'll use a standard TextField, like you said, and I'll validate the user input with my RegEx and show an error message if it's invalid. I'll wait until this feature comes. Hope so!

Thanks!

yanckst commented 6 years ago

I found a bug with the setStates (issues/5879). Is it why the setStatesdoesn't work properly?

ecraig12345 commented 5 years ago

@yanckst Actually I think the issue you described is closer to #5539 about supporting on-the-fly mask updates.

Is it okay with you if I close this issue in favor of #5539, since fixing that would provide another way to solve your problem? Although that one will also be a challenge to fix (and I can't guarantee how it will be prioritized), it's more feasible than supporting an arbitrary regex with patterns that could match multiple characters.

yanckst commented 5 years ago

@ecraig12345 Yeah of course !