Skip to content
Merged
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
5 changes: 5 additions & 0 deletions packages/components-dev/dropdown/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
DropdownOverviewExample,
DropdownRecursiveTemplateExample,
DropdownWithFilterExample,
DropdownWithFooterExample,
DropdownXPositionExample
} from 'packages/docs-examples/components/dropdown';
import { DevThemeToggle } from '../theme-toggle';
Expand All @@ -33,9 +34,13 @@ import { DevThemeToggle } from '../theme-toggle';
DropdownOpenByArrowDownExample,
DropdownRecursiveTemplateExample,
DropdownWithFilterExample,
DropdownWithFooterExample,
DropdownXPositionExample
],
template: `
<dropdown-with-footer-example />
<hr />

<dropdown-with-filter-example />
<hr />

Expand Down
Binary file modified packages/components/code-block/__screenshots__/01-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/components/dropdown/__screenshots__/01-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/components/dropdown/__screenshots__/01-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions packages/components/dropdown/dropdown.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ import {
})
export class KbqDropdownStaticContent {}

/** Footer that is rendered below the dropdown options panel. */
@Directive({
selector: '[kbqDropdownFooter], kbq-dropdown-footer',
host: { class: 'kbq-dropdown-footer' }
})
export class KbqDropdownFooter {}
Comment thread
artembelik marked this conversation as resolved.
Comment thread
artembelik marked this conversation as resolved.

@Component({
selector: 'kbq-dropdown',
imports: [],
Expand Down
6 changes: 6 additions & 0 deletions packages/components/dropdown/dropdown.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ Add the `progress` attribute to `kbq-dropdown-item` to show a loading shimmer on

<!-- example(dropdown-item-progress) -->

### Footer

You can place auxiliary elements in the footer: [buttons](en/components/button), [links](en/components/link), hints.

<!-- example(dropdown-with-footer) -->

### Open by Arrow Down ↓

<!-- example(dropdown-open-by-arrow-down) -->
Expand Down
1 change: 1 addition & 0 deletions packages/components/dropdown/dropdown.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<div class="kbq-dropdown__content">
<ng-content />
</div>
<ng-content select="[kbqDropdownFooter], kbq-dropdown-footer" />
</div>
</ng-template>

Expand Down
8 changes: 5 additions & 3 deletions packages/components/dropdown/dropdown.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { KbqIconModule } from '@koobiq/components/icon';
import { KbqDropdownContent } from './dropdown-content.directive';
import { KbqDropdownItem } from './dropdown-item.component';
import { KBQ_DROPDOWN_SCROLL_STRATEGY_FACTORY_PROVIDER, KbqDropdownTrigger } from './dropdown-trigger.directive';
import { KbqDropdown, KbqDropdownStaticContent } from './dropdown.component';
import { KbqDropdown, KbqDropdownFooter, KbqDropdownStaticContent } from './dropdown.component';

@NgModule({
imports: [
Expand All @@ -14,15 +14,17 @@ import { KbqDropdown, KbqDropdownStaticContent } from './dropdown.component';
KbqDropdown,
KbqDropdownItem,
KbqDropdownTrigger,
KbqDropdownContent
KbqDropdownContent,
KbqDropdownFooter
],
providers: [KBQ_DROPDOWN_SCROLL_STRATEGY_FACTORY_PROVIDER],
exports: [
KbqDropdown,
KbqDropdownItem,
KbqDropdownTrigger,
KbqDropdownContent,
KbqDropdownStaticContent
KbqDropdownStaticContent,
KbqDropdownFooter
]
})
export class KbqDropdownModule {}
6 changes: 6 additions & 0 deletions packages/components/dropdown/dropdown.ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@

<!-- example(dropdown-item-progress) -->

### Футер

В нижнем колонтитуле можно разместить вспомогательные элементы: [кнопки](ru/components/button), [ссылки](ru/components/link), подсказки.

<!-- example(dropdown-with-footer) -->

### Открытие по стрелке вниз ↓

<!-- example(dropdown-open-by-arrow-down) -->
Expand Down
8 changes: 7 additions & 1 deletion packages/components/dropdown/dropdown.scss
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
border-radius: var(--kbq-dropdown-size-container-border-radius);

box-sizing: border-box;
padding: var(--kbq-size-xxs);

// Prevent users from interacting with the panel while it's animating. Note that
// people won't be able to click through it, because the overlay pane will catch the click.
Expand Down Expand Up @@ -86,6 +85,13 @@
.kbq-dropdown__content {
display: flex;
flex-direction: column;

padding: var(--kbq-size-xxs);
}

.kbq-dropdown-footer {
@extend %kbq-select-footer;
border-color: var(--kbq-line-contrast-less);
}

@include theme.kbq-dropdown-theme();
Expand Down
35 changes: 35 additions & 0 deletions packages/components/dropdown/dropdown.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,27 @@ describe('KbqDropdown', () => {
});
});

describe('footer', () => {
it('should project footer content into the panel, separate from the options content', () => {
const fixture = createComponent(FooterDropdown, [], []);

fixture.detectChanges();
fixture.componentInstance.trigger().open();
fixture.detectChanges();

const content = overlayContainerElement.querySelector('.kbq-dropdown__content') as HTMLElement;
const footer = overlayContainerElement.querySelector('.kbq-dropdown-footer') as HTMLElement;

expect(content.textContent).toContain('Item');
expect(footer).toBeTruthy();
expect(footer.textContent!.trim()).toBe('Footer content');

// The footer must render outside the scrollable options content, not inside it.
expect(content.contains(footer)).toBe(false);
expect(content.textContent).not.toContain('Footer content');
});
});

describe('nested dropdown', () => {
let fixture: ComponentFixture<NestedDropdown>;
let instance: NestedDropdown;
Expand Down Expand Up @@ -2068,6 +2089,20 @@ class SimpleDropdown {
backdropClass: string;
}

@Component({
imports: [KbqDropdownModule],
template: `
<button #triggerEl [kbqDropdownTriggerFor]="dropdown">Toggle dropdown</button>
<kbq-dropdown #dropdown="kbqDropdown">
<button kbq-dropdown-item>Item</button>
<kbq-dropdown-footer>Footer content</kbq-dropdown-footer>
</kbq-dropdown>
`
})
class FooterDropdown {
readonly trigger = viewChild.required(KbqDropdownTrigger);
}

@Component({
imports: [KbqDropdownModule],
template: `
Expand Down
5 changes: 4 additions & 1 deletion packages/components/dropdown/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ import { KbqDropdownModule } from './dropdown.module';

<button kbq-dropdown-item progress>In progress</button>
<button kbq-dropdown-item progress disabled>In progress and disabled</button>

<kbq-dropdown-footer>Dropdown footer</kbq-dropdown-footer>
</kbq-dropdown>

<kbq-dropdown #submenu="kbqDropdown">
Expand All @@ -89,6 +91,7 @@ import { KbqDropdownModule } from './dropdown.module';
Disabled item
<div class="kbq-dropdown-item__caption">Caption</div>
</button>
<kbq-dropdown-footer>Dropdown footer</kbq-dropdown-footer>
</kbq-dropdown>

<kbq-dropdown #submenu2="kbqDropdown">
Expand All @@ -105,7 +108,7 @@ import { KbqDropdownModule } from './dropdown.module';
styles: `
:host {
display: flex;
height: 500px;
height: 600px;
width: 1020px;
padding: var(--kbq-size-s);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { KbqButtonModule } from '@koobiq/components/button';
import { KbqDropdownModule } from '@koobiq/components/dropdown';
import { KbqIconModule } from '@koobiq/components/icon';
import { KbqLinkModule } from '@koobiq/components/link';

/**
* @title Dropdown with footer
*/
@Component({
selector: 'dropdown-with-footer-example',
imports: [KbqDropdownModule, KbqButtonModule, KbqLinkModule, KbqIconModule],
template: `
<button kbq-button [kbqDropdownTriggerFor]="appDropdown">
Trigger
<i kbq-icon="kbq-chevron-down-s_16"></i>
</button>

<kbq-dropdown #appDropdown="kbqDropdown">
<button kbq-dropdown-item>Menu item 1</button>
<button kbq-dropdown-item>Menu item 2</button>
<button kbq-dropdown-item>Menu item 3</button>
<button kbq-dropdown-item [kbqDropdownTriggerFor]="submenu">Menu item 4</button>
<button kbq-dropdown-item>Menu item 5</button>

<kbq-dropdown-footer>
<a
class="kbq-link_external"
href="https://github.com/koobiq/angular-components"
target="_blank"
rel="noopener noreferrer"
kbq-link
>
<span class="kbq-link__text">GitHub</span>
<i kbq-icon="kbq-north-east_16"></i>
</a>
</kbq-dropdown-footer>
</kbq-dropdown>

<kbq-dropdown #submenu="kbqDropdown">
<button kbq-dropdown-item>Menu item 1</button>
<button kbq-dropdown-item>Menu item 2</button>
<button kbq-dropdown-item>Menu item 3</button>
<kbq-dropdown-footer class="example-submenu-footer">
Footer text is multiline and wraps across lines
</kbq-dropdown-footer>
</kbq-dropdown>
`,
styles: `
:host {
height: 200px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

.example-submenu-footer {
font-size: var(--kbq-typography-text-compact-font-size);
color: var(--kbq-foreground-contrast-secondary);
}
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DropdownWithFooterExample {}
3 changes: 3 additions & 0 deletions packages/docs-examples/components/dropdown/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DropdownOpenByArrowDownExample } from './dropdown-open-by-arrow-down/dr
import { DropdownOverviewExample } from './dropdown-overview/dropdown-overview-example';
import { DropdownRecursiveTemplateExample } from './dropdown-recursive-template/dropdown-recursive-template-example';
import { DropdownWithFilterExample } from './dropdown-with-filter/dropdown-with-filter-example';
import { DropdownWithFooterExample } from './dropdown-with-footer/dropdown-with-footer-example';
import { DropdownXPositionExample } from './dropdown-x-position/dropdown-x-position-example';

export {
Expand All @@ -20,12 +21,14 @@ export {
DropdownOverviewExample,
DropdownRecursiveTemplateExample,
DropdownWithFilterExample,
DropdownWithFooterExample,
DropdownXPositionExample
};

const EXAMPLES = [
DropdownNestedExample,
DropdownOverviewExample,
DropdownWithFooterExample,
DropdownNavigationWrapExample,
DropdownDisabledExample,
DropdownItemProgressExample,
Expand Down
12 changes: 10 additions & 2 deletions tools/public_api_guard/components/dropdown.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export class KbqDropdown implements AfterContentInit, KbqDropdownPanel, OnInit,
get yPosition(): KbqDropdownPositionY;
set yPosition(value: KbqDropdownPositionY);
// (undocumented)
static ɵcmp: i0.ɵɵComponentDeclaration<KbqDropdown, "kbq-dropdown", ["kbqDropdown"], { "navigationWithWrap": { "alias": "navigationWithWrap"; "required": false; "isSignal": true; }; "xPosition": { "alias": "xPosition"; "required": false; }; "yPosition": { "alias": "yPosition"; "required": false; }; "overlapTriggerY": { "alias": "overlapTriggerY"; "required": false; }; "overlapTriggerX": { "alias": "overlapTriggerX"; "required": false; }; "hasBackdrop": { "alias": "hasBackdrop"; "required": false; }; "panelClass": { "alias": "class"; "required": false; }; "backdropClass": { "alias": "backdropClass"; "required": false; }; "panelWidth": { "alias": "panelWidth"; "required": false; "isSignal": true; }; "panelMinWidth": { "alias": "panelMinWidth"; "required": false; "isSignal": true; }; "panelMaxWidth": { "alias": "panelMaxWidth"; "required": false; "isSignal": true; }; }, { "closed": "closed"; }, ["search", "lazyContent", "items"], ["*", "[kbqDropdownStaticContent]"], true, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<KbqDropdown, "kbq-dropdown", ["kbqDropdown"], { "navigationWithWrap": { "alias": "navigationWithWrap"; "required": false; "isSignal": true; }; "xPosition": { "alias": "xPosition"; "required": false; }; "yPosition": { "alias": "yPosition"; "required": false; }; "overlapTriggerY": { "alias": "overlapTriggerY"; "required": false; }; "overlapTriggerX": { "alias": "overlapTriggerX"; "required": false; }; "hasBackdrop": { "alias": "hasBackdrop"; "required": false; }; "panelClass": { "alias": "class"; "required": false; }; "backdropClass": { "alias": "backdropClass"; "required": false; }; "panelWidth": { "alias": "panelWidth"; "required": false; "isSignal": true; }; "panelMinWidth": { "alias": "panelMinWidth"; "required": false; "isSignal": true; }; "panelMaxWidth": { "alias": "panelMaxWidth"; "required": false; "isSignal": true; }; }, { "closed": "closed"; }, ["search", "lazyContent", "items"], ["*", "[kbqDropdownFooter], kbq-dropdown-footer", "[kbqDropdownStaticContent]"], true, never>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<KbqDropdown, never>;
}
Expand Down Expand Up @@ -164,6 +164,14 @@ export interface KbqDropdownDefaultOptions {
yPosition: KbqDropdownPositionY;
}

// @public
export class KbqDropdownFooter {
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<KbqDropdownFooter, "[kbqDropdownFooter], kbq-dropdown-footer", never, {}, {}, never, never, true, never>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<KbqDropdownFooter, never>;
}

// @public
export class KbqDropdownItem implements KbqTitleTextRef, IFocusableOption, AfterViewInit, OnDestroy {
checkDisabled(event: Event): void;
Expand Down Expand Up @@ -210,7 +218,7 @@ export class KbqDropdownModule {
// (undocumented)
static ɵinj: i0.ɵɵInjectorDeclaration<KbqDropdownModule>;
// (undocumented)
static ɵmod: i0.ɵɵNgModuleDeclaration<KbqDropdownModule, never, [typeof i1.OverlayModule, typeof i2.KbqIconModule, typeof KbqDropdownStaticContent, typeof KbqDropdown, typeof KbqDropdownItem, typeof KbqDropdownTrigger, typeof KbqDropdownContent], [typeof KbqDropdown, typeof KbqDropdownItem, typeof KbqDropdownTrigger, typeof KbqDropdownContent, typeof KbqDropdownStaticContent]>;
static ɵmod: i0.ɵɵNgModuleDeclaration<KbqDropdownModule, never, [typeof i1.OverlayModule, typeof i2.KbqIconModule, typeof KbqDropdownStaticContent, typeof KbqDropdown, typeof KbqDropdownItem, typeof KbqDropdownTrigger, typeof KbqDropdownContent, typeof KbqDropdownFooter], [typeof KbqDropdown, typeof KbqDropdownItem, typeof KbqDropdownTrigger, typeof KbqDropdownContent, typeof KbqDropdownStaticContent, typeof KbqDropdownFooter]>;
}

// @public
Expand Down