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

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>

Related

Draftjs mentions plugin with scroll

The issue is keydown/keyup aren't working when mention list popup has scroll , i can scroll using mouse but keyup/keydown aren't making the scroll move to the right position
This can be achieved by custom entry Component ->
const entryComponent = (props:any) => {
const { mention, isFocused, searchValue, ...parentProps } = props;
const entryRef = React.useRef<HTMLDivElement>(null);
useEffect(() => {
if (isFocused) {
if (entryRef.current && entryRef.current.parentElement) {
entryRef.current.scrollIntoView({
block: 'nearest',
inline: 'center',
behavior: 'auto'
});
}}
}, [isFocused]);
return (
<>
<div
ref={entryRef}
role='option'
aria-selected={(isFocused ? 'true' : 'false')}
{...parentProps}>
<div className={'mentionStyle'}>
{mention.name}
</div>
</div>
</> );
};

Implement Aria 1.1

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.

In nextTick component is not redrawn

Please, look at my code, I assign a new value to the variable in data and var width have value 100. After that, when animation end, i try return value to var width 100, and start animation again, but Vue does not assign new value 100 and stay 0. But if i will do this with setTimeout it's work perfect. Why is this not happening in nextTick?
link to jsfiddle
new Vue({
el: '#app',
data: {
width: 100,
time: 0
},
mounted() {
setTimeout(() => {
this.time = 5000;
this.width = 0;
setTimeout(() => {
this.rerenderBar();
}, 5100)
}, 1000)
},
methods: {
rerenderBar() {
this.time = 0;
this.width = 100;
/* this.$nextTick(() => {
this.time = 5000;
this.width = 0;
}) */
setTimeout(() => {
this.time = 5000;
this.width = 0;
}, 1500)
}
}
})
<div id="app">
<div class="progress-bar-wrap">
<div class="progress-bar" :style="{
'width': width + '%',
'transition-duration': `${time}ms`
}"></div>
</div>
</div>
My guess is that because $nextTick runs after Vue's DOM update cycle and your animations are powered by css transitions directly on the element (not handled by Vue), the $nextTick happens immediately after calling renderBar It does not wait for your animation to complete.
If you need to wait for the animation to finish, you can look into using Vue Transitions and use Javascript Hooks to reset the width of the bar when the animation finishes.

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 to get click working in redux-react component with enzyme

Try to test click event to increase a value in the redux state. But the counter value is always 0.
Counter render
render() {
const { counter, label, isSaving, isLoading, error } = this.props
return <form>
<legend>{label}</legend>
<pre>{JSON.stringify({ counter, isSaving, isLoading }, null, 2)}</pre>
<button ref='increment' onClick={this._onClickIncrement}>click me!</button>
<button ref='save' disabled={isSaving} onClick={this._onClickSave}>{isSaving ? 'saving...' : 'save'}</button>
<button ref='load' disabled={isLoading} onClick={this._onClickLoad}>{isLoading ? 'loading...' : 'load'}</button>
{error ? <div className='error'>{error}</div> : null}
</form>
}
Jest Test
let container, store
beforeEach(() => {
store = mockStore(initialState)
container = shallow(<Counter label='a counter!' store={store} />)
})
it('+++ check Props after increments counter', () => {
const mockClick = jasmine.createSpy('click');
expect(container.find('button').at(0).type()).toEqual('button');
const increment = container.find('button').at(0)
container.find('button').at(0).simulate('click')
container.find('button').at(0).simulate('click')
container.find('button').at(0).simulate('click')
const pre = container.find('pre');
// const pre = TestUtils.findRenderedDOMComponentWithTag(counter, 'pre')
console.log(container.props().counter);
// expect(JSON.parse(counter.find('pre').first.text()).counter.value).toEqual(3)
})

Resources