Skip to content

Commit

Permalink
TransferList.Toolbar implements toolbar pattern (#2274)
Browse files Browse the repository at this point in the history
  • Loading branch information
r100-stack authored Oct 4, 2024
1 parent 1b654e6 commit 7f1bb28
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/beige-lions-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@itwin/itwinui-react': patch
---

`TransferList.Toolbar` implements the previously missing [toolbar pattern](https://www.w3.org/WAI/ARIA/apg/patterns/toolbar/), including the arrow-key navigation functionality.
5 changes: 5 additions & 0 deletions .changeset/orange-cats-mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@itwin/itwinui-react': patch
---

`IconButton`s inside `TransferList.Toolbar` will now show tooltips on the right side by default to avoid obscuring adjacent buttons in the group. This placement can be changed using the `labelProps.placement` prop on the `IconButton`.
Binary file modified ...act-workshop/cypress-visual-screenshots/baseline/TransferList.test.ts-Basic.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 16 additions & 2 deletions packages/itwinui-react/src/core/TransferList/TransferList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,21 @@ it('should render in its most basic state', () => {
});
});

it('should handle keyboard navigation', () => {
it('should render Toolbar in its most basic state', () => {
const { container } = render(
<TransferList>
<TransferList.Toolbar>
<Button />
</TransferList.Toolbar>
</TransferList>,
);

const toolbar = container.querySelector('.iui-transfer-list-toolbar');
expect(toolbar).toBeTruthy();
expect(toolbar).toHaveAttribute('role', 'toolbar');
});

it('should handle keyboard navigation in Listbox', () => {
const { container } = render(
<TransferList>
<TransferList.ListboxWrapper>
Expand Down Expand Up @@ -105,7 +119,7 @@ it('should handle keyboard navigation', () => {
});
});

it('should handle key presses', async () => {
it('should handle key presses in Listbox', async () => {
const mockedOnClick = vi.fn();
render(
<TransferList>
Expand Down
18 changes: 15 additions & 3 deletions packages/itwinui-react/src/core/TransferList/TransferList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type { PolymorphicForwardRefComponent } from '../../utils/index.js';
import { List } from '../List/List.js';
import { ListItem } from '../List/ListItem.js';
import { Label } from '../Label/Label.js';
import { ButtonGroup } from '../ButtonGroup/ButtonGroup.js';

// ----------------------------------------------------------------------------
// TransferListComponent
Expand Down Expand Up @@ -226,9 +227,20 @@ if (process.env.NODE_ENV === 'development') {
// ----------------------------------------------------------------------------
// TransferList.Toolbar component

const TransferListToolbar = polymorphic.div('iui-transfer-list-toolbar', {
role: 'toolbar',
});
const TransferListToolbar = React.forwardRef((props, ref) => {
const { className, children, ...rest } = props;
return (
<ButtonGroup
role='toolbar'
ref={ref}
{...rest}
orientation='vertical'
className={cx('iui-transfer-list-toolbar', className)}
>
{children}
</ButtonGroup>
);
}) as PolymorphicForwardRefComponent<'div', object>;
if (process.env.NODE_ENV === 'development') {
TransferListToolbar.displayName = 'TransferList.Toolbar';
}
Expand Down
5 changes: 5 additions & 0 deletions testing/e2e/app/routes/TransferList/route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { TransferListMainExample } from 'examples';

export default function Page() {
return <TransferListMainExample />;
}
56 changes: 56 additions & 0 deletions testing/e2e/app/routes/TransferList/spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { test, expect } from '@playwright/test';

test.describe('toolbar pattern in TransferList.Toolbar', () => {
test('should support toolbar arrow-key keyboard navigation', async ({
page,
}) => {
await page.goto('/TransferList');
const listboxes = page.getByRole('listbox');
const toolbar = page.getByRole('toolbar');
const buttons = toolbar.first().locator('button');

await page.keyboard.press('Tab');
await expect(listboxes.first()).toBeFocused();

await page.keyboard.press('Tab');
await expect(buttons.nth(0)).toBeFocused();
await page.keyboard.press('ArrowDown');
await expect(buttons.nth(1)).toBeFocused();
await page.keyboard.press('ArrowDown');
await expect(buttons.nth(2)).toBeFocused();
await page.keyboard.press('ArrowDown');
await expect(buttons.nth(3)).toBeFocused();
await page.keyboard.press('ArrowDown');
await expect(buttons.nth(0)).toBeFocused();
await page.keyboard.press('ArrowUp');
await expect(buttons.nth(3)).toBeFocused();
});

test('should have only one tab stop and support Tab/Shift+Tab key navigation', async ({
page,
}) => {
await page.goto('/TransferList');
const listboxes = page.getByRole('listbox');
const toolbar = page.getByRole('toolbar');
const buttons = toolbar.first().locator('button');

await page.keyboard.press('Tab');
await expect(listboxes.first()).toBeFocused();

await page.keyboard.press('Tab');
await expect(buttons.first()).toBeFocused();

await page.keyboard.press('Tab');
await expect(listboxes.last()).toBeFocused();

await page.keyboard.down('Shift');
await page.keyboard.press('Tab');
await page.keyboard.up('Shift');
await expect(buttons.first()).toBeFocused();

await page.keyboard.down('Shift');
await page.keyboard.press('Tab');
await page.keyboard.up('Shift');
await expect(listboxes.first()).toBeFocused();
});
});

0 comments on commit 7f1bb28

Please sign in to comment.