Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ProductArea service #316

Merged
merged 3 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</div>
{{else if this.loadingHasFailed}}
<div class="h-16">
<div class="related-resources-failed-to-load">
<div class="failed-to-load-text">
Failed to load
</div>
<Hds::Button
Expand Down
20 changes: 16 additions & 4 deletions web/app/components/inputs/product-select/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
@isSaving={{@isSaving}}
@renderOut={{@renderOut}}
@icon={{this.icon}}
class="w-80 product-select-dropdown-list"
class="product-select-dropdown-list w-80"
...attributes
>
<:item as |dd|>
Expand All @@ -32,7 +32,7 @@
@placement={{@placement}}
@isSaving={{@isSaving}}
@renderOut={{@renderOut}}
class="w-[300px] product-select-dropdown-list"
class="product-select-dropdown-list w-[300px]"
...attributes
>
<:anchor as |dd|>
Expand Down Expand Up @@ -68,12 +68,24 @@
</:item>
</X::DropdownList>
{{/if}}
{{else if this.fetchProducts.isRunning}}
{{else if this.fetchProductAreas.isRunning}}
<FlightIcon data-test-product-select-spinner @name="loading" />
{{else if this.errorIsShown}}
<div class="failed-to-load-text">
Failed to load
</div>
<Hds::Button
data-test-product-select-failed-to-load-button
@color="secondary"
@size="small"
{{on "click" (perform this.fetchProductAreas)}}
@text="Retry"
@icon="reload"
/>
{{else}}
<div
class="absolute top-0 left-0"
{{did-insert (perform this.fetchProducts)}}
{{did-insert (perform this.fetchProductAreas)}}
></div>
{{/if}}
</div>
31 changes: 13 additions & 18 deletions web/app/components/inputs/product-select/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { task } from "ember-concurrency";
import FetchService from "hermes/services/fetch";
import ProductAreasService, {
ProductArea,
} from "hermes/services/product-areas";
import getProductId from "hermes/utils/get-product-id";

interface InputsProductSelectSignature {
Expand All @@ -20,21 +23,16 @@ interface InputsProductSelectSignature {
};
}

type ProductAreas = {
[key: string]: ProductArea;
};

export type ProductArea = {
abbreviation: string;
perDocDataType: unknown;
};

export default class InputsProductSelectComponent extends Component<InputsProductSelectSignature> {
@service("fetch") declare fetchSvc: FetchService;
@service declare productAreas: ProductAreasService;

@tracked selected = this.args.selected;
@tracked protected errorIsShown = false;

@tracked products: ProductAreas | undefined = undefined;
get products() {
return this.productAreas.index;
}

get icon(): string {
let icon = "folder";
Expand All @@ -58,15 +56,12 @@ export default class InputsProductSelectComponent extends Component<InputsProduc
this.args.onChange(newValue, attributes);
}

protected fetchProducts = task(async () => {
protected fetchProductAreas = task(async () => {
try {
let products = await this.fetchSvc
.fetch("/api/v1/products")
.then((resp) => resp?.json());
this.products = products;
} catch (err) {
console.error(err);
throw err;
await this.productAreas.fetch.perform();
this.errorIsShown = false;
} catch {
this.errorIsShown = true;
}
});
}
Expand Down
2 changes: 1 addition & 1 deletion web/app/components/new/doc-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { HermesUser } from "hermes/types/document";
import FlashService from "ember-cli-flash/services/flash-messages";
import { assert } from "@ember/debug";
import cleanString from "hermes/utils/clean-string";
import { ProductArea } from "../inputs/product-select";
import { ProductArea } from "hermes/services/product-areas";

interface DocFormErrors {
title: string | null;
Expand Down
27 changes: 27 additions & 0 deletions web/app/services/product-areas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Service, { inject as service } from "@ember/service";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import RouterService from "@ember/routing/router-service";
import { task, timeout } from "ember-concurrency";
import FetchService from "./fetch";

export type ProductArea = {
abbreviation: string;
};

export default class ProductAreasService extends Service {
@service("fetch") declare fetchSvc: FetchService;

@tracked index: Record<string, ProductArea> | null = null;

fetch = task(async () => {
try {
this.index = await this.fetchSvc
.fetch("/api/v1/products")
.then((resp) => resp?.json());
} catch (err) {
this.index = null;
throw err;
}
});
}
4 changes: 0 additions & 4 deletions web/app/styles/components/document/related-resources.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@
}
}

.related-resources-failed-to-load {
@apply mb-2 text-display-300 font-semibold text-color-foreground-faint opacity-50;
}

.related-resources-modal-container {
@apply relative w-full px-3;
}
Expand Down
4 changes: 4 additions & 0 deletions web/app/styles/typography.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@
}
}
}

.failed-to-load-text {
@apply mb-2 text-display-300 font-semibold text-color-foreground-faint opacity-50;
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const HERMES_DOCUMENT_SELECTOR = ".hermes-document";
const EXTERNAL_RESOURCE_SELECTOR = ".external-resource";
const BADGE_SELECTOR = "[data-test-sidebar-section-header-badge]";
const HEADER_SELECTOR = ".sidebar-section-header";
const ERROR_MESSAGE_SELECTOR = ".related-resources-failed-to-load";
const ERROR_MESSAGE_SELECTOR = ".failed-to-load-text";
const ERROR_BUTTON_SELECTOR = "[data-test-related-resources-error-button]";
const OVERFLOW_BUTTON_SELECTOR = ".related-resource-overflow-button";
const EDIT_BUTTON_SELECTOR = "[data-test-overflow-menu-action='edit']";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { click, render } from "@ember/test-helpers";
import { setupMirage } from "ember-cli-mirage/test-support";
import { MirageTestContext } from "ember-cli-mirage/test-support";
import { Placement } from "@floating-ui/dom";
import { Response } from "miragejs";

const DEFAULT_DROPDOWN_SELECTOR = ".product-select-default-toggle";
const LIST_ITEM_SELECTOR = "[data-test-product-select-item]";
Expand Down Expand Up @@ -37,8 +38,7 @@ module("Integration | Component | inputs/product-select", function (hooks) {

this.set("formatIsBadge", true);

await render(hbs`
{{! @glint-nocheck: not typesafe yet }}
await render<InputsProductSelectContext>(hbs`
<Inputs::ProductSelect
@selected={{this.selected}}
@onChange={{this.onChange}}
Expand All @@ -64,8 +64,7 @@ module("Integration | Component | inputs/product-select", function (hooks) {
test("it can render the toggle with a product abbreviation", async function (this: InputsProductSelectContext, assert) {
this.set("selected", this.server.schema.products.first().name);

await render(hbs`
{{! @glint-nocheck: not typesafe yet }}
await render<InputsProductSelectContext>(hbs`
<Inputs::ProductSelect
@selected={{this.selected}}
@onChange={{this.onChange}}
Expand All @@ -78,8 +77,7 @@ module("Integration | Component | inputs/product-select", function (hooks) {
test("it shows an empty state when nothing is selected (default toggle)", async function (this: InputsProductSelectContext, assert) {
this.set("selected", undefined);

await render(hbs`
{{! @glint-nocheck: not typesafe yet }}
await render<InputsProductSelectContext>(hbs`
<Inputs::ProductSelect
@selected={{this.selected}}
@onChange={{this.onChange}}
Expand All @@ -91,8 +89,7 @@ module("Integration | Component | inputs/product-select", function (hooks) {
});

test("it displays the products in a dropdown list with abbreviations", async function (this: InputsProductSelectContext, assert) {
await render(hbs`
{{! @glint-nocheck: not typesafe yet }}
await render<InputsProductSelectContext>(hbs`
<Inputs::ProductSelect
@selected={{this.selected}}
@onChange={{this.onChange}}
Expand All @@ -110,8 +107,7 @@ module("Integration | Component | inputs/product-select", function (hooks) {
test("it fetches the products if they aren't already loaded", async function (this: InputsProductSelectContext, assert) {
this.server.db.emptyData();

await render(hbs`
{{! @glint-nocheck: not typesafe yet }}
await render<InputsProductSelectContext>(hbs`
<Inputs::ProductSelect
@onChange={{this.onChange}}
/>
Expand All @@ -131,8 +127,7 @@ module("Integration | Component | inputs/product-select", function (hooks) {
count++;
});

await render(hbs`
{{! @glint-nocheck: not typesafe yet }}
await render<InputsProductSelectContext>(hbs`
<Inputs::ProductSelect
@selected={{this.selected}}
@onChange={{this.onChange}}
Expand All @@ -144,4 +139,22 @@ module("Integration | Component | inputs/product-select", function (hooks) {

assert.equal(count, 1, "the action was called once");
});

test("it shows an error when the index fails to fetch", async function (this: InputsProductSelectContext, assert) {
this.server.get("/products", () => {
return new Response(500, {});
});

await render<InputsProductSelectContext>(hbs`
<Inputs::ProductSelect
@selected={{this.selected}}
@onChange={{this.onChange}}
/>
`);

assert.dom(".failed-to-load-text").hasText("Failed to load");
assert
.dom("[data-test-product-select-failed-to-load-button]")
.hasText("Retry");
});
});
37 changes: 37 additions & 0 deletions web/tests/unit/services/product-areas-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { module, test, todo } from "qunit";
import { setupTest } from "ember-qunit";
import ProductAreasService from "hermes/services/product-areas";
import { MirageTestContext, setupMirage } from "ember-cli-mirage/test-support";
import { authenticateSession } from "ember-simple-auth/test-support";

module("Unit | Service | product-areas", function (hooks) {
setupTest(hooks);
setupMirage(hooks);

hooks.beforeEach(function () {
authenticateSession({});
});

test("can set or close an active modal", async function (this: MirageTestContext, assert) {
const productAreas = this.owner.lookup(
"service:product-areas"
) as ProductAreasService;

this.server.create("product", {
name: "Labs",
abbreviation: "LABS",
});

const expectedResponse = {
Labs: {
abbreviation: "LABS",
},
};

assert.equal(productAreas.index, null);

await productAreas.fetch.perform();

assert.deepEqual(productAreas.index, expectedResponse);
});
});