diff --git a/packages/manager/.changeset/pr-12020-added-1744609645278.md b/packages/manager/.changeset/pr-12020-added-1744609645278.md new file mode 100644 index 00000000000..c744be9aa0e --- /dev/null +++ b/packages/manager/.changeset/pr-12020-added-1744609645278.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Added +--- + +Custom checkbox sizing per Akamai Design System ([#12020](https://github.com/linode/manager/pull/12020)) diff --git a/packages/manager/.changeset/pr-12020-tech-stories-1745313505956.md b/packages/manager/.changeset/pr-12020-tech-stories-1745313505956.md new file mode 100644 index 00000000000..77d7a90cd56 --- /dev/null +++ b/packages/manager/.changeset/pr-12020-tech-stories-1745313505956.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tech Stories +--- + +Apply small-size checkboxes to table components and remove hardcoded checkbox sizes from `SelectableTableRow` and `TransferTable` ([#12020](https://github.com/linode/manager/pull/12020)) diff --git a/packages/manager/src/components/AccessPanel/UserSSHKeyPanel.tsx b/packages/manager/src/components/AccessPanel/UserSSHKeyPanel.tsx index c745a8bbb0e..df2cfe534df 100644 --- a/packages/manager/src/components/AccessPanel/UserSSHKeyPanel.tsx +++ b/packages/manager/src/components/AccessPanel/UserSSHKeyPanel.tsx @@ -26,8 +26,6 @@ const MAX_SSH_KEYS_DISPLAY = 25; const useStyles = makeStyles()((theme: Theme) => ({ cellCheckbox: { - paddingLeft: theme.spacing(1), - paddingRight: theme.spacing(1), width: 50, }, cellUser: { @@ -57,16 +55,11 @@ interface Props { export const UserSSHKeyPanel = (props: Props) => { const { classes } = useStyles(); const theme = useTheme(); - const { - authorizedUsers, - disabled, - headingVariant, - setAuthorizedUsers, - } = props; - - const [isCreateDrawerOpen, setIsCreateDrawerOpen] = React.useState( - false - ); + const { authorizedUsers, disabled, headingVariant, setAuthorizedUsers } = + props; + + const [isCreateDrawerOpen, setIsCreateDrawerOpen] = + React.useState(false); const pagination = usePagination(1); @@ -140,12 +133,13 @@ export const UserSSHKeyPanel = (props: Props) => { onToggle(profile.username)} + size="small" /> @@ -169,12 +163,13 @@ export const UserSSHKeyPanel = (props: Props) => { onToggle(user.username)} + size="small" /> diff --git a/packages/manager/src/components/SelectableTableRow/SelectableTableRow.tsx b/packages/manager/src/components/SelectableTableRow/SelectableTableRow.tsx index c76965c7422..d12ab3e9667 100644 --- a/packages/manager/src/components/SelectableTableRow/SelectableTableRow.tsx +++ b/packages/manager/src/components/SelectableTableRow/SelectableTableRow.tsx @@ -28,19 +28,20 @@ export const SelectableTableRow = React.memo( return ( ({ '& td': { - padding: '0px 15px', + padding: `0 ${theme.tokens.spacing.S12}`, }, - }} + })} > {props.children} @@ -52,10 +53,6 @@ export const SelectableTableRow = React.memo( const StyledTableCell = styled(TableCell, { label: 'StyledTableCell', })({ - '& svg': { - height: 20, - width: 20, - }, paddingLeft: 0, paddingRight: 0, textAlign: 'center', diff --git a/packages/manager/src/features/CloudPulse/Alerts/AlertsResources/DisplayAlertResources.tsx b/packages/manager/src/features/CloudPulse/Alerts/AlertsResources/DisplayAlertResources.tsx index 7a5212ab7a1..d9f9c4a2ce0 100644 --- a/packages/manager/src/features/CloudPulse/Alerts/AlertsResources/DisplayAlertResources.tsx +++ b/packages/manager/src/features/CloudPulse/Alerts/AlertsResources/DisplayAlertResources.tsx @@ -203,14 +203,15 @@ export const DisplayAlertResources = React.memo( {isSelectionsNeeded && ( ) : undefined } - placement="right-start" > @@ -253,13 +251,13 @@ export const DisplayAlertResources = React.memo( )} {columns.map(({ label, sortingKey }) => ( - handleSort(orderBy, order, handlePageChange) - } active={sorting.orderBy === sortingKey} data-qa-header={label.toLowerCase()} data-testid={label.toLowerCase()} direction={sorting.order} + handleClick={(orderBy, order) => + handleSort(orderBy, order, handlePageChange) + } key={label} label={sortingKey ?? ''} > @@ -288,6 +286,7 @@ export const DisplayAlertResources = React.memo( }} > ) : undefined } - placement="right-start" > { - handleSelectionChange([id], !checked); - }} - sx={{ - p: 0, - }} checked={checked} data-testid={`select_item_${id}`} disabled={isItemCheckboxDisabled} + onClick={() => { + handleSelectionChange([id], !checked); + }} + size="small" /> @@ -353,6 +349,8 @@ export const DisplayAlertResources = React.memo( {!isDataLoadingError && paginatedData.length !== 0 && ( { handlePageNumberChange(handlePageChange, page); }} @@ -361,8 +359,6 @@ export const DisplayAlertResources = React.memo( handlePageNumberChange(handlePageChange, 1); // Moves to the first page after page size change scrollToGivenElement(); }} - count={count} - eventCategory="alerts_resources" page={page} pageSize={pageSize} /> diff --git a/packages/manager/src/features/EntityTransfers/EntityTransfersCreate/TransferTable.styles.ts b/packages/manager/src/features/EntityTransfers/EntityTransfersCreate/TransferTable.styles.ts index de7b50afa3b..12e3fc563a9 100644 --- a/packages/manager/src/features/EntityTransfers/EntityTransfersCreate/TransferTable.styles.ts +++ b/packages/manager/src/features/EntityTransfers/EntityTransfersCreate/TransferTable.styles.ts @@ -1,4 +1,4 @@ -import { Checkbox, Typography } from '@linode/ui'; +import { Typography } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { DebouncedSearchTextField } from 'src/components/DebouncedSearchTextField'; @@ -6,21 +6,6 @@ import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFoot import { Table } from 'src/components/Table'; import { TableCell } from 'src/components/TableCell'; -export const StyledCheckbox = styled(Checkbox, { - label: 'StyledCheckbox', -})({ - '& svg': { - height: 20, - width: 20, - }, -}); - -export const StyledEmptyCheckbox = styled(Checkbox, { - label: 'StyledEmptyCheckbox', -})({ - '& svg': { height: 20, width: 20 }, -}); - export const StyledPaginationFooter = styled(PaginationFooter, { label: 'StyledPaginationFooter', })(({ theme }) => ({ diff --git a/packages/manager/src/features/EntityTransfers/EntityTransfersCreate/TransferTable.tsx b/packages/manager/src/features/EntityTransfers/EntityTransfersCreate/TransferTable.tsx index 1744eb1dabe..73490e5b5d1 100644 --- a/packages/manager/src/features/EntityTransfers/EntityTransfersCreate/TransferTable.tsx +++ b/packages/manager/src/features/EntityTransfers/EntityTransfersCreate/TransferTable.tsx @@ -1,3 +1,4 @@ +import { Checkbox } from '@linode/ui'; import * as React from 'react'; import { TableBody } from 'src/components/TableBody'; @@ -7,9 +8,7 @@ import { TableRow } from 'src/components/TableRow'; import { StyledCheckAllTableCell, - StyledCheckbox, StyledDebouncedSearchTextField, - StyledEmptyCheckbox, StyledPaginationFooter, StyledTable, StyledTypography, @@ -45,10 +44,6 @@ export const TransferTable = React.memo((props: Props) => { return toggleSelectAll(e.target.checked); }; - const ConditionalCheckbox = hasSelectedAll - ? StyledCheckbox - : StyledEmptyCheckbox; - return ( <> Linodes @@ -65,12 +60,13 @@ export const TransferTable = React.memo((props: Props) => { - {headers.map((thisHeader) => ( diff --git a/packages/manager/src/features/Linodes/CloneLanding/Disks.tsx b/packages/manager/src/features/Linodes/CloneLanding/Disks.tsx index 92988aadb42..4a33769e7d8 100644 --- a/packages/manager/src/features/Linodes/CloneLanding/Disks.tsx +++ b/packages/manager/src/features/Linodes/CloneLanding/Disks.tsx @@ -15,8 +15,8 @@ import type { DiskSelection } from './utilities'; import type { Disk } from '@linode/api-v4/lib/linodes'; export interface DisksProps { - diskSelection: DiskSelection; disks: Disk[]; + diskSelection: DiskSelection; handleSelect: (id: number) => void; selectedConfigIds: number[]; } @@ -73,6 +73,7 @@ export const Disks = (props: DisksProps) => { data-testid={`checkbox-${disk.id}`} disabled={isConfigSelected} onChange={() => handleSelect(disk.id)} + size="small" text={disk.label} /> diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Addons/Backups.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Addons/Backups.tsx index a538e48e9dd..20bc9cc35e5 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Addons/Backups.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Addons/Backups.tsx @@ -1,3 +1,4 @@ +import { useAccountSettings, useRegionsQuery } from '@linode/queries'; import { Checkbox, FormControlLabel, @@ -12,7 +13,6 @@ import { Currency } from 'src/components/Currency'; import { DISK_ENCRYPTION_BACKUPS_CAVEAT_COPY } from 'src/components/Encryption/constants'; import { Link } from 'src/components/Link'; import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGrantCheck'; -import { useAccountSettings, useRegionsQuery } from '@linode/queries'; import { useTypeQuery } from 'src/queries/types'; import { getMonthlyBackupsPrice } from 'src/utilities/pricing/backups'; @@ -63,6 +63,11 @@ export const Backups = () => { return ( ({ mt: `-${theme.tokens.spacing.S8}` })} /> + } + data-testid="backups" disabled={ isDistributedRegionSelected || isLinodeCreateRestricted || @@ -82,12 +87,12 @@ export const Backups = () => { {checked && diskEncryption === 'enabled' && ( )} @@ -108,9 +113,6 @@ export const Backups = () => { } - checked={checked} - control={} - data-testid="backups" onChange={field.onChange} sx={{ alignItems: 'start' }} /> diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Addons/PrivateIP.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Addons/PrivateIP.tsx index fd3eb51d79d..36f2f5dc267 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Addons/PrivateIP.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Addons/PrivateIP.tsx @@ -30,6 +30,11 @@ export const PrivateIP = () => { return ( ({ mt: `-${theme.tokens.spacing.S8}` })} /> + } + disabled={isDistributedRegionSelected || isLinodeCreateRestricted} label={ @@ -41,9 +46,6 @@ export const PrivateIP = () => { } - checked={field.value ?? false} - control={} - disabled={isDistributedRegionSelected || isLinodeCreateRestricted} onChange={field.onChange} sx={{ alignItems: 'start' }} /> diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodesDetail.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodesDetail.tsx index be9cf2ad04c..1e623966790 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodesDetail.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodesDetail.tsx @@ -42,9 +42,10 @@ export const LinodeDetail = () => { const location = useLocation(); const history = useHistory(); - const queryParams = getQueryParamsFromQueryString( - location.search - ); + const queryParams = + getQueryParamsFromQueryString( + location.search + ); const pathname = location.pathname; @@ -84,6 +85,8 @@ export const LinodeDetail = () => { {['resize', 'rescue', 'migrate', 'upgrade', 'rebuild'].map((path) => ( { [path]: 'true', }).toString(), }} - from={`${url}/${path}`} - key={path} /> ))} { )} diff --git a/packages/ui/src/components/Checkbox/Checkbox.stories.tsx b/packages/ui/src/components/Checkbox/Checkbox.stories.tsx index 1c63c838d3b..c430f2aedab 100644 --- a/packages/ui/src/components/Checkbox/Checkbox.stories.tsx +++ b/packages/ui/src/components/Checkbox/Checkbox.stories.tsx @@ -115,3 +115,9 @@ export const WithLabelAndTooltip: Story = { toolTipText: 'This is the tooltip!', }, }; + +export const SmallSize: Story = { + args: { + size: 'small', + }, +}; diff --git a/packages/ui/src/components/Checkbox/Checkbox.tsx b/packages/ui/src/components/Checkbox/Checkbox.tsx index 1470e11affe..c13f45741dd 100644 --- a/packages/ui/src/components/Checkbox/Checkbox.tsx +++ b/packages/ui/src/components/Checkbox/Checkbox.tsx @@ -7,13 +7,19 @@ import { CheckboxIcon, CheckboxIndeterminateIcon, } from '../../assets/icons'; -import { TooltipIcon } from '../TooltipIcon'; import { FormControlLabel } from '../FormControlLabel'; +import { TooltipIcon } from '../TooltipIcon'; import type { CheckboxProps } from '@mui/material/Checkbox'; import type { SxProps, Theme } from '@mui/material/styles'; interface Props extends CheckboxProps { + /** + * New custom size prop (Overides and restrict 'size' to only 'small' and 'medium' per ADS) + * + * @default medium + */ + size?: 'medium' | 'small'; /** * Styles applied to the `FormControlLabel`. Only works when `text` is defined. */ @@ -75,7 +81,9 @@ export const Checkbox = (props: Props) => { ); }; -const StyledCheckbox = styled(_Checkbox)(({ theme, ...props }) => ({ +const StyledCheckbox = styled(_Checkbox, { + label: 'StyledCheckbox', +})(({ theme, ...props }) => ({ '& .defaultFill': { transition: theme.transitions.create(['fill']), }, diff --git a/packages/ui/src/foundations/themes/light.ts b/packages/ui/src/foundations/themes/light.ts index 3b32c7e326e..8dbbd1acddd 100644 --- a/packages/ui/src/foundations/themes/light.ts +++ b/packages/ui/src/foundations/themes/light.ts @@ -423,9 +423,10 @@ export const lightTheme: ThemeOptions = { }, root: { // Spacing for clear and popup icons (circular loading) - '&.MuiAutocomplete-hasPopupIcon.MuiAutocomplete-hasClearIcon .MuiAutocomplete-inputRoot': { - paddingRight: Spacing.S48, - }, + '&.MuiAutocomplete-hasPopupIcon.MuiAutocomplete-hasClearIcon .MuiAutocomplete-inputRoot': + { + paddingRight: Spacing.S48, + }, maxWidth: inputMaxWidth, }, tag: { @@ -550,11 +551,12 @@ export const lightTheme: ThemeOptions = { { props: { color: 'error' }, style: { - '&:not([aria-disabled="true"]):hover, &:not([aria-disabled="true"]):focus': { - backgroundColor: Background.Negativesubtle, - border: `1px solid ${Border.Negative}`, - color: Content.Text.Negative, - }, + '&:not([aria-disabled="true"]):hover, &:not([aria-disabled="true"]):focus': + { + backgroundColor: Background.Negativesubtle, + border: `1px solid ${Border.Negative}`, + color: Content.Text.Negative, + }, '&[aria-disabled="true"]': { backgroundColor: 'transparent', border: `1px solid ${Button.Secondary.Disabled.Border}`, @@ -627,6 +629,29 @@ export const lightTheme: ThemeOptions = { color: Component.Checkbox.Empty.Default.Border, }, }, + defaultProps: { + size: 'medium', + }, + variants: [ + { + props: { size: 'small' }, + style: { + svg: { + height: '16px', + width: '16px', + }, + }, + }, + { + props: { size: 'medium' }, + style: { + svg: { + height: '20px', + width: '20px', + }, + }, + }, + ], }, MuiChip: { styleOverrides: { @@ -957,15 +982,16 @@ export const lightTheme: ThemeOptions = { border: `1px solid ${TextField.Focus.Border}`, color: TextField.Focus.Text, }, - '&:disabled, &[aria-disabled="true"], &.Mui-disabled, &.Mui-disabled:hover': { - '& .MuiInputAdornment-root': { + '&:disabled, &[aria-disabled="true"], &.Mui-disabled, &.Mui-disabled:hover': + { + '& .MuiInputAdornment-root': { + cursor: 'not-allowed', + }, + backgroundColor: TextField.Disabled.Background, + border: `1px solid ${TextField.Disabled.Border}`, + color: TextField.Disabled.Text, cursor: 'not-allowed', }, - backgroundColor: TextField.Disabled.Background, - border: `1px solid ${TextField.Disabled.Border}`, - color: TextField.Disabled.Text, - cursor: 'not-allowed', - }, '&:hover': { backgroundColor: TextField.Hover.Background, border: `1px solid ${TextField.Hover.Border}`, @@ -1393,9 +1419,11 @@ export const lightTheme: ThemeOptions = { // Zebra Striping '&.MuiTable-zebra': { // Linodes Group by Tag: First Row is the Title - '&.MuiTable-groupByTag .MuiTableRow-root:not(:first-of-type):nth-of-type(odd)': MuiTableZebraStyles, + '&.MuiTable-groupByTag .MuiTableRow-root:not(:first-of-type):nth-of-type(odd)': + MuiTableZebraStyles, // Default Striping - '&:not(.MuiTable-groupByTag) .MuiTableRow-root:not(.MuiTableRow-nested):nth-of-type(even)': MuiTableZebraStyles, + '&:not(.MuiTable-groupByTag) .MuiTableRow-root:not(.MuiTableRow-nested):nth-of-type(even)': + MuiTableZebraStyles, '.MuiTableRow-root:not(:last-of-type)': { '.MuiTableCell-root': { borderBottom: 0, @@ -1491,9 +1519,10 @@ export const lightTheme: ThemeOptions = { backgroundColor: Table.Row.Background.Hover, }, // Disable hover for nested rows (VPC) - '&.MuiTableRow-nested, &.MuiTableRow-nested.MuiTableRow-hover:hover': { - backgroundColor: Table.Row.Background.Default, - }, + '&.MuiTableRow-nested, &.MuiTableRow-nested.MuiTableRow-hover:hover': + { + backgroundColor: Table.Row.Background.Default, + }, '&.disabled-row .MuiTableCell-root': { // TODO: Use design tokens in future when ready backgroundColor: Interaction.Background.Disabled,