Storybook: les histoires uniques tirent-elles les feuilles de style de toutes les autres histoires?

Créé le 21 mars 2017  ·  67Commentaires  ·  Source: storybookjs/storybook

Salut,

J'essaie de comprendre pourquoi j'ai ce problème particulier. Je charge dynamiquement des histoires comme celle-ci:

function loadStories() {
    const req = require.context('../components', true, /story.js$/);
    req.keys().forEach(filename => req(filename));
}
configure(loadStories, module);

et chaque fichier story.js a un fichier sass ou css respectif qui y est importé (dans le but de styles spécifiques à l'histoire qui sont en dehors du composant que story.js importerait, pour afficher:

import './story.sass';

J'ai environ 4 histoires en ce moment et c'est la source de chaque iframe d'histoire ... chargement de chaque feuille de style:

screen shot 2017-03-21 at 9 56 22 am

Est-ce un comportement normal ...?

-

démo

https://github.com/moimikey/729-single-stories-pulling-all-stylesheets

bug todo

Commentaire le plus utile

Mettre en place une racine fantôme autour des histoires résoudrait instantanément ce problème sans les problèmes de performances de @ndelangen

Tous les 67 commentaires

aussi, webpack émet toutes ces histoires, donc je me demande si c'est comme ça que j'ai configuré webpack?

screen shot 2017-03-21 at 11 27 10 am

J'ai également essayé d'utiliser un décorateur, et cela a fini par être à moitié cassé, car j'ai au moins pu isoler la feuille de style supplémentaire de l'histoire, mais au fur et à mesure que j'en parcourais d'autres, ces styles se briseraient à moins que je ne fasse un rafraîchissement dur.

.addDecorator((getStory) => {
  require('./story.sass');
  return getStory();
})

@arunoda @mnmtanish

Intéressant !, avez-vous un repo qui le démontre?

@ndelangen en fera un sous peu

J'essaie toujours de trouver le temps de vérifier cela.

merci; D c'est bizarre. Je l'ai jeté un coup d'œil, mais je ne sais pas par où commencer.

Donc, ce que je pense, c'est que tous les fichiers CSS sont récupérés par le chargeur de style webpack et juste injectés dans la tête. qu'ils soient utilisés ou non.

Mais comment y remédier?

Ce que j'ai fait pour mon CSS, c'est d'utiliser des modules CSS. et importez les noms de classe générés dans mon JS. Même si tout le CSS est injecté ensemble dans la tête, cela n'a pas d'importance, car il est garanti qu'il s'agit de classes / sélecteurs uniques.

Cela ne résout pas vraiment le problème exact que vous rencontrez. Mais je pense que c'est le comportement prévu du chargeur de style.

c'est vrai. moi (et mon entreprise) étudions avec css, mais nous faisons un énorme redéveloppement du code, nous avons donc partagé des styles, donc une combinaison de styleName et className . donc ce qui finit par affecter le livre d'histoires, ce sont ces fichiers css "extérieurs".

J'examinerai à nouveau le code du livre d'histoires ce soir après avoir bu quelques bières et trouverai comment résoudre ce problème, du point de vue de la mise en œuvre.

@moimikey pour votre problème Je suppose que vous devez ignorer le chargeur de style et charger les fichiers css manuellement avec un décorateur. Quelque chose comme ça peut-être:

.addDecorator(getStory => (
  <div>
    <link ... />
    {getStory()}
  </div>
))

Wow ... ça ne m'a jamais traversé l'esprit ... J'essaierai ça.

ick mais là encore, en utilisant sass ... x_x. J'ai même essayé une demande en ligne. mais je n'ai pas eu beaucoup de chance. ce serait une excellente solution pour le CSS directement :)

donc @mnmtanish. merci pour vos conseils. J'ai résolu mon problème avec votre inspiration:

.addDecorator((getStory) => {
  require('./story.sass');
  return getStory();
})

mmm donc le seul problème est que lorsque vous naviguez d'une histoire à l'autre, il empile les styles :(

Peut-être que le chargeur de style peut être configuré pour l'insérer ailleurs que HEAD afin qu'il puisse être supprimé. Je n'ai pas fait cela, donc je ne sais pas si c'est même possible. Vérifiez ces out.

N'est-il pas de la responsabilité de react storybook de charger le composant de manière totalement isolée et donc de ne faire exécuter que JavaScript / CSS lié à l'histoire sélectionnée?

Est-ce lié à https://github.com/storybooks/react-storybook/issues/686?

@ConneXNL oui. bon point. c'est vrai ...; X

@mnmtanish ma prochaine réponse conduit bien sûr à un autre problème, insertAt pourrait être utile, mais il n'est disponible que dans la dernière version de style-loader , ce qui n'est pas possible d'utiliser avec storybook, car il utilise [email protected] . livre de contes utilise toujours 0.x . la dernière version possible que nous pouvons utiliser est [email protected] : (...

Hey @moimikey peut-être pouvez-vous essayer l'approche mentionnée en dernier avec la version alpha?

N'est-il pas préférable / plus sûr de créer un nouvel élément iframe lors du changement d'histoires?

Ce serait sûr et aussi mauvais pour les performances.

Le nouvel iframe devrait analyser le javascript, analyser le CSS, se connecter au canal postmessage, se reconnecter au websocket, et probablement plus de choses.

Peut-être que la suppression des éléments <style> sur story-switch est suffisante pour résoudre ce problème?

Bien sûr, il n'y a pas de styles globaux et tout est correctement espacé, ce ne serait pas un problème. Un vœu pieux, je suppose. 😃

Si quelqu'un peut prouver que les déclarations ci-dessus sont fausses ou peut démontrer qu'il n'y a pas de conséquences négatives, je suis toute oreille!

J'ai fait quelques tests aujourd'hui. Le motif du décorateur a bien fonctionné pour que les styles ne soient injectés qu'une fois que vous passez à l'histoire. Cependant, j'ai eu le même problème où les styles ne sont pas supprimés lors du changement d'histoires.

J'ai joué avec la suppression de styles dans un décorateur, mais il semble que le style requis ne soit appliqué qu'une seule fois. Est-il possible de re-déclencher un require ()? J'ai essayé d'utiliser singleton: false mais cela n'a pas résolu le problème.

J'hésite presque à suggérer cela, mais vous pouvez essayer de casser le cache du pack Web:
https://webpack.github.io/docs/api-in-modules.html#advanced

Ceci est la documentation Webpack 1, mais peut toujours fonctionner.

Idée: vous pourriez écrire un décorateur qui supprime tous les <style>...</style> lors du changement d'histoires. Pour nettoyer les styles non pertinents pour l'histoire en cours.

J'y suis presque. Dans la configuration du webpack que j'utilise
'style-loader/useable' instead of 'style-loader',

Cela ajoute une API avec laquelle vous pouvez travailler. .use () pour ajouter les styles, unuse () pour les supprimer. Dans mon fichier d'histoires, j'utilise le décorateur comme:

.addDecorator((c) => <ReactStylesheet stylesheets={[require('./stories.scss')]}>{ c() }</ReactStylesheet> )

Utilisation du composant React suivant pour ajouter et supprimer les styles.

import * en tant que React depuis 'react';

export class ReactStylesheet extends React.Component{

    componentWillUnmount(){
        let stylesheets = Array.isArray(this.props.stylesheets) ? this.props.stylesheets : [this.props.stylesheets];
        stylesheets.forEach((stylesheet) => {
            console.log("Unmounting....");
            stylesheet.unuse();
        });

    }

    componentDidMount(){
         let stylesheets = Array.isArray(this.props.stylesheets) ? this.props.stylesheets : [this.props.stylesheets];
        stylesheets.forEach((stylesheet) => {
            console.log("Mounting....");
            stylesheet.use();
        });
    }

    render(){
        return this.props.children;
    }
}

Changer correctement la feuille de style recharge à chaud les styles. Le passage à une autre histoire unuse () est appelé et les feuilles de style sont nettoyées. Cependant, la méthode s'interrompt lors de la réajout des styles car elle annonce la version mise à jour non hrm de la feuille de style. Faire tout changement après cela génère également cette erreur:

Uncaught (in promise) TypeError: Cannot read property 'refs' of undefined
    at update (webpack:///./~/style-loader/addStyles.js?:63:4)
    at eval (webpack:///./src/Component/stories.scss?:32:4)
    at Object.hotApply [as apply] (http://dev.test:6006/static/preview.bundle.js:499:14)
    at cb (webpack:///(webpack)-hot-middleware/process-update.js?:52:36)
    at eval (webpack:///(webpack)-hot-middleware/process-update.js?:68:13)
    at <anonymous>

Je ne sais pas comment mettre à jour l'instruction require pour qu'elle pointe vers la dernière version de HRM.

Fantastique travail d'enquête! J'ai cherché quelque chose comme ça avant et je n'ai pas pu le trouver.

Que pouvons-nous faire de ce côté pour aider @ConneXNL ?

les solutions se rapprochent. l'idée de stylesheet.use() et unuse m'était étrangère, mais cela semble être sur la bonne voie.

c'est aussi une autre chose intéressante pour le livre d'histoires de sandbox https://github.com/Wildhoney/ReactShadow

Salut à tous! On dirait qu'il n'y a pas eu grand-chose dans ce numéro ces derniers temps. S'il y a encore des questions, des commentaires ou des bugs, n'hésitez pas à poursuivre la discussion. Malheureusement, nous n'avons pas le temps d'aborder tous les problèmes. Nous sommes toujours ouverts aux contributions, veuillez donc nous envoyer une demande de tirage si vous souhaitez aider. Les problèmes inactifs seront résolus après 60 jours. Merci!

@ConneXNL pour terminer ce numéro, pensez-vous que vous pourriez nous aider à améliorer la documentation à cet égard?

Si vous ne pouvez pas trouver un bon endroit pour cela, placez-le simplement quelque part au format démarqué. Je vais m'occuper de le placer.

🙇

Salut à tous! On dirait qu'il n'y a pas eu grand-chose dans ce numéro ces derniers temps. S'il y a encore des questions, des commentaires ou des bugs, n'hésitez pas à poursuivre la discussion. Malheureusement, nous n'avons pas le temps d'aborder tous les problèmes. Nous sommes toujours ouverts aux contributions, veuillez donc nous envoyer une demande de tirage si vous souhaitez aider. Les problèmes inactifs seront résolus après 60 jours. Merci!

Hé là, c'est encore moi! Je vais clore ce numéro pour aider nos responsables à se concentrer sur la feuille de route de développement actuelle. Si le problème mentionné est toujours un problème, veuillez ouvrir un nouveau ticket et mentionner l'ancien. Bravo et merci d'utiliser Storybook!

Même problème ici, le livre de contes n'isole pas chaque histoire, ce qui le rend inutilisable pour tout test visuel / test d'acceptation.

Mettre en place une racine fantôme autour des histoires résoudrait instantanément ce problème sans les problèmes de performances de @ndelangen

@bennypowers intéressant! auriez-vous un exemple de code sur la façon d'y parvenir? 🙇

Cela pourrait aussi être intéressant pour

Salut. Je rencontre également ce problème
Cela a-t-il été résolu ou existe-t-il une solution de contournement?

@ndelangen

Le chemin le plus rapide à la satisfaction ici est sans doute la suggestion de @moimikey à l' utilisation ReactShadow

La stratégie à adopter peut être d'envelopper la racine dans un composant ReactShadow, puis d'apporter les styles avec adoptedStyleSheets (ou un élément <style> pour les navigateurs non compatibles)

https://github.com/storybookjs/storybook/blob/ba74d889fcfd87849a6ae9369f5e4176e8149d33/lib/core/src/client/preview/start.js#L253

Veuillez le rouvrir, ce problème rend extrêmement difficile l'ajout de styles personnalisés par histoire. J'ai des histoires MDX totalement séparées qui implémentent un style personnalisé pour leurs exemples, et l'inclusion globale de tous les styles de chaque histoire rend ce cas d'utilisation intenable.

edit: Merci !!!

J'espère que cela sera résolu d'ici le 21 mars 2020.

@moimikey Est-ce que ça vous intéresse? Meilleur moyen de s'assurer que c'est fait à une certaine date ... 😉

IMO, nous devrions ajouter des paramètres ou un addon pour gérer cette fonctionnalité au lieu d'ajouter des fonctionnalités spéciales uniquement pour les feuilles de style. Cela entraînera un comportement incohérent entre les feuilles de style et les scripts. Mais peut-être que c'est le bon moment pour réfléchir à ce que devrait être «l'isolement»?

J'ai écrit un PoC rapide pour l'approche addon, pour qui se demande que c'est possible.
https://github.com/pocka/storybook-addon-css

@pocka Vous êtes

yazzzzz. bravo @pocka

Il convient de noter @pocka ne fonctionne pas si vous utilisez des histoires MDX à cause de mdx-js / mdx # 894.

Edit: Mon mal, c'est définitivement le cas! Vous devez avoir style-loader 1.x +, puis faire quelque chose comme ceci:

--- a/components/grid/GridChild.stories.mdx
+++ b/components/grid/GridChild.stories.mdx
@@ -1,7 +1,9 @@
 import { Meta, Story, Preview } from '@storybook/addon-docs/blocks';
 import { GridContainer, GridRow, GridChild } from './';
 import '../../shared/critical-path.scss';
-import 'o-grid/demos/src/scss/_demos.scss';
+import demoStylesModule from '!!style-loader?injectType=lazyStyleTag!css-loader!sass-loader!o-grid/demos/src/scss/_demos.scss?story';
+
+export const demoStyles = Promise.resolve(demoStylesModule);

 <Meta title="Core|Grid/GridChild" component={GridChild} />

@@ -37,7 +39,12 @@ You supply it a `colspan` prop in one of the following formats:
     ```

 <Preview>
-  <Story name="Default unresponsive columns">
+  <Story
+    name="Default unresponsive columns"
+    parameters={{
+      styles: [demoStyles],
+    }}
+  >
     <GridContainer>
       <GridRow>
         <GridChild colspan="1">

@aendrew
Merci d'avoir signalé cela, j'ai complètement oublié MDX: no_mouth:
J'ai mis à jour PoC et ajouté des exemples MDX .

Avec l'approche addon (par styles d'histoire), contrairement à l'approche de portée de fichier (par styles de fichier), l'onglet Docs obtiendra toutes les feuilles de style de chaque histoire. Dans mon exemple , "foo" et "baz" importent foo.css et "bar" importent bar.css , puis l'onglet Docs obtient à la fois foo.css et bar.css . Je pense que c'est inévitable et je ne sais pas que c'est acceptable ou non.

@pocka Je pense que cette approche pourrait bien fonctionner avec https://github.com/storybookjs/storybook/tree/next/addons/cssresources WDYT?

@ndelangen
Ah, c'est vrai!

foo.story = {
  parameters: {
    cssresources: [
      {
        id: 'foo',
        code: `<style>${require('!to-string-loader!css-loader!./foo.css')}</style>`,
        picked: true
      }
    ]
  }
}

Un souci: si un utilisateur importe de très grandes feuilles de style, l'onglet "Ressources CSS" sera désordonné.

@pocka à droite, je pense que l'addon cssresources peut être considérablement amélioré dans ce département. Avez-vous envie de faire ça?

@ndelangen
Oui: smiley:

Au fait, que pensez-vous de permettre aux utilisateurs d'écrire raw-webpack-query? (comme !to-string-loader!... ) Je pense que nous avons besoin de beaucoup de magie noire dans le code de l'addon si nous voulons nous débarrasser de cela ...

Je pense que nous prenons en charge les macros babel par défaut, donc les utilisateurs pourraient également utiliser macro-preval pour injecter le contenu des fichiers dans le bundle?

Je ne le savais pas, je vais y jeter un œil bientôt!

Salut, je rencontre le même problème.

Pour toute personne confrontée au problème, essayez cette solution de contournement . (Il faut cependant une petite connaissance approfondie du webpack).


@ndelangen J'ai to-string-loader (ou file-loader ) afin que l'utilisateur puisse importer un fichier CSS puis l'utiliser pour l'addon.

// adding a rule like this
{
  test: /\.css$/,
  resourceQuery: /cssresources/,
  use: ['to-string-loader', 'css-loader', 'postcss-loader']
}

Pour les pré-processeurs, une option pour personnaliser test et use est également nécessaire. Il est possible de choisir une règle pour le fichier de style puis de la modifier avec oneOf mais ce serait tellement compliqué ...

Qu'est-ce que tu penses?

@pocka ouais cela ressemble à un concept intéressant!

Salut, vous vous demandez si cela est toujours en cours d'élaboration? Je rencontre également le problème, je suis au courant du contournement, mais j'aimerais savoir si un correctif sera bientôt disponible.

Salut, pouvez-vous fournir un exemple de la solution de contournement avec une histoire Vue.?

Pour autant que je sache, la solution de contournement entraîne toujours l'empilement des feuilles de style après la vue initiale, du moins si vous utilisez le module complémentaire Docs. 😕

Sans manquer de respect, je trouve que l'approche addon de import './Button.css' intérieur de Button.jsx qui n'est utilisé que dans les fichiers d'histoire où Button.jsx est importé. Le style par histoire, de la manière fournie par Button (directement ou indirectement) n'est pas affecté par toutes les règles CSS de Button.css . (Le problème ici est de s'assurer que OtherWidget.css , disons, ne manque pas de règles qui se sont terminées par inadvertance dans Button.css place - peut-être qu'elles ont été négligées dans une refactorisation ou quelque chose du genre - et manquantes parce que les histoires pour OtherWidget obtiennent tous les CSS importés statiquement de l'application entière, donc OtherWidget a toujours l'air bien dans Storybook.)

Ce que je pense que je ferais à la place, c'est de changer tous les chargeurs CSS pour qu'ils injectent avec lazyStyleTag , puis d'utiliser l'API webpack pour générer un nouveau module qui regroupe les modules CSS par les fichiers d'histoire qui les nécessitent finalement et écoute le changement d'histoire événements pour activer et désactiver les modules appropriés.
Cette approche a-t-elle déjà été envisagée et rejetée, ou y a-t-il des problèmes avec elle que vous voyez maintenant? Je pense que je peux tout faire dans un addon Storybook, mais il pourrait être plus propre s'il est intégré dans Storybook en tant que fonctionnalité principale.

Si vous voulez une encapsulation de style fort, les navigateurs sont livrés avec. Au risque d'exposer ma propre ignorance ici, je ne sais toujours pas pourquoi tout ce JavaScript utilisateur (avec ses coûts de complexité associés) est nécessaire pour accomplir quelque chose qui est intégré et prêt à l'emploi.

Pourquoi ne pas simplement rendre le DOM de chaque histoire en une racine fantôme avec quelque chose comme ça

customElements.define('encapsulated-story', class EncapsulatedStory extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
  }

  /* not sure why we'd need this getter, but let's say */
  get storyHTML() {
    return this.shadowRoot.innerHTML;
  }

  set storyHTML(string) {
    this.shadowRoot.innerHTML = storyHTML;
  }
});

et chaque fois que l'histoire change

encapsulatedStory.storyHTML = theStoryDOMStringWithAllStyleTags;

Et.. Voila? Ici, theStoryDOMStringWithAllStyleTags est juste la concaténation du HTML d'une histoire avec tous les styles associés concaténés comme des balises en ligne <style> . L'histoire pourrait styliser l'élément hôte avec le sélecteur :host , comme d'habitude.

C'est un point de départ minimal, qui pourrait être construit plus tard peut-être avec du code de bibliothèque, mais au moins, il atteindra l'objectif d'une encapsulation de style fort sans avoir besoin de toutes ces nouvelles API proposées.

Mais comment cela fonctionne-t-il avec Webpack? Webpack regroupe tout dans un bundle JavaScript, pas une chaîne DOM; et la façon dont webpack est actuellement configuré, il regroupe toutes les histoires dans un seul paquet. Je ne pense pas qu'une racine d'ombre aide lorsque le JavaScript en cours de (re) chargement insère des styles directement dans la tête du document. Vous devez faire une configuration poilue de Webpack d'une manière ou d'une autre pour changer cela.

Utiliser le DOM shadow pour isoler complètement chaque histoire signifierait également répliquer les balises de style partagées par de nombreuses histoires dans chacune; l'utilisation d'un style partagé fourni par webpack sera plus efficace. Peut-être pas assez pour faire une énorme différence, mais peut-être assez pour compenser les avantages, s'il y en a, de l'utilisation du shadow DOM au lieu d'utiliser lazyStyleTag (ce qui, je pense, est le seul élément de complexité que les racines d'ombre vous sauve).

Je suis également intéressé de voir cela corrigé tôt ou tard.

Ce serait sûr et aussi mauvais pour les performances.

Le nouvel iframe devrait analyser le javascript, analyser le CSS, se connecter au canal postmessage, se reconnecter au websocket, et probablement plus de choses.

@ndelangen désolé de vous citer 2017, mais est-ce toujours votre point de vue, que recharger une iframe est trop cher? Les navigateurs font ce type de chose en permanence; ils sont probablement très optimisés pour cela. Dans ce cas, ce serait encore plus rapide qu'un chargement de page normal, car aucune requête réseau n'est impliquée.

Pour moi, l'avantage l'emporte sur le coût, car je préférerais de loin une nouvelle iframe pour chaque histoire. Je tolérerais un retard de 600 ms pour un tel luxe.

(Mon cas d'utilisation est que j'essaie de rendre certains composants angularjs hérités dans un livre de contes, et disons simplement que les composants ne sont pas très purs. Ils sont très avec état; ils ont des effets secondaires et utilisent les services angularjs qui sont également très avec état. Les choses se cassent de manière inattendue.)

Une idée pour une surface API est de configurer le livre d'histoires en .storybook/manager.js .

addons.setConfig({
  refreshBetweenStories: true,
})

Cela pourrait être considéré comme un paramètre d'interface utilisateur si vous inclinez la tête dans le bon sens.

Il n'y aurait aucun coût d'exécution pour les personnes qui n'activent pas cet indicateur, et pour ceux qui l'activent, ils en ont vraiment besoin, donc un tel retard serait tolérable.

Si vous voulez que cela soit corrigé, veuillez voter pour en ajoutant un 👍 à la description du problème. Nous utilisons cela pour aider à établir des priorités!

.addDecorator ((getStory) => {
require ('./ story.sass');
return getStory ();
})

SALUT!!!!
où puis-je mettre celui-ci? !!!

Cette page vous a été utile?
0 / 5 - 0 notes