feat: add aria label, fix test, transition constant for better testing
This commit is contained in:
@@ -22,9 +22,9 @@
|
|||||||
<CropInline />
|
<CropInline />
|
||||||
|
|
||||||
<div class="crop-controls">
|
<div class="crop-controls">
|
||||||
<Button label="Загрузить" icon={Upload} type="secondary" />
|
<Button label="Загрузить" ariaLabel="Upload" icon={Upload} type="secondary" />
|
||||||
<Button label="Редактировать" icon={Reset} type="outline" />
|
<Button label="Редактировать" ariaLabel="Edit" icon={Reset} type="outline" />
|
||||||
<Button label="Сбросить" icon={Pencil} type="outline" />
|
<Button label="Сбросить" ariaLabel="Reset" icon={Pencil} type="outline" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SettingsGrid>
|
<SettingsGrid>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Button from "$components/ui/Button.svelte";
|
import Button from "$components/ui/Button.svelte";
|
||||||
import type { SlideDirectionType } from "$lib/constants";
|
import type { SlideDirectionType } from "$lib/constants";
|
||||||
import { PANEL_SETTINGS } from "$lib/constants";
|
|
||||||
import ChevronLeft from "~icons/lucide/chevron-left";
|
import ChevronLeft from "~icons/lucide/chevron-left";
|
||||||
import ChevronRight from "~icons/lucide/chevron-right";
|
import ChevronRight from "~icons/lucide/chevron-right";
|
||||||
|
|
||||||
@@ -25,13 +24,11 @@
|
|||||||
if (current < max - 1) current++;
|
if (current < max - 1) current++;
|
||||||
direction = "next";
|
direction = "next";
|
||||||
}
|
}
|
||||||
|
|
||||||
let xDirection = $derived(direction == "next" ? PANEL_SETTINGS.PANEL_WIDTH : -PANEL_SETTINGS.PANEL_WIDTH);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Button icon={ChevronLeft} type="mini" onclick={prev} disabled={isFirst} />
|
<Button icon={ChevronLeft} ariaLabel="Previous slide" type="mini" onclick={prev} disabled={isFirst} />
|
||||||
<span class="panel-indicator">{current + 1} / {max}</span>
|
<span class="panel-indicator">{current + 1} / {max}</span>
|
||||||
<Button icon={ChevronRight} type="mini" onclick={next} disabled={isLast} />
|
<Button icon={ChevronRight} ariaLabel="Next slide" type="mini" onclick={next} disabled={isLast} />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.panel-indicator {
|
.panel-indicator {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import Card from "$components/layout/Card.svelte";
|
import Card from "$components/layout/Card.svelte";
|
||||||
import Badge from "$components/ui/Badge.svelte";
|
import Badge from "$components/ui/Badge.svelte";
|
||||||
import Button from "$components/ui/Button.svelte";
|
import Button from "$components/ui/Button.svelte";
|
||||||
import { PANEL_SETTINGS, type SlideDirectionType } from "$lib/constants";
|
import { PANEL_SETTINGS, TRANSITION_DURATION, type SlideDirectionType } from "$lib/constants";
|
||||||
import { downloadService, type DownloadItem } from "$services/downloadService";
|
import { downloadService, type DownloadItem } from "$services/downloadService";
|
||||||
import { konvaAllStagesState } from "$states/konvaAllStages.svelte";
|
import { konvaAllStagesState } from "$states/konvaAllStages.svelte";
|
||||||
import { konvaStageState } from "$states/konvaStage.svelte";
|
import { konvaStageState } from "$states/konvaStage.svelte";
|
||||||
@@ -62,8 +62,8 @@
|
|||||||
{@const text = textsState?.texts[current]?.text}
|
{@const text = textsState?.texts[current]?.text}
|
||||||
<div
|
<div
|
||||||
class="konva-wrapper"
|
class="konva-wrapper"
|
||||||
in:fly={{ x: xDirection, duration: 300 }}
|
in:fly={{ x: xDirection, duration: TRANSITION_DURATION }}
|
||||||
out:fly={{ x: -xDirection, duration: 300 }}
|
out:fly={{ x: -xDirection, duration: TRANSITION_DURATION }}
|
||||||
>
|
>
|
||||||
<Preview {text} bind:stage={konvaStageState.stage} />
|
<Preview {text} bind:stage={konvaStageState.stage} />
|
||||||
</div>
|
</div>
|
||||||
@@ -77,8 +77,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel-actions">
|
<div class="panel-actions">
|
||||||
<Button label="Скачать всё" icon={Download} onclick={downloadAll} />
|
<Button label="Скачать всё" ariaLabel="Download all" icon={Download} onclick={downloadAll} />
|
||||||
<Button label="Скачать" type="outline" icon={Download} onclick={downloadCurrent} />
|
<Button label="Скачать" ariaLabel="Download current" type="outline" icon={Download} onclick={downloadCurrent} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Button from "$components/ui/Button.svelte";
|
import Button from "$components/ui/Button.svelte";
|
||||||
|
import { TRANSITION_DURATION } from "$lib/constants";
|
||||||
import { fly } from "svelte/transition";
|
import { fly } from "svelte/transition";
|
||||||
import Cross from "~icons/lucide/x";
|
import Cross from "~icons/lucide/x";
|
||||||
|
|
||||||
@@ -12,10 +13,10 @@
|
|||||||
let { text = $bindable(), id, ondelete }: Props = $props();
|
let { text = $bindable(), id, ondelete }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="text-item" transition:fly={{ x: 600, duration: 300 }}>
|
<li class="text-item" transition:fly={{ x: 600, duration: TRANSITION_DURATION }}>
|
||||||
<input type="text" bind:value={text} />
|
<input type="text" bind:value={text} />
|
||||||
<Button icon={Cross} type="danger" onclick={() => ondelete(id)} />
|
<Button icon={Cross} ariaLabel="Delete" type="danger" onclick={() => ondelete(id)} />
|
||||||
</div>
|
</li>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.text-item {
|
.text-item {
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
text: string;
|
text: string;
|
||||||
|
ariaLabel: string;
|
||||||
onenter: () => void;
|
onenter: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { text = $bindable(), onenter }: Props = $props();
|
let { text = $bindable(), onenter, ariaLabel }: Props = $props();
|
||||||
|
|
||||||
function handleKeyboard(event: KeyboardEvent) {
|
function handleKeyboard(event: KeyboardEvent) {
|
||||||
if (event.key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
@@ -13,7 +14,14 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<input bind:value={text} class="text-input" type="text" placeholder="Введите текст..." onkeydown={handleKeyboard} />
|
<input
|
||||||
|
aria-label={ariaLabel}
|
||||||
|
bind:value={text}
|
||||||
|
class="text-input"
|
||||||
|
type="text"
|
||||||
|
placeholder="Введите текст..."
|
||||||
|
onkeydown={handleKeyboard}
|
||||||
|
/>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.text-input {
|
.text-input {
|
||||||
|
|||||||
@@ -21,14 +21,14 @@
|
|||||||
|
|
||||||
<Card title="Тексты панелей">
|
<Card title="Тексты панелей">
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<TextInput bind:text onenter={addText} />
|
<TextInput ariaLabel="Input new text" bind:text onenter={addText} />
|
||||||
<Button icon={Plus} onclick={addText} />
|
<Button icon={Plus} ariaLabel="Add text" onclick={addText} />
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
<div class="texts-list">
|
<ul class="texts-list">
|
||||||
{#each textsState.texts as { text, id } (id)}
|
{#each textsState.texts as { text, id } (id)}
|
||||||
<TextInlineEdit {id} {text} ondelete={() => deleteText(id)} />
|
<TextInlineEdit {id} {text} ondelete={() => deleteText(id)} />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</ul>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -4,13 +4,14 @@
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
icon: Component;
|
icon: Component;
|
||||||
|
ariaLabel: string;
|
||||||
onclick?: MouseEventHandler<HTMLButtonElement>;
|
onclick?: MouseEventHandler<HTMLButtonElement>;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
label?: string;
|
label?: string;
|
||||||
type?: "primary" | "secondary" | "danger" | "outline" | "mini";
|
type?: "primary" | "secondary" | "danger" | "outline" | "mini";
|
||||||
}
|
}
|
||||||
|
|
||||||
let { icon: Icon, onclick = () => {}, disabled = false, label = "", type = "primary" }: Props = $props();
|
let { icon: Icon, ariaLabel, onclick = () => {}, disabled = false, label = "", type = "primary" }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
class:btn-mini={type === "mini"}
|
class:btn-mini={type === "mini"}
|
||||||
{disabled}
|
{disabled}
|
||||||
{onclick}
|
{onclick}
|
||||||
|
aria-label={ariaLabel}
|
||||||
>
|
>
|
||||||
<Icon />
|
<Icon />
|
||||||
{label}
|
{label}
|
||||||
|
|||||||
@@ -65,3 +65,5 @@ export const TextAlign = {
|
|||||||
|
|
||||||
export type TextAlignType = (typeof TextAlign)[keyof typeof TextAlign];
|
export type TextAlignType = (typeof TextAlign)[keyof typeof TextAlign];
|
||||||
export const DEFAULT_TEXT_ALIGN: TextAlignType = TextAlign.CENTER;
|
export const DEFAULT_TEXT_ALIGN: TextAlignType = TextAlign.CENTER;
|
||||||
|
|
||||||
|
export const TRANSITION_DURATION = import.meta.env.MODE === "test" ? 0 : 300;
|
||||||
|
|||||||
@@ -75,6 +75,11 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(ul) {
|
||||||
|
list-style: none;
|
||||||
|
text-indent: 0;
|
||||||
|
}
|
||||||
|
|
||||||
:global(body) {
|
:global(body) {
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", sans-serif;
|
||||||
background: var(--bg-primary);
|
background: var(--bg-primary);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ describe("TextInput.svelte", () => {
|
|||||||
props: {
|
props: {
|
||||||
text: "Test text",
|
text: "Test text",
|
||||||
onenter: vi.fn(),
|
onenter: vi.fn(),
|
||||||
|
ariaLabel: "Test input",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ describe("TextInput.svelte", () => {
|
|||||||
props: {
|
props: {
|
||||||
text: "Initial",
|
text: "Initial",
|
||||||
onenter: vi.fn(),
|
onenter: vi.fn(),
|
||||||
|
ariaLabel: "Test input",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -37,6 +39,7 @@ describe("TextInput.svelte", () => {
|
|||||||
props: {
|
props: {
|
||||||
text: "Test",
|
text: "Test",
|
||||||
onenter,
|
onenter,
|
||||||
|
ariaLabel: "Test input",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -53,6 +56,7 @@ describe("TextInput.svelte", () => {
|
|||||||
props: {
|
props: {
|
||||||
text: "Test",
|
text: "Test",
|
||||||
onenter,
|
onenter,
|
||||||
|
ariaLabel: "Test input",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,194 @@
|
|||||||
import TextManager from "$components/text/TextManager.svelte";
|
import TextManager from "$components/text/TextManager.svelte";
|
||||||
import { textsState } from "$states/texts.svelte";
|
import { textsState } from "$states/texts.svelte";
|
||||||
import { render, screen } from "@testing-library/svelte";
|
import { fireEvent, render, screen, waitFor } from "@testing-library/svelte";
|
||||||
import { beforeEach, describe, expect, it } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
describe("TextManager.svelte", () => {
|
describe("TextManager.svelte", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
while (textsState.texts.length > 0) {
|
textsState.clear();
|
||||||
textsState.texts.pop();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render without crashing", () => {
|
describe("Rendering", () => {
|
||||||
|
it("should render card with title, input and add button", () => {
|
||||||
render(TextManager);
|
render(TextManager);
|
||||||
|
|
||||||
const cardTitle = screen.getByText("Тексты панелей");
|
const cardTitle = screen.getByText("Тексты панелей");
|
||||||
expect(cardTitle).toBeInTheDocument();
|
expect(cardTitle).toBeInTheDocument();
|
||||||
|
|
||||||
|
const input = screen.getByRole("textbox");
|
||||||
|
expect(input).toBeInTheDocument();
|
||||||
|
|
||||||
|
const addButton = screen.getByRole("button", { name: /add/i });
|
||||||
|
expect(addButton).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show empty list when textsState.texts is empty", () => {
|
||||||
|
render(TextManager);
|
||||||
|
|
||||||
|
const textItems = screen.queryAllByRole("listitem");
|
||||||
|
expect(textItems).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Adding text", () => {
|
||||||
|
it("should call textsState.addText when add button is clicked", async () => {
|
||||||
|
const addTextSpy = vi.spyOn(textsState, "addText");
|
||||||
|
|
||||||
|
render(TextManager);
|
||||||
|
|
||||||
|
const input = screen.getByRole("textbox");
|
||||||
|
const addButton = screen.getByRole("button", { name: /add/i });
|
||||||
|
|
||||||
|
fireEvent.input(input, { target: { value: "New text" } });
|
||||||
|
fireEvent.click(addButton);
|
||||||
|
|
||||||
|
expect(addTextSpy).toHaveBeenCalledWith("New text");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should clear input after adding text", async () => {
|
||||||
|
render(TextManager);
|
||||||
|
|
||||||
|
const input = screen.getByRole("textbox");
|
||||||
|
const addButton = screen.getByRole("button", { name: /add/i });
|
||||||
|
|
||||||
|
fireEvent.input(input, { target: { value: "New text" } });
|
||||||
|
fireEvent.click(addButton);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(input).toHaveValue("");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add text when Enter key is pressed", () => {
|
||||||
|
const addTextSpy = vi.spyOn(textsState, "addText");
|
||||||
|
|
||||||
|
render(TextManager);
|
||||||
|
|
||||||
|
const input = screen.getByRole("textbox");
|
||||||
|
|
||||||
|
fireEvent.input(input, { target: { value: "New text" } });
|
||||||
|
fireEvent.keyDown(input, { key: "Enter" });
|
||||||
|
|
||||||
|
expect(addTextSpy).toHaveBeenCalledWith("New text");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("List and deletion", () => {
|
||||||
|
it("should render TextInlineEdit for each text", () => {
|
||||||
|
textsState.addText("Text 1");
|
||||||
|
textsState.addText("Text 2");
|
||||||
|
textsState.addText("Text 3");
|
||||||
|
|
||||||
|
render(TextManager);
|
||||||
|
|
||||||
|
const textItems = screen.queryAllByRole("listitem");
|
||||||
|
expect(textItems).toHaveLength(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should pass correct props to TextInlineEdit", () => {
|
||||||
|
textsState.addText("Test text");
|
||||||
|
const textId = textsState.texts[0].id;
|
||||||
|
|
||||||
|
render(TextManager);
|
||||||
|
|
||||||
|
const textItems = screen.queryAllByRole("listitem");
|
||||||
|
expect(textItems).toHaveLength(1);
|
||||||
|
|
||||||
|
const inputs = textItems[0].querySelectorAll('input[type="text"]');
|
||||||
|
expect(inputs).toHaveLength(1);
|
||||||
|
expect(inputs[0]).toHaveValue("Test text");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call textsState.removeText when delete button is clicked", () => {
|
||||||
|
textsState.addText("Text to delete");
|
||||||
|
const textId = textsState.texts[0].id;
|
||||||
|
const removeTextSpy = vi.spyOn(textsState, "removeText");
|
||||||
|
|
||||||
|
render(TextManager);
|
||||||
|
|
||||||
|
const deleteButton = screen.getByRole("button", { name: /delete/i });
|
||||||
|
fireEvent.click(deleteButton);
|
||||||
|
|
||||||
|
expect(removeTextSpy).toHaveBeenCalledWith(textId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should remove element from DOM after deletion", async () => {
|
||||||
|
textsState.addText("Text to delete");
|
||||||
|
|
||||||
|
render(TextManager);
|
||||||
|
|
||||||
|
let textItems = screen.queryAllByRole("listitem");
|
||||||
|
expect(textItems).toHaveLength(1);
|
||||||
|
|
||||||
|
const deleteButton = screen.getByRole("button", { name: /delete/i });
|
||||||
|
fireEvent.click(deleteButton);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
textItems = screen.queryAllByRole("listitem");
|
||||||
|
expect(textItems).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Reactivity", () => {
|
||||||
|
it("should update list when textsState.texts changes externally", async () => {
|
||||||
|
render(TextManager);
|
||||||
|
|
||||||
|
let textItems = screen.queryAllByRole("listitem");
|
||||||
|
expect(textItems).toHaveLength(0);
|
||||||
|
|
||||||
|
textsState.addText("New text");
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
textItems = screen.queryAllByRole("listitem");
|
||||||
|
expect(textItems).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should update when text is removed externally", async () => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
|
||||||
|
textsState.addText("Text to remove");
|
||||||
|
const textId = textsState.texts[0].id;
|
||||||
|
|
||||||
|
render(TextManager);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryAllByRole("listitem")).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
textsState.removeText(textId);
|
||||||
|
await vi.advanceTimersByTimeAsync(400);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryAllByRole("listitem")).toHaveLength(0);
|
||||||
|
});
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Accessibility", () => {
|
||||||
|
// it("should keep focus in input after adding text", async () => {
|
||||||
|
// render(TextManager);
|
||||||
|
// const input = screen.getByRole("textbox");
|
||||||
|
// const addButton = screen.getByRole("button", { name: /add/i });
|
||||||
|
// fireEvent.input(input, { target: { value: "New text" } });
|
||||||
|
// fireEvent.click(addButton);
|
||||||
|
// await waitFor(() => {
|
||||||
|
// expect(input).toHaveFocus();
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
it("should have proper aria-labels for input and button", () => {
|
||||||
|
render(TextManager);
|
||||||
|
const input = screen.getByRole("textbox");
|
||||||
|
expect(input).toHaveAttribute("aria-label", "Input new text");
|
||||||
|
const addButton = screen.getByRole("button", { name: /add/i });
|
||||||
|
expect(addButton).toHaveAttribute("aria-label", "Add text");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ describe("Button.svelte", () => {
|
|||||||
props: {
|
props: {
|
||||||
icon: MockIcon,
|
icon: MockIcon,
|
||||||
label: "Test Button",
|
label: "Test Button",
|
||||||
|
ariaLabel: "Test button",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ describe("Button.svelte", () => {
|
|||||||
const { container } = render(Button, {
|
const { container } = render(Button, {
|
||||||
props: {
|
props: {
|
||||||
icon: MockIcon,
|
icon: MockIcon,
|
||||||
|
ariaLabel: "Test button",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -33,6 +35,7 @@ describe("Button.svelte", () => {
|
|||||||
icon: MockIcon,
|
icon: MockIcon,
|
||||||
label: "Click me",
|
label: "Click me",
|
||||||
onclick,
|
onclick,
|
||||||
|
ariaLabel: "Test button",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -49,6 +52,7 @@ describe("Button.svelte", () => {
|
|||||||
icon: MockIcon,
|
icon: MockIcon,
|
||||||
label: "Disabled",
|
label: "Disabled",
|
||||||
disabled: true,
|
disabled: true,
|
||||||
|
ariaLabel: "Test button",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -61,6 +65,7 @@ describe("Button.svelte", () => {
|
|||||||
props: {
|
props: {
|
||||||
icon: MockIcon,
|
icon: MockIcon,
|
||||||
label: "Enabled",
|
label: "Enabled",
|
||||||
|
ariaLabel: "Test button",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user