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.
 
 
 

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