Skip to content

Commit 02efb66

Browse files
committed
Fix add component button grayed out on mobile for container/gallery
1 parent d2c68fc commit 02efb66

File tree

5 files changed

+132
-24
lines changed

5 files changed

+132
-24
lines changed

services/backend-api/client/src/mocks/data/createPreview.ts

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { CreatePreviewResult, SendTestArticleDeliveryStatus } from "@/types";
22

3-
export const mockCreatePreviewResult: CreatePreviewResult = {
3+
// eslint-disable-next-line no-bitwise
4+
const DISCORD_COMPONENTS_V2_FLAG = 1 << 15;
5+
6+
const legacyPreviewResult: CreatePreviewResult = {
47
status: SendTestArticleDeliveryStatus.Success,
58
messages: [
69
{
@@ -59,3 +62,108 @@ export const mockCreatePreviewResult: CreatePreviewResult = {
5962
},
6063
],
6164
};
65+
66+
// V2 components have a different structure than the typed schema allows,
67+
// so we cast to CreatePreviewResult since this is mock data simulating the actual API response
68+
const v2PreviewResult = {
69+
status: SendTestArticleDeliveryStatus.Success,
70+
messages: [
71+
{
72+
flags: DISCORD_COMPONENTS_V2_FLAG,
73+
components: [
74+
{
75+
type: 17, // Container
76+
accent_color: 0x5865f2,
77+
spoiler: false,
78+
components: [
79+
{
80+
type: 10, // TextDisplay
81+
content: "**Welcome to the Media Gallery Preview**",
82+
},
83+
{
84+
type: 14, // Separator
85+
divider: true,
86+
spacing: 1,
87+
},
88+
{
89+
type: 9, // Section
90+
components: [
91+
{
92+
type: 10, // TextDisplay
93+
content: "This is a section with a thumbnail accessory",
94+
},
95+
],
96+
accessory: {
97+
type: 11, // Thumbnail
98+
media: {
99+
url: "https://placehold.co/80x80",
100+
},
101+
description: "Thumbnail image",
102+
spoiler: false,
103+
},
104+
},
105+
{
106+
type: 14, // Separator
107+
divider: true,
108+
spacing: 2,
109+
},
110+
{
111+
type: 12, // MediaGallery
112+
items: [
113+
{
114+
media: {
115+
url: "https://i.bo3.no/image/388845/Skjermbilde%202025-12-21%20180341.png?c=0&h=338&w=600",
116+
},
117+
description: "First gallery image",
118+
spoiler: false,
119+
},
120+
// {
121+
// media: {
122+
// url: "https://placehold.co/600x400/e74c3c/ffffff?text=Image+2",
123+
// },
124+
// description: "Second gallery image",
125+
// spoiler: false,
126+
// },
127+
// {
128+
// media: {
129+
// url: "https://placehold.co/600x400/2ecc71/ffffff?text=Image+3",
130+
// },
131+
// description: "Third gallery image",
132+
// spoiler: false,
133+
// },
134+
],
135+
},
136+
{
137+
type: 14, // Separator
138+
divider: true,
139+
spacing: 1,
140+
},
141+
{
142+
type: 1, // ActionRow
143+
components: [
144+
{
145+
type: 2, // Button
146+
style: 5, // Link
147+
label: "View Article",
148+
url: "https://www.example.com",
149+
},
150+
{
151+
type: 2, // Button
152+
style: 2, // Secondary
153+
label: "Share",
154+
url: "https://www.example.com/share",
155+
},
156+
],
157+
},
158+
],
159+
},
160+
],
161+
},
162+
],
163+
} as CreatePreviewResult;
164+
165+
export const getMockCreatePreviewResult = (useV2Components = false): CreatePreviewResult => {
166+
return useV2Components ? v2PreviewResult : legacyPreviewResult;
167+
};
168+
169+
export const mockCreatePreviewResult: CreatePreviewResult = legacyPreviewResult;

services/backend-api/client/src/mocks/handlers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ import { mockSendTestArticleResult } from "./data/testArticleResult";
7575
import { mockUserFeedArticles } from "./data/userFeedArticles";
7676
import { GetUserFeedRequestsOutput } from "../features/feed/api/getUserFeedRequests";
7777
import { mockUserFeedRequests } from "./data/userFeedRequests";
78-
import { mockCreatePreviewResult } from "./data/createPreview";
78+
import { getMockCreatePreviewResult } from "./data/createPreview";
7979
import mockDiscordThreads from "./data/discordThreads";
8080
import mockDiscordServerMembers from "./data/discordServerMembers";
8181
import mockDiscordUser from "./data/discordUser";
@@ -955,7 +955,7 @@ const handlers = [
955955
await delay(500);
956956

957957
return HttpResponse.json<CreateDiscordChannelConnectionPreviewOutput>({
958-
result: mockCreatePreviewResult,
958+
result: getMockCreatePreviewResult(true),
959959
});
960960
}),
961961

services/backend-api/client/src/pages/MessageBuilder/ComponentTreeItem.tsx

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { FaExclamationCircle } from "react-icons/fa";
44
import { ChevronDownIcon, ChevronRightIcon } from "@chakra-ui/icons";
55
import { AddComponentButton } from "./AddComponentButton";
66
import type { Component, SectionComponent } from "./types";
7-
import { ComponentType } from "./types";
7+
import { ComponentType, canComponentHaveChildren } from "./types";
88
import {
99
NavigableTreeItem,
1010
NavigableTreeItemExpandButton,
@@ -43,16 +43,7 @@ export const ComponentTreeItem: React.FC<ComponentTreeItemProps> = ({
4343
const hasAccessory =
4444
component.type === ComponentType.V2Section &&
4545
(component as SectionComponent).accessory !== undefined;
46-
const canHaveChildren =
47-
component.type === ComponentType.LegacyRoot ||
48-
component.type === ComponentType.LegacyEmbedContainer ||
49-
component.type === ComponentType.LegacyEmbed ||
50-
component.type === ComponentType.LegacyActionRow ||
51-
component.type === ComponentType.V2Root ||
52-
component.type === ComponentType.V2ActionRow ||
53-
component.type === ComponentType.V2Section ||
54-
component.type === ComponentType.V2Container ||
55-
component.type === ComponentType.V2MediaGallery;
46+
const canHaveChildren = canComponentHaveChildren(component.type);
5647
const { isFocused, isExpanded, isSelected } = useNavigableTreeItemContext();
5748

5849
const handleAddChild = (childType: ComponentType, asAccessory?: boolean) => {

services/backend-api/client/src/pages/MessageBuilder/ComponentTreeToolbar.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Box, HStack, VStack, Button, Text } from "@chakra-ui/react";
33
import { useFormContext } from "react-hook-form";
44
import { FaCog } from "react-icons/fa";
55
import { VscCollapseAll } from "react-icons/vsc";
6-
import { ComponentType, Component, SectionComponent } from "./types";
6+
import { ComponentType, Component, SectionComponent, canComponentHaveChildren } from "./types";
77
import { useMessageBuilderContext } from "./MessageBuilderContext";
88
import { useNavigableTreeContext } from "../../contexts/NavigableTreeContext";
99
import MessageBuilderFormState from "./types/MessageBuilderFormState";
@@ -55,15 +55,7 @@ export const ComponentTreeToolbar: React.FC = () => {
5555
? findComponentById(messageComponent || null, currentSelectedId)
5656
: null;
5757

58-
const canAddChildren =
59-
selectedComponent &&
60-
(selectedComponent.type === ComponentType.LegacyRoot ||
61-
selectedComponent.type === ComponentType.LegacyEmbedContainer ||
62-
selectedComponent.type === ComponentType.LegacyEmbed ||
63-
selectedComponent.type === ComponentType.LegacyActionRow ||
64-
selectedComponent.type === ComponentType.V2Root ||
65-
selectedComponent.type === ComponentType.V2ActionRow ||
66-
selectedComponent.type === ComponentType.V2Section);
58+
const canAddChildren = selectedComponent && canComponentHaveChildren(selectedComponent.type);
6759

6860
const handleAddChild = (childType: ComponentType, isAccessory?: boolean) => {
6961
if (!selectedComponent) return;
@@ -103,6 +95,7 @@ export const ComponentTreeToolbar: React.FC = () => {
10395
align="center"
10496
width="100%"
10597
display={{ base: "flex", [MESSAGE_BUILDER_MOBILE_BREAKPOINT]: "none" }}
98+
flexWrap="wrap"
10699
>
107100
<Text fontSize="md">
108101
Selected:{" "}

services/backend-api/client/src/pages/MessageBuilder/types.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,22 @@ export enum ComponentType {
2929

3030
export const ROOT_COMPONENT_TYPES = [ComponentType.LegacyRoot, ComponentType.V2Root];
3131

32+
export const PARENT_COMPONENT_TYPES = [
33+
ComponentType.LegacyRoot,
34+
ComponentType.LegacyEmbedContainer,
35+
ComponentType.LegacyEmbed,
36+
ComponentType.LegacyActionRow,
37+
ComponentType.V2Root,
38+
ComponentType.V2ActionRow,
39+
ComponentType.V2Section,
40+
ComponentType.V2Container,
41+
ComponentType.V2MediaGallery,
42+
] as const;
43+
44+
export function canComponentHaveChildren(type: ComponentType): boolean {
45+
return (PARENT_COMPONENT_TYPES as readonly ComponentType[]).includes(type);
46+
}
47+
3248
export interface MessageBuilderProblem {
3349
message: string;
3450
path: string;

0 commit comments

Comments
 (0)