From dbf2e0c0673b34b612cbd3e6382d97661f70e20a Mon Sep 17 00:00:00 2001 From: Hussain Khalil <122488130+hkhalil-akamai@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:41:20 -0400 Subject: [PATCH] upcoming: [M3-8638] - Introduce the new `(at)linode/ui` package (#11057) * Introduce the new @linode/ui package * Feedback @bnussman-akamai * Move existing usages of to new package * Tweaks * Fix unnecessary prop on component * Add CHANGELOG.md for UI package * Added changeset: new `@linode/ui` package with `BetaChip` as the first component * Add UI typechecking step to ci.yml --- .github/workflows/ci.yml | 33 +++++++- README.md | 2 +- package.json | 4 +- packages/manager/package.json | 1 + .../src/components/PrimaryNav/PrimaryNav.tsx | 2 +- .../DatabaseCreate/DatabaseCreate.tsx | 2 +- .../DatabaseLanding/DatabaseLogo.tsx | 2 +- packages/ui/.changeset/README.md | 18 +++++ .../pr-11057-added-1728496448213.md | 5 ++ packages/ui/.eslintrc.json | 75 +++++++++++++++++++ packages/ui/.prettierrc | 4 + packages/ui/CHANGELOG.md | 0 packages/ui/package.json | 61 +++++++++++++++ .../src/components/BetaChip/BetaChip.test.tsx | 33 ++++++++ .../ui/src/components/BetaChip/BetaChip.tsx | 62 +++++++++++++++ packages/ui/src/components/BetaChip/index.ts | 1 + packages/ui/src/components/Chip.tsx | 8 ++ packages/ui/src/components/index.ts | 2 + packages/ui/src/index.ts | 1 + packages/ui/testSetup.ts | 6 ++ packages/ui/tsconfig.json | 15 ++++ packages/ui/vitest.config.ts | 8 ++ scripts/changelog/utils/constants.mjs | 2 +- scripts/package-versions/index.js | 3 + 24 files changed, 342 insertions(+), 8 deletions(-) create mode 100644 packages/ui/.changeset/README.md create mode 100644 packages/ui/.changeset/pr-11057-added-1728496448213.md create mode 100644 packages/ui/.eslintrc.json create mode 100644 packages/ui/.prettierrc create mode 100644 packages/ui/CHANGELOG.md create mode 100644 packages/ui/package.json create mode 100644 packages/ui/src/components/BetaChip/BetaChip.test.tsx create mode 100644 packages/ui/src/components/BetaChip/BetaChip.tsx create mode 100644 packages/ui/src/components/BetaChip/index.ts create mode 100644 packages/ui/src/components/Chip.tsx create mode 100644 packages/ui/src/components/index.ts create mode 100644 packages/ui/src/index.ts create mode 100644 packages/ui/testSetup.ts create mode 100644 packages/ui/tsconfig.json create mode 100644 packages/ui/vitest.config.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 119f95cec4f..e2d1bd208bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: lint: strategy: matrix: - package: ["linode-manager", "@linode/api-v4", "@linode/validation"] + package: ["linode-manager", "@linode/api-v4", "@linode/validation", "@linode/ui"] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -191,6 +191,37 @@ jobs: - run: yarn --frozen-lockfile - run: yarn workspace @linode/search run test + test-ui: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "20.17" + - uses: actions/cache@v4 + with: + path: | + **/node_modules + key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + - run: yarn --frozen-lockfile + - run: yarn workspace @linode/ui run test + + typecheck-ui: + runs-on: ubuntu-latest + needs: build-sdk + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "20.17" + - uses: actions/cache@v4 + with: + path: | + **/node_modules + key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + - run: yarn --frozen-lockfile + - run: yarn workspace @linode/ui run typecheck + typecheck-manager: runs-on: ubuntu-latest needs: build-sdk diff --git a/README.md b/README.md index 942c886ab92..618cc77f6c0 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ ## Overview -This repository is home to the Akamai Connected **[Cloud Manager](https://cloud.linode.com)** and related [`@linode/api-v4`](packages/api-v4/) and [`@linode/validation`](packages/validation/) Typescript packages. +This repository is home to the Akamai Connected **[Cloud Manager](https://cloud.linode.com)** and related [`@linode/api-v4`](packages/api-v4/), [`@linode/validation`](packages/validation/) and [`@linode/ui`](packages/ui/) Typescript packages. ## Developing Locally diff --git a/package.json b/package.json index 92048af4679..e84e1d54952 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,8 @@ "up": "yarn install:all && yarn build:validation && yarn build:sdk && yarn start:all", "up:expose": "yarn install:all && yarn build:validation && yarn build:sdk && yarn start:all:expose", "dev": "yarn install:all && yarn start:all", - "start:all": "concurrently -n api-v4,validation,manager -c blue,yellow,green \"yarn workspace @linode/api-v4 start\" \"yarn workspace @linode/validation start\" \"yarn workspace linode-manager start\"", - "start:all:expose": "concurrently -n api-v4,validation,manager -c blue,yellow,green \"yarn workspace @linode/api-v4 start\" \"yarn workspace @linode/validation start\" \"yarn workspace linode-manager start:expose\"", + "start:all": "concurrently -n api-v4,validation,ui,manager -c blue,yellow,magenta,green \"yarn workspace @linode/api-v4 start\" \"yarn workspace @linode/validation start\" \"yarn workspace @linode/ui start\" \"yarn workspace linode-manager start\"", + "start:all:expose": "concurrently -n api-v4,validation,ui,manager -c blue,yellow,magenta,green \"yarn workspace @linode/api-v4 start\" \"yarn workspace @linode/validation start\" \"yarn workspace @linode/ui start\" \"yarn workspace linode-manager start:expose\"", "start:manager": "yarn workspace linode-manager start", "start:manager:ci": "yarn workspace linode-manager start:ci", "clean": "rm -rf node_modules && rm -rf packages/@linode/api-v4/node_modules && rm -rf packages/manager/node_modules && rm -rf packages/@linode/validation/node_modules", diff --git a/packages/manager/package.json b/packages/manager/package.json index 53a6d3f1681..bd7aa7e825d 100644 --- a/packages/manager/package.json +++ b/packages/manager/package.json @@ -20,6 +20,7 @@ "@linode/api-v4": "*", "@linode/design-language-system": "^2.6.1", "@linode/search": "*", + "@linode/ui": "*", "@linode/validation": "*", "@lukemorales/query-key-factory": "^1.3.4", "@mui/icons-material": "^5.14.7", diff --git a/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx b/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx index 73c476bdb9d..b6f614a24d4 100644 --- a/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx +++ b/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx @@ -1,3 +1,4 @@ +import { BetaChip } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { Link, useLocation } from 'react-router-dom'; @@ -22,7 +23,6 @@ import VPC from 'src/assets/icons/entityIcons/vpc.svg'; import TooltipIcon from 'src/assets/icons/get_help.svg'; import Longview from 'src/assets/icons/longview.svg'; import AkamaiLogo from 'src/assets/logo/akamai-logo.svg'; -import { BetaChip } from 'src/components/BetaChip/BetaChip'; import { Box } from 'src/components/Box'; import { Divider } from 'src/components/Divider'; import { useIsACLPEnabled } from 'src/features/CloudPulse/Utils/utils'; diff --git a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx index 3fbae61e3b9..ab8868f93f8 100644 --- a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx +++ b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx @@ -1,3 +1,4 @@ +import { BetaChip } from '@linode/ui'; import { createDatabaseSchema } from '@linode/validation/lib/databases.schema'; import Grid from '@mui/material/Unstable_Grid2'; import { createLazyRoute } from '@tanstack/react-router'; @@ -10,7 +11,6 @@ import { makeStyles } from 'tss-react/mui'; import MongoDBIcon from 'src/assets/icons/mongodb.svg'; import MySQLIcon from 'src/assets/icons/mysql.svg'; import PostgreSQLIcon from 'src/assets/icons/postgresql.svg'; -import { BetaChip } from 'src/components/BetaChip/BetaChip'; import { Button } from 'src/components/Button/Button'; import { CircleProgress } from 'src/components/CircleProgress'; import { Divider } from 'src/components/Divider'; diff --git a/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLogo.tsx b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLogo.tsx index 71c7dd23b1e..78d6e2a9abd 100644 --- a/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLogo.tsx +++ b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLogo.tsx @@ -1,9 +1,9 @@ +import { BetaChip } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import * as React from 'react'; import LogoWhite from 'src/assets/icons/db-logo-white.svg'; import Logo from 'src/assets/icons/db-logo.svg'; -import { BetaChip } from 'src/components/BetaChip/BetaChip'; import { Box } from 'src/components/Box'; import { Typography } from 'src/components/Typography'; diff --git a/packages/ui/.changeset/README.md b/packages/ui/.changeset/README.md new file mode 100644 index 00000000000..d96182a25b0 --- /dev/null +++ b/packages/ui/.changeset/README.md @@ -0,0 +1,18 @@ +# Changesets + +This directory gets auto-populated when running `yarn changeset`. +You can however add your changesets manually as well, knowing that the [TYPE] is limited to the following options `Added`, `Fixed`, `Changed`, `Removed`, `Tech Stories`, `Tests`, `Upcoming Features` and follow this format: + +```md +--- +"@linode/[PACKAGE]": [TYPE] +--- + +My PR Description ([#`PR number`](`PR link`)) +``` + +You must commit them to the repo so they can be picked up for the changelog generation. + +This directory get wiped out when running `yarn generate-changelog`. + +See `changeset.mjs` for implementation details. diff --git a/packages/ui/.changeset/pr-11057-added-1728496448213.md b/packages/ui/.changeset/pr-11057-added-1728496448213.md new file mode 100644 index 00000000000..a3d689c48ab --- /dev/null +++ b/packages/ui/.changeset/pr-11057-added-1728496448213.md @@ -0,0 +1,5 @@ +--- +"@linode/ui": Added +--- + +new `@linode/ui` package with `BetaChip` as the first component ([#11057](https://github.com/linode/manager/pull/11057)) diff --git a/packages/ui/.eslintrc.json b/packages/ui/.eslintrc.json new file mode 100644 index 00000000000..3388926d8ea --- /dev/null +++ b/packages/ui/.eslintrc.json @@ -0,0 +1,75 @@ +{ + "ignorePatterns": [ + "node_modules", + "lib", + "index.js", + "!.eslintrc.js" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 2020, + "warnOnUnsupportedTypeScriptVersion": true + }, + "plugins": [ + "@typescript-eslint", + "sonarjs", + "prettier", + "@linode/eslint-plugin-cloud-manager" + ], + "extends": [ + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "plugin:sonarjs/recommended", + "plugin:prettier/recommended" + ], + "rules": { + "no-unused-vars": [ + "warn", + { + "argsIgnorePattern": "^_" + } + ], + "no-unused-expressions": "warn", + "no-bitwise": "error", + "no-caller": "error", + "no-eval": "error", + "no-throw-literal": "warn", + "no-loop-func": "error", + "no-await-in-loop": "error", + "array-callback-return": "error", + "no-invalid-this": "off", + "no-new-wrappers": "error", + "no-restricted-imports": [ + "error", + "rxjs" + ], + "no-console": "error", + "no-undef-init": "off", + "radix": "error", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-inferrable-types": "off", + "@typescript-eslint/no-namespace": "warn", + "@typescript-eslint/camelcase": "off", + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-empty-interface": "warn", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-use-before-define": "off", + "@typescript-eslint/interface-name-prefix": "off", + "sonarjs/cognitive-complexity": "warn", + "sonarjs/no-duplicate-string": "warn", + "sonarjs/prefer-immediate-return": "warn", + "sonarjs/no-identical-functions": "warn", + "sonarjs/no-redundant-jump": "warn", + "sonarjs/no-small-switch": "warn", + "no-multiple-empty-lines": "error", + "curly": "warn", + "sort-keys": "off", + "comma-dangle": "off", + "no-trailing-spaces": "warn", + "no-mixed-requires": "warn", + "spaced-comment": "warn", + "object-shorthand": "warn", + "prettier/prettier": "warn" + } +} \ No newline at end of file diff --git a/packages/ui/.prettierrc b/packages/ui/.prettierrc new file mode 100644 index 00000000000..c563e850dad --- /dev/null +++ b/packages/ui/.prettierrc @@ -0,0 +1,4 @@ +{ + "printWidth": 80, + "singleQuote": true +} diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/ui/package.json b/packages/ui/package.json new file mode 100644 index 00000000000..ee91f171d29 --- /dev/null +++ b/packages/ui/package.json @@ -0,0 +1,61 @@ +{ + "name": "@linode/ui", + "author": "Linode", + "description": "Linode UI component library", + "version": "0.0.1", + "type": "module", + "main": "src/index.ts", + "module": "src/index.ts", + "types": "src/index.ts", + "exports": { + ".": "./src/index.ts" + }, + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/linode/manager/tree/develop/packages/ui" + }, + "dependencies": { + "@emotion/react": "^11.11.1", + "@emotion/styled": "^11.11.0", + "@mui/material": "^5.14.7", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "scripts": { + "start": "tsc -w --preserveWatchOutput", + "lint": "eslint . --quiet --ext .js,.ts,.tsx", + "typecheck": "tsc", + "precommit": "lint-staged", + "test": "vitest run", + "test:watch": "vitest", + "test:debug": "node --inspect-brk scripts/test.js --runInBand", + "coverage": "vitest run --coverage && open coverage/index.html", + "coverage:summary": "vitest run --coverage.enabled --reporter=junit --coverage.reporter=json-summary" + }, + "lint-staged": { + "*.{ts,tsx,js}": [ + "prettier --write", + "eslint --ext .js,.ts,.tsx --quiet" + ] + }, + "devDependencies": { + "@linode/eslint-plugin-cloud-manager": "^0.0.5", + "@testing-library/dom": "^10.1.0", + "@testing-library/jest-dom": "~6.4.2", + "@testing-library/react": "~16.0.0", + "@types/node": "^12.7.1", + "@types/react": "^18.2.55", + "@types/react-dom": "^18.2.18", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "@vitest/ui": "^2.1.1", + "eslint": "^7.1.0", + "eslint-config-prettier": "~8.1.0", + "eslint-plugin-prettier": "~3.3.1", + "eslint-plugin-sonarjs": "^0.5.0", + "lint-staged": "^15.2.9", + "prettier": "~2.2.1", + "vitest": "^2.1.1" + } +} \ No newline at end of file diff --git a/packages/ui/src/components/BetaChip/BetaChip.test.tsx b/packages/ui/src/components/BetaChip/BetaChip.test.tsx new file mode 100644 index 00000000000..c4da709edd5 --- /dev/null +++ b/packages/ui/src/components/BetaChip/BetaChip.test.tsx @@ -0,0 +1,33 @@ +import { fireEvent, render } from '@testing-library/react'; +import React from 'react'; + +import { BetaChip } from './BetaChip'; + +import { expect, vi, describe, it } from 'vitest'; +import '@testing-library/jest-dom/vitest'; + +describe('BetaChip', () => { + it('renders with default color', () => { + const { getByTestId } = render(); + const betaChip = getByTestId('betaChip'); + expect(betaChip).toBeInTheDocument(); + expect(betaChip).toHaveStyle('background-color: rgba(0, 0, 0, 0.08)'); + }); + + it('renders with primary color', () => { + const { getByTestId } = render(); + const betaChip = getByTestId('betaChip'); + expect(betaChip).toBeInTheDocument(); + expect(betaChip).toHaveStyle('background-color: rgb(25, 118, 210)'); + }); + + it('triggers an onClick callback', () => { + const onClickMock = vi.fn(); + const { getByTestId } = render( + + ); + const betaChip = getByTestId('betaChip'); + fireEvent.click(betaChip); + expect(onClickMock).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/ui/src/components/BetaChip/BetaChip.tsx b/packages/ui/src/components/BetaChip/BetaChip.tsx new file mode 100644 index 00000000000..981003043fa --- /dev/null +++ b/packages/ui/src/components/BetaChip/BetaChip.tsx @@ -0,0 +1,62 @@ +import { styled } from '@mui/material/styles'; +import * as React from 'react'; +import { Chip } from '../Chip'; +import type { ChipProps } from '@mui/material'; + +export interface BetaChipProps + extends Omit< + ChipProps, + | 'avatar' + | 'clickable' + | 'deleteIcon' + | 'disabled' + | 'icon' + | 'label' + | 'onDelete' + | 'outlineColor' + | 'size' + | 'variant' + > { + /** + * The color of the chip. + * default renders a gray chip, primary renders a blue chip. + */ + color?: 'default' | 'primary'; +} + +/** + * ## Usage + * + * Beta chips label features that are not yet part of Cloud Manager's core supported functionality.
+ * **Example:** A beta chip may appear in the [primary navigation](https://github.com/linode/manager/pull/8104#issuecomment-1309334374), + * breadcrumbs, [banners](/docs/components-notifications-dismissible-banners--beta-banners), tabs, and/or plain text to designate beta functionality.
+ * **Visual style:** bold, capitalized text; reduced height, letter spacing, and font size; solid color background. + * + */ +export const BetaChip = (props: BetaChipProps) => { + const { color } = props; + + return ( + + ); +}; + +const StyledBetaChip = styled(Chip, { + label: 'StyledBetaChip', +})(({ theme }) => ({ + '& .MuiChip-label': { + padding: 0, + }, + fontSize: '0.625rem', + fontFamily: '"LatoWebBold", sans-serif', // TODO: remove hardcoded font once theme is added to this package + height: 16, + letterSpacing: '.25px', + marginLeft: theme.spacing(0.5), + padding: theme.spacing(0.5), + textTransform: 'uppercase', +})); diff --git a/packages/ui/src/components/BetaChip/index.ts b/packages/ui/src/components/BetaChip/index.ts new file mode 100644 index 00000000000..3badebf5d28 --- /dev/null +++ b/packages/ui/src/components/BetaChip/index.ts @@ -0,0 +1 @@ +export * from './BetaChip'; diff --git a/packages/ui/src/components/Chip.tsx b/packages/ui/src/components/Chip.tsx new file mode 100644 index 00000000000..882b98a26f7 --- /dev/null +++ b/packages/ui/src/components/Chip.tsx @@ -0,0 +1,8 @@ +import { default as _Chip } from '@mui/material/Chip'; +import * as React from 'react'; + +import type { ChipProps } from '@mui/material/Chip'; + +export const Chip = (props: ChipProps) => { + return <_Chip {...props} />; +}; diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts new file mode 100644 index 00000000000..ea92b0846b8 --- /dev/null +++ b/packages/ui/src/components/index.ts @@ -0,0 +1,2 @@ +export * from './Chip'; +export * from './BetaChip'; diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts new file mode 100644 index 00000000000..07635cbbc8e --- /dev/null +++ b/packages/ui/src/index.ts @@ -0,0 +1 @@ +export * from './components'; diff --git a/packages/ui/testSetup.ts b/packages/ui/testSetup.ts new file mode 100644 index 00000000000..e42051f8ee3 --- /dev/null +++ b/packages/ui/testSetup.ts @@ -0,0 +1,6 @@ +import { cleanup } from '@testing-library/react'; +import { afterEach } from 'vitest'; + +afterEach(() => { + cleanup(); +}); diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json new file mode 100644 index 00000000000..a9b006a91d5 --- /dev/null +++ b/packages/ui/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "jsx": "react", + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "skipLibCheck": true, + "noEmit": true, + "strict": true, + "noUnusedLocals": true, + "forceConsistentCasingInFileNames": true, + "incremental": true + }, + "include": ["src"], +} diff --git a/packages/ui/vitest.config.ts b/packages/ui/vitest.config.ts new file mode 100644 index 00000000000..95754d431b5 --- /dev/null +++ b/packages/ui/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + environment: 'jsdom', + setupFiles: './testSetup.ts', + }, +}); diff --git a/scripts/changelog/utils/constants.mjs b/scripts/changelog/utils/constants.mjs index a2a017cf3b3..c82be817eef 100644 --- a/scripts/changelog/utils/constants.mjs +++ b/scripts/changelog/utils/constants.mjs @@ -6,7 +6,7 @@ const __dirname = path.dirname(__filename); export const BOT = 'linode-gh-bot'; -export const PACKAGES = ["api-v4", "manager", "validation"]; +export const PACKAGES = ["api-v4", "manager", "validation", "ui"]; export const CHANGESET_TYPES = [ "Added", "Fixed", diff --git a/scripts/package-versions/index.js b/scripts/package-versions/index.js index 6272dea5b90..f03f9654e79 100644 --- a/scripts/package-versions/index.js +++ b/scripts/package-versions/index.js @@ -9,6 +9,7 @@ * - `` (Optional) Desired Cloud Manager package version. * - `` (Optional) Desired APIv4 package version. * - `` (Optional) Desired Validation package version. + * - `` (Optional) Desired UI package version. * * Optional Flags: * - `-f | --force` Forces the script to update package versions without @@ -99,6 +100,7 @@ const [ desiredManagerVersion, desiredApiVersion, desiredValidationVersion, + desiredUiVersion ] = desiredVersions; // Describes packages that should be modified by this script. @@ -106,6 +108,7 @@ const jobs = [ { name: 'manager', path: getPackagePath('manager'), desiredVersion: desiredManagerVersion }, { name: 'api-v4', path: getPackagePath('api-v4'), desiredVersion: desiredApiVersion }, { name: 'validation', path: getPackagePath('validation'), desiredVersion: desiredValidationVersion }, + { name: 'ui', path: getPackagePath('ui'), desiredVersion: desiredUiVersion }, ]; // Describes the files that will be written to, and the changes that will be made.