Normalize tags (#48982)
--------- Co-authored-by: RachelElysia <71795832+RachelElysia@users.noreply.github.com>
This commit is contained in:
co-authored by
RachelElysia
parent
967d5e69b5
commit
0c1e75ae8a
@@ -0,0 +1 @@
|
||||
- Fixed low color contrast on the "Inherited" tag and unified the styling of tags (e.g. "Inherited," "API," "Patch," host filter chips, and host label pills) across the UI to match the design system.
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
IPolicyStats,
|
||||
} from "interfaces/policy";
|
||||
import CriticalPolicyBadge from "components/CriticalPolicyBadge";
|
||||
import PillBadge from "components/PillBadge";
|
||||
import Tag from "components/Tag";
|
||||
import { PATCH_TOOLTIP_CONTENT } from "components/SoftwareInstallPolicyBadges/SoftwareInstallPolicyBadges";
|
||||
|
||||
import usePickerSearch from "./usePickerSearch";
|
||||
@@ -110,12 +110,14 @@ const PolicyPicker = ({
|
||||
</span>
|
||||
{showCriticalBadge && <CriticalPolicyBadge />}
|
||||
{showPatchBadge && (
|
||||
<PillBadge tipContent={PATCH_TOOLTIP_CONTENT}>Patch</PillBadge>
|
||||
<Tag tooltip={PATCH_TOOLTIP_CONTENT} size="small">
|
||||
Patch
|
||||
</Tag>
|
||||
)}
|
||||
{showInheritedBadge && (
|
||||
<PillBadge tipContent="This policy runs on all hosts.">
|
||||
<Tag tooltip="This policy runs on all hosts." size="small">
|
||||
Inherited
|
||||
</PillBadge>
|
||||
</Tag>
|
||||
)}
|
||||
</div>
|
||||
</Command.Item>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { APP_CONTEXT_ALL_TEAMS_ID, ITeamSummary } from "interfaces/team";
|
||||
import queriesAPI, { IQueriesResponse } from "services/entities/queries";
|
||||
import { ISchedulableQuery } from "interfaces/schedulable_query";
|
||||
import Icon from "components/Icon";
|
||||
import PillBadge from "components/PillBadge";
|
||||
import Tag from "components/Tag";
|
||||
import TooltipWrapper from "components/TooltipWrapper";
|
||||
|
||||
import usePickerSearch from "./usePickerSearch";
|
||||
@@ -110,9 +110,9 @@ const ReportPicker = ({
|
||||
</TooltipWrapper>
|
||||
)}
|
||||
{showInheritedBadge && (
|
||||
<PillBadge tipContent="This report runs on all hosts.">
|
||||
<Tag tooltip="This report runs on all hosts." size="small">
|
||||
Inherited
|
||||
</PillBadge>
|
||||
</Tag>
|
||||
)}
|
||||
</div>
|
||||
</Command.Item>
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import React from "react";
|
||||
import classnames from "classnames";
|
||||
|
||||
import TooltipWrapper from "components/TooltipWrapper";
|
||||
|
||||
const baseClass = "pill-badge";
|
||||
|
||||
interface IPillBadgeProps {
|
||||
children: React.ReactNode;
|
||||
tipContent?: JSX.Element | string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const PillBadge = ({ children, tipContent, className }: IPillBadgeProps) => {
|
||||
const classNames = classnames(baseClass, className);
|
||||
|
||||
return (
|
||||
<div className={classNames}>
|
||||
<TooltipWrapper
|
||||
tipContent={tipContent}
|
||||
showArrow
|
||||
underline={false}
|
||||
position="top"
|
||||
tipOffset={12}
|
||||
delayInMs={300}
|
||||
>
|
||||
<span className={`${baseClass}__element`}>{children}</span>
|
||||
</TooltipWrapper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PillBadge;
|
||||
@@ -1,25 +0,0 @@
|
||||
.pill-badge {
|
||||
&__element {
|
||||
display: flex;
|
||||
height: 16px;
|
||||
padding: 0 4px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: $pad-small;
|
||||
font-weight: $bold;
|
||||
font-size: $xxx-small;
|
||||
color: $ui-fleet-black-75;
|
||||
line-height: 15px;
|
||||
border-radius: $border-radius;
|
||||
background: $ui-fleet-black-10;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
|
||||
@include tooltip5-arrow-styles;
|
||||
|
||||
.react-tooltip {
|
||||
@include tooltip-text;
|
||||
font-style: normal;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from "./PillBadge";
|
||||
@@ -4,7 +4,7 @@ import TooltipWrapper from "components/TooltipWrapper";
|
||||
import Icon from "components/Icon";
|
||||
|
||||
import { SoftwareInstallPolicyTypeSet } from "interfaces/software";
|
||||
import PillBadge from "components/PillBadge";
|
||||
import Tag from "components/Tag";
|
||||
|
||||
const baseClass = "software-install-policy-badges";
|
||||
|
||||
@@ -20,7 +20,9 @@ interface IPatchBadgesProps {
|
||||
|
||||
const SoftwareInstallPolicyBadges = ({ policyType }: IPatchBadgesProps) => {
|
||||
const renderPatchBadge = () => (
|
||||
<PillBadge tipContent={PATCH_TOOLTIP_CONTENT}>Patch</PillBadge>
|
||||
<Tag tooltip={PATCH_TOOLTIP_CONTENT} size="small">
|
||||
Patch
|
||||
</Tag>
|
||||
);
|
||||
|
||||
const renderAutomaticInstallBadge = () => (
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import React from "react";
|
||||
import { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import Icon from "components/Icon";
|
||||
|
||||
import Tag from "./Tag";
|
||||
import "../../index.scss";
|
||||
|
||||
const meta: Meta<typeof Tag> = {
|
||||
component: Tag,
|
||||
title: "Components/Tag",
|
||||
argTypes: {
|
||||
children: { control: "text" },
|
||||
size: { control: "radio", options: ["large", "small"] },
|
||||
disabled: { control: "boolean" },
|
||||
tooltip: { control: "text" },
|
||||
className: { control: "text" },
|
||||
onClick: { table: { disable: true } },
|
||||
onDismiss: { table: { disable: true } },
|
||||
},
|
||||
parameters: { controls: { expanded: true } },
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof Tag>;
|
||||
|
||||
export const Static: Story = {
|
||||
args: {
|
||||
children: "Inherited",
|
||||
},
|
||||
};
|
||||
|
||||
export const Small: Story = {
|
||||
args: {
|
||||
children: "Patch",
|
||||
size: "small",
|
||||
},
|
||||
};
|
||||
|
||||
export const WithTooltip: Story = {
|
||||
args: {
|
||||
children: "Inherited",
|
||||
tooltip: "This report runs on all hosts.",
|
||||
},
|
||||
};
|
||||
|
||||
export const WithIconAndText: Story = {
|
||||
args: {
|
||||
children: (
|
||||
<>
|
||||
<Icon size="small" name="warning" color="ui-fleet-black-75" />
|
||||
Report clipped
|
||||
</>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
export const Clickable: Story = {
|
||||
args: {
|
||||
type: "clickable",
|
||||
children: "iPadOS",
|
||||
onClick: () => undefined,
|
||||
},
|
||||
};
|
||||
|
||||
export const ClickableDisabled: Story = {
|
||||
args: {
|
||||
type: "clickable",
|
||||
children: "iPadOS",
|
||||
disabled: true,
|
||||
onClick: () => undefined,
|
||||
},
|
||||
};
|
||||
|
||||
export const Dismissible: Story = {
|
||||
args: {
|
||||
type: "dismissible",
|
||||
children: "Apple Silicon macOS hosts",
|
||||
onDismiss: () => undefined,
|
||||
},
|
||||
};
|
||||
|
||||
export const DismissibleWithTooltip: Story = {
|
||||
args: {
|
||||
type: "dismissible",
|
||||
children: "Apple Silicon macOS hosts",
|
||||
tooltip: "Hosts filtered to Apple Silicon Macs.",
|
||||
dismissLabel: "Apple Silicon macOS hosts",
|
||||
onDismiss: () => undefined,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,158 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import Tag from "./Tag";
|
||||
|
||||
describe("Tag", () => {
|
||||
it("renders static tags as non-interactive text", () => {
|
||||
render(<Tag>Inherited</Tag>);
|
||||
|
||||
expect(screen.getByText("Inherited")).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("defaults to the large size", () => {
|
||||
render(<Tag>Inherited</Tag>);
|
||||
|
||||
expect(screen.getByText("Inherited")).not.toHaveClass("tag--small");
|
||||
});
|
||||
|
||||
it("adds the small modifier class when size is set to small", () => {
|
||||
render(<Tag size="small">Inherited</Tag>);
|
||||
|
||||
expect(screen.getByText("Inherited")).toHaveClass("tag--small");
|
||||
});
|
||||
|
||||
it("does not wrap the tag in a tooltip when tooltip is omitted", () => {
|
||||
const { container } = render(<Tag>Inherited</Tag>);
|
||||
|
||||
expect(container.querySelector(".component__tooltip-wrapper")).toBeNull();
|
||||
});
|
||||
|
||||
it("wraps the tag in a tooltip when tooltip is provided", () => {
|
||||
const { container } = render(
|
||||
<Tag tooltip="This report runs on all hosts.">Inherited</Tag>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Inherited")).toBeInTheDocument();
|
||||
expect(
|
||||
container.querySelector(".component__tooltip-wrapper")
|
||||
).not.toBeNull();
|
||||
});
|
||||
|
||||
it("renders clickable tags as a button and calls onClick", async () => {
|
||||
const handler = jest.fn();
|
||||
render(
|
||||
<Tag type="clickable" onClick={handler}>
|
||||
iPadOS
|
||||
</Tag>
|
||||
);
|
||||
|
||||
const button = screen.getByRole("button", { name: "iPadOS" });
|
||||
await userEvent.click(button);
|
||||
expect(handler).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("disables the clickable tag's button when disabled is set", () => {
|
||||
render(
|
||||
<Tag type="clickable" onClick={() => undefined} disabled>
|
||||
iPadOS
|
||||
</Tag>
|
||||
);
|
||||
|
||||
expect(screen.getByRole("button", { name: "iPadOS" })).toBeDisabled();
|
||||
});
|
||||
|
||||
it("renders dismissible tags with a dismiss button and calls onDismiss", async () => {
|
||||
const handler = jest.fn();
|
||||
render(
|
||||
<Tag type="dismissible" onDismiss={handler}>
|
||||
Apple Silicon macOS hosts
|
||||
</Tag>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Apple Silicon macOS hosts")).toBeInTheDocument();
|
||||
const dismissButton = screen.getByRole("button");
|
||||
await userEvent.click(dismissButton);
|
||||
expect(handler).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("gives the dismiss button an accessible name even when dismissLabel is omitted", () => {
|
||||
render(
|
||||
<Tag type="dismissible" onDismiss={() => undefined}>
|
||||
Apple Silicon macOS hosts
|
||||
</Tag>
|
||||
);
|
||||
|
||||
expect(screen.getByRole("button", { name: "Dismiss" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("uses dismissLabel as the dismiss button's accessible name when provided", () => {
|
||||
render(
|
||||
<Tag
|
||||
type="dismissible"
|
||||
onDismiss={() => undefined}
|
||||
dismissLabel="Apple Silicon macOS hosts"
|
||||
>
|
||||
Apple Silicon macOS hosts
|
||||
</Tag>
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByRole("button", { name: "Apple Silicon macOS hosts" })
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render a native title tooltip on the dismiss button (aria-label carries the accessible name)", () => {
|
||||
render(
|
||||
<Tag type="dismissible" onDismiss={() => undefined}>
|
||||
Apple Silicon macOS hosts
|
||||
</Tag>
|
||||
);
|
||||
|
||||
expect(screen.getByRole("button", { name: "Dismiss" })).not.toHaveAttribute(
|
||||
"title"
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
case: "static",
|
||||
renderTag: () => render(<Tag className="custom-tag">Inherited</Tag>),
|
||||
label: "Inherited",
|
||||
},
|
||||
{
|
||||
case: "clickable",
|
||||
renderTag: () =>
|
||||
render(
|
||||
<Tag
|
||||
type="clickable"
|
||||
className="custom-tag"
|
||||
onClick={() => undefined}
|
||||
>
|
||||
iPadOS
|
||||
</Tag>
|
||||
),
|
||||
label: "iPadOS",
|
||||
},
|
||||
{
|
||||
case: "dismissible",
|
||||
renderTag: () =>
|
||||
render(
|
||||
<Tag
|
||||
type="dismissible"
|
||||
className="custom-tag"
|
||||
onDismiss={() => undefined}
|
||||
>
|
||||
Apple Silicon macOS hosts
|
||||
</Tag>
|
||||
),
|
||||
label: "Apple Silicon macOS hosts",
|
||||
},
|
||||
])("applies className to the root of a $case tag", ({ renderTag, label }) => {
|
||||
renderTag();
|
||||
|
||||
expect(screen.getByText(label).closest(".tag")).toHaveClass("custom-tag");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,108 @@
|
||||
import React from "react";
|
||||
import classnames from "classnames";
|
||||
|
||||
import Icon from "components/Icon";
|
||||
import TooltipWrapper from "components/TooltipWrapper";
|
||||
|
||||
const baseClass = "tag";
|
||||
|
||||
interface ITagBaseProps {
|
||||
children: React.ReactNode;
|
||||
/** Default: "large" (28px). Per design, use "small" (24px) sparingly. */
|
||||
size?: "large" | "small";
|
||||
className?: string;
|
||||
/** Wraps the tag in a tooltip that shows this content on hover */
|
||||
tooltip?: JSX.Element | string;
|
||||
}
|
||||
|
||||
interface IStaticTagProps extends ITagBaseProps {
|
||||
type?: "static";
|
||||
onClick?: never;
|
||||
onDismiss?: never;
|
||||
dismissLabel?: never;
|
||||
/** Static tags are non-interactive — disabled doesn't apply. */
|
||||
disabled?: never;
|
||||
}
|
||||
|
||||
interface IClickableTagProps extends ITagBaseProps {
|
||||
type: "clickable";
|
||||
onClick: () => void;
|
||||
onDismiss?: never;
|
||||
dismissLabel?: never;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
interface IDismissibleTagProps extends ITagBaseProps {
|
||||
type: "dismissible";
|
||||
onClick?: never;
|
||||
onDismiss: () => void;
|
||||
/** Accessible name for the dismiss button (screen readers only, no native tooltip). Defaults to "Dismiss". */
|
||||
dismissLabel?: string;
|
||||
/** Dismissible tags are always interactive — no production caller disables them. */
|
||||
disabled?: never;
|
||||
}
|
||||
|
||||
type ITagProps = IStaticTagProps | IClickableTagProps | IDismissibleTagProps;
|
||||
|
||||
const Tag = (props: ITagProps) => {
|
||||
const { children, className, tooltip } = props;
|
||||
|
||||
const classNames = classnames(baseClass, className, {
|
||||
[`${baseClass}--clickable`]: props.type === "clickable",
|
||||
[`${baseClass}--dismissible`]: props.type === "dismissible",
|
||||
[`${baseClass}--small`]: props.size === "small",
|
||||
});
|
||||
|
||||
let content: JSX.Element;
|
||||
|
||||
if (props.type === "clickable") {
|
||||
content = (
|
||||
<button
|
||||
type="button"
|
||||
className={classNames}
|
||||
disabled={props.disabled}
|
||||
onClick={props.onClick}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
} else if (props.type === "dismissible") {
|
||||
const dismissLabel = props.dismissLabel ?? "Dismiss";
|
||||
|
||||
content = (
|
||||
<span className={classNames}>
|
||||
<span className={`${baseClass}__label`}>{children}</span>
|
||||
<button
|
||||
type="button"
|
||||
className={`${baseClass}__dismiss`}
|
||||
onClick={props.onDismiss}
|
||||
aria-label={dismissLabel}
|
||||
>
|
||||
<Icon name="close" color="core-fleet-black" size="small" />
|
||||
</button>
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
content = <span className={classNames}>{children}</span>;
|
||||
}
|
||||
|
||||
if (!tooltip) {
|
||||
return content;
|
||||
}
|
||||
|
||||
return (
|
||||
<TooltipWrapper
|
||||
tipContent={tooltip}
|
||||
showArrow
|
||||
underline={false}
|
||||
position="top"
|
||||
tipOffset={12}
|
||||
delayShow={300}
|
||||
fixedPositionStrategy
|
||||
>
|
||||
{content}
|
||||
</TooltipWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Tag;
|
||||
@@ -0,0 +1,95 @@
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: $pad-small;
|
||||
height: 28px;
|
||||
padding: 0 $pad-small;
|
||||
border: 1px solid $ui-fleet-black-25;
|
||||
border-radius: $border-radius;
|
||||
font-size: $xx-small;
|
||||
font-weight: $bold;
|
||||
color: $ui-fleet-black-75;
|
||||
white-space: nowrap;
|
||||
box-sizing: border-box;
|
||||
|
||||
// per design, large (28px, the default set above) is used most often;
|
||||
// small is reserved for tight contexts like a table row
|
||||
&--small {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
&--clickable {
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
|
||||
// Interactive states only apply while enabled — disabled clickable tags
|
||||
// should look inert, not respond to hover/focus/press.
|
||||
&:not(:disabled) {
|
||||
&:hover {
|
||||
background-color: $ui-off-white;
|
||||
border-color: $ui-fleet-black-50;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
border-color: $core-fleet-black;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: $ui-fleet-black-5;
|
||||
border-color: $ui-fleet-black-50;
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
border-color: $ui-fleet-black-33;
|
||||
color: $ui-fleet-black-33;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
&--dismissible {
|
||||
padding: 0 $pad-xsmall 0 $pad-small;
|
||||
|
||||
// Only the dismiss button is interactive on a dismissible tag, so hover
|
||||
// and active feedback lives on the button itself (see &__dismiss below).
|
||||
// :focus-within stays here — when the button takes keyboard focus, the
|
||||
// outer tag border lights up so the focused element is easy to see.
|
||||
&:focus-within {
|
||||
border-color: $core-fleet-black;
|
||||
}
|
||||
}
|
||||
|
||||
&__label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: $pad-small;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__dismiss {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: $ui-off-white;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: $ui-fleet-black-5;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./Tag";
|
||||
@@ -10,7 +10,6 @@ export type ButtonVariant =
|
||||
| "default"
|
||||
| "alert"
|
||||
| "pill"
|
||||
| "grey-pill"
|
||||
| "link" // Looks like CustomLink with animated underline on hover
|
||||
| "secondary" // Bordered secondary button (off-white fill + border). The new preferred secondary — see #35329.
|
||||
| "subdued" // Low-emphasis borderless text + icon button. Not to be confused with a link.
|
||||
|
||||
@@ -196,32 +196,6 @@ $base-class: "button";
|
||||
}
|
||||
}
|
||||
|
||||
&--grey-pill {
|
||||
@include button-variant(
|
||||
$core-fleet-white,
|
||||
$ui-off-white,
|
||||
null,
|
||||
$inverse: true
|
||||
);
|
||||
color: $ui-fleet-black-75;
|
||||
border: 1px solid $ui-fleet-black-25;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
font-size: $xx-small;
|
||||
font-weight: $bold;
|
||||
padding: 0 $pad-small;
|
||||
height: 28px;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
border: 1px solid $ui-fleet-black-50;
|
||||
}
|
||||
|
||||
&:active {
|
||||
box-shadow: inset 2px 2px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
// Looks exactly like a CustomLink but is a <button>
|
||||
&--link {
|
||||
@include link;
|
||||
|
||||
+5
-4
@@ -11,7 +11,7 @@ import TooltipTruncatedTextCell from "components/TableContainer/DataTable/Toolti
|
||||
import ActionsDropdown from "components/ActionsDropdown";
|
||||
import CustomLink from "components/CustomLink";
|
||||
import TooltipWrapper from "components/TooltipWrapper";
|
||||
import PillBadge from "components/PillBadge";
|
||||
import Tag from "components/Tag";
|
||||
|
||||
interface IHeaderProps {
|
||||
column: {
|
||||
@@ -61,8 +61,8 @@ export interface ITeamUsersTableData {
|
||||
|
||||
export const renderApiUserIndicator = () => {
|
||||
return (
|
||||
<PillBadge
|
||||
tipContent={
|
||||
<Tag
|
||||
tooltip={
|
||||
<>
|
||||
This user was created using fleetctl and
|
||||
<br /> only has API access.{" "}
|
||||
@@ -74,9 +74,10 @@ export const renderApiUserIndicator = () => {
|
||||
/>
|
||||
</>
|
||||
}
|
||||
size="small"
|
||||
>
|
||||
API
|
||||
</PillBadge>
|
||||
</Tag>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+6
-3
@@ -11,7 +11,7 @@ import { isEmpty } from "lodash";
|
||||
|
||||
import TableContainer from "components/TableContainer";
|
||||
import TextCell from "components/TableContainer/DataTable/TextCell/TextCell";
|
||||
import PillBadge from "components/PillBadge";
|
||||
import Tag from "components/Tag";
|
||||
import Button from "components/buttons/Button";
|
||||
import InputFieldWithIcon from "components/forms/fields/InputFieldWithIcon/InputFieldWithIcon";
|
||||
import DataError from "components/DataError";
|
||||
@@ -81,9 +81,12 @@ const NameCell = (cellProps: ICellProps) => {
|
||||
<span className={`${baseClass}__name-cell`}>
|
||||
<TextCell value={cellProps.cell.value} className="" />
|
||||
{deprecated && (
|
||||
<PillBadge tipContent="This endpoint is deprecated and may be removed in a future version.">
|
||||
<Tag
|
||||
tooltip="This endpoint is deprecated and may be removed in a future version."
|
||||
size="small"
|
||||
>
|
||||
Deprecated
|
||||
</PillBadge>
|
||||
</Tag>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -5,7 +5,7 @@ import StatusIndicator from "components/StatusIndicator";
|
||||
import TextCell from "components/TableContainer/DataTable/TextCell/TextCell";
|
||||
import TooltipTruncatedTextCell from "components/TableContainer/DataTable/TooltipTruncatedTextCell";
|
||||
import TooltipWrapper from "components/TooltipWrapper";
|
||||
import PillBadge from "components/PillBadge";
|
||||
import Tag from "components/Tag";
|
||||
import { IInvite } from "interfaces/invite";
|
||||
import { IUser, UserRole } from "interfaces/user";
|
||||
import { IDropdownOption } from "interfaces/dropdownOption";
|
||||
@@ -23,7 +23,11 @@ import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
|
||||
import ActionsDropdown from "../../../../../components/ActionsDropdown";
|
||||
|
||||
const renderApiUserIndicator = () => {
|
||||
return <PillBadge tipContent="This user only has API access.">API</PillBadge>;
|
||||
return (
|
||||
<Tag tooltip="This user only has API access." size="small">
|
||||
API
|
||||
</Tag>
|
||||
);
|
||||
};
|
||||
|
||||
interface IHeaderProps {
|
||||
|
||||
@@ -3,7 +3,7 @@ import classnames from "classnames";
|
||||
|
||||
import { useCheckTruncatedElement } from "hooks/useCheckTruncatedElement";
|
||||
|
||||
import Button from "components/buttons/Button";
|
||||
import Tag from "components/Tag";
|
||||
import Icon from "components/Icon";
|
||||
import { IconNames } from "components/icons";
|
||||
import TooltipWrapper from "components/TooltipWrapper";
|
||||
@@ -26,9 +26,7 @@ const FilterPill = ({
|
||||
onClear,
|
||||
}: IFilterPillProps) => {
|
||||
const baseClasses = classnames(baseClass, className);
|
||||
const labelClasses = classnames(`${baseClass}__label`, {
|
||||
tooltip: tooltipDescription !== undefined && tooltipDescription !== "",
|
||||
});
|
||||
const labelClasses = `${baseClass}__label`;
|
||||
|
||||
const pillText = useRef(null);
|
||||
const isTruncated = useCheckTruncatedElement(pillText);
|
||||
@@ -63,22 +61,15 @@ const FilterPill = ({
|
||||
role="status"
|
||||
aria-label={`hosts filtered by ${label}`}
|
||||
>
|
||||
<>
|
||||
<span>
|
||||
<div className={labelClasses}>
|
||||
{icon && <Icon name={icon} />}
|
||||
{labelWithTooltip}
|
||||
<Button
|
||||
className={`${baseClass}__clear-filter`}
|
||||
onClick={onClear}
|
||||
variant="subdued"
|
||||
title={label}
|
||||
>
|
||||
<Icon name="close" color="core-fleet-black" size="small" />
|
||||
</Button>
|
||||
</div>
|
||||
</span>
|
||||
</>
|
||||
<Tag
|
||||
type="dismissible"
|
||||
className={labelClasses}
|
||||
onDismiss={onClear}
|
||||
dismissLabel={`Remove ${label} filter`}
|
||||
>
|
||||
{icon && <Icon name={icon} />}
|
||||
{labelWithTooltip}
|
||||
</Tag>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,28 +1,11 @@
|
||||
.filter-pill {
|
||||
// Strip the inline baseline gap so the pill vertically centers alongside
|
||||
// sibling dropdowns in `.hosts-filter-block__labels-active-filter-wrap`.
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
&__label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 6px 12px;
|
||||
border: 1px solid $ui-fleet-black-25;
|
||||
border-radius: $border-radius;
|
||||
overflow: hidden;
|
||||
box-shadow: none;
|
||||
color: $core-fleet-black;
|
||||
font-size: $xx-small;
|
||||
font-weight: $bold;
|
||||
cursor: default;
|
||||
gap: $small;
|
||||
|
||||
button {
|
||||
margin: -$pad-small; // Keep button padding from affecting parent padding
|
||||
}
|
||||
|
||||
.premium-icon-tip {
|
||||
.premium-feature-icon {
|
||||
position: relative;
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__tooltip-text {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { IconNames } from "components/icons";
|
||||
import InfoBanner from "components/InfoBanner";
|
||||
import ActionsDropdown from "components/ActionsDropdown";
|
||||
import { IDropdownOption } from "interfaces/dropdownOption";
|
||||
import PillBadge from "components/PillBadge";
|
||||
import Tag from "components/Tag";
|
||||
import TooltipTruncatedText from "components/TooltipTruncatedText";
|
||||
import { Colors } from "styles/var/colors";
|
||||
|
||||
@@ -154,10 +154,11 @@ const HostReportCard = ({
|
||||
className={`${baseClass}__view-full-report`}
|
||||
variant="subdued"
|
||||
size="small"
|
||||
icon="chevron-right"
|
||||
iconPosition="right"
|
||||
onClick={() => onShowDetails(report)}
|
||||
>
|
||||
View full report
|
||||
<Icon name="chevron-right" color={ICON_COLOR} />
|
||||
</Button>
|
||||
</ReportBanner>
|
||||
);
|
||||
@@ -180,13 +181,10 @@ const HostReportCard = ({
|
||||
</div>
|
||||
<div className={`${baseClass}__header-right`}>
|
||||
{report.report_clipped && (
|
||||
<PillBadge
|
||||
className={`${baseClass}__clipped-badge`}
|
||||
tipContent="This report has paused saving results. If automations are enabled, results are still sent to your log destination."
|
||||
>
|
||||
<Tag tooltip="This report has paused saving results. If automations are enabled, results are still sent to your log destination.">
|
||||
<Icon size="small" name="warning" color={ICON_COLOR} />
|
||||
Report clipped
|
||||
</PillBadge>
|
||||
</Tag>
|
||||
)}
|
||||
<ActionsDropdown
|
||||
options={actionOptions}
|
||||
|
||||
@@ -121,16 +121,6 @@
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
&__clipped-badge {
|
||||
.pill-badge__element {
|
||||
background: transparent;
|
||||
font-size: $xx-small;
|
||||
color: $ui-fleet-black-75;
|
||||
border: 1px solid $ui-fleet-black-25;
|
||||
padding: $pad-small;
|
||||
}
|
||||
}
|
||||
|
||||
&__data-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
import Button from "components/buttons/Button";
|
||||
import Tag from "components/Tag";
|
||||
import { ILabel } from "interfaces/label";
|
||||
import classnames from "classnames";
|
||||
|
||||
@@ -29,13 +29,13 @@ const Labels = ({
|
||||
.map((label: ILabel) => {
|
||||
return (
|
||||
<li className={`${baseClass}__list-item`} key={label.id}>
|
||||
<Button
|
||||
<Tag
|
||||
type="clickable"
|
||||
onClick={() => onLabelClick(label)}
|
||||
variant="pill"
|
||||
className={`${baseClass}__list-button`}
|
||||
>
|
||||
<TooltipTruncatedText value={label.name} />
|
||||
</Button>
|
||||
</Tag>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -17,15 +17,14 @@
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.button,
|
||||
.children-wrapper {
|
||||
.tag {
|
||||
max-width: 100%;
|
||||
.__react_component_tooltip {
|
||||
font-weight: $regular;
|
||||
}
|
||||
}
|
||||
|
||||
.button.host-labels-card__list-button {
|
||||
.tag.host-labels-card__list-button {
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,10 +163,6 @@
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.inherited-badge {
|
||||
overflow: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -26,7 +26,7 @@ import sortUtils from "utilities/sort";
|
||||
import { DEFAULT_EMPTY_CELL_VALUE, PolicyResponse } from "utilities/constants";
|
||||
|
||||
import CriticalPolicyBadge from "components/CriticalPolicyBadge";
|
||||
import PillBadge from "components/PillBadge";
|
||||
import Tag from "components/Tag";
|
||||
import { PATCH_TOOLTIP_CONTENT } from "components/SoftwareInstallPolicyBadges/SoftwareInstallPolicyBadges";
|
||||
import { getConditionalSelectHeaderCheckboxProps } from "components/TableContainer/utilities/config_utils";
|
||||
import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper";
|
||||
@@ -293,14 +293,14 @@ const generateTableHeaders = (
|
||||
<>
|
||||
{isPremiumTier && critical && <CriticalPolicyBadge />}
|
||||
{type === "patch" && (
|
||||
<PillBadge tipContent={PATCH_TOOLTIP_CONTENT}>
|
||||
<Tag tooltip={PATCH_TOOLTIP_CONTENT} size="small">
|
||||
Patch
|
||||
</PillBadge>
|
||||
</Tag>
|
||||
)}
|
||||
{viewingTeamPolicies && team_id === null && (
|
||||
<PillBadge tipContent="This policy runs on all hosts.">
|
||||
<Tag tooltip="This policy runs on all hosts." size="small">
|
||||
Inherited
|
||||
</PillBadge>
|
||||
</Tag>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -94,10 +94,6 @@
|
||||
tbody {
|
||||
.name__cell {
|
||||
width: auto;
|
||||
|
||||
.inherited-badge {
|
||||
overflow: initial;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $break-md) {
|
||||
|
||||
+3
-3
@@ -36,7 +36,7 @@ import PlatformCell from "components/TableContainer/DataTable/PlatformCell";
|
||||
import TextCell from "components/TableContainer/DataTable/TextCell";
|
||||
import PerformanceImpactCell from "components/TableContainer/DataTable/PerformanceImpactCell";
|
||||
import TooltipWrapper from "components/TooltipWrapper";
|
||||
import PillBadge from "components/PillBadge";
|
||||
import Tag from "components/Tag";
|
||||
import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper";
|
||||
import { HumanTimeDiffWithDateTip } from "components/HumanTimeDiffWithDateTip";
|
||||
|
||||
@@ -171,9 +171,9 @@ const generateColumnConfigs = ({
|
||||
{viewingTeamScope &&
|
||||
// inherited
|
||||
team_id !== currentTeamId && (
|
||||
<PillBadge tipContent="This report runs on all hosts.">
|
||||
<Tag tooltip="This report runs on all hosts." size="small">
|
||||
Inherited
|
||||
</PillBadge>
|
||||
</Tag>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user