@dialpad/dialtone-css 8.80.0-next.1 → 8.80.0-next.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/lib/build/js/dialtone_migrate_link_rendering/button-nav-test-examples.vue +92 -0
  2. package/lib/build/js/dialtone_migrate_link_rendering/button-nav.test.mjs +272 -0
  3. package/lib/build/js/dialtone_migrate_link_rendering/helpers.mjs +25 -0
  4. package/lib/build/js/dialtone_migrate_link_rendering/index.mjs +1041 -0
  5. package/lib/build/js/dialtone_migrate_link_rendering/link-nav-test-examples.vue +97 -0
  6. package/lib/build/js/dialtone_migrate_link_rendering/link-nav.test.mjs +194 -0
  7. package/lib/build/js/dialtone_migrate_link_rendering/underline-test-examples.vue +57 -0
  8. package/lib/build/js/dialtone_migrate_link_rendering/underline.test.mjs +161 -0
  9. package/lib/build/js/dialtone_migrate_props/index.mjs +794 -0
  10. package/lib/build/js/dialtone_migrate_props/test.mjs +959 -0
  11. package/lib/build/js/dialtone_migration_helper/configs/base-to-semantic.mjs +8 -8
  12. package/lib/build/js/dialtone_migration_helper/configs/size-to-layout.mjs +10 -0
  13. package/lib/build/js/dialtone_migration_helper/configs/success-to-positive.mjs +73 -0
  14. package/lib/build/js/dialtone_migration_helper/configs/utility-class-to-token-stops.mjs +92 -12
  15. package/lib/build/js/dialtone_migration_helper/tests/base-to-semantic-test-examples.vue +10 -10
  16. package/lib/build/js/dialtone_migration_helper/tests/base-to-semantic.test.mjs +8 -8
  17. package/lib/build/js/dialtone_migration_helper/tests/size-to-layout-test-examples.vue +16 -0
  18. package/lib/build/js/dialtone_migration_helper/tests/size-to-layout.test.mjs +87 -0
  19. package/lib/build/js/dialtone_migration_helper/tests/success-to-positive-test-examples.vue +166 -0
  20. package/lib/build/js/dialtone_migration_helper/tests/success-to-positive.test.mjs +287 -0
  21. package/lib/build/js/dialtone_migration_helper/tests/utility-class-to-token-stops-radius-examples.vue +66 -0
  22. package/lib/build/js/dialtone_migration_helper/tests/utility-class-to-token-stops.test.mjs +170 -0
  23. package/lib/build/less/components/badge.less +1 -1
  24. package/lib/build/less/components/banner.less +1 -1
  25. package/lib/build/less/components/box.less +228 -0
  26. package/lib/build/less/components/description-list.less +4 -0
  27. package/lib/build/less/components/forms.less +4 -2
  28. package/lib/build/less/components/input.less +2 -2
  29. package/lib/build/less/components/link.less +18 -4
  30. package/lib/build/less/components/modal.less +1 -1
  31. package/lib/build/less/components/notice.less +3 -3
  32. package/lib/build/less/components/progress_circle.less +10 -2
  33. package/lib/build/less/components/prose.less +512 -0
  34. package/lib/build/less/components/rich-text-editor.less +7 -0
  35. package/lib/build/less/components/selects.less +1 -1
  36. package/lib/build/less/components/text.less +2 -2
  37. package/lib/build/less/components/toast.less +1 -1
  38. package/lib/build/less/dialtone.less +2 -0
  39. package/lib/build/less/recipes/leftbar_row.less +1 -0
  40. package/lib/build/less/recipes/settings_menu_button.less +1 -1
  41. package/lib/build/less/recipes/top_banner_info.less +1 -1
  42. package/lib/build/less/utilities/backgrounds.less +12 -0
  43. package/lib/build/less/utilities/borders.less +56 -89
  44. package/lib/build/less/utilities/colors.less +8 -0
  45. package/lib/build/less/utilities/effects.less +1 -0
  46. package/lib/build/less/utilities/flex.less +145 -18
  47. package/lib/build/less/utilities/grid.less +40 -152
  48. package/lib/build/less/utilities/layout.less +19 -7
  49. package/lib/build/less/utilities/sizing.less +148 -143
  50. package/lib/build/less/variables/visual-styles.less +2 -1
  51. package/lib/dist/dialtone-default-theme.css +4364 -1756
  52. package/lib/dist/dialtone-default-theme.min.css +1 -1
  53. package/lib/dist/dialtone-docs.json +1 -1
  54. package/lib/dist/dialtone.css +4271 -1705
  55. package/lib/dist/dialtone.min.css +1 -1
  56. package/lib/dist/js/dialtone_migrate_link_rendering/button-nav-test-examples.vue +92 -0
  57. package/lib/dist/js/dialtone_migrate_link_rendering/button-nav.test.mjs +272 -0
  58. package/lib/dist/js/dialtone_migrate_link_rendering/helpers.mjs +25 -0
  59. package/lib/dist/js/dialtone_migrate_link_rendering/index.mjs +1041 -0
  60. package/lib/dist/js/dialtone_migrate_link_rendering/link-nav-test-examples.vue +97 -0
  61. package/lib/dist/js/dialtone_migrate_link_rendering/link-nav.test.mjs +194 -0
  62. package/lib/dist/js/dialtone_migrate_link_rendering/underline-test-examples.vue +57 -0
  63. package/lib/dist/js/dialtone_migrate_link_rendering/underline.test.mjs +161 -0
  64. package/lib/dist/js/dialtone_migrate_props/index.mjs +794 -0
  65. package/lib/dist/js/dialtone_migrate_props/test.mjs +959 -0
  66. package/lib/dist/js/dialtone_migration_helper/configs/base-to-semantic.mjs +8 -8
  67. package/lib/dist/js/dialtone_migration_helper/configs/size-to-layout.mjs +10 -0
  68. package/lib/dist/js/dialtone_migration_helper/configs/success-to-positive.mjs +73 -0
  69. package/lib/dist/js/dialtone_migration_helper/configs/utility-class-to-token-stops.mjs +92 -12
  70. package/lib/dist/js/dialtone_migration_helper/tests/base-to-semantic-test-examples.vue +10 -10
  71. package/lib/dist/js/dialtone_migration_helper/tests/base-to-semantic.test.mjs +8 -8
  72. package/lib/dist/js/dialtone_migration_helper/tests/size-to-layout-test-examples.vue +16 -0
  73. package/lib/dist/js/dialtone_migration_helper/tests/size-to-layout.test.mjs +87 -0
  74. package/lib/dist/js/dialtone_migration_helper/tests/success-to-positive-test-examples.vue +166 -0
  75. package/lib/dist/js/dialtone_migration_helper/tests/success-to-positive.test.mjs +287 -0
  76. package/lib/dist/js/dialtone_migration_helper/tests/utility-class-to-token-stops-radius-examples.vue +66 -0
  77. package/lib/dist/js/dialtone_migration_helper/tests/utility-class-to-token-stops.test.mjs +170 -0
  78. package/lib/dist/tokens/tokens-101-dark.css +81 -45
  79. package/lib/dist/tokens/tokens-101-light.css +75 -39
  80. package/lib/dist/tokens/tokens-102-dark.css +81 -45
  81. package/lib/dist/tokens/tokens-102-light.css +75 -39
  82. package/lib/dist/tokens/tokens-103-dark.css +81 -45
  83. package/lib/dist/tokens/tokens-103-light.css +75 -39
  84. package/lib/dist/tokens/tokens-104-dark.css +81 -45
  85. package/lib/dist/tokens/tokens-104-light.css +75 -39
  86. package/lib/dist/tokens/tokens-105-dark.css +81 -45
  87. package/lib/dist/tokens/tokens-105-light.css +75 -39
  88. package/lib/dist/tokens/tokens-106-dark.css +81 -45
  89. package/lib/dist/tokens/tokens-106-light.css +75 -39
  90. package/lib/dist/tokens/tokens-107-dark.css +81 -45
  91. package/lib/dist/tokens/tokens-107-light.css +75 -39
  92. package/lib/dist/tokens/tokens-108-dark.css +81 -45
  93. package/lib/dist/tokens/tokens-108-light.css +75 -39
  94. package/lib/dist/tokens/tokens-109-dark.css +81 -45
  95. package/lib/dist/tokens/tokens-109-light.css +75 -39
  96. package/lib/dist/tokens/tokens-110-dark.css +81 -45
  97. package/lib/dist/tokens/tokens-110-light.css +75 -39
  98. package/lib/dist/tokens/tokens-111-dark.css +81 -45
  99. package/lib/dist/tokens/tokens-111-light.css +75 -39
  100. package/lib/dist/tokens/tokens-112-dark.css +81 -45
  101. package/lib/dist/tokens/tokens-112-light.css +75 -39
  102. package/lib/dist/tokens/tokens-113-dark.css +81 -45
  103. package/lib/dist/tokens/tokens-113-light.css +75 -39
  104. package/lib/dist/tokens/tokens-114-dark.css +81 -45
  105. package/lib/dist/tokens/tokens-114-light.css +75 -39
  106. package/lib/dist/tokens/tokens-115-dark.css +81 -45
  107. package/lib/dist/tokens/tokens-115-light.css +75 -39
  108. package/lib/dist/tokens/tokens-116-dark.css +81 -45
  109. package/lib/dist/tokens/tokens-116-light.css +75 -39
  110. package/lib/dist/tokens/tokens-117-dark.css +81 -45
  111. package/lib/dist/tokens/tokens-117-light.css +75 -39
  112. package/lib/dist/tokens/tokens-118-dark.css +81 -45
  113. package/lib/dist/tokens/tokens-118-light.css +75 -39
  114. package/lib/dist/tokens/tokens-119-dark.css +81 -45
  115. package/lib/dist/tokens/tokens-119-light.css +75 -39
  116. package/lib/dist/tokens/tokens-120-dark.css +81 -45
  117. package/lib/dist/tokens/tokens-120-light.css +75 -39
  118. package/lib/dist/tokens/tokens-121-dark.css +81 -45
  119. package/lib/dist/tokens/tokens-121-light.css +75 -39
  120. package/lib/dist/tokens/tokens-122-dark.css +81 -45
  121. package/lib/dist/tokens/tokens-122-light.css +75 -39
  122. package/lib/dist/tokens/tokens-123-dark.css +81 -45
  123. package/lib/dist/tokens/tokens-123-light.css +75 -39
  124. package/lib/dist/tokens/tokens-124-dark.css +81 -45
  125. package/lib/dist/tokens/tokens-124-light.css +75 -39
  126. package/lib/dist/tokens/tokens-125-dark.css +81 -45
  127. package/lib/dist/tokens/tokens-125-light.css +75 -39
  128. package/lib/dist/tokens/tokens-126-dark.css +81 -45
  129. package/lib/dist/tokens/tokens-126-light.css +75 -39
  130. package/lib/dist/tokens/tokens-127-dark.css +81 -45
  131. package/lib/dist/tokens/tokens-127-light.css +75 -39
  132. package/lib/dist/tokens/tokens-128-dark.css +81 -45
  133. package/lib/dist/tokens/tokens-128-light.css +75 -39
  134. package/lib/dist/tokens/tokens-129-dark.css +81 -45
  135. package/lib/dist/tokens/tokens-129-light.css +75 -39
  136. package/lib/dist/tokens/tokens-130-dark.css +81 -45
  137. package/lib/dist/tokens/tokens-130-light.css +75 -39
  138. package/lib/dist/tokens/tokens-131-dark.css +81 -45
  139. package/lib/dist/tokens/tokens-131-light.css +75 -39
  140. package/lib/dist/tokens/tokens-132-dark.css +81 -45
  141. package/lib/dist/tokens/tokens-132-light.css +75 -39
  142. package/lib/dist/tokens/tokens-133-dark.css +81 -45
  143. package/lib/dist/tokens/tokens-133-light.css +75 -39
  144. package/lib/dist/tokens/tokens-134-dark.css +81 -45
  145. package/lib/dist/tokens/tokens-134-light.css +75 -39
  146. package/lib/dist/tokens/tokens-135-dark.css +81 -45
  147. package/lib/dist/tokens/tokens-135-light.css +75 -39
  148. package/lib/dist/tokens/tokens-136-dark.css +81 -45
  149. package/lib/dist/tokens/tokens-136-light.css +75 -39
  150. package/lib/dist/tokens/tokens-137-dark.css +81 -45
  151. package/lib/dist/tokens/tokens-137-light.css +75 -39
  152. package/lib/dist/tokens/tokens-aegean-dark.css +81 -45
  153. package/lib/dist/tokens/tokens-aegean-light.css +75 -39
  154. package/lib/dist/tokens/tokens-base-dark.css +18 -12
  155. package/lib/dist/tokens/tokens-base-light.css +18 -12
  156. package/lib/dist/tokens/tokens-botany-dark.css +81 -45
  157. package/lib/dist/tokens/tokens-botany-light.css +75 -39
  158. package/lib/dist/tokens/tokens-buttercream-dark.css +81 -45
  159. package/lib/dist/tokens/tokens-buttercream-light.css +75 -39
  160. package/lib/dist/tokens/tokens-ceruleo-dark.css +81 -45
  161. package/lib/dist/tokens/tokens-ceruleo-light.css +75 -39
  162. package/lib/dist/tokens/tokens-debug-base.css +6 -0
  163. package/lib/dist/tokens/tokens-debug-dp.css +39 -3
  164. package/lib/dist/tokens/tokens-dp-dark.css +81 -45
  165. package/lib/dist/tokens/tokens-dp-light.css +75 -39
  166. package/lib/dist/tokens/tokens-expressive-dark.css +81 -45
  167. package/lib/dist/tokens/tokens-expressive-light.css +75 -39
  168. package/lib/dist/tokens/tokens-expressive-sm-dark.css +81 -45
  169. package/lib/dist/tokens/tokens-expressive-sm-light.css +75 -39
  170. package/lib/dist/tokens/tokens-high-desert-dark.css +81 -45
  171. package/lib/dist/tokens/tokens-high-desert-light.css +75 -39
  172. package/lib/dist/tokens/tokens-melon-dark.css +81 -45
  173. package/lib/dist/tokens/tokens-melon-light.css +75 -39
  174. package/lib/dist/tokens/tokens-plum-dark.css +81 -45
  175. package/lib/dist/tokens/tokens-plum-light.css +75 -39
  176. package/lib/dist/tokens/tokens-prota-deuter-dark.css +79 -43
  177. package/lib/dist/tokens/tokens-prota-deuter-light.css +74 -38
  178. package/lib/dist/tokens/tokens-sunflower-dark.css +81 -45
  179. package/lib/dist/tokens/tokens-sunflower-light.css +75 -39
  180. package/lib/dist/tokens/tokens-tmo-dark.css +81 -45
  181. package/lib/dist/tokens/tokens-tmo-light.css +75 -39
  182. package/lib/dist/tokens/tokens-trita-dark.css +81 -45
  183. package/lib/dist/tokens/tokens-trita-light.css +75 -39
  184. package/lib/dist/tokens/tokens-verdant-haze-dark.css +81 -45
  185. package/lib/dist/tokens/tokens-verdant-haze-light.css +75 -39
  186. package/lib/dist/tokens-docs.json +1 -1
  187. package/package.json +4 -4
@@ -0,0 +1,97 @@
1
+ <template>
2
+ <!--
3
+ TEST FILE FOR link-nav MIGRATION (DLT-3034)
4
+
5
+ Each example shows a legacy DtLink pattern the codemod recognizes and the expected
6
+ rewritten form. The fixture doubles as a `npx dialtone-migrate-link-rendering
7
+ --dry-run --cwd <this-folder>` demo target.
8
+ -->
9
+
10
+ <!-- ============================================ -->
11
+ <!-- 1. <a class="d-link"> → <dt-link href> -->
12
+ <!-- ============================================ -->
13
+
14
+ <a class="d-link" href="/profile">Profile</a>
15
+
16
+ <a class="d-link" href="https://example.com" target="_blank" rel="noopener">
17
+ External link
18
+ </a>
19
+
20
+ <!-- ============================================ -->
21
+ <!-- 2. <router-link class="d-link"> → <dt-link :to> -->
22
+ <!-- ============================================ -->
23
+
24
+ <router-link class="d-link" to="/dashboard">Dashboard</router-link>
25
+
26
+ <router-link class="d-link" :to="{ name: 'profile' }">Profile</router-link>
27
+
28
+ <!-- ============================================ -->
29
+ <!-- 3. Tone modifier extraction (with renames) -->
30
+ <!-- ============================================ -->
31
+
32
+ <a class="d-link d-link--critical" href="/danger">Critical action</a>
33
+
34
+ <!-- d-link--danger renames to tone="critical" -->
35
+ <a class="d-link d-link--danger" href="/danger">Danger (legacy name)</a>
36
+
37
+ <a class="d-link d-link--positive" href="/win">Positive</a>
38
+
39
+ <!-- d-link--success renames to tone="positive" -->
40
+ <a class="d-link d-link--success" href="/win">Success (legacy name)</a>
41
+
42
+ <a class="d-link d-link--warning" href="/careful">Warning</a>
43
+
44
+ <a class="d-link d-link--info" href="/info">Info</a>
45
+
46
+ <a class="d-link d-link--muted" href="/secondary">Muted</a>
47
+
48
+ <a class="d-link d-link--mention" href="/user/brad">@brad</a>
49
+
50
+ <!-- ============================================ -->
51
+ <!-- 4. d-link--no-underline → :underline="false" -->
52
+ <!-- ============================================ -->
53
+
54
+ <a class="d-link d-link--no-underline" href="/x">No underline at rest</a>
55
+
56
+ <!-- ============================================ -->
57
+ <!-- 5. Inverted modifiers — extracted + per-file note nudges to v-dt-mode -->
58
+ <!-- ============================================ -->
59
+
60
+ <a class="d-link d-link--inverted" href="/x">Inverted (note)</a>
61
+
62
+ <a class="d-link d-link--inverted-critical" href="/x">Inverted critical (note + tone)</a>
63
+
64
+ <!-- ============================================ -->
65
+ <!-- 6. CSS-only modifiers preserved on class -->
66
+ <!-- ============================================ -->
67
+
68
+ <!-- d-link--disabled has no prop equivalent — preserve on class -->
69
+ <a class="d-link d-link--disabled" href="/x">Disabled link</a>
70
+
71
+ <!-- d-link--inverted-disabled — preserve on class -->
72
+ <a class="d-link d-link--inverted-disabled" href="/x">Inverted + disabled</a>
73
+
74
+ <!-- Custom user class preserved -->
75
+ <a class="d-link my-typography-class" href="/x">Custom class</a>
76
+
77
+ <!-- ============================================ -->
78
+ <!-- 7. Patterns that warn (not transformed) -->
79
+ <!-- ============================================ -->
80
+
81
+ <!-- Dynamic :href — warn, skip -->
82
+ <a class="d-link" :href="dynamicUrl">Dynamic</a>
83
+
84
+ <!-- Dynamic :class alongside static — warn, skip -->
85
+ <a class="d-link" :class="extraClasses" href="/x">Mixed</a>
86
+
87
+ <!-- <router-link custom> wrapping <dt-link> — warn, manual review -->
88
+ <router-link
89
+ custom
90
+ v-slot="{ navigate, isActive }"
91
+ :to="{ name: 'profile' }"
92
+ >
93
+ <dt-link :class="{ 'is-active': isActive }" @click="navigate">
94
+ Profile (custom)
95
+ </dt-link>
96
+ </router-link>
97
+ </template>
@@ -0,0 +1,194 @@
1
+ /**
2
+ * DLT-3034 — link-nav transform tests.
3
+ *
4
+ * One assertion per test; data-driven via for..of where multiple cases share a concept.
5
+ */
6
+
7
+ import { describe, it } from 'node:test';
8
+ import assert from 'node:assert/strict';
9
+ import { runTransform, runTransformVerbose } from './helpers.mjs';
10
+
11
+ describe('link-nav: <a class="d-link"> → <dt-link href>', () => {
12
+ const cases = [
13
+ [
14
+ 'plain anchor',
15
+ '<a class="d-link" href="/x">Home</a>',
16
+ '<dt-link href="/x">Home</dt-link>',
17
+ ],
18
+ [
19
+ 'anchor with target/rel passes through as fallthrough attrs',
20
+ '<a class="d-link" href="https://example.com" target="_blank" rel="noopener">Ext</a>',
21
+ '<dt-link target="_blank" rel="noopener" href="https://example.com">Ext</dt-link>',
22
+ ],
23
+ [
24
+ 'arbitrary user class preserved',
25
+ '<a class="d-link my-typography-class" href="/x">Go</a>',
26
+ '<dt-link href="/x" class="my-typography-class">Go</dt-link>',
27
+ ],
28
+ ];
29
+
30
+ for (const [label, input, expected] of cases) {
31
+ it(label, () => {
32
+ assert.equal(runTransform(input), expected);
33
+ });
34
+ }
35
+ });
36
+
37
+ describe('link-nav: <router-link class="d-link"> → <dt-link :to>', () => {
38
+ const cases = [
39
+ [
40
+ 'router-link with static to',
41
+ '<router-link class="d-link" to="/x">Home</router-link>',
42
+ '<dt-link to="/x">Home</dt-link>',
43
+ ],
44
+ [
45
+ 'router-link with bound :to',
46
+ '<router-link class="d-link" :to="route">Home</router-link>',
47
+ '<dt-link :to="route">Home</dt-link>',
48
+ ],
49
+ ];
50
+
51
+ for (const [label, input, expected] of cases) {
52
+ it(label, () => {
53
+ assert.equal(runTransform(input), expected);
54
+ });
55
+ }
56
+ });
57
+
58
+ describe('link-nav: tone modifier extraction', () => {
59
+ // Per Q7 mapping table — including the danger→critical and success→positive renames
60
+ const cases = [
61
+ ['d-link--critical', 'tone="critical"'],
62
+ ['d-link--danger', 'tone="critical"'], // rename
63
+ ['d-link--warning', 'tone="warning"'],
64
+ ['d-link--positive', 'tone="positive"'],
65
+ ['d-link--success', 'tone="positive"'], // rename
66
+ ['d-link--info', 'tone="info"'],
67
+ ['d-link--muted', 'tone="muted"'],
68
+ ['d-link--mention', 'tone="mention"'],
69
+ ];
70
+
71
+ for (const [modifier, expectedAttr] of cases) {
72
+ it(`${modifier} → ${expectedAttr}`, () => {
73
+ const input = `<a class="d-link ${modifier}" href="/x">Go</a>`;
74
+ const expected = `<dt-link href="/x" ${expectedAttr}>Go</dt-link>`;
75
+ assert.equal(runTransform(input), expected);
76
+ });
77
+ }
78
+ });
79
+
80
+ describe('link-nav: no-underline modifier', () => {
81
+ it('d-link--no-underline → :underline="false"', () => {
82
+ assert.equal(
83
+ runTransform('<a class="d-link d-link--no-underline" href="/x">Go</a>'),
84
+ '<dt-link href="/x" :underline="false">Go</dt-link>',
85
+ );
86
+ });
87
+ });
88
+
89
+ describe('link-nav: inverted modifier emits per-file note', () => {
90
+ it('d-link--inverted is stripped and emits a note', () => {
91
+ const { transformed, notes } = runTransformVerbose(
92
+ '<a class="d-link d-link--inverted" href="/x">Go</a>',
93
+ { filePath: 'fixture.vue' },
94
+ );
95
+ assert.equal(transformed, '<dt-link href="/x">Go</dt-link>');
96
+ assert.equal(notes.length, 1);
97
+ });
98
+
99
+ it('d-link--inverted-critical extracts the tone and emits a note', () => {
100
+ const { transformed, notes } = runTransformVerbose(
101
+ '<a class="d-link d-link--inverted-critical" href="/x">Go</a>',
102
+ { filePath: 'fixture.vue' },
103
+ );
104
+ assert.equal(transformed, '<dt-link href="/x" tone="critical">Go</dt-link>');
105
+ assert.equal(notes.length, 1);
106
+ assert.match(notes[0].message, /v-dt-mode/);
107
+ });
108
+ });
109
+
110
+ describe('link-nav: CSS-only modifiers preserved on class', () => {
111
+ it('d-link--disabled preserved (no prop equivalent)', () => {
112
+ assert.equal(
113
+ runTransform('<a class="d-link d-link--disabled" href="/x">Go</a>'),
114
+ '<dt-link href="/x" class="d-link--disabled">Go</dt-link>',
115
+ );
116
+ });
117
+
118
+ it('d-link--inverted-disabled preserved (no prop equivalent)', () => {
119
+ assert.equal(
120
+ runTransform('<a class="d-link d-link--inverted-disabled" href="/x">Go</a>'),
121
+ '<dt-link href="/x" class="d-link--inverted-disabled">Go</dt-link>',
122
+ );
123
+ });
124
+ });
125
+
126
+ describe('link-nav: warning paths', () => {
127
+ it('dynamic :href on <a class="d-link"> is lifted to :href on the output', () => {
128
+ assert.equal(
129
+ runTransform('<a class="d-link" :href="url">Go</a>'),
130
+ '<dt-link :href="url">Go</dt-link>',
131
+ );
132
+ });
133
+
134
+ it('dynamic :class alongside static class warns', () => {
135
+ const { transformed, warnings } = runTransformVerbose(
136
+ '<a class="d-link" :class="extraClass" href="/x">Go</a>',
137
+ { filePath: 'fixture.vue' },
138
+ );
139
+ assert.equal(transformed, '<a class="d-link" :class="extraClass" href="/x">Go</a>');
140
+ assert.equal(warnings.length, 1);
141
+ assert.match(warnings[0], /dynamic :class/);
142
+ });
143
+
144
+ it('<router-link custom> wrapping <dt-link> warns', () => {
145
+ const input = '<router-link custom v-slot="{ navigate }" to="/x"><dt-link @click="navigate">Go</dt-link></router-link>';
146
+ const { warnings } = runTransformVerbose(input, { filePath: 'fixture.vue' });
147
+ assert.ok(warnings.some(w => /router-link custom.*dt-link/.test(w)));
148
+ });
149
+
150
+ it('<router-link custom class="d-link"> on the source tag itself warns and skips', () => {
151
+ const input = '<router-link custom v-slot="{ navigate }" to="/x" class="d-link">Go</router-link>';
152
+ const { transformed, warnings } = runTransformVerbose(input, { filePath: 'fixture.vue' });
153
+ assert.equal(transformed, input);
154
+ assert.ok(warnings.some(w => /router-link custom.*don't transfer/.test(w)));
155
+ });
156
+ });
157
+
158
+ describe('link-nav: PascalCase tags accepted, kebab-case emitted', () => {
159
+ it('<RouterLink class="d-link" :to="..."> → <dt-link :to="...">', () => {
160
+ assert.equal(
161
+ runTransform('<RouterLink class="d-link" :to="route">Home</RouterLink>'),
162
+ '<dt-link :to="route">Home</dt-link>',
163
+ );
164
+ });
165
+
166
+ it('<a class="d-link d-link--muted" v-show="x > 0"> — `>` inside binding preserved with tone extraction', () => {
167
+ assert.equal(
168
+ runTransform('<a class="d-link d-link--muted" v-show="x > 0" href="/x">Help</a>'),
169
+ '<dt-link v-show="x > 0" href="/x" tone="muted">Help</dt-link>',
170
+ );
171
+ });
172
+ });
173
+
174
+ describe('link-nav: idempotency', () => {
175
+ it('already-migrated <dt-link href> is a no-op', () => {
176
+ const input = '<dt-link href="/x" tone="muted">Go</dt-link>';
177
+ assert.equal(runTransform(input), input);
178
+ });
179
+
180
+ it('running the transform twice produces no further changes', () => {
181
+ const input = '<a class="d-link d-link--muted" href="/x">Go</a>';
182
+ const once = runTransform(input);
183
+ const twice = runTransform(once);
184
+ assert.equal(once, twice);
185
+ });
186
+ });
187
+
188
+ describe('link-nav: --only=link-nav runs only this transform', () => {
189
+ it('skips button-nav rewrites', () => {
190
+ const input = '<a class="d-btn" href="/x">Go</a>';
191
+ const { transformed } = runTransformVerbose(input, { only: ['link-nav'] });
192
+ assert.equal(transformed, input);
193
+ });
194
+ });
@@ -0,0 +1,57 @@
1
+ <template>
2
+ <!--
3
+ TEST FILE FOR underline MIGRATION (DLT-3035)
4
+
5
+ Each block shows a `<dt-link>` with d-td-* utility classes and the expected
6
+ rewritten form. Most patterns map exactly; "alone" and "both-same" patterns
7
+ map to the closest prop value with a per-file informational note about
8
+ hover-state delta.
9
+ -->
10
+
11
+ <!-- ============================================ -->
12
+ <!-- 1. Clean mappings (no hover delta) -->
13
+ <!-- ============================================ -->
14
+
15
+ <!-- d-td-underline h:d-td-none → strip both (matches default) -->
16
+ <dt-link href="/x" class="d-td-underline h:d-td-none">Default</dt-link>
17
+
18
+ <!-- d-td-none h:d-td-underline → :underline="false" (the canonical example) -->
19
+ <dt-link href="/x" class="d-td-none h:d-td-underline">No-underline link</dt-link>
20
+
21
+ <!-- h:d-td-none alone — strip (matches default hover) -->
22
+ <dt-link href="/x" class="h:d-td-none">Default redundant</dt-link>
23
+
24
+ <!-- ============================================ -->
25
+ <!-- 2. "Alone" patterns — closest mapping + per-file hover-delta note -->
26
+ <!-- ============================================ -->
27
+
28
+ <!-- d-td-none alone — closest is :underline="false" (hover gains underline) -->
29
+ <dt-link href="/x" class="d-td-none">No underline ever (will hover-underline)</dt-link>
30
+
31
+ <!-- d-td-underline alone — closest is default (hover loses underline) -->
32
+ <dt-link href="/x" class="d-td-underline">Always underlined (will lose hover)</dt-link>
33
+
34
+ <!-- ============================================ -->
35
+ <!-- 3. Other classes preserved -->
36
+ <!-- ============================================ -->
37
+
38
+ <dt-link href="/x" class="d-td-none h:d-td-underline d-fc-secondary">
39
+ Mixed with font-color utility
40
+ </dt-link>
41
+
42
+ <!-- ============================================ -->
43
+ <!-- 4. Patterns that warn (not transformed) -->
44
+ <!-- ============================================ -->
45
+
46
+ <!-- Responsive variant — warn, skip -->
47
+ <dt-link href="/x" class="sm:d-td-none h:d-td-underline">Responsive</dt-link>
48
+
49
+ <!-- Dynamic :class containing d-td-* — warn -->
50
+ <dt-link href="/x" :class="{ 'd-td-none': isQuiet }">Dynamic underline</dt-link>
51
+
52
+ <!-- ============================================ -->
53
+ <!-- 5. Idempotent: already on the new API -->
54
+ <!-- ============================================ -->
55
+
56
+ <dt-link href="/x" :underline="false">Already migrated</dt-link>
57
+ </template>
@@ -0,0 +1,161 @@
1
+ /**
2
+ * DLT-3035 — underline transform tests.
3
+ *
4
+ * One assertion per test; data-driven covering each row of the Q1 revised mapping table.
5
+ */
6
+
7
+ import { describe, it } from 'node:test';
8
+ import assert from 'node:assert/strict';
9
+ import { runTransform, runTransformVerbose } from './helpers.mjs';
10
+
11
+ describe('underline: clean-mapping cases (no hover delta)', () => {
12
+ const cases = [
13
+ [
14
+ 'd-td-underline h:d-td-none → strip both (matches default)',
15
+ '<dt-link href="/x" class="d-td-underline h:d-td-none">Go</dt-link>',
16
+ '<dt-link href="/x">Go</dt-link>',
17
+ ],
18
+ [
19
+ 'd-td-none h:d-td-underline → strip + :underline="false"',
20
+ '<dt-link href="/x" class="d-td-none h:d-td-underline">Go</dt-link>',
21
+ '<dt-link href="/x" :underline="false">Go</dt-link>',
22
+ ],
23
+ [
24
+ 'h:d-td-none alone → strip (matches default hover)',
25
+ '<dt-link href="/x" class="h:d-td-none">Go</dt-link>',
26
+ '<dt-link href="/x">Go</dt-link>',
27
+ ],
28
+ ];
29
+
30
+ for (const [label, input, expected] of cases) {
31
+ it(label, () => {
32
+ const { transformed, notes } = runTransformVerbose(input, { filePath: 'fixture.vue' });
33
+ assert.equal(transformed, expected);
34
+ assert.equal(notes.length, 0, 'expected no hover-delta note');
35
+ });
36
+ }
37
+ });
38
+
39
+ describe('underline: alone/same cases (closest-prop with hover delta note)', () => {
40
+ const cases = [
41
+ {
42
+ label: 'd-td-none alone → :underline="false" + note',
43
+ input: '<dt-link href="/x" class="d-td-none">Go</dt-link>',
44
+ expected: '<dt-link href="/x" :underline="false">Go</dt-link>',
45
+ },
46
+ {
47
+ label: 'd-td-none h:d-td-none → :underline="false" + note',
48
+ input: '<dt-link href="/x" class="d-td-none h:d-td-none">Go</dt-link>',
49
+ expected: '<dt-link href="/x" :underline="false">Go</dt-link>',
50
+ },
51
+ {
52
+ label: 'd-td-underline alone → strip (default true) + note',
53
+ input: '<dt-link href="/x" class="d-td-underline">Go</dt-link>',
54
+ expected: '<dt-link href="/x">Go</dt-link>',
55
+ },
56
+ {
57
+ label: 'd-td-underline h:d-td-underline → strip (default true) + note',
58
+ input: '<dt-link href="/x" class="d-td-underline h:d-td-underline">Go</dt-link>',
59
+ expected: '<dt-link href="/x">Go</dt-link>',
60
+ },
61
+ {
62
+ label: 'h:d-td-underline alone → strip (default true) + note',
63
+ input: '<dt-link href="/x" class="h:d-td-underline">Go</dt-link>',
64
+ expected: '<dt-link href="/x">Go</dt-link>',
65
+ },
66
+ ];
67
+
68
+ for (const { label, input, expected } of cases) {
69
+ it(label, () => {
70
+ const { transformed } = runTransformVerbose(input, { filePath: 'fixture.vue' });
71
+ assert.equal(transformed, expected);
72
+ });
73
+ it(`${label} — emits hover-delta note`, () => {
74
+ const { notes } = runTransformVerbose(input, { filePath: 'fixture.vue' });
75
+ assert.equal(notes.length, 1);
76
+ assert.match(notes[0].message, /hover behavior/);
77
+ });
78
+ }
79
+ });
80
+
81
+ describe('underline: warning paths', () => {
82
+ it('responsive variant sm:d-td-none warns and skips', () => {
83
+ const input = '<dt-link href="/x" class="sm:d-td-none">Go</dt-link>';
84
+ const { transformed, warnings } = runTransformVerbose(input, { filePath: 'fixture.vue' });
85
+ assert.equal(transformed, input);
86
+ assert.equal(warnings.length, 1);
87
+ assert.match(warnings[0], /responsive or unsupported/);
88
+ });
89
+
90
+ it('focus variant f:d-td-underline warns and skips', () => {
91
+ const input = '<dt-link href="/x" class="f:d-td-underline">Go</dt-link>';
92
+ const { transformed, warnings } = runTransformVerbose(input, { filePath: 'fixture.vue' });
93
+ assert.equal(transformed, input);
94
+ assert.equal(warnings.length, 1);
95
+ });
96
+
97
+ it('dynamic :class containing d-td-* warns', () => {
98
+ const input = '<dt-link href="/x" :class="{ \'d-td-none\': isHover }">Go</dt-link>';
99
+ const { warnings } = runTransformVerbose(input, { filePath: 'fixture.vue' });
100
+ assert.equal(warnings.length, 1);
101
+ assert.match(warnings[0], /dynamic binding/);
102
+ });
103
+ });
104
+
105
+ describe('underline: self-closing tags preserve the /', () => {
106
+ it('<dt-link class="d-td-none" /> → <dt-link :underline="false" />', () => {
107
+ assert.equal(
108
+ runTransform('<dt-link href="/x" class="d-td-none" />'),
109
+ '<dt-link href="/x" :underline="false" />',
110
+ );
111
+ });
112
+ });
113
+
114
+ describe('underline: dynamic :class with d-td-* warns even when a static class is also present', () => {
115
+ it('emits dynamic-binding warning and leaves the tag unchanged', () => {
116
+ const input = '<dt-link href="/x" class="foo" :class="{ \'d-td-none\': off }">Go</dt-link>';
117
+ const { transformed, warnings } = runTransformVerbose(input, { filePath: 'fixture.vue' });
118
+ assert.equal(transformed, input);
119
+ assert.ok(warnings.some(w => /dynamic binding/.test(w)));
120
+ });
121
+ });
122
+
123
+ describe('underline: PascalCase tag (DtLink) accepted; case preserved on output', () => {
124
+ it('<DtLink class="d-td-none h:d-td-underline"> → <DtLink :underline="false">', () => {
125
+ assert.equal(
126
+ runTransform('<DtLink href="/x" class="d-td-none h:d-td-underline">Go</DtLink>'),
127
+ '<DtLink href="/x" :underline="false">Go</DtLink>',
128
+ );
129
+ });
130
+ });
131
+
132
+ describe('underline: idempotency and other classes preserved', () => {
133
+ it('already-migrated <dt-link :underline="false"> is a no-op', () => {
134
+ const input = '<dt-link href="/x" :underline="false">Go</dt-link>';
135
+ assert.equal(runTransform(input), input);
136
+ });
137
+
138
+ it('non-d-td classes preserved when stripping d-td-*', () => {
139
+ const input = '<dt-link href="/x" class="d-td-none h:d-td-underline custom-class">Go</dt-link>';
140
+ const expected = '<dt-link href="/x" class="custom-class" :underline="false">Go</dt-link>';
141
+ assert.equal(runTransform(input), expected);
142
+ });
143
+
144
+ it('<dt-link> with no d-td-* classes is unchanged', () => {
145
+ const input = '<dt-link href="/x" class="custom-class">Go</dt-link>';
146
+ assert.equal(runTransform(input), input);
147
+ });
148
+
149
+ it('<dt-link> with no class attr is unchanged', () => {
150
+ const input = '<dt-link href="/x">Go</dt-link>';
151
+ assert.equal(runTransform(input), input);
152
+ });
153
+ });
154
+
155
+ describe('underline: --only=underline runs only this transform', () => {
156
+ it('skips button-nav and link-nav', () => {
157
+ const input = '<a class="d-btn" href="/x">Go</a>\n<a class="d-link" href="/y">Y</a>';
158
+ const { transformed } = runTransformVerbose(input, { only: ['underline'] });
159
+ assert.equal(transformed, input);
160
+ });
161
+ });