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
}