Skip to content

Commit

Permalink
More work on data-event to use action params
Browse files Browse the repository at this point in the history
  • Loading branch information
weaverryan committed Feb 20, 2024
1 parent a8405dc commit a29beee
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 93 deletions.
5 changes: 5 additions & 0 deletions src/LiveComponent/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
>Save</button>
```

- [BC BREAK] The `data-event` attribute was removed in favor of using Stimulus
"action parameters": rename `data-event` to `data-live-event-param`. Additionally,
if you were passing arguments to the event name, use action parameter attributes
for those as well - e.g. `data-live-foo-param="bar"`.

## 2.15.0

- [BC BREAK] The `data-live-id` attribute was changed to `id`.
Expand Down
30 changes: 16 additions & 14 deletions src/LiveComponent/assets/src/live_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,28 +187,28 @@ export default class LiveControllerDefault extends Controller<HTMLElement> imple
});
}

emit(event: Event) {
$render() {
return this.component.render();
}

emit(event: any) {
this.getEmitDirectives(event).forEach(({ name, data, nameMatch }) => {
this.component.emit(name, data, nameMatch);
});
}

emitUp(event: Event) {
emitUp(event: any) {
this.getEmitDirectives(event).forEach(({ name, data, nameMatch }) => {
this.component.emitUp(name, data, nameMatch);
});
}

emitSelf(event: Event) {
emitSelf(event: any) {
this.getEmitDirectives(event).forEach(({ name, data }) => {
this.component.emitSelf(name, data);
});
}

$render() {
return this.component.render();
}

/**
* Update a model value.
*
Expand All @@ -229,13 +229,15 @@ export default class LiveControllerDefault extends Controller<HTMLElement> imple
this.component.fingerprint = this.fingerprintValue;
}

private getEmitDirectives(event: Event): Array<{ name: string; data: any; nameMatch: string | null }> {
const element = event.currentTarget as HTMLElement;
if (!element.dataset.event) {
throw new Error(`No data-event attribute found on element: ${getElementAsTagText(element)}`);
private getEmitDirectives(event: any): Array<{ name: string; data: any; nameMatch: string | null }> {
const params = event.params;
if (!params.action) {
throw new Error(`No event name provided on element: ${getElementAsTagText(event.currentTarget)}. Did you forget to add the "data-live-event-param" attribute?`);
}

const eventInfo = element.dataset.event;
const eventInfo = params.event;
// all other params are considered event arguments
const eventArgs = { ...params };
delete eventArgs.event;

// data-event="name(product_list)|some_event"
const directives = parseDirectives(eventInfo);
Expand All @@ -254,7 +256,7 @@ export default class LiveControllerDefault extends Controller<HTMLElement> imple

emits.push({
name: directive.action,
data: directive.named,
data: eventArgs,
nameMatch,
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ describe('directives parser', () => {
assertDirectiveEquals(directives[0], {
action: 'hide',
args: [],
named: {},
modifiers: [],
})
});
Expand All @@ -40,7 +39,6 @@ describe('directives parser', () => {
assertDirectiveEquals(directives[0], {
action: 'addClass',
args: ['opacity-50'],
named: {},
modifiers: [],
})
});
Expand All @@ -51,7 +49,6 @@ describe('directives parser', () => {
assertDirectiveEquals(directives[0], {
action: 'addClass',
args: ['opacity-50 disabled'],
named: {},
modifiers: [],
})
});
Expand All @@ -63,7 +60,6 @@ describe('directives parser', () => {
action: 'addClass',
// space between arguments is trimmed
args: ['opacity-50', 'disabled'],
named: {},
modifiers: [],
})
});
Expand All @@ -74,13 +70,11 @@ describe('directives parser', () => {
assertDirectiveEquals(directives[0], {
action: 'addClass',
args: ['opacity-50'],
named: {},
modifiers: [],
})
assertDirectiveEquals(directives[1], {
action: 'addAttribute',
args: ['disabled'],
named: {},
modifiers: [],
})
});
Expand All @@ -91,63 +85,16 @@ describe('directives parser', () => {
assertDirectiveEquals(directives[0], {
action: 'hide',
args: [],
named: {},
modifiers: [],
})
assertDirectiveEquals(directives[1], {
action: 'addClass',
args: ['opacity-50 disabled'],
named: {},
modifiers: [],
})
assertDirectiveEquals(directives[2], {
action: 'addAttribute',
args: ['disabled'],
named: {},
modifiers: [],
})
});

it('parses single named argument', () => {
const directives = parseDirectives('save(foo=bar)');
expect(directives).toHaveLength(1);
assertDirectiveEquals(directives[0], {
action: 'save',
args: [],
named: { foo: 'bar' },
modifiers: [],
})
});

it('parses multiple named arguments', () => {
const directives = parseDirectives('save(foo=bar, baz=bazzles)');
expect(directives).toHaveLength(1);
assertDirectiveEquals(directives[0], {
action: 'save',
args: [],
named: { foo: 'bar', baz: 'bazzles' },
modifiers: [],
})
});

it('parses arguments and spaces are kept', () => {
const directives = parseDirectives('save(foo= bar)');
expect(directives).toHaveLength(1);
assertDirectiveEquals(directives[0], {
action: 'save',
args: [],
named: { foo: ' bar' },
modifiers: [],
})
});

it('parses argument names with space is trimmed', () => {
const directives = parseDirectives('save(foo =bar)');
expect(directives).toHaveLength(1);
assertDirectiveEquals(directives[0], {
action: 'save',
args: [],
named: { foo: 'bar' },
modifiers: [],
})
});
Expand All @@ -158,7 +105,6 @@ describe('directives parser', () => {
assertDirectiveEquals(directives[0], {
action: 'addClass',
args: ['disabled'],
named: {},
modifiers: [
{ name: 'delay', value: null }
],
Expand All @@ -171,20 +117,18 @@ describe('directives parser', () => {
assertDirectiveEquals(directives[0], {
action: 'addClass',
args: ['disabled'],
named: {},
modifiers: [
{ name: 'delay', value: '400' },
],
})
});

it('parses multiple modifiers', () => {
const directives = parseDirectives('prevent|debounce(400)|save(foo=bar)');
const directives = parseDirectives('prevent|debounce(400)|save');
expect(directives).toHaveLength(1);
assertDirectiveEquals(directives[0], {
action: 'save',
args: [],
named: { foo: 'bar' },
modifiers: [
{ name: 'prevent', value: null },
{ name: 'debounce', value: '400' },
Expand All @@ -211,22 +155,10 @@ describe('directives parser', () => {
}).toThrow('Missing space after addClass()')
});

it('named and unnamed arguments cannot be mixed', () => {
expect(() => {
parseDirectives('save(foo=bar, baz)');
}).toThrow('Normal and named arguments cannot be mixed inside "save()"')
});

it('modifier cannot have multiple arguments', () => {
expect(() => {
parseDirectives('debounce(10, 20)|save');
}).toThrow('The modifier "debounce()" does not support multiple arguments.')
});

it('modifier cannot have named arguments', () => {
expect(() => {
parseDirectives('debounce(foo=bar)|save');
}).toThrow('The modifier "debounce()" does not support named arguments.')
});
});
});
6 changes: 3 additions & 3 deletions src/LiveComponent/assets/test/controller/action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,14 @@ describe('LiveController Action Tests', () => {
data-live-action-param="sendNamedArgs"
data-live-a-param="1"
data-live-b-param="2"
data-live-c-param="3"
data-live-c-param="banana"
>Send named args</button>
</div>
`);

// ONLY a post is sent, not a re-render GET
test.expectsAjaxCall()
.expectActionCalled('sendNamedArgs', {a: '1', b: '2', c: '3'})
.expectActionCalled('sendNamedArgs', {a: 1, b: 2, c: 'banana'})
.serverWillChangeProps((data: any) => {
// server marks component as "saved"
data.isSaved = true;
Expand Down Expand Up @@ -185,7 +185,7 @@ describe('LiveController Action Tests', () => {
test.expectsAjaxCall()
// 3 actions called
.expectActionCalled('save')
.expectActionCalled('sync', { syncAll: '1' })
.expectActionCalled('sync', { syncAll: 1 })
.expectActionCalled('save')
.serverWillChangeProps((data: any) => {
data.isSaved = true;
Expand Down
6 changes: 3 additions & 3 deletions src/LiveComponent/assets/test/controller/emit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,17 @@ describe('LiveController Emit Tests', () => {
Render Count: ${data.renderCount}
<button
data-action="live#emit"
data-event="fooEvent"
data-live-event-param="fooEvent"
>Emit Simple</button>
<button
data-action="live#emit"
data-event="name(simple-component)|fooEvent"
data-live-event-param="name(simple-component)|fooEvent"
>Emit Named Matching</button>
<button
data-action="live#emit"
data-event="name(other-component)|fooEvent"
data-live-event-param="name(other-component)|fooEvent"
>Emit Named Not Matching</button>
</div>
`);
Expand Down
8 changes: 4 additions & 4 deletions src/LiveComponent/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2462,7 +2462,7 @@ There are three ways to emit an event:

<button
data-action="live#emit"
data-event="productAdded"
data-live-event-param="productAdded"
>

2. From your PHP component via ``ComponentToolsTrait``::
Expand Down Expand Up @@ -2559,7 +2559,7 @@ If you want to emit an event to only the parent components, use the

<button
data-action="live#emitUp"
data-event="productAdded"
data-live-event-param="productAdded"
>

Or, in PHP::
Expand All @@ -2576,7 +2576,7 @@ use the ``name()`` modifier:

<button
data-action="live#emit"
data-event="name(ProductList)|productAdded"
data-live-event-param="name(ProductList)|productAdded"
>

Or, in PHP::
Expand All @@ -2592,7 +2592,7 @@ To emit an event to only yourself, use the ``emitSelf()`` method:

<button
data-action="live#emitSelf"
data-event="productAdded"
data-live-event-param="productAdded"
>

Or, in PHP::
Expand Down

0 comments on commit a29beee

Please sign in to comment.