Skip to content

Commit

Permalink
feat: Add Banner component (#49)
Browse files Browse the repository at this point in the history
* feat: Add Banner component
* Fix plop file for component
* Make dropdown story better by including options creation in the story

* feat: CR fixes
* Add close button support
* Add RTL support
* Conform to styling standards
  • Loading branch information
DorShakedMonday authored Jan 10, 2021
1 parent 54d61f4 commit d4d3da2
Show file tree
Hide file tree
Showing 10 changed files with 813 additions and 1 deletion.
2 changes: 1 addition & 1 deletion plop/component/component-js.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const {{properCase componentName}} = forwardRef(({ className }, ref) => {
{text}
</div>
);
;
});

{{properCase componentName}}.propTypes = {
className: PropTypes.string,
Expand Down
151 changes: 151 additions & 0 deletions src/components/Banner/Banner.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import React, { useRef, forwardRef, useMemo } from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import useMergeRefs from "../../hooks/useMergeRefs";
import { IMAGE_POSITIONS } from "./BannerConstants";
import "./Banner.scss";
import Button from "../Button/Button";
import Icon from "../Icon/Icon";
import CloseSmall from "../Icon/Icons/components/CloseSmall";

const PRESERVE_VALUE = value => value;

const Banner = forwardRef(
(
{
className,
imageAlt,
imageSrc,
renderTitle,
renderSubtitle,
title,
subtitle,
imageClassName,
imagePosition,
onClose,
rtl
},
ref
) => {
const componentRef = useRef(null);
const mergedRef = useMergeRefs({ refs: [ref, componentRef] });

const renderedTitle = useMemo(() => {
const computedTitle = renderTitle(title);
if (!computedTitle) return null;
return <h1 className="banner--title">{computedTitle}</h1>;
}, [title, renderTitle]);

const renderedSubtitle = useMemo(() => {
const computedSubtitle = renderSubtitle(subtitle);
if (!computedSubtitle) return null;
return <h2 className="banner--subtitle">{computedSubtitle}</h2>;
}, [subtitle, renderSubtitle]);

const renderImage = useMemo(() => {
if (!imageSrc) return null;
return <img src={imageSrc} alt={imageAlt} className={cx("banner--image", imageClassName)} />;
}, [imageAlt, imageSrc, imageClassName]);

const renderCloseButton = useMemo(() => {
if (!onClose) return null;
return (
<Button
onClick={onClose}
className="banner--close"
size={Button.sizes.SMALL}
kind={Button.kinds.TERTIARY}
color={Button.colors.PRIMARY}
ariaLabel="close-banner"
>
<Icon iconType={Icon.type.SVG} clickable={false} icon={CloseSmall} iconSize="16px" ignoreFocusStyle />
</Button>
);
}, [onClose]);

return (
<aside ref={mergedRef} className={cx("banner", className, { rtl })}>
<div
className={cx("banner--content", `image-position__${imagePosition}`, {
"close-button-spacing": !!renderCloseButton
})}
>
{renderCloseButton}
{renderedTitle}
{renderedSubtitle}
{renderImage}
</div>
</aside>
);
}
);

Banner.imagePosition = IMAGE_POSITIONS;

Banner.propTypes = {
/**
* custom style
*/
className: PropTypes.string,
/**
* image alt attribute
*/
imageAlt: PropTypes.string,
/**
* image source
*/
imageSrc: PropTypes.string,
/**
* determines the image position
*/
imagePosition: PropTypes.oneOf([
Banner.imagePosition.LEFT,
Banner.imagePosition.RIGHT,
Banner.imagePosition.BOTTOM,
Banner.imagePosition.TOP
]),
/**
* image custom style
*/
imageClassName: PropTypes.string,
/**
* title custom render
*/
renderTitle: PropTypes.func,
/**
* subtitle custom render
*/
renderSubtitle: PropTypes.func,
/**
* title value
*/
title: PropTypes.string,
/**
* sub title value
*/
subtitle: PropTypes.string,
/**
* Add X button to the component when initialized and called when the button is clicked
*/
onClose: PropTypes.func,
/**
* Change to "Right to Left" if set to `true`. Defaults to "Left to Right"
*/
rtl: PropTypes.bool
};

Banner.defaultProps = {
className: "",
imagePosition: IMAGE_POSITIONS.LEFT,
imageAlt: "Banner main image",
imageSrc: "",
renderTitle: PRESERVE_VALUE,
renderSubtitle: PRESERVE_VALUE,
title: "",
subtitle: "",
imageClassName: "",
rtl: false,
onClose: null
};

export default Banner;
138 changes: 138 additions & 0 deletions src/components/Banner/Banner.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
@import "../../styles/themes.scss";
@import "../../styles/typography.scss";

.banner {
width: content-box;

.banner--content {
display: grid;
position: relative;
padding: $spacing-large;
border-radius: $border-radius-medium;
grid-gap: $spacing-small;
justify-content: center;
align-content: center;
@include theme-prop(background-color, dark-background-on-secondary-color);
@include theme-prop(color, primary-text-color);

.banner--close {
position: absolute;
top: 0;
right: 0;
}

&.close-button-spacing {
padding-right: $spacing-xl;
}

&.image-position__left,
&.image-position__right {
grid-auto-rows: auto;
grid-auto-columns: auto;
}

&.image-position__left {
grid-template-areas:
'image title title '
'image subtitle subtitle';
}

&.image-position__right {
grid-template-areas:
'title title image'
'subtitle subtitle image'
}

&.image-position__bottom,
&.image-position__top {
grid-auto-rows: auto;
grid-template-columns: repeat(1, 1fr);
}

&.image-position__top {
grid-template-areas:
'image'
'title'
'subtitle';
}
&.image-position__bottom {
grid-template-areas:
'title'
'subtitle'
'image';
}

.banner--title {
align-self: end;
margin: 0;
grid-area: title;
height: 32px;
font-size: 24px;
font-weight: 500;
letter-spacing: -0.2px;
max-width: 782px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.banner--subtitle {
margin: 0;
grid-area: subtitle;
font-size: 18px;
font-weight: 400;
line-height: 24px;
max-width: 782px;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}

.banner--image {
align-self: center;
justify-self: center;
grid-area: image;
width: 100px;
height: 100px;
border-radius: $border-radius-medium;
object-fit: contain;
@include theme-prop(background-color, primary-background-color, true);
}

.banner--close {
align-self: start;
grid-area: close;
border-radius: $border-radius-small;
}
}

&.rtl {
.banner--content {
text-align: right;

.banner--close {
position: absolute;
top: 0;
left: 0;
right: auto;
}

&.close-button-spacing {
padding-left: $spacing-xl;
}

&.image-position__left {
grid-template-areas:
'image title title'
'image subtitle subtitle';
}

&.image-position__right {
grid-template-areas:
'title title image'
'subtitle subtitle image';
}
}
}
}
6 changes: 6 additions & 0 deletions src/components/Banner/BannerConstants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const IMAGE_POSITIONS = Object.freeze({
LEFT: "left",
RIGHT: "right",
TOP: "top",
BOTTOM: "bottom"
});
Loading

0 comments on commit d4d3da2

Please sign in to comment.