-
Notifications
You must be signed in to change notification settings - Fork 3
Remove Ow #615
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
Conversation
🦋 Changeset detectedLatest commit: ecdf00d The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
f3cd80b
to
3a229f1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the directive, because it moves the assertions closer to the markup. When a developer is looking at a slot, they can see we assert that slot for certain types of content.
Contrast that with what we have prior to this PR - there's a bit more scrolling around and diving in to understand (checking firstUpdated
, among other places).
From what I could tell, there shouldn't be any issue, but I'll ask anyway - any concerns from your end around async slotted content coming in like we had in the past?
CONTRIBUTING.md
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So much easier/cleaner to document too!
@@ -2,6 +2,7 @@ | |||
|
|||
import { expect, fixture, html } from '@open-wc/testing'; | |||
import GlideCoreAccordion from './accordion.js'; | |||
import expectUnhandledRejection from './library/expect-unhandled-rejection.js'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dig the name of this helper - very clear of its intent 👍
@@ -3,7 +3,7 @@ | |||
import { expect, fixture, html } from '@open-wc/testing'; | |||
import GlideCoreButtonGroup from './button-group.js'; | |||
import GlideCoreButtonGroupButton from './button-group.button.js'; | |||
import expectArgumentError from './library/expect-argument-error.js'; | |||
import expectWindowError from './library/expect-window-error.js'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one too!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hah. Yeah. Figured they'd do better as two separate libraries so the name of each could be exact. They're also general enough to be usable not just for when slots throws.
src/library/assert-slot.test.ts
Outdated
it('throws when a typed and required default slot only contains text', async () => { | ||
const stub = sinon.stub(console, 'error'); | ||
const spy = sinon.spy(); | ||
const onerror = window.onerror; | ||
|
||
// eslint-disable-next-line unicorn/prefer-add-event-listener | ||
window.onerror = spy; | ||
|
||
await fixture<GlideCoreWithSlot>( | ||
html`<glide-core-with-slot .slotted=${[HTMLButtonElement]}> | ||
</glide-core-with-slot>`, | ||
); | ||
|
||
expect(spy.callCount).to.equal(1); | ||
|
||
expect(spy.args.at(0)?.at(0)).to.equal( | ||
'Uncaught TypeError: Expected GlideCoreWithSlot to have a slotted element that extends HTMLButtonElement.', | ||
); | ||
|
||
// eslint-disable-next-line unicorn/prefer-add-event-listener | ||
window.onerror = onerror; | ||
|
||
stub.restore(); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading the test title, I would've expected the default slot to contain text here, but I don't see any text! Oh - unless it's whitespace being added here in the test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly. I'll throw some text into the slot to make that clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Throw some text into the slot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅ f3be546
slotted?: (typeof Element | typeof Text)[] | null, | ||
isOptional?: boolean, | ||
) { | ||
return noChange; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIL - noChange
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though only useful with custom directives as far as I can tell.
? `Expected the "${part.element.name}" slot of ${host.constructor.name} to have a slotted element that extends ${slotted | ||
.map(({ name }) => name) | ||
.join(' or ')}.` | ||
: `Expected ${host.constructor.name} to have a slotted element that extends ${slotted |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wondering how host.constructor.name
's will appear for consumers, but looking at dist/
I think we'll be okay?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep. Should appear the same as it does now: GlideCoreCheckbox
. We skip mangling class names in Terser for this reason.
Just saw the comment in that file referencing Ow. I'll tweak it before merging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Tweak comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅ f3be546
100%. I would have presented it to the group first for comment. But it struck me as an obvious and significant improvement.
Great question. No issue. Both Menu Options and Dropdown allow asynchronous slotted content by allowing As with Ow, these components do assume that consumers are rendering their asynchronous content via a whitespace-producing template—like so: <glide-core-dropdown>
${repeat(
this.options,
() => html`<glide-core-dropdown-option></glide-core-dropdown-option>`,
)}
</glide-core-dropdown> They make that assumption because we'd otherwise be forced to make the slots optional, and then we could only assert against the type of slotted content and not the existence thereof. For example, if we allowed a consumer to render asynchronous content programmatically: import {html, render} from 'lit-html';
const dropdown = document.querySelector('glide-core-dropdown');
const options = html`<glide-core-dropdown-option></glide-core-dropdown-option>;
render(options, dropdown); <glide-core-dropdown></glide-core-dropdown> There'd be no slotted whitespace and we'd have to make the slots optional by passing a second argument to We'll presumably have to make the slots optional eventually. But, when I initially added slot assertions via Ow, I thought it best to warn consumers of missing slotted content until a use case for programmatic slotted content presented itself. |
Pushed an amended commit resolving the merge conflicts from #617. |
Pushed another amended commit resolving conflicts from #619. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great!
Thanks for taking a look! |
🚀 Description
I've been meaning to revisit our use of Ow—especially
owSlot()
andowSlotType()
—for a while now. After doing some research I found the functionality inowSlot()
andowSlotType()
would be much better as a custom directive.Modal is the best showcase for how a directive simplifies things. No more
firstUpdated()
assertions. Fewerref
s. And no more "slotchange" listeners with assertions.Instead, a single directive is placed on the slot we're asserting against:
assertSlot()
takes two arguments—both optional:The first is an array of allowed node types—similar to
owSlotType()
. The second is a boolean. If a slot is optional, it doesn't have to have content. But, if it does, then the content needs to satisfy the types from the first argument.Modal's "secondary" slot is an example of a slot that's optional but requires specific content.
Accordion's default slot is an example of a non-optional slot that allows arbitrary content. While
assertSlot()
simplifies Modal the most, every component will see some simplification—at least a reduction of a few lines:What do you think?
After implementing
assertSlot()
throughout, Ow became even less useful.I initially thought Ow was worth using (or at least trying out) to reduce indentation and improve readability. Menu is a case where Ow arguably carried its weight. But most other uses of Ow (aside from it's use via
owSlot()
andowSlotType()
) were marginally useful at best—for example, this use of Ow in Dropdown.Then there's the fact that we can't replace every conditional with an Ow assertion because some conditionals aren't strictly for type narrowing but exist because the property or element in question may legitimately be undefined.
Plus Ow adds about 9 KB to our package and complicates our Web Test Runner configuration. So it felt like it was time to remove it altogether.
Yay? Nay?
📋 Checklist
🔬 How to Test
📸 Images/Videos of Functionality
N/A