diff --git a/shell/components/form/LabeledSelect.vue b/shell/components/form/LabeledSelect.vue index dc24f2f7f0..8c00fc4d0c 100644 --- a/shell/components/form/LabeledSelect.vue +++ b/shell/components/form/LabeledSelect.vue @@ -5,6 +5,7 @@ import { get } from '@shell/utils/object'; import { LabeledTooltip } from '@components/LabeledTooltip'; import VueSelectOverrides from '@shell/mixins/vue-select-overrides'; import { onClickOption, calculatePosition } from '@shell/utils/select'; +import isEqual from 'lodash/isEqual'; export default { name: 'LabeledSelect', @@ -152,10 +153,13 @@ export default { const isOutdated = !this.options.find(opt => option[this.optionLabel] === opt[this.optionLabel]); if (isOutdated && this.options) { - const newOption = this.options.find(opt => option[this.optionKey] === opt[this.optionKey]); - const label = get(newOption, this.optionLabel); + const newOption = this.options.find(opt => isEqual(this.reduce(option), this.reduce(opt))); - return this.localizedLabel ? this.$store.getters['i18n/t'](label) || label : label; + if (newOption) { + const label = get(newOption, this.optionLabel); + + return this.localizedLabel ? this.$store.getters['i18n/t'](label) || label : label; + } } if (this.$attrs['get-option-label']) { diff --git a/shell/components/form/__tests__/LabeledSelect.test.ts b/shell/components/form/__tests__/LabeledSelect.test.ts index a729166d12..7dd63e8e0d 100644 --- a/shell/components/form/__tests__/LabeledSelect.test.ts +++ b/shell/components/form/__tests__/LabeledSelect.test.ts @@ -2,42 +2,137 @@ import { mount } from '@vue/test-utils'; import LabeledSelect from '@shell/components/form/LabeledSelect.vue'; describe('component: LabeledSelect', () => { - it('should automatically pick option for given value', () => { - const label = 'Foo'; - const value = 'foo'; - const wrapper = mount(LabeledSelect, { - propsData: { - value, - options: [ - { label, value }, - ], - } + describe('should display correct label', () => { + it('given an existing value and option', () => { + const label = 'Foo'; + const value = 'foo'; + const wrapper = mount(LabeledSelect, { + propsData: { + value, + options: [ + { label, value }, + ], + } + }); + + // Component is from a library and class is not going to be changed + expect(wrapper.find('.vs__selected').text()).toBe(label); }); - // Component is from a library and class is not going to be changed - expect(wrapper.find('.vs__selected').text()).toBe(label); - }); + it('using value as label if no options', () => { + const value = 'foo'; + const wrapper = mount(LabeledSelect, { + propsData: { + value, + options: [], + } + }); - it('should update the displayed label if options are changed', async() => { - const newLabel = 'Baz'; - const oldLabel = 'Foo'; - const value = 'foo'; - const wrapper = mount(LabeledSelect, { - propsData: { - value, - options: [ - { label: oldLabel, value }, - ], - } + // Component is from a library and class is not going to be changed + expect(wrapper.find('.vs__selected').text()).toBe(value); }); - await wrapper.setProps({ - options: [ - { label: newLabel, value }, - ] + it('using custom key as label for option object', () => { + const value = 'foo'; + const label = 'Foo'; + const customLabelKey = 'bananas'; + const wrapper = mount(LabeledSelect, { + propsData: { + value, + optionLabel: customLabelKey, + options: [{ + [customLabelKey]: label, + value + }], + } + }); + + // Component is from a library and class is not going to be changed + expect(wrapper.find('.vs__selected').text()).toBe(label); }); - // Component is from a library and class is not going to be changed - expect(wrapper.find('.vs__selected').text()).toBe(newLabel); + it('translating localized cases', () => { + const value = 'foo'; + const translation = 'bananas'; + const wrapper = mount(LabeledSelect, { + propsData: { + localizedLabel: true, + value, + options: [{ label: 'whatever', value }], + }, + mocks: { $store: { getters: { 'i18n/t': () => translation } } } + }); + + // Component is from a library and class is not going to be changed + expect(wrapper.find('.vs__selected').text()).toBe(translation); + }); + + describe('updating the value on options change', () => { + it('using new label', async() => { + const value = 'foo'; + const oldLabel = 'Foo'; + const newLabel = 'Baz'; + const wrapper = mount(LabeledSelect, { + propsData: { + value, + options: [ + { label: oldLabel, value }, + ], + } + }); + + await wrapper.setProps({ + options: [ + { label: newLabel, value }, + ] + }); + + // Component is from a library and class is not going to be changed + expect(wrapper.find('.vs__selected').text()).toBe(newLabel); + }); + + it('using values only and no labels', async() => { + const value = 'foo'; + const newValue = 'bananas'; + const wrapper = mount(LabeledSelect, { + propsData: { + value, + options: [value], + } + }); + + await wrapper.setProps({ options: [newValue] }); + + // Component is from a library and class is not going to be changed + expect(wrapper.find('.vs__selected').text()).toBe(value); + }); + + it('using translated value', async() => { + const value = 'foo'; + const oldLabel = 'Foo'; + const newLabel = 'Baz'; + const translation = 'bananas'; + const i18nMap: Record = { [newLabel]: translation }; + const wrapper = mount(LabeledSelect, { + propsData: { + value, + localizedLabel: true, + options: [ + { label: oldLabel, value }, + ], + }, + mocks: { $store: { getters: { 'i18n/t': (text: string) => i18nMap[text] } } } + }); + + await wrapper.setProps({ + options: [ + { label: newLabel, value }, + ] + }); + + // Component is from a library and class is not going to be changed + expect(wrapper.find('.vs__selected').text()).toBe(translation); + }); + }); }); });