diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 5e17dde..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,111 +0,0 @@ -version: 2.1 - -defaults: &defaults - working_directory: ~/design-system - docker: - - image: circleci/node:12-browsers - environment: - TZ: "/usr/share/zoneinfo/America/Los_Angeles" - -aliases: - # Circle related commands - - &restore-cache - keys: - # Find a cache corresponding to this specific package.json checksum - # when this file is changed, this key will fail - - design-system-{{ checksum "yarn.lock" }}-{{ checksum ".circleci/config.yml" }} - - design-system-{{ checksum "yarn.lock" }} - # Find the most recent cache used from any branch - - design-system- - - &save-cache - key: design-system-{{ checksum "yarn.lock" }}-{{ checksum ".circleci/config.yml" }} - paths: - - ~/.cache/yarn - - node_modules - # Yarn commands - - &yarn - name: Install Dependencies - command: yarn install --frozen-lockfile --non-interactive - - &build - name: Build - command: yarn build - -jobs: - install: - <<: *defaults - steps: - - checkout - - restore_cache: *restore-cache - - run: *yarn - - save_cache: *save-cache - - persist_to_workspace: - root: . - paths: - - . - - build: - <<: *defaults - steps: - - attach_workspace: - at: ~/design-system - - run: *build - - persist_to_workspace: - root: . - paths: - - . - - documentation: - <<: *defaults - steps: - - attach_workspace: - at: ~/design-system - - run: - name: Build Storybook - command: yarn build:storybook - - store_artifacts: - path: ./out - destination: documentation - - run: - name: Post Link to storybook + playroom - command: node ./.circleci/post-build-url.js - - lint: - <<: *defaults - steps: - - attach_workspace: - at: ~/design-system - - run: - name: Lint - command: yarn lint - - release: - <<: *defaults - steps: - - attach_workspace: - at: ~/design-system - - run: mkdir ~/.ssh/ && echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config - - run: - name: Release - command: yarn release - -workflows: - version: 2 - build_and_test: - jobs: - - install - - - build: - requires: - - install - - - lint: - requires: - - build - - - documentation: - requires: - - lint - - - release: - requires: - - documentation diff --git a/.circleci/get-storybook-url.js b/.circleci/get-storybook-url.js deleted file mode 100644 index d1ad0f8..0000000 --- a/.circleci/get-storybook-url.js +++ /dev/null @@ -1,23 +0,0 @@ -const { execSync } = require("child_process"); - -const user = - process.env.CIRCLE_PR_USERNAME || process.env.CIRCLE_PROJECT_USERNAME; -const repo = - process.env.CIRCLE_PR_REPONAME || process.env.CIRCLE_PROJECT_REPONAME; - -module.exports = function () { - const artifacts = JSON.parse( - execSync( - `curl https://circleci.com/api/v1.1/project/github/${user}/${repo}/latest/artifacts?circle-token=$CIRCLE_TOKEN&branch=$CIRCLE_BRANCH`, - { encoding: "utf8" } - ) - ); - - const artifact = artifacts.find((artifact) => - artifact.url.includes("index.html") - ); - - if (artifact) { - return artifact.url; - } -}; diff --git a/.circleci/post-build-url.js b/.circleci/post-build-url.js deleted file mode 100644 index 0ab723a..0000000 --- a/.circleci/post-build-url.js +++ /dev/null @@ -1,27 +0,0 @@ -const { execSync } = require("child_process"); - -const user = - process.env.CIRCLE_PR_USERNAME || process.env.CIRCLE_PROJECT_USERNAME; -const repo = - process.env.CIRCLE_PR_REPONAME || process.env.CIRCLE_PROJECT_REPONAME; - -const artifacts = JSON.parse( - execSync( - `curl https://circleci.com/api/v1.1/project/github/${user}/${repo}/$CIRCLE_BUILD_NUM/artifacts?circle-token=$CIRCLE_TOKEN`, - { encoding: "utf8" } - ) -); - -if (artifacts && Array.isArray(artifacts)) { - const urls = (artifacts).filter((artifact) => - artifact.url.includes("index.html") - ); - const message = `### Build Info\n\nYour PR was successfully deployed by circleCI [#$CIRCLE_BUILD_NUM]($CIRCLE_BUILD_URL)\n\n[Storybook](${urls[0].url})`; - - execSync( - `yarn auto comment --context $CIRCLE_BUILD_URL --message "${message}" || true`, - { - stdio: "inherit", - } - ); -} \ No newline at end of file diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index e78e1d7..0000000 --- a/.eslintrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "@design-systems" -} diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml new file mode 100644 index 0000000..1f6f0d3 --- /dev/null +++ b/.github/workflows/npm-publish.yml @@ -0,0 +1,35 @@ +# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created +# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages + +name: Node.js Package + +on: + pull_request: + types: [opened, synchronize, reopened] + release: + types: [created] + +jobs: + build-publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: "yarn" + - name: Install dependencies + run: yarn install + - name: Test + run: yarn test + - name: Build + run: yarn build + - name: Prepare repository + run: git fetch --unshallow --tags + - name: Release + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + GH_TOKEN: ${{ secrets.GH_TOKEN }} + PR: ${{ steps.PR.outputs.number }} + run: yarn release diff --git a/.github/workflows/storybook-deploy.yml b/.github/workflows/storybook-deploy.yml new file mode 100644 index 0000000..3beb85d --- /dev/null +++ b/.github/workflows/storybook-deploy.yml @@ -0,0 +1,40 @@ +# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created +# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages + +name: Storybook Deployment + +on: + push: + branches: + - "master" + +permissions: + contents: read + pages: write + id-token: write + +jobs: + storybook-publish: + environment: + name: github-pages + url: ${{ steps.build-publish.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: "yarn" + - name: Install + run: yarn install + - name: Build + run: yarn build + - name: Deploy Storybook + uses: bitovi/github-actions-storybook-to-github-pages@v1.0.3 + with: + path: storybook-static + install_command: yarn install + build_command: yarn build:storybook + checkout: false diff --git a/.gitignore b/.gitignore index 8a6a1b6..6b01db8 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ core.* .env .eslintcache tsconfig.tsbuildinfo + +*storybook.log +storybook-static/ \ No newline at end of file diff --git a/.storybook/main.js b/.storybook/main.js deleted file mode 100644 index d953a27..0000000 --- a/.storybook/main.js +++ /dev/null @@ -1,16 +0,0 @@ -// const { -// addons, -// stories, -// webpackFinal -// } = require('@design-systems/storybook/preset'); -// const modifyWebpack = require("./webpack.config.js"); - -// module.exports = { -// stories, -// addons, -// presets: ['@storybook/addon-docs/preset'], -// webpackFinal: async config => modifyWebpack(await webpackFinal(config)) -// }; -module.exports = { - presets: ["@storybook/addon-docs/preset", "@design-systems/storybook/preset"], -}; diff --git a/.storybook/main.ts b/.storybook/main.ts new file mode 100644 index 0000000..2ef41f4 --- /dev/null +++ b/.storybook/main.ts @@ -0,0 +1,26 @@ +import type { StorybookConfig } from "@storybook/react-vite"; +import { join, dirname } from "path"; + +/** + * This function is used to resolve the absolute path of a package. + * It is needed in projects that use Yarn PnP or are set up within a monorepo. + */ +function getAbsolutePath(value: string): any { + return dirname(require.resolve(join(value, "package.json"))); +} +const config: StorybookConfig = { + stories: ["../components/**/*.stories.@(js|jsx|mjs|ts|tsx)"], + core: { + builder: "@storybook/builder-vite", + }, + addons: [ + getAbsolutePath("@storybook/addon-essentials"), + getAbsolutePath("@chromatic-com/storybook"), + getAbsolutePath("@storybook/addon-interactions"), + ], + framework: { + name: getAbsolutePath("@storybook/react-vite"), + options: {}, + }, +}; +export default config; diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html deleted file mode 100644 index 4af002d..0000000 --- a/.storybook/preview-head.html +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/.storybook/preview.js b/.storybook/preview.js deleted file mode 100644 index fdbb5c1..0000000 --- a/.storybook/preview.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from "react"; -import * as dsPreview from "@design-systems/storybook/preview"; - -const compare = (order) => (a, b) => { - let first = order.findIndex((str) => str === a.toLowerCase()); - let second = order.findIndex((str) => str === b.toLowerCase()); - - if (first === -1) first = order.length; - if (second === -1) second = order.length; - - return first < second ? -1 : second < first ? 1 : 0; -}; - -const sectionOrder = ["gallery", "examples", "components"]; - -const sectionCompare = compare(sectionOrder); - -export const decorators = dsPreview.decorators; - -export const parameters = { - ...dsPreview.parameters, - options: { - storySort: ([, a], [, b]) => { - const [aSection, aComponent] = a.kind.split("/"); - const [bSection, bComponent] = b.kind.split("/"); - - if (aSection === bSection) { - // Sort components alphabetically - return aComponent.localeCompare(bComponent); - } - - // Sort sections according to order defined above - return sectionCompare(aSection, bSection); - }, - }, -}; diff --git a/.storybook/preview.ts b/.storybook/preview.ts new file mode 100644 index 0000000..459601f --- /dev/null +++ b/.storybook/preview.ts @@ -0,0 +1,15 @@ +import type { Preview } from "@storybook/react"; +import "./styles.css"; + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + }, +}; + +export default preview; diff --git a/.storybook/styles.css b/.storybook/styles.css new file mode 100644 index 0000000..9c5d6c5 --- /dev/null +++ b/.storybook/styles.css @@ -0,0 +1,5 @@ +* { + font-family: Avenir, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", + "Segoe UI Symbol"; +} diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js deleted file mode 100644 index 78c7841..0000000 --- a/.storybook/webpack.config.js +++ /dev/null @@ -1,30 +0,0 @@ -const webpack = require("webpack"); -const fs = require("fs"); -const { execSync } = require("child_process"); -const path = require("path"); -const HtmlWebpackInsertPlugin = require("html-webpack-insert-text-plugin") - .default; -const { createGallerySpecs } = require("@doc-blocks/gallery/specs"); - -function getSpecs() { - // Return and array of component specs - return [{ name: "Button", description: "A button to go clicky clicky" }]; -} - -module.exports = async ({ config, env }) => { - config.plugins.push(await createGallerySpecs({ specs: getSpecs() })); - - config.plugins.push( - new HtmlWebpackInsertPlugin([ - { - target: "iframe.html", - parent: "head", - text: fs.readFileSync(path.join(__dirname, "preview-head.html"), { - encoding: "utf8", - }), - }, - ]) - ); - - return config; -}; diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index b13ce3c..0000000 --- a/babel.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = (api) => { - api.cache(true); - - return {}; -}; diff --git a/components/Accessibility/package.json b/components/Accessibility/package.json index 66af70d..150ab69 100644 --- a/components/Accessibility/package.json +++ b/components/Accessibility/package.json @@ -1,8 +1,15 @@ { "name": "@doc-blocks/accessibility", "version": "0.8.15", + "type": "module", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", + "exports": { + ".": { + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js" + } + }, "types": "./dist", "repository": "https://github.com/intuit/doc-blocks", "author": "Kendall Gassner kendall.gassner@yahoo.com", @@ -18,22 +25,17 @@ "access": "public" }, "scripts": { - "clean": "ds clean", - "build": "ds build", - "dev": "ds dev", - "storybook": "ds storybook start", - "start": "ds build --watch", - "test": "ds test", - "lint": "ds lint", - "size": "ds size" + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" }, "dependencies": { "@babel/runtime": "^7.11.2", - "@doc-blocks/shield": "link:../Shield", - "emotion": "^10.0.27" + "@doc-blocks/shield": "link:../Shield" }, "peerDependencies": { - "react": ">= 16.8.6" + "react": ">= 18.2.0" }, "files": [ "dist", @@ -43,5 +45,19 @@ "!*.test.*", "!__snapshots__", "!__tests__" - ] + ], + "devDependencies": { + "@eslint/js": "9.25.1", + "@types/node": "22.15.3", + "@types/react": "19.1.2", + "@types/react-dom": "19.1.3", + "@vitejs/plugin-react": "4.4.1", + "eslint": "9.25.1", + "eslint-plugin-react-hooks": "5.2.0", + "eslint-plugin-react-refresh": "0.4.20", + "global": "4.4.0", + "typescript": "5.8.3", + "typescript-eslint": "8.31.1", + "vite": "6.3.4" + } } diff --git a/components/Accessibility/src/Accessibility.stories.mdx b/components/Accessibility/src/Accessibility.stories.mdx deleted file mode 100644 index e2fe5fc..0000000 --- a/components/Accessibility/src/Accessibility.stories.mdx +++ /dev/null @@ -1,16 +0,0 @@ -import { Story, Meta, Canvas } from "@storybook/addon-docs/blocks"; - -import { Accessibility } from "."; -import notes from "../README.md"; - - - -# @doc-blocks/accessibility - -A component that makes displays an accessibility tag. - - - - - - diff --git a/components/Accessibility/src/Accessibility.stories.tsx b/components/Accessibility/src/Accessibility.stories.tsx new file mode 100644 index 0000000..1a3f884 --- /dev/null +++ b/components/Accessibility/src/Accessibility.stories.tsx @@ -0,0 +1,21 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { Accessibility } from "."; + +const meta: Meta = { + title: "Shields/Accessibility", + component: Accessibility, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +}; + +export default meta; +type Story = StoryObj; + +export const BasicUsage: Story = { + args: { + tag: "a11y", + }, +}; diff --git a/components/Accessibility/src/index.tsx b/components/Accessibility/src/index.tsx index 2d0a1a4..d750440 100644 --- a/components/Accessibility/src/index.tsx +++ b/components/Accessibility/src/index.tsx @@ -1,7 +1,6 @@ -import React from "react"; import { Shield } from "@doc-blocks/shield"; -interface AccessibilityProps { +export interface AccessibilityProps { /** The current accessibility tag */ tag?: string; } diff --git a/components/Accessibility/src/vite-env.d.ts b/components/Accessibility/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/components/Accessibility/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/components/Accessibility/tsconfig.build.json b/components/Accessibility/tsconfig.build.json new file mode 100644 index 0000000..154391f --- /dev/null +++ b/components/Accessibility/tsconfig.build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["src/**/*.test.+(ts|tsx)", "src/**/*.stories.+(ts|tsx)"], + "types": ["vite/client"] +} diff --git a/components/Accessibility/tsconfig.json b/components/Accessibility/tsconfig.json index b9c8b59..c818eda 100644 --- a/components/Accessibility/tsconfig.json +++ b/components/Accessibility/tsconfig.json @@ -1,16 +1,9 @@ { "extends": "../../tsconfig.build.json", - "include": ["src/**/*", "../../typings/**/*"], - + "exclude": ["node_modules", "dist", "coverage", "src/**/*.test.+(ts|tsx)"], "compilerOptions": { "outDir": "./dist", - "rootDir": "./src", - "composite": true, - - // Need to override emotion's types because they pollute the global - "baseUrl": ".", - "paths": { - "@emotion/core": ["../../typings/emotion.d.ts"] - } - } + "rootDir": "./src" + }, + "include": ["src/**/*"] } diff --git a/components/Accessibility/vite.config.ts b/components/Accessibility/vite.config.ts new file mode 100644 index 0000000..bc49a97 --- /dev/null +++ b/components/Accessibility/vite.config.ts @@ -0,0 +1,38 @@ +import { defineConfig } from "vite"; +import { resolve } from "path"; +import react from "@vitejs/plugin-react"; +import dts from "vite-plugin-dts"; +import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js"; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + react(), + dts({ + tsconfigPath: "tsconfig.build.json", + }), + cssInjectedByJsPlugin(), + ], + build: { + lib: { + entry: resolve(__dirname, `src/index.tsx`), + formats: ["es", "cjs"], + fileName(format) { + if (format === "es") { + return "esm/[name].js"; + } + + return "cjs/[name].js"; + }, + }, + rollupOptions: { + external: ["react", "react-dom"], + output: { + globals: { + react: "React", + "react-dom": "React-dom", + }, + }, + }, + }, +}); diff --git a/components/Accordion/jest.config.js b/components/Accordion/jest.config.js index 5c282f8..685d67c 100644 --- a/components/Accordion/jest.config.js +++ b/components/Accordion/jest.config.js @@ -5,4 +5,6 @@ module.exports = { ...base, name, displayName: name, + setupFiles: [], + setupFilesAfterEnv: [require.resolve("@testing-library/jest-dom")], }; diff --git a/components/Accordion/package.json b/components/Accordion/package.json index ad53dd0..6c0743b 100644 --- a/components/Accordion/package.json +++ b/components/Accordion/package.json @@ -1,9 +1,16 @@ { "name": "@doc-blocks/accordion", "version": "0.8.15", + "type": "module", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", "types": "./dist", + "exports": { + ".": { + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js" + } + }, "repository": "https://github.com/intuit/doc-blocks", "author": "Sean Powell sean_powell@intuit.com", "contributors": [ @@ -18,23 +25,16 @@ "access": "public" }, "scripts": { - "clean": "ds clean", - "build": "ds build", - "dev": "ds dev", - "storybook": "ds storybook start", - "start": "ds build --watch", - "test": "ds test", - "lint": "ds lint", - "size": "ds size" + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" }, "dependencies": { - "@babel/runtime": "7.11.2", - "@emotion/react": "11.4.1", - "react-is": "^16.8.1", - "styled-components": "5.3.0" + "@babel/runtime": "7.11.2" }, "peerDependencies": { - "react": ">= 16.8.6" + "react": ">= 18.2.0" }, "files": [ "dist", diff --git a/components/Accordion/src/Accordion.stories.tsx b/components/Accordion/src/Accordion.stories.tsx index a9122e9..2d61c8d 100644 --- a/components/Accordion/src/Accordion.stories.tsx +++ b/components/Accordion/src/Accordion.stories.tsx @@ -1,30 +1,38 @@ import React from "react"; +import type { Meta, StoryObj } from "@storybook/react"; +import { fn } from "@storybook/test"; import { Accordion } from "."; -import notes from "../README.md"; -export default { +const meta: Meta = { title: "Components/Accordion", - parameters: { notes }, - argTypes: { onChange: { action: "Selected" } }, + component: Accordion, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + args: { + onChange: fn(), + }, }; -export const BasicUsage = ({ - onChange, -}: { - onChange: (selectedId: string | null) => void; -}) => ( - onChange(selectedId)}> - - Title 1 - - Panel 1 - - Title 2 - - Panel 2 - -); +export default meta; +type Story = StoryObj; + +export const BasicUsage: Story = { + render: ({ onChange }) => ( + onChange?.(selectedId)}> + + Title 1 + + Panel 1 + + Title 2 + + Panel 2 + + ), +}; const Chevron = ({ style }: { style: React.CSSProperties }) => ( }) => ( - spy(selectedId)}> +const AccordionStub = ({ spy }: { spy?: jest.Mock }) => ( + spy?.(selectedId)}> { expect(queryByText("Panel 1")).toHaveClass("panel-1-default"); }); - test("It expands and collapses the accordion panel when selected", () => { - const { queryByText } = render(); + test("It expands and collapses the accordion panel when selected", async () => { + const user = userEvent.setup(); + const { queryByText, getByText } = render(); expect(queryByText("Panel 1")).toHaveClass("collapsed"); expect(queryByText("Panel 1")).not.toHaveClass("expanded"); - userEvent.click(screen.getByText("Title 1")); + await user.click(getByText("Title 1")); expect(queryByText("Panel 1")).toHaveClass("expanded"); expect(queryByText("Panel 1")).not.toHaveClass("collapsed"); - userEvent.click(screen.getByText("Title 1")); + await user.click(getByText("Title 1")); expect(queryByText("Panel 1")).not.toHaveClass("expanded"); expect(queryByText("Panel 1")).toHaveClass("collapsed"); }); - test("It expands and collapses the accordion panel with keyboard events", () => { + test("It expands and collapses the accordion panel with keyboard events", async () => { + const user = userEvent.setup(); const { queryByText } = render(); expect(queryByText("Panel 1")).toHaveClass("collapsed"); expect(queryByText("Panel 1")).not.toHaveClass("expanded"); - userEvent.type(screen.getByText("Title 1"), "{space}"); + await user.type(screen.getByText("Title 1"), "{space}"); expect(queryByText("Panel 1")).toHaveClass("expanded"); expect(queryByText("Panel 1")).not.toHaveClass("collapsed"); - userEvent.type(screen.getByText("Title 1"), "{space}"); + await user.type(screen.getByText("Title 1"), "{space}"); expect(queryByText("Panel 1")).not.toHaveClass("expanded"); expect(queryByText("Panel 1")).toHaveClass("collapsed"); }); - test("It collapses all other panels when another title is selected", () => { + test("It collapses all other panels when another title is selected", async () => { + const user = userEvent.setup(); const { queryByText } = render(); expect(queryByText("Panel 1")).toHaveClass("collapsed"); expect(queryByText("Panel 1")).not.toHaveClass("expanded"); - userEvent.click(screen.getByText("Title 1")); + await user.click(screen.getByText("Title 1")); expect(queryByText("Panel 1")).toHaveClass("expanded"); expect(queryByText("Panel 1")).not.toHaveClass("collapsed"); - userEvent.click(screen.getByText("Title 2")); + await user.click(screen.getByText("Title 2")); expect(queryByText("Panel 1")).not.toHaveClass("expanded"); expect(queryByText("Panel 1")).toHaveClass("collapsed"); @@ -115,29 +118,31 @@ describe("Accordion", () => { expect(queryByText("Panel 2")).toHaveClass("expanded"); }); - test("It toggles the activeClassName to the selected accordion title & panel", () => { + test("It toggles the activeClassName to the selected accordion title & panel", async () => { + const user = userEvent.setup(); const { queryByText } = render(); expect(queryByText("Title 1")).not.toHaveClass("title-1-selected"); expect(queryByText("Panel 1")).not.toHaveClass("panel-1-selected"); - userEvent.click(screen.getByText("Title 1")); + await user.click(screen.getByText("Title 1")); expect(queryByText("Title 1")).toHaveClass("title-1-selected"); expect(queryByText("Panel 1")).toHaveClass("panel-1-selected"); - userEvent.click(screen.getByText("Title 1")); + await user.click(screen.getByText("Title 1")); expect(queryByText("Title 1")).not.toHaveClass("title-1-selected"); expect(queryByText("Panel 1")).not.toHaveClass("panel-1-selected"); }); - test("It calls onChange when a tab is clicked", () => { + test("It calls onChange when a tab is clicked", async () => { + const user = userEvent.setup(); const spy = jest.fn(); const { queryByText } = render(); expect(queryByText("Title 1")).toBeInTheDocument(); expect(queryByText("Title 2")).toBeInTheDocument(); - userEvent.click(screen.getByText("Title 2")); + await user.click(screen.getByText("Title 2")); expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledWith("two"); diff --git a/components/Accordion/src/__snapshots__/Accordion.test.tsx.snap b/components/Accordion/src/__snapshots__/Accordion.test.tsx.snap index 1a438ad..2aae5a0 100644 --- a/components/Accordion/src/__snapshots__/Accordion.test.tsx.snap +++ b/components/Accordion/src/__snapshots__/Accordion.test.tsx.snap @@ -2,38 +2,38 @@ exports[`Accordion It matches the snapshot 1`] = `
-
+
Title 1
Title 2
diff --git a/components/Accordion/src/index.tsx b/components/Accordion/src/index.tsx index bfd99c9..cc8f0c9 100644 --- a/components/Accordion/src/index.tsx +++ b/components/Accordion/src/index.tsx @@ -1,8 +1,7 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import React from "react"; -/** @jsx jsx */ -import { css, jsx } from "@emotion/react"; -interface AccordionCompositionProps +export interface AccordionCompositionProps extends React.DetailedHTMLProps< React.HTMLAttributes, HTMLDivElement @@ -13,9 +12,9 @@ interface AccordionCompositionProps id: string; } -interface AccordionProps { +export interface AccordionProps { /** Children of accordion wrapper */ - children: React.ReactChild[]; + children?: React.ReactNode[]; /** Classes to apply to accordion wrapper **/ className?: string; /** Callback after an accordion panel is expanded */ @@ -43,12 +42,16 @@ export const Accordion = ({ className: accordionWrapperClassName, children, onChange, + ...rest }: AccordionProps) => { const [selectedId, setSelectedId] = React.useState(null); return ( + /* @ts-ignore */ -
{children}
+
+ {children} +
); }; @@ -66,22 +69,35 @@ const Title = ({ return (
{ setSelectedId((prev) => { const selected = prev === id ? null : id; - onChange && onChange(selected); + onChange?.(selected); return selected; }); }} - onKeyPress={() => setSelectedId((prev) => (prev === id ? null : id))} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " " || e.key === "Space") { + e.preventDefault(); + setSelectedId((prev) => { + const selected = prev === id ? null : id; + onChange?.(selected); + return selected; + }); + } + }} > {children}
@@ -102,22 +118,17 @@ const Panel = ({ return (
{children}
diff --git a/components/Accordion/src/vite-env.d.ts b/components/Accordion/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/components/Accordion/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/components/Accordion/tsconfig.build.json b/components/Accordion/tsconfig.build.json new file mode 100644 index 0000000..154391f --- /dev/null +++ b/components/Accordion/tsconfig.build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["src/**/*.test.+(ts|tsx)", "src/**/*.stories.+(ts|tsx)"], + "types": ["vite/client"] +} diff --git a/components/Accordion/tsconfig.json b/components/Accordion/tsconfig.json index 2a35a08..c818eda 100644 --- a/components/Accordion/tsconfig.json +++ b/components/Accordion/tsconfig.json @@ -1,18 +1,9 @@ { "extends": "../../tsconfig.build.json", - "include": ["src/**/*", "../../typings/**/*"], - + "exclude": ["node_modules", "dist", "coverage", "src/**/*.test.+(ts|tsx)"], "compilerOptions": { "outDir": "./dist", - "rootDir": "./src", - "composite": true, - "jsxImportSource": "@emotion/react", - - // Need to override emotion's types because they pollute the global - "baseUrl": ".", - "paths": { - "@emotion/core": ["../../typings/emotion.d.ts"] - }, - "types": ["@emotion/react/types/css-prop"] - } + "rootDir": "./src" + }, + "include": ["src/**/*"] } diff --git a/components/Accordion/vite.config.ts b/components/Accordion/vite.config.ts new file mode 100644 index 0000000..bc49a97 --- /dev/null +++ b/components/Accordion/vite.config.ts @@ -0,0 +1,38 @@ +import { defineConfig } from "vite"; +import { resolve } from "path"; +import react from "@vitejs/plugin-react"; +import dts from "vite-plugin-dts"; +import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js"; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + react(), + dts({ + tsconfigPath: "tsconfig.build.json", + }), + cssInjectedByJsPlugin(), + ], + build: { + lib: { + entry: resolve(__dirname, `src/index.tsx`), + formats: ["es", "cjs"], + fileName(format) { + if (format === "es") { + return "esm/[name].js"; + } + + return "cjs/[name].js"; + }, + }, + rollupOptions: { + external: ["react", "react-dom"], + output: { + globals: { + react: "React", + "react-dom": "React-dom", + }, + }, + }, + }, +}); diff --git a/components/BundleSize/package.json b/components/BundleSize/package.json index 152b48e..49aa80b 100644 --- a/components/BundleSize/package.json +++ b/components/BundleSize/package.json @@ -1,9 +1,16 @@ { "name": "@doc-blocks/bundle-size", "version": "0.8.15", + "type": "module", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", "types": "./dist", + "exports": { + ".": { + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js" + } + }, "repository": "https://github.com/intuit/doc-blocks", "author": "Andrew Lisowski lisowski54@gmail.com", "contributors": [ @@ -18,22 +25,17 @@ "access": "public" }, "scripts": { - "clean": "ds clean", - "build": "ds build", - "dev": "ds dev", - "storybook": "ds storybook start", - "start": "ds build --watch", - "test": "ds test", - "lint": "ds lint", - "size": "ds size" + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" }, "dependencies": { "@babel/runtime": "^7.11.2", - "@doc-blocks/shield": "link:../Shield", - "emotion": "^10.0.27" + "@doc-blocks/shield": "link:../Shield" }, "peerDependencies": { - "react": ">= 16.8.6" + "react": ">= 18.2.0" }, "files": [ "dist", diff --git a/components/BundleSize/src/BundleSize.stories.mdx b/components/BundleSize/src/BundleSize.stories.mdx deleted file mode 100644 index da93926..0000000 --- a/components/BundleSize/src/BundleSize.stories.mdx +++ /dev/null @@ -1,16 +0,0 @@ -import { Story, Meta, Canvas } from "@storybook/addon-docs/blocks"; - -import { BundleSize } from "."; -import notes from "../README.md"; - - - -# @doc-blocks/bundle-size - -A component that makes displays an bundle-size tag. - - - - - - diff --git a/components/BundleSize/src/BundleSize.stories.tsx b/components/BundleSize/src/BundleSize.stories.tsx new file mode 100644 index 0000000..72e1938 --- /dev/null +++ b/components/BundleSize/src/BundleSize.stories.tsx @@ -0,0 +1,21 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { BundleSize } from "./index"; + +const meta: Meta = { + title: "Shields/Bundle Size", + component: BundleSize, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +}; + +export default meta; +type Story = StoryObj; + +export const BasicUsage: Story = { + args: { + size: "1.2 kB", + }, +}; diff --git a/components/BundleSize/src/index.tsx b/components/BundleSize/src/index.tsx index 48d103b..9984aff 100644 --- a/components/BundleSize/src/index.tsx +++ b/components/BundleSize/src/index.tsx @@ -1,7 +1,7 @@ import React from "react"; import { Shield } from "@doc-blocks/shield"; -interface BundleSizeProps { +export interface BundleSizeProps { /** The current BundleSize */ size: string; } diff --git a/components/BundleSize/src/vite-env.d.ts b/components/BundleSize/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/components/BundleSize/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/components/BundleSize/tsconfig.build.json b/components/BundleSize/tsconfig.build.json new file mode 100644 index 0000000..154391f --- /dev/null +++ b/components/BundleSize/tsconfig.build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["src/**/*.test.+(ts|tsx)", "src/**/*.stories.+(ts|tsx)"], + "types": ["vite/client"] +} diff --git a/components/BundleSize/tsconfig.json b/components/BundleSize/tsconfig.json index b9c8b59..c818eda 100644 --- a/components/BundleSize/tsconfig.json +++ b/components/BundleSize/tsconfig.json @@ -1,16 +1,9 @@ { "extends": "../../tsconfig.build.json", - "include": ["src/**/*", "../../typings/**/*"], - + "exclude": ["node_modules", "dist", "coverage", "src/**/*.test.+(ts|tsx)"], "compilerOptions": { "outDir": "./dist", - "rootDir": "./src", - "composite": true, - - // Need to override emotion's types because they pollute the global - "baseUrl": ".", - "paths": { - "@emotion/core": ["../../typings/emotion.d.ts"] - } - } + "rootDir": "./src" + }, + "include": ["src/**/*"] } diff --git a/components/BundleSize/vite.config.ts b/components/BundleSize/vite.config.ts new file mode 100644 index 0000000..bc49a97 --- /dev/null +++ b/components/BundleSize/vite.config.ts @@ -0,0 +1,38 @@ +import { defineConfig } from "vite"; +import { resolve } from "path"; +import react from "@vitejs/plugin-react"; +import dts from "vite-plugin-dts"; +import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js"; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + react(), + dts({ + tsconfigPath: "tsconfig.build.json", + }), + cssInjectedByJsPlugin(), + ], + build: { + lib: { + entry: resolve(__dirname, `src/index.tsx`), + formats: ["es", "cjs"], + fileName(format) { + if (format === "es") { + return "esm/[name].js"; + } + + return "cjs/[name].js"; + }, + }, + rollupOptions: { + external: ["react", "react-dom"], + output: { + globals: { + react: "React", + "react-dom": "React-dom", + }, + }, + }, + }, +}); diff --git a/components/DesignSpec/package.json b/components/DesignSpec/package.json index e77a763..44da1a6 100644 --- a/components/DesignSpec/package.json +++ b/components/DesignSpec/package.json @@ -1,9 +1,16 @@ { "name": "@doc-blocks/design-spec", "version": "0.8.15", + "type": "module", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", "types": "./dist", + "exports": { + ".": { + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js" + } + }, "repository": "https://github.com/intuit/doc-blocks", "author": "Andrew Lisowski lisowski54@gmail.com", "contributors": [ @@ -18,22 +25,17 @@ "access": "public" }, "scripts": { - "clean": "ds clean", - "build": "ds build", - "dev": "ds dev", - "storybook": "ds storybook start", - "start": "ds build --watch", - "test": "ds test", - "lint": "ds lint", - "size": "ds size" + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" }, "dependencies": { "@babel/runtime": "^7.11.2", - "@doc-blocks/shield": "link:../Shield", - "emotion": "^10.0.27" + "@doc-blocks/shield": "link:../Shield" }, "peerDependencies": { - "react": ">= 16.8.6" + "react": ">= 18.2.0" }, "files": [ "dist", diff --git a/components/DesignSpec/src/DesignSpec.stories.mdx b/components/DesignSpec/src/DesignSpec.stories.mdx deleted file mode 100644 index a846b0a..0000000 --- a/components/DesignSpec/src/DesignSpec.stories.mdx +++ /dev/null @@ -1,22 +0,0 @@ -import { Story, Meta, Canvas } from "@storybook/addon-docs/blocks"; - -import { DesignSpec } from "."; -import notes from "../README.md"; - - - -# @doc-blocks/design-spec - -A component that makes displays a link to a design spec. - - - - -
- -
- -
- - - diff --git a/components/DesignSpec/src/DesignSpec.stories.tsx b/components/DesignSpec/src/DesignSpec.stories.tsx new file mode 100644 index 0000000..b2cfad1 --- /dev/null +++ b/components/DesignSpec/src/DesignSpec.stories.tsx @@ -0,0 +1,22 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { DesignSpec } from "."; + +const meta: Meta = { + title: "Shields/Design Spec", + component: DesignSpec, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +}; + +export default meta; +type Story = StoryObj; + +export const BasicUsage: Story = { + args: { + type: "figma", + url: "http://figma.com/", + }, +}; diff --git a/components/DesignSpec/src/vite-env.d.ts b/components/DesignSpec/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/components/DesignSpec/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/components/DesignSpec/tsconfig.build.json b/components/DesignSpec/tsconfig.build.json new file mode 100644 index 0000000..154391f --- /dev/null +++ b/components/DesignSpec/tsconfig.build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["src/**/*.test.+(ts|tsx)", "src/**/*.stories.+(ts|tsx)"], + "types": ["vite/client"] +} diff --git a/components/DesignSpec/tsconfig.json b/components/DesignSpec/tsconfig.json index b9c8b59..c818eda 100644 --- a/components/DesignSpec/tsconfig.json +++ b/components/DesignSpec/tsconfig.json @@ -1,16 +1,9 @@ { "extends": "../../tsconfig.build.json", - "include": ["src/**/*", "../../typings/**/*"], - + "exclude": ["node_modules", "dist", "coverage", "src/**/*.test.+(ts|tsx)"], "compilerOptions": { "outDir": "./dist", - "rootDir": "./src", - "composite": true, - - // Need to override emotion's types because they pollute the global - "baseUrl": ".", - "paths": { - "@emotion/core": ["../../typings/emotion.d.ts"] - } - } + "rootDir": "./src" + }, + "include": ["src/**/*"] } diff --git a/components/DesignSpec/vite.config.ts b/components/DesignSpec/vite.config.ts new file mode 100644 index 0000000..bc49a97 --- /dev/null +++ b/components/DesignSpec/vite.config.ts @@ -0,0 +1,38 @@ +import { defineConfig } from "vite"; +import { resolve } from "path"; +import react from "@vitejs/plugin-react"; +import dts from "vite-plugin-dts"; +import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js"; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + react(), + dts({ + tsconfigPath: "tsconfig.build.json", + }), + cssInjectedByJsPlugin(), + ], + build: { + lib: { + entry: resolve(__dirname, `src/index.tsx`), + formats: ["es", "cjs"], + fileName(format) { + if (format === "es") { + return "esm/[name].js"; + } + + return "cjs/[name].js"; + }, + }, + rollupOptions: { + external: ["react", "react-dom"], + output: { + globals: { + react: "React", + "react-dom": "React-dom", + }, + }, + }, + }, +}); diff --git a/components/Gallery/CHANGELOG.md b/components/Gallery/CHANGELOG.md deleted file mode 100644 index 50f9113..0000000 --- a/components/Gallery/CHANGELOG.md +++ /dev/null @@ -1,84 +0,0 @@ -# v0.8.3 (Mon May 24 2021) - -#### 🐛 Bug Fix - -- Bump Storybook to 6.2.9 [#9](https://github.com/intuit/doc-blocks/pull/9) ([@kendallgassner](https://github.com/kendallgassner)) -- lint ([@kendallgassner](https://github.com/kendallgassner)) - -#### Authors: 1 - -- Kendall Gassner ([@kendallgassner](https://github.com/kendallgassner)) - ---- - -# v0.8.2 (Thu Nov 05 2020) - -#### 🐛 Bug Fix - -- Fix for wider characters in alphabet nav [#7](https://github.com/intuit/doc-blocks/pull/7) ([@kharrop](https://github.com/kharrop)) -- reverting accidental change ([@kharrop](https://github.com/kharrop)) -- Fix for wider characters in alphabet nav ([@kharrop](https://github.com/kharrop)) - -#### Authors: 1 - -- Kelly Harrop ([@kharrop](https://github.com/kharrop)) - ---- - -# v0.8.1 (Mon Nov 02 2020) - -#### 🐛 Bug Fix - -- Gallery updates [#6](https://github.com/intuit/doc-blocks/pull/6) ([@kharrop](https://github.com/kharrop)) -- replace margin with padding ([@kharrop](https://github.com/kharrop)) -- extra margin for scroll ([@kharrop](https://github.com/kharrop)) -- adding link to demo ([@kharrop](https://github.com/kharrop)) -- gallery updates ([@kharrop](https://github.com/kharrop)) - -#### Authors: 1 - -- Kelly Harrop ([@kharrop](https://github.com/kharrop)) - ---- - -# v0.8.0 (Wed Oct 28 2020) - -#### 🚀 Enhancement - -- allow for regex in match path [#5](https://github.com/intuit/doc-blocks/pull/5) ([@hipstersmoothie](https://github.com/hipstersmoothie)) - -#### 🐛 Bug Fix - -- render just the kind ([@hipstersmoothie](https://github.com/hipstersmoothie)) -- allow for regex in match path ([@hipstersmoothie](https://github.com/hipstersmoothie)) -- more docs for gallery props ([@hipstersmoothie](https://github.com/hipstersmoothie)) -- add gallery prop docs ([@hipstersmoothie](https://github.com/hipstersmoothie)) - -#### 📝 Documentation - -- more docs for gallery props [#4](https://github.com/intuit/doc-blocks/pull/4) ([@hipstersmoothie](https://github.com/hipstersmoothie)) -- add gallery prop docs [#3](https://github.com/intuit/doc-blocks/pull/3) ([@hipstersmoothie](https://github.com/hipstersmoothie)) - -#### Authors: 1 - -- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) - ---- - -# v0.7.0 (Mon Oct 19 2020) - -#### 🚀 Enhancement - -- Add Gallery component [#2](https://github.com/intuit/doc-blocks/pull/2) ([@hipstersmoothie](https://github.com/hipstersmoothie)) - -#### 🐛 Bug Fix - -- make component dir configurable for getOverviewSpecs ([@hipstersmoothie](https://github.com/hipstersmoothie)) -- split up webpack code and component code ([@hipstersmoothie](https://github.com/hipstersmoothie)) -- upgrade prettier ([@hipstersmoothie](https://github.com/hipstersmoothie)) -- make component title link story configurable and fall back to first story ([@hipstersmoothie](https://github.com/hipstersmoothie)) -- Add Gallery component ([@hipstersmoothie](https://github.com/hipstersmoothie)) - -#### Authors: 1 - -- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie)) diff --git a/components/Gallery/README.md b/components/Gallery/README.md deleted file mode 100644 index ba8a96c..0000000 --- a/components/Gallery/README.md +++ /dev/null @@ -1,142 +0,0 @@ -# @doc-blocks/gallery - -The `Gallery` component lets you easily showcase an example from each on of your components. - -[Demo](https://intuit.github.io/doc-blocks/?path=/story/gallery--page) - -## Installation - -```sh -npm i @doc-blocks/gallery -# or with yarn -yarn add @doc-blocks/gallery -``` - -## Usage - -### Create the Gallery page - -Then create an MDX only story that renders the `Gallery` component. - -```md -import { Meta } from '@storybook/addon-docs/blocks'; -import { Gallery } from '@doc-blocks/gallery'; - - - -# Gallery - -A showcase of frequently-used components. - - -``` - -## Props - -All of the props are optional. - -### Component props - -- `excludedComponents` - Component names to exclude from the gallery -- `matchPath` - Storybook folder path or regex that looks for stories to generate components (ex: `Features`) -- `titleStory` - Story to make the component title link to - -### Story name props - -Props that determine which stories appear under each component, allowing users to quickly navigate pages without opening folders in the sidebar. - -- `includedStoryNames` - Story names to include from the gallery (default: [`Basic`]) -- `excludedStoryNames` - Story names to exclude from the gallery - -### Add to webpack - -If you want the components to have a description and link to the design spec add the following to your storybook's webpack configuration. - -You must provide a function that will gather information about your components that the `Gallery` component uses to create the gallery. -This function should return an array of component specs that have the following data: - -- `name` (required) -- `description` -- `type` -- `url` - -```js -const { createGallerySpecs } = require("@doc-blocks/gallery/specs"); - -function getSpecs() { - // Return and array of component specs - return [{ name: "Button", description: "A button to go clicky clicky" }]; -} - -module.exports = async (config) => { - config.plugins.push(await createGallerySpecs({ specs: getSpecs() })); - return config; -}; -``` - -## Intuit's `getOverviewSpecs` - -This package also includes `getOverviewSpecs` which depends on the way we structure our stories. -This works wonderfully with [`@design-systems/cli`](https://github.com/intuit/design-systems-cli/) and [`@doc-blocks/design-spec`]. -If the component follows the structure we define it is automatically included in the `Gallery` without any other configuration. - -**Structure:** - -1. All of your components are located in a directory named `components/` -2. Each component has a MDX only entry point named `Overview.stories.mdx` - -For each `Overview.stories.mdx` that is found `createGallerySpecs` will gather the following information: - -- `name` - The name of the component defined in `Meta.title` -- `description` - The first sentence from the component's `README.md` -- `type` - The type of design spec defined in the `DesignSpec` component -- `url` - The url of design spec defined in the `DesignSpec` component - -**Example `Overview.stories.mdx`** - -```md -import { Meta, Description, Title } from '@storybook/addon-docs/blocks'; -import { Version, RelatedComponents, ShieldRow, DesignSpec, BundleSize } from 'storybook-doc-blocks'; - -import notes from '../../README.md'; -import { version } from '../../package.json'; -import BadgeDocs from './Badge.mdx'; - - - -@cgds/badge - ---- - - - - - - - - - - - - -``` - -Just modify the webpack configuration to use this function: - -```js -const { - createGallerySpecs, - getOverviewSpecs, -} = require("@doc-blocks/gallery/specs"); - -module.exports = async (config) => { - config.plugins.push( - createGallerySpecs({ - specs: await getOverviewSpecs({ - componentDirectory: path.join(__dirname, "../components"), - }), - }) - ); - return config; -}; -``` diff --git a/components/Gallery/jest.config.js b/components/Gallery/jest.config.js deleted file mode 100644 index 5c282f8..0000000 --- a/components/Gallery/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -const base = require("@design-systems/test/jest.config.base"); -const { name } = require("./package.json"); - -module.exports = { - ...base, - name, - displayName: name, -}; diff --git a/components/Gallery/package.json b/components/Gallery/package.json deleted file mode 100644 index 1e5c989..0000000 --- a/components/Gallery/package.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "name": "@doc-blocks/gallery", - "version": "0.8.15", - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.js", - "types": "./dist", - "repository": "https://github.com/intuit/doc-blocks", - "author": "Andrew Lisowski lisowski54@gmail.com", - "contributors": [ - "Andrew Lisowski lisowski54@gmail.com" - ], - "license": "MIT", - "sideEffects": [ - "*.css" - ], - "publishConfig": { - "registry": "https://registry.npmjs.org/", - "access": "public" - }, - "scripts": { - "clean": "ds clean", - "build": "ds build", - "dev": "ds dev", - "storybook": "ds storybook start", - "start": "ds build --watch", - "test": "ds test", - "lint": "ds lint", - "size": "ds size" - }, - "dependencies": { - "@babel/runtime": "7.11.2", - "@doc-blocks/design-spec": "link:../DesignSpec", - "@doc-blocks/row": "link:../Row", - "@doc-blocks/shield": "link:../Shield", - "@doc-blocks/shield-row": "link:../ShieldRow", - "fast-glob": "^3.1.1", - "use-isomorphic-layout-effect": "1.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.6", - "webpack": ">= 4.x" - }, - "files": [ - "dist", - "src", - "specs.js", - "specs.d.ts", - "!*.snippet.*", - "!*theme.*", - "!*.test.*", - "!__snapshots__", - "!__tests__" - ] -} diff --git a/components/Gallery/specs.d.ts b/components/Gallery/specs.d.ts deleted file mode 100644 index c6cea5f..0000000 --- a/components/Gallery/specs.d.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./dist/get-specs"; diff --git a/components/Gallery/specs.js b/components/Gallery/specs.js deleted file mode 100644 index 4770dfa..0000000 --- a/components/Gallery/specs.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require("./dist/cjs/get-specs"); diff --git a/components/Gallery/src/Gallery.stories.mdx b/components/Gallery/src/Gallery.stories.mdx deleted file mode 100644 index df218a3..0000000 --- a/components/Gallery/src/Gallery.stories.mdx +++ /dev/null @@ -1,14 +0,0 @@ -import { Meta } from "@storybook/addon-docs/blocks"; - -import { Gallery } from "."; -import notes from "../README.md"; - - - -# @doc-blocks/gallery - -The `Gallery` component lets you easily showcase an example from each on of your components. - -[View the documentation.](https://github.com/intuit/doc-blocks/blob/master/components/Gallery/README.md) - - diff --git a/components/Gallery/src/get-specs.ts b/components/Gallery/src/get-specs.ts deleted file mode 100644 index f7ad9e7..0000000 --- a/components/Gallery/src/get-specs.ts +++ /dev/null @@ -1,65 +0,0 @@ -import fs from "fs"; -import path from "path"; -import glob from "fast-glob"; -import webpack from "webpack"; - -import { ComponentSpec } from "./types"; - -interface GetOverviewSpecsOptions { - /** The directory all of your components live in */ - componentDirectory: string; -} - -/** Gather all of the design specs */ -export const getOverviewSpecs = async ({ - componentDirectory, -}: GetOverviewSpecsOptions): Promise => { - const overviews = await glob( - path.resolve(path.join(componentDirectory, "**/Overview.stories.mdx")) - ); - - return overviews - .map((overview) => { - const contents = fs.readFileSync(overview, { encoding: "utf-8" }); - const [, componentName] = - overview.match(/\/components\/([^\\/]+)\//) || []; - - const [, title] = - contents.match(/title=['"]\S+\/([^\\/]+)\/Overview/) || []; - const [, type, url] = - contents.match(/ - Boolean(spec) - ); -}; - -interface DesignSpecsPluginOptions { - /** A function that gets all of the components spec information for the Gallery to use */ - specs: () => ComponentSpec[]; -} - -/** Create the webpack plugin that provides the design specs */ -export const createGallerySpecs = ({ specs }: DesignSpecsPluginOptions) => { - return new webpack.DefinePlugin({ - "process.env": { - DESIGN_SPECS: JSON.stringify(specs), - }, - }); -}; diff --git a/components/Gallery/src/index.tsx b/components/Gallery/src/index.tsx deleted file mode 100644 index 5df05de..0000000 --- a/components/Gallery/src/index.tsx +++ /dev/null @@ -1,314 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ - -import React from "react"; -import { DesignSpec } from "@doc-blocks/design-spec"; -import { Row } from "@doc-blocks/row"; -import { Shield } from "@doc-blocks/shield"; -import { ShieldRow } from "@doc-blocks/shield-row"; -import { Story, Canvas } from "@storybook/addon-docs/blocks"; -import styled from "@emotion/styled"; -import LinkTo from "@storybook/addon-links/dist/esm/react"; -import Markdown from "markdown-to-jsx"; -import { css } from "emotion"; -import useLayoutEffect from "use-isomorphic-layout-effect"; - -import { StorybookReference } from "./story-reference"; -import { ComponentSpec } from "./types"; - -export const StoryWrapper = styled.div` - display: flex; - flex-wrap: wrap; - margin-top: 40px; - - a { - background-color: #eceef1; - border-radius: 4px; - color: #393a3d; - margin-bottom: 12px; - margin-right: 12px; - padding: 6px 8px; - text-decoration: none; - - &:hover, - &:focus { - background-color: #e3e5e8; - } - } -`; - -let stories: Kind[]; - -interface Story { - /** The name of a story */ - name: string; -} - -interface Kind { - /** The kind of story */ - kind: string; - /** the stories in the kind */ - stories: Story[]; -} - -/** Get all the stories in the storybook */ -const getStories = () => { - if (!stories) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any, no-underscore-dangle - stories = (window as any).__STORYBOOK_CLIENT_API__.getStorybook(); - } - - return stories; -}; - -interface StoryInclusionProps { - /** Story to make the component title link to */ - titleStory?: string; - /** Story name to include as the story in the gallery */ - includedStoryNames?: string[]; - /** Story name to not include as the story in the gallery */ - excludedStoryNames?: string[]; - /** Storybook folder path that matched stories should live in. */ - matchPath?: string; -} - -interface GalleryItemProps extends Exclude { - /** The name of the component to display a gallery for */ - name: string; - /** The kind to display a gallery item for */ - kind: Kind; -} - -/** A component showcase */ -export const GalleryItem = ({ - name, - includedStoryNames = ["Basic"], - excludedStoryNames = [], - kind, - titleStory, -}: GalleryItemProps) => { - const firstStory = - kind.stories.find((s) => includedStoryNames.includes(s.name)) || - kind.stories.find((s) => !excludedStoryNames.includes(s.name)) || - kind.stories[0]; - - const firstStoryId = `${kind.kind - .replace(/\//g, "-") - .replace(/\s+-\s+/g, "-") - .replace(/\s/g, "-") - .toLowerCase()}--${firstStory.name.replace(/[\s\\/]/g, "-").toLowerCase()}`; - - const designSpec = ((process.env - .DESIGN_SPECS as any) as ComponentSpec[]).find( - (spec) => spec.name === name - ); - const category = kind.kind.split("/")[0]; - const renderShieldRow = - (designSpec?.type && designSpec.url) || - category === "Utilities" || - category === "Experimental"; - - return ( - -
-

- - {name} - -

- - {renderShieldRow && ( - - {designSpec?.type && designSpec.url && ( - - )} - {category === "Utilities" && ( - - )} - {category === "Experimental" && ( - - )} - - )} - - {designSpec?.description && ( -

- {designSpec.description} -

- )} - - {kind.stories.map((story) => ( - - {story.name} - - ))} - -
- - - - -
- ); -}; - -export const Alphabet = styled.div` - position: sticky; - top: 24px; - max-height: calc(100vh - 50px); - overflow-y: auto; - margin-top: 40px; - padding-right: 8px; -`; - -interface CharacterProps { - /** Whether the character is disabled */ - disabled: boolean; -} - -export const Character = styled.div` - box-sizing: content-box; - color: #6b6c72; - display: grid; - grid-gap: 8px; - align-items: center; - justify-content: center; - text-align: center; - width: 1rem; - padding: 4px; - line-height: 1.4rem; - height: 1.4rem; - font-size: 1rem; - cursor: pointer; - transition: font-size 0.1s ease-in; - - ${(props: CharacterProps) => - props.disabled - ? `cursor: not-allowed; - opacity: .4; - ` - : ` - &:hover, - &:focus { - color: #1e1e1e; - font-size: 1.3rem; - } - `} -`; - -interface AlphabetNavigationProps { - /** All the stories in the gallery */ - stories: Kind[]; -} - -/** Jump between gallery items */ -const AlphabetNavigation = (props: AlphabetNavigationProps) => { - const chars = React.useMemo(() => { - const usedCharacters = new Set( - props.stories.map((story) => { - return story.kind.split("/")[1][0]; - }) - ); - const items: React.ReactNode[] = []; - - for (let charCode = 65; charCode <= 90; charCode++) { - const char = String.fromCharCode(charCode); - - items.push( - - document - .querySelector(`.${char}`) - ?.scrollIntoView({ behavior: "smooth" }) - } - > - {char} - - ); - } - - return items; - }, [props.stories]); - - return ( -
- {chars} -
- ); -}; - -interface GalleryProps extends StoryInclusionProps { - /** Components to not include in the gallery */ - excludedComponents?: string[]; -} - -/** Show all possible gallery items */ -export const Gallery = ({ - excludedComponents = [], - excludedStoryNames, - includedStoryNames, - matchPath = "", - titleStory, -}: GalleryProps) => { - const [allStories, setAllStories] = React.useState([]); - const items = React.useMemo(() => { - return allStories - .sort((a, b) => a.kind.split("/")[1].localeCompare(b.kind.split("/")[1])) - .map((component) => { - const name = component.kind.split("/")[1]; - return ( - - ); - }); - }, [allStories, includedStoryNames, excludedStoryNames, titleStory]); - - useLayoutEffect(() => { - setAllStories( - getStories().filter( - (item) => - (item.kind.endsWith(matchPath) || - item.kind.match(new RegExp(matchPath))) && - !excludedComponents.some((i) => item.kind.includes(i)) - ) - ); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( -
-
{items}
- -
- ); -}; diff --git a/components/Gallery/src/story-reference.tsx b/components/Gallery/src/story-reference.tsx deleted file mode 100644 index 2074c8b..0000000 --- a/components/Gallery/src/story-reference.tsx +++ /dev/null @@ -1,57 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -/* eslint-disable import/no-extraneous-dependencies */ - -import React from "react"; -import { hrefTo, navigate } from "@storybook/addon-links/dist/esm/preview"; -import { Link } from "@storybook/components"; -import { Element } from "@design-systems/utils"; - -interface StorybookLinkProps extends Element<"a"> { - /** Story kind to link to */ - kind: string; - /** Story title to link to */ - story: string; -} - -/** Link to a part of storybook using a cgds link */ -export const StorybookReference = ({ - kind, - story, - style = {}, - ...html -}: StorybookLinkProps) => { - return ( - // @ts-ignore - // eslint-disable-next-line jsx-a11y/anchor-has-content - { - if (e.metaKey) { - hrefTo(kind, story).then((resolvedHref: any) => { - window.open( - resolvedHref - .replace("?id=", "?path=/story/") - .replace("iframe", "index") - .replace("--", "-") - .replace("&viewMode=story", "--page") - ); - }); - - return; - } - - e.preventDefault(); - navigate({ kind, story }); - }} - /> - ); -}; - -/** Link to a part of storybook using a cgds link */ -export const StorybookLink = (props: StorybookLinkProps) => ( - // @ts-ignore - -); diff --git a/components/Gallery/src/types.ts b/components/Gallery/src/types.ts deleted file mode 100644 index dac1965..0000000 --- a/components/Gallery/src/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface ComponentSpec { - /** The name of the component */ - name: string; - /** The description of the component */ - description?: string; - /** The type of design spec */ - type?: string; - /** The url to the design spec */ - url?: string; -} diff --git a/components/Gallery/tsconfig.json b/components/Gallery/tsconfig.json deleted file mode 100644 index b9c8b59..0000000 --- a/components/Gallery/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": "../../tsconfig.build.json", - "include": ["src/**/*", "../../typings/**/*"], - - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src", - "composite": true, - - // Need to override emotion's types because they pollute the global - "baseUrl": ".", - "paths": { - "@emotion/core": ["../../typings/emotion.d.ts"] - } - } -} diff --git a/components/Guideline/package.json b/components/Guideline/package.json index 97f9cc2..30b8040 100644 --- a/components/Guideline/package.json +++ b/components/Guideline/package.json @@ -1,9 +1,16 @@ { "name": "@doc-blocks/guideline", "version": "0.8.15", + "type": "module", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", "types": "./dist", + "exports": { + ".": { + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js" + } + }, "repository": "https://github.com/intuit/doc-blocks", "author": "Andrew Lisowski lisowski54@gmail.com", "contributors": [ @@ -18,24 +25,16 @@ "access": "public" }, "scripts": { - "clean": "ds clean", - "build": "ds build", - "dev": "ds dev", - "storybook": "ds storybook start", - "start": "ds build --watch", - "test": "ds test", - "lint": "ds lint", - "size": "ds size" + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" }, "dependencies": { - "@babel/runtime": "^7.11.2", - "@design-systems/utils": "2.17.5", - "@emotion/core": "^10.0.28", - "@emotion/styled": "^10.0.27", - "emotion": "^10.0.27" + "@babel/runtime": "^7.11.2" }, "peerDependencies": { - "react": ">= 16.8.6" + "react": ">= 18.2.0" }, "devDependencies": { "@doc-blocks/row": "link:../Row" diff --git a/components/Guideline/src/Guideline.stories.mdx b/components/Guideline/src/Guideline.stories.mdx deleted file mode 100644 index f97ce04..0000000 --- a/components/Guideline/src/Guideline.stories.mdx +++ /dev/null @@ -1,47 +0,0 @@ -import { Story, Meta, Canvas } from "@storybook/addon-docs/blocks"; -import { Button } from "@storybook/react/demo"; -import { Row } from "@doc-blocks/row"; - -import { Do, Dont } from "."; -import notes from "../README.md"; - - - -# @doc-blocks/guideline - -A component to display the `Do`'s and `Don't`s of you component. - -The `Do` guideline should show the right way to use the component., - - - - - - - - - -The `Don't` guideline should show the wrong way to use the component. - - - - - - - - - -Used with `@cgds/row` you can display a pair of `Do/Don't` on one line. - - - - - - - - - - - - - diff --git a/components/Guideline/src/Guideline.stories.tsx b/components/Guideline/src/Guideline.stories.tsx new file mode 100644 index 0000000..3a0d127 --- /dev/null +++ b/components/Guideline/src/Guideline.stories.tsx @@ -0,0 +1,57 @@ +import React from "react"; +import type { Meta, StoryObj } from "@storybook/react"; +import { Row } from "@doc-blocks/row"; + +import { Do, Dont } from "."; + +const meta: Meta = { + title: "Components/Guideline", + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +}; + +export default meta; +type Story = StoryObj; + +/** + * The `Do` guideline should show the right way to use the component. + */ +export const DoExample: Story = { + name: "Do", + render: () => ( + + + + ), +}; + +/** + * The `Don't` guideline should show the wrong way to use the component. + */ +export const DontExample: Story = { + name: "Dont", + render: () => ( + + + + ), +}; + +/** + * Used with `@doc-blocks/row` you can display a pair of `Do/Don't` on one line. + */ +export const DoAndDontRow: Story = { + name: "A Row of Do/Dont", + render: () => ( + + + + + + + + + ), +}; diff --git a/components/Guideline/src/index.tsx b/components/Guideline/src/index.tsx index faa758d..5268e96 100644 --- a/components/Guideline/src/index.tsx +++ b/components/Guideline/src/index.tsx @@ -1,28 +1,34 @@ import React from "react"; -import { css } from "emotion"; -import styled from "@emotion/styled"; -import { Element } from "@design-systems/utils"; interface LineProps { /** The color of the line */ color: string; } -const Line = styled.div` - width: 100%; - height: 10px; - margin-bottom: 8px; - background-color: ${(props) => props.color}; -`; +/** A line */ +const Line = ({ color }: LineProps) => ( +
+); -interface CreateGuidelineProps { +export interface CreateGuidelineProps { /** The label for the guideline */ guidelineLabel: React.ReactNode; /** The color of the line */ color: string; } -interface GuidelineProps extends Element<"div"> { +export interface GuidelineProps + extends React.DetailedHTMLProps< + React.HTMLAttributes, + HTMLDivElement + > { /** The label for the guideline */ label: React.ReactNode; } @@ -37,10 +43,10 @@ const Guideline = ({ }: GuidelineProps & CreateGuidelineProps) => (
{children}
diff --git a/components/Guideline/src/vite-env.d.ts b/components/Guideline/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/components/Guideline/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/components/Guideline/tsconfig.build.json b/components/Guideline/tsconfig.build.json new file mode 100644 index 0000000..154391f --- /dev/null +++ b/components/Guideline/tsconfig.build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["src/**/*.test.+(ts|tsx)", "src/**/*.stories.+(ts|tsx)"], + "types": ["vite/client"] +} diff --git a/components/Guideline/tsconfig.json b/components/Guideline/tsconfig.json index b9c8b59..c818eda 100644 --- a/components/Guideline/tsconfig.json +++ b/components/Guideline/tsconfig.json @@ -1,16 +1,9 @@ { "extends": "../../tsconfig.build.json", - "include": ["src/**/*", "../../typings/**/*"], - + "exclude": ["node_modules", "dist", "coverage", "src/**/*.test.+(ts|tsx)"], "compilerOptions": { "outDir": "./dist", - "rootDir": "./src", - "composite": true, - - // Need to override emotion's types because they pollute the global - "baseUrl": ".", - "paths": { - "@emotion/core": ["../../typings/emotion.d.ts"] - } - } + "rootDir": "./src" + }, + "include": ["src/**/*"] } diff --git a/components/Guideline/vite.config.ts b/components/Guideline/vite.config.ts new file mode 100644 index 0000000..bc49a97 --- /dev/null +++ b/components/Guideline/vite.config.ts @@ -0,0 +1,38 @@ +import { defineConfig } from "vite"; +import { resolve } from "path"; +import react from "@vitejs/plugin-react"; +import dts from "vite-plugin-dts"; +import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js"; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + react(), + dts({ + tsconfigPath: "tsconfig.build.json", + }), + cssInjectedByJsPlugin(), + ], + build: { + lib: { + entry: resolve(__dirname, `src/index.tsx`), + formats: ["es", "cjs"], + fileName(format) { + if (format === "es") { + return "esm/[name].js"; + } + + return "cjs/[name].js"; + }, + }, + rollupOptions: { + external: ["react", "react-dom"], + output: { + globals: { + react: "React", + "react-dom": "React-dom", + }, + }, + }, + }, +}); diff --git a/components/IntendedUsage/package.json b/components/IntendedUsage/package.json index d63711c..c0b2a25 100644 --- a/components/IntendedUsage/package.json +++ b/components/IntendedUsage/package.json @@ -1,9 +1,16 @@ { "name": "@doc-blocks/intended-usage", "version": "0.8.15", + "type": "module", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", "types": "./dist", + "exports": { + ".": { + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js" + } + }, "repository": "https://github.com/intuit/doc-blocks", "author": "Andrew Lisowski lisowski54@gmail.com", "contributors": [ @@ -18,22 +25,18 @@ "access": "public" }, "scripts": { - "clean": "ds clean", - "build": "ds build", - "dev": "ds dev", - "storybook": "ds storybook start", - "start": "ds build --watch", - "test": "ds test", - "lint": "ds lint", - "size": "ds size" + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" }, "dependencies": { "@babel/runtime": "^7.11.2", - "@design-systems/utils": "2.17.5", + "@design-systems/utils": "4.15.4", "emotion": "^10.0.27" }, "peerDependencies": { - "react": ">= 16.8.6" + "react": ">= 18.2.0" }, "files": [ "dist", diff --git a/components/IntendedUsage/src/IntendedUsage.module.css b/components/IntendedUsage/src/IntendedUsage.module.css new file mode 100644 index 0000000..8de8897 --- /dev/null +++ b/components/IntendedUsage/src/IntendedUsage.module.css @@ -0,0 +1,27 @@ +.intended-usage { + margin: 40px 0; +} + +.intended-usage ul { + list-style: none; + padding-left: 0; + margin: 0; +} + +.intended-usage li { + padding: 5px 0 5px 30px; + position: relative; + line-height: 24px; +} + +.intended-usage li:before { + position: absolute; + left: 0; + top: 6px; +} + +.intended-usage-title { + font-weight: 500; + margin-bottom: 10px; + font-size: 24px; +} diff --git a/components/IntendedUsage/src/IntendedUsage.stories.mdx b/components/IntendedUsage/src/IntendedUsage.stories.mdx deleted file mode 100644 index bcc7fe7..0000000 --- a/components/IntendedUsage/src/IntendedUsage.stories.mdx +++ /dev/null @@ -1,81 +0,0 @@ -import { Story, Meta, Canvas } from "@storybook/addon-docs/blocks"; - -import { BestFor, NotFor } from "."; -import notes from "../README.md"; - - - -# @doc-blocks/intended-usage - -A component to describe how a component should be used. - - - - -- Describing common situations to use your components in. - - - - - - - -- Ordering bagels - - - - -## Usage - -Describe how to use a component. - - - - -
    -
  • one
  • -
  • two
  • -
  • three
  • -
-
-
-
- -Or how not a component should be used. - - - - -
    -
  • one
  • -
  • two
  • -
  • - With a really really really really really really really really really - really really really really really really really really really really - really really really really really long line -
  • -
-
-
-
- -They are best if used together. - - - - -
    -
  • one
  • -
  • two
  • -
  • three
  • -
-
- -
    -
  • four
  • -
  • five
  • -
  • six
  • -
-
-
-
diff --git a/components/IntendedUsage/src/IntendedUsage.stories.tsx b/components/IntendedUsage/src/IntendedUsage.stories.tsx new file mode 100644 index 0000000..400ca22 --- /dev/null +++ b/components/IntendedUsage/src/IntendedUsage.stories.tsx @@ -0,0 +1,68 @@ +import React from "react"; +import { Meta, StoryObj } from "@storybook/react"; +import { BestFor, NotFor } from "."; + +const meta: Meta = { + title: "Components/Intended Usage", + component: BestFor, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +}; + +export default meta; + +type Story = StoryObj; + +export const BestForStory: Story = { + name: "Best For", + render: () => ( + +
    +
  • one
  • +
  • two
  • +
  • three
  • +
+
+ ), +}; + +export const NotForStory: Story = { + name: "Not For", + render: () => ( + +
    +
  • one
  • +
  • two
  • +
  • + With a really really really really really really really really really + really really really really really really really really really really + really really really really really long line +
  • +
+
+ ), +}; + +export const Together: Story = { + name: "Together", + render: () => ( + <> + +
    +
  • one
  • +
  • two
  • +
  • three
  • +
+
+ +
    +
  • four
  • +
  • five
  • +
  • six
  • +
+
+ + ), +}; diff --git a/components/IntendedUsage/src/index.tsx b/components/IntendedUsage/src/index.tsx index 7c2f941..264177e 100644 --- a/components/IntendedUsage/src/index.tsx +++ b/components/IntendedUsage/src/index.tsx @@ -1,9 +1,12 @@ import React from "react"; -import { Element } from "@design-systems/utils"; -import { css } from "emotion"; +import styles from "./IntendedUsage.module.css"; -interface IntendedUsageProps { +export interface IntendedUsageProps + extends React.DetailedHTMLProps< + React.HTMLAttributes, + HTMLDivElement + > { /** The title of the section */ sectionTitle: string; /** The icon for the list */ @@ -16,51 +19,37 @@ export const IntendedUsage = ({ sectionTitle, icon, ...props -}: Element<"div"> & IntendedUsageProps) => ( -
-
- {sectionTitle} +}: IntendedUsageProps) => { + const uid = Math.random().toString(36).substring(2, 15); + + // Add the icon dynamically with a style element + const iconStyle = ` + .${styles["intended-usage"]}#${uid} li:before { + content: "${icon}"; + } + `; + + return ( +
+ +
{sectionTitle}
+ {children}
- {children} -
-); + ); +}; /** A component that lists what the component being documented is intended to be used like. */ -export const BestFor = (props: Element<"div">) => ( - -); +export const BestFor = ( + props: React.DetailedHTMLProps< + React.HTMLAttributes, + HTMLDivElement + > +) => ; /** A component that lists what the component being documented is not intended to be used like. */ -export const NotFor = (props: Element<"div">) => ( - -); +export const NotFor = ( + props: React.DetailedHTMLProps< + React.HTMLAttributes, + HTMLDivElement + > +) => ; diff --git a/components/IntendedUsage/src/vite-env.d.ts b/components/IntendedUsage/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/components/IntendedUsage/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/components/IntendedUsage/tsconfig.build.json b/components/IntendedUsage/tsconfig.build.json new file mode 100644 index 0000000..154391f --- /dev/null +++ b/components/IntendedUsage/tsconfig.build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["src/**/*.test.+(ts|tsx)", "src/**/*.stories.+(ts|tsx)"], + "types": ["vite/client"] +} diff --git a/components/IntendedUsage/tsconfig.json b/components/IntendedUsage/tsconfig.json index b9c8b59..c818eda 100644 --- a/components/IntendedUsage/tsconfig.json +++ b/components/IntendedUsage/tsconfig.json @@ -1,16 +1,9 @@ { "extends": "../../tsconfig.build.json", - "include": ["src/**/*", "../../typings/**/*"], - + "exclude": ["node_modules", "dist", "coverage", "src/**/*.test.+(ts|tsx)"], "compilerOptions": { "outDir": "./dist", - "rootDir": "./src", - "composite": true, - - // Need to override emotion's types because they pollute the global - "baseUrl": ".", - "paths": { - "@emotion/core": ["../../typings/emotion.d.ts"] - } - } + "rootDir": "./src" + }, + "include": ["src/**/*"] } diff --git a/components/IntendedUsage/vite.config.ts b/components/IntendedUsage/vite.config.ts new file mode 100644 index 0000000..bc49a97 --- /dev/null +++ b/components/IntendedUsage/vite.config.ts @@ -0,0 +1,38 @@ +import { defineConfig } from "vite"; +import { resolve } from "path"; +import react from "@vitejs/plugin-react"; +import dts from "vite-plugin-dts"; +import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js"; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + react(), + dts({ + tsconfigPath: "tsconfig.build.json", + }), + cssInjectedByJsPlugin(), + ], + build: { + lib: { + entry: resolve(__dirname, `src/index.tsx`), + formats: ["es", "cjs"], + fileName(format) { + if (format === "es") { + return "esm/[name].js"; + } + + return "cjs/[name].js"; + }, + }, + rollupOptions: { + external: ["react", "react-dom"], + output: { + globals: { + react: "React", + "react-dom": "React-dom", + }, + }, + }, + }, +}); diff --git a/components/RelatedComponents/package.json b/components/RelatedComponents/package.json index 46c3668..31bbf17 100644 --- a/components/RelatedComponents/package.json +++ b/components/RelatedComponents/package.json @@ -1,9 +1,16 @@ { "name": "@doc-blocks/related-components", "version": "0.8.15", + "type": "module", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", "types": "./dist", + "exports": { + ".": { + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js" + } + }, "repository": "https://github.com/intuit/doc-blocks", "author": "Andrew Lisowski lisowski54@gmail.com", "contributors": [ @@ -18,29 +25,20 @@ "access": "public" }, "scripts": { - "clean": "ds clean", - "build": "ds build", - "dev": "ds dev", - "storybook": "ds storybook start", - "start": "ds build --watch", - "test": "ds test", - "lint": "ds lint", - "size": "ds size" + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" }, "dependencies": { - "@babel/runtime": "^7.11.2", - "@emotion/core": "^10.0.28", - "@emotion/styled": "^10.0.27", - "emotion": "^10.0.27" + "@babel/runtime": "^7.11.2" }, "peerDependencies": { - "@storybook/addon-links": ">= 6.2.9", - "@storybook/components": ">= 6.2.9", - "react": ">= 16.8.6" + "@storybook/addon-links": ">= 8.6.12", + "react": ">= 18.2.0" }, "devDependencies": { - "@storybook/addon-links": "6.2.9", - "@storybook/components": "6.2.9" + "@storybook/addon-links": "8.6.12" }, "files": [ "dist", diff --git a/components/RelatedComponents/src/RelatedComponents.stories.mdx b/components/RelatedComponents/src/RelatedComponents.stories.mdx deleted file mode 100644 index 7608007..0000000 --- a/components/RelatedComponents/src/RelatedComponents.stories.mdx +++ /dev/null @@ -1,21 +0,0 @@ -import { Story, Meta, Canvas } from "@storybook/addon-docs/blocks"; -import { ThemeProvider, themes, ensure } from "@storybook/theming"; - -import { RelatedComponents } from "."; -import notes from "../README.md"; - - - -# @doc-blocks/related-components - -A component that makes it easy to list a bunch of related components. - - - - - - - - diff --git a/components/RelatedComponents/src/RelatedComponents.stories.tsx b/components/RelatedComponents/src/RelatedComponents.stories.tsx new file mode 100644 index 0000000..f2e8e0d --- /dev/null +++ b/components/RelatedComponents/src/RelatedComponents.stories.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import type { Meta, StoryObj } from "@storybook/react"; + +import { RelatedComponents } from "."; + +const meta: Meta = { + title: "Components/Related Components", + component: RelatedComponents, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +}; + +export default meta; +type Story = StoryObj; + +export const BasicUsage: Story = { + render: (args) => ( + + ), + args: { + components: ["Components/Responsive Story", "Components/Row"], + }, +}; diff --git a/components/RelatedComponents/src/index.tsx b/components/RelatedComponents/src/index.tsx index 8e6717f..9c9bf2d 100644 --- a/components/RelatedComponents/src/index.tsx +++ b/components/RelatedComponents/src/index.tsx @@ -1,7 +1,5 @@ import React from "react"; -import { Link } from "@storybook/components"; -import { navigate } from "@storybook/addon-links/dist/esm/preview"; -import { css } from "emotion"; +import LinkTo from "@storybook/addon-links/react"; interface KindLinkProps { /** Text of the link */ @@ -12,12 +10,10 @@ interface KindLinkProps { /** A link to a story kind */ const KindLink = ({ kind, children }: KindLinkProps) => ( - navigate({ kind } as any)}> - {children} - + {children} ); -interface RelatedComponentsProps { +export interface RelatedComponentsProps { /** Stories that are related to this one */ components: string[]; } @@ -25,18 +21,18 @@ interface RelatedComponentsProps { /** A component to list related components */ export const RelatedComponents = (props: RelatedComponentsProps) => (
Related Components: diff --git a/components/RelatedComponents/src/vite-env.d.ts b/components/RelatedComponents/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/components/RelatedComponents/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/components/RelatedComponents/tsconfig.build.json b/components/RelatedComponents/tsconfig.build.json new file mode 100644 index 0000000..154391f --- /dev/null +++ b/components/RelatedComponents/tsconfig.build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["src/**/*.test.+(ts|tsx)", "src/**/*.stories.+(ts|tsx)"], + "types": ["vite/client"] +} diff --git a/components/RelatedComponents/tsconfig.json b/components/RelatedComponents/tsconfig.json index b9c8b59..c818eda 100644 --- a/components/RelatedComponents/tsconfig.json +++ b/components/RelatedComponents/tsconfig.json @@ -1,16 +1,9 @@ { "extends": "../../tsconfig.build.json", - "include": ["src/**/*", "../../typings/**/*"], - + "exclude": ["node_modules", "dist", "coverage", "src/**/*.test.+(ts|tsx)"], "compilerOptions": { "outDir": "./dist", - "rootDir": "./src", - "composite": true, - - // Need to override emotion's types because they pollute the global - "baseUrl": ".", - "paths": { - "@emotion/core": ["../../typings/emotion.d.ts"] - } - } + "rootDir": "./src" + }, + "include": ["src/**/*"] } diff --git a/components/RelatedComponents/vite.config.ts b/components/RelatedComponents/vite.config.ts new file mode 100644 index 0000000..bc49a97 --- /dev/null +++ b/components/RelatedComponents/vite.config.ts @@ -0,0 +1,38 @@ +import { defineConfig } from "vite"; +import { resolve } from "path"; +import react from "@vitejs/plugin-react"; +import dts from "vite-plugin-dts"; +import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js"; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + react(), + dts({ + tsconfigPath: "tsconfig.build.json", + }), + cssInjectedByJsPlugin(), + ], + build: { + lib: { + entry: resolve(__dirname, `src/index.tsx`), + formats: ["es", "cjs"], + fileName(format) { + if (format === "es") { + return "esm/[name].js"; + } + + return "cjs/[name].js"; + }, + }, + rollupOptions: { + external: ["react", "react-dom"], + output: { + globals: { + react: "React", + "react-dom": "React-dom", + }, + }, + }, + }, +}); diff --git a/components/ResponsiveStory/package.json b/components/ResponsiveStory/package.json index 62b8332..722256b 100644 --- a/components/ResponsiveStory/package.json +++ b/components/ResponsiveStory/package.json @@ -1,9 +1,16 @@ { "name": "@doc-blocks/responsive-story", "version": "0.8.15", + "type": "module", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", "types": "./dist", + "exports": { + ".": { + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js" + } + }, "repository": "https://github.com/intuit/doc-blocks", "author": "Andrew Lisowski lisowski54@gmail.com", "contributors": [ @@ -18,29 +25,19 @@ "access": "public" }, "scripts": { - "clean": "ds clean", - "build": "ds build", - "dev": "ds dev", - "storybook": "ds storybook start", - "start": "ds build --watch", - "test": "ds test", - "lint": "ds lint", - "size": "ds size" + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" }, "dependencies": { "@babel/runtime": "^7.11.2", - "@design-systems/utils": "2.17.5", - "@emotion/core": "^10.0.28", - "@emotion/styled": "^10.0.27", - "clsx": "^1.1.1", - "emotion": "^10.0.27" + "@storybook/addon-docs": "8.6.12", + "clsx": "^1.1.1" }, "peerDependencies": { - "@storybook/addon-docs": ">= 6.2.9", - "react": ">= 16.8.6" - }, - "devDependencies": { - "@storybook/addon-docs": "6.2.9" + "@storybook/addon-docs": ">= 8.6.12", + "react": ">= 18.2.0" }, "files": [ "dist", diff --git a/components/ResponsiveStory/src/ResponsiveStory.module.css b/components/ResponsiveStory/src/ResponsiveStory.module.css new file mode 100644 index 0000000..def51e0 --- /dev/null +++ b/components/ResponsiveStory/src/ResponsiveStory.module.css @@ -0,0 +1,83 @@ +.deviceSelect { + cursor: pointer; + color: rgb(51, 51, 51); + text-align: center; + appearance: none; + font-size: 12px; + line-height: 16px; + font-family: "Nunito Sans", -apple-system, ".SFNSText-Regular", + "San Francisco", BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, + Arial, sans-serif; + font-weight: 700; + padding: 4px 10px; + background: rgb(255, 255, 255); + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 4px; + margin: 20px 0; + text-align-last: center; +} + +.infoBar { + height: 30px; + display: flex; + justify-content: space-between; + align-items: flex-end; + padding: 0 25px 0 34px; + font-size: 14px; +} + +.iframeLabel { + width: 100%; + align-items: center; + justify-content: center; + display: flex; + margin-top: 14px; +} + +.iframeLabel > *:not(:last-child) { + margin-right: 10px; +} + +.showCodeButton { + cursor: pointer; + display: flex; + align-items: center; + color: rgb(51, 51, 51); + font-size: 12px; + line-height: 16px; + font-family: "Nunito Sans", -apple-system, ".SFNSText-Regular", + "San Francisco", BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, + Arial, sans-serif; + font-weight: 700; + margin-left: -1px; + padding: 4px 10px; + background: rgb(255, 255, 255); + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 4px; +} + +.responsiveStoryWrapper { + margin: 30px auto; + display: flex; + flex-direction: column; +} + +.iframe { + border: none; + width: 100%; + height: 100%; + margin: auto; + display: block; +} + +.iframeIphoneLandscape { + padding-left: 30px; + box-sizing: border-box; +} + +.iframeMac { + width: 1200px; + height: 750px; + transform: scale(0.8); + transform-origin: 0 0; +} diff --git a/components/ResponsiveStory/src/ResponsiveStory.stories.mdx b/components/ResponsiveStory/src/ResponsiveStory.stories.mdx deleted file mode 100644 index d9da5e9..0000000 --- a/components/ResponsiveStory/src/ResponsiveStory.stories.mdx +++ /dev/null @@ -1,128 +0,0 @@ -import { Story, Meta, Canvas } from "@storybook/addon-docs/blocks"; - -import { ResponsiveStory } from "."; -import notes from "../README.md"; - - - -# @doc-blocks/responsive-story - -The `ResponsiveStory` lets you render any story in an `iframe` with a fancy device wrapper. -Since it is in an `iframe` all of the media queries work and your component will actually render at that screen size. -It will also display the source for the story you are documenting. - - - - - - - -You can add a label to the story for extra context. - - - - - - - -It can render as an `iPhone`, `iPad`, or `mac`. - - - -
- - - -
-
-
- -You can also let the user of your docs choose what device to render on. - - - - - - - -Choose the alignment of the device for the prettiest docs. - - - -
- - - -
-
-
- -Show only the important portion of the device. - - - -
- - -
-
-
- -Rotate the device to landscape mode to visualize even more device states. - - - -
- - -
-
-
- -Change the color of the header on the `iPhone` to match your product header's color. - - - - - - - -Add styles to the device wrapper. - - - - - - - ---- - -Or even declare stories right withing MDX when writing MDX only stories. - - - - - - - - diff --git a/components/ResponsiveStory/src/ResponsiveStory.stories.tsx b/components/ResponsiveStory/src/ResponsiveStory.stories.tsx new file mode 100644 index 0000000..64cbb86 --- /dev/null +++ b/components/ResponsiveStory/src/ResponsiveStory.stories.tsx @@ -0,0 +1,147 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import React from "react"; +import { ResponsiveStory } from "."; + +const meta: Meta = { + title: "Components/Responsive Story", + component: ResponsiveStory, + parameters: { + layout: "centered", + docs: { + description: { + component: `The \`ResponsiveStory\` lets you render any story in an \`iframe\` with a fancy device wrapper. +Since it is in an \`iframe\` all of the media queries work and your component will actually render at that screen size. +It will also display the source for the story you are documenting.`, + }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +/** + * Basic example of the ResponsiveStory component. + */ +export const BasicUsage: Story = { + args: { + id: "components-row--basic-usage", + }, +}; + +/** + * Example with a label for additional context. + */ +export const WithLabel: Story = { + args: { + id: "components-row--basic-usage", + label: "A label for the story", + }, +}; + +/** + * Examples showing different device options. + */ +export const Devices: Story = { + render: () => ( +
+ + + +
+ ), +}; + +/** + * Let the user choose which device to render on. + */ +export const ChooseDevice: Story = { + args: { + id: "components-row--basic-usage", + device: "choose", + }, +}; + +/** + * Different alignment options for the device. + */ +export const Align: Story = { + render: () => ( +
+ + + +
+ ), +}; + +/** + * Examples showing only portions of the device. + */ +export const TopOrBottom: Story = { + render: () => ( +
+ + +
+ ), +}; + +/** + * Examples showing devices in landscape mode. + */ +export const RotateMobile: Story = { + render: () => ( +
+ + +
+ ), +}; + +/** + * Example showing customized header colors. + */ +export const MobileHeader: Story = { + args: { + id: "components-row--basic-usage", + headerColor: "black", + headerText: "white", + }, +}; + +/** + * Example showing device with custom screen styles. + */ +export const DeviceStyles: Story = { + args: { + id: "components-row--basic-usage", + headerColor: "black", + headerText: "white", + screenStyles: { paddingTop: 20 }, + }, +}; + +/** + * Inline example story for demonstrating a simple button. + */ +export const Test: Story = { + render: () => , +}; + +/** + * Example showing how to reference an inline story. + */ +export const InlineReference: Story = { + render: () => ( + + ), +}; diff --git a/components/ResponsiveStory/src/devices.min.css b/components/ResponsiveStory/src/devices.min.css new file mode 100644 index 0000000..c004740 --- /dev/null +++ b/components/ResponsiveStory/src/devices.min.css @@ -0,0 +1,2509 @@ +.marvel-device { + display: inline-block; + position: relative; + -webkit-box-sizing: content-box !important; + box-sizing: content-box !important; +} +.marvel-device .screen { + width: 100%; + position: relative; + height: 100%; + z-index: 3; + background: white; + overflow: hidden; + display: block; + border-radius: 1px; + -webkit-box-shadow: 0 0 0 3px #111; + box-shadow: 0 0 0 3px #111; +} +.marvel-device .top-bar, +.marvel-device .bottom-bar { + height: 3px; + background: black; + width: 100%; + display: block; +} +.marvel-device .middle-bar { + width: 3px; + height: 4px; + top: 0px; + left: 90px; + background: black; + position: absolute; +} +.marvel-device.iphone8 { + width: 375px; + height: 667px; + padding: 105px 24px; + background: #d9dbdc; + border-radius: 56px; + -webkit-box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, 0.2); + box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, 0.2); +} +.marvel-device.iphone8:before { + width: calc(100% - 12px); + height: calc(100% - 12px); + position: absolute; + top: 6px; + content: ""; + left: 6px; + border-radius: 50px; + background: #f8f8f8; + z-index: 1; +} +.marvel-device.iphone8:after { + width: calc(100% - 16px); + height: calc(100% - 16px); + position: absolute; + top: 8px; + content: ""; + left: 8px; + border-radius: 48px; + -webkit-box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, 0.1), inset 0 0 6px 3px #fff; + box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, 0.1), inset 0 0 6px 3px #fff; + z-index: 2; +} +.marvel-device.iphone8 .home { + border-radius: 100%; + width: 68px; + height: 68px; + position: absolute; + left: 50%; + margin-left: -34px; + bottom: 22px; + z-index: 3; + background: #303233; + background: linear-gradient( + 135deg, + #303233 0%, + #b5b7b9 50%, + #f0f2f2 69%, + #303233 100% + ); +} +.marvel-device.iphone8 .home:before { + background: #f8f8f8; + position: absolute; + content: ""; + border-radius: 100%; + width: calc(100% - 8px); + height: calc(100% - 8px); + top: 4px; + left: 4px; +} +.marvel-device.iphone8 .top-bar { + height: 14px; + background: #bfbfc0; + position: absolute; + top: 68px; + left: 0; +} +.marvel-device.iphone8 .bottom-bar { + height: 14px; + background: #bfbfc0; + position: absolute; + bottom: 68px; + left: 0; +} +.marvel-device.iphone8 .sleep { + position: absolute; + top: 190px; + right: -4px; + width: 4px; + height: 66px; + border-radius: 0px 2px 2px 0px; + background: #d9dbdc; +} +.marvel-device.iphone8 .volume { + position: absolute; + left: -4px; + top: 188px; + z-index: 0; + height: 66px; + width: 4px; + border-radius: 2px 0px 0px 2px; + background: #d9dbdc; +} +.marvel-device.iphone8 .volume:before { + position: absolute; + left: 2px; + top: -78px; + height: 40px; + width: 2px; + border-radius: 2px 0px 0px 2px; + background: inherit; + content: ""; + display: block; +} +.marvel-device.iphone8 .volume:after { + position: absolute; + left: 0px; + top: 82px; + height: 66px; + width: 4px; + border-radius: 2px 0px 0px 2px; + background: inherit; + content: ""; + display: block; +} +.marvel-device.iphone8 .camera { + background: #3c3d3d; + width: 12px; + height: 12px; + position: absolute; + top: 24px; + left: 50%; + margin-left: -6px; + border-radius: 100%; + z-index: 3; +} +.marvel-device.iphone8 .sensor { + background: #3c3d3d; + width: 16px; + height: 16px; + position: absolute; + top: 49px; + left: 134px; + z-index: 3; + border-radius: 100%; +} +.marvel-device.iphone8 .speaker { + background: #292728; + width: 70px; + height: 6px; + position: absolute; + top: 54px; + left: 50%; + margin-left: -35px; + border-radius: 6px; + z-index: 3; +} +.marvel-device.iphone8.gold { + background: #f9e7d3; +} +.marvel-device.iphone8.gold .top-bar, +.marvel-device.iphone8.gold .bottom-bar { + background: white; +} +.marvel-device.iphone8.gold .sleep, +.marvel-device.iphone8.gold .volume { + background: #f9e7d3; +} +.marvel-device.iphone8.gold .home { + background: #cebba9; + background: linear-gradient(135deg, #cebba9 0%, #f9e7d3 50%, #cebba9 100%); +} +.marvel-device.iphone8.black { + background: #464646; + -webkit-box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, 0.7); + box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, 0.7); +} +.marvel-device.iphone8.black:before { + background: #080808; +} +.marvel-device.iphone8.black:after { + -webkit-box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, 0.1), + inset 0 0 6px 3px #212121; + box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, 0.1), inset 0 0 6px 3px #212121; +} +.marvel-device.iphone8.black .top-bar, +.marvel-device.iphone8.black .bottom-bar { + background: #212121; +} +.marvel-device.iphone8.black .volume, +.marvel-device.iphone8.black .sleep { + background: #464646; +} +.marvel-device.iphone8.black .camera { + background: #080808; +} +.marvel-device.iphone8.black .home { + background: #080808; + background: linear-gradient(135deg, #080808 0%, #464646 50%, #080808 100%); +} +.marvel-device.iphone8.black .home:before { + background: #080808; +} +.marvel-device.iphone8.landscape { + padding: 24px 105px; + height: 375px; + width: 667px; +} +.marvel-device.iphone8.landscape .sleep { + top: 100%; + border-radius: 0px 0px 2px 2px; + right: 190px; + height: 4px; + width: 66px; +} +.marvel-device.iphone8.landscape .volume { + width: 66px; + height: 4px; + top: -4px; + left: calc(100% - 188px - 66px); + border-radius: 2px 2px 0px 0px; +} +.marvel-device.iphone8.landscape .volume:before { + width: 40px; + height: 2px; + top: 2px; + right: -78px; + left: auto; + border-radius: 2px 2px 0px 0px; +} +.marvel-device.iphone8.landscape .volume:after { + left: -82px; + width: 66px; + height: 4px; + top: 0; + border-radius: 2px 2px 0px 0px; +} +.marvel-device.iphone8.landscape .top-bar { + width: 14px; + height: 100%; + left: calc(100% - 68px - 14px); + top: 0; +} +.marvel-device.iphone8.landscape .bottom-bar { + width: 14px; + height: 100%; + left: 68px; + top: 0; +} +.marvel-device.iphone8.landscape .home { + top: 50%; + margin-top: -34px; + margin-left: 0; + left: 22px; +} +.marvel-device.iphone8.landscape .sensor { + top: 134px; + left: calc(100% - 49px - 16px); +} +.marvel-device.iphone8.landscape .speaker { + height: 70px; + width: 6px; + left: calc(100% - 54px - 6px); + top: 50%; + margin-left: 0px; + margin-top: -35px; +} +.marvel-device.iphone8.landscape .camera { + left: calc(100% - 32px); + top: 50%; + margin-left: 0px; + margin-top: -5px; +} +.marvel-device.iphone8plus { + width: 414px; + height: 736px; + padding: 112px 26px; + background: #d9dbdc; + border-radius: 56px; + -webkit-box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, 0.2); + box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, 0.2); +} +.marvel-device.iphone8plus:before { + width: calc(100% - 12px); + height: calc(100% - 12px); + position: absolute; + top: 6px; + content: ""; + left: 6px; + border-radius: 50px; + background: #f8f8f8; + z-index: 1; +} +.marvel-device.iphone8plus:after { + width: calc(100% - 16px); + height: calc(100% - 16px); + position: absolute; + top: 8px; + content: ""; + left: 8px; + border-radius: 48px; + -webkit-box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, 0.1), inset 0 0 6px 3px #fff; + box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, 0.1), inset 0 0 6px 3px #fff; + z-index: 2; +} +.marvel-device.iphone8plus .home { + border-radius: 100%; + width: 68px; + height: 68px; + position: absolute; + left: 50%; + margin-left: -34px; + bottom: 24px; + z-index: 3; + background: #303233; + background: linear-gradient( + 135deg, + #303233 0%, + #b5b7b9 50%, + #f0f2f2 69%, + #303233 100% + ); +} +.marvel-device.iphone8plus .home:before { + background: #f8f8f8; + position: absolute; + content: ""; + border-radius: 100%; + width: calc(100% - 8px); + height: calc(100% - 8px); + top: 4px; + left: 4px; +} +.marvel-device.iphone8plus .top-bar { + height: 14px; + background: #bfbfc0; + position: absolute; + top: 68px; + left: 0; +} +.marvel-device.iphone8plus .bottom-bar { + height: 14px; + background: #bfbfc0; + position: absolute; + bottom: 68px; + left: 0; +} +.marvel-device.iphone8plus .sleep { + position: absolute; + top: 190px; + right: -4px; + width: 4px; + height: 66px; + border-radius: 0px 2px 2px 0px; + background: #d9dbdc; +} +.marvel-device.iphone8plus .volume { + position: absolute; + left: -4px; + top: 188px; + z-index: 0; + height: 66px; + width: 4px; + border-radius: 2px 0px 0px 2px; + background: #d9dbdc; +} +.marvel-device.iphone8plus .volume:before { + position: absolute; + left: 2px; + top: -78px; + height: 40px; + width: 2px; + border-radius: 2px 0px 0px 2px; + background: inherit; + content: ""; + display: block; +} +.marvel-device.iphone8plus .volume:after { + position: absolute; + left: 0px; + top: 82px; + height: 66px; + width: 4px; + border-radius: 2px 0px 0px 2px; + background: inherit; + content: ""; + display: block; +} +.marvel-device.iphone8plus .camera { + background: #3c3d3d; + width: 12px; + height: 12px; + position: absolute; + top: 29px; + left: 50%; + margin-left: -6px; + border-radius: 100%; + z-index: 3; +} +.marvel-device.iphone8plus .sensor { + background: #3c3d3d; + width: 16px; + height: 16px; + position: absolute; + top: 54px; + left: 154px; + z-index: 3; + border-radius: 100%; +} +.marvel-device.iphone8plus .speaker { + background: #292728; + width: 70px; + height: 6px; + position: absolute; + top: 59px; + left: 50%; + margin-left: -35px; + border-radius: 6px; + z-index: 3; +} +.marvel-device.iphone8plus.gold { + background: #f9e7d3; +} +.marvel-device.iphone8plus.gold .top-bar, +.marvel-device.iphone8plus.gold .bottom-bar { + background: white; +} +.marvel-device.iphone8plus.gold .sleep, +.marvel-device.iphone8plus.gold .volume { + background: #f9e7d3; +} +.marvel-device.iphone8plus.gold .home { + background: #cebba9; + background: linear-gradient(135deg, #cebba9 0%, #f9e7d3 50%, #cebba9 100%); +} +.marvel-device.iphone8plus.black { + background: #464646; + -webkit-box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, 0.7); + box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, 0.7); +} +.marvel-device.iphone8plus.black:before { + background: #080808; +} +.marvel-device.iphone8plus.black:after { + -webkit-box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, 0.1), + inset 0 0 6px 3px #212121; + box-shadow: inset 0 0 3px 0 rgba(0, 0, 0, 0.1), inset 0 0 6px 3px #212121; +} +.marvel-device.iphone8plus.black .top-bar, +.marvel-device.iphone8plus.black .bottom-bar { + background: #212121; +} +.marvel-device.iphone8plus.black .volume, +.marvel-device.iphone8plus.black .sleep { + background: #464646; +} +.marvel-device.iphone8plus.black .camera { + background: #080808; +} +.marvel-device.iphone8plus.black .home { + background: #080808; + background: linear-gradient(135deg, #080808 0%, #464646 50%, #080808 100%); +} +.marvel-device.iphone8plus.black .home:before { + background: #080808; +} +.marvel-device.iphone8plus.landscape { + padding: 26px 112px; + height: 414px; + width: 736px; +} +.marvel-device.iphone8plus.landscape .sleep { + top: 100%; + border-radius: 0px 0px 2px 2px; + right: 190px; + height: 4px; + width: 66px; +} +.marvel-device.iphone8plus.landscape .volume { + width: 66px; + height: 4px; + top: -4px; + left: calc(100% - 188px - 66px); + border-radius: 2px 2px 0px 0px; +} +.marvel-device.iphone8plus.landscape .volume:before { + width: 40px; + height: 2px; + top: 2px; + right: -78px; + left: auto; + border-radius: 2px 2px 0px 0px; +} +.marvel-device.iphone8plus.landscape .volume:after { + left: -82px; + width: 66px; + height: 4px; + top: 0; + border-radius: 2px 2px 0px 0px; +} +.marvel-device.iphone8plus.landscape .top-bar { + width: 14px; + height: 100%; + left: calc(100% - 68px - 14px); + top: 0; +} +.marvel-device.iphone8plus.landscape .bottom-bar { + width: 14px; + height: 100%; + left: 68px; + top: 0; +} +.marvel-device.iphone8plus.landscape .home { + top: 50%; + margin-top: -34px; + margin-left: 0; + left: 24px; +} +.marvel-device.iphone8plus.landscape .sensor { + top: 154px; + left: calc(100% - 54px - 16px); +} +.marvel-device.iphone8plus.landscape .speaker { + height: 70px; + width: 6px; + left: calc(100% - 59px - 6px); + top: 50%; + margin-left: 0px; + margin-top: -35px; +} +.marvel-device.iphone8plus.landscape .camera { + left: calc(100% - 29px); + top: 50%; + margin-left: 0px; + margin-top: -5px; +} +.marvel-device.iphone5s, +.marvel-device.iphone5c { + padding: 105px 22px; + background: #2c2b2c; + width: 320px; + height: 568px; + border-radius: 50px; +} +.marvel-device.iphone5s:before, +.marvel-device.iphone5c:before { + width: calc(100% - 8px); + height: calc(100% - 8px); + position: absolute; + top: 4px; + content: ""; + left: 4px; + border-radius: 46px; + background: #1e1e1e; + z-index: 1; +} +.marvel-device.iphone5s .sleep, +.marvel-device.iphone5c .sleep { + position: absolute; + top: -4px; + right: 60px; + width: 60px; + height: 4px; + border-radius: 2px 2px 0px 0px; + background: #282727; +} +.marvel-device.iphone5s .volume, +.marvel-device.iphone5c .volume { + position: absolute; + left: -4px; + top: 180px; + z-index: 0; + height: 27px; + width: 4px; + border-radius: 2px 0px 0px 2px; + background: #282727; +} +.marvel-device.iphone5s .volume:before, +.marvel-device.iphone5c .volume:before { + position: absolute; + left: 0px; + top: -75px; + height: 35px; + width: 4px; + border-radius: 2px 0px 0px 2px; + background: inherit; + content: ""; + display: block; +} +.marvel-device.iphone5s .volume:after, +.marvel-device.iphone5c .volume:after { + position: absolute; + left: 0px; + bottom: -64px; + height: 27px; + width: 4px; + border-radius: 2px 0px 0px 2px; + background: inherit; + content: ""; + display: block; +} +.marvel-device.iphone5s .camera, +.marvel-device.iphone5c .camera { + background: #3c3d3d; + width: 10px; + height: 10px; + position: absolute; + top: 32px; + left: 50%; + margin-left: -5px; + border-radius: 5px; + z-index: 3; +} +.marvel-device.iphone5s .sensor, +.marvel-device.iphone5c .sensor { + background: #3c3d3d; + width: 10px; + height: 10px; + position: absolute; + top: 60px; + left: 160px; + z-index: 3; + margin-left: -32px; + border-radius: 5px; +} +.marvel-device.iphone5s .speaker, +.marvel-device.iphone5c .speaker { + background: #292728; + width: 64px; + height: 10px; + position: absolute; + top: 60px; + left: 50%; + margin-left: -32px; + border-radius: 5px; + z-index: 3; +} +.marvel-device.iphone5s.landscape, +.marvel-device.iphone5c.landscape { + padding: 22px 105px; + height: 320px; + width: 568px; +} +.marvel-device.iphone5s.landscape .sleep, +.marvel-device.iphone5c.landscape .sleep { + right: -4px; + top: calc(100% - 120px); + height: 60px; + width: 4px; + border-radius: 0px 2px 2px 0px; +} +.marvel-device.iphone5s.landscape .volume, +.marvel-device.iphone5c.landscape .volume { + width: 27px; + height: 4px; + top: -4px; + left: calc(100% - 180px); + border-radius: 2px 2px 0px 0px; +} +.marvel-device.iphone5s.landscape .volume:before, +.marvel-device.iphone5c.landscape .volume:before { + width: 35px; + height: 4px; + top: 0px; + right: -75px; + left: auto; + border-radius: 2px 2px 0px 0px; +} +.marvel-device.iphone5s.landscape .volume:after, +.marvel-device.iphone5c.landscape .volume:after { + bottom: 0px; + left: -64px; + z-index: 999; + height: 4px; + width: 27px; + border-radius: 2px 2px 0px 0px; +} +.marvel-device.iphone5s.landscape .sensor, +.marvel-device.iphone5c.landscape .sensor { + top: 160px; + left: calc(100% - 60px); + margin-left: 0px; + margin-top: -32px; +} +.marvel-device.iphone5s.landscape .speaker, +.marvel-device.iphone5c.landscape .speaker { + height: 64px; + width: 10px; + left: calc(100% - 60px); + top: 50%; + margin-left: 0px; + margin-top: -32px; +} +.marvel-device.iphone5s.landscape .camera, +.marvel-device.iphone5c.landscape .camera { + left: calc(100% - 32px); + top: 50%; + margin-left: 0px; + margin-top: -5px; +} +.marvel-device.iphone5s .home { + border-radius: 36px; + width: 68px; + -webkit-box-shadow: inset 0 0 0 4px #2c2b2c; + box-shadow: inset 0 0 0 4px #2c2b2c; + height: 68px; + position: absolute; + left: 50%; + margin-left: -34px; + bottom: 19px; + z-index: 3; +} +.marvel-device.iphone5s .top-bar { + top: 70px; + position: absolute; + left: 0; +} +.marvel-device.iphone5s .bottom-bar { + bottom: 70px; + position: absolute; + left: 0; +} +.marvel-device.iphone5s.landscape .home { + left: 19px; + bottom: 50%; + margin-bottom: -34px; + margin-left: 0px; +} +.marvel-device.iphone5s.landscape .top-bar { + left: 70px; + top: 0px; + width: 3px; + height: 100%; +} +.marvel-device.iphone5s.landscape .bottom-bar { + right: 70px; + left: auto; + bottom: 0px; + width: 3px; + height: 100%; +} +.marvel-device.iphone5s.silver { + background: #bcbcbc; +} +.marvel-device.iphone5s.silver:before { + background: #fcfcfc; +} +.marvel-device.iphone5s.silver .volume, +.marvel-device.iphone5s.silver .sleep { + background: #d6d6d6; +} +.marvel-device.iphone5s.silver .top-bar, +.marvel-device.iphone5s.silver .bottom-bar { + background: #eaebec; +} +.marvel-device.iphone5s.silver .home { + -webkit-box-shadow: inset 0 0 0 4px #bcbcbc; + box-shadow: inset 0 0 0 4px #bcbcbc; +} +.marvel-device.iphone5s.gold { + background: #f9e7d3; +} +.marvel-device.iphone5s.gold:before { + background: #fcfcfc; +} +.marvel-device.iphone5s.gold .volume, +.marvel-device.iphone5s.gold .sleep { + background: #f9e7d3; +} +.marvel-device.iphone5s.gold .top-bar, +.marvel-device.iphone5s.gold .bottom-bar { + background: white; +} +.marvel-device.iphone5s.gold .home { + -webkit-box-shadow: inset 0 0 0 4px #f9e7d3; + box-shadow: inset 0 0 0 4px #f9e7d3; +} +.marvel-device.iphone5c { + background: white; + -webkit-box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2); + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2); +} +.marvel-device.iphone5c .top-bar, +.marvel-device.iphone5c .bottom-bar { + display: none; +} +.marvel-device.iphone5c .home { + background: #242324; + border-radius: 36px; + width: 68px; + height: 68px; + z-index: 3; + position: absolute; + left: 50%; + margin-left: -34px; + bottom: 19px; +} +.marvel-device.iphone5c .home:after { + width: 20px; + height: 20px; + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 4px; + position: absolute; + display: block; + content: ""; + top: 50%; + left: 50%; + margin-top: -11px; + margin-left: -11px; +} +.marvel-device.iphone5c.landscape .home { + left: 19px; + bottom: 50%; + margin-bottom: -34px; + margin-left: 0px; +} +.marvel-device.iphone5c .volume, +.marvel-device.iphone5c .sleep { + background: #dddddd; +} +.marvel-device.iphone5c.red { + background: #f96b6c; +} +.marvel-device.iphone5c.red .volume, +.marvel-device.iphone5c.red .sleep { + background: #ed5758; +} +.marvel-device.iphone5c.yellow { + background: #f2dc60; +} +.marvel-device.iphone5c.yellow .volume, +.marvel-device.iphone5c.yellow .sleep { + background: #e5ce4c; +} +.marvel-device.iphone5c.green { + background: #97e563; +} +.marvel-device.iphone5c.green .volume, +.marvel-device.iphone5c.green .sleep { + background: #85d94d; +} +.marvel-device.iphone5c.blue { + background: #33a2db; +} +.marvel-device.iphone5c.blue .volume, +.marvel-device.iphone5c.blue .sleep { + background: #2694cd; +} +.marvel-device.iphone4s { + padding: 129px 27px; + width: 320px; + height: 480px; + background: #686868; + border-radius: 54px; +} +.marvel-device.iphone4s:before { + content: ""; + width: calc(100% - 8px); + height: calc(100% - 8px); + position: absolute; + top: 4px; + left: 4px; + z-index: 1; + border-radius: 50px; + background: #1e1e1e; +} +.marvel-device.iphone4s .top-bar { + top: 60px; + position: absolute; + left: 0; +} +.marvel-device.iphone4s .bottom-bar { + bottom: 90px; + position: absolute; + left: 0; +} +.marvel-device.iphone4s .camera { + background: #3c3d3d; + width: 10px; + height: 10px; + position: absolute; + top: 72px; + left: 134px; + z-index: 3; + margin-left: -5px; + border-radius: 100%; +} +.marvel-device.iphone4s .speaker { + background: #292728; + width: 64px; + height: 10px; + position: absolute; + top: 72px; + left: 50%; + z-index: 3; + margin-left: -32px; + border-radius: 5px; +} +.marvel-device.iphone4s .sensor { + background: #292728; + width: 40px; + height: 10px; + position: absolute; + top: 36px; + left: 50%; + z-index: 3; + margin-left: -20px; + border-radius: 5px; +} +.marvel-device.iphone4s .home { + background: #242324; + border-radius: 100%; + width: 72px; + height: 72px; + z-index: 3; + position: absolute; + left: 50%; + margin-left: -36px; + bottom: 30px; +} +.marvel-device.iphone4s .home:after { + width: 20px; + height: 20px; + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 4px; + position: absolute; + display: block; + content: ""; + top: 50%; + left: 50%; + margin-top: -11px; + margin-left: -11px; +} +.marvel-device.iphone4s .sleep { + position: absolute; + top: -4px; + right: 60px; + width: 60px; + height: 4px; + border-radius: 2px 2px 0px 0px; + background: #4d4d4d; +} +.marvel-device.iphone4s .volume { + position: absolute; + left: -4px; + top: 160px; + height: 27px; + width: 4px; + border-radius: 2px 0px 0px 2px; + background: #4d4d4d; +} +.marvel-device.iphone4s .volume:before { + position: absolute; + left: 0px; + top: -70px; + height: 35px; + width: 4px; + border-radius: 2px 0px 0px 2px; + background: inherit; + content: ""; + display: block; +} +.marvel-device.iphone4s .volume:after { + position: absolute; + left: 0px; + bottom: -64px; + height: 27px; + width: 4px; + border-radius: 2px 0px 0px 2px; + background: inherit; + content: ""; + display: block; +} +.marvel-device.iphone4s.landscape { + padding: 27px 129px; + height: 320px; + width: 480px; +} +.marvel-device.iphone4s.landscape .bottom-bar { + left: 90px; + bottom: 0px; + height: 100%; + width: 3px; +} +.marvel-device.iphone4s.landscape .top-bar { + left: calc(100% - 60px); + top: 0px; + height: 100%; + width: 3px; +} +.marvel-device.iphone4s.landscape .camera { + top: 134px; + left: calc(100% - 72px); + margin-left: 0; +} +.marvel-device.iphone4s.landscape .speaker { + top: 50%; + margin-left: 0; + margin-top: -32px; + left: calc(100% - 72px); + width: 10px; + height: 64px; +} +.marvel-device.iphone4s.landscape .sensor { + height: 40px; + width: 10px; + left: calc(100% - 36px); + top: 50%; + margin-left: 0; + margin-top: -20px; +} +.marvel-device.iphone4s.landscape .home { + left: 30px; + bottom: 50%; + margin-left: 0; + margin-bottom: -36px; +} +.marvel-device.iphone4s.landscape .sleep { + height: 60px; + width: 4px; + right: -4px; + top: calc(100% - 120px); + border-radius: 0px 2px 2px 0px; +} +.marvel-device.iphone4s.landscape .volume { + top: -4px; + left: calc(100% - 187px); + height: 4px; + width: 27px; + border-radius: 2px 2px 0px 0px; +} +.marvel-device.iphone4s.landscape .volume:before { + right: -70px; + left: auto; + top: 0px; + width: 35px; + height: 4px; + border-radius: 2px 2px 0px 0px; +} +.marvel-device.iphone4s.landscape .volume:after { + width: 27px; + height: 4px; + bottom: 0px; + left: -64px; + border-radius: 2px 2px 0px 0px; +} +.marvel-device.iphone4s.silver { + background: #bcbcbc; +} +.marvel-device.iphone4s.silver:before { + background: #fcfcfc; +} +.marvel-device.iphone4s.silver .home { + background: #fcfcfc; + -webkit-box-shadow: inset 0 0 0 1px #bcbcbc; + box-shadow: inset 0 0 0 1px #bcbcbc; +} +.marvel-device.iphone4s.silver .home:after { + border: 1px solid rgba(0, 0, 0, 0.2); +} +.marvel-device.iphone4s.silver .volume, +.marvel-device.iphone4s.silver .sleep { + background: #d6d6d6; +} +.marvel-device.nexus5 { + padding: 50px 15px 50px 15px; + width: 320px; + height: 568px; + background: #1e1e1e; + border-radius: 20px; +} +.marvel-device.nexus5:before { + border-radius: 600px / 50px; + background: inherit; + content: ""; + top: 0; + position: absolute; + height: 103.1%; + width: calc(100% - 26px); + top: 50%; + left: 50%; + -webkit-transform: translateX(-50%) translateY(-50%); + transform: translateX(-50%) translateY(-50%); +} +.marvel-device.nexus5 .top-bar { + width: calc(100% - 8px); + height: calc(100% - 6px); + position: absolute; + top: 3px; + left: 4px; + border-radius: 20px; + background: #181818; +} +.marvel-device.nexus5 .top-bar:before { + border-radius: 600px / 50px; + background: inherit; + content: ""; + top: 0; + position: absolute; + height: 103%; + width: calc(100% - 26px); + top: 50%; + left: 50%; + -webkit-transform: translateX(-50%) translateY(-50%); + transform: translateX(-50%) translateY(-50%); +} +.marvel-device.nexus5 .bottom-bar { + display: none; +} +.marvel-device.nexus5 .sleep { + width: 3px; + position: absolute; + left: -3px; + top: 110px; + height: 100px; + background: inherit; + border-radius: 2px 0px 0px 2px; +} +.marvel-device.nexus5 .volume { + width: 3px; + position: absolute; + right: -3px; + top: 70px; + height: 45px; + background: inherit; + border-radius: 0px 2px 2px 0px; +} +.marvel-device.nexus5 .camera { + background: #3c3d3d; + width: 10px; + height: 10px; + position: absolute; + top: 18px; + left: 50%; + z-index: 3; + margin-left: -5px; + border-radius: 100%; +} +.marvel-device.nexus5 .camera:before { + background: #3c3d3d; + width: 6px; + height: 6px; + content: ""; + display: block; + position: absolute; + top: 2px; + left: -100px; + z-index: 3; + border-radius: 100%; +} +.marvel-device.nexus5.landscape { + padding: 15px 50px 15px 50px; + height: 320px; + width: 568px; +} +.marvel-device.nexus5.landscape:before { + width: 103.1%; + height: calc(100% - 26px); + border-radius: 50px / 600px; +} +.marvel-device.nexus5.landscape .top-bar { + left: 3px; + top: 4px; + height: calc(100% - 8px); + width: calc(100% - 6px); +} +.marvel-device.nexus5.landscape .top-bar:before { + width: 103%; + height: calc(100% - 26px); + border-radius: 50px / 600px; +} +.marvel-device.nexus5.landscape .sleep { + height: 3px; + width: 100px; + left: calc(100% - 210px); + top: -3px; + border-radius: 2px 2px 0px 0px; +} +.marvel-device.nexus5.landscape .volume { + height: 3px; + width: 45px; + right: 70px; + top: 100%; + border-radius: 0px 0px 2px 2px; +} +.marvel-device.nexus5.landscape .camera { + top: 50%; + left: calc(100% - 18px); + margin-left: 0; + margin-top: -5px; +} +.marvel-device.nexus5.landscape .camera:before { + top: -100px; + left: 2px; +} +.marvel-device.s5 { + padding: 60px 18px; + border-radius: 42px; + width: 320px; + height: 568px; + background: #bcbcbc; +} +.marvel-device.s5:before, +.marvel-device.s5:after { + width: calc(100% - 52px); + content: ""; + display: block; + height: 26px; + background: inherit; + position: absolute; + border-radius: 500px / 40px; + left: 50%; + -webkit-transform: translateX(-50%); + transform: translateX(-50%); +} +.marvel-device.s5:before { + top: -7px; +} +.marvel-device.s5:after { + bottom: -7px; +} +.marvel-device.s5 .bottom-bar { + display: none; +} +.marvel-device.s5 .top-bar { + border-radius: 37px; + width: calc(100% - 10px); + height: calc(100% - 10px); + top: 5px; + left: 5px; + background: radial-gradient(rgba(0, 0, 0, 0.02) 20%, transparent 60%) 0 0, + radial-gradient(rgba(0, 0, 0, 0.02) 20%, transparent 60%) 3px 3px; + background-color: white; + background-size: 4px 4px; + background-position: center; + z-index: 2; + position: absolute; +} +.marvel-device.s5 .top-bar:before, +.marvel-device.s5 .top-bar:after { + width: calc(100% - 48px); + content: ""; + display: block; + height: 26px; + background: inherit; + position: absolute; + border-radius: 500px / 40px; + left: 50%; + -webkit-transform: translateX(-50%); + transform: translateX(-50%); +} +.marvel-device.s5 .top-bar:before { + top: -7px; +} +.marvel-device.s5 .top-bar:after { + bottom: -7px; +} +.marvel-device.s5 .sleep { + width: 3px; + position: absolute; + left: -3px; + top: 100px; + height: 100px; + background: #cecece; + border-radius: 2px 0px 0px 2px; +} +.marvel-device.s5 .speaker { + width: 68px; + height: 8px; + position: absolute; + top: 20px; + display: block; + z-index: 3; + left: 50%; + margin-left: -34px; + background-color: #bcbcbc; + background-position: top left; + border-radius: 4px; +} +.marvel-device.s5 .sensor { + display: block; + position: absolute; + top: 20px; + right: 110px; + background: #3c3d3d; + border-radius: 100%; + width: 8px; + height: 8px; + z-index: 3; +} +.marvel-device.s5 .sensor:after { + display: block; + content: ""; + position: absolute; + top: 0px; + right: 12px; + background: #3c3d3d; + border-radius: 100%; + width: 8px; + height: 8px; + z-index: 3; +} +.marvel-device.s5 .camera { + display: block; + position: absolute; + top: 24px; + right: 42px; + background: black; + border-radius: 100%; + width: 10px; + height: 10px; + z-index: 3; +} +.marvel-device.s5 .camera:before { + width: 4px; + height: 4px; + background: #3c3d3d; + border-radius: 100%; + position: absolute; + content: ""; + top: 50%; + left: 50%; + margin-top: -2px; + margin-left: -2px; +} +.marvel-device.s5 .home { + position: absolute; + z-index: 3; + bottom: 17px; + left: 50%; + width: 70px; + height: 20px; + background: white; + border-radius: 18px; + display: block; + margin-left: -35px; + border: 2px solid black; +} +.marvel-device.s5.landscape { + padding: 18px 60px; + height: 320px; + width: 568px; +} +.marvel-device.s5.landscape:before, +.marvel-device.s5.landscape:after { + height: calc(100% - 52px); + width: 26px; + border-radius: 40px / 500px; + -webkit-transform: translateY(-50%); + transform: translateY(-50%); +} +.marvel-device.s5.landscape:before { + top: 50%; + left: -7px; +} +.marvel-device.s5.landscape:after { + top: 50%; + left: auto; + right: -7px; +} +.marvel-device.s5.landscape .top-bar:before, +.marvel-device.s5.landscape .top-bar:after { + width: 26px; + height: calc(100% - 48px); + border-radius: 40px / 500px; + -webkit-transform: translateY(-50%); + transform: translateY(-50%); +} +.marvel-device.s5.landscape .top-bar:before { + right: -7px; + top: 50%; + left: auto; +} +.marvel-device.s5.landscape .top-bar:after { + left: -7px; + top: 50%; + right: auto; +} +.marvel-device.s5.landscape .sleep { + height: 3px; + width: 100px; + left: calc(100% - 200px); + top: -3px; + border-radius: 2px 2px 0px 0px; +} +.marvel-device.s5.landscape .speaker { + height: 68px; + width: 8px; + left: calc(100% - 20px); + top: 50%; + margin-left: 0; + margin-top: -34px; +} +.marvel-device.s5.landscape .sensor { + right: 20px; + top: calc(100% - 110px); +} +.marvel-device.s5.landscape .sensor:after { + left: -12px; + right: 0px; +} +.marvel-device.s5.landscape .camera { + top: calc(100% - 42px); + right: 24px; +} +.marvel-device.s5.landscape .home { + width: 20px; + height: 70px; + bottom: 50%; + margin-bottom: -35px; + margin-left: 0; + left: 17px; +} +.marvel-device.s5.black { + background: #1e1e1e; +} +.marvel-device.s5.black .speaker { + background: black; +} +.marvel-device.s5.black .sleep { + background: #1e1e1e; +} +.marvel-device.s5.black .top-bar { + background: radial-gradient(rgba(0, 0, 0, 0.05) 20%, transparent 60%) 0 0, + radial-gradient(rgba(0, 0, 0, 0.05) 20%, transparent 60%) 3px 3px; + background-color: #2c2b2c; + background-size: 4px 4px; +} +.marvel-device.s5.black .home { + background: #2c2b2c; +} +.marvel-device.lumia920 { + padding: 80px 35px 125px 35px; + background: #ffdd00; + width: 320px; + height: 533px; + border-radius: 40px / 3px; +} +.marvel-device.lumia920 .bottom-bar { + display: none; +} +.marvel-device.lumia920 .top-bar { + width: calc(100% - 24px); + height: calc(100% - 32px); + position: absolute; + top: 16px; + left: 12px; + border-radius: 24px; + background: black; + z-index: 1; +} +.marvel-device.lumia920 .top-bar:before { + background: #1e1e1e; + display: block; + content: ""; + width: calc(100% - 4px); + height: calc(100% - 4px); + top: 2px; + left: 2px; + position: absolute; + border-radius: 22px; +} +.marvel-device.lumia920 .volume { + width: 3px; + position: absolute; + top: 130px; + height: 100px; + background: #1e1e1e; + right: -3px; + border-radius: 0px 2px 2px 0px; +} +.marvel-device.lumia920 .volume:before { + width: 3px; + position: absolute; + top: 190px; + content: ""; + display: block; + height: 50px; + background: inherit; + right: 0px; + border-radius: 0px 2px 2px 0px; +} +.marvel-device.lumia920 .volume:after { + width: 3px; + position: absolute; + top: 460px; + content: ""; + display: block; + height: 50px; + background: inherit; + right: 0px; + border-radius: 0px 2px 2px 0px; +} +.marvel-device.lumia920 .camera { + background: #3c3d3d; + width: 10px; + height: 10px; + position: absolute; + top: 34px; + right: 130px; + z-index: 5; + border-radius: 5px; +} +.marvel-device.lumia920 .speaker { + background: #292728; + width: 64px; + height: 10px; + position: absolute; + top: 38px; + left: 50%; + margin-left: -32px; + border-radius: 5px; + z-index: 3; +} +.marvel-device.lumia920.landscape { + padding: 35px 80px 35px 125px; + height: 320px; + width: 568px; + border-radius: 2px / 100px; +} +.marvel-device.lumia920.landscape .top-bar { + height: calc(100% - 24px); + width: calc(100% - 32px); + left: 16px; + top: 12px; +} +.marvel-device.lumia920.landscape .volume { + height: 3px; + right: 130px; + width: 100px; + top: 100%; + border-radius: 0px 0px 2px 2px; +} +.marvel-device.lumia920.landscape .volume:before { + height: 3px; + right: 190px; + top: 0px; + width: 50px; + border-radius: 0px 0px 2px 2px; +} +.marvel-device.lumia920.landscape .volume:after { + height: 3px; + right: 430px; + top: 0px; + width: 50px; + border-radius: 0px 0px 2px 2px; +} +.marvel-device.lumia920.landscape .camera { + right: 30px; + top: calc(100% - 140px); +} +.marvel-device.lumia920.landscape .speaker { + width: 10px; + height: 64px; + top: 50%; + margin-left: 0; + margin-top: -32px; + left: calc(100% - 48px); +} +.marvel-device.lumia920.black { + background: black; +} +.marvel-device.lumia920.white { + background: white; + -webkit-box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2); + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2); +} +.marvel-device.lumia920.blue { + background: #00acdd; +} +.marvel-device.lumia920.red { + background: #cc3e32; +} +.marvel-device.htc-one { + padding: 72px 25px 100px 25px; + width: 320px; + height: 568px; + background: #bebebe; + border-radius: 34px; +} +.marvel-device.htc-one:before { + content: ""; + display: block; + width: calc(100% - 4px); + height: calc(100% - 4px); + position: absolute; + top: 2px; + left: 2px; + background: #adadad; + border-radius: 32px; +} +.marvel-device.htc-one:after { + content: ""; + display: block; + width: calc(100% - 8px); + height: calc(100% - 8px); + position: absolute; + top: 4px; + left: 4px; + background: #eeeeee; + border-radius: 30px; +} +.marvel-device.htc-one .top-bar { + width: calc(100% - 4px); + height: 635px; + position: absolute; + background: #424242; + top: 50px; + z-index: 1; + left: 2px; +} +.marvel-device.htc-one .top-bar:before { + content: ""; + position: absolute; + width: calc(100% - 4px); + height: 100%; + position: absolute; + background: black; + top: 0px; + z-index: 1; + left: 2px; +} +.marvel-device.htc-one .bottom-bar { + display: none; +} +.marvel-device.htc-one .speaker { + height: 16px; + width: 216px; + display: block; + position: absolute; + top: 22px; + z-index: 2; + left: 50%; + margin-left: -108px; + background: radial-gradient(#343434 25%, transparent 50%) 0 0, + radial-gradient(#343434 25%, transparent 50%) 4px 4px; + background-size: 4px 4px; + background-position: top left; +} +.marvel-device.htc-one .speaker:after { + content: ""; + height: 16px; + width: 216px; + display: block; + position: absolute; + top: 676px; + z-index: 2; + left: 50%; + margin-left: -108px; + background: inherit; +} +.marvel-device.htc-one .camera { + display: block; + position: absolute; + top: 18px; + right: 38px; + background: #3c3d3d; + border-radius: 100%; + width: 24px; + height: 24px; + z-index: 3; +} +.marvel-device.htc-one .camera:before { + width: 8px; + height: 8px; + background: black; + border-radius: 100%; + position: absolute; + content: ""; + top: 50%; + left: 50%; + margin-top: -4px; + margin-left: -4px; +} +.marvel-device.htc-one .sensor { + display: block; + position: absolute; + top: 29px; + left: 60px; + background: #3c3d3d; + border-radius: 100%; + width: 8px; + height: 8px; + z-index: 3; +} +.marvel-device.htc-one .sensor:after { + display: block; + content: ""; + position: absolute; + top: 0px; + right: 12px; + background: #3c3d3d; + border-radius: 100%; + width: 8px; + height: 8px; + z-index: 3; +} +.marvel-device.htc-one.landscape { + padding: 25px 72px 25px 100px; + height: 320px; + width: 568px; +} +.marvel-device.htc-one.landscape .top-bar { + height: calc(100% - 4px); + width: 635px; + left: calc(100% - 685px); + top: 2px; +} +.marvel-device.htc-one.landscape .speaker { + width: 16px; + height: 216px; + left: calc(100% - 38px); + top: 50%; + margin-left: 0px; + margin-top: -108px; +} +.marvel-device.htc-one.landscape .speaker:after { + width: 16px; + height: 216px; + left: calc(100% - 692px); + top: 50%; + margin-left: 0; + margin-top: -108px; +} +.marvel-device.htc-one.landscape .camera { + right: 18px; + top: calc(100% - 38px); +} +.marvel-device.htc-one.landscape .sensor { + left: calc(100% - 29px); + top: 60px; +} +.marvel-device.htc-one.landscape .sensor :after { + right: 0; + top: -12px; +} +.marvel-device.ipad { + width: 576px; + height: 768px; + padding: 90px 25px; + background: #242324; + border-radius: 44px; +} +.marvel-device.ipad:before { + width: calc(100% - 8px); + height: calc(100% - 8px); + position: absolute; + content: ""; + display: block; + top: 4px; + left: 4px; + border-radius: 40px; + background: #1e1e1e; +} +.marvel-device.ipad .camera { + background: #3c3d3d; + width: 10px; + height: 10px; + position: absolute; + top: 44px; + left: 50%; + margin-left: -5px; + border-radius: 100%; +} +.marvel-device.ipad .top-bar, +.marvel-device.ipad .bottom-bar { + display: none; +} +.marvel-device.ipad .home { + background: #242324; + border-radius: 36px; + width: 50px; + height: 50px; + position: absolute; + left: 50%; + margin-left: -25px; + bottom: 22px; +} +.marvel-device.ipad .home:after { + width: 15px; + height: 15px; + margin-top: -8px; + margin-left: -8px; + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 4px; + position: absolute; + display: block; + content: ""; + top: 50%; + left: 50%; +} +.marvel-device.ipad.landscape { + height: 576px; + width: 768px; + padding: 25px 90px; +} +.marvel-device.ipad.landscape .camera { + left: calc(100% - 44px); + top: 50%; + margin-left: 0; + margin-top: -5px; +} +.marvel-device.ipad.landscape .home { + top: 50%; + left: 22px; + margin-left: 0; + margin-top: -25px; +} +.marvel-device.ipad.silver { + background: #bcbcbc; +} +.marvel-device.ipad.silver:before { + background: #fcfcfc; +} +.marvel-device.ipad.silver .home { + background: #fcfcfc; + -webkit-box-shadow: inset 0 0 0 1px #bcbcbc; + box-shadow: inset 0 0 0 1px #bcbcbc; +} +.marvel-device.ipad.silver .home:after { + border: 1px solid rgba(0, 0, 0, 0.2); +} +.marvel-device.macbook { + width: 960px; + height: 600px; + padding: 44px 44px 76px; + margin: 0 auto; + background: #bebebe; + border-radius: 34px; +} +.marvel-device.macbook:before { + width: calc(100% - 8px); + height: calc(100% - 8px); + position: absolute; + content: ""; + display: block; + top: 4px; + left: 4px; + border-radius: 30px; + background: #1e1e1e; +} +.marvel-device.macbook .top-bar { + width: calc(100% + 2 * 70px); + height: 40px; + position: absolute; + content: ""; + display: block; + top: 680px; + left: -70px; + border-bottom-left-radius: 90px 18px; + border-bottom-right-radius: 90px 18px; + background: #bebebe; + -webkit-box-shadow: inset 0px -4px 13px 3px rgba(34, 34, 34, 0.6); + box-shadow: inset 0px -4px 13px 3px rgba(34, 34, 34, 0.6); +} +.marvel-device.macbook .top-bar:before { + width: 100%; + height: 24px; + content: ""; + display: block; + top: 0; + left: 0; + background: #f0f0f0; + border-bottom: 2px solid #aaa; + border-radius: 5px; + position: relative; +} +.marvel-device.macbook .top-bar:after { + width: 16%; + height: 14px; + content: ""; + display: block; + top: 0; + background: #ddd; + position: absolute; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; + border-radius: 0 0 20px 20px; + -webkit-box-shadow: inset 0px -3px 10px #999; + box-shadow: inset 0px -3px 10px #999; +} +.marvel-device.macbook .bottom-bar { + background: transparent; + width: calc(100% + 2 * 70px); + height: 26px; + position: absolute; + content: ""; + display: block; + top: 680px; + left: -70px; +} +.marvel-device.macbook .bottom-bar:before, +.marvel-device.macbook .bottom-bar:after { + height: calc(100% - 2px); + width: 80px; + content: ""; + display: block; + top: 0; + position: absolute; +} +.marvel-device.macbook .bottom-bar:before { + left: 0; + background: #f0f0f0; + background: -webkit-gradient( + linear, + left top, + right top, + from(#747474), + color-stop(5%, #c3c3c3), + color-stop(14%, #ebebeb), + color-stop(41%, #979797), + color-stop(80%, #f0f0f0), + color-stop(100%, #f0f0f0), + to(#f0f0f0) + ); + background: linear-gradient( + to right, + #747474 0%, + #c3c3c3 5%, + #ebebeb 14%, + #979797 41%, + #f0f0f0 80%, + #f0f0f0 100%, + #f0f0f0 100% + ); +} +.marvel-device.macbook .bottom-bar:after { + right: 0; + background: #f0f0f0; + background: -webkit-gradient( + linear, + left top, + right top, + from(#f0f0f0), + color-stop(0%, #f0f0f0), + color-stop(20%, #f0f0f0), + color-stop(59%, #979797), + color-stop(86%, #ebebeb), + color-stop(95%, #c3c3c3), + to(#747474) + ); + background: linear-gradient( + to right, + #f0f0f0 0%, + #f0f0f0 0%, + #f0f0f0 20%, + #979797 59%, + #ebebeb 86%, + #c3c3c3 95%, + #747474 100% + ); +} +.marvel-device.macbook .camera { + background: #3c3d3d; + width: 10px; + height: 10px; + position: absolute; + top: 20px; + left: 50%; + margin-left: -5px; + border-radius: 100%; +} +.marvel-device.macbook .home { + display: none; +} +.marvel-device.iphone-x { + width: 375px; + height: 812px; + padding: 26px; + background: #fdfdfd; + -webkit-box-shadow: inset 0 0 11px 0 black; + box-shadow: inset 0 0 11px 0 black; + border-radius: 66px; +} +.marvel-device.iphone-x .overflow { + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + border-radius: 66px; + overflow: hidden; +} +.marvel-device.iphone-x .shadow { + border-radius: 100%; + width: 90px; + height: 90px; + position: absolute; + background: radial-gradient( + ellipse at center, + rgba(0, 0, 0, 0.6) 0%, + rgba(255, 255, 255, 0) 60% + ); +} +.marvel-device.iphone-x .shadow--tl { + top: -20px; + left: -20px; +} +.marvel-device.iphone-x .shadow--tr { + top: -20px; + right: -20px; +} +.marvel-device.iphone-x .shadow--bl { + bottom: -20px; + left: -20px; +} +.marvel-device.iphone-x .shadow--br { + bottom: -20px; + right: -20px; +} +.marvel-device.iphone-x:before { + width: calc(100% - 10px); + height: calc(100% - 10px); + position: absolute; + top: 5px; + content: ""; + left: 5px; + border-radius: 61px; + background: black; + z-index: 1; +} +.marvel-device.iphone-x .inner-shadow { + width: calc(100% - 20px); + height: calc(100% - 20px); + position: absolute; + top: 10px; + overflow: hidden; + left: 10px; + border-radius: 56px; + -webkit-box-shadow: inset 0 0 15px 0 rgba(255, 255, 255, 0.66); + box-shadow: inset 0 0 15px 0 rgba(255, 255, 255, 0.66); + z-index: 1; +} +.marvel-device.iphone-x .inner-shadow:before { + -webkit-box-shadow: inset 0 0 20px 0 #ffffff; + box-shadow: inset 0 0 20px 0 #ffffff; + width: 100%; + height: 116%; + position: absolute; + top: -8%; + content: ""; + left: 0; + border-radius: 200px / 112px; + z-index: 2; +} +.marvel-device.iphone-x .screen { + border-radius: 40px; + -webkit-box-shadow: none; + box-shadow: none; +} +.marvel-device.iphone-x .top-bar, +.marvel-device.iphone-x .bottom-bar { + width: 100%; + position: absolute; + height: 8px; + background: rgba(0, 0, 0, 0.1); + left: 0; +} +.marvel-device.iphone-x .top-bar { + top: 80px; +} +.marvel-device.iphone-x .bottom-bar { + bottom: 80px; +} +.marvel-device.iphone-x .volume, +.marvel-device.iphone-x .volume:before, +.marvel-device.iphone-x .volume:after, +.marvel-device.iphone-x .sleep { + width: 3px; + background: #b5b5b5; + position: absolute; +} +.marvel-device.iphone-x .volume { + left: -3px; + top: 116px; + height: 32px; +} +.marvel-device.iphone-x .volume:before { + height: 62px; + top: 62px; + content: ""; + left: 0; +} +.marvel-device.iphone-x .volume:after { + height: 62px; + top: 140px; + content: ""; + left: 0; +} +.marvel-device.iphone-x .sleep { + height: 96px; + top: 200px; + right: -3px; +} +.marvel-device.iphone-x .camera { + width: 6px; + height: 6px; + top: 9px; + border-radius: 100%; + position: absolute; + left: 154px; + background: #0d4d71; +} +.marvel-device.iphone-x .speaker { + height: 6px; + width: 60px; + left: 50%; + position: absolute; + top: 9px; + margin-left: -30px; + background: #171818; + border-radius: 6px; +} +.marvel-device.iphone-x .notch { + position: absolute; + width: 210px; + height: 30px; + top: 26px; + left: 108px; + z-index: 4; + background: black; + border-bottom-left-radius: 24px; + border-bottom-right-radius: 24px; +} +.marvel-device.iphone-x .notch:before, +.marvel-device.iphone-x .notch:after { + content: ""; + height: 8px; + position: absolute; + top: 0; + width: 8px; +} +.marvel-device.iphone-x .notch:after { + background: radial-gradient( + circle at bottom left, + transparent 0, + transparent 70%, + black 70%, + black 100% + ); + left: -8px; +} +.marvel-device.iphone-x .notch:before { + background: radial-gradient( + circle at bottom right, + transparent 0, + transparent 70%, + black 70%, + black 100% + ); + right: -8px; +} +.marvel-device.iphone-x.landscape { + height: 375px; + width: 812px; +} +.marvel-device.iphone-x.landscape .top-bar, +.marvel-device.iphone-x.landscape .bottom-bar { + width: 8px; + height: 100%; + top: 0; +} +.marvel-device.iphone-x.landscape .top-bar { + left: 80px; +} +.marvel-device.iphone-x.landscape .bottom-bar { + right: 80px; + bottom: auto; + left: auto; +} +.marvel-device.iphone-x.landscape .volume, +.marvel-device.iphone-x.landscape .volume:before, +.marvel-device.iphone-x.landscape .volume:after, +.marvel-device.iphone-x.landscape .sleep { + height: 3px; +} +.marvel-device.iphone-x.landscape .inner-shadow:before { + height: 100%; + width: 116%; + left: -8%; + top: 0; + border-radius: 112px / 200px; +} +.marvel-device.iphone-x.landscape .volume { + bottom: -3px; + top: auto; + left: 116px; + width: 32px; +} +.marvel-device.iphone-x.landscape .volume:before { + width: 62px; + left: 62px; + top: 0; +} +.marvel-device.iphone-x.landscape .volume:after { + width: 62px; + left: 140px; + top: 0; +} +.marvel-device.iphone-x.landscape .sleep { + width: 96px; + left: 200px; + top: -3px; + right: auto; +} +.marvel-device.iphone-x.landscape .camera { + left: 9px; + bottom: 154px; + top: auto; +} +.marvel-device.iphone-x.landscape .speaker { + width: 6px; + height: 60px; + left: 9px; + top: 50%; + margin-top: -30px; + margin-left: 0; +} +.marvel-device.iphone-x.landscape .notch { + height: 210px; + width: 30px; + left: 26px; + bottom: 108px; + top: auto; + border-top-right-radius: 24px; + border-bottom-right-radius: 24px; + border-bottom-left-radius: 0; +} +.marvel-device.iphone-x.landscape .notch:before, +.marvel-device.iphone-x.landscape .notch:after { + left: 0; +} +.marvel-device.iphone-x.landscape .notch:after { + background: radial-gradient( + circle at bottom right, + transparent 0, + transparent 70%, + black 70%, + black 100% + ); + bottom: -8px; + top: auto; +} +.marvel-device.iphone-x.landscape .notch:before { + background: radial-gradient( + circle at top right, + transparent 0, + transparent 70%, + black 70%, + black 100% + ); + top: -8px; +} +.marvel-device.note8 { + width: 400px; + height: 822px; + background: black; + border-radius: 34px; + padding: 45px 10px; +} +.marvel-device.note8 .overflow { + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + border-radius: 34px; + overflow: hidden; +} +.marvel-device.note8 .speaker { + height: 8px; + width: 56px; + left: 50%; + position: absolute; + top: 25px; + margin-left: -28px; + background: #171818; + z-index: 1; + border-radius: 8px; +} +.marvel-device.note8 .camera { + height: 18px; + width: 18px; + left: 86px; + position: absolute; + top: 18px; + background: #212b36; + z-index: 1; + border-radius: 100%; +} +.marvel-device.note8 .camera:before { + content: ""; + height: 8px; + width: 8px; + left: -22px; + position: absolute; + top: 5px; + background: #212b36; + z-index: 1; + border-radius: 100%; +} +.marvel-device.note8 .sensors { + height: 10px; + width: 10px; + left: 120px; + position: absolute; + top: 22px; + background: #1d233b; + z-index: 1; + border-radius: 100%; +} +.marvel-device.note8 .sensors:before { + content: ""; + height: 10px; + width: 10px; + left: 18px; + position: absolute; + top: 0; + background: #1d233b; + z-index: 1; + border-radius: 100%; +} +.marvel-device.note8 .more-sensors { + height: 16px; + width: 16px; + left: 285px; + position: absolute; + top: 18px; + background: #33244a; + -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.1); + box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.1); + z-index: 1; + border-radius: 100%; +} +.marvel-device.note8 .more-sensors:before { + content: ""; + height: 11px; + width: 11px; + left: 40px; + position: absolute; + top: 4px; + background: #214a61; + z-index: 1; + border-radius: 100%; +} +.marvel-device.note8 .sleep { + width: 2px; + height: 56px; + background: black; + position: absolute; + top: 288px; + right: -2px; +} +.marvel-device.note8 .volume { + width: 2px; + height: 120px; + background: black; + position: absolute; + top: 168px; + left: -2px; +} +.marvel-device.note8 .volume:before { + content: ""; + top: 168px; + width: 2px; + position: absolute; + left: 0; + background: black; + height: 56px; +} +.marvel-device.note8 .inner { + width: 100%; + height: calc(100% - 8px); + position: absolute; + top: 2px; + content: ""; + left: 0px; + border-radius: 34px; + border-top: 2px solid #9fa0a2; + border-bottom: 2px solid #9fa0a2; + background: black; + z-index: 1; + -webkit-box-shadow: inset 0 0 6px 0 rgba(255, 255, 255, 0.5); + box-shadow: inset 0 0 6px 0 rgba(255, 255, 255, 0.5); +} +.marvel-device.note8 .shadow { + -webkit-box-shadow: inset 0 0 60px 0 white, + inset 0 0 30px 0 rgba(255, 255, 255, 0.5), 0 0 20px 0 white, + 0 0 20px 0 rgba(255, 255, 255, 0.5); + box-shadow: inset 0 0 60px 0 white, inset 0 0 30px 0 rgba(255, 255, 255, 0.5), + 0 0 20px 0 white, 0 0 20px 0 rgba(255, 255, 255, 0.5); + height: 101%; + position: absolute; + top: -0.5%; + content: ""; + width: calc(100% - 20px); + left: 10px; + border-radius: 38px; + z-index: 5; + pointer-events: none; +} +.marvel-device.note8 .screen { + border-radius: 14px; + -webkit-box-shadow: none; + box-shadow: none; +} +.marvel-device.note8.landscape { + height: 400px; + width: 822px; + padding: 10px 45px; +} +.marvel-device.note8.landscape .speaker { + height: 56px; + width: 8px; + top: 50%; + margin-top: -28px; + margin-left: 0; + right: 25px; + left: auto; +} +.marvel-device.note8.landscape .camera { + top: 86px; + right: 18px; + left: auto; +} +.marvel-device.note8.landscape .camera:before { + top: -22px; + left: 5px; +} +.marvel-device.note8.landscape .sensors { + top: 120px; + right: 22px; + left: auto; +} +.marvel-device.note8.landscape .sensors:before { + top: 18px; + left: 0; +} +.marvel-device.note8.landscape .more-sensors { + top: 285px; + right: 18px; + left: auto; +} +.marvel-device.note8.landscape .more-sensors:before { + top: 40px; + left: 4px; +} +.marvel-device.note8.landscape .sleep { + bottom: -2px; + top: auto; + right: 288px; + width: 56px; + height: 2px; +} +.marvel-device.note8.landscape .volume { + width: 120px; + height: 2px; + top: -2px; + right: 168px; + left: auto; +} +.marvel-device.note8.landscape .volume:before { + right: 168px; + left: auto; + top: 0; + width: 56px; + height: 2px; +} +.marvel-device.note8.landscape .inner { + height: 100%; + width: calc(100% - 8px); + left: 2px; + top: 0; + border-top: 0; + border-bottom: 0; + border-left: 2px solid #9fa0a2; + border-right: 2px solid #9fa0a2; +} +.marvel-device.note8.landscape .shadow { + width: 101%; + height: calc(100% - 20px); + left: -0.5%; + top: 10px; +} diff --git a/components/ResponsiveStory/src/index.tsx b/components/ResponsiveStory/src/index.tsx index 67b3b32..39316ce 100644 --- a/components/ResponsiveStory/src/index.tsx +++ b/components/ResponsiveStory/src/index.tsx @@ -2,11 +2,10 @@ import React from "react"; import makeClass from "clsx"; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -import { Source } from "@storybook/addon-docs/blocks"; -import { css } from "@emotion/core"; -import styled from "@emotion/styled"; +import { Source } from "@storybook/addon-docs"; + +import styles from "./ResponsiveStory.module.css"; +import "./devices.min.css"; const devices = ["iPad", "iPhone", "mac"] as const; type Device = typeof devices[number]; @@ -21,96 +20,7 @@ interface IsLandscape { isLandscape?: boolean; } -const DeviceSelect = styled.select` - cursor: pointer; - color: rgb(51, 51, 51); - text-align: center; - appearance: none; - font-size: 12px; - line-height: 16px; - font-family: "Nunito Sans", -apple-system, ".SFNSText-Regular", - "San Francisco", BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, - Arial, sans-serif; - font-weight: 700; - padding: 4px 10px; - background: rgb(255, 255, 255); - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 4px; - margin: 20px 0; - text-align-last: center; -`; - -const InfoBar = styled.div` - height: 30px; - display: flex; - justify-content: space-between; - align-items: flex-end; - padding: 0 25px 0 34px; - font-size: 14px; -`; - -const IframeLabel = styled.div` - width: 100%; - align-items: center; - justify-content: center; - display: flex; - margin-top: 14px; - - > *:not(:last-child) { - margin-right: 10px; - } -`; - -const ShowCodeButton = styled.button` - cursor: pointer; - display: flex; - align-items: center; - color: rgb(51, 51, 51); - font-size: 12px; - line-height: 16px; - font-family: "Nunito Sans", -apple-system, ".SFNSText-Regular", - "San Francisco", BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, - Arial, sans-serif; - font-weight: 700; - margin-left: -1px; - padding: 4px 10px; - background: rgb(255, 255, 255); - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 4px; -`; - -const ResponsiveStoryWrapper = styled.div` - margin: 30px auto; - display: flex; - flex-direction: column; -`; - -const Iframe = styled.iframe` - border: none; - width: 100%; - height: 100%; - margin: auto; - display: block; - - ${({ device, isLandscape }) => css` - ${device === "iPhone" && - isLandscape && - css` - padding-left: 30px; - box-sizing: border-box; - `} - - ${device === "mac" && - css` - width: 1200px; - height: 750px; - transform: scale(0.8); - transform-origin: 0 0; - `} - `} -`; - -type DeviceProps = IsLandscape & { +export type DeviceProps = IsLandscape & { /** The content to display in the device screen */ children: React.ReactNode; /** Color to make iOS header */ @@ -152,9 +62,12 @@ const IPhone = ({
{!isLandscape && ( - +
{`${time.getHours() % 12}:${time.getMinutes()}`} - +
)} {children}
@@ -238,7 +151,10 @@ interface BottomProps extends ResponsiveStoryBaseProps { bottom: number; } -type ResponsiveStoryProps = ResponsiveStoryBaseProps | TopProps | BottomProps; +export type ResponsiveStoryProps = + | ResponsiveStoryBaseProps + | TopProps + | BottomProps; /** Render a story in an iframe so it's responsive */ export const ResponsiveStory = ({ @@ -261,9 +177,15 @@ export const ResponsiveStory = ({ const top = "top" in rest ? rest.top : undefined; const bottom = "bottom" in rest ? rest.bottom : undefined; - // TODO: should scale the device instead of scrolling? + const iframeClasses = makeClass( + styles.iframe, + currentDevice === "iPhone" && isLandscape && styles.iframeIphoneLandscape, + currentDevice === "mac" && styles.iframeMac + ); + return ( - -