Skip to content

Commit

Permalink
chore: o3 components, initial commit (#1054)
Browse files Browse the repository at this point in the history
This commit is huge, truly monstrous. We worked from a long running feature branch, merging individually reviewed PRs into it. Due to a mix in commit message quality and the risk of triggering release-please via semantic commits in an unexpected way, we've decided to squash these changes into one "chore" commit and create a separate release commit.

Introduce our first "o3" Origami For Everyone components:
- Blog: https://origami.ft.com/blog/2023/08/14/origami-for-everyone/
- Narrative document: https://docs.google.com/document/d/19dqsDNph1a1EkyIijBhTfi8DhACYfUoSwOZBY8C57nI/edit#heading=h.g6h5rat0x3qj


"o3" changes include:
- `apps/dictionary`: Design tokens, which contains design tokens and Style Dictionary tooling. We use a Figma plugin, Token Studio, to help manage these.
- `apps/o3-storybook`: A dedicated Storybook for "o3" components, published to Chromatic.
- `components/o3-web-token`: A package of "base" and "usecase" CSS Custom Properties for all supported brands.
- `components/o3-button`: A re-implementation of o-button for "o3", built with design tokens and css custom properties, and without Sass. We publish transpiled JSX with types rather than TSX source.
- `scripts/component/build-tokens.bash`: A script to build CSS Custom Properties for components from tokens.
- `scripts/component/build-o3.bash`: A script to build/bundle "o3" components for publication.


"o2" changes include:
- We commented out a whole bunch of "o2" component tests. Yikes. It appears dependency additions changed our `node_modules` structure, likely due to hoisting, and the configuration within `web-test-runner.config.mjs` to convert `@testing-library/dom`/`@testing-library/event` to ESM which appeared to no longer be effective. We tried adding to this configuration; we tried removing new dependencies but could (sometimes?) reproduce with a fresh install on `main`; we tried switching to webdriver.io and its inbuilt event simulation, only to receive not dissimilar errors for different component tests; we tried pinning existing decencies; we explored npm install modes (`link` is experimental and failed) and thought about switching to pnmp thinking module resolution may be an issue. All to no avail. Extracting components from the monorepo, all tests ran fine, but we avoided the temptation to move "o3" to a separate repository. Since "o2" components aren't under active development, and we did reproduce on `main` with a fresh install, we decided it was best to comment out tests which depend on `@testing-library` to unblock releasing experimental "o3" components. We intend to revisit when working on our approach to "o3" component testing – which currently includes no JavaScript, outside TSX templates.
    - https://github.com/Financial-Times/origami/blob/c47209e7f56f4a5d46437927ff73ca65012bbdfa/tools/test-javascript/web-test-runner.config.mjs#L18
    - https://modern-web.dev/guides/going-buildless/es-modules/#commonjs-plugin
    - https://docs.npmjs.com/cli/v10/commands/npm-install#install-strategy
- Removing `percy` from the `package.json` file of existing components. This wasn't used.
- Some demo accessibility improvements, flagged by bumping our version of `axe`.
  • Loading branch information
frshwtr authored Oct 23, 2023
1 parent c47209e commit b49b43b
Show file tree
Hide file tree
Showing 166 changed files with 59,377 additions and 89,582 deletions.
10 changes: 10 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ o-viewport:
- components/o-viewport/**/*
o-visual-effects:
- components/o-visual-effects/**/*
o3-button:
- components/o3-button/**/*
o3-web-token:
- components/o3-web-token/**/*
ftdomdelegate:
- libraries/ftdomdelegate/**/*
math:
Expand Down Expand Up @@ -148,6 +152,8 @@ create-component:
- tools/create-component/**/*
demo-build:
- tools/demo-build/**/*
o3-compilation:
- tools/o3-compilation/**/*
origami-bower-safe-version-supervisor:
- tools/origami-bower-safe-version-supervisor/**/*
origami-tools-helpers:
Expand All @@ -174,5 +180,9 @@ markdown-tabs:
- apps/storybook/addons/markdown-tabs/**/*
astro-website:
- apps/astro-website/**/*
dictionary:
- apps/dictionary/**/*
o3-storybook:
- apps/o3-storybook/**/*
storybook:
- apps/storybook/**/*
29 changes: 29 additions & 0 deletions .github/workflows/chromatic.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: 'Chromatic'

on:
push:
branches: [ main ]
paths:
- "package.json"
- "package-lock.json"
- "apps/o3-storybook/**"
- "components/o3-*/**"
- "components/o3-*/**"

jobs:
chromatic-deployment:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 0 # 👈 Required to retrieve git history
- name: Install dependencies
run: npm ci
# 👇 Adds Chromatic as a step in the workflow
- name: Publish to Chromatic
uses: chromaui/action@v1
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
workingDir: apps/o3-storybook
exitOnceUploaded: true
2 changes: 2 additions & 0 deletions .github/workflows/percy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ on:
- "package-lock.json"
- "components/**"
- "!components/**/*.md"
- "!components/o3-*/**"
push:
branches: main
paths:
- "package.json"
- "package-lock.json"
- "components/**"
- "!components/**/*.md"
- "!components/o3-*/**"

jobs:
changes:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
node-version: 16
registry-url: "https://registry.npmjs.org"
if: ${{ steps.release.outputs.releases_created }}
- run: npm i -g npm@7
- run: npm i -g npm@8
if: ${{ steps.release.outputs.releases_created }}
- run: npm ci
if: ${{ steps.release.outputs.releases_created }}
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ jobs:
- 'components/o-viewport/**'
components/o-visual-effects:
- 'components/o-visual-effects/**'
components/o3-button:
- 'components/o3-button/**'
components/o3-web-token:
- 'components/o3-web-token/**'
libraries/ftdomdelegate:
- 'libraries/ftdomdelegate/**'
libraries/math:
Expand Down Expand Up @@ -151,6 +155,8 @@ jobs:
- 'tools/create-component/**'
tools/demo-build:
- 'tools/demo-build/**'
tools/o3-compilation:
- 'tools/o3-compilation/**'
tools/origami-bower-safe-version-supervisor:
- 'tools/origami-bower-safe-version-supervisor/**'
tools/origami-tools-helpers:
Expand All @@ -177,6 +183,10 @@ jobs:
- 'apps/storybook/addons/markdown-tabs/**'
apps/astro-website:
- 'apps/astro-website/**'
apps/dictionary:
- 'apps/dictionary/**'
apps/o3-storybook:
- 'apps/o3-storybook/**'
apps/storybook:
- 'apps/storybook/**'
test:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ Icon?
.lsp/
.log/
.nova/
storybook-static/
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"components/ft-concept-button":"1.2.2","components/g-audio":"2.0.2","components/n-notification":"8.2.5","components/o-audio":"2.1.3","components/o-autocomplete":"1.9.0","components/o-banner":"4.5.1","components/o-big-number":"3.2.2","components/o-buttons":"7.9.0","components/o-colors":"6.6.2","components/o-comments":"10.3.3","components/o-cookie-message":"6.7.0","components/o-date":"6.0.0","components/o-editorial-layout":"2.4.2","components/o-editorial-typography":"2.4.0","components/o-expander":"6.2.6","components/o-fonts":"5.3.4","components/o-footer":"9.2.8","components/o-footer-services":"4.2.7","components/o-forms":"9.12.0","components/o-ft-affiliate-ribbon":"5.2.1","components/o-grid":"6.1.7","components/o-header":"11.1.0","components/o-header-services":"5.5.1","components/o-icons":"7.7.0","components/o-labels":"6.5.7","components/o-layout":"5.3.3","components/o-lazy-load":"3.1.2","components/o-loading":"5.2.2","components/o-message":"5.4.3","components/o-meter":"3.2.3","components/o-multi-select":"2.2.1","components/o-normalise":"3.3.1","components/o-overlay":"4.2.10","components/o-quote":"5.3.3","components/o-share":"10.0.0","components/o-social-follow":"1.0.9","components/o-spacing":"3.2.4","components/o-stepped-progress":"4.0.8","components/o-subs-card":"6.2.5","components/o-syntax-highlight":"4.2.4","components/o-table":"9.3.2","components/o-tabs":"8.1.3","components/o-teaser":"6.2.8","components/o-teaser-collection":"4.2.4","components/o-toggle":"3.2.5","components/o-tooltip":"5.3.1","components/o-top-banner":"1.0.3","components/o-topper":"6.0.7","components/o-typography":"7.6.0","components/o-video":"7.2.10","components/o-viewport":"5.1.1","components/o-visual-effects":"4.2.1","libraries/ftdomdelegate":"5.0.0","libraries/math":"1.1.0","libraries/o-autoinit":"3.1.3","libraries/o-brand":"4.2.1","libraries/o-errors":"5.2.2","libraries/o-tracking":"4.5.0","libraries/o-utils":"2.2.0","libraries/sass-mq":"5.2.3","presets/eslint-config-origami-component":"2.2.0","presets/remark-preset-lint-origami-component":"2.0.2","presets/stylelint-config-origami-component":"1.0.5","tools/origami-bower-safe-version-supervisor":"1.1.2"}
{"components/ft-concept-button":"1.2.2","components/g-audio":"2.0.2","components/n-notification":"8.2.5","components/o-audio":"2.1.3","components/o-autocomplete":"1.9.0","components/o-banner":"4.5.1","components/o-big-number":"3.2.2","components/o-buttons":"7.9.0","components/o-colors":"6.6.2","components/o-comments":"10.3.3","components/o-cookie-message":"6.7.0","components/o-date":"6.0.0","components/o-editorial-layout":"2.4.2","components/o-editorial-typography":"2.4.0","components/o-expander":"6.2.6","components/o-fonts":"5.3.4","components/o-footer":"9.2.8","components/o-footer-services":"4.2.7","components/o-forms":"9.12.0","components/o-ft-affiliate-ribbon":"5.2.1","components/o-grid":"6.1.7","components/o-header":"11.1.0","components/o-header-services":"5.5.1","components/o-icons":"7.7.0","components/o-labels":"6.5.7","components/o-layout":"5.3.3","components/o-lazy-load":"3.1.2","components/o-loading":"5.2.2","components/o-message":"5.4.3","components/o-meter":"3.2.3","components/o-multi-select":"2.2.1","components/o-normalise":"3.3.1","components/o-overlay":"4.2.10","components/o-quote":"5.3.3","components/o-share":"10.0.0","components/o-social-follow":"1.0.9","components/o-spacing":"3.2.4","components/o-stepped-progress":"4.0.8","components/o-subs-card":"6.2.5","components/o-syntax-highlight":"4.2.4","components/o-table":"9.3.2","components/o-tabs":"8.1.3","components/o-teaser":"6.2.8","components/o-teaser-collection":"4.2.4","components/o-toggle":"3.2.5","components/o-tooltip":"5.3.1","components/o-top-banner":"1.0.3","components/o-topper":"6.0.7","components/o-typography":"7.6.0","components/o-video":"7.2.10","components/o-viewport":"5.1.1","components/o-visual-effects":"4.2.1","components/o3-button":"0.1.0","components/o3-web-token":"0.1.0","libraries/ftdomdelegate":"5.0.0","libraries/math":"1.1.0","libraries/o-autoinit":"3.1.3","libraries/o-brand":"4.2.1","libraries/o-errors":"5.2.2","libraries/o-tracking":"4.5.0","libraries/o-utils":"2.2.0","libraries/sass-mq":"5.2.3","presets/eslint-config-origami-component":"2.2.0","presets/remark-preset-lint-origami-component":"2.0.2","presets/stylelint-config-origami-component":"1.0.5","tools/origami-bower-safe-version-supervisor":"1.1.2"}
43 changes: 43 additions & 0 deletions apps/dictionary/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Origami Dictionary

The Origami Dictionary is the repository for storing, managing and publishing Origami Design Tokens.

## Getting Started

The dictionary uses [Tokens Studio](https://tokens.studio/) and [Style Dictionary](https://amzn.github.io/style-dictionary/#/) to manage and publish design tokens.

### Developers and Desingers

- Ensure you have been given a Tokens Studio account, the #origami-support Slack channel can help with this.
- Follow the steps on the [token's studio docs](https://docs.tokens.studio/sync/github) to sync the dictionary with Tokens Studio in Figma.
- On step 4, ensure you use the following details:
- Add your GitHub repository `Financial-Times/origami`.
- Add your dictionary branch `poc/multi-brand`.
- Specify a file path where your tokens should be stored `apps/dictionary/tokens`.

### Adding/updating SVG icon tokens

First of all, the icon will need to be added/updated in Figma. Once the designer has updated the icon, the designer will need to copy the SVG code and put it inside the Token Studio under Icons set with type of `Asset`. After this we will need to push changes from token studio to GitHub so new tokens can be used by other developers or components.

### Developers

- Follow the steps for [Developers and Designers](#developers-and-desingers).
- Ensure you have been added to the Origami team on GitHub.
- Clone the repository

```bash
git clone [email protected]:Financial-Times/origami.git
```

- Install the dependencies

```bash
npm ci
```

- Build the dictionary locally

```bash
cd apps/dictionary
npm run build
```
13 changes: 13 additions & 0 deletions apps/dictionary/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "origami-dictionary",
"version": "1.0.0",
"description": "",
"type": "module",
"author": "",
"license": "ISC",
"private": true,
"devDependencies": {
"@tokens-studio/sd-transforms": "^0.11.0",
"style-dictionary": "^3.7.2"
}
}
72 changes: 72 additions & 0 deletions apps/dictionary/scripts/build-config/build-component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import StyleDictionaryPackage from "style-dictionary"
import {brandClasses} from "../formatters/css/brand-classes.js"
import {nameOrigamiPrivatePrefix} from "../transforms/nameOrigamiPrefix.js"
import {tokenStudioThemes, ConfigBuilder} from "./utils.js"

StyleDictionaryPackage.registerFormat({
name: "css/brand/classes",
formatter: brandClasses,
})
StyleDictionaryPackage.registerTransform({
name: "name/origamiPrivatePrefix",
type: "name",
transformer: nameOrigamiPrivatePrefix,
})
StyleDictionaryPackage.registerFilter({
name: "filter/doNotUse",
matcher: token =>
token.original.value !== "{DO-NOT-USE}" && token.path[0] !== "DO-NOT-USE",
})
StyleDictionaryPackage.registerTransform({
name: "value/transformAssetUrls",
type: "value",
transformer: token => (token.value = `url("${token.value}")`),
matcher: token => token.type === "asset",
transitive: true,
})

function buildComponentTokens() {
const componentName = process.argv[2]
const includeTokens = [
"tokens/core/base/color.json",
"tokens/core/base/border-radius.json",
"tokens/core/base/spacing.json",
"tokens/core/base/typography.json",
"tokens/utility-tokens.json",
]

const transformers = ["value/transformAssetUrls", "name/origamiPrivatePrefix"]

const filesConfig = [
{
filter: "filter/doNotUse",
format: "css/brand/classes",
options: {
outputReferences: true,
includePrefix: [componentName],
},
},
]

tokenStudioThemes.forEach(theme => {
const brandName =
theme.group != theme.name ? `${theme.group}/${theme.name}` : theme.group
includeTokens.push(`tokens/${brandName}/**.json`)
const sources = [`tokens/${brandName}/components/${componentName}.json`]
const exportPath = `../../components/${componentName}/src/css/tokens/${brandName}/${componentName}/_variables.css`
filesConfig[0].destination = exportPath
filesConfig[0].options.classNames = [
`o-brand-${brandName.split("/").slice(-1)}`,
`${componentName}`,
]

new ConfigBuilder(StyleDictionaryPackage)
.setSources(sources)
.setIncludeFiles(includeTokens)
.setTransforms(transformers)
.setBuildPath(`./`)
.setFilesConfig(filesConfig)
.buildDictionary()
})
}
buildComponentTokens()
91 changes: 91 additions & 0 deletions apps/dictionary/scripts/build-config/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import path from "path"
import {fileURLToPath} from "url"
import StyleDictionaryPackage from "style-dictionary"
import {brandClasses} from "../formatters/css/brand-classes.js"
import {transformSVG} from "../transforms/transformSVG.js"
import {ConfigBuilder, tokenStudioThemes} from "./utils.js"

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const basePath = path.join(__dirname, "../../")

StyleDictionaryPackage.registerFormat({
name: "css/brand/classes",
formatter: brandClasses,
})

StyleDictionaryPackage.registerTransform({
name: "value/transformSVG",
type: "value",
transformer: transformSVG,
matcher: token => token.type === "asset",
transitive: true,
})

StyleDictionaryPackage.registerFilter({
name: "filter/doNotUse",
matcher: token =>
token.original.value !== "{DO-NOT-USE}" && token.path[0] !== "DO-NOT-USE",
})

function getBrands() {
return tokenStudioThemes.map(theme => {
const brandName =
theme.group != theme.name ? `${theme.group}/${theme.name}` : theme.group
return {
name: brandName,
sources: Object.keys(theme.selectedTokenSets)
.filter(
tokenSet =>
!tokenSet.startsWith(`${brandName}/components/`) &&
theme.selectedTokenSets[tokenSet] === "enabled"
)
.map(tokenSet => `tokens/${tokenSet}.json`),
}
})
}

function buildBrandTokens() {
const brands = getBrands()
brands.forEach(brand => {
const filesConfig = [
{
filter: "filter/doNotUse",
destination: "_variables.css",
format: "css/brand/classes",
options: {
outputReferences: true,
classNames: [`o-brand-${brand.name.split("/").slice(-1)}`],
},
},
]
brand.sources = brand.sources.map(source => path.join(basePath, source))
new ConfigBuilder(StyleDictionaryPackage)
.setSources(brand.sources)
.setBuildPath(`build/${brand.name}/`)
.setFilesConfig(filesConfig)
.buildDictionary()
})
}
function buildIconTokens() {
const iconTransformers = ["value/transformSVG"]
const iconsFileConfig = [
{
destination: "_variables.css",
format: "css/variables",
options: {
outputReferences: true,
},
},
]

new ConfigBuilder(StyleDictionaryPackage)
.setSources([path.join(basePath, "tokens/icons/icons.json")])
.setTransforms(iconTransformers)
.setBuildPath("build/icons/")
.setFilesConfig(iconsFileConfig)
.buildDictionary()
}

buildBrandTokens()
buildIconTokens()
Loading

0 comments on commit b49b43b

Please sign in to comment.