diff --git a/packages/pluggableWidgets/carousel-web/src/Carousel.tsx b/packages/pluggableWidgets/carousel-web/src/Carousel.tsx
index 8bd335a2bd..93784da793 100644
--- a/packages/pluggableWidgets/carousel-web/src/Carousel.tsx
+++ b/packages/pluggableWidgets/carousel-web/src/Carousel.tsx
@@ -1,18 +1,41 @@
import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action";
import classNames from "classnames";
import { GUID, ObjectItem, ValueStatus } from "mendix";
-import { ReactNode, useCallback, useId } from "react";
-import { CarouselContainerProps } from "../typings/CarouselProps";
+import { ReactNode, useCallback, useId, useMemo } from "react";
+
+import { type CarouselContainerProps } from "../typings/CarouselProps";
import { Carousel as CarouselComponent } from "./components/Carousel";
-import "./ui/Carousel.scss";
+
import loadingCircleSvg from "./ui/loading-circle.svg";
+import "./ui/Carousel.scss";
+
export function Carousel(props: CarouselContainerProps): ReactNode {
- const { showPagination, loop, tabIndex, navigation, animation, delay, autoplay } = props;
+ const {
+ showPagination,
+ loop,
+ tabIndex,
+ navigation,
+ animation,
+ delay,
+ autoplay,
+ slidesPerView,
+ slidesPerGroup,
+ dataSource,
+ content
+ } = props;
const onClick = useCallback(() => executeAction(props.onClickAction), [props.onClickAction]);
const id = useId();
+ const carouselItems = useMemo(
+ () =>
+ dataSource?.items?.map((item: ObjectItem) => ({
+ id: item.id as GUID,
+ content: content?.get(item)
+ })) ?? [],
+ [dataSource]
+ );
- if (props.dataSource?.status !== ValueStatus.Available) {
+ if (dataSource?.status !== ValueStatus.Available) {
return (

@@ -27,16 +50,13 @@ export function Carousel(props: CarouselContainerProps): ReactNode {
tabIndex={tabIndex}
pagination={showPagination}
loop={loop}
+ slidesPerView={slidesPerView}
+ slidesPerGroup={slidesPerGroup}
animation={animation}
autoplay={autoplay}
delay={delay}
navigation={navigation}
- items={
- props.dataSource?.items?.map((item: ObjectItem) => ({
- id: item.id as GUID,
- content: props.content?.get(item)
- })) ?? []
- }
+ items={carouselItems}
onClick={onClick}
/>
);
diff --git a/packages/pluggableWidgets/carousel-web/src/Carousel.xml b/packages/pluggableWidgets/carousel-web/src/Carousel.xml
index 2db73936e0..a7b5afc6c5 100644
--- a/packages/pluggableWidgets/carousel-web/src/Carousel.xml
+++ b/packages/pluggableWidgets/carousel-web/src/Carousel.xml
@@ -37,6 +37,14 @@
Infinite loop
+
+ Slides per view
+
+
+
+ Slides per group
+
+
Animation
diff --git a/packages/pluggableWidgets/carousel-web/src/components/Carousel.tsx b/packages/pluggableWidgets/carousel-web/src/components/Carousel.tsx
index d9c3d10c40..2fede6df97 100644
--- a/packages/pluggableWidgets/carousel-web/src/components/Carousel.tsx
+++ b/packages/pluggableWidgets/carousel-web/src/components/Carousel.tsx
@@ -19,6 +19,8 @@ export interface CarouselProps {
animation?: boolean;
autoplay?: boolean;
delay?: number;
+ slidesPerView?: number;
+ slidesPerGroup?: number;
navigation: boolean;
className: string;
tabIndex?: number | undefined;
@@ -27,7 +29,21 @@ export interface CarouselProps {
}
export function Carousel(props: CarouselProps): ReactElement {
- const { items, pagination, loop, animation, autoplay, delay, navigation, className, tabIndex, id, onClick } = props;
+ const {
+ items,
+ pagination,
+ loop,
+ animation,
+ autoplay,
+ delay,
+ slidesPerView,
+ slidesPerGroup,
+ navigation,
+ className,
+ tabIndex,
+ id,
+ onClick
+ } = props;
const [activeIndex, setActiveIndex] = useState(0);
const getSlideId = useCallback(
@@ -47,7 +63,8 @@ export function Carousel(props: CarouselProps): ReactElement {
};
const options: SwiperOptions = {
- slidesPerView: 1,
+ slidesPerView,
+ slidesPerGroup,
centeredSlides: true,
loop,
navigation,
@@ -76,9 +93,9 @@ export function Carousel(props: CarouselProps): ReactElement {
{items?.map((item, index) => (
diff --git a/packages/pluggableWidgets/carousel-web/src/components/__tests__/Carousel.spec.tsx b/packages/pluggableWidgets/carousel-web/src/components/__tests__/Carousel.spec.tsx
index e831171997..7ecda07879 100644
--- a/packages/pluggableWidgets/carousel-web/src/components/__tests__/Carousel.spec.tsx
+++ b/packages/pluggableWidgets/carousel-web/src/components/__tests__/Carousel.spec.tsx
@@ -156,6 +156,7 @@ describe("Carousel", () => {
jest.resetAllMocks();
jest.spyOn(Math, "random").mockReturnValue(0.123456789);
});
+
const defaultCarouselProps: CarouselProps = {
id: "Carousel",
className: "",
@@ -163,6 +164,8 @@ describe("Carousel", () => {
{ id: "1" as GUID, content: test1
},
{ id: "2" as GUID, content: test2
}
],
+ slidesPerView: 1,
+ slidesPerGroup: 1,
pagination: true,
animation: true,
autoplay: true,
@@ -177,21 +180,25 @@ describe("Carousel", () => {
expect(asFragment()).toMatchSnapshot();
});
+
it("renders correctly without pagination", () => {
const { asFragment } = render();
expect(asFragment()).toMatchSnapshot();
});
+
it("renders correctly without navigation", () => {
const { asFragment } = render();
expect(asFragment()).toMatchSnapshot();
});
+
it("renders correctly with minimal setup", () => {
const { asFragment } = render();
expect(asFragment()).toMatchSnapshot();
});
+
afterEach(() => {
jest.restoreAllMocks();
});
diff --git a/packages/pluggableWidgets/carousel-web/typings/CarouselProps.d.ts b/packages/pluggableWidgets/carousel-web/typings/CarouselProps.d.ts
index 87e017a5c8..ac896d0379 100644
--- a/packages/pluggableWidgets/carousel-web/typings/CarouselProps.d.ts
+++ b/packages/pluggableWidgets/carousel-web/typings/CarouselProps.d.ts
@@ -18,6 +18,8 @@ export interface CarouselContainerProps {
autoplay: boolean;
delay: number;
loop: boolean;
+ slidesPerView: number;
+ slidesPerGroup: number;
animation: boolean;
onClickAction?: ActionValue;
}
@@ -40,6 +42,8 @@ export interface CarouselPreviewProps {
autoplay: boolean;
delay: number | null;
loop: boolean;
+ slidesPerView: number | null;
+ slidesPerGroup: number | null;
animation: boolean;
onClickAction: {} | null;
}