slack_messaging/blocks/elements/
button.rs

1use crate::composition_objects::{ConfirmationDialog, Plain, Text};
2use crate::validators::*;
3
4use serde::Serialize;
5use slack_messaging_derive::Builder;
6
7/// [Button element](https://docs.slack.dev/reference/block-kit/block-elements/button-element)
8/// representation.
9///
10/// # Fields and Validations
11///
12/// For more details, see the [official
13/// documentation](https://docs.slack.dev/reference/block-kit/block-elements/button-element).
14///
15/// | Field | Type | Required | Validation |
16/// |-------|------|----------|------------|
17/// | text | [Text]<[Plain]> | Yes | Max length 75 characters |
18/// | action_id | String | No | Max length 255 characters |
19/// | url | String | No | Max length 3000 characters |
20/// | value | String | No | Max length 2000 characters |
21/// | style | &str | No | Must be either "primary" or "danger" |
22/// | confirm | [ConfirmationDialog] | No | N/A |
23/// | accessibility_label | String | No | Max length 75 characters |
24///
25/// # Example
26///
27/// ```
28/// use slack_messaging::plain_text;
29/// use slack_messaging::blocks::elements::Button;
30/// # use std::error::Error;
31///
32/// # fn try_main() -> Result<(), Box<dyn Error>> {
33/// let button = Button::builder()
34///     .text(plain_text!("Click Me")?)
35///     .value("click_me_123")
36///     .action_id("button-0")
37///     .build()?;
38///
39/// let expected = serde_json::json!({
40///     "type": "button",
41///     "text": {
42///         "type": "plain_text",
43///         "text": "Click Me"
44///     },
45///     "value": "click_me_123",
46///     "action_id": "button-0"
47/// });
48///
49/// let json = serde_json::to_value(button).unwrap();
50///
51/// assert_eq!(json, expected);
52///
53/// // If your object has any validation errors, the build method returns Result::Err
54/// let button = Button::builder()
55///     .value("click_me_123")
56///     .action_id("button-0")
57///     .build();
58///
59/// assert!(button.is_err());
60/// #     Ok(())
61/// # }
62/// # fn main() {
63/// #     try_main().unwrap()
64/// # }
65/// ```
66#[derive(Debug, Clone, Serialize, PartialEq, Builder)]
67#[serde(tag = "type", rename = "button")]
68pub struct Button {
69    #[builder(validate("required", "text_object::max_75"))]
70    pub(crate) text: Option<Text<Plain>>,
71
72    #[serde(skip_serializing_if = "Option::is_none")]
73    #[builder(validate("text::max_255"))]
74    pub(crate) action_id: Option<String>,
75
76    #[serde(skip_serializing_if = "Option::is_none")]
77    #[builder(validate("text::max_3000"))]
78    pub(crate) url: Option<String>,
79
80    #[serde(skip_serializing_if = "Option::is_none")]
81    #[builder(validate("text::max_2000"))]
82    pub(crate) value: Option<String>,
83
84    #[serde(skip_serializing_if = "Option::is_none")]
85    #[builder(private_setter)]
86    pub(crate) style: Option<&'static str>,
87
88    #[serde(skip_serializing_if = "Option::is_none")]
89    pub(crate) confirm: Option<ConfirmationDialog>,
90
91    #[serde(skip_serializing_if = "Option::is_none")]
92    #[builder(validate("text::max_75"))]
93    pub(crate) accessibility_label: Option<String>,
94}
95
96impl ButtonBuilder {
97    /// set "primary" to style field
98    pub fn primary(self) -> Self {
99        self.style("primary")
100    }
101
102    /// set "danger" to style field
103    pub fn danger(self) -> Self {
104        self.style("danger")
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111    use crate::composition_objects::test_helpers::*;
112    use crate::errors::*;
113
114    #[test]
115    fn it_implements_builder() {
116        let expected = Button {
117            text: Some(plain_text("Click Me")),
118            action_id: Some("button_0".into()),
119            url: Some("https://docs.slack.dev/block-kit".into()),
120            value: Some("click_me_123".into()),
121            style: Some("primary"),
122            confirm: Some(confirm()),
123            accessibility_label: Some("Click Me!".into()),
124        };
125
126        let val = Button::builder()
127            .set_text(Some(plain_text("Click Me")))
128            .set_action_id(Some("button_0"))
129            .set_url(Some("https://docs.slack.dev/block-kit"))
130            .set_value(Some("click_me_123"))
131            .primary()
132            .set_confirm(Some(confirm()))
133            .set_accessibility_label(Some("Click Me!"))
134            .build()
135            .unwrap();
136
137        assert_eq!(val, expected);
138
139        let expected = Button {
140            style: Some("danger"),
141            ..expected
142        };
143
144        let val = Button::builder()
145            .text(plain_text("Click Me"))
146            .action_id("button_0")
147            .url("https://docs.slack.dev/block-kit")
148            .value("click_me_123")
149            .danger()
150            .confirm(confirm())
151            .accessibility_label("Click Me!")
152            .build()
153            .unwrap();
154
155        assert_eq!(val, expected);
156    }
157
158    #[test]
159    fn it_requires_text_field() {
160        let err = Button::builder().build().unwrap_err();
161        assert_eq!(err.object(), "Button");
162
163        let errors = err.field("text");
164        assert!(errors.includes(ValidationErrorKind::Required));
165    }
166
167    #[test]
168    fn it_requires_text_less_than_75_characters_long() {
169        let err = Button::builder()
170            .text(plain_text("a".repeat(76)))
171            .build()
172            .unwrap_err();
173        assert_eq!(err.object(), "Button");
174
175        let errors = err.field("text");
176        assert!(errors.includes(ValidationErrorKind::MaxTextLength(75)));
177    }
178
179    #[test]
180    fn it_requires_action_id_less_than_255_characters_long() {
181        let err = Button::builder()
182            .text(plain_text("Click Me"))
183            .action_id("a".repeat(256))
184            .build()
185            .unwrap_err();
186        assert_eq!(err.object(), "Button");
187
188        let errors = err.field("action_id");
189        assert!(errors.includes(ValidationErrorKind::MaxTextLength(255)));
190    }
191
192    #[test]
193    fn it_requires_url_less_than_3000_characters_long() {
194        let err = Button::builder()
195            .text(plain_text("Click Me"))
196            .url("a".repeat(3001))
197            .build()
198            .unwrap_err();
199        assert_eq!(err.object(), "Button");
200
201        let errors = err.field("url");
202        assert!(errors.includes(ValidationErrorKind::MaxTextLength(3000)));
203    }
204
205    #[test]
206    fn it_requires_value_less_than_2000_characters_long() {
207        let err = Button::builder()
208            .text(plain_text("Click Me"))
209            .value("a".repeat(2001))
210            .build()
211            .unwrap_err();
212        assert_eq!(err.object(), "Button");
213
214        let errors = err.field("value");
215        assert!(errors.includes(ValidationErrorKind::MaxTextLength(2000)));
216    }
217
218    #[test]
219    fn it_requires_accessibility_label_less_than_75_characters_long() {
220        let err = Button::builder()
221            .text(plain_text("Click Me"))
222            .accessibility_label("a".repeat(76))
223            .build()
224            .unwrap_err();
225        assert_eq!(err.object(), "Button");
226
227        let errors = err.field("accessibility_label");
228        assert!(errors.includes(ValidationErrorKind::MaxTextLength(75)));
229    }
230}