-
Notifications
You must be signed in to change notification settings - Fork 44
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
Generalize mocking of states in dummy pages #314
Conversation
…ng `mock-state` attributes
… CSS files) to use the new `mock-status` approach
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
@@ -29,13 +29,13 @@ $hds-button-focus-border-width: 3px; | |||
// This covers all of the browsers and focus scenarios (due to the custom focus design). | |||
&:disabled, | |||
&[disabled], | |||
&.is-disabled, | |||
&.mock-disabled, |
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.
in the future, this class selectors could be removed using a css post-processor
@@ -8,7 +8,8 @@ | |||
|
|||
// default focus for browsers that still rely on ":focus" | |||
&:focus, | |||
&.is-focus { | |||
&.is-focus, |
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.
once the changes done across all the pages, I'll remove the is-[state]
selectors from here, for now we need both to avoid breaking the percy tests
if (element.attributes['mock-state-selector']) { | ||
target = element.querySelector( | ||
element.attributes['mock-state-selector'].value | ||
); | ||
} else { | ||
target = element; | ||
} | ||
const state = element.attributes['mock-state-value'].value; | ||
target.classList.add(`mock-${state}`); | ||
}); |
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.
The logic here is this: we have two possible custom HTML attributes (not prefixed with data
intentionally, even if it's not valid HTML for our purpose is OK)
mock-state-value
is the value of the state that we want to use, to apply a classmock-[state]
to an element, so that a custom CSS class can be used to emulate a particular state in a component (typically pseudo states like:hover
or:focus
) in a showcasemock-state-selector
is the DOM selectors we want to use (relative to the element to which is applied) to select the target element on which dynamically apply the mock class at runtime; if no selector is defined, we simply use the element with themock-state-value
attribute itself
@text={{capitalize state}} | ||
@size={{size}} | ||
@color={{color}} | ||
mock-state-value={{state}} |
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.
here you can see how one of these mock states is declared on a component, but only in the dummy documentation page
<Hds::Dropdown::ListItem::CopyItem | ||
@text="{{state}}: 91ee1e8ef65b337f0e70d793f456c71d" | ||
mock-state-value={{state}} | ||
mock-state-selector="button" |
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.
here instead you can see a case where the target element is inside the component, but using the mock-state-selector
it's possible to apply the mock state class directly to it, without the need to pass down arguments across components and sub-components
…multiple targets
Ah yeah, this is the ultimate gotcha with controllers — they’re singletons. So that constructor will only be run once for the lifetime of the application.
@alex-ju @MelSumner @Dhaulagiri now the PR is ready for review. |
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 very much prefer using attributes to mock states, especially as I got a bit confused at my first interaction with the repo when looking at those classes. Code looks good to me (as much as I understand the lifecycle of an Ember instance at the moment).
export default class ComponentsController extends Controller { | ||
@service router; | ||
|
||
constructor() { |
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 all feels a bit magical to me but I also don't have a more idiomatic suggestion springing to mind. I think this is ok for "scrappy" but something we should at least be remembering if we run into unforeseen issues in the future.
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 am surprised that Ember is not throwing several errors on this, especially re: having a controller named "components".
-
I am curious why you didn't use a
data-
attribute; seems strange to purposefully craft invalid HTML when valid HTML is available for exactly this sort of thing. 🤔 -
The approach itself seems similar to what we do with
ember-a11y-refocus
Me too :)
Shorter, easier to read but more importantly to spot in code or in devtools (there may be other legit
👍 |
📌 Summary
This task has been on my todo list for a long time, and while implementing the states for the form controls elements I have realized it was time to bite the bullet and implement it properly (without the proposed changes, would have required a lot of hacks).
What I am proposing here is to replace the way we declare and handle the visual states for the showcase of components, from using Ember arguments (
@state
) passed down in the actual components code (HBS + SCSS), to custom HTML attributes (mock-state
) applied only in the dummy webpages (and referenced only in the CSS).What is the problem I am trying to solve?
The fact is that with the previous approach we were mixing two concerns, the code for the actual components, and the code used to showcase them in the dummy website, and we were exposing both in production code.
Why this solution is better?
With the proposed change, we get rid of this entanglement on the HBS template side (and we may be able to get rid of the same thing on the CSS side, in the future, using a post-css processor that removed the
mock-[state]
selectors from the generated CSS for production code).🛠️ Detailed description
In this PR I have
mock-state
attributesdata
intentionally, even if it's not valid HTML for our purpose is OK)mock-state-value
is the value of the state that we want to use, to apply a classmock-[state]
to an element, so that a custom CSS class can be used to emulate a particular state in a component (typically pseudo states like:hover
or:focus
) in a showcasemock-state-selector
is the DOM selectors we want to use (relative to the element to which is applied) to select the target element on which dynamically apply the mock class at runtime; if no selector is defined, we simply use the element with themock-state-value
attribute itselfDropdown
+Button
dummy pages (and related components and CSS files) to use the newmock-status
approachNotice: since the other pages using the states showcase are
Link::CTA
andLink::Standalone, we will apply the changes to the remaining dummy pages once this branch is merged to
mainand
main` merged in #217 (we will apply the changes there).🖤 Credits
Thanks to @jgwhite 🙏 for helping me solve the problem with
extends Component
+constructor()
+scheduleOnce('afterRender'...
). Essentially this is the ultimate gotcha with controllers — they’re singletons. So that constructor will only be run once for the lifetime of the application.. TIL 🤷♂️(the code fix is in ddfc49a)
👀 How to review
👉 Review by files changed
Notice: I'll add comments inline in the
files changed
section, to explain what I did and why.Reviewer's checklist:
💬 Please consider using conventional comments when reviewing this PR.