Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0f60fd7181 | |||
| 251062e42e | |||
| 05a328976e | |||
| 503cb08b2f | |||
| f71be20476 | |||
| e6397cecc1 | |||
| 80ce80ece4 | |||
| 9546fcd631 |
@@ -1,3 +1,14 @@
|
||||
## [7.7.2](https://git.datacontroller.io/dc/dc/compare/v7.7.1...v7.7.2) (2026-05-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **client:** bundle Metropolis font locally to satisfy CSP ([9546fcd](https://git.datacontroller.io/dc/dc/commit/9546fcd6312f3e81f746ef6e32ef398810ed434a))
|
||||
* **client:** clear angular build cache on font strip to avoid stale dist ([503cb08](https://git.datacontroller.io/dc/dc/commit/503cb08b2fa40397434189f9c20eff3358eb7010))
|
||||
* **client:** postinstall removal of Metropolis [@font-face](https://git.datacontroller.io/font-face) from @clr/ui ([e6397ce](https://git.datacontroller.io/dc/dc/commit/e6397cecc13afe2a9238bdfb2b4b9b81f38d055c))
|
||||
* **client:** serve text-security-disc font locally ([80ce80e](https://git.datacontroller.io/dc/dc/commit/80ce80ece40012e59c7cd0340b4aa9a9aca46443))
|
||||
* **editor:** preserve numeric type for SAS num cols with static SOFTSELECT/HARDSELECT ([05a3289](https://git.datacontroller.io/dc/dc/commit/05a328976ea3d1d6ef7559850369aa580f0d067f))
|
||||
|
||||
## [7.7.1](https://git.datacontroller.io/dc/dc/compare/v7.7.0...v7.7.1) (2026-05-05)
|
||||
|
||||
|
||||
|
||||
+2
-1
@@ -62,7 +62,8 @@
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "src/images",
|
||||
"output": "images"
|
||||
"output": "images",
|
||||
"ignore": ["spinner.svg", "caret.svg"]
|
||||
}
|
||||
],
|
||||
"styles": ["src/styles.scss"],
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
"watch": "ng test watch=true",
|
||||
"pree2e": "webdriver-manager update",
|
||||
"e2e": "protractor protractor.config.js",
|
||||
"postinstall": "node ./src/version.ts && npm run add-githook",
|
||||
"postinstall": "node ./src/version.ts && npm run add-githook && node ./scripts/strip-clr-base64-fonts.mjs",
|
||||
"add-githook": "[ -d ../.git ] && git config core.hooksPath ./.git-hooks || true",
|
||||
"cypress": "cypress open",
|
||||
"cy:run": "cypress run",
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { readFileSync, writeFileSync, statSync, rmSync, existsSync } from 'fs'
|
||||
import { resolve } from 'path'
|
||||
|
||||
/**
|
||||
* Remove Clarity's Metropolis @font-face blocks from clr-ui.min.css.
|
||||
*
|
||||
* Why: Clarity ships Metropolis as base64 data: URLs. The deployed app
|
||||
* runs under CSP `default-src 'self'` (no data: font-src), so every page
|
||||
* logs a font-load failure for each weight. Firefox preemptively
|
||||
* validates every parsed src against CSP even when a later @font-face
|
||||
* supersedes the rule at render time, so the only way to silence the
|
||||
* console is to remove the offending blocks from the parsed CSS.
|
||||
*
|
||||
* Our styles.scss declares the same family/weight/style with same-origin
|
||||
* .woff files, so removing Clarity's blocks entirely is safe and leaves
|
||||
* Metropolis fully functional.
|
||||
*
|
||||
* Idempotent: matches by font-family, so works on a fresh install or a
|
||||
* file that's already been stripped on a previous run.
|
||||
*/
|
||||
const target = resolve('node_modules/@clr/ui/clr-ui.min.css')
|
||||
|
||||
let css
|
||||
try {
|
||||
css = readFileSync(target, 'utf8')
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
console.log(`skip: ${target} not found (likely pre-install run)`)
|
||||
process.exit(0)
|
||||
}
|
||||
throw err
|
||||
}
|
||||
|
||||
const sizeBefore = statSync(target).size
|
||||
const blockRe = /@font-face\{[^}]*Metropolis[^}]*\}/g
|
||||
const matches = css.match(blockRe) ?? []
|
||||
|
||||
if (matches.length === 0) {
|
||||
console.log(`already stripped: ${target}`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const stripped = css.replace(blockRe, '')
|
||||
writeFileSync(target, stripped)
|
||||
const sizeAfter = Buffer.byteLength(stripped)
|
||||
console.log(
|
||||
`removed ${matches.length} Metropolis @font-face block(s) from clr-ui.min.css ` +
|
||||
`(${sizeBefore} -> ${sizeAfter} bytes, saved ${sizeBefore - sizeAfter})`
|
||||
)
|
||||
|
||||
// Webpack 5's persistent cache treats node_modules as immutable
|
||||
// (snapshot.module.managedPaths default), so in-place edits don't
|
||||
// invalidate cached entries. Drop the Angular build cache so the next
|
||||
// build re-reads our stripped clr-ui.min.css.
|
||||
const cacheDir = resolve('.angular/cache')
|
||||
if (existsSync(cacheDir)) {
|
||||
rmSync(cacheDir, { recursive: true, force: true })
|
||||
console.log(`cleared ${cacheDir} (webpack persistent cache)`)
|
||||
}
|
||||
@@ -284,10 +284,18 @@ export class DcValidator {
|
||||
)
|
||||
|
||||
if (source.length > 0) {
|
||||
// For SAS num cols keep type='numeric' so HOT's numericEditor /
|
||||
// numericRenderer + numbro coercion stay alive — same per-column
|
||||
// model as the per-cell pattern in
|
||||
// editor.component.ts:reSetCellValidationValues().
|
||||
this.rules[i].source = source
|
||||
this.rules[i].type = 'autocomplete'
|
||||
this.rules[i].editor = 'autocomplete.custom'
|
||||
this.rules[i].renderer = 'autocomplete'
|
||||
this.rules[i].filter = false
|
||||
|
||||
if (this.rules[i].sasType !== 'num') {
|
||||
this.rules[i].type = 'autocomplete'
|
||||
}
|
||||
}
|
||||
|
||||
if (this.hasDqRules(ruleColName, ['SOFTSELECT'])) {
|
||||
|
||||
@@ -10,6 +10,9 @@ export interface DcColumnSettings {
|
||||
valid?: boolean
|
||||
desc?: string
|
||||
clsRule?: string
|
||||
// SAS-side column type from $dataFormats (e.g. 'num', 'char') — distinct
|
||||
// from Handsontable's `type` which drives renderer/editor selection
|
||||
sasType?: string
|
||||
}
|
||||
|
||||
export interface DcValidation extends HotColumnSettings, DcColumnSettings {}
|
||||
|
||||
@@ -38,11 +38,52 @@ describe('DC Validator - merge spec rules', () => {
|
||||
data: 'test_col',
|
||||
desc: 'test_desc',
|
||||
clsRule: 'cls_rule',
|
||||
length: 8
|
||||
length: 8,
|
||||
sasType: 'test_type'
|
||||
}
|
||||
]
|
||||
|
||||
expect(mergeColsRules(cols, rules, $dataFormats)).toEqual(expected)
|
||||
expect(cols[0].TYPE).toEqual('test_type')
|
||||
})
|
||||
|
||||
it('should populate sasType for num and char cols', () => {
|
||||
const rules: DcValidation[] = [{ data: 'num_col' }, { data: 'char_col' }]
|
||||
const cols: Col[] = [
|
||||
{
|
||||
NAME: 'num_col',
|
||||
MEMLABEL: '',
|
||||
DESC: '',
|
||||
LONGDESC: '',
|
||||
TYPE: '',
|
||||
CLS_RULE: '',
|
||||
VARNUM: 0,
|
||||
LABEL: '',
|
||||
FMTNAME: '',
|
||||
DDTYPE: ''
|
||||
},
|
||||
{
|
||||
NAME: 'char_col',
|
||||
MEMLABEL: '',
|
||||
DESC: '',
|
||||
LONGDESC: '',
|
||||
TYPE: '',
|
||||
CLS_RULE: '',
|
||||
VARNUM: 0,
|
||||
LABEL: '',
|
||||
FMTNAME: '',
|
||||
DDTYPE: ''
|
||||
}
|
||||
]
|
||||
const $dataFormats: any = {
|
||||
vars: {
|
||||
num_col: { format: 'best.', label: '', length: '8', type: 'num' },
|
||||
char_col: { format: '$32.', label: '', length: '32', type: 'char' }
|
||||
}
|
||||
}
|
||||
|
||||
const merged = mergeColsRules(cols, rules, $dataFormats)
|
||||
expect(merged.find((r) => r.data === 'num_col')?.sasType).toEqual('num')
|
||||
expect(merged.find((r) => r.data === 'char_col')?.sasType).toEqual('char')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -29,6 +29,7 @@ export const mergeColsRules = (
|
||||
if (rule && col.DESC) rule.desc = col.DESC
|
||||
if (rule && colFormats.length) rule.length = parseInt(colFormats.length)
|
||||
if (rule && col.CLS_RULE) rule.clsRule = col.CLS_RULE
|
||||
if (rule && colFormats?.type) rule.sasType = colFormats.type
|
||||
}
|
||||
|
||||
return rules
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+31
-1
@@ -9,9 +9,39 @@
|
||||
|
||||
@import './colors.scss';
|
||||
|
||||
/* CSP: replace Clarity's base64 Metropolis @font-face srcs with same-origin files. */
|
||||
@font-face {
|
||||
font-family: 'Metropolis';
|
||||
src: url('./assets/fonts/Metropolis-200.woff') format('woff');
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Metropolis';
|
||||
src: url('./assets/fonts/Metropolis-400.woff') format('woff');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Metropolis';
|
||||
src: url('./assets/fonts/Metropolis-500.woff') format('woff');
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Metropolis';
|
||||
src: url('./assets/fonts/Metropolis-600.woff') format('woff');
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: text-security-disc;
|
||||
src: url('https://raw.githubusercontent.com/noppa/text-security/master/dist/text-security-disc.woff');
|
||||
src: url('./assets/fonts/text-security-disc.woff') format('woff');
|
||||
}
|
||||
|
||||
// TODO: IMPORTANT CSP WOKRAROUND
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dcfrontend",
|
||||
"version": "7.7.1",
|
||||
"version": "7.7.2",
|
||||
"description": "Data Controller",
|
||||
"devDependencies": {
|
||||
"@saithodev/semantic-release-gitea": "^2.1.0",
|
||||
|
||||
Reference in New Issue
Block a user