From 0e966b90610c2ee2fd52d285b48dd8a9339165d4 Mon Sep 17 00:00:00 2001 From: Nipun Jindal <35667299+njindal239@users.noreply.github.com> Date: Tue, 16 Jul 2019 09:43:11 -0700 Subject: [PATCH] Cap Max Number of Pages Generated (#908) * limit number of pages to 20; add error message * create new component notification * refactor dependency checker * remove unused imports from dependency checker * add to translations * css tweaks * cleanup * css tweaks * PR Feedback --- .../src/components/Notification/index.tsx | 45 ++++++++ .../components/Notification/styles.module.css | 40 +++++++ .../src/containers/DependencyInfo/index.tsx | 26 ++--- .../DependencyInfo/styles.module.css | 31 ------ .../src/containers/SelectOption/index.tsx | 102 +++++++++++++----- .../containers/SelectOption/styles.module.css | 23 ++++ .../src/containers/SelectPages/index.tsx | 1 + src/client/src/translations/en.json | 3 + src/client/src/utils/constants.ts | 3 + 9 files changed, 198 insertions(+), 76 deletions(-) create mode 100644 src/client/src/components/Notification/index.tsx create mode 100644 src/client/src/components/Notification/styles.module.css diff --git a/src/client/src/components/Notification/index.tsx b/src/client/src/components/Notification/index.tsx new file mode 100644 index 0000000000..d23917a1a1 --- /dev/null +++ b/src/client/src/components/Notification/index.tsx @@ -0,0 +1,45 @@ +import React from "react"; +import classnames from "classnames"; +import { injectIntl, defineMessages, InjectedIntl } from "react-intl"; + +import { ReactComponent as Warning } from "../../assets/warning.svg"; +import { ReactComponent as Checkmark } from "../../assets/checkgreen.svg"; + +import styles from "./styles.module.css"; + +interface IProps { + showWarning: Boolean; + text: string; + altMessage: string; +} + +const Notification = ({ showWarning, text, altMessage }: IProps) => { + const messages = defineMessages({ + notificationMessage: { + id: "notification.notificationAltMessage", + defaultMessage: altMessage + } + }); + + return ( + +
+ {showWarning ? ( + + ) : ( + + )} +
+
+ {text} +
+
+ ); +}; + +export default injectIntl(Notification); diff --git a/src/client/src/components/Notification/styles.module.css b/src/client/src/components/Notification/styles.module.css new file mode 100644 index 0000000000..4b7f5cd644 --- /dev/null +++ b/src/client/src/components/Notification/styles.module.css @@ -0,0 +1,40 @@ +.text { + margin-left: 6px; + font-style: normal; + font-weight: 600; + font-size: 12px; +} + +.iconCheck { + margin-top: 3px; + height: 14px; +} + +.iconWarning { + margin-top: 3px; + height: 16px; +} + +.iconCheck path { + fill: var(--vscode-gitDecoration-untrackedResourceForeground); +} + +.iconWarning path { + fill: var(--vscode-editorMarkerNavigationWarning-background); +} + +.borderGreen { + border: 1px solid var(--vscode-gitDecoration-untrackedResourceForeground); +} + +.borderYellow { + border: 1px solid var(--vscode-editorMarkerNavigationWarning-background); +} + +.bodyGreen { + color: var(--vscode-gitDecoration-untrackedResourceForeground); +} + +.bodyYellow { + color: var(--vscode-editorMarkerNavigationWarning-background); +} diff --git a/src/client/src/containers/DependencyInfo/index.tsx b/src/client/src/containers/DependencyInfo/index.tsx index b25f5967e2..5f2da64faf 100644 --- a/src/client/src/containers/DependencyInfo/index.tsx +++ b/src/client/src/containers/DependencyInfo/index.tsx @@ -12,8 +12,7 @@ import { IDependenciesInstalled } from "../../reducers/dependencyInfoReducers"; import * as ModalActions from "../../actions/modalActions/modalActions"; import { ThunkDispatch } from "redux-thunk"; import RootAction from "../../actions/ActionType"; -import { ReactComponent as Warning } from "../../assets/warning.svg"; -import { ReactComponent as Checkmark } from "../../assets/checkgreen.svg"; +import Notification from "../../components/Notification"; const messages = defineMessages({ installed: { @@ -137,24 +136,11 @@ class DependencyInfo extends React.Component { [styles.borderYellow]: !installed })} > -
- {installed ? ( - - ) : ( - - )} -
-
- {`${dependencyMessage}`} -
+ ); } diff --git a/src/client/src/containers/DependencyInfo/styles.module.css b/src/client/src/containers/DependencyInfo/styles.module.css index f338c73953..4eb5629e35 100644 --- a/src/client/src/containers/DependencyInfo/styles.module.css +++ b/src/client/src/containers/DependencyInfo/styles.module.css @@ -17,29 +17,6 @@ text-decoration: none; } -.body { - margin-left: 6px; - font-style: normal; - font-weight: 600; - font-size: 12px; -} - -.iconCheck { - height: 12px; -} - -.iconWarning { - height: 16px; -} - -.iconCheck path { - fill: var(--vscode-gitDecoration-untrackedResourceForeground); -} - -.iconWarning path { - fill: var(--vscode-editorMarkerNavigationWarning-background); -} - .borderGreen { border: 1px solid var(--vscode-gitDecoration-untrackedResourceForeground); } @@ -47,11 +24,3 @@ .borderYellow { border: 1px solid var(--vscode-editorMarkerNavigationWarning-background); } - -.bodyGreen { - color: var(--vscode-gitDecoration-untrackedResourceForeground); -} - -.bodyYellow { - color: var(--vscode-editorMarkerNavigationWarning-background); -} diff --git a/src/client/src/containers/SelectOption/index.tsx b/src/client/src/containers/SelectOption/index.tsx index 596c46f879..785acb8104 100644 --- a/src/client/src/containers/SelectOption/index.tsx +++ b/src/client/src/containers/SelectOption/index.tsx @@ -1,8 +1,11 @@ import * as React from "react"; import { connect } from "react-redux"; +import classnames from "classnames"; import SelectableCard from "../../components/SelectableCard"; +import Notification from "../../components/Notification"; import Title from "../../components/Title"; +import { MAX_PAGES_ALLOWED } from "../../utils/constants"; import styles from "./styles.module.css"; @@ -13,12 +16,34 @@ import { ISelected } from "../../types/selected"; import { Dispatch } from "redux"; import RootAction from "../../actions/ActionType"; +import { InjectedIntl, defineMessages, injectIntl } from "react-intl"; + +const messages = defineMessages({ + limitedPages: { + id: "pages.limitedPagesMessage", + defaultMessage: "You can select up to 20 pages" + }, + overlimitPages: { + id: "pages.overlimitPagesMessage", + defaultMessage: "You cannot add more than 20 pages to the project" + }, + iconAltMessage: { + id: "pages.maxPagesText", + defaultMessage: "Icon for Max Pages Description" + } +}); + interface ICount { [key: string]: number; } +interface IProps { + intl: InjectedIntl; +} + interface ISelectOptionProps { title: string; + description: string; internalName?: string; selectCard?: (card: ISelected) => void; selectedCardIndices: number[]; @@ -34,28 +59,28 @@ interface ISelectOptionProps { interface ISelectOptionState { selectedCardIndices: number[]; + maxPageReached: boolean; + description: string; } interface IDispatchProps { setDetailPage: (detailPageInfo: IOption) => void; } -type Props = IDispatchProps & ISelectOptionProps; +type Props = IDispatchProps & ISelectOptionProps & IProps; class SelectOption extends React.Component { constructor(props: Props) { super(props); const { selectedCardIndices } = props; this.state = { - selectedCardIndices + selectedCardIndices, + maxPageReached: false, + description: props.intl.formatMessage(messages.limitedPages) }; } public componentDidMount() { - const { - selectCard, - selectOptions, - selectedCardIndices - } = this.props; + const { selectCard, selectOptions, selectedCardIndices } = this.props; if (selectCard) { this.exchangeOption(selectedCardIndices[0]); this.setState({ @@ -144,11 +169,7 @@ class SelectOption extends React.Component { }); } - public removeOption( - cardNumber: number, - cardCount: number, - internalName: string - ) { + public removeOption(internalName: string) { const { selectedCardIndices, currentCardData, selectOptions } = this.props; if (selectOptions && currentCardData && currentCardData.length > 1) { const size = currentCardData.length; @@ -197,10 +218,7 @@ class SelectOption extends React.Component { } public onCardClick(cardNumber: number) { - const { - options, - multiSelect - } = this.props; + const { options, multiSelect } = this.props; const { unselectable } = options[cardNumber]; if (unselectable) { return; @@ -227,9 +245,22 @@ class SelectOption extends React.Component { } }; - public addPage(cardNumber: number) { - const { options, cardTypeCount, handleCountUpdate } = this.props; + public addPage = (cardNumber: number) => { + const { + options, + cardTypeCount, + handleCountUpdate, + currentCardData, + intl + } = this.props; const { internalName } = options[cardNumber]; + if (currentCardData && currentCardData.length >= MAX_PAGES_ALLOWED) { + this.setState({ + maxPageReached: true, + description: intl.formatMessage(messages.overlimitPages) + }); + return; + } if (cardTypeCount && handleCountUpdate) { cardTypeCount[internalName] = cardTypeCount[internalName] ? cardTypeCount[internalName] + 1 @@ -237,25 +268,30 @@ class SelectOption extends React.Component { handleCountUpdate(cardTypeCount); this.addOption(cardNumber, cardTypeCount[internalName], internalName); } - } + }; - public removePage(cardNumber: number) { + public removePage = (cardNumber: number) => { const { options, currentCardData, cardTypeCount, - handleCountUpdate + handleCountUpdate, + intl } = this.props; const { internalName } = options[cardNumber]; + this.setState({ + maxPageReached: false, + description: intl.formatMessage(messages.limitedPages) + }); if ( cardTypeCount && handleCountUpdate && currentCardData && currentCardData.length > 1 ) { - this.removeOption(cardNumber, cardTypeCount[internalName], internalName); + this.removeOption(internalName); } - } + }; public render() { const { @@ -263,11 +299,27 @@ class SelectOption extends React.Component { options, setDetailPage, isFrameworkSelection, - isPagesSelection + isPagesSelection, + intl } = this.props; + const { maxPageReached, description } = this.state; return (
{title} + {isPagesSelection && ( +
+ +
+ )}
{options.map((option, cardNumber) => { const { svgUrl, title, body, unselectable, internalName } = option; @@ -311,4 +363,4 @@ const mapDispatchToProps = ( export default connect( null, mapDispatchToProps -)(SelectOption); +)(injectIntl(SelectOption)); diff --git a/src/client/src/containers/SelectOption/styles.module.css b/src/client/src/containers/SelectOption/styles.module.css index d38a506006..8db434d864 100644 --- a/src/client/src/containers/SelectOption/styles.module.css +++ b/src/client/src/containers/SelectOption/styles.module.css @@ -3,6 +3,29 @@ flex-wrap: wrap; } +.description { + border-radius: 3px; + padding: 3px 10px 3px 10px; + margin-right: 5px; + margin-bottom: 30px; + display: inline-flex; + justify-content: center; + align-items: center; + text-decoration: none; +} + +.description:focus { + outline: 1px solid var(--vscode-contrastActiveBorder); +} + +.borderGreen { + border: 1px solid var(--vscode-gitDecoration-untrackedResourceForeground); +} + +.borderYellow { + border: 1px solid var(--vscode-editorMarkerNavigationWarning-background); +} + .icon { height: 36px; } diff --git a/src/client/src/containers/SelectPages/index.tsx b/src/client/src/containers/SelectPages/index.tsx index c3dff0ab09..38f4a9e8b5 100644 --- a/src/client/src/containers/SelectPages/index.tsx +++ b/src/client/src/containers/SelectPages/index.tsx @@ -118,6 +118,7 @@ class SelectPages extends React.Component { selectedPages )} title={intl.formatMessage(messages.pagesTitleQuestion)} + description="Max 20 pages can be selected" options={options} currentCardData={selectedPages} cardTypeCount={pageCount} diff --git a/src/client/src/translations/en.json b/src/client/src/translations/en.json index 6438a0c8bf..181863543e 100644 --- a/src/client/src/translations/en.json +++ b/src/client/src/translations/en.json @@ -101,6 +101,9 @@ "pageNameError.emptyName": "Name cannot be empty", "pageNameError.invalidRegex": "Name may only contain letters, numbers, spaces, dashes or underscores", "pageNameError.nameStartLetter": "Page name may only start with letters", + "pages.limitedPagesMessage": "You can select up to 20 pages", + "pages.maxPagesText": "Icon for Max Pages Description", + "pages.overlimitPagesMessage": "You cannot add more than 20 pages to the project", "postGenerationModal.azureServices": "Azure Services", "postGenerationModal.closeWizard": "Close Wizard", "postGenerationModal.createAnotherProject": "Create New Project", diff --git a/src/client/src/utils/constants.ts b/src/client/src/utils/constants.ts index 245310c72f..bf9948dbe8 100644 --- a/src/client/src/utils/constants.ts +++ b/src/client/src/utils/constants.ts @@ -9,6 +9,8 @@ const NEW_PROJECT = "/"; const PROJECT_NAME_CHARACTER_LIMIT = 50; +const MAX_PAGES_ALLOWED = 20; + const PRODUCTION = "production"; const DEVELOPMENT = "development"; @@ -141,6 +143,7 @@ export { COSMOS_APIS, DEVELOPMENT, PROJECT_NAME_CHARACTER_LIMIT, + MAX_PAGES_ALLOWED, FRAMEWORK_TYPE, KEY_EVENTS };