Skip to content

Commit 42d7227

Browse files
authoredJan 27, 2025
Fleet UI: Fix Manage automation dropdown styling (#25753)
1 parent ac03358 commit 42d7227

File tree

11 files changed

+194
-101
lines changed

11 files changed

+194
-101
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Fleet UI: Fixed styling for manage automation buttons and dropdown

‎frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx

+167-79
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import FormField from "components/forms/FormField";
2929
import DropdownOptionTooltipWrapper from "components/forms/fields/Dropdown/DropdownOptionTooltipWrapper";
3030
import Icon from "components/Icon";
3131
import { IconNames } from "components/icons";
32+
import { TooltipContent } from "interfaces/dropdownOption";
3233

3334
const getOptionBackgroundColor = (
3435
state: OptionProps<CustomOptionType, false>
@@ -37,9 +38,9 @@ const getOptionBackgroundColor = (
3738
};
3839

3940
export interface CustomOptionType {
40-
label: string;
41+
label: React.ReactNode;
4142
value: string;
42-
tooltipContent?: string;
43+
tooltipContent?: TooltipContent;
4344
helpText?: string;
4445
isDisabled?: boolean;
4546
iconName?: IconNames;
@@ -62,8 +63,12 @@ export interface IDropdownWrapper {
6263
placeholder?: string;
6364
/** E.g. scroll to view dropdown menu in a scrollable parent container */
6465
onMenuOpen?: () => void;
65-
/** Table filter dropdowns have filter icon and height: 40px */
66-
tableFilter?: boolean;
66+
/** Table filter dropdowns have filter icon and height: 40px
67+
* Button dropdowns have hover/active state, padding, height like actual buttons */
68+
variant?: "table-filter" | "button";
69+
/** This makes the menu fit all text without wrapping,
70+
* aligning right to fit text on screen */
71+
nowrapMenu?: boolean;
6772
}
6873

6974
const baseClass = "dropdown-wrapper";
@@ -84,10 +89,11 @@ const DropdownWrapper = ({
8489
iconName,
8590
placeholder,
8691
onMenuOpen,
87-
tableFilter = false,
92+
variant,
93+
nowrapMenu,
8894
}: IDropdownWrapper) => {
8995
const wrapperClassNames = classnames(baseClass, className, {
90-
[`${baseClass}__table-filter`]: tableFilter,
96+
[`${baseClass}__table-filter`]: variant === "table-filter",
9197
[`${wrapperClassname}`]: !!wrapperClassname,
9298
});
9399

@@ -164,7 +170,8 @@ const DropdownWrapper = ({
164170
children,
165171
...props
166172
}: ValueContainerProps<CustomOptionType, false>) => {
167-
const iconToDisplay = iconName || (tableFilter ? "filter" : null);
173+
const iconToDisplay =
174+
iconName || (variant === "table-filter" ? "filter" : null);
168175

169176
return (
170177
components.ValueContainer && (
@@ -179,80 +186,150 @@ const DropdownWrapper = ({
179186
};
180187

181188
const customStyles: StylesConfig<CustomOptionType, false> = {
182-
container: (provided) => ({
183-
...provided,
184-
width: "100%",
185-
height: "40px",
186-
}),
187-
control: (provided, state) => ({
188-
...provided,
189-
display: "flex",
190-
flexDirection: "row",
191-
width: "100%",
192-
backgroundColor: COLORS["ui-off-white"],
193-
paddingLeft: "8px", // TODO: Update to match styleguide of (16px) when updating rest of UI (8px)
194-
paddingRight: "8px",
195-
cursor: "pointer",
196-
boxShadow: "none",
197-
borderRadius: "4px",
198-
borderColor: state.isFocused
199-
? COLORS["core-fleet-blue"]
200-
: COLORS["ui-fleet-black-10"],
201-
"&:hover": {
189+
container: (provided) => {
190+
const buttonVariantContainer = {
191+
borderRadius: "6px",
192+
"&:active": {
193+
backgroundColor: "rgba(25, 33, 71, 0.05)",
194+
},
195+
height: "38px",
196+
};
197+
198+
return {
199+
...provided,
200+
width: "100%",
201+
height: "40px",
202+
...(variant === "button" && buttonVariantContainer),
203+
};
204+
},
205+
206+
control: (provided, state) => {
207+
if (variant === "button")
208+
return {
209+
backgroundColor: "initial",
210+
borderColor: "none",
211+
display: "flex",
212+
flexDirection: "row",
213+
width: "max-content",
214+
padding: PADDING["pad-small"],
215+
border: 0,
216+
borderRadius: "6px",
217+
boxShadow: "none",
218+
cursor: "pointer",
219+
".dropdown-wrapper__indicator path": {
220+
stroke: COLORS["core-fleet-blue"],
221+
},
222+
"&:hover": {
223+
backgroundColor: "rgba(25, 33, 71, 0.05)",
224+
boxShadow: "none",
225+
".dropdown-wrapper__placeholder": {
226+
color: COLORS["core-vibrant-blue-over"],
227+
},
228+
".dropdown-wrapper__indicator path": {
229+
stroke: COLORS["core-vibrant-blue-over"],
230+
},
231+
},
232+
"&.react-select__control--is-focused": {
233+
backgroundColor: "rgba(25, 33, 71, 0.05)",
234+
},
235+
"&:active .dropdown-wrapper__indicator path": {
236+
stroke: COLORS["core-vibrant-blue-down"],
237+
},
238+
// TODO: Figure out a way to apply separate &:focus-visible styling
239+
// Currently only relying on &:focus styling for tabbing through app
240+
...(state.menuIsOpen && {
241+
".dropdown-wrapper__indicator svg": {
242+
transform: "rotate(180deg)",
243+
transition: "transform 0.25s ease",
244+
},
245+
}),
246+
...(variant === "button" && { height: "22px" }),
247+
};
248+
249+
return {
250+
...provided,
251+
display: "flex",
252+
flexDirection: "row",
253+
width: "100%",
254+
backgroundColor: COLORS["ui-off-white"],
255+
paddingLeft: "8px", // TODO: Update to match styleguide of (16px) when updating rest of UI (8px)
256+
paddingRight: "8px",
257+
cursor: "pointer",
202258
boxShadow: "none",
203-
borderColor: COLORS["core-fleet-blue"],
204-
".dropdown-wrapper__single-value": {
205-
color: COLORS["core-vibrant-blue-over"],
259+
borderRadius: "4px",
260+
borderColor: state.isFocused
261+
? COLORS["core-fleet-blue"]
262+
: COLORS["ui-fleet-black-10"],
263+
"&:hover": {
264+
boxShadow: "none",
265+
borderColor: COLORS["core-fleet-blue"],
266+
".dropdown-wrapper__single-value": {
267+
color: COLORS["core-vibrant-blue-over"],
268+
},
269+
".dropdown-wrapper__indicator path": {
270+
stroke: COLORS["core-vibrant-blue-over"],
271+
},
206272
},
207-
".dropdown-wrapper__indicator path": {
208-
stroke: COLORS["core-vibrant-blue-over"],
273+
// When tabbing
274+
// Relies on --is-focused for styling as &:focus-visible cannot be applied
275+
"&.react-select__control--is-focused": {
276+
".dropdown-wrapper__single-value": {
277+
color: COLORS["core-vibrant-blue-over"],
278+
},
279+
".dropdown-wrapper__indicator path": {
280+
stroke: COLORS["core-vibrant-blue-over"],
281+
},
209282
},
210283
".filter-icon path": {
211284
fill: COLORS["core-vibrant-blue-over"],
212285
},
213-
},
214-
// When tabbing
215-
// Relies on --is-focused for styling as &:focus-visible cannot be applied
216-
"&.dropdown-wrapper__control--is-focused": {
217-
".dropdown-wrapper__single-value": {
218-
color: COLORS["core-vibrant-blue-over"],
219-
},
220-
".dropdown-wrapper__indicator path": {
221-
stroke: COLORS["core-vibrant-blue-over"],
286+
...(state.isDisabled && {
287+
".dropdown-wrapper__single-value": {
288+
color: COLORS["ui-fleet-black-50"],
289+
},
290+
".dropdown-wrapper__indicator path": {
291+
stroke: COLORS["ui-fleet-black-50"],
292+
},
293+
".filter-icon path": {
294+
fill: COLORS["ui-fleet-black-50"],
295+
},
296+
}),
297+
"&:active": {
298+
".dropdown-wrapper__single-value": {
299+
color: COLORS["core-vibrant-blue-down"],
300+
},
301+
".dropdown-wrapper__indicator path": {
302+
stroke: COLORS["core-vibrant-blue-down"],
303+
},
304+
".filter-icon path": {
305+
fill: COLORS["core-vibrant-blue-down"],
306+
},
222307
},
223-
".filter-icon path": {
224-
fill: COLORS["core-vibrant-blue-over"],
225-
},
226-
},
227-
...(state.isDisabled && {
228-
".dropdown-wrapper__single-value": {
229-
color: COLORS["ui-fleet-black-50"],
230-
},
231-
".dropdown-wrapper__indicator path": {
232-
stroke: COLORS["ui-fleet-black-50"],
233-
},
234-
".filter-icon path": {
235-
fill: COLORS["ui-fleet-black-50"],
236-
},
237-
}),
238-
"&:active": {
239-
".dropdown-wrapper__single-value": {
240-
color: COLORS["core-vibrant-blue-down"],
241-
},
242-
".dropdown-wrapper__indicator path": {
243-
stroke: COLORS["core-vibrant-blue-down"],
244-
},
245-
".filter-icon path": {
246-
fill: COLORS["core-vibrant-blue-down"],
247-
},
248-
},
249-
...(state.menuIsOpen && {
250-
".dropdown-wrapper__indicator svg": {
251-
transform: "rotate(180deg)",
252-
transition: "transform 0.25s ease",
253-
},
254-
}),
255-
}),
308+
...(state.menuIsOpen && {
309+
".dropdown-wrapper__indicator svg": {
310+
transform: "rotate(180deg)",
311+
transition: "transform 0.25s ease",
312+
},
313+
}),
314+
};
315+
},
316+
placeholder: (provided, state) => {
317+
const buttonVariantPlaceholder = {
318+
color: state.isFocused
319+
? COLORS["core-vibrant-blue-over"]
320+
: COLORS["core-fleet-blue"],
321+
fontSize: "14px",
322+
fontWeight: "bold",
323+
lineHeight: "normal",
324+
paddingLeft: 0,
325+
marginTop: variant === "button" ? "-1px" : "1px", // TODO: Figure out vertical centering to not need pixel fix
326+
};
327+
328+
return {
329+
...provided,
330+
...(variant === "button" && buttonVariantPlaceholder),
331+
};
332+
},
256333
singleValue: (provided) => ({
257334
...provided,
258335
fontSize: "16px",
@@ -274,22 +351,28 @@ const DropdownWrapper = ({
274351
zIndex: 6,
275352
overflow: "hidden",
276353
border: 0,
277-
marginTop: 0,
354+
marginTop: variant === "button" ? "3px" : 0,
355+
left: 0,
278356
maxHeight: "none",
279357
position: "absolute",
280-
left: "0",
281358
animation: "fade-in 150ms ease-out",
359+
...(nowrapMenu && {
360+
width: "fit-content",
361+
left: "auto",
362+
right: "0",
363+
}),
282364
}),
283365
menuList: (provided) => ({
284366
...provided,
285367
padding: PADDING["pad-small"],
286368
maxHeight: "none",
369+
...(nowrapMenu && { width: "fit-content" }),
287370
}),
288371
valueContainer: (provided) => ({
289372
...provided,
290373
padding: 0,
291374
display: "flex",
292-
gap: PADDING["pad-small"],
375+
gap: PADDING[variant === "button" ? "pad-xsmall" : "pad-small"],
293376
}),
294377
option: (provided, state) => ({
295378
...provided,
@@ -303,6 +386,7 @@ const DropdownWrapper = ({
303386
backgroundColor: state.isDisabled
304387
? "transparent"
305388
: COLORS["ui-vibrant-blue-10"],
389+
cursor: state.isDisabled ? "not-allowed" : "pointer",
306390
},
307391
"&:active": {
308392
backgroundColor: state.isDisabled
@@ -320,11 +404,15 @@ const DropdownWrapper = ({
320404
flexDirection: "column",
321405
gap: "8px",
322406
width: "100%",
407+
whiteSpace: nowrapMenu ? "nowrap" : "normal",
323408
},
324409
".dropdown-wrapper__help-text": {
325410
fontSize: "12px",
326-
whiteSpace: "normal",
327-
color: COLORS["ui-fleet-black-75"],
411+
width: "100%",
412+
whiteSpace: nowrapMenu ? "nowrap" : "normal",
413+
color: state.isDisabled
414+
? COLORS["ui-fleet-black-50"]
415+
: COLORS["ui-fleet-black-75"],
328416
fontStyle: "italic",
329417
fontWeight: "normal",
330418
},

‎frontend/pages/SoftwarePage/SoftwareOS/SoftwareOSTable/SoftwareOSTable.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ const SoftwareOSTable = ({
240240
className={`${baseClass}__platform-dropdown`}
241241
options={PLATFORM_FILTER_OPTIONS}
242242
onChange={handlePlatformFilterDropdownChange}
243-
tableFilter
243+
variant="table-filter"
244244
/>
245245
);
246246
};

‎frontend/pages/SoftwarePage/SoftwareTitles/SoftwareTable/SoftwareTable.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ const SoftwareTable = ({
307307
newValue.value as ISoftwareDropdownFilterVal
308308
)
309309
}
310-
tableFilter
310+
variant="table-filter"
311311
/>
312312
</div>
313313
);

‎frontend/pages/SoftwarePage/SoftwareVulnerabilities/SoftwareVulnerabilitiesTable/SoftwareVulnerabilitiesTable.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ const SoftwareVulnerabilitiesTable = ({
245245
onChange={(newValue: SingleValue<CustomOptionType>) =>
246246
newValue && handleExploitedVulnFilterDropdownChange(newValue.value)
247247
}
248-
tableFilter
248+
variant="table-filter"
249249
/>
250250
);
251251
};

‎frontend/pages/SoftwarePage/_styles.scss

-4
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@
99
}
1010
}
1111

12-
&__manage-automations {
13-
padding: $pad-small $pad-medium;
14-
}
15-
1612
&__header {
1713
display: flex;
1814
align-items: center;

‎frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1468,7 +1468,7 @@ const ManageHostsPage = ({
14681468
className={`${baseClass}__status-filter`}
14691469
options={hostSelectStatuses}
14701470
onChange={handleStatusDropdownChange}
1471-
tableFilter
1471+
variant="table-filter"
14721472
/>
14731473
<LabelFilterSelect
14741474
className={`${baseClass}__label-filter-dropdown`}

‎frontend/pages/hosts/details/cards/Software/HostSoftwareTable/HostSoftwareTable.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ const HostSoftwareTable = ({
132132
className={`${baseClass}__software-filter`}
133133
options={DROPDOWN_OPTIONS}
134134
onChange={handleFilterDropdownChange}
135-
tableFilter
135+
variant="table-filter"
136136
/>
137137
);
138138
}, [handleFilterDropdownChange, hostSoftwareFilter]);

0 commit comments

Comments
 (0)
Please sign in to comment.