Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
9 changes: 4 additions & 5 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,19 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
node_version: [20]
# Build processes work differently across operating systems, so the
# Build processes work differently across operating systems, so the
# checks run on each one to make sure anyone can contribute to the
# docs.
os: [windows-latest, macOS-latest]

steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: ⚙️ Use Node.js ${{ matrix.node_version }}
- name: ⚙️ Use Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ matrix.node_version }}
node-version: 24
- name: 🕸️ Install Dependencies
run: npm ci --legacy-peer-deps
run: npm ci
- name: 🖌️ Lint
run: npm run lint
- name: Test
Expand Down
15 changes: 7 additions & 8 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
# Wrapped components are our own code, so they follow the repo's formatting.
src/theme/DocItem/Layout/index.tsx
src/theme/EditMetaRow/index.tsx
src/theme/Icon/DarkMode/index.tsx
src/theme/Icon/Edit/index.tsx
src/theme/Icon/LightMode/index.tsx
src/theme/Icon/Language/index.tsx
src/theme/Layout/index.tsx
src/theme/NavbarItem/LocaleDropdownNavbarItem/index.tsx
Expand All @@ -12,21 +14,18 @@ src/theme/prism-include-languages.ts
legacy-stencil-components
scripts/bak

docs/api
versioned_docs/version-v*/api
# Auto-generated files
docs/native
versioned_docs/version-v*/native
docs/cli/commands
# Each definition in these files is one line of prose inside a JSX <section>.
# Prettier's mdx parser reflows those children and moves link text onto its own
# line, which MDX then wraps in a paragraph, rendering invalid HTML such as
# <a><p>Android SDK</p></a>. Formatting these files reintroduces that markup.
docs/reference/glossary.md
versioned_docs/version-v*/reference/glossary.md
versioned_docs/version-v*/cli/commands

# Archived versions
versioned_docs/version-v5
versioned_docs/version-v6
versioned_docs/version-v7
static/usage/v6
static/usage/v7

static/code/stackblitz

Expand Down
11 changes: 11 additions & 0 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Prettier configuration for the docs site.
*/

const ionicConfig = require('@ionic/prettier-config');

module.exports = {
...ionicConfig,
// Override the trailingComma setting to avoid trailing commas in function arguments and JSX props.
trailingComma: 'es5',
};
112 changes: 102 additions & 10 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,27 +214,119 @@ The Ionic documentation's `main` branch is deployed automatically and separately

### Archiving a Version

Archived versions are served from a frozen Vercel deployment instead of being rebuilt on every `main` deploy, which keeps build times and memory usage low. Two files control this:
Archived versions are served from a frozen Vercel deployment instead of being rebuilt on every `main` deploy, which keeps build times and memory usage low. That deployment is a full snapshot of the site, so it serves every version that was in the build. Two files control this:

- [`versions.json`](./versions.json): lists the versions Docusaurus rebuilds on every deploy.
- [`versions.json`](./versions.json): lists the older versions Docusaurus rebuilds on every deploy. It does not include the current version, which is built from `docs/` and takes its label from `versions.current` in `docusaurus.config.js`.
- [`versionsArchived.json`](./versionsArchived.json): maps each archived version to the frozen deployment URL the version picker links to.

The archived URL has to point at a build that _included_ the version, so you build it first, then move it to `versionsArchived.json`:
The docs keep the current version plus one older version rebuildable, so each major release archives one version and involves two different version numbers. The steps below refer to them as:

1. **Build the version.** Make sure it is in `versions.json`. If you are refreshing an already-archived version, move it out of `versionsArchived.json` and back into `versions.json`. Commit, push and let Vercel deploy.
2. **Promote the deployment.** In the Vercel dashboard, open that deployment and **Promote to Production** so it does not get cleaned up. Wait for the build to finish before pushing again, or it may get canceled.
3. **Copy its URL.** Use the deployment's unique `ionic-docs-<hash>-ionic1.vercel.app` URL, not the branch or production alias.
4. **Archive it.** Remove the version from `versions.json`, then add it to `versionsArchived.json` with `/docs/<version>` appended and no trailing slash (a trailing slash causes a brief 404 flash):
- `<archiving>`: the version being frozen and removed from `versions.json`.
- `<last-current>`: the version that just stopped being current and moved into `versions.json`. It is not being archived, but step 1 has to account for it.

For example, when `v9` becomes current: `v8` is `<last-current>` and `v7` is `<archiving>`.

The archived URL has to point at a build that _included_ `<archiving>`, so you build it first, then move it to `versionsArchived.json`:

1. **Check the `vercel.json` redirects.** The frozen deployment bakes in whatever `vercel.json` looked like at build time, and it serves every version in that build. Any `:version(...)` group missing a version therefore stays broken on that host for good. Make sure the groups already list `<last-current>`, which is the one most likely to be missing since it only just moved into `versions.json`. The `angular`, `react`, `vue` and `javascript` landing pages each need a group, as no version ships an index page for them:

```json
{
"source": "/docs/:version(v6|v7|<last-current>)/angular",
"destination": "/docs/:version/angular/overview"
},
{
"source": "/docs/:version(<last-current>)/javascript",
"destination": "/docs/:version/javascript/overview"
}
```

The groups accumulate, so existing entries stay in place whether or not that version is archived and you are only ever adding to them. The `javascript` group only covers versions that have a `javascript/` section.

Everything from here on refers to `<archiving>`:

2. **Build `<archiving>`.** Make sure it is in `versions.json`. If you are refreshing an already-archived version, move it out of `versionsArchived.json` and back into `versions.json`, and add it back to the generation scripts in step 10. Commit, push and let Vercel deploy.
3. **Promote the deployment.** In the Vercel dashboard, open that deployment and **Promote to Production** so it does not get cleaned up. Wait for the build to finish before pushing again, or it may get canceled.
4. **Copy its URL.** Use the deployment's unique `ionic-docs-<hash>-ionic1.vercel.app` URL, not the branch or production alias.
5. **Archive it.** Remove `<archiving>` from `versions.json`, then add it to `versionsArchived.json` with `/docs/<archiving>` appended and no trailing slash (a trailing slash causes a brief 404 flash):

_`versions.json`_

```diff
[
- "v8",
- "<archiving>"
+ "v8"
]
```

_`versionsArchived.json`_

```json
{
"v6": "https://ionic-docs-<hash>-ionic1.vercel.app/docs/v6"
"<archiving>": "https://ionic-docs-<hash>-ionic1.vercel.app/docs/<archiving>",
"v6": "https://ionic-docs-lq0if04rc-ionic1.vercel.app/docs/v6",
"v5": "https://ionic-docs-5utg8ms4c-ionic1.vercel.app/docs/v5"
}
```

5. **Open a PR.** Once merged, the version picker links to the archive and `main` stops building that version.
6. **Update `.prettierignore`.** Add the archived version folders to the archived versions group to keep Prettier from formatting generated files:

```
static/usage/<archiving>
versioned_docs/version-<archiving>
```

7. **Update `cspell.json`.** Add the archived version to the `ignorePaths` array so the spell checker skips generated files:

```diff
"ignorePaths": [
...
"versioned_docs/version-v5",
"versioned_docs/version-v6",
+ "versioned_docs/version-<archiving>"
]
```

8. **Update `renovate.json`.** Add the archived version's StackBlitz examples to `ignorePaths` so Renovate stops opening dependency PRs against frozen examples:

```diff
"ignorePaths": [
"static/code/stackblitz/v6/**",
+ "static/code/stackblitz/<archiving>/**"
]
```

Then remove that version's `@ionic/` `allowedVersions` rule from `packageRules`, since it no longer has anything to match.

9. **Update the playground generator.** Remove `<archiving>` from the version choices in [`_templates/playground/new/index.js`](./_templates/playground/new/index.js) so `npm run playground:new` stops offering a version that is no longer built. The choices are bare numbers, without the `v`:

```diff
- choices: ['<archiving>', '8', '9'],
+ choices: ['8', '9'],
```

10. **Update the generation scripts.** Remove `<archiving>` from the write lists in [`scripts/native.mjs`](./scripts/native.mjs) and [`scripts/cli.mjs`](./scripts/cli.mjs), so every build stops regenerating content for a version served from a frozen deployment. The remaining targets are `docs/` (the current version) and the one older version still in `versions.json`.

_`scripts/native.mjs`_

```diff
writeFileSync(`docs/native/${fileName}`, apiContent);
- writeFileSync(`versioned_docs/version-<archiving>/native/${fileName}`, apiContent);
writeFileSync(`versioned_docs/version-v8/native/${fileName}`, apiContent);
```

_`scripts/cli.mjs`_

```diff
writeFileSync(`docs/${path}`, data);
- writeFileSync(`versioned_docs/version-<archiving>/${path}`, data);
writeFileSync(`versioned_docs/version-v8/${path}`, data);
```

11. **Open a PR.** Once merged, the version picker links to the archive and `main` stops building `<archiving>`.

Removed versions keep their `versioned_docs/` and `versioned_sidebars/` content, so they can be rebuilt anytime by adding them back to `versions.json`.
Removed versions keep their authored `versioned_docs/` and `versioned_sidebars/` content, so they can be rebuilt anytime by adding them back to `versions.json`. Their `native/` and `cli/commands/` pages are generated rather than committed, so a rebuild also needs the version back in the write lists from step 10. Without it those sections build empty.

> [!NOTE]
> Ionic v3 and v4 use other build tools and are not managed here.
Expand Down
2 changes: 1 addition & 1 deletion _templates/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Once you've generated your playground, you need to add it to the main markdown f
```
## Feature

import Feature from '@site/static/usage/v8/button/feature/index.md';
import Feature from '@site/static/usage/v9/button/feature/index.mdx';

<Feature />
```
4 changes: 2 additions & 2 deletions _templates/playground/new/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ module.exports = {
name: 'version',
message: 'Select the Ionic Framework version for the playground',
initial: '10',
choices: ['6', '7', '8', '9', '10'],
choices: ['8', '9', '10'],
},
{
type: 'toggle',
Expand All @@ -72,7 +72,7 @@ module.exports = {

const playgroundName = changeCase.pascal(answers.path.split('/').pop());
console.log(
`\nTo use this playground in a docs markdown file, include\nthe following:\n\n## ${playgroundName}\n\nimport ${playgroundName} from '@site/static/usage/v${answers.version}/${answers.name}/${answers.path}/index.md';\n\n<${playgroundName} />\n`
`\nTo use this playground in a docs markdown file, include\nthe following:\n\n## ${playgroundName}\n\nimport ${playgroundName} from '@site/static/usage/v${answers.version}/${answers.name}/${answers.path}/index.mdx';\n\n<${playgroundName} />\n`
);

return answers;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
to: "<%= `static/usage/v${version}/${name}/${path}/index.md` %>"
to: "<%= `static/usage/v${version}/${name}/${path}/index.mdx` %>"
---
import Playground from '@site/src/components/global/Playground';

Expand Down
1 change: 1 addition & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"versioned_docs/**/native",
"versioned_docs/version-v5",
"versioned_docs/version-v6",
"versioned_docs/version-v7",
"node_modules"
],
"flagWords": [
Expand Down
12 changes: 12 additions & 0 deletions docs/angular/build-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ The Standalone approach uses modern Angular APIs and is the recommended way to b
## Standalone

:::info

Ionic UI components as Angular standalone components is supported starting in Ionic v7.5.

:::

### Overview
Expand All @@ -30,13 +32,17 @@ Refer to the [Standalone Migration Guide](#migrating-from-modules-to-standalone)
1. Ionic components need to be imported into every Angular component they are used in which can be time consuming to set up.

:::info[Code splitting]

Ionic ships standalone components from a single entry point (`@ionic/angular`). Bundlers such as Webpack and esbuild cannot split code from a single entry point across separate chunks, so the Ionic components you import are included in the main bundle rather than in the chunk for the route or component where they are used. Unused components are still tree-shaken out of the build.

:::

### Usage with Standalone-based Applications

:::warning

All Ionic imports should be imported from the `@ionic/angular` submodule. This includes imports such as components, directives, providers, and types. Importing from `@ionic/angular/lazy` may pull in lazy loaded Ionic code which can interfere with treeshaking.

:::

**Bootstrapping and Configuration**
Expand Down Expand Up @@ -203,7 +209,9 @@ Ionic Angular's standalone components use ES Modules. As a result, developers us
### Usage with NgModule-based Applications

:::warning

All Ionic imports should be imported from the `@ionic/angular` submodule. This includes imports such as components, directives, providers, and types. Importing from `@ionic/angular/lazy` may pull in lazy loaded Ionic code which can interfere with treeshaking.

:::

**Bootstrapping and Configuration**
Expand Down Expand Up @@ -366,7 +374,9 @@ Ionic Angular's standalone components use ES Modules. As a result, developers us
## Modules

:::warning[Deprecation Notice]

The Modules approach, including `IonicModule`, is **deprecated** and will be removed in a future major release. Existing applications will continue to work during the deprecation period but should migrate using the [Standalone migration guide](#migrating-from-modules-to-standalone). New applications should use the [Standalone](#standalone) approach.

:::

### Overview
Expand Down Expand Up @@ -405,9 +415,11 @@ export class AppModule {}
## Migrating from Modules to Standalone

:::tip

Try our automated utility for migrating to standalone!

Refer to the [standalone migration codemods](https://github.com/ionic-team/ionic-angular-standalone-codemods) for instructions on how to get started. All issues related to the migration utility should be filed on the linked repo.

:::

The Standalone option is newer than the Modules option, so developers may wish to switch during the development of their application. This guide details the steps needed to migrate.
Expand Down
2 changes: 2 additions & 0 deletions docs/angular/lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ Ionic embraces the life cycle events provided by Angular. The two Angular events
For more info on the Angular Component Life Cycle events, visit their [component lifecycle docs](https://angular.io/guide/lifecycle-hooks).

:::note

Components that use `ion-nav` or `ion-router-outlet` should not use the `OnPush` change detection strategy. Doing so will prevent lifecycle hooks such as `ngOnInit` from firing. Additionally, asynchronous state changes may not render properly.

:::

## Ionic Page Events
Expand Down
6 changes: 5 additions & 1 deletion docs/angular/navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,9 @@ import { LoginComponent } from './login.component';
```

:::note

We're excluding some additional content and only including the necessary parts.

:::

Here, we have a typical Angular Module setup, along with a RouterModule import, but we're now using `forChild` and declaring the component in that setup. With this setup, when we run our build, we will produce separate chunks for both the app component, the login component, and the detail component.
Expand All @@ -194,14 +196,16 @@ export class AppRoutingModule {}
```

:::tip

If you are using `routerLink`, `routerDirection`, or `routerAction` be sure to also import the `IonRouterLink` directive for Ionic components or the `IonRouterLinkWithHref` directive for `<a>` elements. An example of this is available in the [Ionic Angular Build Options docs](./build-options.md#migrating-from-modules-to-standalone).

:::

To get started with standalone components [visit Angular's official docs](https://angular.io/guide/standalone-components).

## Live Example

import NavigationPlayground from '@site/static/usage/v10/navigation/index.md';
import NavigationPlayground from '@site/static/usage/v10/navigation/index.mdx';

<NavigationPlayground defaultFramework="angular" />

Expand Down
20 changes: 16 additions & 4 deletions docs/angular/overlays.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ import { MyRouteService } from './my-route.service';
providers: [MyRouteService], // Service provided at route level
})
export class FeatureComponent {
constructor(private modalController: ModalController, private injector: Injector) {}
constructor(
private modalController: ModalController,
private injector: Injector
) {}

async openModal() {
const modal = await this.modalController.create({
Expand Down Expand Up @@ -113,7 +116,10 @@ import { MyService } from './my.service';
templateUrl: './feature.component.html',
})
export class FeatureComponent {
constructor(private modalController: ModalController, private injector: Injector) {}
constructor(
private modalController: ModalController,
private injector: Injector
) {}

async openModal() {
const myService = new MyService();
Expand Down Expand Up @@ -148,7 +154,10 @@ import { MyModalComponent } from './my-modal.component';
templateUrl: './feature.component.html',
})
export class FeatureComponent {
constructor(private modalController: ModalController, private injector: Injector) {}
constructor(
private modalController: ModalController,
private injector: Injector
) {}

async openModal() {
const modal = await this.modalController.create({
Expand All @@ -174,7 +183,10 @@ import { MyPopoverComponent } from './my-popover.component';
templateUrl: './feature.component.html',
})
export class FeatureComponent {
constructor(private popoverController: PopoverController, private injector: Injector) {}
constructor(
private popoverController: PopoverController,
private injector: Injector
) {}

async openPopover(event: Event) {
const popover = await this.popoverController.create({
Expand Down
Loading