diff --git a/packages/manager/.changeset/pr-10218-upcoming-features-1708616641655.md b/packages/manager/.changeset/pr-10218-upcoming-features-1708616641655.md
new file mode 100644
index 00000000000..203895b0000
--- /dev/null
+++ b/packages/manager/.changeset/pr-10218-upcoming-features-1708616641655.md
@@ -0,0 +1,5 @@
+---
+"@linode/manager": Upcoming Features
+---
+
+Add scrolling for S3 hostnames in the Access Keys modal. ([#10218](https://github.com/linode/manager/pull/10218))
diff --git a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyLanding.tsx b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyLanding.tsx
index 639b5a6d6ac..38d48b1d327 100644
--- a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyLanding.tsx
+++ b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyLanding.tsx
@@ -30,8 +30,8 @@ import { AccessKeyDrawer } from './AccessKeyDrawer';
import { AccessKeyTable } from './AccessKeyTable/AccessKeyTable';
import { OMC_AccessKeyDrawer } from './OMC_AccessKeyDrawer';
import { RevokeAccessKeyDialog } from './RevokeAccessKeyDialog';
-import { MODE, OpenAccessDrawer } from './types';
import ViewPermissionsDrawer from './ViewPermissionsDrawer';
+import { MODE, OpenAccessDrawer } from './types';
interface Props {
accessDrawerOpen: boolean;
diff --git a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/HostNamesList.test.tsx b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/HostNamesList.test.tsx
new file mode 100644
index 00000000000..2056065a2bb
--- /dev/null
+++ b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/HostNamesList.test.tsx
@@ -0,0 +1,40 @@
+import { screen } from '@testing-library/react';
+import React from 'react';
+
+import { regionFactory } from 'src/factories';
+import { makeResourcePage } from 'src/mocks/serverHandlers';
+import { rest, server } from 'src/mocks/testServer';
+import { renderWithTheme } from 'src/utilities/testHelpers';
+
+import { HostNamesList } from './HostNamesList';
+
+const mockObjectStorageKey = {
+ access_key: 'test',
+ bucket_access: null,
+ id: 0,
+ label: 'test-label',
+ limited: false,
+ regions: [{ id: 'us-central', s3_endpoint: 'http://example.com' }],
+ secret_key: 'testKey',
+};
+
+describe('HostNamesList', () => {
+ it('renders without crashing', async () => {
+ server.use(
+ rest.get('*/regions', (req, res, ctx) => {
+ const regions = regionFactory.buildList(1, {
+ id: 'us-central',
+ label: 'Fake Region, NC',
+ });
+ return res(ctx.json(makeResourcePage(regions)));
+ })
+ );
+
+ renderWithTheme();
+
+ const copyButton = await screen.findByLabelText(
+ 'Copy Fake Region, NC: http://example.com to clipboard'
+ );
+ expect(copyButton).toBeInTheDocument();
+ });
+});
diff --git a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/HostNamesList.tsx b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/HostNamesList.tsx
new file mode 100644
index 00000000000..965740e38d8
--- /dev/null
+++ b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/HostNamesList.tsx
@@ -0,0 +1,84 @@
+import { styled } from '@mui/material/styles';
+import React, { useRef } from 'react';
+
+import { Box } from 'src/components/Box';
+import { CopyableTextField } from 'src/components/CopyableTextField/CopyableTextField';
+import { List } from 'src/components/List';
+import { useRegionsQuery } from 'src/queries/regions';
+import { omittedProps } from 'src/utilities/omittedProps';
+import { getRegionsByRegionId } from 'src/utilities/regions';
+
+import type { ObjectStorageKey } from '@linode/api-v4/lib/object-storage';
+
+const maxHeight = 200;
+
+interface Props {
+ objectStorageKey: ObjectStorageKey;
+}
+
+export const HostNamesList = ({ objectStorageKey }: Props) => {
+ const { data: regionsData } = useRegionsQuery();
+ const regionsLookup = regionsData && getRegionsByRegionId(regionsData);
+
+ const listRef = useRef(null);
+ const currentListHeight = listRef?.current?.clientHeight || 0;
+
+ return (
+
+ ({
+ backgroundColor: theme.bg.main,
+ border: `1px solid ${theme.name === 'light' ? '#ccc' : '#222'}`,
+ minHeight: '34px',
+ })}
+ displayShadow={currentListHeight > maxHeight}
+ >
+
+
+ {objectStorageKey?.regions.map((region, index) => (
+
+ ))}
+
+
+
+
+ );
+};
+
+export const StyledScrollBox = styled(Box, {
+ label: 'StyledScrollBox',
+})<{ maxHeight: string }>(({ maxHeight }) => ({
+ maxHeight: `${maxHeight}px`,
+ overflow: 'auto',
+}));
+
+export const StyledBoxShadowWrapper = styled(Box, {
+ label: 'StyledBoxShadowWrapper',
+ shouldForwardProp: omittedProps(['displayShadow']),
+})<{ displayShadow: boolean }>(({ displayShadow, theme }) => ({
+ '&:after': {
+ bottom: 0,
+ content: '""',
+ height: '15px',
+ position: 'absolute',
+ width: '100%',
+ ...(displayShadow && {
+ boxShadow: `${theme.color.boxShadow} 0px -15px 10px -10px inset`,
+ }),
+ },
+ position: 'relative',
+}));
+
+export const StyledHostNamesList = styled(List, {
+ label: 'RegionsList',
+})(({ theme }) => ({
+ background: theme.name === 'light' ? theme.bg.main : theme.bg.app,
+}));
diff --git a/packages/manager/src/features/Profile/SecretTokenDialog/SecretTokenDialog.tsx b/packages/manager/src/features/Profile/SecretTokenDialog/SecretTokenDialog.tsx
index f49b977a3a0..c4a747978e3 100644
--- a/packages/manager/src/features/Profile/SecretTokenDialog/SecretTokenDialog.tsx
+++ b/packages/manager/src/features/Profile/SecretTokenDialog/SecretTokenDialog.tsx
@@ -5,8 +5,8 @@ import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel';
import { Box } from 'src/components/Box';
import { ConfirmationDialog } from 'src/components/ConfirmationDialog/ConfirmationDialog';
import { CopyableAndDownloadableTextField } from 'src/components/CopyableAndDownloadableTextField';
-import { CopyableTextField } from 'src/components/CopyableTextField/CopyableTextField';
import { Notice } from 'src/components/Notice/Notice';
+import { HostNamesList } from 'src/features/ObjectStorage/AccessKeyLanding/HostNamesList';
import { CopyAllHostnames } from 'src/features/ObjectStorage/AccessKeyLanding/CopyAllHostnames';
import { useAccountManagement } from 'src/hooks/useAccountManagement';
import { useFlags } from 'src/hooks/useFlags';
@@ -15,6 +15,7 @@ import { isFeatureEnabled } from 'src/utilities/accountCapabilities';
import { getRegionsByRegionId } from 'src/utilities/regions';
import type { ObjectStorageKey } from '@linode/api-v4/lib/object-storage';
+
interface Props {
objectStorageKey?: ObjectStorageKey | null;
onClose: () => void;
@@ -59,6 +60,11 @@ export const SecretTokenDialog = (props: Props) => {
return (
({
+ '.MuiPaper-root': {
+ overflow: 'hidden',
+ },
+ })}
actions={actions}
disableEscapeKeyDown
fullWidth
@@ -106,31 +112,9 @@ export const SecretTokenDialog = (props: Props) => {
{isObjMultiClusterEnabled &&
objectStorageKey &&
objectStorageKey?.regions?.length > 0 && (
- ({
- '.copyIcon': {
- marginRight: 0,
- paddingRight: 0,
- },
- backgroundColor: theme.bg.main,
- border: `1px solid ${theme.color.grey3}`,
- borderColor: theme.name === 'light' ? '#ccc' : '#222',
- padding: theme.spacing(1),
- })}
- >
- {objectStorageKey?.regions.map((region, index) => (
-
- ))}
-
+
)}
+
{objectStorageKey ? (
<>