-
Notifications
You must be signed in to change notification settings - Fork 65
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
Toolbar #397
Toolbar #397
Conversation
@@ -32,7 +33,7 @@ export class App extends WidgetBase<WidgetProperties> { | |||
} | |||
|
|||
render() { | |||
return v('div', [ | |||
return v('div', { id: 'module-select' }, [ |
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 Toolbar
uses a flexbox-based layout wherein page content should be a child of the toolbar itself. This negates the need for any padding-top
on the <body>
element to account for a fixed-positioned toolbar, and allows the toolbar to be an arbitrary height while still allowing page content to scroll independently.
Unfortunately, because our example pages render this module select UI before any component is rendered, it's impossible to place this inside the Toolbar
example. To get around this, the toolbar example page adds a single CSS rule to the example page to style the select in a more sane manner, hence this added ID.
* Properties that can be set on a Toolbar component | ||
* | ||
* @property actions Action elements to show in the toolbar | ||
* @property collapseWidth Width at which to collapse actions into a SlidePane |
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 feel strongly that we shouldn't always be inverting control to the parent for every piece of UI functionality. It's both tedious and common enough to hide action items in a SlidePane that our opinionated component should provide this, while also allowing easy extension. In practice, using a property like collapseWidth
instead of collapsed
and internalizing window resizing logic felt much cleaner.
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'm happy with that here
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.
Might want an onCollapse(collapsed: boolean)
callback though to notify parents?
src/toolbar/Toolbar.ts
Outdated
constructor() { | ||
super(); | ||
this._onResize = debounce(this._collapseIfNecessary, 250); | ||
window.addEventListener('resize', this._onResize); |
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 stopgap until dojo/widget-core#618 lands. For now, we do the best we can by debouncing a resize handler that's explicitly removed on detachment.
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.
Does this need to be debounced
given that it will already be limited by requestAnimationFrame
} | ||
|
||
onAttach() { | ||
const style = document.createElement('style'); |
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 method is purely for demo styling purposes. See above comment re: Toolbar and its children.
src/toolbar/styles/toolbar.m.css
Outdated
overflow-y: auto; | ||
} | ||
|
||
.action { } |
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.
We may not need all these classes to be themeable, but it felt right to expose as many of them as possible.
src/themes/dojo/toolbar.m.css
Outdated
@import './variables.css'; | ||
|
||
.toolbar { | ||
background: #FFF; |
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.
can this be moved to a var?
padding: var(--grid-base); | ||
position: absolute; | ||
right: var(--grid-base); | ||
top: 50%; |
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.
can you not use align-items: center
on the parent to achieve this?
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.
Sort of, but it would require one extra element. Since I'm already absolutely positioning the button to the right it was a bit cleaner (and more uniform) to just use top
and transform
.
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.
what about when our users want to use flexbox? If adding an extra element to make this simpler and removes absolute positioning etc is it not worth it?
* Properties that can be set on a Toolbar component | ||
* | ||
* @property actions Action elements to show in the toolbar | ||
* @property collapseWidth Width at which to collapse actions into a SlidePane |
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'm happy with that here
* Properties that can be set on a Toolbar component | ||
* | ||
* @property actions Action elements to show in the toolbar | ||
* @property collapseWidth Width at which to collapse actions into a SlidePane |
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.
Might want an onCollapse(collapsed: boolean)
callback though to notify parents?
src/toolbar/Toolbar.ts
Outdated
constructor() { | ||
super(); | ||
this._onResize = debounce(this._collapseIfNecessary, 250); | ||
window.addEventListener('resize', this._onResize); |
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.
Does this need to be debounced
given that it will already be limited by requestAnimationFrame
src/toolbar/Toolbar.ts
Outdated
return this._collapsed ? w(SlidePane, { | ||
align: Align.right, | ||
closeText: 'close menu', | ||
key: 'menu', |
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.
not sure you should have two elements with the same key
protected renderTitle(): DNode { | ||
const { title } = this.properties; | ||
|
||
return title ? v('div', { |
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.
no h1
?
src/toolbar/Toolbar.ts
Outdated
]; | ||
} | ||
|
||
protected getModifierClasses(): (string | null)[] { |
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.
renamed these to getRootClasses
elsewhere
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.
Just the question re. flex remains
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 it! Looks like a great addition to widgets :)
src/toolbar/styles/toolbar.m.css
Outdated
height: 100%; | ||
left: 0; | ||
overflow: hidden; | ||
position: fixed; |
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.
Does this need to be fixed? It also works with position: absolute
, and then it isn't constrained to only being document-level (although a toolbar on anything else seems weird, granted).
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.
Good catch. The fixed
styling was left over from the approach that didn't rely on content being a child of the Toolbar.
const actionsElements = actions.map((action, index) => v('div', { | ||
classes: [ this.theme(css.action) ], | ||
key: index | ||
}, [ action ])); |
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 don't really understand why these need to be wrapped in a div. Is it just so their css/theming is within the toolbar CSS as opposed to in parent-widget-level CSS?
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.
Yes, this is really just for easier theming. Most situations I thought of were pretty difficult to accomplish without actions being grouped in a common parent. I'm open to removing it if necessary though.
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.
Hm, I kind of imagine most of the time the actions will be passed in already wrapped in a parent, for semantics and styling. E.g.:
w(Toolbar, {
actions: [
v('div', { id: 'menu', role: 'menubar' }, [
v('a', { href: '/#home' }, [ 'Home' ]),
v('a', { href: '/#about' }, [ 'About' ]),
v('a', { href: '/#contact' }, [ 'Contact' ])
])
],
title: 'Foobar'
}, [
content
])
In that case the extra wrapping div seems a little unnecessary. I don't really dislike it, I just think it introduces an extra wrapping element where none is needed.
src/toolbar/Toolbar.ts
Outdated
|
||
return title ? v('h1', { | ||
classes: [ this.theme(css.title), css.titleFixed ] | ||
}, [ title ]) : null; |
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 wonder if this should really be an h1
. Usually that should go to a content-specific title, not to a company name/logo, which is more common for toolbars. Since title
is already a DNode
, could we just return that, wrapped in a div
for flex styling?
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.
p.s.: really like the breakdown of the render fn :)
.then(displayed => { | ||
assert.isFalse(displayed); | ||
}); | ||
} |
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 know functional tests are a pain, but maybe one to make sure the slidepane is shown or not shown based on window width would be good
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.
^ ignore that if it's too much time, or if you already tried and removed it in frustration
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 think this is a good idea, but I had the hardest time getting functional tests to run locally. I can open a new issue for this if that works for you.
@@ -12,7 +12,7 @@ import * as css from './styles/tooltip.m.css'; | |||
* | |||
* @property content Information to show within the tooltip | |||
* @property orientation Where this tooltip should render relative to its child | |||
* @property open Determines if this tooltip is visible | |||
* @property open Determines if this tooltip is visible |
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.
lol
]), | ||
v('div', { | ||
classes: [ this.theme(css.content), css.contentFixed ] | ||
}, this.children) |
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.
Not sure if this needs to be added now, but reversing the order of the toolbar and content based on a position
property would be easy, and having the option of a bottom toolbar is nice. Works with the styling as-is, 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.
Added!
classes: [ this.theme(css.menuButton), css.menuButtonFixed ], | ||
onclick: this._toggleMenu | ||
}, [ | ||
'open menu', |
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.
You probably already know this, but depending on the order of merging of this and Neil's i18n PR, the 'open menu'/'close menu' text should either be changed here, or Neil should know that you added some more strings for his PR
Codecov Report
@@ Coverage Diff @@
## master #397 +/- ##
==========================================
+ Coverage 97.14% 97.86% +0.72%
==========================================
Files 26 27 +1
Lines 1997 2062 +65
Branches 517 531 +14
==========================================
+ Hits 1940 2018 +78
+ Misses 20 6 -14
- Partials 37 38 +1
Continue to review full report at Codecov.
|
@smhigley This is ready for another review when you have a chance. |
src/toolbar/README.md
Outdated
|
||
### Accessibility Features | ||
|
||
- The menu button displays screen-readable text that's visually hidden and its icon has an `aria-hidden` attribute |
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.
Thinking about the menu wrapping, it might also be good to add some text like this:
Toolbar makes no assumptions about what content will be inserted through
actions
. If you wish to create a site-wide navigation section, it would be best to wrap links in an element with the appropriate semantics (e.g.<nav>
orrole="navigation"
)
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.
MERGE ALL THE THINGS
Type: feature
The following has been addressed in the PR:
Description:
This PR implements an opinionated Toolbar component that can configured to use either fixed or static positioning. Users can provide a
title
element andaction
elements. Action elements will optionally be rendered in aSlidePane
when the component width is less than thecollapseWidth
. Both the title and actions can be anyDNode
, which means events can be attached directly to those elements instead of being propagated up through the Toolbar itself.Resolves #386