PR-722301: Adding Library Components.

This commit is contained in:
Alejandro Lembke Barrientos 2023-10-11 23:15:46 +00:00
parent 2410a0f8d0
commit b100dc40c3
34 changed files with 885 additions and 108 deletions

43
.github/workflows/npm-publish.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: NPM testing and publish package
on:
push:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
registry-url: https://registry.npmjs.org/
- run: npm ci --legacy-peer-deps
- run: npm test
cypress-run:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
# Install NPM dependencies, cache them correctly
# and run all Cypress tests
- name: Cypress install
run: npm install --legacy-peer-deps
- name: Cypress run
uses: cypress-io/github-action@v5 # use the explicit version number
with:
install: false
component: true
publish-npm:
needs: [ build, cypress-run ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
registry-url: https://registry.npmjs.org/
- run: npm ci --legacy-peer-deps
- run: npm publish --access=public
env:
NODE_AUTH_TOKEN: ${{secrets.npm_token}}

47
.github/workflows/npm-test.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: Testing package
on:
pull_request:
branches: ['*']
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v3
- name: Use Node.js 16
uses: actions/setup-node@v3
with:
node-version: 16
cache: 'npm'
registry-url: https://registry.npmjs.org/
- run: npm ci --legacy-peer-deps
- run: npm test
cypress-run:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
# Install NPM dependencies, cache them correctly
# and run all Cypress tests
- name: Cypress install
run: npm install --legacy-peer-deps
- name: Cypress run
uses: cypress-io/github-action@v5 # use the explicit version number
with:
install: false
component: true
test-build-package:
needs: [ test, cypress-run ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
registry-url: https://registry.npmjs.org/
- run: npm ci --legacy-peer-deps
- run: npm run build

View File

@ -16,7 +16,9 @@ module.exports = {
}),
config.resolve.alias = {
...config.resolve.alias,
'@components': path.resolve(__dirname, "../src/components/")
'@components': path.resolve(__dirname, "../src/components/"),
'@styles': path.resolve(__dirname, "../src/styles/"),
'@utils': path.resolve(__dirname, "../src/utils/")
};
config.resolve.plugins = [new TsconfigPathsPlugin()];
return config;

142
package-lock.json generated
View File

@ -1,22 +1,24 @@
{
"name": "@aleleba/create-react-component-library",
"version": "1.2.14",
"name": "react-list-ui-library",
"version": "0.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@aleleba/create-react-component-library",
"version": "1.2.14",
"name": "react-list-ui-library",
"version": "0.0.1",
"license": "MIT",
"bin": {
"create-react-component-library": "bin/cli.js"
},
"devDependencies": {
"@babel/core": "^7.23.0",
"@babel/preset-env": "^7.22.20",
"@babel/preset-react": "^7.22.15",
"@babel/preset-typescript": "^7.23.0",
"@babel/register": "^7.22.15",
"@fortawesome/fontawesome-svg-core": "^6.4.2",
"@fortawesome/free-brands-svg-icons": "^6.4.2",
"@fortawesome/free-regular-svg-icons": "^6.4.2",
"@fortawesome/free-solid-svg-icons": "^6.4.2",
"@fortawesome/react-fontawesome": "^0.2.0",
"@mdx-js/react": "^2.3.0",
"@storybook/addon-actions": "^7.4.6",
"@storybook/addon-docs": "^7.4.6",
@ -2892,6 +2894,81 @@
"integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==",
"dev": true
},
"node_modules/@fortawesome/fontawesome-common-types": {
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz",
"integrity": "sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA==",
"dev": true,
"hasInstallScript": true,
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/fontawesome-svg-core": {
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.2.tgz",
"integrity": "sha512-gjYDSKv3TrM2sLTOKBc5rH9ckje8Wrwgx1CxAPbN5N3Fm4prfi7NsJVWd1jklp7i5uSCVwhZS5qlhMXqLrpAIg==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.4.2"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/free-brands-svg-icons": {
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.4.2.tgz",
"integrity": "sha512-LKOwJX0I7+mR/cvvf6qIiqcERbdnY+24zgpUSouySml+5w8B4BJOx8EhDR/FTKAu06W12fmUIcv6lzPSwYKGGg==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.4.2"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/free-regular-svg-icons": {
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.4.2.tgz",
"integrity": "sha512-0+sIUWnkgTVVXVAPQmW4vxb9ZTHv0WstOa3rBx9iPxrrrDH6bNLsDYuwXF9b6fGm+iR7DKQvQshUH/FJm3ed9Q==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.4.2"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/free-solid-svg-icons": {
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.2.tgz",
"integrity": "sha512-sYwXurXUEQS32fZz9hVCUUv/xu49PEJEyUOsA51l6PU/qVgfbTb2glsTEaJngVVT8VqBATRIdh7XVgV1JF1LkA==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.4.2"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/react-fontawesome": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz",
"integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==",
"dev": true,
"dependencies": {
"prop-types": "^15.8.1"
},
"peerDependencies": {
"@fortawesome/fontawesome-svg-core": "~1 || ~6",
"react": ">=16.3"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.11",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
@ -22689,6 +22766,57 @@
"integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==",
"dev": true
},
"@fortawesome/fontawesome-common-types": {
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz",
"integrity": "sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA==",
"dev": true
},
"@fortawesome/fontawesome-svg-core": {
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.2.tgz",
"integrity": "sha512-gjYDSKv3TrM2sLTOKBc5rH9ckje8Wrwgx1CxAPbN5N3Fm4prfi7NsJVWd1jklp7i5uSCVwhZS5qlhMXqLrpAIg==",
"dev": true,
"requires": {
"@fortawesome/fontawesome-common-types": "6.4.2"
}
},
"@fortawesome/free-brands-svg-icons": {
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.4.2.tgz",
"integrity": "sha512-LKOwJX0I7+mR/cvvf6qIiqcERbdnY+24zgpUSouySml+5w8B4BJOx8EhDR/FTKAu06W12fmUIcv6lzPSwYKGGg==",
"dev": true,
"requires": {
"@fortawesome/fontawesome-common-types": "6.4.2"
}
},
"@fortawesome/free-regular-svg-icons": {
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.4.2.tgz",
"integrity": "sha512-0+sIUWnkgTVVXVAPQmW4vxb9ZTHv0WstOa3rBx9iPxrrrDH6bNLsDYuwXF9b6fGm+iR7DKQvQshUH/FJm3ed9Q==",
"dev": true,
"requires": {
"@fortawesome/fontawesome-common-types": "6.4.2"
}
},
"@fortawesome/free-solid-svg-icons": {
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.2.tgz",
"integrity": "sha512-sYwXurXUEQS32fZz9hVCUUv/xu49PEJEyUOsA51l6PU/qVgfbTb2glsTEaJngVVT8VqBATRIdh7XVgV1JF1LkA==",
"dev": true,
"requires": {
"@fortawesome/fontawesome-common-types": "6.4.2"
}
},
"@fortawesome/react-fontawesome": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz",
"integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==",
"dev": true,
"requires": {
"prop-types": "^15.8.1"
}
},
"@humanwhocodes/config-array": {
"version": "0.11.11",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",

View File

@ -1,8 +1,8 @@
{
"name": "react-list-ui-library",
"version": "0.0.1",
"version": "1.0.0",
"description": "A starter kit for create a React component Library with storybook",
"main": "dist/index.js",
"main": "dist/index.js",
"scripts": {
"start": "npm run storybook",
"build": "webpack",
@ -39,6 +39,11 @@
"@babel/preset-react": "^7.22.15",
"@babel/preset-typescript": "^7.23.0",
"@babel/register": "^7.22.15",
"@fortawesome/fontawesome-svg-core": "^6.4.2",
"@fortawesome/free-brands-svg-icons": "^6.4.2",
"@fortawesome/free-regular-svg-icons": "^6.4.2",
"@fortawesome/free-solid-svg-icons": "^6.4.2",
"@fortawesome/react-fontawesome": "^0.2.0",
"@mdx-js/react": "^2.3.0",
"@storybook/addon-actions": "^7.4.6",
"@storybook/addon-docs": "^7.4.6",

View File

@ -0,0 +1,21 @@
import React, { FC, MouseEventHandler } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPlus } from '@fortawesome/free-solid-svg-icons';
import "./style.scss";
type TAddButtonProps = {
/**
* Is this the onClick Event of the button.
*/
onClick?: MouseEventHandler<HTMLButtonElement> | undefined
};
const AddButton: FC<TAddButtonProps> = ({ onClick }) => {
return (
<button type="button" className="addButton" onClick={onClick}>
<FontAwesomeIcon icon={faPlus} />
</button>
);
};
export { AddButton, TAddButtonProps }

View File

@ -0,0 +1,25 @@
.addButton {
border: none;
height: 40px;
width: 40px;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14),
0 3px 1px -2px rgba(0, 0, 0, 0.12), 0 1px 5px 0 rgba(0, 0, 0, 0.2);
// background-color: #FF6955;
background-color: #71b9f5;
border-radius: 25px;
cursor: pointer;
transition: 0.3s ease-in-out;
font-size: 18px;
&:hover,
&:focus,
&:active {
// background-color: #bc2d1a;
background-color: #185c94;
}
svg {
color: #ffffff;
}
}

View File

@ -0,0 +1,40 @@
import React from 'react';
import { StoryFn, Meta } from '@storybook/react';
import { Button, ButtonTypes } from '@components';
// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
export default {
title: 'List Design System/Button',
component: Button,
argTypes: {
type: {
options: [
ButtonTypes.ADD,
ButtonTypes.REMOVE
],
control: {
type: 'select',
labels: {
[ButtonTypes.ADD]: 'ADD',
[ButtonTypes.REMOVE]: 'REMOVE'
}
},
}
}
} as Meta<typeof Button>;
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
const Template: StoryFn<typeof Button> = (args) => <Button {...args} />;
export const AddButton = Template.bind({});
export const RemoveButton = Template.bind({});
// More on args: https://storybook.js.org/docs/react/writing-stories/args
AddButton.args = {
type: ButtonTypes.ADD,
onClick: () => alert('AddButton Clicked')
};
RemoveButton.args = {
type: ButtonTypes.REMOVE,
onClick: () => alert('RemoveButton Clicked')
};

View File

@ -0,0 +1,21 @@
import React, { FC, MouseEventHandler } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTrashCan } from '@fortawesome/free-solid-svg-icons';
import "./style.scss";
type TRemoveButtonProps = {
/**
* Is this the onClick Event of the button.
*/
onClick?: MouseEventHandler<HTMLButtonElement> | undefined
};
const RemoveButton: FC<TRemoveButtonProps> = ({ onClick }) => {
return (
<button type="button" className="removeButton" onClick={onClick}>
<FontAwesomeIcon icon={faTrashCan} />
</button>
);
};
export { RemoveButton, TRemoveButtonProps }

View File

@ -0,0 +1,19 @@
.removeButton {
border: none;
height: 40px;
width: 40px;
background-color: transparent;
cursor: pointer;
transition: 0.3s ease-in-out;
font-size: 18px;
svg {
color: #FF6955;
transition: 0.3s ease-in-out;
&:hover,
&:focus,
&:active {
color: #bc2d1a;
}
}
}

View File

@ -0,0 +1,36 @@
import React, { FC, MouseEventHandler } from 'react';
import { AddButton } from './AddButton';
import { RemoveButton } from './RemoveButton';
type TButtonProps = {
/**
* Is this the title of the card.
*/
type?: ButtonTypes,
/**
* Is this the onClick Event of the button.
*/
onClick?: MouseEventHandler<HTMLButtonElement> | undefined
};
enum ButtonTypes {
ADD = 'ADD',
REMOVE = 'REMOVE'
}
const Button: FC<TButtonProps> = ({ type = ButtonTypes.ADD, onClick }) => {
return(
<>
{
type === ButtonTypes.ADD &&
<AddButton onClick={onClick} />
}
{
type === ButtonTypes.REMOVE &&
<RemoveButton onClick={onClick} />
}
</>
)
};
export { Button, TButtonProps, ButtonTypes }

View File

@ -1,25 +0,0 @@
import React, { FC } from "react";
import "./style.scss";
type TCardProps = {
/**
* Is this the title of the card.
*/
title?: string,
/**
* Is this the child component of the card. (The content)
*/
children?: JSX.Element,
};
const Card: FC<TCardProps> = ({ title, children}) => {
return (
<div className="Card">
<div className="Title">{title}</div>
<div className="Content">{children}</div>
</div>
);
};
export { Card, TCardProps }

View File

@ -1,25 +0,0 @@
.Card{
background-color: #20b0f3;
border-radius: 10px;
border: 3px solid #20b0f3;
color: #ffffff;
font-weight: 700;
margin: 10px;
display: flex;
flex-direction: column;
min-width: 500px;
max-width: 500px;
.Title {
padding: 15px 0;
display: flex;
justify-content: center;
}
.Content {
flex: 1;
padding: 30px;
background-color: #ffffff;
color: #000000;
}
}

View File

@ -0,0 +1,42 @@
import React from 'react';
import { StoryFn, Meta } from '@storybook/react';
import { ContainerList, List, Status } from '@components';
// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
export default {
title: 'List Design System/ContainerList',
component: ContainerList,
} as Meta<typeof ContainerList>;
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
const Template: StoryFn<typeof ContainerList> = (args) => <ContainerList {...args} />;
export const Basic = Template.bind({});
// More on args: https://storybook.js.org/docs/react/writing-stories/args
Basic.args = {
title: 'List Title',
children:
<>
<List
list={[
{
name: 'First Item',
status: Status.TODO
},
{
name: 'Second Item',
status: Status.DONE
},
{
name: 'Third Item',
status: Status.TODO
},
{
name: 'Fourth Item',
status: Status.DONE
}
]}
placeholderInput='Add a Item'
/>
</>
};

View File

@ -0,0 +1,24 @@
import React, { FC } from 'react';
import './style.scss';
type TContainerListProps = {
/**
* Is this the title of the card.
*/
title?: string,
/**
* Is this the child component of the card. (The content)
*/
children?: JSX.Element,
};
const ContainerList: FC<TContainerListProps> = ({ title, children }) => {
return (
<div className="ContainerList">
<div className="title">{title}</div>
<div className="content">{children}</div>
</div>
);
};
export { ContainerList, TContainerListProps }

View File

@ -0,0 +1,29 @@
.ContainerList{
position: relative;
background-color: #FEF6F4;
border-radius: 10px;
color: #000000;
font-weight: 200;
display: flex;
flex-direction: column;
min-width: 100px;
max-width: 600px;
font-family: 'Roboto', 'sans-serif';
left: 50%;
width: 100%;
transform: translateX(-50%);
.title {
padding: 20px 0;
display: flex;
justify-content: center;
font-weight: 500;
}
.content {
flex: 1;
padding: 10px 30px 30px 30px;
background-color: #FEF6F4;
color: #000000;
}
}

View File

@ -1,19 +1,19 @@
import React from 'react';
import { StoryFn, Meta } from '@storybook/react';
import { Card } from '@components';
import { Input } from '@components';
// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
export default {
title: 'Example/Card',
component: Card,
} as Meta<typeof Card>;
title: 'List Design System/Input',
component: Input,
} as Meta<typeof Input>;
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
const Template: StoryFn<typeof Card> = (args) => <Card {...args} />;
const Template: StoryFn<typeof Input> = (args) => <Input {...args} />;
export const Basic = Template.bind({});
// More on args: https://storybook.js.org/docs/react/writing-stories/args
Basic.args = {
title: 'Test Title',
children: <p>Test Content</p>,
placeholder: 'Basic Input',
onChange: (e) => { console.log(e.target.value) }
};

View File

@ -0,0 +1,22 @@
import React, { FC, ChangeEventHandler } from 'react';
import './style.scss';
export type TInputProps = {
/**
* Is this the text you want to add to the input placeholder
*/
placeholder?: string
/**
* Is this the onChange event of the input
*/
onChange?: ChangeEventHandler<HTMLInputElement>
};
export const Input:FC<TInputProps> = ({
placeholder = '',
onChange = (e) => {}
}) => {
return(
<input className='input' placeholder={placeholder} type='text' onChange={onChange} />
)
}

View File

@ -0,0 +1,11 @@
.input{
width: 100%;
height: 30px;
border: 1px solid #ccc;
border-radius: 50px;
font-size: 16px;
outline: none;
&:focus{
border: 1px solid #000;
}
}

View File

@ -0,0 +1,49 @@
import React, { useState } from 'react';
import { StoryFn, Meta } from '@storybook/react';
import { Item, Status } from '@components';
export default {
title: 'List Design System/Item',
component: Item,
argTypes: {
status: {
options: [
Status.TODO,
Status.DONE
],
control: {
type: 'select',
labels: {
[Status.TODO]: 'TODO',
[Status.DONE]: 'DONE'
}
},
}
}
} as Meta<typeof Item>;
const TemplateBasic: StoryFn<typeof Item> = (args) => {
return <Item {...args} />;
};
const TemplateWithHandleChange: StoryFn<typeof Item> = (args) => {
const [status, setStatus] = useState(args.status);
const handleChange = (event) => {
setStatus(event.target.checked ? Status.DONE : Status.TODO);
};
return <Item {...args} status={status} handleChange={handleChange} />;
};
export const Basic = TemplateBasic.bind({});
Basic.args = {
name: 'Item Name',
handleChange: () => {}
};
export const WithHandleChange = TemplateWithHandleChange.bind({});
WithHandleChange.args = {
name: 'Item Name'
};

View File

@ -0,0 +1,14 @@
import React from 'react';
import { Item, Status } from '@components';
describe('Testing Card Component', () => {
beforeEach(() => {
cy.mount(<Item name='Item Test' status={Status.DONE} />);
})
it('Show Item name', () => {
cy.get('span').contains('Item Test');
})
it('Show Item as Checked', () => {
cy.get('input').should('be.checked');
})
})

View File

@ -0,0 +1,17 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { Item, Status } from '@components';
describe('<App/> Component', () => {
beforeEach(() => {
// fetchMock.resetMocks();
render(<Item name='Item Test' status={Status.DONE} handleChange={ () => {}} />)
});
it('Show Item Name', async () => {
/* fetchMock.mockResponseOnce(JSON.stringify({
//First Data Fetch
data: 'data'
})); */
screen.getByText('Item Test')
})
})

View File

@ -0,0 +1,63 @@
import React, { FC, ChangeEventHandler } from 'react';
import './style.scss';
import { joinClassNames } from '@utils/index';
type TItemProps = {
/**
* Is this the name of the item.
*/
name?: string,
/**
* Is this the status of the item.
*/
status?: Status,
/**
* Is this the on Event triggered by the checkbox.
*/
handleChange?: ChangeEventHandler<HTMLInputElement>
};
type TItem = {
name: string,
status: Status
}
enum Status {
TODO = 'TODO',
DONE = 'DONE'
}
export const getStatusClass = ({ status }: { status: Status }) => {
switch (status) {
case 'TODO':
return 'to-do';
case 'DONE':
return 'done';
default:
return ''
}
}
const Item: FC<TItemProps> = ({
name,
status = Status.TODO,
handleChange
}) => {
const classNames = joinClassNames(getStatusClass({ status }));
return (
<div className="round">
<input
id={name}
type="checkbox"
checked={status === Status.DONE}
name={name}
className={classNames}
onChange={handleChange}
/>
<label htmlFor={name}/>
<span className={classNames}>{name}</span>
</div>
);
};
export { Item, TItemProps, TItem, Status }

View File

@ -0,0 +1,57 @@
.round {
position: relative;
label {
background-color: #fff;
border: 1px solid #ccc;
border-radius: 50%;
cursor: pointer;
height: 19px;
left: 0;
position: absolute;
top: 0;
width: 19px;
}
label:after {
border: 2px solid #fff;
border-top: none;
border-right: none;
content: "";
height: 6px;
left: 2.5px;
opacity: 0;
position: absolute;
top: 4px;
transform: rotate(-45deg);
width: 11px;
}
input[type="checkbox"] {
visibility: hidden;
}
input[type="checkbox"]:checked + label {
background-color: #66bb6a;
border-color: #66bb6a;
}
input[type="checkbox"]:checked + label:after {
opacity: 1;
}
span {
position: relative;
top: 0px;
margin-left: 10px;
font-family: 'Roboto', 'sans-serif';
color: #77838F;
font-weight: 400;
&.done {
text-decoration: line-through;
color: #77838F;
}
}
}

View File

@ -0,0 +1,28 @@
import React from 'react';
import { StoryFn, Meta } from '@storybook/react';
import { List, Status } from '@components';
// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
export default {
title: 'List Design System/List',
component: List,
} as Meta<typeof List>;
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
const Template: StoryFn<typeof List> = (args) => <List {...args} />;
export const Basic = Template.bind({});
// More on args: https://storybook.js.org/docs/react/writing-stories/args
Basic.args = {
list: [
{
name: 'Item 1',
status: Status.TODO
},
{
name: 'Item 2',
status: Status.DONE
}
],
placeholderInput: 'Add a Item',
};

View File

@ -0,0 +1,71 @@
import React, { FC, MouseEventHandler, ChangeEventHandler } from 'react';
import { TItem, Item, ButtonTypes, Button, Input } from '@components';
import './style.scss';
import { on } from 'events';
type TListProps = {
/**
* Is this the title of the card.
*/
list: TItem[]
/**
* Is this the title of the card.
*/
placeholderInput?: string
/**
* Is this the onChange event of the input
*/
onChangeInput?: ChangeEventHandler<HTMLInputElement>
/**
* Is this the onClick Event of the button.
*/
onClickAddItem?: MouseEventHandler<HTMLButtonElement> | undefined
/**
* Is this the onClick Event of the button.
*/
onClickRemoveItem?: MouseEventHandler<HTMLButtonElement> |undefined
};
const List: FC<TListProps> = ({
list,
placeholderInput,
onChangeInput,
onClickAddItem,
onClickRemoveItem
}) => {
return (
<div className="List">
<table>
<tbody>
{ list !== undefined && list.map((item, index) => (
<tr key={index}>
<td><Item name={item.name} status={item.status} /></td>
<td>
<div className="delete-button-container">
<Button
type={ButtonTypes.REMOVE}
onClick={onClickRemoveItem}
/>
</div>
</td>
</tr>
))}
</tbody>
</table>
<div>
<Input
placeholder={placeholderInput}
onChange={onChangeInput}
/>
</div>
<div className="button-container">
<Button
type={ButtonTypes.ADD}
onClick={onClickAddItem}
/>
</div>
</div>
);
};
export { List, TListProps }

View File

@ -0,0 +1,40 @@
.List {
position: relative;
display: flex;
flex-direction: column;
max-width: 600px;
padding: 15px;
border-radius: 8px;
background: #FFF;
box-shadow: 0px 2px 48px 0px rgba(0, 0, 0, 0.08);
//left: 50%;
//transform: translateX(50%);
table {
margin: 10px 0 30px 0;
tbody {
tr {
td {
padding: 5px 0 5px 0;
border-bottom: 1px solid #E0E0E0;
font-size: 14px;
color: #4A4A4A;
font-weight: 500;
line-height: 1.5;
.delete-button-container{
position: relative;
width: 100%;
left: 65%;
}
}
}
}
}
.button-container {
position: relative;
left: 90%;
top: 35px;
}
}

View File

@ -1,14 +0,0 @@
import React from 'react';
import { Card } from '@components';
describe('Testing Card Component', () => {
beforeEach(() => {
cy.mount(<Card title='Test Title'><p>Test Content</p></Card>);
})
it('Show Title', () => {
cy.get('div').contains('Test Title');
})
it('Show Child Component', () => {
cy.get('p').contains('Test Content');
})
})

View File

@ -1,24 +0,0 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { Card } from '@components';
describe('<App/> Component', () => {
beforeEach(() => {
// fetchMock.resetMocks();
render(<Card title='Test Title'><p>Test Content</p></Card>)
});
it('Show Title', async () => {
/* fetchMock.mockResponseOnce(JSON.stringify({
//First Data Fetch
data: 'data'
})); */
screen.getByText('Test Title')
})
it('Show Child Component', async () => {
/* fetchMock.mockResponseOnce(JSON.stringify({
//First Data Fetch
data: 'data'
})); */
screen.getByText('Test Content')
})
})

View File

@ -1 +1,7 @@
export * from './Card';
import '@styles/global.scss'
export * from './ContainerList';
export * from './List';
export * from './Item';
export * from './Button';
export * from './Input';

View File

@ -8,7 +8,7 @@ import Plugin from './assets/plugin.svg';
import Repo from './assets/repo.svg';
import StackAlt from './assets/stackalt.svg';
<Meta title="Example/Introduction" />
<Meta title="List Design System/Introduction" />
<style>
{`

1
src/styles/global.scss Normal file
View File

@ -0,0 +1 @@
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap');

1
src/utils/index.ts Normal file
View File

@ -0,0 +1 @@
export const joinClassNames = (...classes: string[]) => classes.filter(className => className).join(' ')

View File

@ -33,7 +33,10 @@
"noImplicitAny": false,
"paths": {
"@components/*": ["src/components/*"],
"@components": ["src/components"]
"@components": ["src/components"],
"@styles": ["src/styles"],
"@utils/*": ["src/utils/*"],
"@utils": ["src/utils"]
}
},
"include": [