Skip to content

Commit d43d160

Browse files
author
Balwant Singh 🚀
committed
Initial Commit
0 parents  commit d43d160

10 files changed

+324
-0
lines changed

README.md

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Feature Flag Chrome Plugin
2+
3+
Chrome Plugin to manage Feature Flags on any supported web app.
4+
5+
Chrome Web Store Link : **TBC**
6+
7+
---
8+
9+
## Adding Feature Flag Support to your web app
10+
11+
To enable chrome plugins on your web app you need to register your feature flags from your app code.
12+
13+
To register, call the `featureFlagsPluginRegister` function on your window object.
14+
15+
```javascript
16+
function featureFlagsPluginRegister(flags, listener);
17+
```
18+
19+
Example:
20+
21+
```javascript
22+
// Somewhere in you app's JS code
23+
24+
// Declare Flags
25+
const flags = [
26+
{
27+
key: 'flag.one'
28+
value: true, // current / initial state of feature flags in you app.
29+
description: 'First feature description', // Optional
30+
title: 'First feature', // Optional - If not passed, key will be used.
31+
},
32+
{
33+
key: 'flag.two'
34+
value: true, // current / initial state of feature flags in you app.
35+
description: 'Second feature description', // Optional
36+
title: 'Second feature', // Optional - If not passed, key will be used.
37+
}
38+
];
39+
40+
// This function will be called whenever a change happens in Chrome extension
41+
function listener(key, value) {
42+
// Feature flag with key change and update value is in value.
43+
44+
// Make your changes in your app
45+
toggleFeatureFlag(key, value);
46+
updateApp();
47+
}
48+
49+
50+
// Register Feature Flags with plugin
51+
if (window.featureFlagsPluginRegister) {
52+
window.featureFlagsPluginRegister(flags, listener);
53+
}
54+
```
55+
56+
---
57+
58+
### Testing Snippet
59+
60+
```javascript
61+
window.featureFlagsPluginRegister(
62+
[
63+
{
64+
key: 'flag.one',
65+
value: true,
66+
description: 'First feature description',
67+
title: 'First feature',
68+
},
69+
{
70+
key: 'flag.two',
71+
value: true,
72+
description: 'Second feature description',
73+
title: 'Second feature',
74+
},
75+
],
76+
(key, value) => console.log('KV Received', key, value)
77+
);
78+
```

plugin/background.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
2+
// We only accept messages from ourselves
3+
switch (message.type) {
4+
case 'REGISTER_FLAGS':
5+
chrome.storage.local.set({[`${sender.tab.windowId}_${sender.tab.id}`] : message.flags})
6+
break;
7+
8+
case 'FLAG_UPDATED':
9+
chrome.tabs.sendMessage(parseInt(message.tab, 10), message);
10+
11+
chrome.storage.local.get(`${message.win}_${message.tab}`, value => {
12+
let flags = value[`${message.win}_${message.tab}`];
13+
for (let fIdx = 0; fIdx < flags.length; fIdx++) {
14+
const flag = flags[fIdx];
15+
if (flag.key === message.key) {
16+
flag.value = message.value;
17+
}
18+
}
19+
chrome.storage.local.set({[`${message.win}_${message.tab}`] : flags})
20+
})
21+
break;
22+
23+
default:
24+
console.debug('Default case called in BG SVC', message, sender);
25+
break;
26+
}
27+
return true;
28+
});

plugin/content-script.js

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
;(function() {
2+
3+
chrome.runtime.onMessage.addListener(function (event) {
4+
var injected = document.documentElement.appendChild(document.createElement('script'));
5+
injected.text = '(' + function(key, value) {
6+
if(window.featureFlagsPluginConfig && window.featureFlagsPluginConfig.listener) {
7+
window.featureFlagsPluginConfig.listener(key, value)
8+
}
9+
} + `)('${event.key}', ${event.value})`;
10+
injected.remove();
11+
})
12+
13+
window.addEventListener("message", function(event) {
14+
// We only accept messages from ourselves
15+
if (event.source != window)
16+
return;
17+
18+
if (event.data.type && (event.data.type == "REGISTER_FLAGS")) {
19+
chrome.runtime.sendMessage(event.data);
20+
}
21+
}, false);
22+
23+
function script() {
24+
window.featureFlagsPluginConfig = {
25+
listener: () => {},
26+
}
27+
function featureFlagsPluginRegister(flags, listener) {
28+
console.debug('Register Called with', flags, listener);
29+
window.postMessage({ type: "REGISTER_FLAGS", flags: flags }, "*");
30+
window.featureFlagsPluginConfig.listener = listener;
31+
}
32+
window.featureFlagsPluginRegister = featureFlagsPluginRegister;
33+
window.addEventListener('message', event => {
34+
35+
});
36+
console.info('featureFlagsPluginRegister Registered :)');
37+
}
38+
39+
function inject(fn) {
40+
const script = document.createElement('script')
41+
script.text = `(${fn.toString()})();`
42+
document.documentElement.appendChild(script)
43+
}
44+
45+
inject(script)
46+
})()

plugin/images/switch128.png

3.45 KB
Loading

plugin/images/switch16.png

574 Bytes
Loading

plugin/images/switch32.png

965 Bytes
Loading

plugin/images/switch48.png

1.19 KB
Loading

plugin/manifest.json

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"name": "Feature Flags",
3+
"version": "1.0",
4+
"description": "Chrome Extension to manage Feature Flags in supported web apps",
5+
"permissions": [
6+
"tabs",
7+
"activeTab",
8+
"storage"
9+
],
10+
"background": {
11+
"scripts": [
12+
"background.js"
13+
],
14+
"persistent": false
15+
},
16+
"content_scripts": [
17+
{
18+
"matches": [
19+
"<all_urls>"
20+
],
21+
"js": [
22+
"content-script.js"
23+
]
24+
}
25+
],
26+
"browser_action": {
27+
"default_popup": "popup.html",
28+
"default_icon": {
29+
"16": "images/switch16.png",
30+
"32": "images/switch32.png",
31+
"48": "images/switch48.png",
32+
"128": "images/switch128.png"
33+
}
34+
},
35+
"icons": {
36+
"16": "images/switch16.png",
37+
"32": "images/switch32.png",
38+
"48": "images/switch48.png",
39+
"128": "images/switch128.png"
40+
},
41+
"manifest_version": 2
42+
}

plugin/popup.html

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<html>
2+
3+
<head>
4+
<style>
5+
body {
6+
min-width: 340px;
7+
padding: 20px;
8+
}
9+
10+
td {
11+
padding-bottom: 20px;
12+
font-size: 12px;
13+
}
14+
15+
strong {
16+
font-size: 14px;
17+
}
18+
19+
input[type=checkbox] {
20+
height: 0;
21+
width: 0;
22+
visibility: hidden;
23+
}
24+
25+
label {
26+
cursor: pointer;
27+
text-indent: -9999px;
28+
width: 40px;
29+
height: 20px;
30+
background: grey;
31+
display: block;
32+
border-radius: 20px;
33+
position: relative;
34+
}
35+
36+
label:after {
37+
content: '';
38+
position: absolute;
39+
top: 1px;
40+
left: 1px;
41+
width: 18px;
42+
height: 18px;
43+
background: #fff;
44+
border-radius: 18px;
45+
transition: 0.3s;
46+
}
47+
48+
input:checked+label {
49+
background: #bada55;
50+
}
51+
52+
input:checked+label:after {
53+
left: calc(100% - 1px);
54+
transform: translateX(-100%);
55+
}
56+
57+
label:active:after {
58+
width: 20px;
59+
}
60+
</style>
61+
</head>
62+
63+
<body>
64+
<h1>Feature Flags</h1>
65+
<div id="flags-container">
66+
<p>Feature flags not configured. </p>
67+
<p>Please see Readme.md : <a target="_blank"
68+
href="https://github.com/phenixcoder/feature-flags-chrome-plugin">https://github.com/phenixcoder/feature-flags-chrome-plugin</a>
69+
</p>
70+
</div>
71+
</div>
72+
<script src="popup.js"></script>
73+
</body>
74+
75+
</html>

plugin/popup.js

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
let flagsContainer = document.getElementById('flags-container');
2+
3+
window.onload = function() {
4+
chrome.tabs.getSelected(null,function(tab) {
5+
chrome.storage.local.get(`${tab.windowId}_${tab.id}`, (value) => {
6+
flagsContainer.innerHTML = renderTable(value[`${tab.windowId}_${tab.id}`], tab.windowId, tab.id);
7+
bindEvents();
8+
})
9+
});
10+
}
11+
12+
function bindEvents() {
13+
var checks = document.getElementsByClassName('checks');
14+
if (checks.length) {
15+
for (let cIdx = 0; cIdx < checks.length; cIdx++) {
16+
const check = checks[cIdx];
17+
if (check) {
18+
check.addEventListener('change', checkChanged)
19+
}
20+
}
21+
}
22+
23+
24+
}
25+
function checkChanged(event) {
26+
var check = event.target;
27+
chrome.runtime.sendMessage({
28+
type: 'FLAG_UPDATED',
29+
key: check.getAttribute('name'),
30+
tab: check.getAttribute('tabId'),
31+
win: check.getAttribute('windowId'),
32+
value: check.checked
33+
})
34+
35+
}
36+
37+
function renderTable(flags, winId, tabId) {
38+
return `
39+
<table width="100%">
40+
${flags.map(flag => `
41+
<tr>
42+
<td>
43+
<strong>${flag.title}</strong><br />
44+
${flag.description}
45+
</td>
46+
<td valign="top">
47+
<input type="checkbox" id="${flag.key}" windowId="${winId}" tabId="${tabId}" class="checks" name="${flag.key}" ${flag.value ? 'checked' : ''} />
48+
<label for="${flag.key}">${flag.title}</label>
49+
</td>
50+
</tr>
51+
`).join('')}
52+
</table>
53+
`;
54+
55+
}

0 commit comments

Comments
 (0)