From 29befd934adedf78fd1440c67879e4d144d13e73 Mon Sep 17 00:00:00 2001 From: Carine Dengler Date: Mon, 26 Feb 2024 16:21:48 +0100 Subject: [PATCH 1/5] feat: update smart contract --- contracts/evoting/types/ballots.go | 18 ++++++++++------ contracts/evoting/types/ballots_test.go | 28 ++++++++++++------------- integration/performance_test.go | 2 +- internal/testing/fake/election.go | 4 ++-- 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/contracts/evoting/types/ballots.go b/contracts/evoting/types/ballots.go index 907678a2f..11cc69b28 100644 --- a/contracts/evoting/types/ballots.go +++ b/contracts/evoting/types/ballots.go @@ -89,7 +89,7 @@ func (b *Ballot) Unmarshal(marshalledBallot string, form Form) error { ID: ID(questionID), MaxN: q.GetMaxN(), MinN: q.GetMinN(), - Choices: make([]string, q.GetChoicesLength()), + Choices: make([]Choice, q.GetChoicesLength()), } results, err := selectQ.unmarshalAnswers(selections) @@ -108,7 +108,7 @@ func (b *Ballot) Unmarshal(marshalledBallot string, form Form) error { ID: ID(questionID), MaxN: q.GetMaxN(), MinN: q.GetMinN(), - Choices: make([]string, q.GetChoicesLength()), + Choices: make([]Choice, q.GetChoicesLength()), } results, err := rankQ.unmarshalAnswers(ranks) @@ -127,7 +127,7 @@ func (b *Ballot) Unmarshal(marshalledBallot string, form Form) error { MaxN: q.GetMaxN(), MinN: q.GetMinN(), MaxLength: 0, // TODO: Should the length check be also done at decryption? - Choices: make([]string, q.GetChoicesLength()), + Choices: make([]Choice, q.GetChoicesLength()), } results, err := textQ.unmarshalAnswers(texts) @@ -267,6 +267,12 @@ type Hint struct { De string } +// Choice contains a choice and an optional URL +type Choice struct { + Choice string + URL string +} + // Subject is a wrapper around multiple questions that can be of type "select", // "rank", or "text". type Subject struct { @@ -431,7 +437,7 @@ type Select struct { Title Title MaxN uint MinN uint - Choices []string + Choices []Choice Hint Hint } @@ -497,7 +503,7 @@ type Rank struct { Title Title MaxN uint MinN uint - Choices []string + Choices []Choice Hint Hint } @@ -572,7 +578,7 @@ type Text struct { MinN uint MaxLength uint Regex string - Choices []string + Choices []Choice Hint Hint } diff --git a/contracts/evoting/types/ballots_test.go b/contracts/evoting/types/ballots_test.go index bc94dea27..e280f513e 100644 --- a/contracts/evoting/types/ballots_test.go +++ b/contracts/evoting/types/ballots_test.go @@ -59,13 +59,13 @@ func TestBallot_Unmarshal(t *testing.T) { Title: Title{En: "", Fr: "", De: ""}, MaxN: 2, MinN: 2, - Choices: make([]string, 3), + Choices: make([]Choice, 3), }, { ID: decodedQuestionID(2), Title: Title{En: "", Fr: "", De: ""}, MaxN: 3, MinN: 3, - Choices: make([]string, 5), + Choices: make([]Choice, 5), }}, Ranks: []Rank{{ @@ -73,7 +73,7 @@ func TestBallot_Unmarshal(t *testing.T) { Title: Title{En: "", Fr: "", De: ""}, MaxN: 4, MinN: 0, - Choices: make([]string, 4), + Choices: make([]Choice, 4), }}, Texts: []Text{{ @@ -83,7 +83,7 @@ func TestBallot_Unmarshal(t *testing.T) { MinN: 2, MaxLength: 10, Regex: "", - Choices: make([]string, 2), + Choices: make([]Choice, 2), }}, }, }} @@ -318,13 +318,13 @@ func TestSubject_MaxEncodedSize(t *testing.T) { Title: Title{En: "", Fr: "", De: ""}, MaxN: 3, MinN: 0, - Choices: make([]string, 3), + Choices: make([]Choice, 3), }, { ID: encodedQuestionID(2), Title: Title{En: "", Fr: "", De: ""}, MaxN: 5, MinN: 0, - Choices: make([]string, 5), + Choices: make([]Choice, 5), }}, Ranks: []Rank{{ @@ -332,7 +332,7 @@ func TestSubject_MaxEncodedSize(t *testing.T) { Title: Title{En: "", Fr: "", De: ""}, MaxN: 4, MinN: 0, - Choices: make([]string, 4), + Choices: make([]Choice, 4), }}, Texts: []Text{{ @@ -342,7 +342,7 @@ func TestSubject_MaxEncodedSize(t *testing.T) { MinN: 0, MaxLength: 10, Regex: "", - Choices: make([]string, 2), + Choices: make([]Choice, 2), }, { ID: encodedQuestionID(5), Title: Title{En: "", Fr: "", De: ""}, @@ -350,7 +350,7 @@ func TestSubject_MaxEncodedSize(t *testing.T) { MinN: 0, MaxLength: 10, Regex: "", - Choices: make([]string, 3), + Choices: make([]Choice, 3), }}, } @@ -403,7 +403,7 @@ func TestSubject_IsValid(t *testing.T) { Title: Title{En: "", Fr: "", De: ""}, MaxN: 0, MinN: 0, - Choices: make([]string, 0), + Choices: make([]Choice, 0), }} mainSubject.Ranks = []Rank{{ @@ -411,7 +411,7 @@ func TestSubject_IsValid(t *testing.T) { Title: Title{En: "", Fr: "", De: ""}, MaxN: 0, MinN: 0, - Choices: make([]string, 0), + Choices: make([]Choice, 0), }} configuration.Scaffold = []Subject{*mainSubject} @@ -426,7 +426,7 @@ func TestSubject_IsValid(t *testing.T) { Title: Title{En: "", Fr: "", De: ""}, MaxN: 0, MinN: 2, - Choices: make([]string, 0), + Choices: make([]Choice, 0), } configuration.Scaffold = []Subject{*mainSubject} @@ -442,7 +442,7 @@ func TestSubject_IsValid(t *testing.T) { Title: Title{En: "", Fr: "", De: ""}, MaxN: 1, MinN: 0, - Choices: make([]string, 0), + Choices: make([]Choice, 0), } configuration.Scaffold = []Subject{*mainSubject} @@ -460,7 +460,7 @@ func TestSubject_IsValid(t *testing.T) { MinN: 4, MaxLength: 0, Regex: "", - Choices: make([]string, 0), + Choices: make([]Choice, 0), }} configuration.Scaffold = []Subject{*mainSubject} diff --git a/integration/performance_test.go b/integration/performance_test.go index 220418e8b..6b46c46e6 100644 --- a/integration/performance_test.go +++ b/integration/performance_test.go @@ -210,7 +210,7 @@ func createFormNChunks(m txManager, title types.Title, admin string, numChunks i MinN: 0, MaxLength: uint(base64.StdEncoding.DecodedLen(textSize)), Regex: "", - Choices: []string{"Your fav snack: "}, + Choices: []types.Choice{{Choice: "Your fav snack: ", URL: ""}}, }}, }, }, diff --git a/internal/testing/fake/election.go b/internal/testing/fake/election.go index 7b53d4764..65902d306 100644 --- a/internal/testing/fake/election.go +++ b/internal/testing/fake/election.go @@ -88,7 +88,7 @@ var BasicConfiguration = types.Configuration{ Title: types.Title{En: "Select your favorite snacks", Fr: "", De: ""}, MaxN: 3, MinN: 0, - Choices: []string{"snickers", "mars", "vodka", "babibel"}, + Choices: []types.Choice{{Choice: "snickers", URL: ""}, {Choice: "mars", URL: ""}, {Choice: "vodka", URL: ""}, {Choice: "babibel", URL: ""}}, }, }, Ranks: []types.Rank{}, @@ -109,7 +109,7 @@ var BasicConfiguration = types.Configuration{ MinN: 1, MaxLength: 3, Regex: "", - Choices: []string{"write yes in your language"}, + Choices: []types.Choice{{Choice: "write yes in your language", URL: ""}}, }, }, }, From 414997950f7f82f61653217b4f14dcb98ce92852 Mon Sep 17 00:00:00 2001 From: Carine Dengler Date: Mon, 26 Feb 2024 17:46:44 +0100 Subject: [PATCH 2/5] feat: add URL field to choices --- web/frontend/src/language/de.json | 1 + web/frontend/src/language/en.json | 1 + web/frontend/src/language/fr.json | 1 + web/frontend/src/mocks/mockData.ts | 79 ++++---- .../src/pages/ballot/components/Rank.tsx | 35 +++- .../src/pages/ballot/components/Select.tsx | 26 ++- .../src/pages/ballot/components/Text.tsx | 29 ++- .../form/components/AddQuestionModal.tsx | 170 ++++++------------ .../src/pages/form/components/RankResult.tsx | 15 +- .../pages/form/components/SelectResult.tsx | 33 ++-- .../src/pages/form/components/TextResult.tsx | 6 +- .../form/components/utils/countResult.ts | 2 +- .../form/components/utils/useQuestionForm.ts | 86 +++++---- .../src/schema/configurationValidation.ts | 25 ++- web/frontend/src/schema/form_conf.json | 24 ++- web/frontend/src/types/configuration.ts | 24 ++- web/frontend/src/types/getObjectType.ts | 37 ++-- .../tests/json/evoting/forms/canceled.json | 35 +++- .../tests/json/evoting/forms/closed.json | 35 +++- .../tests/json/evoting/forms/combined.json | 35 +++- .../tests/json/evoting/forms/created.json | 36 +++- .../tests/json/evoting/forms/decrypted.json | 35 +++- .../tests/json/evoting/forms/open.json | 35 +++- .../tests/json/evoting/forms/shuffled.json | 35 +++- 24 files changed, 525 insertions(+), 315 deletions(-) diff --git a/web/frontend/src/language/de.json b/web/frontend/src/language/de.json index f938a4333..30e6f332c 100644 --- a/web/frontend/src/language/de.json +++ b/web/frontend/src/language/de.json @@ -37,6 +37,7 @@ "removetext": "Text entfernen", "subject": "Betreff", "choices": "Auswahlmöglichkeiten", + "url": "URL", "answers": "Antworten", "enterMaxLength": "Geben Sie die MaxLength", "maxChoices": "Maximale Anzahl von Auswahlmöglichkeiten", diff --git a/web/frontend/src/language/en.json b/web/frontend/src/language/en.json index 7f0a25b6e..ac964cafd 100644 --- a/web/frontend/src/language/en.json +++ b/web/frontend/src/language/en.json @@ -33,6 +33,7 @@ "removetext": "Remove text", "subject": "Subject", "choices": "Choices", + "url": "URL", "answers": "Answers", "enterMaxLength": "Enter the MaxLength", "maxChoices": "Max number of choices", diff --git a/web/frontend/src/language/fr.json b/web/frontend/src/language/fr.json index d783e8c1d..d0ad19969 100644 --- a/web/frontend/src/language/fr.json +++ b/web/frontend/src/language/fr.json @@ -37,6 +37,7 @@ "removetext": "Supprimer le texte", "subject": "Sujet", "choices": "Choix", + "url": "URL", "answers": "Réponses", "enterMaxLength": "Entrer la longueur max", "maxChoices": "Max nombre de choix", diff --git a/web/frontend/src/mocks/mockData.ts b/web/frontend/src/mocks/mockData.ts index a254e0871..6aa58feca 100644 --- a/web/frontend/src/mocks/mockData.ts +++ b/web/frontend/src/mocks/mockData.ts @@ -51,9 +51,9 @@ const mockForm1: any = { MaxN: 2, MinN: 1, Choices: [ - '{"en": "tomato", "fr": "tomate", "de": "Tomate"}', - '{"en": "salad", "fr": "salade", "de": "Salat"}', - '{"en": "onion", "fr": "oignon", "de": "Zwiebel"}', + { Choice: '{"en": "tomato", "fr": "tomate", "de": "Tomate"}', URL: '' }, + { Choice: '{"en": "salad", "fr": "salade", "de": "Salat"}', URL: '' }, + { Choice: '{"en": "onion", "fr": "oignon", "de": "Zwiebel"}', URL: '' }, ], Hint: { En: '', Fr: '', De: '' }, }, @@ -69,9 +69,9 @@ const mockForm1: any = { MaxN: 3, MinN: 3, Choices: [ - '{"en": "BC", "fr": "BC", "de": "BC"}', - '{"en": "SV", "fr": "SV", "de": "SV"}', - '{"en": "Parmentier", "fr": "Parmentier", "de": "Parmentier"}', + { Choice: '{"en": "BC", "fr": "BC", "de": "BC"}', URL: '' }, + { Choice: '{"en": "SV", "fr": "SV", "de": "SV"}', URL: '' }, + { Choice: '{"en": "Parmentier", "fr": "Parmentier", "de": "Parmentier"}', URL: '' }, ], Hint: { En: '', Fr: '', De: '' }, }, @@ -90,11 +90,11 @@ const mockForm1: any = { MaxN: 1, MinN: 1, Choices: [ - '{"en":"1" ,"fr": "1", "de": "1"}', - '{"en":"2", "fr": "2", "de": "2"}', - '{"en":"3", "fr": "3", "de": "3"}', - '{"en":"4", "fr": "4", "de": "4"}', - '{ "en": "5", "fr": "5", "de": "5" }', + { Choice: '{"en":"1" ,"fr": "1", "de": "1"}', URL: '' }, + { Choice: '{"en":"2", "fr": "2", "de": "2"}', URL: '' }, + { Choice: '{"en":"3", "fr": "3", "de": "3"}', URL: '' }, + { Choice: '{"en":"4", "fr": "4", "de": "4"}', URL: '' }, + { Choice: '{ "en": "5", "fr": "5", "de": "5" }', URL: '' }, ], Hint: { En: '', Fr: '', De: '' }, }, @@ -108,9 +108,9 @@ const mockForm1: any = { MaxN: 1, MinN: 1, Choices: [ - '{"en" : "bad", "fr": "mauvais", "de": "schlecht"}', - '{"en" : "normal", "fr": "normal", "de": "durchschnittlich"}', - '{"en" : "good", "fr": "super", "de": "gut"}', + { Choice: '{"en" : "bad", "fr": "mauvais", "de": "schlecht"}', URL: '' }, + { Choice: '{"en" : "normal", "fr": "normal", "de": "durchschnittlich"}', URL: '' }, + { Choice: '{"en" : "good", "fr": "super", "de": "gut"}', URL: '' }, ], Hint: { En: 'Be honest. This is anonymous anyway', @@ -132,8 +132,8 @@ const mockForm1: any = { MinN: 1, Regex: '', Choices: [ - '{"en":"TA1", "fr": "TA1", "de": "TA1"}', - '{"en":"TA2", "fr":"TA2","de": "TA2"}', + { Choice: '{"en":"TA1", "fr": "TA1", "de": "TA1"}', URL: '' }, + { Choice: '{"en":"TA2", "fr":"TA2","de": "TA2"}', URL: '' }, ], Hint: { En: '', Fr: '', De: '' }, }, @@ -191,11 +191,11 @@ const mockForm2: any = { MaxN: 1, MinN: 1, Choices: [ - '{"en":"1" ,"fr": "1", "de": "1"}', - '{"en":"2", "fr": "2", "de": "2"}', - '{"en":"3", "fr": "3", "de": "3"}', - '{"en":"4", "fr": "4", "de": "4"}', - '{ "en": "5", "fr": "5", "de": "5" }', + { Choice: '{"en":"1" ,"fr": "1", "de": "1"}', URL: '' }, + { Choice: '{"en":"2", "fr": "2", "de": "2"}', URL: '' }, + { Choice: '{"en":"3", "fr": "3", "de": "3"}', URL: '' }, + { Choice: '{"en":"4", "fr": "4", "de": "4"}', URL: '' }, + { Choice: '{ "en": "5", "fr": "5", "de": "5" }', URL: '' }, ], Hint: { En: '', Fr: '', De: '' }, }, @@ -212,8 +212,8 @@ const mockForm2: any = { MaxN: 2, MinN: 2, Choices: [ - '{"en":"TA1", "fr": "TA1", "de": "TA1"}', - '{"en":"TA2", "fr":"TA2","de": "TA2"}', + { Choice: '{"en":"TA1", "fr": "TA1", "de": "TA1"}', URL: '' }, + { Choice: '{"en":"TA2", "fr":"TA2","de": "TA2"}', URL: '' }, ], Regex: '^[A-Z][a-z]+$', Hint: { En: '', Fr: '', De: '' }, @@ -238,10 +238,10 @@ const mockForm2: any = { MaxN: 3, MinN: 0, Choices: [ - '{"en": "tomato", "fr": "tomate", "de": "Tomate"}', - '{"en": "salad", "fr": "salade", "de": "Salat"}', - '{"en": "onion", "fr": "oignon", "de": "Zwiebel"}', - '{"en": "falafel", "fr": "falafel", "de": "Falafel"}', + { Choice: '{"en": "tomato", "fr": "tomate", "de": "Tomate"}', URL: '' }, + { Choice: '{"en": "salad", "fr": "salade", "de": "Salat"}', URL: '' }, + { Choice: '{"en": "onion", "fr": "oignon", "de": "Zwiebel"}', URL: '' }, + { Choice: '{"en": "falafel", "fr": "falafel", "de": "Falafel"}', URL: '' }, ], Hint: { En: '', Fr: '', De: '' }, }, @@ -258,10 +258,14 @@ const mockForm2: any = { MaxN: 4, MinN: 1, Choices: [ - '{"en": "Esplanade", "fr": "Esplanade", "de": "Esplanade"}', - '{"en": "Giacometti", "fr": "Giacometti", "de": "Giacometti"}', - '{"en": "Arcadie", "fr": "Arcadie", "de": "Arcadie"}', - '{"en": "Montreux Jazz Cafe", "fr": "Montreux Jazz Cafe", "de": "Montreux Jazz Cafe"}', + { Choice: '{"en": "Esplanade", "fr": "Esplanade", "de": "Esplanade"}', URL: '' }, + { Choice: '{"en": "Giacometti", "fr": "Giacometti", "de": "Giacometti"}', URL: '' }, + { Choice: '{"en": "Arcadie", "fr": "Arcadie", "de": "Arcadie"}', URL: '' }, + { + Choice: + '{"en": "Montreux Jazz Cafe", "fr": "Montreux Jazz Cafe", "de": "Montreux Jazz Cafe"}', + URL: '', + }, ], Hint: { En: '', Fr: '', De: '' }, }, @@ -270,7 +274,10 @@ const mockForm2: any = { ID: (0xbeef).toString(), MaxN: 2, MinN: 1, - Choices: ['{"en": "IN", "fr": "IN", "de": "IN"}', '{"en": "SC", "fr": "SC", "de": "SC"}'], + Choices: [ + { Choice: '{"en": "IN", "fr": "IN", "de": "IN"}', URL: '' }, + { Choice: '{"en": "SC", "fr": "SC", "de": "SC"}', URL: '' }, + ], Hint: { En: 'The right answer is IN ;-)', Fr: 'La bonne réponse est IN ;-)', @@ -310,10 +317,10 @@ const mockForm3: any = { MaxLength: 50, Regex: '', Choices: [ - '{"en": "Firstname", "fr": "Prénom", "de": "Firstname"}', - '{"en": "Main 🍕", "fr" : "Principal 🍕", "de": "Main 🍕"}', - '{"en": "Drink 🧃", "fr": "Boisson 🧃", "de": "Drink 🧃"}', - '{"en":"Dessert 🍰", "fr": "Dessert 🍰", "de": "Nachtisch 🍰"}', + { Choice: '{"en": "Firstname", "fr": "Prénom", "de": "Firstname"}', URL: '' }, + { Choice: '{"en": "Main 🍕", "fr" : "Principal 🍕", "de": "Main 🍕"}', URL: '' }, + { Choice: '{"en": "Drink 🧃", "fr": "Boisson 🧃", "de": "Drink 🧃"}', URL: '' }, + { Choice: '{"en":"Dessert 🍰", "fr": "Dessert 🍰", "de": "Nachtisch 🍰"}', URL: '' }, ], Hint: { En: 'If you change opinion call me before 11:30 a.m.', diff --git a/web/frontend/src/pages/ballot/components/Rank.tsx b/web/frontend/src/pages/ballot/components/Rank.tsx index 9bf4f9016..4091a67be 100644 --- a/web/frontend/src/pages/ballot/components/Rank.tsx +++ b/web/frontend/src/pages/ballot/components/Rank.tsx @@ -61,7 +61,14 @@ const Rank: FC = ({ rank, answers, language }) => { useEffect(() => { setTitles(rank.Title); }, [rank]); - const choiceDisplay = (choice: string, rankIndex: number) => { + const choiceDisplay = (choice: string, url: string, rankIndex: number) => { + const prettyChoice = url ? ( + + {choice} + + ) : ( + choice + ); return ( {(provided) => ( @@ -74,7 +81,7 @@ const Rank: FC = ({ rank, answers, language }) => {

{rankIndex + 1}

- {choice} + {prettyChoice}
@@ -102,13 +109,25 @@ const Rank: FC = ({ rank, answers, language }) => {
    {Array.from(answers.RankAnswers.get(rank.ID).entries()) .map(([rankIndex, choiceIndex]) => { - if (language === 'fr' && rank.ChoicesMap.has('fr')) - return choiceDisplay(rank.ChoicesMap.get('fr')[choiceIndex], rankIndex); - else if (language === 'de' && rank.ChoicesMap.has('de')) - return choiceDisplay(rank.ChoicesMap.get('de')[choiceIndex], rankIndex); - else if (rank.ChoicesMap.has('en')) + if (language === 'fr' && rank.ChoicesMap.ChoicesMap.has('fr')) + return choiceDisplay( + rank.ChoicesMap.ChoicesMap.get('fr')[choiceIndex], + rank.ChoicesMap.URLs[choiceIndex], + rankIndex + ); + else if (language === 'de' && rank.ChoicesMap.ChoicesMap.has('de')) + return choiceDisplay( + rank.ChoicesMap.ChoicesMap.get('de')[choiceIndex], + rank.ChoicesMap.URLs[choiceIndex], + rankIndex + ); + else if (rank.ChoicesMap.ChoicesMap.has('en')) // 'en' is the default language - return choiceDisplay(rank.ChoicesMap.get('en')[choiceIndex], rankIndex); + return choiceDisplay( + rank.ChoicesMap.ChoicesMap.get('en')[choiceIndex], + rank.ChoicesMap.URLs[choiceIndex], + rankIndex + ); return undefined; }) .filter((e) => e !== undefined)} diff --git a/web/frontend/src/pages/ballot/components/Select.tsx b/web/frontend/src/pages/ballot/components/Select.tsx index 8b3c2c7a6..211dbe019 100644 --- a/web/frontend/src/pages/ballot/components/Select.tsx +++ b/web/frontend/src/pages/ballot/components/Select.tsx @@ -60,7 +60,14 @@ const Select: FC = ({ select, answers, setAnswers, language }) => { setTitles(select.Title); }, [select]); - const choiceDisplay = (isChecked: boolean, choice: string, choiceIndex: number) => { + const choiceDisplay = (isChecked: boolean, choice: string, url: string, choiceIndex: number) => { + const prettyChoice = url ? ( + + {choice} + + ) : ( + choice + ); return (
    = ({ select, answers, setAnswers, language }) => { onChange={(e) => handleChecks(e, choiceIndex)} />
    ); @@ -93,22 +100,25 @@ const Select: FC = ({ select, answers, setAnswers, language }) => {
    {Array.from(answers.SelectAnswers.get(select.ID).entries()) .map(([choiceIndex, isChecked]) => { - if (language === 'fr' && select.ChoicesMap.has('fr')) + if (language === 'fr' && select.ChoicesMap.ChoicesMap.has('fr')) return choiceDisplay( isChecked, - select.ChoicesMap.get('fr')[choiceIndex], + select.ChoicesMap.ChoicesMap.get('fr')[choiceIndex], + select.ChoicesMap.URLs[choiceIndex], choiceIndex ); - else if (language === 'de' && select.ChoicesMap.has('de')) + else if (language === 'de' && select.ChoicesMap.ChoicesMap.has('de')) return choiceDisplay( isChecked, - select.ChoicesMap.get('de')[choiceIndex], + select.ChoicesMap.ChoicesMap.get('de')[choiceIndex], + select.ChoicesMap.URLs[choiceIndex], choiceIndex ); - else if (select.ChoicesMap.has('en')) + else if (select.ChoicesMap.ChoicesMap.has('en')) return choiceDisplay( isChecked, - select.ChoicesMap.get('en')[choiceIndex], + select.ChoicesMap.ChoicesMap.get('en')[choiceIndex], + select.ChoicesMap.URLs[choiceIndex], choiceIndex ); return undefined; diff --git a/web/frontend/src/pages/ballot/components/Text.tsx b/web/frontend/src/pages/ballot/components/Text.tsx index 3358d0784..f8a9635e0 100644 --- a/web/frontend/src/pages/ballot/components/Text.tsx +++ b/web/frontend/src/pages/ballot/components/Text.tsx @@ -78,12 +78,19 @@ const Text: FC = ({ text, answers, setAnswers, language }) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [answers]); - const choiceDisplay = (choice: string, choiceIndex: number) => { + const choiceDisplay = (choice: string, url: string, choiceIndex: number) => { const columns = text.MaxLength > 50 ? 50 : text.MaxLength; + const prettyChoice = url ? ( + + {choice} + + ) : ( + choice + ); return (