@torpor/ui 0.3.5 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/metafile-esm.json +1 -1
- package/package.json +5 -2
- package/CHANGELOG.md +0 -344
- package/scripts/postbuild.ts +0 -127
- package/src/Accordion/Accordion.torp +0 -230
- package/src/Accordion/AccordionContent.torp +0 -46
- package/src/Accordion/AccordionHeader.torp +0 -45
- package/src/Accordion/AccordionItem.torp +0 -101
- package/src/Accordion/AccordionTrigger.torp +0 -70
- package/src/Accordion/AccordionTypes.ts +0 -36
- package/src/Accordion/index.ts +0 -7
- package/src/Breadcrumb/Breadcrumb.torp +0 -45
- package/src/Breadcrumb/BreadcrumbItem.torp +0 -30
- package/src/Breadcrumb/BreadcrumbSeparator.torp +0 -27
- package/src/Breadcrumb/index.ts +0 -5
- package/src/Calendar/Calendar.torp +0 -277
- package/src/Calendar/CalendarDay.torp +0 -78
- package/src/Calendar/CalendarTypes.ts +0 -22
- package/src/Calendar/index.ts +0 -4
- package/src/Contextual/Contextual.torp +0 -129
- package/src/Contextual/ContextualContent.torp +0 -112
- package/src/Contextual/ContextualTrigger.torp +0 -70
- package/src/Contextual/ContextualTypes.ts +0 -30
- package/src/Contextual/index.ts +0 -5
- package/src/Dialog/Dialog.torp +0 -136
- package/src/Dialog/DialogBody.torp +0 -44
- package/src/Dialog/DialogButton.torp +0 -66
- package/src/Dialog/DialogFooter.torp +0 -25
- package/src/Dialog/DialogHeader.torp +0 -45
- package/src/Dialog/DialogTypes.ts +0 -17
- package/src/Dialog/index.ts +0 -7
- package/src/Disclosure/Disclosure.torp +0 -82
- package/src/Disclosure/DisclosureContent.torp +0 -46
- package/src/Disclosure/DisclosureHeader.torp +0 -45
- package/src/Disclosure/DisclosureTrigger.torp +0 -47
- package/src/Disclosure/DisclosureTypes.ts +0 -13
- package/src/Disclosure/index.ts +0 -6
- package/src/Drawer/Drawer.torp +0 -127
- package/src/Drawer/DrawerContent.torp +0 -83
- package/src/Drawer/DrawerOverlay.torp +0 -42
- package/src/Drawer/DrawerTrigger.torp +0 -39
- package/src/Drawer/DrawerTypes.ts +0 -24
- package/src/Drawer/index.ts +0 -6
- package/src/Form/CheckBox.torp +0 -94
- package/src/Form/Field.torp +0 -95
- package/src/Form/File.torp +0 -102
- package/src/Form/Form.torp +0 -147
- package/src/Form/FormTypes.ts +0 -35
- package/src/Form/Hidden.torp +0 -25
- package/src/Form/Input.torp +0 -112
- package/src/Form/Label.torp +0 -35
- package/src/Form/Message.torp +0 -39
- package/src/Form/Option.torp +0 -28
- package/src/Form/Select.torp +0 -101
- package/src/Form/StandardSchemaV1.ts +0 -70
- package/src/Form/TextArea.torp +0 -105
- package/src/Form/index.ts +0 -13
- package/src/Menu/Menu.torp +0 -174
- package/src/Menu/MenuButton.torp +0 -74
- package/src/Menu/MenuCheck.torp +0 -89
- package/src/Menu/MenuGroup.torp +0 -31
- package/src/Menu/MenuIndicator.torp +0 -40
- package/src/Menu/MenuPopout.torp +0 -83
- package/src/Menu/MenuPopoutContent.torp +0 -84
- package/src/Menu/MenuPopoutTrigger.torp +0 -128
- package/src/Menu/MenuRadio.torp +0 -103
- package/src/Menu/MenuRadioGroup.torp +0 -109
- package/src/Menu/MenuSeparator.torp +0 -26
- package/src/Menu/MenuTypes.ts +0 -60
- package/src/Menu/index.ts +0 -25
- package/src/MenuBar/MenuBar.torp +0 -162
- package/src/MenuBar/MenuBarItem.torp +0 -116
- package/src/MenuBar/MenuBarItemContent.torp +0 -119
- package/src/MenuBar/MenuBarItemTrigger.torp +0 -99
- package/src/MenuBar/MenuBarTypes.ts +0 -36
- package/src/MenuBar/index.ts +0 -6
- package/src/Modal/Modal.torp +0 -122
- package/src/Modal/ModalContent.torp +0 -81
- package/src/Modal/ModalOverlay.torp +0 -42
- package/src/Modal/ModalTrigger.torp +0 -54
- package/src/Modal/ModalTypes.ts +0 -22
- package/src/Modal/index.ts +0 -10
- package/src/Modal/showModal.ts +0 -19
- package/src/Modal/showPrompt.ts +0 -23
- package/src/NavMenu/NavMenu.torp +0 -190
- package/src/NavMenu/NavMenuButton.torp +0 -55
- package/src/NavMenu/NavMenuElement.torp +0 -55
- package/src/NavMenu/NavMenuGroup.torp +0 -41
- package/src/NavMenu/NavMenuGroupHeader.torp +0 -41
- package/src/NavMenu/NavMenuLink.torp +0 -60
- package/src/NavMenu/NavMenuPopout.torp +0 -89
- package/src/NavMenu/NavMenuPopoutButton.torp +0 -129
- package/src/NavMenu/NavMenuPopoutContent.torp +0 -92
- package/src/NavMenu/NavMenuSeparator.torp +0 -24
- package/src/NavMenu/NavMenuTypes.ts +0 -32
- package/src/NavMenu/index.ts +0 -23
- package/src/Notification/Notification.torp +0 -71
- package/src/Notification/NotificationClose.torp +0 -39
- package/src/Notification/NotificationContainer.torp +0 -26
- package/src/Notification/NotificationContent.torp +0 -25
- package/src/Notification/NotificationTypes.ts +0 -5
- package/src/Notification/index.ts +0 -13
- package/src/Notification/showNotification.ts +0 -30
- package/src/Popover/Popover.torp +0 -129
- package/src/Popover/PopoverContent.torp +0 -117
- package/src/Popover/PopoverTrigger.torp +0 -62
- package/src/Popover/PopoverTypes.ts +0 -30
- package/src/Popover/index.ts +0 -6
- package/src/Popover/showPopover.ts +0 -24
- package/src/Slider/Slider.torp +0 -84
- package/src/Slider/index.ts +0 -3
- package/src/TabGroup/TabContent.torp +0 -49
- package/src/TabGroup/TabGroup.torp +0 -250
- package/src/TabGroup/TabGroupTypes.ts +0 -44
- package/src/TabGroup/TabHeader.torp +0 -81
- package/src/TabGroup/TabList.torp +0 -61
- package/src/TabGroup/TabTrigger.torp +0 -76
- package/src/TabGroup/index.ts +0 -7
- package/src/ambient.d.ts +0 -7
- package/src/icons/ArrowDown.torp +0 -18
- package/src/icons/ArrowLeft.torp +0 -18
- package/src/icons/ArrowRight.torp +0 -18
- package/src/icons/ArrowUp.torp +0 -18
- package/src/icons/Bars.torp +0 -20
- package/src/icons/Check.torp +0 -18
- package/src/icons/ChevronDown.torp +0 -18
- package/src/icons/ChevronLeft.torp +0 -18
- package/src/icons/ChevronRight.torp +0 -18
- package/src/icons/ChevronUp.torp +0 -18
- package/src/icons/Cross.torp +0 -19
- package/src/icons/IconTypes.ts +0 -10
- package/src/icons/Loading.torp +0 -19
- package/src/icons/Minus.torp +0 -18
- package/src/icons/Plus.torp +0 -19
- package/src/icons/index.ts +0 -31
- package/src/motion/fade.ts +0 -16
- package/src/motion/grow.ts +0 -23
- package/src/motion/index.ts +0 -6
- package/src/motion/measure.ts +0 -28
- package/src/motion/slide.ts +0 -45
- package/src/mount/clickPopover.ts +0 -51
- package/src/mount/contextPopover.ts +0 -51
- package/src/mount/disableScroll.ts +0 -28
- package/src/mount/hoverPopover.ts +0 -58
- package/src/mount/hoverTrigger.ts +0 -59
- package/src/mount/trapFocus.ts +0 -58
- package/src/utils/dateUtils.ts +0 -244
- package/src/utils/documentEvents.ts +0 -17
- package/src/utils/getId.ts +0 -6
- package/src/utils/getScrollParent.ts +0 -22
- package/src/utils/setContextPosition.ts +0 -31
- package/src/utils/setPopoverPosition.ts +0 -142
- package/test/Accordion/accessibility.test.ts +0 -59
- package/test/Accordion/components/AccordionAccessibility.torp +0 -44
- package/test/Accordion/components/AccordionKeyboard.torp +0 -32
- package/test/Accordion/components/AccordionList.torp +0 -23
- package/test/Accordion/components/AccordionMultiple.torp +0 -32
- package/test/Accordion/components/AccordionSingle.torp +0 -32
- package/test/Accordion/for-items.test.ts +0 -52
- package/test/Accordion/keyboard.test.ts +0 -64
- package/test/Accordion/multiple-item-toggle.test.ts +0 -32
- package/test/Accordion/multiple-item-value.test.ts +0 -17
- package/test/Accordion/single-item-toggle.test.ts +0 -31
- package/test/Accordion/single-item-value.test.ts +0 -17
- package/test/Breadcrumb/accessibility.test.ts +0 -29
- package/test/Breadcrumb/basic.test.ts +0 -17
- package/test/Breadcrumb/components/BreadcrumbTest.torp +0 -15
- package/test/Calendar/components/CalendarTest.torp +0 -9
- package/test/Calendar/default.test.ts +0 -18
- package/test/Dialog/accessibility-alert.test.ts +0 -40
- package/test/Dialog/accessibility-modal.test.ts +0 -52
- package/test/Dialog/components/DialogAccessibility.torp +0 -25
- package/test/Dialog/components/DialogFunction.torp +0 -36
- package/test/Dialog/components/DialogModal.torp +0 -30
- package/test/Dialog/components/DialogVariable.torp +0 -33
- package/test/Dialog/function.test.ts +0 -26
- package/test/Dialog/variable.test.ts +0 -27
- package/test/Disclosure/accessibility.test.ts +0 -31
- package/test/Disclosure/components/DisclosureTest.torp +0 -17
- package/test/Disclosure/default.test.ts +0 -15
- package/test/Disclosure/toggle.test.ts +0 -20
- package/test/Form/components/FieldTest.torp +0 -25
- package/test/Form/components/FormTest.torp +0 -31
- package/test/Form/components/InputTest.torp +0 -9
- package/test/Form/field.test.ts +0 -34
- package/test/Form/form.test.ts +0 -34
- package/test/Form/input.test.ts +0 -19
- package/test/Menu/accessibility.test.ts +0 -83
- package/test/Menu/basic.test.ts +0 -58
- package/test/Menu/components/MenuAccessibility.torp +0 -59
- package/test/Menu/components/MenuBasic.torp +0 -58
- package/test/MenuBar/accessibility.test.ts +0 -87
- package/test/MenuBar/basic.test.ts +0 -22
- package/test/MenuBar/components/MenuBarAccessibility.torp +0 -34
- package/test/MenuBar/components/MenuBarBasic.torp +0 -25
- package/test/NavMenu/accessibility.test.ts +0 -97
- package/test/NavMenu/basic.test.ts +0 -22
- package/test/NavMenu/components/NavMenuAccessibility.torp +0 -35
- package/test/NavMenu/components/NavMenuBasic.torp +0 -28
- package/test/Notification/accessibility.test.ts +0 -21
- package/test/Notification/components/CustomNotification.torp +0 -14
- package/test/Notification/components/NotificationAccessibility.torp +0 -14
- package/test/Notification/components/NotificationFunction.torp +0 -17
- package/test/Notification/components/NotificationVariable.torp +0 -22
- package/test/Notification/function.test.ts +0 -25
- package/test/Notification/variable.test.ts +0 -25
- package/test/Popover/components/CustomDialog.torp +0 -25
- package/test/Popover/components/PopoverFunction.torp +0 -37
- package/test/Popover/components/PopoverTrigger.torp +0 -36
- package/test/Popover/components/PopoverVariable.torp +0 -40
- package/test/Popover/function.test.ts +0 -21
- package/test/Popover/trigger.test.ts +0 -27
- package/test/Popover/variable.test.ts +0 -27
- package/test/TabGroup/accessibility.test.ts +0 -67
- package/test/TabGroup/components/TabGroupAccessibility.torp +0 -28
- package/test/TabGroup/components/TabGroupKeyboard.torp +0 -28
- package/test/TabGroup/components/TabGroupList.torp +0 -26
- package/test/TabGroup/components/TabGroupTrigger.torp +0 -28
- package/test/TabGroup/for-items.test.ts +0 -52
- package/test/TabGroup/item-trigger.test.ts +0 -33
- package/test/TabGroup/item-value.test.ts +0 -17
- package/test/TabGroup/keyboard.test.ts +0 -72
- package/tsconfig.json +0 -4
- package/tsup.config.ts +0 -37
- package/vitest.config.ts +0 -12
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { queryByText } from "@testing-library/dom";
|
|
2
|
-
import "@testing-library/jest-dom/vitest";
|
|
3
|
-
import { mount } from "@torpor/view";
|
|
4
|
-
import { describe, expect, it } from "vitest";
|
|
5
|
-
import DisclosureTest from "./components/DisclosureTest.torp";
|
|
6
|
-
|
|
7
|
-
describe("Disclosure", () => {
|
|
8
|
-
it("Default value", async () => {
|
|
9
|
-
const container = document.createElement("div");
|
|
10
|
-
document.body.appendChild(container);
|
|
11
|
-
mount(container, DisclosureTest, { expanded: true });
|
|
12
|
-
|
|
13
|
-
expect(queryByText(container, "Disclosure content")).toBeInTheDocument();
|
|
14
|
-
});
|
|
15
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { getByText, queryByText } from "@testing-library/dom";
|
|
2
|
-
import "@testing-library/jest-dom/vitest";
|
|
3
|
-
import userEvent from "@testing-library/user-event";
|
|
4
|
-
import { mount } from "@torpor/view";
|
|
5
|
-
import { describe, expect, it } from "vitest";
|
|
6
|
-
import DisclosureTest from "./components/DisclosureTest.torp";
|
|
7
|
-
|
|
8
|
-
describe("Disclosure", () => {
|
|
9
|
-
it("Toggle", async () => {
|
|
10
|
-
const container = document.createElement("div");
|
|
11
|
-
document.body.appendChild(container);
|
|
12
|
-
mount(container, DisclosureTest);
|
|
13
|
-
|
|
14
|
-
expect(queryByText(container, "Disclosure content")).not.toBeInTheDocument();
|
|
15
|
-
|
|
16
|
-
// Clicking the header should reveal the body
|
|
17
|
-
await userEvent.click(getByText(container, "Disclosure header"));
|
|
18
|
-
expect(queryByText(container, "Disclosure content")).toBeInTheDocument();
|
|
19
|
-
});
|
|
20
|
-
});
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Field,
|
|
3
|
-
Label,
|
|
4
|
-
Input,
|
|
5
|
-
Message,
|
|
6
|
-
} from "../../../src/Form/index";
|
|
7
|
-
import * as z from "zod";
|
|
8
|
-
|
|
9
|
-
export default function FormTest($props: { value: string }) {
|
|
10
|
-
const schema = z.string().trim().min(1, { message: 'Name is required!' });
|
|
11
|
-
|
|
12
|
-
let $state = $watch({
|
|
13
|
-
data: {
|
|
14
|
-
name: "",
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
@render {
|
|
19
|
-
<Field name="name" &value={$state.data.name} {schema}>
|
|
20
|
-
<Label>Name:</Label>
|
|
21
|
-
<Input id="name-id" placeholder="Name..." />
|
|
22
|
-
<Message />
|
|
23
|
-
</Field>
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Form,
|
|
3
|
-
Field,
|
|
4
|
-
Label,
|
|
5
|
-
Input,
|
|
6
|
-
Message,
|
|
7
|
-
} from "../../../src/Form/index";
|
|
8
|
-
import * as z from "zod";
|
|
9
|
-
|
|
10
|
-
export default function FormTest($props: { value: string }) {
|
|
11
|
-
const schema = z.object({
|
|
12
|
-
name: z.string().trim().min(1, { message: 'Name is required!' }),
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
let $state = $watch({
|
|
16
|
-
data: {
|
|
17
|
-
name: "",
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
@render {
|
|
22
|
-
<Form {$state.data} {schema}>
|
|
23
|
-
<Field name="name" &value={$state.data.name}>
|
|
24
|
-
<Label>Name:</Label>
|
|
25
|
-
<Input id="name-id" placeholder="Name..." />
|
|
26
|
-
<Message />
|
|
27
|
-
</Field>
|
|
28
|
-
<button>Submit</button>
|
|
29
|
-
</Form>
|
|
30
|
-
}
|
|
31
|
-
}
|
package/test/Form/field.test.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
fireEvent,
|
|
3
|
-
getByPlaceholderText,
|
|
4
|
-
queryByPlaceholderText,
|
|
5
|
-
queryByText,
|
|
6
|
-
} from "@testing-library/dom";
|
|
7
|
-
import "@testing-library/jest-dom/vitest";
|
|
8
|
-
import userEvent from "@testing-library/user-event";
|
|
9
|
-
import { mount } from "@torpor/view";
|
|
10
|
-
import { expect, test } from "vitest";
|
|
11
|
-
import Field from "./components/FieldTest.torp";
|
|
12
|
-
|
|
13
|
-
test("Field", async () => {
|
|
14
|
-
const container = document.createElement("div");
|
|
15
|
-
document.body.appendChild(container);
|
|
16
|
-
mount(container, Field);
|
|
17
|
-
|
|
18
|
-
// Label, input, message
|
|
19
|
-
expect(queryByText(container, "Name:")).not.toBeNull();
|
|
20
|
-
expect(queryByPlaceholderText(container, "Name...")).not.toBeNull();
|
|
21
|
-
expect(queryByText(container, "Name is required!")).toBeNull();
|
|
22
|
-
|
|
23
|
-
// Label `for` should be automatically hooked up
|
|
24
|
-
expect(queryByPlaceholderText(container, "Name...")).toHaveAttribute("id", "name-id");
|
|
25
|
-
expect(queryByText(container, "Name:")).toHaveAttribute("for", "name-id");
|
|
26
|
-
|
|
27
|
-
// Validation should be fired on blur
|
|
28
|
-
fireEvent.blur(queryByPlaceholderText(container, "Name...")!);
|
|
29
|
-
expect(queryByText(container, "Name is required!")).not.toBeNull();
|
|
30
|
-
|
|
31
|
-
// After blur, validation should fire on input
|
|
32
|
-
await userEvent.type(getByPlaceholderText(container, "Name..."), "G");
|
|
33
|
-
expect(queryByText(container, "Name is required!")).toBeNull();
|
|
34
|
-
});
|
package/test/Form/form.test.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getByPlaceholderText,
|
|
3
|
-
getByText,
|
|
4
|
-
queryByPlaceholderText,
|
|
5
|
-
queryByText,
|
|
6
|
-
} from "@testing-library/dom";
|
|
7
|
-
import "@testing-library/jest-dom/vitest";
|
|
8
|
-
import userEvent from "@testing-library/user-event";
|
|
9
|
-
import { mount } from "@torpor/view";
|
|
10
|
-
import { expect, test } from "vitest";
|
|
11
|
-
import Form from "./components/FormTest.torp";
|
|
12
|
-
|
|
13
|
-
test("Form", async () => {
|
|
14
|
-
const container = document.createElement("div");
|
|
15
|
-
document.body.appendChild(container);
|
|
16
|
-
mount(container, Form);
|
|
17
|
-
|
|
18
|
-
// Label, input, message
|
|
19
|
-
expect(queryByText(container, "Name:")).not.toBeNull();
|
|
20
|
-
expect(queryByPlaceholderText(container, "Name...")).not.toBeNull();
|
|
21
|
-
expect(queryByText(container, "Name is required!")).toBeNull();
|
|
22
|
-
|
|
23
|
-
// Label `for` should be automatically hooked up
|
|
24
|
-
expect(queryByPlaceholderText(container, "Name...")).toHaveAttribute("id", "name-id");
|
|
25
|
-
expect(queryByText(container, "Name:")).toHaveAttribute("for", "name-id");
|
|
26
|
-
|
|
27
|
-
// Validation should be fired on submit
|
|
28
|
-
await userEvent.click(getByText(container, "Submit"));
|
|
29
|
-
expect(queryByText(container, "Name is required!")).not.toBeNull();
|
|
30
|
-
|
|
31
|
-
// After submitting, validation should fire on input
|
|
32
|
-
await userEvent.type(getByPlaceholderText(container, "Name..."), "G");
|
|
33
|
-
expect(queryByText(container, "Name is required!")).toBeNull();
|
|
34
|
-
});
|
package/test/Form/input.test.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { getByPlaceholderText, queryByPlaceholderText } from "@testing-library/dom";
|
|
2
|
-
import "@testing-library/jest-dom/vitest";
|
|
3
|
-
import userEvent from "@testing-library/user-event";
|
|
4
|
-
import { mount } from "@torpor/view";
|
|
5
|
-
import { expect, test } from "vitest";
|
|
6
|
-
import Input from "./components/InputTest.torp";
|
|
7
|
-
|
|
8
|
-
test("Input", async () => {
|
|
9
|
-
let props = { value: "" };
|
|
10
|
-
|
|
11
|
-
const container = document.createElement("div");
|
|
12
|
-
document.body.appendChild(container);
|
|
13
|
-
mount(container, Input, props);
|
|
14
|
-
|
|
15
|
-
expect(queryByPlaceholderText(container, "Name...")).not.toBeNull();
|
|
16
|
-
|
|
17
|
-
await userEvent.type(getByPlaceholderText(container, "Name..."), "Greg");
|
|
18
|
-
expect(props.value).toBe("Greg");
|
|
19
|
-
});
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { getByText, queryByText } from "@testing-library/dom";
|
|
2
|
-
import "@testing-library/jest-dom/vitest";
|
|
3
|
-
import userEvent from "@testing-library/user-event";
|
|
4
|
-
import { mount } from "@torpor/view";
|
|
5
|
-
import { describe, expect, it } from "vitest";
|
|
6
|
-
import MenuAccessibility from "./components/MenuAccessibility.torp";
|
|
7
|
-
|
|
8
|
-
describe("Menu", () => {
|
|
9
|
-
it("Accessibility", async () => {
|
|
10
|
-
const container = document.createElement("div");
|
|
11
|
-
document.body.appendChild(container);
|
|
12
|
-
mount(container, MenuAccessibility);
|
|
13
|
-
|
|
14
|
-
// Tests from https://www.w3.org/WAI/ARIA/apg/patterns/menu/
|
|
15
|
-
|
|
16
|
-
// A menu is a container of items that represent choices. The element serving as the menu has
|
|
17
|
-
// a role of either menu or menubar
|
|
18
|
-
expect(queryByText(container, "Menu button")?.parentElement).toHaveAttribute("role", "menu");
|
|
19
|
-
|
|
20
|
-
// The items contained in a menu are child elements of the containing menu or menubar and have
|
|
21
|
-
// any of the following roles:
|
|
22
|
-
// * menuitem
|
|
23
|
-
// * menuitemcheckbox
|
|
24
|
-
// * menuitemradio
|
|
25
|
-
expect(queryByText(container, "Menu button")).toHaveAttribute("role", "menuitem");
|
|
26
|
-
expect(queryByText(container, "Check popout button")).toHaveAttribute("role", "menuitem");
|
|
27
|
-
|
|
28
|
-
// If activating a menuitem opens a submenu, the menuitem is known as a parent menuitem. A submenu's
|
|
29
|
-
// menu element is:
|
|
30
|
-
// * Contained inside the same menu element as its parent menuitem
|
|
31
|
-
// * Is the sibling element immediately following its parent menuitem
|
|
32
|
-
|
|
33
|
-
// A parent menuitem has aria-haspopup set to either menu or true
|
|
34
|
-
expect(queryByText(container, "Check popout button")).toHaveAttribute("aria-haspopup", "true");
|
|
35
|
-
|
|
36
|
-
// A parent menuitem has aria-expanded set to false when its child menu is not visible and set
|
|
37
|
-
// to true when the child menu is visible
|
|
38
|
-
expect(queryByText(container, "Check popout button")).toHaveAttribute("aria-expanded", "false");
|
|
39
|
-
await userEvent.click(getByText(container, "Check popout button"));
|
|
40
|
-
expect(queryByText(container, "Check popout button")).toHaveAttribute("aria-expanded", "true");
|
|
41
|
-
|
|
42
|
-
// One of the following approaches is used to enable scripts to move focus among items in a
|
|
43
|
-
// menu as described in Keyboard Navigation Inside Components:
|
|
44
|
-
// * The menu container has tabindex set to -1 or 0 and aria-activedescendant set to the ID
|
|
45
|
-
// of the focused item
|
|
46
|
-
// * Each item in the menu has tabindex set to -1, except in a menubar, where the first item
|
|
47
|
-
// has tabindex set to 0
|
|
48
|
-
|
|
49
|
-
// When a menuitemcheckbox or menuitemradio is checked, aria-checked is set to true
|
|
50
|
-
// NOTE: We don't (currently?) have checkboxes or radios in Menus
|
|
51
|
-
|
|
52
|
-
// When a menu item is disabled, aria-disabled is set to true
|
|
53
|
-
expect(queryByText(container, "Menu button")).not.toHaveAttribute("aria-disabled");
|
|
54
|
-
expect(queryByText(container, "Disabled button")).toHaveAttribute("aria-disabled", "true");
|
|
55
|
-
|
|
56
|
-
// Items in a menu may be divided into groups by placing an element with a role of separator
|
|
57
|
-
// between groups. For example, this technique should be used when a menu contains a set of
|
|
58
|
-
// menuitemradio items
|
|
59
|
-
expect(queryByText(container, "---")).toHaveAttribute("role", "separator");
|
|
60
|
-
|
|
61
|
-
// All separators should have aria-orientation consistent with the separator's orientation
|
|
62
|
-
// NOTE: What does this mean? None of the example separators have aria-orientation set
|
|
63
|
-
|
|
64
|
-
// An element with role menu either has:
|
|
65
|
-
// * aria-labelledby set to a value that refers to the menuitem or button that controls its
|
|
66
|
-
// display
|
|
67
|
-
// * A label provided by aria-label
|
|
68
|
-
// NOTE: We only support aria-label and it's required
|
|
69
|
-
expect(queryByText(container, "Menu button")?.parentElement).toHaveAttribute(
|
|
70
|
-
"aria-label",
|
|
71
|
-
"A menu",
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
// If a menu is horizontally oriented, it has aria-orientation set to horizontal. The default
|
|
75
|
-
// value of aria-orientation for a menu is vertical
|
|
76
|
-
// NOTE: For these menus, the only orientation is vertical -- can't really think of a situation
|
|
77
|
-
// where horizontal would make sense?
|
|
78
|
-
expect(queryByText(container, "Menu button")?.parentElement).toHaveAttribute(
|
|
79
|
-
"aria-orientation",
|
|
80
|
-
"vertical",
|
|
81
|
-
);
|
|
82
|
-
});
|
|
83
|
-
});
|
package/test/Menu/basic.test.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { getByText, queryByText } from "@testing-library/dom";
|
|
2
|
-
import "@testing-library/jest-dom/vitest";
|
|
3
|
-
import userEvent from "@testing-library/user-event";
|
|
4
|
-
import { mount } from "@torpor/view";
|
|
5
|
-
import { describe, expect, it } from "vitest";
|
|
6
|
-
import MenuBasic from "./components/MenuBasic.torp";
|
|
7
|
-
|
|
8
|
-
describe("Menu", () => {
|
|
9
|
-
it("Basic operation", async () => {
|
|
10
|
-
const container = document.createElement("div");
|
|
11
|
-
document.body.appendChild(container);
|
|
12
|
-
mount(container, MenuBasic);
|
|
13
|
-
|
|
14
|
-
// By default, the top-level buttons should be displayed
|
|
15
|
-
expect(queryByText(container, "Menu button")).toBeInTheDocument();
|
|
16
|
-
expect(queryByText(container, "Check popout button")).toBeInTheDocument();
|
|
17
|
-
expect(queryByText(container, "Radio popout button")).toBeInTheDocument();
|
|
18
|
-
|
|
19
|
-
// Submenus should not be displayed
|
|
20
|
-
expect(queryByText(container, "Checked 1")).not.toBeInTheDocument();
|
|
21
|
-
expect(queryByText(container, "Radio 1")).not.toBeInTheDocument();
|
|
22
|
-
|
|
23
|
-
// Clicking a popout button should show the appropriate menu
|
|
24
|
-
await userEvent.click(getByText(container, "Check popout button"));
|
|
25
|
-
expect(queryByText(container, "Checked 1")).toBeInTheDocument();
|
|
26
|
-
expect(queryByText(container, "Radio 1")).not.toBeInTheDocument();
|
|
27
|
-
|
|
28
|
-
// Clicking another popout button should show that menu instead
|
|
29
|
-
await userEvent.click(getByText(container, "Radio popout button"));
|
|
30
|
-
//expect(queryByText(container, 'Checked 1')).not.toBeInTheDocument();
|
|
31
|
-
expect(queryByText(container, "Radio 1")).toBeInTheDocument();
|
|
32
|
-
|
|
33
|
-
// Checking a box should show or hide the indicator
|
|
34
|
-
await userEvent.click(getByText(container, "Check popout button"));
|
|
35
|
-
expect(queryByText(container, "Tick 1")).not.toBeInTheDocument();
|
|
36
|
-
expect(queryByText(container, "Tick 2")).not.toBeInTheDocument();
|
|
37
|
-
await userEvent.click(getByText(container, "Checked 1"));
|
|
38
|
-
expect(queryByText(container, "Tick 1")).toBeInTheDocument();
|
|
39
|
-
expect(queryByText(container, "Tick 2")).not.toBeInTheDocument();
|
|
40
|
-
await userEvent.click(getByText(container, "Checked 2"));
|
|
41
|
-
expect(queryByText(container, "Tick 1")).toBeInTheDocument();
|
|
42
|
-
expect(queryByText(container, "Tick 2")).toBeInTheDocument();
|
|
43
|
-
await userEvent.click(getByText(container, "Checked 1"));
|
|
44
|
-
expect(queryByText(container, "Tick 1")).not.toBeInTheDocument();
|
|
45
|
-
expect(queryByText(container, "Tick 2")).toBeInTheDocument();
|
|
46
|
-
|
|
47
|
-
// Checking a radio should show the indicator, and hide another indicator if already selected
|
|
48
|
-
await userEvent.click(getByText(container, "Radio popout button"));
|
|
49
|
-
expect(queryByText(container, "Circle 1")).not.toBeInTheDocument();
|
|
50
|
-
expect(queryByText(container, "Circle 2")).not.toBeInTheDocument();
|
|
51
|
-
await userEvent.click(getByText(container, "Radio 1"));
|
|
52
|
-
expect(queryByText(container, "Circle 1")).toBeInTheDocument();
|
|
53
|
-
expect(queryByText(container, "Circle 2")).not.toBeInTheDocument();
|
|
54
|
-
await userEvent.click(getByText(container, "Radio 2"));
|
|
55
|
-
expect(queryByText(container, "Circle 1")).not.toBeInTheDocument();
|
|
56
|
-
expect(queryByText(container, "Circle 2")).toBeInTheDocument();
|
|
57
|
-
});
|
|
58
|
-
});
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { Modal, ModalContent } from "../../../src/Modal";
|
|
2
|
-
import {
|
|
3
|
-
Menu,
|
|
4
|
-
MenuButton,
|
|
5
|
-
MenuRadioGroup,
|
|
6
|
-
MenuCheck,
|
|
7
|
-
MenuRadio,
|
|
8
|
-
MenuIndicator,
|
|
9
|
-
MenuPopout,
|
|
10
|
-
MenuPopoutTrigger,
|
|
11
|
-
MenuPopoutContent,
|
|
12
|
-
MenuSeparator
|
|
13
|
-
} from "../../../src/Menu";
|
|
14
|
-
|
|
15
|
-
export default function MenuAccessibility() {
|
|
16
|
-
@render {
|
|
17
|
-
<Modal visible>
|
|
18
|
-
<ModalContent>
|
|
19
|
-
<Menu ariaLabel="A menu">
|
|
20
|
-
<MenuButton>Menu button</MenuButton>
|
|
21
|
-
<MenuButton disabled>Disabled button</MenuButton>
|
|
22
|
-
<MenuSeparator>---</MenuSeparator>
|
|
23
|
-
<MenuPopout>
|
|
24
|
-
<MenuPopoutTrigger>Check popout button</MenuPopoutTrigger>
|
|
25
|
-
<MenuPopoutContent>
|
|
26
|
-
<Menu ariaLabel="A checked menu">
|
|
27
|
-
<MenuCheck>
|
|
28
|
-
<MenuIndicator>✔</MenuIndicator>
|
|
29
|
-
Checked 1
|
|
30
|
-
</MenuCheck>
|
|
31
|
-
<MenuCheck>
|
|
32
|
-
<MenuIndicator>✔</MenuIndicator>
|
|
33
|
-
Checked 2
|
|
34
|
-
</MenuCheck>
|
|
35
|
-
</Menu>
|
|
36
|
-
</MenuPopoutContent>
|
|
37
|
-
</MenuPopout>
|
|
38
|
-
<MenuPopout>
|
|
39
|
-
<MenuPopoutTrigger>Radio popout button</MenuPopoutTrigger>
|
|
40
|
-
<MenuPopoutContent>
|
|
41
|
-
<Menu ariaLabel="A radio menu">
|
|
42
|
-
<MenuRadioGroup ariaLabel="Options">
|
|
43
|
-
<MenuRadio value="radio1">
|
|
44
|
-
<MenuIndicator>•</MenuIndicator>
|
|
45
|
-
Radio 1
|
|
46
|
-
</MenuRadio>
|
|
47
|
-
<MenuRadio value="radio2">
|
|
48
|
-
<MenuIndicator>•</MenuIndicator>
|
|
49
|
-
Radio 2
|
|
50
|
-
</MenuRadio>
|
|
51
|
-
</MenuRadioGroup>
|
|
52
|
-
</Menu>
|
|
53
|
-
</MenuPopoutContent>
|
|
54
|
-
</MenuPopout>
|
|
55
|
-
</Menu>
|
|
56
|
-
</ModalContent>
|
|
57
|
-
</Modal>
|
|
58
|
-
}
|
|
59
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { Modal, ModalContent } from "../../../src/Modal";
|
|
2
|
-
import {
|
|
3
|
-
Menu,
|
|
4
|
-
MenuButton,
|
|
5
|
-
MenuRadioGroup,
|
|
6
|
-
MenuCheck,
|
|
7
|
-
MenuRadio,
|
|
8
|
-
MenuIndicator,
|
|
9
|
-
MenuPopout,
|
|
10
|
-
MenuPopoutTrigger,
|
|
11
|
-
MenuPopoutContent,
|
|
12
|
-
MenuSeparator
|
|
13
|
-
} from "../../../src/Menu";
|
|
14
|
-
|
|
15
|
-
export default function MenuBasic() {
|
|
16
|
-
@render {
|
|
17
|
-
<Modal visible>
|
|
18
|
-
<ModalContent>
|
|
19
|
-
<Menu ariaLabel="A menu">
|
|
20
|
-
<MenuButton>Menu button</MenuButton>
|
|
21
|
-
<MenuSeparator>---</MenuSeparator>
|
|
22
|
-
<MenuPopout>
|
|
23
|
-
<MenuPopoutTrigger>Check popout button</MenuPopoutTrigger>
|
|
24
|
-
<MenuPopoutContent>
|
|
25
|
-
<Menu ariaLabel="A checked menu">
|
|
26
|
-
<MenuCheck>
|
|
27
|
-
<MenuIndicator>Tick 1</MenuIndicator>
|
|
28
|
-
Checked 1
|
|
29
|
-
</MenuCheck>
|
|
30
|
-
<MenuCheck>
|
|
31
|
-
<MenuIndicator>Tick 2</MenuIndicator>
|
|
32
|
-
Checked 2
|
|
33
|
-
</MenuCheck>
|
|
34
|
-
</Menu>
|
|
35
|
-
</MenuPopoutContent>
|
|
36
|
-
</MenuPopout>
|
|
37
|
-
<MenuPopout>
|
|
38
|
-
<MenuPopoutTrigger>Radio popout button</MenuPopoutTrigger>
|
|
39
|
-
<MenuPopoutContent>
|
|
40
|
-
<Menu ariaLabel="A radio menu">
|
|
41
|
-
<MenuRadioGroup ariaLabel="Font color">
|
|
42
|
-
<MenuRadio value="radio1">
|
|
43
|
-
<MenuIndicator>Circle 1</MenuIndicator>
|
|
44
|
-
Radio 1
|
|
45
|
-
</MenuRadio>
|
|
46
|
-
<MenuRadio value="radio2">
|
|
47
|
-
<MenuIndicator>Circle 2</MenuIndicator>
|
|
48
|
-
Radio 2
|
|
49
|
-
</MenuRadio>
|
|
50
|
-
</MenuRadioGroup>
|
|
51
|
-
</Menu>
|
|
52
|
-
</MenuPopoutContent>
|
|
53
|
-
</MenuPopout>
|
|
54
|
-
</Menu>
|
|
55
|
-
</ModalContent>
|
|
56
|
-
</Modal>
|
|
57
|
-
}
|
|
58
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { getByText, queryByText } from "@testing-library/dom";
|
|
2
|
-
import "@testing-library/jest-dom/vitest";
|
|
3
|
-
import userEvent from "@testing-library/user-event";
|
|
4
|
-
import { mount } from "@torpor/view";
|
|
5
|
-
import { describe, expect, it } from "vitest";
|
|
6
|
-
import MenuBarAccessibility from "./components/MenuBarAccessibility.torp";
|
|
7
|
-
|
|
8
|
-
describe("MenuBar", () => {
|
|
9
|
-
it("Accessibility", async () => {
|
|
10
|
-
const container = document.createElement("div");
|
|
11
|
-
document.body.appendChild(container);
|
|
12
|
-
mount(container, MenuBarAccessibility);
|
|
13
|
-
|
|
14
|
-
// Tests from https://www.w3.org/WAI/ARIA/apg/patterns/menu/
|
|
15
|
-
// NOTE: MenuBar is a menubar
|
|
16
|
-
|
|
17
|
-
// A menu is a container of items that represent choices. The element serving as the menu has
|
|
18
|
-
// a role of either menu or menubar
|
|
19
|
-
expect(queryByText(container, "Top level")?.parentElement?.parentElement).toHaveAttribute(
|
|
20
|
-
"role",
|
|
21
|
-
"menubar",
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
// The items contained in a menu are child elements of the containing menu or menubar and have
|
|
25
|
-
// any of the following roles:
|
|
26
|
-
// * menuitem
|
|
27
|
-
// * menuitemcheckbox
|
|
28
|
-
// * menuitemradio
|
|
29
|
-
expect(queryByText(container, "Top level")).toHaveAttribute("role", "menuitem");
|
|
30
|
-
|
|
31
|
-
// If activating a menuitem opens a submenu, the menuitem is known as a parent menuitem. A submenu's
|
|
32
|
-
// menu element is:
|
|
33
|
-
// * Contained inside the same menu element as its parent menuitem
|
|
34
|
-
// * Is the sibling element immediately following its parent menuitem
|
|
35
|
-
|
|
36
|
-
// A parent menuitem has aria-haspopup set to either menu or true
|
|
37
|
-
expect(queryByText(container, "Top level")).toHaveAttribute("aria-haspopup", "true");
|
|
38
|
-
|
|
39
|
-
// A parent menuitem has aria-expanded set to false when its child menu is not visible and set
|
|
40
|
-
// to true when the child menu is visible
|
|
41
|
-
expect(queryByText(container, "Top level")).toHaveAttribute("aria-expanded", "false");
|
|
42
|
-
await userEvent.click(getByText(container, "Top level"));
|
|
43
|
-
expect(queryByText(container, "Top level")).toHaveAttribute("aria-expanded", "true");
|
|
44
|
-
|
|
45
|
-
// One of the following approaches is used to enable scripts to move focus among items in a
|
|
46
|
-
// menu as described in Keyboard Navigation Inside Components:
|
|
47
|
-
// * The menu container has tabindex set to -1 or 0 and aria-activedescendant set to the ID
|
|
48
|
-
// of the focused item
|
|
49
|
-
// * Each item in the menu has tabindex set to -1, except in a menubar, where the first item
|
|
50
|
-
// has tabindex set to 0
|
|
51
|
-
|
|
52
|
-
// When a menuitemcheckbox or menuitemradio is checked, aria-checked is set to true
|
|
53
|
-
// NOTE: We don't (currently?) have checkboxes or radios in MenuBars
|
|
54
|
-
|
|
55
|
-
// When a menu item is disabled, aria-disabled is set to true
|
|
56
|
-
expect(queryByText(container, "Top level")).not.toHaveAttribute("aria-disabled");
|
|
57
|
-
expect(queryByText(container, "Disabled")).toHaveAttribute("aria-disabled", "true");
|
|
58
|
-
|
|
59
|
-
// Items in a menu may be divided into groups by placing an element with a role of separator
|
|
60
|
-
// between groups. For example, this technique should be used when a menu contains a set of
|
|
61
|
-
// menuitemradio items
|
|
62
|
-
expect(queryByText(container, "---")).toHaveAttribute("role", "separator");
|
|
63
|
-
|
|
64
|
-
// All separators should have aria-orientation consistent with the separator's orientation
|
|
65
|
-
// NOTE: What does this mean? None of the example separators have aria-orientation set
|
|
66
|
-
|
|
67
|
-
// If a menubar has a visible label, the element with role menubar has aria-labelledby set to
|
|
68
|
-
// a value that refers to the labelling element. Otherwise, the menubar element has a label
|
|
69
|
-
// provided by aria-label
|
|
70
|
-
// NOTE: We only support aria-label and it's required
|
|
71
|
-
expect(queryByText(container, "Top level")?.parentElement?.parentElement).toHaveAttribute(
|
|
72
|
-
"aria-label",
|
|
73
|
-
"Window menu",
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
// If a menubar is vertically oriented, it has aria-orientation set to vertical. The default
|
|
77
|
-
// value of aria-orientation for a menubar is horizontal
|
|
78
|
-
expect(queryByText(container, "Top level")?.parentElement?.parentElement).toHaveAttribute(
|
|
79
|
-
"aria-orientation",
|
|
80
|
-
"horizontal",
|
|
81
|
-
);
|
|
82
|
-
expect(queryByText(container, "Sub item 1")?.parentElement).toHaveAttribute(
|
|
83
|
-
"aria-orientation",
|
|
84
|
-
"vertical",
|
|
85
|
-
);
|
|
86
|
-
});
|
|
87
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { getByText, queryByText } from "@testing-library/dom";
|
|
2
|
-
import "@testing-library/jest-dom/vitest";
|
|
3
|
-
import userEvent from "@testing-library/user-event";
|
|
4
|
-
import { mount } from "@torpor/view";
|
|
5
|
-
import { describe, expect, it } from "vitest";
|
|
6
|
-
import MenuBarBasic from "./components/MenuBarBasic.torp";
|
|
7
|
-
|
|
8
|
-
describe("MenuBar", () => {
|
|
9
|
-
it("Basic operation", async () => {
|
|
10
|
-
const container = document.createElement("div");
|
|
11
|
-
document.body.appendChild(container);
|
|
12
|
-
mount(container, MenuBarBasic);
|
|
13
|
-
|
|
14
|
-
// The top level item should be displayed
|
|
15
|
-
expect(queryByText(container, "Top level")).toBeInTheDocument();
|
|
16
|
-
expect(queryByText(container, "Sub item 1")).not.toBeInTheDocument();
|
|
17
|
-
|
|
18
|
-
// Clicking the top level item should show the sub item
|
|
19
|
-
await userEvent.click(getByText(container, "Top level"));
|
|
20
|
-
expect(queryByText(container, "Sub item 1")).toBeInTheDocument();
|
|
21
|
-
});
|
|
22
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
MenuBar,
|
|
3
|
-
MenuBarItem,
|
|
4
|
-
MenuBarItemTrigger,
|
|
5
|
-
MenuBarItemContent
|
|
6
|
-
} from "../../../src/MenuBar";
|
|
7
|
-
import { Menu, MenuButton, MenuSeparator } from "../../../src/Menu";
|
|
8
|
-
|
|
9
|
-
export default function MenuBarAccessibility() {
|
|
10
|
-
@render {
|
|
11
|
-
<MenuBar ariaLabel="Window menu">
|
|
12
|
-
<MenuBarItem>
|
|
13
|
-
<MenuBarItemTrigger>Top level</MenuBarItemTrigger>
|
|
14
|
-
<MenuBarItemContent>
|
|
15
|
-
<Menu ariaLabel="Sub window menu">
|
|
16
|
-
<MenuButton>Sub item 1</MenuButton>
|
|
17
|
-
<MenuButton>Sub item 2</MenuButton>
|
|
18
|
-
<MenuSeparator>---</MenuSeparator>
|
|
19
|
-
<MenuButton>Sub item 3</MenuButton>
|
|
20
|
-
</Menu>
|
|
21
|
-
</MenuBarItemContent>
|
|
22
|
-
</MenuBarItem>
|
|
23
|
-
<MenuBarItem>
|
|
24
|
-
<MenuBarItemTrigger disabled>Disabled</MenuBarItemTrigger>
|
|
25
|
-
<MenuBarItemContent>
|
|
26
|
-
<Menu ariaLabel="Disabled sub window menu">
|
|
27
|
-
<MenuButton>Disabled sub 1</MenuButton>
|
|
28
|
-
<MenuButton>Disabled sub 2</MenuButton>
|
|
29
|
-
</Menu>
|
|
30
|
-
</MenuBarItemContent>
|
|
31
|
-
</MenuBarItem>
|
|
32
|
-
</MenuBar>
|
|
33
|
-
}
|
|
34
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
MenuBar,
|
|
3
|
-
MenuBarItem,
|
|
4
|
-
MenuBarItemTrigger,
|
|
5
|
-
MenuBarItemContent
|
|
6
|
-
} from "../../../src/MenuBar";
|
|
7
|
-
import { Menu, MenuButton, MenuSeparator } from "../../../src/Menu";
|
|
8
|
-
|
|
9
|
-
export default function MenuBarBasic() {
|
|
10
|
-
@render {
|
|
11
|
-
<MenuBar ariaLabel="Window menu">
|
|
12
|
-
<MenuBarItem>
|
|
13
|
-
<MenuBarItemTrigger>Top level</MenuBarItemTrigger>
|
|
14
|
-
<MenuBarItemContent>
|
|
15
|
-
<Menu ariaLabel="Sub window menu">
|
|
16
|
-
<MenuButton>Sub item 1</MenuButton>
|
|
17
|
-
<MenuButton>Sub item 2</MenuButton>
|
|
18
|
-
<MenuSeparator />
|
|
19
|
-
<MenuButton>Sub item 3</MenuButton>
|
|
20
|
-
</Menu>
|
|
21
|
-
</MenuBarItemContent>
|
|
22
|
-
</MenuBarItem>
|
|
23
|
-
</MenuBar>
|
|
24
|
-
}
|
|
25
|
-
}
|