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

create Button as web component #9

Draft
wants to merge 4 commits into
base: feature/storybook-setup
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
41 changes: 18 additions & 23 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,38 @@
/** @type { import('@storybook/html-webpack5').StorybookConfig } */

const path = require('path');

const config = {
stories: ["../stories/**/*.mdx", "../stories/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
],
addons: ["@storybook/addon-links", "@storybook/addon-essentials", "@storybook/addon-interactions", "@storybook/addon-mdx-gfm"],
framework: {
name: "@storybook/html-webpack5",
options: {},
name: "@storybook/web-components-webpack5",
options: {}
},
docs: {
autodocs: "tag",
autodocs: "tag"
},
webpackFinal: async (config) => {
webpackFinal: async config => {
config.module.rules.push({
test: /\.(scss|css)$/,
use: [
"style-loader",
"css-loader",
"postcss-loader",
"sass-loader"
],
include: path.resolve(__dirname, '../'),

use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"],
include: path.resolve(__dirname, '../')
}, {
test: /\.elm$/,
exclude: [/elm-stuff/, /node_modules/],
loader: 'elm-webpack-loader',
options: {
debug: true
}
});

config.module.rules.push({
test: /\.(woff(2)?|ttf|eot|otf|svg)(\?v=\d+\.\d+\.\d+)?$/,
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/',
},
outputPath: 'fonts/'
}
});
return config;
},
}
};
export default config;
export default config;
2 changes: 1 addition & 1 deletion .storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import '../scss/main.scss';

/** @type { import('@storybook/html').Preview } */
/** @type { import('@storybook/web-components').Preview } */

const preview = {
parameters: {
Expand Down
4 changes: 1 addition & 3 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
export { createButton as Button } from "./stories/Button"
export { ButtonSize } from "./stories/Button"
export type { ButtonProps } from "./stories/Button"
export { WebButton } from './stories/Button';
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@
"@storybook/addon-essentials": "^7.0.7",
"@storybook/addon-interactions": "^7.0.7",
"@storybook/addon-links": "^7.0.7",
"@storybook/addon-mdx-gfm": "^7.0.22",
"@storybook/blocks": "^7.0.7",
"@storybook/html": "^7.0.7",
"@storybook/html-webpack5": "^7.0.7",
"@storybook/testing-library": "^0.0.14-next.2",
"@storybook/web-components-webpack5": "^7.0.22",
"css-loader": "^6.7.3",
"elm-webpack-loader": "^8.0.0",
"file-loader": "^6.2.0",
"nodemon": "^2.0.19",
"postcss-loader": "^7.2.4",
Expand All @@ -41,6 +42,7 @@
"style-loader": "^3.3.2"
},
"dependencies": {
"lit": "^2.7.5",
"storybook": "^7.0.7"
}
}
58 changes: 16 additions & 42 deletions stories/Button.stories.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,25 @@
import { createButton, ButtonSize } from './Button';
import './Button';

export default {
title: 'Example/Button',
tags: ['autodocs'],
render: ({ size, color, label, onClick }) => {
// alternatively, can use plain HTML string to return element
// return `<div>${label}</div>`;
return createButton({ size, color, label, onClick });
},
argTypes: {
size: {
control: { type: 'select' },
options: [ButtonSize.Small, ButtonSize.Medium, ButtonSize.Large, ButtonSize.XLarge],
},
color: { control: 'color' },
label: { control: 'text' },
onClick: { action: 'onClick' },
},
title: 'WebButton',
};

export const Small = {
args: {
size: ButtonSize.Small,
label: 'Button',
},
};

export const Medium = {
args: {
size: ButtonSize.Medium,
label: 'Button',
},
};
const Template = (args: any) => `<web-button label="${args.label}" size="${args.size}"></web-button>`;

export const Large = {
args: {
size: ButtonSize.Large,
label: 'Button',
},
export const Small = Template.bind({});
Small.args = {
label: 'Small Button',
size: '12px',
};

export const XLarge = {
args: {
size: ButtonSize.XLarge,
label: 'Button',
},
export const Medium = Template.bind({});
Medium.args = {
label: 'Medium Button',
size: '16px',
};



export const Large = Template.bind({});
Large.args = {
label: 'Large Button',
size: '20px',
};
138 changes: 113 additions & 25 deletions stories/Button.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,118 @@
import '../scss/main.scss';

export enum ButtonSize {
Small = 'small',
Medium = 'medium',
Large = 'large',
XLarge = 'x-large',
}
export class WebButton extends HTMLElement {
// Observe the 'label', 'size', and 'color' attributes
static get observedAttributes() {
return ['label', 'size', 'color'];
}

export type ButtonProps = {
size: ButtonSize;
label: string;
color?: string;
onClick: () => void;
}
constructor() {
super();
this.attachShadow({ mode: 'open' });
}

// lifecycle hook that runs when component is inserted into DOM
connectedCallback() {
this.render();
}

export const createButton = ({
size,
color = "",
label,
onClick,
}: ButtonProps) => {
const btn = document.createElement('button');
btn.type = 'button';
btn.innerText = label;
btn.addEventListener('click', onClick);
// we re-render anytime an attribute value changes, regardless of which attribute (name)
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
this.render();
}
}

render() {
const label = this.getAttribute('label') || "";
const color = this.getAttribute('color') || 'grey';

if (this.shadowRoot) {
this.shadowRoot.innerHTML = `
<style>
:host {
display: inline-block;
}
button,
.styled-button {
align-items: center;
background-color: #{$neutral--80};
border: 0.5px solid #{$neutral--80};
border-radius: 3.625rem;
color: ${color};
cursor: pointer;
display: flex;
justify-content: center;
padding: 0.84375rem 0;
font-size: 0.6875rem;
font-style: normal;
font-weight: 500;
line-height: 1rem;
transition: background-color 0.25s ease, height 0.25s ease;
min-width: 6.45rem;

.label {
cursor: pointer;
pointer-events: inherit;
}

&--small {
padding: 0.5rem 1rem;
min-width: unset;
}

&--large {
padding: 0rem 1rem;
height: 2.75rem;
}

&--x-large {
border-radius: 100px;
font-size: 0.8125rem;
font-weight: 500;
line-height: 1.125rem;
padding: 1.1875rem 0;
width: 100%;
}

&:disabled {
background-color: #{$neutral--95};
border: 0.5px solid #{$neutral--95};
color: #{$neutral--60};
cursor: auto;

.label {
cursor: auto;
pointer-events: inherit;
}
}

&:not(:disabled) {
&:hover {
filter: brightness(90%);
}

&:active {
filter: brightness(85%);
}
}

&+& {
margin-left: 1rem;
}
}

.theme--light {
.button--large {
border-color: transparent;
}
}

</style>
<button class="styled-button">${label}</button>
`;
}
}
}

btn.className = ['styled-button', `styled-button--${size}`, color && `styled-button--${color}`].join(' ');
return btn;
};
customElements.define('web-button', WebButton);