From 2f8cd94fdab497539c061d51cf19a840711971b5 Mon Sep 17 00:00:00 2001 From: MacAlmighty Date: Fri, 7 Jan 2022 19:38:20 -0500 Subject: [PATCH 1/2] Menu Item Card + Limited time banner initial commit New branch since the old one was on an old version of storybook and had other issues. Added new functions to get values of item image and prices. Changed HTML so sale tag and loyalty points are aligned. Made modifications to SaleTag and limited time banner to make the dimensions adjustable and fit with my menu item card. Also added loyalty points since it was never merged to main. --- .../LimitedTimeBanner.stories.tsx | 4 +- .../LimitedTimeBanner/LimitedTimeBanner.tsx | 16 +- .../LoyaltyPoints/LoyaltyPoints.stories.tsx | 17 ++ .../LoyaltyPoints/LoyaltyPoints.tsx | 42 +++ .../MenuItemCard/MenuItemCard.stories.tsx | 86 ++++++ src/Containers/MenuItemCard/MenuItemCard.tsx | 248 ++++++++++++++++++ src/Containers/SaleTag/SaleTag.stories.tsx | 1 - src/Containers/SaleTag/SaleTag.tsx | 7 +- src/Containers/index.ts | 2 + src/Themes/MainTheme.ts | 8 +- 10 files changed, 420 insertions(+), 11 deletions(-) create mode 100644 src/Containers/LoyaltyPoints/LoyaltyPoints.stories.tsx create mode 100644 src/Containers/LoyaltyPoints/LoyaltyPoints.tsx create mode 100644 src/Containers/MenuItemCard/MenuItemCard.stories.tsx create mode 100644 src/Containers/MenuItemCard/MenuItemCard.tsx diff --git a/src/Containers/LimitedTimeBanner/LimitedTimeBanner.stories.tsx b/src/Containers/LimitedTimeBanner/LimitedTimeBanner.stories.tsx index 48b3a8881..5b7f2f31f 100644 --- a/src/Containers/LimitedTimeBanner/LimitedTimeBanner.stories.tsx +++ b/src/Containers/LimitedTimeBanner/LimitedTimeBanner.stories.tsx @@ -7,7 +7,9 @@ export default { title: 'Components/LimitedTimeBanner', component: LimitedTimeBanner, args: { - minsRemaining: 120, + minsRemaining: 120, + bannerWidth: 300, + bannerHeight: 40, }, } as Meta; diff --git a/src/Containers/LimitedTimeBanner/LimitedTimeBanner.tsx b/src/Containers/LimitedTimeBanner/LimitedTimeBanner.tsx index 4cd4af9c2..745ef52aa 100644 --- a/src/Containers/LimitedTimeBanner/LimitedTimeBanner.tsx +++ b/src/Containers/LimitedTimeBanner/LimitedTimeBanner.tsx @@ -9,14 +9,18 @@ const MINUTES_IN_YEAR = 524160; export interface LimitedTimeBannerProps extends MainInterface { /* minutes until time runs out */ - minsRemaining: number, + minsRemaining: number, + bannerWidth?: number, + bannerHeight?: number, } export const LimitedTimeBanner: React.FC = ({ minsRemaining, + bannerWidth, + bannerHeight, ...props }): React.ReactElement => ( - + {alterTime(minsRemaining)} Remaining @@ -47,14 +51,16 @@ const alterTime = (value:number) => { return(moment.duration(value,'minutes').humanize()); } -const BannerBox = styled.div` +const BannerBox = styled.div` ${({theme}):string => ` font-family: ${theme.font.family}; color: ${theme.colors.background}}; background-color: ${theme.colors.bannerBackgroundColor}; `} - width:350px; - height:40px; + ${({ bannerWidth, bannerHeight }): string => ` + width: ${bannerWidth}px; + height: ${bannerHeight}px; + `} `; const Icon = styled(Clock)` diff --git a/src/Containers/LoyaltyPoints/LoyaltyPoints.stories.tsx b/src/Containers/LoyaltyPoints/LoyaltyPoints.stories.tsx new file mode 100644 index 000000000..47c93d279 --- /dev/null +++ b/src/Containers/LoyaltyPoints/LoyaltyPoints.stories.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { Meta, Story } from '@storybook/react'; +import { LoyaltyPointsProps, LoyaltyPoints } from '../../index'; + +export default { + + title: 'Components/LoyaltyPoints', + component: LoyaltyPoints, + args: { + loyaltyamount: 10, + loyaltypointlimit: 100, + } +} as Meta; + +export const Basic: Story = (args) => ( + +) \ No newline at end of file diff --git a/src/Containers/LoyaltyPoints/LoyaltyPoints.tsx b/src/Containers/LoyaltyPoints/LoyaltyPoints.tsx new file mode 100644 index 000000000..1f25df384 --- /dev/null +++ b/src/Containers/LoyaltyPoints/LoyaltyPoints.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import styled from 'styled-components'; +import { Stars } from '@styled-icons/material/Stars'; + +export interface LoyaltyPointsProps + extends React.HTMLAttributes { + loyaltyamount: number; //the amount of loyalty points that the user gets with purchase, used to display on component. + loyaltypointlimit: number; //the limit of loyalty points before it says 99+ instead. +} + +export const LoyaltyPoints: React.FC = ({ + loyaltyamount, + loyaltypointlimit, + ...props +}): React.ReactElement => +
+{getLoyaltyPoints(loyaltyamount, loyaltypointlimit)}
+
; + +function getLoyaltyPoints(loyaltypoints: number, loyaltypointlimit: number) { + if (loyaltypoints >= loyaltypointlimit) { + return loyaltypointlimit + "⁺"; + } + return Math.round(loyaltypoints); +} + +const LoyaltyPointsBox = styled.div` + ${({ theme }): string => ` + font-family: ${theme.font.family}; + font-size: 20px; + width: fit-content; + min-width: 60px; + height: 30px; + border-radius:0px 50px 50px 0px; + background-color: ${theme.colors.background}; + color: ${theme.colors.loyaltyText}; + text-align: center; + `} +` +const Star = styled(Stars)` + width: 20px; + height: 20px; +` \ No newline at end of file diff --git a/src/Containers/MenuItemCard/MenuItemCard.stories.tsx b/src/Containers/MenuItemCard/MenuItemCard.stories.tsx new file mode 100644 index 000000000..883e24628 --- /dev/null +++ b/src/Containers/MenuItemCard/MenuItemCard.stories.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import { Meta, Story } from '@storybook/react'; +import { MenuItemCard, MenuItemCardProps, LoyaltyPoints, LimitedTimeBanner, SaleTag } from '../../index'; + +export default { + title: 'Components/Menu Item Card', + component: MenuItemCard, + subcomponents: { LoyaltyPoints, LimitedTimeBanner, SaleTag }, + argTypes: { onClick: { action: 'This card was clicked!' } }, +} as Meta; + +const Template: Story = (args) => + ; + +export const MenuItemCardBasic = Template.bind({}); +MenuItemCardBasic.args = { + itemImage: 'https://keyassets-p2.timeincuk.net/wp/prod/wp-content/uploads/sites/63/2007/09/Ricotta-cheese-pancakes-with-blackberry-butter.jpg', + itemName: 'Blackberry Pancakes', + itemPrice: 15.99, + itemPriceLimit: 1000, + saleAmount: 5, + loyaltyamount: 20, + loyaltypointlimit: 100, + bannerWidth: 280, + bannerHeight: 40, + minsRemaining: 120, + sale: false, + soldOut: false, + animated: true, + flat: false, +}; + +export const MenuItemCardStates = Template.bind({}); +MenuItemCardStates.args = { + itemImage: 'https://keyassets-p2.timeincuk.net/wp/prod/wp-content/uploads/sites/63/2007/09/Ricotta-cheese-pancakes-with-blackberry-butter.jpg', + itemName: 'Blackberry Pancakes', + itemPrice: 15.99, + itemPriceLimit: 1000, + saleAmount: 5, + loyaltyamount: 0, + loyaltypointlimit: 100, + bannerWidth: 300, + bannerHeight: 40, + minsRemaining: 0, + sale: true, + soldOut: true, + animated: false, + flat: false, +}; + +export const MenuItemCardLongText = Template.bind({}); +MenuItemCardLongText.args = { + itemImage: 'https://keyassets-p2.timeincuk.net/wp/prod/wp-content/uploads/sites/63/2007/09/Ricotta-cheese-pancakes-with-blackberry-butter.jpg', + itemName: 'Super crazy long combo special order with extra sides and drinks', + itemPrice: 2560, + itemPriceLimit: 1000, + saleAmount: 200, + loyaltyamount: 1500, + loyaltypointlimit: 1200, + minsRemaining: 24000, + bannerWidth: 300, + bannerHeight: 40, + sale: true, + soldOut: false, + animated: true, + flat: false, +}; + +export const MenuItemCardEmpty = Template.bind({}); +MenuItemCardEmpty.args = { + itemImage: '', + itemName: '', + itemPrice: 0, + itemPriceLimit: 1000, + saleAmount: 0, + loyaltyamount: 0, + loyaltypointlimit: 100, + minsRemaining: 0, + bannerWidth: 300, + bannerHeight: 40, + sale: false, + soldOut: false, + animated: false, + flat: false, +}; + diff --git a/src/Containers/MenuItemCard/MenuItemCard.tsx b/src/Containers/MenuItemCard/MenuItemCard.tsx new file mode 100644 index 000000000..16488aed9 --- /dev/null +++ b/src/Containers/MenuItemCard/MenuItemCard.tsx @@ -0,0 +1,248 @@ +import React from 'react'; +import styled from 'styled-components'; +import { MainInterface, ResponsiveInterface } from '@Utils/BaseStyles'; +import { transition } from '@Utils/Mixins'; +import { LoyaltyPoints, LimitedTimeBanner, SaleTag } from '../../index'; + +export interface MenuItemCardProps + extends MainInterface, ResponsiveInterface, React.HTMLAttributes { + /* Loyalty points component */ + LoyaltyPoints?: React.ReactElement; + /* Limited time banner component*/ + LimitedTimeBanner?: React.ReactElement; + /* Sale tag component */ + SaleTag?: React.ReactElement; + /* Html link for the item, displayed as 280px x 125px*/ + itemImage: string, + /* Name of the product, after 30 characters '...' replaces the rest */ + itemName: string, + /* Price of the item, is reduced by saleAmount if on sale and will be rounded by 1000 if above itemPriceLimit */ + itemPrice: number, + /* Limit before the item is rounded by 1000, should be greater than 1000 */ + itemPriceLimit: number, + /* Controls if the sale style is used and if a sale amount should be reduced */ + sale: boolean; + /* Controls if the item can still be sold, also removes other components from card display */ + soldOut: boolean; + /* Number passed to LoyaltyPoints to display the loyalty points amount */ + loyaltyamount: number, + /* Limit before loyaltyPoints gets capped */ + loyaltypointlimit: number, + /* Amount that the item is on sale, reduced from itemPrice when sale is active */ + saleAmount: number, + /* Minutes that the item is remaining for, if 0 then will not display */ + minsRemaining: number, + /* Width of the time remaining banner */ + /* Add box shadow when hovered over */ + animated?: boolean; + /* Controls the border around the menu item card, animated will still display the border when hovered over */ + flat?: boolean; + bannerWidth?: number, + /* Height of the time remaining banner */ + bannerHeight?: number, +} + +export const MenuItemCard: React.FC = ({ + animated, + flat, + sale, + soldOut, + itemImage, + itemName, + itemPrice, + itemPriceLimit, + saleAmount, + loyaltyamount, + loyaltypointlimit, + minsRemaining, + bannerHeight, + bannerWidth, + ...props + +}): React.ReactElement => { + /** + * Checks the value of the item and checks for the sale and soldout states, then conditionally renders the item value based on the limit and states + * Item sale amount checks that the price of the sale won't lower the price of the item below 0, if it does, output 0 + * @param sale - Indicates if the item is on sale + * @param soldOut - Indicates if the item is sold out + * @param itemPrice - The price of the item + * @param itemPriceLimit - Limit before using the roundItemValue function that returns rounded by 1000 + * @param saleAmount - Amount reduced from the item if its on sale + */ + function getMenuItemStatus(sale: boolean, soldOut: boolean, itemPrice: number, itemPriceLimit: number, saleAmount: number) { + + var itemSaleAmount; + if (itemPrice - saleAmount > 0) { + itemSaleAmount = itemPrice - saleAmount + } else { + itemSaleAmount = 0; + } + + if (itemPrice >= itemPriceLimit) { + return ${roundItemValue(itemPrice, itemPriceLimit)}K+ + } else if (sale) { + return <> + ${itemPrice} + ${itemSaleAmount} + + } else return ${itemPrice} + } + /** + * Checks if item image has a value, then returns it or uses a defualt image + * @param itemImage - URL for the image, if left empty then return default image from below + * @param alt - Full name of the item + */ + function getItemImage(itemImage: string, alt: string) { + if (!!itemImage) { + return + } else return + } + return ( + + + + {(!soldOut && !!loyaltyamount) && + } + {(!soldOut && sale) && + } + + + {getItemImage(itemImage, itemName)} + + {getStringValue(itemName)} + {soldOut && Sold Out} + + { (!!minsRemaining && !soldOut) && + } + + {(!!itemPrice && !soldOut) && getMenuItemStatus(sale, soldOut, itemPrice, itemPriceLimit, saleAmount)} + + + ); +} +/** + * Checks if the Item Price is greater than or equal to the item price limit + * Returns item price to the nearest tenth rounded + * Also reduces the amount by 1000, the K is added in the component + * @param itemPrice - Current price of the item displayed on the card + * @param itemPriceLimit - A threshold to hit before the item price starts being rounded + */ +function roundItemValue(itemPrice: number, itemPriceLimit: number) { + if (itemPrice >= itemPriceLimit) { + return ((Math.round(itemPrice * .1) / .1) / 1000) + } else return itemPrice; +}; +/** + * Returns the value of the string capped at 30 characters, and adds ... if that limit is hit + * @param itemName + */ +function getStringValue(itemName: string) { + var newString = itemName.substring(0, 30); + if (itemName.length > 30) { + return newString + '...'; + } else + return newString; +} + +const MenuItemCardBox = styled.div` + position: relative; + width: 300px; + height: 250px; + z-index: 1; + cursor: pointer; + box-shadow: ${({ flat, theme }): string => theme.depth[flat ? 0 : 1]}; + + ${({ theme }): string => ` + border-radius: ${theme.dimensions.radius}; + background-color: ${theme.colors.background}; + `} + + ${({ animated, flat, theme, soldOut }): string => + (animated && !soldOut) ? ` ${transition(['box-shadow'])} &:hover { + box-shadow: ${theme.depth[flat ? 1 : 2]}; + }` : ''} + + ${({ soldOut }) => + soldOut && ` + opacity: 0.5; + cursor: not-allowed; `} +`; +const MenuItemCardImage = styled.img` + height: 150px; + width: 300px; + ${({ theme }): string => ` + border-top-left-radius: ${theme.dimensions.radius}; + border-top-right-radius: ${theme.dimensions.radius}; + `} +` +const SoldOutBox = styled.div` + ${({ theme }): string => ` + font-family: ${theme.font.family}; + color: ${theme.colors.background}}; + background-color: ${theme.colors.menuItemCardSoldoutBox}; + border-top-left-radius: ${theme.dimensions.radius}; + border-top-right-radius: ${theme.dimensions.radius}; + `} + position: absolute; + z-index: 2; + top: 0px; + font-size: 25px; + text-align: center; + width: 300px; + height: 40px; + padding-top: 15px; +`; +const MenuItemHeader = styled.header` + font-weight: bolder; + padding-left: 10px; + padding-bottom: 10px; + padding-right: 120px; + font-size: 25px; +` +const PriceText = styled.header` + font-weight: bold; + position: absolute; + font-size: 25px; + text-align: right; + right: 10px; + bottom: 40px; +` +const PriceTextSlash = styled(PriceText)` + text-decoration: line-through; + font-size: 20px; + opacity: .6; + bottom: 60px; + position: absolute; +` +const PriceText1k = styled(PriceText)` + font-size: 25px; + ${({ theme }): string => ` + color: ${theme.colors.ItemCardSaleGreen}; + `} +` +const OnSale = styled.header` + position: absolute; + font-size: 30px; + text-align: right; + font-weight: bold; + ${({ theme }): string => ` + color: ${theme.colors.primary}; + `} + right: 10px; + bottom: 10px; +` +const MenuItemCardAccessoryDiv = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + position: absolute; + width: 100%; + top: 20px; + align-items: center; +` +const LimitedTimeBannerPosition = styled.div` + position: absolute; + top: 110px; +` \ No newline at end of file diff --git a/src/Containers/SaleTag/SaleTag.stories.tsx b/src/Containers/SaleTag/SaleTag.stories.tsx index a5bcd908b..0d730e624 100644 --- a/src/Containers/SaleTag/SaleTag.stories.tsx +++ b/src/Containers/SaleTag/SaleTag.stories.tsx @@ -2,7 +2,6 @@ import React from 'react'; import { Meta, Story } from '@storybook/react'; import { SaleTag, SaleTagProps } from '../../index'; - export default { title: 'Components/SaleTag', component: SaleTag, diff --git a/src/Containers/SaleTag/SaleTag.tsx b/src/Containers/SaleTag/SaleTag.tsx index b65107c13..351213a0f 100644 --- a/src/Containers/SaleTag/SaleTag.tsx +++ b/src/Containers/SaleTag/SaleTag.tsx @@ -16,11 +16,12 @@ export const SaleTag: React.FC = ({ ); -const SaleTagDiv = styled.span` +const SaleTagDiv = styled.div` ${({theme}):string => ` border-radius: 100px; - width: 100px; - height: 40px; + width: fit-content; + min-width: 60px; + height: 20px; padding: 3px 10px; border-style: solid; border-width: 1px; diff --git a/src/Containers/index.ts b/src/Containers/index.ts index c4d13999c..bdf244451 100644 --- a/src/Containers/index.ts +++ b/src/Containers/index.ts @@ -95,3 +95,5 @@ export * from './LimitedTimeBanner/LimitedTimeBanner'; export * from './TreeAccordion/TreeAccordion'; export * from './ReachIndicator/ReachIndicator'; export * from './InfoHeader/InfoHeader'; +export * from './MenuItemCard/MenuItemCard'; +export * from './LoyaltyPoints/LoyaltyPoints'; diff --git a/src/Themes/MainTheme.ts b/src/Themes/MainTheme.ts index 05c6f13d1..90c8f1ba2 100644 --- a/src/Themes/MainTheme.ts +++ b/src/Themes/MainTheme.ts @@ -31,12 +31,15 @@ export interface MainThemeInterface extends ThemeTemplateInterface { chairTableBackground: string; chairOccupiedBackground: string; chairTableEditBackground: string; + menuItemCardSoldoutBox: string; reachIndicatorColors: { red: string; yellow: string; green: string; }; + loyaltyText: string; bannerBackgroundColor: string; + ItemCardSaleGreen: string; }; } @@ -66,7 +69,10 @@ export const MainTheme: MainThemeInterface = { chairTableBackground: '#6c757d', chairOccupiedBackground: '#EE2434', chairTableEditBackground: '#C4C4C4', - bannerBackgroundColor : 'rgba(0,0,0,0.5)', + bannerBackgroundColor: 'rgba(0,0,0,0.5)', + menuItemCardSoldoutBox: '#303030', + loyaltyText: '#0000FF', + ItemCardSaleGreen: '#04CC00', PieChartColors: { Red: '#FF0000', Green: '#008000', From dcb13695a91df4cc720db77cd27d3976afa1bf61 Mon Sep 17 00:00:00 2001 From: MacAlmighty Date: Wed, 12 Jan 2022 20:36:20 -0500 Subject: [PATCH 2/2] Fixed casing, documenting, changed banner props, added action Fixed casing and documentation in the loyalty points tag. Removed banner width and height properties and made minutes remaining optional in the limited time banner. Changed 'var' to 'let' in menu item card functions. Added a new div with onClick that calls an action when not in the sold out state. --- .../LimitedTimeBanner.stories.tsx | 2 - .../LimitedTimeBanner/LimitedTimeBanner.tsx | 18 +++---- .../LoyaltyPoints/LoyaltyPoints.stories.tsx | 4 +- .../LoyaltyPoints/LoyaltyPoints.tsx | 18 ++++--- .../MenuItemCard/MenuItemCard.stories.tsx | 52 ++++++++++++------- src/Containers/MenuItemCard/MenuItemCard.tsx | 51 +++++++++--------- 6 files changed, 77 insertions(+), 68 deletions(-) diff --git a/src/Containers/LimitedTimeBanner/LimitedTimeBanner.stories.tsx b/src/Containers/LimitedTimeBanner/LimitedTimeBanner.stories.tsx index 5b7f2f31f..57fddac47 100644 --- a/src/Containers/LimitedTimeBanner/LimitedTimeBanner.stories.tsx +++ b/src/Containers/LimitedTimeBanner/LimitedTimeBanner.stories.tsx @@ -8,8 +8,6 @@ export default { component: LimitedTimeBanner, args: { minsRemaining: 120, - bannerWidth: 300, - bannerHeight: 40, }, } as Meta; diff --git a/src/Containers/LimitedTimeBanner/LimitedTimeBanner.tsx b/src/Containers/LimitedTimeBanner/LimitedTimeBanner.tsx index 745ef52aa..10d7a9a90 100644 --- a/src/Containers/LimitedTimeBanner/LimitedTimeBanner.tsx +++ b/src/Containers/LimitedTimeBanner/LimitedTimeBanner.tsx @@ -8,22 +8,18 @@ const MINUTES_IN_YEAR = 524160; export interface LimitedTimeBannerProps extends MainInterface { - /* minutes until time runs out */ - minsRemaining: number, - bannerWidth?: number, - bannerHeight?: number, + /* minutes until time runs out */ + minsRemaining?: number, } export const LimitedTimeBanner: React.FC = ({ minsRemaining, - bannerWidth, - bannerHeight, ...props }): React.ReactElement => ( - + - {alterTime(minsRemaining)} Remaining + {alterTime(minsRemaining!)} Remaining ); @@ -52,15 +48,13 @@ const alterTime = (value:number) => { } const BannerBox = styled.div` + width: 300px; + height; 40px; ${({theme}):string => ` font-family: ${theme.font.family}; color: ${theme.colors.background}}; background-color: ${theme.colors.bannerBackgroundColor}; `} - ${({ bannerWidth, bannerHeight }): string => ` - width: ${bannerWidth}px; - height: ${bannerHeight}px; - `} `; const Icon = styled(Clock)` diff --git a/src/Containers/LoyaltyPoints/LoyaltyPoints.stories.tsx b/src/Containers/LoyaltyPoints/LoyaltyPoints.stories.tsx index 47c93d279..898ab9309 100644 --- a/src/Containers/LoyaltyPoints/LoyaltyPoints.stories.tsx +++ b/src/Containers/LoyaltyPoints/LoyaltyPoints.stories.tsx @@ -7,8 +7,8 @@ export default { title: 'Components/LoyaltyPoints', component: LoyaltyPoints, args: { - loyaltyamount: 10, - loyaltypointlimit: 100, + loyaltyAmount: 10, + loyaltyPointLimit: 100, } } as Meta; diff --git a/src/Containers/LoyaltyPoints/LoyaltyPoints.tsx b/src/Containers/LoyaltyPoints/LoyaltyPoints.tsx index 1f25df384..d49d8380a 100644 --- a/src/Containers/LoyaltyPoints/LoyaltyPoints.tsx +++ b/src/Containers/LoyaltyPoints/LoyaltyPoints.tsx @@ -4,21 +4,23 @@ import { Stars } from '@styled-icons/material/Stars'; export interface LoyaltyPointsProps extends React.HTMLAttributes { - loyaltyamount: number; //the amount of loyalty points that the user gets with purchase, used to display on component. - loyaltypointlimit: number; //the limit of loyalty points before it says 99+ instead. + /* The amount of loyalty points displayed on the component */ + loyaltyAmount: number; + /* Limit before loyalty amount displays only this number instead */ + loyaltyPointLimit: number; } export const LoyaltyPoints: React.FC = ({ - loyaltyamount, - loyaltypointlimit, + loyaltyAmount, + loyaltyPointLimit, ...props }): React.ReactElement => -
+{getLoyaltyPoints(loyaltyamount, loyaltypointlimit)}
+
+{getLoyaltyPoints(loyaltyAmount, loyaltyPointLimit)}
; -function getLoyaltyPoints(loyaltypoints: number, loyaltypointlimit: number) { - if (loyaltypoints >= loyaltypointlimit) { - return loyaltypointlimit + "⁺"; +function getLoyaltyPoints(loyaltypoints: number, loyaltyPointLimit: number) { + if (loyaltypoints >= loyaltyPointLimit) { + return loyaltyPointLimit + "⁺"; } return Math.round(loyaltypoints); } diff --git a/src/Containers/MenuItemCard/MenuItemCard.stories.tsx b/src/Containers/MenuItemCard/MenuItemCard.stories.tsx index 883e24628..37aeea0a9 100644 --- a/src/Containers/MenuItemCard/MenuItemCard.stories.tsx +++ b/src/Containers/MenuItemCard/MenuItemCard.stories.tsx @@ -1,12 +1,12 @@ import React from 'react'; import { Meta, Story } from '@storybook/react'; import { MenuItemCard, MenuItemCardProps, LoyaltyPoints, LimitedTimeBanner, SaleTag } from '../../index'; +import { action } from "@storybook/addon-actions"; export default { title: 'Components/Menu Item Card', component: MenuItemCard, subcomponents: { LoyaltyPoints, LimitedTimeBanner, SaleTag }, - argTypes: { onClick: { action: 'This card was clicked!' } }, } as Meta; const Template: Story = (args) => @@ -19,29 +19,44 @@ MenuItemCardBasic.args = { itemPrice: 15.99, itemPriceLimit: 1000, saleAmount: 5, - loyaltyamount: 20, - loyaltypointlimit: 100, - bannerWidth: 280, - bannerHeight: 40, - minsRemaining: 120, + loyaltyAmount: 0, + loyaltyPointLimit: 100, + minsRemaining: 0, + cardWasClicked: action("Card was clicked and not sold out!"), sale: false, soldOut: false, animated: true, flat: false, }; -export const MenuItemCardStates = Template.bind({}); -MenuItemCardStates.args = { +export const MenuItemCardSale = Template.bind({}); +MenuItemCardSale.args = { + itemImage: 'https://keyassets-p2.timeincuk.net/wp/prod/wp-content/uploads/sites/63/2007/09/Ricotta-cheese-pancakes-with-blackberry-butter.jpg', + itemName: 'Blackberry Pancakes', + itemPrice: 15.99, + itemPriceLimit: 1000, + saleAmount: 5, + loyaltyAmount: 20, + loyaltyPointLimit: 100, + minsRemaining: 120, + cardWasClicked: action("Card was clicked and not sold out!"), + sale: true, + soldOut: false, + animated: true, + flat: false, +}; + +export const MenuItemCardSoldOut = Template.bind({}); +MenuItemCardSoldOut.args = { itemImage: 'https://keyassets-p2.timeincuk.net/wp/prod/wp-content/uploads/sites/63/2007/09/Ricotta-cheese-pancakes-with-blackberry-butter.jpg', itemName: 'Blackberry Pancakes', itemPrice: 15.99, itemPriceLimit: 1000, saleAmount: 5, - loyaltyamount: 0, - loyaltypointlimit: 100, - bannerWidth: 300, - bannerHeight: 40, + loyaltyAmount: 0, + loyaltyPointLimit: 100, minsRemaining: 0, + cardWasClicked: action("Card was clicked and not sold out!"), sale: true, soldOut: true, animated: false, @@ -55,11 +70,10 @@ MenuItemCardLongText.args = { itemPrice: 2560, itemPriceLimit: 1000, saleAmount: 200, - loyaltyamount: 1500, - loyaltypointlimit: 1200, + loyaltyAmount: 1500, + loyaltyPointLimit: 1200, minsRemaining: 24000, - bannerWidth: 300, - bannerHeight: 40, + cardWasClicked: action("Card was clicked and not sold out!"), sale: true, soldOut: false, animated: true, @@ -73,11 +87,9 @@ MenuItemCardEmpty.args = { itemPrice: 0, itemPriceLimit: 1000, saleAmount: 0, - loyaltyamount: 0, - loyaltypointlimit: 100, + loyaltyAmount: 0, + loyaltyPointLimit: 100, minsRemaining: 0, - bannerWidth: 300, - bannerHeight: 40, sale: false, soldOut: false, animated: false, diff --git a/src/Containers/MenuItemCard/MenuItemCard.tsx b/src/Containers/MenuItemCard/MenuItemCard.tsx index 16488aed9..5caa08db6 100644 --- a/src/Containers/MenuItemCard/MenuItemCard.tsx +++ b/src/Containers/MenuItemCard/MenuItemCard.tsx @@ -8,11 +8,11 @@ export interface MenuItemCardProps extends MainInterface, ResponsiveInterface, React.HTMLAttributes { /* Loyalty points component */ LoyaltyPoints?: React.ReactElement; - /* Limited time banner component*/ + /* Limited time banner component */ LimitedTimeBanner?: React.ReactElement; /* Sale tag component */ SaleTag?: React.ReactElement; - /* Html link for the item, displayed as 280px x 125px*/ + /* Html link for the item, displayed as 300px x 150px */ itemImage: string, /* Name of the product, after 30 characters '...' replaces the rest */ itemName: string, @@ -25,21 +25,19 @@ export interface MenuItemCardProps /* Controls if the item can still be sold, also removes other components from card display */ soldOut: boolean; /* Number passed to LoyaltyPoints to display the loyalty points amount */ - loyaltyamount: number, + loyaltyAmount: number, /* Limit before loyaltyPoints gets capped */ - loyaltypointlimit: number, + loyaltyPointLimit: number, /* Amount that the item is on sale, reduced from itemPrice when sale is active */ saleAmount: number, /* Minutes that the item is remaining for, if 0 then will not display */ - minsRemaining: number, - /* Width of the time remaining banner */ + minsRemaining?: number, /* Add box shadow when hovered over */ animated?: boolean; /* Controls the border around the menu item card, animated will still display the border when hovered over */ flat?: boolean; - bannerWidth?: number, - /* Height of the time remaining banner */ - bannerHeight?: number, + /* Handles card clicks when the item is not soldout */ + cardWasClicked?: () => void, } export const MenuItemCard: React.FC = ({ @@ -52,11 +50,10 @@ export const MenuItemCard: React.FC = ({ itemPrice, itemPriceLimit, saleAmount, - loyaltyamount, - loyaltypointlimit, + loyaltyAmount, + loyaltyPointLimit, minsRemaining, - bannerHeight, - bannerWidth, + cardWasClicked, ...props }): React.ReactElement => { @@ -71,12 +68,11 @@ export const MenuItemCard: React.FC = ({ */ function getMenuItemStatus(sale: boolean, soldOut: boolean, itemPrice: number, itemPriceLimit: number, saleAmount: number) { - var itemSaleAmount; + let itemSaleAmount = 0; + if (itemPrice - saleAmount > 0) { itemSaleAmount = itemPrice - saleAmount - } else { - itemSaleAmount = 0; - } + } if (itemPrice >= itemPriceLimit) { return ${roundItemValue(itemPrice, itemPriceLimit)}K+ @@ -98,13 +94,15 @@ export const MenuItemCard: React.FC = ({ } else return } return ( - + + + {(!soldOut) && } - {(!soldOut && !!loyaltyamount) && - } + {(!soldOut && !!loyaltyAmount) && + } {(!soldOut && sale) && } @@ -115,7 +113,7 @@ export const MenuItemCard: React.FC = ({ {soldOut && Sold Out} { (!!minsRemaining && !soldOut) && - } + } {(!!itemPrice && !soldOut) && getMenuItemStatus(sale, soldOut, itemPrice, itemPriceLimit, saleAmount)} @@ -139,7 +137,7 @@ function roundItemValue(itemPrice: number, itemPriceLimit: number) { * @param itemName */ function getStringValue(itemName: string) { - var newString = itemName.substring(0, 30); + let newString = itemName.substring(0, 30); if (itemName.length > 30) { return newString + '...'; } else @@ -169,6 +167,11 @@ const MenuItemCardBox = styled.div