diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 0b58f8ed..00000000 --- a/.editorconfig +++ /dev/null @@ -1,15 +0,0 @@ -root = true - -[*] -indent_style = space -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[{package.json,*.yml}] -indent_style = space -indent_size = 2 - -[*.md] -trim_trailing_whitespace = false \ No newline at end of file diff --git a/.gitignore b/.gitignore index dcbcabfb..1d074887 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules dist coverage +cosmos-export *.tgz .DS_Store diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..3619c556 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "js/ts.tsdk.path": "./node_modules/typescript/lib", + "js/ts.tsdk.promptToUseWorkspaceVersion": true, +} \ No newline at end of file diff --git a/Readme.md b/Readme.md index 9303a921..84454f96 100644 --- a/Readme.md +++ b/Readme.md @@ -2,11 +2,10 @@ ## Getting started -Install `yarn` (`>v1.7.0`) +Install `pnpm` through Corepack or your preferred package manager. -1. `npm i -g yarn` -1. `yarn install` -1. `yarn cosmos` +1. `pnpm install` +1. `pnpm cosmos` ## Regular tasks @@ -14,26 +13,36 @@ Install `yarn` (`>v1.7.0`) To install a specific package to all workspaces -`yarn add -DW ${package}` +`pnpm add -D ${package}` ### Installing dependencies to a specific package To install a specific package to a specific workspace -`yarn workspace @newfrontdoor/${workspace} add ${package} {--dev}` +`pnpm --filter @newfrontdoor/${workspace} add ${package} {--save-dev}` ### Bundling -When importing other packages within the ui library, make sure they have been bundled. -Either run `yarn install`, (or `yarn prepare`, which runs after install) +When importing published package outputs within the ui library, make sure they have been bundled. +Run `pnpm run build` when package `dist` output is needed. If you make a change to a package you depend on, -make sure you run `yarn prepare` to ensure the changes have been bundled +run `pnpm run build` to ensure the changes have been bundled. + +### Component fixtures + +Cosmos is the component fixture browser. + +1. `pnpm install` +1. `pnpm run build` if you need package `dist` output +1. `pnpm cosmos` + +Use `pnpm cosmos:export` to produce a static fixture export in `cosmos-export`. ### Publishing a package 1. Create a branch with your changes -1. From the root of the monorepo, run `yarn changeset` and follow the prompts +1. From the root of the monorepo, run `pnpm changeset` and follow the prompts 1. Create a Pull Request with the generated changeset, and your changes 1. Merge your Pull Request once the tests pass 1. Once merged, the release workflow will start diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index 5222bd97..00000000 --- a/babel.config.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - presets: [ - '@babel/preset-env', - '@babel/preset-react', - '@babel/preset-typescript' - ], - plugins: ['@babel/plugin-proposal-class-properties', 'inline-react-svg'], - env: { - test: { - plugins: ['@babel/plugin-transform-runtime'] - } - } -}; diff --git a/cosmos.decorator.js b/cosmos.decorator.js deleted file mode 100644 index 7b5e0709..00000000 --- a/cosmos.decorator.js +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import {ThemeProvider} from 'theme-ui'; -import {QueryClient, QueryClientProvider} from 'react-query'; -import theme from './cosmos/theme'; - -function pageLookup(href) { - // This depends on the fact that no sub-subdirectory of pages contains an index file - if (href.includes('/')) { - const root = href - .split('/') - .pop() - .reduce((url, element) => { - return url.concat('/', element); - }); - return `/${root}/[slug]`; - } - - switch (href) { - case '': - case 'sermons': - case 'search': - case 'all-sermons': - return `/${href}`; - default: - return '/[slug]'; - } -} - -const Link = ({href, ...rest}) => { - return ; -}; - -Link.propTypes = { - href: PropTypes.string.isRequired -}; - -const components = {a: Link}; - -const decorator = (props) => { - const client = new QueryClient(); - return ( - - - - ); -}; - -export default decorator; diff --git a/cosmos.decorator.jsx b/cosmos.decorator.jsx new file mode 100644 index 00000000..568f7f09 --- /dev/null +++ b/cosmos.decorator.jsx @@ -0,0 +1,58 @@ +import {ThemeUIProvider} from 'theme-ui'; +import {QueryClient, QueryClientProvider} from '@tanstack/react-query'; +import {ApiContext} from '@newfrontdoor/api-config'; +import theme from './cosmos/theme'; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + refetchOnWindowFocus: false + } + } +}); + +const apiConfig = { + baseUrl: 'https://cornerstoneapi.newfrontdoor.org/api/views/' +}; + +function pageLookup(href) { + if (/^https?:\/\//.test(href)) { + return href; + } + + // This depends on the fact that no sub-subdirectory of pages contains an index file + if (href.includes('/')) { + const parts = href.split('/').filter(Boolean); + const root = parts[parts.length - 1]; + return root ? `/${root}/[slug]` : '/[slug]'; + } + + switch (href) { + case '': + case 'sermons': + case 'search': + case 'all-sermons': + return `/${href}`; + default: + return '/[slug]'; + } +} + +const Link = ({href, ...rest}) => { + return ; +}; + +const components = {a: Link}; + +const decorator = (props) => { + return ( + + + + + + ); +}; + +export default decorator; diff --git a/cosmos.imports.ts b/cosmos.imports.ts new file mode 100644 index 00000000..fff497ec --- /dev/null +++ b/cosmos.imports.ts @@ -0,0 +1,108 @@ +// NOTE: This file is auto-generated by React Cosmos. +// It imports all fixture and decorator modules, making them available to the +// React Cosmos renderer along with the renderer config. + +import type { RendererConfig, UserModuleWrappers } from 'react-cosmos-core'; + +import './cosmos/msw/browser.js'; + +import * as fixture0 from './packages/about/__fixtures__/about.jsx'; +import * as fixture1 from './packages/audio-player/__fixtures__/audio-manager'; +import * as fixture2 from './packages/audio-player/__fixtures__/audio-player'; +import * as fixture3 from './packages/audio-player/__fixtures__/native-player'; +import * as fixture4 from './packages/bible/__fixtures__/bible-input'; +import * as fixture5 from './packages/bible/__fixtures__/bible'; +import * as fixture6 from './packages/blog/__fixtures__/blog.jsx'; +import * as fixture7 from './packages/blog/__fixtures__/post-page.jsx'; +import * as fixture8 from './packages/calendar/__fixtures__/calendar'; +import * as fixture9 from './packages/calendar/__fixtures__/drupal-calendar'; +import * as fixture10 from './packages/calendar/__fixtures__/elvanto-calendar'; +import * as fixture11 from './packages/calendar/__fixtures__/random-events'; +import * as fixture12 from './packages/carousel/__fixtures__/autoplay-off-hide-nav-dots'; +import * as fixture13 from './packages/carousel/__fixtures__/autoplay-off'; +import * as fixture14 from './packages/carousel/__fixtures__/autoplay-on'; +import * as fixture15 from './packages/carousel/__fixtures__/custom-dots'; +import * as fixture16 from './packages/collapse/__fixtures__/collapse'; +import * as fixture17 from './packages/collapse/__fixtures__/use-collapse'; +import * as fixture18 from './packages/content-editor/__fixtures__/editor.jsx'; +import * as fixture19 from './packages/footer/__fixtures__/footer'; +import * as fixture20 from './packages/footer/__fixtures__/slimline-footer'; +import * as fixture21 from './packages/form/__fixtures__/gridform.jsx'; +import * as fixture22 from './packages/grid-wrapper/__fixtures__/gridblock.jsx'; +import * as fixture23 from './packages/link/__fixtures__/link.jsx'; +import * as fixture24 from './packages/link/__fixtures__/navlink.jsx'; +import * as fixture25 from './packages/location-map/__fixtures__/location-map.jsx'; +import * as fixture26 from './packages/people/__fixtures__/allpeople.jsx'; +import * as fixture27 from './packages/ppt-generator/__fixtures__/ppt-generator.jsx'; +import * as fixture28 from './packages/s3-file-upload/__fixtures__/s3-dropzone'; +import * as fixture29 from './packages/search/__fixtures__/search-component.jsx'; +import * as fixture30 from './packages/sermon/__fixtures__/current-series.jsx'; +import * as fixture31 from './packages/sermon/__fixtures__/drupal.jsx'; +import * as fixture32 from './packages/sermon/__fixtures__/featured-series.jsx'; +import * as fixture33 from './packages/sermon/__fixtures__/latest-sermon-container.jsx'; +import * as fixture34 from './packages/sermon/__fixtures__/recent-series.jsx'; +import * as fixture35 from './packages/sermon/__fixtures__/series-sermon-list.jsx'; +import * as fixture36 from './packages/sermon/__fixtures__/sermon-table.jsx'; +import * as fixture37 from './packages/sermon/__fixtures__/sermons.jsx'; +import * as fixture38 from './packages/toggleselector/__fixtures__/toggleselector.jsx'; +import * as fixture39 from './packages/vanilla-form/__fixtures__/gridform.jsx'; + +import * as decorator0 from './cosmos.decorator.jsx'; + +export const rendererConfig: RendererConfig = { + "webSocketUrl": "ws://172.20.10.3:5001", + "rendererUrl": "http://localhost:5050" +}; + +const fixtures = { + 'packages/about/__fixtures__/about.jsx': { module: fixture0 }, + 'packages/audio-player/__fixtures__/audio-manager.tsx': { module: fixture1 }, + 'packages/audio-player/__fixtures__/audio-player.tsx': { module: fixture2 }, + 'packages/audio-player/__fixtures__/native-player.tsx': { module: fixture3 }, + 'packages/bible/__fixtures__/bible-input.ts': { module: fixture4 }, + 'packages/bible/__fixtures__/bible.tsx': { module: fixture5 }, + 'packages/blog/__fixtures__/blog.jsx': { module: fixture6 }, + 'packages/blog/__fixtures__/post-page.jsx': { module: fixture7 }, + 'packages/calendar/__fixtures__/calendar.tsx': { module: fixture8 }, + 'packages/calendar/__fixtures__/drupal-calendar.tsx': { module: fixture9 }, + 'packages/calendar/__fixtures__/elvanto-calendar.tsx': { module: fixture10 }, + 'packages/calendar/__fixtures__/random-events.tsx': { module: fixture11 }, + 'packages/carousel/__fixtures__/autoplay-off-hide-nav-dots.tsx': { module: fixture12 }, + 'packages/carousel/__fixtures__/autoplay-off.tsx': { module: fixture13 }, + 'packages/carousel/__fixtures__/autoplay-on.tsx': { module: fixture14 }, + 'packages/carousel/__fixtures__/custom-dots.tsx': { module: fixture15 }, + 'packages/collapse/__fixtures__/collapse.tsx': { module: fixture16 }, + 'packages/collapse/__fixtures__/use-collapse.tsx': { module: fixture17 }, + 'packages/content-editor/__fixtures__/editor.jsx': { module: fixture18 }, + 'packages/footer/__fixtures__/footer.tsx': { module: fixture19 }, + 'packages/footer/__fixtures__/slimline-footer.tsx': { module: fixture20 }, + 'packages/form/__fixtures__/gridform.jsx': { module: fixture21 }, + 'packages/grid-wrapper/__fixtures__/gridblock.jsx': { module: fixture22 }, + 'packages/link/__fixtures__/link.jsx': { module: fixture23 }, + 'packages/link/__fixtures__/navlink.jsx': { module: fixture24 }, + 'packages/location-map/__fixtures__/location-map.jsx': { module: fixture25 }, + 'packages/people/__fixtures__/allpeople.jsx': { module: fixture26 }, + 'packages/ppt-generator/__fixtures__/ppt-generator.jsx': { module: fixture27 }, + 'packages/s3-file-upload/__fixtures__/s3-dropzone.tsx': { module: fixture28 }, + 'packages/search/__fixtures__/search-component.jsx': { module: fixture29 }, + 'packages/sermon/__fixtures__/current-series.jsx': { module: fixture30 }, + 'packages/sermon/__fixtures__/drupal.jsx': { module: fixture31 }, + 'packages/sermon/__fixtures__/featured-series.jsx': { module: fixture32 }, + 'packages/sermon/__fixtures__/latest-sermon-container.jsx': { module: fixture33 }, + 'packages/sermon/__fixtures__/recent-series.jsx': { module: fixture34 }, + 'packages/sermon/__fixtures__/series-sermon-list.jsx': { module: fixture35 }, + 'packages/sermon/__fixtures__/sermon-table.jsx': { module: fixture36 }, + 'packages/sermon/__fixtures__/sermons.jsx': { module: fixture37 }, + 'packages/toggleselector/__fixtures__/toggleselector.jsx': { module: fixture38 }, + 'packages/vanilla-form/__fixtures__/gridform.jsx': { module: fixture39 } +}; + +const decorators = { + 'cosmos.decorator.jsx': { module: decorator0 } +}; + +export const moduleWrappers: UserModuleWrappers = { + lazy: false, + fixtures, + decorators +}; diff --git a/cosmos/cosmos.config.json b/cosmos/cosmos.config.json index 5e0b4263..43c10897 100644 --- a/cosmos/cosmos.config.json +++ b/cosmos/cosmos.config.json @@ -1,7 +1,21 @@ { - "staticPath": "static", - "publicUrl": "/static/", + "$schema": "../node_modules/react-cosmos/config.schema.json", "rootDir": "..", + "plugins": ["react-cosmos-plugin-vite"], + "globalImports": ["./cosmos/msw/browser.js"], + "watchDirs": ["packages", "cosmos"], + "ignore": [ + "**/dist/**", + "**/coverage/**", + "**/cosmos-export/**", + "**/cosmos.imports.ts", + "**/public/**" + ], + "lazy": true, + "vite": { + "configPath": "./cosmos/vite.config.ts", + "mainScriptUrl": "/cosmos/main.tsx" + }, "ui": { "responsivePreview": { "devices": [ @@ -13,8 +27,5 @@ { "label": "1080p", "width": 1920, "height": 1080 } ] } - }, - "webpack": { - "overridePath": "./cosmos/webpack.override.js" } } diff --git a/cosmos/index.html b/cosmos/index.html new file mode 100644 index 00000000..80a329a6 --- /dev/null +++ b/cosmos/index.html @@ -0,0 +1,12 @@ + + + + + + React Cosmos Renderer + + +
+ + + \ No newline at end of file diff --git a/cosmos/main.tsx b/cosmos/main.tsx new file mode 100644 index 00000000..7c79b553 --- /dev/null +++ b/cosmos/main.tsx @@ -0,0 +1,10 @@ +import { createRoot } from "react-dom/client"; +import { mountDomRenderer } from "react-cosmos/dom"; + +import { moduleWrappers, rendererConfig } from "../cosmos.imports"; + +mountDomRenderer({ + rendererConfig, + moduleWrappers, + createRoot +}); \ No newline at end of file diff --git a/cosmos/msw/browser.js b/cosmos/msw/browser.js new file mode 100644 index 00000000..21ca3ff1 --- /dev/null +++ b/cosmos/msw/browser.js @@ -0,0 +1,15 @@ +import {setupWorker} from 'msw/browser'; +import {handlers} from './handlers'; + +const worker = setupWorker(...handlers); + +if (typeof window !== 'undefined' && !window.__COSMOS_MSW_STARTED__) { + window.__COSMOS_MSW_STARTED__ = true; + + worker.start({ + onUnhandledRequest: 'bypass', + serviceWorker: { + url: '/mockServiceWorker.js' + } + }); +} diff --git a/cosmos/msw/handlers.js b/cosmos/msw/handlers.js new file mode 100644 index 00000000..2ebe8dae --- /dev/null +++ b/cosmos/msw/handlers.js @@ -0,0 +1,127 @@ +import {http, HttpResponse} from 'msw'; + +const apiViews = 'https://cornerstoneapi.newfrontdoor.org/api/views'; + +const calendarEvents = [ + { + id: 'cosmos-calendar-1', + nid: 'cosmos-calendar-1', + name: 'Morning Service', + color: '#2f80ed', + start_date: '2026-05-10T10:00:00+10:00', + end_date: '2026-05-10T11:30:00+10:00', + description: 'Fixture event supplied by MSW.', + location: 'Main Auditorium', + where: 'Main Auditorium', + url: '/events/morning-service' + }, + { + id: 'cosmos-calendar-2', + nid: 'cosmos-calendar-2', + name: 'Community Dinner', + color: '#27ae60', + start_date: '2026-05-13T18:00:00+10:00', + end_date: '2026-05-13T20:00:00+10:00', + description: 'Fixture event supplied by MSW.', + location: 'Hall', + where: 'Hall', + url: '/events/community-dinner' + } +]; + +const people = [ + { + name: 'Jane Elder', + title: 'Elder', + roles: ['Elder'], + email: 'jane@example.com', + image: 'https://vignette.wikia.nocookie.net/gumby/images/c/c7/Gumby_at_his_Desk.jpg' + }, + { + name: 'Sam Deacon', + title: 'Deacon', + roles: ['Deacon'], + email: 'sam@example.com', + image: 'https://vignette.wikia.nocookie.net/gumby/images/c/c7/Gumby_at_his_Desk.jpg' + }, + { + name: 'Alex Staff', + title: 'Staff', + roles: ['Staff Member'], + email: 'alex@example.com', + image: 'https://vignette.wikia.nocookie.net/gumby/images/c/c7/Gumby_at_his_Desk.jpg' + } +]; + +const sermon = { + node_title: 'A Fixture Sermon', + preacher: 'Jane Speaker', + datepreached: '2026-05-03', + url: '/sermons/a-fixture-sermon', + sermon_img: 'https://cornerstoneapi.newfrontdoor.org/sites/cornerstoneapi.newfrontdoor.org/files/default_images/sermonSeriesImage_1.jpg', + series_img: 'https://cornerstoneapi.newfrontdoor.org/sites/cornerstoneapi.newfrontdoor.org/files/default_images/sermonSeriesImage_1.jpg', + sermonseries: 'Fixture Series', + text: 'Romans 8:31-39' +}; + +const series = { + node_title: 'Fixture Series', + 'series thumbnail': + 'https://cornerstoneapi.newfrontdoor.org/sites/cornerstoneapi.newfrontdoor.org/files/default_images/sermonSeriesImage_1.jpg', + series_img: + 'https://cornerstoneapi.newfrontdoor.org/sites/cornerstoneapi.newfrontdoor.org/files/default_images/sermonSeriesImage_1.jpg', + image: + 'https://cornerstoneapi.newfrontdoor.org/sites/cornerstoneapi.newfrontdoor.org/files/default_images/sermonSeriesImage_1.jpg', + url: '/sermons/series/fixture-series', + series_id: 'fixture-series' +}; + +export const handlers = [ + http.get('https://serverless.newfrontdoor.org/bible', ({request}) => { + const passage = new URL(request.url).searchParams.get('passage') ?? 'Romans 8'; + + return HttpResponse.json([ + { + bookname: passage.split(' ')[0] ?? 'Romans', + chapter: '8', + verse: '31', + title: '', + text: 'If God is for us, who can be against us?' + }, + { + bookname: passage.split(' ')[0] ?? 'Romans', + chapter: '8', + verse: '32', + title: '', + text: 'This fixture response is served by Cosmos MSW.' + } + ]); + }), + + http.get(`${apiViews}/all_events_api`, () => HttpResponse.json(calendarEvents)), + http.get('https://cornerstoneapi.newfrontdoor.org/api/all-people', () => + HttpResponse.json(people) + ), + http.get(`${apiViews}/all_sermons_api`, () => HttpResponse.json([sermon])), + http.get(`${apiViews}/current_series_api`, () => HttpResponse.json([series])), + http.get(`${apiViews}/recent_series_api`, () => + HttpResponse.json([series, {...series, node_title: 'Previous Fixture Series'}]) + ), + http.get(`${apiViews}/all_sermon_series_api`, () => HttpResponse.json([series])), + + http.post('http://localhost:3000/api/sermon-upload', async ({request}) => { + const body = await request.json().catch(() => ({name: 'cosmos-upload.mp3'})); + const key = body.name ?? 'cosmos-upload.mp3'; + + return HttpResponse.json({ + url: 'https://sermons.crossroadshobart.org/', + fields: { + key, + Policy: 'cosmos-policy', + 'X-Amz-Signature': 'cosmos-signature' + } + }); + }), + http.post('https://sermons.crossroadshobart.org/', () => new HttpResponse(null, {status: 204})), + http.head('https://sermons.crossroadshobart.org/*', () => new HttpResponse(null, {status: 200})) +]; diff --git a/cosmos/vite.config.ts b/cosmos/vite.config.ts new file mode 100644 index 00000000..6ba933b3 --- /dev/null +++ b/cosmos/vite.config.ts @@ -0,0 +1,23 @@ +import {defineConfig} from 'vite'; +import react from '@vitejs/plugin-react'; +import {fileURLToPath} from 'node:url'; + +const rootUrl = new URL('../', import.meta.url); + +export default defineConfig({ + plugins: [ + react({ + jsxImportSource: 'theme-ui' + }) + ], + resolve: { + alias: { + '@newfrontdoor/api-config': fileURLToPath( + new URL('packages/api-config/src/index.js', rootUrl) + ), + '@newfrontdoor/audio-player': fileURLToPath( + new URL('packages/audio-player/src/index.ts', rootUrl) + ) + } + } +}); diff --git a/cosmos/webpack.override.js b/cosmos/webpack.override.js deleted file mode 100644 index f463a9ba..00000000 --- a/cosmos/webpack.override.js +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = (webpackConfig) => { - return { - ...webpackConfig, - module: { - ...webpackConfig.module, - rules: [ - ...webpackConfig.module.rules, - { - test: /\.(png|jpe?g|gif)$/i, - use: ['file-loader'] - } - ] - } - }; -}; diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..fb9e38e5 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,127 @@ +import js from "@eslint/js"; +import { defineConfig, globalIgnores } from "eslint/config"; +import eslintConfigPrettier from "eslint-config-prettier"; +import jestDom from "eslint-plugin-jest-dom"; +import react from "eslint-plugin-react"; +import reactHooks from "eslint-plugin-react-hooks"; +import testingLibrary from "eslint-plugin-testing-library"; +import globals from "globals"; +import tseslint from "typescript-eslint"; + +const sourceFiles = ["**/*.{js,jsx,ts,tsx}"]; + +const testFiles = [ + "**/*.{test,spec}.{js,jsx,ts,tsx}", + "**/__tests__/**/*.{js,jsx,ts,tsx}" +]; + +export default defineConfig( + globalIgnores([ + "**/dist/**", + "**/coverage/**", + "**/node_modules/**", + "**/.pnpm/**", + "**/.turbo/**", + "**/.vite/**", + "**/cosmos-export/**", + "cosmos.imports.ts", + "cosmos/**", + "**/__fixtures__/**", + "**/__tests__/**", + "vitest.config.ts", + "vitest.setup.ts" + ]), + + { + files: sourceFiles, + extends: [js.configs.recommended], + languageOptions: { + globals: { + ...globals.browser, + ...globals.es2024 + } + } + }, + + { + files: ["browserlist.config.js", "__mocks__/**/*.js"], + languageOptions: { + globals: { + ...globals.node, + ...globals.commonjs + } + } + }, + + { + files: ["**/*.{ts,tsx}"], + extends: [...tseslint.configs.recommendedTypeChecked], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname + } + }, + rules: { + "@typescript-eslint/consistent-type-imports": [ + "error", + { prefer: "type-imports" } + ] + } + }, + + { + files: ["**/*.{js,jsx}"], + extends: [tseslint.configs.disableTypeChecked] + }, + + { + files: sourceFiles, + extends: [ + react.configs.flat.recommended, + react.configs.flat["jsx-runtime"] + ], + settings: { + react: { + version: "detect" + } + }, + plugins: { + "react-hooks": reactHooks + }, + rules: { + ...reactHooks.configs.recommended.rules, + + "react/prop-types": "off", + "react/require-default-props": "off", + "react/default-props-match-prop-types": "off", + "react/no-unknown-property": [ + "error", + { + ignore: ["sx"] + } + ], + "react/jsx-pascal-case": "off", + "react/boolean-prop-naming": "off", + "react/jsx-no-useless-fragment": "off", + "react/jsx-fragments": ["error", "syntax"] + } + }, + + { + files: testFiles, + languageOptions: { + globals: { + ...globals.browser, + ...globals.es2024, + ...globals.jest + } + }, + extends: [ + testingLibrary.configs["flat/react"], + jestDom.configs["flat/recommended"] + ] + }, + + eslintConfigPrettier +); diff --git a/index.html b/index.html new file mode 100644 index 00000000..3bfdc5c9 --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + + + React Cosmos Vite Renderer + + +
+ + + diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index b4a9cda0..00000000 --- a/jest.config.js +++ /dev/null @@ -1,197 +0,0 @@ -// For a detailed explanation regarding each configuration property, visit: -// https://jestjs.io/docs/en/configuration.html - -module.exports = { - // All imported modules in your tests should be mocked automatically - // automock: false, - - // Stop running tests after `n` failures - // bail: 0, - - // Respect "browser" field in package.json when resolving modules - // browser: false, - - // The directory where Jest should store its cached dependency information - // cacheDirectory: "/private/var/folders/3n/fwyxm2nn7sl52wt5p0gx5phr0000gn/T/jest_dx", - - // Automatically clear mock calls and instances between every test - clearMocks: true, - - // Indicates whether the coverage information should be collected while executing the test - // collectCoverage: false, - - // An array of glob patterns indicating a set of files for which coverage information should be collected - // collectCoverageFrom: undefined, - - // The directory where Jest should output its coverage files - coverageDirectory: 'coverage', - - // An array of regexp pattern strings used to skip coverage collection - // coveragePathIgnorePatterns: [ - // "/node_modules/" - // ], - - // A list of reporter names that Jest uses when writing coverage reports - // coverageReporters: [ - // "json", - // "text", - // "lcov", - // "clover" - // ], - - // An object that configures minimum threshold enforcement for coverage results - // coverageThreshold: undefined, - - // A path to a custom dependency extractor - // dependencyExtractor: undefined, - - // Make calling deprecated APIs throw helpful error messages - // errorOnDeprecated: false, - - // Force coverage collection from ignored files using an array of glob patterns - // forceCoverageMatch: [], - - // A path to a module which exports an async function that is triggered once before all test suites - // globalSetup: undefined, - - // A path to a module which exports an async function that is triggered once after all test suites - // globalTeardown: undefined, - - // A set of global variables that need to be available in all test environments - globals: { - 'ts-jest': { - tsconfig: { - target: 'es2018', - module: 'commonjs', - esModuleInterop: true, - resolveJsonModule: true, - jsx: 'react', - alwaysStrict: true, - noImplicitThis: true, - noImplicitAny: false, - strictBindCallApply: false - } - } - }, - - // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. - // maxWorkers: "50%", - - // An array of directory names to be searched recursively up from the requiring module's location - moduleDirectories: ['node_modules'], - - // An array of file extensions your modules use - // moduleFileExtensions: [ - // "js", - // "json", - // "jsx", - // "ts", - // "tsx", - // "node" - // ], - - // A map from regular expressions to module names that allow to stub out resources with a single module - moduleNameMapper: { - '.*\\.css$': '/__mocks__/style-mock.js' - }, - - // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader - // modulePathIgnorePatterns: [], - - // Activates notifications for test results - // notify: false, - - // An enum that specifies notification mode. Requires { notify: true } - // notifyMode: "failure-change", - - // A preset that is used as a base for Jest's configuration - preset: 'ts-jest/presets/js-with-babel', - - // Run tests from one or more projects - // projects: [], - - // Use this configuration option to add custom reporters to Jest - // reporters: undefined, - - // Automatically reset mock state between every test - // resetMocks: false, - - // Reset the module registry before running each individual test - // resetModules: false, - - // A path to a custom resolver - // resolver: undefined, - - // Automatically restore mock state between every test - // restoreMocks: false, - - // The root directory that Jest should scan for tests and modules within - // rootDir: undefined, - - // A list of paths to directories that Jest should use to search for files in - // roots: [ - // "" - // ], - - // Allows you to use a custom runner instead of Jest's default test runner - // runner: "jest-runner", - - // The paths to modules that run some code to configure or set up the testing environment before each test - // setupFiles: [], - - // A list of paths to modules that run some code to configure or set up the testing framework before each test - setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'], - - // A list of paths to snapshot serializer modules Jest should use for snapshot testing - snapshotSerializers: ['jest-emotion'], - - // The test environment that will be used for testing - // testEnvironment: "jest-environment-jsdom", - - // Options that will be passed to the testEnvironment - // testEnvironmentOptions: {}, - - // Adds a location field to test results - // testLocationInResults: false, - - // The glob patterns Jest uses to detect test files - testMatch: ['packages/**/__tests__/*.{js,ts,tsx}'], - - // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - testPathIgnorePatterns: ['/node_modules/', '/dist/'] - - // The regexp pattern or array of patterns that Jest uses to detect test files - // testRegex: [], - - // This option allows the use of a custom results processor - // testResultsProcessor: undefined, - - // This option allows use of a custom test runner - // testRunner: "jasmine2", - - // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href - // testURL: "http://localhost", - - // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" - // timers: "real", - - // A map from regular expressions to paths to transformers - // transform: undefined, - - // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation - // transformIgnorePatterns: [ - // "/node_modules/" - // ], - - // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them - // unmockedModulePathPatterns: undefined, - - // Indicates whether each individual test should be reported during the run - // verbose: undefined, - - // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode - // watchPathIgnorePatterns: [], - - // Whether to use watchman for file crawling - // watchman: true, -}; diff --git a/package.json b/package.json index 97399dba..f5ba7683 100644 --- a/package.json +++ b/package.json @@ -1,102 +1,60 @@ { "private": true, "scripts": { - "release": "changeset publish", + "release": "pnpm run build && changeset publish", "cosmos": "cosmos --config cosmos/cosmos.config.json", "cosmos:export": "cosmos-export --config cosmos/cosmos.config.json", - "lint": "xo", - "prepare": "del \"./packages/*/dist\" && yarn workspaces run prepare", - "test": "jest" + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "clean": "del \"./packages/*/dist\"", + "build": "pnpm run clean && pnpm -r --filter \"./packages/**\" run build", + "test": "vitest run", + "typecheck": "tsc --noEmit" }, "devDependencies": { - "@babel/cli": "^7.1.2", - "@babel/core": "^7.9.0", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-transform-runtime": "^7.12.1", - "@babel/preset-env": "^7.12.11", - "@babel/preset-react": "^7.9.4", - "@babel/preset-typescript": "^7.9.0", - "@changesets/cli": "^2.13.1", - "@emotion/core": "^10.1.1", - "@emotion/is-prop-valid": "^0.8.3", - "@emotion/styled": "^10.0.10", - "@mdx-js/react": "^1.6.6", + "@changesets/cli": "^2.31.0", + "@emotion/jest": "^11.14.2", + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@eslint/js": "^9.39.4", + "@mdx-js/react": "^3.1.1", "@styled-system/css": "^5.1.5", - "@testing-library/jest-dom": "^5.11.9", - "@testing-library/react": "^11.2.3", - "@types/faker": "^5.1.4", - "@types/jest": "^26.0.20", - "@types/lodash": "^4.14.168", - "@types/prop-types": "^15.7.3", - "@types/react": "^16.9.27", - "@types/theme-ui": "^0.3.7", - "babel-jest": "^26.0.1", - "babel-loader": "^8.0.6", - "babel-plugin-inline-react-svg": "^1.1.0", - "codecov": "^3.5.0", - "css-loader": "^5.0.1", - "del-cli": "^3.0.1", - "eslint": "^7.18.0", - "eslint-config-xo-react": "^0.23.0", - "eslint-plugin-jest": "^24.1.3", - "eslint-plugin-react": "^7.22.0", - "eslint-plugin-react-hooks": "^4.0.4", - "eslint-plugin-testing-library": "^3.10.1", - "file-loader": "^6.0.0", - "html-webpack-plugin": "^4.5.1", - "jest": "^26.0.1", - "jest-emotion": "^10.0.32", - "microbundle": "^0.13.0", - "prettier": "^2.2.1", - "prop-types": "^15.6.1", - "react": "^16.8.6", - "react-cosmos": "^5.0.11", - "react-dom": "^16.8.6", - "react-router": "^5.1.2", - "react-router-dom": "^5.1.2", - "style-loader": "^2.0.0", - "theme-ui": "^0.3.5", - "ts-jest": "^26.1.1", - "ts-loader": "^8.0.14", + "@tanstack/react-query": "^5.100.9", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.2.0", + "@vitest/eslint-plugin": "^1.6.16", + "eslint": "^9.39.4", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-jest-dom": "^5.5.0", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-testing-library": "^7.16.2", + "globals": "^17.6.0", + "jsdom": "^27.4.0", + "msw": "^2.14.3", + "prettier": "^3.8.3", + "react": "^19.2.6", + "react-cosmos": "^7.3.0", + "react-cosmos-plugin-vite": "^7.3.0", + "react-dom": "^19.2.6", + "theme-ui": "^0.17.4", + "tsdown": "^0.22.0", "type-fest": "^0.20.2", - "typescript": "^4.1.2", - "webpack": "^4.7.0", - "xo": "^0.37.1" - }, - "eslint": { - "settings": { - "react": { - "version": "detect" - } - } + "typescript": "^6.0.3", + "typescript-eslint": "^8.59.2", + "vite": "^8.0.11", + "vitest": "^4.1.5" }, "workspaces": [ "packages/*" ], - "xo": { - "extends": [ - "xo-react", - "plugin:jest/recommended", - "plugin:testing-library/react" - ], - "prettier": true, - "space": true, - "plugins": [ - "jest", - "testing-library" - ], - "env": [ - "browser" - ], - "rules": { - "react/jsx-pascal-case": "off", - "react/boolean-prop-naming": "off", - "react/jsx-no-useless-fragment": "off", - "react/jsx-fragments": [ - "error", - "element" - ], - "@typescript-eslint/no-implicit-any-catch": "off" - } + "packageManager": "pnpm@10.33.0", + "msw": { + "workerDirectory": [ + "public" + ] } } diff --git a/packages/about/__fixtures__/about.js b/packages/about/__fixtures__/about.jsx similarity index 99% rename from packages/about/__fixtures__/about.js rename to packages/about/__fixtures__/about.jsx index 5da2bf8b..ce257857 100644 --- a/packages/about/__fixtures__/about.js +++ b/packages/about/__fixtures__/about.jsx @@ -1,4 +1,4 @@ -import React from 'react'; + import About from '../src'; const people = [ diff --git a/packages/about/babel.config.js b/packages/about/babel.config.js deleted file mode 100644 index 4c05afbe..00000000 --- a/packages/about/babel.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../babel.config'); diff --git a/packages/about/package.json b/packages/about/package.json index 312aa929..0a5920ba 100644 --- a/packages/about/package.json +++ b/packages/about/package.json @@ -3,21 +3,26 @@ "version": "0.0.0", "license": "MIT", "source": "src/index.js", - "main": "dist/index.js", - "module": "dist/index.module.js", + "main": "dist/index.cjs", + "module": "dist/index.mjs", "scripts": { - "prepare": "microbundle --no-compress", - "watch": "microbundle watch --no-compress" + "build": "tsdown src/index.js --format esm --format cjs --deps.skip-node-modules-bundle --clean", + "watch": "tsdown src/index.js --format esm --format cjs --deps.skip-node-modules-bundle --watch" }, "files": [ "dist" ], "peerDependencies": { - "react": "^16.0" + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "theme-ui": "^0.17.4" }, - "dependencies": { - "prop-types": "^15.6.0", - "react-portal": "^4.1.5" - }, - "xo": false + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" + } + } } diff --git a/packages/about/src/about.js b/packages/about/src/about.js deleted file mode 100644 index 3f08e876..00000000 --- a/packages/about/src/about.js +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import {PortalWithState} from 'react-portal'; -import ImageView from './image-view'; -import Modal from './modal'; -import PersonModal from './person-modal'; - -const About = (props) => { - return ( -
- {props.people.map((person) => ( - - {({openPortal, closePortal, portal}) => [ - , - portal( - - - - ) - ]} - - ))} -
- ); -}; - -About.propTypes = { - people: PropTypes.arrayOf( - PropTypes.shape({ - name: PropTypes.string.isRequired, - image: PropTypes.string.isRequired, - body: PropTypes.string.isRequired, - email: PropTypes.string, - phone: PropTypes.string - }) - ).isRequired -}; - -export default About; diff --git a/packages/about/src/about.jsx b/packages/about/src/about.jsx new file mode 100644 index 00000000..02bcaefc --- /dev/null +++ b/packages/about/src/about.jsx @@ -0,0 +1,71 @@ +import {useEffect, useState} from 'react'; +import {createPortal} from 'react-dom'; +import ImageView from './image-view'; +import Modal from './modal'; +import PersonModal from './person-modal'; + +const PersonPortal = ({person}) => { + const [isOpen, setIsOpen] = useState(false); + + useEffect(() => { + if (!isOpen) { + return undefined; + } + + const closeOnEscape = (event) => { + if (event.key === 'Escape') { + setIsOpen(false); + } + }; + + document.addEventListener('keydown', closeOnEscape); + return () => document.removeEventListener('keydown', closeOnEscape); + }, [isOpen]); + + const modal = + isOpen && typeof document !== 'undefined' + ? createPortal( + setIsOpen(false)}> + + , + document.body + ) + : null; + + return ( + <> + setIsOpen(true)} + /> + {modal} + + ); +}; + +const About = (props) => { + return ( +
+ {props.people.map((person) => ( + + ))} +
+ ); +}; + +/* +About.propTypes = { + people: PropTypes.arrayOf( + PropTypes.shape({ + name: PropTypes.string.isRequired, + image: PropTypes.string.isRequired, + body: PropTypes.string.isRequired, + email: PropTypes.string, + phone: PropTypes.string + }) + ).isRequired +}; +*/ + +export default About; diff --git a/packages/about/src/close-svg.js b/packages/about/src/close-svg.jsx similarity index 91% rename from packages/about/src/close-svg.js rename to packages/about/src/close-svg.jsx index 3034434d..16e7810f 100644 --- a/packages/about/src/close-svg.js +++ b/packages/about/src/close-svg.jsx @@ -1,5 +1,4 @@ -/** @jsx jsx */ -import {jsx, css} from 'theme-ui'; +import {css} from 'theme-ui'; const closeIcon = css` position: absolute; diff --git a/packages/about/src/image-view.js b/packages/about/src/image-view.jsx similarity index 69% rename from packages/about/src/image-view.js rename to packages/about/src/image-view.jsx index 4cf93373..9a37911a 100644 --- a/packages/about/src/image-view.js +++ b/packages/about/src/image-view.jsx @@ -1,5 +1,4 @@ -import React from 'react'; -import PropTypes from 'prop-types'; + const ImageView = (props) => (
@@ -8,9 +7,9 @@ const ImageView = (props) => (
); -ImageView.propTypes = { +/* ImageView.propTypes = { name: PropTypes.string.isRequired, image: PropTypes.string.isRequired -}; +}; */ export default ImageView; diff --git a/packages/about/src/modal.js b/packages/about/src/modal.jsx similarity index 95% rename from packages/about/src/modal.js rename to packages/about/src/modal.jsx index bc15a858..f5142794 100644 --- a/packages/about/src/modal.js +++ b/packages/about/src/modal.jsx @@ -1,5 +1,4 @@ -/** @jsx jsx */ -import {jsx, css} from 'theme-ui'; +import {css} from 'theme-ui'; import CloseSvg from './close-svg'; const modalClass = css` diff --git a/packages/about/src/person-modal.js b/packages/about/src/person-modal.jsx similarity index 53% rename from packages/about/src/person-modal.js rename to packages/about/src/person-modal.jsx index a972572f..32902692 100644 --- a/packages/about/src/person-modal.js +++ b/packages/about/src/person-modal.jsx @@ -1,18 +1,17 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -const PersonModal = (props) => ( + +const PersonModal = ({name, image, body, email = '', phone = ''}) => (
-

{props.name}

- -

{props.body}

+

{name}

+ +

{body}

Contact

-

{props.email}

-

{props.phone}

+

{email}

+

{phone}

); -PersonModal.propTypes = { +/* PersonModal.propTypes = { name: PropTypes.string.isRequired, image: PropTypes.string.isRequired, body: PropTypes.string.isRequired, @@ -23,6 +22,6 @@ PersonModal.propTypes = { PersonModal.defaultProps = { email: '', phone: '' -}; +}; */ export default PersonModal; diff --git a/packages/api-config/babel.config.js b/packages/api-config/babel.config.js deleted file mode 100644 index 4c05afbe..00000000 --- a/packages/api-config/babel.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../babel.config'); diff --git a/packages/api-config/package.json b/packages/api-config/package.json index 9a686ec2..ac80a2f4 100644 --- a/packages/api-config/package.json +++ b/packages/api-config/package.json @@ -2,15 +2,23 @@ "name": "@newfrontdoor/api-config", "version": "0.1.0", "private": false, - "main": "dist/index.js", + "main": "dist/index.cjs", "scripts": { - "prepare": "babel src -d dist" + "build": "tsdown src/index.js --format esm --format cjs --deps.skip-node-modules-bundle --clean", + "watch": "tsdown src/index.js --format esm --format cjs --deps.skip-node-modules-bundle --watch" }, "files": [ "dist" ], "peerDependencies": { - "react": "^16.0" + "react": "^19.2.0" }, - "xo": false + + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" + } + }, + "module": "dist/index.mjs" } diff --git a/packages/api-config/src/drupal-api-config-provider.js b/packages/api-config/src/drupal-api-config-provider.js index a206f438..86889494 100644 --- a/packages/api-config/src/drupal-api-config-provider.js +++ b/packages/api-config/src/drupal-api-config-provider.js @@ -1,6 +1,6 @@ -import React, {useContext} from 'react'; +import {createContext, useContext} from 'react'; -export const ApiContext = React.createContext({ +export const ApiContext = createContext({ baseUrl: 'https://cornerstoneapi.newfrontdoor.org/api/views/' }); diff --git a/packages/audio-player/__fixtures__/audio-manager.tsx b/packages/audio-player/__fixtures__/audio-manager.tsx index 01b5641b..d32d9eb0 100644 --- a/packages/audio-player/__fixtures__/audio-manager.tsx +++ b/packages/audio-player/__fixtures__/audio-manager.tsx @@ -1,4 +1,4 @@ -import React, {FC, Fragment} from 'react'; +import type {FC} from 'react'; import {useThemeUI} from 'theme-ui'; import {AudioManager, PlayButton, AudioPlayer} from '../src/audio-manager'; @@ -16,7 +16,7 @@ const AudioManagerFixture: FC = () => { const {theme} = useThemeUI(); return ( - + <> { Track 2 - + ); }; diff --git a/packages/audio-player/__fixtures__/audio-player.tsx b/packages/audio-player/__fixtures__/audio-player.tsx index d808ac65..5b56d5e2 100644 --- a/packages/audio-player/__fixtures__/audio-player.tsx +++ b/packages/audio-player/__fixtures__/audio-player.tsx @@ -1,6 +1,6 @@ -/** @jsx jsx */ -import {jsx, Text} from 'theme-ui'; -import {FC, Fragment} from 'react'; +import {Text} from 'theme-ui'; +import type {FC} from 'react'; +import { Fragment} from 'react'; import AudioPlayer from '../src/audio-player'; const props = { diff --git a/packages/audio-player/__fixtures__/native-player.tsx b/packages/audio-player/__fixtures__/native-player.tsx index 3a98efef..fd3599d8 100644 --- a/packages/audio-player/__fixtures__/native-player.tsx +++ b/packages/audio-player/__fixtures__/native-player.tsx @@ -1,4 +1,4 @@ -import React, {FC, Fragment} from 'react'; +import type {FC} from 'react'; import {AudioManager, NativePlayer, PlayButton} from '../src/audio-manager'; const track1 = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3'; @@ -6,7 +6,7 @@ const track2 = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3'; const StyledPlayerFixture: FC = () => { return ( - + <> Track 1 @@ -14,7 +14,7 @@ const StyledPlayerFixture: FC = () => { Track 2 - + ); }; diff --git a/packages/audio-player/__tests__/audio-player.test.tsx b/packages/audio-player/__tests__/audio-player.test.tsx index 661f46e9..a2398cfb 100644 --- a/packages/audio-player/__tests__/audio-player.test.tsx +++ b/packages/audio-player/__tests__/audio-player.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; + import {render, screen} from '@testing-library/react'; import AudioPlayer from '../src/audio-player'; diff --git a/packages/audio-player/babel.config.js b/packages/audio-player/babel.config.js deleted file mode 100644 index 4c05afbe..00000000 --- a/packages/audio-player/babel.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../babel.config'); diff --git a/packages/audio-player/package.json b/packages/audio-player/package.json index 0bf560a2..d2471103 100644 --- a/packages/audio-player/package.json +++ b/packages/audio-player/package.json @@ -4,13 +4,12 @@ "description": "An audio element wrapper written in React with hooks", "license": "MIT", "author": "Alan Reader", - "main": "dist/index.js", - "module": "dist/index.module.js", + "main": "dist/index.cjs", + "module": "dist/index.mjs", "source": "index.ts", - "types": "dist/index.d.ts", "scripts": { - "prepare": "microbundle --no-compress", - "watch": "microbundle watch --no-compress" + "build": "tsdown src/index.ts --format esm --format cjs --deps.skip-node-modules-bundle --clean", + "watch": "tsdown src/index.ts --format esm --format cjs --deps.skip-node-modules-bundle --watch" }, "files": [ "dist" @@ -22,16 +21,20 @@ "hooks" ], "peerDependencies": { - "react": "^16.0", - "theme-ui": "^0.3.1" + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "react": "^19.2.0", + "theme-ui": "^0.17.4" }, "dependencies": { - "prop-types": "^15.7.2", - "react-icons": "^3.7.0", + "react-icons": "^5.6.0", "react-range": "^1.8.6" }, - "devDependencies": { - "@testing-library/react": "^11.2.3" - }, - "xo": false + + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" + } + } } diff --git a/packages/audio-player/src/audio-manager.tsx b/packages/audio-player/src/audio-manager.tsx index ee4efdb3..d50085fe 100644 --- a/packages/audio-player/src/audio-manager.tsx +++ b/packages/audio-player/src/audio-manager.tsx @@ -1,7 +1,6 @@ -/** @jsx jsx */ -import {jsx, Button, ButtonProps} from 'theme-ui'; -import PropTypes from 'prop-types'; -import {FC, HTMLProps, ReactNode} from 'react'; +import type { ButtonProps} from 'theme-ui'; +import {Button} from 'theme-ui'; +import type {FC, HTMLProps, ReactNode} from 'react'; import {MdPlayArrow as Play, MdPause as Pause} from 'react-icons/md'; import useAudioManager, { useAudioPlayer, @@ -10,7 +9,8 @@ import useAudioManager, { AudioStateContext, AudioDispatchContext } from './use-audio-manager'; -import StyledPlayer, {StyledPlayerProps} from './styled-player'; +import type {StyledPlayerProps} from './styled-player'; +import StyledPlayer from './styled-player'; type AudioManagerProps = { initialSrc?: string; @@ -34,11 +34,11 @@ export const AudioManager: FC = ({ ); }; -AudioManager.propTypes = { +/* AudioManager.propTypes = { initialSrc: PropTypes.string, isPlayOnLoad: PropTypes.bool, children: PropTypes.node.isRequired -}; +}; */ type PlayButtonProps = ButtonProps & { src: string; @@ -71,11 +71,11 @@ export const AudioPlayer: FC = (props) => { return ; }; -PlayButton.propTypes = { +/* PlayButton.propTypes = { src: PropTypes.string.isRequired, variant: PropTypes.string, children: PropTypes.node -}; +}; */ type NativePlayerProps = HTMLProps; diff --git a/packages/audio-player/src/audio-player.tsx b/packages/audio-player/src/audio-player.tsx index f9d1bd61..8978793b 100644 --- a/packages/audio-player/src/audio-player.tsx +++ b/packages/audio-player/src/audio-player.tsx @@ -1,17 +1,20 @@ -/** @jsx jsx */ -import {jsx} from 'theme-ui'; -import {FC} from 'react'; -import PropTypes from 'prop-types'; + +import type {FC} from 'react'; import {AudioManager} from './audio-manager'; -import StyledPlayer, {StyledPlayerProps} from './styled-player'; +import type {StyledPlayerProps} from './styled-player'; +import StyledPlayer from './styled-player'; type AudioPlayerProps = { src?: string; isPlayOnLoad?: boolean; } & StyledPlayerProps; -const AudioPlayer: FC = ({src, isPlayOnLoad, ...props}) => { +const AudioPlayer: FC = ({ + src, + isPlayOnLoad = false, + ...props +}) => { return ( @@ -19,13 +22,13 @@ const AudioPlayer: FC = ({src, isPlayOnLoad, ...props}) => { ); }; -AudioPlayer.defaultProps = { +/* AudioPlayer.defaultProps = { isPlayOnLoad: false }; AudioPlayer.propTypes = { src: PropTypes.string, isPlayOnLoad: PropTypes.bool -}; +}; */ export default AudioPlayer; diff --git a/packages/audio-player/src/index.ts b/packages/audio-player/src/index.ts new file mode 100644 index 00000000..709c4d44 --- /dev/null +++ b/packages/audio-player/src/index.ts @@ -0,0 +1,5 @@ +export {default as AudioPlayer} from './audio-player'; +export {AudioManager, PlayButton} from './audio-manager'; +export {default as StyledPlayer} from './styled-player'; +export {default as ProgressBar} from './progress-bar'; +export {default as useAudioManager, useAudioPlayer} from './use-audio-manager'; diff --git a/packages/audio-player/src/progress-bar.tsx b/packages/audio-player/src/progress-bar.tsx index e6e9683e..a9ff3cd0 100644 --- a/packages/audio-player/src/progress-bar.tsx +++ b/packages/audio-player/src/progress-bar.tsx @@ -1,9 +1,16 @@ -/** @jsx jsx */ -import {jsx} from 'theme-ui'; +import type {ComponentProps, ForwardedRef, ReactNode} from 'react'; import {forwardRef} from 'react'; -import PropTypes from 'prop-types'; import {Range, getTrackBackground} from 'react-range'; +type RangeProps = ComponentProps; +type RangeAdapterProps = Partial & + Pick & { + ref?: ForwardedRef; +}; +const AnyRange = Range as unknown as (props: RangeAdapterProps) => ReactNode; +type TrackRenderProps = Parameters>[0]; +type ThumbRenderProps = Parameters>[0]; + type ProgressBarProps = { value: number; max: number; @@ -15,9 +22,12 @@ type ProgressBarProps = { }; const ProgressBar = forwardRef( - ({value, max, onChange, step, isInteracting, color, isInvert}, rangeRef) => { + ( + {value, max, onChange, step = 1, isInteracting, color, isInvert = false}, + rangeRef + ) => { return ( - ( renderTrack={({ props: {style, onMouseDown, onTouchStart, ref}, children - }) => ( + }: TrackRenderProps) => (
(
)} - renderThumb={({props: {style, ...props}}) => ( + renderThumb={({props: {style, ...props}}: ThumbRenderProps) => (
( />
)} - onChange={(values) => onChange(values[0])} + onChange={(values: number[]) => onChange(values[0])} /> ); } ); +ProgressBar.displayName = 'ProgressBar'; + export default ProgressBar; -ProgressBar.defaultProps = { +/* ProgressBar.defaultProps = { step: 1, isInvert: false }; @@ -107,4 +119,4 @@ ProgressBar.propTypes = { isInteracting: PropTypes.bool.isRequired, color: PropTypes.string.isRequired, isInvert: PropTypes.bool -}; +}; */ diff --git a/packages/audio-player/src/styled-player.tsx b/packages/audio-player/src/styled-player.tsx index 9a0d620e..62dcd3a5 100644 --- a/packages/audio-player/src/styled-player.tsx +++ b/packages/audio-player/src/styled-player.tsx @@ -1,14 +1,13 @@ -/** @jsx jsx */ -import {jsx} from 'theme-ui'; -import {FC, HTMLProps, useRef} from 'react'; -import PropTypes from 'prop-types'; + +import type {FC, HTMLProps} from 'react'; +import { useRef} from 'react'; import { MdPlayArrow as Play, MdPause as Pause, MdVolumeUp, MdVolumeOff } from 'react-icons/md'; -import {Range} from 'react-range'; +import type {Range} from 'react-range'; import ProgressBar from './progress-bar'; import {useAudioPlayer} from './use-audio-manager'; @@ -16,7 +15,7 @@ type ButtonProps = HTMLProps & { background?: string; }; -const Button: FC = ({background, ...props}) => { +const Button: FC = ({background = '#ddd', ...props}) => { return (
+ {event.url && ( + + )} + + + , + document.body + ) + : null; return ( - + <> {event.name && (
= ({event, handleNav}) => { onClick={() => setShowDialog(true)} /> )} - setShowDialog(false)} - > - {event.name && ( -
-

- {/* eslint-disable-next-line react/no-danger */} - -

-
- )} -

- {event.startTime} - {event.endTime} -

-

- Location - {event.location} -

- {event.description && ( - /* eslint-disable-next-line react/no-danger */ -
- )} -
- - {event.url && ( - - )} -
-
- + {dialog} + ); }; -EventWrapper.propTypes = { +/* EventWrapper.propTypes = { event: PropTypes.shape({ name: PropTypes.string.isRequired, startTime: PropTypes.string.isRequired, @@ -95,6 +121,6 @@ EventWrapper.propTypes = { location: PropTypes.string.isRequired }).isRequired, handleNav: PropTypes.func -}; +}; */ export default EventWrapper; diff --git a/packages/calendar/src/components/method-toggle.tsx b/packages/calendar/src/components/method-toggle.tsx index ff3c0e57..25be7ad9 100644 --- a/packages/calendar/src/components/method-toggle.tsx +++ b/packages/calendar/src/components/method-toggle.tsx @@ -1,9 +1,8 @@ -/** @jsx jsx */ -import {jsx, Label} from 'theme-ui'; -import {FC, Fragment} from 'react'; -import PropTypes from 'prop-types'; +import {Label} from 'theme-ui'; +import type {FC} from 'react'; +import { Fragment} from 'react'; import {hideVisually} from 'polished'; -import {calendarViews, CalendarView} from '../types'; +import type { CalendarView} from '../types'; import {useCalendarDispatch} from '../use-calendar-events'; type MethodToggleProps = { @@ -111,8 +110,8 @@ const MethodToggle: FC = (props) => { export default MethodToggle; -MethodToggle.propTypes = { +/* MethodToggle.propTypes = { location: PropTypes.string.isRequired, calendarView: PropTypes.oneOf(calendarViews).isRequired, inputs: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired -}; +}; */ diff --git a/packages/calendar/src/drupal-client.ts b/packages/calendar/src/drupal-client.ts index d34acec2..75cdef45 100644 --- a/packages/calendar/src/drupal-client.ts +++ b/packages/calendar/src/drupal-client.ts @@ -9,7 +9,7 @@ import { differenceInDays } from 'date-fns'; -import {CalendarEvent} from './types'; +import type {CalendarEvent} from './types'; type DrupalClient = { fetchEvents: (date: string) => Promise; diff --git a/packages/calendar/src/elvanto-client.ts b/packages/calendar/src/elvanto-client.ts index 211d6654..617ec2f3 100644 --- a/packages/calendar/src/elvanto-client.ts +++ b/packages/calendar/src/elvanto-client.ts @@ -10,7 +10,7 @@ import { differenceInDays } from 'date-fns'; -import {CalendarEvent} from './types'; +import type {CalendarEvent} from './types'; type ElvantoClient = { fetchEvents: (date: string) => Promise; diff --git a/packages/calendar/src/use-calendar-events.ts b/packages/calendar/src/use-calendar-events.ts index dc25ffc4..b74ac8a7 100644 --- a/packages/calendar/src/use-calendar-events.ts +++ b/packages/calendar/src/use-calendar-events.ts @@ -1,5 +1,7 @@ -import React, {useContext, useReducer, Dispatch} from 'react'; -import {useQuery, QueryStatus} from 'react-query'; +import type { Dispatch} from 'react'; +import {createContext, useContext, useReducer} from 'react'; +import type { QueryStatus} from '@tanstack/react-query'; +import {useQuery} from '@tanstack/react-query'; import { addMonths, subMonths, @@ -8,19 +10,18 @@ import { subDays, subWeeks } from 'date-fns/fp'; -import {update} from 'lodash/fp'; import {buildCalendarData} from './utilities/date-utils-grid'; -import { +import type { CalendarData, CalendarClient, CalendarView, CalendarState } from './types'; -export const CalendarDispatch = React.createContext>( - (_: Action) => undefined +export const CalendarDispatch = createContext>( + () => undefined ); export const useCalendarDispatch = (): Dispatch => @@ -45,14 +46,31 @@ type Action = | {type: 'decrement-jump'} | {type: 'increment-jump'}; -const previousYear = update('currentDate', subMonths(12)); -const previousMonth = update('currentDate', subMonths(1)); -const previousWeek = update('currentDate', subWeeks(1)); -const previousDay = update('currentDate', subDays(1)); -const nextDay = update('currentDate', addDays(1)); -const nextWeek = update('currentDate', addWeeks(1)); -const nextMonth = update('currentDate', addMonths(1)); -const nextYear = update('currentDate', addMonths(12)); +function updateCurrentDate( + state: CalendarState, + updateDate: (date: Date) => Date +): CalendarState { + return { + ...state, + currentDate: updateDate(state.currentDate) + }; +} + +const previousYear = (state: CalendarState) => + updateCurrentDate(state, subMonths(12)); +const previousMonth = (state: CalendarState) => + updateCurrentDate(state, subMonths(1)); +const previousWeek = (state: CalendarState) => + updateCurrentDate(state, subWeeks(1)); +const previousDay = (state: CalendarState) => + updateCurrentDate(state, subDays(1)); +const nextDay = (state: CalendarState) => updateCurrentDate(state, addDays(1)); +const nextWeek = (state: CalendarState) => + updateCurrentDate(state, addWeeks(1)); +const nextMonth = (state: CalendarState) => + updateCurrentDate(state, addMonths(1)); +const nextYear = (state: CalendarState) => + updateCurrentDate(state, addMonths(12)); function decrementStep(state: CalendarState): CalendarState { switch (state.calendarView) { @@ -175,13 +193,11 @@ export function useCalendarEvents( const currentISODate = currentDate.toISOString(); - const {data, status, error} = useQuery( - currentISODate, - async () => client.fetchEvents(currentISODate), - { - keepPreviousData: true - } - ); + const {data, status, error} = useQuery({ + queryKey: [currentISODate], + queryFn: async () => client.fetchEvents(currentISODate), + placeholderData: (previousData) => previousData + }); const calendarData = buildCalendarData( {calendarView, currentDate}, diff --git a/packages/calendar/src/utilities/date-utils-grid.tsx b/packages/calendar/src/utilities/date-utils-grid.tsx index bf67ba21..a62ad58f 100644 --- a/packages/calendar/src/utilities/date-utils-grid.tsx +++ b/packages/calendar/src/utilities/date-utils-grid.tsx @@ -17,7 +17,7 @@ import { startOfWeek } from 'date-fns'; -import { +import type { CalendarState, CalendarData, CalendarEvent, diff --git a/packages/carousel/__fixtures__/autoplay-off-hide-nav-dots.tsx b/packages/carousel/__fixtures__/autoplay-off-hide-nav-dots.tsx index c6385561..beef3eba 100644 --- a/packages/carousel/__fixtures__/autoplay-off-hide-nav-dots.tsx +++ b/packages/carousel/__fixtures__/autoplay-off-hide-nav-dots.tsx @@ -1,5 +1,4 @@ -/** @jsx jsx */ -import {jsx, css} from 'theme-ui'; +import {css} from 'theme-ui'; import {Carousel} from '../src'; const slide = css({ diff --git a/packages/carousel/__fixtures__/autoplay-off.tsx b/packages/carousel/__fixtures__/autoplay-off.tsx index e2c125ff..35e13e03 100644 --- a/packages/carousel/__fixtures__/autoplay-off.tsx +++ b/packages/carousel/__fixtures__/autoplay-off.tsx @@ -1,5 +1,4 @@ -/** @jsx jsx */ -import {jsx, css} from 'theme-ui'; +import {css} from 'theme-ui'; import {Carousel} from '../src'; const slide = css({ diff --git a/packages/carousel/__fixtures__/autoplay-on.tsx b/packages/carousel/__fixtures__/autoplay-on.tsx index 73c1e269..d252ac21 100644 --- a/packages/carousel/__fixtures__/autoplay-on.tsx +++ b/packages/carousel/__fixtures__/autoplay-on.tsx @@ -1,5 +1,4 @@ -/** @jsx jsx */ -import {jsx, css} from 'theme-ui'; +import {css} from 'theme-ui'; import {Carousel} from '../src'; const slide = css({ diff --git a/packages/carousel/__fixtures__/custom-dots.tsx b/packages/carousel/__fixtures__/custom-dots.tsx index 735358b9..9159b4bd 100644 --- a/packages/carousel/__fixtures__/custom-dots.tsx +++ b/packages/carousel/__fixtures__/custom-dots.tsx @@ -1,5 +1,4 @@ -/** @jsx jsx */ -import {jsx, css} from 'theme-ui'; +import {css} from 'theme-ui'; import {Carousel} from '../src'; const slide = css({ diff --git a/packages/carousel/babel.config.js b/packages/carousel/babel.config.js deleted file mode 100644 index 4c05afbe..00000000 --- a/packages/carousel/babel.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../babel.config'); diff --git a/packages/carousel/package.json b/packages/carousel/package.json index e694c9b1..8695accf 100644 --- a/packages/carousel/package.json +++ b/packages/carousel/package.json @@ -2,27 +2,30 @@ "name": "@newfrontdoor/carousel", "version": "3.2.0", "license": "MIT", - "main": "dist/index.js", - "module": "dist/index.module.js", + "main": "dist/index.cjs", + "module": "dist/index.mjs", "source": "src/index.ts", - "types": "dist/index.d.ts", "scripts": { - "prepare": "microbundle --no-compress", - "watch": "microbundle watch --no-compress" + "build": "tsdown src/index.ts --format esm --format cjs --deps.skip-node-modules-bundle --clean", + "watch": "tsdown src/index.ts --format esm --format cjs --deps.skip-node-modules-bundle --watch" }, "files": [ "dist" ], "peerDependencies": { - "react": "^16.8.6", - "theme-ui": "^0.3.1" + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "react": "^19.2.0", + "theme-ui": "^0.17.4" }, "dependencies": { - "embla-carousel": "^4.2.0", - "prop-types": "^15.7.2" + "embla-carousel": "^4.2.0" }, - "devDependencies": { - "@testing-library/react": "^11.2.3" - }, - "xo": false + + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" + } + } } diff --git a/packages/carousel/src/carousel-buttons.tsx b/packages/carousel/src/carousel-buttons.tsx index 09508f5d..7d53bfe0 100644 --- a/packages/carousel/src/carousel-buttons.tsx +++ b/packages/carousel/src/carousel-buttons.tsx @@ -1,6 +1,5 @@ -/** @jsx jsx */ -import {jsx} from 'theme-ui'; -import {HTMLProps} from 'react'; + +import type {HTMLProps} from 'react'; const Next = (props: HTMLProps) => (
)} @@ -171,10 +167,10 @@ const Carousel = ({ ); }; -Carousel.propTypes = { +/* Carousel.propTypes = { autoplay: PropTypes.bool, delayLength: PropTypes.number, children: PropTypes.node -}; +}; */ export default Carousel; diff --git a/packages/collapse/__fixtures__/collapse.tsx b/packages/collapse/__fixtures__/collapse.tsx index bbfd6a47..d03a185f 100644 --- a/packages/collapse/__fixtures__/collapse.tsx +++ b/packages/collapse/__fixtures__/collapse.tsx @@ -1,14 +1,16 @@ -import React, {useRef} from 'react'; -import {Collapse, UseCollapseOptions} from '../src'; +import {useRef} from 'react'; +import type {UseCollapseOptions} from '../src'; +import {Collapse} from '../src'; const CollapseFixture = () => { - const contentRef = useRef(null); + const contentRef = useRef(null!); return ( + {Array.from({length: 10}, (_, key) => (
; const CollapseDisabledFixture = (props: CollapseDisabledFixtureProps) => { - const contentRef = useRef(null); + const contentRef = useRef(null!); return ( + {Array.from({length: 10}, (_, key) => (
{ }; const NestedCollapse = () => { - const contentRef = useRef(null); + const contentRef = useRef(null!); return ( + @@ -70,7 +74,7 @@ const fixtures = { disabled: , nestedCollapse: , manyCollapseFixture: ( - + <> @@ -80,8 +84,8 @@ const fixtures = { - + ) }; -export default fixtures; +export default fixtures; \ No newline at end of file diff --git a/packages/collapse/__fixtures__/use-collapse.tsx b/packages/collapse/__fixtures__/use-collapse.tsx index 364b5ff3..e3660008 100644 --- a/packages/collapse/__fixtures__/use-collapse.tsx +++ b/packages/collapse/__fixtures__/use-collapse.tsx @@ -1,6 +1,4 @@ -/** @jsx jsx */ -import {jsx} from 'theme-ui'; -import {Fragment, useRef} from 'react'; +import {useRef} from 'react'; import {useCollapse} from '../src'; type CollapseFixtureProps = { @@ -8,17 +6,19 @@ type CollapseFixtureProps = { }; const CollapseFixture = ({initiallyExpanded}: CollapseFixtureProps) => { - const contentRef = useRef(null); + const contentRef = useRef(null!); + const {getToggleProps, getCollapseProps} = useCollapse({ initiallyExpanded, contentRef }); return ( - + <> +
{Array.from({length: 10}, (_, key) => ( @@ -31,7 +31,7 @@ const CollapseFixture = ({initiallyExpanded}: CollapseFixtureProps) => { ))}
-
+ ); }; @@ -40,4 +40,4 @@ const fixtures = { defaultExpanded: }; -export default fixtures; +export default fixtures; \ No newline at end of file diff --git a/packages/collapse/babel.config.js b/packages/collapse/babel.config.js deleted file mode 100644 index 4c05afbe..00000000 --- a/packages/collapse/babel.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../babel.config'); diff --git a/packages/collapse/package.json b/packages/collapse/package.json index 9019221c..8990844a 100644 --- a/packages/collapse/package.json +++ b/packages/collapse/package.json @@ -2,27 +2,27 @@ "name": "@newfrontdoor/collapse", "version": "0.2.3", "license": "MIT", - "main": "dist/index.js", - "module": "dist/index.module.js", + "main": "dist/index.cjs", + "module": "dist/index.mjs", "source": "src/index.ts", - "types": "dist/index.d.ts", "scripts": { - "prepare": "microbundle --no-compress", - "watch": "microbundle watch --no-compress" + "build": "tsdown src/index.ts --format esm --format cjs --deps.skip-node-modules-bundle --clean", + "watch": "tsdown src/index.ts --format esm --format cjs --deps.skip-node-modules-bundle --watch" }, "files": [ "dist" ], "peerDependencies": { - "react": "^16.8.6", - "theme-ui": "^0.3.1" + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "react": "^19.2.0", + "theme-ui": "^0.17.4" }, - "dependencies": { - "prop-types": "^15.6.2" - }, - "devDependencies": { - "@testing-library/react": "^11.2.3", - "@types/resize-observer-browser": "^0.1.5" - }, - "xo": false + + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" + } + } } diff --git a/packages/collapse/src/collapse.tsx b/packages/collapse/src/collapse.tsx index d243de84..95a95adb 100644 --- a/packages/collapse/src/collapse.tsx +++ b/packages/collapse/src/collapse.tsx @@ -1,16 +1,26 @@ -/** @jsx jsx */ -import {jsx} from 'theme-ui'; + +import type { + ReactNode, + ReactElement +} from 'react'; import { cloneElement, createContext, - useContext, - ReactNode, - ReactElement + useContext } from 'react'; -import {useCollapse, UseCollapseOptions, CollapseState} from './use-collapse'; +import type { UseCollapseOptions, CollapseState} from './use-collapse'; +import {useCollapse} from './use-collapse'; + +type CollapseContextValue = Pick< + CollapseState, + 'contentRef' +> & { + collapseProps: ReturnType['getCollapseProps']>; + toggleProps: ReturnType['getToggleProps']>; +}; -export const CollapseContext = createContext>( - {} as CollapseState +export const CollapseContext = createContext( + {} as CollapseContextValue ); export const useCollapseContext = () => useContext(CollapseContext); @@ -20,9 +30,9 @@ type ToggleProps = { }; export const Toggle = ({children}: ToggleProps) => { - const {getToggleProps} = useCollapseContext(); + const {toggleProps} = useCollapseContext(); - return cloneElement(children, getToggleProps()); + return cloneElement(children, toggleProps); }; type ManagerProps = UseCollapseOptions & { @@ -31,9 +41,14 @@ type ManagerProps = UseCollapseOptions & { export const Manager = ({children, ...rest}: ManagerProps) => { const collapse = useCollapse(rest); + const value = { + contentRef: collapse.contentRef, + collapseProps: collapse.getCollapseProps(), + toggleProps: collapse.getToggleProps() + }; return ( - + {children} ); @@ -44,10 +59,10 @@ type PanelProps = { }; export const Panel = ({children}: PanelProps) => { - const {contentRef, getCollapseProps} = useCollapseContext(); + const {contentRef, collapseProps} = useCollapseContext(); return ( -
+
{children}
); diff --git a/packages/collapse/src/use-collapse.ts b/packages/collapse/src/use-collapse.ts index ff4779aa..eb931563 100644 --- a/packages/collapse/src/use-collapse.ts +++ b/packages/collapse/src/use-collapse.ts @@ -1,7 +1,5 @@ import {css} from 'theme-ui'; -import { - useState, - useEffect, +import type { Dispatch, SetStateAction, MouseEvent, @@ -10,6 +8,10 @@ import { TransitionEvent, ButtonHTMLAttributes } from 'react'; +import { + useState, + useEffect +} from 'react'; type CalculateStyleOptions = Required> & { duration: number; diff --git a/packages/content-editor/__fixtures__/editor.js b/packages/content-editor/__fixtures__/editor.jsx similarity index 99% rename from packages/content-editor/__fixtures__/editor.js rename to packages/content-editor/__fixtures__/editor.jsx index 0ffe1f02..9f8913b0 100644 --- a/packages/content-editor/__fixtures__/editor.js +++ b/packages/content-editor/__fixtures__/editor.jsx @@ -1,4 +1,4 @@ -import React from 'react'; + import {PTEditor, toSlate} from '../src'; const initialPT = [ diff --git a/packages/content-editor/babel.config.js b/packages/content-editor/babel.config.js deleted file mode 100644 index 4c05afbe..00000000 --- a/packages/content-editor/babel.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../babel.config'); diff --git a/packages/content-editor/package.json b/packages/content-editor/package.json index 7557b78f..1e5bf154 100644 --- a/packages/content-editor/package.json +++ b/packages/content-editor/package.json @@ -3,29 +3,36 @@ "version": "0.1.0", "license": "MIT", "source": "src/index.js", - "main": "dist/index.js", - "module": "dist/index.module.js", + "main": "dist/index.cjs", + "module": "dist/index.mjs", "scripts": { - "prepare": "microbundle --no-compress", - "watch": "microbundle watch --no-compress" + "build": "tsdown src/index.js --format esm --format cjs --deps.skip-node-modules-bundle --clean", + "watch": "tsdown src/index.js --format esm --format cjs --deps.skip-node-modules-bundle --watch" }, "files": [ "dist" ], "peerDependencies": { - "react": "^16.10.0", - "react-dom": "^16.10.0", - "theme-ui": "^0.3.1" + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "theme-ui": "^0.17.4" }, "dependencies": { + "@emotion/css": "^11.13.5", "@sanity/block-content-to-react": "^2.0.7", - "emotion": "^10.0.27", "is-hotkey": "^0.1.6", "is-url": "^1.2.4", - "prop-types": "^15.6.0", "slate": "^0.57.1", "slate-history": "^0.57.1", "slate-react": "^0.57.1" }, - "xo": false + + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" + } + } } diff --git a/packages/content-editor/src/components.js b/packages/content-editor/src/components.js deleted file mode 100644 index 81f0ed75..00000000 --- a/packages/content-editor/src/components.js +++ /dev/null @@ -1,145 +0,0 @@ -/** @jsx jsx */ -/** @jsxFrag React.Fragment */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import {cx, css} from 'emotion'; -import {jsx} from 'theme-ui'; - -export const Button = React.forwardRef( - ({className, active, reversed, ...props}, ref) => ( - - ) -); - -export const EditorValue = React.forwardRef( - ({className, value, ...props}, ref) => { - const textLines = value.document.nodes - .map((node) => node.text) - .toArray() - .join('\n'); - return ( -
-
- Slate's value as text -
-
- {textLines} -
-
- ); - } -); - -export const Icon = React.forwardRef(({className, ...props}, ref) => ( - -)); - -export const Instruction = React.forwardRef(({className, ...props}, ref) => ( -
-)); - -export const Menu = React.forwardRef(({className, ...props}, ref) => ( -
* { - display: inline-block; - } - & > * + * { - margin-left: 15px; - } - ` - )} - /> -)); - -export const Portal = ({children}) => { - return ReactDOM.createPortal(children, document.body); -}; - -export const Toolbar = React.forwardRef(({className, ...props}, ref) => ( - -)); diff --git a/packages/content-editor/src/components.jsx b/packages/content-editor/src/components.jsx new file mode 100644 index 00000000..359d6f6a --- /dev/null +++ b/packages/content-editor/src/components.jsx @@ -0,0 +1,183 @@ +import { createPortal } from 'react-dom'; +import { css, cx } from '@emotion/css'; + +export function Button({ + className, + active, + reversed, + ref, + ...props +}) { + return ( + + ); +} + +export function EditorValue({ + className, + value, + ref, + ...props +}) { + const textLines = value.document.nodes + .map((node) => node.text) + .toArray() + .join('\n'); + + return ( +
+
+ Slate's value as text +
+ +
+ {textLines} +
+
+ ); +} + +export function Icon({ + className, + ref, + ...props +}) { + return ( + + ); +} + +export function Instruction({ + className, + ref, + ...props +}) { + return ( +
+ ); +} + +export function Menu({ + className, + ref, + ...props +}) { + return ( +
* { + display: inline-block; + } + + & > * + * { + margin-left: 15px; + } + ` + )} + /> + ); +} + +export function Portal({ children }) { + if (typeof document === 'undefined') { + return null; + } + + return createPortal(children, document.body); +} + +export function Toolbar({ + className, + ref, + ...props +}) { + return ( + + ); +} \ No newline at end of file diff --git a/packages/content-editor/src/editor.js b/packages/content-editor/src/editor.jsx similarity index 97% rename from packages/content-editor/src/editor.js rename to packages/content-editor/src/editor.jsx index b84e9dbf..65c6a396 100644 --- a/packages/content-editor/src/editor.js +++ b/packages/content-editor/src/editor.jsx @@ -1,4 +1,4 @@ -import React, {useCallback, useMemo, useState} from 'react'; +import {useCallback, useMemo, useState} from 'react'; import isUrl from 'is-url'; import isHotkey from 'is-hotkey'; @@ -28,7 +28,7 @@ export const PTEditor = (props) => { ); return ( - + <> { Slate Doc
{JSON.stringify(value, null, 2)}
-
+ ); }; @@ -112,7 +112,10 @@ const LinkButton = () => { active={isLinkActive(editor)} onMouseDown={(event) => { event.preventDefault(); - const url = window.prompt('Enter the URL of the link:'); + const url = + typeof window === 'undefined' + ? null + : window.prompt('Enter the URL of the link:'); if (!url) return; insertLink(editor, url); }} diff --git a/packages/content-editor/src/invert-utils.js b/packages/content-editor/src/invert-utils.js index 4c841eab..4754ece1 100644 --- a/packages/content-editor/src/invert-utils.js +++ b/packages/content-editor/src/invert-utils.js @@ -26,9 +26,9 @@ export function toSlate(content) { ...(block.markDefs ? '' : ''), children: block.children.map((item) => { if (item.marks.some((r) => defsKey.includes(r))) { - const customMarks = item.marks.filter( + /* const customMarks = item.marks.filter( (item) => !standardMarks.has(item) - ); + ); */ const inherentMarks = item.marks.filter((item) => standardMarks.has(item) ); diff --git a/packages/content-editor/src/portable-text.js b/packages/content-editor/src/portable-text.jsx similarity index 82% rename from packages/content-editor/src/portable-text.js rename to packages/content-editor/src/portable-text.jsx index b2794562..02bf3108 100644 --- a/packages/content-editor/src/portable-text.js +++ b/packages/content-editor/src/portable-text.jsx @@ -1,8 +1,6 @@ -/** @jsx jsx */ -/** @jsxFrag React.Fragment */ -import React from 'react'; + import BlockContent from '@sanity/block-content-to-react'; -import {jsx, Styled} from 'theme-ui'; + const serializers = { types: { block: (props) => { @@ -30,13 +28,13 @@ const serializers = { export const PortableText = (props) => { return ( - - PortableText to React + <> +

PortableText to React

PortableText Doc
{JSON.stringify(props.blocks, null, 2)}
-
+ ); }; diff --git a/packages/footer/__fixtures__/footer.tsx b/packages/footer/__fixtures__/footer.tsx index 7b0f1528..85dea9dd 100644 --- a/packages/footer/__fixtures__/footer.tsx +++ b/packages/footer/__fixtures__/footer.tsx @@ -1,11 +1,11 @@ -import React, {FC, Fragment} from 'react'; +import type {FC} from 'react'; import {Footer} from '../src/footer'; const FooterFixture: FC = () => { return ( - + <>