Skip to content

Commit

Permalink
Extract Spaces-Projects tiles to design-system/oc-tiles component (#8137
Browse files Browse the repository at this point in the history
)
  • Loading branch information
pascalwengerter authored Jan 4, 2023
1 parent 207fa58 commit 35d98ce
Show file tree
Hide file tree
Showing 18 changed files with 509 additions and 321 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Add tile component

We have added a new component to display a resource as a tile.

https://github.com/owncloud/web/pull/8137
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
</span>
</template>

<script>
<script lang="ts">
import uniqueId from '../../utils/uniqueId'
/**
Expand Down Expand Up @@ -53,14 +53,14 @@ export default {
}
},
computed: {
isFolder() {
return this.resource.isFolder
isNavigatable() {
return this.resource.isFolder && !this.resource.disabled
},
componentType() {
return this.isFolder ? 'router-link' : 'oc-button'
return this.isNavigatable ? 'router-link' : 'oc-button'
},
componentProps() {
if (!this.isRouterLink) {
if (!this.isNavigatable) {
return {
appearance: 'raw',
gapSize: 'none',
Expand All @@ -79,11 +79,8 @@ export default {
return null
},
isRouterLink() {
return this.isResourceClickable && this.isFolder
},
linkTargetBlank() {
if (this.isRouterLink && this.resource.opensInNewWindow) {
if (this.isNavigatable && this.resource.opensInNewWindow) {
return '_blank'
}
Expand All @@ -92,7 +89,7 @@ export default {
},
methods: {
emitClick() {
if (this.isFolder) {
if (this.isNavigatable) {
return
}
Expand Down
37 changes: 37 additions & 0 deletions packages/design-system/src/components/OcTile/OcTile.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { shallowMount } from 'web-test-helpers'
import OcTile from './OcTile.vue'

const defaultSpace = {
name: 'Space 1',
path: '',
type: 'space',
isFolder: true,
getDriveAliasAndItem: () => '1'
}
const disabledSpace = {
name: 'Space 1',
path: '',
type: 'space',
isFolder: true,
disabled: true,
getDriveAliasAndItem: () => '1'
}

describe('OcTile component', () => {
it('renders default space correctly', () => {
const wrapper = getWrapper({ resource: defaultSpace })
expect(wrapper).toMatchSnapshot()
})
it('renders disabled space correctly', () => {
const wrapper = getWrapper({ resource: disabledSpace })
expect(wrapper).toMatchSnapshot()
})

function getWrapper(props = {}, slots = {}) {
return shallowMount(OcTile, {
props: {
...props
}
})
}
})
135 changes: 135 additions & 0 deletions packages/design-system/src/components/OcTile/OcTile.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<template>
<div
class="oc-tile-card oc-card oc-card-default oc-rounded"
:data-item-id="resource.id"
:class="resource.disabled ? 'state-trashed' : ''"
>
<oc-resource-link
class="oc-card-media-top oc-border-b oc-flex oc-flex-center oc-flex-middle"
:resource="resource"
:folder-link="resourceRoute"
@click="$emit('click', $event)"
>
<oc-tag
v-if="resource.disabled"
class="resource-disabled-indicator oc-position-absolute"
type="span"
>
<span v-translate>Disabled</span>
</oc-tag>
<!-- Slot for resource image, renders resource type icon by default -->
<slot name="imageField" :item="resource">
<oc-img v-if="resource.thumbnail" class="tile-preview" :src="resource.thumbnail" />
<oc-resource-icon
v-else
:resource="resource"
size="xxlarge"
class="tile-default-image oc-p-m"
/>
</slot>
</oc-resource-link>
<div class="oc-card-body oc-p-s">
<div class="oc-flex oc-flex-between oc-flex-middle">
<div class="oc-flex oc-flex-middle oc-text-truncate">
<oc-resource
:resource="resource"
:folder-link="resourceRoute"
@click="$emit('click', $event)"
/>
</div>
<div class="oc-flex oc-flex-middle">
<!-- Slot for individual actions -->
<slot name="actions" :item="resource" />
</div>
</div>
<p
v-if="resource.description"
class="oc-text-left oc-ml-xs oc-mt-xs oc-mb-rm oc-text-truncate"
>
<small v-text="resource.description" />
</p>
</div>
</div>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue'
import { Resource } from 'web-client'
import OcImg from '../OcImage/OcImage.vue'
import OcResource from '../OcResource/OcResource.vue'
import OcResourceIcon from '../OcResourceIcon/OcResourceIcon.vue'
import OcResourceLink from '../OcResourceLink/OcResourceLink.vue'
import OcTag from '../OcTag/OcTag.vue'
export default defineComponent({
name: 'OcTile',
status: 'prototype',
release: 'unreleased',
components: {
OcImg,
OcResource,
OcResourceIcon,
OcResourceLink,
OcTag
},
props: {
/**
* Resource to be displayed within the tile
*/
resource: {
type: Object as PropType<Resource>,
default: () => {}
},
resourceRoute: {
type: Object,
default: () => {}
}
},
emits: ['click']
})
</script>

<style lang="scss" scoped>
.oc-tile-card {
box-shadow: none !important;
background-color: var(--oc-color-background-highlight) !important;
height: 100%;
&.state-trashed {
cursor: pointer;
.tile-image,
.tile-default-image > svg {
filter: grayscale(100%);
opacity: 80%;
}
}
.oc-card-media-top {
aspect-ratio: 16/9;
justify-content: center;
width: 100%;
.oc-tag {
color: var(--oc-color-text-default);
&.resource-disabled-indicator {
z-index: 1;
}
}
.tile-preview {
min-width: 252px;
height: auto;
aspect-ratio: 16/9;
object-fit: cover;
}
}
.resource-name {
overflow: hidden;
color: var(--oc-color-text-default);
}
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`OcTile component renders default space correctly 1`] = `
<div class="oc-tile-card oc-card oc-card-default oc-rounded">
<oc-resource-link-stub class="oc-card-media-top oc-border-b oc-flex oc-flex-center oc-flex-middle" isresourceclickable="true" resource="[object Object]">
<!---->
<oc-resource-icon-stub class="tile-default-image oc-p-m" resource="[object Object]" size="xxlarge"></oc-resource-icon-stub>
</oc-resource-link-stub>
<div class="oc-card-body oc-p-s">
<div class="oc-flex oc-flex-between oc-flex-middle">
<div class="oc-flex oc-flex-middle oc-text-truncate">
<oc-resource-stub isextensiondisplayed="true" isresourceclickable="true" isthumbnaildisplayed="true" parentfoldernamedefault="" resource="[object Object]"></oc-resource-stub>
</div>
<div class="oc-flex oc-flex-middle"></div>
</div>
<!---->
</div>
</div>
`;

exports[`OcTile component renders disabled space correctly 1`] = `
<div class="oc-tile-card oc-card oc-card-default oc-rounded state-trashed">
<oc-resource-link-stub class="oc-card-media-top oc-border-b oc-flex oc-flex-center oc-flex-middle" isresourceclickable="true" resource="[object Object]">
<oc-tag-stub class="resource-disabled-indicator oc-position-absolute" size="medium" type="span">
<span data-current-language="en_US" data-msgid="Disabled">Disabled</span>
</oc-tag-stub>
<oc-resource-icon-stub class="tile-default-image oc-p-m" resource="[object Object]" size="xxlarge"></oc-resource-icon-stub>
</oc-resource-link-stub>
<div class="oc-card-body oc-p-s">
<div class="oc-flex oc-flex-between oc-flex-middle">
<div class="oc-flex oc-flex-middle oc-text-truncate">
<oc-resource-stub isextensiondisplayed="true" isresourceclickable="true" isthumbnaildisplayed="true" parentfoldernamedefault="" resource="[object Object]"></oc-resource-stub>
</div>
<div class="oc-flex oc-flex-middle"></div>
</div>
<!---->
</div>
</div>
`;
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,10 @@ describe('OcTableCell', () => {
width: 'shrink'
},
slots: {
deafult: 'Hello world!'
default: 'Hello world!'
}
})

expect(wrapper.element.tagName).toBe('TH')
expect(wrapper.classes()).toContain('oc-table-cell-align-right')
expect(wrapper.classes()).toContain('oc-table-cell-align-bottom')
expect(wrapper.classes()).toContain('oc-table-cell-width-shrink')
expect(wrapper).toMatchSnapshot()
})
})
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`OcTableCell Uses correct element 1`] = `<th class="oc-table-cell oc-table-cell-align-right oc-table-cell-align-bottom oc-table-cell-width-shrink"></th>`;
exports[`OcTableCell Uses correct element 1`] = `<th class="oc-table-cell oc-table-cell-align-right oc-table-cell-align-bottom oc-table-cell-width-shrink">Hello world!</th>`;
1 change: 1 addition & 0 deletions packages/design-system/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,4 @@ export { default as OcTableSimple } from './OcTableSimple/OcTableSimple.vue'
export { default as OcTag } from './OcTag/OcTag.vue'
export { default as OcTextarea } from './OcTextarea/OcTextarea.vue'
export { default as OcTextInput } from './OcTextInput/OcTextInput.vue'
export { default as OcTile } from './OcTile/OcTile.vue'
103 changes: 103 additions & 0 deletions packages/web-app-files/src/components/FilesList/ResourceTiles.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<template>
<div>
<oc-list class="oc-tiles oc-flex">
<li v-for="(resource, index) in data" :key="resource.id" class="oc-tiles-item">
<oc-tile
:ref="`row-${index}`"
:resource="resource"
:resource-route="getRoute(resource)"
@click="tileResourceClicked(resource, $event)"
@contextmenu.native="
$emit(EVENT_TROW_CONTEXTMENU, $refs[`row-${index}`][0], $event, resource)
"
>
<template #imageField="{ item }">
<slot name="image" :item="item" />
</template>
<template #actions="{ item }">
<slot name="actions" :item="item" />
</template>
</oc-tile>
</li>
</oc-list>
<!-- @slot Footer of the tiles list -->
<slot name="footer" />
</div>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue'
import { createLocationSpaces } from 'web-app-files/src/router'
import { Resource, SpaceResource } from 'web-client'
import { createFileRouteOptions } from 'web-pkg/src/helpers/router'
import { useStore, useTranslations } from 'web-pkg/src/composables'
// Constants should match what is being used in OcTable/ResourceTable
// Alignment regarding naming would be an API-breaking change and can
// Be done at a later point in time?
import { EVENT_TROW_CONTEXTMENU } from '../../constants'
export default defineComponent({
name: 'ResourceTiles',
props: {
/**
* Array of resources (spaces, folders, files) to be displayed as tiles
*/
data: {
type: Array as PropType<Resource[]>,
default: () => []
}
},
setup() {
const store = useStore()
const { $gettext } = useTranslations()
const getRoute = (resource) => {
if (resource.type === 'space') {
return resource.disabled
? { path: '#' }
: createLocationSpaces(
'files-spaces-generic',
createFileRouteOptions(resource as SpaceResource, {
path: '',
fileId: resource.fileId
})
)
}
return { path: '#' }
}
const tileResourceClicked = (resource, event) => {
// Needs to handle file actions and potentially disabled folders also
if (resource.disabled && resource.type === 'space') {
store.dispatch('showMessage', {
title: $gettext('Disabled spaces cannot be entered'),
status: 'warning'
})
}
}
return {
EVENT_TROW_CONTEXTMENU,
getRoute,
tileResourceClicked
}
}
})
</script>

<style lang="scss" scoped>
.oc-tiles {
flex-wrap: wrap;
gap: 15px;
justify-content: flex-start;
.oc-tiles-item {
width: 252px;
@media (max-width: $oc-breakpoint-xsmall-max) {
width: 100%;
}
}
}
</style>
Loading

0 comments on commit 35d98ce

Please sign in to comment.