Skip to content

refactor: [M3-9656] - [Akamai Design System] Label Component #12005

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

Draft
wants to merge 4 commits into
base: develop
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ export const FirewallAuthorization = () => {
<AkamaiBanner
action={
<FormControlLabel
label={
flags.secureVmCopy?.firewallAuthorizationLabel ??
'I am authorized to create a Linode without a firewall'
}
checked={field.value ?? false}
className="error-for-scroll"
control={<Checkbox />}
disableTypography
label={
flags.secureVmCopy?.firewallAuthorizationLabel ??
'I am authorized to create a Linode without a firewall'
}
onChange={field.onChange}
sx={{ fontSize: 14 }}
sx={(theme) => ({ fontSize: theme.tokens.font.FontSize.Xs })}
/>
}
text={
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useRegionsQuery } from '@linode/queries';
import { Accordion, Notice, TextField, Typography } from '@linode/ui';
import React, { useMemo } from 'react';
import { Controller, useFormContext, useWatch } from 'react-hook-form';

import { Link } from 'src/components/Link';
import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGrantCheck';
import { useImageQuery } from 'src/queries/images';
import { useRegionsQuery } from '@linode/queries';

import { UserDataHeading } from './UserDataHeading';

Expand Down Expand Up @@ -39,10 +39,10 @@ export const UserData = () => {
);
};

const region = useMemo(() => regions?.find((r) => r.id === regionId), [
regions,
regionId,
]);
const region = useMemo(
() => regions?.find((r) => r.id === regionId),
[regions, regionId]
);

const isLinodeCreateRestricted = useRestrictedGlobalGrantCheck({
globalGrantType: 'add_linodes',
Expand Down Expand Up @@ -75,8 +75,20 @@ export const UserData = () => {
</Notice>
)}
<Controller
control={control}
name="metadata.user_data"
render={({ field, fieldState }) => (
<TextField
disabled={isLinodeCreateRestricted}
errorText={fieldState.error?.message}
expand
label="User Data"
labelTooltipIconPosition="right"
labelTooltipIconSize="small"
// labelTooltipIconPosition="left"
// labelTooltipIconSize="large"
labelTooltipText="Compatible formats include cloud-config data and executable scripts."
multiline
onBlur={(e) => {
field.onBlur();
checkFormat({
Expand All @@ -91,18 +103,10 @@ export const UserData = () => {
userData: e.target.value,
});
}}
disabled={isLinodeCreateRestricted}
errorText={fieldState.error?.message}
expand
label="User Data"
labelTooltipText="Compatible formats include cloud-config data and executable scripts."
multiline
rows={1}
value={field.value ?? ''}
/>
)}
control={control}
name="metadata.user_data"
/>
</Accordion>
);
Expand Down
15 changes: 7 additions & 8 deletions packages/manager/src/features/Linodes/LinodeCreate/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { createLazyRoute } from '@tanstack/react-router';
import { useSnackbar } from 'notistack';
import React, { useEffect, useRef } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import type { SubmitHandler } from 'react-hook-form';
import { useHistory } from 'react-router-dom';

import { DocumentTitleSegment } from 'src/components/DocumentTitle';
Expand Down Expand Up @@ -70,7 +71,6 @@ import type {
LinodeCreateFormContext,
LinodeCreateFormValues,
} from './utilities';
import type { SubmitHandler } from 'react-hook-form';

export const LinodeCreate = () => {
const { params, setParams } = useLinodeCreateQueryParams();
Expand All @@ -97,9 +97,8 @@ export const LinodeCreate = () => {
const { mutateAsync: cloneLinode } = useCloneLinodeMutation();
const { mutateAsync: updateAccountAgreements } = useMutateAccountAgreements();

const {
handleLinodeCreateAnalyticsFormError,
} = useHandleLinodeCreateAnalyticsFormError(params.type ?? 'OS');
const { handleLinodeCreateAnalyticsFormError } =
useHandleLinodeCreateAnalyticsFormError(params.type ?? 'OS');

const currentTabIndex = getTabIndex(params.type);

Expand Down Expand Up @@ -193,15 +192,15 @@ export const LinodeCreate = () => {
<FormProvider {...form}>
<DocumentTitleSegment segment="Create a Linode" />
<LandingHeader
docsLabel="Getting Started"
docsLink="https://techdocs.akamai.com/cloud-computing/docs/getting-started"
onDocsClick={() =>
sendLinodeCreateFormInputEvent({
createType: params.type ?? 'OS',
interaction: 'click',
label: 'Getting Started',
})
}
docsLabel="Getting Started"
docsLink="https://techdocs.akamai.com/cloud-computing/docs/getting-started"
title="Create"
/>
<form onSubmit={form.handleSubmit(onSubmit)}>
Expand All @@ -218,13 +217,13 @@ export const LinodeCreate = () => {
</TabList>
{isLinodeCreateRestricted && (
<Notice
important
sx={{ marginBottom: 2 }}
text={getRestrictedResourceText({
action: 'create',
isSingular: false,
resourceType: 'Linodes',
})}
important
sx={{ marginBottom: 2 }}
variant="error"
/>
)}
Expand Down
98 changes: 56 additions & 42 deletions packages/ui/src/components/TextField/TextField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@
type Value = null | number | string | undefined;

interface LabelToolTipProps {
labelTooltipIconPosition?: 'left' | 'right';
labelTooltipIconSize?: 'large' | 'small';
labelTooltipText?: JSX.Element | string;
}

Expand Down Expand Up @@ -146,6 +148,8 @@
inputId,
inputProps,
label,
labelTooltipIconPosition,
labelTooltipIconSize,
labelTooltipText,
loading,
max,
Expand All @@ -170,12 +174,8 @@
const [_value, setValue] = React.useState<Value>(value ?? '');
const theme = useTheme();

const {
errorScrollClassName,
errorTextId,
helperTextId,
validInputId,
} = useFieldIds({ errorGroup, hasError: Boolean(errorText), inputId, label });
const { errorScrollClassName, errorTextId, helperTextId, validInputId } =
useFieldIds({ errorGroup, hasError: Boolean(errorText), inputId, label });

const isControlled = value !== undefined;

Expand All @@ -186,7 +186,7 @@
}, [value, isControlled]);

const handleBlur = (
e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>
e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
if (trimmed) {
const trimmedValue = e.target.value.trim();
Expand Down Expand Up @@ -246,21 +246,19 @@
}
}
},
[min, max, type, onChange]
[min, max, type, onChange],
);

const labelSuffixText = required
? '(required)'
: optional
? '(optional)'
: null;
? '(optional)'
: null;

return (
<Box
{...containerProps}
className={`${errorText ? errorScrollClassName : ''} ${
containerProps?.className || ''
}`}
className={`${errorText ? errorScrollClassName : ''} ${containerProps?.className || ''}`}
sx={{
...(Boolean(tooltipText) && {
alignItems: 'flex-end',
Expand All @@ -271,22 +269,38 @@
}}
>
<Box
sx={{
marginBottom: theme.spacing(1),
...(!noMarginTop && { marginTop: theme.spacing(2) }),
}}
alignItems={'center'}
className={hideLabel ? 'visually-hidden' : ''}
data-testid="inputLabelWrapper"
display="flex"
sx={{
marginBottom: theme.spacingFunction(8),

Check failure on line 277 in packages/ui/src/components/TextField/TextField.tsx

View workflow job for this annotation

GitHub Actions / test-shared

src/components/LinodeSelect/LinodeSelect.test.tsx > LinodeSelect > should display custom no options message

TypeError: theme.spacingFunction is not a function ❯ TextField ../ui/src/components/TextField/TextField.tsx:277:31 ❯ renderWithHooks ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:15486:18 ❯ mountIndeterminateComponent ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:20103:13 ❯ beginWork ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:21626:16 ❯ beginWork$1 ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:27465:14 ❯ performUnitOfWork ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26599:12 ❯ workLoopSync ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26505:5 ❯ renderRootSync ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26473:7 ❯ recoverFromConcurrentError ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:25889:20 ❯ performConcurrentWorkOnRoot ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:25789:22

Check failure on line 277 in packages/ui/src/components/TextField/TextField.tsx

View workflow job for this annotation

GitHub Actions / test-shared

src/components/LinodeSelect/LinodeSelect.test.tsx > LinodeSelect > should display default no options message

TypeError: theme.spacingFunction is not a function ❯ TextField ../ui/src/components/TextField/TextField.tsx:277:31 ❯ renderWithHooks ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:15486:18 ❯ mountIndeterminateComponent ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:20103:13 ❯ beginWork ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:21626:16 ❯ beginWork$1 ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:27465:14 ❯ performUnitOfWork ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26599:12 ❯ workLoopSync ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26505:5 ❯ renderRootSync ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26473:7 ❯ recoverFromConcurrentError ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:25889:20 ❯ performConcurrentWorkOnRoot ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:25789:22

Check failure on line 277 in packages/ui/src/components/TextField/TextField.tsx

View workflow job for this annotation

GitHub Actions / test-shared

src/components/LinodeSelect/LinodeSelect.test.tsx > LinodeSelect > should display no options message when user input does not match

TypeError: theme.spacingFunction is not a function ❯ TextField ../ui/src/components/TextField/TextField.tsx:277:31 ❯ renderWithHooks ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:15486:18 ❯ mountIndeterminateComponent ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:20103:13 ❯ beginWork ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:21626:16 ❯ beginWork$1 ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:27465:14 ❯ performUnitOfWork ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26599:12 ❯ workLoopSync ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26505:5 ❯ renderRootSync ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26473:7 ❯ recoverFromConcurrentError ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:25889:20 ❯ performConcurrentWorkOnRoot ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:25789:22

Check failure on line 277 in packages/ui/src/components/TextField/TextField.tsx

View workflow job for this annotation

GitHub Actions / test-shared

src/components/LinodeSelect/LinodeSelect.test.tsx > LinodeSelect > should display no options message when user input does not match an option

TypeError: theme.spacingFunction is not a function ❯ TextField ../ui/src/components/TextField/TextField.tsx:277:31 ❯ renderWithHooks ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:15486:18 ❯ mountIndeterminateComponent ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:20103:13 ❯ beginWork ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:21626:16 ❯ beginWork$1 ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:27465:14 ❯ performUnitOfWork ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26599:12 ❯ workLoopSync ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26505:5 ❯ renderRootSync ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26473:7 ❯ recoverFromConcurrentError ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:25889:20 ❯ performConcurrentWorkOnRoot ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:25789:22

Check failure on line 277 in packages/ui/src/components/TextField/TextField.tsx

View workflow job for this annotation

GitHub Actions / test-shared

src/components/LinodeSelect/LinodeSelect.test.tsx > LinodeSelect > should not display no options message when user input matches an option

TypeError: theme.spacingFunction is not a function ❯ TextField ../ui/src/components/TextField/TextField.tsx:277:31 ❯ renderWithHooks ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:15486:18 ❯ mountIndeterminateComponent ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:20103:13 ❯ beginWork ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:21626:16 ❯ beginWork$1 ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:27465:14 ❯ performUnitOfWork ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26599:12 ❯ workLoopSync ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26505:5 ❯ renderRootSync ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:26473:7 ❯ recoverFromConcurrentError ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:25889:20 ❯ performConcurrentWorkOnRoot ../../node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:25789:22
...(!noMarginTop && { marginTop: theme.spacingFunction(16) }),
}}
>
{labelTooltipText && labelTooltipIconPosition === 'left' && (
<TooltipIcon
labelTooltipIconSize={labelTooltipIconSize}
status="help"
sxTooltipIcon={{
marginRight: `${theme.spacingFunction(4)}`,
padding: `${theme.spacingFunction(4)} ${theme.spacingFunction(4)} ${theme.spacingFunction(4)} ${theme.spacingFunction(2)}`,
}}
text={labelTooltipText}
width={tooltipWidth}
/>
)}
<InputLabel
data-qa-textfield-label={label}
htmlFor={validInputId}
sx={{
marginBottom: 0,
transform: 'none',
fontSize:
labelTooltipIconSize === 'large'
? theme.tokens.font.FontSize.S
: theme.tokens.font.FontSize.Xs,
}}
data-qa-textfield-label={label}
htmlFor={validInputId}
{...InputLabelProps} // We should change this name so that it's not conflicting with the deprecated prop
>
{label}
Expand All @@ -297,26 +311,26 @@
</Box>
)}
</InputLabel>
{labelTooltipText && (
{labelTooltipText && labelTooltipIconPosition === 'right' && (
<TooltipIcon
labelTooltipIconSize={labelTooltipIconSize}
status="help"
sxTooltipIcon={{
marginLeft: `${theme.spacing(0.5)}`,
padding: `${theme.spacing(0.5)}`,
marginLeft: `${theme.spacingFunction(4)}`,
padding: `${theme.spacingFunction(4)}`,
}}
status="help"
text={labelTooltipText}
width={tooltipWidth}
/>
)}
</Box>

{helperText && helperTextPosition === 'top' && (
<FormHelperText
data-qa-textfield-helper-text
id={helperTextId}
sx={{
marginTop: 0,
}}
data-qa-textfield-helper-text
id={helperTextId}
>
{helperText}
</FormHelperText>
Expand All @@ -332,6 +346,17 @@
<_TextField
{...textFieldProps}
{...dataAttrs}
className={className}
error={!!error || !!errorText}
fullWidth
helperText={''}
/**
* Set _helperText_ and _label_ to no value because we want to
* have the ability to put the helper text under the label at the top.
*/
label={''}
onBlur={handleBlur}
onChange={handleChange}
slotProps={{
htmlInput: {
'aria-describedby': helperText ? helperTextId : undefined,
Expand Down Expand Up @@ -379,17 +404,6 @@
}),
...props.sx,
}}
className={className}
error={!!error || !!errorText}
fullWidth
helperText={''}
/**
* Set _helperText_ and _label_ to no value because we want to
* have the ability to put the helper text under the label at the top.
*/
label={''}
onBlur={handleBlur}
onChange={handleChange}
type={type}
/*
* Let us explicitly pass an empty string to the input
Expand All @@ -402,15 +416,15 @@
</_TextField>
{tooltipText && (
<TooltipIcon
classes={{ popper: tooltipClasses }}
onMouseEnter={tooltipOnMouseEnter}
status="help"
sxTooltipIcon={{
height: '34px',
margin: '0px 0px 0px 4px',
padding: '17px',
width: '34px',
}}
classes={{ popper: tooltipClasses }}
onMouseEnter={tooltipOnMouseEnter}
status="help"
text={tooltipText}
tooltipPosition={tooltipPosition}
width={tooltipWidth}
Expand All @@ -419,6 +433,8 @@
</Box>
{errorText && (
<FormHelperText
data-qa-textfield-error-text={label}
role="alert"
sx={{
...((editable || hasAbsoluteError) && {
position: 'absolute',
Expand All @@ -434,8 +450,6 @@
top: 42,
width: '100%',
}}
data-qa-textfield-error-text={label}
role="alert"
>
{errorText}
</FormHelperText>
Expand Down
Loading
Loading