Implement Aria 1.1 - wai-aria

I am trying to implement the example 1 provided by w3c.org. The URL is https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html. It keeps giving me aria undefined error on line (var ex1Combobox = new aria.ListboxCombobox). Here is the code:
<!DOCTYPE html>
<html>
<body>
<label for="ex1-input"
id="ex1-label"
class="combobox-label">
Choice 1 Fruit or Vegetable
</label>
<div class="combobox-wrapper">
<div role="combobox"
aria-expanded="false"
aria-owns="ex1-listbox"
aria-haspopup="listbox"
id="ex1-combobox">
<input type="text"
aria-autocomplete="list"
aria-controls="ex1-listbox"
id="ex1-input">
</div>
<ul aria-labelledby="ex1-label"
role="listbox"
id="ex1-listbox"
class="listbox hidden">
</ul>
</div>
<script>
/*
* This content is licensed according to the W3C Software License at
* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
*
* ARIA Combobox Examples
*/
var FRUITS_AND_VEGGIES = [
'Apple',
'Artichoke',
'Asparagus',
'Banana',
'Beets',
'Bell pepper',
'Broccoli',
'Brussels sprout',
'Cabbage',
'Carrot',
'Cauliflower',
'Celery',
'Chard',
'Chicory',
'Corn',
'Cucumber',
'Daikon',
'Date',
'Edamame',
'Eggplant',
'Elderberry',
'Fennel',
'Fig',
'Garlic',
'Grape',
'Honeydew melon',
'Iceberg lettuce',
'Jerusalem artichoke',
'Kale',
'Kiwi',
'Leek',
'Lemon',
'Mango',
'Mangosteen',
'Melon',
'Mushroom',
'Nectarine',
'Okra',
'Olive',
'Onion',
'Orange',
'Parship',
'Pea',
'Pear',
'Pineapple',
'Potato',
'Pumpkin',
'Quince',
'Radish',
'Rhubarb',
'Shallot',
'Spinach',
'Squash',
'Strawberry',
'Sweet potato',
'Tomato',
'Turnip',
'Ugli fruit',
'Victoria plum',
'Watercress',
'Watermelon',
'Yam',
'Zucchini'
];
function searchVeggies (searchString) {
var results = [];
for (var i = 0; i < FRUITS_AND_VEGGIES.length; i++) {
var veggie = FRUITS_AND_VEGGIES[i].toLowerCase();
if (veggie.indexOf(searchString.toLowerCase()) === 0) {
results.push(FRUITS_AND_VEGGIES[i]);
}
}
return results;
}
/**
* #function onload
* #desc Initialize the combobox examples once the page has loaded
*/
window.addEventListener('load', function () {
var ex1Combobox = new aria.ListboxCombobox(
document.getElementById('ex1-combobox'),
document.getElementById('ex1-input'),
document.getElementById('ex1-listbox'),
searchVeggies,
false
);
var ex2Combobox = new aria.ListboxCombobox(
document.getElementById('ex2-combobox'),
document.getElementById('ex2-input'),
document.getElementById('ex2-listbox'),
searchVeggies,
true
);
var ex3Combobox = new aria.ListboxCombobox(
document.getElementById('ex3-combobox'),
document.getElementById('ex3-input'),
document.getElementById('ex3-listbox'),
searchVeggies,
true,
function () {
// on show
document.getElementById('ex3-combobox-arrow')
.setAttribute('aria-label', 'Hide vegetable options');
},
function () {
// on hide
document.getElementById('ex3-combobox-arrow')
.setAttribute('aria-label', 'Show vegetable options');
}
);
document.getElementById('ex3-combobox-arrow').addEventListener(
'click',
function () {
if (ex3Combobox.shown) {
document.getElementById('ex3-input').focus();
ex3Combobox.hideListbox();
}
else {
document.getElementById('ex3-input').focus();
ex3Combobox.updateResults(true);
}
}
);
});
</script>
</body>
</html>
Any help would be appreciated.

I realize that this is an old post, but I have determined why the undefined error is occurring:
There are two js files associated with this example:
ListBox-Combox.js &
ListBox-Combo-example.js
The 'ListBox-Combo-example.js' file has event listeners for all three examples on the page
https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html.
Since you only copied the code for the first example, when the javascript attempts to find the combobox 'ex2-Combobox' it cannot find it so javascript throws an error.
You can comment out these lines in the 'ListBox-Combo-example.js' file:
);
var ex2Combobox = new aria.ListboxCombobox(
document.getElementById('ex2-combobox'),
document.getElementById('ex2-input'),
document.getElementById('ex2-listbox'),
searchVeggies,
true
);
var ex3Combobox = new aria.ListboxCombobox(
document.getElementById('ex3-combobox'),
document.getElementById('ex3-input'),
document.getElementById('ex3-listbox'),
searchVeggies,
true,
and replace with a comma. That should solve the problem.

Related

How not to get NAN error in x-text of alpinejs

Here I want to get the value of the gap property, but it shows the NAN error!
<div x-data="tamrin()">
<h1 x-text="gap"></h1>
<script>
function tamrin() {
return {
countTo: new Date("May 30, 2021 00:00:00").getTime(),
now: new Date().getTime(),
gap: this.countTo - this.now,
}
}
</script>
</div>
You are returning an object literal, so the this there refers to the global window, not to that object literal.
One option would be to first create the countTo and now variables and then the object with the gap property:
window.MyComponent = function() {
console.log(`this === window = ${ this === window }`);
const countTo = new Date('May 30, 2021 00:00:00').getTime();
const now = new Date().getTime();
return {
countTo,
now,
gap: countTo - now,
};
};
<div x-data="MyComponent()">
<span x-text="gap"></span>
</div>
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/dist/alpine.min.js"></script>
Another option would be to use a getter and turn gap into a computed property so that this inside that getter will refer to your component instance:
window.MyComponent = () => ({
countTo: new Date('May 30, 2021 00:00:00').getTime(),
now: new Date().getTime(),
get gap() {
console.log(`this === window = ${ this === window }`);
return this.countTo - this.now;
},
});
<div x-data="MyComponent()">
<span x-text="gap"></span>
</div>
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/dist/alpine.min.js"></script>
Edit:
If you want to continuously refresh the displayed value, use the second solution (getter) and update the now property using setInterval:
window.MyComponent = () => ({
countTo: new Date('May 30, 2021 00:00:00').getTime(),
now: Date.now(),
init() {
setInterval(() => {
requestAnimationFrame(() => {
this.now = Date.now();
});
}, 200);
},
get gap() {
return `${ Math.round(this.countTo - this.now / 1000) } seconds left.`;
},
});
<div x-data="MyComponent()" x-init="init">
<span x-text="gap"></span>
</div>
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/dist/alpine.min.js"></script>

Expected null to be truthy. Jasmine / Karma

I'm trying to test my component injected into a mock class I created. Although the component works when I try to test its existence it returns null.
Injectable Component:
import { Injectable, ElementRef, Renderer2, RendererFactory2 } from '#angular/core';
#Injectable()
export class NgBackdropComponent {
private renderer: Renderer2;
private appElementRef: ElementRef;
message: string = 'Carregando...';
constructor(rendererFactory: RendererFactory2) {
this.renderer = rendererFactory.createRenderer(null, null);
this.appElementRef = new ElementRef(<Element>document.getElementsByTagName('body').item(0));
}
show() {
const divSpinnerItem1 = this.renderer.createElement('i');
const divSpinnerItem2 = this.renderer.createElement('i');
const divSpinnerItem3 = this.renderer.createElement('i');
const divSpinner = this.renderer.createElement('div');
this.renderer.addClass(divSpinner, 'spinner');
this.renderer.appendChild(divSpinner, divSpinnerItem1);
this.renderer.appendChild(divSpinner, divSpinnerItem2);
this.renderer.appendChild(divSpinner, divSpinnerItem3);
const spanMensagem = this.renderer.createElement('span');
spanMensagem.innerHTML = this.message;
const div = this.renderer.createElement('div');
this.renderer.addClass(div, 'lock-content');
this.renderer.appendChild(div, divSpinner);
this.renderer.appendChild(div, spanMensagem);
this.renderer.appendChild(this.appElementRef.nativeElement, div);
}
hide() {
const elemento = this.appElementRef.nativeElement.querySelector('.lock-content');
if (elemento) {
elemento.remove();
}
}
}
my testing environment:
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { NgBackdropComponent } from './ng-backdrop.component';
import { Component } from '#angular/core';
import { By } from '#angular/platform-browser';
#Component({
template: `
<button (click)="clickButton()"></button>
`
})
class MockNgBackdropComponent {
constructor(private backdrop: NgBackdropComponent) { }
clickButton() {
this.backdrop.message = 'Teste BackDrop aesdas';
this.backdrop.show();
console.log('iniciei backdrop');
}
closeBackdrop() {
this.backdrop.hide();
}
}
describe('NgBackdropComponent', () => {
let component: MockNgBackdropComponent;
let fixture: ComponentFixture<MockNgBackdropComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MockNgBackdropComponent],
providers: [NgBackdropComponent]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MockNgBackdropComponent);
component = fixture.componentInstance;
});
describe('Deve injetar', async () => {
it('Deve ter uma div principal', function () {
const btnClick = fixture.nativeElement.querySelector('button');
btnClick.click();
fixture.detectChanges();
const el = fixture.nativeElement.querySelector('.lock-content');
console.log(el);
expect(el).toBeTruthy();
});
});
});
In testing I create a Mock class where I inject my component.
I do not understand why it can not find the class because it exists.
The reason you can't find it in the component is because you did not create it in the component. If you look at this line in your constructor:
this.appElementRef = new ElementRef(<Element>document.getElementsByTagName('body').item(0))
You are creating it on the document directly in the <body> element. If you search for that in your spec, you will find it there. I created a STACKBLITZ to show you what I mean. Here is the spec from that stackblitz:
it('Deve ter uma div principal', () => {
const btnClick = fixture.nativeElement.querySelector('button');
console.log(btnClick);
btnClick.click();
fixture.detectChanges();
const appElementRef = new ElementRef(<Element>document.getElementsByTagName('body').item(0));
const el = appElementRef.nativeElement.querySelector('.lock-content');
expect(el).toBeTruthy();
});
Adding a little more clarification:
If you console.log(appElementRef) you'll notice that its tagName is body, and note the contents of its nativeElement.innerHTML Here is what that would look like "prettyfied":
<body>
<div class="jasmine_html-reporter">
<div class="jasmine-banner"><a class="jasmine-title" href="http://jasmine.github.io/" target="_blank"></a><span
class="jasmine-version">3.3.0</span></div>
<ul class="jasmine-symbol-summary"></ul>
<div class="jasmine-alert"></div>
<div class="jasmine-results">
<div class="jasmine-failures"></div>
</div>
</div>
<div id="nprogress" style="transition: none 0s ease 0s; opacity: 1;">
<div class="bar" role="bar" style="transform: translate3d(0%, 0px, 0px); transition: all 200ms ease 0s;">
<div class="peg"></div>
</div>
</div>
<div id="root0" ng-version="7.0.1">
<button></button>
</div>
<div class="lock-content">
<div class="spinner">
<i></i>
<i></i>
<i></i>
</div>
<span>Teste BackDrop aesdas</span>
</div>
</body>
Note how the button was created within the div with id="root0"? However, the div with class="lock-content" was created right off the root <body> element, and therefore is not within the div of the component.
In fact, you can see this very clearly when you console.log(fixture.nativeElement) and see that the tagName is "div", its innerHTML is <button></button>, and it has two attributes: id: "root0" and ng-version: "7.0.1". Put that all together and it looks like this:
<div id="root0" ng-version="7.0.1">
<button></button>
</div>
So you can clearly see that you cannot find the div you created in the component because you created it outside the component.
I hope this helps.
I think you should use DebugElement, for example:
it('Deve ter uma div principal', function () {
const btnClick = fixture.debugElement.query(By.css('button'));
btnClick.click();
fixture.detectChanges();
const el = fixture.debugElement.query(By.css('.lock-content'));
console.log(el);
expect(el).toBeTruthy();
});
Follow this link for more information.

How can you reuse a common overlay trigger

I've got a field with dynamic values in it. Testing I posted about 500 buttons. Since each element currently gets it's own OverlayTrigger and Tooltip. The results of actually clicking on of the elements is super slow.
The Tooltip elements have only 2 unique elements. Which are "Click to enable" or "Click to disable". So I should only need 2 OverlayTriggers as well. Right now for each of the 500 buttons there are 500 different OverlayTriggers and 500 different Tooltips.
Is there a way to reduce this down to 2 OverlayTriggers and 2 Tooltips? Then add the unique buttons to the appropriate OverlayTrigger? Or should I come at this problem in a different direction?
Here's some code:
var bsstyle = this.props.enabled ? this.props.enableBsStyle : this.props.disableBsStyle;
var toolTipContent = `Click to ${this.props.enabled ? "disable" : "enable"}.`;
var toolTipId = `toolTip.${this.props.value}`;
const tooltip = (
<Tooltip id={toolTipId}>{toolTipContent}</Tooltip>
);
return (
<OverlayTrigger
placment={this.props.placement}
overlay={tooltip}
>
<Button
bsStyle={bsstyle}
bsSize="xs"
onClick={this.props.onClick}
>
{this.props.value}
</Button>
</OverlayTrigger>
Here's some testing code:
import React from 'react';
import ReactDom from 'react-dom';
import { Row, Col, Tooltip, OverlayTrigger, Button } from 'react-bootstrap';
import Formsy from 'formsy-react';
var values = [];
for (i=0; i < 500; i++){values.push({value: i, enabled: i % 2 === 0});}
const RemoveableLabel = React.createClass({
getDefaultProps: function (){
return {
placment: "bottom",
enableBsStyle: "primary",
disableBsStyle: "default",
enabled: true
};
},
render: function () {
var bsstyle = this.props.enabled ? this.props.enableBsStyle : this.props.disableBsStyle;
var toolTipContent = `Click to ${this.props.enabled ? "disable" : "enable"}.`;
var toolTipId = `toolTip.${this.props.value}`;
const tooltip = (
<Tooltip id={toolTipId}>{toolTipContent}</Tooltip>
);
return (
<OverlayTrigger placment={this.props.placement} overlay={tooltip}>
<Button bsStyle={bsstyle} bsSize="xs" onClick={this.props.onClick}>{this.props.value}</Button>
</OverlayTrigger>
);
},
});
const RemoveableLabelCollection = React.createClass({
mixins: [Formsy.Mixin],
handleClick: function(index){
var values = this.getValue();
var indexValue = values[index];
var enabled = indexValue.enabled ? false : true;
indexValue.enabled = enabled;
values[index] = indexValue;
this.setValue(values);
},
render: function () {
var self = this;
var labels = this.getValue().map(function (item, i) {
var boundClick = self.handleClick.bind(self, i)
return (
<RemoveableLabel key={i + self.props.name + item} name={self.props.name + item} value={item.value} enabled={item.enabled} onClick={boundClick}/>
);
});
return (
<Row>
<Col sm={12}>
{labels}
</Col>
</Row>
);
},
});
const View = React.createClass({
getInitialState: function(){return {labels: values, canSubmit: false};},
onInvalid: function () {
this.setState({ canSubmit: false });
},
onValid: function() {
this.setState({ canSubmit: true });
},
onSubmit: function(data) {
alert(JSON.stringify(data, null, 4));
this.setState({labels: []});
this.refs.form.reset();
},
render: function() {
var clsName = this.props.className;
return (
<Form ref="form" onSubmit={this.onSubmit} onValid={this.onValid} onInvalid={this.onInvalid} className={clsName}>
</Grid>
<Row>
<Col sm={12}>
<RemovableLabelCollection name="labels" value={this.state.labels}/>
</Col>
</Row>
</Grid>
</Form>
);
}
});
ReactDom.render(<View />, document.getElementById('root'));

how to make angular filters customizable and how to use them in javascript

I have an array on my controller
$scope.arr = [......], a
nd in html I want to do
ng-repeat = "item in arr | color:'blue'" //this line works, filter done in the app.filter way.
where color is an attribute of all objects in arr.
How Do I make this color:'blue' customizable and sub in color:'red'?
also if I want to do controller filtering instead of html, what would be the syntax, right now I have
$scope.filteredArr = $filter('color')($scope.arr,'blue');
which is giving error
http://jsfiddle.net/x3azn/YpMKX/
posted fiddle, please remove -1
You can customize color:'blue' with any expression in the format filter:expression, so something like color:myColor would work fine provided a color filter has been defined and myColor exists in the current scope.
In your controller, you can do the same.
$scope.filteredArr = $filter('color')($scope.arr,myColor);
Here is an example based on your jsFiddle example.
Javascript:
angular.module('app', [])
.filter('eyecolor', function () {
return function ( people, color ) {
var arr = [];
for ( var i = people.length; i--; ) {
if ( people[i].eyeColor === color ) {
arr.push( people[i] );
}
}
return arr;
}
})
.controller('ctrl', function ($scope, $filter) {
$scope.desiredColor = 'Brown';
$scope.people = [
{
name: 'Bob',
eyeColor: 'Brown'
}, {
name: 'Katherine',
eyeColor: 'Yellow'
}, {
name: 'Chun',
eyeColor: 'Black'
}
];
$scope.peopleFilter = $filter('eyecolor')( $scope.people, $scope.desiredColor );
});
Html:
<div ng-app="app">
<div ng-controller="ctrl">Color Desired:
<input ng-model="desiredColor" /><br/>
<div ng-repeat="person in people | eyecolor: desiredColor">
HTML filter: {{person.name}} has eye color {{person.eyeColor}}</div>
<div ng-repeat="person in peopleFilter">
Controller filter: {{ person.name }} has eye color {{ person.eyeColor }}</div>
</div>
</div>

knockout.js - add/remove items from child list of model

I have a foreach template displaying a list of jobs on a page. Inside each job I have a list of job notes. I'm having trouble setting up the proper observable to allow for the job notes to be updated in the view.
Here is a link to a jsFiddle: http://jsfiddle.net/b3B8p/
Or, here is the code as it stands. I'm not sure whether to put the observable array of the sublist into the Job object itself or into the View...
<div data-bind="foreach: jobs">
<div>
<div class="jobContainer">
<label data-bind="text: JobTitle"></label>
<label data-bind="text: CompanyName"></label>
<div class="jobDetails" data-bind="visible: expanded">
<label data-bind="text: City"></label>
<label data-bind="text: State"></label>
<div data-bind="foreach: Notes">
<label data-bind="text: Text"></label>
</div>
</div>
<div>
Expand
Add Note
</div>
</div>
function JobNote() {
this.Text = "some text!";
}
function Job() {
this.JobTitle = ko.observable("some job");
this.CompanyName = ko.observable("some company");
this.City = ko.observable("some city");
this.State = ko.observable("some state");
this.Notes = ko.observableArray([
new JobNote(),
new JobNote(),
]);
this.someValue = ko.observable().extend({ defaultIfNull: "some default" });
this.expanded = ko.observable(false);
this.linkLabel = ko.computed(function () {
return this.expanded() ? "collapse" : "expand";
}, this);
};
var viewModel = function () {
this.jobs= ko.observableArray([
new Job(),
new Job(),
new Job(),
new Job(),
new Job(),
new Job(),
new Job(),
new Job(),
new Job(),
new Job(),
]);
this.toggle = function (item) {
item.expanded(!item.expanded());
}
this.addNote = function(job) {
// what should go in here?
}
};
ko.applyBindings(new viewModel());​
As Niko said in this scenario I would add the method to the class. Like so.
function Job() {
var self = this;
this.JobTitle = ko.observable("some job");
this.CompanyName = ko.observable("some company");
this.City = ko.observable("some city");
this.State = ko.observable("some state");
this.Notes = ko.observableArray([
new JobNote(),
new JobNote(),
]);
this.someValue = ko.observable().extend({ defaultIfNull: "some default" });
this.expanded = ko.observable(false);
this.linkLabel = ko.computed(function () {
return this.expanded() ? "collapse" : "expand";
}, this);
this.toggle = function () {
self.expanded(!self.expanded());
};
this.addNote = function () {
self.Notes.push(new JobNote());
}
};
This keeps everything nice and encapsulated.
Hope this helps.

Resources