Skip to content
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ If no RenderingResolver is provided, Hypertable uses a default resolver with bui

- `integer` type: properly formats numbers in cells and brings range-based filtering
- `timestamp` type: properly displays dates and brings calendar-based filtering
- `money` type: properly formats amounts expressed in cents and brings range-based filtering
- a default `text` renderer to display textual content and filter using a typeahead input

The Rendering Resolver extends BaseRenderingResolver from `@upfluence/hypertable/core/rendering-resolver` and determines which component should be used to render each cell, filter, and header according to column key.
Expand Down Expand Up @@ -361,6 +362,7 @@ Hypertable includes built-in renderers for common data types:
- **text** - Basic text with ellipsis and tooltip
- **numeric** - Formatted numbers
- **date** - Formatted dates
- **amount** - Formatted monetary amounts, expecting a `{ cents, currency }` value

#### Filter Renderers

Expand Down
7 changes: 7 additions & 0 deletions addon/components/hyper-table-v2/cell-renderers/amount.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<div class="fx-row fx-1 fx-malign-end">
{{#if this.value}}
{{format-money this.value.cents this.value.currency "cents"}}
{{else}}
β€”
{{/if}}
</div>
21 changes: 21 additions & 0 deletions addon/components/hyper-table-v2/cell-renderers/amount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Component from '@glimmer/component';

import TableHandler from '@upfluence/hypertable/core/handler';
import { Column, Row } from '@upfluence/hypertable/core/interfaces';

interface HyperTableV2CellRenderersAmountArgs {
handler: TableHandler;
column: Column;
row: Row;
}

export type Amount = {
cents: number;
currency: string;
};

export default class HyperTableV2CellRenderersAmount extends Component<HyperTableV2CellRenderersAmountArgs> {
get value(): Amount | undefined {
return this.args.row[this.args.column.definition.key];
}
}
5 changes: 5 additions & 0 deletions addon/core/rendering-resolver.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ensureSafeComponent } from '@embroider/util';
import GlimmerComponent from '@glimmer/component';

import AmountCellRenderer from '@upfluence/hypertable/components/hyper-table-v2/cell-renderers/amount';
import DateCellRenderer from '@upfluence/hypertable/components/hyper-table-v2/cell-renderers/date';
import NumericCellRenderer from '@upfluence/hypertable/components/hyper-table-v2/cell-renderers/numeric';
import TextCellRenderer from '@upfluence/hypertable/components/hyper-table-v2/cell-renderers/text';
Expand All @@ -21,6 +22,10 @@ const rendererMatchers: { [key: string]: RendererDictionaryItem } = {
cell: NumericCellRenderer,
filter: NumericFilteringRenderer
},
money: {
cell: AmountCellRenderer,
filter: NumericFilteringRenderer
},
timestamp: {
cell: DateCellRenderer,
filter: DateFilteringRenderer
Expand Down
1 change: 1 addition & 0 deletions app/components/hyper-table-v2/cell-renderers/amount.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '@upfluence/hypertable/components/hyper-table-v2/cell-renderers/amount';
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { render, type TestContext } from '@ember/test-helpers';

import { setupRenderingTest } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import { module, test } from 'qunit';

import TableHandler from '@upfluence/hypertable/core/handler';
import { TableManager, RowsFetcher } from '@upfluence/hypertable/test-support';
import { buildColumn } from '@upfluence/hypertable/test-support/table-manager';

const CELL_SELECTOR = '.fx-row.fx-1.fx-malign-end';

module('Integration | Component | hyper-table-v2/cell-renderers/amount', function (hooks) {
setupRenderingTest(hooks);

hooks.beforeEach(function (this: TestContext) {
this.tableManager = new TableManager();
this.rowsFetcher = new RowsFetcher();
this.handler = new TableHandler(this, this.tableManager, this.rowsFetcher);
this.column = buildColumn('amount', { type: 'money' });
});

test('it renders the amount converted from cents', async function (this: TestContext, assert) {
this.row = { amount: { cents: 10000, currency: 'USD' } };

await render(
hbs`<HyperTableV2::CellRenderers::Amount @handler={{this.handler}} @row={{this.row}} @column={{this.column}} />`
);

assert.dom(CELL_SELECTOR).hasText('$100');
});

test('it renders the amount with the currency of the value', async function (this: TestContext, assert) {
this.row = { amount: { cents: 10000, currency: 'EUR' } };

await render(
hbs`<HyperTableV2::CellRenderers::Amount @handler={{this.handler}} @row={{this.row}} @column={{this.column}} />`
);

assert.dom(CELL_SELECTOR).hasText('€100');
});

test('it renders a zero amount', async function (this: TestContext, assert) {
this.row = { amount: { cents: 0, currency: 'USD' } };

await render(
hbs`<HyperTableV2::CellRenderers::Amount @handler={{this.handler}} @row={{this.row}} @column={{this.column}} />`
);

assert.dom(CELL_SELECTOR).hasText('$0');
});

test('it renders the empty state when the value is null', async function (this: TestContext, assert) {
this.row = { amount: null };

await render(
hbs`<HyperTableV2::CellRenderers::Amount @handler={{this.handler}} @row={{this.row}} @column={{this.column}} />`
);

assert.dom(CELL_SELECTOR).hasText('β€”');
});

test('it renders the empty state when the key is missing from the row', async function (this: TestContext, assert) {
this.row = {};

await render(
hbs`<HyperTableV2::CellRenderers::Amount @handler={{this.handler}} @row={{this.row}} @column={{this.column}} />`
);

assert.dom(CELL_SELECTOR).hasText('β€”');
});
});
22 changes: 22 additions & 0 deletions tests/unit/core/rendering-resolver-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { ensureSafeComponent } from '@embroider/util';
import { setupApplicationTest } from 'ember-qunit';
import { module, test } from 'qunit';

import AmountCellRenderer from '@upfluence/hypertable/components/hyper-table-v2/cell-renderers/amount';
import NumericFilteringRenderer from '@upfluence/hypertable/components/hyper-table-v2/filtering-renderers/numeric';
import BaseHeaderRenderer from '@upfluence/hypertable/components/hyper-table-v2/header-renderers/base';
import BaseRenderingResolver from '@upfluence/hypertable/core/rendering-resolver';
import { buildColumnDefinition } from '@upfluence/hypertable/test-support/table-manager';
Expand All @@ -23,4 +25,24 @@ module('Unit | core/rendering-resolver', function (hooks) {
assert.deepEqual(resolved.component, ensureSafeComponent(BaseHeaderRenderer, getContext()));
assert.equal(resolved.args, undefined);
});

test('it returns the amount cell rendering component for a money column', async function (assert: Assert) {
const renderingResolver = new BaseRenderingResolver(getContext());
const resolved = await renderingResolver.lookupCellComponent(buildColumnDefinition('amount', { type: 'money' }));
assert.deepEqual(resolved.component, ensureSafeComponent(AmountCellRenderer, getContext()));
});

test('it returns the numeric filtering component for a money column', async function (assert: Assert) {
const renderingResolver = new BaseRenderingResolver(getContext());
const resolved = await renderingResolver.lookupFilteringComponent(
buildColumnDefinition('amount', { type: 'money' })
);
assert.deepEqual(resolved.component, ensureSafeComponent(NumericFilteringRenderer, getContext()));
});

test('it returns the base header rendering component for a money column', async function (assert: Assert) {
const renderingResolver = new BaseRenderingResolver(getContext());
const resolved = await renderingResolver.lookupHeaderComponent(buildColumnDefinition('amount', { type: 'money' }));
assert.deepEqual(resolved.component, ensureSafeComponent(BaseHeaderRenderer, getContext()));
});
});
Loading