зеркало из https://github.com/microsoft/fast.git
Remove @microsoft/fast-foundation and @microsoft/fast-web-utilities (#6996)
# Pull Request ## 📖 Description This change removes: - `@microsoft/fast-foundation` - the most recent version can be found on branch `archives/fast-foundation-3` - `@microsoft/fast-web-utilities` ### 🎫 Issues Closes #6951 and closes #6954 ## ✅ Checklist ### General <!--- Review the list and put an x in the boxes that apply. --> - [ ] I have included a change request file using `$ yarn change` - [ ] I have added tests for my changes. - [x] I have tested my changes. - [ ] I have updated the project documentation to reflect my changes. - [x] I have read the [CONTRIBUTING](https://github.com/microsoft/fast/blob/master/CONTRIBUTING.md) documentation and followed the [standards](https://github.com/microsoft/fast/blob/master/CODE_OF_CONDUCT.md#our-standards) for this project. ## ⏭ Next Steps - File an issue to remove dependency on `@microsoft/fast-foundation` in `@microsoft/fast-ssr` for testing.
This commit is contained in:
Родитель
79a5c0e765
Коммит
32446b8516
|
@ -28,12 +28,8 @@ build/ @janechu @nicholasrice @chrisdholt @awentzel
|
|||
|
||||
# Package specific owners
|
||||
|
||||
# Utilities
|
||||
/packages/utilities/fast-web-utilities/ @janechu @chrisdholt @nicholasrice
|
||||
|
||||
# Web components
|
||||
/packages/web-components/fast-element/ @chrisdholt @janechu @nicholasrice
|
||||
/packages/web-components/fast-foundation/ @chrisdholt @bheston @scomea @radium-v @kingoftac
|
||||
|
||||
# the change directory has no owners
|
||||
/change/
|
||||
|
|
|
@ -96,8 +96,8 @@ When working across feature branches, you'll need to target the branch using the
|
|||
```json
|
||||
{
|
||||
"type": "minor",
|
||||
"comment": "add fancy new feature for foundation",
|
||||
"packageName": "@microsoft/fast-foundation",
|
||||
"comment": "add fancy new feature to FASTElement",
|
||||
"packageName": "@microsoft/fast-element",
|
||||
"email": "name@example.com",
|
||||
"dependentChangeType": "minor",
|
||||
"date": "2021-03-01T19:10:06.323Z"
|
||||
|
@ -122,7 +122,7 @@ If you are merging a pull request, be sure to use the pull request title as the
|
|||
|
||||
### Documenting breaking changes
|
||||
|
||||
Make sure to document the migration strategy in a `MIGRATION.md` file in the package(s) that has breaking changes, eg. `packages/web-components/fast-foundation/MIGRATION.md`.
|
||||
Make sure to document the migration strategy in a `MIGRATION.md` file in the package(s) that has breaking changes, eg. `packages/web-components/fast-element/MIGRATION.md`.
|
||||
|
||||
Example of how to format `MIGRATION.md`:
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
"private": true,
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
"packages/utilities/*",
|
||||
"packages/web-components/*",
|
||||
"sites/*",
|
||||
"examples/todo-app",
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
# don't ever lint node_modules
|
||||
node_modules
|
||||
# don't lint build output (make sure it's set to your correct build folder name)
|
||||
dist
|
||||
# don't lint coverage output
|
||||
coverage
|
||||
# Don't lint test files
|
||||
**/*.spec.ts
|
||||
|
||||
# Ignore karma config
|
||||
karma.conf.ts
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"extends": ["../../../.eslintrc.js"],
|
||||
"rules": {
|
||||
"import/extensions": [
|
||||
"error",
|
||||
"always"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"colors": true,
|
||||
"recursive": true,
|
||||
"timeout": 5000,
|
||||
"require": [
|
||||
"esm",
|
||||
"jsdom-global/register"
|
||||
]
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
# Tests
|
||||
__test__/
|
||||
*.spec.*
|
||||
*.test.*
|
||||
|
||||
# Source files
|
||||
coverage/
|
||||
src/
|
||||
|
||||
# config
|
||||
babel.config.js
|
||||
tsconfig.json
|
|
@ -1 +0,0 @@
|
|||
package-lock=false
|
|
@ -1,3 +0,0 @@
|
|||
coverage/*
|
||||
dist/*
|
||||
www/*
|
|
@ -1,358 +0,0 @@
|
|||
{
|
||||
"name": "@microsoft/fast-web-utilities",
|
||||
"entries": [
|
||||
{
|
||||
"date": "Thu, 20 Jun 2024 17:00:57 GMT",
|
||||
"version": "6.0.0",
|
||||
"tag": "@microsoft/fast-web-utilities_v6.0.0",
|
||||
"comments": {
|
||||
"none": [
|
||||
{
|
||||
"author": "7559015+janechu@users.noreply.github.com",
|
||||
"package": "@microsoft/fast-web-utilities",
|
||||
"commit": "4b23b5caa76c172d3ea6c6559699d632a278442c",
|
||||
"comment": "Convert karma configuration to cjs"
|
||||
},
|
||||
{
|
||||
"author": "7559015+janechu@users.noreply.github.com",
|
||||
"package": "@microsoft/fast-web-utilities",
|
||||
"commit": "00bccd7812ca072fc2efdf39af7762bbfdc90846",
|
||||
"comment": "Remove eslint config package"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Wed, 20 Dec 2023 19:03:47 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v6.0.0",
|
||||
"version": "6.0.0",
|
||||
"comments": {
|
||||
"none": [
|
||||
{
|
||||
"author": "863023+radium-v@users.noreply.github.com",
|
||||
"package": "@microsoft/fast-web-utilities",
|
||||
"commit": "fd65254531ac3a593b32528d81b44ebf82c8e4ab",
|
||||
"comment": "fix RtlScrollConverter test"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 16 Jun 2023 18:17:13 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v6.0.0",
|
||||
"version": "6.0.0",
|
||||
"comments": {
|
||||
"none": [
|
||||
{
|
||||
"author": "chhol@microsoft.com",
|
||||
"package": "@microsoft/fast-web-utilities",
|
||||
"commit": "ca0e62ee8d05f72d1d8c1ad66bd6eea8e3f0a4eb",
|
||||
"comment": "update prettier and eslint-config-prettier versions"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Sat, 11 Mar 2023 00:09:48 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v6.0.0",
|
||||
"version": "6.0.0",
|
||||
"comments": {
|
||||
"none": [
|
||||
{
|
||||
"author": "nonusethi1272@gmail.com",
|
||||
"package": "@microsoft/fast-web-utilities",
|
||||
"commit": "2e47102bbf16415a4d2ec27bfbf27f619dd649a8",
|
||||
"comment": "Turned on strictNullChecks, strictPropertyInitialization for fast-web-utilities"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Thu, 06 Oct 2022 23:21:20 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v6.0.0",
|
||||
"version": "6.0.0",
|
||||
"comments": {
|
||||
"none": [
|
||||
{
|
||||
"author": "863023+radium-v@users.noreply.github.com",
|
||||
"package": "@microsoft/fast-web-utilities",
|
||||
"commit": "c06e32d72d518bf5c3152efe4e666f233190b445",
|
||||
"comment": "upgrade karma to ^6.4.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Thu, 18 Aug 2022 20:46:10 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v6.0.0",
|
||||
"version": "6.0.0",
|
||||
"comments": {
|
||||
"none": [
|
||||
{
|
||||
"author": "nicholasrice@users.noreply.github.com",
|
||||
"package": "@microsoft/fast-web-utilities",
|
||||
"commit": "0b57f1bc812e8e6371b7d27bb625f99a25bfaa66",
|
||||
"comment": "Reverts PR #6253"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Wed, 15 Jun 2022 17:41:10 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v6.0.0",
|
||||
"version": "6.0.0",
|
||||
"comments": {
|
||||
"none": [
|
||||
{
|
||||
"comment": "Updated README for usage of keyboard event keys",
|
||||
"author": "7559015+janechu@users.noreply.github.com",
|
||||
"commit": "a6b2a570c1cb592bc92b4c9d8366d197658819ae",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Wed, 01 Jun 2022 17:53:14 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v6.0.0",
|
||||
"version": "6.0.0",
|
||||
"comments": {
|
||||
"none": [
|
||||
{
|
||||
"comment": "chore: update package.json metadata",
|
||||
"author": "roeisenb@microsoft.com",
|
||||
"commit": "4699e77715068f8610aae908ede6356a249574b6",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
},
|
||||
{
|
||||
"comment": "update api extractor and typescript to use the latest versions",
|
||||
"author": "chhol@microsoft.com",
|
||||
"commit": "4699e77715068f8610aae908ede6356a249574b6",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
},
|
||||
{
|
||||
"comment": "Bump @microsoft/eslint-config-fast-dna to v2.1.0",
|
||||
"author": "roeisenb@microsoft.com",
|
||||
"commit": "4699e77715068f8610aae908ede6356a249574b6",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
}
|
||||
],
|
||||
"patch": [
|
||||
{
|
||||
"comment": "chore: fix broken build",
|
||||
"author": "roeisenb@microsoft.com",
|
||||
"commit": "4699e77715068f8610aae908ede6356a249574b6",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
},
|
||||
{
|
||||
"comment": "Upgrade TypeScript",
|
||||
"author": "nicholasrice@users.noreply.github.com",
|
||||
"commit": "4699e77715068f8610aae908ede6356a249574b6",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
}
|
||||
],
|
||||
"major": [
|
||||
{
|
||||
"comment": "remove deprecated keycodes and add additional keys",
|
||||
"author": "chhol@microsoft.com",
|
||||
"commit": "4699e77715068f8610aae908ede6356a249574b6",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Wed, 04 May 2022 07:14:00 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v5.4.1",
|
||||
"version": "5.4.1",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "convert orientation enum to const object with corresponding type",
|
||||
"author": "chhol@microsoft.com",
|
||||
"commit": "d39284193f6d476b5b40b0fad75d3dbd836d55da",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Wed, 27 Apr 2022 07:21:09 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v5.4.0",
|
||||
"version": "5.4.0",
|
||||
"comments": {
|
||||
"minor": [
|
||||
{
|
||||
"comment": "update to typescript 4.6.2 and update ARIAMixin typings",
|
||||
"author": "chhol@microsoft.com",
|
||||
"commit": "35bdab45550b5d8b8762041110eccb06de78add5",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
}
|
||||
],
|
||||
"patch": [
|
||||
{
|
||||
"comment": "Bump @microsoft/eslint-config-fast-dna to v2.1.0",
|
||||
"author": "chhol@microsoft.com",
|
||||
"commit": "35bdab45550b5d8b8762041110eccb06de78add5",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Sun, 17 Apr 2022 07:11:18 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v5.3.0",
|
||||
"version": "5.3.0",
|
||||
"comments": {
|
||||
"minor": [
|
||||
{
|
||||
"comment": "Instead of matchAll, used regex replace for pascalCase and added number to add to spinalCase",
|
||||
"author": "74849806+wannieman98@users.noreply.github.com",
|
||||
"commit": "14bc5d5f2ae608328eb16ad7e619bab00415f60a",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Sun, 03 Apr 2022 07:12:01 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v5.2.0",
|
||||
"version": "5.2.0",
|
||||
"comments": {
|
||||
"minor": [
|
||||
{
|
||||
"comment": "Declare package as using ES modules",
|
||||
"author": "nicholasrice@users.noreply.github.com",
|
||||
"commit": "f6107c448ab6446667f4d9e86d9f9c11fff075aa",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
}
|
||||
],
|
||||
"patch": [
|
||||
{
|
||||
"comment": "update exenv-es6 to 1.1.0 to ensure we lock to es module support",
|
||||
"author": "chhol@microsoft.com",
|
||||
"commit": "077a14b2e63332384349d2be205b4afa7246b13c",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Tue, 25 Jan 2022 07:11:53 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v5.1.0",
|
||||
"version": "5.1.0",
|
||||
"comments": {
|
||||
"minor": [
|
||||
{
|
||||
"comment": "add findLastIndex and inRange functions to fast-web-utilities",
|
||||
"author": "john.kreitlow@microsoft.com",
|
||||
"commit": "97f653f8ee62c74d47df9b60024ff8eef05c79d1",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Sun, 31 Oct 2021 07:17:45 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v5.0.2",
|
||||
"version": "5.0.2",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "update fast eslint package version",
|
||||
"author": "chhol@microsoft.com",
|
||||
"commit": "a150068ee196e73fe7a4f7b538a38752e0e506ba",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Wed, 13 Oct 2021 01:53:37 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v5.0.1",
|
||||
"version": "5.0.1",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "remove prefix from uniqueId function",
|
||||
"author": "john.kreitlow@microsoft.com",
|
||||
"commit": "d609cffb4657e8447fb65d3b52899f48b8bb87cb",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
},
|
||||
{
|
||||
"comment": "remove throttle function",
|
||||
"author": "john.kreitlow@microsoft.com",
|
||||
"commit": "696d66c1382aebedc9410b83362c4dbda131a638",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
},
|
||||
{
|
||||
"comment": "refactor: remove lodash-es as a dependency",
|
||||
"author": "connor@peet.io",
|
||||
"commit": "4ef4b325f8259dd0f648b5fe1b393ef8839e643e",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Sun, 19 Sep 2021 07:17:17 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v5.0.0",
|
||||
"version": "5.0.0",
|
||||
"comments": {
|
||||
"major": [
|
||||
{
|
||||
"comment": "add picker component",
|
||||
"author": "scomea@microsoft.com",
|
||||
"commit": "395775ca58b6529861aae2e2ab1a628e2d8db081",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Sun, 12 Sep 2021 07:17:43 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v4.8.1",
|
||||
"version": "4.8.1",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "remove dependencies on keycode",
|
||||
"author": "scomea@microsoft.com",
|
||||
"commit": "e8602a247065b0c916bdc8e55314f2d3e3403fd1",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Thu, 20 May 2021 07:24:10 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v4.8.0",
|
||||
"version": "4.8.0",
|
||||
"comments": {
|
||||
"minor": [
|
||||
{
|
||||
"comment": "add key string constants",
|
||||
"author": "john.kreitlow@microsoft.com",
|
||||
"commit": "c7db517ac34ee6f0e5454bbf395711f464b438a0",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 16 Apr 2021 01:19:08 GMT",
|
||||
"tag": "@microsoft/fast-web-utilities_v4.7.3",
|
||||
"version": "4.7.3",
|
||||
"comments": {
|
||||
"none": [
|
||||
{
|
||||
"comment": "chore: convert Jest tests to Karma/Mocha/Chai",
|
||||
"author": "7559015+janechu@users.noreply.github.com",
|
||||
"commit": "832c1086c05099de4f7c9261dd85b7b32605538c",
|
||||
"package": "@microsoft/fast-web-utilities"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,520 +0,0 @@
|
|||
# Change Log - @microsoft/fast-web-utilities
|
||||
|
||||
This log was last generated on Wed, 01 Jun 2022 17:53:14 GMT and should not be manually modified.
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 6.0.0
|
||||
|
||||
Wed, 01 Jun 2022 17:53:14 GMT
|
||||
|
||||
### Major changes
|
||||
|
||||
- remove deprecated keycodes and add additional keys (chhol@microsoft.com)
|
||||
|
||||
### Patches
|
||||
|
||||
- chore: fix broken build (roeisenb@microsoft.com)
|
||||
- Upgrade TypeScript (nicholasrice@users.noreply.github.com)
|
||||
|
||||
## 5.4.1
|
||||
|
||||
Wed, 04 May 2022 07:14:00 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- convert orientation enum to const object with corresponding type (chhol@microsoft.com)
|
||||
|
||||
## 5.4.0
|
||||
|
||||
Wed, 27 Apr 2022 07:21:09 GMT
|
||||
|
||||
### Minor changes
|
||||
|
||||
- update to typescript 4.6.2 and update ARIAMixin typings (chhol@microsoft.com)
|
||||
|
||||
### Patches
|
||||
|
||||
- Bump @microsoft/eslint-config-fast-dna to v2.1.0 (chhol@microsoft.com)
|
||||
|
||||
## 5.3.0
|
||||
|
||||
Sun, 17 Apr 2022 07:11:18 GMT
|
||||
|
||||
### Minor changes
|
||||
|
||||
- Instead of matchAll, used regex replace for pascalCase and added number to add to spinalCase (74849806+wannieman98@users.noreply.github.com)
|
||||
|
||||
## 5.2.0
|
||||
|
||||
Sun, 03 Apr 2022 07:12:01 GMT
|
||||
|
||||
### Minor changes
|
||||
|
||||
- Declare package as using ES modules (nicholasrice@users.noreply.github.com)
|
||||
|
||||
### Patches
|
||||
|
||||
- update exenv-es6 to 1.1.0 to ensure we lock to es module support (chhol@microsoft.com)
|
||||
|
||||
## 5.1.0
|
||||
|
||||
Tue, 25 Jan 2022 07:11:53 GMT
|
||||
|
||||
### Minor changes
|
||||
|
||||
- add findLastIndex and inRange functions to fast-web-utilities (john.kreitlow@microsoft.com)
|
||||
|
||||
## 5.0.2
|
||||
|
||||
Sun, 31 Oct 2021 07:17:45 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- update fast eslint package version (chhol@microsoft.com)
|
||||
|
||||
## 5.0.1
|
||||
|
||||
Wed, 13 Oct 2021 01:53:37 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- remove prefix from uniqueId function (john.kreitlow@microsoft.com)
|
||||
- remove throttle function (john.kreitlow@microsoft.com)
|
||||
- refactor: remove lodash-es as a dependency (connor@peet.io)
|
||||
|
||||
## 5.0.0
|
||||
|
||||
Sun, 19 Sep 2021 07:17:17 GMT
|
||||
|
||||
### Major changes
|
||||
|
||||
- add picker component (scomea@microsoft.com)
|
||||
|
||||
## 4.8.1
|
||||
|
||||
Sun, 12 Sep 2021 07:17:43 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- remove dependencies on keycode (scomea@microsoft.com)
|
||||
|
||||
## 4.8.0
|
||||
|
||||
Thu, 20 May 2021 07:24:10 GMT
|
||||
|
||||
### Minor changes
|
||||
|
||||
- add key string constants (john.kreitlow@microsoft.com)
|
||||
|
||||
## [4.7.3](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.7.1...@microsoft/fast-web-utilities@4.7.3) (2021-02-08)
|
||||
|
||||
**Note:** Version bump only for package @microsoft/fast-web-utilities
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.7.2](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.7.1...@microsoft/fast-web-utilities@4.7.2) (2021-02-08)
|
||||
|
||||
**Note:** Version bump only for package @microsoft/fast-web-utilities
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.7.1](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.7.0...@microsoft/fast-web-utilities@4.7.1) (2021-01-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow nonce usage in canUseFocusVisible ([#4243](https://github.com/Microsoft/fast/issues/4243)) ([6e8b917](https://github.com/Microsoft/fast/commit/6e8b917dcbff8c5c0452e2dccc92020070ae3e3c))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.7.0](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.6.1...@microsoft/fast-web-utilities@4.7.0) (2020-12-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add standard event types as exported strings ([#4161](https://github.com/Microsoft/fast/issues/4161)) ([f2d9087](https://github.com/Microsoft/fast/commit/f2d9087296401d613ff4aa85d8eb4c0d54e73be0))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.6.1](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.6.0...@microsoft/fast-web-utilities@4.6.1) (2020-10-14)
|
||||
|
||||
**Note:** Version bump only for package @microsoft/fast-web-utilities
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.6.0](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.5.2...@microsoft/fast-web-utilities@4.6.0) (2020-07-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* update typescript version and remove utility types dependencies for react packages ([#3422](https://github.com/Microsoft/fast/issues/3422)) ([09d07b5](https://github.com/Microsoft/fast/commit/09d07b580cda3bcc5d28f83d3568521f710c9576))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.5.2](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.5.1...@microsoft/fast-web-utilities@4.5.2) (2020-06-26)
|
||||
|
||||
**Note:** Version bump only for package @microsoft/fast-web-utilities
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.5.0](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.4.5...@microsoft/fast-web-utilities@4.5.0) (2020-05-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add system-colors to fast-web-utilities ([#3137](https://github.com/Microsoft/fast/issues/3137)) ([ee37b3f](https://github.com/Microsoft/fast/commit/ee37b3f51336a040b64fc0f00d73c57185cafaa7))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.4.5](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.4.4...@microsoft/fast-web-utilities@4.4.5) (2020-04-29)
|
||||
|
||||
**Note:** Version bump only for package @microsoft/fast-web-utilities
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.4.4](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.4.3...@microsoft/fast-web-utilities@4.4.4) (2020-04-27)
|
||||
|
||||
**Note:** Version bump only for package @microsoft/fast-web-utilities
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.4.3](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.4.2...@microsoft/fast-web-utilities@4.4.3) (2020-04-22)
|
||||
|
||||
**Note:** Version bump only for package @microsoft/fast-web-utilities
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.4.2](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.4.1...@microsoft/fast-web-utilities@4.4.2) (2020-04-10)
|
||||
|
||||
**Note:** Version bump only for package @microsoft/fast-web-utilities
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.4.1](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.4.0...@microsoft/fast-web-utilities@4.4.1) (2020-03-13)
|
||||
|
||||
**Note:** Version bump only for package @microsoft/fast-web-utilities
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.4.0](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.3.7...@microsoft/fast-web-utilities@4.4.0) (2019-12-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* horizontal overflow rtl scroll behavior and add rtl scrollLeft utility ([#2462](https://github.com/Microsoft/fast/issues/2462)) ([0cc2da5](https://github.com/Microsoft/fast/commit/0cc2da5fde2abe23da987c216e03d73370cd4ad3))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add new utilities for checking HTML elements ([#2481](https://github.com/Microsoft/fast/issues/2481)) ([15de67e](https://github.com/Microsoft/fast/commit/15de67e207796c0b799827f33cdb47a8e32432e3))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.3.7](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.3.6...@microsoft/fast-web-utilities@4.3.7) (2019-11-19)
|
||||
|
||||
**Note:** Version bump only for package @microsoft/fast-web-utilities
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.3.6](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.3.5...@microsoft/fast-web-utilities@4.3.6) (2019-11-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* canUsedForcedColors should be called canUseForcedColors ([#2403](https://github.com/Microsoft/fast/issues/2403)) ([8904df4](https://github.com/Microsoft/fast/commit/8904df4d8af78d917a3aa6104f34ad45e12bfda5))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.3.5](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.3.4...@microsoft/fast-web-utilities@4.3.5) (2019-10-25)
|
||||
|
||||
**Note:** Version bump only for package @microsoft/fast-web-utilities
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.3.4](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.3.3...@microsoft/fast-web-utilities@4.3.4) (2019-10-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* canUsedForcedColors function is not isomorphic ([#2378](https://github.com/Microsoft/fast/issues/2378)) ([0c929c8](https://github.com/Microsoft/fast/commit/0c929c8a333f810d95d43b09d171e71e55276f16))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.3.3](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.3.2...@microsoft/fast-web-utilities@4.3.3) (2019-10-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* components are not showing correct high contrast colors in Edge chromium, removed hard code value, and add more examples ([#2327](https://github.com/Microsoft/fast/issues/2327)) ([125a85c](https://github.com/Microsoft/fast/commit/125a85c))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.3.2](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.3.1...@microsoft/fast-web-utilities@4.3.2) (2019-09-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve render performance of page, grid, and column components ([#2241](https://github.com/Microsoft/fast/issues/2241)) ([7936adc](https://github.com/Microsoft/fast/commit/7936adc))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.3.1](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.3.0...@microsoft/fast-web-utilities@4.3.1) (2019-09-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* exit focus visible utility immediately with false value if DOM is unavailable ([#2223](https://github.com/Microsoft/fast/issues/2223)) ([3fbd6c2](https://github.com/Microsoft/fast/commit/3fbd6c2))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.3.0](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.2.1...@microsoft/fast-web-utilities@4.3.0) (2019-08-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adds classNames utility ([#2163](https://github.com/Microsoft/fast/issues/2163)) ([d6a872d](https://github.com/Microsoft/fast/commit/d6a872d))
|
||||
* export individual keycodes as named exports and unreference KeyCodes ([327d806](https://github.com/Microsoft/fast/commit/327d806))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.2.1](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.2.0...@microsoft/fast-web-utilities@4.2.1) (2019-08-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* establish no side effects in fast-web-utilities ([a97741a](https://github.com/Microsoft/fast/commit/a97741a))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.2.0](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.1.0...@microsoft/fast-web-utilities@4.2.0) (2019-05-31)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add query string parser ([#1784](https://github.com/Microsoft/fast/issues/1784)) ([de20112](https://github.com/Microsoft/fast/commit/de20112))
|
||||
* show values as dash separated and send camelCase values in the CSS property editor callback ([#1772](https://github.com/Microsoft/fast/issues/1772)) ([93d6223](https://github.com/Microsoft/fast/commit/93d6223))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.0.1...@microsoft/fast-web-utilities@4.1.0) (2019-04-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* reduce dependency on peer dependencies ([#1669](https://github.com/Microsoft/fast/issues/1669)) ([cc06b10](https://github.com/Microsoft/fast/commit/cc06b10))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.0.1](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@4.0.0...@microsoft/fast-web-utilities@4.0.1) (2019-04-09)
|
||||
|
||||
**Note:** Version bump only for package @microsoft/fast-web-utilities
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.0.0](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@3.1.3...@microsoft/fast-web-utilities@4.0.0) (2019-03-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update to use esModuleInterop in the TypeScript configuration files ([#1211](https://github.com/Microsoft/fast/issues/1211)) ([2ec0644](https://github.com/Microsoft/fast/commit/2ec0644))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* remove fast-application-utilities package ([#1455](https://github.com/Microsoft/fast/issues/1455)) ([7ee34fa](https://github.com/Microsoft/fast/commit/7ee34fa))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* removal of fast-application-utilities-package
|
||||
* This will affect how imports will be handled by
|
||||
consumers
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.1.3](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@3.1.2...@microsoft/fast-web-utilities@3.1.3) (2019-03-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update jest to fix build break ([#1531](https://github.com/Microsoft/fast/issues/1531)) ([73ae6de](https://github.com/Microsoft/fast/commit/73ae6de))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.1.2](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@3.1.1...@microsoft/fast-web-utilities@3.1.2) (2019-02-21)
|
||||
|
||||
**Note:** Version bump only for package @microsoft/fast-web-utilities
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.1.1](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@3.1.0...@microsoft/fast-web-utilities@3.1.1) (2019-02-07)
|
||||
|
||||
**Note:** Version bump only for package @microsoft/fast-web-utilities
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="3.1.0"></a>
|
||||
# [3.1.0](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@3.0.4...@microsoft/fast-web-utilities@3.1.0) (2019-01-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add direction and localization helpers to fast-web-utilities ([#1330](https://github.com/Microsoft/fast/issues/1330)) ([be0f603](https://github.com/Microsoft/fast/commit/be0f603))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="3.0.4"></a>
|
||||
## [3.0.4](https://github.com/Microsoft/fast/compare/@microsoft/fast-web-utilities@3.0.3...@microsoft/fast-web-utilities@3.0.4) (2018-12-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* style cleanup and consolidation ([#1198](https://github.com/Microsoft/fast/issues/1198)) ([4151f39](https://github.com/Microsoft/fast/commit/4151f39))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="2.2.0"></a>
|
||||
# 2.2.0 (2018-09-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add contrast based color system ([#810](https://github.com/Microsoft/fast/issues/810)) ([5ec457c](https://github.com/Microsoft/fast/commit/5ec457c))
|
||||
|
||||
|
||||
|
||||
<a name="2.1.0"></a>
|
||||
# 2.1.0 (2018-08-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **fast-web-utilities:** fix an issue where getClientRectWithMargin threw an error in EDGE and IE strict modes due to readonly properties ([#767](https://github.com/Microsoft/fast/issues/767)) ([d3fa002](https://github.com/Microsoft/fast/commit/d3fa002))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* update Lerna to ^3.0.0 ([#795](https://github.com/Microsoft/fast/issues/795)) ([9ce9a56](https://github.com/Microsoft/fast/commit/9ce9a56))
|
||||
* upgrade to TypeScript 3.0.0 ([#793](https://github.com/Microsoft/fast/issues/793)) ([e203e86](https://github.com/Microsoft/fast/commit/e203e86))
|
||||
* **fast-components-react-base:** add callback to horizontal overflow to return and object that informs scroll start and end ([#797](https://github.com/Microsoft/fast/issues/797)) ([37975f3](https://github.com/Microsoft/fast/commit/37975f3))
|
||||
* **fast-components-react-base:** add tabs component ([#761](https://github.com/Microsoft/fast/issues/761)) ([24a5bb3](https://github.com/Microsoft/fast/commit/24a5bb3))
|
||||
* **paragraph:** adds paragraph as a new MSFT component ([#805](https://github.com/Microsoft/fast/issues/805)) ([8325d3f](https://github.com/Microsoft/fast/commit/8325d3f))
|
||||
|
||||
|
||||
|
||||
<a name="2.0.0-corrected"></a>
|
||||
# 2.0.0-corrected (2018-08-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **utilities:** add fast-web-utilities as a new package ([#686](https://github.com/Microsoft/fast/issues/686)) ([a31a581](https://github.com/Microsoft/fast/commit/a31a581))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="2.1.0"></a>
|
||||
# [2.1.0](https://github.com/Microsoft/fast/compare/v2.0.0-corrected...v2.1.0) (2018-08-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **fast-web-utilities:** fix an issue where getClientRectWithMargin threw an error in EDGE and IE strict modes due to readonly properties ([#767](https://github.com/Microsoft/fast/issues/767)) ([d3fa002](https://github.com/Microsoft/fast/commit/d3fa002))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* update Lerna to ^3.0.0 ([#795](https://github.com/Microsoft/fast/issues/795)) ([9ce9a56](https://github.com/Microsoft/fast/commit/9ce9a56))
|
||||
* upgrade to TypeScript 3.0.0 ([#793](https://github.com/Microsoft/fast/issues/793)) ([e203e86](https://github.com/Microsoft/fast/commit/e203e86))
|
||||
* **fast-components-react-base:** add callback to horizontal overflow to return and object that informs scroll start and end ([#797](https://github.com/Microsoft/fast/issues/797)) ([37975f3](https://github.com/Microsoft/fast/commit/37975f3))
|
||||
* **fast-components-react-base:** add tabs component ([#761](https://github.com/Microsoft/fast/issues/761)) ([24a5bb3](https://github.com/Microsoft/fast/commit/24a5bb3))
|
||||
* **paragraph:** adds paragraph as a new MSFT component ([#805](https://github.com/Microsoft/fast/issues/805)) ([8325d3f](https://github.com/Microsoft/fast/commit/8325d3f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="2.0.0"></a>
|
||||
# [2.0.0](https://github.com/Microsoft/fast/compare/v1.6.0...v2.0.0) (2018-08-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **utilities:** add fast-web-utilities as a new package ([#686](https://github.com/Microsoft/fast/issues/686)) ([a31a581](https://github.com/Microsoft/fast/commit/a31a581))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.9.0"></a>
|
||||
# [1.9.0](https://github.com/Microsoft/fast/compare/v1.6.0...v1.9.0) (2018-07-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **utilities:** add fast-web-utilities as a new package ([#686](https://github.com/Microsoft/fast/issues/686)) ([a31a581](https://github.com/Microsoft/fast/commit/a31a581))
|
|
@ -1,173 +0,0 @@
|
|||
# FAST Web utilities
|
||||
|
||||
This package is a collection of utilities intended to be used for web projects.
|
||||
|
||||
## Installation
|
||||
|
||||
`npm i --save @microsoft/fast-web-utilities`
|
||||
|
||||
## Usage
|
||||
|
||||
### DOM utilities
|
||||
|
||||
#### Keys
|
||||
|
||||
Various keys are available to use with keyboard events.
|
||||
|
||||
```js
|
||||
import { keyEnter } from "@microsoft/fast-web-utilities";
|
||||
|
||||
handleKeyPress = (e) => {
|
||||
if (e.key === keyEnter) {
|
||||
// Do something when the Enter key has been pressed
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
View our [available keys](https://github.com/microsoft/fast/blob/master/packages/utilities/fast-web-utilities/src/key-codes.ts) file for details.
|
||||
|
||||
### HTML utilities
|
||||
|
||||
#### getClientRectWithMargin
|
||||
|
||||
The `getClientRectWithMargin` function gets the client bounding rectangle including any margins of an element.
|
||||
|
||||
```js
|
||||
import { getClientRectWithMargin } from "@microsoft/fast-web-utilities";
|
||||
|
||||
const itemWidth = getClientRectWithMargin(item).width;
|
||||
const itemHeight = getClientRectWithMargin(item).height;
|
||||
```
|
||||
|
||||
#### convertStylePropertyPixelsToNumber
|
||||
|
||||
The `convertStylePropertyPixelsToNumber` function will convert a property value from an elements computed style from pixels to a number value.
|
||||
|
||||
```js
|
||||
import { convertStylePropertyPixelsToNumber } from "@microsoft/fast-web-utilities";
|
||||
|
||||
const elementTopMargin = convertStylePropertyPixelsToNumber(style, "margin-top");
|
||||
```
|
||||
|
||||
### Key utilities
|
||||
|
||||
#### Key strings
|
||||
|
||||
Commonly used `event.key` values are available as individual exports. Additional `key` values will be added as needed.
|
||||
|
||||
```js
|
||||
import { keyEnter, keySpace } from "@microsoft/fast-web-utilities";
|
||||
|
||||
handleKeyPress = (e) => {
|
||||
switch (e.key) {
|
||||
case keySpace:
|
||||
case keyEnter:
|
||||
// Do something if key matches
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### KeyCodes (enum)
|
||||
|
||||
Keycodes are deprecated and their use should be avoided. Use the individual string `key` values instead.
|
||||
|
||||
### Localization utilities
|
||||
|
||||
#### Typescript enum
|
||||
|
||||
The `Direction` enum contains the `ltr` and `rtl` enum for use in a Typescript project.
|
||||
|
||||
```typescript
|
||||
import { Direction } from "@microsoft/fast-web-utilities";
|
||||
|
||||
let direction: Direction = Direction.ltr;
|
||||
```
|
||||
|
||||
### Number utilities
|
||||
|
||||
#### Limit
|
||||
|
||||
The `limit` function ensures that a value is between a min and max value. If the value is lower than min, min will be returned. If the value is greater than max, max will be retured.
|
||||
|
||||
```js
|
||||
import { limit } from "@microsoft/fast-web-utilities";
|
||||
const incomingNumber; // 11
|
||||
const setNumberByLimit = limit(0, 10, incomingNumber); // returns 10
|
||||
```
|
||||
|
||||
#### wrapInBounds
|
||||
|
||||
The `wrapInBounds` function keeps a given value within the bounds of a min and max value. If the value is larger than the max, the minimum value will be returned. If the value is smaller than the minimum, the maximum will be returned. Otherwise, the value is returned un-changed.
|
||||
|
||||
```js
|
||||
import { wrapInBounds } from "@microsoft/fast-web-utilities";
|
||||
const slides; // 5
|
||||
const index; // 5
|
||||
const activeIndex = wrapInBounds(0, this.slides.length - 1, index) // returns 0
|
||||
```
|
||||
|
||||
### String utilities
|
||||
|
||||
#### Format
|
||||
|
||||
The `format` function builds a string from a format specifier and replacement parameters.
|
||||
|
||||
```js
|
||||
import { format } from "@microsoft/fast-web-utilities";
|
||||
|
||||
const formatterString = "View {0} {1}";
|
||||
|
||||
const newString = format(formatterString, "page", "4")); // "View page 4"
|
||||
```
|
||||
|
||||
#### startsWith
|
||||
|
||||
The `startsWith` function checks to see if one string starts with another. The function is case sensitive.
|
||||
|
||||
```js
|
||||
import { startsWith } from "@microsoft/fast-web-utilities";
|
||||
|
||||
const matchIsFalse = startsWith("HelloWorld", "World"); // false
|
||||
const matchIsTrue = startsWith("HelloWorld", "Hello"); // true
|
||||
```
|
||||
|
||||
#### isNullOrWhiteSpace
|
||||
|
||||
The `isNullOrWhiteSpace` function determines if the specified string is undefined, null, empty, or whitespace. The function returns true if the value is undefined, null, empty, or whitespace, otherwise false.
|
||||
|
||||
```js
|
||||
import { isNullOrWhiteSpace } from "@microsoft/fast-web-utilities";
|
||||
|
||||
const myAnchor = document.querySelector("#id");
|
||||
const checkWhitespace = isNullOrWhiteSpace(myAnchor.href);
|
||||
```
|
||||
|
||||
#### pascalCase
|
||||
|
||||
The `pascalCase` function converts a string to Pascal Case
|
||||
|
||||
```js
|
||||
import { pascalCase } from "@microsoft/fast-web-utilities";
|
||||
|
||||
const hyphenatedToPascal = pascalCase("my-string");
|
||||
const uppercaseToPascal = pascalCase("MY STRING");
|
||||
const whitespaceToPascal = pascalCase(" my string ");
|
||||
```
|
||||
|
||||
#### classNames
|
||||
A utility for merging class names into a single string conditionally. Accepts any number of strings, functions that return strings and two index arrays where the first index is a string or function that returns a string, and the second index is a boolean.
|
||||
|
||||
```js
|
||||
import { classNames } from "@microsoft/fast-web-utilities";
|
||||
|
||||
// evaluates to "classOne classTwo classThree classFive"
|
||||
const myJoinedClassNames = classNames(
|
||||
"classOne",
|
||||
() => "classTwo",
|
||||
["classThree", true],
|
||||
["classFour", false]
|
||||
[() => "classFive", true],
|
||||
[() => "classSix", false]
|
||||
)
|
||||
```
|
|
@ -1,160 +0,0 @@
|
|||
const path = require("path");
|
||||
|
||||
const basePath = path.resolve(__dirname);
|
||||
|
||||
const commonChromeFlags = [
|
||||
"--no-default-browser-check",
|
||||
"--no-first-run",
|
||||
"--no-sandbox",
|
||||
"--no-managed-user-acknowledgment-check",
|
||||
"--disable-background-timer-throttling",
|
||||
"--disable-backing-store-limit",
|
||||
"--disable-boot-animation",
|
||||
"--disable-cloud-import",
|
||||
"--disable-contextual-search",
|
||||
"--disable-default-apps",
|
||||
"--disable-extensions",
|
||||
"--disable-infobars",
|
||||
"--disable-translate",
|
||||
"--force-device-scale-factor=1",
|
||||
];
|
||||
|
||||
module.exports = function (config) {
|
||||
let browsers;
|
||||
if (process.env.BROWSERS) {
|
||||
browsers = [process.env.BROWSERS];
|
||||
} else if (config.browsers) {
|
||||
browsers = config.browsers;
|
||||
} else {
|
||||
browsers = ["Chrome"];
|
||||
}
|
||||
|
||||
const setup = "setup-browser" + (config.package ? "-" + config.package : "");
|
||||
const options = {
|
||||
basePath,
|
||||
browserDisconnectTimeout: 10000,
|
||||
processKillTimeout: 10000,
|
||||
frameworks: ["source-map-support", "mocha"],
|
||||
plugins: [
|
||||
require("karma-mocha"),
|
||||
require("karma-mocha-reporter"),
|
||||
require("karma-webpack"),
|
||||
require("karma-source-map-support"),
|
||||
require("karma-sourcemap-loader"),
|
||||
require("karma-coverage-istanbul-reporter"),
|
||||
require("karma-chrome-launcher"),
|
||||
require("karma-firefox-launcher"),
|
||||
],
|
||||
files: [`dist/__test__/${setup}.cjs`],
|
||||
preprocessors: {
|
||||
[`dist/__test__/${setup}.cjs`]: ["webpack", "sourcemap"],
|
||||
},
|
||||
webpackMiddleware: {
|
||||
// webpack-dev-middleware configuration
|
||||
// i. e.
|
||||
stats: "errors-only",
|
||||
},
|
||||
webpack: {
|
||||
mode: "none",
|
||||
resolve: {
|
||||
extensions: [".js"],
|
||||
modules: ["node_modules"],
|
||||
mainFields: ["module", "main"],
|
||||
},
|
||||
devtool: "inline-source-map",
|
||||
performance: {
|
||||
hints: false,
|
||||
},
|
||||
optimization: {
|
||||
nodeEnv: false,
|
||||
usedExports: true,
|
||||
flagIncludedChunks: false,
|
||||
sideEffects: true,
|
||||
concatenateModules: true,
|
||||
splitChunks: {
|
||||
name: false,
|
||||
},
|
||||
runtimeChunk: false,
|
||||
noEmitOnErrors: false,
|
||||
checkWasmTypes: false,
|
||||
minimize: false,
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js\.map$/,
|
||||
use: ["ignore-loader"],
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
use: [
|
||||
{
|
||||
loader: "source-map-loader",
|
||||
options: {
|
||||
enforce: "pre",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
mime: {
|
||||
"text/x-typescript": ["ts"],
|
||||
},
|
||||
reporters: [config.reporter || (process.env.CI ? "min" : "progress")],
|
||||
browsers: browsers,
|
||||
customLaunchers: {
|
||||
ChromeDebugging: {
|
||||
base: "Chrome",
|
||||
flags: [...commonChromeFlags, "--remote-debugging-port=9333"],
|
||||
debug: true,
|
||||
},
|
||||
ChromeHeadlessOpt: {
|
||||
base: "ChromeHeadless",
|
||||
flags: [...commonChromeFlags],
|
||||
},
|
||||
},
|
||||
client: {
|
||||
captureConsole: true,
|
||||
mocha: {
|
||||
bail: config["bail"],
|
||||
ui: "bdd",
|
||||
timeout: 5000,
|
||||
},
|
||||
},
|
||||
logLevel: config.LOG_ERROR, // to disable the WARN 404 for image requests
|
||||
};
|
||||
|
||||
if (config.coverage) {
|
||||
options.webpack.module.rules.push({
|
||||
enforce: "post",
|
||||
exclude: /(__tests__|testing|node_modules|\.spec\.[tj]s$)/,
|
||||
loader: "istanbul-instrumenter-loader",
|
||||
options: { esModules: true },
|
||||
test: /\.[tj]s$/,
|
||||
});
|
||||
options.reporters = ["coverage-istanbul", ...options.reporters];
|
||||
options.coverageIstanbulReporter = {
|
||||
reports: ["html", "text-summary", "json", "lcovonly", "cobertura"],
|
||||
dir: "coverage",
|
||||
verbose: true,
|
||||
thresholds: {
|
||||
emitWarning: false,
|
||||
global: {
|
||||
statements: 90,
|
||||
lines: 90,
|
||||
branches: 90,
|
||||
functions: 90,
|
||||
},
|
||||
},
|
||||
};
|
||||
options.junitReporter = {
|
||||
outputDir: "coverage",
|
||||
outputFile: "test-results.xml",
|
||||
useBrowserName: false,
|
||||
};
|
||||
}
|
||||
|
||||
config.set(options);
|
||||
};
|
|
@ -1,75 +0,0 @@
|
|||
{
|
||||
"name": "@microsoft/fast-web-utilities",
|
||||
"description": "FAST web utilities",
|
||||
"version": "6.0.0",
|
||||
"sideEffects": false,
|
||||
"author": {
|
||||
"name": "Microsoft",
|
||||
"url": "https://discord.gg/FcSNfg4"
|
||||
},
|
||||
"homepage": "https://www.fast.design/",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Microsoft/fast.git",
|
||||
"directory": "packages/utilities/fast-web-utilities"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/Microsoft/fast/issues/new/choose"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc -p ./tsconfig.build.json",
|
||||
"clean:dist": "node ../../../build/clean.js dist",
|
||||
"prepare": "yarn clean:dist && yarn build",
|
||||
"prettier": "prettier --config ../../../.prettierrc --write \"**/*.ts\"",
|
||||
"prettier:diff": "prettier --config ../../../.prettierrc \"**/*.ts\" --list-different",
|
||||
"test": "yarn eslint && yarn build && yarn test-chrome:verbose",
|
||||
"test-node": "mocha --reporter min --exit dist/esm/__test__/setup-node.js './dist/esm/**/*.spec.js'",
|
||||
"test-node:verbose": "mocha --reporter spec --exit dist/esm/__test__/setup-node.js './dist/esm/**/*.spec.js'",
|
||||
"test-chrome": "karma start karma.conf.cjs --browsers=ChromeHeadlessOpt --single-run --coverage",
|
||||
"test-chrome:verbose": "karma start karma.conf.cjs --browsers=ChromeHeadlessOpt --single-run --coverage --reporter=mocha",
|
||||
"test-chrome:watch": "karma start karma.conf.cjs --browsers=ChromeHeadlessOpt --coverage --watch-extensions js",
|
||||
"test-chrome:debugger": "karma start karma.conf.cjs --browsers=ChromeDebugging",
|
||||
"test-chrome:verbose:watch": "karma start karma.conf.cjs --browsers=ChromeHeadlessOpt --coverage --watch-extensions js --reporter=mocha",
|
||||
"test-chrome:verbose:debugger": "karma start karma.conf.cjs --browsers=ChromeDebugging --reporter=mocha",
|
||||
"test-firefox": "karma start karma.conf.cjs --browsers=FirefoxHeadless --single-run --coverage",
|
||||
"test-firefox:verbose": "karma start karma.conf.cjs --browsers=FirefoxHeadless --single-run --coverage --reporter=mocha",
|
||||
"test-firefox:watch": "karma start karma.conf.cjs --browsers=FirefoxHeadless --coverage --watch-extensions js",
|
||||
"eslint": "eslint . --ext .ts,.tsx",
|
||||
"eslint:fix": "eslint . --ext .ts,.tsx --fix",
|
||||
"watch": "yarn build -- -w --preserveWatchOutput"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.2.11",
|
||||
"@types/karma": "^6.3.3",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"chai": "^4.2.0",
|
||||
"chai-spies": "^1.0.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-loader": "^4.0.0",
|
||||
"istanbul": "^0.4.5",
|
||||
"istanbul-instrumenter-loader": "^3.0.1",
|
||||
"jsdom": "^16.2.2",
|
||||
"jsdom-global": "3.0.2",
|
||||
"karma": "^6.4.1",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-coverage": "^2.0.2",
|
||||
"karma-coverage-istanbul-reporter": "^3.0.0",
|
||||
"karma-firefox-launcher": "^2.1.0",
|
||||
"karma-mocha": "^2.0.1",
|
||||
"karma-mocha-reporter": "^2.2.5",
|
||||
"karma-source-map-support": "^1.4.0",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-webpack": "^5.0.0",
|
||||
"mocha": "^7.1.2",
|
||||
"prettier": "2.8.8",
|
||||
"ts-loader": "^4.0.1",
|
||||
"typescript": "^4.7.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"exenv-es6": "^1.1.1"
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
function importAll(r: __WebpackModuleApi.RequireContext): void {
|
||||
r.keys().forEach(r);
|
||||
}
|
||||
|
||||
// Explicitly add to browser test
|
||||
importAll(require.context("../", true, /\.spec\.js$/));
|
|
@ -1,12 +0,0 @@
|
|||
/* eslint-disable */
|
||||
if (window.document && !window.document.createRange) {
|
||||
window.document.createRange = () => ({
|
||||
setStart: () => {},
|
||||
setEnd: () => {},
|
||||
// @ts-ignore
|
||||
commonAncestorContainer: {
|
||||
nodeName: "BODY",
|
||||
ownerDocument: document,
|
||||
},
|
||||
});
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
import { expect } from "chai";
|
||||
import { Orientation } from "./aria.js";
|
||||
|
||||
describe("aria-orientation", () => {
|
||||
it("should correctly return orientation values", () => {
|
||||
expect(Orientation.horizontal).to.equal("horizontal");
|
||||
expect(Orientation.vertical).to.equal("vertical");
|
||||
});
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
/**
|
||||
* Standard orientation values
|
||||
*/
|
||||
export const Orientation = {
|
||||
horizontal: "horizontal",
|
||||
vertical: "vertical",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* The orientation type
|
||||
*/
|
||||
export type Orientation = (typeof Orientation)[keyof typeof Orientation];
|
|
@ -1,30 +0,0 @@
|
|||
import { expect } from "chai";
|
||||
import { findLastIndex } from "./array.js";
|
||||
|
||||
describe("findLastIndex", (): void => {
|
||||
it("should return -1 when array is empty", (): void => {
|
||||
expect(findLastIndex([], () => true)).to.equal(-1);
|
||||
});
|
||||
|
||||
it("should return the last valid item that matches the predicate", (): void => {
|
||||
const array = [
|
||||
{ value: true },
|
||||
{ value: false },
|
||||
{ value: true },
|
||||
{ value: false },
|
||||
];
|
||||
|
||||
expect(findLastIndex(array, v => v.value)).to.equal(2);
|
||||
});
|
||||
|
||||
it("should return -1 when no items match the predicate", (): void => {
|
||||
const array = [
|
||||
{ value: false },
|
||||
{ value: false },
|
||||
{ value: false },
|
||||
{ value: false },
|
||||
];
|
||||
|
||||
expect(findLastIndex(array, v => v.value)).to.equal(-1);
|
||||
});
|
||||
});
|
|
@ -1,21 +0,0 @@
|
|||
/**
|
||||
* Returns the index of the last element in the array where predicate is true, and -1 otherwise.
|
||||
*
|
||||
* @param array - the array to test
|
||||
* @param predicate - find calls predicate once for each element of the array, in descending order,
|
||||
* until it finds one where predicate returns true. If such an element is found, findLastIndex immediately returns that element index.
|
||||
* Otherwise, findIndex returns -1.
|
||||
*/
|
||||
export function findLastIndex<T>(
|
||||
array: Array<T>,
|
||||
predicate: (value: T, index: number, obj: T[]) => unknown
|
||||
): number {
|
||||
let k = array.length;
|
||||
while (k--) {
|
||||
if (predicate(array[k], k, array)) {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
import { expect } from "chai";
|
||||
import { classNames } from "./class-names.js";
|
||||
|
||||
describe("classNames", (): void => {
|
||||
it("should return a string when invalid values are provided", (): void => {
|
||||
expect(classNames()).to.equal("");
|
||||
expect(classNames(undefined as any)).to.equal("");
|
||||
expect(classNames(null as any)).to.equal("");
|
||||
expect(classNames(NaN as any)).to.equal("");
|
||||
expect(classNames(Infinity as any)).to.equal("");
|
||||
expect(classNames(new Date() as any)).to.equal("");
|
||||
expect(classNames(1 as any)).to.equal("");
|
||||
expect(classNames([undefined as any, true])).to.equal("");
|
||||
expect(classNames([null as any, true])).to.equal("");
|
||||
expect(classNames([NaN as any, true])).to.equal("");
|
||||
expect(classNames([Infinity as any, true])).to.equal("");
|
||||
expect(classNames([new Date() as any, true])).to.equal("");
|
||||
expect(classNames([1 as any, true])).to.equal("");
|
||||
});
|
||||
|
||||
it("should return a single string argument unmodified", (): void => {
|
||||
expect(classNames("hello")).to.equal("hello");
|
||||
});
|
||||
|
||||
it("should join multiple string arguments together", (): void => {
|
||||
expect(classNames("hello", "world")).to.equal("hello world");
|
||||
});
|
||||
|
||||
it("should return the return value of a single function", (): void => {
|
||||
expect(classNames(() => "hello")).to.equal("hello");
|
||||
});
|
||||
|
||||
it("should join the return value of a multiple functions", (): void => {
|
||||
expect(
|
||||
classNames(
|
||||
() => "hello",
|
||||
() => "world"
|
||||
)
|
||||
).to.equal("hello world");
|
||||
});
|
||||
|
||||
it("should return a the first index of an array arg when the second index is truthy", (): void => {
|
||||
expect(classNames(["foo", true])).to.equal("foo");
|
||||
});
|
||||
|
||||
it("should return a single function return value of an array arg when the second index is truthy", (): void => {
|
||||
expect(classNames([(): string => "foo", true])).to.equal("foo");
|
||||
});
|
||||
|
||||
it("should join multiple array index when all second indexes are true", (): void => {
|
||||
expect(classNames(["foo", true], ["bar", true])).to.equal("foo bar");
|
||||
});
|
||||
|
||||
it("should omit first indexes of an array argument when the second index is falsey", (): void => {
|
||||
expect(classNames(["foo", true], ["bar", false], ["bat", true])).to.equal(
|
||||
"foo bat"
|
||||
);
|
||||
});
|
||||
|
||||
it("should join string, function, and object arguments", (): void => {
|
||||
expect(
|
||||
classNames(
|
||||
"hello",
|
||||
["foo", true],
|
||||
["bar", false],
|
||||
[(): string => "bat", true],
|
||||
"world",
|
||||
() => "earth"
|
||||
)
|
||||
).to.equal("hello foo bat world earth");
|
||||
});
|
||||
});
|
|
@ -1,17 +0,0 @@
|
|||
type ClassNamesArg = string | (() => string) | [string | (() => string), boolean];
|
||||
|
||||
export function classNames(...args: ClassNamesArg[]): string {
|
||||
return args.reduce<string>((accum: string, value: ClassNamesArg): string => {
|
||||
const leadingChar: string = accum.length ? " " : "";
|
||||
const normalizedValue: string =
|
||||
Array.isArray(value) && value[1]
|
||||
? classNames.call(null, value[0])
|
||||
: typeof value === "function"
|
||||
? value()
|
||||
: typeof value === "string"
|
||||
? value
|
||||
: "";
|
||||
|
||||
return !normalizedValue.length ? accum : accum + leadingChar + normalizedValue;
|
||||
}, "");
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
import chai, { expect } from "chai";
|
||||
import spies from "chai-spies";
|
||||
import {
|
||||
canUseCssGrid,
|
||||
canUseFocusVisible,
|
||||
canUseForcedColors,
|
||||
getDisplayedNodes,
|
||||
isHTMLElement,
|
||||
resetDocumentCache,
|
||||
} from "./dom.js";
|
||||
|
||||
chai.use(spies);
|
||||
|
||||
describe("isHTMLElement", () => {
|
||||
document.body.innerHTML = `
|
||||
<div id="element">
|
||||
Child
|
||||
</div>
|
||||
`;
|
||||
|
||||
it("should not throw", () => {
|
||||
expect(() => {
|
||||
isHTMLElement();
|
||||
}).not.to.throw();
|
||||
});
|
||||
it("should return true if all arguments are HTML elements", () => {
|
||||
expect(isHTMLElement(document.getElementById("element"))).to.equal(true);
|
||||
});
|
||||
it("should return false if all arguments are NOT HTML elements", () => {
|
||||
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
||||
expect(isHTMLElement(document.getElementById("element")!.childNodes)).to.equal(
|
||||
false
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getDisplayedNodes", () => {
|
||||
it("should not throw if both arguments are null or undefined", () => {
|
||||
expect(() => {
|
||||
getDisplayedNodes(null, null);
|
||||
getDisplayedNodes(undefined, undefined);
|
||||
}).not.to.throw();
|
||||
});
|
||||
});
|
||||
|
||||
describe("canUseFocusVisible", () => {
|
||||
beforeEach(() => {
|
||||
resetDocumentCache();
|
||||
});
|
||||
it("should not throw", () => {
|
||||
expect(() => {
|
||||
canUseFocusVisible();
|
||||
}).not.to.throw();
|
||||
});
|
||||
it("should return true if the environment supports focus-visible selectors", () => {
|
||||
expect(canUseFocusVisible()).to.equal(true);
|
||||
});
|
||||
it("should use a nonce if once is present on the page", () => {
|
||||
const nonce: string = "foo-nonce";
|
||||
const metaEl: HTMLMetaElement = document.createElement("meta");
|
||||
metaEl.setAttribute("property", "csp-nonce");
|
||||
metaEl.setAttribute("content", nonce);
|
||||
document.head.appendChild(metaEl);
|
||||
|
||||
// Run the function and intercept its appendChild call
|
||||
const realAppendChild = document.head.appendChild;
|
||||
const mockAppendChild = chai.spy(realAppendChild);
|
||||
Object.defineProperty(document.head, "appendChild", {
|
||||
value: mockAppendChild,
|
||||
configurable: true,
|
||||
});
|
||||
const mutationObserverCallback = (mutationsList: MutationRecord[]): void => {
|
||||
expect(mutationsList).to.have.length.greaterThan(0);
|
||||
expect(mutationsList[0].addedNodes).to.have.length.greaterThan(0);
|
||||
expect(mutationsList[0].addedNodes.item(0)).not.to.equal(undefined);
|
||||
expect(
|
||||
(mutationsList[0].addedNodes.item(0) as HTMLStyleElement).nonce
|
||||
).to.equal(nonce);
|
||||
};
|
||||
const mutationObserver = new MutationObserver(mutationObserverCallback);
|
||||
mutationObserver.observe(document.head, { childList: true, subtree: true });
|
||||
canUseFocusVisible();
|
||||
|
||||
expect(mockAppendChild).to.have.been.called.exactly(1);
|
||||
Object.defineProperty(document.head, "appendChild", {
|
||||
value: realAppendChild,
|
||||
configurable: true,
|
||||
});
|
||||
});
|
||||
it("should cache the result for subsequent calls", () => {
|
||||
const realAppendChild = document.head.appendChild;
|
||||
const mockAppendChild = chai.spy(realAppendChild);
|
||||
Object.defineProperty(document.head, "appendChild", {
|
||||
value: mockAppendChild,
|
||||
configurable: true,
|
||||
});
|
||||
canUseFocusVisible();
|
||||
|
||||
expect(mockAppendChild).to.have.been.called.exactly(1);
|
||||
|
||||
canUseFocusVisible();
|
||||
expect(mockAppendChild).to.have.been.called.exactly(1);
|
||||
Object.defineProperty(document.head, "appendChild", {
|
||||
value: realAppendChild,
|
||||
configurable: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("canUseCssGrid", () => {
|
||||
beforeEach(() => {
|
||||
resetDocumentCache();
|
||||
});
|
||||
it("should not throw", () => {
|
||||
expect(() => {
|
||||
canUseCssGrid();
|
||||
}).not.to.throw();
|
||||
});
|
||||
});
|
||||
|
||||
describe("canUseForcedColors", () => {
|
||||
beforeEach(() => {
|
||||
window.matchMedia = (query: any): any => {
|
||||
return {
|
||||
matches: true,
|
||||
media: query,
|
||||
};
|
||||
};
|
||||
});
|
||||
it("should return true if forced color is enabled", () => {
|
||||
expect(canUseForcedColors()).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("canUseForcedColors", () => {
|
||||
beforeEach(() => {
|
||||
window.matchMedia = (query: any): any => {
|
||||
return {
|
||||
matches: false,
|
||||
media: query,
|
||||
};
|
||||
};
|
||||
});
|
||||
it("should return false if forced color is not enabled", () => {
|
||||
expect(canUseForcedColors()).to.equal(false);
|
||||
});
|
||||
});
|
|
@ -1,111 +0,0 @@
|
|||
import { canUseDOM } from "exenv-es6";
|
||||
|
||||
/**
|
||||
* A test that ensures that all arguments are HTML Elements
|
||||
*/
|
||||
export function isHTMLElement(...args: any[]): boolean {
|
||||
return args.every((arg: any) => arg instanceof HTMLElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all displayed elements inside of a root node that match a provided selector
|
||||
*/
|
||||
export function getDisplayedNodes(
|
||||
rootNode: HTMLElement | null | undefined,
|
||||
selector: string | null | undefined
|
||||
): HTMLElement[] | void {
|
||||
if (!rootNode || !selector || !isHTMLElement(rootNode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nodes: HTMLElement[] = Array.from(rootNode.querySelectorAll(selector));
|
||||
|
||||
// offsetParent will be null if the element isn't currently displayed,
|
||||
// so this will allow us to operate only on visible nodes
|
||||
return nodes.filter((node: HTMLElement) => node.offsetParent !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nonce used in the page, if any.
|
||||
*
|
||||
* Based on https://github.com/cssinjs/jss/blob/master/packages/jss/src/DomRenderer.js
|
||||
*/
|
||||
function getNonce(): string | null {
|
||||
const node = document.querySelector('meta[property="csp-nonce"]');
|
||||
if (node) {
|
||||
return node.getAttribute("content");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the document supports :focus-visible
|
||||
*/
|
||||
let _canUseFocusVisible: boolean | undefined;
|
||||
export function canUseFocusVisible(): boolean {
|
||||
if (typeof _canUseFocusVisible === "boolean") {
|
||||
return _canUseFocusVisible;
|
||||
}
|
||||
|
||||
if (!canUseDOM()) {
|
||||
_canUseFocusVisible = false;
|
||||
|
||||
return _canUseFocusVisible;
|
||||
}
|
||||
|
||||
// Check to see if the document supports the focus-visible element
|
||||
const styleElement: HTMLStyleElement = document.createElement("style");
|
||||
|
||||
// If nonces are present on the page, use it when creating the style element
|
||||
// to test focus-visible support.
|
||||
const styleNonce = getNonce();
|
||||
if (styleNonce !== null) {
|
||||
styleElement.setAttribute("nonce", styleNonce);
|
||||
}
|
||||
document.head.appendChild(styleElement);
|
||||
|
||||
try {
|
||||
(styleElement.sheet as any).insertRule("foo:focus-visible {color:inherit}", 0);
|
||||
_canUseFocusVisible = true;
|
||||
} catch (e) {
|
||||
_canUseFocusVisible = false;
|
||||
} finally {
|
||||
document.head.removeChild(styleElement);
|
||||
}
|
||||
|
||||
return _canUseFocusVisible as boolean;
|
||||
}
|
||||
|
||||
let _canUseCssGrid: boolean | undefined;
|
||||
export function canUseCssGrid(): boolean {
|
||||
if (typeof _canUseCssGrid === "boolean") {
|
||||
return _canUseCssGrid;
|
||||
}
|
||||
|
||||
try {
|
||||
_canUseCssGrid = CSS.supports("display", "grid");
|
||||
} catch {
|
||||
_canUseCssGrid = false;
|
||||
}
|
||||
|
||||
return _canUseCssGrid;
|
||||
}
|
||||
|
||||
export function canUseForcedColors(): boolean {
|
||||
return (
|
||||
canUseDOM() &&
|
||||
(window.matchMedia("(forced-colors: none)").matches ||
|
||||
window.matchMedia("(forced-colors: active)").matches)
|
||||
);
|
||||
}
|
||||
|
||||
export function resetDocumentCache(): void {
|
||||
_canUseCssGrid = undefined;
|
||||
_canUseFocusVisible = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use 'canUseForcedColors' instead
|
||||
*/
|
||||
export const canUsedForcedColors: typeof canUseForcedColors = canUseForcedColors;
|
|
@ -1,142 +0,0 @@
|
|||
/**
|
||||
* This set of exported strings reference https://developer.mozilla.org/en-US/docs/Web/Events
|
||||
* and should include all non-deprecated and non-experimental Standard events
|
||||
*/
|
||||
|
||||
export const eventAbort: string = "abort";
|
||||
export const eventAfterPrint: string = "afterprint";
|
||||
export const eventAnimationCancel: string = "animationcancel";
|
||||
export const eventAnimationEnd: string = "animationend";
|
||||
export const eventAnimationIteration: string = "animationiteration";
|
||||
export const eventAnimationStart: string = "animationstart";
|
||||
export const eventAppInstalled: string = "appinstalled";
|
||||
export const eventBeforePrint: string = "beforeprint";
|
||||
export const eventBeforeUnload: string = "beforeunload";
|
||||
export const eventBeginEvent: string = "beginEvent";
|
||||
export const eventBlocked: string = "blocked";
|
||||
export const eventBlur: string = "blur";
|
||||
export const eventCanPlay: string = "canplay";
|
||||
export const eventCanPlayThrough: string = "canplaythrough";
|
||||
export const eventChange: string = "change";
|
||||
export const eventChargingChange: string = "chargingchange";
|
||||
export const eventChargingTimeChange: string = "chargingtimechange";
|
||||
export const eventClick: string = "click";
|
||||
export const eventClose: string = "close";
|
||||
export const eventComplete: string = "complete";
|
||||
export const eventCompositionEnd: string = "compositionend";
|
||||
export const eventCompositionStart: string = "compositionstart";
|
||||
export const eventCompositionUpdate: string = "compositionupdate";
|
||||
export const eventContextMenu: string = "contextmenu";
|
||||
export const eventCopy: string = "copy";
|
||||
export const eventCut: string = "cut";
|
||||
export const eventDblClick: string = "dblclick";
|
||||
export const eventDeviceChange: string = "devicechange";
|
||||
export const eventDeviceMotion: string = "devicemotion";
|
||||
export const eventDeviceOrientation: string = "deviceorientation";
|
||||
export const eventDischargingTimeChange: string = "dischargingtimechange";
|
||||
export const eventDrag: string = "drag";
|
||||
export const eventDragEnd: string = "dragend";
|
||||
export const eventDragEnter: string = "dragenter";
|
||||
export const eventDragLeave: string = "dragleave";
|
||||
export const eventDragOver: string = "dragover";
|
||||
export const eventDragStart: string = "dragstart";
|
||||
export const eventDrop: string = "drop";
|
||||
export const eventDurationChange: string = "durationchange";
|
||||
export const eventEmptied: string = "emptied";
|
||||
export const eventEnded: string = "ended";
|
||||
export const eventEndEvent: string = "endevent";
|
||||
export const eventError: string = "error";
|
||||
export const eventFocus: string = "focus";
|
||||
export const eventFocusIn: string = "focusin";
|
||||
export const eventFocusOut: string = "focusout";
|
||||
export const eventFullScreenChange: string = "fullscreenchange";
|
||||
export const eventFullScreenError: string = "fullscreenerror";
|
||||
export const eventGamePadConnected: string = "gamepadconnected";
|
||||
export const eventGamePadDisconnected: string = "gamepaddisconnected";
|
||||
export const eventGotPointerCapture: string = "gotpointercapture";
|
||||
export const eventHashChange: string = "hashchange";
|
||||
export const eventLostPointerCapture: string = "lostpointercapture";
|
||||
export const eventInput: string = "input";
|
||||
export const eventInvalid: string = "invalid";
|
||||
export const eventKeyDown: string = "keydown";
|
||||
export const eventKeyUp: string = "keyup";
|
||||
export const eventLevelChange: string = "levelchange";
|
||||
export const eventLoad: string = "load";
|
||||
export const eventLoadedData: string = "loadeddata";
|
||||
export const eventLoadedMetaData: string = "loadedmetadata";
|
||||
export const eventLoadEnd: string = "loadend";
|
||||
export const eventLoadStart: string = "loadstart";
|
||||
export const eventMessage: string = "message";
|
||||
export const eventMessageError: string = "messageerror";
|
||||
export const eventMouseDown: string = "mousedown";
|
||||
export const eventMouseEnter: string = "mouseenter";
|
||||
export const eventMouseLeave: string = "mouseleave";
|
||||
export const eventMouseMove: string = "mousemove";
|
||||
export const eventMouseOut: string = "mouseout";
|
||||
export const eventMouseOver: string = "mouseover";
|
||||
export const eventMouseUp: string = "mouseup";
|
||||
export const eventNotificationClick: string = "notificationclick";
|
||||
export const eventOffline: string = "offline";
|
||||
export const eventOnline: string = "online";
|
||||
export const eventOpen: string = "open";
|
||||
export const eventOrientationChange: string = "orientationchange";
|
||||
export const eventPageHide: string = "pagehide";
|
||||
export const eventPageShow: string = "pageshow";
|
||||
export const eventPaste: string = "paste";
|
||||
export const eventPause: string = "pause";
|
||||
export const eventPointerCancel: string = "pointercancel";
|
||||
export const eventPointerDown: string = "pointerdown";
|
||||
export const eventPointerEnter: string = "pointerenter";
|
||||
export const eventPointerLeave: string = "pointerleave";
|
||||
export const eventPointerLockChange: string = "pointerlockchange";
|
||||
export const eventPointerLockError: string = "pointerlockerror";
|
||||
export const eventPointerMove: string = "pointermove";
|
||||
export const eventPointerOut: string = "pointerout";
|
||||
export const eventPointerOver: string = "pointerover";
|
||||
export const eventPointerUp: string = "pointerup";
|
||||
export const eventPlay: string = "play";
|
||||
export const eventPlaying: string = "playing";
|
||||
export const eventPopState: string = "popstate";
|
||||
export const eventProgress: string = "progress";
|
||||
export const eventPush: string = "push";
|
||||
export const eventPushSubscriptionChange: string = "pushsubscriptionchange";
|
||||
export const eventRateChange: string = "ratechange";
|
||||
export const eventReadyStateChange: string = "readystatechange";
|
||||
export const eventRepeatEvent: string = "repeatevent";
|
||||
export const eventReset: string = "reset";
|
||||
export const eventResize: string = "resize";
|
||||
export const eventResourceTimingBufferFull: string = "resourcetimingbufferfull";
|
||||
export const eventScroll: string = "scroll";
|
||||
export const eventSeeked: string = "seeked";
|
||||
export const eventSeeking: string = "seeking";
|
||||
export const eventSelect: string = "select";
|
||||
export const eventShow: string = "show";
|
||||
export const eventSlotChange: string = "slotchange";
|
||||
export const eventStalled: string = "stalled";
|
||||
export const eventStart: string = "start";
|
||||
export const eventStorage: string = "storage";
|
||||
export const eventSubmit: string = "submit";
|
||||
export const eventSuccess: string = "success";
|
||||
export const eventSuspend: string = "suspend";
|
||||
export const eventSVGAbort: string = "SVGAbort";
|
||||
export const eventSVGError: string = "SVGError";
|
||||
export const eventSVGLoad: string = "SVGLoad";
|
||||
export const eventSVGResize: string = "SVGResize";
|
||||
export const eventSVGScroll: string = "SVGScroll";
|
||||
export const eventSVGUnload: string = "SVGUnload";
|
||||
export const eventSVGZoom: string = "SVGZoom";
|
||||
export const eventTimeOut: string = "timeout";
|
||||
export const eventTimeUpdate: string = "timeupdate";
|
||||
export const eventTouchCancel: string = "touchcancel";
|
||||
export const eventTouchEnd: string = "touchend";
|
||||
export const eventTouchMove: string = "touchmove";
|
||||
export const eventTouchStart: string = "touchstart";
|
||||
export const eventTransitionEnd: string = "transitionend";
|
||||
export const eventUnload: string = "unload";
|
||||
export const eventUpgradeNeeded: string = "upgradeneeded";
|
||||
export const eventUserProximity: string = "userproximity";
|
||||
export const eventVersionChange: string = "versionchange";
|
||||
export const eventVisibilityChange: string = "visibilitychange";
|
||||
export const eventVolumeChange: string = "volumechange";
|
||||
export const eventWaiting: string = "waiting";
|
||||
export const eventWheel: string = "wheel";
|
|
@ -1,96 +0,0 @@
|
|||
import { expect } from "chai";
|
||||
import { convertStylePropertyPixelsToNumber, getClientRectWithMargin } from "./html.js";
|
||||
|
||||
describe("getClientRectWithMargin", () => {
|
||||
const mockWidth: number = 120;
|
||||
const mockHeight: number = 120;
|
||||
const mockRect: DOMRect = {
|
||||
width: mockWidth,
|
||||
height: mockHeight,
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
x: undefined!,
|
||||
y: undefined!,
|
||||
toJSON: undefined!,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
Element.prototype.getBoundingClientRect = (): any => {
|
||||
return mockRect;
|
||||
};
|
||||
});
|
||||
|
||||
it("should correctly manage undefined and null values", () => {
|
||||
expect(() => getClientRectWithMargin(null)).not.to.throw();
|
||||
expect(() => getClientRectWithMargin(undefined)).not.to.throw();
|
||||
});
|
||||
|
||||
it("should correctly return computed client rect with margin values", () => {
|
||||
document.body.innerHTML = `
|
||||
<div id="element" style="margin: 10px 20px;"></div>
|
||||
`;
|
||||
|
||||
const element: HTMLElement | undefined | null =
|
||||
document.getElementById("element");
|
||||
const expectedWidth: number = mockWidth + 40;
|
||||
const expectedHeight: number = mockHeight + 20;
|
||||
|
||||
const expectedRect: DOMRect = Object.assign({}, mockRect, {
|
||||
width: expectedWidth,
|
||||
height: expectedHeight,
|
||||
});
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
expect(getClientRectWithMargin(element)!.bottom).to.equal(expectedRect.bottom);
|
||||
expect(getClientRectWithMargin(element)!.height).to.equal(expectedRect.height);
|
||||
expect(getClientRectWithMargin(element)!.left).to.equal(expectedRect.left);
|
||||
expect(getClientRectWithMargin(element)!.right).to.equal(expectedRect.right);
|
||||
expect(getClientRectWithMargin(element)!.top).to.equal(expectedRect.top);
|
||||
expect(getClientRectWithMargin(element)!.width).to.equal(expectedRect.width);
|
||||
/* eslint-enable @typescript-eslint/no-non-null-assertion */
|
||||
});
|
||||
});
|
||||
|
||||
describe("convertStylePropertyPixelsToNumber", () => {
|
||||
it("should correctly manage undefined and null values", () => {
|
||||
expect(() => convertStylePropertyPixelsToNumber(null, null)).not.to.throw();
|
||||
expect(() => convertStylePropertyPixelsToNumber(undefined, null)).not.to.throw();
|
||||
expect(() =>
|
||||
convertStylePropertyPixelsToNumber(undefined, undefined)
|
||||
).not.to.throw();
|
||||
});
|
||||
|
||||
it("should correctly convert an element's computed style property pixel value and return a number", () => {
|
||||
document.body.innerHTML = `
|
||||
<div id="element" style="margin: 20px 5px 12px 8px;"></div>
|
||||
`;
|
||||
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
||||
const element: HTMLElement = document.getElementById("element")!;
|
||||
|
||||
expect(
|
||||
convertStylePropertyPixelsToNumber(
|
||||
window.getComputedStyle(element),
|
||||
"margin-top"
|
||||
)
|
||||
).to.equal(20);
|
||||
expect(
|
||||
convertStylePropertyPixelsToNumber(
|
||||
window.getComputedStyle(element),
|
||||
"margin-bottom"
|
||||
)
|
||||
).to.equal(12);
|
||||
expect(
|
||||
convertStylePropertyPixelsToNumber(
|
||||
window.getComputedStyle(element),
|
||||
"margin-left"
|
||||
)
|
||||
).to.equal(8);
|
||||
expect(
|
||||
convertStylePropertyPixelsToNumber(
|
||||
window.getComputedStyle(element),
|
||||
"margin-right"
|
||||
)
|
||||
).to.equal(5);
|
||||
});
|
||||
});
|
|
@ -1,53 +0,0 @@
|
|||
export interface ClientRectWithMargin {
|
||||
width: number;
|
||||
height: number;
|
||||
top: number;
|
||||
bottom: number;
|
||||
left: number;
|
||||
right: number;
|
||||
}
|
||||
|
||||
export function convertStylePropertyPixelsToNumber(
|
||||
computedStyle: CSSStyleDeclaration | null | undefined,
|
||||
property: string | null | undefined
|
||||
): number | void {
|
||||
if (!computedStyle || !property) {
|
||||
return;
|
||||
}
|
||||
|
||||
return parseInt(
|
||||
computedStyle
|
||||
.getPropertyValue(property)
|
||||
.substring(0, computedStyle.getPropertyValue(property).length - 2),
|
||||
10
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the client bounding rectangle including any margins of an element.
|
||||
*/
|
||||
export function getClientRectWithMargin(
|
||||
element: HTMLElement | null | undefined
|
||||
): ClientRectWithMargin | undefined {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rect: DOMRect = element.getBoundingClientRect();
|
||||
const style: CSSStyleDeclaration = window.getComputedStyle(element, null);
|
||||
const clone: ClientRectWithMargin = {
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
top: rect.top,
|
||||
bottom: rect.bottom,
|
||||
left: rect.left,
|
||||
right: rect.right,
|
||||
};
|
||||
|
||||
clone.width += convertStylePropertyPixelsToNumber(style, "margin-left") as number;
|
||||
clone.width += convertStylePropertyPixelsToNumber(style, "margin-right") as number;
|
||||
clone.height += convertStylePropertyPixelsToNumber(style, "margin-top") as number;
|
||||
clone.height += convertStylePropertyPixelsToNumber(style, "margin-bottom") as number;
|
||||
|
||||
return clone;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
export * from "./aria.js";
|
||||
export * from "./array.js";
|
||||
export * from "./class-names.js";
|
||||
export * from "./dom.js";
|
||||
export * from "./events.js";
|
||||
export * from "./html.js";
|
||||
export * from "./key-codes.js";
|
||||
export * from "./localization.js";
|
||||
export * from "./numbers.js";
|
||||
export * from "./strings.js";
|
||||
export * from "./query.js";
|
||||
export * from "./rtl-scroll-converter.js";
|
||||
export * from "./system-colors.js";
|
|
@ -1,49 +0,0 @@
|
|||
/**
|
||||
* String values for use with KeyboardEvent.key
|
||||
*/
|
||||
export const keyAlt: "Alt" = "Alt" as const;
|
||||
export const keyAltGraph: "AltGraph" = "AltGraph" as const;
|
||||
export const keyCapsLock: "CapsLock" = "CapsLock" as const;
|
||||
export const keyControl: "Control" = "Control" as const;
|
||||
export const keyArrowDown: "ArrowDown" = "ArrowDown" as const;
|
||||
export const keyArrowLeft: "ArrowLeft" = "ArrowLeft" as const;
|
||||
export const keyArrowRight: "ArrowRight" = "ArrowRight" as const;
|
||||
export const keyArrowUp: "ArrowUp" = "ArrowUp" as const;
|
||||
export const keyBackspace: "Backspace" = "Backspace" as const;
|
||||
export const keyDelete: "Delete" = "Delete" as const;
|
||||
export const keyEnd: "End" = "End" as const;
|
||||
export const keyEnter: "Enter" = "Enter" as const;
|
||||
export const keyEscape: "Escape" = "Escape" as const;
|
||||
export const keyHome: "Home" = "Home" as const;
|
||||
export const keyFunction: "Fn" = "Fn" as const;
|
||||
export const keyFunctionLock: "FnLock" = "FnLock" as const;
|
||||
export const keyFunction2: "F2" = "F2" as const;
|
||||
export const keyFunction3: "F3" = "F3" as const;
|
||||
export const keyFunction4: "F4" = "F4" as const;
|
||||
export const keyFunction5: "F5" = "F5" as const;
|
||||
export const keyFunction6: "F6" = "F6" as const;
|
||||
export const keyFunction7: "F7" = "F7" as const;
|
||||
export const keyFunction8: "F8" = "F8" as const;
|
||||
export const keyFunction9: "F9" = "F9" as const;
|
||||
export const keyFunction10: "F10" = "F10" as const;
|
||||
export const keyFunction11: "F11" = "F11" as const;
|
||||
export const keyFunction12: "F12" = "F12" as const;
|
||||
export const keyFunction13: "F13" = "F13" as const;
|
||||
export const keyFunction14: "F14" = "F14" as const;
|
||||
export const keyFunction15: "F15" = "F15" as const;
|
||||
export const keyNumLock: "NumLock" = "NumLock" as const;
|
||||
export const keyPageDown: "PageDown" = "PageDown" as const;
|
||||
export const keyPageUp: "PageUp" = "PageUp" as const;
|
||||
export const keyScrollLock: "ScrollLock" = "ScrollLock" as const;
|
||||
export const keyShift: "Shift" = "Shift" as const;
|
||||
export const keySpace: " " = " " as const;
|
||||
export const keyTab: "Tab" = "Tab" as const;
|
||||
|
||||
export const ArrowKeys = {
|
||||
ArrowDown: keyArrowDown,
|
||||
ArrowLeft: keyArrowLeft,
|
||||
ArrowRight: keyArrowRight,
|
||||
ArrowUp: keyArrowUp,
|
||||
} as const;
|
||||
|
||||
export type ArrowKeys = (typeof ArrowKeys)[keyof typeof ArrowKeys];
|
|
@ -1,7 +0,0 @@
|
|||
/**
|
||||
* Expose ltr and rtl strings
|
||||
*/
|
||||
export enum Direction {
|
||||
ltr = "ltr",
|
||||
rtl = "rtl",
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
import { expect } from "chai";
|
||||
import { inRange, limit, wrapInBounds } from "./numbers.js";
|
||||
|
||||
describe("wrapInBounds", () => {
|
||||
it("should not throw if any parameters are null", () => {
|
||||
expect(() => {
|
||||
wrapInBounds(null!, null!, null!);
|
||||
}).not.to.throw();
|
||||
expect(() => {
|
||||
wrapInBounds(1, null!, null!);
|
||||
}).not.to.throw();
|
||||
expect(() => {
|
||||
wrapInBounds(1, 2, 3);
|
||||
}).not.to.throw();
|
||||
expect(() => {
|
||||
wrapInBounds(1, null!, 3);
|
||||
}).not.to.throw();
|
||||
expect(() => {
|
||||
wrapInBounds(1, 2, null!);
|
||||
}).not.to.throw();
|
||||
});
|
||||
|
||||
it("should return `min` if `value` is greater than `max`", () => {
|
||||
expect(wrapInBounds(0, 10, 11)).to.equal(0);
|
||||
expect(wrapInBounds(-10, 0, 1)).to.equal(-10);
|
||||
expect(wrapInBounds(-10, 10, 11)).to.equal(-10);
|
||||
expect(wrapInBounds(10, 20, 30)).to.equal(10);
|
||||
});
|
||||
|
||||
it("should return `max` if `value` is less than `min`", () => {
|
||||
expect(wrapInBounds(0, 10, -10)).to.equal(10);
|
||||
expect(wrapInBounds(-10, 0, -11)).to.equal(0);
|
||||
expect(wrapInBounds(-20, -10, -30)).to.equal(-10);
|
||||
expect(wrapInBounds(-10, 10, -11)).to.equal(10);
|
||||
});
|
||||
|
||||
it("should return the correct value if both min and max are the same", () => {
|
||||
expect(wrapInBounds(0, 0, -1)).to.equal(0);
|
||||
expect(wrapInBounds(0, 0, 1)).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("limit", () => {
|
||||
it("should not throw if any parameters are null", () => {
|
||||
expect(() => {
|
||||
limit(null!, null!, null!);
|
||||
}).not.to.throw();
|
||||
expect(() => {
|
||||
limit(0, null!, null!);
|
||||
}).not.to.throw();
|
||||
expect(() => {
|
||||
limit(0, null!, 1);
|
||||
}).not.to.throw();
|
||||
expect(() => {
|
||||
limit(0, 10, null!);
|
||||
}).not.to.throw();
|
||||
});
|
||||
|
||||
it("should return `min` if `value` is equal to `min`", () => {
|
||||
expect(limit(0, 10, 0)).to.equal(0);
|
||||
});
|
||||
|
||||
it("should return `min` if `value` is greater than `min`", () => {
|
||||
expect(limit(10, 15, -1)).to.equal(10);
|
||||
});
|
||||
|
||||
it("should return `max` if `value` is equal to `max`", () => {
|
||||
expect(limit(0, 10, 10)).to.equal(10);
|
||||
});
|
||||
|
||||
it("should return `max` if `value` is greater than `max`", () => {
|
||||
expect(limit(0, 10, 11)).to.equal(10);
|
||||
});
|
||||
|
||||
it("should return the value if `value` is not less min or greater than max", () => {
|
||||
expect(limit(0, 10, 5)).to.equal(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe("inRange", () => {
|
||||
it("should not throw if any parameters are null", () => {
|
||||
expect(() => {
|
||||
inRange(null!, null!, null!);
|
||||
}).not.to.throw();
|
||||
expect(() => {
|
||||
inRange(0, null!, null!);
|
||||
}).not.to.throw();
|
||||
expect(() => {
|
||||
inRange(0, null!, 1);
|
||||
}).not.to.throw();
|
||||
expect(() => {
|
||||
inRange(0, 10, null!);
|
||||
}).not.to.throw();
|
||||
});
|
||||
|
||||
it("should return `true` if `value` is within range of `min` and `max`", () => {
|
||||
expect(inRange(10, 0, 20)).to.be.true;
|
||||
expect(inRange(10, 20)).to.be.true;
|
||||
});
|
||||
|
||||
it("should return `false` when `value` is less than `min` and `max`", () => {
|
||||
expect(inRange(10, 20, 30)).to.be.false;
|
||||
});
|
||||
|
||||
it("should return `false` when `value` is greater than `min` and `max`", () => {
|
||||
expect(inRange(10, 0, 5)).to.be.false;
|
||||
});
|
||||
|
||||
it("should return `false` when `value` is equal to `max`", () => {
|
||||
expect(inRange(10, 0, 10)).to.be.false;
|
||||
});
|
||||
|
||||
it("should return `true` when `value` is less than `min` and `max` is omitted", () => {
|
||||
expect(inRange(10, 20)).to.be.true;
|
||||
});
|
||||
|
||||
it("should return `false` when `value` is less than 0 and `max` is omitted", () => {
|
||||
expect(inRange(-10, 20)).to.be.false;
|
||||
});
|
||||
});
|
|
@ -1,34 +0,0 @@
|
|||
/**
|
||||
* This method keeps a given value within the bounds of a min and max value. If the value
|
||||
* is larger than the max, the minimum value will be returned. If the value is smaller than the minimum,
|
||||
* the maximum will be returned. Otherwise, the value is returned un-changed.
|
||||
*/
|
||||
export function wrapInBounds(min: number, max: number, value: number): number {
|
||||
if (value < min) {
|
||||
return max;
|
||||
} else if (value > max) {
|
||||
return min;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that a value is between a min and max value. If value is lower than min, min will be returned.
|
||||
* If value is greater than max, max will be returned.
|
||||
*/
|
||||
export function limit(min: number, max: number, value: number): number {
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a number value is within a specified range.
|
||||
*
|
||||
* @param value - the value to check
|
||||
* @param min - the range start
|
||||
* @param max - the range end
|
||||
*/
|
||||
export function inRange(value: number, min: number, max: number = 0): boolean {
|
||||
[min, max] = [min, max].sort((a, b) => a - b);
|
||||
return min <= value && value < max;
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
import { expect } from "chai";
|
||||
import { parseQueryStringParams } from "./query.js";
|
||||
|
||||
describe("parseQueryStringParams", (): void => {
|
||||
it("basic query string", (): void => {
|
||||
const params: Map<string, string> = parseQueryStringParams(
|
||||
"www.microsoft.com?a=12345&b=qwerty"
|
||||
);
|
||||
|
||||
expect(params.size).to.equal(2);
|
||||
expect(params.get("a")).to.equal("12345");
|
||||
expect(params.get("b")).to.equal("qwerty");
|
||||
});
|
||||
|
||||
it("query string not part of a full url", (): void => {
|
||||
const params: Map<string, string> = parseQueryStringParams("a=12345&b=qwerty");
|
||||
|
||||
expect(params.size).to.equal(2);
|
||||
expect(params.get("a")).to.equal("12345");
|
||||
expect(params.get("b")).to.equal("qwerty");
|
||||
});
|
||||
|
||||
it("query string with encoding", (): void => {
|
||||
const params: Map<string, string> = parseQueryStringParams(
|
||||
"www.microsoft.com?a=CHAPTER%201.%20Loomings.%20Call%20me%20Ishmael.%20Some%20years%20ago%E2%80%94never%20mind%20how%20long%20precisely%E2%80%94having%20little%20or%20no%20money%20in%20my%20purse%2C%20and%20nothing%20particular%20to%20interest%20me%20on%20shore%2C%20I%20thought%20I%20would%20sail%20about%20a%20little%20and%20see%20the%20watery%20part%20of%20the%20world.%20It%20is%20a%20way%20I%20have%20of%20driving%20off%20the%20spleen%20and%20regulating%20the%20circulation.%20Whenever%20I%20find%20myself%20growing%20grim%20about%20the%20mouth%3B%20whenever%20it%20is%20a%20damp%2C%20drizzly%20November%20in%20my%20soul%3B%20whenever%20I%20find%20myself%20involuntarily%20pausing%20before%20coffin%20warehouses%2C%20and%20bringing%20up%20the%20rear%20of%20every%20funeral%20I%20meet%3B%20and%20especially%20whenever%20my%20hypos%20get%20such%20an%20upper%20hand%20of%20me%2C%20that%20it%20requires%20a%20strong%20moral%20principle%20to%20prevent%20me%20from%20deliberately%20stepping%20into%20the%20street%2C%20and%20methodically%20knocking%20people%E2%80%99s%20hats%20off%E2%80%94then%2C%20I%20account%20it%20high%20time%20to%20get%20to%20sea%20as%20soon%20as%20I%20can.%20This%20is%20my%20substitute%20for%20pistol%20and%20ball.%20With%20a%20philosophical%20flourish%20Cato%20throws%20himself%20upon%20his%20sword%3B%20I%20quietly%20take%20to%20the%20ship.%20There%20is%20nothing%20surprising%20in%20this.%20If%20they%20but%20knew%20it%2C%20almost%20all%20men%20in%20their%20degree%2C%20some%20time%20or%20other%2C%20cherish%20very%20nearly%20the%20same%20feelings%20towards%20the%20ocean%20with%20me."
|
||||
);
|
||||
|
||||
expect(params.size).to.equal(1);
|
||||
expect(params.get("a")).to.equal(
|
||||
"CHAPTER 1. Loomings. Call me Ishmael. Some years ago—never mind how long precisely—having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world. It is a way I have of driving off the spleen and regulating the circulation. Whenever I find myself growing grim about the mouth; whenever it is a damp, drizzly November in my soul; whenever I find myself involuntarily pausing before coffin warehouses, and bringing up the rear of every funeral I meet; and especially whenever my hypos get such an upper hand of me, that it requires a strong moral principle to prevent me from deliberately stepping into the street, and methodically knocking people’s hats off—then, I account it high time to get to sea as soon as I can. This is my substitute for pistol and ball. With a philosophical flourish Cato throws himself upon his sword; I quietly take to the ship. There is nothing surprising in this. If they but knew it, almost all men in their degree, some time or other, cherish very nearly the same feelings towards the ocean with me."
|
||||
);
|
||||
});
|
||||
|
||||
it("undefined or empty input should return an empty Map", (): void => {
|
||||
const params1: Map<string, string> = parseQueryStringParams("");
|
||||
expect(params1.size).to.equal(0);
|
||||
const params2: Map<string, string> = parseQueryStringParams(undefined!);
|
||||
expect(params2.size).to.equal(0);
|
||||
});
|
||||
|
||||
it("gibberish input should return an empty Map", (): void => {
|
||||
const params: Map<string, string> = parseQueryStringParams(
|
||||
"qwertyuiopasdfghjklzxcvbnm"
|
||||
);
|
||||
|
||||
expect(params.size).to.equal(0);
|
||||
});
|
||||
});
|
|
@ -1,28 +0,0 @@
|
|||
/**
|
||||
* @param input Can be in the form ANYTHING?a=1&b=2&c=3 ... or just a=1&b=2&c=3 ...
|
||||
* all query string keys and values will be run through decodeURIComponent
|
||||
*/
|
||||
export function parseQueryStringParams(input: string): Map<string, string> {
|
||||
const retVal: Map<string, string> = new Map<string, string>();
|
||||
if (typeof input !== "string" || input.length <= 0) {
|
||||
return retVal;
|
||||
}
|
||||
const splitLocation: string[] = input.split("?");
|
||||
let rawQuery: string;
|
||||
if (splitLocation.length === 1) {
|
||||
rawQuery = splitLocation[0];
|
||||
} else {
|
||||
rawQuery = splitLocation[1];
|
||||
}
|
||||
const querySegments: string[] = rawQuery.split("&");
|
||||
for (const querySegment of querySegments) {
|
||||
const paramSegments: string[] = querySegment.split("=");
|
||||
if (paramSegments.length === 2) {
|
||||
retVal.set(
|
||||
decodeURIComponent(paramSegments[0]),
|
||||
decodeURIComponent(paramSegments[1])
|
||||
);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
|
@ -1,341 +0,0 @@
|
|||
import { expect } from "chai";
|
||||
import { RtlScrollConverter } from "./rtl-scroll-converter.js";
|
||||
import { Direction } from "./localization.js";
|
||||
|
||||
function getDummyDiv(): HTMLDivElement {
|
||||
const dummy: HTMLDivElement = document.createElement("div");
|
||||
dummy.appendChild(document.createTextNode("ABCD"));
|
||||
dummy.dir = "rtl";
|
||||
dummy.style.fontSize = "14px";
|
||||
dummy.style.width = "4px";
|
||||
dummy.style.height = "1px";
|
||||
dummy.style.position = "absolute";
|
||||
dummy.style.top = "-1000px";
|
||||
dummy.style.overflow = "scroll";
|
||||
return dummy;
|
||||
}
|
||||
|
||||
describe("RtlScrollConverter", (): void => {
|
||||
it("should not throw on getter", () => {
|
||||
const testElement: HTMLDivElement = getDummyDiv();
|
||||
|
||||
expect(() => {
|
||||
RtlScrollConverter.getScrollLeft(testElement, Direction.ltr);
|
||||
}).not.to.throw();
|
||||
});
|
||||
|
||||
it("should not throw on setter", () => {
|
||||
const testElement: HTMLDivElement = getDummyDiv();
|
||||
|
||||
expect(() => {
|
||||
RtlScrollConverter.setScrollLeft(testElement, 0, Direction.ltr);
|
||||
}).not.to.throw();
|
||||
});
|
||||
|
||||
// note: this test must happen before any rtl calls to getScrollLeft/setScrollLeft in this test suite
|
||||
it("getter and setter start as referencing initial function", () => {
|
||||
expect(RtlScrollConverter["getRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["initialGetRtlScrollConverter"]
|
||||
);
|
||||
expect(RtlScrollConverter["setRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["initialSetRtlScrollConverter"]
|
||||
);
|
||||
});
|
||||
|
||||
it("calling getter with inital function set applies converters", () => {
|
||||
const testElement: HTMLDivElement = getDummyDiv();
|
||||
|
||||
RtlScrollConverter["getRtlScrollLeftConverter"] =
|
||||
RtlScrollConverter["initialGetRtlScrollConverter"];
|
||||
RtlScrollConverter["setRtlScrollLeftConverter"] =
|
||||
RtlScrollConverter["initialSetRtlScrollConverter"];
|
||||
|
||||
expect(RtlScrollConverter["getRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["initialGetRtlScrollConverter"]
|
||||
);
|
||||
expect(RtlScrollConverter["setRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["initialSetRtlScrollConverter"]
|
||||
);
|
||||
|
||||
RtlScrollConverter.getScrollLeft(testElement, Direction.rtl);
|
||||
|
||||
expect(RtlScrollConverter["getRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["directGetRtlScrollConverter"]
|
||||
);
|
||||
expect(RtlScrollConverter["setRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["directSetRtlScrollConverter"]
|
||||
);
|
||||
});
|
||||
|
||||
it("calling setter with inital function set applies converters", () => {
|
||||
const testElement: HTMLDivElement = getDummyDiv();
|
||||
|
||||
RtlScrollConverter["getRtlScrollLeftConverter"] =
|
||||
RtlScrollConverter["initialGetRtlScrollConverter"];
|
||||
RtlScrollConverter["setRtlScrollLeftConverter"] =
|
||||
RtlScrollConverter["initialSetRtlScrollConverter"];
|
||||
|
||||
expect(RtlScrollConverter["getRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["initialGetRtlScrollConverter"]
|
||||
);
|
||||
expect(RtlScrollConverter["setRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["initialSetRtlScrollConverter"]
|
||||
);
|
||||
|
||||
RtlScrollConverter.setScrollLeft(testElement, -1, Direction.rtl);
|
||||
|
||||
expect(RtlScrollConverter["getRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["directGetRtlScrollConverter"]
|
||||
);
|
||||
expect(RtlScrollConverter["setRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["directSetRtlScrollConverter"]
|
||||
);
|
||||
});
|
||||
|
||||
it("directGetRtlScrollConverter returns correct value", () => {
|
||||
const testElement: HTMLDivElement = getDummyDiv();
|
||||
document.body.appendChild(testElement);
|
||||
testElement.scrollLeft = -1;
|
||||
|
||||
expect(RtlScrollConverter["directGetRtlScrollConverter"](testElement)).to.equal(
|
||||
-1
|
||||
);
|
||||
});
|
||||
|
||||
it("invertedGetRtlScrollConverter returns a value <= 0", () => {
|
||||
const testElement: HTMLDivElement = getDummyDiv();
|
||||
document.body.appendChild(testElement);
|
||||
testElement.scrollLeft = 1;
|
||||
|
||||
expect(
|
||||
RtlScrollConverter["invertedGetRtlScrollConverter"](testElement)
|
||||
).to.be.lessThanOrEqual(0);
|
||||
});
|
||||
|
||||
it("reverseGetRtlScrollConverter returns correct value", () => {
|
||||
const testElement: HTMLDivElement = getDummyDiv();
|
||||
testElement.scrollLeft = 0;
|
||||
document.body.appendChild(testElement);
|
||||
|
||||
expect(RtlScrollConverter["reverseGetRtlScrollConverter"](testElement)).to.equal(
|
||||
testElement.scrollLeft - (testElement.scrollWidth - testElement.clientWidth)
|
||||
);
|
||||
|
||||
testElement.scrollLeft = -1;
|
||||
|
||||
expect(RtlScrollConverter["reverseGetRtlScrollConverter"](testElement)).to.equal(
|
||||
-1 - (testElement.scrollWidth - testElement.clientWidth)
|
||||
);
|
||||
});
|
||||
|
||||
it("directSetRtlScrollConverter applies correct value", () => {
|
||||
const testElement: HTMLDivElement = { scrollLeft: 0 } as HTMLDivElement;
|
||||
RtlScrollConverter["directSetRtlScrollConverter"](testElement, -100);
|
||||
expect(testElement.scrollLeft).to.equal(-100);
|
||||
});
|
||||
|
||||
it("invertedSetRtlScrollConverter applies correct value", () => {
|
||||
const testElement: HTMLDivElement = { scrollLeft: 0 } as HTMLDivElement;
|
||||
RtlScrollConverter["invertedSetRtlScrollConverter"](testElement, -100);
|
||||
expect(testElement.scrollLeft).to.equal(100);
|
||||
});
|
||||
|
||||
it("reverseSetRtlScrollConverter applies correct value", () => {
|
||||
const testElement: HTMLDivElement = {
|
||||
scrollLeft: 0,
|
||||
clientWidth: 100,
|
||||
scrollWidth: 200,
|
||||
} as HTMLDivElement;
|
||||
RtlScrollConverter["reverseSetRtlScrollConverter"](testElement, -100);
|
||||
expect(testElement.scrollLeft).to.equal(0);
|
||||
});
|
||||
|
||||
it("getter should not adjust value in ltr mode", () => {
|
||||
const testElement: HTMLDivElement = {
|
||||
scrollLeft: -200,
|
||||
} as HTMLDivElement;
|
||||
|
||||
expect(RtlScrollConverter.getScrollLeft(testElement, Direction.ltr)).to.equal(
|
||||
-200
|
||||
);
|
||||
});
|
||||
|
||||
it("setter should not adjust value in ltr mode", () => {
|
||||
const testElement: HTMLDivElement = {
|
||||
scrollLeft: 0,
|
||||
} as HTMLDivElement;
|
||||
|
||||
RtlScrollConverter.setScrollLeft(testElement, -200, Direction.ltr);
|
||||
expect(testElement.scrollLeft).to.equal(-200);
|
||||
});
|
||||
|
||||
it("generated test element has correct attributes", () => {
|
||||
const testElement: HTMLDivElement = RtlScrollConverter["getTestElement"]();
|
||||
|
||||
expect(testElement.dir).to.equal("rtl");
|
||||
expect(testElement.style.fontSize).to.equal("14px");
|
||||
expect(testElement.style.width).to.equal("4px");
|
||||
expect(testElement.style.height).to.equal("1px");
|
||||
expect(testElement.style.position).to.equal("absolute");
|
||||
expect(testElement.style.top).to.equal("-1000px");
|
||||
expect(testElement.style.overflow).to.equal("scroll");
|
||||
});
|
||||
|
||||
it("applyDirectScrollConverters applies correct converters", () => {
|
||||
RtlScrollConverter["getRtlScrollLeftConverter"] = null!;
|
||||
RtlScrollConverter["setRtlScrollLeftConverter"] = null!;
|
||||
|
||||
expect(RtlScrollConverter["getRtlScrollLeftConverter"]).not.to.equal(
|
||||
RtlScrollConverter["directGetRtlScrollConverter"]
|
||||
);
|
||||
expect(RtlScrollConverter["setRtlScrollLeftConverter"]).not.to.equal(
|
||||
RtlScrollConverter["directSetRtlScrollConverter"]
|
||||
);
|
||||
|
||||
RtlScrollConverter["applyDirectScrollConverters"]();
|
||||
|
||||
expect(RtlScrollConverter["getRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["directGetRtlScrollConverter"]
|
||||
);
|
||||
expect(RtlScrollConverter["setRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["directSetRtlScrollConverter"]
|
||||
);
|
||||
});
|
||||
|
||||
it("applyInvertedScrollConverters applies correct converters", () => {
|
||||
RtlScrollConverter["getRtlScrollLeftConverter"] = null!;
|
||||
RtlScrollConverter["setRtlScrollLeftConverter"] = null!;
|
||||
|
||||
expect(RtlScrollConverter["getRtlScrollLeftConverter"]).not.to.equal(
|
||||
RtlScrollConverter["invertedGetRtlScrollConverter"]
|
||||
);
|
||||
expect(RtlScrollConverter["setRtlScrollLeftConverter"]).not.to.equal(
|
||||
RtlScrollConverter["invertedSetRtlScrollConverter"]
|
||||
);
|
||||
|
||||
RtlScrollConverter["applyInvertedScrollConverters"]();
|
||||
|
||||
expect(RtlScrollConverter["getRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["invertedGetRtlScrollConverter"]
|
||||
);
|
||||
expect(RtlScrollConverter["setRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["invertedSetRtlScrollConverter"]
|
||||
);
|
||||
});
|
||||
|
||||
it("applyReverseScrollConverters applies correct converters", () => {
|
||||
RtlScrollConverter["getRtlScrollLeftConverter"] = null!;
|
||||
RtlScrollConverter["setRtlScrollLeftConverter"] = null!;
|
||||
|
||||
expect(RtlScrollConverter["getRtlScrollLeftConverter"]).not.to.equal(
|
||||
RtlScrollConverter["reverseGetRtlScrollConverter"]
|
||||
);
|
||||
expect(RtlScrollConverter["setRtlScrollLeftConverter"]).not.to.equal(
|
||||
RtlScrollConverter["reverseSetRtlScrollConverter"]
|
||||
);
|
||||
|
||||
RtlScrollConverter["applyReverseScrollConverters"]();
|
||||
|
||||
expect(RtlScrollConverter["getRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["reverseGetRtlScrollConverter"]
|
||||
);
|
||||
expect(RtlScrollConverter["setRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["reverseSetRtlScrollConverter"]
|
||||
);
|
||||
});
|
||||
|
||||
it("isReverse returns true if provided with an element with a positive scroll value", () => {
|
||||
const testElement: HTMLDivElement = {
|
||||
scrollLeft: 1,
|
||||
} as HTMLDivElement;
|
||||
|
||||
expect(RtlScrollConverter["isReverse"](testElement)).to.equal(true);
|
||||
});
|
||||
|
||||
it("isReverse returns false if provided with an element with a 0 scroll value", () => {
|
||||
const testElement: HTMLDivElement = {
|
||||
scrollLeft: 0,
|
||||
} as HTMLDivElement;
|
||||
|
||||
expect(RtlScrollConverter["isReverse"](testElement)).to.equal(false);
|
||||
});
|
||||
|
||||
it("isReverse returns false if provided with an element with a negative scroll value", () => {
|
||||
const testElement: HTMLDivElement = {
|
||||
scrollLeft: -1,
|
||||
} as HTMLDivElement;
|
||||
|
||||
expect(RtlScrollConverter["isReverse"](testElement)).to.equal(false);
|
||||
});
|
||||
|
||||
it("isDirect returns true if provided with an element that accepts a negative scroll value", () => {
|
||||
const testElement: HTMLDivElement = {
|
||||
scrollLeft: 0,
|
||||
} as HTMLDivElement;
|
||||
|
||||
expect(RtlScrollConverter["isDirect"](testElement)).to.equal(true);
|
||||
});
|
||||
|
||||
it("checkForScrollType applies reverse converters if provided with an element with a positive scroll value", () => {
|
||||
const testElement: HTMLDivElement = {
|
||||
scrollLeft: 1,
|
||||
} as HTMLDivElement;
|
||||
|
||||
RtlScrollConverter["getRtlScrollLeftConverter"] = null!;
|
||||
RtlScrollConverter["setRtlScrollLeftConverter"] = null!;
|
||||
|
||||
RtlScrollConverter["checkForScrollType"](testElement);
|
||||
|
||||
expect(RtlScrollConverter["getRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["reverseGetRtlScrollConverter"]
|
||||
);
|
||||
expect(RtlScrollConverter["setRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["reverseSetRtlScrollConverter"]
|
||||
);
|
||||
});
|
||||
|
||||
it("checkForScrollType applies direct converters if provided with an element with a scroll value of 0 that uses negative values", () => {
|
||||
const testElement: HTMLDivElement = {
|
||||
scrollLeft: 0,
|
||||
} as HTMLDivElement;
|
||||
|
||||
RtlScrollConverter["getRtlScrollLeftConverter"] = null!;
|
||||
RtlScrollConverter["setRtlScrollLeftConverter"] = null!;
|
||||
|
||||
RtlScrollConverter["checkForScrollType"](testElement);
|
||||
|
||||
expect(RtlScrollConverter["getRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["directGetRtlScrollConverter"]
|
||||
);
|
||||
expect(RtlScrollConverter["setRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["directSetRtlScrollConverter"]
|
||||
);
|
||||
});
|
||||
|
||||
it("checkForScrollType applies inverted converters if provided with a scroll value of 0 that uses positive values", () => {
|
||||
const testElement: HTMLDivElement = {
|
||||
scrollLeft: 0,
|
||||
} as HTMLDivElement;
|
||||
|
||||
Object.defineProperty(testElement, "scrollLeft", {
|
||||
get(): number {
|
||||
return this._value;
|
||||
},
|
||||
set(newValue: number): void {
|
||||
this._value = Math.abs(newValue);
|
||||
},
|
||||
});
|
||||
|
||||
RtlScrollConverter["getRtlScrollLeftConverter"] = null!;
|
||||
RtlScrollConverter["setRtlScrollLeftConverter"] = null!;
|
||||
|
||||
RtlScrollConverter["checkForScrollType"](testElement);
|
||||
|
||||
expect(RtlScrollConverter["getRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["invertedGetRtlScrollConverter"]
|
||||
);
|
||||
expect(RtlScrollConverter["setRtlScrollLeftConverter"]).to.equal(
|
||||
RtlScrollConverter["invertedSetRtlScrollConverter"]
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,233 +0,0 @@
|
|||
import { canUseDOM } from "exenv-es6";
|
||||
import { Direction } from "./localization.js";
|
||||
|
||||
/**
|
||||
* Standardize left scroll conversion when direction is rtl
|
||||
* inspired by
|
||||
* https://github.com/alitaheri/normalize-scroll-left
|
||||
*/
|
||||
export class RtlScrollConverter {
|
||||
/**
|
||||
* Gets the scrollLeft value of the provided element
|
||||
*/
|
||||
public static getScrollLeft(scrolledElement: Element, direction: Direction): number {
|
||||
if (direction === Direction.rtl) {
|
||||
return RtlScrollConverter.getRtlScrollLeftConverter(scrolledElement);
|
||||
}
|
||||
return scrolledElement.scrollLeft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scrollLeft value of the provided element
|
||||
*/
|
||||
public static setScrollLeft(
|
||||
scrolledElement: Element,
|
||||
scrollValue: number,
|
||||
direction: Direction
|
||||
): void {
|
||||
if (direction === Direction.rtl) {
|
||||
RtlScrollConverter.setRtlScrollLeftConverter(scrolledElement, scrollValue);
|
||||
return;
|
||||
}
|
||||
scrolledElement.scrollLeft = scrollValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* This variable holds the appropriate converter function to get the scrollLeft value
|
||||
* The functions initially assigned triggers a browser check when called which sets
|
||||
* the correct converter based on browser and then invokes it
|
||||
*/
|
||||
private static getRtlScrollLeftConverter: (scrolledElement: Element) => number =
|
||||
RtlScrollConverter.initialGetRtlScrollConverter;
|
||||
|
||||
/**
|
||||
* This variable holds the appropriate converter function to set the scrollLeft value
|
||||
* The functions initially assigned triggers a browser check when called which sets
|
||||
* the correct function based on browser and then invokes it
|
||||
*/
|
||||
private static setRtlScrollLeftConverter: (
|
||||
scrolledElement: Element,
|
||||
scrollValue: number
|
||||
) => void = RtlScrollConverter.initialSetRtlScrollConverter;
|
||||
|
||||
/**
|
||||
* The initial rtl scroll converter getter function, it calls the browser test to set the correct converter
|
||||
* functions and then invokes the getter
|
||||
*/
|
||||
private static initialGetRtlScrollConverter(scrolledElement: Element): number {
|
||||
RtlScrollConverter.initializeRtlScrollConverters();
|
||||
return RtlScrollConverter.getRtlScrollLeftConverter(scrolledElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* The "direct" rtl get scroll converter does not need to tamper with the scrollLeft
|
||||
* values as the browser is already doing the right thing. Content start = 0 and
|
||||
* scrolling left goes negative.
|
||||
*/
|
||||
private static directGetRtlScrollConverter(scrolledElement: Element): number {
|
||||
return scrolledElement.scrollLeft;
|
||||
}
|
||||
|
||||
/**
|
||||
* The "inverted" get scroll converter is used when the browser reports scroll left
|
||||
* as a positive maximum scroll value at content start and then goes to zero as content
|
||||
* is scrolled left
|
||||
*/
|
||||
private static invertedGetRtlScrollConverter(scrolledElement: Element): number {
|
||||
return -Math.abs(scrolledElement.scrollLeft);
|
||||
}
|
||||
|
||||
/**
|
||||
* The "reverse" get scroll converter is used when the browser reports scroll left
|
||||
* as 0 at content start and then goes positive as content is scrolled left
|
||||
*/
|
||||
private static reverseGetRtlScrollConverter(scrolledElement: Element): number {
|
||||
return (
|
||||
scrolledElement.scrollLeft -
|
||||
(scrolledElement.scrollWidth - scrolledElement.clientWidth)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The initial rtl scroll converter setter function, it calls the browser test to set the correct converter
|
||||
* functions and then invokes the setter
|
||||
*/
|
||||
private static initialSetRtlScrollConverter(
|
||||
scrolledElement: Element,
|
||||
newScrollValue: number
|
||||
): void {
|
||||
RtlScrollConverter.initializeRtlScrollConverters();
|
||||
RtlScrollConverter.setRtlScrollLeftConverter(scrolledElement, newScrollValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* The "direct" rtl set scroll converter does not need to tamper with the scrollLeft
|
||||
* values as the browser is already doing the right thing. Content start = 0 and
|
||||
* scrolling left goes negative.
|
||||
*/
|
||||
private static directSetRtlScrollConverter(
|
||||
scrolledElement: Element,
|
||||
newScrollValue: number
|
||||
): void {
|
||||
scrolledElement.scrollLeft = newScrollValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* The "inverted" set scroll converter is used when the browser reports scroll left
|
||||
* as a positive maximum scroll value at content start and then goes to zero as content
|
||||
* is scrolled left
|
||||
*/
|
||||
private static invertedSetRtlScrollConverter(
|
||||
scrolledElement: Element,
|
||||
newScrollValue: number
|
||||
): void {
|
||||
scrolledElement.scrollLeft = Math.abs(newScrollValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* The "reverse" set scroll converter is used when the browser reports scroll left
|
||||
* as 0 at content start and then goes positive as content is scrolled left
|
||||
*/
|
||||
private static reverseSetRtlScrollConverter(
|
||||
scrolledElement: Element,
|
||||
newScrollValue: number
|
||||
): void {
|
||||
const maxScroll: number =
|
||||
scrolledElement.scrollWidth - scrolledElement.clientWidth;
|
||||
scrolledElement.scrollLeft = maxScroll + newScrollValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* detects the appropriate rtl scroll converter functions and assigns them
|
||||
* should only run once
|
||||
*/
|
||||
private static initializeRtlScrollConverters(): void {
|
||||
if (!canUseDOM()) {
|
||||
RtlScrollConverter.applyDirectScrollConverters();
|
||||
return;
|
||||
}
|
||||
const testElement: HTMLDivElement = RtlScrollConverter.getTestElement();
|
||||
document.body.appendChild(testElement);
|
||||
|
||||
RtlScrollConverter.checkForScrollType(testElement);
|
||||
|
||||
document.body.removeChild(testElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* checks the provided test element to determine scroll type
|
||||
* and apply appropriate converters
|
||||
*/
|
||||
private static checkForScrollType(testElement: HTMLDivElement): void {
|
||||
if (RtlScrollConverter.isReverse(testElement)) {
|
||||
RtlScrollConverter.applyReverseScrollConverters();
|
||||
} else {
|
||||
if (RtlScrollConverter.isDirect(testElement)) {
|
||||
RtlScrollConverter.applyDirectScrollConverters();
|
||||
} else {
|
||||
RtlScrollConverter.applyInvertedScrollConverters();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* checks test element initial state for rtl "reverse" mode
|
||||
*/
|
||||
private static isReverse(testElement: HTMLDivElement): boolean {
|
||||
return testElement.scrollLeft > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks test element for rtl "direct" mode
|
||||
*/
|
||||
private static isDirect(testElement: HTMLDivElement): boolean {
|
||||
testElement.scrollLeft = -1;
|
||||
return testElement.scrollLeft < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* apply direct scroll conververters
|
||||
*/
|
||||
private static applyDirectScrollConverters(): void {
|
||||
RtlScrollConverter.setRtlScrollLeftConverter =
|
||||
RtlScrollConverter.directSetRtlScrollConverter;
|
||||
RtlScrollConverter.getRtlScrollLeftConverter =
|
||||
RtlScrollConverter.directGetRtlScrollConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* apply inverted scroll conververters
|
||||
*/
|
||||
private static applyInvertedScrollConverters(): void {
|
||||
RtlScrollConverter.setRtlScrollLeftConverter =
|
||||
RtlScrollConverter.invertedSetRtlScrollConverter;
|
||||
RtlScrollConverter.getRtlScrollLeftConverter =
|
||||
RtlScrollConverter.invertedGetRtlScrollConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* apply reverse scroll conververters
|
||||
*/
|
||||
private static applyReverseScrollConverters(): void {
|
||||
RtlScrollConverter.setRtlScrollLeftConverter =
|
||||
RtlScrollConverter.reverseSetRtlScrollConverter;
|
||||
RtlScrollConverter.getRtlScrollLeftConverter =
|
||||
RtlScrollConverter.reverseGetRtlScrollConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* generate a test element for rtl testing
|
||||
*/
|
||||
private static getTestElement(): HTMLDivElement {
|
||||
const testElement: HTMLDivElement = document.createElement("div");
|
||||
testElement.appendChild(document.createTextNode("ABCD"));
|
||||
testElement.dir = "rtl";
|
||||
testElement.style.fontSize = "14px";
|
||||
testElement.style.width = "4px";
|
||||
testElement.style.height = "1px";
|
||||
testElement.style.position = "absolute";
|
||||
testElement.style.top = "-1000px";
|
||||
testElement.style.overflow = "scroll";
|
||||
return testElement;
|
||||
}
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
import { expect } from "chai";
|
||||
import {
|
||||
format,
|
||||
isNullOrWhiteSpace,
|
||||
pascalCase,
|
||||
spinalCase,
|
||||
startsWith,
|
||||
} from "./strings.js";
|
||||
|
||||
describe("format", (): void => {
|
||||
it("should correctly manage undefined by returning an unformatted string", (): void => {
|
||||
const formatterString: string = "Hello {0} world";
|
||||
|
||||
expect(format(formatterString, undefined!)).to.equal("Hello world");
|
||||
});
|
||||
|
||||
it("should correctly manage null by returning an unformatted string", (): void => {
|
||||
const formatterString: string = "Hello {0} world";
|
||||
|
||||
expect(format(formatterString, null!)).to.equal("Hello world");
|
||||
});
|
||||
|
||||
it("should correctly manage having too many parameters", (): void => {
|
||||
const formatterString: string = "View {0} {1}";
|
||||
|
||||
expect(format(formatterString, "page", "five", "now")).to.equal("View page five");
|
||||
});
|
||||
|
||||
it("should correctly manage a formatter with not enough parameters", (): void => {
|
||||
const formatterString: string = "View {0} {1}";
|
||||
|
||||
expect(format(formatterString, "page")).to.equal("View page {1}");
|
||||
});
|
||||
|
||||
it("should correctly manage empty strings by returning a formatted string with white space", (): void => {
|
||||
const formatterString: string = "Hello {0} world";
|
||||
|
||||
expect(format(formatterString, "")).to.equal("Hello world");
|
||||
});
|
||||
|
||||
it("should correctly manage strings by returning a formatted string", (): void => {
|
||||
const formatterString: string = "Hello {0} world";
|
||||
|
||||
expect(format(formatterString, "foo")).to.equal("Hello foo world");
|
||||
});
|
||||
|
||||
it("should correctly manage multiple strings parameters", (): void => {
|
||||
const formatterString: string = "View {0} {1}";
|
||||
|
||||
expect(format(formatterString, "page", "five")).to.equal("View page five");
|
||||
});
|
||||
|
||||
it("should correctly manage non-formatted strings by returning the initial string", (): void => {
|
||||
const formatterString: string = "Hello";
|
||||
|
||||
expect(format(formatterString, "world")).to.equal("Hello");
|
||||
});
|
||||
|
||||
it("should correctly manage non-formatted strings by returning the initial string", (): void => {
|
||||
const formatterString: string = "Hello";
|
||||
|
||||
expect(format(formatterString, "world")).to.equal("Hello");
|
||||
});
|
||||
});
|
||||
|
||||
describe("isNullOrWhiteSpace", (): void => {
|
||||
it("should correctly manage undefined", () => {
|
||||
expect(isNullOrWhiteSpace(undefined!)).to.equal(true);
|
||||
});
|
||||
it("should correctly manage null", () => {
|
||||
expect(isNullOrWhiteSpace(null!)).to.equal(true);
|
||||
});
|
||||
it("should correctly manage a value with only white space", () => {
|
||||
expect(isNullOrWhiteSpace("\t\n ")).to.equal(true);
|
||||
});
|
||||
it("should correctly manage a value without white space", () => {
|
||||
expect(isNullOrWhiteSpace("foobar")).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("pascalCase", (): void => {
|
||||
it("should correctly manage hyphenated strings", (): void => {
|
||||
expect(pascalCase("string-extensions")).to.equal("StringExtensions");
|
||||
});
|
||||
|
||||
it("should correctly manage strings with whitespace", (): void => {
|
||||
expect(pascalCase(" foo bar ")).to.equal("FooBar");
|
||||
});
|
||||
|
||||
it("should correctly manage all caps strings", (): void => {
|
||||
expect(pascalCase("STRING EXTENSIONS")).to.equal("StringExtensions");
|
||||
});
|
||||
|
||||
it("should no-op on existing pascal case", (): void => {
|
||||
expect(pascalCase("StringExtensions")).to.equal("StringExtensions");
|
||||
});
|
||||
|
||||
it("should correctly manage one capital case with no whitespace", (): void => {
|
||||
expect(pascalCase("thinkIAm")).to.equal("ThinkIAm");
|
||||
});
|
||||
|
||||
it("should correctly manage strings with dashes", (): void => {
|
||||
expect(pascalCase("--foo bar--")).to.equal("FooBar");
|
||||
});
|
||||
|
||||
it("should correctly manage strings with underscores", (): void => {
|
||||
expect(pascalCase("__foo bar__")).to.equal("FooBar");
|
||||
});
|
||||
});
|
||||
|
||||
describe("spinalCase", () => {
|
||||
it("should convert pascalCase strings", (): void => {
|
||||
expect(spinalCase("stringExtensions")).to.equal("string-extensions");
|
||||
});
|
||||
it("should convert CamelCase strings", (): void => {
|
||||
expect(spinalCase("StringExtensions")).to.equal("string-extensions");
|
||||
});
|
||||
it("should convert CamelCase with numbers", (): void => {
|
||||
expect(spinalCase("typeRampMinus1FontSize")).to.equal(
|
||||
"type-ramp-minus-1-font-size"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("startsWith", (): void => {
|
||||
it("should correctly manage undefined", () => {
|
||||
expect(startsWith(undefined!, undefined!)).to.equal(false);
|
||||
expect(startsWith("Hello", undefined!)).to.equal(false);
|
||||
});
|
||||
it("should correctly manage null", () => {
|
||||
expect(startsWith(null!, null!)).to.equal(false);
|
||||
expect(startsWith("Hello", null!)).to.equal(false);
|
||||
});
|
||||
it("should correctly manage searching for an empty string", () => {
|
||||
expect(startsWith("Helloworld", "")).to.equal(false);
|
||||
});
|
||||
it("should correctly manage a string which includes a match but does not start with it", () => {
|
||||
expect(startsWith("HelloWorld", "World")).to.equal(false);
|
||||
});
|
||||
it("should correctly manage finding a valid string that starts with a match", () => {
|
||||
expect(startsWith("start", "start")).to.equal(true);
|
||||
expect(startsWith("start", "star")).to.equal(true);
|
||||
});
|
||||
it("should correctly manage incorrect casing as an invalid match", () => {
|
||||
expect(startsWith("start", "START")).to.equal(false);
|
||||
});
|
||||
});
|
|
@ -1,108 +0,0 @@
|
|||
let uniqueIdCounter: number = 0;
|
||||
|
||||
/**
|
||||
* Generates a unique ID based on incrementing a counter.
|
||||
*/
|
||||
export function uniqueId(prefix: string = ""): string {
|
||||
return `${prefix}${uniqueIdCounter++}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a string from a format specifier and replacement parameters.
|
||||
*/
|
||||
export function format(formatSpecifier: string, ...parameters: string[]): string {
|
||||
return formatSpecifier.replace(
|
||||
/{(\d+)}/g,
|
||||
function (match: string, index: number): any {
|
||||
if (index >= parameters.length) {
|
||||
return match;
|
||||
}
|
||||
|
||||
const value: string = parameters[index];
|
||||
|
||||
if (typeof value !== "number" && !value) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if one string starts with another
|
||||
*/
|
||||
export function startsWith(
|
||||
stringToSearch: string,
|
||||
searchFor: string,
|
||||
position: number = 0
|
||||
): boolean {
|
||||
if (!stringToSearch || !searchFor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return stringToSearch.substr(position, searchFor.length) === searchFor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the specified string is undefined, null, empty, or whitespace.
|
||||
* True if the value is undefined, null, empty, or whitespace, otherwise false.
|
||||
*/
|
||||
export function isNullOrWhiteSpace(value: string): boolean {
|
||||
return !value || !value.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string to Pascal Case
|
||||
* where the first letter of each compound word is capitalized.
|
||||
*/
|
||||
export function pascalCase(value: string): string {
|
||||
let newValue: string = `${value}`
|
||||
.replace(new RegExp(/[-_]+/, "g"), " ")
|
||||
.replace(new RegExp(/[^\w\s]/, "g"), "")
|
||||
.replace(/^\s+|\s+$|\s+(?=\s)/g, "")
|
||||
.replace(
|
||||
new RegExp(/\s+(.)(\w*)/, "g"),
|
||||
($1, $2, $3) => `${$2.toUpperCase() + $3.toLowerCase()}`
|
||||
)
|
||||
.replace(new RegExp(/\w/), s => s.toUpperCase());
|
||||
|
||||
let firstLowerIdx: number = 0;
|
||||
|
||||
for (let i = 0; i < newValue.length; i++) {
|
||||
const currChar: string = newValue.charAt(i);
|
||||
|
||||
if (currChar == currChar.toLowerCase()) {
|
||||
firstLowerIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstLowerIdx > 1) {
|
||||
newValue =
|
||||
`${newValue.charAt(0).toUpperCase()}${newValue
|
||||
.slice(1, firstLowerIdx - 1)
|
||||
.toLowerCase()}` + newValue.slice(firstLowerIdx - 1);
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* converts a string from camelCase or pascalCase to spinal-case
|
||||
* which is an lowercase dash separated naming style.
|
||||
*
|
||||
* An example of spinal case: foo-bar-bat
|
||||
*/
|
||||
export function spinalCase(value: string): string {
|
||||
const valueWithLowerCaseFirstLetter: string = `${value
|
||||
.charAt(0)
|
||||
.toLowerCase()}${value.slice(1)}`;
|
||||
|
||||
return valueWithLowerCaseFirstLetter.replace(
|
||||
/([A-Z]|[0-9])/g,
|
||||
function (match: string, group1: string): string {
|
||||
return `-${group1.toLowerCase()}`;
|
||||
}
|
||||
);
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
/**
|
||||
* Define system colors for use in CSS stylesheets.
|
||||
*
|
||||
* https://drafts.csswg.org/css-color/#css-system-colors
|
||||
*/
|
||||
export enum SystemColors {
|
||||
Canvas = "Canvas",
|
||||
CanvasText = "CanvasText",
|
||||
LinkText = "LinkText",
|
||||
VisitedText = "VisitedText",
|
||||
ActiveText = "ActiveText",
|
||||
ButtonFace = "ButtonFace",
|
||||
ButtonText = "ButtonText",
|
||||
Field = "Field",
|
||||
FieldText = "FieldText",
|
||||
Highlight = "Highlight",
|
||||
HighlightText = "HighlightText",
|
||||
GrayText = "GrayText",
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"outDir": "./dist",
|
||||
"module": "Node16",
|
||||
"moduleResolution": "Node16",
|
||||
"strictNullChecks": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"allowJs": true,
|
||||
"types": ["mocha", "webpack-env", "node"]
|
||||
}
|
||||
}
|
|
@ -9,15 +9,6 @@ Our web component packages.
|
|||
|
||||
The `@microsoft/fast-element` library is a lightweight means to easily build performant, memory-efficient, standards-compliant Web Components. FAST Elements work in every major browser and can be used in combination with any front-end framework or even without a framework. To get up and running with `@microsoft/fast-element` see [the Getting Started guide](https://fast.design/docs/fast-element/getting-started).
|
||||
|
||||
## `@microsoft/fast-foundation`
|
||||
|
||||
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
|
||||
[![npm version](https://badge.fury.io/js/%40microsoft%2Ffast-foundation.svg)](https://badge.fury.io/js/%40microsoft%2Ffast-foundation)
|
||||
|
||||
The `@microsoft/fast-foundation` package is a library of Web Component classes, templates, and other utilities intended to be composed into registered Web Components by design systems (e.g. Fluent Design, Material Design, etc.). The exports of this package can generally be thought of as un-styled base components that implement semantic and accessible markup and behavior.
|
||||
|
||||
This package does not export Web Components registered as [custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) - it exports parts and pieces intended to be *composed* into Web Components, allowing you to implement your own design language by simply applying CSS styles and behaviors without having to write all the JavaScript that's involved in building production-quality component implementations.
|
||||
|
||||
## `@microsoft/fast-ssr`
|
||||
|
||||
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
coverage
|
||||
dist
|
||||
node_modules
|
||||
*.spec.ts
|
||||
!*.pw.spec.ts
|
||||
.eslintrc.*
|
|
@ -1,66 +0,0 @@
|
|||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
extends: [
|
||||
"../../../.eslintrc.js",
|
||||
"plugin:storybook/recommended",
|
||||
],
|
||||
rules: {
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"error",
|
||||
{
|
||||
selector: "typeLike",
|
||||
format: ["UPPER_CASE", "camelCase", "PascalCase"],
|
||||
leadingUnderscore: "allow",
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/ban-types": [
|
||||
"error",
|
||||
{
|
||||
types: {
|
||||
"{}": false,
|
||||
Function: false,
|
||||
Object: false,
|
||||
},
|
||||
extendDefaults: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ["**/*.cjs"],
|
||||
env: {
|
||||
commonjs: true,
|
||||
node: true,
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.ts"],
|
||||
excludedFiles: ["playwright.config.ts"],
|
||||
parserOptions: {
|
||||
parser: "@typescript-eslint/parser",
|
||||
project: path.resolve(__dirname, "./tsconfig.json"),
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/consistent-type-imports": "error",
|
||||
"@typescript-eslint/consistent-type-exports": [
|
||||
"error",
|
||||
{ fixMixedExportsWithInlineTypeSpecifier: false },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.ts"],
|
||||
excludedFiles: ["**/*.stories.ts"],
|
||||
rules: {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{ patterns: ["**/stories/**", "**/*.pw.spec.ts"] },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,4 +0,0 @@
|
|||
.rollupcache
|
||||
coverage
|
||||
tsdoc-metadata.json
|
||||
temp
|
|
@ -1,25 +0,0 @@
|
|||
# Tests
|
||||
dist/dts/__test__/
|
||||
dist/esm/__test__/
|
||||
*.spec.*
|
||||
coverage/
|
||||
|
||||
# Source files
|
||||
src/
|
||||
|
||||
# images
|
||||
images/
|
||||
|
||||
# config files
|
||||
.eslintignore
|
||||
.eslintrc.js
|
||||
.mocharc.json
|
||||
.prettierignore
|
||||
api-extractor.json
|
||||
karma.conf.js
|
||||
rollup.config.js
|
||||
tsconfig.json
|
||||
|
||||
# cache
|
||||
.rollupcache
|
||||
temp
|
|
@ -1 +0,0 @@
|
|||
package-lock=false
|
|
@ -1,3 +0,0 @@
|
|||
coverage/*
|
||||
dist/*
|
||||
*.spec.ts
|
|
@ -1,13 +0,0 @@
|
|||
/**
|
||||
* This is a special story that allows us to load a blank preview page for
|
||||
* playwright tests which need to generate elements and insert them directly,
|
||||
* rather than start with a pre-determined element.
|
||||
*/
|
||||
|
||||
import type { Meta } from "@storybook/html";
|
||||
|
||||
export default {
|
||||
title: "Debug",
|
||||
} as Meta;
|
||||
|
||||
export const Blank = () => "";
|
|
@ -1,23 +0,0 @@
|
|||
import type { StorybookConfig } from "@storybook/html-vite";
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ["../src/**/stories/*.stories.ts", "debug.stories.ts"],
|
||||
framework: "@storybook/html-vite",
|
||||
addons: ["@storybook/addon-essentials"],
|
||||
core: {
|
||||
disableTelemetry: true,
|
||||
disableWhatsNewNotifications: true,
|
||||
},
|
||||
async viteFinal(config) {
|
||||
const { mergeConfig } = await import("vite");
|
||||
|
||||
return mergeConfig(config, {
|
||||
build: { chunkSizeWarningLimit: 1000 },
|
||||
resolve: {
|
||||
alias: [{ find: /^(.*\.svg)$/, replacement: "$1?raw" }],
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
|
@ -1,6 +0,0 @@
|
|||
<style>
|
||||
/* hide the blank debug "story" from the sidebar */
|
||||
[data-item-id*="debug"] {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
|
@ -1,46 +0,0 @@
|
|||
<svg style="display: none">
|
||||
<symbol
|
||||
id="test-icon"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 5.5a1 1 0 100 2 1 1 0 000-2zm-5 1a1 1 0 112 0 1 1 0 01-2 0zm3.5-4a.5.5 0 00-1 0V3h-3C5.67 3 5 3.67 5 4.5v4c0 .83.67 1.5 1.5 1.5h7c.83 0 1.5-.67 1.5-1.5v-4c0-.83-.67-1.5-1.5-1.5h-3v-.5zM6.5 4h7c.28 0 .5.22.5.5v4a.5.5 0 01-.5.5h-7a.5.5 0 01-.5-.5v-4c0-.28.22-.5.5-.5zm3.75 14c2.62-.04 4.2-.6 5.12-1.44A3.52 3.52 0 0016.5 14h.01v-.69c0-1-.81-1.8-1.8-1.8h-3.2v-.01H5.3c-.99 0-1.8.81-1.8 1.81v.7c.04.77.25 1.75 1.13 2.55.93.84 2.5 1.4 5.12 1.44h.5zm-4.94-5.5h9.38c.45 0 .81.37.81.81v.44c0 .69-.13 1.46-.8 2.07C14 16.45 12.66 17 10 17s-4.01-.55-4.7-1.18a2.63 2.63 0 01-.8-2.07v-.44c0-.44.36-.8.8-.8z"
|
||||
/>
|
||||
</symbol>
|
||||
<symbol
|
||||
id="test-icon-2"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.26 4.6a5.21 5.21 0 0 1 9.03 5.22l-.2.34a.5.5 0 0 1-.67.19l-3.47-2-1.93 3.38c1.34.4 2.5 1.33 3.31 2.52h-.09c-.34 0-.66.11-.92.31A4.9 4.9 0 0 0 9.5 12.5a4.9 4.9 0 0 0-3.82 2.06 1.5 1.5 0 0 0-1.01-.3 5.94 5.94 0 0 1 5.31-2.74l2.1-3.68-3.83-2.2a.5.5 0 0 1-.18-.7l.2-.33Zm.92.42 1.7.98.02-.02a8.08 8.08 0 0 1 3.27-2.74 4.22 4.22 0 0 0-4.99 1.78ZM14 7.8c.47-.82.7-1.46.77-2.09a5.8 5.8 0 0 0-.06-1.62 6.96 6.96 0 0 0-2.95 2.41L14 7.8Zm.87.5 1.61.93a4.22 4.22 0 0 0-.74-5.02c.07.56.09 1.1.02 1.63-.1.79-.38 1.56-.89 2.46Zm-9.63 7.3a.5.5 0 0 0-.96.03c-.17.7-.5 1.08-.86 1.3-.38.23-.87.32-1.42.32a.5.5 0 0 0 0 1c.64 0 1.33-.1 1.94-.47.34-.2.64-.5.88-.87a2.96 2.96 0 0 0 4.68-.01 2.96 2.96 0 0 0 4.74-.06c.64.9 1.7 1.41 2.76 1.41a.5.5 0 1 0 0-1c-.98 0-1.96-.64-2.29-1.65a.5.5 0 0 0-.95 0 1.98 1.98 0 0 1-3.79.07.5.5 0 0 0-.94 0 1.98 1.98 0 0 1-3.8-.08Z"
|
||||
/>
|
||||
</symbol>
|
||||
<symbol
|
||||
id="chevron-left-icon"
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M7.35 2.15c.2.2.2.5 0 .7L4.21 6l3.14 3.15a.5.5 0 1 1-.7.7l-3.5-3.5a.5.5 0 0 1 0-.7l3.5-3.5c.2-.2.5-.2.7 0Z"
|
||||
/>
|
||||
</symbol>
|
||||
<symbol
|
||||
id="chevron-right-icon"
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M4.65 2.15a.5.5 0 0 0 0 .7L7.79 6 4.65 9.15a.5.5 0 1 0 .7.7l3.5-3.5a.5.5 0 0 0 0-.7l-3.5-3.5a.5.5 0 0 0-.7 0Z"
|
||||
/>
|
||||
</symbol>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 2.5 KiB |
|
@ -1,178 +0,0 @@
|
|||
<style>
|
||||
:root {
|
||||
--accent-color: #da1a5f;
|
||||
--accent-fill-active: #df3c77;
|
||||
--accent-fill-focus: #dc2969;
|
||||
--accent-fill-hover: #da1a5f;
|
||||
--accent-fill-rest: #dc2969;
|
||||
--accent-foreground-active: #dc2969;
|
||||
--accent-foreground-focus: #da1a5f;
|
||||
--accent-foreground-hover: #b3154e;
|
||||
--accent-foreground-rest: #da1a5f;
|
||||
--ambient-shadow-alpha: calc(0.11 * (2 - var(--background-luminance, 1)));
|
||||
--ambient-shadow-color: rgba(0, 0, 0, var(--ambient-shadow-alpha));
|
||||
--ambient-shadow: 0 0 calc((var(--elevation) * 0.225px) + 2px)
|
||||
var(--ambient-shadow-color);
|
||||
--base-height-multiplier: 10;
|
||||
--base-horizontal-spacing-multiplier: 3;
|
||||
--base-layer-luminance: 1;
|
||||
--body-font: Arial, Helvetica, sans-serif;
|
||||
--clear-button-active: #ededed;
|
||||
--clear-button-hover: #f2f2f2;
|
||||
--control-corner-radius: 4;
|
||||
--density: 0;
|
||||
--design-unit: 4;
|
||||
--direction: ltr;
|
||||
--directional-shadow: 0 calc(var(--elevation) * 0.4px)
|
||||
calc((var(--elevation) * 0.9px)) var(--directional-shadow-color);
|
||||
--directional-shadow-alpha: calc(0.13 * (2 - var(--background-luminance, 1)));
|
||||
--directional-shadow-color: rgba(0, 0, 0, var(--directional-shadow-alpha));
|
||||
--disabled-opacity: 0.3;
|
||||
--elevation: 14;
|
||||
--elevation-shadow: var(--ambient-shadow), var(--directional-shadow);
|
||||
--height-number: (var(--base-height-multiplier) + var(--density)) *
|
||||
var(--design-unit);
|
||||
--fill-color: #ffffff;
|
||||
--focus-stroke-inner: #ffffff;
|
||||
--focus-stroke-outer: #888888;
|
||||
--focus-stroke-width: 2;
|
||||
--foreground-on-accent-active-large: #000000;
|
||||
--foreground-on-accent-active: #000000;
|
||||
--foreground-on-accent-focus-large: #000000;
|
||||
--foreground-on-accent-focus: #ffffff;
|
||||
--foreground-on-accent-hover-large: #000000;
|
||||
--foreground-on-accent-hover: #ffffff;
|
||||
--foreground-on-accent-rest-large: #000000;
|
||||
--foreground-on-accent-rest: #ffffff;
|
||||
--neutral-color: #808080;
|
||||
--neutral-fill-active: #f2f2f2;
|
||||
--neutral-fill-focus: #ffffff;
|
||||
--neutral-fill-hover: #e5e5e5;
|
||||
--neutral-fill-input-active: #ffffff;
|
||||
--neutral-fill-input-focus: #ffffff;
|
||||
--neutral-fill-input-hover: #ffffff;
|
||||
--neutral-fill-input-rest: #ffffff;
|
||||
--neutral-fill-layer-rest: #f7f7f7;
|
||||
--neutral-fill-rest: #ededed;
|
||||
--neutral-fill-stealth-active: #f7f7f7;
|
||||
--neutral-fill-stealth-focus: #ffffff;
|
||||
--neutral-fill-stealth-hover: #f2f2f2;
|
||||
--neutral-fill-stealth-rest: #ffffff;
|
||||
--neutral-fill-strong-active: #838383;
|
||||
--neutral-fill-strong-focus: #767676;
|
||||
--neutral-fill-strong-hover: #616161;
|
||||
--neutral-fill-strong-rest: #767676;
|
||||
--neutral-foreground-hint: #767676;
|
||||
--neutral-foreground-rest: #2b2b2b;
|
||||
--neutral-layer-1: #ffffff;
|
||||
--neutral-layer-2: #e5e5e5;
|
||||
--neutral-layer-3: #dddddd;
|
||||
--neutral-layer-4: #d6d6d6;
|
||||
--neutral-layer-card-container: #f7f7f7;
|
||||
--neutral-layer-floating: #ffffff;
|
||||
--neutral-stroke-active: #d6d6d6;
|
||||
--neutral-stroke-divider-rest: #eaeaea;
|
||||
--neutral-stroke-focus: #bebebe;
|
||||
--neutral-stroke-hover: #979797;
|
||||
--neutral-stroke-rest: #bebebe;
|
||||
--stroke-width: 1;
|
||||
--tree-item-expand-collapse-hover: #e5e5e5;
|
||||
--tree-item-expand-collapse-selected-hover: #e0e0e0;
|
||||
--type-ramp-base-font-size: 14px;
|
||||
--type-ramp-base-line-height: 20px;
|
||||
--type-ramp-minus-1-font-size: 12px;
|
||||
--type-ramp-minus-1-line-height: 16px;
|
||||
--type-ramp-minus-2-font-size: 10px;
|
||||
--type-ramp-minus-2-line-height: 16px;
|
||||
--type-ramp-plus-1-font-size: 16px;
|
||||
--type-ramp-plus-1-line-height: 24px;
|
||||
--type-ramp-plus-2-font-size: 20px;
|
||||
--type-ramp-plus-2-line-height: 28px;
|
||||
--type-ramp-plus-3-font-size: 28px;
|
||||
--type-ramp-plus-3-line-height: 36px;
|
||||
--type-ramp-plus-4-font-size: 34px;
|
||||
--type-ramp-plus-4-line-height: 44px;
|
||||
--type-ramp-plus-5-font-size: 46px;
|
||||
--type-ramp-plus-5-line-height: 56px;
|
||||
--type-ramp-plus-6-font-size: 60px;
|
||||
--type-ramp-plus-6-line-height: 72px;
|
||||
}
|
||||
|
||||
html,
|
||||
.docs-story {
|
||||
background-color: var(--fill-color);
|
||||
color: var(--neutral-foreground-rest);
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
/* Disable zoom transform for Firefox, see https://github.com/storybookjs/storybook/issues/16774 */
|
||||
.docs-story [class^="css-"] {
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#root {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--accent-fill-active: #a01346;
|
||||
--accent-fill-focus: #c01754;
|
||||
--accent-fill-hover: #da1a5f;
|
||||
--accent-fill-rest: #c01754;
|
||||
--accent-foreground-active: #df3874;
|
||||
--accent-foreground-focus: #e1477e;
|
||||
--accent-foreground-hover: #e55e8e;
|
||||
--accent-foreground-rest: #e1477e;
|
||||
--base-layer-luminance: 0.1;
|
||||
--clear-button-active: #3b3b3b;
|
||||
--clear-button-hover: #404040;
|
||||
--fill-color: #1a1a1a;
|
||||
--focus-stroke-inner: #350617;
|
||||
--focus-stroke-outer: #717171;
|
||||
--foreground-on-accent-active-large: #ffffff;
|
||||
--foreground-on-accent-active: #ffffff;
|
||||
--foreground-on-accent-focus-large: #000000;
|
||||
--foreground-on-accent-focus: #ffffff;
|
||||
--foreground-on-accent-hover-large: #000000;
|
||||
--foreground-on-accent-hover: #ffffff;
|
||||
--foreground-on-accent-rest-large: #000000;
|
||||
--foreground-on-accent-rest: #ffffff;
|
||||
--neutral-fill-active: #262626;
|
||||
--neutral-fill-focus: #181818;
|
||||
--neutral-fill-hover: #333333;
|
||||
--neutral-fill-input-active: #181818;
|
||||
--neutral-fill-input-focus: #181818;
|
||||
--neutral-fill-input-hover: #181818;
|
||||
--neutral-fill-input-rest: #181818;
|
||||
--neutral-fill-layer-rest: #212121;
|
||||
--neutral-fill-rest: #2b2b2b;
|
||||
--neutral-fill-stealth-active: #212121;
|
||||
--neutral-fill-stealth-focus: #181818;
|
||||
--neutral-fill-stealth-hover: #262626;
|
||||
--neutral-fill-stealth-rest: #181818;
|
||||
--neutral-fill-strong-active: #767676;
|
||||
--neutral-fill-strong-focus: #838383;
|
||||
--neutral-fill-strong-hover: #979797;
|
||||
--neutral-fill-strong-rest: #838383;
|
||||
--neutral-foreground-hint: #838383;
|
||||
--neutral-foreground-rest: #e8e8e8;
|
||||
--neutral-layer-1: #181818;
|
||||
--neutral-layer-2: #101010;
|
||||
--neutral-layer-3: #000000;
|
||||
--neutral-layer-4: #000000;
|
||||
--neutral-layer-card-container: #101010;
|
||||
--neutral-layer-floating: #292929;
|
||||
--neutral-stroke-active: #424242;
|
||||
--neutral-stroke-divider-rest: #2e2e2e;
|
||||
--neutral-stroke-focus: #5a5a5a;
|
||||
--neutral-stroke-hover: #808080;
|
||||
--neutral-stroke-rest: #5a5a5a;
|
||||
--tree-item-expand-collapse-hover: #333333;
|
||||
--tree-item-expand-collapse-selected-hover: #383838;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,19 +0,0 @@
|
|||
/// <reference types="vite/client" />
|
||||
|
||||
// Import all component definitions. This is a vite-specific feature:
|
||||
// https://vitejs.dev/guide/features.html#glob-import
|
||||
const modules = import.meta.glob("../src/**/*.register.ts");
|
||||
|
||||
for (const path in modules) {
|
||||
modules[path]();
|
||||
}
|
||||
|
||||
// Provide the `html` tag function as a global. This is needed for some tests.
|
||||
(async () => {
|
||||
const { html } = await import("@microsoft/fast-element");
|
||||
|
||||
Object.defineProperty(globalThis, "html", {
|
||||
value: html,
|
||||
writable: false,
|
||||
});
|
||||
})();
|
|
@ -1,4 +0,0 @@
|
|||
# Acknowledgements
|
||||
|
||||
* A huge thank-you to [TypeScript](www.typescriptlang.org/) for providing an amazing language, build tools, and at least one [code sample](./src/utilities/apply-mixins.ts) we've shamelessly stolen.
|
||||
* Big thanks to https://github.com/fkleuver and the https://github.com/aurelia/aurelia project for [Dependency Injection](./src/di/di.ts) code and core tests.
|
|
@ -1,217 +0,0 @@
|
|||
import fs from "fs";
|
||||
import { default as os } from "os";
|
||||
import { customElementsManifestToMarkdown } from "@custom-elements-manifest/to-markdown";
|
||||
|
||||
// new line and double new line shorthands
|
||||
const LF = os.EOL;
|
||||
const LF2 = LF + LF;
|
||||
|
||||
// Read the custom-elements.json file and parse the JSON
|
||||
const fullManifest = JSON.parse(fs.readFileSync("dist/custom-elements.json", "utf-8"));
|
||||
|
||||
// Filter out template modules and ensure that they are in alphabetical order
|
||||
const modules = fullManifest.modules
|
||||
.filter(module => module.path.indexOf("template.ts") === -1)
|
||||
.sort((a, b) =>
|
||||
getComponentNameFromPath(a.path).localeCompare(getComponentNameFromPath(b.path))
|
||||
);
|
||||
|
||||
// Loop through the manifest grouping modules from the same folder in order to produce one markdown file per component.
|
||||
for (var i = 0, modulesLength = modules.length; i < modulesLength; i++) {
|
||||
// We only care about javascript-modules.
|
||||
if (modules[i].kind === "javascript-module") {
|
||||
// Create a new manifest object just for this component.
|
||||
let componentManifest = {};
|
||||
componentManifest.schemaVersion = fullManifest.schemaVersion;
|
||||
componentManifest.readme = fullManifest.readme;
|
||||
componentManifest.modules = [];
|
||||
|
||||
// Determine the component name from the path. This name will be used to group following modules.
|
||||
const currName = getComponentNameFromPath(modules[i].path);
|
||||
const componentIndex = i;
|
||||
|
||||
componentManifest.modules.push(modules[i]);
|
||||
|
||||
// Continue looping through the main manifest, adding modules to the small manifest until we find a module with a different name.
|
||||
// Include modules with names like "component-item" so components like Accordion and Accordion-item are included in the same file.
|
||||
// Include modules with names like "component-label" so components like Slider and Slider-label are included in the same file.
|
||||
while (
|
||||
i < modules.length - 1 &&
|
||||
(currName === getComponentNameFromPath(modules[i + 1].path) ||
|
||||
currName + "-item" === getComponentNameFromPath(modules[i + 1].path) ||
|
||||
currName + "-label" === getComponentNameFromPath(modules[i + 1].path))
|
||||
) {
|
||||
componentManifest.modules.push(modules[i + 1]);
|
||||
i++;
|
||||
}
|
||||
|
||||
// Special logic for the "tab" components.
|
||||
// If the current module is "tabs" include the previous two modules which should be "tab" and "tab-panel".
|
||||
if (currName === "tabs") {
|
||||
componentManifest.modules.push(modules[i - 1]);
|
||||
componentManifest.modules.push(modules[i - 2]);
|
||||
}
|
||||
|
||||
// enclose html tags in `` to prevent tags in comments from confusing docusaurus
|
||||
// and remove new line characters from descriptions
|
||||
componentManifest.modules.forEach(module => {
|
||||
module.declarations?.forEach(dec => {
|
||||
if (dec.description) {
|
||||
dec.description = replaceJSDOCLinksWithMDLinks(
|
||||
fixTagsInText(dec.description.replaceAll(LF, " "))
|
||||
);
|
||||
}
|
||||
if (dec.default) {
|
||||
dec.default = replaceJSDOCLinksWithMDLinks(
|
||||
fixTagsInText(dec.default.replaceAll(LF, " "))
|
||||
);
|
||||
}
|
||||
if (dec.type) {
|
||||
dec.type.text = cleanUpVariableTypes(dec.type.text);
|
||||
}
|
||||
dec.members?.forEach(member => {
|
||||
if (member.description) {
|
||||
member.description = replaceJSDOCLinksWithMDLinks(
|
||||
fixTagsInText(member.description.replaceAll(LF, " "))
|
||||
);
|
||||
}
|
||||
if (member.default) {
|
||||
member.default = replaceJSDOCLinksWithMDLinks(
|
||||
fixTagsInText(member.default.replaceAll(LF, " "))
|
||||
);
|
||||
}
|
||||
if (member.return?.type?.text) {
|
||||
// these are already rendered inside of back-ticks so we only need to remove new lines
|
||||
member.return.type.text = member.return.type.text.replaceAll(
|
||||
LF,
|
||||
" "
|
||||
);
|
||||
}
|
||||
});
|
||||
dec.attributes?.forEach(attr => {
|
||||
if (attr.description) {
|
||||
attr.description = replaceJSDOCLinksWithMDLinks(
|
||||
fixTagsInText(attr.description.replaceAll(LF, " "))
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Convert the single component manifest into a markdown string.
|
||||
let markdown = customElementsManifestToMarkdown(componentManifest, {
|
||||
headingOffset: 1,
|
||||
private: "hidden",
|
||||
omitDeclarations: ["exports"],
|
||||
omitSections: ["static-methods"],
|
||||
});
|
||||
|
||||
// Replace our < and > markers with backticks and < >
|
||||
// This is necessary because customElementsManifestToMarkdown escapes the backticks during the conversion
|
||||
// and we don't want that because then docusaurus will see the tags as real tags instead of just text.
|
||||
markdown = markdown.replaceAll("REPLACELT", "`<").replaceAll("REPLACEGT", ">`");
|
||||
|
||||
// Clean up some additional formatting issues
|
||||
// Remove the file source header
|
||||
markdown = markdown.replaceAll(/## `src.*`:/g, "");
|
||||
// Fix escape of colon in urls
|
||||
markdown = markdown.replaceAll("https\\:", "https:");
|
||||
// Fix escape of . in some urls
|
||||
markdown = markdown.replaceAll("www\\.w3", "www.w3");
|
||||
// Fix escape of open bracket on links
|
||||
markdown = markdown.replaceAll("\\[", "[");
|
||||
// Fix escape of open paren on links
|
||||
markdown = markdown.replaceAll("\\(", "(");
|
||||
|
||||
// Replace \| with 'or'
|
||||
markdown = markdown.replaceAll("\\|", "or");
|
||||
|
||||
// Get the README.md file
|
||||
let path = modules[componentIndex].path.split("/");
|
||||
path[path.length - 1] = "README.md";
|
||||
path = path.join("/");
|
||||
|
||||
// If a README.md file exists
|
||||
if (fs.existsSync(path)) {
|
||||
// Read the contents of the file
|
||||
let readMe = fs.readFileSync(path, "utf-8");
|
||||
|
||||
// Find the location of the "## API" section
|
||||
let apiLoc = readMe.indexOf("## API");
|
||||
|
||||
// Find the location of the "## Additional resources" section
|
||||
let resourcesLoc = readMe.indexOf("## Additional resources");
|
||||
|
||||
// If the API section does not exist then create it either just
|
||||
// above the Additional Resources section or at the end of the file.
|
||||
if (apiLoc === -1) {
|
||||
// no API section yet so add it
|
||||
if (resourcesLoc > 0) {
|
||||
// add API section above Additional Resources
|
||||
readMe = readMe.replace(
|
||||
"## Additional resources",
|
||||
"## API" + LF2 + "## Additional resources"
|
||||
);
|
||||
} else {
|
||||
// add API to the end
|
||||
readMe += LF2 + "## API";
|
||||
}
|
||||
|
||||
// Get the updated locations
|
||||
apiLoc = readMe.indexOf("## API");
|
||||
resourcesLoc = readMe.indexOf("## Additional resources");
|
||||
}
|
||||
|
||||
// customElementsManifestToMarkdown() hard codes line endings as '/n'. This causes GIT to detect changes
|
||||
// to the file on windows environments even if nothing but the line endings change. If the os.EOL is '\r\n'
|
||||
// then replace all '\n' in the markdown with '\r\n'.
|
||||
if (LF === "\r\n") {
|
||||
markdown = markdown.replaceAll("\n", "\r\n");
|
||||
}
|
||||
|
||||
// Replace everything in between the API and Additional Resources sections with the
|
||||
// updated markdown.
|
||||
const startIndex = apiLoc;
|
||||
const endIndex = resourcesLoc >= 0 ? resourcesLoc : readMe.length;
|
||||
readMe = readMe.replace(
|
||||
readMe.slice(startIndex, endIndex),
|
||||
"## API" + LF2 + markdown + LF2
|
||||
);
|
||||
|
||||
// Replace the README.md file with the new content.
|
||||
fs.writeFileSync(path, readMe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getComponentNameFromPath(path) {
|
||||
return path.split("/")[1];
|
||||
}
|
||||
|
||||
function fixTagsInText(text) {
|
||||
// replace < and > characters in text with something that can be easily replaced later.
|
||||
return text.replaceAll(/<.*>/gi, match => {
|
||||
return match.replace("<", "REPLACELT").replace(">", "REPLACEGT");
|
||||
});
|
||||
}
|
||||
|
||||
function replaceJSDOCLinksWithMDLinks(text) {
|
||||
// Replace jsdoc links with markdown links
|
||||
// [TEXT]{@link URL} => [TEXT](URL)
|
||||
// {@link URL} => URL
|
||||
// {@link URL TEXT} => [TEXT](URL)
|
||||
// {@link URL | TEXT} => [TEXT](URL)
|
||||
return text
|
||||
.replace(/\[(.*)\]\{@link (\S*)\}/gm, "[$1]($2)")
|
||||
.replace(/\{@link (\S*)\}/gm, "$1")
|
||||
.replace(/\{@link (\S*)\s*\|?\s*([^}]+?)\s*\}/gm, "[$2]($1)");
|
||||
}
|
||||
|
||||
function cleanUpVariableTypes(text) {
|
||||
// Remove block comments
|
||||
text = text.replace(/\/\*(.|\s)*?\*\//gm, "");
|
||||
// Remove inline comments, line breaks, and extra spaces
|
||||
return text
|
||||
.replace(/\/\/.*$/gm, "")
|
||||
.replace(/\s+/gm, " ");
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,58 +0,0 @@
|
|||
# FAST Foundation
|
||||
|
||||
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
|
||||
[![npm version](https://badge.fury.io/js/%40microsoft%2Ffast-foundation.svg)](https://badge.fury.io/js/%40microsoft%2Ffast-foundation)
|
||||
|
||||
The `fast-foundation` package is a library of Web Component classes, templates, and other utilities intended to be composed into registered Web Components by design systems (e.g. Fluent Design, Material Design, etc.). The exports of this package can generally be thought of as un-styled base components that implement semantic and accessible markup and behavior.
|
||||
|
||||
This package does not export Web Components registered as [custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) - it exports parts and pieces intended to be *composed* into Web Components, allowing you to implement your own design language by simply applying CSS styles and behaviors without having to write all the JavaScript that's involved in building production-quality component implementations.
|
||||
|
||||
## Installation
|
||||
|
||||
### From NPM
|
||||
|
||||
To install the `fast-foundation` library, use either `npm` or `yarn` as follows:
|
||||
|
||||
```shell
|
||||
npm install --save @microsoft/fast-foundation
|
||||
```
|
||||
|
||||
```shell
|
||||
yarn add @microsoft/fast-foundation
|
||||
```
|
||||
|
||||
Within your JavaScript or TypeScript code, you can then import library APIs like this:
|
||||
|
||||
```ts
|
||||
import { Anchor } from '@microsoft/fast-foundation';
|
||||
```
|
||||
|
||||
Looking for a setup that integrates with a particular front-end framework or bundler? Check out [our integration docs](https://fast.design/docs/integrations/introduction).
|
||||
|
||||
### From CDN
|
||||
|
||||
A pre-bundled script that contains all APIs needed to use FAST Foundation is available on CDN. You can use this script by adding [`type="module"`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) to the script element and then importing from the CDN.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script type="module">
|
||||
import { Anchor } from "https://cdn.jsdelivr.net/npm/@microsoft/fast-foundation/dist/fast-foundation.min.js";
|
||||
|
||||
// your code here
|
||||
</script>
|
||||
</head>
|
||||
<!-- ... -->
|
||||
</html>
|
||||
```
|
||||
|
||||
The markup above always references the latest release. When deploying to production, you will want to ship with a specific version. Here's an example of the markup for that:
|
||||
|
||||
```html
|
||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@microsoft/fast-foundation@2.26.2/dist/fast-foundation.min.js"></script>
|
||||
```
|
||||
|
||||
:::note
|
||||
For simplicity, examples throughout the documentation will assume the library has been installed from NPM, but you can always replace the import location with the CDN URL.
|
||||
:::
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
"extends": "../../../api-extractor.json",
|
||||
"mainEntryPointFilePath": "./dist/dts/index.d.ts",
|
||||
"compiler": {
|
||||
"tsconfigFilePath": "./tsconfig.build.json"
|
||||
},
|
||||
"dtsRollup": {
|
||||
"enabled": true,
|
||||
"untrimmedFilePath": "./dist/fast-foundation.untrimmed.d.ts",
|
||||
"betaTrimmedFilePath": "./dist/fast-foundation.d.ts"
|
||||
},
|
||||
"messages": {
|
||||
"extractorMessageReporting": {
|
||||
"ae-different-release-tags": {
|
||||
"logLevel": "none",
|
||||
"addToApiReportFile": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
import { getClassMemberDoc } from "@custom-elements-manifest/analyzer/src/utils/manifest-helpers.js";
|
||||
import { parse } from "comment-parser";
|
||||
|
||||
/**
|
||||
* Code for this plugin was mostly copied from: https://github.com/bennypowers/cem-plugins/tree/main/plugins/cem-plugin-jsdoc-example
|
||||
*/
|
||||
|
||||
/**
|
||||
* Does the AST node have JSDoc tags?
|
||||
* @template {import('typescript').Node} N
|
||||
* @param {N} node
|
||||
* @return {node is N & { jsDoc: import('typescript').JSDoc[] }}
|
||||
*/
|
||||
function hasJsDocComments(node) {
|
||||
return "jsDoc" in node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a manifest and a declaration name, get the declaration doc
|
||||
* @param {Partial<import("custom-elements-manifest/schema").Module>} moduleDoc
|
||||
* @param {string} name
|
||||
* @return {import("custom-elements-manifest/schema").Declaration}
|
||||
*/
|
||||
function getDeclarationDoc(moduleDoc, name) {
|
||||
return moduleDoc.declarations.find(x => x.name === name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix a Node's JSDoc link comments
|
||||
* @param {import('typescript').Node & { jsDoc: import('typescript').JSDoc[] }} node
|
||||
* @param {import("custom-elements-manifest/schema").Declaration} doc
|
||||
* @param {*} context
|
||||
* @param {number} [offset=0]
|
||||
*/
|
||||
function fixLinks(node, doc, context, offset = 0) {
|
||||
node?.jsDoc?.forEach(jsDoc => {
|
||||
const parsed = parse(jsDoc?.getFullText());
|
||||
parsed?.forEach(parsedJsDoc => {
|
||||
if (
|
||||
parsedJsDoc.description &&
|
||||
parsedJsDoc.description.indexOf("{@link") >= 0
|
||||
) {
|
||||
doc.description = parsedJsDoc.description.trim();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {import('@custom-elements-manifest/analyzer').Plugin}
|
||||
*/
|
||||
export function jsdocLinkFix() {
|
||||
return {
|
||||
name: "jsdoc-example",
|
||||
analyzePhase({ ts, node, moduleDoc, context }) {
|
||||
if (!hasJsDocComments(node)) return;
|
||||
if (ts.isClassDeclaration(node)) {
|
||||
const className = node.name.getText();
|
||||
const classDoc = getDeclarationDoc(moduleDoc, className);
|
||||
if (!classDoc)
|
||||
return (
|
||||
context.dev &&
|
||||
console.warn(
|
||||
`[jsdoc-example]: Could not find class ${className} in module doc for path ${moduleDoc.path}`
|
||||
)
|
||||
);
|
||||
fixLinks(node, classDoc, context);
|
||||
} else if (ts.isPropertyDeclaration(node)) {
|
||||
const propertyName = node.name.getText();
|
||||
const className = node.parent?.name?.getText?.();
|
||||
const classDoc = getDeclarationDoc(moduleDoc, className);
|
||||
if (!classDoc) return;
|
||||
const isStatic = node.modifiers?.some?.(
|
||||
x => x.kind === ts.SyntaxKind.StaticKeyword
|
||||
);
|
||||
const propertyDoc = getClassMemberDoc(
|
||||
moduleDoc,
|
||||
className,
|
||||
propertyName,
|
||||
isStatic
|
||||
);
|
||||
if (!propertyDoc)
|
||||
return (
|
||||
context.dev &&
|
||||
console.warn(
|
||||
`[jsdoc-example]: Could not find property ${propertyName} of ${className} in module doc for path ${moduleDoc.path}`
|
||||
)
|
||||
);
|
||||
fixLinks(node, propertyDoc, context, 1);
|
||||
} else if (ts.isMethodDeclaration(node)) {
|
||||
if (!ts.isClassDeclaration(node.parent)) return;
|
||||
const methodName = node.name.getText();
|
||||
const className = node.parent.name.getText();
|
||||
const classDoc = getDeclarationDoc(moduleDoc, className);
|
||||
if (!classDoc) return;
|
||||
const isStatic = node.modifiers?.some?.(
|
||||
x => x.kind === ts.SyntaxKind.StaticKeyword
|
||||
);
|
||||
const methodDoc = getClassMemberDoc(
|
||||
moduleDoc,
|
||||
className,
|
||||
methodName,
|
||||
isStatic
|
||||
);
|
||||
if (!methodDoc)
|
||||
return (
|
||||
context.dev &&
|
||||
console.warn(
|
||||
`[jsdoc-example]: Could not find method ${methodName} of ${className} in module doc for path ${moduleDoc.path}`
|
||||
)
|
||||
);
|
||||
fixLinks(node, methodDoc, context, 1);
|
||||
} else if (ts.isFunctionDeclaration(node)) {
|
||||
const functionName = node.name.getText();
|
||||
const functionDoc = getDeclarationDoc(moduleDoc, functionName);
|
||||
if (!functionDoc)
|
||||
return (
|
||||
context.dev &&
|
||||
console.warn(
|
||||
`[jsdoc-example]: Could not find function ${functionName} in module doc for path ${moduleDoc.path}`
|
||||
)
|
||||
);
|
||||
fixLinks(node, functionDoc, context);
|
||||
} else if (ts.isVariableStatement(node)) {
|
||||
node.declarationList?.declarations.forEach(dec => {
|
||||
const name = dec.name.getText();
|
||||
const doc = getDeclarationDoc(moduleDoc, name);
|
||||
if (!doc)
|
||||
return (
|
||||
context.dev &&
|
||||
console.warn(
|
||||
`[jsdoc-example]: Could not find variable ${name} in module doc for path ${moduleDoc.path}`
|
||||
)
|
||||
);
|
||||
fixLinks(node, doc, context);
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import { jsdocLinkFix } from "./custom-elements-manifest-plugins.mjs";
|
||||
|
||||
export default {
|
||||
/** Globs to analyze */
|
||||
globs: ["src/**/*.ts"],
|
||||
/** Globs to exclude */
|
||||
exclude: [
|
||||
"src/*.ts",
|
||||
"src/__test__/*",
|
||||
"src/di/*",
|
||||
"src/design-token/*",
|
||||
"src/**/*.md",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/index.ts",
|
||||
"src/**/stories/*",
|
||||
],
|
||||
/** Directory to output CEM to */
|
||||
outdir: "dist",
|
||||
/** Run in dev mode, provides extra logging */
|
||||
dev: false,
|
||||
/** Enable special handling for fast */
|
||||
fast: true,
|
||||
plugins: [jsdocLinkFix()],
|
||||
};
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,155 +0,0 @@
|
|||
---
|
||||
id: angular
|
||||
title: Angular
|
||||
sidebar_label: Angular
|
||||
custom_edit_url: https://github.com/microsoft/fast/edit/master/packages/web-components/fast-foundation/docs/integrations/angular.md
|
||||
description: FAST integrates nicely with Angular. Let's take a look at how you can set up an Angular project, starting from scratch.
|
||||
---
|
||||
|
||||
FAST integrates nicely with Angular. Let's take a look at how you can set up an Angular project, starting from scratch.
|
||||
|
||||
## Setting up the Angular project
|
||||
|
||||
First, you'll need to make sure that you have Node.js installed. You can learn more and download that [on the official site](https://nodejs.org/).
|
||||
|
||||
With Node.js installed, you can run the following command to install the Angular CLI:
|
||||
|
||||
```shell
|
||||
npm install -g @angular/cli
|
||||
```
|
||||
|
||||
With the CLI installed, you have access to the `ng` command-line interface. This can be used to create a new Angular project. For example, to create a new Angular App named "fast-angular", you would use the following command:
|
||||
|
||||
```shell
|
||||
ng new fast-angular
|
||||
```
|
||||
|
||||
Follow the prompts, answering each question in turn. When the CLI completes, you should have a basic runnable Angular application.
|
||||
|
||||
## Configuring packages
|
||||
|
||||
Next, we'll install the FAST packages, along with supporting libraries. To do that, run this command from your new project folder:
|
||||
|
||||
```shell
|
||||
npm install --save @microsoft/fast-components @microsoft/fast-element
|
||||
```
|
||||
|
||||
## Using the components
|
||||
|
||||
With all the basic pieces in place, let's run our app in dev mode with `ng serve --open`. The Angular CLI should build your project and make it available on localhost. Right now, it displays a basic welcome message, since we haven't added any code or interesting HTML. Let's change that.
|
||||
|
||||
First, open your `src/main.ts` file and add the following code:
|
||||
|
||||
```ts
|
||||
import {
|
||||
provideFASTDesignSystem,
|
||||
fastCard,
|
||||
fastButton,
|
||||
fastTextField
|
||||
} from '@microsoft/fast-components';
|
||||
|
||||
provideFASTDesignSystem()
|
||||
.register(
|
||||
fastCard(),
|
||||
fastButton(),
|
||||
fastTextField()
|
||||
);
|
||||
```
|
||||
|
||||
This code uses the FAST Design System to register `<fast-card>`, `<fast-button>` and `<fast-text-field>` components. Once you save, the dev server will rebuild and refresh your browser. However, you still won't see anything. To get some UI showing up, we need to write some HTML that uses our components. Replace the HTML template in your `app/app.component.html` file with the following markup:
|
||||
|
||||
```html
|
||||
<fast-card>
|
||||
<h2>{{title}}</h2>
|
||||
<fast-text-field [(ngModel)]='exampleTextField' name='exampleTextField' ngDefaultControl placeholder="Enter Some Text"></fast-text-field>
|
||||
<fast-button appearance="accent" (click)="onClick()">Click Me</fast-button>
|
||||
</fast-card>
|
||||
```
|
||||
|
||||
Replace the code in your `app/app.component.ts` file contents with this:
|
||||
|
||||
```ts
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'fast-angular';
|
||||
|
||||
exampleTextField = '';
|
||||
|
||||
onClick() {
|
||||
console.log(this.exampleTextField);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To allow an NgModule to contain Non-Angular element names, add the following code in your `app/app.module.ts` file:
|
||||
|
||||
```ts
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
|
||||
@NgModule({
|
||||
schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
|
||||
})
|
||||
```
|
||||
|
||||
Moreover the "[(ngModel)]" might give you warnings that "Event can't be assigned to String", this can be fixed by importing FormsModule in the `app/app.module.ts` file like below which contains the above Code too for completeness:
|
||||
```ts
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule, FormsModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent],
|
||||
schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
|
||||
|
||||
})
|
||||
```
|
||||
To add a splash of style, replace the `app/app.component.css` file contents with this:
|
||||
|
||||
```css
|
||||
fast-card {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
fast-text-field {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--type-ramp-plus-5-font-size);
|
||||
line-height: var(--type-ramp-plus-5-line-height);
|
||||
}
|
||||
|
||||
fast-card > fast-button {
|
||||
align-self: flex-end;
|
||||
}
|
||||
```
|
||||
|
||||
:::note
|
||||
|
||||
Third party controls require a ControlValueAccessor for writing a value and listening to changes on input elements. Add ngDefaultControl attribute to your component to have two-way binding working with FormControlDirective, FormControlName, or NgModel directives:
|
||||
|
||||
:::
|
||||
|
||||
```html
|
||||
<fast-text-field placeholder="name" id="name" formControlName="name" ngDefaultControl></fast-text-field>
|
||||
```
|
||||
|
||||
Congratulations! You're now set up to use FAST and Angular!
|
|
@ -1,107 +0,0 @@
|
|||
---
|
||||
id: aspnet
|
||||
title: ASP.NET
|
||||
sidebar_label: ASP.NET
|
||||
custom_edit_url: https://github.com/microsoft/fast/edit/master/packages/web-components/fast-foundation/docs/integrations/aspnet.md
|
||||
description: FAST works naturally with ASP.NET server-side development, by simply adding a script tag and using the custom HTML elements. Let's take a look at how to set things up.
|
||||
keywords:
|
||||
- asp.net
|
||||
---
|
||||
|
||||
FAST works naturally with ASP.NET server-side development, by simply adding a script tag and using the custom HTML elements. Let's take a look at how to set things up.
|
||||
|
||||
## Setting up the ASP.NET project
|
||||
|
||||
First, you'll need to make sure that you have the .NET SDK installed. You can learn more and download that [on the official site](https://dotnet.microsoft.com/download).
|
||||
|
||||
With the SDK installed, you have access to the `dotnet` command-line interface. This can be used to create a new ASP.NET project. For example, to create a new ASP.NET Core MVC Web App named "fast-aspnet", you would use the following command:
|
||||
|
||||
```shell
|
||||
dotnet new mvc -o fast-aspnet
|
||||
```
|
||||
|
||||
Create a project using the command above if you don't already have one. When the CLI completes, you should have a basic runnable ASP.NET Core MVC application.
|
||||
|
||||
## Configuring scripts
|
||||
|
||||
Now that we've got our basic project setup, we need to add our web components script and update ASP.NET accordingly. You can either add the script from our CDN directly, or you can install it with NPM and then add that.
|
||||
|
||||
To add a CDN script for `fast-components` use the following markup:
|
||||
|
||||
```html
|
||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@microsoft/fast-components/dist/fast-components.min.js"></script>
|
||||
```
|
||||
|
||||
The markup above always references the latest release of the components. When deploying to production, you will want to ship with a specific version. Here's an example of the markup for that:
|
||||
|
||||
```html
|
||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@microsoft/fast-components@2.16.0/dist/fast-components.min.js"></script>
|
||||
```
|
||||
|
||||
The best place to put this is typically in your `_Layout.cshtml` file in the script section at the bottom of the `<body>`. Be sure to use double `@` if you are placing this script in any of the files with `cshtml` extension as compiler will treat it with Razor directive:
|
||||
|
||||
```html
|
||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@@microsoft/fast-components/dist/fast-components.min.js"></script>
|
||||
```
|
||||
|
||||
If you wish to leverage NPM instead, run the following command:
|
||||
|
||||
```shell
|
||||
npm install --save @microsoft/fast-components
|
||||
```
|
||||
|
||||
You can locate the single file script build in the following location:
|
||||
|
||||
```shell
|
||||
node_modules/@microsoft/fast-components/dist/fast-components.min.js
|
||||
```
|
||||
|
||||
Copy this to your `wwwroot/js` folder and reference it with a script tag as described above.
|
||||
|
||||
Should you wish to go one step further and leverage a client-side bundler, such as Webpack, there is some additional setup to integrate with ASP.NET that is beyond the scope of this tutorial. Basic Webpack instructions for FAST can be found [here](./webpack.md). The most important detail with respect to FAST is that you'll want to install a few more packages. Use the following command if this is your preferred setup:
|
||||
|
||||
```shell
|
||||
npm install --save @microsoft/fast-components @microsoft/fast-element
|
||||
```
|
||||
|
||||
In this case, because Webpack can tree-shake unused components, you'll also want to be sure to register the components you want to use somewhere in your own JavaScript code. See [our Webpack guide](./webpack.md) for an example.
|
||||
|
||||
## Using the components
|
||||
|
||||
With your script tag added (or your client bundle in place), you can use any component in any of your views. For example, you could put something like this in your `Index.cshtml` file:
|
||||
|
||||
```html
|
||||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<fast-card>
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<fast-button id="button" appearance="accent">Click Me</fast-button>
|
||||
</fast-card>
|
||||
```
|
||||
|
||||
For a splash of style, add the following to your `wwwroot/css/site.css` file:
|
||||
|
||||
```css
|
||||
:not(:defined) {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
fast-card {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--type-ramp-plus-5-font-size);
|
||||
line-height: var(--type-ramp-plus-5-line-height);
|
||||
}
|
||||
|
||||
#button {
|
||||
align-self: flex-end;
|
||||
}
|
||||
```
|
||||
|
||||
Congratulations! You're now set up to use FAST with ASP.NET. You can use more components, build your own components, and when you are ready, build and deploy your website or app to production.
|
|
@ -1,312 +0,0 @@
|
|||
---
|
||||
id: aurelia
|
||||
title: Aurelia
|
||||
sidebar_label: Aurelia
|
||||
custom_edit_url: https://github.com/microsoft/fast/edit/master/packages/web-components/fast-foundation/docs/integrations/aurelia.md
|
||||
description: FAST works flawlessly with both Aurelia 1 and Aurelia 2, with full integration into the binding engine and component model. Let's take a look at how you can set up an Aurelia project, starting from scratch.
|
||||
keywords:
|
||||
- aurelia
|
||||
---
|
||||
|
||||
FAST works flawlessly with both Aurelia 1 and Aurelia 2, with full integration into the binding engine and component model. Let's take a look at how you can set up an Aurelia project, starting from scratch.
|
||||
|
||||
## Aurelia 2
|
||||
|
||||
### Setting up the Aurelia 2 project
|
||||
|
||||
First, you'll need to make sure that you have Node.js installed. You can learn more and download that [on the official site](https://nodejs.org/).
|
||||
|
||||
With Node.js installed, you can run the following command to create a new Aurelia 2 project:
|
||||
|
||||
```shell
|
||||
npx makes Aurelia
|
||||
```
|
||||
|
||||
Follow the prompts, answering each question in turn. It is recommended that you select the "Default TypeScript Aurelia 2 App" when prompted unless you have previous experience with the CLI. Be sure to choose to install dependencies when asked.
|
||||
|
||||
When the CLI completes, you should have a basic runnable Aurelia 2 application.
|
||||
|
||||
### Configuring packages
|
||||
|
||||
Next, we'll install the FAST packages, along with supporting libraries. To do that, run this command from your new project folder:
|
||||
|
||||
```shell
|
||||
npm install --save @microsoft/fast-components @microsoft/fast-element
|
||||
```
|
||||
|
||||
### Using the components
|
||||
|
||||
With all the basic pieces in place, let's run our app in dev mode with `npm start`. Webpack should build your project and open your default browser with your `index.html` page. Right now, it should only have a hello message, since we haven't added any code or interesting HTML. Let's change that.
|
||||
|
||||
First, open your `src/main.ts` file and add the following code:
|
||||
|
||||
```ts
|
||||
import {
|
||||
provideFASTDesignSystem,
|
||||
fastCard,
|
||||
fastButton
|
||||
} from '@microsoft/fast-components';
|
||||
|
||||
provideFASTDesignSystem()
|
||||
.register(
|
||||
fastCard(),
|
||||
fastButton()
|
||||
);
|
||||
```
|
||||
|
||||
This code uses the FAST Design System to register the `<fast-card>` and `<fast-button>` components. Once you save, the dev server will rebuild and refresh your browser. However, you still won't see anything. To get some UI showing up, we need to write some HTML that uses our components. Replace your `my-app.html` file with the following markup:
|
||||
|
||||
```html
|
||||
<fast-card>
|
||||
<h2>${message}</h2>
|
||||
<fast-button appearance="accent" click.trigger="onClick()">Click Me</fast-button>
|
||||
</fast-card>
|
||||
```
|
||||
|
||||
Replace your `my-app.ts` with this:
|
||||
|
||||
```ts
|
||||
export class MyApp {
|
||||
public message = 'Hello World!';
|
||||
|
||||
onClick() {
|
||||
console.log('clicked!');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To add a splash of style, replace your `my-app.css` content with this:
|
||||
|
||||
```css
|
||||
fast-card {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--type-ramp-plus-5-font-size);
|
||||
line-height: var(--type-ramp-plus-5-line-height);
|
||||
}
|
||||
|
||||
fast-card > fast-button {
|
||||
align-self: flex-end;
|
||||
}
|
||||
```
|
||||
|
||||
### Enabling two-way bindings
|
||||
|
||||
Aurelia knows by default how to listen for changes in native elements. Now we need to teach it how to listen for changes in FAST elements. You can do so by [extending its templating syntax](https://docs.aurelia.io/examples/integration/ms-fast).
|
||||
|
||||
You can either use a [wrapper](https://www.npmjs.com/package/aurelia-fast-adapter) developed by the community or teach Aurelia manually:
|
||||
|
||||
### Import and register `aurelia-fast-adapter`
|
||||
|
||||
Start by installing the adapter
|
||||
|
||||
```ts
|
||||
npm install aurelia-fast-adapter
|
||||
```
|
||||
|
||||
and then simply register it from your `src/main.ts`:
|
||||
|
||||
```ts
|
||||
// src/main.ts
|
||||
|
||||
import { FASTAdapter } from 'aurelia-fast-adapter';
|
||||
|
||||
Aurelia
|
||||
.register(FASTAdapter) // add this line
|
||||
// other registrations...
|
||||
.start();
|
||||
```
|
||||
|
||||
If you use FAST in its default configuration that's all you need to do. But if you changed the prefix of your components to something else, you can customize the adapter as such:
|
||||
|
||||
```ts
|
||||
// src/main.ts
|
||||
|
||||
import { FASTAdapter } from 'aurelia-fast-adapter';
|
||||
|
||||
Aurelia
|
||||
.register(FASTAdapter.customize({withPrefix: 'my-custom-prefix'}) // customized with prefix
|
||||
.start();
|
||||
```
|
||||
|
||||
Also, in case you have local components that require two-way binding, you can adjust the adapter before to register it as such:
|
||||
|
||||
```ts
|
||||
// src/main.ts
|
||||
|
||||
import { FASTAdapter } from 'aurelia-fast-adapter';
|
||||
|
||||
// this line will tell the adapter that it must use two-way binding on the <my-custom-prefix-date-field> component and use this two-way binding on the `value` property. It's possible to add several properties at once if necessary
|
||||
FASTAdapter.tags['DATE-FIELD'] = ['value'];
|
||||
|
||||
Aurelia
|
||||
.register(FASTAdapter.customize({withPrefix: 'my-custom-prefix'})
|
||||
.start();
|
||||
```
|
||||
|
||||
Congratulations! You're now set up to use FAST and Aurelia 2!
|
||||
|
||||
### Manually teach Aurelia 2 about two-way binding:
|
||||
|
||||
If the example doesn't seem obvious, the following prerequisite reads are recommended:
|
||||
|
||||
* [extending Aurelia templating syntax](https://docs.aurelia.io/app-basics/extending-templating-syntax)
|
||||
|
||||
The following is a code example of how to teach Aurelia to work seamlessly with Microsoft FAST.
|
||||
|
||||
```typescript
|
||||
import { AppTask, IContainer, IAttrMapper, NodeObserverLocator } from 'aurelia';
|
||||
|
||||
Aurelia.register(AppTask.beforeCreate(IContainer, container => {
|
||||
const attrMapper = container.get(IAttrMapper);
|
||||
const nodeObserverLocator = container.get(NodeObserverLocator);
|
||||
attrMapper.useTwoWay((el, property) => {
|
||||
switch (el.tagName) {
|
||||
case 'FAST-SLIDER':
|
||||
case 'FAST-TEXT-FIELD':
|
||||
case 'FAST-TEXT-AREA':
|
||||
return property === 'value';
|
||||
case 'FAST-CHECKBOX':
|
||||
case 'FAST-RADIO':
|
||||
case 'FAST-RADIO-GROUP':
|
||||
case 'FAST-SWITCH':
|
||||
return property === 'checked';
|
||||
case 'FAST-TABS':
|
||||
return property === 'activeid';
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Teach Aurelia what events to use to observe properties of elements.
|
||||
// Because FAST components all use a single change event to notify,
|
||||
// we can use a single common object
|
||||
const valuePropertyConfig = { events: ['input', 'change'] };
|
||||
nodeObserverLocator.useConfig({
|
||||
'FAST-CHECKBOX': {
|
||||
checked: valuePropertyConfig
|
||||
},
|
||||
'FAST-RADIO': {
|
||||
checked: valuePropertyConfig
|
||||
},
|
||||
'FAST-RADIO-GROUP': {
|
||||
value: valuePropertyConfig
|
||||
},
|
||||
'FAST-SLIDER': {
|
||||
value: valuePropertyConfig
|
||||
},
|
||||
'FAST-SWITCH': {
|
||||
checked: valuePropertyConfig
|
||||
},
|
||||
'FAST-TABS': {
|
||||
activeid: valuePropertyConfig
|
||||
},
|
||||
'FAST-TEXT-FIELD': {
|
||||
value: valuePropertyConfig
|
||||
},
|
||||
'FAST-TEXT-AREA': {
|
||||
value: valuePropertyConfig
|
||||
}
|
||||
});
|
||||
}))
|
||||
```
|
||||
|
||||
## Aurelia 1
|
||||
|
||||
### Setting up the Aurelia 1 project
|
||||
|
||||
First, you'll need to make sure that you have Node.js installed. You can learn more and download that [on the official site](https://nodejs.org/).
|
||||
|
||||
With Node.js installed, you can run the following command to install the Aurelia 1 CLI:
|
||||
|
||||
```shell
|
||||
npm install -g aurelia-cli
|
||||
```
|
||||
|
||||
And then use the CLI like this:
|
||||
|
||||
```shell
|
||||
au new fast-aurelia
|
||||
```
|
||||
|
||||
Follow the prompts, answering each question in turn. It is recommended that you select the "Default TypeScript App" when prompted unless you have previous experience with the CLI. Be sure to choose to install dependencies when asked.
|
||||
|
||||
When the CLI completes, you should have a basic runnable Aurelia 1 application.
|
||||
|
||||
### Configuring packages
|
||||
|
||||
Next, we'll install the FAST packages, along with supporting libraries. To do that, run this command from your new project folder:
|
||||
|
||||
```shell
|
||||
npm install --save @microsoft/fast-components @microsoft/fast-element
|
||||
```
|
||||
|
||||
### Using the components
|
||||
|
||||
With all the basic pieces in place, let's run our app in dev mode with `npm start`. Webpack should build your project and make it available at `http://localhost:8080/`. If you visit this address it should only have a hello message, since we haven't added any code or interesting HTML. Let's change that.
|
||||
|
||||
First, open your `src/main.ts` file and add the following code:
|
||||
|
||||
```ts
|
||||
import {
|
||||
provideFASTDesignSystem,
|
||||
fastCard,
|
||||
fastButton
|
||||
} from '@microsoft/fast-components';
|
||||
|
||||
provideFASTDesignSystem()
|
||||
.register(
|
||||
fastCard(),
|
||||
fastButton()
|
||||
);
|
||||
```
|
||||
|
||||
This code uses the FAST Design System to register the `<fast-card>` and `<fast-button>` components. Once you save, the dev server will rebuild and refresh your browser. However, you still won't see anything. To get some UI showing up, we need to write some HTML that uses our components. Replace your `app.html` file with the following markup:
|
||||
|
||||
```html
|
||||
<template>
|
||||
<fast-card>
|
||||
<h2>${message}</h2>
|
||||
<fast-button appearance="accent" click.trigger="onClick()">Click Me</fast-button>
|
||||
</fast-card>
|
||||
</template>
|
||||
```
|
||||
|
||||
Replace your `app.ts` with this:
|
||||
|
||||
```ts
|
||||
export class App {
|
||||
public message: string = 'Hello World!';
|
||||
|
||||
onClick() {
|
||||
console.log('clicked!');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To add a splash of style, add the following to your `app.html` template:
|
||||
|
||||
```html
|
||||
<style>
|
||||
fast-card {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--type-ramp-plus-5-font-size);
|
||||
line-height: var(--type-ramp-plus-5-line-height);
|
||||
}
|
||||
|
||||
fast-card > fast-button{
|
||||
align-self: flex-end;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
Congratulations! You're now set up to use FAST and Aurelia 1!
|
|
@ -1,291 +0,0 @@
|
|||
---
|
||||
id: blazor
|
||||
title: Blazor
|
||||
sidebar_label: Blazor
|
||||
custom_edit_url: https://github.com/microsoft/fast/edit/master/packages/web-components/fast-foundation/docs/integrations/blazor.md
|
||||
description: FAST works seamlessly with Blazor, including integration with Blazor's binding engine and components. Let's take a look at how to set things up.
|
||||
keywords:
|
||||
- blazor
|
||||
---
|
||||
|
||||
FAST works seamlessly with Blazor, including integration with Blazor's binding engine and components. Let's take a look at how to set things up.
|
||||
|
||||
## Setting up the Blazor project
|
||||
|
||||
First, you'll need to make sure that you have the .NET SDK installed. You can learn more and download that [on the official site](https://dotnet.microsoft.com/download).
|
||||
|
||||
With the SDK installed, you have access to the `dotnet` command-line interface. This can be used to create a new Blazor project. For example, to create a new Blazor App named "fast-blazor", you would use the following command:
|
||||
|
||||
```shell
|
||||
dotnet new blazorwasm -o fast-blazor
|
||||
```
|
||||
|
||||
Create a project using the command above if you don't already have one. When the CLI completes, you should have a basic runnable Blazor application. For more information on setting up and using Blazor, [see the official Blazor Getting Started guide](https://docs.microsoft.com/en-us/aspnet/core/blazor/get-started).
|
||||
|
||||
## Getting Started with the FluentUI Web Components
|
||||
|
||||
FAST has special Blazor support for Microsoft's FluentUI Web Components. To get started using the Fluent UI Web Components for Blazor, you will first need to install [the official Nuget package for Fluent UI](https://www.nuget.org/packages/Microsoft.Fast.Components.FluentUI/). You can use the following command:
|
||||
|
||||
```shell
|
||||
dotnet add package Microsoft.Fast.Components.FluentUI
|
||||
```
|
||||
|
||||
Next, you need to add the web components script. You can either add the script from CDN directly, or you can install it with NPM, whichever you prefer.
|
||||
|
||||
To add the script from CDN use the following markup:
|
||||
|
||||
```html
|
||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@fluentui/web-components/dist/web-components.min.js"></script>
|
||||
```
|
||||
|
||||
The markup above always references the latest release of the components. When deploying to production, you will want to ship with a specific version. Here's an example of the markup for that:
|
||||
|
||||
```html
|
||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@fluentui/web-components@2.0.2/dist/web-components.min.js"></script>
|
||||
```
|
||||
|
||||
The best place to put the script tag is typically in your index.html file in the script section at the bottom of the `<body>`.
|
||||
|
||||
If you wish to leverage NPM instead, run the following command:
|
||||
|
||||
```html
|
||||
npm install --save @fluentui/web-components
|
||||
```
|
||||
|
||||
You can locate the single file script build in the following location:
|
||||
|
||||
```html
|
||||
node_modules/@fluentui/web-components/dist/web-components.min.js
|
||||
```
|
||||
|
||||
Copy this to your `wwwroot/script` folder and reference it with a script tag as described above.
|
||||
|
||||
:::note
|
||||
|
||||
If you are setting up Fluent UI Web Components on a Blazor Server project, you will need to escape the `@` character by repeating it in the source link. For more information check out the [Razor Pages syntax documentation](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-6.0).
|
||||
|
||||
:::
|
||||
|
||||
### Using the FluentUI Web Components
|
||||
|
||||
With the package installed and the script configured, you can begin using the Fluent UI Web Components in the same way as any other Blazor component. Just be sure to add the following using statement to your views:
|
||||
|
||||
```html
|
||||
@using Microsoft.Fast.Components.FluentUI
|
||||
```
|
||||
|
||||
Here's a small example of a `FluentCard` with a `FluentButton` that uses the Fluent "Accent" appearance:
|
||||
|
||||
```html
|
||||
@using Microsoft.Fast.Components.FluentUI
|
||||
|
||||
<FluentCard>
|
||||
<h2>Hello World!</h2>
|
||||
<FluentButton Appearance="@Appearance.Accent">Click Me</FluentButton>
|
||||
</FluentCard>
|
||||
```
|
||||
|
||||
:::tip
|
||||
|
||||
You can add `@using Microsoft.Fast.Components.FluentUI` to the namespace collection in `_Imports.razor`, so that you can avoid repeating it in every single razor page.
|
||||
|
||||
:::
|
||||
|
||||
If you are using the .NET CLI, you can run your project with the following command from the project folder:
|
||||
|
||||
```shell
|
||||
dotnet watch run
|
||||
```
|
||||
|
||||
Congratulations! You're now set up to use the Fluent UI Web Components with Blazor!
|
||||
### Configuring the Design System
|
||||
|
||||
The Fluent UI Web Components are built on FAST's Adaptive UI technology, which enables design customization and personalization, while automatically maintaining accessibility. This is accomplished through setting various "Design Tokens". As of version 1.4 you can use all of the (160) individual Design Tokens, both from code as in a declarative way in your `.razor` pages. See https://docs.microsoft.com/en-us/fluent-ui/web-components/design-system/design-tokens for more information on how Design Tokens work
|
||||
|
||||
#### Option 1: Using Design Tokens from C# code
|
||||
|
||||
Given the following `.razor` page fragment:
|
||||
```html
|
||||
<FluentButton @ref="ref1" Appearance="Appearance.Filled">A button</FluentButton>
|
||||
<FluentButton @ref="ref2" Appearance="Appearance.Filled">Another button</FluentButton>
|
||||
<FluentButton @ref="ref3" Appearance="Appearance.Filled">And one more</FluentButton>
|
||||
<FluentButton @ref="ref4" Appearance="Appearance.Filled" @onclick=OnClick>Last button</FluentButton>
|
||||
|
||||
```
|
||||
You can use Design Tokens to manipulate the styles from C# code as follows:
|
||||
|
||||
```csharp
|
||||
[Inject]
|
||||
private BaseLayerLuminance BaseLayerLuminance { get; set; } = default!;
|
||||
|
||||
[Inject]
|
||||
private AccentBaseColor AccentBaseColor { get; set; } = default!;
|
||||
|
||||
[Inject]
|
||||
private BodyFont BodyFont { get; set; } = default!;
|
||||
|
||||
[Inject]
|
||||
private StrokeWidth StrokeWidth { get; set; } = default!;
|
||||
|
||||
[Inject]
|
||||
private ControlCornerRadius ControlCornerRadius { get; set; } = default!;
|
||||
|
||||
private FluentButton? ref1;
|
||||
private FluentButton? ref2;
|
||||
private FluentButton? ref3;
|
||||
private FluentButton? ref4;
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
//Set to dark mode
|
||||
await BaseLayerLuminance.SetValueFor(ref1!.Element, (float)0.15);
|
||||
|
||||
//Set to Excel color
|
||||
await AccentBaseColor.SetValueFor(ref2!.Element, "#185ABD".ToColor());
|
||||
|
||||
//Set the font
|
||||
await BodyFont.SetValueFor(ref3!.Element, "Comic Sans MS");
|
||||
|
||||
//Set 'border' width for ref4
|
||||
await StrokeWidth.SetValueFor(ref4!.Element, 7);
|
||||
//And change conrner radius as well
|
||||
await ControlCornerRadius.SetValueFor(ref4!.Element, 15);
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task OnClick()
|
||||
{
|
||||
//Remove the wide border
|
||||
await StrokeWidth.DeleteValueFor(ref4!.Element);
|
||||
}
|
||||
```
|
||||
As can be seen in the code above (with the `ref4.Element`), it is posible to apply multiple tokens to the same component.
|
||||
|
||||
For Design Tokens that work with a color value, it is needed to add the `ToColor()` extension method on the string value. This converts the string into a RGB value that the Design Token can operate with. Internally we are using the `System.Drawing.Color` struct for this and this means you can use all the available methods, operators, etc from that namespace in your code too.
|
||||
|
||||
:::important
|
||||
|
||||
The Design Tokens are manipulated through JavaScript interop working with an `ElementReference`. There is no JavaScript element until after the component is rendered. This means you can only work with the Design Tokens from code after the component has been rendered in `OnAfterRenderAsync` and not in any earlier lifecycle methods.
|
||||
|
||||
:::
|
||||
|
||||
#### Option 2: Using Design Tokens as components
|
||||
The Design Tokens can also be used as components in a `.razor` page directely. It looks like this:
|
||||
|
||||
```html
|
||||
<BaseLayerLuminance Value="(float?)0.15">
|
||||
<FluentCard BackReference="@context">
|
||||
<div class="contents">
|
||||
Dark
|
||||
<FluentButton Appearance="Appearance.Accent">Accent</FluentButton>
|
||||
<FluentButton Appearance="Appearance.Stealth">Stealth</FluentButton>
|
||||
<FluentButton Appearance="Appearance.Outline">Outline</FluentButton>
|
||||
<FluentButton Appearance="Appearance.Lightweight">Lightweight</FluentButton>
|
||||
</div>
|
||||
</FluentCard>
|
||||
</BaseLayerLuminance>
|
||||
```
|
||||
|
||||
To make this work, a link needs to be created between the Design Token component and its child components. This is done with the `BackReference="@context"` construct.
|
||||
|
||||
:::note
|
||||
|
||||
Only one Design Token component at a time can be used this way. If you need to set more tokens, use the code approach as described in Option 1 above.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
#### Option 3: Using the `<FluentDesignSystemProvider>`
|
||||
The third way to customize the design in Blazor is to wrap the entire block you want to manipulate in a `<FluentDesignSystemProvider>`. This special element has a number of properties you can set to configure a subset of the tokens. **Not all tokens are available/supported** and we recommend this to only be used as a fall-back mechanism. The preferred mehod of working with the desgn tokens is to manipulate them from code as described in option 1.
|
||||
|
||||
Here's an example of changing the "accent base color" and switching the system into dark mode (in the file `app.razor`):
|
||||
|
||||
```html
|
||||
<FluentDesignSystemProvider AccentBaseColor="#464EB8" BaseLayerLuminance="0">
|
||||
<Router AppAssembly="@typeof(App).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
|
||||
</Found>
|
||||
<NotFound>
|
||||
<PageTitle>Not found</PageTitle>
|
||||
<LayoutView Layout="@typeof(MainLayout)">
|
||||
<p role="alert">Sorry, there's nothing at this address.</p>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
</FluentDesignSystemProvider>
|
||||
```
|
||||
|
||||
:::note
|
||||
|
||||
Provider token attributes can be changed on-the-fly like any other Blazor component attribute.
|
||||
|
||||
:::
|
||||
|
||||
#### Colors for integration with specific Microsoft products
|
||||
If you are attempting to configure the components for integration into a specific Microsoft product, the following table provides `AccentBaseColor` values you can use:
|
||||
|
||||
Product | AccentBaseColor
|
||||
------- | ---------------
|
||||
| Office | #D83B01 |
|
||||
| Word | #185ABD |
|
||||
| Excel | #107C41 |
|
||||
| PowerPoint | #C43E1C |
|
||||
| Teams | #6264A7 |
|
||||
| OneNote | #7719AA |
|
||||
| SharePoint | #03787C |
|
||||
| Stream | #BC1948 |
|
||||
|
||||
For a list of all available token attributes, [see here](https://github.com/microsoft/fast-blazor/blob/main/src/Microsoft.Fast.Components.FluentUI/Components/FluentDesignSystemProvider.razor#L69). More examples for other components can be found in the `examples` folder [of this repository](https://github.com/microsoft/fast-blazor).
|
||||
|
||||
## Web components / Blazor components mapping, implementation status and remarks
|
||||
Web component | Blazor component | Status | Remarks
|
||||
----------------- | -------------- | ------ | -------
|
||||
|[`<fluent-accordion>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/accordion)|`<FluentAccordion>`|✔️|-|
|
||||
|`<fluent-accordion-item>`|`<FluentAccordionItem>`|✔️|-|
|
||||
|[`<fluent-anchor>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/anchor)|`<FluentAnchor>`|✔️|-|
|
||||
|[`<fluent-anchored-region>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/anchored-region)|`<FluentAnchoredRegion>`|✔️|-|
|
||||
|[`<fluent-badge>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/badge)|`<FluentBadge>`|✔️|-|
|
||||
|[`<fluent-breadcrumb>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/breadcrumb)|`<FluentBreadcrumb>`|✔️|-|
|
||||
|`<fluent-breadcrumb-item>`|`<FluentBreadcrumbItem>`|✔️|-|
|
||||
|[`<fluent-button>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/button)|`<FluentButton>`|✔️|-|
|
||||
|[`<fluent-card>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/card)|`<FluentCard>`|✔️|-|
|
||||
|[`<fluent-checkbox>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/checkbox)|`<FluentCheckbox>`|✔️|-|
|
||||
|[`<fluent-combobox>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/combobox)|`<FluentCombobox>`|✔️|-|
|
||||
|[`<fluent-data-grid>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/data-grid)|`<FluentDataGrid>`|✔️|-|
|
||||
|`<fluent-data-grid-cell>`|`<FluentDataGridCell>`|✔️|-|
|
||||
|`<fluent-data-grid-row>`|`<FluentDataGridRow>`|✔️|-|
|
||||
|[`<fluent-design-system-provider>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/provider)|`<FluentDesignSystemProvider>`|✔️|-|
|
||||
|[`<fluent-dialog>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/dialog)|`<FluentDialog>`|✔️|-|
|
||||
|[`<fluent-divider>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/divider)|`<FluentDivider>`|✔️|-|
|
||||
|[`<fluent-flipper>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/flipper)|`<FluentFlipper>`|✔️|-|
|
||||
|[`<fluent-horizontal-scroll>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/horizontal-scroll)|`<FluentHorizontalScroll>`|✔️|-|
|
||||
|No web component|`<FluentIcon>`|✔️|-|
|
||||
|[`<fluent-listbox>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/listbox)|`<FluentListbox>`|✔️|-|
|
||||
|[`<fluent-menu>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/menu)|`<FluentMenu>`|✔️|-|
|
||||
|`<fluent-menu-item>`|`<FluentMenuItem>`|✔️|-|
|
||||
|[`<fluent-number-field>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/number-field)|`<FluentNumberField>`|✔️|-|
|
||||
|`<fluent-option>`|`<FluentOption>`|✔️|-|
|
||||
|[`<fluent-progress>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/progress)|`<FluentProgress>`|✔️|-|
|
||||
|[`<fluent-progress-ring>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/progress-ring)|`<FluentProgressRing>`|✔️|-|
|
||||
|[`<fluent-radio>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/radio-group)|`<FluentRadio>`|✔️|-|
|
||||
|[`<fluent-radio-group>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/radio-group)|`<FluentRadioGroup>`|✔️|-|
|
||||
|[`<fluent-select>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/select)|`<FluentSelect>`|✔️|-|
|
||||
|[`<fluent-skeleton>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/skeleton)|`<FluentSkeleton>`|✔️|-|
|
||||
|[`<fluent-slider>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/slider)|`<FluentSlider>`|✔️|-|
|
||||
|`<fluent-slider-label>`|`<FluentSliderLabel>`|✔️|-|
|
||||
|[`<fluent-switch>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/switch)|`<FluentSwitch>`|✔️|-|
|
||||
|[`<fluent-tabs>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/tabs)|`<FluentTabs>`|✔️|-|
|
||||
|`<fluent-tab>`|`<FluentTab>`|✔️|-|
|
||||
|`<fluent-tab-panel>`|`<FluentTabPanel>`|✔️|-|
|
||||
|[`<fluent-text-area>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/text-area)|`<FluentTextArea>`|✔️|-|
|
||||
|[`<fluent-text-field>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/text-field)|`<FluentTextField>`|✔️|-|
|
||||
|[`<fluent-toolbar>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/toolbar)|`<FluentToolbar>`|✔️|-|
|
||||
|[`<fluent-tooltip>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/tooltip)|`<FluentTooltip>`|✔️|-|
|
||||
|[`<fluent-tree-view>`](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/tree-view)|`<FluentTreeView>`|✔️|-|
|
||||
|`<fluent-tree-item>`|`<FluentTreeItem>`|✔️|-|
|
|
@ -1,124 +0,0 @@
|
|||
---
|
||||
id: ember
|
||||
title: Ember
|
||||
sidebar_label: Ember
|
||||
custom_edit_url: https://github.com/microsoft/fast/edit/master/packages/web-components/fast-foundation/docs/integrations/ember.md
|
||||
description: FAST and Ember work great together. Let's take a look at how you can set up an Ember project, starting from scratch.
|
||||
keywords:
|
||||
- ember
|
||||
---
|
||||
|
||||
FAST and Ember work great together. Let's take a look at how you can set up an Ember project, starting from scratch.
|
||||
|
||||
## Setting up the Ember project
|
||||
|
||||
First, you'll need to make sure that you have Node.js installed. You can learn more and download that [on the official site](https://nodejs.org/).
|
||||
|
||||
With Node.js installed, you can run the following command to install the Ember CLI:
|
||||
|
||||
```shell
|
||||
npm install -g ember-cli
|
||||
```
|
||||
|
||||
With the CLI installed, you have access to the `ember` command-line interface. This can be used to create a new Ember project. For example, to create a new Ember App named "fast-ember", you would use the following command:
|
||||
|
||||
```shell
|
||||
ember new fast-ember --lang en
|
||||
```
|
||||
|
||||
When the CLI completes, you should have a basic runnable Ember application.
|
||||
|
||||
## Configuring packages
|
||||
|
||||
Next, we'll install the FAST packages, along with supporting libraries. To do that, run this command from your new project folder:
|
||||
|
||||
```shell
|
||||
npm install --save @microsoft/fast-components @microsoft/fast-element
|
||||
```
|
||||
|
||||
## Using the components
|
||||
|
||||
With all the basic pieces in place, let's run our app with `npm start`. The Ember CLI should build your project and make it available on localhost. Right now, it displays a basic welcome message, since we haven't added any code or interesting HTML. Let's change that.
|
||||
|
||||
First, open your `app/app.js` file and add the following code:
|
||||
|
||||
```ts
|
||||
import {
|
||||
provideFASTDesignSystem,
|
||||
fastCard,
|
||||
fastButton,
|
||||
fastTextField
|
||||
} from '@microsoft/fast-components';
|
||||
|
||||
provideFASTDesignSystem()
|
||||
.register(
|
||||
fastCard(),
|
||||
fastButton(),
|
||||
fastTextField()
|
||||
);
|
||||
```
|
||||
|
||||
This code uses the FAST Design System to register `<fast-card>`, `<fast-button>` and `<fast-text-field>` components. Once you save, the dev server will rebuild and refresh your browser. However, you still won't see anything. Open your `application.hbs` file and replace the `<WelcomePage />` component with the following HTML and then save again.
|
||||
|
||||
```html
|
||||
<fast-card>
|
||||
<h2>FAST Ember</h2>
|
||||
<fast-text-field placeholder="Enter Some Text"></fast-text-field>
|
||||
<fast-button appearance="accent">Click Me</fast-button>
|
||||
</fast-card>
|
||||
```
|
||||
|
||||
Now you should see the FAST web components displayed in your Ember application.
|
||||
|
||||
Next, let's improve this by refactoring this code into a component. Stop the CLI and run the following command to scaffold an Ember component.
|
||||
|
||||
```shell
|
||||
ember generate component fast-demo
|
||||
```
|
||||
|
||||
Copy the the HTML above and use it to replace the HTML in your `app/components/fast-demo.hbs` file. Next replace the same HTML in your `templates/application.hbs` file with the following Ember component use:
|
||||
|
||||
```html
|
||||
<FastDemo/>
|
||||
```
|
||||
|
||||
Run `npm start` again and you should see the same output, but now we have moved our web components into a `FastDemo` Ember component.
|
||||
|
||||
Let's go a little further. Create a `fast-demo.js` file in the same folder as your `fast-demo.hbs` file and paste the following code:
|
||||
|
||||
```js
|
||||
import Component from '@glimmer/component';
|
||||
import { action } from '@ember/object';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
|
||||
export default class FastDemoComponent extends Component {
|
||||
@tracked exampleTextField = '';
|
||||
|
||||
@action
|
||||
onClick() {
|
||||
console.log(this.exampleTextField);
|
||||
}
|
||||
|
||||
@action
|
||||
onInput(event) {
|
||||
this.exampleTextField = event.target.value;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next, update the `fast-demo.hbs` file with the following HTML:
|
||||
|
||||
```html
|
||||
<fast-card>
|
||||
<h2>FAST Ember</h2>
|
||||
<fast-text-field placeholder="Enter Some Text"
|
||||
value="{{this.exampleTextField}}"
|
||||
{{on "input" this.onInput}}
|
||||
></fast-text-field>
|
||||
<fast-button appearance="accent" {{on "click" this.onClick}}>Click Me</fast-button>
|
||||
</fast-card>
|
||||
```
|
||||
|
||||
With this code in place, you now have FAST Web Components fully binding to data and handling user interactions, all from inside an Ember component.
|
||||
|
||||
Congratulations! You're now set up to use FAST and Ember!
|
|
@ -1,13 +0,0 @@
|
|||
---
|
||||
id: introduction
|
||||
title: Introduction
|
||||
sidebar_label: Introduction
|
||||
custom_edit_url: https://github.com/microsoft/fast/edit/master/packages/web-components/fast-foundation/docs/integrations/introduction.md
|
||||
description: The integrations section of our documentation is dedicated to helping you get FAST Element working with your existing or preferred stack.
|
||||
---
|
||||
|
||||
FAST libraries can be used on their own to build modern web sites and applications, but they are also designed to be used in combination with a wide variety of existing technologies. The integrations section of our documentation is dedicated to helping you get FAST Element working with your existing or preferred stack.
|
||||
|
||||
:::note
|
||||
Not seeing an integration for your preferred technology? We'd be happy to work with you to add it. Feel free to kick off a discussion by [opening an issue on GitHub](https://github.com/microsoft/fast/issues).
|
||||
:::
|
|
@ -1,313 +0,0 @@
|
|||
---
|
||||
id: react
|
||||
title: React
|
||||
sidebar_label: React
|
||||
custom_edit_url: https://github.com/microsoft/fast/edit/master/packages/web-components/fast-foundation/docs/integrations/react.md
|
||||
description: FAST can be used in React applications. We recommend using it with the TypeScript template. Let's take a look at how you can set up a project, starting from scratch.
|
||||
---
|
||||
|
||||
FAST can be used in React applications. We recommend using it with the TypeScript template. Let's take a look at how you can set up a project, starting from scratch.
|
||||
|
||||
## Setting up the React project
|
||||
|
||||
First, you'll need to make sure that you have Node.js >= 8.2 and npm >= 5.6 installed. You can learn more and download that [on the official site](https://nodejs.org/).
|
||||
|
||||
With Node.js installed, you can use [create-react-app](https://reactjs.org/docs/create-a-new-react-app.html#create-react-app) to create a new React project.
|
||||
|
||||
```shell
|
||||
npx create-react-app fast-app --template typescript
|
||||
```
|
||||
|
||||
> It is recommended to set up the create-react-app project with TypeScript.
|
||||
|
||||
## Configuring packages
|
||||
|
||||
Next, we'll install the FAST packages, along with supporting libraries. To do that, run this command from your new project folder:
|
||||
|
||||
```shell
|
||||
npm install --save @microsoft/fast-components @microsoft/fast-foundation @microsoft/fast-element @microsoft/fast-react-wrapper
|
||||
```
|
||||
|
||||
## Configure create-react-app
|
||||
|
||||
[create-react-app](https://reactjs.org/docs/create-a-new-react-app.html#create-react-app) ships with an [eslint](https://eslint.org/) rule that makes working with FAST components difficult. There are two changes that will need to be made in the `package.json`:
|
||||
|
||||
**Set the EXTEND_ESLINT environment variable in start, build, and test scripts**
|
||||
|
||||
```jsonc
|
||||
// package.json
|
||||
{
|
||||
//...
|
||||
"scripts": {
|
||||
"start": "EXTEND_ESLINT=true react-scripts start",
|
||||
"build": "EXTEND_ESLINT=true react-scripts build",
|
||||
"test": "EXTEND_ESLINT=true react-scripts test"
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
> The above will not work on Windows. You can adjust the scripts to use [cross-env](https://www.npmjs.com/package/cross-env) to add Windows support.
|
||||
|
||||
**Override the `eslintConfig` field to turn off the 'no-unused-expressions' rule**
|
||||
|
||||
```jsonc
|
||||
// package.json
|
||||
{
|
||||
//..
|
||||
"eslintConfig": {
|
||||
"extends": "react-app",
|
||||
"rules": {
|
||||
"no-unused-expressions": "off"
|
||||
}
|
||||
}
|
||||
//..
|
||||
}
|
||||
```
|
||||
|
||||
See [configuring eslint](https://create-react-app.dev/docs/setting-up-your-editor#experimental-extending-the-eslint-config) for more information.
|
||||
|
||||
Next, be sure to update the TypeScript config file.
|
||||
|
||||
**Set 'experimentalDecorators' to true in the `tsconfig.json` file**
|
||||
|
||||
> When using decorators in a new create-react-app setup with TypeScript, you'll most likely see the warning `Support for the experimental syntax 'decorators-legacy' isn't currently enabled`. Configuring tsconfig will remove this warning.
|
||||
|
||||
```jsonc
|
||||
// tsconfig.json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
//..
|
||||
}
|
||||
```
|
||||
|
||||
## Using the components
|
||||
|
||||
With all the basic pieces in place, let's run our app in dev mode with `npm start`. Right now, it displays the React logo and some editing instructions, since we haven't added any code or interesting HTML. Let's change that.
|
||||
|
||||
First, open your `src/app.js` file and add the following code:
|
||||
|
||||
```js
|
||||
import {
|
||||
provideFASTDesignSystem,
|
||||
fastCard,
|
||||
fastButton,
|
||||
} from "@microsoft/fast-components";
|
||||
import { provideReactWrapper } from "@microsoft/fast-react-wrapper";
|
||||
import React from "react";
|
||||
|
||||
const { wrap } = provideReactWrapper(React, provideFASTDesignSystem());
|
||||
|
||||
export const FastCard = wrap(fastCard());
|
||||
export const FastButton = wrap(fastButton());
|
||||
```
|
||||
|
||||
This code uses the FAST Design System to register the `<fast-card>` and `<fast-button>` components while automatically wrapping them into React components. Once you save, the dev server will rebuild and refresh your browser. However, you still won't see anything. To get some UI showing up, we need to write some HTML that uses our components. Replace the App component in your `src/app.js` file with the following:
|
||||
|
||||
```jsx
|
||||
function App() {
|
||||
return (
|
||||
<FastCard>
|
||||
<h2>FAST React</h2>
|
||||
<FastButton appearance="accent" onClick={() => console.log("clicked")}>
|
||||
Click Me
|
||||
</FastButton>
|
||||
</FastCard>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
To add a splash of style, add the following to the `src/App.css`:
|
||||
|
||||
```css
|
||||
fast-card {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--type-ramp-plus-5-font-size);
|
||||
line-height: var(--type-ramp-plus-5-line-height);
|
||||
}
|
||||
|
||||
fast-card > fast-button {
|
||||
align-self: flex-end;
|
||||
}
|
||||
```
|
||||
|
||||
Congratulations! You're now set up to use FAST and React!
|
||||
|
||||
## Using the React Wrapper
|
||||
|
||||
Above, we leveraged the `@microsoft/fast-react-wrapper` library to enable seamless integration of FAST Components. There are a few additional ways to use this API for different web component scenarios.
|
||||
|
||||
### Wrapping Design System Components
|
||||
|
||||
Previously, you've seen that we can wrap a Design System component by passing its registration function to the `wrap` method as follows:
|
||||
|
||||
```ts
|
||||
const { wrap } = provideReactWrapper(React, provideFASTDesignSystem());
|
||||
|
||||
export const FastButton = wrap(fastButton());
|
||||
```
|
||||
|
||||
This code creates a wrapper that is configured with a React-compatible API and a Design System instance. When passing a Design System as the second parameter, you can then pass component registration functions to the `wrap` helper. The helper will both register the web component with the Design System and wrap it in a type-safe React component, all with a single call.
|
||||
|
||||
Alternatively, you can skip providing the Design System to the wrapper, and use the generated registry to manually register all previously wrapped components.
|
||||
|
||||
```ts
|
||||
const { wrap, registry } = provideReactWrapper(React);
|
||||
|
||||
export const FastButton = wrap(fastButton());
|
||||
|
||||
provideFASTDesignSystem().register(registry);
|
||||
```
|
||||
|
||||
The final option is to handle everything by hand:
|
||||
|
||||
```ts
|
||||
const { wrap } = provideReactWrapper(React);
|
||||
|
||||
export const FastButton = wrap(fastButton());
|
||||
|
||||
provideFASTDesignSystem().register(fastButton());
|
||||
```
|
||||
|
||||
### Wrapping FAST Components
|
||||
|
||||
The `wrap` helper can also wrap any FAST Web Component defined using the `@customElement` decorator or by manually calling `FASTElement.define`. To do so, pass the custom element class to the wrapper.
|
||||
|
||||
```ts
|
||||
import { FASTElement, customElement, html } from '@microsoft/fast-element';
|
||||
|
||||
@customElement({
|
||||
name: 'my-component',
|
||||
template: html`...`,
|
||||
styles:...
|
||||
})
|
||||
class _MyComponent extends FASTElement {
|
||||
|
||||
}
|
||||
|
||||
export const MyComponent = provideReactWrapper(React).wrap(_MyComponent);
|
||||
```
|
||||
|
||||
### Wrapping VanillaJS Web Components
|
||||
|
||||
If you have a component from a 3rd party library, not written with FAST, or a VanillaJS Web Component, you can wrap that as well. In this scenario you will have to provide some additional information, such as the element name and the list of properties that should be handled by the wrapper rather than React. Components created with libraries like `Lit` require the element name to be configured but not the properties, while some other libraries or hand-written components may also require the property list. This depends on how the component was defined. Below is an example of configuring both the name and the property list.
|
||||
|
||||
```ts
|
||||
import { CoolComponent as _CoolComponent } from "@cool/component";
|
||||
|
||||
const { wrap } = provideReactWrapper(React);
|
||||
|
||||
export const CoolComponent = wrap(_CoolComponent, {
|
||||
name: "cool-component",
|
||||
properties: ["list", "properties", "here"],
|
||||
});
|
||||
```
|
||||
|
||||
### Configuring Custom Events
|
||||
|
||||
If the wrapped component uses custom events that you intend to use from React, you will need to manually configure a mapping from React event name to the native event name. Here's an example of what that would look like if you wanted to leverage the FAST MenuItem's `expanded-change` event:
|
||||
|
||||
```ts
|
||||
const { wrap } = provideReactWrapper(React, provideFASTDesignSystem());
|
||||
|
||||
export const FastMenuItem = wrap(fastMenuItem(), {
|
||||
events: {
|
||||
onExpandedChange: "expanded-change",
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Working without the fast-react-wrapper
|
||||
|
||||
The `@microsoft/fast-react-wrapper` library described above addresses all the challenges involved in using Web Components from React. We strongly recommend using this library for integration. However, if you cannot use this library or want to explore other options, follow the steps below.
|
||||
|
||||
#### Extend the React.Component
|
||||
|
||||
After creating your custom element, define another class that extends `React.Component`, this will allow you to use your custom element in the _render()_ method.
|
||||
|
||||
```ts
|
||||
// App.tsx
|
||||
import { FASTElement, customElement, attr } from "@microsoft/fast-element";
|
||||
import { provideFASTDesignSystem } from "@microsoft/fast-components";
|
||||
|
||||
@customElement("name-tag")
|
||||
class _NameTag extends FASTElement {
|
||||
@attr greeting: string = "";
|
||||
}
|
||||
|
||||
provideFASTDesignSystem().register(_NameTag);
|
||||
|
||||
class NameTag extends React.Component {
|
||||
render() {
|
||||
return <name-tag greeting="hi"></name-tag>;
|
||||
}
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<NameTag />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
```
|
||||
|
||||
> At this stage, you will get a warning `Property 'name-tag' does not exist on type 'JSX.IntrinsicElements'`. Adding `custom-elements.d.ts` in the following step will remove this warning.
|
||||
|
||||
#### TypeScript and TSX support
|
||||
|
||||
With TypeScript, you'll need to augment the `JSX.IntrinsicElements` interface to use custom elements in TSX. To do so, create a `custom-elements.d.ts` file in your source directory and add the following:
|
||||
|
||||
```ts
|
||||
// custom-elements.d.ts
|
||||
declare namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
"fast-design-system-provider": React.DetailedHTMLProps<
|
||||
React.HTMLAttributes<HTMLElement>,
|
||||
HTMLElement
|
||||
> & {
|
||||
"use-defaults"?: boolean;
|
||||
};
|
||||
"name-tag": React.DetailedHTMLProps<
|
||||
React.HTMLAttributes<HTMLElement>,
|
||||
HTMLElement
|
||||
> & {
|
||||
greeting?: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> Note: The above example works with React version 18.0.0
|
||||
|
||||
## Additional Notes
|
||||
|
||||
FAST makes use of decorators to define components. At this time, `create-react-app` [does not support decorators](https://create-react-app.dev/docs/can-i-use-decorators/). This won't be a problem when using components _imported_ from FAST because they have already been transpiled by TypeScript - but to _create_ components in a `create-react-app` application, here are some additional ways to work without the fast-react-wrapper:
|
||||
|
||||
- [Creating custom components with Babel](https://fast.design/docs/integrations/webpack#creating-custom-components-with-babel)
|
||||
- [Define components without decorators](https://fast.design/docs/fast-element/defining-elements#working-without-decorators)
|
||||
- Use an intermediary like [react-app-rewired](https://www.npmjs.com/package/react-app-rewired)
|
||||
|
||||
You can read more about decorator configuration issues [here.](https://github.com/microsoft/fast/issues/4503)
|
||||
|
||||
#### HTML Attributes
|
||||
|
||||
React is capable of rendering custom HTML elements and binding data to them, but it is beneficial to understand _how_ React does this. React will apply all _props_ to a custom HTML element as _HTML attributes_ - including non-primitive types such as arrays and objects. Where some UI libraries provide binding syntaxes to distinguish setting properties, attributes, and events, React does not. This means that it can be very easy to end up with `my-prop="[object Object]"` in your HTML. React is exploring solutions [in this issue](https://github.com/facebook/react/issues/11347). See the section on [interop layers](#interop-layers-skatejsval-and-reactify-wc) for a work-around for this issue.
|
||||
|
||||
#### Custom events
|
||||
|
||||
React's synthetic eventing system comes with an unfortunate side-effect of being incapable of declaratively applying [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) listeners. [interop layers](#interop-layers-skatejsval-and-reactify-wc) can be used to address this issue. Alternatively, a `ref` can be used on the custom element to imperatively apply the event listener to the HTML element directly.
|
||||
|
||||
#### Interop layers: @skatejs/val and reactify-wc
|
||||
|
||||
[@skatejs/val](https://github.com/skatejs/val) is a small library that wraps React's `createElement` function and provides the ability direct React _props_ explicitly to HTML attributes, DOM properties, or to declarative event listeners.
|
||||
|
||||
Another good option is [reactify-wc](https://github.com/BBKolton/reactify-wc). It provides similar capabilities as `@skatejs/val` but does so by creating component wrappers.
|
|
@ -1,260 +0,0 @@
|
|||
---
|
||||
id: rollup
|
||||
title: Rollup
|
||||
sidebar_label: Rollup
|
||||
custom_edit_url: https://github.com/microsoft/fast/edit/master/packages/web-components/fast-foundation/docs/integrations/rollup.md
|
||||
description: FAST works great with Rollup and TypeScript, using a standard setup. Let's take a look at how you can set up a FAST+Rollup+TypeScript project, starting from scratch.
|
||||
keywords:
|
||||
- rollup
|
||||
- typescript
|
||||
---
|
||||
|
||||
FAST works great with Rollup and TypeScript, using a standard setup. Let's take a look at how you can set up a FAST+Rollup+TypeScript project, starting from scratch.
|
||||
|
||||
## Setting up the package
|
||||
|
||||
First, let's make a directory for our new project. From the terminal:
|
||||
|
||||
```shell
|
||||
mkdir fast-rollup
|
||||
```
|
||||
|
||||
Next, let's move into that directory, where we'll set up our project:
|
||||
|
||||
```shell
|
||||
cd fast-rollup
|
||||
```
|
||||
|
||||
From here, we'll initialize npm:
|
||||
|
||||
```shell
|
||||
npm init
|
||||
```
|
||||
|
||||
Follow the prompts from npm, answering each question in turn. You can always accept the defaults at first and then make changes later in the `package.json` file.
|
||||
|
||||
Next, we'll install the FAST packages, along with supporting libraries. To do that, run this command:
|
||||
|
||||
```shell
|
||||
npm install @microsoft/fast-components @microsoft/fast-element tslib --save
|
||||
```
|
||||
|
||||
We also need to install the Rollup build tooling:
|
||||
|
||||
```shell
|
||||
npm install rollup typescript @rollup/plugin-typescript @rollup/plugin-node-resolve rollup-plugin-cleaner rollup-plugin-copy rollup-plugin-serve rollup-plugin-terser --save-dev
|
||||
```
|
||||
|
||||
## Adding configuration and source
|
||||
|
||||
Now that we've got our basic package and dependencies set up, let's create some source files and get things configured. Since we're going to be writing a bit of code, now is a great time to involve a code editor in the process. If you're looking for a great editor for TypeScript and front-end in general, we highly recommend [VS Code](https://code.visualstudio.com/).
|
||||
|
||||
Open the `fast-rollup` folder in your favorite editor. You should see your `package.json` along with a `package-lock.json` and a `node_modules` folder.
|
||||
|
||||
First, let's create a `src` folder where we'll put all our TypeScript code. In the `src` folder, add a `main.ts` file. You can leave the file empty for now. We'll come back to it in a bit.
|
||||
|
||||
Then, in the root of your project folder, add a `tsconfig.json` file to configure the TypeScript compiler. Here's an example starter config that you can put into that file:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"pretty": true,
|
||||
"target": "ES2015",
|
||||
"module": "ES2015",
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"experimentalDecorators": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"noEmitOnError": true,
|
||||
"strict": true,
|
||||
"outDir": "dist/build",
|
||||
"rootDir": "src",
|
||||
"lib": [
|
||||
"dom",
|
||||
"esnext"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
You can learn more about `tsconfig.json` options in [the official TypeScript documentation](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
|
||||
|
||||
:::note
|
||||
Do not set `useDefineForClassFields` to `true` in your `tsconfig.json` if you are using decorators (e.g. `ExperimentalDecorators`). These two features conflict at present. This will be resolved in future versions of TypeScript and FAST.
|
||||
:::
|
||||
|
||||
Next, create a `rollup.config.js` file in the root of your project folder with the following source:
|
||||
|
||||
```js
|
||||
import transformTaggedTemplate from 'rollup-plugin-transform-tagged-template';
|
||||
import typescript from '@rollup/plugin-typescript';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import cleaner from 'rollup-plugin-cleaner';
|
||||
import copy from 'rollup-plugin-copy';
|
||||
import serve from 'rollup-plugin-serve';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
|
||||
export default {
|
||||
input: 'src/main.ts',
|
||||
output: {
|
||||
file: 'dist/bundle.js',
|
||||
name: 'bundle',
|
||||
format: 'umd',
|
||||
sourcemap: true
|
||||
},
|
||||
plugins: [
|
||||
transformTaggedTemplate({
|
||||
tagsToProcess: ['html','css'],
|
||||
parserOptions: {
|
||||
sourceType: "module",
|
||||
plugins: [
|
||||
"typescript",
|
||||
[
|
||||
"decorators",
|
||||
{ decoratorsBeforeExport: true }
|
||||
]
|
||||
]
|
||||
},
|
||||
transformer(data) {
|
||||
data = data.replace(/\s([{}()>~+=^$:!;])\s/gm, '$1');
|
||||
data = data.replace(/([",[]])\s+/gm, '$1');
|
||||
data = data.replace(/\s{2,}/gm, ' ');
|
||||
return data.trim();
|
||||
}
|
||||
}),
|
||||
typescript(),
|
||||
resolve(),
|
||||
cleaner({
|
||||
targets: [
|
||||
'dist'
|
||||
]
|
||||
}),
|
||||
copy({
|
||||
targets: [
|
||||
{ src: 'index.html', dest: 'dist' },
|
||||
]
|
||||
}),
|
||||
serve({
|
||||
open: true,
|
||||
contentBase: 'dist'
|
||||
}),
|
||||
terser(),
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
Let's go over our config file:
|
||||
|
||||
- `input` specifies the entrypoint of our application.
|
||||
- `output` defines the configuration for the JavaScript bundle that Rollup generates.
|
||||
- This is the script that gets loaded by our `index.html` file; in our case, it is `bundle.js`.
|
||||
- `plugins` is where you can add additional functionality to Rollup.
|
||||
|
||||
Let's go over the plugins we're using:
|
||||
- `transformTaggedTemplate` minifies content within tagged templates (e.g. html and css)
|
||||
- `typescript` allows us to write our source files in TypeScript.
|
||||
- `resolve` allows us to import modules installed from npm, like `@microsoft/fast-components`.
|
||||
- `cleaner` will delete the `dist` folder before rebuilding.
|
||||
- This ensures `dist` remains up-to-date with our source code.
|
||||
- Without this plugin, Rollup would only add or overwrite files in the `dist` folder, but never delete them.
|
||||
- `copy` will copy source files to our `dist` folder, ensuring everything we need for deploying our app is in one place.
|
||||
- `serve` provides a development server that will open our application in the browser for us.
|
||||
- `terser` will minify the generated es bundle.
|
||||
|
||||
Let's add some helpful commands to our `package.json` file. Find the `scripts` section of your `package.json` file and add the following lines:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"build": "rollup --config",
|
||||
"dev": "rollup --config --watch"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `npm run build`: running this command in the terminal will build our application into the `dist` folder, allowing us to run it locally or deploy it.
|
||||
|
||||
- `npm run dev`: running this command in the terminal does the same as `build`, except it will keep a process running that rebuilds our app whenever our source files change. Rollup will watch our entrypoint `main.ts` and any files that get imported from there.
|
||||
|
||||
To complete our setup, we need to add an `index.html` file to the root of our project. We'll start with some basic content as follows:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>FAST Rollup</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
There's nothing special about the HTML yet, other than the `script` tag in the `body` that references the `bundle.js` file that our Rollup build will produce.
|
||||
|
||||
## Using the components
|
||||
|
||||
With all the basic pieces in place, let's run our app with `npm run build`.
|
||||
|
||||
Rollup should build your project and open your default browser with your `index.html` page. Right now, it should be blank, since we haven't added any code or interesting HTML. Let's change that.
|
||||
|
||||
First, open your `src/main.ts` file and add the following code:
|
||||
|
||||
```ts
|
||||
import {
|
||||
provideFASTDesignSystem,
|
||||
fastCard,
|
||||
fastButton
|
||||
} from '@microsoft/fast-components';
|
||||
|
||||
provideFASTDesignSystem()
|
||||
.register(
|
||||
fastCard(),
|
||||
fastButton()
|
||||
);
|
||||
```
|
||||
|
||||
This code uses the FAST Design System to register the `<fast-card>` and `<fast-button>` components. To get some UI showing, we need to write some HTML that uses our components.
|
||||
|
||||
Replace the contents of the `<body>` in your `index.html` file with the following markup:
|
||||
|
||||
```html
|
||||
<body>
|
||||
<fast-card>
|
||||
<h2>Hello World!</h2>
|
||||
<fast-button appearance="accent">Click Me</fast-button>
|
||||
</fast-card>
|
||||
<style>
|
||||
:not(:defined) {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
fast-card {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: white;
|
||||
font-size: var(--type-ramp-plus-5-font-size);
|
||||
line-height: var(--type-ramp-plus-5-line-height);
|
||||
}
|
||||
|
||||
fast-card > fast-button {
|
||||
align-self: flex-end;
|
||||
}
|
||||
</style>
|
||||
<script src="/bundle.js"></script>
|
||||
</body>
|
||||
```
|
||||
|
||||
After saving your `index.html` file, run `npm run build` again, and you should see a card with text and a button.
|
||||
|
||||
Congratulations! You're now set up to use FAST, Rollup, and TypeScript. You can import and use more components, build your own components, and when you are ready, build and deploy your website or app to production.
|
|
@ -1,132 +0,0 @@
|
|||
---
|
||||
id: svelte
|
||||
title: Svelte
|
||||
sidebar_label: Svelte
|
||||
custom_edit_url: https://github.com/microsoft/fast/edit/master/packages/web-components/fast-foundation/docs/integrations/svelte.md
|
||||
description: FAST works great with Svelte, Vite, and TypeScript, using a standard setup. Let's take a look at how you can set up a FAST+Svelte+Vite+TypeScript project, starting from scratch.
|
||||
keywords:
|
||||
- svelte
|
||||
- vite
|
||||
- typescript
|
||||
---
|
||||
|
||||
FAST works great with `Svelte`, `Vite`, and `TypeScript`, using a standard setup. Let's take a look at how you can set up a FAST+Svelte+Vite+TypeScript project, starting from scratch.
|
||||
|
||||
## Setting up the Svelte project
|
||||
|
||||
First, you'll need to make sure that you have `Node.js` version >=12.2.0 installed. You can learn more and download that [on the official site](https://nodejs.org/).
|
||||
|
||||
With `Node.js` installed, you can use [create-vite](https://github.com/vitejs/vite/tree/main/packages/create-vite) to scaffold a new Svelte project.
|
||||
|
||||
```shell
|
||||
npm create vite@latest fast-svelte --template svelte-ts
|
||||
```
|
||||
|
||||
Follow the prompts, answering each question in turn. Select `svelte` as the framework and `svelte-ts` as the variant. When the CLI completes, you should have a basic runnable Svelte application.
|
||||
|
||||
Let's move into the project directory, where we'll set up our project.
|
||||
|
||||
```shell
|
||||
cd fast-svelte
|
||||
```
|
||||
|
||||
## Configuring packages
|
||||
|
||||
From your new project folder, run this command to install the FAST packages, along with supporting libraries.
|
||||
|
||||
```shell
|
||||
npm install --save @microsoft/fast-components @microsoft/fast-foundation @microsoft/fast-element
|
||||
```
|
||||
|
||||
## Adding configuration and source
|
||||
|
||||
Now that we have a Svelte project scaffolded, let's get things configured.
|
||||
|
||||
In the root of your project folder, you will see a `tsconfig.js` file. Replace the contents of the file with the following markup:
|
||||
|
||||
```js
|
||||
{
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"useDefineForClassFields": false,
|
||||
"module": "esnext",
|
||||
"resolveJsonModule": true,
|
||||
"experimentalDecorators": true,
|
||||
"baseUrl": ".",
|
||||
/**
|
||||
* Typecheck JS in `.svelte` and `.js` files by default.
|
||||
* Disable checkJs if you'd like to use dynamic types in JS.
|
||||
* Note that setting allowJs false does not prevent the use
|
||||
* of JS in `.svelte` files.
|
||||
*/
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"isolatedModules": true
|
||||
},
|
||||
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
```
|
||||
|
||||
## Using the components
|
||||
|
||||
Open your `main.ts` file and replace the contents of the file with the following code:
|
||||
|
||||
```ts
|
||||
import App from './App.svelte'
|
||||
|
||||
import {
|
||||
provideFASTDesignSystem,
|
||||
fastCard,
|
||||
fastButton
|
||||
} from '@microsoft/fast-components';
|
||||
|
||||
provideFASTDesignSystem()
|
||||
.register(
|
||||
fastCard(),
|
||||
fastButton()
|
||||
);
|
||||
|
||||
const app = new App({
|
||||
target: document.getElementById('app')
|
||||
})
|
||||
|
||||
export default app
|
||||
```
|
||||
|
||||
This code uses the FAST Design System to register the `<fast-card>` and `<fast-button>` components. Let's update our App.svelte file to use our components. Replace the contents of the file with the following markup:
|
||||
|
||||
```html
|
||||
<main>
|
||||
<fast-card>
|
||||
<h2>Hello World!</h2>
|
||||
<fast-button appearance="accent">Click Me</fast-button>
|
||||
</fast-card>
|
||||
|
||||
</main>
|
||||
|
||||
<style>
|
||||
fast-card {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: white;
|
||||
font-size: var(--type-ramp-plus-5-font-size);
|
||||
line-height: var(--type-ramp-plus-5-line-height);
|
||||
}
|
||||
|
||||
fast-card > fast-button {
|
||||
align-self: flex-end;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
With all the basic pieces in place, let's run our app in dev mode with `npm run dev`. The Svelte CLI should build your project and make it available on localhost.
|
||||
|
||||
Congratulations! You're now set up to use FAST, Svelte, Vite, and TypeScript. You can import and use more components, build your own components, and when you are ready, build and deploy your website or app to production.
|
||||
|
||||
See a FAST+Svelte+Vite+TypeScript starter project [here](https://github.com/microsoft/fast/tree/master/examples/svelte-starters/svelte-fast-typescript-starter).
|
|
@ -1,154 +0,0 @@
|
|||
---
|
||||
id: vite
|
||||
title: Vite
|
||||
sidebar_label: Vite
|
||||
custom_edit_url: https://github.com/microsoft/fast/edit/master/packages/web-components/fast-foundation/docs/integrations/vite.md
|
||||
description: FAST works great with TypeScript and Vite, using a fairly standard setup. Let's take a look at how you can set up a FAST+Vite+TypeScript project, starting from scratch.
|
||||
keywords:
|
||||
- vite
|
||||
---
|
||||
|
||||
FAST works great with Vite and TypeScript, using a fairly standard setup. Let's take a look at how you can set up a FAST+TypeScript+Vite project, starting from scratch.
|
||||
|
||||
## Setting up the Vite project
|
||||
|
||||
First, you'll need to make sure that you have Node.js version >=12.2.0 installed. You can learn more and download that [on the official site](https://nodejs.org/).
|
||||
|
||||
With Node.js installed, you can use [create-vite](https://github.com/vitejs/vite/tree/main/packages/create-vite) to scaffold a new Vite project.
|
||||
|
||||
```shell
|
||||
npm create vite@latest
|
||||
```
|
||||
|
||||
Follow the prompts, answering each question in turn. Enter `fast-vite` as the project name, select `vanilla` as the framework, and `vanilla-ts` as the variant. When the CLI completes, you should have a basic runnable Vite application.
|
||||
|
||||
Next, we'll move into the project directory, where we'll set up our project.
|
||||
|
||||
```shell
|
||||
cd fast-vite
|
||||
```
|
||||
|
||||
## Configuring packages
|
||||
|
||||
Let's install the FAST packages, along with supporting libraries. To do that, run this command from your new project folder:
|
||||
|
||||
```shell
|
||||
npm install --save @microsoft/fast-components @microsoft/fast-foundation @microsoft/fast-element
|
||||
```
|
||||
|
||||
## Adding configuration and source
|
||||
|
||||
Now that we have a Vite project scaffolded, let's get things configured.
|
||||
|
||||
In the root of your project folder, you will see a `tsconfig.js` file. Replace the contents of the file with the following markup:
|
||||
|
||||
```js
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "esnext",
|
||||
"lib": ["es2017", "dom"],
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "./dist",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitAny": false,
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"experimentalDecorators": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"useDefineForClassFields": false
|
||||
},
|
||||
"include": ["src/*/.ts"]
|
||||
}
|
||||
```
|
||||
|
||||
Then, create a new file at the root of your project folder called `vite.config.js` and add the following code:
|
||||
|
||||
```js
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
build: {
|
||||
lib: {
|
||||
entry: 'src/main.ts',
|
||||
formats: ['es']
|
||||
},
|
||||
rollupOptions: {
|
||||
external: /^@microsoft\/fast-(element|components)/
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
## Using the components
|
||||
|
||||
With all the basic pieces in place, let's run our app in dev mode with `npm run dev`. The Vite CLI should build your project and make it available on localhost. Right now, it displays a basic welcome message since we haven't added any code or interesting HTML. Let's change that.
|
||||
|
||||
First, open your `src/main.ts` file and replace the contents of the file with the following code:
|
||||
|
||||
```ts
|
||||
import {
|
||||
provideFASTDesignSystem,
|
||||
fastCard,
|
||||
fastButton
|
||||
} from '@microsoft/fast-components';
|
||||
|
||||
provideFASTDesignSystem()
|
||||
.register(
|
||||
fastCard(),
|
||||
fastButton()
|
||||
);
|
||||
```
|
||||
|
||||
This code uses the FAST Design System to register the `<fast-card>` and `<fast-button>` components. Once you save, the dev server will rebuild and refresh your browser. However, you still won't see anything. To get some UI showing up, we need to update the HTML that uses our components. Replace the HTML template at the root of your folder with the following markup:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>FAST Vite</title>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
<link rel="stylesheet" href="/src/style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<fast-card>
|
||||
<h2>Hello World!</h2>
|
||||
<fast-button appearance="accent">Click Me</fast-button>
|
||||
</fast-card>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Next, replace the contents of your `src/style.css` file with the following code:
|
||||
|
||||
```css
|
||||
:not(:defined) {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
fast-card {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: white;
|
||||
font-size: var(--type-ramp-plus-5-font-size);
|
||||
line-height: var(--type-ramp-plus-5-line-height);
|
||||
}
|
||||
|
||||
fast-card > fast-button {
|
||||
align-self: flex-end;
|
||||
}
|
||||
```
|
||||
|
||||
After saving your `style.css` file, your browser will automatically refresh and you should see a card with text and a button.
|
||||
|
||||
Congratulations! You're now set up to use FAST, TypeScript, and Vite. You can import and use more components, build your own components, and when you are ready, build and deploy your website or app to production.
|
||||
|
||||
See a FAST+Vite+TypeScript starter project [here](https://github.com/microsoft/fast/tree/master/examples/vite-starters/vite-fast-typescript-starter).
|
|
@ -1,111 +0,0 @@
|
|||
---
|
||||
id: vue
|
||||
title: Vue
|
||||
sidebar_label: Vue
|
||||
custom_edit_url: https://github.com/microsoft/fast/edit/master/packages/web-components/fast-foundation/docs/integrations/vue.md
|
||||
description: FAST works great with Vue. Let's take a look at how you can set up a Vue project, starting from scratch.
|
||||
keywords:
|
||||
- vue
|
||||
---
|
||||
|
||||
FAST works great with Vue. Let's take a look at how you can set up a Vue project, starting from scratch.
|
||||
|
||||
## Setting up the Vue project
|
||||
|
||||
First, you'll need to make sure that you have Node.js installed. You can learn more and download that [on the official site](https://nodejs.org/).
|
||||
|
||||
With Node.js installed, you can run the following command to install the Vue CLI:
|
||||
|
||||
```shell
|
||||
npm install -g @vue/cli
|
||||
```
|
||||
|
||||
With the CLI installed, you have access to the `vue` command-line interface. This can be used to create a new Vue project. For example, to create a new Vue App named "fast-vue", you would use the following command:
|
||||
|
||||
```shell
|
||||
vue create fast-vue
|
||||
```
|
||||
|
||||
When prompted to select options, choose "Manually select features". Follow the prompts, answering each question in turn. It is recommended that you select "TypeScript" when prompted.
|
||||
|
||||
When the CLI completes, you should have a basic runnable Vue application.
|
||||
|
||||
## Configuring packages
|
||||
|
||||
Next, we'll install the FAST packages, along with supporting libraries. To do that, run this command from your new project folder:
|
||||
|
||||
```shell
|
||||
npm install --save @microsoft/fast-components @microsoft/fast-element
|
||||
```
|
||||
|
||||
## Using the components
|
||||
|
||||
With all the basic pieces in place, let's run our app in dev mode with `npm run serve`. The Vue CLI should build your project and make it available on localhost. Right now, it displays a basic welcome message, since we haven't added any code or interesting HTML. Let's change that.
|
||||
|
||||
First, open your `src/main.ts` file and add the following code:
|
||||
|
||||
```ts
|
||||
import {
|
||||
provideFASTDesignSystem,
|
||||
fastCard,
|
||||
fastButton
|
||||
} from '@microsoft/fast-components';
|
||||
|
||||
provideFASTDesignSystem()
|
||||
.register(
|
||||
fastCard(),
|
||||
fastButton()
|
||||
);
|
||||
```
|
||||
|
||||
This code uses the FAST Design System to register the `<fast-card>` and `<fast-button>` components. Once you save, the dev server will rebuild and refresh your browser. However, you still won't see anything. To get some UI showing up, we need to write some HTML that uses our components. Replace the HTML template in your `components/HelloWorld.vue` file with the following markup:
|
||||
|
||||
```html
|
||||
<template>
|
||||
<fast-card>
|
||||
<h2>{{msg}}</h2>
|
||||
<fast-button appearance="accent" v-on:click="onClick">Click Me</fast-button>
|
||||
</fast-card>
|
||||
</template>
|
||||
```
|
||||
|
||||
Replace your script tag with this:
|
||||
|
||||
```html
|
||||
<script>
|
||||
export default {
|
||||
name: 'HelloWorld',
|
||||
props: {
|
||||
msg: String
|
||||
},
|
||||
methods: {
|
||||
onClick: function () {
|
||||
console.log('clicked!');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
To add a splash of style, replace the `style` tag with this:
|
||||
|
||||
```html
|
||||
<style scoped>
|
||||
fast-card {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--type-ramp-plus-5-font-size);
|
||||
line-height: var(--type-ramp-plus-5-line-height);
|
||||
}
|
||||
|
||||
fast-card > fast-button {
|
||||
align-self: flex-end;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
Congratulations! You're now set up to use FAST and Vue!
|
|
@ -1,419 +0,0 @@
|
|||
---
|
||||
id: webpack
|
||||
title: Webpack
|
||||
sidebar_label: Webpack
|
||||
custom_edit_url: https://github.com/microsoft/fast/edit/master/packages/web-components/fast-foundation/docs/integrations/webpack.md
|
||||
description: FAST works great with TypeScript and Webpack, using a fairly standard setup. Let's take a look at how you can set up a TypeScript+Webpack project, starting from scratch.
|
||||
---
|
||||
|
||||
FAST works great with TypeScript and Webpack, using a fairly standard setup. Let's take a look at how you can set up a TypeScript+Webpack project, starting from scratch.
|
||||
We recommend using TypeScript, but if you are using Babel, skip down to [Setting up the package with Babel](#setting-up-the-package-with-babel)
|
||||
|
||||
## Setting up Webpack
|
||||
|
||||
First, let's make a directory for our new project. From the terminal:
|
||||
|
||||
```shell
|
||||
mkdir fast-webpack
|
||||
```
|
||||
|
||||
Next, let's move into that directory, where we'll set up our project:
|
||||
|
||||
```shell
|
||||
cd fast-webpack
|
||||
```
|
||||
|
||||
From here, we'll initialize npm:
|
||||
|
||||
```shell
|
||||
npm init
|
||||
```
|
||||
|
||||
Follow the prompts from npm, answering each question in turn. You can always accept the defaults at first and then make changes later in the package.json file.
|
||||
|
||||
Next, we'll install the FAST packages, along with supporting libraries. To do that, run this command:
|
||||
|
||||
```shell
|
||||
npm install --save @microsoft/fast-components @microsoft/fast-element
|
||||
```
|
||||
|
||||
We also need to install the Webpack build tooling:
|
||||
|
||||
```shell
|
||||
npm install --save-dev clean-webpack-plugin webpack webpack-cli webpack-dev-server
|
||||
```
|
||||
|
||||
## Setting up the package with TypeScript
|
||||
|
||||
Install TypeScript build tooling:
|
||||
|
||||
```shell
|
||||
npm install --save-dev ts-loader typescript
|
||||
```
|
||||
|
||||
### Adding configuration for TypeScript
|
||||
|
||||
Now that we've got our basic package and dependencies set up, let's create some source files and get things configured. Since we're going to be writing a bit of code, now is a great time to involve a code editor in the process. If you're looking for a great editor for TypeScript and front-end in general, we highly recommend [VS Code](https://code.visualstudio.com/).
|
||||
|
||||
Open the `fast-webpack` folder in your favorite editor. You should see your `package.json` along with a `package-lock.json` and a `node_modules` folder.
|
||||
|
||||
First, let's create a `src` folder where we'll put all our TypeScript code. In the `src` folder, add a `main.ts` file. You can leave the file empty for now. We'll come back to it in a bit.
|
||||
|
||||
Next, in the root of your project folder, add a `tsconfig.json` file to configure the TypeScript compiler. Here's an example starter config that you can put into that file:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"pretty": true,
|
||||
"target": "ES2015",
|
||||
"module": "ES2015",
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"experimentalDecorators": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"noEmitOnError": true,
|
||||
"strict": true,
|
||||
"outDir": "dist/build",
|
||||
"rootDir": "src",
|
||||
"lib": ["dom", "esnext"]
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
```
|
||||
|
||||
You can learn more about `tsconfig.json` options in [the official TypeScript documentation](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
|
||||
|
||||
:::note
|
||||
Do not set `useDefineForClassFields` to `true` in your `tsconfig.json` if you are using decorators. These two features conflict at present. This will be resolved in future versions of TypeScript and FAST.
|
||||
:::
|
||||
|
||||
Next, create a `webpack.config.js` file in the root of your project folder with the following source:
|
||||
|
||||
```js
|
||||
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
||||
const path = require("path");
|
||||
|
||||
module.exports = function (env, { mode }) {
|
||||
const production = mode === "production";
|
||||
return {
|
||||
mode: production ? "production" : "development",
|
||||
devtool: production ? "source-map" : "inline-source-map",
|
||||
entry: {
|
||||
app: ["./src/main.ts"],
|
||||
},
|
||||
output: {
|
||||
filename: "bundle.js",
|
||||
publicPath: "/",
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".ts", ".js"],
|
||||
modules: ["src", "node_modules"],
|
||||
},
|
||||
devServer: {
|
||||
port: 9000,
|
||||
historyApiFallback: true,
|
||||
open: !process.env.CI,
|
||||
devMiddleware: {
|
||||
writeToDisk: true,
|
||||
},
|
||||
static: {
|
||||
directory: path.join(__dirname, "./"),
|
||||
},
|
||||
},
|
||||
plugins: [new CleanWebpackPlugin()],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/i,
|
||||
use: [
|
||||
{
|
||||
loader: "ts-loader",
|
||||
},
|
||||
],
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
This setup uses `ts-loader` to process TypeScript. It will also enable both a production mode and a development mode that watches your source, recompiling and refreshing your browser as things change. You can read more about Webpack configuration in [the official Webpack documentation](https://webpack.js.org/).
|
||||
|
||||
### Completing the setup
|
||||
|
||||
To enable easy execution of both our production and development builds, let's add some script commands to our `package.json` file. Find the `scripts` section of your `package.json` file and add the following two scripts:
|
||||
|
||||
```json
|
||||
"scripts": {
|
||||
"build": "webpack --mode=production",
|
||||
"dev": "webpack serve"
|
||||
}
|
||||
```
|
||||
|
||||
The `build` script will build your TypeScript for production deployment while the `dev` script will run the development web server so you can write code and see the results in your browser. You can run these scripts with `npm run build` and `npm run dev` respectively.
|
||||
|
||||
To complete our setup, we need to add an `index.html` file to the root of our project. We'll start with some basic content as follows:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>FAST Webpack</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="dist/bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
There's nothing special about the HTML yet other than the `script` tag in the `body` that references the `bundle.js` file that our Webpack build will produce.
|
||||
|
||||
### Using the components
|
||||
|
||||
With all the basic pieces in place, let's run our app in dev mode with `npm run dev`. Webpack should build your project and open your default browser with your `index.html` page. Right now, it should be blank, since we haven't added any code or interesting HTML. Let's change that.
|
||||
|
||||
First, open your `src/main.ts` file and add the following code:
|
||||
|
||||
```ts
|
||||
import {
|
||||
provideFASTDesignSystem,
|
||||
fastCard,
|
||||
fastButton,
|
||||
} from "@microsoft/fast-components";
|
||||
|
||||
provideFASTDesignSystem().register(fastCard(), fastButton());
|
||||
```
|
||||
|
||||
This code uses the FAST Design System to register the `<fast-card>` and `<fast-button>` components. Once you save, the dev server will rebuild and refresh your browser. However, you still won't see anything. To get some UI showing up, we need to write some HTML that uses our components. Replace the contents of the `<body>` in your `index.html` file with the following markup:
|
||||
|
||||
```html
|
||||
<body>
|
||||
<fast-card>
|
||||
<h2>Hello World!</h2>
|
||||
<fast-button appearance="accent">Click Me</fast-button>
|
||||
</fast-card>
|
||||
<style>
|
||||
:not(:defined) {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
fast-card {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--type-ramp-plus-5-font-size);
|
||||
line-height: var(--type-ramp-plus-5-line-height);
|
||||
}
|
||||
|
||||
fast-card > fast-button {
|
||||
align-self: flex-end;
|
||||
}
|
||||
</style>
|
||||
<script src="dist/bundle.js"></script>
|
||||
</body>
|
||||
```
|
||||
|
||||
After saving your `index.html` file, refresh your browser and you should see a card with text and a button.
|
||||
|
||||
Congratulations! You're now set up to use FAST, TypeScript, and Webpack. You can import and use more components, build your own components, and when you are ready, build and deploy your website or app to production.
|
||||
|
||||
## Setting up the package with Babel
|
||||
|
||||
Follow the steps from [Setting up Webpack](#setting-up-webpack)
|
||||
|
||||
Then, install Babel build tooling:
|
||||
|
||||
```shell
|
||||
npm install --save-dev babel-loader @babel/preset-env
|
||||
```
|
||||
|
||||
### Adding configuration for Babel
|
||||
|
||||
First, let's create a `src` folder. In the `src` folder, add a `main.js` file.
|
||||
|
||||
Next, in the root of your project folder, add a `jsconfig.json` file. Here's an example starter config that you can put into that file:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"allowJs": true,
|
||||
"experimentalDecorators": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next, we'll need a `.babelrc.json` file, we don't have one yet, so let's create one in the root directory. We can start with a simple setup:
|
||||
|
||||
```json
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"browsers": ["last 2 versions", "safari >= 7"]
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Here, by adding `@babel/preset-env` to presets will allow you to use the latest JavaScript features.
|
||||
Targeting specific browser versions prevents Babel from transpiling too much to support old JavaScript versions, increasing total file size and potentially hurting overall performance.
|
||||
|
||||
Then, create a `webpack.config.js` file in the root of your project folder with the following source:
|
||||
|
||||
```js
|
||||
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
||||
const path = require("path");
|
||||
|
||||
module.exports = function (env, { mode }) {
|
||||
const production = mode === "production";
|
||||
return {
|
||||
mode: production ? "production" : "development",
|
||||
devtool: production ? "source-map" : "inline-source-map",
|
||||
entry: {
|
||||
app: ["./src/main.js"],
|
||||
},
|
||||
output: {
|
||||
filename: "bundle.js",
|
||||
publicPath: "/",
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".js"],
|
||||
modules: ["src", "node_modules"],
|
||||
},
|
||||
devServer: {
|
||||
port: 9000,
|
||||
historyApiFallback: true,
|
||||
open: !process.env.CI,
|
||||
devMiddleware: {
|
||||
writeToDisk: true,
|
||||
},
|
||||
static: {
|
||||
directory: path.join(__dirname, "./"),
|
||||
},
|
||||
},
|
||||
plugins: [new CleanWebpackPlugin()],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/i,
|
||||
use: [
|
||||
{
|
||||
loader: "babel-loader",
|
||||
},
|
||||
],
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
This setup uses the `babel-loader` to transpile JavaScript files.
|
||||
|
||||
Finish up the setup by following steps from [Completing the setup](#completing-the-setup).
|
||||
|
||||
Then, try to add some components by following [Using the components](#using-the-components).
|
||||
|
||||
At this point, you should be able to see some FAST components show up! You're now set up to use FAST, Babel, and Webpack.
|
||||
|
||||
### Creating Custom Components with Babel
|
||||
|
||||
Let's take a look at how you can create custom components with Babel.
|
||||
|
||||
First, open your `src/main.js` file and add the following code:
|
||||
|
||||
```js
|
||||
import { FASTElement, customElement, attr } from "@microsoft/fast-element";
|
||||
import {
|
||||
provideFASTDesignSystem,
|
||||
fastCard,
|
||||
fastButton,
|
||||
} from "@microsoft/fast-components";
|
||||
|
||||
@customElement("name-tag")
|
||||
class NameTag extends FASTElement {
|
||||
@attr greeting = "";
|
||||
}
|
||||
|
||||
provideFASTDesignSystem().register(fastCard(), fastButton(), NameTag);
|
||||
```
|
||||
|
||||
At this point, when you try to build this piece of code, you will most likely get a error in `src/main.js`"
|
||||
|
||||
> SyntaxError: Support for the experimental syntax 'decorators' isn't currently enabled
|
||||
|
||||
Let's add a babel plugins to help us with this error:
|
||||
|
||||
```shell
|
||||
npm install --save-dev @babel/plugin-proposal-decorators
|
||||
```
|
||||
|
||||
With this plugin installed, we'll need to add it in the `.babelrc.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"browsers": ["last 2 versions", "safari >= 7"]
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": [["@babel/plugin-proposal-decorators", { "legacy": true }]]
|
||||
}
|
||||
```
|
||||
|
||||
Adding the `@babel/plugin-proposal-decorators` plugin should fix the syntax error we got earlier. And your Babel application should work with decorators now.
|
||||
|
||||
Let's add our custom component to the `index.html` file:
|
||||
|
||||
```html
|
||||
<body>
|
||||
<fast-card>
|
||||
<h2>Hello World!</h2>
|
||||
<fast-button appearance="accent">Click Me</fast-button>
|
||||
<name-tag greeting="hello"></name-tag>
|
||||
</fast-card>
|
||||
<style>
|
||||
:not(:defined) {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
fast-card {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--type-ramp-plus-5-font-size);
|
||||
line-height: var(--type-ramp-plus-5-line-height);
|
||||
}
|
||||
|
||||
fast-card > fast-button {
|
||||
align-self: flex-end;
|
||||
}
|
||||
</style>
|
||||
<script src="dist/bundle.js"></script>
|
||||
</body>
|
||||
```
|
||||
|
||||
Now, when you inspect the devtools, you should be able to see the `greeting` attribute from `<name-tag>` present with the `hello` property!
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
id: component-explorer
|
||||
title: Component Explorer
|
||||
sidebar_label: Component Explorer
|
||||
custom_edit_url: https://github.com/microsoft/fast/edit/master/packages/web-components/fast-foundation/docs/tools/component-explorer.md
|
||||
description: Launch our Component Explorer to experience our FAST Components and development tools.
|
||||
---
|
||||
|
||||
Launch our [Component Explorer](https://explore.fast.design) to experience our [FAST Components](https://www.npmjs.com/package/@microsoft/fast-components) and development tools.
|
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
id: hot-module-reload
|
||||
title: Hot Module Reload
|
||||
sidebar_label: Hot Module Reload
|
||||
custom_edit_url: https://github.com/microsoft/fast/edit/master/packages/web-components/fast-foundation/docs/tools/hot-module-reload.md
|
||||
description: Hot Module Replacement (HMR) allows your web components to be updated as you develop, without needing a full refresh of the browser.
|
||||
---
|
||||
|
||||
Hot Module Replacement (HMR) allows your web components to be updated as you develop, without needing a full refresh of the browser. The [Open Web Components](https://open-wc.org/) project maintains a number of tools and libraries for working with Web Components, including a plugin for enabling HMR with FAST Web Components.
|
||||
|
||||
To learn more about HMR for Web Components and to see how to setup HMR in your own FAST projects, [please see the Open Web Components HMR documentation](https://open-wc.org/docs/development/hot-module-replacement/).
|
||||
|
||||
:::note
|
||||
HMR is limited in what it can update for a given component. So, full-page refreshes are still needed in certain cases. Changes to constructor logic or adding new `@attr` properties are notable examples of HMR's update limitations. However, template and style changes should be handled automatically without needing a full refresh.
|
||||
:::
|
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
id: vscode
|
||||
title: Visual Studio Code
|
||||
sidebar_label: Visual Studio Code
|
||||
custom_edit_url: https://github.com/microsoft/fast/edit/master/packages/web-components/fast-foundation/docs/tools/vscode.md
|
||||
description: You can use any code editor you like when working with FAST. One of our favorites is Visual Studio Code.
|
||||
---
|
||||
|
||||
You can use any code editor you like when working with FAST. One of our favorites is [Visual Studio Code](https://code.visualstudio.com/). VS Code has great support for API autocomplete for TypeScript and JavaScript APIs, as well as a rich ecosystem of plugins.
|
||||
|
||||
When working with VS Code, we recommend using these plugins:
|
||||
|
||||
* [FAST Snippets](https://marketplace.visualstudio.com/items?itemName=kingoftac.fast-snippets) to get commonly used conventions when creating `FAST Components`.
|
||||
* [literally-html](https://marketplace.visualstudio.com/items?itemName=webreflection.literally-html) to get syntax highlighting and documentation in your `html` blocks.
|
||||
* [es6-string-css](https://marketplace.visualstudio.com/items?itemName=bashmish.es6-string-css) to get syntax highlighting in your `css` blocks.
|
|
@ -1,282 +0,0 @@
|
|||
{
|
||||
"name": "@microsoft/fast-foundation",
|
||||
"description": "A library of Web Component building blocks",
|
||||
"version": "3.0.0-alpha.33",
|
||||
"sideEffects": false,
|
||||
"author": {
|
||||
"name": "Microsoft",
|
||||
"url": "https://discord.gg/FcSNfg4"
|
||||
},
|
||||
"homepage": "https://www.fast.design/",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Microsoft/fast.git",
|
||||
"directory": "packages/web-components/fast-foundation"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/Microsoft/fast/issues/new/choose"
|
||||
},
|
||||
"main": "dist/esm/index.js",
|
||||
"types": "dist/dts/index.d.ts",
|
||||
"unpkg": "dist/fast-foundation.min.js",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/dts/index.d.ts",
|
||||
"default": "./dist/esm/index.js"
|
||||
},
|
||||
"./accordion.js": {
|
||||
"types": "./dist/dts/accordion/index.d.ts",
|
||||
"default": "./dist/esm/accordion/index.js"
|
||||
},
|
||||
"./accordion-item.js": {
|
||||
"types": "./dist/dts/accordion-item/index.d.ts",
|
||||
"default": "./dist/esm/accordion-item/index.js"
|
||||
},
|
||||
"./anchor.js": {
|
||||
"types": "./dist/dts/anchor/index.d.ts",
|
||||
"default": "./dist/esm/anchor/index.js"
|
||||
},
|
||||
"./anchored-region.js": {
|
||||
"types": "./dist/dts/anchored-region/index.d.ts",
|
||||
"default": "./dist/esm/anchored-region/index.js"
|
||||
},
|
||||
"./avatar.js": {
|
||||
"types": "./dist/dts/avatar/index.d.ts",
|
||||
"default": "./dist/esm/avatar/index.js"
|
||||
},
|
||||
"./badge.js": {
|
||||
"types": "./dist/dts/badge/index.d.ts",
|
||||
"default": "./dist/esm/badge/index.js"
|
||||
},
|
||||
"./breadcrumb.js": {
|
||||
"types": "./dist/dts/breadcrumb/index.d.ts",
|
||||
"default": "./dist/esm/breadcrumb/index.js"
|
||||
},
|
||||
"./breadcrumb-item.js": {
|
||||
"types": "./dist/dts/breadcrumb-item/index.d.ts",
|
||||
"default": "./dist/esm/breadcrumb-item/index.js"
|
||||
},
|
||||
"./button.js": {
|
||||
"types": "./dist/dts/button/index.d.ts",
|
||||
"default": "./dist/esm/button/index.js"
|
||||
},
|
||||
"./calendar.js": {
|
||||
"types": "./dist/dts/calendar/index.d.ts",
|
||||
"default": "./dist/esm/calendar/index.js"
|
||||
},
|
||||
"./card.js": {
|
||||
"types": "./dist/dts/card/index.d.ts",
|
||||
"default": "./dist/esm/card/index.js"
|
||||
},
|
||||
"./checkbox.js": {
|
||||
"types": "./dist/dts/checkbox/index.d.ts",
|
||||
"default": "./dist/esm/checkbox/index.js"
|
||||
},
|
||||
"./combobox.js": {
|
||||
"types": "./dist/dts/combobox/index.d.ts",
|
||||
"default": "./dist/esm/combobox/index.js"
|
||||
},
|
||||
"./data-grid.js": {
|
||||
"types": "./dist/dts/data-grid/index.d.ts",
|
||||
"default": "./dist/esm/data-grid/index.js"
|
||||
},
|
||||
"./design-token.js": {
|
||||
"types": "./dist/dts/design-token/exports.d.ts",
|
||||
"default": "./dist/esm/design-token/exports.js"
|
||||
},
|
||||
"./dialog.js": {
|
||||
"types": "./dist/dts/dialog/index.d.ts",
|
||||
"default": "./dist/esm/dialog/index.js"
|
||||
},
|
||||
"./disclosure.js": {
|
||||
"types": "./dist/dts/disclosure/index.d.ts",
|
||||
"default": "./dist/esm/disclosure/index.js"
|
||||
},
|
||||
"./divider.js": {
|
||||
"types": "./dist/dts/divider/index.d.ts",
|
||||
"default": "./dist/esm/divider/index.js"
|
||||
},
|
||||
"./flipper.js": {
|
||||
"types": "./dist/dts/flipper/index.d.ts",
|
||||
"default": "./dist/esm/flipper/index.js"
|
||||
},
|
||||
"./horizontal-scroll.js": {
|
||||
"types": "./dist/dts/horizontal-scroll/index.d.ts",
|
||||
"default": "./dist/esm/horizontal-scroll/index.js"
|
||||
},
|
||||
"./listbox.js": {
|
||||
"types": "./dist/dts/listbox/index.d.ts",
|
||||
"default": "./dist/esm/listbox/index.js"
|
||||
},
|
||||
"./listbox-option.js": {
|
||||
"types": "./dist/dts/listbox-option/index.d.ts",
|
||||
"default": "./dist/esm/listbox-option/index.js"
|
||||
},
|
||||
"./menu.js": {
|
||||
"types": "./dist/dts/menu/index.d.ts",
|
||||
"default": "./dist/esm/menu/index.js"
|
||||
},
|
||||
"./menu-item.js": {
|
||||
"types": "./dist/dts/menu-item/index.d.ts",
|
||||
"default": "./dist/esm/menu-item/index.js"
|
||||
},
|
||||
"./number-field.js": {
|
||||
"types": "./dist/dts/number-field/index.d.ts",
|
||||
"default": "./dist/esm/number-field/index.js"
|
||||
},
|
||||
"./picker.js": {
|
||||
"types": "./dist/dts/picker/index.d.ts",
|
||||
"default": "./dist/esm/picker/index.js"
|
||||
},
|
||||
"./progress.js": {
|
||||
"types": "./dist/dts/progress/index.d.ts",
|
||||
"default": "./dist/esm/progress/index.js"
|
||||
},
|
||||
"./progress-ring.js": {
|
||||
"types": "./dist/dts/progress-ring/index.d.ts",
|
||||
"default": "./dist/esm/progress-ring/index.js"
|
||||
},
|
||||
"./radio.js": {
|
||||
"types": "./dist/dts/radio/index.d.ts",
|
||||
"default": "./dist/esm/radio/index.js"
|
||||
},
|
||||
"./radio-group.js": {
|
||||
"types": "./dist/dts/radio-group/index.d.ts",
|
||||
"default": "./dist/esm/radio-group/index.js"
|
||||
},
|
||||
"./search.js": {
|
||||
"types": "./dist/dts/search/index.d.ts",
|
||||
"default": "./dist/esm/search/index.js"
|
||||
},
|
||||
"./select.js": {
|
||||
"types": "./dist/dts/select/index.d.ts",
|
||||
"default": "./dist/esm/select/index.js"
|
||||
},
|
||||
"./skeleton.js": {
|
||||
"types": "./dist/dts/skeleton/index.d.ts",
|
||||
"default": "./dist/esm/skeleton/index.js"
|
||||
},
|
||||
"./slider.js": {
|
||||
"types": "./dist/dts/slider/index.d.ts",
|
||||
"default": "./dist/esm/slider/index.js"
|
||||
},
|
||||
"./slider-label.js": {
|
||||
"types": "./dist/dts/slider-label/index.d.ts",
|
||||
"default": "./dist/esm/slider-label/index.js"
|
||||
},
|
||||
"./switch.js": {
|
||||
"types": "./dist/dts/switch/index.d.ts",
|
||||
"default": "./dist/esm/switch/index.js"
|
||||
},
|
||||
"./tab.js": {
|
||||
"types": "./dist/dts/tab/index.d.ts",
|
||||
"default": "./dist/esm/tab/index.js"
|
||||
},
|
||||
"./tab-panel.js": {
|
||||
"types": "./dist/dts/tab-panel/index.d.ts",
|
||||
"default": "./dist/esm/tab-panel/index.js"
|
||||
},
|
||||
"./tabs.js": {
|
||||
"types": "./dist/dts/tabs/index.d.ts",
|
||||
"default": "./dist/esm/tabs/index.js"
|
||||
},
|
||||
"./text-area.js": {
|
||||
"types": "./dist/dts/text-area/index.d.ts",
|
||||
"default": "./dist/esm/text-area/index.js"
|
||||
},
|
||||
"./text-field.js": {
|
||||
"types": "./dist/dts/text-field/index.d.ts",
|
||||
"default": "./dist/esm/text-field/index.js"
|
||||
},
|
||||
"./toolbar.js": {
|
||||
"types": "./dist/dts/toolbar/index.d.ts",
|
||||
"default": "./dist/esm/toolbar/index.js"
|
||||
},
|
||||
"./tooltip.js": {
|
||||
"types": "./dist/dts/tooltip/index.d.ts",
|
||||
"default": "./dist/esm/tooltip/index.js"
|
||||
},
|
||||
"./tree-item.js": {
|
||||
"types": "./dist/dts/tree-item/index.d.ts",
|
||||
"default": "./dist/esm/tree-item/index.js"
|
||||
},
|
||||
"./tree-view.js": {
|
||||
"types": "./dist/dts/tree-view/index.d.ts",
|
||||
"default": "./dist/esm/tree-view/index.js"
|
||||
},
|
||||
"./utilities.js": {
|
||||
"types": "./dist/dts/utilities/index.d.ts",
|
||||
"default": "./dist/esm/utilities/index.js"
|
||||
},
|
||||
"./patterns.js": {
|
||||
"types": "./dist/dts/patterns/index.d.ts",
|
||||
"default": "./dist/esm/patterns/index.js"
|
||||
},
|
||||
"./custom-elements.json": "./dist/custom-elements.json",
|
||||
"./design-token-core.js": {
|
||||
"types": "./dist/dts/design-token/core/exports.d.ts",
|
||||
"default": "./dist/esm/design-token/core/exports.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"scripts": {
|
||||
"clean:dist": "rimraf dist storybook-static",
|
||||
"doc": "api-extractor run --local",
|
||||
"doc:ci": "api-extractor run",
|
||||
"build:rollup": "rollup -c",
|
||||
"build:tsc": "tsc -p ./tsconfig.build.json",
|
||||
"prebuild": "yarn build:tsc",
|
||||
"build": "yarn build:rollup",
|
||||
"postbuild": "yarn doc && yarn cem",
|
||||
"dev": "tsc -p ./tsconfig.build.json -w",
|
||||
"cem": "yarn cem-analyze && yarn cem-markdown",
|
||||
"cem-analyze": "cem analyze",
|
||||
"cem-markdown": "node CEMToMarkdown.mjs",
|
||||
"tdd": "yarn dev & yarn test-chrome:watch",
|
||||
"tdd:firefox": "yarn dev & yarn test-firefox:watch",
|
||||
"prepare": "yarn clean:dist && yarn build",
|
||||
"prettier": "prettier --config ../../../.prettierrc --write \"**/*.ts\"",
|
||||
"prettier:diff": "prettier --config ../../../.prettierrc \"**/*.ts\" --list-different",
|
||||
"eslint": "eslint . --ext .ts",
|
||||
"eslint:fix": "eslint . --ext .ts --fix",
|
||||
"pretest": "yarn eslint && yarn storybook build --test",
|
||||
"test": "playwright test",
|
||||
"posttest:ci": "yarn doc:ci",
|
||||
"start": "storybook dev -p 6006"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@custom-elements-manifest/analyzer": "^0.9.3",
|
||||
"@custom-elements-manifest/to-markdown": "^0.1.0",
|
||||
"@microsoft/api-extractor": "7.24.2",
|
||||
"@microsoft/tsdoc-config": "^0.13.4",
|
||||
"@playwright/test": "^1.41.2",
|
||||
"@rollup/plugin-node-resolve": "^13.3.0",
|
||||
"@storybook/addon-essentials": "^8.0.0",
|
||||
"@storybook/addon-links": "^8.0.0",
|
||||
"@storybook/csf": "^0.1.3",
|
||||
"@storybook/html-vite": "^8.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"eslint-plugin-storybook": "^0.8.0",
|
||||
"express": "^4.18.1",
|
||||
"expect": "29.2.1",
|
||||
"prettier": "2.8.8",
|
||||
"qs": "^6.11.0",
|
||||
"rollup-plugin-filesize": "^9.1.2",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-transform-tagged-template": "^0.0.3",
|
||||
"rollup": "^2.71.1",
|
||||
"storybook": "^8.0.0",
|
||||
"typescript": "^4.7.0",
|
||||
"vite": "^5.1.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.0.3",
|
||||
"@microsoft/fast-element": "2.0.0-beta.26",
|
||||
"@microsoft/fast-web-utilities": "^6.0.0",
|
||||
"tabbable": "^5.2.0",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"customElements": "dist/custom-elements.json"
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
import type { PlaywrightTestConfig } from "@playwright/test";
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
reporter: "list",
|
||||
testMatch: /.*\.pw\.spec\.ts$/,
|
||||
retries: 3,
|
||||
fullyParallel: process.env.CI ? false : true,
|
||||
timeout: process.env.CI ? 10000 : 30000,
|
||||
use: {
|
||||
baseURL: "http://localhost:6006/iframe.html",
|
||||
deviceScaleFactor: 1,
|
||||
launchOptions: { args: ["--force-device-scale-factor=1"] },
|
||||
viewport: {
|
||||
height: 1280,
|
||||
width: 720,
|
||||
},
|
||||
},
|
||||
webServer: {
|
||||
// double-quotes are required for Windows
|
||||
command: "yarn start",
|
||||
port: 6006,
|
||||
reuseExistingServer: process.env.CI ? false : true,
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
|
@ -1,63 +0,0 @@
|
|||
import filesize from "rollup-plugin-filesize";
|
||||
import { nodeResolve } from "@rollup/plugin-node-resolve";
|
||||
import { terser } from "rollup-plugin-terser";
|
||||
import transformTaggedTemplate from "rollup-plugin-transform-tagged-template";
|
||||
import {
|
||||
transformCSSFragment,
|
||||
transformHTMLFragment,
|
||||
} from "../../../build/transform-fragments.js";
|
||||
|
||||
const parserOptions = {
|
||||
sourceType: "module",
|
||||
};
|
||||
|
||||
const plugins = [
|
||||
nodeResolve(),
|
||||
transformTaggedTemplate({
|
||||
tagsToProcess: ["css"],
|
||||
transformer: transformCSSFragment,
|
||||
parserOptions,
|
||||
}),
|
||||
transformTaggedTemplate({
|
||||
tagsToProcess: ["child", "html", "item"],
|
||||
transformer: transformHTMLFragment,
|
||||
parserOptions,
|
||||
}),
|
||||
filesize({
|
||||
showMinifiedSize: false,
|
||||
showBrotliSize: true,
|
||||
}),
|
||||
];
|
||||
|
||||
export default [
|
||||
{
|
||||
input: "dist/esm/index.rollup.js",
|
||||
output: [
|
||||
{
|
||||
file: "dist/fast-foundation.js",
|
||||
format: "esm",
|
||||
},
|
||||
{
|
||||
file: "dist/fast-foundation.min.js",
|
||||
format: "esm",
|
||||
plugins: [terser()],
|
||||
},
|
||||
],
|
||||
plugins,
|
||||
},
|
||||
{
|
||||
input: "dist/esm/index.rollup.debug.js",
|
||||
output: [
|
||||
{
|
||||
file: "dist/fast-foundation.debug.js",
|
||||
format: "esm",
|
||||
},
|
||||
{
|
||||
file: "dist/fast-foundation.debug.min.js",
|
||||
format: "esm",
|
||||
plugins: [terser()],
|
||||
},
|
||||
],
|
||||
plugins,
|
||||
},
|
||||
];
|
|
@ -1 +0,0 @@
|
|||
/// <reference types="vite/client" />
|
|
@ -1,10 +0,0 @@
|
|||
declare global {
|
||||
namespace PlaywrightTest {
|
||||
interface Matchers<R> {
|
||||
hasAttribute(a: string): Promise<R>;
|
||||
toHaveBooleanAttribute(a: string): Promise<R>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
|
@ -1,100 +0,0 @@
|
|||
import type { FASTElement, ViewTemplate } from "@microsoft/fast-element";
|
||||
import type {
|
||||
AnnotatedStoryFn,
|
||||
Args,
|
||||
ComponentAnnotations,
|
||||
StoryAnnotations,
|
||||
StoryContext,
|
||||
} from "@storybook/csf";
|
||||
import qs from "qs";
|
||||
|
||||
/**
|
||||
* Returns a formatted URL for a given Storybook fixture.
|
||||
*
|
||||
* @param id - the Storybook fixture ID
|
||||
* @param args - Story args
|
||||
* @returns - the local URL for the Storybook fixture iframe
|
||||
*/
|
||||
export function fixtureURL(
|
||||
id: string = "debug--blank",
|
||||
args?: Record<string, any>
|
||||
): string {
|
||||
const params: Record<string, any> = { id };
|
||||
if (args) {
|
||||
params.args = qs
|
||||
.stringify(args, {
|
||||
allowDots: true,
|
||||
delimiter: ";",
|
||||
format: "RFC1738",
|
||||
encode: false,
|
||||
})
|
||||
.replace(/=/g, ":")
|
||||
.replace(/\//g, "--");
|
||||
}
|
||||
|
||||
const url = qs.stringify(params, {
|
||||
addQueryPrefix: true,
|
||||
format: "RFC1738",
|
||||
encode: false,
|
||||
});
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper that returns a function to bind a Storybook story to a ViewTemplate.
|
||||
*
|
||||
* @param template - The ViewTemplate to render
|
||||
* @returns - a function to bind a Storybook story
|
||||
*/
|
||||
export function renderComponent<TArgs = Args>(
|
||||
template: ViewTemplate
|
||||
): (args: TArgs, context: StoryContext) => Element | DocumentFragment | null {
|
||||
return function (args, { updateArgs }) {
|
||||
const storyFragment = new DocumentFragment();
|
||||
template.render({ ...args, updateArgs }, storyFragment);
|
||||
if (storyFragment.childElementCount === 1) {
|
||||
return storyFragment.firstElementChild;
|
||||
}
|
||||
return storyFragment;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper that returns a function to bind a Storybook story to a ViewTemplate.
|
||||
*/
|
||||
export type FASTFramework = {
|
||||
canvasElement: HTMLElement;
|
||||
component: typeof FASTElement;
|
||||
storyResult: FASTElement | Element | DocumentFragment;
|
||||
};
|
||||
|
||||
/**
|
||||
* Metadata to configure the stories for a component.
|
||||
*/
|
||||
export type Meta<TArgs = Args> = ComponentAnnotations<
|
||||
FASTFramework,
|
||||
Omit<TArgs, keyof FASTElement>
|
||||
>;
|
||||
|
||||
/**
|
||||
* Story function that represents a CSFv3 component example.
|
||||
*/
|
||||
export declare type StoryObj<TArgs = Args> = StoryAnnotations<FASTFramework, TArgs>;
|
||||
|
||||
/**
|
||||
* Story function that represents a CSFv2 component example.
|
||||
*/
|
||||
export declare type StoryFn<TArgs = Args> = AnnotatedStoryFn<FASTFramework, TArgs>;
|
||||
|
||||
/**
|
||||
* Story function that represents a CSFv2 component example.
|
||||
*
|
||||
* NOTE that in Storybook 7.0, this type will be renamed to `StoryFn` and replaced by the current `StoryObj` type.
|
||||
*/
|
||||
export declare type Story<TArgs = Args> = StoryFn<StoryArgs<TArgs>>;
|
||||
|
||||
/**
|
||||
* Combined Storybook story args.
|
||||
*/
|
||||
export type StoryArgs<TArgs = Args> = Partial<Omit<TArgs, keyof FASTElement>> & Args;
|
|
@ -1,111 +0,0 @@
|
|||
import type { Locator, Page } from "@playwright/test";
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { fixtureURL } from "../__test__/helpers.js";
|
||||
import type { FASTAccordionItem } from "./accordion-item.js";
|
||||
|
||||
test.describe("Accordion item", () => {
|
||||
let page: Page;
|
||||
let element: Locator;
|
||||
let root: Locator;
|
||||
let heading: Locator;
|
||||
let button: Locator;
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage();
|
||||
|
||||
element = page.locator("fast-accordion-item");
|
||||
|
||||
root = page.locator("#storybook-root");
|
||||
|
||||
heading = page.locator(`[role="heading"]`);
|
||||
|
||||
button = element.locator("button");
|
||||
|
||||
await page.goto(fixtureURL("accordion-item--accordion-item"));
|
||||
|
||||
await element.waitFor({ state: "visible" });
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
test("should set a default heading level of 2 when `headinglevel` is not provided", async () => {
|
||||
await root.evaluate(node => {
|
||||
node.innerHTML = /* html */ `
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 1</span>
|
||||
<div>Content 1</div>
|
||||
</fast-accordion-item>
|
||||
`;
|
||||
});
|
||||
|
||||
await expect(element).not.toHaveAttribute("headinglevel");
|
||||
|
||||
await expect(element).toHaveJSProperty("headinglevel", 2);
|
||||
});
|
||||
|
||||
test("should set the `aria-level` attribute on the internal heading element equal to the heading level", async () => {
|
||||
await root.evaluate(node => {
|
||||
node.innerHTML = /* html */ `
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 1</span>
|
||||
<div>Content 1</div>
|
||||
</fast-accordion-item>
|
||||
`;
|
||||
});
|
||||
|
||||
await element.evaluate<void, FASTAccordionItem>(node => {
|
||||
node.headinglevel = 3;
|
||||
});
|
||||
|
||||
await expect(heading).toHaveAttribute("aria-level", "3");
|
||||
});
|
||||
|
||||
test("should set `aria-expanded` property on the internal control equal to the `expanded` property", async () => {
|
||||
await root.evaluate(node => {
|
||||
node.innerHTML = /* html */ `
|
||||
<fast-accordion-item expanded></fast-accordion-item>
|
||||
`;
|
||||
});
|
||||
|
||||
await expect(button).toHaveAttribute("aria-expanded", "true");
|
||||
|
||||
await element.evaluate<void, FASTAccordionItem>(node => {
|
||||
node.expanded = false;
|
||||
});
|
||||
|
||||
await expect(button).toHaveAttribute("aria-expanded", "false");
|
||||
});
|
||||
|
||||
test("should set `disabled` attribute on the internal control equal to the `disabled` property", async () => {
|
||||
await root.evaluate(node => {
|
||||
node.innerHTML = /* html */ `
|
||||
<fast-accordion-item disabled></fast-accordion-item>
|
||||
`;
|
||||
});
|
||||
|
||||
await expect(button).toHaveAttribute("disabled");
|
||||
|
||||
await element.evaluate<void, FASTAccordionItem>(node => {
|
||||
node.disabled = false;
|
||||
});
|
||||
|
||||
await expect(button).not.toHaveAttribute("disabled");
|
||||
});
|
||||
|
||||
test("should set internal properties to match the id when provided", async () => {
|
||||
await root.evaluate(node => {
|
||||
node.innerHTML = /* html */ `
|
||||
<fast-accordion-item id="foo"></fast-accordion-item>
|
||||
`;
|
||||
});
|
||||
|
||||
await expect(element.locator(`[role="region"]`)).toHaveAttribute(
|
||||
"aria-labelledby",
|
||||
"foo"
|
||||
);
|
||||
|
||||
await expect(button).toHaveId("foo");
|
||||
});
|
||||
});
|
|
@ -1,56 +0,0 @@
|
|||
import type { ElementViewTemplate } from "@microsoft/fast-element";
|
||||
import { html, ref } from "@microsoft/fast-element";
|
||||
import { endSlotTemplate, startSlotTemplate } from "../patterns/index.js";
|
||||
import { staticallyCompose } from "../utilities/template-helpers.js";
|
||||
import type { AccordionItemOptions, FASTAccordionItem } from "./accordion-item.js";
|
||||
|
||||
/**
|
||||
* The template for the {@link @microsoft/fast-foundation#(FASTAccordionItem:class)} component.
|
||||
* @public
|
||||
*/
|
||||
export function accordionItemTemplate<T extends FASTAccordionItem>(
|
||||
options: AccordionItemOptions = {}
|
||||
): ElementViewTemplate<T> {
|
||||
return html<T>`
|
||||
<div
|
||||
class="heading"
|
||||
part="heading"
|
||||
role="heading"
|
||||
aria-level="${x => x.headinglevel}"
|
||||
>
|
||||
<button
|
||||
class="button"
|
||||
part="button"
|
||||
${ref("expandbutton")}
|
||||
?disabled="${x => (x.disabled ? "true" : void 0)}"
|
||||
aria-expanded="${x => x.expanded}"
|
||||
aria-controls="${x => x.id}-panel"
|
||||
id="${x => x.id}"
|
||||
@click="${(x, c) => x.clickHandler(c.event as MouseEvent)}"
|
||||
>
|
||||
<span class="heading-content" part="heading-content">
|
||||
<slot name="heading"></slot>
|
||||
</span>
|
||||
</button>
|
||||
${
|
||||
/* The start slot is after the button for an improved screen reader experience */ ""
|
||||
}
|
||||
${startSlotTemplate(options)}
|
||||
${endSlotTemplate(options)}
|
||||
<span class="expand-collapse-icon" part="expand-collapse-icon" aria-hidden="true">
|
||||
<slot name="expand-collapse-icon">
|
||||
${staticallyCompose(options.expandCollapseIcon)}
|
||||
</slot>
|
||||
<span>
|
||||
</div>
|
||||
<div
|
||||
class="panel"
|
||||
part="panel"
|
||||
id="${x => x.id}-panel"
|
||||
role="region"
|
||||
aria-labelledby="${x => x.id}"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
`;
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
import { attr, FASTElement, nullableNumberConverter } from "@microsoft/fast-element";
|
||||
import { uniqueId } from "@microsoft/fast-web-utilities";
|
||||
import type { StaticallyComposableHTML } from "../utilities/template-helpers.js";
|
||||
import { StartEnd } from "../patterns/index.js";
|
||||
import type { StartEndOptions } from "../patterns/start-end.js";
|
||||
import { applyMixins } from "../utilities/apply-mixins.js";
|
||||
|
||||
/**
|
||||
* Accordion Item configuration options
|
||||
* @public
|
||||
*/
|
||||
export type AccordionItemOptions = StartEndOptions<FASTAccordionItem> & {
|
||||
expandCollapseIcon?: StaticallyComposableHTML<FASTAccordionItem>;
|
||||
};
|
||||
|
||||
/**
|
||||
* An individual item in an {@link @microsoft/fast-foundation#(FASTAccordion:class) }.
|
||||
*
|
||||
* @slot start - Content which can be provided between the heading and the icon
|
||||
* @slot end - Content which can be provided between the start slot and icon
|
||||
* @slot heading - Content which serves as the accordion item heading and text of the expand button
|
||||
* @slot - The default slot for accordion item content
|
||||
* @slot expand-collapse-icon - The expanded / collapsed icon
|
||||
* @fires change - Fires a custom 'change' event when the button is invoked
|
||||
* @csspart heading - Wraps the button
|
||||
* @csspart button - The button which serves to invoke the item
|
||||
* @csspart heading-content - Wraps the slot for the heading content within the button
|
||||
* @csspart expand-collapse-icon - The icon container
|
||||
* @csspart panel - The wrapper for the accordion item content
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class FASTAccordionItem extends FASTElement {
|
||||
/**
|
||||
* Configures the {@link https://www.w3.org/TR/wai-aria-1.1/#aria-level | level} of the
|
||||
* heading element.
|
||||
*
|
||||
* @defaultValue 2
|
||||
* @public
|
||||
* @remarks
|
||||
* HTML attribute: heading-level
|
||||
*/
|
||||
@attr({
|
||||
attribute: "heading-level",
|
||||
mode: "fromView",
|
||||
converter: nullableNumberConverter,
|
||||
})
|
||||
public headinglevel: 1 | 2 | 3 | 4 | 5 | 6 = 2;
|
||||
|
||||
/**
|
||||
* Expands or collapses the item.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
* HTML attribute: expanded
|
||||
*/
|
||||
@attr({ mode: "boolean" })
|
||||
public expanded: boolean = false;
|
||||
|
||||
/**
|
||||
* Disables an accordion item
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
* HTML attribute: disabled
|
||||
*/
|
||||
@attr({ mode: "boolean" })
|
||||
public disabled: boolean = false;
|
||||
|
||||
/**
|
||||
* The item ID
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
* HTML Attribute: id
|
||||
*/
|
||||
@attr
|
||||
public id: string = uniqueId("accordion-");
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public expandbutton: HTMLElement;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public clickHandler = (e: MouseEvent) => {
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$emit("click", e);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark internal because exporting class and interface of the same name
|
||||
* confuses API documenter.
|
||||
* TODO: https://github.com/microsoft/fast/issues/3317
|
||||
* @internal
|
||||
*/
|
||||
export interface FASTAccordionItem extends StartEnd {}
|
||||
applyMixins(FASTAccordionItem, StartEnd);
|
|
@ -1,3 +0,0 @@
|
|||
export { FASTAccordionItem } from "./accordion-item.js";
|
||||
export type { AccordionItemOptions } from "./accordion-item.js";
|
||||
export { accordionItemTemplate } from "./accordion-item.template.js";
|
|
@ -1,125 +0,0 @@
|
|||
import { css } from "@microsoft/fast-element";
|
||||
import chevronUpIcon from "../../../statics/svg/chevron_up_12_regular.svg";
|
||||
import { FASTAccordionItem } from "../accordion-item.js";
|
||||
import { accordionItemTemplate } from "../accordion-item.template.js";
|
||||
|
||||
const styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
font-family: var(--body-font);
|
||||
flex-direction: column;
|
||||
font-size: var(--type-ramp-base-font-size);
|
||||
line-height: var(--type-ramp-base-line-height);
|
||||
border-bottom: calc(var(--stroke-width) * 1px) solid
|
||||
var(--neutral-stroke-divider-rest);
|
||||
}
|
||||
|
||||
:host([disabled]) {
|
||||
opacity: var(--disabled-opacity);
|
||||
}
|
||||
|
||||
.panel {
|
||||
display: none;
|
||||
padding: calc((6 + (var(--design-unit) * 2 * var(--density))) * 1px);
|
||||
}
|
||||
|
||||
.heading {
|
||||
display: grid;
|
||||
position: relative;
|
||||
grid-template-columns: auto 1fr auto calc(
|
||||
(var(--base-height-multiplier) + var(--density)) * var(--design-unit) *
|
||||
1px
|
||||
);
|
||||
}
|
||||
|
||||
.button {
|
||||
appearance: none;
|
||||
border: none;
|
||||
background: none;
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
outline: none;
|
||||
padding: 0 calc((6 + (var(--design-unit) * 2 * var(--density))) * 1px);
|
||||
text-align: left;
|
||||
height: calc(
|
||||
(var(--base-height-multiplier) + var(--density)) * var(--design-unit) * 1px
|
||||
);
|
||||
color: var(--neutral-foreground-rest);
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
color: var(--neutral-foreground-rest);
|
||||
}
|
||||
|
||||
.button:active {
|
||||
color: var(--neutral-foreground-rest);
|
||||
}
|
||||
|
||||
.button::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button:focus-visible::before {
|
||||
outline: none;
|
||||
border: calc(var(--focus-stroke-width) * 1px) solid var(--focus-stroke-outer);
|
||||
border-radius: calc(var(--control-corner-radius) * 1px);
|
||||
}
|
||||
|
||||
:host([expanded]) .panel {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.expand-collapse-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
grid-column: 4;
|
||||
pointer-events: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
slot[name="expand-collapse-icon"] * {
|
||||
transition: transform 0.1s linear;
|
||||
transform: rotate(0deg);
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
:host([expanded]) slot[name="expand-collapse-icon"] * {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
::slotted([slot="start"]) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-inline-end: calc(var(--design-unit) * 1px);
|
||||
justify-content: center;
|
||||
grid-column: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
::slotted([slot="end"]) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-inline-start: calc(var(--design-unit) * 1px);
|
||||
justify-content: center;
|
||||
grid-column: 3;
|
||||
position: relative;
|
||||
}
|
||||
`;
|
||||
|
||||
FASTAccordionItem.define({
|
||||
name: "fast-accordion-item",
|
||||
template: accordionItemTemplate({
|
||||
expandCollapseIcon: chevronUpIcon,
|
||||
}),
|
||||
styles,
|
||||
});
|
|
@ -1,49 +0,0 @@
|
|||
import { html } from "@microsoft/fast-element";
|
||||
import type { Meta, Story, StoryArgs } from "../../__test__/helpers.js";
|
||||
import { renderComponent } from "../../__test__/helpers.js";
|
||||
import type { FASTAccordionItem } from "../accordion-item.js";
|
||||
|
||||
const storyTemplate = html<StoryArgs<FASTAccordionItem>>`
|
||||
<fast-accordion-item
|
||||
?expanded="${x => x.expanded}"
|
||||
heading-level="${x => x.headinglevel}"
|
||||
id="${x => x.id}"
|
||||
>
|
||||
${x => x.storyContent}
|
||||
</fast-accordion-item>
|
||||
`;
|
||||
|
||||
export default {
|
||||
title: "Accordion Item",
|
||||
args: {
|
||||
expanded: false,
|
||||
},
|
||||
argTypes: {
|
||||
expanded: { control: "boolean" },
|
||||
headinglevel: { control: { type: "number", max: 6, min: 1 } },
|
||||
id: { control: "text" },
|
||||
storyContent: { table: { disable: true } },
|
||||
},
|
||||
} as Meta<FASTAccordionItem>;
|
||||
|
||||
export const AccordionItem: Story<FASTAccordionItem> = renderComponent(
|
||||
storyTemplate
|
||||
).bind({});
|
||||
AccordionItem.args = {
|
||||
storyContent: html`
|
||||
<span slot="heading">Accordion Item Heading</span>
|
||||
Accordion Item Content
|
||||
`,
|
||||
};
|
||||
|
||||
export const AccordionItemWithSlottedStartEnd: Story<FASTAccordionItem> = renderComponent(
|
||||
storyTemplate
|
||||
).bind({});
|
||||
AccordionItemWithSlottedStartEnd.args = {
|
||||
storyContent: html`
|
||||
<fast-badge slot="start">start</fast-badge>
|
||||
<span slot="heading">Accordion Item Heading</span>
|
||||
<fast-badge slot="end">end</fast-badge>
|
||||
Accordion Item Content
|
||||
`,
|
||||
};
|
|
@ -1,219 +0,0 @@
|
|||
---
|
||||
id: accordion
|
||||
title: fast-accordion
|
||||
sidebar_label: accordion
|
||||
custom_edit_url: https://github.com/microsoft/fast/edit/master/packages/web-components/fast-foundation/src/accordion/README.md
|
||||
description: fast-accordion is a web component implementation of an accordion.
|
||||
---
|
||||
|
||||
As defined by the [W3C](https://w3c.github.io/aria-practices/#accordion):
|
||||
|
||||
> An accordion is a vertically stacked set of interactive headings that each contain a title, content snippet, or thumbnail representing a section of content. The headings function as controls that enable users to reveal or hide their associated sections of content. Accordions are commonly used to reduce the need to scroll when presenting multiple sections of content on a single page.
|
||||
|
||||
## Setup
|
||||
|
||||
### Basic Setup
|
||||
|
||||
```ts
|
||||
import {
|
||||
provideFASTDesignSystem,
|
||||
fastAccordion,
|
||||
fastAccordionItem
|
||||
} from "@microsoft/fast-components";
|
||||
|
||||
provideFASTDesignSystem()
|
||||
.register(
|
||||
fastAccordion(),
|
||||
fastAccordionItem()
|
||||
);
|
||||
```
|
||||
|
||||
### Customizing Icons
|
||||
|
||||
```ts
|
||||
import {
|
||||
provideFASTDesignSystem,
|
||||
fastAccordion,
|
||||
fastAccordionItem
|
||||
} from "@microsoft/fast-components";
|
||||
|
||||
provideFASTDesignSystem()
|
||||
.register(
|
||||
fastAccordion(),
|
||||
fastAccordionItem({
|
||||
collapsedIcon: `...your collapsed icon...`,
|
||||
expandedIcon: `...your expanded icon...`,
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```html live
|
||||
<fast-accordion>
|
||||
<fast-accordion-item expanded>
|
||||
<span slot="heading">Panel one</span>
|
||||
Panel one content
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Panel two</span>
|
||||
Panel two content
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item expanded>
|
||||
<span slot="heading">Panel three</span>
|
||||
Panel three content
|
||||
</fast-accordion-item>
|
||||
</fast-accordion>
|
||||
```
|
||||
|
||||
## Create your own design
|
||||
|
||||
### Accordion
|
||||
|
||||
```ts
|
||||
import { Accordion, accordionTemplate as template } from "@microsoft/fast-foundation";
|
||||
import { accordionStyles as styles } from "./my-accordion.styles";
|
||||
|
||||
export const myAccordion = Accordion.compose({
|
||||
baseName: "accordion",
|
||||
template,
|
||||
styles,
|
||||
});
|
||||
```
|
||||
|
||||
### AccordionItem
|
||||
|
||||
```ts
|
||||
import {
|
||||
AccordionItem,
|
||||
AccordionItemOptions,
|
||||
accordionItemTemplate as template,
|
||||
} from "@microsoft/fast-foundation";
|
||||
import { accordionItemStyles as styles } from "./my-accordion-item.styles";
|
||||
|
||||
export const myAccordionItem = AccordionItem.compose<AccordionItemOptions>({
|
||||
baseName: "accordion-item",
|
||||
template,
|
||||
styles,
|
||||
collapsedIcon: `...default collapsed icon...`,
|
||||
expandedIcon: `...default expanded icon...`,
|
||||
});
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
|
||||
|
||||
### Variables
|
||||
|
||||
| Name | Description | Type |
|
||||
| --------------------- | ----------------------------- | --------------------------------------- |
|
||||
| `AccordionExpandMode` | Expand mode for FASTAccordion | `{ single: "single", multi: "multi", }` |
|
||||
|
||||
<hr/>
|
||||
|
||||
|
||||
|
||||
### class: `FASTAccordion`
|
||||
|
||||
#### Superclass
|
||||
|
||||
| Name | Module | Package |
|
||||
| ------------- | ------ | ----------------------- |
|
||||
| `FASTElement` | | @microsoft/fast-element |
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Privacy | Type | Default | Description | Inherited From |
|
||||
| ---------------- | --------- | --------------------- | ------- | --------------------------------------------------------------------------------------------- | -------------- |
|
||||
| `expandmode` | public | `AccordionExpandMode` | | Controls the expand mode of the Accordion, either allowing single or multiple item expansion. | |
|
||||
| `accordionItems` | protected | `Element[]` | | | |
|
||||
|
||||
#### Methods
|
||||
|
||||
| Name | Privacy | Description | Parameters | Return | Inherited From |
|
||||
| ------------------- | ------- | ----------- | ------------------------------------------------------ | ------ | -------------- |
|
||||
| `expandmodeChanged` | public | | `prev: AccordionExpandMode, next: AccordionExpandMode` | | |
|
||||
|
||||
#### Events
|
||||
|
||||
| Name | Type | Description | Inherited From |
|
||||
| -------- | ---- | ---------------------------------------------------------- | -------------- |
|
||||
| `change` | | Fires a custom 'change' event when the active item changes | |
|
||||
|
||||
#### Attributes
|
||||
|
||||
| Name | Field | Inherited From |
|
||||
| ------------- | ---------- | -------------- |
|
||||
| `expand-mode` | expandmode | |
|
||||
|
||||
#### Slots
|
||||
|
||||
| Name | Description |
|
||||
| ---- | -------------------------------- |
|
||||
| | The slot for the accordion items |
|
||||
|
||||
<hr/>
|
||||
|
||||
|
||||
|
||||
### class: `FASTAccordionItem`
|
||||
|
||||
#### Superclass
|
||||
|
||||
| Name | Module | Package |
|
||||
| ------------- | ------ | ----------------------- |
|
||||
| `FASTElement` | | @microsoft/fast-element |
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Privacy | Type | Default | Description | Inherited From |
|
||||
| -------------- | ------- | ---------------------------- | ------- | -------------------------------------------------------------------------------------------------- | -------------- |
|
||||
| `headinglevel` | public | `1 or 2 or 3 or 4 or 5 or 6` | `2` | Configures the [level](https://www.w3.org/TR/wai-aria-1.1/#aria-level) of the heading element. | |
|
||||
| `expanded` | public | `boolean` | `false` | Expands or collapses the item. | |
|
||||
| `disabled` | public | `boolean` | `false` | Disables an accordion item | |
|
||||
| `id` | public | `string` | | The item ID | |
|
||||
|
||||
#### Events
|
||||
|
||||
| Name | Type | Description | Inherited From |
|
||||
| -------- | ---- | -------------------------------------------------------- | -------------- |
|
||||
| `change` | | Fires a custom 'change' event when the button is invoked | |
|
||||
|
||||
#### Attributes
|
||||
|
||||
| Name | Field | Inherited From |
|
||||
| --------------- | ------------ | -------------- |
|
||||
| `heading-level` | headinglevel | |
|
||||
| | expanded | |
|
||||
| | disabled | |
|
||||
| `id` | id | |
|
||||
|
||||
#### CSS Parts
|
||||
|
||||
| Name | Description |
|
||||
| ---------------------- | -------------------------------------------------------- |
|
||||
| `heading` | Wraps the button |
|
||||
| `button` | The button which serves to invoke the item |
|
||||
| `heading-content` | Wraps the slot for the heading content within the button |
|
||||
| `expand-collapse-icon` | The icon container |
|
||||
| `panel` | The wrapper for the accordion item content |
|
||||
|
||||
#### Slots
|
||||
|
||||
| Name | Description |
|
||||
| ---------------------- | -------------------------------------------------------------------------------- |
|
||||
| `start` | Content which can be provided between the heading and the icon |
|
||||
| `end` | Content which can be provided between the start slot and icon |
|
||||
| `heading` | Content which serves as the accordion item heading and text of the expand button |
|
||||
| | The default slot for accordion item content |
|
||||
| `expand-collapse-icon` | The expanded / collapsed icon |
|
||||
|
||||
<hr/>
|
||||
|
||||
|
||||
## Additional resources
|
||||
|
||||
* [Component explorer examples](https://explore.fast.design/components/fast-accordion)
|
||||
* [Component technical specification](https://github.com/microsoft/fast/blob/master/packages/web-components/fast-foundation/src/accordion/accordion.spec.md)
|
||||
* [W3C Component Aria Practices](https://w3c.github.io/aria-practices/#accordion)
|
|
@ -1,23 +0,0 @@
|
|||
import type { ValuesOf } from "../utilities/index.js";
|
||||
|
||||
/**
|
||||
* Expand mode for {@link FASTAccordion}
|
||||
* @public
|
||||
*/
|
||||
export const AccordionExpandMode = {
|
||||
/**
|
||||
* Designates only a single {@link @microsoft/fast-foundation#(FASTAccordionItem:class) } can be open a time.
|
||||
*/
|
||||
single: "single",
|
||||
|
||||
/**
|
||||
* Designates multiple {@link @microsoft/fast-foundation#(FASTAccordionItem:class) | FASTAccordionItemItems} can be open simultaneously.
|
||||
*/
|
||||
multi: "multi",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Type for the {@link FASTAccordion} Expand Mode
|
||||
* @public
|
||||
*/
|
||||
export type AccordionExpandMode = ValuesOf<typeof AccordionExpandMode>;
|
|
@ -1,387 +0,0 @@
|
|||
import type { Locator, Page } from "@playwright/test";
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { fixtureURL } from "../__test__/helpers.js";
|
||||
import { AccordionExpandMode } from "./accordion.options.js";
|
||||
|
||||
test.describe("Accordion", () => {
|
||||
let page: Page;
|
||||
let element: Locator;
|
||||
let root: Locator;
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage();
|
||||
|
||||
element = page.locator("fast-accordion");
|
||||
|
||||
root = page.locator("#storybook-root");
|
||||
|
||||
await page.goto(fixtureURL("accordion--accordion"));
|
||||
|
||||
await element.waitFor({ state: "visible" });
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
test("should set an expand mode of `multi` when passed to the `expand-mode` attribute", async () => {
|
||||
await root.evaluate(node => {
|
||||
node.innerHTML = /* html */ `
|
||||
<fast-accordion expand-mode="multi">
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 1</span>
|
||||
<div>Content 1</div>
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 2</span>
|
||||
<div>Content 2</div>
|
||||
</fast-accordion-item>
|
||||
</fast-accordion>
|
||||
`;
|
||||
});
|
||||
|
||||
await expect(element).toHaveAttribute("expand-mode", AccordionExpandMode.multi);
|
||||
});
|
||||
|
||||
test("should set an expand mode of `single` when passed to the `expand-mode` attribute", async () => {
|
||||
await root.evaluate(node => {
|
||||
node.innerHTML = /* html */ `
|
||||
<fast-accordion expand-mode="single">
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 1</span>
|
||||
<div>Content 1</div>
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 2</span>
|
||||
<div>Content 2</div>
|
||||
</fast-accordion-item>
|
||||
</fast-accordion>
|
||||
`;
|
||||
});
|
||||
|
||||
await expect(element).toHaveAttribute("expand-mode", AccordionExpandMode.single);
|
||||
});
|
||||
|
||||
test("should set a default expand mode of `multi` when `expand-mode` attribute is not passed", async () => {
|
||||
await root.evaluate(node => {
|
||||
node.innerHTML = /* html */ `
|
||||
<fast-accordion>
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 1</span>
|
||||
<div>Content 1</div>
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 2</span>
|
||||
<div>Content 2</div>
|
||||
</fast-accordion-item>
|
||||
</fast-accordion>
|
||||
`;
|
||||
});
|
||||
|
||||
await expect(element).toHaveJSProperty("expandmode", AccordionExpandMode.multi);
|
||||
|
||||
await expect(element).toHaveAttribute("expand-mode", AccordionExpandMode.multi);
|
||||
});
|
||||
|
||||
test("should expand/collapse items when clicked in multi mode", async () => {
|
||||
await root.evaluate(node => {
|
||||
node.innerHTML = /* html */ `
|
||||
<fast-accordion expand-mode="multi">
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 1</span>
|
||||
<div>Content 1</div>
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 2</span>
|
||||
<div>Content 2</div>
|
||||
</fast-accordion-item>
|
||||
</fast-accordion>
|
||||
`;
|
||||
});
|
||||
|
||||
const items = element.locator("fast-accordion-item");
|
||||
|
||||
await items.nth(0).click();
|
||||
|
||||
await items.nth(1).click();
|
||||
|
||||
await expect(items.nth(0)).toHaveAttribute("expanded", "");
|
||||
|
||||
await expect(items.nth(1)).toHaveAttribute("expanded", "");
|
||||
});
|
||||
|
||||
test("should only have one expanded item in single mode", async () => {
|
||||
await root.evaluate(node => {
|
||||
node.innerHTML = /* html */ `
|
||||
<fast-accordion expand-mode="single">
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 1</span>
|
||||
<div>Content 1</div>
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 2</span>
|
||||
<div>Content 2</div>
|
||||
</fast-accordion-item>
|
||||
</fast-accordion>
|
||||
`;
|
||||
});
|
||||
|
||||
const items = element.locator("fast-accordion-item");
|
||||
|
||||
const firstItem = items.nth(0);
|
||||
|
||||
const secondItem = items.nth(1);
|
||||
|
||||
await firstItem.click();
|
||||
|
||||
await expect(firstItem).toHaveAttribute("expanded");
|
||||
|
||||
await expect(secondItem).not.toHaveAttribute("expanded");
|
||||
|
||||
const secondItemButton = secondItem.locator(`[part="button"]`);
|
||||
|
||||
await secondItemButton.click();
|
||||
|
||||
await secondItemButton.evaluate(node => {
|
||||
node.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
});
|
||||
|
||||
await expect(firstItem).not.toHaveAttribute("expanded");
|
||||
|
||||
await expect(secondItem).toHaveAttribute("expanded");
|
||||
});
|
||||
|
||||
test("should set the expanded items' button to aria-disabled when in single expand mode", async () => {
|
||||
await root.evaluate(node => {
|
||||
node.innerHTML = /* html */ `
|
||||
<fast-accordion expand-mode="single">
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 1</span>
|
||||
<div>Content 1</div>
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 2</span>
|
||||
<div>Content 2</div>
|
||||
</fast-accordion-item>
|
||||
</fast-accordion>
|
||||
`;
|
||||
});
|
||||
|
||||
const items = element.locator("fast-accordion-item");
|
||||
|
||||
const firstItem = items.nth(0);
|
||||
|
||||
const secondItem = items.nth(1);
|
||||
|
||||
await firstItem.click();
|
||||
|
||||
await expect(firstItem).toHaveAttribute("expanded");
|
||||
|
||||
await expect(firstItem.locator("button")).toHaveAttribute(
|
||||
"aria-disabled",
|
||||
"true"
|
||||
);
|
||||
|
||||
await secondItem.click();
|
||||
|
||||
await expect(firstItem).not.toHaveAttribute("expanded");
|
||||
|
||||
await expect(firstItem.locator("button")).not.toHaveAttribute(
|
||||
"aria-disabled",
|
||||
"true"
|
||||
);
|
||||
await expect(firstItem.locator("button")).not.toHaveAttribute(
|
||||
"aria-disabled",
|
||||
"false"
|
||||
);
|
||||
|
||||
await expect(secondItem).toHaveAttribute("expanded");
|
||||
|
||||
await expect(secondItem.locator("button")).toHaveAttribute(
|
||||
"aria-disabled",
|
||||
"true"
|
||||
);
|
||||
});
|
||||
|
||||
/* eslint-disable-next-line max-len */
|
||||
test("should remove an expanded items' expandbutton aria-disabled attribute when expand mode changes from single to multi", async () => {
|
||||
await root.evaluate(node => {
|
||||
node.innerHTML = /* html */ `
|
||||
<fast-accordion expand-mode="single">
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 1</span>
|
||||
<div>Content 1</div>
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 2</span>
|
||||
<div>Content 2</div>
|
||||
</fast-accordion-item>
|
||||
</fast-accordion>
|
||||
`;
|
||||
});
|
||||
|
||||
const items = element.locator("fast-accordion-item");
|
||||
|
||||
const firstItem = items.nth(0);
|
||||
|
||||
await firstItem.click();
|
||||
|
||||
await expect(firstItem).toHaveAttribute("expanded");
|
||||
|
||||
await expect(firstItem.locator("button")).toHaveAttribute(
|
||||
"aria-disabled",
|
||||
"true"
|
||||
);
|
||||
|
||||
await element.evaluate(node => {
|
||||
node.setAttribute("expand-mode", "multi");
|
||||
});
|
||||
|
||||
await expect(firstItem.locator("button")).not.toHaveAttribute("aria-disabled");
|
||||
});
|
||||
|
||||
test("should set the first item as expanded if no child is expanded by default in single mode", async () => {
|
||||
await root.evaluate(node => {
|
||||
node.innerHTML = /* html */ `
|
||||
<fast-accordion expand-mode="single">
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 1</span>
|
||||
<div>Content 1</div>
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 2</span>
|
||||
<div>Content 2</div>
|
||||
</fast-accordion-item>
|
||||
</fast-accordion>
|
||||
`;
|
||||
});
|
||||
|
||||
const items = element.locator("fast-accordion-item");
|
||||
|
||||
const firstItem = items.nth(0);
|
||||
|
||||
const secondItem = items.nth(1);
|
||||
|
||||
await expect(firstItem).toHaveAttribute("expanded");
|
||||
|
||||
await expect(secondItem).not.toHaveAttribute("expanded");
|
||||
|
||||
await secondItem.evaluate<void>(node => node.setAttribute("expanded", ""));
|
||||
|
||||
await expect(firstItem).not.toHaveAttribute("expanded");
|
||||
|
||||
await expect(secondItem).toHaveAttribute("expanded");
|
||||
});
|
||||
|
||||
test("should set the first item with an expanded attribute to expanded in single mode", async () => {
|
||||
await root.evaluate(node => {
|
||||
node.innerHTML = /* html */ `
|
||||
<fast-accordion expand-mode="single">
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 1</span>
|
||||
<div>Content 1</div>
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item expanded>
|
||||
<span slot="heading">Heading 2</span>
|
||||
<div>Content 2</div>
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item expanded>
|
||||
<span slot="heading">Heading 3</span>
|
||||
<div>Content 2</div>
|
||||
</fast-accordion-item>
|
||||
</fast-accordion>
|
||||
`;
|
||||
});
|
||||
|
||||
const items = element.locator("fast-accordion-item");
|
||||
|
||||
const firstItem = items.nth(0);
|
||||
|
||||
const secondItem = items.nth(1);
|
||||
|
||||
const thirdItem = items.nth(2);
|
||||
|
||||
await expect(firstItem).not.toHaveAttribute("expanded");
|
||||
|
||||
await expect(secondItem).toHaveAttribute("expanded");
|
||||
|
||||
await expect(thirdItem).not.toHaveAttribute("expanded");
|
||||
});
|
||||
|
||||
test("should allow disabled items to be expanded when in single mode", async () => {
|
||||
test.slow();
|
||||
await root.evaluate(node => {
|
||||
node.innerHTML = /* html */ `
|
||||
<fast-accordion expand-mode="single">
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Heading 1</span>
|
||||
<div>Content 1</div>
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item expanded disabled>
|
||||
<span slot="heading">Heading 2</span>
|
||||
<div>Content 2</div>
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item expanded>
|
||||
<span slot="heading">Heading 3</span>
|
||||
<div>Content 2</div>
|
||||
</fast-accordion-item>
|
||||
</fast-accordion>
|
||||
`;
|
||||
});
|
||||
|
||||
const items = element.locator("fast-accordion-item");
|
||||
|
||||
const firstItem = items.nth(0);
|
||||
|
||||
const secondItem = items.nth(1);
|
||||
|
||||
const thirdItem = items.nth(2);
|
||||
|
||||
await expect(firstItem).not.toHaveAttribute("expanded");
|
||||
|
||||
await expect(secondItem).toHaveAttribute("expanded");
|
||||
|
||||
await expect(thirdItem).toHaveAttribute("expanded");
|
||||
|
||||
await secondItem.evaluate(node => {
|
||||
node.removeAttribute("disabled");
|
||||
});
|
||||
|
||||
await expect(firstItem).not.toHaveAttribute("expanded");
|
||||
|
||||
await expect(secondItem).toHaveAttribute("expanded");
|
||||
|
||||
await expect(thirdItem).not.toHaveAttribute("expanded");
|
||||
});
|
||||
|
||||
test("should ignore `change` events from components other than accordion items", async () => {
|
||||
await root.evaluate(node => {
|
||||
node.innerHTML = /* html */ `
|
||||
<fast-accordion expand-mode="single">
|
||||
<fast-accordion-item>
|
||||
<div slot="heading">Accordion Item 1 Heading</div>
|
||||
Accordion Item 1 Content
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item>
|
||||
<div slot="heading">Accordion Item 2 Heading</div>
|
||||
<fast-checkbox>A checkbox as content</fast-checkbox>
|
||||
</fast-accordion-item>
|
||||
</fast-accordion>
|
||||
`;
|
||||
});
|
||||
|
||||
const item = element.locator("fast-accordion-item").nth(1);
|
||||
|
||||
const button = item.locator(`button[part="button"]`);
|
||||
|
||||
await button.click();
|
||||
|
||||
await expect(item).toHaveAttribute("expanded", "");
|
||||
|
||||
const checkbox = item.locator("fast-checkbox");
|
||||
|
||||
await checkbox.click();
|
||||
|
||||
await expect(item).toHaveAttribute("expanded", "");
|
||||
});
|
||||
});
|
|
@ -1,203 +0,0 @@
|
|||
# Accordion
|
||||
|
||||
## Overview
|
||||
|
||||
As defined by the W3C:
|
||||
> An accordion is a vertically stacked set of interactive headings that each contain a title, content snippet, or thumbnail representing a section of content. The headings function as controls that enable users to reveal or hide their associated sections of content. Accordions are commonly used to reduce the need to scroll when presenting multiple sections of content on a single page.
|
||||
|
||||
### Use Cases
|
||||
|
||||
- Frank finds a spelling error in the FAST documentation. He submits a pull request (PR) to fix the error and goes to eat lunch while waiting for the maintainers to review his PR. Upon returning from lunch he notices a widget on the screen saying certain status checks failed. He clicks on the headline of the widget and it reveals additional information stating that he needs to run prettier to ensure proper formatting. He clicks the headline again and the additional information collapses. Frank runs prettier and his PR is completed successfully.
|
||||
|
||||
- On April 16th Randy wakes up in a cold sweat because he realized he made a mistake in filing his taxes with the IRS. He opens his laptop and goes to the IRS website and sees a link to "frequently asked questions". Randy sees a list of questions on the page with an arrow next to it indicating that answers are hidden. He sees a question titled, "What should I do if I made a mistake on my federal turn that I've already filed?" Randy clicks the question and sees a section detailing out the exact steps to take. With his anxiety eased, Randy goes back to bed and falls asleep.
|
||||
|
||||
### Features
|
||||
|
||||
1. Optionally allow only a single section to be expanded. This behavior is opt-in and not default behavior.
|
||||
|
||||
2. Support for interactive content within the header, such as a menu button, checkbox, etc.
|
||||
|
||||
3. Optional support for nested accordions. Ideally this behavior will "just work" and no special behaviors will need to be added to enable/support this. The expectation here should be that an accordion takes content and whatever content is inside the accordion panel respects its own interaction model.
|
||||
|
||||
### Prior Art/Examples
|
||||
|
||||
- [Ant Design](https://ant.design/components/collapse/)
|
||||
- [Carbon Design](https://www.carbondesignsystem.com/components/accordion/usage/)
|
||||
- [Lightning Design](https://www.lightningdesignsystem.com/components/accordion/)
|
||||
- [Fluent UI](https://fluentsite.z22.web.core.windows.net/components/accordion/accessibility)
|
||||
- [Primer](https://primer.style/components/Details)
|
||||
- [Bootstrap](https://getbootstrap.com/docs/4.3/components/collapse/)
|
||||
|
||||
---
|
||||
|
||||
### API
|
||||
|
||||
**Accordion**
|
||||
*Component Name*
|
||||
- `fast-accordion`
|
||||
|
||||
*Attributes:*
|
||||
- `expand-mode` - enum
|
||||
- single
|
||||
- multi - default
|
||||
|
||||
*Events*
|
||||
- `change: CustomEvent`
|
||||
- no custom data
|
||||
- bubbles
|
||||
|
||||
**Accordion Item**
|
||||
*Component names:*
|
||||
- `fast-accordion-item`
|
||||
|
||||
*Attributes:*
|
||||
- `expanded` - boolean
|
||||
- `id` - string
|
||||
- `heading-level` - enum
|
||||
- 1
|
||||
- 2
|
||||
- 3 - default
|
||||
- 4
|
||||
- 5
|
||||
- 6
|
||||
|
||||
*Parts:*
|
||||
- panel
|
||||
- button
|
||||
|
||||
*Slot Names*
|
||||
- default
|
||||
- heading
|
||||
- collapsed-icon
|
||||
- expanded-icon
|
||||
- start
|
||||
- end
|
||||
|
||||
### Anatomy and Appearance
|
||||
|
||||
```HTML
|
||||
<!-- shadow root -->
|
||||
<div>
|
||||
<div role="heading" aria-level="3">
|
||||
<slot name="start" part="start"></slot>
|
||||
<button
|
||||
class="button"
|
||||
part="button"
|
||||
aria-expanded="true"
|
||||
aria-controls="accordion1-panel"
|
||||
id="accordion1"
|
||||
>
|
||||
<slot name="heading" part="heading">Panel one</slot>
|
||||
</button>
|
||||
<slot name="end" part="end"></slot>
|
||||
<span class="icon" part="icon">
|
||||
<slot name="expanded-icon" part="expanded-icon"></slot>
|
||||
<slot name="collapsed-icon" part="collapsed-icon"></slot>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
id="accordion1-panel"
|
||||
role="region"
|
||||
aria-labelledby="accordion1"
|
||||
>
|
||||
Panel one content
|
||||
</div>
|
||||
<div role="heading" aria-level="3">
|
||||
<slot name="start" part="start"></slot>
|
||||
<button
|
||||
class="button"
|
||||
part="button"
|
||||
aria-expanded="false"
|
||||
aria-controls="accordion2-panel"
|
||||
id="accordion2"
|
||||
>
|
||||
<slot name="heading" part="heading">Panel two</slot>
|
||||
</button>
|
||||
<slot name="end" part="end"></slot>
|
||||
<span class="icon" part="icon">
|
||||
<slot name="expanded-icon" part="expanded-icon"></slot>
|
||||
<slot name="collapsed-icon" part="collapsed-icon"></slot>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
id="accordion2-panel"
|
||||
part="panel"
|
||||
role="region"
|
||||
aria-labelledby="accordion2"
|
||||
class="panel"
|
||||
>
|
||||
Panel two content
|
||||
</div>
|
||||
<div role="heading" aria-level="3">
|
||||
<slot name="start" part="start"></slot>
|
||||
<button
|
||||
class="button"
|
||||
part="button"
|
||||
aria-expanded="true"
|
||||
aria-controls="accordion3-panel"
|
||||
id="accordion3"
|
||||
>
|
||||
<slot name="heading" part="heading">Panel three</slot>
|
||||
</button>
|
||||
<slot name="end" part="end"></slot>
|
||||
<span class="icon" part="icon">
|
||||
<slot name="expanded-icon" part="expanded-icon"></slot>
|
||||
<slot name="collapsed-icon" part="collapsed-icon"></slot>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
id="accordion3-panel"
|
||||
part="panel"
|
||||
role="region"
|
||||
aria-labelledby="accordion3"
|
||||
class="panel"
|
||||
>
|
||||
Panel three content
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- end shadow root -->
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation
|
||||
|
||||
```HTML
|
||||
<fast-accordion>
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Panel one</span>
|
||||
<span slot="icon">^</span>
|
||||
Panel one content
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item expanded>
|
||||
<span slot="heading">Panel two</span>
|
||||
<span slot="icon">^</span>
|
||||
Panel two content
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Panel three</span>
|
||||
<span slot="icon">^</span>
|
||||
Panel three content
|
||||
</fast-accordion-item>
|
||||
</fast-accordion>
|
||||
```
|
||||
|
||||
### States
|
||||
|
||||
Accordion has 2 modes of expansion let can be set via `expand-mode`. `expand-mode` defaults to `multi`, meaning multiple accordion-items can be expanded at any given time. If `expand-mode` is set to `single` then only one item can be expanded, meaning that when you activate a header all other headers with collapse.
|
||||
|
||||
|
||||
### Accessibility
|
||||
|
||||
Keyboard interactions for tabbing and up and down arrow should focus the next or previous header. Space bar should toggle the expansion of the focused header.
|
||||
|
||||
### Globalization
|
||||
|
||||
Component should mirror in RTL presentations.
|
||||
|
||||
### Test Plan
|
||||
|
||||
While testing is still TBD for our web components, we would expect this to align with the testing strategy and not require any additional test support.
|
||||
|
||||
---
|
|
@ -1,15 +0,0 @@
|
|||
import type { ElementViewTemplate } from "@microsoft/fast-element";
|
||||
import { elements, html, slotted } from "@microsoft/fast-element";
|
||||
import type { FASTAccordion } from "./accordion.js";
|
||||
|
||||
/**
|
||||
* Creates a template for the {@link @microsoft/fast-foundation#FASTAccordion} component.
|
||||
* @public
|
||||
*/
|
||||
export function accordionTemplate<T extends FASTAccordion>(): ElementViewTemplate<T> {
|
||||
return html<T>`
|
||||
<slot
|
||||
${slotted({ property: "slottedAccordionItems", filter: elements() })}
|
||||
></slot>
|
||||
`;
|
||||
}
|
|
@ -1,276 +0,0 @@
|
|||
import { Observable } from "@microsoft/fast-element";
|
||||
import { attr, FASTElement, observable } from "@microsoft/fast-element";
|
||||
import {
|
||||
keyArrowDown,
|
||||
keyArrowUp,
|
||||
keyEnd,
|
||||
keyHome,
|
||||
wrapInBounds,
|
||||
} from "@microsoft/fast-web-utilities";
|
||||
import { FASTAccordionItem } from "../accordion-item/accordion-item.js";
|
||||
import { AccordionExpandMode } from "./accordion.options.js";
|
||||
|
||||
/**
|
||||
* An Accordion Custom HTML Element
|
||||
* Implements {@link https://www.w3.org/TR/wai-aria-practices-1.1/#accordion | ARIA Accordion}.
|
||||
*
|
||||
* @slot - The slot for the accordion items
|
||||
* @fires change - Fires a custom 'change' event when the active item changes
|
||||
* @public
|
||||
*
|
||||
* @remarks
|
||||
* Designed to be used with {@link @microsoft/fast-foundation#accordionTemplate}
|
||||
* and {@link @microsoft/fast-foundation#(FASTAccordionItem:class)}.
|
||||
*/
|
||||
export class FASTAccordion extends FASTElement {
|
||||
/**
|
||||
* Controls the expand mode of the Accordion, either allowing
|
||||
* single or multiple item expansion.
|
||||
* @public
|
||||
*
|
||||
* @remarks
|
||||
* HTML attribute: expand-mode
|
||||
*/
|
||||
@attr({ attribute: "expand-mode" })
|
||||
public expandmode: AccordionExpandMode = AccordionExpandMode.multi;
|
||||
public expandmodeChanged(prev: AccordionExpandMode, next: AccordionExpandMode) {
|
||||
if (!this.$fastController.isConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
const expandedItem = this.findExpandedItem();
|
||||
|
||||
if (!expandedItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (next !== AccordionExpandMode.single) {
|
||||
(expandedItem as FASTAccordionItem)?.expandbutton.removeAttribute(
|
||||
"aria-disabled"
|
||||
);
|
||||
} else {
|
||||
this.setSingleExpandMode(expandedItem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@observable
|
||||
public slottedAccordionItems: HTMLElement[];
|
||||
|
||||
protected accordionItems: Element[];
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public slottedAccordionItemsChanged(
|
||||
oldValue: HTMLElement[],
|
||||
newValue: HTMLElement[]
|
||||
): void {
|
||||
if (this.$fastController.isConnected) {
|
||||
this.setItems();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public handleChange(source: any, propertyName: string) {
|
||||
if (propertyName === "disabled") {
|
||||
this.setItems();
|
||||
} else if (propertyName === "expanded") {
|
||||
// we only need to manage single expanded instances
|
||||
// such as scenarios where a child is programatically expanded
|
||||
if (source.expanded && this.isSingleExpandMode()) {
|
||||
this.setSingleExpandMode(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private activeid: string | null;
|
||||
private activeItemIndex: number = 0;
|
||||
private accordionIds: Array<string | null>;
|
||||
|
||||
private change = (): void => {
|
||||
this.$emit("change", this.activeid);
|
||||
};
|
||||
|
||||
private findExpandedItem(): FASTAccordionItem | Element | null {
|
||||
if (this.accordionItems.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
this.accordionItems.find(
|
||||
(item: Element | FASTAccordionItem) =>
|
||||
item instanceof FASTAccordionItem && item.expanded
|
||||
) ?? this.accordionItems[0]
|
||||
);
|
||||
}
|
||||
|
||||
private setItems = (): void => {
|
||||
if (this.slottedAccordionItems.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const children: Element[] = Array.from(this.children);
|
||||
|
||||
this.removeItemListeners(children);
|
||||
|
||||
children.forEach((child: Element) =>
|
||||
Observable.getNotifier(child).subscribe(this, "disabled")
|
||||
);
|
||||
|
||||
this.accordionItems = children.filter(child => !child.hasAttribute("disabled"));
|
||||
|
||||
this.accordionIds = this.getItemIds();
|
||||
|
||||
this.accordionItems.forEach((item: HTMLElement, index: number) => {
|
||||
if (item instanceof FASTAccordionItem) {
|
||||
item.addEventListener("click", this.activeItemChange);
|
||||
Observable.getNotifier(item).subscribe(this, "expanded");
|
||||
}
|
||||
|
||||
const itemId: string | null = this.accordionIds[index];
|
||||
item.setAttribute(
|
||||
"id",
|
||||
typeof itemId !== "string" ? `accordion-${index + 1}` : itemId
|
||||
);
|
||||
this.activeid = this.accordionIds[this.activeItemIndex] as string;
|
||||
item.addEventListener("keydown", this.handleItemKeyDown);
|
||||
item.addEventListener("focus", this.handleItemFocus);
|
||||
});
|
||||
|
||||
if (this.isSingleExpandMode()) {
|
||||
const expandedItem = this.findExpandedItem() as FASTAccordionItem;
|
||||
this.setSingleExpandMode(expandedItem);
|
||||
}
|
||||
};
|
||||
|
||||
private setSingleExpandMode(expandedItem: Element): void {
|
||||
if (this.accordionItems.length === 0) {
|
||||
return;
|
||||
}
|
||||
const currentItems = Array.from(this.accordionItems);
|
||||
this.activeItemIndex = currentItems.indexOf(expandedItem);
|
||||
|
||||
currentItems.forEach((item: FASTAccordionItem, index: number) => {
|
||||
if (this.activeItemIndex === index) {
|
||||
item.expanded = true;
|
||||
item.expandbutton.setAttribute("aria-disabled", "true");
|
||||
} else {
|
||||
item.expanded = false;
|
||||
|
||||
if (!item.hasAttribute("disabled")) {
|
||||
item.expandbutton.removeAttribute("aria-disabled");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private removeItemListeners = (oldValue: any): void => {
|
||||
oldValue.forEach((item: HTMLElement, index: number) => {
|
||||
Observable.getNotifier(item).unsubscribe(this, "disabled");
|
||||
Observable.getNotifier(item).unsubscribe(this, "expanded");
|
||||
item.removeEventListener("click", this.activeItemChange);
|
||||
item.removeEventListener("keydown", this.handleItemKeyDown);
|
||||
item.removeEventListener("focus", this.handleItemFocus);
|
||||
});
|
||||
};
|
||||
|
||||
private activeItemChange = (event: Event): void => {
|
||||
if (event.defaultPrevented || event.target !== event.currentTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
this.handleExpandedChange(event.target as HTMLElement);
|
||||
};
|
||||
|
||||
private handleExpandedChange = (item: HTMLElement) => {
|
||||
if (item instanceof FASTAccordionItem) {
|
||||
this.activeid = item.getAttribute("id");
|
||||
|
||||
if (!this.isSingleExpandMode()) {
|
||||
item.expanded = !item.expanded;
|
||||
// setSingleExpandMode sets activeItemIndex on its own
|
||||
this.activeItemIndex = this.accordionItems.indexOf(item);
|
||||
} else {
|
||||
this.setSingleExpandMode(item);
|
||||
}
|
||||
|
||||
this.change();
|
||||
}
|
||||
};
|
||||
|
||||
private getItemIds(): Array<string | null> {
|
||||
return this.slottedAccordionItems.map((accordionItem: HTMLElement) => {
|
||||
return accordionItem.id;
|
||||
});
|
||||
}
|
||||
|
||||
private isSingleExpandMode(): boolean {
|
||||
return this.expandmode === AccordionExpandMode.single;
|
||||
}
|
||||
|
||||
private handleItemKeyDown = (event: KeyboardEvent): void => {
|
||||
// only handle the keydown if the event target is the accordion item
|
||||
// prevents arrow keys from moving focus to accordion headers when focus is on accordion item panel content
|
||||
if (event.target !== event.currentTarget) {
|
||||
return;
|
||||
}
|
||||
this.accordionIds = this.getItemIds();
|
||||
switch (event.key) {
|
||||
case keyArrowUp:
|
||||
event.preventDefault();
|
||||
this.adjust(-1);
|
||||
break;
|
||||
case keyArrowDown:
|
||||
event.preventDefault();
|
||||
this.adjust(1);
|
||||
break;
|
||||
case keyHome:
|
||||
this.activeItemIndex = 0;
|
||||
this.focusItem();
|
||||
break;
|
||||
case keyEnd:
|
||||
this.activeItemIndex = this.accordionItems.length - 1;
|
||||
this.focusItem();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
private handleItemFocus = (event: FocusEvent): void => {
|
||||
// update the active item index if the focus moves to an accordion item
|
||||
// via a different method other than the up and down arrow key actions
|
||||
// only do so if the focus is actually on the accordion item and not on any of its children
|
||||
if (event.target === event.currentTarget) {
|
||||
const focusedItem = event.target as HTMLElement;
|
||||
const focusedIndex: number = (this.activeItemIndex = Array.from(
|
||||
this.accordionItems
|
||||
).indexOf(focusedItem));
|
||||
if (this.activeItemIndex !== focusedIndex && focusedIndex !== -1) {
|
||||
this.activeItemIndex = focusedIndex;
|
||||
this.activeid = this.accordionIds[this.activeItemIndex] as string;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private adjust(adjustment: number): void {
|
||||
this.activeItemIndex = wrapInBounds(
|
||||
0,
|
||||
this.accordionItems.length - 1,
|
||||
this.activeItemIndex + adjustment
|
||||
);
|
||||
this.focusItem();
|
||||
}
|
||||
|
||||
private focusItem(): void {
|
||||
const element: Element = this.accordionItems[this.activeItemIndex];
|
||||
if (element instanceof FASTAccordionItem) {
|
||||
element.expandbutton.focus();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
export { FASTAccordion } from "./accordion.js";
|
||||
export { AccordionExpandMode } from "./accordion.options.js";
|
||||
export { accordionTemplate } from "./accordion.template.js";
|
|
@ -1,18 +0,0 @@
|
|||
import { css } from "@microsoft/fast-element";
|
||||
import { FASTAccordion } from "../accordion.js";
|
||||
import { accordionTemplate } from "../accordion.template.js";
|
||||
|
||||
const styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-top: calc(var(--stroke-width) * 1px) solid
|
||||
var(--neutral-stroke-divider-rest);
|
||||
}
|
||||
`;
|
||||
|
||||
FASTAccordion.define({
|
||||
name: "fast-accordion",
|
||||
template: accordionTemplate(),
|
||||
styles,
|
||||
});
|
|
@ -1,137 +0,0 @@
|
|||
import { html } from "@microsoft/fast-element";
|
||||
import type { Meta, Story, StoryArgs } from "../../__test__/helpers.js";
|
||||
import { renderComponent } from "../../__test__/helpers.js";
|
||||
import { FASTAccordion } from "../accordion.js";
|
||||
import { AccordionExpandMode } from "../accordion.options.js";
|
||||
|
||||
const storyTemplate = html<StoryArgs<FASTAccordion>>`
|
||||
<fast-accordion expand-mode="${x => x.expandmode}">
|
||||
${x => x.storyContent}
|
||||
</fast-accordion>
|
||||
`;
|
||||
|
||||
export default {
|
||||
title: "Accordion",
|
||||
component: FASTAccordion,
|
||||
args: {
|
||||
expandmode: AccordionExpandMode.multi,
|
||||
},
|
||||
argTypes: {
|
||||
expandmode: { control: "select", options: Object.values(AccordionExpandMode) },
|
||||
storyContent: { table: { disable: true } },
|
||||
},
|
||||
} as Meta<FASTAccordion>;
|
||||
|
||||
export const Accordion: Story<FASTAccordion> = renderComponent(storyTemplate).bind({});
|
||||
Accordion.args = {
|
||||
storyContent: html`
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Accordion Item 1 Heading</span>
|
||||
Accordion Item 1 Content
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item>
|
||||
<span slot="heading">Accordion Item 2 Heading</span>
|
||||
<fast-checkbox>A checkbox as content</fast-checkbox>
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item disabled>
|
||||
<span slot="heading">Accordion Item 3 Heading</span>
|
||||
Accordion Item 3 Content
|
||||
</fast-accordion-item>
|
||||
`,
|
||||
};
|
||||
|
||||
export const AccordionWithSlottedStartEnd: Story<FASTAccordion> = renderComponent(
|
||||
storyTemplate
|
||||
).bind({});
|
||||
AccordionWithSlottedStartEnd.args = {
|
||||
storyContent: html`
|
||||
<fast-accordion-item>
|
||||
<fast-badge slot="start">start</fast-badge>
|
||||
<span slot="heading">Accordion Item 1 Heading</span>
|
||||
<fast-badge slot="end">end</fast-badge>
|
||||
Accordion Item 1 Content
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item>
|
||||
<fast-badge slot="start">start</fast-badge>
|
||||
<span slot="heading">Accordion Item 2 Heading</span>
|
||||
<fast-badge slot="end">end</fast-badge>
|
||||
<fast-checkbox>A checkbox as content</fast-checkbox>
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item disabled>
|
||||
<fast-badge slot="start">start</fast-badge>
|
||||
<span slot="heading">Accordion Item 3 Heading</span>
|
||||
<fast-badge slot="end">end</fast-badge>
|
||||
Accordion Item 3 Content
|
||||
</fast-accordion-item>
|
||||
`,
|
||||
};
|
||||
|
||||
export const AccordionWithSlottedInteractiveStartEnd: Story<FASTAccordion> =
|
||||
renderComponent(storyTemplate).bind({});
|
||||
AccordionWithSlottedInteractiveStartEnd.args = {
|
||||
storyContent: html`
|
||||
<fast-accordion-item>
|
||||
<fast-button slot="start">start</fast-button>
|
||||
<span slot="heading">Accordion Item 1 Heading</span>
|
||||
<fast-button slot="end">end</fast-button>
|
||||
Accordion Item 1 Content
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item>
|
||||
<fast-button slot="start">start</fast-button>
|
||||
<span slot="heading">Accordion Item 2 Heading</span>
|
||||
<fast-button slot="end">end</fast-button>
|
||||
<fast-checkbox>A checkbox as content</fast-checkbox>
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item disabled>
|
||||
<fast-button slot="start" disabled>start</fast-button>
|
||||
<span slot="heading">Accordion Item 3 Heading</span>
|
||||
<fast-button slot="end" disabled>end</fast-button>
|
||||
Accordion Item 3 Content
|
||||
</fast-accordion-item>
|
||||
`,
|
||||
};
|
||||
|
||||
export const AccordionWithExpandedChild: Story<FASTAccordion> = renderComponent(
|
||||
storyTemplate
|
||||
).bind({});
|
||||
AccordionWithExpandedChild.args = {
|
||||
storyContent: html`
|
||||
<fast-accordion-item>
|
||||
<div slot="heading">Accordion Item 1 Heading</div>
|
||||
Accordion Item 1 Content
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item>
|
||||
<div slot="heading">Accordion Item 2 Heading</div>
|
||||
<fast-checkbox>A checkbox as content</fast-checkbox>
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item expanded>
|
||||
<div slot="heading">Accordion Item 3 Heading</div>
|
||||
Accordion Item 3 Content
|
||||
</fast-accordion-item>
|
||||
`,
|
||||
};
|
||||
|
||||
export const AccordionWithSingleExpandMode: Story<FASTAccordion> = renderComponent(
|
||||
storyTemplate
|
||||
).bind({});
|
||||
AccordionWithSingleExpandMode.args = {
|
||||
expandmode: "single",
|
||||
storyContent: html`
|
||||
<fast-accordion-item expanded disabled>
|
||||
<div slot="heading">Accordion Item 1 Heading</div>
|
||||
Accordion Item 1 Content
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item expanded>
|
||||
<div slot="heading">Accordion Item 2 Heading</div>
|
||||
<fast-checkbox>A checkbox as content</fast-checkbox>
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item>
|
||||
<div slot="heading">Accordion Item 3 Heading</div>
|
||||
Accordion Item 3 Content
|
||||
</fast-accordion-item>
|
||||
<fast-accordion-item>
|
||||
<div slot="heading">Accordion Item 4 Heading</div>
|
||||
Accordion Item 4 Content
|
||||
</fast-accordion-item>
|
||||
`,
|
||||
};
|
|
@ -1,143 +0,0 @@
|
|||
---
|
||||
id: anchor
|
||||
title: fast-anchor
|
||||
sidebar_label: anchor
|
||||
custom_edit_url: https://github.com/microsoft/fast/edit/master/packages/web-components/fast-foundation/src/anchor/README.md
|
||||
description: fast-anchor is a web component implementation of an anchor element.
|
||||
---
|
||||
|
||||
As defined by the W3C:
|
||||
|
||||
> An anchor is a piece of text which marks the beginning and/or the end of a hypertext link.
|
||||
|
||||
`fast-anchor` is a web component implementation of an [HTML anchor element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a). The `fast-components` anchor supports the same visual appearances as the button component (accent, lightweight, neutral, outline, stealth) as well as a hypertext appearance for use inline with text.
|
||||
|
||||
## Setup
|
||||
|
||||
```ts
|
||||
import {
|
||||
provideFASTDesignSystem,
|
||||
fastAnchor
|
||||
} from "@microsoft/fast-components";
|
||||
|
||||
provideFASTDesignSystem()
|
||||
.register(
|
||||
fastAnchor()
|
||||
);
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```html live
|
||||
<fast-anchor href="https://fast.design" appearance="hypertext">FAST</fast-anchor>
|
||||
```
|
||||
|
||||
## Create your own design
|
||||
|
||||
```ts
|
||||
import {
|
||||
Anchor,
|
||||
anchorTemplate as template,
|
||||
} from "@microsoft/fast-foundation";
|
||||
import { anchorStyles as styles } from "./my-anchor.styles";
|
||||
|
||||
export const myAnchor = Anchor.compose({
|
||||
baseName: "anchor",
|
||||
template,
|
||||
styles,
|
||||
shadowOptions: {
|
||||
delegatesFocus: true,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
:::note
|
||||
This component is built with the expectation that focus is delegated to the anchor element rendered into the shadow DOM.
|
||||
:::
|
||||
|
||||
## API
|
||||
|
||||
|
||||
|
||||
### Variables
|
||||
|
||||
| Name | Description | Type |
|
||||
| -------------- | --------------------- | ------------------------------------------------------------------------- |
|
||||
| `AnchorTarget` | Anchor target values. | `{ _self: "_self", _blank: "_blank", _parent: "_parent", _top: "_top", }` |
|
||||
|
||||
<hr/>
|
||||
|
||||
|
||||
|
||||
### class: `FASTAnchor`
|
||||
|
||||
#### Superclass
|
||||
|
||||
| Name | Module | Package |
|
||||
| ------------- | ------ | ----------------------- |
|
||||
| `FASTElement` | | @microsoft/fast-element |
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Privacy | Type | Default | Description | Inherited From |
|
||||
| ---------------- | ------- | ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- |
|
||||
| `download` | public | `string` | | Prompts the user to save the linked URL. See [`<a>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) for more information. | |
|
||||
| `href` | public | `string` | | The URL the hyperlink references. See [`<a>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) for more information. | |
|
||||
| `hreflang` | public | `string` | | Hints at the language of the referenced resource. See [`<a>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) for more information. | |
|
||||
| `ping` | public | `string` | | See [`<a>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) for more information. | |
|
||||
| `referrerpolicy` | public | `string` | | See [`<a>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) for more information. | |
|
||||
| `rel` | public | `string` | | See [`<a>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) for more information. | |
|
||||
| `target` | public | `AnchorTarget` | | See [`<a>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) for more information. | |
|
||||
| `type` | public | `string` | | See [`<a>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) for more information. | |
|
||||
| `control` | public | `HTMLAnchorElement` | | References the root element | |
|
||||
|
||||
#### Attributes
|
||||
|
||||
| Name | Field | Inherited From |
|
||||
| ---------------- | -------------- | -------------- |
|
||||
| `download` | download | |
|
||||
| `href` | href | |
|
||||
| `hreflang` | hreflang | |
|
||||
| `ping` | ping | |
|
||||
| `referrerpolicy` | referrerpolicy | |
|
||||
| `rel` | rel | |
|
||||
| `target` | target | |
|
||||
| `type` | type | |
|
||||
|
||||
#### CSS Parts
|
||||
|
||||
| Name | Description |
|
||||
| --------- | ----------------------------------- |
|
||||
| `control` | The anchor element |
|
||||
| `content` | The element wrapping anchor content |
|
||||
|
||||
#### Slots
|
||||
|
||||
| Name | Description |
|
||||
| ------- | ------------------------------------------------------- |
|
||||
| `start` | Content which can be provided before the anchor content |
|
||||
| `end` | Content which can be provided after the anchor content |
|
||||
| | The default slot for anchor content |
|
||||
|
||||
<hr/>
|
||||
|
||||
### class: `DelegatesARIALink`
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Privacy | Type | Default | Description | Inherited From |
|
||||
| -------------- | ------- | ------------------------------------- | ------- | -------------------------------------------------------------------- | -------------- |
|
||||
| `ariaExpanded` | public | `"true" or "false" or string or null` | | See https://www.w3.org/WAI/PF/aria/roles#link for more information | |
|
||||
|
||||
#### Attributes
|
||||
|
||||
| Name | Field | Inherited From |
|
||||
| --------------- | ------------ | -------------- |
|
||||
| `aria-expanded` | ariaExpanded | |
|
||||
|
||||
<hr/>
|
||||
|
||||
|
||||
## Additional resources
|
||||
|
||||
* [Component explorer examples](https://explore.fast.design/components/fast-anchor)
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче