I wanted to build rich, interactive Slack and Discord workflows in a familiar idiom. Hence, a custom React renderer for declarative chat interactions.
- Support Slack's new Block-kit and Interactive Messaging Workflows.
- Attachments considered legacy/obsolete
- Each Component is a pure function with a parent-agnostic view of a Slack message entity (eg. a layout block). It's responsible for
render
ing its own JSON shape.- these
FCs
should always return a JSON entity that is a subtree of a Slack message.
- these
- Should be able to build USEFUL, self-contained components that can do asynchronous things. Don't need a full-blown hooks implementation, but you CAN make the JSX factory asynchronous.
- Microsoft Teams support
- more out-of-the-box elements
/** @jsx slack.h */
/** @jsxFrag slack.Fragment */
import {
slack,
render,
ContextBlock,
ImageElement,
PlainText,
FC,
} from 'react-chat-renderer';
const DeltaIndicator: FC<{delta: number}, any> = async ({ delta }) => {
await fakePromise();
return delta > 0 ? (
<ImageElement
altText="improved"
imageUrl="https://user-images.githubusercontent.com/97470/75739421-a7138180-5cb9-11ea-9547-e64acf86eb59.png"
/>
) : delta === 0 ? (
'okay!'
) : (
<ImageElement
altText="declined"
imageUrl="https://user-images.githubusercontent.com/97470/75739424-a7ac1800-5cb9-11ea-969a-e1ac9f12a41a.png"
/>
);
};
it('renders contextblock with component children', async () => {
const message = (
<ContextBlock>
<PlainText emoji>Hello, world</PlainText>
<DeltaIndicator delta={-3} />
<DeltaIndicator delta={0} />
</ContextBlock>
);
expect(await render(message)).toMatchSnapshot();
});
/** @jsx slack.h */
import {
slack,
DividerBlock,
SectionBlock,
ButtonElement,
PlainText,
MarkdownText,
ProgressBar,
Message,
} from '..';
const message = (
<Message responseType="in_channel">
<SectionBlock
accessory={<ButtonElement actionId="doAThing">Go!</ButtonElement>}
>
<PlainText emoji>section text :sadkeanu:</PlainText>
</SectionBlock>
<DividerBlock />
<SectionBlock blockId="section1">
<MarkdownText>
section ```code``` *progress:*{' '}
<ProgressBar color="red" columnWidth="10" total="300" value="200" />
</MarkdownText>
</SectionBlock>
</Message>
);
{
"response_type": "in_channel",
"as_user": false,
"blocks": [
{
"type": "section",
"accessory": {
"type": "button",
"text": {
"type": "plain_text",
"emoji": true,
"text": "Go!"
},
"action_id": "doAThing"
},
"text": {
"type": "plain_text",
"text": "section text :sadkeanu:",
"emoji": true
}
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "section ```code``` *progress:* `▓▓▓▓▓▓▓░░░`",
"verbatim": false
},
"block_id": "section1"
}
]
}