From fe42b966c537f83d70d6f979ef43dc606e7f6508 Mon Sep 17 00:00:00 2001 From: Stamen Stoychev Date: Thu, 22 Oct 2026 12:06:04 +0300 Subject: [PATCH 1/5] Type-check samples in CI Samples are outside `astro check`, and their own tsconfigs mostly use `moduleResolution: node`, which cannot see the types the IG packages publish through `exports`. Every IG component types as `any`, so a prop the component does not have passes unnoticed; vnext merged two color-picker samples that way. `npm run check:samples` checks every published sample in one program with tsconfig.samples.json: the add:sample settings, bundler resolution, not strict. It takes about 30 seconds. A shim restores the global JSX.Element that @types/react 19 removed, which 683 generated samples still use. 25 samples fail today. They are listed with the reason in scripts/lib/known-type-errors.json, so CI blocks only new errors. A listed sample that no longer fails also fails the check, so the list only shrinks. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 5 ++ README.md | 8 ++- package.json | 1 + scripts/check-samples.js | 106 +++++++++++++++++++++++++++++ scripts/lib/known-type-errors.json | 30 ++++++++ scripts/lib/sample-globals.d.ts | 14 ++++ tsconfig.samples.json | 23 +++++++ 7 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 scripts/check-samples.js create mode 100644 scripts/lib/known-type-errors.json create mode 100644 scripts/lib/sample-globals.d.ts create mode 100644 tsconfig.samples.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c9409b8e6c..0e05dd6da2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,11 @@ jobs: - name: Type check run: npm run check + # Samples are outside `astro check`. Their own tsconfigs cannot see the + # IG packages' types, so this checks them all with one shared config. + - name: Type check samples + run: npm run check:samples + - name: Build run: npm run build diff --git a/README.md b/README.md index afcb9f184a..d8e476f82f 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,12 @@ Use the same `BASE_PATH` for the build and the tests. CI builds and tests with ` - the sample appears in the sidebar - it loads without errors in the browser console -4. Commit, push, and open a pull request targeting `vnext`. Include a screenshot of the running sample. +4. Type-check it along with every other sample (CI runs the same check): + ```bash + npm run check:samples + ``` + +5. Commit, push, and open a pull request targeting `vnext`. Include a screenshot of the running sample. ## Updating Ignite UI Package Versions @@ -156,6 +161,7 @@ Do **not** edit version strings in `package.json` files by hand; CI fails when a | `npm run preview` | Serve `dist/` on port 4200 | | `npm run test:smoke` | Run the Playwright smoke tests against the production build | | `npm run check` | Type-check `src/` and `tests/` with `astro check` | +| `npm run check:samples` | Type-check every sample; failures allowed only for samples in `scripts/lib/known-type-errors.json` | | `npm run add:sample` | Scaffold `samples/{group}/{component}/{name}/` | | `npm run update:ig` | Apply `scripts/lib/versions.js` to every `package.json` | | `npm run generate:code-viewer` | Regenerate `public/code-viewer/**/*.json` | diff --git a/package.json b/package.json index 87eeaf8a35..eb06a907d0 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "preview": "astro preview --port 4200", "build:preview": "npm run build && npm run preview", "check": "astro check", + "check:samples": "node scripts/check-samples.js", "generate:code-viewer": "node scripts/generate-code-viewer.js", "update:ig": "node scripts/update-ig.js", "test:smoke": "playwright test" diff --git a/scripts/check-samples.js b/scripts/check-samples.js new file mode 100644 index 0000000000..7eef03e1f9 --- /dev/null +++ b/scripts/check-samples.js @@ -0,0 +1,106 @@ +#!/usr/bin/env node +/** + * check-samples.js: type-check every published sample in one TypeScript run. + * + * WHY not each sample's own tsconfig + * ────────────────────────────────── + * Most samples resolve modules with `moduleResolution: node`, which cannot + * see the types the IG packages publish only through `exports`. Every IG + * component then types as `any`, so a prop the component does not have, e.g. + * , passes unnoticed. tsconfig.samples.json + * checks all of them with bundler resolution instead; one program over every + * sample takes well under a minute, where ~1000 `tsc -p` runs take an hour. + * + * Samples listed in lib/known-type-errors.json may fail. Any other sample + * with an error fails the check, and so does a listed sample that no longer + * has one, so the list only shrinks. + */ +import path from 'node:path'; +import ts from 'typescript'; + +import { REPO_ROOT, UNPUBLISHED } from './lib/samples.js'; +import knownErrors from './lib/known-type-errors.json' with { type: 'json' }; + +const CONFIG = path.join(REPO_ROOT, 'tsconfig.samples.json'); +const KNOWN_FILE = 'scripts/lib/known-type-errors.json'; +const KNOWN = new Set(Object.keys(knownErrors.samples)); + +// samples////… → // +const SLUG_RE = /^samples\/([^/]+\/[^/]+\/[^/]+)\//; + +/** The sample slug a file belongs to, or undefined outside samples/. */ +function slugOf(fileName) { + const rel = path.relative(REPO_ROOT, fileName).replace(/\\/g, '/'); + return rel.match(SLUG_RE)?.[1]; +} + +const formatHost = { + getCanonicalFileName: f => f, + getCurrentDirectory: () => REPO_ROOT, + getNewLine: () => '\n', +}; +// Pretty output in a terminal, one line per error in CI logs (as tsc does). +const format = process.stdout.isTTY ? ts.formatDiagnosticsWithColorAndContext : ts.formatDiagnostics; + +function report(diagnostics) { + console.error(format(diagnostics, formatHost)); +} + +function run() { + const { config, error } = ts.readConfigFile(CONFIG, ts.sys.readFile); + if (error) { + report([error]); + process.exit(1); + } + + const parsed = ts.parseJsonConfigFileContent(config, ts.sys, REPO_ROOT, undefined, CONFIG); + if (parsed.errors.length) { + report(parsed.errors); + process.exit(1); + } + + const fileNames = parsed.fileNames.filter(f => !UNPUBLISHED.has(slugOf(f))); + const checked = new Set(fileNames.map(slugOf).filter(Boolean)); + const program = ts.createProgram(fileNames, parsed.options); + + // Errors by sample; anything else (config, globals) is never allowed. + const bySlug = new Map(); + const other = []; + for (const d of ts.getPreEmitDiagnostics(program)) { + const slug = d.file && slugOf(d.file.fileName); + if (!slug) { + other.push(d); + continue; + } + if (!bySlug.has(slug)) { + bySlug.set(slug, []); + } + bySlug.get(slug).push(d); + } + + const failing = [...bySlug.keys()].filter(slug => !KNOWN.has(slug)).sort(); + const stale = [...KNOWN].filter(slug => !bySlug.has(slug)).sort(); + + if (other.length) { + report(other); + } + for (const slug of failing) { + report(bySlug.get(slug)); + } + + console.log(`Type-checked ${checked.size} sample(s); ${bySlug.size - failing.length} known to fail (${KNOWN_FILE}).`); + + if (failing.length) { + console.error(`\n${failing.length} sample(s) have type errors:`); + failing.forEach(slug => console.error(` ${slug}`)); + } + if (stale.length) { + console.error(`\n${stale.length} sample(s) in ${KNOWN_FILE} no longer fail; remove them from the list:`); + stale.forEach(slug => console.error(` ${slug}`)); + } + if (other.length || failing.length || stale.length) { + process.exit(1); + } +} + +run(); diff --git a/scripts/lib/known-type-errors.json b/scripts/lib/known-type-errors.json new file mode 100644 index 0000000000..96301c86e0 --- /dev/null +++ b/scripts/lib/known-type-errors.json @@ -0,0 +1,30 @@ +{ + "//": "Published samples that fail `npm run check:samples`, and why. The check fails on any other sample with a type error, and on a sample listed here that no longer has one: fix a sample, then remove it here. `npx tsc -p tsconfig.samples.json` prints every error.", + "samples": { + "editors/x-date-picker/date-limits": "root.render() without `const root = ReactDOM.createRoot(...)`", + "editors/x-date-picker/editing": "root.render() without `const root = ReactDOM.createRoot(...)`", + "editors/x-date-picker/format": "root.render() without `const root = ReactDOM.createRoot(...)`", + "grids/data-grid/column-resizing": "renders ; the component is GridColumnResizing", + "grids/data-grid/row-selection": "root.render() without `const root = ReactDOM.createRoot(...)`", + "grids/grid/disabled-summaries": "igniteui-react-grids publishes no types for the grids/combined side-effect import", + "grids/grid/editing-excel-style": "useRef() needs an initial value in React 19", + "grids/grid/localization": "IgrSelect onChange handler typed as a DOM change event", - "grids/grid/remote-paging-grid": "igniteui-react-grids publishes no types for the grids/combined side-effect import", - "grids/grid/row-pinning-extra-column": "rowList is an array; it has no toArray()", - "grids/grid-lite/sort-config-events": "IgrRating has readOnly, not readonly", - "grids/grid-lite/sort-config-pipeline": "IgrRating has readOnly, not readonly", - "grids/grid-lite/styling-config-themes": "igc-select and igc-grid-lite tags used in JSX without type declarations", - "inputs/badge/dot": "IgrAvatar has no size prop", - "inputs/badge/icon": "IgrAvatar has no size prop", - "inputs/badge/overview": "IgrAvatar has no size prop", - "inputs/badge/tailwind-styling": "IgrAvatar has no size prop", - "inputs/badge/type": "IgrAvatar has no size prop", - "inputs/button-group/custom-toggle": "selectionMode=\"multi\"; the values are single, single-required and multiple", - "inputs/color-picker/styling": "IgrColorPicker has no placeholder prop", - "inputs/color-picker/tailwind-styling": "IgrColorPicker has no size prop", - "interactions/chat/features": "createChatMarkdownRenderer() requires an options argument", - "maps/geo-map/display-azure-imagery": "ref callback returns a value, which React 19 reads as a cleanup function", - "maps/geo-map/display-bing-imagery": "ref callback returns a value, which React 19 reads as a cleanup function", - "scheduling/date-range-picker/slots": "useRef() needs an initial value in React 19" - } -} From 15b3460432f3bd388ba48533b4d795684225637f Mon Sep 17 00:00:00 2001 From: Stamen Stoychev Date: Thu, 22 Oct 2026 15:16:03 +0300 Subject: [PATCH 3/5] Run the build and smoke tests when a check fails A failed step skips every later one, so the red sample type check hid whether the build and smoke tests pass. The build now runs regardless, and the smoke tests whenever the build succeeds. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e05dd6da2..81d5f77a9d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,17 +49,23 @@ jobs: - name: Type check samples run: npm run check:samples + # A failed check above must not hide a broken build or smoke test, so + # the build runs regardless, and the smoke tests whenever it succeeds. - name: Build + id: build + if: ${{ !cancelled() }} run: npm run build # Browser binaries are cached per Playwright version; OS deps (apt) are # not cacheable and are installed on every run. - name: Get Playwright version id: playwright + if: ${{ !cancelled() && steps.build.outcome == 'success' }} run: echo "version=$(npx playwright --version | awk '{print $2}')" >> "$GITHUB_OUTPUT" - name: Cache Playwright browser id: playwright-cache + if: ${{ !cancelled() && steps.build.outcome == 'success' }} uses: actions/cache@v6 with: path: ~/.cache/ms-playwright @@ -67,12 +73,13 @@ jobs: # Headless tests only need the headless shell, not full Chromium. - name: Install Playwright browser - if: steps.playwright-cache.outputs.cache-hit != 'true' + if: ${{ !cancelled() && steps.build.outcome == 'success' && steps.playwright-cache.outputs.cache-hit != 'true' }} run: npx playwright install --with-deps --only-shell chromium - name: Install Playwright OS deps - if: steps.playwright-cache.outputs.cache-hit == 'true' + if: ${{ !cancelled() && steps.build.outcome == 'success' && steps.playwright-cache.outputs.cache-hit == 'true' }} run: npx playwright install-deps chromium - name: Smoke tests + if: ${{ !cancelled() && steps.build.outcome == 'success' }} run: npm run test:smoke From d73a3e5fad83f05e52e61dda821091ef9bc7dd92 Mon Sep 17 00:00:00 2001 From: Stamen Stoychev Date: Thu, 22 Oct 2026 16:28:07 +0300 Subject: [PATCH 4/5] Fix type errors in hand-maintained samples The errors `npm run check:samples` found in the 20 samples maintained here, not exported from igniteui-xplat-examples: - x-date-picker date-limits, editing, format: create `root` before root.render(), as the other x-date-picker samples do. They threw when run on their own; the browser strips the mount, so its build is byte-identical. - grid editing-excel-style, date-range-picker slots: give useRef() an initial value, which React 19 requires. - grid localization: type the IgrSelect change handler as the CustomEvent it is, reading the value from the selected item. - grid-lite sort-config-events, sort-config-pipeline: readOnly, not the attribute name readonly. The ratings were read-only already. - grid-lite styling-config-themes: declare its igc-* tags in React.JSX, which React 19 reads instead of the global JSX namespace. - badge dot, icon, overview, tailwind-styling, type; color-picker tailwind-styling: drop size, removed from every component in igniteui-webcomponents 5.0. The badge CSS already sets --ig-size. - color-picker styling: drop placeholder, which it does not have. - button-group custom-toggle: selection="multiple", not selectionMode="multi". The buttons were single-select; they now multi-select, as in the Web Components sample. - chat features: pass createChatMarkdownRenderer() its options object; every option is optional. Screenshots of the 15 samples that render deterministically are pixel-identical before and after. Co-Authored-By: Claude Opus 5.5 --- samples/editors/x-date-picker/date-limits/src/index.tsx | 2 ++ samples/editors/x-date-picker/editing/src/index.tsx | 2 ++ samples/editors/x-date-picker/format/src/index.tsx | 2 ++ samples/grids/grid-lite/sort-config-events/src/index.tsx | 2 +- .../grids/grid-lite/sort-config-pipeline/src/index.tsx | 2 +- .../grids/grid-lite/styling-config-themes/src/index.tsx | 2 +- samples/grids/grid/editing-excel-style/src/index.tsx | 2 +- samples/grids/grid/localization/src/index.tsx | 2 +- samples/inputs/badge/dot/src/index.tsx | 1 - samples/inputs/badge/icon/src/index.tsx | 2 +- samples/inputs/badge/overview/src/index.tsx | 1 - samples/inputs/badge/tailwind-styling/src/index.tsx | 8 ++++---- samples/inputs/badge/type/src/index.tsx | 3 --- samples/inputs/button-group/custom-toggle/src/index.tsx | 2 +- samples/inputs/color-picker/styling/src/index.tsx | 2 +- .../inputs/color-picker/tailwind-styling/src/index.tsx | 4 ---- samples/interactions/chat/features/src/index.tsx | 2 +- samples/scheduling/date-range-picker/slots/src/index.tsx | 2 +- 18 files changed, 20 insertions(+), 23 deletions(-) diff --git a/samples/editors/x-date-picker/date-limits/src/index.tsx b/samples/editors/x-date-picker/date-limits/src/index.tsx index 52b5350e4f..5962ee993a 100644 --- a/samples/editors/x-date-picker/date-limits/src/index.tsx +++ b/samples/editors/x-date-picker/date-limits/src/index.tsx @@ -48,4 +48,6 @@ export default class DatePickerDateLimits extends React.Component { } } +// rendering above class to the React DOM +const root = ReactDOM.createRoot(document.getElementById('root')); root.render(); diff --git a/samples/editors/x-date-picker/editing/src/index.tsx b/samples/editors/x-date-picker/editing/src/index.tsx index 3623d05ea5..478e41b2ac 100644 --- a/samples/editors/x-date-picker/editing/src/index.tsx +++ b/samples/editors/x-date-picker/editing/src/index.tsx @@ -33,4 +33,6 @@ export default class DatePickerEditing extends React.Component { } } +// rendering above class to the React DOM +const root = ReactDOM.createRoot(document.getElementById('root')); root.render(); diff --git a/samples/editors/x-date-picker/format/src/index.tsx b/samples/editors/x-date-picker/format/src/index.tsx index 605638bf44..3739b700fb 100644 --- a/samples/editors/x-date-picker/format/src/index.tsx +++ b/samples/editors/x-date-picker/format/src/index.tsx @@ -47,4 +47,6 @@ export default class DatePickerFormat extends React.Component { } } +// rendering above class to the React DOM +const root = ReactDOM.createRoot(document.getElementById('root')); root.render(); diff --git a/samples/grids/grid-lite/sort-config-events/src/index.tsx b/samples/grids/grid-lite/sort-config-events/src/index.tsx index 45392a43b7..3067cf98e4 100644 --- a/samples/grids/grid-lite/sort-config-events/src/index.tsx +++ b/samples/grids/grid-lite/sort-config-events/src/index.tsx @@ -12,7 +12,7 @@ const getTimeStamp = (): string => `[${new Date().toLocaleTimeString()}]`; // Define cellTemplate function outside component const ratingCellTemplate = (ctx: any) => { return ( - + ); }; diff --git a/samples/grids/grid-lite/sort-config-pipeline/src/index.tsx b/samples/grids/grid-lite/sort-config-pipeline/src/index.tsx index 5cd62608f5..e9b2128d06 100644 --- a/samples/grids/grid-lite/sort-config-pipeline/src/index.tsx +++ b/samples/grids/grid-lite/sort-config-pipeline/src/index.tsx @@ -10,7 +10,7 @@ import "./index.css"; // Define cellTemplate function outside component const ratingCellTemplate = (ctx: any) => { return ( - + ); }; diff --git a/samples/grids/grid-lite/styling-config-themes/src/index.tsx b/samples/grids/grid-lite/styling-config-themes/src/index.tsx index 4ffdad21a2..f96cb8bf8a 100644 --- a/samples/grids/grid-lite/styling-config-themes/src/index.tsx +++ b/samples/grids/grid-lite/styling-config-themes/src/index.tsx @@ -12,7 +12,7 @@ import { html } from 'lit-html'; import "igniteui-webcomponents/themes/light/bootstrap.css"; import "./index.scss"; -declare global { +declare module 'react' { namespace JSX { interface IntrinsicElements { 'igc-select': React.DetailedHTMLProps, HTMLElement>; diff --git a/samples/grids/grid/editing-excel-style/src/index.tsx b/samples/grids/grid/editing-excel-style/src/index.tsx index 3f404fe3c8..b6f3b7d2a0 100644 --- a/samples/grids/grid/editing-excel-style/src/index.tsx +++ b/samples/grids/grid/editing-excel-style/src/index.tsx @@ -7,7 +7,7 @@ import NwindData from "./NwindData.json"; import "igniteui-react-grids/grids/themes/light/bootstrap.css"; function Sample() { - const gridRef = useRef(); + const gridRef = useRef(null); useEffect(() => { gridRef.current.addEventListener("keydown", handleKeyDown); return () => { diff --git a/samples/grids/grid/localization/src/index.tsx b/samples/grids/grid/localization/src/index.tsx index 518fdbd9c2..47220a78d4 100644 --- a/samples/grids/grid/localization/src/index.tsx +++ b/samples/grids/grid/localization/src/index.tsx @@ -87,7 +87,7 @@ const GridLocalizationSample: React.FC = () => { Grid with Localization ) => setLocale(e.target.value)} + onChange={(e) => setLocale(e.detail.value)} > HI BG diff --git a/samples/inputs/badge/dot/src/index.tsx b/samples/inputs/badge/dot/src/index.tsx index 551a61e7b7..8fd13d05aa 100644 --- a/samples/inputs/badge/dot/src/index.tsx +++ b/samples/inputs/badge/dot/src/index.tsx @@ -59,7 +59,6 @@ export default function BadgeDot(): JSX.Element { diff --git a/samples/inputs/badge/icon/src/index.tsx b/samples/inputs/badge/icon/src/index.tsx index bd4f68d39b..4de74975fd 100644 --- a/samples/inputs/badge/icon/src/index.tsx +++ b/samples/inputs/badge/icon/src/index.tsx @@ -49,7 +49,7 @@ export default function BadgeIcon(): JSX.Element { ))}
- + diff --git a/samples/inputs/badge/overview/src/index.tsx b/samples/inputs/badge/overview/src/index.tsx index 46b6333d63..ba0dcc902c 100644 --- a/samples/inputs/badge/overview/src/index.tsx +++ b/samples/inputs/badge/overview/src/index.tsx @@ -24,7 +24,6 @@ export default function BadgeOverview(): JSX.Element { diff --git a/samples/inputs/badge/tailwind-styling/src/index.tsx b/samples/inputs/badge/tailwind-styling/src/index.tsx index a34c17722e..1113aa0c66 100644 --- a/samples/inputs/badge/tailwind-styling/src/index.tsx +++ b/samples/inputs/badge/tailwind-styling/src/index.tsx @@ -27,13 +27,13 @@ export default function BadgeTailwindStyling(): JSX.Element { return (
- +
- + @@ -41,13 +41,13 @@ export default function BadgeTailwindStyling(): JSX.Element {
- +
- + diff --git a/samples/inputs/badge/type/src/index.tsx b/samples/inputs/badge/type/src/index.tsx index e5b881c715..f15df48766 100644 --- a/samples/inputs/badge/type/src/index.tsx +++ b/samples/inputs/badge/type/src/index.tsx @@ -19,7 +19,6 @@ export default function BadgeType(): JSX.Element {
@@ -30,7 +29,6 @@ export default function BadgeType(): JSX.Element { @@ -43,7 +41,6 @@ export default function BadgeType(): JSX.Element { 2 diff --git a/samples/inputs/button-group/custom-toggle/src/index.tsx b/samples/inputs/button-group/custom-toggle/src/index.tsx index f4ea4d7c1f..ff9b862789 100644 --- a/samples/inputs/button-group/custom-toggle/src/index.tsx +++ b/samples/inputs/button-group/custom-toggle/src/index.tsx @@ -20,7 +20,7 @@ icons.forEach(([name, text]) => registerIconFromText(name, text, 'material')); export default function ButtonGroupCustomToggle() { return ( - + diff --git a/samples/inputs/color-picker/styling/src/index.tsx b/samples/inputs/color-picker/styling/src/index.tsx index e8a0954cac..db6bc27e79 100644 --- a/samples/inputs/color-picker/styling/src/index.tsx +++ b/samples/inputs/color-picker/styling/src/index.tsx @@ -86,7 +86,7 @@ export default function ColorPickerStyling() {
Colors
- +
diff --git a/samples/inputs/color-picker/tailwind-styling/src/index.tsx b/samples/inputs/color-picker/tailwind-styling/src/index.tsx index 3b8519493d..a3649d38a8 100644 --- a/samples/inputs/color-picker/tailwind-styling/src/index.tsx +++ b/samples/inputs/color-picker/tailwind-styling/src/index.tsx @@ -84,7 +84,6 @@ export default function ColorPickerTailwindStyling() { className={pickerClassName} id="textPicker" value={text} - size="small" label="Text" onInput={(e: CustomEvent) => setText(e.detail)} > @@ -98,7 +97,6 @@ export default function ColorPickerTailwindStyling() { className={pickerClassName} id="accentPicker" value={accent} - size="small" label="Accent" onInput={(e: CustomEvent) => setAccent(e.detail)} > @@ -112,7 +110,6 @@ export default function ColorPickerTailwindStyling() { className={pickerClassName} id="borderPicker" value={border} - size="small" label="Border" onInput={(e: CustomEvent) => setBorder(e.detail)} > @@ -126,7 +123,6 @@ export default function ColorPickerTailwindStyling() { className={pickerClassName} disabled={true} value="#7d91ab" - size="small" label="Shadow (Pro)" > diff --git a/samples/interactions/chat/features/src/index.tsx b/samples/interactions/chat/features/src/index.tsx index 4015112390..3fd13cefe5 100644 --- a/samples/interactions/chat/features/src/index.tsx +++ b/samples/interactions/chat/features/src/index.tsx @@ -90,7 +90,7 @@ igc-avatar::part(base) { useEffect(() => { let mounted = true; - createChatMarkdownRenderer().then(renderer => { + createChatMarkdownRenderer({}).then(renderer => { if (!mounted) return; setOptions((prev: any) => ({ diff --git a/samples/scheduling/date-range-picker/slots/src/index.tsx b/samples/scheduling/date-range-picker/slots/src/index.tsx index 855d1e48d5..44f7a55def 100644 --- a/samples/scheduling/date-range-picker/slots/src/index.tsx +++ b/samples/scheduling/date-range-picker/slots/src/index.tsx @@ -15,7 +15,7 @@ const Down = ''; export default function DrpSlots() { - const dateRangeRef = useRef(); + const dateRangeRef = useRef(null); const today: Date = new Date(); let tomorrow: Date = new Date(); From a5d6307871c306f59dd9c1fe500bf40ddf371eaa Mon Sep 17 00:00:00 2001 From: Stamen Stoychev Date: Thu, 22 Oct 2026 16:28:08 +0300 Subject: [PATCH 5/5] Skip generated samples with type errors in the check Five samples exported from igniteui-xplat-examples fail the check; a fix made here is lost at the next export. scripts/lib/skip-type-check.json lists them with their errors until the fix lands there. They stay published: unpublished.json would also drop them from the site and the docs code viewer. Turn off noUncheckedSideEffectImports, as in the TypeScript 5.9 the samples build with: igniteui-react-grids/grids/combined is an untyped side-effect entry, not an error in the two samples that import it. Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- scripts/check-samples.js | 9 ++++++--- scripts/lib/skip-type-check.json | 10 ++++++++++ tsconfig.samples.json | 3 +++ 4 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 scripts/lib/skip-type-check.json diff --git a/README.md b/README.md index 1938ef674b..05040b32b5 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ Do **not** edit version strings in `package.json` files by hand; CI fails when a | `npm run preview` | Serve `dist/` on port 4200 | | `npm run test:smoke` | Run the Playwright smoke tests against the production build | | `npm run check` | Type-check `src/` and `tests/` with `astro check` | -| `npm run check:samples` | Type-check every published sample | +| `npm run check:samples` | Type-check every published sample, except the generated ones listed in `scripts/lib/skip-type-check.json` | | `npm run add:sample` | Scaffold `samples/{group}/{component}/{name}/` | | `npm run update:ig` | Apply `scripts/lib/versions.js` to every `package.json` | | `npm run generate:code-viewer` | Regenerate `public/code-viewer/**/*.json` | diff --git a/scripts/check-samples.js b/scripts/check-samples.js index fccdd31c91..f3e0324ae5 100644 --- a/scripts/check-samples.js +++ b/scripts/check-samples.js @@ -11,15 +11,18 @@ * checks all of them with bundler resolution instead; one program over every * sample takes well under a minute, where ~1000 `tsc -p` runs take an hour. * - * Any type error fails the check. Samples in lib/unpublished.json are - * skipped, as they are everywhere else. + * Any type error fails the check. Skipped: samples in lib/unpublished.json, + * as everywhere else, and in lib/skip-type-check.json, generated samples + * whose fix has to land in igniteui-xplat-examples. */ import path from 'node:path'; import ts from 'typescript'; import { REPO_ROOT, UNPUBLISHED } from './lib/samples.js'; +import skipTypeCheck from './lib/skip-type-check.json' with { type: 'json' }; const CONFIG = path.join(REPO_ROOT, 'tsconfig.samples.json'); +const SKIPPED = new Set([...UNPUBLISHED, ...Object.keys(skipTypeCheck.samples)]); // samples////… → // const SLUG_RE = /^samples\/([^/]+\/[^/]+\/[^/]+)\//; @@ -55,7 +58,7 @@ function run() { process.exit(1); } - const fileNames = parsed.fileNames.filter(f => !UNPUBLISHED.has(slugOf(f))); + const fileNames = parsed.fileNames.filter(f => !SKIPPED.has(slugOf(f))); const checked = new Set(fileNames.map(slugOf).filter(Boolean)); const program = ts.createProgram(fileNames, parsed.options); diff --git a/scripts/lib/skip-type-check.json b/scripts/lib/skip-type-check.json new file mode 100644 index 0000000000..55535cbee5 --- /dev/null +++ b/scripts/lib/skip-type-check.json @@ -0,0 +1,10 @@ +{ + "//": "Samples `npm run check:samples` skips, with the error each has. All are exported from igniteui-xplat-examples, so a fix made only here is lost at the next export: fix the source there, then remove the sample from this list.", + "samples": { + "grids/data-grid/column-resizing": "renders ; the component is GridColumnResizing", + "grids/data-grid/row-selection": "root.render() without `const root = ReactDOM.createRoot(...)`", + "grids/grid/row-pinning-extra-column": "rowList is an array; it has no toArray()", + "maps/geo-map/display-azure-imagery": "ref callback returns a value, which React 19 reads as a cleanup function", + "maps/geo-map/display-bing-imagery": "ref callback returns a value, which React 19 reads as a cleanup function" + } +} diff --git a/tsconfig.samples.json b/tsconfig.samples.json index 72bc564c67..5a40109e7d 100644 --- a/tsconfig.samples.json +++ b/tsconfig.samples.json @@ -11,6 +11,9 @@ "types": ["vite/client"], // TypeScript 6 is strict by default. "strict": false, + // Off in TypeScript 5.9, which the samples build with. On, it rejects + // untyped side-effect entries such as igniteui-react-grids/grids/combined. + "noUncheckedSideEffectImports": false, "resolveJsonModule": true, "isolatedModules": true, "skipLibCheck": true,