From e5eb9f4ef20195196538fb18a55f38bf9ee9957a Mon Sep 17 00:00:00 2001 From: Su Li Date: Mon, 21 Oct 2024 10:45:59 -0700 Subject: [PATCH] refactor(styles/common): create makeSdsSemanticAppTheme style util (#878) * refactor(styles/common): create makeSdsSemanticAppTheme style util Expose an internal style util that allows consumers to create SDS semantic app themes using custom SDS color model values * fix: lint error and remove package-lock.json * docs: document makeSdsSemanticAppTheme --- packages/components/README.md | 27 +++ .../src/core/styles/common/SDSAppTheme.ts | 189 +++++++----------- yarn.lock | 117 +++++------ 3 files changed, 141 insertions(+), 192 deletions(-) diff --git a/packages/components/README.md b/packages/components/README.md index f49ee21ef..cf0ba97ee 100644 --- a/packages/components/README.md +++ b/packages/components/README.md @@ -254,6 +254,33 @@ const theme = createTheme(appTheme) ``` +To apply custom colors that align with your branding while maintaining the SDS design configurations, use the `makeSdsSemanticAppTheme`. Provide it with a Colors object that follows the SDS model. + +```tsx +import { ThemeProvider as EmotionThemeProvider } from "@emotion/react"; +import { makeSdsSemanticAppTheme, makeThemeOptions, type Colors } from "@czi-sds/components"; +import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles"; +import createTheme from "@mui/material/styles/createTheme"; + +const CustomColors: Colors = { + ... +} + +const customColorAppTheme = makeSdsSemanticAppTheme(CustomColors); + +const appTheme = makeThemeOptions(customColorAppTheme) + +const theme = createTheme(appTheme) + + + + + + + + +``` + 💡 CZGE example available [here](https://github.com/chanzuckerberg/czgenepi/blob/trunk/src/frontend/src/common/styles/theme.ts). 💡 Material UI docs for custom theming available [here](https://mui.com/material-ui/customization/theming/). diff --git a/packages/components/src/core/styles/common/SDSAppTheme.ts b/packages/components/src/core/styles/common/SDSAppTheme.ts index 76382543a..9d9c88cd7 100644 --- a/packages/components/src/core/styles/common/SDSAppTheme.ts +++ b/packages/components/src/core/styles/common/SDSAppTheme.ts @@ -466,130 +466,77 @@ const sharedAppTheme: Omit = { }, }; -export const SDSLightAppTheme: AppTheme = { +/** + * Create a SDS App Theme with custom colors that follows the SDS color model. + */ +export const makeSdsSemanticAppTheme = (colors: Colors): AppTheme => ({ ...sharedAppTheme, - colors: SDSLightThemeColors, -}; - -// (mlila) whenever our theme uses colors, we need to make sure we allow consuming -// applications to override those colors using their own custom theme. -// By defining borders using SDSAppTheme.colors instead of defaultThemeColors, -// we allow other apps to specify their colors once, and have them apply -// throughout the application, such as in borders, etc without having to manually -// override every theme property that makes use of colors. - -SDSLightAppTheme.borders = { - accent: { - default: `1px solid ${SDSLightAppTheme.colors.blue[500]}`, - focused: `1px solid ${SDSLightAppTheme.colors.blue[500]}`, - hover: `1px solid ${SDSLightAppTheme.colors.blue[600]}`, - open: `1px solid ${SDSLightAppTheme.colors.blue[500]}`, - pressed: `1px solid ${SDSLightAppTheme.colors.blue[700]}`, - selected: `1px solid ${SDSLightAppTheme.colors.blue[500]}`, - }, - base: { - default: `1px solid ${SDSLightAppTheme.colors.gray[500]}`, - disabled: `1px solid ${SDSLightAppTheme.colors.gray[300]}`, - divider: `1px solid ${SDSLightAppTheme.colors.gray[200]}`, - hover: `1px solid ${SDSLightAppTheme.colors.gray[900]}`, - pressed: `1px solid ${SDSLightAppTheme.colors.gray[900]}`, - table: `1px solid ${SDSLightAppTheme.colors.gray[300]}`, - }, - beta: { - default: `1px solid ${SDSLightAppTheme.colors.purple[600]}`, - extraThick: `4px solid ${SDSLightAppTheme.colors.purple[600]}`, - thick: `2px solid ${SDSLightAppTheme.colors.purple[600]}`, - }, - info: { - default: `1px solid ${SDSLightAppTheme.colors.blue[600]}`, - extraThick: `4px solid ${SDSLightAppTheme.colors.blue[600]}`, - thick: `2px solid ${SDSLightAppTheme.colors.blue[600]}`, - }, - link: { - dashed: `1px dashed`, - solid: `1px solid`, - }, - negative: { - default: `1px solid ${SDSLightAppTheme.colors.red[600]}`, - extraThick: `4px solid ${SDSLightAppTheme.colors.red[600]}`, - thick: `2px solid ${SDSLightAppTheme.colors.red[600]}`, - }, - neutral: { - default: `1px solid ${SDSLightAppTheme.colors.gray[500]}`, - extraThick: `4px solid ${SDSLightAppTheme.colors.gray[500]}`, - thick: `2px solid ${SDSLightAppTheme.colors.gray[500]}`, - }, - none: "none", - notice: { - default: `1px solid ${SDSLightAppTheme.colors.yellow[600]}`, - extraThick: `4px solid ${SDSLightAppTheme.colors.yellow[600]}`, - thick: `2px solid ${SDSLightAppTheme.colors.yellow[600]}`, - }, - positive: { - default: `1px solid ${SDSLightAppTheme.colors.green[600]}`, - extraThick: `4px solid ${SDSLightAppTheme.colors.green[600]}`, - thick: `2px solid ${SDSLightAppTheme.colors.green[600]}`, + // (mlila) whenever our theme uses colors, we need to make sure we allow consuming + // applications to override those colors using their own custom theme. + // By defining borders using SDSAppTheme.colors instead of defaultThemeColors, + // we allow other apps to specify their colors once, and have them apply + // throughout the application, such as in borders, etc without having to manually + // override every theme property that makes use of colors. + borders: { + accent: { + default: `1px solid ${colors.blue[500]}`, + focused: `1px solid ${colors.blue[500]}`, + hover: `1px solid ${colors.blue[600]}`, + open: `1px solid ${colors.blue[500]}`, + pressed: `1px solid ${colors.blue[700]}`, + selected: `1px solid ${colors.blue[500]}`, + }, + base: { + default: `1px solid ${colors.gray[500]}`, + disabled: `1px solid ${colors.gray[300]}`, + divider: `1px solid ${colors.gray[200]}`, + hover: `1px solid ${colors.gray[900]}`, + pressed: `1px solid ${colors.gray[900]}`, + table: `1px solid ${colors.gray[300]}`, + }, + beta: { + default: `1px solid ${colors.purple[600]}`, + extraThick: `4px solid ${colors.purple[600]}`, + thick: `2px solid ${colors.purple[600]}`, + }, + info: { + default: `1px solid ${colors.blue[600]}`, + extraThick: `4px solid ${colors.blue[600]}`, + thick: `2px solid ${colors.blue[600]}`, + }, + link: { + dashed: `1px dashed`, + solid: `1px solid`, + }, + negative: { + default: `1px solid ${colors.red[600]}`, + extraThick: `4px solid ${colors.red[600]}`, + thick: `2px solid ${colors.red[600]}`, + }, + neutral: { + default: `1px solid ${colors.gray[500]}`, + extraThick: `4px solid ${colors.gray[500]}`, + thick: `2px solid ${colors.gray[500]}`, + }, + none: "none", + notice: { + default: `1px solid ${colors.yellow[600]}`, + extraThick: `4px solid ${colors.yellow[600]}`, + thick: `2px solid ${colors.yellow[600]}`, + }, + positive: { + default: `1px solid ${colors.green[600]}`, + extraThick: `4px solid ${colors.green[600]}`, + thick: `2px solid ${colors.green[600]}`, + }, }, -}; + colors, +}); -export const SDSDarkAppTheme: AppTheme = { - ...sharedAppTheme, - colors: SDSDarkThemeColors, -}; - -SDSDarkAppTheme.borders = { - accent: { - default: `1px solid ${SDSDarkAppTheme.colors.blue[500]}`, - focused: `1px solid ${SDSDarkAppTheme.colors.blue[500]}`, - hover: `1px solid ${SDSDarkAppTheme.colors.blue[600]}`, - open: `1px solid ${SDSDarkAppTheme.colors.blue[500]}`, - pressed: `1px solid ${SDSDarkAppTheme.colors.blue[700]}`, - selected: `1px solid ${SDSDarkAppTheme.colors.blue[500]}`, - }, - base: { - default: `1px solid ${SDSDarkAppTheme.colors.gray[500]}`, - disabled: `1px solid ${SDSDarkAppTheme.colors.gray[300]}`, - divider: `1px solid ${SDSDarkAppTheme.colors.gray[200]}`, - hover: `1px solid ${SDSDarkAppTheme.colors.gray[900]}`, - pressed: `1px solid ${SDSDarkAppTheme.colors.gray[900]}`, - table: `1px solid ${SDSDarkAppTheme.colors.gray[300]}`, - }, - beta: { - default: `1px solid ${SDSDarkAppTheme.colors.purple[600]}`, - extraThick: `4px solid ${SDSDarkAppTheme.colors.purple[600]}`, - thick: `2px solid ${SDSDarkAppTheme.colors.purple[600]}`, - }, - info: { - default: `1px solid ${SDSDarkAppTheme.colors.blue[600]}`, - extraThick: `4px solid ${SDSDarkAppTheme.colors.blue[600]}`, - thick: `2px solid ${SDSDarkAppTheme.colors.blue[600]}`, - }, - link: { - dashed: `1px dashed`, - solid: `1px solid`, - }, - negative: { - default: `1px solid ${SDSDarkAppTheme.colors.red[600]}`, - extraThick: `4px solid ${SDSDarkAppTheme.colors.red[600]}`, - thick: `2px solid ${SDSDarkAppTheme.colors.red[600]}`, - }, - neutral: { - default: `1px solid ${SDSDarkAppTheme.colors.gray[500]}`, - extraThick: `4px solid ${SDSDarkAppTheme.colors.gray[500]}`, - thick: `2px solid ${SDSDarkAppTheme.colors.gray[500]}`, - }, - none: "none", - notice: { - default: `1px solid ${SDSDarkAppTheme.colors.yellow[600]}`, - extraThick: `4px solid ${SDSDarkAppTheme.colors.yellow[600]}`, - thick: `2px solid ${SDSDarkAppTheme.colors.yellow[600]}`, - }, - positive: { - default: `1px solid ${SDSDarkAppTheme.colors.green[600]}`, - extraThick: `4px solid ${SDSDarkAppTheme.colors.green[600]}`, - thick: `2px solid ${SDSDarkAppTheme.colors.green[600]}`, - }, -}; +export const SDSLightAppTheme: AppTheme = + makeSdsSemanticAppTheme(SDSLightThemeColors); +export const SDSDarkAppTheme: AppTheme = + makeSdsSemanticAppTheme(SDSDarkThemeColors); /** * Helper function to select the appropriate theme settings. diff --git a/yarn.lock b/yarn.lock index c58bce4b5..408f5fa5a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -301,18 +301,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.25.7": - version: 7.25.7 - resolution: "@babel/parser@npm:7.25.7" - dependencies: - "@babel/types": "npm:^7.25.7" - bin: - parser: ./bin/babel-parser.js - checksum: 10c0/b771469bb6b636c18a8d642b9df3c73913c3860a979591e1a29a98659efd38b81d3e393047b5251fe382d4c82c681c12da9ce91c98d69316d2604d155a214bcf - languageName: node - linkType: hard - -"@babel/parser@npm:^7.25.8": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.25.7, @babel/parser@npm:^7.25.8": version: 7.25.8 resolution: "@babel/parser@npm:7.25.8" dependencies: @@ -1410,18 +1399,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.22.5, @babel/types@npm:^7.25.7, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": - version: 7.25.7 - resolution: "@babel/types@npm:7.25.7" - dependencies: - "@babel/helper-string-parser": "npm:^7.25.7" - "@babel/helper-validator-identifier": "npm:^7.25.7" - to-fast-properties: "npm:^2.0.0" - checksum: 10c0/e03e1e2e08600fa1e8eb90632ac9c253dd748176c8d670d85f85b0dc83a0573b26ae748a1cbcb81f401903a3d95f43c3f4f8d516a5ed779929db27de56289633 - languageName: node - linkType: hard - -"@babel/types@npm:^7.25.8": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.22.5, @babel/types@npm:^7.25.7, @babel/types@npm:^7.25.8, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": version: 7.25.8 resolution: "@babel/types@npm:7.25.8" dependencies: @@ -5190,7 +5168,7 @@ __metadata: languageName: node linkType: hard -"@testing-library/jest-dom@npm:6.5.0, @testing-library/jest-dom@npm:^6.1.5": +"@testing-library/jest-dom@npm:6.5.0": version: 6.5.0 resolution: "@testing-library/jest-dom@npm:6.5.0" dependencies: @@ -5205,6 +5183,21 @@ __metadata: languageName: node linkType: hard +"@testing-library/jest-dom@npm:^6.1.5": + version: 6.6.2 + resolution: "@testing-library/jest-dom@npm:6.6.2" + dependencies: + "@adobe/css-tools": "npm:^4.4.0" + aria-query: "npm:^5.0.0" + chalk: "npm:^3.0.0" + css.escape: "npm:^1.5.1" + dom-accessibility-api: "npm:^0.6.3" + lodash: "npm:^4.17.21" + redent: "npm:^3.0.0" + checksum: 10c0/1c51390f97063ce8b06a7267534aac0d6ac9b1f27042a0ae43ca52d886b32c9d2b0ff85e62e7cfeb7e1f089ef03c4d75fda4f2b7b1f08afb4bdb09a5d56566d9 + languageName: node + linkType: hard + "@testing-library/react@npm:^14.1.2": version: 14.3.1 resolution: "@testing-library/react@npm:14.3.1" @@ -5666,11 +5659,11 @@ __metadata: linkType: hard "@types/node@npm:*, @types/node@npm:^22.0.0": - version: 22.7.4 - resolution: "@types/node@npm:22.7.4" + version: 22.7.6 + resolution: "@types/node@npm:22.7.6" dependencies: undici-types: "npm:~6.19.2" - checksum: 10c0/c22bf54515c78ff3170142c1e718b90e2a0003419dc2d55f79c9c9362edd590a6ab1450deb09ff6e1b32d1b4698da407930b16285e8be3a009ea6cd2695cac01 + checksum: 10c0/d4406a63afce981c363fb1d1954aaf1759ad2d487c0833ebf667565ea4e45ff217d6fab4b5343badbdeccdf9d2e4a0841d633e0c929ceabcb33c288663dd0c73 languageName: node linkType: hard @@ -6498,16 +6491,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.1.0, acorn@npm:^8.12.1, acorn@npm:^8.7.1, acorn@npm:^8.8.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": - version: 8.12.1 - resolution: "acorn@npm:8.12.1" - bin: - acorn: bin/acorn - checksum: 10c0/51fb26cd678f914e13287e886da2d7021f8c2bc0ccc95e03d3e0447ee278dd3b40b9c57dc222acd5881adcf26f3edc40901a4953403232129e3876793cd17386 - languageName: node - linkType: hard - -"acorn@npm:^8.11.0": +"acorn@npm:^8.1.0, acorn@npm:^8.11.0, acorn@npm:^8.12.1, acorn@npm:^8.7.1, acorn@npm:^8.8.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": version: 8.13.0 resolution: "acorn@npm:8.13.0" bin: @@ -7044,9 +7028,9 @@ __metadata: linkType: hard "axe-core@npm:^4.10.0, axe-core@npm:^4.2.0, axe-core@npm:^4.8.4": - version: 4.10.0 - resolution: "axe-core@npm:4.10.0" - checksum: 10c0/732c171d48caaace5e784895c4dacb8ca6155e9d98045138ebe3952f78457dd05b92c57d05b41ce2a570aff87dbd0471e8398d2c0f6ebe79617b746c8f658998 + version: 4.10.1 + resolution: "axe-core@npm:4.10.1" + checksum: 10c0/53d865efb7284fd69bc95ced1a1709fd603ea07f06e272da06942e7cfeca1c823e09bde28f57178e3a1a4c9a089fe4b5d274c871e3e6522a3b1bffec8eaa7dd8 languageName: node linkType: hard @@ -7762,8 +7746,8 @@ __metadata: linkType: hard "chromatic@npm:^11.4.0": - version: 11.12.5 - resolution: "chromatic@npm:11.12.5" + version: 11.12.6 + resolution: "chromatic@npm:11.12.6" peerDependencies: "@chromatic-com/cypress": ^0.*.* || ^1.0.0 "@chromatic-com/playwright": ^0.*.* || ^1.0.0 @@ -7776,7 +7760,7 @@ __metadata: chroma: dist/bin.js chromatic: dist/bin.js chromatic-cli: dist/bin.js - checksum: 10c0/4a083e4b12ebd0e1f04f944fef66c8345d0895e4c42e253cc4726328b13207553d860fbd5955804fbea806ae06a58d88f8ef845e6c90ce8ce2bc62f5660a3c8e + checksum: 10c0/7d161c52bc239ac6e8380f9a64533dd3792819d8f103d0083a805d5c9ece0844787eedd45c19c41e69d0ef0a3756051fa3e5c1f2fc9630d60f2ada4d5452e929 languageName: node linkType: hard @@ -9521,9 +9505,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.5.28": - version: 1.5.39 - resolution: "electron-to-chromium@npm:1.5.39" - checksum: 10c0/36364f9c68a7d20c54b020fe81cabf3e6022b7feb91f63695dd1039612cac031cc98bbcc0c3ba957f1876dde89853ba48407c590a4227fd13616a028ac1a0944 + version: 1.5.41 + resolution: "electron-to-chromium@npm:1.5.41" + checksum: 10c0/97b82383963029e6ed0bd7a71eb527f640c8cf658c9e43c776b0257b3c65e366590ac54135683a21e4474a156b8be78717d6e94d3c1def84b69f92bf48f2390f languageName: node linkType: hard @@ -11202,13 +11186,6 @@ __metadata: languageName: node linkType: hard -"get-func-name@npm:^2.0.1": - version: 2.0.2 - resolution: "get-func-name@npm:2.0.2" - checksum: 10c0/89830fd07623fa73429a711b9daecdb304386d237c71268007f788f113505ef1d4cc2d0b9680e072c5082490aec9df5d7758bf5ac6f1c37062855e8e3dc0b9df - languageName: node - linkType: hard - "get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4": version: 1.2.4 resolution: "get-intrinsic@npm:1.2.4" @@ -11966,8 +11943,8 @@ __metadata: linkType: hard "html-webpack-plugin@npm:^5.5.0": - version: 5.6.0 - resolution: "html-webpack-plugin@npm:5.6.0" + version: 5.6.2 + resolution: "html-webpack-plugin@npm:5.6.2" dependencies: "@types/html-minifier-terser": "npm:^6.0.0" html-minifier-terser: "npm:^6.0.2" @@ -11982,7 +11959,7 @@ __metadata: optional: true webpack: optional: true - checksum: 10c0/50d1a0f90d512463ea8d798985d91a7ccc9d5e461713dedb240125b2ff0671f58135dd9355f7969af341ff4725e73b2defbc0984cfdce930887a48506d970002 + checksum: 10c0/4a6e8367d47e9265aaebd30cd83076bb4a1006eda526b7273f06328f68d1ad2d088da29bd10b41e5016352e8dc1f2b63707a36dde26bed438a01c23cd3799844 languageName: node linkType: hard @@ -14577,11 +14554,9 @@ __metadata: linkType: hard "loupe@npm:^3.1.0, loupe@npm:^3.1.1": - version: 3.1.1 - resolution: "loupe@npm:3.1.1" - dependencies: - get-func-name: "npm:^2.0.1" - checksum: 10c0/99f88badc47e894016df0c403de846fedfea61154aadabbf776c8428dd59e8d8378007135d385d737de32ae47980af07d22ba7bec5ef7beebd721de9baa0a0af + version: 3.1.2 + resolution: "loupe@npm:3.1.2" + checksum: 10c0/b13c02e3ddd6a9d5f8bf84133b3242de556512d824dddeea71cce2dbd6579c8f4d672381c4e742d45cf4423d0701765b4a6e5fbc24701def16bc2b40f8daa96a languageName: node linkType: hard @@ -16513,9 +16488,9 @@ __metadata: linkType: hard "picocolors@npm:^1.0.0, picocolors@npm:^1.0.1, picocolors@npm:^1.1.0": - version: 1.1.0 - resolution: "picocolors@npm:1.1.0" - checksum: 10c0/86946f6032148801ef09c051c6fb13b5cf942eaf147e30ea79edb91dd32d700934edebe782a1078ff859fb2b816792e97ef4dab03d7f0b804f6b01a0df35e023 + version: 1.1.1 + resolution: "picocolors@npm:1.1.1" + checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 languageName: node linkType: hard @@ -18161,8 +18136,8 @@ __metadata: linkType: hard "sass@npm:^1.23.7, sass@npm:^1.70.0": - version: 1.79.5 - resolution: "sass@npm:1.79.5" + version: 1.80.2 + resolution: "sass@npm:1.80.2" dependencies: "@parcel/watcher": "npm:^2.4.1" chokidar: "npm:^4.0.0" @@ -18170,7 +18145,7 @@ __metadata: source-map-js: "npm:>=0.6.2 <2.0.0" bin: sass: sass.js - checksum: 10c0/7331865fd1d0c03e6e180a4fe0e175ac1bf1214f6c77f0d99ad72fbe2ed9ede3fab8a64c0c41471cb8a358a9d11624ec59a49283f9b6070eb99c522b34b814bf + checksum: 10c0/0006e2ee7a732abaa96a663167b078b8d53b4d7fe5ce435636a8bca6e8a3b6ebc12308c0725f577a456329ed962436d96cfcdd50a8e733ed4c4e4590f21243c9 languageName: node linkType: hard @@ -19445,8 +19420,8 @@ __metadata: linkType: hard "terser@npm:^5.10.0, terser@npm:^5.26.0": - version: 5.35.0 - resolution: "terser@npm:5.35.0" + version: 5.36.0 + resolution: "terser@npm:5.36.0" dependencies: "@jridgewell/source-map": "npm:^0.3.3" acorn: "npm:^8.8.2" @@ -19454,7 +19429,7 @@ __metadata: source-map-support: "npm:~0.5.20" bin: terser: bin/terser - checksum: 10c0/c9899d23c2d1548329d5b5717cb0cfaae5cfa26e6c4930daebc7a4dc00658c3ca44c06626777a9d09f03b2b13338921769c0eb56b6ad8560d705a375425129d6 + checksum: 10c0/f4ed2bead19f64789ddcfb85b7cef78f3942f967b8890c54f57d1e35bc7d547d551c6a4c32210bce6ba45b1c738314bbfac6acbc6c762a45cd171777d0c120d9 languageName: node linkType: hard