@dialpad/dialtone-css 8.81.0-next.1 → 8.81.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 (24) hide show
  1. package/README.md +8 -0
  2. package/lib/build/js/dialtone_migrate/index.mjs +5 -3
  3. package/lib/build/js/dialtone_migrate_flex_to_stack/index.mjs +87 -5
  4. package/lib/build/js/dialtone_migrate_typography/index.mjs +116 -17
  5. package/lib/build/js/dialtone_migrate_typography/test.mjs +170 -0
  6. package/lib/build/js/dialtone_migration_helper/configs/space-to-spacing.mjs +6 -3
  7. package/lib/build/js/dialtone_migration_helper/configs/stack-gap-to-spacing.mjs +3 -0
  8. package/lib/build/js/dialtone_migration_helper/configs/utility-class-to-token-stops.mjs +24 -36
  9. package/lib/build/js/dialtone_migration_helper/tests/space-to-spacing-test-examples.vue +5 -9
  10. package/lib/build/js/dialtone_migration_helper/tests/utility-class-to-token-stops.test.mjs +22 -0
  11. package/lib/dist/js/dialtone_migrate/index.mjs +5 -3
  12. package/lib/dist/js/dialtone_migrate_flex_to_stack/index.mjs +87 -5
  13. package/lib/dist/js/dialtone_migrate_typography/index.mjs +116 -17
  14. package/lib/dist/js/dialtone_migrate_typography/test.mjs +170 -0
  15. package/lib/dist/js/dialtone_migration_helper/configs/space-to-spacing.mjs +6 -3
  16. package/lib/dist/js/dialtone_migration_helper/configs/stack-gap-to-spacing.mjs +3 -0
  17. package/lib/dist/js/dialtone_migration_helper/configs/utility-class-to-token-stops.mjs +24 -36
  18. package/lib/dist/js/dialtone_migration_helper/tests/space-to-spacing-test-examples.vue +5 -9
  19. package/lib/dist/js/dialtone_migration_helper/tests/utility-class-to-token-stops.test.mjs +22 -0
  20. package/lib/dist/no-layers/dialtone-default-theme.css +33541 -0
  21. package/lib/dist/no-layers/dialtone-default-theme.min.css +1 -0
  22. package/lib/dist/no-layers/dialtone.css +27809 -0
  23. package/lib/dist/no-layers/dialtone.min.css +1 -0
  24. package/package.json +10 -2
@@ -12,6 +12,7 @@ import {
12
12
  detectImportPathFor,
13
13
  removeMarkersForTest,
14
14
  validateDtTextProps,
15
+ injectComponentImport,
15
16
  } from './index.mjs';
16
17
 
17
18
  function run (input) {
@@ -987,6 +988,26 @@ describe('wrapper safety — positive cases still convert (no false positives)',
987
988
  });
988
989
  });
989
990
 
991
+ describe('dynamic :class flagging — multi-line element', () => {
992
+ it('marker goes before the opening < of a multi-line element, not between attributes', () => {
993
+ const input = [
994
+ '<div',
995
+ ' v-if="condition"',
996
+ ' :class="{ \'d-headline--md\': isHeading }"',
997
+ '>x</div>',
998
+ ].join('\n');
999
+ const out = run(input);
1000
+ // Marker must appear on its own line before <div, never inside the tag
1001
+ assert.ok(out.includes('<!-- dt-text-migrate: review dynamic class -->'), 'should emit marker');
1002
+ const markerIdx = out.indexOf('<!-- dt-text-migrate: review dynamic class -->');
1003
+ const divIdx = out.indexOf('<div');
1004
+ assert.ok(markerIdx < divIdx, 'marker must precede the opening <div');
1005
+ // The tag itself must remain structurally intact (comment not injected mid-tag)
1006
+ assert.ok(out.includes(' :class='), ':class line must be unchanged');
1007
+ assert.ok(out.includes(' v-if='), 'v-if line must be unchanged');
1008
+ });
1009
+ });
1010
+
990
1011
  describe('wrapper safety — override path (d-fw-*, d-fc-*, etc.) with component children', () => {
991
1012
  // Override path mirrors the composed-path safety: if the rewriteable tag
992
1013
  // wraps a component/block child, skip auto-conversion. Behavior here is a
@@ -1018,3 +1039,152 @@ describe('wrapper safety — override path (d-fw-*, d-fc-*, etc.) with component
1018
1039
  );
1019
1040
  });
1020
1041
  });
1042
+
1043
+ // ---------------------------------------------------------------------------
1044
+ // injectComponentImport — auto import insertion
1045
+ // ---------------------------------------------------------------------------
1046
+
1047
+ describe('injectComponentImport — <script setup>', () => {
1048
+ it('inserts import after the last existing import', () => {
1049
+ const content = [
1050
+ '<script setup>',
1051
+ 'import { DtStack } from \'@dialpad/dialtone-vue\';',
1052
+ '</script>',
1053
+ '<template><p>x</p></template>',
1054
+ ].join('\n');
1055
+ const out = injectComponentImport(content, 'DtText', '@dialpad/dialtone-vue');
1056
+ assert.ok(out.includes('import { DtText } from \'@dialpad/dialtone-vue\';'), 'import inserted');
1057
+ const stackIdx = out.indexOf('DtStack');
1058
+ const textIdx = out.indexOf('DtText');
1059
+ assert.ok(textIdx > stackIdx, 'DtText import comes after DtStack import');
1060
+ });
1061
+
1062
+ it('inserts import when no existing imports present', () => {
1063
+ const content = [
1064
+ '<script setup>',
1065
+ 'const x = 1;',
1066
+ '</script>',
1067
+ ].join('\n');
1068
+ const out = injectComponentImport(content, 'DtText', '@dialpad/dialtone-vue');
1069
+ assert.ok(out.includes('import { DtText } from \'@dialpad/dialtone-vue\';'), 'import inserted');
1070
+ });
1071
+
1072
+ it('returns null when component is already imported', () => {
1073
+ const content = [
1074
+ '<script setup>',
1075
+ 'import { DtText } from \'@dialpad/dialtone-vue\';',
1076
+ '</script>',
1077
+ ].join('\n');
1078
+ assert.equal(injectComponentImport(content, 'DtText', '@dialpad/dialtone-vue'), null);
1079
+ });
1080
+
1081
+ it('does not add to components object (not needed for script setup)', () => {
1082
+ const content = [
1083
+ '<script setup>',
1084
+ 'import { DtStack } from \'@dialpad/dialtone-vue\';',
1085
+ '</script>',
1086
+ ].join('\n');
1087
+ const out = injectComponentImport(content, 'DtText', '@dialpad/dialtone-vue');
1088
+ assert.ok(!out.includes('components:'), 'no components object added for script setup');
1089
+ });
1090
+
1091
+ it('handles <script setup lang="ts">', () => {
1092
+ const content = [
1093
+ '<script setup lang="ts">',
1094
+ 'import { DtStack } from \'@dialpad/dialtone-vue\';',
1095
+ '</script>',
1096
+ ].join('\n');
1097
+ const out = injectComponentImport(content, 'DtText', '@dialpad/dialtone-vue');
1098
+ assert.ok(out !== null, 'should succeed');
1099
+ assert.ok(out.includes('import { DtText } from \'@dialpad/dialtone-vue\';'));
1100
+ });
1101
+ });
1102
+
1103
+ describe('injectComponentImport — Options API', () => {
1104
+ it('inserts import and adds to existing components object', () => {
1105
+ const content = [
1106
+ '<script>',
1107
+ 'import { DtStack } from \'@dialpad/dialtone-vue\';',
1108
+ 'export default {',
1109
+ ' components: {',
1110
+ ' DtStack,',
1111
+ ' },',
1112
+ '};',
1113
+ '</script>',
1114
+ ].join('\n');
1115
+ const out = injectComponentImport(content, 'DtText', '@dialpad/dialtone-vue');
1116
+ assert.ok(out.includes('import { DtText } from \'@dialpad/dialtone-vue\';'), 'import inserted');
1117
+ assert.ok(out.includes('DtText,'), 'DtText added to components');
1118
+ assert.ok(out.includes('DtStack,'), 'existing DtStack preserved');
1119
+ });
1120
+
1121
+ it('returns null when no components object exists', () => {
1122
+ const content = [
1123
+ '<script>',
1124
+ 'export default {',
1125
+ ' data () { return {}; },',
1126
+ '};',
1127
+ '</script>',
1128
+ ].join('\n');
1129
+ assert.equal(injectComponentImport(content, 'DtText', '@dialpad/dialtone-vue'), null);
1130
+ });
1131
+
1132
+ it('returns null when no script block found', () => {
1133
+ const content = '<template><p>x</p></template>';
1134
+ assert.equal(injectComponentImport(content, 'DtText', '@dialpad/dialtone-vue'), null);
1135
+ });
1136
+
1137
+ it('returns null when component is already imported', () => {
1138
+ const content = [
1139
+ '<script>',
1140
+ 'import { DtText } from \'@dialpad/dialtone-vue\';',
1141
+ 'export default { components: { DtText } };',
1142
+ '</script>',
1143
+ ].join('\n');
1144
+ assert.equal(injectComponentImport(content, 'DtText', '@dialpad/dialtone-vue'), null);
1145
+ });
1146
+
1147
+ it('does not corrupt a helper object with components: { before export default', () => {
1148
+ const content = [
1149
+ '<script>',
1150
+ 'import { DtStack } from \'@dialpad/dialtone-vue\';',
1151
+ 'const editorConfig = { components: { toolbar: true } };',
1152
+ 'export default {',
1153
+ ' components: {',
1154
+ ' DtStack,',
1155
+ ' },',
1156
+ '};',
1157
+ '</script>',
1158
+ ].join('\n');
1159
+ const out = injectComponentImport(content, 'DtText', '@dialpad/dialtone-vue');
1160
+ assert.ok(out, 'should return updated content');
1161
+ assert.ok(out.includes('import { DtText } from \'@dialpad/dialtone-vue\';'), 'import inserted');
1162
+ // DtText must be added to export default components, not to the editorConfig object
1163
+ const editorConfigIdx = out.indexOf('editorConfig');
1164
+ const dtTextIdx = out.indexOf('DtText,');
1165
+ const exportDefaultIdx = out.indexOf('export default');
1166
+ assert.ok(dtTextIdx > exportDefaultIdx, 'DtText registered after export default');
1167
+ assert.ok(dtTextIdx > editorConfigIdx, 'DtText not inserted into editorConfig helper');
1168
+ assert.ok(!out.slice(0, exportDefaultIdx).includes('DtText,'), 'DtText not in pre-export-default scope');
1169
+ });
1170
+
1171
+ it('returns null when components: { appears only in a template binding, not in export default', () => {
1172
+ // Simulate a file where the script has no components option but the template
1173
+ // has a :config="{ components: { ... } }" binding — the template is masked
1174
+ // during injectComponentImport (which operates on the full SFC), so the
1175
+ // components: { in the template must not be matched.
1176
+ const content = [
1177
+ '<template>',
1178
+ ' <some-editor :config="{ components: { toolbar: MyBar } }" />',
1179
+ '</template>',
1180
+ '<script>',
1181
+ 'import { DtStack } from \'@dialpad/dialtone-vue\';',
1182
+ 'export default {',
1183
+ ' data () { return {}; },',
1184
+ '};',
1185
+ '</script>',
1186
+ ].join('\n');
1187
+ // No components: { in export default → should return null (can't auto-register)
1188
+ assert.equal(injectComponentImport(content, 'DtText', '@dialpad/dialtone-vue'), null);
1189
+ });
1190
+ });
@@ -28,8 +28,8 @@ export default {
28
28
  'eg. var(--dt-space-400) → var(--dt-spacing-100)\n' +
29
29
  '- Replaces var(--dt-space-{stop}-negative) with var(--dt-spacing-{newSuffix}-negative)\n\t' +
30
30
  'eg. var(--dt-space-400-negative) → var(--dt-spacing-100-negative)\n' +
31
- '- Replaces var(--dt-space-{stop}-percent) with var(--dt-spacing-{newSuffix}-percent)\n\t' +
32
- 'eg. var(--dt-space-400-percent) var(--dt-spacing-100-percent)\n' +
31
+ '- var(--dt-space-{stop}-percent) is left unchanged — percent tokens live under\n\t' +
32
+ '--dt-layout-*-percent (a different stop axis), so there is no safe automated mapping.\n' +
33
33
  '- Tokens with no equivalent (720, 730, 750+) are left unchanged for manual review.\n',
34
34
  patterns: ['**/*.{css,less,scss,sass,styl,html,vue,md,js,ts,jsx,tsx}'],
35
35
  globbyConfig: {
@@ -37,7 +37,10 @@ export default {
37
37
  },
38
38
  expressions: [
39
39
  {
40
- from: /var\(--dt-space-([0-9]+)(-negative|-percent)?\)/g,
40
+ // -percent variants are intentionally excluded: --dt-spacing-*-percent tokens do not
41
+ // exist. Percent tokens live under --dt-layout-*-percent with a different stop axis
42
+ // (0/5/10…100 = percentages) and cannot be automatically mapped from space pixel stops.
43
+ from: /var\(--dt-space-([0-9]+)(-negative)?\)/g,
41
44
  to: (match, stop, suffix) => {
42
45
  const newSuffix = MAP[Number(stop)];
43
46
  if (newSuffix == null) return match;
@@ -35,6 +35,9 @@ function isAlreadyMigrated (content) {
35
35
  }
36
36
 
37
37
  export default {
38
+ // Gap values map directly to new spacing stops; the output of one pass (e.g. 200)
39
+ // is also a valid old input (200 → 25). A second pass would double-transform.
40
+ singlePass: true,
38
41
  description:
39
42
  'Migrates DtStack and DtDescriptionList gap prop values from old size stops to new spacing stops.\n' +
40
43
  '- Replaces gap="400" with gap="100" (8px)\n' +
@@ -36,7 +36,7 @@ const NEGATIVE_SPACING_MAP = {
36
36
  32: '400', 48: '600', 64: '800',
37
37
  };
38
38
 
39
- // Layout sizes for margin/padding (96px, 128px use layout tokens)
39
+ // Layout sizes for sizing (96px, 128px use layout tokens)
40
40
  const SPACING_LAYOUT_MAP = {
41
41
  96: '150', 128: '200',
42
42
  };
@@ -77,8 +77,8 @@ export default {
77
77
  'Migrates pixel-based utility class names to token-stop-based names.\n' +
78
78
  '- Sizing: d-h16 → d-h-25, d-w64 → d-w-100, d-hmn96 → d-hmn-150\n' +
79
79
  '- Off-scale sizing: d-w1 → d-w-1px, d-h24 → d-h-24px (pixel-indexed exceptions)\n' +
80
- '- Margin: d-m8 → d-m-100, d-mt16 → d-mt-200, d-mtn8 → d-mt-n100\n' +
81
- '- Padding: d-p8 → d-p-100, d-pt16 → d-pt-200\n' +
80
+ '- Margin: d-m8 → d-m-100, d-mt16 → d-mt-200, d-mtn8 → d-mt-n100 (values ≥ 96px left unchanged)\n' +
81
+ '- Padding: d-p8 → d-p-100, d-pt16 → d-pt-200 (values ≥ 96px left unchanged — no layout-token padding class exists)\n' +
82
82
  '- Gap: d-g8 → d-g-100, d-rg16 → d-rg-200\n' +
83
83
  '- Position: d-t8 → d-t-100, d-tn8 → d-t-n100\n' +
84
84
  '- Border-radius all: d-bar6 → d-bar-350, d-bar24 → d-bar-550\n' +
@@ -134,18 +134,13 @@ export default {
134
134
  ]),
135
135
 
136
136
  // ── Margin: d-m{px} → d-m-{spacing-stop} ─────────────────────────────
137
- ...['m', 'mt', 'mr', 'mb', 'ml', 'mx', 'my'].flatMap(prefix => [
138
- // Spacing sizes (0-64px)
139
- {
140
- from: buildClassRegex(`d-${prefix}`, SPACING_MAP),
141
- to: (match, pre, px, post) => `${pre}d-${prefix}-${SPACING_MAP[px]}${post}`,
142
- },
143
- // Layout sizes (96, 128px)
144
- {
145
- from: buildClassRegex(`d-${prefix}`, SPACING_LAYOUT_MAP),
146
- to: (match, pre, px, post) => `${pre}d-${prefix}-${SPACING_LAYOUT_MAP[px]}${post}`,
147
- },
148
- ]),
137
+ // Only spacing-scale values (0-64px) are migrated. Values ≥ 96px (e.g. d-mt96)
138
+ // are left unchanged: token-stop margin classes use spacing tokens only, so
139
+ // d-mt-150 resolves to --dt-spacing-150 (12px), not --dt-layout-150 (96px).
140
+ ...['m', 'mt', 'mr', 'mb', 'ml', 'mx', 'my'].map(prefix => ({
141
+ from: buildClassRegex(`d-${prefix}`, SPACING_MAP),
142
+ to: (match, pre, px, post) => `${pre}d-${prefix}-${SPACING_MAP[px]}${post}`,
143
+ })),
149
144
 
150
145
  // ── Negative margin: d-m{dir}n{px} → d-m{dir}-n{spacing-stop} ────────
151
146
  ...['mt', 'mr', 'mb', 'ml', 'mx', 'my', 'm'].map(prefix => ({
@@ -154,16 +149,13 @@ export default {
154
149
  })),
155
150
 
156
151
  // ── Padding: d-p{px} → d-p-{spacing-stop} ────────────────────────────
157
- ...['p', 'pt', 'pr', 'pb', 'pl', 'px', 'py'].flatMap(prefix => [
158
- {
159
- from: buildClassRegex(`d-${prefix}`, SPACING_MAP),
160
- to: (match, pre, px, post) => `${pre}d-${prefix}-${SPACING_MAP[px]}${post}`,
161
- },
162
- {
163
- from: buildClassRegex(`d-${prefix}`, SPACING_LAYOUT_MAP),
164
- to: (match, pre, px, post) => `${pre}d-${prefix}-${SPACING_LAYOUT_MAP[px]}${post}`,
165
- },
166
- ]),
152
+ // Only spacing-scale values (0-64px) are migrated. Values ≥ 96px (e.g. d-pt96)
153
+ // are left unchanged: token-stop padding classes use spacing tokens only, so
154
+ // d-pt-150 resolves to --dt-spacing-150 (12px), not --dt-layout-150 (96px).
155
+ ...['p', 'pt', 'pr', 'pb', 'pl', 'px', 'py'].map(prefix => ({
156
+ from: buildClassRegex(`d-${prefix}`, SPACING_MAP),
157
+ to: (match, pre, px, post) => `${pre}d-${prefix}-${SPACING_MAP[px]}${post}`,
158
+ })),
167
159
 
168
160
  // ── Gap: d-g{px} → d-g-{spacing-stop} ────────────────────────────────
169
161
  ...['g', 'rg', 'cg'].map(prefix => ({
@@ -172,17 +164,13 @@ export default {
172
164
  })),
173
165
 
174
166
  // ── Position: d-t{px} → d-t-{spacing-stop} ───────────────────────────
175
- ...['t', 'r', 'b', 'l', 'x', 'y', 'all'].flatMap(prefix => [
176
- {
177
- from: buildClassRegex(`d-${prefix}`, SPACING_MAP),
178
- to: (match, pre, px, post) => `${pre}d-${prefix}-${SPACING_MAP[px]}${post}`,
179
- },
180
- // Layout position sizes (96px)
181
- {
182
- from: buildClassRegex(`d-${prefix}`, SPACING_LAYOUT_MAP),
183
- to: (match, pre, px, post) => `${pre}d-${prefix}-${SPACING_LAYOUT_MAP[px]}${post}`,
184
- },
185
- ]),
167
+ // Only spacing-scale values (0-64px) are migrated. Values ≥ 96px (e.g. d-t96)
168
+ // are left unchanged: token-stop position classes use spacing tokens only, so
169
+ // d-t-150 resolves to --dt-spacing-150 (12px), not --dt-layout-150 (96px).
170
+ ...['t', 'r', 'b', 'l', 'x', 'y', 'all'].map(prefix => ({
171
+ from: buildClassRegex(`d-${prefix}`, SPACING_MAP),
172
+ to: (match, pre, px, post) => `${pre}d-${prefix}-${SPACING_MAP[px]}${post}`,
173
+ })),
186
174
 
187
175
  // ── Negative position: d-{dir}n{px} → d-{dir}-n{spacing-stop} ────────
188
176
  ...['t', 'r', 'b', 'l', 'x', 'y', 'all'].map(prefix => ({
@@ -149,23 +149,19 @@
149
149
  }
150
150
 
151
151
  /* ============================================ */
152
- /* PERCENT VARIANTS */
152
+ /* SKIP CASES (should NOT be modified) */
153
153
  /* ============================================ */
154
154
 
155
- /* space-400-percent spacing-100-percent */
156
- .test-percent-400 {
155
+ /* -percent variants — no --dt-spacing-*-percent tokens exist; percent tokens live under
156
+ --dt-layout-*-percent with a different stop axis, so these are left for manual review */
157
+ .test-skip-percent-400 {
157
158
  padding: var(--dt-space-400-percent);
158
159
  }
159
160
 
160
- /* space-600-percent → spacing-400-percent */
161
- .test-percent-600 {
161
+ .test-skip-percent-600 {
162
162
  gap: var(--dt-space-600-percent);
163
163
  }
164
164
 
165
- /* ============================================ */
166
- /* SKIP CASES (should NOT be modified) */
167
- /* ============================================ */
168
-
169
165
  /* Stops with no --dt-spacing-* equivalent — leave unchanged */
170
166
  .test-skip-720 {
171
167
  padding: var(--dt-space-720); /* 72px — no spacing equivalent */
@@ -89,6 +89,28 @@ describe('utility-class-to-token-stops config', () => {
89
89
  });
90
90
  });
91
91
 
92
+
93
+ // ─── Large spacing values (≥ 96px) pass through unchanged ────────────
94
+ //
95
+ // Token-stop margin/padding/position classes (d-mt-{stop}, d-pt-{stop}, d-t-{stop})
96
+ // use spacing tokens only. d-mt-150 = --dt-spacing-150 (12px), not --dt-layout-150 (96px).
97
+ // These classes must be left for manual review rather than silently changed.
98
+
99
+ describe('large margin/padding/position values — pass through unchanged', () => {
100
+ const unchanged = [
101
+ 'd-mt96', 'd-mb96', 'd-ml96', 'd-mr96', 'd-mx96', 'd-my96', 'd-m96',
102
+ 'd-mt128', 'd-mb128', 'd-ml128', 'd-mr128', 'd-mx128', 'd-my128', 'd-m128',
103
+ 'd-pt96', 'd-pb96', 'd-pl96', 'd-pr96', 'd-px96', 'd-py96', 'd-p96',
104
+ 'd-pt128', 'd-pb128', 'd-pl128', 'd-pr128', 'd-px128', 'd-py128', 'd-p128',
105
+ 'd-t96', 'd-r96', 'd-b96', 'd-l96',
106
+ ];
107
+ for (const cls of unchanged) {
108
+ it(`${cls} is left unchanged`, () => {
109
+ assert.equal(apply(`<div class="${cls}" />`), `<div class="${cls}" />`);
110
+ });
111
+ }
112
+ });
113
+
92
114
  // ─── Border-radius migration (DLT-3329) ───────────────────────────────
93
115
 
94
116
  describe('border-radius all-corners — legacy pixel-suffix to token stop', () => {