Skip to content

Commit

Permalink
upcoming: [M3-7580] - Add list view for Linode Clone/Backup (#10182)
Browse files Browse the repository at this point in the history
* Add Clone Linode power-off notice

* Add new feature flag for Linode Clone UI updates

* Added changeset: Clone Linode power-off notice

* Reduce spacing between notices

* Refactored DebouncedSearchTextField to enable external state management

* Add search to SelectLinodePanel and autopopulate when cloning

* Fix bug causing filter value to update every time a Linode is selected

* Make clear icon blue on hover

* Gate changes behind feature flag

* Added changeset: Search filter in Clone Linode and Create Linode from Backup flows

* Added -> upcoming

* Add explanatory comment

* Change InputAdornment to IconButton

* Add missing useEffect dependencies

* Fix unused customValue prop

* Simplify export

* Add table view to Clone and Backups flow

* Add unit test for SelectLinodeRow

* Switch to useOrder hook

* Gate table view behind feature flag

* Added changeset: List view for Linode Clone and Create from Backup

* No clear icon when search field empty

* Extract render functions to components

* linodeID -> linodeId

* Feedback

* Remove unnecessary ExtendedLinodes interface and utility

* Rename feature flag to camelcase

* Add unit tests for SelectLinodePanel

* Only show power off action for selected linodes

* Feedback @bnussman-akamai

* Bulleted notices

* Don't show power actions for from backup

* UX feedback: remove "Notice:" heading

* Fix table column rendering inconsistencies
  • Loading branch information
hkhalil-akamai authored Feb 26, 2024
1 parent 1ceb91c commit d9b5b92
Show file tree
Hide file tree
Showing 20 changed files with 1,021 additions and 401 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

List view for Linode Clone and Create from Backup ([#10182](https://github.com/linode/manager/pull/10182))
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ export const DebouncedSearchTextField = React.memo(
<CircleProgress mini={true} />
</InputAdornment>
) : (
clearable && (
clearable &&
textFieldValue && (
<IconButton
aria-label="Clear"
onClick={() => setTextFieldValue('')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { StyledActionButton } from 'src/components/Button/StyledActionButton';
interface InlineMenuActionProps {
/** Required action text */
actionText: string;
/** Optional height when displayed as a button */
buttonHeight?: number;
/** Optional class names */
className?: string;
/** Optional disabled */
Expand All @@ -27,6 +29,7 @@ interface InlineMenuActionProps {
export const InlineMenuAction = (props: InlineMenuActionProps) => {
const {
actionText,
buttonHeight,
className,
disabled,
href,
Expand All @@ -53,6 +56,7 @@ export const InlineMenuAction = (props: InlineMenuActionProps) => {
disabled={disabled}
loading={loading}
onClick={onClick}
sx={buttonHeight !== undefined ? { height: buttonHeight } : {}}
tooltipAnalyticsEvent={tooltipAnalyticsEvent}
tooltipText={tooltip}
{...rest}
Expand Down
2 changes: 1 addition & 1 deletion packages/manager/src/dev-tools/FeatureFlagTool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const MOCK_FEATURE_FLAGS_STORAGE_KEY = 'devTools/mock-feature-flags';
const options: { flag: keyof Flags; label: string }[] = [
{ flag: 'aclb', label: 'ACLB' },
{ flag: 'aclbFullCreateFlow', label: 'ACLB Full Create Flow' },
{ flag: 'linodeCloneUIChanges', label: 'Linode Clone UI Changes' },
{ flag: 'linodeCloneUiChanges', label: 'Linode Clone UI Changes' },
{ flag: 'gecko', label: 'Gecko' },
{ flag: 'parentChildAccountAccess', label: 'Parent/Child Account' },
{ flag: 'selfServeBetas', label: 'Self Serve Betas' },
Expand Down
2 changes: 1 addition & 1 deletion packages/manager/src/featureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export interface Flags {
firewallNodebalancer: boolean;
gecko: boolean;
ipv6Sharing: boolean;
linodeCloneUIChanges: boolean;
linodeCloneUiChanges: boolean;
linodeCreateWithFirewall: boolean;
mainContentBanner: MainContentBanner;
metadata: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ import { validatePassword } from 'src/utilities/validatePassword';
import LinodeCreate from './LinodeCreate';
import { deriveDefaultLabel } from './deriveDefaultLabel';
import { HandleSubmit, Info, LinodeCreateValidation, TypeInfo } from './types';
import { getRegionIDFromLinodeID } from './utilities';

import type {
CreateLinodeRequest,
Expand Down Expand Up @@ -588,19 +587,13 @@ class LinodeCreateContainer extends React.PureComponent<CombinedProps, State> {
* since the API does not infer this automatically.
*/

/**
* safe to ignore possibility of "undefined"
* null checking happens in CALinodeCreate
*/
const selectedRegionID = getRegionIDFromLinodeID(
this.props.linodesData!,
id
);
this.setState({
selectedBackupID: undefined,
selectedDiskSize: diskSize,
selectedLinodeID: id,
selectedRegionID,
selectedRegionID: this.props.linodesData?.find(
(linode) => linode.id == id
)?.region,
selectedTypeID: undefined,
});
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Linode } from '@linode/api-v4';
import React from 'react';

import { SelectionCard } from 'src/components/SelectionCard/SelectionCard';
import { useImageQuery } from 'src/queries/images';
import { useRegionsQuery } from 'src/queries/regions';
import { useTypeQuery } from 'src/queries/types';
import { formatStorageUnits } from 'src/utilities/formatStorageUnits';
import { isNotNullOrUndefined } from 'src/utilities/nullOrUndefined';

interface Props {
disabled?: boolean;
handleSelection: () => void;
linode: Linode;
selected?: boolean;
}

export const SelectLinodeCard = ({
disabled,
handleSelection,
linode,
selected,
}: Props) => {
const { data: regions } = useRegionsQuery();

const { data: linodeType } = useTypeQuery(
linode?.type ?? '',
Boolean(linode?.type)
);

const { data: linodeImage } = useImageQuery(
linode?.image ?? '',
Boolean(linode?.image)
);

const type = linodeType ? formatStorageUnits(linodeType?.label) : linode.type;
const image = linodeImage?.label ?? linode.image;
const region =
regions?.find((region) => region.id == linode.region)?.label ??
linode.region;

return (
<SelectionCard
subheadings={[
[type, image, region].filter(isNotNullOrUndefined).join(', '),
]}
checked={selected}
disabled={disabled}
heading={linode.label}
key={`selection-card-${linode.id}`}
onClick={handleSelection}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Grid from '@mui/material/Unstable_Grid2';
import React from 'react';

import { SelectLinodeCard } from './SelectLinodeCard';
import { RenderLinodeProps } from './SelectLinodePanel';

export const SelectLinodeCards = ({
disabled,
handleSelection,
orderBy: { data: linodes },
selectedLinodeId,
}: RenderLinodeProps) => (
<Grid container spacing={2}>
{linodes.map((linode) => (
<SelectLinodeCard
handleSelection={() =>
handleSelection(linode.id, linode.type, linode.specs.disk)
}
disabled={disabled}
key={linode.id}
linode={linode}
selected={linode.id == selectedLinodeId}
/>
))}
</Grid>
);
Loading

0 comments on commit d9b5b92

Please sign in to comment.