-
Notifications
You must be signed in to change notification settings - Fork 296
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 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
1 parent
54d61f4
commit d4d3da2
Showing
10 changed files
with
813 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
}); |
Oops, something went wrong.