Design system.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

268 regels
6.3 KiB

  1. import * as React from 'react';
  2. import {
  3. cleanup,
  4. render,
  5. screen,
  6. } from '@testing-library/react';
  7. import { TextControl } from '@tesseract-design/web-base';
  8. import userEvent from '@testing-library/user-event';
  9. import {
  10. afterEach,
  11. expect,
  12. vi,
  13. describe,
  14. it,
  15. } from 'vitest';
  16. import matchers from '@testing-library/jest-dom/matchers';
  17. import { PluginAPI } from 'tailwindcss/types/config';
  18. import {
  19. MenuMultiSelect,
  20. MenuMultiSelectDerivedElement,
  21. menuMultiSelectPlugin,
  22. } from '.';
  23. expect.extend(matchers);
  24. describe('menuMultiSelectPlugin', () => {
  25. it('adds component styles', () => {
  26. const pluginApi = {
  27. addComponents: vi.fn(),
  28. } as unknown as PluginAPI;
  29. menuMultiSelectPlugin(pluginApi);
  30. expect(pluginApi.addComponents).toBeCalledTimes(1);
  31. });
  32. });
  33. describe('MenuMultiSelect', () => {
  34. afterEach(() => {
  35. cleanup();
  36. });
  37. it('renders a listbox', () => {
  38. render(<MenuMultiSelect />);
  39. const listbox = screen.getByRole('listbox');
  40. expect(listbox).toBeInTheDocument();
  41. });
  42. it('renders a border', () => {
  43. render(
  44. <MenuMultiSelect
  45. border
  46. />,
  47. );
  48. const border = screen.getByTestId('border');
  49. expect(border).toBeInTheDocument();
  50. });
  51. it('renders a label', () => {
  52. render(
  53. <MenuMultiSelect
  54. label="foo"
  55. />,
  56. );
  57. const listbox = screen.getByLabelText('foo');
  58. expect(listbox).toBeInTheDocument();
  59. const label = screen.getByTestId('label');
  60. expect(label).toHaveTextContent('foo');
  61. });
  62. it('renders a hidden label', () => {
  63. render(
  64. <MenuMultiSelect
  65. label="foo"
  66. hiddenLabel
  67. />,
  68. );
  69. const listbox = screen.getByLabelText('foo');
  70. expect(listbox).toBeInTheDocument();
  71. const label = screen.getByTestId('label');
  72. expect(label).toHaveClass('sr-only');
  73. });
  74. it('renders a hint', () => {
  75. render(
  76. <MenuMultiSelect
  77. hint="foo"
  78. />,
  79. );
  80. const hint = screen.getByTestId('hint');
  81. expect(hint).toBeInTheDocument();
  82. });
  83. it('render options with implicit values', () => {
  84. render(
  85. <MenuMultiSelect>
  86. <option>foo</option>
  87. <option>bar</option>
  88. </MenuMultiSelect>,
  89. );
  90. const listbox = screen.getByRole('listbox');
  91. expect(listbox.children).toHaveLength(2);
  92. });
  93. it('renders valid options', () => {
  94. render(
  95. <MenuMultiSelect>
  96. <option value="foo">foo</option>
  97. <option value="bar">bar</option>
  98. </MenuMultiSelect>,
  99. );
  100. const listbox = screen.getByRole('listbox');
  101. expect(listbox.children).toHaveLength(2);
  102. });
  103. it('renders option groups', () => {
  104. render(
  105. <MenuMultiSelect>
  106. <optgroup label="foo">
  107. <option value="baz">baz</option>
  108. </optgroup>
  109. <optgroup label="bar">
  110. <option value="quux">quux</option>
  111. <option value="quuux">quuux</option>
  112. </optgroup>
  113. </MenuMultiSelect>,
  114. );
  115. const listbox = screen.getByRole('listbox');
  116. expect(listbox.children).toHaveLength(2);
  117. expect(listbox.children[0].children).toHaveLength(1);
  118. expect(listbox.children[1].children).toHaveLength(2);
  119. });
  120. describe.each`
  121. size | inputClassNames | hintClassNames | indicatorClassNames
  122. ${'small'} | ${'min-h-10'} | ${'pr-10'} | ${'w-10'}
  123. ${'medium'} | ${'min-h-12'} | ${'pr-12'} | ${'w-12'}
  124. ${'large'} | ${'min-h-16'} | ${'pr-16'} | ${'w-16'}
  125. `('on $size size', ({
  126. size,
  127. inputClassNames,
  128. hintClassNames,
  129. indicatorClassNames,
  130. }: {
  131. size: TextControl.Size,
  132. inputClassNames: string,
  133. hintClassNames: string,
  134. indicatorClassNames: string,
  135. }) => {
  136. it('renders input styles', () => {
  137. render(
  138. <MenuMultiSelect
  139. size={size}
  140. />,
  141. );
  142. const listbox = screen.getByRole('listbox');
  143. expect(listbox).toHaveClass(inputClassNames);
  144. });
  145. it('renders hint styles with indicator', () => {
  146. render(
  147. <MenuMultiSelect
  148. size={size}
  149. hint="hint"
  150. indicator="a"
  151. />,
  152. );
  153. const hint = screen.getByTestId('hint');
  154. expect(hint).toHaveClass(hintClassNames);
  155. });
  156. it('renders indicator styles', () => {
  157. render(
  158. <MenuMultiSelect
  159. size={size}
  160. indicator="a"
  161. />,
  162. );
  163. const indicator = screen.getByTestId('indicator');
  164. expect(indicator).toHaveClass(indicatorClassNames);
  165. });
  166. it('renders indicator styles for label', () => {
  167. render(
  168. <MenuMultiSelect
  169. size={size}
  170. label="label"
  171. indicator="a"
  172. />,
  173. );
  174. const label = screen.getByTestId('label');
  175. expect(label).toHaveClass(hintClassNames);
  176. });
  177. });
  178. it('renders a block input', () => {
  179. render(
  180. <MenuMultiSelect
  181. block
  182. />,
  183. );
  184. const base = screen.getByTestId('base');
  185. expect(base).toHaveClass('block');
  186. });
  187. describe.each`
  188. variant | inputClassNames | hintClassNames
  189. ${'default'} | ${'pl-4'} | ${'bottom-0 pl-4 pb-1'}
  190. ${'alternate'} | ${'pl-1.5 pt-5'} | ${'top-0.5'}
  191. `('on $variant variant', ({
  192. variant,
  193. inputClassNames,
  194. hintClassNames,
  195. }: {
  196. variant: TextControl.Variant,
  197. inputClassNames: string,
  198. hintClassNames: string,
  199. }) => {
  200. it('renders input styles', () => {
  201. render(
  202. <MenuMultiSelect
  203. variant={variant}
  204. />,
  205. );
  206. const listbox = screen.getByRole('listbox');
  207. expect(listbox).toHaveClass(inputClassNames);
  208. });
  209. it('renders hint styles', () => {
  210. render(
  211. <MenuMultiSelect
  212. variant={variant}
  213. hint="hint"
  214. />,
  215. );
  216. const hint = screen.getByTestId('hint');
  217. expect(hint).toHaveClass(hintClassNames);
  218. });
  219. });
  220. it('handles change events', async () => {
  221. const onChange = vi.fn().mockImplementationOnce(
  222. (e: React.ChangeEvent<MenuMultiSelectDerivedElement>) => {
  223. e.preventDefault();
  224. },
  225. );
  226. render(
  227. <MenuMultiSelect
  228. onChange={onChange}
  229. >
  230. <option value="foo">foo</option>
  231. <option value="bar">bar</option>
  232. </MenuMultiSelect>,
  233. );
  234. const listbox: HTMLSelectElement = screen.getByRole('listbox');
  235. const [, secondOption] = screen.getAllByRole('option');
  236. await userEvent.selectOptions(listbox, secondOption);
  237. expect(onChange).toBeCalled();
  238. });
  239. });