@patternfly/elements
Version:
PatternFly Elements
293 lines • 15.3 kB
JavaScript
import { expect, html, nextFrame } from '@open-wc/testing';
import { createFixture } from '@patternfly/pfe-tools/test/create-fixture.js';
import { a11ySnapshot } from '@patternfly/pfe-tools/test/a11y-snapshot.js';
import { setViewport, sendKeys } from '@web/test-runner-commands';
import { allUpdates } from '@patternfly/pfe-tools/test/utils.js';
import { PfTabs } from '../pf-tabs.js';
import { PfTab } from '../pf-tab.js';
import { PfTabPanel } from '../pf-tab-panel.js';
import '@patternfly/pfe-tools/test/stub-logger.js';
function press(key) {
return async function () {
await sendKeys({ press: key });
};
}
describe('<pf-tabs>', function () {
it('instantiates imperatively', function () {
expect(document.createElement('pf-tabs')).to.be.an.instanceof(PfTabs);
expect(document.createElement('pf-tab')).to.be.an.instanceof(PfTab);
expect(document.createElement('pf-tab-panel')).to.be.an.instanceof(PfTabPanel);
});
it('should upgrade', async function () {
const el = await createFixture(html `<pf-tabs></pf-tabs>`);
expect(el, 'pf-tabs should be an instance of PfeTabs')
.to.be.an.instanceOf(customElements.get('pf-tabs'))
.and
.to.be.an.instanceOf(PfTabs);
});
describe('with three valid tab pairs', function () {
let element;
const updateComplete = () => allUpdates(element);
beforeEach(async function () {
element = await createFixture(html `
<pf-tabs>
<pf-tab slot="tab">tab-1</pf-tab>
<pf-tab-panel>panel-1</pf-tab-panel>
<pf-tab slot="tab">tab-2</pf-tab>
<pf-tab-panel>panel-2</pf-tab-panel>
<pf-tab slot="tab">tab-3</pf-tab>
<pf-tab-panel>panel-3</pf-tab-panel>
</pf-tabs>
`);
});
beforeEach(updateComplete);
it('should apply aria attributes on initialization', function () {
const tabs = element.querySelectorAll('pf-tab');
const panels = element.querySelectorAll('pf-tab-panel');
tabs.forEach(function (tab, index) {
const tabId = tab.getAttribute('id');
const tabControls = tab.getAttribute('aria-controls');
panels.forEach(function (panel, pindex) {
if (index === pindex) {
expect(panel.getAttribute('aria-labelledby')).to.equal(tabId);
expect(panel.id).to.equal(tabControls);
}
});
});
});
describe('pressing Tab', function () {
beforeEach(press('Tab'));
beforeEach(updateComplete);
beforeEach(nextFrame);
it('should activate the first focusable tab', function () {
expect(element.querySelector('pf-tab')).to.have.attribute('active');
});
it('should activate the first tab panel', function () {
expect(element.querySelector('pf-tab')).to.not.have.attribute('hidden');
});
});
describe('setting the `vertical` attribute', function () {
beforeEach(function () {
element.setAttribute('vertical', '');
});
beforeEach(updateComplete);
it('should have vertical styles', function () {
// WARNING: asserting on shadow root content;
const tabs = element.shadowRoot.querySelector('[part="tabs"]');
const tabsVerticalStyles = getComputedStyle(tabs).flexDirection;
expect(tabsVerticalStyles).to.be.equal('column');
});
});
describe('setting the second tab\'s `active` attribute', function () {
beforeEach(function () {
const [, tab] = element.querySelectorAll('pf-tab');
tab.active = true;
});
beforeEach(updateComplete);
beforeEach(nextFrame);
it('should activate the second tab', function () {
const [, tab] = element.querySelectorAll('pf-tab');
expect(tab).to.have.attribute('active');
});
it('should activate its panel', function () {
const [, tab] = element.querySelectorAll('pf-tab');
expect(tab).to.not.have.attribute('hidden');
});
it('should deactivate previously active tab', function () {
expect(element.querySelector('pf-tab')).to.not.have.attribute('active');
});
it('should hide previously active panel', function () {
expect(element.querySelector('pf-tab-panel')).to.have.attribute('hidden');
});
});
describe('setting `activeIndex` to 2', function () {
beforeEach(function () {
element.activeIndex = 2;
});
beforeEach(updateComplete);
beforeEach(nextFrame);
it('should activate the third tab', function () {
const [, , tab] = element.querySelectorAll('pf-tab');
expect(tab).to.have.attribute('active');
});
it('should activate the third panel', async function () {
const snapshot = await a11ySnapshot();
expect(snapshot.children?.find(x => x.role === 'tabpanel')?.name).to.equal('tab-3');
});
describe('then setting the first tab\'s `disabled` attribute', function () {
beforeEach(async function () {
element.querySelector('pf-tab').disabled = true;
});
beforeEach(updateComplete);
it('should disable the button', async function () {
const snapshot = await a11ySnapshot();
const disabledTab = snapshot.children?.find(x => x.role === 'tab' && x.disabled);
expect(disabledTab).to.be.ok;
});
describe('and clicking the disabled tab', function () {
beforeEach(async function () {
element.querySelector('pf-tab').click();
});
beforeEach(updateComplete);
it('should not activate', function () {
const [first] = element.querySelectorAll('pf-tab');
expect(first).to.not.have.attribute('active');
});
it('should present the third panel to the ax tree', async function () {
const snapshot = await a11ySnapshot();
expect(snapshot.children?.find(x => x.role === 'tabpanel')?.name).to.equal('tab-3');
});
});
describe('then setting `activeIndex` to 0 (i.e. the disabled tab\'s index)', function () {
beforeEach(function () {
element.activeIndex = 0;
});
beforeEach(updateComplete);
it('should not activate the first tab', function () {
expect(element.querySelector('pf-tab')).to.not.have.attribute('active');
});
it('should present the third panel to the ax tree', async function () {
const snapshot = await a11ySnapshot();
expect(snapshot.children?.find(x => x.role === 'tabpanel')?.name).to.equal('tab-3');
});
});
});
});
describe('when viewed in a small viewport', function () {
beforeEach(async function () {
// this is a comically narrow viewport, but our tabs have quite short
// labels, so viewport must be itsy-pitsy in order to cause overflow
await setViewport({ width: 100, height: 640 });
});
beforeEach(nextFrame);
beforeEach(nextFrame);
beforeEach(nextFrame);
beforeEach(updateComplete);
it('should have visible scroll buttons if overflowed', function () {
// Note: overflow buttons are not included in the accessibility tree otherwise we'd test
// for buttons there. tabindex="-1" is used on the buttons to prevent focus and was a
// decision made to keep logical keyboard navigation order flow between tabs and panels
// as the next overflow button exists in the DOM between the tabs container and the open panel
// and would disrupt the expected flow. For keyboard users they are able to scroll using the
// left and right arrows keys and do not need direct access to the overflow buttons but still
// exist as visual cues for which direction is overflowed
const previousTab = element.shadowRoot.querySelector('#previousTab');
const nextTab = element.shadowRoot.querySelector('#nextTab');
expect(previousTab).to.not.be.null;
expect(nextTab).to.not.be.null;
const prevDisplayStyle = getComputedStyle(previousTab).display;
const nextDisplayStyle = getComputedStyle(nextTab).display;
expect(prevDisplayStyle).to.not.equal('none');
expect(nextDisplayStyle).to.not.equal('none');
});
});
describe(`when navigated by keyboard`, function () {
describe('when focused on the first tab', function () {
beforeEach(async function () {
element.querySelector('pf-tab').focus();
});
describe('pressing ArrowRight', function () {
beforeEach(async function () {
await sendKeys({ down: 'ArrowRight' });
});
beforeEach(updateComplete);
it('should activate the next tab', function () {
const [first, second, third] = element.querySelectorAll('pf-tab');
expect(first).to.not.have.attribute('active');
expect(second).to.have.attribute('active');
expect(third).to.not.have.attribute('active');
});
it('should specify the selected tab to assistive technology', async function () {
expect(await a11ySnapshot()).to.axContainQuery({ role: 'tabpanel', name: 'tab-2' });
});
});
describe('pressing ArrowLeft', function () {
beforeEach(async function () {
await sendKeys({ down: 'ArrowLeft' });
});
it('should activate the last tab', function () {
const [first, second, third] = element.querySelectorAll('pf-tab');
expect(first).to.not.have.attribute('active');
expect(second).to.not.have.attribute('active');
expect(third).to.have.attribute('active');
});
it('should specify the selected tab to assistive technology', async function () {
expect(await a11ySnapshot()).to.axContainQuery({ role: 'tabpanel', name: 'tab-3' });
});
describe('then pressing ArrowRight', function () {
beforeEach(async function () {
await sendKeys({ down: 'ArrowRight' });
});
beforeEach(updateComplete);
it('should activate the first tab', function () {
const [first, second, third] = element.querySelectorAll('pf-tab');
expect(first).to.have.attribute('active');
expect(second).to.not.have.attribute('active');
expect(third).to.not.have.attribute('active');
});
it('should specify the selected tab to assistive technology', async function () {
expect(await a11ySnapshot()).to.axContainQuery({ role: 'tabpanel', name: 'tab-1' });
});
});
});
});
});
describe('setting the `manual` attribute', function () {
beforeEach(function () {
element.setAttribute('manual', '');
});
beforeEach(updateComplete);
describe('pressing Tab', function () {
beforeEach(press('Tab'));
describe('pressing ArrowRight key', function () {
beforeEach(press('ArrowRight'));
beforeEach(updateComplete);
it('should not activate second tab', function () {
const [first, second, third] = element.querySelectorAll('pf-tab');
expect(first).to.have.attribute('active');
expect(second).to.not.have.attribute('active');
expect(third).to.not.have.attribute('active');
});
it('should focus the second tab', function () {
const [, second] = element.querySelectorAll('pf-tab');
expect(document.activeElement).to.equal(second);
});
describe('pressing enter key', function () {
beforeEach(press('Enter'));
beforeEach(updateComplete);
it('should activate second tab', function () {
const [first, second, third] = element.querySelectorAll('pf-tab');
expect(first).to.not.have.attribute('active');
expect(second).to.have.attribute('active');
expect(third).to.not.have.attribute('active');
expect(document.activeElement).to.equal(second);
});
});
});
});
});
});
describe('when no active tab is given and the first tab is disabled', function () {
let element;
beforeEach(async function () {
element = await createFixture(html `
<pf-tabs>
<pf-tab id="tab1" slot="tab" disabled></pf-tab>
<pf-tab id="tab2" slot="tab"></pf-tab>
<pf-tab id="tab3" slot="tab"></pf-tab>
<pf-tab id="tab4" slot="tab" disabled></pf-tab>
<pf-tab id="tab5" slot="tab" aria-disabled="true"></pf-tab>
<pf-tab-panel></pf-tab-panel>
<pf-tab-panel></pf-tab-panel>
<pf-tab-panel></pf-tab-panel>
<pf-tab-panel></pf-tab-panel>
<pf-tab-panel></pf-tab-panel>
</pf-tabs>
`);
});
it('should activate the next focusable tab', function () {
expect(element.activeTab).to.have.id('tab2');
});
});
});
//# sourceMappingURL=pf-tabs.spec.js.map