From 5ef397cc1163d540d500869dc2b3e738f91edae2 Mon Sep 17 00:00:00 2001 From: Alban Bailly Date: Mon, 8 May 2023 17:08:56 -0400 Subject: [PATCH 01/10] feat: [M3-6468] Add resource links to Object Storage empty state --- .../BucketLanding/BucketLanding.tsx | 80 +++++-------------- .../BucketLandingEmptyResourcesData.ts | 79 ++++++++++++++++++ .../BucketLanding/BucketLandingEmptyState.tsx | 40 ++++++++++ .../ObjectStorage/ObjectStorageLanding.tsx | 55 +++++++------ 4 files changed, 168 insertions(+), 86 deletions(-) create mode 100644 packages/manager/src/features/ObjectStorage/BucketLanding/BucketLandingEmptyResourcesData.ts create mode 100644 packages/manager/src/features/ObjectStorage/BucketLanding/BucketLandingEmptyState.tsx diff --git a/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLanding.tsx b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLanding.tsx index 4b5c86d6f72..7600a285e6e 100644 --- a/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLanding.tsx +++ b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLanding.tsx @@ -2,41 +2,38 @@ import { ObjectStorageBucket, ObjectStorageCluster, } from '@linode/api-v4/lib/object-storage'; -import { APIError } from '@linode/api-v4/lib/types'; -import classNames from 'classnames'; import * as React from 'react'; -import BucketIcon from 'src/assets/icons/entityIcons/bucket.svg'; -import { CircleProgress } from 'src/components/CircleProgress'; -import { makeStyles } from '@mui/styles'; -import { Theme } from '@mui/material/styles'; -import Typography from 'src/components/core/Typography'; -import { DocumentTitleSegment } from 'src/components/DocumentTitle'; +import BucketDetailsDrawer from './BucketDetailsDrawer'; +import BucketTable from './BucketTable'; +import CancelNotice from '../CancelNotice'; import ErrorState from 'src/components/ErrorState'; import Grid from '@mui/material/Unstable_Grid2'; import { Notice } from 'src/components/Notice/Notice'; import OrderBy from 'src/components/OrderBy'; -import Placeholder from 'src/components/Placeholder'; import TransferDisplay from 'src/components/TransferDisplay'; import TypeToConfirmDialog from 'src/components/TypeToConfirmDialog'; +import Typography from 'src/components/core/Typography'; import useOpenClose from 'src/hooks/useOpenClose'; +import { APIError } from '@linode/api-v4/lib/types'; +import { BucketLandingEmptyState } from './BucketLandingEmptyState'; +import { CircleProgress } from 'src/components/CircleProgress'; +import { DocumentTitleSegment } from 'src/components/DocumentTitle'; +import { makeStyles } from '@mui/styles'; +import { readableBytes } from 'src/utilities/unitConversions'; +import { Theme } from '@mui/material/styles'; +import { useProfile } from 'src/queries/profile'; +import { useRegionsQuery } from 'src/queries/regions'; + +import { + sendDeleteBucketEvent, + sendDeleteBucketFailedEvent, +} from 'src/utilities/ga'; import { BucketError, useDeleteBucketMutation, useObjectStorageBuckets, useObjectStorageClusters, } from 'src/queries/objectStorage'; -import { useRegionsQuery } from 'src/queries/regions'; -import { - sendDeleteBucketEvent, - sendDeleteBucketFailedEvent, - sendObjectStorageDocsEvent, -} from 'src/utilities/ga'; -import { readableBytes } from 'src/utilities/unitConversions'; -import CancelNotice from '../CancelNotice'; -import BucketDetailsDrawer from './BucketDetailsDrawer'; -import BucketTable from './BucketTable'; -import { useProfile } from 'src/queries/profile'; -import { useHistory } from 'react-router-dom'; const useStyles = makeStyles((theme: Theme) => ({ copy: { @@ -279,46 +276,7 @@ export const BucketLanding = () => { }; const RenderEmpty = () => { - const classes = useStyles(); - const history = useHistory(); - - return ( - - - history.replace('/object-storage/buckets/create'), - children: 'Create Bucket', - }, - ]} - showTransferDisplay - > - Need help getting started? - - sendObjectStorageDocsEvent('Empty state')} - href="https://linode.com/docs/platform/object-storage" - target="_blank" - aria-describedby="external-site" - rel="noopener noreferrer" - className="h-u" - > - Learn more about storage options for your multimedia, archives, and - data backups here. - - - - - ); + return ; }; export default BucketLanding; diff --git a/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLandingEmptyResourcesData.ts b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLandingEmptyResourcesData.ts new file mode 100644 index 00000000000..cce0387df36 --- /dev/null +++ b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLandingEmptyResourcesData.ts @@ -0,0 +1,79 @@ +import { + youtubeChannelLink, + youtubeMoreLinkText, +} from 'src/utilities/emptyStateLandingUtils'; +import type { + ResourcesHeaders, + ResourcesLinkSection, + ResourcesLinks, +} from 'src/components/EmptyLandingPageResources/ResourcesLinksTypes'; + +export const headers: ResourcesHeaders = { + description: '', + subtitle: 'S3-compatible storage solution', + title: 'Object Storage', +}; + +export const gettingStartedGuides: ResourcesLinkSection = { + links: [ + { + to: 'https://www.linode.com/docs/products/storage/object-storage/', + text: 'Overview of Object Storage', + }, + { + to: + 'https://www.linode.com/docs/products/storage/object-storage/guides/linode-cli', + text: 'Using the Linode CLI with Object Storage', + }, + { + to: + 'https://www.linode.com/docs/products/storage/object-storage/guides/s3cmd', + text: 'Use Object Storage with s3cmd', + }, + { + to: + 'https://www.linode.com/docs/products/storage/object-storage/guides/s4cmd', + text: 'Use Object Storage with s4cmd', + }, + { + to: + 'https://www.linode.com/docs/products/storage/object-storage/guides/cyberduck', + text: 'Use Object Storage with Cyberduck', + }, + ], + moreInfo: { + to: 'https://www.linode.com/docs/products/storage/object-storage/', + text: 'View additional Object Storage documentation', + }, + title: 'Getting Started Guides', +}; + +export const youtubeLinkData: ResourcesLinkSection = { + links: [ + { + to: 'https://www.youtube.com/watch?v=q88OKsr5l6c', + text: 'Getting Started with S3 Object Storage on Linode', + external: true, + }, + { + to: 'https://www.youtube.com/watch?v=7J3_NAq7fz0', + text: 'S3 Object Storage Simply Explained', + external: true, + }, + { + to: 'https://www.youtube.com/watch?v=ZfGyeJ8jYxI', + text: 'Deploy a Static Website Using the Linode CLI and Object Storage', + external: true, + }, + ], + moreInfo: { + to: youtubeChannelLink, + text: youtubeMoreLinkText, + }, + title: 'Video Playlist', +}; + +export const linkGAEvent: ResourcesLinks['linkGAEvent'] = { + action: 'Click:link', + category: 'Object Storage landing page empty', +}; diff --git a/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLandingEmptyState.tsx b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLandingEmptyState.tsx new file mode 100644 index 00000000000..3bf413cd5ae --- /dev/null +++ b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLandingEmptyState.tsx @@ -0,0 +1,40 @@ +import * as React from 'react'; +import FirewallIcon from 'src/assets/icons/entityIcons/firewall.svg'; +import { ResourcesSection } from 'src/components/EmptyLandingPageResources/ResourcesSection'; +import { useHistory } from 'react-router-dom'; +import { sendEvent } from 'src/utilities/ga'; +import { + gettingStartedGuides, + headers, + linkGAEvent, + youtubeLinkData, +} from './BucketLandingEmptyResourcesData'; + +export const BucketLandingEmptyState = () => { + const history = useHistory(); + + return ( + { + sendEvent({ + category: linkGAEvent.category, + action: 'Click:button', + label: 'Create Bucket', + }); + history.replace('/object-storage/buckets/create'); + }, + + children: 'Create Bucket', + }, + ]} + gettingStartedGuidesData={gettingStartedGuides} + headers={headers} + icon={FirewallIcon} + linkGAEvent={linkGAEvent} + youtubeLinkData={youtubeLinkData} + showTransferDisplay + /> + ); +}; diff --git a/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx b/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx index 29fa8017bd9..7ae02fa7708 100644 --- a/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx +++ b/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx @@ -1,25 +1,25 @@ import * as React from 'react'; -import { DateTime } from 'luxon'; import { useHistory, useParams } from 'react-router-dom'; -import TabPanels from 'src/components/core/ReachTabPanels'; -import Tabs from 'src/components/core/ReachTabs'; -import { makeStyles } from '@mui/styles'; -import { Theme } from '@mui/material/styles'; -import Typography from 'src/components/core/Typography'; import DismissibleBanner from 'src/components/DismissibleBanner'; -import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import LandingHeader from 'src/components/LandingHeader'; -import { Link } from 'src/components/Link'; import ProductInformationBanner from 'src/components/ProductInformationBanner'; import PromotionalOfferCard from 'src/components/PromotionalOfferCard/PromotionalOfferCard'; import SafeTabPanel from 'src/components/SafeTabPanel'; import SuspenseLoader from 'src/components/SuspenseLoader'; import TabLinkList from 'src/components/TabLinkList'; +import TabPanels from 'src/components/core/ReachTabPanels'; +import Tabs from 'src/components/core/ReachTabs'; +import Typography from 'src/components/core/Typography'; import useAccountManagement from 'src/hooks/useAccountManagement'; import useFlags from 'src/hooks/useFlags'; import useOpenClose from 'src/hooks/useOpenClose'; -import { MODE } from './AccessKeyLanding/types'; import { CreateBucketDrawer } from './BucketLanding/CreateBucketDrawer'; +import { DateTime } from 'luxon'; +import { DocumentTitleSegment } from 'src/components/DocumentTitle'; +import { Link } from 'src/components/Link'; +import { makeStyles } from '@mui/styles'; +import { MODE } from './AccessKeyLanding/types'; +import { Theme } from '@mui/material/styles'; import { useObjectStorageBuckets, useObjectStorageClusters, @@ -44,19 +44,16 @@ export const ObjectStorageLanding = () => { action?: 'create'; tab?: 'buckets' | 'access-keys'; }>(); - const isCreateBucketOpen = tab === 'buckets' && action === 'create'; - const { _isRestrictedUser, accountSettings } = useAccountManagement(); - const { data: objectStorageClusters } = useObjectStorageClusters(); - const { data: objectStorageBucketsResponse, isLoading: areBucketsLoading, error: bucketsErrors, } = useObjectStorageBuckets(objectStorageClusters); - + const userHasNoBucketCreated = + objectStorageBucketsResponse?.buckets.length === 0; const createOrEditDrawer = useOpenClose(); const tabs = [ @@ -94,9 +91,15 @@ export const ObjectStorageLanding = () => { const shouldDisplayBillingNotice = !areBucketsLoading && !bucketsErrors && - objectStorageBucketsResponse?.buckets.length === 0 && + userHasNoBucketCreated && accountSettings?.object_storage === 'active'; + // No need to display header since the it is redundant with the docs and CTA of the empty state + const shouldDisplayHeader = + !userHasNoBucketCreated && + !areBucketsLoading && + objectStorageClusters !== undefined; + const createButtonText = tab === 'access-keys' ? 'Create Access Key' : 'Create Bucket'; @@ -113,15 +116,17 @@ export const ObjectStorageLanding = () => { - + {shouldDisplayHeader && ( + + )} t === tab) !== -1 @@ -130,7 +135,7 @@ export const ObjectStorageLanding = () => { } onChange={navToURL} > - + {shouldDisplayHeader && } {objPromotionalOffers.map((promotionalOffer) => ( Date: Mon, 8 May 2023 17:46:05 -0400 Subject: [PATCH 02/10] feat: [M3-6468] Add changelog entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa426670ad9..cfddf3b0ec9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ## [Unreleased] ### Tech Stories: + - React Query - Linodes - Networking #9046 - React Query - Linodes - Details Header #9099 @@ -19,6 +20,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p - Resource links to empty state StackScripts landing page #9091 - Resource links to empty state Domains landing page #9092 - Resource links to empty state Images landing page #9095 +- Resource links to empty state Object Storage landing page #9092 - Ability download DNS zone file #9075 ### Changed: From 5eeec2b64e87f2a7e75e3d27d69a6d96d9193b6d Mon Sep 17 00:00:00 2001 From: Alban Bailly Date: Tue, 9 May 2023 09:46:49 -0400 Subject: [PATCH 03/10] feat: [M3-6468] Fix smoke test --- .../object-storage.smoke.spec.ts | 6 +++-- packages/manager/cypress/support/ui/index.ts | 2 ++ .../ui/landing-page-empty-state-resources.ts | 15 ++++++++++++ .../ResourcesSection.tsx | 1 + .../components/Placeholder/Placeholder.tsx | 23 +++++++++++-------- 5 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 packages/manager/cypress/support/ui/landing-page-empty-state-resources.ts diff --git a/packages/manager/cypress/e2e/objectStorage/object-storage.smoke.spec.ts b/packages/manager/cypress/e2e/objectStorage/object-storage.smoke.spec.ts index a3bfbf291c8..9015774588c 100644 --- a/packages/manager/cypress/e2e/objectStorage/object-storage.smoke.spec.ts +++ b/packages/manager/cypress/e2e/objectStorage/object-storage.smoke.spec.ts @@ -35,7 +35,9 @@ describe('object storage smoke tests', () => { cy.visitWithLogin('/object-storage'); cy.wait('@getBuckets'); - ui.entityHeader.find().within(() => { + ui.landingPageEmptyStateResources.find().within(() => { + cy.findByText('Getting Started Guides').should('be.visible'); + cy.findByText('Video Playlist').should('be.visible'); cy.findByText('Create Bucket').should('be.visible').click(); }); @@ -183,6 +185,6 @@ describe('object storage smoke tests', () => { }); cy.wait('@deleteBucket'); - cy.findByText('Need help getting started?').should('be.visible'); + cy.findByText('S3-compatible storage solution').should('be.visible'); }); }); diff --git a/packages/manager/cypress/support/ui/index.ts b/packages/manager/cypress/support/ui/index.ts index f91127d7eb0..85b2c13e44f 100644 --- a/packages/manager/cypress/support/ui/index.ts +++ b/packages/manager/cypress/support/ui/index.ts @@ -6,6 +6,7 @@ import * as dialog from './dialog'; import * as drawer from './drawer'; import * as entityHeader from './entity-header'; import * as heading from './heading'; +import * as landingPageEmptyStateResources from './landing-page-empty-state-resources'; import * as nav from './nav'; import * as select from './select'; import * as tabList from './tab-list'; @@ -21,6 +22,7 @@ export const ui = { ...drawer, ...entityHeader, ...heading, + ...landingPageEmptyStateResources, ...nav, ...select, ...toast, diff --git a/packages/manager/cypress/support/ui/landing-page-empty-state-resources.ts b/packages/manager/cypress/support/ui/landing-page-empty-state-resources.ts new file mode 100644 index 00000000000..cbd8d33491f --- /dev/null +++ b/packages/manager/cypress/support/ui/landing-page-empty-state-resources.ts @@ -0,0 +1,15 @@ +/** + * Landing page empty state resources UI element. + * + * Useful for checking the content of an empty state landing page. (e.g. /domains with no domains) + */ +export const landingPageEmptyStateResources = { + /** + * Finds the entity header and returns the Cypress chainable. + * + * @returns Cypress chainable. + */ + find: (): Cypress.Chainable => { + return cy.get('[data-qa-placeholder-container="resources-section"]'); + }, +}; diff --git a/packages/manager/src/components/EmptyLandingPageResources/ResourcesSection.tsx b/packages/manager/src/components/EmptyLandingPageResources/ResourcesSection.tsx index 0eed6702328..776d9129ddb 100644 --- a/packages/manager/src/components/EmptyLandingPageResources/ResourcesSection.tsx +++ b/packages/manager/src/components/EmptyLandingPageResources/ResourcesSection.tsx @@ -101,6 +101,7 @@ export const ResourcesSection = (props: ResourcesSectionProps) => { return ( ; - children?: string | React.ReactNode; - title: string; buttonProps?: ExtendedButtonProps[]; + children?: string | React.ReactNode; className?: string; + dataQAPlaceholder?: string | boolean; descriptionMaxWidth?: number; + icon?: React.ComponentType; isEntity?: boolean; - renderAsSecondary?: boolean; - subtitle?: string; linksSection?: JSX.Element; + renderAsSecondary?: boolean; showTransferDisplay?: boolean; + subtitle?: string; + title: string; } const Placeholder: React.FC = (props) => { const { - isEntity, - title, - icon: Icon, buttonProps, + dataQAPlaceholder, descriptionMaxWidth, - renderAsSecondary, - subtitle, + icon: Icon, + isEntity, linksSection, + renderAsSecondary, showTransferDisplay, + subtitle, + title, } = props; const classes = useStyles(); @@ -196,6 +198,7 @@ const Placeholder: React.FC = (props) => { showTransferDisplay && linksSection === undefined, [classes.rootWithShowTransferDisplay]: showTransferDisplay, })} + data-qa-placeholder-container={dataQAPlaceholder || true} >
Date: Tue, 9 May 2023 09:50:54 -0400 Subject: [PATCH 04/10] feat: [M3-6468] Fix e2e suite --- .../cypress/e2e/objectStorage/object-storage.e2e.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/manager/cypress/e2e/objectStorage/object-storage.e2e.spec.ts b/packages/manager/cypress/e2e/objectStorage/object-storage.e2e.spec.ts index 48316ea0d31..71603399ae4 100644 --- a/packages/manager/cypress/e2e/objectStorage/object-storage.e2e.spec.ts +++ b/packages/manager/cypress/e2e/objectStorage/object-storage.e2e.spec.ts @@ -115,7 +115,7 @@ describe('object storage end-to-end tests', () => { cy.wait('@getBuckets'); // Click "Create Bucket", fill out bucket creation form in drawer. - ui.entityHeader.find().within(() => { + ui.landingPageEmptyStateResources.find().within(() => { cy.findByText('Create Bucket').should('be.visible').click(); }); From 85e24c0d34c0a34b18d2b78bad4e94b82563ce89 Mon Sep 17 00:00:00 2001 From: Alban Bailly Date: Tue, 9 May 2023 11:29:30 -0400 Subject: [PATCH 05/10] feat: [M3-6468] Fix access keys header display and associated tests --- .../LandingHeader/LandingHeader.tsx | 62 ++++++++++--------- .../ObjectStorage/ObjectStorageLanding.tsx | 30 +++++---- 2 files changed, 47 insertions(+), 45 deletions(-) diff --git a/packages/manager/src/components/LandingHeader/LandingHeader.tsx b/packages/manager/src/components/LandingHeader/LandingHeader.tsx index 24e68d867a0..a21eb725767 100644 --- a/packages/manager/src/components/LandingHeader/LandingHeader.tsx +++ b/packages/manager/src/components/LandingHeader/LandingHeader.tsx @@ -24,6 +24,7 @@ export interface Props { onButtonKeyPress?: (e: React.KeyboardEvent) => void; onDocsClick?: () => void; removeCrumbX?: number; + shouldHideDocsAndCreateButtons?: boolean; title?: string | JSX.Element; } @@ -47,6 +48,7 @@ export const LandingHeader = ({ onButtonKeyPress, onDocsClick, removeCrumbX, + shouldHideDocsAndCreateButtons, title, }: Props) => { const theme = useTheme(); @@ -79,36 +81,38 @@ export const LandingHeader = ({ {...breadcrumbProps} /> - - - {docsLink ? ( - - ) : null} - {renderActions && ( - - {extraActions} - {onButtonClick ? ( - - ) : null} - - )} + {!shouldHideDocsAndCreateButtons && ( + + + {docsLink ? ( + + ) : null} + {renderActions && ( + + {extraActions} + {onButtonClick ? ( + + ) : null} + + )} + - + )} ); }; diff --git a/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx b/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx index 7ae02fa7708..1544a178a8f 100644 --- a/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx +++ b/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx @@ -95,10 +95,9 @@ export const ObjectStorageLanding = () => { accountSettings?.object_storage === 'active'; // No need to display header since the it is redundant with the docs and CTA of the empty state - const shouldDisplayHeader = - !userHasNoBucketCreated && - !areBucketsLoading && - objectStorageClusters !== undefined; + // Meanwhile it will still display the header for the access keys tab at all times + const shouldHideDocsAndCreateButtons = + !areBucketsLoading && tab === 'buckets' && userHasNoBucketCreated; const createButtonText = tab === 'access-keys' ? 'Create Access Key' : 'Create Bucket'; @@ -116,17 +115,16 @@ export const ObjectStorageLanding = () => { - {shouldDisplayHeader && ( - - )} + t === tab) !== -1 @@ -135,7 +133,7 @@ export const ObjectStorageLanding = () => { } onChange={navToURL} > - {shouldDisplayHeader && } + {objPromotionalOffers.map((promotionalOffer) => ( Date: Tue, 9 May 2023 11:53:52 -0400 Subject: [PATCH 06/10] feat: [M3-6468] Icon and cleanup --- .../BucketLanding/BucketLandingEmptyState.tsx | 8 ++++---- .../src/features/ObjectStorage/ObjectStorageLanding.tsx | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLandingEmptyState.tsx b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLandingEmptyState.tsx index 3bf413cd5ae..fba8a8e6469 100644 --- a/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLandingEmptyState.tsx +++ b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLandingEmptyState.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; -import FirewallIcon from 'src/assets/icons/entityIcons/firewall.svg'; import { ResourcesSection } from 'src/components/EmptyLandingPageResources/ResourcesSection'; -import { useHistory } from 'react-router-dom'; import { sendEvent } from 'src/utilities/ga'; +import { StyledBucketIcon } from './StylesBucketIcon'; +import { useHistory } from 'react-router-dom'; import { gettingStartedGuides, headers, @@ -31,10 +31,10 @@ export const BucketLandingEmptyState = () => { ]} gettingStartedGuidesData={gettingStartedGuides} headers={headers} - icon={FirewallIcon} + icon={StyledBucketIcon} linkGAEvent={linkGAEvent} - youtubeLinkData={youtubeLinkData} showTransferDisplay + youtubeLinkData={youtubeLinkData} /> ); }; diff --git a/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx b/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx index 1544a178a8f..892a19dbf5b 100644 --- a/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx +++ b/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx @@ -193,7 +193,7 @@ export const BillingNotice = React.memo(() => { expiry: DateTime.utc().plus({ days: 30 }).toISO(), }} > - + You are being billed for Object Storage but do not have any Buckets. You can cancel Object Storage in your{' '} Account Settings, or{' '} From ba24f66455dff056a8a3554286bf3996dd061760 Mon Sep 17 00:00:00 2001 From: Alban Bailly Date: Tue, 9 May 2023 12:08:22 -0400 Subject: [PATCH 07/10] feat: [M3-6468] missing icon file --- .../ObjectStorage/BucketLanding/StylesBucketIcon.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 packages/manager/src/features/ObjectStorage/BucketLanding/StylesBucketIcon.ts diff --git a/packages/manager/src/features/ObjectStorage/BucketLanding/StylesBucketIcon.ts b/packages/manager/src/features/ObjectStorage/BucketLanding/StylesBucketIcon.ts new file mode 100644 index 00000000000..01f1638b117 --- /dev/null +++ b/packages/manager/src/features/ObjectStorage/BucketLanding/StylesBucketIcon.ts @@ -0,0 +1,8 @@ +import BucketIcon from 'src/assets/icons/entityIcons/bucket.svg'; +import { styled } from '@mui/material/styles'; + +const StyledBucketIcon = styled(BucketIcon)(() => ({ + transform: 'scale(0.80)', +})); + +export { StyledBucketIcon }; From a7e43f76b9b278ead8a3bc8f5626ad88a5f562ea Mon Sep 17 00:00:00 2001 From: Alban Bailly Date: Tue, 9 May 2023 12:28:32 -0400 Subject: [PATCH 08/10] feat: [M3-6468] generate mocked keys from utility --- .../e2e/objectStorage/access-keys.smoke.spec.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/manager/cypress/e2e/objectStorage/access-keys.smoke.spec.ts b/packages/manager/cypress/e2e/objectStorage/access-keys.smoke.spec.ts index 2b92837955f..eb14432ee28 100644 --- a/packages/manager/cypress/e2e/objectStorage/access-keys.smoke.spec.ts +++ b/packages/manager/cypress/e2e/objectStorage/access-keys.smoke.spec.ts @@ -9,7 +9,7 @@ import { mockGetAccessKeys, } from 'support/intercepts/object-storage'; import { paginateResponse } from 'support/util/paginate'; -import { randomLabel, randomNumber } from 'support/util/random'; +import { randomLabel, randomNumber, randomString } from 'support/util/random'; import { ui } from 'support/ui'; describe('object storage access keys smoke tests', () => { @@ -21,8 +21,9 @@ describe('object storage access keys smoke tests', () => { */ it('can create access key - smoke', () => { const keyLabel = randomLabel(); - const accessKey = '1Yx6kbVF35t15k2CmNQJ'; - const secretKey = 'bN12cDCBbb90meUgwvb0Tu9KWmNyFqMl2MGK1Ol'; + // Mocked key values + const accessKey = randomString(20); + const secretKey = randomString(39); mockGetAccessKeys(paginateResponse([])).as('getKeys'); @@ -100,8 +101,9 @@ describe('object storage access keys smoke tests', () => { it('can revoke access key - smoke', () => { const keyId = randomNumber(1, 99999); const keyLabel = randomLabel(); - const accessKey = '1Yx6kbVF35t15k2CmNQJ'; - const secretKey = 'bN12cDCBbb90meUgwvb0Tu9KWmNyFqMl2MGK1Ol'; + // Mocked key values + const accessKey = randomString(20); + const secretKey = randomString(39); // Mock initial GET request to include an access key. mockGetAccessKeys( From 5ebc844d7a139b89ba7acd07dbb0a252734f9327 Mon Sep 17 00:00:00 2001 From: Alban Bailly Date: Wed, 10 May 2023 11:37:38 -0400 Subject: [PATCH 09/10] feat: [M3-6468] address feedback --- .../cypress/e2e/objectStorage/object-storage.e2e.spec.ts | 5 +---- .../src/features/ObjectStorage/ObjectStorageLanding.tsx | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/manager/cypress/e2e/objectStorage/object-storage.e2e.spec.ts b/packages/manager/cypress/e2e/objectStorage/object-storage.e2e.spec.ts index 71603399ae4..ee8a61d3be7 100644 --- a/packages/manager/cypress/e2e/objectStorage/object-storage.e2e.spec.ts +++ b/packages/manager/cypress/e2e/objectStorage/object-storage.e2e.spec.ts @@ -114,10 +114,7 @@ describe('object storage end-to-end tests', () => { cy.visitWithLogin('/object-storage'); cy.wait('@getBuckets'); - // Click "Create Bucket", fill out bucket creation form in drawer. - ui.landingPageEmptyStateResources.find().within(() => { - cy.findByText('Create Bucket').should('be.visible').click(); - }); + ui.button.findByTitle('Create Bucket').should('be.visible').click(); ui.drawer .findByTitle('Create Bucket') diff --git a/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx b/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx index 892a19dbf5b..d11513a05fb 100644 --- a/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx +++ b/packages/manager/src/features/ObjectStorage/ObjectStorageLanding.tsx @@ -193,7 +193,7 @@ export const BillingNotice = React.memo(() => { expiry: DateTime.utc().plus({ days: 30 }).toISO(), }} > - + You are being billed for Object Storage but do not have any Buckets. You can cancel Object Storage in your{' '} Account Settings, or{' '} From ca0edcadf4d9467b78c3230c5891ceb417782357 Mon Sep 17 00:00:00 2001 From: Alban Bailly Date: Wed, 10 May 2023 11:45:18 -0400 Subject: [PATCH 10/10] feat: [M3-6468] address feedback: migrate LandingHeader from legacy grid --- .../src/components/LandingHeader/LandingHeader.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/manager/src/components/LandingHeader/LandingHeader.tsx b/packages/manager/src/components/LandingHeader/LandingHeader.tsx index a21eb725767..17071bc6a61 100644 --- a/packages/manager/src/components/LandingHeader/LandingHeader.tsx +++ b/packages/manager/src/components/LandingHeader/LandingHeader.tsx @@ -5,7 +5,7 @@ import { BreadcrumbProps, } from 'src/components/Breadcrumb/Breadcrumb'; import DocsLink from '../DocsLink'; -import Grid from '@mui/material/Grid'; +import Grid from '@mui/material/Unstable_Grid2'; import { useTheme, styled } from '@mui/material/styles'; export interface Props { @@ -70,7 +70,7 @@ export const LandingHeader = ({ justifyContent="space-between" alignItems="center" > - + {!shouldHideDocsAndCreateButtons && ( - - + + {docsLink ? (