Skip to content

Commit

Permalink
Merge pull request #160 from Geoportail-Luxembourg/GSLUX-747-auth-ui
Browse files Browse the repository at this point in the history
GSLUX-747: Auth form ui
  • Loading branch information
AlitaBernachot authored Oct 16, 2024
2 parents a76b429 + 084934a commit 24874eb
Show file tree
Hide file tree
Showing 16 changed files with 447 additions and 90 deletions.
5 changes: 5 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ VITE_MODE_LIB=true
VITE_MYMAPS_URL="/mymaps"
VITE_ARROW_MODEL_URL="/static-ngeo/models/arrow5.glb"
VITE_ELEVATION_URL="/raster"

# Auth
VITE_MYACCOUNT_URL="https://myaccount.geoportail.lu"
VITE_MYACCOUNT_RECOVER_URL="https://myaccount.geoportail.lu/recover-password"
VITE_MYACCOUNT_NEW_URL="https://myaccount.geoportail.lu/new-user"
7 changes: 6 additions & 1 deletion .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,9 @@ VITE_MODE_LIB=false
# MyMaps / Draw
VITE_MYMAPS_URL="https://migration.geoportail.lu/mymaps"
VITE_ARROW_MODEL_URL="https://migration.geoportail.lu/static-ngeo/models/arrow5.glb"
VITE_ELEVATION_URL="https://migration.geoportail.lu/raster"
VITE_ELEVATION_URL="https://migration.geoportail.lu/raster"

# Auth
VITE_MYACCOUNT_URL="https://myaccount.geoportail.lu"
VITE_MYACCOUNT_RECOVER_URL="https://myaccount.geoportail.lu/recover-password"
VITE_MYACCOUNT_NEW_URL="https://myaccount.geoportail.lu/new-user"
5 changes: 5 additions & 0 deletions .env.e2e
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ VITE_MODE_LIB=false
VITE_MYMAPS_URL="https://migration.geoportail.lu/mymaps"
VITE_ARROW_MODEL_URL="https://migration.geoportail.lu/static-ngeo/models/arrow5.glb"
VITE_ELEVATION_URL="https://migration.geoportail.lu/raster"

# Auth
VITE_MYACCOUNT_URL="https://myaccount.geoportail.lu"
VITE_MYACCOUNT_RECOVER_URL="https://myaccount.geoportail.lu/recover-password"
VITE_MYACCOUNT_NEW_URL="https://myaccount.geoportail.lu/new-user"
7 changes: 6 additions & 1 deletion .env.staging
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,9 @@ VITE_MODE_LIB=true
# MyMaps / Draw
VITE_MYMAPS_URL="https://migration.geoportail.lu/mymaps"
VITE_ARROW_MODEL_URL="https://migration.geoportail.lu/static-ngeo/models/arrow5.glb"
VITE_ELEVATION_URL="/raster"
VITE_ELEVATION_URL="/raster"

# Auth
VITE_MYACCOUNT_URL="https://myaccount.geoportail.lu"
VITE_MYACCOUNT_RECOVER_URL="https://myaccount.geoportail.lu/recover-password"
VITE_MYACCOUNT_NEW_URL="https://myaccount.geoportail.lu/new-user"
39 changes: 39 additions & 0 deletions cypress/e2e/auth/auth.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
describe('Authentification', () => {
beforeEach(() => {
cy.visit('/')
})

describe('When user arrives on the page', () => {
it('the authentification icon is located in the header', () => {
cy.get('header [data-cy="authFormIcon"]').should('exist')
})
})

describe('When user clicks on the auth icon in the header', () => {
it('displays the authentification form correctly', () => {
cy.get('header [data-cy="authFormIcon"]').click()
cy.get('header [data-cy="authForm"]').should('exist')
cy.get('header [data-cy="authForm"] form input[name="userName"]').should(
'exist'
)
cy.get(
'header [data-cy="authForm"] form input[name="userPassword"]'
).should('exist')
cy.get(
'header [data-cy="authForm"] form a[data-cy="authFormLostPwd"]'
).should('contain.text', "J'ai perdu mon mot de passe")
cy.get(
'header [data-cy="authForm"] form a[data-cy="authFormNewAccount"]'
).should('contain.text', 'Créer un nouvel utilisateur')
})

describe('When user clicks again', () => {
it('hides the authentification form', () => {
cy.get('header [data-cy="authFormIcon"]').click()
cy.get('header [data-cy="authForm"]').should('exist')
cy.get('header [data-cy="authFormIcon"]').click()
cy.get('header [data-cy="authForm"]').should('not.be.visible')
})
})
})
})
4 changes: 2 additions & 2 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import { onMounted, onUnmounted, watch } from 'vue'
import { storeToRefs } from 'pinia'
import HeaderBar from './components/header/header-bar.vue'
import FooterBar from './components/footer/footer-bar.vue'
import HeaderBar from '@/components/header-bar/header-bar.vue'
import FooterBar from '@/components/footer/footer-bar.vue'
import AlertNotifications from '@/components/alert-notifications/alert-notifications.vue'
import RemoteLayers from '@/components/remote-layers/remote-layers.vue'
Expand Down
30 changes: 29 additions & 1 deletion src/assets/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
}

.lux-btn {
@apply bg-white border text-primary py-[6px] px-[12px] hover:bg-primary hover:text-white leading-snug focus:bg-[#e6e6e6] focus:border-[#8c8c8c] focus:[color:var(--color-primary)] focus:lux-outlined;
@apply cursor-pointer bg-white border text-primary py-[6px] px-[12px] hover:bg-primary hover:text-white leading-snug focus:bg-[#e6e6e6] focus:border-[#8c8c8c] focus:[color:var(--color-primary)] focus:lux-outlined;
border: 1px solid var(--color-gray);
}

Expand Down Expand Up @@ -202,6 +202,10 @@
[&.expanded]:after:content-['\e02c'];
}

.lux-navbar-dropdown-auth .lux-dropdown-btn {
@apply after:content-['\E02E'];
}

.lux-navbar-dropdown .lux-dropdown-wrapper {
@apply absolute w-full right-0;
}
Expand All @@ -224,6 +228,10 @@
@apply fixed w-56;
}

.lux-navbar-dropdown .lux-dropdown-content {
@apply bg-transparent border-none gap-[1px] shadow-none m-0 py-0 float-right relative left-auto;
}

.lux-slider-line {
@apply absolute h-full w-[4px] left-[50%] bg-primary ml-[-2px] cursor-ew-resize block top-0;
}
Expand Down Expand Up @@ -395,6 +403,26 @@
@apply text-white bg-primary cursor-default;
}

.lux-account-tab {
@apply ml-1 bg-primary text-white after:content-['\E02E'] after:font-icons after:text-3xl after:ml-4 w-20 px-2 pt-1;
}

.lux-account-content {
@apply bg-primary p-3;
}

.lux-account input[type='password'],
.lux-account input[type='text'] {
@apply border-none py-2;
background-color: rgba(46, 65, 78, 0.2);
-webkit-box-shadow: inset 0px 2px 4px -1px rgba(0, 57, 79, 0.25);
box-shadow: inset 0px 2px 4px -1px rgba(0, 57, 79, 0.25);
}

.lux-account ::placeholder {
@apply text-gray-400;
}

.form-control {
border: 1px solid var(--color-gray);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
Expand Down
2 changes: 1 addition & 1 deletion src/bundle/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import MapContainer from '@/components/map/map-container.vue'
import BackgroundSelector from '@/components/background-selector/background-selector.vue'
import RemoteLayers from '@/components/remote-layers/remote-layers.vue'
import LayerMetadata from '@/components/layer-metadata/layer-metadata.vue'
import HeaderBar from '@/components/header/header-bar.vue'
import HeaderBar from '@/components/header-bar/header-bar.vue'
import FooterBar from '@/components/footer/footer-bar.vue'
import ToolbarDraw from '@/components/footer/toolbar-draw.vue'
import LayerPanel from '@/components/layer-panel/layer-panel.vue'
Expand Down
129 changes: 129 additions & 0 deletions src/components/auth/auth-form.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useTranslation } from 'i18next-vue'
import { storeToRefs } from 'pinia'
import { useAppStore } from '@/stores/app.store'
const MYACCOUNT_URL = import.meta.env.VITE_MYACCOUNT_URL
const MYACCOUNT_RECOVER_URL = import.meta.env.VITE_MYACCOUNT_RECOVER_URL
const MYACCOUNT_NEW_URL = import.meta.env.VITE_MYACCOUNT_NEW_URL
const { t } = useTranslation()
const { lang } = storeToRefs(useAppStore())
const isAuthenticated = ref(false)
const email = ref('')
const userName = ref('')
function logout() {
alert('TODO: logout')
isAuthenticated.value = false
email.value = '' // TODO: remove when api ok
userName.value = '' // TODO: remove when api ok
}
function submit() {
alert('TODO: submit form')
isAuthenticated.value = true
email.value = 'random email (todo)' // TODO: remove when api ok
userName.value = 'random user name (todo)' // TODO: remove when api ok
}
</script>

<template>
<div data-cy="authForm" class="lux-account bg-secondary">
<h4
class="lux-panel-title pt-5 mb-5 pl-5 h-16 shrink-0 flex justify-between"
>
{{ t('My account') }}
</h4>

<fieldset>
<legend class="lux-account-tab"></legend>

<div class="lux-account-content">
<!-- When user is not authenticated -->
<template v-if="!isAuthenticated">
<form @submit.prevent="submit">
<div class="flex flex-col gap-2 mb-2">
<div>
<input
class="w-full lux-input h-11"
type="text"
name="userName"
v-model="userName"
:placeholder="t('Username')"
/>
</div>
<div>
<input
class="w-full lux-input h-11"
type="password"
name="userPassword"
:placeholder="t('Password')"
/>
</div>
</div>

<div class="flex mt-3 items-center">
<div class="grow leading-5">
<p>
<a
data-cy="authFormLostPwd"
class="text-secondary hover:underline"
target="_blank"
:href="MYACCOUNT_RECOVER_URL"
>{{ t('I lost my password') }}</a
>
</p>
<p>
<a
data-cy="authFormNewAccount"
class="text-secondary hover:underline"
target="_blank"
:href="MYACCOUNT_NEW_URL"
>{{ t('Create a new user account') }}</a
>
</p>
</div>
<div>
<input
class="lux-btn h-11 w-24 text-white placeholder:italic bg-secondary border-none uppercase hover:text-primary hover:bg-secondary"
type="submit"
:value="t('Submit')"
/>
</div>
</div>
</form>
</template>

<!--User is authenticated -->
<template v-else>
<div>
<div class="flex flex-col gap-1 mb-2 text-white">
<div class="bg-secondary p-1">{{ userName }}</div>
<div class="bg-secondary p-1">{{ email }}</div>
</div>
<div class="flex items-center gap-3 justify-end">
<div>
<a
class="text-secondary hover:underline"
target="_blank"
:href="`${MYACCOUNT_URL}?lang=${lang}`"
>{{ t('My account') }}</a
>
</div>
<div>
<button class="lux-btn" @click="logout">
{{ t('Logout') }}
</button>
</div>
</div>
</div>
</template>
</div>
</fieldset>
</div>
</template>
73 changes: 73 additions & 0 deletions src/components/common/dropdown-content.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { mount } from '@vue/test-utils'
import DropdownContent from './dropdown-content.vue'

describe('DropdownContent', () => {
it('renders the placeholder prop', () => {
const wrapper = mount(DropdownContent, {
props: { placeholder: 'Test Placeholder' },
})
expect(wrapper.find('span').text()).toBe('Test Placeholder')
})

it('toggles dropdown on button click', async () => {
const wrapper = mount(DropdownContent)
const button = wrapper.find('button')

expect(wrapper.find('.lux-dropdown-content').classes()).toContain('hidden')

await button.trigger('click')

expect(wrapper.find('.lux-dropdown-content').classes()).not.toContain(
'hidden'
)

await button.trigger('click')

expect(wrapper.find('.lux-dropdown-content').classes()).toContain('hidden')
})

it('opens dropdown when forceOpen is true', async () => {
const wrapper = mount(DropdownContent)
wrapper.vm.toggleDropdown(true)

await wrapper.vm.$nextTick()

expect(wrapper.find('.lux-dropdown-content').classes()).not.toContain(
'hidden'
)
})

it('closes dropdown when clicking outside', async () => {
const wrapper = mount(DropdownContent, {
props: { enableClickOutside: true },
})

wrapper.vm.toggleDropdown(true)

await wrapper.vm.$nextTick()

document.dispatchEvent(new MouseEvent('click'))

await wrapper.vm.$nextTick()

expect(wrapper.find('.lux-dropdown-content').classes()).toContain('hidden')
})

it('does not close dropdown when enableClickOutside is false', async () => {
const wrapper = mount(DropdownContent, {
props: { enableClickOutside: false },
})

wrapper.vm.toggleDropdown(true)

await wrapper.vm.$nextTick()

document.dispatchEvent(new MouseEvent('click'))

await wrapper.vm.$nextTick()

expect(wrapper.find('.lux-dropdown-content').classes()).not.toContain(
'hidden'
)
})
})
Loading

0 comments on commit 24874eb

Please sign in to comment.