Search code examples
javascriptuser-interfacesemantic-uisemantic-ui-react

How to have multiple accordions open with semantic UI react?


I am trying to use a semantic UI accordion containing multiple entries and allow more than one entry to be open at once; each entry has a title portion containing an icon with a popup attached and a content area containing a textarea.

I would like to be able to have both accordions open at the same time which is apparently supported by using the exclusive={false} prop when making the accordion element as described in the documentation here

But that example looks to be using an array of objects with content that is a string, not other react/html/jsx elements (in my case it is semantic ui icons, popups and textareas). That array of objects is passed in to the accordion's panel prop.

And I am unfamiliar with what the semantic ui react accordion requires to function correctly with keeping track of indices and other stuff, I am not sure what else I need to configure or if this is possible with the semantic ui component as is.

I essentially copied this example and use an active index and an onclick handler which switches the active index in the component react state.

Here is a snippet of the accordion and onclick handler and react app state:

class FileUpload extends Component {

 // other stuff omitted 

  constructor(props) {
    super(props);

    this.state = {
      activeAccordionIndex: -1
    };

  handleAccordionClick = (e, titleProps) => {
     const { index } = titleProps;
     const { activeAccordionIndex } = this.state;
     const newIndex = activeAccordionIndex === index ? -1 : index;

     this.setState({
       activeAccordionIndex: newIndex
     })
   }

 // I'm using a small helper function to create the accordion and invoke it in 
 // the render method, just one item for brevity; the other entries are pretty 
 // much the same

getAccordionInputs() {

     const { activeAccordionIndex } = this.state;

     let accordionContent = (
      <Accordion fluid exclusive={false}>
         <Accordion.Title
           className="file-upload-ordinal-accord-title"
           active={activeAccordionIndex === 0}
           index={0}
           onClick={this.handleAccordionClick}
          >
           <Icon name='dropdown' />
           Enter Ordinal Features
           <Popup
             on="click"
             position="right center"
             header="Ordinal Features Help"
             content={
               <div className="content">
                 <p>Ordinal Features help description</p>
               </div>
             }
             trigger={
               <Icon
                 className="file-upload-ordinal-help-icon"
                 inverted
                 size="large"
                 color="orange"
                 name="info circle"
               />
             }
           />
         </Accordion.Title>
         <Accordion.Content
            active={activeAccordionIndex === 0}
          >
           <textarea
             className="file-upload-ordinal-text-area"
             id="ordinal_features_text_area_input"
             label="Ordinal Features"
             placeholder={"{\"ord_feat_1\": [\"MALE\", \"FEMALE\"], \"ord_feat_2\": [\"FIRST\", \"SECOND\", \"THIRD\"]}"}
             onChange={this.handleOrdinalFeatures}
           />
         </Accordion.Content>
       </Accordion>
     )
     return accordionContent;
   }

  }

I don't know how to set this up to allow multiple accordions open at once with content that is not a string. Is this possible with the semantic ui accordion? Or do I need to find an alternative solution and/or make the piece with the desired behavior by hand?


Solution

  • You can change your index logic so instead of setting the active index in your state add the index to an array and check if it exists inside the array and if it does show that accordion

    Here is an example:

    export default class AccordionExampleStandard extends Component {
      state = { activeIndexs: [] };
    
      handleClick = (e, titleProps) => {
        const { index } = titleProps;
        const { activeIndexs } = this.state;
        const newIndex = activeIndexs;
    
        const currentIndexPosition = activeIndexs.indexOf(index);
        if (currentIndexPosition > -1) {
          newIndex.splice(currentIndexPosition, 1);
        } else {
          newIndex.push(index);
        }
    
        this.setState({ activeIndexs: newIndex });
      };
    
      render() {
        const { activeIndexs } = this.state;
    
        return (
          <Accordion>
            <Accordion.Title
              active={activeIndexs.includes(0)}
              index={0}
              onClick={this.handleClick}
            >
              <Icon name="dropdown" />
              What is a dog?
            </Accordion.Title>
            <Accordion.Content active={activeIndexs.includes(0)}>
              <p>
                A dog is a type of domesticated animal. Known for its loyalty and
                faithfulness, it can be found as a welcome guest in many households
                across the world.
              </p>
            </Accordion.Content>
    
            <Accordion.Title
              active={activeIndexs.includes(1)}
              index={1}
              onClick={this.handleClick}
            >
              <Icon name="dropdown" />
              What kinds of dogs are there?
            </Accordion.Title>
            <Accordion.Content active={activeIndexs.includes(1)}>
              <p>
                There are many breeds of dogs. Each breed varies in size and
                temperament. Owners often select a breed of dog that they find to be
                compatible with their own lifestyle and desires from a companion.
              </p>
            </Accordion.Content>
    
            <Accordion.Title
              active={activeIndexs.includes(2)}
              index={2}
              onClick={this.handleClick}
            >
              <Icon name="dropdown" />
              How do you acquire a dog?
            </Accordion.Title>
            <Accordion.Content active={activeIndexs.includes(2)}>
              <p>
                Three common ways for a prospective owner to acquire a dog is from
                pet shops, private owners, or shelters.
              </p>
              <p>
                A pet shop may be the most convenient way to buy a dog. Buying a dog
                from a private owner allows you to assess the pedigree and
                upbringing of your dog before choosing to take it home. Lastly,
                finding your dog from a shelter, helps give a good home to a dog who
                may not find one so readily.
              </p>
            </Accordion.Content>
          </Accordion>
        );
      }
    }
    

    https://codesandbox.io/s/xo226wp5lw?module=example.js