mirror of https://github.com/rancher/dashboard.git
Merge pull request #9709 from s0nea/string-list-bulk-addition
Add bulk addition to string list component
This commit is contained in:
commit
65a5ad308d
|
|
@ -2,7 +2,7 @@
|
|||
"name": "@rancher/components",
|
||||
"repository": "git://github.com:rancher/dashboard.git",
|
||||
"license": "Apache-2.0",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.4",
|
||||
"module": "./main.js",
|
||||
"main": "./dist/@rancher/components.umd.min.js",
|
||||
"files": [
|
||||
|
|
|
|||
|
|
@ -398,6 +398,276 @@ describe('stringList.vue', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('bulk delimiter', () => {
|
||||
const delimiter = /;/;
|
||||
|
||||
describe('add', () => {
|
||||
const items: string[] = [];
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(StringList, {
|
||||
propsData: {
|
||||
items,
|
||||
bulkAdditionDelimiter: delimiter,
|
||||
errorMessages: { duplicate: 'error, item is duplicate.' },
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should split values if delimiter set', async() => {
|
||||
const value = 'test;test1;test2';
|
||||
const result = ['test', 'test1', 'test2'];
|
||||
|
||||
// activate create mode
|
||||
await wrapper.setData({ isCreateItem: true });
|
||||
const inputField = wrapper.find('[data-testid="item-create"]');
|
||||
|
||||
await inputField.setValue(value);
|
||||
|
||||
// press enter
|
||||
await inputField.trigger('keydown.enter');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
||||
|
||||
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
||||
});
|
||||
|
||||
it('should show warning if one of the values is a duplicate', async() => {
|
||||
const value = 'test;test1;test2';
|
||||
|
||||
await wrapper.setProps({ items: ['test1'] });
|
||||
|
||||
// activate create mode
|
||||
await wrapper.setData({ isCreateItem: true });
|
||||
const inputField = wrapper.find('[data-testid="item-create"]');
|
||||
|
||||
await inputField.setValue(value);
|
||||
|
||||
// press enter
|
||||
await inputField.trigger('keydown.enter');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const isDuplicate = (wrapper.emitted('errors') || [])[0][0].duplicate;
|
||||
|
||||
expect(isDuplicate).toBe(true);
|
||||
});
|
||||
|
||||
it('should show a warning if the new values are all duplicates', async() => {
|
||||
const value = 'test;test';
|
||||
|
||||
// activate create mode
|
||||
await wrapper.setData({ isCreateItem: true });
|
||||
const inputField = wrapper.find('[data-testid="item-create"]');
|
||||
|
||||
await inputField.setValue(value);
|
||||
|
||||
// press enter
|
||||
await inputField.trigger('keydown.enter');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const isDuplicate = (wrapper.emitted('errors') || [])[0][0].duplicate;
|
||||
|
||||
expect(isDuplicate).toBe(true);
|
||||
});
|
||||
|
||||
it('should not consider empty strings at the beginning', async() => {
|
||||
const value = ';test;test1;test2';
|
||||
const result = ['test', 'test1', 'test2'];
|
||||
|
||||
// activate create mode
|
||||
await wrapper.setData({ isCreateItem: true });
|
||||
const inputField = wrapper.find('[data-testid="item-create"]');
|
||||
|
||||
await inputField.setValue(value);
|
||||
|
||||
// press enter
|
||||
await inputField.trigger('keydown.enter');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
||||
|
||||
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
||||
});
|
||||
|
||||
it('should not consider empty strings in the middle', async() => {
|
||||
const value = 'test;test1;;test2';
|
||||
const result = ['test', 'test1', 'test2'];
|
||||
|
||||
// activate create mode
|
||||
await wrapper.setData({ isCreateItem: true });
|
||||
const inputField = wrapper.find('[data-testid="item-create"]');
|
||||
|
||||
await inputField.setValue(value);
|
||||
|
||||
// press enter
|
||||
await inputField.trigger('keydown.enter');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
||||
|
||||
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
||||
});
|
||||
|
||||
it('should not consider empty strings at the end', async() => {
|
||||
const value = 'test;test1;test2;';
|
||||
const result = ['test', 'test1', 'test2'];
|
||||
|
||||
// activate create mode
|
||||
await wrapper.setData({ isCreateItem: true });
|
||||
const inputField = wrapper.find('[data-testid="item-create"]');
|
||||
|
||||
await inputField.setValue(value);
|
||||
|
||||
// press enter
|
||||
await inputField.trigger('keydown.enter');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
||||
|
||||
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
||||
});
|
||||
});
|
||||
|
||||
describe('edit', () => {
|
||||
const items = ['test1', 'test2'];
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(StringList, {
|
||||
propsData: {
|
||||
items,
|
||||
bulkAdditionDelimiter: delimiter,
|
||||
errorMessages: { duplicate: 'error, item is duplicate.' },
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should split values if delimiter set', async() => {
|
||||
const newValue = 'test1;new;values';
|
||||
const result = ['test1', 'new', 'values', 'test2'];
|
||||
|
||||
await wrapper.setData({ editedItem: items[0] });
|
||||
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
||||
|
||||
await inputField.setValue(newValue);
|
||||
|
||||
// press enter
|
||||
await inputField.trigger('keydown.enter');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
||||
|
||||
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
||||
});
|
||||
|
||||
it('should show warning if one of the values is a duplicate', async() => {
|
||||
const newValue = 'test1;test2';
|
||||
|
||||
await wrapper.setData({ editedItem: items[0] });
|
||||
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
||||
|
||||
await inputField.setValue(newValue);
|
||||
|
||||
// press enter
|
||||
await inputField.trigger('keydown.enter');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const isDuplicate = (wrapper.emitted('errors') || [])[0][0].duplicate;
|
||||
|
||||
expect(isDuplicate).toBe(true);
|
||||
});
|
||||
|
||||
it('should show a warning if the new values are all duplicates', async() => {
|
||||
const newValue = 'test;test';
|
||||
|
||||
await wrapper.setData({ editedItem: items[0] });
|
||||
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
||||
|
||||
await inputField.setValue(newValue);
|
||||
|
||||
// press enter
|
||||
await inputField.trigger('keydown.enter');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const isDuplicate = (wrapper.emitted('errors') || [])[0][0].duplicate;
|
||||
|
||||
expect(isDuplicate).toBe(true);
|
||||
});
|
||||
|
||||
it('should not consider empty strings at the beginning', async() => {
|
||||
const newValue = ';test1;new;value';
|
||||
const result = ['test1', 'new', 'value', 'test2'];
|
||||
|
||||
await wrapper.setData({ editedItem: items[0] });
|
||||
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
||||
|
||||
await inputField.setValue(newValue);
|
||||
|
||||
// press enter
|
||||
await inputField.trigger('keydown.enter');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
||||
|
||||
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
||||
});
|
||||
|
||||
it('should not consider empty strings in the middle 1', async() => {
|
||||
const newValue = 'test1; ;new;value';
|
||||
const result = ['test1', 'new', 'value', 'test2'];
|
||||
|
||||
await wrapper.setData({ editedItem: items[0] });
|
||||
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
||||
|
||||
await inputField.setValue(newValue);
|
||||
|
||||
// press enter
|
||||
await inputField.trigger('keydown.enter');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
||||
|
||||
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
||||
});
|
||||
|
||||
it('should not consider empty strings in the middle 2', async() => {
|
||||
const newValue = 'test1;;new;value';
|
||||
const result = ['test1', 'new', 'value', 'test2'];
|
||||
|
||||
await wrapper.setData({ editedItem: items[0] });
|
||||
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
||||
|
||||
await inputField.setValue(newValue);
|
||||
|
||||
// press enter
|
||||
await inputField.trigger('keydown.enter');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
||||
|
||||
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
||||
});
|
||||
|
||||
it('should not consider empty strings at the end', async() => {
|
||||
const newValue = 'test1;new;value;';
|
||||
const result = ['test1', 'new', 'value', 'test2'];
|
||||
|
||||
await wrapper.setData({ editedItem: items[0] });
|
||||
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
||||
|
||||
await inputField.setValue(newValue);
|
||||
|
||||
// press enter
|
||||
await inputField.trigger('keydown.enter');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
||||
|
||||
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('errors handling', () => {
|
||||
it('show duplicate warning icon when errorMessages is defined', async() => {
|
||||
const items = ['test'];
|
||||
|
|
|
|||
|
|
@ -82,6 +82,13 @@ export default Vue.extend({
|
|||
return {} as ErrorMessages;
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Enables bulk addition and defines the delimiter to split the input string.
|
||||
*/
|
||||
bulkAdditionDelimiter: {
|
||||
type: RegExp,
|
||||
default: null,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -125,13 +132,9 @@ export default Vue.extend({
|
|||
},
|
||||
|
||||
methods: {
|
||||
onChange(value: string) {
|
||||
onChange(value: string, index?: number) {
|
||||
this.value = value;
|
||||
|
||||
const items = [
|
||||
...this.items,
|
||||
this.value
|
||||
];
|
||||
const items = this.addValueToItems(this.items, value, index);
|
||||
|
||||
this.toggleError(
|
||||
'duplicate',
|
||||
|
|
@ -321,10 +324,7 @@ export default Vue.extend({
|
|||
const value = this.value?.trim();
|
||||
|
||||
if (value) {
|
||||
const items = [
|
||||
...this.items,
|
||||
value,
|
||||
];
|
||||
const items = this.addValueToItems(this.items, value);
|
||||
|
||||
if (!hasDuplicatedStrings(items, this.caseSensitive)) {
|
||||
this.updateItems(items);
|
||||
|
|
@ -343,12 +343,8 @@ export default Vue.extend({
|
|||
const value = this.value?.trim();
|
||||
|
||||
if (value) {
|
||||
const items = [...this.items];
|
||||
const index = findStringIndex(items, item, false);
|
||||
|
||||
if (index !== -1) {
|
||||
items[index] = value;
|
||||
}
|
||||
const index = findStringIndex(this.items, item, false);
|
||||
const items = index !== -1 ? this.addValueToItems(this.items, value, index) : this.items;
|
||||
|
||||
if (!hasDuplicatedStrings(items, this.caseSensitive)) {
|
||||
this.updateItems(items);
|
||||
|
|
@ -360,6 +356,49 @@ export default Vue.extend({
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a new or update an exiting item in the items list.
|
||||
*
|
||||
* @param items The current items list.
|
||||
* @param value The new value to be added.
|
||||
* @param index The list index of the item to be updated (optional).
|
||||
* @returns Updated items list.
|
||||
*/
|
||||
addValueToItems(items: string[], value: string, index?: number): string[] {
|
||||
const updatedItems = [...items];
|
||||
|
||||
// Add new item
|
||||
if (index === undefined) {
|
||||
if (this.bulkAdditionDelimiter && value.search(this.bulkAdditionDelimiter) >= 0) {
|
||||
updatedItems.push(...this.splitBulkValue(value));
|
||||
} else {
|
||||
updatedItems.push(value);
|
||||
}
|
||||
} else { // Update existing item
|
||||
if (this.bulkAdditionDelimiter && value.search(this.bulkAdditionDelimiter) >= 0) {
|
||||
updatedItems.splice(index, 1, ...this.splitBulkValue(value));
|
||||
} else {
|
||||
updatedItems[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return updatedItems;
|
||||
},
|
||||
|
||||
/**
|
||||
* Split the value by the defined delimiter and remove empty strings.
|
||||
*
|
||||
* @param value The value to be split.
|
||||
* @returns Array containing split values.
|
||||
*/
|
||||
splitBulkValue(value: string): string[] {
|
||||
return value
|
||||
.split(this.bulkAdditionDelimiter)
|
||||
.filter((item) => {
|
||||
return item.trim().length > 0;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove an item from items list
|
||||
*/
|
||||
|
|
@ -393,7 +432,7 @@ export default Vue.extend({
|
|||
@dblclick="onClickEmptyBody()"
|
||||
>
|
||||
<div
|
||||
v-for="item in items"
|
||||
v-for="(item, index) in items"
|
||||
:key="item"
|
||||
:ref="item"
|
||||
:class="{
|
||||
|
|
@ -421,7 +460,7 @@ export default Vue.extend({
|
|||
:data-testid="`item-edit-${item}`"
|
||||
class="edit-input static"
|
||||
:value="value != null ? value : item"
|
||||
@input="onChange($event)"
|
||||
@input="onChange($event, index)"
|
||||
@blur.prevent="updateItem(item)"
|
||||
@keydown.native.enter="updateItem(item, !errors.duplicate)"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import { Canvas, Meta, Story, ArgsTable, Source } from '@storybook/addon-docs';
|
|||
import StringList from '@/pkg/rancher-components/src/components/StringList/StringList';
|
||||
import { useArgs } from '@storybook/client-api';
|
||||
|
||||
<Meta
|
||||
title="Components/StringList"
|
||||
<Meta
|
||||
title="Components/StringList"
|
||||
component={StringList}
|
||||
argTypes={{
|
||||
actionsPosition: {
|
||||
|
|
@ -57,6 +57,7 @@ It can be used to handle a list of strings.
|
|||
- An item can be edited by double click on it.
|
||||
- In case of the item name is already in the list, an error message is displayed on the bottom of the box.
|
||||
- Select an item and then click on minus button to remove it from the list.
|
||||
- If a bulk addition delimiter is set, the input value will be split into individual entries.
|
||||
|
||||
<br/>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue