Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

change : [M3-6471] - Add resource links to NodeBalancers empty state landing page. #10345

Merged
merged 8 commits into from
Apr 8, 2024
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-10345-added-1712324244756.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Added
---

Resource links to NodeBalancers empty state landing page. ([#10345](https://github.com/linode/manager/pull/10345))
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { ui } from 'support/ui';
import { mockGetNodeBalancers } from 'support/intercepts/nodebalancers';

describe('NodeBalancers empty landing page', () => {
/**
* - Confirms NodeBalancers landing page empty state is shown when no NodeBalancers are present:
* - Confirms that "Getting Started Guides" and "Video Playlist" are listed on landing page.
* - Confirms that clicking "Create NodeBalancers" navigates user to NodeBalancer create page.
*/
it('shows the empty state when there are no nodebalancers', () => {
mockGetNodeBalancers([]).as('getNodebalancers');

cy.visitWithLogin('/nodebalancers');
cy.wait(['@getNodebalancers']);

// confirms helper text
cy.findByText('Cloud-based load balancing service').should('be.visible');
cy.findByText(
'Add high availability and horizontal scaling to web applications hosted on Linode Compute Instances.'
).should('be.visible');

// checks that guides are visible
cy.findByText('Getting Started Guides').should('be.visible');
cy.findByText('Getting Started with NodeBalancers').should('be.visible');
cy.findByText('Create a NodeBalancer').should('be.visible');
cy.findByText('Configuration Options for NodeBalancers').should(
'be.visible'
);
cy.findByText('View additional NodeBalancer documentation').should(
'be.visible'
);

// checks that videos are visible
cy.findByText('Video Playlist').should('be.visible');
cy.findByText(
'Getting Started With NodeBalancers | How To Prepare For High Server Traffic'
).should('be.visible');
cy.findByText(
'Linode NodeBalancers Explained | Manage Scale with Transparent Load Distribution'
).should('be.visible');
cy.findByText('Load Balancing on an LKE Kubernetes Cluster').should(
'be.visible'
);
cy.findByText('View our YouTube channel').should('be.visible');

// confirms clicking on 'Create NodeBalancer' button
ui.button
.findByTitle('Create NodeBalancer')
.should('be.visible')
.should('be.enabled')
.click();

cy.url().should('endWith', '/nodebalancers/create');
});
});
25 changes: 25 additions & 0 deletions packages/manager/cypress/support/intercepts/nodebalancers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* @file Cypress intercepts and mocks for NodeBalancer API requests.
*/

import { apiMatcher } from 'support/util/intercepts';
import { paginateResponse } from 'support/util/paginate';

import type { NodeBalancer } from '@linode/api-v4';

/**
* Intercepts GET request to mock nodeBalancer data.
*
* @param nodeBalancers - an array of mock nodeBalancer objects
*
* @returns Cypress chainable.
*/
export const mockGetNodeBalancers = (
nodeBalancers: NodeBalancer[]
): Cypress.Chainable<null> => {
return cy.intercept(
'GET',
apiMatcher('nodebalancers*'),
paginateResponse(nodeBalancers)
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface ResourcesMoreLinkProps extends LinkProps {

const StyledMoreLink = styled(Link)<ResourcesMoreLinkProps>(({ ...props }) => ({
alignItems: props.external ? 'baseline' : 'center',
textWrap: 'balance',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent here is good to get the caret not to wrap by itself, unfortunately textWrap: 'balance' isn't very well supported and will (for instance) simply not work on safari.

one way to address it is to the same thing that was done for external links icons (negative margin + transform)
https://github.com/linode/manager/blob/develop/packages/manager/src/components/Link.styles.ts#L23-L26

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @abailly-akamai addressed in 68b9883

}));

export const ResourcesMoreLink = (props: ResourcesMoreLinkProps) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import { styled } from '@mui/material/styles';
import * as React from 'react';
import { useHistory } from 'react-router-dom';

import NodeBalancer from 'src/assets/icons/entityIcons/nodebalancer.svg';
import { DocumentTitleSegment } from 'src/components/DocumentTitle';
import { Link } from 'src/components/Link';
import { Placeholder } from 'src/components/Placeholder/Placeholder';
import { Typography } from 'src/components/Typography';
import { ResourcesSection } from 'src/components/EmptyLandingPageResources/ResourcesSection';
import { getRestrictedResourceText } from 'src/features/Account/utils';
import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGrantCheck';
import { sendEvent } from 'src/utilities/analytics';

import {
gettingStartedGuides,
headers,
linkAnalyticsEvent,
youtubeLinkData,
} from './NodeBalancersLandingEmptyStateData';

export const NodeBalancerLandingEmptyState = () => {
const history = useHistory();
const { push } = useHistory();

const isRestricted = useRestrictedGlobalGrantCheck({
globalGrantType: 'add_nodebalancers',
Expand All @@ -20,42 +25,32 @@ export const NodeBalancerLandingEmptyState = () => {
return (
<React.Fragment>
<DocumentTitleSegment segment="NodeBalancers" />
<StyledPlaceholder
<ResourcesSection
buttonProps={[
{
children: 'Create NodeBalancer',
disabled: isRestricted,
onClick: () => history.push('/nodebalancers/create'),
onClick: () => {
sendEvent({
action: 'Click:button',
category: linkAnalyticsEvent.category,
label: 'Create NodeBalancer',
});
push('/nodebalancers/create');
},
tooltipText: getRestrictedResourceText({
action: 'create',
isSingular: false,
resourceType: 'NodeBalancers',
}),
},
]}
gettingStartedGuidesData={gettingStartedGuides}
headers={headers}
icon={NodeBalancer}
isEntity
showTransferDisplay
title="NodeBalancers"
>
<Typography variant="subtitle1">
<Link to="https://www.linode.com/docs/platform/nodebalancer/getting-started-with-nodebalancers/">
Learn how to use NodeBalancers with your Linode
</Link>
&nbsp;or&nbsp;
<Link to="https://www.linode.com/docs/">
visit our guides and tutorials.
</Link>
</Typography>
</StyledPlaceholder>
linkAnalyticsEvent={linkAnalyticsEvent}
youtubeLinkData={youtubeLinkData}
/>
</React.Fragment>
);
};

const StyledPlaceholder = styled(Placeholder, {
label: 'StyledPlaceholder',
})(({ theme }) => ({
// this important rules can be removed when Placeholder is refactored
// and we can just use sx={{ paddingBottom: 0 }} on placeholder
padding: `${theme.spacing(10)} 0 0 0 !important`,
}));
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import {
youtubeChannelLink,
youtubeMoreLinkText,
} from 'src/utilities/emptyStateLandingUtils';

import type {
ResourcesHeaders,
ResourcesLinkSection,
ResourcesLinks,
} from 'src/components/EmptyLandingPageResources/ResourcesLinksTypes';

export const headers: ResourcesHeaders = {
description:
'Add high availability and horizontal scaling to web applications hosted on Linode Compute Instances.',
subtitle: 'Cloud-based load balancing service',
title: 'NodeBalancers',
};

export const gettingStartedGuides: ResourcesLinkSection = {
links: [
{
text: 'Getting Started with NodeBalancers',
to:
'https://www.linode.com/docs/products/networking/nodebalancers/get-started/',
},
{
text: 'Create a NodeBalancer',
to:
'https://www.linode.com/docs/products/networking/nodebalancers/guides/create/',
},
{
text: 'Configuration Options for NodeBalancers',
to:
'https://www.linode.com/docs/products/networking/nodebalancers/guides/configure/',
},
],
moreInfo: {
text: 'View additional NodeBalancer documentation',
to: ' https://www.linode.com/docs/products/networking/nodebalancers/',
},
title: 'Getting Started Guides',
};

export const youtubeLinkData: ResourcesLinkSection = {
links: [
{
external: true,
text:
'Getting Started With NodeBalancers | How To Prepare For High Server Traffic',
to: 'https://www.youtube.com/watch?v=JlXgl_rtM_s',
},
{
external: true,
text:
'Linode NodeBalancers Explained | Manage Scale with Transparent Load Distribution',
to: 'https://www.youtube.com/watch?v=U6xxgydIG9w',
},
{
external: true,
text: 'Load Balancing on an LKE Kubernetes Cluster',
to: 'https://www.youtube.com/watch?v=odPmyT5DONg',
},
],
moreInfo: {
text: youtubeMoreLinkText,
to: youtubeChannelLink,
},
title: 'Video Playlist',
};

export const linkAnalyticsEvent: ResourcesLinks['linkAnalyticsEvent'] = {
action: 'Click:link',
category: 'NodeBalancers landing page empty',
};
Loading