Skip to content

Commit 076ad9d

Browse files
authored
upcoming: [DPS-36770] - Custom HTTPS form - UX and Tech Writing improvements (#13507)
* upcoming: [DPS-36770] - Custom HTTPS form - UX and Tech Writing improvements * upcoming: [DPS-36770] - Code review changes * upcoming: [DPS-36770] - Prepare stream details on deactivate/activate stream fix * upcoming: [DPS-36770] - Prepare destination details fix * upcoming: [DPS-36770] - Stream form: filtering clusters by log generation fix
1 parent 85edc8c commit 076ad9d

21 files changed

Lines changed: 288 additions & 133 deletions
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Upcoming Features
3+
---
4+
5+
Custom HTTPS destination form: improve the UX and update copy ([#13507](https://github.com/linode/manager/pull/13507))

packages/manager/src/features/Delivery/Destinations/DestinationForm/DestinationCreate.test.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -454,8 +454,9 @@ describe('DestinationCreate', () => {
454454
it('should render Authentication autocomplete with None selected and allow to select Basic', async () => {
455455
await selectCustomHttpsDestinationType();
456456

457-
const authenticationAutocomplete =
458-
screen.getByLabelText('Authentication');
457+
const authenticationAutocomplete = screen.getByLabelText(
458+
'Authentication Type'
459+
);
459460

460461
expect(authenticationAutocomplete).toHaveValue('None');
461462

@@ -470,8 +471,9 @@ describe('DestinationCreate', () => {
470471
it('should render Username input and allow to type text', async () => {
471472
await selectCustomHttpsDestinationType();
472473

473-
const authenticationAutocomplete =
474-
screen.getByLabelText('Authentication');
474+
const authenticationAutocomplete = screen.getByLabelText(
475+
'Authentication Type'
476+
);
475477
await userEvent.click(authenticationAutocomplete);
476478
const basicAuthentication = await screen.findByText('Basic');
477479
await userEvent.click(basicAuthentication);
@@ -485,8 +487,9 @@ describe('DestinationCreate', () => {
485487
it('should render Password input and allow to type text', async () => {
486488
await selectCustomHttpsDestinationType();
487489

488-
const authenticationAutocomplete =
489-
screen.getByLabelText('Authentication');
490+
const authenticationAutocomplete = screen.getByLabelText(
491+
'Authentication Type'
492+
);
490493
await userEvent.click(authenticationAutocomplete);
491494
const basicAuthentication = await screen.findByText('Basic');
492495
await userEvent.click(basicAuthentication);
@@ -507,7 +510,7 @@ describe('DestinationCreate', () => {
507510
expect(endpointUrlInput).toHaveValue('https://test-endpoint.com');
508511
});
509512

510-
describe('Client Certificate fields', () => {
513+
describe('Client Certificate Authentication fields', () => {
511514
it('should render TLS Hostname input and allow to type text', async () => {
512515
await selectCustomHttpsDestinationType();
513516

@@ -539,10 +542,10 @@ describe('DestinationCreate', () => {
539542
expect(clientCertificateInput).toHaveValue('test-client-certificate');
540543
});
541544

542-
it('should render Client Key input and allow to type text', async () => {
545+
it('should render Client Private Key input and allow to type text', async () => {
543546
await selectCustomHttpsDestinationType();
544547

545-
const clientKeyInput = screen.getByLabelText('Client Key');
548+
const clientKeyInput = screen.getByLabelText('Client Private Key');
546549
await userEvent.type(clientKeyInput, 'test-client-key');
547550

548551
expect(clientKeyInput).toHaveValue('test-client-key');

packages/manager/src/features/Delivery/Destinations/DestinationForm/DestinationForm.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { Controller, useWatch } from 'react-hook-form';
1515

1616
import {
1717
getDestinationTypeOption,
18+
isFormInEditMode,
1819
useIsACLPLogsEnabled,
1920
} from 'src/features/Delivery/deliveryUtils';
2021
import { DestinationAkamaiObjectStorageDetailsForm } from 'src/features/Delivery/Shared/DestinationAkamaiObjectStorageDetailsForm';
@@ -106,7 +107,9 @@ export const DestinationForm = (props: DestinationFormProps) => {
106107
render={({ field }) => (
107108
<Autocomplete
108109
disableClearable
109-
disabled={!isACLPLogsCustomHttpsEnabled}
110+
disabled={
111+
isFormInEditMode(mode) || !isACLPLogsCustomHttpsEnabled
112+
}
110113
label="Destination Type"
111114
onBlur={field.onBlur}
112115
onChange={(_, { value }) => {
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
export const DESTINATIONS_TABLE_DEFAULT_ORDER = 'desc';
22
export const DESTINATIONS_TABLE_DEFAULT_ORDER_BY = 'created';
33
export const DESTINATIONS_TABLE_PREFERENCE_KEY = 'destinations';
4+
5+
export const MASKED_VALUE = '*****************';

packages/manager/src/features/Delivery/Shared/CustomHeaders.tsx

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
LinkButton,
55
Stack,
66
TextField,
7+
TooltipIcon,
78
Typography,
89
} from '@linode/ui';
910
import Grid from '@mui/material/Grid';
@@ -21,20 +22,39 @@ interface CustomHeaderTitleProps {
2122
control: Control;
2223
controlPath: string;
2324
index: number;
25+
tooltipText: string;
2426
}
2527

2628
const CustomHeaderTitle = (props: CustomHeaderTitleProps) => {
27-
const { control, controlPath, index } = props;
29+
const { control, controlPath, index, tooltipText } = props;
2830

2931
const headerName = useWatch({
3032
control,
3133
name: `${controlPath}[${index}].name`,
3234
});
3335

3436
return (
35-
<Typography noWrap={true} variant="subtitle1">
36-
{headerName?.length ? headerName : `Custom Header ${index + 1}`}
37-
</Typography>
37+
<Stack
38+
alignItems="center"
39+
direction="row"
40+
flexWrap="nowrap"
41+
sx={{ minWidth: 0 }}
42+
>
43+
<Typography
44+
noWrap={true}
45+
overflow="hidden"
46+
textOverflow="ellipsis"
47+
variant="subtitle1"
48+
>
49+
{headerName?.length ? headerName : `Custom Header ${index + 1}`}
50+
</Typography>
51+
<TooltipIcon
52+
labelTooltipIconSize="small"
53+
status="info"
54+
sxTooltipIcon={{ p: 1 }}
55+
text={tooltipText}
56+
/>
57+
</Stack>
3858
);
3959
};
4060

@@ -91,6 +111,7 @@ export const CustomHeaders = (props: CustomHeadersProps) => {
91111
control={control}
92112
controlPath={controlPath}
93113
index={index}
114+
tooltipText="A custom HTTPS header to include in the delivery request."
94115
/>
95116
<IconButton onClick={() => removeField(index)} sx={{ p: 0 }}>
96117
<CloseIcon />
@@ -105,6 +126,7 @@ export const CustomHeaders = (props: CustomHeadersProps) => {
105126
aria-required
106127
errorText={fieldState.error?.message}
107128
label="Name"
129+
labelTooltipText="The name of the custom header to include in the delivery request."
108130
onBlur={controllerField.onBlur}
109131
onChange={controllerField.onChange}
110132
value={controllerField.value}
@@ -119,6 +141,7 @@ export const CustomHeaders = (props: CustomHeadersProps) => {
119141
aria-required
120142
errorText={fieldState.error?.message}
121143
label="Value"
144+
labelTooltipText="The value of the custom header to include in the delivery request."
122145
multiline
123146
onBlur={controllerField.onBlur}
124147
onChange={controllerField.onChange}

packages/manager/src/features/Delivery/Shared/DestinationCustomHttpsDetailsForm.tsx

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import { authenticationType } from '@linode/api-v4';
2-
import { Autocomplete, Divider, TextField, Typography } from '@linode/ui';
2+
import {
3+
Autocomplete,
4+
Divider,
5+
Stack,
6+
TextField,
7+
TooltipIcon,
8+
Typography,
9+
} from '@linode/ui';
310
import { useTheme } from '@mui/material/styles';
411
import React from 'react';
512
import { Controller, useFormContext, useWatch } from 'react-hook-form';
@@ -58,7 +65,7 @@ export const DestinationCustomHttpsDetailsForm = (
5865
<Autocomplete
5966
disableClearable
6067
errorText={fieldState.error?.message}
61-
label="Authentication"
68+
label="Authentication Type"
6269
onBlur={field.onBlur}
6370
onChange={(_, { value }) => {
6471
if (value === authenticationType.None) {
@@ -67,6 +74,10 @@ export const DestinationCustomHttpsDetailsForm = (
6774
field.onChange(value);
6875
}}
6976
options={authenticationTypeOptions}
77+
textFieldProps={{
78+
labelTooltipText:
79+
'The authentication method used for requests sent to your HTTPS endpoint.',
80+
}}
7081
value={getAuthenticationTypeOption(field.value)}
7182
/>
7283
)}
@@ -113,6 +124,7 @@ export const DestinationCustomHttpsDetailsForm = (
113124
aria-required
114125
errorText={fieldState.error?.message}
115126
label="Endpoint URL"
127+
labelTooltipText="The HTTPS endpoint for audit log delivery."
116128
onBlur={field.onBlur}
117129
onChange={(value) => {
118130
field.onChange(value);
@@ -123,23 +135,32 @@ export const DestinationCustomHttpsDetailsForm = (
123135
/>
124136
<Divider sx={{ my: 3 }} />
125137
<Typography sx={{ mt: 0 }} variant="h2">
126-
Additional Options
127-
</Typography>
128-
<Typography sx={{ mt: 2 }} variant="h3">
129-
Client Certificate&nbsp;
130-
<span
131-
style={{ fontWeight: theme.tokens.font.FontWeight.Regular.Normal }}
132-
>
133-
(optional)
134-
</span>
138+
Connection Settings
135139
</Typography>
140+
<Stack alignItems="center" direction="row" flexWrap="nowrap" mt={2}>
141+
<Typography variant="h3">
142+
Client Certificate Authentication&nbsp;
143+
<span
144+
style={{ fontWeight: theme.tokens.font.FontWeight.Regular.Normal }}
145+
>
146+
(optional)
147+
</span>
148+
</Typography>
149+
<TooltipIcon
150+
labelTooltipIconSize="small"
151+
status="info"
152+
sxTooltipIcon={{ p: 1 }}
153+
text="Certificate details are used to authenticate the audit log delivery service and verify the HTTPs destination during mutual TLS (mTLS( connections. This section is required only if the destination enforces client certificate authentication."
154+
/>
155+
</Stack>
136156
<Controller
137157
control={control}
138158
name={controlPaths.tlsHostname}
139159
render={({ field, fieldState }) => (
140160
<TextField
141161
errorText={fieldState.error?.message}
142162
label="TLS Hostname"
163+
labelTooltipText="The hostname used to verify the server’s certificate and matches the Subject Alternative Names (SANs) in the certificate. If not provided, the hostname is fetched from the endpoint URL."
143164
multiline
144165
onBlur={field.onBlur}
145166
onChange={(value) => {
@@ -156,6 +177,7 @@ export const DestinationCustomHttpsDetailsForm = (
156177
<TextField
157178
errorText={fieldState.error?.message}
158179
label="CA Certificate"
180+
labelTooltipText="The certification authority (CA) certificate used to verify the origin server’s certificate. If the certificate is not signed by a well-known certification authority, enter the CA certificate in the PEM format for verification."
159181
multiline
160182
onBlur={field.onBlur}
161183
onChange={(value) => {
@@ -172,6 +194,7 @@ export const DestinationCustomHttpsDetailsForm = (
172194
<TextField
173195
errorText={fieldState.error?.message}
174196
label="Client Certificate"
197+
labelTooltipText="The digital certificate you want to use to authenticate requests to your destination. Provide both the client certificate and the client private key in the PEM format to use mutual authentication."
175198
multiline
176199
onBlur={field.onBlur}
177200
onChange={(value) => {
@@ -187,7 +210,8 @@ export const DestinationCustomHttpsDetailsForm = (
187210
render={({ field, fieldState }) => (
188211
<TextField
189212
errorText={fieldState.error?.message}
190-
label="Client Key"
213+
label="Client Private Key"
214+
labelTooltipText="The private key you want to use to authenticate to the backend server. Provide both the client certificate and the client private key in the non-encrypted PKCS8 format to use mutual authentication."
191215
multiline
192216
onBlur={field.onBlur}
193217
onChange={(value) => {
@@ -217,6 +241,10 @@ export const DestinationCustomHttpsDetailsForm = (
217241
field.onChange(value?.value || null);
218242
}}
219243
options={contentTypeOptions}
244+
textFieldProps={{
245+
labelTooltipText:
246+
'The format and character encoding of the delivered audit log data.',
247+
}}
220248
value={field.value ? getContentTypeOption(field.value) : null}
221249
/>
222250
)}

0 commit comments

Comments
 (0)