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

[full-ci] Implement long breadcrumb strategy #8984

Merged
merged 52 commits into from
May 24, 2023
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
ea2772e
Implement long breadcrumb strategy
lookacat May 4, 2023
2271aad
WIP
lookacat May 5, 2023
1577516
Bugfix use items in template, not visibleitems
lookacat May 8, 2023
446415a
WIP
lookacat May 8, 2023
c779d88
WIP
lookacat May 8, 2023
9ce8aa7
WIP
lookacat May 8, 2023
66cbe81
Fix ..., Fix first folder not showing up
lookacat May 9, 2023
519019a
Rename itemsWithDropdown
lookacat May 9, 2023
0aa705e
extract max breadcrumb width logic from OcBreadcrumb
lookacat May 9, 2023
995200e
Refactor code
lookacat May 9, 2023
16c30e9
Fix unreadable classes
lookacat May 9, 2023
941d91d
Fix long names
lookacat May 9, 2023
1bef832
Fix small screen size bug
lookacat May 9, 2023
8241c3f
dont use text as identifier in if statements ...
lookacat May 9, 2023
6aaa94a
remove console logs
lookacat May 9, 2023
6e65969
use const instead of let
lookacat May 9, 2023
274176b
remove dev styling
lookacat May 9, 2023
89d03a6
Update snapshots
lookacat May 9, 2023
8f92da3
Fix undefined error in unittests
lookacat May 9, 2023
cbce627
Fix undefined unittest error
lookacat May 9, 2023
27a1d67
Fix `...` item
lookacat May 10, 2023
33376a3
Add resize observer throttling to improve performance on resize
lookacat May 10, 2023
5d78a13
Fix ...
lookacat May 10, 2023
30f3049
Prevent breadcrumb from not displaying the current folder
lookacat May 10, 2023
cf0b0f8
reduce throttle
lookacat May 10, 2023
2afcfbb
Add prop description
lookacat May 10, 2023
1223f89
Fix bug build:w not working
lookacat May 10, 2023
bf85e0a
remove hacky css
lookacat May 10, 2023
a4c9a96
Update tests, refactor logic
lookacat May 10, 2023
01a7fd2
Fix PR issues
lookacat May 15, 2023
88f377a
Extract parent selector, remove dev leftover
lookacat May 15, 2023
4f96701
Rename allItemsIncludingThreeDots
lookacat May 15, 2023
43bbb52
Use oc-hidden instead of hide
lookacat May 15, 2023
3067939
Update prop descriptions
lookacat May 15, 2023
146392e
add window resize event listener again, because without it it causes …
lookacat May 15, 2023
530b04a
Add changelog
lookacat May 15, 2023
a00bdb9
Remove double arrow default value
lookacat May 15, 2023
ff72801
Small code slimming fix
lookacat May 16, 2023
5114193
Refactor maxWidth
lookacat May 17, 2023
4d36386
Update snapshot
lookacat May 17, 2023
6eae49f
Fix breadcrumb cut to early
lookacat May 17, 2023
3d0659c
Fix breadcrumbMaxSize calc, Address PR issues
lookacat May 19, 2023
7a6c78c
Fix console error
lookacat May 19, 2023
f061d88
Fix unittests
lookacat May 19, 2023
74acc72
Fix span positioning
lookacat May 19, 2023
80a5a65
Address current PR issues
lookacat May 23, 2023
3d2c44a
Update snapshot
lookacat May 23, 2023
8737838
Address PR issues
lookacat May 23, 2023
c7bd0b1
Address PR issues
lookacat May 24, 2023
4726512
Address PR issue
lookacat May 24, 2023
b1dd5c7
React on code review
May 24, 2023
70bc349
fix: breadcrumb truncation in project spaces, linting, unit tests
kulmann May 24, 2023
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
6 changes: 6 additions & 0 deletions changelog/unreleased/enhancement-long-breadcrumb-strategy
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Enhancement: Long breadcrumb strategy

We've implemented a new solution to deal with long breadcrumbs even with long folder names.

https://github.com/owncloud/web/pull/8984
https://github.com/owncloud/web/issues/6731
157 changes: 144 additions & 13 deletions packages/design-system/src/components/OcBreadcrumb/OcBreadcrumb.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,27 @@
<nav :id="id" :class="`oc-breadcrumb oc-breadcrumb-${variation}`">
<ol class="oc-breadcrumb-list oc-flex oc-m-rm oc-p-rm">
<li
v-for="(item, index) in items"
v-for="(item, index) in displayItems"
:key="index"
class="oc-breadcrumb-list-item oc-flex oc-flex-middle"
:data-key="index"
:data-item-id="item.id"
:class="[
'oc-breadcrumb-list-item',
'oc-flex',
'oc-flex-middle',
{
'oc-hidden':
lookacat marked this conversation as resolved.
Show resolved Hide resolved
hiddenItems.indexOf(item) !== -1 ||
(item.isPreviousHiddenFolder && hiddenItems.length === 0)
}
]"
>
<router-link v-if="item.to" :aria-current="getAriaCurrent(index)" :to="item.to">
<span>{{ item.text }}</span>
<router-link
v-if="item.to"
:aria-current="getAriaCurrent(index)"
:to="item.isPreviousHiddenFolder ? lastHiddenItem.to : item.to"
>
<span class="oc-breadcrumb-item-text">{{ item.text }}</span>
</router-link>
<oc-icon
v-if="item.to"
Expand All @@ -20,12 +35,27 @@
v-else-if="item.onClick"
:aria-current="getAriaCurrent(index)"
appearance="raw"
class="oc-flex"
@click="item.onClick"
>
<span>{{ item.text }}</span>
<span
:class="[
'oc-breadcrumb-item-text',
{
'oc-breadcrumb-item-text-last': index === displayItems.length - 1
}
]"
v-text="item.text"
/>
</oc-button>
<span v-else :aria-current="getAriaCurrent(index)" tabindex="-1" v-text="item.text" />
<template v-if="showContextActions && index === items.length - 1">
<span
class="oc-breadcrumb-item-text"
v-else
:aria-current="getAriaCurrent(index)"
tabindex="-1"
v-text="item.text"
/>
<template v-if="showContextActions && index === displayItems.length - 1">
<oc-button
id="oc-breadcrumb-contextmenu-trigger"
v-oc-tooltip="contextMenuLabel"
Expand All @@ -48,7 +78,7 @@
</li>
</ol>
<oc-button
v-if="items.length > 1"
v-if="displayItems.length > 1"
appearance="raw"
type="router-link"
:aria-label="$gettext('Navigate one level up')"
Expand All @@ -58,13 +88,13 @@
<oc-icon name="arrow-left-s" fill-type="line" size="large" class="oc-mr-m" />
</oc-button>
</nav>
<div v-if="items.length > 1" class="oc-breadcrumb-mobile-current">
<div v-if="displayItems.length > 1" class="oc-breadcrumb-mobile-current">
<span class="oc-text-truncate" aria-current="page" v-text="currentFolder.text" />
</div>
</template>

<script lang="ts">
import { computed, defineComponent, PropType } from 'vue'
import { computed, defineComponent, nextTick, onMounted, PropType, ref, unref, watch } from 'vue'
import { useGettext } from 'vue3-gettext'

import { AVAILABLE_SIZES } from '../../helpers/constants'
Expand Down Expand Up @@ -92,7 +122,6 @@ export default defineComponent({
OcIcon,
OcButton
},

props: {
/**
* Id for the breadcrumbs. If it's empty, a generated one will be used.
Expand Down Expand Up @@ -132,6 +161,17 @@ export default defineComponent({
return [...AVAILABLE_SIZES, 'remove'].some((e) => e === value)
}
},

/**
* Defines the maximum width of the breadcrumb. If the breadcrumb is wider than the given value, the breadcrumb
* will be reduced from the left side.
* If the value is -1, the breadcrumb will not be reduced.
*/
maxWidth: {
type: Number,
required: false,
default: -1
},
/**
* Determines if the last breadcrumb item should have context menu actions.
*/
Expand All @@ -142,6 +182,71 @@ export default defineComponent({
},
setup(props) {
const { $gettext } = useGettext()
const visibleItems = ref<BreadcrumbItem[]>([])
const hiddenItems = ref<BreadcrumbItem[]>([])
const displayItems = ref<BreadcrumbItem[]>(props.items.slice())

const getBreadcrumbElement = (id): HTMLElement => {
return document.querySelector(`[data-item-id="${id}"]`)
kulmann marked this conversation as resolved.
Show resolved Hide resolved
lookacat marked this conversation as resolved.
Show resolved Hide resolved
}

const calculateTotalBreadcrumbWidth = () => {
let totalBreadcrumbWidth = 100 // 100px margin to the right to avoid breadcrumb from getting too close to the controls
visibleItems.value.forEach((item, index) => {
lookacat marked this conversation as resolved.
Show resolved Hide resolved
const breadcrumbElement = getBreadcrumbElement(item.id)
const itemClientWidth = breadcrumbElement?.getBoundingClientRect()?.width || 0
totalBreadcrumbWidth += itemClientWidth
})
return totalBreadcrumbWidth
}

const reduceBreadcrumb = (offsetIndex) => {
const breadcrumbMaxWidth = props.maxWidth
if (!breadcrumbMaxWidth) {
return
}
document.getElementById(props.id)?.style.setProperty('--max-width', `${breadcrumbMaxWidth}px`)
lookacat marked this conversation as resolved.
Show resolved Hide resolved
const totalBreadcrumbWidth = calculateTotalBreadcrumbWidth()

const isOverflowing = breadcrumbMaxWidth < totalBreadcrumbWidth
if (!isOverflowing || visibleItems.value.length <= 3) {
return
}
// Remove from the left side
const removed = visibleItems.value.splice(offsetIndex, 1)

hiddenItems.value.push(removed[0])
reduceBreadcrumb(offsetIndex)
}

const lastHiddenItem = computed(() =>
hiddenItems.value.length >= 1 ? unref(hiddenItems)[unref(hiddenItems).length - 1] : { to: {} }
)

const renderBreadcrumb = () => {
displayItems.value = props.items.slice()
lookacat marked this conversation as resolved.
Show resolved Hide resolved
if (displayItems.value.length > 1) {
displayItems.value.splice(1, 0, {
text: '...',
allowContextActions: false,
to: {},
isPreviousHiddenFolder: true
})
}
visibleItems.value = displayItems.value.slice()
lookacat marked this conversation as resolved.
Show resolved Hide resolved
hiddenItems.value = []

nextTick(() => {
nextTick(() => {
reduceBreadcrumb(2)
})
})
}

watch([() => props.maxWidth, () => props.items], renderBreadcrumb)
lookacat marked this conversation as resolved.
Show resolved Hide resolved
onMounted(() => {
renderBreadcrumb()
})

const currentFolder = computed<BreadcrumbItem>(() => {
if (props.items.length === 0 || !props.items) {
Expand All @@ -161,14 +266,34 @@ export default defineComponent({
return props.items.length - 1 === index ? 'page' : null
}

return { currentFolder, parentFolderTo, contextMenuLabel, getAriaCurrent }
return {
currentFolder,
parentFolderTo,
contextMenuLabel,
getAriaCurrent,
visibleItems,
hiddenItems,
renderBreadcrumb,
displayItems,
lastHiddenItem
}
}
})
</script>

<style lang="scss">
.oc-breadcrumb {
overflow: hidden;
&-item-text {
max-width: 200px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;

&-last {
vertical-align: text-bottom;
}
}

&-mobile-current,
&-mobile-navigation {
Expand All @@ -184,7 +309,11 @@ export default defineComponent({

list-style: none;
align-items: baseline;
flex-wrap: wrap;
flex-wrap: nowrap;

span {
white-space: nowrap;
}

#oc-breadcrumb-contextmenu-trigger > span {
vertical-align: middle;
Expand Down Expand Up @@ -223,6 +352,8 @@ export default defineComponent({
font-size: var(--oc-font-size-medium);
color: var(--oc-color-text-default);
display: inline-block;
vertical-align: sub;
line-height: normal;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,30 @@
exports[`OcBreadcrumb displays all items 1`] = `
<nav class="oc-breadcrumb oc-breadcrumb-default" id="oc-breadcrumbs-2">
<ol class="oc-breadcrumb-list oc-flex oc-m-rm oc-p-rm">
<li class="oc-breadcrumb-list-item oc-flex oc-flex-middle">
<li class="oc-breadcrumb-list-item oc-flex oc-flex-middle" data-key="0">
<router-link-stub tag="a" to="[object Object]">
<span>First folder</span>
<span class="oc-breadcrumb-item-text">First folder</span>
</router-link-stub>
<oc-icon-stub accessiblelabel="" class="oc-mx-xs" color="var(--oc-color-text-default)" filltype="line" name="arrow-right-s" size="medium" type="span" variation="passive"></oc-icon-stub>
<!--v-if-->
</li>
<li class="oc-breadcrumb-list-item oc-flex oc-flex-middle">
<li class="oc-breadcrumb-list-item oc-flex oc-flex-middle" data-key="1">
<!--v-if-->
<oc-button-stub appearance="raw" disabled="false" gapsize="medium" justifycontent="center" size="medium" submit="button" type="button" variation="passive">
<span>Subfolder</span>
<oc-button-stub appearance="raw" class="oc-flex" disabled="false" gapsize="medium" justifycontent="center" size="medium" submit="button" type="button" variation="passive">
<span class="oc-breadcrumb-item-text">Subfolder</span>
</oc-button-stub>
<!--v-if-->
</li>
<li class="oc-breadcrumb-list-item oc-flex oc-flex-middle">
<li class="oc-breadcrumb-list-item oc-flex oc-flex-middle" data-key="2">
<router-link-stub tag="a" to="[object Object]">
<span>Deep</span>
<span class="oc-breadcrumb-item-text">Deep</span>
</router-link-stub>
<oc-icon-stub accessiblelabel="" class="oc-mx-xs" color="var(--oc-color-text-default)" filltype="line" name="arrow-right-s" size="medium" type="span" variation="passive"></oc-icon-stub>
<!--v-if-->
</li>
<li class="oc-breadcrumb-list-item oc-flex oc-flex-middle">
<li class="oc-breadcrumb-list-item oc-flex oc-flex-middle" data-key="3">
<!--v-if-->
<span aria-current="page" tabindex="-1">Deeper ellipsize in responsive mode</span>
<span aria-current="page" class="oc-breadcrumb-item-text" tabindex="-1">Deeper ellipsize in responsive mode</span>
<!--v-if-->
</li>
</ol>
Expand All @@ -43,30 +43,30 @@ exports[`OcBreadcrumb displays all items 1`] = `
exports[`OcBreadcrumb sets correct variation 1`] = `
<nav class="oc-breadcrumb oc-breadcrumb-lead" id="oc-breadcrumbs-1">
<ol class="oc-breadcrumb-list oc-flex oc-m-rm oc-p-rm">
<li class="oc-breadcrumb-list-item oc-flex oc-flex-middle">
<li class="oc-breadcrumb-list-item oc-flex oc-flex-middle" data-key="0">
<router-link-stub tag="a" to="[object Object]">
<span>First folder</span>
<span class="oc-breadcrumb-item-text">First folder</span>
</router-link-stub>
<oc-icon-stub accessiblelabel="" class="oc-mx-xs" color="var(--oc-color-text-default)" filltype="line" name="arrow-right-s" size="medium" type="span" variation="passive"></oc-icon-stub>
<!--v-if-->
</li>
<li class="oc-breadcrumb-list-item oc-flex oc-flex-middle">
<li class="oc-breadcrumb-list-item oc-flex oc-flex-middle" data-key="1">
<!--v-if-->
<oc-button-stub appearance="raw" disabled="false" gapsize="medium" justifycontent="center" size="medium" submit="button" type="button" variation="passive">
<span>Subfolder</span>
<oc-button-stub appearance="raw" class="oc-flex" disabled="false" gapsize="medium" justifycontent="center" size="medium" submit="button" type="button" variation="passive">
<span class="oc-breadcrumb-item-text">Subfolder</span>
</oc-button-stub>
<!--v-if-->
</li>
<li class="oc-breadcrumb-list-item oc-flex oc-flex-middle">
<li class="oc-breadcrumb-list-item oc-flex oc-flex-middle" data-key="2">
<router-link-stub tag="a" to="[object Object]">
<span>Deep</span>
<span class="oc-breadcrumb-item-text">Deep</span>
</router-link-stub>
<oc-icon-stub accessiblelabel="" class="oc-mx-xs" color="var(--oc-color-text-default)" filltype="line" name="arrow-right-s" size="medium" type="span" variation="passive"></oc-icon-stub>
<!--v-if-->
</li>
<li class="oc-breadcrumb-list-item oc-flex oc-flex-middle">
<li class="oc-breadcrumb-list-item oc-flex oc-flex-middle" data-key="3">
<!--v-if-->
<span aria-current="page" tabindex="-1">Deeper ellipsize in responsive mode</span>
<span aria-current="page" class="oc-breadcrumb-item-text" tabindex="-1">Deeper ellipsize in responsive mode</span>
<!--v-if-->
</li>
</ol>
Expand Down
2 changes: 2 additions & 0 deletions packages/design-system/src/components/OcBreadcrumb/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { RouteLocationRaw } from 'vue-router'

export interface BreadcrumbItem {
id?: string
text: string
to?: RouteLocationRaw
allowContextActions?: boolean
onClick?: () => void
isPreviousHiddenFolder?: boolean
lookacat marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ exports[`Spaces view loading states should render spaces list after loading has
<div class="oc-width-expand oc-height-1-1 oc-position-relative" id="admin-settings-wrapper">
<div class="oc-app-bar oc-py-s" id="admin-settings-app-bar">
<div class="admin-settings-app-bar-controls oc-flex oc-flex-between oc-flex-middle">
<oc-breadcrumb-stub class="oc-flex oc-flex-middle" contextmenupadding="medium" id="admin-settings-breadcrumb" items="[object Object],[object Object]" showcontextactions="false" variation="default"></oc-breadcrumb-stub>
<oc-breadcrumb-stub class="oc-flex oc-flex-middle" contextmenupadding="medium" id="admin-settings-breadcrumb" items="[object Object],[object Object]" maxwidth="-1" showcontextactions="false" variation="default"></oc-breadcrumb-stub>
<portal-target name="app.runtime.mobile.nav"></portal-target>
<div>
<button aria-label="Open sidebar to view details" class="oc-button oc-rounded oc-button-m oc-button-justify-content-center oc-button-gap-m oc-button-passive oc-button-passive-raw oc-my-s oc-p-xs" id="files-toggle-sidebar" type="button">
Expand Down
Loading