-
Notifications
You must be signed in to change notification settings - Fork 208
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
fix(button): adds pending button, fixes #3162 #3163
Changes from 5 commits
305c7e6
52417fe
470b0f5
56e822c
dfc98fe
0be1ae7
b31e47a
49d9a8d
c34de46
312b676
9353e4f
e305d44
fe59c9b
f327507
3233d75
6acf088
80f4baf
e1650cb
1235028
c1b54b5
3201c55
3c6eaeb
cdad70d
4b3f03f
a96ae36
f67a9a5
db88621
740fb8c
b5e3523
07e63b9
0258c3a
4c99a45
e8720ac
d65480f
f36e8ad
3ffb308
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,12 +12,15 @@ governing permissions and limitations under the License. | |
|
||
import { | ||
CSSResultArray, | ||
html, | ||
PropertyValues, | ||
SizedMixin, | ||
TemplateResult, | ||
} from '@spectrum-web-components/base'; | ||
import { property } from '@spectrum-web-components/base/src/decorators.js'; | ||
import { ButtonBase } from './ButtonBase.js'; | ||
import buttonStyles from './button.css.js'; | ||
import { when } from '@spectrum-web-components/base/src/directives.js'; | ||
|
||
export type DeprecatedButtonVariants = 'cta' | 'overBackground'; | ||
export type ButtonStatics = 'white' | 'black'; | ||
|
@@ -51,6 +54,42 @@ export class Button extends SizedMixin(ButtonBase, { noDefaultSize: true }) { | |
return [...super.styles, buttonStyles]; | ||
} | ||
|
||
protected pendingCooldown = -1; | ||
|
||
constructor() { | ||
super(); | ||
this.addEventListener( | ||
'click', | ||
(event: Event): void | boolean => { | ||
if (this.pending) { | ||
event.preventDefault(); | ||
event.stopImmediatePropagation(); | ||
event.stopPropagation(); | ||
return false; | ||
} | ||
}, | ||
{ | ||
capture: true, | ||
} | ||
); | ||
} | ||
|
||
@property({ type: Boolean, reflect: true, attribute: 'is-pending' }) | ||
public isPending = false; | ||
|
||
@property({ type: String, attribute: 'pending-label' }) | ||
public pendingLabel = 'Pending'; | ||
|
||
@property({ type: Boolean, reflect: true, attribute: true }) | ||
public pending = false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't really understand the use of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That timing seems weird, though it does make the code make so much more sense. Based on this documentation, it feels like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well it was more a matter of convenience at point of use. If the If the consumer has to add the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an interesting approach, but not seemingly what the code says?
This may be updated functionality, but this says that as soon as you set I discussed why this might be in with the design team, and they outlined a seemingly valid use case of no wanting to go to "visually pending" and then "processed" in flicker speed progression. It's valid, but it's valid at any point in the process where you might switch to I can be convinced of either way, with clear comments that outline a workflow that we all see the same way, but right now I'm leaning towards it being better to only offer There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yes sorry forgot that nuance (its been a while since i started this one).... I think if we leave management of the visually pending state to the consumer, like as not it will get implemented poorly in many places and result in a multitude of flickering bugs in a variety of places with solutions to it varying across the board. But yes the state flow as I recall now is:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, this is ugly pretty much regardless... @TarunAdobe can you test this approach which would simplify to a single state variable.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
|
||
public override click(): void { | ||
if (this.isPending) { | ||
return; | ||
} | ||
super.click(); | ||
} | ||
|
||
/** | ||
* The visual variant to apply to this button. | ||
*/ | ||
|
@@ -127,11 +166,30 @@ export class Button extends SizedMixin(ButtonBase, { noDefaultSize: true }) { | |
public set quiet(quiet: boolean) { | ||
this.treatment = quiet ? 'outline' : 'fill'; | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add back this line. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done hehe |
||
public get quiet(): boolean { | ||
return this.treatment === 'outline'; | ||
} | ||
|
||
protected override willUpdate( | ||
changedProperties: PropertyValues<this> | ||
): void { | ||
super.willUpdate(changedProperties); | ||
if (changedProperties.has('pending')) { | ||
if (this.pending) { | ||
this.pendingCooldown = window.setTimeout(() => { | ||
if (!this.disabled) { | ||
this.isPending = true; | ||
this.setAttribute('aria-label', this.pendingLabel); | ||
} | ||
}, 1000); | ||
} else if (this.pending === false && this.isPending) { | ||
window.clearTimeout(this.pendingCooldown); | ||
this.isPending = false; | ||
this.setAttribute('aria-label', this.label || ''); | ||
} | ||
} | ||
} | ||
|
||
protected override firstUpdated(changes: PropertyValues<this>): void { | ||
super.firstUpdated(changes); | ||
// There is no Spectrum design context for an `<sp-button>` without a variant | ||
|
@@ -140,4 +198,22 @@ export class Button extends SizedMixin(ButtonBase, { noDefaultSize: true }) { | |
this.setAttribute('variant', this.variant); | ||
} | ||
} | ||
|
||
protected override renderButton(): TemplateResult { | ||
return html` | ||
${this.buttonContent} | ||
${when(this.isPending, () => { | ||
import( | ||
'@spectrum-web-components/progress-circle/sp-progress-circle.js' | ||
); | ||
return html` | ||
<sp-progress-circle | ||
indeterminate | ||
static="white" | ||
size="s" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In internal discussion with the Spectrum-CSS team, specifically @KayiuCarlson we concluded that there are no good t-shirt sizes right now to use for the progress circle inside of a button in these cases. So we concluded that we should fudge the sizes manually here to match the workflow-icon sizes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. now using tshirt sizing for progress-circle. |
||
></sp-progress-circle> | ||
`; | ||
})} | ||
`; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,3 +21,32 @@ governing permissions and limitations under the License. | |
border-color: highlight; | ||
} | ||
} | ||
|
||
::slotted([slot='icon']) { | ||
inset: unset; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this for? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same for this one... It doesn't seem to do anything in context of what we are trying to achieve. |
||
|
||
#container { | ||
position: relative; | ||
display: flex; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This element no longer seems to exist? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think so... maybe it did during the initial phase of this PR however now it's useless. so im removing it. |
||
|
||
sp-progress-circle { | ||
display: none; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any time when not There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. checked and found this is redundant! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the removal of isPending variable in the button. this is now back and useful again as we want to show the progress-circle only when the is-pending attribute is set after the animation |
||
|
||
:host([pending][is-pending]) { | ||
cursor: default; | ||
} | ||
|
||
:host([pending][is-pending]) sp-progress-circle { | ||
display: block; | ||
position: absolute; | ||
left: 50%; | ||
transform: translate(-50%, 0); | ||
Comment on lines
+81
to
+82
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For reference: |
||
} | ||
|
||
:host([pending][is-pending]) slot[name='icon'], | ||
:host([pending][is-pending]) #label { | ||
visibility: hidden; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't remember what visibly hiding this content does to the accessibility tree right off, so making a note to confirm. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Checked in voiceover and the label seems to be hidden? Is there some other way I can test? |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
Copyright 2020 Adobe. All rights reserved. | ||
This file is licensed to you under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. You may obtain a copy | ||
of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software distributed under | ||
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS | ||
OF ANY KIND, either express or implied. See the License for the specific language | ||
governing permissions and limitations under the License. | ||
*/ | ||
import { TemplateResult } from '@spectrum-web-components/base'; | ||
import { Properties, renderButtonSet } from './index.js'; | ||
import { args, argTypes } from './index.js'; | ||
|
||
const variant = 'accent'; | ||
const treatment = 'fill'; | ||
const pending = true; | ||
|
||
export default { | ||
component: 'sp-button', | ||
title: 'Button/Accent/Fill/Pending', | ||
args: { | ||
...args, | ||
variant, | ||
treatment, | ||
pending, | ||
}, | ||
argTypes, | ||
}; | ||
|
||
export const s = (args: Properties): TemplateResult => renderButtonSet(args); | ||
s.args = { | ||
size: 's', | ||
}; | ||
|
||
export const m = (args: Properties): TemplateResult => renderButtonSet(args); | ||
m.args = { | ||
size: 'm', | ||
}; | ||
|
||
export const l = (args: Properties): TemplateResult => renderButtonSet(args); | ||
l.args = { | ||
size: 'l', | ||
}; | ||
|
||
export const XL = (args: Properties): TemplateResult => renderButtonSet(args); | ||
XL.args = { | ||
size: 'xl', | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
Copyright 2020 Adobe. All rights reserved. | ||
This file is licensed to you under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. You may obtain a copy | ||
of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software distributed under | ||
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS | ||
OF ANY KIND, either express or implied. See the License for the specific language | ||
governing permissions and limitations under the License. | ||
*/ | ||
import { TemplateResult } from '@spectrum-web-components/base'; | ||
import { Properties, renderButtonSet } from './index.js'; | ||
import { args, argTypes } from './index.js'; | ||
|
||
const variant = 'accent'; | ||
const treatment = 'outline'; | ||
const pending = true; | ||
|
||
export default { | ||
component: 'sp-button', | ||
title: 'Button/Accent/Outline/Pending', | ||
args: { | ||
...args, | ||
variant, | ||
treatment, | ||
pending, | ||
}, | ||
argTypes, | ||
}; | ||
|
||
export const s = (args: Properties): TemplateResult => renderButtonSet(args); | ||
s.args = { | ||
size: 's', | ||
}; | ||
|
||
export const m = (args: Properties): TemplateResult => renderButtonSet(args); | ||
m.args = { | ||
size: 'm', | ||
}; | ||
|
||
export const l = (args: Properties): TemplateResult => renderButtonSet(args); | ||
l.args = { | ||
size: 'l', | ||
}; | ||
|
||
export const XL = (args: Properties): TemplateResult => renderButtonSet(args); | ||
XL.args = { | ||
size: 'xl', | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
Copyright 2020 Adobe. All rights reserved. | ||
This file is licensed to you under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. You may obtain a copy | ||
of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software distributed under | ||
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS | ||
OF ANY KIND, either express or implied. See the License for the specific language | ||
governing permissions and limitations under the License. | ||
*/ | ||
import { TemplateResult } from '@spectrum-web-components/base'; | ||
import { Properties, renderButtonSet } from './index.js'; | ||
import { args, argTypes } from './index.js'; | ||
|
||
const variant = 'black'; | ||
const treatment = 'fill'; | ||
const pending = true; | ||
|
||
export default { | ||
component: 'sp-button', | ||
title: 'Button/Black/Fill/Pending', | ||
args: { | ||
...args, | ||
variant, | ||
treatment, | ||
pending, | ||
}, | ||
argTypes, | ||
}; | ||
|
||
export const s = (args: Properties): TemplateResult => renderButtonSet(args); | ||
s.args = { | ||
size: 's', | ||
}; | ||
|
||
export const m = (args: Properties): TemplateResult => renderButtonSet(args); | ||
m.args = { | ||
size: 'm', | ||
}; | ||
|
||
export const l = (args: Properties): TemplateResult => renderButtonSet(args); | ||
l.args = { | ||
size: 'l', | ||
}; | ||
|
||
export const XL = (args: Properties): TemplateResult => renderButtonSet(args); | ||
XL.args = { | ||
size: 'xl', | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
Copyright 2020 Adobe. All rights reserved. | ||
This file is licensed to you under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. You may obtain a copy | ||
of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software distributed under | ||
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS | ||
OF ANY KIND, either express or implied. See the License for the specific language | ||
governing permissions and limitations under the License. | ||
*/ | ||
import { TemplateResult } from '@spectrum-web-components/base'; | ||
import { Properties, renderButtonSet } from './index.js'; | ||
import { args, argTypes } from './index.js'; | ||
|
||
const variant = 'black'; | ||
const treatment = 'outline'; | ||
const pending = true; | ||
|
||
export default { | ||
component: 'sp-button', | ||
title: 'Button/Black/Outline/Pending', | ||
args: { | ||
...args, | ||
variant, | ||
treatment, | ||
pending, | ||
}, | ||
argTypes, | ||
}; | ||
|
||
export const s = (args: Properties): TemplateResult => renderButtonSet(args); | ||
s.args = { | ||
size: 's', | ||
}; | ||
|
||
export const m = (args: Properties): TemplateResult => renderButtonSet(args); | ||
m.args = { | ||
size: 'm', | ||
}; | ||
|
||
export const l = (args: Properties): TemplateResult => renderButtonSet(args); | ||
l.args = { | ||
size: 'l', | ||
}; | ||
|
||
export const XL = (args: Properties): TemplateResult => renderButtonSet(args); | ||
XL.args = { | ||
size: 'xl', | ||
}; |
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.
Thoughts on this versus using
pointer-events: none
on the attribute selector?If we use the event listener, I think we should only add it when
pending
rather than always have the listener attached.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.
pointer-events:none
should be better in this case so as to avoid any event propagation at all during pending state.Note: we are overriding the click function to avoid clicking on pending either way so even with
pointer-events:none
we won't run into cases where users tries to click the button programatically while in pending state!