2

I've been struggling with what seems like a simple solution for far too long. I'm new to typescript and new to react.

I'm trying to use the react-mulit-carousel NPM package.

I'm able to get the customButtonGroup to work successfully in the sandbox: https://codesandbox.io/s/fervent-rain-332mn?file=/src/App.js:834-913

But when I try to implement that in my SPFX solution i get the following error:

Type '{}' is missing the following properties from type '{ [x: string]: any; next: any; previous: any; goToSlide: any; }': next, previous, goToSlide

import * as React from 'react';
import { IBrandsCarouselProps } from './IBrandsCarouselProps';
import { IBrandsCarouselState } from './IBrandsCarouselState';
import { IBrand } from './IBrand';
import styles from '../../../styles/styles.module.scss';
import { SPHttpClient } from '@microsoft/sp-http';
import Carousel from 'react-multi-carousel';
import 'react-multi-carousel/lib/styles.css';
import '../../../styles/react-carousel.scss';
import { getNextElement } from 'office-ui-fabric-react';


const responsive = {
    desktop: {
        breakpoint: { max: 4000, min: 768 },
        items: 4,
        partialVisibilityGutter: 0
    },
    tablet: {
        breakpoint: { max: 768, min: 480 },
        items: 3,
        partialVisibilityGutter: 30
    },
    mobile: {
        breakpoint: { max: 480, min: 0 },
        items: 2,
        partialVisibilityGutter: 30
    }
};



export default class BrandsCarousel extends React.Component<IBrandsCarouselProps, IBrandsCarouselState>{


    constructor(props) {
        super(props);

        this.state = {
            displayedBrands: [],
            isLoading: true
        };
    }

    /**
   * Loads data from a list by using a cached view
   */
    public loadBrandsFromList(): Promise<IBrand[]> {

        const queryUrlGetAllItems: string = `[[HIDDEN]]`;


        return this.props.context.spHttpClient.get(
            queryUrlGetAllItems,
            SPHttpClient.configurations.v1)
            .then(
                (response: any) => {
                    if (response.status >= 200 && response.status < 300) {
                        return response.json();
                    } else {
                        return Promise.resolve(new Error(JSON.stringify(response)));
                    }
                })
            .then((data: any) => {
                let documents: IBrand[] = [];
                if (data) {
                    for (let i = 0; i < data.value.length; i++) {
                        let item = data.value[i];
                        var doc: IBrand = {
                            Title: item.Title,
                            Image: item.Image.Url ? item.Image.Url : "No Image Set",
                            Url: item.Url.Url,
                            Business: item.Business
                        };
                        documents.push(doc);
                    }
                }
                return documents;

            }).catch((ex) => {
                // console.log("readDocumentsFromLibrary > spHttpClient.get()...catch:", ex);
                throw ex;
            });

    }


    public render(): React.ReactElement<IBrandsCarouselProps> {


        // Sorting is Done in the Rest Call
        let items = this.state.displayedBrands;

        // create a new list that filters by the tags
        // Business is an array of strings
        // If the item has an array value that matches the Props Business

        if (this.props.Business != "All") {
            let filteredItems = [];

            for (let i = 0; i < items.length; i++) {
                const item = items[i];
                if (item.Business.indexOf(this.props.Business) > -1) {
                    filteredItems.push(item);
                }
            }

            items = filteredItems;
        }

        const ButtonGroup = ({ next, previous, goToSlide, ...rest }) => {
            const {
                carouselState: { currentSlide }
            } = rest;
            return (
                <div className="carousel-button-group">
                    <div
                        className={currentSlide === 0 ? "disable" : ""}
                        onClick={() => previous()}
                    >
                        Prev
                        </div>
                    <div onClick={() => next()}>Next</div>
                    <div onClick={() => goToSlide(currentSlide + 1)}> Go to any slide </div>

                </div>
            );
        };

        return (

            <div className={styles["brands-slider"] + " " + styles["card-docs-slider"] + " hub-carousel"}>
                {this.props.IsTitle && this.props.Title != "" &&
                    <div className={styles["widget-header"]}>
                        <span className={styles["view"]}>{this.props.Title}</span>
                    </div>
                }
                <div className={styles["card-slider"]}>
                    {items && items.length > 0 &&
                        <Carousel
                            responsive={responsive}
                            arrows
                            additionalTransfrom={0}
                            itemClass={"react-carousel-item"}
                            minimumTouchDrag={80}
                            partialVisible
                            renderButtonGroupOutside
                            customButtonGroup={<ButtonGroup />}
                        >
                            {items.map((item) => {
                                return (
                                    <a href={item.Url} className={styles["block-link"]} target="_blank">
                                        <img src={item.Image} alt={item.Title} />
                                    </a>
                                );
                            })
                            }
                        </Carousel>
                    }
                    {items && items.length == 0 &&
                        <p>No Brands found. Please, check the List</p>
                    }
                </div>
            </div>


        );
    }

    public componentDidMount() {
        this.loadBrandsFromList().then(
            //resolve
            (documents: IBrand[]) => {
                this.setState({
                    displayedBrands: documents,
                    isLoading: false,
                });
            },
            //reject
            (data: any) => {
                this.setState({
                    displayedBrands: [],
                    isLoading: false,
                    isErrorOccured: true,
                    errorMessage: data
                });
            }
        ).catch((ex) => {
            this.setState({
                displayedBrands: [],
                isLoading: false,
                isErrorOccured: true,
                errorMessage: ex.errorMessage
            });

        });
    }
}

Any help would be greatly appreciated. Thank you!

rsnyder
  • 383
  • 1
  • 6
  • 19

1 Answers1

4

I was able to figure it out. I needed to pass parameters. Oops!

Hopefully this can help out another JSX, Typescript, React beginner in the future.

                        <Carousel
                            responsive={responsive}
                            arrows
                            additionalTransfrom={0}
                            itemClass={"react-carousel-item"}
                            minimumTouchDrag={80}
                            partialVisible
                            renderButtonGroupOutside
                            customButtonGroup={<ButtonGroup
                                next={this.props.next}
                                previous={this.props.previous}
                                rest={this.props.rest}
                            />}
                        >

Here's the Custom Button group if it helps as well. I couldn't find the documentation to hide the next button.

const ButtonGroup = ({ next, previous, ...rest }) => {
            const {
                carouselState: { currentSlide, totalItems, slidesToShow }
            } = rest;

            return (
                <div className="carousel-button-group">
                    <button aria-label="Go to previous slide"
                        className={currentSlide === 0 ? "disable" : "react-multiple-carousel__arrow react-multiple-carousel__arrow--left"}
                        onClick={() => previous()}></button>
                    <button aria-label="Go to next slide" 
                        className={currentSlide === totalItems - slidesToShow ? "disable" : "react-multiple-carousel__arrow react-multiple-carousel__arrow--right"}
                        onClick={() => next()}></button>
                </div>
            );
        };
rsnyder
  • 383
  • 1
  • 6
  • 19
  • 1
    my Carousel is inside a React functional component, and I do not understand how you can pass this.props.next, this.props.previous and this.props.rest to the button group, as there is no this in a functional component? – jaeyow Aug 17 '21 at 00:09
  • @jaeyow Just remove them. They get passed in automatically. customButtonGroup={} – Nick Sep 19 '21 at 20:03
  • @Nick it doesn't. Mine throws all kinds of whacky errors. ```Overload 1 of 2, '(props: CarouselProps | Readonly): Carousel', gave the following error. Type '(props: any) => Element' is missing the following properties from type 'ReactElement>': type, props, key``` Passing in any type of prop results in the my CustomDot component not being able to read it. Or any custom override that the lib provides. This issue persists across all the custom override functionalities provided. – foreverAnIntern Oct 04 '21 at 12:55