@financial-times/o-expander

6.3.1 • Public • Published

o-expander

Accessible, content-aware component for expanding and collapsing content.

Usage

Check out how to include Origami components in your project to get started with o-expander.

Markup

The o-expander component has a content element o-expander__content (the DOM to expand and collapse) and toggle elements o-expander__toggle (the triggers to toggle the expander).

  • o-expander__toggle and o-expander__content can be put anywhere within o-expander as long as o-expander__toggle is not contained within o-expander__content.
  • o-expander__toggle should be placed on a button or anchor element for keyboard accessibility.
  • There are no restrictions on sibling markup.
<div data-o-component="o-expander" class="o-expander">
	<div class="o-expander__content">
		<!-- Some content to expand and collapse. -->
	</div>
	<button class="o-expander__toggle">
		Toggle
		<span class="o-expander__visually-hidden">(content will be added above button)</span>
	</button>
</div>

By default o-expander will collapse content on initialisation. To prevent this add the class .o-expander__content--expanded (or aria-hidden="false" for the Hidden Expander).

<div data-o-component="o-expander" class="o-expander">
-	<div class="o-expander__content">
+	<div class="o-expander__content o-expander__content--expanded">
		<!-- Some content to expand and collapse. -->
	</div>
	<button class="o-expander__toggle">
		Toggle
		<span class="o-expander__visually-hidden">(content will be added above button)</span>
	</button>
</div>

or

<div data-o-component="o-expander" class="o-expander" data-o-expander-shrink-to="hidden">
-	<div class="o-expander__content">
+	<div class="o-expander__content" aria-hidden="false">
		<!-- Some content to expand and collapse. -->
	</div>
	<button class="o-expander__toggle">
		Toggle
		<span class="o-expander__visually-hidden">(content will be added above button)</span>
	</button>
</div>

Height Expander

By default the expander is based on height. Set the max-height of your collapsed expander using a custom class like my-example-expander below. o-expander will remove your max-height when the toggle is clicked to expand the expander.

-<div data-o-component="o-expander" class="o-expander">
+<div data-o-component="o-expander" class="o-expander my-example-expander">
	<h2>Collapsing content based on its height</h2>
	<div class="o-expander__content">
		<!-- Some content to expand and collapse. -->
	</div>
	<button class="o-expander__toggle">
		Toggle
		<span class="o-expander__visually-hidden">(content will be added above button)</span>
	</button>
</div>
// Set the height to 30% the viewport width (this is for demo purposes and could
// be any height). Only apply a max-height when the expander has the
// `o-expander--initialized` class for progressive enhancement, so when
// JavaScript fails content isn't hidden.
.o-expander--initialized.my-example-expander {
	max-height: 30vh;
}

Item Count Expander

The expander may also be based on the number of items within o-expander__content. For example, to show only two items within a list: Set o-expander__content on your collapsing list (ul or ol), and specify the number of items to collapse to with the data-o-expander-shrink-to data attribute.

-	<div data-o-component="o-expander" class="o-expander">
+	<div data-o-component="o-expander" class="o-expander" data-o-expander-shrink-to="2">
		<h2>Collapsing content to a number of items in a list</h2>
		<ul class="o-expander__content">
			<li>item</li>
			<li>item</li>
			<li>item</li> //hidden when collapsed
			<li>item</li> //hidden when collapsed
			<li>item</li> //hidden when collapsed
		</ul>
		<button class="o-expander__toggle">
			Toggle
			<span class="o-expander__visually-hidden">(content will be added above button)</span>
		</button>
	</div>

By default the item count assumes a list. To expand based on other children, such as paragraph p elements set data-o-expander-item-selector. E.g.

-	<div data-o-component="o-expander" class="o-expander" data-o-expander-shrink-to="2">
+	<div data-o-component="o-expander" class="o-expander" data-o-expander-shrink-to="2" data-o-expander-item-selector="p">
		<h2>Collapsing content to a number of paragraphs</h2>
		<div class="o-expander__content">
			<p>item</p>
			<p>item</p>
			<p>item</p> //hidden when collapsed
			<p>item</p> //hidden when collapsed
			<p>item</p> //hidden when collapsed
		</div>
		<button class="o-expander__toggle">
			Toggle
			<span class="o-expander__visually-hidden">(content will be added above button)</span>
		</button>
	</div>

Hidden Expander

The expander may also toggle the visibility of o-expander__content entirely. Set data-o-expander-shrink-to to hidden.

-	<div data-o-component="o-expander" class="o-expander">
+	<div data-o-component="o-expander" class="o-expander" data-o-expander-shrink-to="hidden">
		<h2>Collapsing all content</h2>
		<div class="o-expander__content">
			<!-- Some content to entirely hide/show. -->
		</div>
		<button class="o-expander__toggle">
			Toggle
			<span class="o-expander__visually-hidden">(content will be added above button)</span>
		</button>
	</div>

Toggle Text

All expanders update toggle text when the expander is toggled. To customise default copy, set data-o-expander-collapsed-toggle-text and data-o-expander-expanded-toggle-text to set the text of your expander toggles when collapsed/expanded respectively.

-	<div data-o-component="o-expander" class="o-expander">
 	<div
+		data-o-component="o-expander"
+		class="o-expander"
+		data-o-expander-collapsed-toggle-text="Show more of this please!"
+		data-o-expander-expanded-toggle-text="Less of this please!">
		<div class="o-expander__content">
			<!-- Some content to expand -->
		</div>
		<!-- This toggle text will update when be "Show more of this please!" when the expander initialises. -->
		<!-- And "Less of this please!" when the user expands the expander. -->
		<button class="o-expander__toggle">
			Toggle
			<span class="o-expander__visually-hidden">(content will be added above button)</span>
		</button>
	</div>

Set data-o-expander-toggle-state="aria" to update the toggle aria attributes but not its text. Set to none to neither update the toggle aria or text attributes.

-	<div data-o-component="o-expander" class="o-expander">
+	<div data-o-component="o-expander" class="o-expander" data-o-expander-toggle-state="none">
		<div class="o-expander__content">
			<!-- Some content to expand -->
		</div>
		<!-- This toggle text will not change. -->
		<button class="o-expander__toggle">
			Toggle
			<span class="o-expander__visually-hidden">(content will be added above button)</span>
		</button>
	</div>

Sass

Default expander styles hide the toggle until the expander is initialised successfully, so no content is obscured if JavaScript fails. When toggled the expander hides and shows content immediately. To include all o-expander CSS use the oExpander mixin.

	@include oExpander();

If using the height expander, also set your max-height. See Height Expander for an example.

For animation and other more complex styles don't include opinionated o-expander CSS. Instead create a custom expander with totally custom styles.

JavaScript

No JavaScript will run automatically unless you are using the Build Service. You must either construct an o-expander object or fire an o.DOMContentLoaded event, which o-expander listens for.

Construction

If you have set up your expander declaratively, use the following to initialise all expanders on the page with the data-o-component="o-expander" attribute:

import Expander from '@financial-times/o-expander';
Expander.init();

Or initialise a specific declarative expander:

import Expander from '@financial-times/o-expander';
const myExpanderElement = document.querySelector('my-expander');
const myExpander = new Expander(myExpanderElement);

All declarative options set via Markup may also be passed as an opts object. See the options section for a full list. e.g:

import Expander from '@financial-times/o-expander';
const myExpanderElement = document.querySelector('my-expander');
const myExpander = new Expander(myExpanderElement, {
	shrinkTo: 4,
	itemSelecor: 'p'
});

Options

All the following can be passed to JavaScript or may be set declaratively via Markup as data-attributes (hyphenated and prefixed by o-expander e.g. data-o-expander-shrink-to="height"):

  • shrinkTo [height]: The expander collapse method, "height", "hidden", or a number of items.
  • itemSelector [li]: A selector for items to count when shrinkTo is set to a number, relative to .o-expander__content.
  • expandedToggleText [less|fewer]: Toggle text for when the expander is collapsed. Defaults to "fewer", or "less" when shrinkTo is "height", or "hidden" when shrinkTo is "hidden".
  • collapsedToggleText [more]: Toggle text for when the expander is collapsed. Defaults to "more", or "show" when shrinkTo is "hidden".
  • toggleState [all|aria|none]: How to update the expander toggles: "all" to update text and aria-expanded attributes, "aria" to update only aria-expanded attributes, "none" to avoid updating toggles on click.

Custom Expander

o-expander may be used to create a custom expander without o-expander CSS. This is useful if you need the functionality of o-expander but a custom UI. E.g. o-expander sets display: none on collapsible items by default, but you may wish to animate them.

To create a custom expander call the static createCustom method. The createCustom method accepts the same options as the init method except itemSelector. Instead of itemSelector it accepts two extra objects, selectors and classnames, to customise all CSS selectors and classes.

import Expander from '@financial-times/o-expander';
const myExpanderElement = document.querySelector('my-expander');
const myCustomExpander = Expander.createCustom(myExpanderElement, {
	shrinkTo: 4,
	selectors: {
		toggle: '.my-expander__toggle', // The toggles within o-expander.
		content: '.my-expander__content', // The content within o-expander which is expandable.
		item: 'li', // The items within o-expander to count, when `shrinkTo` is set to a number.
	},
	classnames: {
		initialized: 'my-expander--initialized', // Added to the expander element when JS is initialised.
		inactive: 'my-expander--inactive', // Added to the expander element if the expander doesn't need to contract/expand.
		expanded: 'my-expander__content--expanded', // Added to the content element when expanded.
		collapsed: 'my-expander__content--collapsed', // Added to the content element when collapsed.
		collapsibleItem: 'my-expander__collapsible-item' // Added to item elements which are hidden when collapsed.
	}
});

See o-exapnder JSDocs for more details.

Events

o-expander fires the following events, which always fire before any repainting/layout occurs

  • oExpander.init - fires when the expander has initialised
  • oExpander.expand - fires when the expander expands
  • oExpander.collapse - fires when the expander collapses

Migration

State Major Version Last Minor Release Migration guide
✨ active 6 N/A migrate to v6
⚠ maintained 5 5.0.11 migrate to v5
╳ deprecated 4 4.7 -
╳ deprecated 3 3.0 -
╳ deprecated 2 2.0 -
╳ deprecated 1 1.4 -

Contact

If you have any questions or comments about this component, or need help using it, please either raise an issue, visit #origami-support or email Origami Support.


Licence

This software is published by the Financial Times under the MIT licence.

Package Sidebar

Install

npm i @financial-times/o-expander

Weekly Downloads

2,075

Version

6.3.1

License

MIT

Unpacked Size

50 kB

Total Files

19

Last publish

Collaborators

  • robertboulton
  • seraph2000
  • hamza.samih
  • notlee
  • emmalewis
  • aendra
  • the-ft
  • rowanmanning
  • chee
  • alexwilson