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

dbc.Input does not behave like dcc.Input wrt pattern option #1045

Open
dagnic opened this issue Jul 12, 2024 · 10 comments
Open

dbc.Input does not behave like dcc.Input wrt pattern option #1045

dagnic opened this issue Jul 12, 2024 · 10 comments

Comments

@dagnic
Copy link

dagnic commented Jul 12, 2024

  • dash 2.17.0
  • dash-bootstrap-components 1.6.0
  • dash-core-components 2.0.0
  • dash-html-components 2.0.0

What is happening?

When dcc.Input content does not match the pattern, the border is coloured in red.
When dbc.Input content does not match the pattern, the border is NOT coloured in red.

What should be happening?

When dbc.Input content does not match the pattern, the border should be coloured in red, as it is the case for dcc.Input (out of the box, without the need to check the pattern in a callback and to set valid/invalid option accordingly).

This is especially true as the option description is identical for dcc and dbc.

pattern (string; optional): A regular expression that the control's value is checked against. The pattern must match the entire value, not just some subset. Use the title attribute to describe the pattern to help the user. This attribute applies when the value of the type attribute is text, search, tel, url, email, or password, otherwise it is ignored. The regular expression language is the same as JavaScript RegExp algorithm, with the 'u' parameter that makes it treat the pattern as a sequence of unicode code points. The pattern is not surrounded by forward slashes.

Code

import dash
from dash import html
from dash.dependencies import Input
from dash import dcc
import dash_bootstrap_components as dbc


app = dash.Dash(__name__)

app.layout = html.Div(
    children=[
        dbc.Input(  # try to replace by dcc.Input
            id="myvalue",
            type="text",
            pattern=u"[a-z]{6}$",
        ),
    ]
)

@app.callback(
    [Input("myvalue", "value")]
)
def write_text(value):
        print(value)

if __name__ == "__main__":
    app.run_server(debug=True)
@tcbegley
Copy link
Collaborator

This is because dash-core-components applies some additional styling when the input is in an invalid state. You can achieve the same effect with some custom CSS. Put the following in a CSS file in your assets/ folder

input:invalid {
    outline: solid red;
}

or if you prefer a more Bootstrappy style you could do something like this

input:invalid {
  border-color: #dc3545;
  padding-right: calc(1.5em + 0.75rem);
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
  background-repeat: no-repeat;
  background-position: right calc(0.375em + 0.1875rem) center;
  background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
  outline: none;
}

@isaacncz
Copy link

does it attached with invalid state in case the pattern does not match?

@AnnMarieW
Copy link
Contributor

Hi @isaacncz
can you make a minimal example where it's not working as expected?

@isaacncz
Copy link

i was trying to check if the input is valid.

dbc.Input( id="project-name-input", placeholder="Enter project name", type="text", pattern="^[a-zA-Z0-9 _-]*$" )

And want to make the component invalid if pattern doesnt match. Not sure how to get that working

@AnnMarieW
Copy link
Contributor

Did you try the solution above? What's not working? A complete example would be helpful.

@isaacncz
Copy link

isaacncz commented Oct 16, 2024

import dash
from dash import html
from dash.dependencies import Input
from dash import dcc
import dash_bootstrap_components as dbc

external_stylesheets = ["assets/test.css"]
app = dash.Dash(
      external_stylesheets=[external_stylesheets],
    )

app.layout = html.Div(
    children=[
        dcc.Input(  # try to replace by dcc.Input
            id="myvalue",
            type="text",
            pattern=u"[a-zA-Z0-9_-]*$",
        ),
    ]
)

@app.callback(
    [Input("myvalue", "value")]
)
def write_text(value):
        print(value)

if __name__ == "__main__":
    app.run_server(debug=True)

i have something like that.
What is not working: pattern attribute doesn't show invalid in my case.

@AnnMarieW
Copy link
Contributor

AnnMarieW commented Oct 16, 2024

Is your pattern incorrect? This live example on PyCafe is based on the original post and works for both dbc and dcc Input. Note that the dbc.Input shows the red outline after the input loses focus.

The example is editable - you can try it with your pattern too.

@AnnMarieW
Copy link
Contributor

@isaacncz

Just confirming that the problem is likely the pattern - I tried entering it in a vanilla html Input and it didn't work either:

https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/pattern

@isaacncz
Copy link

@isaacncz

Just confirming that the problem is likely the pattern - I tried entering it in a vanilla html Input and it didn't work either:

https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/pattern

Yea, the culprit is the pattern. I decided to stick with using callback for now.

@cainmagi
Copy link

Hello, all! I was inspecting a related issue (I have solved that issue by myself). Here, I can offer my inspections on this issue when I was checking my issue.

Reproduction of this issue

I think the pattern here needs to be changed to make it work:

- pattern=u"[a-zA-Z0-9_-]*$",
+ pattern=r"[a-zA-Z0-9_\-]*$",

Well, I do not think the original pattern is wrong, because it can be used in Python and JavaScript:

>>> import re
>>> re.fullmatch(u"[a-zA-Z0-9_-]*$", "dfdf-dfd")
<re.Match object; span=(0, 8), match='dfdf-dfd'>
>>> re.fullmatch(u"[a-zA-Z0-9_-]*$", "dfdf-df#d")
>>> 
> /^[a-zA-Z0-9_-]*$/.exec('dfdf-dfd')
['dfdf-dfd', index: 0, input: 'dfdf-dfd', groups: undefined]
> /^[a-zA-Z0-9_-]*$/.exec('dfdf-df#d')
null

I do not know why the original pattern does not work. Maybe the implementation of <input pattern> does not totally follow the same standards used in Python and JS.

Here is a reproducible example that may already solve this issue:

import dash
from dash import html
from dash.dependencies import Input
import dash_bootstrap_components as dbc


app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.index_string = R"""
<!DOCTYPE html>
<html>
    <head>
        {%metas%}
        <title>{%title%}</title>
        {%favicon%}
        {%css%}
        <style>
        .form-control.is-invalid,
        .form-control:invalid {
            border-color: #dc3545;
            padding-right: calc(1.5em + 0.75rem);
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23dc3545' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M12 13V8m0 8h.01M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0'/%3E%3C/svg%3E");
            background-repeat: no-repeat;
            background-position: right calc(0.375em + 0.1875rem) center;
            background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
            outline: none;
        }
        .form-control.is-valid,
        .form-control:valid {
            border-color: #198754;
            padding-right: calc(1.5em + 0.75rem);
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23198754' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M8.5 11.5L11 14l4-4m6 2a9 9 0 1 1-18 0a9 9 0 0 1 18 0'/%3E%3C/svg%3E");
            background-repeat: no-repeat;
            background-position: right calc(0.375em + 0.1875rem) center;
            background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
            outline: none;
        }
        </style>
    </head>
    <body>
        {%app_entry%}
        <footer>
            {%config%}
            {%scripts%}
            {%renderer%}
        </footer>
    </body>
</html>
"""

app.layout = html.Div(
    children=[
        dbc.Input(
            id="myvalue",
            type="text",
            pattern=r"[a-zA-Z0-9_\-]*$",
        ),
    ],
)


if __name__ == "__main__":
    app.run()

Well, I can confirm that the above configurations are not necessary. Actually, bootstrap has already provided a feature implemented by form:

import dash
import dash_bootstrap_components as dbc


app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

app.layout = dbc.Form(
    class_name="was-validated",
    children=[
        dbc.Input(
            id="myvalue",
            type="text",
            pattern=r"[a-zA-Z0-9_\-]*$",
        ),
    ],
)


if __name__ == "__main__":
    app.run()

These codes should have equivalent effects except for the icons.

Maybe the usage of the dbc.Form(class_name="was-validated") should be documented. Another choice is to add a reference link to the Bootstrap document to remind users to use this feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

5 participants