Silind-Software / direflow

🧩 Use the best of two worlds. Create fast, performant, native Web Components using React.
https://direflow.io
MIT License
501 stars 77 forks source link

Importing antd less styles and overrides #68

Closed traveltek-craigb closed 4 years ago

traveltek-craigb commented 4 years ago

I'm currently trying to get antd default less styles working with direflow and having a few issues. None of the styles from antd.less seem to be getting applied / picked up by the component. I'm not sure where I'm going wrong here.

Here are my files:

App.js

import "antd/dist/antd.less";

import React from "react";
import { Styled } from "direflow-component";
import styles from "./App.css";

import {
  ExportableToursSearchform,
  buildPath
} from "react-hm-toolkit";

const App = props => {

  const config = {
    routes: {
      homepage: "",
      tour: {
        search: "/search/tour",
        results:
          "/search/tour/results/:locationid/:startdate/:enddate/:spreadnights",
        details: "/search/tour/details/:productid/:tourid/:resultno"
      }
    }
  };

  const handleSearchFormSubmit = values => {
    const resultsPath = buildPath(config.routes.tour.results, {
      locationid: values.destination.key,
      startdate: values.dates[0].format("YYYY-MM-DD"),
      enddate: values.dates[1].format("YYYY-MM-DD"),
      spreadnights: values.spreadnights
    });
  };

  return (
    <Styled styles={styles}>
      <div id="app" className="app">
          <ExportableToursSearchform
            onSubmit={handleSearchFormSubmit}
            config={config}
          />
      </div>
    </Styled>
  );
};

export default App; 

direflow-webpack.js

const theme = require("./src/styles/antd-theme.json");
const webpackConfig = require("direflow-component/config-overrides");

module.exports = (config, env) => {
  let configOverride = webpackConfig(config, env);

  // Tweak configOverride here ...
  const rules = [
    {
      test: /\.less$/,
      use: [
        {
          loader: "style-loader"
        },
        {
          loader: "css-loader" // translates CSS into CommonJS
        },
        {
          loader: "less-loader", // compiles Less to CSS
          options: {
            modifyVars: theme,
            javascriptEnabled: true
          }
        }
      ]
    }
  ];

  configOverride.module.rules = [...configOverride.module.rules, ...rules];

  return {
    ...configOverride
  };
}; 

Any help would be appreciated, Thanks.

traveltek-craigb commented 4 years ago

Found another working solution.

JohnDeved commented 4 years ago

hey @traveltek-craigb, may I ask what your solution to your problem was? seems related to #63 & #59

thank you

traveltek-craigb commented 4 years ago

I am using gulp within the application already so I used the gulp-less plugin to do what I needed.

gulpfile.js

const theme = require("./src/styles/antd-theme.json");

gulp.task("less-to-css", function() {
  return gulp
    .src("node_modules/antd/dist/antd.less")
    .pipe(
      less({
        paths: [path.join(__dirname, "less", "includes")],
        modifyVars: theme,
        javascriptEnabled: true
      })
    )
    .pipe(gulp.dest(path.resolve("src/direflow-component")));
});

----------------------------------------------------

gulp.task(
  "merge-and-compile-css",
  gulp.series("less-to-css", "merge-scss", "compile-css")
);
gulp.task("default", gulp.series("translations", "merge-and-compile-css"));

And I added gulp to the beginning of the start and build scripts in package.json.

  "scripts": {
    "start": "gulp && react-app-rewired start",
    "build": "gulp && react-app-rewired build",
    "test": "react-app-rewired test --env=jest-environment-jsdom-fourteen"
  },
JohnDeved commented 4 years ago

im guessing you are not using shadow dom, right?

JohnDeved commented 4 years ago

the problem in your first example is that you need to pass the antd style in the Styled component. this wont work if you are using shadow dom tho

traveltek-craigb commented 4 years ago

Yes I am using the shadowDom.

The gulp script compiles the less down to css and moves it into the direflow-component folder so then inside the App.js I have

import styles from "./App.css";
import antdStyles from "./antd.css";

and then for the logic

  const query = document.querySelector("exportable--searchform");
  const { shadowRoot } = query;

  return (
    <Styled styles={[styles, antdStyles]}>
      <div id="app" className="app">
          <ExportableToursSearchform
            onSubmit={handleSearchFormSubmit}
            getContainer={() => shadowRoot.querySelector("#app")}
            config={config}
          />
      </div>
    </Styled>
  );
JohnDeved commented 4 years ago

Nice solution! So you have the antd css included outside the shadowdom as well? Because in my experience, modals and other popups stop working right otherwise

traveltek-craigb commented 4 years ago

The modals stop working due to the default behaviour of them being added to document.body. Antd has functions you can override to change that default behaviour. This is why I needed to access the shadowRoot.

If you see the getContainer prop function, that is passed down to antd override functions such as "getPopupContainer" and "getCalendarContainer" so that the styles only need to be added inside the shadowDom.

This means that the above implementation is all that is needed for it to work.

JohnDeved commented 4 years ago

oh, that's right, I remember you now! that's pretty cool, I didn't know you could do that with antd 👍 sadly not something we can use for direflow tho, we would need some general solution for UI libraries :D

SimonHoiberg commented 4 years ago

@JohnDeved Maybe we can create custom plugins for some of these cases? For that handful of really commonly used UI Libraries, we may want to consider creating custom support. If we can make it as plugins so we don't compromise too much on bundlesize, I think that's definitely worth considering.

JohnDeved commented 4 years ago

Thats true, we could Support antd and material-ui First, and then see what people want next. It will definitiv be more Work in the Long run, but probably a cleaner solution, i agree.

Question Just is, how well can you make this happen for material-ui aswell

mfreville commented 4 years ago

I am using gulp within the application already so I used the gulp-less plugin to do what I needed.

gulpfile.js

const theme = require("./src/styles/antd-theme.json");

gulp.task("less-to-css", function() {
  return gulp
    .src("node_modules/antd/dist/antd.less")
    .pipe(
      less({
        paths: [path.join(__dirname, "less", "includes")],
        modifyVars: theme,
        javascriptEnabled: true
      })
    )
    .pipe(gulp.dest(path.resolve("src/direflow-component")));
});

----------------------------------------------------

gulp.task(
  "merge-and-compile-css",
  gulp.series("less-to-css", "merge-scss", "compile-css")
);
gulp.task("default", gulp.series("translations", "merge-and-compile-css"));

And I added gulp to the beginning of the start and build scripts in package.json.

  "scripts": {
    "start": "gulp && react-app-rewired start",
    "build": "gulp && react-app-rewired build",
    "test": "react-app-rewired test --env=jest-environment-jsdom-fourteen"
  },

Hey great tricky idea @traveltek-craigb!

I'm trying to add antd too but I didn't know enough gulp to use it like that. Please do you have more complete snippet about the gulpfile.js ? As for the antd getContainer behaviour for shadow dom ?

It will be great :)

KR.

traveltek-craigb commented 4 years ago

As for the shadowRoot getContainer function, what I posted above:

  const query = document.querySelector("exportable--searchform");
  const { shadowRoot } = query;

  return (
    <Styled styles={[styles, antdStyles]}>
      <div id="app" className="app">
          <ExportableToursSearchform
            onSubmit={handleSearchFormSubmit}
            getContainer={() => shadowRoot.querySelector("#app")}
            config={config}
          />
      </div>
    </Styled>
  );

The getContainer function I pass down is then called for antd Select (getPopupContainer) and also for antd range picker (getCalendarContainer). This will place these popup elements inside the exportable components div with the id="app".

Here is my entire gulpfile.js (some of it is completely irrelevant to direflow)

const gulp = require("gulp");
const tap = require("gulp-tap");
const fs = require("fs");
const fsExtra = require("fs-extra");
const less = require("gulp-less");
const path = require("path");
const streamqueue = require("streamqueue");
const sass = require("gulp-sass");
const concat = require("gulp-concat");
const theme = require("./src/styles/antd-theme.json");

gulp.task("translations", () => {
  var isLinux = process.platform === "linux" || process.platform === "darwin";
  var lang, name;
  var initialDir = "node_modules/@traveltek/react-hm-toolkit/src/locales/";
  return gulp.src(initialDir + `**/*.json`).pipe(
    tap(file => {
      if (isLinux) {
        [lang, name] = file.relative.split("/");
      } else {
        [lang, name] = file.relative.split("\\");
      }
      var destPath = `src/overrides/locales/${lang}/${name}`;

      var exists = fs.existsSync(destPath);
      if (exists) {
        var initialFile = JSON.parse(file.contents.toString("utf8"));
        var overrideFile;

        var contents = fs.readFileSync(destPath, "utf8");

        overrideFile = JSON.parse(contents);

        var finalFile = { ...initialFile, ...overrideFile };

        fsExtra.outputFileSync(
          `./public/locales/${lang}/${name}`,
          JSON.stringify(finalFile)
        );
      } else {
        fsExtra.copySync(
          initialDir + `${lang}/${name}`,
          `./public/locales/${lang}/${name}`
        );
      }
    })
  );
});

gulp.task("less-to-css", function() {
  return gulp
    .src("node_modules/antd/dist/antd.less")
    .pipe(
      less({
        paths: [path.join(__dirname, "less", "includes")],
        modifyVars: theme,
        javascriptEnabled: true
      })
    )
    .pipe(gulp.dest(path.resolve("src/direflow-component")));
});

const toolkit = "node_modules/@traveltek/react-hm-toolkit/build";

const scssFiles = [
  `${toolkit}/toolkit-fonts.scss`,
  `${toolkit}/toolkit-mixins.scss`,
  `${toolkit}/toolkit-colors.scss`,
  `${toolkit}/toolkit-main.scss`,
  `${toolkit}/toolkit-var.scss`,
  `${toolkit}/toolkit-assign.scss`
];

gulp.task("merge-scss", () => {
  return streamqueue({ objectMode: true }, gulp.src(scssFiles))
    .pipe(concat("App.scss"))
    .pipe(gulp.dest("build"));
});

gulp.task("compile-css", () => {
  return gulp
    .src("build/App.scss")
    .pipe(sass().on("error", sass.logError))
    .pipe(gulp.dest(path.resolve("src/direflow-component")));
});

gulp.task(
  "merge-and-compile-css",
  gulp.series("less-to-css", "merge-scss", "compile-css")
);
gulp.task("default", gulp.series("translations", "merge-and-compile-css"));