bolt-python icon indicating copy to clipboard operation
bolt-python copied to clipboard

value of `plain_text_input` element in dialog block cannot be cleared

Open knutwannheden opened this issue 1 year ago • 10 comments

(Filling out the following details about bugs will help us solve your issue sooner.)

Reproducible in:

The slack_bolt version

slack-bolt==1.13.1 slack-sdk==3.16.0

Python runtime version

Python 3.10.4

OS info

#51-Ubuntu SMP Thu Aug 11 07:51:15 UTC 2022

Steps to reproduce:

I have a bot which presents the user with a button. When the user clicks the button, a dialog is opened using app.client.views_open(). The dialog's view contains an input block which in turn declares "optional": True and then a plain_text_input element.

Now, this is where it gets a bit strange. Earlier today the user would get an error presented that input is required when pressing the dialog's Submit button. But now there is an indicator that the input is indeed optional. But when the user clears the input text and presses submit (or even replaces the input with one or multiple spaces), the original input value is kept. I am quite positive that I restarted my bot properly earlier today, so I don't understand how the behavior could change during the day.

In any case, a user should be able to clear the text of an optional plain_text_input in a dialog.

Expected result:

See above.

Actual result:

See above.

knutwannheden avatar Sep 15 '22 20:09 knutwannheden

Hi @knutwannheden 👋🏻 Thanks for reaching out when you came across this issue!

I took sometime to look into the status of the Slack API. Today there were some updates made to optional input fields, so this may have been related to your experience!

I've setup a test app that uses an optional input with a plain_text_input element. It appear to work correctly when receiving the submitted values. I also played with an initial_value to re-create your behaviour where the input's original value was kept when you submitted with spaces. On my side, the submitted values appear correct.

Here is the block kit sample that I used:

          {
            "type": "input",
            "optional": true,
            "element": {
              "type": "plain_text_input",
              "action_id": "plain_text_input-action",
              "initial_value": "An initial value"
            },
            "label": {
              "type": "plain_text",
              "text": "Label",
              "emoji": true
            }
          }

🕥 Now that some time has passed, are you still experiencing this odd behaviour? If so, can you provide us with a code sample that we can use to re-create your issue. 🙇🏻

Take care!

mwbrooks avatar Sep 16 '22 06:09 mwbrooks

@mwbrooks Those updates you mention probably explain why the behavior changed then.

I just tested it again and I still can't get it to work. When I clear the input (which had an initial value as you correctly assumed) and press "Submit" I see the following "invalid_blocks" error in my bot's log, which I otherwise don't have:

DEBUG:slack_sdk.webhook.client:Received the following response - status: 404, headers: {'date': 'Fri, 16 Sep 2022 18:58:15 GMT', 'server': 'Apache', 'x-powered-by': 'HHVM/4.153.1', 'x-frame-options': 'SAMEORIGIN', 'access-control-allow-origin': '*', 'referrer-policy': 'no-referrer', 'x-slack-backend': 'r', 'strict-transport-security': 'max-age=31536000; includeSubDomains; preload', 'x-xss-protection': '0', 'vary': 'Accept-Encoding', 'content-type': 'application/json; charset=utf-8', 'x-envoy-upstream-service-time': '172', 'x-backend': 'main_normal main_bedrock_normal_with_overflow main_canary_with_overflow main_bedrock_canary_with_overflow main_control_with_overflow main_bedrock_control_with_overflow', 'x-server': 'slack-www-hhvm-main-iad-gkfs', 'x-slack-shared-secret-outcome': 'no-match', 'via': 'envoy-www-iad-blnx, envoy-edge-fra-t4lm', 'x-edge-backend': 'envoy-www', 'x-slack-edge-shared-secret-outcome': 'no-match', 'connection': 'close', 'transfer-encoding': 'chunked'}, body: {"ok":false,"error":"invalid_blocks"}

Here is what my dialog looks like:

    app.client.views_open(
        trigger_id=body["trigger_id"],
        view={
            "type": "modal",
            "callback_id": "my_callback",
            "private_metadata": my_metadata,
            "title": {"type": "plain_text", "text": "Lobbybot"},
            "submit": {"type": "plain_text", "text": "Submit"},
            "blocks": [
                {
                    "type": "section",
                    "text": {
                        "type": "mrkdwn",
                        "text": my_label
                    }
                },
                {
                    "type": "input",
                    "block_id": "tweet_input_block",
                    "label": {"type": "plain_text", "text": "Text"},
                    "optional": True,
                    "element": {
                        "type": "plain_text_input",
                        "action_id": "my_text",
                        "initial_value": my_initial_value,
                        "multiline": True,
                        "focus_on_load": True
                    }
                }
            ]
        }
    )

knutwannheden avatar Sep 16 '22 19:09 knutwannheden

My dialog's content looks very similar to your example. Could it be related to the "multiline": true setting? Hopefully you will be able to work it out!

I should also mention that my handler (which would here be "my_callback") reads the value from the dialog and then copies it to another text field in the original message (done using respond(replace_original=True)). In the original the text field is declared like this:

            {
                "type": "section",
                "block_id": "my_block",
                "text": {
                    "text": "Some text",
                    "type": "mrkdwn"
                },
                "fields": [
                    {
                        "type": "mrkdwn",
                        "text": "A"
                    },
                    {
                        "type": "mrkdwn",
                        "text": "B"
                    },
                    {
                        "type": "plain_text",
                        "text": "C"
                    },
                    {
                        "type": "plain_text",
                        "text": "D"
                    }
                ]
            },

The dialog's text gets copied to the field which here has the text "C". Does a "plain_text" field in a "secion" block's "fields" maybe not support empty values?

knutwannheden avatar Sep 16 '22 19:09 knutwannheden

Hey @knutwannheden, are you able to share the handler and all related code? That would be easier for me to reproduce.

I'm also curious what sort of data you are storing the private_metadata field (for reproducibility's sake).

filmaj avatar Sep 16 '22 19:09 filmaj

The dialog's text gets copied to the field which here has the text "C". Does a "plain_text" field in a "secion" block's "fields" maybe not support empty values?

You might be on to something here. Looking at the reference documentation for the section Block Kit element I see this bit about the block_id (emphases mine):

A string acting as a unique identifier for a block. If not specified, one will be generated. You can use this block_id when you receive an interaction payload to identify the source of the action. Maximum length for this field is 255 characters. block_id should be unique for each message and each iteration of a message. If a message is updated, use a new block_id.

Are you by any chance using the same block_id when updating the message?

filmaj avatar Sep 16 '22 19:09 filmaj

Yes, it appears to be the case that the "plain_text" field doesn't allow for empty values. AFAICT there is no "optional" property I can set there. As a workaround I set the field to a single blank in my handler.

So, while I was first going to report an issue with the dialog (which got fixed while I reported it, basically), I think it now turns out that the issue is with the "plain_text" field in a "section" block. I am using the "fields" construct here, because I want the fields to be rendered using two columns.

Hey @knutwannheden, are you able to share the handler and all related code? That would be easier for me to reproduce.

I'm also curious what sort of data you are storing the private_metadata field (for reproducibility's sake).

I am unsure how helpful that would be in the end. But here is what I want to do: My bot proposes a Twitter tweet to be posted in a Slack message. The user can push an "Edit" button which brings up the dialog, where the Tweet can be adjusted, if necessary. When pressing "Submit" the edited text gets copied back to the original message and the user pushes the "Post" button to publish the tweet. Now there are in fact two fields (one for a German tweet the other for a French tweet) and if the user clears the text in the dialog, then no tweet should be posted for the respective language.

This is where I use the private_metadata. When I open the dialog I save the original message in a global dict using a unique key and then pass this key to the dialog using private_metadata, so that the handler later can modify the original message. This is very ugly, but I couldn't figure out how else to achieve this.

knutwannheden avatar Sep 16 '22 19:09 knutwannheden

Are you by any chance using the same block_id when updating the message?

Hmm... I will have to look into this. But the fact that everything works as long as the user doesn't enter an empty text into the dialog, makes me think it can't be related to the block_id.

Btw, thank you very much for the very speedy feedback!

knutwannheden avatar Sep 16 '22 19:09 knutwannheden

OK, yes, I have confirmed your suspicion! Indeed it looks like these plain_text elements under the fields of a section Block Kit element cannot have a zero-length string as text!

As a starting point I will see about getting our documentation updated. Secondly, I'll see what can be done to provide a better error message from the API. That must have been a frustrating experience - apologies for that!

filmaj avatar Sep 16 '22 19:09 filmaj

A really good way to highlight the issue is:

  • go to the section Block Kit documentation; at the end of the table row for the fields property, there is a link to a Block Kit Builder example.
  • open the example and edit the last plain_text field so that its text property is zero length.
  • see a validation error!

filmaj avatar Sep 16 '22 19:09 filmaj

Thank you for your help and patience!

knutwannheden avatar Sep 16 '22 20:09 knutwannheden