Working with multiple chainer results in cypress - asynchronous

Since Cypress can't easily use async/await, in order to use multiple chained values, right now I'm doing this:
const getScrubPosition = () => cy.getState().its('meta').its('scrubPosition');
const getScrollBar = () => cy.getByTestId('SCROLL_BAR').then(([bar]) => bar);
const getGutter = () => cy.getByTestId('GUTTER').then(([bar]) => bar);
// my workaround
const getScrubScrollGutter = () => getScrubPosition().then(initialPos =>
getScrollBar().then(([bar]) =>
getGutter().then(([gutter]) =>
({ initialPos, bar, gutter }))));
it('is ridiculous', () => {
getScrubPosition().then(initialPos => {
getScrollBarWidth().then(initialWidth => {
getScrollContainerWidth().then(containerWidth => {
// do something with initialPos, initialWidth, and containerWidth
});
});
});
it('>90 frames, with current series', () => {
clickSeries(1, 3); // x2x4, 94 frames, including pending
getScrubScrollGutter().then(({ initialPos, bar, gutter }) => {
expectPendingShown(true);
expectScrollBar();
// the bar shouldn't be wider than the gutter
expect(bar.offsetWidth).to.be.lessThan(gutter.offsetWidth);
// the right of the bar should be aligned with the right of the gutter
expect(rightEdgeOf(bar)).to.equal(rightEdgeOf(gutter));
debugger
// the bar shouldn't be to the left of the gutter
expect(bar.offsetLeft).to.be.greaterThan(gutter.offsetLeft);
});
There MUST be a better way to do this, right? The github issue mentioned above talks about things like
cy.get('something')
.then(x => doSomethingTo(x))
.then(y => console.log('please help'))
etc. But I'm having a hard time understanding it -- or, if I do understand it, it doesn't actually pass all the values along to the further contexts (i.e., I don't think you can use x in the third line of what I just wrote.)
What about Promise.all?
Yes, I tried that. Please only give a Promise.all related answer if you have actually been able to use it in Cypress.
Thanks.

Related

How do I override CSS custom properties in React?

I want to dynamically set the max-height of an element based on the number of children it has.
In my component, I am setting a custom property using:
document.documentElement.style.setProperty('--children-count', Children.count(props.children));
and in my css, I use it like this:
&.active {
.buttonGroup {
max-height: calc(var(--children-count) * 30px);
}
}
This works fine if I am only using it once. However, once I start loading multiple instances of the component with different number children, the --children-count gets overwritten and all the preceding components styles get changed.
How do I go around this?
The issue is most probably because you change the value of the --children-count CSS variable on the top documentElement, therefore affecting ALL your components in the document.
If you want 1 value per Component then simply apply it on an Element of said Component, see e.g. How to apply CSS variable on dynamic CSS class in dynamic component
If you use styled-components, you can also use style adaptation based on props (instead of CSS variables).
If you need to sync common data between multiple components, pull state upwards! In this case, into a context (unless if you want to manage it with props).
Here's a codesanbox: https://codesandbox.io/s/nervous-lalande-wjpdgb
The key parts of it are the Context Provider which will handle the shared state
function ChildCounterContextProvider({ children }) {
const [count, setCount] = useState(0);
const value = useMemo(() => ({ count, setCount }), [count])
useEffect(() => {
// Handle changes to number of children here
document.documentElement.style.setProperty('--children-count', count);
}, [count]);
return <ChildCounterContext.Provider value={value}>
{children}
</ChildCounterContext.Provider>
}
...and the context consumer, which will write to it.
const useChildCounter = () => useContext(ChildCounterContext);
function MyComponentWithChildren({ children }) {
const childrenCount = React.Children.count(children);
const childCounter = useChildCounter();
useEffect(() => {
// Add when enters
childCounter.setCount(n => n+childrenCount);
// Deduct when exits
return () => childCounter.setCount(n => n-childrenCount);
}, [childrenCount, childCounter]);
return <p>I have {childrenCount} children out of {childCounter.count}</p>
}
The effects will make sure the counts is always in sync.

How do I select my CSS module class using document.querySelector?

I want to be able to select styles.scrollValue using the document.querySelector()
import { useEffect } from "react";
import styles from "./Navbar.module.scss";
const Navbar = () => {
const handleScroll = () => {
document.querySelector(styles.scrollValue).innerHTML = scrollY;
};
useEffect(() => {
document.addEventListener("scroll", handleScroll);
return () => {
document.removeEventListener("scroll", handleScroll);
};
});
return (
<nav className={styles.navbar}>
<div className={styles.content}>
<h1 className={styles.scrollValue}>0</h1>
</div>
</nav>
);
};
Running this code I get an error:
TypeError: document.querySelector(...) is null
I know I can add a global class to that element doing this:
className={`${styles.scrollValue} scrollValue`}
But that would ruin the advantage of using CSS Modules.
Is there a way to select the CSS Module class without adding another one to it?
You need to select it as a class,
document.querySelector(`.${styles.scrollValue}`)
Calling styles.property returns a value such as the following,
scrollValue: '_src_styles_module__scrollValue'
So when you call document.querySelector it's looking for _src_styles_module__scrollValue which has no selector type such ., or # which makes it look for an element that would match <_src_styles_module__scrollValue> (in this case). Since there is no element with that name or the hashed name it will return null.
Working demo

Toggle flip cards in React

I'm trying to create a flip card effect where if I click on a card it flips over. However, if I then click on another card, I'd like the original card to flip back. This means having some sort of global toggle on all of the cards but one that gets overridden by the local toggle. I'm not sure of the most React-y way to do this - I've tried implementing it with the useState hook at both levels, but I'm not having much luck.
I'm using styled-components, with the 'flipped' prop determining the Y-transform.
Here's the flip card component, and you can see what I've tried so far:
const PortfolioItem = props => {
const [flipped, setFlipped] = useState(false)
return (
<PortfolioItemStyles onClick={() => setFlipped(!flipped)}>
// what I'm trying to say here is, if the individual card's 'flipped' is set to true,
use that, otherwise use props.flipped which will be set to false
<PortfolioItemInnerStyle flipped={flipped ? flipped : props.flipped}>
<PortfolioItemFront >
{props.image}
<PortfolioImageCover className="img-cover" />
<PortfolioItemHeader>{props.title}</PortfolioItemHeader>
</PortfolioItemFront>
<PortfolioItemBack>
<h1>Hello there</h1>
</PortfolioItemBack>
</PortfolioItemInnerStyle>
</PortfolioItemStyles>
)
}
function PortfolioStyles() {
const [ allFlipped, setAllFlipped ] = useState(false);
return (
<PortfolioContainer>
{portfolioItems.map(item => {
return <PortfolioItem image={item.image} flipped={allFlipped} title={item.title} onClick={() => setAllFlipped(false)} />
})}
</PortfolioContainer>
)
}
The logic I'm using is clearly faulty, but I was wondering what would be the 'best practice' way of doing this? In vanilla JS you'd use a single event handler and use event.target on it to make sure you were isolating the element, but I'm not sure how to handle this in React. Any help would be much appreciated.
I would personally manage the state only on the container component. Let's say you will store an index of the flipped card instead of a true/false status. The onClick would then change the current index and the flipped is computed by checking index === currentIndex. Something like this:
const PortfolioItem = props => {
return (
<PortfolioItemStyles>
<PortfolioItemInnerStyle flipped={props.flipped}>
<PortfolioItemFront >
{props.image}
<PortfolioImageCover className="img-cover" />
<PortfolioItemHeader>{props.title}</PortfolioItemHeader>
</PortfolioItemFront>
<PortfolioItemBack>
<h1>Hello there</h1>
</PortfolioItemBack>
</PortfolioItemInnerStyle>
</PortfolioItemStyles>
)
}
function PortfolioStyles() {
const [ currentFlippedIndex, setCurrentFlippedIndex ] = useState(-1);
return (
<PortfolioContainer>
{portfolioItems.map((item, index) => {
return <PortfolioItem image={item.image} flipped={index === currentFlippedIndex} title={item.title} onClick={() => setCurrentFlippedIndex(index)} />
})}
</PortfolioContainer>
)
}

What's the correct way to filter and keep the original results with Redux

I'm looking for the "correct" way to filter the original state.
State
{
searchText: '',
items: [
{name: 'Tom'},
{name: 'Larry'},
{name: 'Harry'}
]
}
Reducer
const filterText = action.text.toLowerCase();
const filteredResults = state.items.filter((item) => {
return item.name.toLowerCase().indexOf(filterText.toLowerCase()) > -1
})
const newState = { items: filteredResults , searchText: action.text }
return newState
Container
return this.props.people.items.map((person) => {
return(
<li
key={person.name}
onClick={() => this.props.selectPerson(person)}
>{person.name}</li>
)
})
However if I clear the text input I've obviously removed the items with .filter() I basically need on the keystroke to search all results but not remove from the original state.
I have a solution, and store an additional piece of data on the store and then add the results into the items array.
initialItems: [...],
items: [...]
However as mentioned I'm looking for the correct way to filter my results and I'm not sure if this is it.
Your solution is just one of the solutions to do this. IMO there are no "correct" ways because every implementation will have its pros and cons :)
However, a "cleaner" solution with eventually better performance results that comes to my mind is to use a selector in mapStateToProps which will filter out the items according to the searchText. In practice it would be just moving your code from the reducer to a selector.
const filterItems = state =>
state.items.filter(item =>
item.name.toLowerCase().indexOf(state.searchText.toLowerCase())
);
const mapStateToProps = state => ({
filteredItems: filterItems(state)
});
export default connect(mapStateToProps)(YourComponent);
You could do this even better by using reselect - https://github.com/reactjs/reselect

Telerik Grid ASP.NET MVC2 missing last column separator

My grid has 5 data columns and 1 command column (update/delete)
the column separator between command column and last data column is missing, making everything shift when entering inline edit mode.
Any ideas what am I doing wrong ?
this is the grid's view code:
<%=
Html.Telerik().Grid<ActivityPOCO>()
.Name("ActivityGrid")
.DataKeys(dataKeys =>
{
dataKeys.Add(e => e.ActivityID);
}
.ToolBar(commands => commands.Insert().ButtonType(GridButtonType.Image))
.DataBinding(dataBinding =>
{
dataBinding.Ajax() //Ajax binding
.Select("SelectActivityGridAjax", "Activity")
.Insert("InsertActivityGridAjax", "Activity")
.Update("UpdateActivityGridAjax", "Activity"
.Delete("DeleteActivityGridAjax", "Activity");
})
.Columns(c =>
{
c.Bound(o => o.ActivityID).Title("ID").ReadOnly(true).Width(30);
c.Bound(o => o.ActivityName).Title("NAME").Width(130);
c.Bound(o => o.ActivityTimeHours).Title("TIME").Width(50);
c.Bound(o => o.Cost).Title("COST").Width(100);
c.Bound(o => o.WarrentyMonths).Title("WARRENTY");
c.Command(commands =>
{
commands.Edit().ButtonType(GridButtonType.Image);
commands.Delete().ButtonType(GridButtonType.Image); ;
}).Width(180).Title("COMMAND");
}).Editable(editing => editing.Mode(GridEditMode.InLine))
%>
Well, it looks like a problem in the RTL CSS:
I've changed the .t-last-header{border-right-width:1;} from 0 and got the separator.
Still looking for answer for the grid rows, especially in edit mode...

Resources