Skip to content
This repository was archived by the owner on Jun 3, 2024. It is now read-only.

Load CSS in webpack with style-loader and css-loader #330

Merged
merged 1 commit into from
Nov 8, 2018

Conversation

valentijnnieman
Copy link
Contributor

@valentijnnieman valentijnnieman commented Oct 15, 2018

@chriddyp Here's a little test to see what happens in Percy when we load the CSS using Webpack plugins style-loader and css-loader. These plugins take the CSS we require in JS using import './filename.css'; and injects it into the HTML.

Edit: as talked about in #312

@valentijnnieman
Copy link
Contributor Author

valentijnnieman commented Oct 15, 2018

Ok, that does do something. I guess to really know if this is a suitable solution I'd need to also add the [email protected] CSS file (which I didn't yet because I wasn't sure which components use that), and make sure they are imported in the correct component. @chriddyp Can you help with that?

@chriddyp
Copy link
Member

[email protected] the Dropdown component uses this one

@valentijnnieman
Copy link
Contributor Author

Alright, well, the Percy snapshots show it's working. They just look a little odd. Will check it out more tomorrow!

@chriddyp
Copy link
Member

Note that we'll always have some artifacts in the rendering. Percy needs for all of the styles to be present on the page (in the actual DOM) so if any of the components update the styles through JS in a way that doesn't persist the styles inline (i.e. the style is only in memory), then the styles won't appear in the image diff (since percy just downloads the HTML on the page).

For example, these icons in the corner are from plotly.js's modebar. If you inspect-element these icons, you'll see that their styles aren't persisted in the SVG in the DOM, and so in the screenshot they're just jumbled in the corner.

image

@valentijnnieman
Copy link
Contributor Author

valentijnnieman commented Oct 16, 2018

@chriddyp Interesting, I always wondered about those icons in the corner :) in this setup, style-loader injects the CSS into the <head> tag, but I'm not sure at which point that's being done.

if you run the gallery test as a standalone Dash app, it still looks different from the Percy snapshots.

here's the gallery as a Dash app code for convenience:
from textwrap import dedent
import json
from datetime import datetime
from textwrap import dedent as d
import plotly.graph_objs as go
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

app = dash.Dash(__name__)
app.scripts.config.serve_locally = True

app.layout = html.Div([
    html.Div(id='waitfor'),
    html.Label('Upload'),
    dcc.Upload(),

    html.Label('Horizontal Tabs'),
    dcc.Tabs(id="tabs", children=[
        dcc.Tab(label='Tab one', className='test', style={'border': '1px solid magenta'}, children=[
            html.Div(['Test'])
        ]),
        dcc.Tab(label='Tab two', children=[
            html.Div([
                html.H1("This is the content in tab 2"),
                html.P("A graph here would be nice!")
            ])
        ], id='tab-one'),
        dcc.Tab(label='Tab three', children=[
            html.Div([
                html.H1("This is the content in tab 3"),
            ])
        ]),
    ],
        style={
        'fontFamily': 'system-ui'
    },
        content_style={
        'border': '1px solid #d6d6d6',
        'padding': '44px'
    },
        parent_style={
        'maxWidth': '1000px',
        'margin': '0 auto'
    }
    ),

    html.Label('Vertical Tabs'),
    dcc.Tabs(id="tabs1", vertical=True, children=[
        dcc.Tab(label='Tab one', children=[
            html.Div(['Test'])
        ]),
        dcc.Tab(label='Tab two', children=[
            html.Div([
                html.H1("This is the content in tab 2"),
                html.P("A graph here would be nice!")
            ])
        ]),
        dcc.Tab(label='Tab three', children=[
            html.Div([
                html.H1("This is the content in tab 3"),
            ])
        ]),
    ]
    ),

    html.Label('Dropdown'),
    dcc.Dropdown(
        options=[
            {'label': 'New York City', 'value': 'NYC'},
            {'label': u'Montréal', 'value': 'MTL'},
            {'label': 'San Francisco', 'value': 'SF'},
            {'label': u'北京', 'value': u'北京'}
        ],
        value='MTL',
        id='dropdown'
    ),

    html.Label('Multi-Select Dropdown'),
    dcc.Dropdown(
        options=[
            {'label': 'New York City', 'value': 'NYC'},
            {'label': u'Montréal', 'value': 'MTL'},
            {'label': 'San Francisco', 'value': 'SF'},
            {'label': u'北京', 'value': u'北京'}
        ],
        value=['MTL', 'SF'],
        multi=True
    ),

    html.Label('Radio Items'),
    dcc.RadioItems(
        options=[
            {'label': 'New York City', 'value': 'NYC'},
            {'label': u'Montréal', 'value': 'MTL'},
            {'label': 'San Francisco', 'value': 'SF'},
            {'label': u'北京', 'value': u'北京'}
        ],
        value='MTL'
    ),

    html.Label('Checkboxes'),
    dcc.Checklist(
        options=[
            {'label': 'New York City', 'value': 'NYC'},
            {'label': u'Montréal', 'value': 'MTL'},
            {'label': 'San Francisco', 'value': 'SF'},
            {'label': u'北京', 'value': u'北京'}
        ],
        values=['MTL', 'SF']
    ),

    html.Label('Text Input'),
    dcc.Input(value='', placeholder='type here', type='text',
                id='textinput'),
    html.Label('Disabled Text Input'),
    dcc.Input(value='disabled', type='text',
                id='disabled-textinput', disabled=True),

    html.Label('Slider'),
    dcc.Slider(
        min=0,
        max=9,
        marks={i: 'Label {}'.format(i) if i == 1 else str(i)
                for i in range(1, 6)},
        value=5,
    ),

    html.Label('Graph'),
    dcc.Graph(
        id='graph',
        figure={
            'data': [{
                'x': [1, 2, 3],
                'y': [4, 1, 4]
            }],
            'layout': {
                'title': u'北京'
            }
        }
    ),

    html.Label('DatePickerSingle'),
    dcc.DatePickerSingle(
        id='date-picker-single',
        date=datetime(1997, 5, 10)
    ),

    html.Label('DatePickerRange'),
    dcc.DatePickerRange(
        id='date-picker-range',
        start_date=datetime(1997, 5, 3),
        end_date_placeholder_text='Select a date!'
    ),

    html.Label('TextArea'),
    dcc.Textarea(
        placeholder='Enter a value... 北京',
        style={'width': '100%'}
    ),

    html.Label('Markdown'),
    dcc.Markdown('''
        #### Dash and Markdown

        Dash supports [Markdown](http://commonmark.org/help).

        Markdown is a simple way to write and format text.
        It includes a syntax for things like **bold text** and *italics*,
        [links](http://commonmark.org/help), inline `code` snippets, lists,
        quotes, and more.

        北京
    '''.replace('    ', '')),
    dcc.Markdown(['# Line one', '## Line two']),
    dcc.Markdown(),
    dcc.SyntaxHighlighter(dedent('''import python
        print(3)'''), language='python'),
    dcc.SyntaxHighlighter([
        'import python',
        'print(3)'
    ], language='python'),
    dcc.SyntaxHighlighter()
])

if __name__ == '__main__':
    app.run_server(debug=True)

@wbrgss Do you have any insights into what's going on here?

@Marc-Andre-Rivet
Copy link
Contributor

Marc-Andre-Rivet commented Oct 29, 2018

@valentijnnieman Looks good to me now that I know webpack.config.js already has a rule for css files!
Can you confirm that the percy visual changes are all as expected?

As for when the styles get loaded, except if you have lazy execution, they should be loaded into the DOM shortly after the bundle was added as the entire dependency chain will be evaluated upon download by the browser.

@valentijnnieman valentijnnieman changed the title [WIP] Load CSS in webpack with style-loader and css-loader Load CSS in webpack with style-loader and css-loader Nov 7, 2018
Copy link
Contributor

@wbrgss wbrgss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall a clean and sensible import pattern that makes a net improvement to the Percy diffs, so 💃 from me. Especially as style-loader and css-loader are already there, as @Marc-Andre-Rivet pointed out.

@wbrgss
Copy link
Contributor

wbrgss commented Nov 7, 2018

I'd like to get @chriddyp's thoughts on standardising this pattern. To quote a comment in #312:

If we figure how to do this, then I'd like to make it a standard so that this repo is following our own conventions. That is, I'd like for us to update our other repos with the same system, our dash-component-boilerplate project, and our plugin documentation.

@wbrgss wbrgss force-pushed the load_stylesheets branch 2 times, most recently from 95794bf to 6581c37 Compare November 7, 2018 21:29
Remove external css_dist refs to moved css files

Add react-virtualized css to Dropdown and remove css_dist dep

Version bump to 0.38.0

Load CSS in webpack with style-loader and css-loader

Remove external css_dist refs to moved css files

Add react-virtualized css to Dropdown and remove css_dist dep

Version bump to 0.38.0
@valentijnnieman valentijnnieman merged commit 35dc2e5 into master Nov 8, 2018
@valentijnnieman valentijnnieman deleted the load_stylesheets branch November 8, 2018 14:56
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants