Client side extension imported css via webpack css loader is not reflected

In the bitbucket server client side extension plugin template I have used a react module component for checkbox tree view and configured in the webpack config a css loader.
However, while importing the css file within the js code, shows no error, the classes imported are not effective in the component.
Am I missing a step or does bitbucket somehow override these classes by the one provided by the server itself?

Can you share more about your CSS styling approach? Are you using webpack css-loader with or without CSS modules? Does the problem exist in webpack dev-server or after you bundle webpack in production mode?

I can suggest doing that:

  1. try using some distinguished names for one of the CSS classes you use with React components
  2. build/bundle the plugin in production mode. Run the plugin in Bitbucket.
  3. Use Chrome Devtools and run a global search to find your class name.

You can turn on a global search in devtools using a keyboard shortcut:

Mac: Command+Option+F

Windows/Linux Control+Shift+F

Opens the Search tab in the Drawer , which lets you search for text across all loaded resources

Let me know what are your findings

@madamczak Thanks for the prompt reply as usual.

I have tried packaging (via atlas-package) the plugin and uploaded it on my bitbucket server instance, using the Devtools on chrome as suggested I was able to search and find reference to my css classes in “extensions-pull-request-diff-toolbar-extension-js.chunk.js”, however, they are somehow not applied. I have the impression that aui are somehow overriding these.

My CSS styling approach is the following, I have a css-loader configured for webpack.
A snippet of my “webpack.loaders.js”

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const { I18N_FILES } = require('./webpack.constants');

function getLoaders({ isProductionEnv = false }) {
    return [
        {
        test: /\.css$/,
        use: [ 
            {
                loader: 'css-loader',
                options: {
                    import: true,
                },
            },
            ],

      },
        {
            test: /\.jsx?$/,
            exclude: /node_modules/,
            use: [
                {
                    loader: '@atlassian/i18n-properties-loader',
                    options: {
                        i18nFiles: I18N_FILES,
                        disabled: isProductionEnv,
                    },
                },
                {
                    loader: 'babel-loader',
                    options: {
                        cacheDirectory: true,
                    },
                },
            ],
        },

        {
            test: /\.less$/,
            use: [
                {
                    loader: isProductionEnv ? MiniCssExtractPlugin.loader : 'style-loader',
                    options: {
                        sourceMap: true,
                    },
                },
                {
                    loader: 'css-loader',
                    options: {
                        sourceMap: true,
                    },
                },
                {
                    loader: 'less-loader',
                    options: {
                        sourceMap: true,
                    },
                },
            ],
        },

        {
            test: /\.(png|jpg|gif|svg)$/,
            use: [
                {
                    loader: 'file-loader',
                    options: {},
                },
            ],
        },

        {
            test: /\.soy$/,
            use: [
                {
                    loader: '@atlassian/i18n-properties-loader',
                    options: {
                        I18N_FILES,
                        disabled: isProductionEnv,
                    },
                },

                {
                    loader: '@atlassian/soy-loader',
                    options: {
                        dontExpose: true,
                    },
                },
            ],
        },

        {
            test: /\.cse.graphql$/,
            loader: '@atlassian/clientside-extensions-schema/loader',
        },
    ];
}

module.exports.loaders = isProduction => getLoaders(isProduction);

Then I simply import the css in my plugin js file like this:

import "./styles.css"

Example css file:

@import url("https://fonts.googleapis.com/css?family=Lato");

body {
  background-color: #eee;
}

#app {
  border-radius: 3px;
  border: 1px solid #e5e5e5;
  margin: 15px;
  background-color: white;
}

.tab {
  width: 100%;
  padding: 25px;
  font-family: sans-serif;
  color: #444;
}

ul.inline {
  list-style: none;
  padding: 0;
  margin-bottom: 0;
  -webkit-margin-before: 0;
  -webkit-margin-after: 0;
  -webkit-margin-start: 0px;
  -webkit-margin-end: 0px;
  -webkit-padding-start: 0px;
}

ul.inline,
li {
  display: inline-block;
  margin-left: 0;
  padding: 10px;
  border-bottom: 2px solid #eee;
  transition: all 0.5s;
  font-family: Lato, sans-serif;
  font-weight: 300;
  cursor: pointer;
  color: #aaa;
}

ul.inline,
li.selected {
  border-bottom: 2px solid #337ab7;
  color: #444;
}

I noticed that if I have the css classes inlined in js and apply the style in the component via style attribute it works. Example below:

dummyComponent.js:

import React, { Component } from "react";


const style = {
  "tab": {
    "width": "100%",
    "padding": "25px",
    "fontFamily": "sans-serif",
    "color": "#444"
  }
};

class Tabs extends Component {
  state = {
    selected: this.props.selected || 2
  };

  render() {
    return (
     
        <div style={style.tab}>{this.props.children[this.state.selected]}</div>

    );
  }
}

export default Tabs;

But, somehow classNames when importing the css do not.

I could of course work around this and change every classname to style attribute and reference the inline css, but, this is very impractical especially that I am using some OSS components which come with default css style sheets, and in that case I’ll have to dig in the module and change every className to a style attribute, but, this can get overwhelming with big components.
This is also problematic with element styles like “ul.inline”.

I think the problem is that your css-loader is missing the additional required MiniCssExtractPlugin and style-loader configuration that will handle injecting the CSS files. Similar to what we are doing with .less files:

{
    test: /\.css$/,
    use: [
        { // Add this lines before css-loader
            loader: isProductionEnv ? MiniCssExtractPlugin.loader : 'style-loader'
        },

        {
            loader: 'css-loader',
            options: {
                import: true,
            },
        },
    ]
},

BTW: If you are using webpack v4 you should keep using the css-loader v2. The v3 version of the css-loader is compatible with webpack 5+ only:

I also updated the template proejct to include the mentioned css-loader changes:

Let me know if that helps.

Thanks,
Maciej Adamczak
Atlassian Developer

1 Like

Indeed the MiniCssExtractPlugin and style-loader configuration solved it.
It is very nice to have this added to the template master. Thanks!

1 Like