Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement ShowIf component #2300

Merged
merged 8 commits into from
Oct 24, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions assets/images/componentsThumbs/ShowIf.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions src/components/ShowIf/__test__/showIf.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import { mount } from 'enzyme';
import ShowIf from '..';

describe('<ShowIf />', () => {
it('should render the children in the DOM when isTrue is false', () => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar blocks of code found in 2 locations. Consider refactoring.

const component = mount(
<ShowIf>
<span data-id="test" />
</ShowIf>,
);
expect(component.find('[data-id="test"]').exists()).toBe(true);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* eslint-disable func-names */
/* eslint-disable object-shorthand */
import getElementDimensions from '../getElementDimensions';

describe('getElementDimensions', () => {
it('should return the right dimensions', () => {
const dimensions = { width: 50, height: 50 };
const element = {
style: {
position: 'relative',
display: 'none',
visibility: 'hidden',
},
getBoundingClientRect: () => dimensions,
};
expect(getElementDimensions(element)).toEqual(dimensions);
});

it('should modify the styles to get client rect correctly', () => {
const dimensions = { width: 50, height: 50 };
const element = {
style: {
position: 'relative',
display: 'none',
visibility: 'hidden',
},
getBoundingClientRect: function() {
const { position, visibility, display } = this.style;
if (position === 'absolute' && visibility === 'hidden' && display === 'block') {
return dimensions;
}
return undefined;
},
};
getElementDimensions(element);
expect(element.style).toEqual({
position: 'relative',
display: 'none',
visibility: 'hidden',
});
});

it('should leave the element styles as passed', () => {
const dimensions = { width: 50, height: 50 };
const element = {
style: {
position: 'relative',
display: 'none',
visibility: 'hidden',
},
getBoundingClientRect: () => dimensions,
};
getElementDimensions(element);
expect(element.style).toEqual({
position: 'relative',
display: 'none',
visibility: 'hidden',
});
});

it('should return undefined when there is no element', () => {
expect(getElementDimensions(null)).toBe(undefined);
});
});
17 changes: 17 additions & 0 deletions src/components/ShowIf/helpers/getElementDimensions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* eslint-disable no-param-reassign */
const getElementBoundingClientRect = element => {
if (element) {
const { position, visibility, display } = element.style;
element.style.position = 'absolute';
element.style.visibility = 'hidden';
element.style.display = 'block';
const rect = element.getBoundingClientRect();
element.style.position = position;
element.style.visibility = visibility;
element.style.display = display;
return rect;
}
return undefined;
};

export default getElementBoundingClientRect;
13 changes: 13 additions & 0 deletions src/components/ShowIf/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ReactNode } from 'react';
import { BaseProps } from '../types';

type AnimationName = 'fade' | 'slide';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here we need to specify if the slide will be from right, left, top, bottom
we don't need to set all of them now, but what will be used them let's specify it


export interface ShowIfProps extends BaseProps {
isTrue?: any;
inAnimation?: AnimationName;
outAnimation?: AnimationName;
children?: ReactNode;
}

export default function(props: ShowIfProps): JSX.Element | null;
78 changes: 78 additions & 0 deletions src/components/ShowIf/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React, { useLayoutEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import AnimatedContainer from './styled';
import getElementBoundingClientRect from './helpers/getElementDimensions';

/**
* A component that shows its contents when a condition is met. Works similar to `RenderIf`,
* but `ShowIf` does not remove the elements from the DOM, and animates the content in and out.
*/
const ShowIf = ({ id, className, style, isTrue, inAnimation, outAnimation, children }) => {
const [animation, setAnimation] = useState();
const [isVisible, setIsVisible] = useState(isTrue);
const [dimensions, setDimensions] = useState();
const ref = useRef();

useLayoutEffect(() => {
if (isTrue) {
const rect = getElementBoundingClientRect(ref.current);
setDimensions(rect);
setIsVisible(true);
setAnimation(`${inAnimation}In`);
} else {
setAnimation(`${outAnimation}Out`);
}
}, [isTrue, inAnimation, outAnimation]);

const handleAnimationEnd = event => {
if (event.animationName.includes('Out')) {
setIsVisible(false);
}
};

return (
<AnimatedContainer
id={id}
className={className}
style={style}
animation={animation}
isVisible={isVisible}
dimensions={dimensions}
onAnimationEnd={handleAnimationEnd}
ref={ref}
>
{children}
</AnimatedContainer>
);
};

ShowIf.propTypes = {
/** The id of the outer element. */
id: PropTypes.string,
/** A CSS class for the outer element, in addition to the component's base classes. */
className: PropTypes.string,
/** An object with custom style applied for the outer element. */
style: PropTypes.object,
/** Indicates whether the component content is showed or not. If is set to true, then is showed the component content. */
isTrue: PropTypes.any,
inAnimation: PropTypes.oneOf(['fade', 'slide']),
outAnimation: PropTypes.oneOf(['fade', 'slide']),
/** The content of the component. */
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.object,
PropTypes.node,
]),
};

ShowIf.defaultProps = {
id: undefined,
className: undefined,
style: undefined,
isTrue: false,
inAnimation: 'fade',
outAnimation: 'fade',
children: [],
};

export default ShowIf;
Loading