Skip to content

Commit

Permalink
Better dialog support for template literal (#43)
Browse files Browse the repository at this point in the history
* Allow using dialog compnoents in template literal

* Allows using "Dialog." prefix for template literal

* Resolve component in template by container context

* Fix incorrect closure level

* Update documentation

* Coerce number-expected prop to integer

* Prepare dialog demo

* Add REPL schema for dialog

* Coerce notifyOnCancel prop of <Dialog> to boolean

* Keep undefined value before coerce boolean prop

* Update dialog example to use Dialog prefix

* Update demo to provide selectable examples

* Fix prettier

* [ci skip] Update CHANGELOG.md
  • Loading branch information
Yuki Hattori authored Aug 7, 2019
1 parent 1ee4d0b commit 9eb2834
Show file tree
Hide file tree
Showing 14 changed files with 372 additions and 100 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## [Unreleased]

### Added

- Better dialog support for `jsxslack` template literal ([#42](https://github.com/speee/jsx-slack/issues/42), [#43](https://github.com/speee/jsx-slack/pull/43))
- Update REPL demo to add dialog example ([#43](https://github.com/speee/jsx-slack/pull/43))

### Fixed

- Coerce number-expected prop to integer ([#44](https://github.com/speee/jsx-slack/pull/44))
Expand Down
26 changes: 8 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,24 +191,6 @@ export default function exampleDialog(data) {

- **[JSX components for dialog](docs/jsx-components-for-dialog.md)**

> :information_source: Currently dialog components in `jsxslack` template literal have to use imported components from `@speee-js/jsx-slack/dialog`.
>
> ```javascript
> import { jsxslack } from '@speee-js/jsx-slack'
> import { Dialog, Input, Textarea } from '@speee-js/jsx-slack/dialog'
>
> const exampleDialog = data => jsxslack`
> <${Dialog} callbackId="create" title="Create">
> <${Input} name="name" label="Name" value=${data.name} required />
> <${Textarea} name="desc" label="Description" value=${data.desc} />
>
> <${Input} type="hidden" name="userId" value=${data.userId} />
> <${Input} type="submit" value="Create" />
> <//>
> `
> export default exampleDialog
> ```
## Fragments

[As like as React](https://reactjs.org/docs/fragments.html), jsx-slack provides `<Fragment>` (`<JSXSlack.Fragment>`) component for higher-order component (HOC) consited of multiple blocks or elements.
Expand Down Expand Up @@ -306,6 +288,14 @@ console.log(jsxslack`

Please notice to a usage of component that has a bit different syntax from JSX.

#### Note about dialog components

`<Select>` and similar components are provided by the both of [Block Kit](docs/jsx-components-for-block-kit.md#select-select-menu-with-static-options) and [Dialog](docs/jsx-components-for-dialog.md#select-select-field-with-static-options).

These within template literal will be resolved by the container component [`<Blocks>`](docs/jsx-components-for-block-kit.md#blocks-1) and [`<Dialog>`](docs/jsx-components-for-dialog.md#dialog-create-dialog-json) smartly, but the most use cases of `jsxslack.fragment` won't define the container so jsx-slack prefers [Block Kit components](docs/jsx-components-for-block-kit.md#select-select-menu-with-static-options).
You can use [dialog components](docs/jsx-components-for-dialog.md#select-select-field-with-static-options) correctly by adding prefix `Dialog.XXXXXX`. (e.g. [`<Dialog.Select>`](docs/jsx-components-for-dialog.md#select-select-field-with-static-options))
## Similar projects
- [slack-jsx](https://github.com/zcei/slack-jsx) - Compose Slack messages from JSX Components instead of writing JSON.
Expand Down
19 changes: 17 additions & 2 deletions demo/example.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const initialExample = `
export const blockKit = `
<Blocks>
<Section>
<p>
Expand All @@ -25,4 +25,19 @@ const initialExample = `
</Blocks>
`.trim()

export default initialExample
export const dialog = `
<Dialog callbackId="createUser" title="Create user">
<Input name="name" label="Name" required />
<Textarea name="desc" label="Description" maxLength="300" />
<!-- NOTE: Unprefixed Select also would work in Dialog container. -->
<Dialog.Select name="role" label="Role" value="regular" required>
<Option value="regular">Regular</Option>
<Option value="leader">Leader</Option>
<Option value="admin">Admin</Option>
</Dialog.Select>
<Input type="hidden" name="userId" value="xxxxxxxx" />
<Input type="submit" value="Create" />
</Dialog>
`.trim()
15 changes: 15 additions & 0 deletions demo/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ footer > p {
justify-content: center;
overflow: hidden;
padding: 0 20px;
user-select: none;
}

#preview > img {
Expand All @@ -198,3 +199,17 @@ footer > p {
#preview:hover:active {
background: #151;
}

#preview.disabled {
pointer-events: none;
opacity: 0.4;
}

#examples {
font-size: 15px;
height: 25px;
line-height: 25px;
display: block;
float: right;
cursor: pointer;
}
27 changes: 14 additions & 13 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>@speee-js/jsx-slack: Build JSON for Slack Block Kit from JSX</title>
<title>
@speee-js/jsx-slack: Build JSON for Slack Block Kit and dialog from JSX
</title>
<meta
name="Description"
content="Build JSON object for Slack Block Kit from readable JSX"
content="Build JSON object for Slack Block Kit and dialog from readable JSX"
/>
<link rel="stylesheet" type="text/css" href="index.css" />
</head>
Expand All @@ -21,7 +23,7 @@ <h1>
>@speee-js/jsx-slack</a
>
<small>
Build JSON for Slack Block Kit from JSX
Build JSON for Slack Block Kit and dialog from JSX
</small>
</h1>
<aside>
Expand Down Expand Up @@ -49,7 +51,15 @@ <h1>
</header>
<main>
<section>
<h2>JSX <small>(Try editting)</small></h2>
<h2>
REPL
<small>(Try editting)</small>
<select id="examples">
<option value="" selected>Choose examples...</option>
<option value="blockKit">Block Kit (default)</option>
<option value="dialog">Dialog</option>
</select>
</h2>
<div id="jsx"></div>
<div id="error" class="hide">
<img
Expand All @@ -63,15 +73,6 @@ <h2>JSX <small>(Try editting)</small></h2>
<section>
<h2>
<label for="json">JSON</label>
<small
>for
<a
href="https://api.slack.com/block-kit"
target="_blank"
rel="noopener"
>Block Kit</a
></small
>
</h2>
<textarea id="json" readonly></textarea>
</section>
Expand Down
57 changes: 47 additions & 10 deletions demo/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import CodeMirror from 'codemirror'
import debounce from 'lodash.debounce'
import { jsxslack } from '../src/index'
import example from './example'
import * as _examples from './example'
import schema from './schema'

import 'codemirror/mode/javascript/javascript'
Expand All @@ -17,10 +17,19 @@ const jsx = document.getElementById('jsx')
const json = document.getElementById('json')
const error = document.getElementById('error')
const errorDetails = document.getElementById('errorDetails')
const examplesSelect = document.getElementById('examples')
const previewBtn = document.getElementById('preview')

json.addEventListener('click', () => json.select())

const parseHash = (hash = window.location.hash) => {
if (!hash.toString().startsWith('#')) return undefined
return decodeURIComponent(hash.toString().slice(1))
}

const examples = Object.assign(Object.create(null), _examples)

// CodeMirror
const completeAfter = (cm, pred) => {
if (!pred || pred())
setTimeout(() => {
Expand Down Expand Up @@ -62,20 +71,36 @@ const jsxEditor = CodeMirror(jsx, {
indentUnit: 2,
lineWrapping: true,
mode: 'jsx',
value: example,
value: (() => {
const hash = parseHash()

if (examples[hash]) {
examplesSelect.value = hash
return examples[hash]
}

return examples.blockKit
})(),
})

const convert = () => {
try {
const output = jsxslack([jsxEditor.getValue()])

json.value = JSON.stringify(output, null, ' ')
previewBtn.setAttribute(
'href',
`https://api.slack.com/tools/block-kit-builder?blocks=${encodeURIComponent(
JSON.stringify(output)
)}`
)

if (Array.isArray(output)) {
previewBtn.removeAttribute('tabindex')
previewBtn.setAttribute(
'href',
`https://api.slack.com/tools/block-kit-builder?blocks=${encodeURIComponent(
JSON.stringify(output)
)}`
)
previewBtn.classList.remove('disabled')
} else {
previewBtn.setAttribute('tabindex', -1)
previewBtn.classList.add('disabled')
}

error.classList.add('hide')
} catch (e) {
Expand All @@ -87,6 +112,18 @@ const convert = () => {
}
}

jsxEditor.on('change', debounce(convert, 600))
const onChangeEditor = debounce(convert, 600)
jsxEditor.on('change', onChangeEditor)

convert()

examplesSelect.addEventListener('change', () => {
if (examplesSelect.value) {
jsxEditor.off('change', onChangeEditor)
setTimeout(() => jsxEditor.on('change', onChangeEditor), 0)

window.location.hash = `#${examplesSelect.value}`
jsxEditor.setValue(examples[examplesSelect.value])
convert()
}
})
Loading

0 comments on commit 9eb2834

Please sign in to comment.