Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade Primevue #185

Draft
wants to merge 35 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
a3ab39d
reset scroll in Demo app
sadiqkhoja Jul 12, 2024
9467b61
add new icons
sadiqkhoja Jul 12, 2024
4a92320
Feature #130: Required and constraint validations
sadiqkhoja Jul 12, 2024
02c309b
Styling changes
sadiqkhoja Jul 15, 2024
6c062f7
Refactor: use provide/inject for submitPressed.
sadiqkhoja Jul 26, 2024
2ce71eb
Moved Form level error message out of FormHeader
sadiqkhoja Jul 26, 2024
29879d3
use inset-inline-end instead of right for positioning error icon insi…
sadiqkhoja Jul 26, 2024
811b6ce
set width of form error message to 100% for smaller screens
sadiqkhoja Jul 26, 2024
3b5b6f7
remove additional wrapper div that was added in .smaller-screen section
sadiqkhoja Jul 30, 2024
a2d4ec8
change unit of scroll-margin-top from px to rem
sadiqkhoja Jul 30, 2024
62c7859
Scroll to first invalid question + focus it if it is a textbox
sadiqkhoja Jul 30, 2024
c555db4
fix: submitPressed not provided error
sadiqkhoja Aug 2, 2024
3bffb9d
fix: reduce heading size and weight
sadiqkhoja Jul 17, 2024
8773137
fix: change background color of textboxes
sadiqkhoja Jul 17, 2024
257d504
fix: make width of language change to max-content
sadiqkhoja Jul 17, 2024
ccd7673
fix: reduce the spacing between questions in groups
sadiqkhoja Jul 17, 2024
270a47c
fix: remove shadow from the cards
sadiqkhoja Jul 17, 2024
6f86943
fix: add + icon for repeat add button. Also fix vertical spacing for …
sadiqkhoja Jul 17, 2024
68fe0c0
fix: change the style of disabled textbox
sadiqkhoja Jul 18, 2024
564706a
fix: flat the send button
sadiqkhoja Jul 18, 2024
893ef04
fix: increase the size of repeat number
sadiqkhoja Jul 18, 2024
661530c
fix: size and style of repeat context menu
sadiqkhoja Jul 18, 2024
9c03ed5
fix: align hamburger to the top + show options based on container size
sadiqkhoja Jul 18, 2024
b6abddc
fix: align chevron to the middle
sadiqkhoja Jul 18, 2024
b3d392c
fix: set background of the widget to white
sadiqkhoja Jul 18, 2024
0188fd1
fix: align question container when it is inside groups
sadiqkhoja Jul 18, 2024
e21db60
fix: make fix-error a button
sadiqkhoja Jul 18, 2024
d5cbf6f
fix: gap between label and control
sadiqkhoja Jul 19, 2024
33301d1
format all
sadiqkhoja Jul 19, 2024
d2bc54d
Increase max-width of language changer and min-width of container query
sadiqkhoja Jul 31, 2024
2dd6977
set max-width of add repeat button to 260px
sadiqkhoja Jul 31, 2024
0de7075
remove .chevron padding and make h2(flex) items align center
sadiqkhoja Jul 31, 2024
9588836
use gap instead of margin between select options
sadiqkhoja Jul 31, 2024
8382ffa
fix vertical alignment of validation message and no hover state for l…
sadiqkhoja Jul 31, 2024
3def368
Upgrade primevue from v3 to v4
sadiqkhoja Aug 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Feature #130: Required and constraint validations
sadiqkhoja committed Jul 30, 2024
commit 4a92320c96a245fc6af31dc8567472ffc600d64c
93 changes: 93 additions & 0 deletions packages/ui-solid/fixtures/xforms/validation/1-validation.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?xml version="1.0"?>
<h:html xmlns="http://www.w3.org/2002/xforms" xmlns:h="http://www.w3.org/1999/xhtml"
xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jr="http://openrosa.org/javarosa" xmlns:orx="http://openrosa.org/xforms"
xmlns:odk="http://www.opendatakit.org/xforms">
<h:head>
<h:title>Validation Form</h:title>
<model odk:xforms-version="1.0.0">
<itext>
<translation lang="default" default="true()">
<text id="/data/profession:jr:requiredMsg">
<value>Please enter your profession</value>
</text>
<text id="/data/citizen:jr:constraintMsg">
<value>It has to be two</value>
</text>
</translation>
<translation lang="Urdu (ur)">
<text id="/data/profession:jr:requiredMsg">
<value>اپنا پروفیشن بتائیں</value>
</text>
<text id="/data/citizen:jr:constraintMsg">
<value>صرف دو ہی ہوسکتی ہیں</value>
</text>
</translation>
</itext>
<instance>
<data id="validation" version="20240708154514">
<first_name />
<last_name />
<profession />
<favourite_color />
<citizen />
<age />
<meta>
<instanceID />
</meta>
</data>
</instance>
<instance id="country">
<root>
<item>
<name>pk</name>
<label>Pakistan</label>
</item>
<item>
<name>ca</name>
<label>Canada</label>
</item>
<item>
<name>us</name>
<label>USA</label>
</item>
</root>
</instance>
<bind nodeset="/data/first_name" type="string" required="true()" />
<bind nodeset="/data/last_name" type="string" required="true()"
jr:requiredMsg="Please enter the Last Name" />
<bind nodeset="/data/profession" type="string" required="true()"
jr:requiredMsg="jr:itext('/data/profession:jr:requiredMsg')" />
<bind nodeset="/data/favourite_color" type="string" constraint=". = &quot;red&quot;" />
<bind nodeset="/data/citizen" type="string" constraint="count-selected(.) = 2"
jr:constraintMsg="jr:itext('/data/citizen:jr:constraintMsg')" />
<bind nodeset="/data/age" type="string" required="true()"
constraint=". &gt; 18 and . &lt; 100" jr:constraintMsg="Age has to be between 18 and 100" />
<bind nodeset="/data/meta/instanceID" type="string" readonly="true()" jr:preload="uid" />
</model>
</h:head>
<h:body>
<input ref="/data/first_name">
<label>First Name</label>
</input>
<input ref="/data/last_name">
<label>Last Name</label>
</input>
<input ref="/data/profession">
<label>Profession</label>
</input>
<input ref="/data/favourite_color">
<label>Favourite Color</label>
</input>
<select ref="/data/citizen">
<label>Citizenships</label>
<itemset nodeset="instance('country')/root/item">
<value ref="name" />
<label ref="label" />
</itemset>
</select>
<input ref="/data/age">
<label>Age</label>
</input>
</h:body>
</h:html>
164 changes: 117 additions & 47 deletions packages/web-forms/src/components/FormHeader.vue
Original file line number Diff line number Diff line change
@@ -3,7 +3,8 @@ import { type FormLanguage, type RootNode, type SyntheticDefaultLanguage } from
import PrimeButton from 'primevue/button';
import PrimeCard from 'primevue/card';
import PrimeMenu from 'primevue/menu';
import { ref } from 'vue';
import PrimeMessage from 'primevue/message';
import { computed, ref } from 'vue';
import FormLanguageDialog from './FormLanguageDialog.vue';
import FormLanguageMenu from './FormLanguageMenu.vue';

@@ -19,6 +20,14 @@ const languages = props.form.languages.filter(isFormLanguage);

const print = () => window.print();

const formErrorMessage = computed(() => {
const violationLength = props.form.validationState.violations.length;

if(violationLength === 0) return '';
else if(violationLength === 1) return '1 question with error';
else return `${violationLength} questions with errors`;
});

const items = ref([
{
label: 'Print',
@@ -38,54 +47,73 @@ if(languages.length > 0){
const handleLanguageChange = (event: FormLanguage) => {
props.form.setLanguage(event);
};

const scrollToFirstInvalidQuestion = () => {
document.getElementById(props.form.validationState.violations[0].nodeId + '_container')?.scrollIntoView({
behavior: 'smooth'
});
}
</script>

<template>
<!-- for desktop -->
<div class="hidden lg:flex justify-content-end flex-wrap gap-3 larger-screens">
<PrimeButton class="print-button" severity="secondary" rounded icon="icon-local_printshop" @click="print" />
<FormLanguageMenu
:active-language="form.currentState.activeLanguage"
:languages="languages"
@update:active-language="handleLanguageChange"
/>
</div>
<PrimeCard class="form-title hidden lg:block">
<template #content>
<!-- TODO/q: should the title be on the definition or definition.form be accessible instead of definition.bind.form -->
<h1>{{ form.definition.bind.form.title }}</h1>
<!-- last saved timestamp -->
</template>
</PrimeCard>

<!-- for mobile and tablet -->
<div class="flex lg:hidden align-items-center smaller-screens">
<h1 class="flex-grow-1">
{{ form.definition.bind.form.title }}
</h1>

<!-- for tablet -->
<div class="form-options hidden md:flex justify-content-end gap-3">
<div class="hidden lg:inline larger-screens">
<PrimeMessage v-if="formErrorMessage" severity="error" icon="icon-error_outline" class="form-error-message" :closable="false">
{{ formErrorMessage }}
<span class="fix-errors" @click="scrollToFirstInvalidQuestion()">Fix errors</span>
</PrimeMessage>
<div class="flex justify-content-end flex-wrap gap-3">
<PrimeButton class="print-button" severity="secondary" rounded icon="icon-local_printshop" @click="print" />
<FormLanguageMenu
:active-language="form.currentState.activeLanguage"
:languages="languages"
@update:active-language="handleLanguageChange"
/>
</div>
<PrimeCard class="form-title">
<template #content>
<!-- TODO/q: should the title be on the definition or definition.form be accessible instead of definition.bind.form -->
<h1>{{ form.definition.bind.form.title }}</h1>
<!-- last saved timestamp -->
</template>
</PrimeCard>
</div>


<!-- for mobile and tablet -->
<div class="inline lg:hidden smaller-screens">
<div class="flex align-items-center title-bar">
<h1 class="flex-grow-1">
{{ form.definition.bind.form.title }}
</h1>

<!-- for tablet -->
<div class="form-options hidden md:flex justify-content-end gap-3">
<PrimeButton class="print-button" severity="secondary" rounded icon="icon-local_printshop" @click="print" />
<FormLanguageMenu
:active-language="form.currentState.activeLanguage"
:languages="languages"
@update:active-language="handleLanguageChange"
/>
</div>

<!-- for mobile -->
<div class="form-options flex md:hidden">
<PrimeButton v-if="languages.length > 0" icon="icon-menu" class="btn-menu" text rounded aria-label="Menu" @click="menu?.toggle" />
<PrimeButton v-else class="print-button" severity="secondary" rounded icon="icon-local_printshop" @click="print" />
<PrimeMenu id="overlay_menu" ref="menu" :model="items" :popup="true" />
<FormLanguageDialog
v-model:state="languageDialogState"
:active-language="form.currentState.activeLanguage"
:languages="languages"
@update:active-language="handleLanguageChange"
/>
<!-- for mobile -->
<div class="form-options flex md:hidden">
<PrimeButton v-if="languages.length > 0" icon="icon-menu" class="btn-menu" text rounded aria-label="Menu" @click="menu?.toggle" />
<PrimeButton v-else class="print-button" severity="secondary" rounded icon="icon-local_printshop" @click="print" />
<PrimeMenu id="overlay_menu" ref="menu" :model="items" :popup="true" />
<FormLanguageDialog
v-model:state="languageDialogState"
:active-language="form.currentState.activeLanguage"
:languages="languages"
@update:active-language="handleLanguageChange"
/>
</div>
</div>
<PrimeMessage v-if="formErrorMessage" severity="error" icon="icon-error_outline" class="form-error-message" :closable="false">
{{ formErrorMessage }}
<span class="fix-errors" @click="scrollToFirstInvalidQuestion()">Fix errors</span>
</PrimeMessage>
</div>
</template>

@@ -124,22 +152,64 @@ const handleLanguageChange = (event: FormLanguage) => {
}
}

.smaller-screens {
background-color: var(--surface-0);
filter: drop-shadow(0px 2px 6px rgba(0, 0, 0, 0.15)) drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.30)) ;
.form-error-message.p-message.p-message-error {
border-radius: 10px;
background-color: var(--error-bg-color);
border: 1px solid var(--error-text-color);
width: 70%;
margin: 0rem auto 1rem auto;
position: sticky;
top: 0;
// Some PrimeVue components use z-index.
// Default value for those are either 1000 or 1100
// So 5000 here is safe.
z-index: 5000;
display: none;

:deep(.p-message-wrapper) {
padding: 0.75rem 0.75rem;
flex-grow: 1;
}

h1 {
padding-left: 10px;
font-size: 1.5rem;
:deep(.p-message-text){
font-weight: 400;
flex-grow: 1;

.fix-errors {
float: right;
cursor: pointer;
}
}

.form-options{
padding-right: 10px;
}

.smaller-screens {
.title-bar{
background-color: var(--surface-0);
filter: drop-shadow(0px 2px 6px rgba(0, 0, 0, 0.15)) drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.30)) ;

h1 {
padding-left: 10px;
font-size: 1.5rem;
}

.form-options{
padding-right: 10px;
}

.btn-menu{
color: var(--surface-900);
}
}

.btn-menu{
color: var(--surface-900);

.form-error-message.p-message.p-message-error {
margin-top: 1rem;
margin-bottom: 0;
}
}

:global(.submit-pressed .form-error-message.p-message.p-message-error ){
display: block;
}

</style>
43 changes: 42 additions & 1 deletion packages/web-forms/src/components/FormQuestion.vue
Original file line number Diff line number Diff line change
@@ -13,11 +13,52 @@ const isSelectNode = (n: QuestionNode): n is SelectNode => n.nodeType === 'selec
</script>

<template>
<div class="flex flex-column gap-2">
<div :id="question.nodeId + '_container'" class="question-container" :class="{ invalid: question.validationState.violation?.valid === false}">
<InputText v-if="isStringNode(question)" :question="question" />

<SelectControl v-else-if="isSelectNode(question)" :question="question" />

<UnsupportedControl v-else :question="question" />

<div class="validation-placeholder">
<span class="validation-message">
{{ question.validationState.violation?.message.asString }}
</span>
</div>
</div>
</template>

<style scoped lang="scss">

.question-container {
display: flex;
flex-direction: column;
padding: 0.5rem 1rem 0 1rem;
scroll-margin-top: 60px;
}

.validation-placeholder{
height: 2rem;

.validation-message {
display: none;
color: var(--error-text-color);
margin-top: 0.5rem;
}
}

.invalid:has(.dirty) {
.validation-message {
display: block;
}
}

:global(.odk-form.submit-pressed .invalid){
background-color: var(--error-bg-color);
border-radius: 10px;
}

:global(.odk-form.submit-pressed .invalid .validation-message){
display: block;
}
</style>
Loading