Skip to content

Commit

Permalink
Add login button (#9178)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jan authored Jun 7, 2023
1 parent ccde466 commit 46b36ad
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 70 deletions.
7 changes: 7 additions & 0 deletions changelog/unreleased/enhancement-add-login-button-to-top-bar
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Enhancement: Add login button to top bar

We've added a login button to the top bar, this might be handy if a user receives a public link,
and they want to login with their user account.

https://github.com/owncloud/web/pull/9178
https://github.com/owncloud/web/issues/9177
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export default defineComponent({
width: var(--width);
.oc-icon > svg {
fill: var(--icon-color);
fill: var(--icon-color) !important;
}
}
</style>
Expand Down
6 changes: 1 addition & 5 deletions packages/web-runtime/src/components/Topbar/TopBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
</portal>
<portal to="app.runtime.header.right" :order="100">
<notifications v-if="isNotificationBellEnabled" />
<user-menu v-if="isUserMenuEnabled" :applications-list="userMenuItems" />
<user-menu :applications-list="userMenuItems" />
</portal>
</header>
</template>
Expand Down Expand Up @@ -185,10 +185,6 @@ export default {
...(feedback.ariaLabel && { ariaLabel: feedback.ariaLabel }),
...(feedback.description && { description: feedback.description })
}
},
isUserMenuEnabled() {
return this.user?.id
}
}
}
Expand Down
140 changes: 90 additions & 50 deletions packages/web-runtime/src/components/Topbar/UserMenu.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<nav v-if="userId" :aria-label="$gettext('Account menu')">
<nav :aria-label="$gettext('Account menu')">
<oc-button
id="_userMenuButton"
ref="menuButton"
Expand All @@ -8,11 +8,22 @@
:aria-label="$gettext('User Menu')"
>
<avatar-image
v-if="userId"
class="oc-topbar-personal-avatar oc-flex-inline oc-flex-center oc-flex-middle"
:width="32"
:userid="userId"
:user-name="user.displayname"
/>
<oc-avatar-item
v-else
class="oc-topbar-unauthenticated-avatar oc-flex-inline oc-flex-center oc-flex-middle"
:name="$gettext('User Menu login')"
:width="32"
icon="user-add"
icon-fill-type="line"
icon-color="var(--oc-color-icon-text)"
background="var(--oc-color-swatch-brand-contrast)"
/>
</oc-button>
<oc-drop
ref="menu"
Expand All @@ -23,64 +34,85 @@
padding-size="small"
>
<oc-list class="user-menu-list">
<li>
<oc-button
id="oc-topbar-account-manage"
type="router-link"
:to="{ path: '/account' }"
appearance="raw"
>
<avatar-image :width="32" :userid="userId" :user-name="user.displayname" />
<span class="profile-info-wrapper" :class="{ 'oc-py-xs': !user.email }">
<span class="oc-display-block" v-text="user.displayname" />
<span v-if="user.email" class="oc-text-small" v-text="user.email" />
</span>
</oc-button>
</li>
<li v-for="(app, index) in applicationsList" :key="`user-menu-${index}`">
<oc-button v-if="app.url" type="a" appearance="raw" :target="app.target" :href="app.url">
<oc-icon :name="app.icon" class="oc-p-xs" />
<span v-text="$gettext(app.title)" />
</oc-button>
<oc-button v-else type="router-link" appearance="raw" :to="{ path: app.path }">
<oc-icon :name="app.icon" class="oc-p-xs" />
<span v-text="$gettext(app.title)" />
</oc-button>
</li>
<li>
<oc-button id="oc-topbar-account-logout" appearance="raw" @click="logout">
<oc-icon name="logout-box-r" fill-type="line" class="oc-p-xs" />
<span v-text="$gettext('Log out')" />
</oc-button>
</li>
<li v-if="quotaEnabled" class="storage-wrapper oc-pl-s">
<oc-icon name="cloud" fill-type="line" class="oc-p-xs" />
<div class="storage-wrapper-text">
<p class="oc-my-rm">
<span class="oc-display-block" v-text="personalStorageLabel" />
<span class="oc-text-small" v-text="personalStorageDetailsLabel" />
</p>
<oc-progress
v-if="limitedPersonalStorage"
:value="quotaUsagePercent"
:max="100"
size="small"
:variation="quotaProgressVariant"
/>
</div>
</li>
<template v-if="!userId">
<li>
<oc-button
id="oc-topbar-account-login"
appearance="raw"
type="router-link"
:to="loginLink"
>
<oc-icon name="user-add" fill-type="line" class="oc-p-xs" />
<span v-text="$gettext('Log in')" />
</oc-button>
</li>
</template>
<template v-else>
<li>
<oc-button
id="oc-topbar-account-manage"
type="router-link"
:to="{ path: '/account' }"
appearance="raw"
>
<avatar-image :width="32" :userid="userId" :user-name="user.displayname" />
<span class="profile-info-wrapper" :class="{ 'oc-py-xs': !user.email }">
<span class="oc-display-block" v-text="user.displayname" />
<span v-if="user.email" class="oc-text-small" v-text="user.email" />
</span>
</oc-button>
</li>
<li v-for="(app, index) in applicationsList" :key="`user-menu-${index}`">
<oc-button
v-if="app.url"
type="a"
appearance="raw"
:target="app.target"
:href="app.url"
>
<oc-icon :name="app.icon" class="oc-p-xs" />
<span v-text="$gettext(app.title)" />
</oc-button>
<oc-button v-else type="router-link" appearance="raw" :to="{ path: app.path }">
<oc-icon :name="app.icon" class="oc-p-xs" />
<span v-text="$gettext(app.title)" />
</oc-button>
</li>
<li>
<oc-button id="oc-topbar-account-logout" appearance="raw" @click="logout">
<oc-icon name="logout-box-r" fill-type="line" class="oc-p-xs" />
<span v-text="$gettext('Log out')" />
</oc-button>
</li>
<li v-if="quotaEnabled" class="storage-wrapper oc-pl-s">
<oc-icon name="cloud" fill-type="line" class="oc-p-xs" />
<div class="storage-wrapper-text">
<p class="oc-my-rm">
<span class="oc-display-block" v-text="personalStorageLabel" />
<span class="oc-text-small" v-text="personalStorageDetailsLabel" />
</p>
<oc-progress
v-if="limitedPersonalStorage"
:value="quotaUsagePercent"
:max="100"
size="small"
:variation="quotaProgressVariant"
/>
</div>
</li>
</template>
</oc-list>
</oc-drop>
</nav>
</template>

<script lang="ts">
import { defineComponent, PropType, ComponentPublicInstance } from 'vue'
import { defineComponent, PropType, ComponentPublicInstance, computed, unref } from 'vue'
import { mapGetters, mapState } from 'vuex'
import filesize from 'filesize'
import isNil from 'lodash-es/isNil'
import { authService } from '../../services/auth'
import { useCapabilitySpacesEnabled } from 'web-pkg/src/composables'
import { useCapabilitySpacesEnabled, useRoute } from 'web-pkg/src/composables'
import { OcDrop } from 'design-system/src/components'
export default defineComponent({
Expand All @@ -92,8 +124,16 @@ export default defineComponent({
}
},
setup() {
const route = useRoute()
const loginLink = computed(() => {
return {
name: 'login',
query: { redirectUrl: unref(route).fullPath }
}
})
return {
hasSpaces: useCapabilitySpacesEnabled()
hasSpaces: useCapabilitySpacesEnabled(),
loginLink
}
},
computed: {
Expand Down
37 changes: 28 additions & 9 deletions packages/web-runtime/tests/unit/components/Topbar/UserMenu.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import {
defaultPlugins,
defaultStubs,
mount,
defaultStoreMockOptions
defaultStoreMockOptions,
defaultComponentMocks,
RouteLocation
} from 'web-test-helpers'
import { mock } from 'jest-mock-extended'

const totalQuota = 1000
const basicQuota = 300
Expand All @@ -20,6 +23,12 @@ const noEmail = ''
const email = 'test@test.de'

describe('User Menu component', () => {
describe('when user is not logged in', () => {
it('renders a navigation with login button', () => {
const wrapper = getMountedWrapper({}, noEmail, true)
expect(wrapper.html()).toMatchSnapshot()
})
})
describe('when quota and no email is set', () => {
it('renders a navigation without email', () => {
const wrapper = getMountedWrapper(
Expand Down Expand Up @@ -111,15 +120,24 @@ describe('User Menu component', () => {
})
})

const getMountedWrapper = (quota, userEmail) => {
const getMountedWrapper = (quota, userEmail, noUser = false) => {
const mocks = {
...defaultComponentMocks({
currentRoute: mock<RouteLocation>({ path: '/files', fullPath: '/files' })
})
}
const storeOptions = defaultStoreMockOptions
storeOptions.getters.quota.mockImplementation(() => quota)
storeOptions.getters.user.mockImplementation(() => ({
id: 'einstein',
username: 'einstein',
userDisplayName: 'Albert Einstein',
userEmail
}))
storeOptions.getters.user.mockImplementation(() => {
return noUser
? {}
: {
id: 'einstein',
username: 'einstein',
userDisplayName: 'Albert Einstein',
userEmail
}
})
const store = createStore(storeOptions)
return mount(UserMenu, {
props: {
Expand All @@ -142,7 +160,8 @@ const getMountedWrapper = (quota, userEmail) => {
'avatar-image': true,
'oc-icon': true,
'oc-progress': true
}
},
mocks
}
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -325,3 +325,25 @@ exports[`User Menu component when quota is unlimited renders no percentag of tot
</oc-drop-stub>
</nav>
`;

exports[`User Menu component when user is not logged in renders a navigation with login button 1`] = `
<nav aria-label="Account menu">
<oc-button-stub appearance="raw" aria-label="User Menu" class="oc-topbar-personal" id="_userMenuButton">
<div aria-hidden="true" class="oc-topbar-unauthenticated-avatar oc-flex-inline oc-flex-center oc-flex-middle" data-test-item-name="User Menu login" focusable="false">
<span class="oc-avatar-item" style="--icon-color: var(--oc-color-icon-text); --width: 32px;">
<oc-icon-stub accessiblelabel="" color="" filltype="line" name="user-add" size="small" type="span" variation="passive"></oc-icon-stub>
</span>
</div>
</oc-button-stub>
<oc-drop-stub close-on-click="" drop-id="account-info-container" mode="click" padding-size="small" toggle="#_userMenuButton">
<oc-list-stub class="user-menu-list">
<li>
<oc-button-stub appearance="raw" id="oc-topbar-account-login" to="[object Object]" type="router-link">
<oc-icon-stub class="oc-p-xs" fill-type="line" name="user-add"></oc-icon-stub>
<span>Log in</span>
</oc-button-stub>
</li>
</oc-list-stub>
</oc-drop-stub>
</nav>
`;
2 changes: 0 additions & 2 deletions tests/e2e/cucumber/features/smoke/link.feature
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ Feature: link
| new-lorem.txt | test.txt |
And "Alice" removes the public link named "myPublicLink" of resource "folderPublic"
And "Anonymous" should not be able to open the old link "myPublicLink"
And "Anonymous" logs out
And "Alice" logs out


Expand All @@ -64,5 +63,4 @@ Feature: link
And "Anonymous" downloads the following public link resources using the single share view
| resource | type |
| lorem.txt | file |
And "Anonymous" logs out
And "Alice" logs out
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ Feature: spaces participant management
When "Brian" deletes the following resources using the sidebar panel
| resource | from |
| textfile.txt | parent |
And "Anonymous" logs out
When "Carol" navigates to the trashbin of the project space "team.1"
Then "Carol" should not be able to delete following resources from the trashbin
| resource |
Expand Down
2 changes: 0 additions & 2 deletions tests/e2e/cucumber/features/smoke/spaces/project.ocis.feature
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,6 @@ Feature: spaces.personal
| resource |
| textfile.txt |

# anonymous is done
And "Anonymous" logs out

Scenario: members of the space can control the versions of the files
Given "Admin" creates following users using API
Expand Down

0 comments on commit 46b36ad

Please sign in to comment.