Skip to content

Commit

Permalink
stop unwrapping observable properties in inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
electrovir committed Jul 21, 2023
1 parent 808f2f6 commit 1aa871c
Show file tree
Hide file tree
Showing 9 changed files with 49 additions and 63 deletions.
25 changes: 6 additions & 19 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "element-vir",
"version": "14.2.2",
"version": "15.0.0",
"keywords": [
"custom",
"web",
Expand Down Expand Up @@ -41,7 +41,6 @@
"dependencies": {
"@augment-vir/browser": "^15.4.1",
"@augment-vir/common": "^15.4.1",
"element-vir": "^14.2.1",
"lit": "2.7.6",
"lit-css-vars": "^2.0.3"
},
Expand Down Expand Up @@ -78,7 +77,7 @@
"type-fest": "^4.0.0",
"typescript": "~5.1.6",
"virmator": "^7.2.5",
"vite": "^4.4.5",
"vite": "^4.4.6",
"vite-tsconfig-paths": "^4.2.0"
},
"overrides": {
Expand Down
2 changes: 1 addition & 1 deletion src/declarative-element/declarative-element-init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export type DeclarativeElementInit<
hostClasses?: HostClassesInitMap<
TagName,
HostClassKeys,
FlattenObservablePropertyGetters<Inputs>,
Inputs,
FlattenObservablePropertyGetters<StateInit>
>;
/**
Expand Down
8 changes: 3 additions & 5 deletions src/declarative-element/declarative-element.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {RequiredAndNotNullBy, RequiredBy} from '@augment-vir/common';
import {AllowObservablePropertySetter} from 'element-vir';
import {CSSResult, LitElement} from 'lit';
import {Exact} from 'type-fest';
import {WrappedMinimalDefinition} from '../template-transforms/minimal-element-definition';
import {CustomElementTagName, DeclarativeElementInit} from './declarative-element-init';
import {BaseCssPropertyName} from './properties/css-properties';
Expand Down Expand Up @@ -221,7 +221,7 @@ export abstract class DeclarativeElement<
public abstract override render(): unknown;
public abstract readonly instanceState: FlattenObservablePropertyGetters<StateInit>;
public abstract readonly observablePropertyHandlerMap: ObservablePropertyHandlerMap<StateInit>;
public abstract readonly instanceInputs: FlattenObservablePropertyGetters<Inputs>;
public abstract readonly instanceInputs: Inputs;
public abstract assignInputs(
inputs: {} extends Required<Inputs> ? never : Partial<Inputs>,
): void;
Expand All @@ -248,9 +248,7 @@ export interface StaticDeclarativeElementProperties<
RenderOutputGeneric,
> {
/** Assign inputs to an element directly on its interpolated tag. */
readonly assign: <
const SpecificInputs extends AllowObservablePropertySetter<Inputs, SpecificInputs>,
>(
readonly assign: <SpecificInputs extends Exact<Inputs, SpecificInputs>>(
inputsObject: {} extends Required<Inputs> ? never : SpecificInputs,
) => WrappedMinimalDefinition;
assignedInputs: Inputs | undefined;
Expand Down
6 changes: 2 additions & 4 deletions src/declarative-element/define-element-no-inputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,15 +314,13 @@ export function defineElementNoInputs<
{};

public readonly instanceInputs: ThisElementInstance['instanceInputs'] =
createElementUpdaterProxy<Readonly<FlattenObservablePropertyGetters<Inputs>>>(
this,
false,
);
createElementUpdaterProxy<Readonly<Inputs>>(this, false, false);

public readonly instanceState: ThisElementInstance['instanceState'] =
createElementUpdaterProxy<FlattenObservablePropertyGetters<StateInit>>(
this,
!initInput.options?.allowPolymorphicState,
true,
);

constructor() {
Expand Down
23 changes: 5 additions & 18 deletions src/declarative-element/directives/assign.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {noChange} from 'lit';
import {directive, Directive, DirectiveResult, PartInfo} from 'lit/directive.js';
import {assignInputs} from '../properties/assign-inputs';
import {PropertyInitMapBase} from '../properties/element-properties';
import {AllowObservablePropertySetter} from '../properties/observable-property/observable-property-handler';
import {extractElement} from './directive-helpers';

export type ElementDefinitionWithInputsType<
Expand All @@ -19,16 +18,11 @@ export type ElementDefinitionWithInputsType<
* should be
* html`<${MyElement.assign({value: 1})}>...`
*/
export function assign<
const SpecificDeclarativeElement extends ElementDefinitionWithInputsType,
const SpecificInput extends {
[Prop in keyof SpecificDeclarativeElement['inputsType']]: unknown;
},
>(
export function assign<const SpecificDeclarativeElement extends ElementDefinitionWithInputsType>(
declarativeElement: SpecificDeclarativeElement,
inputsObject: {} extends Required<SpecificDeclarativeElement['inputsType']>
? never
: AllowObservablePropertySetter<SpecificDeclarativeElement['inputsType'], SpecificInput>,
: SpecificDeclarativeElement['inputsType'],
): DirectiveResult;
/**
* Assign an object matching an element's inputs to its inputs.
Expand All @@ -42,9 +36,7 @@ export function assign<
*/
export function assign<
const SpecificDeclarativeElement extends ElementDefinitionWithInputsType,
const SpecificInput extends {
[Prop in keyof SpecificDeclarativeElement['inputsType']]: unknown;
},
const SpecificInput extends SpecificDeclarativeElement['inputsType'],
>(inputsObject: SpecificInput extends typeof HTMLElement ? never : SpecificInput): DirectiveResult;
/**
* Assign an object matching an element's inputs to its inputs.
Expand All @@ -56,16 +48,11 @@ export function assign<
* should be
* html`<${MyElement.assign({value: 1})}>...`
*/
export function assign<
const SpecificDeclarativeElement extends ElementDefinitionWithInputsType,
const SpecificInput extends {
[Prop in keyof SpecificDeclarativeElement['inputsType']]: unknown;
},
>(
export function assign<const SpecificDeclarativeElement extends ElementDefinitionWithInputsType>(
declarativeElementOrInputs: SpecificDeclarativeElement,
inputsObject?: {} extends Required<SpecificDeclarativeElement['inputsType']>
? never
: AllowObservablePropertySetter<SpecificDeclarativeElement['inputsType'], SpecificInput>,
: SpecificDeclarativeElement['inputsType'],
): DirectiveResult {
/**
* The directive generics (in listenDirective) are not strong enough to maintain their values.
Expand Down
7 changes: 4 additions & 3 deletions src/declarative-element/properties/element-updater-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ function assertValidPropertyName<PropertyInitGeneric extends PropertyInitMapBase
export function createElementUpdaterProxy<PropertyInitGeneric extends PropertyInitMapBase>(
element: DeclarativeElement,
verifyExists: boolean,
unwrapObservables: boolean,
): PropertyInitGeneric {
/**
* Lit element updates state and inputs by setting them directly on the element, so we must do
Expand Down Expand Up @@ -72,12 +73,12 @@ export function createElementUpdaterProxy<PropertyInitGeneric extends PropertyIn
elementAsProps[propertyKey] = value;
}

if (isObservablePropertyHandlerCreator(newValue)) {
if (unwrapObservables && isObservablePropertyHandlerCreator(newValue)) {
newValue = newValue.init();
}

/** If we're using an existing observable property */
if (isObservablePropertyHandlerInstance(newValue)) {
if (unwrapObservables && isObservablePropertyHandlerInstance(newValue)) {
if (
existingObservablePropertyHandler &&
newValue !== existingObservablePropertyHandler
Expand All @@ -95,7 +96,7 @@ export function createElementUpdaterProxy<PropertyInitGeneric extends PropertyIn

element.observablePropertyHandlerMap[propertyKey] = newValue;
} else {
if (existingObservablePropertyHandler) {
if (unwrapObservables && existingObservablePropertyHandler) {
existingObservablePropertyHandler.setValue(newValue);
} else {
setValueOnElement(newValue);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {randomBoolean} from '@augment-vir/browser';
import {assertTypeOf, typedAssertInstanceOf} from '@augment-vir/browser-testing';
import {assert, fixture as renderFixture, waitUntil} from '@open-wc/testing';
import {defineElement, html} from '../../..';
Expand All @@ -10,8 +11,8 @@ describe(createObservableProperty.name, () => {
const stateObservable = createObservableProperty({stuff: 2});

const MyElement = defineElement<{
simpleInput: string;
complexInput: {three: number};
simpleInput: typeof inputsObservable;
complexInput: typeof complexInputsObservable;
optionalInput?: string;
}>()({
tagName: 'my-element-for-observable-property-test',
Expand All @@ -27,16 +28,19 @@ describe(createObservableProperty.name, () => {

updateState({complexState: {stuff: 5}});

assertTypeOf(inputs.complexInput).toEqualTypeOf(complexInputsObservable);
assertTypeOf(state.simpleState).toEqualTypeOf<{stuff: number}>();
assertTypeOf(state.stateWithUnion).toEqualTypeOf<{stuff: number} | undefined>();

return html`
<span class="state">${state.simpleState}</span>
<span class="inputs">${inputs.simpleInput}</span>
<span class="inputs">${inputs.simpleInput.getValue()}</span>
`;
},
});

const myRandomBoolean = randomBoolean();

// for type testing purposes
html`
<${MyElement.assign({
Expand All @@ -45,11 +49,7 @@ describe(createObservableProperty.name, () => {
})}></${MyElement}>
<${MyElement.assign({
simpleInput: inputsObservable,
complexInput: {three: 3},
})}></${MyElement}>
<${MyElement.assign({
simpleInput: 'four',
complexInput: {three: 3},
complexInput: complexInputsObservable,
optionalInput: 'hi',
})}></${MyElement}>
<${MyElement.assign(
Expand All @@ -61,12 +61,24 @@ describe(createObservableProperty.name, () => {
},
)}></${MyElement}>
<${MyElement.assign({
simpleInput: 'four',
simpleInput: inputsObservable,
// @ts-expect-error
complexInput: {regex: 3},
// @ts-expect-error
anotherThing: 'five',
})}></${MyElement}>
<${MyElement.assign(
myRandomBoolean
? {
simpleInput: inputsObservable,
complexInput: complexInputsObservable,
}
: {
simpleInput: inputsObservable,
complexInput: complexInputsObservable,
optionalInput: 'hi',
},
)}></${MyElement}>
`;

const fixture = await renderFixture(html`
Expand All @@ -76,6 +88,10 @@ describe(createObservableProperty.name, () => {
})}></${MyElement}>
`);

typedAssertInstanceOf(fixture, MyElement);

assertTypeOf(fixture.instanceInputs.complexInput).toEqualTypeOf(complexInputsObservable);

const stateSpan = fixture.shadowRoot?.querySelector('.state');
const inputsSpan = fixture.shadowRoot?.querySelector('.inputs');

Expand Down
2 changes: 1 addition & 1 deletion src/declarative-element/render-callback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export type RenderParams<
>
| Event,
) => boolean;
inputs: Readonly<FlattenObservablePropertyGetters<Inputs>>;
inputs: Readonly<Inputs>;
};

export function createRenderParams<
Expand Down

0 comments on commit 1aa871c

Please sign in to comment.