diff --git a/.changeset/action-list-small-size.md b/.changeset/action-list-small-size.md new file mode 100644 index 00000000000..c1af54a12bb --- /dev/null +++ b/.changeset/action-list-small-size.md @@ -0,0 +1,5 @@ +--- +'@primer/react': minor +--- + +ActionList: Add a `size` prop with a `small` option for compact lists. diff --git a/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-dark-colorblind-linux.png b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-dark-colorblind-linux.png new file mode 100644 index 00000000000..e28b89e48ba Binary files /dev/null and b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-dark-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-dark-dimmed-linux.png b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-dark-dimmed-linux.png new file mode 100644 index 00000000000..34da68c4f0e Binary files /dev/null and b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-dark-dimmed-linux.png differ diff --git a/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-dark-high-contrast-linux.png b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-dark-high-contrast-linux.png new file mode 100644 index 00000000000..d6e40315f3f Binary files /dev/null and b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-dark-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-dark-linux.png b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-dark-linux.png new file mode 100644 index 00000000000..e28b89e48ba Binary files /dev/null and b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-dark-linux.png differ diff --git a/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-dark-tritanopia-linux.png b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-dark-tritanopia-linux.png new file mode 100644 index 00000000000..e28b89e48ba Binary files /dev/null and b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-dark-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-light-colorblind-linux.png b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-light-colorblind-linux.png new file mode 100644 index 00000000000..bea91d221cd Binary files /dev/null and b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-light-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-light-high-contrast-linux.png b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-light-high-contrast-linux.png new file mode 100644 index 00000000000..f68b585e286 Binary files /dev/null and b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-light-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-light-linux.png b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-light-linux.png new file mode 100644 index 00000000000..bea91d221cd Binary files /dev/null and b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-light-linux.png differ diff --git a/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-light-tritanopia-linux.png b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-light-tritanopia-linux.png new file mode 100644 index 00000000000..bea91d221cd Binary files /dev/null and b/.playwright/snapshots/components/ActionList.test.ts-snapshots/ActionList-Small-List-light-tritanopia-linux.png differ diff --git a/e2e/components/ActionList.test.ts b/e2e/components/ActionList.test.ts index 3c0706bbade..65a3bd7d484 100644 --- a/e2e/components/ActionList.test.ts +++ b/e2e/components/ActionList.test.ts @@ -40,6 +40,10 @@ const stories = [ title: 'Simple List', id: 'components-actionlist-features--simple-list', }, + { + title: 'Small List', + id: 'components-actionlist-features--small-list', + }, { title: 'Single Divider', id: 'components-actionlist-features--single-divider', diff --git a/packages/react/src/ActionList/ActionList.docs.json b/packages/react/src/ActionList/ActionList.docs.json index f97bb51268f..8fa83de271f 100644 --- a/packages/react/src/ActionList/ActionList.docs.json +++ b/packages/react/src/ActionList/ActionList.docs.json @@ -25,6 +25,12 @@ "defaultValue": "", "description": "Whether multiple items or a single item can be selected." }, + { + "name": "size", + "type": "'small' | 'medium'", + "defaultValue": "'medium'", + "description": "The size of the ActionList items." + }, { "name": "showDividers", "type": "boolean", @@ -57,7 +63,7 @@ }, { "name": "size", - "type": "'medium' | 'large'", + "type": "'small' | 'medium' | 'large'", "defaultValue": "'medium'", "description": "The block size of the ActionList items." }, @@ -355,4 +361,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/packages/react/src/ActionList/ActionList.features.stories.tsx b/packages/react/src/ActionList/ActionList.features.stories.tsx index ef111d40bcf..6ed5497b6db 100644 --- a/packages/react/src/ActionList/ActionList.features.stories.tsx +++ b/packages/react/src/ActionList/ActionList.features.stories.tsx @@ -50,6 +50,27 @@ export const SimpleList = () => ( ) +export const SmallList = () => ( + + + + + + Copy link + + + Quote reply + Reply with selected text + + + + + + View repository + + +) + export const WithVisualListHeading = () => ( diff --git a/packages/react/src/ActionList/ActionList.module.css b/packages/react/src/ActionList/ActionList.module.css index 6195a4a1bb8..3d9fb91c71d 100644 --- a/packages/react/src/ActionList/ActionList.module.css +++ b/packages/react/src/ActionList/ActionList.module.css @@ -540,6 +540,16 @@ padding-block: var(--control-large-paddingBlock); } + &[data-size='small'] { + /* stylelint-disable-next-line primer/spacing */ + padding-block: var(--control-small-paddingBlock); + + & .ItemLabel, + & .TrailingVisual { + font-size: var(--text-body-size-small); + } + } + /* collapsible item [aria-expanded] */ &[aria-expanded='true'] { diff --git a/packages/react/src/ActionList/ActionList.stories.tsx b/packages/react/src/ActionList/ActionList.stories.tsx index 7d846aaa26c..a13df95f2e9 100644 --- a/packages/react/src/ActionList/ActionList.stories.tsx +++ b/packages/react/src/ActionList/ActionList.stories.tsx @@ -30,6 +30,7 @@ export const Playground: StoryFn = args => ( ) Playground.args = { + size: 'medium', showDividers: false, selectionVariant: undefined, variant: 'inset', @@ -46,6 +47,12 @@ Playground.argTypes = { }, options: ['inset', 'horizontal-inset', 'full'], }, + size: { + control: { + type: 'radio', + }, + options: ['small', 'medium'], + }, selectionVariant: { control: { type: 'radio', diff --git a/packages/react/src/ActionList/ActionList.test.tsx b/packages/react/src/ActionList/ActionList.test.tsx index e520c6ad03f..6376fb4626a 100644 --- a/packages/react/src/ActionList/ActionList.test.tsx +++ b/packages/react/src/ActionList/ActionList.test.tsx @@ -259,6 +259,24 @@ describe('ActionList', () => { expect(linkElements[1]).toHaveAttribute('data-size', 'medium') expect(linkElements[2]).toHaveAttribute('data-size', 'medium') // default should be medium }) + + it('should support size prop on ActionList', () => { + const {container} = HTMLRender( + + Small Item + Small Link Item + Large Item + , + ) + + const actionList = container.querySelector('[data-component="ActionList"]') + const itemElements = container.querySelectorAll('button, a') + + expect(actionList).toHaveAttribute('data-size', 'small') + expect(itemElements[0]).toHaveAttribute('data-size', 'small') + expect(itemElements[1]).toHaveAttribute('data-size', 'small') + expect(itemElements[2]).toHaveAttribute('data-size', 'large') + }) }) describe('ActionList data-component attributes', () => { diff --git a/packages/react/src/ActionList/Item.tsx b/packages/react/src/ActionList/Item.tsx index 355dd463494..24e838f79e4 100644 --- a/packages/react/src/ActionList/Item.tsx +++ b/packages/react/src/ActionList/Item.tsx @@ -86,7 +86,7 @@ const listRoleTypes = ['listbox', 'menu', 'list', 'tree'] const UnwrappedItem = ( { variant = 'default', - size = 'medium', + size: sizeProp, disabled = false, inactiveText, selected = undefined, @@ -118,8 +118,9 @@ const UnwrappedItem = ( ) : null const trailingVisual = slots.trailingVisual ?? wrappedDefaultTrailingVisual - const {role: listRole, selectionVariant: listSelectionVariant} = React.useContext(ListContext) + const {role: listRole, selectionVariant: listSelectionVariant, size: listSize} = React.useContext(ListContext) const {selectionVariant: groupSelectionVariant} = React.useContext(GroupContext) + const size = sizeProp ?? listSize ?? 'medium' const inactive = Boolean(inactiveText) // TODO change `menuContext` check to ```listRole !== undefined && ['menu', 'listbox'].includes(listRole)``` // once we have a better way to handle existing usage in dotcom that incorrectly use ActionList.TrailingAction diff --git a/packages/react/src/ActionList/List.tsx b/packages/react/src/ActionList/List.tsx index 1ab0a116f0b..e5143d44946 100644 --- a/packages/react/src/ActionList/List.tsx +++ b/packages/react/src/ActionList/List.tsx @@ -20,6 +20,7 @@ const UnwrappedList = ( as: Component = 'ul', variant = 'inset', selectionVariant, + size = 'medium', showDividers = false, role, disableFocusZone = false, @@ -62,11 +63,12 @@ const UnwrappedList = ( () => ({ variant, selectionVariant: selectionVariant || containerSelectionVariant, + size, showDividers, role: listRole, headingId, }), - [variant, selectionVariant, containerSelectionVariant, showDividers, listRole, headingId], + [variant, selectionVariant, containerSelectionVariant, size, showDividers, listRole, headingId], ) // Replaces a CSS `:has([data-has-description])` selector that caused full-subtree @@ -109,6 +111,7 @@ const UnwrappedList = ( data-component="ActionList" data-dividers={showDividers} data-variant={variant} + data-size={size} data-item-gap={itemGapEnabled ? '' : undefined} {...restProps} > diff --git a/packages/react/src/ActionList/shared.ts b/packages/react/src/ActionList/shared.ts index af98c9cd30a..e065789a752 100644 --- a/packages/react/src/ActionList/shared.ts +++ b/packages/react/src/ActionList/shared.ts @@ -33,7 +33,7 @@ export type ActionListItemProps = ExcludeSe * - `"danger"` - A destructive action `Item`. */ variant?: 'default' | 'danger' - size?: 'medium' | 'large' + size?: 'small' | 'medium' | 'large' /** * Items that are disabled can not be clicked, selected, or navigated through. */ @@ -143,6 +143,10 @@ export type ActionListProps = PolymorphicPr * Whether multiple Items or a single Item can be selected. */ selectionVariant?: 'single' | 'radio' | 'multiple' + /** + * The size of the ActionList items. + */ + size?: 'small' | 'medium' /** * Display a divider above each `Item` in this `List` when it does not follow a `Header` or `Divider`. */ @@ -161,7 +165,7 @@ export type ActionListProps = PolymorphicPr type ContextProps = Pick< ActionListProps, - 'variant' | 'selectionVariant' | 'showDividers' | 'role' + 'variant' | 'selectionVariant' | 'showDividers' | 'role' | 'size' > & { headingId?: string }