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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .claude/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "0.0.1",
"configurations": [
{
"name": "lf-documentation",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "serve"],
"port": 4200,
"url": "http://127.0.0.1:4200"
}
]
}
50 changes: 40 additions & 10 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ on:
workflow_dispatch:

env:
NPM_VERSION: "21.1.2"
NPM_VERSION: "21.2.0"

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
Expand All @@ -25,6 +25,14 @@ permissions:
jobs:
build:
runs-on: ubuntu-latest
# Only this job's coverage report comments on the PR. A job-level block replaces the workflow's,
# so it repeats the workflow scopes and keeps pull-requests: write away from the publish jobs.
permissions:
contents: write
pages: write
id-token: write
checks: write
pull-requests: write
steps:
- uses: actions/checkout@v6
with:
Expand All @@ -36,7 +44,7 @@ jobs:
- name: Setup Node.js environment
uses: actions/setup-node@v6
with:
node-version: "24"
node-version-file: ".nvmrc"

- name: npm ci
run: npm ci
Expand All @@ -53,9 +61,7 @@ jobs:
- name: npm run create-types-lf-ui-components
run: npm run create-types-lf-ui-components

- name: npm run create-lf-cdn
run: npm run create-lf-cdn

# Also builds the CDN bundle (create-lf-cdn) that the documentation's live demo loads.
- name: npm run build-lf-documentation-prod
run: npm run build-lf-documentation-prod

Expand All @@ -68,8 +74,8 @@ jobs:
- name: Install Playwright browser
run: npx playwright install --with-deps chromium

- name: npm run test
run: npm run test
- name: npm run test:coverage
run: npm run test:coverage

- name: Test Report
uses: dorny/test-reporter@v3
Expand All @@ -82,6 +88,30 @@ jobs:
use-actions-summary: "true"
fail-on-error: "false"

# Writes the coverage summary to the job summary and, for PRs from this repository, a PR comment.
# PRs from forks get a read-only token, so commenting is skipped for them.
- name: Coverage Report
uses: davelosert/vitest-coverage-report-action@v2
if: ${{ !cancelled() && hashFiles('coverage/coverage-summary.json') != '' }}
with:
name: Unit Test Coverage
json-summary-path: ./coverage/coverage-summary.json
json-final-path: ./coverage/coverage-final.json
comment-on: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && 'pr' || 'none' }}

- name: Upload Coverage Report
uses: actions/upload-artifact@v7
if: ${{ !cancelled() && hashFiles('coverage/index.html') != '' }}
with:
name: coverage-report
path: ./coverage

# Runs the element build in a browser, which the component unit tests cannot do: they never
# register a custom element. Must stay ahead of the step that moves the bundle out of
# ./dist/lf-cdn/browser.
- name: npm run verify-cdn-demo
run: npm run verify-cdn-demo

- name: "create folder ./dist/ui-components/cdn"
run: "mkdir -p ./dist/ui-components/cdn"

Expand Down Expand Up @@ -146,7 +176,7 @@ jobs:
- name: Setup Node.js environment
uses: actions/setup-node@v6
with:
node-version: "24"
node-version-file: ".nvmrc"
registry-url: "https://registry.npmjs.org"

- name: set NpmPackageVersion environment
Expand Down Expand Up @@ -199,7 +229,7 @@ jobs:
- name: Setup Node.js environment
uses: actions/setup-node@v6
with:
node-version: "24"
node-version-file: ".nvmrc"
registry-url: "https://registry.npmjs.org"

- name: "copy license"
Expand Down Expand Up @@ -247,7 +277,7 @@ jobs:
- name: Setup Node.js environment
uses: actions/setup-node@v6
with:
node-version: "24"
node-version-file: ".nvmrc"
registry-url: "https://registry.npmjs.org"

- name: Npm install gulp
Expand Down
6 changes: 2 additions & 4 deletions .github/workflows/security-scan.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- name: Setup Node.js environment
uses: actions/setup-node@v6
with:
node-version: "24"
node-version-file: ".nvmrc"

- name: npm ci
run: npm ci
Expand All @@ -37,9 +37,7 @@ jobs:
- name: npm run create-types-lf-ui-components
run: npm run create-types-lf-ui-components

- name: npm run create-lf-cdn
run: npm run create-lf-cdn

# Also builds the CDN bundle (create-lf-cdn) that the documentation's live demo loads.
- name: npm run build-lf-documentation-dev
run: npm run build-lf-documentation-dev

Expand Down
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
min-release-age=3
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
<!--Copyright Laserfiche.
Licensed under the MIT License. See LICENSE in the project root for license information.-->

## 21.2.0

### Breaking Changes

- **[BREAKING]**: Components the library renders inside another component now use an `lfint-` host element instead of `lf-`, in both the Angular package and the CDN build. For example, `lf-repository-browser` renders `<lfint-breadcrumbs>`, `<lfint-selection-list-component>` and `<lfint-loader-component>`, and `lf-field-container` renders `<lfint-field-template-container>` and `<lfint-field-adhoc-container>`.
Comment thread
paolo-arge-lf marked this conversation as resolved.
- **Action required**: Update stylesheet rules and test selectors that match these inner elements by their `lf-` tag name.
- Creating `LfBreadcrumbsComponent`, `LfSelectionListComponent`, `LfFieldTemplateContainerComponent` or `LfFieldAdhocContainerComponent` dynamically (`ViewContainerRef.createComponent`, `MatDialog.open`) now also gives an `lfint-` host element.
- Not affected: registered tag names, element properties, and components you place yourself (e.g. `<lf-breadcrumbs>` in a template or as a custom element).

### Fixes

- CDN build: Fixed components the library creates inside other components rendering twice (e.g. `lf-field-container` showed two template pickers and two "Add/remove fields" blocks, `lf-repository-browser` had a second empty breadcrumbs bar). Resolved by the `lfint-` rename above.
- CDN build: `lf-ui-components.js` is now wrapped in a function scope so its minified declarations no longer leak into `window`. On pages that share `window` with other bundles (e.g. SharePoint), a clashing global could break component creation with errors such as `Rx is not a function`.
- `[lf-field-template-container]`, `[lf-field-adhoc-container]`: Fixed `ExpressionChangedAfterItHasBeenCheckedError` (NG0100) in development mode when `initAsync` is called from `ngAfterViewInit`. `lf-field-adhoc-container` also no longer briefly shows its "no additional fields" message after rendering fields.

## 21.1.2

### Fixes
Expand Down
6 changes: 4 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ npm run build-lf-cdn-prod # CDN bundle
# Serve documentation locally (interactive development)
npm run serve # http://127.0.0.1:4200

# Watch mode for active UI development (run both in separate terminals)
npm run build-ui-components-dev-watch
# Watch mode for active UI development (run both in separate terminals). Start the documentation
# watch first: it builds the library once before it starts watching, and a library watch started
# during that build deletes the dist/ui-components it compiles against.
npm run build-lf-documentation-dev-watch
npm run build-ui-components-dev-watch

# Test (Vitest + Playwright Chromium)
npm run test
Expand Down
35 changes: 7 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,47 +59,26 @@ npm run create-types-lf-ui-components

The command above will output: `./types-lf-ui-components-publish/laserfiche-types-lf-ui-components-[MajorVersion].0.0.tgz`.

### Build lf-documentation
### Build and view lf-documentation locally

You can use the lf-documentation project for testing changes.

Build lf-documentation:
Build style sheets and lf-documentation. This script, and `npm run serve`, also build the library and the CDN bundle (`npm run create-lf-cdn`) that the framework-agnostic live demo loads; restart `npm run serve` to pick up a rebuilt bundle.

```sh
cd repo-root-folder
npm run build-ui-components-prod
npm run build-lf-documentation-prod
```

to create `./dist/lf-documentation`.
npm run build-lf-documentation-dev

Build style sheets:

```sh
npm run sass-lf
npm run sass-ms
# Documentation output folder: ./dist/lf-documentation
npm run serve
```

### For interactive development: Build lf-documentation in watch mode

This allows you to see changes you make to the ui components quickly. You can run the two commands in different windows so that you can see any changes in the browser, even those that are shared across components.
For watch mode, start the documentation watch first and, once it is watching, the library watch in a second terminal. The documentation watch builds the library once before it starts watching, and a library watch started during that build deletes the `dist/ui-components` it compiles against.

```sh
cd repo-root-folder
npm run build-ui-components-dev-watch
npm run build-lf-documentation-dev-watch
npm run build-ui-components-dev-watch
```

to create `./dist/lf-documentation`.

### View the interactive documentation

```sh
npx ng serve lf-documentation --configuration development --host 127.0.0.1 --port 4200
```

Or configure a web server to serve `.dist/lf-documentation/browser` (e.g. IIS on WIndows).

## Things to verify before creating a Pull Request

### Run tests
Expand Down
3 changes: 1 addition & 2 deletions build-local-npm-package.ps1
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
$ErrorActionPreference = 'Stop'

npm run build-ui-components-prod
npm run create-lf-cdn
npm run build-lf-documentation-prod
npm run build-lf-documentation-prod # also builds the CDN bundle (create-lf-cdn)

New-Item -ItemType Directory -Force -Path dist/ui-components/cdn | Out-Null

Expand Down
4 changes: 2 additions & 2 deletions build-test-locally.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ $steps = @(
Command = { npm run format:write }
}
[PSCustomObject]@{
Name = 'npm run create-lf-cdn'
Command = { npm run create-lf-cdn }
Name = 'npm run format:check'
Command = { npm run format:check }
}
[PSCustomObject]@{
Name = 'npm run sass-lf'
Expand Down
8 changes: 7 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ export default tseslint.config(
rules: {
...sharedRules,
"@angular-eslint/directive-selector": ["error", { type: "attribute", prefix: "lf", style: "camelCase" }],
"@angular-eslint/component-selector": ["error", { type: "element", prefix: "lf", style: "kebab-case" }],
// `lfint` is the internal selector prefix. A registered custom element tag must never be a
// selector Angular matches, or the component renders twice in the element build, so library
// templates reference `lfint-*` while `lf-*` stays available to Angular consumers.
"@angular-eslint/component-selector": [
"error",
{ type: "element", prefix: ["lf", "lfint"], style: "kebab-case" },
],
},
},

Expand Down
33 changes: 29 additions & 4 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,29 @@ async function processTypesFile() {
.pipe(dest(NPM_PUBLISH));
}

// The CDN bundle is loaded as a classic script, so its minified top-level declarations would
// otherwise become globals - two-character names like `Rx`, `Nx`, `$`. On a page that shares
// `window` with other bundles (SharePoint being the obvious case), anything that later assigns
// one of those names replaces the library's own binding, and the next component creation fails.
// Wrapping keeps the declarations private; `customElements.define` still registers globally.
// The wrapper opens on its own first line, so the bundle's own lines keep their columns and the
// source map only needs one empty line prepended (shiftSourceMapOneLine). The leading `;` ends
// any unterminated statement in a script the bundle is concatenated after, which would otherwise
// call the wrapper. The sourceMappingURL comment is moved past the closing brace so it stays last.
const SOURCE_MAPPING_URL_COMMENT = /\n?\/\/# sourceMappingURL=.*\n?$/;

function wrapInFunctionScope(contents) {
const sourceMappingUrl = contents.match(SOURCE_MAPPING_URL_COMMENT)?.[0].trim() ?? '';
return `;(function(){\n${contents.replace(SOURCE_MAPPING_URL_COMMENT, '')}\n})();\n${sourceMappingUrl}\n`;
}

// Matches the line wrapInFunctionScope adds above the bundle: `;` ends an empty generated line.
function shiftSourceMapOneLine(mapContents) {
const map = JSON.parse(mapContents);
map.mappings = `;${map.mappings}`;
return JSON.stringify(map);
}

async function renameLfCdn() {
const mainJsPath = LF_CDN_BROWSER_DIR + MAIN_SCRIPT_FILE;
const mainJsMapPath = LF_CDN_BROWSER_DIR + MAIN_SCRIPT_MAP_FILE;
Expand All @@ -63,12 +86,14 @@ async function renameLfCdn() {

if (fs.existsSync(mainJsPath)) {
const content = fs.readFileSync(mainJsPath, 'utf8');
fs.writeFileSync(cdnJsPath, content.replace(MAIN_SCRIPT_MAP_FILE, CDN_UI_COMPONENTS_MAP_FILE));
fs.writeFileSync(cdnJsPath, wrapInFunctionScope(content.replace(MAIN_SCRIPT_MAP_FILE, CDN_UI_COMPONENTS_MAP_FILE)));
fs.unlinkSync(mainJsPath);
}

if (fs.existsSync(mainJsMapPath)) {
fs.renameSync(mainJsMapPath, cdnJsMapPath);
// Only a map that belongs to the bundle just wrapped is shifted, so a rerun never shifts twice.
if (fs.existsSync(mainJsMapPath)) {
fs.writeFileSync(cdnJsMapPath, shiftSourceMapOneLine(fs.readFileSync(mainJsMapPath, 'utf8')));
fs.unlinkSync(mainJsMapPath);
}
}

const indexHtmlContent = fs.readFileSync(lfCdnIndexPath, 'utf8');
Expand Down
Loading
Loading