diff --git a/.circleci/config.yml b/.circleci/config.yml index 876b8de3..18451f03 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,53 +2,54 @@ version: 2.1 orbs: gh: circleci/github-cli@2.2.0 jobs: - update-nvrf: - docker: - - image: cimg/base:stable - environment: - URL: https://www.eac.gov/sites/default/files/eac_assets/1/6/Federal_Voter_Registration_ENG.pdf - NVRF_FILE_NAME: Federal_Voter_Registration_ENG.pdf - NEW_BRANCH_NAME: upload-nvrf-pdf - steps: - #Install gh - - gh/install - - run: - name: Git Clone and Config - command: | - GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" git clone -b "update-nvrf-pdf" "$CIRCLE_REPOSITORY_URL" - cd vote-gov-nvrf-app - git config user.email "circleci@df52e93b2563.(none)" - git config user.name "Pipeline Commit" - - run: - name: Fetch NVRF from eac.gov - command: | - curl -o Federal_Voter_Registration_ENG.pdf $URL - #Save pdf as an artifact - - store_artifacts: - path: Federal_Voter_Registration_ENG.pdf - destination: /pdf-files - # Create a variable to store the PDF artifact - - run: - name: Replace current NVRF - command: | - cd vote-gov-nvrf-app - artifacts=$(curl -X GET "https://circleci.com/api/v2/project/github/usagov/vote-gov-nvrf-app/$CIRCLE_BUILD_NUM/artifacts" \ - -H "Accept: application/json" \ - -u "$NVRF_PDF_WRITE:") - echo "read -r -d '' STORED_ARTIFACTS \<< 'EOF_ARTIFACTS'" >> $BASH_ENV - echo "$artifacts" >> $BASH_ENV - echo "EOF_ARTIFACTS" >> $BASH_ENV - cd public/files - rm $NVRF_FILE_NAME - echo $STORED_ARTIFACTS > $NVRF_FILE_NAME - - run: - name: Create Pull Request - shell: /bin/bash - command: | - cd vote-gov-nvrf-app - echo $NVRF_PDF_WRITE | tee /tmp/key.txt - gh auth login --with-token < /tmp/key.txt - gh pr create --title "Update NVRF PDF" --body "Upload the new NVRF PDF from eac.gov. This is an automated job from CircleCI" || true +# turning off job until review of pdf can be done + # update-nvrf: + # docker: + # - image: cimg/base:stable + # environment: + # URL: https://www.eac.gov/sites/default/files/eac_assets/1/6/Federal_Voter_Registration_ENG.pdf + # NVRF_FILE_NAME: Federal_Voter_Registration_ENG.pdf + # NEW_BRANCH_NAME: upload-nvrf-pdf + # steps: + # #Install gh + # - gh/install + # - run: + # name: Git Clone and Config + # command: | + # GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" git clone -b "update-nvrf-pdf" "$CIRCLE_REPOSITORY_URL" + # cd vote-gov-nvrf-app + # git config user.email "circleci@df52e93b2563.(none)" + # git config user.name "Pipeline Commit" + # - run: + # name: Fetch NVRF from eac.gov + # command: | + # curl -o Federal_Voter_Registration_ENG.pdf $URL + # #Save pdf as an artifact + # - store_artifacts: + # path: Federal_Voter_Registration_ENG.pdf + # destination: /pdf-files + # # Create a variable to store the PDF artifact + # - run: + # name: Replace current NVRF + # command: | + # cd vote-gov-nvrf-app + # artifacts=$(curl -X GET "https://circleci.com/api/v2/project/github/usagov/vote-gov-nvrf-app/$CIRCLE_BUILD_NUM/artifacts" \ + # -H "Accept: application/json" \ + # -u "$NVRF_PDF_WRITE:") + # echo "read -r -d '' STORED_ARTIFACTS \<< 'EOF_ARTIFACTS'" >> $BASH_ENV + # echo "$artifacts" >> $BASH_ENV + # echo "EOF_ARTIFACTS" >> $BASH_ENV + # cd public/files + # rm $NVRF_FILE_NAME + # echo $STORED_ARTIFACTS > $NVRF_FILE_NAME + # - run: + # name: Create Pull Request + # shell: /bin/bash + # command: | + # cd vote-gov-nvrf-app + # echo $NVRF_PDF_WRITE | tee /tmp/key.txt + # gh auth login --with-token < /tmp/key.txt + # gh pr create --title "Update NVRF PDF" --body "Upload the new NVRF PDF from eac.gov. This is an automated job from CircleCI" || true cypress-testing: docker: - image: cypress/included:cypress-13.3.0-node-20.6.1-chrome-116.0.5845.187-1-ff-117.0-edge-116.0.1938.76-1 @@ -70,13 +71,14 @@ workflows: main-workflow: jobs: - cypress-testing - update-nvrf-workflow: - jobs: - - update-nvrf - triggers: - - schedule: - cron: '0 0 * * 5' - filters: - branches: - only: - - stage \ No newline at end of file +# turning off job until review of pdf can be done + # update-nvrf-workflow: + # jobs: + # - update-nvrf + # triggers: + # - schedule: + # cron: '0 0 * * 5' + # filters: + # branches: + # only: + # - stage \ No newline at end of file diff --git a/data/es/Federal_Voter_Registration_es.pdf b/data/es/Federal_Voter_Registration_es.pdf new file mode 100644 index 00000000..5a855823 Binary files /dev/null and b/data/es/Federal_Voter_Registration_es.pdf differ diff --git a/data/es/navigation.json b/data/es/navigation.json index 52805433..ea0a7fc4 100644 --- a/data/es/navigation.json +++ b/data/es/navigation.json @@ -1,7 +1,5 @@ { "back": { - "vote": "Go back to Vote.gov", - "select_state": "Go back to select your state", "state_reg_options": "Go back to state registration options", "eligibility_req": "Back to state eligibility requirements", "reg_options": "Back to registration options", @@ -11,13 +9,13 @@ "edit_info": "Edit registration information" }, "next": { - "next": "Next", "start": "Continue to the digital form filler on vote.gov", "reg_options": "Continue to view registration options", "address_location": "Continue to address and location", "identification": "Continue to identification", "political_party": "Continue to political party", - "confirm_info": "Continue to review your information" + "confirm_info": "Continue to review your information", + "delivery": "Confirm and continue" }, "step_label_1": "Personal information", "step_label_2": "Address and location", diff --git a/data/es/strings.json b/data/es/strings.json index 10aecfe0..d5216ae9 100644 --- a/data/es/strings.json +++ b/data/es/strings.json @@ -1,33 +1,18 @@ { "notRequired": "Not required for your state", - "newWindow": "Go to your state's PDF form in a new tab", "newTab": "Print your mail-in form in a new tab", "nameChange": "I have legally changed my name since I last registered in this state.", - "dob": "Date of Birth", "month": "Month", "day": "Day", "year": "Year", - "stateName": "Go to the PDF form on @state_name's website", - "checkReg": "Check your registration", - "checkRegWY": "Find your local office to check your registration", - "learnMore": "Learn more about your voting options", - "backBtn": "Back to Vote.gov", "backIcon": "back arrow icon", "forwardIcon": "forward arrow icon", - "stateOnlineName": "Register on @state_name's website", - "confirm": "Confirm and continue", "selectState": "Select your state or territory", "selectStateAriaLabel": "State selection dropdown menu", "lastUpdated": "@state_name information last updated ", - "privacyPolicy": "Privacy policy", - "extlink": "External link opens new window", "emailLabel": "Voter Contact", - "emailHint": "For example: email@address.com", "select": "- Select -", "download": "Download your mail-in form", - "downloadText": "If form does not open in a new tab you can download using the option below.", "mailDeadlineLabel": "Mail-in registration deadline:", - "inPersonBtn": "Learn more on @state_name's election website", - "inPersonBtnWY": "Find in-person registration locations", "idSelectionAriaLabel": "Choose identification type" } \ No newline at end of file diff --git a/data/navigation.json b/data/navigation.json index 52805433..ea0a7fc4 100644 --- a/data/navigation.json +++ b/data/navigation.json @@ -1,7 +1,5 @@ { "back": { - "vote": "Go back to Vote.gov", - "select_state": "Go back to select your state", "state_reg_options": "Go back to state registration options", "eligibility_req": "Back to state eligibility requirements", "reg_options": "Back to registration options", @@ -11,13 +9,13 @@ "edit_info": "Edit registration information" }, "next": { - "next": "Next", "start": "Continue to the digital form filler on vote.gov", "reg_options": "Continue to view registration options", "address_location": "Continue to address and location", "identification": "Continue to identification", "political_party": "Continue to political party", - "confirm_info": "Continue to review your information" + "confirm_info": "Continue to review your information", + "delivery": "Confirm and continue" }, "step_label_1": "Personal information", "step_label_2": "Address and location", diff --git a/data/strings.json b/data/strings.json index 10aecfe0..d5216ae9 100644 --- a/data/strings.json +++ b/data/strings.json @@ -1,33 +1,18 @@ { "notRequired": "Not required for your state", - "newWindow": "Go to your state's PDF form in a new tab", "newTab": "Print your mail-in form in a new tab", "nameChange": "I have legally changed my name since I last registered in this state.", - "dob": "Date of Birth", "month": "Month", "day": "Day", "year": "Year", - "stateName": "Go to the PDF form on @state_name's website", - "checkReg": "Check your registration", - "checkRegWY": "Find your local office to check your registration", - "learnMore": "Learn more about your voting options", - "backBtn": "Back to Vote.gov", "backIcon": "back arrow icon", "forwardIcon": "forward arrow icon", - "stateOnlineName": "Register on @state_name's website", - "confirm": "Confirm and continue", "selectState": "Select your state or territory", "selectStateAriaLabel": "State selection dropdown menu", "lastUpdated": "@state_name information last updated ", - "privacyPolicy": "Privacy policy", - "extlink": "External link opens new window", "emailLabel": "Voter Contact", - "emailHint": "For example: email@address.com", "select": "- Select -", "download": "Download your mail-in form", - "downloadText": "If form does not open in a new tab you can download using the option below.", "mailDeadlineLabel": "Mail-in registration deadline:", - "inPersonBtn": "Learn more on @state_name's election website", - "inPersonBtnWY": "Find in-person registration locations", "idSelectionAriaLabel": "Choose identification type" } \ No newline at end of file diff --git a/index.html b/index.html index af18576a..c9f5bd28 100644 --- a/index.html +++ b/index.html @@ -1,6 +1,21 @@ + + + + + @@ -11,6 +26,10 @@ + + +
@@ -39,7 +58,7 @@

Sign in

-
+
diff --git a/nvrf/assets/cards.json b/nvrf/assets/cards.json index c703ee5f..f289d229 100644 --- a/nvrf/assets/cards.json +++ b/nvrf/assets/cards.json @@ -1 +1 @@ -[{"uuid":"0ac52b5d-4381-4b4e-830e-38319f3a3757","lang":"en","heading":"I am already registered in @state_name and need to change my information.","body":"\u003Cp\u003EReport a change to registration information, such as name, address, or political party.\u003C\/p\u003E","button_label":"Change registration information","image_url":""},{"uuid":"3abd804c-2787-44f9-a06b-ad6d63ca797f","lang":"en","heading":"I am registering in @state_name for the first time.","body":"\u003Cp\u003ERegister for the first time in this state. We recommend having your driver\u2019s license or non-driver identification number available.\u003C\/p\u003E","button_label":"Begin new registration","image_url":""}] \ No newline at end of file +[{"uuid":"0ac52b5d-4381-4b4e-830e-38319f3a3757","lang":"en","heading":"I am already registered in @state_name and need to change my information.","body":"\u003Cp\u003EReport a change to registration information, such as name, address, or political party.\u003C\/p\u003E","button_label":"Change registration information","image_url":""},{"uuid":"5922e06c-ac2f-475d-ab10-abfdeb65de43","lang":"en","heading":"","body":"\u003Cp\u003EOMB Control No. 3265-0015\u003C\/p\u003E\u003Cp\u003E\u003Ca href=\u0022\/form-filler-privacy-policy\u0022\u003EPrivacy policy\u003C\/a\u003E\u003C\/p\u003E","button_label":"","image_url":""},{"uuid":"3abd804c-2787-44f9-a06b-ad6d63ca797f","lang":"en","heading":"I am registering in @state_name for the first time.","body":"\u003Cp\u003ERegister for the first time in this state. We recommend having your driver\u2019s license or non-driver identification number available.\u003C\/p\u003E","button_label":"Begin new registration","image_url":""}] \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index 339864ac..df28f211 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -4,11 +4,11 @@ import PathSelection from 'Views/PathSelection.jsx'; import MultiStepForm from 'Views/MultiStepForm.jsx'; import {fetchData, fetchStaticData, sanitizeDOM} from 'Utils/JsonHelper.jsx'; import { HelmetProvider } from "react-helmet-async"; -import {getFieldValue} from "Utils/fieldParser.jsx"; +import loadPdf from './Utils/pdfLoader'; + const currentStateId = document.getElementById('root').getAttribute('data-stateId'); const returnPath = document.getElementById('root').getAttribute('data-returnPath'); -const privacyPath = document.getElementById('root').getAttribute('data-privacyPath'); function App() { const [states, setStates] = useState(''); @@ -18,6 +18,16 @@ function App() { const [fieldContent, setFieldContent] = useState('') const [stringContent, setStringContent] = useState('') + const [pdfDoc, setPdfDoc] = useState(null); + const [form, setForm] = useState(null); + + useEffect(() => { + loadPdf().then(({ pdfDoc, form }) => { + setPdfDoc(pdfDoc); + setForm(form); + }); + }, []); + useEffect(() => { fetchData("states.json", setStates); fetchData("pages.json", setContent); @@ -87,7 +97,9 @@ function App() { }; // Only render the markup if the data is loaded. - if (states && content && navContent && fieldContent && stringContent) { + if (states && cards && content && navContent && fieldContent && stringContent) { + // Get NVRF footer card + const cardFooter = cards.find(item => item.uuid === "5922e06c-ac2f-475d-ab10-abfdeb65de43"); const statesList = [] for (let i = 0; i < states.length; i++) { @@ -146,17 +158,18 @@ function App() { registrationPath={registrationPath} getFormStep={getFormStep} stringContent={stringContent} + pdfDoc={pdfDoc} + form={form} />} {step >= 1 &&
-

{getFieldValue(content, "2c597df4-53b6-4ef5-8301-7817b04e1099", "omb_number")} -
+

{lastUpdatedText.replace("@state_name", stateData.name)} - -

- {privacyPath && ( -

{stringContent.privacyPolicy}

+ +

+ {cardFooter && ( +
)}
} diff --git a/src/Components/FieldComponents/DateFields.jsx b/src/Components/FieldComponents/DateFields.jsx index 5a4a8595..aee17eaf 100644 --- a/src/Components/FieldComponents/DateFields.jsx +++ b/src/Components/FieldComponents/DateFields.jsx @@ -9,12 +9,14 @@ function DateFields({ inputData, saveFieldData, dateFormat, fieldData }) { let year = fieldData.date_of_birth_year; let yearStart = year.slice(0, 2); + /* Comment out age validation for now let currentDate = new Date(); let currentMonth = currentDate.getMonth(); let currentDay = currentDate.getDate(); let currentYear = currentDate.getFullYear(); let age = currentYear - year - (currentMonth <= month && currentDay < day); - + */ + if (type === "all") { let dobValues = [ month.length === 2, @@ -27,8 +29,8 @@ function DateFields({ inputData, saveFieldData, dateFormat, fieldData }) { day >= 1, yearStart <= 20, yearStart >= 19, - age <= 120, - age >= 16 + //age <= 120, + //age >= 16 ]; if (dobValues.includes(false)) { @@ -50,13 +52,14 @@ function DateFields({ inputData, saveFieldData, dateFormat, fieldData }) { } else { return false } - } else if (type === "year") { + } //Removing age validation for now + /* else if (type === "year") { if (age > 110 || age < 17) { return true } else { return false } - } + } */ }; return ( @@ -138,7 +141,7 @@ function DateFields({ inputData, saveFieldData, dateFormat, fieldData }) { required={true} aria-invalid={false} type="text" - pattern="19\d{2}|200\d{1}" + pattern="19\d{2}|20\d{2}" inputMode="numeric" minLength={4} maxLength={4} diff --git a/src/Components/Fields/StateIDNum.jsx b/src/Components/Fields/StateIDNum.jsx index 5cd3e1de..c168090c 100644 --- a/src/Components/Fields/StateIDNum.jsx +++ b/src/Components/Fields/StateIDNum.jsx @@ -3,7 +3,7 @@ import FieldContainer from 'Components/FieldContainer'; import {getField} from "Utils/fieldParser"; function StateIDNum(props){ - const uuid = "acd7f272-7a37-43f0-b51a-c78daf31e5fd"; + const uuid = "e2da00fa-0f1b-4e98-9472-c00649266eb4"; const field = getField(props.fieldContent, uuid); const stateField = getField(props.stateData.nvrf_fields, field.uuid); diff --git a/src/Utils/GenerateFilledPDF.jsx b/src/Utils/GenerateFilledPDF_en.jsx similarity index 90% rename from src/Utils/GenerateFilledPDF.jsx rename to src/Utils/GenerateFilledPDF_en.jsx index 37fd0565..cfe6c477 100644 --- a/src/Utils/GenerateFilledPDF.jsx +++ b/src/Utils/GenerateFilledPDF_en.jsx @@ -1,22 +1,14 @@ -import { PDFDocument} from 'pdf-lib'; import download from "downloadjs"; +import loadPdf from './pdfLoader'; const GenerateFilledPDF = async function (btnType,formData,pagesKept) { - // Fetch the PDF with form fields - const lang = document.documentElement.lang; - const locale = lang !== "en" ? `/${lang}` : ""; - const formUrl = `/data${locale}/Federal_Voter_Registration_${lang}.pdf`; - const formPdfBytes = await fetch(formUrl).then(res => res.arrayBuffer()) - // Load a PDF with form fields - const pdfDoc = await PDFDocument.load(formPdfBytes) - - // Get the form containing all the fields - const form = pdfDoc.getForm() + // Fetch the PDF with form field + const { pdfDoc, form } = await loadPdf(); //-------- Get PDF Fields by machine name ------------------ const citizen = form.getRadioGroup('citizen'); const eighteenYearsOld = form.getRadioGroup('eighteen_years'); - const title = form.getRadioGroup('salutation'); + const title = form.getRadioGroup('salutation'); const firstName = form.getTextField('first_name'); const middleNames = form.getTextField('middle_names'); const lastName = form.getTextField('last_name'); @@ -68,7 +60,7 @@ const GenerateFilledPDF = async function (btnType,formData,pagesKept) { middleNames.setText(formData.middle_name); lastName.setText(formData.last_name); - //Dropdown to checkbox/radio logic for suffix + //Current suffix if(formData.suffix){ suffix.select(formData.suffix); } @@ -81,7 +73,7 @@ const GenerateFilledPDF = async function (btnType,formData,pagesKept) { middleNames2.setText(formData.prev_middle_name); lastName2.setText(formData.prev_last_name); - //Dropdown to checkbox/radio logic for suffix + //Previous suffix if(formData.prev_suffix){ suffix2.select(formData.prev_suffix); } diff --git a/src/Utils/GenerateFilledPDF_es.jsx b/src/Utils/GenerateFilledPDF_es.jsx new file mode 100644 index 00000000..d8815888 --- /dev/null +++ b/src/Utils/GenerateFilledPDF_es.jsx @@ -0,0 +1,176 @@ +import download from "downloadjs"; +import loadPdf from './pdfLoader'; + +const GenerateFilledPDF = async function (btnType,formData,pagesKept) { + // Fetch the PDF with form field + const { pdfDoc, form } = await loadPdf(); + + //-------- Get PDF Fields by machine name ------------------ + const citizen = form.getDropdown('citizen'); //TEMP spanish condition field type + const eighteenYearsOld = form.getDropdown('eighteen_years'); //TEMP spanish condition field type + const title = form.getDropdown('salutation'); //TEMP spanish condition field type + const firstName = form.getTextField('first_name'); + const middleNames = form.getTextField('middle_names'); + const lastName = form.getTextField('last_name'); + const suffix = form.getDropdown('suffix'); //TEMP spanish condition field type + + const title2 = form.getDropdown('salutation_2');//TEMP spanish condition field type + const firstName2 = form.getTextField('first_name_2'); + const middleNames2 = form.getTextField('middle_names_2'); + const lastName2 = form.getTextField('last_name_2'); + const suffix2 = form.getDropdown('suffix_2');//TEMP spanish condition field type + + const dobMonth = form.getTextField('dob_month'); + const dobDay = form.getTextField('dob_day'); + const dobYear = form.getTextField('dob_year'); + const phoneNumber = form.getTextField('telephone_number'); + const race = form.getTextField('race_ethnic_group'); + + const homeAddress = form.getTextField('home_address'); + const aptNumber = form.getTextField('apt_lot_number'); + const city = form.getTextField('city'); + const state = form.getTextField('state'); + const zipcode = form.getTextField('zip_code'); + + const mailAddress = form.getTextField('mail_address'); + const mailCity = form.getTextField('mail_city'); + const mailState = form.getTextField('mail_state'); + const mailZipcode = form.getTextField('mail_zip_code'); + + const prevAddress = form.getTextField('prev_address'); + const prevAptNumber = form.getTextField('prev_apt_lot_number'); + const prevCity = form.getTextField('prev_city'); + const prevState = form.getTextField('prev_state'); + const prevZipcode = form.getTextField('prev_zip_code'); + + const idNumber = form.getTextField('id_number'); + const politicalParty = form.getTextField('choice_of_party'); + + // -----------Fill in the pdf fields-------------------------- + // (1) Personal Information + //Citizen and age + citizen.select('Yes/si'); + eighteenYearsOld.select('Yes/si'); + + //Current Name - TODO: spanish field data needs to match the PDF + if(formData.title) { + title.select(formData.title); + } + firstName.setText(formData.first_name); + middleNames.setText(formData.middle_name); + lastName.setText(formData.last_name); + + //Current suffix - TODO: spanish field data needs to match the PDF + if(formData.suffix){ + suffix.select(formData.suffix); + } + + //Previous Name + if(formData.prev_title){ + title2.select(formData.prev_title); + } + firstName2.setText(formData.prev_first_name); + middleNames2.setText(formData.prev_middle_name); + lastName2.setText(formData.prev_last_name); + + //Previous suffix + if(formData.prev_suffix){ + suffix2.select(formData.prev_suffix); + } + + //Date of Birth, Phone, Race + dobMonth.setText(formData.date_of_birth_month); + // adjusting font size + dobMonth.setFontSize(12) + dobDay.setText(formData.date_of_birth_day); + // adjusting font size + dobDay.setFontSize(12) + dobYear.setText(formData.date_of_birth_year); + // adjusting font size + dobYear.setFontSize(5) + phoneNumber.setText(formData.phone_number); + race.setText(formData.race); + + //(2) Addresses + //Home + const currentAddress = formData.street_address + formData.apt_num + formData.city + formData.zip_code; + //check if anything other than default state selection is filled out + if (currentAddress) { + homeAddress.setText(formData.street_address); + aptNumber.setText(formData.apt_num); + city.setText(formData.city); + state.setText(formData.state); + zipcode.setText(formData.zip_code); + } + + //Mail + mailAddress.setText(`${formData.mail_street_address} ${formData.mail_apt_num}`); + mailCity.setText(formData.mail_city); + mailState.setText(formData.mail_state); + mailZipcode.setText(formData.mail_zip_code); + //Previous + prevAddress.setText(formData.prev_street_address); + prevAptNumber.setText(formData.prev_apt_num); + prevCity.setText(formData.prev_city); + prevState.setText(formData.prev_state); + prevZipcode.setText(formData.prev_zip_code); + + //(3) Identification + //No id or ssn + if ((formData.id_number === '') && (formData.ssn_number === '')) { + idNumber.setText("None"); + //Both id and ssn + } else if ((formData.id_number != '') && (formData.ssn_number != '')) { + idNumber.setText(formData.id_number + ", " + formData.ssn_number); + //Id or ssn + } else { + idNumber.setText(formData.id_number + formData.ssn_number); + } + + //(4) Political Party + politicalParty.setText(formData.party_choice); + + //-------------End PDF Fill--------------- + + //Remove unneccessary pages + let shift = 0; + const totalPages = pdfDoc.getPageCount(); + let pageCount = totalPages; + let pagesKeptArray = pagesKept.split(','); + for(let i = 0; i < totalPages; i++){ + if(!pagesKeptArray.includes(i.toString())){ + pdfDoc.removePage(i - shift); + shift++; + pageCount--; + } + } + + // Rearrange pages + const genInstrutPages = pagesKeptArray.splice(0,2); + pagesKeptArray.splice(2,0,genInstrutPages[0],genInstrutPages[1]); + + const reorderPages = (pdfDoc, newOrder) => { + const pages = pdfDoc.getPages(); + for (let currentPage = 0; currentPage < newOrder.length; currentPage++) { + pdfDoc.removePage(currentPage); + pdfDoc.insertPage(currentPage, pages[newOrder[currentPage]]); + } + }; + + reorderPages(pdfDoc, pagesKeptArray); + + // Serialize the PDFDocument to bytes (a Uint8Array) + const pdfBytes = await pdfDoc.save() + + + if (btnType === 'newTab') { + // Trigger the browser to open a new tab with the PDF document + var blobURL = URL.createObjectURL(new Blob([pdfBytes], {type: 'application/pdf'})); + window.open(blobURL); + } else if (btnType === 'download') { + // Trigger the browser to download the PDF document + download(pdfBytes, `national_voter_registration_form_${formData.state}.pdf`, "application/pdf"); + } +} + +export default GenerateFilledPDF; \ No newline at end of file diff --git a/src/Utils/pdfLoader.jsx b/src/Utils/pdfLoader.jsx new file mode 100644 index 00000000..a0216c2d --- /dev/null +++ b/src/Utils/pdfLoader.jsx @@ -0,0 +1,13 @@ +import { PDFDocument } from 'pdf-lib'; + +const loadPdf = async () => { + const lang = document.documentElement.lang; + const locale = lang!== "en"? `/${lang}` : ""; + const formUrl = `/data${locale}/Federal_Voter_Registration_${lang}.pdf`; + const formPdfBytes = await fetch(formUrl).then(res => res.arrayBuffer()) + const pdfDoc = await PDFDocument.load(formPdfBytes) + const form = pdfDoc.getForm() + return { pdfDoc, form }; +}; + +export default loadPdf; \ No newline at end of file diff --git a/src/Views/FormPages/Delivery.jsx b/src/Views/FormPages/Delivery.jsx index dad78cf4..e033b281 100644 --- a/src/Views/FormPages/Delivery.jsx +++ b/src/Views/FormPages/Delivery.jsx @@ -1,5 +1,6 @@ import { Button, Icon, Grid } from '@trussworks/react-uswds'; -import GenerateFilledPDF from 'Utils/GenerateFilledPDF'; +import GenerateFilledPDF_en from 'Utils/GenerateFilledPDF_en'; +import GenerateFilledPDF_es from 'Utils/GenerateFilledPDF_es'; import { sanitizeDOM } from 'Utils/JsonHelper'; import {renderToStaticMarkup} from "react-dom/server"; @@ -7,6 +8,8 @@ function Delivery(props) { const content = props.content; const state = props.stateData; const stringContent = props.stringContent + const lang = document.documentElement.lang; + const GenerateFilledPDF = (lang == "es") ? GenerateFilledPDF_es : GenerateFilledPDF_en; // Add A/B Message randomization. // example: const reminderMessage = randomProperty(content.reminder_messages); @@ -45,13 +48,22 @@ function Delivery(props) {
-
- diff --git a/src/Views/MultiStepForm.jsx b/src/Views/MultiStepForm.jsx index 7b40d848..00e828a5 100644 --- a/src/Views/MultiStepForm.jsx +++ b/src/Views/MultiStepForm.jsx @@ -246,7 +246,7 @@ function MultiStepForm(props) { case 4: return navContent.next.confirm_info; case 5: - return (stringContent.confirm);//TODO replace + return navContent.next.delivery; } } diff --git a/testing/cypress/e2e/tests/inPerson.cy.js b/testing/cypress/e2e/tests/inPerson.cy.js index 3a15999e..504edca1 100644 --- a/testing/cypress/e2e/tests/inPerson.cy.js +++ b/testing/cypress/e2e/tests/inPerson.cy.js @@ -19,7 +19,7 @@ cy.get('[data-test="errorText"]').should('contain.text', 'Confirm eligibility to // verify user CAN move forward after checking box cy.get('[data-test="checkBox"]').click() -// todo: come back after fix +// todo: come back after fix // cy.get('[data-test="errorText"]').should('not.exist') cy.get('[data-test="nextBtn"]').click() @@ -29,7 +29,7 @@ cy.get('[data-test="pathBtn"]').then(btn => { cy.get(btn[0]).click({force: true}) }) -// fill out personal information +// fill out personal information // * check that user can not move forward without filling out fields cy.get('[data-test="nextBtn"]').click().click() cy.get('[data-test="errorText"]').should('exist') @@ -49,7 +49,7 @@ cy.get('[data-test="lastName"]').type(data.personalInformationLast) cy.get('[data-test="phoneNumber"]').type(data.personalInformationNumber) // Validate text box has correct text cy.get('[data-test="firstName"]').should('have.value', data.personalInformationName) -cy.get('[data-test="middleName"]').should('have.value', data.personalInformationMiddle) +cy.get('[data-test="middleName"]').should('have.value', data.personalInformationMiddle) cy.get('[data-test="lastName"]').should('have.value', data.personalInformationLast) cy.get('[data-test="phoneNumber"]').should('contain.value', data.personalInformationNumber2) @@ -60,7 +60,7 @@ cy.get('[data-test="prevMiddleName"]').type(data.personalInformationMiddle) cy.get('[data-test="prevLastName"]').type(data.personalInformationLast) // Validate text box has correct text cy.get('[data-test="prevFirstName"]').should('have.value', data.personalInformationName) -cy.get('[data-test="prevMiddleName"]').should('have.value', data.personalInformationMiddle) +cy.get('[data-test="prevMiddleName"]').should('have.value', data.personalInformationMiddle) cy.get('[data-test="prevLastName"]').should('have.value', data.personalInformationLast) // * check date of birth fields @@ -85,12 +85,12 @@ cy.get('[data-test="aptNumber"]').type(data.addressApt) cy.get('[data-test="city"]').type(data.addressTown) cy.get('[data-test="zip"]').type(data.addressZip) // Validate text box has correct text -cy.get('[data-test="street"]').should('have.value', data.addressStreet) +cy.get('[data-test="street"]').should('have.value', data.addressStreet) cy.get('[data-test="aptNumber"]').should('have.value', data.addressApt) cy.get('[data-test="city"]').should('have.value', data.addressTown) cy.get('[data-test="zip"]').should('have.value', data.addressZip) -// * check that mailing address work +// * check that mailing address work cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[1]).click({force: true}) }) @@ -99,7 +99,7 @@ cy.get('[data-test="mailStreet"]').type(data.addressStreet) cy.get('[data-test="mailCity"]').type(data.addressTown) cy.get('[data-test="mailZip"]').type(data.addressZip) // Validate text box has correct text -cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) +cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) cy.get('[data-test="mailCity"]').should('have.value', data.addressTown) cy.get('[data-test="mailZip"]').should('have.value', data.addressZip) @@ -108,7 +108,7 @@ cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[1]).click({force: true}) }) -// * check recently moved option +// * check recently moved option cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[0]).click({force: true}) }) @@ -118,7 +118,7 @@ cy.get('[data-test="prevAptNumber"]').type(data.addressApt) cy.get('[data-test="prevCity"]').type(data.addressTown) cy.get('[data-test="prevZip"]').type(data.addressZip) // Validate text box has correct text -cy.get('[data-test="prevStreet"]').should('have.value', data.addressStreet) +cy.get('[data-test="prevStreet"]').should('have.value', data.addressStreet) cy.get('[data-test="prevAptNumber"]').should('have.value', data.addressApt) cy.get('[data-test="prevCity"]').should('have.value', data.addressTown) cy.get('[data-test="prevZip"]').should('have.value', data.addressZip) @@ -127,15 +127,15 @@ cy.get('[data-test="prevZip"]').should('have.value', data.addressZip) cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[0]).click({force: true}) }) - - // * check does not have permanent option + + // * check does not have permanent option cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[1]).click({force: true}) }) - + cy.get('[data-testid="Select"]').select(data.addressState) // Validate text box has correct text for mailing address - cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) + cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) cy.get('[data-test="mailCity"]').should('have.value', data.addressTown) cy.get('[data-test="mailZip"]').should('have.value', data.addressZip) @@ -158,7 +158,7 @@ cy.get('[data-test="dropDown"]').then(dropDown => { cy.get(dropDown[0]).select("Social security number (last 4 digits)") }) cy.get('[data-test="ssn"]').type(data.ssn) -// Validate fields have correct data +// Validate fields have correct data cy.get('[data-test="dropDown"]').should('have.value', data.ssnValue) cy.get('[data-test="ssn"]').should('have.value', data.ssn) @@ -170,7 +170,7 @@ cy.get('p').should('contain.text', '"None" will appear on your completed form.') cy.get('[data-test="nextBtn"]').click() - // political party + // political party cy.get('[data-test="politicalParty"]').type(data.politicalParty) cy.get('[data-test="nextBtn"]').click() @@ -181,7 +181,7 @@ cy.get('[data-test="nextBtn"]').click() cy.get('[data-test="addressConfirm"]').should('contain.text', 'Your Alabama mail-in registration form is complete and ready to print.') // * check that download opens in new window -cy.get('[data-test="pdfBtn"]').click() +cy.get('[data-test="pdfBtnNewTab"]').click() cy.get('@open').should('have.been.calledOnce') }) @@ -208,7 +208,7 @@ cy.get('[data-test="pathBtn"]').then(btn => { cy.get(btn[1]).click({force: true}) }) -// fill out personal information +// fill out personal information // * check that user can not move forward without filling out fields cy.get('[data-test="nextBtn"]').click().click() cy.get('[data-test="errorText"]').should('exist') @@ -228,7 +228,7 @@ cy.get('[data-test="lastName"]').type(data.personalInformationLast) cy.get('[data-test="phoneNumber"]').type(data.personalInformationNumber) // Validate text box has correct text cy.get('[data-test="firstName"]').should('have.value', data.personalInformationName) -cy.get('[data-test="middleName"]').should('have.value', data.personalInformationMiddle) +cy.get('[data-test="middleName"]').should('have.value', data.personalInformationMiddle) cy.get('[data-test="lastName"]').should('have.value', data.personalInformationLast) cy.get('[data-test="phoneNumber"]').should('contain.value', data.personalInformationNumber2) @@ -254,12 +254,12 @@ cy.get('[data-test="aptNumber"]').type(data.addressApt) cy.get('[data-test="city"]').type(data.addressTown) cy.get('[data-test="zip"]').type(data.addressZip) // Validate text box has correct text -cy.get('[data-test="street"]').should('have.value', data.addressStreet) +cy.get('[data-test="street"]').should('have.value', data.addressStreet) cy.get('[data-test="aptNumber"]').should('have.value', data.addressApt) cy.get('[data-test="city"]').should('have.value', data.addressTown) cy.get('[data-test="zip"]').should('have.value', data.addressZip) -// * check that mailing address work +// * check that mailing address work cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[1]).click({force: true}) }) @@ -267,7 +267,7 @@ cy.get('[data-test="mailStreet"]').type(data.addressStreet) cy.get('[data-test="mailCity"]').type(data.addressTown) cy.get('[data-test="mailZip"]').type(data.addressZip) // Validate text box has correct text -cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) +cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) cy.get('[data-test="mailCity"]').should('have.value', data.addressTown) cy.get('[data-test="mailZip"]').should('have.value', data.addressZip) @@ -276,7 +276,7 @@ cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[1]).click({force: true}) }) -// * check does not have permanent option +// * check does not have permanent option cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[0]).click({force: true}) }) @@ -287,7 +287,7 @@ cy.get('[data-test="mailZip"]').type(data.addressZip) cy.get('[class="usa-select radius-md"]').select(data.addressState) // Validate text box has correct text -cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) +cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) cy.get('[data-test="mailCity"]').should('have.value', data.addressTown) cy.get('[data-test="mailZip"]').should('have.value', data.addressZip) @@ -317,7 +317,7 @@ cy.get('[data-test="dropDown"]').then(dropDown => { cy.get(dropDown[0]).select("Social security number (last 4 digits)") }) cy.get('[data-test="ssn"]').type(data.ssn) -// Validate fields have correct data +// Validate fields have correct data cy.get('[data-test="dropDown"]').should('have.value', data.ssnValue) cy.get('[data-test="ssn"]').should('have.value', data.ssn) @@ -341,7 +341,7 @@ cy.get('[data-test="nextBtn"]').click() cy.get('[data-test="addressConfirm"]').should('contain.text', 'Your Alabama mail-in registration form is complete and ready to print.') // * check that download opens in new window -cy.get('[data-test="pdfBtn"]').click() +cy.get('[data-test="pdfBtnNewTab"]').click() cy.get('@open').should('have.been.calledOnce') }) diff --git a/testing/cypress/e2e/tests/online.cy.js b/testing/cypress/e2e/tests/online.cy.js index 6b3373c2..06ee24aa 100644 --- a/testing/cypress/e2e/tests/online.cy.js +++ b/testing/cypress/e2e/tests/online.cy.js @@ -29,7 +29,7 @@ cy.get('[data-test="pathBtn"]').then(btn => { cy.get(btn[0]).click({force: true}) }) -// fill out personal information +// fill out personal information // * check that user can not move forward without filling out fields cy.get('[data-test="nextBtn"]').click().click() cy.get('[data-test="errorText"]').should('exist') @@ -49,7 +49,7 @@ cy.get('[data-test="lastName"]').type(data.personalInformationLast) cy.get('[data-test="phoneNumber"]').type(data.personalInformationNumber) // Validate text box has correct text cy.get('[data-test="firstName"]').should('have.value', data.personalInformationName) -cy.get('[data-test="middleName"]').should('have.value', data.personalInformationMiddle) +cy.get('[data-test="middleName"]').should('have.value', data.personalInformationMiddle) cy.get('[data-test="lastName"]').should('have.value', data.personalInformationLast) cy.get('[data-test="phoneNumber"]').should('contain.value', data.personalInformationNumber2) @@ -60,7 +60,7 @@ cy.get('[data-test="prevMiddleName"]').type(data.personalInformationMiddle) cy.get('[data-test="prevLastName"]').type(data.personalInformationLast) // Validate text box has correct text cy.get('[data-test="prevFirstName"]').should('have.value', data.personalInformationName) -cy.get('[data-test="prevMiddleName"]').should('have.value', data.personalInformationMiddle) +cy.get('[data-test="prevMiddleName"]').should('have.value', data.personalInformationMiddle) cy.get('[data-test="prevLastName"]').should('have.value', data.personalInformationLast) cy.get('[data-test="dobMonth"]').type(data.personalInformationMonth) @@ -85,12 +85,12 @@ cy.get('[data-test="aptNumber"]').type(data.addressApt) cy.get('[data-test="city"]').type(data.addressTown) cy.get('[data-test="zip"]').type(data.addressZip) // Validate text box has correct text -cy.get('[data-test="street"]').should('have.value', data.addressStreet) +cy.get('[data-test="street"]').should('have.value', data.addressStreet) cy.get('[data-test="aptNumber"]').should('have.value', data.addressApt) cy.get('[data-test="city"]').should('have.value', data.addressTown) cy.get('[data-test="zip"]').should('have.value', data.addressZip) -// * check that mailing address work +// * check that mailing address work cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[1]).click({force: true}) }) @@ -99,7 +99,7 @@ cy.get('[data-test="mailStreet"]').type(data.addressStreet) cy.get('[data-test="mailCity"]').type(data.addressTown) cy.get('[data-test="mailZip"]').type(data.addressZip) // Validate text box has correct text -cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) +cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) cy.get('[data-test="mailCity"]').should('have.value', data.addressTown) cy.get('[data-test="mailZip"]').should('have.value', data.addressZip) @@ -108,7 +108,7 @@ cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[1]).click({force: true}) }) -// * check recently moved option +// * check recently moved option cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[0]).click({force: true}) }) @@ -117,7 +117,7 @@ cy.get('[data-test="prevStreet"]').type(data.addressStreet) cy.get('[data-test="prevCity"]').type(data.addressTown) cy.get('[data-test="prevZip"]').type(data.addressZip) // Validate text box has correct text -cy.get('[data-test="prevStreet"]').should('have.value', data.addressStreet) +cy.get('[data-test="prevStreet"]').should('have.value', data.addressStreet) cy.get('[data-test="prevCity"]').should('have.value', data.addressTown) cy.get('[data-test="prevZip"]').should('have.value', data.addressZip) // * uncheck recently moved block @@ -125,14 +125,14 @@ cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[0]).click({force: true}) }) -// * check does not have permanent option +// * check does not have permanent option cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[1]).click({force: true}) }) cy.get('[data-testid="Select"]').select(data.addressState) // Validate text box has correct text for mailing address -cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) +cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) cy.get('[data-test="mailCity"]').should('have.value', data.addressTown) cy.get('[data-test="mailZip"]').should('have.value', data.addressZip) @@ -162,7 +162,7 @@ cy.get('[data-test="dropDown"]').then(dropDown => { cy.get(dropDown[0]).select("Social security number (last 4 digits)") }) cy.get('[data-test="ssn"]').type(data.ssn) -// Validate fields have correct data +// Validate fields have correct data cy.get('[data-test="dropDown"]').should('have.value', data.ssnValue) cy.get('[data-test="ssn"]').should('have.value', data.ssn) @@ -174,7 +174,7 @@ cy.get('p').should('contain.text', '"None" will appear on your completed form.') cy.get('[data-test="nextBtn"]').click() - // political party + // political party cy.get('[data-test="politicalParty"]').type(data.politicalParty) cy.get('[data-test="nextBtn"]').click() @@ -185,7 +185,7 @@ cy.get('[data-test="nextBtn"]').click() cy.get('[data-test="addressConfirm"]').should('contain.text', 'Your Alabama mail-in registration form is complete and ready to print.') // * check that download opens in new window -cy.get('[data-test="pdfBtn"]').click() +cy.get('[data-test="pdfBtnNewTab"]').click() cy.get('@open').should('have.been.calledOnce') @@ -216,7 +216,7 @@ cy.get('[data-test="pathBtn"]').then(btn => { cy.get(btn[1]).click({force: true}) }) -// fill out personal information +// fill out personal information // * check that user can not move forward without filling out fields cy.get('[data-test="nextBtn"]').click().click() cy.get('[data-test="errorText"]').should('exist') @@ -236,7 +236,7 @@ cy.get('[data-test="lastName"]').type(data.personalInformationLast) cy.get('[data-test="phoneNumber"]').type(data.personalInformationNumber) // Validate text box has correct text cy.get('[data-test="firstName"]').should('have.value', data.personalInformationName) -cy.get('[data-test="middleName"]').should('have.value', data.personalInformationMiddle) +cy.get('[data-test="middleName"]').should('have.value', data.personalInformationMiddle) cy.get('[data-test="lastName"]').should('have.value', data.personalInformationLast) cy.get('[data-test="phoneNumber"]').should('contain.value', data.personalInformationNumber2) @@ -262,12 +262,12 @@ cy.get('[data-test="aptNumber"]').type(data.addressApt) cy.get('[data-test="city"]').type(data.addressTown) cy.get('[data-test="zip"]').type(data.addressZip) // Validate text box has correct text -cy.get('[data-test="street"]').should('have.value', data.addressStreet) +cy.get('[data-test="street"]').should('have.value', data.addressStreet) cy.get('[data-test="aptNumber"]').should('have.value', data.addressApt) cy.get('[data-test="city"]').should('have.value', data.addressTown) cy.get('[data-test="zip"]').should('have.value', data.addressZip) -// * check that mailing address work +// * check that mailing address work cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[1]).click({force: true}) }) @@ -275,7 +275,7 @@ cy.get('[data-test="mailStreet"]').type(data.addressStreet) cy.get('[data-test="mailCity"]').type(data.addressTown) cy.get('[data-test="mailZip"]').type(data.addressZip) // Validate text box has correct text -cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) +cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) cy.get('[data-test="mailCity"]').should('have.value', data.addressTown) cy.get('[data-test="mailZip"]').should('have.value', data.addressZip) // * uncheck mailing address block @@ -283,7 +283,7 @@ cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[1]).click({force: true}) }) -// * check recently moved option +// * check recently moved option cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[0]).click({force: true}) }) @@ -292,7 +292,7 @@ cy.get('[data-test="mailStreet"]').type(data.addressStreet) cy.get('[data-test="mailCity"]').type(data.addressTown) cy.get('[data-test="mailZip"]').type(data.addressZip) // Validate text box has correct text -cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) +cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) cy.get('[data-test="mailCity"]').should('have.value', data.addressTown) cy.get('[data-test="mailZip"]').should('have.value', data.addressZip) @@ -301,7 +301,7 @@ cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[0]).click({force: true}) }) -// * check does not have permanent option +// * check does not have permanent option cy.get('[data-test="checkBox"]').then(checkBox => { cy.get(checkBox[0]).click({force: true}) }) @@ -309,7 +309,7 @@ cy.get('[data-test="checkBox"]').then(checkBox => { cy.get('[class="usa-select radius-md"]').select(data.addressState) // Validate text box has correct text -cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) +cy.get('[data-test="mailStreet"]').should('have.value', data.addressStreet) cy.get('[data-test="mailCity"]').should('have.value', data.addressTown) cy.get('[data-test="mailZip"]').should('have.value', data.addressZip) @@ -339,7 +339,7 @@ cy.get('[data-test="dropDown"]').then(dropDown => { cy.get(dropDown[0]).select("Social security number (last 4 digits)") }) cy.get('[data-test="ssn"]').type(data.ssn) -// Validate fields have correct data +// Validate fields have correct data cy.get('[data-test="dropDown"]').should('have.value', data.ssnValue) cy.get('[data-test="ssn"]').should('have.value', data.ssn) @@ -363,7 +363,7 @@ cy.get('[data-test="nextBtn"]').click() cy.get('[data-test="addressConfirm"]').should('contain.text', 'Your Alabama mail-in registration form is complete and ready to print.') // * check that download opens in new window -cy.get('[data-test="pdfBtn"]').click() +cy.get('[data-test="pdfBtnNewTab"]').click() cy.get('@open').should('have.been.calledOnce')