Skip to content

Commit 77e29e0

Browse files
6543jolheiserdelvh
authored
Extend issue template yaml engine (#29274)
Add new option: `visible`: witch can hide a specific field of the form or the created content afterwards It is a string array witch can contain `form` and `content`. If only `form` is present, it wont show up in the created issue afterwards and the other way around. By default it sets both except for markdown As they are optional and github don't have any similar thing, it is non breaking and also do not conflict with it. With this you can: - define "post issue creation" elements like a TODO list to track an issue state - make sure to have a checkbox that reminds the user to check for a thing but dont have it in the created issue afterwards - define markdown for the created issue (was the downside of using yaml instead of md in the past) - ... ## Demo ```yaml name: New Contribution description: External Contributor creating a pull body: - type: checkboxes id: extern-todo visible: [form] attributes: label: Contribution Guidelines options: - label: I checked there exist no similar feature to be extended required: true - label: I did read the CONTRIBUTION.MD required: true - type: checkboxes id: intern-todo visible: [content] attributes: label: Maintainer Check-List options: - label: Does this pull follow the KISS principe - label: Checked if internal bord was notifyed # .... ``` [Demo Video](https://cloud.obermui.de/s/tm34fSAbJp9qw9z/download/vid-20240220-152751.mkv) --- *Sponsored by Kithara Software GmbH* --------- Co-authored-by: John Olheiser <[email protected]> Co-authored-by: delvh <[email protected]>
1 parent 2fb917f commit 77e29e0

File tree

11 files changed

+247
-50
lines changed

11 files changed

+247
-50
lines changed

docs/content/usage/issue-pull-request-templates.en-us.md

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ body:
136136
attributes:
137137
value: |
138138
Thanks for taking the time to fill out this bug report!
139+
# some markdown that will only be visible once the issue has been created
140+
- type: markdown
141+
attributes:
142+
value: |
143+
This issue was created by an issue **template** :)
144+
visible: [content]
139145
- type: input
140146
id: contact
141147
attributes:
@@ -187,18 +193,25 @@ body:
187193
options:
188194
- label: I agree to follow this project's Code of Conduct
189195
required: true
196+
- label: I have also read the CONTRIBUTION.MD
197+
required: true
198+
visible: [form]
199+
- label: This is a TODO only visible after issue creation
200+
visible: [content]
190201
```
191202
192203
### Markdown
193204
194-
You can use a `markdown` element to display Markdown in your form that provides extra context to the user, but is not submitted.
205+
You can use a `markdown` element to display Markdown in your form that provides extra context to the user, but is not submitted by default.
195206

196207
Attributes:
197208

198209
| Key | Description | Required | Type | Default | Valid values |
199210
|-------|--------------------------------------------------------------|----------|--------|---------|--------------|
200211
| value | The text that is rendered. Markdown formatting is supported. | Required | String | - | - |
201212

213+
visible: Default is **[form]**
214+
202215
### Textarea
203216

204217
You can use a `textarea` element to add a multi-line text field to your form. Contributors can also attach files in `textarea` fields.
@@ -219,6 +232,8 @@ Validations:
219232
|----------|------------------------------------------------------|----------|---------|---------|--------------|
220233
| required | Prevents form submission until element is completed. | Optional | Boolean | false | - |
221234

235+
visible: Default is **[form, content]**
236+
222237
### Input
223238

224239
You can use an `input` element to add a single-line text field to your form.
@@ -240,6 +255,8 @@ Validations:
240255
| is_number | Prevents form submission until element is filled with a number. | Optional | Boolean | false | - |
241256
| regex | Prevents form submission until element is filled with a value that match the regular expression. | Optional | String | - | a [regular expression](https://en.wikipedia.org/wiki/Regular_expression) |
242257

258+
visible: Default is **[form, content]**
259+
243260
### Dropdown
244261

245262
You can use a `dropdown` element to add a dropdown menu in your form.
@@ -259,24 +276,29 @@ Validations:
259276
|----------|------------------------------------------------------|----------|---------|---------|--------------|
260277
| required | Prevents form submission until element is completed. | Optional | Boolean | false | - |
261278

279+
visible: Default is **[form, content]**
280+
262281
### Checkboxes
263282

264283
You can use the `checkboxes` element to add a set of checkboxes to your form.
265284

266285
Attributes:
267286

268287
| Key | Description | Required | Type | Default | Valid values |
269-
|-------------|-------------------------------------------------------------------------------------------------------|----------|--------|--------------|--------------|
288+
| ----------- | ----------------------------------------------------------------------------------------------------- | -------- | ------ | ------------ | ------------ |
270289
| label | A brief description of the expected user input, which is displayed in the form. | Required | String | - | - |
271290
| description | A description of the set of checkboxes, which is displayed in the form. Supports Markdown formatting. | Optional | String | Empty String | - |
272291
| options | An array of checkboxes that the user can select. For syntax, see below. | Required | Array | - | - |
273292

274293
For each value in the options array, you can set the following keys.
275294

276-
| Key | Description | Required | Type | Default | Options |
277-
|----------|------------------------------------------------------------------------------------------------------------------------------------------|----------|---------|---------|---------|
278-
| label | The identifier for the option, which is displayed in the form. Markdown is supported for bold or italic text formatting, and hyperlinks. | Required | String | - | - |
279-
| required | Prevents form submission until element is completed. | Optional | Boolean | false | - |
295+
| Key | Description | Required | Type | Default | Options |
296+
|--------------|------------------------------------------------------------------------------------------------------------------------------------------|----------|--------------|---------|---------|
297+
| label | The identifier for the option, which is displayed in the form. Markdown is supported for bold or italic text formatting, and hyperlinks. | Required | String | - | - |
298+
| required | Prevents form submission until element is completed. | Optional | Boolean | false | - |
299+
| visible | Whether a specific checkbox appears in the form only, in the created issue only, or both. Valid options are "form" and "content". | Optional | String array | false | - |
300+
301+
visible: Default is **[form, content]**
280302

281303
## Syntax for issue config
282304

@@ -292,15 +314,15 @@ contact_links:
292314

293315
### Possible Options
294316

295-
| Key | Description | Type | Default |
296-
|----------------------|-------------------------------------------------------------------------------------------------------|--------------------|----------------|
297-
| blank_issues_enabled | If set to false, the User is forced to use a Template | Boolean | true |
298-
| contact_links | Custom Links to show in the Choose Box | Contact Link Array | Empty Array |
317+
| Key | Description | Type | Default |
318+
|----------------------|-------------------------------------------------------|--------------------|-------------|
319+
| blank_issues_enabled | If set to false, the User is forced to use a Template | Boolean | true |
320+
| contact_links | Custom Links to show in the Choose Box | Contact Link Array | Empty Array |
299321

300322
### Contact Link
301323

302-
| Key | Description | Type | Required |
303-
|----------------------|-------------------------------------------------------------------------------------------------------|---------|----------|
304-
| name | the name of your link | String | true |
305-
| url | The URL of your Link | String | true |
306-
| about | A short description of your Link | String | true |
324+
| Key | Description | Type | Required |
325+
|-------|----------------------------------|--------|----------|
326+
| name | the name of your link | String | true |
327+
| url | The URL of your Link | String | true |
328+
| about | A short description of your Link | String | true |

modules/issue/template/template.go

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,13 @@ func validateRequired(field *api.IssueFormField, idx int) error {
122122
// The label is not required for a markdown or checkboxes field
123123
return nil
124124
}
125-
return validateBoolItem(newErrorPosition(idx, field.Type), field.Validations, "required")
125+
if err := validateBoolItem(newErrorPosition(idx, field.Type), field.Validations, "required"); err != nil {
126+
return err
127+
}
128+
if required, _ := field.Validations["required"].(bool); required && !field.VisibleOnForm() {
129+
return newErrorPosition(idx, field.Type).Errorf("can not require a hidden field")
130+
}
131+
return nil
126132
}
127133

128134
func validateID(field *api.IssueFormField, idx int, ids container.Set[string]) error {
@@ -172,10 +178,38 @@ func validateOptions(field *api.IssueFormField, idx int) error {
172178
return position.Errorf("'label' is required and should be a string")
173179
}
174180

181+
if visibility, ok := opt["visible"]; ok {
182+
visibilityList, ok := visibility.([]any)
183+
if !ok {
184+
return position.Errorf("'visible' should be list")
185+
}
186+
for _, visibleType := range visibilityList {
187+
visibleType, ok := visibleType.(string)
188+
if !ok || !(visibleType == "form" || visibleType == "content") {
189+
return position.Errorf("'visible' list can only contain strings of 'form' and 'content'")
190+
}
191+
}
192+
}
193+
175194
if required, ok := opt["required"]; ok {
176195
if _, ok := required.(bool); !ok {
177196
return position.Errorf("'required' should be a bool")
178197
}
198+
199+
// validate if hidden field is required
200+
if visibility, ok := opt["visible"]; ok {
201+
visibilityList, _ := visibility.([]any)
202+
isVisible := false
203+
for _, v := range visibilityList {
204+
if vv, _ := v.(string); vv == "form" {
205+
isVisible = true
206+
break
207+
}
208+
}
209+
if !isVisible {
210+
return position.Errorf("can not require a hidden checkbox")
211+
}
212+
}
179213
}
180214
}
181215
}
@@ -238,7 +272,7 @@ func RenderToMarkdown(template *api.IssueTemplate, values url.Values) string {
238272
IssueFormField: field,
239273
Values: values,
240274
}
241-
if f.ID == "" {
275+
if f.ID == "" || !f.VisibleInContent() {
242276
continue
243277
}
244278
f.WriteTo(builder)
@@ -253,11 +287,6 @@ type valuedField struct {
253287
}
254288

255289
func (f *valuedField) WriteTo(builder *strings.Builder) {
256-
if f.Type == api.IssueFormFieldTypeMarkdown {
257-
// markdown blocks do not appear in output
258-
return
259-
}
260-
261290
// write label
262291
if !f.HideLabel() {
263292
_, _ = fmt.Fprintf(builder, "### %s\n\n", f.Label())
@@ -269,6 +298,9 @@ func (f *valuedField) WriteTo(builder *strings.Builder) {
269298
switch f.Type {
270299
case api.IssueFormFieldTypeCheckboxes:
271300
for _, option := range f.Options() {
301+
if !option.VisibleInContent() {
302+
continue
303+
}
272304
checked := " "
273305
if option.IsChecked() {
274306
checked = "x"
@@ -302,6 +334,10 @@ func (f *valuedField) WriteTo(builder *strings.Builder) {
302334
} else {
303335
_, _ = fmt.Fprintf(builder, "%s\n", value)
304336
}
337+
case api.IssueFormFieldTypeMarkdown:
338+
if value, ok := f.Attributes["value"].(string); ok {
339+
_, _ = fmt.Fprintf(builder, "%s\n", value)
340+
}
305341
}
306342
_, _ = fmt.Fprintln(builder)
307343
}
@@ -314,6 +350,9 @@ func (f *valuedField) Label() string {
314350
}
315351

316352
func (f *valuedField) HideLabel() bool {
353+
if f.Type == api.IssueFormFieldTypeMarkdown {
354+
return true
355+
}
317356
if label, ok := f.Attributes["hide_label"].(bool); ok {
318357
return label
319358
}
@@ -385,6 +424,22 @@ func (o *valuedOption) IsChecked() bool {
385424
return false
386425
}
387426

427+
func (o *valuedOption) VisibleInContent() bool {
428+
if o.field.Type == api.IssueFormFieldTypeCheckboxes {
429+
if vs, ok := o.data.(map[string]any); ok {
430+
if vl, ok := vs["visible"].([]any); ok {
431+
for _, v := range vl {
432+
if vv, _ := v.(string); vv == "content" {
433+
return true
434+
}
435+
}
436+
return false
437+
}
438+
}
439+
}
440+
return true
441+
}
442+
388443
var minQuotesRegex = regexp.MustCompilePOSIX("^`{3,}")
389444

390445
// minQuotes return 3 or more back-quotes.

0 commit comments

Comments
 (0)