mattermost-mobile-upstream/app/constants/apps.ts
Jack Benning 00a40b4c72 Add collapsible section support to interactive dialogs
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>
2026-10-12 14:46:35 -04:00

69 lines
1.7 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export const AppBindingLocations = {
POST_MENU_ITEM: '/post_menu',
CHANNEL_HEADER_ICON: '/channel_header',
COMMAND: '/command',
IN_POST: '/in_post',
};
export const AppBindingPresentations = {
MODAL: 'modal',
};
export const AppCallResponseTypes: { [name: string]: AppCallResponseType } = {
OK: 'ok',
ERROR: 'error',
FORM: 'form',
CALL: 'call',
NAVIGATE: 'navigate',
};
export const AppExpandLevels: { [name: string]: AppExpandLevel } = {
EXPAND_DEFAULT: '',
EXPAND_NONE: 'none',
EXPAND_ALL: 'all',
EXPAND_SUMMARY: 'summary',
};
export const AppFieldTypes: { [name: string]: AppFieldType } = {
TEXT: 'text',
STATIC_SELECT: 'static_select',
DYNAMIC_SELECT: 'dynamic_select',
BOOL: 'bool',
USER: 'user',
CHANNEL: 'channel',
MARKDOWN: 'markdown',
RADIO: 'radio',
FILE: 'file',
DATE: 'date',
DATETIME: 'datetime',
COLLAPSIBLE: 'collapsible',
};
export const SelectableAppFieldTypes = [
AppFieldTypes.CHANNEL,
AppFieldTypes.USER,
AppFieldTypes.STATIC_SELECT,
AppFieldTypes.DYNAMIC_SELECT,
];
export const DEFAULT_TIME_INTERVAL_MINUTES = 60;
export const COMMAND_SUGGESTION_ERROR = 'error';
export const COMMAND_SUGGESTION_CHANNEL = 'channel';
export const COMMAND_SUGGESTION_USER = 'user';
export default {
AppBindingLocations,
AppBindingPresentations,
AppCallResponseTypes,
AppExpandLevels,
AppFieldTypes,
DEFAULT_TIME_INTERVAL_MINUTES,
COMMAND_SUGGESTION_ERROR,
COMMAND_SUGGESTION_CHANNEL,
COMMAND_SUGGESTION_USER,
};