whelk-ui 0.0.2 → 0.0.3

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.
Files changed (30) hide show
  1. package/README.md +2 -0
  2. package/package.json +4 -3
  3. package/playwright.config.ts +79 -0
  4. package/src/components/add_object/AddObject.vue +40 -0
  5. package/src/components/card/CardComponent.vue +14 -0
  6. package/src/components/card/card_footer/CardFooter.vue +19 -0
  7. package/src/components/card/card_header/CardHeader.vue +16 -0
  8. package/src/components/check_box/CheckBox.vue +42 -0
  9. package/src/components/datetime/DatetimeComponent.vue +147 -0
  10. package/src/components/drop_down/DropDown.vue +104 -0
  11. package/src/components/drop_down/drop_down_item/DropDownItem.vue +58 -0
  12. package/src/components/form_group/FormGroup.spec.ts +16 -0
  13. package/src/components/form_group/FormGroup.vue +19 -0
  14. package/src/components/number_input/NumberInput.spec.ts +712 -0
  15. package/src/components/number_input/NumberInput.vue +264 -0
  16. package/src/components/password_input/PasswordInput.vue +166 -0
  17. package/src/components/render_error_message/RenderErrorMessage.spec.ts +0 -0
  18. package/src/components/render_error_message/RenderErrorMessage.vue +32 -0
  19. package/src/components/switch/SwitchComponent.vue +152 -0
  20. package/src/components/text_area/TextArea.vue +151 -0
  21. package/src/components/text_input/TextInput.vue +178 -0
  22. package/src/components/tool_tip/ToolTip.vue +96 -0
  23. package/src/utils/enums/ObjectTitleCaseEnums.ts +17 -0
  24. package/src/utils/enums/ObjectTypeEnums.ts +15 -0
  25. package/src/utils/interfaces/DocumentItemInterface.ts +5 -0
  26. package/src/utils/interfaces/DropDownItemsInterface.ts +8 -0
  27. package/src/utils/interfaces/FolderItemInterface.ts +4 -0
  28. package/src/utils/interfaces/MenuItemInterface.ts +10 -0
  29. package/tests/example.spec.ts +18 -0
  30. package/vite.config.ts +40 -1
package/README.md CHANGED
@@ -13,6 +13,8 @@ You can follow the completion status on [NearBeach's 0.32 Component Reconstructi
13
13
 
14
14
  ## Forms
15
15
 
16
+ - [] Add Object
17
+ - [x] Buttons
16
18
  - [] Checkbox
17
19
  - [] Datetime picker
18
20
  - [] Date picker
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "whelk-ui",
3
3
  "private": false,
4
- "version": "0.0.2",
4
+ "version": "0.0.3",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",
8
8
  "build": "vue-tsc -b && vite build",
9
9
  "preview": "vite preview",
10
+ "test:e2e": "playwright test",
10
11
  "test:unit": "vitest"
11
12
  },
12
13
  "dependencies": {
@@ -14,10 +15,10 @@
14
15
  },
15
16
  "devDependencies": {
16
17
  "@csstools/postcss-global-data": "^3.1.0",
18
+ "@playwright/test": "^1.58.0",
17
19
  "@types/node": "^24.10.1",
18
20
  "@vitejs/plugin-vue": "^6.0.1",
19
- "@vitest/browser-playwright": "^4.0.7",
20
- "@vitest/coverage-v8": "^4.0.8",
21
+ "@vitest/browser-playwright": "^4.0.18",
21
22
  "@vitest/eslint-plugin": "^1.4.0",
22
23
  "@vue/tsconfig": "^0.8.1",
23
24
  "petite-vue-i18n": "^11.2.8",
@@ -0,0 +1,79 @@
1
+ import { defineConfig, devices } from '@playwright/test';
2
+
3
+ /**
4
+ * Read environment variables from file.
5
+ * https://github.com/motdotla/dotenv
6
+ */
7
+ // import dotenv from 'dotenv';
8
+ // import path from 'path';
9
+ // dotenv.config({ path: path.resolve(__dirname, '.env') });
10
+
11
+ /**
12
+ * See https://playwright.dev/docs/test-configuration.
13
+ */
14
+ export default defineConfig({
15
+ testDir: './tests',
16
+ /* Run tests in files in parallel */
17
+ fullyParallel: true,
18
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
19
+ forbidOnly: !!process.env.CI,
20
+ /* Retry on CI only */
21
+ retries: process.env.CI ? 2 : 0,
22
+ /* Opt out of parallel tests on CI. */
23
+ workers: process.env.CI ? 1 : undefined,
24
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
25
+ reporter: 'html',
26
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
27
+ use: {
28
+ /* Base URL to use in actions like `await page.goto('')`. */
29
+ // baseURL: 'http://localhost:3000',
30
+
31
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
32
+ trace: 'on-first-retry',
33
+ },
34
+
35
+ /* Configure projects for major browsers */
36
+ projects: [
37
+ {
38
+ name: 'chromium',
39
+ use: { ...devices['Desktop Chrome'] },
40
+ },
41
+
42
+ {
43
+ name: 'firefox',
44
+ use: { ...devices['Desktop Firefox'] },
45
+ },
46
+
47
+ {
48
+ name: 'webkit',
49
+ use: { ...devices['Desktop Safari'] },
50
+ },
51
+
52
+ /* Test against mobile viewports. */
53
+ // {
54
+ // name: 'Mobile Chrome',
55
+ // use: { ...devices['Pixel 5'] },
56
+ // },
57
+ // {
58
+ // name: 'Mobile Safari',
59
+ // use: { ...devices['iPhone 12'] },
60
+ // },
61
+
62
+ /* Test against branded browsers. */
63
+ // {
64
+ // name: 'Microsoft Edge',
65
+ // use: { ...devices['Desktop Edge'], channel: 'msedge' },
66
+ // },
67
+ // {
68
+ // name: 'Google Chrome',
69
+ // use: { ...devices['Desktop Chrome'], channel: 'chrome' },
70
+ // },
71
+ ],
72
+
73
+ /* Run your local dev server before starting the tests */
74
+ // webServer: {
75
+ // command: 'npm run start',
76
+ // url: 'http://localhost:3000',
77
+ // reuseExistingServer: !process.env.CI,
78
+ // },
79
+ });
@@ -0,0 +1,40 @@
1
+ <script setup lang="ts">
2
+ import {ObjectTitleCaseEnums} from "../../utils/enums/ObjectTitleCaseEnums.ts";
3
+
4
+ // DEFINE PROPS
5
+ const props = defineProps({
6
+ objectType: {
7
+ type: String,
8
+ required: true,
9
+ validator: function (value: string): boolean {
10
+ const enumValues: string[] = Object.values(ObjectTitleCaseEnums);
11
+ return enumValues.includes(value);
12
+ },
13
+ },
14
+ });
15
+
16
+ </script>
17
+
18
+ <template>
19
+ <button
20
+ class="add-object"
21
+ type="button"
22
+ >
23
+ + {{objectType}}
24
+ </button>
25
+ </template>
26
+
27
+ <style scoped>
28
+ .add-object {
29
+ width: 100%;
30
+ border-style: dashed;
31
+ border-width: var(--border-width);
32
+ border-radius: var(--border-radius);
33
+ padding: 0.5rem 0.75rem;
34
+ font-weight: lighter;
35
+ font-size: 1rem;
36
+ }
37
+
38
+
39
+
40
+ </style>
@@ -0,0 +1,14 @@
1
+ <script setup lang="ts"></script>
2
+
3
+ <template>
4
+ <div class="card">
5
+ <slot></slot>
6
+ </div>
7
+ </template>
8
+
9
+ <style scoped>
10
+ .card {
11
+ border-radius: var(--border-radius);
12
+ background-color: var(--bg-light);
13
+ }
14
+ </style>
@@ -0,0 +1,19 @@
1
+ <script setup lang="ts"></script>
2
+
3
+ <template>
4
+ <div class="card-footer">
5
+ <slot></slot>
6
+ </div>
7
+ </template>
8
+
9
+ <style scoped>
10
+ .card-footer {
11
+ padding: 0 0 0.75rem 0;
12
+
13
+ @media (--large-screen) {
14
+ margin: 1rem -2rem -1rem -2rem;
15
+ padding: 1rem 2rem;
16
+ border-radius: 0 0 var(--border-radius) var(--border-radius);
17
+ }
18
+ }
19
+ </style>
@@ -0,0 +1,16 @@
1
+ <script setup lang="ts"></script>
2
+ <template>
3
+ <div class="card-header">
4
+ <slot />
5
+ </div>
6
+ </template>
7
+
8
+ <style scoped>
9
+ .card-header {
10
+ border-radius: 0;
11
+
12
+ @media (--large-screen) {
13
+ border-radius: var(--border-radius) var(--border-radius) 0 0;
14
+ }
15
+ }
16
+ </style>
@@ -0,0 +1,42 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue';
3
+
4
+ // Model
5
+ const model = defineModel();
6
+
7
+ // Setup Props
8
+ const props = defineProps({
9
+ id: {
10
+ type: String,
11
+ required: true,
12
+ },
13
+ label: {
14
+ type: String,
15
+ required: true,
16
+ },
17
+ });
18
+
19
+ // Computed
20
+ const checkboxId = computed(() => {
21
+ return `checkbox-${props.id}`;
22
+ });
23
+ </script>
24
+
25
+ <template>
26
+ <div class="checkbox-component">
27
+ <input :id="checkboxId" :name="label" v-model="model" type="checkbox" />
28
+ <label :for="checkboxId">{{ props.label }}</label>
29
+ </div>
30
+ </template>
31
+
32
+ <style scoped>
33
+ .checkbox-component {
34
+ label {
35
+ padding: 0.5rem;
36
+ }
37
+
38
+ input {
39
+ padding: 0.5rem;
40
+ }
41
+ }
42
+ </style>
@@ -0,0 +1,147 @@
1
+ <script setup lang="ts">
2
+ import FormGroup from '../form_group/FormGroup.vue';
3
+ import {computed, ref} from 'vue';
4
+ import RenderErrorMessage from '../render_error_message/RenderErrorMessage.vue';
5
+ import ToolTip from '../tool_tip/ToolTip.vue';
6
+
7
+ // Define Emits
8
+ const emit = defineEmits(['isValid']);
9
+
10
+ // Define Props
11
+ const props = defineProps({
12
+ isRequired: {
13
+ type: Boolean,
14
+ default: false,
15
+ },
16
+ label: {
17
+ type: String,
18
+ required: true,
19
+ },
20
+ tooltipMessage: {
21
+ type: String,
22
+ required: false,
23
+ default: '',
24
+ },
25
+ tooltipTitle: {
26
+ type: String,
27
+ required: false,
28
+ default: '',
29
+ },
30
+ });
31
+
32
+ // Define Models
33
+ const model = defineModel('model', {
34
+ type: Date,
35
+ required: false,
36
+ });
37
+
38
+ // Define ref
39
+ const hasError = ref(false);
40
+ const errorMessage = ref('');
41
+
42
+ // Computed
43
+ const getId = computed(() => {
44
+ // Return an id made up of input- + title
45
+ return 'input-' + props.label?.toLowerCase()?.replace(' ', '-');
46
+ });
47
+
48
+ function checkValidation() {
49
+ // Fall back to defaults
50
+ hasError.value = false;
51
+ errorMessage.value = '';
52
+
53
+ // Get the length of the model and if NaN fallback to 0
54
+ let modelLength: number = Number(model?.value?.toString().length);
55
+ modelLength = isNaN(modelLength) ? 0 : modelLength;
56
+
57
+ // Check the first "required" condition
58
+ if (props.isRequired && modelLength === 0) {
59
+ hasError.value = true;
60
+ errorMessage.value = 'This field is required';
61
+ }
62
+
63
+ // Set the defined ref and tell parent
64
+ emit('isValid', !hasError.value);
65
+ }
66
+ </script>
67
+
68
+ <template>
69
+ <FormGroup class="datetime-component">
70
+ <label :for="getId">
71
+ <ToolTip
72
+ v-if="props.tooltipMessage !== ''"
73
+ :title="tooltipTitle"
74
+ :message="tooltipMessage"
75
+ :id="getId"
76
+ />
77
+ {{
78
+ label
79
+ }}<span v-if="isRequired" aria-description="Field is required"
80
+ >*</span
81
+ >
82
+ </label>
83
+ <input
84
+ :id="getId"
85
+ type="datetime-local"
86
+ :name="props.label"
87
+ v-model="model"
88
+ v-on:keyup="checkValidation"
89
+ v-on:focusout="checkValidation"
90
+ />
91
+ <RenderErrorMessage :error-message="errorMessage"/>
92
+ </FormGroup>
93
+ </template>
94
+
95
+ <style scoped>
96
+ .datetime-component {
97
+
98
+ label {
99
+ margin-bottom: 6px;
100
+ }
101
+
102
+ span {
103
+ color: var(--text-red);
104
+ }
105
+
106
+ input {
107
+ border-style: var(--border-style);
108
+ border-width: var(--border-width);
109
+ border-radius: var(--border-radius);
110
+ border-color: var(--border);
111
+ box-sizing: border-box;
112
+ -moz-box-sizing: border-box;
113
+ -webkit-box-sizing: border-box;
114
+
115
+ &:focus {
116
+ border-color: var(--secondary);
117
+ border-width: 2px;
118
+ outline: none;
119
+ padding: calc(0.5rem - 1px);
120
+ }
121
+ }
122
+
123
+ &.compact {
124
+ > label {
125
+ font-size: 1rem;
126
+ line-height: 1.25rem;
127
+ margin-bottom: 2px;
128
+
129
+ @media (--large-screen) {
130
+ font-size: 0.75rem;
131
+ line-height: 1rem;
132
+ }
133
+ }
134
+
135
+ > input {
136
+ font-size: 1.25rem;
137
+ line-height: 1.5rem;
138
+ padding: 0.25rem;
139
+
140
+ @media (--large-screen) {
141
+ font-size: 1rem;
142
+ line-height: 1.25rem;
143
+ }
144
+ }
145
+ }
146
+ }
147
+ </style>
@@ -0,0 +1,104 @@
1
+ <script setup lang="ts">
2
+ import {ref} from 'vue';
3
+ import type {PropType} from "vue";
4
+ import type {DropDownItemsInterface} from "../../utils/interfaces/DropDownItemsInterface.ts";
5
+ import DropDownItem from "./drop_down_item/DropDownItem.vue";
6
+
7
+ // Define props
8
+ defineProps({
9
+ dropDownItems: {
10
+ type: Array as PropType<DropDownItemsInterface[]>,
11
+ required: true,
12
+ },
13
+ });
14
+
15
+ // Define emits
16
+ const emits = defineEmits(["dropDownItemClicked"]);
17
+
18
+ // Define refs
19
+ const menuOpen = ref(false);
20
+
21
+ // Define methods
22
+ function dropDownMenuClicked() {
23
+ menuOpen.value = !menuOpen.value;
24
+ }
25
+
26
+ function dropDownItemClicked(trigger: string) {
27
+ // Emit upstream
28
+ emits("dropDownItemClicked", trigger);
29
+
30
+ // Close the menu
31
+ menuOpen.value = false;
32
+ }
33
+ </script>
34
+
35
+ <template>
36
+ <div class="drop-down">
37
+ <button
38
+ type="button"
39
+ v-on:click="dropDownMenuClicked"
40
+ >
41
+ <slot />
42
+ </button>
43
+ <Transition>
44
+ <DropDownItem
45
+ v-show="menuOpen"
46
+ v-on:dropDownItemClicked="dropDownItemClicked"
47
+ :drop-down-items="dropDownItems"
48
+ />
49
+ </Transition>
50
+ <Transition>
51
+ <div
52
+ v-on:click="dropDownMenuClicked"
53
+ v-if="menuOpen"
54
+ class="drop-down-backdrop"
55
+ ></div>
56
+ </Transition>
57
+ </div>
58
+ </template>
59
+
60
+ <style scoped>
61
+ .drop-down {
62
+ > button {
63
+ border: solid;
64
+ border-width: 1px;
65
+ border-radius: var(--border-radius);
66
+ border-color: var(--border-muted);
67
+ background: var(--bg-light);
68
+ box-shadow: none;
69
+ font-size: 1rem;
70
+ line-height: 1.25rem;
71
+ height: 2rem;
72
+ padding: 0 1rem;
73
+ position: relative;
74
+ z-index: 10;
75
+
76
+ > svg {
77
+ width: 1rem;
78
+ height: 1rem;
79
+ transform: translateY(2px);
80
+ }
81
+ }
82
+
83
+ .drop-down-backdrop {
84
+ width: 100vw;
85
+ height: 100dvh;
86
+ z-index: 5;
87
+ background-color: hsla(0, 0%, 0%, 0.7);
88
+ position: fixed;
89
+ top: 0;
90
+ left: 0;
91
+ }
92
+ }
93
+
94
+ .v-enter-active,
95
+ .v-leave-active {
96
+ transition: opacity 0.5s ease;
97
+ }
98
+
99
+ .v-enter-from,
100
+ .v-leave-to {
101
+ opacity: 0;
102
+ }
103
+
104
+ </style>
@@ -0,0 +1,58 @@
1
+ <script setup lang="ts">
2
+ import type {PropType} from "vue";
3
+ import type {DropDownItemsInterface} from "../../../utils/interfaces/DropDownItemsInterface.ts";
4
+
5
+ // Define props
6
+ defineProps({
7
+ dropDownItems: {
8
+ type: Array as PropType<DropDownItemsInterface[]>,
9
+ required: true,
10
+ },
11
+ });
12
+
13
+ // Define emits
14
+ const emits = defineEmits(["dropDownItemClicked"]);
15
+
16
+ // Define methods
17
+ function dropDownItemClicked(trigger: string) {
18
+ emits("dropDownItemClicked", trigger);
19
+ }
20
+ </script>
21
+
22
+ <template>
23
+ <div class="drop-down-items">
24
+ <div class="drop-down-item"
25
+ v-for="item in dropDownItems"
26
+ v-on:click="dropDownItemClicked(item.trigger)"
27
+ :key="item.label"
28
+ >
29
+ <component :is="item.icon" />
30
+ {{item.label}}
31
+ </div>
32
+ </div>
33
+ </template>
34
+
35
+ <style scoped>
36
+ .drop-down-items {
37
+ position: absolute;
38
+ background-color: var(--bg-light);
39
+ border: solid;
40
+ border-width: var(--border-width);
41
+ border-radius: var(--border-radius);
42
+ border-color: var(--border-muted);
43
+ z-index: 20;
44
+
45
+ > .drop-down-item {
46
+ padding: 0.25rem 0.5rem;
47
+
48
+ &:hover {
49
+ background-color: var(--bg-dark);
50
+ }
51
+
52
+ > svg {
53
+ width: 0.75rem;
54
+ height: 0.75rem;
55
+ }
56
+ }
57
+ }
58
+ </style>
@@ -0,0 +1,16 @@
1
+ // FormGroup.spec.ts
2
+ import { describe, test, expect } from "vitest";
3
+ import FormGroup from "./FormGroup.vue";
4
+ import { mount } from "@vue/test-utils";
5
+
6
+ describe("FormGroup", async () => {
7
+ test("form group slot renders main content", () => {
8
+ const wrapper = mount(FormGroup, {
9
+ slots: {
10
+ default: "Main Content",
11
+ },
12
+ });
13
+ expect(wrapper.html()).toContain("Main Content");
14
+ expect(wrapper.find(".form-group").text()).toContain("Main Content");
15
+ });
16
+ });
@@ -0,0 +1,19 @@
1
+ <script setup lang="ts"></script>
2
+
3
+ <template>
4
+ <div class="form-group">
5
+ <slot></slot>
6
+ </div>
7
+ </template>
8
+
9
+ <style scoped>
10
+ .form-group {
11
+ display: flex;
12
+ flex-direction: column;
13
+ margin-bottom: 1.75rem;
14
+
15
+ &.compact {
16
+ margin-bottom: 0.5rem;
17
+ }
18
+ }
19
+ </style>