Skip to content

Commit cb67d73

Browse files
Rishi-Anand-438-FilestackRishi-Anand-438claude
authored
Angular SDK (@filestack/angular) — Modernization & Critical Fixes (#77)
* Updated node version from 20.10.0 to 22.22.3 * FS-12842: Removed debug console.log from the PickerOverlayComponent * FS-12843: Removed .then(void(0)) * FS-12844: Trigger change detection after Filestack onClose callback * FS-12845: Use unique rootId prefixes for inline and drop pane pickers * FS-12841: Replaced string injection token with InjectionToken * FS-12846: Updated filestack-js version to ^3.47.4 * FS-12847: Added unit test * FS-12848: Set up GitHub Actions CI pipeline * FS-12849: Clean up stale build artifacts * FS-12850, FS-12851, FS-12852: Added support for filestack-js v4 * FS-12853: Convert to standalone components * FS-12854: Adopt modern Angular patterns * FS-12855: Add SSR support * FS-12856: Upgrade Angular peer dep to 19+ * FS-12857: Add 'ng-add' schematics * Added example-dist that will consume filestack-angular from the dist folder * FS-12858, FS-12859: Add upload progress, lazy picker, and zoneless change detection * FS-12860: Added storybook * Removed example-dist * Added apikey in .env file * Reverted FS-12848 * FS-12840: Changes done to read API key from config file instead of .env * FS-12840: Read API inside config filr from .env * fix: align @angular/animations with @angular/common version @angular/animations was pinned to ^20.1.8 while the rest of the Angular framework packages are on ^20.3.25. @angular/animations@20.1.8 has a strict peer dependency on @angular/common@20.1.8, which conflicted with @angular/common@20.3.25 and broke npm ci in CI (ERESOLVE). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Rishi Anand <rishi.a@celestialsys.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 283ddef commit cb67d73

53 files changed

Lines changed: 23782 additions & 17018 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Storybook loads variables prefixed with STORYBOOK_ into process.env.
2+
# Used by the picker *.stories.ts (via .storybook/storybook.config.ts) so the picker loads with a real apikey.
3+
STORYBOOK_FILESTACK_APIKEY=AChYFTnwRzuITHHX95rDmz

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v20.10.0
1+
v22.22.3

.storybook/main.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { StorybookConfig } from '@storybook/angular';
2+
3+
const config: StorybookConfig = {
4+
// Stories live next to the library components they document.
5+
stories: ['../projects/filestack-angular/src/**/*.stories.@(ts|mdx)'],
6+
addons: [],
7+
framework: {
8+
name: '@storybook/angular',
9+
options: {}
10+
}
11+
};
12+
13+
export default config;

.storybook/preview.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import type { Preview } from '@storybook/angular';
2+
3+
const preview: Preview = {
4+
parameters: {
5+
controls: {
6+
matchers: {
7+
color: /(background|color)$/i,
8+
date: /Date$/i
9+
}
10+
}
11+
}
12+
};
13+
14+
export default preview;

.storybook/storybook.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Shared Storybook config for the picker stories.
2+
// Real Filestack apikey so the pickers actually load in Storybook.
3+
// Storybook inlines env vars prefixed with STORYBOOK_ into process.env (see .env).
4+
export const STORYBOOK_FILESTACK_APIKEY = process.env['STORYBOOK_FILESTACK_APIKEY'] ?? '';

.storybook/tsconfig.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"extends": "../tsconfig.json",
3+
"compilerOptions": {
4+
"skipLibCheck": true,
5+
"lib": ["es2022", "dom"],
6+
"types": ["node"]
7+
},
8+
"include": [
9+
"*.ts",
10+
"../projects/filestack-angular/src/**/*.ts",
11+
"../projects/example/src/**/*.ts"
12+
],
13+
"exclude": [
14+
"../projects/**/*.spec.ts"
15+
]
16+
}

README.md

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,61 @@ This repository a contains angular workspace with two projects:
5252
- **PickerDropPaneComponent** - Filestack drop pane component that will open in a provided html container
5353
which can be also used independently if needed
5454

55+
## Compatibility
56+
`@filestack/angular` **v5** requires **Angular 19 or 20** (`@angular/core` / `@angular/common`
57+
`^19.0.0 || ^20.0.0`) and Node 20.19+/22.12+. For Angular 18 use `@filestack/angular` v3.x.
58+
59+
It works with **both filestack-js v3 (`>=3.47.4`) and v4**. The v4 client methods
60+
(`download`, `prefetch`, `setSecurity`, `setCname`, and the extra `storeURL` params) are
61+
exposed by the service but require filestack-js v4 at runtime.
62+
63+
## Server-Side Rendering (SSR)
64+
The library is SSR-safe (Angular Universal). Because the Filestack picker and
65+
`preview()` need the browser DOM, they are guarded by `isPlatformBrowser`:
66+
67+
- The picker components (`ng-picker-overlay`, `ng-picker-inline`, `ng-picker-drop-pane`)
68+
render their container element on the server but **do not** initialize or open the
69+
picker — that happens only in the browser, after hydration.
70+
- `FilestackService.preview()` returns **`null`** on the server (its return type is
71+
`HTMLIFrameElement | Window | null`); guard for `null` in your code.
72+
- `FilestackService.init()` is safe to call on the server (it only constructs the
73+
filestack-js client and does not touch the DOM). DOM-dependent methods such as
74+
`picker()` and `preview()` should still only be called in the browser.
75+
76+
No extra configuration is required — just render as usual; the components produce no
77+
errors during server rendering.
78+
79+
## Lazy picker loading
80+
If your app doesn't show the picker immediately, use `FilestackService.openPicker()` to
81+
load filestack-js on demand via a dynamic `import()`, keeping the SDK out of the initial
82+
bundle (it loads in a separate chunk the first time the picker is opened):
83+
84+
```typescript
85+
async showPicker() {
86+
// filestack-js is fetched only when this runs
87+
const picker = await this.filestackService.openPicker({ /* PickerOptions */ });
88+
}
89+
```
90+
91+
Returns the opened `PickerInstance` (or `null` on the server). For the bundle benefit to
92+
materialize, reach for `openPicker()` instead of eagerly calling synchronous client
93+
methods on the critical path.
94+
95+
## Zoneless change detection
96+
The SDK supports Angular's zoneless change detection (`provideZonelessChangeDetection()`):
97+
98+
- Component state (e.g. the overlay's open/closed flag) is **signal-based**, so changes
99+
schedule change detection without zone.js.
100+
- It never uses `NgZone.run()` or relies on zone.js patching.
101+
- Outputs (`uploadSuccess` / `uploadError`) use the signal `output()` API.
102+
103+
No changes are needed on your side — it works the same with or without zone.js.
104+
55105
## Usage
56106
### Installation
57107
Install it through NPM
58108
```bash
59-
npm install filestack-js
109+
npm install filestack-js # v3 (>=3.47.4) or v4
60110
npm install @filestack/angular
61111
```
62112
Include ```FilestackModule``` in ```app.module.ts```
@@ -121,14 +171,18 @@ Methods get the same input params as client class method.
121171
| init | void | Init filestack client with your apikey |
122172
| picker | [PickerInstance](https://filestack.github.io/filestack-js/interfaces/pickerinstance.html) | Open or close picker instance |
123173
| transform | string | Create a transformation url |
124-
| retrieve | Observable | Retrieve an info about a filestack handle |
174+
| retrieve | Observable | **Deprecated** (filestack-js v4) — use `download` or `metadata` instead |
175+
| download | Observable | Download a file by its Filestack handle (filestack-js v4+) |
125176
| metadata | Observable | Access files via their Filestack handles |
126-
| storeURL | Observable | Get info about a filestack handle metadata |
127-
| upload | Observable | Upload a file to the Filestack |
177+
| storeURL | Observable | Store a file from a URL (supports v4 `uploadTags`/`headers`/`workflowIds`) |
178+
| upload | Observable | Upload a file (or array of files via `multiupload`) to Filestack |
179+
| prefetch | Observable | Check permissions before running operations (filestack-js v4+) |
128180
| remove | Observable | Remove a file from the Filestack |
129181
| removeMetadata | Observable | Remove a file only from the Filestack system. The file remains in storage. |
130-
| preview | HTMLIFrameElement | Window | Get preview of uploaded file (need additional addon in your Filestack account)|
182+
| preview | HTMLIFrameElement | Window | null | Get preview of uploaded file (returns `null` on the server/SSR; needs a preview addon)|
131183
| logout | Observable | Clear cloud session from picker procviders |
184+
| setSecurity | void | Update the client security object at runtime (filestack-js v4+) |
185+
| setCname | void | Update the client CNAME at runtime (filestack-js v4+) |
132186
| setClientInstance | [ClientInstance](https://filestack.github.io/filestack-js/classes/client.html) | Put an existing client instance into filestack service |
133187

134188
### Examples

angular.json

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,11 @@
101101
"serve": {
102102
"builder": "@angular-devkit/build-angular:dev-server",
103103
"options": {
104-
"browserTarget": "example:build"
104+
"buildTarget": "example:build"
105105
},
106106
"configurations": {
107107
"production": {
108-
"browserTarget": "example:build:production"
108+
"buildTarget": "example:build:production"
109109
}
110110
}
111111
},
@@ -146,6 +146,24 @@
146146
"devServerTarget": "example:serve:production"
147147
}
148148
}
149+
},
150+
"storybook": {
151+
"builder": "@storybook/angular:start-storybook",
152+
"options": {
153+
"configDir": ".storybook",
154+
"browserTarget": "example:build",
155+
"compodoc": false,
156+
"port": 6006
157+
}
158+
},
159+
"build-storybook": {
160+
"builder": "@storybook/angular:build-storybook",
161+
"options": {
162+
"configDir": ".storybook",
163+
"browserTarget": "example:build",
164+
"compodoc": false,
165+
"outputDir": "dist/storybook"
166+
}
149167
}
150168
}
151169
}
@@ -155,5 +173,31 @@
155173
"schematicCollections": [
156174
"@angular-eslint/schematics"
157175
]
176+
},
177+
"schematics": {
178+
"@schematics/angular:component": {
179+
"type": "component"
180+
},
181+
"@schematics/angular:directive": {
182+
"type": "directive"
183+
},
184+
"@schematics/angular:service": {
185+
"type": "service"
186+
},
187+
"@schematics/angular:guard": {
188+
"typeSeparator": "."
189+
},
190+
"@schematics/angular:interceptor": {
191+
"typeSeparator": "."
192+
},
193+
"@schematics/angular:module": {
194+
"typeSeparator": "."
195+
},
196+
"@schematics/angular:pipe": {
197+
"typeSeparator": "."
198+
},
199+
"@schematics/angular:resolver": {
200+
"typeSeparator": "."
201+
}
158202
}
159203
}

0 commit comments

Comments
 (0)