Back to Product Matrix Table-guidelines

CSS ComponentThe latest version of this package is: 14.1.0, Opens in new window

A Product Matrix Table is a table for product comparison.

This component provides .css, .styl, .less and .scss -files.

To be able to install this component, please refer to the Project Setup documentation.

$ npm i @ids-core/product-matrix-table@14.1.0

This is the baseline component for product matrices and product comparison tables.

Vad täcker försäkringen? Basic Extra
Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.
Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden, vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett högre belopp.

Table of Contents

Edit this section, Opens in new window

Usage

Product comparison

<div class="if block">
  <div class="if container">
    <table
      class="if table product"
      data-if-selected="1"
      aria-label="Product comparison of our home insurance"
      aria-describedby="pm-implementation-1"
    >
      <thead class="if">
        <tr class="if">
          <th scope="col" class="if"><span class="if" id="pm-implementation-1">Vad täcker försäkringen?</span></th>
          <th scope="col" class="if">
            <button type="button" class="if product-selector previous">
              <span class="if axe sr-only">Previous</span>
            </button>
            <span class="if product-name"> Basic</span>
            <span class="if product-indicator">
              <span class="if"></span>
              <span class="if"></span>
              <span class="if"></span>
            </span>
            <button type="button" class="if product-selector next">
              <span class="if axe sr-only">Next</span>
            </button>
          </th>
          <th scope="col" class="if spacer"></th>
          <th scope="col" class="if">
            <button type="button" class="if product-selector previous">
              <span class="if axe sr-only">Previous</span>
            </button>
            <span class="if product-name"> Extra</span>
            <span class="if product-indicator">
              <span class="if"></span>
              <span class="if"></span>
              <span class="if"></span>
            </span>
            <button type="button" class="if product-selector next">
              <span class="if axe sr-only">Next</span>
            </button>
          </th>
        </tr>
      </thead>
      <tbody class="if">
        <tr class="if expandable">
          <td scope="row" class="if" tabindex="0">Ansvars- och rättskydd</td>
          <td class="if"></td>
          <td class="if spacer"></td>
          <td class="if included">
            <span class="if axe sr-only">This is included in the product: Extra</span>
          </td>
        </tr>
        <tr class="if">
          <td class="if">
            Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden,
            vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett
            högre belopp.
          </td>
          <td class="if"></td>
          <td class="if spacer"></td>
          <td class="if included"></td>
        </tr>
        <tr class="if expandable">
          <td scope="row" class="if" tabindex="0">Brand och explosion</td>
          <td class="if"></td>
          <td class="if spacer"></td>
          <td class="if"></td>
        </tr>
        <tr class="if">
          <td class="if">
            Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden,
            vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett
            högre belopp.
          </td>
          <td class="if"></td>
          <td class="if spacer"></td>
          <td class="if"></td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

Product chooser

<form class="if">
  <div class="if block">
    <div class="if container">
      <table
        class="if table product selectable"
        data-if-selected="1"
        aria-label="Product comparison of our home insurance"
        aria-describedby="pm-implementation-2"
      >
        <thead class="if">
          <tr class="if">
            <th scope="col" class="if"><span class="if" id="pm-implementation-2">Vad täcker försäkringen?</span></th>
            <th scope="col" class="if">
              <button type="button" class="if product-selector previous">
                <span class="if axe sr-only">Previous</span>
              </button>
              <input
                type="radio"
                tabindex="-1"
                id="product-matrix-selectable-group-1-1"
                class="if checkbox standalone"
                name="product-matrix-selectable-group-1"
              />
              <label tabindex="-1" for="product-matrix-selectable-group-1-1"></label>
              <span class="if product-name"> Basic</span>

              <span class="if product-indicator">
                <span class="if"></span>
                <span class="if"></span>
                <span class="if"></span>
              </span>
              <button type="button" class="if product-selector next">
                <span class="if axe sr-only">Next</span>
              </button>
            </th>
            <th scope="col" class="if spacer"></th>
            <th scope="col" class="if">
              <button type="button" class="if product-selector previous">
                <span class="if axe sr-only">Previous</span>
              </button>
              <input
                type="radio"
                tabindex="-1"
                id="product-matrix-selectable-group-1-2"
                class="if checkbox standalone"
                name="product-matrix-selectable-group-1"
              />
              <label tabindex="-1" for="product-matrix-selectable-group-1-2"></label>
              <span class="if product-name"> Extra</span>

              <span class="if product-indicator">
                <span class="if"></span>
                <span class="if"></span>
                <span class="if"></span>
              </span>
              <button type="button" class="if product-selector next">
                <span class="if axe sr-only">Next</span>
              </button>
            </th>
          </tr>
        </thead>
        <tbody class="if">
          <tr class="if expandable">
            <td scope="row" class="if" tabindex="0">Ansvars- och rättskydd</td>
            <td class="if"></td>
            <td class="if spacer"></td>
            <td class="if included">
              <span class="if axe sr-only">This is included in the product: Extra</span>
            </td>
          </tr>
          <tr class="if">
            <td class="if">
              Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden,
              vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett
              högre belopp.
            </td>
            <td class="if"></td>
            <td class="if spacer"></td>
            <td class="if included"></td>
          </tr>
          <tr class="if expandable">
            <td scope="row" class="if" tabindex="0">Brand och explosion</td>
            <td class="if"></td>
            <td class="if spacer"></td>
            <td class="if"></td>
          </tr>
          <tr class="if">
            <td class="if">
              Du får ersättning för dina saker som skadas eller förloras vid inbrott eller skadegörelse i bostaden,
              vattenläckage, brand eller blixtnedslag. Försäkringsbeloppet är 1,5 miljoner kr om du inte själv ​valt ett
              högre belopp.
            </td>
            <td class="if"></td>
            <td class="if spacer"></td>
            <td class="if"></td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</form>

JavaScript implementation example

Here follows an implementation example of the JavaScript required to make the table accessible and interactive.

Do NOT use this code directly. YMMV.
const minMedium = '60rem';
const mediumMQ = window.matchMedia(`screen and (min-width: ${minMedium})`);

const setColumnAsSelectedByElement = el => {
  let nodeIndex = getNodeIndex(el);
  let normalizedIndex = nodeIndex + 1;
  let productIndex = normalizedIndex / 2;
  setColumnAsSelectedByProductIndex(el, productIndex);
};

const setColumnAsSelectedByProductIndex = (el, productIndex) => {
  el.closest('.if.table.product').setAttribute('data-if-selected', productIndex);
  const selectable = el.closest('.if.table.product.selectable');

  if (selectable) {
    selectable.querySelector(`* > tr > th:nth-child(${productIndex * 2}) [type=radio]`).checked = true;
  }
};

const setColumnAsFocused = el => {
  let nodeIndex = getNodeIndex(el);
  let normalizedIndex = nodeIndex + 1;
  let productIndex = normalizedIndex / 2;
  el.closest('.if.table.product.selectable').setAttribute('data-if-focused', productIndex);
};
const clearColumnFocus = el => {
  el.closest('.if.table.product.selectable').removeAttribute('data-if-focused');
};

const selectPreviousProduct = prev => {
  const products = Array.prototype.map.call(
    prev.closest('thead').querySelectorAll('tr > th:nth-child(even)'),
    th => th
  );

  if (products.indexOf(prev) === 0) {
    setColumnAsSelectedByElement(products[products.length - 1]);
  } else {
    setColumnAsSelectedByElement(products[products.indexOf(prev) - 1]);
  }
};

const selectNextProduct = next => {
  const products = Array.prototype.map.call(
    next.closest('thead').querySelectorAll('tr > th:nth-child(even)'),
    th => th
  );

  if (products.indexOf(next) === products.length - 1) {
    setColumnAsSelectedByElement(products[0]);
  } else {
    setColumnAsSelectedByElement(products[products.indexOf(next) + 1]);
  }
};

const handleMedium = mql => {
  if (mql.matches) {
    document.querySelectorAll('.if.product-selector.previous').forEach(prev => {
      prev.removeEventListener('click', e => {
        selectPreviousProduct(prev.closest('th'));
      });
    });
    document.querySelectorAll('.if.product-selector.next').forEach(next => {
      next.removeEventListener('click', e => {
        selectNextProduct(next.closest('th'));
      });
    });
    document
      .querySelectorAll(
        '.if.table.product.selectable > * > tr > th:nth-child(even),.if.table.product.selectable > * > tr > td:nth-child(even)'
      )
      .forEach(el => {
        el.addEventListener('click', e => {
          setColumnAsSelectedByElement(el);
        });
        el.addEventListener('keydown', e => {
          if (e.key === 'Enter') {
            setColumnAsSelectedByElement(el);
          }
        });
        el.addEventListener('focus', e => {
          setColumnAsFocused(el);
        });
        el.addEventListener('blur', e => {
          clearColumnFocus(el);
        });
      });
    document.querySelectorAll('.if.table.product.selectable > * > tr > th:nth-child(even)').forEach(el => {
      el.setAttribute('tabindex', '0');
    });
  } else {
    document.querySelectorAll('.if.product-selector.previous').forEach(prev => {
      prev.addEventListener('click', e => {
        selectPreviousProduct(prev.closest('th'));
      });
    });
    document.querySelectorAll('.if.product-selector.next').forEach(next => {
      next.addEventListener('click', e => {
        selectNextProduct(next.closest('th'));
      });
    });
    document
      .querySelectorAll(
        '.if.table.product.selectable > * > tr > th:nth-child(even),.if.table.product.selectable > * > tr > td:nth-child(even)'
      )
      .forEach(el => {
        el.removeEventListener('click', e => {
          setColumnAsSelectedByElement(el);
        });
        el.removeEventListener('keydown', e => {
          if (e.key === 'Enter') {
            setColumnAsSelectedByElement(el);
          }
        });
        el.removeEventListener('focus', e => {
          setColumnAsFocused(el);
        });
        el.removeEventListener('blur', e => {
          clearColumnFocus(el);
        });
      });
    document.querySelectorAll('.if.table.product.selectable > * > tr > th:nth-child(even)').forEach(el => {
      el.setAttribute('tabindex', '-1');
    });
  }
};
mediumMQ.addListener(handleMedium);

handleMedium(mediumMQ);
const getNodeIndex = elm => [...elm.parentNode.children].indexOf(elm);

document.querySelectorAll('.if.table.product tr.expandable > td:first-child').forEach(td => {
  td.addEventListener('keydown', e => {
    if (e.key === 'Enter') {
      if (td.parentElement.classList.contains('is-expanded')) {
        td.parentElement.classList.remove('is-expanded');
      } else {
        document.querySelectorAll('.if.table.product tr.expandable.is-expanded').forEach(tr => {
          tr.classList.remove('is-expanded');
        });
        td.parentElement.classList.add('is-expanded');
      }
    }
  });
  td.addEventListener('click', e => {
    if (td.parentElement.classList.contains('is-expanded')) {
      td.parentElement.classList.remove('is-expanded');
    } else {
      document.querySelectorAll('.if.table.product tr.expandable.is-expanded').forEach(tr => {
        tr.classList.remove('is-expanded');
      });
      td.parentElement.classList.add('is-expanded');
    }
  });
});
Edit this section, Opens in new window

Changelog

Change Log

All notable changes to this project will be documented in this file. See Conventional Commits for commit guidelines.

14.0.0 (2021-11-09)

chore

  • 🤖 Rename util to utils (f78721f)

Code Refactoring

  • 💡 Rename scope and repository (3ea5423)
  • 💡 Use new navigation structure for documentation (415aee5), closes #490579

Documentation

  • ✏️ Update links and change navigation structure (0bfd27d), closes #490579

BREAKING CHANGES

  • 🧨 The scope for If Design System npm packages has now changed from

@if-design-system to @ids-core. We have also renamed the repository from if-design-system to ids-core

  • 🧨 Util is now renamed to Utils
  • 🧨 We have now changed the navigation structure for the documentation site.

Please update any saved links!

  • 🧨 Navigation structure has now changed. Please see release notes!

13.11.0 (2021-10-19)

Features

  • 🎸 Input field hot reload (eac76b7)

13.6.3 (2021-09-17)

Bug Fixes

13.0.0 (2021-08-25)

chore

  • 🤖 Remove IE11 support for product-matrix-table (8d70520), closes #336127

BREAKING CHANGES

  • 🧨 We are removing IE11 support

12.13.1 (2021-08-11)

Bug Fixes

12.6.0 (2021-05-27)

Bug Fixes

  • 🐛 Manually set firstPublished and lastModified (e83af7d)
  • 🐛 We don't need lastModified (e458a12)

12.0.0 (2021-05-05)

Bug Fixes

Code Refactoring

Features

  • 🎸 Extract components from selection control (50607a4), closes #336508
  • 🎸 Rename and extract and update hero with no image to (384eb77), closes #336508

BREAKING CHANGES

  • 🧨 All of the mixins have now been renamed
  • 🧨 Teasers are no more. It has been replaces with Lifestyle Navigational

Card, Text Navigational Card. Studio Teasers is gone, use Studio Navigational Card instead, which is based on the old Studio Crosslinks

  • 🧨 Notification is now renamed to Alert Banner
  • 🧨 This extracts the Hero variation with no image into a separate, design

updated component named Header

  • 🧨 Footer is now renamed to Global Footer
  • 🧨 Selection controls is no more. Has ceased to be. Bereft of life, it

rests in peace. This is an ex-component. The component is split into Radio Buttons, Toggle Control and Checkbox

7.12.0 (2020-12-08)

Features

  • 🎸 Add less&scss support to product matrix table (425d0ea), closes #322294

6.36.0 (2020-10-12)

Features

  • 🎸 Add updated focus styling for product-matrix-table (f1c375e)

6.11.0 (2020-04-22)

Bug Fixes

  • 🐛 Add sr only text for footer logo link (2971dd9)

6.5.4 (2020-04-08)

Bug Fixes

5.0.1-alpha.160 (2020-03-12)

Reverts

  • Revert "chore: lerna bootstrap" (006ac4a)

5.0.1-alpha.151 (2020-03-05)

Features

  • 🎸 Add new typography rule for captions. Refactor weights (a033f4e)

5.0.1-alpha.150 (2020-03-05)

Bug Fixes

  • 🐛 Adjusting components to baseline grid (5a53cd9)

5.0.1-alpha.120 (2020-02-17)

Bug Fixes

  • 🐛 Imports and usage of extend (f22b281)

5.0.1-alpha.110 (2020-01-21)

Bug Fixes

  • 🐛 Remove duplicate support color (a7f346d)

Bug Fixes

13.0.0 (2021-08-25)

chore

  • 🤖 Remove IE11 support for product-matrix-table (8d70520), closes #336127

BREAKING CHANGES

  • 🧨 We are removing IE11 support

12.13.1 (2021-08-11)

Bug Fixes

12.6.0 (2021-05-27)

Bug Fixes

  • 🐛 Manually set firstPublished and lastModified (e83af7d)
  • 🐛 We don't need lastModified (e458a12)

12.0.0 (2021-05-05)

Bug Fixes

Code Refactoring

Features

  • 🎸 Extract components from selection control (50607a4), closes #336508
  • 🎸 Rename and extract and update hero with no image to (384eb77), closes #336508

BREAKING CHANGES

  • 🧨 All of the mixins have now been renamed
  • 🧨 Teasers are no more. It has been replaces with Lifestyle Navigational

Card, Text Navigational Card. Studio Teasers is gone, use Studio Navigational Card instead, which is based on the old Studio Crosslinks

  • 🧨 Notification is now renamed to Alert Banner
  • 🧨 This extracts the Hero variation with no image into a separate, design

updated component named Header

  • 🧨 Footer is now renamed to Global Footer
  • 🧨 Selection controls is no more. Has ceased to be. Bereft of life, it

rests in peace. This is an ex-component. The component is split into Radio Buttons, Toggle Control and Checkbox

7.12.0 (2020-12-08)

Features

  • 🎸 Add less&scss support to product matrix table (425d0ea), closes #322294

6.36.0 (2020-10-12)

Features

  • 🎸 Add updated focus styling for product-matrix-table (f1c375e)

6.11.0 (2020-04-22)

Bug Fixes

  • 🐛 Add sr only text for footer logo link (2971dd9)

6.5.4 (2020-04-08)

Bug Fixes

5.0.1-alpha.160 (2020-03-12)

Reverts

  • Revert "chore: lerna bootstrap" (006ac4a)

5.0.1-alpha.151 (2020-03-05)

Features

  • 🎸 Add new typography rule for captions. Refactor weights (a033f4e)

5.0.1-alpha.150 (2020-03-05)

Bug Fixes

  • 🐛 Adjusting components to baseline grid (5a53cd9)

5.0.1-alpha.120 (2020-02-17)

Bug Fixes

  • 🐛 Imports and usage of extend (f22b281)

5.0.1-alpha.110 (2020-01-21)

Bug Fixes

  • 🐛 Remove duplicate support color (a7f346d)

5.0.1-alpha.87 (2019-12-04)

Bug Fixes

5.0.1-alpha.76 (2019-12-02)

Bug Fixes

  • 🐛 Reduce greedy selector (fd8d7f2)

5.0.1-alpha.67 (2019-11-26)

Bug Fixes

  • 🐛 Use correct color variable (ea4fbcc)
  • 🐛 Use package-based imports (6822233)

5.0.1-alpha.59 (2019-11-22)

Bug Fixes

  • 🐛 Make state list wider for items (fee039e)

5.0.1-alpha.58 (2019-11-22)

Features

  • 🎸 Remove normalize.css (2c23c2b)

5.0.1-alpha.56 (2019-11-22)

Bug Fixes

  • 🐛 Fix javascript and css implementation for selecting cols (dc9a38e)
  • 🐛 Prevent overflow on radio button (fc56a5e)
  • add fake blocks for now (f188b0c)

Features

  • 🎸 Add product-matrix (34ad4d2)
  • 🎸 Finalize normal and selectable product matrix desktop (cfc2744)
Edit this section, Opens in new window
Contact us, Opens in new window