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 staging
This commit is contained in:
parent
07496c0180
commit
8a43d76c8c
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:bug
|
||||||
|
ui: Fix text searching through upstream instances.
|
||||||
|
```
|
|
@ -1,25 +1,62 @@
|
||||||
---
|
|
||||||
class: ember
|
|
||||||
---
|
|
||||||
# DataCollection
|
# DataCollection
|
||||||
|
|
||||||
```hbs
|
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).
|
||||||
<DataCollection
|
|
||||||
@search={{''}}
|
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`).
|
||||||
@sort={{''}}
|
|
||||||
@filter={{hash
|
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.
|
||||||
searchproperties=(array)
|
|
||||||
}}
|
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.
|
||||||
@items={{array 'hi'}}
|
|
||||||
as |collection|>
|
```hbs preview-template
|
||||||
{{collection.items.length}}
|
<figure>
|
||||||
<collection.Collection>
|
<figcaption>Provide a widget to search with</figcaption>
|
||||||
Has Results
|
<input
|
||||||
</collection.Collection>
|
oninput={{action (mut this.term) value="target.value"}}
|
||||||
<collection.Empty>
|
type="text"
|
||||||
Is Empty
|
/>
|
||||||
</collection.Empty>
|
</figure>
|
||||||
</DataCollection>
|
|
||||||
|
<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
|
### Arguments
|
||||||
|
@ -30,6 +67,8 @@ as |collection|>
|
||||||
| `search` | `String` | '' | A search term to use for searching |
|
| `search` | `String` | '' | A search term to use for searching |
|
||||||
| `sort` | `String` | '' | A sort term to use for sorting on ('Name:asc') |
|
| `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 |
|
| `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
|
### Yields
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,12 @@ const asArray = function(arr) {
|
||||||
return Array.isArray(arr) ? arr : arr.toArray();
|
return Array.isArray(arr) ? arr : arr.toArray();
|
||||||
};
|
};
|
||||||
export default {
|
export default {
|
||||||
Name: (item, value) => item.Name,
|
Name: item => item.Name,
|
||||||
Node: (item, value) => item.Node,
|
Node: item => item.Node,
|
||||||
Service: (item, value) => item.ServiceName,
|
Service: item => item.ServiceName,
|
||||||
CheckID: (item, value) => item.CheckID || '',
|
CheckID: item => item.CheckID || '',
|
||||||
ID: (item, value) => item.Service.ID || '',
|
ID: item => item.Service.ID || '',
|
||||||
Notes: (item, value) => item.Notes,
|
Notes: item => item.Notes,
|
||||||
Output: (item, value) => item.Output,
|
Output: item => item.Output,
|
||||||
ServiceTags: (item, value) => asArray(item.ServiceTags || []),
|
ServiceTags: item => asArray(item.ServiceTags || []),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
export default {
|
export default {
|
||||||
Name: (item, value) => item.Name,
|
Name: item => item.Name,
|
||||||
Description: (item, value) => item.Description,
|
Description: item => item.Description,
|
||||||
Role: (item, value) => {
|
Role: item => {
|
||||||
const acls = item.ACLs || {};
|
const acls = item.ACLs || {};
|
||||||
return (acls.RoleDefaults || []).map(item => item.Name);
|
return (acls.RoleDefaults || []).map(item => item.Name);
|
||||||
},
|
},
|
||||||
Policy: (item, value) => {
|
Policy: item => {
|
||||||
const acls = item.ACLs || {};
|
const acls = item.ACLs || {};
|
||||||
return (acls.PolicyDefaults || []).map(item => item.Name);
|
return (acls.PolicyDefaults || []).map(item => item.Name);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
export default {
|
export default {
|
||||||
Name: (item, value) => item.Name,
|
Name: item => item.Name,
|
||||||
Description: (item, value) => item.Description,
|
Description: item => item.Description,
|
||||||
Policy: (item, value) => {
|
Policy: item => {
|
||||||
return (item.Policies || [])
|
return (item.Policies || [])
|
||||||
.map(item => item.Name)
|
.map(item => item.Name)
|
||||||
.concat((item.ServiceIdentities || []).map(item => item.ServiceName))
|
.concat((item.ServiceIdentities || []).map(item => item.ServiceName))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
export default {
|
export default {
|
||||||
Name: item => item.Name,
|
Name: item => item.Name,
|
||||||
Tags: item => item.Service.Tags || [],
|
Tags: item => item.Service.Tags || [],
|
||||||
ID: (item, value) => item.Service.ID || '',
|
ID: item => item.Service.ID || '',
|
||||||
Address: item => item.Address || '',
|
Address: item => item.Address || '',
|
||||||
Port: item => (item.Service.Port || '').toString(),
|
Port: item => (item.Service.Port || '').toString(),
|
||||||
['Service.Meta']: item =>
|
['Service.Meta']: item =>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
export default {
|
export default {
|
||||||
Name: (item, value) => item.Name,
|
Name: item => item.Name,
|
||||||
Description: (item, value) => item.Description,
|
Description: item => item.Description,
|
||||||
AccessorID: (item, value) => item.AccessorID,
|
AccessorID: item => item.AccessorID,
|
||||||
Role: (item, value) => (item.Roles || []).map(item => item.Name),
|
Role: item => (item.Roles || []).map(item => item.Name),
|
||||||
Policy: (item, value) => {
|
Policy: item => {
|
||||||
return (item.Policies || [])
|
return (item.Policies || [])
|
||||||
.map(item => item.Name)
|
.map(item => item.Name)
|
||||||
.concat((item.ServiceIdentities || []).map(item => item.ServiceName))
|
.concat((item.ServiceIdentities || []).map(item => item.ServiceName))
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
export default {
|
export default {
|
||||||
DestinationName: (item, value) =>
|
DestinationName: (item, value) => item.DestinationName,
|
||||||
item.DestinationName.toLowerCase().indexOf(value.toLowerCase()) !== -1,
|
LocalBindAddress: (item, value) => item.LocalBindAddress,
|
||||||
LocalBindAddress: (item, value) =>
|
LocalBindPort: (item, value) => item.LocalBindPort.toString(),
|
||||||
item.LocalBindAddress.toLowerCase().indexOf(value.toLowerCase()) !== -1,
|
|
||||||
LocalBindPort: (item, value) =>
|
|
||||||
item.LocalBindPort.toString()
|
|
||||||
.toLowerCase()
|
|
||||||
.indexOf(value.toLowerCase()) !== -1,
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -66,6 +66,19 @@
|
||||||
ul {
|
ul {
|
||||||
margin-bottom: 0;
|
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 {
|
// &__snippets__tabs__button {
|
||||||
// display: none;
|
// 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';
|
'use strict';
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
|
|
|
@ -1,42 +1,58 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
const Funnel = require('broccoli-funnel');
|
const Funnel = require('broccoli-funnel');
|
||||||
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
|
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
|
||||||
|
|
||||||
module.exports = function(defaults) {
|
module.exports = function(defaults) {
|
||||||
|
// available environments
|
||||||
|
// ['production', 'development', 'staging', 'test'];
|
||||||
|
|
||||||
const env = EmberApp.env();
|
const env = EmberApp.env();
|
||||||
const prodlike = ['production', 'staging'];
|
const prodlike = ['production', 'staging'];
|
||||||
const isProd = env === 'production';
|
|
||||||
const isProdLike = prodlike.indexOf(env) > -1;
|
|
||||||
const sourcemaps = !isProd;
|
|
||||||
const trees = {};
|
const trees = {};
|
||||||
const addons = {};
|
const addons = {};
|
||||||
const outputPaths = {};
|
const outputPaths = {};
|
||||||
if (isProdLike) {
|
let excludeFiles = [];
|
||||||
// exclude any component/pageobject.js files from production-like environments
|
|
||||||
trees.app = new Funnel('app', {
|
const sourcemaps = !['production'].includes(env);
|
||||||
exclude: [
|
|
||||||
'components/**/pageobject.js',
|
// setup up different build configuration depending on environment
|
||||||
'components/**/*.test-support.js',
|
if(!['test'].includes(env)) {
|
||||||
'components/**/*.test.js',
|
// exclude any component/pageobject.js files from anything but test
|
||||||
// exclude our debug initializer, route and template
|
excludeFiles = excludeFiles.concat([
|
||||||
'instance-initializers/debug.js',
|
'components/**/pageobject.js',
|
||||||
'templates/debug.hbs',
|
'components/**/*.test-support.js',
|
||||||
'components/debug/**/*.*'
|
'components/**/*.test.js',
|
||||||
],
|
])
|
||||||
});
|
}
|
||||||
// exclude any debug like addons from production-like environments
|
|
||||||
|
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 = [
|
addons.blacklist = [
|
||||||
// exclude docfy
|
// exclude docfy
|
||||||
'@docfy/ember'
|
'@docfy/ember'
|
||||||
];
|
];
|
||||||
} else {
|
} 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 = {
|
outputPaths.app = {
|
||||||
css: {
|
css: {
|
||||||
'debug': '/assets/debug.css'
|
'debug': '/assets/debug.css'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let app = new EmberApp(
|
//
|
||||||
|
|
||||||
|
trees.app = new Funnel('app', {
|
||||||
|
exclude: excludeFiles
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new EmberApp(
|
||||||
Object.assign({}, defaults, {
|
Object.assign({}, defaults, {
|
||||||
productionEnvironments: prodlike,
|
productionEnvironments: prodlike,
|
||||||
}),
|
}),
|
||||||
|
@ -138,6 +154,5 @@ module.exports = function(defaults) {
|
||||||
app.import('vendor/init.js', {
|
app.import('vendor/init.js', {
|
||||||
outputFile: 'assets/init.js',
|
outputFile: 'assets/init.js',
|
||||||
});
|
});
|
||||||
let tree = app.toTree();
|
return app.toTree();
|
||||||
return tree;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -42,8 +42,7 @@ module.exports = {
|
||||||
treeFor: function(name) {
|
treeFor: function(name) {
|
||||||
const tree = this._super.treeFor.apply(this, arguments);
|
const tree = this._super.treeFor.apply(this, arguments);
|
||||||
if (name === 'app') {
|
if (name === 'app') {
|
||||||
const prodlike = ['production', 'staging'];
|
if (['production', 'test'].includes(process.env.EMBER_ENV)) {
|
||||||
if (prodlike.includes(process.env.EMBER_ENV)) {
|
|
||||||
return mergeTrees([tree, writeFile('components/debug/navigation/index.hbs', '')]);
|
return mergeTrees([tree, writeFile('components/debug/navigation/index.hbs', '')]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ module.exports = ({ appName, environment, rootURL, config }) => `
|
||||||
<link rel="apple-touch-icon" href="${rootURL}assets/apple-touch-icon.png">
|
<link rel="apple-touch-icon" href="${rootURL}assets/apple-touch-icon.png">
|
||||||
<link integrity="" rel="stylesheet" href="${rootURL}assets/vendor.css">
|
<link integrity="" rel="stylesheet" href="${rootURL}assets/vendor.css">
|
||||||
<link integrity="" rel="stylesheet" href="${rootURL}assets/${
|
<link integrity="" rel="stylesheet" href="${rootURL}assets/${
|
||||||
environment === 'development' ? 'debug' : appName
|
!['test', 'production'].includes(environment) ? 'debug' : appName
|
||||||
}.css">
|
}.css">
|
||||||
${
|
${
|
||||||
environment === 'test' ? `<link rel="stylesheet" href="${rootURL}assets/test-support.css">` : ``
|
environment === 'test' ? `<link rel="stylesheet" href="${rootURL}assets/test-support.css">` : ``
|
||||||
|
|
Loading…
Reference in New Issue