@wordpress/global-styles-engine 1.17.0 → 1.18.0
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/CHANGELOG.md +7 -0
- package/build/core/render.cjs +36 -16
- package/build/core/render.cjs.map +2 -2
- package/build/index.cjs +3 -0
- package/build/index.cjs.map +2 -2
- package/build/lock-unlock.cjs +37 -0
- package/build/lock-unlock.cjs.map +7 -0
- package/build/private-apis.cjs +38 -0
- package/build/private-apis.cjs.map +7 -0
- package/build/settings/get-setting.cjs +3 -1
- package/build/settings/get-setting.cjs.map +2 -2
- package/build/types.cjs.map +1 -1
- package/build/utils/common.cjs +2 -4
- package/build/utils/common.cjs.map +2 -2
- package/build/utils/viewport.cjs +108 -0
- package/build/utils/viewport.cjs.map +7 -0
- package/build-module/core/render.mjs +36 -16
- package/build-module/core/render.mjs.map +2 -2
- package/build-module/index.mjs +2 -0
- package/build-module/index.mjs.map +2 -2
- package/build-module/lock-unlock.mjs +11 -0
- package/build-module/lock-unlock.mjs.map +7 -0
- package/build-module/private-apis.mjs +17 -0
- package/build-module/private-apis.mjs.map +7 -0
- package/build-module/settings/get-setting.mjs +3 -1
- package/build-module/settings/get-setting.mjs.map +2 -2
- package/build-module/utils/common.mjs +2 -4
- package/build-module/utils/common.mjs.map +2 -2
- package/build-module/utils/viewport.mjs +81 -0
- package/build-module/utils/viewport.mjs.map +7 -0
- package/build-types/core/render.d.ts +1 -1
- package/build-types/core/render.d.ts.map +1 -1
- package/build-types/index.d.ts +1 -0
- package/build-types/index.d.ts.map +1 -1
- package/build-types/lock-unlock.d.ts +2 -0
- package/build-types/lock-unlock.d.ts.map +1 -0
- package/build-types/private-apis.d.ts +2 -0
- package/build-types/private-apis.d.ts.map +1 -0
- package/build-types/settings/get-setting.d.ts.map +1 -1
- package/build-types/style-state-back-compat.d.ts.map +1 -1
- package/build-types/types.d.ts +4 -0
- package/build-types/types.d.ts.map +1 -1
- package/build-types/utils/common.d.ts.map +1 -1
- package/build-types/utils/fluid.d.ts +1 -1
- package/build-types/utils/fluid.d.ts.map +1 -1
- package/build-types/utils/viewport.d.ts +41 -0
- package/build-types/utils/viewport.d.ts.map +1 -0
- package/package.json +7 -6
- package/src/core/render.tsx +45 -21
- package/src/index.ts +1 -0
- package/src/lock-unlock.ts +10 -0
- package/src/private-apis.ts +16 -0
- package/src/settings/get-setting.ts +2 -0
- package/src/test/render.test.ts +182 -0
- package/src/test/viewport-utils.test.ts +168 -0
- package/src/types.ts +4 -0
- package/src/utils/common.ts +13 -8
- package/src/utils/viewport.ts +153 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordpress/global-styles-engine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"description": "Pure CSS generation engine for WordPress global styles.",
|
|
5
5
|
"author": "The WordPress Contributors",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -42,10 +42,11 @@
|
|
|
42
42
|
"types": "build-types/index.d.ts",
|
|
43
43
|
"sideEffects": false,
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@wordpress/blocks": "^15.
|
|
46
|
-
"@wordpress/data": "^10.
|
|
47
|
-
"@wordpress/i18n": "^6.
|
|
48
|
-
"@wordpress/
|
|
45
|
+
"@wordpress/blocks": "^15.24.0",
|
|
46
|
+
"@wordpress/data": "^10.51.0",
|
|
47
|
+
"@wordpress/i18n": "^6.24.0",
|
|
48
|
+
"@wordpress/private-apis": "^1.51.0",
|
|
49
|
+
"@wordpress/style-engine": "^2.51.0",
|
|
49
50
|
"colord": "^2.9.3",
|
|
50
51
|
"deepmerge": "^4.3.1",
|
|
51
52
|
"fast-deep-equal": "^3.1.3",
|
|
@@ -59,5 +60,5 @@
|
|
|
59
60
|
"publishConfig": {
|
|
60
61
|
"access": "public"
|
|
61
62
|
},
|
|
62
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "e9a74f9c14095a34398ecd4d1f7a908e55051205"
|
|
63
64
|
}
|
package/src/core/render.tsx
CHANGED
|
@@ -34,6 +34,7 @@ import { getGapCSSValue } from '../utils/gap';
|
|
|
34
34
|
import { setBackgroundStyleDefaults } from '../utils/background';
|
|
35
35
|
import { LAYOUT_DEFINITIONS } from '../utils/layout';
|
|
36
36
|
import { getValueFromObjectPath, setImmutably } from '../utils/object';
|
|
37
|
+
import { getResponsiveMediaQueries } from '../utils/viewport';
|
|
37
38
|
import { getSetting } from '../settings/get-setting';
|
|
38
39
|
import type { GlobalStylesConfig, GlobalStylesStyles } from '../types';
|
|
39
40
|
import { normalizeStyleStateAliases } from '../style-state-back-compat';
|
|
@@ -228,15 +229,6 @@ const VALID_ELEMENT_PSEUDO_SELECTORS: Record< string, string[] > = {
|
|
|
228
229
|
],
|
|
229
230
|
};
|
|
230
231
|
|
|
231
|
-
/**
|
|
232
|
-
* Responsive breakpoint state keys and their corresponding CSS media queries.
|
|
233
|
-
* Keep in sync with WP_Theme_JSON_Gutenberg::RESPONSIVE_BREAKPOINTS.
|
|
234
|
-
*/
|
|
235
|
-
const RESPONSIVE_BREAKPOINTS: Record< string, string > = {
|
|
236
|
-
'@mobile': '@media (width <= 480px)',
|
|
237
|
-
'@tablet': '@media (480px < width <= 782px)',
|
|
238
|
-
};
|
|
239
|
-
|
|
240
232
|
/**
|
|
241
233
|
* Transform given preset tree into a set of preset class declarations.
|
|
242
234
|
*
|
|
@@ -913,13 +905,21 @@ const STYLE_KEYS = [
|
|
|
913
905
|
'background',
|
|
914
906
|
];
|
|
915
907
|
|
|
916
|
-
function pickStyleKeys(
|
|
917
|
-
|
|
908
|
+
function pickStyleKeys(
|
|
909
|
+
treeToPickFrom: any,
|
|
910
|
+
responsiveMediaQueries: Record< string, string >
|
|
911
|
+
): any {
|
|
912
|
+
return pickStyleAndPseudoKeys(
|
|
913
|
+
treeToPickFrom,
|
|
914
|
+
undefined,
|
|
915
|
+
responsiveMediaQueries
|
|
916
|
+
);
|
|
918
917
|
}
|
|
919
918
|
|
|
920
919
|
function pickStyleAndPseudoKeys(
|
|
921
920
|
treeToPickFrom: any,
|
|
922
|
-
blockName
|
|
921
|
+
blockName: string | undefined,
|
|
922
|
+
responsiveMediaQueries: Record< string, string >
|
|
923
923
|
): any {
|
|
924
924
|
if ( ! treeToPickFrom ) {
|
|
925
925
|
return {};
|
|
@@ -933,7 +933,7 @@ function pickStyleAndPseudoKeys(
|
|
|
933
933
|
( [ key ] ) =>
|
|
934
934
|
STYLE_KEYS.includes( key ) ||
|
|
935
935
|
allowedPseudoSelectors.includes( key ) ||
|
|
936
|
-
|
|
936
|
+
responsiveMediaQueries[ key ]
|
|
937
937
|
);
|
|
938
938
|
// clone the style objects so that `getFeatureDeclarations` can remove consumed keys from it
|
|
939
939
|
const clonedEntries = pickedEntries.map( ( [ key, style ] ) => [
|
|
@@ -1001,14 +1001,22 @@ function getPseudoStyleNodes( node: StylesNode ): StylesNode[] {
|
|
|
1001
1001
|
* Breakpoint nodes render feature-level, base, and pseudo declarations through
|
|
1002
1002
|
* the normal node renderer.
|
|
1003
1003
|
*
|
|
1004
|
-
* @param node
|
|
1004
|
+
* @param node Style node that may contain configured responsive state styles.
|
|
1005
|
+
* @param responsiveMediaQueries Media queries keyed by responsive state name.
|
|
1005
1006
|
* @return Responsive style nodes in configured breakpoint order.
|
|
1006
1007
|
*/
|
|
1007
|
-
function getResponsiveStyleNodes(
|
|
1008
|
+
function getResponsiveStyleNodes(
|
|
1009
|
+
node: StylesNode,
|
|
1010
|
+
responsiveMediaQueries: Record< string, string >
|
|
1011
|
+
): StylesNode[] {
|
|
1008
1012
|
const {
|
|
1009
1013
|
styles,
|
|
1010
1014
|
selector,
|
|
1015
|
+
fallbackGapValue,
|
|
1011
1016
|
featureSelectors,
|
|
1017
|
+
hasLayoutSupport,
|
|
1018
|
+
layoutHasBlockGapSupport,
|
|
1019
|
+
layoutSelector,
|
|
1012
1020
|
name,
|
|
1013
1021
|
elementName,
|
|
1014
1022
|
isStyleVariation,
|
|
@@ -1019,7 +1027,7 @@ function getResponsiveStyleNodes( node: StylesNode ): StylesNode[] {
|
|
|
1019
1027
|
return [];
|
|
1020
1028
|
}
|
|
1021
1029
|
|
|
1022
|
-
return Object.entries(
|
|
1030
|
+
return Object.entries( responsiveMediaQueries ).flatMap(
|
|
1023
1031
|
( [ breakpointKey, mediaQuery ] ) => {
|
|
1024
1032
|
const breakpointStyles = styles?.[ breakpointKey ];
|
|
1025
1033
|
if ( ! breakpointStyles || typeof breakpointStyles !== 'object' ) {
|
|
@@ -1035,10 +1043,14 @@ function getResponsiveStyleNodes( node: StylesNode ): StylesNode[] {
|
|
|
1035
1043
|
featureSelectors && typeof featureSelectors !== 'string'
|
|
1036
1044
|
? featureSelectors
|
|
1037
1045
|
: undefined,
|
|
1046
|
+
fallbackGapValue,
|
|
1047
|
+
hasLayoutSupport,
|
|
1038
1048
|
name,
|
|
1039
1049
|
elementName,
|
|
1040
1050
|
isStyleVariation,
|
|
1041
1051
|
variationName,
|
|
1052
|
+
layoutSelector,
|
|
1053
|
+
layoutHasBlockGapSupport,
|
|
1042
1054
|
},
|
|
1043
1055
|
];
|
|
1044
1056
|
}
|
|
@@ -1055,8 +1067,10 @@ export const getNodesWithStyles = (
|
|
|
1055
1067
|
return nodes;
|
|
1056
1068
|
}
|
|
1057
1069
|
|
|
1070
|
+
const responsiveMediaQueries = getResponsiveMediaQueries( tree );
|
|
1071
|
+
|
|
1058
1072
|
// Top-level.
|
|
1059
|
-
const styles = pickStyleKeys( tree.styles );
|
|
1073
|
+
const styles = pickStyleKeys( tree.styles, responsiveMediaQueries );
|
|
1060
1074
|
if ( styles ) {
|
|
1061
1075
|
nodes.push( {
|
|
1062
1076
|
styles,
|
|
@@ -1085,7 +1099,11 @@ export const getNodesWithStyles = (
|
|
|
1085
1099
|
// Iterate over blocks: they can have styles & elements.
|
|
1086
1100
|
Object.entries( tree.styles?.blocks ?? {} ).forEach(
|
|
1087
1101
|
( [ blockName, node ] ) => {
|
|
1088
|
-
const blockStyles = pickStyleAndPseudoKeys(
|
|
1102
|
+
const blockStyles = pickStyleAndPseudoKeys(
|
|
1103
|
+
node,
|
|
1104
|
+
blockName,
|
|
1105
|
+
responsiveMediaQueries
|
|
1106
|
+
);
|
|
1089
1107
|
const typedNode = node as BlockNode;
|
|
1090
1108
|
|
|
1091
1109
|
// Store variation child nodes so they can be inserted after the block's own elements.
|
|
@@ -1098,7 +1116,8 @@ export const getNodesWithStyles = (
|
|
|
1098
1116
|
const typedVariation = variation as BlockVariation;
|
|
1099
1117
|
const variationStyles = pickStyleAndPseudoKeys(
|
|
1100
1118
|
typedVariation,
|
|
1101
|
-
blockName
|
|
1119
|
+
blockName,
|
|
1120
|
+
responsiveMediaQueries
|
|
1102
1121
|
);
|
|
1103
1122
|
if ( typedVariation?.css ) {
|
|
1104
1123
|
variationStyles.css = typedVariation.css;
|
|
@@ -1193,7 +1212,8 @@ export const getNodesWithStyles = (
|
|
|
1193
1212
|
const variationBlockStyleNodes =
|
|
1194
1213
|
pickStyleAndPseudoKeys(
|
|
1195
1214
|
variationBlockStyles,
|
|
1196
|
-
variationBlockName
|
|
1215
|
+
variationBlockName,
|
|
1216
|
+
responsiveMediaQueries
|
|
1197
1217
|
);
|
|
1198
1218
|
|
|
1199
1219
|
if ( variationBlockStyles?.css ) {
|
|
@@ -1757,12 +1777,16 @@ export const transformToStyles = (
|
|
|
1757
1777
|
}
|
|
1758
1778
|
|
|
1759
1779
|
if ( options.blockStyles ) {
|
|
1780
|
+
const responsiveMediaQueries = getResponsiveMediaQueries( tree );
|
|
1760
1781
|
nodesWithStyles.forEach( ( node ) => {
|
|
1761
1782
|
if ( node.isStyleVariation && ! options.variationStyles ) {
|
|
1762
1783
|
return;
|
|
1763
1784
|
}
|
|
1764
1785
|
|
|
1765
|
-
const responsiveNodes = getResponsiveStyleNodes(
|
|
1786
|
+
const responsiveNodes = getResponsiveStyleNodes(
|
|
1787
|
+
node,
|
|
1788
|
+
responsiveMediaQueries
|
|
1789
|
+
);
|
|
1766
1790
|
// Match PHP node order: base, responsive base, pseudo, responsive pseudo.
|
|
1767
1791
|
[
|
|
1768
1792
|
node,
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/private-apis';
|
|
5
|
+
|
|
6
|
+
export const { lock, unlock } =
|
|
7
|
+
__dangerousOptInToUnstableAPIsOnlyForCoreModules(
|
|
8
|
+
'I acknowledge private features are not for use in themes or plugins and doing so will break in the next version of WordPress.',
|
|
9
|
+
'@wordpress/global-styles-engine'
|
|
10
|
+
);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import {
|
|
5
|
+
getResponsiveMediaQueries,
|
|
6
|
+
getViewportBreakpoints,
|
|
7
|
+
getViewportBreakpointValueInPixels,
|
|
8
|
+
} from './utils/viewport';
|
|
9
|
+
import { lock } from './lock-unlock';
|
|
10
|
+
|
|
11
|
+
export const privateApis = {};
|
|
12
|
+
lock( privateApis, {
|
|
13
|
+
getResponsiveMediaQueries,
|
|
14
|
+
getViewportBreakpoints,
|
|
15
|
+
getViewportBreakpointValueInPixels,
|
|
16
|
+
} );
|
package/src/test/render.test.ts
CHANGED
|
@@ -1040,6 +1040,47 @@ describe( 'global styles renderer', () => {
|
|
|
1040
1040
|
);
|
|
1041
1041
|
} );
|
|
1042
1042
|
|
|
1043
|
+
it( 'handles responsive block gap styles', () => {
|
|
1044
|
+
const tree = {
|
|
1045
|
+
styles: {
|
|
1046
|
+
blocks: {
|
|
1047
|
+
'core/group': {
|
|
1048
|
+
'@mobile': {
|
|
1049
|
+
spacing: {
|
|
1050
|
+
blockGap: '24px',
|
|
1051
|
+
},
|
|
1052
|
+
},
|
|
1053
|
+
},
|
|
1054
|
+
},
|
|
1055
|
+
},
|
|
1056
|
+
} as unknown as GlobalStylesConfig;
|
|
1057
|
+
|
|
1058
|
+
const blockSelectors = {
|
|
1059
|
+
'core/group': {
|
|
1060
|
+
selector: '.wp-block-group',
|
|
1061
|
+
hasLayoutSupport: true,
|
|
1062
|
+
},
|
|
1063
|
+
};
|
|
1064
|
+
|
|
1065
|
+
const result = transformToStyles(
|
|
1066
|
+
Object.freeze( tree ),
|
|
1067
|
+
blockSelectors,
|
|
1068
|
+
true,
|
|
1069
|
+
false,
|
|
1070
|
+
false,
|
|
1071
|
+
true,
|
|
1072
|
+
minimalStyleOptions
|
|
1073
|
+
);
|
|
1074
|
+
|
|
1075
|
+
expect( result ).toContain( '@media (width <= 480px)' );
|
|
1076
|
+
expect( result ).toContain(
|
|
1077
|
+
':root :where(.wp-block-group-is-layout-flow) > * { margin-block-start: 24px; margin-block-end: 0; }'
|
|
1078
|
+
);
|
|
1079
|
+
expect( result ).toContain(
|
|
1080
|
+
':root :where(.wp-block-group-is-layout-flex) { gap: 24px; }'
|
|
1081
|
+
);
|
|
1082
|
+
} );
|
|
1083
|
+
|
|
1043
1084
|
it( 'handles legacy responsive block styles', () => {
|
|
1044
1085
|
const tree = {
|
|
1045
1086
|
styles: {
|
|
@@ -1079,6 +1120,147 @@ describe( 'global styles renderer', () => {
|
|
|
1079
1120
|
);
|
|
1080
1121
|
} );
|
|
1081
1122
|
|
|
1123
|
+
it( 'uses custom viewport breakpoints for responsive block styles', () => {
|
|
1124
|
+
const tree = {
|
|
1125
|
+
settings: {
|
|
1126
|
+
viewport: {
|
|
1127
|
+
mobile: '640px',
|
|
1128
|
+
tablet: '960px',
|
|
1129
|
+
},
|
|
1130
|
+
},
|
|
1131
|
+
styles: {
|
|
1132
|
+
blocks: {
|
|
1133
|
+
'core/button': {
|
|
1134
|
+
'@mobile': {
|
|
1135
|
+
color: {
|
|
1136
|
+
text: 'blue',
|
|
1137
|
+
},
|
|
1138
|
+
},
|
|
1139
|
+
'@tablet': {
|
|
1140
|
+
color: {
|
|
1141
|
+
text: 'green',
|
|
1142
|
+
},
|
|
1143
|
+
},
|
|
1144
|
+
},
|
|
1145
|
+
},
|
|
1146
|
+
},
|
|
1147
|
+
} as unknown as GlobalStylesConfig;
|
|
1148
|
+
|
|
1149
|
+
const blockSelectors = {
|
|
1150
|
+
'core/button': {
|
|
1151
|
+
selector: '.wp-block-button',
|
|
1152
|
+
},
|
|
1153
|
+
};
|
|
1154
|
+
|
|
1155
|
+
const result = transformToStyles(
|
|
1156
|
+
Object.freeze( tree ),
|
|
1157
|
+
blockSelectors,
|
|
1158
|
+
false,
|
|
1159
|
+
false,
|
|
1160
|
+
true,
|
|
1161
|
+
true,
|
|
1162
|
+
minimalStyleOptions
|
|
1163
|
+
);
|
|
1164
|
+
|
|
1165
|
+
expect( result ).toEqual(
|
|
1166
|
+
'@media (width <= 640px){:root :where(.wp-block-button){color: blue;}}@media (640px < width <= 960px){:root :where(.wp-block-button){color: green;}}'
|
|
1167
|
+
);
|
|
1168
|
+
} );
|
|
1169
|
+
|
|
1170
|
+
it( 'omits tablet responsive styles when the tablet breakpoint is not larger than mobile', () => {
|
|
1171
|
+
const tree = {
|
|
1172
|
+
settings: {
|
|
1173
|
+
viewport: {
|
|
1174
|
+
mobile: '960px',
|
|
1175
|
+
tablet: '640px',
|
|
1176
|
+
},
|
|
1177
|
+
},
|
|
1178
|
+
styles: {
|
|
1179
|
+
blocks: {
|
|
1180
|
+
'core/button': {
|
|
1181
|
+
'@mobile': {
|
|
1182
|
+
color: {
|
|
1183
|
+
text: 'blue',
|
|
1184
|
+
},
|
|
1185
|
+
},
|
|
1186
|
+
'@tablet': {
|
|
1187
|
+
color: {
|
|
1188
|
+
text: 'green',
|
|
1189
|
+
},
|
|
1190
|
+
},
|
|
1191
|
+
},
|
|
1192
|
+
},
|
|
1193
|
+
},
|
|
1194
|
+
} as unknown as GlobalStylesConfig;
|
|
1195
|
+
|
|
1196
|
+
const blockSelectors = {
|
|
1197
|
+
'core/button': {
|
|
1198
|
+
selector: '.wp-block-button',
|
|
1199
|
+
},
|
|
1200
|
+
};
|
|
1201
|
+
|
|
1202
|
+
const result = transformToStyles(
|
|
1203
|
+
Object.freeze( tree ),
|
|
1204
|
+
blockSelectors,
|
|
1205
|
+
false,
|
|
1206
|
+
false,
|
|
1207
|
+
true,
|
|
1208
|
+
true,
|
|
1209
|
+
minimalStyleOptions
|
|
1210
|
+
);
|
|
1211
|
+
|
|
1212
|
+
expect( result ).toEqual(
|
|
1213
|
+
'@media (width <= 960px){:root :where(.wp-block-button){color: blue;}}'
|
|
1214
|
+
);
|
|
1215
|
+
} );
|
|
1216
|
+
|
|
1217
|
+
it( 'uses a single max-width tablet query when only the tablet breakpoint is valid', () => {
|
|
1218
|
+
const tree = {
|
|
1219
|
+
settings: {
|
|
1220
|
+
viewport: {
|
|
1221
|
+
mobile: '100%',
|
|
1222
|
+
tablet: '64rem',
|
|
1223
|
+
},
|
|
1224
|
+
},
|
|
1225
|
+
styles: {
|
|
1226
|
+
blocks: {
|
|
1227
|
+
'core/button': {
|
|
1228
|
+
'@mobile': {
|
|
1229
|
+
color: {
|
|
1230
|
+
text: 'blue',
|
|
1231
|
+
},
|
|
1232
|
+
},
|
|
1233
|
+
'@tablet': {
|
|
1234
|
+
color: {
|
|
1235
|
+
text: 'green',
|
|
1236
|
+
},
|
|
1237
|
+
},
|
|
1238
|
+
},
|
|
1239
|
+
},
|
|
1240
|
+
},
|
|
1241
|
+
} as unknown as GlobalStylesConfig;
|
|
1242
|
+
|
|
1243
|
+
const blockSelectors = {
|
|
1244
|
+
'core/button': {
|
|
1245
|
+
selector: '.wp-block-button',
|
|
1246
|
+
},
|
|
1247
|
+
};
|
|
1248
|
+
|
|
1249
|
+
const result = transformToStyles(
|
|
1250
|
+
Object.freeze( tree ),
|
|
1251
|
+
blockSelectors,
|
|
1252
|
+
false,
|
|
1253
|
+
false,
|
|
1254
|
+
true,
|
|
1255
|
+
true,
|
|
1256
|
+
minimalStyleOptions
|
|
1257
|
+
);
|
|
1258
|
+
|
|
1259
|
+
expect( result ).toEqual(
|
|
1260
|
+
'@media (width <= 64rem){:root :where(.wp-block-button){color: green;}}'
|
|
1261
|
+
);
|
|
1262
|
+
} );
|
|
1263
|
+
|
|
1082
1264
|
it( 'handles responsive pseudo selector styles', () => {
|
|
1083
1265
|
const tree = {
|
|
1084
1266
|
styles: {
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import {
|
|
5
|
+
getResponsiveMediaQueries,
|
|
6
|
+
getViewportBreakpoints,
|
|
7
|
+
getViewportBreakpointValueInPixels,
|
|
8
|
+
} from '../utils/viewport';
|
|
9
|
+
|
|
10
|
+
describe( 'viewport utils', () => {
|
|
11
|
+
describe( 'getViewportBreakpointValueInPixels', () => {
|
|
12
|
+
it( 'returns numbers unchanged', () => {
|
|
13
|
+
expect( getViewportBreakpointValueInPixels( 640 ) ).toBe( 640 );
|
|
14
|
+
} );
|
|
15
|
+
|
|
16
|
+
it( 'returns pixel values as numbers', () => {
|
|
17
|
+
expect( getViewportBreakpointValueInPixels( '640px' ) ).toBe( 640 );
|
|
18
|
+
} );
|
|
19
|
+
|
|
20
|
+
it( 'converts em and rem values using a 16px base font size', () => {
|
|
21
|
+
expect( getViewportBreakpointValueInPixels( '40em' ) ).toBe( 640 );
|
|
22
|
+
expect( getViewportBreakpointValueInPixels( '64rem' ) ).toBe(
|
|
23
|
+
1024
|
|
24
|
+
);
|
|
25
|
+
} );
|
|
26
|
+
|
|
27
|
+
it( 'returns undefined for unsupported values', () => {
|
|
28
|
+
expect( getViewportBreakpointValueInPixels( undefined ) ).toBe(
|
|
29
|
+
undefined
|
|
30
|
+
);
|
|
31
|
+
expect( getViewportBreakpointValueInPixels( '100%' ) ).toBe(
|
|
32
|
+
undefined
|
|
33
|
+
);
|
|
34
|
+
expect( getViewportBreakpointValueInPixels( 'auto' ) ).toBe(
|
|
35
|
+
undefined
|
|
36
|
+
);
|
|
37
|
+
} );
|
|
38
|
+
} );
|
|
39
|
+
|
|
40
|
+
describe( 'getViewportBreakpoints', () => {
|
|
41
|
+
it( 'returns custom viewport breakpoints when they are ordered', () => {
|
|
42
|
+
expect(
|
|
43
|
+
getViewportBreakpoints( {
|
|
44
|
+
mobile: '40rem',
|
|
45
|
+
tablet: '64rem',
|
|
46
|
+
} )
|
|
47
|
+
).toEqual( {
|
|
48
|
+
mobile: '40rem',
|
|
49
|
+
tablet: '64rem',
|
|
50
|
+
} );
|
|
51
|
+
} );
|
|
52
|
+
|
|
53
|
+
it( 'returns default viewport breakpoints when no custom breakpoints are valid', () => {
|
|
54
|
+
expect(
|
|
55
|
+
getViewportBreakpoints( {
|
|
56
|
+
mobile: '100%',
|
|
57
|
+
tablet: 'auto',
|
|
58
|
+
} )
|
|
59
|
+
).toEqual( {
|
|
60
|
+
mobile: '480px',
|
|
61
|
+
tablet: '782px',
|
|
62
|
+
} );
|
|
63
|
+
} );
|
|
64
|
+
|
|
65
|
+
it( 'uses a valid custom breakpoint without merging defaults', () => {
|
|
66
|
+
const viewportSettings = {
|
|
67
|
+
mobile: ' 640px ',
|
|
68
|
+
tablet: 'calc(100% - 1rem)',
|
|
69
|
+
desktop: '1200px',
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
expect( getViewportBreakpoints( viewportSettings ) ).toEqual( {
|
|
73
|
+
mobile: '640px',
|
|
74
|
+
} );
|
|
75
|
+
} );
|
|
76
|
+
|
|
77
|
+
it( 'returns the configured viewport for a single breakpoint', () => {
|
|
78
|
+
expect(
|
|
79
|
+
getViewportBreakpoints( {
|
|
80
|
+
tablet: '64rem',
|
|
81
|
+
} )
|
|
82
|
+
).toEqual( {
|
|
83
|
+
tablet: '64rem',
|
|
84
|
+
} );
|
|
85
|
+
} );
|
|
86
|
+
|
|
87
|
+
it( 'preserves the tablet key when mobile is invalid', () => {
|
|
88
|
+
expect(
|
|
89
|
+
getViewportBreakpoints( {
|
|
90
|
+
mobile: '100%',
|
|
91
|
+
tablet: '64rem',
|
|
92
|
+
} )
|
|
93
|
+
).toEqual( {
|
|
94
|
+
tablet: '64rem',
|
|
95
|
+
} );
|
|
96
|
+
} );
|
|
97
|
+
|
|
98
|
+
it( 'omits tablet when it is not larger than mobile', () => {
|
|
99
|
+
expect(
|
|
100
|
+
getViewportBreakpoints( {
|
|
101
|
+
mobile: '64rem',
|
|
102
|
+
tablet: '40rem',
|
|
103
|
+
} )
|
|
104
|
+
).toEqual( {
|
|
105
|
+
mobile: '64rem',
|
|
106
|
+
} );
|
|
107
|
+
} );
|
|
108
|
+
} );
|
|
109
|
+
|
|
110
|
+
describe( 'getResponsiveMediaQueries', () => {
|
|
111
|
+
it( 'returns custom media queries when viewport breakpoints are ordered', () => {
|
|
112
|
+
expect(
|
|
113
|
+
getResponsiveMediaQueries( {
|
|
114
|
+
mobile: '640px',
|
|
115
|
+
tablet: '960px',
|
|
116
|
+
} )
|
|
117
|
+
).toEqual( {
|
|
118
|
+
'@mobile': '@media (width <= 640px)',
|
|
119
|
+
'@tablet': '@media (640px < width <= 960px)',
|
|
120
|
+
} );
|
|
121
|
+
} );
|
|
122
|
+
|
|
123
|
+
it( 'returns default media queries when no custom breakpoints are valid', () => {
|
|
124
|
+
expect(
|
|
125
|
+
getResponsiveMediaQueries( {
|
|
126
|
+
mobile: '100%',
|
|
127
|
+
tablet: 'auto',
|
|
128
|
+
} )
|
|
129
|
+
).toEqual( {
|
|
130
|
+
'@mobile': '@media (width <= 480px)',
|
|
131
|
+
'@tablet': '@media (480px < width <= 782px)',
|
|
132
|
+
} );
|
|
133
|
+
} );
|
|
134
|
+
|
|
135
|
+
it( 'uses a valid custom media query without merging defaults', () => {
|
|
136
|
+
expect(
|
|
137
|
+
getResponsiveMediaQueries( {
|
|
138
|
+
mobile: ' 640px ',
|
|
139
|
+
tablet: 'calc(100% - 1rem)',
|
|
140
|
+
} )
|
|
141
|
+
).toEqual( {
|
|
142
|
+
'@mobile': '@media (width <= 640px)',
|
|
143
|
+
} );
|
|
144
|
+
} );
|
|
145
|
+
|
|
146
|
+
it( 'uses a single max-width tablet media query when only tablet is valid', () => {
|
|
147
|
+
expect(
|
|
148
|
+
getResponsiveMediaQueries( {
|
|
149
|
+
mobile: '100%',
|
|
150
|
+
tablet: '64rem',
|
|
151
|
+
} )
|
|
152
|
+
).toEqual( {
|
|
153
|
+
'@tablet': '@media (width <= 64rem)',
|
|
154
|
+
} );
|
|
155
|
+
} );
|
|
156
|
+
|
|
157
|
+
it( 'omits the tablet media query when the tablet breakpoint is not larger than mobile', () => {
|
|
158
|
+
expect(
|
|
159
|
+
getResponsiveMediaQueries( {
|
|
160
|
+
mobile: '960px',
|
|
161
|
+
tablet: '640px',
|
|
162
|
+
} )
|
|
163
|
+
).toEqual( {
|
|
164
|
+
'@mobile': '@media (width <= 960px)',
|
|
165
|
+
} );
|
|
166
|
+
} );
|
|
167
|
+
} );
|
|
168
|
+
} );
|
package/src/types.ts
CHANGED
|
@@ -193,6 +193,10 @@ export interface SpacingSettings {
|
|
|
193
193
|
*/
|
|
194
194
|
export interface GlobalStylesSettings {
|
|
195
195
|
useRootPaddingAwareAlignments?: boolean;
|
|
196
|
+
viewport?: {
|
|
197
|
+
mobile?: string;
|
|
198
|
+
tablet?: string;
|
|
199
|
+
};
|
|
196
200
|
typography?: TypographySettings;
|
|
197
201
|
layout?: LayoutSettings;
|
|
198
202
|
spacing?: SpacingSettings;
|
package/src/utils/common.ts
CHANGED
|
@@ -317,14 +317,19 @@ export function getBlockStyleVariationSelector(
|
|
|
317
317
|
return variationClass;
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
320
|
+
/*
|
|
321
|
+
* Append the variation class to each selector's ancestor: the first run
|
|
322
|
+
* of characters before any combinator (whitespace) or pseudo-class (`:`).
|
|
323
|
+
* `String.prototype.replace` only replaces the first match.
|
|
324
|
+
*
|
|
325
|
+
* Examples ("custom" variation):
|
|
326
|
+
* - `.wp-block` => `.wp-block.is-style-custom`
|
|
327
|
+
* - `.wp-block .inner` => `.wp-block.is-style-custom .inner`
|
|
328
|
+
* - `.wp-block:where(.a .b)` => `.wp-block.is-style-custom:where(.a .b)`
|
|
329
|
+
* - `:where(.outer .inner)` => `:where(.outer.is-style-custom .inner)`
|
|
330
|
+
*/
|
|
331
|
+
const ancestorRegex = /[^\s:]+/;
|
|
332
|
+
const addVariationClass = ( match: string ) => match + variationClass;
|
|
328
333
|
|
|
329
334
|
const result = splitSelectorList( blockSelector ).map( ( part ) =>
|
|
330
335
|
part.replace( ancestorRegex, addVariationClass )
|