|
| 1 | +/** |
| 2 | + * ObjectUI |
| 3 | + * Copyright (c) 2024-present ObjectStack Inc. |
| 4 | + * |
| 5 | + * This source code is licensed under the MIT license found in the |
| 6 | + * LICENSE file in the root directory of this source tree. |
| 7 | + */ |
| 8 | + |
| 9 | +/** |
| 10 | + * objectstack#28910 — the built-in string bulk action `delete` reads the |
| 11 | + * grid's translation key. |
| 12 | + * |
| 13 | + * An object with no declared bulk actions gets the string action `delete`, |
| 14 | + * and the bar drew it with `formatActionLabel`, so under zh it read "Delete". |
| 15 | + * `useBulkActionItems` now resolves the label once, so the bar's button and |
| 16 | + * the selection's context menu read `grid.delete` from the pack. Any other |
| 17 | + * string name is still formatted from itself, and a declared def keeps its |
| 18 | + * own label. Strings are read from the packs, never from literals. |
| 19 | + */ |
| 20 | +import '@testing-library/jest-dom/vitest'; |
| 21 | +import { describe, it, expect, afterEach } from 'vitest'; |
| 22 | +import React from 'react'; |
| 23 | +import { render, screen, cleanup } from '@testing-library/react'; |
| 24 | +import { I18nProvider } from '@object-ui/react'; |
| 25 | +import { DropdownMenu, DropdownMenuContent } from '@object-ui/components'; |
| 26 | +import { en, zh } from '@object-ui/i18n/locales'; |
| 27 | +import type { BulkActionDef } from '@object-ui/types'; |
| 28 | +import { BulkActionBar } from '../components/BulkActionBar'; |
| 29 | +import { SelectionContextMenuItems } from '../components/RowContextMenu'; |
| 30 | + |
| 31 | +afterEach(cleanup); |
| 32 | + |
| 33 | +const ROWS = [{ id: '1' }, { id: '2' }]; |
| 34 | + |
| 35 | +const inLanguage = (language: string, node: React.ReactNode) => ( |
| 36 | + <I18nProvider config={{ defaultLanguage: language, detectBrowserLanguage: false }} persistLanguage={false}> |
| 37 | + {node} |
| 38 | + </I18nProvider> |
| 39 | +); |
| 40 | + |
| 41 | +const renderBar = (language: string, actions: string[], actionDefs?: BulkActionDef[]) => |
| 42 | + render(inLanguage(language, <BulkActionBar selectedRows={ROWS} actions={actions} actionDefs={actionDefs} />)); |
| 43 | + |
| 44 | +const renderMenu = (language: string, actions: string[]) => |
| 45 | + render( |
| 46 | + inLanguage( |
| 47 | + language, |
| 48 | + <DropdownMenu open> |
| 49 | + <DropdownMenuContent> |
| 50 | + <SelectionContextMenuItems selectedRows={ROWS} actions={actions} /> |
| 51 | + </DropdownMenuContent> |
| 52 | + </DropdownMenu>, |
| 53 | + ), |
| 54 | + ); |
| 55 | + |
| 56 | +describe('the built-in bulk delete reads grid.delete (objectstack#28910)', () => { |
| 57 | + it('the zh pack translates the key, so the zh pins below can tell the two apart', () => { |
| 58 | + expect(zh.grid.delete).not.toBe(en.grid.delete); |
| 59 | + }); |
| 60 | + |
| 61 | + it('under zh, the bar button reads the zh pack, not the English name', async () => { |
| 62 | + renderBar('zh', ['delete']); |
| 63 | + const button = await screen.findByTestId('bulk-action-delete'); |
| 64 | + expect(button).toHaveTextContent(zh.grid.delete); |
| 65 | + expect(button).not.toHaveTextContent(en.grid.delete); |
| 66 | + }); |
| 67 | + |
| 68 | + it('under en, the bar button reads the en pack', async () => { |
| 69 | + renderBar('en', ['delete']); |
| 70 | + expect(await screen.findByTestId('bulk-action-delete')).toHaveTextContent(en.grid.delete); |
| 71 | + }); |
| 72 | + |
| 73 | + it("under zh, the selection's context menu reads the same zh label", async () => { |
| 74 | + renderMenu('zh', ['delete']); |
| 75 | + const item = await screen.findByTestId('row-context-bulk-delete'); |
| 76 | + expect(item).toHaveTextContent(zh.grid.delete); |
| 77 | + expect(item).not.toHaveTextContent(en.grid.delete); |
| 78 | + }); |
| 79 | + |
| 80 | + it('another string name is still formatted from itself', async () => { |
| 81 | + renderBar('zh', ['mark_paid']); |
| 82 | + expect(await screen.findByTestId('bulk-action-mark_paid')).toHaveTextContent('Mark Paid'); |
| 83 | + }); |
| 84 | + |
| 85 | + it('a declared bulk def keeps its own label', async () => { |
| 86 | + const def = { name: 'delete', label: 'Purge selected', operation: 'delete' } as BulkActionDef; |
| 87 | + renderBar('zh', [], [def]); |
| 88 | + expect(await screen.findByText('Purge selected')).toBeInTheDocument(); |
| 89 | + expect(screen.queryByText(zh.grid.delete)).toBeNull(); |
| 90 | + }); |
| 91 | +}); |
0 commit comments