Skip to content

Commit

Permalink
feat(kmultiselect): add search-placeholder prop [KHCP-13352] (#2419)
Browse files Browse the repository at this point in the history
  • Loading branch information
portikM authored Sep 30, 2024
1 parent 2851b6d commit 4062052
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 13 deletions.
55 changes: 55 additions & 0 deletions docs/components/multiselect.md
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,55 @@ const debouncedHandler = debounce(function (val) {
</script>
```

### placeholder

Text to be displayed inside of the dropdown trigger element (only applies when [`collapsedContext` prop](#collapsedcontext) is `false`). See [`searchPlaceholder` prop](#searchplaceholder) if you want to set a placeholder in the dropdown search input.

<KMultiselect
placeholder="Select one or more items"
:items="deepClone(defaultItemsUnselect)"
/>

```html
<KMultiselect
placeholder="Select one or more items"
:items="items"
/>
```

### searchPlaceholder

Search input placeholder (when [`collapsedContext` prop](#collapsedcontext) is `false`, acts as placeholder in dropdown input).

<div class="vertical-container">
<KMultiselect
label="Multiselect with a placeholder"
search-placeholder="Search"
:items="deepClone(defaultItemsUnselect)"
/>

<KMultiselect
label="Multiselect with a placeholder (collapsedContext)"
collapsed-context
search-placeholder="Search"
:items="deepClone(defaultItemsUnselect)"
/>
</div>

```html
<KMultiselect
search-placeholder="Search"
label="Multiselect with a placeholder"
:items="items"
/>
<KMultiselect
search-placeholder="Search"
collapsed-context
label="Multiselect with a placeholder (collapsedContext)"
:items="items"
/>
```

## Attribute Binding

You can pass any input attribute and it will get properly bound to the element.
Expand Down Expand Up @@ -1105,4 +1154,10 @@ export default defineComponent({
color: $kui-color-text-neutral;
}
}

.vertical-container {
display: flex;
flex-direction: column;
gap: $kui-space-50;
}
</style>
7 changes: 4 additions & 3 deletions sandbox/pages/SandboxMultiselect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,14 @@
<KMultiselect
:items="multiselectItemsUnselected"
label="I have a placeholder"
placeholder="Placeholder"
placeholder="Trigger element placeholder"
search-placeholder="Search placeholder"
/>
<KMultiselect
collapsed-context
:items="multiselectItemsUnselected"
label="I have a placeholder"
placeholder="Placeholder"
label="I have a placeholder (collapsedContext)"
search-placeholder="Search placeholder"
/>
</SandboxSectionComponent>

Expand Down
43 changes: 40 additions & 3 deletions src/components/KMultiselect/KMultiselect.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,27 +352,64 @@ describe('KMultiselect', () => {
})
})

it('only shows placeholder when collapsedContext is true', () => {
it('displays placeholder and searchPlaceholder props correctly', () => {
const labels = ['Label 1', 'Label 2']
const vals = ['label1', 'label2']
const placeholder = 'Select something'
const searchPlaceholder = 'Search here'

mount(KMultiselect, {
props: {
placeholder,
searchPlaceholder,
items: [{
label: labels[0],
value: vals[0],
}, {
label: labels[1],
value: vals[1],
}],
},
})

cy.getTestId('selection-badges-container').should('not.exist')
cy.get('.expanded-selection-empty').should('be.visible').should('contain.text', placeholder)

cy.getTestId('multiselect-trigger').click()
cy.getTestId('multiselect-dropdown-input').should('have.attr', 'placeholder', searchPlaceholder)

cy.get('.multiselect-item').eq(0).click()
cy.get('.expanded-selection-empty').should('not.exist')
cy.getTestId('selection-badges-container').should('be.visible')
})

it('handles searchPlaceholder prop correctly when collapsedContext is true', () => {
const labels = ['Label 1', 'Label 2']
const vals = ['label1', 'label2']
const searchPlaceholder = 'Search here'

mount(KMultiselect, {
props: {
collapsedContext: true,
searchPlaceholder,
items: [{
label: labels[0],
value: vals[0],
selected: true,
}, {
label: labels[1],
value: vals[1],
selected: true,
}],
},
})

cy.getTestId('selection-badges-container').should('not.exist')

cy.get('.multiselect-trigger input').should('have.attr', 'placeholder', searchPlaceholder)

cy.getTestId('multiselect-trigger').click()
cy.get('.multiselect-item').eq(0).click()
cy.get('.multiselect-item').eq(1).click()

cy.get('.multiselect-trigger input').should('have.attr', 'placeholder', '2 items selected')
})

Expand Down
23 changes: 16 additions & 7 deletions src/components/KMultiselect/KMultiselect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
data-testid="multiselect-input"
:disabled="isDisabled"
:model-value="filterString"
:placeholder="placeholderText"
:placeholder="triggerElementText"
:readonly="isReadonly ? true : undefined"
type="text"
@blur="() => isFocused = false"
Expand All @@ -70,7 +70,7 @@
v-else-if="!selectedItems.length"
class="expanded-selection-empty"
>
{{ selectedItemsText }}
{{ triggerElementText }}
</div>
<div
v-else
Expand Down Expand Up @@ -164,7 +164,7 @@
class="multiselect-dropdown-input"
data-testid="multiselect-dropdown-input"
:model-value="filterString"
:placeholder="placeholder ? placeholder : 'Filter...'"
:placeholder="searchPlaceholder || DEFAULT_SEARCH_PLACEHOLDER"
type="text"
@click.stop
@focus="triggerInitialFocus"
Expand Down Expand Up @@ -322,6 +322,7 @@ const slots = useSlots()
const { getSizeFromString, cloneDeep, stripRequiredLabel } = useUtilities()
const SELECTED_ITEMS_SINGLE_LINE_HEIGHT = 36
const DEFAULT_SEARCH_PLACEHOLDER = 'Filter...'
const props = defineProps({
modelValue: {
Expand All @@ -348,6 +349,10 @@ const props = defineProps({
type: String,
default: '',
},
searchPlaceholder: {
type: String,
default: '',
},
kpopAttributes: {
type: Object,
default: () => ({
Expand Down Expand Up @@ -594,11 +599,15 @@ const numericWidthStyle = computed(() => {
}
})
const placeholderText = computed(() => {
return selectedItems.value.length ? selectedItemsText.value : props.placeholder || 'Filter...'
})
const triggerElementText = computed((): string => {
if (selectedItems.value.length === 0) {
if (!props.collapsedContext && props.placeholder) {
return props.placeholder
} else if (props.collapsedContext && props.searchPlaceholder) {
return props.searchPlaceholder
}
}
const selectedItemsText = computed((): string => {
if (selectedItems.value.length === 1) {
return `${selectedItems.value.length} item selected`
}
Expand Down

0 comments on commit 4062052

Please sign in to comment.