I am using the Chrome toolbar from http://wave.webaim.org/extension/ to check the ADA compliance of my React-Bootstrap app.
When I use a Popover within an OverlayTrigger without an ID, it warns me in the console:
Warning: Failed propType: The prop 'id' is required to make 'Popover' accessible for users using assistive technologies such as screen readers
Problem is, when I add an ID to the Popover, I then get the following error on my accessibility scan:
Broken ARIA reference: An element has an aria-labelledby or aria-describedby value that does not match the id attribute value of another element in the page.
I am guessing it's happening because the element with that ID doesn't exist until the button is clicked. Am I missing something, or is this element not ADA compliant? Or, is this just a problem with the scan, and there's a better tool I should be using to prove my site is compliant?
Here is the code for an example site that demonstrates the issue. I have thrown it in a Fiddle, but it won't do you much good because if you run the accessibility tool on that, it will give you JSFiddle's errors rather than the ones for the relevant code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>React-Bootstrap Popover Accessibility</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.5/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.5/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-bootstrap/0.28.1/react-bootstrap.js"></script>
<link type="text/css" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"/>
</head>
<body>
<div id="container"></div>
<script type="text/babel">
var Button = ReactBootstrap.Button;
var OverlayTrigger = ReactBootstrap.OverlayTrigger;
var Popover = ReactBootstrap.Popover;
var Overlay = React.createClass({
render: function() {
return (
<OverlayTrigger trigger="click" placement="right" overlay={
<Popover title="Popover" id="popover-id">Here's the contents of the popover</Popover>
}>
<Button bsStyle="primary">Click to see Popover</Button>
</OverlayTrigger>
);
}
});
ReactDOM.render(
<Overlay />,
document.getElementById('container')
);
</script>
</body>
</html>
I can confirm that the code is not compliant. You can double-check whether this code validates by:
Inspecting this element in the developer console (before the button is clicked)
Copying the rendered HTML to the clipboard
Loading http://validator.nu and selecting the ‘Textfield’ option
Pasting your HTML between the <body></body>tags
Clicking ‘Validate’
As you’ll see, the code does not validate, because, as oobgam mentioned, the target ID is not initially present in the DOM.
There are a number of different approaches to fixing this. Once I understand which design pattern you’re trying to accessibly support, I can provide more concrete advice.
Can you please provide more information about why you chose this implementation? How do you see desktop and mobile users interacting with this, and to what end?
Quora has a good list of related patterns at What's the difference between a modal, a popover and a popup?
Related
So I was using react-bootstrap dropdown menu and i found a bug that it doesn't close .It actually renders all its options horizontally on my screen.
I searched over Stackoverflow .There were few resembling posts but none of them is for react
<DropdownButton
title={this.state.selectedOption}
id="document-type"
onSelect={this.handleSelect.bind(this)}
>
{myoptions.map((opt, i) => (
<Dropdown.Item key={i} eventKey={i}>
{opt}
</Dropdown.Item>
))}`enter code here`
</DropdownButton>
I expected Nomral Behavior
As per docs,
Dropdowns are toggleable, contextual overlays for displaying lists of links and more. Like overlays, Dropdowns are built using a third-party library Popper.js, which provides dynamic positioning and viewport detection.
Also you need to add bootstrap.css, either by adding link in index.html
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous"
/>
or by importing in index.js file.
Add popper in index.html
<script
src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
crossorigin="anonymous"
></script>
Demo
Basically I have an angular project that is based on bootstrap 3 for media queries resize.
What I intend to do is allow user to change between desktop mode (laptop full screen width) or mobile mode (media width < 667px).
I have see a lot of theme preview sites have this feature. As it is quite a common feature, I expect that it can be done this way but not sure how it could be implemented exactly.
Note: I am not expecting to change any part of existing CSS.
My opinion on how to implement this.
<html ng-viewport="deviceWidth">
<button ng-click="changeDeviceWidth()">
</html>
// initial
$scope.deviceWidth = getDeviceWidthFunction();
$scope.changeDeviceWidth = function (deviceWidth) {
$scope.deviceWidth = deviceWidth;
}
First: Give id to you viewport meta
<meta id="viewport" name="viewport" content="width=device-width, initial-scale=1">
Second: Create selector buttons
<div id="selecter">
<button onclick="toDesktop()"> Desktop</button>
<button onclick="toTablet()"> Tablet</button>
<button onclick="toMobile()"> Mobile</button>
</div>
Third: Add iframe as content viewer
<iframe id="mycontent" src="http://www.majali.net"></iframe>
Fourth: Add JS functions to set viewport,content width and height
<script>
function toDesktop() {
document.getElementById("viewport").setAttribute("content", "width=1200");
document.getElementById("mycontent").style.width='100%';
document.getElementById("mycontent").style.height='600px';
}
function toMobile() {
document.getElementById("viewport").setAttribute("content", "width=340");
document.getElementById("mycontent").style.width='320px';
document.getElementById("mycontent").style.height='480px';
}
function toTablet() {
document.getElementById("viewport").setAttribute("content", "width=767");
document.getElementById("mycontent").style.width='767px';
document.getElementById("mycontent").style.height='600px';
}
</script>
I'm using bootstrap and angular to build an application. In a HTML page, i'm using this:
<div class="btn-group-justified" data-toggle="buttons-checkbox">
<button type="button" class="btn fzbtn-consServices" ng-repeat="service in availability.services">{{service.name}}</button>
</div>
It's building a button group with dynamic values. Is there a practical way to obtain the selected buttons inside this button group?
I already tried some solutions, some of them are working but I don't know if it's the best way...
1: On "ng-click" method I would change a attribute value (eg. "checked" attribute) of each service to true or false;
2: I searched about any html attribute for btn-group which could offer me all the selected buttons inside this group, but i had no success;
3: I heard that i could beat this problem using Angular Filter, but i didn't find any similar example;
Anyone with a better idea? Thanks so much :)
This is the best solution I found until now:
<div class="btn-group-justified" data-toggle="buttons-checkbox">
<button type="button" class="btn fzbtn-consServices" ng-repeat="service in availability.services" ng-click="onClickService(service)" ng->{{service.name}}</button>
</div>
Controller:
$scope.onClickService = function (service) {
if (service.checked) {
service.checked = "false"
} else {
service.checked = "true";
}
}
Answer: Bootstrap UI
I feel your pain. Bootstrap is not always angular-friendly. But there is a good solution:
The easiest (and by far the cleanest) approach is to use Bootstrap UI. Built by the Angular Team, it is a rewrite of the javascript-portion of Bootstrap but for an Angular-friendly usage. Here's the section about buttons: http://angular-ui.github.io/bootstrap/#/buttons
Example solution: Checkbox button behavior
In this solution, the initial services array is used to store a boolean field 'selected' to know if any particular service is selected or not. (Similar to the "checked" in the question). This field is 2-way bounded to the checkbox state. Clicking the checkbox changes the field and changing the field changes the checkbox state.
var app = angular.module('plunker', ['ui.bootstrap']);
app.controller('MainCtrl', function($scope) {
var services = [
{ name:'Service A' },
{ name:'Service B', selected:true },
{ name:'Service C' },
{ name:'Service D', selected:true }
];
$scope.availability = { services:services };
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS BootStrap UI radios</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.2.js"></script>
<script src="app.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body ng-controller="MainCtrl">
<div class="btn-group-justified">
<label ng-repeat="service in availability.services" class="btn btn-primary" ng-model="service.selected" btn-checkbox>{{service.name}}</label>
</div>
<div ng-repeat="service in availability.services">{{service.name}} <span ng-show="service.selected">- selected</span></div>
</body>
</html>
Radio button behavior
I've included a solution for a "single selection" checkbox also known as a "radio-button". The "current selection" is bound to a variable on the scope. It will get updated automatically when the user picks an element. Setting it will, in turn, change the current selection.
var app = angular.module('plunker', ['ui.bootstrap']);
app.controller('MainCtrl', function($scope) {
var services = [
{ name:'Service A' },
{ name:'Service B' },
{ name:'Service C' },
{ name:'Service D' }
];
$scope.availability = { services:services };
$scope.model = {};
// Here we are using the service object instance as the "selection value".
// Depending on what you need, you could also use some sort of identifier or
// even the $index if that's more useful.
// Immediately select the second one.
$scope.model.selectedService = services[1];
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS BootStrap UI radios</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.2.js"></script>
<script src="app.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body ng-controller="MainCtrl">
<div class="btn-group-justified">
<label ng-repeat="service in availability.services" class="btn btn-primary" ng-model="model.selectedService" btn-radio="service">{{service.name}}</label>
</div>
<div>Current Selection is: {{model.selectedService.name}}</div>
</body>
</html>
NOTE: I used <label> instead of <button>, but I did not have your special CSS styles so it wasn't behaving on the screen, functionality-wise it works equally well with <button> elements.
You don't really specify why you are wanting to "get" the buttons. In general, getting a reference to DOM elements like this is not the "angular way." It is generally better to find a way to use angular's data-binding to manipulate UI elements.
For instance, if you want to show or hide buttons based on data or on another UI event, then use ng-show or ng-hide bound to a property of the service object you are binding these buttons to. Then you can update the object's property to change the UI. You should be able to find a similar way to make other changes (like setting classes, attributes, etc.) with angular's data-binding rather than doing it manually.
I am running into an issue in my backbone/underscore application. Our site uses recaptcha and we want to put that content inside a view. We are using underscore for templates. How would i put the recaptcha code inside a template? THe problem is there are scripts tags required for recaptcha and it collides with the underscore script tag. For example it would look something like this:
<script type="text/javascript" id="someTemplate">
<div>
some html here
</div>
<script type="text/javascript" src="https://www.google.com/recaptcha/api/challengek=YOUR_PUBLIC_KEY"
</script>
any help is appreciated. Thanks!
Underscore does not prevent you from using script tags, your problems come from your template declaration : you use type="text/javascript" which means your browser tries to interpret your template as Javascript and you get erratic results.
Try
<script type="text/template" id="someTemplate">
<div><%= text %></div>
<script type="text/javascript"
src="https://www.google.com/recaptcha/api/challengek=<%= key %>"
/>
</script>
and a demo http://jsfiddle.net/VxjBs/
As you noted in the comments, Recaptcha tries to loads a second script via document.write and fails when inserted in the DOM (see Can't append <script> element for a probable explanation).
Your best bet is probably to go through Recaptcha Ajax API, generate your HTML, identify a node and apply Recaptcha.create on it. Something like
<script type="text/template" id="someTemplate">
<div><%= text %></div>
<div class='recaptcha'></div>
</script>
The basis for a view could be
var html = _.template(src, {
text: 'in div'
});
var $el = $('#render').append(html);
$el.find('.recaptcha').each(function(idx, el) {
Recaptcha.create(
pubkey,
el
);
});
http://jsfiddle.net/nikoshr/VxjBs/2/
Not sure what all the reCAPTCHA script does, but I'm assume it tries to append HTML right after itself. If that's the case then you will probably need to manually attach a script node to the view after you've rendered it and then set the src of the script node to the URL of the external javascript file.
You cannot put script tags inside an underscore template as the browser will only parse the outer-most script tag (your template).
The proposed solution is too complicated.
This can be achieved very easily as follows (in fact I've just implemented it in my project).
Make sure to include the recaptcha js file in the "head" element of your page as follows:
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
Add this function in your javascript somewhere.
var render_recaptcha = function(target_id) {
grecaptcha.render(target_id, {
'sitekey' : RECAPTCHA_SITE_KEY
});
};
Then, just call this function after you render your template:
<script type="text/template" id="someTemplate">
<div><%= text %></div>
<div id='recaptcha'></div>
</script>
//render template like you usually would
//...
//then render the recaptcha
render_recaptcha('recaptcha');
That's it.
I'm trying to use dojo's dialog box in a page in my application but having some problems with the page in IE7 (or in IE 8 in some cases, when the page is viewed in compatibility mode).
Following is a rough skeleton structure of the page i'm trying to write.
<head>
<style type="text/css">
body, html { font-family:helvetica,arial,sans-serif; font-size:90%; }
</style>
<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dojo/dojo.xd.js"
djConfig="parseOnLoad: true">
</script>
<script type="text/javascript">
dojo.require("dijit.Dialog");
var secondDlg;
dojo.addOnLoad(function() {
var foo = new dijit.Dialog({id:'testDialog', title: "test dialog", content: "test content" }, dojo.byId('dialog1Container'));
foo.startup();
var foo2 = new dijit.Dialog({id:'testDialog2', title: "test dialog 2", content: "test content 2" }, dojo.byId('dialog2Container'));
foo2.startup();
});
wrapper = function() {
dijit.byId('testDialog').show();
}
</script>
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dijit/themes/claro/claro.css"
/>
</head>
<body class=" claro ">
<p>
When pressing this button the dialog will popup. Notice this time there
is no DOM node with content for the dialog:
</p>
<a onClick="wrapper();"> Show the test dialog</a>
<br />
<a onClick="dijit.byId('testDialog2').show();"> Show the test dialog</a>
<div id="dialog1Container"></div>
<div id="dialog2Container"></div>
</body>
In IE7, the page just hangs when it tries to display the dialog box from the 1st link.
Here are some symptoms of the malady ailing this page:
It breaks if there are more than 1 Dijit.dialog in the dom. If there is only 1, then it works fine
If there are more than 1 dialogs instances in the dom, only the last one works correctly. All previous ones end up freezing the browser.
The work around i'm using is to dynamically create an instance of dijit.Dialog in my js and insert it into the dom container and hitch a custom method to hide it. And when i'm hiding it i also call destroy on the dialog so it removes the dialog from the dom. This allows me to have multiple places in my page which can use the dialog, but only 1 will be displayed and be present in the dom at any point of time
And some extra info:
The html doc type i'm using is DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd
It works fine in FF, Chrome and IE 8, only breaks in IE 7
I'm using dojo 1.5 (not the one from google's site, but a copy from my server, but i cant put that in the sample code)
Anybody have any idea about dojo's dialog having problems in IE 7?
Turns out this wasn't a problem with the dijit Dialogs itself, there was an event handler (not connected to dojo at all) which was causing the error, which is why it wasn't reproducible in a standalone page.