Skip to content

Commit

Permalink
Merge pull request #167 from CityOfDetroit/feature.issue.154
Browse files Browse the repository at this point in the history
Create component for New Action Button
  • Loading branch information
jedgar1mx authored Mar 21, 2024
2 parents 2656c7a + 046d355 commit 13e0d43
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 5 deletions.
45 changes: 45 additions & 0 deletions src/components/atoms/ActionButtonV2/ActionButtonV2.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
div {
font-family: var(--font-family);
}

.action-button-container,
.action-button-container > a,
.action-button-container > a > .abutton {
width: 100%;
height: 100%;
border-top: none;
border-right: none;
border-left: none;
}

.top-icon {
padding-top: 1em;
}

.abutton-title {
margin-bottom: var(--abutton-title-spacer-y);
}

.abutton-text:last-child {
margin-bottom: 0;
}

.abutton-body {
padding: var(--abutton-spacer-y) var(--abutton-spacer-x);
text-align: left;
}

.abutton {
display: flex;
flex-direction: row;
min-width: 0;
padding: 1em;
--abutton-spacer-y: 1em;
--abutton-spacer-x: 1em;
--abutton-title-spacer-y: 0.5em;
}

.btn {
--bs-btn-padding-x: 0rem;
--bs-btn-padding-y: 0rem;
}
63 changes: 63 additions & 0 deletions src/components/atoms/ActionButtonV2/ActionButtonV2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import styles from '!!raw-loader!./ActionButtonV2.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="action-button-container">
<a class="btn" role="button" href="">
<div class="abutton">
<div class="top-icon">
<cod-icon data-icon="" data-size="x-large" is-highlighted>
</cod-icon>
</div>
<div class="abutton-body">
<slot class="abutton-title" name="title"></slot>
<slot name="body"></slot>
</div>
</div>
</a>
</div>
`;

class ActionButtonV2 extends HTMLElement {
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() {
// Update the icon.
const icon = this.getAttribute('icon');
const iconElt = this.shadowRoot.querySelector('cod-icon');
iconElt.setAttribute('data-icon', icon);

// Update the button link and style.
const btnColor = this.getAttribute('btn-color') ?? 'btn-outline-primary';
const href = this.getAttribute('href');
const target = this.getAttribute('target');
const aElt = this.shadowRoot.querySelector('a.btn');
aElt.classList.add('btn', btnColor);
aElt.setAttribute('href', href);
if (target) {
aElt.setAttribute('target', target);
}
}
}

export { ActionButtonV2 as default };
2 changes: 2 additions & 0 deletions src/components/atoms/ActionButtonV2/cod-action-button-v2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import ActionButtonV2 from './ActionButtonV2';
customElements.define('cod-action-button-v2', ActionButtonV2);
8 changes: 8 additions & 0 deletions src/components/atoms/Icon/Icon.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* Highlighted styles */
.icon-container.highlighted {
background-image: url('data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%2049%2019%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cellipse%20cx%3D%2224.5%22%20cy%3D%229.5%22%20rx%3D%2224.5%22%20ry%3D%229.5%22%20fill%3D%22%239FD5B3%22%2F%3E%3C%2Fsvg%3E');
background-repeat: no-repeat;
background-position-y: bottom;
background-size: 100% auto;
padding-bottom: 0.9%;
}
44 changes: 39 additions & 5 deletions src/components/atoms/Icon/Icon.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
import styles from '!!raw-loader!./Icon.css';
import varStyles from '!!raw-loader!../../../shared/variables.css';
import bootstrapStyles from '!!raw-loader!../../../shared/themed-bootstrap.css';

export default class Icon extends HTMLElement {
constructor() {
// Always call super first in constructor
super();
// Create a shadow root.
this.attachShadow({ mode: 'open' });
// Create a shadow root
const shadow = this.attachShadow({ mode: 'open' });

// 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() {
if (this.isIconConnected()) {
return;
}

// Create a container for the icon and circle image
const container = document.createElement('div');
container.classList.add('icon-container', 'd-inline-block');

// Icon attributes
const icon = this.getAttribute('data-icon');
let size = this.getAttribute('data-size');

switch (size) {
case 'small':
size = '16';
Expand All @@ -35,9 +55,23 @@ export default class Icon extends HTMLElement {
size = '24';
break;
}
const iconContainer = document.createElement('span');
iconContainer.innerHTML = this.getIcon(icon, size);
this.shadowRoot.appendChild(iconContainer);

const iconElement = document.createElement('span');
iconElement.innerHTML = this.getIcon(icon, size);

// Append the icon element to the container
container.appendChild(iconElement);

// Boolean Attribute adds circle if present
const isHighlighted = this.hasAttribute('is-highlighted');

// Add the highlighted class if is-highlighted attribute is present
if (isHighlighted) {
container.classList.add('highlighted');
}

// Append the container to the shadow root
this.shadowRoot.appendChild(container);
}

isIconConnected() {
Expand Down
161 changes: 161 additions & 0 deletions src/stories/actionbuttonV2.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { html } from 'lit-html';
import '../components/atoms/ActionButtonV2/cod-action-button-v2';
import '../components/atoms/Icon/cod-icon';
import IconStory from './icon.stories';

export default {
component: 'cod-action-button-v2',
title: 'Components/Atoms/ActionButtonV2',
// 👇 Creates specific argTypes
argTypes: {
buttonColor: {
options: [
'btn-outline-primary',
'btn-outline-secondary',
'btn-outline-success',
],
control: 'select',
},
icon: IconStory.argTypes.icon,
title: {
control: 'text',
},
body: {
control: 'text',
},
},
args: {
buttonColor: 'btn-outline-primary',
title: 'Do Something',
body: 'Like click on this card',
icon: 'house-fill',
},
};

// Template
const Template = (args) => {
const aButton = document.createElement('cod-action-button-v2');
aButton.setAttribute('btn-color', args.buttonColor);
aButton.setAttribute('icon', args.icon);

// Create a title slot
const titleSlot = document.createElement('h4');
titleSlot.setAttribute('slot', 'title');
titleSlot.innerText = args.title;
aButton.appendChild(titleSlot);

// Create a body slot
const bodySlot = document.createElement('p');
bodySlot.setAttribute('slot', 'body');
bodySlot.innerText = args.body;
aButton.appendChild(bodySlot);

aButton.setAttribute('href', 'https://example.com');
return aButton;
};

export const ActionButtonV2 = Template.bind({});

export const ActionButtonV2RichBody = () => html`
<div style="width: 300px; height: 300px">
<cod-action-button-v2
btn-color="btn-outline-primary"
icon="house"
href="https://example.com"
target="_blank"
>
<h4 slot="title">Do Something</h4>
<div slot="body">
<p>
Anything can go inside an action button but it's best to keep to
simple text.
</p>
<img
src="https://placehold.co/800x400/000000/FFF"
alt="..."
width="100"
height="50"
/>
</div>
</cod-action-button-v2>
</div>
`;

export const ActionButtonV2Grid = () => html`
<div class="container-fluid">
<div class="row my-3">
<div class="col-sm-4">
<cod-action-button-v2
btn-color="btn-outline-primary"
icon="house"
href="https://example.com"
target="_blank"
>
<h4 slot="title">Do Something</h4>
<p slot="body">Like Click on This Button</p>
</cod-action-button-v2>
</div>
<div class="col-sm-4">
<cod-action-button-v2
btn-color="btn-outline-primary"
icon="house"
href="https://example.com"
target="_blank"
>
<h4 slot="title">Do Something</h4>
<p slot="body">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec
luctus eros sit amet augue tempus sollicitudin. Mauris lacinia ante
et.
</p>
</cod-action-button-v2>
</div>
<div class="col-sm-4">
<cod-action-button-v2
btn-color="btn-outline-primary"
icon="house"
href="https://example.com"
target="_blank"
>
<h4 slot="title">Do Something</h4>
<p slot="body">Like Click on This Button</p>
</cod-action-button-v2>
</div>
</div>
<div class="row my-3">
<div class="col-sm-4">
<cod-action-button-v2
btn-color="btn-outline-primary"
icon="house"
href="https://example.com"
target="_blank"
>
<h4 slot="title">Do Something</h4>
<p slot="body">Like Click on This Button</p>
</cod-action-button-v2>
</div>
<div class="col-sm-4">
<cod-action-button-v2
btn-color="btn-outline-primary"
icon="house"
href="https://example.com"
target="_blank"
>
<h4 slot="title">Do Something</h4>
<p slot="body">Like Click on This Button</p>
</cod-action-button-v2>
</div>
<div class="col-sm-4">
<cod-action-button-v2
btn-color="btn-outline-primary"
icon="house"
href="https://example.com"
target="_blank"
>
<h4 slot="title">Do Something</h4>
<p slot="body">Like Click on This Button</p>
</cod-action-button-v2>
</div>
</div>
</div>
`;
9 changes: 9 additions & 0 deletions src/stories/icon.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,22 @@ export default {
control: { type: 'select' },
options: ['small', 'medium', 'large', 'x-large'],
},
isHighlighted: {
control: { type: 'boolean' },
defaultValue: false,
},
},
};
// Template
const Template = (args) => {
const icon = document.createElement('cod-icon');
icon.setAttribute('data-icon', args.icon);
icon.setAttribute('data-size', args.size);
if (args.isHighlighted) {
icon.setAttribute('is-highlighted', ''); // Set the attribute if isHighlighted is true
} else {
icon.removeAttribute('is-highlighted'); // Remove the attribute if isHighlighted is false
}
return icon;
};

Expand Down

0 comments on commit 13e0d43

Please sign in to comment.