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

feat: add location picker into embed mode #9863

Merged
merged 1 commit into from
Oct 30, 2023
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
7 changes: 7 additions & 0 deletions changelog/unreleased/enhancement-location-picker
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Enhancement: Location picker in embed mode

We've added a new query param called `embed-target` which can have value `location`. This value is then saved in the `configuration.options` object as `embedTarget`.
When the value is set to `location`, it allows selecting the `currentFolder` as location instead of selecting resources.

https://github.com/owncloud/web/pull/9863
https://github.com/owncloud/web/issues/9768
36 changes: 36 additions & 0 deletions docs/embed-mode/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,39 @@ The app is emitting various events depending on the goal of the user. All events
| **owncloud-embed:select** | Resource[] | Gets emitted when user selects resources via the "Attach as copy" action |
| **owncloud-embed:share** | string[] | Gets emitted when user selects resources and shares them via the "Share links" action |
| **owncloud-embed:cancel** | void | Gets emitted when user attempts to close the embedded instance via "Cancel" action |

### Example

```html
<iframe src="https://my-owncloud-web-instance?mode=embed"></iframe>

<script>
function selectEventHandler(event) {
const resources = event.detail

doSomethingWithSelectedResources(resources)
}

window.addEventListener('owncloud-embed:select', selectEventHandler)
</script>
```

## Location picker

By default, the Embed mode allows users to select resources. In certain cases (e.g. uploading a file), this needs to be changed to allow selecting a location. This can be achieved by running the embed mode with additional parameter `embed-target=location`. With this parameter, resource selection is disabled and the selected resources array always includes the current folder as the only item.

### Example

```html
<iframe src="https://my-owncloud-web-instance?mode=embed&embed-target=location"></iframe>

<script>
function selectEventHandler(event) {
const currentFolder = event.detail[0]

uploadIntoCurrentFolder(currentFolder)
}

window.addEventListener('owncloud-embed:select', selectEventHandler)
</script>
```
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
$gettext('Cancel')
}}</oc-button>
<oc-button
v-if="!isLocationPicker"
key="btn-share"
data-testid="button-share"
variation="inverse"
appearance="filled"
Expand All @@ -17,7 +19,7 @@
appearance="filled"
:disabled="areSelectActionsDisabled"
@click="emitSelect"
>{{ $gettext('Attach as copy') }}</oc-button
>{{ selectLabel }}</oc-button
>
</section>
</template>
Expand All @@ -29,6 +31,7 @@ import {
showQuickLinkPasswordModal,
useAbility,
useClientService,
useEmbedMode,
usePasswordPolicyService,
useStore
} from '@ownclouders/web-pkg'
Expand All @@ -42,15 +45,24 @@ export default {
const clientService = useClientService()
const passwordPolicyService = usePasswordPolicyService()
const language = useGettext()
const { isLocationPicker } = useEmbedMode()

const selectedFiles = computed<Resource[]>(() => {
if (isLocationPicker.value) {
return [store.getters['Files/currentFolder']]
}

return store.getters['Files/selectedFiles']
})

const areSelectActionsDisabled = computed<boolean>(() => selectedFiles.value.length < 1)

const canCreatePublicLinks = computed<boolean>(() => ability.can('create-all', 'PublicLink'))

const selectLabel = computed<string>(() =>
isLocationPicker.value ? language.$gettext('Choose') : language.$gettext('Attach as copy')
)

const emitSelect = (): void => {
const event: CustomEvent<Resource[]> = new CustomEvent('owncloud-embed:select', {
detail: selectedFiles.value
Expand Down Expand Up @@ -132,6 +144,8 @@ export default {
return {
areSelectActionsDisabled,
canCreatePublicLinks,
isLocationPicker,
selectLabel,
sharePublicLinks,
emitCancel,
emitSelect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,29 @@ describe('EmbedActions', () => {
payload: { detail: [{ id: 1 }] }
})
})

it('should enable select action when embedTarget is set to location', () => {
const { wrapper } = getWrapper({ configuration: { options: { embedTarget: 'location' } } })

expect(wrapper.find(selectors.btnSelect).attributes()).not.toHaveProperty('disabled')
})

it('should emit select event with currentFolder as selected resource when select action is triggered', async () => {
window.parent.dispatchEvent = jest.fn()
global.CustomEvent = jest.fn().mockImplementation(mockCustomEvent)

const { wrapper } = getWrapper({
currentFolder: { id: 1 },
configuration: { options: { embedTarget: 'location' } }
})

await wrapper.find(selectors.btnSelect).trigger('click')

expect(window.parent.dispatchEvent).toHaveBeenCalledWith({
name: 'owncloud-embed:select',
payload: { detail: [{ id: 1 }] }
})
})
})

describe('cancel action', () => {
Expand Down Expand Up @@ -127,26 +150,43 @@ describe('EmbedActions', () => {
payload: { detail: ['password-link-1'] }
})
})

it('should hide share action when embedTarget is set to location', () => {
const { wrapper } = getWrapper({ configuration: { options: { embedTarget: 'location' } } })

expect(wrapper.find(selectors.btnShare).exists()).toBe(false)
})
})
})

function getWrapper(
{ selectedFiles = [], abilities = [], capabilities = jest.fn().mockReturnValue({}) } = {
{
selectedFiles = [],
abilities = [],
capabilities = jest.fn().mockReturnValue({}),
configuration = { options: {} },
currentFolder = {}
} = {
selectedFiles: [],
abilities: [],
capabilities: jest.fn().mockReturnValue({})
}
) {
const storeOptions = {
...defaultStoreMockOptions,
getters: { ...defaultStoreMockOptions.getters, capabilities },
getters: {
...defaultStoreMockOptions.getters,
capabilities,
configuration: jest.fn().mockReturnValue(configuration || { options: {} })
},
modules: {
...defaultStoreMockOptions.modules,
Files: {
...defaultStoreMockOptions.modules.Files,
getters: {
...defaultStoreMockOptions.modules.Files.getters,
selectedFiles: jest.fn().mockReturnValue(selectedFiles)
selectedFiles: jest.fn().mockReturnValue(selectedFiles),
currentFolder: jest.fn().mockReturnValue(currentFolder)
}
}
}
Expand Down
11 changes: 7 additions & 4 deletions packages/web-pkg/src/components/FilesList/ResourceTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
@sort="sort"
@update:model-value="$emit('update:modelValue', $event)"
>
<template #selectHeader>
<template v-if="!isLocationPicker" #selectHeader>
<div class="resource-table-select-all">
<oc-checkbox
id="resource-table-select-all"
Expand All @@ -39,7 +39,7 @@
/>
</div>
</template>
<template #select="{ item }">
<template v-if="!isLocationPicker" #select="{ item }">
<oc-checkbox
:id="`resource-table-select-${resourceDomSelector(item)}`"
:label="getResourceCheckboxLabel(item)"
Expand Down Expand Up @@ -227,7 +227,8 @@ import {
ViewModeConstants,
useConfigurationManager,
useGetMatchingSpace,
useFolderLink
useFolderLink,
useEmbedMode
} from '../../composables'
import { EVENT_TROW_MOUNTED, EVENT_FILE_DROPPED, ImageDimension } from '../../constants'
import { eventBus } from '../../services'
Expand Down Expand Up @@ -441,6 +442,7 @@ export default defineComponent({
const configurationManager = useConfigurationManager()
const { getMatchingSpace } = useGetMatchingSpace()
const { $gettext } = useGettext()
const { isLocationPicker } = useEmbedMode()

const { width } = useWindowSize()
const hasTags = computed(
Expand Down Expand Up @@ -487,7 +489,8 @@ export default defineComponent({
...useFolderLink({
space: ref(props.space),
targetRouteCallback: computed(() => props.targetRouteCallback)
})
}),
isLocationPicker
}
},
data() {
Expand Down
6 changes: 5 additions & 1 deletion packages/web-pkg/src/composables/embedMode/useEmbedMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@ export const useEmbedMode = () => {

const isEnabled = computed<boolean>(() => store.getters.configuration.options.mode === 'embed')

return { isEnabled }
const isLocationPicker = computed<boolean>(() => {
return store.getters.configuration.options.embedTarget === 'location'
})

return { isEnabled, isLocationPicker }
}
1 change: 1 addition & 0 deletions packages/web-pkg/src/configuration/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface OptionsConfiguration {
disabledExtensions?: string[]
mode?: string
isRunningOnEos?: boolean
embedTarget?: string
}

export interface OAuth2Configuration {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,26 @@ describe('ResourceTable', () => {
expect((wrapper.emitted('update:selectedIds')[0][0] as any).length).toBe(0)
})
})

describe('embed mode location target', () => {
it('should not hide checkboxes when embed mode does not have location as target', () => {
const { wrapper } = getMountedWrapper({
configuration: { options: { embedTarget: undefined } }
})

expect(wrapper.find('.resource-table-select-all').exists()).toBe(true)
expect(wrapper.find('.resource-table-select-all .oc-checkbox').exists()).toBe(true)
})

it('should hide checkboxes when embed mode has location as target', () => {
const { wrapper } = getMountedWrapper({
configuration: { options: { embedTarget: 'location' } }
})

expect(wrapper.find('.resource-table-select-all').exists()).toBe(false)
expect(wrapper.find('.resource-table-select-all .oc-checkbox').exists()).toBe(false)
})
})
})

describe('resource activation', () => {
Expand Down Expand Up @@ -429,7 +449,8 @@ describe('ResourceTable', () => {
function getMountedWrapper({
props = {},
isUserContextReady = true,
addProcessingResources = false
addProcessingResources = false,
configuration = { options: {} }
} = {}) {
const storeOptions = defaultStoreMockOptions
storeOptions.modules.runtime.modules.auth.getters.isUserContextReady.mockReturnValue(
Expand All @@ -440,6 +461,17 @@ function getMountedWrapper({
tags: true
}
}))
storeOptions.getters.configuration.mockImplementation(() => ({
currentTheme: { general: { slogan: '' } },
...configuration,
options: {
editor: {
autosaveEnabled: false,
autosaveInterval: 120
},
...configuration?.options
}
}))

const store = createStore(storeOptions)

Expand Down
7 changes: 7 additions & 0 deletions packages/web-runtime/src/container/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ export const announceConfiguration = async (path: string): Promise<RuntimeConfig
rawConfig.options = { ...rawConfig.options, mode: getQueryParam('mode') ?? 'web' }
}

// Can enable location picker in embed mode
const embedTarget = getQueryParam('embed-target')

if (embedTarget) {
rawConfig.options.embedTarget = embedTarget
}

configurationManager.initialize(rawConfig)
// TODO: we might want to get rid of exposing the raw config. needs more refactoring though.
return rawConfig
Expand Down