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

feat: [M3-8831] - New GPUv2 egress transfer display #11209

Merged
merged 7 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-11209-added-1730788980490.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Added
---

New GPUv2 egress transfer helpers ([#11209](https://github.com/linode/manager/pull/11209))
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
mockGetRegionAvailability,
} from 'support/intercepts/regions';
import { mockGetLinodeTypes } from 'support/intercepts/linodes';
import { mockAppendFeatureFlags } from 'support/intercepts/feature-flags';

const mockRegions = [
regionFactory.build({
Expand Down Expand Up @@ -356,19 +357,26 @@ describe('displays specific linode plans for GPU', () => {
mockGetRegionAvailability(mockRegions[0].id, mockRegionAvailability).as(
'getRegionAvailability'
);
mockAppendFeatureFlags({
gpuv2: {
transferBanner: true,
planDivider: true,
egressBanner: true,
},
}).as('getFeatureFlags');
});

it('Should render divided tables when GPU divider enabled', () => {
cy.visitWithLogin('/linodes/create');

cy.wait(['@getRegions', '@getLinodeTypes', '@getFeatureFlags']);
ui.regionSelect.find().click();
ui.regionSelect.findItemByRegionLabel(mockRegions[0].label).click();

// GPU tab
// Should display two separate tables
cy.findByText('GPU').click();
cy.get(linodePlansPanel).within(() => {
cy.findAllByRole('alert').should('have.length', 2);
cy.findAllByRole('alert').should('have.length', 3);
cy.get(notices.unavailable).should('be.visible');

cy.findByRole('table', {
Expand Down
15 changes: 9 additions & 6 deletions packages/manager/src/components/TableCell/TableCell.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import {
default as _TableCell,
TableCellProps as _TableCellProps,
} from '@mui/material/TableCell';
import { Theme } from '@mui/material/styles';
import { default as _TableCell } from '@mui/material/TableCell';
import * as React from 'react';
import { makeStyles } from 'tss-react/mui';

import { TooltipIcon } from 'src/components/TooltipIcon';

import type { Theme } from '@mui/material/styles';
import type { TableCellProps as _TableCellProps } from '@mui/material/TableCell';

const useStyles = makeStyles()((theme: Theme) => ({
actionCell: {
textAlign: 'right',
Expand Down Expand Up @@ -106,7 +105,11 @@ export const TableCell = (props: TableCellProps) => {
[classes.root]: true,
[classes.sortable]: sortable,
// hide the cell at small breakpoints if it's empty with no parent column
emptyCell: !parentColumn && !props.children,
emptyCell:
(!parentColumn && !props.children) ||
(!parentColumn &&
Array.isArray(props.children) &&
!props.children[0]),
},
className
)}
Expand Down
1 change: 1 addition & 0 deletions packages/manager/src/featureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export interface CloudPulseResourceTypeMapFlag {
interface gpuV2 {
egressBanner: boolean;
planDivider: boolean;
transferBanner: boolean;
}

interface DesignUpdatesBannerFlag extends BaseFeatureFlag {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ interface StyledTableCellPropsProps extends TableCellProps {

export const StyledTable = styled(Table, {
label: 'StyledTable',
})(({ theme }) => ({
})({
overflowX: 'hidden',
}));
});

export const StyledTableCell = styled(TableCell, {
label: 'StyledTableCell',
shouldForwardProp: omittedProps(['isPlanCell']),
})<StyledTableCellPropsProps>(({ theme, ...props }) => ({
...(props.isPlanCell && { width: '30%' }),
'&.emptyCell': {
borderRight: 'none',
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { LinodeTypeClass } from '@linode/api-v4/lib/linodes';
import Grid from '@mui/material/Unstable_Grid2';
import * as React from 'react';
import { useLocation } from 'react-router-dom';
Expand All @@ -14,6 +13,7 @@ import { PlanSelectionTable } from './PlanSelectionTable';

import type { PlanWithAvailability } from './types';
import type { Region } from '@linode/api-v4';
import type { LinodeTypeClass } from '@linode/api-v4/lib/linodes';

export interface PlanContainerProps {
allDisabledPlans: PlanWithAvailability[];
Expand Down Expand Up @@ -50,7 +50,8 @@ export const PlanContainer = (props: PlanContainerProps) => {

// Show the Transfer column if, for any plan, the api returned data and we're not in the Database Create flow
const showTransfer =
showLimits && plans.some((plan: PlanWithAvailability) => plan.transfer);
showLimits &&
plans.some((plan: PlanWithAvailability) => plan.transfer !== undefined);

// Show the Network throughput column if, for any plan, the api returned data (currently Bare Metal does not)
const showNetwork =
Expand Down Expand Up @@ -198,6 +199,7 @@ export const PlanContainer = (props: PlanContainerProps) => {
}
key={`plan-filter-${idx}`}
planFilter={table.planFilter}
plans={plans}
showNetwork={showNetwork}
showTransfer={showTransfer}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const PlanInformation = (props: PlanInformationProps) => {
return Boolean(disabledClasses?.includes(thisClass));
};
const showGPUEgressBanner = Boolean(useFlags().gpuv2?.egressBanner);
const showTransferBanner = Boolean(useFlags().gpuv2?.transferBanner);

return (
<>
Expand All @@ -68,6 +69,23 @@ export const PlanInformation = (props: PlanInformationProps) => {
</Typography>
</Notice>
)}
{showTransferBanner && (
<Notice spacingBottom={8} variant="warning">
<Typography
fontFamily={(theme: Theme) => theme.font.bold}
fontSize="1rem"
>
Some plans do not include bundled network transfer. If the
transfer allotment is 0, all outbound network transfer is
subject to standard charges.
<br />
<Link to="https://techdocs.akamai.com/cloud-computing/docs/network-transfer-usage-and-costs">
Learn more about transfer costs
</Link>
.
</Typography>
</Notice>
)}
<PlansAvailabilityNotice
hasSelectedRegion={hasSelectedRegion}
isSelectedRegionEligibleForPlan={isSelectedRegionEligibleForPlan}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,11 @@ export const PlanSelection = (props: PlanSelectionProps) => {
</TableCell>
{showTransfer ? (
<TableCell center data-qa-transfer>
{plan.transfer ? <>{plan.transfer / 1000} TB</> : ''}
{plan.transfer !== undefined ? (
<>{plan.transfer / 1000} TB</>
) : (
''
)}
</TableCell>
) : null}
{showNetwork ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import { TableBody } from 'src/components/TableBody';
import { TableHead } from 'src/components/TableHead';
import { TableRow } from 'src/components/TableRow';
import { TableRowEmpty } from 'src/components/TableRowEmpty/TableRowEmpty';
import { TooltipIcon } from 'src/components/TooltipIcon';
import { useFlags } from 'src/hooks/useFlags';
import { PLAN_SELECTION_NO_REGION_SELECTED_MESSAGE } from 'src/utilities/pricing/constants';

import { StyledTable, StyledTableCell } from './PlanContainer.styles';
import { PlanWithAvailability } from './types';

import type { PlanWithAvailability } from './types';

interface PlanSelectionFilterOptionsTable {
header?: string;
Expand All @@ -17,6 +20,7 @@ interface PlanSelectionFilterOptionsTable {
interface PlanSelectionTableProps {
filterOptions?: PlanSelectionFilterOptionsTable;
planFilter?: (plan: PlanWithAvailability) => boolean;
plans?: PlanWithAvailability[];
renderPlanSelection: (
filterOptions?: PlanSelectionFilterOptionsTable | undefined
) => React.JSX.Element[];
Expand Down Expand Up @@ -45,11 +49,26 @@ const tableCells = [
export const PlanSelectionTable = (props: PlanSelectionTableProps) => {
const {
filterOptions,
plans,
renderPlanSelection,
shouldDisplayNoRegionSelectedMessage,
showNetwork: shouldShowNetwork,
showTransfer: shouldShowTransfer,
} = props;
const flags = useFlags();

const showTransferTooltip = React.useCallback(
(cellName: string) =>
plans?.some((plan) => {
return (
flags.gpuv2?.transferBanner &&
plan.class === 'gpu' &&
filterOptions?.header?.includes('Ada') &&
cellName === 'Transfer'
);
}),
[plans, filterOptions, flags.gpuv2]
);

return (
<StyledTable
Expand Down Expand Up @@ -78,6 +97,19 @@ export const PlanSelectionTable = (props: PlanSelectionTableProps) => {
{isPlanCell && filterOptions?.header
? filterOptions?.header
: cellName}
{showTransferTooltip(cellName) && (
<TooltipIcon
sxTooltipIcon={{
height: 12,
marginTop: '-2px',
ml: 0.5,
px: 0,
py: 0,
}}
status="help"
text="Some plans do not include bundled network transfer. If the transfer allotment is 0, all outbound network transfer is subject to standard charges."
/>
)}
</StyledTableCell>
);
})}
Expand Down
14 changes: 13 additions & 1 deletion packages/manager/src/mocks/serverHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,17 @@ const nanodeType = linodeTypeFactory.build({ id: 'g6-nanode-1' });
const standardTypes = linodeTypeFactory.buildList(7);
const dedicatedTypes = dedicatedTypeFactory.buildList(7);
const proDedicatedType = proDedicatedTypeFactory.build();

const gpuTypesAda = linodeTypeFactory.buildList(7, {
class: 'gpu',
gpus: 5,
label: 'Ada Lovelace',
transfer: 0,
});
const gpuTypesRX = linodeTypeFactory.buildList(7, {
class: 'gpu',
gpus: 1,
transfer: 5000,
});
const proxyAccountUser = accountUserFactory.build({
email: '[email protected]',
last_login: null,
Expand Down Expand Up @@ -578,6 +588,8 @@ export const handlers = [
nanodeType,
...standardTypes,
...dedicatedTypes,
...gpuTypesAda,
...gpuTypesRX,
proDedicatedType,
])
);
Expand Down