Fix unresponsive copy button on instructions modal
This commit fixes the bug where the "Copy" button does not copy when
clicked on download instructions modal (on Linux and macOS).
This commit also introduces several improvements to the UI components
related to copy action and their interaction with the clipboard feature.
It adds more tests to avoid regression of the bugs and improves
maintainability, testability and adherence to Vue's reactive principles.
Changes include:
- Fix non-responsive copy button in the download instructions modal by
triggering a `click` event in `AppIcon.vue`.
- Improve `TheCodeButtons.vue`:
- Remove redundant `getCurrentCode` function.
- Separate components for each button for better separation of
concerns and higher testability.
- Use the `gap` property in the flexbox layout, replacing the less
explicit sibling combinator approach.
- Add `useClipboard` compositional hook for more idiomatic Vue approach
to interacting with the clipboard.
- Add `useCurrentCode` compositional hook to handle current code state
more effectively with unified logic.
- Abstract clipboard operations to an interface to isolate
responsibilities.
- Switch clipboard implementation to the `navigator.clipboard` API,
moving away from the deprecated `document.execCommand`.
- Move clipboard logic to the presentation layer to conform to
separation of concerns and domain-driven design principles.
- Improve `IconButton.vue` component to increase reusability with
consistent sizing.
This commit is contained in:
@@ -1,10 +1,19 @@
|
||||
import { ICodeChangedEvent } from '@/application/Context/State/Code/Event/ICodeChangedEvent';
|
||||
import { IApplicationCode } from '@/application/Context/State/Code/IApplicationCode';
|
||||
import { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||
import { EventSource } from '@/infrastructure/Events/EventSource';
|
||||
import { EventSourceStub } from './EventSourceStub';
|
||||
|
||||
export class ApplicationCodeStub implements IApplicationCode {
|
||||
public changed: IEventSource<ICodeChangedEvent> = new EventSource<ICodeChangedEvent>();
|
||||
public changed = new EventSourceStub<ICodeChangedEvent>();
|
||||
|
||||
public current = '';
|
||||
|
||||
public triggerCodeChange(event: ICodeChangedEvent): this {
|
||||
this.changed.notify(event);
|
||||
return this;
|
||||
}
|
||||
|
||||
public withCurrentCode(currentCode: string): this {
|
||||
this.current = currentCode;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ApplicationCodeStub } from './ApplicationCodeStub';
|
||||
import { CategoryStub } from './CategoryStub';
|
||||
|
||||
export class CategoryCollectionStateStub implements ICategoryCollectionState {
|
||||
public readonly code: IApplicationCode = new ApplicationCodeStub();
|
||||
public code: IApplicationCode = new ApplicationCodeStub();
|
||||
|
||||
public filter: IUserFilter = new UserFilterStub();
|
||||
|
||||
@@ -39,6 +39,11 @@ export class CategoryCollectionStateStub implements ICategoryCollectionState {
|
||||
return this;
|
||||
}
|
||||
|
||||
public withCode(code: IApplicationCode): this {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
public withOs(os: OperatingSystem): this {
|
||||
if (this.collection instanceof CategoryCollectionStub) {
|
||||
this.collection = this.collection.withOs(os);
|
||||
|
||||
14
tests/unit/shared/Stubs/ClipboardStub.ts
Normal file
14
tests/unit/shared/Stubs/ClipboardStub.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Clipboard } from '@/presentation/components/Shared/Hooks/Clipboard/Clipboard';
|
||||
import { StubWithObservableMethodCalls } from './StubWithObservableMethodCalls';
|
||||
|
||||
export class ClipboardStub
|
||||
extends StubWithObservableMethodCalls<Clipboard>
|
||||
implements Clipboard {
|
||||
public copyText(text: string): Promise<void> {
|
||||
this.registerMethodCall({
|
||||
methodName: 'copyText',
|
||||
args: [text],
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
26
tests/unit/shared/Stubs/CodeChangedEventStub.ts
Normal file
26
tests/unit/shared/Stubs/CodeChangedEventStub.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { ICodeChangedEvent } from '@/application/Context/State/Code/Event/ICodeChangedEvent';
|
||||
import { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
|
||||
import { IScript } from '@/domain/IScript';
|
||||
|
||||
export class CodeChangedEventStub implements ICodeChangedEvent {
|
||||
public code: string;
|
||||
|
||||
public addedScripts: readonly IScript[];
|
||||
|
||||
public removedScripts: readonly IScript[];
|
||||
|
||||
public changedScripts: readonly IScript[];
|
||||
|
||||
public isEmpty(): boolean {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public getScriptPositionInCode(): ICodePosition {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public withCode(code: string): this {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
17
tests/unit/shared/Stubs/UseClipboardStub.ts
Normal file
17
tests/unit/shared/Stubs/UseClipboardStub.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { useClipboard } from '@/presentation/components/Shared/Hooks/Clipboard/UseClipboard';
|
||||
import { Clipboard } from '@/presentation/components/Shared/Hooks/Clipboard/Clipboard';
|
||||
import { StubWithObservableMethodCalls } from './StubWithObservableMethodCalls';
|
||||
import { ClipboardStub } from './ClipboardStub';
|
||||
|
||||
export class UseClipboardStub
|
||||
extends StubWithObservableMethodCalls<ReturnType<typeof useClipboard>> {
|
||||
constructor(private readonly clipboard: Clipboard = new ClipboardStub()) {
|
||||
super();
|
||||
}
|
||||
|
||||
public get(): ReturnType<typeof useClipboard> {
|
||||
const { clipboard } = this;
|
||||
clipboard.copyText = clipboard.copyText.bind(clipboard);
|
||||
return this.clipboard;
|
||||
}
|
||||
}
|
||||
17
tests/unit/shared/Stubs/UseCurrentCodeStub.ts
Normal file
17
tests/unit/shared/Stubs/UseCurrentCodeStub.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { ref } from 'vue';
|
||||
import { useCurrentCode } from '@/presentation/components/Shared/Hooks/UseCurrentCode';
|
||||
|
||||
export class UseCurrentCodeStub {
|
||||
public currentCodeRef = ref('');
|
||||
|
||||
public withCurrentCode(code: string): this {
|
||||
this.currentCodeRef.value = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
public get(): ReturnType<typeof useCurrentCode> {
|
||||
return {
|
||||
currentCode: this.currentCodeRef,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user