Skip to content
Open
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
16 changes: 15 additions & 1 deletion packages/components/code-block/code-block.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,21 @@ When the code block should fill an entire container or screen, it is best to use

### Action panel

The action panel is located in the upper right corner of the block, visible on hover or when one of the buttons is focused, and stays fixed during scrolling. The component configuration determines which actions are available.
The action panel is located in the upper-right corner of the block and remains fixed while scrolling. The component configuration determines which actions are available.

The panel is always visible when tabs are shown, as well as on iOS and Android devices. When tabs are hidden, it appears on hover on other devices.

The `alwaysShowActionbar` attribute keeps the panel visible regardless of tabs or hover. It is disabled by default.

```html
<kbq-code-block [files]="files" canCopy alwaysShowActionbar />
```

To enable the option for all code blocks within an injector scope, use the provider:

```typescript
providers: [kbqCodeBlockDefaultOptionsProvider({ alwaysShowActionbar: true })];
```

#### Changing wrap mode

Expand Down
16 changes: 15 additions & 1 deletion packages/components/code-block/code-block.ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,21 @@ providers: [

### Панель действий

Панель действий расположена в правом верхнем углу блока, она видна по ховеру на блок или при фокусе на одну из кнопок, залипает при прокрутке. Конфигурация компонента определяет, какие действия будут доступны.
Панель действий расположена в правом верхнем углу блока и остается на месте при прокрутке. Конфигурация компонента определяет, какие действия будут доступны.

При видимых вкладках, а также на устройствах iOS и Android панель отображается постоянно. Если вкладки скрыты, на остальных устройствах панель появляется при наведении на блок.

Атрибут `alwaysShowActionbar` позволяет отображать панель постоянно независимо от наличия вкладок и наведения. По умолчанию он выключен.

```html
<kbq-code-block [files]="files" canCopy alwaysShowActionbar />
```

Чтобы включить настройку для всех блоков кода, зарегистрируйте провайдер:

```typescript
providers: [kbqCodeBlockDefaultOptionsProvider({ alwaysShowActionbar: true })];
```

#### Изменение режима переноса

Expand Down
121 changes: 120 additions & 1 deletion packages/components/code-block/code-block.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { KbqTabNavBar } from '@koobiq/components/tabs';
import { HLJSApi } from 'highlight.js';
import { Observable, Subject } from 'rxjs';
import { KBQ_CODE_BLOCK_FALLBACK_FILE_NAME, KbqCodeBlock, kbqCodeBlockLocaleConfigurationProvider } from './code-block';
import {
KBQ_CODE_BLOCK_FALLBACK_FILE_NAME,
KbqCodeBlock,
kbqCodeBlockDefaultOptionsProvider,
kbqCodeBlockLocaleConfigurationProvider
} from './code-block';
import {
KBQ_CODE_BLOCK_FALLBACK_FILE_LANGUAGE,
KbqCodeBlockHighlight,
Expand Down Expand Up @@ -92,6 +97,7 @@ const mockPreHeight = (debugElement: DebugElement, height: number): void => {
[canDownload]="canDownload"
[noBorder]="noBorder"
[hideTabs]="hideTabs"
[alwaysShowActionbar]="alwaysShowActionbar"
[canCopy]="canCopy"
[maxHeight]="maxHeight"
[(activeFileIndex)]="activeFileIndex"
Expand Down Expand Up @@ -126,10 +132,22 @@ class BaseCodeBlock {
activeFileIndex: number = 0;
noBorder: boolean = false;
hideTabs: boolean = false;
alwaysShowActionbar: boolean = false;
softWrap: boolean = false;
maxHeight: number | undefined = undefined;
}

@Component({
imports: [KbqCodeBlockModule],
template: `
<kbq-code-block hideTabs [files]="files" />
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
class CodeBlockWithDefaultOptions {
readonly files: KbqCodeBlockFile[] = [{ language: 'typescript', filename: 'main.ts', content: 'const value = 1;' }];
}

@Component({
imports: [KbqCodeBlockModule],
template: `
Expand Down Expand Up @@ -515,6 +533,107 @@ describe(KbqCodeBlock.name, () => {
expect(codeBlock.classes['kbq-code-block_show-actionbar']).toBeFalsy();
}));

it('should always show actionbar when alwaysShowActionbar is enabled', fakeAsync(() => {
const fixture = createComponent(BaseCodeBlock);
const { debugElement, componentInstance } = fixture;
const codeBlock = geCodeBlockDebugElement(debugElement);

componentInstance.hideTabs = true;
componentInstance.alwaysShowActionbar = true;
fixture.detectChanges();
expect(codeBlock.classes['kbq-code-block_show-actionbar']).toBeTruthy();

codeBlock.nativeElement.dispatchEvent(new MouseEvent('mouseleave'));
tick(HOVER_DEBOUNCE_TIME);
expect(codeBlock.classes['kbq-code-block_show-actionbar']).toBeTruthy();
}));

it('should use alwaysShowActionbar from default options', () => {
const { debugElement } = createComponent(CodeBlockWithDefaultOptions, [
kbqCodeBlockDefaultOptionsProvider({ alwaysShowActionbar: true })
]);
const codeBlock = geCodeBlockDebugElement(debugElement);

expect(codeBlock.componentInstance.alwaysShowActionbar()).toBeTruthy();
expect(codeBlock.classes['kbq-code-block_show-actionbar']).toBeTruthy();
});

it('should override alwaysShowActionbar from default options with input', () => {
const fixture = createComponent(BaseCodeBlock, [
kbqCodeBlockDefaultOptionsProvider({ alwaysShowActionbar: true })
]);
const { debugElement, componentInstance } = fixture;
const codeBlock = geCodeBlockDebugElement(debugElement);

componentInstance.hideTabs = true;
fixture.detectChanges();

expect(codeBlock.componentInstance.alwaysShowActionbar()).toBeFalsy();
expect(codeBlock.classes['kbq-code-block_show-actionbar']).toBeFalsy();
});

it('should not track hover when alwaysShowActionbar is enabled', () => {
const addEventListenerSpy = jest.spyOn(HTMLElement.prototype, 'addEventListener');

try {
TestBed.configureTestingModule({ imports: [BaseCodeBlock, NoopAnimationsModule] });
const fixture = TestBed.createComponent(BaseCodeBlock);

fixture.componentInstance.hideTabs = true;
fixture.componentInstance.alwaysShowActionbar = true;
fixture.detectChanges();

const codeBlockElement = geCodeBlockDebugElement(fixture.debugElement).nativeElement;
const hostHoverListeners = addEventListenerSpy.mock.calls.filter(
([eventName], index) =>
addEventListenerSpy.mock.contexts[index] === codeBlockElement &&
(eventName === 'mouseenter' || eventName === 'mouseleave')
);

expect(hostHoverListeners).toHaveLength(0);
} finally {
addEventListenerSpy.mockRestore();
}
});

it('should start tracking hover when alwaysShowActionbar is disabled', fakeAsync(() => {
TestBed.configureTestingModule({ imports: [BaseCodeBlock, NoopAnimationsModule] });
const fixture = TestBed.createComponent(BaseCodeBlock);

fixture.componentInstance.hideTabs = true;
fixture.componentInstance.alwaysShowActionbar = true;
fixture.detectChanges();

fixture.componentInstance.alwaysShowActionbar = false;
fixture.detectChanges();

const codeBlock = geCodeBlockDebugElement(fixture.debugElement);

codeBlock.nativeElement.dispatchEvent(new MouseEvent('mouseenter'));
tick(HOVER_DEBOUNCE_TIME);
fixture.detectChanges();
expect(codeBlock.classes['kbq-code-block_show-actionbar']).toBeTruthy();
}));

it('should restore hover behavior when alwaysShowActionbar is disabled', fakeAsync(() => {
const fixture = createComponent(BaseCodeBlock);
const { debugElement, componentInstance } = fixture;
const codeBlock = geCodeBlockDebugElement(debugElement);

componentInstance.hideTabs = true;
componentInstance.alwaysShowActionbar = true;
fixture.detectChanges();
expect(codeBlock.classes['kbq-code-block_show-actionbar']).toBeTruthy();

componentInstance.alwaysShowActionbar = false;
fixture.detectChanges();
expect(codeBlock.classes['kbq-code-block_show-actionbar']).toBeFalsy();

codeBlock.nativeElement.dispatchEvent(new MouseEvent('mouseenter'));
tick(HOVER_DEBOUNCE_TIME);
expect(codeBlock.classes['kbq-code-block_show-actionbar']).toBeTruthy();
}));

it('should stop tracking hover when hideTabs changes to false', fakeAsync(() => {
const fixture = createComponent(BaseCodeBlock);
const { debugElement, componentInstance } = fixture;
Expand Down
Loading