diff --git a/src/components/Contribute/Knowledge/AttributionInformation/AttributionInformation.tsx b/src/components/Contribute/Knowledge/AttributionInformation/AttributionInformation.tsx
index d34c98b6..530697dd 100644
--- a/src/components/Contribute/Knowledge/AttributionInformation/AttributionInformation.tsx
+++ b/src/components/Contribute/Knowledge/AttributionInformation/AttributionInformation.tsx
@@ -1,8 +1,16 @@
import React from 'react';
-import { FormFieldGroupExpandable, FormFieldGroupHeader, FormGroup } from '@patternfly/react-core/dist/dynamic/components/Form';
+import { FormFieldGroupExpandable, FormFieldGroupHeader, FormGroup, FormHelperText } from '@patternfly/react-core/dist/dynamic/components/Form';
import { TextInput } from '@patternfly/react-core/dist/dynamic/components/TextInput';
+import { HelperText } from '@patternfly/react-core/dist/dynamic/components/HelperText';
+import { HelperTextItem } from '@patternfly/react-core/dist/dynamic/components/HelperText';
+import ExclamationCircleIcon from '@patternfly/react-icons/dist/dynamic/icons/exclamation-circle-icon';
+import { ValidatedOptions } from '@patternfly/react-core/dist/esm/helpers/constants';
+import { KnowledgeFormData } from '..';
+import { checkKnowledgeFormCompletion } from '../validation';
interface Props {
+ knowledgeFormData: KnowledgeFormData;
+ setDisableAction: React.Dispatch>;
titleWork: string;
setTitleWork: React.Dispatch>;
linkWork: string;
@@ -16,6 +24,8 @@ interface Props {
}
const AttributionInformation: React.FC = ({
+ knowledgeFormData,
+ setDisableAction,
titleWork,
setTitleWork,
linkWork,
@@ -27,12 +37,87 @@ const AttributionInformation: React.FC = ({
creators,
setCreators
}) => {
+ const [validTitle, setValidTitle] = React.useState();
+ const [validLink, setValidLink] = React.useState();
+ const [validRevision, setValidRevision] = React.useState();
+ const [validLicense, setValidLicense] = React.useState();
+ const [validCreators, setValidCreators] = React.useState();
+
+ const validateTitle = (title: string) => {
+ if (title.length > 0) {
+ setValidTitle(ValidatedOptions.success);
+ setDisableAction(!checkKnowledgeFormCompletion(knowledgeFormData));
+ return;
+ }
+ setDisableAction(true);
+ setValidTitle(ValidatedOptions.error);
+ return;
+ };
+
+ const validateLink = (link: string) => {
+ if (link.length === 0) {
+ setDisableAction(true);
+ setValidLink(ValidatedOptions.error);
+ return;
+ }
+ try {
+ new URL(link);
+ setValidLink(ValidatedOptions.success);
+ setDisableAction(!checkKnowledgeFormCompletion(knowledgeFormData));
+ return;
+ } catch (e) {
+ setDisableAction(true);
+ setValidLink(ValidatedOptions.warning);
+ return;
+ }
+ };
+
+ const validateRevision = (revision: string) => {
+ if (revision.length > 0) {
+ setValidRevision(ValidatedOptions.success);
+ setDisableAction(!checkKnowledgeFormCompletion(knowledgeFormData));
+ return;
+ }
+ setDisableAction(true);
+ setValidRevision(ValidatedOptions.error);
+ return;
+ };
+
+ const validateLicense = (license: string) => {
+ if (license.length > 0) {
+ setValidLicense(ValidatedOptions.success);
+ setDisableAction(!checkKnowledgeFormCompletion(knowledgeFormData));
+ return;
+ }
+ setDisableAction(true);
+ setValidLicense(ValidatedOptions.error);
+ return;
+ };
+
+ const validateCreators = (creators: string) => {
+ if (creators.length > 0) {
+ setValidCreators(ValidatedOptions.success);
+ setDisableAction(!checkKnowledgeFormCompletion(knowledgeFormData));
+ return;
+ }
+ setDisableAction(true);
+ setValidCreators(ValidatedOptions.error);
+ return;
+ };
+
return (
+ Attribution Info *
+
+ ),
+ id: 'attribution-info-id'
+ }}
titleDescription="Provide attribution information."
/>
}
@@ -43,41 +128,106 @@ const AttributionInformation: React.FC = ({
type="text"
aria-label="title_work"
placeholder="Enter title of work"
+ validated={validTitle}
value={titleWork}
onChange={(_event, value) => setTitleWork(value)}
+ onBlur={() => validateTitle(titleWork)}
/>
+ {validTitle === ValidatedOptions.error && (
+
+
+ } variant={validTitle}>
+ Title is required.
+
+
+
+ )}
+
setLinkWork(value)}
+ onBlur={() => validateLink(linkWork)}
/>
+ {validLink === ValidatedOptions.error && (
+
+
+ } variant={validLink}>
+ Link to title is required.
+
+
+
+ )}
+ {validLink === ValidatedOptions.warning && (
+
+
+ } variant={validLink}>
+ Please enter a valid URL.
+
+
+
+ )}
setRevision(value)}
+ onBlur={() => validateRevision(revision)}
/>
+ {validRevision === ValidatedOptions.error && (
+
+
+ } variant={validRevision}>
+ Revision is required.
+
+
+
+ )}
setLicenseWork(value)}
+ onBlur={() => validateLicense(licenseWork)}
/>
+ {validLicense === ValidatedOptions.error && (
+
+
+ } variant={validLicense}>
+ License is required.
+
+
+
+ )}
setCreators(value)}
+ onBlur={() => validateCreators(creators)}
/>
+ {validCreators === ValidatedOptions.error && (
+
+
+ } variant={validCreators}>
+ Creators is required.
+
+
+
+ )}
);
diff --git a/src/components/Contribute/Knowledge/AuthorInformation/AuthorInformation.tsx b/src/components/Contribute/Knowledge/AuthorInformation/AuthorInformation.tsx
index bad1b65e..f27d3488 100644
--- a/src/components/Contribute/Knowledge/AuthorInformation/AuthorInformation.tsx
+++ b/src/components/Contribute/Knowledge/AuthorInformation/AuthorInformation.tsx
@@ -1,21 +1,62 @@
-import React from 'react';
-import { FormFieldGroupExpandable, FormFieldGroupHeader, FormGroup } from '@patternfly/react-core/dist/dynamic/components/Form';
+import React, { useState } from 'react';
+import { FormFieldGroupExpandable, FormFieldGroupHeader, FormGroup, FormHelperText } from '@patternfly/react-core/dist/dynamic/components/Form';
import { TextInput } from '@patternfly/react-core/dist/dynamic/components/TextInput';
+import { HelperText } from '@patternfly/react-core/dist/dynamic/components/HelperText';
+import { HelperTextItem } from '@patternfly/react-core/dist/dynamic/components/HelperText';
+import ExclamationCircleIcon from '@patternfly/react-icons/dist/dynamic/icons/exclamation-circle-icon';
+import { ValidatedOptions } from '@patternfly/react-core/dist/esm/helpers/constants';
+import { KnowledgeFormData } from '..';
+import { checkKnowledgeFormCompletion } from '../validation';
interface Props {
+ knowledgeFormData: KnowledgeFormData;
+ setDisableAction: React.Dispatch>;
email: string;
setEmail: React.Dispatch>;
name: string;
setName: React.Dispatch>;
}
-const AuthorInformation: React.FC = ({ email, setEmail, name, setName }) => {
+const AuthorInformation: React.FC = ({ knowledgeFormData, setDisableAction, email, setEmail, name, setName }) => {
+ const [validEmail, setValidEmail] = useState();
+ const [validName, setValidName] = useState();
+
+ const validateEmail = (email: string) => {
+ const re = /\S+@\S+\.\S+/;
+ if (re.test(email)) {
+ setValidEmail(ValidatedOptions.success);
+ setDisableAction(!checkKnowledgeFormCompletion(knowledgeFormData));
+ return;
+ }
+ setDisableAction(true);
+ setValidEmail(ValidatedOptions.error);
+ return;
+ };
+
+ const validateName = (name: string) => {
+ if (name.length > 0) {
+ setValidName(ValidatedOptions.success);
+ setDisableAction(!checkKnowledgeFormCompletion(knowledgeFormData));
+ return;
+ }
+ setDisableAction(true);
+ setValidName(ValidatedOptions.error);
+ return;
+ };
+
return (
+ Author Info *
+
+ ),
+ id: 'author-info-id'
+ }}
titleDescription="Provide your information required for a GitHub DCO sign-off."
/>
}
@@ -27,16 +68,38 @@ const AuthorInformation: React.FC = ({ email, setEmail, name, setName })
aria-label="email"
placeholder="Enter your email address"
value={email}
+ validated={validEmail}
onChange={(_event, value) => setEmail(value)}
+ onBlur={() => validateEmail(email)}
/>
+ {validEmail === ValidatedOptions.error && (
+
+
+ } variant={validEmail}>
+ Please enter a valid email address.
+
+
+
+ )}
setName(value)}
+ onBlur={() => validateName(name)}
/>
+ {validName === ValidatedOptions.error && (
+
+
+ } variant={validName}>
+ Name is required.
+
+
+
+ )}
);
diff --git a/src/components/Contribute/Knowledge/DocumentInformation/DocumentInformation.tsx b/src/components/Contribute/Knowledge/DocumentInformation/DocumentInformation.tsx
index 38a445bf..aa162413 100644
--- a/src/components/Contribute/Knowledge/DocumentInformation/DocumentInformation.tsx
+++ b/src/components/Contribute/Knowledge/DocumentInformation/DocumentInformation.tsx
@@ -4,8 +4,16 @@ import { Button } from '@patternfly/react-core/dist/dynamic/components/Button';
import { TextInput } from '@patternfly/react-core/dist/dynamic/components/TextInput';
import { UploadFile } from './../UploadFile';
import { Alert, AlertActionLink, AlertActionCloseButton } from '@patternfly/react-core/dist/dynamic/components/Alert';
+import { HelperText } from '@patternfly/react-core/dist/dynamic/components/HelperText';
+import { HelperTextItem } from '@patternfly/react-core/dist/dynamic/components/HelperText';
+import ExclamationCircleIcon from '@patternfly/react-icons/dist/dynamic/icons/exclamation-circle-icon';
+import { ValidatedOptions } from '@patternfly/react-core/dist/esm/helpers/constants';
+import { KnowledgeFormData } from '..';
+import { checkKnowledgeFormCompletion } from '../validation';
interface Props {
+ knowledgeFormData: KnowledgeFormData;
+ setDisableAction: React.Dispatch>;
knowledgeDocumentRepositoryUrl: string;
setKnowledgeDocumentRepositoryUrl: React.Dispatch>;
knowledgeDocumentCommit: string;
@@ -17,6 +25,8 @@ interface Props {
}
const DocumentInformation: React.FC = ({
+ knowledgeFormData,
+ setDisableAction,
knowledgeDocumentRepositoryUrl,
setKnowledgeDocumentRepositoryUrl,
knowledgeDocumentCommit,
@@ -35,6 +45,50 @@ const DocumentInformation: React.FC = ({
const [failureAlertTitle, setFailureAlertTitle] = useState();
const [failureAlertMessage, setFailureAlertMessage] = useState();
+ const [validRepo, setValidRepo] = useState();
+ const [validCommit, setValidCommit] = useState();
+ const [validDocumentName, setValidDocumentName] = useState();
+
+ const validateRepo = (repo: string) => {
+ if (repo.length === 0) {
+ setDisableAction(true);
+ setValidRepo(ValidatedOptions.error);
+ return;
+ }
+ try {
+ new URL(repo);
+ setValidRepo(ValidatedOptions.success);
+ setDisableAction(!checkKnowledgeFormCompletion(knowledgeFormData));
+ return;
+ } catch (e) {
+ setDisableAction(true);
+ setValidRepo(ValidatedOptions.warning);
+ return;
+ }
+ };
+
+ const validateCommit = (commit: string) => {
+ if (commit.length > 0) {
+ setValidCommit(ValidatedOptions.success);
+ setDisableAction(!checkKnowledgeFormCompletion(knowledgeFormData));
+ return;
+ }
+ setDisableAction(true);
+ setValidCommit(ValidatedOptions.error);
+ return;
+ };
+
+ const validateDocumentName = (documentName: string) => {
+ if (documentName.length > 0) {
+ setValidDocumentName(ValidatedOptions.success);
+ setDisableAction(!checkKnowledgeFormCompletion(knowledgeFormData));
+ return;
+ }
+ setDisableAction(true);
+ setValidDocumentName(ValidatedOptions.error);
+ return;
+ };
+
const handleFilesChange = (files: File[]) => {
setUploadedFiles(files);
setDocumentName(files.map((file) => file.name).join(', ')); // Populate the patterns field
@@ -104,7 +158,17 @@ const DocumentInformation: React.FC = ({
+
+ Document Info *
+
+ ),
+ id: 'doc-info-id'
+ }}
+ titleDescription="Add the relevant document's information"
+ />
}
>
@@ -132,26 +196,69 @@ const DocumentInformation: React.FC = ({
isRequired
type="url"
aria-label="repo"
+ validated={validRepo}
placeholder="Enter repo url where document exists"
value={knowledgeDocumentRepositoryUrl}
onChange={(_event, value) => setKnowledgeDocumentRepositoryUrl(value)}
+ onBlur={() => validateRepo(knowledgeDocumentRepositoryUrl)}
/>
+ {validRepo === ValidatedOptions.error && (
+
+
+ } variant={validRepo}>
+ Repo URL is required.
+
+
+
+ )}
+ {validRepo === ValidatedOptions.warning && (
+
+
+ } variant="error">
+ Please enter a valid URL.
+
+
+
+ )}
+
setKnowledgeDocumentCommit(value)}
+ onBlur={() => validateCommit(knowledgeDocumentCommit)}
/>
+ {validCommit === ValidatedOptions.error && (
+
+
+ } variant={validCommit}>
+ Valid commit SHA is required.
+
+
+
+ )}
setDocumentName(value)}
+ onBlur={() => validateDocumentName(documentName)}
/>
+ {validDocumentName === ValidatedOptions.error && (
+
+
+ } variant={validDocumentName}>
+ Document name is required.
+
+
+
+ )}
) : (
<>
diff --git a/src/components/Contribute/Knowledge/DownloadAttribution/DownloadAttribution.tsx b/src/components/Contribute/Knowledge/DownloadAttribution/DownloadAttribution.tsx
index 7b4ba60e..e979ba38 100644
--- a/src/components/Contribute/Knowledge/DownloadAttribution/DownloadAttribution.tsx
+++ b/src/components/Contribute/Knowledge/DownloadAttribution/DownloadAttribution.tsx
@@ -4,11 +4,12 @@ import { validateFields } from '../validation';
import { ActionGroupAlertContent, KnowledgeFormData } from '..';
interface Props {
+ disableAction: boolean;
knowledgeFormData: KnowledgeFormData;
setActionGroupAlertContent: React.Dispatch>;
}
-const DownloadAttribution: React.FC = ({ knowledgeFormData, setActionGroupAlertContent }) => {
+const DownloadAttribution: React.FC = ({ disableAction, knowledgeFormData, setActionGroupAlertContent }) => {
const handleDownloadAttribution = () => {
// Because I have overly complicated the validatedFields function all fields are being checked and not just the attribution ones here. Not ideal.
if (!validateFields(knowledgeFormData, setActionGroupAlertContent)) return;
@@ -31,7 +32,7 @@ const DownloadAttribution: React.FC = ({ knowledgeFormData, setActionGrou
};
return (
-