-
Notifications
You must be signed in to change notification settings - Fork 221
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(collection): Add removable support (#3036)
Fixes: #3025 Adds `remove` event to the `ListModel`. An `onRemove` config should be added to dynamic lists to remove the item from the collection. The `MultiSelect.Input` uses this new remove event to handle removing items from the Selected pill list when the user uses the “Delete” key. Focus is managed by the collection system when an item is removed. [category:Components]
- Loading branch information
1 parent
b0122ce
commit cbfbb06
Showing
21 changed files
with
372 additions
and
141 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
modules/preview-react/multi-select/lib/MultiSelectedItem.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import React from 'react'; | ||
|
||
import { | ||
composeHooks, | ||
createElemPropsHook, | ||
createSubModelElemPropsHook, | ||
createSubcomponent, | ||
} from '@workday/canvas-kit-react/common'; | ||
import {useListItemRegister, useListItemRovingFocus} from '@workday/canvas-kit-react/collection'; | ||
import {Pill} from '@workday/canvas-kit-preview-react/pill'; | ||
|
||
import {useMultiSelectItemRemove} from './useMultiSelectItemRemove'; | ||
import {useMultiSelectModel} from './useMultiSelectModel'; | ||
|
||
export interface MultiSelectedItemProps { | ||
/** | ||
* Remove label on a MultiSelectedItem. In English, the label may be "Remove" and the screen | ||
* reader will read out "Remove {option}". | ||
* | ||
* @default "remove" | ||
*/ | ||
removeLabel?: string; | ||
} | ||
|
||
export const useMultiSelectedItem = composeHooks( | ||
createElemPropsHook(useMultiSelectModel)(model => { | ||
return { | ||
'aria-selected': true, | ||
}; | ||
}), | ||
useMultiSelectItemRemove, | ||
createSubModelElemPropsHook(useMultiSelectModel)(m => m.selected, useListItemRovingFocus), | ||
createSubModelElemPropsHook(useMultiSelectModel)(m => m.selected, useListItemRegister) | ||
); | ||
|
||
export const MultiSelectedItem = createSubcomponent('span')({ | ||
modelHook: useMultiSelectModel, | ||
elemPropsHook: useMultiSelectedItem, | ||
})<MultiSelectedItemProps>(({children, removeLabel, ref, ...elemProps}, Element) => { | ||
return ( | ||
<Pill as={Element} variant="removable"> | ||
{children} | ||
<Pill.IconButton aria-label={removeLabel} ref={ref} {...(elemProps as any)} /> | ||
</Pill> | ||
); | ||
}); |
30 changes: 30 additions & 0 deletions
30
modules/preview-react/multi-select/lib/MultiSelectedList.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import React from 'react'; | ||
|
||
import {createSubcomponent} from '@workday/canvas-kit-react/common'; | ||
import {ListBox} from '@workday/canvas-kit-react/collection'; | ||
|
||
import {useMultiSelectModel} from './useMultiSelectModel'; | ||
import {MultiSelectedItem, MultiSelectedItemProps} from './MultiSelectedItem'; | ||
|
||
export interface MultiSelectedListProps | ||
extends MultiSelectedItemProps, | ||
React.HTMLAttributes<HTMLDivElement> {} | ||
|
||
export const MultiSelectedList = createSubcomponent()({ | ||
modelHook: useMultiSelectModel, | ||
})<MultiSelectedListProps>(({'aria-labelledby': ariaLabelledBy, removeLabel}, Element, model) => { | ||
return model.selected.state.items.length ? ( | ||
<> | ||
<div data-part="separator" /> | ||
<ListBox | ||
model={model.selected} | ||
as="div" | ||
role="listbox" | ||
aria-orientation="horizontal" | ||
aria-labelledby={ariaLabelledBy} | ||
> | ||
{item => <MultiSelectedItem removeLabel={removeLabel}>{item.textValue}</MultiSelectedItem>} | ||
</ListBox> | ||
</> | ||
) : null; | ||
}); |
48 changes: 48 additions & 0 deletions
48
modules/preview-react/multi-select/lib/useMultiSelectItemRemove.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import React from 'react'; | ||
import {createElemPropsHook} from '@workday/canvas-kit-react/common'; | ||
|
||
import {useMultiSelectModel} from './useMultiSelectModel'; | ||
import {focusOnCurrentCursor, listItemRemove} from '@workday/canvas-kit-react/collection'; | ||
|
||
/** | ||
* This elemProps hook is used when a menu item is expected to be removed. It will advance the cursor to | ||
* another item. | ||
* This elemProps hook is used for cursor navigation by using [Roving | ||
* Tabindex](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex). Only a single item in the | ||
* collection has a tab stop. Pressing an arrow key moves the tab stop to a different item in the | ||
* corresponding direction. See the [Roving Tabindex](#roving-tabindex) example. This elemProps hook | ||
* should be applied to an `*.Item` component. | ||
* | ||
* ```ts | ||
* const useMyItem = composeHooks( | ||
* useListItemRovingFocus, // adds the roving tabindex support | ||
* useListItemRegister | ||
* ); | ||
* ``` | ||
*/ | ||
export const useMultiSelectItemRemove = createElemPropsHook(useMultiSelectModel)((model, _ref) => { | ||
return { | ||
onKeyDown(event: React.KeyboardEvent<HTMLElement>) { | ||
if (event.key === 'Backspace' || event.key === 'Delete') { | ||
const id = event.currentTarget.dataset.id || ''; | ||
const nextId = listItemRemove(id, model.selected); | ||
model.selected.events.remove({id, event}); | ||
if (nextId) { | ||
focusOnCurrentCursor(model.selected, nextId, event.currentTarget); | ||
} else { | ||
model.state.inputRef.current?.focus(); | ||
} | ||
} | ||
}, | ||
onClick(event: React.MouseEvent<HTMLElement>) { | ||
const id = event.currentTarget.dataset.id || ''; | ||
const nextId = listItemRemove(id, model.selected); | ||
model.selected.events.remove({id, nextId, event}); | ||
if (nextId) { | ||
focusOnCurrentCursor(model.selected, nextId, event.currentTarget); | ||
} else { | ||
model.state.inputRef.current?.focus(); | ||
} | ||
}, | ||
}; | ||
}); |
Oops, something went wrong.