From 783bb2b598e0048149d96fbb27cd0574f59b0ef8 Mon Sep 17 00:00:00 2001 From: Matthew Irish Date: Thu, 1 Aug 2019 16:04:59 -0500 Subject: [PATCH] UI kmip acceptance (#7129) * change the story blueprint so that using -ir will generate a story in the expected place for in-repo addons and engines * update gen-story-md script to do output md to stories folder inside of in-repo addons and engines * update storybook config to look for story files in /lib * add story for list-view component * add list view page object * add kmip page objects and tests * update storybook commands in the README * split tests up more * update var name in storybook --- ui/.storybook/config.js | 6 +- ui/README.md | 12 +- .../stories/__name__.stories.js | 4 +- ui/blueprints/story/index.js | 19 ++ ui/lib/core/addon/components/list-view.js | 39 ++- .../templates/components/empty-state.hbs | 2 +- .../addon/templates/components/list-item.hbs | 10 +- .../addon/templates/components/list-view.hbs | 5 +- .../templates/components/navigate-input.hbs | 1 + .../templates/components/toolbar-link.hbs | 1 + ui/lib/core/stories/list-view.md | 32 +++ ui/lib/core/stories/list-view.stories.js | 58 +++++ .../components/edit-form-kmip-role.hbs | 2 +- .../components/header-credentials.hbs | 4 +- .../templates/components/header-scope.hbs | 4 +- ui/lib/kmip/addon/templates/configuration.hbs | 1 + .../addon/templates/credentials/index.hbs | 1 + .../kmip/addon/templates/credentials/show.hbs | 1 + ui/lib/kmip/addon/templates/role.hbs | 1 + ui/lib/kmip/addon/templates/scope/roles.hbs | 1 + ui/lib/kmip/addon/templates/scopes/index.hbs | 27 +- ui/scripts/gen-story-md.js | 9 +- ui/tests/acceptance/enterprise-kmip-test.js | 233 ++++++++++++++++++ ui/tests/pages/components/list-view.js | 23 ++ .../pages/secrets/backend/kmip/credentials.js | 15 ++ ui/tests/pages/secrets/backend/kmip/roles.js | 14 ++ ui/tests/pages/secrets/backend/kmip/scopes.js | 14 ++ 27 files changed, 496 insertions(+), 43 deletions(-) rename ui/blueprints/story/files/{ => __path__}/stories/__name__.stories.js (80%) create mode 100644 ui/lib/core/stories/list-view.md create mode 100644 ui/lib/core/stories/list-view.stories.js create mode 100644 ui/tests/acceptance/enterprise-kmip-test.js create mode 100644 ui/tests/pages/components/list-view.js create mode 100644 ui/tests/pages/secrets/backend/kmip/credentials.js create mode 100644 ui/tests/pages/secrets/backend/kmip/roles.js create mode 100644 ui/tests/pages/secrets/backend/kmip/scopes.js diff --git a/ui/.storybook/config.js b/ui/.storybook/config.js index be3be01e83..d395d6230b 100644 --- a/ui/.storybook/config.js +++ b/ui/.storybook/config.js @@ -5,8 +5,10 @@ import { assign } from '@ember/polyfills'; function loadStories() { // automatically import all files ending in *.stories.js - const req = require.context('../stories/', true, /.stories.js$/); - req.keys().forEach(filename => req(filename)); + const appStories = require.context('../stories', true, /.stories.js$/); + const addonAndRepoStories = require.context('../lib', true, /.stories.js$/); + appStories.keys().forEach(filename => appStories(filename)); + addonAndRepoStories.keys().forEach(filename => addonAndRepoStories(filename)); } addParameters({ diff --git a/ui/README.md b/ui/README.md index 10cab89ade..782a6854bf 100644 --- a/ui/README.md +++ b/ui/README.md @@ -111,14 +111,16 @@ The Vault UI uses Storybook to catalog all of its components. Below are details ### Storybook Commands at a Glance | Command | Description | -| ------------------------------------------ | ------------------------- | -| `yarn storybook` | run storybook | -| `ember generate story [name-of-component]` | generate a new story | -| `yarn gen-story-md [name-of-component]` | update a story notes file | +| ------------------------------------------------------------------------ | ---------------------------------------------------------- | +| `yarn storybook` | run storybook | +| `ember generate story [name-of-component]` | generate a new story | +| `ember generate story [name-of-component] -ir [name-of-engine-or-addon]` | generate a new story in the specified engine or addon | +| `yarn gen-story-md [name-of-component]` | update a story notes file | +| `yarn gen-story-md [name-of-component] [name-of-engine-or-addon]` | update a story notes file in the specified engine or addon | ### Writing Stories -Each component in `vault/ui/app/components` should have a corresponding `[component-name].stories.js` and `[component-name].md` files within `vault/ui/stories`. +Each component in `vault/ui/app/components` should have a corresponding `[component-name].stories.js` and `[component-name].md` files within `vault/ui/stories`. Components in the `core` addon located at `vault/ui/lib/core/addon/components` have corresponding stories and markdown files in `vault/ui/lib/core/stories`. #### Adding a new story diff --git a/ui/blueprints/story/files/stories/__name__.stories.js b/ui/blueprints/story/files/__path__/stories/__name__.stories.js similarity index 80% rename from ui/blueprints/story/files/stories/__name__.stories.js rename to ui/blueprints/story/files/__path__/stories/__name__.stories.js index 6b251549c5..3d434c8418 100644 --- a/ui/blueprints/story/files/stories/__name__.stories.js +++ b/ui/blueprints/story/files/__path__/stories/__name__.stories.js @@ -7,8 +7,8 @@ storiesOf('<%= classifiedModuleName %>/', module) .addParameters({ options: { showPanel: true } }) .add(`<%= classifiedModuleName %>`, () => ({ template: hbs` -
<%= header %>
- <<%= classifiedModuleName %>/> +
<%= header %>
+ <<%= classifiedModuleName %>/> `, context: {}, }), diff --git a/ui/blueprints/story/index.js b/ui/blueprints/story/index.js index cbdbbc42c4..497e4fd54e 100644 --- a/ui/blueprints/story/index.js +++ b/ui/blueprints/story/index.js @@ -1,12 +1,31 @@ 'use strict'; const getPathOption = require('ember-cli-get-component-path-option'); const stringUtil = require('ember-cli-string-utils'); +const path = require('path'); + +function findAddonByName(addonOrProject, name) { + let addon = addonOrProject.addons.find(addon => addon.name === name); + + if (addon) { + return addon; + } + + return addonOrProject.addons.find(addon => findAddonByName(addon, name)); +} module.exports = { description: 'generates a story for storybook', fileMapTokens: function() { + let { project } = this; return { + __path__: function(options) { + if (options.inRepoAddon) { + let addon = findAddonByName(project, options.inRepoAddon); + return path.relative(project.root, addon.root); + } + return path.relative(project.root, project.root); + }, __markdownname__: function(options) { return options.dasherizedModuleName; }, diff --git a/ui/lib/core/addon/components/list-view.js b/ui/lib/core/addon/components/list-view.js index 37882a35cd..84daddf50c 100644 --- a/ui/lib/core/addon/components/list-view.js +++ b/ui/lib/core/addon/components/list-view.js @@ -3,19 +3,44 @@ import { computed } from '@ember/object'; import { pluralize } from 'ember-inflector'; import layout from '../templates/components/list-view'; +/** + * @module ListView + * `ListView` components are used in conjuction with `ListItem` for rendering a list. + * + * @example + * ```js + * + * {{#if list.empty}} + * + * {{else}} + *
+ * {{list.item.id}} + *
+ * {{/if}} + *
+ * ``` + * + * @param items=null {Array} - An array of items to render as a list + * @param [itemNoun=null {String}] - A noun to use in the empty state of message and title. + * @param [message=null {String}] - The message to display within the banner. + * @yields Object with `item` that is the current item in the loop. + * @yields If there are no objects in items, then `empty` will be yielded - this is an instance of + * the EmptyState component. + * @yields If `item` or `empty` isn't present on the object, the component can still yield a block - this is + * useful for showing states where there are items but there may be a filter applied that returns an + * empty set. + * + */ export default Component.extend({ layout, tagName: '', items: null, itemNoun: 'item', - // the dasherized name of a component to render - // in the EmptyState component if there are no items in items.length - emptyActions: '', - showPagination: computed('paginationRouteName', 'items.meta{lastPage,total}', function() { - return this.paginationRouteName && this.items.meta.lastPage > 1 && this.items.meta.total > 0; - }), - paginationRouteName: '', + showPagination: computed('paginationRouteName', 'items.meta{lastPage,total}', function() { + let meta = this.items.meta; + return this.paginationRouteName && meta && meta.lastPage > 1 && meta.total > 0; + }), emptyTitle: computed('itemNoun', function() { let items = pluralize(this.get('itemNoun')); diff --git a/ui/lib/core/addon/templates/components/empty-state.hbs b/ui/lib/core/addon/templates/components/empty-state.hbs index 457909fe72..0fb2a18992 100644 --- a/ui/lib/core/addon/templates/components/empty-state.hbs +++ b/ui/lib/core/addon/templates/components/empty-state.hbs @@ -1,4 +1,4 @@ -
+

{{title}} diff --git a/ui/lib/core/addon/templates/components/list-item.hbs b/ui/lib/core/addon/templates/components/list-item.hbs index 08e224b462..d3cd2807b7 100644 --- a/ui/lib/core/addon/templates/components/list-item.hbs +++ b/ui/lib/core/addon/templates/components/list-item.hbs @@ -1,15 +1,15 @@ {{#if componentName}} {{component componentName item=item}} {{else if linkParams}} - +
-
+
{{#link-to params=linkParams class="has-text-weight-semibold has-text-black is-display-flex is-flex-1 is-no-underline"}} {{yield (hash content=(component "list-item/content"))}} {{/link-to}}
-
+
{{yield (hash callMethod=callMethod menu=(component "list-item/popup-menu" item=item hasMenu=hasMenu))}}
@@ -18,11 +18,11 @@ {{else}}
-
+
{{yield (hash content=(component "list-item/content"))}}
-
+
{{yield (hash callMethod=callMethod menu=(component "list-item/popup-menu" item=item hasMenu=hasMenu))}}
diff --git a/ui/lib/core/addon/templates/components/list-view.hbs b/ui/lib/core/addon/templates/components/list-view.hbs index 76d779ec31..a24c01576c 100644 --- a/ui/lib/core/addon/templates/components/list-view.hbs +++ b/ui/lib/core/addon/templates/components/list-view.hbs @@ -4,8 +4,8 @@ items.length ) }} -
- {{#each items as |item|}} +
+ {{#each (or items.content items) as |item|}} {{yield (hash item=item)}} {{else}} {{yield}} @@ -15,6 +15,7 @@ @page={{items.meta.currentPage}} @lastPage={{items.meta.lastPage}} @link={{@paginationRouteName}} + data-test-list-view-pagination /> {{/if}}
diff --git a/ui/lib/core/addon/templates/components/navigate-input.hbs b/ui/lib/core/addon/templates/components/navigate-input.hbs index ffc5cfb7ca..b5493c948c 100644 --- a/ui/lib/core/addon/templates/components/navigate-input.hbs +++ b/ui/lib/core/addon/templates/components/navigate-input.hbs @@ -6,6 +6,7 @@ value={{@filter}} placeholder={{ or @placeholder "Filter keys" }} type="text" + data-test-comoponent="navigate-input" oninput={{action "handleInput" value="target.value"}} onkeyup={{action "handleKeyUp" }} diff --git a/ui/lib/core/addon/templates/components/toolbar-link.hbs b/ui/lib/core/addon/templates/components/toolbar-link.hbs index d3a98a0546..196d79cacd 100644 --- a/ui/lib/core/addon/templates/components/toolbar-link.hbs +++ b/ui/lib/core/addon/templates/components/toolbar-link.hbs @@ -13,6 +13,7 @@ data-test-policy-create-link={{data-test-policy-create-link}} data-test-policy-edit-toggle={{data-test-policy-edit-toggle}} data-test-secret-backend-configure={{data-test-secret-backend-configure}} + ...attributes > {{yield}} diff --git a/ui/lib/core/stories/list-view.md b/ui/lib/core/stories/list-view.md new file mode 100644 index 0000000000..d8d89ad427 --- /dev/null +++ b/ui/lib/core/stories/list-view.md @@ -0,0 +1,32 @@ + + +## ListView +`ListView` components are used in conjuction with `ListItem` for rendering a list. + + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| items | Array | | An array of items to render as a list | +| [itemNoun] | String | | A noun to use in the empty state of message and title. | +| [message] | String | | The message to display within the banner. | + +**Example** + +```js + + {{#if list.empty}} + + {{else}} +
+ {{list.item.id}} +
+ {{/if}} +
+``` + +**See** + +- [Uses of ListView](https://github.com/hashicorp/vault/search?l=Handlebars&q=ListView+OR+list-view) +- [ListView Source Code](https://github.com/hashicorp/vault/blob/master/ui/lib/core/addon/components/list-view.js) + +--- diff --git a/ui/lib/core/stories/list-view.stories.js b/ui/lib/core/stories/list-view.stories.js new file mode 100644 index 0000000000..c8192ac0ee --- /dev/null +++ b/ui/lib/core/stories/list-view.stories.js @@ -0,0 +1,58 @@ +/* eslint-disable import/extensions */ +import hbs from 'htmlbars-inline-precompile'; +import { storiesOf } from '@storybook/ember'; +import { withKnobs, select } from '@storybook/addon-knobs'; +import notes from './list-view.md'; + +import ArrayProxy from '@ember/array/proxy'; + +let filtered = ArrayProxy.create({ content: [] }); +filtered.set('meta', { + lastPage: 1, + currentPage: 1, + total: 100, +}); + +let paginated = ArrayProxy.create({ + content: [{ id: 'middle' }, { id: 'of' }, { id: 'the' }, { id: 'list' }], +}); +paginated.set('meta', { + lastPage: 10, + currentPage: 4, + total: 100, +}); + +let options = { + list: [{ id: 'one' }, { id: 'two' }], + empty: [], + filtered, + paginated, +}; + +storiesOf('ListView/', module) + .addParameters({ options: { showPanel: true } }) + .addDecorator(withKnobs()) + .add( + `ListView`, + () => ({ + template: hbs` +
{{title}}
+ + {{#if list.empty}} + + {{else if list.item}} +
+ {{list.item.id}} +
+ {{else}} +
There aren't any items in this filter
+ {{/if}} +
+ `, + context: { + title: 'ListView', + items: select('items', options, options['list']), + }, + }), + { notes } + ); diff --git a/ui/lib/kmip/addon/templates/components/edit-form-kmip-role.hbs b/ui/lib/kmip/addon/templates/components/edit-form-kmip-role.hbs index a73cdf2c9e..b26a0c561d 100644 --- a/ui/lib/kmip/addon/templates/components/edit-form-kmip-role.hbs +++ b/ui/lib/kmip/addon/templates/components/edit-form-kmip-role.hbs @@ -98,7 +98,7 @@
{{#if cancelLinkParams}}
- {{#link-to params=cancelLinkParams class="button"}} + {{#link-to params=cancelLinkParams class="button" data-test-edit-form-cancel="true"}} Cancel {{/link-to}}
diff --git a/ui/lib/kmip/addon/templates/components/header-credentials.hbs b/ui/lib/kmip/addon/templates/components/header-credentials.hbs index daf73e9872..b103c266c6 100644 --- a/ui/lib/kmip/addon/templates/components/header-credentials.hbs +++ b/ui/lib/kmip/addon/templates/components/header-credentials.hbs @@ -12,12 +12,12 @@