@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,152 @@
|
|
|
1
|
+
import { html } from 'lit';
|
|
2
|
+
import './ds-tabs.js';
|
|
3
|
+
import '../ds-tab-item/ds-tab-item.js';
|
|
4
|
+
import '../ds-icon/ds-icon.js';
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
title: 'Components/Tabs',
|
|
8
|
+
component: 'ds-tabs',
|
|
9
|
+
argTypes: {
|
|
10
|
+
variant: {
|
|
11
|
+
control: { type: 'select' },
|
|
12
|
+
options: ['line', 'contained'],
|
|
13
|
+
defaultValue: 'line',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const Template = (args) => html`
|
|
19
|
+
<style>
|
|
20
|
+
/* Demo container styles */
|
|
21
|
+
.demo-container {
|
|
22
|
+
font-family: var(--ds-font-family-content);
|
|
23
|
+
}
|
|
24
|
+
p {
|
|
25
|
+
padding: 16px;
|
|
26
|
+
color: var(--ds-color-text-secondary);
|
|
27
|
+
}
|
|
28
|
+
</style>
|
|
29
|
+
<div class="demo-container">
|
|
30
|
+
<ds-tabs variant=${args.variant || 'line'}>
|
|
31
|
+
<ds-tab-item label="First Tab" selected></ds-tab-item>
|
|
32
|
+
<ds-tab-item label="Second Tab"></ds-tab-item>
|
|
33
|
+
<ds-tab-item label="Third Tab"></ds-tab-item>
|
|
34
|
+
<ds-tab-item label="Disabled Tab" disabled></ds-tab-item>
|
|
35
|
+
</ds-tabs>
|
|
36
|
+
<p>Content for the selected tab would go here.</p>
|
|
37
|
+
</div>
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
export const Default = Template.bind({});
|
|
41
|
+
Default.args = {
|
|
42
|
+
variant: 'line',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const WithIcons = () => html`
|
|
46
|
+
<ds-tabs>
|
|
47
|
+
<ds-tab-item label="Home" selected>
|
|
48
|
+
<ds-icon slot="icon" name="home" size="sm"></ds-icon>
|
|
49
|
+
</ds-tab-item>
|
|
50
|
+
<ds-tab-item label="Settings">
|
|
51
|
+
<ds-icon slot="icon" name="settings" size="sm"></ds-icon>
|
|
52
|
+
</ds-tab-item>
|
|
53
|
+
<ds-tab-item label="Profile">
|
|
54
|
+
<ds-icon slot="icon" name="person" size="sm"></ds-icon>
|
|
55
|
+
</ds-tab-item>
|
|
56
|
+
</ds-tabs>
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
export const InteractiveDemo = () => html`
|
|
60
|
+
<script>
|
|
61
|
+
// Simple script to handle tab switching for the demo
|
|
62
|
+
document.addEventListener('ds-tab-selected', (e) => {
|
|
63
|
+
// Find parent tabs
|
|
64
|
+
const tabs = e.target.closest('ds-tabs');
|
|
65
|
+
if(tabs && tabs.contains(document.currentScript?.parentElement)) {
|
|
66
|
+
// Logic is mainly handled by ds-tabs internal handler for selection state visualization
|
|
67
|
+
console.log('Tab selected:', e.target.label);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
</script>
|
|
71
|
+
<ds-tabs>
|
|
72
|
+
<ds-tab-item label="Overview" selected></ds-tab-item>
|
|
73
|
+
<ds-tab-item label="Details"></ds-tab-item>
|
|
74
|
+
<ds-tab-item label="Reviews"></ds-tab-item>
|
|
75
|
+
</ds-tabs>
|
|
76
|
+
`;
|
|
77
|
+
|
|
78
|
+
export const Overflow = () => html`
|
|
79
|
+
<div style="max-width: 300px; border: 1px solid #ccc;">
|
|
80
|
+
<ds-tabs>
|
|
81
|
+
<ds-tab-item label="Tab 1" selected></ds-tab-item>
|
|
82
|
+
<ds-tab-item label="Tab 2"></ds-tab-item>
|
|
83
|
+
<ds-tab-item label="Tab 3"></ds-tab-item>
|
|
84
|
+
<ds-tab-item label="Tab 4 long label"></ds-tab-item>
|
|
85
|
+
<ds-tab-item label="Tab 5"></ds-tab-item>
|
|
86
|
+
<ds-tab-item label="Tab 6"></ds-tab-item>
|
|
87
|
+
</ds-tabs>
|
|
88
|
+
</div>
|
|
89
|
+
`;
|
|
90
|
+
|
|
91
|
+
import './ds-tab-panel.js';
|
|
92
|
+
|
|
93
|
+
// ... (existing exports)
|
|
94
|
+
|
|
95
|
+
// Full Example with Panels
|
|
96
|
+
export const Contained = Template.bind({});
|
|
97
|
+
Contained.args = {
|
|
98
|
+
variant: 'contained',
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const WithPanels = (args) => {
|
|
102
|
+
const handleTabSelected = (e) => {
|
|
103
|
+
console.log('Event ds-tab-selected captured in Story:', e);
|
|
104
|
+
const selectedTab = e.target;
|
|
105
|
+
const targetId = selectedTab.getAttribute('aria-controls');
|
|
106
|
+
|
|
107
|
+
// robustly find the wrapper: e.currentTarget is the ds-tabs list
|
|
108
|
+
const wrapper = e.currentTarget.parentElement;
|
|
109
|
+
|
|
110
|
+
if (!wrapper) {
|
|
111
|
+
console.warn('Wrapper not found');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const panels = wrapper.querySelectorAll('ds-tab-panel');
|
|
116
|
+
panels.forEach(panel => {
|
|
117
|
+
const shouldSelect = panel.id === targetId;
|
|
118
|
+
// Use property setting AND attribute for robustness
|
|
119
|
+
panel.selected = shouldSelect;
|
|
120
|
+
if (shouldSelect) {
|
|
121
|
+
console.log('Activating panel:', panel.id);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
return html`
|
|
127
|
+
<div class="tabs-wrapper">
|
|
128
|
+
<ds-tabs id="tabs-demo" @ds-tab-selected=${handleTabSelected}>
|
|
129
|
+
<ds-tab-item label="Account" selected aria-controls="panel-1"></ds-tab-item>
|
|
130
|
+
<ds-tab-item label="Notifications" aria-controls="panel-2"></ds-tab-item>
|
|
131
|
+
<ds-tab-item label="Security" aria-controls="panel-3"></ds-tab-item>
|
|
132
|
+
</ds-tabs>
|
|
133
|
+
|
|
134
|
+
<ds-tab-panel id="panel-1" aria-labelledby="tab-1" selected>
|
|
135
|
+
<h3>Account Settings</h3>
|
|
136
|
+
<p>Manage your account details properly here.</p>
|
|
137
|
+
</ds-tab-panel>
|
|
138
|
+
|
|
139
|
+
<ds-tab-panel id="panel-2" aria-labelledby="tab-2">
|
|
140
|
+
<h3>Notifications</h3>
|
|
141
|
+
<p>Choose your notification preferences.</p>
|
|
142
|
+
</ds-tab-panel>
|
|
143
|
+
|
|
144
|
+
<ds-tab-panel id="panel-3" aria-labelledby="tab-3">
|
|
145
|
+
<h3>Security</h3>
|
|
146
|
+
<p>Update your password and security settings.</p>
|
|
147
|
+
</ds-tab-panel>
|
|
148
|
+
</div>
|
|
149
|
+
`;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// Isolated TabItem story moved to ds-tab-item.stories.js
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import './ds-tabs.js';
|
|
3
|
+
import '../ds-tab-item/ds-tab-item.js';
|
|
4
|
+
|
|
5
|
+
describe('ds-tabs', () => {
|
|
6
|
+
let container;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
container = document.createElement('div');
|
|
10
|
+
document.body.appendChild(container);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
container.remove();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('renders tabs correctly', async () => {
|
|
18
|
+
container.innerHTML = `
|
|
19
|
+
<ds-tabs>
|
|
20
|
+
<ds-tab-item label="Tab 1" selected></ds-tab-item>
|
|
21
|
+
<ds-tab-item label="Tab 2"></ds-tab-item>
|
|
22
|
+
</ds-tabs>
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
// Wait for elements to be defined and rendered
|
|
26
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
27
|
+
|
|
28
|
+
const tabs = container.querySelectorAll('ds-tab-item');
|
|
29
|
+
expect(tabs.length).toBe(2);
|
|
30
|
+
expect(tabs[0].getAttribute('selected')).toBe('');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('handles click selection', async () => {
|
|
34
|
+
container.innerHTML = `
|
|
35
|
+
<ds-tabs>
|
|
36
|
+
<ds-tab-item label="Tab 1" selected></ds-tab-item>
|
|
37
|
+
<ds-tab-item label="Tab 2" id="tab2"></ds-tab-item>
|
|
38
|
+
</ds-tabs>
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
42
|
+
const tab2 = container.querySelector('#tab2');
|
|
43
|
+
|
|
44
|
+
tab2.click();
|
|
45
|
+
|
|
46
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
47
|
+
|
|
48
|
+
const tabs = container.querySelectorAll('ds-tab-item');
|
|
49
|
+
expect(tabs[0].hasAttribute('selected')).toBe(false);
|
|
50
|
+
expect(tabs[1].hasAttribute('selected')).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('handles keyboard navigation (ArrowRight)', async () => {
|
|
54
|
+
container.innerHTML = `
|
|
55
|
+
<ds-tabs>
|
|
56
|
+
<ds-tab-item label="Tab 1" selected></ds-tab-item>
|
|
57
|
+
<ds-tab-item label="Tab 2"></ds-tab-item>
|
|
58
|
+
<ds-tab-item label="Tab 3"></ds-tab-item>
|
|
59
|
+
</ds-tabs>
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
63
|
+
const tabs = container.querySelectorAll('ds-tab-item');
|
|
64
|
+
tabs[0].focus();
|
|
65
|
+
|
|
66
|
+
// Simulate ArrowRight keydown
|
|
67
|
+
tabs[0].dispatchEvent(new KeyboardEvent('keydown', {
|
|
68
|
+
key: 'ArrowRight',
|
|
69
|
+
bubbles: true,
|
|
70
|
+
composed: true
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
74
|
+
|
|
75
|
+
// Check if next tab is selected
|
|
76
|
+
expect(tabs[1].selected).toBe(true);
|
|
77
|
+
expect(document.activeElement === tabs[1]).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// KEYBOARD NAVIGATION COMPLETE
|
|
81
|
+
it('ArrowLeft moves to previous tab (wraps around)', async () => {
|
|
82
|
+
container.innerHTML = `
|
|
83
|
+
<ds-tabs>
|
|
84
|
+
<ds-tab-item label="Tab 1" selected></ds-tab-item>
|
|
85
|
+
<ds-tab-item label="Tab 2"></ds-tab-item>
|
|
86
|
+
<ds-tab-item label="Tab 3"></ds-tab-item>
|
|
87
|
+
</ds-tabs>
|
|
88
|
+
`;
|
|
89
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
90
|
+
const tabs = container.querySelectorAll('ds-tab-item');
|
|
91
|
+
tabs[0].focus();
|
|
92
|
+
|
|
93
|
+
tabs[0].dispatchEvent(new KeyboardEvent('keydown', {
|
|
94
|
+
key: 'ArrowLeft',
|
|
95
|
+
bubbles: true,
|
|
96
|
+
composed: true
|
|
97
|
+
}));
|
|
98
|
+
|
|
99
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
100
|
+
|
|
101
|
+
// Should wrap to last tab
|
|
102
|
+
expect(tabs[2].selected).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('Home key selects first tab', async () => {
|
|
106
|
+
container.innerHTML = `
|
|
107
|
+
<ds-tabs>
|
|
108
|
+
<ds-tab-item label="Tab 1"></ds-tab-item>
|
|
109
|
+
<ds-tab-item label="Tab 2" selected></ds-tab-item>
|
|
110
|
+
<ds-tab-item label="Tab 3"></ds-tab-item>
|
|
111
|
+
</ds-tabs>
|
|
112
|
+
`;
|
|
113
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
114
|
+
const tabs = container.querySelectorAll('ds-tab-item');
|
|
115
|
+
tabs[1].focus();
|
|
116
|
+
|
|
117
|
+
tabs[1].dispatchEvent(new KeyboardEvent('keydown', {
|
|
118
|
+
key: 'Home',
|
|
119
|
+
bubbles: true,
|
|
120
|
+
composed: true
|
|
121
|
+
}));
|
|
122
|
+
|
|
123
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
124
|
+
|
|
125
|
+
expect(tabs[0].selected).toBe(true);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('End key selects last tab', async () => {
|
|
129
|
+
container.innerHTML = `
|
|
130
|
+
<ds-tabs>
|
|
131
|
+
<ds-tab-item label="Tab 1" selected></ds-tab-item>
|
|
132
|
+
<ds-tab-item label="Tab 2"></ds-tab-item>
|
|
133
|
+
<ds-tab-item label="Tab 3"></ds-tab-item>
|
|
134
|
+
</ds-tabs>
|
|
135
|
+
`;
|
|
136
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
137
|
+
const tabs = container.querySelectorAll('ds-tab-item');
|
|
138
|
+
tabs[0].focus();
|
|
139
|
+
|
|
140
|
+
tabs[0].dispatchEvent(new KeyboardEvent('keydown', {
|
|
141
|
+
key: 'End',
|
|
142
|
+
bubbles: true,
|
|
143
|
+
composed: true
|
|
144
|
+
}));
|
|
145
|
+
|
|
146
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
147
|
+
|
|
148
|
+
expect(tabs[2].selected).toBe(true);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('skips disabled tabs during keyboard navigation', async () => {
|
|
152
|
+
container.innerHTML = `
|
|
153
|
+
<ds-tabs>
|
|
154
|
+
<ds-tab-item label="Tab 1" selected></ds-tab-item>
|
|
155
|
+
<ds-tab-item label="Tab 2" disabled></ds-tab-item>
|
|
156
|
+
<ds-tab-item label="Tab 3"></ds-tab-item>
|
|
157
|
+
</ds-tabs>
|
|
158
|
+
`;
|
|
159
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
160
|
+
const tabs = container.querySelectorAll('ds-tab-item');
|
|
161
|
+
tabs[0].focus();
|
|
162
|
+
|
|
163
|
+
tabs[0].dispatchEvent(new KeyboardEvent('keydown', {
|
|
164
|
+
key: 'ArrowRight',
|
|
165
|
+
bubbles: true,
|
|
166
|
+
composed: true
|
|
167
|
+
}));
|
|
168
|
+
|
|
169
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
170
|
+
|
|
171
|
+
// Should skip disabled tab 2 and go to tab 3
|
|
172
|
+
expect(tabs[2].selected).toBe(true);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// VARIANT TESTS
|
|
176
|
+
it('propagates variant="line" to all tab items', async () => {
|
|
177
|
+
container.innerHTML = `
|
|
178
|
+
<ds-tabs variant="line">
|
|
179
|
+
<ds-tab-item label="Tab 1"></ds-tab-item>
|
|
180
|
+
<ds-tab-item label="Tab 2"></ds-tab-item>
|
|
181
|
+
</ds-tabs>
|
|
182
|
+
`;
|
|
183
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
184
|
+
|
|
185
|
+
const tabsEl = container.querySelector('ds-tabs');
|
|
186
|
+
const tabs = container.querySelectorAll('ds-tab-item');
|
|
187
|
+
|
|
188
|
+
expect(tabsEl.variant).toBe('line');
|
|
189
|
+
tabs.forEach(tab => {
|
|
190
|
+
expect(tab.variant).toBe('line');
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('propagates variant="contained" to all tab items', async () => {
|
|
195
|
+
container.innerHTML = `
|
|
196
|
+
<ds-tabs variant="contained">
|
|
197
|
+
<ds-tab-item label="Tab 1"></ds-tab-item>
|
|
198
|
+
<ds-tab-item label="Tab 2"></ds-tab-item>
|
|
199
|
+
</ds-tabs>
|
|
200
|
+
`;
|
|
201
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
202
|
+
|
|
203
|
+
const tabsEl = container.querySelector('ds-tabs');
|
|
204
|
+
const tabs = container.querySelectorAll('ds-tab-item');
|
|
205
|
+
|
|
206
|
+
expect(tabsEl.variant).toBe('contained');
|
|
207
|
+
tabs.forEach(tab => {
|
|
208
|
+
expect(tab.variant).toBe('contained');
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('updates variant when prop changes', async () => {
|
|
213
|
+
container.innerHTML = `
|
|
214
|
+
<ds-tabs variant="line">
|
|
215
|
+
<ds-tab-item label="Tab 1"></ds-tab-item>
|
|
216
|
+
<ds-tab-item label="Tab 2"></ds-tab-item>
|
|
217
|
+
</ds-tabs>
|
|
218
|
+
`;
|
|
219
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
220
|
+
|
|
221
|
+
const tabsEl = container.querySelector('ds-tabs');
|
|
222
|
+
tabsEl.variant = 'contained';
|
|
223
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
224
|
+
|
|
225
|
+
const tabs = container.querySelectorAll('ds-tab-item');
|
|
226
|
+
tabs.forEach(tab => {
|
|
227
|
+
expect(tab.variant).toBe('contained');
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// SELECTION TESTS
|
|
232
|
+
it('only one tab selected at a time', async () => {
|
|
233
|
+
container.innerHTML = `
|
|
234
|
+
<ds-tabs>
|
|
235
|
+
<ds-tab-item label="Tab 1" selected></ds-tab-item>
|
|
236
|
+
<ds-tab-item label="Tab 2" id="tab2"></ds-tab-item>
|
|
237
|
+
</ds-tabs>
|
|
238
|
+
`;
|
|
239
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
240
|
+
|
|
241
|
+
const tab2 = container.querySelector('#tab2');
|
|
242
|
+
tab2.click();
|
|
243
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
244
|
+
|
|
245
|
+
const tabs = container.querySelectorAll('ds-tab-item');
|
|
246
|
+
const selectedTabs = [...tabs].filter(t => t.selected);
|
|
247
|
+
expect(selectedTabs.length).toBe(1);
|
|
248
|
+
expect(selectedTabs[0]).toBe(tab2);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('activates tab on click', async () => {
|
|
252
|
+
container.innerHTML = `
|
|
253
|
+
<ds-tabs>
|
|
254
|
+
<ds-tab-item label="Tab 1" selected></ds-tab-item>
|
|
255
|
+
<ds-tab-item label="Tab 2" id="tab2"></ds-tab-item>
|
|
256
|
+
</ds-tabs>
|
|
257
|
+
`;
|
|
258
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
259
|
+
|
|
260
|
+
const tab2 = container.querySelector('#tab2');
|
|
261
|
+
expect(tab2.selected).toBe(false);
|
|
262
|
+
|
|
263
|
+
tab2.click();
|
|
264
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
265
|
+
|
|
266
|
+
expect(tab2.selected).toBe(true);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// EDGE CASES
|
|
270
|
+
it('handles empty slot (no tabs)', async () => {
|
|
271
|
+
container.innerHTML = `<ds-tabs></ds-tabs>`;
|
|
272
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
273
|
+
|
|
274
|
+
const tabsEl = container.querySelector('ds-tabs');
|
|
275
|
+
expect(tabsEl).toBeTruthy();
|
|
276
|
+
|
|
277
|
+
// No tabs, no errors
|
|
278
|
+
const tabs = container.querySelectorAll('ds-tab-item');
|
|
279
|
+
expect(tabs.length).toBe(0);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('handles all tabs disabled', async () => {
|
|
283
|
+
container.innerHTML = `
|
|
284
|
+
<ds-tabs>
|
|
285
|
+
<ds-tab-item label="Tab 1" disabled selected></ds-tab-item>
|
|
286
|
+
<ds-tab-item label="Tab 2" disabled></ds-tab-item>
|
|
287
|
+
</ds-tabs>
|
|
288
|
+
`;
|
|
289
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
290
|
+
|
|
291
|
+
const tabsEl = container.querySelector('ds-tabs');
|
|
292
|
+
const tabs = container.querySelectorAll('ds-tab-item');
|
|
293
|
+
|
|
294
|
+
// All disabled, keyboard nav should do nothing
|
|
295
|
+
tabs[0].dispatchEvent(new KeyboardEvent('keydown', {
|
|
296
|
+
key: 'ArrowRight',
|
|
297
|
+
bubbles: true,
|
|
298
|
+
composed: true
|
|
299
|
+
}));
|
|
300
|
+
|
|
301
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
302
|
+
|
|
303
|
+
// Nothing should change
|
|
304
|
+
expect(tabs[0].selected).toBe(true);
|
|
305
|
+
});
|
|
306
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import './ds-tag-action.js';
|
|
3
|
+
|
|
4
|
+
describe('DsTagAction A11y', () => {
|
|
5
|
+
let container;
|
|
6
|
+
beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); });
|
|
7
|
+
afterEach(() => { container.remove(); });
|
|
8
|
+
|
|
9
|
+
it('renders internal button', async () => {
|
|
10
|
+
container.innerHTML = '<ds-tag-action label="Action"></ds-tag-action>';
|
|
11
|
+
await new Promise(r => setTimeout(r, 0));
|
|
12
|
+
const el = container.querySelector('ds-tag-action');
|
|
13
|
+
const btn = el.shadowRoot.querySelector('button');
|
|
14
|
+
expect(btn).to.exist;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('sets aria-pressed when selected', async () => {
|
|
18
|
+
container.innerHTML = '<ds-tag-action label="Selected" selected></ds-tag-action>';
|
|
19
|
+
await new Promise(r => setTimeout(r, 0));
|
|
20
|
+
const el = container.querySelector('ds-tag-action');
|
|
21
|
+
const btn = el.shadowRoot.querySelector('button');
|
|
22
|
+
expect(btn.getAttribute('aria-pressed')).toBe('true');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('sets disabled attribute on button', async () => {
|
|
26
|
+
container.innerHTML = '<ds-tag-action label="Disabled" disabled></ds-tag-action>';
|
|
27
|
+
await new Promise(r => setTimeout(r, 0));
|
|
28
|
+
const el = container.querySelector('ds-tag-action');
|
|
29
|
+
const btn = el.shadowRoot.querySelector('button');
|
|
30
|
+
expect(btn.hasAttribute('disabled')).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { LitElement, html, css } from 'lit';
|
|
2
|
+
import '../ds-icon/ds-icon.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @element ds-tag-action
|
|
6
|
+
* @summary A rounded, interactive tag (chip) that can be selected.
|
|
7
|
+
*
|
|
8
|
+
* @prop {string} label - Text label.
|
|
9
|
+
* @prop {string} icon - Optional icon name (left).
|
|
10
|
+
* @prop {boolean} selected - Whether the tag is selected.
|
|
11
|
+
* @prop {boolean} disabled - Whether the tag is disabled.
|
|
12
|
+
* @prop {string} size - 's' (24px) | 'm' (32px). Default: 'm'.
|
|
13
|
+
* @prop {string} color - 'neutral' | 'info'. Default: 'neutral'.
|
|
14
|
+
*/
|
|
15
|
+
export class DsTagAction extends LitElement {
|
|
16
|
+
static properties = {
|
|
17
|
+
label: { type: String },
|
|
18
|
+
icon: { type: String },
|
|
19
|
+
selected: { type: Boolean, reflect: true },
|
|
20
|
+
disabled: { type: Boolean, reflect: true },
|
|
21
|
+
size: { type: String, reflect: true },
|
|
22
|
+
color: { type: String, reflect: true },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
static styles = css`
|
|
26
|
+
:host {
|
|
27
|
+
display: inline-flex; /* Fix ghost space from inline-block */
|
|
28
|
+
vertical-align: middle;
|
|
29
|
+
outline: none;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
button {
|
|
33
|
+
appearance: none;
|
|
34
|
+
display: inline-flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
justify-content: center;
|
|
37
|
+
box-sizing: border-box;
|
|
38
|
+
|
|
39
|
+
border-radius: var(--ds-radius-action, 999px);
|
|
40
|
+
border: 1px solid transparent; /* Reserve space for border to avoid shift */
|
|
41
|
+
cursor: pointer;
|
|
42
|
+
position: relative;
|
|
43
|
+
|
|
44
|
+
font: var(--ds-typo-content-body-regular); /* Correct token */
|
|
45
|
+
white-space: nowrap;
|
|
46
|
+
|
|
47
|
+
transition: background-color 0.2s, border-color 0.2s, color 0.2s;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* Gap Logic */
|
|
51
|
+
button {
|
|
52
|
+
gap: 4px; /* "gap de 4px entre eles" */
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
* SIZES
|
|
57
|
+
*/
|
|
58
|
+
/*
|
|
59
|
+
* SIZES
|
|
60
|
+
*/
|
|
61
|
+
/* Size M (default): 32px height */
|
|
62
|
+
:host([size="m"]) button,
|
|
63
|
+
:host(:not([size])) button {
|
|
64
|
+
height: 32px;
|
|
65
|
+
padding: 0 var(--ds-space-sm);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* Size S: 24px height */
|
|
69
|
+
:host([size="s"]) button {
|
|
70
|
+
height: 24px;
|
|
71
|
+
padding: 0 var(--ds-space-sm);
|
|
72
|
+
box-sizing: border-box;
|
|
73
|
+
line-height: 1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/*
|
|
77
|
+
* COLORS / VARIANTS
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/* NEUTRAL (Default) */
|
|
81
|
+
:host([color="neutral"]) button,
|
|
82
|
+
:host(:not([color])) button {
|
|
83
|
+
background-color: var(--ds-color-bg-neutral-subtle);
|
|
84
|
+
color: var(--ds-color-text-default);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Neutral Hover */
|
|
88
|
+
:host([color="neutral"]:not([disabled])) button:hover,
|
|
89
|
+
:host(:not([color]):not([disabled])) button:hover {
|
|
90
|
+
background-color: var(--ds-color-bg-hover);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* Neutral Selected */
|
|
94
|
+
:host([color="neutral"][selected]) button,
|
|
95
|
+
:host(:not([color])[selected]) button {
|
|
96
|
+
border-color: var(--ds-color-border-selected-secondary, #949494);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* INFO */
|
|
100
|
+
:host([color="info"]) button {
|
|
101
|
+
background-color: var(--ds-color-bg-brand-subtle);
|
|
102
|
+
color: var(--ds-color-text-default);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Info Hover */
|
|
106
|
+
:host([color="info"]:not([disabled])) button:hover {
|
|
107
|
+
background-color: var(--ds-color-bg-brand-subtle-hover);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* Info Selected */
|
|
111
|
+
:host([color="info"][selected]) button {
|
|
112
|
+
border-color: var(--ds-color-border-selected);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/*
|
|
116
|
+
* DISABLED
|
|
117
|
+
*/
|
|
118
|
+
:host([disabled]) button {
|
|
119
|
+
cursor: not-allowed;
|
|
120
|
+
background-color: var(--ds-color-bg-disabled);
|
|
121
|
+
color: var(--ds-color-text-disabled);
|
|
122
|
+
border-color: transparent;
|
|
123
|
+
opacity: 1;
|
|
124
|
+
pointer-events: none;
|
|
125
|
+
}
|
|
126
|
+
:host([disabled][selected]) button {
|
|
127
|
+
border-color: var(--ds-color-border-disabled);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
:host([disabled]) {
|
|
131
|
+
pointer-events: none;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* FOCUS */
|
|
135
|
+
button:focus-visible {
|
|
136
|
+
outline: 2px solid var(--ds-color-border-focus);
|
|
137
|
+
outline-offset: 2px;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* ICON styling */
|
|
141
|
+
ds-icon {
|
|
142
|
+
/* User requested size S consistently */
|
|
143
|
+
--size: var(--ds-icon-size-sm, 20px);
|
|
144
|
+
color: currentColor;
|
|
145
|
+
}
|
|
146
|
+
`;
|
|
147
|
+
|
|
148
|
+
constructor() {
|
|
149
|
+
super();
|
|
150
|
+
this.selected = false;
|
|
151
|
+
this.disabled = false;
|
|
152
|
+
this.size = 'm';
|
|
153
|
+
this.color = 'neutral';
|
|
154
|
+
this.label = '';
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
_handleClick() {
|
|
158
|
+
if (this.disabled) return;
|
|
159
|
+
this.selected = !this.selected;
|
|
160
|
+
this.dispatchEvent(new CustomEvent('ds-selected-change', {
|
|
161
|
+
detail: { selected: this.selected },
|
|
162
|
+
bubbles: true,
|
|
163
|
+
composed: true
|
|
164
|
+
}));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
render() {
|
|
168
|
+
return html`
|
|
169
|
+
<button
|
|
170
|
+
type="button"
|
|
171
|
+
part="container"
|
|
172
|
+
?disabled=${this.disabled}
|
|
173
|
+
aria-pressed=${this.selected}
|
|
174
|
+
@click=${this._handleClick}
|
|
175
|
+
>
|
|
176
|
+
${this.icon ? html`<ds-icon name=${this.icon} size="sm" part="icon"></ds-icon>` : ''}
|
|
177
|
+
<span>${this.label}</span>
|
|
178
|
+
</button>
|
|
179
|
+
`;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (!customElements.get('ds-tag-action')) {
|
|
184
|
+
customElements.define('ds-tag-action', DsTagAction);
|
|
185
|
+
}
|