Skip to content

Commit

Permalink
Merge branch 'beta' into fix/khcp-3431-kpagination-fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
adamdehaven authored Apr 20, 2022
2 parents 7b1f9d4 + c69de57 commit 4d7c6a7
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 40 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# [7.0.0-beta.16](https://github.com/Kong/kongponents/compare/v7.0.0-beta.15...v7.0.0-beta.16) (2022-04-20)


### Bug Fixes

* **kselect:** page jump, caret, and docs ([#597](https://github.com/Kong/kongponents/issues/597)) ([4110bcc](https://github.com/Kong/kongponents/commit/4110bcc7b4035a8062f458fd480465477a2befc7))

# [7.0.0-beta.15](https://github.com/Kong/kongponents/compare/v7.0.0-beta.14...v7.0.0-beta.15) (2022-04-19)


Expand Down
139 changes: 114 additions & 25 deletions docs/components/select.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,30 +204,6 @@ You can configure the button text when an item is selected, if `appearance` is t

<KSelect appearance='button' width="225" @selected="item => handleItemSelect(item)" :buttonText="`Show ${mySelect} per page`" :items="items" />

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

export default defineComponent({
data() {
return {
mySelect: '',
items: [{
label: '25',
value: '25'
}, {
label: '50',
value: '50'
}]
}
},
methods: {
handleItemSelect (item) {
this.mySelect = item.label
}
}
})
</script>

```html
<KSelect
appearance='button'
Expand Down Expand Up @@ -264,7 +240,7 @@ export default defineComponent({

### width

You can pass a `width` string for dropdown. By default the `width` is `170px`. This is the width of the input, dropdown, and selected item.
You can pass a `width` string for dropdown. By default the `width` is `200px`. This is the width of the input, dropdown, and selected item.

<KSelect width="350" :items="[{
label: 'test',
Expand Down Expand Up @@ -292,6 +268,17 @@ You can pass a `width` string for dropdown. By default the `width` is `170px`. T

Use fixed positioning of the popover to avoid content being clipped by parental boundaries - defaults to `true`. See [`KPop` docs](popover.html#positionfixed) for more information.

### filterFunc

Use this prop to override the default filter function if you want to do something like filter on an attribute other than `label`. Your filter function
should take as parameter a JSON object containing the items to filter on (`items`) and the query string (`query`) and return the filtered items. See [Slots](#slots) for an example.

```js
myCustomFilter ({ items, query }) {
return items.filter(anItem => anItem.label.includes(query))
}
```

### v-model

KSelect works as regular inputs do using v-model for data binding:
Expand Down Expand Up @@ -336,10 +323,112 @@ You can pass any input attribute and it will get properly bound to the element.
<KSelect disabled placeholder="type something" :items="[{ label: 'test', value: 'test' }]" />
```

## Slots

You can use the `item-template` slot to customize the look and feel of your items. Use slots to gain access to the `item` data.

<KSelect :items="myItems" width="500" :filterFunc="customFilter">
<template v-slot:item-template="{ item }">
<div class="select-item-label">{{ item.label }}</div>
<div class="select-item-desc">{{ item.description }}</div>
</template>
</KSelect>

```html
<KSelect :items="myItems" width="500" :filterFunc="customFilter">
<template v-slot:item-template="{ item }">
<div class="select-item-label">{{item.label}}</div>
<div class="select-item-desc">{{item.description}}</div>
</template>
</KSelect>

<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
data() {
return {
myItems: this.getItems(5),
}
},
methods: {
getItems(count) {
let myItems = []
for (let i = 0; i < count; i++) {
myItems.push({
label: "Item " + i,
value: "item_" + i,
description: "The item's description for number " + i
})
}
return myItems
},
customFilter (items, queryStr) {
return items.filter(item => {
return item.label.toLowerCase().includes(queryStr.toLowerCase()) ||
item.description.toLowerCase().includes(queryStr.toLowerCase())
})
}
}
})
</script>
```

## Events

| Event | returns |
| :-------- | :------------------ |
| `selected` | `selectedItem` Object |
| `input` | `selectedItem` Object or null |
| `change` | `selectedItem` Object or null |

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

function getItems(count) {
let myItems = []
for (let i = 0; i < count; i++) {
myItems.push({
label: "Item " + i,
value: "item_" + i,
description: "The item's description for number " + i
})
}
return myItems
}

export default defineComponent({
data() {
return {
myItems: getItems(5),
mySelect: '',
items: [{
label: '25',
value: '25'
}, {
label: '50',
value: '50'
}]
}
},
methods: {
handleItemSelect (item) {
this.mySelect = item.label
},
customFilter ({items, query}) {
return items.filter(item => item.label.toLowerCase().includes(query.toLowerCase()) || item.description.toLowerCase().includes(query.toLowerCase()))
}
}
})
</script>

<style lang="scss">
.select-item-label {
color: blue;
font-weight: bold;
}

.select-item-desc {
color: red;
}
</style>
23 changes: 23 additions & 0 deletions src/components/KSelect/KSelect.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/// <reference types="../../cypress/support" />

import { mount } from '@cypress/vue'
import { h } from 'vue'
import KSelect from '@/components/KSelect/KSelect.vue'

/**
Expand Down Expand Up @@ -170,4 +171,26 @@ describe('KSelect', () => {
cy.getTestId(`k-select-item-${vals[0]}`).click({ multiple: true, force: true })
cy.get('.selected-item-label').should('contain.text', labels[0])
})

it('allows slotting content into the items', async () => {
const itemSlotContent = 'I am slotted baby!'
const itemLabel = 'Label 1'
const itemValue = 'label1'

mount(KSelect, {
props: {
testMode: true,
appearance: 'button',
items: [{
label: itemLabel,
value: itemValue,
}],
},
slots: {
'item-template': h('span', {}, itemSlotContent),
},
})

cy.getTestId(`k-select-item-${itemValue}`).should('contain.text', itemSlotContent)
})
})
49 changes: 36 additions & 13 deletions src/components/KSelect/KSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,21 @@
:is-open="isToggled.value"
name="items"
>
<div v-if="filteredItems.length">
<KSelectItem
v-for="item in filteredItems"
:key="item.key"
:item="item"
@selected="handleItemSelect"
/>
</div>
<KSelectItem
v-else
v-for="item in filteredItems"
:key="item.key"
:item="item"
@selected="handleItemSelect"
>
<template #content>
<slot
:item="item"
name="item-template"
/>
</template>
</KSelectItem>
<KSelectItem
v-if="!filteredItems.length"
key="k-select-empty-state"
:item="{ label: 'No results', value: 'no_results' }"
class="k-select-empty-item"
Expand Down Expand Up @@ -152,13 +157,18 @@ const defaultKPopAttributes = {
hideCaret: true,
}
interface SelectItem {
export interface SelectItem {
label: string
value: string | number
key?: string
selected?: boolean
}
export interface SelectFilterFnParams {
items: SelectItem[]
query: string
}
export default defineComponent({
name: 'KSelect',
components: {
Expand Down Expand Up @@ -238,6 +248,13 @@ export default defineComponent({
type: Boolean,
default: true,
},
/**
* Override default filter functionality of case-insensitive search on label
*/
filterFunc: {
type: Function,
default: (params: SelectFilterFnParams) => params.items.filter((item: SelectItem) => item.label.toLowerCase().includes(params.query.toLowerCase())),
},
/**
* Test mode - for testing only, strips out generated ids
*/
Expand All @@ -258,9 +275,9 @@ export default defineComponent({
const widthValue = computed(() => {
let w
if (!props.width) {
w = 170
w = 205
if (props.appearance === 'button') {
w = 200
w = 230
}
} else {
w = props.width
Expand All @@ -287,7 +304,7 @@ export default defineComponent({
// TypeScript complains if I bind the original object
const boundKPopAttributes = computed(() => ({ ...createKPopAttributes.value }))
const filteredItems = computed(() => selectItems.value.filter((item: SelectItem) => item.label.toLowerCase().includes(filterStr.value.toLowerCase())))
const filteredItems = computed(() => props.filterFunc({ items: selectItems.value, query: filterStr.value }))
const placeholderText = computed((): string => {
if (props.placeholder) {
Expand Down Expand Up @@ -450,6 +467,12 @@ export default defineComponent({
margin-left: auto;
}
.k-select-button {
:deep(.k-button.btn-link):hover {
text-decoration: none;
}
}
:deep(.k-input) { // need this so input takes the
width: 100%; // k-input-wrapper's width which uses this.width prop
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/KSelect/KSelectItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<li
:key="item.key"
:data-testid="`k-select-item-${item.value}`"
class="k-select-item"
class="k-select-item mx-4"
@click="handleClick"
>
<div
Expand All @@ -14,7 +14,7 @@
:value="item.value"
>
<span class="k-select-item-label mr-2">
<slot>{{ item.label }}</slot>
<slot name="content">{{ item.label }}</slot>
</span>
<span class="k-select-selected-icon-container">
<KIcon
Expand Down

0 comments on commit 4d7c6a7

Please sign in to comment.