diff --git a/backstop_data/bitmaps_reference/ds-vr-test__components_tabs_example-tabs-details_0_document_0_desktop.png b/backstop_data/bitmaps_reference/ds-vr-test__components_tabs_example-tabs-details_0_document_0_desktop.png index 3f65515856..6a1150e3ce 100644 --- a/backstop_data/bitmaps_reference/ds-vr-test__components_tabs_example-tabs-details_0_document_0_desktop.png +++ b/backstop_data/bitmaps_reference/ds-vr-test__components_tabs_example-tabs-details_0_document_0_desktop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec1d92de84e27d20c98eccf8f05dde05f6a67cb219f22a223712ee0adc84d4ec -size 1809732 +oid sha256:6c89c70befb878210223070cefdc2e62afae0ae6ae4a190d3478fbb32bccb7a0 +size 1809753 diff --git a/backstop_data/bitmaps_reference/ds-vr-test__components_tabs_example-tabs-details_0_document_1_tablet.png b/backstop_data/bitmaps_reference/ds-vr-test__components_tabs_example-tabs-details_0_document_1_tablet.png index 0fb27938c8..4d5c0c6ce5 100644 --- a/backstop_data/bitmaps_reference/ds-vr-test__components_tabs_example-tabs-details_0_document_1_tablet.png +++ b/backstop_data/bitmaps_reference/ds-vr-test__components_tabs_example-tabs-details_0_document_1_tablet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:84809b640b75e10f419064cffb694d8a56c5b889dc4d763779b0c233d80e0b29 -size 411453 +oid sha256:2d49008e94d98a327a8c5c87fc276c74b4398ad7e57581bfdc9c5e63791c1745 +size 411454 diff --git a/backstop_data/bitmaps_reference/ds-vr-test__components_tabs_example-tabs_0_document_0_desktop.png b/backstop_data/bitmaps_reference/ds-vr-test__components_tabs_example-tabs_0_document_0_desktop.png index 0ff6e9c17c..e1ac597a22 100644 --- a/backstop_data/bitmaps_reference/ds-vr-test__components_tabs_example-tabs_0_document_0_desktop.png +++ b/backstop_data/bitmaps_reference/ds-vr-test__components_tabs_example-tabs_0_document_0_desktop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:41caab6022a6dd6eb87cb09cbb39f1bf9977aafc1178781274bdaa9319c7281a -size 54367 +oid sha256:569ae69c7ba5f3d1d066ff2eae56a676b80e98f18239bb7041e74916b20713e5 +size 54456 diff --git a/backstop_data/bitmaps_reference/ds-vr-test__components_tabs_example-tabs_0_document_1_tablet.png b/backstop_data/bitmaps_reference/ds-vr-test__components_tabs_example-tabs_0_document_1_tablet.png index 8fef221423..70f7fc2e95 100644 --- a/backstop_data/bitmaps_reference/ds-vr-test__components_tabs_example-tabs_0_document_1_tablet.png +++ b/backstop_data/bitmaps_reference/ds-vr-test__components_tabs_example-tabs_0_document_1_tablet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:046d68407c91e414a728880f2c0c59c590826bbe376de996afda5ebdf4c6c160 -size 59734 +oid sha256:0f7a01166af61802227092ff43fbaa0179bbb2534d8efa3ceb68c8d8f3d5ca82 +size 59824 diff --git a/backstop_data/bitmaps_reference/ds-vr-test__components_timeout-panel_example-panel-with-timeout-warning_0_document_0_desktop.png b/backstop_data/bitmaps_reference/ds-vr-test__components_timeout-panel_example-panel-with-timeout-warning_0_document_0_desktop.png index 7f20b39e92..6ff63d5d1d 100644 --- a/backstop_data/bitmaps_reference/ds-vr-test__components_timeout-panel_example-panel-with-timeout-warning_0_document_0_desktop.png +++ b/backstop_data/bitmaps_reference/ds-vr-test__components_timeout-panel_example-panel-with-timeout-warning_0_document_0_desktop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:67986068c453ec5534fd2138bd3974e43a6b837e2603416495be0e48679a66d6 -size 17441 +oid sha256:47df53d9a4630fa991d39f9420d0920c814cbf2b78891da45b678b1f45452b04 +size 17486 diff --git a/backstop_data/bitmaps_reference/ds-vr-test__components_timeout-panel_example-panel-with-timeout-warning_0_document_1_tablet.png b/backstop_data/bitmaps_reference/ds-vr-test__components_timeout-panel_example-panel-with-timeout-warning_0_document_1_tablet.png index 38d3cec173..e57dee7bd7 100644 --- a/backstop_data/bitmaps_reference/ds-vr-test__components_timeout-panel_example-panel-with-timeout-warning_0_document_1_tablet.png +++ b/backstop_data/bitmaps_reference/ds-vr-test__components_timeout-panel_example-panel-with-timeout-warning_0_document_1_tablet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:255ba5261f88e83a8fb612ac518ba06379e3d196a20b65f9d17e2b856fe84124 -size 13594 +oid sha256:8cb52531d1d281043cf0541c3c8d2a5b93bbc00c3d2a60d1244b4949a954c265 +size 13505 diff --git a/backstop_data/bitmaps_reference/ds-vr-test__components_timeout-panel_example-panel-with-timeout-warning_0_document_2_mobile.png b/backstop_data/bitmaps_reference/ds-vr-test__components_timeout-panel_example-panel-with-timeout-warning_0_document_2_mobile.png index f0f681a56c..2695f0770a 100644 --- a/backstop_data/bitmaps_reference/ds-vr-test__components_timeout-panel_example-panel-with-timeout-warning_0_document_2_mobile.png +++ b/backstop_data/bitmaps_reference/ds-vr-test__components_timeout-panel_example-panel-with-timeout-warning_0_document_2_mobile.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f67e4ca5163a3770c17f4768fd548fc4e4ac0099d5cef4330b6176ee7c5174f2 -size 13805 +oid sha256:ca21587d7e4bf313e46414c7c484509716ee93ad9d66e7b02efc844b2757bb25 +size 13752 diff --git a/src/components/tabs/_macro-options.md b/src/components/tabs/_macro-options.md index dabb65d925..1dcddaf088 100644 --- a/src/components/tabs/_macro-options.md +++ b/src/components/tabs/_macro-options.md @@ -8,9 +8,10 @@ ## Tab -| Name | Type | Required | Description | -| ---------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -| id | string | false | Sets the HTML `id` of the tab | -| title | string | true | The title for the tab | -| hiddenSpan | string | false | Sets a visually hidden span after the title to distinguish the tab from others if multiple tabs with same title are displayed in the same page | -| content | string | true | The contents of the tab. This can contain HTML. | +| Name | Type | Required | Description | +| ---------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| id | string | false | Sets the HTML `id` of the tab | +| title | string | true | The title for the tab | +| showTitle | boolean | false | Sets an optional `h2` which will be displayed only in toc view and visually hidden in tab view. | +| hiddenSpan | string | false | Sets a visually hidden span after the title to distinguish the tab from others if multiple tabs with same title are displayed in the same page | +| content | string | true | The contents of the tab. This can contain HTML. | diff --git a/src/components/tabs/_macro.njk b/src/components/tabs/_macro.njk index d6c97441f8..87c9efadd4 100644 --- a/src/components/tabs/_macro.njk +++ b/src/components/tabs/_macro.njk @@ -12,17 +12,18 @@ > {% set titleTag = params.titleTag | default("h2") %} <{{ titleTag }} class="ons-tabs__title ons-u-fs-r--b ons-u-mt-no">{{ params.title }} - +
- +
{% for tab in params.tabs %}
+ {% if tab.showTitle %}

{{ tab.title }}

{% endif %} {{ tab.content | safe }}
- {% endfor %} + {% endfor %} {% endmacro %} diff --git a/src/components/tabs/_macro.spec.js b/src/components/tabs/_macro.spec.js index dff4c20f7d..c27745bda2 100644 --- a/src/components/tabs/_macro.spec.js +++ b/src/components/tabs/_macro.spec.js @@ -22,6 +22,23 @@ const EXAMPLE_TABS = { ], }; +const EXAMPLE_TABS_WITH_SHOWTITLE = { + title: 'Example tabs', + tabs: [ + { + id: 'first-tab', + title: 'Tab 1', + showTitle: true, + content: 'Example content...', + }, + { + id: 'second-tab', + title: 'Tab 2', + content: 'Some nested strong element...', + }, + ], +}; + const EXAMPLE_TABS_WITHOUT_TAB_IDS = { title: 'Example tabs', tabs: [ @@ -127,4 +144,10 @@ describe('macro: tabs', () => { .trim(), ).toBe('Some nested strong element...'); }); + + it('displays a h2 when showTitle set to true', () => { + const $ = cheerio.load(renderComponent('tabs', EXAMPLE_TABS_WITH_SHOWTITLE)); + + expect($('.ons-tabs__panel:first').find('h2').length).toBe(1); + }); }); diff --git a/src/components/tabs/_tabs.scss b/src/components/tabs/_tabs.scss index 4a5b9cc2b5..2c013b944b 100644 --- a/src/components/tabs/_tabs.scss +++ b/src/components/tabs/_tabs.scss @@ -11,24 +11,19 @@ margin: 0 0 1rem; overflow: visible; padding: 0; - + width: max-content; // Tabs &--row { margin: 0; position: relative; - - &::after { - background: var(--ons-color-borders); - bottom: 0; - box-shadow: 0 1px 0 0 var(--ons-color-page-light); - content: ''; - height: 1px; - left: 0; - position: absolute; - width: 100%; - } } } + &__container { + border-bottom: 1px solid var(--ons-color-borders); + margin: 0; + padding: 0; + z-index: -2; + } } .ons-tab__list-item { @@ -81,10 +76,12 @@ // Tab when selected &[aria-selected='true'] { background-color: var(--ons-color-page-light); - border-bottom: none; + border-bottom: 2px white; border-color: var(--ons-color-borders); border-radius: 3px 3px 0 0; + position: relative; text-decoration: none; + top: 1px; z-index: 1; &:focus { @@ -109,7 +106,7 @@ } &:focus { - box-shadow: 0 0 0 3px var(--ons-color-page-light), 0 0 0 5px var(--ons-color-text-link-focus), 0 0 0 8px var(--ons-color-focus); + box-shadow: 0 0 0 0 var(--ons-color-page-light), 0 0 0 2px var(--ons-color-text-link-focus), 0 0 0 6px var(--ons-color-focus); outline: 3px solid transparent; // Add transparent outline because Windows High Contrast Mode doesn't show box-shadows z-index: 1; } @@ -137,7 +134,6 @@ } .ons-tabs__list--row { - margin-bottom: -1px; padding: 0; } diff --git a/src/components/tabs/example-tabs.njk b/src/components/tabs/example-tabs.njk index 23be1a1139..0ad96765fc 100644 --- a/src/components/tabs/example-tabs.njk +++ b/src/components/tabs/example-tabs.njk @@ -7,9 +7,9 @@ { "id": "ukis", "title": 'UKIS', + "showTitle": true, "hiddenSpan": 'for UKIS', - "content": '

UKIS

-

Aim of this survey

+ "content": '

Aim of this survey

The aim of the UK Innovation Survey (UKIS) is to collect data from businesses about various aspects of their innovation related activities. Using this data we can measure the level, types and trends in innovation.

How we’ll use this data

The UKIS data is a major source of evidence to inform government policy. It is used to promote innovation activities among businesses to boost economic growth. It is an important contribution to the European-wide Community Innovation Survey (CIS). The CIS is used for international benchmarking and comparison purposes.

@@ -18,16 +18,16 @@ { "id": "vacancy-survey", "title": 'Vacancy survey', - "content": '

Vacancy survey

-

Purpose

+ "showTitle": true, + "content": '

Purpose

The Vacancy Survey is a regular survey of businesses, which provides an accurate and comprehensive measure of the total number of vacancies across the economy and fills a gap in the information available regarding the demand for labour. Before the Vacancy Survey was introduced, the only information available nationally about vacancies was from records of vacancies notified to Job Centres by employers. This provided only a partial picture, possibly less than half of all vacancies, because employers are under no obligation to notify vacancies to Job Centres. This business based survey has a more complete coverage and is included in the monthly ONS Labour Market Statistical Bulletin.

You can find more information on the Vacancy Survey on the ONS website.

' }, { "id": "monthly-business-survey", "title": 'Monthly Business Survey', - "content": '

Monthly Business Survey

-

Aim of this survey

+ "showTitle": true, + "content": '

Aim of this survey

The Monthly Business Survey (MBS) collects monthly information on employment of businesses in Great Britain. Your response contributes to Labour Market Statistics.

What you need to know

To complete the survey, you will need the following information to answer the survey questions:

diff --git a/src/components/tabs/tabs.js b/src/components/tabs/tabs.js index 0b14bff584..f8e2d6bf1c 100644 --- a/src/components/tabs/tabs.js +++ b/src/components/tabs/tabs.js @@ -10,7 +10,6 @@ const classTabTitle = 'ons-tabs__title'; const classTabList = 'ons-tabs__list'; const classTabListItems = 'ons-tab__list-item'; const classTabsPanel = 'ons-tabs__panel'; - const matchMediaUtil = matchMedia; export default class Tabs { @@ -22,6 +21,7 @@ export default class Tabs { this.tabsTitle = component.querySelector(`.${classTabTitle}`); this.tabs = [...component.getElementsByClassName(classTab)]; this.tabList = component.getElementsByClassName(classTabList); + this.tabListContainer = this.tabList[0].parentElement; this.tabListItems = [...component.getElementsByClassName(classTabListItems)]; this.tabPanels = [...component.getElementsByClassName(classTabsPanel)]; @@ -31,7 +31,6 @@ export default class Tabs { this.jsTabAsListClass = 'ons-tab--row'; this.noInitialActiveTab = this.component.getAttribute('data-no-initial-active-tab'); - if (matchMediaUtil.hasMatchMedia()) { this.setupViewportChecks(); } else { @@ -40,11 +39,22 @@ export default class Tabs { } // Set up checks for responsive functionality - // The tabs will display as tabs for >40rem viewports - // Tabs will display as a TOC list and show full content for <740px viewports - // Aria tags are added only for >740px viewports + // The tabs will display as tabs up until this.breakpoint is reached + // Tabs will display as a TOC list and show full content for { + let finalBreakpoint = 0; + this.tabListItems.forEach(tab => { + finalBreakpoint += tab.offsetWidth; + }); + if (finalBreakpoint < 450) { + return (finalBreakpoint = 450); + } else { + return finalBreakpoint; + } + }; + this.viewport = matchMediaUtil(`(min-width: ${breakpoint()}px)`); this.viewport.addListener(this.checkViewport.bind(this)); this.checkViewport(); } @@ -62,9 +72,12 @@ export default class Tabs { this.tabList[0].classList.add(this.jsTabListAsRowClass); this.tabsTitle.classList.add('ons-u-vh'); - + this.tabListContainer.classList.add('ons-tabs__container'); this.tabPanels.forEach(panel => { panel.setAttribute('tabindex', '0'); + if (panel.querySelector('[id*="content-title"]')) { + panel.firstElementChild.classList.add('ons-u-vh'); + } }); this.tabListItems.forEach(item => { @@ -96,11 +109,14 @@ export default class Tabs { makeList() { this.tabList[0].removeAttribute('role'); this.tabList[0].classList.remove(this.jsTabListAsRowClass); - + this.tabListContainer.classList.remove('ons-tabs__container'); this.tabsTitle.classList.remove('ons-u-vh'); this.tabPanels.forEach(panel => { panel.removeAttribute('tabindex', '0'); + if (panel.firstElementChild.classList.contains('ons-u-vh')) { + panel.firstElementChild.classList.remove('ons-u-vh'); + } }); this.tabListItems.forEach(item => { diff --git a/src/components/tabs/tabs.spec.js b/src/components/tabs/tabs.spec.js index a1753cda27..2bcb900077 100644 --- a/src/components/tabs/tabs.spec.js +++ b/src/components/tabs/tabs.spec.js @@ -9,6 +9,7 @@ const EXAMPLE_TABS = { { id: 'tab.id.1', title: 'Tab 1', + showTitle: true, content: 'First content...', }, { @@ -24,6 +25,38 @@ const EXAMPLE_TABS = { ], }; +const EXAMPLE_TABS_LONGER = { + title: 'Example tabs', + tabs: [ + { + id: 'tab.id.1', + title: 'Tab 1', + showTitle: true, + content: 'First content...', + }, + { + id: 'tab.id.2', + title: 'Tab 2', + content: 'Second content...', + }, + { + id: 'tab.id.3', + title: 'Tab 3', + content: 'Third content...', + }, + { + id: 'tab.id.4', + title: 'Tab 4', + content: 'Fourth content...', + }, + { + id: 'tab.id.5', + title: 'Tab 5', + content: 'Fifth content...', + }, + ], +}; + const EXAMPLE_TABS_WITH_NO_INITIAL_ACTIVE_TAB = { ...EXAMPLE_TABS, noInitialActiveTab: true, @@ -175,7 +208,7 @@ describe('script: tabs', () => { beforeEach(async () => { await page.emulate(puppeteer.devices['iPhone X']); - await setTestPage('/test', renderComponent('tabs', EXAMPLE_TABS)); + await setTestPage('/test', renderComponent('tabs', EXAMPLE_TABS_LONGER)); }); it('has no aria attributes on tabs', async () => { @@ -194,11 +227,16 @@ describe('script: tabs', () => { it('has no hidden tab panels', async () => { const panelCount = await page.$$eval('.ons-tabs__panel', nodes => nodes.length); - expect(panelCount).toBe(3); + expect(panelCount).toBe(5); const hiddenPanelCount = await page.$$eval('.ons-tabs__panel--hidden', nodes => nodes.length); expect(hiddenPanelCount).toBe(0); }); + + it('displays a h2 element with a unique id', async () => { + const panelCount = await page.$$eval('#tab-1-content-title', nodes => nodes.length); + expect(panelCount).toBe(1); + }); }); describe('when `data-no-initial-active-tab` is present', () => {