@nuskin/ns-feature-flags-vue
v2.1.1
Published
Nu Skin Feature Flagging Vue Plugin
Downloads
996
Keywords
Readme
Nu Skin Feature Flags - Vue
This package includes Nu Skin's Vue plugin for working with feature flagging.
For more information on feature flagging at Nu Skin, like instructions about how to set up feature flags, see our internal documentation.
Note that, due to limitations of our current feature flag server, all flag names must
consist of lower case letters, digits, underscores _
, and dashes -
. The name must
start with a letter and must end with a letter or number. For more, see
Feature Flag Naming.
Installation
npm i --save @nuskin/ns-feature-flags-vue
yarn add @nuskin/ns-feature-flags-vue
This package exports both CommonJS and ES Modules versions, so all build tooling which respects the 'modules' option in package.json (like Webpack and Rollup) will find the correct version for their environment.
Usage
In a Component
In an individual component, you can use the Mixin to expose feature flags to your component. This works both in applications and component libraries.
import {UsesFeatureFlags} from '@nuskin/ns-feature-flags-vue';
export default {
mixins: [UsesFeatureFlags]
}
The mixin exposes a method named featureEnabled
.
if (this.featureEnabled('my-feature-flag')) {
// do something flag-ish
}
<div v-if="featureEnabled('my-feature-flag')">
<!-- Feature-related markup -->
</div>
Using a Global Mixin
If you want to expose featureEnabled
to all components within your application without them
having to explicitly use the mixin, you can. This introduces a bit of 'magic' into the application,
so be sure to document that this is what you're doing so that future developers aren't stumped
by where this random method is coming from. You'll also want to disable some normally helpful
warnings that will annoy you if you use this as a global mixin.
Vue CLI
In main.js:
import { UsesFeatureFlags } from '@nuskin/ns-feature-flags-vue';
Vue.mixin(UsesFeatureFlags);
UsesFeatureFlags.logMissingAppComponent = false;
Nuxt
Create a file named plugins/FeatureFlags.js
.
import { UsesFeatureFlags } from '@nuskin/ns-feature-flags-vue';
import Vue from 'vue';
Vue.mixin(UsesFeatureFlags);
UsesFeatureFlags.logMissingAppComponent = false;
Now, in your Nuxt configuration (nuxt.config.js), tell Nuxt about your plugin:
module.exports = {
//... other stuff ...
plugins: [
// ... other plugins ...
'~/plugins/FeatureFlags.js'
]
};
In an Application
By themselves, components will always see flags as disabled. To control how flags are loaded in a consistent
way across the entire application, the root node of the application should be wrapped in a feature-flag-app
(exact naming to TBD) component.
If you don't know where the 'root element' is in your app, look at the Vue CLI and Nuxt instructions below, or look for a
v-app
tag in your application.
feature-flag-app
provides two things: A single source of truth for where and how flags should
be loaded, as well as deferring rendering of the application until feature flags have
been loaded (which greatly simplifies the use of feature flags).
feature-flag-app
contains optional configuration that controls from where it loads
the flags. Note that changing any of these
properties will cause all children of feature-flag-app to re-render, and thus should not be done
outside of a development environment.
Prop | Note -----|------ env | Environment from which to load flags. If not set, infers the environment from the current page URL. Valid names are 'dev', 'test', 'stage', 'local'. Can also be an explicit URL to the feature flag service. user | User ID for user-specific feature flags
<!-- Put this in the correct place for your project. See below. -->
<template>
<feature-flag-app>
<!-- Your App Here -->
</feature-flag-app>
</template>
<script>
import {FeatureFlagApp} from '@nuskin/ns-feature-flags-vue';
export default {
components: {
FeatureFlagApp
},
// Other Options
};
</script>
You can also specify a template to be rendered while the flags are loading using the loading
slot.
<feature-flag-app>
<my-app />
<template #loading>
<my-loading-indicator />
</template>
</feature-flag-app>
Usage in a component library
In a component library, you can safely use UsesFeatureFlags in any component. Do NOT register UsesFeatureFlags as a global mixin inside of a component library - mutating anything on the global Vue instance (Vue.use, Vue.mixin, etc) is highly discouraged in a component library.
When in a component library, do NOT include FeatureFlagApp in any production components. The point of FeatureFlagApp is to provide a single source of truth for what flags are loaded, so if a component library says one thing and the consuming application says another, you can get into lots of weird situations.
However, it is quite nice to be able to see feature flags turned on in your storybook. In order to do this, you can add a 'decorator' to your storybook which contains the FeatureFlagApp component.
In your Storybook preview.js (usually located in either .storybook/preview.js
or config/storybook/preview.js
), add
a decorator that will wrap your stories in the component.
Storybook 6
import { FeatureFlagApp } from '@nuskin/ns-feature-flags-vue';
export const decorators = [
(story) => ({
components: { story, FeatureFlagApp },
template: '<feature-flag-app><story /></feature-flag-app>'
})
];
Storybook 5
import { FeatureFlagApp } from '@nuskin/ns-feature-flags-vue';
import { addDecorator } from '@storybook/vue';
addDecorator(() => ({
components: { FeatureFlagApp },
template: '<feature-flag-app><story /></feature-flag-app>'
}));
Vue CLI
By default, Vue CLI components have an App.vue which bootstraps the rest of the application. You can add feature-flag-app
inside that component.
Nuxt
If you already have layouts in your Nuxt app (in the layouts
directory), you can add this component to the root of each of your layouts. If you do
not have a layouts directory, create one in the root of your application, and then create default.vue
:
<template>
<feature-flag-app>
<nuxt />
</feature-flag-app>
</template>
<script>
import {FeatureFlagApp} from '@nuskin/ns-feature-flags-vue';
export default {
components: { FeatureFlagApp }
}
</script>
This will apply the feature flag element to all of the pages in your nuxt app.
Unit Testing
To test code that's behind a feature flag, you can use the mocks
option on mount
(in @vue/test-utils)
or render
(in @testing-library/vue). Alternatively, you can use the testing
export in this library.
Vue Test Utils
import { mountWithFlags } from '@nuskin/ns-feature-flags-vue/testing';
test('some fancy new feature', () => {
const wrapper = mountWithFlags(MyComponent, {
flags: ['my-fancy-flag']
});
// Test your feature
});
Vue Testing Library
import { renderWithFlags } from '@nuskin/ns-feature-flags-vue/testing';
test('some fancy new feature', () => {
const { getByText } = renderWithFlags(MyComponent, {
flags: ['my-fancy-flag']
});
// Test your feature
});
Configuring Warnings
FeatureFlagApp and UsesFeatureFlags can emit helpful warnings when you're using them incorrectly.
- FeatureFlagApp.logMultipleInstances controls whether a warning is emitted if there are multiple FeatureFlagApp instances present in the same application.
- UsesFeatureFlags.logMissingAppComponent will log if UsesFeatureFlag is used without a FeatureFlagApp being present somewhere in its tree.
By default, these warnings are turned on. If you are in a Jest testing environment, they are turned off. You can also turn them off manually:
import { FeatureFlagApp, UsesFeatureFlags } from '@nuskin/ns-feature-flags-vue';
FeatureFlagApp.logMultipleInstances = false;
UsesFeatureFlags.logMissingAppComponent = false;
Legacy Docs
This section contains the documentation for deprecated plugin-based version of this library. It can still be used, but has a lot of sharp edges that are solved by the new mixin-and-root-component-based version.
Nuxt
Deprecated!
To use with Nuxt, install as described above, then add the following to your Nuxt configuration:
export default {
// ... other configuration
modules: [
'@nuskin/ns-feature-flags-vue/nuxt',
// ... other modules
]
}
Vue CLI
Deprecated!
There will be a Vue CLI plugin released soon. For now, add a file named src/plugins/ns-feature-flags.js
:
import FeatureFlagPlugin from '@nuskin/ns-feature-flags-vue';
Vue.use(FeatureFlagPlugin);
Then, import this plugin in src/main.js
file:
import 'plugins/ns-feature-flags.js';
Usage
Configuration
Deprecated!
You can configure the URL of the feature flag server:
Vue.use(FeatureFlagPlugin, { url: 'https://www.nuskin.com/my-fancy-flags' });
By default, the plugin uses the production feature flag server.
Consuming Flags
Deprecated!
The plugin registers a $features
property on all Vue components. The keys of this object are the feature flag names.
So, to check the value of a flag named 'foo':
if (this.$features.foo) {
// do something neat
}
Or:
<span v-if="$features.foo">Something Neat</span>
You can also use array indexing to get other styles of flag name, like Jira issue numbers:
if (this.$features['test-1234']) {
// do something neat
}
IMPORTANT
In order to consume feature flags outside of templates and computed properties, you MUST wait for the flags to load before checking them. Otherwise, your code may not see the correct value!
await this.$features.$waitForLoad();
For example, if you want to use flags in 'mounted':
async mounted() { await this.$features.$waitForLoad(); if (this.$features.my_flag) { // do something cool } }
Triggering Reloads
Deprecated!
You may wish to trigger a reload of the flags. You can do this by invoking $forceReload()
:
await this.$features.$forceReload();
this.$features.foo // will be the latest value from the server
The returned promise will resolve when the reload finishes.
To wait for any loads that are currently in progress, you can call $waitForLoad()
:
await this.$features.$waitForLoad();
this.$features.foo // will be the latest value from the server
Overriding Flag Values
To explicitly set a flag value, like in an integration test or local development, you can set a value in the query string or in session storage.
Both the query string and session storage values use the same key names. To enable flags,
the key is $enableFeatureFlags
; to disable them, it is $disableFeatureFlags
. Both of
these values are a comma-separated list of the flags you want to enable or disable.
Examples
To enable the flags 'foo' and 'bar' and disable the flag 'baz' in the query string,
add this to the URL: ?$enableFeatureFlags=foo,bar&$disableFeatureFlags=baz
.
To do the same in session storage, run the following command in your devtools console:
sessionStorage.setItem('$enableFeatureFlags', 'foo,bar');
sessionStorage.setItem('$disableFeatureFlags', 'baz');
Feature Flag Users
You can set a 'userId' to be sent to the Feature Flag Server. This ID allows you to enable flags for specific users. For example, to allow QA to test a feature before general release, you could enable the flag for a user named 'qa'. Then, when browsing the application, the QA just has to set the user.
There are four supported ways to set the feature flag user:
- When the Vue plugin is installed
- By calling
$setUser
- By setting a query parameter named
$featureFlagUser
- By setting a session storage value named
$featureFlagUser
At plugin installation
Vue.use(FeatureFlagPlugin, { user: 'qa' });
With $setUser
In a Vue component:
await this.$features.$setUser('qa');
this.$features.foo // will be the latest value for this user
Development
NPM Scripts
test
- Runs Jest unit testslint
- Runs ES Lintbuild
- Transpiles the library for use in Node environments.