Compare commits

..

1 Commits

Author SHA1 Message Date
Ben Hong b36d53143a docs: fix missing security guide in navigation
The paths for the security guide were flipped in the original PR. As a result, it looked for a markdown file in the best-practices directory when it should have looked for it in the guide directory instead.
2024-03-12 12:18:54 -07:00
10186 changed files with 547676 additions and 383577 deletions
+3 -1
View File
@@ -5,6 +5,9 @@
.git
node_modules
dist
aio/node_modules
aio/tools/examples/shared/node_modules
aio/tools/examples/shared/example-scaffold
# All integration test node_modules folders
integration/animations/node_modules
@@ -14,7 +17,6 @@ integration/cli-hello-world-ivy-i18n/node_modules
integration/cli-hello-world-lazy/node_modules
integration/cli-hello-world-mocha/node_modules
integration/cli-signal-inputs/node_modules
integration/platform-server-hydration/node_modules
integration/defer/node_modules
integration/dynamic-compiler/node_modules
integration/forms/node_modules
+28 -8
View File
@@ -56,16 +56,31 @@ build --enable_runfiles
build:release --workspace_status_command="yarn -s ng-dev release build-env-stamp --mode=release"
build:release --stamp
# Building AIO against local Angular deps requires stamping
# versions in Angular packages due to CLI version checks.
build:aio_local_deps --stamp
build:aio_local_deps --workspace_status_command="yarn -s --cwd aio local-workspace-status"
# Snapshots should also be stamped with version control information.
build:snapshot-build --workspace_status_command="yarn -s ng-dev release build-env-stamp --mode=snapshot"
build:snapshot-build --stamp
# Angular DevTools for Firefox releases *cannot* be stamped and `--config snapshot-build-firefox` is a no-op.
# This is because Mozilla requires add-on source code to be uploaded and then they manually reproduce the build.
# If we stamp the build based on Git information such as the current commit, then reproducing the build would require
# uploading the entire `.git` directory. We already must upload the entire monorepo, and uploading `.git` as well
# exceeds the max source file size. To address this, we just *don't* stamp Firefox builds of Angular DevTools.
build:snapshot-build-firefox --nostamp
##########################################################
# AIO architect build configuration #
# See aio/angular.json for available configurations. #
# To build with a partiular configuration: #
# bazel build //aio:build --aio_build_config=<config> #
# Default config is `stable``. #
##########################################################
build --flag_alias=aio_build_config=//aio:flag_aio_build_config
####################################
# AIO first party dep substitution #
# Turn on with #
# --config=aio_local_deps #
####################################
build:aio_local_deps --//aio:flag_aio_local_deps
###############################
# Output #
@@ -103,6 +118,10 @@ common:remote --jobs=200
build:remote --google_default_credentials
# Limit the number of test jobs for on an AIO local deps build. The example tests running
# concurrently pushes the circleci executor RAM usage to its limits.
test:aio_local_deps --jobs=24
# Force remote exeuctions to consider the entire run as linux
build:remote --cpu=k8
build:remote --host_cpu=k8
@@ -158,8 +177,9 @@ test:saucelabs --flaky_test_attempts=1
# --ng_perf will ask the Ivy compiler to produce performance results for each build.
build --flag_alias=ng_perf=//packages/compiler-cli:ng_perf
# --prerender_adev will run adev build/serve in a full mode, performing prerendering
build --flag_alias=prerender_adev=//adev:prerender_adev
# --adev_fast will run adev build/serve in a faster mode, skipping things like prerendering
# for local development.
build --flag_alias=fast_adev=//adev:fast_build_mode
####################################################
# User bazel configuration
+1 -1
View File
@@ -44,7 +44,7 @@ function setSecretVar() {
# Create a function to set an environment variable, when called.
#
# Use this function for creating setter for public environment variables that require expensive or
# time-consuming computations and may not be needed. When needed, you can call this function to set
# time-consuming computaions and may not be needed. When needed, you can call this function to set
# the environment variable (which will be available through `$BASH_ENV` from that point onwards).
#
# Arguments:
+1
View File
@@ -16,6 +16,7 @@ echo "source $envHelpersPath;" >> $BASH_ENV;
####################################################################################################
setPublicVar CI "$CI"
setPublicVar PROJECT_ROOT "$projectDir";
setPublicVar CI_AIO_MIN_PWA_SCORE "95";
# This is the branch being built; e.g. `pull/12345` for PR builds.
setPublicVar CI_BRANCH "$CIRCLE_BRANCH";
setPublicVar CI_BUILD_URL "$CIRCLE_BUILD_URL";
+6 -1
View File
@@ -6,6 +6,11 @@
"postCreateCommand": "yarn install",
"extensions": [
"devondcarew.bazel-code",
"ms-vscode.vscode-typescript-tslint-plugin"
"gkalpak.aio-docs-utils",
"ms-vscode.vscode-typescript-tslint-plugin",
"xaver.clang-format",
// The following extensions are useful when working on angular.io (i.e. inside the `aio/` directory).
//"angular.ng-template",
//"dbaeumer.vscode-eslint",
],
}
+2 -2
View File
@@ -1,5 +1,5 @@
name: 'Docs or angular.dev Bug Report'
description: Report an issue in Angular's documentation or angular.dev application
name: 'Docs or angular.io Bug Report'
description: Report an issue in Angular's documentation or angular.io application
body:
- type: textarea
+1 -1
View File
@@ -1,7 +1,7 @@
blank_issues_enabled: false
contact_links:
- name: Security issue disclosure
url: https://angular.dev/best-practices/security#report-issues
url: https://angular.io/guide/security#report-issues
about: Report a security issue in Angular Framework, CDK, Material, or CLI
- name: Angular CLI
url: https://github.com/angular/angular-cli/issues/new/choose
+1 -1
View File
@@ -18,7 +18,7 @@ What kind of change does this PR introduce?
- [ ] Build related changes
- [ ] CI related changes
- [ ] Documentation content changes
- [ ] angular.dev application / infrastructure changes
- [ ] angular.io application / infrastructure changes
- [ ] Other... Please describe:
@@ -1,18 +0,0 @@
load("//tools:defaults.bzl", "esbuild_checked_in")
exports_files([
"tsconfig.json",
])
esbuild_checked_in(
name = "main",
entry_point = "//.github/actions/deploy-docs-site/lib:main.ts",
external = [
"undici",
],
platform = "node",
target = "node14",
deps = [
"//.github/actions/deploy-docs-site/lib",
],
)
@@ -1,19 +0,0 @@
name: 'Deploy angular.dev site'
description: 'Automatically deploy and set up redirects for angular.dev'
author: 'Angular'
inputs:
serviceKey:
description: 'The service key used for firebase deployments.'
required: true
githubReleaseTrainReadToken:
description: 'GitHub access token for reading release trains without rate limits.'
required: true
configPath:
description: 'The path to the firebase config file.'
required: true
distDir:
description: 'The path to the firebase dist directory.'
required: true
runs:
using: 'node20'
main: 'main.js'
@@ -1,23 +0,0 @@
load("//tools:defaults.bzl", "ts_library")
package(default_visibility = ["//.github/actions/deploy-docs-site:__subpackages__"])
exports_files([
"main.ts",
])
ts_library(
name = "lib",
srcs = glob(
["*.ts"],
),
#tsconfig = "//.github/actions/deploy-docs-site:tsconfig.json",
deps = [
"@npm//@actions/core",
"@npm//@actions/github",
"@npm//@angular/ng-dev",
"@npm//@types/node",
"@npm//@types/tmp",
"@npm//tmp",
],
)
@@ -1,20 +0,0 @@
import {fileSync} from 'tmp';
import {writeSync} from 'fs';
import {getInput, setSecret} from '@actions/core';
let credentialFilePath: undefined | string;
export function getCredentialFilePath(): string {
if (credentialFilePath === undefined) {
const tmpFile = fileSync({postfix: '.json'});
writeSync(tmpFile.fd, getInput('serviceKey', {required: true}));
setSecret(tmpFile.name);
credentialFilePath = tmpFile.name;
}
return credentialFilePath;
}
/** Github access token. Used for querying the active release trains. */
export const githubReleaseTrainReadToken: string = getInput('githubReleaseTrainReadToken', {
required: true,
});
@@ -1,116 +0,0 @@
import {cp, mkdtemp, readFile, rm, writeFile} from 'fs/promises';
import {Deployment} from './deployments';
import {join} from 'path';
import {tmpdir} from 'os';
import {spawnSync} from 'child_process';
import {getCredentialFilePath} from './credential';
export async function deployToFirebase(
deployment: Deployment,
configPath: string,
distDirPath: string,
) {
if (deployment.destination == undefined) {
console.log(`No deployment necessary for docs created from: ${deployment.branch}`);
return;
}
console.log('Preparing for deployment to firebase...');
const tmpDeployDir = await mkdtemp(join(tmpdir(), 'deploy-directory'));
const deployConfigPath = join(tmpDeployDir, 'firebase.json');
const config = JSON.parse(await readFile(configPath, {encoding: 'utf-8'})) as {
hosting: {public: string};
};
config['hosting']['public'] = './dist';
await writeFile(deployConfigPath, JSON.stringify(config, null, 2));
await cp(distDirPath, join(tmpDeployDir, 'dist'), {recursive: true});
spawnSync(`chmod 777 -R ${tmpDeployDir}`, {encoding: 'utf-8', shell: true});
firebase(
`target:clear --config ${deployConfigPath} --project angular-dev-site hosting angular-docs`,
tmpDeployDir,
);
firebase(
`target:apply --config ${deployConfigPath} --project angular-dev-site hosting angular-docs ${deployment.destination}`,
tmpDeployDir,
);
firebase(
`deploy --config ${deployConfigPath} --project angular-dev-site --only hosting --non-interactive`,
tmpDeployDir,
);
firebase(
`target:clear --config ${deployConfigPath} --project angular-dev-site hosting angular-docs`,
tmpDeployDir,
);
await rm(tmpDeployDir, {recursive: true});
}
export async function setupRedirect(deployment: Deployment) {
if (deployment.redirect === undefined) {
console.log(`No redirect necessary for docs created from: ${deployment.branch}`);
return;
}
console.log('Preparing to set up redirect on firebase...');
const redirectConfig = JSON.stringify(
{
hosting: {
target: 'angular-docs',
redirects: [
{
type: 302,
regex: '^(.*)$',
destination: `${deployment.redirect.to}:1`,
},
],
},
},
null,
2,
);
const tmpRedirectDir = await mkdtemp(join(tmpdir(), 'redirect-directory'));
const redirectConfigPath = join(tmpRedirectDir, 'firebase.json');
await writeFile(redirectConfigPath, redirectConfig);
spawnSync(`chmod 777 -R ${tmpRedirectDir}`, {encoding: 'utf-8', shell: true});
firebase(
`target:clear --config ${redirectConfigPath} --project angular-dev-site hosting angular-docs`,
tmpRedirectDir,
);
firebase(
`target:apply --config ${redirectConfigPath} --project angular-dev-site hosting angular-docs ${deployment.redirect.from}`,
tmpRedirectDir,
);
firebase(
`deploy --config ${redirectConfigPath} --project angular-dev-site --only hosting --non-interactive`,
tmpRedirectDir,
);
firebase(
`target:clear --config ${redirectConfigPath} --project angular-dev-site hosting angular-docs`,
tmpRedirectDir,
);
await rm(tmpRedirectDir, {recursive: true});
}
function firebase(cmd: string, cwd?: string) {
spawnSync('npx', `-y firebase-tools@13.15.1 ${cmd}`.split(' '), {
cwd,
encoding: 'utf-8',
shell: true,
stdio: 'inherit',
env: {
...process.env,
GOOGLE_APPLICATION_CREDENTIALS: getCredentialFilePath(),
},
});
}
@@ -1,67 +0,0 @@
import {fetchLongTermSupportBranchesFromNpm, ActiveReleaseTrains} from '@angular/ng-dev';
import {ReleaseConfig} from '@angular/ng-dev';
import {AuthenticatedGitClient} from '@angular/ng-dev';
export interface Deployment {
branch: string;
redirect?: {
from: string;
to: string;
};
destination?: string;
}
export type Deployments = Map<string, Deployment>;
export async function getDeployments(): Promise<Deployments> {
const {github} = await AuthenticatedGitClient.get();
const releaseTrains = await ActiveReleaseTrains.fetch({
api: github,
name: 'angular',
owner: 'angular',
nextBranchName: 'main',
});
const ltsBranches = await fetchLongTermSupportBranchesFromNpm({
representativeNpmPackage: '@angular/core',
} as ReleaseConfig);
const docSites = new Map<string, Deployment>();
[...ltsBranches.active, ...ltsBranches.inactive].forEach((branch) => {
docSites.set(branch.name, {
branch: branch.name,
destination: `v${branch.version.major}-angular-dev`,
});
});
docSites.set(releaseTrains.latest.branchName, {
branch: releaseTrains.latest.branchName,
redirect: {
from: `v${releaseTrains.latest.version.major}-angular-dev`,
to: 'https://angular.dev',
},
destination: 'angular-dev-site',
});
if (releaseTrains.releaseCandidate) {
docSites.set(releaseTrains.next.branchName, {
branch: releaseTrains.next.branchName,
});
docSites.set(releaseTrains.releaseCandidate.branchName, {
branch: releaseTrains.releaseCandidate.branchName,
destination: 'next-angular-dev',
redirect: {
from: `v${releaseTrains.releaseCandidate.version.major}-angular-dev`,
to: 'https://next.angular.dev',
},
});
} else {
docSites.set(releaseTrains.next.branchName, {
branch: releaseTrains.next.branchName,
destination: 'next-angular-dev',
});
}
return docSites;
}
@@ -1,75 +0,0 @@
import {getInput, setFailed} from '@actions/core';
import {context} from '@actions/github';
import {deployToFirebase, setupRedirect} from './deploy';
import {getDeployments} from './deployments';
import {AuthenticatedGitClient, GithubConfig, setConfig} from '@angular/ng-dev';
import {githubReleaseTrainReadToken} from './credential';
const refMatcher = /refs\/heads\/(.*)/;
async function deployDocs() {
setConfig({
github: <GithubConfig>{
mainBranchName: 'main',
name: 'angular',
owner: 'angular',
},
});
AuthenticatedGitClient.configure(githubReleaseTrainReadToken);
if (context.eventName !== 'push') {
throw Error();
}
const matchedRef = context.ref.match(refMatcher);
if (matchedRef === null) {
throw Error();
}
const currentBranch = matchedRef[1];
const configPath = getInput('configPath');
const distDir = getInput('distDir');
const deployment = (await getDeployments()).get(currentBranch);
if (deployment === undefined) {
console.log(`Current branch (${currentBranch}) does not deploy a documentation site.`);
console.log(`Exiting...`);
process.exit(0);
}
console.log('Doc site deployment information');
console.log('');
console.log('Current Branch:');
console.log(` ${deployment.branch}`);
console.log('');
console.log('Firebase Site:');
if (deployment.destination === undefined) {
console.log(' No deployment of a documenation site is necessary');
} else {
console.log(` Deploying to: ${deployment.destination}`);
}
console.log('');
console.log('Redirect Configuration:');
if (deployment.redirect === undefined) {
console.log(' No redirects are necessary');
} else {
console.log(` From: ${deployment.redirect.from}`);
console.log(` To: ${deployment.redirect.to}`);
}
await deployToFirebase(deployment, configPath, distDir);
await setupRedirect(deployment);
}
// Only run if the action is executed in a repository with is in the Angular org. This is in place
// to prevent the action from actually running in a fork of a repository with this action set up.
if (context.repo.owner === 'angular') {
deployDocs().catch((e: Error) => {
setFailed(e.message);
console.error(e);
});
} else {
console.warn(
'The action was skipped as this action is only meant to run in repos belonging to the Angular organization.',
);
}
File diff suppressed because it is too large Load Diff
@@ -1,3 +0,0 @@
{
"type": "module"
}
@@ -1,19 +0,0 @@
{
"compilerOptions": {
"module": "Node16",
"target": "es2020",
"lib": ["es2021", "dom"],
"experimentalDecorators": true,
"strict": true,
"outDir": "../../../dist/tsc-non-bazel-out",
"skipLibCheck": true,
"noImplicitOverride": true,
// TODO(ESM): Remove this when Bazel also uses the Node resolution.
"esModuleInterop": true,
"noImplicitReturns": true,
"jsx": "react",
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment"
},
"exclude": []
}
@@ -1,45 +0,0 @@
name: 'Saucelabs legacy test job'
description: 'Runs tests against Saucelabs (outside of Bazel)'
runs:
using: 'composite'
steps:
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- name: Setup Saucelabs Variables
uses: angular/dev-infra/github-actions/saucelabs@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- name: Starting Saucelabs tunnel service
shell: bash
run: ./tools/saucelabs/sauce-service.sh run &
# Build test fixtures for a test that rely on Bazel-generated fixtures. Note that disabling
# specific tests which are reliant on such generated fixtures is not an option as SystemJS
# in the Saucelabs legacy job always fetches referenced files, even if the imports would be
# guarded by an check to skip in the Saucelabs legacy job. We should be good running such
# test in all supported browsers on Saucelabs anyway until this job can be removed.
- name: Preparing Bazel-generated fixtures required in legacy tests
shell: bash
run: |
yarn bazel build //packages/core/test:downleveled_es5_fixture //packages/common/locales
# Needed for the ES5 downlevel reflector test in `packages/core/test/reflection`.
mkdir -p dist/legacy-test-out/core/test/reflection/
cp dist/bin/packages/core/test/reflection/es5_downleveled_inheritance_fixture.js \
dist/legacy-test-out/core/test/reflection/es5_downleveled_inheritance_fixture.js
# Locale files are needed for i18n tests running within Saucelabs. These are added
# directly as sources so that the TypeScript compilation of `/packages/tsconfig.json`
# can succeed. Note that the base locale and currencies files are checked-in, so
# we do not need to re-generate those through Bazel.
mkdir -p packages/common/locales/extra
cp dist/bin/packages/common/locales/*.ts packages/common/locales
cp dist/bin/packages/common/locales/extra/*.ts packages/common/locales/extra
- name: Build bundle of tests to run on Saucelabs
shell: bash
run: node tools/legacy-saucelabs/build-saucelabs-test-bundle.mjs
- name: Wait and confirm Saucelabs tunnel has connected
shell: bash
run: ./tools/saucelabs/sauce-service.sh ready-wait
- name: Running tests on Saucelabs.
shell: bash
run: KARMA_WEB_TEST_MODE=SL_REQUIRED yarn karma start ./karma-js.conf.js --single-run
- name: Stop Saucelabs tunnel service
shell: bash
run: ./tools/saucelabs/sauce-service.sh stop
+1 -1
View File
@@ -4,7 +4,7 @@ description: 'Installs the dependencies using Yarn'
runs:
using: 'composite'
steps:
- uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4
- uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4
with:
path: |
./node_modules/
+5 -5
View File
@@ -21,16 +21,16 @@ jobs:
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'adev: preview'))
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Build adev to ensure it continues to work
run: yarn bazel build //adev:build --prerender_adev --config=release
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
run: yarn bazel build --config=aio_local_deps //adev:build
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@c83e99a12397014162531ca125c94549db55dd84
with:
workflow-artifact-name: 'adev-preview'
pull-number: '${{github.event.pull_request.number}}'
+2 -2
View File
@@ -29,7 +29,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
token: '${{secrets.GITHUB_TOKEN}}'
@@ -40,7 +40,7 @@ jobs:
npx -y firebase-tools@latest target:clear --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs
npx -y firebase-tools@latest target:apply --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs ${{env.PREVIEW_SITE}}
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@c83e99a12397014162531ca125c94549db55dd84
with:
github-token: '${{secrets.GITHUB_TOKEN}}'
workflow-artifact-name: 'adev-preview'
@@ -13,9 +13,9 @@ jobs:
assistant_to_the_branch_manager:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
- uses: angular/dev-infra/github-actions/branch-manager@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- uses: angular/dev-infra/github-actions/branch-manager@c83e99a12397014162531ca125c94549db55dd84
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+4 -4
View File
@@ -19,7 +19,7 @@ jobs:
exit: true
# Indicate that the benchmark command was received.
- uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
- uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3
with:
comment-id: ${{github.event.comment.id}}
token: '${{secrets.BENCHMARK_POST_RESULTS_GITHUB_TOKEN}}'
@@ -28,7 +28,7 @@ jobs:
- uses: alessbell/pull-request-comment-branch@aad01d65d6982b8eacabed5e9a684cd8ceb98da6 # v1.1
id: comment-branch
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
# Specify repository as the PR branch might be from a fork.
repository: ${{steps.comment-branch.outputs.head_owner}}/${{steps.comment-branch.outputs.head_repo}}
@@ -38,7 +38,7 @@ jobs:
- uses: ./.github/actions/yarn-install
- uses: angular/dev-infra/github-actions/bazel/configure-remote@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
with:
bazelrc: ./.bazelrc.user
@@ -53,7 +53,7 @@ jobs:
id: benchmark
name: Running benchmark
- uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
- uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3
with:
issue-number: ${{github.event.issue.number}}
token: '${{secrets.BENCHMARK_POST_RESULTS_GITHUB_TOKEN}}'
+64
View File
@@ -0,0 +1,64 @@
name: CI
on:
pull_request_target:
types: [opened, synchronize, reopened]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true
permissions: {}
defaults:
run:
shell: bash
jobs:
saucelabs:
runs-on: ubuntu-latest-4core
env:
SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
SAUCE_USERNAME: ${{ vars.SAUCE_USERNAME }}
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
with:
cache-node-modules: true
# Checking out the pull request commit is intended here as we need to run the changed code tests.
ref: ${{ github.event.pull_request.head.sha }}
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
- name: Starting Saucelabs tunnel service
run: ./tools/saucelabs/sauce-service.sh run &
# Build test fixtures for a test that rely on Bazel-generated fixtures. Note that disabling
# specific tests which are reliant on such generated fixtures is not an option as SystemJS
# in the Saucelabs legacy job always fetches referenced files, even if the imports would be
# guarded by an check to skip in the Saucelabs legacy job. We should be good running such
# test in all supported browsers on Saucelabs anyway until this job can be removed.
- name: Preparing Bazel-generated fixtures required in legacy tests
run: |
yarn bazel build //packages/core/test:downleveled_es5_fixture //packages/common/locales
# Needed for the ES5 downlevel reflector test in `packages/core/test/reflection`.
mkdir -p dist/legacy-test-out/core/test/reflection/
cp dist/bin/packages/core/test/reflection/es5_downleveled_inheritance_fixture.js \
dist/legacy-test-out/core/test/reflection/es5_downleveled_inheritance_fixture.js
# Locale files are needed for i18n tests running within Saucelabs. These are added
# directly as sources so that the TypeScript compilation of `/packages/tsconfig.json`
# can succeed. Note that the base locale and currencies files are checked-in, so
# we do not need to re-generate those through Bazel.
mkdir -p packages/common/locales/extra
cp dist/bin/packages/common/locales/*.ts packages/common/locales
cp dist/bin/packages/common/locales/extra/*.ts packages/common/locales/extra
- name: Build bundle of tests to run on Saucelabs
run: node tools/legacy-saucelabs/build-saucelabs-test-bundle.mjs
- name: Wait and confirm Saucelabs tunnel has connected
run: ./tools/saucelabs/sauce-service.sh ready-wait
timeout-minutes: 3
- name: Running tests on Saucelabs.
run: KARMA_WEB_TEST_MODE=SL_REQUIRED yarn karma start ./karma-js.conf.js --single-run
- name: Stop Saucelabs tunnel service
run: ./tools/saucelabs/sauce-service.sh stop
+77 -37
View File
@@ -1,10 +1,12 @@
name: CI (push)
name: CI
on:
push:
branches:
- main
- '[0-9]+.[0-9]+.x'
pull_request:
types: [opened, synchronize, reopened]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -21,11 +23,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
with:
cache-node-modules: true
node-module-directories: |
./node_modules
./aio/node_modules
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Install node modules in aio
run: yarn install --frozen-lockfile --cwd aio
- name: Check code lint
run: yarn -s tslint
- name: Check for circular dependencies
@@ -36,18 +43,28 @@ jobs:
run: yarn -s ng-dev ngbot verify
- name: Confirm code builds with typescript as expected
run: yarn -s check-tooling-setup
- name: Check commit message
# Commit message validation is only done on pull requests as its too late to validate once
# it has been merged.
if: github.event_name == 'pull_request'
run: yarn ng-dev commit-message validate-range ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }}
- name: Check code format
# Code formatting checks are only done on pull requests as its too late to validate once
# it has been merged.
if: github.event_name == 'pull_request'
run: yarn ng-dev format changed --check ${{ github.event.pull_request.base.sha }}
devtools:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Run unit tests
@@ -59,13 +76,13 @@ jobs:
runs-on: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
- name: Install node modules
run: yarn install --frozen-lockfile --network-timeout 100000
- name: Run CI tests for framework
@@ -76,32 +93,29 @@ jobs:
labels: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Build adev in fast mode to ensure it continues to work
run: yarn bazel build //adev:build --config=release
# TODO: re-enable tests once the next release is shipped
# Tests are broken because of https://github.com/angular/angular/issues/54858
# - name: Run tests
# run: yarn bazel test //adev/...
- name: Build adev to ensure it continues to work
run: yarn bazel test --config=aio_local_deps //adev:test
publish-snapshots:
if: github.event_name == 'push'
runs-on:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
- name: Install node modules
run: yarn install --frozen-lockfile
- run: echo "https://${{secrets.SNAPSHOT_BUILDS_GITHUB_TOKEN}}:@github.com" > ${HOME}/.git_credentials
@@ -113,7 +127,7 @@ jobs:
labels: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
with:
cache-node-modules: true
node-module-directories: |
@@ -121,9 +135,9 @@ jobs:
./packages/zone.js/node_modules
./packages/zone.js/test/typings/node_modules
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
- name: Install node modules
run: yarn install --frozen-lockfile
- run: |
@@ -154,38 +168,64 @@ jobs:
- run: yarn --cwd packages/zone.js/test/typings install --frozen-lockfile --non-interactive
- run: yarn --cwd packages/zone.js/test/typings test
saucelabs:
bazel-saucelabs:
# TODO: Replace legacy job with Bazel saucelabs job.
if: github.event_name == 'push'
runs-on: ubuntu-latest-4core
env:
SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
JOBS: 2
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
with:
cache-node-modules: true
# Checking out the pull request commit is intended here as we need to run the changed code tests.
ref: ${{ github.event.pull_request.head.sha }}
- name: Install node modules
run: yarn install --frozen-lockfile
- uses: ./.github/actions/saucelabs-legacy
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
- name: Set up Sauce Tunnel Daemon
run: yarn bazel run //tools/saucelabs-daemon/background-service -- $JOBS &
env:
SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
SAUCE_USERNAME: ${{ vars.SAUCE_USERNAME }}
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
- name: Run all saucelabs bazel tests
run: |
TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "fixme-saucelabs", ...)')
yarn bazel test --config=saucelabs --jobs=$JOBS ${TESTS}
adev-deploy:
needs: [adev]
if: needs.adev.result == 'success'
if: needs.adev.result == 'success' && github.event_name == 'push' && github.ref_name == 'main'
env:
DEPLOY_PROJECT: angular-dev-site
DEPLOY_SITE: angular-dev-site
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Build adev to ensure it continues to work
run: yarn bazel build //adev:build --prerender_adev --config=release
run: yarn bazel build --config=aio_local_deps //adev:build
- name: Move generated project files into deployment directory
run: cp -r dist/bin/adev/build adev/build
- name: Setup firebase context
run: |
npx -y firebase-tools@latest target:clear --config adev/firebase.json --project ${{ env.DEPLOY_PROJECT }} hosting angular-docs
npx -y firebase-tools@latest target:apply --config adev/firebase.json --project ${{ env.DEPLOY_PROJECT }} hosting angular-docs ${{ env.DEPLOY_SITE }}
- name: Deploy to firebase
uses: ./.github/actions/deploy-docs-site
uses: FirebaseExtended/action-hosting-deploy@120e124148ab7016bec2374e5050f15051255ba2 # v0.7.1
with:
serviceKey: ${{ secrets.ANGULAR_DEV_SITE_DEPLOY }}
githubReleaseTrainReadToken: ${{ secrets.DOCS_DEPLOY_GITHUB_RELEASE_TRAIN_TOKEN }}
configPath: 'adev/firebase.json'
distDir: 'dist/bin/adev/build/browser'
entryPoint: './adev'
firebaseServiceAccount: ${{ secrets.ANGULAR_DEV_SITE_DEPLOY }}
projectId: ${{ env.DEPLOY_PROJECT }}
channelId: live
+4 -4
View File
@@ -12,14 +12,14 @@ jobs:
labels:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: angular/dev-infra/github-actions/commit-message-based-labels@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: angular/dev-infra/github-actions/commit-message-based-labels@c83e99a12397014162531ca125c94549db55dd84
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
post_approval_changes:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: angular/dev-infra/github-actions/post-approval-changes@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: angular/dev-infra/github-actions/post-approval-changes@c83e99a12397014162531ca125c94549db55dd84
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+2 -2
View File
@@ -13,8 +13,8 @@ jobs:
statuses: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: angular/dev-infra/github-actions/google-internal-tests@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: angular/dev-infra/github-actions/google-internal-tests@c83e99a12397014162531ca125c94549db55dd84
with:
run-tests-guide-url: http://go/angular-g3sync-start
github-token: ${{ secrets.GITHUB_TOKEN }}
-34
View File
@@ -1,34 +0,0 @@
name: Manual jobs
on:
workflow_dispatch:
inputs: {}
jobs:
# Bazel saucelabs job resides in `manual.yml` because it's currently unstable, but
# kept as "runnable" for debugging/stabilization effort purposes.
bazel-saucelabs:
runs-on: ubuntu-latest-4core
env:
JOBS: 2
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
with:
cache-node-modules: true
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- name: Setup Saucelabs Variables
uses: angular/dev-infra/github-actions/saucelabs@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- name: Set up Sauce Tunnel Daemon
run: yarn bazel run //tools/saucelabs-daemon/background-service -- $JOBS &
env:
SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
- name: Run all saucelabs bazel tests
run: |
TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "fixme-saucelabs", ...)')
yarn bazel test --config=saucelabs --jobs=$JOBS ${TESTS}
+1 -1
View File
@@ -9,6 +9,6 @@ jobs:
status:
runs-on: ubuntu-latest
steps:
- uses: angular/dev-infra/github-actions/unified-status-check@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- uses: angular/dev-infra/github-actions/unified-status-check@c83e99a12397014162531ca125c94549db55dd84
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+55
View File
@@ -0,0 +1,55 @@
name: Monitoring
on:
schedule:
# Run at 10:00AM every day.
- cron: '0 10 * * *'
permissions: {}
defaults:
run:
shell: bash
jobs:
check_aio:
runs-on: ubuntu-latest
env:
MINIMUM_PWA_SCORE: 95
strategy:
fail-fast: false
matrix:
version: ['https://next.angular.io/', 'https://angular.io/']
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
with:
cache-node-modules: true
node-module-directories: |
./aio/node_modules
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
- name: Install node modules in aio
run: yarn install --frozen-lockfile --cwd aio
- name: Run basic e2e and deployment config tests.
run: yarn --cwd aio test-production-url --test_env=TARGET_URL="${{ matrix.version }}"
- name: Run PWA-score tests.
run: yarn --cwd aio test-pwa-score "${{ matrix.version }}" "${{ env.MINIMUM_PWA_SCORE }}"
- name: Run a11y tests.
run: yarn --cwd aio test-a11y-score "${{ matrix.version }}"
check_contributor_links:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
with:
cache-node-modules: true
node-module-directories: |
./aio/node_modules
- name: Install node modules in aio
run: yarn install --frozen-lockfile --cwd aio
- name: Check website provided in contributors.json file
run: node aio/scripts/test-external-urls.js
-179
View File
@@ -1,179 +0,0 @@
name: Pull Request
on:
pull_request:
types: [opened, synchronize, reopened]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions: {}
defaults:
run:
shell: bash
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
with:
cache-node-modules: true
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Check code lint
run: yarn -s tslint
- name: Check for circular dependencies
run: yarn -s ts-circular-deps:check
- name: Validate pull approve configuration
run: yarn -s ng-dev pullapprove verify
- name: Validate angular robot configuration
run: yarn -s ng-dev ngbot verify
- name: Confirm code builds with typescript as expected
run: yarn -s check-tooling-setup
- name: Check commit message
run: yarn ng-dev commit-message validate-range ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }}
- name: Check code format
run: yarn ng-dev format changed --check ${{ github.event.pull_request.base.sha }}
- name: Check Package Licenses
uses: angular/dev-infra/github-actions/linting/licenses@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
with:
allow-dependencies-licenses: 'pkg:npm/google-protobuf@'
devtools:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Run unit tests
run: yarn devtools:test
- name: Test build
run: yarn devtools:build:chrome
test:
runs-on: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- name: Install node modules
run: yarn install --frozen-lockfile --network-timeout 100000
- name: Run CI tests for framework
run: yarn test:ci
artifacts:
needs: [test]
if: needs.test.result == 'success'
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- name: Install node modules
run: yarn install --frozen-lockfile --network-timeout 100000
- name: Run CI tests for framework
run: yarn tsx ./scripts/build/build-packages-dist.mts
- name: Archive build artifacts
uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5
with:
name: pr-artifacts-${{ github.event.number }}
path: dist/packages-dist/
adev:
runs-on:
labels: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Build adev in fast mode to ensure it continues to work
run: yarn bazel build //adev:build --config=release
# TODO: re-enable tests once the next release is shipped
# Tests are broken because of https://github.com/angular/angular/issues/54858
# - name: Run tests
# run: yarn bazel test //adev/...
zone-js:
runs-on:
labels: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
with:
cache-node-modules: true
node-module-directories: |
./node_modules
./packages/zone.js/node_modules
./packages/zone.js/test/typings/node_modules
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
- name: Install node modules
run: yarn install --frozen-lockfile
- run: |
yarn bazel build \
//packages/zone.js/bundles:zone.umd.js \
//packages/zone.js:npm_package \
//packages/zone.js/test/closure:closure_js \
- run: |
rm -Rf packages/zone.js/build
rm -Rf packages/zone.js/test/extra/*.umd.js
mkdir -p packages/zone.js/build/
mkdir -p packages/zone.js/build/test/
cp dist/bin/packages/zone.js/bundles/zone.umd.js packages/zone.js/build/zone.umd.js
cp dist/bin/packages/zone.js/npm_package/bundles/zone-mix.umd.js ./packages/zone.js/test/extra/
cp dist/bin/packages/zone.js/npm_package/bundles/zone-patch-electron.umd.js ./packages/zone.js/test/extra/
cp dist/bin/packages/zone.js/test/closure/zone.closure.mjs ./packages/zone.js/build/test/zone.closure.mjs
# Install
- run: yarn --cwd packages/zone.js install --frozen-lockfile --non-interactive
# Run zone.js tools tests
- run: yarn --cwd packages/zone.js promisefinallytest
- run: yarn --cwd packages/zone.js jest:test
- run: yarn --cwd packages/zone.js jest:nodetest
- run: yarn --cwd packages/zone.js electrontest
- run: yarn --cwd packages/zone.js/test/typings install --frozen-lockfile --non-interactive
- run: yarn --cwd packages/zone.js/test/typings test
saucelabs:
runs-on: ubuntu-latest-4core
env:
SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
with:
cache-node-modules: true
- name: Install node modules
run: yarn install --frozen-lockfile
- uses: ./.github/actions/saucelabs-legacy
+4 -4
View File
@@ -25,12 +25,12 @@ jobs:
steps:
- name: 'Checkout code'
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
- name: 'Run analysis'
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
with:
results_file: results.sarif
results_format: sarif
@@ -39,7 +39,7 @@ jobs:
# Upload the results as artifacts.
- name: 'Upload artifact'
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: SARIF file
path: results.sarif
@@ -47,6 +47,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: 'Upload to code-scanning'
uses: github/codeql-action/upload-sarif@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13
uses: github/codeql-action/upload-sarif@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5
with:
sarif_file: results.sarif
+4 -4
View File
@@ -1,4 +1,4 @@
name: Update ADEV Angular CLI help
name: Update AIO Angular CLI help
on:
workflow_dispatch:
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
# Setting `persist-credentials: false` prevents the github-action account from being the
# account that is attempted to be used for authentication, instead the remote is set to
@@ -28,11 +28,11 @@ jobs:
# This is needed as otherwise the PR creation will fail with `shallow update not allowed` when the forked branch is not in sync.
fetch-depth: 0
- name: Generate CLI help
run: node adev/scripts/update-cli-help/index.mjs
run: node aio/scripts/update-cli-help/index.mjs
env:
ANGULAR_CLI_BUILDS_READONLY_GITHUB_TOKEN: ${{ secrets.ANGULAR_CLI_BUILDS_READONLY_GITHUB_TOKEN }}
- name: Create a PR (if necessary)
uses: angular/dev-infra/github-actions/create-pr-for-changes@8d3cb7b9e1b3edd1dc168376689d27407d2dfa7a
uses: angular/dev-infra/github-actions/create-pr-for-changes@c83e99a12397014162531ca125c94549db55dd84
with:
branch-prefix: update-cli-help
pr-title: 'docs: update Angular CLI help [${{github.ref_name}}]'
+2 -5
View File
@@ -56,11 +56,8 @@ baseline.json
# Husky
.husky/_
aio/content/examples/.DS_Store
# Ignore cache created with the Angular CLI.
.angular/
# Ignore AIO files, useful when changing branches
aio/node_modules
aio/out-tsc
.angular/
+55 -5
View File
@@ -7,7 +7,23 @@ export const format: FormatConfig = {
'prettier': {
'matchers': [
'**/*.{yaml,yml}',
'**/*.{js,ts}',
'adev/**/*.{js,ts}',
'devtools/**/*.{js,ts}',
'tools/**/*.{js,ts}',
'modules/**/*.{js,ts}',
'scripts/**/*.{js,ts}',
'packages/animations/**/*.{js,ts}',
'packages/bazel/**/*.{js,ts}',
'packages/benchpress/**/*.{js,ts}',
'packages/common/**/*.{js,ts}',
'packages/docs/**/*.{js,ts}',
'packages/elements/**/*.{js,ts}',
'packages/examples/**/*.{js,ts}',
'packages/misc/**/*.{js,ts}',
'packages/private/**/*.{js,ts}',
'packages/router/**/*.{js,ts}',
'packages/service-worker/**/*.{js,ts}',
'packages/upgrade/**/*.{js,ts}',
// Do not format d.ts files as they are generated
'!**/*.d.ts',
@@ -15,16 +31,50 @@ export const format: FormatConfig = {
// not be modified.
'!third_party/**',
'!.yarn/**',
],
},
'clang-format': {
'matchers': [
'**/*.{js,ts}',
// TODO: burn down format failures and remove aio and integration exceptions.
'!aio/**',
'!integration/**',
// Both third_party and .yarn are directories containing copied code which should
// not be modified.
'!third_party/**',
'!.yarn/**',
// Do not format d.ts files as they are generated
'!**/*.d.ts',
// Do not format generated ng-dev script
'!dev-infra/ng-dev.js',
'!dev-infra/build-worker.js',
// Do not format compliance test-cases since they must match generated code
'!packages/compiler-cli/test/compliance/test_cases/**/*.js',
// Do not format the locale files which are checked-in for Google3, but generated using
// the `generate-locales-tool` from `packages/common/locales`.
'!packages/core/src/i18n/locale_en.ts',
'!packages/common/locales/closure-locale.ts',
'!packages/common/src/i18n/currencies.ts',
// Test cases contain non valid code.
'!packages/compiler-cli/test/compliance/test_cases/**/*.{js,ts}',
// Temporarily disable formatting for adev
'!adev/**',
// Ignore generated javascript file(s)
'!.github/actions/deploy-docs-site/main.js',
// Migrated to prettier
'!devtools/**/*.{js,ts}',
'!tools/**/*.{js,ts}',
'!modules/**/*.{js,ts}',
'!scripts/**/*.{js,ts}',
'!packages/animations/**/*.{js,ts}',
'!packages/bazel/**/*.{js,ts}',
'!packages/benchpress/**/*.{js,ts}',
'!packages/common/**/*.{js,ts}',
'!packages/docs/**/*.{js,ts}',
'!packages/elements/**/*.{js,ts}',
'!packages/examples/**/*.{js,ts}',
'!packages/misc/**/*.{js,ts}',
'!packages/private/**/*.{js,ts}',
'!packages/router/**/*.{js,ts}',
'!packages/service-worker/**/*.{js,ts}',
'!packages/upgrade/**/*.{js,ts}',
],
},
'buildifier': true,
+1 -1
View File
@@ -28,7 +28,7 @@ export const release: ReleaseConfig = {
return performNpmReleaseBuild();
},
releaseNotes: {
hiddenScopes: ['bazel', 'dev-infra', 'docs-infra', 'zone.js', 'devtools'],
hiddenScopes: ['aio', 'bazel', 'dev-infra', 'docs-infra', 'zone.js', 'devtools'],
},
releasePrLabels: ['area: build & ci', 'action: merge', 'PullApprove: disable'],
};
+1 -1
View File
@@ -1 +1 @@
18.20.0
18.18.2
+2 -2
View File
@@ -1,10 +1,10 @@
{
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"tabs": false,
"embeddedLanguageFormatting": "off",
"singleQuote": true,
"semi": true,
"semicolon": true,
"quoteProps": "preserve",
"bracketSpacing": false
}
+567 -85
View File
@@ -57,7 +57,7 @@
version: 3
availability:
users_unavailable: ['atscott']
users_unavailable: []
# Meta field that goes unused by PullApprove to allow for defining aliases to be
# used throughout the config.
@@ -121,13 +121,26 @@ groups:
contains_any_globs(files, [
'packages/animations/**/{*,.*}',
'packages/platform-browser/animations/**/{*,.*}',
'aio/content/guide/animations.md',
'aio/content/guide/add-an-animation.md',
'aio/content/guide/animate-state-style.md',
'aio/content/guide/animation-api-summary.md',
'aio/content/guide/understanding-angular-animation.md',
'aio/content/examples/animations/**/{*,.*}',
'aio/content/images/guide/animations/**/{*,.*}',
'aio/content/guide/complex-animation-sequences.md',
'aio/content/guide/reusable-animations.md',
'aio/content/guide/route-animations.md',
'aio/content/guide/transition-and-triggers.md',
'aio/content/guide/animations-attach-to-html-template.md',
'aio/content/guide/animation-transition-timing.md'
])
reviewers:
users:
- alxhub
- crisbeto
- ~jelbourn
- thePunderWoman
- jessicajaniuk
# =========================================================
# Framework: Compiler
@@ -139,7 +152,13 @@ groups:
contains_any_globs(files, [
'packages/compiler/**/{*,.*}',
'packages/examples/compiler/**/{*,.*}',
'aio/content/examples/angular-compiler-options/**/{*,.*}',
'packages/compiler-cli/**/{*,.*}',
'aio/content/guide/angular-compiler-options.md',
'aio/content/guide/aot-compiler.md',
'aio/content/guide/aot-metadata-errors.md',
'aio/content/guide/template-typecheck.md',
'aio/content/extended-diagnostics/*.md'
])
reviewers:
users:
@@ -164,7 +183,7 @@ groups:
- crisbeto
- devversion
- dylhunn
- thePunderWoman
- jessicajaniuk
- AndrewKushnir
# =========================================================
@@ -174,13 +193,162 @@ groups:
<<: *defaults
conditions:
- >
contains_any_globs(files.exclude("packages/core/schematics/*").exclude('packages/core/primitives/*'), [
contains_any_globs(files.exclude("packages/core/schematics/*"), [
'packages/core/**/{*,.*}',
'packages/examples/core/**/{*,.*}',
'packages/platform-browser/**/{*,.*}',
'packages/examples/platform-browser/**/{*,.*}',
'packages/platform-browser-dynamic/**/{*,.*}',
'packages/docs/**/{*,.*}',
'aio/content/guide/accessibility.md',
'aio/content/examples/accessibility/**/{*,.*}',
'aio/content/guide/architecture-components.md',
'aio/content/guide/architecture-modules.md',
'aio/content/guide/architecture-next-steps.md',
'aio/content/guide/architecture-services.md',
'aio/content/guide/architecture.md',
'aio/content/examples/architecture/**/{*,.*}',
'aio/content/guide/rxjs-interop.md',
'aio/content/images/guide/architecture/**/{*,.*}',
'aio/content/guide/attribute-directives.md',
'aio/content/examples/attribute-directives/**/{*,.*}',
'aio/content/images/guide/attribute-directives/**/{*,.*}',
'aio/content/guide/bootstrapping.md',
'aio/content/examples/bootstrapping/**/{*,.*}',
'aio/content/guide/change-detection.md',
'aio/content/guide/change-detection-zone-pollution.md',
'aio/content/guide/change-detection-slow-computations.md',
'aio/content/guide/change-detection-skipping-subtrees.md',
'aio/content/images/guide/change-detection/**/{*,.*}',
'aio/content/guide/class-binding.md',
'aio/content/guide/cheatsheet.md',
'aio/content/guide/component-interaction.md',
'aio/content/examples/component-interaction/**/{*,.*}',
'aio/content/images/guide/component-interaction/**/{*,.*}',
'aio/content/guide/component-overview.md',
'aio/content/errors/*.md',
'aio/content/examples/errors/**/{*,.*}',
'aio/content/examples/component-overview/**/{*,.*}',
'aio/content/guide/component-styles.md',
'aio/content/guide/developer-guide-overview.md',
'aio/content/guide/view-encapsulation.md',
'aio/content/examples/component-styles/**/{*,.*}',
'aio/content/examples/content-projection/**/{*,.*}',
'aio/content/guide/content-projection.md',
'aio/content/guide/creating-injectable-service.md',
'aio/content/guide/dependency-injection-overview.md',
'aio/content/guide/dependency-injection.md',
'aio/content/examples/dependency-injection/**/{*,.*}',
'aio/content/images/guide/dependency-injection/**/{*,.*}',
'aio/content/guide/dependency-injection-in-action.md',
'aio/content/examples/dependency-injection-in-action/**/{*,.*}',
'aio/content/images/guide/dependency-injection-in-action/**/{*,.*}',
'aio/content/guide/dependency-injection-navtree.md',
'aio/content/guide/dependency-injection-providers.md',
'aio/content/guide/dependency-injection-context.md',
'aio/content/guide/lightweight-injection-tokens.md',
'aio/content/guide/displaying-data.md',
'aio/content/examples/displaying-data/**/{*,.*}',
'aio/content/images/guide/displaying-data/**/{*,.*}',
'aio/content/guide/dynamic-component-loader.md',
'aio/content/examples/dynamic-component-loader/**/{*,.*}',
'aio/content/images/guide/dynamic-component-loader/**/{*,.*}',
'aio/content/guide/example-apps-list.md',
'aio/content/guide/feature-modules.md',
'aio/content/examples/feature-modules/**/{*,.*}',
'aio/content/images/guide/feature-modules/**/{*,.*}',
'aio/content/guide/frequent-ngmodules.md',
'aio/content/images/guide/frequent-ngmodules/**/{*,.*}',
'aio/content/guide/hierarchical-dependency-injection.md',
'aio/content/examples/hierarchical-dependency-injection/**/{*,.*}',
'aio/content/guide/image-directive.md',
'aio/content/guide/image-directive-setup.md',
'aio/content/examples/providers-viewproviders/**/{*,.*}',
'aio/content/examples/resolution-modifiers/**/{*,.*}',
'aio/content/guide/lazy-loading-ngmodules.md',
'aio/content/examples/lazy-loading-ngmodules/**/{*,.*}',
'aio/content/images/guide/lazy-loading-ngmodules/**/{*,.*}',
'aio/content/guide/lifecycle-hooks.md',
'aio/content/examples/lifecycle-hooks/**/{*,.*}',
'aio/content/images/guide/lifecycle-hooks/**/{*,.*}',
'aio/content/examples/ngcontainer/**/{*,.*}',
'aio/content/guide/ngmodules.md',
'aio/content/examples/ngmodules/**/{*,.*}',
'aio/content/guide/ngmodule-api.md',
'aio/content/guide/ngmodule-faq.md',
'aio/content/guide/ngmodule-vs-jsmodule.md',
'aio/content/guide/module-types.md',
'aio/content/guide/template-overview.md',
'aio/content/guide/template-syntax.md',
'aio/content/guide/built-in-template-functions.md',
'aio/content/examples/built-in-template-functions/**/{*,.*}',
'aio/content/guide/event-binding.md',
'aio/content/guide/event-binding-concepts.md',
'aio/content/examples/event-binding/**/{*,.*}',
'aio/content/guide/interpolation.md',
'aio/content/examples/interpolation/**/{*,.*}',
'aio/content/examples/template-syntax/**/{*,.*}',
'aio/content/images/guide/template-syntax/**/{*,.*}',
'aio/content/guide/binding-overview.md',
'aio/content/guide/binding-syntax.md',
'aio/content/examples/binding-syntax/**/{*,.*}',
'aio/content/guide/property-binding.md',
'aio/content/examples/property-binding/**/{*,.*}',
'aio/content/guide/property-binding-best-practices.md',
'aio/content/guide/attribute-binding.md',
'aio/content/examples/attribute-binding/**/{*,.*}',
'aio/content/guide/two-way-binding.md',
'aio/content/examples/two-way-binding/**/{*,.*}',
'aio/content/guide/built-in-directives.md',
'aio/content/guide/directive-composition-api.md',
'aio/content/examples/built-in-directives/**/{*,.*}',
'aio/content/images/guide/built-in-directives/**/{*,.*}',
'aio/content/guide/template-reference-variables.md',
'aio/content/examples/template-reference-variables/**/{*,.*}',
'aio/content/guide/inputs-outputs.md',
'aio/content/examples/inputs-outputs/**/{*,.*}',
'aio/content/images/guide/inputs-outputs/**/{*,.*}',
'aio/content/guide/understanding-template-expr-overview.md',
'aio/content/guide/template-expression-operators.md',
'aio/content/examples/template-expression-operators/**/{*,.*}',
'aio/content/guide/pipes.md',
'aio/content/guide/pipes-custom-data-trans.md',
'aio/content/guide/pipes-overview.md',
'aio/content/guide/pipe-precedence.md',
'aio/content/guide/pipes-transform-data.md',
'aio/content/guide/pipe-template.md',
'aio/content/examples/pipes/**/{*,.*}',
'aio/content/images/guide/pipes/**/{*,.*}',
'aio/content/guide/providers.md',
'aio/content/examples/providers/**/{*,.*}',
'aio/content/images/guide/providers/**/{*,.*}',
'aio/content/guide/signal-queries.md',
'aio/content/guide/singleton-services.md',
'aio/content/guide/signal-inputs.md',
'aio/content/guide/sharing-ngmodules.md',
'aio/content/guide/standalone-components.md',
'aio/content/guide/standalone-migration.md',
'aio/content/guide/structural-directives.md',
'aio/content/examples/structural-directives/**/{*,.*}',
'aio/content/guide/svg-in-templates.md',
'aio/content/guide/style-precedence.md',
'aio/content/images/guide/structural-directives/**/{*,.*}',
'aio/content/guide/template-statements.md',
'aio/content/guide/understanding-angular-overview.md',
'aio/content/guide/user-input.md',
'aio/content/examples/user-input/**/{*,.*}',
'aio/content/images/guide/user-input/**/{*,.*}',
'aio/content/guide/view-encapsulation.md',
'aio/content/examples/view-encapsulation/**/{*,.*}',
'aio/content/images/guide/view-encapsulation/**/{*,.*}',
'aio/content/special-elements/**/{*,.*}',
'aio/content/blocks/**/{*,.*}',
'aio/content/guide/hydration.md',
'aio/content/guide/defer.md',
'aio/content/guide/signals.md',
'aio/content/guide/control_flow.md',
'aio/content/guide/model-inputs.md',
'aio/content/examples/injection-token/**/{*,.*}',
])
reviewers:
users:
@@ -190,7 +358,7 @@ groups:
- crisbeto
- devversion
- dylhunn
- thePunderWoman
- jessicajaniuk
- pkozlowski-opensource
# =========================================================
@@ -210,7 +378,7 @@ groups:
- AndrewKushnir
- atscott
- dylhunn
- thePunderWoman
- jessicajaniuk
- pkozlowski-opensource
# =========================================================
@@ -223,6 +391,23 @@ groups:
contains_any_globs(files, [
'packages/common/http/**/{*,.*}',
'packages/examples/http/**/{*,.*}',
'aio/content/guide/understanding-communicating-with-http.md',
'aio/content/guide/http-configure-http-url-parameters.md',
'aio/content/guide/http-handle-request-errors.md',
'aio/content/guide/http-intercept-requests-and-responses.md',
'aio/content/guide/http-interceptor-use-cases.md',
'aio/content/guide/http-make-jsonp-request.md',
'aio/content/guide/http-optimize-server-interaction.md',
'aio/content/guide/http-pass-metadata-to-interceptors.md',
'aio/content/guide/http-request-data-from-server.md',
'aio/content/guide/http-security-xsrf-protection.md',
'aio/content/guide/http-send-data-to-server.md',
'aio/content/guide/http-server-communication.md',
'aio/content/guide/http-setup-server-communication.md',
'aio/content/guide/http-test-requests.md',
'aio/content/guide/http-track-show-request-progress.md',
'aio/content/examples/http/**/{*,.*}',
'aio/content/images/guide/http/**/{*,.*}'
])
reviewers:
users:
@@ -230,7 +415,7 @@ groups:
- AndrewKushnir
- atscott
- dylhunn
- thePunderWoman
- jessicajaniuk
- pkozlowski-opensource
# =========================================================
@@ -242,6 +427,9 @@ groups:
- >
contains_any_globs(files, [
'packages/elements/**/{*,.*}',
'aio/content/examples/elements/**/{*,.*}',
'aio/content/images/guide/elements/**/{*,.*}',
'aio/content/guide/elements.md'
])
reviewers:
users:
@@ -250,7 +438,7 @@ groups:
- andrewseguin
- atscott
- dylhunn
- thePunderWoman
- jessicajaniuk
- pkozlowski-opensource
# =========================================================
@@ -263,6 +451,22 @@ groups:
contains_any_globs(files, [
'packages/forms/**/{*,.*}',
'packages/examples/forms/**/{*,.*}',
'aio/content/guide/forms.md',
'aio/content/examples/forms/**/{*,.*}',
'aio/content/images/guide/forms/**/{*,.*}',
'aio/content/guide/forms-overview.md',
'aio/content/examples/forms-overview/**/{*,.*}',
'aio/content/images/guide/forms-overview/**/{*,.*}',
'aio/content/guide/form-validation.md',
'aio/content/guide/typed-forms.md',
'aio/content/examples/form-validation/**/{*,.*}',
'aio/content/images/guide/form-validation/**/{*,.*}',
'aio/content/guide/dynamic-form.md',
'aio/content/examples/dynamic-form/**/{*,.*}',
'aio/content/images/guide/dynamic-form/**/{*,.*}',
'aio/content/guide/reactive-forms.md',
'aio/content/examples/reactive-forms/**/{*,.*}',
'aio/content/images/guide/reactive-forms/**/{*,.*}'
])
reviewers:
users:
@@ -291,6 +495,21 @@ groups:
'packages/compiler/src/render3/view/i18n/**/{*,.*}',
'packages/compiler-cli/src/extract_i18n.ts',
'packages/localize/**/{*,.*}',
'aio/content/guide/i18n-overview.md',
'aio/content/guide/i18n-common-overview.md',
'aio/content/guide/i18n-common-add-package.md',
'aio/content/guide/i18n-common-locale-id.md',
'aio/content/guide/i18n-common-format-data-locale.md',
'aio/content/guide/i18n-common-prepare.md',
'aio/content/guide/i18n-common-translation-files.md',
'aio/content/guide/i18n-common-merge.md',
'aio/content/guide/i18n-common-deploy.md',
'aio/content/guide/i18n-example.md',
'aio/content/guide/i18n-optional-overview.md',
'aio/content/guide/i18n-optional-manual-runtime-locale.md',
'aio/content/guide/i18n-optional-import-global-variants.md',
'aio/content/guide/i18n-optional-manage-marked-text.md',
'aio/content/examples/i18n/**/{*,.*}'
])
reviewers:
users:
@@ -306,6 +525,9 @@ groups:
- >
contains_any_globs(files, [
'packages/platform-server/**/{*,.*}',
'aio/content/guide/prerendering.md',
'aio/content/guide/ssr.md',
'aio/content/examples/ssr/**/{*,.*}',
])
reviewers:
users:
@@ -314,7 +536,7 @@ groups:
- AndrewKushnir
- atscott
- dylhunn
- thePunderWoman
- jessicajaniuk
- pkozlowski-opensource
# =========================================================
@@ -327,6 +549,16 @@ groups:
contains_any_globs(files, [
'packages/router/**/{*,.*}',
'packages/examples/router/**/{*,.*}',
'aio/content/guide/router.md',
'aio/content/guide/router-tutorial.md',
'aio/content/guide/router-tutorial-toh.md',
'aio/content/guide/routing-overview.md',
'aio/content/guide/router-reference.md',
'aio/content/examples/router-tutorial/**/{*,.*}',
'aio/content/examples/router/**/{*,.*}',
'aio/content/images/guide/router/**/{*,.*}',
'aio/content/guide/routing-with-urlmatcher.md',
'aio/content/examples/routing-with-urlmatcher/**/{*,.*}'
])
reviewers:
users:
@@ -343,6 +575,15 @@ groups:
contains_any_globs(files, [
'packages/service-worker/**/{*,.*}',
'packages/examples/service-worker/**/{*,.*}',
'aio/content/guide/service-worker-getting-started.md',
'aio/content/examples/service-worker-getting-started/**/{*,.*}',
'aio/content/guide/app-shell.md',
'aio/content/guide/service-worker-communications.md',
'aio/content/guide/service-worker-config.md',
'aio/content/guide/service-worker-devops.md',
'aio/content/guide/service-worker-intro.md',
'aio/content/guide/service-worker-notifications.md',
'aio/content/images/guide/service-worker/**/{*,.*}'
])
reviewers:
users:
@@ -359,11 +600,22 @@ groups:
'packages/upgrade/**/{*,.*}',
'packages/common/upgrade/**/{*,.*}',
'packages/examples/upgrade/**/{*,.*}',
'aio/content/guide/upgrade.md',
'aio/content/examples/upgrade-lazy-load-ajs/**/{*,.*}',
'aio/content/examples/upgrade-module/**/{*,.*}',
'aio/content/images/guide/upgrade/**/{*,.*}',
'aio/content/examples/upgrade-phonecat-1-typescript/**/{*,.*}',
'aio/content/examples/upgrade-phonecat-2-hybrid/**/{*,.*}',
'aio/content/examples/upgrade-phonecat-3-final/**/{*,.*}',
'aio/content/guide/upgrade-performance.md',
'aio/content/guide/upgrade-setup.md',
'aio/content/guide/ajs-quick-reference.md',
'aio/content/examples/ajs-quick-reference/**/{*,.*}'
])
reviewers:
users:
- alxhub
- thePunderWoman
- jessicajaniuk
# =========================================================
# Framework: Testing
@@ -372,8 +624,19 @@ groups:
<<: *defaults
conditions:
- >
contains_any_globs(files.exclude('packages/compiler-cli/*').exclude('packages/language-service/*').exclude('packages/service-worker/*').exclude('packages/core/schematics/*'), [
contains_any_globs(files.exclude('packages/compiler-cli/*').exclude('packages/language-service/*').exclude('packages/service-worker/*'), [
'packages/**/testing/**/{*,.*}',
'aio/content/guide/testing.md',
'aio/content/guide/test-debugging.md',
'aio/content/guide/testing-attribute-directives.md',
'aio/content/guide/testing-code-coverage.md',
'aio/content/guide/testing-components-basics.md',
'aio/content/guide/testing-components-scenarios.md',
'aio/content/guide/testing-pipes.md',
'aio/content/guide/testing-services.md',
'aio/content/guide/testing-utility-apis.md',
'aio/content/examples/testing/**/{*,.*}',
'aio/content/images/guide/testing/**/{*,.*}'
])
reviewers:
users:
@@ -381,7 +644,7 @@ groups:
- AndrewKushnir
- atscott
- dylhunn
- thePunderWoman
- jessicajaniuk
- pkozlowski-opensource
# =========================================================
@@ -400,7 +663,7 @@ groups:
- AndrewKushnir
- atscott
- dylhunn
- thePunderWoman
- jessicajaniuk
- pkozlowski-opensource
# =========================================================
@@ -419,7 +682,7 @@ groups:
- AndrewKushnir
- atscott
- dylhunn
- thePunderWoman
- jessicajaniuk
- pkozlowski-opensource
# =========================================================
@@ -437,6 +700,9 @@ groups:
'packages/tsconfig-tsec-base.json',
'packages/tsec-exemption.json',
'tools/tsec.bzl',
'aio/content/guide/security.md',
'aio/content/examples/security/**/{*,.*}',
'aio/content/images/guide/security/**/{*,.*}',
])
reviewers:
users:
@@ -473,12 +739,13 @@ groups:
- >
contains_any_globs(files, [
'packages/language-service/**/{*,.*}',
'aio/content/guide/language-service.md',
'aio/content/images/guide/language-service/**/{*,.*}'
])
reviewers:
users:
- alxhub
- atscott
- dylhunn
# =========================================================
# zone.js
@@ -489,6 +756,7 @@ groups:
- >
contains_any_globs(files, [
'packages/zone.js/**/{*,.*}',
'aio/content/guide/zone.md'
])
reviewers:
users:
@@ -510,7 +778,7 @@ groups:
- atscott
- crisbeto
- dylhunn
- thePunderWoman
- jessicajaniuk
- pkozlowski-opensource
# =========================================================
@@ -545,8 +813,108 @@ groups:
- AndrewKushnir
- atscott
- dylhunn
- thePunderWoman
- jessicajaniuk
- josephperrott
- pkozlowski-opensource
# =========================================================
# Docs: Contributors
# =========================================================
docs-contributors:
<<: *defaults
conditions:
- >
contains_any_globs(files, [
'aio/content/marketing/contributors.json',
'aio/content/images/bios/**/{*,.*}',
])
reviewers:
users:
- MarkTechson
- mgechev
- twerske
# =========================================================
# Docs: Gettings Started & Tutorial
# =========================================================
docs-getting-started-and-tutorial:
<<: *defaults
conditions:
- >
contains_any_globs(files, [
'aio/content/guide/setup-local.md',
'aio/content/images/guide/setup-local/**/{*,.*}',
'aio/content/tutorial/**/{*,.*}',
'aio/content/images/guide/toh/**/{*,.*}',
'aio/content/demos/first-app/**/{*,.*}',
'aio/content/examples/first-app-*/**/{*,.*}',
'aio/content/images/guide/faa/**/{*,.*}',
'aio/content/examples/toh-pt0/**/{*,.*}',
'aio/content/examples/toh-pt1/**/{*,.*}',
'aio/content/examples/toh-pt2/**/{*,.*}',
'aio/content/examples/toh-pt3/**/{*,.*}',
'aio/content/examples/toh-pt4/**/{*,.*}',
'aio/content/examples/toh-pt5/**/{*,.*}',
'aio/content/examples/toh-pt6/**/{*,.*}',
'aio/content/examples/getting-started-v0/**/{*,.*}',
'aio/content/examples/getting-started/**/{*,.*}',
'aio/content/start/**/{*,.*}',
'aio/content/guide/quick-start.md',
'aio/content/images/guide/start/**/{*,.*}',
'aio/content/examples/what-is-angular/**/{*,.*}',
'aio/content/guide/what-is-angular.md'
])
reviewers:
users:
- AndrewKushnir
- jessicajaniuk
# =========================================================
# Docs: Marketing
# =========================================================
docs-marketing:
<<: *defaults
conditions:
- >
contains_any_globs(files.exclude("aio/content/marketing/contributors.json"), [
'aio/content/examples/marketing-first-app/**/{*,.*}',
'aio/content/guide/roadmap.md',
'aio/content/marketing/**/{*,.*}',
'aio/content/images/marketing/**/{*,.*}',
'aio/content/file-not-found.md',
'aio/content/license.md',
'aio/content/navigation.json'
])
reviewers:
users:
- MarkTechson
- mgechev
- twerske
# =========================================================
# Docs: Observables
# =========================================================
docs-observables:
<<: *defaults
conditions:
- >
contains_any_globs(files, [
'aio/content/guide/observables.md',
'aio/content/examples/observables/**/{*,.*}',
'aio/content/guide/comparing-observables.md',
'aio/content/examples/comparing-observables/**/{*,.*}',
'aio/content/guide/observables-in-angular.md',
'aio/content/examples/observables-in-angular/**/{*,.*}',
'aio/content/guide/practical-observable-usage.md',
'aio/content/examples/practical-observable-usage/**/{*,.*}',
'aio/content/guide/rx-library.md',
'aio/content/examples/rx-library/**/{*,.*}'
])
reviewers:
users:
- alxhub
- atscott
- dylhunn
# =========================================================
# Docs: Packaging, Tooling, Releasing
@@ -556,16 +924,48 @@ groups:
conditions:
- >
contains_any_globs(files, [
'contributing-docs/public-api-surface.md',
'docs/PUBLIC_API.md',
'docs/RELEASE_SCHEDULE.md',
'aio/content/guide/npm-packages.md',
'aio/content/guide/browser-support.md',
'aio/content/guide/releases.md',
'aio/content/guide/updating.md',
'aio/content/guide/deprecations.md',
'aio/content/examples/deprecation-guide/**/{*,.*}',
'aio/content/guide/migration-module-with-providers.md',
'aio/content/guide/static-query-migration.md',
'aio/content/guide/update-to-latest-version.md',
'aio/content/guide/versions.md',
'aio/content/guide/update-to-version-14.md',
'aio/content/guide/update-to-version-15.md',
'aio/content/guide/update-to-version-16.md',
'aio/content/guide/update-to-version-17.md',
])
reviewers:
users:
- alxhub
- AndrewKushnir
- atscott
- thePunderWoman
- jessicajaniuk
- jelbourn
# =========================================================
# Docs: Angular DevTools
# =========================================================
docs-devtools:
<<: *defaults
conditions:
- >
contains_any_globs(files, [
'aio/content/guide/devtools.md',
'aio/content/images/guide/devtools/**/{*,.*}'
])
reviewers:
users:
- MarkTechson
- mgechev
- twerske
# =========================================================
# Tooling: Compiler API shared with Angular CLI
#
@@ -589,6 +989,80 @@ groups:
required: 2 # require at least 2 approvals
reviewed_for: required
# =========================================================
# Docs: CLI
# =========================================================
docs-cli:
<<: *defaults
conditions:
- >
contains_any_globs(files, [
'aio/content/cli/**/{*,.*}',
'aio/content/guide/typescript-configuration.md',
'aio/content/examples/setup/**/{*,.*}',
'aio/content/guide/build.md',
'aio/content/images/guide/build/**/{*,.*}',
'aio/content/images/guide/cli/**/{*,.*}',
'aio/content/guide/cli-builder.md',
'aio/content/examples/cli-builder/**/{*,.*}',
'aio/content/guide/deployment.md',
'aio/content/images/guide/deployment/**/{*,.*}',
'aio/content/guide/file-structure.md',
'aio/content/guide/ivy.md',
'aio/content/guide/strict-mode.md',
'aio/content/guide/web-worker.md',
'aio/content/guide/workspace-config.md',
'aio/content/guide/migration-solution-style-tsconfig.md',
'aio/content/guide/migration-update-libraries-tslib.md',
'aio/content/guide/esbuild.md',
])
reviewers:
users:
- alan-agius4
- clydin
- dgp1130
- mgechev
# =========================================================
# Docs: CLI Libraries
# =========================================================
docs-libraries:
<<: *defaults
conditions:
- >
contains_any_globs(files, [
'aio/content/examples/angular-linker-plugin/webpack.config.mjs',
'aio/content/guide/creating-libraries.md',
'aio/content/guide/libraries.md',
'aio/content/guide/using-libraries.md',
'aio/content/guide/angular-package-format.md'
])
reviewers:
users:
- alan-agius4
- alxhub
- mgechev
# =========================================================
# Docs: Schematics
# =========================================================
docs-schematics:
<<: *defaults
conditions:
- >
contains_any_globs(files, [
'aio/content/guide/schematics.md',
'aio/content/guide/schematics-authoring.md',
'aio/content/guide/schematics-for-libraries.md',
'aio/content/images/guide/schematics/**/{*,.*}',
'aio/content/examples/schematics-for-libraries/**/{*,.*}'
])
reviewers:
users:
- alan-agius4
- dylhunn
- mgechev
# =========================================================
# Docs: Angular Dev Site
# =========================================================
@@ -597,25 +1071,81 @@ groups:
conditions:
- >
contains_any_globs(files, [
'adev/**/{.*,*}',
'aio/**/{.*,*}'
'adev/**/{.*,*}'
])
reviewers:
users:
- josephperrott
- jelbourn
- bencodezen
- thePunderWoman
- jessicajaniuk
- dylhunn
- AndrewKushnir
- alxhub
- crisbeto
- devversion
- atscott
- pkozlowski-opensource
- MarkTechson
- ~JeanMeche
# =========================================================
# Docs-infra
# =========================================================
docs-infra:
<<: *defaults
conditions:
- >
contains_any_globs(files, [
'aio/{*,.*}',
'aio/aio-builds-setup/**/{*,.*}',
'aio/content/cli-src/**/{*,.*}',
'aio/content/examples/{*,.*}',
'aio/scripts/**/{*,.*}',
'aio/src/**/{*,.*}',
'aio/tests/**/{*,.*}',
'aio/tools/**/{*,.*}',
'aio/tools/examples/shared/boilerplate/cli/.vscode/**/{*,.*}',
'aio/content/BUILD.bazel',
'aio/content/images/guide/contributors-guide/**/{*,.*}',
'aio/content/guide/contributors-guide-overview.md',
'aio/content/guide/docs-style-guide.md',
'aio/content/examples/docs-style-guide/**/{*,.*}',
'aio/content/images/guide/docs-style-guide/**/{*,.*}',
'aio/content/guide/docs-lint-errors.md',
'aio/content/images/guide/docs-lint-errors/**/{*,.*}',
'aio/content/guide/localized-documentation.md',
'aio/content/guide/localizing-angular.md',
'aio/content/guide/reviewing-content.md',
'aio/content/guide/updating-content-github-ui.md',
'aio/content/guide/updating-search-keywords.md',
'aio/content/guide/doc-build-test.md',
'aio/content/guide/doc-edit-finish.md',
'aio/content/guide/doc-editing.md',
'aio/content/guide/doc-github-tasks.md',
'aio/content/guide/doc-pr-open.md',
'aio/content/guide/doc-pr-prep.md',
'aio/content/guide/doc-pr-update.md',
'aio/content/guide/doc-prepare-to-edit.md',
'aio/content/guide/doc-select-issue.md',
'aio/content/guide/doc-tasks.md',
'aio/content/guide/doc-update-overview.md',
'aio/content/guide/doc-update-start.md',
'aio/content/guide/docs-lint-errors.md',
'aio/content/images/guide/preparing-to-edit-docs/**/{*,.*}',
'aio/content/images/guide/github-for-docs/**/{*,.*}',
'aio/content/images/guide/doc-update-start/**/{*,.*}',
'aio/content/images/guide/doc-update-overview/**/{*,.*}',
'aio/content/images/guide/doc-pr-update/**/{*,.*}',
'aio/content/images/guide/doc-pr-prep/**/{*,.*}',
'aio/content/images/guide/doc-pr-open/**/{*,.*}',
'aio/content/images/guide/doc-github-tasks/**/{*,.*}',
'aio/content/images/guide/doc-prepare-to-edit/**/{*,.*}',
])
reviewers:
users:
- AndrewKushnir
- jessicajaniuk
- josephperrott
# =========================================================
# Angular DevTools
# =========================================================
@@ -633,7 +1163,7 @@ groups:
- dgp1130
- josephperrott
- mgechev
- MarkTechson
- twerske
- ~JeanMeche
# =========================================================
@@ -652,8 +1182,8 @@ groups:
'.ng-dev/**/{*,.*}',
'.vscode/**/{*,.*}',
'.yarn/**/{*,.*}',
'contributing-docs/*.md',
'contributing-docs/images/**/{*,.*}',
'docs/*.md',
'docs/images/**/{*,.*}',
'goldens/{*,.*}',
'goldens/public-api/manage.js',
'modules/{*,.*}',
@@ -698,6 +1228,12 @@ groups:
- >
contains_any_globs(files.exclude("goldens/public-api/manage.js"), [
'goldens/public-api/**/{*,.*}',
'docs/NAMING.md',
'aio/content/guide/angular-package-format.md',
'aio/content/guide/glossary.md',
'aio/content/guide/styleguide.md',
'aio/content/examples/styleguide/**/{*,.*}',
'aio/content/images/guide/styleguide/{*,.*}'
])
reviewers:
users:
@@ -706,11 +1242,8 @@ groups:
- atscott
- dylhunn
- ~jelbourn
- thePunderWoman
- jessicajaniuk
- pkozlowski-opensource
- ~iteriani
- ~tbondwilkinson
- ~rahatarmanahmed
reviews:
request: 3 # Request reviews from 3 people
required: 2 # Require that 2 people approve
@@ -735,7 +1268,7 @@ groups:
- atscott
- dylhunn
- ~jelbourn
- thePunderWoman
- jessicajaniuk
- pkozlowski-opensource
reviews:
request: 2 # Request reviews from 2 people
@@ -761,7 +1294,7 @@ groups:
- atscott
- dylhunn
- ~jelbourn
- thePunderWoman
- jessicajaniuk
- pkozlowski-opensource
####################################################################################
@@ -785,60 +1318,9 @@ groups:
- andrewseguin
- dgp1130
- ~jelbourn
- thePunderWoman
- jessicajaniuk
- josephperrott
# =========================================================
# Primitives
# =========================================================
# Angular team required reviews
primitives:
<<: *defaults
conditions:
- >
contains_any_globs(files, [
'packages/core/primitives/**/{*,.*}',
])
reviewers:
users:
- pkozlowski-opensource # Pawel Kozlowski
- alxhub # Alex Rickabaugh
- thePunderWoman # Jessica Janiuk
- dylhunn # Dylan Hunn
- AndrewKushnir # Andrew Kushnir
- atscott # Andrew Scott
labels:
pending: 'requires: TGP'
approved: 'requires: TGP'
rejected: 'requires: TGP'
# External team required reviews
primitives-shared:
<<: *defaults
conditions:
- >
contains_any_globs(files, [
'packages/core/primitives/**/{*,.*}',
])
reviewers:
users:
- csmick # Cameron Smick
- lannka # Hongfei Ding
- eduhmc # Eduardo Huerta-Mercado
- ehlemur # Edward Lesmes
- ellenyuan # Ellen Yuan
- jesse-good # Jesse Costello-Good
- mturco # Matt Turco
- neonstalwart # Ben Hockey
- iteriani # Thomas Nguyen
- tbondwilkinson # Tom Wilkinson
- rahatarmanahmed # Rahat Ahmed
labels:
pending: 'requires: TGP'
approved: 'requires: TGP'
rejected: 'requires: TGP'
####################################################################################
# Override managed result groups
#
+1
View File
@@ -6,5 +6,6 @@
"recommendations": [
"BazelBuild.vscode-bazel",
"ms-vscode.vscode-typescript-tslint-plugin",
"xaver.clang-format",
],
}
+8
View File
@@ -1,10 +1,16 @@
{
// Format js and ts files on save with `clang-format.executable`
// If `clang-format.executable` is not being used, these two settings should be removed otherwise it will break existing formatting.
// You can instead run `yarn gulp format` to manually format your code.
"[javascript]": {
"editor.formatOnSave": true,
},
"[typescript]": {
"editor.formatOnSave": true,
},
// Please install https://marketplace.visualstudio.com/items?itemName=xaver.clang-format to take advantage of `clang-format` in VSCode.
// (See https://clang.llvm.org/docs/ClangFormat.html for more info `clang-format`.)
"clang-format.executable": "${workspaceRoot}/node_modules/.bin/clang-format",
// Exclude third party modules and build artifacts from the editor watchers/searches.
"files.watcherExclude": {
"**/.git/objects/**": true,
@@ -12,12 +18,14 @@
"**/node_modules/**": true,
"**/bazel-out/**": true,
"**/dist/**": true,
"**/aio/src/generated/**": true,
},
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"**/bazel-out": true,
"**/dist": true,
"**/aio/src/generated": true,
".history": true,
},
"git.ignoreLimitWarning": true,
+1
View File
@@ -67,6 +67,7 @@ nodejs_binary(
data = [".yarn/releases/yarn-1.22.19.cjs"],
entry_point = ".yarn/releases/yarn-1.22.19.cjs",
visibility = [
"//aio/tools/examples/shared:__pkg__",
"//integration:__subpackages__",
],
)
+1724 -3733
View File
File diff suppressed because it is too large Load Diff
+13 -8
View File
@@ -32,7 +32,7 @@ Stack Overflow is a much better place to ask questions since:
To save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow.
If you would like to chat about the question in real-time, you can reach out via [the Angular community Discord server][discord].
If you would like to chat about the question in real-time, you can reach out via [our Discord server][discord].
## <a name="issue"></a> Found a Bug?
@@ -89,7 +89,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
We cannot accept code without a signed CLA.
Make sure you author all contributed Git commits with email address associated with your CLA signature.
4. [Fork](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) the [angular/angular](https://github.com/angular/angular/fork) repo.
4. [Fork](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) the angular/angular repo.
5. In your forked repository, make your changes in a new git branch:
@@ -138,7 +138,7 @@ If we ask for changes via code reviews then:
git push
```
For more info on working with fixup commits see [here](./contributing-docs/using-fixup-commits.md).
For more info on working with fixup commits see [here](docs/FIXUP_COMMITS.md).
That's it! Thank you for your contribution!
@@ -207,7 +207,7 @@ To ensure consistency throughout the source code, keep these rules in mind as yo
* All public API methods **must be documented**.
* We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at **100 characters**.
An automated formatter is available, see [DEVELOPER.md](contributing-docs/building-and-testing-angular.md#formatting-your-source-code).
An automated formatter is available, see [DEVELOPER.md](docs/DEVELOPER.md#clang-format).
## <a name="commit"></a> Commit Message Format
@@ -302,7 +302,7 @@ There are currently a few exceptions to the "use package name" rule:
* `dev-infra`: used for dev-infra related changes within the directories /scripts and /tools
* `docs-infra`: used for docs-app (angular.dev) related changes within the /adev directory of the repo
* `docs-infra`: used for docs-app (angular.io) related changes within the /aio directory of the repo
* `migrations`: used for changes to the `ng update` migrations.
@@ -353,9 +353,9 @@ DEPRECATED: <what is deprecated>
Closes #<pr number>
```
Breaking Change section should start with the phrase `BREAKING CHANGE: ` followed by a summary of the breaking change, a blank line, and a detailed description of the breaking change that also includes migration instructions.
Breaking Change section should start with the phrase "BREAKING CHANGE: " followed by a summary of the breaking change, a blank line, and a detailed description of the breaking change that also includes migration instructions.
Similarly, a Deprecation section should start with `DEPRECATED: ` followed by a short description of what is deprecated, a blank line, and a detailed description of the deprecation that also mentions the recommended update path.
Similarly, a Deprecation section should start with "DEPRECATED: " followed by a short description of what is deprecated, a blank line, and a detailed description of the deprecation that also mentions the recommended update path.
### Revert commits
@@ -389,11 +389,16 @@ The following documents can help you sort out issues with GitHub accounts and mu
[angular-group]: https://groups.google.com/forum/#!forum/angular
[coc]: https://github.com/angular/code-of-conduct/blob/main/CODE_OF_CONDUCT.md
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
[corporate-cla]: https://cla.developers.google.com/about/google-corporate
[dev-doc]: ./contributing-docs/building-and-testing-angular.md
[dev-doc]: https://github.com/angular/angular/blob/main/docs/DEVELOPER.md
[github]: https://github.com/angular/angular
[discord]: https://discord.gg/angular
[individual-cla]: https://cla.developers.google.com/about/google-individual
[js-style-guide]: https://google.github.io/styleguide/jsguide.html
[jsfiddle]: https://jsfiddle.net/
[plunker]: https://plnkr.co/edit
[runnable]: https://runnable.com/
[stackoverflow]: https://stackoverflow.com/questions/tagged/angular
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2010-2024 Google LLC. https://angular.dev/license
Copyright (c) 2010-2024 Google LLC. https://angular.io/license
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+22 -7
View File
@@ -1,7 +1,7 @@
<h1 align="center">Angular - The modern web developer's platform</h1>
<p align="center">
<img src="adev/src/assets/images/press-kit/angular_icon_gradient.gif" alt="angular-logo" width="120px" height="120px"/>
<img src="aio/src/assets/images/logos/angular/angular_renaissance.png" alt="angular-logo" width="120px" height="120px"/>
<br>
<em>Angular is a development platform for building mobile and desktop web applications
<br> using TypeScript/JavaScript and other languages.</em>
@@ -18,7 +18,7 @@
·
<a href="https://github.com/angular/angular/issues">Submit an Issue</a>
·
<a href="https://blog.angular.dev/">Blog</a>
<a href="https://blog.angular.io/">Blog</a>
<br>
<br>
</p>
@@ -63,7 +63,22 @@ Get started with Angular, learn the fundamentals and explore advanced topics on
### Local Development
To contribute to the Angular Docs, check out the [Angular.dev README](adev/README.md)
To contribute to Angular docs, you can setup a local environment with the following commands:
```bash
# Clone Angular repo
git clone https://github.com/angular/angular.git
# Navigate to project directory
cd angular
# Install dependencies
yarn
# Build and run local dev server
# Note: Initial build will take some time
yarn docs
```
## Development Setup
@@ -101,7 +116,7 @@ Angular is cross-platform, fast, scalable, has incredible tooling, and is loved
## Ecosystem
<p>
<img src="/contributing-docs/images/angular-ecosystem-logos.png" alt="angular ecosystem logos" width="500px" height="auto">
<img src="/docs/images/angular-ecosystem-logos.png" alt="angular ecosystem logos" width="500px" height="auto">
</p>
- [Angular Command Line (CLI)][cli]
@@ -113,7 +128,7 @@ Angular is cross-platform, fast, scalable, has incredible tooling, and is loved
## Upgrading
Check out our [upgrade guide](https://angular.dev/update-guide/) to find out the best way to upgrade your project.
Check out our [upgrade guide](https://update.angular.io/) to find out the best way to upgrade your project.
## Contributing
@@ -152,8 +167,8 @@ Join the conversation and help the community.
[angularmaterial]: https://material.angular.io/
[cli]: https://angular.dev/tools/cli
[architecture]: https://angular.dev/essentials
[componentstemplates]: https://angular.dev/tutorials/learn-angular/1-components-in-angular
[forms]: https://angular.dev/tutorials/learn-angular/15-forms
[componentstemplates]: https://angular.dev/tutorials/learn-angular/components-in-angular
[forms]: https://angular.dev/tutorials/learn-angular/forms
[api]: https://angular.dev/api
[angularelements]: https://angular.dev/guide/elements
[ssr]: https://angular.dev/guide/ssr
+1 -1
View File
@@ -1,3 +1,3 @@
Angular is part of Google [Open Source Software Vulnerability Reward Program](https://bughunters.google.com/about/rules/6521337925468160/google-open-source-software-vulnerability-reward-program-rules). For vulnerabilities in Angular, please submit your report [here](https://bughunters.google.com/report).
For more information, check out [Angular's security policy](https://angular.dev/best-practices/security).
For more information, check out [Angular's security policy](https://angular.io/guide/security).
+75 -24
View File
@@ -2,12 +2,27 @@ workspace(
name = "angular",
managed_directories = {
"@npm": ["node_modules"],
"@aio_npm": ["aio/node_modules"],
},
)
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("//:yarn.bzl", "YARN_LABEL")
# Add a patch fix for rules_webtesting v0.3.5 required for enabling runfiles on Windows.
# TODO: Remove the http_archive for this transitive dependency when a release is cut
# for https://github.com/bazelbuild/rules_webtesting/commit/581b1557e382f93419da6a03b91a45c2ac9a9ec8
# and the version is updated in rules_nodejs.
http_archive(
name = "io_bazel_rules_webtesting",
patch_args = ["-p1"],
patches = [
"//:tools/bazel-repo-patches/rules_webtesting__windows_runfiles_fix.patch",
],
sha256 = "e9abb7658b6a129740c0b3ef6f5a2370864e102a5ba5ffca2cea565829ed825a",
urls = ["https://github.com/bazelbuild/rules_webtesting/releases/download/0.3.5/rules_webtesting.tar.gz"],
)
http_archive(
name = "build_bazel_rules_nodejs",
patches = [
@@ -50,16 +65,16 @@ load("@rules_nodejs//nodejs:repositories.bzl", "nodejs_register_toolchains")
nodejs_register_toolchains(
name = "nodejs",
node_repositories = {
"18.20.0-darwin_arm64": ("node-v18.20.0-darwin-arm64.tar.gz", "node-v18.20.0-darwin-arm64", "10066ad4dd9e03ea5c4c45ef8775420ff37b860de09bbdf87b97e0c07b1ea036"),
"18.20.0-darwin_amd64": ("node-v18.20.0-darwin-x64.tar.gz", "node-v18.20.0-darwin-x64", "062ba71618e88e06321de5caa038843c350aababa2d315f3ca7b8551f8e66c1c"),
"18.20.0-linux_arm64": ("node-v18.20.0-linux-arm64.tar.xz", "node-v18.20.0-linux-arm64", "afe51da9ffb38ac1e3a20d9a06efd403ced4bf8831ab554a02a088dd8392fd79"),
"18.20.0-linux_ppc64le": ("node-v18.20.0-linux-ppc64le.tar.xz", "node-v18.20.0-linux-ppc64le", "9e652bbf53a3e37285b11dfb9d6a9bb8b02010c3b50e5c8229d4cc10e72681da"),
"18.20.0-linux_s390x": ("node-v18.20.0-linux-s390x.tar.xz", "node-v18.20.0-linux-s390x", "a6c2a5796b9d9e9bf21da62ec89ff30b41a8108880b9eaa3c912f1ce795a7743"),
"18.20.0-linux_amd64": ("node-v18.20.0-linux-x64.tar.xz", "node-v18.20.0-linux-x64", "03eea148e56785babb27930b05ed6bf311aaa3bc573c0399dd63cad2fe5713c7"),
"18.20.0-windows_amd64": ("node-v18.20.0-win-x64.zip", "node-v18.20.0-win-x64", "1c0aab05cc6836a8f5148cca345b92ebc948a4a2013f18d117b7ade6ff05aca6"),
"18.18.2-darwin_arm64": ("node-v18.18.2-darwin-arm64.tar.gz", "node-v18.18.2-darwin-arm64", "9f982cc91b28778dd8638e4f94563b0c2a1da7aba62beb72bd427721035ab553"),
"18.18.2-darwin_amd64": ("node-v18.18.2-darwin-x64.tar.gz", "node-v18.18.2-darwin-x64", "5bb8da908ed590e256a69bf2862238c8a67bc4600119f2f7721ca18a7c810c0f"),
"18.18.2-linux_arm64": ("node-v18.18.2-linux-arm64.tar.xz", "node-v18.18.2-linux-arm64", "2e630e18548627f61eaf573233da7949dc0a1df5eef3f486fa9820c5f6c121aa"),
"18.18.2-linux_ppc64le": ("node-v18.18.2-linux-ppc64le.tar.xz", "node-v18.18.2-linux-ppc64le", "b0adff5cf5938266b711d6c724fb134d802e0dee40b3a3f73d162de1b3d11880"),
"18.18.2-linux_s390x": ("node-v18.18.2-linux-s390x.tar.xz", "node-v18.18.2-linux-s390x", "c70ec2074b5e2b42c55bb4b8105418b67bf8a61c500d9376a07430dfcc341fdb"),
"18.18.2-linux_amd64": ("node-v18.18.2-linux-x64.tar.xz", "node-v18.18.2-linux-x64", "75aba25ae76999309fc6c598efe56ce53fbfc221381a44a840864276264ab8ac"),
"18.18.2-windows_amd64": ("node-v18.18.2-win-x64.zip", "node-v18.18.2-win-x64", "3bb0e51e579a41a22b3bf6cb2f3e79c03801aa17acbe0ca00fc555d1282e7acd"),
},
# We need at least Node 18.17 due to some transitive dependencies.
node_version = "18.20.0",
node_version = "18.18.2",
)
# Download npm dependencies.
@@ -75,7 +90,7 @@ yarn_install(
"//:.yarnrc",
"//:tools/npm-patches/@bazel+jasmine+5.8.1.patch",
"//tools:postinstall-patches.js",
"//tools/esm-interop:patches/npm/@angular+build-tooling+0.0.0-d30a56c19bafaac67cf44e605ed8c2c0e45b0a51.patch",
"//tools/esm-interop:patches/npm/@angular+build-tooling+0.0.0-e0ec7b60641d7f6369be45d8d02663fd50f320be.patch",
"//tools/esm-interop:patches/npm/@bazel+concatjs+5.8.1.patch",
"//tools/esm-interop:patches/npm/@bazel+esbuild+5.7.1.patch",
"//tools/esm-interop:patches/npm/@bazel+protractor+5.7.1.patch",
@@ -94,6 +109,54 @@ yarn_install(
yarn_lock = "//:yarn.lock",
)
yarn_install(
name = "aio_npm",
# Note that we add the postinstall scripts here so that the dependencies are re-installed
# when the postinstall patches are modified.
data = [
YARN_LABEL,
"//:.yarnrc",
"//:tools/npm-patches/@bazel+jasmine+5.8.1.patch",
"//aio:tools/cli-patches/bazel-architect-output.patch",
"//aio:tools/cli-patches/patch.js",
],
# Currently disabled due to:
# 1. Missing Windows support currently.
# 2. Incompatibilites with the `ts_library` rule.
exports_directories_only = False,
manual_build_file_contents = npm_package_archives(),
package_json = "//aio:package.json",
# We prefer to symlink the `node_modules` to only maintain a single install.
# See https://github.com/angular/dev-infra/pull/446#issuecomment-1059820287 for details.
symlink_node_modules = True,
yarn = YARN_LABEL,
yarn_lock = "//aio:yarn.lock",
)
yarn_install(
name = "aio_example_deps",
# Rename the default js_library target from "node_modules" as this obscures the
# the source directory stamped as a filegroup in the manual BUILD contents below.
all_node_modules_target_name = "node_modules_all",
data = [
YARN_LABEL,
"//:.yarnrc",
],
# Disabled because, when False, yarn_install preserves the node_modules folder
# with bin symlinks in the external repository. This is needed to link the shared
# set of deps for example e2es.
exports_directories_only = False,
manual_build_file_contents = """\
filegroup(
name = "node_modules_files",
srcs = ["node_modules"],
)
""",
package_json = "//aio/tools/examples/shared:package.json",
yarn = YARN_LABEL,
yarn_lock = "//aio/tools/examples/shared:yarn.lock",
)
load("@aspect_bazel_lib//lib:repositories.bzl", "aspect_bazel_lib_dependencies")
aspect_bazel_lib_dependencies()
@@ -143,10 +206,10 @@ cldr_xml_data_repository(
# sass rules
http_archive(
name = "io_bazel_rules_sass",
sha256 = "b00f13a280fc60ef2a1d142b31c629075bac841c980a0c3462190f338e5d584a",
strip_prefix = "rules_sass-6e23fb1351f392f7fe7a07038cd537770fe97742",
sha256 = "974f5e14c54d300ca63a67910ebfcea6d838d8811fefc61184f2872107152705",
strip_prefix = "rules_sass-888dd9c02709e228b60a8b3e51e2b16b2ca5279d",
urls = [
"https://github.com/bazelbuild/rules_sass/archive/6e23fb1351f392f7fe7a07038cd537770fe97742.zip",
"https://github.com/bazelbuild/rules_sass/archive/888dd9c02709e228b60a8b3e51e2b16b2ca5279d.zip",
],
)
@@ -181,15 +244,3 @@ http_archive(
strip_prefix = "sc-4.8.2-osx",
url = "https://saucelabs.com/downloads/sc-4.8.2-osx.zip",
)
yarn_install(
name = "npm_ts_versions",
data = [
YARN_LABEL,
"//:.yarnrc",
],
exports_directories_only = False,
package_json = "//packages/core/schematics/migrations/signal-migration/test/ts-versions:package.json",
yarn = YARN_LABEL,
yarn_lock = "//packages/core/schematics/migrations/signal-migration/test/ts-versions:yarn.lock",
)
+46 -59
View File
@@ -1,17 +1,11 @@
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
load("@npm//@angular/build-tooling/bazel/http-server:index.bzl", "http_server")
load("//:packages.bzl", "link_packages")
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
load("@build_bazel_rules_nodejs//:index.bzl", "copy_to_bin")
load("@npm//@angular-devkit/architect-cli:index.bzl", "architect", "architect_test")
load("@bazel_skylib//lib:collections.bzl", "collections")
load("//adev/tools/local_deps:index.bzl", "ensure_local_package_deps", "link_local_packages")
package(default_visibility = ["//visibility:public"])
exports_files([
"tsconfig.json",
])
# All source and configuration files required to build the docs app
APPLICATION_FILES = [
"angular.json",
@@ -22,30 +16,21 @@ APPLICATION_FILES = [
["src/**/*"],
exclude = [
"src/**/*.spec.ts",
# Temporarily exclude generated sources produced by the non-bazel
# build until the whole project is built by bazel and this directory
# isn't needed.
"src/generated/**/*",
],
)
TEST_FILES = APPLICATION_FILES + [
"karma.conf.js",
"test-main.ts",
"tsconfig.spec.json",
] + glob(
["src/**/*.spec.ts"],
["**/*.spec.ts"],
)
APPLICATION_ASSETS = [
"//adev/src/assets/images",
"//adev/src/assets/textures",
"//adev/src/assets/previews",
"//adev/src/assets:tutorials",
"//adev/src/assets/icons",
"//adev/src/assets:api",
"//adev/src/assets:content",
]
APPLICATION_DEPS = [
"@npm//@angular/docs",
"@npm//@angular/build",
APPLICATION_DEPS = link_packages([
"@npm//@angular-devkit/build-angular",
"@npm//@angular/animations",
"@npm//@angular/cdk",
@@ -53,6 +38,7 @@ APPLICATION_DEPS = [
"@npm//@angular/compiler",
"@npm//@angular/compiler-cli",
"@npm//@angular/core",
"@npm//@angular/docs",
"@npm//@angular/forms",
"@npm//@angular/material",
"@npm//@angular/platform-browser",
@@ -64,6 +50,13 @@ APPLICATION_DEPS = [
"@npm//ogl",
"@npm//rxjs",
"@npm//typescript",
"//adev/src/assets/images",
"//adev/src/assets/textures",
"//adev/src/assets/previews",
"//adev/src/assets:tutorials",
"//adev/src/assets/icons",
"//adev/src/assets:api",
"//adev/src/assets:content",
"@npm//@typescript/vfs",
"@npm//@codemirror/state",
"@npm//@codemirror/view",
@@ -80,33 +73,25 @@ APPLICATION_DEPS = [
"@npm//@lezer/highlight",
"@npm//@lezer/javascript",
"@npm//@lezer/common",
"@npm//@stackblitz/sdk",
"@npm//open-in-idx",
"@npm//@webcontainer/api",
"@npm//@xterm/xterm",
"@npm//@xterm/addon-fit",
"@npm//algoliasearch",
"@npm//xterm",
"@npm//xterm-addon-fit",
"@npm//angular-split",
"@npm//zone.js",
]
])
TEST_DEPS = APPLICATION_DEPS + [
TEST_DEPS = APPLICATION_DEPS + link_packages([
"@npm//@angular/platform-browser-dynamic",
"@npm//@angular/build-tooling/bazel/browsers/chromium",
"@npm//@types/jasmine",
"@npm//@types/node",
"@npm//assert",
"@npm//jasmine",
"@npm//jasmine-core",
"@npm//karma-chrome-launcher",
"@npm//karma-coverage",
"@npm//karma-jasmine",
"@npm//karma-jasmine-html-reporter",
]
# Create `npm_link` targets for all dependencies that correspond to a
# first-party Angular package that can be built from `HEAD`.
link_local_packages(
all_deps = collections.uniq(APPLICATION_DEPS + TEST_DEPS),
)
"//aio/tools:windows-chromium-path",
])
copy_to_bin(
name = "application_files_bin",
@@ -114,27 +99,27 @@ copy_to_bin(
)
bool_flag(
name = "prerender_adev",
name = "fast_build_mode",
build_setting_default = False,
)
config_setting(
name = "no_prerender",
name = "fast",
flag_values = {
":prerender_adev": "false",
":fast_build_mode": "true",
},
)
config_setting(
name = "prerender",
name = "full",
flag_values = {
":prerender_adev": "true",
":fast_build_mode": "false",
},
)
config_based_architect_flags = select({
":no_prerender": ["--no-prerender"],
":prerender": ["--prerender"],
":fast": ["--no-prerender"],
":full": ["--prerender"],
})
architect(
@@ -144,12 +129,9 @@ architect(
"--output-path=build",
] + config_based_architect_flags,
chdir = "$(RULEDIR)",
data = ensure_local_package_deps(APPLICATION_DEPS) + APPLICATION_ASSETS + [
data = APPLICATION_DEPS + [
":application_files_bin",
],
env = {
"NG_BUILD_OPTIMIZE_CHUNKS": "1",
},
# Network is required to inline fonts.
exec_properties = ENABLE_NETWORK,
output_dir = True,
@@ -158,13 +140,21 @@ architect(
],
)
http_server(
architect(
name = "serve",
additional_root_paths = [
"angular/adev/build/browser",
args = [
"angular-dev:serve",
"--poll=1000",
"--live-reload",
"--watch",
],
chdir = package_name(),
data = APPLICATION_DEPS + [
":application_files_bin",
],
tags = [
"no-remote-exec",
],
enable_dev_ui = True,
deps = [":build"],
)
architect_test(
@@ -174,10 +164,7 @@ architect_test(
"--no-watch",
],
chdir = package_name(),
data = ensure_local_package_deps(TEST_DEPS) + TEST_FILES + APPLICATION_ASSETS + [
"//adev/tools:windows-chromium-path",
"@npm//@angular/build-tooling/bazel/browsers/chromium",
],
data = TEST_DEPS + TEST_FILES,
env = {
"CHROME_BIN": "../$(CHROMIUM)",
},
-53
View File
@@ -1,53 +0,0 @@
# [Angular.dev](https://www.angular.dev)
This site is built with Angular.
The content is written primarily in Markdown format located in `src/content`. For simple edits, you can directly edit the file on GitHub and generate a Pull Request.
## Local Development
For local development, [yarn](https://yarnpkg.com/) is the preferred package manager. You can setup a local environment with the following commands
:
```bash
# Clone Angular repo
git clone https://github.com/angular/angular.git
# Navigate to project directory
cd angular
# Install dependencies
yarn
# Build and run local dev server
# Note: Initial build will take some time
yarn docs
```
If you are having issues with the docs building, see the [FAQs](#faqs) section.
## Contributing
Want to report a bug, contribute some code, or improve the documentation? Excellent!
Read through our [contributing guidelines](/CONTRIBUTING.md) to learn about our submission process, coding rules, and more.
And if you're new, check out one of our issues labeled as <kbd>[help wanted](https://github.com/angular/angular/labels/help%20wanted)</kbd> or <kbd>[good first issue](https://github.com/angular/angular/labels/good%20first%20issue)</kbd>.
### Code of Conduct
Help us keep Angular open and inclusive. Please read and follow our [Code of Conduct](CODE_OF_CONDUCT.md).
## FAQs
### The build is failing and I'm seeing `bazel:bazel failed: missing input file` messages.
This is most likely due to a bazel dependency / caching issue. To resolve this, run the following command:
```
# Try this first
yarn bazel clean
# If that doesn't work, try it with the expunge flag
yarn bazel clean --expunge
```
+13 -18
View File
@@ -17,24 +17,24 @@
"prefix": "adev",
"architect": {
"build": {
"builder": "@angular/build:application",
"builder": "@angular-devkit/build-angular:application",
"options": {
"externalDependencies": ["path"],
"define": {
// We need this until xterm 5.6.0 is released
// see https://github.com/xtermjs/xterm.js/pull/4940
"self": "this"
},
"externalDependencies": [
"path"
],
"outputPath": "dist/angular-dev",
"index": "src/index.html",
"browser": "src/main.ts",
"server": "src/main.server.ts",
"ssr": true,
"polyfills": [],
"polyfills": ["src/polyfills.ts", "zone.js"],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": ["src/favicon.ico", "src/robots.txt", "src/assets"],
"styles": ["@angular/docs/styles/global-styles.scss", "./src/local-styles.scss"],
"assets": [
"src/favicon.ico",
"src/robots.txt",
"src/assets"
],
"styles": ["@angular/docs/styles/global-styles.scss"],
"scripts": [],
"webWorkerTsConfig": "tsconfig.worker.json",
"optimization": {
@@ -84,10 +84,10 @@
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": ["zone.js", "zone.js/testing"],
"tsConfig": "tsconfig.spec.json",
"include": ["src/app"],
"karmaConfig": "karma.conf.js",
"main": "test-main.ts",
"inlineStyleLanguage": "scss",
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["@angular/docs/styles/global-styles.scss"],
@@ -99,11 +99,6 @@
}
},
"cli": {
"analytics": "dca119a9-da31-47f7-a6cb-b60541037021",
"cache": {
// Disable build caching as the cache folder will just be dropped
// when run under Bazel sandboxed execution.
"enabled": false
}
"analytics": "dca119a9-da31-47f7-a6cb-b60541037021"
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
"ignore": ["**/.*"],
"headers": [
{
"source": "assets/images/tutorials/common/*.jpg",
"source": "assets/tutorials/common/*.jpg",
"headers": [
{
"key": "Cross-Origin-Resource-Policy",
+1 -1
View File
@@ -1,7 +1,7 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
const {getAdjustedChromeBinPathForWindows} = require('../adev/tools/windows-chromium-path');
const {getAdjustedChromeBinPathForWindows} = require('../aio/tools/windows-chromium-path');
process.env.CHROME_BIN = getAdjustedChromeBinPathForWindows();
-3
View File
@@ -1,3 +0,0 @@
# Generating data for `angular.dev/cli`
This script updates the Angular CLI help JSON files stored in `adev/src/content/cli/help`. This files are used to generate the [angular.dev CLI](https://angular.dev/cli) pages.
-154
View File
@@ -1,154 +0,0 @@
import {execSync} from 'node:child_process';
import {readFile, writeFile, readdir, mkdtemp, realpath, copyFile, unlink} from 'node:fs/promises';
import {tmpdir} from 'node:os';
import {get} from 'node:https';
import {dirname, resolve as resolvePath, posix, join} from 'node:path';
import {fileURLToPath} from 'node:url';
import {existsSync, constants as fsConstants} from 'node:fs';
const GITHUB_API = 'https://api.github.com/repos/';
const CLI_BUILDS_REPO = 'angular/cli-builds';
const GITHUB_API_CLI_BUILDS = posix.join(GITHUB_API, CLI_BUILDS_REPO);
const scriptDir = dirname(fileURLToPath(import.meta.url));
const CLI_HELP_CONTENT_PATH = resolvePath(scriptDir, '../../src/content/cli/help');
const CLI_SHA_PATH = join(CLI_HELP_CONTENT_PATH, 'build-info.json');
async function main() {
if (!existsSync(CLI_SHA_PATH)) {
throw new Error(`${CLI_SHA_PATH} does not exist.`);
}
const branch = process.env.GITHUB_REF;
const {sha: currentSha} = JSON.parse(await readFile(CLI_SHA_PATH, 'utf-8'));
const latestSha = await getShaFromCliBuilds(branch);
console.log(`Comparing ${currentSha}...${latestSha}.`);
const affectedFiles = await getAffectedFiles(currentSha, latestSha);
const changedHelpFiles = affectedFiles.filter((file) => file.startsWith('help/'));
if (changedHelpFiles.length === 0) {
console.log(`No 'help/**' files changed between ${currentSha} and ${latestSha}.`);
return;
}
console.log(
`The below help files changed between ${currentSha} and ${latestSha}:\n` +
changedHelpFiles.map((f) => '* ' + f).join('\n'),
);
const temporaryDir = await realpath(await mkdtemp(join(tmpdir(), 'cli-src-')));
const execOptions = {cwd: temporaryDir, stdio: 'inherit'};
execSync('git init', execOptions);
execSync('git remote add origin https://github.com/angular/cli-builds.git', execOptions);
// fetch a commit
execSync(`git fetch origin ${latestSha}`, execOptions);
// reset this repository's main branch to the commit of interest
execSync('git reset --hard FETCH_HEAD', execOptions);
// get sha when files where changed
const shaWhenFilesChanged = execSync(`git rev-list -1 ${latestSha} "help/"`, {
encoding: 'utf8',
cwd: temporaryDir,
stdio: ['ignore', 'pipe', 'ignore'],
}).trim();
// Delete existing JSON help files.
const helpFilesUnlink = (await readdir(CLI_HELP_CONTENT_PATH))
.filter((f) => f.endsWith('.json'))
.map((f) => unlink(join(CLI_HELP_CONTENT_PATH, f)));
await Promise.allSettled(helpFilesUnlink);
// Copy new help files
const tempHelpDir = join(temporaryDir, 'help');
const helpFilesCopy = (await readdir(tempHelpDir)).map((f) => {
const src = join(tempHelpDir, f);
const dest = join(CLI_HELP_CONTENT_PATH, f);
return copyFile(src, dest, fsConstants.COPYFILE_FICLONE);
});
await Promise.allSettled(helpFilesCopy);
// Write SHA to file.
await writeFile(
CLI_SHA_PATH,
JSON.stringify(
{
branchName: branch,
sha: shaWhenFilesChanged,
},
undefined,
2,
),
);
console.log('\nChanges: ');
execSync(`git status --porcelain`, {stdio: 'inherit'});
console.log(`Successfully updated help files in '${CLI_HELP_CONTENT_PATH}'.\n`);
}
/**
* Get SHA of a branch.
*
* @param {string} branch
* @param {string} headSha
* @returns Promise<string>
*/
async function getShaFromCliBuilds(branch) {
const sha = await httpGet(`${GITHUB_API_CLI_BUILDS}/commits/${branch}`, {
headers: {Accept: 'application/vnd.github.VERSION.sha'},
});
if (!sha) {
throw new Error(`Unable to extract the SHA for '${branch}'.`);
}
return sha;
}
/**
* Get the affected files.
*
* @param {string} baseSha
* @param {string} headSha
* @returns Promise<string[]>
*/
async function getAffectedFiles(baseSha, headSha) {
const {files} = JSON.parse(
await httpGet(`${GITHUB_API_CLI_BUILDS}/compare/${baseSha}...${headSha}`),
);
return files.map((f) => f.filename);
}
function httpGet(url, options = {}) {
options.headers ??= {};
options.headers[
'Authorization'
] = `token ${process.env.ANGULAR_CLI_BUILDS_READONLY_GITHUB_TOKEN}`;
// User agent is required
// https://docs.github.com/en/rest/overview/resources-in-the-rest-api?apiVersion=2022-11-28#user-agent-required
options.headers['User-Agent'] = `ADEV_Angular_CLI_Sources_Update`;
return new Promise((resolve, reject) => {
get(url, options, (res) => {
let data = '';
res
.on('data', (chunk) => {
data += chunk;
})
.on('end', () => {
resolve(data);
});
}).on('error', (e) => {
reject(e);
});
});
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
-49
View File
@@ -1,49 +0,0 @@
load("//tools:defaults.bzl", "ng_module", "ng_package")
package(default_visibility = ["//visibility:private"])
ng_module(
name = "docs",
srcs = [
"index.ts",
],
module_name = "@angular/docs",
deps = [
"//adev/shared-docs/components",
"//adev/shared-docs/constants",
"//adev/shared-docs/directives",
"//adev/shared-docs/interfaces",
"//adev/shared-docs/pipes",
"//adev/shared-docs/providers",
"//adev/shared-docs/services",
"//adev/shared-docs/testing",
"//adev/shared-docs/utils",
],
)
ng_package(
name = "npm_package",
srcs = [
"package.json",
"//adev/shared-docs/icons",
"//adev/shared-docs/pipeline:BUILD.bazel",
"//adev/shared-docs/pipeline:_guides.bzl",
"//adev/shared-docs/pipeline:_playground.bzl",
"//adev/shared-docs/pipeline:_stackblitz.bzl",
"//adev/shared-docs/pipeline:_tutorial.bzl",
"//adev/shared-docs/pipeline:guides.mjs",
"//adev/shared-docs/pipeline:guides-no-mermaid.mjs",
"//adev/shared-docs/pipeline:playground.mjs",
"//adev/shared-docs/pipeline:stackblitz.mjs",
"//adev/shared-docs/pipeline:tutorial.mjs",
"//adev/shared-docs/pipeline/examples/template:files",
"//adev/shared-docs/pipeline/tutorials/common:files",
"//adev/shared-docs/styles",
],
visibility = [
"//adev:__pkg__",
],
deps = [
":docs",
],
)
-28
View File
@@ -1,28 +0,0 @@
load("//tools:defaults.bzl", "ts_library")
package(default_visibility = ["//visibility:private"])
ts_library(
name = "components",
srcs = [
"index.ts",
],
visibility = [
"//adev/shared-docs:__pkg__",
],
deps = [
"//adev/shared-docs/components/algolia-icon",
"//adev/shared-docs/components/breadcrumb",
"//adev/shared-docs/components/cookie-popup",
"//adev/shared-docs/components/copy-source-code-button",
"//adev/shared-docs/components/icon",
"//adev/shared-docs/components/navigation-list",
"//adev/shared-docs/components/search-dialog",
"//adev/shared-docs/components/select",
"//adev/shared-docs/components/slide-toggle",
"//adev/shared-docs/components/table-of-contents",
"//adev/shared-docs/components/text-field",
"//adev/shared-docs/components/top-level-banner",
"//adev/shared-docs/components/viewers",
],
)
@@ -1,20 +0,0 @@
load("//tools:defaults.bzl", "ng_module")
package(default_visibility = ["//visibility:private"])
ng_module(
name = "algolia-icon",
srcs = [
"algolia-icon.component.ts",
],
assets = [
"algolia-icon.component.html",
],
visibility = [
"//adev/shared-docs/components:__pkg__",
"//adev/shared-docs/components/search-dialog:__pkg__",
],
deps = [
"//packages/core",
],
)
@@ -1,52 +0,0 @@
<!-- Algolia logo -->
<svg
id="Layer_1"
class="docs-algolia-logo"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 2196.2 500"
>
<defs>
<style>
.cls-1,
.cls-2 {
fill: #003dff;
}
.cls-2 {
fill-rule: evenodd;
}
</style>
</defs>
<path
class="cls-2"
d="M1070.38,275.3V5.91c0-3.63-3.24-6.39-6.82-5.83l-50.46,7.94c-2.87,.45-4.99,2.93-4.99,5.84l.17,273.22c0,12.92,0,92.7,95.97,95.49,3.33,.1,6.09-2.58,6.09-5.91v-40.78c0-2.96-2.19-5.51-5.12-5.84-34.85-4.01-34.85-47.57-34.85-54.72Z"
/>
<rect class="cls-1" x="1845.88" y="104.73" width="62.58" height="277.9" rx="5.9" ry="5.9" />
<path
class="cls-2"
d="M1851.78,71.38h50.77c3.26,0,5.9-2.64,5.9-5.9V5.9c0-3.62-3.24-6.39-6.82-5.83l-50.77,7.95c-2.87,.45-4.99,2.92-4.99,5.83v51.62c0,3.26,2.64,5.9,5.9,5.9Z"
/>
<path
class="cls-2"
d="M1764.03,275.3V5.91c0-3.63-3.24-6.39-6.82-5.83l-50.46,7.94c-2.87,.45-4.99,2.93-4.99,5.84l.17,273.22c0,12.92,0,92.7,95.97,95.49,3.33,.1,6.09-2.58,6.09-5.91v-40.78c0-2.96-2.19-5.51-5.12-5.84-34.85-4.01-34.85-47.57-34.85-54.72Z"
/>
<path
class="cls-2"
d="M1631.95,142.72c-11.14-12.25-24.83-21.65-40.78-28.31-15.92-6.53-33.26-9.85-52.07-9.85-18.78,0-36.15,3.17-51.92,9.85-15.59,6.66-29.29,16.05-40.76,28.31-11.47,12.23-20.38,26.87-26.76,44.03-6.38,17.17-9.24,37.37-9.24,58.36,0,20.99,3.19,36.87,9.55,54.21,6.38,17.32,15.14,32.11,26.45,44.36,11.29,12.23,24.83,21.62,40.6,28.46,15.77,6.83,40.12,10.33,52.4,10.48,12.25,0,36.78-3.82,52.7-10.48,15.92-6.68,29.46-16.23,40.78-28.46,11.29-12.25,20.05-27.04,26.25-44.36,6.22-17.34,9.24-33.22,9.24-54.21,0-20.99-3.34-41.19-10.03-58.36-6.38-17.17-15.14-31.8-26.43-44.03Zm-44.43,163.75c-11.47,15.75-27.56,23.7-48.09,23.7-20.55,0-36.63-7.8-48.1-23.7-11.47-15.75-17.21-34.01-17.21-61.2,0-26.89,5.59-49.14,17.06-64.87,11.45-15.75,27.54-23.52,48.07-23.52,20.55,0,36.63,7.78,48.09,23.52,11.47,15.57,17.36,37.98,17.36,64.87,0,27.19-5.72,45.3-17.19,61.2Z"
/>
<path
class="cls-2"
d="M894.42,104.73h-49.33c-48.36,0-90.91,25.48-115.75,64.1-14.52,22.58-22.99,49.63-22.99,78.73,0,44.89,20.13,84.92,51.59,111.1,2.93,2.6,6.05,4.98,9.31,7.14,12.86,8.49,28.11,13.47,44.52,13.47,1.23,0,2.46-.03,3.68-.09,.36-.02,.71-.05,1.07-.07,.87-.05,1.75-.11,2.62-.2,.34-.03,.68-.08,1.02-.12,.91-.1,1.82-.21,2.73-.34,.21-.03,.42-.07,.63-.1,32.89-5.07,61.56-30.82,70.9-62.81v57.83c0,3.26,2.64,5.9,5.9,5.9h50.42c3.26,0,5.9-2.64,5.9-5.9V110.63c0-3.26-2.64-5.9-5.9-5.9h-56.32Zm0,206.92c-12.2,10.16-27.97,13.98-44.84,15.12-.16,.01-.33,.03-.49,.04-1.12,.07-2.24,.1-3.36,.1-42.24,0-77.12-35.89-77.12-79.37,0-10.25,1.96-20.01,5.42-28.98,11.22-29.12,38.77-49.74,71.06-49.74h49.33v142.83Z"
/>
<path
class="cls-2"
d="M2133.97,104.73h-49.33c-48.36,0-90.91,25.48-115.75,64.1-14.52,22.58-22.99,49.63-22.99,78.73,0,44.89,20.13,84.92,51.59,111.1,2.93,2.6,6.05,4.98,9.31,7.14,12.86,8.49,28.11,13.47,44.52,13.47,1.23,0,2.46-.03,3.68-.09,.36-.02,.71-.05,1.07-.07,.87-.05,1.75-.11,2.62-.2,.34-.03,.68-.08,1.02-.12,.91-.1,1.82-.21,2.73-.34,.21-.03,.42-.07,.63-.1,32.89-5.07,61.56-30.82,70.9-62.81v57.83c0,3.26,2.64,5.9,5.9,5.9h50.42c3.26,0,5.9-2.64,5.9-5.9V110.63c0-3.26-2.64-5.9-5.9-5.9h-56.32Zm0,206.92c-12.2,10.16-27.97,13.98-44.84,15.12-.16,.01-.33,.03-.49,.04-1.12,.07-2.24,.1-3.36,.1-42.24,0-77.12-35.89-77.12-79.37,0-10.25,1.96-20.01,5.42-28.98,11.22-29.12,38.77-49.74,71.06-49.74h49.33v142.83Z"
/>
<path
class="cls-2"
d="M1314.05,104.73h-49.33c-48.36,0-90.91,25.48-115.75,64.1-11.79,18.34-19.6,39.64-22.11,62.59-.58,5.3-.88,10.68-.88,16.14s.31,11.15,.93,16.59c4.28,38.09,23.14,71.61,50.66,94.52,2.93,2.6,6.05,4.98,9.31,7.14,12.86,8.49,28.11,13.47,44.52,13.47h0c17.99,0,34.61-5.93,48.16-15.97,16.29-11.58,28.88-28.54,34.48-47.75v50.26h-.11v11.08c0,21.84-5.71,38.27-17.34,49.36-11.61,11.08-31.04,16.63-58.25,16.63-11.12,0-28.79-.59-46.6-2.41-2.83-.29-5.46,1.5-6.27,4.22l-12.78,43.11c-1.02,3.46,1.27,7.02,4.83,7.53,21.52,3.08,42.52,4.68,54.65,4.68,48.91,0,85.16-10.75,108.89-32.21,21.48-19.41,33.15-48.89,35.2-88.52V110.63c0-3.26-2.64-5.9-5.9-5.9h-56.32Zm0,64.1s.65,139.13,0,143.36c-12.08,9.77-27.11,13.59-43.49,14.7-.16,.01-.33,.03-.49,.04-1.12,.07-2.24,.1-3.36,.1-1.32,0-2.63-.03-3.94-.1-40.41-2.11-74.52-37.26-74.52-79.38,0-10.25,1.96-20.01,5.42-28.98,11.22-29.12,38.77-49.74,71.06-49.74h49.33Z"
/>
<path
class="cls-1"
d="M249.83,0C113.3,0,2,110.09,.03,246.16c-2,138.19,110.12,252.7,248.33,253.5,42.68,.25,83.79-10.19,120.3-30.03,3.56-1.93,4.11-6.83,1.08-9.51l-23.38-20.72c-4.75-4.21-11.51-5.4-17.36-2.92-25.48,10.84-53.17,16.38-81.71,16.03-111.68-1.37-201.91-94.29-200.13-205.96,1.76-110.26,92-199.41,202.67-199.41h202.69V407.41l-115-102.18c-3.72-3.31-9.42-2.66-12.42,1.31-18.46,24.44-48.53,39.64-81.93,37.34-46.33-3.2-83.87-40.5-87.34-86.81-4.15-55.24,39.63-101.52,94-101.52,49.18,0,89.68,37.85,93.91,85.95,.38,4.28,2.31,8.27,5.52,11.12l29.95,26.55c3.4,3.01,8.79,1.17,9.63-3.3,2.16-11.55,2.92-23.58,2.07-35.92-4.82-70.34-61.8-126.93-132.17-131.26-80.68-4.97-148.13,58.14-150.27,137.25-2.09,77.1,61.08,143.56,138.19,145.26,32.19,.71,62.03-9.41,86.14-26.95l150.26,133.2c6.44,5.71,16.61,1.14,16.61-7.47V9.48C499.66,4.25,495.42,0,490.18,0H249.83Z"
/>
</svg>

Before

Width:  |  Height:  |  Size: 5.0 KiB

@@ -1,18 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {ChangeDetectionStrategy, Component} from '@angular/core';
@Component({
selector: 'docs-algolia-icon',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [],
templateUrl: './algolia-icon.component.html',
})
export class AlgoliaIcon {}
@@ -1,54 +0,0 @@
load("//tools:defaults.bzl", "karma_web_test_suite", "ng_module", "ts_library")
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")
package(default_visibility = ["//visibility:private"])
ng_module(
name = "breadcrumb",
srcs = [
"breadcrumb.component.ts",
],
assets = [
":breadcrumb.component.css",
"breadcrumb.component.html",
],
visibility = [
"//adev/shared-docs/components:__pkg__",
"//adev/shared-docs/components/viewers:__pkg__",
],
deps = [
"//adev/shared-docs/interfaces",
"//adev/shared-docs/services",
"//packages/common",
"//packages/core",
"//packages/router",
],
)
sass_binary(
name = "style",
src = "breadcrumb.component.scss",
)
ts_library(
name = "test_lib",
testonly = True,
srcs = glob(
["*.spec.ts"],
),
deps = [
":breadcrumb",
"//adev/shared-docs/interfaces",
"//adev/shared-docs/services",
"//packages/core",
"//packages/core/testing",
"//packages/platform-browser",
"//packages/router",
"//packages/router/testing",
],
)
karma_web_test_suite(
name = "test",
deps = [":test_lib"],
)
@@ -1,13 +0,0 @@
@for (breadcrumb of breadcrumbItems(); track breadcrumb) {
<div class="docs-breadcrumb">
@if (breadcrumb.path) {
@if (breadcrumb.isExternal) {
<a [href]="breadcrumb.path">{{ breadcrumb.label }}</a>
} @else {
<a [routerLink]="'/' + breadcrumb.path">{{ breadcrumb.label }}</a>
}
} @else {
<span>{{ breadcrumb.label }}</span>
}
</div>
}
@@ -1,25 +0,0 @@
:host {
display: flex;
align-items: center;
padding-block-end: 1.5rem;
}
.docs-breadcrumb {
span {
color: var(--quaternary-contrast);
font-size: 0.875rem;
display: flex;
align-items: center;
}
&:not(:last-child) {
span {
&::after {
content: 'chevron_right';
font-family: var(--icons);
margin-inline: 0.5rem;
color: var(--quinary-contrast);
}
}
}
}
@@ -1,83 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {Breadcrumb} from './breadcrumb.component';
import {NavigationState} from '../../services';
import {NavigationItem} from '../../interfaces';
import {By} from '@angular/platform-browser';
import {RouterTestingModule} from '@angular/router/testing';
import {provideExperimentalZonelessChangeDetection} from '@angular/core';
describe('Breadcrumb', () => {
let fixture: ComponentFixture<Breadcrumb>;
let navigationStateSpy: jasmine.SpyObj<NavigationState>;
beforeEach(() => {
navigationStateSpy = jasmine.createSpyObj('NavigationState', ['activeNavigationItem']);
TestBed.configureTestingModule({
imports: [Breadcrumb, RouterTestingModule],
providers: [
provideExperimentalZonelessChangeDetection(),
{
provide: NavigationState,
useValue: navigationStateSpy,
},
],
});
fixture = TestBed.createComponent(Breadcrumb);
});
it('should display proper breadcrumb structure based on navigation state', () => {
navigationStateSpy.activeNavigationItem.and.returnValue(item);
fixture.detectChanges();
const breadcrumbs = fixture.debugElement.queryAll(By.css('.docs-breadcrumb span'));
expect(breadcrumbs.length).toBe(2);
expect(breadcrumbs[0].nativeElement.innerText).toEqual('Grandparent');
expect(breadcrumbs[1].nativeElement.innerText).toEqual('Parent');
});
it('should display breadcrumb links when navigation item has got path', () => {
navigationStateSpy.activeNavigationItem.and.returnValue(exampleItemWithPath);
fixture.detectChanges();
const breadcrumbs = fixture.debugElement.queryAll(By.css('.docs-breadcrumb a'));
expect(breadcrumbs.length).toBe(1);
expect(breadcrumbs[0].nativeElement.innerText).toEqual('Parent');
expect(breadcrumbs[0].nativeElement.href).toEqual(`${window.origin}/example`);
});
});
const grandparent: NavigationItem = {
label: 'Grandparent',
};
const parent: NavigationItem = {
label: 'Parent',
parent: grandparent,
};
const item: NavigationItem = {
label: 'Active Item',
parent: parent,
};
const parentWithPath: NavigationItem = {
label: 'Parent',
path: '/example',
};
const exampleItemWithPath: NavigationItem = {
label: 'Active Item',
parent: parentWithPath,
};
@@ -1,49 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {ChangeDetectionStrategy, Component, OnInit, inject, signal} from '@angular/core';
import {NavigationState} from '../../services/index';
import {NavigationItem} from '../../interfaces/index';
import {RouterLink} from '@angular/router';
@Component({
selector: 'docs-breadcrumb',
standalone: true,
imports: [RouterLink],
templateUrl: './breadcrumb.component.html',
styleUrls: ['./breadcrumb.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class Breadcrumb implements OnInit {
private readonly navigationState = inject(NavigationState);
breadcrumbItems = signal<NavigationItem[]>([]);
ngOnInit(): void {
this.setBreadcrumbItemsBasedOnNavigationStructure();
}
private setBreadcrumbItemsBasedOnNavigationStructure(): void {
let breadcrumbs: NavigationItem[] = [];
const traverse = (node: NavigationItem | null) => {
if (!node) {
return;
}
if (node.parent) {
breadcrumbs = [node.parent, ...breadcrumbs];
traverse(node.parent);
}
};
traverse(this.navigationState.activeNavigationItem());
this.breadcrumbItems.set(breadcrumbs);
}
}
@@ -1,49 +0,0 @@
load("//tools:defaults.bzl", "karma_web_test_suite", "ng_module", "ts_library")
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")
package(default_visibility = ["//visibility:private"])
ng_module(
name = "cookie-popup",
srcs = [
"cookie-popup.component.ts",
],
assets = [
":cookie-popup.component.css",
"cookie-popup.component.html",
],
visibility = [
"//adev/shared-docs/components:__pkg__",
],
deps = [
"//adev/shared-docs/providers",
"//adev/shared-docs/utils",
"//packages/common",
"//packages/core",
],
)
sass_binary(
name = "style",
src = "cookie-popup.component.scss",
)
ts_library(
name = "test_lib",
testonly = True,
srcs = glob(
["*.spec.ts"],
),
deps = [
":cookie-popup",
"//adev/shared-docs/providers",
"//adev/shared-docs/testing",
"//packages/core",
"//packages/core/testing",
],
)
karma_web_test_suite(
name = "test",
deps = [":test_lib"],
)
@@ -1,22 +0,0 @@
@if (!hasAccepted()) {
<div class="docs-cookies-popup docs-invert-mode">
<p>This site uses cookies from Google to deliver its services and to analyze traffic.</p>
<div>
<a href="https://policies.google.com/technologies/cookies" target="_blank" rel="noopener">
<button class="docs-primary-btn" [attr.text]="'Learn more'" aria-label="Learn More">
Learn more
</button>
</a>
<button
type="button"
(click)="accept()"
class="docs-primary-btn"
[attr.text]="'Ok, Got it'"
aria-label="Ok, Got it"
>
Ok, Got it
</button>
</div>
</div>
}
@@ -1,40 +0,0 @@
:host {
position: fixed;
bottom: 0.5rem;
right: 0.5rem;
z-index: var(--z-index-cookie-consent);
opacity: 0;
visibility: hidden;
animation: 1s linear forwards 0.5s fadeIn;
}
.docs-cookies-popup {
padding: 1rem;
background-color: var(--page-background);
border: 1px solid var(--senary-contrast);
border-radius: 0.25rem;
font-size: 0.875rem;
max-width: 265px;
transition: background-color 0.3s ease, border-color 0.3s ease, color 0.3s ease;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
> div {
display: flex;
gap: 0.5rem;
align-items: center;
width: 100%;
margin-block-start: 1rem;
}
p {
margin-block: 0;
color: var(--primary-contrast);
}
}
@keyframes fadeIn {
100% {
opacity: 100%;
visibility: visible;
}
}
@@ -1,82 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {CookiePopup, STORAGE_KEY} from './cookie-popup.component';
import {LOCAL_STORAGE} from '../../providers/index';
import {MockLocalStorage} from '../../testing/index';
import {provideExperimentalZonelessChangeDetection} from '@angular/core';
describe('CookiePopup', () => {
let fixture: ComponentFixture<CookiePopup>;
let mockLocalStorage = new MockLocalStorage();
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CookiePopup],
providers: [
provideExperimentalZonelessChangeDetection(),
{
provide: LOCAL_STORAGE,
useValue: mockLocalStorage,
},
],
});
});
it('should make the popup visible by default', () => {
initComponent(false);
expect(getCookiesPopup()).not.toBeNull();
});
it('should hide the cookies popup if the user has already accepted cookies', () => {
initComponent(true);
expect(getCookiesPopup()).toBeNull();
});
it('should hide the cookies popup', () => {
initComponent(false);
accept();
fixture.detectChanges();
expect(getCookiesPopup()).toBeNull();
});
it('should store the user confirmation', () => {
initComponent(false);
expect(mockLocalStorage.getItem(STORAGE_KEY)).toBeNull();
accept();
expect(mockLocalStorage.getItem(STORAGE_KEY)).toBe('true');
});
// Helpers
function getCookiesPopup() {
return (fixture.nativeElement as HTMLElement).querySelector('.docs-cookies-popup');
}
function accept() {
(fixture.nativeElement as HTMLElement)
.querySelector<HTMLButtonElement>('button[text="Ok, Got it"]')
?.click();
}
function initComponent(cookiesAccepted: boolean) {
mockLocalStorage.setItem(STORAGE_KEY, cookiesAccepted ? 'true' : null);
fixture = TestBed.createComponent(CookiePopup);
fixture.detectChanges();
}
});
@@ -1,54 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {ChangeDetectionStrategy, Component, inject, signal} from '@angular/core';
import {LOCAL_STORAGE} from '../../providers/index';
import {setCookieConsent} from '../../utils';
/**
* Decelare gtag as part of the window in this file as gtag is expected to already be loaded.
*/
declare const window: Window & typeof globalThis & {gtag?: Function};
export const STORAGE_KEY = 'docs-accepts-cookies';
@Component({
selector: 'docs-cookie-popup',
standalone: true,
templateUrl: './cookie-popup.component.html',
styleUrls: ['./cookie-popup.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CookiePopup {
private readonly localStorage = inject(LOCAL_STORAGE);
/** Whether the user has accepted the cookie disclaimer. */
hasAccepted = signal<boolean>(false);
constructor() {
// Needs to be in a try/catch, because some browsers will
// throw when using `localStorage` in private mode.
try {
this.hasAccepted.set(this.localStorage?.getItem(STORAGE_KEY) === 'true');
} catch {
this.hasAccepted.set(false);
}
}
/** Accepts the cookie disclaimer. */
protected accept(): void {
try {
this.localStorage?.setItem(STORAGE_KEY, 'true');
} catch {}
this.hasAccepted.set(true);
// Enable Google Analytics consent properties
setCookieConsent('granted');
}
}
@@ -1,43 +0,0 @@
load("//tools:defaults.bzl", "karma_web_test_suite", "ng_module", "ts_library")
package(default_visibility = ["//visibility:private"])
ng_module(
name = "copy-source-code-button",
srcs = [
"copy-source-code-button.component.ts",
],
assets = [
"copy-source-code-button.component.html",
],
visibility = [
"//adev/shared-docs/components:__pkg__",
"//adev/shared-docs/components/viewers:__pkg__",
],
deps = [
"//adev/shared-docs/components/icon",
"//packages/common",
"//packages/core",
"@npm//@angular/cdk",
],
)
ts_library(
name = "test_lib",
testonly = True,
srcs = glob(
["*.spec.ts"],
),
deps = [
":copy-source-code-button",
"//packages/core",
"//packages/core/testing",
"//packages/platform-browser",
"@npm//@angular/cdk",
],
)
karma_web_test_suite(
name = "test",
deps = [":test_lib"],
)
@@ -1,18 +0,0 @@
<i>
<svg
aria-hidden="true"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
class="docs-copy"
>
<path
d="M5 22C4.45 22 3.97917 21.8042 3.5875 21.4125C3.19583 21.0208 3 20.55 3 20V6H5V20H16V22H5ZM9 18C8.45 18 7.97917 17.8042 7.5875 17.4125C7.19583 17.0208 7 16.55 7 16V4C7 3.45 7.19583 2.97917 7.5875 2.5875C7.97917 2.19583 8.45 2 9 2H18C18.55 2 19.0208 2.19583 19.4125 2.5875C19.8042 2.97917 20 3.45 20 4V16C20 16.55 19.8042 17.0208 19.4125 17.4125C19.0208 17.8042 18.55 18 18 18H9ZM9 16H18V4H9V16Z"
fill="#A39FA9"
/>
</svg>
</i>
<docs-icon class="docs-check">check</docs-icon>
@@ -1,127 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing';
import {
CONFIRMATION_DISPLAY_TIME_MS,
CopySourceCodeButton,
} from './copy-source-code-button.component';
import {
ChangeDetectionStrategy,
Component,
provideExperimentalZonelessChangeDetection,
signal,
} from '@angular/core';
import {By} from '@angular/platform-browser';
import {Clipboard} from '@angular/cdk/clipboard';
const SUCCESSFULLY_COPY_CLASS_NAME = 'docs-copy-source-code-button-success';
const FAILED_COPY_CLASS_NAME = 'docs-copy-source-code-button-failed';
describe('CopySourceCodeButton', () => {
let component: CodeSnippetWrapper;
let fixture: ComponentFixture<CodeSnippetWrapper>;
let copySpy: jasmine.Spy<(text: string) => boolean>;
beforeEach(async () => {
TestBed.configureTestingModule({
imports: [CodeSnippetWrapper],
providers: [provideExperimentalZonelessChangeDetection()],
});
fixture = TestBed.createComponent(CodeSnippetWrapper);
component = fixture.componentInstance;
await fixture.whenStable();
});
beforeEach(() => {
const clipboardService = TestBed.inject(Clipboard);
copySpy = spyOn(clipboardService, 'copy');
});
it('should call clipboard service when clicked on copy source code', async () => {
const expectedCodeToBeCopied = 'npm install -g @angular/cli';
component.code.set(expectedCodeToBeCopied);
await fixture.whenStable();
const button = fixture.debugElement.query(By.directive(CopySourceCodeButton)).nativeElement;
button.click();
expect(copySpy.calls.argsFor(0)[0].trim()).toBe(expectedCodeToBeCopied);
});
it('should not copy lines marked as deleted when code snippet contains diff', async () => {
const codeInHtmlFormat = `
<code>
<div class="line remove"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> *<span class="hljs-attr">ngFor</span>=<span class="hljs-string">"let product of products"</span>&gt;</span></div>
<div class="line add"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> *<span class="hljs-attr">ngFor</span>=<span class="hljs-string">"let product of products()"</span>&gt;</span></div>
</code>
`;
const expectedCodeToBeCopied = `<div *ngFor="let product of products()">`;
component.code.set(codeInHtmlFormat);
await fixture.whenStable();
const button = fixture.debugElement.query(By.directive(CopySourceCodeButton)).nativeElement;
button.click();
expect(copySpy.calls.argsFor(0)[0].trim()).toBe(expectedCodeToBeCopied);
});
it(`should set ${SUCCESSFULLY_COPY_CLASS_NAME} for ${CONFIRMATION_DISPLAY_TIME_MS} ms when copy was executed properly`, fakeAsync(() => {
component.code.set('example');
fixture.detectChanges();
const button = fixture.debugElement.query(By.directive(CopySourceCodeButton)).nativeElement;
button.click();
fixture.detectChanges();
expect(button).toHaveClass(SUCCESSFULLY_COPY_CLASS_NAME);
tick(CONFIRMATION_DISPLAY_TIME_MS);
fixture.detectChanges();
expect(button).not.toHaveClass(SUCCESSFULLY_COPY_CLASS_NAME);
}));
it(`should set ${FAILED_COPY_CLASS_NAME} for ${CONFIRMATION_DISPLAY_TIME_MS} ms when copy failed`, fakeAsync(() => {
component.code.set('example');
copySpy.and.throwError('Fake copy error');
fixture.detectChanges();
const button = fixture.debugElement.query(By.directive(CopySourceCodeButton)).nativeElement;
button.click();
fixture.detectChanges();
expect(button).toHaveClass(FAILED_COPY_CLASS_NAME);
tick(CONFIRMATION_DISPLAY_TIME_MS);
fixture.detectChanges();
expect(button).not.toHaveClass(FAILED_COPY_CLASS_NAME);
}));
});
@Component({
template: `
<pre>
<code [innerHtml]="code()"></code>
</pre>
<button docs-copy-source-code></button>
`,
imports: [CopySourceCodeButton],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
})
class CodeSnippetWrapper {
code = signal('');
}
@@ -1,89 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
WritableSignal,
inject,
signal,
} from '@angular/core';
import {CommonModule} from '@angular/common';
import {Clipboard} from '@angular/cdk/clipboard';
import {IconComponent} from '../icon/icon.component';
export const REMOVED_LINE_CLASS_NAME = '.line.remove';
export const CONFIRMATION_DISPLAY_TIME_MS = 2000;
@Component({
selector: 'button[docs-copy-source-code]',
standalone: true,
imports: [CommonModule, IconComponent],
templateUrl: './copy-source-code-button.component.html',
host: {
'type': 'button',
'aria-label': 'Copy example source to clipboard',
'title': 'Copy example source',
'(click)': 'copySourceCode()',
'[class.docs-copy-source-code-button-success]': 'showCopySuccess()',
'[class.docs-copy-source-code-button-failed]': 'showCopyFailure()',
},
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CopySourceCodeButton {
private readonly changeDetector = inject(ChangeDetectorRef);
private readonly clipboard = inject(Clipboard);
private readonly elementRef = inject(ElementRef);
protected readonly showCopySuccess = signal(false);
protected readonly showCopyFailure = signal(false);
copySourceCode(): void {
try {
const codeElement = this.elementRef.nativeElement.parentElement.querySelector(
'code',
) as HTMLElement;
const sourceCode = this.getSourceCode(codeElement);
this.clipboard.copy(sourceCode);
this.showResult(this.showCopySuccess);
} catch {
this.showResult(this.showCopyFailure);
}
}
private getSourceCode(codeElement: HTMLElement): string {
this.showCopySuccess.set(false);
this.showCopyFailure.set(false);
const removedLines: NodeList = codeElement.querySelectorAll(REMOVED_LINE_CLASS_NAME);
if (removedLines.length) {
// Get only those lines which are not marked as removed
const formattedText = Array.from(codeElement.querySelectorAll('.line:not(.remove)'))
.map((line) => (line as HTMLDivElement).innerText)
.join('\n');
return formattedText.trim();
} else {
const text: string = codeElement.innerText || '';
return text.replace(/\n\n\n/g, ``).trim();
}
}
private showResult(messageState: WritableSignal<boolean>) {
messageState.set(true);
setTimeout(() => {
messageState.set(false);
// It's required for code snippets embedded in the ExampleViewer.
this.changeDetector.markForCheck();
}, CONFIRMATION_DISPLAY_TIME_MS);
}
}
@@ -1,33 +0,0 @@
load("//tools:defaults.bzl", "ng_module")
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")
package(default_visibility = ["//visibility:private"])
ng_module(
name = "icon",
srcs = [
"icon.component.ts",
],
assets = [
":icon.component.css",
"icon.component.html",
],
visibility = [
"//adev/shared-docs/components:__pkg__",
"//adev/shared-docs/components/copy-source-code-button:__pkg__",
"//adev/shared-docs/components/navigation-list:__pkg__",
"//adev/shared-docs/components/table-of-contents:__pkg__",
"//adev/shared-docs/components/text-field:__pkg__",
"//adev/shared-docs/components/top-level-banner:__pkg__",
"//adev/shared-docs/components/viewers:__pkg__",
],
deps = [
"//packages/common",
"//packages/core",
],
)
sass_binary(
name = "style",
src = "icon.component.scss",
)
@@ -1 +0,0 @@
<ng-content></ng-content>
@@ -1,3 +0,0 @@
.docs-icon_high-contrast {
color: var(--primary-contrast);
}
@@ -1,55 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {DOCUMENT} from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
afterNextRender,
computed,
inject,
signal,
} from '@angular/core';
@Component({
selector: 'docs-icon',
standalone: true,
templateUrl: './icon.component.html',
styleUrl: './icon.component.scss',
host: {
'[class]': 'MATERIAL_SYMBOLS_OUTLINED',
'[style.font-size.px]': 'fontSize()',
'aria-hidden': 'true',
'translate': 'no',
},
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class IconComponent {
fontSize = computed(() => {
return IconComponent.isFontLoaded() ? null : 0;
});
protected readonly MATERIAL_SYMBOLS_OUTLINED = 'material-symbols-outlined';
private static isFontLoaded = signal(false);
/** Share the same promise across different instances of the component */
private static whenFontLoad?: Promise<FontFace[]> | undefined;
constructor() {
if (IconComponent.isFontLoaded()) {
return;
}
const document = inject(DOCUMENT);
afterNextRender(async () => {
IconComponent.whenFontLoad ??= document.fonts.load('normal 1px "Material Symbols Outlined"');
await IconComponent.whenFontLoad;
IconComponent.isFontLoaded.set(true);
});
}
}
-18
View File
@@ -1,18 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
export * from './viewers/docs-viewer/docs-viewer.component';
export * from './cookie-popup/cookie-popup.component';
export * from './navigation-list/navigation-list.component';
export * from './select/select.component';
export * from './slide-toggle/slide-toggle.component';
export * from './table-of-contents/table-of-contents.component';
export * from './text-field/text-field.component';
export * from './icon/icon.component';
export * from './search-dialog/search-dialog.component';
export * from './top-level-banner/top-level-banner.component';
@@ -1,58 +0,0 @@
load("//tools:defaults.bzl", "karma_web_test_suite", "ng_module", "ts_library")
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")
package(default_visibility = ["//visibility:private"])
ng_module(
name = "navigation-list",
srcs = [
"navigation-list.component.ts",
],
assets = [
":navigation-list.component.css",
"navigation-list.component.html",
],
visibility = [
"//adev/shared-docs/components:__pkg__",
],
deps = [
"//adev/shared-docs/components/icon",
"//adev/shared-docs/interfaces",
"//adev/shared-docs/pipes",
"//adev/shared-docs/services",
"//packages/common",
"//packages/core",
"//packages/router",
],
)
sass_binary(
name = "style",
src = "navigation-list.component.scss",
deps = [
"//adev/shared-docs/styles",
],
)
ts_library(
name = "test_lib",
testonly = True,
srcs = glob(
["*.spec.ts"],
),
deps = [
":navigation-list",
"//adev/shared-docs/interfaces",
"//adev/shared-docs/services",
"//packages/core",
"//packages/core/testing",
"//packages/platform-browser",
"//packages/router",
"//packages/router/testing",
],
)
karma_web_test_suite(
name = "test",
deps = [":test_lib"],
)
@@ -1,76 +0,0 @@
<ng-template #navigationList let-navigationItems>
<ul
class="docs-navigation-list docs-faceted-list"
[class.docs-navigation-list-dropdown]="isDropdownView"
>
@for (item of navigationItems; track $index) {
<li
class="docs-faceted-list-item"
[class.docs-navigation-link-hidden]="displayItemsToLevel && item.level > displayItemsToLevel"
>
@if (item.path) { @if (item.isExternal) {
<a [href]="item.path" target="_blank">
<span [class.docs-external-link]="item.isExternal">{{ item.label }}</span>
@if (item.children && item.level! > 1 && !item.isExpanded) {
<docs-icon>chevron_right</docs-icon>
}
</a>
} @else {
<a
[routerLink]="'/' + item.path"
[routerLinkActiveOptions]="{
queryParams: 'ignored',
fragment: 'ignored',
matrixParams: 'exact',
paths: 'exact',
exact: false
}"
routerLinkActive="docs-faceted-list-item-active"
(click)="emitClickOnLink()"
>
<span>{{ item.label }}</span>
@if (item.children && !item.isExpanded) {
<docs-icon>chevron_right</docs-icon>
}
</a>
} } @else {
<!-- Nav Section Header -->
@if (item.level !== collapsableLevel && item.level !== expandableLevel) {
<div class="docs-secondary-nav-header">
<span>{{ item.label }}</span>
</div>
}
<!-- Nav Button Expand/Collapse -->
@if ((item.children && item.level === expandableLevel) || item.level === collapsableLevel) {
<button
type="button"
(click)="toggle(item)"
attr.aria-label="{{ item.isExpanded ? 'Collapse' : 'Expand' }} {{ item.label }}"
[attr.aria-expanded]="item.isExpanded"
class="docs-secondary-nav-button"
[class.docs-faceted-list-item-active]="item | isActiveNavigationItem: activeItem()"
[class.docs-expanded-button]="item.children && item.level == collapsableLevel"
[class.docs-not-expanded-button]="item.children && item.level === expandableLevel"
[class.docs-nav-item-has-icon]="
item.children && item.level === expandableLevel && !item.isExpanded
"
>
@if (item.children && item.level === collapsableLevel) {
<docs-icon>arrow_back</docs-icon>
}
<span>{{ item.label }}</span>
</button>
} } @if (item.children?.length > 0) {
<ng-container
*ngTemplateOutlet="navigationList; context: {$implicit: item.children}"
></ng-container>
}
</li>
}
</ul>
</ng-template>
<ng-container
*ngTemplateOutlet="navigationList; context: {$implicit: navigationItems}"
></ng-container>
@@ -1,148 +0,0 @@
@use '../../styles/media-queries' as mq;
:host {
display: flex;
min-width: var(--secondary-nav-width);
list-style: none;
overflow-y: auto;
overflow-x: hidden;
height: 100vh;
padding: 0;
margin: 0;
padding-block: 1.5rem;
font-size: 0.875rem;
box-sizing: border-box;
&::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0);
cursor: pointer;
}
&::-webkit-scrollbar {
width: 6px;
height: 6px;
}
&::-webkit-scrollbar-thumb {
background-color: var(--septenary-contrast);
@include mq.for-tablet-landscape-down {
background-color: var(--quinary-contrast);
}
border-radius: 10px;
transition: background-color 0.3s ease;
}
&::-webkit-scrollbar-thumb:hover {
background-color: var(--quinary-contrast);
}
.docs-nav-secondary & {
padding-block: 2rem;
}
> .docs-faceted-list {
border: 0;
}
.docs-navigation-link-hidden {
display: none;
}
.docs-nav-item-has-icon {
&::after {
// FIXME: for some reason this disappears when transformed
content: 'chevron_right';
font-size: 1.25rem;
font-family: var(--icons);
}
}
}
.docs-secondary-nav-header {
padding-block: 1.25rem;
font-weight: 500;
}
.docs-secondary-nav-button {
width: 15rem;
display: flex;
justify-content: space-between;
align-items: center;
border: none;
padding-block: 1.25rem;
padding-inline-start: 0;
color: var(--primary-contrast);
font-size: 0.875rem;
font-family: var(--inter-font);
line-height: 160%;
letter-spacing: -0.00875rem;
transition: color 0.3s ease, background 0.3s ease;
text-align: left; // forces left alignment of text in button
&.docs-secondary-nav-button-active {
// font gradient
background-image: var(--pink-to-purple-vertical-gradient);
&::before {
opacity: 1;
transform: scaleY(1);
background: var(--pink-to-purple-vertical-gradient);
}
&:hover {
&::before {
opacity: 1;
transform: scaleY(1.1);
}
}
}
}
.docs-expanded-button {
justify-content: start;
gap: 0.5rem;
}
a,
.docs-not-expanded-button {
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 500;
line-height: 1.4rem;
letter-spacing: -0.00875rem;
padding: 0.5rem;
padding-inline-start: 1rem;
text-align: left;
}
// Add padding-bottom to last item in the list
.docs-navigation-list {
width: 100%;
li:last-of-type {
ul:last-of-type {
li:last-of-type {
padding-block-end: 1rem;
}
}
}
&:first-child {
margin-inline-start: 1rem;
}
}
.docs-external-link {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
gap: 0.5rem;
&::after {
content: 'open_in_new';
font-family: var(--icons);
font-size: 1.1rem;
color: var(--quinary-contrast);
transition: color 0.3s ease;
margin-inline-end: 0.4rem;
}
}
@@ -1,147 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {NavigationList} from './navigation-list.component';
import {By} from '@angular/platform-browser';
import {NavigationItem} from '../../interfaces';
import {RouterTestingModule} from '@angular/router/testing';
import {provideExperimentalZonelessChangeDetection, signal} from '@angular/core';
import {NavigationState} from '../../services';
const navigationItems: NavigationItem[] = [
{
label: 'Introduction',
path: 'guide',
level: 1,
},
{
label: 'Getting Started',
level: 1,
children: [
{label: 'What is Angular?', path: 'guide/what-is-angular', level: 2},
{label: 'Setup', path: 'guide/setup', level: 2},
],
},
];
describe('NavigationList', () => {
let component: NavigationList;
let fixture: ComponentFixture<NavigationList>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [NavigationList, RouterTestingModule],
providers: [
{provide: NavigationState, useClass: FakeNavigationListState},
provideExperimentalZonelessChangeDetection(),
],
}).compileComponents();
fixture = TestBed.createComponent(NavigationList);
component = fixture.componentInstance;
});
it('should display provided navigation structure', () => {
component.navigationItems = [...navigationItems];
fixture.detectChanges();
const links = fixture.debugElement.queryAll(By.css('a'));
const nonClickableItem = fixture.debugElement.queryAll(By.css('.docs-secondary-nav-header'));
expect(links.length).toBe(3);
expect(nonClickableItem.length).toBe(1);
});
it('should append `docs-navigation-list-dropdown` when isDropdownView is true', () => {
component.isDropdownView = true;
fixture.detectChanges();
const ulElement = fixture.debugElement.query(By.css('ul.docs-navigation-list-dropdown'));
expect(ulElement).toBeTruthy();
});
it('should not append `docs-navigation-list-dropdown` when isDropdownView is false', () => {
component.isDropdownView = false;
fixture.detectChanges();
const ulElement = fixture.debugElement.query(By.css('ul.docs-navigation-list-dropdown'));
expect(ulElement).toBeFalsy();
});
it('should emit linkClicked when user clicked on link', () => {
const emitClickOnLinkSpy = spyOn(component, 'emitClickOnLink');
component.navigationItems = [...navigationItems];
fixture.detectChanges(true);
const guideLink = fixture.debugElement.query(By.css('a[href="/guide"]'));
guideLink.nativeElement.click();
expect(emitClickOnLinkSpy).toHaveBeenCalledTimes(1);
});
it(`should not call navigationState.toggleItem() when item's level is equal to 1 and is not neither expandable or collapsable level`, () => {
const navigationState = TestBed.inject(NavigationState);
const toggleItemSpy = spyOn(navigationState, 'toggleItem');
const itemToToggle = navigationItems[1];
component.toggle(itemToToggle);
expect(toggleItemSpy).not.toHaveBeenCalled();
});
it(`should call navigationState.toggleItem() when item's level is expandable`, () => {
const navigationState = TestBed.inject(NavigationState);
const toggleItemSpy = spyOn(navigationState, 'toggleItem');
const itemToToggle = navigationItems[1];
component.expandableLevel = 1;
component.toggle(itemToToggle);
expect(toggleItemSpy).toHaveBeenCalledOnceWith(itemToToggle);
});
it(`should call navigationState.toggleItem() when item's level is collapsable`, () => {
const navigationState = TestBed.inject(NavigationState);
const toggleItemSpy = spyOn(navigationState, 'toggleItem');
const itemToToggle = navigationItems[1].children![1];
component.collapsableLevel = 2;
component.toggle(itemToToggle);
expect(toggleItemSpy).toHaveBeenCalledOnceWith(itemToToggle);
});
it('should display items to provided level', () => {
component.navigationItems = [...navigationItems];
component.displayItemsToLevel = 1;
fixture.detectChanges(true);
const visibleItems = fixture.debugElement.queryAll(
By.css('li.docs-faceted-list-item:not(.docs-navigation-link-hidden)'),
);
const hiddenItems = fixture.debugElement.queryAll(
By.css('li.docs-faceted-list-item.docs-navigation-link-hidden'),
);
expect(visibleItems.length).toBe(2);
expect(visibleItems[0].nativeElement.innerText).toBe(navigationItems[0].label);
expect(visibleItems[1].nativeElement.innerText).toBe(navigationItems[1].label);
expect(hiddenItems.length).toBe(2);
expect(hiddenItems[0].nativeElement.innerText).toBe(navigationItems[1].children![0].label);
expect(hiddenItems[1].nativeElement.innerText).toBe(navigationItems[1].children![1].label);
});
});
class FakeNavigationListState {
isOpened = signal(true);
activeNavigationItem = signal(navigationItems.at(1));
toggleItem(item: NavigationItem) {}
}
@@ -1,60 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
Input,
Output,
inject,
} from '@angular/core';
import {NavigationItem} from '../../interfaces/index';
import {NavigationState} from '../../services/index';
import {RouterLink, RouterLinkActive} from '@angular/router';
import {CommonModule} from '@angular/common';
import {IconComponent} from '../icon/icon.component';
import {IsActiveNavigationItem} from '../../pipes/is-active-navigation-item.pipe';
@Component({
selector: 'docs-navigation-list',
standalone: true,
imports: [CommonModule, RouterLink, RouterLinkActive, IconComponent, IsActiveNavigationItem],
templateUrl: './navigation-list.component.html',
styleUrls: ['./navigation-list.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NavigationList {
@Input({required: true}) navigationItems: NavigationItem[] = [];
@Input() displayItemsToLevel: number = 2;
@Input() collapsableLevel: number | undefined = undefined;
@Input() expandableLevel: number = 2;
@Input() isDropdownView = false;
@Output() linkClicked = new EventEmitter<void>();
private readonly navigationState = inject(NavigationState);
expandedItems = this.navigationState.expandedItems;
activeItem = this.navigationState.activeNavigationItem;
toggle(item: NavigationItem): void {
if (
item.level === 1 &&
item.level !== this.expandableLevel &&
item.level !== this.collapsableLevel
) {
return;
}
this.navigationState.toggleItem(item);
}
emitClickOnLink(): void {
this.linkClicked.emit();
}
}
@@ -1,61 +0,0 @@
load("//tools:defaults.bzl", "karma_web_test_suite", "ng_module", "ts_library")
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")
package(default_visibility = ["//visibility:private"])
ng_module(
name = "search-dialog",
srcs = [
"search-dialog.component.ts",
],
assets = [
":search-dialog.component.css",
"search-dialog.component.html",
],
visibility = [
"//adev/shared-docs/components:__pkg__",
],
deps = [
"//adev/shared-docs/components/algolia-icon",
"//adev/shared-docs/components/text-field",
"//adev/shared-docs/directives",
"//adev/shared-docs/interfaces",
"//adev/shared-docs/pipes",
"//adev/shared-docs/services",
"//packages/common",
"//packages/core",
"//packages/forms",
"//packages/router",
],
)
sass_binary(
name = "style",
src = "search-dialog.component.scss",
)
ts_library(
name = "test_lib",
testonly = True,
srcs = glob(
["*.spec.ts"],
),
deps = [
":search-dialog",
"//adev/shared-docs/components/algolia-icon",
"//adev/shared-docs/interfaces",
"//adev/shared-docs/providers",
"//adev/shared-docs/services",
"//adev/shared-docs/testing",
"//packages/core",
"//packages/core/testing",
"//packages/platform-browser",
"//packages/router",
"//packages/router/testing",
],
)
karma_web_test_suite(
name = "test",
deps = [":test_lib"],
)
@@ -1,89 +0,0 @@
<dialog #searchDialog>
<div class="docs-search-container" (docsClickOutside)="closeSearchDialog()">
<docs-text-field
[autofocus]="true"
[hideIcon]="true"
[ngModel]="searchQuery()"
(ngModelChange)="updateSearchQuery($event)"
class="docs-search-input"
placeholder="Search docs"
></docs-text-field>
@if (searchResults() && searchResults()!.length > 0) {
<ul class="docs-search-results docs-mini-scroll-track">
@for (result of searchResults(); track result.objectID) {
<li docsSearchItem [item]="result">
<a
[routerLink]="'/' + result.url | relativeLink: 'pathname'"
[fragment]="result.url | relativeLink: 'hash'"
>
<div>
<div class="docs-result-icon-and-type">
<!-- Icon -->
<span class="docs-search-result-icon" aria-hidden="true">
<i role="presentation" class="material-symbols-outlined docs-icon-small">
{{ result.hierarchy.lvl0 === 'Tutorials' ? 'code' : 'description'}}
</i>
</span>
<!-- Results type -->
<span class="docs-search-results__type">
@let snippet = result._snippetResult.hierarchy?.lvl1?.value ?? '';
<ng-container
[ngTemplateOutlet]="highlightSnippet"
[ngTemplateOutletContext]="{snippet}"
></ng-container>
</span>
</div>
@let content = result._snippetResult.content;
@let hierarchy = result._snippetResult.hierarchy;
@if (content || hierarchy?.lvl2 || hierarchy?.lvl3 || hierarchy?.lvl4) {
<span class="docs-search-results__type docs-search-results__lvl2">
@let snippet = getBestSnippetForMatch(result);
<ng-container
[ngTemplateOutlet]="highlightSnippet"
[ngTemplateOutletContext]="{snippet}"
></ng-container>
</span>
}
</div>
<!-- Page title -->
<span class="docs-result-page-title">{{ result.hierarchy?.lvl0 }}</span>
</a>
</li>
}
</ul>
} @else {
<div class="docs-search-results docs-mini-scroll-track">
@if (searchResults() === undefined) {
<div class="docs-search-results__start-typing">
<span>Start typing to see results</span>
</div>
} @else if (searchResults()?.length === 0) {
<div class="docs-search-results__no-results">
<span>No results found</span>
</div>
}
</div>
}
<div class="docs-algolia">
<span>Search by</span>
<a href="https://www.algolia.com/developers/" target="_blank" rel="noopener">
<docs-algolia-icon />
</a>
</div>
</div>
</dialog>
<ng-template #highlightSnippet let-snippet="snippet">
@let parts = splitMarkedText(snippet);
@for (part of parts; track $index) {
@if (part.highlight) {
<mark>{{part.text}}</mark>
} @else {
<span>{{part.text}}</span>
}
}
</ng-template>
@@ -1,155 +0,0 @@
dialog {
background-color: transparent;
border: none;
padding-block-end: 3rem;
&::backdrop {
backdrop-filter: blur(5px);
}
}
.docs-search-container {
width: 750px;
max-width: 90vw;
background-color: var(--page-background);
border: 1px solid var(--senary-contrast);
border-radius: 0.25rem;
box-sizing: border-box;
.docs-search-input {
border-radius: 0.25rem 0.25rem 0 0;
border: none;
border-block-end: 1px solid var(--senary-contrast);
height: 2.6875rem; // 43px;
padding-inline-start: 1rem;
position: relative;
&::after {
content: 'Esc';
position: absolute;
right: 1rem;
color: var(--gray-400);
font-size: 0.875rem;
}
}
ul {
max-height: 50vh;
overflow-y: auto;
list-style-type: none;
padding-inline: 0;
padding-block-start: 1rem;
margin: 0;
border-block-end: 1px solid var(--senary-contrast);
li {
border-inline-start: 2px solid var(--senary-contrast);
margin-inline-start: 1rem;
padding-inline-end: 1rem;
padding-block: 0.25rem;
mark {
background: #e62600;
background: var(--red-to-orange-horizontal-gradient);
background-clip: text;
-webkit-background-clip: text;
color: transparent;
}
a {
color: var(--secondary-contrast);
display: flex;
justify-content: space-between;
gap: 0.5rem;
.docs-search-result-icon {
i {
display: flex;
align-items: center;
font-size: 1.2rem;
}
}
}
&.active {
background-color: var(--septenary-contrast); // stylelint-disable-line
}
&:hover,
&.active {
background-color: var(--octonary-contrast); // stylelint-disable-line
border-inline-start: 2px solid var(--primary-contrast);
a {
span:not(.docs-result-page-title),
.docs-search-results__type {
color: var(--primary-contrast);
i {
color: var(--primary-contrast);
}
}
}
}
}
.docs-search-result-icon,
.docs-search-results__type,
.docs-result-page-title {
color: var(--quaternary-contrast);
display: inline-block;
font-size: 0.875rem;
transition: color 0.3s ease;
padding: 0.75rem;
padding-inline-end: 0;
}
.docs-search-results__lvl2 {
display: inline-block;
margin-inline-start: 2rem;
padding-block-start: 0;
}
.docs-search-results__lvl3 {
margin-inline-start: 2rem;
padding-block-start: 0;
}
}
.docs-result-page-title {
font-size: 0.875rem;
font-weight: 400;
}
}
.docs-search-results__start-typing,
.docs-search-results__no-results {
padding: 0.75rem;
color: var(--gray-400);
}
.docs-result-icon-and-type {
display: flex;
.docs-search-results__type {
padding-inline-start: 0;
}
}
.docs-algolia {
display: flex;
align-items: center;
justify-content: end;
color: var(--gray-400);
padding: 1rem;
font-size: 0.75rem;
font-weight: 500;
gap: 0.25rem;
background-color: var(--page-background);
border-radius: 0 0 0.25rem 0.25rem;
docs-algolia-icon {
display: inline-flex;
margin-block-start: 0.12rem;
margin-inline-start: 0.15rem;
width: 4rem;
}
}
@@ -1,159 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {SearchDialog} from './search-dialog.component';
import {WINDOW} from '../../providers';
import {Search} from '../../services';
import {FakeEventTarget} from '../../testing/index';
import {By} from '@angular/platform-browser';
import {AlgoliaIcon} from '../algolia-icon/algolia-icon.component';
import {RouterTestingModule} from '@angular/router/testing';
import {Router} from '@angular/router';
import {provideExperimentalZonelessChangeDetection} from '@angular/core';
import {SearchResult} from '../../interfaces';
describe('SearchDialog', () => {
let fixture: ComponentFixture<SearchDialog>;
const fakeSearch = {
searchQuery: jasmine.createSpy(),
searchResults: jasmine.createSpy(),
};
const fakeWindow = new FakeEventTarget();
beforeEach(async () => {
fakeSearch.searchResults.and.returnValue([]);
fakeSearch.searchQuery.and.returnValue('');
await TestBed.configureTestingModule({
imports: [SearchDialog, RouterTestingModule],
providers: [
provideExperimentalZonelessChangeDetection(),
{
provide: Search,
useValue: fakeSearch,
},
{
provide: WINDOW,
useValue: fakeWindow,
},
],
}).compileComponents();
fixture = TestBed.createComponent(SearchDialog);
fixture.detectChanges();
});
it('should navigate to active item when user pressed Enter', () => {
const router = TestBed.inject(Router);
const navigateByUrlSpy = spyOn(router, 'navigateByUrl');
fakeSearch.searchResults.and.returnValue(fakeSearchResults);
fixture.detectChanges();
fakeWindow.dispatchEvent(
new KeyboardEvent('keydown', {
code: 'Enter',
key: 'Enter',
charCode: 13,
keyCode: 13,
view: window,
bubbles: true,
}),
);
expect(navigateByUrlSpy).toHaveBeenCalledOnceWith('fakeUrl1#h1');
});
it('should always display algolia logo', () => {
const algoliaIcon = fixture.debugElement.query(By.directive(AlgoliaIcon));
expect(algoliaIcon).toBeTruthy();
});
it('should display `No results found` message when there are no results for provided query', () => {
fakeSearch.searchResults.and.returnValue([]);
fixture.detectChanges();
const noResultsContainer = fixture.debugElement.query(
By.css('.docs-search-results__no-results'),
);
expect(noResultsContainer).toBeTruthy();
});
it('should display `Start typing to see results` message when there are no provided query', () => {
fakeSearch.searchResults.and.returnValue(undefined);
fixture.detectChanges();
const startTypingContainer = fixture.debugElement.query(
By.css('.docs-search-results__start-typing'),
);
expect(startTypingContainer).toBeTruthy();
});
it('should display list of the search results when results exist', () => {
fakeSearch.searchResults.and.returnValue(fakeSearchResults);
fixture.detectChanges();
const resultListContainer = fixture.debugElement.query(By.css('ul.docs-search-results'));
const resultItems = fixture.debugElement.queryAll(By.css('ul.docs-search-results li a'));
expect(resultListContainer).toBeTruthy();
expect(resultItems.length).toBe(2);
expect(resultItems[0].nativeElement.href).toBe(`${window.origin}/fakeUrl1#h1`);
expect(resultItems[1].nativeElement.href).toBe(`${window.origin}/fakeUrl2#h1`);
});
it('should close search dialog when user clicked outside `.docs-search-container`', () => {
const dialogContainer = fixture.debugElement.query(By.css('dialog'));
const closeSearchDialogSpy = spyOn(fixture.componentInstance, 'closeSearchDialog');
dialogContainer.nativeElement.click();
expect(closeSearchDialogSpy).toHaveBeenCalled();
});
});
const fakeSearchResults = [
{
'url': 'https://angular.dev/fakeUrl1#h1',
'hierarchy': {
'lvl0': 'FakeLvl0',
'lvl1': 'FakeLvl1',
'lvl2': 'FakeLvl2',
'lvl3': null,
'lvl4': null,
'lvl5': null,
'lvl6': null,
},
'objectID': 'fakeObjectId1',
_snippetResult: {},
type: '',
content: null,
},
{
'url': 'https://angular.dev/fakeUrl2#h1',
'hierarchy': {
'lvl0': 'FakeLvl0',
'lvl1': 'FakeLvl1',
'lvl2': 'FakeLvl2',
'lvl3': null,
'lvl4': null,
'lvl5': null,
'lvl6': null,
},
'objectID': 'fakeObjectId2',
type: '',
content: null,
_snippetResult: {},
},
] satisfies SearchResult[];
@@ -1,173 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {
ChangeDetectionStrategy,
Component,
ElementRef,
Injector,
OnDestroy,
Signal,
afterNextRender,
effect,
inject,
output,
viewChild,
viewChildren,
} from '@angular/core';
import {NgTemplateOutlet} from '@angular/common';
import {WINDOW} from '../../providers/index';
import {ClickOutside} from '../../directives/index';
import {Search} from '../../services/index';
import {TextField} from '../text-field/text-field.component';
import {FormsModule} from '@angular/forms';
import {ActiveDescendantKeyManager} from '@angular/cdk/a11y';
import {SearchItem} from '../../directives/search-item/search-item.directive';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {Router, RouterLink} from '@angular/router';
import {filter, fromEvent} from 'rxjs';
import {AlgoliaIcon} from '../algolia-icon/algolia-icon.component';
import {RelativeLink} from '../../pipes/relative-link.pipe';
import {SearchResult, SnippetResult} from '../../interfaces';
@Component({
selector: 'docs-search-dialog',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
ClickOutside,
TextField,
FormsModule,
SearchItem,
AlgoliaIcon,
RelativeLink,
RouterLink,
NgTemplateOutlet,
],
templateUrl: './search-dialog.component.html',
styleUrls: ['./search-dialog.component.scss'],
})
export class SearchDialog implements OnDestroy {
onClose = output();
dialog = viewChild.required<ElementRef<HTMLDialogElement>>('searchDialog');
items = viewChildren(SearchItem);
private readonly search = inject(Search);
private readonly relativeLink = new RelativeLink();
private readonly router = inject(Router);
private readonly window = inject(WINDOW);
private readonly injector = inject(Injector);
private readonly keyManager = new ActiveDescendantKeyManager(
this.items,
this.injector,
).withWrap();
searchQuery = this.search.searchQuery;
searchResults = this.search.searchResults;
constructor() {
effect(() => {
this.items();
afterNextRender(
{
write: () => this.keyManager.setFirstItemActive(),
},
{injector: this.injector},
);
});
this.keyManager.change.pipe(takeUntilDestroyed()).subscribe(() => {
this.keyManager.activeItem?.scrollIntoView();
});
afterNextRender({
write: () => {
if (!this.dialog().nativeElement.open) {
this.dialog().nativeElement.showModal?.();
}
},
});
fromEvent<KeyboardEvent>(this.window, 'keydown')
.pipe(takeUntilDestroyed())
.subscribe((event) => {
// When user presses Enter we can navigate to currently selected item in the search result list.
if (event.key === 'Enter') {
this.navigateToTheActiveItem();
} else {
this.keyManager.onKeydown(event);
}
});
}
splitMarkedText(snippet: string): Array<{highlight: boolean; text: string}> {
const parts: Array<{highlight: boolean; text: string}> = [];
while (snippet.indexOf('<ɵ>') !== -1) {
const beforeMatch = snippet.substring(0, snippet.indexOf('<ɵ>'));
const match = snippet.substring(snippet.indexOf('<ɵ>') + 3, snippet.indexOf('</ɵ>'));
parts.push({highlight: false, text: beforeMatch});
parts.push({highlight: true, text: match});
snippet = snippet.substring(snippet.indexOf('</ɵ>') + 4);
}
parts.push({highlight: false, text: snippet});
return parts;
}
getBestSnippetForMatch(result: SearchResult): string {
// if there is content, return it
if (result._snippetResult.content !== undefined) {
return result._snippetResult.content.value;
}
const hierarchy = result._snippetResult.hierarchy;
if (hierarchy === undefined) {
return '';
}
function matched(snippet: SnippetResult | undefined) {
return snippet?.matchLevel !== undefined && snippet.matchLevel !== 'none';
}
// return the most specific subheader match
if (matched(hierarchy.lvl4)) {
return hierarchy.lvl4!.value;
}
if (matched(hierarchy.lvl3)) {
return hierarchy.lvl3!.value;
}
if (matched(hierarchy.lvl2)) {
return hierarchy.lvl2!.value;
}
// if no subheader matched the query, fall back to just returning the most specific one
return hierarchy.lvl3?.value ?? hierarchy.lvl2?.value ?? '';
}
ngOnDestroy(): void {
this.keyManager.destroy();
}
closeSearchDialog() {
this.dialog().nativeElement.close();
this.onClose.emit();
}
updateSearchQuery(query: string) {
this.search.updateSearchQuery(query);
}
private navigateToTheActiveItem(): void {
const activeItemLink: string | undefined = this.keyManager.activeItem?.item?.url;
if (!activeItemLink) {
return;
}
this.router.navigateByUrl(this.relativeLink.transform(activeItemLink));
this.onClose.emit();
}
}
@@ -1,46 +0,0 @@
load("//tools:defaults.bzl", "karma_web_test_suite", "ng_module", "ts_library")
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")
package(default_visibility = ["//visibility:private"])
ng_module(
name = "select",
srcs = [
"select.component.ts",
],
assets = [
":select.component.css",
"select.component.html",
],
visibility = [
"//adev/shared-docs/components:__pkg__",
],
deps = [
"//packages/common",
"//packages/core",
"//packages/forms",
],
)
sass_binary(
name = "style",
src = "select.component.scss",
)
ts_library(
name = "test_lib",
testonly = True,
srcs = glob(
["*.spec.ts"],
),
deps = [
":select",
"//packages/core",
"//packages/core/testing",
],
)
karma_web_test_suite(
name = "test",
deps = [":test_lib"],
)
@@ -1,5 +0,0 @@
<select [attr.id]="id" [attr.name]="name" [ngModel]="selectedOption()" (ngModelChange)="setOption($event)">
@for (item of options; track item) {
<option [value]="item.value">{{ item.label }}</option>
}
</select>
@@ -1,31 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {Select} from './select.component';
import {provideExperimentalZonelessChangeDetection} from '@angular/core';
describe('Select', () => {
let component: Select;
let fixture: ComponentFixture<Select>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [Select],
providers: [provideExperimentalZonelessChangeDetection()],
});
fixture = TestBed.createComponent(Select);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -1,79 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {ControlValueAccessor, NG_VALUE_ACCESSOR, FormsModule} from '@angular/forms';
import {ChangeDetectionStrategy, Component, Input, forwardRef, signal} from '@angular/core';
import {CommonModule} from '@angular/common';
type SelectOptionValue = string | number | boolean;
export interface SelectOption {
label: string;
value: SelectOptionValue;
}
@Component({
selector: 'docs-select',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [CommonModule, FormsModule],
templateUrl: './select.component.html',
styleUrls: ['./select.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => Select),
multi: true,
},
],
host: {
class: 'docs-form-element',
},
})
export class Select implements ControlValueAccessor {
@Input({required: true, alias: 'selectId'}) id!: string;
@Input({required: true}) name!: string;
@Input({required: true}) options!: SelectOption[];
@Input() disabled = false;
// Implemented as part of ControlValueAccessor.
private onChange: (value: SelectOptionValue) => void = (_: SelectOptionValue) => {};
private onTouched: () => void = () => {};
protected readonly selectedOption = signal<SelectOptionValue | null>(null);
// Implemented as part of ControlValueAccessor.
writeValue(value: SelectOptionValue): void {
this.selectedOption.set(value);
}
// Implemented as part of ControlValueAccessor.
registerOnChange(fn: any): void {
this.onChange = fn;
}
// Implemented as part of ControlValueAccessor.
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
// Implemented as part of ControlValueAccessor.
setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled;
}
setOption($event: SelectOptionValue): void {
if (this.disabled) {
return;
}
this.selectedOption.set($event);
this.onChange($event);
this.onTouched();
}
}
@@ -1,46 +0,0 @@
load("//tools:defaults.bzl", "karma_web_test_suite", "ng_module", "ts_library")
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")
package(default_visibility = ["//visibility:private"])
ng_module(
name = "slide-toggle",
srcs = [
"slide-toggle.component.ts",
],
assets = [
":slide-toggle.component.css",
"slide-toggle.component.html",
],
visibility = [
"//adev/shared-docs/components:__pkg__",
],
deps = [
"//packages/common",
"//packages/core",
"//packages/forms",
],
)
sass_binary(
name = "style",
src = "slide-toggle.component.scss",
)
ts_library(
name = "test_lib",
testonly = True,
srcs = glob(
["*.spec.ts"],
),
deps = [
":slide-toggle",
"//packages/core",
"//packages/core/testing",
],
)
karma_web_test_suite(
name = "test",
deps = [":test_lib"],
)
@@ -1,14 +0,0 @@
<label [attr.for]="buttonId">
<span class="docs-label">{{ label }}</span>
<div class="docs-toggle">
<input
type="checkbox"
[id]="buttonId"
role="switch"
(click)="toggle()"
[class.docs-toggle-active]="checked()"
[checked]="checked()"
/>
<span class="docs-slider"></span>
</div>
</label>
@@ -1,78 +0,0 @@
:host,
label {
display: inline-flex;
gap: 0.5em;
align-items: center;
}
.docs-label {
font-size: 0.875rem;
font-style: normal;
font-weight: 500;
line-height: 160%; // 1.4rem
letter-spacing: -0.00875rem;
color: var(--quaternary-contrast);
}
.docs-toggle {
position: relative;
display: inline-block;
width: 3rem;
height: 1.5rem;
border: 1px solid var(--senary-contrast);
border-radius: 34px;
input {
opacity: 0;
width: 0;
height: 0;
}
}
.docs-slider {
position: absolute;
cursor: pointer;
border-radius: 34px;
inset: 0;
background-color: var(--septenary-contrast);
transition: background-color 0.3s ease, border-color 0.3s ease;
// background
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: 34px;
background: var(--pink-to-purple-horizontal-gradient);
opacity: 0;
transition: opacity 0.3s ease;
}
// toggle knob
&::after {
position: absolute;
content: '';
height: 1.25rem;
width: 1.25rem;
left: 0.125rem;
bottom: 0.125rem;
background-color: var(--page-background);
transition: transform 0.3s ease, background-color 0.3s ease;
border-radius: 50%;
}
}
input {
&:checked + .docs-slider {
// background
&::before {
opacity: 1;
}
// toggle knob
&::after {
transform: translateX(1.5rem);
}
}
}
@@ -1,60 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {SlideToggle} from './slide-toggle.component';
import {provideExperimentalZonelessChangeDetection} from '@angular/core';
describe('SlideToggle', () => {
let component: SlideToggle;
let fixture: ComponentFixture<SlideToggle>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [SlideToggle],
providers: [provideExperimentalZonelessChangeDetection()],
});
fixture = TestBed.createComponent(SlideToggle);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should toggle the value when clicked', () => {
expect(component['checked']()).toBeFalse();
const buttonElement = fixture.nativeElement.querySelector('input');
buttonElement.click();
expect(component['checked']()).toBeTrue();
});
it('should call onChange and onTouched when toggled', () => {
const onChangeSpy = jasmine.createSpy('onChangeSpy');
const onTouchedSpy = jasmine.createSpy('onTouchedSpy');
component.registerOnChange(onChangeSpy);
component.registerOnTouched(onTouchedSpy);
component.toggle();
expect(onChangeSpy).toHaveBeenCalled();
expect(onChangeSpy).toHaveBeenCalledWith(true);
expect(onTouchedSpy).toHaveBeenCalled();
});
it('should set active class for button when is checked', () => {
component.writeValue(true);
fixture.detectChanges();
const buttonElement: HTMLButtonElement = fixture.nativeElement.querySelector('input');
expect(buttonElement.classList.contains('docs-toggle-active')).toBeTrue();
component.writeValue(false);
fixture.detectChanges();
expect(buttonElement.classList.contains('docs-toggle-active')).toBeFalse();
});
});
@@ -1,69 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {ChangeDetectionStrategy, Component, Input, forwardRef, signal} from '@angular/core';
import {CommonModule} from '@angular/common';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
@Component({
selector: 'docs-slide-toggle',
standalone: true,
imports: [CommonModule],
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './slide-toggle.component.html',
styleUrls: ['./slide-toggle.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SlideToggle),
multi: true,
},
],
})
export class SlideToggle implements ControlValueAccessor {
@Input({required: true}) buttonId!: string;
@Input({required: true}) label!: string;
@Input() disabled = false;
// Implemented as part of ControlValueAccessor.
private onChange: (value: boolean) => void = (_: boolean) => {};
private onTouched: () => void = () => {};
protected readonly checked = signal(false);
// Implemented as part of ControlValueAccessor.
writeValue(value: boolean): void {
this.checked.set(value);
}
// Implemented as part of ControlValueAccessor.
registerOnChange(fn: any): void {
this.onChange = fn;
}
// Implemented as part of ControlValueAccessor.
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
// Implemented as part of ControlValueAccessor.
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
// Toggles the checked state of the slide-toggle.
toggle(): void {
if (this.disabled) {
return;
}
this.checked.update((checked) => !checked);
this.onChange(this.checked());
this.onTouched();
}
}
@@ -1,55 +0,0 @@
load("//tools:defaults.bzl", "karma_web_test_suite", "ng_module", "ts_library")
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")
package(default_visibility = ["//visibility:private"])
ng_module(
name = "table-of-contents",
srcs = [
"table-of-contents.component.ts",
],
assets = [
":table-of-contents.component.css",
"table-of-contents.component.html",
],
visibility = [
"//adev/shared-docs/components:__pkg__",
"//adev/shared-docs/components/viewers:__pkg__",
],
deps = [
"//adev/shared-docs/components/icon",
"//adev/shared-docs/interfaces",
"//adev/shared-docs/services",
"//packages/common",
"//packages/core",
"//packages/router",
],
)
sass_binary(
name = "style",
src = "table-of-contents.component.scss",
)
ts_library(
name = "test_lib",
testonly = True,
srcs = glob(
["*.spec.ts"],
),
deps = [
":table-of-contents",
"//adev/shared-docs/interfaces",
"//adev/shared-docs/providers",
"//adev/shared-docs/services",
"//packages/core",
"//packages/core/testing",
"//packages/router",
"//packages/router/testing",
],
)
karma_web_test_suite(
name = "test",
deps = [":test_lib"],
)

Some files were not shown because too many files have changed in this diff Show More