Skip to content

Commit

Permalink
Merge pull request #84 from pegasystems/image_carousel
Browse files Browse the repository at this point in the history
Image carousel
  • Loading branch information
ricmars authored Oct 23, 2024
2 parents 4f80d21 + 220fb15 commit 342fa6f
Show file tree
Hide file tree
Showing 9 changed files with 847 additions and 0 deletions.
20 changes: 20 additions & 0 deletions src/components/Pega_Extensions_ImageCarousel/Controls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { ButtonPrev, ButtonNext } from './styles';

type SliderControlsProps = {
prevSlide: () => void;
nextSlide: () => void;
};

const SliderControls: React.FC<SliderControlsProps> = ({ prevSlide, nextSlide }) => (
<>
<ButtonPrev onClick={prevSlide} aria-label="Previous Slide">
&#10094;
</ButtonPrev>
<ButtonNext onClick={nextSlide} aria-label="Next Slide">
&#10095;
</ButtonNext>
</>
);

export default SliderControls;
85 changes: 85 additions & 0 deletions src/components/Pega_Extensions_ImageCarousel/Docs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Meta, Primary, Controls, Story } from '@storybook/blocks';
import * as DemoStories from './demo.stories';

<Meta of={DemoStories} />

# Overview

The Image Carousel Component is a dynamic and flexible Carousel designed for Pega Constellation applications. It fetches images from a Data Page as a source, allowing developers to display a collection of images in a rotating slideshow format. This component supports configurable height, transition effects, and dynamic image handling. Its responsive design ensures that it adjusts automatically to different screen sizes, making it an ideal solution for creating engaging, dynamic visual content in your Pega applications.

<Primary />

## Props

<Controls />

# Deployment and Integration Instructions:-

**1. Create a Data Type for the Carousel**

To manage and display the images in the Carousel component, create a Data Type in Pega **(Example: Carousel)** with the following columns:

- **Id:** Unique identifier for the image.

- **Title:** Optional title or caption of the image.

- **Description:** Optional description or metadata for the image.

- **Image URL:** URL of the image to be displayed in the Carousel.

- **Visible:** Boolean field to indicate if the image should be shown in the Carousel.

Each entry in the Data Type corresponds to an image, allowing for easy updates and management of the content.

Users will create a custom list Data Page **(D_CarouselsList)** that retrieves records filtered by visibility, using a report definition **(RD_VisibleCarousels)**. This ensures that only Carousels with **Visible = true** are displayed, and it can be seamlessly integrated into components like the Image Carousel for a dynamic user experience.

**2. Component Configuration**

Add the Carousel component to your **Landing Page** and **Summary panel** using the following configuration:

- **Data Page:** Specify the Data Page **(Example:- D_CarouselList.pxResults)**

- **Image Source:** Specify the image property.

- **Title:** Specify the Title property.

- **Description:** Specify the Description property.

# Limitations and Enhancements

The limitations of the component are:

- **Keyboard navigation:**
No support for keyboard navigation and screen reader compatibility to ensure the Carousel is usable by individuals with disabilities.

- **Font Size:** There is no option to change the font size for the title and description.

- **Font Type:** The components do not support changing the font family for the title and description.

- **Font Color:** There is no capability to modify the font color for the title and description.

Ideas for enhancements are:

- **Video Support:**
Extend the Carousel to support video content alongside images, offering a richer multimedia experience.

- **Customizable Transitions:**
Developers can create and apply their transition effects beyond the built-in options.

- **Thumbnail Navigation:**
Provide a thumbnail view of all Carousel items, allowing users to quickly jump to a specific slide.

- **Interactive Elements:**
Incorporate interactive features like hotspots or clickable areas within Carousel images to enhance engagement.

# Example Use Cases

- **Product Gallery:** Display a Carousel of product images that update based on user selections or current promotions.

- **User Profile Carousel:** Show a collection of user-uploaded photos or media.

- **Dynamic Advertisements:** Rotating Carousel for dynamic ad Carousels or promotions fetched from a backend system.

# Contributors

This component was contributed by Khozema Nagdi working with [Bits In Glass | www.bitsinglass.com](https://www.bitsinglass.com)
26 changes: 26 additions & 0 deletions src/components/Pega_Extensions_ImageCarousel/DotsNavigation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { Dot, DotsContainer } from './styles';

type DotsNavigationProps = {
imageSliderData: { id: string | number | null | undefined }[];
currentSlide: number;
showSlide: (index: number) => void;
};

const DotsNavigation: React.FC<DotsNavigationProps> = ({
imageSliderData,
currentSlide,
showSlide,
}) => (
<DotsContainer>
{imageSliderData.map((slide, index) => (
<Dot
key={slide.id ? slide.id : `dot-${index}`}
onClick={() => showSlide(index)}
active={currentSlide === index}
/>
))}
</DotsContainer>
);

export default DotsNavigation;
34 changes: 34 additions & 0 deletions src/components/Pega_Extensions_ImageCarousel/SlideItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import { Slide, SlideImage, SliderTextContainer } from './styles';

type SlideItemProps = {
slide: {
title: string;
description: string;
imageURL: string | undefined;
};
animationClass: string;
isActive: boolean;
objectFit?: string;
textPosition?: string;
};

const SlideItem: React.FC<SlideItemProps> = ({
slide,
animationClass,
isActive,
objectFit,
textPosition = 'Center'
}) => {
return (
<Slide animationClass={animationClass} style={{ display: isActive ? 'block' : '' }}>
<SliderTextContainer position={textPosition}>
<h2 title='This is image slide title'>{slide.title}</h2>
<p>{slide.description}</p>
</SliderTextContainer>
<SlideImage src={slide.imageURL} alt={`Slide ${slide.title}`} objectFit={objectFit} />
</Slide>
);
};

export default SlideItem;
138 changes: 138 additions & 0 deletions src/components/Pega_Extensions_ImageCarousel/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
{
"name": "Pega_Extensions_Image_Carousel",
"label": "Image carousel",
"description": "Image carousel",
"organization": "Pega",
"version": "1.0.0",
"library": "Extensions",
"allowedApplications": [],
"type": "Widget",
"subtype": ["PAGE", "CASE"],
"properties": [
{
"name": "datasource",
"label": "Data source for Carousel",
"defaultValue": "@DATASOURCE D_pyAnnouncements.pxResults",
"format": "DATASOURCE",
"required": true,
"properties": [
{
"name": "imageURL",
"label": "Image URL",
"required": true,
"defaultValue": "@P .pyImageURL"
},
{
"name": "title",
"label": "Image Title",
"required": false,
"defaultValue": "@P .pyTitle"
},
{
"name": "description",
"label": "Image Description",
"required": false,
"defaultValue": "@P .pyDescription"
}
]
},
{
"name": "height",
"label": "Minimum height of the Carousel",
"format": "TEXT",
"required": true
},
{
"format": "SELECT",
"label": "Text Position",
"name": "textPosition",
"defaultValue": "TopLeft",
"required": true,
"source": [
{ "key": "TopLeft", "value": "Top Left" },
{ "key": "TopCenter", "value": "Top Center" },
{ "key": "TopRight", "value": "Top Right" },
{ "key": "CenterLeft", "value": "Center Left" },
{ "key": "Center", "value": "Center" },
{ "key": "CenterRight", "value": "Center Right" },
{ "key": "BottomLeft", "value": "Bottom Left" },
{ "key": "BottomCenter", "value": "Bottom Center" },
{ "key": "BottomRight", "value": "Bottom Right" }
]
},
{
"format": "SELECT",
"label": "Object Fit",
"name": "objectFit",
"defaultValue": "cover",
"required": true,
"source": [
{ "key": "fill", "value": "Fill" },
{ "key": "contain", "value": "Contain" },
{ "key": "cover", "value": "Cover" },
{ "key": "none", "value": "None" },
{ "key": "scale-down", "value": "Scale Down" }
]
},
{
"format": "BOOLEAN",
"label": "Autoplay",
"name": "autoplay",
"defaultValue": false
},
{
"name": "autoplayDuration",
"label": "Autoplay Duration (ms)",
"format": "INTEGER",
"defaultValue": 3000
},
{
"format": "SELECT",
"label": "Control Type",
"name": "controlType",
"defaultValue": "Dots",
"required": true,
"source": [
{ "key": "None", "value": "None" },
{ "key": "Dots", "value": "Dots" },
{ "key": "Buttons", "value": "Buttons" },
{ "key": "Both", "value": "Both" }
]
},
{
"format": "SELECT",
"label": "Animation Type",
"name": "animationType",
"defaultValue": "fade-in",
"required": true,
"source": [
{ "key": "fade-in", "value": "Fade In" },
{ "key": "fade-out", "value": "Fade Out" },
{ "key": "slide-in", "value": "Slide In" },
{ "key": "slide-out", "value": "Slide Out" },
{ "key": "zoom-in", "value": "Zoom In" },
{ "key": "zoom-out", "value": "Zoom Out" },
{ "key": "bounce", "value": "Bounce" },
{ "key": "shake", "value": "Shake" }
]
},
{
"label": "Conditions",
"format": "GROUP",
"properties": [
{
"name": "visibility",
"label": "Visibility",
"format": "VISIBILITY"
}
]
}
],
"defaultConfig": {
"height": "40rem",
"controlType": "dots",
"Autoplay": "yes",
"autoplayDuration": "3000",
"label": "Image Carousel"
}
}
69 changes: 69 additions & 0 deletions src/components/Pega_Extensions_ImageCarousel/demo.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { StoryObj } from '@storybook/react';
import { PegaExtensionsImageCarousel } from './index';

export default {
title: 'Widgets/Image Carousel',
argTypes: {
datasource: {
table: {
disable: true
}
},
getPConnect: {
table: {
disable: true
}
}
},
component: PegaExtensionsImageCarousel
};

const setPCore = () => {
(window as any).PCore = {
/* Nothing */
};
};

type Story = StoryObj<typeof PegaExtensionsImageCarousel>;

export const Default: Story = {
render: args => {
setPCore();
const props = {
...args,
datasource: {
source: [
{
imageURL:
'https://images.pexels.com/photos/1166644/pexels-photo-1166644.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
description: 'Description for Image 1',
title: 'Title for Image 1'
},
{
imageURL:
'https://images.pexels.com/photos/1166644/pexels-photo-1166644.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
description: 'Description for Image 2',
title: 'Title for Image 2'
},
{
imageURL:
'https://images.pexels.com/photos/1166644/pexels-photo-1166644.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
description: 'Description for Image 3',
title: 'Title for Image 3'
}
],
fields: {}
}
};
return <PegaExtensionsImageCarousel {...props} />;
},
args: {
height: '40rem',
textPosition: 'Center',
objectFit: 'cover',
autoplay: true,
autoplayDuration: 3000,
controlType: 'Dots',
animationType: 'fade-in'
}
};
10 changes: 10 additions & 0 deletions src/components/Pega_Extensions_ImageCarousel/demo.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { render, screen } from '@testing-library/react';
import { composeStories } from '@storybook/react';
import * as DemoStories from './demo.stories';

const { Default } = composeStories(DemoStories);

test('renders the Image Carousel with default args', async () => {
render(<Default />);
expect(await screen.findByText('Title for Image 1')).toBeVisible();
});
Loading

0 comments on commit 342fa6f

Please sign in to comment.