@autoafleveren/ui 1.3.2 → 1.4.0
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/config/tailwind/config.cjs +1 -0
- package/dist/icons.cjs +17 -17
- package/dist/icons.js +1710 -1644
- package/dist/types/components/AppActionBar/AppActionBarItem.vue.d.ts +4 -0
- package/dist/types/composables/index.d.ts +2 -1
- package/dist/types/composables/useActionBar/index.d.ts +3 -3
- package/dist/types/composables/useFocusTrap/index.d.ts +10 -0
- package/dist/ui-storybook.css +1 -1
- package/dist/ui.cjs +47 -40
- package/dist/ui.js +12385 -11552
- package/package.json +5 -2
- package/src/App.vue +15 -0
- package/src/Playground.vue.example +9 -0
- package/src/config/eslint.cjs +199 -0
- package/src/config/tailwind/config.cjs +229 -0
- package/src/config/tailwind/screens.json +9 -0
- package/src/css/build.css +52 -0
- package/src/css/main.css +4 -0
- package/src/css/theme.css +208 -0
- package/src/css/tinymce.css +58 -0
- package/src/main.ts +34 -0
- package/src/modules/components/AppActionBar/AppActionBar.vue +96 -0
- package/src/modules/components/AppActionBar/AppActionBarItem.vue +149 -0
- package/src/modules/components/AppActionBar/AppActionBarSubMenu.vue +89 -0
- package/src/modules/components/AppActionBar/Components/Error.vue +11 -0
- package/src/modules/components/AppActionBar/Components/Loading.vue +9 -0
- package/src/modules/components/AppActionBar/Components/MultiSelect.vue +102 -0
- package/src/modules/components/AppActionBar/Components/__tests__/multi-select.spec.ts +74 -0
- package/src/modules/components/AppActionBar/__mocks__/index.ts +36 -0
- package/src/modules/components/AppActionBar/__tests__/app-action-bar-item.spec.ts +134 -0
- package/src/modules/components/AppActionBar/__tests__/app-action-bar-sub-menu.spec.ts +45 -0
- package/src/modules/components/AppActionBar/__tests__/app-action-bar.spec.ts +92 -0
- package/src/modules/components/AppActionBar/index.d.ts +29 -0
- package/src/modules/components/AppActionBar/index.ts +18 -0
- package/src/modules/components/AppAlert/AppAlert.vue +69 -0
- package/src/modules/components/AppAlert/__tests__/app-alert.spec.ts +67 -0
- package/src/modules/components/AppAlert/index.d.ts +3 -0
- package/src/modules/components/AppAlert/index.ts +18 -0
- package/src/modules/components/AppAvatar/AppAvatar.vue +30 -0
- package/src/modules/components/AppAvatar/DefaultAvatar.vue +187 -0
- package/src/modules/components/AppAvatar/__tests__/app-avatar.spec.ts +70 -0
- package/src/modules/components/AppAvatar/index.d.ts +9 -0
- package/src/modules/components/AppAvatar/index.ts +9 -0
- package/src/modules/components/AppBackButton/AppBackButton.vue +51 -0
- package/src/modules/components/AppBackButton/__tests__/app-back-button.spec.ts +70 -0
- package/src/modules/components/AppBadge/AppBadge.vue +65 -0
- package/src/modules/components/AppBadge/__tests__/app-badge.spec.ts +64 -0
- package/src/modules/components/AppBadge/index.d.ts +13 -0
- package/src/modules/components/AppBadge/index.ts +29 -0
- package/src/modules/components/AppButton/AppButton.vue +53 -0
- package/src/modules/components/AppButton/ButtonIconSlot.vue +26 -0
- package/src/modules/components/AppButton/__tests__/app-button.spec.ts +145 -0
- package/src/modules/components/AppButton/__tests__/button-icon-slot.spec.ts +30 -0
- package/src/modules/components/AppButton/index.d.ts +16 -0
- package/src/modules/components/AppButton/index.ts +57 -0
- package/src/modules/components/AppCard/AppCard.vue +88 -0
- package/src/modules/components/AppCard/CardAction.vue +101 -0
- package/src/modules/components/AppCard/CardIconSlot.vue +65 -0
- package/src/modules/components/AppCard/__tests__/app-card.spec.ts +109 -0
- package/src/modules/components/AppCard/__tests__/card-action.spec.ts +55 -0
- package/src/modules/components/AppCard/__tests__/card-icon-slot.spec.ts +27 -0
- package/src/modules/components/AppCard/index.d.ts +12 -0
- package/src/modules/components/AppCard/index.ts +5 -0
- package/src/modules/components/AppColor/AppColor.vue +74 -0
- package/src/modules/components/AppColor/__tests__/app-color.spec.ts +53 -0
- package/src/modules/components/AppColor/index.d.ts +10 -0
- package/src/modules/components/AppColorCard/AppColorCard.vue +41 -0
- package/src/modules/components/AppColorCard/__tests__/app-color-card.spec.ts +55 -0
- package/src/modules/components/AppConfirm/AppConfirm.vue +237 -0
- package/src/modules/components/AppConfirm/__tests__/app-confirm.spec.ts +366 -0
- package/src/modules/components/AppConfirm/index.d.ts +31 -0
- package/src/modules/components/AppConfirm/index.ts +1 -0
- package/src/modules/components/AppContextMenu/AppContextMenu.vue +109 -0
- package/src/modules/components/AppContextMenu/ShortcutItem.vue +38 -0
- package/src/modules/components/AppContextMenu/__mocks__/index.ts +25 -0
- package/src/modules/components/AppContextMenu/__tests__/app-context-menu.spec.ts +71 -0
- package/src/modules/components/AppContextMenu/__tests__/shortcut-item.spec.ts +29 -0
- package/src/modules/components/AppContextMenu/index.d.ts +23 -0
- package/src/modules/components/AppDataTable/AppDataTable.vue +323 -0
- package/src/modules/components/AppDataTable/AppDataTableFooter.vue +91 -0
- package/src/modules/components/AppDataTable/__mocks__/index.ts +71 -0
- package/src/modules/components/AppDataTable/__tests__/app-data-table-footer.spec.ts +107 -0
- package/src/modules/components/AppDataTable/__tests__/app-data-table.spec.ts +153 -0
- package/src/modules/components/AppDataTable/index.d.ts +29 -0
- package/src/modules/components/AppDefinitionList/AppDefinitionItem.vue +57 -0
- package/src/modules/components/AppDefinitionList/AppDefinitionList.vue +32 -0
- package/src/modules/components/AppDefinitionList/__mocks__/index.ts +31 -0
- package/src/modules/components/AppDefinitionList/__tests__/app-definition-item.spec.ts +93 -0
- package/src/modules/components/AppDefinitionList/__tests__/app-definition-list.spec.ts +35 -0
- package/src/modules/components/AppDefinitionList/index.d.ts +8 -0
- package/src/modules/components/AppDisclosure/AppDisclosure.vue +19 -0
- package/src/modules/components/AppDisclosure/AppDisclosureButton.vue +43 -0
- package/src/modules/components/AppDisclosure/AppDisclosurePanel.vue +31 -0
- package/src/modules/components/AppDisclosure/__tests__/app-disclosure-button.spec.ts +70 -0
- package/src/modules/components/AppDisclosure/__tests__/app-disclosure-panel.spec.ts +64 -0
- package/src/modules/components/AppDisclosure/__tests__/app-disclosure.spec.ts +41 -0
- package/src/modules/components/AppDrawer/AppDrawer.vue +149 -0
- package/src/modules/components/AppDrawer/__tests__/app-drawer.spec.ts +120 -0
- package/src/modules/components/AppDrawer/index.d.ts +27 -0
- package/src/modules/components/AppDropdownButton/AppDropdownButton.vue +82 -0
- package/src/modules/components/AppDropdownButton/AppDropdownItem.vue +67 -0
- package/src/modules/components/AppDropdownButton/__mocks__/index.ts +25 -0
- package/src/modules/components/AppDropdownButton/__tests__/app-dropdown-button.spec.ts +81 -0
- package/src/modules/components/AppDropdownButton/__tests__/app-dropdown-item.spec.ts +108 -0
- package/src/modules/components/AppDropdownButton/index.d.ts +26 -0
- package/src/modules/components/AppDropdownButton/index.ts +8 -0
- package/src/modules/components/AppError/AppError.vue +233 -0
- package/src/modules/components/AppError/__tests__/app-error.spec.ts +366 -0
- package/src/modules/components/AppError/index.d.ts +30 -0
- package/src/modules/components/AppError/index.ts +1 -0
- package/src/modules/components/AppImageDropzone/AppImageDropzone.vue +130 -0
- package/src/modules/components/AppImageDropzone/__tests__/app-image-dropzone.spec.ts +92 -0
- package/src/modules/components/AppImageDropzone/index.d.ts +8 -0
- package/src/modules/components/AppInput/AppInput.vue +247 -0
- package/src/modules/components/AppInput/FileInput.vue +58 -0
- package/src/modules/components/AppInput/Input.vue +141 -0
- package/src/modules/components/AppInput/InputIconSlot.vue +27 -0
- package/src/modules/components/AppInput/LocationInput.vue +150 -0
- package/src/modules/components/AppInput/__mocks__/location.ts +13 -0
- package/src/modules/components/AppInput/__tests__/app-input.spec.ts +255 -0
- package/src/modules/components/AppInput/__tests__/file-input.spec.ts +48 -0
- package/src/modules/components/AppInput/__tests__/input-icon-slot.spec.ts +27 -0
- package/src/modules/components/AppInput/__tests__/input.spec.ts +260 -0
- package/src/modules/components/AppInput/__tests__/location-input.spec.ts +159 -0
- package/src/modules/components/AppInput/choice.ts +24 -0
- package/src/modules/components/AppInput/datepicker.ts +62 -0
- package/src/modules/components/AppInput/index.d.ts +68 -0
- package/src/modules/components/AppInput/index.ts +133 -0
- package/src/modules/components/AppInput/location.ts +8 -0
- package/src/modules/components/AppInput/richText.ts +45 -0
- package/src/modules/components/AppInputLabel/AppInputLabel.vue +15 -0
- package/src/modules/components/AppInputLabel/__tests__/app-input-label.spec.ts +38 -0
- package/src/modules/components/AppInputLabel/index.d.ts +6 -0
- package/src/modules/components/AppLicensePlate/AppLicensePlate.vue +34 -0
- package/src/modules/components/AppLicensePlate/__tests__/app-license-plate.spec.ts +46 -0
- package/src/modules/components/AppLicensePlate/index.d.ts +1 -0
- package/src/modules/components/AppLoader/AppLoader.vue +37 -0
- package/src/modules/components/AppLoader/index.d.ts +1 -0
- package/src/modules/components/AppLoader/index.ts +8 -0
- package/src/modules/components/AppMaps/AppMaps.vue +105 -0
- package/src/modules/components/AppMaps/index.ts +44 -0
- package/src/modules/components/AppMenu/AppMenu.vue +79 -0
- package/src/modules/components/AppMenu/AppMenuItem.vue +40 -0
- package/src/modules/components/AppMenu/__mocks__/index.ts +23 -0
- package/src/modules/components/AppMenu/__tests__/app-menu-item.spec.ts +47 -0
- package/src/modules/components/AppMenu/__tests__/app-menu.spec.ts +53 -0
- package/src/modules/components/AppMenu/index.d.ts +15 -0
- package/src/modules/components/AppModal/AppModal.vue +261 -0
- package/src/modules/components/AppModal/__tests__/app-modal.spec.ts +282 -0
- package/src/modules/components/AppModal/index.d.ts +36 -0
- package/src/modules/components/AppNavigationMenu/AppNavigationMenu.vue +95 -0
- package/src/modules/components/AppNavigationMenu/Mobile.vue +126 -0
- package/src/modules/components/AppNavigationMenu/NavigationItem.vue +82 -0
- package/src/modules/components/AppNavigationMenu/SupportItem.vue +29 -0
- package/src/modules/components/AppNavigationMenu/__tests__/app-navigation-menu.spec.ts +104 -0
- package/src/modules/components/AppNavigationMenu/__tests__/mobile.spec.ts +155 -0
- package/src/modules/components/AppNavigationMenu/__tests__/navigation-item.spec.ts +91 -0
- package/src/modules/components/AppNavigationMenu/__tests__/support-item.spec.ts +48 -0
- package/src/modules/components/AppPagination/AppPagination.vue +133 -0
- package/src/modules/components/AppPagination/AppPaginationItem.vue +28 -0
- package/src/modules/components/AppPagination/__mocks__/index.ts +20 -0
- package/src/modules/components/AppPagination/__tests__/app-pagination.spec.ts +143 -0
- package/src/modules/components/AppPagination/index.d.ts +24 -0
- package/src/modules/components/AppProgressBar/AppProgressBar.vue +93 -0
- package/src/modules/components/AppProgressBar/AppProgressBarStep.vue +5 -0
- package/src/modules/components/AppProgressBar/__mocks__/index.ts +17 -0
- package/src/modules/components/AppProgressBar/__tests__/app-progress-bar-step.spec.ts +18 -0
- package/src/modules/components/AppProgressBar/__tests__/app-progress-bar.spec.ts +77 -0
- package/src/modules/components/AppProgressBar/index.d.ts +21 -0
- package/src/modules/components/AppRating/AppRating.vue +42 -0
- package/src/modules/components/AppRating/VueStarRating/Star.vue +215 -0
- package/src/modules/components/AppRating/VueStarRating/StarRating.vue +231 -0
- package/src/modules/components/AppRating/VueStarRating/classes/AlphaColor.ts +68 -0
- package/src/modules/components/AppRating/VueStarRating/readme.md +279 -0
- package/src/modules/components/AppRating/__tests__/app-rating.spec.ts +36 -0
- package/src/modules/components/AppSection/AppSection.vue +35 -0
- package/src/modules/components/AppSection/__tests__/app-section.spec.ts +53 -0
- package/src/modules/components/AppSelect/AppSelect.vue +176 -0
- package/src/modules/components/AppSelect/__mocks__/index.ts +24 -0
- package/src/modules/components/AppSelect/__tests__/app-select.spec.ts +73 -0
- package/src/modules/components/AppSelect/index.d.ts +43 -0
- package/src/modules/components/AppSelect/index.ts +69 -0
- package/src/modules/components/AppStepper/AppStepper.vue +79 -0
- package/src/modules/components/AppStepper/__tests__/app-stepper.spec.ts +59 -0
- package/src/modules/components/AppTable/AppTable.vue +40 -0
- package/src/modules/components/AppTimeline/AppTimeline.vue +22 -0
- package/src/modules/components/AppTimeline/AppTimelineItem.vue +97 -0
- package/src/modules/components/AppTimeline/AppTimelineItemIcon.vue +55 -0
- package/src/modules/components/AppTimeline/__mocks__/timeline.ts +29 -0
- package/src/modules/components/AppTimeline/__tests__/app-timeline-item-Icon.spec.ts +35 -0
- package/src/modules/components/AppTimeline/__tests__/app-timeline-item.spec.ts +121 -0
- package/src/modules/components/AppTimeline/__tests__/app-timeline.spec.ts +55 -0
- package/src/modules/components/AppTimeline/index.d.ts +30 -0
- package/src/modules/components/AppTimeline/index.ts +13 -0
- package/src/modules/components/AppToggle/AppToggle.vue +36 -0
- package/src/modules/components/AppToggle/__tests__/app-toggle.spec.ts +54 -0
- package/src/modules/components/AppToggle/index.d.ts +3 -0
- package/src/modules/components/AppToggleCard/AppToggleCard.vue +45 -0
- package/src/modules/components/AppToggleCard/__tests__/app-toggle-card.spec.ts +55 -0
- package/src/modules/components/index.ts +43 -0
- package/src/modules/composables/index.ts +14 -0
- package/src/modules/composables/useActionBar/__mocks__/index.ts +17 -0
- package/src/modules/composables/useActionBar/__tests__/index.spec.ts +62 -0
- package/src/modules/composables/useActionBar/index.d.ts +1 -0
- package/src/modules/composables/useActionBar/index.ts +67 -0
- package/src/modules/composables/useComputedPosition/index.d.ts +16 -0
- package/src/modules/composables/useComputedPosition/index.ts +199 -0
- package/src/modules/composables/useConfirm/__tests__/index.spec.ts +29 -0
- package/src/modules/composables/useConfirm/index.ts +63 -0
- package/src/modules/composables/useContextMenu/index.ts +127 -0
- package/src/modules/composables/useDrawer/__tests__/index.spec.ts +34 -0
- package/src/modules/composables/useDrawer/index.ts +136 -0
- package/src/modules/composables/useEcho/index.ts +167 -0
- package/src/modules/composables/useError/__tests__/index.spec.ts +29 -0
- package/src/modules/composables/useError/index.ts +61 -0
- package/src/modules/composables/useFocusTrap/index.d.ts +3 -0
- package/src/modules/composables/useFocusTrap/index.ts +98 -0
- package/src/modules/composables/useGoogleApi/__tests__/index.spec.ts +39 -0
- package/src/modules/composables/useGoogleApi/index.ts +26 -0
- package/src/modules/composables/useLayout/__tests__/index.spec.ts +34 -0
- package/src/modules/composables/useLayout/index.d.ts +1 -0
- package/src/modules/composables/useLayout/index.ts +68 -0
- package/src/modules/composables/useModal/__tests__/index.spec.ts +34 -0
- package/src/modules/composables/useModal/index.ts +97 -0
- package/src/modules/composables/useNavigation/__mocks__/navigation.ts +22 -0
- package/src/modules/composables/useNavigation/__tests__/index.spec.ts +88 -0
- package/src/modules/composables/useNavigation/index.d.ts +17 -0
- package/src/modules/composables/useNavigation/index.ts +97 -0
- package/src/modules/icons/BuildingCircleCheck.vue +32 -0
- package/src/modules/icons/BuildingCircleXmark.vue +20 -0
- package/src/modules/icons/CarsIcon.vue +29 -0
- package/src/modules/icons/ChatPersonRoundedIcon.vue +184 -0
- package/src/modules/icons/CompanyIcon.vue +18 -0
- package/src/modules/icons/HeroGirlIcon.vue +246 -0
- package/src/modules/icons/HeroPersonIcon.vue +402 -0
- package/src/modules/icons/HeroPersonRoundedIcon.vue +412 -0
- package/src/modules/icons/HeroPersonWithBgIcon.vue +4503 -0
- package/src/modules/icons/LocationMarkerIcon.vue +33 -0
- package/src/modules/icons/PartyPopperIcon.vue +146 -0
- package/src/modules/icons/index.ts +32 -0
- package/src/modules/icons/status/ErrorIcon.vue +24 -0
- package/src/modules/icons/status/SuccessIcon.vue +24 -0
- package/src/modules/icons/status/WarningIcon.vue +27 -0
- package/src/modules/icons/status/index.ts +3 -0
- package/src/modules/index.ts +8 -0
- package/src/modules/layouts/Auth/Auth.vue +36 -0
- package/src/modules/layouts/Auth/__tests__/auth.spec.ts +63 -0
- package/src/modules/layouts/Base/Base.vue +69 -0
- package/src/modules/layouts/Base/__tests__/base.spec.ts +56 -0
- package/src/modules/layouts/Platform/Platform.vue +96 -0
- package/src/modules/layouts/Platform/__tests__/platform.spec.ts +56 -0
- package/src/modules/layouts/index.ts +9 -0
- package/src/modules/plugins/Sentry/index.d.ts +16 -0
- package/src/modules/plugins/Sentry/index.ts +65 -0
- package/src/modules/plugins/Sentry/language/nl.ts +13 -0
- package/src/modules/plugins/TinyMCE/lang/nl.js +430 -0
- package/src/modules/plugins/Toast/Toast.vue +58 -0
- package/src/modules/plugins/Toast/__tests__/toast.spec.ts +90 -0
- package/src/modules/plugins/Toast/index.ts +36 -0
- package/src/modules/plugins/Toast/types.d.ts +265 -0
- package/src/modules/plugins/index.ts +63 -0
- package/src/stories/Introduction.mdx +4 -0
- package/src/stories/assets/code-brackets.svg +1 -0
- package/src/stories/assets/colors.svg +1 -0
- package/src/stories/assets/comments.svg +1 -0
- package/src/stories/assets/direction.svg +1 -0
- package/src/stories/assets/flow.svg +1 -0
- package/src/stories/assets/images/logo.png +0 -0
- package/src/stories/assets/images/road.png +0 -0
- package/src/stories/assets/plugin.svg +1 -0
- package/src/stories/assets/repo.svg +1 -0
- package/src/stories/assets/stackalt.svg +1 -0
- package/src/stories/components/ActionBar/ActionBar.stories.ts +67 -0
- package/src/stories/components/Alert/Alert.stories.ts +53 -0
- package/src/stories/components/Avatar/Avatar.stories.ts +44 -0
- package/src/stories/components/BackButton/BackButton.stories.ts +39 -0
- package/src/stories/components/Badge/Badge.stories.ts +42 -0
- package/src/stories/components/Button/Button.stories.ts +132 -0
- package/src/stories/components/Card/Card.stories.ts +70 -0
- package/src/stories/components/Color/Color.stories.ts +41 -0
- package/src/stories/components/ColorCard/ColorCard.stories.ts +43 -0
- package/src/stories/components/Confirm/Confirm.stories.ts +110 -0
- package/src/stories/components/ContextMenu/ContextMenu.stories.ts +85 -0
- package/src/stories/components/DefinitionList/DefinitionList.stories.ts +32 -0
- package/src/stories/components/Disclosure/Disclosure.stories.ts +61 -0
- package/src/stories/components/DropdownButton/DropdownButton.stories.ts +121 -0
- package/src/stories/components/Error/Error.stories.ts +106 -0
- package/src/stories/components/ImageDropzone/ImageDropzone.stories.ts +41 -0
- package/src/stories/components/Input/Input.stories.ts +180 -0
- package/src/stories/components/Input/LocationInput.stories.ts +77 -0
- package/src/stories/components/LicensePlate/LicensePlate.stories.ts +39 -0
- package/src/stories/components/Maps/Maps.stories.ts +36 -0
- package/src/stories/components/Menu/Menu.stories.ts +41 -0
- package/src/stories/components/Modal/Modal.stories.ts +68 -0
- package/src/stories/components/Navigation/Navigation.stories.ts +62 -0
- package/src/stories/components/Pagination/Pagination.stories.ts +62 -0
- package/src/stories/components/ProgressBar/ProgressBar.stories.ts +48 -0
- package/src/stories/components/Rating/Rating.stories.ts +38 -0
- package/src/stories/components/Section/Section.stories.ts +44 -0
- package/src/stories/components/Select/Select.stories.ts +90 -0
- package/src/stories/components/Stepper/Stepper.stories.ts +38 -0
- package/src/stories/components/Table/DataTable.stories.ts +96 -0
- package/src/stories/components/Table/Table.stories.ts +45 -0
- package/src/stories/components/Timeline/Timeline.stories.ts +46 -0
- package/src/stories/components/Toast/Toast.stories.ts +47 -0
- package/src/stories/components/Toggle/Toggle.stories.ts +41 -0
- package/src/stories/components/ToggleCard/ToggleCard.stories.ts +43 -0
- package/src/stories/layouts/Auth.stories.ts +43 -0
- package/src/stories/layouts/Base.stories.ts +70 -0
- package/src/tests/mocks/resize-observer.ts +13 -0
- package/src/tests/stubs/AppSelect.ts +89 -0
- package/src/tests/stubs/HeadlessUiDialogStub.ts +24 -0
- package/src/tests/stubs/HeadlessUiTransitionChildStub.ts +20 -0
- package/src/tests/stubs/HeadlessUiTransitionRootStub.ts +25 -0
- package/src/tests/stubs/IconStub.ts +9 -0
- package/src/tests/stubs/Vue3EasyDataTableStub.ts +53 -0
- package/src/typings/plugin.d.ts +5 -0
- package/src/typings/shims-vue.d.ts +13 -0
- package/src/typings/utilities.d.ts +4 -0
- package/src/typings/vite-environment.d.ts +12 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { shallowMount } from '@vue/test-utils';
|
|
3
|
+
import AppButton from '../AppButton.vue';
|
|
4
|
+
|
|
5
|
+
import type ButtonIconSlot from '../ButtonIconSlot.vue';
|
|
6
|
+
|
|
7
|
+
const leadingIconSelector = '[data-test-leading-icon]';
|
|
8
|
+
const trailingIconSelector = '[data-test-trailing-icon]';
|
|
9
|
+
|
|
10
|
+
describe('the AppButton component', () => {
|
|
11
|
+
it('should render a button element as root element', () => {
|
|
12
|
+
const wrapper = shallowMount(AppButton);
|
|
13
|
+
expect(wrapper.element).toBeInstanceOf(HTMLButtonElement);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should render a custom element as root element when provided', () => {
|
|
17
|
+
const wrapper = shallowMount(AppButton, {
|
|
18
|
+
props: { as: 'span' },
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
expect(wrapper.element).toBeInstanceOf(HTMLSpanElement);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should set the button type to \'button\' by default', () => {
|
|
25
|
+
const wrapper = shallowMount(AppButton);
|
|
26
|
+
expect(wrapper.attributes('type')).toBe('button');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('can change the button type', () => {
|
|
30
|
+
const wrapper = shallowMount(AppButton, {
|
|
31
|
+
props: { type: 'submit' },
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
expect(wrapper.attributes('type')).toBe('submit');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('can set the button disabled state', () => {
|
|
38
|
+
const wrapper = shallowMount(AppButton, {
|
|
39
|
+
props: { disabled: true },
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(wrapper.attributes('disabled')).toBeDefined();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should not be disabled by default', () => {
|
|
46
|
+
const wrapper = shallowMount(AppButton);
|
|
47
|
+
expect(wrapper.attributes('disabled')).toBeUndefined();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should be disabled when setting the loading prop to true', () => {
|
|
51
|
+
const wrapper = shallowMount(AppButton, {
|
|
52
|
+
props: { loading: true },
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
expect(wrapper.attributes('disabled')).toBeDefined();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('renders the default slot', () => {
|
|
59
|
+
const buttonText = 'Button text';
|
|
60
|
+
const wrapper = shallowMount(AppButton, {
|
|
61
|
+
slots: { default: buttonText },
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
expect(wrapper.text()).toBe(buttonText);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should not render text in the button by default', () => {
|
|
68
|
+
const wrapper = shallowMount(AppButton);
|
|
69
|
+
expect(wrapper.text()).toBe('');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('can set an icon before the button text', () => {
|
|
73
|
+
const iconSelector = 'data-test-leading-icon';
|
|
74
|
+
const leadingIcon = `<i ${iconSelector}>Icon</i>`;
|
|
75
|
+
const wrapper = shallowMount(AppButton, {
|
|
76
|
+
slots: { default: 'Button text', leadingIcon },
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const leadingIconComponent = wrapper
|
|
80
|
+
.findComponent<typeof ButtonIconSlot>(leadingIconSelector);
|
|
81
|
+
expect(leadingIconComponent.exists()).toBe(true);
|
|
82
|
+
expect(leadingIconComponent.props('loading')).toBe(false);
|
|
83
|
+
|
|
84
|
+
const iconElement = leadingIconComponent.find('[data-test-leading-icon]');
|
|
85
|
+
expect(iconElement.exists()).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should set the ButtonIconSlot loading prop to true if button loading prop is true', () => {
|
|
89
|
+
const iconSelector = 'data-test-leading-icon';
|
|
90
|
+
const leadingIcon = `<i ${iconSelector}>Icon</i>`;
|
|
91
|
+
const wrapper = shallowMount(AppButton, {
|
|
92
|
+
slots: { default: 'Button text', leadingIcon },
|
|
93
|
+
props: { loading: true },
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const leadingIconComponent = wrapper
|
|
97
|
+
.findComponent<typeof ButtonIconSlot>(leadingIconSelector);
|
|
98
|
+
expect(leadingIconComponent.exists()).toBe(true);
|
|
99
|
+
expect(leadingIconComponent.props('loading')).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('can set an icon after the button text', () => {
|
|
103
|
+
const iconSelector = 'data-test-trailing-icon';
|
|
104
|
+
const trailingIcon = `<i ${iconSelector}>Icon</i>`;
|
|
105
|
+
const wrapper = shallowMount(AppButton, {
|
|
106
|
+
slots: { default: 'Button text', trailingIcon },
|
|
107
|
+
props: { loading: true },
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const trailingIconComponent = wrapper
|
|
111
|
+
.findComponent<typeof ButtonIconSlot>(trailingIconSelector);
|
|
112
|
+
expect(trailingIconComponent.exists()).toBe(true);
|
|
113
|
+
expect(trailingIconComponent.props('loading')).toBe(false);
|
|
114
|
+
|
|
115
|
+
const iconElement = trailingIconComponent.find(`[${iconSelector}]`);
|
|
116
|
+
expect(iconElement.exists()).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should not render icons by default', () => {
|
|
120
|
+
const wrapper = shallowMount(AppButton);
|
|
121
|
+
|
|
122
|
+
const leadingIconComponent = wrapper
|
|
123
|
+
.findComponent<typeof ButtonIconSlot>(leadingIconSelector);
|
|
124
|
+
expect(leadingIconComponent.exists()).toBe(false);
|
|
125
|
+
|
|
126
|
+
const trailingIconComponent = wrapper
|
|
127
|
+
.findComponent<typeof ButtonIconSlot>(trailingIconSelector);
|
|
128
|
+
expect(trailingIconComponent.exists()).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it(
|
|
132
|
+
'should render the ButtonIconSlot when loading is true and no leadingIcon slot is provided',
|
|
133
|
+
() => {
|
|
134
|
+
const wrapper = shallowMount(AppButton, {
|
|
135
|
+
props: { loading: true },
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const leadingIconComponent = wrapper
|
|
139
|
+
.findComponent<typeof ButtonIconSlot>(leadingIconSelector);
|
|
140
|
+
expect(leadingIconComponent.exists()).toBe(true);
|
|
141
|
+
expect(leadingIconComponent.props('loading')).toBe(true);
|
|
142
|
+
expect(leadingIconComponent.element.innerHTML).toBe('');
|
|
143
|
+
},
|
|
144
|
+
);
|
|
145
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { shallowMount } from '@vue/test-utils';
|
|
3
|
+
import ButtonIconSlot from '../ButtonIconSlot.vue';
|
|
4
|
+
import AppLoader from '../../AppLoader/AppLoader.vue';
|
|
5
|
+
|
|
6
|
+
describe('the ButtonIconSlot component', () => {
|
|
7
|
+
it('should render the default slot by default', () => {
|
|
8
|
+
const icon = '<i>Icon</i>';
|
|
9
|
+
const wrapper = shallowMount(ButtonIconSlot, {
|
|
10
|
+
slots: { default: icon },
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
expect(wrapper.element.innerHTML).toBe(icon);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should render a loader when loading prop is true', () => {
|
|
17
|
+
const iconSelector = 'data-test-icon';
|
|
18
|
+
const icon = `<i ${iconSelector}>Icon</i>`;
|
|
19
|
+
const wrapper = shallowMount(ButtonIconSlot, {
|
|
20
|
+
props: { loading: true },
|
|
21
|
+
slots: { default: icon },
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const loader = wrapper.findComponent(AppLoader);
|
|
25
|
+
expect(loader.exists()).toBe(true);
|
|
26
|
+
|
|
27
|
+
const iconElement = wrapper.find(`[${iconSelector}]`);
|
|
28
|
+
expect(iconElement.exists()).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ButtonHTMLAttributes } from 'vue';
|
|
2
|
+
|
|
3
|
+
export type ColorType = 'primary' | 'secondary' | 'white' | 'plain';
|
|
4
|
+
export type Size = 'small' | 'medium' | 'large';
|
|
5
|
+
export type Type = NonNullable<ButtonHTMLAttributes['type']>;
|
|
6
|
+
|
|
7
|
+
export interface Props {
|
|
8
|
+
size?: Size;
|
|
9
|
+
type?: Type;
|
|
10
|
+
colorType?: ColorType;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
loading?: boolean;
|
|
13
|
+
bordered?: boolean;
|
|
14
|
+
block?: boolean;
|
|
15
|
+
as?: string;
|
|
16
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ColorType, Size } from './index.d';
|
|
2
|
+
|
|
3
|
+
export const domClassesPerColorType: Record<ColorType, string[]> = {
|
|
4
|
+
plain: [
|
|
5
|
+
'text-primary',
|
|
6
|
+
'border-transparent',
|
|
7
|
+
],
|
|
8
|
+
white: [
|
|
9
|
+
'text-secondary',
|
|
10
|
+
'bg-white',
|
|
11
|
+
'border-white',
|
|
12
|
+
],
|
|
13
|
+
primary: [
|
|
14
|
+
'text-white',
|
|
15
|
+
'bg-primary',
|
|
16
|
+
'border-transparent',
|
|
17
|
+
'enabled:hover:bg-primary-active',
|
|
18
|
+
],
|
|
19
|
+
secondary: [
|
|
20
|
+
'text-white',
|
|
21
|
+
'bg-secondary',
|
|
22
|
+
'border-transparent',
|
|
23
|
+
'enabled:hover:bg-secondary-active',
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const domClassesPerColorBorderedType: Record<ColorType, string[]> = {
|
|
28
|
+
plain: [
|
|
29
|
+
'text-primary',
|
|
30
|
+
'border-transparent',
|
|
31
|
+
],
|
|
32
|
+
white: [
|
|
33
|
+
'text-secondary',
|
|
34
|
+
'bg-white',
|
|
35
|
+
'border-secondary',
|
|
36
|
+
],
|
|
37
|
+
primary: [
|
|
38
|
+
'text-primary',
|
|
39
|
+
'bg-transparent',
|
|
40
|
+
'border-primary',
|
|
41
|
+
'enabled:hover:text-white',
|
|
42
|
+
'enabled:hover:bg-primary',
|
|
43
|
+
],
|
|
44
|
+
secondary: [
|
|
45
|
+
'text-secondary',
|
|
46
|
+
'bg-transparent',
|
|
47
|
+
'border-secondary',
|
|
48
|
+
'enabled:hover:text-white',
|
|
49
|
+
'enabled:hover:bg-secondary',
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const domClassesPerSize: Record<Size, string[]> = {
|
|
54
|
+
small: ['px-5', 'py-1.5'],
|
|
55
|
+
medium: ['px-5', 'py-2.5', 'h-[42px]'],
|
|
56
|
+
large: ['px-5', 'py-3.5'],
|
|
57
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed, useSlots } from 'vue';
|
|
3
|
+
import CardIconSlot from './CardIconSlot.vue';
|
|
4
|
+
import CardAction from './CardAction.vue';
|
|
5
|
+
|
|
6
|
+
import type { Component } from 'vue';
|
|
7
|
+
import type { Action } from './index.d';
|
|
8
|
+
|
|
9
|
+
const props = withDefaults(defineProps<{
|
|
10
|
+
title?: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
actions?: Action[];
|
|
13
|
+
icon?: string | Component;
|
|
14
|
+
bordered?: boolean;
|
|
15
|
+
contentClasses?: string;
|
|
16
|
+
hoverTransition?: boolean;
|
|
17
|
+
}>(), {
|
|
18
|
+
title: undefined,
|
|
19
|
+
description: undefined,
|
|
20
|
+
actions: undefined,
|
|
21
|
+
icon: undefined,
|
|
22
|
+
bordered: false,
|
|
23
|
+
contentClasses: undefined,
|
|
24
|
+
hoverTransition: false,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const slots = useSlots();
|
|
28
|
+
|
|
29
|
+
const hasTitle = computed(() => !!slots.title || !!props.title);
|
|
30
|
+
const hasIcon = computed(() => !!slots.icon || !!props.icon);
|
|
31
|
+
const contentClasses = computed(() => `${props.contentClasses} ${hasTitle.value ? 'mt-4' : undefined}`);
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<template>
|
|
35
|
+
<div
|
|
36
|
+
:class="{
|
|
37
|
+
'before:hidden': !bordered,
|
|
38
|
+
'cursor-pointer transition hover:drop-shadow-cardhighlight': (actions && actions.length > 0) || hoverTransition,
|
|
39
|
+
}"
|
|
40
|
+
class="group relative w-full overflow-hidden rounded-2xl bg-white px-6 py-5 text-zinc-600 drop-shadow-card
|
|
41
|
+
before:absolute before:left-0 before:top-0 before:h-full before:w-1.5 before:bg-primary"
|
|
42
|
+
>
|
|
43
|
+
<CardIconSlot
|
|
44
|
+
v-if="hasIcon"
|
|
45
|
+
:icon="icon"
|
|
46
|
+
:class="{ 'mt-1.5': hasTitle }"
|
|
47
|
+
data-test-icon
|
|
48
|
+
>
|
|
49
|
+
<slot name="icon"></slot>
|
|
50
|
+
</CardIconSlot>
|
|
51
|
+
|
|
52
|
+
<h3
|
|
53
|
+
v-if="hasTitle"
|
|
54
|
+
class="text-2xl font-bold text-zinc-900"
|
|
55
|
+
data-test-title
|
|
56
|
+
>
|
|
57
|
+
<slot name="title">
|
|
58
|
+
{{ title }}
|
|
59
|
+
</slot>
|
|
60
|
+
|
|
61
|
+
<slot name="description">
|
|
62
|
+
<p
|
|
63
|
+
class="text-base font-normal text-zinc-600"
|
|
64
|
+
data-test-description
|
|
65
|
+
>
|
|
66
|
+
{{ description }}
|
|
67
|
+
</p>
|
|
68
|
+
</slot>
|
|
69
|
+
</h3>
|
|
70
|
+
|
|
71
|
+
<div
|
|
72
|
+
:class="contentClasses"
|
|
73
|
+
data-test-content
|
|
74
|
+
>
|
|
75
|
+
<slot></slot>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<slot name="actions">
|
|
79
|
+
<div
|
|
80
|
+
v-for="action in actions"
|
|
81
|
+
:key="action.name"
|
|
82
|
+
class="mt-2 flex w-full"
|
|
83
|
+
>
|
|
84
|
+
<CardAction :item="action" />
|
|
85
|
+
</div>
|
|
86
|
+
</slot>
|
|
87
|
+
</div>
|
|
88
|
+
</template>
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed, h, ref } from 'vue';
|
|
3
|
+
import { useNavigation } from '~composables';
|
|
4
|
+
import { FontAwesomeIcon } from '~icons';
|
|
5
|
+
import { AppLoader } from '~components';
|
|
6
|
+
import { strokeClasses } from '.';
|
|
7
|
+
|
|
8
|
+
import type { Component } from 'vue';
|
|
9
|
+
import type { FontAwesomeIconProps } from '@fortawesome/vue-fontawesome';
|
|
10
|
+
import type { Action } from './index.d';
|
|
11
|
+
|
|
12
|
+
const props = defineProps<{ item: Action }>();
|
|
13
|
+
|
|
14
|
+
const { navigationComponent } = useNavigation();
|
|
15
|
+
const isLoading = ref(false);
|
|
16
|
+
|
|
17
|
+
const isFontAwesomeIcon = computed((): boolean => {
|
|
18
|
+
if (typeof props.item.icon === 'string') {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// @ts-expect-error prefix not always exists in type
|
|
23
|
+
return typeof props.item.icon?.prefix === 'string' || Array.isArray(props.item.icon);
|
|
24
|
+
});
|
|
25
|
+
const actionIcon = computed((): string | Component => {
|
|
26
|
+
if (isLoading.value) {
|
|
27
|
+
return AppLoader;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (isFontAwesomeIcon.value) {
|
|
31
|
+
return h(FontAwesomeIcon, { icon: props.item.icon as FontAwesomeIconProps['icon'] });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return (props.item.icon as string | Component);
|
|
35
|
+
});
|
|
36
|
+
const iconIsComponent = computed((): boolean => isLoading.value || typeof props.item.icon !== 'string');
|
|
37
|
+
const iconClasses = computed(() => [
|
|
38
|
+
!iconIsComponent.value ? 'h-6!' : '',
|
|
39
|
+
strokeClasses[props.item.iconStroke ?? 3],
|
|
40
|
+
]);
|
|
41
|
+
const isFontAwesomeNode = computed((): boolean => {
|
|
42
|
+
// @ts-expect-error props not always exists in type
|
|
43
|
+
return typeof props.item.icon === 'object' && props.item.icon?.props?.icon !== undefined;
|
|
44
|
+
});
|
|
45
|
+
const component = computed((): string | Component => {
|
|
46
|
+
if (isFontAwesomeIcon.value) {
|
|
47
|
+
return actionIcon.value;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (typeof props.item.icon === 'string') {
|
|
51
|
+
return 'span';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return (props.item.icon as string | Component);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
async function onClick(event: Event): Promise<void> {
|
|
58
|
+
if (!props.item?.onClick) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
|
|
64
|
+
isLoading.value = true;
|
|
65
|
+
|
|
66
|
+
await Promise.resolve(props.item?.onClick?.());
|
|
67
|
+
|
|
68
|
+
isLoading.value = false;
|
|
69
|
+
}
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<template>
|
|
73
|
+
<Component
|
|
74
|
+
:is="navigationComponent"
|
|
75
|
+
:to="item.to"
|
|
76
|
+
:href="item.href"
|
|
77
|
+
:class="{ 'flex-row-reverse': item?.iconPosition === 'right' }"
|
|
78
|
+
class="app-card__action ml-auto flex items-center justify-center font-bold text-zinc-900 transition-colors group-hover:text-primary"
|
|
79
|
+
@click="onClick"
|
|
80
|
+
>
|
|
81
|
+
<Component
|
|
82
|
+
:is="component"
|
|
83
|
+
:class="iconClasses"
|
|
84
|
+
:size="isLoading ? 'extra-small' : undefined"
|
|
85
|
+
class="size-4"
|
|
86
|
+
data-test-icon
|
|
87
|
+
v-html="(isFontAwesomeIcon || isFontAwesomeNode) ? undefined : actionIcon"
|
|
88
|
+
/>
|
|
89
|
+
|
|
90
|
+
<span
|
|
91
|
+
:class="{
|
|
92
|
+
'pl-2': item.icon && item?.iconPosition !== 'right',
|
|
93
|
+
'pr-2': item.icon && item?.iconPosition === 'right',
|
|
94
|
+
}"
|
|
95
|
+
class="text-sm"
|
|
96
|
+
data-test-name
|
|
97
|
+
>
|
|
98
|
+
{{ item.name }}
|
|
99
|
+
</span>
|
|
100
|
+
</Component>
|
|
101
|
+
</template>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed, h } from 'vue';
|
|
3
|
+
import { FontAwesomeIcon } from '~icons';
|
|
4
|
+
|
|
5
|
+
import type { Component } from 'vue';
|
|
6
|
+
import type { FontAwesomeIconProps } from '@fortawesome/vue-fontawesome';
|
|
7
|
+
|
|
8
|
+
const props = defineProps<{ icon?: string | Component | FontAwesomeIconProps['icon'] }>();
|
|
9
|
+
|
|
10
|
+
const isFontAwesomeIcon = computed((): boolean => {
|
|
11
|
+
if (typeof props.icon === 'string') {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// @ts-expect-error prefix not always exists in type
|
|
16
|
+
return typeof props.icon?.prefix === 'string';
|
|
17
|
+
});
|
|
18
|
+
const isFontAwesomeNode = computed((): boolean => {
|
|
19
|
+
// @ts-expect-error props not always exists in type
|
|
20
|
+
return typeof props.icon === 'object' && props.icon?.props?.icon !== undefined;
|
|
21
|
+
});
|
|
22
|
+
const cardIcon = computed((): string | Component => {
|
|
23
|
+
if (isFontAwesomeIcon.value) {
|
|
24
|
+
return h(FontAwesomeIcon, { icon: props.icon as FontAwesomeIconProps['icon'] });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (props.icon as string | Component);
|
|
28
|
+
});
|
|
29
|
+
const component = computed((): string | Component => {
|
|
30
|
+
if (isFontAwesomeIcon.value) {
|
|
31
|
+
return cardIcon.value;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (typeof props.icon === 'string') {
|
|
35
|
+
return 'span';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return (props.icon as string | Component);
|
|
39
|
+
});
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<template>
|
|
43
|
+
<div
|
|
44
|
+
v-if="$slots.default || icon"
|
|
45
|
+
class="app-card__icon float-right text-primary"
|
|
46
|
+
>
|
|
47
|
+
<slot>
|
|
48
|
+
<Component
|
|
49
|
+
:is="component"
|
|
50
|
+
v-if="cardIcon"
|
|
51
|
+
:class="{ 'size-5': !isFontAwesomeIcon, 'size-4': isFontAwesomeIcon }"
|
|
52
|
+
data-test-icon-component
|
|
53
|
+
v-html="(isFontAwesomeIcon || isFontAwesomeNode) ? undefined : cardIcon"
|
|
54
|
+
/>
|
|
55
|
+
</slot>
|
|
56
|
+
</div>
|
|
57
|
+
</template>
|
|
58
|
+
|
|
59
|
+
<style scoped>
|
|
60
|
+
@reference "./../../../css/main.css";
|
|
61
|
+
|
|
62
|
+
.app-card__icon > :deep(*) {
|
|
63
|
+
@apply h-full!;
|
|
64
|
+
}
|
|
65
|
+
</style>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { shallowMount } from '@vue/test-utils';
|
|
3
|
+
import AppCard from '../AppCard.vue';
|
|
4
|
+
import CardIconSlot from '../CardIconSlot.vue';
|
|
5
|
+
|
|
6
|
+
describe('the AppCard component', () => {
|
|
7
|
+
it('should hide the optional elements by default', () => {
|
|
8
|
+
const wrapper = shallowMount(AppCard);
|
|
9
|
+
|
|
10
|
+
expect(wrapper.find('[data-test-title]').exists()).toBe(false);
|
|
11
|
+
expect(wrapper.find('[data-test-icon]').exists()).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should hide the left border by default', () => {
|
|
15
|
+
const wrapper = shallowMount(AppCard);
|
|
16
|
+
|
|
17
|
+
expect(wrapper.classes('before:hidden')).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should show the left border when enabled by prop', () => {
|
|
21
|
+
const wrapper = shallowMount(AppCard, {
|
|
22
|
+
props: { bordered: true },
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
expect(wrapper.classes('before:hidden')).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should render the default slot', () => {
|
|
29
|
+
const content = 'content';
|
|
30
|
+
|
|
31
|
+
const wrapper = shallowMount(AppCard, {
|
|
32
|
+
slots: { default: content },
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const contentElement = wrapper.find('[data-test-content]');
|
|
36
|
+
expect(contentElement.exists()).toBe(true);
|
|
37
|
+
expect(contentElement.text()).toBe(content);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('can set the title by prop', () => {
|
|
41
|
+
const wrapper = shallowMount(AppCard, {
|
|
42
|
+
props: { title: 'title' },
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const titleElement = wrapper.find('[data-test-title]');
|
|
46
|
+
expect(titleElement.exists()).toBe(true);
|
|
47
|
+
expect(titleElement.text()).toBe('title');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('can set the description by prop', () => {
|
|
51
|
+
const wrapper = shallowMount(AppCard, {
|
|
52
|
+
props: { title: 'title', description: 'description' },
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const descriptionElement = wrapper.find('[data-test-description]');
|
|
56
|
+
expect(descriptionElement.exists()).toBe(true);
|
|
57
|
+
expect(descriptionElement.text()).toBe('description');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('can set the title by slot', () => {
|
|
61
|
+
const wrapper = shallowMount(AppCard, {
|
|
62
|
+
slots: { title: 'title' },
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const titleElement = wrapper.find('[data-test-title]');
|
|
66
|
+
expect(titleElement.exists()).toBe(true);
|
|
67
|
+
expect(titleElement.text()).toBe('title');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('can set the description by slot', () => {
|
|
71
|
+
const wrapper = shallowMount(AppCard, {
|
|
72
|
+
slots: { title: 'title', description: '<p data-test-description>description</p>' },
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const descriptionElement = wrapper.find('[data-test-description]');
|
|
76
|
+
expect(descriptionElement.exists()).toBe(true);
|
|
77
|
+
expect(descriptionElement.text()).toBe('description');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('can set the icon by prop', () => {
|
|
81
|
+
const wrapper = shallowMount(AppCard, {
|
|
82
|
+
props: { icon: 'icon' },
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const cardIconComponent = wrapper.findComponent(CardIconSlot);
|
|
86
|
+
expect(cardIconComponent.exists()).toBe(true);
|
|
87
|
+
expect(cardIconComponent.props('icon')).toBe('icon');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('can set the icon by slot', () => {
|
|
91
|
+
const wrapper = shallowMount(AppCard, {
|
|
92
|
+
slots: { icon: 'icon' },
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const cardIconComponent = wrapper.findComponent(CardIconSlot);
|
|
96
|
+
expect(cardIconComponent.exists()).toBe(true);
|
|
97
|
+
expect(cardIconComponent.text()).toBe('icon');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('can set set additional content classes', () => {
|
|
101
|
+
const wrapper = shallowMount(AppCard, {
|
|
102
|
+
props: { contentClasses: 'content-class' },
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const cardContent = wrapper.find('[data-test-content]');
|
|
106
|
+
expect(cardContent.exists()).toBe(true);
|
|
107
|
+
expect(cardContent.classes('content-class')).toBe(true);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { RouterLinkStub, shallowMount } from '@vue/test-utils';
|
|
3
|
+
import { useNavigation } from '~/modules/composables';
|
|
4
|
+
import CardAction from '../CardAction.vue';
|
|
5
|
+
import { navigationItem as navigationItemMock } from '~composables/useNavigation/__mocks__/navigation';
|
|
6
|
+
|
|
7
|
+
import type { HomeIcon } from '@heroicons/vue/24/outline';
|
|
8
|
+
import type { VueWrapper } from '@vue/test-utils';
|
|
9
|
+
import type { NavigationItem as NavigationItemType } from '~composables/useNavigation';
|
|
10
|
+
|
|
11
|
+
function createWrapper(mockData: Partial<NavigationItemType> = {}): VueWrapper<InstanceType<typeof CardAction>> {
|
|
12
|
+
return shallowMount(CardAction, {
|
|
13
|
+
props: {
|
|
14
|
+
item: { ...navigationItemMock, ...mockData },
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe('the CardAction component', () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
vi.useFakeTimers();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should render an "a" element as root element based on useNavigation', () => {
|
|
25
|
+
const wrapper = createWrapper();
|
|
26
|
+
expect(wrapper.element).toBeInstanceOf(HTMLAnchorElement);
|
|
27
|
+
expect(wrapper.attributes('href')).toBe(navigationItemMock.href);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should render an "router" element as root element based on useNavigation', () => {
|
|
31
|
+
const navigation = useNavigation();
|
|
32
|
+
navigation.setNavigationComponent(RouterLinkStub);
|
|
33
|
+
|
|
34
|
+
const wrapper = createWrapper();
|
|
35
|
+
|
|
36
|
+
const routerLink = wrapper.findComponent(RouterLinkStub);
|
|
37
|
+
expect(routerLink.exists()).toBe(true);
|
|
38
|
+
expect(routerLink.attributes('href')).toBe(navigationItemMock.href);
|
|
39
|
+
expect(routerLink.props('to')).toStrictEqual(navigationItemMock.to);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should render an icon', () => {
|
|
43
|
+
const wrapper = createWrapper();
|
|
44
|
+
|
|
45
|
+
const icon = wrapper.findComponent<typeof HomeIcon>('[data-test-icon]');
|
|
46
|
+
expect(icon.exists()).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should render the name', () => {
|
|
50
|
+
const wrapper = createWrapper();
|
|
51
|
+
|
|
52
|
+
const name = wrapper.find('[data-test-name]');
|
|
53
|
+
expect(name.text()).toBe(navigationItemMock.name);
|
|
54
|
+
});
|
|
55
|
+
});
|