mirror of https://github.com/hashicorp/consul
ui: Fix text search for upstream instances (#10151)
* ui: Fix text search for upstream instances * Clean up predicates for other model types * Add some docs around DataCollection and searching * Enable UI Engineering Docs for our preview sites * Use debug CSS in dev and stagingpull/10182/head
parent
2d6f0d1854
commit
9ebc9561d9
|
@ -0,0 +1,3 @@
|
|||
```release-note:bug
|
||||
ui: Fix text searching through upstream instances.
|
||||
```
|
|
@ -1,25 +1,62 @@
|
|||
---
|
||||
class: ember
|
||||
---
|
||||
# DataCollection
|
||||
|
||||
```hbs
|
||||
<DataCollection
|
||||
@search={{''}}
|
||||
@sort={{''}}
|
||||
@filter={{hash
|
||||
searchproperties=(array)
|
||||
}}
|
||||
@items={{array 'hi'}}
|
||||
as |collection|>
|
||||
{{collection.items.length}}
|
||||
<collection.Collection>
|
||||
Has Results
|
||||
</collection.Collection>
|
||||
<collection.Empty>
|
||||
Is Empty
|
||||
</collection.Empty>
|
||||
</DataCollection>
|
||||
The DataCollection component is the component we use for searching, filtering and sorting. It does not dictate any rendering of your data, but it does provide two different states/contextual components for rendering: when there are some results, when there are no results and also (by not using the contextual components) when the amount of results does not matter to what you want to render (say a count of number of results).
|
||||
|
||||
The component should be configured with the `@type` attribute, the component will use this to lookup 'specifications' for searching, sorting and filtering in the respective `app/search/predicates`, `app/sort/comparators` and `app/filter/predicates` folders. These specifications define _what_ is searched when you search a list of objects (for example searching Name could mean searching `model.Name` whereas searching Role could mean searching through an array of `item.RoleDefaults`).
|
||||
|
||||
The component can also be further configured by specifications for _how_ to search, for example we currently only use `Exact` searching (pretty much an `indexOf` search), but we also have `fuzzy` and `regexp` searching methods available (but disabled until needed). These search 'methods' will use the above specifications again to define _what_ to search. Searching methods (the _how_) are in `utils/search/{exact,fuzzy,regexp}.js` and any new ones should implement at least a single `search` method.
|
||||
|
||||
Lastly, a `SearchService` in `services/search.js` configures what is available for _what_ to search and _how_ to search, so if you need to add new models or search methods, that is where to look.
|
||||
|
||||
```hbs preview-template
|
||||
<figure>
|
||||
<figcaption>Provide a widget to search with</figcaption>
|
||||
<input
|
||||
oninput={{action (mut this.term) value="target.value"}}
|
||||
type="text"
|
||||
/>
|
||||
</figure>
|
||||
|
||||
<figure>
|
||||
<figcaption>Get some data to search on</figcaption>
|
||||
<DataSource @src="/nspace/dc-1/services" as |source|>
|
||||
|
||||
<figure>
|
||||
<figcaption>and show the complete set of data</figcaption>
|
||||
{{#each source.data as |item|}}
|
||||
{{item.Name}},
|
||||
{{/each}}
|
||||
</figure>
|
||||
|
||||
<hr />
|
||||
|
||||
<figure>
|
||||
<figcaption>Use the component</figcaption>
|
||||
|
||||
<DataCollection
|
||||
@type="service"
|
||||
@search={{this.term}}
|
||||
@sort={{'Name:asc'}}
|
||||
@filter={{hash
|
||||
searchproperties=(array 'Name')
|
||||
}}
|
||||
@items={{source.data}}
|
||||
as |collection|>
|
||||
Has {{collection.items.length}} results:<br />
|
||||
<collection.Collection>
|
||||
{{#each collection.items as |item|}}
|
||||
{{item.Name}},
|
||||
{{/each}}
|
||||
</collection.Collection>
|
||||
<collection.Empty>
|
||||
Is Empty
|
||||
</collection.Empty>
|
||||
</DataCollection>
|
||||
|
||||
</figure>
|
||||
|
||||
</DataSource>
|
||||
</figure>
|
||||
```
|
||||
|
||||
### Arguments
|
||||
|
@ -30,6 +67,8 @@ as |collection|>
|
|||
| `search` | `String` | '' | A search term to use for searching |
|
||||
| `sort` | `String` | '' | A sort term to use for sorting on ('Name:asc') |
|
||||
| `filter` | `Object` | | An object whose properties are the properties/values to filter on |
|
||||
| `type` | `String` | '' | The name of the specification describing what to search, filter, sort |
|
||||
| `searchable` | `String` | `exact` | The name of the method to use for searching |
|
||||
|
||||
### Yields
|
||||
|
||||
|
|
|
@ -2,12 +2,12 @@ const asArray = function(arr) {
|
|||
return Array.isArray(arr) ? arr : arr.toArray();
|
||||
};
|
||||
export default {
|
||||
Name: (item, value) => item.Name,
|
||||
Node: (item, value) => item.Node,
|
||||
Service: (item, value) => item.ServiceName,
|
||||
CheckID: (item, value) => item.CheckID || '',
|
||||
ID: (item, value) => item.Service.ID || '',
|
||||
Notes: (item, value) => item.Notes,
|
||||
Output: (item, value) => item.Output,
|
||||
ServiceTags: (item, value) => asArray(item.ServiceTags || []),
|
||||
Name: item => item.Name,
|
||||
Node: item => item.Node,
|
||||
Service: item => item.ServiceName,
|
||||
CheckID: item => item.CheckID || '',
|
||||
ID: item => item.Service.ID || '',
|
||||
Notes: item => item.Notes,
|
||||
Output: item => item.Output,
|
||||
ServiceTags: item => asArray(item.ServiceTags || []),
|
||||
};
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
export default {
|
||||
Name: (item, value) => item.Name,
|
||||
Description: (item, value) => item.Description,
|
||||
Role: (item, value) => {
|
||||
Name: item => item.Name,
|
||||
Description: item => item.Description,
|
||||
Role: item => {
|
||||
const acls = item.ACLs || {};
|
||||
return (acls.RoleDefaults || []).map(item => item.Name);
|
||||
},
|
||||
Policy: (item, value) => {
|
||||
Policy: item => {
|
||||
const acls = item.ACLs || {};
|
||||
return (acls.PolicyDefaults || []).map(item => item.Name);
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export default {
|
||||
Name: (item, value) => item.Name,
|
||||
Description: (item, value) => item.Description,
|
||||
Policy: (item, value) => {
|
||||
Name: item => item.Name,
|
||||
Description: item => item.Description,
|
||||
Policy: item => {
|
||||
return (item.Policies || [])
|
||||
.map(item => item.Name)
|
||||
.concat((item.ServiceIdentities || []).map(item => item.ServiceName))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export default {
|
||||
Name: item => item.Name,
|
||||
Tags: item => item.Service.Tags || [],
|
||||
ID: (item, value) => item.Service.ID || '',
|
||||
ID: item => item.Service.ID || '',
|
||||
Address: item => item.Address || '',
|
||||
Port: item => (item.Service.Port || '').toString(),
|
||||
['Service.Meta']: item =>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
export default {
|
||||
Name: (item, value) => item.Name,
|
||||
Description: (item, value) => item.Description,
|
||||
AccessorID: (item, value) => item.AccessorID,
|
||||
Role: (item, value) => (item.Roles || []).map(item => item.Name),
|
||||
Policy: (item, value) => {
|
||||
Name: item => item.Name,
|
||||
Description: item => item.Description,
|
||||
AccessorID: item => item.AccessorID,
|
||||
Role: item => (item.Roles || []).map(item => item.Name),
|
||||
Policy: item => {
|
||||
return (item.Policies || [])
|
||||
.map(item => item.Name)
|
||||
.concat((item.ServiceIdentities || []).map(item => item.ServiceName))
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
export default {
|
||||
DestinationName: (item, value) =>
|
||||
item.DestinationName.toLowerCase().indexOf(value.toLowerCase()) !== -1,
|
||||
LocalBindAddress: (item, value) =>
|
||||
item.LocalBindAddress.toLowerCase().indexOf(value.toLowerCase()) !== -1,
|
||||
LocalBindPort: (item, value) =>
|
||||
item.LocalBindPort.toString()
|
||||
.toLowerCase()
|
||||
.indexOf(value.toLowerCase()) !== -1,
|
||||
DestinationName: (item, value) => item.DestinationName,
|
||||
LocalBindAddress: (item, value) => item.LocalBindAddress,
|
||||
LocalBindPort: (item, value) => item.LocalBindPort.toString(),
|
||||
};
|
||||
|
|
|
@ -59,6 +59,19 @@
|
|||
ul {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
figure {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
figcaption {
|
||||
margin-bottom: .5rem;
|
||||
color: var(--gray-400);
|
||||
font-style: italic;
|
||||
}
|
||||
figure > [type=text] {
|
||||
border: 1px solid var(--gray-999);
|
||||
width: 100%;
|
||||
padding: .5rem;
|
||||
}
|
||||
}
|
||||
// &__snippets__tabs__button {
|
||||
// display: none;
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
// All of the configuration here is shared between buildtime and runtime and
|
||||
// is therefore added to ember's <meta> tag in the actual app, if the
|
||||
// configuration is for buildtime only you should probably just use
|
||||
// ember-cli-build to prevent values being outputted in the meta tag
|
||||
'use strict';
|
||||
const path = require('path');
|
||||
const utils = require('./utils');
|
||||
|
|
|
@ -1,42 +1,58 @@
|
|||
'use strict';
|
||||
const Funnel = require('broccoli-funnel');
|
||||
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
|
||||
|
||||
module.exports = function(defaults) {
|
||||
// available environments
|
||||
// ['production', 'development', 'staging', 'test'];
|
||||
|
||||
const env = EmberApp.env();
|
||||
const prodlike = ['production', 'staging'];
|
||||
const isProd = env === 'production';
|
||||
const isProdLike = prodlike.indexOf(env) > -1;
|
||||
const sourcemaps = !isProd;
|
||||
|
||||
const trees = {};
|
||||
const addons = {};
|
||||
const outputPaths = {};
|
||||
if (isProdLike) {
|
||||
// exclude any component/pageobject.js files from production-like environments
|
||||
trees.app = new Funnel('app', {
|
||||
exclude: [
|
||||
'components/**/pageobject.js',
|
||||
'components/**/*.test-support.js',
|
||||
'components/**/*.test.js',
|
||||
// exclude our debug initializer, route and template
|
||||
'instance-initializers/debug.js',
|
||||
'templates/debug.hbs',
|
||||
'components/debug/**/*.*'
|
||||
],
|
||||
});
|
||||
// exclude any debug like addons from production-like environments
|
||||
let excludeFiles = [];
|
||||
|
||||
const sourcemaps = !['production'].includes(env);
|
||||
|
||||
// setup up different build configuration depending on environment
|
||||
if(!['test'].includes(env)) {
|
||||
// exclude any component/pageobject.js files from anything but test
|
||||
excludeFiles = excludeFiles.concat([
|
||||
'components/**/pageobject.js',
|
||||
'components/**/*.test-support.js',
|
||||
'components/**/*.test.js',
|
||||
])
|
||||
}
|
||||
|
||||
if(['test', 'production'].includes(env)) {
|
||||
// exclude our debug initializer, route and template
|
||||
excludeFiles = excludeFiles.concat([
|
||||
'instance-initializers/debug.js',
|
||||
'templates/debug.hbs',
|
||||
'components/debug/**/*.*'
|
||||
])
|
||||
// exclude any debug like addons from production or test environments
|
||||
addons.blacklist = [
|
||||
// exclude docfy
|
||||
'@docfy/ember'
|
||||
];
|
||||
} else {
|
||||
// add debug css is we are not in prodlike environments
|
||||
// add debug css is we are not in test or production environments
|
||||
outputPaths.app = {
|
||||
css: {
|
||||
'debug': '/assets/debug.css'
|
||||
}
|
||||
}
|
||||
}
|
||||
let app = new EmberApp(
|
||||
//
|
||||
|
||||
trees.app = new Funnel('app', {
|
||||
exclude: excludeFiles
|
||||
});
|
||||
|
||||
const app = new EmberApp(
|
||||
Object.assign({}, defaults, {
|
||||
productionEnvironments: prodlike,
|
||||
}),
|
||||
|
@ -138,6 +154,5 @@ module.exports = function(defaults) {
|
|||
app.import('vendor/init.js', {
|
||||
outputFile: 'assets/init.js',
|
||||
});
|
||||
let tree = app.toTree();
|
||||
return tree;
|
||||
return app.toTree();
|
||||
};
|
||||
|
|
|
@ -42,8 +42,7 @@ module.exports = {
|
|||
treeFor: function(name) {
|
||||
const tree = this._super.treeFor.apply(this, arguments);
|
||||
if (name === 'app') {
|
||||
const prodlike = ['production', 'staging'];
|
||||
if (prodlike.includes(process.env.EMBER_ENV)) {
|
||||
if (['production', 'test'].includes(process.env.EMBER_ENV)) {
|
||||
return mergeTrees([tree, writeFile('components/debug/navigation/index.hbs', '')]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ module.exports = ({ appName, environment, rootURL, config }) => `
|
|||
<link rel="icon" type="image/png" href="${rootURL}assets/favicon-16x16.png" sizes="16x16">
|
||||
<link integrity="" rel="stylesheet" href="${rootURL}assets/vendor.css">
|
||||
<link integrity="" rel="stylesheet" href="${rootURL}assets/${
|
||||
environment === 'development' ? 'debug' : appName
|
||||
!['test', 'production'].includes(environment) ? 'debug' : appName
|
||||
}.css">
|
||||
${
|
||||
environment === 'test' ? `<link rel="stylesheet" href="${rootURL}assets/test-support.css">` : ``
|
||||
|
|
Loading…
Reference in New Issue