ionic-team / ionicons

Premium hand-crafted icons built by Ionic, for Ionic apps and web apps everywhere 🌎
http://ionicons.com
MIT License
17.55k stars 2.06k forks source link

Cannot import anything from IonIcons in Storybook #670

Open anatoliyarkhipov opened 5 years ago

anatoliyarkhipov commented 5 years ago

Originally I encountered the error while trying to render components from @ionic/react in Storybook, but I was able to narrow it down to ionicons and so I post the issue in this repo.

TLDR;

If you try to import anything from @ionic/react in a Storybook story, you get this error:

image

Here is a minimal example: https://github.com/anatoliyarkhipov/ionic-react-in-storybook

Details

Object(...) is not a function seems a little bit obscure, but it actually this line in the bundle:

Object(ionicons__WEBPACK_IMPORTED_MODULE_0__["addIcons"])(ionicons_icons__WEBPACK_IMPORTED_MODULE_1__["ICON_PATHS"]);

And this is the original line in the @ionic/react sources:

addIcons(ICON_PATHS);

Webpack wraps every direct call of an imported symbol with Object() and there is nothing wrong with it. The problem is that ionicons__WEBPACK_IMPORTED_MODULE_0__["addIcons"] is undefined and so Object(undefined) returns an empty object which is "not a function".

And that problem appears only for the addIcons - other symbols are imported correctly and aren't undefined: image

I also tried to remove everything from from .storybook/config.js, import the function directly and console.log it, and it again was undefined:

// .storybook/config.js
import { addIcons } from "ionicons"
console.log(addIcons)

Then I rewrote the import to const ionicons = require("ionicons") and run config.js directly via node config.js and ionicons was an empty object.

// .storybook/config.js
const ionicons = require("ionicons")
console.log(ionicons)

At the same time, I have no problems when I use it in create-reactp-app! I set up the project following guide in this announcement and everything is fine: if I import { addIcons } from "ionicons" it's a normal function. I tried to figure out what's so special in the CRA's Webpack config, but had no success due to its complexity.

And here we are 😀.

tm-3 commented 5 years ago

I'm using storybook/html with stenciljs and the ionic framework. I had similar issues. Check out adding a custom head tag:

https://storybook.js.org/docs/configurations/add-custom-head-tags/

In my case I added:

<link href="https://unpkg.com/@ionic/core@4.0.0/css/ionic.bundle.css" rel="stylesheet" />
<script src="https://unpkg.com/@ionic/core@4.0.0/dist/ionic.js"></script>
<link href="https://unpkg.com/ionicons@4.5.5/dist/css/ionicons.min.css" rel="stylesheet" />

I didn't have to do anything special to the config.js or a custom webpack.config.js rule. I haven't worked with React in some time so I may be a bit off here. You also might want to use the latest version of Storybook. I've had much better success with v5. Hope this helps a bit.

michelepatrassi commented 4 years ago

@tm-3 thanks! I also added the js script for the icons (I import ionic as module in the story itself for the missing js)

<link href="https://unpkg.com/@ionic/core@4.0.0/css/ionic.bundle.css" rel="stylesheet" />
<link href="https://unpkg.com/ionicons@4.5.5/dist/css/ionicons.min.css" rel="stylesheet" />

<script src="https://unpkg.com/ionicons@4.5.10-0/dist/ionicons.js"></script>
onderceylan commented 3 years ago

Local dependencies could be copied over to the dist via the webpack copy plugin. This is how I loaded my local ionicons dependency;

.storybook/main.js

const CopyPlugin = require('copy-webpack-plugin');

module.exports = {
  stories: ['../src/**/*.stories.ts'],
  webpackFinal: config => {
    config.plugins.push(
      new CopyPlugin({
        patterns: [{ from: './node_modules/ionicons/dist/ionicons', to: './ionicons/' }],
      })
    );
    return config;
  },
};

.storybook/preview-head.html

<script async type="module" src="/ionicons/ionicons.esm.js"></script>
corysmc commented 3 years ago

Thanks @onderceylan - this pointed me downt he right path. What I needed to do was copy the svg folder:

// main.ts
import * as CopyPlugin from 'copy-webpack-plugin';
export const stories = [
  '../**/*.stories.mdx',
  '../**/*.stories.@(js|jsx|ts|tsx)',
];
export const addons = ['@storybook/addon-essentials', '@storybook/addon-a11y'];
export const webpackFinal = (config) => {
  config.plugins.push(
    new CopyPlugin({
      patterns: [
        { from: './node_modules/ionicons/dist/ionicons/svg', to: './svg/' },
      ],
    })
  );
  return config;
};
RyanClementsHax commented 6 months ago

@corysmc Massive thanks for posting your solution. It worked for me.

This was my final config

import type { StorybookConfig } from '@storybook/angular';
import CopyPlugin from 'copy-webpack-plugin';

const config: StorybookConfig = {
  stories: ['../src/app/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
  addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
  staticDirs: ['../src/assets'],
  framework: {
    name: '@storybook/angular',
    options: {},
  },
  webpackFinal: (config) => {
    config.plugins?.push(
      new CopyPlugin({
        patterns: [
          { from: './node_modules/ionicons/dist/ionicons/svg', to: './svg/' },
        ],
      }),
    );
    return config;
  },
};

export default config;

// To customize your webpack configuration you can use the webpackFinal field.
// Check https://storybook.js.org/docs/react/builders/webpack#extending-storybooks-webpack-config
// and https://nx.dev/recipes/storybook/custom-builder-configs