Skip to content

Commit

Permalink
refactor: glimmer migration
Browse files Browse the repository at this point in the history
  • Loading branch information
GreatWizard committed Jan 18, 2022
1 parent bfdf316 commit f2e222f
Show file tree
Hide file tree
Showing 13 changed files with 1,382 additions and 324 deletions.
84 changes: 51 additions & 33 deletions addon/components/feature-controls.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,49 @@
import Component from '@ember/component'
import Component from '@glimmer/component'
import { get, set, action } from '@ember/object'
import { inject as service } from '@ember/service'
import { action, set } from '@ember/object'
import { camelize } from '@ember/string'
import { assign } from '@ember/polyfills'
import config from 'ember-get-config'
import windowUtil from 'ember-feature-controls/utils/window'
import { getOwner } from '@ember/application'

const { featureFlags, featureControls } = config
export default class FeatureControlsComponent extends Component {
@service features
@service featureControlsStorage

export default Component.extend({
tagName: '',
features: service(),
featureControlStorage: service(),
showRefresh: true,
showReset: true,
featureControls,
featureFlags,
get featureFlags() {
return this.args.featureFlags ? this.args.featureFlags : this._featureFlags
}

init() {
this._super(...arguments)
get featureControls() {
return this.args.featureControls
? this.args.featureControls
: this._featureControls
}

get showRefresh() {
return this.args.showRefresh ? this.args.showRefresh : true
}

get showReset() {
return this.args.showReset ? this.args.showReset : true
}

constructor(...args) {
super(...args)
let { featureFlags, featureControls } =
getOwner(this).resolveRegistration('config:environment')
this._featureFlags = featureFlags
this._featureControls = featureControls
this.refresh()
},
}

_normalizeFlag(key) {
return this.features._normalizeFlag(key)
},
return this.features._normalizeFlag(key) || camelize(key)
}

// Refresh the state of the feature flags list component
refresh: action(function () {
@action
refresh() {
// Take the existing flags from the config and put them in a list of default values
let featureFlags = this.featureFlags
let defaults = {}
Expand All @@ -36,7 +53,7 @@ export default Component.extend({
// Model is a local copy of the list of flags register for features service, used to compute properties on the full list
let model = (this.features.flags || []).map((key) => {
let meta =
((featureControls && this.featureControls.metadata) || []).find(
((this.featureControls && this.featureControls.metadata) || []).find(
(obj) => {
return this._normalizeFlag(obj.key) === key
}
Expand All @@ -46,11 +63,11 @@ export default Component.extend({
}
let isFlagLS =
this.featureControls.useLocalStorage &&
this.get(`featureControlStorage.featuresLS.${key}`) !== undefined
get(this, `featureControlsStorage.featuresLS.${key}`) !== undefined
let featureFlag = {
key,
isEnabled: isFlagLS
? this.get(`featureControlStorage.featuresLS.${key}`)
? get(this, `featureControlsStorage.featuresLS.${key}`)
: this.features.isEnabled(key),
default: defaults[key] || false,
}
Expand All @@ -61,44 +78,45 @@ export default Component.extend({
'model',
model.filter((item) => item !== undefined)
)
}),
}

reset: action(function () {
@action
reset() {
// Reset the flags from the features service to the default value in the config
let featureFlags = this.featureFlags
Object.keys(featureFlags).forEach((key) => {
this.updateFeature(this._normalizeFlag(key), featureFlags[key])
})
// If we use local storage then we want to clear the stored data
if (this.featureControls.useLocalStorage) {
this.featureControlStorage.featuresLS.reset()
this.featureControlsStorage.featuresLS.reset()
}
}),
}

@action
updateFeature(key, isEnabled) {
if (isEnabled) {
this.features.enable(key)
} else {
this.features.disable(key)
}
// Update the local model accordingly
let model = this.model
let modelFlag = model.find((obj) => {
let modelFlag = this.model.find((obj) => {
return obj.key === key
})
if (modelFlag) {
set(modelFlag, 'isEnabled', isEnabled)
set(this, 'model', model)
if (modelFlag.reload) {
windowUtil.reload()
}
}
},
}

doToggleFeature: action(function (key, checkboxState) {
@action
doToggleFeature(key, checkboxState) {
this.updateFeature(key, !checkboxState)
if (this.featureControls.useLocalStorage) {
this.set(`featureControlStorage.featuresLS.${key}`, !checkboxState)
set(this, `featureControlsStorage.featuresLS.${key}`, !checkboxState)
}
}),
})
}
}
15 changes: 7 additions & 8 deletions addon/instance-initializers/load-feature-controls.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import config from 'ember-get-config'

const { featureControls } = config

export function initialize(appInstance) {
const features = appInstance.lookup('service:features')
const { featureControls } =
appInstance.resolveRegistration('config:environment')
if (featureControls && featureControls.useLocalStorage) {
const controlStorageService = appInstance.lookup(
'service:feature-control-storage'
const storageService = appInstance.lookup(
'service:feature-controls-storage'
)
// result of controlStorageService.get('featuresLS') is an ObjectProxy we need to use "content"
let { content: featureControls } = controlStorageService.get('featuresLS')
// result of storageService.get('featuresLS') is an ObjectProxy we need to use "content"
let { content: featureControls } = storageService.get('featuresLS')
if (featureControls) {
Object.keys(featureControls).forEach((flag) => {
if (features.get('flags').includes(flag)) {
console.log(`i ${flag} => ${featureControls[flag]}`)
featureControls[flag] ? features.enable(flag) : features.disable(flag)
}
})
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { default } from 'ember-feature-controls/services/feature-control-storage'
export { default } from 'ember-feature-controls/services/feature-controls-storage'
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
"ember-cli-babel": "^7.26.6",
"ember-cli-htmlbars": "^6.0.0",
"ember-feature-flags": "^6.0.0",
"ember-get-config": "^0.3.0",
"ember-local-storage": "^2.0.1",
"ember-truth-helpers": "^3.0.0"
},
Expand All @@ -53,7 +52,7 @@
"@embroider/test-setup": "^0.47.1",
"@glimmer/component": "^1.0.4",
"@glimmer/tracking": "^1.0.4",
"babel-eslint": "^10.1.0",
"babel-eslint": "^8.0.0",
"broccoli-asset-rev": "^3.0.0",
"ember-auto-import": "^2.2.0",
"ember-cli": "~3.28.1",
Expand All @@ -62,6 +61,7 @@
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sri": "^2.1.1",
"ember-cli-terser": "^4.0.2",
"ember-decorators": "^6.1.1",
"ember-disable-prototype-extensions": "^1.1.3",
"ember-export-application-global": "^2.0.1",
"ember-load-initializers": "^2.1.2",
Expand Down
90 changes: 90 additions & 0 deletions tests/acceptance/change-flag-test copy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { module, test } from 'qunit'
import { visit, click } from '@ember/test-helpers'
import { setupApplicationTest } from 'ember-qunit'
import resetStorages from 'ember-local-storage/test-support/reset-storage'
import windowUtil from 'ember-feature-controls/utils/window'

const originalWindowReload = windowUtil.reload

module('Acceptance | change flag', function (hooks) {
setupApplicationTest(hooks)

hooks.beforeEach(function () {
windowUtil.reload = function () {}
})

hooks.afterEach(function () {
windowUtil.reload = originalWindowReload
if (window.localStorage) {
window.localStorage.clear()
}
if (window.sessionStorage) {
window.sessionStorage.clear()
}
resetStorages()
})

test('it initializes the app with bear on and bacon off', async function (assert) {
await visit('/')
assert.dom('img[alt="bear"]').exists()
assert.dom('img[alt="bacon"]').doesNotExist()
await visit('/__features')
assert.dom('[data-test-label-flag=showBear]').hasText('')
assert.dom('[data-test-label-flag=showBacon]').hasText('')
})

test('it changes feature flags', async function (assert) {
await visit('/__features')
await click('[data-test-checkbox-flag=showBear]')
assert.dom('[data-test-label-flag=showBear]').hasText('❗')
await click('[data-test-checkbox-flag=showBacon]')
assert.dom('[data-test-label-flag=showBacon]').hasText('❗')
})

test('it persists the changes at link to another page', async function (assert) {
await visit('/__features')
await click('[data-test-checkbox-flag=showBacon]')
await click('[data-test-link-index]')
assert.dom('img[alt="bear"]').exists()
assert.dom('img[alt="bacon"]').exists()
await click('[data-test-link-features]')
await click('[data-test-checkbox-flag=showBear]')
await click('[data-test-link-index]')
assert.dom('img[alt="bear"]').doesNotExist()
assert.dom('img[alt="bacon"]').exists()
})

test('it persists the flags when clicking on refresh button', async function (assert) {
await visit('/__features')
await click('[data-test-checkbox-flag=showBacon]')
await click('[data-test-button-refresh]')
assert.dom('[data-test-label-flag=showBacon]').hasText('❗')
})

test('it resets feature flags when clicking on reset button', async function (assert) {
await visit('/__features')
await click('[data-test-checkbox-flag=showBear]')
await click('[data-test-checkbox-flag=showBacon]')
await click('[data-test-button-reset]')
assert.dom('[data-test-label-flag=showBacon]').hasText('')
assert.dom('[data-test-label-flag=showBacon]').hasText('')
})

test("it doesn't reload page when clicking on a not reloadable feature flag", async function (assert) {
assert.expect(0)
windowUtil.reload = function () {
assert.ok(true, 'Reload function is called')
}
await visit('/__features')
await click('[data-test-checkbox-flag=showBear]')
})

test('it reloads page when clicking on a reloadable feature flag', async function (assert) {
assert.expect(1)
windowUtil.reload = function () {
assert.ok(true, 'Reload function is called')
}
await visit('/__features')
await click('[data-test-checkbox-flag=showBacon]')
})
})
Loading

0 comments on commit f2e222f

Please sign in to comment.