@applicaster/zapp-react-native-ui-components 16.0.0-rc.90 → 16.0.0-rc.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -2
- package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +58 -51
- package/Components/ModalComponent/BottomSheetModalContent.tsx +4 -17
- package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +20 -5
- package/package.json +5 -5
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { CollapsibleTextContainer } from "../index";
|
|
2
|
+
|
|
3
|
+
const getValue = (key: string) => {
|
|
4
|
+
const values: Record<string, unknown> = {
|
|
5
|
+
text_alignment: "left",
|
|
6
|
+
margin_top: 0,
|
|
7
|
+
margin_right: 0,
|
|
8
|
+
margin_bottom: 0,
|
|
9
|
+
margin_left: 0,
|
|
10
|
+
padding_top: 0,
|
|
11
|
+
padding_right: 0,
|
|
12
|
+
padding_bottom: 0,
|
|
13
|
+
padding_left: 0,
|
|
14
|
+
corner_radius: 0,
|
|
15
|
+
data_key: "title",
|
|
16
|
+
custom_data_key: undefined,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
return values[key];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
describe("CollapsibleTextContainer view tree", () => {
|
|
23
|
+
it("stretches to the parent width so labels wrap using the cell width", () => {
|
|
24
|
+
const result = CollapsibleTextContainer({
|
|
25
|
+
getValue,
|
|
26
|
+
elements: [],
|
|
27
|
+
additionalStyles: { backgroundColor: "transparent" },
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
expect(result.style.alignSelf).toBe("stretch");
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -3,7 +3,6 @@ import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/he
|
|
|
3
3
|
import {
|
|
4
4
|
getColorFromData,
|
|
5
5
|
getKeyForLabel,
|
|
6
|
-
mapSelfAlignment,
|
|
7
6
|
} from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
8
7
|
import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
9
8
|
|
|
@@ -23,7 +22,7 @@ export const CollapsibleTextContainer = ({
|
|
|
23
22
|
return {
|
|
24
23
|
type: "CollapsibleTextContainer", // Collapsible Text Container,
|
|
25
24
|
style: {
|
|
26
|
-
alignSelf:
|
|
25
|
+
alignSelf: "stretch",
|
|
27
26
|
marginTop: toNumberWithDefaultZero(getValue("margin_top")),
|
|
28
27
|
marginRight: toNumberWithDefaultZero(getValue("margin_right")),
|
|
29
28
|
marginBottom: toNumberWithDefaultZero(getValue("margin_bottom")),
|
|
@@ -17,6 +17,9 @@ import {
|
|
|
17
17
|
Text,
|
|
18
18
|
View,
|
|
19
19
|
} from "react-native";
|
|
20
|
+
import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
|
|
21
|
+
|
|
22
|
+
const logger = createLogger({ category: "ModalComponent:AudioPlayerButton" });
|
|
20
23
|
|
|
21
24
|
const styles = StyleSheet.create({
|
|
22
25
|
container: {
|
|
@@ -130,6 +133,28 @@ export const DEFAULT_ICONS_CONFIGURATION: IconsConfiguration = {
|
|
|
130
133
|
},
|
|
131
134
|
};
|
|
132
135
|
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// Variant configurations
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
export const VARIANT_TEXT_ONLY: ButtonConfiguration = {
|
|
141
|
+
background: DEFAULT_BACKGROUND_CONFIGURATION,
|
|
142
|
+
title: DEFAULT_TITLE_CONFIGURATION,
|
|
143
|
+
icons: DEFAULT_ICONS_CONFIGURATION,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export const VARIANT_WITH_ICON: ButtonConfiguration = {
|
|
147
|
+
background: DEFAULT_BACKGROUND_CONFIGURATION,
|
|
148
|
+
title: DEFAULT_TITLE_CONFIGURATION,
|
|
149
|
+
icons: {
|
|
150
|
+
...DEFAULT_ICONS_CONFIGURATION,
|
|
151
|
+
leadingIcon: {
|
|
152
|
+
enabled: true,
|
|
153
|
+
actionType: "addToPlaylist",
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
|
|
133
158
|
// ---------------------------------------------------------------------------
|
|
134
159
|
// Data (runtime / localization — not styling config)
|
|
135
160
|
// ---------------------------------------------------------------------------
|
|
@@ -140,30 +165,15 @@ export type ButtonData = {
|
|
|
140
165
|
};
|
|
141
166
|
|
|
142
167
|
export type AudioPlayerButtonProps = {
|
|
143
|
-
configuration
|
|
144
|
-
data
|
|
168
|
+
configuration?: ButtonConfiguration;
|
|
169
|
+
data?: ButtonData;
|
|
145
170
|
disabled?: boolean;
|
|
146
171
|
focused?: boolean;
|
|
147
172
|
onPress?: () => void;
|
|
148
173
|
};
|
|
149
174
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
// ---------------------------------------------------------------------------
|
|
153
|
-
|
|
154
|
-
// Hardcoded: action type → icon asset mapping (base64 PNG)
|
|
155
|
-
const ACTION_ICON_ASSETS: Record<ButtonActionType, string> = {
|
|
156
|
-
addToQueue:
|
|
157
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAKUlEQVR42mNgGAVDCfxHw6MWjFpAvEGk4lELKLdgNJmOWkA7C0YBbQAAwiZvkR9eFgQAAAAASUVORK5CYII=",
|
|
158
|
-
addToPlaylist:
|
|
159
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAJklEQVR42mNgGAUjFvzHgmluATF4cFswGsmjkTwayaORPAqGLwAA6Jpxj9Ms9QMAAAAASUVORK5CYII=",
|
|
160
|
-
goToPlaylist:
|
|
161
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAALklEQVR42mNgGAWjYNiD/7Q2/D8tLflPiSX/ycQ0t+D/yLGAEkeMWjAKRgG9AQB4zlSshwSzWwAAAABJRU5ErkJggg==",
|
|
162
|
-
confirm:
|
|
163
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAJElEQVR42mNgGAWjYBSQCv4TwKMWDHELho/hNE/3o2AUDDcAAPv0QMAyy1CtAAAAAElFTkSuQmCC",
|
|
164
|
-
cancel:
|
|
165
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAASElEQVR42mNgGAWjGvynkVoUTf+pqA6nxv8UqiHbEooNx2cQ1QzHZiDVDcdlCcOQsoCmQUTTSKZpMqV5RqNLUUHTwm4UDBMAALBLQ73LTvoyAAAAAElFTkSuQmCC",
|
|
166
|
-
};
|
|
175
|
+
const PLACEHOLDER_RED_BOX =
|
|
176
|
+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAJ0lEQVR4nO3NMQEAAAjDsIF/z2ACvlRAU5NMHuvPOQAAAAAAAMBdC/07Ai5sNw+CAAAAAElFTkSuQmCC";
|
|
167
177
|
|
|
168
178
|
function getTextTransformStyle(
|
|
169
179
|
textTransform: TextTransform
|
|
@@ -276,13 +286,19 @@ type ButtonLeadingIconProps = {
|
|
|
276
286
|
configuration: IconsConfiguration;
|
|
277
287
|
};
|
|
278
288
|
|
|
279
|
-
function ButtonLeadingIcon({
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
289
|
+
function ButtonLeadingIcon({ configuration }: ButtonLeadingIconProps) {
|
|
290
|
+
const asset = configuration.leadingIcon.asset || PLACEHOLDER_RED_BOX;
|
|
291
|
+
|
|
292
|
+
React.useEffect(() => {
|
|
293
|
+
if (!configuration.leadingIcon.asset) {
|
|
294
|
+
logger.log_warning(
|
|
295
|
+
"AudioPlayerButton has leadingIcon enabled but no asset was provided. Falling back to placeholder red box. Ensure the relevant plugin (e.g. playlist-experience) provides leadingIcon.asset in styleOverrides.",
|
|
296
|
+
{
|
|
297
|
+
leadingIcon: configuration.leadingIcon,
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
}, [configuration.leadingIcon.asset]);
|
|
286
302
|
|
|
287
303
|
if (!asset) {
|
|
288
304
|
return null;
|
|
@@ -308,13 +324,14 @@ import { useModalBlocksStyle } from "../../ModalBlocksStyleContext";
|
|
|
308
324
|
import { mergeButtonConfiguration } from "../utils/mergeStyles";
|
|
309
325
|
|
|
310
326
|
export function AudioPlayerButton({
|
|
311
|
-
configuration,
|
|
312
|
-
data,
|
|
327
|
+
configuration = VARIANT_WITH_ICON,
|
|
328
|
+
data = { title: undefined },
|
|
313
329
|
disabled,
|
|
314
330
|
focused = false,
|
|
315
331
|
onPress,
|
|
316
332
|
}: AudioPlayerButtonProps) {
|
|
317
333
|
const modalBlocksStyle = useModalBlocksStyle();
|
|
334
|
+
|
|
318
335
|
const buttonBlock = modalBlocksStyle?.button;
|
|
319
336
|
const actionBlock = modalBlocksStyle?.action;
|
|
320
337
|
|
|
@@ -328,9 +345,21 @@ export function AudioPlayerButton({
|
|
|
328
345
|
icons: iconsConfiguration,
|
|
329
346
|
} = effectiveConfig;
|
|
330
347
|
|
|
331
|
-
const
|
|
348
|
+
const title = data?.title || buttonBlock?.title.default;
|
|
332
349
|
const isDisabled = disabled ?? data.disabled ?? false;
|
|
333
350
|
|
|
351
|
+
React.useEffect(() => {
|
|
352
|
+
if (!title) {
|
|
353
|
+
logger.log_warning(
|
|
354
|
+
"Create New Playlist button rendering with empty title. Ensure data.title is set for AudioPlayerButton or styleOverrides providing button title.",
|
|
355
|
+
{
|
|
356
|
+
leadingIcon: iconsConfiguration.leadingIcon,
|
|
357
|
+
modalBlocksStyle: modalBlocksStyle,
|
|
358
|
+
}
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
}, [title]);
|
|
362
|
+
|
|
334
363
|
const { leadingIcon } = iconsConfiguration;
|
|
335
364
|
const showIcon = shouldShowLeadingIcon(leadingIcon);
|
|
336
365
|
|
|
@@ -361,25 +390,3 @@ export function AudioPlayerButton({
|
|
|
361
390
|
</ButtonContainer>
|
|
362
391
|
);
|
|
363
392
|
}
|
|
364
|
-
|
|
365
|
-
// ---------------------------------------------------------------------------
|
|
366
|
-
// Variant configurations
|
|
367
|
-
// ---------------------------------------------------------------------------
|
|
368
|
-
|
|
369
|
-
export const VARIANT_TEXT_ONLY: ButtonConfiguration = {
|
|
370
|
-
background: DEFAULT_BACKGROUND_CONFIGURATION,
|
|
371
|
-
title: DEFAULT_TITLE_CONFIGURATION,
|
|
372
|
-
icons: DEFAULT_ICONS_CONFIGURATION,
|
|
373
|
-
};
|
|
374
|
-
|
|
375
|
-
export const VARIANT_WITH_ICON: ButtonConfiguration = {
|
|
376
|
-
background: DEFAULT_BACKGROUND_CONFIGURATION,
|
|
377
|
-
title: DEFAULT_TITLE_CONFIGURATION,
|
|
378
|
-
icons: {
|
|
379
|
-
...DEFAULT_ICONS_CONFIGURATION,
|
|
380
|
-
leadingIcon: {
|
|
381
|
-
enabled: true,
|
|
382
|
-
actionType: "addToPlaylist",
|
|
383
|
-
},
|
|
384
|
-
},
|
|
385
|
-
};
|
|
@@ -33,7 +33,6 @@ import { SortableList } from "./SortableList";
|
|
|
33
33
|
import {
|
|
34
34
|
AudioPlayerButton,
|
|
35
35
|
NowPlayingHeaderSection,
|
|
36
|
-
VARIANT_WITH_ICON,
|
|
37
36
|
} from "./AudioPlayer/Components";
|
|
38
37
|
import { BottomSheetDragArea } from "./BottomSheetDragArea";
|
|
39
38
|
import { ModalBlocksStyleContext } from "./ModalBlocksStyleContext";
|
|
@@ -176,9 +175,6 @@ function _BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
176
175
|
? pluginLocalizations?.[`${normalizedPluginId}_header_title`]
|
|
177
176
|
: undefined;
|
|
178
177
|
|
|
179
|
-
const createPlaylistTitle =
|
|
180
|
-
pluginLocalizations?.create_new_playlist_title || "Create New Playlist";
|
|
181
|
-
|
|
182
178
|
const nextUpTitle =
|
|
183
179
|
pluginLocalizations?.next_up_title ||
|
|
184
180
|
(normalizedPluginId
|
|
@@ -324,9 +320,9 @@ function _BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
324
320
|
const handleCreatePlaylist = useCallback(() => {
|
|
325
321
|
editableCollection?.add?.({
|
|
326
322
|
id: "create_new_playlist",
|
|
327
|
-
title:
|
|
323
|
+
title: "unknown title",
|
|
328
324
|
});
|
|
329
|
-
}, [editableCollection
|
|
325
|
+
}, [editableCollection]);
|
|
330
326
|
|
|
331
327
|
const headers = useMemo(() => {
|
|
332
328
|
const list: Array<{ index: number; component: () => React.ReactElement }> =
|
|
@@ -352,11 +348,7 @@ function _BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
352
348
|
<View
|
|
353
349
|
style={{ paddingHorizontal: 20, paddingTop: 8, paddingBottom: 12 }}
|
|
354
350
|
>
|
|
355
|
-
<AudioPlayerButton
|
|
356
|
-
configuration={VARIANT_WITH_ICON}
|
|
357
|
-
data={{ title: createPlaylistTitle }}
|
|
358
|
-
onPress={handleCreatePlaylist}
|
|
359
|
-
/>
|
|
351
|
+
<AudioPlayerButton onPress={handleCreatePlaylist} />
|
|
360
352
|
</View>
|
|
361
353
|
),
|
|
362
354
|
});
|
|
@@ -371,7 +363,6 @@ function _BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
371
363
|
nowPlayingTitle,
|
|
372
364
|
hasAdd,
|
|
373
365
|
handleCreatePlaylist,
|
|
374
|
-
createPlaylistTitle,
|
|
375
366
|
]);
|
|
376
367
|
|
|
377
368
|
const _items = useMemo(() => {
|
|
@@ -483,11 +474,7 @@ function _BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
483
474
|
) : null}
|
|
484
475
|
{hasAdd ? (
|
|
485
476
|
<View style={styles.someStyles}>
|
|
486
|
-
<AudioPlayerButton
|
|
487
|
-
configuration={VARIANT_WITH_ICON}
|
|
488
|
-
data={{ title: createPlaylistTitle }}
|
|
489
|
-
onPress={handleCreatePlaylist}
|
|
490
|
-
/>
|
|
477
|
+
<AudioPlayerButton onPress={handleCreatePlaylist} />
|
|
491
478
|
</View>
|
|
492
479
|
) : null}
|
|
493
480
|
{_items}
|
|
@@ -82,18 +82,21 @@ jest.mock("../AudioPlayer/Components/Item", () => ({
|
|
|
82
82
|
VARIANT_PLAYLIST_ITEM: {},
|
|
83
83
|
}));
|
|
84
84
|
|
|
85
|
+
const mockAudioPlayerButtonProps: Array<any> = [];
|
|
86
|
+
|
|
85
87
|
jest.mock("../AudioPlayer/Components", () => ({
|
|
86
88
|
NowPlayingHeaderSection: () => null,
|
|
87
|
-
AudioPlayerButton: (
|
|
89
|
+
AudioPlayerButton: (props: any) => {
|
|
88
90
|
const { Text } = require("react-native");
|
|
89
91
|
|
|
92
|
+
mockAudioPlayerButtonProps.push(props);
|
|
93
|
+
|
|
90
94
|
return (
|
|
91
|
-
<Text testID="create-playlist-button" onPress={onPress}>
|
|
92
|
-
{data
|
|
95
|
+
<Text testID="create-playlist-button" onPress={props.onPress}>
|
|
96
|
+
{props.data?.title}
|
|
93
97
|
</Text>
|
|
94
98
|
);
|
|
95
99
|
},
|
|
96
|
-
VARIANT_WITH_ICON: {},
|
|
97
100
|
}));
|
|
98
101
|
|
|
99
102
|
const baseProps = {
|
|
@@ -171,6 +174,8 @@ describe("BottomSheetModalContent role/behavior cells", () => {
|
|
|
171
174
|
});
|
|
172
175
|
|
|
173
176
|
it("renders create playlist button when operations include add and calls editableCollection.add on press", () => {
|
|
177
|
+
mockAudioPlayerButtonProps.length = 0;
|
|
178
|
+
|
|
174
179
|
const addMock = jest.fn();
|
|
175
180
|
const { BehaviorSubject } = require("rxjs");
|
|
176
181
|
|
|
@@ -192,12 +197,22 @@ describe("BottomSheetModalContent role/behavior cells", () => {
|
|
|
192
197
|
const createBtn = screen.getByTestId("create-playlist-button");
|
|
193
198
|
expect(createBtn).toBeTruthy();
|
|
194
199
|
|
|
200
|
+
// The button is rendered bare: styling comes from the modal blocks theme
|
|
201
|
+
// and the title from the button block, so no configuration/data is passed.
|
|
202
|
+
expect(mockAudioPlayerButtonProps.length).toBeGreaterThan(0);
|
|
203
|
+
|
|
204
|
+
for (const props of mockAudioPlayerButtonProps) {
|
|
205
|
+
expect(props.configuration).toBeUndefined();
|
|
206
|
+
expect(props.data).toBeUndefined();
|
|
207
|
+
expect(typeof props.onPress).toBe("function");
|
|
208
|
+
}
|
|
209
|
+
|
|
195
210
|
const { fireEvent } = require("@testing-library/react-native");
|
|
196
211
|
fireEvent.press(createBtn);
|
|
197
212
|
|
|
198
213
|
expect(addMock).toHaveBeenCalledWith({
|
|
199
214
|
id: "create_new_playlist",
|
|
200
|
-
title: "
|
|
215
|
+
title: "unknown title",
|
|
201
216
|
});
|
|
202
217
|
});
|
|
203
218
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "16.0.0-rc.
|
|
3
|
+
"version": "16.0.0-rc.91",
|
|
4
4
|
"description": "Applicaster Zapp React Native ui components for the Quick Brick App",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "16.0.0-rc.
|
|
32
|
-
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.
|
|
33
|
-
"@applicaster/zapp-react-native-redux": "16.0.0-rc.
|
|
34
|
-
"@applicaster/zapp-react-native-utils": "16.0.0-rc.
|
|
31
|
+
"@applicaster/applicaster-types": "16.0.0-rc.91",
|
|
32
|
+
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.91",
|
|
33
|
+
"@applicaster/zapp-react-native-redux": "16.0.0-rc.91",
|
|
34
|
+
"@applicaster/zapp-react-native-utils": "16.0.0-rc.91",
|
|
35
35
|
"fast-json-stable-stringify": "^2.1.0",
|
|
36
36
|
"promise": "^8.3.0",
|
|
37
37
|
"react-native-sortables": "1.7.1",
|