Expansion panel

Use the function chi.epanel(elem) to instantiate a new extension panel component in the DOM element passed as a parameter. It will return an ExpansionPanel object that will provide functions for interacting with the panel, such as setState. Valid states are pending, active, disabled and done.

Expansion panel
CenturyLink
1100 112th Ave NE
Suite 400
Bellevue, WA 98004
Active panel title

Lorem ipsum dolor sit amet, consectetur adipisicing elit. At atque consequatur consequuntur excepturi illo maiores, nobis placeat recusandae rem! Accusantium ad in minus molestiae? Commodi cupiditate labore nihil sed veritatis!

Toggle panel state
<div class="m-epanel" id="example1">
  <div class="m-epanel__header">
    <div class="m-epanel__title">Expansion panel</div>
    <div class="m-epanel__content">
      <div class="m-epanel__collapse">
        <div class="-done--only">
          <!-- some body content -->
        </div>
      </div>
    </div>
    <div class="m-epanel__action -done--only">
      <button class="a-btn -primary -flat" data-chi-epanel-action="active">Change</button>
    </div>
  </div>
  <div class="m-epanel__collapse -ml--0">
    <div class="-active--only">
      <div class="m-epanel__body">
        <div class="m-epanel__content">
          <div class="m-epanel__subtitle">Active panel title</div>
          <!-- some body content -->
        </div>
        <div class="m-epanel__footer -justify-content--end">
          <button class="a-btn -lg">Previous</button>
          <button class="a-btn -lg -primary -ml--2">Continue</button>
        </div>
      </div>
    </div>
  </div>
</div>

<div class="-d--flex -justify-content--center -mt--3">
  <div class="m-form__item -inline -row">
    <div class="a-radio">
      <input class="a-radio__input" type="radio" name="example1" value="pending" id="example1_radio1" checked="checked">
      <label class="a-radio__label" for="example1_radio1">Pending</label>
    </div>
  </div>
  <div class="m-form__item -inline -row">
    <div class="a-radio">
      <input class="a-radio__input" type="radio" name="example1" value="active" id="example1_radio2">
      <label class="a-radio__label" for="example1_radio2">Active</label>
    </div>
  </div>
  <div class="m-form__item -inline -row">
    <div class="a-radio">
      <input class="a-radio__input" type="radio" name="example1" value="done" id="example1_radio3">
      <label class="a-radio__label" for="example1_radio3">Done</label>
    </div>
  </div>
  <div class="m-form__item -inline -row">
    <div class="a-radio">
      <input class="a-radio__input" type="radio" name="example1" value="disabled" id="example1_radio4">
      <label class="a-radio__label" for="example1_radio4">Disabled</label>
    </div>
  </div>
</div>
var epDom = document.getElementById('example1');
var ePanel = chi.expansionPanel(epDom);
var checkBoxes = document.querySelectorAll('input[name="example1"]');

// Add event listeners to radio buttons
Array.prototype.forEach.call(
  checkBoxes,
  function(input) {
    input.addEventListener('change', function() {
      if (this.checked) {
        ePanel.setState(this.value);
      }
    }, false);
  }
);

// Listen to Extension Panel state changes and update radio buttons
epDom.addEventListener('chi.epanel.change', function() {
  var state = ePanel.getStateName();
  Array.prototype.forEach.call(
    checkBoxes,
    function(input) {
      if (input.value === state) {
        input.checked = true;
      }
    }
  );
});

Actions

You can bind actions to activators inside the expansion panel with the data-chi-epanel-action attribute.

ActionDescription
pendingSets the panel to the pending state. All the contents under the -active--only and the -done--only will not be visible.
activeSets the panel to the active state. All the contents under the -active--only will be visible.
toggleIn the case the panel is previously set to active, this action sets it to pending. It sets to active state otherwise.
doneSets the panel to the done state. All the contents under the -done--only will be visible.
disabledSets the panel to the disabled state. All the contents under the -active--only and the -done--only will not be visible, and the title will render in a soft grey color.
inactiveIs a synonym for pending.
nextSets the next panel in active status.
previousSets the previous panel in active status.

TipYou can freely add, remove or change the data-chi-epanel-action attribute on the fly, as is the main element with the m-epanel class, the one which listens for the events.

Example

Click me to toggle
CenturyLink
1100 112th Ave NE
Suite 400
Bellevue, WA 98004
Active panel title

Content in expansion panel

<div class="m-epanel" id="example2" data-chi-epanel-group="example2">
  <div class="m-epanel__header">
    <div class="m-epanel__title" data-chi-epanel-action="toggle">Click me to toggle</div>
    <div class="m-epanel__content">
      <div class="m-epanel__collapse">
        <div class="-done--only">CenturyLink...</div>
      </div>
    </div>
    <div class="m-epanel__action -done--only">
      <button class="a-btn -primary -flat" data-chi-epanel-action="active">Change</button>
    </div>
  </div>
  <div class="m-epanel__collapse -ml--0">
    <div class="-active--only">
      <div class="m-epanel__body">
        <div class="m-epanel__content">
          <div class="m-epanel__subtitle">Active panel title</div>
          <p class="m-epanel__text">Content in expansion panel</p>
        </div>
        <div class="m-epanel__footer -justify-content--end">
          <button class="a-btn -lg" data-chi-epanel-action="pending">Pending</button>
          <button class="a-btn -lg -primary -ml--2" data-chi-epanel-action="done">Done</button>
        </div>
      </div>
    </div>
  </div>
</div>
var ep = chi.expansionPanel(document.getElementById('example2'));

Group of expansion panels

The real power of the component comes when several expansion panels are used together. You can group them using the attribute data-chi-epanel-group="name-of-the-group". Now they will work together as an stepped form. When an element is activated, the previous expanding panels will get the done state and the next will get the pending state.

Expansion panel #1
Done
CenturyLink
1100 112th Ave NE
Suite 400
Bellevue, WA 98004
Active panel title

Content in expansion panel

Expansion panel #2
Done
CenturyLink
1100 112th Ave NE
Suite 400
Bellevue, WA 98004
Active panel title

Content in expansion panel

Expansion panel #3
Done
CenturyLink
1100 112th Ave NE
Suite 400
Bellevue, WA 98004
Active panel title

Content in expansion panel

Expansion panel #4
Done
CenturyLink
1100 112th Ave NE
Suite 400
Bellevue, WA 98004
Active panel title

Content in expansion panel

<div class="m-epanel -active" data-chi-epanel-group="example3">
  <div class="m-epanel__header">
    <div class="m-epanel__title">Expansion panel #1</div>
    <div class="m-epanel__content">
      <div class="m-epanel__collapse">
        <div class="-done--only">Done<br>CenturyLink...</div>
      </div>
    </div>
    <div class="m-epanel__action -done--only">
      <button class="a-btn -primary -flat" data-chi-epanel-action="active">Change</button>
    </div>
  </div>
  <div class="m-epanel__collapse -ml--0">
    <div class="-active--only">
      <div class="m-epanel__body">
        <div class="m-epanel__content">
          <div class="m-epanel__subtitle">Active panel title</div>
          <p class="m-epanel__text">Content in expansion panel</p>
        </div>
        <div class="m-epanel__footer -justify-content--end">
          <button class="a-btn -lg -primary -ml--2" data-chi-epanel-action="next">Continue</button>
        </div>
      </div>
    </div>
  </div>
</div>

<div class="m-epanel" data-chi-epanel-group="example3">
  <div class="m-epanel__header">
    <div class="m-epanel__title">Expansion panel #2</div>
    <div class="m-epanel__content">
      <div class="m-epanel__collapse">
        <div class="-done--only">Done<br>CenturyLink...</div>
      </div>
    </div>
    <div class="m-epanel__action -done--only">
      <button class="a-btn -primary -flat" data-chi-epanel-action="active">Change</button>
    </div>
  </div>
  <div class="m-epanel__collapse -ml--0">
    <div class="-active--only">
      <div class="m-epanel__body">
        <div class="m-epanel__content">
          <div class="m-epanel__subtitle">Active panel title</div>
          <p class="m-epanel__text">Content in expansion panel</p>
        </div>
        <div class="m-epanel__footer -justify-content--end">
          <button class="a-btn -lg" data-chi-epanel-action="previous">Previous</button>
          <button class="a-btn -lg -primary -ml--2" data-chi-epanel-action="next">Continue</button>
        </div>
      </div>
    </div>
  </div>
</div>

<!-- last div x3 -->
chi.expansionPanel(document.querySelectorAll('[data-chi-epanel-group="example3"]'));

Accordion mode

You can configure the expansion panels component to work as an accordion in a way that when an element is activated, any other panel in the same group will be deactivated by putting it in the pending state. To activate this mode, you have to add the mode: "accordion" configuration parameter at the moment of creation. Done states should not be used in this mode. In this example, titles have been configured to trigger the action toggle, and the expansion panel #3 also have next and previous buttons that fulfills the accordion behavior.

Expansion panel #1
Active panel title

Content in expansion panel

Expansion panel #2
Active panel title

Content in expansion panel

Expansion panel #3
Active panel title

Content in expansion panel

Expansion panel #4
Active panel title

Content in expansion panel

<div class="m-epanel -bordered -active" data-chi-epanel-group="example4">
  <div class="m-epanel__header">
    <div class="m-epanel__title" data-chi-epanel-action="toggle">Expansion panel #1</div>
  </div>
  <div class="m-epanel__collapse">
    <div class="-active--only">
      <div class="m-epanel__body">
        <div class="m-epanel__content">
          <div class="m-epanel__subtitle">Active panel title</div>
          <p class="m-epanel__text">Content in expansion panel</p>
        </div>
      </div>
    </div>
  </div>
</div>

<div class="m-epanel -bordered" data-chi-epanel-group="example4">
  <div class="m-epanel__header">
    <div class="m-epanel__title" data-chi-epanel-action="toggle">Expansion panel #2</div>
  </div>
  <div class="m-epanel__collapse">
    <div class="-active--only">
      <div class="m-epanel__body">
        <div class="m-epanel__content">
          <div class="m-epanel__subtitle">Active panel title</div>
          <p class="m-epanel__text">Content in expansion panel</p>
        </div>
      </div>
    </div>
  </div>
</div>

<!-- last div x3 -->
chi.expansionPanel(
  document.querySelectorAll('[data-chi-epanel-group="example4"]'),
  {mode: 'accordion'}
);

Free mode

In this mode, there is no automated action triggered when an element activates. So you have to manage all custom behavior.

Expansion panel #1
Active panel title

Content in expansion panel

Expansion panel #2
Active panel title

Content in expansion panel

Expansion panel #3
Active panel title

Content in expansion panel

Expansion panel #4
Active panel title

Content in expansion panel

<div class="m-epanel -bordered -active" data-chi-epanel-group="example5">
  <div class="m-epanel__header">
    <div class="m-epanel__title" data-chi-epanel-action="toggle">Expansion panel #1</div>
  </div>
  <div class="m-epanel__collapse">
    <div class="-active--only">
      <div class="m-epanel__body">
        <div class="m-epanel__content">
          <div class="m-epanel__subtitle">Active panel title</div>
          <p class="m-epanel__text">Content in expansion panel</p>
        </div>
      </div>
    </div>
  </div>
</div>

<div class="m-epanel -bordered" data-chi-epanel-group="example4">
  <div class="m-epanel__header">
    <div class="m-epanel__title" data-chi-epanel-action="toggle">Expansion panel #2</div>
  </div>
  <div class="m-epanel__collapse">
    <div class="-active--only">
      <div class="m-epanel__body">
        <div class="m-epanel__content">
          <div class="m-epanel__subtitle">Active panel title</div>
          <p class="m-epanel__text">Content in expansion panel</p>
        </div>
      </div>
    </div>
  </div>
</div>

<!-- last div x3 -->
chi.expansionPanel(
  document.querySelectorAll('[data-chi-epanel-group="example5"]'),
  {mode: 'free'}
);

Custom mode

Write your custom mode by adding a handler for state changes of the expansion panels in the configuration, and writing your own functions for the active, done, pending, disabled, toggle, next and previous actions. In the example, the component behaves similar to the accordion but alternates between done and pending states instead of active and pending states. The overridden action functions are for documenting purposes only because they clone the functionality of the default ones.

Expansion panel #1
CenturyLink
1100 112th Ave NE
Suite 400
Bellevue, WA 98004
Expansion panel #2
CenturyLink
1100 112th Ave NE
Suite 400
Bellevue, WA 98004
Expansion panel #3
CenturyLink
1100 112th Ave NE
Suite 400
Bellevue, WA 98004
Expansion panel #4
CenturyLink
1100 112th Ave NE
Suite 400
Bellevue, WA 98004
<div class="m-epanel -done" data-chi-epanel-group="example6">
  <div class="m-epanel__header">
    <div class="m-epanel__title" data-chi-epanel-action="done">Expansion panel #1</div>
    <div class="m-epanel__content">
      <div class="m-epanel__collapse">
        <div class="-done--only">CenturyLink...</div>
      </div>
    </div>
  </div>
</div>

<div class="m-epanel" data-chi-epanel-group="example6">
  <div class="m-epanel__header">
    <div class="m-epanel__title" data-chi-epanel-action="done">Expansion panel #2</div>
    <div class="m-epanel__content">
      <div class="m-epanel__collapse">
        <div class="-done--only">CenturyLink...</div>
      </div>
    </div>
  </div>
</div>
<!-- last div x3 -->
chi.expansionPanel(
  document.querySelectorAll('[data-chi-epanel-group="example6"]'),
  {
    mode: 'custom',
    changeHandler: function (newState, oldState, expansionPanel, panelGroup) {
      if (newState === chi.EXPANSION_PANEL_STATES.DONE) {
        panelGroup.expansion_panels.forEach(function (exPa) {
          if (exPa !== expansionPanel) {
            exPa.setState(chi.EXPANSION_PANEL_STATES.PENDING.NAME);
          }
        });
      }
    },
    customActions: {
      active: (expansionPanel, epGroup) =>
        expansionPanel.setState(chi.EXPANSION_PANEL_STATES.ACTIVE.NAME),
      done: (expansionPanel, epGroup) =>
        expansionPanel.setState(chi.EXPANSION_PANEL_STATES.DONE.NAME),
      pending: (expansionPanel, epGroup) =>
        expansionPanel.setState(chi.EXPANSION_PANEL_STATES.PENDING.NAME),
      toggle: (expansionPanel, epGroup) => {
        if (expansionPanel._state === chi.EXPANSION_PANEL_STATES.ACTIVE) {
          expansionPanel.setState(chi.EXPANSION_PANEL_STATES.PENDING.NAME);
        } else {
          expansionPanel.setState(chi.EXPANSION_PANEL_STATES.ACTIVE.NAME);
        }
      },
      next: (expansionPanel, epGroup) => {
        epGroup.reset(expansionPanel);
        epGroup.next().setState(chi.EXPANSION_PANEL_STATES.ACTIVE.NAME);
      },
      previous: (expansionPanel, epGroup) => {
        epGroup.reset(expansionPanel);
        epGroup.previous().setState(chi.EXPANSION_PANEL_STATES.ACTIVE.NAME);
      },
      disabled: (expansionPanel, epGroup) =>
        expansionPanel.setState(chi.EXPANSION_PANEL_STATES.DISABLED.NAME)
    }
  }
);

Preventing memory leaks

Expansion panel components have a dispose function to free all resources attached to the element, such as event listeners and object data. You should call this method when you want to remove the component.

var elemArray = document.querySelectorAll('[data-chi-epanel-group="groupName"]');
var expansionPanelsArray = chi.expansionPanel(elemArray);
// do stuff
expansionPanelsArray.forEach(function (expansionPanel) {
  expansionPanel.dispose();
});

TipIt is safe to call the expansionPanel method more than once, as it will return any previously created expansion panel component associated to the element.

var elem = document.getElementById('expansion-panel-1');
var expansionPanelComponent = chi.expansionPanel(elem);
var elem2 = document.getElementById('expansion-panel-1');
var expansionPanelComponent2 = chi.expansionPanel(elem2);
expansionPanelComponent === expansionPanelComponent2; // returns true

expansionPanelComponent.dispose(); // Only have to do it once.