From 7e8d7e09866896196415aca529dca9ea5ada9a28 Mon Sep 17 00:00:00 2001 From: Roman Zaritskyi Date: Fri, 2 Oct 2026 11:07:31 +0300 Subject: [PATCH 01/10] fix: open the one-plus-many popup on click and Enter [WTEL-10231](https://webitel.atlassian.net/browse/WTEL-10231) --- .../one-plus-many-table-cell.spec.ts | 133 ++++++++++++++++++ .../one-plus-many-table-cell.vue | 15 +- 2 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 src/app/components/utils/table-cell/one-plus-many-table-cell/__tests__/one-plus-many-table-cell.spec.ts diff --git a/src/app/components/utils/table-cell/one-plus-many-table-cell/__tests__/one-plus-many-table-cell.spec.ts b/src/app/components/utils/table-cell/one-plus-many-table-cell/__tests__/one-plus-many-table-cell.spec.ts new file mode 100644 index 000000000..464cff718 --- /dev/null +++ b/src/app/components/utils/table-cell/one-plus-many-table-cell/__tests__/one-plus-many-table-cell.spec.ts @@ -0,0 +1,133 @@ +import { mount } from '@vue/test-utils'; +import { describe, expect, it } from 'vitest'; + +import Cell from '../one-plus-many-table-cell.vue'; + +/** `wt-*` stay unresolved under @vue/compat, so tests assert plain DOM */ + +interface Item { + id?: string; + name?: string; +} + +const cell = ( + collection: Item[], + options: { + routeName?: string; + } = {}, +) => + mount(Cell, { + props: { + collection, + routeName: options.routeName ?? null, + }, + }); + +const value = (wrapper: ReturnType) => + wrapper.find('.one-plus-many-table-cell__value'); + +const counter = (wrapper: ReturnType) => + wrapper.find('.one-plus-many-table-cell__chip'); + +const lookup = (name: string, id?: string): Item => ({ + id, + name, +}); + +describe('one plus many table cell', () => { + it('renders the first item as the cell value', () => { + const wrapper = cell([ + lookup('First team'), + lookup('Second team'), + ]); + + expect(value(wrapper).text()).toBe('First team'); + }); + + it('counts the items it does not show', () => { + const wrapper = cell([ + lookup('a'), + lookup('b'), + lookup('c'), + lookup('d'), + ]); + + expect(counter(wrapper).text()).toBe('+3'); + }); + + /** a single item is the whole cell value; the wrapper used to stay clickable */ + it('draws no counter for a single item', async () => { + const wrapper = cell([ + lookup('Only team'), + ]); + + expect(counter(wrapper).exists()).toBe(false); + expect(value(wrapper).text()).toBe('Only team'); + + await value(wrapper).trigger('click'); + + expect(wrapper.emitted('input')).toBeUndefined(); + }); + + it('renders nothing at all for an empty collection', () => { + expect(cell([]).find('.one-plus-many-table-cell').exists()).toBe(false); + }); + + /** the handler used to sit on `wt-chip`, where a `@click` never reached the DOM */ + it('asks to open the list when the counter is clicked', async () => { + const wrapper = cell([ + lookup('a'), + lookup('b'), + ]); + + await counter(wrapper).trigger('click'); + + expect(wrapper.emitted('input')).toEqual([ + [], + ]); + }); + + /** `tabindex` and `keydown` used to sit on different elements */ + it('opens the list from the keyboard too', async () => { + const wrapper = cell([ + lookup('a'), + lookup('b'), + ]); + + expect(counter(wrapper).attributes('tabindex')).toBe('0'); + + await counter(wrapper).trigger('keydown.enter'); + + expect(wrapper.emitted('input')).toEqual([ + [], + ]); + }); + + it('links the shown item when it has an id and a route', () => { + const wrapper = cell( + [ + lookup('First team', '42'), + lookup('Second team', '43'), + ], + { + routeName: 'teams', + }, + ); + + const link = wrapper.find('adm-item-link'); + + expect(link.text()).toBe('First team'); + expect(link.attributes('id')).toBe('42'); + expect(link.attributes('route-name')).toBe('teams'); + }); + + /** an item without a route is not a link — the other branch of the same v-if */ + it('renders plain text when no route is given', () => { + const wrapper = cell([ + lookup('First team', '42'), + ]); + + expect(wrapper.find('adm-item-link').exists()).toBe(false); + expect(value(wrapper).find('span').text()).toBe('First team'); + }); +}); diff --git a/src/app/components/utils/table-cell/one-plus-many-table-cell/one-plus-many-table-cell.vue b/src/app/components/utils/table-cell/one-plus-many-table-cell/one-plus-many-table-cell.vue index 913407838..39445c9bc 100644 --- a/src/app/components/utils/table-cell/one-plus-many-table-cell/one-plus-many-table-cell.vue +++ b/src/app/components/utils/table-cell/one-plus-many-table-cell/one-plus-many-table-cell.vue @@ -1,9 +1,9 @@ + + +