ToolJet Button 组件完全指南:属性、事件、组件动作与样式配置详解
【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 🚀项目地址: https://gitcode.com/GitHub_Trending/to/ToolJet
本文基于 ToolJet 开源仓库中 docs/docs/widgets/button.md 官方文档撰写,并结合 Button 组件源码、组件配置定义 与 Cypress 端到端测试 进行深度佐证与扩充。文中所涉相对路径均以仓库根目录为起点。
组件定位:Button 是 ToolJet 应用中的动作触发器
Button组件是 ToolJet 低代码应用构建器中最基础也最常用的交互组件。它的核心职责是触发一个动作(Action)——既可以是提交表单、导航到另一个页面,也可以是触发一条数据查询(Query),例如调用 REST API、更新数据库记录或运行 JavaScript。
在 ToolJet 的组件配置体系中,Button 的定位由 button.js 配置定义 中的元信息明确给出:
- 名称:
Button - 描述:
Trigger actions: queries, alerts, set variables etc.(触发查询、告警、设置变量等动作) - 默认尺寸:宽 4 格、高 40 像素
- 默认标签:
Button,默认类型为primary(实心)
从源码结构可以推断,Button 的渲染实现集中在 frontend/src/AppBuilder/Widgets/Button.jsx,它负责将配置面板中的属性、样式、事件配置转化为可交互的 HTML 按钮,同时通过暴露变量(Exposed Variables)和组件特定动作(CSA)与外部查询、JavaScript 代码联动。
Properties(属性)
| 属性 | 描述 | 期望值 |
|---|---|---|
| Label | 按钮上显示的文本 | 字符串(例如Submit) |
Label 属性的配置在 button.js 中被定义为text字段,类型为code(代码编辑器),校验规则要求其值为字符串。这意味着 Label既可以是静态文本,也可以是动态表达式。
Label 的动态绑定
在 Button 组件的源码 Button.jsx 中,Label 被初始化为 React state,并通过useEffect监听properties.text的变化实时刷新:
const [label, setLabel] = useState(typeof properties.text === 'string' ? properties.text : ''); useEffect(() => { if (typeof properties.text === 'string') { setLabel(properties.text); setExposedVariable('buttonText', properties.text); } }, [properties.text]);由此可知,Label 支持以下几类取值:
- 静态字符串:
Send Message、Delete - 动态表达式:
{{queries.xyz.data.action}}—— 用双花括号包裹的 JavaScript 表达式会在运行时求值,例如读取某条查询的返回数据作为按钮文本
渲染时,文本会经过getSafeRenderableValue安全处理(见 utils.js),该函数会:
- 直接返回字符串、数字、布尔值等原始类型;
- 对其他类型(如对象)尝试调用
String()转换,失败则回退为空字符串,避免渲染异常。
小提示:有关 ToolJet 中可用的全部动作类型(Actions)的详细说明,可参考仓库中 docs/docs/actions 目录下的动作参考文档,其中包括 RunJS、RunQuery、ShowAlert、导航等动作的完整配置说明。
Events(事件)
| 事件 | 描述 |
|---|---|
| On click | 用户点击按钮时触发 |
| On hover | 用户将鼠标光标悬停在按钮上时触发 |
这两个事件在 button.js 中注册为组件可绑定的事件处理器:
events: { onClick: { displayName: 'On click' }, onHover: { displayName: 'On hover' }, },源码中的事件触发机制
在 Button.jsx 中,handleClick是点击事件的核心处理函数:
const handleClick = () => { if (!disable && !loading) { const event1 = new CustomEvent('submitForm', { detail: { buttonComponentId: id, buttonModuleId: moduleId } }); document.dispatchEvent(event1); fireEvent('onClick'); } };这段代码揭示了 Button 事件的两个底层细节:
- 表单提交联动:点击按钮时会派发一个
submitForm自定义事件(CustomEvent),携带buttonComponentId和buttonModuleId信息。这意味着将按钮与支持表单提交的组件(如表单容器)配合使用时,点击按钮即可触发表单的提交逻辑。 - 事件处理器触发:随后调用
fireEvent('onClick'),通知构建器执行用户在事件配置面板中绑定的动作链(如 RunQuery、ShowAlert、导航等)。 - 守卫条件:当按钮处于禁用(
disable)或加载(loading)状态时,点击事件不会触发,这从源码层面保证了「加载中不可重复提交」的交互正确性。
对于On hover事件,源码通过onMouseOver/onMouseLeave监听鼠标进出:
onMouseOver={() => { setHovered(true); }} onMouseLeave={() => { setHovered(false); }}hovered状态变化会触发fireEvent('onHover')(见 Button.jsx)。源码注释特别说明:之所以使用mouseover而非mouseenter,是因为mouseenter在子组件上触发不稳定,而mouseover对所有子组件都能可靠触发。
Component Specific Actions(CSA,组件特定动作)
ToolJet 支持通过**组件特定动作(CSA)**以编程方式控制 Button 组件的状态。这些动作既可以在 RunJS 查询中调用,也可以通过事件配置触发。
| 动作 | 描述 | 调用方式 |
|---|---|---|
click() | 模拟/控制按钮的点击 | RunJS 查询:await components.button1.click(),或通过事件触发 |
setText() | 设置组件标签文本 | RunJS 查询:await components.button1.setText('Update'),或通过事件触发 |
setVisibility() | 设置组件可见性 | RunJS 查询:await components.button1.setVisibility(false),或通过事件触发 |
setLoading() | 设置组件加载状态 | RunJS 查询:await components.button1.setLoading(true),或通过事件触发 |
setDisable() | 禁用/启用组件 | RunJS 查询:await components.button1.setDisable(true),或通过事件触发 |
注:原文档中
setVisibility示例写的是textinput1,实际应用中请替换为目标组件(如button1)的实例名。
源码中的 CSA 注册机制
这些动作在 button.js 的actions数组中声明,每个动作包含handle(调用句柄)、displayName和参数定义。以setText为例:
{ handle: 'setText', displayName: 'Set text', params: [{ handle: 'text', displayName: 'Text', defaultValue: 'New Text' }], },在渲染组件 Button.jsx 中,这些动作通过setExposedVariables/setExposedVariable暴露给运行时:
const exposedVariables = { click: async function () { if (canClick) { fireEvent('onClick'); } }, setText: async function (text) { setLabel(text); setExposedVariable('buttonText', text); }, ... }; setExposedVariables(exposedVariables);关键实现要点:
click()动作:与物理点击一致,只有当canClick(即!disable && !loading)为真时才触发onClick事件,保证了编程触发与用户点击行为的一致性;setText()动作:更新内部label状态的同时,同步更新暴露变量buttonText,实现文本与数据源的联动;setVisibility()/setDisable()/setLoading():均会将传入值强制转换为布尔值(!!value),并同步更新对应的暴露变量(isVisible/isDisabled/isLoading)。
此外,配置中还保留了三个标记为deprecated(已废弃)的旧动作句柄:disable、visibility、loading,以兼容旧版本创建的应用,新开发场景应使用带set前缀的规范动作。
Exposed Variables(暴露变量)
Button 组件对外暴露以下变量,可在应用任意位置的 JavaScript 表达式中通过components对象动态访问:
| 变量 | 描述 | 访问方式 |
|---|---|---|
buttonText | 存储按钮当前显示的文本 | {{components.button1.buttonText}} |
isValid | 指示输入是否满足校验条件 | {{components.button1.isValid}} |
isLoading | 指示组件是否处于加载状态 | {{components.button1.isLoading}} |
isVisible | 指示组件是否可见 | {{components.button1.isVisible}} |
暴露变量的默认值与声明位于 button.js:
exposedVariables: { buttonText: 'Button', isVisible: true, isDisabled: false, isLoading: false, },从源码看,这些变量通过多处useEffect与状态同步(见 Button.jsx):
isLoading随loading状态变化;isVisible随visibility状态变化;isDisabled随disable状态变化。
典型用法:在另一个组件的属性中使用{{components.button1.buttonText}}引用按钮当前文本,或用{{components.button1.isLoading}}作为条件判断来显示/隐藏其他元素。
Additional Actions(附加动作)
附加动作提供了不通过 JS 编码即可控制组件状态的开关型属性。在 button.js 中,这些属性被归入additionalActions分组:
| 动作 | 描述 | 配置方式 |
|---|---|---|
| Loading state | 启用加载状态,在按钮内容处显示旋转加载图标(spinner),常与查询的isLoading属性配合使用,在查询执行期间展示加载状态 | 切换开关On/Off,或点击fx以编程方式设置为{{true}}/{{false}} |
| Visibility | 控制组件可见性 | 切换开关,或点击fx输入逻辑表达式动态设置 |
| Disable | 启用/禁用组件 | 切换开关,或点击fx输入逻辑表达式动态设置 |
| Tooltip | 鼠标悬停时显示附加提示信息 | 输入字符串,例如Button to Submit Form |
其中Tooltip的配置较为灵活,支持三种渲染格式:
Plain text(纯文本)MarkdownHTML
这一配置在 button.js 中通过tooltipFormat开关(默认plainText)与tooltip文本字段(类型为code)组合实现,意味着 Tooltip 内容同样可以动态绑定数据。
从源码实现(Button.jsx)可以确认这些属性在渲染层的实际作用:
- Loading state:加载中渲染
<Loader color={computedLoaderColor} width="16" />替代按钮文本与图标; - Visibility:为
false时设置display: 'none',组件不渲染; - Disable:为
true时按钮透明度降为 50%(opacity: disable && '50%'),且点击事件被canClick守卫拦截,同时设置aria-disabled无障碍属性。
Devices(设备可见性)
| 属性 | 描述 | 期望值 |
|---|---|---|
| Show on desktop | 在桌面视图中显示组件 | 通过开关设置,或点击fx输入逻辑表达式动态配置 |
| Show on mobile | 在移动视图中显示组件 | 通过开关设置,或点击fx输入逻辑表达式动态配置 |
这两个属性在 button.js 中被定义为others分组:
others: { showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' }, showOnMobile: { type: 'toggle', displayName: 'Show on mobile' }, },默认值为桌面可见({{true}})、移动端不可见({{false}},见 button.js)。通过响应式布局能力,你可以为不同终端分别控制按钮的显示与隐藏,例如在移动端用更紧凑的导航方式替代按钮组。
Styles(样式)
Button(按钮样式)
Button 的样式体系在 button.js 中完整定义,覆盖「填充类型、颜色、字体、边框、图标、圆角与阴影」等多个维度:
| 属性 | 描述 | 配置方式 |
|---|---|---|
| Type | 设置按钮的填充方式 | 选择Solid(实心背景)或Outline(透明背景 + 描边) |
| Background | 设置组件背景色 | 选择颜色,或点击fx输入返回 Hex 颜色值的代码 |
| Text color | 设置按钮文字颜色 | 选择颜色,或点击fx输入返回 Hex 颜色值的代码 |
| Border color | 设置按钮边框颜色 | 选择颜色,或点击fx输入返回 Hex 颜色值的代码 |
| Loader color | 设置加载图标(spinner)颜色 | 选择颜色,或点击fx输入返回 Hex 颜色值的代码 |
| Icon | 为按钮选择图标 | 开启图标可见性、选择图标与图标颜色,也可通过fx编程设置 |
| Border radius | 修改按钮的圆角半径 | 输入数字,或点击fx输入返回数值的代码 |
| Box shadow | 设置按钮的盒阴影属性 | 选择阴影颜色并调整相关属性,或通过fx编程设置 |
类型(Type)与主题联动
Type在源码中对应primary(Solid)与outline(Outline)两个值。默认配置(见 button.js):
styles: { textSize: { value: '{{14}}' }, fontWeight: { value: 'normal' }, textColor: { value: 'var(--cc-surface1-surface)' }, borderColor: { value: 'var(--cc-primary-brand)' }, loaderColor: { value: 'var(--cc-surface1-surface)' }, contentAlignment: { value: 'center' }, borderRadius: { value: '{{6}}' }, backgroundColor: { value: 'var(--cc-primary-brand)' }, ... type: { value: 'primary' }, },值得关注的是,默认颜色值使用的是CSS 变量(如var(--cc-primary-brand)),而非硬编码的 Hex 值。这意味着按钮颜色会随 ToolJet 主题(Theme)联动——切换组织主题时按钮自动适配新配色。这与 Button.jsx 中的颜色计算逻辑相呼应:
const computedBgColor = '#4368E3' === backgroundColor ? type === 'primary' ? 'var(--cc-primary-brand)' : 'transparent' : type === 'primary' ? backgroundColor : 'transparent';源码逻辑表明:
- Solid(primary)类型:使用配置的背景色,未显式配置时回退到主题品牌色
var(--cc-primary-brand); - Outline 类型:无论背景色如何设置,背景始终为
transparent(透明),仅保留边框; - 边框、文字、加载器颜色同理,会针对 primary 类型映射到主题变量,确保两种类型在任意主题下均有良好的对比度。
悬停与点击状态
虽然原文档样式表未单列,但从源码(Button.jsx)可以确认按钮内置了悬停(hover)与点击(active)状态的处理:
- Hover background:
hoverBackgroundMode支持Auto(自动,基于背景色通过getModifiedColor计算加深/减淡)与Manual(手动指定颜色)两种模式,默认auto; - 悬停色通过 CSS 变量
--tblr-btn-color-darker传递,点击态色通过--tblr-btn-color-clicked传递; - 这些高级样式可在 Inspector 面板的对应手风琴分组中调整。
内容排版细节
- Font size:默认 14px(
textSize),源码中computedFontSize会做数值校验,非法值回退到 14; - Font weight:支持
normal、medium(映射为 500)、bold、lighter、bolder; - Content alignment:内容水平对齐方式,可选
left、center、right,默认居中; - Direction:控制图标与文本的排列方向(图标在左/在右,源码中
direction == 'left'时使用row-reverse实现); - Icon:图标可见性(
iconVisibility,默认关闭)、图标名与图标颜色均可配置,图标尺寸随字号自动缩放(computedIconSize = computedLineHeight * 0.8)。
Container(容器样式)
Padding(内边距)
允许通过启用Default选项来保持标准内边距。在 button.js 中:
padding: { type: 'switch', displayName: 'Padding', options: [ { displayName: 'Default', value: 'default' }, { displayName: 'None', value: 'none' }, ], accordian: 'container', },源码中(Button.jsx)padding会影响按钮的高度计算:
height: height == 36 ? (padding == 'default' ? '36px' : '40px') : padding == 'default' ? height : height + 4,即选择Default时保持组件设定高度,选择None时高度额外增加 4px,以适应无内边距场景下的可点击区域。同时按钮水平内边距固定为0px 12px。
源码实现深度解析:Button 的完整渲染流程
综合以上配置与源码,Button 组件在画布上的渲染流程可以归纳为:
- 属性解析:
props.properties提供text(标签)、loadingState、visibility、disabledState、collapseWhenHidden等运行时属性; - 样式计算:
props.styles中的颜色、字号、圆角、阴影等值经过主题变量映射与类型判断,生成最终的computedStyles内联样式对象; - 状态管理:
label、disable、visibility、loading四个 React state 分别对应暴露变量buttonText、isDisabled、isVisible、isLoading; - 交互绑定:点击触发
submitForm自定义事件 +fireEvent('onClick'),悬停触发fireEvent('onHover'); - CSA 注册:通过
setExposedVariables/setExposedVariable将click、setText、setVisibility、setDisable、setLoading暴露给 RunJS 与事件系统; - 无障碍支持:渲染的
<button>元素包含aria-label、aria-disabled、aria-busy、aria-hidden等无障碍属性,并支持data-cy测试标识(generateCypressDataCy),方便端到端测试定位元素。
测试验证:Button 的端到端覆盖
仓库为 Button 组件提供了完整的 Cypress 端到端测试: cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/components/buttonHappyPath.cy.js。
该测试套件(共 605 行)覆盖了 Button 组件的核心交互路径:
- 组件拖拽放置:将 Button 从组件库拖入画布,并验证位置;
- 属性与事件验证:通过
verifyAndModifyParameter、addDefaultEventHandler等公共工具验证 Label 属性修改、事件处理器绑定; - CSA 动作验证:通过
selectCSA、addSupportCSAData工具验证组件特定动作(如 setText、setLoading)的配置与执行; - 样式验证:通过
verifyWidgetColorCss、verifyLoaderColor、fillBoxShadowParams、verifyBoxShadowCss等工具逐项断言背景色、加载色、盒阴影等 CSS 输出是否符合预期; - Tooltip 验证:
addAndVerifyTooltip、verifyTooltip覆盖悬停提示的配置与展示; - 布局与设备可见性:
verifyLayout验证桌面/移动端显示开关。
测试的beforeEach流程(cy.apiLogin→cy.apiCreateApp→cy.openApp→cy.dragAndDropWidget(buttonText.defaultWidgetText, 500, 100))完整复现了「登录 → 创建应用 → 拖入 Button 组件」的真实用户路径,为开发者理解 Button 的构建器行为提供了可执行的参考范例。
快速上手:5 步创建一个带查询触发的按钮
综合上述全部能力,在 ToolJet 中创建一个可用的业务按钮只需以下步骤:
- 拖入组件:从左侧组件库将Button拖入画布;
- 设置标签:在 Inspector 的 Properties 面板中,将 Label 设为
提交订单,或使用{{queries.createOrder.data.status}}动态显示查询结果; - 绑定事件:在 Events 面板中为On click添加处理器,选择
Run Query并指定目标查询(如createOrder); - 优化反馈:开启Loading state并将其设置为
{{queries.createOrder.isLoading}},使查询执行期间按钮自动显示加载动画并屏蔽重复点击; - 定制外观:在 Styles 面板中选择
Solid类型、设置品牌色背景、圆角与图标,并配置合适的 Tooltip 提示文案。
通过以上配置,即可实现「点击 → 触发查询 → 加载反馈 → 结果联动」的完整交互闭环,这也是 Button 组件在 ToolJet 内部工具、仪表盘与业务流程应用中最典型的使用模式。
总结
Button 组件虽小,却是 ToolJet 应用交互的基石。本文基于官方文档 button.md 完整梳理了其属性、事件、组件特定动作、暴露变量、附加动作、设备可见性与样式体系,并通过源码 Button.jsx、配置定义 button.js 与测试用例 buttonHappyPath.cy.js 验证了各能力的底层实现逻辑。掌握了这些要点,你就可以在 ToolJet 中灵活构建出具备动态文本、加载反馈、编程控制与主题自适应能力的按钮交互组件。
【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 🚀项目地址: https://gitcode.com/GitHub_Trending/to/ToolJet
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考