Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[gatsby-plugin-netlify-cms] Enabling preview styles #6641

Closed
ascorbic opened this issue Jul 21, 2018 · 16 comments
Closed

[gatsby-plugin-netlify-cms] Enabling preview styles #6641

ascorbic opened this issue Jul 21, 2018 · 16 comments
Labels
help wanted Issue with a clear description that the community can help with. type: question or discussion Issue discussing or asking a question about Gatsby

Comments

@ascorbic
Copy link
Contributor

ascorbic commented Jul 21, 2018

Summary

I'm not sure how to get Netlify CMS to display custom preview styles.

Relevant information

I am using Netlify CMS with custom preview components that use CSS modules. It's unclear how to get the styles to be generated and loaded. Particularly, I don't know what I should be putting in stylesPath in the config. All of the styles are imported in the components, so there is no entry point as such. I tried setting the path to point to one of the base stylesheets, but styles.css is not generated. There is a styles.js file created by mini-css-extract-plugin, but no matching CSS.

import CMS from "netlify-cms";
import "netlify-cms/dist/cms.css";

// import preview templates for the CMS here

import ProjectPagePreview from "./preview-templates/ProjectPagePreview";

// Styles for the CMS
CMS.registerPreviewStyle("styles.css");

// Register preview templates

CMS.registerPreviewTemplate("projects", ProjectPagePreview);

Environment (if relevant)

File contents (if changed)

gatsby-config.js:

const siteConfig = require("./site-config");

module.exports = {
    pathPrefix: siteConfig.pathPrefix,
    siteMetadata: siteConfig.siteMetadata,
    mapping: {
        "ProjectsJson.client": "ClientsJson"
    },
    plugins: [
        `gatsby-plugin-typescript`,
        `gatsby-plugin-react-helmet`,
        {
            resolve: `gatsby-source-filesystem`,
            options: {
                path: `${__dirname}/static/assets`,
                name: "uploads"
            }
        },
        {
            resolve: `gatsby-source-filesystem`,
            options: {
                path: `${__dirname}/src/pages`,
                name: "pages"
            }
        },
        // This plugin identifies file nodes that are images and
        // transforms these to create new “ImageSharp” nodes.
        // With them you can resize images and
        // generate responsive image thumbnails.
        `gatsby-transformer-sharp`,
        // transform JSON file nodes
        `gatsby-transformer-json`,
        // This plugin exposes helper functions for processing
        // images with the NPM package “sharp”. It's used by
        // several plugins.
        `gatsby-plugin-sharp`,
        `gatsby-plugin-sass`,
        // Manifest for AppCache and PWA compatibility
        {
            resolve: `gatsby-plugin-manifest`,
            options: siteConfig.manifest
        },
        {
            resolve: `gatsby-plugin-netlify-cms`,
            options: {
                // One convention is to place your Netlify CMS customization code in a
                // `src/cms` directory.
                modulePath: `${__dirname}/src/cms/cms.ts`,
                stylesPath: `${__dirname}/src/scss/base-theme.scss`
            }
        }
    ]
};

package.json:

{
    "name": "aerian-site-rebuild",
    "description": "Aerian Studios.",
    "version": "0.0.1",
    "dependencies": {
        "@babel/preset-env": "^7.0.0-beta.54",
        "@fortawesome/fontawesome-svg-core": "^1.2.0-14",
        "@fortawesome/free-brands-svg-icons": "^5.1.0-11",
        "@fortawesome/free-solid-svg-icons": "^5.1.0-11",
        "@fortawesome/react-fontawesome": "^0.1.0-11",
        "@researchgate/react-intersection-observer": "^0.7.3",
        "classnames": "^2.2.6",
        "deep-map": "^1.5.0",
        "extract-text-webpack-plugin": "^3.0.2",
        "gatsby": "2.0.0-beta.45",
        "gatsby-image": "^2.0.0-beta.6",
        "gatsby-plugin-manifest": "^2.0.2-beta.2",
        "gatsby-plugin-netlify-cms": "^2.0.0-beta.6",
        "gatsby-plugin-offline": "next",
        "gatsby-plugin-react-helmet": "^3.0.0-beta.3",
        "gatsby-plugin-sass": "^2.0.0-beta.5",
        "gatsby-plugin-sharp": "^2.0.0-beta.5",
        "gatsby-plugin-typescript": "^2.0.0-beta.5",
        "gatsby-source-filesystem": "^2.0.1-beta.5",
        "gatsby-transformer-json": "^2.1.1-beta.2",
        "gatsby-transformer-sharp": "^2.1.1-beta.5",
        "leaflet": "^1.3.1",
        "netlify-cms": "^1.9.2",
        "react": "^16.4.1",
        "react-dom": "^16.4.1",
        "react-helmet": "^5.2.0",
        "react-leaflet": "^2.0.0",
        "react-markdown": "^3.3.4"
    },
    "keywords": [
        "gatsby"
    ],
    "license": "MIT",
    "scripts": {
        "build": "gatsby build",
        "start": "gatsby develop",
        "test": "jest",
        "updateSnapshot": "jest --updateSnapshot",
        "storybook": "start-storybook -p 9001 -c .storybook",
        "storybook-build": "build-storybook -c .storybook -o docs",
        "precommit": "lint-staged",
        "prepush": "jest --ci",
        "test-ci": "jest --coverage --coverageReporters=text-lcov --maxWorkers=2 > coverage.lcov && codecov"
    },
    "devDependencies": {
        "@babel/core": "^7.0.0-beta.54",
        "@babel/plugin-proposal-class-properties": "^7.0.0-beta.54",
        "@babel/plugin-proposal-optional-chaining": "^7.0.0-beta.54",
        "@babel/plugin-syntax-dynamic-import": "^7.0.0-beta.51",
        "@babel/preset-react": "^7.0.0-beta.54",
        "@storybook/addon-actions": "^3.4.8",
        "@storybook/addon-info": "^3.4.8",
        "@storybook/react": "^4.0.0-alpha.10",
        "@types/classnames": "^2.2.4",
        "@types/jest": "^23.1.1",
        "@types/leaflet": "^1.2.8",
        "@types/node": "^10.5.2",
        "@types/react-helmet": "^5.0.6",
        "@types/react-leaflet": "^1.1.5",
        "@types/react-test-renderer": "^16.0.1",
        "@types/storybook__addon-actions": "^3.0.3",
        "@types/storybook__addon-info": "^3.2.3",
        "@types/storybook__react": "^3.0.7",
        "autoprefixer": "^8.6.5",
        "babel-core": "^7.0.0-bridge.0",
        "babel-jest": "^23.4.0",
        "codecov": "^3.0.4",
        "css-loader": "^0.28.11",
        "extract-text-webpack-plugin": "^3.0.2",
        "gh-pages": "^1.2.0",
        "husky": "^0.14.3",
        "hygen": "^1.6.2",
        "hygen-react-typescript": "^1.0.2",
        "identity-obj-proxy": "^3.0.0",
        "intersection-observer": "^0.5.0",
        "jest": "^23.1.0",
        "lint-staged": "^7.2.0",
        "node-sass": "^4.9.0",
        "prettier": "^1.13.5",
        "react-docgen-typescript-loader": "^2.1.1",
        "react-docgen-typescript-webpack-plugin": "^1.1.0",
        "react-test-renderer": "^16.4.1",
        "regenerator-runtime": "^0.12.0",
        "ts-jest": "^22.4.6",
        "ts-loader": "^4.4.1",
        "tslint": "^5.10.0",
        "tslint-config-aerian": "^1.0.2",
        "typescript": "^2.9.2",
        "typings-for-css-modules-loader": "^1.7.0"
    },
    "lint-staged": {
        "*.{js,jsx,css,md,scss}": [
            "prettier --write",
            "git add",
            "jest --ci --findRelatedTests"
        ],
        "*.{ts,tsx}": [
            "tslint --fix",
            "git add",
            "jest --ci --findRelatedTests"
        ]
    },
    "repository": {
        "type": "git",
        "url": "git+https://github.com:aerian-studios/aerian-site-rebuild.git"
    },
    "jest": {
        "transform": {
            "^.+\\.tsx?$": "ts-jest",
            "^.+\\.jsx?$": "<rootDir>/jestPreprocess.js"
        },
        "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(tsx?)$",
        "moduleFileExtensions": [
            "ts",
            "tsx",
            "js",
            "jsx",
            "json",
            "node"
        ],
        "moduleNameMapper": {
            "^.+\\.(css|less|scss|png)$": "identity-obj-proxy",
            "^./pages.json$": "<rootDir>/__mocks__/pages.json"
        },
        "transformIgnorePatterns": [
            "node_modules/(?!(gatsby)/)"
        ]
    }
}

gatsby-node.js:

const path = require("path");
const { createFilePath } = require("gatsby-source-filesystem");
const deepMap = require("deep-map");
// Implement the Gatsby API “createPages”. This is
// called after the Gatsby bootstrap is finished so you have
// access to any information necessary to programmatically
// create pages.
exports.createPages = ({ actions, graphql }) => {
    const { createPage } = actions;

    /**
     * Work out the necessary to generate disntinct pages
     * @param {object} edge - The data for the distinct page
     */
    const generateDistinctPage = data => {
        const template = data.path && data.path.replace("/", "");
        const { id, sections, staff } = data;

        // for the time being we can just assume that there are different teplates for each of the pages, but we can add logic here to reuse page templates
        createPage({
            path: template,
            component: path.resolve(`src/templates/${String(template)}.tsx`),
            // additional data can be passed via context
            context: {
                id
            }
        });
    };

    /**
     *
     * @param {string} id - JSON id, generally a path to the file
     * @param {string} template - the string name of the template without the `.tsx`
     * @param {string} slug - generally the unique name of the JSON file (without path or file type)
     */
    const generatePage = (id, template, slug) => {
        createPage({
            path: slug,
            component: path.resolve(`src/templates/${String(template)}.tsx`),
            // additional data can be passed via context
            context: {
                id
            }
        });
    };

    /**
     * Run queries to get all the types of pages for which we need to make static pages
     *
     * `allPagesJson` needs a bit more information if we want to control how they are
     * processed in the future
     */
    return graphql(
        `
            {
                allProjectsJson(limit: 1000) {
                    edges {
                        node {
                            id
                            slug
                        }
                    }
                }
                allPagesJson(limit: 1000) {
                    edges {
                        node {
                            id
                            path
                            staff {
                                name
                            }
                            sections {
                                title
                            }
                        }
                    }
                }
            }
        `
    ).then(result => {
        if (result.errors) {
            result.errors.forEach(e => console.error(e.toString()));
            return Promise.reject(result.errors);
        }

        // Gatsby uses Redux to manage its internal state.
        // Plugins and sites can use functions like "createPage"
        // to interact with Gatsby.
        result.data.allProjectsJson.edges.forEach(edge => {
            const id = edge.node.id;
            const template = "project";
            const slug = `our-work/project/${edge.node.slug}`;

            generatePage(id, template, slug);
        });

        result.data.allPagesJson.edges.forEach(edge => {
            generateDistinctPage(edge.node);
        });
        return Promise.resolve();
    });
};

const excluded = new Set(["internal", "children", "parent", "id"]);

exports.onCreateNode = ({ node, getNode, getNodes }) => {
    if (node.internal.owner === "gatsby-transformer-json") {
        const parent = getNode(node.parent);
        const makeRelative = value => {
            if (typeof value === "string") {
                const pathToFile = path.join(__dirname, "static", value);
                const foundFileNode = getNodes().find(
                    n => n.absolutePath === pathToFile
                );

                if (foundFileNode) {
                    const p = path.relative(
                        parent.dir,
                        foundFileNode.absolutePath
                    );
                    if (p) {
                        return p;
                    }
                }
            }
            return value;
        };
        Object.keys(node).forEach(key => {
            if (excluded.has(key)) {
                return;
            }

            if (typeof node[key] === "string") {
                node[key] = makeRelative(node[key]);
            }
            deepMap(node[key], makeRelative, {
                inPlace: true
            });
        });
    }
};

gatsby-browser.js: N/A
gatsby-ssr.js: N/A

@LekoArts LekoArts added help wanted Issue with a clear description that the community can help with. type: question or discussion Issue discussing or asking a question about Gatsby labels Jul 21, 2018
@giraffesyo
Copy link
Contributor

Do the styles work in production?

@ascorbic
Copy link
Contributor Author

Yes, it's just in the CMS preview. The preview even generates the correct HTML, with the correct CSS module classnames. It just isn't building the styles bundle.

@giraffesyo
Copy link
Contributor

giraffesyo commented Jul 23, 2018

There was another issue like this, see:

decaporg/gatsby-starter-decap-cms#19

For the record I'm having similar issues getting styling to work in Gatsby v2, but linking in case its relevant to you. In my case, I can't get even the class names to actually apply to the stuff in the preview so it all just looks like plain text. You're one step ahead of me.

@ascorbic
Copy link
Contributor Author

ascorbic commented Jul 23, 2018

The comment on that thread talks about it generating styles.css. My issue is that that file isn't being generated.
Edit: to be clear, mine is just showing unstyled too. It just has the correct class names in the HTML, but no stylesheet.

@giraffesyo
Copy link
Contributor

I don't think this is the issue but another thing I'm looking at here is that you're calling CMS.registerPreviewStyle("styles.css"); in your cms.js then setting it to something different in gatsby-config.js The config option also calls CMS.registerPreviewStyles() so I imagine that would cause something to be overridden.

@ascorbic
Copy link
Contributor Author

I tried it with various combinations. From what I understand, the setting in gatsby-config.js specifies the input file, while CMS.registerPreviewStyles() refers to the output css, which is hard-coded as styles.css in the plugin.

@giraffesyo
Copy link
Contributor

giraffesyo commented Jul 23, 2018

No, I don't think so.

Take a look here at cms.js:

// eslint-disable-next-line no-undef
if (NETLIFY_CMS_PREVIEW_STYLES_SET) {
  CMS.registerPreviewStyle(`styles.css`)
}

And here in gatsby-node.js

plugins.define({
      NETLIFY_CMS_PREVIEW_STYLES_SET: !!stylesPath,
    }),

The plugin is directly calling the CMS.registerPreviewStyles() method on whatever option is set.

Per the documentation here, you can pass a path or array of paths to the option.

Changing my config to provide it with the path to the css file that is used by my site rendered some of the styles in the preview pane. My case is probably a bit simpler though because all the styles are contained within actual css files.

@ascorbic
Copy link
Contributor Author

The bit in gatsby-node.js just sets NETLIFY_CMS_PREVIEW_STYLES_SET to true if stylesPath is set to anything. Then cms.js calls CMS.registerPreviewStyle("styles.css") if that is set to true.

@giraffesyo
Copy link
Contributor

Yeah you're right. I'll post back if I find more!

@erquhart
Copy link
Contributor

@ascorbic try making the Netlify CMS plugin last (except for any plugins that also require being last, like gatsby-plugin-netlify. Specifically you want the Netlify CMS plugin to come after any plugins that process CSS. Can you give that a shot and comment back?

@erquhart
Copy link
Contributor

Ah, disregard, just noticed your config in your original post.

@erquhart
Copy link
Contributor

erquhart commented Jul 30, 2018

@ascorbic I took the liberty of forking your repo and checking things out locally - styles seem to be working. You'll want to remove the registerPreviewStyle in your cms.ts file, it's duplicating what the Netlify CMS plugin already does (not sure if it's hurting things, it shouldn't be).

Run the develop task and check out http://localhost:8000/admin/styles.css - you'll see the output from base-theme.scss per your stylesPath. I'd recommend creating a styles.ts module (name can be anything) in your cms directory, importing all necessary site styles there, and using that module in your stylesPath.

Hope that makes sense lol.

Closing for now, please comment if you have further issues.

@ascorbic
Copy link
Contributor Author

@erquhart Thanks. Unfortunately that doesn't work. The issue is that I'm using CSS modules. Importing base-theme.scss only gives the base styles (fonts etc), not the component and page styles. Those are loaded in each component, rather than from an entry point. I can't load them manually from a styles.ts file, as the classNames won't match.

@erquhart
Copy link
Contributor

Can you test out #6871 and see if it works for you? With that, any imported styles in your modulePath module or any of the modules it imports down the chain will all be output to cms.css, which is automatically registered to the preview pane. (So no more stylesPath.)

@ascorbic
Copy link
Contributor Author

ascorbic commented Jul 30, 2018

Ah, that looks like a much better approach. I have a branch with Netlify CMS 2, so I'll try it with that and let you know. If you could merge decaporg/decap-cms#1543 it would be even better ;)

@ascorbic
Copy link
Contributor Author

I can confirm that #6871 fixes this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Issue with a clear description that the community can help with. type: question or discussion Issue discussing or asking a question about Gatsby
Projects
None yet
Development

No branches or pull requests

4 participants