diff --git a/server/command_hooks.go b/server/command_hooks.go index 6d0d125..4762e12 100644 --- a/server/command_hooks.go +++ b/server/command_hooks.go @@ -46,6 +46,7 @@ const ( "- `/dialog datetime-basic` - Open an Interactive Dialog with basic date/datetime features (min date, intervals, relative dates).\n" + "- `/dialog datetime-timezone` - Open an Interactive Dialog with timezone support and manual time entry.\n" + "- `/dialog multi-select` - Open an Interactive Dialog with multi-select fields. Once submitted, user-entered input is posted back into a channel.\n" + + "- `/dialog collapsible` - Open an Interactive Dialog with collapsible sections grouping child fields.\n" + "- `/dialog error` - Open an Interactive Dialog which always returns an general error.\n" + "- `/dialog error-no-elements` - Open an Interactive Dialog with no elements which always returns an general error.\n" + "- `/dialog field-refresh` - Open an Interactive Dialog with field refresh functionality.\n" + @@ -209,6 +210,9 @@ func getCommandDialogAutocompleteData() *model.AutocompleteData { multiSelect := model.NewAutocompleteData("multi-select", "", "Open an Interactive Dialog with multi-select fields.") command.AddCommand(multiSelect) + collapsible := model.NewAutocompleteData("collapsible", "", "Open an Interactive Dialog with collapsible sections.") + command.AddCommand(collapsible) + help := model.NewAutocompleteData("help", "", "") command.AddCommand(help) @@ -501,6 +505,12 @@ func (p *Plugin) executeCommandDialog(args *model.CommandArgs) *model.CommandRes URL: fmt.Sprintf("%s/plugins/%s/dialog/1", *serverConfig.ServiceSettings.SiteURL, manifest.Id), Dialog: getDialogWithMultiSelectElements(), } + case "collapsible": + dialogRequest = model.OpenDialogRequest{ + TriggerId: args.TriggerId, + URL: fmt.Sprintf("%s/plugins/%s/dialog/3", *serverConfig.ServiceSettings.SiteURL, manifest.Id), + Dialog: getDialogWithCollapsibleElements(), + } case "error": dialogRequest = model.OpenDialogRequest{ TriggerId: args.TriggerId, diff --git a/server/dialog_samples.go b/server/dialog_samples.go index 88b398c..748049c 100644 --- a/server/dialog_samples.go +++ b/server/dialog_samples.go @@ -204,6 +204,114 @@ func getDialogBasic() model.Dialog { } } +// getDialogWithCollapsibleElements shows collapsible sections nested 1, 2, and 3 +// levels deep. Collapsed=true means the section starts closed; Borderless=true omits the outline. +func getDialogWithCollapsibleElements() model.Dialog { + return model.Dialog{ + CallbackId: "collapsiblecallbackid", + Title: "Collapsible Sections Demo", + IconURL: "http://www.mattermost.org/wp-content/uploads/2016/04/icon.png", + SubmitLabel: "Submit", + NotifyOnCancel: true, + State: dialogStateSome, + IntroductionText: "**Collapsible Sections Demo**\n\n" + + "This dialog demonstrates collapsible sections nested 1, 2, and 3 " + + "levels deep. Toggle the carets to expand and collapse each section.", + Elements: []model.DialogElement{{ + // Level 1: one collapsible with plain fields. Starts expanded (default). + DisplayName: "Level 1 — Flat section", + Name: "level1_section", + Type: "collapsible", + Elements: []model.DialogElement{{ + DisplayName: "First Name", + Name: "first_name", + Type: "text", + Placeholder: "Enter your first name...", + }, { + DisplayName: "Last Name", + Name: "last_name", + Type: "text", + Placeholder: "Enter your last name...", + }}, + }, { + // Level 2: starts collapsed and borderless; inner section is expanded and borderless. + DisplayName: "Level 2 — One level of nesting (no border)", + Name: "level2_section", + Type: "collapsible", + Collapsed: true, + Borderless: true, + Elements: []model.DialogElement{{ + DisplayName: "Company", + Name: "company", + Type: "text", + Placeholder: "Enter your company...", + Optional: true, + }, { + DisplayName: "Contact Details", + Name: "level2_inner_section", + Type: "collapsible", + Borderless: true, + Elements: []model.DialogElement{{ + DisplayName: "Email", + Name: "email", + Type: "text", + SubType: "email", + Placeholder: "you@example.com", + }, { + DisplayName: "Phone", + Name: "phone", + Type: "text", + Placeholder: "Optional phone number...", + Optional: true, + }}, + }}, + }, { + // Level 3: outer two levels start collapsed; innermost starts expanded (default). + DisplayName: "Level 3 — Two levels of nesting", + Name: "level3_section", + Type: "collapsible", + Collapsed: true, + Elements: []model.DialogElement{{ + DisplayName: "Project Name", + Name: "project_name", + Type: "text", + Placeholder: "Enter a project name...", + Optional: true, + }, { + DisplayName: "Advanced", + Name: "level3_inner_section", + Type: "collapsible", + Collapsed: true, + Elements: []model.DialogElement{{ + DisplayName: "Environment", + Name: "environment", + Type: "text", + Placeholder: "e.g. staging, production...", + Optional: true, + }, { + DisplayName: "Experimental", + Name: "level3_innermost_section", + Type: "collapsible", + Elements: []model.DialogElement{{ + DisplayName: "Custom Settings", + Name: "custom_settings", + Type: "textarea", + Placeholder: "Optional advanced configuration...", + Optional: true, + MaxLength: 500, + }, { + DisplayName: "Enable Beta Features", + Name: "enable_beta", + Type: "bool", + Placeholder: "Turn on experimental features", + Optional: true, + }}, + }}, + }}, + }}, + } +} + func getDialogBoolean() model.Dialog { return model.Dialog{ CallbackId: "booleancallbackid", diff --git a/server/server b/server/server new file mode 100755 index 0000000..0c70139 Binary files /dev/null and b/server/server differ