Я хочу писать разные истории, по одной для каждого запроса @media
в моем CSS.
Например, я скрываю определенные элементы для @media print
. Я хотел бы добиться чего-то вроде этого:
export const PrintFoo: Story = {
render: (args) => (
<MediaWrapper media = "print">
<Foo .../>
</List>
),
};
Это должна быть обычная задача в Storybook (для тестирования экранов разных размеров), но я не смог найти документации, как это сделать.
Я нашел JavaScript, который изменяет таблицу стилей (заменяет print
на screen
: https://github.com/RRMoelker/print-css-toggle/blob/master/src/index.js), но для этого мне понадобится «крючок пост-рендеринга, но перед сравнением», где я могу назвать это из истории.
Или мне нужно написать для этого аддон?
🤔 А знаете ли вы, что...
CSS поддерживает медиа-запросы, что позволяет разрабатывать стили для разных устройств и разрешений экранов.
Вот элемент-оболочка (React с TypeScript), который может имитировать режим печати. Оболочка собирает все медиа-правила таблиц стилей CSS, прикрепленных к документу. Если вы переключитесь на @media print
, все правила @media screen
изменятся на @media disabled
(фактически сделав их бесполезными) и заменят print
на screen
во всех @media print
правилах, сделав их стандартными.
import { ReactElement, ReactNode } from "react";
type MediaWrapperProps = {
children: ReactNode;
media: string;
};
export const MediaWrapper = ({ children, media }: MediaWrapperProps): ReactElement => {
const result = <>{children}</>;
console.info("MediaWrapper", media, result);
const mediaRules = collectRules();
if (media === "print") {
console.info("MediaWrapper: Simulating print mode");
replaceInAllRules(mediaRules.screen, "disabled");
replaceInAllRules(mediaRules.print, "screen");
console.info(mediaRules);
} else if (media === "screen") {
console.info("MediaWrapper: Simulating screen mode");
replaceInAllRules(mediaRules.screen, "screen");
replaceInAllRules(mediaRules.print, "print");
console.info(mediaRules);
}
return result;
};
type MediaRules = {
print: CSSMediaRuleWithConditionText[];
screen: CSSMediaRuleWithConditionText[];
};
const replaceInAllRules = (rules: CSSMediaRuleWithConditionText[], replacement: string): void => {
rules.forEach((rule) => {
rule.media.mediaText = replacement;
});
};
// node_modules/typescript/lib/lib.dom.d.ts doesn't have all the properties of the browser's CSSMediaRule
type CSSMediaRuleWithConditionText = CSSMediaRule & {
conditionText: string;
};
const collectRules = (): MediaRules => {
const styleSheets = document.styleSheets;
const printRules = [];
const screenRules = [];
const disabledRules = [];
for (const sheet of styleSheets) {
const rules = sheet.cssRules || sheet.rules; // IE <= 8 use "rules" property
for (const rule of rules) {
if (rule.constructor.name === "CSSMediaRule") {
const cast = rule as CSSMediaRuleWithConditionText;
const condition = cast.conditionText;
if (condition.includes("screen")) {
screenRules.push(cast);
} else if (condition.includes("print")) {
printRules.push(cast);
} else if (cast.media.mediaText === "disabled") {
disabledRules.push(cast);
}
}
}
}
if (disabledRules.length) {
// Storybook doesn't always reset the CSS stylesheets in the iframe.
// if this happened, then there will be "disabled" rules from the last "print mode" story.
// Return the disabled rules as "screen" and "screen" rules as "print".
return {
print: screenRules,
screen: disabledRules,
};
}
return {
print: printRules,
screen: screenRules,
};
};
Если у вас есть компонент, который использует @media print
, как этот:
@media print {
.printMe {
display: block;
background-color: white;
}
.hideMe {
display: none;
}
}
@media screen {
.printMe {
background-color: blue;
}
.hideMe {
background-color: green;
}
}
export const PrintTestComponent = (): ReactElement => {
return (
<div>
<div className = "printMe">Print Mode</div>
<div className = "hideMe">Screen Mode</div>
</div>
);
};
то вы можете проверить это в двух историях:
import { MediaWrapper } from "./MediaWrapper";
import { PrintTestComponent } from "./PrintTestComponent";
import { ReactElement } from "react";
type StoryProps = {
media: "screen" | "print";
};
export default {
title: "Print Test",
component: (args: StoryProps): ReactElement => (
<MediaWrapper media = {args.media}>
<PrintTestComponent />
</MediaWrapper>
),
parameters: {
media: "print",
},
};
export const Screen = {
storyName: "Screen Mode",
args: {
media: "screen",
},
};
export const Print = {
storyName: "Print Mode",
args: {
media: "print",
},
};