diff --git a/src/assets/removeIcon.png b/src/assets/removeIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..3c66b2a75ff7d81fd33622bf13a903f838f114ee Binary files /dev/null and b/src/assets/removeIcon.png differ diff --git a/src/components/CreateNewGroup.vue b/src/components/CreateNewGroup.vue new file mode 100644 index 0000000000000000000000000000000000000000..ceb5303249f9797e27e06fea8c189f9ab99a095f --- /dev/null +++ b/src/components/CreateNewGroup.vue @@ -0,0 +1,306 @@ +<template> + <div class="m-6"> + <!-- Component heading --> + <div class="flex justify-center mt-6"> + <p class="text-4xl">Opprett Gruppe</p> + </div> + + <!-- Radio boxes --> + <div class="mt-6"> + <label + class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300" + id="radioBoxLabel" + >Synlighet</label + > + <div class="form-check"> + <input + class="form-check-input appearance-none rounded-full h-4 w-4 border border-gray-300 bg-white checked:bg-blue-600 checked:border-blue-600 focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer" + type="radio" + name="flexRadioDefault" + id="flexRadioOpen" + value="Åpen" + @change="checkRadioButton($event)" + checked + /> + <label + class="form-check-label inline-block text-gray-800" + for="flexRadioOpen" + id="radioBoxOpenLabel" + > + Åpen + </label> + </div> + <div class="form-check"> + <input + class="form-check-input appearance-none rounded-full h-4 w-4 border border-gray-300 bg-white checked:bg-blue-600 checked:border-blue-600 focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer" + type="radio" + name="flexRadioDefault" + id="flexRadioPrivate" + value="Privat" + @change="checkRadioButton($event)" + /> + <label + class="form-check-label inline-block text-gray-800" + for="flexRadioPrivate" + id="radioBoxPrivateLabel" + > + Privat + </label> + </div> + </div> + + <!-- Title --> + <div class="mt-6" :class="{ error: v$.group.name.$errors.length }"> + <label + class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300" + id="titleLabel" + >Gruppenavn</label + > + <input + type="text" + id="title" + class="bg-gray-200 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" + v-model="v$.group.name.$model" + required + /> + + <!-- error message for title--> + <div + class="text-red" + v-for="(error, index) of v$.group.name.$errors" + :key="index" + > + <div class="text-red-600 text-sm"> + {{ error.$message }} + </div> + </div> + </div> + + <!-- Select category --> + <div class="mt-6"> + <label + class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400" + id="selectCategoryLabel" + >Kategori</label + > + <select + v-model="v$.group.select.$model" + id="categories" + class="bg-gray-200 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" + > + <option class="text-gray-400" value="" disabled selected> + Select a category + </option> + <option + v-for="category in group.categories" + :key="category" + class="text-gray-900 text-sm" + > + {{ category }} + </option> + </select> + + <!-- error message for select box --> + <div + class="text-red" + v-for="(error, index) of v$.group.select.$errors" + :key="index" + > + <div class="text-red-600 text-sm"> + {{ error.$message }} + </div> + </div> + </div> + + <!-- Description --> + <div class="mt-6" :class="{ error: v$.group.description.$errors.length }"> + <label + class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400" + id="descriptionLabel" + >Beskrivelse</label + > + <textarea + id="description" + rows="4" + v-model="v$.group.description.$model" + class="block p-2.5 w-full text-sm text-gray-900 bg-gray-200 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" + required + ></textarea> + + <!-- error message for description --> + <div + class="text-red" + v-for="(error, index) of v$.group.description.$errors" + :key="index" + > + <div class="text-red-600 text-sm"> + {{ error.$message }} + </div> + </div> + </div> + + <!-- Images --> + <div class="mt-6"> + <label + class="block mb-2 text-xl font-medium text-gray-900 dark:text-gray-400" + id="imageLabel" + > + Bilde + </label> + + <input + type="file" + ref="file" + style="display: none" + @change="addImage" + multiple + accept="image/png, image/jpeg" + /> + + <!-- Button for adding an image --> + <div class="inline-flex rounded-md shadow-sm"> + <button + @click="$refs.file.click()" + class="text-black bg-gray-200 hover:bg-grey-800 focus:ring-4 focus:outline-none focus:ring-grey-300 font-medium rounded-lg text-sm sm:w-auto px-5 py-2.5 text-center dark:bg-grey-600 dark:hover:bg-grey-700 dark:focus:ring-grey-800 disabled:opacity-50 cursor-not-allowed" + :disabled="imageAdded" + > + Velg bilde + </button> + + <!-- Button for removing an image --> + <button + class="w-1/12 ml-5 text-white bg-white-500 font-medium rounded-lg text-sm" + v-show="imageAdded" + @click="removeImage" + > + <img src="../assets/removeIcon.png" alt="Remove icon image" /> + </button> + </div> + + <!-- Div box for showing all chosen images --> + <div v-for="image in group.images" :key="image" class="m-2"> + <img :src="image" class="w-1/2 inline" alt="Bilde av gjenstanden" /> + </div> + </div> + + <!-- Save item button --> + <div class="flex justify-center mt-10"> + <button + @click="saveClicked" + class="content-center text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800" + id="saveButton" + > + Lagre + </button> + </div> + </div> +</template> + +<script> +import useVuelidate from "@vuelidate/core"; +import { required, helpers, maxLength } from "@vuelidate/validators"; + +export default { + name: "CreateNewGroup.vue", + + setup() { + return { v$: useVuelidate() }; + }, + + validations() { + return { + group: { + name: { + required: helpers.withMessage( + () => "Navnt kan ikke være tom", + required + ), + max: helpers.withMessage( + () => `Navnet kan være på max 50 tegn`, + maxLength(50) + ), + }, + description: { + required: helpers.withMessage( + () => "Beskrivelsen kan ikke være tom", + required + ), + max: helpers.withMessage( + () => `Beskrivelsen kan inneholde max 200 tegn`, + maxLength(200) + ), + }, + select: { + required: helpers.withMessage(() => `Velg en kategori`, required), + }, + }, + }; + }, + data() { + return { + group: { + name: "", + select: null, + description: "", + images: [], + categories: ["Borettslag", "Idrettsklubb", "Fritidsklubb"], + radio: null, + }, + imageThere: false, + }; + }, + computed: { + imageAdded: function () { + if (this.imageThere) { + return true; + } else { + return false; + } + }, + }, + methods: { + removeImage: function () { + this.group.images.pop(); + this.imageThere = false; + console.log("Bilder nå: " + this.group.images.length); + }, + checkRadioButton: function (event) { + this.group.radio = event.target.value; + console.log(this.group.radio); + }, + checkValidation: function () { + console.log("sjekker validering"); + + this.v$.group.$touch(); + if (this.v$.group.$invalid) { + console.log("Invalid, avslutter..."); + return false; + } + + console.log("validert!"); + return true; + }, + + async saveClicked() { + console.log("Attempting to save item"); + + if (this.checkValidation()) { + console.log("validert, videre..."); + console.log("Navn: " + this.group.name); + console.log("Synlighet: " + this.group.radio); + console.log("Kategori: " + this.group.select); + console.log("Beskrivelse: " + this.group.description); + console.log("bilder: " + this.group.images); + } + }, + + addImage: function (event) { + console.log(event.target.files); + this.group.images.push(URL.createObjectURL(event.target.files[0])); + console.log("antall bilder: " + this.group.images.length); + this.imageThere = true; + console.log("image: " + this.imageThere); + }, + }, +}; +</script> diff --git a/src/router/index.js b/src/router/index.js index 284f32e9039d6c6cdd0cffed21ddf02be8106929..63c352f01601c004be23eca91277b9d0bc39f716 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -42,6 +42,11 @@ const routes = [ name: "newPassword", component: NewPasswordView, }, + { + path: "/createNewGroup", + name: "createNewGroup", + component: () => import("../views/CreateNewGroupView.vue"), + }, ]; const router = createRouter({ diff --git a/src/views/CreateNewGroupView.vue b/src/views/CreateNewGroupView.vue new file mode 100644 index 0000000000000000000000000000000000000000..d3b69d68220d7cd3a1cc3cd4fa4e3398feafe696 --- /dev/null +++ b/src/views/CreateNewGroupView.vue @@ -0,0 +1,15 @@ +<template> + <CreateNewGroup></CreateNewGroup> +</template> + +<script> +import CreateNewGroup from "@/components/CreateNewGroup"; +export default { + name: "CreateNewGroupView.vue", + components: { + CreateNewGroup, + }, +}; +</script> + +<style scoped></style> diff --git a/tests/unit/create-new-group.spec.js b/tests/unit/create-new-group.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..2b269b082ece04c8d1cbfa9c8eb9233d0d4e00f8 --- /dev/null +++ b/tests/unit/create-new-group.spec.js @@ -0,0 +1,49 @@ +import { shallowMount } from "@vue/test-utils"; +import CreateNewGroup from "@/components/CreateNewGroup.vue"; + +describe("CreateNewGroup elements rendering", () => { + + it("renders all labels", () => { + + const wrapper = shallowMount(CreateNewGroup); + + expect(wrapper.find('#radioBoxLabel').text()).toMatch("Synlighet"); + expect(wrapper.find('#radioBoxOpenLabel').text()).toMatch("Åpen"); + expect(wrapper.find('#radioBoxPrivateLabel').text()).toMatch("Privat"); + expect(wrapper.find('#titleLabel').text()).toMatch("Gruppenavn"); + expect(wrapper.find('#selectCategoryLabel').text()).toMatch("Kategori"); + expect(wrapper.find('#descriptionLabel').text()).toMatch("Beskrivelse"); + expect(wrapper.find('#imageLabel').text()).toMatch("Bilde"); + + }); + + it("Tests setting values of input field", async() => { + + const wrapper = shallowMount(CreateNewGroup); + + const titleInput = wrapper.find('#title'); + await titleInput.setValue("Fjellgata"); + expect(titleInput.element.value).toBe("Fjellgata"); + + const selectedCategory = wrapper.find('#categories'); + await selectedCategory.setValue("Borettslag"); + expect(selectedCategory.element.value).toBe("Borettslag"); + + const descriptionInput = wrapper.find('#description'); + await descriptionInput.setValue("Dette er et borettslag"); + expect(descriptionInput.element.value).toBe("Dette er et borettslag"); + }); + + it("Tests if radio box checks", async() => { + + const wrapper = shallowMount(CreateNewGroup); + + const radioInputOpen = wrapper.find('#flexRadioOpen'); + await radioInputOpen.setChecked(); + expect(radioInputOpen.element.checked).toBeTruthy(); + + const radioInputPrivate = wrapper.find('#flexRadioPrivate'); + await radioInputPrivate.setChecked(); + expect(radioInputPrivate.element.checked).toBeTruthy(); + }); +});