Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { defaultPlugins, PartialComponentProps, shallowMount } from '@opencloud-eu/web-test-helpers'
import { nextTick } from 'vue'
import Breadcrumb from './OcBreadcrumb.vue'
import OcBreadcrumbItem from './OcBreadcrumbItem.vue'
import OcBreadcrumbContextMenu from './OcBreadcrumbContextMenu.vue'
Expand Down Expand Up @@ -56,6 +57,23 @@ describe('OcBreadcrumb', () => {
expect(wrapper.findComponent(OcBreadcrumbContextMenu).exists()).toBe(false)
})
})
describe('collapsed items', () => {
it('shows the full name as title on the truncated item text', async () => {
const longName = 'A very long folder name that does not fit into the drop'
const { wrapper } = getWrapper({
maxWidth: 50,
items: [
{ id: '1', text: 'Root', to: { path: '/root' } },
{ id: '2', text: longName, to: { path: '/root/long' } },
{ id: '3', text: 'Parent', to: { path: '/root/long/parent' } },
{ id: '4', text: 'Current', to: { path: '/root/long/parent/current' } }
]
})
await nextTick()
const texts = wrapper.findAll('.oc-breadcrumb-truncation-item-text')
expect(texts.map((text) => text.attributes('title'))).toEqual([longName, 'Parent'])
})
})
describe('mobile navigation', () => {
it.each([
{ items: [], shows: false },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@
size-class="size-4"
/>
<oc-icon v-else name="folder" class="align-middle" fill-type="line" />
<span>{{ truncationItem.text }}</span>
<span
class="oc-breadcrumb-truncation-item-text truncate"
:title="truncationItem.text"
v-text="truncationItem.text"
/>
</oc-button>
</div>
</oc-drop>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ describe('OcBreadcrumbItem', () => {
expect(wrapper.find('[aria-current="page"]').exists()).toBe(false)
expect(wrapper.find('.font-bold').exists()).toBe(false)
})
it('shows the full item text as title, so truncated names stay readable', () => {
const text = 'A very long folder name that gets truncated in the breadcrumb'
const { wrapper } = getWrapper({
item: { text, to: { path: 'folder' } },
textClass: 'truncate'
})
expect(wrapper.find('.truncate').attributes('title')).toBe(text)
})
it('renders item icons passed via props', () => {
const { wrapper } = getWrapper({
item: { text: 'Vault', icon: 'resource-type-vault', iconAccessibleLabel: 'Encrypted vault' }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
:aria-current="current ? 'page' : null"
:class="{ 'text-role-on-surface': !item.onClick, 'font-bold': current && isInteractive }"
>
<span :class="[textClass, { 'hover:underline': isInteractive }]">
<span :class="[textClass, { 'hover:underline': isInteractive }]" :title="item.text">
<oc-icon
v-if="item.icon"
:icon="item.icon"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const { items } = defineProps<Props>()

@layer components {
.details-list dd {
@apply font-semibold mb-2 last-of-type:mb-0;
@apply font-semibold mb-2 last-of-type:mb-0 wrap-anywhere;
}

.details-list dt {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ describe('OcMobileDrop', () => {
expect(label).toBe('Close Bottom Drawer')
})

it('shows the full drawer title as native title on the truncated header', async () => {
const { wrapper } = getWrapper()
await wrapper.find(selectors.toggle).trigger('click')
const header = wrapper.find('.oc-mobile-drop span.truncate')
const title = header.attributes('title')
wrapper.unmount()
expect(title).toBe('Bottom Drawer')
})

it('does not render when toggle is not clicked', async () => {
const { wrapper } = getWrapper()
await nextTick()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
>
<oc-icon name="arrow-left" fill-type="line" />
</oc-button>
<span class="font-semibold truncate" v-text="title" />
<span class="font-semibold truncate" :title="title" v-text="title" />
<oc-button
appearance="raw"
class="raw-hover-surface oc-bottom-drawer-close-button"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ describe('OcFilterChip', () => {
const { wrapper } = getWrapper({ props: { selectedItemNames } })
expect(wrapper.find(selectors.filterChipLabel).text()).toEqual(selectedItemNames[0])
})
it('shows the full first selected item name as title on the truncated label', () => {
const selectedItemNames = ['A very long tag name that gets truncated', 'Marie']
const { wrapper } = getWrapper({ props: { selectedItemNames } })
expect(wrapper.find(selectors.filterChipLabel).attributes('title')).toEqual(
selectedItemNames[0]
)
})
it('does not set a title on the static filter label', () => {
const { wrapper } = getWrapper()
expect(wrapper.find(selectors.filterChipLabel).attributes('title')).toBeUndefined()
})
it('emits the "clearFilter"-event when clicking the clear-button', async () => {
const { wrapper } = getWrapper({ props: { selectedItemNames: ['Einstein', 'Marie'] } })
await wrapper.find(selectors.clearBtn).trigger('click')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
/>
<span
class="truncate oc-filter-chip-label"
:title="selectedItemNames[0]"
v-text="!!selectedItemNames.length ? selectedItemNames[0] : filterLabel"
/>
<span v-if="selectedItemNames.length > 1" v-text="` +${selectedItemNames.length - 1}`" />
Expand Down
10 changes: 10 additions & 0 deletions packages/design-system/src/components/OcModal/OcModal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ describe('OcModal', () => {
expect(wrapper.html()).toMatchSnapshot()
})

it('shows the full title as native title on the truncated heading', () => {
const title = 'Rename folder »A very long folder name that gets truncated«'
const wrapper = shallowMount(Modal, {
global: { renderStubDefaultSlot: true, plugins: [...defaultPlugins()] },
props: { ...defaultProps, title }
})

expect(wrapper.find('.oc-modal-title h2').attributes('title')).toBe(title)
})

it('only dims the background while it is the topmost modal', () => {
const backdrop = (active: boolean) =>
shallowMount(Modal, {
Expand Down
2 changes: 1 addition & 1 deletion packages/design-system/src/components/OcModal/OcModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<div
class="oc-modal-title shrink-0 flex items-center flex-row justify-between py-3 px-4 rounded-t-xl"
>
<h2 :id="titleId" class="truncate m-0 text-base" v-text="title" />
<h2 :id="titleId" class="truncate m-0 text-base" :title="title" v-text="title" />
<div class="flex items-center gap-1">
<slot name="headerActions"></slot>
<oc-button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ exports[`OcModal > displays input 1`] = `
<focus-trap-stub tabbableoptions="[object Object]" active="true" escapedeactivates="true" returnfocusondeactivate="true" allowoutsideclick="true" clickoutsidedeactivates="false" initialfocus="[Function]" delayinitialfocus="true" preventscroll="false" setreturnfocus="false">
<div class="oc-modal bg-role-surface z-[calc(var(--z-index-modal)+1)] rounded-xl w-full max-w-xl max-h-[90vh] flex flex-col overflow-hidden shadow-2xl border border-role-surface-container-highest" role="dialog" aria-modal="true" aria-labelledby="oc-modal-title-v-0">
<div class="oc-modal-title shrink-0 flex items-center flex-row justify-between py-3 px-4 rounded-t-xl">
<h2 id="oc-modal-title-v-0" class="truncate m-0 text-base">Create new folder</h2>
<h2 id="oc-modal-title-v-0" class="truncate m-0 text-base" title="Create new folder">Create new folder</h2>
<div class="flex items-center gap-1">
<oc-button-stub arialabel="Cancel" appearance="raw" colorrole="secondary" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" submit="button" type="button" nohover="false" class="oc-modal-title-actions-cancel">
<oc-icon-stub accessiblelabel="" color="" filltype="fill" name="close" sizeclass="size-5" type="span" uniqueids="false"></oc-icon-stub>
Expand Down Expand Up @@ -34,7 +34,7 @@ exports[`OcModal > displays loading state 1`] = `
<focus-trap-stub tabbableoptions="[object Object]" active="true" escapedeactivates="true" returnfocusondeactivate="true" allowoutsideclick="true" clickoutsidedeactivates="false" initialfocus="[Function]" delayinitialfocus="true" preventscroll="false" setreturnfocus="false">
<div class="oc-modal bg-role-surface z-[calc(var(--z-index-modal)+1)] rounded-xl w-full max-w-xl max-h-[90vh] flex flex-col overflow-hidden shadow-2xl border border-role-surface-container-highest" role="dialog" aria-modal="true" aria-labelledby="oc-modal-title-v-0">
<div class="oc-modal-title shrink-0 flex items-center flex-row justify-between py-3 px-4 rounded-t-xl">
<h2 id="oc-modal-title-v-0" class="truncate m-0 text-base">Example title</h2>
<h2 id="oc-modal-title-v-0" class="truncate m-0 text-base" title="Example title">Example title</h2>
<div class="flex items-center gap-1"> <button type="button" disabled="" aria-label="Cancel" class="oc-button-secondary oc-button-raw oc-button-secondary-raw gap-2 justify-center text-base min-h-4 oc-button cursor-pointer disabled:opacity-60 disabled:cursor-default oc-modal-title-actions-cancel">
<!--v-if-->
<!-- @slot Content of the button --> <span class="oc-icon box-content inline-block align-baseline [&amp;_svg]:block size-5"><svg data-testid="inline-svg-stub" src="icons/close-fill.svg" unique-ids="false" transform-source="(svg) => {
Expand Down Expand Up @@ -69,7 +69,7 @@ exports[`OcModal > matches snapshot 1`] = `
<focus-trap-stub tabbableoptions="[object Object]" active="true" escapedeactivates="true" returnfocusondeactivate="true" allowoutsideclick="true" clickoutsidedeactivates="false" initialfocus="[Function]" delayinitialfocus="true" preventscroll="false" setreturnfocus="false">
<div class="oc-modal bg-role-surface z-[calc(var(--z-index-modal)+1)] rounded-xl w-full max-w-xl max-h-[90vh] flex flex-col overflow-hidden shadow-2xl border border-role-surface-container-highest" role="dialog" aria-modal="true" aria-labelledby="oc-modal-title-v-0">
<div class="oc-modal-title shrink-0 flex items-center flex-row justify-between py-3 px-4 rounded-t-xl">
<h2 id="oc-modal-title-v-0" class="truncate m-0 text-base">Example title</h2>
<h2 id="oc-modal-title-v-0" class="truncate m-0 text-base" title="Example title">Example title</h2>
<div class="flex items-center gap-1">
<oc-button-stub arialabel="Cancel" appearance="raw" colorrole="secondary" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" submit="button" type="button" nohover="false" class="oc-modal-title-actions-cancel">
<oc-icon-stub accessiblelabel="" color="" filltype="fill" name="close" sizeclass="size-5" type="span" uniqueids="false"></oc-icon-stub>
Expand Down Expand Up @@ -98,7 +98,7 @@ exports[`OcModal > overrides props message with slot 1`] = `
<focus-trap-stub tabbableoptions="[object Object]" active="true" escapedeactivates="true" returnfocusondeactivate="true" allowoutsideclick="true" clickoutsidedeactivates="false" initialfocus="[Function]" delayinitialfocus="true" preventscroll="false" setreturnfocus="false">
<div class="oc-modal bg-role-surface z-[calc(var(--z-index-modal)+1)] rounded-xl w-full max-w-xl max-h-[90vh] flex flex-col overflow-hidden shadow-2xl border border-role-surface-container-highest" role="dialog" aria-modal="true" aria-labelledby="oc-modal-title-v-0">
<div class="oc-modal-title shrink-0 flex items-center flex-row justify-between py-3 px-4 rounded-t-xl">
<h2 id="oc-modal-title-v-0" class="truncate m-0 text-base">Example title</h2>
<h2 id="oc-modal-title-v-0" class="truncate m-0 text-base" title="Example title">Example title</h2>
<div class="flex items-center gap-1">
<oc-button-stub arialabel="Cancel" appearance="raw" colorrole="secondary" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" submit="button" type="button" nohover="false" class="oc-modal-title-actions-cancel">
<oc-icon-stub accessiblelabel="" color="" filltype="fill" name="close" sizeclass="size-5" type="span" uniqueids="false"></oc-icon-stub>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,12 @@
:user-name="rowData.item.displayName"
background-color="var(--oc-role-secondary)"
/>
<oc-filter-highlight class="truncate" :text="rowData.item.displayName" :term="filterTerm" />
<oc-filter-highlight
class="truncate"
:title="rowData.item.displayName"
:text="rowData.item.displayName"
:term="filterTerm"
/>
<oc-icon
v-if="rowData.item.groupTypes?.includes('ReadOnly')"
v-oc-tooltip="readOnlyLabel"
Expand Down
1 change: 1 addition & 0 deletions packages/web-app-admin-settings/src/views/Groups.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
/>
<h2
class="m-0 text-base font-semibold min-w-0 flex-1 truncate"
:title="selectedGroups[0].displayName"
v-text="selectedGroups[0].displayName"
/>
</div>
Expand Down
1 change: 1 addition & 0 deletions packages/web-app-admin-settings/src/views/Users.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
/>
<h2
class="m-0 text-base font-semibold min-w-0 flex-1 truncate"
:title="selectedUsers[0].displayName"
v-text="selectedUsers[0].displayName"
/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion packages/web-app-app-store/src/components/AppAuthors.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<ul class="inline-flex flex-wrap justify-end gap-x-1 m-0 p-0">
<ul class="inline-flex flex-wrap justify-end gap-x-1 m-0 p-0 wrap-anywhere">
<li v-for="(author, index) in authors" :key="author.name" class="app-author-item">
<a
v-if="author.url"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@
aria-live="polite"
aria-atomic="true"
>
<span class="app-details-gallery-caption truncate" v-text="currentImage?.caption" />
<span
class="app-details-gallery-caption truncate"
:title="currentImage?.caption"
v-text="currentImage?.caption"
/>
<span
v-if="hasMultipleImages"
class="app-details-gallery-counter shrink-0"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div class="app-details-header flex flex-col md:flex-row md:items-start md:justify-between gap-4">
<div class="flex flex-col gap-1 min-w-0">
<div class="flex flex-col gap-1 min-w-0 wrap-anywhere">
<h1 class="app-details-title my-0 text-2xl font-bold">{{ app.name }}</h1>
<p class="app-details-subtitle my-0 text-base" v-text="app.subtitle" />
<p
Expand Down
7 changes: 6 additions & 1 deletion packages/web-app-app-store/src/components/AppResources.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@
size-class="size-5"
class="mr-1"
/>
<span data-testid="resource-label" class="truncate">{{ resource.label }}</span>
<span
data-testid="resource-label"
class="truncate"
:title="resource.label"
v-text="resource.label"
/>
</span>
<oc-icon name="external-link" fill-type="line" size-class="size-4" class="shrink-0" />
</a>
Expand Down
5 changes: 3 additions & 2 deletions packages/web-app-app-store/src/components/AppTagFilter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,13 @@
data-testid="tag-filter-hidden-button"
type="button"
rounded
class="whitespace-nowrap cursor-pointer"
class="max-w-full whitespace-nowrap cursor-pointer"
:appearance="isActive(tag.name) ? 'filled' : 'outline'"
:aria-pressed="isActive(tag.name)"
:title="tag.name"
@click="toggleTag(tag.name)"
>
{{ tag.name }}
<span class="truncate" v-text="tag.name" />
<app-tag-count :count="tag.count" :active="isActive(tag.name)" />
</oc-tag>
</div>
Expand Down
5 changes: 3 additions & 2 deletions packages/web-app-app-store/src/components/AppTags.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
data-testid="tag-button"
rounded
size="small"
class="relative whitespace-nowrap cursor-pointer"
class="relative max-w-full whitespace-nowrap cursor-pointer"
type="button"
:title="tag"
@click="emit('click', tag)"
>
<oc-filter-highlight :text="tag" :term="term" />
<oc-filter-highlight class="truncate" :text="tag" :term="term" />
</oc-tag>
</div>
</template>
Expand Down
6 changes: 5 additions & 1 deletion packages/web-app-app-store/src/components/AppTitle.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
<div class="flex items-center min-w-0">
<h3 class="min-w-0" :class="titleClass">
<!-- the link covers the whole surrounding item, interactive elements need to be positioned on top -->
<router-link :to="getAppDetailsRoute(app)" class="block after:absolute after:inset-0">
<router-link
:to="getAppDetailsRoute(app)"
class="block after:absolute after:inset-0"
:title="app.name"
>
<oc-filter-highlight class="block" :class="nameClass" :text="app.name" :term="term" />
</router-link>
</h3>
Expand Down
2 changes: 1 addition & 1 deletion packages/web-app-app-store/src/views/AppList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<div v-if="filteredApps.length" class="flex items-center justify-between gap-4 mb-2">
<div class="flex items-center gap-1">
<h2
class="app-list-results-title my-0 text-base capitalize"
class="app-list-results-title my-0 text-base capitalize wrap-anywhere"
v-text="`${resultsTitle} (${filteredApps.length})`"
/>
<oc-filter-chip
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ describe('AppDetailsGallery', () => {
await wrapper.findAll(selectors.thumbnail)[1].trigger('click')
expect(wrapper.find(selectors.image).attributes('src')).toBe(screenshots[0].url)
expect(wrapper.find(selectors.caption).text()).toBe('First screenshot')
expect(wrapper.find(selectors.caption).attributes('title')).toBe('First screenshot')
expect(wrapper.find(selectors.counter).text()).toBe('2 / 3')
expect(wrapper.findAll(selectors.thumbnail)[1].attributes('aria-pressed')).toBe('true')
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ describe('AppResources.vue', () => {
expect(link1.exists()).toBeTruthy()
expect(link1.attributes().href).toBe(resource1.url)
expect(link1.find(selectors.label).text()).toBe(resource1.label)
expect(link1.find(selectors.label).attributes('title')).toBe(resource1.label)
expect(link1.find(selectors.icon).exists()).toBeTruthy()
if (link1.find(selectors.icon).exists()) {
expect(link1.find(selectors.icon).attributes().name).toBe(resource1.icon)
Expand Down
29 changes: 29 additions & 0 deletions packages/web-app-app-store/tests/unit/components/AppTitle.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import AppTitle from '../../../src/components/AppTitle.vue'
import { mount, RouterLinkStub } from '@opencloud-eu/web-test-helpers'
import { mock } from 'vitest-mock-extended'
import { App } from '../../../src/types'

describe('AppTitle.vue', () => {
it('shows the full app name as title on the link that covers the item', () => {
const { wrapper } = getWrapper()
expect(wrapper.findComponent(RouterLinkStub).attributes('title')).toBe(
'An app with a very long name'
)
})
})

function getWrapper() {
const app = {
...mock<App>({}),
id: 'app',
name: 'An app with a very long name',
mostRecentVersion: { version: '1.0.0' }
} as App

return {
wrapper: mount(AppTitle, {
props: { app },
global: { stubs: { RouterLink: RouterLinkStub } }
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@
<div class="flex items-center justify-between w-full">
<div class="flex items-center truncate">
<oc-icon name="folder" class="mr-2" fill-type="line" />
<span class="truncate font-bold" v-text="addressBook.name" />
<span
class="truncate font-bold"
:title="addressBook.name"
v-text="addressBook.name"
/>
</div>
</div>
</oc-button>
Expand Down
7 changes: 5 additions & 2 deletions packages/web-app-contacts/src/components/ContactsListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
<div class="contacts-list-item flex min-w-0 w-full items-center gap-3">
<oc-avatar :user-name="getContactDisplayName(contact)" />
<div class="flex-1 min-w-0">
<div class="truncate font-bold text-lg">
<div class="truncate font-bold text-lg" :title="getContactDisplayName(contact)">
<oc-filter-highlight :text="getContactDisplayName(contact)" :term="term" />
</div>
<div class="truncate text-sm text-role-on-surface-variant">
<div
class="truncate text-sm text-role-on-surface-variant"
:title="getContactPrimaryEmail(contact)"
>
<oc-filter-highlight :text="getContactPrimaryEmail(contact)" :term="term" />
</div>
</div>
Expand Down
Loading
Loading