Merge branch 'master' into fix-avatar-seeding

This commit is contained in:
mahula 2023-04-27 11:50:49 +02:00 committed by GitHub
commit 4b29b271c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 127 additions and 63 deletions

View File

@ -223,8 +223,7 @@ export default {
},
searchResults: async (_parent, args, context, _resolveInfo) => {
const { query, limit } = args
let userId = null
if (context.user) userId = context.user.id
const userId = context.user?.id || null
const searchType = query.replace(/^([!@#&]?).*$/, '$1')
const searchString = query.replace(/^([!@#&])/, '')

View File

@ -33,7 +33,7 @@ const matchSomeWordsExactly = (str, boost = 2) => {
const matchBeginningOfWords = (str) => {
return str
.split(' ')
.filter((s) => s.length > 3)
.filter((s) => s.length >= 2)
.map((s) => s + '*')
.join(' ')
}

View File

@ -37,7 +37,7 @@ describe('queryString', () => {
describe('globbing for longer words', () => {
it('globs words with more than three characters', () => {
expect(queryString('a couple of words')).toContain('couple* words*')
expect(queryString('a couple of words')).toContain('couple* of* words*')
})
})
})

View File

@ -65,10 +65,10 @@ services:
backend:
image: ocelotsocialnetwork/backend-branded:local-${CONFIGURATION}
container_name: backend
container_name: backend-branded
build:
dockerfile: src/docker/backend.Dockerfile
target: branded-branded
target: branded
context: .
args:
- CONFIGURATION=$CONFIGURATION
@ -143,7 +143,7 @@ services:
neo4j:
image: ocelotsocialnetwork/neo4j-community:latest
container_name: neo4j
container_name: neo4j-branded
networks:
- test-network
volumes:

View File

@ -43,6 +43,14 @@ export default {
selectedCategoryIds: this.existingCategoryIds,
}
},
watch: {
existingCategoryIds() {
if (!this.selectedCategoryIds.length && this.existingCategoryIds.length) {
this.selectedCategoryIds = this.existingCategoryIds
this.$forceUpdate()
}
},
},
computed: {
selectedCount() {
return this.selectedCategoryIds.length

View File

@ -177,6 +177,15 @@ export default {
groupName() {
return this.group && this.group.name
},
groupCategories() {
return this.group && this.group.categories
},
},
watch: {
groupCategories() {
if (!this.formData.categoryIds.length && this.groupCategories)
this.formData.categoryIds = this.groupCategories.map((cat) => cat.id)
},
},
methods: {
submit() {

View File

@ -37,8 +37,6 @@
<ds-text class="select-label">
{{ $t('group.type') }}
</ds-text>
<!-- TODO: change it has to be implemented later -->
<!-- TODO: move 'ds-select' from style guide to main code and implement missing translation etc. functionality -->
<select
class="select ds-input appearance--auto"
name="groupType"
@ -94,21 +92,10 @@
<ds-text class="select-label">
{{ $t('group.actionRadius') }}
</ds-text>
<!-- TODO: move 'ds-select' from styleguide to main code and implement missing translation etc. functionality -->
<select
class="select ds-input appearance--auto"
name="actionRadius"
:value="formData.actionRadius"
@change="changeActionRadius($event)"
>
<option
v-for="actionRadius in actionRadiusOptions"
:key="actionRadius"
:value="actionRadius"
>
{{ $t(`group.actionRadii.${actionRadius}`) }}
</option>
</select>
<action-radius-select
v-model="formData.actionRadius"
@change.native="changeActionRadius($event)"
/>
<ds-chip
size="base"
:color="
@ -123,6 +110,7 @@
</ds-chip>
<!-- location -->
<!-- TODO: move 'ds-select' from styleguide to main code and implement missing translation etc. functionality -->
<ds-select
id="city"
:label="$t('settings.data.labelCity') + locationNameLabelAddOnOldName"
@ -187,6 +175,7 @@ import {
DESCRIPTION_WITHOUT_HTML_LENGTH_MIN,
} from '~/constants/groups.js'
import Editor from '~/components/Editor/Editor'
import ActionRadiusSelect from '~/components/Select/ActionRadiusSelect'
import { queryLocations } from '~/graphql/location'
let timeout
@ -196,6 +185,7 @@ export default {
components: {
CategoriesSelect,
Editor,
ActionRadiusSelect,
},
props: {
update: {
@ -216,7 +206,6 @@ export default {
categoriesActive: this.$env.CATEGORIES_ACTIVE,
disabled: false,
groupTypeOptions: ['public', 'closed', 'hidden'],
actionRadiusOptions: ['regional', 'national', 'continental', 'global'],
loadingGeo: false,
cities: [],
formData: {
@ -327,10 +316,10 @@ export default {
return false
},
changeGroupType(event) {
this.formData.groupType = event.target.value
this.$refs.groupForm.update('groupType', event.target.value)
},
changeActionRadius(event) {
this.formData.actionRadius = event.target.value
this.$refs.groupForm.update('actionRadius', event.target.value)
},
updateEditorDescription(value) {
this.$refs.groupForm.update('description', value)

View File

@ -0,0 +1,37 @@
import { shallowMount } from '@vue/test-utils'
import ActionRadiusSelect from './ActionRadiusSelect'
const localVue = global.localVue
const propsData = { value: 'regional' }
describe('ActionRadiusSelect.', () => {
let wrapper
let mocks
beforeEach(() => {
mocks = {
$t: jest.fn(),
}
})
describe('mount', () => {
const Wrapper = () => {
return shallowMount(ActionRadiusSelect, { propsData, mocks, localVue })
}
beforeEach(() => {
wrapper = Wrapper()
})
it('renders the select', () => {
expect(wrapper.findComponent(ActionRadiusSelect).exists()).toBe(true)
})
describe('when an option is selected', () => {
it('emits a change event with the new value', () => {
const select = wrapper.find('select')
select.trigger('change')
expect(wrapper.emitted().change[0]).toEqual(['regional'])
})
})
})
})

View File

@ -0,0 +1,33 @@
<template>
<select
class="select ds-input appearance--auto"
name="actionRadius"
:value="value"
@change="onActionRadiusChange"
>
<option v-for="actionRadius in actionRadiusOptions" :key="actionRadius" :value="actionRadius">
{{ $t(`group.actionRadii.${actionRadius}`) }}
</option>
</select>
</template>
<script>
export default {
name: 'ActionRadiusSelect',
props: {
value: {
required: true,
},
},
data() {
return {
actionRadiusOptions: ['regional', 'national', 'continental', 'global'],
}
},
methods: {
onActionRadiusChange(event) {
this.$emit('change', event.target.value)
},
},
}
</script>

View File

@ -63,13 +63,6 @@ describe('SearchableInput.vue', () => {
expect(select.element.value).toBe('abcd')
})
it('calls onDelete when the delete key is pressed', () => {
const spy = jest.spyOn(wrapper.vm, 'onDelete')
select.trigger('input')
select.trigger('keyup.delete')
expect(spy).toHaveBeenCalledTimes(1)
})
describe('navigating to resource', () => {
beforeEach(() => {
propsData = { options: searchResults }

View File

@ -1,9 +1,10 @@
<template>
<div class="searchable-input" aria-label="search" role="search">
<ds-select
ref="select"
type="search"
icon="search"
v-model="searchValue"
v-model="value"
:id="id"
label-prop="id"
:icon-right="null"
@ -11,12 +12,11 @@
:loading="loading"
:filter="(item) => item"
:no-options-available="emptyText"
:auto-reset-search="!searchValue"
:auto-reset-search="!value"
:placeholder="$t('search.placeholder')"
@focus.capture.native="onFocus"
@input.native="handleInput"
@input.native="onInput"
@keyup.enter.native="onEnter"
@keyup.delete.native="onDelete"
@keyup.esc.native="clear"
@blur.capture.native="onBlur"
@input.exact="onSelect"
@ -77,23 +77,29 @@ export default {
},
data() {
return {
searchValue: '',
value: '',
unprocessedSearchInput: '',
searchProcess: null,
previousSearchTerm: '',
delay: 300,
}
},
computed: {
emptyText() {
return this.isActive && !this.loading ? this.$t('search.failed') : this.$t('search.hint')
return !this.loading && this.isSearchable()
? this.$t('search.failed')
: this.$t('search.hint')
},
isActive() {
return !isEmpty(this.previousSearchTerm)
return !isEmpty(this.value)
},
},
methods: {
isSearchable() {
return (
!isEmpty(this.value) &&
typeof this.value === 'string' &&
this.value.replace(/\s+/g, '').length >= 3
)
},
isFirstOfType(option) {
return (
this.options.findIndex((o) => o === option) ===
@ -103,49 +109,36 @@ export default {
onFocus(event) {
clearTimeout(this.searchProcess)
},
handleInput(event) {
onInput(event) {
clearTimeout(this.searchProcess)
this.value = event.target ? event.target.value.replace(/\s+/g, ' ').trim() : ''
this.unprocessedSearchInput = this.value
if (isEmpty(this.value) || this.value.replace(/\s+/g, '').length < 3) {
if (!this.isSearchable()) {
this.$emit('clearSearch')
return
}
this.searchProcess = setTimeout(() => {
this.previousSearchTerm = this.value
this.$emit('query', this.value)
}, this.delay)
},
onEnter(event) {
this.$router.push({
path: '/search/search-results',
query: { search: this.unprocessedSearchInput },
query: { search: this.value },
})
this.$emit('clearSearch')
},
onDelete(event) {
clearTimeout(this.searchProcess)
const value = event.target ? event.target.value.trim() : ''
if (isEmpty(value)) {
this.clear()
} else {
this.handleInput(event)
}
this.$refs.select.close()
},
clear() {
this.unprocessedSearchInput = ''
this.previousSearchTerm = ''
this.searchValue = ''
this.value = ''
this.$emit('clearSearch')
clearTimeout(this.searchProcess)
},
onBlur(event) {
this.searchValue = this.previousSearchTerm
clearTimeout(this.searchProcess)
},
onSelect(item) {
this.goToResource(item)
this.$nextTick(() => {
this.searchValue = this.previousSearchTerm
this.value = this.$refs.select.$data.searchString
})
},
getRouteName(item) {

View File

@ -207,6 +207,9 @@ export const groupMembersQuery = () => {
name
slug
myRoleInGroup
avatar {
url
}
}
}
`