From 010ef2c93adf3c845325edcb9ffb63a5e096b079 Mon Sep 17 00:00:00 2001 From: Adam Rasheed Date: Sat, 27 Dec 2025 21:53:42 -0800 Subject: [PATCH 1/3] [LG-5840] feat(collection-toolbar): Collection toolbar actions --- .vscode/settings.json | 21 +- packages/collection-toolbar/package.json | 8 +- .../src/CollectionToolbar.stories.tsx | 38 +- .../CollectionToolbar.styles.ts | 11 +- .../CollectionToolbar/CollectionToolbar.tsx | 60 +- .../CollectionToolbar.types.ts | 21 + .../CollectionToolbarActions.spec.ts | 16 + .../CollectionToolbarActions.styles.ts | 20 + .../CollectionToolbarActions.tsx | 60 ++ .../CollectionToolbarActions.types.ts | 14 + .../CollectionToolbarActionsButton.tsx | 20 + .../CollectionToolbarActionsButton.types.ts | 4 + .../CollectionToolbarActionsButton/index.ts | 3 + .../CollectionToolbarActions/index.ts | 7 + .../CollectionToolbarContext.tsx | 49 ++ .../CollectionToolbarContext/index.ts | 4 + .../src/CollectionToolbar/index.ts | 5 + packages/collection-toolbar/src/index.ts | 3 + packages/collection-toolbar/tsconfig.json | 15 + pnpm-lock.yaml | 643 +++++++++++++++++- tools/install/src/ALL_PACKAGES.ts | 1 + 21 files changed, 994 insertions(+), 29 deletions(-) create mode 100644 packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.spec.ts create mode 100644 packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.styles.ts create mode 100644 packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.tsx create mode 100644 packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.types.ts create mode 100644 packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActionsButton/CollectionToolbarActionsButton.tsx create mode 100644 packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActionsButton/CollectionToolbarActionsButton.types.ts create mode 100644 packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActionsButton/index.ts create mode 100644 packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/index.ts create mode 100644 packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarContext/CollectionToolbarContext.tsx create mode 100644 packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarContext/index.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index c91191360b..bddbf86186 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -42,5 +42,24 @@ { "file": "node_modules/@lg-tools/prompt-kit/src/prompts/pullRequestDescriptionGeneration.md" } - ] + ], + "workbench.colorCustomizations": { + "activityBar.activeBackground": "#009b6e", + "activityBar.background": "#009b6e", + "activityBar.foreground": "#e7e7e7", + "activityBar.inactiveForeground": "#e7e7e799", + "activityBarBadge.background": "#8800bf", + "activityBarBadge.foreground": "#e7e7e7", + "commandCenter.border": "#e7e7e799", + "sash.hoverBorder": "#009b6e", + "statusBar.background": "#00684a", + "statusBar.foreground": "#e7e7e7", + "statusBarItem.hoverBackground": "#009b6e", + "statusBarItem.remoteBackground": "#00684a", + "statusBarItem.remoteForeground": "#e7e7e7", + "titleBar.activeBackground": "#00684a", + "titleBar.activeForeground": "#e7e7e7", + "titleBar.inactiveBackground": "#00684a99", + "titleBar.inactiveForeground": "#e7e7e799" + } } 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..aafe8ad86f 100644 --- a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbar.tsx +++ b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbar.tsx @@ -1,23 +1,55 @@ -import React from 'react'; +import React, { useState } from 'react'; + +import { + CompoundComponent, + findChild, +} from '@leafygreen-ui/compound-component'; 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 [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.ts b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.spec.ts new file mode 100644 index 0000000000..5afb2cf545 --- /dev/null +++ b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.spec.ts @@ -0,0 +1,16 @@ +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..e75422bcda --- /dev/null +++ b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.styles.ts @@ -0,0 +1,20 @@ +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 = ({ + variant, + 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..8aa9790ec8 --- /dev/null +++ b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.tsx @@ -0,0 +1,60 @@ +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 { + CollectionToolbarSubComponentProperty, + Variant, +} from '../CollectionToolbar.types'; +import { useCollectionToolbarContext } from '../CollectionToolbarContext'; + +import { getCollectionToolbarActionsStyles } from './CollectionToolbarActions.styles'; +import { + CollectionToolbarActionsProps, + CollectionToolbarActionsSubComponentProperty, +} from './CollectionToolbarActions.types'; +import { CollectionToolbarActionsButton } from './CollectionToolbarActionsButton'; + +export const CollectionToolbarActions = CompoundSubComponent( + ({ children }: CollectionToolbarActionsProps) => { + 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..4c69ba006a --- /dev/null +++ b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.types.ts @@ -0,0 +1,14 @@ +import { CollectionToolbarProps } from '../CollectionToolbar.types'; + +export interface CollectionToolbarActionsProps + extends Pick {} + +export const CollectionToolbarActionsSubComponentProperty = { + Button: 'isCollectionToolbarActionsButton', + Menu: 'isCollectionToolbarActionsMenu', + MenuItems: 'isCollectionToolbarActionsMenuItems', + PaginationView: 'isCollectionToolbarActionsPaginationView', +} as const; + +export type CollectionToolbarActionsSubComponentProperty = + (typeof CollectionToolbarActionsSubComponentProperty)[keyof typeof CollectionToolbarActionsSubComponentProperty]; 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 - - , - ); + 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 index e75422bcda..865a1df00a 100644 --- a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.styles.ts +++ b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.styles.ts @@ -12,7 +12,6 @@ export const baseStyles = css` `; export const getCollectionToolbarActionsStyles = ({ - variant, className, }: { variant?: Variant; diff --git a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.tsx b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.tsx index 8aa9790ec8..4b51e007df 100644 --- a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.tsx +++ b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.tsx @@ -8,6 +8,7 @@ import { Icon } from '@leafygreen-ui/icon'; import { IconButton } from '@leafygreen-ui/icon-button'; import { consoleOnce } from '@leafygreen-ui/lib'; +import { getLgIds } from '../../utils/getLgIds'; import { CollectionToolbarSubComponentProperty, Variant, @@ -22,9 +23,12 @@ import { import { CollectionToolbarActionsButton } from './CollectionToolbarActionsButton'; export const CollectionToolbarActions = CompoundSubComponent( - ({ children }: CollectionToolbarActionsProps) => { + ({ children, className }: CollectionToolbarActionsProps) => { + const lgIds = getLgIds(); const { onCollapse, variant } = useCollectionToolbarContext(); + const showToggleButton = variant === Variant.Collapsible; + const Buttons = findChildren( children, CollectionToolbarActionsSubComponentProperty.Button, @@ -40,12 +44,16 @@ export const CollectionToolbarActions = CompoundSubComponent( return (
{PrimaryButtons} {showToggleButton && ( - + )} diff --git a/packages/collection-toolbar/src/utils/getLgIds.ts b/packages/collection-toolbar/src/utils/getLgIds.ts index 35a1a7186e..198e5a5287 100644 --- a/packages/collection-toolbar/src/utils/getLgIds.ts +++ b/packages/collection-toolbar/src/utils/getLgIds.ts @@ -5,6 +5,8 @@ export const DEFAULT_LGID_ROOT = 'lg-collection_toolbar'; export const getLgIds = (root: LgIdString = DEFAULT_LGID_ROOT) => { const ids = { root, + actions: `${root}-actions`, + toggleButton: `${root}-toggle_button`, } as const; return ids; }; From 8d88e1f4adfdc22057dd1b0831dff216b221268b Mon Sep 17 00:00:00 2001 From: Adam Rasheed Date: Tue, 30 Dec 2025 10:09:44 -0800 Subject: [PATCH 3/3] updating types --- .../CollectionToolbarActions.tsx | 6 ++---- .../CollectionToolbarActions.types.ts | 10 ---------- packages/collection-toolbar/src/shared.types.ts | 9 +++++++++ 3 files changed, 11 insertions(+), 14 deletions(-) create mode 100644 packages/collection-toolbar/src/shared.types.ts diff --git a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.tsx b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.tsx index 4b51e007df..5551d16ef1 100644 --- a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.tsx +++ b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.tsx @@ -8,6 +8,7 @@ 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, @@ -16,10 +17,7 @@ import { import { useCollectionToolbarContext } from '../CollectionToolbarContext'; import { getCollectionToolbarActionsStyles } from './CollectionToolbarActions.styles'; -import { - CollectionToolbarActionsProps, - CollectionToolbarActionsSubComponentProperty, -} from './CollectionToolbarActions.types'; +import { CollectionToolbarActionsProps } from './CollectionToolbarActions.types'; import { CollectionToolbarActionsButton } from './CollectionToolbarActionsButton'; export const CollectionToolbarActions = CompoundSubComponent( diff --git a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.types.ts b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.types.ts index 4c69ba006a..9c28e18923 100644 --- a/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.types.ts +++ b/packages/collection-toolbar/src/CollectionToolbar/CollectionToolbarActions/CollectionToolbarActions.types.ts @@ -2,13 +2,3 @@ import { CollectionToolbarProps } from '../CollectionToolbar.types'; export interface CollectionToolbarActionsProps extends Pick {} - -export const CollectionToolbarActionsSubComponentProperty = { - Button: 'isCollectionToolbarActionsButton', - Menu: 'isCollectionToolbarActionsMenu', - MenuItems: 'isCollectionToolbarActionsMenuItems', - PaginationView: 'isCollectionToolbarActionsPaginationView', -} as const; - -export type CollectionToolbarActionsSubComponentProperty = - (typeof CollectionToolbarActionsSubComponentProperty)[keyof typeof CollectionToolbarActionsSubComponentProperty]; diff --git a/packages/collection-toolbar/src/shared.types.ts b/packages/collection-toolbar/src/shared.types.ts new file mode 100644 index 0000000000..06d3532ff4 --- /dev/null +++ b/packages/collection-toolbar/src/shared.types.ts @@ -0,0 +1,9 @@ +export const CollectionToolbarActionsSubComponentProperty = { + Button: 'isCollectionToolbarActionsButton', + Menu: 'isCollectionToolbarActionsMenu', + MenuItems: 'isCollectionToolbarActionsMenuItems', + PaginationView: 'isCollectionToolbarActionsPaginationView', +} as const; + +export type CollectionToolbarActionsSubComponentProperty = + (typeof CollectionToolbarActionsSubComponentProperty)[keyof typeof CollectionToolbarActionsSubComponentProperty];