-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #228 from CityOfDetroit/issue.222
Add article cards to design system
- Loading branch information
Showing
7 changed files
with
255 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
Large diffs are not rendered by default.
Oops, something went wrong.
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,40 @@ | ||
.img-placeholder { | ||
width: 100%; | ||
height: 400px; | ||
background-color: #d3d3d3; | ||
} | ||
|
||
.card-container { | ||
position: relative; | ||
overflow: hidden; | ||
} | ||
|
||
.text-container { | ||
position: absolute; | ||
box-sizing: border-box; | ||
padding: 0.5em; | ||
width: 100%; | ||
height: 20%; | ||
bottom: 0; | ||
left: 0; | ||
transition: | ||
padding-top 0.2s, | ||
height 0.2s ease-in-out; | ||
} | ||
|
||
.card-container:hover .text-container { | ||
box-sizing: border-box; | ||
height: 100%; | ||
padding-top: 40%; | ||
} | ||
|
||
.subtitle-container { | ||
margin-top: 2em; | ||
opacity: 0; | ||
transition: opacity 0.2s ease-in-out; | ||
} | ||
|
||
.card-container:hover .subtitle-container { | ||
margin-top: 2em; | ||
opacity: 1; | ||
} |
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,83 @@ | ||
import styles from '!!raw-loader!./ArticleCard.css'; | ||
import varStyles from '!!raw-loader!../../../shared/variables.css'; | ||
import bootstrapStyles from '!!raw-loader!../../../shared/themed-bootstrap.css'; | ||
|
||
const template = document.createElement('template'); | ||
template.innerHTML = ` | ||
<div class="card-container container-fluid px-0"> | ||
<div class="img-placeholder"></div> | ||
<div class="text-container"> | ||
<slot name="title"></slot> | ||
<div class="subtitle-container"> | ||
<slot name="subtitle"></slot> | ||
</div> | ||
</div> | ||
</div> | ||
`; | ||
|
||
class ArticleCard extends HTMLElement { | ||
static observedAttributes = []; | ||
|
||
constructor() { | ||
// Always call super first in constructor | ||
super(); | ||
// Create a shadow root | ||
const shadow = this.attachShadow({ mode: 'open' }); | ||
shadow.appendChild(template.content.cloneNode(true)); | ||
|
||
// Add styles | ||
const bootStyles = document.createElement('style'); | ||
bootStyles.textContent = bootstrapStyles; | ||
const variableStyles = document.createElement('style'); | ||
variableStyles.textContent = varStyles; | ||
const itemStyles = document.createElement('style'); | ||
itemStyles.textContent = styles; | ||
shadow.appendChild(bootStyles); | ||
shadow.appendChild(variableStyles); | ||
shadow.appendChild(itemStyles); | ||
} | ||
|
||
connectedCallback() { | ||
this._replaceImgPlacehold(); | ||
this._setColor(); | ||
this._wrapWithLink(); | ||
} | ||
|
||
/** | ||
* Wraps the .card-container element in an 'a' element with an href attribute. | ||
*/ | ||
_wrapWithLink() { | ||
const target = this.getAttribute('target'); | ||
const href = this.getAttribute('href'); | ||
const cardContainer = this.shadowRoot.querySelector('.card-container'); | ||
const link = document.createElement('a'); | ||
link.setAttribute('href', href); | ||
link.setAttribute('target', target); | ||
cardContainer.parentNode.insertBefore(link, cardContainer); | ||
link.appendChild(cardContainer); | ||
} | ||
|
||
/** | ||
* Sets the color of the article card. | ||
*/ | ||
_setColor() { | ||
const color = this.getAttribute('color'); | ||
const textContainer = this.shadowRoot.querySelector('.text-container'); | ||
textContainer.classList.add(`bg-${color}`); | ||
} | ||
|
||
/** | ||
* Replaces the image placeholder with the actual image element. | ||
*/ | ||
_replaceImgPlacehold() { | ||
const img = document.createElement('img'); | ||
img.classList.add('w-100'); | ||
img.src = this.getAttribute('src'); | ||
|
||
// Replace div with img element | ||
const imgPlaceholder = this.shadowRoot.querySelector('.img-placeholder'); | ||
imgPlaceholder.replaceWith(img); | ||
} | ||
} | ||
|
||
export { ArticleCard as default }; |
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,2 @@ | ||
import ArticleCard from './ArticleCard'; | ||
customElements.define('cod-article-card', ArticleCard); |
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,116 @@ | ||
import '../components/organisms/ArticleCard/cod-article-card'; | ||
import { COMMON_STORY_ARGS } from '../shared/js/storybook/args-utils'; | ||
|
||
export default { | ||
title: 'Components/Organisms/ArticleCard', | ||
argTypes: { | ||
href: { | ||
control: { type: 'text' }, | ||
description: 'The URL of where the card will link to.', | ||
}, | ||
target: { | ||
control: { type: 'select' }, | ||
options: ['_self', '_blank', '_parent', '_top'], | ||
description: 'The URL of where the card will link to.', | ||
}, | ||
src: { | ||
control: { type: 'text' }, | ||
description: 'The source of the article card image.', | ||
}, | ||
title: { | ||
control: { type: 'text' }, | ||
description: 'The title of the article. Custom markdown is supported.', | ||
}, | ||
subTitle: { | ||
control: { type: 'text' }, | ||
description: 'The subtitle of the article. Custom markdown is supported.', | ||
}, | ||
color: COMMON_STORY_ARGS.bootstrapColor, | ||
}, | ||
args: { | ||
src: 'https://placehold.co/300x400', | ||
title: | ||
'<h3 class="text-center text-light" style="text-transform: uppercase; font-weight: 900;">The Great Money Transfer</h3>', | ||
subTitle: | ||
'<h5 class="text-center text-success" style="text-transform: uppercase; font-weight: 900;">The Power of Generational Wealth</h4>', | ||
color: 'primary', | ||
href: 'https://www.example.com', | ||
target: '_blank', | ||
}, | ||
}; | ||
|
||
function _containsHTMLTags(str) { | ||
return /<\/?[a-z][\s\S]*>/i.test(str); | ||
} | ||
|
||
function _createElementFromHTML(htmlString) { | ||
var div = document.createElement('div'); | ||
div.innerHTML = htmlString.trim(); | ||
|
||
// Change this to div.childNodes to support multiple top-level nodes. | ||
return div.firstChild; | ||
} | ||
|
||
function _createArticleCard(args) { | ||
const articleCardElt1 = document.createElement('cod-article-card'); | ||
articleCardElt1.setAttribute('src', args.src); | ||
articleCardElt1.setAttribute('color', args.color); | ||
articleCardElt1.setAttribute('href', args.href); | ||
articleCardElt1.setAttribute('target', args.target); | ||
if (_containsHTMLTags(args.title)) { | ||
const title = _createElementFromHTML(args.title); | ||
title.slot = 'title'; | ||
articleCardElt1.appendChild(title); | ||
} else { | ||
const title = document.createElement('h3'); | ||
title.innerText = args.title; | ||
title.classList.add('text-center'); | ||
title.slot = 'title'; | ||
articleCardElt1.appendChild(title); | ||
} | ||
|
||
if (_containsHTMLTags(args.subTitle)) { | ||
const subTitle = _createElementFromHTML(args.subTitle); | ||
subTitle.slot = 'subtitle'; | ||
articleCardElt1.appendChild(subTitle); | ||
} else { | ||
const subTitle = document.createElement('h4'); | ||
subTitle.innerText = args.subTitle; | ||
subTitle.classList.add('text-center'); | ||
subTitle.slot = 'subtitle'; | ||
articleCardElt1.appendChild(subTitle); | ||
} | ||
return articleCardElt1; | ||
} | ||
|
||
// Template | ||
const Template = (args) => { | ||
const articleCardElt1 = _createArticleCard(args); | ||
const articleCardElt2 = _createArticleCard(args); | ||
const articleCardElt3 = _createArticleCard(args); | ||
|
||
const rowElt = document.createElement('div'); | ||
rowElt.classList.add('row'); | ||
|
||
const colElt1 = document.createElement('div'); | ||
colElt1.classList.add('col'); | ||
colElt1.classList.add('px-0'); // Add bootstrap class to remove left and right padding | ||
colElt1.appendChild(articleCardElt1); | ||
rowElt.appendChild(colElt1); | ||
|
||
const colElt2 = document.createElement('div'); | ||
colElt2.classList.add('col'); | ||
colElt2.classList.add('px-0'); // Add bootstrap class to remove left and right padding | ||
colElt2.appendChild(articleCardElt2); | ||
rowElt.appendChild(colElt2); | ||
|
||
const colElt3 = document.createElement('div'); | ||
colElt3.classList.add('col'); | ||
colElt3.classList.add('px-0'); // Add bootstrap class to remove left and right padding | ||
colElt3.appendChild(articleCardElt3); | ||
rowElt.appendChild(colElt3); | ||
|
||
return rowElt; | ||
}; | ||
|
||
export const Primary = Template.bind({}); |