Skip to content

Commit

Permalink
feat: implement remove list item
Browse files Browse the repository at this point in the history
Ref: #38
  • Loading branch information
AdeAttwood committed Dec 21, 2022
1 parent c0c60e5 commit 0ff581c
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 15 deletions.
4 changes: 4 additions & 0 deletions src/attribute-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export type AttributeContextType<T> = {
* requests multiple options
*/
options: T[];
/**
* A callback function that will remove an item from the options at the index
*/
remove?: (index: number) => void;
};

/**
Expand Down
22 changes: 18 additions & 4 deletions src/list-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,23 @@ const useArrayAttribute = createUseAttributeHook((value) => value);
export function useListAttribute<T>(attribute: string, newItem: () => T) {
const { id, error, value, set } = useArrayAttribute(attribute, []);
const add = () => set([...value, newItem()]);
const remove = (index: number) => {
// Only attempt to remove the item from the array if its a valid array
// index `splice` will throw errors for out of range.
if (index > -1 && index < value.length) {
const newValue = [...value];
newValue.splice(index, 1);
set(newValue);
}
};

return { id, error, value, set, add };
return { id, error, value, set, add, remove };
}

export const ListGroup: FC<ListGroupProps> = ({ children, attribute, newItem }) => {
const { value: options, add } = useListAttribute(attribute, newItem || defaultNewItem);
const { value: options, add, remove } = useListAttribute(attribute, newItem || defaultNewItem);

return React.createElement(AttributeContextProvider, { attribute, options }, children({ add }));
return React.createElement(AttributeContextProvider, { attribute, options, remove }, children({ add }));
};

ListGroup.defaultProps = {
Expand All @@ -56,6 +65,10 @@ export interface ListOptionChildProps {
* The index this option is at
*/
index: string;
/**
* Callback that will remove the current item from the list
*/
remove: () => void;
}

export interface ListOptionProps {
Expand All @@ -66,13 +79,14 @@ export interface ListOptionProps {
}

export const ListOption: FC<ListOptionProps> = ({ children }) => {
const { options } = useAttributeContext();
const { options, remove } = useAttributeContext();

return (
<>
{Object.keys(options).map((index) =>
children({
index,
remove: () => remove?.(parseInt(index)),
})
)}
</>
Expand Down
31 changes: 20 additions & 11 deletions tests/list-group.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@ it("will render and submit with a radio list", async () => {
{({ add }) => (
<div>
<ListOption>
{({ index }) => (
<div key={index}>
<InputGroup attribute={`tags.${index}`}>
{({ props }) => (
<div>
<label htmlFor={props.id}>Tag {parseInt(index) + 1}</label>
<input {...props} />
</div>
)}
</InputGroup>
</div>
{({ index, remove }) => (
<InputGroup key={index} attribute={`tags.${index}`}>
{({ props }) => (
<div>
<label htmlFor={props.id}>Tag {parseInt(index) + 1}</label>
<input {...props} />
<button type="button" onClick={remove}>
Remove {parseInt(index) + 1}
</button>
</div>
)}
</InputGroup>
)}
</ListOption>
<button type="button" onClick={add}>
Expand Down Expand Up @@ -61,4 +62,12 @@ it("will render and submit with a radio list", async () => {

expect(onSubmit).toBeCalledTimes(3);
expect(onSubmit).toBeCalledWith(expect.objectContaining({ formState: { tags: ["Tag One", "Tag Two"] } }));

await act(async () => {
await userEvent.click(getByText("Remove 1"));
await userEvent.click(getByText("Submit"));
});

expect(onSubmit).toBeCalledTimes(4);
expect(onSubmit).toBeCalledWith(expect.objectContaining({ formState: { tags: ["Tag Two"] } }));
});

0 comments on commit 0ff581c

Please sign in to comment.