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
4 changes: 2 additions & 2 deletions .github/workflows/cypress-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
- '**.vue'

init:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
needs: changes
if: needs.changes.outputs.src != 'false'

Expand Down Expand Up @@ -147,7 +147,7 @@ jobs:
/home/runner/.cache/Cypress

cypress:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
needs: [changes, init]
if: needs.changes.outputs.src != 'false'

Expand Down
58 changes: 58 additions & 0 deletions cypress/e2e/pages.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -297,4 +297,62 @@ describe('Pages', function() {
.should('contain', 'your thoughts that really matter')
})
})

if (!['stable27', 'stable28', 'stable29'].includes(Cypress.env('ncVersion'))) {
describe('Search dialog', () => {
beforeEach(() => {
cy.get('input[name="pageFilter"]').type('collective')
cy.get('.search-dialog__container', { timeout: 5000 })
.should('be.visible')
.as('searchDialog')
})

it('Shows search dialog', () => {
cy.get('.search-dialog__info')
.invoke('text')
.invoke('trim')
.should('equal', 'Found 5 matches for "collective"')
})

it('Clears search', () => {
cy.get('.search-dialog__buttons')
.find('button[aria-label="Clear search"]')
.click()
cy.get('@searchDialog').should('not.exist')
})

it('Toggles highlight all', () => {
cy.get('.search-dialog__highlight-all')
.find('span.checkbox-radio-switch-checkbox')
.click()

cy.get('.search-dialog__info')
.invoke('text')
.invoke('trim')
.should('equal', 'Match 1 of 5 for "collective"')
})

it('Moves to next search', () => {
cy.get('.search-dialog__buttons')
.find('button[aria-label="Find next match"]')
.click()

cy.get('.search-dialog__info')
.invoke('text')
.invoke('trim')
.should('equal', 'Match 2 of 5 for "collective"')
})

it('Moves to previous search', () => {
cy.get('.search-dialog__buttons')
.find('button[aria-label="Find previous match"]')
.click()

cy.get('.search-dialog__info')
.invoke('text')
.invoke('trim')
.should('equal', 'Match 5 of 5 for "collective"')
})
})
}
})
4 changes: 3 additions & 1 deletion src/components/Page.vue
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
</h2>
<LandingPageWidgets v-if="isLandingPage" />
<TextEditor :key="`text-editor-${currentPage.id}`" ref="texteditor" />
<SearchDialog />
<SearchDialog :show="shouldShowSearchDialog" />
</div>
</template>

Expand All @@ -125,6 +125,7 @@ import { mapActions, mapState } from 'pinia'
import { useRootStore } from '../stores/root.js'
import { useCollectivesStore } from '../stores/collectives.js'
import { usePagesStore } from '../stores/pages.js'
import { useSearchStore } from '../stores/search.js'
import pageMixin from '../mixins/pageMixin.js'
import { showError } from '@nextcloud/dialogs'

Expand Down Expand Up @@ -175,6 +176,7 @@ export default {
'isTemplatePage',
'isLandingPage',
]),
...mapState(useSearchStore, ['shouldShowSearchDialog']),

hasSidebarToggle() {
return !this.showing('sidebar')
Expand Down
57 changes: 33 additions & 24 deletions src/components/SearchDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
-->

<template>
<div v-if="totalMatches !== null" class="search-dialog__container">
<div v-if="shouldShow" class="search-dialog__container">
<div class="search-dialog__buttons">
<NcButton alignment="center-reverse"
type="tertiary"
Expand Down Expand Up @@ -38,15 +38,15 @@
<div class="search-dialog__info">
<span v-if="matchAll">
{{ t('collectives', 'Found {matches} matches for "{query}"', {
matches: totalMatches,
matches: results.totalMatches ?? 0,
query: searchQuery,
}) }}
</span>

<span v-else>
{{ t('collectives', 'Match {index} of {matches} for "{query}"', {
index: matchIndex + 1,
matches: totalMatches,
index: results.matchIndex + 1,
matches: results.totalMatches,
query: searchQuery,
}) }}
</span>
Expand All @@ -61,10 +61,10 @@
</template>

<script>
import { subscribe } from '@nextcloud/event-bus'
import { NcButton, NcCheckboxRadioSwitch } from '@nextcloud/vue'
import { translate as t } from '@nextcloud/l10n'
import { mapActions, mapState } from 'pinia'
import { useRootStore } from '../stores/root.js'
import { useSearchStore } from '../stores/search.js'
import ArrowDown from 'vue-material-design-icons/ArrowDown.vue'
import ArrowUp from 'vue-material-design-icons/ArrowUp.vue'
Expand All @@ -81,16 +81,20 @@ export default {
Close,
},

data() {
return {
totalMatches: null,
matchIndex: 0,
}
props: {
show: {
type: Boolean,
default: false,
},
},

computed: {
...mapState(useSearchStore, ['searchQuery', 'matchAll']),

...mapState(useRootStore, ['isTextEdit']),
...mapState(useSearchStore, [
'searchQuery',
'matchAll',
'results',
]),
isHighlightAllChecked: {
get() {
return this.matchAll
Expand All @@ -99,40 +103,45 @@ export default {
this.toggleMatchAll()
},
},
},

created() {
subscribe('text:editor:search-results', ({ results, index }) => {
this.totalMatches = results
this.matchIndex = index
})
shouldShow() {
return this.show && this.results.totalMatches !== null
},
},

methods: {
t,
...mapActions(useSearchStore, [
'setSearchQuery',
'toggleMatchAll',
'nextSearch',
'previousSearch',
'showSearchDialog',
'searchNext',
'searchPrevious',
]),

previous() {
this.previousSearch()
this.searchPrevious()
this.scrollIntoView()
},

next() {
this.nextSearch()
this.searchNext()
this.scrollIntoView()
},

clearSearch() {
this.setSearchQuery('')
this.showSearchDialog(false)
},

getActiveTextElement() {
return this.isTextEdit
? document.querySelector('[data-collectives-el="editor"]')
: document.querySelector('[data-collectives-el="reader"]')
},

scrollIntoView() {
document.querySelector('[data-text-el="search-decoration"]')?.scrollIntoView({ block: 'center' })
this.getActiveTextElement()
.querySelector('[data-text-el="search-decoration"]')?.scrollIntoView({ block: 'center' })
},
},
}
Expand Down
35 changes: 29 additions & 6 deletions src/mixins/editorMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { useSearchStore } from '../stores/search.js'
import linkHandlerMixin from '../mixins/linkHandlerMixin.js'
import PageInfoBar from '../components/Page/PageInfoBar.vue'
import { editorApiReaderFileId } from '../constants.js'
import { emit } from '@nextcloud/event-bus'
import { subscribe } from '@nextcloud/event-bus'

export default {
mixins: [
Expand All @@ -37,8 +37,8 @@ export default {
'shareTokenParam',
'showing',
]),
...mapState(useSearchStore, ['searchQuery', 'matchAll']),
...mapState(useCollectivesStore, ['currentCollectiveCanEdit']),
...mapState(useSearchStore, ['searchQuery']),
...mapState(usePagesStore, ['currentPage', 'pageFilePath']),

pageContent() {
Expand All @@ -62,11 +62,33 @@ export default {
},
},

created() {
subscribe('collectives:next-search', () => {
this.editor?.searchNext()
this.reader?.searchNext()
})

subscribe('collectives:previous-search', () => {
this.editor?.searchPrevious()
this.reader?.searchPrevious()
})
},

watch: {
'showOutline'(value) {
this.editor?.setShowOutline(value)
this.reader?.setShowOutline(value)
},
'searchQuery'(value) {
this.editor?.setSearchQuery(value)
this.reader?.setSearchQuery(value)
},
'matchAll'(newValue, oldValue) {
if (newValue !== oldValue) {
this.editor?.setSearchQuery(this.searchQuery, newValue)
this.reader?.setSearchQuery(this.searchQuery, newValue)
}
},
},

beforeDestroy() {
Expand All @@ -76,6 +98,7 @@ export default {

methods: {
...mapActions(useRootStore, ['done', 'hide', 'show']),
...mapActions(useSearchStore, ['showSearchDialog', 'setSearchResults']),

async setupReader() {
const fileId = this.editorApiFlags.includes(editorApiReaderFileId)
Expand Down Expand Up @@ -107,10 +130,10 @@ export default {
const element = document.querySelector(`[href="${document.location.hash}"]`)
element?.click()
}

if (this.searchQuery && this.searchQuery !== '') {
emit('text:editor:search', { query: this.searchQuery })
}
},
onSearch: (results) => {
this.setSearchResults(results)
this.showSearchDialog(true)
},
})

Expand Down
18 changes: 12 additions & 6 deletions src/stores/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,30 @@ export const useSearchStore = defineStore('search', {
state: () => ({
searchQuery: '',
matchAll: true,
shouldShowSearchDialog: false,
results: null,
}),

actions: {
setSearchQuery(query) {
this.searchQuery = query
emit('text:editor:search', { query: this.searchQuery, matchAll: this.matchAll })
},
showSearchDialog(value) {
this.shouldShowSearchDialog = value
},
toggleMatchAll() {
this.matchAll = !this.matchAll
emit('text:editor:search', { query: this.searchQuery, matchAll: this.matchAll })
},
nextSearch() {
setSearchResults(results) {
this.results = results
},
searchNext() {
emit('collectives:next-search')
this.matchAll = false
emit('text:editor:search-next', {})
},
previousSearch() {
searchPrevious() {
emit('collectives:previous-search')
this.matchAll = false
emit('text:editor:search-previous', {})
},
},
})