diff --git a/src/components/Accordion/Accordion.styles.ts b/src/components/Accordion/Accordion.styles.ts index cf3b9bb88..da6e4454b 100644 --- a/src/components/Accordion/Accordion.styles.ts +++ b/src/components/Accordion/Accordion.styles.ts @@ -63,6 +63,8 @@ export const DivStyled = styled.div` ${resetCSS} ${fonts.text} margin: 0 6px; + min-width: 100px; + text-align: right; } svg { diff --git a/src/components/CreditCard/CreditCard.stories.tsx b/src/components/CreditCard/CreditCard.stories.tsx new file mode 100644 index 000000000..72670b94d --- /dev/null +++ b/src/components/CreditCard/CreditCard.stories.tsx @@ -0,0 +1,38 @@ +import { ComponentStory, ComponentMeta } from '@storybook/react'; +import CreditCard from './CreditCard'; +import React from 'react'; + +export default { + title: 'UI/CreditCard', + component: CreditCard, +} as ComponentMeta; + +const Template: ComponentStory = (args) => { + return ; +}; + +export const Mastercard = Template.bind({}); +Mastercard.args = { + id: 'marty-mc-fly-cc-id', + isExpired: false, + name: 'Marty McFly', + type: 'visa', + lastDigits: '1177', + expiresAt: { + month: '04', + year: '22', + }, +}; + +export const MastercardExpired = Template.bind({}); +MastercardExpired.args = { + id: 'marty-mc-fly-cc-id', + isExpired: true, + name: 'Marty McFly', + type: 'mastercard', + lastDigits: '1177', + expiresAt: { + month: '11', + year: '21', + }, +}; diff --git a/src/components/CreditCard/CreditCard.styles.ts b/src/components/CreditCard/CreditCard.styles.ts new file mode 100644 index 000000000..49fa812a9 --- /dev/null +++ b/src/components/CreditCard/CreditCard.styles.ts @@ -0,0 +1,125 @@ +import styled, { css } from 'styled-components'; +import resetCSS from '../../styles/reset'; +import fonts from '../../styles/fonts'; +import colors, { colorPercentage } from '../../styles/colors'; +import { CreditCardProps } from './types'; + +type TStyleProps = Pick; + +export const DivStyledCreditCard = styled.div` + ${resetCSS}; + ${fonts.text}; + background: linear-gradient( + 113.54deg, + rgba(60, 87, 140, 0.5) 14.91%, + rgba(70, 86, 169, 0.5) 43.21%, + rgba(125, 150, 217, 0.345) 44.27%, + rgba(129, 161, 225, 0.185) 55.76% + ), + linear-gradient(151.07deg, #141659 33.25%, #4152a7 98.24%); + border: 2px solid ${colorPercentage(colors.white, 40)}; + border-radius: 16px; + display: grid; + gap: 20%; + padding: 16px; + height: 154px; + width: 277px; + + label { + height: 10px; + } + + ${(p) => (p.isExpired ? expiredStyles : p.pressed ? pressedStyles : '')} + + @keyframes hoverEffect { + from { + background: linear-gradient( + 113.54deg, + rgba(60, 87, 140, 0.5) 18.91%, + rgba(70, 86, 169, 0.5) 44.21%, + rgba(125, 150, 217, 0.345) 48.27%, + rgba(129, 161, 225, 0.185) 55.76% + ), + linear-gradient(151.07deg, #141659 33.25%, #4152a7 98.24%); + } + from { + background: linear-gradient( + 113.54deg, + rgba(60, 87, 140, 0.5) 19.91%, + rgba(70, 86, 169, 0.5) 45.21%, + rgba(125, 150, 217, 0.345) 49.27%, + rgba(129, 161, 225, 0.185) 55.76% + ), + linear-gradient(151.07deg, #141659 33.25%, #4152a7 98.24%); + } + to { + background: linear-gradient( + 113.54deg, + rgba(60, 87, 140, 0.5) 20.91%, + rgba(70, 86, 169, 0.5) 46.21%, + rgba(125, 150, 217, 0.345) 50.27%, + rgba(129, 161, 225, 0.185) 55.76% + ), + linear-gradient(151.07deg, #141659 33.25%, #4152a7 98.24%); + } + } + + :hover { + animation-name: hoverEffect; + animation-duration: 40ms; + background: linear-gradient( + 113.54deg, + rgba(60, 87, 140, 0.5) 16.91%, + rgba(70, 86, 169, 0.5) 45.21%, + rgba(125, 150, 217, 0.345) 52.27%, + rgba(129, 161, 225, 0.185) 55.76% + ), + linear-gradient(151.07deg, #141659 33.25%, #4152a7 98.24%); + cursor: pointer; + } +`; + +export const DivStyledFlex = styled.div` + display: flex; + justify-content: space-between; + align-items: center; +`; + +export const DivStyledFlexText = styled.div` + color: white; + display: flex; + gap: 15px; +`; + +export const DivStyledRemove = styled.div` + display: grid; + place-items: center; + + :hover { + cursor: pointer; + } +`; + +export const PStyledDigits = styled.p` + ${fonts.semiBold}; + color: ${colors.white}; + font-size: 18px; + line-height: 24px; + margin: 0; +`; + +export const PStyledText = styled.p` + ${fonts.semiBold} + color: ${colors.white}; + font-size: 12px; + line-height: 16px; + margin: 0; +`; + +const expiredStyles = css` + border-color: ${colors.red}; +`; + +const pressedStyles = css` + border-color: ${colors.green}; +`; diff --git a/src/components/CreditCard/CreditCard.tsx b/src/components/CreditCard/CreditCard.tsx new file mode 100644 index 000000000..a2dc0b7c0 --- /dev/null +++ b/src/components/CreditCard/CreditCard.tsx @@ -0,0 +1,55 @@ +import React, { useState } from 'react'; +import { Radios } from '../Radios'; +import { Icon } from '../Icon'; +import { Logo } from '../Logo'; +import { iconTypes } from '../Icon/collection'; +import colors from '../../styles/colors'; +import { CreditCardProps } from './types'; +import { + DivStyledCreditCard, + DivStyledFlex, + DivStyledFlexText, + DivStyledRemove, + PStyledDigits, + PStyledText, +} from './CreditCard.styles'; + +const CreditCard: React.FC = ({ + expiresAt, + id, + isExpired, + lastDigits, + name, + onRemove, + type, +}: CreditCardProps) => { + const [pressed, setPressed] = useState(false); + + return ( + setPressed(!pressed)} + pressed={pressed} + > + + setPressed(!pressed)} + /> + + + {`•••• ${lastDigits}`} + + + {name} + {`${expiresAt.month} / ${expiresAt.year}`} + + + + + ); +}; + +export default CreditCard; diff --git a/src/components/CreditCard/index.tsx b/src/components/CreditCard/index.tsx new file mode 100644 index 000000000..70b3e6350 --- /dev/null +++ b/src/components/CreditCard/index.tsx @@ -0,0 +1,2 @@ +export { default as CreditCard } from './CreditCard'; +export { CreditCardProps } from './types'; diff --git a/src/components/CreditCard/types.ts b/src/components/CreditCard/types.ts new file mode 100644 index 000000000..3f015d8c1 --- /dev/null +++ b/src/components/CreditCard/types.ts @@ -0,0 +1,50 @@ +export interface CreditCardProps { + /** + * set the id of credit-card + */ + id?: string; + + /** + * set date of expiration MM/YY + */ + expiresAt: { + /** + * 01 -> Jan, 02 -> Feb, ..., 12 -> Dec + */ + month: string; + /** + * 22 -> 2022 , 23 -> 2023 ..., 40 -> 2040 + */ + year: string; + }; + + /** + * set if credit-card is expired + */ + isExpired: boolean; + + /** + * last 4 digits of credit-card-number + */ + lastDigits: string | number; + + /** + * set full-name of credit-card holder + */ + name: string; + + /** + * run function when remove icon is clicked + */ + onRemove?: () => void; + + /** + * set pressed + */ + pressed?: boolean; + + /** + * set type + */ + type: 'mastercard' | 'visa'; +} diff --git a/src/components/Icon/collection.tsx b/src/components/Icon/collection.tsx index 72c898220..c8cb9690b 100644 --- a/src/components/Icon/collection.tsx +++ b/src/components/Icon/collection.tsx @@ -156,7 +156,6 @@ export enum iconTypes { const collection = { arrowCircleDownIcon, - mailIcon, arrowCircleLeftIcon, arrowCircleRightIcon, bellIcon, @@ -200,6 +199,7 @@ const collection = { lockClosedIcon, lockOpenIcon, logOutIcon, + mailIcon, maximizeIcon, messageCircleIcon, minimizeIcon, diff --git a/src/components/Icon/icons/mastercard.tsx b/src/components/Icon/icons/mastercard.tsx new file mode 100644 index 000000000..d88813b4f --- /dev/null +++ b/src/components/Icon/icons/mastercard.tsx @@ -0,0 +1,40 @@ +import React from 'react'; + +const mastercardIcon = ( + fill: string, + size: number, + style?: React.CSSProperties, +) => { + return ( + + ); +}; + +export default mastercardIcon; diff --git a/src/components/Icon/icons/visa.tsx b/src/components/Icon/icons/visa.tsx new file mode 100644 index 000000000..69876cb1e --- /dev/null +++ b/src/components/Icon/icons/visa.tsx @@ -0,0 +1,24 @@ +import React from 'react'; + +const visaIcon = (fill: string, size: number, style?: React.CSSProperties) => { + return ( + + ); +}; + +export default visaIcon; diff --git a/src/components/Illustrations/index.tsx b/src/components/Illustrations/index.tsx index 5c696244b..0378c1186 100644 --- a/src/components/Illustrations/index.tsx +++ b/src/components/Illustrations/index.tsx @@ -1,2 +1,2 @@ -export { default as Illustration } from "./Illustration"; -export { IllustrationProps } from "./types"; \ No newline at end of file +export { default as Illustration } from './Illustration'; +export type { IllustrationProps } from './types'; diff --git a/src/components/Illustrations/types.ts b/src/components/Illustrations/types.ts index f400a3a60..2f0872daa 100644 --- a/src/components/Illustrations/types.ts +++ b/src/components/Illustrations/types.ts @@ -21,8 +21,8 @@ export const logoState = [ 'marketplace', ] as const; export type Logo = typeof logoState[number]; - export type Size = number | string; + export interface IllustrationProps { id?: string; diff --git a/src/components/Radios/Radios.stories.tsx b/src/components/Radios/Radios.stories.tsx index 813245133..e4b2b0d3c 100644 --- a/src/components/Radios/Radios.stories.tsx +++ b/src/components/Radios/Radios.stories.tsx @@ -37,3 +37,20 @@ RadiosWithLongText.args = { ], title: 'What is your favorite bitCoin quote of 2021?', }; + +export const RadiosSetDefault = Template.bind({}); +RadiosSetDefault.args = { + id: 'radios', + items: ['Charmander', 'Squirtle', 'Bulbasaur', 'Pikachu'], + title: "Who's that Pokemon?", + checked: true, +}; + +export const RadiosSetParticular = Template.bind({}); +RadiosSetParticular.args = { + id: 'radios', + items: ['Charmander', 'Squirtle', 'Bulbasaur', 'Pikachu'], + title: "Who's that Pokemon?", + checked: true, + setWhichIsChecked: 2, +}; diff --git a/src/components/Radios/Radios.test.tsx b/src/components/Radios/Radios.test.tsx index 637b988b3..72434e07f 100644 --- a/src/components/Radios/Radios.test.tsx +++ b/src/components/Radios/Radios.test.tsx @@ -294,3 +294,5 @@ describe('Radios - RadiosWithLongText', () => { expect(testEvent).toHaveBeenCalledWith(input); }); }); + +// TODO @bill add tests for SetDefault and SetParticular diff --git a/src/components/Radios/Radios.tsx b/src/components/Radios/Radios.tsx index ee6d205c7..321fd3549 100644 --- a/src/components/Radios/Radios.tsx +++ b/src/components/Radios/Radios.tsx @@ -20,11 +20,13 @@ const RadioButtonStyled = styled.input` `; const Radios: React.FC = ({ + checked, id, items, onChange, - validation, + setWhichIsChecked = 0, title, + validation, }) => { const formattedID = id.replace(/\s/g, '-'); @@ -37,6 +39,7 @@ const Radios: React.FC = ({ {items.map((value, i) => ( ) => void; + /** + * you can have a radio checked by default, it always checks the first (position 0) unless you pass a valid array position to setWhichIsChecked + */ + checked?: boolean; + + /** + * if checked is set to true but you don't want to check the first radio, you can pass an array position number here. Note this should not be greater or less than the array.length + */ + setWhichIsChecked?: number; + /** * You can validate your radios */ diff --git a/src/styles/fonts.ts b/src/styles/fonts.ts index 60c5604a6..03c829989 100644 --- a/src/styles/fonts.ts +++ b/src/styles/fonts.ts @@ -9,7 +9,7 @@ const heading = css` font-size: 36px; font-style: normal; font-weight: 600; - letter-spacing: 0px; + letter-spacing: 0; `; const h1 = css` @@ -40,7 +40,7 @@ const h5 = css` `; const ibm = css` - font-family: IBM Plex Mono; + font-family: IBM Plex Mono, sans-serif; font-size: 16px; font-style: normal; `; @@ -53,7 +53,7 @@ const text = css` font-size: 16px; font-style: normal; font-weight: 400; - letter-spacing: 0em; + letter-spacing: 0; line-height: 24px; `; @@ -72,12 +72,12 @@ const textBold700 = css` const textSmall = css` font-size: 14px; font-weight: 400; - letter-spacing: 0px; + letter-spacing: 0; line-height: 24px; `; const semiBold = css` - ${text} + ${text}; font-weight: 600; `;