Преглед изворни кода

Move Storybook back into web (#13219)

GitOrigin-RevId: 61b69b5fd178dd4f5ab26858c434f959dfc410bc
Alf Eaton пре 2 година
родитељ
комит
76d195cd39

Разлика између датотеке није приказан због своје велике величине
+ 129 - 6
package-lock.json


+ 1 - 2
package.json

@@ -49,7 +49,6 @@
     "services/third-party-datastore",
     "services/third-party-references",
     "services/tpdsworker",
-    "services/web",
-    "tools/storybook"
+    "services/web"
   ]
 }

+ 62 - 0
services/web/.storybook/main.ts

@@ -0,0 +1,62 @@
+import type { StorybookConfig } from '@storybook/react-webpack5'
+import path from 'node:path'
+
+const rootDir = path.resolve(__dirname, '..')
+
+// NOTE: must be set before webpack config is imported
+process.env.SHARELATEX_CONFIG = path.join(rootDir, 'config/settings.webpack.js')
+
+const config: StorybookConfig = {
+  core: {
+    disableTelemetry: true,
+  },
+  staticDirs: [path.join(rootDir, 'public')],
+  stories: [
+    path.join(rootDir, 'frontend/stories/**/*.stories.{js,jsx,ts,tsx}'),
+    path.join(rootDir, 'modules/**/stories/**/*.stories.{js,jsx,ts,tsx}'),
+  ],
+  addons: [
+    '@storybook/addon-links',
+    '@storybook/addon-essentials',
+    '@storybook/addon-interactions',
+    '@storybook/addon-a11y',
+  ],
+  framework: {
+    name: '@storybook/react-webpack5',
+    options: {},
+  },
+  docs: {
+    autodocs: 'tag',
+  },
+  babel: options => {
+    return {
+      ...options,
+      plugins: [
+        // ensure that TSX files are transformed before other plugins run
+        ['@babel/plugin-transform-typescript', { isTSX: true }],
+        ...(options.plugins ?? []),
+      ],
+    }
+  },
+  webpackFinal: storybookConfig => {
+    return {
+      ...storybookConfig,
+      resolve: {
+        ...storybookConfig.resolve,
+        fallback: {
+          ...storybookConfig.resolve?.fallback,
+          fs: false,
+          os: false,
+          module: false,
+        },
+        extensions: ['.js', '.jsx', '.mjs', '.ts', '.tsx', '.json'],
+        alias: {
+          ...storybookConfig.resolve?.alias,
+          // custom prefixes for import paths
+          '@': path.join(rootDir, 'frontend/js/'),
+        },
+      },
+    }
+  },
+}
+export default config

+ 3 - 0
services/web/.storybook/manager.css

@@ -0,0 +1,3 @@
+.sidebar-container a[title='Overleaf'] {
+  max-width: 100px;
+}

+ 15 - 0
services/web/.storybook/manager.ts

@@ -0,0 +1,15 @@
+import { addons } from '@storybook/addons'
+import { create } from '@storybook/theming'
+
+import './manager.css'
+
+import brandImage from '../public/img/ol-brand/overleaf.svg'
+
+const theme = create({
+  base: 'light',
+  brandTitle: 'Overleaf',
+  brandUrl: 'https://www.overleaf.com',
+  brandImage,
+})
+
+addons.setConfig({ theme })

+ 101 - 0
services/web/.storybook/preview.tsx

@@ -0,0 +1,101 @@
+import type { Preview } from '@storybook/react'
+
+// Storybook does not (currently) support async loading of "stories". Therefore
+// the strategy in frontend/js/i18n.js does not work (because we cannot wait on
+// the promise to resolve).
+// Therefore we have to use the synchronous method for configuring
+// react-i18next. Because this, we can only hard-code a single language.
+import i18n from 'i18next'
+import { initReactI18next } from 'react-i18next'
+// @ts-ignore
+import en from '../../../services/web/locales/en.json'
+import { ExposedSettings } from '../types/exposed-settings'
+import { User } from '../types/user'
+
+window.i18n = window.i18n || {}
+window.i18n.currentLangCode = window.i18n.currentLangCode || 'en'
+i18n.use(initReactI18next).init({
+  lng: 'en',
+
+  // still using the v3 plural suffixes
+  compatibilityJSON: 'v3',
+
+  resources: {
+    en: { translation: en },
+  },
+
+  react: {
+    useSuspense: false,
+    transSupportBasicHtmlNodes: false,
+  },
+
+  interpolation: {
+    prefix: '__',
+    suffix: '__',
+    unescapeSuffix: 'HTML',
+    skipOnVariables: true,
+    escapeValue: false,
+    defaultVariables: {
+      appName: 'Overleaf',
+    },
+  },
+})
+
+// avoid some errors by creating these objects in advance
+window.user = {} as User
+window.ExposedSettings = {} as ExposedSettings
+
+const preview: Preview = {
+  parameters: {
+    // Automatically mark prop-types like onClick, onToggle, etc as Storybook
+    // "actions", so that they are logged in the Actions pane at the bottom of the
+    // viewer
+    actions: { argTypesRegex: '^on.*' },
+    docs: {
+      // render stories in iframes, to isolate modals
+      inlineStories: false,
+    },
+  },
+  globalTypes: {
+    theme: {
+      name: 'Theme',
+      description: 'Editor theme',
+      defaultValue: 'main-',
+      toolbar: {
+        icon: 'circlehollow',
+        items: [
+          { value: 'main-', title: 'Default' },
+          { value: 'main-light-', title: 'Light' },
+          { value: 'main-ieee-', title: 'IEEE' },
+        ],
+      },
+    },
+  },
+  loaders: [
+    async ({ globals }) => {
+      const { theme } = globals
+
+      return {
+        // NOTE: this uses `${theme}style.less` rather than `${theme}.less`
+        // so that webpack only bundles files ending with "style.less"
+        activeStyle: await import(
+          `!!to-string-loader!css-loader!less-loader!../../../services/web/frontend/stylesheets/${theme}style.less`
+        ),
+      }
+    },
+  ],
+  decorators: [
+    (Story, context) => {
+      const { activeStyle } = context.loaded
+
+      return (
+        <>
+          {activeStyle && <style>{activeStyle.default}</style>}
+          <Story {...context} />
+        </>
+      )
+    },
+  ],
+}
+
+export default preview

+ 9 - 0
services/web/Makefile

@@ -552,6 +552,15 @@ tar:
 	COMPOSE_PROJECT_NAME=tar_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) run --rm tar
 	COMPOSE_PROJECT_NAME=tar_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) down -v -t 0
 
+build_storybook:
+	npm run build-storybook
+	if [[ -n "$(BRANCH_NAME)" ]]; then \
+	  echo "Renaming storybook-static -> $(BRANCH_NAME)."; \
+	  d=$$(dirname "$(BRANCH_NAME)"); \
+	  mkdir -p "storybook-output/$$d"; \
+	  mv storybook-static "storybook-output/$$d/$$(basename "$(BRANCH_NAME)")/"; \
+	fi
+
 MODULE_TARGETS = \
 	$(TEST_ACCEPTANCE_MODULES_SAAS) \
 	$(TEST_ACCEPTANCE_MODULES_SERVER_CE) \

+ 78 - 0
services/web/cloudbuild-storybook-index.html.m4

@@ -0,0 +1,78 @@
+divert(`-1')
+define(`foreach', `pushdef(`$1')_foreach($@)popdef(`$1')')
+define(`_arg1', `$1')
+define(`_foreach', `ifelse(`$2', `()', `',
+  `define(`$1', _arg1$2)$3`'$0(`$1', (shift$2), `$3')')')
+divert`'dnl
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>Overleaf Storybook builds</title>
+      <style>
+        body {
+          background-color: #fff;
+          color: #5d6879;
+          font-family: Lato,sans-serif;
+          font-size: 16px;
+          line-height: 1.5625;
+          min-height: 100%;
+          position: relative;
+          margin: 0px;
+        }
+        h1, h2, h3, h4, h5, h6 {
+          color: rgb(80, 80, 80);
+          font-family: Merriweather, serif;
+          font-weight: 500;
+          line-height: 1.35;
+        }
+        h1 {
+          margin-top: 0px;
+          padding-top: 100px;
+        }
+        .navbar-default {
+          background-color: #1e2530;
+          border-color: transparent;
+          height: 68px;
+          padding: 15px 10px;
+          position: absolute;
+          top: 0;
+          width: 100%;
+          box-sizing: border-box;
+          display: block;
+        }
+        .navbar-default .navbar-brand {
+          background-image: url(https://cdn.overleaf.com/images/overleaf-white-65b70e33f35fccdf6f8d.svg);
+          background-position: 0;
+          background-repeat: no-repeat;
+          background-size: contain;
+          bottom: 5px;
+          padding: 0;
+          position: absolute;
+          top: 5px;
+          width: 130px;
+        }
+      </style>
+  </head>
+  <body>
+    <nav class="navbar-default">
+      <a class="navbar-brand" href="https://www.overleaf.com/"></a>
+    </nav>
+    <h1>Overleaf Storybook builds</h1>
+    <h2>Branches:</h2>
+    <ul>
+      foreach(DIR, (LIST),
+      <li>
+        <a href="DIR/">DIR/</a>
+        <a href="https://github.com/overleaf/internal/tree/DIR">
+          <img src="https://github.com/favicon.ico" alt="GitHub" width="15em">
+        </a>
+      </li>
+      )
+    </ul>
+    <small>
+      Last updated on syscmd(date)dnl
+      for ifdef(`BRANCH_NAME',<a href="BRANCH_NAME/">BRANCH_NAME</a>,unknown branch)
+      (<a href="https://console.cloud.google.com/cloud-build/builds?project=overleaf-dev&pageState=(%22builds%22:(%22f%22:%22%255B%257B_22k_22_3A_22Trigger%2520Name_22_2C_22t_22_3A10_2C_22v_22_3A_22_5C_22storybook-pr-trigger_5C_22_22_2C_22s_22_3Atrue_2C_22i_22_3A_22triggerName_22%257D_2C%257B_22k_22_3A_22_22_2C_22t_22_3A10_2C_22v_22_3A_22_5C_22OR_5C_22_22_2C_22o_22_3Atrue_2C_22s_22_3Atrue%257D_2C%257B_22k_22_3A_22Trigger%2520Name_22_2C_22t_22_3A10_2C_22v_22_3A_22_5C_22storybook-push-trigger_5C_22_22_2C_22s_22_3Atrue_2C_22i_22_3A_22triggerName_22%257D%255D%22))">build history</a>).
+    </small>
+  </body>
+</html>

+ 63 - 0
services/web/cloudbuild-storybook.yaml

@@ -0,0 +1,63 @@
+steps:
+  - id: build-storybook
+    name: 'node:18.18.2'
+    env:
+      - 'BRANCH_NAME=$BRANCH_NAME'
+      - 'BUILD_ID=$BUILD_ID'
+    script: |
+      #!/bin/bash
+      set -e
+      make -C services/web BRANCH_NAME="${BRANCH_NAME:-$BUILD_ID}" build_storybook
+
+  - id: deploy-storybook
+    name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
+    env:
+      - 'BRANCH_NAME=$BRANCH_NAME'
+      - 'BUILD_ID=$BUILD_ID'
+      - 'BUCKET=gs://overleaf-dev-storybook'
+    dir: services/web
+    script: |
+      #!/bin/bash
+      : ${BRANCH_NAME:=$BUILD_ID}
+      [[ "$BRANCH_NAME" ]] || {
+        echo 1>&2 "ERROR: BRANCH_NAME not set"
+        exit 2
+      }
+      gsutil -m copy -r storybook-output/* "${BUCKET}/"
+    waitFor:
+      - build-storybook
+
+  - id: read-storybook-bucket
+    name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
+    dir: services/web
+    env:
+      - 'BUCKET=gs://overleaf-dev-storybook'
+    script: |
+      #!/bin/bash
+      set -ex
+      gsutil ls "${BUCKET}/" \
+      | sed -E "s@^${BUCKET}/([^/]+)/@\1@" \
+      > storybook-bucket-listing.txt
+    waitFor:
+      - deploy-storybook
+
+  - id: create-storybook-index
+    name: 'node:18.18.2'
+    dir: services/web
+    env:
+      - 'BRANCH_NAME=$BRANCH_NAME'
+    script: |
+      #!/bin/bash
+      set -ex
+      LIST=$(tr '\n' , < storybook-bucket-listing.txt)
+      m4 -DLIST="$LIST" -DBRANCH_NAME="$BRANCH_NAME" cloudbuild-storybook-index.html.m4 > storybook-index.html
+    waitFor:
+      - read-storybook-bucket
+
+  - id: update-storybook-index
+    name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
+    dir: services/web
+    entrypoint: 'gsutil'
+    args: [ "copy", "storybook-index.html", "gs://overleaf-dev-storybook/index.html" ]
+    waitFor:
+      - create-storybook-index

+ 12 - 0
services/web/package.json

@@ -39,6 +39,8 @@
     "lezer-latex:benchmark": "node scripts/lezer-latex/benchmark.mjs",
     "lezer-latex:benchmark-incremental": "node scripts/lezer-latex/test-incremental-parser.mjs",
     "routes": "bin/routes",
+    "storybook": "storybook dev -p 6006",
+    "build-storybook": "storybook build",
     "local:nodemon": "set -a;. ../../config/dev-environment.env;. ./docker-compose.common.env;. ../../config/local-dev.env;. ./local-dev.env;. ../../config/local.env; set +a; echo $SHARELATEX_CONFIG; WEB_PORT=13000 LISTEN_ADDRESS=0.0.0.0 npm run nodemon",
     "local:webpack": "set -a;. ../../config/dev-environment.env;. ./docker-compose.common.env;. ../../config/local-dev.env;. ./local-dev.env;. ../../config/local.env; set +a; PORT=13808 SHARELATEX_CONFIG=$(pwd)/config/settings.webpack.js npm run webpack",
     "local:test:acceptance:run_dir": "set -a;. $(pwd)/docker-compose.common.env;. $(pwd)/local-test.env; set +a; npm run test:acceptance:run_dir",
@@ -211,6 +213,15 @@
     "@replit/codemirror-indentation-markers": "overleaf/codemirror-indentation-markers#1b1f93c0bcd04293aea6986aa2275185b2c56803",
     "@replit/codemirror-vim": "overleaf/codemirror-vim#07f1b50f4b2e703792da75a29e9e1e479b6b7067",
     "@sentry/browser": "^7.8.1",
+    "@storybook/addon-a11y": "^7.5.1",
+    "@storybook/addon-essentials": "^7.5.1",
+    "@storybook/addon-interactions": "^7.5.1",
+    "@storybook/addon-links": "^7.5.1",
+    "@storybook/addons": "^7.5.1",
+    "@storybook/blocks": "^7.5.1",
+    "@storybook/react": "^7.5.1",
+    "@storybook/react-webpack5": "^7.5.1",
+    "@storybook/testing-library": "^0.2.2",
     "@testing-library/cypress": "^10.0.1",
     "@testing-library/dom": "^9.3.0",
     "@testing-library/react": "^12.1.5",
@@ -332,6 +343,7 @@
     "sinon-chai": "^3.7.0",
     "sinon-mongoose": "^2.3.0",
     "socket.io-mock": "^1.3.1",
+    "storybook": "^7.4.0",
     "strict-event-emitter": "^0.5.1",
     "terser-webpack-plugin": "^5.3.9",
     "thread-loader": "^4.0.2",

+ 1 - 0
services/web/tsconfig.json

@@ -23,6 +23,7 @@
     "modules/**/test/frontend/**/*.*",
     "frontend/stories/**/*.*",
     "modules/**/stories/**/*.*",
+    ".storybook/*.*",
     "cypress",
     "types"
   ],

Неке датотеке нису приказане због велике количине промена