Skip to content

Commit 66f2472

Browse files
committed
feat: adding slides configuration
1 parent 07eb1c8 commit 66f2472

5 files changed

Lines changed: 70 additions & 14 deletions

File tree

packages/pluggableWidgets/carousel-web/src/Carousel.tsx

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,41 @@
11
import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action";
22
import classNames from "classnames";
33
import { GUID, ObjectItem, ValueStatus } from "mendix";
4-
import { ReactNode, useCallback, useId } from "react";
5-
import { CarouselContainerProps } from "../typings/CarouselProps";
4+
import { ReactNode, useCallback, useId, useMemo } from "react";
5+
6+
import { type CarouselContainerProps } from "../typings/CarouselProps";
67
import { Carousel as CarouselComponent } from "./components/Carousel";
7-
import "./ui/Carousel.scss";
8+
89
import loadingCircleSvg from "./ui/loading-circle.svg";
910

11+
import "./ui/Carousel.scss";
12+
1013
export function Carousel(props: CarouselContainerProps): ReactNode {
11-
const { showPagination, loop, tabIndex, navigation, animation, delay, autoplay } = props;
14+
const {
15+
showPagination,
16+
loop,
17+
tabIndex,
18+
navigation,
19+
animation,
20+
delay,
21+
autoplay,
22+
slidesPerView,
23+
slidesPerGroup,
24+
dataSource,
25+
content
26+
} = props;
1227
const onClick = useCallback(() => executeAction(props.onClickAction), [props.onClickAction]);
1328
const id = useId();
29+
const carouselItems = useMemo(
30+
() =>
31+
dataSource?.items?.map((item: ObjectItem) => ({
32+
id: item.id as GUID,
33+
content: content?.get(item)
34+
})) ?? [],
35+
[dataSource]
36+
);
1437

15-
if (props.dataSource?.status !== ValueStatus.Available) {
38+
if (dataSource?.status !== ValueStatus.Available) {
1639
return (
1740
<div className={classNames(props.class, "widget-carousel")} tabIndex={tabIndex}>
1841
<img src={loadingCircleSvg} className="widget-carousel-loading-spinner" alt="" aria-hidden />
@@ -27,16 +50,13 @@ export function Carousel(props: CarouselContainerProps): ReactNode {
2750
tabIndex={tabIndex}
2851
pagination={showPagination}
2952
loop={loop}
53+
slidesPerView={slidesPerView}
54+
slidesPerGroup={slidesPerGroup}
3055
animation={animation}
3156
autoplay={autoplay}
3257
delay={delay}
3358
navigation={navigation}
34-
items={
35-
props.dataSource?.items?.map((item: ObjectItem) => ({
36-
id: item.id as GUID,
37-
content: props.content?.get(item)
38-
})) ?? []
39-
}
59+
items={carouselItems}
4060
onClick={onClick}
4161
/>
4262
);

packages/pluggableWidgets/carousel-web/src/Carousel.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@
3737
<caption>Infinite loop</caption>
3838
<description />
3939
</property>
40+
<property key="slidesPerView" type="integer" defaultValue="1">
41+
<caption>Slides per view</caption>
42+
<description />
43+
</property>
44+
<property key="slidesPerGroup" type="integer" defaultValue="1">
45+
<caption>Slides per group</caption>
46+
<description />
47+
</property>
4048
<property key="animation" type="boolean" defaultValue="true">
4149
<caption>Animation</caption>
4250
<description />

packages/pluggableWidgets/carousel-web/src/components/Carousel.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export interface CarouselProps {
1919
animation?: boolean;
2020
autoplay?: boolean;
2121
delay?: number;
22+
slidesPerView?: number;
23+
slidesPerGroup?: number;
2224
navigation: boolean;
2325
className: string;
2426
tabIndex?: number | undefined;
@@ -27,7 +29,21 @@ export interface CarouselProps {
2729
}
2830

2931
export function Carousel(props: CarouselProps): ReactElement {
30-
const { items, pagination, loop, animation, autoplay, delay, navigation, className, tabIndex, id, onClick } = props;
32+
const {
33+
items,
34+
pagination,
35+
loop,
36+
animation,
37+
autoplay,
38+
delay,
39+
slidesPerView,
40+
slidesPerGroup,
41+
navigation,
42+
className,
43+
tabIndex,
44+
id,
45+
onClick
46+
} = props;
3147
const [activeIndex, setActiveIndex] = useState<number>(0);
3248

3349
const getSlideId = useCallback(
@@ -47,7 +63,8 @@ export function Carousel(props: CarouselProps): ReactElement {
4763
};
4864

4965
const options: SwiperOptions = {
50-
slidesPerView: 1,
66+
slidesPerView,
67+
slidesPerGroup,
5168
centeredSlides: true,
5269
loop,
5370
navigation,
@@ -76,9 +93,9 @@ export function Carousel(props: CarouselProps): ReactElement {
7693
<Swiper
7794
onActiveIndexChange={updateSwiperIndex}
7895
wrapperTag={"ul"}
79-
{...options}
8096
onClick={onClick}
8197
onSwiper={updateSwiperIndex}
98+
{...options}
8299
>
83100
{items?.map((item, index) => (
84101
<SwiperSlide tag={"li"} aria-hidden={index !== activeIndex} key={item.id} id={getSlideId(item)}>

packages/pluggableWidgets/carousel-web/src/components/__tests__/Carousel.spec.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,16 @@ describe("Carousel", () => {
156156
jest.resetAllMocks();
157157
jest.spyOn(Math, "random").mockReturnValue(0.123456789);
158158
});
159+
159160
const defaultCarouselProps: CarouselProps = {
160161
id: "Carousel",
161162
className: "",
162163
items: [
163164
{ id: "1" as GUID, content: <div>test1</div> },
164165
{ id: "2" as GUID, content: <div>test2</div> }
165166
],
167+
slidesPerView: 1,
168+
slidesPerGroup: 1,
166169
pagination: true,
167170
animation: true,
168171
autoplay: true,
@@ -177,21 +180,25 @@ describe("Carousel", () => {
177180

178181
expect(asFragment()).toMatchSnapshot();
179182
});
183+
180184
it("renders correctly without pagination", () => {
181185
const { asFragment } = render(<Carousel {...defaultCarouselProps} pagination={false} />);
182186

183187
expect(asFragment()).toMatchSnapshot();
184188
});
189+
185190
it("renders correctly without navigation", () => {
186191
const { asFragment } = render(<Carousel {...defaultCarouselProps} navigation={false} />);
187192

188193
expect(asFragment()).toMatchSnapshot();
189194
});
195+
190196
it("renders correctly with minimal setup", () => {
191197
const { asFragment } = render(<Carousel {...defaultCarouselProps} pagination={false} navigation={false} />);
192198

193199
expect(asFragment()).toMatchSnapshot();
194200
});
201+
195202
afterEach(() => {
196203
jest.restoreAllMocks();
197204
});

packages/pluggableWidgets/carousel-web/typings/CarouselProps.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ export interface CarouselContainerProps {
1818
autoplay: boolean;
1919
delay: number;
2020
loop: boolean;
21+
slidesPerView: number;
22+
slidesPerGroup: number;
2123
animation: boolean;
2224
onClickAction?: ActionValue;
2325
}
@@ -40,6 +42,8 @@ export interface CarouselPreviewProps {
4042
autoplay: boolean;
4143
delay: number | null;
4244
loop: boolean;
45+
slidesPerView: number | null;
46+
slidesPerGroup: number | null;
4347
animation: boolean;
4448
onClickAction: {} | null;
4549
}

0 commit comments

Comments
 (0)