Skip to content

chore: release 1.46.0 #217

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

Merged
merged 25 commits into from
Jun 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2e175b8
chore: update sdk to 1.15.0-beta.1
pheekus May 30, 2025
8c11d21
chore: fix nested zooms in demo server
pheekus May 30, 2025
6c54e5f
refactor(foxy-internal-card): reduce default line height
pheekus May 30, 2025
e7bb784
fix: fix unstable month name generation in date pickers
pheekus May 30, 2025
a68d92b
refactor(foxy-internal-form): allow more customization in form badges
pheekus May 30, 2025
fdada93
refactor(foxy-internal-text-control): add "pill" layout
pheekus May 30, 2025
8453f44
refactor(foxy-swipe-actions): use css-only approach to hiding scrollbars
pheekus May 30, 2025
f4e3d0d
feat(foxy-form-dialog): propagate nucleon update events from remote r…
pheekus May 30, 2025
dfac0eb
test(foxy-query-builder): fix eslint issues
pheekus May 30, 2025
44513dd
feat(foxy-filter-attribute-card): add filter icon and update counter …
pheekus May 30, 2025
66b3a60
feat(foxy-nucleon): add an option to skip reporting validity on submit
pheekus May 30, 2025
b2ab7f9
feat: add `foxy-store-transaction-folder-form` element
pheekus May 30, 2025
939c465
feat: add `foxy-store-transaction-folder-card` element
pheekus May 30, 2025
ced0218
feat: add `foxy-admin-transaction-card` element
pheekus May 30, 2025
c3e94ef
feat(foxy-transaction): add support for transaction folders
pheekus May 30, 2025
97fd8c4
refactor(foxy-admin-subscription-form): switch to admin-specific tran…
pheekus May 30, 2025
1d3a97c
fix(foxy-coupon-code-form): fix transactions display
pheekus May 30, 2025
c23c7b4
fix(foxy-nucleon): maintain resource state, including zooms, on creat…
pheekus May 30, 2025
70aa435
fix(foxy-nucleon): make sure unused `Rumour` instances are always des…
pheekus May 30, 2025
3e10345
refactor(foxy-nucleon): don't share `Rumour` updates on initial load
pheekus May 30, 2025
51c353e
refactor(foxy-nucleon): invoke refresh() synchronously on UpdateError
pheekus May 30, 2025
45e6a72
chore: regenerate custom-elements.json
pheekus May 30, 2025
dad92f3
refactor: unify tooltip styles
pheekus May 30, 2025
cb287cb
fix(foxy-store-transaction-folder-form): fix usability issues
pheekus May 30, 2025
ed6b466
build: update sdk to latest stable
pheekus Jun 4, 2025
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
828 changes: 783 additions & 45 deletions custom-elements.json

Large diffs are not rendered by default.

5,640 changes: 1,855 additions & 3,785 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"prepack": "npm run lint && rimraf dist && node ./.build/compile-for-npm.js && rollup -c"
},
"dependencies": {
"@foxy.io/sdk": "^1.14.0",
"@foxy.io/sdk": "^1.15.0",
"@open-wc/lit-helpers": "^0.3.12",
"@open-wc/scoped-elements": "^1.2.1",
"@polymer/iron-icons": "^3.0.1",
Expand Down
2 changes: 1 addition & 1 deletion src/elements/internal/InternalCard/InternalCard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class InternalCard<TData extends Data> extends ThemeableMixin(NucleonElem
<div
aria-busy=${this.in('busy')}
aria-live="polite"
class="relative leading-m text-body text-m font-lumo"
class="relative leading-s text-body text-m font-lumo"
>
<div
class=${classMap({
Expand Down
4 changes: 2 additions & 2 deletions src/elements/internal/InternalDateControl/getMonthNames.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export function getMonthNames(lang: string): string[] {
return new Array(12).fill(new Date()).map((date: Date, i) => {
date.setMonth(i);
return new Array(12).fill(null).map((_, i) => {
const date = new Date(2000, i, 1);
return date.toLocaleDateString(lang, { month: 'long' });
});
}
19 changes: 12 additions & 7 deletions src/elements/internal/InternalForm/InternalForm.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ButtonElement } from '@vaadin/vaadin-button';
import { expect, fixture, waitUntil } from '@open-wc/testing';
import { html, render } from 'lit-html';
import { html, render, svg } from 'lit-html';
import { spy, stub } from 'sinon';
import { createRouter } from '../../../server/index';
import { getByKey } from '../../../testgen/getByKey';
Expand All @@ -11,6 +11,7 @@ import { NucleonElement } from '../../public/NucleonElement/NucleonElement';
import { InternalForm } from './index';
import { Resource } from '@foxy.io/sdk/core';
import { Rels } from '@foxy.io/sdk/backend';
import { getByTestClass } from '../../../testgen/getByTestClass';

describe('InternalForm', () => {
it('imports and registers foxy-internal-timestamps-control', () => {
Expand Down Expand Up @@ -100,7 +101,10 @@ describe('InternalForm', () => {
}

get headerSubtitleBadges() {
return [{ key: 'abc' }, { key: 'def' }];
return [
{ key: 'abc', icon: svg`<svg id="icon-abc"></svg>` },
{ text: 'def', class: 'bg-success' },
];
}
}
);
Expand All @@ -122,11 +126,12 @@ describe('InternalForm', () => {
expect(subtitle).to.exist;
expect(subtitle).to.have.deep.property('options', { baz: 'qux' });

const abcBadge = root.querySelector(`foxy-i18n[infer="header badges"][key="abc"]`);
expect(abcBadge).to.exist;

const defBadge = root.querySelector(`foxy-i18n[infer="header badges"][key="def"]`);
expect(defBadge).to.exist;
const badges = await getByTestClass(root, 'badge');
expect(badges).to.have.lengthOf(2);
expect(badges[0].querySelector(`foxy-i18n[infer="header badges"][key="abc"]`)).to.exist;
expect(badges[0].querySelector(`svg#icon-abc`)).to.exist;
expect(badges[1]).to.include.text('def');
expect(badges[1]).to.have.class('bg-success');
});

it('when loaded, renders a Copy ID button in the optional header', async () => {
Expand Down
29 changes: 18 additions & 11 deletions src/elements/internal/InternalForm/InternalForm.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { PropertyDeclarations } from 'lit-element';
import type { HALJSONResource } from '../../public/NucleonElement/types';
import type { TemplateResult } from 'lit-html';
import type { Status } from './types';
import type { Badge, Status } from './types';

import { BooleanSelector, getResourceId } from '@foxy.io/sdk/core';
import { ConfigurableMixin } from '../../../mixins/configurable';
Expand Down Expand Up @@ -67,7 +67,7 @@ export class InternalForm<TData extends HALJSONResource> extends Base<TData> {
}

/** Getter that returns a list of the optional badges to put into the subtitle. The badges are shown only if subtitle is visible. */
get headerSubtitleBadges(): { key: string }[] {
get headerSubtitleBadges(): Badge[] {
return [];
}

Expand Down Expand Up @@ -100,8 +100,8 @@ export class InternalForm<TData extends HALJSONResource> extends Base<TData> {
return html`
<div>
${this.renderTemplateOrSlot('header:before')}
<h2>
<span class="flex items-center gap-s leading-xs text-xl font-medium break-all">
<h2 class="grid gap-xs">
<span class="flex items-center gap-s leading-none text-xl font-medium break-all">
<foxy-i18n
options=${JSON.stringify(this.headerTitleOptions)}
infer="header"
Expand Down Expand Up @@ -136,25 +136,32 @@ export class InternalForm<TData extends HALJSONResource> extends Base<TData> {
</span>
${data
? html`
<div class="flex items-center gap-s text-secondary leading-s">
<div class="flex flex-wrap items-center gap-s leading-xs">
${this.headerSubtitleBadges.map(badge => {
return html`
<foxy-i18n
class="border border-contrast-60 font-medium uppercase tracking-wider block rounded-s px-xs text-xs"
infer="header badges"
key=${badge.key}
<span
data-testclass="badge"
class=${classMap({
'inline-flex gap-xs items-center rounded-s px-xs': true,
[badge.class ?? 'bg-contrast-5']: true,
})}
>
</foxy-i18n>
${badge.icon}
${'text' in badge
? badge.text
: html`<foxy-i18n infer="header badges" key=${badge.key}></foxy-i18n>`}
</span>
`;
})}
<foxy-i18n
infer="header"
class="text-secondary"
key=${this.headerSubtitleKey}
.options=${this.headerSubtitleOptions}
>
</foxy-i18n>
</div>
${actions ? html`<div class="mt-xs flex gap-m">${actions}</div>` : ''}
${actions ? html`<div class="flex gap-x-m">${actions}</div>` : ''}
`
: ''}
</h2>
Expand Down
7 changes: 7 additions & 0 deletions src/elements/internal/InternalForm/types.ts
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
import type { SVGTemplateResult } from 'lit-html';

export type Status = { key: string; options?: unknown };

export type Badge = ({ key: string } | { text: string }) & {
class?: string;
icon?: SVGTemplateResult;
};
121 changes: 120 additions & 1 deletion src/elements/internal/InternalTextControl/InternalTextControl.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ describe('InternalTextControl', () => {
expect(new Control()).to.have.property('layout', null);
});

it('renders vaadin-text-field element', async () => {
it('renders vaadin-text-field element in standalone mode', async () => {
const layout = html`<test-internal-text-control></test-internal-text-control>`;
const control = await fixture<TestControl>(layout);
const field = control.renderRoot.querySelector('vaadin-text-field');
Expand Down Expand Up @@ -216,6 +216,19 @@ describe('InternalTextControl', () => {
expect(control.renderRoot).to.include.text('Foo bar');
});

it('renders label in pill layout', async () => {
const control = await fixture<TestControl>(html`
<test-internal-text-control layout="pill"></test-internal-text-control>
`);

const input = control.renderRoot.querySelector('input');
expect(input).to.have.attribute('aria-label', 'label');

control.label = 'Foo bar';
await control.requestUpdate();
expect(input).to.have.attribute('aria-label', 'Foo bar');
});

it('renders helper text in summary item layout', async () => {
const control = await fixture<TestControl>(html`
<test-internal-text-control layout="summary-item"></test-internal-text-control>
Expand Down Expand Up @@ -256,6 +269,18 @@ describe('InternalTextControl', () => {
expect(input).to.have.attribute('type', 'text');
});

it('renders text input in pill layout', async () => {
const control = await fixture<TestControl>(html`
<test-internal-text-control layout="pill"></test-internal-text-control>
`);

const vaadinTextField = control.renderRoot.querySelector('vaadin-text-field');
expect(vaadinTextField).to.be.null;

const input = control.renderRoot.querySelector('input');
expect(input).to.not.be.null;
});

it('sets "disabled" on input from "disabled" on itself in summary item layout', async () => {
const control = await fixture<TestControl>(html`
<test-internal-text-control layout="summary-item"></test-internal-text-control>
Expand All @@ -275,6 +300,25 @@ describe('InternalTextControl', () => {
expect(input).to.have.property('disabled', false);
});

it('sets "disabled" on input from "disabled" on itself in pill layout', async () => {
const control = await fixture<TestControl>(html`
<test-internal-text-control layout="pill"></test-internal-text-control>
`);

const input = control.renderRoot.querySelector('input')!;
expect(input).to.have.property('disabled', false);

control.disabled = true;
await control.requestUpdate();

expect(input).to.have.property('disabled', true);

control.disabled = false;
await control.requestUpdate();

expect(input).to.have.property('disabled', false);
});

it('sets "readonly" on input from "readonly" on itself in summary item layout', async () => {
const control = await fixture<TestControl>(html`
<test-internal-text-control layout="summary-item"></test-internal-text-control>
Expand All @@ -294,6 +338,25 @@ describe('InternalTextControl', () => {
expect(input).to.have.property('readOnly', false);
});

it('sets "readonly" on input from "readonly" on itself in pill layout', async () => {
const control = await fixture<TestControl>(html`
<test-internal-text-control layout="pill"></test-internal-text-control>
`);

const input = control.renderRoot.querySelector('input')!;
expect(input).to.have.property('readOnly', false);

control.readonly = true;
await control.requestUpdate();

expect(input).to.have.property('readOnly', true);

control.readonly = false;
await control.requestUpdate();

expect(input).to.have.property('readOnly', false);
});

it('sets "placeholder" on input from "placeholder" on itself in summary item layout', async () => {
const control = await fixture<TestControl>(html`
<test-internal-text-control layout="summary-item"></test-internal-text-control>
Expand All @@ -308,6 +371,20 @@ describe('InternalTextControl', () => {
expect(input).to.have.property('placeholder', 'Test placeholder');
});

it('sets "placeholder" on input from "placeholder" on itself in pill layout', async () => {
const control = await fixture<TestControl>(html`
<test-internal-text-control layout="pill"></test-internal-text-control>
`);

const input = control.renderRoot.querySelector('input')!;
expect(input).to.have.property('placeholder', 'placeholder');

control.placeholder = 'Test placeholder';
await control.requestUpdate();

expect(input).to.have.property('placeholder', 'Test placeholder');
});

it('sets "value" on input from "_value" on itself in summary item layout', async () => {
const control = await fixture<TestControl>(html`
<test-internal-text-control layout="summary-item"></test-internal-text-control>
Expand All @@ -322,6 +399,20 @@ describe('InternalTextControl', () => {
expect(input).to.have.property('value', 'test_value');
});

it('sets "value" on input from "_value" on itself in pill layout', async () => {
const control = await fixture<TestControl>(html`
<test-internal-text-control layout="pill"></test-internal-text-control>
`);

const input = control.renderRoot.querySelector('input')!;
expect(input).to.have.property('value', '');

control.testValue = 'test_value';
await control.requestUpdate();

expect(input).to.have.property('value', 'test_value');
});

it('writes to "_value" on input in summary item layout', async () => {
const control = await fixture<TestControl>(html`
<test-internal-text-control layout="summary-item"></test-internal-text-control>
Expand All @@ -336,6 +427,20 @@ describe('InternalTextControl', () => {
expect(control).to.have.property('testValue', 'test_value');
});

it('writes to "_value" on input in pill layout', async () => {
const control = await fixture<TestControl>(html`
<test-internal-text-control layout="pill"></test-internal-text-control>
`);

const input = control.renderRoot.querySelector('input')!;
expect(input).to.have.property('value', '');

input.value = 'test_value';
input.dispatchEvent(new CustomEvent('input'));

expect(control).to.have.property('testValue', 'test_value');
});

it('submits the host nucleon form on Enter in summary item layout', async () => {
const control = await fixture<TestControl>(html`
<test-internal-text-control layout="summary-item"></test-internal-text-control>
Expand All @@ -350,6 +455,20 @@ describe('InternalTextControl', () => {
submitMethod.restore();
});

it('submits the host nucleon form on Enter in pill layout', async () => {
const control = await fixture<TestControl>(html`
<test-internal-text-control layout="pill"></test-internal-text-control>
`);

const input = control.renderRoot.querySelector('input')!;
const submitMethod = stub(control.nucleon, 'submit');

input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
expect(submitMethod).to.have.been.calledOnce;

submitMethod.restore();
});

it('renders prefix text in summary item layout', async () => {
const control = await fixture<TestControl>(html`
<test-internal-text-control layout="summary-item" prefix="Test Prefix">
Expand Down
41 changes: 40 additions & 1 deletion src/elements/internal/InternalTextControl/InternalTextControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export class InternalTextControl extends InternalEditableControl {
];
}

layout: 'summary-item' | 'standalone' | null = null;
layout: 'summary-item' | 'standalone' | 'pill' | null = null;

prefix: string | null = null;

Expand All @@ -54,6 +54,7 @@ export class InternalTextControl extends InternalEditableControl {

renderControl(): TemplateResult {
if (this.layout === 'summary-item') return this.__renderSummaryItemLayout();
if (this.layout === 'pill') return this.__renderPillLayout();

return html`
<vaadin-text-field
Expand Down Expand Up @@ -151,4 +152,42 @@ export class InternalTextControl extends InternalEditableControl {
</div>
`;
}

private __renderPillLayout() {
const content = this._value || this.placeholder;
const contentClass =
'relative block whitespace-pre text-center text-xl font-medium px-s py-xs -my-xs opacity-0';

return html`
<div class="text-center flex items-center gap-s relative" style="min-width: 6rem">
<span class=${contentClass}>${content}</span>
<input
placeholder=${this.placeholder}
aria-label=${this.label}
class=${classMap({
'text-center appearance-none transition-all text-xl font-medium rounded': true,
'focus-outline-none focus-ring-2 focus-ring-primary-50': true,
'px-s py-xs -my-xs absolute inset-0': true,
'hover-bg-contrast-10': !this.disabled && !this.readonly,
'bg-transparent': this.readonly,
'bg-contrast-5': !this.readonly,
'text-disabled': this.disabled && !this.readonly,
})}
?disabled=${this.disabled}
?readonly=${this.readonly}
.value=${this._value ?? ''}
@keydown=${(evt: KeyboardEvent) => {
if (evt.key === 'Enter') {
evt.preventDefault();
this.nucleon?.submit();
}
}}
@input=${(evt: Event) => {
const input = evt.currentTarget as HTMLInputElement;
this._value = input.value;
}}
/>
</div>
`;
}
}
Loading