Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4564a21
fix: skip api doc generation for packages without a js entry point
HendrikThePendric Sep 10, 2026
487680a
chore(storybook): resolve typescript entry points for internal packages
HendrikThePendric Sep 10, 2026
efd2c82
chore(lint): parse typescript sources with @typescript-eslint/parser
HendrikThePendric Sep 10, 2026
1ae8825
fix(lint): silence import/no-unresolved for typescript's .js->.ts spe…
HendrikThePendric Sep 10, 2026
4b76738
chore(ou-tree): add typescript config and typecheck script
HendrikThePendric Sep 10, 2026
fbc43df
feat(ou-tree): add placeholder component with tests and i18n
HendrikThePendric Sep 10, 2026
d0fcdb6
chore(storybook): load typescript stories
HendrikThePendric Sep 10, 2026
e55914b
chore(ou-tree): emit type declarations to build/types
HendrikThePendric Sep 10, 2026
653b25a
fix(storybook): resolve .js specifiers to .ts/.tsx sources in webpack
HendrikThePendric Sep 11, 2026
62fea71
docs(ou-tree): add package-scoped claude instructions
HendrikThePendric Sep 11, 2026
347fa6e
fix(ou-tree): move .js->.ts jest resolver mapping to shared jest configs
HendrikThePendric Sep 11, 2026
8b8d6a1
fix: apply final review fixes for ou-tree typescript groundwork
HendrikThePendric Sep 11, 2026
8cc15be
fix(docs): repair docusaurus build config and stale sidebar entry
HendrikThePendric Sep 11, 2026
0dbc33a
refactor(ou-tree): drop .js import extensions in favour of extensionless
HendrikThePendric Sep 11, 2026
8e1811d
chore: ignore generated build output in prettier checks
HendrikThePendric Sep 11, 2026
217ac06
feat(docs): generate api documentation for typescript components
HendrikThePendric Sep 11, 2026
306fb14
feat(ci): type-check typescript packages
HendrikThePendric Sep 11, 2026
18b7eae
docs(ou-tree): add package readme
HendrikThePendric Sep 11, 2026
e5343b1
chore: stop ignoring generated api docs that are actually committed
HendrikThePendric Sep 11, 2026
3aa5507
fix(ci): fail the build on library errors, not on docs errors
HendrikThePendric Sep 11, 2026
541620c
chore: address sonar findings and trim explanatory comments
HendrikThePendric Sep 11, 2026
5798896
refactor: hoist shared typescript config and ambient types to the rep…
HendrikThePendric Sep 11, 2026
b1f13e0
revert: leave the docusaurus config alone
HendrikThePendric Sep 11, 2026
799a514
fix(storybook): correct entry-point glob and explain the resolve.exte…
HendrikThePendric Sep 11, 2026
78bc4de
refactor: drop the jest-dom ambient declaration and tighten comments
HendrikThePendric Sep 11, 2026
c3556c4
fix(lint): keep base rules enabled for typescript and align file globs
HendrikThePendric Sep 11, 2026
bcd2290
refactor: make type-checking global, like linting
HendrikThePendric Sep 14, 2026
0939a97
refactor: emit declarations from a script instead of a config per pac…
HendrikThePendric Sep 14, 2026
bc0ea13
refactor: trim build-types script to what it needs
HendrikThePendric Sep 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 39 additions & 10 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ const config = {
},
overrides: [
{
files: ['*.stories.js', '*.stories.e2e.js', '**/__stories__/*.js'],
files: [
'*.stories.{js,jsx,ts,tsx}',
'*.stories.e2e.{js,jsx,ts,tsx}',
'**/__stories__/*.{js,jsx,ts,tsx}',
],
rules: {
'import/no-extraneous-dependencies': 'off',
'react/display-name': 'off',
Expand All @@ -22,23 +26,48 @@ const config = {
},
{
files: [
'components/*/src/**/*.js',
'collections/*/src/**/*.js',
'utilities/*/src/**/*.js',
'components/*/src/**/*.{js,jsx,ts,tsx}',
'collections/*/src/**/*.{js,jsx,ts,tsx}',
'utilities/*/src/**/*.{js,jsx,ts,tsx}',
],
excludedFiles: [
'**/features/**/*.js',
'**/__tests__/**/*.js',
'*.test.js',
'*.stories*.js',
'**/__stories__/*.js',
'**/__stories__/**/*.js',
'**/features/**/*.{js,jsx,ts,tsx}',
'**/__tests__/**/*.{js,jsx,ts,tsx}',
'*.test.{js,jsx,ts,tsx}',
'*.stories*.{js,jsx,ts,tsx}',
'**/__stories__/*.{js,jsx,ts,tsx}',
'**/__stories__/**/*.{js,jsx,ts,tsx}',
'*.d.ts',
],
rules: {
'import/no-extraneous-dependencies': 'error',
},
},
{
files: ['**/*.{ts,tsx}'],
parser: require.resolve('@typescript-eslint/parser'),
// TypeScript ambient namespaces, which `no-undef` does not know.
globals: { JSX: 'readonly', NodeJS: 'readonly' },
settings: {
// Lets `import/no-unresolved` follow an extensionless relative
// import to a `.ts`/`.tsx` file.
'import/resolver': {
node: { extensions: ['.js', '.jsx', '.ts', '.tsx'] },
},
},
rules: {
/*
* TypeScript sources import siblings without an extension
* (`./ou-tree` resolves to `ou-tree.tsx`), the inverse of the
* `ignorePackages` rule the JavaScript packages follow.
*/
'import/extensions': [
'error',
'never',
{ ts: 'never', tsx: 'never' },
],
},
},
],
}

Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/dhis2-verify-lib.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ jobs:
- name: Lint
run: yarn lint

# Babel strips TypeScript types without checking them, so no
# other job verifies them.
- name: Typecheck
run: yarn typecheck

test:
runs-on: ubuntu-latest
needs: [build]
Expand Down
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,3 @@ icons/src/react/

.eslintcache

# Documentation files that are generated on CI
API.md
6 changes: 6 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ cypress/assets/*.js

# Documentation files that are generated on CI
API.md

# Build output, generated locally by `yarn build` (docusaurus site into dist/,
# its build cache into docs/.docusaurus/). Without these, `yarn build` followed
# by `yarn lint` fails on generated files nobody wrote.
dist/
**/.docusaurus/
1 change: 1 addition & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- [@dhis2-ui/node](components/node/API.md)
- [@dhis2-ui/notice-box](components/notice-box/API.md)
- [@dhis2-ui/organisation-unit-tree](components/organisation-unit-tree/API.md)
- [@dhis2-ui/ou-tree](components/ou-tree/API.md)
- [@dhis2-ui/pagination](components/pagination/API.md)
- [@dhis2-ui/popover](components/popover/API.md)
- [@dhis2-ui/popper](components/popper/API.md)
Expand Down
18 changes: 18 additions & 0 deletions components/ou-tree/API.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
### OuTree

#### Usage

To use `OuTree`, you can import the component from the `@dhis2/ui` library


```js
import { OuTree } from '@dhis2/ui'
```


#### Props

|Name|Type|Default|Required|Description|
|---|---|---|---|---|
|className|string||||
|dataTest|string|``'dhis2-uicore-outree'``|||
60 changes: 60 additions & 0 deletions components/ou-tree/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# @dhis2-ui/ou-tree

This package is TypeScript. **The rest of this repository is JavaScript** — do not
apply anything here to the other component packages.

## Conventions

- **Relative imports carry NO extension**, the inverse of the JavaScript packages:
`import { OuTree } from './ou-tree'` resolves to `ou-tree.tsx`. Enforced by
`import/extensions: ['error', 'never', …]` in the root `.eslintrc.js`, scoped to
`.ts`/`.tsx`; the JavaScript packages keep their `ignorePackages` rule.

Writing `./ou-tree.js` instead would name the compiled output rather than a file
on disk. TypeScript resolves that, but eslint-plugin-import, Jest and webpack do
not. Consequence to know about: `build/es` therefore carries extensionless
specifiers, which Node's _native_ ESM loader rejects — fine for apps that bundle,
and `exports.require` routes Node to the CJS build.

- **Type-only re-exports must use `export type`.** `isolatedModules` is on because
Babel compiles file-by-file; a plain `export { SomeType }` compiles to a runtime
import of a value that does not exist.
- **`src/index.ts` is the module boundary.** Import from a subcomponent's
`index.ts`, never reach past it into its internals.
- **Styles are styled-jsx** (`<style jsx>`), not CSS modules. It only type-checks
because of `typings/styled-jsx.d.ts` at the repo root.
- **Test files import `@testing-library/jest-dom` directly**, for the types behind
`toBeInTheDocument` and friends. The repo's Jest setup already loads it at
runtime, but that setup file is not part of the TypeScript program.
- **One shared setting exists for this package — do not "clean it up".** The
`**/*.{ts,tsx}` override in the root `.eslintrc.js` points
eslint-plugin-import's node resolver at `['.js', '.jsx', '.ts', '.tsx']`.
Without it `import/no-unresolved` cannot follow an extensionless import to a
`.tsx` file and reports every relative import as unresolved. Jest and webpack
need no equivalent — both already resolve `.ts`/`.tsx` by default.
- **This package has no TypeScript config of its own.** Checking is global
(root `tsconfig.json`, `yarn typecheck`). Declaration emit is per package,
because each package publishes its own types, and
`scripts/build-types.js` does it — reading the compiler options from the
root config so they cannot drift from the ones used to check.
- **Combine className values with `cx` from `classnames`**, not template literals.
- **Colors, spacers and elevations come from `@dhis2/ui-constants`**, not literals.

## Commands

- `yarn typecheck` from the repo root — type-checking is global, like linting: one
run covers every package. Babel strips types without checking them, so a green
test run says nothing about type correctness. CI runs this in the lint job.
- `yarn workspace @dhis2-ui/ou-tree test` — Jest.
- `yarn workspace @dhis2-ui/ou-tree build` — Babel build plus declaration emit to
`build/types/`.
- `yarn workspace @dhis2-ui/ou-tree d2-app-scripts i18n extract` — run after adding
or changing any `i18n.t(...)` string, and commit the updated `i18n/en.pot`.
- From the repo root: `yarn setup` once, then `yarn start` for Storybook.

## Status

The package is `"private": true` and is deliberately **not** exported from
`@dhis2/ui`. It is a placeholder that will replace
`@dhis2-ui/organisation-unit-tree`. Making it public is a separate, deliberate
change — do not add it to `collections/ui`.
15 changes: 15 additions & 0 deletions components/ou-tree/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
> :warning:
> This is currently considered internal, please use `@dhis2/ui`.
>
> See the [Getting started
> guide](https://github.com/dhis2/ui/blob/master/docs/getting-started.md)
> for more information.

A placeholder for the eventual replacement of
[`@dhis2-ui/organisation-unit-tree`](../organisation-unit-tree). It is not
published and is not exported from `@dhis2/ui` yet, so there is no
documentation page for it — both components are intended to live side by side
while the old one is phased out.

See [API.md](./API.md) for the current props, and
[CLAUDE.md](./CLAUDE.md) for the conventions that apply inside this package.
6 changes: 6 additions & 0 deletions components/ou-tree/d2.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
type: 'lib',
entryPoints: {
lib: 'src/index.ts',
},
}
12 changes: 12 additions & 0 deletions components/ou-tree/i18n/en.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
msgid ""
msgstr ""
"Project-Id-Version: i18next-conv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2026-09-10T16:28:42.024Z\n"
"PO-Revision-Date: 2026-09-10T16:28:42.024Z\n"

msgid "ouTree - coming soon"
msgstr "ouTree - coming soon"
47 changes: 47 additions & 0 deletions components/ou-tree/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"name": "@dhis2-ui/ou-tree",
"version": "10.17.0",
"description": "UI OuTree",
"repository": {
"type": "git",
"url": "https://github.com/dhis2/ui.git",
"directory": "components/ou-tree"
},
"homepage": "https://github.com/dhis2/ui#readme",
"license": "BSD-3-Clause",
"private": true,
"main": "./build/cjs/index.js",
"module": "./build/es/index.js",
"types": "./build/types/index.d.ts",
"exports": {
"types": "./build/types/index.d.ts",
"import": "./build/es/index.js",
"require": "./build/cjs/index.js"
},
"sideEffects": false,
"scripts": {
"start": "storybook dev -c ../../storybook/config --port 5000",
"build": "d2-app-scripts build && node ../../scripts/build-types.js",
"test": "d2-app-scripts test --jestConfig ../../jest.config.shared.js"
},
"peerDependencies": {
"@dhis2/d2-i18n": "^1",
"react": "^16.13 || ^18",
"react-dom": "^16.13 || ^18",
"styled-jsx": "^4"
},
"dependencies": {
"@dhis2/ui-constants": "10.17.0",
"classnames": "^2.3.1"
},
"devDependencies": {
"@dhis2/d2-i18n": "^1.1.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"styled-jsx": "^4.0.1"
},
"files": [
"build",
"src"
]
}
2 changes: 2 additions & 0 deletions components/ou-tree/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { OuTree } from './ou-tree/index'
export type { OuTreeProps } from './ou-tree/ou-tree'
22 changes: 22 additions & 0 deletions components/ou-tree/src/ou-tree.prod.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react'
import { OuTree } from './ou-tree/index'

const description = `
A replacement for \`OrganisationUnitTree\`, currently a placeholder.

\`\`\`js
import { OuTree } from '@dhis2-ui/ou-tree'
\`\`\`
`

export default {
title: 'OuTree',
component: OuTree,
parameters: {
componentSubtitle:
'Placeholder for the organisation unit tree replacement.',
docs: { description: { component: description } },
},
}

export const Default = () => <OuTree />
1 change: 1 addition & 0 deletions components/ou-tree/src/ou-tree/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { OuTree } from './ou-tree'
20 changes: 20 additions & 0 deletions components/ou-tree/src/ou-tree/ou-tree.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import '@testing-library/jest-dom'
import { render, screen } from '@testing-library/react'
import React from 'react'
import { OuTree } from './ou-tree'

describe('OuTree', () => {
it('renders the placeholder heading', () => {
render(<OuTree />)

expect(
screen.getByRole('heading', { name: 'ouTree - coming soon' })
).toBeInTheDocument()
})

it('applies a custom data-test attribute', () => {
render(<OuTree dataTest="custom-ou-tree" />)

expect(screen.getByTestId('custom-ou-tree')).toBeInTheDocument()
})
})
33 changes: 33 additions & 0 deletions components/ou-tree/src/ou-tree/ou-tree.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import i18n from '@dhis2/d2-i18n'
import { colors } from '@dhis2/ui-constants'
import cx from 'classnames'
import React from 'react'

export interface OuTreeProps {
className?: string
dataTest?: string
}

export const OuTree = ({
className,
dataTest = 'dhis2-uicore-outree',
}: OuTreeProps) => (
<div className={cx('container', className)} data-test={dataTest}>
<h1>{i18n.t('ouTree - coming soon')}</h1>
<style jsx>
{`
.container {
padding: 16px;
border: 1px dashed ${colors.grey400};
border-radius: 3px;
}
h1 {
margin: 0;
color: ${colors.grey700};
font-size: 16px;
font-weight: 400;
}
`}
</style>
</div>
)
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"format": "d2-style apply",
"format:staged": "d2-style apply --staged",
"lint": "d2-style check",
"typecheck": "tsc --noEmit",
"lint:staged": "d2-style check --staged",
"test": "d2-app-scripts test",
"start": "yarn workspace ui-storybook start",
Expand Down Expand Up @@ -70,7 +71,9 @@
"@testing-library/react": "^16.0.1",
"@testing-library/react-hooks": "^7.0.1",
"@testing-library/user-event": "^14.5.2",
"@types/jest": "^27.5.2",
"@types/react": "^18",
"@typescript-eslint/parser": "^7.18.0",
"ast-types": "^0.14.2",
"concurrently": "^6.2.1",
"cypress": "^13.8.0",
Expand All @@ -81,6 +84,7 @@
"react-dev-utils": "^10.2.1",
"react-docgen": "^5.4.0",
"rimraf": "^3.0.2",
"typescript": "~5.5.4",
"wait-on": "^6.0.0"
},
"overrides": {
Expand Down
Loading
Loading