├── force-app ├── main │ └── default │ │ └── lwc │ │ ├── dynamicElement │ │ ├── dynamicElement.html │ │ ├── templates │ │ │ ├── node │ │ │ │ └── text.html │ │ │ ├── lightning │ │ │ │ ├── lightning-button-group.html │ │ │ │ ├── lightning-badge.html │ │ │ │ ├── lightning-formatted-time.html │ │ │ │ ├── lightning-relative-date-time.html │ │ │ │ ├── lightning-formatted-text.html │ │ │ │ ├── lightning-formatted-phone.html │ │ │ │ ├── lightning-breadcrumb.html │ │ │ │ ├── lightning-tree.html │ │ │ │ ├── lightning-formatted-location.html │ │ │ │ ├── lightning-progress-bar.html │ │ │ │ ├── lightning-formatted-rich-text.html │ │ │ │ ├── lightning-spinner.html │ │ │ │ ├── lightning-click-to-dial.html │ │ │ │ ├── lightning-helptext.html │ │ │ │ ├── lightning-dynamic-icon.html │ │ │ │ ├── lightning-output-field.html │ │ │ │ ├── lightning-vertical-navigation-item.html │ │ │ │ ├── lightning-progress-ring.html │ │ │ │ ├── lightning-formatted-email.html │ │ │ │ ├── lightning-icon.html │ │ │ │ ├── lightning-formatted-url.html │ │ │ │ ├── lightning-layout.html │ │ │ │ ├── lightning-vertical-navigation-item-icon.html │ │ │ │ ├── lightning-avatar.html │ │ │ │ ├── lightning-vertical-navigation-item-badge.html │ │ │ │ ├── lightning-input-field.html │ │ │ │ ├── lightning-file-upload.html │ │ │ │ ├── lightning-button-icon-stateful.html │ │ │ │ ├── lightning-formatted-name.html │ │ │ │ ├── lightning-pill-container.html │ │ │ │ ├── lightning-button-icon.html │ │ │ │ └── lightning-map.html │ │ │ └── html │ │ │ │ ├── b.html │ │ │ │ ├── i.html │ │ │ │ ├── s.html │ │ │ │ ├── u.html │ │ │ │ ├── wbr.html │ │ │ │ ├── bdo.html │ │ │ │ ├── big.html │ │ │ │ ├── dd.html │ │ │ │ ├── dfn.html │ │ │ │ ├── dt.html │ │ │ │ ├── em.html │ │ │ │ ├── kbd.html │ │ │ │ └── nav.html │ │ └── dynamicElement.js-meta.xml │ │ └── .eslintrc.json └── example │ └── lwc │ ├── .eslintrc.json │ └── dynamicElementTestApp │ ├── dynamicElementTestApp.js-meta.xml │ ├── dynamicElementTestApp.html │ └── dynamicElementTestApp.js ├── .eslintignore ├── .prettierignore ├── config └── project-scratch-def.json ├── bin └── orgInit.sh ├── sfdx-project.json ├── .prettierrc ├── .forceignore ├── .gitignore ├── package.json └── README.md /force-app/main/default/lwc/dynamicElement/dynamicElement.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /force-app/example/lwc/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@salesforce/eslint-config-lwc/recommended"] 3 | } 4 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@salesforce/eslint-config-lwc/recommended"] 3 | } 4 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/node/text.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/lwc/**/*.css 2 | **/lwc/**/*.html 3 | **/lwc/**/*.json 4 | **/lwc/**/*.svg 5 | **/lwc/**/*.xml 6 | .sfdx 7 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # List files or directories below to ignore them when running prettier 2 | # More information: https://prettier.io/docs/en/ignore.html 3 | # 4 | 5 | **/staticresources/** 6 | .localdevserver 7 | .sfdx 8 | coverage/ 9 | .vscode -------------------------------------------------------------------------------- /config/project-scratch-def.json: -------------------------------------------------------------------------------- 1 | { 2 | "orgName": "LWC Dynamic Element", 3 | "edition": "Developer", 4 | "hasSampleData": false, 5 | "settings": { 6 | "lightningExperienceSettings": { 7 | "enableS1DesktopEnabled": true 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/dynamicElement.js-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 48.0 4 | false 5 | 6 | -------------------------------------------------------------------------------- /bin/orgInit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DURATION=7 4 | 5 | if [ "$#" -eq 1 ]; then 6 | DURATION=$1 7 | fi 8 | 9 | sfdx force:org:create -a lwc-dynamic-element -s -f config/project-scratch-def.json -d $DURATION 10 | sfdx force:source:push 11 | sfdx force:org:open -p /lightning/page/home 12 | echo "Org is set up" -------------------------------------------------------------------------------- /sfdx-project.json: -------------------------------------------------------------------------------- 1 | { 2 | "packageDirectories": [ 3 | { 4 | "path": "force-app", 5 | "default": true, 6 | "package": "LWCDynamicElement", 7 | "versionName": "Spring '20", 8 | "versionNumber": "1.0.0" 9 | } 10 | ], 11 | "namespace": "", 12 | "sourceApiVersion": "48.0", 13 | "sfdcLoginUrl": "https://login.salesforce.com" 14 | } 15 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "overrides": [ 6 | { 7 | "files": "**/lwc/**/*.html", 8 | "options": { "parser": "lwc" } 9 | }, 10 | { 11 | "files": "*.{cmp,page,component}", 12 | "options": { "parser": "html" } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /force-app/example/lwc/dynamicElementTestApp/dynamicElementTestApp.js-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 48.0 4 | true 5 | 6 | lightning__AppPage 7 | lightning__RecordPage 8 | lightning__HomePage 9 | 10 | 11 | -------------------------------------------------------------------------------- /.forceignore: -------------------------------------------------------------------------------- 1 | # List files or directories below to ignore them when running force:source:push, force:source:pull, and force:source:status 2 | # More information: https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_exclude_source.htm 3 | # 4 | 5 | # Standard metadata 6 | package.xml 7 | **appMenu 8 | **appSwitcher 9 | **objectTranslations 10 | **profiles 11 | **settings 12 | 13 | # LWC configuration files 14 | **/jsconfig.json 15 | **/.eslintrc.json 16 | 17 | # LWC Jest 18 | **/__tests__/** -------------------------------------------------------------------------------- /force-app/example/lwc/dynamicElementTestApp/dynamicElementTestApp.html: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This file is used for Git repositories to specify intentionally untracked files that Git should ignore. 2 | # If you are not using git, you can delete this file. For more information see: https://git-scm.com/docs/gitignore 3 | # For useful gitignore templates see: https://github.com/github/gitignore 4 | 5 | # Salesforce dev 6 | .sfdx/ 7 | .localdevserver/ 8 | 9 | # LWC VSCode autocomplete 10 | jsconfig.json 11 | 12 | # LWC Jest coverage reports 13 | coverage/ 14 | 15 | # Logs 16 | logs 17 | *.log 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | 22 | # Dependency directories 23 | node_modules/ 24 | 25 | # Eslint cache 26 | .eslintcache 27 | 28 | # MacOS system files 29 | .DS_Store 30 | 31 | # Windows system files 32 | Thumbs.db 33 | ehthumbs.db 34 | [Dd]esktop.ini 35 | $RECYCLE.BIN/ 36 | 37 | # VS Code project settings 38 | .vscode/ -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lwc-dynamic-element", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "bin": {}, 7 | "scripts": { 8 | "lint": "npm run lint:lwc", 9 | "lint:lwc": "eslint **/lwc/**", 10 | "test": "npm run lint && npm run test:unit", 11 | "test:unit": "sfdx-lwc-jest", 12 | "test:unit:watch": "sfdx-lwc-jest --watch", 13 | "test:unit:debug": "sfdx-lwc-jest --debug", 14 | "test:unit:coverage": "sfdx-lwc-jest --coverage", 15 | "prettier": "prettier --write \"**/*.{cls,cmp,component,css,html,js,json,md,page,trigger,xml,yaml,yml}\"", 16 | "prettier:verify": "prettier --list-different \"**/*.{cls,cmp,component,css,html,js,json,md,page,trigger,xml,yaml,yml}\"" 17 | }, 18 | "author": "Shinichi Tomita ", 19 | "license": "MIT", 20 | "devDependencies": { 21 | "@prettier/plugin-xml": "^0.7.2", 22 | "@salesforce/eslint-config-lwc": "^0.5.0", 23 | "eslint": "^6.8.0", 24 | "prettier": "^1.19.1" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LWC Dynamic Element 2 | 3 | A reusable component that renders dynamic component element UI 4 | 5 | ## Install using a Scratch Org 6 | 7 | 1. Set up your environment. Follow the steps in the [Quick Start: Lightning Web Components](https://trailhead.salesforce.com/content/learn/projects/quick-start-lightning-web-components/) Trailhead project. The steps include: 8 | 9 | - Enable Dev Hub in your Trailhead Playground 10 | - Install Salesforce CLI 11 | - Install Visual Studio Code 12 | - Install the Visual Studio Code Salesforce extensions, including the Lightning Web Components extension 13 | 14 | 2. If you haven't already done so, authenticate with your hub org and provide it with an alias (**myhuborg** in the command below): 15 | 16 | ``` 17 | sfdx force:auth:web:login -d -a myhuborg 18 | ``` 19 | 20 | 3. Clone the lwc-dynamic-element repository: 21 | 22 | ``` 23 | git clone https://github.com/stomita/lwc-dynamic-element 24 | cd lwc-dynamic-element 25 | ``` 26 | 27 | 4. Create a scratch org and provide it with an alias 28 | 29 | ``` 30 | sfdx force:org:create -s -f config/project-scratch-def.json -a lwc-dynamic-element 31 | ``` 32 | 33 | 5. Push the app to your scratch org: 34 | 35 | ``` 36 | sfdx force:source:push 37 | ``` 38 | 39 | 6. Open the scratch org: 40 | 41 | ``` 42 | sfdx force:org:open 43 | ``` 44 | 45 | 7. Go to **Home** tab, and select "Edit Page" from menu to lauch Lightning App Builder. 46 | 47 | 8. Put "dynamicElementTestApp" component onto the area in the page, and save. 48 | 49 | -------------------------------------------------------------------------------- /force-app/example/lwc/dynamicElementTestApp/dynamicElementTestApp.js: -------------------------------------------------------------------------------- 1 | import { LightningElement, track } from 'lwc'; 2 | 3 | /** 4 | * 5 | */ 6 | export default class DynamicElementTestApp extends LightningElement { 7 | @track 8 | rootNode = { 9 | tag: 'lightning-card', 10 | attrs: { 11 | title: 'Dynamic Element Test', 12 | iconName: 'custom:custom14', 13 | }, 14 | children: [], 15 | }; 16 | 17 | activeContainerKey = null; 18 | 19 | getActiveContainer() { 20 | console.log('container=>', this.activeContainerKey); 21 | if (!this.activeContainerKey) { 22 | return this.rootNode; 23 | } 24 | const container = this.findByKey(this.activeContainerKey) || this.rootNode; 25 | console.log( 26 | 'container', 27 | container.tag, 28 | container.key, 29 | container.attrs.style, 30 | ); 31 | return container; 32 | } 33 | 34 | createNode(tag, attrs = {}, children = []) { 35 | const key = Math.random() 36 | .toString(16) 37 | .substring(2); 38 | return { key, tag, attrs, children }; 39 | } 40 | 41 | query(filterFn, node = this.rootNode) { 42 | const nodes = []; 43 | if (filterFn(node)) { 44 | nodes.push(node); 45 | } 46 | for (const cn of node.children) { 47 | const ns = this.query(filterFn, cn); 48 | nodes.push(...ns); 49 | } 50 | return nodes; 51 | } 52 | 53 | find(filterFn, node = this.rootNode) { 54 | if (filterFn(node)) { 55 | return node; 56 | } 57 | for (const cn of node.children) { 58 | const n = this.find(filterFn, cn); 59 | if (n) { 60 | return n; 61 | } 62 | } 63 | return undefined; 64 | } 65 | 66 | findByKey(key, node = this.rootNode) { 67 | return this.find(n => n.key === key, node); 68 | } 69 | 70 | findByTag(tag, node = this.rootNode) { 71 | return this.find(n => n.tag === tag, node); 72 | } 73 | 74 | remove(rejectFn, node = this.rootNode) { 75 | const children = node.children.filter(n => !rejectFn(n)); 76 | if (children.length !== node.children.length) { 77 | node.children = children; 78 | } 79 | for (const cn of node.children) { 80 | this.remove(rejectFn, cn); 81 | } 82 | } 83 | 84 | focusContainer(key, e) { 85 | console.log('focus container', key); 86 | if (e) { 87 | e.stopPropagation(); 88 | } 89 | this.activeContainerKey = key; 90 | const containers = this.query(n => n.tag === 'div'); 91 | for (const ct of containers) { 92 | ct.attrs.style = undefined; 93 | } 94 | const focusContainer = this.getActiveContainer(); 95 | focusContainer.attrs.style = 'border: 1px solid blue'; 96 | } 97 | 98 | addIcon() { 99 | console.log('add icon'); 100 | console.log(this.rootNode); 101 | const node = this.createNode('lightning-icon', { 102 | iconName: 'utility:success', 103 | style: 'cursor: pointer', 104 | }); 105 | node.attrs.onclick = this.toggleIcon.bind(this, node.key); 106 | const container = this.getActiveContainer(); 107 | container.children.push(node); 108 | } 109 | 110 | addContainer() { 111 | const currentContainer = this.getActiveContainer(); 112 | const newContainer = this.createNode('div', { 113 | class: 'slds-m-left_large slds-p-around_small', 114 | style: 'min-height: 10px', 115 | }); 116 | newContainer.attrs.onclick = this.focusContainer.bind( 117 | this, 118 | newContainer.key, 119 | ); 120 | currentContainer.children.push(newContainer); 121 | this.focusContainer(newContainer.key); 122 | } 123 | 124 | toggleIcon(key) { 125 | console.log('toggle icon', key); 126 | const icon = this.findByKey(key); 127 | if (icon) { 128 | icon.attrs.variant = 129 | icon.attrs.variant === 'success' ? undefined : 'success'; 130 | } 131 | } 132 | 133 | removeSelectedIcons() { 134 | this.remove( 135 | n => n.tag === 'lightning-icon' && n.attrs.variant === 'success', 136 | ); 137 | } 138 | 139 | removeContainer() { 140 | this.remove(n => n.key === this.activeContainerKey); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-button-group.html: -------------------------------------------------------------------------------- 1 | 114 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-badge.html: -------------------------------------------------------------------------------- 1 | 115 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-formatted-time.html: -------------------------------------------------------------------------------- 1 | 115 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-relative-date-time.html: -------------------------------------------------------------------------------- 1 | 115 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-formatted-text.html: -------------------------------------------------------------------------------- 1 | 116 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-formatted-phone.html: -------------------------------------------------------------------------------- 1 | 116 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-breadcrumb.html: -------------------------------------------------------------------------------- 1 | 117 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-tree.html: -------------------------------------------------------------------------------- 1 | 117 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-formatted-location.html: -------------------------------------------------------------------------------- 1 | 116 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-progress-bar.html: -------------------------------------------------------------------------------- 1 | 117 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-formatted-rich-text.html: -------------------------------------------------------------------------------- 1 | 116 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-spinner.html: -------------------------------------------------------------------------------- 1 | 117 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-click-to-dial.html: -------------------------------------------------------------------------------- 1 | 117 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-helptext.html: -------------------------------------------------------------------------------- 1 | 117 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-dynamic-icon.html: -------------------------------------------------------------------------------- 1 | 117 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-output-field.html: -------------------------------------------------------------------------------- 1 | 117 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-vertical-navigation-item.html: -------------------------------------------------------------------------------- 1 | 117 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-progress-ring.html: -------------------------------------------------------------------------------- 1 | 118 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-formatted-email.html: -------------------------------------------------------------------------------- 1 | 118 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-icon.html: -------------------------------------------------------------------------------- 1 | 119 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-formatted-url.html: -------------------------------------------------------------------------------- 1 | 119 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-layout.html: -------------------------------------------------------------------------------- 1 | 118 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-vertical-navigation-item-icon.html: -------------------------------------------------------------------------------- 1 | 118 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-avatar.html: -------------------------------------------------------------------------------- 1 | 120 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-vertical-navigation-item-badge.html: -------------------------------------------------------------------------------- 1 | 119 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-input-field.html: -------------------------------------------------------------------------------- 1 | 121 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-file-upload.html: -------------------------------------------------------------------------------- 1 | 121 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-button-icon-stateful.html: -------------------------------------------------------------------------------- 1 | 121 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-formatted-name.html: -------------------------------------------------------------------------------- 1 | 121 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-pill-container.html: -------------------------------------------------------------------------------- 1 | 121 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-button-icon.html: -------------------------------------------------------------------------------- 1 | 123 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/html/b.html: -------------------------------------------------------------------------------- 1 | 125 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/html/i.html: -------------------------------------------------------------------------------- 1 | 125 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/html/s.html: -------------------------------------------------------------------------------- 1 | 125 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/html/u.html: -------------------------------------------------------------------------------- 1 | 125 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/html/wbr.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/lightning/lightning-map.html: -------------------------------------------------------------------------------- 1 | 122 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/html/bdo.html: -------------------------------------------------------------------------------- 1 | 125 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/html/big.html: -------------------------------------------------------------------------------- 1 | 125 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/html/dd.html: -------------------------------------------------------------------------------- 1 | 125 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/html/dfn.html: -------------------------------------------------------------------------------- 1 | 125 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/html/dt.html: -------------------------------------------------------------------------------- 1 | 125 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/html/em.html: -------------------------------------------------------------------------------- 1 | 125 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/html/kbd.html: -------------------------------------------------------------------------------- 1 | 125 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/dynamicElement/templates/html/nav.html: -------------------------------------------------------------------------------- 1 | 125 | --------------------------------------------------------------------------------