Skip to content

Commit

Permalink
feat(refs DPLAN-12968): introduce condensed search field [vue 3] (1156)
Browse files Browse the repository at this point in the history
* feat(refs DPLAN-12968, #AB21840): introduce condensed search field

search button is now an icon-only button that is attached to the right
side of the search field

* test(refs DPLAN-12968, #AB21840): add unit test

* docs(refs DPLAN-12968, #AB21840): add changelog entry

* style(refs DPLAN-12968): use demosplan-ui translations

* fix(refs DPLAN-12968): remove duplicate button
  • Loading branch information
hwiem authored Feb 4, 2025
1 parent f3e47c5 commit cfc3b2c
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Since v0.0.10, this Changelog is formatted according to the [Common Changelog][c
## UNRELEASED

### Changed
- ([#1156](https://github.com/demos-europe/demosplan-ui/pull/1156)) DpSearchField: Introduce condensed search field with attached search button ([@hwiem](https://github.com/hwiem))
- ([#1074](https://github.com/demos-europe/demosplan-ui/pull/1080)) DpEditableList: Use DpButton instead of buttons and use new icons ([@gruenbergerdemos](https://github.com/gruenbergerdemos))

### Fixed
Expand Down
19 changes: 12 additions & 7 deletions src/components/DpResettableInput/DpResettableInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
icon="xmark"
:size="iconSize" />
</button>
<!-- Slot for additional buttons -->
<slot />
</div>
</template>

Expand All @@ -39,12 +41,6 @@ export default {
},
props: {
dataCy: {
type: String,
required: false,
default: ''
},
/**
* By default, the normal variant is used. If set to 'small', a smaller variant is displayed
*/
Expand All @@ -55,6 +51,12 @@ export default {
validator: (prop) => ['small', 'medium'].includes(prop)
},
dataCy: {
type: String,
required: false,
default: ''
},
defaultValue: {
type: String,
required: false,
Expand Down Expand Up @@ -99,7 +101,10 @@ export default {
computed: {
buttonClass () {
return this.buttonVariant === 'small' ? 'o-form__control-search-reset--small' : 'o-form__control-search-reset'
let classes = this.buttonVariant === 'small' ? 'o-form__control-search-reset--small' : 'o-form__control-search-reset'
classes = this.$slots.default ? `${classes} grouped` : classes
return classes
},
iconSize () {
Expand Down
33 changes: 19 additions & 14 deletions src/components/DpSearchField/DpSearchField.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
<template>
<span :class="{ 'inline-block w-full': inputWidth !== ''}">
<span
class="inline-flex"
:class="{ 'w-full': inputWidth !== ''}">
<dp-resettable-input
id="searchField"
data-cy="searchField"
:class="cssClasses"
:input-attributes="{ placeholder: translations.search, type: 'search' }"
:input-attributes="{ placeholder: translations.search, type: 'search'}"
@reset="handleReset"
@enter="handleSearch"
v-model="searchTerm" /><!--
v-model="searchTerm">
<!-- Slot for additional buttons -->
<slot />
</dp-resettable-input>

--><dp-button
class="align-top"
<dp-button
class="search rounded-r-md rounded-l-none"
data-cy="handleSearch"
@click="handleSearch"
:text="translations.searching" />
hide-text
icon="search"
:text="translations.searching"
variant="outline"
@click="handleSearch" />
</span>
</template>

Expand Down Expand Up @@ -66,7 +74,9 @@ export default {
computed: {
cssClasses () {
return this.inputWidth !== '' ? `inline-block u-mr-0_5 ${this.inputWidth}` : 'inline-block u-mr-0_5'
const classes = 'inline-block rounded-r-none'
return this.inputWidth !== '' ? `${classes} ${this.inputWidth}` : classes
}
},
Expand All @@ -79,7 +89,7 @@ export default {
* The empty string is emitted to stick to only one type.
*/
if (this.searchTermApplied !== this.searchTerm) {
this.$emit('reset', '')
this.$emit('reset')
this.searchTermApplied = ''
}
},
Expand All @@ -92,11 +102,6 @@ export default {
this.searchTermApplied = this.searchTerm
this.$emit('search', this.searchTerm)
},
reset () {
this.searchTermApplied = ''
this.searchTerm = ''
}
},
Expand Down
58 changes: 58 additions & 0 deletions tests/DpSearchField.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { afterEach, beforeEach, expect, it } from '@jest/globals'
import DpButton from '~/components/DpButton'
import DpSearchField from '~/components/DpSearchField'
import { mount } from '@vue/test-utils'

describe('DpSearchField', () => {
let wrapper
const mocks = {
Translator: {
trans: jest.fn((key => key))
}
}

beforeEach(() => {
wrapper = mount(DpSearchField, {
components: {
DpButton
},
mocks,
stubs: {
DpResettableInput: true
}
})
})

afterEach(() => {
wrapper.destroy()
})

it('emits a search event with the search term when search button is clicked and search term is changed', async() => {
wrapper.setData({ searchTerm: 'test search' })
await wrapper.find('[data-cy="handleSearch"]').trigger('click')

expect(wrapper.emitted().search[0]).toEqual(['test search'])
})

it('does not emit a search event when search button is clicked and search term is unchanged', async () => {
wrapper.setData({ searchTerm: 'test search' })
wrapper.setData({ searchTermApplied: 'test search' })
await wrapper.find('[data-cy="handleSearch"]').trigger('click')

expect(wrapper.emitted().search).toBeUndefined()
})

it('emits a reset event when search field is reset', async() => {
wrapper.setData({ searchTerm: 'test search' })
wrapper.setData({ searchTermApplied: 'test search' })
await wrapper.vm.handleReset()

expect(wrapper.emitted().reset[0]).toBeDefined()
})

it('does not emit a reset event when search field is reset but no search term has been entered', async() => {
await wrapper.vm.handleReset()

expect(wrapper.emitted().reset).toBeUndefined()
})
})

0 comments on commit cfc3b2c

Please sign in to comment.