Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

shadowDOM accessible text calculation #420

Merged
merged 7 commits into from
Jul 17, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/commons/dom/find-elms-in-context.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* global axe, dom */
/**
* Find a elements reference from a given context
* Find elements referenced from a given context
*
* @param object {
* context: Node | virtual node Element in the same context
Expand Down
9 changes: 6 additions & 3 deletions lib/commons/text/accessible-text.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ function formValueText({ actualNode }) {
return '';
}

/**
* Get the accessible text of first matching node
* IMPORTANT: This method does not look at the composed tree
*/
function checkDescendant({ actualNode }, nodeName) {
var candidate = actualNode.querySelector(nodeName.toLowerCase());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we put a note in here to indicate that this function is intended to be used only on elements that cannot contain a shadow root?

if (candidate) {
Expand Down Expand Up @@ -153,9 +157,8 @@ text.accessibleText = function(element, inLabelledByContext) {
if (!phrasingElements.includes(actualNode.nodeName.toUpperCase())) {
returnText += ' ';
}
returnText += accessibleNameComputation(
child, inLabelledByContext, inControlContext
);
returnText += accessibleNameComputation(child, inLabelledByContext,
inControlContext);
}
return returnText;
}, '');
Expand Down
16 changes: 14 additions & 2 deletions test/checks/keyboard/focusable-no-name.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ describe('focusable-no-name', function () {

var fixture = document.getElementById('fixture');
var fixtureSetup = axe.testUtils.fixtureSetup;
var shadowSupport = axe.testUtils.shadowSupport;

afterEach(function () {
fixture.innerHTML = '';
Expand All @@ -26,16 +27,27 @@ describe('focusable-no-name', function () {
assert.isTrue(checks['focusable-no-name'].evaluate(node));
});

it('should fail if element is tabbable with no name - ARIA', function () {
it('should fail if element is tabable with no name - ARIA', function () {
fixtureSetup('<span tabindex="0" role="link" href="#"></spam>');
var node = fixture.querySelector('span');
assert.isTrue(checks['focusable-no-name'].evaluate(node));
});

it('should pass if the element is tabbable but has an accessible name', function () {
it('should pass if the element is tabable but has an accessible name', function () {
fixtureSetup('<a href="#" title="Hello"></a>');
var node = fixture.querySelector('a');
assert.isFalse(checks['focusable-no-name'].evaluate(node));
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a test where when an anchor has content inside the shadow root, it returns false. Test this for slotted content as well as fallback content inside the default slot

Copy link
Contributor

@dylanb dylanb Jul 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@WilcoFiers Can you add one more

		var node = document.createElement('a');
		var shadow = node.attachShadow({ mode: 'open' });
		shadow.innerHTML = '<slot>Fallback content</slot>';
		fixtureSetup(node);

		assert.isFalse(checks['focusable-no-name'].evaluate(node));


(shadowSupport.v1 ? it : xit)('should pass if the content is passed in with shadow DOM', function () {
var node = document.createElement('div');
node.innerText = 'Content!';
var shadow = node.attachShadow({ mode: 'open' });
shadow.innerHTML = '<a href="#"><slot></slot></a>';
fixtureSetup(node);

var link = shadow.querySelector('a');
assert.isFalse(checks['focusable-no-name'].evaluate(link));
});

});
18 changes: 17 additions & 1 deletion test/checks/tables/same-caption-summary.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ describe('same-caption-summary', function () {

var fixture = document.getElementById('fixture');
var fixtureSetup = axe.testUtils.fixtureSetup;
var shadowSupport = axe.testUtils.shadowSupport;

afterEach(function () {
fixture.innerHTML = '';
Expand Down Expand Up @@ -37,8 +38,23 @@ describe('same-caption-summary', function () {
var node = fixture.querySelector('table');

assert.isTrue(checks['same-caption-summary'].evaluate(node));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should catch the following situation

        <div class="table">
            <span slot="caption">Caption</span>
            <span slot="one">Data element 1</span>
            <span slot="two">Data element 2</span>
        </div>
        <script>
            function createContentTable() {
                var group = document.createElement('table');
                group.innerHTML = '<caption><slot name="caption"></slot></caption>' +
                    '<tr><td><slot name="one"></slot></td><td><slot name="two"></slot></td></tr>';
                group.setAttribute('summary', 'Caption');
                return group;
            }

            function makeShadowTreeTable(node) {
                var root = node.attachShadow({mode: 'open'});
                root.appendChild(createContentTable());
            }

            document.addEventListener('DOMContentLoaded', function() {
                document.querySelectorAll('.table').forEach(makeShadowTreeTable);
            });
        </script>


});

(shadowSupport.v1 ? it : xit)('should match slotted caption elements', function () {
var node = document.createElement('div');
node.innerHTML = '<span slot="caption">Caption</span>' +
'<span slot="one">Data element 1</span>' +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@WilcoFiers indentation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's an indentation feature ;)

'<span slot="two">Data element 2</span>';

var root = node.attachShadow({ mode: 'open' });
var table = document.createElement('table');
table.innerHTML = '<caption><slot name="caption"></slot></caption>' +
'<tr><td><slot name="one"></slot></td><td><slot name="two"></slot></td></tr>';
table.setAttribute('summary', 'Caption');
root.appendChild(table);
fixtureSetup(node);

assert.isTrue(checks['same-caption-summary'].evaluate(table));
});

});
38 changes: 36 additions & 2 deletions test/commons/text/accessible-text.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ describe('text.accessibleText', function() {
'</fieldset>';
axe._tree = axe.utils.getFlattenedTree(fixture);

// var rule2a = axe.utils.querySelectorAll(axe._tree, '#beep')[0];
var rule2a = axe.utils.querySelectorAll(axe._tree, '#beep')[0];
var rule2b = axe.utils.querySelectorAll(axe._tree, '#flash')[0];
// assert.equal(axe.commons.text.accessibleText(rule2a), 'Beep');
assert.equal(axe.commons.text.accessibleText(rule2a), 'Beep');
assert.equal(axe.commons.text.accessibleText(rule2b), 'Flash the screen 3 times');
});

Expand Down Expand Up @@ -413,6 +413,40 @@ describe('text.accessibleText', function() {
assert.equal(axe.commons.text.accessibleText(target), 'ARIA Label');
});

(shadowSupport.v1 ? it : xit)('should find attributes within a shadow tree', function() {
fixture.innerHTML = '<div id="shadow"></div>';

var shadow = document.getElementById('shadow').attachShadow({ mode: 'open' });
shadow.innerHTML = '<input type="text" id="t1" title="I will be king">';

axe._tree = axe.utils.getFlattenedTree(fixture);
var target = axe.utils.querySelectorAll(axe._tree, 'input')[0];
assert.equal(axe.commons.text.accessibleText(target), 'I will be king');
});

(shadowSupport.v1 ? it : xit)('should find attributes within a slot on the shadow tree', function() {
fixture.innerHTML = '<div id="shadow"><input type="text" id="t1" title="you will be queen"></div>';

var shadow = document.getElementById('shadow').attachShadow({ mode: 'open' });
shadow.innerHTML = '<slot></slot>';

axe._tree = axe.utils.getFlattenedTree(fixture);
var target = axe.utils.querySelectorAll(axe._tree, 'input')[0];
assert.equal(axe.commons.text.accessibleText(target), 'you will be queen');
});

(shadowSupport.v1 ? it : xit)('should find fallback content for shadow DOM', function() {
fixture.innerHTML = '<div id="shadow"></div>';

var shadow = document.getElementById('shadow').attachShadow({ mode: 'open' });
shadow.innerHTML = '<input type="text" id="t1">' +
'<label for="t1"><slot>Fallback content heroes</slot></label>';

axe._tree = axe.utils.getFlattenedTree(fixture);
var target = axe.utils.querySelectorAll(axe._tree, 'input')[0];
assert.equal(axe.commons.text.accessibleText(target), 'Fallback content heroes');
});

describe('figure', function() {

it('should check aria-labelledby', function() {
Expand Down