@sproutsocial/seeds-react-menu 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/CHANGELOG.md +15 -0
  3. package/dist/esm/index.js +234 -80
  4. package/dist/esm/index.js.map +1 -1
  5. package/dist/index.d.mts +43 -17
  6. package/dist/index.d.ts +43 -17
  7. package/dist/index.js +228 -72
  8. package/dist/index.js.map +1 -1
  9. package/package.json +3 -1
  10. package/src/Autocomplete/Autocomplete.feature +14 -43
  11. package/src/Autocomplete/Autocomplete.stories.tsx +128 -0
  12. package/src/Autocomplete/Autocomplete.tsx +29 -11
  13. package/src/Autocomplete/AutocompleteInput.tsx +51 -0
  14. package/src/Autocomplete/AutocompleteTypes.ts +8 -1
  15. package/src/Autocomplete/__tests__/Autocomplete.test.tsx +142 -89
  16. package/src/Autocomplete/__tests__/Autocomplete.typetest.tsx +25 -43
  17. package/src/Autocomplete/index.ts +1 -0
  18. package/src/MenuContext.tsx +11 -5
  19. package/src/MenuGroup/MenuGroup.feature +6 -24
  20. package/src/MenuGroup/MenuGroup.tsx +12 -1
  21. package/src/MenuGroup/MenuGroupTypes.ts +4 -1
  22. package/src/MenuGroup/__tests__/MenuGroup.test.tsx +41 -18
  23. package/src/MenuGroup/__tests__/MenuGroup.typetest.tsx +4 -2
  24. package/src/MenuItem/MenuItem.tsx +8 -5
  25. package/src/MenuLabel.tsx +4 -4
  26. package/src/MenuRoot/MenuRoot.tsx +5 -3
  27. package/src/SingleSelectMenu/SingleSelectMenu.tsx +2 -2
  28. package/src/__fixtures__/menu-test-data.ts +36 -0
  29. package/src/__tests__/Menu.test.tsx +29 -96
  30. package/src/hooks/useItemsFromChildren.tsx +70 -27
  31. package/src/index.ts +1 -0
  32. package/src/reducers/useComboboxStateReducer.tsx +11 -8
  33. package/src/storybookUtilities/menu-storybook-components.tsx +39 -0
  34. package/src/types.ts +1 -0
@@ -1,27 +1,26 @@
1
1
  import * as React from "react";
2
- import {
3
- MultiSelectMenu,
4
- MenuContent,
5
- MenuHeader,
6
- MenuFooter,
7
- } from "../../index";
2
+ import { Autocomplete, MenuContent, MenuHeader, MenuFooter } from "../../index";
8
3
 
9
- export const MultiSelectMenuTypeTest = () => (
4
+ export const AutocompleteTypeTest = () => (
10
5
  <>
11
- {/* Valid MultiSelectMenu with minimal required props */}
12
- <MultiSelectMenu menuToggleElement={<>test</>}>Test</MultiSelectMenu>
13
- {/* Valid MultiSelectMenu allows MenuContent by itself as a child */}
14
- <MultiSelectMenu menuToggleElement={<>test</>}>
6
+ {/* Valid Autocomplete with minimal required props */}
7
+ <Autocomplete menuToggleElement={<>test</>}>Test</Autocomplete>
8
+ {/* Valid Autocomplete allows MenuContent by itself as a child */}
9
+ <Autocomplete menuToggleElement={<>test</>}>
15
10
  <MenuContent>Test</MenuContent>
16
- </MultiSelectMenu>
17
- {/* Valid MultiSelectMenu allows multiple children without fragment */}
18
- <MultiSelectMenu menuToggleElement={<>test</>}>
11
+ </Autocomplete>
12
+ {/* Valid Autocomplete allows multiple children without fragment */}
13
+ <Autocomplete menuToggleElement={<>test</>}>
19
14
  <MenuHeader>Test</MenuHeader>
20
15
  <MenuContent>Test</MenuContent>
21
16
  <MenuFooter>Test</MenuFooter>
22
- </MultiSelectMenu>
23
- {/* Valid MultiSelectMenu accepts Downshift props and functions for useSelect */}
24
- <MultiSelectMenu
17
+ </Autocomplete>
18
+ {/* Valid Autocomplete allows getIsItemVisible function */}
19
+ <Autocomplete menuToggleElement={<>test</>} getIsItemVisible={() => true}>
20
+ Test
21
+ </Autocomplete>
22
+ {/* Valid Autocomplete accepts Downshift props and functions for useCombobox */}
23
+ <Autocomplete
25
24
  menuToggleElement={<>test</>}
26
25
  onSelectedItemChange={() => {}}
27
26
  selectedItem={null}
@@ -31,31 +30,18 @@ export const MultiSelectMenuTypeTest = () => (
31
30
  isOpen
32
31
  >
33
32
  Test
34
- </MultiSelectMenu>
35
- {/* Valid MultiSelectMenu accepts Downshift props and functions for useMultipleSelection */}
36
- <MultiSelectMenu
37
- menuToggleElement={<>test</>}
38
- onSelectedItemsChange={() => {}}
39
- selectedItems={[]}
40
- multiSelectStateReducer={(c) => c}
41
- onMultiSelectStateChange={() => {}}
42
- getA11yMultiStatusMessage={() => ""}
43
- >
44
- Test
45
- </MultiSelectMenu>
46
-
33
+ </Autocomplete>
47
34
  {/* @ts-expect-error - menuToggleElement is a required prop */}
48
- <MultiSelectMenu>Test</MultiSelectMenu>
49
- <MultiSelectMenu
35
+ <Autocomplete>Test</Autocomplete>
36
+ <Autocomplete
50
37
  /* @ts-expect-error - menuToggleElement must be a valid react element */
51
38
  menuToggleElement="test"
52
39
  >
53
40
  Test
54
- </MultiSelectMenu>
41
+ </Autocomplete>
55
42
  {/* @ts-expect-error - when no children provided */}
56
- <MultiSelectMenu menuToggleElement="test" />
57
-
58
- <MultiSelectMenu
43
+ <Autocomplete menuToggleElement="test" />
44
+ <Autocomplete
59
45
  menuToggleElement={<>test</>}
60
46
  /* @ts-expect-error - isOpen can't be a string */
61
47
  isOpen="false"
@@ -65,14 +51,10 @@ export const MultiSelectMenuTypeTest = () => (
65
51
  selectedItem="test"
66
52
  /* @ts-expect-error - stateReducer must have a return value */
67
53
  stateReducer={() => {}}
68
- /* @ts-expect-error - onMultiSelectStateChange can't be a string */
69
- onMultiSelectStateChange="test"
70
- /* @ts-expect-error - selectedItems can't be a string */
71
- selectedItems="test"
72
- /* @ts-expect-error - multiSelectStateReducer must have a return value */
73
- multiSelectStateReducer="test"
54
+ /* @ts-expect-error - getIsItemVisible must be a valid function */
55
+ getIsItemVisible={true}
74
56
  >
75
57
  Test
76
- </MultiSelectMenu>
58
+ </Autocomplete>
77
59
  </>
78
60
  );
@@ -1,2 +1,3 @@
1
1
  export * from "./Autocomplete";
2
+ export * from "./AutocompleteInput";
2
3
  export * from "./AutocompleteTypes";
@@ -63,6 +63,7 @@ export interface TypeMenuContextProps
63
63
  TypeDownshiftMultiSelectReturnValue,
64
64
  TypeDownshiftMultiSelectProps {
65
65
  items: TypeDefaultItemProps[];
66
+ itemsMap?: { [key: string]: TypeDefaultItemProps };
66
67
  isListbox?: boolean;
67
68
  onHighlightedIndexChange?:
68
69
  | TypeDownshiftComboboxProps["onHighlightedIndexChange"]
@@ -94,8 +95,9 @@ export interface TypeMenuContext extends TypeMenuContextProps {
94
95
  itemsMap: { [key: string]: TypeDefaultItemProps };
95
96
  }
96
97
 
97
- export interface TypeMenuProviderProps extends Partial<TypeMenuContextProps> {
98
+ export interface TypeMenuProviderProps {
98
99
  children: TypeNotEmptyChildren;
100
+ menuProps: Partial<TypeMenuContextProps>;
99
101
  }
100
102
 
101
103
  /**
@@ -113,17 +115,21 @@ export const useMenu = ({
113
115
  items = [],
114
116
  isListbox = false,
115
117
  isOpen = false,
118
+ itemsMap: itemsMapFromProps,
116
119
  ...rest
117
120
  }: Partial<TypeMenuContextProps> = {}): TypeMenuContext => {
118
121
  const menuProps = useMemo(() => ({ ...rest }), [rest]);
119
122
 
120
123
  const itemsMap = useMemo(
121
124
  () =>
125
+ itemsMapFromProps ||
122
126
  items.reduce((map, item) => {
123
- map[`${item.id}`] = item;
127
+ map[`${item.id}`] = {
128
+ ...item,
129
+ };
124
130
  return map;
125
131
  }, {} as TypeMenuContext["itemsMap"]),
126
- [items]
132
+ [items, itemsMapFromProps]
127
133
  );
128
134
 
129
135
  return {
@@ -145,9 +151,9 @@ export const MenuContext = createContext<TypeMenuContext>(defaultMenuContext);
145
151
  */
146
152
  export const MenuProvider = ({
147
153
  children,
148
- ...overrides
154
+ menuProps,
149
155
  }: TypeMenuProviderProps) => {
150
- const filterContext = useMenu(overrides);
156
+ const filterContext = useMenu(menuProps);
151
157
 
152
158
  return (
153
159
  <MenuContext.Provider value={{ ...filterContext }}>
@@ -61,23 +61,6 @@ Feature: Menu Group
61
61
  And a valid component is provided to the customMenuItem prop
62
62
  Then the MenuGroup will render the items using the customMenuItem component
63
63
 
64
- Scenario: MenuGroup ignores the menuItemType without items prop
65
- When a MenuGroup has menuItemType set
66
- And is not provided with a valid items prop
67
- Then the menuItemType will be ignored
68
-
69
- Scenario Outline: MenuGroup can set the type for all MenuItems
70
- When menuItemType is set to <menu item type>
71
- And MenuGroup is provided with a valid items prop
72
- Then each MenuItem rendered by the MenuGroup with use this type
73
-
74
- Examples: Prop Types
75
- | menu item type |
76
- | undefined (default) |
77
- | checkbox |
78
- | radio |
79
- | switch |
80
-
81
64
  Scenario Outline: MenuGroup has custom content
82
65
  When <custom content> is present at the <location>
83
66
  Then <displayed content> will render
@@ -93,10 +76,9 @@ Feature: Menu Group
93
76
  Then MenuRoot expects it to be of type <propType>
94
77
 
95
78
  Examples: Prop Types
96
- | propName | propType |
97
- | children | ReactNode |
98
- | title | string |
99
- | items | TypeItem[] |
100
- | isSingleSelect | boolean |
101
- | menuItemType | TypeMenuItemProps['type'] |
102
- | customMenuItem | tyepof MenuItem |
79
+ | propName | propType |
80
+ | children | ReactNode |
81
+ | title | string |
82
+ | items | TypeMenuItemProps[] |
83
+ | isSingleSelect | boolean |
84
+ | MenuItemComponent | tyepof MenuItem |
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import type { TypeMenuGroupProps } from "./MenuGroupTypes";
3
3
  import { StyledMenuGroup, StyledTitle, StyledMenuGroupUl } from "./styles";
4
+ import { MenuItem } from "../MenuItem";
4
5
 
5
6
  /**
6
7
  * @link https://seeds.sproutsocial.com/components/menu-group/
@@ -24,16 +25,26 @@ import { StyledMenuGroup, StyledTitle, StyledMenuGroupUl } from "./styles";
24
25
 
25
26
  export const MenuGroup = ({
26
27
  titleAs = "h3",
27
- children,
28
+ children: childrenProp,
28
29
  title,
29
30
  color,
30
31
  isSingleSelect,
31
32
  id,
33
+ items,
34
+ MenuItemComponent = MenuItem,
32
35
  ...rest
33
36
  }: TypeMenuGroupProps) => {
34
37
  // Generate a unique ID for the title
35
38
  const titleId = `menu-group-title-${Math.random().toString(36).slice(2, 11)}`;
36
39
 
40
+ const children =
41
+ childrenProp || !items || !items.length
42
+ ? childrenProp
43
+ : items.map((item) => <MenuItemComponent key={item.id} {...item} />);
44
+
45
+ if (!children) {
46
+ return null;
47
+ }
37
48
  return (
38
49
  <StyledMenuGroup
39
50
  role="group"
@@ -9,6 +9,7 @@ import type {
9
9
  TypeSpaceSystemProps,
10
10
  TypeTypographySystemProps,
11
11
  } from "@sproutsocial/seeds-react-system-props";
12
+ import { MenuItem, type TypeMenuItemProps } from "../MenuItem/index";
12
13
 
13
14
  export interface TypeMenuGroupStyledProps
14
15
  extends TypeBorderSystemProps,
@@ -26,6 +27,8 @@ export interface TypeMenuGroupProps
26
27
  id: string;
27
28
  isSingleSelect?: boolean;
28
29
  title?: string;
29
- children: ReactNode;
30
+ children?: ReactNode;
31
+ items?: TypeMenuItemProps[];
32
+ MenuItemComponent?: typeof MenuItem;
30
33
  titleAs?: string | React.ComponentType<any>;
31
34
  }
@@ -2,9 +2,9 @@ import {
2
2
  render,
3
3
  screen,
4
4
  waitFor,
5
- fireEvent,
6
5
  act,
7
6
  } from "@sproutsocial/seeds-react-testing-library";
7
+ import { PointerEventsCheckLevel } from "@testing-library/user-event";
8
8
  import {
9
9
  SingleSelectMenu,
10
10
  MultiSelectMenu,
@@ -22,8 +22,8 @@ const menuItems = [
22
22
 
23
23
  describe("MenuGroup", () => {
24
24
  describe("User Behavior", () => {
25
- it("allows multiple selections per group when isSingleSelect is undefined", () => {
26
- render(
25
+ it("allows multiple selections per group when isSingleSelect is undefined", async () => {
26
+ const { user } = render(
27
27
  <MultiSelectMenu
28
28
  defaultIsOpen
29
29
  menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
@@ -44,7 +44,11 @@ describe("MenuGroup", () => {
44
44
 
45
45
  // Click on each option
46
46
  for (const option of screen.getAllByRole("option")) {
47
- fireEvent.click(option);
47
+ await user
48
+ .setup({
49
+ pointerEventsCheck: PointerEventsCheckLevel.Never,
50
+ })
51
+ .click(option);
48
52
  }
49
53
 
50
54
  // Expect all options to be selected.
@@ -52,8 +56,8 @@ describe("MenuGroup", () => {
52
56
  expect(option).toHaveAttribute("aria-selected", "true");
53
57
  }
54
58
  });
55
- it("allows multiple selections per group when isSingleSelect is false", () => {
56
- render(
59
+ it("allows multiple selections per group when isSingleSelect is false", async () => {
60
+ const { user } = render(
57
61
  <MultiSelectMenu
58
62
  defaultIsOpen
59
63
  menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
@@ -74,7 +78,11 @@ describe("MenuGroup", () => {
74
78
 
75
79
  // Click on each option
76
80
  for (const option of screen.getAllByRole("option")) {
77
- fireEvent.click(option);
81
+ await user
82
+ .setup({
83
+ pointerEventsCheck: PointerEventsCheckLevel.Never,
84
+ })
85
+ .click(option);
78
86
  }
79
87
 
80
88
  // Expect all options to be selected.
@@ -83,7 +91,7 @@ describe("MenuGroup", () => {
83
91
  }
84
92
  });
85
93
  it("only allows one selection per group when isSingleSelect is true", async () => {
86
- render(
94
+ const { user } = render(
87
95
  <MultiSelectMenu
88
96
  defaultIsOpen
89
97
  menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
@@ -102,24 +110,35 @@ describe("MenuGroup", () => {
102
110
 
103
111
  const options = screen.getAllByRole("option");
104
112
 
105
- // Click on each option
106
- for (const option of screen.getAllByRole("option")) {
107
- fireEvent.click(option);
108
- }
113
+ // Click on the first option
114
+ await user
115
+ .setup({
116
+ pointerEventsCheck: PointerEventsCheckLevel.Never,
117
+ })
118
+ .click(options[0]);
109
119
 
110
- // Expect only the last option to be selected.
120
+ // Confirm it is selected
111
121
  await waitFor(() =>
112
- expect(options[0]).toHaveAttribute("aria-selected", "false")
122
+ expect(options[0]).toHaveAttribute("aria-selected", "true")
113
123
  );
124
+
125
+ // Click on the second option
126
+ await user
127
+ .setup({
128
+ pointerEventsCheck: PointerEventsCheckLevel.Never,
129
+ })
130
+ .click(options[1]);
131
+
132
+ // Expect only the most recently clicked option to be selected.
114
133
  await waitFor(() =>
115
- expect(options[1]).toHaveAttribute("aria-selected", "false")
134
+ expect(options[1]).toHaveAttribute("aria-selected", "true")
116
135
  );
117
136
  await waitFor(() =>
118
- expect(options[2]).toHaveAttribute("aria-selected", "true")
137
+ expect(options[0]).toHaveAttribute("aria-selected", "false")
119
138
  );
120
139
  });
121
140
  it("only allows one selection per group in SingleSelectMenu, even if isSingleSelect is false", async () => {
122
- render(
141
+ const { user } = render(
123
142
  <SingleSelectMenu
124
143
  defaultIsOpen
125
144
  menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
@@ -140,7 +159,11 @@ describe("MenuGroup", () => {
140
159
 
141
160
  // Click on each option
142
161
  for (const option of screen.getAllByRole("option")) {
143
- fireEvent.click(option);
162
+ await user
163
+ .setup({
164
+ pointerEventsCheck: PointerEventsCheckLevel.Never,
165
+ })
166
+ .click(option);
144
167
  }
145
168
 
146
169
  // Expect only the last option to be selected.
@@ -34,9 +34,11 @@ export const MenuGroupTypeTest = () => (
34
34
  <MenuGroup id="group" border="1px solid black" bg="white" width="150px">
35
35
  Test
36
36
  </MenuGroup>
37
+ {/* MenuGroup allows children prop */}
38
+ <MenuGroup id="group">Test</MenuGroup>
39
+ {/* MenuGroup allows items prop */}
40
+ <MenuGroup id="group" items={[{ id: "1", children: "1" }]} />
37
41
 
38
- {/* @ts-expect-error - MenuGroup requires children */}
39
- <MenuGroup id="group" />
40
42
  {/* @ts-expect-error - MenuGroup title is invalid as number */}
41
43
  <MenuGroup id="group" title={123}>
42
44
  Test
@@ -1,5 +1,5 @@
1
- import React, { useContext } from "react";
2
- import { MenuContext } from "../MenuContext";
1
+ import { useMemo } from "react";
2
+ import { useMenuContext } from "../MenuContext";
3
3
  import type { TypeDefaultItemProps } from "../types";
4
4
  import type { TypeMenuItemProps } from "./MenuItemTypes";
5
5
  import { MenuItemContainer, StyledMenuItem } from "./styles";
@@ -24,16 +24,19 @@ export const MenuItem = ({
24
24
  ...props
25
25
  }: TypeMenuItemProps) => {
26
26
  const { getItemProps, itemsMap, highlightedIndex, isListbox } =
27
- useContext(MenuContext);
28
- const item = (itemsMap[id] || { index: 0, id }) as TypeDefaultItemProps;
27
+ useMenuContext();
29
28
  const { ...optionProps } = useOptionProps({
30
29
  id,
31
30
  children,
32
31
  ...props,
33
32
  });
33
+ const item = (itemsMap[id] || { index: 0, id }) as TypeDefaultItemProps;
34
+
34
35
  const highlighted = highlightedIndex === item?.index;
35
36
 
36
- return (
37
+ return item.hidden ? (
38
+ <></>
39
+ ) : (
37
40
  <MenuItemContainer role="none">
38
41
  <StyledMenuItem
39
42
  id={id}
package/src/MenuLabel.tsx CHANGED
@@ -1,12 +1,12 @@
1
1
  import React from "react";
2
- import { Text, type TypeTextProps } from "@sproutsocial/seeds-react-text";
2
+ import { Label, type TypeLabelProps } from "@sproutsocial/seeds-react-label";
3
3
  import { useMenuContext } from "./MenuContext";
4
4
 
5
- export const MenuLabel = ({ children, ...rest }: TypeTextProps) => {
5
+ export const MenuLabel = ({ children, ...rest }: Partial<TypeLabelProps>) => {
6
6
  const { getLabelProps } = useMenuContext();
7
7
  return (
8
- <Text as="label" color="text.body" {...getLabelProps?.()} {...rest}>
8
+ <Label htmlFor="fallback_menu_label" {...getLabelProps?.()} {...rest}>
9
9
  {children}
10
- </Text>
10
+ </Label>
11
11
  );
12
12
  };
@@ -12,7 +12,8 @@ export interface TypeMenuRootOwnProps {
12
12
  width?: TypePopoutProps["width"];
13
13
  }
14
14
  export interface TypeMenuRootProps
15
- extends Partial<TypeMenuProviderProps>,
15
+ extends Partial<TypeMenuProviderProps["menuProps"]>,
16
+ Pick<TypeMenuProviderProps, "children">,
16
17
  TypeMenuRootOwnProps {}
17
18
 
18
19
  const CustomPopoutContent = styled(Popout.Content)`
@@ -30,7 +31,7 @@ const CustomPopoutContent = styled(Popout.Content)`
30
31
  export const MenuRoot = ({
31
32
  children,
32
33
  menuToggleElement,
33
- popoutProps,
34
+ popoutProps: { focusLockProps, ...popoutProps } = {},
34
35
  width = "300px",
35
36
  ...contextProps
36
37
  }: TypeMenuRootProps) => {
@@ -43,7 +44,7 @@ export const MenuRoot = ({
43
44
  );
44
45
 
45
46
  return (
46
- <MenuProvider {...contextProps}>
47
+ <MenuProvider menuProps={contextProps}>
47
48
  <Popout
48
49
  menuPopout
49
50
  isOpen={!!isOpen}
@@ -55,6 +56,7 @@ export const MenuRoot = ({
55
56
  // correct jerking motion when scrolling while the Popout is open
56
57
  // returnFocus: false,
57
58
  crossFrame: false,
59
+ ...focusLockProps,
58
60
  }}
59
61
  disableWrapperAria
60
62
  {...popoutProps}
@@ -24,11 +24,11 @@ export const SingleSelectMenu = ({
24
24
  stateReducer: externalStateReducer,
25
25
  ...useSelectProps
26
26
  }: TypeSingleSelectMenuProps) => {
27
- /** Import the manipulated map of menu items from the context */
27
+ /** Get a list of items from the children */
28
28
  const { allMenuItems, menuItemsMap } = useItemsFromChildren({ children });
29
29
 
30
30
  /**
31
- * Destructure Downshift props and call the core useSelect with the new menu items and state handler
31
+ * Destructure Downshift props and call the core useSelect with items and state handler
32
32
  */
33
33
  const { isOpen, ...useSelectReturnProps } = useSelect({
34
34
  ...defaultUseSelectProps,
@@ -0,0 +1,36 @@
1
+ interface TypeBook {
2
+ id: string;
3
+ author: string;
4
+ title: string;
5
+ }
6
+
7
+ export const TEST_BOOKS: TypeBook[] = [
8
+ { id: "book-1", author: "Harper Lee", title: "To Kill a Mockingbird" },
9
+ { id: "book-2", author: "Leo Tolstoy", title: "War and Peace" },
10
+ { id: "book-3", author: "Fyodor Dostoyevsy", title: "The Idiot" },
11
+ { id: "book-4", author: "Oscar Wilde", title: "A Picture of Dorian Gray" },
12
+ { id: "book-5", author: "George Orwell", title: "Nineteen Eighty Four" },
13
+ { id: "book-6", author: "Jane Austen", title: "Pride and Prejudice" },
14
+ { id: "book-7", author: "Marcus Aurelius", title: "Meditations" },
15
+ {
16
+ id: "book-8",
17
+ author: "Fyodor Dostoevsky",
18
+ title: "The Brothers Karamazov",
19
+ },
20
+ { id: "book-9", author: "Lev Tolstoy", title: "Anna Karenina" },
21
+ {
22
+ id: "book-10",
23
+ author: "Fyodor Dostoevsky",
24
+ title: "Crime and Punishment",
25
+ },
26
+ ];
27
+
28
+ export const EXTRA_BOOKS: TypeBook[] = [
29
+ { id: "book-11", author: "F. Scott Fitzgerald", title: "The Great Gatsby" },
30
+ { id: "book-12", author: "J. D. Salinger", title: "The Catcher in the Rye" },
31
+ { id: "book-13", author: "J. R. R. Tolkien", title: "The Lord of the Rings" },
32
+ { id: "book-14", author: "John Steinbeck", title: "The Grapes of Wrath" },
33
+ { id: "book-15", author: "George Orwell", title: "Animal Farm" },
34
+ { id: "book-16", author: "Jack Kerouac", title: "On the Road" },
35
+ { id: "book-17", author: "Aldous Huxley", title: "Brave New World" },
36
+ ];