diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 68c173baf..400c2d2b0 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,10 +1,13 @@ -## Requirements -- [ ] This PR has a title that briefly describes the work done including the ticket number. If there is a ticket, make sure your PR title includes a [conventional commit](https://o3-dev.docs.openmrs.org/#/getting_started/contributing?id=your-pr-title-should-indicate-the-type-of-change-it-is) label. See existing PR titles for inspiration. +# Requirements -#### For changes to apps -- [ ] My work conforms to the [**OpenMRS 3.0 Styleguide**](https://om.rs/styleguide) and **design documentation**. +- [ ] This PR has a title that briefly describes the work done including the ticket number. Ensure your PR title includes a [conventional commit](https://o3-docs.openmrs.org/docs/frontend-modules/contributing#contributing-guidelines) label (such as `feat`, `fix`, or `chore`, among others). See existing PR titles for inspiration. + +## For changes to apps + +- [ ] My work conforms to the [**O3 Styleguide**](https://om.rs/styleguide) and [**design documentation**](https://om.rs/o3ui). + +## If applicable -#### If applicable - [ ] My work includes tests or is validated by existing tests. - [ ] I have updated the [esm-framework mock](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/mock.tsx) to reflect any API changes I have made. diff --git a/README.md b/README.md index 09539cd0b..1f34a76cd 100644 --- a/README.md +++ b/README.md @@ -158,8 +158,7 @@ yarn link path/to/openmrs-esm-core/packages/framework/esm-api ``` This satisfies the build tooling, but we must do one more step to get the frontend -to load these dependencies at runtime -(see docs on [Runtime Dependencies](https://o3-dev.docs.openmrs.org/#/main/deps)). +to load these dependencies at runtime. Here, there are two options: @@ -175,9 +174,6 @@ Then run your patient chart dev server as usual, with `yarn start`. #### Method 2: Using import map overrides -Read the [dev documentation](https://o3-dev.docs.openmrs.org/#/getting_started/setup?id=import-map-overrides) -about import map overrides if you have not already. - In `esm-core`, start the app shell with `yarn run:shell`. Then, in the patient chart repository, `cd` into whatever packages you are working on and run `yarn serve` from there. Then use the import map override tool in the browser to tell the frontend to load your local patient chart packages. #### Once it's working diff --git a/docs/.nojekyll b/docs/.nojekyll deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/CNAME b/docs/CNAME deleted file mode 100644 index a47300bba..000000000 --- a/docs/CNAME +++ /dev/null @@ -1 +0,0 @@ -o3-dev.docs.openmrs.org \ No newline at end of file diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index f113d1aa4..000000000 --- a/docs/README.md +++ /dev/null @@ -1,95 +0,0 @@ -# OpenMRS Frontend 3.0 for Developers - - - -:link: **Link to this guide: [om.rs/dev3docs](https://om.rs/dev3docs)** :link: - -This documentation is intended to enable developers to develop and deploy -custom UI features for OpenMRS. - -Visit the -[Wiki page](https://wiki.openmrs.org/display/projects/OpenMRS+3.0%3A+A+Frontend+Framework+that+enables+collaboration+and+better+User+Experience) -if you haven't already. It provides a high-level -introduction to the project and all of the relevant links. - -You should first read the -[Implementer Documentation](https://wiki.openmrs.org/pages/viewpage.action?pageId=224527013), -which provides some conceptual orientation as well as instructions for deployment -and configuring. - -Once you've read that, click [Prerequisite knowledge](getting_started/prerequisites.md) -here or in the left sidebar to begin, or check out some videos below. - -> Note: This documentation tends to assume that the developer is using React, -but this is not a requirement of OpenMRS Frontend 3.0. Indeed, the entire purpose -of choosing a microfrontends-based architecture was to allow collaboration between -different teams using different technologies. If you are developing -frontend modules in a technology other than React, please tell us so in the -[#openmrs3](https://openmrs.slack.com/archives/CHP5QAE5R) channel on Slack. -We'd love to work with you to make the development experience as smooth as possible, -and take the opportunity to expand OpenMRS Frontend 3.0 support for different -frameworks. - -## Videos 🎥 - -These videos are available to show how you can develop new frontend functionalities using the OpenMRS 3.0 setup. - -### Talks - -These are videos which have been recorded for some talks and conference sessions. - -- [Why Microfrontends?](https://youtu.be/XDIIuM7Ffas) - -### Spotlight Videos - -These are videos below 5 minutes showing small dedicated things. - -- [Tooling](https://youtu.be/KDC8DwPWwjc) -- [Breadcrumbs](https://youtu.be/Rq4QGSF9r2M) -- [Extensions Introduction](https://youtu.be/crdEL91oBGs) -- [Internationalization](https://youtu.be/1pLUi47BIBo) -- [How to see UX & component guidance in Zeplin designs](https://www.youtube.com/watch?v=SjluEGDH4LU&feature=youtu.be&ab_channel=OpenMRS) - -### Tutorials - -These are more extensive videos with focus on showing how to develop new frontend modules. - -- [Part 1: OpenMRS SPA Extensions Tutorial: About our Frontend Module Architecture & How to Use Extensions](https://iu.mediaspace.kaltura.com/media/t/1_e7kvnx9t?st=702) -- [Part 2: OpenMRS SPA Extensions Workshop: Practical Session on our MFE Architecture & How to Use Extensions](https://iu.mediaspace.kaltura.com/media/t/1_iaq63mfd?st=282) - - [OMRS SPA Workshop practice tasks](https://github.com/openmrs/openmrs-esm-testresults/tree/feature/workshop) - - [OMRS SPA Workshop practice solutions](https://github.com/openmrs/openmrs-esm-testresults/tree/feature/workshop-solutions) - - -### Others - -- [Development Overview](https://youtu.be/aIi1t5o7agI) -- [Patient Banner Implementation](https://youtu.be/3AoxdCjXbys) -- [Attachments Demo](https://youtu.be/Vm6sWV55nBQ) -- [Registration Configuration](https://youtu.be/PA9IiNgHAq8) diff --git a/docs/_sidebar.md b/docs/_sidebar.md deleted file mode 100644 index 629bdb954..000000000 --- a/docs/_sidebar.md +++ /dev/null @@ -1,36 +0,0 @@ - - -- Getting started - - [Prerequisite Knowledge](/getting_started/prerequisites.md) - - [Setup](/getting_started/setup.md) - - [Contributing](/getting_started/contributing.md) - - [Release and Deployment](/getting_started/release_and_deployment.md) - - [Tour of a Frontend Module](/getting_started/tour.md) -- Main concepts - - [Map of the Project](/main/map.md) - - [Creating a Frontend Module](/main/creating_a_microfrontend.md) - - [Design Conventions](/main/carbon.md) - - [Dependencies](/main/deps.md) - - [Retrieving and Posting Data](/main/data.md) - - [Dates](/main/dates.md) - - [Extension System](/main/extensions.md) - - [Configuration System](/main/config.md) - - [Sharing State](/main/state.md) - - [Translations](/main/translations.md) - - [Breadcrumbs](/main/breadcrumbs.md) - - [Creating a Distribution](/main/distribution.md) - - [Upgrade 3 to 4](/main/upgrade_3_to_4.md) - - [FAQ](/main/faq.md) - - [Framework API Reference](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md) -- Offline - - [Offline Mode](/offline/offline.md) - - [Dynamic Offline Data](/offline/dynamic_offline_data.md) -- Under the hood - - [Architecture Overview](/under_the_hood/architecture.md) - - [Tooling](/under_the_hood/tooling.md) - - [Translations](/under_the_hood/translations.md) - - [Extensions System](/under_the_hood/extensions.md) - - [Squad DevOps and CI](/under_the_hood/squad_devops.md) - - [Migration Guide](/under_the_hood/migration_guide.md) - diff --git a/docs/getting_started/contributing.md b/docs/getting_started/contributing.md deleted file mode 100644 index 78cc8c03e..000000000 --- a/docs/getting_started/contributing.md +++ /dev/null @@ -1,113 +0,0 @@ -# Contributing - -You can build the UI features that you need for your implementation. -However, it can be more fun and fruitful to collaborate on frontend -modules that can be useful to organizations around the world. - -## Getting Connected - -If you're interested in contributing, you'll need to take the following steps. - -- Get access to [OpenMRS Jira](https://issues.openmrs.org). - If you don't have access, raise a request at the - [Help Desk](https://wiki.openmrs.org/display/~helpdesk). - Wait at least 24 hours for the request to get approved; you will receive an - email upon approval. Once approved, take a look at all the - [frontend module-related issues](https://issues.openmrs.org/projects/MF/issues). - Good first issues are filed under the - [intro](https://issues.openmrs.org/browse/MF-508?jql=project%20%3D%20MF%20AND%20resolution%20%3D%20Unresolved%20AND%20labels%20%3D%22intro%22%20ORDER%20BY%20priority%20DESC%2C%20updated%20DESC) - label. -- Join our [Slack Channel](https://openmrs.slack.com/archives/CHP5QAE5R) and introduce yourself. -You will first need to [create an OpenMRS Slack account](https://slack.openmrs.org/) if you don't have one. - -## Guidelines for Contributing - -#### Pull requests (PRs) are not required to correspond to a ticket. - -If there is a ticket for the PR, it should be clearly named and linked in the PR. - -#### Push additional commits. - -Pull requests are allowed (and encouraged) to contain more than one commit. -Those commits are squashed when merged into master/main. - -#### Do not close a PR and recreate a new one with the same code. - -If you have opened a PR for some work and a reviewer has requested some changes, -do not open a new PR with the updates and close the old one. This makes things -very hard for reviewers. - -#### Read and fill out the PR template. - -That's all. - -#### Work incrementally. - -Smaller PRs are easier to read and validate. Opening multiple PRs for the same ticket -is excellent if those PRs represent different pieces or stages of work relating -to that ticket. - -#### Do not increase the scope of a PR after it has been reviewed. - -If your code has been reviewed, don't push a bunch more code that isn't related to -the review. Fixups are fine, but new features should have their own PRs. -Remember—small PRs are easier to review anyway! - -#### Your first PR to a codebase should be small. - -Get a feel for what the reviewer expects before submitting a PR with a large amount of code in it. - -#### Your PR title should indicate the type of change it is. - -We use PR titles to determine version bumps. PR titles should start with something -in parentheses, or the word `BREAKING`. Examples of good PR titles: - -- "(docs) Add to contributing docs" -- "(fix) Console error when visiting allergies page" -- "(feat) Add search bar to medications widget" -- "(refactor) Tidy the dashboard implementation" -- "BREAKING: New left nav system" - -Common prefixes include the above, and `chore`, `style`, `perf`, and `test`. - -The versioning bump to indicate is based on the -[OpenMRS Frontend 3.0 versioning conventions](https://github.com/openmrs/openmrs-rfc-frontend/blob/master/text/0022-versions.md). -*Don't worry if you aren't sure which one you should use!* Your reviewer should make -sure your PR has an appropriate one. - -## Guidelines for Reviewing - -- Encourage feedback as both an author and reviewer. -- Encourage as many contributors to engage in deep and meaningful contribution. -- Be sensitive to the varying experience levels of OpenMRS contributors. -- Avoid gatekeeping, which includes over-emphasizing to contributors how they're failing to follow the correct process. -- Encourage code ownership, including reviewing changes to the code and projects you've contributed to. -- Provide detailed and helpful feedback to contributors, including using GitHub's - [suggestion feature](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/incorporating-feedback-in-your-pull-request) - to help the contributor know how to make the needed changes. - ---- - -*The above guidelines were established in [RFC-20](https://github.com/openmrs/openmrs-rfc-frontend/blob/master/text/0020-contributing-guidelines.md).* - - -## Code Conventions - -### Filenames - -Above all, maintain consistency with the repository you are working in. - -Our general conventions are these: - -- The React component `FooBar` should be called `foo-bar.tsx` or `foo-bar.component.tsx`. -- The style file for `FooBar` should be called `foo-bar.[s]css` or `foo-bar.style.[s]css`. -- `FooBar` should not handle API calls itself, rather it should use functions that fetch - and clean the data as needed. Those functions should be in a file called `foo-bar.resource.ts`. -- The tests for `FooBar` should be in a file called `foo-bar.test.tsx`. -- Avoid calling things "utils." It is a meaningless name. Group the functions by what - they are for. If the function is only used in one place, just put it in the file - where it is used. - -### Naming things - -Use the [Naming Cheatsheet](https://github.com/kettanaito/naming-cheatsheet#readme). diff --git a/docs/getting_started/prerequisites.md b/docs/getting_started/prerequisites.md deleted file mode 100644 index 8706f03df..000000000 --- a/docs/getting_started/prerequisites.md +++ /dev/null @@ -1,50 +0,0 @@ -# Prerequisites - -If you haven't already done so: Please first see [the Implementer Documentation](https://wiki.openmrs.org/x/pQJiDQ), which provides some conceptual orientation as well as instructions for deployment and configuring. - -Before developing OpenMRS Frontend 3.0, you should be familiar with the following technologies. - -## Javascript - -Knowing Javascript is prerequisite to everything else. Consider -[Introduction to Javascript](https://www.codecademy.com/learn/introduction-to-javascript) and [The Modern JavaScript Tutorial](https://javascript.info/). - -## JavaScript Tooling - -You'll want to understand the basics of [npm](https://docs.npmjs.com/), -including the command-line interface (run `npm help`), [package.json](https://docs.npmjs.com/cli/v7/configuring-npm/package-json), [`npx`](https://docs.npmjs.com/cli/v7/commands/npx), and know what [node](https://www.w3schools.com/nodejs/) is. - -If the repository you're looking at has a `yarn.lock` file, then it uses `yarn`, -and you'll want to read the [Yarn docs](https://classic.yarnpkg.com/en/docs/getting-started). -Otherwise it uses `npm`. Yarn and npm are exclusive of each other within a repository. - -## React - -You should know everything from [React Main Concepts](https://reactjs.org/docs/hello-world.html), -and sections 1-5 of [React Hooks](https://reactjs.org/docs/hooks-intro.html). - -If you're new to React, use a tutorial or interactive learner's guide. -The [Learn React Docs](https://beta.reactjs.org/learn) is an excellent resource. -You can also try the -[React Tutorial](https://reactjs.org/tutorial/tutorial.html) -or the video-based [React JS Crash Course 2021](https://www.youtube.com/watch?v=w7ejDZ8SWv8). - -Take your time. This is a lot of material. The better you understand it, the more -easily you will be able to develop high-quality frontend modules (and other React applications). - -### React Hooks -As mentioned above, you should definitely review sections 1-5 of [React Hooks](https://reactjs.org/docs/hooks-intro.html). Still looking for more videos? Some of our favorite videos are from when hooks first came out in 2018 - 2019. :clapper:[90% Cleaner React with Hooks](https://www.youtube.com/watch?v=wXLf18DsV-I), and -:clapper:[React Today and Tomorrow](https://youtu.be/dpw9EHDh2bM?t=1047). These do a good job of showing the difference before and after hooks. But before watching, you should have some experience with JavaScript and React in general to appreciate just how cool hooks are. :smile: - -## TypeScript - -You can get started with Typescript pretty quickly. If you're new to it, -please read [TypeScript in 5 Minutes](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html). - -## Git - -It is imperative that you keep your code in a version control system. OpenMRS uses -[Git](https://git-scm.com/). You should know the basics of using Git and GitHub. -The website [Learn Git Branching](https://learngitbranching.js.org/) and the video -[Git and GitHub for Beginners](https://www.youtube.com/watch?v=RGOj5yH7evk) -are excellent resources. diff --git a/docs/getting_started/release_and_deployment.md b/docs/getting_started/release_and_deployment.md deleted file mode 100644 index 7380fd3e4..000000000 --- a/docs/getting_started/release_and_deployment.md +++ /dev/null @@ -1,23 +0,0 @@ -# Continuous Integration and Releases - - - -Distributions use frontend modules which are published as packages in NPM. -Those distributions can refer to frontend module versions by number (e.g. `3.1.0`) -or by tag (e.g. `latest`). - -GitHub Actions is used to publish frontend modules from the `main` branch -to the `next` tag. -You can see the published versions of each frontend module on npmjs.com. See for example -[@openmrs/esm-login-app](https://www.npmjs.com/package/@openmrs/esm-login-app?activeTab=versions). -The [OpenMRS CI server](https://dev3.openmrs.org/openmrs/spa/login) -always shows the `next` version of all frontend modules (i.e., the latest from the -`main` branch). - -You can version a frontend module by creating a release in GitHub. This will trigger -GitHub Actions to publish a new version of the package with the `latest` tag. -Write release notes that explain what's happened since the last release. See -[an example](https://github.com/openmrs/openmrs-esm-core/releases/tag/v3.1.2). - -## Subscribe to Community Release Announcements -To follow the latest O3 release announcements, join [OpenMRS Slack](https://slack.openmrs.org) and subscribe to the channel [#openmrs3-releaseannouncements](https://openmrs.slack.com/archives/C052500RS0L). diff --git a/docs/getting_started/setup.md b/docs/getting_started/setup.md deleted file mode 100644 index 6fb4dd43a..000000000 --- a/docs/getting_started/setup.md +++ /dev/null @@ -1,130 +0,0 @@ -# Setup - - -## Prerequisities - -:bangbang: This setup guide is for individuals who want to develop on the O3 Framework. **You will need to set up an O3 instance first.** *If you do not already have an O3 Instance set up, please **first** follow the guidance at: https://om.rs/o3setup.* - -You must have git, node, npm, and yarn installed. The versions required are -- The Node [Active LTS version](https://github.com/nodejs/release#release-schedule) -- The latest stable version of NPM -- The latest stable version of Yarn - -See [prerequisites](./prerequisites.md) if any of these technologies -are unfamiliar to you. -Consider using [nvm](https://github.com/nvm-sh/nvm#node-version-manager---) -to manage your node version. - -Note that as a frontend project, we use Node for compiling and bundling frontend code, -not for running the backend or server. - -## Working on a frontend module - -Clone the repository of interest. For this example we'll use -:sparkles:**[openmrs-esm-template-app](https://github.com/openmrs/openmrs-esm-template-app)**:sparkles:. - -> Note that this repository is a good starting point for creating a new -frontend module, and also contains lots of information explaining the -different pieces of frontend modules, along with the -[Tour of a Frontend Module](./tour.md). - -```bash -git clone https://github.com/openmrs/openmrs-esm-template-app.git -``` - -Then change to the cloned repository's directory and install its dependencies. - -```bash -cd openmrs-esm-template-app -yarn -``` - -Then you're ready to start a dev server! The command to run will depend on -the repository you checked out. Read the README for that repository to find -out what command you should run. In the case of `openmrs-esm-template-app`, -use `yarn start` to start the server. - -```bash -yarn start -``` - -This command will run a "script" from the `package.json` -file. Take a look at the `scripts` section of that file to find out what -the command actually does. - -Once the frontend server starts up, you should be able to access the template -app at http://localhost:8080/openmrs/spa/hello. -You might be redirected to the log in page to log in first. - -> :bulb: You run almost any command with `--help` to learn more about it. -> This includes `yarn start --help`. - -### Troubleshooting - -If you're running Linux, you may see the following error the first time you run -a a dev server: `Error: ENOSPC: System limit for number of file watchers reached`. -If that happens, you need to increase the system limit for the number of file -watchers. See -[this StackOverflow answer](https://stackoverflow.com/a/55763478/1464495). - -## Import map overrides - -If you'd like to work on multiple frontend modules that aren't in a monorepo together, -or if you'd like to run a frontend module you are developing locally on a -deployed server somewhere, you can use import map overrides, -which is made available through the OpenMRS DevTools. - -> If you'd like to understand how Import Map Overrides works, check out - the [documentation](https://github.com/joeldenning/import-map-overrides). - If you'd just like to use it, continue reading here. - -To enable the OpenMRS DevTools, open your browser's JavaScript console and execute - -```javascript -localStorage.setItem('openmrs:devtools', true) -``` - -After refreshing the page, a little box should appear in the lower-right hand corner of the page. -Clicking this box opens the OpenMRS DevTools. - -In the frontend module you want to develop, run - -```bash -# if the OpenMRS frontend you're looking at uses HTTP (e.g. a local server) -yarn serve -# if the OpenMRS frontend you're looking at uses HTTPS (e.g. dev3.openmrs.org) -yarn serve --https -``` - -> Substitute `npm run serve` if the project uses NPM, or `narn serve` if you use - [narn](https://github.com/joeldenning/narn). - -The protocol of the application must match the protocol of the locally-served frontend module. - -This command will serve the frontend module and tell you the port where it is serving, -as well as showing you the filenames that are being served. You can then use -the import map overrides panel to override the existing import map -entry, or add your frontend module as a new entry. - -For example, if you run `yarn serve` in -[esm-login-app](https://github.com/openmrs/openmrs-esm-core/tree/main/packages/apps/esm-login-app), -and if it says something like `Project is running at http://localhost:8080/`, -as well as something like `asset openmrs-esm-login-app.js`, then in the import -map overrides you can click on the entry for `openmrs-esm-login-app` and give it the value - -``` -//localhost:8081/openmrs-esm-login-app.js -``` - -You can also simply type `8081` and Import Map Overrides will infer the URL above. - -> **Note**: The name of the entrypoint file (such as `openmrs-esm-login-app.js`) is set - by the `browser` parameter of the `package.json`. - -### Note on using HTTPS - -If you're using import map overrides on a server that uses HTTPS, you must use `serve` with the -`--https` flag. Your browser will probably complain about the certificate. -You will need to convince it -that there is no security risk. In Chrome, enabling the "allow insecure localhost" flag -(chrome://flags/#allow-insecure-localhost) can help. diff --git a/docs/getting_started/tour.md b/docs/getting_started/tour.md deleted file mode 100644 index 1f1a5beda..000000000 --- a/docs/getting_started/tour.md +++ /dev/null @@ -1,57 +0,0 @@ -# Tour of a Frontend Module - -Let's explore the contents of -[openmrs-esm-template-app](https://github.com/openmrs/openmrs-esm-template-app). - -## Tooling 🧰 - -OpenMRS uses [SWC](https://swc.rs/) as it's transpiler. This means that it is responsible for translating the code we write into Javascript the browser understands. E.g., we can use TypeScript, JSC, and ES2020, even though most browsers don't support those features. - -There are a number of configuration files at the project level. These -can generally be treated as boilerplate. The important ones are - -- `.github/workflows`: Configures [GitHub Actions](https://docs.github.com/en/actions). - See the [Actions panel](https://github.com/openmrs/openmrs-esm-template-app/actions). -- `.husky`: Husky runs validations when committing. -- `.eslintrc`: ESLint is a linter for ECMAScript, a.k.a. JavaScript -- `jest.config.js`: Jest is the test runner. In this file you'll see: - - Jest is configured to use SWC (via @swc/jest) to transform code so that Jest, like the - browser, can understand it. The contents of `node_modules` are not transformed, - except the `@openmrs` packages, so that tests can make use of the generic mock - for `@openmrs/esm-framework`. - - The `moduleNameMapper` entry transforms `import` - statements in the code, whether to mock them or make them understandable to Jest. - Default mocks are provided for everything in `@openmrs/esm-framework`. Note that many - of these mocked functions have no implementation or return `undefined`, which may - not work for your tests—you may have to override their implementations. -- `prettier.config.js`: Prettier is an auto-formatter. This ensures that you never have - to worry about correct indentation, optional punctuation, or line breaks. Configuration - is optional. -- `tsconfig.json`: This is the TypeScript configuration. In general, it should not - vary much between projects. -- `webpack.config.js`: Webpack is what builds all the code (using SWC) into - a "bundle" in the `dist/` directory which can be read by the browser. This single - line is usually all you'll need in the config file. If you need to add something, - you can simply override the properties of that default object. - -## The package 📂 - -`package.json`, as you should be [aware](./prerequisites.md), defines dependencies and -metadata for the frontend module (which is a -[package](https://docs.npmjs.com/about-packages-and-modules)). - -Looking inside, we find a bunch of metadata. Most of it can be understood with reference -to the [`package.json` docs](https://docs.npmjs.com/cli/v7/configuring-npm/package-json). -- The `name` of our frontend module ends in `-app` so that it will be recognized as a frontend module - by the app shell and the build tooling. -- The `browser` entry is the entrypoint of the bundle. -- The `main` entry is the entrypoint of the source code. -- We use `dependencies`, `peerDependencies`, and `devDependencies`. For information - about how we use them please see [Build-time and runtime dependencies](../main/deps.md). - -## The application 💻 - -Now open -[`src/index.ts`](https://github.com/openmrs/openmrs-esm-template-app/blob/main/src/index.ts). -The rest of the tour is in the comments in that file and the components -it uses. diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index a559a50d7..000000000 --- a/docs/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - OpenMRS Frontend 3.0 Documentation for Developers - - - - These docs have been moved.. If you are not redirected automatically, follow this link to the updated docs. - - diff --git a/docs/logo.png b/docs/logo.png deleted file mode 100644 index fc5b3b2b6..000000000 Binary files a/docs/logo.png and /dev/null differ diff --git a/docs/logo.xcf b/docs/logo.xcf deleted file mode 100644 index a62156b25..000000000 Binary files a/docs/logo.xcf and /dev/null differ diff --git a/docs/main/application.png b/docs/main/application.png deleted file mode 100644 index 073239e23..000000000 Binary files a/docs/main/application.png and /dev/null differ diff --git a/docs/main/breadcrumbs.md b/docs/main/breadcrumbs.md deleted file mode 100644 index 538624ea0..000000000 --- a/docs/main/breadcrumbs.md +++ /dev/null @@ -1,49 +0,0 @@ -# Breadcrumbs - -Like many systems, OpenMRS uses navigation breadcrumbs to help the user keep track of their place in the UI. Frontend modules can register these breadcrumbs using the `registerBreadCrumbs` function exported by `@openmrs/esm-framework`. For instance, the following snippet registers a simple breadcrumb for a page: - -```ts -import { registerBreadcrumb } from "@openmrs/esm-framework"; - -function setupOpenmrs() { - registerBreadcrumb({ - path: `${window.spaBase}/myPage`, - title: "My Great Page" - }); - - return { - // usual return object - } -} -``` - -The breadcrumb will be displayed to the user as "My Great Page". - -Breadcrumbs are usually part of a hierarchy of pages, e.g., where one page is a sub-page of another. For this case, you can use the `parent` property to tell the framework what should be the parent breadcrumb for this breadcrumb. For instance, here's a relatively simple breadcrumb setup for a page with some subpages: - -```ts -import { registerBreadcrumbs } from "@openmrs/esm-framework"; - -function setupOpenmrs() { - registerBreadcrumbs([ - { - path: `${window.spaBase}/myPage`, - title: "My Great Page" - }, - { - path: `${window.spaBase}/myPage/subpage1`, - title: "Subpage 1", - parent: `${window.spaBase}/myPage` - }, - { - path: `${window.spaBase}/myPage/subpage2`, - title: "Subpage 2", - parent: `${window.spaBase}/myPage` - } - ]); - - return { - // usual return object - } -} -``` diff --git a/docs/main/carbon.md b/docs/main/carbon.md deleted file mode 100644 index a4be618ef..000000000 --- a/docs/main/carbon.md +++ /dev/null @@ -1,72 +0,0 @@ -# Design, Carbon, and the Styleguide - -## Design Patterns in OpenMRS 3.x - -First, review these introduction materials that explain the patterns to follow to keep OpenMRS 3.x a consistent User Experience: - -👉 [**Click here to see the 3.x Design Patterns**](https://scene.zeplin.io/project/61434fa756474d5545f65cf4) 👈 - -Key areas that all Developers and Project Managers/BA team members should be most familiar with are: - -* [NavBar/Header](https://scene.zeplin.io/project/61434fa756474d5545f65cf4/screen/6143628f56474d5545f6fce0) -* [Side Rail](https://scene.zeplin.io/project/61434fa756474d5545f65cf4/screen/614362c96abfb0557aa9e357) -* [Workspace](https://scene.zeplin.io/project/61434fa756474d5545f65cf4/screen/614363d860459a20f7936333) -* [Side Panel](https://scene.zeplin.io/project/61434fa756474d5545f65cf4/screen/614363d8b8582a2b4c3365a2) -* [Data Tables](https://scene.zeplin.io/project/61434fa756474d5545f65cf4/screen/61436254475da3542a84b400) - -## What is Carbon? - -**Carbon**, or [Carbon Design System](https://www.carbondesignsystem.com/), is an open-source design system with working code, design tools and resources, human interface guidelines, and a community of contributors. - -OpenMRS 3.0 uses Carbon to create a unified UI. Third party Design Systems like Carbon are kept professionally maintained by another community which reduces work for OpenMRS contributors. (More details in the [Why Use a Style Guide or a Design System](#why-use-a-style-guide-or-a-design-system) section below. - -## How to Use Carbon in OpenMRS 3.0 - -* See [Getting Started with Carbon](https://www.carbondesignsystem.com/developing/get-started/) for first steps, dev resources, and a React-specific tutorial. -* More Carbon resources are available on [GitHub here](https://github.com/carbon-design-system/carbon). - -## How to Use Zeplin - -**Zeplin** is a design collaboration tool that facilitates prototyping between UI designers and frontend developers. OpenMRS uses Zeplin for two purposes: - -1. *Design Handover*: To share designs as community assets. -2. *Style Guide*: To clearly communicate the OpenMRS 3.0 frontend components, spacing and layout, color palette, and text styles (since Carbon Design doesn't automatically customize all these for us). - -### To see the designs anytime - -You can see the designs anytime (without creating a Zeplin account) by accessing these public links: - -* [Patient Chart designs](https://zpl.io/aNYmqeN) -* [OHRI package designs](https://zpl.io/aMXW7e7) -* [Offline Patient Follow-up designs](https://zpl.io/amRdKomv) - -### To use the designs and the styleguide - -First, watch this video guide for Developers on how to use Zeplin: - -[![How to see UX & component guidance in Zeplin designs](https://img.youtube.com/vi/SjluEGDH4LU/0.jpg)](https://www.youtube.com/watch?v=SjluEGDH4LU&feature=youtu.be&ab_channel=OpenMRS "OpenMRS 3.0: Zeplin Intro for New OpenMRS Devs") - -To use the designs as a developer, you'll need to request Zeplin access from an OpenMRS admin; then, you can find them here: - -[OpenMRS Zeplin Project](https://app.zeplin.io/workspace/60d4d9bc220676b218e75ed2/projects?pid=60d59321e8100b0324762e05) - -You can find re-useable shared components & more at the [OpenMRS Styleguide here](https://app.zeplin.io/project/60d5947dd636aebbd63dce4c/styleguide/components). - -## Why use a Style Guide or a Design System? - -Users want a consistent, professional front-end UI. Without a style guide, -the expectations for various UI elements are unclear. This can create a messy, -inconsistent user experience. This is especially important in medical software: -end users want confidence in a professional system, and the UI is the user's -first encounter with that system. A poor UI can damage users' trust and can -slow down staff workflows. - -A shared, clear Design System helps to - -* assure consistency among frontend contributors in the UI look and feel for users -* use precious technical time focusing on engineering work and on the clinical use/workflows, rather than having to spend time on the details of how widgets should look and behave -* make it faster for developers to contribute new features that add value for end users - -A **design system** is a guide for making decisions about how interfaces should look, how they should be structured, and the patterns according to which user experiences are structured. Style Guides like Bootstrap and Material typically don’t provide this kind of detail. We wanted a Style Guide that would also link to code you'll use in your actual code base. - -Learn more about [Why we chose Carbon](https://wiki.openmrs.org/x/uAwGDg) as our design system. diff --git a/docs/main/config.md b/docs/main/config.md deleted file mode 100644 index a20f6e5b6..000000000 --- a/docs/main/config.md +++ /dev/null @@ -1,343 +0,0 @@ -# Using the Configuration System - -A framework is provided to make configurability easier for developers and -configuring easier for implementers. - -For introduction to Frontend 3.0 config files and how to configure frontend modules, -please see the the -[Implementer Documentation](https://wiki.openmrs.org/pages/viewpage.action?pageId=224527013#O3ImplementerDocumentation:SetUp,Configure&Deploy-Part2:ConfigureYourO3Application). - -## How to make a frontend module configurable - -You should use the OpenMRS Frontend Framework to make modules configurable. - -The main task is to create a config schema for your module. The config schema -is what tells the framework what configuration files should look like, -including defaults and validations. - -### Designing a schema - -You'll probably start with some idea of what you want configs for your module -to look like. Try and put yourself in the implementer's shoes and imagine what -features they will expect to be configurable, and what they might expect the -configuration property to be called. Assume they don't know anything about -the internal workings of your module. - -By way of example, let's say we're building a module for a virtual provider -functionality at a very futuristic hospital. Maybe we want an implementer to -be able to write the following in their config file: - -```json -"@openmrs/esm-hologram-doctor": { - "hologram": { - "color": true - }, - "virtualProvider": { - "name": { - "given": ["Qui", "Gon"] - } - }, - "robots": [ - { "name": "R2-D2", "homeworld": "Naboo" }, - { "name": "BB-8", "homeworld": "Hosnian Prime" } - ] -} -``` - -In the following section, we'll see how to write a config schema that supports these config elements. - -### Defining a schema - -We'll start with just that first nested config element from above, `hologram.color`. -We must provide defaults for all of the values—in OpenMRS Frontend 3.0, all -configuration is optional. All modules should do something reasonable out of the box. - -```js -import { defineConfigSchema, Type } from "@openmrs/esm-framework" - -defineConfigSchema("@openmrs/esm-hologram-doctor", { - hologram: { - color: { - _type: Type.Boolean, - _default: false, - _description: "Whether the hologram supports color display." - } - } -} -``` - -Note that each configuration element should have an object for a value, and -that this object must define the properties for that element. Do not do this: - -```js -❌ // This is wrong! -❌ defineConfigSchema("@openmrs/esm-hologram-doctor", -❌ hologram: { -❌ salutation: "Some friendly default salutation! ? this is wrong!" -❌ }) -``` - -The words prefixed with `_` are schema keywords. Do not prefix the names of your -config elements with underscores. Especially do not use a schema keyword as -a config element name. - -```js -❌ // Don't do this! -❌ defineConfigSchema("@openmrs/esm-hologram-doctor", -❌ hologram: { -❌ salutation: { -❌ _default: { -❌ _default: "Greetings ? this is bad don't do it" -❌ }}}) -``` - -#### Typing - -While not strictly required in the current version, you should provide a type -for every config element you define. The `_type` keyword accepts values from -the Type enum. - -These types are used both to validate input and to support special behavior -in the implementer tools. - -#### Validators - -You should provide validators for your configuration elements wherever possible. -This reduces the probability that implementers using your module will have -hard-to-debug runtime errors. It gives you, the module developer, the opportunity -to provide implementers with very helpful explanations about why their configuration -doesn't work. - -```js -robot: { - name: { - _type: Type.String, - _default: "R2D2", - _description: "What to call the robot", - _validators: [ - validator(n => /\d/.test(n), "Robots must have numbers in their names") - ] - } -} -``` - -(Note that this piece of schema is not part of our above example. It only supports -a single robot, whereas we need to allow the implementer to provide an array of robots). - -A validator can be created using the `validator` function, as above. - -The first argument is a function that takes the config value as its only argument. -If the function returns something -[truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy), validation passes. -If the function -returns something [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy), -an error is thrown with the second argument as an explanation. - -You can even validate nested objects: - -```js -colorPicker: { - options: { _default: ["green", "red", "blue"] } - initial: { _default: "green" }, - _description: "The color picker for lightsabers", - _validators: [ - validator(o => o.options.includes(o.initial), - "Initial must be one of the options") - ] -} -``` - -For convenience, some common validators are provided out of the box. See the -[API / validators](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#validators). - -#### Arrays - -You can accept and validate arrays, and arrays containing objects, in your -configuration schema. This is configured with the `elements` parameter, used -with `_type: Type.Array`. For example, a schema which would accept an array -of strings up to 30 characters long would look like this: - -```js -virtualProvider: { - name: { - given: { - _type: Type.Array, - _default: ["Obi", "Wan"] - _elements: { - _type: Type.String - _validators: [validator(n => n.length < 30, "Must be less than 30 characters")] - } - }, - _description: "The name of the avatar. Does not have to be the name of the actual provider" - }, - _description: "The avatar of the medical practitioner" -} -``` - -Here is an example of a schema that expects an array of objects structured in a particular way. - -```js -robots: { - _type: Type.Array, - _default: [ - { name: "R2-D2", homeworld: "Naboo" }, - { name: "C-3PO", homeworld: "Tatooine" } - ], - _description: "The list of all available robots", - _elements: { - name: { - _type: Type.String, - _description: "What to call the robot", - _validators: [robotNameValidator] - }, - homeworld: { - _type: Type.String, - _description: "Where the robot is from", - _default: null // not required - } - } -} -``` - -This schema will require that any objects in the robots array must only have -the keys `name` and `homeworld`, and that `name` is required. - -Objects within arrays do not -have to have defaults. If an object is supplied to the `robots` array that -does not have a `name`, an error will be thrown. - -### Using config values - -#### The generic way - -The config is fetched asynchronously using `getConfig(moduleName)`. Continuing the -above example, we would have something like - -```js -import { getConfig } from "@openmrs/esm-framework" - -async function doctorGreeting() { - const config = await getConfig("@openmrs/esm-hologram-doctor") - return "Hello, my name is Dr. " + config.virtualProvider.name.family -} -``` - -The content of config will be pulled from the config files, falling back to -the defaults for configuration elements for which no values have been provided. - -#### React support - -A React Hook is provided to hide the asynchronicity of config loading. The -`moduleName`provided to the -[`openmrsComponentDecorator` in esm-react-utils](https://github.com/openmrs/openmrs-esm-core/tree/main/packages/framework/esm-react-utils) -is used to look up the configuration elsewhere in the application. - -```js -export default openmrsRootDecorator({ - featureName: "hologram doctor", - moduleName: "@openmrs/esm-hologram-doctor" -})(Root) -``` - -You can then get the config tree as an object using the `useConfig` React hook. - -```js -import { useConfig } from "@openmrs/esm-framework" - -export default function DoctorGreeting() { - const config = useConfig() - const greeting = "Hello, my name is Dr. " + config.virtualProvider.name.family - return
{greeting}
-} -``` - -The content of config will be pulled from the config files, falling back to the -defaults for configuration elements for which no values have been provided. - -#### Support in other frameworks (Angular, Vue, Svelte, etc.) - -This hasn't been implemented yet, but we would like to implement it! See [Contributing](../getting_started/contributing.md). - - -### Typing - -It is nice to be able to have type validation for your use of configuration. To -accomplish this, define an interface alongside your config schema. - -```ts -import { defineConfigSchema, Type } from "@openmrs/esm-framework" - -defineConfigSchema("@openmrs/esm-hologram-doctor", { - hologram: { - color: { - _type: Type.Boolean, - _default: false, - _description: "Whether the hologram supports color display." - } - } -}); - -export interface HologramDoctorConfig { - hologram: { - color: boolean; - } -} -``` - -You can then use this typing information using the `as` keyword. - -```ts -const config = useConfig() as HologramDoctorConfig; -``` - -## Schema Reference - -#### `_default` - -All config elements must have a default (excluding elements within arrays of objects). - -The default does not necessarily need to satisfy the `_type` or the `_validators` of -the element, but this may change in future versions. - -#### `_type` - -One of the values from [the `Type` enum](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/enums/Type.md). Used for validation and to help the -implementer tools work with the element. - -Should always appear alongside `_default`. - -#### `_description` - -Helps implementers understand what the configuration element actually does and -how it is intended to be used. - -Can be used anywhere within the schema structure. - -#### `_validators` - -An array of validator objects. - -Some common validators are -provided: [API / validators](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#config-validation-functions). - -Custom validators should -be created with the [validator](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#validator) function. - -Can be used anywhere within the schema structure. - -#### `_elements` - -Only valid alongside `_type: Type.Array`. A `_default` -must also be provided at this level. Value should be a schema for the values -contained in the array. - -## API Documentation - -See the Config Functions section of the [API docs](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md). - -## The RFC - -This package was established as the result of -[RFC #14](https://github.com/openmrs/openmrs-rfc-frontend/blob/master/text/0014-configuration.md). -This document provides the rationale and guiding principles of the configuration -system. diff --git a/docs/main/creating_a_microfrontend.md b/docs/main/creating_a_microfrontend.md deleted file mode 100644 index 84f9cd12c..000000000 --- a/docs/main/creating_a_microfrontend.md +++ /dev/null @@ -1,133 +0,0 @@ -# Creating a Frontend Module - -**New frontend modules can be created from the -[openmrs-esm-template-app](https://github.com/openmrs/openmrs-esm-template-app)**. :point_left: - -You can fork that repository, or clone-and-copy it. -Follow the instructions in the README to turn it into your own frontend module. - -## Creating a new frontend module in a Lerna monorepo - -The process for creating a new frontend module in a monorepo -is nearly identical to the one for creating one in an independent -repo. - -1. Put the contents into a new directory under `packages/` -2. Remove configuration files that are already present at the - workspace level—things like `.github` and `.eslintrc`. -3. Remove `devDependencies` from the new frontend module's `package.json` - which are already in the `devDependencies` of the `package.json` at - the workspace level. - -That's it! Use the `@openrms/esm-template-app` above along with this guide -to get your frontend module started. - -## The `index.ts` file - -All frontend modules have an `index.ts` as an -[entry point](https://webpack.js.org/concepts/entry-points/). -See the [example](https://github.com/openmrs/openmrs-esm-template-app/blob/main/src/index.ts) -in openmrs-esm-template-app. - -This file is loaded by the app shell and must have a specific structure. -It must have three exports: -`setupOpenMRS`, `backendDependencies`, and `importTranslation`. - -### The `setupOpenMRS` function - -This function is called when the application is first starting up. The -app shell loads the `index.ts` files for all the ESMs and then executes -the `setupOpenMRS` function for each of them. The `setupOpenMRS` function -should only do the following four things: - -- define the [configuration schema](config.md) -- register [breadcrumbs](breadcrumbs.md) -- set up [offline support](../offline/offline.md) -- return an object specifying the frontend module's pages and extensions - -#### The `setupOpenMRS` return object - -This is an object with two top-level keys, both optional: `pages` and `extensions`. -Here is an example which uses all of the possible keys. - -```typescript - const options = - featureName: 'Hello World', - moduleName: '@openmrs/esm-hello-world', - }; - - return { - pages: [ - { - // load (required): tells the app shell how to load the page content - load: getAsyncLifecycle(() => import('./hello'), options), - // route (required): a string, RegEx, function, or array of any of the - // above, which will be used to determine when to load the - // frontend module content. It will be matched against the URL path. - route: 'hello', - // order (optional): a number which specifies the order in which the - // page should be loaded in the application. The default is 1. This - // is generally only needed if you need to specify the DOM position - // of your page; for example, if it is a navbar and needs to be at the - // top, you might use order 0. - order: 2, - // `online`, `offline`, and `resources` are described in the offline - // support documentation. All are optional. - online: { allowSayingHelloBack: true }, - offline: { allowSayingHelloBack: false }, - resources: { greeting: fetchGreetingOffline } - }, - ], - // To understand the `extensions` section, please read the "Extension System" - // page of the documentation. - extensions: [ - { - // name (required): The name of the extension. Must be unique across - // all modules in the application. - name: 'greeting-bot', - // load (required): tells the app shell how to load the extension content - load: getAsyncLifecycle(() => import('./greeting-bot'), options), - // slot (optional): Specifies a slot for the extension to be attached to. - slot: 'bot-slot', - // slots (optional): Alternative to `slot` which allows attaching to - // multiple slots. For example: - // slots: ['bot-slot', 'greeters-slot'] - - // `online`, `offline`, and `resources` are described in the offline - // support documentation. All are optional. - online: true, - offline: true, - resources: { greeting: fetchGreetingOffline } - }, - ] - }; -``` - -### The `backendDependencies` object - -This is an object that tells the frontend application what OpenMRS server modules -the frontend module depends on, and what versions. If these dependencies are not -met, administrators will be alerted. - -### The `importTranslation` function - -This is required for translations to work. It tells the frontend application -how to load translation strings. The boilerplate which you will find in -[openmrs-esm-template-app](https://github.com/openmrs/openmrs-esm-template-app/blob/main/src/index.ts) -is almost certainly what you want. Note that the first argument to -`require.context` is a directory, `../translations`. -That directory must exist at that location relative to the `index.ts` file. - -## Integrating into your application - -Once you've created a frontend module, you'll want to integrate it into your -application. There are two steps for doing so. - -First, publish your frontend module package to NPM. See -[Release and Deployment](../getting_started/release_and_deployment.md) -for more information. At the end, your package should be visible on npm.js, -like [`@openmrs/esm-login-app`](https://www.npmjs.com/package/@openmrs/esm-login-app). - -Then, you'll need to integrate this package into your application. -Information about that can be found in the Implementer Documentation on -[Deployment](https://wiki.openmrs.org/pages/viewpage.action?pageId=224527013). diff --git a/docs/main/data.md b/docs/main/data.md deleted file mode 100644 index 75f5f3065..000000000 --- a/docs/main/data.md +++ /dev/null @@ -1,157 +0,0 @@ -# Retrieving and Posting Data - -Frontend modules interact with the OpenMRS server via the APIs exposed -by its modules. In general, most of the endpoints we use are provided -by the [FHIR Module](https://wiki.openmrs.org/display/projects/FHIR+101%3A+OpenMRS+Strategy%2C+Tools%2C+FHIR+API%2C+and+Help). -Most of the rest are provided by the -[REST Module](https://wiki.openmrs.org/display/docs/REST+Module), which is -documented [here](https://rest.openmrs.org/). - -Endpoints from the FHIR Module should always be preferred, when they are -available. FHIR is an interoperability standard which OpenMRS supports. - -Some data is available using higher-level functions or custom React Hooks provided -by `@openmrs/esm-framework`. These should be used when available. - -All of this functionality (React hooks excepted) is provided by the -[`@openmrs/esm-api`](https://github.com/openmrs/openmrs-esm-core/tree/main/packages/framework/esm-api) -package, which is part of `@openmrs/esm-framework`. -The React hooks are in -[`@openmrs/esm-react-utils`](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-react-utils), -which is also part of `@openmrs/esm-framework`. See the -[API Docs](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md). - -## FHIR - -To use the FHIR API, we recommend using [SWR](https://swr.vercel.app/docs/data-fetching) based hooks and the [`openmrsFetch`](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#openmrsfetch) fetcher function to retrieve data from the server. SWR offers a host of features that help us deliver an improved user experience. - -Here's an example of using SWR to retrieve a patient from the FHIR API. - -```typescript -import useSWR from 'swr'; -import { fhirBaseUrl, openmrsFetch, fhir } from '@openmrs/esm-framework'; - -const { data, error, isLoading, mutate } = useSWR( - `${fhirBaseUrl}/Patient/${patientUuid}`, - openmrsFetch -); -``` - -If you have questions about FHIR support in OpenMRS, you can ask in the -[FHIR Squad Slack channel](https://openmrs.slack.com/archives/CKLPH66BB). - -## Other OpenMRS server APIs - -Some administrative endpoints will likely never have a proper representation in FHIR (e.g., endpoints for managing encounter types). When no suitable FHIR endpoint is available, you will want to use a different OpenMRS server API. The [REST Web Services API](https://rest.openmrs.org/) is used widely across many of our frontend modules. - -Here's an example of a custom SWR hook that retrieves visits data. - -```typescript -import useSWR from 'swr'; -import { openmrsFetch, Visit } from '@openmrs/esm-framework'; - -interface VisitData { - results: Array; -} - -/* Custom data fetching hook */ -export function useVisits() { - const url = `/ws/rest/v1/visit?includeInactive=false`; - - const { data, error, isLoading, mutate } = useSWR<{ data: VisitData }, Error>(url, openmrsFetch); - - return { - visits: data, - isLoading, - error, - mutate, - }; -} -``` - -We can leverage this `useVisits` hook in our `visits` component as follows: - -```typescript -const Visits = () => { - const { visits, isError, isLoading } = useVisits(); - - if (isLoading) { - return - } - if (error) { - // render error state - } - if (visits?.length) { - // render visits - } - return ( - // render empty state - ) -} - -export default Visits; -``` - -The `mutate` function returned by `useSWR` can be used to update the cache and trigger a re-render of the component. This is useful when we want to update the UI after a successful mutation. - -```typescript -const res = await saveVisitNote(payload); - -try { - if (res.status === 201) { - mutate(); - closeWorkspace(); - - // show success toast - } -} catch (error) { - // handle error -} -``` - -## Posting data to the server - -Here's an example that demonstrates posting session data to the server. - -```typescript -import { openmrsFetch } from '@openmrs/esm-framework' - -const abortController = new AbortController(); -abortController.abort(); -openmrsFetch('/ws/rest/v1/session', { - method: 'POST', - body: { - username: 'hi', - password: 'there', - }, - signal: abortController.signal -}); -``` - -It is best practice for POST requests to have an -[AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort). -You should ensure that `AbortController.abort()` is called when the component is unmounted -if the request is not yet completed. - -In a React Component this is usually accomplished by making the request -in a [`useEffect` hook](https://reactjs.org/docs/hooks-effect.html): - -```typescript -useEffect(() => { - const abortController = new AbortController(); - someFetchFunction(abortController) - .then(setResult) - .catch(setError); - return () => abortController.abort(); -}, []); -``` - -## API Objects - -Some API objects are made available via React hooks (or via framework-agnostic subscriptions). -The hooks are in -`@openmrs/esm-react-utils`, -and the subscription-yielding equivalents are in -[`@openmrs/esm-api`](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#api-functions). -See for example [`useVisitTypes`](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#usevisittypes) -and the corresponding [`getVisitTypes`](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#getvisittypes). diff --git a/docs/main/dates.md b/docs/main/dates.md deleted file mode 100644 index 16348dddf..000000000 --- a/docs/main/dates.md +++ /dev/null @@ -1,34 +0,0 @@ -# Working with Dates - -The OpenMRS Frontend Framework provides several utilities for working with -dates. These have been designed with locale-sensitivity as a key concern. -Acceptable date formats vary by language and region, and these functions -are intended to accomodate that variation. - -## Formatting for display - -Date objects can be displayed in a variety of standard formats using the -[formatDate](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#formatdate), -[formatTime](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#formattime), and -[formatDatetime](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#formatdatetime) -functions. These functions format dates in ways that are both adaptable -(using the `mode` parameter) and localized according to the user's locale. - -Date strings produced by these formatting functions should never be -used for comparisons or other kinds of date math. It is recommended -to format dates for display as close to the "view" or "render" as -possible to minimize the risk of this happening. - -## Formatting to send to the server - -Dates sent in the request body via `openmrsFetch` are automatically -formatted into ISO-8601 format. - -## Parsing - -The ISO-8601 date strings sent by the server can be parsed into Javascript -`Date` objects using the -[parseDate](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#parsedate) -function. This uses [dayjs](https://day.js.org/docs/en/parse/string) for -parsing. - diff --git a/docs/main/deps.md b/docs/main/deps.md deleted file mode 100644 index edd987010..000000000 --- a/docs/main/deps.md +++ /dev/null @@ -1,25 +0,0 @@ -# Build-time and Runtime Dependencies - -In a frontend modules architecture, different JavaScript applications -come together on the client in a coordinated way. Each application -reaches the client as one or more *bundles*. A bundle is the built -code, transpiled and compiled into a format the browser can read. - -An application's dependencies can be *built into the bundle*, or -they can be kept as references and *resolved -on the client*. We refer to these as **build-time dependencies** -and **runtime dependencies**, respectively. - -> **For the curious:** The client has to know how to resolve runtime - dependencies. This is the role of SystemJS and the import map. - SystemJS tells the browser how to use the import map to resolve - runtime dependencies. Frontend modules are then compiled into a - format that tell the browser to use SystemJS for runtime - dependency resolution. - -**Build-time dependencies go in `devDependencies`**, alongside -development tooling. - -**Runtime dependencies go in `peerDependencies`**. - - diff --git a/docs/main/distribution.md b/docs/main/distribution.md deleted file mode 100644 index 3e5e5b740..000000000 --- a/docs/main/distribution.md +++ /dev/null @@ -1,87 +0,0 @@ -# Creating a Distribution - -One of the reasons for choosing this kind of modularization in the frontend space is to allow maximum flexibility for creating your own distribution. That way, you can use what you find useful and drop what you don't like to see in your distribution. - -This concept is transported to almost all areas including, but not limited to, the available frontend modules, the delivered app shell, and the method of serving the application. - -In this section we look at some considerations when creating a distribution. Specifically, we investigate what options we provide and how you can leverage these for your use case. - -## Local Build vs CI Setup - -You may be tempted to clone the `openmrs-esm-core` repository for building your distribution. **Don't** do this unless you know exactly *why* you want to work against the repository. The repository is only there for *development* of the OpenMRS Frontend. It is **not** there for building distributions. - -To build your own distribution a simple Node.js tool called `openmrs` was created. This allows: - -- creating an import map with all resources for the contained frontend modules (`openmrs assemble`) -- build a new app shell to host frontend modules (`openmrs build`) -- start a debugging session of the shell and a frontend module (`openmrs debug`) -- start a debugging session of a frontend module in the shell (`openmrs develop`) -- starts the default app shell locally (`openmrs start`) - -For creating a distribution we recommend doing two things: - -1. Build the app shell (`openmrs build`) with the configuration you want to see. -2. Use `openmrs assemble` to get a custom configuration for your import map. - -The import map is used to define what frontend modules are included and where these frontend modules are located. - -## Customizing the Import Map - -By building the app shell you'll already get a rudimentary version of an import map, which can be used for development purposes. Generally, however, you should provide your own. - -An import map can also be specified as an URL. For instance, for the development instance as `dev3.openmrs.org` we have [https://spa-modules.nyc3.digitaloceanspaces.com/import-map.json](https://spa-modules.nyc3.digitaloceanspaces.com/import-map.json). The contents of this import map are updated once an update to any (official) frontend module has been pushed. Thus, while this import map may be great for development purposes, it should be considered unstable. Avoid this for your distribution or any application that should not break unexpectedly. - -A custom import map can be created using the `openmrs assemble` command. If run directly the command will open a command line survey, guiding you through the different options. It will list all OpenMRS frontend modules that can be found on the NPM registry. - -For CI/CD purposes we encourage you to use a configuration file `spa-build-config.json` instead. This file then defines the wanted frontend modules and configures the whole process. - -The file may looks as follows: - -```json -{ - "publicUrl": ".", - "frontendModules": { - "@openmrs/esm-patient-chart-app": "latest", - "@openmrs/esm-patient-registration-app": "3.0.0" - } -} -``` - -The `publicUrl` may be important for later. If the gathered resources are placed (and served) in the same folder as the SPA resources then `.` is good. If they are uploaded to say a CDN, then the (base) URL of the CDN should be defined. - -Example: - -```json -{ - "publicUrl": "https://openmrs-cdn-example.com/mf", - "frontendModules": { - "@openmrs/esm-patient-chart-app": "latest", - "@openmrs/esm-patient-registration-app": "3.0.0" - } -} -``` - -In this case the resulting `import-map.json` could look as follows: - -```json -{ - "imports": { - "@openmrs/esm-patient-chart-app": "https://openmrs-cdn-example.com/mf/openmrs-esm-patient-chart-app-3.2.1/openmrs-esm-patient-chart-app.js", - "@openmrs/esm-patient-registration-app": "https://openmrs-cdn-example.com/mf/openmrs-esm-patient-registration-app-3.0.0/openmrs-esm-patient-registration-app.js" - } -} -``` - -Either way the assemble command makes sure to have all assets made available properly. - -## Canary vs Stable - -Regarding the versioning you'll have three options: - -- Go for the `latest` tag -- Go for the `next` tag -- Go for a specific (i.e., explicit) version - -In general we recommend to stay on non-preview (e.g., `3.2.1`) versions. Preview versions (e.g., `3.2.1-pre.0`) are for development purposes and may not be stable. - -For creating a working distribution ideally you'll stick to explicit versioning of non-preview versions. If you use `latest` then individual frontend modules may work as expected, but incompatibilities (e.g., if a certain frontend module was updated but is now incompatible to another frontend module that you also use) may then exist - making additional testing required. With an explicit version you can be sure that a working system remains as such in rebuild scenarios. diff --git a/docs/main/distro_setup.png b/docs/main/distro_setup.png deleted file mode 100644 index df8e336b7..000000000 Binary files a/docs/main/distro_setup.png and /dev/null differ diff --git a/docs/main/extensions.md b/docs/main/extensions.md deleted file mode 100644 index fbfad6756..000000000 --- a/docs/main/extensions.md +++ /dev/null @@ -1,212 +0,0 @@ -# Using the Extension System - -The extension system makes it possible for frontend modules to -insert UI elements into each other, and for these interactions -to be configurable by system administrators. - -> Those familiar with the -[OpenMRS RefApp 2.x extension system](https://wiki.openmrs.org/display/docs/Module+Extension+Points) -will be glad to know that the basic concepts here are similar, but simpler. -"Extensions" are roughly the same thing as before, "points" are now -called "slots," and there is no longer anything like "apps," which no one -really understood anyway. - -## Key Concepts - -There are two kinds of things in the extension system: **extensions** and **slots**. - -An **extension** is a component. - -A **slot** is a place. - -Read the above two lines until they resonate in your head like a mantra. - -Extensions get rendered into slots. An extension gets associated with a -slot in one of the following ways: -- The extension names the slot in its definition, under `slot[s]` -- A call to [attach](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#attach) -- A system administrator adds the extension to the slot using the - slot's `add` array - -## When should I use extensions and slots? - -The extension system should be thought of as a system for making -behavior configurable by administrators. It should not be thought of -way to reuse components across modules. - -This is the key question: - -_Am I creating a collection of similar things, such as buttons or -tiles, which an administrator might want to re-order or otherwise -change?_ - -If so, this may be a good place to use extensions. - -### What if I just want to mount something from one framework into something in another framework? - -Just use the Single-SPA -[mountParcel](https://single-spa.js.org/docs/parcels-overview/#mountrootparcel-vs-mountparcel) -function. - -### What if I just want to use a component from one module in a different module, and I can change both? - -Consider exporting the component and using it the normal way. - -## Usage - -Extensions are defined in the `routes.json` file bundled with a module, in an -`extensions` array. Each element of this array defines an extension, with -a name and a load function. It may also specify the names of slots to -attach the extension to by default. It may also specify -[a number of other things](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/interfaces/ExtensionRegistration.md), -some of which will be covered below. - -Slots are components. There is an [ExtensionSlot](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#extensionslot) -React component. If you are working in a different framework and would like to create -an extension slot, please get in touch with the OpenMRS Frontend 3.0 team -on Slack. - -## Principles - -### Naming extensions - -An extension will have a name which identifies it. That name should describe -what the extension does. It should not have anything to do with where the -extension will appear in the application. It has no innate sense of place. - -:heavy_check_mark: Good extension names: -- Vitals table -- User avatar -- Biometrics tile - -:x: Bad extension names: -- _Top_ bar ("top" indicates a place) -- _Home page_ reports link ("home page" indicates a place) -- Steve (names should be descriptive) - -> **Note**: You will likely see a lot of extension and slot names which -> are all lowercase with dashes. This is not necessary; it is better -> to give extensions names that are pleasant to read. -> -> Similarly, you will see many slots suffixed with "slot." This is also -> not necessary. - -### Naming slots - -A slot will also have a name which identifies it. That name should describe -the location in the app that it represents. If it describes the things that -can go in it, it should only use the most general terms imaginable—things -like "button" or "tile" or "widget." - -:heavy_check_mark: Good slot names: -- Primary nav right menu -- Patient header detail box -- Form header buttons - -:x: Bad slot names: -- Patient address (too prescriptive about contents) -- homepage-widgets-slot (should be `Homepage widgets`) -- Extra buttons (too vague) - -### Styling - -An extension should be as agnostic as possible to the context in which it -appears. This means that you should avoid defining the size of an extension. -Extensions should be responsive (within reason), such that the contents -will adapt to a variety of different extension dimensions. - -Slots should be responsible for as much styling as generically applies to -all of their contents. If all of the extensions in a slot should have a -border, the slot should apply the border. The slot should also be responsible -for setting the dimensions into which the extensions will render. - -A slot can apply styles to an extension with the following CSS selector: - -```css -.slot > * > * { - ... -} -``` - -## Extension configurability - -The beautiful thing about configurability in the extension system is -that you don't need to think about it. Extensions and -slots have a standard configuration interface that allows administrators -to add, remove, and re-order extensions, as well as specific -configuration specific to an extension within a particular slot. - -You can use `useConfig` as usual within an extension. - -The schema for an extension can be specified using `defineExtensionConfigSchema`. -If no schema is defined specifically for your extension, the extension will inherit -the configuration of the module that contains it. - -## State - -Sometimes, extensions are not as independent as we might wish they were, -and have to expect some state from the slot in which they are mounted. -Most commonly, extensions that pertain to a specific patient will accept -a `patientUuid` parameter which can be used to fetch relevant patient -information. - -State is provided as a parameter to the `ExtensionSlot` or `Extension` -components, and recieved as a prop by the extension. - -See the [ExtensionSlot API docs](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#extensionslot) -for more. - -## Meta - -Sometimes, extensions might want to pass information to the slot that -receives them. This is used, for example, by patient chart widgets. -Dashboards render these widgets into a grid format. When a dashboard -receives a widget, the widget informs the dashboard (which is a slot) -how many grid columns it would like to take up. This happens using `meta`. - -Meta is provided by extensions in their definition in the `routes.json` -file. - -Slots can access meta through the extension system API, such as by using -[useExtensionSlotMeta](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#useextensionslotmeta). - -## Offline Support - -For information about offline support, please see [Offline Mode](../offline/offline.md). - -## Order - -By default, extensions will render into slots in the order that they are -declared or attached. Extensions which are added by an administrator come -last. - -Extensions can provide an `order` index in their definition to influence -the order in which they are rendered. This works like -[z-index](https://developer.mozilla.org/en-US/docs/Web/CSS/z-index) -in CSS—similarly, it is a way of setting relative order among elements -that don't officially know about each other. - -Administrators can also override the order of extensions within any slot -by modifying the `order` configuration parameter of that slot. - -## Additional Resources - -Short introductory videos: -- [OpenMRS Frontend 3 Extension System 1 - Basics](https://youtu.be/Teq3FwKofSc) -- [OpenMRS Frontend 3 Extension System 2 - State and Meta](https://youtu.be/8514ebpAEWI) - -Introductory presentation: [Quick Guide to Slots](https://docs.google.com/presentation/d/1mQxh7qAYLD-gc9sh0I58t4o_XNndPcu6hAJmTZQZ_fo/edit#slide=id.gbe34f6b087_0_34 ) - -For a terse technical description of the extension system, see the -[Extensions RFC](https://github.com/openmrs/openmrs-rfc-frontend/pull/27/files). - -### Workshop - -A live workshop was hosted on Zoom, providing a comprehensive introduction -to the extension system, as well as practical problems. -Recordings and materials are available below. - -- [Part 1: About our Frontend Module Architecture & How to Use Extensions](https://iu.mediaspace.kaltura.com/media/t/1_e7kvnx9t?st=702) -- [Part 2: Practical Session on our MFE Architecture & How to Use Extensions](https://iu.mediaspace.kaltura.com/media/t/1_iaq63mfd?st=282) - - [Practice tasks](https://github.com/openmrs/openmrs-esm-testresults/tree/feature/workshop) - - [Practice solutions](https://github.com/openmrs/openmrs-esm-testresults/tree/feature/workshop-solutions) diff --git a/docs/main/faq.md b/docs/main/faq.md deleted file mode 100644 index bd67034d2..000000000 --- a/docs/main/faq.md +++ /dev/null @@ -1,115 +0,0 @@ -# Frequently Asked Questions - -## Troubleshooting Local Dev Environment - -It's important to keep your working code base current with the latest fixes, latest features, and library APIs to develop against. When developing a frontend module your code will depend on the app shell and core libraries, and sometimes other frontend modules. If you are having trouble running code locally when it appears to be working on the server ([dev3](https://dev3.openmrs.org/openmrs/spa)), try the following. - - -### How do I keep my local dev server up to date? - -Just pull the repository you want to work on and run `yarn`. -That will install the latest dependencies, which includes `openmrs`, which is -the tool that runs the dev server. - -For help with pulling the latest code see the [community help page](https://wiki.openmrs.org/display/docs/Using+Git) for basic git commands. - -### I'm not seeing the latest `@openmrs/esm-framework`. How do I update the dependency? - -If you notice your app suddenly stop working, it might be because a core library has changed in the app shell on the server you are developing against. The main dependencies shared by all OpenMRS frontend modules are `openmrs` and `@openmrs/esm-framework`. They can be updated like this: - -```sh -# Upgrade core libraries -yarn up openmrs @openmrs/esm-framework - -# Reset version specifiers to `next`. Don't commit actual version numbers. -git checkout package.json - -# Run `yarn` to recreate the lockfile -yarn -``` - -### Using a custom importmap - -By default, when you run `yarn start`, the command will use the importmap from dev3, that is, https://dev3.openmrs.org/openmrs/spa/importmap.json. This is the default importmap for the reference application and generally what you will want to use. However, in some situations, you may want to us a custom importmap, such as a distribution-specific importmap. In this case, you can use the `--importmap` argument to the `yarn start` command to point to any importmap you like: - -```sh -yarn start --importmap "https://dev3.openmrs.org/openmrs/spa/import-map.json" -``` - -### Clearing out the browser cache - -By default the server [tells your browser](https://github.com/openmrs/openmrs-contrib-ansible-docker-compose/blob/c36115ca22b8fb842f8cf0ff745d1d35a4567912/files/emr-3-dev/proxy.conf#L97) to cache JavaScript files for 30 days. You can [bypass your browser's cache](https://en.wikipedia.org/wiki/Wikipedia:Bypass_your_cache) when refreshing the page. If you want to keep the cache disabled while you are developing, open your browser's developer tools, go to the "Network" tab, and check "Disable Cache." The cache will only be disabled while the developer tools are open, and only for the page that they are attached to. - -How to open browser developer tools (chrome) -open-devtools-chrome - -How to disable asset (javascript, css) cache (chrome) -disable-cache-chrome - -### Clear local package cache - -Although yarn is very good at maintaining its dependency list as specified in `yarn.lock` it is possible for the `node_modules` folder containing library packages to get corrupted. You can manually remove the the `node_modules` folder and rebuild it with `yarn` - -```sh -rm -rf node_modules/ -yarn -``` - -This will only delete the root `node_modules` folder. You may also want to delete the `node_modules` of all packages in a monorepo. - -Yarn is smart and will quickly compile the new `node_modules` folder from its internal cache. If you are worried that yarn has cached an invalid version of a package you can clear yarn cache to force it to download the packages again - -```sh -yarn cache clean -``` - - -## Further Info - -### How do I find out where the code I need to work on is? - -There is no simple correlation between packages and pages and content. - -The first thing to check is the [list of repositories](http://o3-dev.docs.openmrs.org/#/main/map?id=repositories-you-should-know-octocat) -and their descriptions. There aren't very many, so it's likely you can -figure out what you're trying to work on just by looking at the list. - -If that doesn't work out, another trick is to clone all of the repositories -and then run `grep` across them to look for something you expect to find— -e.g. a piece of text from the UI. - -If you have done both of the above things and still haven't found the -code you're looking for, feel free to ask in the -[#openmrs-helpme](https://openmrs.slack.com/archives/C02UNMKFH8V) channel -on Slack. - -### How do I develop against a restricted environment? - -In general you can develop against another environment using the `--backend` flag. -If the other environment is guarded, e.g., by an IP or network restriction then -this is something you need to take care of on your local machine. - -In case the guarded environment is restricted via some SSO mechanism using a -cookie you could use the `--add-cookie` flag to achieve this. As an example, -look at the access for a development server from the ICRC: - -```sh -npx openmrs start --backend "https://emr-v2.test.icrc.org/" --add-cookie "MRHSession=1234..." -``` - -The cookie must be obtained by you and strongly depends on the used backend. - - -## Dev hacks - -### Turn on dev tools - -```js -localStorage.setItem('openmrs:devtools', true) -``` - -### Manually set an importmap override - -```js -localStorage.setItem("import-map-override:@openmrs/esm-form-entry-app", "http://localhost:4200/openmrs-esm-form-entry-app.js"); -``` diff --git a/docs/main/forms.md b/docs/main/forms.md deleted file mode 100644 index 190a18037..000000000 --- a/docs/main/forms.md +++ /dev/null @@ -1 +0,0 @@ -123 diff --git a/docs/main/map.md b/docs/main/map.md deleted file mode 100644 index 590aba653..000000000 --- a/docs/main/map.md +++ /dev/null @@ -1,67 +0,0 @@ -# Map of the Project - -## Orientation - -OpenMRS Frontend 3.0 uses a microfrontends architecture. We call our microfrontends "frontend modules." A frontend module is a self-contained piece of application. It has some UI, and some ideas about where in the application it should be rendered. Each frontend module can be configured, and some aspects of how frontend modules interact can be configured, too. - - -There are four important pieces of the OpenMRS Frontend modules system: - -- The **app shell**, which is the "base" of the application and coordinates everything -- The **framework**, which is a library that all frontend modules use -- The **import map**, which is a file that tells the app shell what frontend modules to use and where they are -- The **frontend modules**, from which the interface is composed - -The frontend modules communicate with the **backend**, which is the OpenMRS server, using its APIs. - -### The framework - -The **framework** is an NPM package called `@openmrs/esm-framework`, which is composed of multiple smaller libraries. These are documented in the -[OpenMRS Frontend Core README](https://github.com/openmrs/openmrs-esm-core#openmrs-frontend-core). - -### The frontend modules - -The **frontend modules** are shipped in the ES Module format and usually thus just called `esm`s (same prefix as the libraries). In any case these frontend modules are indeed also libraries, but very special ones that - -- are not directly integrated into the main application, -- are loaded indirectly via a special JSON called an "import map", and they -- export a predefined set of functionality including a function called `setupOpenMRS`. - -These frontend modules bring in domain-specific UI capabilities such as menu entries, page content, or notifications. - -#### Understanding frontend modules :brain: - -All the pieces of a frontend module are explained in -[Tour of a Frontend Module](../getting_started/tour.md). - -Also see the -**[openmrs-esm-template-app](https://github.com/openmrs/openmrs-esm-template-app)**, -which explains lots of things about frontend modules. - -### The backend - -The backend is an OpenMRS server and its APIs. The frontend application and backend server do not have to be colocated. Since the frontend really just a set of static files, it can be served from anywhere. The specific setup is up to you. - -## Structure of the application - -The process of building an application, which is described in the -[Implementer Documentation](https://wiki.openmrs.org/pages/viewpage.action?pageId=224527013), -looks like this: - -![Distro Build Diagram](./distro_setup.png) - - -The built application is then executed on the client like this: - -![Application Diagram](./application.png) - - -## Repositories you should know :octocat: -The 3.x EMR Reference Application is made up of all the frontend modules found in the following repositories: - -- :construction: [openmrs-esm-template-app](https://github.com/openmrs/openmrs-esm-template-app): This repository provides a starting point for creating your own OpenMRS Microfrontend. -- [openmrs-esm-core](https://github.com/openmrs/openmrs-esm-core/tree/master/packages/apps): Frontend modules which are administrative or else integral to the application -- [openmrs-esm-patient-management](https://github.com/openmrs/openmrs-esm-patient-management/tree/main/packages): Frontend modules which deal with creating, searching, and listing patients -- [openmrs-esm-patient-chart](https://github.com/openmrs/openmrs-esm-patient-chart/tree/master/packages): The patient chart frontend module and all its widgets -- [openmrs-esm-home](https://github.com/openmrs/openmrs-esm-home/tree/master/packages): Frontend modules tied to the home page -- [openmrs-rfc-frontend](https://github.com/openmrs/openmrs-rfc-frontend): A git repository that facilitates a democratic process where folks can propose changes to the frontend implementation via RFCs (Request For Comments). See, for example, the [Contributing Guidelines for Frontend Modules](https://github.com/openmrs/openmrs-rfc-frontend/blob/master/text/0020-contributing-guidelines.md), which were established with RFC #20. diff --git a/docs/main/state.md b/docs/main/state.md deleted file mode 100644 index a9cf58d8a..000000000 --- a/docs/main/state.md +++ /dev/null @@ -1,85 +0,0 @@ -# Sharing State between Frontend Modules - -In general, state in React apps should be -[managed using React](https://kentcdodds.com/blog/application-state-management-with-react). -Use the [State Hook](https://reactjs.org/docs/hooks-state.html) (or -[Reducer Hook](https://reactjs.org/docs/hooks-reference.html#usereducer)) -and [Context](https://reactjs.org/docs/context.html) to pass state around within your -frontend module. - -In some cases, you may need to manage state outside React, such as when you -have separate React applications that need to share state. -This can come up, for example, if you your frontend module has multiple extensions -that need to share state with each other. - -In these cases you can use the -[`@openmrs/esm-state`](https://github.com/openmrs/openmrs-esm-core/tree/main/packages/framework/esm-state) -features of `@openmrs/esm-framework`. The framework provides functions for -managing state using [Unistore](https://github.com/developit/unistore#unistore). - -## How do I use it? - -A Unistore store can be created using -[`createGlobalStore`](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#createglobalstore): - -```typescript -import { createGlobalStore } from '@openmrs/esm-framework'; - -export interface BooksStore { - books: Array; -} - -createGlobalStore("books", { - books: [], -}); -``` - -The store can then be accessed using -[`getGlobalStore`](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#getglobalstore) - -```typescript -import { getGlobalStore } from '@openmrs/esm-framework'; - -const bookStore = getGlobalStore("books"); -console.log(bookStore.getState()); -bookStore.subscribe(books => console.log(books)); -bookStore.setState({ books: ["Pathologies of Power"]}); -``` - -### In React: - -```typescript -import React, { useEffect } from 'react'; -import { getGlobalStore, useStore } from '@openmrs/esm-framework'; - -const bookStore = getGlobalStore("books"); - -function BookShelf() { - const { books } = useStore(bookStore); - return <>{books.join(",")}; -} -``` - -There are a few other ways that you can use stores in React, such as by creating -a subscription in a `useEffect` block, or by using `Provider` and `connect`, but -this method is by far the simplest. - -### Other notes - -If your directory structure allows, you can also pass stores around explicitly: - -```typescript -// bookStore.ts -export bookStore = createGlobalStore("books", { - books: [], -}); -``` - -```typescript -import { bookStore } from "./bookStore"; -bookStore.setState({ books: ["A History of Global Health"] }); -``` - -See the [Unistore docs](https://github.com/developit/unistore#unistore) for more -information about stores. - diff --git a/docs/main/translations.md b/docs/main/translations.md deleted file mode 100644 index b88681dff..000000000 --- a/docs/main/translations.md +++ /dev/null @@ -1,27 +0,0 @@ -# Using the Translation System - -We use i18next and react-i18next to internationalize frontend module content. - -Watch a quick tutorial video: - -[![https://www.youtube.com/watch?v=1pLUi47BIBo](https://img.youtube.com/vi/1pLUi47BIBo/0.jpg)](https://www.youtube.com/watch?v=1pLUi47BIBo). - -In order to internationalize your frontend module, it needs to have a line in -its `index.ts` file which tells the app shell where to find translations. -This will generally be exactly: - -```javascript -const importTranslation = require.context("../translations", false, /.json$/, "lazy"); -``` - -The `translations` directory should exist at that path relative to the `index.ts` file -(generally, at the top level of the package). It should contain a file `en.json`, which -is the translation *source file*, and may contain other JSON files corresponding -to languages into which the frontend module is translated. - -You should then be able to use [react-i18next](https://react.i18next.com/) in your -React components. - -> **Practice**: Try changing the language to Spanish (`es`), French (`fr`), or a language - of your choice. Check to see if any English text is still visible. If it is, follow - the example steps in the tutorial video to correct this. \ No newline at end of file diff --git a/docs/main/upgrade_3_to_4.md b/docs/main/upgrade_3_to_4.md deleted file mode 100644 index adebd2575..000000000 --- a/docs/main/upgrade_3_to_4.md +++ /dev/null @@ -1,193 +0,0 @@ -# Upgrading to Framework Version 4 - -Version 4 of the OpenMRS 3 frontend framework includes a number of essential updates, including: - -- Updating React from v16 to 18 -- Updating React Router from v5 to 6 -- Updating Carbon Design System from v10 to 11 -- Major improvement to the system that loads frontend modules into the application. - -> Note: This has nothing to do with any OpenMRS 4, which does not exist yet (in 2022). This is about upgrading some libraries within OpenMRS 3. The packages `openmrs` and `@openmrs/esm-framework` are being upgraded from version 3 to version 4. - -A consequence of this last change is that frontend modules built with the `openmrs` -library at version 3.x will not work in an application running the app shell at -version 4.x. - -There are some breaking changes that require changes to frontend module code. -You will also need to upgrade testing libraries to support the new version of React, which -involve some additional changes: -- Jest from 26 to 28 -- React Testing Library from 10 to 13 -- User Event from 12 to 14 - -## Setting Up to Upgrade - - - -In brief, you're going to run the app shell, upgrade the libraries in your frontend module, and load your frontend module into the app shell using [import map overrides](https://o3-dev.docs.openmrs.org/#/getting_started/setup?id=import-map-overrides). - -### Getting Frontend Framework 4.0 - -Check out the `main` branch of [openmrs-esm-core](https://github.com/openmrs/openmrs-esm-core/tree/main). -Set it up by running - -```sh -yarn -yarn build:apps -``` - -and then run the app shell using `yarn run:shell`. - -### Running your Frontend Module - -In your checked-out frontend module, upgrade the `openmrs` and `@openmrs/esm-framework` -packages to version `4.0.1` or later - -```sh -yarn up openmrs@next @openmrs/esm-framework@next -``` - -In `openmrs-esm-core` run `yarn run:shell`. Then in your frontend module run `yarn serve` your frontend module and load it using the [import map overrides UI](https://o3-dev.docs.openmrs.org/#/getting_started/setup?id=import-map-overrides). - -### Alternate Approach - -Another way to upgrade frontend modules is [used for the core apps](https://o3-dev.docs.openmrs.org/#/under_the_hood/migration_guide?id=procedure). You can use that technique for your frontend module, but it is much more involved than the above. - -## Upgrading - -For a complete look at what needs to be upgraded, please read the relevant documentation for the following: - -- [React 16 to 17](https://reactjs.org/blog/2020/08/10/react-v17-rc.html#other-breaking-changes) -- [React 17 to 18](https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html) -- [React Router 5 to 6](https://reactrouter.com/en/main/upgrading/v5) -- [Carbon 10 to 11](https://github.com/carbon-design-system/carbon/blob/main/docs/migration/v11.md) -- [Jest 26 to 27](https://jestjs.io/blog/2021/05/25/jest-27) -- [Jest 27 to 28](https://jestjs.io/blog/2022/04/25/jest-28) -- [User Event 12 to 13](https://github.com/testing-library/user-event/releases/tag/v12.0.0) -- [User Event 13 to 14](https://github.com/testing-library/user-event/releases/tag/v14.0.0) - -In practice, relatively little of that will apply to your frontend module. Here are the -most likely changes you'll need to make: - -### Jest - -- Add the following to your `jest.config.json`. - -```json - "testEnvironment": "jsdom", - "testEnvironmentOptions": { - "url": "http://localhost/" - } -``` - -- Remove the following `moduleNameMapper` references in your `jest.config.json`: - -```json -"moduleNameMapper": { - "lodash-es": "lodash" - "\\.(s?css)$": "identity-obj-proxy", - "@openmrs/esm-framework": "@openmrs/esm-framework/mock", - // Remove the following lines - "^@carbon/icons-react/es/(.*)$": "@carbon/icons-react/lib/$1", - "^@carbon/charts": "identity-obj-proxy", - "^carbon-components-react/es/(.*)$": "carbon-components-react/lib/$1", -}, -``` - -### User Event - -- Invoke `userEvent.setup()` before your component gets rendered in your test spec. Use the methods returned from the invocation rather than calling `userEvent` directly. - -```js -it('trigger some awesome feature when clicking the button', async () => { - const user = userEvent.setup(); - - render(); - - await user.click(screen.getByRole('button', { name: /click me!/i })); - - // ...assertions... -}); -``` - -- Make sure to `await` every `userEvent` function call. - -```js -await user.click(screen.getByRole('button', { name: /click me!/i })); -``` - -### React Router - -This is the most involved of the changes. You really just have to follow the [React Router 5 to 6 upgrade guide](https://reactrouter.com/en/main/upgrading/v5). - -### Carbon -- Change `carbon-components-react` to `@carbon/react` -- Change `carbon-icons` to `@carbon/icons-react` -- Icons now get size from a prop. So change e.g. `` to ``. -- Replace any style rules that use the `bx--` prefix with `cds--`. Often, you can do a global find and replace to achieve this. -- Import Carbon's spacing and type token mixins using `@use '@carbon/styles/scss/spacing` and `@use '@carbon/styles/scss/type` instead of the old `@import "~carbon-components/src/globals/scss/mixins"` import. Also, the signature for referencing mixins changes as follows: - -```scss -// old type mixin -.heading { - @include carbon--type-style("productive-heading-02"); -} - -// new type mixin -.heading { - @include type.type-style("productive-heading-02"); -} - -// old spacing mixin -.formGroup { - margin-bottom: $spacing-02; -} - -// new spacing mixin -.formGroup { - margin-bottom: spacing.$spacing-02; -} -``` - -- Replace deprecated [type tokens](https://github.com/carbon-design-system/carbon/blob/main/docs/migration/v11.md#type-tokens). -- Components no longer use the `light` prop. Wrap your code in a `Layer` component instead. -- Changes to the [Grid](https://github.com/carbon-design-system/carbon/blob/main/docs/migration/v11.md#grid) component might mean your layouts no longer work as they should. Consider using the [FlexGrid](https://github.com/carbon-design-system/carbon/blob/main/docs/migration/v11.md#flexgrid) component instead as well as a [Stack](https://react.carbondesignsystem.com/?path=/story/layout-stack--default) for vertical spacing. - - -If errors or visual inconsistencies remain, reference the [migration docs](https://github.com/carbon-design-system/carbon/blob/main/docs/migration/v11.md). - -### OpenMRS - -Where: - -```typescript -const layout = useLayoutType(); -``` - -Change `layout == "desktop"` to `isDesktop(layout)`. - -### ESLint - -Add following rules to your `.eslintrc`: - -```json -"rules": { - "no-restricted-imports": [ - "error", - { - "patterns": [ - { - "group": ["carbon-components-react"], - "message": "Import from `@carbon/react` directly. e.g. `import { Toggle } from '@carbon/react'`" - }, - { - "group": ["@carbon/icons-react"], - "message": "Import from `@carbon/react/icons`. e.g. `import { ChevronUp } from '@carbon/react/icons'`" - } - ] - } - ] -} -``` - -These patterns will help flag imports that use the older Carbon 10 import notation. diff --git a/docs/offline/dynamic_offline_data.md b/docs/offline/dynamic_offline_data.md deleted file mode 100644 index 2a67986a6..000000000 --- a/docs/offline/dynamic_offline_data.md +++ /dev/null @@ -1,190 +0,0 @@ -# Dynamic Offline Data - -In order to support offline usage of the application, OpenMRS must precache specific resources. -In general, we can differentiate between two offline resource categories: - -1. Static resources -2. Dynamic resources - -An example of *static resources* are compiled artifacts like HTML files, compiled JS files or -images provided by the app shell or arbitrary microfrontend modules. During a user session, -*static resources* don't change - they are always known and can automatically be precached -by the application. - -*Dynamic resources*, on the other hand, depend on choices the user actively makes during a session. -Take, for example, offline patients. Automatically precaching every single patient is not possible -due to the sheer size of data that would have to be stored on a device. To solve this issue, -OpenMRS provides the concept of the *offline patient list* in the offline tools UI. This offline -patient list is a screen that allows a user to actively select and deselect the patients to -be taken offline. Upon selection, each microfrontend module must ensure that it precaches all -patient-related resources that it requires for an offline session. - -The above paragraph only mentioned offline *patient* lists. The concept of an "offline list" exists -for multiple resources though, e.g. for the aforementioned patients, but also offline forms. -In theory, it is possible to maintain dynamic offline lists of any kind of resource. -The OpenMRS `esm-framework` provides a shared API for managing those arbitrary offline lists -and synchronizing the data of the resources tracked on each list. This API is called the -"Dynamic Offline Data API". - -In the following, the concepts and members of the dynamic offline data API are explained. - - -## Dynamic Offline Data Overview - -From a high-level perspective, the dynamic offline data API needs to address two concerns: - -1. Offline list maintenance: Adding/removing/updating items on an offline list of a - specific *resource type*. -2. Offline list synchronization: Enable microfrontend modules to precache the data of a resource on - an offline list. - -These two cornerstones enable the offline data management flow shown in the following diagram, -using the example of making a patient offline-ready: - -![Dynamic Offline Data Flow](./dynamic_offline_data_flow.png) - -As shown in the image above, the raw "lists" are simply stored in a client-side database table. -These table entries only contain the information required for synchronizing a resource, i.e. -its `type` (to distinguish between different resources, e.g. `patient` vs. `form`) and an -`identifier` (which is typically the UUID of the resource). -With this information available, each microfrontend module can register a *synchronization -handler* function which, when invoked, receives the information from the table and can, by using -that information, make any API calls necessary for fetching and precaching the exact data -that this specific microfrontend module needs for working with that patient in offline mode. -One microfrontend module might, for example, need to precache the entire patient resource while -another one might only require a slimmed-down version that only contains the patient's name. - - -## Managing Dynamic Offline Data ("Resource Lists") - -This section describes the API that is provided by `esm-framework` for managing dynamic offline -data lists. This section can only provide an overview. For additional details, see the corresponding -API documentation. - -The following code fragment shows how the API can be used to manage the offline patient list: - -```ts -// In order to manage a list, we require a `type`. -// The type is a convention. Different microfrontend modules must agree on the same type -// in order to access the same lists. -// OpenMRS uses the `patient` type for managing the "Patient" resource. -const patientListType = 'patient'; - -// Adding patients to the list is simple: -// We can call `putDynamicOfflineData` which adds the patient with the given identifier/UUID -// to the offline patient list. -// If a patient already exists on the list, nothing happens (hence the `put`). -const patientUuid = '00000000-0000-0000-0000-000000000001'; -await putDynamicOfflineData(patientListType, patientUuid); - -// In order to synchronize the data of that patient, we can use `syncDynamicOfflineData`. -// This only synchronizes this specific patient. Other patients on the list are *not* synced. -await syncDynamicOfflineData(patientListType, patientUuid); - -// It is also possible to sync *all* entries on a given list at the same time: -await syncAllDynamicOfflineData(patientListType); - -// If we want to remove the patient, we can do so with `removeDynamicOfflineData`. -await removeDynamicOfflineData(patientListType, patientUuid); -``` - -It is also possible to retrieve all entries on a list. This is, for example, required for -displaying the patients who are currently marked as offline patients in a table. - -```ts -// getDynamicOfflineDataEntries can be used to either retrieve all entries or only those of a given type. -const allEntries = await getDynamicOfflineDataEntries(); -const allOfflinePatientEntries = await getDynamicOfflineDataEntries('patient'); - -for (const entry of allEntries) { - console.log( - 'Entry information (not necessarily complete):', - entry.type, - entry.identifier, - entry.users, - entry.syncState, - ); -} -``` - - -## Dynamic Offline Data Synchronization - -The previous section showed how dynamic offline data lists are managed and how synchronization -can be triggered. In this section, the second cornerstone, data synchronization, is shown. - -The core idea of synchronization is that each individual microfrontend module might need to -precache different data for the same resource. At the example of the offline patient, it could -happen that the following microfrontend modules might need to precache the following, different -datasets: - -* **MF 1:** The entire patient resource. -* **MF 2:** A slimmed-down patient resource, only containing the patient's name. -* **MF 3:** Nothing at all. - -Therefore, each module must be allowed to provide its own synchronization logic. -This is centrally enabled by the dynamic offline data API via so-called "synchronization handlers" -(a.k.a. `DynamicOfflineDataHandler`). -A synchronization handler is a simple object providing, at minimum: - -1. A unique ID. -2. Information about which resource `type` it can synchronize. -3. A function `sync` that does the actual precaching/synchronization logic. -4. A function `isSynced` which evaluates whether all required data is precached - *at this very moment*. - -The following code fragment shows how a handler for precaching patient data can be setup: - -```ts -setupDynamicOfflineDataHandler({ - id: 'my-microfrontend-module:patient-handler', - type: 'patient', - - // Optional. - get displayName() { - return i18n('My microfrontend offline patient handler'); - }, - - async isSynced(identifier: string, abortSignal?: AbortSignal) { - return await isUrlInCache(`/ws/rest/v1/patient/${identifier}`, abortSignal); - }, - - async sync(identifier: string, abortSignal?: AbortSignal) { - return await precacheUrl(`/ws/rest/v1/patient/${identifier}`, abortSignal); - }, -}) -``` - -Once registered, the handler is automatically invoked when a `patient` is synchronized via -one of the synchronization functions described in the previous section. - -It is also possible for any microfrontend module to retrieve (and directly use) all currently -registered handlers. This may, for example, be useful for determining which data is currently -synchronized: - -```ts -const allHandlers = getDynamicOfflineDataHandlers(); -const patientHandlers = allHandlers.filter(x => x.type === 'patient'); - -for (const patientHandler of patientHandlers) { - console.log( - 'Is a patient synchronized?', - await patientHandler.isSynced('00000000-0000-0000-0000-000000000001') - ); -} -``` - - -## User-Specific Dynamic Offline Data Entries - -OpenMRS, naturally, supports multiple users. If multiple users use the same device, it is possible -for both users to have different offline lists (for example because they require different patients -in offline mode). -By default, the functions described above automatically use the logged-in user. For example, -`putDynamicOfflineData` automatically stores the UUID of the currently logged-in user in -the entry that is created in the table. - -Despite this, it is also possible to actively declare the user for which an entry should be managed. -By convention, functions which allow specifying a user have the `For` suffix. -`putDynamicOfflineData(type, identifier)` becomes `putDynamicOfflineDataFor(userId, type, identifier)`. -For additional details, see the corresponding API documentation or the codebase itself. diff --git a/docs/offline/dynamic_offline_data_flow.drawio b/docs/offline/dynamic_offline_data_flow.drawio deleted file mode 100644 index 6ee921a2c..000000000 --- a/docs/offline/dynamic_offline_data_flow.drawio +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/docs/offline/dynamic_offline_data_flow.png b/docs/offline/dynamic_offline_data_flow.png deleted file mode 100644 index ccaf3242d..000000000 Binary files a/docs/offline/dynamic_offline_data_flow.png and /dev/null differ diff --git a/docs/offline/offline.md b/docs/offline/offline.md deleted file mode 100644 index 366d51d9f..000000000 --- a/docs/offline/offline.md +++ /dev/null @@ -1,129 +0,0 @@ -# Offline Mode - -The OpenMRS 3.0 SPA solution uses [Workbox](https://developers.google.com/web/tools/workbox) to easily create an extensible service worker implementation to support offline capabilities. - -There are three facets that are unique to this approach: - -1. Each component (page / extension) can be marked as offline (or online) capable. By default every component is online capable, but not offline capable. -2. Each frontend module can declare certain (HTTP) calls to be cacheable. -3. Each frontend module can queue items to be processed when the application is back online. - -## Registration - -For instance, the following snippet shows a typical extension registration: - -```js -function setupOpenMRS() { - return { - extensions: [ - { - id: "foo", - load: getAsyncLifecycle(() => import("./foo.component")), - }, - ], - }; -} -``` - -Now regarding the extension's online / offline capabilities this is equivalent to: - -```js -function setupOpenMRS() { - return { - extensions: [ - { - id: "foo", - online: true, - offline: false, - load: getAsyncLifecycle(() => import("./foo.component")), - }, - ], - }; -} -``` - -Which means that the component can be used online, but will not be available when offline. If we would want to provide offline support a simple `true` would be sufficient: - -```js -function setupOpenMRS() { - return { - extensions: [ - { - id: "foo", - offline: true, - load: getAsyncLifecycle(() => import("./foo.component")), - }, - ], - }; -} -``` - -Alternatively, we might want to specify some services to be injected depending on the connectivity mode: - -```js -function setupOpenMRS() { - return { - extensions: [ - { - id: "foo", - online: { - onSave: OnlineSaveService, - }, - offline: { - onSave: OfflineSaveService, - }, - load: getAsyncLifecycle(() => import("./foo.component")), - }, - ], - }; -} -``` - -In this case the extension is available independent of the connectivity. In the offline case the `onSave` prop is defined to the value of a `OfflineSaveService` variable, while in the online case the same prop will be set to `OnlineSaveService`. This way the behavior of the actual component will be adjusted to the environment. - -## Synchronization - -When offline there are two aspects that might be handled with higher priority: - -1. How to deal with items that would need to be sent to the backend right now (e.g., submitting a form) -2. How to deal with items that should have been send to the backend previously (e.g., previously submitted forms) - -For (1) you can just place such items in a queue. Ideally, you'd just recognize the action either via a dedicated offline service or via the service worker (i.e., HTTP request). Either way the `queueSynchronizationItem` function from `@openmrs/esm-framework` (more specifically `@openmrs/esm-offline`) will help you to do that. - -Example: - -```js -queueSynchronizationItem('my-offline-data', { - // content of item to queue -}); -``` - -Once items are in this queue it makes sense to start thinking about (2). For this, you should register a queue processor via the `setupOfflineSync` function: - -```js -function setupOpenMRS() { - setupOfflineSync('my-offline-data', ['other-offline-data'], async item => { - // process the item, e.g., send it to the backend - }); - - // ... -} -``` - -The previous snippet creates a synchronization processor for items of type `my-offline-data`. These items have a (potential) dependency to other items of type `other-offline-data`. Therefore, these items will be processed *before* the `my-offline-data` items. - -## State Machine - -Offline is in general a bit of a difficult topic. Most importantly, there are multiple states associated with it. Not only is the specific behavior dependent on the state of the network (online, offline), but also of the user (wants or does not want to be offline ready) and the application (already offline ready or still trying to be offline ready or updating the files required to go offline). - -The following state diagram shows the different entry points and their possible transitions. - -![Map of OpenMRS offline capability and state transitions](./openmrs-offline-state.png) - -Importantly, the user has 5 possible entry points to the application: - -- Fresh state (did not opt-in) -- Downloading assets state (did opt-in, but never finished) -- Ready state (did opt-in, no changes since last use), followed by a data sync -- Outdated state (did opt-in, changes to assets since last use) -- Offline state (did opt-in, no Internet connectivity) diff --git a/docs/offline/openmrs-offline-state.drawio b/docs/offline/openmrs-offline-state.drawio deleted file mode 100644 index d07bc11af..000000000 --- a/docs/offline/openmrs-offline-state.drawio +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/docs/offline/openmrs-offline-state.png b/docs/offline/openmrs-offline-state.png deleted file mode 100644 index 6dc3e9450..000000000 Binary files a/docs/offline/openmrs-offline-state.png and /dev/null differ diff --git a/docs/openmrs.svg b/docs/openmrs.svg deleted file mode 100644 index ec5be81e8..000000000 Binary files a/docs/openmrs.svg and /dev/null differ diff --git a/docs/under_the_hood/architecture.md b/docs/under_the_hood/architecture.md deleted file mode 100644 index 46079c881..000000000 --- a/docs/under_the_hood/architecture.md +++ /dev/null @@ -1,32 +0,0 @@ -# Architecture Overview - -Below you'll find an architecture diagram connecting the parts from the core application with the (frontend) ecosystem. - -![Architecture Diagram](./architecture.png) - -## Startup - -When the application loads it goes through the following steps. - -1. Preloads the importmap and related files -2. Evaluates the stylesheets and scripts -3. Configures the SPA application -4. Installs the service worker for offline capabilities -5. Loads and evaluates the different frontend modules from the importmap -6. Loads and validates the provided configurations -7. Sets up the offline capabilities and synchronization features -8. Renders the UI - -Once the UI is rendered the content is exclusively coming from the different frontend modules. The app shell is solely an orchestration layer. - -## Frontend module anatomy - -Frontend modules use [dynamic imports](https://webpack.js.org/guides/code-splitting/) -to split into a "front bundle" and content bundles. -The front bundle begins at the entry point, which the -[webpack config](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/tooling/openmrs/default-webpack-config.js) -takes from the -[main entry of package.json](https://github.com/openmrs/openmrs-esm-template-app/blob/69b0f7a3ef3e79e9851fc0621e8b6c8311e7e6d7/package.json#L7) -and which is usually `src/index.ts`. This bundle should be small and not contain much -code. The functions `getAsyncLifecycle` and `getLifecycle` use dynamic imports -to import the frontend module content only when it is needed. diff --git a/docs/under_the_hood/architecture.png b/docs/under_the_hood/architecture.png deleted file mode 100644 index 0139e9e65..000000000 Binary files a/docs/under_the_hood/architecture.png and /dev/null differ diff --git a/docs/under_the_hood/extensions.md b/docs/under_the_hood/extensions.md deleted file mode 100644 index a117f4bad..000000000 --- a/docs/under_the_hood/extensions.md +++ /dev/null @@ -1,69 +0,0 @@ -# How the Extension System Works - -For the extension system to work four things exist: - -1. A generic component model with a defined lifecycle and loading mechanism -2. A way to define where extensions should be placed (so called "slot") -3. A way to define an extension coupling it to (1) -4. A configuration for assigning available extensions from (3) to slots (2) - -Let's look at these four things. - -## Behind the Scenes - -For (1) the parcel mechanism of single-spa is used. - -For (2) you can use the `registerExtensionSlot` function together with `renderExtension`. For frameworks such as React helper components may exist (e.g., `ExtensionSlot`). - -For (3) you can define an extension in the return value from the `setupOpenMRS` function. An example for this: - -```js -function setupOpenMRS() { - return { - extensions: [ - { - id: "foo", - load: getAsyncLifecycle(() => import("./foo.component")), - }, - ], - }; -} -``` - -As a shorthand for (4) you could already specify a target slot via the `slot` property in the previous code snippet. Without that convenience way you'd still be able to register it programmatically using `attach`: - -```js -// attaches an extension "foo" to a slot "foo-slot" -attach('foo-slot', 'foo'); -``` - -Generally, though this is either done at initialization time as a default, or explicitly via a user-provided configuration. The only exception can be found with "dynamic" (or "special") slots. One example in this area is the workspace of the patient chart frontend module. - -## Extensions and Slots - - An extension can be in any of the following four states with respect to an extension slot: - - _attached_ (set via code using `attach` and `detach`) - - _configured_ (set via configuration using: `add` and `remove`) - - _assigned_ (computed from attached and configured) - - _connected_ (computed from assigned using connectivity and online / offline) - -## Rendering - -Extensions are rendered by following their exported lifecycle functions. The `getAsyncLifecycle` function from `@openmrs/esm-react-utils` is a convenience layer that already exports these lifecycle functions wired together with `single-spa-react`. - -In a nutshell: - -1. When the component should be rendered the `load` function is evaluated - in case of a `Promise` (via the asynchronously loaded `import` function) this first waits for the component to be available -2. The component is placed into its lifecycle functions provided by `single-spa-react` -3. The lifecycle functions `bootstrap`. `mount`. `unmount`, and `update` are exported - -These lifecycle functions are not magic - theoretically you could write them on your own, however, since the single-spa ecosystem already provides convenience wrappers such as `single-spa-react` for many frameworks we don't recommend it. - -To actually render also two more things need to be considered: - -1. Does the extension render in offline or online mode, and which mode is the browser in? -2. What properties should be passed to the component which is rendered? - -The answer to (1) is found in `navigator.onLine`. Only if `offline` was set to `true` or some object the component renders in offline mode. Likewise, if `online: false` was supplied the component will not render in online mode. - -The answer to (2) are the `meta` properties along with the extension's context (e.g., what slot it is rendered to) and its injected services. The injected services are defined via `online` or `offline`. In case of `true` no services are injected. In case of an object the provided key-value pairs are interpreted as services, which should be injected depending on the connectivity case. diff --git a/docs/under_the_hood/migration_guide.md b/docs/under_the_hood/migration_guide.md deleted file mode 100644 index cad18a1fd..000000000 --- a/docs/under_the_hood/migration_guide.md +++ /dev/null @@ -1,217 +0,0 @@ -# Migration Guide for Core Dependencies - -## Motivation - -Presently, the OpenMRS 3 frontend uses core dependencies that are several years out of date. Specifically, we are looking to migrate the following dependencies to the latest available versions to be able to leverage new features, fixes, API improvements, and bundle size savings: - -- React -- React Router -- Carbon Design System -- Jest / React Testing Library / userEvent - -## Preamble - -* React 18 adds in concurrency, automatic render batching, and makes `` a first class citizen leading to **faster loading** of elements due to the decoupling of UI rendering and API fetching. -* This compounds with React Router 6 which can enable a **several times speedup** of widget load times when implemented correctly. -* Updates to Carbon v11 keep the UI modern for designers, the package and API cleanup makes it faster to use for developers, and smaller bundle sizes mean a faster UI for customers. -* Jest now supports test sharding which means our tests can run faster than ever. - -## Dependencies - -### React -We're currently on React 16.14 (released in October 2020) across all our repositories. React 18, released in March 2022, brought out-of-the-box improvements like automatic batching, new APIs like `startTransition`, and streaming server-side rendering with support for `Suspense`. Notably: - - - `Suspense` is now a first-class citizen. This will allow us to unblock component rendering while API calls decide to load. - - Children are no longer implicitly declared as props of type `React.ReactNode`. They have to be declared explicitly. - - Automatic batching is enabled by default which helps reduce the number of renders. - - The new concurrent render means that the UI can immediately respond to user input even if it's in the middle of a large render task -. This should result in a more fluid user experience. - - The concurrent render also adds support for reusable state so that previously rendered sections of the UI can be added back in. - -Important resources: [React 18 release notes](https://reactjs.org/blog/2022/03/29/react-v18.html) and [Migration Guide](https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html). - -### React Router -We're currently on React Router v5.3 (released in March 2019). React Router 6 introduces several powerful new features, as well as improved compatibility with the latest versions of React. Notably: - - - All ``s and ``s inside a `` are relative. This leads to leaner and more predictable code in `` and ``. - - Routes are chosen based on the best match instead of being traversed in order. This avoids bugs due to unreachable routes because they were defined later in the `` - - Routes are allowed to be nested instead of being spread out. This allows all routes to be easily seen at once. Meanwhile, nested routes can still be loaded dynamically via `React.lazy`. - - Route children can only be nested routes; rendered elements need to go in `element`. This simplifies reading the routing structure leading to fewer bugs. - - Switches to using React Elements instead of Components. This combined with React 18's native Suspense makes for very quick rendering. - - Discontinued use of regex routes leads to cleaner route syntax and drops `path-to-regexp` dependency reducing bundle size. -`useNavigate` is more suspense friendly than the old `useHistory`. This provides a smoother experience when a user interaction needs to interrupt a pending route transition. - -Important resources: [Migration Guide](https://reactrouter.com/en/main/upgrading/v5). - -### Carbon Design System -We're currently on Carbon v10 (released in December 2020). Carbon 11 shipped with a host of feature improvements, fixes and enhancements to the developer experience. Notably: - - - The [Design Kit](https://carbondesignsystem.com/migrating/guide/design) has: - - Updated concepts for Notifications, Tooltip, Tabs, Sizing, Type tokens, and Color tokens. - - New Popover and Toggletip features. - - The UI shell is now theme-able to support light and dark mode. - - The [React packages](https://carbondesignsystem.com/migrating/guide/develop) have been simplified from 4 packages down to one: `@carbon/react`. - - Sass styles are now accessible directly from `@carbon/react/scss` - - 90% decrease in compilation time of Styles from Carbon. - - `size` is now consistent across all components. - - Uses CSS Grid (2 dimensional) by default instead of flexbox grid (1 dimension). (See [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Relationship_of_Grid_Layout) for more info.) - - Same IBM Design Language. Does not require any brand-driven product redesigns. - -Important resources: [Carbon v11 release notes](https://v10.carbondesignsystem.com/whats-happening/v11-release/) / [Migration Guide](https://github.com/carbon-design-system/carbon/blob/main/docs/migration/v11.md) / [Carbon v11 announcement blog post](https://medium.com/carbondesign/carbon-v11-72ace7fac01f). - -### Jest / Testing Library React / userEvent - -We're currently on the following versions: - -- Jest v26 -- Testing Library React v10 -- userEvent v12 - -Jest v28 introduced some long-requested features such as: - - - Support for sharding a test run across multiple machines. Upon leveraging this feature, Jest's own test suite on CI went from about 10 minutes to 3 on Ubuntu, and on Windows from 20 minutes to 7. - - In Jest 28, jest-environment-node will now automatically provide node and node-addons conditions, while jest-environment-jsdom will provide the browser condition. - - The ability to customize the behavior of fake timers. - - A new GitHub Actions reporter, which will use annotations to print test errors inline. - Vastly improved type annotations. - -Testing Library React v13 notably adds support for React 18 and drops support for older versions of React. - -userEvent v14 adds various features and fixes. Notably: - - - The new `userEvent.setup` allows you to prepare the document for interacting per user-event APIs right when you set up your test. - - v14 introduces a new `userEvent.pointer` API that enables users to simulate the events for user interaction per touchscreen or stylus. - - APIs in v14 now always return a promise and are therefore asynchronous. As such, they have to be await-ed. - -Important resources: [Jest 28 release notes](https://jestjs.io/blog/2022/04/25/jest-28) / [Testing Library React 13 changelog](https://github.com/testing-library/react-testing-library/releases/tag/v13.0.0) / [userEvent 14 changelog](https://github.com/testing-library/user-event/releases/tag/v14.0.0). - -## Strategy - -Because all versions are pointing to `next` version of `esm-framework`, `openmrs`, and `webpack-config` -we will need to manually point to a 4.0 shell. Because this is not addressable using tag syntax and the 4.0 -branch is not found on the package manager npmjs.com we need to pull the branch locally, build it into -consumable .js files and serve the .js files from the app shell. Once the shell is ready to serve the -backbone of the app we can start to add microfrontends to it one at a time through serving those microfrontends -locally and pointing to the local server via import map. With the core and apps running on the same local -browser, we can start to integrate the modules together using the new libraries. - -## Procedure - -1. Get latest openmrs-esm-core branch ([4.0](https://github.com/openmrs/openmrs-esm-core/tree/main)) - This is the branch where new versions of the core libraries are specified (thanks to a prior effort by Brandon) - -2. Build the App Shell – This compiles the core source files into static webpack JS files which can be served. This will automatically bundle all of the openmrs-esm-core/packages/apps/ into a dist folder. The yarn run:shell at the end serves this app shell on 8080 and will be our main entry into the app. - - - In openmrs-esm-core: `cd packages/tooling/webpack-config` then `yarn build` - - - In openmrs-esm-core: `cd packages/tooling/openmrs` then `yarn build` - - - In openmrs-esm-core: `cd packages/shell/esm-app-shell` then `yarn build` - - - In openmrs-esm-core: `yarn run:shell` - - At this point there should be no errors in the terminal. The web server should start in your terminal and the app shell should load in your browser, although most of the microfrontends will be missing and should tell you this in the browser console. - -3. Create links to all core packages (if you haven't already). This will make them available in the yarn system to do a symlink shorthand inside of node_modules - - * In each openmrs-esm-core/packages/[apps, framework, shell/esm-app-shell/, tooling] repo create a yarn link like: `yarn link` - - -4. Link child package to the up-to-date openmrs-esm-core. This will tell them to use the local version rather than downloading "next" from [npmjs.com](npmjs.com). - - * In openmrs-esm-patient-management `yarn link @openmrs/esm-app-shell` (example of doing one at a time) - - * In openmrs-esm-patient-management `yarn link @openmrs/esm-api @openmrs/esm-config @openmrs/esm-extensions @openmrs/esm-globals @openmrs/esm-react-utils @openmrs/esm-styleguide @openmrs/esm-breadcrumbs @openmrs/esm-error-handling @openmrs/esm-framework @openmrs/esm-offline @openmrs/esm-state @openmrs/esm-utils` (do everything at once to save time) - - * You can check that this is working by using patient-management% `ls –l node_modules@openmrs`. This should show symlinks instead of the usual directories containing the source code. - -5. Copy the contents of openmrs-esm-core webpack-config build files ( in `webpack-config/dist/`) to the the target repo. We need to do it this way because symlinks will not work with webpack config - - * Copy the files over: In esm-patient management `cp -r /path/to/esm-core/packages/tooling/webpack-config/dist/* node_modules/@openmrs/webpack-config/dist/ ` - - * Point the individual mocrofrontend up to the repo's version of webpack config: Change the contents of `openmrs-esm-patient-management/packages/esm-patient-search-app/webpack.config.js` to be: - >`module.exports = require('../../node_modules/@openmrs/webpack-config'); ` - - * Using `ls -l node_modules/@openmrs` in patient-management again we should see all the previous symlinks and now `webpack-config` which is a directory instead - -6. Upgrade React within a repo. We do this now so that the types can resolve properly. - - * [Add React](https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html). In openmrs-esm-patient-management: `yarn add -D -W react react-dom` - - * Add types. In openmrs-esm-patient-management: `yarn add -D -W @types/react@latest @types/react-dom@latest @types/react-router-dom@latest` - - * Update microfrontend reference. In patient-search: `yarn add --peer react` - -7. Serve the specific app you want the app shell to find. This will start another local server where this module will be active, referencing by this point hopefully all the correct versions of the core. - - * In openmrs-esm-patient-management `cd packages/esm-patient-search-app` then `yarn serve` - - * This will start a webpack dev server. Your microfrontend will be available at something like `[webpack-dev-server] Loopback: http://localhost:8081/` - -8. Add to import map overrides to the app shell ([See the developer docs](http://o3-dev.docs.openmrs.org/#/getting_started/setup)). In the browser where app shell is running (`http://localhost:8080/openmrs/spa/home`): - - * Enable devtools by setting `localStorage.setItem('openmrs:devtools', true)` in your console browser - - * Open the devtools by clicking grey box in lower right of page. - - * Find 'patient-search' -> click on it to edit entry - - * Type `8081` to set import url to "`//localhost:8081/openmrs-esm-patient-search-app.js`" (Make corrections to this based on what your port was from the previous step) - - * Refresh the page. You should see the app running - -At this point you will have a local version of the core app shell running, and one of the microfrontends served locally on a different server. The app shell is then pointed to the second server thanks to the importmap override done with the help of the devtools. Now you can really start the integration effort in earnest. Let's upgrade the rest of the packages. - -9. Upgrade Carbon to v11 - - * Follow the upgrade guide [here](https://carbondesignsystem.com/migrating/guide/develop). - -10. Upgrade React Router Dom to v6 - - * Follow the upgrade guide [here](https://reactrouter.com/en/main/upgrading/v5) - -11. Upgrade Jest to v28 - -12. Upgrade Testing Library React to v13 - -13. Upgrade userEvent to v13 - -Congratulations! You have upgraded one microfrontend to use the latest libraries. - -Repeat steps 4-8 for all packages you want to serve, upgrade, and re-integrate at the same time. - -## Troubleshooting & Tips - -The following commands can help you figure out what is happening when you run into trouble: - -* `yarn list @types/react` to see conflicting type definitions of react. - -* `yarn why` to see why a particular module is loaded. - -* `ls –l node_modules/@openmrs` to see which packages are symlinks or downloaded files. - -* When in doubt destroy node_modules folder and rebuild by running `yarn`. - -* When in doubt start from the beginning with `git status` to check you are on the right branch and `git pull` to find the latest updates. - -* `yarn upgrade` for this migration is tricky since we technically don't want the latest of the core or framework packages listed on npmjs.com. We are trying to use our own versions. This will help upgrade other packages (to within their current semver specification in package.json) if they need it, but likely you will need to re-link the esm-framework folders - -* Easy to revert all symlinks by destroying node_modules and running `yarn` - -## Releasing a major pre-release version of esm-core - -To prepare other frontend modules it is helpful to have pre-releases of new major versions. -For example, before 4.0.0 is released, 4.0.0-pre.1 is released so that frontend -module maintainers can install that version and fix everything that needs to be -fixed. - -To release a version like `4.0.0-pre.5`: -1. Make sure the release branch is in good shape. `yarn` and `yarn verify`. -2. Build everything. `yarn turbo run build`. -3. Version bump. `yarn lerna version`. Select "Custom Version" and type `4.0.0-pre.5`. -4. Publish - - In the past I have published with `yarn lerna publish from-package`. However, this - tags the published version as `latest`, which is definitely not what you want. I have - corrected this by manually running `npm dist-tag add @openmrs/esm-abc@3.4.0 latest` - for everything published, which is a very annoying thing to have to do. - - You can try doing `yarn lerna publish from-package --dist-tag=4.0`; I think `4.0` is - a reasonable tag for this. There is no way to publish without tagging at all. diff --git a/docs/under_the_hood/squad_devops.md b/docs/under_the_hood/squad_devops.md deleted file mode 100644 index 45cbb38ec..000000000 --- a/docs/under_the_hood/squad_devops.md +++ /dev/null @@ -1,19 +0,0 @@ -# Squad DevOps and CI - -The CI server at dev3.openmrs.org is a community resource, hosted on the OpenMRS infrastructure, though the actual ESMs are stored in a Digital Ocean space kindly provided by AMPATH. Every time a commit is made to -`main` in a community-managed frontend module repository, GitHub Actions -deploys the built package to Digital Ocean and updates the importmap so that the CI server always runs the latest version of all modules. - -Every time a GitHub release is created in a community-managed frontend module repository, GitHub Actions releases a new NPM package for each frontend module (tag: "latest"). The difference to an ordinary commit to `main` is that these only create preview packages (tag: "next"). - -[OpenMRS Bamboo](https://ci.openmrs.org/allPlans.action) is used only for -[openmrs-module-spa](https://github.com/openmrs/openmrs-module-spa/). Its jobs -are [here](https://ci.openmrs.org/browse/SM). - -Its CI and release process is the same as all other OpenMRS modules. - -## Docker Containers -Dockerized containers of the 3.x Reference Application are available here: -* https://hub.docker.com/r/openmrs/openmrs-reference-application-3-frontend -* https://hub.docker.com/r/openmrs/openmrs-reference-application-3-backend -* https://hub.docker.com/r/openmrs/openmrs-reference-application-3-gateway diff --git a/docs/under_the_hood/tooling.md b/docs/under_the_hood/tooling.md deleted file mode 100644 index 36843497b..000000000 --- a/docs/under_the_hood/tooling.md +++ /dev/null @@ -1,61 +0,0 @@ -# How the Developer Tooling Works - -## `openmrs develop` vs `openmrs debug` - -Generally, `openmrs develop` is the one you will want to use. It uses -build artifacts from the -version of `@openmrs/esm-framework` installed in the `openmrs` package -itself. It uses [webpack-dev-server](https://webpack.js.org/configuration/dev-server/) -to serve the application in the directory where it is run. It uses an -Express server to serve a custom import map and index file, and to -proxy the backend. - -`openmrs debug` uses the `@openmrs/esm-app-shell` webpack config. Therefore -it does approximately the same thing as doing `yarn run:shell` from -`openmrs-esm-core`, but that it also works to run the frontend module -in the directory where it is run. It will generally be slower than -`openmrs develop`, but may have some advantages related to debugging. - -## Avoiding Webpack - -If you want to use another bundler or build tool, you can still use the -`openmrs` tooling. You just have to add a special section in the `package.json`. - -Consider the following example from the `@openmrs/esm-form-entry-app` package contained in -the `openmrs-esm-patient-chart` monorepo: - -```json -{ - "scripts": { - "start": "openmrs develop", - "serve": "ng serve --port 4200 --live-reload true" - }, - "openmrs:develop": { - "command": "npm run serve", - "url": "http://localhost:4200/openmrs-esm-form-entry-app.js" - }, - // ... -} -``` - -This one will use `ng serve --port 4200 --live-reload true` when `openmrs develop` is used. -The URL `http://localhost:4200/openmrs-esm-form-entry-app.js` will be added to the import map -used for debugging. - -### Updating the Default Import Map Used by the `openmrs` Tooling - -By default, the `openmrs` tooling uses the `importmap.json` file provided by the -`@openmrs/esm-app-shell` package to serve its default set of frontend modules. -This can lead to issues, because this `importmap.json` file, in most cases, lists -outdated versions. - -To update the `importmap.json` file, you can open the `importmap.json` file in the `esm-core` -repository and replace any outdated reference with a newer one. -At the moment, we are keeping the `importmap.json` file in sync with the import map -used by the CI environment. Updating the file is therefore straightforward: -You can navigate to [https://spa-modules.nyc3.digitaloceanspaces.com/import-map.json](https://spa-modules.nyc3.digitaloceanspaces.com/import-map.json) -(which is the most recent import map used by the CI environment) and copy the JSON data to the -local `importmap.json` file. -Note that the import map from the CI environment often contains values which are not required -(for example, at the time of writing this documentation, it references both `@openmrs/esm-outpatient-app` -**and** `@openmrs/esm-outpatient-app/` - the latter, i.e. the one ending with a `/`, is unnecessary and can be removed). diff --git a/docs/under_the_hood/translations.md b/docs/under_the_hood/translations.md deleted file mode 100644 index 72b32c13d..000000000 --- a/docs/under_the_hood/translations.md +++ /dev/null @@ -1,26 +0,0 @@ -# How Translation Works - -There are three places in frontend code that relate to translation/i18n. They are: - -- The app shell, in [locale.ts](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/shell/esm-app-shell/src/locale.ts). - This sets up i18next and react-i18next. -- The [OpenMRS Component Decorator](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#openmrscomponentdecorator). - This decorator is generally wrapped around root components by - [getLifecycle](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#getasynclifecycle)/[getAsyncLifecycle](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-framework/docs/API.md#getasynclifecycle)— - it is generally not used directly by frontend modules. - This component provides the connection between the i18next "backend" - (still on the client side, despite the name) and the frontend module it wraps. -- The frontend module, which uses the `t` function or `` component from react-i18next - to produce rendered content. Upon each commit, [i18next-parser](https://github.com/i18next/i18next-parser) parses the frontend module code and automatically extracts translation keys and strings into locale-specific translation files found in the `translations` directory of a frontend module. - -## Language Detection - -The identify the currently used language the following steps are performed: - -1. Looking at the site's current query string (`lang`) -2. Looking at the user's language preference placed on the `html` tag, which is synced with the backend -3. Looking at the site's language preference stored in `localStorage` (key `i18nextLng`) -4. Looking at the browser's language preference via `navigator` (trying properties such as `languages`, `userLanguage`, `language`) -5. Falling back to English ("en") - -In any case changing the language on the fly is as simple as changing the `lang` attribute on the site's `documentElement`. diff --git a/package.json b/package.json index 0daa12a49..aa8f40ba7 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "packages/tooling/*" ], "scripts": { - "run:docs": "docsify serve docs", "run:omrs": "openmrs", "run:shell": "turbo run watch --filter=@openmrs/esm-app-shell --parallel --no-cache", "ci:publish-next": "yarn workspaces foreach --all --topological --exclude @openmrs/esm-core npm publish --access public --tag next", @@ -42,7 +41,6 @@ "autoprefixer": "^10.4.2", "classnames": "^2.3.2", "cross-env": "7.0.2", - "docsify": "^4.12.2", "dotenv": "^16.0.3", "eslint": "^7.10.0", "eslint-config-prettier": "^6.11.0", diff --git a/packages/apps/esm-devtools-app/README.md b/packages/apps/esm-devtools-app/README.md index 781fcd726..5cb2308ad 100644 --- a/packages/apps/esm-devtools-app/README.md +++ b/packages/apps/esm-devtools-app/README.md @@ -12,15 +12,10 @@ This is useful for developing features and for debugging problems, because you can tell the browser to load one module from your localhost while keeping all other modules using the default version in the import map. -Usage is documented both below and in the -[Import Map Overrides](http://o3-dev.docs.openmrs.org/#/getting_started/setup?id=import-map-overrides) -section of the developer documentation. - ## Installation / Usage The devtools are registered as a [single-spa application](https://single-spa.js.org/docs/building-applications/) - that creates a gray or red rectangular button near the bottom right of the screen. The rectangular button will only show up if you run the following command in the browser console: diff --git a/packages/framework/esm-config/src/module-config/module-config.ts b/packages/framework/esm-config/src/module-config/module-config.ts index 35f8ab437..da717422a 100644 --- a/packages/framework/esm-config/src/module-config/module-config.ts +++ b/packages/framework/esm-config/src/module-config/module-config.ts @@ -133,7 +133,7 @@ function computeExtensionConfigs( * configuration system how the module can be configured. It specifies * what makes configuration valid or invalid. * - * See [Configuration System](http://o3-dev.docs.openmrs.org/#/main/config) + * See [Configuration System](https://o3-docs.openmrs.org/docs/configuration-system) * for more information about defining a config schema. * * @param moduleName Name of the module the schema is being defined for. Generally @@ -159,7 +159,7 @@ export function defineConfigSchema(moduleName: string, schema: ConfigSchema) { * The schema tells the configuration system how the module can be configured. * It specifies what makes configuration valid or invalid. * - * See [Configuration System](http://o3-dev.docs.openmrs.org/#/main/config) + * See [Configuration System](https://o3-docs.openmrs.org/docs/configuration-system) * for more information about defining a config schema. * * @param extensionName Name of the extension the schema is being defined for. diff --git a/packages/framework/esm-framework/docs/API.md b/packages/framework/esm-framework/docs/API.md index 1f89cbec9..bbbd68a46 100644 --- a/packages/framework/esm-framework/docs/API.md +++ b/packages/framework/esm-framework/docs/API.md @@ -1729,7 +1729,7 @@ This defines a configuration schema for a module. The schema tells the configuration system how the module can be configured. It specifies what makes configuration valid or invalid. -See [Configuration System](http://o3-dev.docs.openmrs.org/#/main/config) +See [Configuration System](https://o3-docs.openmrs.org/docs/configuration-system) for more information about defining a config schema. #### Parameters @@ -1761,7 +1761,7 @@ in which it is defined. The schema tells the configuration system how the module can be configured. It specifies what makes configuration valid or invalid. -See [Configuration System](http://o3-dev.docs.openmrs.org/#/main/config) +See [Configuration System](https://o3-docs.openmrs.org/docs/configuration-system) for more information about defining a config schema. #### Parameters @@ -3899,7 +3899,7 @@ ___ ▸ **ExtensionSlot**(`__namedParameters`): `Element` -An [extension slot](https://o3-dev.docs.openmrs.org/#/main/extensions). +An [extension slot](https://o3-docs.openmrs.org/docs/extension-system). A place with a name. Extensions that get connected to that name will be rendered into this. diff --git a/packages/framework/esm-react-utils/src/ExtensionSlot.tsx b/packages/framework/esm-react-utils/src/ExtensionSlot.tsx index 3fb586171..65c637099 100644 --- a/packages/framework/esm-react-utils/src/ExtensionSlot.tsx +++ b/packages/framework/esm-react-utils/src/ExtensionSlot.tsx @@ -30,7 +30,7 @@ function defaultSelect(extensions: Array) { } /** - * An [extension slot](https://o3-dev.docs.openmrs.org/#/main/extensions). + * An [extension slot](https://o3-docs.openmrs.org/docs/extension-system). * A place with a name. Extensions that get connected to that name * will be rendered into this. * diff --git a/yarn.lock b/yarn.lock index 43039b339..173cd5170 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2682,7 +2682,6 @@ __metadata: autoprefixer: "npm:^10.4.2" classnames: "npm:^2.3.2" cross-env: "npm:7.0.2" - docsify: "npm:^4.12.2" dotenv: "npm:^16.0.3" eslint: "npm:^7.10.0" eslint-config-prettier: "npm:^6.11.0" @@ -7909,22 +7908,6 @@ __metadata: languageName: node linkType: hard -"docsify@npm:^4.12.2": - version: 4.12.2 - resolution: "docsify@npm:4.12.2" - dependencies: - dompurify: "npm:^2.3.1" - marked: "npm:^1.2.9" - medium-zoom: "npm:^1.0.6" - opencollective-postinstall: "npm:^2.0.2" - prismjs: "npm:^1.23.0" - strip-indent: "npm:^3.0.0" - tinydate: "npm:^1.3.0" - tweezer.js: "npm:^1.4.0" - checksum: 2af7a57fb2aa542e2929f42c1ecb6ac2aeb2953944da8efa354cf49fb5c22fb9ffd18211872d332eed8e92d9be3d6b506851c4cf742a19546c5257a09d256666 - languageName: node - linkType: hard - "doctrine@npm:^3.0.0": version: 3.0.0 resolution: "doctrine@npm:3.0.0" @@ -8049,13 +8032,6 @@ __metadata: languageName: node linkType: hard -"dompurify@npm:^2.3.1": - version: 2.4.0 - resolution: "dompurify@npm:2.4.0" - checksum: 24c488b40129c6d1f28fa46fef780b68ef5b7d966b6dfa15c6776bc3aa5ebae588ba44d5b52564721708eeb16de338f22ef4a4d8dff8f2ce7a5b8d2c1ea25083 - languageName: node - linkType: hard - "domutils@npm:^1.7.0": version: 1.7.0 resolution: "domutils@npm:1.7.0" @@ -11920,15 +11896,6 @@ __metadata: languageName: node linkType: hard -"marked@npm:^1.2.9": - version: 1.2.9 - resolution: "marked@npm:1.2.9" - bin: - marked: bin/marked - checksum: ad88455b9e2f0ffc44fa3bb89efc3aed7c0cdcd3af874ebff05d86ce9fef1593ac33bb7a582cbc8925acb802bad80c4480f759e255da39f3b5a99f8759f5ce74 - languageName: node - linkType: hard - "marked@npm:^4.0.16": version: 4.1.1 resolution: "marked@npm:4.1.1" @@ -11969,13 +11936,6 @@ __metadata: languageName: node linkType: hard -"medium-zoom@npm:^1.0.6": - version: 1.0.6 - resolution: "medium-zoom@npm:1.0.6" - checksum: d2eee0dc4089ab1362073e22e22aa3763199b8fcd5bf58adeeafbc7e7d7dfa2adbf4d96623d8c230ba9d7d175c27bd65d0517ad8b3d04dac502e3bfdf91a511a - languageName: node - linkType: hard - "memfs@npm:^3.1.2, memfs@npm:^3.4.1, memfs@npm:^3.4.3": version: 3.4.7 resolution: "memfs@npm:3.4.7" @@ -12787,15 +12747,6 @@ __metadata: languageName: node linkType: hard -"opencollective-postinstall@npm:^2.0.2": - version: 2.0.3 - resolution: "opencollective-postinstall@npm:2.0.3" - bin: - opencollective-postinstall: index.js - checksum: 69d63778087cd10c9d707d9ed360556780cfdd0cd6241ded0e26632f467f1d5a064f4a9aec19a30c187770c17adba034d988f7684b226f3a73e79f44e73fab0e - languageName: node - linkType: hard - "opener@npm:^1.5.2": version: 1.5.2 resolution: "opener@npm:1.5.2" @@ -14090,13 +14041,6 @@ __metadata: languageName: node linkType: hard -"prismjs@npm:^1.23.0": - version: 1.29.0 - resolution: "prismjs@npm:1.29.0" - checksum: 2080db382c2dde0cfc7693769e89b501ef1bfc8ff4f8d25c07fd4c37ca31bc443f6133d5b7c145a73309dc396e829ddb7cc18560026d862a887ae08864ef6b07 - languageName: node - linkType: hard - "proc-log@npm:^2.0.0, proc-log@npm:^2.0.1": version: 2.0.1 resolution: "proc-log@npm:2.0.1" @@ -16149,13 +16093,6 @@ __metadata: languageName: node linkType: hard -"tinydate@npm:^1.3.0": - version: 1.3.0 - resolution: "tinydate@npm:1.3.0" - checksum: 43574835768e19b1b566aae9f018a363c86e40bec5355f331d9dbf18a901ca4b737a9b863ed5e8f95ef07a0cac87d2c2ae68efd19b8b5eb693b78376cb419283 - languageName: node - linkType: hard - "tmp@npm:^0.0.33": version: 0.0.33 resolution: "tmp@npm:0.0.33" @@ -16383,13 +16320,6 @@ __metadata: languageName: node linkType: hard -"tweezer.js@npm:^1.4.0": - version: 1.5.0 - resolution: "tweezer.js@npm:1.5.0" - checksum: 46999b2dd02ccf0e27fc844ee17dbf581f44aa54b9cb57dc3fab700948fee82124d80219d369848a886691416a8be87926678ab1d410ec25bb4252f84d237dec - languageName: node - linkType: hard - "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0"