diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index b099262..dbd11d3 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -14,6 +14,16 @@ N.B. See changelogs for individual packages, where most change will occur: This log covers the [monorepo](https://en.wikipedia.org/wiki/Monorepo). +## [0.11.0] - 2025-09-29 + +### Changed + +- removed the `tags` badge from main `README.md`, this monorepo does not use release tags + +### Added + +- a `discussion` badge + ## [0.10.7] - 2025-07-14 ### Fixed diff --git a/docs/README.md b/docs/README.md index a59e5f2..f8be1d5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,10 +5,10 @@ A library providing a means to toggle or branch web application code.

- Current version Current test status PRs are welcome toggle point issues + toggle point discussions toggle point stars toggle point forks toggle point license diff --git a/examples/next/README.md b/examples/next/README.md index 9ff4860..48a7229 100644 --- a/examples/next/README.md +++ b/examples/next/README.md @@ -1,48 +1,24 @@ # Next JS example -This example shows the use of the [`react-pointcuts`](../../packages/react-pointcuts/docs/README.md), [`features`](../../packages/features/docs/README.md) and [`webpack`](../../packages/webpack/docs/README.md) packages, as part of an ["app router"](https://nextjs.org/docs/app) [Next.js](https://nextjs.org/) application. +These examples show the use of the [`react-pointcuts`](../../packages/react-pointcuts/docs/README.md), [`features`](../../packages/features/docs/README.md) and [`webpack`](../../packages/webpack/docs/README.md) packages, as part of an ["app router"](https://nextjs.org/docs/app) [Next.js](https://nextjs.org/) application. N.B. NextJs support is currently experimental, see [caveats](#caveats). -## Setup +## Examples -It is using a contrived point cut plugin, replicating an Optimizely activation handler: +1. [content management](./src/app/fixtures/content-management/README.mdx) -```js -{ - onCodeSelected: ({ matchedFeatures }) => { - if (matchedFeatures?.length) { - const [[feature]] = matchedFeatures; - console.log( - `activated ${feature} with audience ${getFeatures().audience}` - ); - } - } -} -``` + This is a basic example demonstrating the ability to vary react hooks, using a contrived content management feature. -...which logs the activation event normally destined for the toggle router (Optimizely) to the console. +2. [experiments](./src/app/fixtures/experiments/README.mdx) -A contrived server function called `getExperiments` exists to parse inbound headers containing experiments, used to drive the toggling. - -## Usage - -(from the `examples/next` folder of the monorepo) - -1. install [mod header](https://modheader.com/), or some other tool for modifying request headers sent in a browser -2. `npm install` -4. `npm run dev` -5. open `localhost:3000/fixtures/experiments` in a browser - -See appropriate documentation within [the examples folder](./src/app/fixtures/experiments/README.md). - -N.B. To confirm the `experiments` header you've set with `mod header`, you can add `?showExperiments=true` to the URL to render the value to the top of the page. -If you're not seeing the experiments header show up, try refreshing the page. NextJs is perhaps pre-caching the pages. + These examples show various toggle setups with react components being varied, opting out of variation, etc. ## Caveats -- Only client components are supported, since request-bound context is not supported by server components. They are not meant to be stateful. - - API routes may be supportable, via use of [an async local storage wrapper](https://github.com/rexfordessilfie/nextwrappers/tree/main/packages/async-local-storage), once support for named exports is added ([Issue #4](https://github.com/ASOS/web-toggle-point/issues/4)) - since would need to match the HTTP verbs. -- The webpack package cannot currently vary NextJs managed files such as [pages](https://nextjs.org/docs/app/building-your-application/routing/pages) themselves, but can vary modules they import ([Issue #9](https://github.com/ASOS/web-toggle-point/issues/9)). +- Only client components can be toggled using a per-request features store, since request-bound context is not supported by server components. They are not meant to be stateful + - API routes may be supportable, via use of [an async local storage wrapper](https://github.com/rexfordessilfie/nextwrappers/tree/main/packages/async-local-storage), once support for named exports is added ([Issue #4](https://github.com/ASOS/web-toggle-point/issues/4)) - since would need to match the HTTP verbs +- The webpack package cannot currently vary some of NextJs' [filesystem convention files](https://nextjs.org/docs/pages/getting-started/project-structure#files-conventions) ([Issue #9](https://github.com/ASOS/web-toggle-point/issues/9)) - The `webpack` plugin uses webpack hooks, so is incompatible with the new TurboPack bundler -- The `webpack` plugin uses Node JS APIs to access the filesystem, so may be incompatible with [the edge runtime](https://nextjs.org/docs/app/api-reference/edge#unsupported-apis). +- The `webpack` plugin uses Node JS APIs to access the filesystem, so may be incompatible with [the edge runtime](https://nextjs.org/docs/app/api-reference/edge#unsupported-apis) +- The `nodeRequestScopedFeaturesStoreFactory` from the [`features`](../../packages/features/docs/README.md) package relies on singleton values held in top-level scope, which Next does not support. See [issue 50](https://github.com/ASOS/web-toggle-point/issues/50), which should remedy this. \ No newline at end of file diff --git a/examples/next/docs/CHANGELOG.md b/examples/next/docs/CHANGELOG.md index 6429dd7..48ab2d0 100644 --- a/examples/next/docs/CHANGELOG.md +++ b/examples/next/docs/CHANGELOG.md @@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.0] - 2025-09-29 + +### Added + +- added a "content management" example, demonstrating use of `withToggledHookFactory` from the `react-pointcuts` package + +### Changed + +- colocate documentation for "experiments" example to sit with its own `README.mdx` +- update to static `webpackNormalModule` option of webpack package [version 0.8.1](../../../packages/webpack/docs/CHANGELOG.md#081---2025-07-27) +- Updated to named exports version of "features" package ([0.4.0](../../../packages/features/docs/CHANGELOG.md#040---2025-07-15)) + +### Fixed + +- consistent "Explanation" and "Activation" sections in example `README.mdx` files +- removed errant `toggle-point.d.ts` in `tsconfig.json` +- moved type packages to devDependencies + ## [0.2.5] - 2025-07-15 ### Changed @@ -19,19 +37,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- updated to use `variantGlobs` array, with updated webpack plugin [0.8.0][version 0.8.0](../../../packages/webpack/docs/CHANGELOG.md#080---2025-05-27) +- updated to use `variantGlobs` array, with updated webpack plugin [version 0.8.0](../../../packages/webpack/docs/CHANGELOG.md#080---2025-05-27) ## [0.2.3] - 2025-02-07 ### Fixed -- fixed a regression in the ability to use `?showExperiments` query in the experiments fixtures, regressed in version `0.2.0`. +- fixed a regression in the ability to use `?showExperiments` query in the experiments fixtures, regressed in [version 0.2.0](#020---2024-12-06). ## [0.2.2] - 2024-12-24 ### Fixed - links to folders, not `README.mdx`, in the experiments examples +- create `FeaturesProvider` via factory in outermost scope, rather than on each render of an example ## [0.2.1] - 2024-12-18 diff --git a/examples/next/next-env.d.ts b/examples/next/next-env.d.ts index 1b3be08..830fb59 100644 --- a/examples/next/next-env.d.ts +++ b/examples/next/next-env.d.ts @@ -1,5 +1,6 @@ /// /// +/// // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/examples/next/next.config.mjs b/examples/next/next.config.mjs index 428a031..afbacf1 100644 --- a/examples/next/next.config.mjs +++ b/examples/next/next.config.mjs @@ -1,24 +1,18 @@ import createMDX from "@next/mdx"; import remarkGfm from "remark-gfm"; +import { TogglePointInjection } from "@asos/web-toggle-point-webpack/plugins"; +import experimentPointCutConfig from "./src/app/fixtures/experiments/__pointCutConfig.js"; +import contentManagementPointCutConfig from "./src/app/fixtures/content-management/__pointCutConfig.js"; +import webpackNormalModule from "next/dist/compiled/webpack/NormalModule.js"; /** @type {import('next').NextConfig} */ const nextConfig = { pageExtensions: ["js", "md", "mdx", "ts", "tsx"] }; -import { TogglePointInjection } from "@asos/web-toggle-point-webpack/plugins"; const togglePointInjection = new TogglePointInjection({ - pointCuts: [ - { - name: "experiments", - togglePointModule: "/src/app/fixtures/experiments/withTogglePoint", - variantGlobs: [ - "./src/app/fixtures/experiments/**/__variants__/*/*/!(*.spec).tsx" - ] - } - ], - webpackNormalModule: async () => - (await import("next/dist/compiled/webpack/NormalModule.js")).default + pointCuts: [...experimentPointCutConfig, ...contentManagementPointCutConfig], + webpackNormalModule }); nextConfig.webpack = (config) => { diff --git a/examples/next/package.json b/examples/next/package.json index a7b9aa1..321755a 100644 --- a/examples/next/package.json +++ b/examples/next/package.json @@ -1,6 +1,6 @@ { "name": "web-toggle-point-next-example", - "version": "0.2.5", + "version": "0.3.0", "private": true, "type": "module", "scripts": { @@ -17,20 +17,21 @@ "lint:docs": "eslint **/*.mdx" }, "dependencies": { - "@asos/web-toggle-point-react-pointcuts": "file:../../packages/react-pointcuts", "@asos/web-toggle-point-features": "file:../../packages/features", + "@asos/web-toggle-point-react-pointcuts": "file:../../packages/react-pointcuts", "@asos/web-toggle-point-webpack": "file:../../packages/webpack", "@mdx-js/loader": "^3.1.0", "@mdx-js/react": "^3.1.0", "@next/mdx": "^15.0.3", - "@types/mdx": "^2.0.13", - "next": "^15.0.3", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "remark-gfm": "^4.0.0" + "next": "^15.3.0", + "remark-gfm": "^4.0.0", + "turndown": "^7.2.0" }, "devDependencies": { "@next/eslint-plugin-next": "^15.0.3", + "@types/mdx": "^2.0.13", + "@types/turndown": "^5.0.5", + "@types/webpack-env": "^1.18.8", "eslint-config-next": "^15.0.3", "eslint-import-resolver-alias": "^1.1.2", "eslint-plugin-mdx": "^3.1.5", diff --git a/examples/next/src/app/fixtures/content-management/README.mdx b/examples/next/src/app/fixtures/content-management/README.mdx new file mode 100644 index 0000000..61d9b75 --- /dev/null +++ b/examples/next/src/app/fixtures/content-management/README.mdx @@ -0,0 +1,18 @@ +Content Management +================== + +Explanation +----------- + +This example demonstrates the `withToggledHookFactory` from the `react-pointcuts` package, and the `reactContextFeaturesStoreFactory` from the `features` package. + +A React hook is varied, from a ["no operation"](https://en.wikipedia.org/wiki/NOP_\(code\)) control version, to one that enables [design mode](https://developer.mozilla.org/en-US/docs/Web/API/Document/designMode) for the document. + +Activation +---------- + +If a cookie exists with the name `i-am-a-content-editor` and any value, on refreshing the page, the document becomes editable. Its background will be green to help indicate this state. + +After making changes, pressing [Command](https://en.wikipedia.org/wiki/Command_key) and S (⌘+S or ⊞+S, per O/S) will save changes back to disk, via a [server function](https://react.dev/reference/rsc/server-functions). The background will flash blue whilst save is in progress. + +N.B. The action uses [turndown](https://github.com/mixmark-io/turndown) to convert the HTML back to [Markdown](https://en.wikipedia.org/wiki/Markdown), so is limited by what that can interpret. \ No newline at end of file diff --git a/examples/next/src/app/fixtures/content-management/__pointCutConfig.js b/examples/next/src/app/fixtures/content-management/__pointCutConfig.js new file mode 100644 index 0000000..bc97cb9 --- /dev/null +++ b/examples/next/src/app/fixtures/content-management/__pointCutConfig.js @@ -0,0 +1,9 @@ +export default [ + { + name: "content management", + togglePointModule: "/src/app/fixtures/content-management/withToggledHook", + variantGlobs: [ + "./src/app/fixtures/content-management/__variants__/devMode/active/useContentEditable.ts" + ] + } +]; diff --git a/examples/next/src/app/fixtures/content-management/__variants__/devMode/active/useContentEditable.ts b/examples/next/src/app/fixtures/content-management/__variants__/devMode/active/useContentEditable.ts new file mode 100644 index 0000000..e61c907 --- /dev/null +++ b/examples/next/src/app/fixtures/content-management/__variants__/devMode/active/useContentEditable.ts @@ -0,0 +1,51 @@ +import { useEffect, useRef, useTransition } from "react"; +import { saveMarkdown } from "../../../actions"; +import type TurndownService from "turndown"; + +const useContentEditable = () => { + const [isPending, startTransition] = useTransition(); + const turndownServiceRef = useRef(null); + + useEffect(() => { + if (isPending) { + document.body.setAttribute("data-is-saving", "true"); + } else { + document.body.removeAttribute("data-is-saving"); + } + }, [isPending]); + + const okKeyDown = (e: KeyboardEvent) => { + if (e.metaKey && e.key === "s") { + e.preventDefault(); + startTransition(async () => { + await saveMarkdown( + turndownServiceRef.current?.turndown( + document.body.innerHTML.replaceAll(/