I know that react uses its own synthetic implementation of events. However it appears to me that they are not exactly like standard html and this is a problem. In my case I have a checkbox that is a sibling of some img tags. There are two img tags, one for representing checked and another unchecked. I have some css styling that does a display none when the checkbox is in an unchecked state. Trying to get this html/css working with react is hard. It appears that the click event is not propagating onto the checkbox sibling with a react component, although it works from standard html. Note as there's some confusion I know that the non-react version of this is using css and not events. But I'm trying to implement a react evented version of the same thing, and was expecting normal html event propagation behavior--which I'm not seeing. Also note the checkbox is a sibling of the label. It's also transparent, so the user never actually clicks on the checkbox they click on the img tags.
.checkbox-image input[type="checkbox"] + label img.selected {
display: none;
}
.checkbox-image input[type="checkbox"] + label img.unselected {
display: block;
}
<div class="checkbox-image"><input id="portfolio-standard-dev" type="checkbox" data-name="PortfolioStandardDeviation"><label for="mp-chart3"><img class="unselected" src="/images/img-843599.png"><img class="selected" src="/images/img-1b9f30.png"><span>Portfolio Standard Deviation</span></label></div>
CSS has nothing to do with events. An event propagation cannot impact your styles.
You have to handle when the user check/uncheck the checkbox and manually update the class of the image.
Or you can try the following pure CSS:
.checkbox-image input[type="checkbox"]:checked + label img {
display: none;
}
.checkbox-image input[type="checkbox"] + label {
display: block;
}
Related
I need to test a popover from chakra-ui in a React App.
I tried with this. But It does not find the popover. If I try by text, then I cannot assert if it is visible.
it('show a popover when hover terms and conditions', () => {
render(<SummaryForm />);
const link = screen.getByText(/terms and conditions/i);
const popover = screen.getByRole('dialog');
expect(popover.parentNode).not.toBeVisible();
userEvent.click(link);
expect(popover.parentNode).toBeVisible();
});
Try using the hidden option of the API:
const popover = screen.getByRole('dialog', {hidden: true})
ChakraUI renders a wrapper div around the section that has the dialog role. You can see this by using screen.debug() if you are using the testing-library. Notice the wrapper controls the visibility of said section, which starts as hidden, with styling elements and aria tags.
Using the hidden option allows you to look amognst the elements that aren't visible in the accessibility tree.
Since you want to test the popover, you should know there are some issues with modifying and checking the visibility of the popover when using jest-dom.
The chakra Modal applies a transform style to toggle its visibility. toBeVisible only checks a limited set of style attributes - transform is not one of them - so you might have to check for those instead, for example:
For invisibility:
expect(screen.getByRole('dialog')).toHaveStyle('transform: translateX(0px) translateY(0.18967%) translateZ(0);')
try toBeInTheDocument() or toMatchSnapshot()
I am using React dropzone for file upload
<DropZone
accept='.pdf,.pptx,.ppt,.docx,.doc,.xls,.xlsx,.xslx,.png,.xsl,.jpg,.jpeg,.gif,.zip'
onDrop={ files => {
this.handleFileDrop(files);
this.dragLeaveHandler();
} }
onDragEnter={ this.dragOverHandler }
onDragLeave={ this.dragLeaveHandler }
multiple={ false }
style={ { height: '100%' } }
>
dragOverHandler = () => {
console.log('enter');
this.setState({
isDragOver: true,
});
};
dragLeaveHandler = () => {
console.log('exit');
this.setState({
isDragOver: false,
});
};
When a file is moving above the drop zone onDragLeave event fires simultaneously.
Should I use some other events?
How can I fix this issue?
You could use pointer-events: none; on the element(s) that are firing the drag leave. That should still allow the dropped event and getting the accepted file though would stop overriding the dropzone events.
The problem you're facing is most likely caused by the DOM events dragEnter and dragLeave getting messed up instead of any flaw in the react-dropzone package. Some elements may cause hovering over them in certain positions not to register as hovering over their parent element. For example, there is a thin sliver at the top edge of any plain string rendered inside a block displayed element. Most commonly this happens inside a <p> tag.
Without seeing the children rendered inside your dropzone, it is impossible to give a specific fix. Generally, you will have to mess with the styling of the children, though. <p> tags for example will not be a problem if their size is set to 0 pixels or if they're replaced with <span> tags. Both options will disrupt the displaying of the children, which is unfortunatley unavoidable.
As for using other events, you're out of luck. The DropZone component relies on the onDragEnter and onDragLeave HTML DOM events. Therefore any fix you might come up with won't fix the component itself.
All in all, it's an unfortunate issue that just has to be dealt with. The simplest way to deal with it is to just have at most one piece of text inside the dropzone and to set its size to 0 pixels with css: height: 0px;. Regular <div> elements won't cause issues, so you can craft an intricate dropzone using them.
I am running into an issue where part of my component within the modal generated by lightning:overlayLib requires the user to scroll. I want the div that appears to display over the top of the footer component. Is this possible?
Current issue:
If I disable the two overflow attributes in css on the slds-modal__content (shown below):
I get the desired result:
BUT, I cannot for the life of me, get this to work. When I pass in the modified css class slds-modal__content, it fills the screen with white. (When I set overflow: visible)
I am generating the component using the lightning:overlayLib using this code:
var modalHeader, modalBody, modalFooter;
$A.createComponents([
["c:UWB_modalHeader",{'label':'Approve'}],
["c:UWB_utilityModal",{'modalName':'approve', 'approvalId':data.proccessInstanceToPiwi[data.approvalHistory[0].ProcessInstanceId], 'relatedObject':recordData}],
["c:UWB_modalFooter",{'cancelLabel':'Cancel', 'submitLabel':'Approve'}]
],
function(components, status){
if (status === "SUCCESS") {
modalHeader=components[0];
modalBody = components[1];
modalFooter = components[2];
component.find('overlayLib').showCustomModal({
header: modalHeader,
body: modalBody,
footer: modalFooter,
showCloseButton: false,
cssClass: 'slds-modal__content'
})
}
}
);
Where the css class 'slds-modal__content' is as follows:
.THIS .slds-modal__content{
overflow-y: visible !important;
overflow-x: visible !important;
}
Even after attempting to modify the class, the issue still persists. I have been successful with this method generating a standard modal without using lightning:overlayLib, but I'm not able to generate the modal in this way.
I've just recently started using lightning:overlayLib myself. I didn't have the issue you're having, but I realized that since the body and footer components require application events to communicate, the feature of declaring a footer component that requires communication with the body component at all is too cumbersome to be worth it.
I would recommend not using the footer component feature of lightning:overlayLib at all, and just put the buttons you need in the body component.
I have to create some complex animations. It was cool to develop them with jQuery or VanillaJS, but I guess that I should choose another way with React. Google gave me ReactCSSTransitionGroup but it seems to be broken and unmaintained (according to this message: github.com). E.g. I can't make a delay before starting the animation.
I also found a library called React-motion but I'm not sure if it's actually a tool that I need. So I want to ask whether you can recommend me something about it. Probably I should use VanillaJS (using refs and other ReactDOM functions)? Or there is another approach to it? Thanks in advance.
The easiest way to do animations in React, or, in fact, anywhere on the web, is to use CSS Transitions.
CSS Transitions actually has nothing to do with React. Quoting MDN,
CSS Transitions is a module of CSS that lets you create
gradual transitions between the values of specific CSS properties. The
behavior of these transitions can be controlled by specifying their
timing function, duration, and other attributes.
Because CSS transitions is a pure CSS, they can be used in React applications, Angular, plain Javascript or even old-school server-rendered pages with no Javascript at all.
It is not the most versatile or powerful technique. But since in most cases the animations we want are pretty simple, why looking for something more complicated when a simple will do the job?
CSS Transitions are also well-supported by all major browsers with a notable exception of Opera Mini and IE below version 10.
CSS Transitions give us an ability to animate between the two CSS states. Let's say you want to animate opening and closing of a drawer (triggered by a click on a button). Let's assume we have a flex container around the drawer. When the drawer is opened, we want it to occupy 100% of the container width, therefore its max-width should be 100%. When it is closed, its width should be 0. We can create two CSS styles:
/* This CSS style is applied when the drawer is opened */
const openedStyle = {
maxWidth: '100%' /* max-with is 100% when the drawer is opened */,
};
/* This CSS style is applied when the drawer is closed */
const closedStyle = {
maxWidth: 0 /* max-width is 0 in the closed drawer */,
};
Then we handle opening / closing event apply one of those classes to the drawer object on opening / closing:
class Drawer extends React.Component {
state = {
opened: false // Initially search form is Closed
};
toggleOpened = () =>
// Toggle opened / closed state.
// Because we rely on the previous state, we need to use
// a functional setState form
// https://ozmoroz.com/2018/11/why-my-setstate-doesnt-work/
this.setState(state => ({ ...state, opened: !state.opened }));
render() {
const { opened } = this.state;
return (
<div className="drawer-container col-12 col-md-4">
<input
type="text"
className="drawer"
// Apply 'openedStyle' CSS class if the drawer is opened,
// and 'closedStyle' if the drawer is closed.
style={opened ? openedStyle : closedStyle}
/>
<button
type="button"
className="open-close-button btn btn-primary"
onClick={this.toggleOpened}
>
{opened ? 'Close' : 'Open'}
</button>
</div>
);
}
}
export default Drawer;
When we press the "Open / Close" button, the drawer will flip between 0 and 100% width, effectively opening and closing.
All we need now is to animate it.
For that we need a secret ingredient - a CSS transition property. All we need to do is to add the following style to the drawer:
/* This CSS style is applied when the drawer is opened */
const openedStyle = {
maxWidth: '100%' /* max-with is 100% when the drawer is opened */,
/* Upon transitioning to Open,
animate `max-width' for 0.5s*/
transition: 'max-width 0.5s'
};
/* This CSS style is applied when the drawer is closed */
const closedStyle = {
maxWidth: 0 /* max-width is 0 in the closed drawer */,
/* Upon transitioning to Closed,
animate `max-width' for 0.5s */
transition: 'max-width 0.5s'
};
VoilĂ ! We've got our animation - upon clicking the button our drawer is now expanded and collapsed within half a second!
This is it in the nutshell, but there is more to it:
You can animate more than one CSS attribute with a CSS transition.
You can apply delay to transitionable properties, i.e. animate opacity first, and then animate width of the same element after 0.5 second delay.
You can apply various timing functions to transitions.
I wrote an expanded blog post explaining all the above: Painless React Animations via CSS Transitions.
Check out this easy to use and popular package:
https://www.npmjs.com/package/react-transition-group
Install:
npm install react-transition-group
Usage:
import { CSSTransition } from 'react-transition-group';
<CSSTransition
in={toShow} // boolean value passed via state/props to either mount or unmount this component
timeout={300}
classNames='my-element' // IMP!
unmountOnExit
>
<ComponentToBeAnimated />
</CSSTransition>
NOTE: Make sure to apply below styles using the class property in CSS:
.my-element-enter {
opacity: 0;
transform: scale(0.9);
}
.my-element-enter-active {
opacity: 1;
transform: translateX(0);
transition: opacity 300ms, transform 300ms;
}
.my-element-exit {
opacity: 1;
}
.my-element-exit-active {
opacity: 0;
transform: scale(0.9);
transition: opacity 300ms, transform 300ms;
}
Maybe react-magic can help you.
Using Bootstrap and Angularjs I'd like to create a button that doesn't ever appear to be "active". I'd like the button to darken slightly when the mouse is over it, and I'd like it to darken further when it's clicked. When the mouse leaves the button, however, I'd like it to return to its original appearance.
Semantically, I'm using the button to "reset" part of my app. I want to execute some code when it's clicked. After it's been pressed, though, it doesn't make sense for the button to remain in a "depressed" state.
Here's a live example.
Any ideas?
Alternatively you could use the ng-mouseenter and ng-mosueleave directives.
For example:
<div ng-app="ButtonApp">
<div ng-controller="MainCtrl">
<button ng-class="buttonClass"
ng-mouseenter="onMouseEnter()"
ng-mouseleave="onMouseLeave()"> Click Me!
</div>
</div>
And in your controller:
var app = angular.module('ButtonApp',[]);
app.controller('MainCtrl', ['$scope',function($scope){
var defaultButtonClass = ['btn','btn-foxtrot'];
$scope.buttonClass = defaultButtonClass;
$scope.onMouseEnter = function(){
$scope.buttonClass = ['btn','btn-bravo'];
};
$scope.onMouseLeave = function() {
$scope.buttonClass = defaultButtonClass;
}
}]);
You can see my JSFiddle.
To generate the button colors you could use something like Beautiful Buttons for
Twitter Bootstrappers.
I'd give it another class, say btn-reset and add the following CSS.
// the order of these two is also important
.btn-reset:hover{
background-color: Dark !important;
}
.btn-reset:active{
background-color: Darkest !important;
}
// you need this to reset it after it's been clicked and released
.btn-reset:focus{
background-color: Normal !important;
}
It's working here http://plnkr.co/edit/QVChtJ8w70HXmyAaVY4A?p=preview
The issue is that the :focus pseudo class has a darker colour than the standard button so after it's been clicked it still has focus so still has the darker colour, if you want to stick with the standard colours you can just add a new selector for the :focus pseudo class.