<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #46583 Figma: https://www.figma.com/design/0F1sw63SuYaKVWlcL7mnc6/-33441-Policies--Custom-targets-with-%22Include-any%22-and-%22Exclude-any%22?node-id=5303-5687&t=Fszpf83KhcZ7ViWh-0 # Checklist for submitter - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually Note: policy creation will fail if more than one inclusion/exclusion option is provided. This will be addressed as part of https://github.com/fleetdm/fleet/issues/46582 (we'll relax that check to make sure we can accept a combination). https://github.com/user-attachments/assets/9cfbcbca-54ce-4978-8248-7d550f18785b https://github.com/user-attachments/assets/16138fb1-da7c-4838-8819-f370bf7072c1 Empty state: <img width="850" height="628" alt="Screenshot 2026-06-10 at 11 30 45 AM" src="https://github.com/user-attachments/assets/141c4d26-e464-4ae1-b6ed-94d24a640e1c" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Add optional "exclude all" label targeting for policies (labels_exclude_all) and tab-based Include/Exclude targeting UI. * **Improvements** * In-modal navigation to add new labels; Custom targeting requires at least one selected label to enable Save. * Payloads now include include/exclude label fields when using Custom targeting. * **Tests** * Updated modal tests to cover exclude-tab label selection. * **Style** * Improved empty-state border styling in label selector. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
147 lines
2.9 KiB
SCSS
147 lines
2.9 KiB
SCSS
.target-label-selector {
|
|
width: 100%;
|
|
font-size: $x-small;
|
|
|
|
&__custom-label-chooser {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: $pad-medium;
|
|
|
|
// Hardcoded to fit all 3 custom target options (Exclude any, Include all,
|
|
// Include any) without scrolling
|
|
.Select-menu-outer {
|
|
max-height: 260px;
|
|
}
|
|
|
|
.Select-menu {
|
|
max-height: 244px;
|
|
}
|
|
}
|
|
|
|
&__checkboxes {
|
|
display: flex;
|
|
// 210px shows about 4.5 rows so the last row is half-visible, hinting
|
|
// there is more to scroll to
|
|
max-height: 210px;
|
|
flex-direction: column;
|
|
border-radius: $border-radius;
|
|
border: 1px solid $ui-fleet-black-10;
|
|
overflow-y: auto;
|
|
|
|
.loading-spinner {
|
|
margin: 69.5px auto;
|
|
}
|
|
}
|
|
|
|
&__label {
|
|
width: 100%;
|
|
padding: $pad-small $pad-medium;
|
|
box-sizing: border-box;
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
&:not(:last-child) {
|
|
border-bottom: 1px solid $ui-fleet-black-10;
|
|
}
|
|
|
|
.form-field--checkbox {
|
|
width: auto;
|
|
}
|
|
}
|
|
|
|
&__label-name {
|
|
padding-left: $pad-small;
|
|
}
|
|
|
|
// Tabbed include/exclude experience (TargetLabelSelector).
|
|
.react-tabs {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
// react-tabs unmounts inactive panels by default, so only the selected
|
|
// panel needs layout.
|
|
.react-tabs__tab-panel--selected {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: $pad-medium;
|
|
margin-top: 0;
|
|
}
|
|
|
|
// Truncate long label names in the tabbed checkbox list.
|
|
.react-tabs__tab-panel .fleet-checkbox {
|
|
height: 20px;
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
&__label {
|
|
max-width: 490px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
}
|
|
|
|
&__mode-toggle {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: $pad-small;
|
|
}
|
|
|
|
&__selected-badges {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: $pad-xsmall;
|
|
}
|
|
|
|
&__selected-badge {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: $pad-xxsmall;
|
|
padding: $pad-xxsmall $pad-small;
|
|
background-color: $ui-fleet-black-5;
|
|
border: 1px solid $ui-fleet-black-10;
|
|
border-radius: $border-radius;
|
|
font-size: $xx-small;
|
|
cursor: pointer;
|
|
color: $ui-fleet-black-75;
|
|
|
|
&:hover {
|
|
background-color: $ui-fleet-black-10;
|
|
border-color: $ui-fleet-black-25;
|
|
}
|
|
}
|
|
|
|
&__empty-state {
|
|
display: flex;
|
|
height: 187px;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: $pad-small;
|
|
justify-content: center;
|
|
text-align: center;
|
|
border: 1px solid $ui-fleet-black-10;
|
|
border-radius: $border-radius-large;
|
|
|
|
span {
|
|
color: $ui-fleet-black-75;
|
|
}
|
|
|
|
&--title {
|
|
font-size: $small;
|
|
font-weight: $bold;
|
|
color: $core-fleet-black !important;
|
|
}
|
|
|
|
&--description {
|
|
font-size: $xx-small;
|
|
margin-top: -$pad-xsmall;
|
|
}
|
|
|
|
.button {
|
|
margin-top: $pad-small;
|
|
}
|
|
}
|
|
}
|