From 03d277cdd69bd339d9993e4b9b9d44816165887c Mon Sep 17 00:00:00 2001 From: mirovladimitrovski Date: Mon, 16 Oct 2023 16:51:48 +0200 Subject: [PATCH] feat: reintroduce custom registration fields --- public/locales/en/common.json | 1 - public/locales/en/country.json | 250 ++ public/locales/en/us_state.json | 53 + public/locales/en/user.json | 1 + public/locales/es/common.json | 1 - public/locales/es/country.json | 250 ++ public/locales/es/us_state.json | 53 + public/locales/es/user.json | 1 + src/components/Account/Account.module.scss | 6 + src/components/Account/Account.tsx | 60 +- .../__snapshots__/Account.test.tsx.snap | 62 +- src/components/Checkbox/Checkbox.tsx | 4 +- .../CustomRegisterField.test.tsx | 55 + .../CustomRegisterField.tsx | 69 + .../CustomRegisterField.test.tsx.snap | 2029 +++++++++++++++++ .../DateField/DateField.module.scss | 4 + src/components/DateField/DateField.tsx | 32 +- src/components/Dropdown/Dropdown.tsx | 6 +- src/components/Form/Form.tsx | 2 +- src/components/Form/FormSection.tsx | 21 +- .../Form/__snapshots__/Form.test.tsx.snap | 5 +- .../PersonalDetailsForm.tsx | 19 +- .../PersonalDetailsForm.test.tsx.snap | 22 +- src/components/Radio/Radio.test.tsx | 2 +- src/components/Radio/Radio.tsx | 10 +- .../Radio/__snapshots__/Radio.test.tsx.snap | 6 +- .../RegistrationForm.module.scss | 6 + .../RegistrationForm/RegistrationForm.tsx | 50 +- .../RegistrationForm.test.tsx.snap | 1 + src/components/TextField/TextField.tsx | 4 +- .../AccountModal/forms/Registration.tsx | 18 +- src/i18n/resources.ts | 2 +- .../User/__snapshots__/User.test.tsx.snap | 16 +- src/services/cleeng.account.service.ts | 4 +- src/services/inplayer.account.service.ts | 65 +- src/utils/collection.ts | 30 +- test-e2e/tests/account_test.ts | 2 +- test/vitest.setup.ts | 4 + types/account.d.ts | 20 +- 39 files changed, 3102 insertions(+), 144 deletions(-) create mode 100644 public/locales/en/country.json create mode 100644 public/locales/en/us_state.json create mode 100644 public/locales/es/country.json create mode 100644 public/locales/es/us_state.json create mode 100644 src/components/CustomRegisterField/CustomRegisterField.test.tsx create mode 100644 src/components/CustomRegisterField/CustomRegisterField.tsx create mode 100644 src/components/CustomRegisterField/__snapshots__/CustomRegisterField.test.tsx.snap diff --git a/public/locales/en/common.json b/public/locales/en/common.json index e917c625f..9d912c722 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -19,7 +19,6 @@ "live": "LIVE", "now": "Now", "optional": "(Optional)", - "play_item": "Play {{title}}", "scheduled": "Scheduled", "series": "series", "sign_in": "Sign in", diff --git a/public/locales/en/country.json b/public/locales/en/country.json new file mode 100644 index 000000000..e10ac2742 --- /dev/null +++ b/public/locales/en/country.json @@ -0,0 +1,250 @@ +{ + "af": "Afghanistan", + "ax": "Åland Islands", + "al": "Albania", + "dz": "Algeria", + "as": "American Samoa", + "ad": "Andorra", + "ao": "Angola", + "ai": "Anguilla", + "aq": "Antarctica", + "ag": "Antigua and Barbuda", + "ar": "Argentina", + "am": "Armenia", + "aw": "Aruba", + "au": "Australia", + "at": "Austria", + "az": "Azerbaijan", + "bs": "Bahamas", + "bh": "Bahrain", + "bd": "Bangladesh", + "bb": "Barbados", + "by": "Belarus", + "be": "Belgium", + "bz": "Belize", + "bj": "Benin", + "bm": "Bermuda", + "bt": "Bhutan", + "bo": "Bolivia", + "bq": "Bonaire, Sint Eustatius and Saba", + "ba": "Bosnia and Herzegovina", + "bw": "Botswana", + "bv": "Bouvet Island", + "br": "Brazil", + "io": "British Indian Ocean Territory", + "bn": "Brunei Darussalam", + "bg": "Bulgaria", + "bf": "Burkina Faso", + "bi": "Burundi", + "kh": "Cambodia", + "cm": "Cameroon", + "ca": "Canada", + "cv": "Cabo Verde", + "ky": "Cayman Islands", + "cf": "Central African Republic", + "td": "Chad", + "cl": "Chile", + "cn": "China", + "cx": "Christmas Island", + "cc": "Cocos (Keeling) Islands", + "co": "Colombia", + "km": "Comoros", + "cg": "Congo", + "cd": "Congo, The Democratic Republic of the", + "ck": "Cook Islands", + "cr": "Costa Rica", + "ci": "Cote D'Ivoire", + "hr": "Croatia", + "cu": "Cuba", + "cw": "Curaçao", + "cy": "Cyprus", + "cz": "Czech Republic", + "dk": "Denmark", + "dj": "Djibouti", + "dm": "Dominica", + "do": "Dominican Republic", + "ec": "Ecuador", + "eg": "Egypt", + "sv": "El Salvador", + "gq": "Equatorial Guinea", + "er": "Eritrea", + "ee": "Estonia", + "et": "Ethiopia", + "fk": "Falkland Islands (Malvinas)", + "fo": "Faroe Islands", + "fj": "Fiji", + "fi": "Finland", + "fr": "France", + "gf": "French Guiana", + "pf": "French Polynesia", + "tf": "French Southern Territories", + "ga": "Gabon", + "gm": "Gambia", + "ge": "Georgia", + "de": "Germany", + "gh": "Ghana", + "gi": "Gibraltar", + "gr": "Greece", + "gl": "Greenland", + "gd": "Grenada", + "gp": "Guadeloupe", + "gu": "Guam", + "gt": "Guatemala", + "gg": "Guernsey", + "gn": "Guinea", + "gw": "Guinea-Bissau", + "gy": "Guyana", + "ht": "Haiti", + "hm": "Heard Island and McDonald Islands", + "va": "Holy See (Vatican City State)", + "hn": "Honduras", + "hk": "Hong Kong", + "hu": "Hungary", + "is": "Iceland", + "in": "India", + "id": "Indonesia", + "ir": "Iran, Islamic Republic Of", + "iq": "Iraq", + "ie": "Ireland", + "im": "Isle of Man", + "il": "Israel", + "it": "Italy", + "jm": "Jamaica", + "jp": "Japan", + "je": "Jersey", + "jo": "Jordan", + "kz": "Kazakhstan", + "ke": "Kenya", + "ki": "Kiribati", + "kp": "Korea, Democratic People's Republic of", + "kr": "Korea, Republic of", + "kw": "Kuwait", + "kg": "Kyrgyzstan", + "la": "Lao People's Democratic Republic", + "lv": "Latvia", + "lb": "Lebanon", + "ls": "Lesotho", + "lr": "Liberia", + "ly": "Libya", + "li": "Liechtenstein", + "lt": "Lithuania", + "lu": "Luxembourg", + "mo": "Macao", + "mk": "Macedonia, The Former Yugoslav Republic of", + "mg": "Madagascar", + "mw": "Malawi", + "my": "Malaysia", + "mv": "Maldives", + "ml": "Mali", + "mt": "Malta", + "mh": "Marshall Islands", + "mq": "Martinique", + "mr": "Mauritania", + "mu": "Mauritius", + "yt": "Mayotte", + "mx": "Mexico", + "fm": "Micronesia, Federated States of", + "md": "Moldova, Republic of", + "mc": "Monaco", + "mn": "Mongolia", + "me": "Montenegro", + "ms": "Montserrat", + "ma": "Morocco", + "mz": "Mozambique", + "mm": "Myanmar", + "na": "Namibia", + "nr": "Nauru", + "np": "Nepal", + "nl": "Netherlands", + "nc": "New Caledonia", + "nz": "New Zealand", + "ni": "Nicaragua", + "ne": "Niger", + "ng": "Nigeria", + "nu": "Niue", + "nf": "Norfolk Island", + "mp": "Northern Mariana Islands", + "no": "Norway", + "om": "Oman", + "pk": "Pakistan", + "pw": "Palau", + "ps": "Palestine, State of", + "pa": "Panama", + "pg": "Papua New Guinea", + "py": "Paraguay", + "pe": "Peru", + "ph": "Philippines", + "pn": "Pitcairn", + "pl": "Poland", + "pt": "Portugal", + "pr": "Puerto Rico", + "qa": "Qatar", + "re": "Réunion", + "ro": "Romania", + "ru": "Russian Federation", + "rw": "Rwanda", + "bl": "Saint Barthélemy", + "sh": "Saint Helena, Ascension and Tristan da Cunha", + "kn": "Saint Kitts and Nevis", + "lc": "Saint Lucia", + "mf": "Saint Martin (French part)", + "pm": "Saint Pierre and Miquelon", + "vc": "Saint Vincent and the Grenadines", + "ws": "Samoa", + "sm": "San Marino", + "st": "Sao Tome and Principe", + "sa": "Saudi Arabia", + "sn": "Senegal", + "rs": "Serbia", + "sc": "Seychelles", + "sl": "Sierra Leone", + "sg": "Singapore", + "sx": "Sint Maarten (Dutch part)", + "sk": "Slovakia", + "si": "Slovenia", + "sb": "Solomon Islands", + "so": "Somalia", + "za": "South Africa", + "gs": "South Georgia and the South Sandwich Islands", + "es": "Spain", + "lk": "Sri Lanka", + "sd": "Sudan", + "sr": "Suriname", + "sj": "Svalbard and Jan Mayen", + "sz": "Swaziland", + "se": "Sweden", + "ch": "Switzerland", + "sy": "Syrian Arab Republic", + "tw": "Taiwan", + "tj": "Tajikistan", + "tz": "Tanzania, United Republic of", + "th": "Thailand", + "tl": "Timor-Leste", + "tg": "Togo", + "tk": "Tokelau", + "to": "Tonga", + "tt": "Trinidad and Tobago", + "tn": "Tunisia", + "tr": "Turkey", + "tm": "Turkmenistan", + "tc": "Turks and Caicos Islands", + "tv": "Tuvalu", + "ug": "Uganda", + "ua": "Ukraine", + "ae": "United Arab Emirates", + "gb": "United Kingdom", + "us": "United States of America", + "um": "United States Minor Outlying Islands", + "uy": "Uruguay", + "uz": "Uzbekistan", + "vu": "Vanuatu", + "ve": "Venezuela", + "vn": "Viet Nam", + "vg": "Virgin Islands, British", + "vi": "Virgin Islands, U.S.", + "wf": "Wallis and Futuna", + "eh": "Western Sahara", + "ye": "Yemen", + "zm": "Zambia", + "zw": "Zimbabwe" +} \ No newline at end of file diff --git a/public/locales/en/us_state.json b/public/locales/en/us_state.json new file mode 100644 index 000000000..3fffc9328 --- /dev/null +++ b/public/locales/en/us_state.json @@ -0,0 +1,53 @@ +{ + "al": "Alabama", + "ak": "Alaska", + "az": "Arizona", + "ar": "Arkansas", + "ca": "California", + "co": "Colorado", + "ct": "Connecticut", + "de": "Delaware", + "dc": "District of Columbia", + "fl": "Florida", + "ga": "Georgia", + "hi": "Hawaii", + "id": "Idaho", + "il": "Illinois", + "in": "Indiana", + "ia": "Iowa", + "ks": "Kansas", + "ky": "Kentucky", + "la": "Louisiana", + "me": "Maine", + "md": "Maryland", + "ma": "Massachusetts", + "mi": "Michigan", + "mn": "Minnesota", + "ms": "Mississippi", + "mo": "Missouri", + "mt": "Montana", + "ne": "Nebraska", + "nv": "Nevada", + "nh": "New Hampshire", + "nj": "New Jersey", + "nm": "New Mexico", + "ny": "New York", + "nc": "North Carolina", + "nd": "North Dakota", + "oh": "Ohio", + "ok": "Oklahoma", + "or": "Oregon", + "pa": "Pennsylvania", + "ri": "Rhode Island", + "sc": "South Carolina", + "sd": "South Dakota", + "tn": "Tennessee", + "tx": "Texas", + "ut": "Utah", + "vt": "Vermont", + "va": "Virginia", + "wa": "Washington", + "wv": "West Virginia", + "wi": "Wisconsin", + "wy": "Wyoming" +} \ No newline at end of file diff --git a/public/locales/en/user.json b/public/locales/en/user.json index df7fa12c1..159cb62d4 100644 --- a/public/locales/en/user.json +++ b/public/locales/en/user.json @@ -42,6 +42,7 @@ "hide_password": "Hide password", "lastname": "Last name", "manage_profiles": "Manage profiles", + "other_registration_details": "Other registration details", "password": "Password", "save": "Save", "security": "Password", diff --git a/public/locales/es/common.json b/public/locales/es/common.json index 45c5af95c..8b2684f24 100644 --- a/public/locales/es/common.json +++ b/public/locales/es/common.json @@ -19,7 +19,6 @@ "live": "EN VIVO", "now": "Ahora", "optional": "(Opcional)", - "play_item": "Reproducir {{title}}", "scheduled": "Programada", "series": "serie", "sign_in": "Iniciar sesión", diff --git a/public/locales/es/country.json b/public/locales/es/country.json new file mode 100644 index 000000000..023006f8a --- /dev/null +++ b/public/locales/es/country.json @@ -0,0 +1,250 @@ +{ + "af": "Afganistán", + "ax": "Islas Aland", + "al": "Albania", + "dz": "Argelia", + "as": "Samoa Americana", + "ad": "Andorra", + "ao": "Angola", + "ai": "Anguila", + "aq": "Antártida", + "ag": "Antigua y Barbuda", + "ar": "Argentina", + "am": "Armenia", + "aw": "Aruba", + "au": "Australia", + "at": "Austria", + "az": "Azerbaiyán", + "bs": "Bahamas", + "bh": "Baréin", + "bd": "Bangladesh", + "bb": "Barbados", + "by": "Bielorrusia", + "be": "Bélgica", + "bz": "Belice", + "bj": "Benín", + "bm": "Islas Bermudas", + "bt": "Bután", + "bo": "Bolivia", + "bq": "Caribe Neerlandés", + "ba": "Bosnia y Herzegovina", + "bw": "Botsuana", + "bv": "Isla Bouvet", + "br": "Brasil", + "io": "Territorio Británico del Océano Índico", + "bn": "Brunéi", + "bg": "Bulgaria", + "bf": "Burkina Faso", + "bi": "Burundi", + "kh": "Camboya", + "cm": "Camerún", + "ca": "Canadá", + "cv": "Cabo Verde", + "ky": "Islas Caimán", + "cf": "República Centroafricana", + "td": "Chad", + "cl": "Chile", + "cn": "China", + "cx": "Isla de Navidad", + "cc": "Islas Cocos", + "co": "Colombia", + "km": "Comoras", + "cg": "Congo", + "cd": "Congo, República Democrática del", + "ck": "Islas Cook", + "cr": "Costa Rica", + "ci": "Costa de Marfil", + "hr": "Croacia", + "cu": "Cuba", + "cw": "Curazao", + "cy": "Chipre", + "cz": "Chequia", + "dk": "Dinamarca", + "dj": "Yibuti", + "dm": "Dominica", + "do": "República Dominicana", + "ec": "Ecuador", + "eg": "Egipto", + "sv": "El Salvador", + "gq": "Guinea Ecuatorial", + "er": "Eritrea", + "ee": "Estonia", + "et": "Etiopía", + "fk": "Islas Malvinas", + "fo": "Islas Feroe", + "fj": "Fiyi", + "fi": "Finlandia", + "fr": "Francia", + "gf": "Guayana Francesa", + "pf": "Polinesia Francesa", + "tf": "Territorios Franceses del Sur", + "ga": "Gabón", + "gm": "Gambia", + "ge": "Georgia", + "de": "Alemania", + "gh": "Ghana", + "gi": "Gibraltar", + "gr": "Grecia", + "gl": "Groenlandia", + "gd": "Grenada", + "gp": "Guadalupe", + "gu": "Guam", + "gt": "Guatemala", + "gg": "Guernsey", + "gn": "Guinea", + "gw": "Guinea-Bisáu", + "gy": "Guayana", + "ht": "Haití", + "hm": "Territorio de las Islas Heard y McDonald", + "va": "Santa Sede (Ciudad del Vaticano)", + "hn": "Honduras", + "hk": "Hong Kong", + "hu": "Hungría", + "is": "Islandia", + "in": "India", + "id": "Indonesia", + "ir": "Irán", + "iq": "Irak", + "ie": "Irlanda", + "im": "Isla de Man", + "il": "Israel", + "it": "Italia", + "jm": "Jamaica", + "jp": "Japona", + "je": "Jersey", + "jo": "Jordán", + "kz": "Kazajstán", + "ke": "Kenia", + "ki": "Kiribati", + "kp": "Corea, República Popular Democrática de", + "kr": "Corea, República", + "kw": "Kuwait", + "kg": "Kirguistán", + "la": "Laos", + "lv": "Letonia", + "lb": "Líbano", + "ls": "Lesoto", + "lr": "Liberia", + "ly": "Libia", + "li": "Liechtenstein", + "lt": "Lituania", + "lu": "Luxemburgo", + "mo": "Macao", + "mk": "Macedonia", + "mg": "Madagascar", + "mw": "Malaui", + "my": "Malasia", + "mv": "Maldivas", + "ml": "Malí", + "mt": "Malta", + "mh": "Islas Marshall", + "mq": "Martinica", + "mr": "Mauritania", + "mu": "Mauricio", + "yt": "Mayotte", + "mx": "México", + "fm": "Micronesia", + "md": "Moldavia", + "mc": "Mónaco", + "mn": "Mongolia", + "me": "Montenegro", + "ms": "Montserrat", + "ma": "Marruecos", + "mz": "Mozambique", + "mm": "Birmania", + "na": "Namibia", + "nr": "Nauru", + "np": "Nepal", + "nl": "Países Bajos", + "nc": "Nueva Caledonia", + "nz": "Nueva Zelanda", + "ni": "Nicaragua", + "ne": "Níger", + "ng": "Nigeria", + "nu": "Niue", + "nf": "Isla Norfolk", + "mp": "Islas Marianas del Norte", + "no": "Noruega", + "om": "Omán", + "pk": "Pakistán", + "pw": "Palaos", + "ps": "Palestina, Estado de", + "pa": "Panamá", + "pg": "Papúa Nueva Guinea", + "py": "Paraguay", + "pe": "Perú", + "ph": "Filipinas", + "pn": "Islas Pitcairn", + "pl": "Polonia", + "pt": "Portugal", + "pr": "Puerto Rico", + "qa": "Katar", + "re": "Reunión", + "ro": "Rumania", + "ru": "Rusia", + "rw": "Ruanda", + "bl": "San Bartolomé", + "sh": "Santa Elena, Ascensión y Tristán de Acuña", + "kn": "San Cristóbal y Nieves", + "lc": "Santa Lucía", + "mf": "Isla de San Martín", + "pm": "San Pedro y Miquelón", + "vc": "San Vicente y las Granadinas", + "ws": "Samoa", + "sm": "San Marino", + "st": "Santo Tomé y Príncipe", + "sa": "Arabia Saudita", + "sn": "Senegal", + "rs": "Serbia", + "sc": "Seychelles", + "sl": "Sierra Leone", + "sg": "Singapur", + "sx": "San Martín", + "sk": "Eslovaquia", + "si": "Eslovenia", + "sb": "Islas Salomón", + "so": "Somalia", + "za": "Sudáfrica", + "gs": "Islas Georgias del Sur y Sandwich del Sur", + "es": "España", + "lk": "Sri Lanka", + "sd": "Sudán", + "sr": "Surinam", + "sj": "Svalbard y Jan Mayen", + "sz": "Suazilandia", + "se": "Suecia", + "ch": "Suiza", + "sy": "Siria", + "tw": "Taiwán", + "tj": "Tayikistán", + "tz": "Tanzania", + "th": "Tailandia", + "tl": "Timor Oriental", + "tg": "Togo", + "tk": "Tokelau", + "to": "Tonga", + "tt": "Trinidad y Tobago", + "tn": "Túnez", + "tr": "Turquía", + "tm": "Turkmenistán", + "tc": "Islas Turcas y Caicos", + "tv": "Tuvalu", + "ug": "Uganda", + "ua": "Ucrania", + "ae": "Emiratos Árabes Unidos", + "gb": "Reino Unido", + "us": "Estados Unidos", + "um": "Islas Ultramarinas Menores de los Estados Unidos", + "uy": "Uruguay", + "uz": "Uzbekistán", + "vu": "Vanuatu", + "ve": "Venezuela", + "vn": "Vietnam", + "vg": "Islas Vírgenes Británicas", + "vi": "Islas Vírgenes, Estados Unidos", + "wf": "Wallis y Futuna", + "eh": "Sahara Occidental", + "ye": "Yemen", + "zm": "Zambia", + "zw": "Zimbabue" +} \ No newline at end of file diff --git a/public/locales/es/us_state.json b/public/locales/es/us_state.json new file mode 100644 index 000000000..82ef691e6 --- /dev/null +++ b/public/locales/es/us_state.json @@ -0,0 +1,53 @@ +{ + "al": "Alabama", + "ak": "Alaska", + "az": "Arizona", + "ar": "Arkansas", + "ca": "California", + "co": "Colorado", + "ct": "Connecticut", + "de": "Delaware", + "dc": "Distrito de Columbia", + "fl": "Florida", + "ga": "Georgia", + "hi": "Hawai", + "id": "Idaho", + "il": "Illinois", + "in": "Indiana", + "ia": "Iowa", + "ks": "Kansas", + "ky": "Kentucky", + "la": "Luisiana", + "me": "Maine", + "md": "Maryland", + "ma": "Massachusetts", + "mi": "Michigan", + "mn": "Minnesota", + "ms": "Misisipi", + "mo": "Misuri", + "mt": "Montana", + "ne": "Nebraska", + "nv": "Nevada", + "nh": "Nueva Hampshire", + "nj": "Nueva Jersey", + "nm": "Nuevo México", + "ny": "Nueva York", + "nc": "Carolina del Norte", + "nd": "Dakota del Norte", + "oh": "Ohio", + "ok": "Oklahoma", + "or": "Oregón", + "pa": "Pensilvania", + "ri": "Rhode Island", + "sc": "Carolina del Sur", + "sd": "Dakota del Sur", + "tn": "Tennesse", + "tx": "Texas", + "ut": "Utah", + "vt": "Vermont", + "va": "Virginia", + "wa": "Washington", + "wv": "Virginia Occidental", + "wi": "Wisconsin", + "wy": "Wyoming" +} \ No newline at end of file diff --git a/public/locales/es/user.json b/public/locales/es/user.json index 85e7b0257..7f269b42f 100644 --- a/public/locales/es/user.json +++ b/public/locales/es/user.json @@ -42,6 +42,7 @@ "hide_password": "Ocultar contraseña", "lastname": "Apellido", "manage_profiles": "Administrar perfiles", + "other_registration_details": "Otros detalles de registro", "password": "Contraseña", "save": "Guardar", "security": "Contraseña", diff --git a/src/components/Account/Account.module.scss b/src/components/Account/Account.module.scss index c9062eb0a..68e25c900 100644 --- a/src/components/Account/Account.module.scss +++ b/src/components/Account/Account.module.scss @@ -3,3 +3,9 @@ flex-direction: column; gap: 1rem; } + +.customFields { + display: flex; + flex-direction: column; + gap: 0.5em; +} \ No newline at end of file diff --git a/src/components/Account/Account.tsx b/src/components/Account/Account.tsx index 0510ab0b6..da3bdaa81 100644 --- a/src/components/Account/Account.tsx +++ b/src/components/Account/Account.tsx @@ -8,6 +8,7 @@ import { useMutation } from 'react-query'; import styles from './Account.module.scss'; import type { FormSectionContentArgs, FormSectionProps } from '#components/Form/FormSection'; +import type { Consent } from '#types/account'; import Alert from '#components/Alert/Alert'; import Visibility from '#src/icons/Visibility'; import VisibilityOff from '#src/icons/VisibilityOff'; @@ -17,8 +18,9 @@ import IconButton from '#components/IconButton/IconButton'; import TextField from '#components/TextField/TextField'; import Checkbox from '#components/Checkbox/Checkbox'; import HelperText from '#components/HelperText/HelperText'; +import CustomRegisterField from '#components/CustomRegisterField/CustomRegisterField'; import useToggle from '#src/hooks/useToggle'; -import { formatConsentsFromValues, formatConsentValues } from '#src/utils/collection'; +import { formatConsentsFromValues, formatConsents, formatConsentValues } from '#src/utils/collection'; import { addQueryParam } from '#src/utils/location'; import { useAccountStore } from '#src/stores/AccountStore'; import { isTruthy, logDev } from '#src/utils/common'; @@ -65,15 +67,33 @@ const Account = ({ panelClassName, panelHeaderClassName, canUpdateEmail = true } shallow, ); - const consentValues = useMemo(() => formatConsentValues(publisherConsents, customerConsents), [publisherConsents, customerConsents]); + const [termsConsents, nonTermsConsents] = useMemo(() => { + const terms: Consent[] = []; + const nonTerms: Consent[] = []; + + publisherConsents?.forEach((consent) => { + if (consent?.type === 'checkbox') { + terms.push(consent); + } else { + nonTerms.push(consent); + } + }); + + return [terms, nonTerms]; + }, [publisherConsents]); + + const consents = useMemo(() => formatConsents(publisherConsents, customerConsents), [publisherConsents, customerConsents]); + + const consentsValues = useMemo(() => formatConsentValues(publisherConsents, customerConsents), [publisherConsents, customerConsents]); const initialValues = useMemo( () => ({ ...customer, - consents: consentValues, + consents, + consentsValues, confirmationPassword: '', }), - [customer, consentValues], + [customer, consents, consentsValues], ); const formatConsentLabel = (label: string): string | JSX.Element => { @@ -242,15 +262,14 @@ const Account = ({ panelClassName, panelHeaderClassName, canUpdateEmail = true } formSection({ label: t('account.terms_and_tracking'), saveButton: t('account.update_consents'), - onSubmit: (values) => updateConsents(formatConsentsFromValues(publisherConsents, values)), + onSubmit: (values) => updateConsents(formatConsentsFromValues(publisherConsents, { ...values.consentsValues, terms: true })), content: (section) => ( <> - {publisherConsents?.map((consent, index) => ( + {termsConsents?.map((consent, index) => ( ), }), + nonTermsConsents?.length && + formSection({ + label: t('account.other_registration_details'), + saveButton: t('account.update_consents'), + onSubmit: (values) => updateConsents(formatConsentsFromValues(publisherConsents, values.consentsValues)), + content: (section) => ( +
+ {nonTermsConsents.map((consent) => ( + + ))} +
+ ), + }), canExportAccountData && formSection({ label: t('account.export_data_title'), diff --git a/src/components/Account/__snapshots__/Account.test.tsx.snap b/src/components/Account/__snapshots__/Account.test.tsx.snap index 52dbbf55e..b98adc8ae 100644 --- a/src/components/Account/__snapshots__/Account.test.tsx.snap +++ b/src/components/Account/__snapshots__/Account.test.tsx.snap @@ -12,8 +12,9 @@ exports[` > renders and matches snapshot 1`] = ` account.about_you -
> renders and matches snapshot 1`] = ` Doe

-
+
@@ -65,8 +66,9 @@ exports[` > renders and matches snapshot 1`] = ` account.email
-
> renders and matches snapshot 1`] = ` email@domain.com

-
+
@@ -105,8 +107,9 @@ exports[` > renders and matches snapshot 1`] = ` account.security
-
> renders and matches snapshot 1`] = ` account.terms_and_tracking
+
+
+
+
+

+ account.other_registration_details +

+
+
- - + + +
-
+
diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index 40c4b0dc5..3a1292da7 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { type ReactNode } from 'react'; import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; @@ -8,7 +8,7 @@ import HelperText from '#components/HelperText/HelperText'; import useOpaqueId from '#src/hooks/useOpaqueId'; type Props = { - label?: string | JSX.Element; + label?: ReactNode; name: string; value?: string; checked?: boolean; diff --git a/src/components/CustomRegisterField/CustomRegisterField.test.tsx b/src/components/CustomRegisterField/CustomRegisterField.test.tsx new file mode 100644 index 000000000..65bc78fc5 --- /dev/null +++ b/src/components/CustomRegisterField/CustomRegisterField.test.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { render } from '@testing-library/react'; + +import { CustomRegisterField } from './CustomRegisterField'; + +describe('', () => { + test('renders and matches snapshot ', () => { + const { container } = render(); + + expect(container).toMatchSnapshot(); + }); + + test('renders and matches snapshot ', () => { + const { container } = render(); + + expect(container).toMatchSnapshot(); + }); + + test('renders and matches snapshot ', () => { + const { container } = render(); + + expect(container).toMatchSnapshot(); + }); + + test('renders and matches snapshot ', () => { + const { container } = render(); + + expect(container).toMatchSnapshot(); + }); + + test('renders and matches snapshot ', () => { + const { container } = render(); + + expect(container).toMatchSnapshot(); + }); + + test('renders and matches snapshot ', () => { + const { container } = render(); + + expect(container).toMatchSnapshot(); + }); + + test('renders and matches snapshot ', () => { + const { container } = render(); + + expect(container).toMatchSnapshot(); + }); + + test('renders and matches snapshot ', () => { + // @ts-ignore + const { container } = render(); + + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/components/CustomRegisterField/CustomRegisterField.tsx b/src/components/CustomRegisterField/CustomRegisterField.tsx new file mode 100644 index 000000000..93c08f0ad --- /dev/null +++ b/src/components/CustomRegisterField/CustomRegisterField.tsx @@ -0,0 +1,69 @@ +import { type FC, type ChangeEventHandler, type ReactNode, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import type { GetRegisterFieldOption } from '@inplayer-org/inplayer.js'; + +import type { CustomRegisterFieldVariant } from '#types/account'; +import Checkbox from '#components/Checkbox/Checkbox'; +import TextField from '#components/TextField/TextField'; +import Radio from '#components/Radio/Radio'; +import Dropdown from '#components/Dropdown/Dropdown'; +import DateField from '#components/DateField/DateField'; + +type Props = { + type?: CustomRegisterFieldVariant; + name: string; + value: string | boolean; + onChange: ChangeEventHandler; +} & Partial<{ + label: ReactNode; + placeholder: string; + error: boolean; + helperText: string; + disabled: boolean; + required: boolean; + options: GetRegisterFieldOption; +}>; + +export type CustomRegisterFieldCommonProps = Props; + +export const CustomRegisterField: FC = ({ type, value = '', ...props }) => { + const { t, i18n } = useTranslation(); + + const optionsList = useMemo(() => { + switch (type) { + case 'country': + case 'us_state': { + const codes = Object.keys(i18n.getResourceBundle(i18n.language, type)); + + return codes.map((code) => ({ + value: code, + label: t(`${type}:${code}`), + })); + } + default: { + if (props.options) { + return Object.entries(props.options).map(([value, label]) => ({ value, label })); + } + + return []; + } + } + }, [t, type, props.options, i18n]); + + switch (type) { + case 'input': + return ; + case 'radio': + return ; + case 'select': + case 'country': + case 'us_state': + return ; + case 'datepicker': + return ; + default: + return ; + } +}; + +export default CustomRegisterField; diff --git a/src/components/CustomRegisterField/__snapshots__/CustomRegisterField.test.tsx.snap b/src/components/CustomRegisterField/__snapshots__/CustomRegisterField.test.tsx.snap new file mode 100644 index 000000000..0be7efbbe --- /dev/null +++ b/src/components/CustomRegisterField/__snapshots__/CustomRegisterField.test.tsx.snap @@ -0,0 +1,2029 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[` > renders and matches snapshot 1`] = ` +
+
+
+ + +
+
+
+`; + +exports[` > renders and matches snapshot 1`] = ` +
+
+ +
+ +
+
+
+`; + +exports[` > renders and matches snapshot 1`] = ` +
+
+ +
+ + + / + + / + +
+
+
+`; + +exports[` > renders and matches snapshot 1`] = ` +
+
+
+ + +
+
+
+`; + +exports[` > renders and matches snapshot 1`] = ` +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+`; + +exports[` > renders and matches snapshot 1`] = ` +
+
+
+ label + + optional + +
+
+
+`; + +exports[` > renders and matches snapshot 1`] = ` +
+
+ +
+ +
+
+
+`; diff --git a/src/components/DateField/DateField.module.scss b/src/components/DateField/DateField.module.scss index db2d64c01..b38188006 100644 --- a/src/components/DateField/DateField.module.scss +++ b/src/components/DateField/DateField.module.scss @@ -99,3 +99,7 @@ padding-left: 4px; } } + +.hiddenInput { + display: none; +} diff --git a/src/components/DateField/DateField.tsx b/src/components/DateField/DateField.tsx index bf3a26b1a..7c2188df6 100644 --- a/src/components/DateField/DateField.tsx +++ b/src/components/DateField/DateField.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, type ReactNode, useRef } from 'react'; import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; @@ -9,12 +9,12 @@ import useOpaqueId from '#src/hooks/useOpaqueId'; type Props = { className?: string; - label?: string; + label?: ReactNode; placeholder?: string; name?: string; value: string; format?: string; - onChange?: (dateString: string) => void; + onChange?: React.ChangeEventHandler; onFocus?: React.ChangeEventHandler; helperText?: React.ReactNode; error?: boolean; @@ -39,6 +39,8 @@ const DateField: React.FC = ({ className, label, error, helperText, value const id = useOpaqueId('text-field', rest.name); + const hiddenInputRef = useRef(null); + const DateFieldClassName = classNames( styles.dateField, { @@ -95,6 +97,18 @@ const DateField: React.FC = ({ className, label, error, helperText, value }); }; + const triggerChangeEvent = (date: string, month: string, year: string) => { + const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set; + + const newValue = date && month && year ? format.replace('YYYY', year).replace('MM', month).replace('DD', date) : ''; + + nativeInputValueSetter?.call(hiddenInputRef.current, newValue); + + const inputEvent = new Event('input', { bubbles: true }); + + hiddenInputRef.current?.dispatchEvent(inputEvent); + }; + const handleChange = (event: React.ChangeEvent) => { const { name, value } = event.target; const nextSibling = event.currentTarget?.nextElementSibling as HTMLInputElement; @@ -105,9 +119,7 @@ const DateField: React.FC = ({ className, label, error, helperText, value setValues({ date, month, year }); - if (onChange) { - onChange(date && month && year ? format.replace('YYYY', year).replace('MM', month).replace('DD', date) : ''); - } + triggerChangeEvent(date, month, year); if ((nextSibling && name === 'month' && month.length === 2) || (name === 'date' && date.length === 2)) { setTimeout(() => nextSibling.focus(), 1); @@ -121,6 +133,8 @@ const DateField: React.FC = ({ className, label, error, helperText, value {!rest.required ? {t('optional')} : null}
+ {/* don't be tempted to make it type="hidden", onChange will practically be ignored that way */} + = ({ className, label, error, helperText, value onKeyDown={handleKeyDown} maxLength={2} type="number" - id={id} + id={`${id}-date`} /> {' / '} = ({ className, label, error, helperText, value onKeyDown={handleKeyDown} maxLength={2} type="number" - id={id} + id={`${id}-month`} /> {' / '} = ({ className, label, error, helperText, value onKeyDown={handleKeyDown} maxLength={4} type="number" - id={id} + id={`${id}-year`} />
{helperText} diff --git a/src/components/Dropdown/Dropdown.tsx b/src/components/Dropdown/Dropdown.tsx index 0ace87dbf..6c56a1025 100644 --- a/src/components/Dropdown/Dropdown.tsx +++ b/src/components/Dropdown/Dropdown.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { type ReactNode } from 'react'; import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; @@ -14,7 +14,7 @@ type Props = { defaultLabel?: string; options?: (string | { value: string; label: string })[]; optionsStyle?: string; - label?: string; + label?: ReactNode; fullWidth?: boolean; size?: 'small' | 'medium'; error?: boolean; @@ -44,7 +44,7 @@ const Dropdown: React.FC = ({ return (
- {label && ( + {(label || !required) && (
{isBusy && isEditing && } - {content && - (isEditing ? ( -
event.preventDefault()}> - {content({ values, isEditing, isBusy, onChange, errors: formErrors })} -
- ) : ( -
{content({ values, isEditing, isBusy, onChange })}
- ))} + {content && ( +
event.preventDefault()}> + {content({ + values, + isEditing, + isBusy, + onChange, + ...(isEditing ? { errors: formErrors } : {}), + })} +
+ )} {(saveButton || editButton || cancelButton) && (
{isEditing ? ( diff --git a/src/components/Form/__snapshots__/Form.test.tsx.snap b/src/components/Form/__snapshots__/Form.test.tsx.snap index 69279e41d..8c2122f37 100644 --- a/src/components/Form/__snapshots__/Form.test.tsx.snap +++ b/src/components/Form/__snapshots__/Form.test.tsx.snap @@ -8,14 +8,15 @@ exports[`
> renders Form 1`] = ` Edit This
-
-
+
diff --git a/src/components/PersonalDetailsForm/PersonalDetailsForm.tsx b/src/components/PersonalDetailsForm/PersonalDetailsForm.tsx index 03cb8cd3b..9cb67bb33 100644 --- a/src/components/PersonalDetailsForm/PersonalDetailsForm.tsx +++ b/src/components/PersonalDetailsForm/PersonalDetailsForm.tsx @@ -45,7 +45,12 @@ const PersonalDetailsForm: React.FC = ({ }: Props) => { const { t } = useTranslation('account'); const renderQuestion = ({ value, key, question, required }: CleengCaptureQuestionField) => { - const values = value?.split(';') || []; + const values = value?.split(';').map((question) => { + const [value, label = value] = question.split(':'); + + return { value, label }; + }); + const props = { name: key, onChange: onQuestionChange, @@ -55,11 +60,13 @@ const PersonalDetailsForm: React.FC = ({ key, }; - if (values.length === 1) { - return ; - } else if (values.length === 2) { + const optionsKeys = Object.keys(values); + + if (optionsKeys.length === 1) { + return ; + } else if (optionsKeys.length === 2) { return ; - } else if (values.length > 2) { + } else if (optionsKeys.length > 2) { return ; } @@ -174,7 +181,7 @@ const PersonalDetailsForm: React.FC = ({ {fields.birthDate?.enabled ? ( setValue('birthDate', value)} + onChange={(event) => setValue('birthDate', event.currentTarget.value)} label={t('personal_details.birth_date')} placeholder={t('personal_details.birth_date')} error={!!errors.birthDate || !!errors.form} diff --git a/src/components/PersonalDetailsForm/__snapshots__/PersonalDetailsForm.test.tsx.snap b/src/components/PersonalDetailsForm/__snapshots__/PersonalDetailsForm.test.tsx.snap index b557f3bdd..2cc575e42 100644 --- a/src/components/PersonalDetailsForm/__snapshots__/PersonalDetailsForm.test.tsx.snap +++ b/src/components/PersonalDetailsForm/__snapshots__/PersonalDetailsForm.test.tsx.snap @@ -285,8 +285,13 @@ exports[` > Renders with errors 1`] = ` class="_container_eb6974" > + > Renders with errors 1`] = ` / > Renders with errors 1`] = ` / > Renders without crashing 1`] = ` class="_container_eb6974" > + > Renders without crashing 1`] = ` / > Renders without crashing 1`] = ` / ', () => { header={'Choose a Value'} onChange={vi.fn()} value="value1" - values={Array.of('value1', 'value2', 'value3')} + values={Array.of({ value: 'value1', label: 'Label 1 ' }, { value: 'value2', label: 'Label 2 ' }, { value: 'value3', label: 'Label 3 ' })} helperText={'This is required!'} error={false} />, diff --git a/src/components/Radio/Radio.tsx b/src/components/Radio/Radio.tsx index d4b14039f..81c6862ad 100644 --- a/src/components/Radio/Radio.tsx +++ b/src/components/Radio/Radio.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { type ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; import styles from './Radio.module.scss'; @@ -9,9 +9,9 @@ import useOpaqueId from '#src/hooks/useOpaqueId'; type Props = { name: string; value?: string; - values: string[]; + values: { value: string; label: string }[]; onChange: React.ChangeEventHandler; - header?: string; + header?: ReactNode; helperText?: string; error?: boolean; required?: boolean; @@ -29,10 +29,10 @@ const Radio: React.FC = ({ name, onChange, header, value, values, helperT {!required ? {t('optional')} : null}
) : null} - {values.map((optionValue, index) => ( + {values.map(({ value: optionValue, label: optionLabel }, index) => (
- +
))} {helperText} diff --git a/src/components/Radio/__snapshots__/Radio.test.tsx.snap b/src/components/Radio/__snapshots__/Radio.test.tsx.snap index 434e3c8ca..69849e8a0 100644 --- a/src/components/Radio/__snapshots__/Radio.test.tsx.snap +++ b/src/components/Radio/__snapshots__/Radio.test.tsx.snap @@ -24,7 +24,7 @@ exports[` > renders and matches snapshot 1`] = `
> renders and matches snapshot 1`] = `
> renders and matches snapshot 1`] = `
; onChange: React.ChangeEventHandler; onBlur: React.FocusEventHandler; - onConsentChange: React.ChangeEventHandler; + onConsentChange: ChangeEventHandler; errors: FormErrors; values: RegistrationFormData; loading: boolean; - consentValues: Record; + consentValues: Record; consentErrors: string[]; submitting: boolean; canSubmit: boolean; @@ -55,6 +55,8 @@ const RegistrationForm: React.FC = ({ const { t } = useTranslation('account'); const location = useLocation(); + const ref = useRef(null); + const formatConsentLabel = (label: string): string | JSX.Element => { const sanitizedLabel = DOMPurify.sanitize(label); const hasHrefOpenTag = //.test(sanitizedLabel); @@ -67,6 +69,12 @@ const RegistrationForm: React.FC = ({ return label; }; + useEffect(() => { + if (errors.form) { + ref.current?.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'nearest' }); + } + }, [errors.form]); + if (loading) { return (
@@ -78,7 +86,7 @@ const RegistrationForm: React.FC = ({ return (

{t('registration.sign_up')}

- {errors.form ? {errors.form} : null} +
{errors.form ? {errors.form} : null}
= ({ } required /> - {publisherConsents?.map((consent, index) => ( - - ))} + {publisherConsents && ( +
+ {publisherConsents.map((consent) => ( + + ))} +
+ )}