1
0
Fork 0

Two ember versions locally, different ports

This commit is contained in:
Ava Wroten 2020-03-02 09:26:23 -06:00
parent d54f413f7d
commit 790515b024
63 changed files with 24414 additions and 0 deletions

View file

@ -0,0 +1,20 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.hbs]
insert_final_newline = false
[*.{diff,md}]
trim_trailing_whitespace = false

View file

@ -0,0 +1,15 @@
'use strict';
const { setEdition } = require('@ember/edition-utils');
setEdition('octane');
module.exports = {
/**
Ember CLI sends analytics information by default. The data is completely
anonymous, but there are times when you might want to disable this behavior.
Setting `disableAnalytics` to true will prevent any data from being sent.
*/
"disableAnalytics": false
}

View file

@ -0,0 +1,20 @@
# unconventional js
/blueprints/*/files/
/vendor/
# compiled output
/dist/
/tmp/
# dependencies
/bower_components/
/node_modules/
# misc
/coverage/
!.*
# ember-try
/.node_modules.ember-try/
/bower.json.ember-try
/package.json.ember-try

View file

@ -0,0 +1,58 @@
module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module'
},
plugins: [
'ember'
],
extends: [
'eslint:recommended',
'plugin:ember/recommended'
],
env: {
browser: true
},
rules: {
},
overrides: [
// node files
{
files: [
'.ember-cli.js',
'.eslintrc.js',
'.template-lintrc.js',
'ember-cli-build.js',
'testem.js',
'blueprints/*/index.js',
'config/**/*.js',
'lib/*/index.js',
'server/**/*.js'
],
excludedFiles: [
'app/**',
],
parserOptions: {
sourceType: 'script',
ecmaVersion: 2015
},
env: {
browser: false,
node: true
},
plugins: ['node'],
rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, {
// add your custom rules and overrides for node files here
// this can be removed once the following is fixed
// https://github.com/mysticatea/eslint-plugin-node/issues/77
'node/no-unpublished-require': 'off'
}),
extends: [
'plugin:node/recommended'
],
}
]
};

25
ember-ui-modern/.gitignore vendored Normal file
View file

@ -0,0 +1,25 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist/
/tmp/
# dependencies
/bower_components/
/node_modules/
# misc
/.env*
/.pnp*
/.sass-cache
/connect.lock
/coverage/
/libpeerconnection.log
/npm-debug.log*
/testem.log
/yarn-error.log
# ember-try
/.node_modules.ember-try/
/bower.json.ember-try
/package.json.ember-try

View file

@ -0,0 +1,8 @@
'use strict';
module.exports = {
extends: 'recommended',
rules: {
'no-implicit-this': true,
}
};

View file

@ -0,0 +1,27 @@
---
language: node_js
node_js:
- "8"
sudo: false
dist: trusty
addons:
chrome: stable
cache:
directories:
- $HOME/.npm
env:
global:
# See https://git.io/vdao3 for details.
- JOBS=1
before_install:
- npm config set spin false
script:
- npm run lint:hbs
- npm run lint:js
- npm test

View file

@ -0,0 +1,3 @@
{
"ignore_dirs": ["tmp", "dist"]
}

62
ember-ui-modern/README.md Normal file
View file

@ -0,0 +1,62 @@
# Sortable Recipes
This README outlines the details of collaborating on this Ember application.
A short introduction of this app could easily go here.
This example apps uses:
* Ember-Sortable
* Ember-Concurrency
## Prerequisites
You will need the following things properly installed on your computer.
* [Git](https://git-scm.com/)
* [Node.js](https://nodejs.org/) (with npm)
* [Ember CLI](https://ember-cli.com/)
* [Google Chrome](https://google.com/chrome/)
## Installation
* `git clone <repository-url>` this repository
* `cd sortable-recipes`
* `npm install`
## Running / Development
* `ember serve`
* Visit your app at [http://localhost:4200](http://localhost:4200).
* Visit your tests at [http://localhost:4200/tests](http://localhost:4200/tests).
### Code Generators
Make use of the many generators for code, try `ember help generate` for more details
### Running Tests
* `ember test`
* `ember test --server`
### Linting
* `npm run lint:hbs`
* `npm run lint:js`
* `npm run lint:js -- --fix`
### Building
* `ember build` (development)
* `ember build --environment production` (production)
### Deploying
Specify what it takes to deploy your app.
## Further Reading / Useful Links
* [ember.js](https://emberjs.com/)
* [ember-cli](https://ember-cli.com/)
* Development Browser Extensions
* [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi)
* [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/)

View file

@ -0,0 +1,5 @@
import JSONAPIAdapter from '@ember-data/adapter/json-api';
export default class ApplicationAdapter extends JSONAPIAdapter {
host = 'http://localhost:8000/';
}

View file

@ -0,0 +1,15 @@
import ApplicationAdapter from './application';
export default class MealAdapter extends ApplicationAdapter {
async findRecord(store, model, id) {
let result = await fetch(`${this.host}meal/${id}`);
return await result.json();
}
async queryRecord(store, model, query) {
if (query === 'random') {
let result = await fetch(`${this.host}meals/random`);
return await result.json();
}
}
}

View file

@ -0,0 +1,12 @@
import Application from '@ember/application';
import Resolver from './resolver';
import loadInitializers from 'ember-load-initializers';
import config from './config/environment';
export default class App extends Application {
modulePrefix = config.modulePrefix;
podModulePrefix = config.podModulePrefix;
Resolver = Resolver;
}
loadInitializers(App, config.modulePrefix);

View file

@ -0,0 +1,3 @@
<a href={{@href}} class="block mt-4 md:inline-block md:mt-0 text-teal-200 hover:text-white focus:text-white mr-4" data-test-id="header-nav-link">
{{yield}}
</a>

View file

@ -0,0 +1,42 @@
<nav class="flex items-center justify-between flex-wrap bg-teal-500 p-6">
<div class="flex items-center flex-shrink-0 text-white mr-6">
<LinkTo @route="index" class="flex border border-transparent hover:border-white focus:border-white rounded px-4 py-2" data-test-id="branding-link">
<svg class="fill-current h-8 w-8 mr-2" width="54" height="54" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M12 0c6.623 0 12 5.377 12 12s-5.377 12-12 12-12-5.377-12-12 5.377-12 12-12zm6.997 20.486c2.444-2.019 4.003-5.072 4.003-8.486 0-6.071-4.929-11-11-11s-11 4.929-11 11c0 4.27 2.439 7.975 5.998 9.798v-3.228c0-.691-.441-.917-1.384-1.673-.698-.56-1.177-1.433-1.089-2.322.252-2.537.862-7.575.862-7.575l.909.003-.597 5.997h1.291l.005-6h1l-.002 6h1.003l-.001-6h1l.004 6 1.34.002-.675-6.002h.887c.002.011.675 5.008.951 7.55.098.902-.409 1.792-1.121 2.356-.95.751-1.382.967-1.382 1.669v4.243c.649.12 1.318.182 2.001.182 1.409 0 2.756-.265 3.994-.749l.001-3.251h-2.467c.802-6.996 3.103-12 4.66-12 .447 0 .804.357.807.851.008 1.164.004 6.814.002 12.635zm-7.563-6.486h-5.845c-.067.642-.26 1.387.651 2.117.938.754 1.758 1.231 1.758 2.453v3.678c.326.128.66.24 1.001.337v-4.01c0-1.237.811-1.7 1.761-2.453.944-.747.75-1.464.674-2.122zm6.561 7.222l.002-13.029c-1.14 1.352-2.563 4.206-3.31 9.809h2.308l-.001 3.8c.345-.176.679-.37 1.001-.58z"/>
</svg>
<span class="font-semibold text-xl tracking-tight">
Sortable Recipes
</span>
</LinkTo>
</div>
<div class="block md:hidden">
<button
type="button"
{{on "click" this.toggle}}
class="flex items-center px-3 py-2 border rounded text-teal-200 border-teal-400 hover:text-white hover:border-white"
data-test-id="toggle-menu">
<svg class="fill-current h-3 w-3" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>Menu</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"/></svg>
</button>
</div>
<div class="w-full block flex-grow md:flex md:items-center md:w-auto {{if this.hideLinks "hidden"}}" data-test-id="nav-links">
<div class="text-sm md:flex-grow">
<HeaderNavLink @href="https://github.com/adopted-ember-addons/ember-sortable">
Ember-Sortable
</HeaderNavLink>
<HeaderNavLink @href="https://tailwindcss.com/docs/responsive-design">
TailwindCSS
</HeaderNavLink>
<HeaderNavLink @href="https://www.themealdb.com/">
TheMealDB
</HeaderNavLink>
</div>
<div>
<a href="https://gitlab.com/gaiety/sortable-recipes" class="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white hover:border-transparent focus:text-teal-500 focus:bg-white mt-4 md:mt-0" data-test-id="fork-link">
<svg class="fill-current h-8 w-8 inline-block" width="54" height="54" viewBox="0 0 586 559" xmlns="http://www.w3.org/2000/svg">
<path d="M461.48,298.35,443.7,243.72a7.72,7.72,0,0,0-.43-1.47L407.6,132.45a14.18,14.18,0,0,0-13.54-9.67,13.94,13.94,0,0,0-13.38,9.75l-34,104.63H239.37L205.32,132.53A13.94,13.94,0,0,0,192,122.78h-.08a14.22,14.22,0,0,0-13.5,9.76L142.72,242.47c0,.1-.08.18-.11.28l-18.1,55.61a20.29,20.29,0,0,0,7.37,22.71L288.26,434.7a8,8,0,0,0,9.45-.05l0,0L454.12,321.07A20.28,20.28,0,0,0,461.48,298.35ZM227.73,253.22l43.59,134.16L166.68,253.22Zm87,134.19,41.8-128.62,1.8-5.57h61.1L324.76,374.5Zm79.47-244.58,30.63,94.33H363.52ZM341.49,253.16l-30.37,93.46L293,402.28,244.58,253.16ZM191.85,142.83l30.69,94.33H161.27Zm-50.56,165.3a4.31,4.31,0,0,1-1.56-4.83L153.17,262l98.57,126.37Zm303.43,0L334.26,388.34l.37-.48L432.83,262l13.44,41.28A4.31,4.31,0,0,1,444.72,308.12Z"/>
</svg>
Fork on Gitlab
</a>
</div>
</div>
</nav>

View file

@ -0,0 +1,12 @@
import Component from '@glimmer/component';
import { action } from "@ember/object";
import { tracked } from "@glimmer/tracking";
export default class HeaderNavComponent extends Component {
@tracked hideLinks = true;
@action
toggle() {
this.hideLinks = !this.hideLinks;
}
}

View file

@ -0,0 +1,23 @@
{{!https://samherbert.net/svg-loaders/}}
<svg class="fill-current text-teal-500 w-full h-8 my-4" viewBox="0 0 135 140" xmlns="http://www.w3.org/2000/svg" data-test-id="loading-indicator">
<rect y="10" width="15" height="120" rx="6">
<animate attributeName="height" begin="0.5s" dur="1s" values="120;110;100;90;80;70;60;50;40;140;120" calcMode="linear" repeatCount="indefinite" />
<animate attributeName="y" begin="0.5s" dur="1s" values="10;15;20;25;30;35;40;45;50;0;10" calcMode="linear" repeatCount="indefinite" />
</rect>
<rect x="30" y="10" width="15" height="120" rx="6">
<animate attributeName="height" begin="0.25s" dur="1s" values="120;110;100;90;80;70;60;50;40;140;120" calcMode="linear" repeatCount="indefinite" />
<animate attributeName="y" begin="0.25s" dur="1s" values="10;15;20;25;30;35;40;45;50;0;10" calcMode="linear" repeatCount="indefinite" />
</rect>
<rect x="60" width="15" height="140" rx="6">
<animate attributeName="height" begin="0s" dur="1s" values="120;110;100;90;80;70;60;50;40;140;120" calcMode="linear" repeatCount="indefinite" />
<animate attributeName="y" begin="0s" dur="1s" values="10;15;20;25;30;35;40;45;50;0;10" calcMode="linear" repeatCount="indefinite" />
</rect>
<rect x="90" y="10" width="15" height="120" rx="6">
<animate attributeName="height" begin="0.25s" dur="1s" values="120;110;100;90;80;70;60;50;40;140;120" calcMode="linear" repeatCount="indefinite" />
<animate attributeName="y" begin="0.25s" dur="1s" values="10;15;20;25;30;35;40;45;50;0;10" calcMode="linear" repeatCount="indefinite" />
</rect>
<rect x="120" y="10" width="15" height="120" rx="6">
<animate attributeName="height" begin="0.5s" dur="1s" values="120;110;100;90;80;70;60;50;40;140;120" calcMode="linear" repeatCount="indefinite" />
<animate attributeName="y" begin="0.5s" dur="1s" values="10;15;20;25;30;35;40;45;50;0;10" calcMode="linear" repeatCount="indefinite" />
</rect>
</svg>

View file

@ -0,0 +1,11 @@
{{#if this.fetchMeal.isRunning}}
<LoadingIndicator />
{{/if}}
<button
type="button"
{{on "click" this.addMeal}}
class="w-full bg-teal-500 hover:bg-teal-400 focus:bg-teal-400 text-white font-bold mt-4 py-2 px-4 border-b-4 border-teal-700 hover:border-teal-500 focus:border-teal-500 rounded"
data-test-id="meal-add-button">
Add Another Meal
</button>

View file

@ -0,0 +1,17 @@
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import { task } from 'ember-concurrency';
import { action } from "@ember/object";
export default class MealAddComponent extends Component {
@service store;
@(task(function * () {
return yield this.store.queryRecord('meal', 'random');
})) fetchMeal;
@action
async addMeal() {
await this.fetchMeal.perform();
}
}

View file

@ -0,0 +1,21 @@
<LinkTo @route="meal" @model={{@meal}} class="block bg-white border border-gray-400 focus:border-teal-500 focus:shadow-sm focus:bg-teal-100 outline-none" data-test-id="meal-link">
<div class="max-w-sm w-full lg:max-w-full lg:flex">
<div class="h-48 lg:h-auto lg:w-48 flex-none">
<img src={{@meal.thumbnailUrl}} alt="" role="presentation" class="h-full w-full object-cover" data-test-id="meal-preview-image">
</div>
<div class="p-4 flex flex-col justify-between leading-normal">
<div class="mb-8">
<div class="text-gray-900 font-bold text-xl mb-2" data-test-id="meal-name">
{{@meal.name}}
</div>
<p class="text-gray-700 text-base" data-test-id="meal-ingredients-list">
{{@meal.ingredientsList}}
</p>
</div>
<div data-test-id="meal-tags">
<MealTag>{{@meal.category}}</MealTag>
<MealTag>{{@meal.area}}</MealTag>
</div>
</div>
</div>
</LinkTo>

View file

@ -0,0 +1,17 @@
<div class="rounded-md shadow-md bg-gray-200">
<SortableGroup
@model={{this.sortedItems}}
@onChange={{action this.reorderMeals}}
as |group|>
{{#each group.model as |meal|}}
<group.item
@model={{meal}}
data-test-id="meal-item"
as |item|>
<item.handle>
<MealItem @meal={{meal}} {{on "click" this.openMealDetails}} />
</item.handle>
</group.item>
{{/each}}
</SortableGroup>
</div>

View file

@ -0,0 +1,34 @@
import Component from '@glimmer/component';
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import { A } from '@ember/array';
import { inject as service } from '@ember/service';
export default class MealListComponent extends Component {
@service store;
@tracked items = A([]);
constructor() {
super(...arguments);
this.assignExistingItems();
}
get sortedItems() {
return this.items.sortBy('listOrder');
}
async assignExistingItems() {
let items = await this.store.peekAll('meal');
this.items = items;
}
@action
async reorderMeals(reorderedMeals) {
let orderedIds = reorderedMeals.map(meal => meal.id);
for (let index = 0; index < orderedIds.length; index++) {
let meal = await this.store.peekRecord('meal', orderedIds[index]);
meal.listOrder = index;
}
}
}

View file

@ -0,0 +1,3 @@
<span class="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2" data-test-id="meal-tag">
#{{yield}}
</span>

View file

@ -0,0 +1,23 @@
import Controller from '@ember/controller';
import { tracked } from "@glimmer/tracking";
import { A } from '@ember/array';
import { inject as service } from '@ember/service';
export default class IndexController extends Controller {
@service store;
@tracked items = A([]);
constructor() {
super(...arguments);
this.assignExistingItems();
}
async assignExistingItems() {
let items = await this.store.peekAll('meal');
this.items = items;
}
get userHasMeals() {
return this.items.length >= 1;
}
}

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Sortable Recipes</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{content-for "head"}}
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/vendor.css">
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/sortablerecipes.css">
{{content-for "head-footer"}}
</head>
<body class="bg-gray-100">
{{content-for "body"}}
<script src="{{rootURL}}assets/vendor.js"></script>
<script src="{{rootURL}}assets/sortablerecipes.js"></script>
{{content-for "body-footer"}}
</body>
</html>

View file

@ -0,0 +1,22 @@
import DS from 'ember-data';
const { Model, attr } = DS;
export default class MealModel extends Model {
@attr alternateDrink;
@attr area;
@attr category;
@attr dateModified;
@attr ingredients;
@attr instructions;
@attr name;
@attr sourceUrl;
@attr tags;
@attr thumbnailUrl;
@attr youtubeUrl;
@attr('number', { defaultValue: Infinity }) listOrder;
get ingredientsList() {
return this.ingredients.map(ingredient => ingredient.name).join(', ');
}
}

View file

@ -0,0 +1,4 @@
import Resolver from 'ember-resolver';
export default Resolver;

View file

@ -0,0 +1,11 @@
import EmberRouter from '@ember/routing/router';
import config from './config/environment';
export default class Router extends EmberRouter {
location = config.locationType;
rootURL = config.rootURL;
}
Router.map(function() {
this.route('meal', { path: '/:id' });
});

View file

@ -0,0 +1,7 @@
import Route from '@ember/routing/route';
export default class MealRoute extends Route {
model({ id }) {
return this.store.findRecord('meal', id);
}
}

View file

@ -0,0 +1,15 @@
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "components.css";
@import "tailwindcss/utilities";
@import "utilities.css";
.sortable-item {
transition: all .125s;
}
.sortable-item.is-dragging {
transition-duration: 0s;
z-index: 10;
}

View file

View file

@ -0,0 +1,11 @@
/*global module*/
module.exports = {
theme: {
extend: {}
},
variants: {
borderWidth: ['responsive', 'hover', 'focus'],
},
plugins: []
}

View file

@ -0,0 +1,11 @@
<HeaderNav />
<div class="flex p-4">
<div class="w-2/6">
<MealList />
<MealAdd />
</div>
<div class="w-4/6 pl-4">
{{outlet}}
</div>
</div>

View file

@ -0,0 +1,17 @@
{{#if this.userHasMeals}}
<h1 class="text-5xl -mt-4 text-teal-500 font-hairline">
Great, you have meals!
</h1>
<p class="text-gray-700 text-base">
You can see more details of any meal by clicking on them. You may also reorder meals via drag and drop (or your keyboard).
</p>
{{else}}
<h1 class="text-5xl -mt-4 text-teal-500 font-hairline">
Welcome to Sortable Recipes!
</h1>
<p class="text-gray-700 text-base">
Add some recipes to get started.
</p>
{{/if}}

View file

@ -0,0 +1,14 @@
<h1 class="text-5xl -mt-4 text-teal-500 font-hairline">
{{@model.name}}
</h1>
<img src={{@model.thumbnailUrl}} alt="" role="presentation" class="float-right w-1/3 rounded-lg ml-8 mb-4 shadow-lg border-8 border-white">
<p class="text-gray-700 text-base whitespace-pre-line">
{{@model.instructions}}
</p>
<div class="mt-4">
<MealTag>{{@model.category}}</MealTag>
<MealTag>{{@model.area}}</MealTag>
</div>

View file

@ -0,0 +1,54 @@
'use strict';
module.exports = function(environment) {
let ENV = {
modulePrefix: 'sortablerecipes',
environment,
rootURL: '/',
locationType: 'auto',
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. EMBER_MODULE_UNIFICATION: true
},
EXTEND_PROTOTYPES: {
// Prevent Ember Data from overriding Date.parse.
Date: false
}
},
APP: {
// Here you can pass flags/options to your application instance
// when it is created
}
};
if (environment === 'development') {
ENV['ember-cli-mirage'] = {
enabled: false
};
// ENV.APP.LOG_RESOLVER = true;
// ENV.APP.LOG_ACTIVE_GENERATION = true;
// ENV.APP.LOG_TRANSITIONS = true;
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
// ENV.APP.LOG_VIEW_LOOKUPS = true;
}
if (environment === 'test') {
// Testem prefers this...
ENV.locationType = 'none';
// keep test console output quieter
ENV.APP.LOG_ACTIVE_GENERATION = false;
ENV.APP.LOG_VIEW_LOOKUPS = false;
ENV.APP.rootElement = '#ember-testing';
ENV.APP.autoboot = false;
}
if (environment === 'production') {
// here you can enable a production-specific feature
}
return ENV;
};

View file

@ -0,0 +1,5 @@
{
"application-template-wrapper": false,
"jquery-integration": false,
"template-only-glimmer-components": true
}

View file

@ -0,0 +1,18 @@
'use strict';
const browsers = [
'last 1 Chrome versions',
'last 1 Firefox versions',
'last 1 Safari versions'
];
const isCI = !!process.env.CI;
const isProduction = process.env.EMBER_ENV === 'production';
if (isCI || isProduction) {
browsers.push('ie 11');
}
module.exports = {
browsers
};

View file

@ -0,0 +1,36 @@
'use strict';
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const isProduction = EmberApp.env() === 'production';
const purgeCSS = {
module: require('@fullhuman/postcss-purgecss'),
options: {
content: [
// add extra paths here for components/controllers which include tailwind classes
'./app/index.html',
'./app/templates/**/*.hbs'
],
defaultExtractor: content => content.match(/[A-Za-z0-9-_:/]+/g) || []
}
}
module.exports = function(defaults) {
let app = new EmberApp(defaults, {
postcssOptions: {
compile: {
plugins: [
{
module: require('postcss-import'),
options: {
path: ['node_modules']
}
},
require('tailwindcss')('./app/tailwind/config.js'),
...isProduction ? [purgeCSS] : []
]
}
}
});
return app.toTree();
};

View file

@ -0,0 +1,6 @@
{
"compilerOptions": {
"experimentalDecorators": true
},
"exclude": ["node_modules", "dist"]
}

View file

@ -0,0 +1,12 @@
export default function() {
this.urlPrefix = 'http://localhost:8000/';
this.get('/meals/random', (schema) => {
let id = 'random';
schema.meals.create({id});
return schema.meals.find(id);
});
this.get('/meal/:id');
}

View file

@ -0,0 +1,5 @@
import { Factory } from 'ember-cli-mirage';
export default Factory.extend({
ingredients: Object.freeze([]),
});

View file

@ -0,0 +1,9 @@
export default function(/* server */) {
/*
Seed your development database using your factories.
This data will not be loaded in your tests.
*/
// server.createList('post', 10);
}

View file

@ -0,0 +1,4 @@
import { JSONAPISerializer } from 'ember-cli-mirage';
export default JSONAPISerializer.extend({
});

23208
ember-ui-modern/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,59 @@
{
"name": "sortablerecipes",
"version": "0.0.0",
"private": true,
"description": "Sort and view random recipes from MealDB",
"repository": "",
"license": "MIT",
"author": "",
"directories": {
"doc": "doc",
"test": "tests"
},
"scripts": {
"build": "ember build",
"lint:hbs": "ember-template-lint .",
"lint:js": "eslint .",
"start": "ember serve --port 4201",
"test": "ember test"
},
"devDependencies": {
"@ember/edition-utils": "^1.2.0",
"@ember/optional-features": "^1.3.0",
"@fullhuman/postcss-purgecss": "^2.0.6",
"@glimmer/component": "^1.0.0",
"babel-eslint": "^10.0.3",
"broccoli-asset-rev": "^3.0.0",
"ember-auto-import": "^1.5.3",
"ember-cli": "3.16.0",
"ember-cli-app-version": "^3.2.0",
"ember-cli-babel": "^7.18.0",
"ember-cli-dependency-checker": "^3.2.0",
"ember-cli-htmlbars": "^4.2.3",
"ember-cli-inject-live-reload": "^2.0.2",
"ember-cli-mirage": "^1.1.6",
"ember-cli-postcss": "^5.0.0",
"ember-cli-sri": "^2.1.1",
"ember-cli-uglify": "^3.0.0",
"ember-concurrency": "^1.1.5",
"ember-data": "~3.16.0",
"ember-export-application-global": "^2.0.1",
"ember-load-initializers": "^2.1.1",
"ember-maybe-import-regenerator": "^0.1.6",
"ember-qunit": "^4.6.0",
"ember-resolver": "^7.0.0",
"ember-sortable": "^2.1.3",
"ember-source": "^3.16.3",
"ember-template-lint": "^2.0.1",
"eslint": "^6.8.0",
"eslint-plugin-ember": "^7.8.1",
"eslint-plugin-node": "^11.0.0",
"loader.js": "^4.7.0",
"postcss-import": "^12.0.1",
"qunit-dom": "^1.0.0",
"tailwindcss": "^1.2.0"
},
"engines": {
"node": "8.* || >= 10.*"
}
}

View file

@ -0,0 +1,3 @@
# http://www.robotstxt.org
User-agent: *
Disallow:

25
ember-ui-modern/testem.js Normal file
View file

@ -0,0 +1,25 @@
module.exports = {
test_page: 'tests/index.html?hidepassed',
disable_watching: true,
launch_in_ci: [
'Chrome'
],
launch_in_dev: [
'Chrome'
],
browser_args: {
Chrome: {
ci: [
// --no-sandbox is needed when running Chrome inside a container
process.env.CI ? '--no-sandbox' : null,
'--headless',
'--disable-dev-shm-usage',
'--disable-software-rasterizer',
'--mute-audio',
'--remote-debugging-port=0',
'--window-size=1440,900'
].filter(Boolean)
}
}
};

View file

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Sortable Recipes Tests</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{content-for "head"}}
{{content-for "test-head"}}
<link rel="stylesheet" href="{{rootURL}}assets/vendor.css">
<link rel="stylesheet" href="{{rootURL}}assets/sortablerecipes.css">
<link rel="stylesheet" href="{{rootURL}}assets/test-support.css">
{{content-for "head-footer"}}
{{content-for "test-head-footer"}}
</head>
<body>
{{content-for "body"}}
{{content-for "test-body"}}
<script src="/testem.js" integrity=""></script>
<script src="{{rootURL}}assets/vendor.js"></script>
<script src="{{rootURL}}assets/test-support.js"></script>
<script src="{{rootURL}}assets/sortablerecipes.js"></script>
<script src="{{rootURL}}assets/tests.js"></script>
{{content-for "body-footer"}}
{{content-for "test-body-footer"}}
</body>
</html>

View file

@ -0,0 +1,19 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | header-nav-link', function(hooks) {
setupRenderingTest(hooks);
test('it renders link with href', async function(assert) {
await render(hbs`
<HeaderNavLink @href="foo">
bar
</HeaderNavLink>
`);
assert.dom('[data-test-id=header-nav-link]').hasAttribute('href', 'foo');
assert.dom('[data-test-id=header-nav-link]').includesText('bar');
});
});

View file

@ -0,0 +1,40 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, click } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | header-nav', function(hooks) {
setupRenderingTest(hooks);
test('it renders branding', async function(assert) {
await render(hbs`<HeaderNav />`);
assert.dom('[data-test-id=branding-link]').hasTagName('a');
assert.dom('[data-test-id=branding-link]').includesText('Sortable Recipes');
});
test('it renders fork link', async function(assert) {
await render(hbs`<HeaderNav />`);
assert.dom('[data-test-id=fork-link]').hasTagName('a');
assert.dom('[data-test-id=fork-link]').includesText('Fork');
});
test('it renders links', async function(assert) {
await render(hbs`<HeaderNav />`);
assert.dom('[data-test-id=nav-links] a').exists();
});
test('it can toggle link visibility', async function(assert) {
await render(hbs`<HeaderNav />`);
assert.dom('[data-test-id=nav-links]').hasClass('hidden');
await click('[data-test-id=toggle-menu]');
assert.dom('[data-test-id=nav-links]').doesNotHaveClass('hidden');
await click('[data-test-id=toggle-menu]');
assert.dom('[data-test-id=nav-links]').hasClass('hidden');
});
});

View file

@ -0,0 +1,15 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | loading-indicator', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function(assert) {
await render(hbs`<LoadingIndicator />`);
assert.dom('[data-test-id=loading-indicator]').hasTagName('svg');
assert.dom('[data-test-id=loading-indicator] animate').exists();
});
});

View file

@ -0,0 +1,21 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { render, click } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | meal-add', function(hooks) {
setupRenderingTest(hooks);
setupMirage(hooks);
test('it adds a new meal (meal) when button is clicked', async function(assert) {
await render(hbs`<MealAdd />`);
let store = this.owner.lookup('service:store');
await click('[data-test-id=meal-add-button]');
let randomMeal = await store.findRecord('meal', 'random');
assert.ok(randomMeal);
assert.equal(store.peekAll('meal').length, 1);
});
});

View file

@ -0,0 +1,54 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | meal-preview', function(hooks) {
setupRenderingTest(hooks);
setupMirage(hooks);
hooks.beforeEach(initMeal);
test('it renders a wrapper href', async function(assert) {
await render(hbs`<MealItem @meal={{meal}} />`);
assert.dom('[data-test-id=meal-link]').hasTagName('a');
});
test('it renders meal preview image', async function(assert) {
await render(hbs`<MealItem @meal={{meal}} />`);
assert.dom('[data-test-id=meal-preview-image]').hasAttribute('src', 'image.jpg');
});
test('it renders meal data as text', async function(assert) {
await render(hbs`<MealItem @meal={{meal}} />`);
assert.dom('[data-test-id=meal-name]').hasText('Cookies');
assert.dom('[data-test-id=meal-ingredients-list]').hasText('Love, Chocolate');
assert.dom('[data-test-id=meal-tags]').includesText('Home');
assert.dom('[data-test-id=meal-tags]').includesText('Desserts');
});
async function initMeal() {
let store = this.owner.lookup('service:store');
this.server.create('meal', {
area: 'Home',
category: 'Desserts',
id: 1,
ingredients: [
{
name: 'Love',
measure: 'Infinity',
}, {
name: 'Chocolate',
measure: 'As Much As You Desire',
},
],
name: 'Cookies',
thumbnailUrl: 'image.jpg',
});
this.set('meal', await store.findRecord('meal', 1));
}
});

View file

@ -0,0 +1,70 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { render, find, findAll, focus, triggerKeyEvent } from '@ember/test-helpers';
import { drag } from 'ember-sortable/test-support/helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | meal-list', function(hooks) {
setupRenderingTest(hooks);
setupMirage(hooks);
hooks.beforeEach(initMeals);
test('it renders existing meals into a list', async function(assert) {
await render(hbs`<MealList />`);
assert.dom('[data-test-id=meal-item]').exists({ count: 2 });
});
test('it can focus meal wrappers', async function(assert) {
await render(hbs`<MealList />`);
await focus('[data-sortable-handle]');
assert.equal(find('[data-sortable-handle]'), document.activeElement);
});
test('it can reorder meals via keyboard shortcuts', async function(assert) {
await render(hbs`<MealList />`);
assert.dom(findAll('[data-test-id=meal-name]')[0]).includesText('Meal 1');
assert.dom(findAll('[data-test-id=meal-name]')[1]).includesText('Meal 2');
await focus('[data-sortable-handle]'); // Not technically required, but emulates user interaction
await triggerKeyEvent('[data-sortable-handle]', 'keydown', 'Enter');
await triggerKeyEvent('[data-sortable-handle]', 'keydown', 'ArrowDown');
await triggerKeyEvent('[data-sortable-handle]', 'keydown', 'Enter');
assert.dom(findAll('[data-test-id=meal-name]')[0]).includesText('Meal 2');
assert.dom(findAll('[data-test-id=meal-name]')[1]).includesText('Meal 1');
});
test('it can drag and drop meals', async function(assert) {
await render(hbs`<MealList />`);
assert.dom(findAll('[data-test-id=meal-name]')[0]).includesText('Meal 1');
assert.dom(findAll('[data-test-id=meal-name]')[1]).includesText('Meal 2');
await drag('mouse', '[data-sortable-handle]', () => { return { dy: 100, dx: 0 } });
assert.dom(findAll('[data-test-id=meal-name]')[0]).includesText('Meal 2');
assert.dom(findAll('[data-test-id=meal-name]')[1]).includesText('Meal 1');
});
async function initMeals() {
let store = this.owner.lookup('service:store');
let meal = {
area: 'area',
category: 'category',
ingredients: [
{ name: 'Love', measure: 'Infinity' },
{ name: 'Chocolate', measure: 'As much as you like!' },
],
};
this.server.create('meal', Object.assign({}, meal, { name: 'Meal 1', id: 1 }));
this.server.create('meal', Object.assign({}, meal, { name: 'Meal 2', id: 2 }));
await store.findRecord('meal', 1);
await store.findRecord('meal', 2);
}
});

View file

@ -0,0 +1,16 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | meal-tag', function(hooks) {
setupRenderingTest(hooks);
test('it renders with hashtag', async function(assert) {
await render(hbs`
<MealTag>foo</MealTag>
`);
assert.dom('[data-test-id="meal-tag"]').includesText('#foo');
});
});

View file

@ -0,0 +1,8 @@
import Application from '../app';
import config from '../config/environment';
import { setApplication } from '@ember/test-helpers';
import { start } from 'ember-qunit';
setApplication(Application.create(config.APP));
start();

View file

@ -0,0 +1,12 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Adapter | application', function(hooks) {
setupTest(hooks);
// Replace this with your real tests.
test('it exists', function(assert) {
let adapter = this.owner.lookup('adapter:application');
assert.ok(adapter);
});
});

View file

@ -0,0 +1,12 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Adapter | meal', function(hooks) {
setupTest(hooks);
// Replace this with your real tests.
test('it exists', function(assert) {
let adapter = this.owner.lookup('adapter:meal');
assert.ok(adapter);
});
});

View file

@ -0,0 +1,12 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Controller | index', function(hooks) {
setupTest(hooks);
// Replace this with your real tests.
test('it exists', function(assert) {
let controller = this.owner.lookup('controller:index');
assert.ok(controller);
});
});

View file

@ -0,0 +1,30 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Model | meal', function(hooks) {
setupTest(hooks);
test('it exists', function(assert) {
let store = this.owner.lookup('service:store');
let model = store.createRecord('meal', {});
assert.ok(model);
});
test('it creates comma separated ingredients list', function(assert) {
let store = this.owner.lookup('service:store');
let model = store.createRecord('meal', {
ingredients: [
{
name: 'Love',
measure: 'Infinity',
}, {
name: 'Chocolate',
measure: 'As Much As You Desire',
},
],
});
assert.equal(model.ingredientsList, 'Love, Chocolate');
});
});

View file

@ -0,0 +1,11 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Route | meal', function(hooks) {
setupTest(hooks);
test('it exists', function(assert) {
let route = this.owner.lookup('route:meal');
assert.ok(route);
});
});

View file

@ -78,6 +78,10 @@
</section> </section>
<section data-background-iframe="http://localhost:4200/52958" data-background-interactive> <section data-background-iframe="http://localhost:4200/52958" data-background-interactive>
</section>
<section data-background-iframe="http://localhost:4201/52958" data-background-interactive>
<p>Latest ember-sortable</p>
</section> </section>
</div> </div>
</div> </div>