ba-tab-price-comparison
A tab component for comparing prices across multiple dates
Figma
GitHub
Storybook
Live Demo
<ba-tab-price-comparison></ba-tab-price-comparison>
<script>
(() => {
const priceDates = [
{
date: '2025-10-16',
price: 'Sold out',
state: 'unavailable'
}, {
date: '2025-10-17',
price: 'Sold out',
state: 'unavailable'
}, {
date: '2025-10-18',
price: 'Sold out',
state: 'unavailable'
}, {
date: '2025-10-19',
price: '£144',
state: ""
}, {
date: '2025-10-20',
price: '£176',
state: ""
}, {
date: '2025-10-21',
price: '£176',
state: ""
}, {
date: '2025-10-22',
price: '£148',
state: "sale",
overline: "Special offer"
}, {
date: '2025-10-23',
price: '£130',
state: ""
}, {
date: '2025-10-24',
price: '£189',
state: ""
}, {
date: '2025-10-25',
price: '£149',
state: ""
}, {
date: '2025-10-26',
price: '£108',
state: ""
}, {
date: '2025-10-27',
price: '£113',
state: ""
}, {
date: '2025-10-28',
price: '£107',
state: ""
}, {
date: '2025-10-29',
price: '£107',
state: ""
}, {
date: '2025-10-30',
price: '£93',
state: ""
}, {
date: '2025-10-31',
price: '£107',
state: ""
}, {
date: '2025-11-01',
price: '£107',
state: ""
}, {
date: '2025-11-02',
price: '£86',
state: ""
}, {
date: '2025-11-03',
price: '£86',
state: ""
}, {
date: '2025-11-04',
price: '£83',
state: "success",
overline: "Lowest fare"
}, {
date: '2025-11-05',
price: '£95',
state: ""
}, {
date: '2025-11-06',
price: '£97',
state: ""
}, {
date: '2025-11-07',
price: 'Sold out',
state: 'unavailable'
}, {
date: '2025-11-08',
price: 'Sold out',
state: 'unavailable'
}, {
date: '2025-11-09',
price: 'Sold out',
state: 'unavailable'
}
];
let showMoreDates = false;
let selectedDate = '';
let startIndex = 0
let previousButtonHidden = false;
let nextButtonHidden = false;
const data = {
data7: [],
data2: []
};
const tabPrice = document.querySelector('ba-tab-price-comparison');
if (!tabPrice)
return;
function setData() {
if (selectedDate !== '') {
const selectedIndex = priceDates.findIndex(item => item.date === selectedDate);
let startIndex2 = selectedIndex % 2 == 0
? selectedIndex
: selectedIndex - 1;
data.data2 = structuredClone(priceDates.slice(startIndex2, startIndex2 + 2));
} else {
data.data2 = structuredClone(priceDates.slice(startIndex, startIndex + 2));
}
data.data7 = structuredClone(priceDates.slice(startIndex, startIndex + 7));
setLowestFare()
if (selectedDate !== '') {
const data2SelectedIndex = data
.data2
.findIndex(item => item.date === selectedDate);
if (data2SelectedIndex !== -1) {
data
.data2[data2SelectedIndex]
.state = 'selected'
}
const data7SelectedIndex = data
.data7
.findIndex(item => item.date === selectedDate);
if (data7SelectedIndex !== -1) {
data
.data7[data7SelectedIndex]
.state = 'selected'
}
}
tabPrice.setAttribute('data', JSON.stringify(data));
}
function setLowestFare() {
let lowestFare2 = null;
data
.data2
.forEach(item => {
const price = parseInt(item.price.replace('£', ''));
if (!isNaN(price)) {
if (lowestFare2 === null || price < lowestFare2) {
lowestFare2 = price;
}
}
})
if (lowestFare2 !== null) {
const data2LowestFareIndex = data
.data2
.findIndex(item => item.price === '£' + lowestFare2);
data
.data2[data2LowestFareIndex]
.state = 'success';
data
.data2[data2LowestFareIndex]
.overline = 'Lowest fare';
}
let lowestFare7 = null;
data
.data7
.forEach(item => {
const price = parseInt(item.price.replace('£', ''));
if (!isNaN(price)) {
if (lowestFare7 === null || price < lowestFare7) {
lowestFare7 = price;
}
}
})
if (lowestFare7 !== null) {
const data7LowestFareIndex = data
.data7
.findIndex(item => item.price === '£' + lowestFare7);
data
.data7[data7LowestFareIndex]
.state = 'success';
data
.data7[data7LowestFareIndex]
.overline = 'Lowest fare';
}
}
setData();
document.addEventListener('baDateSelected', (event) => {
selectedDate = event.detail.date;
const state = event.detail.state;
if (state == 'unavailable') {
return;
}
setData()
});
document.addEventListener('baNextPage', (event) => {
const dataType = event.detail.dataType;
if (previousButtonHidden) {
tabPrice.removeAttribute('hide-previous-button');
previousButtonHidden = false;
}
if (dataType === 'data-7') {
const index = startIndex + 7;
startIndex = index > priceDates.length - 7
? priceDates.length - 7
: index;
if (index >= priceDates.length - 7) {
tabPrice.setAttribute('hide-next-button', '');
nextButtonHidden = true;
}
}
if (dataType === 'data-2') {
const index = startIndex + 2;
startIndex = index >= priceDates.length - 1
? priceDates.length - 2
: index;
if (index >= priceDates.length - 1) {
tabPrice.setAttribute('hide-next-button', '');
nextButtonHidden = true;
}
}
setData()
})
document.addEventListener('baPreviousPage', (event) => {
const dataType = event.detail.dataType;
if (nextButtonHidden) {
tabPrice.removeAttribute('hide-next-button');
nextButtonHidden = false;
}
if (dataType === 'data-7') {
const index = startIndex - 7;
startIndex = index <= 0
? 0
: index;
if (index <= 0) {
tabPrice.setAttribute('hide-previous-button', '');
previousButtonHidden = true;
}
}
if (dataType === 'data-2') {
const index = startIndex - 2;
startIndex = index < 0
? 0
: index;
if (index <= 0) {
tabPrice.setAttribute('hide-previous-button', '');
hidePreviousButton = true;
}
}
setData()
})
document.addEventListener('baMoreDatesToggled', (event) => {
const dataType = event.detail.dataType;
showMoreDates = dataType === 'data-7';
})
})()
</script>
Guidelines
The component accepts a data attribute which should contain the following properties within it.
- data-2: Data to be shown when in mobile
- data-7: Data to be shown in expanded mobile mode and desktop
As the component just ingests and displays the data, it is important to keep both sets of data up to date. This is incase the user switches between mobile and desktop views or toggles the view more date button.
Accessibility
The content below the component should update when a user selects a date. This content should have the following attributes added to it.
- role="tabpanel"
- aria-labelledby={the id of the selected tab}
This ensures that both the tabs and content are linked, which is useful for screen readers
| Element | State | Key press | Behaviour |
|---|---|---|---|
| Previous element in DOM | Previous element in DOM has focus | Tab |
<ba-tab-price-comparison> gets focus. |
<ba-tab-price-comparison> |
Arrow left button has focus | Tab. |
Moves focus to <ba-tab-price> element |
<ba-tab-price> |
<ba-tab-price> has focus |
Right arrow |
Moves focus to next <ba-tab-price> element |
<ba-tab-price> |
<ba-tab-price> has focus |
Left arrow |
Moves focus to previous <ba-tab-price> element |
<ba-tab-price> |
<ba-tab-price> has focus |
Home. |
Moves focus to the first <ba-tab-price> element |
<ba-tab-price> |
<ba-tab-price> has focus |
End. |
Moves focus to the last <ba-tab-price> element |
<ba-tab-price> |
<ba-tab-price> has focus |
Tab. |
Moves focus to <ba-tab-price-comparison> next button element |
<ba-tab-price-comparison> |
<ba-tab-price-comparison> has focus |
Tab. |
Moves focus to content below element. |
Code
Properties & Attributes
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
data |
data |
Data for the price comparison tab | TabPriceData | string | undefined |
undefined |
hideNextButton |
hide-next-button |
Whether to hide the next button | boolean | undefined |
false |
hidePreviousButton |
hide-previous-button |
Whether to hide the previous button | boolean | undefined |
false |
loading |
loading |
Whether to show the loading skeletons | boolean | undefined |
false |
No slotted content available for this component.
Usage
Basic usage
<ba-tab-price-comparison
id="priceComparison"
data="{
'data-2': [
{
date: '2025-10-16',
price: 'Sold out',
state: 'unavailable',
id: '2025-10-16'
}, {
date: '2025-10-17',
price: 'Sold out',
state: 'unavailable',
id: '2025-10-17'
}
],
'data-7': [
{
date: '2025-10-16',
price: 'Sold out',
state: 'unavailable',
id: '2025-10-16'
}, {
date: '2025-10-17',
price: 'Sold out',
state: 'unavailable',
id: '2025-10-17'
}, {
date: '2025-10-18',
price: 'Sold out',
state: 'unavailable',
id: '2025-10-18'
}, {
date: '2025-10-19',
price: '£144',
state: '',
id: '2025-10-19'
}, {
date: '2025-10-20',
price: '£176',
state: '',
id: '2025-10-20'
}, {
date: '2025-10-21',
price: '£176',
state: '',
id: '2025-10-21'
}, {
date: '2025-10-22',
price: '£148',
state: 'sale',
overline: 'Special offer',
id: '2025-10-22'
}]
}"
></ba-tab-price-comparison>
<ba-page-segment>
<div role="tabpanel" id="content" aria-labelledby="2025-10-16"></div>
</ba-page-segment>
<script>
const priceComparison = document.getElementById('priceComparison')
const content = document.getElementById('content')
priceComparison.addEventListener('baDateSelected', (event) => {
const tabId = event.detail.id
content.setAttribute("aria-labelledby", tabId)
})
</script>
Hiding the arrow buttons
<ba-tab-price-comparison
hide-previous-button
hide-next-button
></ba-tab-price-comparison>