-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WNMGDS-3009][WNMGDS-3010] Add Angular example project and fix Angula…
…r web component content issue (#3275) * Add a basic Angular example that imports the design system styles * Configure it to allow web components and add a couple simple ones * Copy and paste the other wc examples without the scripts * Update version after patch * Add some debug logging * Strip the example down to just a few components These components show the current issue, which is that our custom elements don't have any initial `innerHTML`, which causes various symptoms. For the month picker, we don't get the default checked state or the disabled months. For the alert content, we don't get any of the alert content, but we get the header because it was defined with an attribute. * I think this fixes it! When Angular renders the web component at first, it doesn't have any innerHTML, but then it inserts the content later and comes in under the watchful eye of our MutationObserver. This might be a bad assumption, but the code I've added just now assumes the same kind of thing that we assumed before that if we're getting mutations to the root content, it means that we want to replace the previous content and re-render everything. Instead of the mutation kicking off the `renderPreactComponent` function and trying to read the `innerHTML` that isn't really settled with the new content, it will take the new content from the mutations and use that instead as the new content to render. For this to work, we're not taking into account previously defined template elements. I have to think about this a bit more, but I think after our changes to make the web components more stateful, we don't actually need to keep the old template around...that could be wrong. I need to think about it more. * Get rid of debug logging * A unit test in `define.test.tsx` caught an issue It was the `correctly caches children when moved in the DOM` test, and it caught that when a component was moved in the DOM it ended up with nested templates. The fix for that is to bring back the old logic of only creating a template if one doesn't already exist WHEN we're not dealing with inner content mutations. * Add some web component examples back in * Revert "A unit test in `define.test.tsx` caught an issue" This reverts commit 32237c1. Apparently we can't both make this unit test happy and have working Angular examples. I'm going to have to think about this scenario some more. If I bring back the code that will look for previous templates when `addedNodes` doesn't exist, nested web components don't work right in the Angular examples. Try checking out 32237c1 and running the `examples/angular` project. * Fix c578c7c (see note) I think next I'll try actually removing the template if it is empty so even the empty template doesn't get duplicated inside the new template * This is a better version of the previous fix where we not only create a new template if the previous one was empty, but we remove the empty template before creating the new one so that it doesn't make its way into the content of the new one. Before this fix, the Angular-rendered alert example with a nested button would end up looking like this in the DOM: ```html <ds-button> <button type="button" class="ds-c-button ds-c-button--alternate ds-c-button--solid" > <template><span></span></template> Break things </button> <template> <span> <template><span></span></template> Break things </span> </template> </ds-button> ``` The above HTML has an empty content inside the actual button content, and the top-level template for the `ds-button` element has a another template inside of it! After this fix, it looks as we expect it: ```html <ds-button> <button type="button" class="ds-c-button ds-c-button--alternate ds-c-button--solid" > Break things </button> <template><span>Break things</span></template> </ds-button> ``` * Edited the comment to try to make it clearer * Update our nvm node version for testing * Fix bad ds version after bump * Move the template content into a separate HTML file—thanks, Michael! * Update lockfile (automatically removing bad entries) * An ugly solution: polling for changes to the template The way I got to this solution is that I observed that Angular was actually making changes to the web component's `template` element contents whenever Angular was re-rendering (probably because of [this line](https://github.com/CMSgov/design-system/blob/pwolfert/angular-example/packages/design-system/src/components/web-components/preactement/define.ts#L393)). The problem is that I can't use a MutationObserver on template content because it's only a document fragment and not part of the DOM. Polling for changes works, but I don't think this is a production-level solution. * Remove the polling part of my last commit I just wanted to get that code in version control, but I don't think this is the long-term solution * Polling now working in the Angular wrapper instead of the design system Ideally if we have to have an Angular wrapper, we could detect changes made by Angular and trigger a re-render, but nothing has seemed to work. This is the only thing so far that has worked. * Looks like custom event binding works even without the wrapper * Remove experimental solutions for ticket 3041 * Add new comment and remove old one * Add a README for the project * Add some more unit tests around this functionality * Using `flatMap` is more succinct and readible here * Get rid of debug logs
Showing
16 changed files
with
4,341 additions
and
123 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
18.19.0 | ||
18.20.4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.angular | ||
dist | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
## Example: An Angular project with TypeScript | ||
|
||
This shows the usage of CMS design system components in a TypeScript-enabled [Angular](https://angular.dev/) project. | ||
|
||
In this project, the design system package is installed as an npm dependency. Refer to [install using npm](https://design.cms.gov/getting-started/for-developers/#option-1-install-using-npm) for instructions on how to install the design system as a dependency package. | ||
|
||
## Getting started | ||
|
||
1. Install packages at the root of this repository (see [root README](../../README.md)) | ||
1. Start the application: `yarn start` | ||
|
||
_Note: Whenever changes have been made to the design system packages, you must **clear the local cache** for this application to receive those changes by deleting the hidden `examples/angular/.angular` directory._ | ||
|
||
## 🧞 Commands | ||
|
||
All commands are run from the root of the project, from a terminal: | ||
|
||
| Command | Action | | ||
| :----------- | :------------------------------------------ | | ||
| `yarn start` | Starts local dev server at `localhost:4200` | | ||
| `yarn build` | Build your production site to `./dist/` | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
{ | ||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json", | ||
"cli": { | ||
"analytics": "1e1de97b-a744-405a-8b5a-0397bb3d01ce" | ||
}, | ||
"newProjectRoot": "projects", | ||
"projects": { | ||
"demo": { | ||
"architect": { | ||
"build": { | ||
"builder": "@angular-devkit/build-angular:application", | ||
"configurations": { | ||
"development": { | ||
"extractLicenses": false, | ||
"namedChunks": true, | ||
"optimization": false, | ||
"sourceMap": true | ||
}, | ||
"production": { | ||
"aot": true, | ||
"extractLicenses": true, | ||
"namedChunks": false, | ||
"optimization": true, | ||
"outputHashing": "all", | ||
"sourceMap": false | ||
} | ||
}, | ||
"options": { | ||
"assets": [], | ||
"index": "src/index.html", | ||
"browser": "src/main.ts", | ||
"outputPath": "dist/demo", | ||
"polyfills": ["zone.js"], | ||
"scripts": [], | ||
"styles": ["src/global_styles.css"], | ||
"tsConfig": "tsconfig.app.json" | ||
} | ||
}, | ||
"serve": { | ||
"builder": "@angular-devkit/build-angular:dev-server", | ||
"configurations": { | ||
"development": { | ||
"buildTarget": "demo:build:development" | ||
}, | ||
"production": { | ||
"buildTarget": "demo:build:production" | ||
} | ||
}, | ||
"defaultConfiguration": "development" | ||
} | ||
}, | ||
"prefix": "app", | ||
"projectType": "application", | ||
"root": "", | ||
"schematics": {}, | ||
"sourceRoot": "src" | ||
} | ||
}, | ||
"version": 1 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
{ | ||
"name": "angular-example", | ||
"private": true, | ||
"version": "11.1.0", | ||
"scripts": { | ||
"ng": "ng", | ||
"start": "ng serve", | ||
"build": "ng build" | ||
}, | ||
"dependencies": { | ||
"@angular/animations": "^18.1.0", | ||
"@angular/common": "^18.1.0", | ||
"@angular/compiler": "^18.1.0", | ||
"@angular/core": "^18.1.0", | ||
"@angular/forms": "^18.1.0", | ||
"@angular/platform-browser": "^18.1.0", | ||
"@angular/router": "^18.1.0", | ||
"@cmsgov/design-system": "11.1.0", | ||
"rxjs": "^7.8.1", | ||
"tslib": "^2.5.0", | ||
"zone.js": "~0.14.0" | ||
}, | ||
"devDependencies": { | ||
"@angular-devkit/build-angular": "^18.1.0", | ||
"@angular/cli": "^18.1.0", | ||
"@angular/compiler-cli": "^18.1.0", | ||
"typescript": "~5.5.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
@import '@cmsgov/design-system/css/index.css'; | ||
@import '@cmsgov/design-system/css/core-theme.css'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<title>My app</title> | ||
<meta charset="UTF-8" /> | ||
<base href="/" /> | ||
</head> | ||
<body> | ||
<app-root>Loading...</app-root> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
<div class="ds-content"> | ||
<ds-skip-nav href="#main"></ds-skip-nav> | ||
<ds-usa-banner></ds-usa-banner> | ||
<main id="main" class="ds-l-container"> | ||
<h1 class="ds-text-heading--4xl ds-u-margin-top--2">Web-component examples</h1> | ||
|
||
<div class="ds-u-measure--base ds-u-padding-bottom--4"> | ||
<p>This is an example showing how to use our library of interactive web components.</p> | ||
|
||
<ds-alert | ||
heading="{{alertHeading}}" | ||
[attr.variation]="alertVariation ?? null" | ||
class="ds-u-margin-y--2" | ||
#myAlert | ||
> | ||
In accordance with HIPAA, this application does not store any data. All data is stored | ||
locally on your computer and is not transmitted to any external servers. The data you enter | ||
into this application is not stored or shared with anyone. | ||
</ds-alert> | ||
|
||
<ds-button (click)="onButtonClick()" variation="solid" *ngIf="!hideButton" | ||
>Click here</ds-button | ||
> | ||
|
||
<ds-accordion bordered="true" class="ds-u-margin-top--2"> | ||
<ds-accordion-item heading="First Amendment" default-open="true"> | ||
<p> | ||
We the People of the United States, in Order to form a more perfect Union, establish | ||
Justice, insure domestic Tranquility, provide for the common defence, promote the | ||
general Welfare, and secure the Blessings of Liberty to ourselves and our Posterity, do | ||
ordain and establish this Constitution for the United States of America. | ||
</p> | ||
</ds-accordion-item> | ||
<ds-accordion-item heading="Second Amendment"> | ||
<p> | ||
A well regulated Militia, being necessary to the security of a free State, the right of | ||
the people to keep and bear Arms, shall not be infringed. | ||
</p> | ||
</ds-accordion-item> | ||
</ds-accordion> | ||
|
||
<ds-month-picker | ||
requirement-label="Required." | ||
hint="Select many." | ||
name="fooPicker" | ||
label="Select any months you want!" | ||
> | ||
<input type="checkbox" value="10" checked /> | ||
<input type="checkbox" value="11" checked disabled /> | ||
<input type="checkbox" value="12" disabled /> | ||
</ds-month-picker> | ||
|
||
<ds-spinner aria-valuetext="aria sets spinner label!" | ||
>children don't set spinner label</ds-spinner | ||
> | ||
|
||
<ds-choice type="checkbox" label="I agree to the terms and conditions" name="agree"> | ||
<div slot="checked-children"> | ||
<ds-alert class="ds-u-margin-top--1">Cool, we hoped you'd check this box.</ds-alert> | ||
</div> | ||
</ds-choice> | ||
|
||
<ds-choice-list type="radio" label="Choice list example" name="foo"> | ||
<ds-choice label="Choice without associated children" value="no children" /> | ||
<ds-choice | ||
label="Checked children" | ||
hint="Selecting this checkbox will reveal its associated children." | ||
value="checked children" | ||
> | ||
<div slot="checked-children"> | ||
<div class="ds-c-choice__checkedChild"> | ||
<ds-alert heading="🫣 Tag! You're it!"> | ||
You can reveal content by applying <code>checked-children</code> to the | ||
<code>slot</code> attribute of an HTML element. Do not forget to include a | ||
<code>div</code> element with the class <code>ds-c-choice__checkedChild</code> to | ||
whatever content you want to show/hide so it gets side border showing an association | ||
with its choice parent. | ||
</ds-alert> | ||
</div> | ||
</div> | ||
</ds-choice> | ||
<ds-choice | ||
label="Unchecked children" | ||
hint="Selecting this checkbox will hide its associated children." | ||
value="unchecked children" | ||
> | ||
<div slot="unchecked-children"> | ||
<div class="ds-c-choice__checkedChild"> | ||
<ds-alert variation="warn" heading="I banish thee!"> | ||
You can hide content by applying <code>unchecked-children</code> to the | ||
<code>slot</code> attribute of an HTML element. Do not forget to include a | ||
<code>div</code> element with the class <code>ds-c-choice__checkedChild</code> to | ||
whatever content you want to show/hide so it gets side border showing an association | ||
with its choice parent. | ||
</ds-alert> | ||
</div> | ||
</div> | ||
</ds-choice> | ||
</ds-choice-list> | ||
</div> | ||
</main> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { NgIf } from '@angular/common'; | ||
import { Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, ViewChild } from '@angular/core'; | ||
import { bootstrapApplication } from '@angular/platform-browser'; | ||
import '@cmsgov/design-system/web-components'; // TODO: Move to project.demo.architect.build.options.scripts | ||
|
||
@Component({ | ||
selector: 'app-root', | ||
standalone: true, | ||
schemas: [CUSTOM_ELEMENTS_SCHEMA], | ||
templateUrl: './main.html', | ||
imports: [NgIf], | ||
}) | ||
export class App { | ||
name = 'Angular'; | ||
|
||
alertVariation: string | null = null; | ||
alertHeading = 'Confidentiality and medical data sharing'; | ||
hideButton = false; | ||
@ViewChild('myAlert') myAlert!: ElementRef; | ||
|
||
onButtonClick() { | ||
this.hideButton = true; | ||
this.alertVariation = 'success'; | ||
this.alertHeading = 'You did it!'; | ||
// Temporary workaround for components not responding to dynamic content changes made | ||
// by Angular templates | ||
this.myAlert.nativeElement.innerHTML = 'You successfully clicked a button.'; | ||
} | ||
} | ||
|
||
bootstrapApplication(App); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/* To learn more about this file see: https://angular.io/config/tsconfig. */ | ||
{ | ||
"extends": "./tsconfig.json", | ||
"compilerOptions": { | ||
"outDir": "./out-tsc/app", | ||
"types": [] | ||
}, | ||
"files": ["src/main.ts"], | ||
"include": ["src/**/*.d.ts"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* To learn more about this file see: https://angular.io/config/tsconfig. */ | ||
{ | ||
"compileOnSave": false, | ||
"compilerOptions": { | ||
"outDir": "./dist/out-tsc", | ||
"forceConsistentCasingInFileNames": true, | ||
"strict": true, | ||
"noImplicitOverride": true, | ||
"noPropertyAccessFromIndexSignature": true, | ||
"noImplicitReturns": true, | ||
"noFallthroughCasesInSwitch": true, | ||
"esModuleInterop": true, | ||
"sourceMap": true, | ||
"declaration": false, | ||
"downlevelIteration": true, | ||
"experimentalDecorators": true, | ||
"moduleResolution": "node", | ||
"importHelpers": true, | ||
"target": "ES2022", | ||
"module": "ES2022", | ||
"useDefineForClassFields": false, | ||
"lib": ["ES2022", "dom"] | ||
}, | ||
"angularCompilerOptions": { | ||
"enableI18nLegacyMessageIdFormat": false, | ||
"strictInjectionParameters": true, | ||
"strictInputAccessModifiers": true, | ||
"strictTemplates": true | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
...esign-system/src/components/web-components/preactement/__snapshots__/define.test.tsx.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`define() when run in the browser renders child HTML 1`] = ` | ||
<description-pair | ||
term="supercalifragilisticexpialidocious" | ||
> | ||
<dt> | ||
supercalifragilisticexpialidocious | ||
</dt> | ||
<dd> | ||
Something to say when the | ||
<a | ||
href="#cat" | ||
> | ||
cat's got your tongue | ||
</a> | ||
</dd> | ||
<template /> | ||
</description-pair> | ||
`; |
Oops, something went wrong.