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
Expand Up @@ -43,11 +43,11 @@ describe('SharedWithMe view', () => {
describe('different files view states', () => {
it('shows the loading spinner during loading', () => {
const { wrapper } = getMountedWrapper({ loading: true })
expect(wrapper.find('app-loading-spinner-stub').exists()).toBeTruthy()
expect(wrapper.find('#app-loading-spinner').exists()).toBeTruthy()
})
it('does not show the loading spinner after loading finished', () => {
const { wrapper } = getMountedWrapper()
expect(wrapper.find('app-loading-spinner-stub').exists()).toBeFalsy()
expect(wrapper.find('#app-loading-spinner').exists()).toBeFalsy()
})
})
describe('filter', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ describe('SharedWithOthers view', () => {
describe('different files view states', () => {
it('shows the loading spinner during loading', () => {
const { wrapper } = getMountedWrapper({ loading: true })
expect(wrapper.find('app-loading-spinner-stub').exists()).toBeTruthy()
expect(wrapper.find('#app-loading-spinner').exists()).toBeTruthy()
})
it('shows the no-content-message after loading', () => {
const { wrapper } = getMountedWrapper()
expect(wrapper.find('app-loading-spinner-stub').exists()).toBeFalsy()
expect(wrapper.find('#app-loading-spinner').exists()).toBeFalsy()
expect(wrapper.find('.no-content-message').exists()).toBeTruthy()
})
it('shows the files table when files are available', () => {
Expand Down
8 changes: 1 addition & 7 deletions packages/web-pkg/src/components/AppLoadingSpinner.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,4 @@
</div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
name: 'AppLoadingSpinner'
})
</script>
<script setup lang="ts"></script>
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,8 @@
</div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'ErrorScreen',
props: {
/**
* Error message passed from external app
*/
message: {
default: '',
type: String,
required: false
}
}
})
<script setup lang="ts">
const { message = '' } = defineProps<{
message?: string
}>()
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,4 @@
</div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
name: 'LoadingScreen'
})
</script>
<script setup lang="ts"></script>
184 changes: 75 additions & 109 deletions packages/web-pkg/src/components/ContextActions/ActionMenuItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,128 +52,94 @@
</li>
</template>

<script lang="ts">
import { computed, defineComponent, PropType, unref } from 'vue'
<script setup lang="ts">
import { computed, unref } from 'vue'
import { Action, ActionOptions, useConfigStore } from '../../composables'
import { storeToRefs } from 'pinia'
import { AppearanceType } from '@opencloud-eu/design-system/helpers'

export default defineComponent({
name: 'ActionMenuItem',
props: {
action: {
type: Object as PropType<Action>,
required: true
},
actionOptions: {
type: Object as PropType<ActionOptions>,
required: true
},
size: {
type: String as PropType<'small' | 'medium' | 'large'>,
required: false,
default: 'medium'
},
appearance: {
type: String as PropType<AppearanceType>,
default: 'raw'
},
shortcutHint: {
type: Boolean,
default: true,
required: false
},
showTooltip: {
type: Boolean,
default: false,
required: false
},
buttonClasses: {
type: Array as PropType<string[]>,
default: (): string[] => []
}
},
setup(props) {
const configStore = useConfigStore()
const { options } = storeToRefs(configStore)
const {
action,
actionOptions,
size = 'medium',
appearance = 'raw',
shortcutHint = true,
showTooltip = false,
buttonClasses = []
} = defineProps<{
action: Action
actionOptions: ActionOptions
size?: 'small' | 'medium' | 'large'
appearance?: AppearanceType
shortcutHint?: boolean
showTooltip?: boolean
buttonClasses?: string[]
}>()

const componentType = computed<'a' | 'button' | 'router-link'>(() => {
if (Object.hasOwn(props.action, 'route')) {
return 'router-link'
}
if (Object.hasOwn(props.action, 'href')) {
return 'a'
}
if (Object.hasOwn(props.action, 'handler')) {
return 'button'
}
console.warn(
'ActionMenuItem: No handler, route or href callback found in action',
props.action
)
return 'button'
})
const configStore = useConfigStore()
const { options } = storeToRefs(configStore)

const componentProps = computed(() => {
const properties = {
appearance: props.action.appearance || props.appearance,
...(props.action.isDisabled && {
disabled: props.action.isDisabled(props.actionOptions)
}),
...(props.action.id && { id: props.action.id })
}
const componentType = computed<'a' | 'button' | 'router-link'>(() => {
if (Object.hasOwn(action, 'route')) {
return 'router-link'
}
if (Object.hasOwn(action, 'href')) {
return 'a'
}
if (Object.hasOwn(action, 'handler')) {
return 'button'
}
console.warn('ActionMenuItem: No handler, route or href callback found in action', action)
return 'button'
})

return {
...properties,
...(unref(componentType) === 'router-link' && {
to: props.action.route(props.actionOptions)
}),
...(unref(componentType) === 'a' && {
href: props.action.href(props.actionOptions)
}),
...(['router-link', 'a'].includes(unref(componentType)) && {
target: options.value.openFilesInNewTab ? ('_blank' as const) : ('_self' as const)
})
}
})
const componentProps = computed(() => {
const properties = {
appearance: action.appearance || appearance,
...(action.isDisabled && {
disabled: action.isDisabled(actionOptions)
}),
...(action.id && { id: action.id })
}

const actionIcon = computed(() => {
return typeof props.action.icon === 'function'
? props.action.icon(props.actionOptions)
: props.action.icon
return {
...properties,
...(unref(componentType) === 'router-link' && {
to: action.route(actionOptions)
}),
...(unref(componentType) === 'a' && {
href: action.href(actionOptions)
}),
...(['router-link', 'a'].includes(unref(componentType)) && {
target: options.value.openFilesInNewTab ? ('_blank' as const) : ('_self' as const)
})
}
})

return {
componentType,
componentProps,
actionIcon
}
},
computed: {
hasExternalImageIcon() {
return this.actionIcon && /^https?:\/\//i.test(this.actionIcon)
},
componentListeners() {
if (typeof this.action.handler !== 'function') {
return {}
}
const actionIcon = computed(() => {
return typeof action.icon === 'function' ? action.icon(actionOptions) : action.icon
})

const callback = () =>
this.action.handler({
...this.actionOptions
})
if (this.action.keepOpen) {
return {
click: (event: Event) => {
event.stopPropagation()
callback()
}
}
}
return {
click: callback
const hasExternalImageIcon = computed(() => {
return actionIcon.value && /^https?:\/\//i.test(actionIcon.value)
})

const componentListeners = computed(() => {
if (typeof action.handler !== 'function') {
return {}
}

const callback = () => action.handler({ ...actionOptions })
if (action.keepOpen) {
return {
click: (event: Event) => {
event.stopPropagation()
callback()
}
}
}
return {
click: callback
}
})
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -40,56 +40,46 @@
</div>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue'
<script setup lang="ts">
import ActionMenuItem from './ActionMenuItem.vue'
import { Action, ActionOptions } from '../../composables'
import { AppearanceType } from '@opencloud-eu/design-system/helpers'
import ActionMenuDropItem from './ActionMenuDropItem.vue'
import { MenuSection, MenuSectionDrop } from './types'

export default defineComponent({
name: 'ContextActionMenu',
components: { ActionMenuDropItem, ActionMenuItem },
props: {
menuSections: {
type: Array as PropType<MenuSection[]>,
required: true
},
appearance: {
type: String as PropType<AppearanceType>,
default: 'raw'
},
actionOptions: {
type: Object as PropType<ActionOptions>,
required: true
}
},
methods: {
actionToDropItem(action: Action): MenuSectionDrop {
return {
label: action.label(this.actionOptions),
name: action.name,
icon: typeof action.icon === 'function' ? action.icon(this.actionOptions) : action.icon,
items: (action.children || []).filter((child) => child.isVisible(this.actionOptions))
}
},
getSectionClasses(index: number) {
const classes: string[] = []
if (!this.menuSections.length) {
return classes
}
if (index < this.menuSections.length - 1) {
classes.push('pb-2')
}
if (index > 0) {
classes.push('pt-2')
}
if (index < this.menuSections.length - 1) {
classes.push('border-b')
}
return classes
}
const {
menuSections,
actionOptions,
appearance = 'raw'
} = defineProps<{
menuSections: MenuSection[]
actionOptions: ActionOptions
appearance?: AppearanceType
}>()

function actionToDropItem(action: Action): MenuSectionDrop {
return {
label: action.label(actionOptions),
name: action.name,
icon: typeof action.icon === 'function' ? action.icon(actionOptions) : action.icon,
items: (action.children || []).filter((child) => child.isVisible(actionOptions))
}
}

function getSectionClasses(index: number) {
const classes: string[] = []
if (!menuSections.length) {
return classes
}
if (index < menuSections.length - 1) {
classes.push('pb-2')
}
if (index > 0) {
classes.push('pt-2')
}
if (index < menuSections.length - 1) {
classes.push('border-b')
}
})
return classes
}
</script>
Loading