@nuvia-ui/components 4.0.1
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/package.json +27 -0
- package/src/ds-accordion/ds-accordion-item.js +288 -0
- package/src/ds-accordion/ds-accordion-item.stories.js +82 -0
- package/src/ds-accordion/ds-accordion.a11y.test.js +92 -0
- package/src/ds-accordion/ds-accordion.js +68 -0
- package/src/ds-accordion/ds-accordion.stories.js +118 -0
- package/src/ds-accordion/ds-accordion.test.js +146 -0
- package/src/ds-accordion/index.js +2 -0
- package/src/ds-action-bar/ds-action-bar.js +116 -0
- package/src/ds-action-bar/ds-action-bar.stories.js +86 -0
- package/src/ds-action-bar/ds-action-bar.test.js +64 -0
- package/src/ds-action-bar/index.js +1 -0
- package/src/ds-alert/ds-alert.a11y.test.js +151 -0
- package/src/ds-alert/ds-alert.js +223 -0
- package/src/ds-alert/ds-alert.mdx +142 -0
- package/src/ds-alert/ds-alert.stories.js +166 -0
- package/src/ds-alert/ds-alert.test.js +256 -0
- package/src/ds-alert/index.js +1 -0
- package/src/ds-avatar/ds-avatar.a11y.test.js +45 -0
- package/src/ds-avatar/ds-avatar.js +216 -0
- package/src/ds-avatar/ds-avatar.stories.js +120 -0
- package/src/ds-avatar/ds-avatar.test.js +83 -0
- package/src/ds-avatar/index.js +1 -0
- package/src/ds-avatar-extended/ds-avatar-extended.a11y.test.js +29 -0
- package/src/ds-avatar-extended/ds-avatar-extended.js +108 -0
- package/src/ds-avatar-extended/ds-avatar-extended.stories.js +93 -0
- package/src/ds-avatar-extended/ds-avatar-extended.test.js +66 -0
- package/src/ds-avatar-extended/index.js +1 -0
- package/src/ds-banner/ds-banner.a11y.test.js +51 -0
- package/src/ds-banner/ds-banner.js +233 -0
- package/src/ds-banner/ds-banner.stories.js +185 -0
- package/src/ds-banner/ds-banner.test.js +116 -0
- package/src/ds-banner/index.js +1 -0
- package/src/ds-breadcrumb-item/ds-breadcrumb-item.js +135 -0
- package/src/ds-breadcrumb-item/ds-breadcrumb-item.stories.js +49 -0
- package/src/ds-breadcrumb-item/ds-breadcrumb-item.test.js +55 -0
- package/src/ds-breadcrumbs/ds-breadcrumbs.js +194 -0
- package/src/ds-breadcrumbs/ds-breadcrumbs.stories.js +54 -0
- package/src/ds-breadcrumbs/ds-breadcrumbs.test.js +33 -0
- package/src/ds-button/ds-button.a11y.test.js +49 -0
- package/src/ds-button/ds-button.js +205 -0
- package/src/ds-button/ds-button.mdx +141 -0
- package/src/ds-button/ds-button.stories.js +152 -0
- package/src/ds-button/ds-button.test.js +62 -0
- package/src/ds-button/index.js +1 -0
- package/src/ds-button-group/ds-button-group.js +82 -0
- package/src/ds-button-group/ds-button-group.mdx +39 -0
- package/src/ds-button-group/ds-button-group.stories.js +47 -0
- package/src/ds-button-group/ds-button-group.test.js +47 -0
- package/src/ds-button-group/index.js +1 -0
- package/src/ds-checkbox/ds-checkbox.a11y.test.js +79 -0
- package/src/ds-checkbox/ds-checkbox.js +271 -0
- package/src/ds-checkbox/ds-checkbox.stories.js +77 -0
- package/src/ds-checkbox/ds-checkbox.test.js +191 -0
- package/src/ds-checkbox/index.js +1 -0
- package/src/ds-checkbox-group/ds-checkbox-group.a11y.test.js +146 -0
- package/src/ds-checkbox-group/ds-checkbox-group.js +235 -0
- package/src/ds-checkbox-group/ds-checkbox-group.stories.js +210 -0
- package/src/ds-checkbox-group/ds-checkbox-group.test.js +150 -0
- package/src/ds-checkbox-group/index.js +1 -0
- package/src/ds-dialog/ds-dialog.js +466 -0
- package/src/ds-dialog/ds-dialog.stories.js +274 -0
- package/src/ds-dialog/ds-dialog.test.js +441 -0
- package/src/ds-dialog/index.js +1 -0
- package/src/ds-dropdown/ds-dropdown.a11y.test.js +80 -0
- package/src/ds-dropdown/ds-dropdown.js +891 -0
- package/src/ds-dropdown/ds-dropdown.stories.js +259 -0
- package/src/ds-dropdown/ds-dropdown.test.js +268 -0
- package/src/ds-dropdown/index.js +1 -0
- package/src/ds-dropdown-group/ds-dropdown-group.js +55 -0
- package/src/ds-dropdown-panel/ds-dropdown-panel.js +34 -0
- package/src/ds-file-uploaded/ds-file-uploaded.a11y.test.js +40 -0
- package/src/ds-file-uploaded/ds-file-uploaded.js +135 -0
- package/src/ds-file-uploaded/ds-file-uploaded.mdx +33 -0
- package/src/ds-file-uploaded/ds-file-uploaded.stories.js +81 -0
- package/src/ds-file-uploaded/ds-file-uploaded.test.js +85 -0
- package/src/ds-file-uploader/ds-file-uploader.a11y.test.js +61 -0
- package/src/ds-file-uploader/ds-file-uploader.js +442 -0
- package/src/ds-file-uploader/ds-file-uploader.mdx +44 -0
- package/src/ds-file-uploader/ds-file-uploader.stories.js +76 -0
- package/src/ds-file-uploader/ds-file-uploader.test.js +142 -0
- package/src/ds-header/ds-header.a11y.test.js +38 -0
- package/src/ds-header/ds-header.js +149 -0
- package/src/ds-header/ds-header.stories.js +63 -0
- package/src/ds-header/ds-header.test.js +52 -0
- package/src/ds-header/index.js +1 -0
- package/src/ds-header-nav/ds-header-nav.a11y.test.js +69 -0
- package/src/ds-header-nav/ds-header-nav.js +114 -0
- package/src/ds-header-nav/ds-header-nav.stories.js +17 -0
- package/src/ds-header-nav/ds-header-nav.test.js +93 -0
- package/src/ds-header-nav-item/ds-header-nav-item.a11y.test.js +71 -0
- package/src/ds-header-nav-item/ds-header-nav-item.js +124 -0
- package/src/ds-header-nav-item/ds-header-nav-item.stories.js +43 -0
- package/src/ds-header-nav-item/ds-header-nav-item.test.js +61 -0
- package/src/ds-icon/ds-icon.a11y.test.js +49 -0
- package/src/ds-icon/ds-icon.js +75 -0
- package/src/ds-icon/ds-icon.mdx +36 -0
- package/src/ds-icon/ds-icon.stories.js +88 -0
- package/src/ds-icon/ds-icon.test.js +97 -0
- package/src/ds-icon/index.js +1 -0
- package/src/ds-icon-button/ds-icon-button.a11y.test.js +55 -0
- package/src/ds-icon-button/ds-icon-button.js +224 -0
- package/src/ds-icon-button/ds-icon-button.mdx +131 -0
- package/src/ds-icon-button/ds-icon-button.stories.js +128 -0
- package/src/ds-icon-button/ds-icon-button.test.js +90 -0
- package/src/ds-icon-button/index.js +1 -0
- package/src/ds-input/ds-input.a11y.test.js +145 -0
- package/src/ds-input/ds-input.js +645 -0
- package/src/ds-input/ds-input.mdx +251 -0
- package/src/ds-input/ds-input.stories.js +298 -0
- package/src/ds-input/ds-input.test.js +792 -0
- package/src/ds-input/index.js +1 -0
- package/src/ds-link/ds-link.js +111 -0
- package/src/ds-link/ds-link.stories.js +56 -0
- package/src/ds-link/ds-link.test.js +74 -0
- package/src/ds-list-item/ds-list-item.a11y.test.js +39 -0
- package/src/ds-list-item/ds-list-item.js +292 -0
- package/src/ds-list-item/ds-list-item.stories.js +101 -0
- package/src/ds-list-item/ds-list-item.test.js +63 -0
- package/src/ds-menu/ds-menu.js +30 -0
- package/src/ds-menu/ds-menu.stories.js +120 -0
- package/src/ds-menu/ds-menu.test.js +123 -0
- package/src/ds-menu-group/ds-menu-group.js +101 -0
- package/src/ds-menu-group/ds-menu-group.stories.js +99 -0
- package/src/ds-nav-item/ds-nav-item.a11y.test.js +91 -0
- package/src/ds-nav-item/ds-nav-item.js +307 -0
- package/src/ds-nav-item/ds-nav-item.stories.js +99 -0
- package/src/ds-nav-item/ds-nav-item.test.js +169 -0
- package/src/ds-nav-item/index.js +1 -0
- package/src/ds-nav-vertical/ds-nav-vertical.a11y.test.js +69 -0
- package/src/ds-nav-vertical/ds-nav-vertical.js +173 -0
- package/src/ds-nav-vertical/ds-nav-vertical.stories.js +124 -0
- package/src/ds-nav-vertical/ds-nav-vertical.test.js +176 -0
- package/src/ds-nav-vertical/index.js +1 -0
- package/src/ds-pagination/ds-pagination.a11y.test.js +50 -0
- package/src/ds-pagination/ds-pagination.js +232 -0
- package/src/ds-pagination/ds-pagination.stories.js +63 -0
- package/src/ds-pagination/ds-pagination.test.js +141 -0
- package/src/ds-pagination/index.js +1 -0
- package/src/ds-progress-bar/ds-progress-bar.a11y.test.js +25 -0
- package/src/ds-progress-bar/ds-progress-bar.js +81 -0
- package/src/ds-progress-bar/ds-progress-bar.stories.js +69 -0
- package/src/ds-progress-bar/ds-progress-bar.test.js +60 -0
- package/src/ds-radio/ds-radio.a11y.test.js +69 -0
- package/src/ds-radio/ds-radio.js +240 -0
- package/src/ds-radio/ds-radio.stories.js +102 -0
- package/src/ds-radio/ds-radio.test.js +114 -0
- package/src/ds-radio/index.js +1 -0
- package/src/ds-radio-group/ds-radio-group.a11y.test.js +164 -0
- package/src/ds-radio-group/ds-radio-group.js +257 -0
- package/src/ds-radio-group/ds-radio-group.stories.js +247 -0
- package/src/ds-radio-group/ds-radio-group.test.js +194 -0
- package/src/ds-radio-group/index.js +1 -0
- package/src/ds-rich-list/ds-rich-list.js +246 -0
- package/src/ds-rich-list/ds-rich-list.stories.js +368 -0
- package/src/ds-rich-list/ds-rich-list.test.js +293 -0
- package/src/ds-rich-list-item/ds-rich-list-item.js +579 -0
- package/src/ds-rich-list-item/ds-rich-list-item.stories.js +197 -0
- package/src/ds-rich-list-item/ds-rich-list-item.test.js +434 -0
- package/src/ds-slider/ds-slider.js +399 -0
- package/src/ds-slider/ds-slider.stories.js +107 -0
- package/src/ds-slider/ds-slider.test.js +308 -0
- package/src/ds-spinner/ds-spinner.js +173 -0
- package/src/ds-spinner/ds-spinner.stories.js +52 -0
- package/src/ds-spinner/ds-spinner.test.js +50 -0
- package/src/ds-status-border/ds-status-border.js +88 -0
- package/src/ds-status-border/ds-status-border.stories.js +242 -0
- package/src/ds-status-border/ds-status-border.test.js +168 -0
- package/src/ds-stepper/ds-stepper.a11y.test.js +198 -0
- package/src/ds-stepper/ds-stepper.js +207 -0
- package/src/ds-stepper/ds-stepper.stories.js +530 -0
- package/src/ds-stepper/ds-stepper.test.js +311 -0
- package/src/ds-stepper-item/ds-stepper-item.js +485 -0
- package/src/ds-stepper-item/ds-stepper-item.stories.js +288 -0
- package/src/ds-switch/ds-switch.js +348 -0
- package/src/ds-switch/ds-switch.stories.js +145 -0
- package/src/ds-switch/ds-switch.test.js +226 -0
- package/src/ds-switch/index.js +1 -0
- package/src/ds-tab-item/ds-tab-item.js +341 -0
- package/src/ds-tab-item/ds-tab-item.stories.js +69 -0
- package/src/ds-tabs/ds-tab-panel.js +48 -0
- package/src/ds-tabs/ds-tabs.a11y.test.js +56 -0
- package/src/ds-tabs/ds-tabs.js +180 -0
- package/src/ds-tabs/ds-tabs.stories.js +152 -0
- package/src/ds-tabs/ds-tabs.test.js +306 -0
- package/src/ds-tabs/index.js +3 -0
- package/src/ds-tag-action/ds-tag-action.a11y.test.js +32 -0
- package/src/ds-tag-action/ds-tag-action.js +185 -0
- package/src/ds-tag-action/ds-tag-action.stories.js +55 -0
- package/src/ds-tag-action/ds-tag-action.test.js +44 -0
- package/src/ds-tag-removable/ds-tag-removable.a11y.test.js +24 -0
- package/src/ds-tag-removable/ds-tag-removable.js +146 -0
- package/src/ds-tag-removable/ds-tag-removable.stories.js +52 -0
- package/src/ds-tag-removable/ds-tag-removable.test.js +46 -0
- package/src/ds-tag-status/ds-tag-status.a11y.test.js +93 -0
- package/src/ds-tag-status/ds-tag-status.js +164 -0
- package/src/ds-tag-status/ds-tag-status.stories.js +200 -0
- package/src/ds-tag-status/ds-tag-status.test.js +140 -0
- package/src/ds-tag-status/index.js +1 -0
- package/src/ds-textarea/ds-textarea-clearable.test.js +89 -0
- package/src/ds-textarea/ds-textarea.a11y.test.js +66 -0
- package/src/ds-textarea/ds-textarea.js +505 -0
- package/src/ds-textarea/ds-textarea.stories.js +335 -0
- package/src/ds-textarea/ds-textarea.test.js +218 -0
- package/src/ds-textarea/index.js +1 -0
- package/src/ds-thumbnail/ds-thumbnail.js +207 -0
- package/src/ds-thumbnail/ds-thumbnail.stories.js +217 -0
- package/src/ds-thumbnail/ds-thumbnail.test.js +220 -0
- package/src/ds-toast/ds-toast-provider.js +110 -0
- package/src/ds-toast/ds-toast.a11y.test.js +34 -0
- package/src/ds-toast/ds-toast.js +243 -0
- package/src/ds-toast/ds-toast.stories.js +143 -0
- package/src/ds-toast/ds-toast.test.js +93 -0
- package/src/ds-toast/index.js +2 -0
- package/src/ds-tooltip/ds-tooltip.a11y.test.js +110 -0
- package/src/ds-tooltip/ds-tooltip.js +217 -0
- package/src/ds-tooltip/ds-tooltip.mdx +75 -0
- package/src/ds-tooltip/ds-tooltip.stories.js +72 -0
- package/src/ds-tooltip/ds-tooltip.test.js +191 -0
- package/src/ds-tooltip/index.js +1 -0
- package/src/ds-tooltip/positioner.js +117 -0
- package/src/index.js +50 -0
- package/src/mixins/field-label.mixin.js +113 -0
- package/src/mixins/field-message.mixin.js +66 -0
- package/src/token-provider/index.js +1 -0
- package/src/token-provider/token-provider.a11y.test.js +44 -0
- package/src/token-provider/token-provider.js +85 -0
- package/src/token-provider/token-provider.stories.js +105 -0
- package/src/token-provider/token-provider.test.js +134 -0
- package/src/utils/number-input.utils.js +42 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { html } from 'lit';
|
|
2
|
+
import './ds-banner.js';
|
|
3
|
+
import '../ds-button/ds-button.js';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: 'Components/Banner',
|
|
7
|
+
component: 'ds-banner',
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
argTypes: {
|
|
10
|
+
status: {
|
|
11
|
+
control: 'select',
|
|
12
|
+
options: ['error', 'warning', 'success', 'info'],
|
|
13
|
+
description: 'Status type that determines background color and icon',
|
|
14
|
+
table: {
|
|
15
|
+
defaultValue: { summary: 'info' }
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
title: {
|
|
19
|
+
control: 'text',
|
|
20
|
+
description: 'Optional title text'
|
|
21
|
+
},
|
|
22
|
+
message: {
|
|
23
|
+
control: 'text',
|
|
24
|
+
description: 'Message text'
|
|
25
|
+
},
|
|
26
|
+
dismissible: {
|
|
27
|
+
control: 'boolean',
|
|
28
|
+
description: 'Show/hide close button'
|
|
29
|
+
},
|
|
30
|
+
fixed: {
|
|
31
|
+
control: 'boolean',
|
|
32
|
+
description: 'Fix banner to the top of the viewport'
|
|
33
|
+
},
|
|
34
|
+
hideIcon: {
|
|
35
|
+
control: 'boolean',
|
|
36
|
+
description: 'Hide status icon'
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const Template = (args) => html`
|
|
42
|
+
<ds-banner
|
|
43
|
+
.status=${args.status}
|
|
44
|
+
.title=${args.title}
|
|
45
|
+
.message=${args.message}
|
|
46
|
+
?dismissible=${args.dismissible}
|
|
47
|
+
?fixed=${args.fixed}
|
|
48
|
+
?hide-icon=${args.hideIcon}
|
|
49
|
+
>
|
|
50
|
+
${args.slotContent ? html`${args.slotContent}` : ''}
|
|
51
|
+
</ds-banner>
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Default banner with info status
|
|
56
|
+
*/
|
|
57
|
+
export const Default = {
|
|
58
|
+
args: {
|
|
59
|
+
status: 'info',
|
|
60
|
+
message: 'System update scheduled for tonight at 2 AM.',
|
|
61
|
+
dismissible: false,
|
|
62
|
+
fixed: false,
|
|
63
|
+
hideIcon: false
|
|
64
|
+
},
|
|
65
|
+
render: (args) => Template(args)
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Banner with title property
|
|
70
|
+
*/
|
|
71
|
+
export const WithTitle = {
|
|
72
|
+
args: {
|
|
73
|
+
status: 'warning',
|
|
74
|
+
title: 'Maintenance Alert',
|
|
75
|
+
message: 'The system will be down for maintenance from 2 AM to 4 AM UTC.',
|
|
76
|
+
dismissible: true,
|
|
77
|
+
fixed: false,
|
|
78
|
+
hideIcon: false
|
|
79
|
+
},
|
|
80
|
+
render: (args) => Template(args)
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Success banner with dismiss action
|
|
85
|
+
*/
|
|
86
|
+
export const Success = {
|
|
87
|
+
args: {
|
|
88
|
+
status: 'success',
|
|
89
|
+
message: 'Your profile settings have been successfully updated.',
|
|
90
|
+
dismissible: true,
|
|
91
|
+
fixed: false,
|
|
92
|
+
hideIcon: false
|
|
93
|
+
},
|
|
94
|
+
render: (args) => Template(args)
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Warning banner
|
|
99
|
+
*/
|
|
100
|
+
export const Warning = {
|
|
101
|
+
args: {
|
|
102
|
+
status: 'warning',
|
|
103
|
+
message: 'Your subscription is about to expire. Please renew to avoid interruption.',
|
|
104
|
+
dismissible: true,
|
|
105
|
+
fixed: false,
|
|
106
|
+
hideIcon: false
|
|
107
|
+
},
|
|
108
|
+
render: (args) => Template(args)
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Error banner
|
|
113
|
+
*/
|
|
114
|
+
export const Error = {
|
|
115
|
+
args: {
|
|
116
|
+
status: 'error',
|
|
117
|
+
message: 'Failed to connect to the database. Please try again later.',
|
|
118
|
+
dismissible: true,
|
|
119
|
+
fixed: false,
|
|
120
|
+
hideIcon: false
|
|
121
|
+
},
|
|
122
|
+
render: (args) => Template(args)
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Banner with custom actions
|
|
127
|
+
*/
|
|
128
|
+
export const WithActions = {
|
|
129
|
+
args: {
|
|
130
|
+
status: 'info',
|
|
131
|
+
message: 'A new version of the application is available.',
|
|
132
|
+
dismissible: true
|
|
133
|
+
},
|
|
134
|
+
render: (args) => html`
|
|
135
|
+
<ds-banner
|
|
136
|
+
.status=${args.status}
|
|
137
|
+
.title=${args.title}
|
|
138
|
+
.message=${args.message}
|
|
139
|
+
?dismissible=${args.dismissible}
|
|
140
|
+
?fixed=${args.fixed}
|
|
141
|
+
?hide-icon=${args.hideIcon}
|
|
142
|
+
>
|
|
143
|
+
<div slot="actions" style="display: flex; gap: 8px;">
|
|
144
|
+
<ds-button variant="action">Update Now</ds-button>
|
|
145
|
+
<ds-button variant="action">Dismiss</ds-button>
|
|
146
|
+
</div>
|
|
147
|
+
</ds-banner>
|
|
148
|
+
`
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Fixed banner at the top of the page.
|
|
153
|
+
* (View in Canvas mode to see it stick to the top)
|
|
154
|
+
*/
|
|
155
|
+
export const FixedTop = {
|
|
156
|
+
args: {
|
|
157
|
+
status: 'warning',
|
|
158
|
+
message: 'This banner is fixed to the top of the viewport. Scroll down to see it stick.',
|
|
159
|
+
fixed: true,
|
|
160
|
+
dismissible: true
|
|
161
|
+
},
|
|
162
|
+
render: (args) => html`
|
|
163
|
+
<div style="min-height: 200px; transform: scale(1);">
|
|
164
|
+
${Template(args)}
|
|
165
|
+
<div style="padding: 60px 20px;">
|
|
166
|
+
<p>Scroll content...</p>
|
|
167
|
+
<p>Scroll content...</p>
|
|
168
|
+
<p>Scroll content...</p>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
`
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Demonstrating the 800px max-width constraint on the message text.
|
|
176
|
+
* The banner background spans full width, but the text wraps at 800px.
|
|
177
|
+
*/
|
|
178
|
+
export const LongMessage = {
|
|
179
|
+
args: {
|
|
180
|
+
status: 'info',
|
|
181
|
+
message: 'This is a very long message that demonstrates the 800px max-width constraint for readability. Even though the banner background extends to the full width of the viewport, this text content will wrap once it exceeds 800 pixels. This ensures that lines of text do not become too long to read comfortably on wide screens, adhering to accessibility best practices.',
|
|
182
|
+
dismissible: true
|
|
183
|
+
},
|
|
184
|
+
render: (args) => Template(args)
|
|
185
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import './ds-banner.js';
|
|
3
|
+
|
|
4
|
+
describe('ds-banner', () => {
|
|
5
|
+
let container;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
container = document.createElement('div');
|
|
9
|
+
document.body.appendChild(container);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
container.remove();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('renders with default values', async () => {
|
|
17
|
+
container.innerHTML = '<ds-banner message="Default message"></ds-banner>';
|
|
18
|
+
const element = container.querySelector('ds-banner');
|
|
19
|
+
await element.updateComplete;
|
|
20
|
+
|
|
21
|
+
expect(element.status).toBe('info');
|
|
22
|
+
expect(element.title).toBe('');
|
|
23
|
+
expect(element.message).toBe('Default message');
|
|
24
|
+
expect(element.dismissible).toBe(false);
|
|
25
|
+
expect(element.fixed).toBe(false);
|
|
26
|
+
|
|
27
|
+
const banner = element.shadowRoot.querySelector('.banner');
|
|
28
|
+
expect(banner).toBeTruthy();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('renders title when provided', async () => {
|
|
32
|
+
container.innerHTML = '<ds-banner title="Test Title" message="Test Message"></ds-banner>';
|
|
33
|
+
const element = container.querySelector('ds-banner');
|
|
34
|
+
await element.updateComplete;
|
|
35
|
+
|
|
36
|
+
expect(element.title).toBe('Test Title');
|
|
37
|
+
const titleEl = element.shadowRoot.querySelector('.title');
|
|
38
|
+
expect(titleEl).toBeTruthy();
|
|
39
|
+
expect(titleEl.textContent).toBe('Test Title');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('reflects fixed attribute', async () => {
|
|
43
|
+
container.innerHTML = '<ds-banner fixed></ds-banner>';
|
|
44
|
+
const element = container.querySelector('ds-banner');
|
|
45
|
+
await element.updateComplete;
|
|
46
|
+
|
|
47
|
+
expect(element.hasAttribute('fixed')).toBe(true);
|
|
48
|
+
expect(element.fixed).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('renders correct icon for each status', async () => {
|
|
52
|
+
const statuses = {
|
|
53
|
+
error: 'cancel',
|
|
54
|
+
warning: 'warning',
|
|
55
|
+
success: 'check-circle',
|
|
56
|
+
info: 'info'
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
for (const [status, iconName] of Object.entries(statuses)) {
|
|
60
|
+
container.innerHTML = `<ds-banner status="${status}"></ds-banner>`;
|
|
61
|
+
const element = container.querySelector('ds-banner');
|
|
62
|
+
await element.updateComplete;
|
|
63
|
+
|
|
64
|
+
const icon = element.shadowRoot.querySelector('ds-icon');
|
|
65
|
+
expect(icon.getAttribute('name')).toBe(iconName);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('hides icon when hideIcon is true', async () => {
|
|
70
|
+
container.innerHTML = '<ds-banner hide-icon></ds-banner>';
|
|
71
|
+
const element = container.querySelector('ds-banner');
|
|
72
|
+
await element.updateComplete;
|
|
73
|
+
|
|
74
|
+
const icon = element.shadowRoot.querySelector('ds-icon');
|
|
75
|
+
expect(icon).toBeNull();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('shows close button only when dismissible', async () => {
|
|
79
|
+
container.innerHTML = '<ds-banner></ds-banner>';
|
|
80
|
+
const element = container.querySelector('ds-banner');
|
|
81
|
+
await element.updateComplete;
|
|
82
|
+
|
|
83
|
+
expect(element.shadowRoot.querySelector('ds-icon-button')).toBeNull();
|
|
84
|
+
|
|
85
|
+
element.dismissible = true;
|
|
86
|
+
await element.updateComplete;
|
|
87
|
+
|
|
88
|
+
expect(element.shadowRoot.querySelector('ds-icon-button')).toBeTruthy();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('dispatches ds-dismiss event when close button is clicked', async () => {
|
|
92
|
+
const spy = vi.fn();
|
|
93
|
+
container.innerHTML = '<ds-banner dismissible></ds-banner>';
|
|
94
|
+
const element = container.querySelector('ds-banner');
|
|
95
|
+
element.addEventListener('ds-dismiss', spy);
|
|
96
|
+
await element.updateComplete;
|
|
97
|
+
|
|
98
|
+
const button = element.shadowRoot.querySelector('ds-icon-button');
|
|
99
|
+
button.click();
|
|
100
|
+
|
|
101
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('renders actions slot when content is provided', async () => {
|
|
105
|
+
container.innerHTML = `
|
|
106
|
+
<ds-banner>
|
|
107
|
+
<div slot="actions">Action</div>
|
|
108
|
+
</ds-banner>
|
|
109
|
+
`;
|
|
110
|
+
const element = container.querySelector('ds-banner');
|
|
111
|
+
await element.updateComplete;
|
|
112
|
+
|
|
113
|
+
const actionsSlot = element.shadowRoot.querySelector('slot[name="actions"]');
|
|
114
|
+
expect(actionsSlot).toBeTruthy();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DsBanner } from './ds-banner.js';
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { LitElement, html, css, nothing } from 'lit';
|
|
2
|
+
import '../ds-link/ds-link.js';
|
|
3
|
+
import '../ds-icon/ds-icon.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @element ds-breadcrumb-item
|
|
7
|
+
* @summary A single item within a breadcrumb navigation, representing a page or a collapsed set of pages.
|
|
8
|
+
*
|
|
9
|
+
* @prop {string} href - URL for the link. If omitted, and not current/collapsed, simple text is rendered (though typically breadcrumbs are links).
|
|
10
|
+
* @prop {string} label - Text to display.
|
|
11
|
+
* @prop {boolean} current - Whether this item represents the current page. Renders as bold text, not a link.
|
|
12
|
+
* @prop {boolean} collapsed - Whether this item represents a collapsed section (renders ". . .").
|
|
13
|
+
* @prop {boolean} last - If true, hides the separator icon.
|
|
14
|
+
*/
|
|
15
|
+
export class DsBreadcrumbItem extends LitElement {
|
|
16
|
+
static properties = {
|
|
17
|
+
href: { type: String },
|
|
18
|
+
label: { type: String },
|
|
19
|
+
icon: { type: String },
|
|
20
|
+
current: { type: Boolean, reflect: true },
|
|
21
|
+
collapsed: { type: Boolean, reflect: true },
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
static styles = css`
|
|
25
|
+
:host {
|
|
26
|
+
display: inline-flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
gap: var(--ds-space-sm, 8px);
|
|
29
|
+
color: var(--ds-color-text-default);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Accessibility Role */
|
|
33
|
+
:host {
|
|
34
|
+
role: 'listitem';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* Current Item (Static Text) */
|
|
38
|
+
span.current {
|
|
39
|
+
font: var(--ds-typo-content-body-bold);
|
|
40
|
+
color: var(--ds-color-text-default);
|
|
41
|
+
display: inline-flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
gap: var(--ds-space-xs, 4px);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Separator Icon */
|
|
47
|
+
ds-icon.separator {
|
|
48
|
+
color: var(--ds-color-icon-default);
|
|
49
|
+
display: var(--ds-breadcrumb-separator-display, inline-flex);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Strictly hide separator if current */
|
|
53
|
+
:host([current]) ds-icon.separator {
|
|
54
|
+
display: none !important;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Collapsed Action Button */
|
|
58
|
+
button.collapsed-action {
|
|
59
|
+
appearance: none;
|
|
60
|
+
background: none;
|
|
61
|
+
border: none;
|
|
62
|
+
padding: 0;
|
|
63
|
+
margin: 0;
|
|
64
|
+
font: inherit;
|
|
65
|
+
color: var(--ds-color-text-link, #005cc5); /* Fallback color */
|
|
66
|
+
cursor: pointer;
|
|
67
|
+
text-decoration: underline;
|
|
68
|
+
display: inline-flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
min-width: 1.5em; /* Ensure clickable area */
|
|
71
|
+
justify-content: center;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
button.collapsed-action:hover {
|
|
75
|
+
color: var(--ds-color-text-link-hover);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
button.collapsed-action:focus-visible {
|
|
79
|
+
outline: 2px solid var(--ds-color-border-focus);
|
|
80
|
+
border-radius: var(--ds-radius-container, 0px);
|
|
81
|
+
}
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
constructor() {
|
|
85
|
+
super();
|
|
86
|
+
this.current = false;
|
|
87
|
+
this.collapsed = false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
_handleExpand() {
|
|
91
|
+
this.dispatchEvent(new CustomEvent('ds-expand', {
|
|
92
|
+
bubbles: true,
|
|
93
|
+
composed: true
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
render() {
|
|
98
|
+
// 1. Current Page (Static Text, Bold)
|
|
99
|
+
if (this.current) {
|
|
100
|
+
return html`
|
|
101
|
+
<span class="current" aria-current="page">
|
|
102
|
+
${this.icon ? html`<ds-icon name="${this.icon}" size="sm"></ds-icon>` : nothing}
|
|
103
|
+
${this.label}
|
|
104
|
+
</span>
|
|
105
|
+
`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 2. Collapsed State (Action Button)
|
|
109
|
+
if (this.collapsed) {
|
|
110
|
+
return html`
|
|
111
|
+
<button
|
|
112
|
+
type="button"
|
|
113
|
+
class="collapsed-action"
|
|
114
|
+
aria-label="Show hidden breadcrumbs"
|
|
115
|
+
@click=${this._handleExpand}
|
|
116
|
+
>
|
|
117
|
+
. . .
|
|
118
|
+
</button>
|
|
119
|
+
<ds-icon class="separator" name="chevron-right" size="xs" aria-hidden="true"></ds-icon>
|
|
120
|
+
`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 3. Standard Link
|
|
124
|
+
return html`
|
|
125
|
+
<ds-link href=${this.href} .inline=${false} icon-start=${this.icon}>
|
|
126
|
+
${this.label}
|
|
127
|
+
</ds-link>
|
|
128
|
+
<ds-icon class="separator" name="chevron-right" size="xs" aria-hidden="true"></ds-icon>
|
|
129
|
+
`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!customElements.get('ds-breadcrumb-item')) {
|
|
134
|
+
customElements.define('ds-breadcrumb-item', DsBreadcrumbItem);
|
|
135
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { html } from 'lit';
|
|
2
|
+
import './ds-breadcrumb-item.js';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Components/Breadcrumb Item',
|
|
6
|
+
component: 'ds-breadcrumb-item',
|
|
7
|
+
argTypes: {
|
|
8
|
+
label: { control: 'text' },
|
|
9
|
+
href: { control: 'text' },
|
|
10
|
+
current: { control: 'boolean' },
|
|
11
|
+
collapsed: { control: 'boolean' },
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const Template = (args) => html`
|
|
16
|
+
<ds-breadcrumb-item
|
|
17
|
+
label="${args.label || ''}"
|
|
18
|
+
href="${args.href || ''}"
|
|
19
|
+
icon="${args.icon || ''}"
|
|
20
|
+
?current=${args.current}
|
|
21
|
+
?collapsed=${args.collapsed}
|
|
22
|
+
></ds-breadcrumb-item>
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
export const Default = Template.bind({});
|
|
26
|
+
Default.args = {
|
|
27
|
+
label: 'Home',
|
|
28
|
+
href: '/',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const Current = Template.bind({});
|
|
32
|
+
Current.args = {
|
|
33
|
+
label: 'Current Page',
|
|
34
|
+
current: true,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const Collapsed = Template.bind({});
|
|
38
|
+
Collapsed.args = {
|
|
39
|
+
href: '#',
|
|
40
|
+
collapsed: true,
|
|
41
|
+
label: 'Expand',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const WithIcon = Template.bind({});
|
|
45
|
+
WithIcon.args = {
|
|
46
|
+
label: 'Home',
|
|
47
|
+
href: '/',
|
|
48
|
+
icon: 'home',
|
|
49
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import './ds-breadcrumb-item.js';
|
|
3
|
+
|
|
4
|
+
describe('ds-breadcrumb-item', () => {
|
|
5
|
+
let container;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
container = document.createElement('div');
|
|
9
|
+
document.body.appendChild(container);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
container.remove();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('renders label and separator by default', async () => {
|
|
17
|
+
container.innerHTML = '<ds-breadcrumb-item label="Home" href="/home"></ds-breadcrumb-item>';
|
|
18
|
+
const el = container.querySelector('ds-breadcrumb-item');
|
|
19
|
+
await new Promise(r => setTimeout(r, 50));
|
|
20
|
+
|
|
21
|
+
const link = el.shadowRoot.querySelector('ds-link');
|
|
22
|
+
expect(link).toBeTruthy();
|
|
23
|
+
expect(link.textContent.trim()).toBe('Home');
|
|
24
|
+
|
|
25
|
+
const icon = el.shadowRoot.querySelector('ds-icon[name="chevron-right"]');
|
|
26
|
+
expect(icon).toBeTruthy();
|
|
27
|
+
expect(icon.getAttribute('aria-hidden')).toBe('true');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('renders current page as text without separator', async () => {
|
|
31
|
+
container.innerHTML = '<ds-breadcrumb-item label="Current" current></ds-breadcrumb-item>';
|
|
32
|
+
const el = container.querySelector('ds-breadcrumb-item');
|
|
33
|
+
await new Promise(r => setTimeout(r, 50));
|
|
34
|
+
|
|
35
|
+
const span = el.shadowRoot.querySelector('span.current');
|
|
36
|
+
expect(span).toBeTruthy();
|
|
37
|
+
expect(span.getAttribute('aria-current')).toBe('page');
|
|
38
|
+
expect(span.textContent.trim()).toBe('Current');
|
|
39
|
+
|
|
40
|
+
const icon = el.shadowRoot.querySelector('ds-icon');
|
|
41
|
+
expect(icon).toBeNull();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('renders collapsed state with ellipses', async () => {
|
|
45
|
+
container.innerHTML = '<ds-breadcrumb-item collapsed href="#"></ds-breadcrumb-item>';
|
|
46
|
+
const el = container.querySelector('ds-breadcrumb-item');
|
|
47
|
+
await new Promise(r => setTimeout(r, 50));
|
|
48
|
+
|
|
49
|
+
const button = el.shadowRoot.querySelector('button.collapsed-action');
|
|
50
|
+
expect(button.textContent.trim()).toBe('. . .');
|
|
51
|
+
|
|
52
|
+
const icon = el.shadowRoot.querySelector('ds-icon[name="chevron-right"]');
|
|
53
|
+
expect(icon).toBeTruthy();
|
|
54
|
+
});
|
|
55
|
+
});
|