Skip to content

Commit

Permalink
feat(react-list-preview): pass value to onAction callback (microsoft#…
Browse files Browse the repository at this point in the history
  • Loading branch information
george-cz authored Jun 26, 2024
1 parent 9e3fd07 commit df9899f
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: Pass data with value to onAction callback",
"packageName": "@fluentui/react-list-preview",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ export const listItemClassNames: SlotClassNames<ListItemSlots>;

// @public
export type ListItemProps = ComponentProps<ListItemSlots> & {
value?: string | number;
onAction?: (e: ListItemActionEvent) => void;
value?: ListItemValue;
onAction?: EventHandler<ListItemActionEventData>;
};

// @public (undocumented)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { isConformant } from '../../testing/isConformant';
import { List } from './List';
import { ListProps } from './List.types';
import { ListItem } from '../ListItem/ListItem';
import { ListItemActionEvent } from '../../events/ListItemActionEvent';
import { ListItemActionEventData } from '../ListItem/ListItem.types';
import { EventHandler } from '@fluentui/react-utilities';

function expectListboxItemSelected(item: HTMLElement, selected: boolean) {
expect(item.getAttribute('aria-selected')).toBe(selected.toString());
Expand Down Expand Up @@ -335,12 +336,32 @@ describe('List', () => {
firstItem.click();
expect(onAction).toHaveBeenCalledTimes(1);
});
it('onAction should be called with the value', () => {
const onAction = jest.fn();

const result = render(
<List>
<ListItem onAction={onAction} value="first-item">
First ListItem
</ListItem>
<ListItem>Second ListItem</ListItem>
</List>,
);

const firstItem = result.getByText('First ListItem');
firstItem.click();
expect(onAction).toHaveBeenCalledWith(expect.any(Object), {
event: expect.any(Object),
type: 'ListItemAction',
value: 'first-item',
});
});
});

describe('with selection', () => {
function interactWithFirstElement(
interaction: (firstItem: HTMLElement) => void,
customAction?: (e: ListItemActionEvent) => void,
customAction?: EventHandler<ListItemActionEventData>,
) {
const onAction = jest.fn(customAction);

Expand Down Expand Up @@ -454,7 +475,9 @@ describe('List', () => {

const result = render(
<List>
<ListItem onAction={onAction}>First ListItem</ListItem>
<ListItem onAction={onAction} value="first-item">
First ListItem
</ListItem>
<ListItem>Second ListItem</ListItem>
</List>,
);
Expand All @@ -473,11 +496,18 @@ describe('List', () => {
it('Enter should trigger onClick', () => {
expect(pressKeyOnListItem('Enter').onAction).toHaveBeenCalledTimes(1);
});
it('onAction should be called with list item value', () => {
expect(pressKeyOnListItem('Enter').onAction).toHaveBeenCalledWith(expect.any(Object), {
event: expect.any(Object),
type: 'ListItemAction',
value: 'first-item',
});
});
});

describe('with selection', () => {
function pressOnListItem(key: string, customOnaction?: (e: ListItemActionEvent) => void) {
const onAction = jest.fn(customOnaction);
function pressOnListItem(key: string, customOnAction?: EventHandler<ListItemActionEventData>) {
const onAction = jest.fn(customOnAction);

const result = render(
<List selectionMode="multiselect">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import { Checkbox } from '@fluentui/react-checkbox';
import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities';
import { ListItemActionEvent } from '../../events/ListItemActionEvent';
import type { ComponentProps, ComponentState, EventData, EventHandler, Slot } from '@fluentui/react-utilities';
import { ListItemActionEvent, ListItemActionEventName } from '../../events/ListItemActionEvent';

export type ListItemSlots = {
root: NonNullable<Slot<'li', 'div'>>;
checkmark?: Slot<typeof Checkbox>;
};

export type ListItemValue = string | number;

export type ListItemActionEventData = EventData<typeof ListItemActionEventName, ListItemActionEvent> & {
value: ListItemValue;
};
/**
* ListItem Props
*/
export type ListItemProps = ComponentProps<ListItemSlots> & {
value?: string | number;
// eslint-disable-next-line @nx/workspace-consistent-callback-type -- using custom event here with no data
onAction?: (e: ListItemActionEvent) => void;
value?: ListItemValue;
onAction?: EventHandler<ListItemActionEventData>;
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ import type { ListItemProps, ListItemState } from './ListItem.types';
import { useListContext_unstable } from '../List/listContext';
import { Enter, Space, ArrowUp, ArrowDown, ArrowRight, ArrowLeft } from '@fluentui/keyboard-keys';
import { Checkbox, CheckboxOnChangeData } from '@fluentui/react-checkbox';
import { createListItemActionEvent, ListItemActionEvent } from '../../events/ListItemActionEvent';
import {
createListItemActionEvent,
ListItemActionEvent,
ListItemActionEventName,
} from '../../events/ListItemActionEvent';

const DEFAULT_ROOT_EL_TYPE = 'li';

Expand Down Expand Up @@ -56,15 +60,15 @@ export const useListItem_unstable = (
const rootRef = React.useRef<HTMLLIElement | HTMLDivElement>(null);
const checkmarkRef = React.useRef<HTMLInputElement | null>(null);

const handleAction: (e: ListItemActionEvent) => void = useEventCallback(e => {
onAction?.(e);
const handleAction: (event: ListItemActionEvent) => void = useEventCallback(event => {
onAction?.(event, { event, value, type: ListItemActionEventName });

if (e.defaultPrevented) {
if (event.defaultPrevented) {
return;
}

if (isSelectionEnabled) {
toggleItem?.(e.detail.originalEvent, value);
toggleItem?.(event.detail.originalEvent, value);
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ const CustomListItem = (props: { title: string; value: string }) => {
const { value } = props;

// This will be triggered by user pressing Enter or clicking on the list item
const onAction = React.useCallback(event => {
const onAction = React.useCallback((event, { value: val }) => {
// This prevents the change in selection on click/Enter
event.preventDefault();
alert(`Triggered custom action!`);
alert(`Triggered custom action on ${val}`);
}, []);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ export const SingleActionSelectionDifferentPrimary = () => {
const [selectedItems, setSelectedItems] = React.useState<SelectionItemId[]>(['Demetra Manwaring', 'Bart Merrill']);

// This will be triggered by user pressing Enter or clicking on the list item
const onAction = React.useCallback(event => {
const onAction = React.useCallback((event, { value: val }) => {
// This prevents the change in selection on click/Enter
event.preventDefault();
alert(`Triggered custom action!`);
alert(`Triggered custom action on ${val}`);
}, []);

return (
Expand Down

0 comments on commit df9899f

Please sign in to comment.