pimelon-ui 0.1.262 → 0.1.267
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/melon/DataImport/UploadStep.vue +27 -8
- package/melon/DataImport/types.ts +6 -0
- package/melon/drive/components/RenameDialog.vue +1 -1
- package/melon/drive/components/ShareDialog.vue +8 -5
- package/melon/drive/components/TagInput/TagInput.vue +33 -20
- package/melon/drive/js/resources.js +2 -3
- package/melon/drive/js/utils.js +1 -1
- package/package.json +4 -2
- package/src/components/Alert/Alert.cy.ts +64 -0
- package/src/components/Avatar/Avatar.cy.ts +47 -0
- package/src/components/Badge/Badge.cy.ts +273 -0
- package/src/components/Breadcrumbs/Breadcrumbs.cy.ts +45 -0
- package/src/components/Button/Button.cy.ts +128 -0
- package/src/components/Button/Button.vue +1 -6
- package/src/components/Checkbox/Checkbox.cy.ts +31 -0
- package/src/components/Checkbox/Checkbox.vue +1 -1
- package/src/components/Combobox/Combobox.cy.ts +93 -0
- package/src/components/Combobox/Combobox.vue +1 -1
- package/src/components/DatePicker/DatePicker.cy.ts +124 -0
- package/src/components/DatePicker/DatePicker.vue +3 -0
- package/src/components/DatePicker/DateRangePicker.cy.ts +105 -0
- package/src/components/DatePicker/DateRangePicker.vue +3 -0
- package/src/components/Dialog/Dialog.cy.ts +90 -0
- package/src/components/Dropdown/Dropdown.cy.ts +69 -0
- package/src/components/ErrorMessage/ErrorMessage.cy.ts +15 -0
- package/src/components/FormControl/FormControl.vue +2 -2
- package/src/components/ListFilter/ListFilter.vue +1 -1
- package/src/components/ListView/ListGroupHeader.vue +2 -2
- package/src/components/ListView/ListHeader.vue +1 -1
- package/src/components/ListView/ListHeaderItem.vue +1 -1
- package/src/components/ListView/ListRow.vue +2 -2
- package/src/components/ListView/ListRowItem.vue +1 -1
- package/src/components/ListView/ListSelectBanner.vue +4 -4
- package/src/components/MonthPicker/MonthPicker.cy.ts +83 -0
- package/src/components/MonthPicker/MonthPicker.vue +7 -5
- package/src/components/MultiSelect/MultiSelect.cy.ts +70 -0
- package/src/components/Password/Password.cy.ts +35 -0
- package/src/components/Popover/Popover.cy.ts +62 -0
- package/src/components/Popover/Popover.vue +8 -8
- package/src/components/Popover/types.ts +1 -0
- package/src/components/Progress/Progress.cy.ts +80 -0
- package/src/components/Rating/Rating.cy.ts +67 -0
- package/src/components/Select/Select.cy.ts +84 -0
- package/src/components/Select/Select.vue +1 -1
- package/src/components/Sidebar/Sidebar.cy.ts +89 -0
- package/src/components/Switch/Switch.cy.ts +46 -0
- package/src/components/Switch/Switch.vue +5 -11
- package/src/components/Tabs/Tabs.cy.ts +78 -0
- package/src/components/Tabs/Tabs.vue +3 -3
- package/src/components/TextEditor/TextEditor.cy.ts +181 -0
- package/src/components/TextEditor/commands.js +47 -12
- package/src/components/TextEditor/components/MediaNodeView.vue +32 -13
- package/src/components/TextEditor/components/Menu.vue +167 -152
- package/src/components/TextEditor/extensions/image/image-extension.ts +57 -17
- package/src/components/TextEditor/extensions/video-extension.ts +67 -18
- package/src/components/TextEditor/index.ts +1 -0
- package/src/components/TextInput/TextInput.cy.ts +87 -0
- package/src/components/TimePicker/TimePicker.cy.ts +122 -0
- package/src/components/Tooltip/Tooltip.cy.ts +66 -0
- package/src/components/Tree/tree.cy.ts +144 -0
- package/src/resources/resources.js +1 -0
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
<script setup lang="ts">
|
|
154
154
|
import { computed, nextTick, ref, watch } from 'vue'
|
|
155
155
|
import { useRouter } from 'vue-router'
|
|
156
|
-
import type { DataImports, DataImport, DocField } from './types'
|
|
156
|
+
import type { DataImports, DataImport, DocField, DocType } from './types'
|
|
157
157
|
import { toast } from "../../src/components/Toast/index"
|
|
158
158
|
import { fieldsToIgnore, getChildTableName, getBadgeColor } from './dataImport'
|
|
159
159
|
import Badge from '../../src/components/Badge/Badge.vue'
|
|
@@ -327,20 +327,39 @@ const getExportFields = (type: 'mandatory' | 'all') => {
|
|
|
327
327
|
}
|
|
328
328
|
|
|
329
329
|
const getMandatoryFields = () => {
|
|
330
|
-
let
|
|
331
|
-
let
|
|
330
|
+
let exportableFields: Record<string, string[]> = {}
|
|
331
|
+
let docs = props.fields.data?.docs || []
|
|
332
|
+
let referenceDoctype = props.doctype || props.data?.reference_doctype as string
|
|
333
|
+
let parentDoctype = docs.find((doc: DocType) => doc.name == referenceDoctype)
|
|
334
|
+
|
|
335
|
+
let parentFields = parentDoctype.fields.filter((field: DocField) => {
|
|
332
336
|
return !fieldsToIgnore.includes(field.fieldtype) && field.reqd
|
|
333
337
|
}).map((field: DocField) => field.fieldname)
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
+
parentFields.unshift('name')
|
|
339
|
+
exportableFields[referenceDoctype] = parentFields
|
|
340
|
+
|
|
341
|
+
let childDoctypes = parentDoctype.fields.filter((field: DocField) => {
|
|
342
|
+
return (field.fieldtype === 'Table' || field.fieldtype === 'Table MultiSelect') && field.reqd
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
childDoctypes.forEach((field: DocField) => {
|
|
346
|
+
let childDoctype = docs.find((doc: DocType) => doc.name == field.options)
|
|
347
|
+
if (childDoctype) {
|
|
348
|
+
let childFields = childDoctype.fields.filter((f: DocField) => {
|
|
349
|
+
return !fieldsToIgnore.includes(f.fieldtype) && f.reqd
|
|
350
|
+
}).map((f: DocField) => f.fieldname)
|
|
351
|
+
childFields.unshift('name')
|
|
352
|
+
exportableFields[field.fieldname] = childFields
|
|
353
|
+
}
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
return exportableFields
|
|
338
357
|
}
|
|
339
358
|
|
|
340
359
|
const getAllFields = () => {
|
|
341
360
|
let doctypeMap: Record<string, string[]> = {}
|
|
342
361
|
let docs = props.fields.data?.docs || []
|
|
343
|
-
docs.forEach((doc:
|
|
362
|
+
docs.forEach((doc: DocType) => {
|
|
344
363
|
let exportableFields = doc.fields.filter((field: DocField) => {
|
|
345
364
|
return !fieldsToIgnore.includes(field.fieldtype)
|
|
346
365
|
}).map((field: DocField) => field.fieldname)
|
|
@@ -49,7 +49,7 @@ const open = ref(true)
|
|
|
49
49
|
const newTitle = ref('')
|
|
50
50
|
const file_ext = ref('')
|
|
51
51
|
|
|
52
|
-
if (props.entity.is_group || props.entity.
|
|
52
|
+
if (props.entity.is_group || props.entity.doc) {
|
|
53
53
|
newTitle.value = props.entity.title
|
|
54
54
|
} else {
|
|
55
55
|
const parts = props.entity.title.split('.')
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Dialog v-model="open" :options="{ size: 'lg' }">
|
|
3
3
|
<template #body-main>
|
|
4
|
-
<div class="p-4
|
|
4
|
+
<div class="p-4">
|
|
5
5
|
<!-- Header -->
|
|
6
6
|
<div class="flex w-full justify-between gap-x-2 mb-4">
|
|
7
7
|
<div class="font-semibold text-2xl flex text-nowrap overflow-hidden">
|
|
@@ -180,7 +180,7 @@
|
|
|
180
180
|
v-if="usersToAdd.length"
|
|
181
181
|
label="Invite"
|
|
182
182
|
variant="solid"
|
|
183
|
-
@click="
|
|
183
|
+
@click="inviteUsers"
|
|
184
184
|
/>
|
|
185
185
|
</div>
|
|
186
186
|
</div>
|
|
@@ -338,17 +338,20 @@ watch(
|
|
|
338
338
|
{ immediate: true },
|
|
339
339
|
)
|
|
340
340
|
|
|
341
|
-
const
|
|
341
|
+
const inviteUsers = () => {
|
|
342
342
|
const access = getAccess(accessToAdd.value)
|
|
343
|
-
for (
|
|
343
|
+
for (let user of usersToAdd.value) {
|
|
344
344
|
const r = {
|
|
345
345
|
entity_name: props.entity.name,
|
|
346
346
|
user,
|
|
347
347
|
...access,
|
|
348
348
|
}
|
|
349
349
|
props.updateAccess.submit(r)
|
|
350
|
+
const userObj = filteredUsers.value.find((k) => k.value === user)
|
|
351
|
+
// For new records
|
|
352
|
+
if (!userObj.email) userObj.email = userObj.label
|
|
350
353
|
props.usersWithAccess.data.push({
|
|
351
|
-
...
|
|
354
|
+
...userObj,
|
|
352
355
|
...access,
|
|
353
356
|
})
|
|
354
357
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { computed, ref } from 'vue'
|
|
3
3
|
import {
|
|
4
4
|
TagsInputRoot,
|
|
5
|
+
TagsInputInput,
|
|
5
6
|
TagsInputItem,
|
|
6
7
|
TagsInputItemText,
|
|
7
8
|
TagsInputItemDelete,
|
|
@@ -10,11 +11,13 @@ import Combobox from '../../../../src/components/Combobox/Combobox.vue'
|
|
|
10
11
|
import { getLabel, getIcon, RenderIcon, getValue } from './utils'
|
|
11
12
|
import { type SimpleOption } from '../../../../src/components/Combobox/types'
|
|
12
13
|
import { TagInputProps } from './types'
|
|
14
|
+
import LucideX from '~icons/lucide/x'
|
|
13
15
|
|
|
14
16
|
const props = defineProps<TagInputProps>()
|
|
15
|
-
const search = ref('')
|
|
16
17
|
const options = defineModel<SimpleOption[]>('options', { default: [] })
|
|
17
18
|
const modelValue = defineModel<SimpleOption[]>({ default: [] })
|
|
19
|
+
const rerenderCombobox = ref(0)
|
|
20
|
+
|
|
18
21
|
const optionsWithIcons = computed(() => {
|
|
19
22
|
if (!props.renderIcon) return options.value
|
|
20
23
|
return options.value.map((k) =>
|
|
@@ -64,7 +67,7 @@ const filteredOptions = computed(() => {
|
|
|
64
67
|
function addTag(tag: string) {
|
|
65
68
|
if (!tag) return
|
|
66
69
|
if (!modelValue.value.includes(tag)) modelValue.value.push(tag)
|
|
67
|
-
|
|
70
|
+
rerenderCombobox.value += 1
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
function removeTag(tag: string) {
|
|
@@ -81,33 +84,43 @@ function removeTag(tag: string) {
|
|
|
81
84
|
v-for="item in selectedTags"
|
|
82
85
|
:key="getValue(item)"
|
|
83
86
|
:value="getValue(item)"
|
|
84
|
-
class="shadow-sm m-0.25 mr-0 p-1.5 text-sm bg-white flex items-center justify-center gap-1.5 rounded p-0.5 ring-1 ring-outline-gray-
|
|
87
|
+
class="shadow-sm m-0.25 mr-0 p-1.5 text-sm bg-white flex items-center justify-center gap-1.5 rounded p-0.5 ring-1 ring-outline-gray-2 shadow-xs"
|
|
85
88
|
>
|
|
86
89
|
<RenderIcon :icon="getIcon(item)" />
|
|
87
90
|
<TagsInputItemText class="text-xs text-ink-gray-8">{{
|
|
88
91
|
getLabel(item)
|
|
89
92
|
}}</TagsInputItemText>
|
|
90
93
|
<TagsInputItemDelete
|
|
91
|
-
class="p-0.5 rounded bg-transparent hover:bg-
|
|
94
|
+
class="p-0.5 rounded-sm bg-transparent hover:bg-surface-gray-1"
|
|
92
95
|
@click="removeTag(getValue(item))"
|
|
93
96
|
>
|
|
94
97
|
<LucideX class="size-3 text-ink-gray-6" />
|
|
95
98
|
</TagsInputItemDelete>
|
|
96
99
|
</TagsInputItem>
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
{
|
|
110
|
-
|
|
111
|
-
|
|
100
|
+
<TagsInputInput :as-child="true">
|
|
101
|
+
<!-- Explicitly set unset type as TagInput passes it in -->
|
|
102
|
+
<Combobox
|
|
103
|
+
:key="rerenderCombobox"
|
|
104
|
+
:options="filteredOptions"
|
|
105
|
+
type=""
|
|
106
|
+
:placeholder
|
|
107
|
+
class="flex-1 min-w-[100px] text-xs focus:outline-none"
|
|
108
|
+
@update:modelValue="addTag"
|
|
109
|
+
:open-on-click="true"
|
|
110
|
+
variant="ghost"
|
|
111
|
+
>
|
|
112
|
+
<template #add-email="{ searchTerm }">
|
|
113
|
+
{{ searchTerm }}
|
|
114
|
+
</template>
|
|
115
|
+
</Combobox>
|
|
116
|
+
</TagsInputInput>
|
|
112
117
|
</TagsInputRoot>
|
|
113
118
|
</template>
|
|
119
|
+
<style>
|
|
120
|
+
[data-state='active'] {
|
|
121
|
+
background: var(--surface-gray-1);
|
|
122
|
+
}
|
|
123
|
+
[aria-label='Show popup'] {
|
|
124
|
+
display: none;
|
|
125
|
+
}
|
|
126
|
+
</style>
|
|
@@ -68,7 +68,7 @@ export const usersWithAccess = createResource({
|
|
|
68
68
|
})
|
|
69
69
|
|
|
70
70
|
export const updateAccess = createResource({
|
|
71
|
-
url: 'drive.api.files.
|
|
71
|
+
url: 'drive.api.files.update_access',
|
|
72
72
|
makeParams: (params) => ({ ...params, method: params.method || 'share' }),
|
|
73
73
|
onError: (error) => toast.error(error.messages[0]),
|
|
74
74
|
})
|
|
@@ -96,11 +96,10 @@ export const getTeam = createResource({
|
|
|
96
96
|
})
|
|
97
97
|
|
|
98
98
|
export const rename = createResource({
|
|
99
|
-
url: 'drive.api.files.
|
|
99
|
+
url: 'drive.api.files.rename',
|
|
100
100
|
method: 'POST',
|
|
101
101
|
makeParams: (data) => {
|
|
102
102
|
return {
|
|
103
|
-
method: 'rename',
|
|
104
103
|
...data,
|
|
105
104
|
}
|
|
106
105
|
},
|
package/melon/drive/js/utils.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pimelon-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.267",
|
|
4
4
|
"description": "A set of components and utilities for rapid UI development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"husky": "^9.1.7",
|
|
112
112
|
"idb-keyval": "^6.2.0",
|
|
113
113
|
"lowlight": "^3.3.0",
|
|
114
|
-
"lucide-static": "^0.
|
|
114
|
+
"lucide-static": "^0.545.0",
|
|
115
115
|
"marked": "^15.0.12",
|
|
116
116
|
"ora": "5.4.1",
|
|
117
117
|
"prettier": "^3.3.2",
|
|
@@ -133,6 +133,8 @@
|
|
|
133
133
|
"devDependencies": {
|
|
134
134
|
"@vitejs/plugin-vue": "^4.0.0",
|
|
135
135
|
"autoprefixer": "^10.4.13",
|
|
136
|
+
"cypress": "^15.8.2",
|
|
137
|
+
"cypress-split": "^1.24.28",
|
|
136
138
|
"jsdom": "^27.4.0",
|
|
137
139
|
"lint-staged": ">=10",
|
|
138
140
|
"msw": "^2.7.0",
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import Alert from './Alert.vue'
|
|
2
|
+
import { h } from 'vue'
|
|
3
|
+
|
|
4
|
+
const titleTxt = 'some title'
|
|
5
|
+
const description = 'some description'
|
|
6
|
+
const el = '[role="alert"]'
|
|
7
|
+
const themes = ['blue', 'red', 'green']
|
|
8
|
+
|
|
9
|
+
const TestIcon = {
|
|
10
|
+
render() {
|
|
11
|
+
return h('svg', { 'data-cy': 'prefix-icon' })
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe('Alert', () => {
|
|
16
|
+
it('Test text', () => {
|
|
17
|
+
cy.mount(Alert, {
|
|
18
|
+
props: {
|
|
19
|
+
title: titleTxt,
|
|
20
|
+
description: description,
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
cy.get(`${el} span`).should('have.text', titleTxt)
|
|
25
|
+
cy.get(`${el} p`).should('have.text', description)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('Themes', () => {
|
|
29
|
+
themes.forEach((x) => {
|
|
30
|
+
cy.mount(Alert, {
|
|
31
|
+
props: { theme: x, title: titleTxt, description: description },
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
cy.get(el).should('have.class', `bg-surface-${x}-2`)
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('Dismiss', () => {
|
|
39
|
+
cy.mount(Alert)
|
|
40
|
+
cy.get(el).should('exist')
|
|
41
|
+
cy.get(`${el} button`).click()
|
|
42
|
+
cy.get(el).should('not.exist')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('Non Dismissable', () => {
|
|
46
|
+
cy.mount(Alert, { props: { dismissable: false } })
|
|
47
|
+
cy.get(`${el} button`).should('not.exist')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('Icon slot', () => {
|
|
51
|
+
cy.mount(Alert, {
|
|
52
|
+
slots: { icon: TestIcon },
|
|
53
|
+
})
|
|
54
|
+
cy.get('[data-cy="prefix-icon"]').should('exist')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('Footer slot', () => {
|
|
58
|
+
cy.mount(Alert, {
|
|
59
|
+
slots: { footer: h('div', { id: 'footer' }, 'some footer') },
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
cy.get(`${el} #footer`).should('exist')
|
|
63
|
+
})
|
|
64
|
+
})
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import Avatar from './Avatar.vue'
|
|
2
|
+
|
|
3
|
+
const sizes = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl']
|
|
4
|
+
|
|
5
|
+
const sizeHeights = {
|
|
6
|
+
xs: '4',
|
|
7
|
+
sm: '5',
|
|
8
|
+
md: '6',
|
|
9
|
+
lg: '7',
|
|
10
|
+
xl: '8',
|
|
11
|
+
'2xl': '10',
|
|
12
|
+
'3xl': '11.5',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const defaultProps = {
|
|
16
|
+
'data-cy': 'avatar',
|
|
17
|
+
image: 'https://avatars.githubusercontent.com/u/499550',
|
|
18
|
+
label: 'Abc',
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('Avatar', () => {
|
|
22
|
+
it('Renders', () => {
|
|
23
|
+
cy.mount(Avatar, {
|
|
24
|
+
props: defaultProps,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
cy.get('[data-cy="avatar"]').should('exist')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('Sizes', () => {
|
|
31
|
+
sizes.forEach((x) => {
|
|
32
|
+
cy.mount(Avatar, {
|
|
33
|
+
props: { ...defaultProps, size: x },
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
cy.get('[data-cy="avatar"]').should('have.class', 'h-' + sizeHeights[x])
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('Name', () => {
|
|
41
|
+
cy.mount(Avatar, {
|
|
42
|
+
props: { 'data-cy': 'avatar', label: 'Abc' },
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
cy.get('[data-cy="avatar"]').should('have.text', 'A')
|
|
46
|
+
})
|
|
47
|
+
})
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import Badge from './Badge.vue'
|
|
2
|
+
import { h } from 'vue'
|
|
3
|
+
|
|
4
|
+
describe('<Badge />', () => {
|
|
5
|
+
it('renders default badge', () => {
|
|
6
|
+
cy.mount(Badge, {
|
|
7
|
+
slots: {
|
|
8
|
+
default: 'Default',
|
|
9
|
+
},
|
|
10
|
+
})
|
|
11
|
+
cy.get('.inline-flex.rounded-full').should('contain.text', 'Default')
|
|
12
|
+
// Default theme is gray, variant is subtle
|
|
13
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'text-ink-gray-6')
|
|
14
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'bg-surface-gray-2')
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('renders label prop', () => {
|
|
18
|
+
cy.mount(Badge, {
|
|
19
|
+
props: {
|
|
20
|
+
label: 'Badge Label',
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
cy.get('.inline-flex.rounded-full').should('have.text', 'Badge Label')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('renders different themes with subtle variant', () => {
|
|
27
|
+
// Gray (default)
|
|
28
|
+
cy.mount(Badge, {
|
|
29
|
+
props: {
|
|
30
|
+
theme: 'gray',
|
|
31
|
+
label: 'Gray',
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'text-ink-gray-6')
|
|
35
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'bg-surface-gray-2')
|
|
36
|
+
|
|
37
|
+
// Blue
|
|
38
|
+
cy.mount(Badge, {
|
|
39
|
+
props: {
|
|
40
|
+
theme: 'blue',
|
|
41
|
+
label: 'Blue',
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'text-ink-blue-2')
|
|
45
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'bg-surface-blue-2')
|
|
46
|
+
|
|
47
|
+
// Green
|
|
48
|
+
cy.mount(Badge, {
|
|
49
|
+
props: {
|
|
50
|
+
theme: 'green',
|
|
51
|
+
label: 'Green',
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'text-ink-green-3')
|
|
55
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'bg-surface-green-2')
|
|
56
|
+
|
|
57
|
+
// Orange
|
|
58
|
+
cy.mount(Badge, {
|
|
59
|
+
props: {
|
|
60
|
+
theme: 'orange',
|
|
61
|
+
label: 'Orange',
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'text-ink-amber-3')
|
|
65
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'bg-surface-amber-1')
|
|
66
|
+
|
|
67
|
+
// Red
|
|
68
|
+
cy.mount(Badge, {
|
|
69
|
+
props: {
|
|
70
|
+
theme: 'red',
|
|
71
|
+
label: 'Red',
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'text-ink-red-4')
|
|
75
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'bg-surface-red-2')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('renders different variants with gray theme', () => {
|
|
79
|
+
// Solid
|
|
80
|
+
cy.mount(Badge, {
|
|
81
|
+
props: {
|
|
82
|
+
variant: 'solid',
|
|
83
|
+
label: 'Solid',
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'text-ink-white')
|
|
87
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'bg-surface-gray-7')
|
|
88
|
+
|
|
89
|
+
// Subtle (default)
|
|
90
|
+
cy.mount(Badge, {
|
|
91
|
+
props: {
|
|
92
|
+
variant: 'subtle',
|
|
93
|
+
label: 'Subtle',
|
|
94
|
+
},
|
|
95
|
+
})
|
|
96
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'text-ink-gray-6')
|
|
97
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'bg-surface-gray-2')
|
|
98
|
+
|
|
99
|
+
// Outline
|
|
100
|
+
cy.mount(Badge, {
|
|
101
|
+
props: {
|
|
102
|
+
variant: 'outline',
|
|
103
|
+
label: 'Outline',
|
|
104
|
+
},
|
|
105
|
+
})
|
|
106
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'text-ink-gray-6')
|
|
107
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'bg-transparent')
|
|
108
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'border-outline-gray-1')
|
|
109
|
+
|
|
110
|
+
// Ghost
|
|
111
|
+
cy.mount(Badge, {
|
|
112
|
+
props: {
|
|
113
|
+
variant: 'ghost',
|
|
114
|
+
label: 'Ghost',
|
|
115
|
+
},
|
|
116
|
+
})
|
|
117
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'text-ink-gray-6')
|
|
118
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'bg-transparent')
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('renders different sizes', () => {
|
|
122
|
+
// Small
|
|
123
|
+
cy.mount(Badge, {
|
|
124
|
+
props: {
|
|
125
|
+
size: 'sm',
|
|
126
|
+
label: 'Small',
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'h-4')
|
|
130
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'text-xs')
|
|
131
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'px-1.5')
|
|
132
|
+
|
|
133
|
+
// Medium (default)
|
|
134
|
+
cy.mount(Badge, {
|
|
135
|
+
props: {
|
|
136
|
+
size: 'md',
|
|
137
|
+
label: 'Medium',
|
|
138
|
+
},
|
|
139
|
+
})
|
|
140
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'h-5')
|
|
141
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'text-xs')
|
|
142
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'px-1.5')
|
|
143
|
+
|
|
144
|
+
// Large
|
|
145
|
+
cy.mount(Badge, {
|
|
146
|
+
props: {
|
|
147
|
+
size: 'lg',
|
|
148
|
+
label: 'Large',
|
|
149
|
+
},
|
|
150
|
+
})
|
|
151
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'h-6')
|
|
152
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'text-sm')
|
|
153
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'px-2')
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('renders prefix slot', () => {
|
|
157
|
+
const TestIcon = {
|
|
158
|
+
render() {
|
|
159
|
+
return h('svg', { 'data-cy': 'prefix-icon' })
|
|
160
|
+
},
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
cy.mount(Badge, {
|
|
164
|
+
props: { label: 'With Icon' },
|
|
165
|
+
slots: {
|
|
166
|
+
prefix: () => h(TestIcon),
|
|
167
|
+
},
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
cy.get('[data-cy="prefix-icon"]').should('exist')
|
|
171
|
+
cy.get('.inline-flex.rounded-full').should('contain.text', 'With Icon')
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('renders suffix slot', () => {
|
|
175
|
+
const TestIcon = {
|
|
176
|
+
render() {
|
|
177
|
+
return h('svg', { 'data-cy': 'suffix-icon' })
|
|
178
|
+
},
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
cy.mount(Badge, {
|
|
182
|
+
props: { label: 'With Icon' },
|
|
183
|
+
slots: {
|
|
184
|
+
suffix: () => h(TestIcon),
|
|
185
|
+
},
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
cy.get('[data-cy="suffix-icon"]').should('exist')
|
|
189
|
+
cy.get('.inline-flex.rounded-full').should('contain.text', 'With Icon')
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
it('renders both prefix and suffix slots', () => {
|
|
193
|
+
const PrefixIcon = {
|
|
194
|
+
render() {
|
|
195
|
+
return h('svg', { 'data-cy': 'prefix-icon' })
|
|
196
|
+
},
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const SuffixIcon = {
|
|
200
|
+
render() {
|
|
201
|
+
return h('svg', { 'data-cy': 'suffix-icon' })
|
|
202
|
+
},
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
cy.mount(Badge, {
|
|
206
|
+
props: { label: 'With Icons' },
|
|
207
|
+
slots: {
|
|
208
|
+
prefix: () => h(PrefixIcon),
|
|
209
|
+
suffix: () => h(SuffixIcon),
|
|
210
|
+
},
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
cy.get('[data-cy="prefix-icon"]').should('exist')
|
|
214
|
+
cy.get('[data-cy="suffix-icon"]').should('exist')
|
|
215
|
+
cy.get('.inline-flex.rounded-full').should('contain.text', 'With Icons')
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('supports numeric label', () => {
|
|
219
|
+
cy.mount(Badge, {
|
|
220
|
+
props: {
|
|
221
|
+
label: 42,
|
|
222
|
+
},
|
|
223
|
+
})
|
|
224
|
+
cy.get('.inline-flex.rounded-full').should('have.text', '42')
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
it('has correct layout classes', () => {
|
|
228
|
+
cy.mount(Badge, {
|
|
229
|
+
props: {
|
|
230
|
+
label: 'Test',
|
|
231
|
+
},
|
|
232
|
+
})
|
|
233
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'inline-flex')
|
|
234
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'items-center')
|
|
235
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'rounded-full')
|
|
236
|
+
cy.get('.inline-flex.rounded-full').should('have.class', 'whitespace-nowrap')
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
it('renders prefix slot with correct size constraints', () => {
|
|
240
|
+
const TestIcon = {
|
|
241
|
+
render() {
|
|
242
|
+
return h('svg', { 'data-cy': 'prefix-icon', class: 'w-4 h-4' })
|
|
243
|
+
},
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Test with sm size
|
|
247
|
+
cy.mount(Badge, {
|
|
248
|
+
props: { label: 'SM', size: 'sm' },
|
|
249
|
+
slots: {
|
|
250
|
+
prefix: () => h(TestIcon),
|
|
251
|
+
},
|
|
252
|
+
})
|
|
253
|
+
cy.get('[data-cy="prefix-icon"]').parent().should('have.class', 'max-h-4')
|
|
254
|
+
|
|
255
|
+
// Test with md size (default)
|
|
256
|
+
cy.mount(Badge, {
|
|
257
|
+
props: { label: 'MD', size: 'md' },
|
|
258
|
+
slots: {
|
|
259
|
+
prefix: () => h(TestIcon),
|
|
260
|
+
},
|
|
261
|
+
})
|
|
262
|
+
cy.get('[data-cy="prefix-icon"]').parent().should('have.class', 'max-h-4')
|
|
263
|
+
|
|
264
|
+
// Test with lg size
|
|
265
|
+
cy.mount(Badge, {
|
|
266
|
+
props: { label: 'LG', size: 'lg' },
|
|
267
|
+
slots: {
|
|
268
|
+
prefix: () => h(TestIcon),
|
|
269
|
+
},
|
|
270
|
+
})
|
|
271
|
+
cy.get('[data-cy="prefix-icon"]').parent().should('have.class', 'max-h-6')
|
|
272
|
+
})
|
|
273
|
+
})
|