Code Monkey home page Code Monkey logo

jsx-slack's Introduction

Yuki Hattori (yhatt) ๐Ÿ‰

Follow X GitHub Sponsors

I'm senior frontend developer, weekend OSS maintainer, and the author of Marp, Markdown Presentation Ecosystem.

Sponsors

Organization sponsors

Organization sponsors

Personal sponsors

Personal sponsors

jsx-slack's People

Contributors

dependabot[bot] avatar javaphil avatar mashabow avatar nholden avatar nicosachse avatar nihalgonsalves avatar odanado avatar yhatt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsx-slack's Issues

Interactive components for radio button

Today Slack introduced new Block Kit blocks, includes radio buttons. Now developer seems to be able using radio button at modal app home.
https://api.slack.com/reference/block-kit/block-elements#radio

radio_buttons is available in following:

  • <Modal>
    • <Actions>
    • <Section>'s accessory
    • <Input>'s element

I've tried to use radio_buttons element in modal but failed to open modal. Probably the reference of above URL is wrong.

By referring to https://api.slack.com/reference/block-kit/interactive-components, radio buttons have only supported in App Home (#75).

ToDo

  • Consider interface
    • Based on existing <Select> component?
      • Internal spec is similar to static_select block elements.
    • or HTML-compatible <input type="radio"> elements?
      • It may be better to implement later as aliases for the created interactive components.
  • Implement radio button interactive component(s)
    • Interactive component
    • [ ] Input component
  • Update documents

responseUrlEnabled prop for input component of single select with channel and conversation

A newly added response_url_enabled field provides a lazy way to respond message into selected channel or conversation. To follow Slack API, we should add responseUrlEnabled prop into <ChannelsSelect> and <ConversationsSelect>.
https://api.slack.com/reference/block-kit/block-elements#conversation_select

Only working it within Modal's input block is a noteworthy point. In jsx-slack, I think it's better to add property only for input component.

<Modal title="modal">
  <ChannelsSelect
    blockId="channel"
    actionId="channel"
    label="Share to..."
    responseUrlEnabled
  />
</Modal>

The below case may work with transpiled by jsxslack template literal tag and Babel, but it would have to throw compile error in TypeScript because TS does not support contextual type narrowing by JSX component.

<Modal title="modal">
  <Input blockId="channel" label="Share to...">
    {/* May work but TypeScript would complain to `responseUrlEnabled` */}
    <ChannelsSelect actionId="channel" responseUrlEnabled />
  </Input>
</Modal>

And this case would just be ignored: responseUrlEnabled is only for single select. (TS throws compile error)

<Modal title="modal">
  {/*`responseUrlEnabled` and `multiple` cannot coexist */}
  <ChannelsSelect
    blockId="channel"
    actionId="channel"
    label="Share to..."
    responseUrlEnabled
    multiple
  />
</Modal>

Of course, following won't work: responseUrlEnabled cannot enable without (an implicit) input block.

<Blocks>
  <Section>
    Share to...
    {/* Error: `responseUrlEnabled` cannot use in accessory component of <Section> */}
    <ChannelsSelect actionId="channel" responseUrlEnabled />
  </Section>
</Blocks>

We may have to add a validation to check responseUrlEnabled, into <Section> accessory and <Actions> block.

Interpolation escaping forbids mrkdwn formatted links

I'm currently working on a project where most of the strings for the messages we send are stored on a CMS. One of the things we wanted to do is to allow the content writter to be able to insert links within the messages; we thought that using slacks' markdwn would be the way to go, but when trying to interpolate a CMS text with a link on it, the < and > symbols get escaped into their respective html entities, which causes the final message to not be what's expected.

For example:

const textWithLink = '<https://example.com|Click here> to read more about the topic!';
const message = (
  <Section>
    {textWithLink}
  <Section>
);

will output something like:

{
  ...
  text: {
    type: 'mrkdwn',
    text: '&gt;https://example.com|Click here&lt; to read more about the topic!',
    verbatim: true,
  }
}

which will cause the text to be rendered as-is instead of the intended result of having the "Click here" text pointing to the link.

Looking through the code, this seems to be part of the escapeEntity logic of the mrkdwn module and I couldn't find any way to skip that.
Is this behaviour intentional? Is there any way to mark my string as safe so it doesn't get escaped?

In any case, big congratz in the library; my team and I have been using it and we're really enjoying it!

<Escape> component will over-escape emoji shorthand

Now emoji shorthand such as :+1: is not included the escape target of <Escape> component. jsx-slack will render emoji even if wrapped in <Escape>.

Putting the discussion about whether escaping emoji shorthand aside, <Escape> component in the current specification is over-escaping emoji shorthand that has included underscore (_). Slack will render emoji consisted of a simple word such as :cat:, but emoji shorthand that has 2 and more words like :arrow_down: won't be rendered in <Escape> component because of over-escaped underscore.

If jsx-slack detected known emoji shorthand in <Escape> (with decision of keeping current specification), we have to disable underscore escaping only in emoji.

Interactive components for checkbox

Today Slack introduced new interactive component checkboxes.
https://api.slack.com/reference/block-kit/block-elements#checkboxes

It is available on <Modal> and <Home> surfaces: <Actions>, <Section>'s accessory, and <Input> element

The interface looks like radio button group with multiple value, but a difference important for jsx-slack is supporting mrkdwn text and mrkdwn description.

<Home>
  <Actions>
    <Checkboxes>
      <Checkbox value="plain_text">Plain text</Checkbox>
      <Checkbox value="raw_mrkdwn">_Raw mrkdwn_</Checkbox>
      <Checkbox value="rich_text">
        <b>Rich text</b> with <i>some stylings</i>
      </Checkbox>

      {/* Descriptions */}
      <Checkbox value="with_description" description="Description will show with dimmed text.">
        With description
      </Checkbox>
      <Checkbox
        value="with_rich_description"
        description={
          <Fragment>
            <b>Description has got <i>styling</i> too!</b>
          </Fragment>
        }
      >
        <p>With <b>rich</b> description</p>
        <blockquote>Block HTML elements also can use</blockquote>
      </Checkbox>
    </Checkboxes>
  </Actions>
</Home>

radio-buttons

There is room for considering about an interface of <Checkbox>: checked attribute property for <Checkbox> vs value property in the root <Checkboxes> component, <Fragment> in description property vs <small> element in children, etc...

Add interface for default_to_current_conversation field on conversation selects

Today Slack has added default_to_current_conversation field into (multi_)conversation_select menu.
https://api.slack.com/changelog#April_2020

A more direct route to messaging in response to modals: the new default_to_current_conversation field allows your conversation_select and multi_conversation_select menus to be pre-populate the currently open conversation.

Proposal

We have already a lot of props for <ConversationSelect> and have to make a simple interface if possible.

The added field is mutually exclusive with initial_conversations, so I think that to accept current special string in initialConversation prop (or aliased value prop) is better.

<Modal title="test">
  {/* Pre-select a conversation that opened modal from. */}
  <ConversationsSelect label="Send to..." initialConversation="current" />
</Modal>

At the present time, multi_conversations_select does not seem to be able to mix the current conversation along with specific conversations. Probably <ConversationsSelect multiple value={['current', 'C0123456789']} /> is valid as Block Kit JSON, but Slack API will ignore the state of default_to_current_conversation generated from current value.

Add React-compatible camelCase prop <time dateTime>

jsx-slack has supported HTML style <time datetime> attribute. On the other hand, React uses camelCase prop <time dateTime> normally. Both will work in React but not in jsx-slack.

For compatibility of React JSX, it's good to add an alias prop dateTime for datetime attribute.

<Confirm> props should make optional

While at work for jsx-slack v2 (#128), I've noticed title, confirm, and deny field in the confirm composition object have made optional in Slack platform.

The type of <Confirm> props in jsx-slack has marked as required, so we should relax them to optional props.

If omitted props, each buttons would be used the locale default label and the title would be empty. In the mobile client, it looks great as like as the native app. Web and PC client has a strange space for the title.

Over-escape for URL and time format is triggered by styling and <Escape>

<Escape> and format with corresponded special characters will break URL and time format by over-escaping.

<Blocks>
  <Section>
    <p><a href="https://example.com/a_b_c">link</a></p>
    <p><time datetime={1234567890} fallback="fail">{date} {time_secs}</time></p>
  </Section>
  <Section>
    <Escape>
      <p><a href="https://example.com/a_b_c">link</a></p>
      <p><time datetime={1234567890} fallback="fail">{date} {time_secs}</time></p>
    </Escape>
  </Section>
  <Section>
    <i>
      <p><a href="https://example.com/a_b_c">link</a></p>
      <p><time datetime={1234567890} fallback="fail">{date} {time_secs}</time></p>
    </i>
  </Section>
</Blocks>
{
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "<https://example.com/a_b_c|link>\n\n<!date^1234567890^{date} {time_secs}|fail>",
        "verbatim": true
      }
    },
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "<https://example.com/aหbหc|link>\n\n<!date^1234567890^{date} {timeหsecs}|fail>",
        "verbatim": true
      }
    },
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "_<https://example.com/aหbหc|link>_\n\n_<!date^1234567890^{date} {timeหsecs}|fail>_",
        "verbatim": true
      }
    }
  ]
}

URL in <a> link is broken by escaping, and <time> tag shows as fallback text.

<a> tag has a workaround: Move <Escape>/ styling element into the inside of <a> tag.

New handling way for HTML entities

Luckily escaping and decoding HTML entities in the current jsx-slack stands on a fine balance, but it's just like riding on a rolling ball.

Currentlly we escape string to HTML entity by heuristic parsing, but sometimes it would return unexpected result. We had not determined a specification handling entities, so there are some issues on to express the creative message.

I had tried to fix partially in #16 and #33, but I noticed to have to determine a new way of handling HTML entities.

New specifications

Because Slack does not provide a correct way to escape most characters, jsx-slack will continue to allow using raw characters to format message, like *, _, ~, and so on. However, some special characters, <, >, and & in the string, will always escape to HTML entities.

It means that raw mrkdwns like <!channel> and <https://example.com/|hyperlink> would not work under the new specification. Slack just would show an original text as you wrote. In most cases, this is an expected behavior and safer than before. Just use <a> HTML tag to use hyperlink.

<Call> layout block component

Calls API provides a way for call app to tell Slack the calling. In the context of Block Kit, the call type for block element has been available.

We will consider to add <Call> layout block to support indicating the call by your app.

<Blocks>
  <Section>
    <a href="@U1234567890" />, you've got a call!
  </Section>
  <Call callId="R123" />
</Blocks>

The above JSX will transform into following:

[
  {
    "type": "section",
    "text": {
      "type": "mrkdwn",
      "text": "<@U1234567890>, you've got a call!",
      "verbatim": true
    }
  },
  {
    "type": "call",
    "call_id": "R123"
  }
]

NOTE: <Call> is available only in <Blocks> container as same as <File> layout block.

Modal for workflow_step type

Newly added "Steps from apps" for Workflow Builder allows opening configuration modal by passing view object. However, we can be seen that the shape of payload has some differences by referring to the documentation:

  • The type property in the modal view will be workflow_step instead of modal.
  • Apps cannot specify title, submit or the cancel properties.

-- https://api.slack.com/workflows/steps#handle_config_view

For building Block Kit for the modal of workflow_step through jsx-slack, we should allow overriding type property for <Modal>.

<Modal type="workflow_step">
  <Section>
    ...
  </Section>
</Modal>

<Modal type="workflow_step"> does not require setting title, submit, and cancel close prop (including setting through <Input type="submit" />). Should ignore them even if set, and should throw type error in TypeScript when there are defined props of them.

Integration for Phelia framework

Phelia, the React-integrated framework for Slack Block Kit, has become popular suddenly in spite of the newcomer. Surprisingly it has amassed many stars than Bolt, the official framework by Slack.

It has unique components to build Block Kit JSON, with an awesome React-style event handler. However, React-style HTML tags cannot use in the content.

On the other hand, jsx-slack has a stable ergonomic templating and HTML rendering that had taken a year to reach. And we're still catching up Slack's update rapidly.

One of the crazy ideas is to provide jsx-slack integration to Phelia through another render function of JSX. Wisely merging of Phelia's awesome event handling (such as <Button onClick={}>) and jsx-slack's ergonomic Block Kit template would make the wonderful experience to create the Slack app and would grow the value of both libs more.

/* @jsx JSXSlack.h */
import React from "react";
import {
  JSXSlack, // JSXSlack.h is a render function for Phelia
  Message, // <Message> is inherited from Phelia
  Actions,
  Button,
} from "@speee-js/jsx-slack/phelia";
import randomImage from "../utils";

export function RandomImage({ useState }: PheliaMessageProps) {
  const [imageUrl, setImageUrl] = useState("imageUrl", randomImage());

  return (
    <Message text="Choose a dog">
      {/* Can use jsx-slack style templating that is familiar to HTML */}
      <img
        src={imageUrl}
        alt="a very adorable doggy dog"
        title="an adorable :dog:"
      />
      <hr />
      <Actions>
        <Button
          style="primary"
          actionId="randomImage"
          onClick={() => {
            // Handle event by Phelia
            setImageUrl(randomImage())
          }}
        >
          <p>
            <b>New doggy</b>
          </p>
        </Button>
      </Actions>
    </Message>
  );
}

Asynchronous component

I discovered jsx-slack v2 can use asynchronous component as following:

const AsyncComponent = async ({ userId }) => {
  // Fetch some data
  const name = await getUserName({ id: userId })

  return <Section>Hello, {name}!</Section>
}

;(async () => {
  console.log(
    <Blocks>
      <Context>Asynchronous component example</Context>
      <Divider />
      {await <AsyncComponent userId={123} />}
    </Blocks>
  )
})()

TypeScript requires to cast the component into any type.

This usage was found out by chance and it's out of our specification, but may have a worth to write down to the documentation as one of the useful way to use async functions.

Don't throw error even if <Overflow> has only one <OverflowItem>

Recently Slack seems to have loosened restriction of overflow menu items. Slack does no longer throw error even if overflow menu item has only one option.

Now Block Kit Builder shows error message "options must provide at least 1 items" when options has empty array.

Currently jsx-slack throws an error when trying to pass only one <OverflowItem> into <Overflow> because Slack API document is making clear to require 2 items.

I'm sure that the spec of Slack platform will continue to update internally. Thus, we would be better to leave some validations to Slack rather than to validate Block Kit on jsx-slack with taking the whole of document on trust. (Perhaps it applies to #81 too)

Modals support

Slack has announced new surface for Block Kit called Modals. It provides the second place to show Block Kit contents, and the first-class Block Kit support within dialog.

https://api.slack.com/block-kit/surfaces/modals

We've already supported the classic <Dialog> (#19), but it would be soft-deprecated in favor of Modals. Some interfaces for Dialog components would be able to apply into Modals too. (<Input>, <Textarea>, etc...)

ToDo

  • Consider interface
  • Add Modals support to main entry
    • Add attributes for Modals to existing components
    • <Input> block
      • Add HTML-like components based on existing dialog support
      • Add intrinsic elements for aliasing to added components
  • Soft-deprecate @speee-js/jsx-slack/dialog entry point
    • Don't mention the classic dialog support in README, but existing user can use entrypoint without warning
  • Update schema for demo

Styled channel links and mentions vanish

When styled the void <a> element for channel links / mentions, the content of link will vanish in an outputted JSON.

JSX

<Blocks>
  <Section>
    <b><a href="#C01234567" /></b>: Channel link
  </Section>
  <Section>
    <i><a href="@U01234567" /></i>: User mention
  </Section>
  <Section>
    <s><a href="@here" /></s>: Special mention
  </Section>
</Blocks>

JSON

[
  {
    "type": "section",
    "text": {
      "text": ": Channel link",
      "type": "mrkdwn",
      "verbatim": true
    }
  },
  {
    "type": "section",
    "text": {
      "text": ": User mention",
      "type": "mrkdwn",
      "verbatim": true
    }
  },
  {
    "type": "section",
    "text": {
      "text": ": Special mention",
      "type": "mrkdwn",
      "verbatim": true
    }
  }
]

jsx-slack should return *<#C01234567>*, _<@U01234567>_, and ~<!here|here>~. They are valid mrkdwn.

FYI, it seems there is no problem if it styles together with the other text as like as <b>text<a href="#C01234567" /></b>. I guess the cause is the empty content from a void <a /> tag against spec of HTML.

<Mrkdwn> return a lot of error

using the tag <Mrkdwn....>
generate a lot of error

I have also tried the example in
https://api.slack.com/reference/block-kit/composition-objects#text
but it generate this error:

[
{
type: 'section',
text: {
type: 'mrkdwn',
text: 'https://example.com/',
verbatim: '{false}'
}
},
{ type: 'section', fields: [ [Object] ] },
{ type: 'context', elements: [ [Object], [Object] ] }
]
2020-02-17T07:19:16.014Z 3523c5bf-21ca-4f9f-b2a2-23712cebaff4 ERROR Error: An API error occurred: invalid_blocks
at Object.platformErrorFromResult (/var/task/node_modules/@slack/web-api/dist/errors.js:50:33)
at WebClient.apiCall (/var/task/node_modules/@slack/web-api/dist/WebClient.js:465:28)
at processTicksAndRejections (internal/process/task_queues.js:94:5)
at module.exports (/var/task/api/lansweeper-events.js:107:17)
at Server. (/var/task/___now_helpers.js:875:13) {
code: 'slack_webapi_platform_error',
data: {
ok: false,
error: 'invalid_blocks',
response_metadata: { messages: [Array], scopes: [Array], acceptedScopes: [Array] }
}
}

Improve documentation

Now we have too long README.md to explain our features. jsx-slack must have a well-organized documentation to use easily.

ToDo

  • Migrate documentation from README.md to the other hosted site
  • Add / Improve more useful usages
    • Prefer using template literal to using JSX transpiler as a basic usage
    • Add how to define custom block

Convert html string to JSXSlack template

Hi, thank you for this library. It is very useful.

I have not been able to figure something out, if it is possible at all. I have html strings saved from somewhere that I would like to convert to Slack blocks using this library. The html strings conform to the standards specified for the html-like formatting that jsx-slack supports: https://github.com/speee/jsx-slack/blob/master/docs/html-like-formatting.md

I have simple strings like
"<p>Hello world</p>"

Is it possible to convert these strings to blocks using this library?

Container component for App Home surface

Today Slack introduced "App Home", the new Block Kit surface for the interaction between Slack app and user.
https://api.slack.com/reference/app-home

An usage of Block Kit for App Home is not so different from message. User still can compose contents for App Home by using jsx-slack's <Blocks> container.

So why should we create container component? Now the official document of layout blocks are sorted to show the supported blocks in each surfaces. Providing <Home> container component, with check valid blocks strictly (like #64), is better for composing correct blocks suitable with each view.

Allow `verbatim={boolean}` parameter

For text objects, Slack allows a parameter verbatim to be passed, allowing some elements to be parsed by slack, without needing specific mrkdwn syntax (e.g. channels, users, urls). From Slack's doc on text object: https://api.slack.com/reference/block-kit/composition-objects#text

When set toย falseย (as is default) URLs will be auto-converted into links, conversation names will be link-ified, and certain mentions will beย automatically parsed.ย Using a value ofย trueย will skip any preprocessing of this nature, although you can still includeย manual parsing strings. This field is only usable whenย typeisย mrkdwn.

It would be ideal to be able to use this attribute for components that support a text object (e.g. Section, Context, Fields (and pass them down to child elements). A specific use case where this would be ideal is rendering dynamic input in slack. If the dynamic content contains a URL, passing the text into a jsx-slack component such as a <Button> means you would first have to search for url's and wrap them with <a>...</a> to get them to render as links in Slack. Ideally you could opt into the verbatim parameter to simply pass the raw text to Slack to parse

Regression of <pre> tag in v0.9.0 about not preserved whitespaces

A released v0.9.0 has unexpected regression about <pre> tag: It has not preserved whitespaces like code indents.

<Blocks>
  <Section>
    <pre>{
  hello
}</pre>
  </Section>
</Blocks>

v0.8.1

[
  {
    "type": "section",
    "text": {
      "text": "```\n{\n  hello\n}\n```",
      "type": "mrkdwn",
      "verbatim": true
    }
  }
]

v0.9.0

[
  {
    "type": "section",
    "text": {
      "text": "```\n{\nhello\n}\n```",
      "type": "mrkdwn",
      "verbatim": true
    }
  }
]

Nested Fragments fail

Attempting to nest Fragments with multiple children leads to exception:

TypeError: Cannot read property 'children' of undefined
     at JSXSlack (.../node_modules/@speee-js/jsx-slack/lib/jsx.js:23:60)
     at children.reduce (.../node_modules/@speee-js/jsx-slack/lib/jsx.js:27:69)
     at Array.reduce (<anonymous>)
     at toArray (.../node_modules/@speee-js/jsx-slack/lib/jsx.js:25:53)
     at Object.JSXSlack (.../node_modules/@speee-js/jsx-slack/lib/jsx.js:37:20)
     at ...

To reproduce:

JSXSlack(
  <Blocks>
    <Fragment>
      <Fragment>
        <Section>A</Section>
        <Section>B</Section>
      </Fragment>
      <Fragment>
        <Section>C</Section>
        <Section>D</Section>
      </Fragment>
    </Fragment>
    <Fragment>
      <Fragment>
        <Section>A</Section>
        <Section>B</Section>
      </Fragment>
      <Fragment>
        <Section>C</Section>
        <Section>D</Section>
      </Fragment>
    </Fragment>
  </Blocks>
)

Improve escaping special characters to keep original character as possible

The fallback text of date localization won't parse special characters for formatting. So I suppose it can be applied to escape special characters.

{
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*bold* _italic_ ~strikethrough~ `code` ~_*`foobar`*_~"
      }
    },
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "<!date^12345678^{dummy}|*>bold<!date^12345678^{dummy}|*> <!date^12345678^{dummy}|_>italic<!date^12345678^{dummy}|_> <!date^12345678^{dummy}|~>strikethrough<!date^12345678^{dummy}|~> <!date^12345678^{dummy}|`>code<!date^12345678^{dummy}|`> <!date^12345678^{dummy}|~_*`>foobar<!date^12345678^{dummy}|`*_~>"
      }
    }
  ]
}

Some exceptions are the quote character and escaping the content within hyperlink. They have to apply the classic replacement with the other character.

{
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "<https://example.com/|<!date^12345678^{dummy}|*>test<!date^12345678^{dummy}|*>>"
      }
    },
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "<!date^12345678^{dummy}|>>"
      }
    },
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "<!date^12345678^{dummy}|&gt;>"
      }
    }
  ]
}

Pros

Compared to replacing with similar character, the rendered message have kept the original character (except the leading quote and characters in hyperlink). Slack user can copy the message almost exactly expected by app.

Cons

Consume +21 characters per special character or its group to escape. Many fields have the limit of maximum characters so characters for the content would be less.

Confirm Button should be green

<Confirm title="Commit your action" confirm="Yes, please" deny="Cancel">
  <b>Are you sure?</b> Please confirm your action again.
</Confirm>

^ Inside a RadioButtonGroup confirm leads to:

image

I propose the button should be green by default or adjustable.

Dialog support

Our idea of conversion from readable JSX to JSON is working well, and suceeded removing our painfuls in managing messages built on Block Kit. However, there still is an another painfull: Dialog JSON.

https://api.slack.com/dialogs

{
    "callback_id": "ryde-46e2b0",
    "title": "Request a Ride",
    "submit_label": "Request",
    "notify_on_cancel": true,
    "state": "Limo",
    "elements": [
        {
            "type": "text",
            "label": "Pickup Location",
            "name": "loc_origin"
        },
        {
            "type": "text",
            "label": "Dropoff Location",
            "name": "loc_destination"
        }
    ]
}

An additional idea is building JSON for dialog.open API by new <Dialog> component, as like as <Blocks>.

We not yet decide details of interface, but what a wonderful experience if dialog JSON would be written by JSX.

For example, the following conecpt code has structure like HTML <form> element.

<Dialog
  callbackId="ryde-46e2b0"
  title="Request a Ride"
  submitLabel="Request"
  notifyOnCancel={true}
  state="Limo"
>
  <label>Pickup Location</label>
  <input type="text" name="loc_origin" />
  <label>Dropoff Location</label>
  <input type="text" name="loc_destination" />
</Dialog>

Question regarding types

Hey,

I'm trying to use this lib in my TS project. Here is the snippet of my code:

app.event('app_home_opened', async ({ event, context }) => {
  try {
    /* view.publish is the method that your app uses to push a view to the Home tab */
    await app.client.views.publish({
      /* retrieves your xoxb token from context */
      token: context.botToken,

      /* the user that opened your app's app home */
      user_id: event.user,

      /* the view payload that appears in the app home*/
      view: {
        type: 'home',
        callback_id: 'home_view',

        /* body of the view */

        blocks: (
          <Blocks>
            <Section>
              Hello!!
            </Section>
          </Blocks>
        ),
      },
    })
  } catch (error) {
    console.error(error)
  }
})

TypeScript complains:

Screen Shot 2020-03-16 at 17 16 06

For search:

Type 'Element' is missing the following properties from type '(ImageBlock | Block | ContextBlock | ActionsBlock | DividerBlock | SectionBlock | InputBlock | FileBlock)[]': length, pop, push, concat, and 26 more.ts(2740)
index.d.ts(31, 5): The expected type comes from property 'blocks' which is declared here on type 'View'

I have no idea how to fix this. Can you help me please?

Filter props for <ConversationsSelect> (experimental)

Slack has shipped filter field for conversations select and its multiple version as beta.
https://api.slack.com/reference/block-kit/composition-objects#filter_conversations

If it was implemented in jsx-slack's <ConversationsSelect> component, I suppose that would become to specify in properties directly instead of using composition object.

<Blocks>
  <Section>
    Select DM channel:
    <ConversationsSelect
      multiple
      include={['im', 'mpim']}
      excludeBotUsers
    />
  </Section>
</Blocks>

During beta, jsx-slack should mark these props as experimental. There are known issues in iOS.

Native dialog support for template literal

The specification of <Select> and similar components has some differences between Block Kit and dialog. So jsxslack template literal in v0.8.0 cannot use dialog components without interpolation.

import { jsxslack } from '@speee-js/jsx-slack'
import { Dialog, Select, Option } from '@speee-js/jsx-slack/dialog'

console.log(jsxslack`
  <${Dialog} callbackId="example" title="Example">
    <${Select} name="rating" label="Rating" required>
      <${Option} value="5">${':star:'.repeat(5)}<//>
      <${Option} value="4">${':star:'.repeat(4)}<//>
      <${Option} value="3">${':star:'.repeat(3)}<//>
      <${Option} value="2">${':star:'.repeat(2)}<//>
      <${Option} value="1">${':star:'.repeat(1)}<//>
    <//>
  <//>
`)

Ideally, it should be better to use dialog components directly without interpolations.

import { jsxslack } from '@speee-js/jsx-slack'

console.log(jsxslack`
  <Dialog callbackId="example" title="Example">
    <Select name="rating" label="Rating" required>
      <Option value="5">${':star:'.repeat(5)}</Option>
      <Option value="4">${':star:'.repeat(4)}</Option>
      <Option value="3">${':star:'.repeat(3)}</Option>
      <Option value="2">${':star:'.repeat(2)}</Option>
      <Option value="1">${':star:'.repeat(1)}</Option>
    </Select>
  </Option>
`)

A main problem is the confusion of <Select> and similar components. The order to parse components in HTM is from the deepest children to the root. Thus, we cannot detect which kind of component will use in <Select> while parsing.

For better parsing, we might have to be lazy evaluating to virtual node on building JSON.

UPDATE: I found that lazy evaluation would bring broken JSON conversion. I'll try multi-time parsing on template literal to resolve target element.

JSX interface v2

Background

We have a virtual node interface of JSX element, to manipulate nodes easily for ergonomic templating. But it also has caused of always using a complex render function JSXSlack().

I suppose jsx-slack wants to have a simple interface without confusion and hesitation. It's the world JSXSlack() isn't needed.

v1.5.0 had a provisional improvement about usage of JSXSlack(): Some JSX components make serializable implictly due to implementing toJSON() function. However, there is ambiguous in the usability for developer. When developer wanted to manipulate raw JSON, still have to convert JSX into JSON by calling JSXSlack() explicitly.

In the new JSX interface, get rid of ambiguous and make developer use generated JSON simpler.

Proposal

The functional component for jsx-slack will return the actual JSON for output, and store meta data for JSX element in not-enumerable key of the object.

/** NOTE: The following is psuedo-code */

// Component returns JSON object
const Divider = () => ({ type: 'divider' })

// JSXSlack.h defines JSX metadata to the output
JSXSlack.h = (type, props, ...children) =>
  Object.defineProperty(
    type({ ...props, children }),
    '$$jsxslack',
    { value: { type, props, children } },
  )

console.log(JSON.stringify(<Divider />))
// => {"type":"divider"}

console.log((<Divider />).$$jsxslack)
// => { type: Divider, props: {}, children: [] }

JSON.stringify() ignores not-enumerable key so developer can use JSX element as serialized JSON for Slack API without using JSXSlack().

JSX metadata is required by built-in components to manipulate children nodes.

<Mrkdwn raw>

<Mrkdwn raw> is an idea to provide the solution for #160 right out of the box.

If enabled raw attribute, <Mrkdwn> won't parse the children as HTML that requires escaping some characters, and set the text to the content of text JSON field as is.

console.log(
  <Mrkdwn>
    {'Hey <@U024BE7LH>, thanks for submitting your report.'}
  </Mrkdwn>
)
// {
//   "type": "mrkdwn",
//   "text": "Hey &lt;@U024BE7LH&gt;, thanks for submitting your report."
// }

console.log(
  <Mrkdwn raw>
    {'Hey <@U024BE7LH>, thanks for submitting your report.'}
  </Mrkdwn>
)
// {
//   "type": "mrkdwn",
//   "text": "Hey <@U024BE7LH>, thanks for submitting your report."
// }

It can avoid escaping < and > by jsx-slack so would be useful for using the raw mrkdwn text, that is included advanced formatting with special parsing.

Using map inside of a fragment returns incorrect block structure

Hey there again!
I ran into an issue when trying to use a fragment to contain multiple blocks, including some generated by a call to an array's map.
For example, in this case:

<>
  <Section>Test</Section>
  {[1,2,3].map((number) => (
    <Section>{number}</Section>
  ))}
</>

would return

[
  { type: 'section', text: {...} },
  [
    { type: 'section', text: {...} },
    { type: 'section', text: {...} },
    { type: 'section', text: {...} },
  ],
]

while I would expect this instead

[
  { type: 'section', text: {...} },
  { type: 'section', text: {...} },
  { type: 'section', text: {...} },
  { type: 'section', text: {...} },
]

Using Blocks instead of a fragment seems to do the right thing and flatten the array out.

Is this is a known behaviour? Am I doing something wrong?

`Block` fragment is a little confusing

The Block fragment that contains all of the blocks is a little misleading since it isn't a single block but an array of blocks that you provide as children.

ie currently you have:

<Block>
   <Section ...>
   <Section ...>
</Block>

Is there a reason this isn't just:

<Blocks>
   <Section ...>
   <Section ...>
</Blocks>

?

Multi-select menus

Multi-select menus are new variation for select menu elements, to allow picking multiple options.

https://api.slack.com/messaging/interactivity#multi_select_menus
https://api.slack.com/reference/block-kit/block-elements#multi_select

We have to think JSX interface to support multi-select in jsx-slack. It might well to use multiple attribute in existing components.

<Blocks>
  <Section>
    ๐Ÿถ
    <Select actionId="dogs" placeholder="Choose favorite dog(s)..." multiple>
      <Option value="labrador">Labrador</Option>
      <Option value="german_shepherd">German Shepherd</Option>
      <Option value="golden_retriver">Golden Retriever</Option>
      <Option value="bulldog">Bulldog</Option>
    </Select>
  </Section>
</Blocks>

Remove a limitation of preview button in too long JSON payload on REPL demo

Recently updated Slack Block Kit Builder seems to be removed a limitation in too long JSON payload by changing how to pass JSON via URL. A previous way that includes payload in a query parameter had returned 414 error response with the long payload (Similar: #82). And now, Slack uses the hash (as same as our REPL demo) and no longer returns the error response from the server.

The classic way for opening preview with specific payload is still working, but it's better to update a way to generate URL for Block Kit Builder because the one of example has prevented opening preview by a classic limitation.

<File> block component

Recently Slack added new file type to layout blocks for supporting remote file.
https://api.slack.com/reference/messaging/blocks#file

We have to follow this addition by adding new <File> built-in component.

<Blocks>
  <File externalId="ABCD1" />
</Blocks>
[
  {
    "type": "file",
    "external_id": "ABCD1",
    "source": "remote"
  }
]

<File> component requires externalId prop. source prop is optional because remote is an only acceptable value at the moment.

<input type="radio"> interface

Related to #88. To support HTML-form style template in <Modal>, aliasing input component from <input type="radio"> to <RadioButtonGroup> / <RadioButton> is awesome.

Interface

<input type="radio"> has supported grouping by name attribute, but Slack Modals has to make clear group by radio_buttons. We have to bundle <input type="radio"> buttons whose same name into one radio button group in finalization process <Modal>.

Radio buttons for <Modal>

Today Slack shipped radio buttons for Modal surface.
https://api.slack.com/reference/block-kit/block-elements#radio

Currently jsx-slack allows using <RadioButtonGroup> and <RadioButton> only in <Home> for home tab. We have to remove this restriction.

In addition, we may consider the interface of <input type="radio" /> for HTML-form style templating. (Probably it's better for the other issue)

ToDo

  • Allow using <RadioButtonGroup> and <RadioButton> in...
    • <Modal> container component
    • <Input> block
      • <RadioButtonGroup> as input component

Question: How to keep linebreaks from variable

Hello yhatt.
This is a question and not an issue. I hope it is ok for me to ask here.

I am trying something like:

...
<Section>
  <b>{headline}</b>
  <br />
  {description}
</Section>
...

where description is a string like This is my description.\nWith linebreaks.\nTest

When i try to display it in slack the linebreaks in the description get lost somewhere in the parsing.
Can you tell me how to keep the linebreaks to be also displayed in slack?

Allow multiple mrkdwn elements in <Context>

<Context> merges continuous text contents into one mrkdwn automatically. However, someone may want to control mrkdwn elements separately for layouting message.

Separated mrkdwn have a look like fields in section block, or flexbox layout in HTML.

[
  {
    "type": "context",
    "elements": [
      {
        "type": "mrkdwn",
        "text": "foo\nbar",
        "verbatim": true
      },
      {
        "type": "mrkdwn",
        "text": "A ->\nB ----->\nC ->\nD -->",
        "verbatim": true
      },
      {
        "type": "mrkdwn",
        "text": "Hello,\nworld!",
        "verbatim": true
      }
    ]
  }
]

Our idea is to allow separated mrkdwn contents by using <div> HTML element. It would allow to use an interesting layout in <Context>.

<Blocks>
  <Context>
    <div>
      First mrkdwn element
    </div>
    <div>
      โ”<br />Second markdown element
    </div>
    <div>
      โ”Œโ†’<br />โ”˜
    </div>
    <img src="https://placekitten.com/100/100" alt="Kitten" />
  </Context>
</Blocks>

This layout makes sense by treating <Context> block as a flex container.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.