diff --git a/packages/collection-toolbar/package.json b/packages/collection-toolbar/package.json index 938d33fd8e..6856b70754 100644 --- a/packages/collection-toolbar/package.json +++ b/packages/collection-toolbar/package.json @@ -1,4 +1,3 @@ - { "name": "@leafygreen-ui/collection-toolbar", "version": "0.0.1", @@ -28,10 +27,15 @@ "access": "public" }, "dependencies": { + "@leafygreen-ui/button": "workspace:^", + "@leafygreen-ui/compound-component": "workspace:^", "@leafygreen-ui/emotion": "workspace:^", + "@leafygreen-ui/icon": "workspace:^", + "@leafygreen-ui/icon-button": "workspace:^", "@leafygreen-ui/lib": "workspace:^", "@leafygreen-ui/tokens": "workspace:^", - "@lg-tools/test-harnesses": "workspace:^" + "@lg-tools/test-harnesses": "workspace:^", + "@leafygreen-ui/hooks": "workspace:^" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "workspace:^5.0.0 || ^4.0.0 || ^3.2.0" diff --git a/packages/collection-toolbar/src/CollectionToolbar.stories.tsx b/packages/collection-toolbar/src/CollectionToolbar.stories.tsx index be8ae36fd8..50a4051f22 100644 --- a/packages/collection-toolbar/src/CollectionToolbar.stories.tsx +++ b/packages/collection-toolbar/src/CollectionToolbar.stories.tsx @@ -2,7 +2,12 @@ import React from 'react'; import { storybookArgTypes, StoryMetaType } from '@lg-tools/storybook-utils'; import { StoryFn } from '@storybook/react'; -import { CollectionToolbar, Size, Variant } from '.'; +import { + CollectionToolbar, + CollectionToolbarActionsButtonVariant, + Size, + Variant, +} from '.'; const meta: StoryMetaType = { title: 'Components/CollectionToolbar', @@ -28,6 +33,22 @@ const meta: StoryMetaType = { options: Object.values(Variant), }, }, + decorators: [ + (Story: StoryFn) => { + return ( +
+ +
+ ); + }, + ], args: { children: 'Collection Toolbar', }, @@ -36,5 +57,18 @@ const meta: StoryMetaType = { export default meta; export const LiveExample: StoryFn = props => ( - + + + + Default Button + + + Primary Button + + + ); diff --git a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbar.styles.ts b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbar.styles.ts index b793f985f6..61fe1ec8f5 100644 --- a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbar.styles.ts +++ b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbar.styles.ts @@ -1,8 +1,17 @@ import { css, cx } from '@leafygreen-ui/emotion'; +import { spacing } from '@leafygreen-ui/tokens'; import { Size, Variant } from './CollectionToolbar.types'; -export const baseStyles = css``; +export const baseStyles = css` + width: 100%; + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: ${spacing[200]}px; + align-items: center; + justify-content: flex-end; +`; export const getCollectionToolbarStyles = ({ size, diff --git a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbar.tsx b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbar.tsx index f9e3f062e0..003fdbbe9b 100644 --- a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbar.tsx +++ b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbar.tsx @@ -1,23 +1,59 @@ -import React from 'react'; +import React, { useState } from 'react'; + +import { + CompoundComponent, + findChild, +} from '@leafygreen-ui/compound-component'; + +import { getLgIds } from '../utils/getLgIds'; import { getCollectionToolbarStyles } from './CollectionToolbar.styles'; import { CollectionToolbarProps, + CollectionToolbarSubComponentProperty, Size, Variant, } from './CollectionToolbar.types'; +import { CollectionToolbarActions } from './CollectionToolbarActions'; +import { CollectionToolbarContextProvider } from './CollectionToolbarContext'; + +export const CollectionToolbar = CompoundComponent( + ({ + size = Size.Default, + variant = Variant.Default, + className, + children, + }: CollectionToolbarProps) => { + const lgIds = getLgIds(); + const [isCollapsed, setIsCollapsed] = useState(false); + + const toggleCollapse = () => { + setIsCollapsed(curr => !curr); + }; -export function CollectionToolbar({ - size = Size.Default, - variant = Variant.Default, - className, - children, -}: CollectionToolbarProps) { - return ( -
- {children} -
- ); -} + const Actions = findChild( + children, + CollectionToolbarSubComponentProperty.Actions, + ); -CollectionToolbar.displayName = 'CollectionToolbar'; + return ( + +
+ {Actions} +
+
+ ); + }, + { + displayName: 'CollectionToolbar', + Actions: CollectionToolbarActions, + }, +); diff --git a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbar.types.ts b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbar.types.ts index cb9154409a..4d9e51da43 100644 --- a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbar.types.ts +++ b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbar.types.ts @@ -21,3 +21,24 @@ export interface CollectionToolbarProps { className?: string; children?: React.ReactNode; } + +/** + * Static property names used to identify CollectionToolbar compound components. + * These are implementation details for the compound component pattern and should not be exported. + */ +export const CollectionToolbarSubComponentProperty = { + Title: 'isCollectionToolbarTitle', + SearchInput: 'isCollectionToolbarSearchInput', + Actions: 'isCollectionToolbarActions', + ActionsButtons: 'isCollectionToolbarActionsButtons', + ActionsMenu: 'isCollectionToolbarActionsMenu', + ActionsMenuItems: 'isCollectionToolbarActionsMenuItems', + ActionsPaginationView: 'isCollectionToolbarActionsPaginationView', + Filters: 'isCollectionToolbarFilters', +} as const; + +/** + * Type representing the possible static property names for CollectionToolbar sub components. + */ +export type CollectionToolbarSubComponentProperty = + (typeof CollectionToolbarSubComponentProperty)[keyof typeof CollectionToolbarSubComponentProperty]; diff --git a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.spec.tsx b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.spec.tsx new file mode 100644 index 0000000000..941c403d8b --- /dev/null +++ b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.spec.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; + +import { CollectionToolbarActions } from '.'; + +describe('packages/collection-toolbar-actions', () => { + test('renders correctly', () => { + render(); + expect(screen.getByText('Collection Toolbar Actions')).toBeInTheDocument(); + }); +}); diff --git a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.styles.ts b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.styles.ts new file mode 100644 index 0000000000..865a1df00a --- /dev/null +++ b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.styles.ts @@ -0,0 +1,19 @@ +import { css, cx } from '@leafygreen-ui/emotion'; +import { spacing } from '@leafygreen-ui/tokens'; + +import { Variant } from '../CollectionToolbar.types'; + +export const baseStyles = css` + display: flex; + flex-direction: row; + gap: ${spacing[200]}px; + align-items: center; + justify-content: flex-end; +`; + +export const getCollectionToolbarActionsStyles = ({ + className, +}: { + variant?: Variant; + className?: string; +}) => cx(baseStyles, className); diff --git a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.tsx b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.tsx new file mode 100644 index 0000000000..5551d16ef1 --- /dev/null +++ b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.tsx @@ -0,0 +1,66 @@ +import React from 'react'; + +import { + CompoundSubComponent, + findChildren, +} from '@leafygreen-ui/compound-component'; +import { Icon } from '@leafygreen-ui/icon'; +import { IconButton } from '@leafygreen-ui/icon-button'; +import { consoleOnce } from '@leafygreen-ui/lib'; + +import { CollectionToolbarActionsSubComponentProperty } from '../../shared.types'; +import { getLgIds } from '../../utils/getLgIds'; +import { + CollectionToolbarSubComponentProperty, + Variant, +} from '../CollectionToolbar.types'; +import { useCollectionToolbarContext } from '../CollectionToolbarContext'; + +import { getCollectionToolbarActionsStyles } from './CollectionToolbarActions.styles'; +import { CollectionToolbarActionsProps } from './CollectionToolbarActions.types'; +import { CollectionToolbarActionsButton } from './CollectionToolbarActionsButton'; + +export const CollectionToolbarActions = CompoundSubComponent( + ({ children, className }: CollectionToolbarActionsProps) => { + const lgIds = getLgIds(); + const { onCollapse, variant } = useCollectionToolbarContext(); + + const showToggleButton = variant === Variant.Collapsible; + + const Buttons = findChildren( + children, + CollectionToolbarActionsSubComponentProperty.Button, + ); + + if (Buttons.length > 2) { + consoleOnce.error( + 'CollectionToolbarActions can only have up to 2 buttons', + ); + } + + const PrimaryButtons = Buttons.slice(0, 2); + + return ( +
+ {PrimaryButtons} + {showToggleButton && ( + + + + )} +
+ ); + }, + { + displayName: 'CollectionToolbarActions', + key: CollectionToolbarSubComponentProperty.Actions, + Button: CollectionToolbarActionsButton, + }, +); diff --git a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.types.ts b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.types.ts new file mode 100644 index 0000000000..9c28e18923 --- /dev/null +++ b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.types.ts @@ -0,0 +1,4 @@ +import { CollectionToolbarProps } from '../CollectionToolbar.types'; + +export interface CollectionToolbarActionsProps + extends Pick {} diff --git a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActionsButton/CollectionToolbarActionsButton.tsx b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActionsButton/CollectionToolbarActionsButton.tsx new file mode 100644 index 0000000000..5366193702 --- /dev/null +++ b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActionsButton/CollectionToolbarActionsButton.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +import { Button } from '@leafygreen-ui/button'; +import { CompoundSubComponent } from '@leafygreen-ui/compound-component'; + +import { useCollectionToolbarContext } from '../../CollectionToolbarContext'; +import { CollectionToolbarActionsSubComponentProperty } from '../CollectionToolbarActions.types'; + +import { CollectionToolbarActionsButtonProps } from './CollectionToolbarActionsButton.types'; + +export const CollectionToolbarActionsButton = CompoundSubComponent( + ({ ...props }: CollectionToolbarActionsButtonProps) => { + const { size } = useCollectionToolbarContext(); + return