Storybook: historias individuales están incorporando las hojas de estilo de todas las demás historias?

Creado en 21 mar. 2017  ·  67Comentarios  ·  Fuente: storybookjs/storybook

Hola,

Estoy tratando de averiguar por qué tengo este problema en particular. Estoy cargando dinámicamente historias como esta:

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

y cada archivo story.js tiene un archivo sass o css respectivo que se importa en él (con el propósito de estilos específicos de historia que están fuera del componente que story.js importaría, para mostrar:

import './story.sass';

Tengo alrededor de 4 historias en este momento y esta es la fuente de cada iframe de la historia ... cargando cada hoja de estilo:

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

¿Es este comportamiento normal ...?

-

manifestación

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

bug todo

Comentario más útil

Poner una raíz en la sombra alrededor de las historias resolvería instantáneamente este problema sin las preocupaciones de rendimiento de @ndelangen

Todos 67 comentarios

Además, el paquete web emite todas esas historias, así que me pregunto si así es como configuré el paquete web.

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

Traté de usar un decorador también, y esto terminó medio roto, ya que pude al menos aislar la hoja de estilo adicional a la historia, pero mientras recorría otros, esos estilos se romperían a menos que hiciera una actualización completa.

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

@arunoda @mnmtanish

¡Interesante !, ¿tienes un repositorio que demuestre esto?

@ndelangen hará uno en breve

Sigo intentando encontrar tiempo para comprobar esto ...

gracias; D es raro. Le he echado un vistazo, pero no estoy seguro de por dónde empezar.

Entonces, lo que creo que están sucediendo es que todos los archivos CSS son recogidos por el cargador de estilo de paquete web y simplemente se inyectan en la cabeza. si se utilizan o no.

¿Pero cómo arreglarlo?

Lo que he hecho por mi CSS es usar módulos CSS. e importar los nombres de clase generados en mi JS. Incluso si todos los CSS se inyectan juntos en la cabeza, no importa, porque se garantiza que serán clases / selectores únicos.

Realmente no resuelve el problema exacto que estás teniendo. Pero creo que este es el comportamiento previsto del cargador de estilos.

Esto es verdad. yo (y mi empresa) estamos evaluando el alcance con CSS, pero estamos haciendo una gran remodelación de código, por lo que hemos compartido estilos, por lo tanto, una combinación de styleName y className . así que lo que termina afectando al libro de cuentos son esos archivos CSS "externos".

Examinaré el código del libro de cuentos nuevamente esta noche después de tomar algunas cervezas y descubrir cómo quizás resolver esto, en cuanto a la implementación.

@moimikey para su problema, supongo que debe omitir el cargador de estilos y cargar los archivos css manualmente con un decorador. Algo como esto quizás:

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

Vaya ... eso nunca se me pasó por la cabeza ... Lo intentaré.

ick pero de nuevo, usando sass ... x_x. Incluso probé un requisito en línea. pero no tuve mucha suerte. Sin embargo, esa sería una excelente solución para CSS directo :)

así que @mnmtanish. gracias por su orientación. He resuelto mi problema con tu inspiración:

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

mmm, entonces el único problema ahora es que cuando navegas de una historia a otra, se apilan los estilos :(

Tal vez el cargador de estilos se pueda configurar para insertarlo en otro lugar que no sea HEAD para poder quitarlo. No he hecho esto, así que no estoy seguro de si es posible. Mira estos .

¿No es responsabilidad de React Storybook cargar el componente en completo aislamiento y, por lo tanto, solo ejecutar JavaScript / CSS que esté relacionado con la historia seleccionada actualmente?

¿Está relacionado con https://github.com/storybooks/react-storybook/issues/686?

@ConneXNL sí. buen punto. esto es cierto ...; X

@mnmtanish mi próxima respuesta, por supuesto, lleva a otro problema, insertAt podría ser útil, pero solo está disponible en la última versión de style-loader , que no es posible usar con el libro de cuentos, porque usa [email protected] . Storybook todavía usa 0.x . la última versión posible que podemos usar es [email protected] : (...

Hola @moimikey, ¿ quizás puedas probar el enfoque que se mencionó en último lugar con la versión alfa?

¿No es mejor / más seguro crear un nuevo elemento iframe al cambiar historias?

Eso sería seguro y también malo para el rendimiento.

El nuevo iframe tendría que analizar el javascript, analizar el CSS, conectarse al canal de mensaje posterior, volver a conectarse al websocket y probablemente más cosas.

¿Quizás eliminar los elementos <style> en story-switch sea suficiente para resolver este problema?

Por supuesto, no hay estilos globales y todo tiene el espacio de nombres adecuado, esto no sería un problema. Pensamientos ilusorios, supongo. 😃

Si alguien puede probar que las declaraciones anteriores son falsas o puede demostrar que no hay consecuencias negativas, ¡soy todo oídos!

Hice algunas pruebas hoy. El patrón del decorador funcionó bien, de modo que los estilos solo se inyectan una vez que se cambia a la historia. Sin embargo, tuve el mismo problema en el que los estilos no se eliminan al cambiar de historia.

Jugué con la eliminación de estilos en un decorador, pero parece que el estilo requerido solo se aplica una vez. ¿Es posible volver a activar un require ()? Intenté usar singleton: false pero eso no solucionó el problema.

Casi dudo en sugerir esto, pero podrías intentar romper la caché del paquete web:
https://webpack.github.io/docs/api-in-modules.html#advanced

Estos son los documentos del paquete web 1, pero aún pueden funcionar.

Idea: podrías escribir un decorador que elimine todos los <style>...</style> al cambiar de historia. Limpiar los estilos que no son relevantes para la historia actual.

Ya casi estoy allí. En la configuración del paquete web que uso
'style-loader/useable' instead of 'style-loader',

Esto agrega una API con la que trabajar. .use () para agregar los estilos, unuse () para eliminarlos. En mi archivo de historias utilizo el decorador como:

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

Usando el siguiente componente React para agregar y eliminar los estilos.

importar * como Reaccionar desde 'reaccionar';

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;
    }
}

Cambiar la hoja de estilo correctamente recarga los estilos. Al cambiar a una historia diferente, se llama a unuse () y se limpian las hojas de estilo. Sin embargo, el método se rompe al volver a agregar los estilos, ya que anuncia la versión no actualizada de la hoja de estilo. Hacer cualquier cambio después de eso también arroja este error:

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>

No estoy seguro de cómo actualizar la declaración obligatoria para que apunte a la última versión de HRM.

¡Fantástico trabajo de investigación! Busqué algo como esto antes y no pude encontrarlo.

¿ Hay algo que podamos hacer de este lado para ayudar a

las soluciones se acercan. la idea de stylesheet.use() y unuse era ajena, pero parece que va por buen camino.

esto también es otra cosa interesante para el libro de cuentos de sandboxing https://github.com/Wildhoney/ReactShadow

¡Hola a todos! Parece que últimamente no ha habido mucho en este tema. Si todavía hay preguntas, comentarios o errores, no dude en continuar con la discusión. Desafortunadamente, no tenemos tiempo para abordar todos los problemas. Siempre estamos abiertos a contribuciones, así que envíenos una solicitud de extracción si desea ayudar. Los problemas inactivos se cerrarán después de 60 días. ¡Gracias!

@ConneXNL para finalizar este número, ¿cree que podría ayudarnos a mejorar los documentos en este sentido?

Si no puede encontrar un buen lugar para ello, simplemente póngalo en algún lugar en formato de rebajas. Yo me encargaré de colocarlo.

🙇

¡Hola a todos! Parece que últimamente no ha habido mucho en este tema. Si todavía hay preguntas, comentarios o errores, no dude en continuar con la discusión. Desafortunadamente, no tenemos tiempo para abordar todos los problemas. Siempre estamos abiertos a contribuciones, así que envíenos una solicitud de extracción si desea ayudar. Los problemas inactivos se cerrarán después de 60 días. ¡Gracias!

¡Hola, soy yo de nuevo! Voy a cerrar este problema para ayudar a nuestros mantenedores a centrarse en la hoja de ruta de desarrollo actual. Si el problema mencionado sigue siendo motivo de preocupación, abra un nuevo ticket y mencione el anterior. ¡Saludos y gracias por usar Storybook!

El mismo problema aquí, el libro de cuentos no aísla cada historia, lo que la hace inutilizable para cualquier prueba visual / prueba de aceptación.

Poner una raíz en la sombra alrededor de las historias resolvería instantáneamente este problema sin las preocupaciones de rendimiento de @ndelangen

@bennypowers interesante! ¿tendría una muestra de código sobre cómo lograrlo? 🙇

También podría ser interesante para

Hola. También estoy experimentando este problema
¿Se solucionó esto o hay alguna solución?

@ndelangen

El camino más rápido a la satisfacción aquí es probablemente @moimikey 's sugerencia de utilizar ReactShadow

La estrategia a seguir podría ser envolver la raíz en un componente ReactShadow, luego traer los estilos con adoptedStyleSheets (o un elemento <style> para navegadores no compatibles)

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

Vuelva a abrir esto, este problema hace que agregar estilos personalizados por historia sea extremadamente difícil. Tengo historias MDX totalmente separadas que implementan estilos personalizados para sus ejemplos, y la inclusión global de todos los estilos de cada historia hace que este caso de uso sea insostenible.

editar: ¡¡¡Gracias !!!

Espero que esto se resuelva antes del 21 de marzo de 2020.

@moimikey ¿ Tienes interés en asumir esto? La mejor manera de asegurarse de que esté listo para una fecha determinada ... 😉

En mi opinión, deberíamos agregar parámetros o complementos para manejar esta función en lugar de agregar una funcionalidad especial solo para hojas de estilo. Causará un comportamiento inconsistente entre hojas de estilo y scripts. Pero tal vez sea un buen momento para considerar cómo debería ser el "aislamiento".

Escribí un PoC rápido para el enfoque de complemento, para quienes se preguntan si es posible.
https://github.com/pocka/storybook-addon-css

@pocka ¡ Eres IMPRESIONANTE! 💯

yazzzzz. salud @pocka

Vale la pena señalar que la solución de @pocka no funciona si está usando historias MDX debido a mdx-js / mdx # 894.

Editar: ¡Dios mío, definitivamente lo hace! Necesita tener style-loader 1.x + y luego hacer algo como esto:

--- 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
Gracias por señalar eso, me olvidé por completo de MDX: no_mouth:
Actualicé PoC y agregué ejemplos de MDX .

Con el enfoque de complementos (estilos por historia), en contraste con el enfoque de alcance de archivo (estilos por archivo), la pestaña Documentos obtendrá todas las hojas de estilo de cada historia. En mi ejemplo , la historia "foo" y "baz" importando foo.css y la historia "bar" importando bar.css , luego la pestaña Docs obtiene tanto foo.css como bar.css . Creo que esto es inevitable y no sé si es aceptable o no.

@pocka Creo que ese enfoque podría funcionar bien con https://github.com/storybookjs/storybook/tree/next/addons/cssresources WDYT?

@ndelangen
¡Ah, es cierto!

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

Una preocupación: si un usuario importa hojas de estilo muy grandes, la pestaña "Recursos CSS" se verá desordenada.

@pocka correcto, creo que el complemento cssresources se puede mejorar enormemente en ese departamento. ¿Tienes ganas de asumir eso?

@ndelangen
Sí: smiley:

Por cierto, ¿qué opinas sobre permitir que los usuarios escriban consultas de paquete web sin formato? (como !to-string-loader!... ) Creo que necesitamos mucha magia negra en el código del complemento si queremos deshacernos de esto ...

Creo que admitimos babel-macros de forma predeterminada, por lo que los usuarios también podrían estar usando macro-preval para inyectar contenido de archivos en el paquete.

No sabía eso, ¡lo veré pronto!

Hola, estoy experimentando el mismo problema.

Para cualquiera que tenga este problema, pruebe esta solución . (Sin embargo, necesita un poco de conocimiento profundo del paquete web).


@ndelangen Eché un vistazo a la macro pero lo que habilita es "cargar archivos CSS desde el sistema de archivos", ¿verdad? Creo que muchos usuarios quieren importar archivos SASS, Less, Stylus, CSS con PostCSS, etc., para que ese enfoque no satisfaga las necesidades. Mi idea actual es que el complemento CSSResource agregue una regla que importe el archivo CSS con to-string-loader (o file-loader ) para que el usuario pueda importar un archivo CSS y luego usarlo para el complemento.

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

Para los preprocesadores, también se necesita una opción para personalizar test y use . Es posible elegir una regla para el archivo de estilo y luego modificarla con oneOf pero sería muy complicado ...

¿Qué piensas?

@pocka, sí, ¡suena como un concepto interesante!

Hola, ¿me pregunto si todavía se está trabajando en esto? También estoy experimentando el problema, estoy al tanto de la solución pero me gustaría saber si habrá una solución disponible pronto.

Hola, ¿puedes dar un ejemplo de la solución alternativa con Vue Story?

Por lo que puedo decir, la solución aún da como resultado que las hojas de estilo se apilen después de la vista inicial, al menos si está utilizando el complemento Docs. 😕

Sin faltarle el respeto, me parece que el enfoque de complemento de @pocka es bastante deficiente, ya que no aísla los estilos que se han importado en archivos de componentes en lugar de en los archivos de historia, que creo que es el patrón más común. Mi deseo personal, sospecho que esto puede ser compartido por otros en este hilo, es poder tener un import './Button.css' dentro de Button.jsx que solo se use en archivos de historia donde Button.jsx es importado. El estilo por historia, de la manera que @pocka ha proporcionado, no es tan importante para mí como asegurarme de que ningún componente que no importe Button (directa o indirectamente) no se vea afectado por cualquier regla CSS de Button.css . (La preocupación aquí es querer asegurarse de que a OtherWidget.css , digamos, no le falten algunas reglas que inadvertidamente terminaron en Button.css , tal vez se pasaron por alto en una refactorización o algo así, y faltan porque las historias para OtherWidget obtienen todo el CSS importado estáticamente de toda la aplicación, por lo que OtherWidget todavía se ve bien en Storybook).

Lo que creo que haría en su lugar es cambiar todos los cargadores de CSS para inyectar con lazyStyleTag , y luego usar la API de paquete web para generar un nuevo módulo que agrupa los módulos CSS por los archivos de historia que finalmente los requieren y escucha el cambio de historia eventos para encender y apagar los módulos apropiados.
¿Este enfoque ya ha sido considerado y descartado, o hay algún problema con él que ve ahora? Creo que puedo hacerlo todo en un complemento de Storybook, pero podría ser más limpio si se integra en Storybook como una característica principal.

Si desea un encapsulado de estilo sólido, los navegadores lo incluyen. A riesgo de exponer mi propia ignorancia aquí, todavía no estoy seguro de por qué todo este JavaScript de usuario (con sus costos de complejidad asociados) es necesario para lograr algo que está integrado y listo para funcionar.

¿Por qué no convertir el DOM de cada historia en una raíz de sombra con algo como esto?

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;
  }
});

y cada vez que cambia la historia

encapsulatedStory.storyHTML = theStoryDOMStringWithAllStyleTags;

¿y hecho? Aquí, theStoryDOMStringWithAllStyleTags es solo la concatenación del HTML de una historia con todos los estilos asociados concatenadas como etiquetas <style> . Story podría diseñar el elemento anfitrión con el selector :host , como es habitual.

Ese es un punto de partida mínimo, que podría construirse más adelante quizás con algún código de biblioteca, pero al menos logrará el objetivo de un encapsulado de estilo sólido sin la necesidad de todas estas nuevas API propuestas.

Sin embargo, ¿cómo funciona eso con webpack? Webpack empaqueta todo en un paquete de JavaScript, no en una cadena DOM; y la forma en que el paquete web está configurado actualmente, empaqueta todas las historias en un solo paquete. No creo que una raíz de sombra ayude cuando el JavaScript que se está cargando (hot-re) inserta estilos directamente en el encabezado del documento. Necesita hacer una configuración peluda del paquete web de una forma u otra para cambiar eso.

Usar el DOM en la sombra para aislar completamente cada historia también significaría replicar las etiquetas de estilo compartidas por muchas historias en cada una; el uso de un estilo compartido incluido en el paquete web será más eficiente. Tal vez no sea suficiente para hacer una gran diferencia, pero posiblemente lo suficiente para compensar los beneficios, si los hay, de usar el DOM de sombra en lugar de usar lazyStyleTag (que creo que es la única pieza de complejidad que las raíces de sombra salvarte).

También estoy interesado en que esto se solucione tarde o temprano.

Eso sería seguro y también malo para el rendimiento.

El nuevo iframe tendría que analizar el javascript, analizar el CSS, conectarse al canal de mensaje posterior, volver a conectarse al websocket y probablemente más cosas.

@ndelangen siento citarlo de 2017, pero ¿sigue siendo esta su perspectiva, que recargar un iframe es demasiado costoso? Los navegadores hacen este tipo de cosas constantemente; probablemente estén muy optimizados para ello. En este caso, sería incluso más rápido que una carga de página normal, ya que no hay solicitudes de red involucradas.

Para mí, el beneficio supera el costo, porque preferiría un iframe nuevo para cada historia. Toleraría un retraso de hasta 600 ms por tal lujo.

(Mi caso de uso es que estoy tratando de renderizar algunos componentes de angularjs heredados en un libro de cuentos, y digamos que los componentes no son muy puros. Son muy con estado; tienen efectos secundarios y hacen uso de los servicios de angularjs que son también tiene mucho estado. Las cosas se rompen de formas inesperadas).

Una idea para una superficie API es configurar el libro de cuentos en .storybook/manager.js .

addons.setConfig({
  refreshBetweenStories: true,
})

Esto podría considerarse una configuración de IU si inclina la cabeza de la manera correcta.

No habría ningún costo de tiempo de ejecución para las personas que no habiliten esta bandera, y para aquellos que la habiliten, realmente la necesitan, por lo que cualquier retraso de este tipo sería tolerable.

Si quieres que esto se solucione, vota añadiendo un 👍 a la descripción del problema. ¡Usamos esto para ayudar a priorizar!

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

¡¡¡¡HOLA!!!!
donde puedo poner este? !!!

¿Fue útil esta página
0 / 5 - 0 calificaciones