mirror of
https://github.com/mattermost/mattermost-mobile.git
synced 2026-11-12 06:00:14 +00:00
Mobile had no support for the `collapsible` dialog element: a dialog using one rendered none of its fields, because the container itself carries no value and every lookup walked only the top level. Conversion gains `flattenCollapsible` / `flattenDialogElements` / `flattenAppFields`, named to match the webapp's helpers. Every path that resolves a value by field name — submission, `collectFileIds`, `mergeFileFieldValues`, refresh-source detection, dynamic-select lookup in the dialog router, and `initValues` — now walks the flattened list, so a nested field behaves exactly like a top-level one. `CollapsibleSection` renders the chevron and title. Children unmount while collapsed; their values live in the form's reducer, so nothing typed is lost by closing a section, and a value typed into a section that is then collapsed still submits. Tested with 199 unit tests. The most complete of them, dialog_router_collapsible.test.tsx, drives the real payload from the demo plugin's `/dialog collapsible` sample through the real adapter, conversion, form and input components, mocking only the network, and asserts what the server receives on submit. E2E coverage is added to the existing demo-plugin suite, against the `/dialog collapsible` sample already present in the pinned demo plugin release: each section's initial expanded state, opening a nested section, a value surviving its section being collapsed, and a nested value surviving a collapse round trip. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
154 lines
3.6 KiB
TypeScript
154 lines
3.6 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
type Command = {
|
|
'id': string;
|
|
'token': string;
|
|
'create_at': number;
|
|
'update_at': number;
|
|
'delete_at': number;
|
|
'creator_id': string;
|
|
'team_id': string;
|
|
'trigger': string;
|
|
'method': 'P' | 'G' | '';
|
|
'username': string;
|
|
'icon_url': string;
|
|
'auto_complete': boolean;
|
|
'auto_complete_desc': string;
|
|
'auto_complete_hint': string;
|
|
'display_name': string;
|
|
'description': string;
|
|
'url': string;
|
|
'autocomplete_icon_data'?: string;
|
|
};
|
|
|
|
type CommandArgs = {
|
|
channel_id: string;
|
|
team_id: string;
|
|
root_id?: string;
|
|
parent_id?: string;
|
|
};
|
|
|
|
// AutocompleteSuggestion represents a single suggestion downloaded from the server.
|
|
type AutocompleteSuggestion = {
|
|
Complete: string;
|
|
Suggestion: string;
|
|
Hint: string;
|
|
Description: string;
|
|
IconData: string;
|
|
};
|
|
|
|
type DialogSubmission = {
|
|
url: string;
|
|
callback_id: string;
|
|
state: string;
|
|
user_id: string;
|
|
channel_id: string;
|
|
team_id: string;
|
|
submission: {
|
|
[x: string]: string;
|
|
};
|
|
cancelled: boolean;
|
|
file_ids?: string[];
|
|
};
|
|
|
|
type DialogOption = {
|
|
text: string;
|
|
value: string;
|
|
};
|
|
|
|
type SelectedDialogOption = DialogOption | DialogOption[] | undefined;
|
|
|
|
type SelectedDialogValue = string | string[] | undefined;
|
|
|
|
type DialogElement = {
|
|
display_name: string;
|
|
name: string;
|
|
type: InteractiveDialogElementType;
|
|
subtype?: InteractiveDialogTextSubtype;
|
|
default: string | boolean;
|
|
placeholder: string;
|
|
help_text: string;
|
|
optional: boolean;
|
|
min_length: number;
|
|
max_length: number;
|
|
data_source: string;
|
|
data_source_url?: string;
|
|
options: DialogOption[];
|
|
multiselect?: boolean;
|
|
allow_multiple?: boolean;
|
|
refresh?: boolean;
|
|
|
|
// Date/DateTime fields
|
|
min_date?: string;
|
|
max_date?: string;
|
|
time_interval?: number;
|
|
datetime_config?: DateTimeConfig;
|
|
|
|
// Collapsible sections hold child elements and carry no value of their own.
|
|
collapsible_config?: DialogCollapsibleConfig;
|
|
};
|
|
|
|
type DialogCollapsibleConfig = {
|
|
elements: DialogElement[];
|
|
collapsed?: boolean;
|
|
borderless?: boolean;
|
|
};
|
|
|
|
type InteractiveDialogConfig = {
|
|
app_id: string;
|
|
trigger_id: string;
|
|
url: string;
|
|
dialog: {
|
|
callback_id: string;
|
|
title: string;
|
|
introduction_text: string;
|
|
icon_url?: string;
|
|
elements: DialogElement[];
|
|
submit_label: string;
|
|
notify_on_cancel: boolean;
|
|
state: string;
|
|
source_url?: string;
|
|
};
|
|
};
|
|
|
|
type PostAction = {
|
|
id?: string;
|
|
type?: string;
|
|
name?: string;
|
|
disabled?: boolean;
|
|
style?: string;
|
|
data_source?: string;
|
|
options?: PostActionOption[];
|
|
default_option?: string;
|
|
integration?: PostActionIntegration;
|
|
cookie?: string;
|
|
};
|
|
|
|
type PostActionOption = {
|
|
text?: string;
|
|
value?: string;
|
|
};
|
|
|
|
type PostActionIntegration = {
|
|
url?: string;
|
|
context?: Record<string, any>;
|
|
}
|
|
|
|
/** `integration_format` on the do-post-action API body — identifies which format originally had the action. */
|
|
type PostActionIntegrationFormat =
|
|
| 'attachment'
|
|
| 'apps_binding'
|
|
| 'block'
|
|
| 'card'
|
|
| 'mm_block';
|
|
|
|
type PostActionResponse = {
|
|
status: string;
|
|
trigger_id: string;
|
|
goto_location?: string;
|
|
};
|
|
|
|
type InteractiveDialogElementType = 'text' | 'textarea' | 'select' | 'radio' | 'bool' | 'file' | 'date' | 'datetime' | 'collapsible'
|
|
type InteractiveDialogTextSubtype = 'email' | 'number' | 'tel' | 'url' | 'password'
|