Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,37 @@
*/

import { lastMediaQueryWinsTransform } from '../media-query-transform.js';
import { MediaQuery } from '../media-query.js';

describe('Media Query Transformer', () => {
test('parses container query syntax', () => {
const query = '@container (width >= 360px)';

expect(() => MediaQuery.parser.parseToEnd(query)).not.toThrow();
});
test('basic usage: container queries preserve ordering', () => {
const originalStyles = {
gridColumn: {
default: '1 / 2',
'@container (max-width: 1440px)': '1 / 4',
'@container (max-width: 1024px)': '1 / 3',
'@container (max-width: 768px)': '1 / -1',
},
};

const expectedStyles = {
gridColumn: {
default: '1 / 2',
'@container (min-width: 1024.01px) and (max-width: 1440px)': '1 / 4',
'@container (min-width: 768.01px) and (max-width: 1024px)': '1 / 3',
'@container (max-width: 768px)': '1 / -1',
},
};

const result = lastMediaQueryWinsTransform(originalStyles);
expect(JSON.stringify(result)).toBe(JSON.stringify(expectedStyles));
});

test('basic usage: multiple widths', () => {
const originalStyles = {
gridColumn: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,22 @@ describe('style-value-parser/at-queries', () => {
);
});

test('@container (width >= 360px)', () => {
const parsed = MediaQuery.parser.parseToEnd('@container (width >= 360px)');
expect(parsed.queries).toMatchInlineSnapshot(`
{
"key": "min-width",
"type": "pair",
"value": {
"signCharacter": undefined,
"type": "integer",
"unit": "px",
"value": 360,
},
}
`);
});

test('@media only screen and (max-width: 38em)', () => {
const parsed = MediaQuery.parser.parseToEnd(
'@media only screen and (max-width: 38em)',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,47 @@ function combineMediaQueryWithNegations(
} as const;
}

return new MediaQuery(combinedAst as $FlowFixMe as MediaQueryRule);
return new MediaQuery(
combinedAst as $FlowFixMe as MediaQueryRule,
current.atRuleName,
);
}

function applyQueryOrder(resultObj: { [key: string]: any }, prefix: string) {
if (!Object.keys(resultObj).some((key) => key.startsWith(prefix))) {
return;
}

const queryKeys = Object.keys(resultObj).filter((key) => key.startsWith(prefix));

const negations = [];
const accumulatedNegations = [];

for (let i = queryKeys.length - 1; i > 0; i--) {
const query = MediaQuery.parser.parseToEnd(queryKeys[i]);
negations.push(query);
accumulatedNegations.push([...negations]);
}
accumulatedNegations.reverse();
accumulatedNegations.push([]);

for (let i = 0; i < queryKeys.length; i++) {
const currentKey = queryKeys[i];
const currentValue = resultObj[currentKey];

const baseQuery = MediaQuery.parser.parseToEnd(currentKey);
const reversedNegations = [...accumulatedNegations[i]].reverse();

const combinedQuery = combineMediaQueryWithNegations(
baseQuery,
reversedNegations,
);

const newQueryKey = combinedQuery.toString();

delete resultObj[currentKey];
resultObj[newQueryKey] = currentValue;
}
}

function dfsProcessQueries(
Expand All @@ -69,43 +109,9 @@ function dfsProcessQueries(
}
});

if (
depth >= 1 &&
Object.keys(result).some((key) => key.startsWith('@media '))
) {
const mediaKeys = Object.keys(result).filter((key) =>
key.startsWith('@media '),
);

const negations = [];
const accumulatedNegations = [];

for (let i = mediaKeys.length - 1; i > 0; i--) {
// Skip last iteration
const mediaQuery = MediaQuery.parser.parseToEnd(mediaKeys[i]);
negations.push(mediaQuery);
accumulatedNegations.push([...negations]); // Clone array before pushing
}
accumulatedNegations.reverse();
accumulatedNegations.push([]);

for (let i = 0; i < mediaKeys.length; i++) {
const currentKey = mediaKeys[i];
const currentValue = result[currentKey];

const baseMediaQuery = MediaQuery.parser.parseToEnd(currentKey);
const reversedNegations = [...accumulatedNegations[i]].reverse();

const combinedQuery = combineMediaQueryWithNegations(
baseMediaQuery,
reversedNegations,
);

const newMediaKey = combinedQuery.toString();

delete result[currentKey];
result[newMediaKey] = currentValue;
}
if (depth >= 1) {
applyQueryOrder(result, '@media ');
applyQueryOrder(result, '@container ');
}

return result;
Expand Down
21 changes: 13 additions & 8 deletions packages/style-value-parser/src/at-queries/media-query.js
Original file line number Diff line number Diff line change
Expand Up @@ -510,11 +510,14 @@ function mergeAndSimplifyRanges(

export class MediaQuery {
queries: MediaQueryRule;
constructor(queries: MediaQueryRule) {
atRuleName: string;

constructor(queries: MediaQueryRule, atRuleName: string = 'media') {
this.queries = MediaQuery.normalize(queries);
this.atRuleName = atRuleName;
}
toString(): string {
return `@media ${this.#toString(this.queries, true)}`;
return `@${this.atRuleName} ${this.#toString(this.queries, true)}`;
}
#toString(queries: MediaQueryRule, isTopLevel: boolean = false): string {
switch (queries.type) {
Expand Down Expand Up @@ -688,11 +691,13 @@ export class MediaQuery {
),
);

const atRuleNameParser = TokenParser.tokens.AtKeyword.where(
(token: TokenAtKeyword): implies token is TokenAtKeyword =>
token[4].value === 'media' || token[4].value === 'container',
).map((token) => token[4].value);

return TokenParser.sequence(
TokenParser.tokens.AtKeyword.where(
(token: TokenAtKeyword): implies token is TokenAtKeyword =>
token[4].value === 'media',
),
atRuleNameParser,
TokenParser.oneOrMore(
TokenParser.oneOf(leadingNotParser, normalRuleParser),
).separatedBy(
Expand All @@ -702,12 +707,12 @@ export class MediaQuery {
),
)
.separatedBy(TokenParser.tokens.Whitespace)
.map(([_at, querySets]) => {
.map(([atRuleName, querySets]) => {
const rule =
querySets.length > 1
? { type: 'or', rules: querySets }
: querySets[0];
return new MediaQuery(rule as $FlowFixMe as MediaQueryRule);
return new MediaQuery(rule as $FlowFixMe as MediaQueryRule, atRuleName);
});
}
}
Expand Down