UNPKG

jsx-slack

Version:

Build JSON object for Slack Block Kit surfaces from JSX

287 lines (286 loc) 10.2 kB
/** @jsx createElementInternal */ import { InputBlock } from '@slack/types'; import { JSXSlack } from '../../jsx'; import { BuiltInComponent } from '../../jsx-internals'; import { DistributedProps } from '../../utils'; import { InputDispatchActionProps } from '../composition/utils'; import { ActionProps, AutoFocusibleProps } from '../elements/utils'; import { LayoutBlockProps } from './utils'; interface InputLayoutProps extends LayoutBlockProps { children: JSXSlack.Node; /** The label string for the interactive element. */ label: string; /** * By setting `true`, the input element will dispatch * [`block_actions` payload](https://api.slack.com/reference/interaction-payloads/block-actions) * when used this. */ dispatchAction?: boolean; /** Set a helpful text appears under the element. */ hint?: string; /** A HTML-compatible alias into `hint` prop. */ title?: string; /** * Set whether any value must be filled when user confirms modal. * * @remarks * HTML-compatible `required` prop means reversed `optional` field in Slack * API. _Please notice jsx-slack's default `required: false` is different from * Slack's default `optional: false`._ */ required?: boolean; } interface InputComponentBaseProps extends Omit<InputLayoutProps, 'children'> { /** * A string of unique identifier for the implicit parent `input` layout block. * * @input-component-remarks * _This is only working in input components enabled by defining `label` prop._ */ blockId?: string; /** * By setting `true`, the input element will dispatch * [`block_actions` payload](https://api.slack.com/reference/interaction-payloads/block-actions) * when used this. * * @input-component-remarks * _This is only working in input components enabled by defining `label` prop._ */ dispatchAction?: boolean; /** * Set `label` prop for the implicit parent `input` layout block, to display * the label string. * * @input-component-remarks * Please notice that this prop is **always required** in input components, * and _**never** in interactive elements for `<Section>` and `<Actions>`._ */ label: string; /** * Set a helpful text appears under the element. * * @input-component-remarks * _This is only working in input components enabled by defining `label` prop._ */ hint?: string; /** * Set whether any value must be filled when user confirms modal. * * @input-component-remarks * _This is only working in input components enabled by defining `label` prop._ * * HTML-compatible `required` prop means reversed `optional` field in Slack * API. _Please notice jsx-slack's default `required: false` is different from * Slack's default `optional: false`._ */ required?: boolean; } interface InputTextBaseProps extends Omit<InputComponentBaseProps, 'dispatchAction'>, ActionProps, AutoFocusibleProps, InputDispatchActionProps { children?: never; /** * The placeholder text shown in empty text field. * * Please notice the text input element cannot use emoji shorthand unlike the * other many plain text fields. */ placeholder?: string; /** * The _initial_ value of the input element. * * This prop would rather similar to `defaultValue` than `value` in React. A * defined value would be filled to the element only when the view was opened. * [`views.update`](https://api.slack.com/methods/views.update) cannot * update the text changed by user even if changed this prop. * * @remark * If `type` prop is `url`, `email`, or `number`, an initial value should be a * valid string for the type. */ value?: string; } export interface InputTextProps extends InputTextBaseProps { /** * Select input type from `text`, `url`, `email`, `number`, `hidden`, and * `submit`. * * The default type is `text`, for the input layout block with single-text * element. */ type?: 'text'; /** * Set the maximum number of characters user can enter into the text element. */ maxLength?: number; /** * Set the minimum number of characters user can enter into the text element. */ minLength?: number; } export interface InputURLProps extends InputTextBaseProps { type: 'url'; } export interface InputEmailProps extends InputTextBaseProps { type: 'email'; } export interface InputNumberProps extends Omit<InputTextBaseProps, 'value'> { type: 'number'; value?: number | string; /** * @doc-input-number * Set whether the number input element accepts decimal fractions. The default * value is `false`. */ decimal?: boolean; /** * @doc-input-number * The maximum value to accept for this number input. */ max?: number | string; /** * @doc-input-number * The minimum value to accept for this number input. */ min?: number | string; } interface InputHiddenProps { children?: never; type: 'hidden'; /** * @doc-input-hidden * A key of private metadata JSON to store. */ name: string; /** * @doc-input-hidden * A value of private metadata JSON to store. * * It must be able to serializable into JSON (except while using custom * transformer in the container). * */ value: any; } interface InputSubmitProps { children?: never; type: 'submit'; /** * @doc-input-submit * The label string of submit button for the modal. * * If the parent `<Modal>` has defined `submit` prop, this value will ignore. */ value: string; } export type InputComponentProps<P extends {}, // eslint-disable-line @typescript-eslint/ban-types T extends {} = {}> = DistributedProps<P | (P & InputComponentBaseProps & T)>; export type InputProps = DistributedProps<InputLayoutProps | InputTextProps | InputURLProps | InputEmailProps | InputNumberProps | InputHiddenProps | InputSubmitProps>; export declare const knownInputs: string[]; export declare const wrapInInput: <T extends object>(obj: T, props: Omit<Partial<InputLayoutProps>, 'children'>, generatedFrom?: BuiltInComponent<any>) => InputBlock | T; /** * `<Input>` has various usages: Input component for single text element, * helpers for the container, and Slack-style * [`input` layout block](https://api.slack.com/reference/messaging/blocks#input). * * It should place on immidiate children of container component. * * --- * * ### Input component for single-text * * `<Input label="..." />` means the input component for single text element and * will render `input` layout block containing with single-line text input. * * It has an interface very similar to `<input>` HTML element, but an important * difference is to require defining `label` prop. * * ```jsx * <Modal title="My App"> * <Input label="Title" type="text" name="title" maxLength={80} required /> * <Input label="URL" type="url" name="url" placeholder="https://..." /> * <Input label="Email" type="email" name="email" required /> * <Input label="Number" type="number" name="num" required min={1} max={100} /> * </Modal> * ``` * * --- * * ### Store hidden values to modal and home tab * * `<Input type="hidden" />` can assign hidden values for the private metadata * JSON of `<Modal>` and `<Home>` with a familiar way in HTML form. * * ```jsx * <Modal title="modal"> * <Input type="hidden" name="foo" value="bar" /> * <Input type="hidden" name="userId" value={123} /> * <Input type="hidden" name="data" value={[{ hidden: 'value' }]} /> * </Modal> * ``` * * Take care that the maximum length validation by Slack will still apply for * stringified JSON. The value like string and array that cannot predict the * length might over the limit of JSON string length easily (3000 characters). * * The best practice is only storing the value of a pointer to reference data * stored elsewhere. It's better not to store complex data as hidden value * directly. * * When the parent `<Modal>` or `<Home>` has assigned `privateMetadata` prop, * hidden values may override by assigned string or manipulate through the * custom transformer. * * --- * * ### Set the label of submit button for modal * * `<Input type="submit" />` can set the label of submit button for the current * modal. It's meaning just an alias into `submit` prop of `<Modal>`, but JSX * looks like more natural HTML form. * * ```jsx * <Modal title="Example"> * <Input name="name" label="Name" /> * <Input type="submit" value="Send" /> * </Modal> * ``` * --- * * ### Slack-style `input` layout block * * `<Input>` also can render * [`input` layout block](https://api.slack.com/reference/messaging/blocks#input) * as same usage as other components for Slack layout block. Please place one of * the available interactive component as a child. * * ```jsx * <Modal title="Register" submit="OK" close="Cancel"> * <Input label="User" title="Please select user." required> * <UsersSelect placeholder="Choose user..." /> * </Input> * </Modal> * ``` * * #### Available interactive components * * - `<Select>` * - `<ExternalSelect>` * - `<UsersSelect>` * - `<ConversationsSelect>` * * - `<ChannelsSelect>` * * - `<DatePicker>` * - `<TimePicker>` * - `<DateTimePicker>` * - `<CheckboxGroup>` * - `<RadioButtonGroup>` * * __*__: Some components have unique properties only for input components. You * cannot define them to the interactive component wrapped in `<Input>` layout * block because TypeScript would throw error while compile. * * **NOTE**: _We usually recommend to use input components instead of using * `<Input>` layout block._ This usage is provided for user that want templating * with Slack API style rather than HTML style. * * @return The partial JSON for `input` layout block or internal JSX element */ export declare const Input: BuiltInComponent<InputProps>; export {};